xref: /onnv-gate/usr/src/uts/common/disp/sysdc.c (revision 11173:87f3734e64df)
1*11173SJonathan.Adams@Sun.COM /*
2*11173SJonathan.Adams@Sun.COM  * CDDL HEADER START
3*11173SJonathan.Adams@Sun.COM  *
4*11173SJonathan.Adams@Sun.COM  * The contents of this file are subject to the terms of the
5*11173SJonathan.Adams@Sun.COM  * Common Development and Distribution License (the "License").
6*11173SJonathan.Adams@Sun.COM  * You may not use this file except in compliance with the License.
7*11173SJonathan.Adams@Sun.COM  *
8*11173SJonathan.Adams@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*11173SJonathan.Adams@Sun.COM  * or http://www.opensolaris.org/os/licensing.
10*11173SJonathan.Adams@Sun.COM  * See the License for the specific language governing permissions
11*11173SJonathan.Adams@Sun.COM  * and limitations under the License.
12*11173SJonathan.Adams@Sun.COM  *
13*11173SJonathan.Adams@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
14*11173SJonathan.Adams@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*11173SJonathan.Adams@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
16*11173SJonathan.Adams@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
17*11173SJonathan.Adams@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
18*11173SJonathan.Adams@Sun.COM  *
19*11173SJonathan.Adams@Sun.COM  * CDDL HEADER END
20*11173SJonathan.Adams@Sun.COM  */
21*11173SJonathan.Adams@Sun.COM /*
22*11173SJonathan.Adams@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23*11173SJonathan.Adams@Sun.COM  * Use is subject to license terms.
24*11173SJonathan.Adams@Sun.COM  */
25*11173SJonathan.Adams@Sun.COM 
26*11173SJonathan.Adams@Sun.COM /*
27*11173SJonathan.Adams@Sun.COM  * The System Duty Cycle (SDC) scheduling class
28*11173SJonathan.Adams@Sun.COM  * --------------------------------------------
29*11173SJonathan.Adams@Sun.COM  *
30*11173SJonathan.Adams@Sun.COM  * Background
31*11173SJonathan.Adams@Sun.COM  *
32*11173SJonathan.Adams@Sun.COM  * Kernel threads in Solaris have traditionally not been large consumers
33*11173SJonathan.Adams@Sun.COM  * of CPU time.  They typically wake up, perform a small amount of
34*11173SJonathan.Adams@Sun.COM  * work, then go back to sleep waiting for either a timeout or another
35*11173SJonathan.Adams@Sun.COM  * signal.  On the assumption that the small amount of work that they do
36*11173SJonathan.Adams@Sun.COM  * is important for the behavior of the whole system, these threads are
37*11173SJonathan.Adams@Sun.COM  * treated kindly by the dispatcher and the SYS scheduling class: they run
38*11173SJonathan.Adams@Sun.COM  * without preemption from anything other than real-time and interrupt
39*11173SJonathan.Adams@Sun.COM  * threads; when preempted, they are put at the front of the queue, so they
40*11173SJonathan.Adams@Sun.COM  * generally do not migrate between CPUs; and they are allowed to stay
41*11173SJonathan.Adams@Sun.COM  * running until they voluntarily give up the CPU.
42*11173SJonathan.Adams@Sun.COM  *
43*11173SJonathan.Adams@Sun.COM  * As Solaris has evolved, new workloads have emerged which require the
44*11173SJonathan.Adams@Sun.COM  * kernel to perform significant amounts of CPU-intensive work.  One
45*11173SJonathan.Adams@Sun.COM  * example of such a workload is ZFS's transaction group sync processing.
46*11173SJonathan.Adams@Sun.COM  * Each sync operation generates a large batch of I/Os, and each I/O
47*11173SJonathan.Adams@Sun.COM  * may need to be compressed and/or checksummed before it is written to
48*11173SJonathan.Adams@Sun.COM  * storage.  The taskq threads which perform the compression and checksums
49*11173SJonathan.Adams@Sun.COM  * will run nonstop as long as they have work to do; a large sync operation
50*11173SJonathan.Adams@Sun.COM  * on a compression-heavy dataset can keep them busy for seconds on end.
51*11173SJonathan.Adams@Sun.COM  * This causes human-time-scale dispatch latency bubbles for any other
52*11173SJonathan.Adams@Sun.COM  * threads which have the misfortune to share a CPU with the taskq threads.
53*11173SJonathan.Adams@Sun.COM  *
54*11173SJonathan.Adams@Sun.COM  * The SDC scheduling class is a solution to this problem.
55*11173SJonathan.Adams@Sun.COM  *
56*11173SJonathan.Adams@Sun.COM  *
57*11173SJonathan.Adams@Sun.COM  * Overview
58*11173SJonathan.Adams@Sun.COM  *
59*11173SJonathan.Adams@Sun.COM  * SDC is centered around the concept of a thread's duty cycle (DC):
60*11173SJonathan.Adams@Sun.COM  *
61*11173SJonathan.Adams@Sun.COM  *			      ONPROC time
62*11173SJonathan.Adams@Sun.COM  *	Duty Cycle =	----------------------
63*11173SJonathan.Adams@Sun.COM  *			ONPROC + Runnable time
64*11173SJonathan.Adams@Sun.COM  *
65*11173SJonathan.Adams@Sun.COM  * This is the ratio of the time that the thread spent running on a CPU
66*11173SJonathan.Adams@Sun.COM  * divided by the time it spent running or trying to run.  It is unaffected
67*11173SJonathan.Adams@Sun.COM  * by any time the thread spent sleeping, stopped, etc.
68*11173SJonathan.Adams@Sun.COM  *
69*11173SJonathan.Adams@Sun.COM  * A thread joining the SDC class specifies a "target" DC that it wants
70*11173SJonathan.Adams@Sun.COM  * to run at.  To implement this policy, the routine sysdc_update() scans
71*11173SJonathan.Adams@Sun.COM  * the list of active SDC threads every few ticks and uses each thread's
72*11173SJonathan.Adams@Sun.COM  * microstate data to compute the actual duty cycle that that thread
73*11173SJonathan.Adams@Sun.COM  * has experienced recently.  If the thread is under its target DC, its
74*11173SJonathan.Adams@Sun.COM  * priority is increased to the maximum available (sysdc_maxpri, which is
75*11173SJonathan.Adams@Sun.COM  * 99 by default).  If the thread is over its target DC, its priority is
76*11173SJonathan.Adams@Sun.COM  * reduced to the minimum available (sysdc_minpri, 0 by default).  This
77*11173SJonathan.Adams@Sun.COM  * is a fairly primitive approach, in that it doesn't use any of the
78*11173SJonathan.Adams@Sun.COM  * intermediate priorities, but it's not completely inappropriate.  Even
79*11173SJonathan.Adams@Sun.COM  * though threads in the SDC class might take a while to do their job, they
80*11173SJonathan.Adams@Sun.COM  * are by some definition important if they're running inside the kernel,
81*11173SJonathan.Adams@Sun.COM  * so it is reasonable that they should get to run at priority 99.
82*11173SJonathan.Adams@Sun.COM  *
83*11173SJonathan.Adams@Sun.COM  * If a thread is running when sysdc_update() calculates its actual duty
84*11173SJonathan.Adams@Sun.COM  * cycle, and there are other threads of equal or greater priority on its
85*11173SJonathan.Adams@Sun.COM  * CPU's dispatch queue, sysdc_update() preempts that thread.  The thread
86*11173SJonathan.Adams@Sun.COM  * acknowledges the preemption by calling sysdc_preempt(), which calls
87*11173SJonathan.Adams@Sun.COM  * setbackdq(), which gives other threads with the same priority a chance
88*11173SJonathan.Adams@Sun.COM  * to run.  This creates a de facto time quantum for threads in the SDC
89*11173SJonathan.Adams@Sun.COM  * scheduling class.
90*11173SJonathan.Adams@Sun.COM  *
91*11173SJonathan.Adams@Sun.COM  * An SDC thread which is assigned priority 0 can continue to run if
92*11173SJonathan.Adams@Sun.COM  * nothing else needs to use the CPU that it's running on.  Similarly, an
93*11173SJonathan.Adams@Sun.COM  * SDC thread at priority 99 might not get to run as much as it wants to
94*11173SJonathan.Adams@Sun.COM  * if there are other priority-99 or higher threads on its CPU.  These
95*11173SJonathan.Adams@Sun.COM  * situations would cause the thread to get ahead of or behind its target
96*11173SJonathan.Adams@Sun.COM  * DC; the longer the situations lasted, the further ahead or behind the
97*11173SJonathan.Adams@Sun.COM  * thread would get.  Rather than condemning a thread to a lifetime of
98*11173SJonathan.Adams@Sun.COM  * paying for its youthful indiscretions, SDC keeps "base" values for
99*11173SJonathan.Adams@Sun.COM  * ONPROC and Runnable times in each thread's sysdc data, and updates these
100*11173SJonathan.Adams@Sun.COM  * values periodically.  The duty cycle is then computed using the elapsed
101*11173SJonathan.Adams@Sun.COM  * amount of ONPROC and Runnable times since those base times.
102*11173SJonathan.Adams@Sun.COM  *
103*11173SJonathan.Adams@Sun.COM  * Since sysdc_update() scans SDC threads fairly frequently, it tries to
104*11173SJonathan.Adams@Sun.COM  * keep the list of "active" threads small by pruning out threads which
105*11173SJonathan.Adams@Sun.COM  * have been asleep for a brief time.  They are not pruned immediately upon
106*11173SJonathan.Adams@Sun.COM  * going to sleep, since some threads may bounce back and forth between
107*11173SJonathan.Adams@Sun.COM  * sleeping and being runnable.
108*11173SJonathan.Adams@Sun.COM  *
109*11173SJonathan.Adams@Sun.COM  *
110*11173SJonathan.Adams@Sun.COM  * Interfaces
111*11173SJonathan.Adams@Sun.COM  *
112*11173SJonathan.Adams@Sun.COM  * void sysdc_thread_enter(t, dc, flags)
113*11173SJonathan.Adams@Sun.COM  *
114*11173SJonathan.Adams@Sun.COM  *	Moves a kernel thread from the SYS scheduling class to the
115*11173SJonathan.Adams@Sun.COM  *	SDC class. t must have an associated LWP (created by calling
116*11173SJonathan.Adams@Sun.COM  *	lwp_kernel_create()).  The thread will have a target DC of dc.
117*11173SJonathan.Adams@Sun.COM  *	Flags should be either 0 or SYSDC_THREAD_BATCH.  If
118*11173SJonathan.Adams@Sun.COM  *	SYSDC_THREAD_BATCH is specified, the thread will run with a
119*11173SJonathan.Adams@Sun.COM  *	slightly lower priority (see "Batch threads", below).
120*11173SJonathan.Adams@Sun.COM  *
121*11173SJonathan.Adams@Sun.COM  *
122*11173SJonathan.Adams@Sun.COM  * Complications
123*11173SJonathan.Adams@Sun.COM  *
124*11173SJonathan.Adams@Sun.COM  * - Run queue balancing
125*11173SJonathan.Adams@Sun.COM  *
126*11173SJonathan.Adams@Sun.COM  *	The Solaris dispatcher is biased towards letting a thread run
127*11173SJonathan.Adams@Sun.COM  *	on the same CPU which it last ran on, if no more than 3 ticks
128*11173SJonathan.Adams@Sun.COM  *	(i.e. rechoose_interval) have passed since the thread last ran.
129*11173SJonathan.Adams@Sun.COM  *	This helps to preserve cache warmth.  On the other hand, it also
130*11173SJonathan.Adams@Sun.COM  *	tries to keep the per-CPU run queues fairly balanced; if the CPU
131*11173SJonathan.Adams@Sun.COM  *	chosen for a runnable thread has a run queue which is three or
132*11173SJonathan.Adams@Sun.COM  *	more threads longer than a neighboring CPU's queue, the runnable
133*11173SJonathan.Adams@Sun.COM  *	thread is dispatched onto the neighboring CPU instead.
134*11173SJonathan.Adams@Sun.COM  *
135*11173SJonathan.Adams@Sun.COM  *	These policies work well for some workloads, but not for many SDC
136*11173SJonathan.Adams@Sun.COM  *	threads.  The taskq client of SDC, for example, has many discrete
137*11173SJonathan.Adams@Sun.COM  *	units of work to do.  The work units are largely independent, so
138*11173SJonathan.Adams@Sun.COM  *	cache warmth is not an important consideration.  It is important
139*11173SJonathan.Adams@Sun.COM  *	that the threads fan out quickly to different CPUs, since the
140*11173SJonathan.Adams@Sun.COM  *	amount of work these threads have to do (a few seconds worth at a
141*11173SJonathan.Adams@Sun.COM  *	time) doesn't leave much time to correct thread placement errors
142*11173SJonathan.Adams@Sun.COM  *	(i.e. two SDC threads being dispatched to the same CPU).
143*11173SJonathan.Adams@Sun.COM  *
144*11173SJonathan.Adams@Sun.COM  *	To fix this, SDC uses the TS_RUNQMATCH flag introduced for FSS.
145*11173SJonathan.Adams@Sun.COM  *	This tells the dispatcher to keep neighboring run queues' lengths
146*11173SJonathan.Adams@Sun.COM  *	more evenly matched, which allows SDC threads to migrate more
147*11173SJonathan.Adams@Sun.COM  *	easily.
148*11173SJonathan.Adams@Sun.COM  *
149*11173SJonathan.Adams@Sun.COM  * - LWPs and system processes
150*11173SJonathan.Adams@Sun.COM  *
151*11173SJonathan.Adams@Sun.COM  *	SDC can only be used for kernel threads.  Since SDC uses microstate
152*11173SJonathan.Adams@Sun.COM  *	accounting data to compute each thread's actual duty cycle, all
153*11173SJonathan.Adams@Sun.COM  *	threads entering the SDC class must have associated LWPs (which
154*11173SJonathan.Adams@Sun.COM  *	store the microstate data).  This means that the threads have to
155*11173SJonathan.Adams@Sun.COM  *	be associated with an SSYS process, i.e. one created by newproc().
156*11173SJonathan.Adams@Sun.COM  *	If the microstate accounting information is ever moved into the
157*11173SJonathan.Adams@Sun.COM  *	kthread_t, this restriction could be lifted.
158*11173SJonathan.Adams@Sun.COM  *
159*11173SJonathan.Adams@Sun.COM  * - Dealing with oversubscription
160*11173SJonathan.Adams@Sun.COM  *
161*11173SJonathan.Adams@Sun.COM  *	Since SDC duty cycles are per-thread, it is possible that the
162*11173SJonathan.Adams@Sun.COM  *	aggregate requested duty cycle of all SDC threads in a processor
163*11173SJonathan.Adams@Sun.COM  *	set could be greater than the total CPU time available in that set.
164*11173SJonathan.Adams@Sun.COM  *	The FSS scheduling class has an analogous situation, which it deals
165*11173SJonathan.Adams@Sun.COM  *	with by reducing each thread's allotted CPU time proportionally.
166*11173SJonathan.Adams@Sun.COM  *	Since SDC doesn't need to be as precise as FSS, it uses a simpler
167*11173SJonathan.Adams@Sun.COM  *	solution to the oversubscription problem.
168*11173SJonathan.Adams@Sun.COM  *
169*11173SJonathan.Adams@Sun.COM  *	sysdc_update() accumulates the amount of time that max-priority SDC
170*11173SJonathan.Adams@Sun.COM  *	threads have spent on-CPU in each processor set, and uses that sum
171*11173SJonathan.Adams@Sun.COM  *	to create an implied duty cycle for that processor set:
172*11173SJonathan.Adams@Sun.COM  *
173*11173SJonathan.Adams@Sun.COM  *				accumulated CPU time
174*11173SJonathan.Adams@Sun.COM  *	   pset DC =	-----------------------------------
175*11173SJonathan.Adams@Sun.COM  *			 (# CPUs) * time since last update
176*11173SJonathan.Adams@Sun.COM  *
177*11173SJonathan.Adams@Sun.COM  *	If this implied duty cycle is above a maximum pset duty cycle (90%
178*11173SJonathan.Adams@Sun.COM  *	by default), sysdc_update() sets the priority of all SDC threads
179*11173SJonathan.Adams@Sun.COM  *	in that processor set to sysdc_minpri for a "break" period.  After
180*11173SJonathan.Adams@Sun.COM  *	the break period, it waits for a "nobreak" period before trying to
181*11173SJonathan.Adams@Sun.COM  *	enforce the pset duty cycle limit again.
182*11173SJonathan.Adams@Sun.COM  *
183*11173SJonathan.Adams@Sun.COM  * - Processor sets
184*11173SJonathan.Adams@Sun.COM  *
185*11173SJonathan.Adams@Sun.COM  *	As the above implies, SDC is processor set aware, but it does not
186*11173SJonathan.Adams@Sun.COM  *	currently allow threads to change processor sets while in the SDC
187*11173SJonathan.Adams@Sun.COM  *	class.  Instead, those threads must join the desired processor set
188*11173SJonathan.Adams@Sun.COM  *	before entering SDC. [1]
189*11173SJonathan.Adams@Sun.COM  *
190*11173SJonathan.Adams@Sun.COM  * - Batch threads
191*11173SJonathan.Adams@Sun.COM  *
192*11173SJonathan.Adams@Sun.COM  *	A thread joining the SDC class can specify the SDC_THREAD_BATCH
193*11173SJonathan.Adams@Sun.COM  *	flag.  This flag causes the maximum priority for that thread to be
194*11173SJonathan.Adams@Sun.COM  *	reduced (by default, the maximum is reduced by 1).  This allows
195*11173SJonathan.Adams@Sun.COM  *	longer-running, batch-oriented SDC threads to be interrupted by
196*11173SJonathan.Adams@Sun.COM  *	more immediate, higher-priority work.
197*11173SJonathan.Adams@Sun.COM  *
198*11173SJonathan.Adams@Sun.COM  * - t_kpri_req
199*11173SJonathan.Adams@Sun.COM  *
200*11173SJonathan.Adams@Sun.COM  *	The TS and FSS scheduling classes pay attention to t_kpri_req,
201*11173SJonathan.Adams@Sun.COM  *	which provides a simple form of priority inheritance for
202*11173SJonathan.Adams@Sun.COM  *	synchronization primitives (such as rwlocks held as READER) which
203*11173SJonathan.Adams@Sun.COM  *	cannot be traced to a unique thread.  The SDC class does not honor
204*11173SJonathan.Adams@Sun.COM  *	t_kpri_req, for a few reasons:
205*11173SJonathan.Adams@Sun.COM  *
206*11173SJonathan.Adams@Sun.COM  *	1.  t_kpri_req is notoriously inaccurate.  A measure of its
207*11173SJonathan.Adams@Sun.COM  *	    inaccuracy is that it needs to be cleared every time a thread
208*11173SJonathan.Adams@Sun.COM  *	    returns to user mode, because it is frequently non-zero at that
209*11173SJonathan.Adams@Sun.COM  *	    point.  This can happen because "ownership" of synchronization
210*11173SJonathan.Adams@Sun.COM  *	    primitives that use t_kpri_req can be silently handed off,
211*11173SJonathan.Adams@Sun.COM  *	    leaving no opportunity to will the t_kpri_req inheritance.
212*11173SJonathan.Adams@Sun.COM  *
213*11173SJonathan.Adams@Sun.COM  *	2.  Unlike in TS and FSS, threads in SDC *will* eventually run at
214*11173SJonathan.Adams@Sun.COM  *	    kernel priority.  This means that even if an SDC thread
215*11173SJonathan.Adams@Sun.COM  *	    is holding a synchronization primitive and running at low
216*11173SJonathan.Adams@Sun.COM  *	    priority, its priority will eventually be raised above 60,
217*11173SJonathan.Adams@Sun.COM  *	    allowing it to drive on and release the resource.
218*11173SJonathan.Adams@Sun.COM  *
219*11173SJonathan.Adams@Sun.COM  *	3.  The first consumer of SDC uses the taskq subsystem, which holds
220*11173SJonathan.Adams@Sun.COM  *	    a reader lock for the duration of the task's execution.  This
221*11173SJonathan.Adams@Sun.COM  *	    would mean that SDC threads would never drop below kernel
222*11173SJonathan.Adams@Sun.COM  *	    priority in practice, which defeats one of the purposes of SDC.
223*11173SJonathan.Adams@Sun.COM  *
224*11173SJonathan.Adams@Sun.COM  * - Why not FSS?
225*11173SJonathan.Adams@Sun.COM  *
226*11173SJonathan.Adams@Sun.COM  *	It might seem that the existing FSS scheduling class could solve
227*11173SJonathan.Adams@Sun.COM  *	the problems that SDC is attempting to solve.  FSS's more precise
228*11173SJonathan.Adams@Sun.COM  *	solution to the oversubscription problem would hardly cause
229*11173SJonathan.Adams@Sun.COM  *	trouble, as long as it performed well.  SDC is implemented as
230*11173SJonathan.Adams@Sun.COM  *	a separate scheduling class for two main reasons: the initial
231*11173SJonathan.Adams@Sun.COM  *	consumer of SDC does not map well onto the "project" abstraction
232*11173SJonathan.Adams@Sun.COM  *	that is central to FSS, and FSS does not expect to run at kernel
233*11173SJonathan.Adams@Sun.COM  *	priorities.
234*11173SJonathan.Adams@Sun.COM  *
235*11173SJonathan.Adams@Sun.COM  *
236*11173SJonathan.Adams@Sun.COM  * Tunables
237*11173SJonathan.Adams@Sun.COM  *
238*11173SJonathan.Adams@Sun.COM  * - sysdc_batch_niceness:  The amount below sysdc_maxpri that
239*11173SJonathan.Adams@Sun.COM  *	SDC_THREAD_BATCH threads should use as their per-thread
240*11173SJonathan.Adams@Sun.COM  *	maximum priority.
241*11173SJonathan.Adams@Sun.COM  *
242*11173SJonathan.Adams@Sun.COM  * - sysdc_update_interval_msec:  Number of milliseconds between
243*11173SJonathan.Adams@Sun.COM  *	consecutive thread priority updates.
244*11173SJonathan.Adams@Sun.COM  *
245*11173SJonathan.Adams@Sun.COM  * - sysdc_reset_interval_msec:  Number of milliseconds between
246*11173SJonathan.Adams@Sun.COM  *	consecutive resets of a thread's base ONPROC and Runnable
247*11173SJonathan.Adams@Sun.COM  *	times.
248*11173SJonathan.Adams@Sun.COM  *
249*11173SJonathan.Adams@Sun.COM  * - sysdc_prune_interval_msec:  Number of milliseconds of sleeping
250*11173SJonathan.Adams@Sun.COM  *	before a thread is pruned from the active list.
251*11173SJonathan.Adams@Sun.COM  *
252*11173SJonathan.Adams@Sun.COM  * - sysdc_max_pset_DC:  Allowable percentage of a processor set's
253*11173SJonathan.Adams@Sun.COM  *	CPU time which SDC can give to its high-priority threads.
254*11173SJonathan.Adams@Sun.COM  *
255*11173SJonathan.Adams@Sun.COM  * - sysdc_break_msec:  Number of milliseconds of "break" taken when
256*11173SJonathan.Adams@Sun.COM  *	sysdc_max_pset_DC is exceeded.
257*11173SJonathan.Adams@Sun.COM  *
258*11173SJonathan.Adams@Sun.COM  *
259*11173SJonathan.Adams@Sun.COM  * Future work (in SDC and related subsystems)
260*11173SJonathan.Adams@Sun.COM  *
261*11173SJonathan.Adams@Sun.COM  * - Per-thread rechoose interval (0 for SDC)
262*11173SJonathan.Adams@Sun.COM  *
263*11173SJonathan.Adams@Sun.COM  *	Allow each thread to specify its own rechoose interval.  SDC
264*11173SJonathan.Adams@Sun.COM  *	threads would specify an interval of zero, which would rechoose
265*11173SJonathan.Adams@Sun.COM  *	the CPU with the lowest priority once per update.
266*11173SJonathan.Adams@Sun.COM  *
267*11173SJonathan.Adams@Sun.COM  * - Allow threads to change processor sets after joining the SDC class
268*11173SJonathan.Adams@Sun.COM  *
269*11173SJonathan.Adams@Sun.COM  * - Thread groups and per-group DC
270*11173SJonathan.Adams@Sun.COM  *
271*11173SJonathan.Adams@Sun.COM  *	It might be nice to be able to specify a duty cycle which applies
272*11173SJonathan.Adams@Sun.COM  *	to a group of threads in aggregate.
273*11173SJonathan.Adams@Sun.COM  *
274*11173SJonathan.Adams@Sun.COM  * - Per-group DC callback to allow dynamic DC tuning
275*11173SJonathan.Adams@Sun.COM  *
276*11173SJonathan.Adams@Sun.COM  *	Currently, DCs are assigned when the thread joins SDC.  Some
277*11173SJonathan.Adams@Sun.COM  *	workloads could benefit from being able to tune their DC using
278*11173SJonathan.Adams@Sun.COM  *	subsystem-specific knowledge about the workload.
279*11173SJonathan.Adams@Sun.COM  *
280*11173SJonathan.Adams@Sun.COM  * - Finer-grained priority updates
281*11173SJonathan.Adams@Sun.COM  *
282*11173SJonathan.Adams@Sun.COM  * - More nuanced management of oversubscription
283*11173SJonathan.Adams@Sun.COM  *
284*11173SJonathan.Adams@Sun.COM  * - Moving other CPU-intensive threads into SDC
285*11173SJonathan.Adams@Sun.COM  *
286*11173SJonathan.Adams@Sun.COM  * - Move msacct data into kthread_t
287*11173SJonathan.Adams@Sun.COM  *
288*11173SJonathan.Adams@Sun.COM  *	This would allow kernel threads without LWPs to join SDC.
289*11173SJonathan.Adams@Sun.COM  *
290*11173SJonathan.Adams@Sun.COM  *
291*11173SJonathan.Adams@Sun.COM  * Footnotes
292*11173SJonathan.Adams@Sun.COM  *
293*11173SJonathan.Adams@Sun.COM  * [1] The details of doing so are left as an exercise for the reader.
294*11173SJonathan.Adams@Sun.COM  */
295*11173SJonathan.Adams@Sun.COM 
296*11173SJonathan.Adams@Sun.COM #include <sys/types.h>
297*11173SJonathan.Adams@Sun.COM #include <sys/sysdc.h>
298*11173SJonathan.Adams@Sun.COM #include <sys/sysdc_impl.h>
299*11173SJonathan.Adams@Sun.COM 
300*11173SJonathan.Adams@Sun.COM #include <sys/class.h>
301*11173SJonathan.Adams@Sun.COM #include <sys/cmn_err.h>
302*11173SJonathan.Adams@Sun.COM #include <sys/cpuvar.h>
303*11173SJonathan.Adams@Sun.COM #include <sys/cpupart.h>
304*11173SJonathan.Adams@Sun.COM #include <sys/debug.h>
305*11173SJonathan.Adams@Sun.COM #include <sys/disp.h>
306*11173SJonathan.Adams@Sun.COM #include <sys/errno.h>
307*11173SJonathan.Adams@Sun.COM #include <sys/inline.h>
308*11173SJonathan.Adams@Sun.COM #include <sys/kmem.h>
309*11173SJonathan.Adams@Sun.COM #include <sys/modctl.h>
310*11173SJonathan.Adams@Sun.COM #include <sys/schedctl.h>
311*11173SJonathan.Adams@Sun.COM #include <sys/sdt.h>
312*11173SJonathan.Adams@Sun.COM #include <sys/sunddi.h>
313*11173SJonathan.Adams@Sun.COM #include <sys/sysmacros.h>
314*11173SJonathan.Adams@Sun.COM #include <sys/systm.h>
315*11173SJonathan.Adams@Sun.COM #include <sys/var.h>
316*11173SJonathan.Adams@Sun.COM 
317*11173SJonathan.Adams@Sun.COM /*
318*11173SJonathan.Adams@Sun.COM  * Tunables - loaded into the internal state at module load time
319*11173SJonathan.Adams@Sun.COM  */
320*11173SJonathan.Adams@Sun.COM uint_t		sysdc_update_interval_msec = 20;
321*11173SJonathan.Adams@Sun.COM uint_t		sysdc_reset_interval_msec = 400;
322*11173SJonathan.Adams@Sun.COM uint_t		sysdc_prune_interval_msec = 100;
323*11173SJonathan.Adams@Sun.COM uint_t		sysdc_max_pset_DC = 90;
324*11173SJonathan.Adams@Sun.COM uint_t		sysdc_break_msec = 80;
325*11173SJonathan.Adams@Sun.COM pri_t		sysdc_batch_niceness = 1;
326*11173SJonathan.Adams@Sun.COM 
327*11173SJonathan.Adams@Sun.COM /*
328*11173SJonathan.Adams@Sun.COM  * Internal state - constants set up by sysdc_initparam()
329*11173SJonathan.Adams@Sun.COM  */
330*11173SJonathan.Adams@Sun.COM static clock_t	sysdc_update_ticks;	/* ticks between updates */
331*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_prune_updates;	/* updates asleep before pruning */
332*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_reset_updates;	/* # of updates before reset */
333*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_break_updates;	/* updates to break */
334*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_nobreak_updates;	/* updates to not check */
335*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_minDC;		/* minimum allowed DC */
336*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_maxDC;		/* maximum allowed DC */
337*11173SJonathan.Adams@Sun.COM static pri_t	sysdc_minpri;		/* minimum allowed priority */
338*11173SJonathan.Adams@Sun.COM static pri_t	sysdc_maxpri;		/* maximum allowed priority */
339*11173SJonathan.Adams@Sun.COM 
340*11173SJonathan.Adams@Sun.COM /*
341*11173SJonathan.Adams@Sun.COM  * Internal state
342*11173SJonathan.Adams@Sun.COM  */
343*11173SJonathan.Adams@Sun.COM static kmutex_t	sysdc_pset_lock;	/* lock protecting pset data */
344*11173SJonathan.Adams@Sun.COM static list_t	sysdc_psets;		/* list of psets with SDC threads */
345*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_param_init;	/* sysdc_initparam() has been called */
346*11173SJonathan.Adams@Sun.COM static uint_t	sysdc_update_timeout_started; /* update timeout is active */
347*11173SJonathan.Adams@Sun.COM static hrtime_t	sysdc_last_update;	/* time of last sysdc_update() */
348*11173SJonathan.Adams@Sun.COM static sysdc_t	sysdc_dummy;		/* used to terminate active lists */
349*11173SJonathan.Adams@Sun.COM 
350*11173SJonathan.Adams@Sun.COM /*
351*11173SJonathan.Adams@Sun.COM  * Internal state - active hash table
352*11173SJonathan.Adams@Sun.COM  */
353*11173SJonathan.Adams@Sun.COM #define	SYSDC_NLISTS	8
354*11173SJonathan.Adams@Sun.COM #define	SYSDC_HASH(sdc)	(((uintptr_t)(sdc) >> 6) & (SYSDC_NLISTS - 1))
355*11173SJonathan.Adams@Sun.COM static sysdc_list_t	sysdc_active[SYSDC_NLISTS];
356*11173SJonathan.Adams@Sun.COM #define	SYSDC_LIST(sdc)		(&sysdc_active[SYSDC_HASH(sdc)])
357*11173SJonathan.Adams@Sun.COM 
358*11173SJonathan.Adams@Sun.COM #ifdef DEBUG
359*11173SJonathan.Adams@Sun.COM static struct {
360*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_asleep;
361*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_base_ran_backwards;
362*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_already_done;
363*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_cur_ran_backwards;
364*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_compute_pri_breaking;
365*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_activate_enter;
366*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_enter;
367*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_exited;
368*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_not_sdc;
369*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_idle;
370*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_take_break;
371*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_no_psets;
372*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_tick_not_sdc;
373*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_tick_quantum_expired;
374*11173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_thread_enter_enter;
375*11173SJonathan.Adams@Sun.COM } sysdc_stats;
376*11173SJonathan.Adams@Sun.COM 
377*11173SJonathan.Adams@Sun.COM #define	SYSDC_INC_STAT(x)	(sysdc_stats.x++)
378*11173SJonathan.Adams@Sun.COM #else
379*11173SJonathan.Adams@Sun.COM #define	SYSDC_INC_STAT(x)	((void)0)
380*11173SJonathan.Adams@Sun.COM #endif
381*11173SJonathan.Adams@Sun.COM 
382*11173SJonathan.Adams@Sun.COM /* macros are UPPER CASE */
383*11173SJonathan.Adams@Sun.COM #define	HOWMANY(a, b)	howmany((a), (b))
384*11173SJonathan.Adams@Sun.COM #define	MSECTOTICKS(a)	HOWMANY((a) * 1000, usec_per_tick)
385*11173SJonathan.Adams@Sun.COM 
386*11173SJonathan.Adams@Sun.COM static void
387*11173SJonathan.Adams@Sun.COM sysdc_initparam(void)
388*11173SJonathan.Adams@Sun.COM {
389*11173SJonathan.Adams@Sun.COM 	uint_t sysdc_break_ticks;
390*11173SJonathan.Adams@Sun.COM 
391*11173SJonathan.Adams@Sun.COM 	/* update / prune intervals */
392*11173SJonathan.Adams@Sun.COM 	sysdc_update_ticks = MSECTOTICKS(sysdc_update_interval_msec);
393*11173SJonathan.Adams@Sun.COM 
394*11173SJonathan.Adams@Sun.COM 	sysdc_prune_updates = HOWMANY(sysdc_prune_interval_msec,
395*11173SJonathan.Adams@Sun.COM 	    sysdc_update_interval_msec);
396*11173SJonathan.Adams@Sun.COM 	sysdc_reset_updates = HOWMANY(sysdc_reset_interval_msec,
397*11173SJonathan.Adams@Sun.COM 	    sysdc_update_interval_msec);
398*11173SJonathan.Adams@Sun.COM 
399*11173SJonathan.Adams@Sun.COM 	/* We must get at least a little time on CPU. */
400*11173SJonathan.Adams@Sun.COM 	sysdc_minDC = 1;
401*11173SJonathan.Adams@Sun.COM 	sysdc_maxDC = SYSDC_DC_MAX;
402*11173SJonathan.Adams@Sun.COM 	sysdc_minpri = 0;
403*11173SJonathan.Adams@Sun.COM 	sysdc_maxpri = maxclsyspri;
404*11173SJonathan.Adams@Sun.COM 
405*11173SJonathan.Adams@Sun.COM 	/* break parameters */
406*11173SJonathan.Adams@Sun.COM 	if (sysdc_max_pset_DC > SYSDC_DC_MAX) {
407*11173SJonathan.Adams@Sun.COM 		sysdc_max_pset_DC = SYSDC_DC_MAX;
408*11173SJonathan.Adams@Sun.COM 	}
409*11173SJonathan.Adams@Sun.COM 	sysdc_break_ticks = MSECTOTICKS(sysdc_break_msec);
410*11173SJonathan.Adams@Sun.COM 	sysdc_break_updates = HOWMANY(sysdc_break_ticks, sysdc_update_ticks);
411*11173SJonathan.Adams@Sun.COM 
412*11173SJonathan.Adams@Sun.COM 	/*
413*11173SJonathan.Adams@Sun.COM 	 * We want:
414*11173SJonathan.Adams@Sun.COM 	 *
415*11173SJonathan.Adams@Sun.COM 	 *	sysdc_max_pset_DC = (nobreak / (break + nobreak))
416*11173SJonathan.Adams@Sun.COM 	 *
417*11173SJonathan.Adams@Sun.COM 	 *	==>	  nobreak = sysdc_max_pset_DC * (break + nobreak)
418*11173SJonathan.Adams@Sun.COM 	 *
419*11173SJonathan.Adams@Sun.COM 	 *			    sysdc_max_pset_DC * break
420*11173SJonathan.Adams@Sun.COM 	 *	==>	  nobreak = -------------------------
421*11173SJonathan.Adams@Sun.COM 	 *			    1 - sysdc_max_pset_DC
422*11173SJonathan.Adams@Sun.COM 	 */
423*11173SJonathan.Adams@Sun.COM 	sysdc_nobreak_updates =
424*11173SJonathan.Adams@Sun.COM 	    HOWMANY((uint64_t)sysdc_break_updates * sysdc_max_pset_DC,
425*11173SJonathan.Adams@Sun.COM 	    (SYSDC_DC_MAX - sysdc_max_pset_DC));
426*11173SJonathan.Adams@Sun.COM 
427*11173SJonathan.Adams@Sun.COM 	sysdc_param_init = 1;
428*11173SJonathan.Adams@Sun.COM }
429*11173SJonathan.Adams@Sun.COM 
430*11173SJonathan.Adams@Sun.COM #undef HOWMANY
431*11173SJonathan.Adams@Sun.COM #undef MSECTOTICKS
432*11173SJonathan.Adams@Sun.COM 
433*11173SJonathan.Adams@Sun.COM #define	SDC_UPDATE_INITIAL	0x1	/* for the initial update */
434*11173SJonathan.Adams@Sun.COM #define	SDC_UPDATE_TIMEOUT	0x2	/* from sysdc_update() */
435*11173SJonathan.Adams@Sun.COM #define	SDC_UPDATE_TICK		0x4	/* from sysdc_tick(), on expiry */
436*11173SJonathan.Adams@Sun.COM 
437*11173SJonathan.Adams@Sun.COM /*
438*11173SJonathan.Adams@Sun.COM  * Updates the recorded times in the sdc, and returns the elapsed ONPROC
439*11173SJonathan.Adams@Sun.COM  * and Runnable times since the last reset.
440*11173SJonathan.Adams@Sun.COM  *
441*11173SJonathan.Adams@Sun.COM  * newO is the thread's actual ONPROC time; it's used during sysdc_update()
442*11173SJonathan.Adams@Sun.COM  * to track processor set usage.
443*11173SJonathan.Adams@Sun.COM  */
444*11173SJonathan.Adams@Sun.COM static void
445*11173SJonathan.Adams@Sun.COM sysdc_update_times(sysdc_t *sdc, uint_t flags,
446*11173SJonathan.Adams@Sun.COM     hrtime_t *O, hrtime_t *R, hrtime_t *newO)
447*11173SJonathan.Adams@Sun.COM {
448*11173SJonathan.Adams@Sun.COM 	kthread_t *const t = sdc->sdc_thread;
449*11173SJonathan.Adams@Sun.COM 	const uint_t	initial = (flags & SDC_UPDATE_INITIAL);
450*11173SJonathan.Adams@Sun.COM 	const uint_t	update = (flags & SDC_UPDATE_TIMEOUT);
451*11173SJonathan.Adams@Sun.COM 	const clock_t	now = ddi_get_lbolt();
452*11173SJonathan.Adams@Sun.COM 	uint_t		do_reset;
453*11173SJonathan.Adams@Sun.COM 
454*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
455*11173SJonathan.Adams@Sun.COM 
456*11173SJonathan.Adams@Sun.COM 	*O = *R = 0;
457*11173SJonathan.Adams@Sun.COM 
458*11173SJonathan.Adams@Sun.COM 	/* If we've been sleeping, we know we haven't had any ONPROC time. */
459*11173SJonathan.Adams@Sun.COM 	if (sdc->sdc_sleep_updates != 0 &&
460*11173SJonathan.Adams@Sun.COM 	    sdc->sdc_sleep_updates != sdc->sdc_nupdates) {
461*11173SJonathan.Adams@Sun.COM 		*newO = sdc->sdc_last_base_O;
462*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_times_asleep);
463*11173SJonathan.Adams@Sun.COM 		return;
464*11173SJonathan.Adams@Sun.COM 	}
465*11173SJonathan.Adams@Sun.COM 
466*11173SJonathan.Adams@Sun.COM 	/*
467*11173SJonathan.Adams@Sun.COM 	 * If this is our first update, or we've hit the reset point,
468*11173SJonathan.Adams@Sun.COM 	 * we need to reset our base_{O,R}.  Once we've updated them, we
469*11173SJonathan.Adams@Sun.COM 	 * report O and R for the entire prior interval.
470*11173SJonathan.Adams@Sun.COM 	 */
471*11173SJonathan.Adams@Sun.COM 	do_reset = initial;
472*11173SJonathan.Adams@Sun.COM 	if (update) {
473*11173SJonathan.Adams@Sun.COM 		++sdc->sdc_nupdates;
474*11173SJonathan.Adams@Sun.COM 		if ((sdc->sdc_nupdates % sysdc_reset_updates) == 0)
475*11173SJonathan.Adams@Sun.COM 			do_reset = 1;
476*11173SJonathan.Adams@Sun.COM 	}
477*11173SJonathan.Adams@Sun.COM 	if (do_reset) {
478*11173SJonathan.Adams@Sun.COM 		hrtime_t baseO, baseR;
479*11173SJonathan.Adams@Sun.COM 		if (initial) {
480*11173SJonathan.Adams@Sun.COM 			/*
481*11173SJonathan.Adams@Sun.COM 			 * Start off our cycle count somewhere in the middle,
482*11173SJonathan.Adams@Sun.COM 			 * to keep the resets from all happening at once.
483*11173SJonathan.Adams@Sun.COM 			 *
484*11173SJonathan.Adams@Sun.COM 			 * 4999 is a handy prime much larger than
485*11173SJonathan.Adams@Sun.COM 			 * sysdc_reset_updates, so that we don't run into
486*11173SJonathan.Adams@Sun.COM 			 * trouble if the resolution is a multiple of
487*11173SJonathan.Adams@Sun.COM 			 * sysdc_reset_updates.
488*11173SJonathan.Adams@Sun.COM 			 */
489*11173SJonathan.Adams@Sun.COM 			sdc->sdc_nupdates = (uint_t)((gethrtime() % 4999) %
490*11173SJonathan.Adams@Sun.COM 			    sysdc_reset_updates);
491*11173SJonathan.Adams@Sun.COM 			baseO = baseR = 0;
492*11173SJonathan.Adams@Sun.COM 		} else {
493*11173SJonathan.Adams@Sun.COM 			baseO = sdc->sdc_base_O;
494*11173SJonathan.Adams@Sun.COM 			baseR = sdc->sdc_base_R;
495*11173SJonathan.Adams@Sun.COM 		}
496*11173SJonathan.Adams@Sun.COM 
497*11173SJonathan.Adams@Sun.COM 		mstate_systhread_times(t, &sdc->sdc_base_O, &sdc->sdc_base_R);
498*11173SJonathan.Adams@Sun.COM 		*newO = sdc->sdc_base_O;
499*11173SJonathan.Adams@Sun.COM 
500*11173SJonathan.Adams@Sun.COM 		sdc->sdc_reset = now;
501*11173SJonathan.Adams@Sun.COM 		sdc->sdc_pri_check = -1; /* force mismatch below */
502*11173SJonathan.Adams@Sun.COM 
503*11173SJonathan.Adams@Sun.COM 		/*
504*11173SJonathan.Adams@Sun.COM 		 * See below for rationale.
505*11173SJonathan.Adams@Sun.COM 		 */
506*11173SJonathan.Adams@Sun.COM 		if (baseO > sdc->sdc_base_O || baseR > sdc->sdc_base_R) {
507*11173SJonathan.Adams@Sun.COM 			SYSDC_INC_STAT(sysdc_update_times_base_ran_backwards);
508*11173SJonathan.Adams@Sun.COM 			baseO = sdc->sdc_base_O;
509*11173SJonathan.Adams@Sun.COM 			baseR = sdc->sdc_base_R;
510*11173SJonathan.Adams@Sun.COM 		}
511*11173SJonathan.Adams@Sun.COM 
512*11173SJonathan.Adams@Sun.COM 		/* compute based on the entire interval */
513*11173SJonathan.Adams@Sun.COM 		*O = (sdc->sdc_base_O - baseO);
514*11173SJonathan.Adams@Sun.COM 		*R = (sdc->sdc_base_R - baseR);
515*11173SJonathan.Adams@Sun.COM 		return;
516*11173SJonathan.Adams@Sun.COM 	}
517*11173SJonathan.Adams@Sun.COM 
518*11173SJonathan.Adams@Sun.COM 	/*
519*11173SJonathan.Adams@Sun.COM 	 * If we're called from sysdc_update(), we *must* return a value
520*11173SJonathan.Adams@Sun.COM 	 * for newO, so we always call mstate_systhread_times().
521*11173SJonathan.Adams@Sun.COM 	 *
522*11173SJonathan.Adams@Sun.COM 	 * Otherwise, if we've already done a pri check this tick,
523*11173SJonathan.Adams@Sun.COM 	 * we can skip it.
524*11173SJonathan.Adams@Sun.COM 	 */
525*11173SJonathan.Adams@Sun.COM 	if (!update && sdc->sdc_pri_check == now) {
526*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_times_already_done);
527*11173SJonathan.Adams@Sun.COM 		return;
528*11173SJonathan.Adams@Sun.COM 	}
529*11173SJonathan.Adams@Sun.COM 
530*11173SJonathan.Adams@Sun.COM 	/* Get the current times from the thread */
531*11173SJonathan.Adams@Sun.COM 	sdc->sdc_pri_check = now;
532*11173SJonathan.Adams@Sun.COM 	mstate_systhread_times(t, &sdc->sdc_cur_O, &sdc->sdc_cur_R);
533*11173SJonathan.Adams@Sun.COM 	*newO = sdc->sdc_cur_O;
534*11173SJonathan.Adams@Sun.COM 
535*11173SJonathan.Adams@Sun.COM 	/*
536*11173SJonathan.Adams@Sun.COM 	 * The updating of microstate accounting is not done under a
537*11173SJonathan.Adams@Sun.COM 	 * consistent set of locks, particularly the t_waitrq field.  This
538*11173SJonathan.Adams@Sun.COM 	 * can lead to narrow windows in which we account for time in the
539*11173SJonathan.Adams@Sun.COM 	 * wrong bucket, which on the next read will be accounted for
540*11173SJonathan.Adams@Sun.COM 	 * correctly.
541*11173SJonathan.Adams@Sun.COM 	 *
542*11173SJonathan.Adams@Sun.COM 	 * If our sdc_base_* fields were affected by one of these blips, we
543*11173SJonathan.Adams@Sun.COM 	 * throw away the old data, and pretend this tick didn't happen.
544*11173SJonathan.Adams@Sun.COM 	 */
545*11173SJonathan.Adams@Sun.COM 	if (sdc->sdc_cur_O < sdc->sdc_base_O ||
546*11173SJonathan.Adams@Sun.COM 	    sdc->sdc_cur_R < sdc->sdc_base_R) {
547*11173SJonathan.Adams@Sun.COM 
548*11173SJonathan.Adams@Sun.COM 		sdc->sdc_base_O = sdc->sdc_cur_O;
549*11173SJonathan.Adams@Sun.COM 		sdc->sdc_base_R = sdc->sdc_cur_R;
550*11173SJonathan.Adams@Sun.COM 
551*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_times_cur_ran_backwards);
552*11173SJonathan.Adams@Sun.COM 		return;
553*11173SJonathan.Adams@Sun.COM 	}
554*11173SJonathan.Adams@Sun.COM 
555*11173SJonathan.Adams@Sun.COM 	*O = sdc->sdc_cur_O - sdc->sdc_base_O;
556*11173SJonathan.Adams@Sun.COM 	*R = sdc->sdc_cur_R - sdc->sdc_base_R;
557*11173SJonathan.Adams@Sun.COM }
558*11173SJonathan.Adams@Sun.COM 
559*11173SJonathan.Adams@Sun.COM /*
560*11173SJonathan.Adams@Sun.COM  * sysdc_compute_pri()
561*11173SJonathan.Adams@Sun.COM  *
562*11173SJonathan.Adams@Sun.COM  *	Recomputes the priority of the thread, leaving the result in
563*11173SJonathan.Adams@Sun.COM  *	sdc->sdc_epri.  Returns 1 if a priority update should occur
564*11173SJonathan.Adams@Sun.COM  *	(which will also trigger a cpu_surrender()), otherwise
565*11173SJonathan.Adams@Sun.COM  *	returns 0.
566*11173SJonathan.Adams@Sun.COM  */
567*11173SJonathan.Adams@Sun.COM static uint_t
568*11173SJonathan.Adams@Sun.COM sysdc_compute_pri(sysdc_t *sdc, uint_t flags)
569*11173SJonathan.Adams@Sun.COM {
570*11173SJonathan.Adams@Sun.COM 	kthread_t *const t = sdc->sdc_thread;
571*11173SJonathan.Adams@Sun.COM 	const uint_t	update = (flags & SDC_UPDATE_TIMEOUT);
572*11173SJonathan.Adams@Sun.COM 	const uint_t	tick = (flags & SDC_UPDATE_TICK);
573*11173SJonathan.Adams@Sun.COM 
574*11173SJonathan.Adams@Sun.COM 	hrtime_t	O, R;
575*11173SJonathan.Adams@Sun.COM 	hrtime_t	newO = -1;
576*11173SJonathan.Adams@Sun.COM 
577*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
578*11173SJonathan.Adams@Sun.COM 
579*11173SJonathan.Adams@Sun.COM 	sysdc_update_times(sdc, flags, &O, &R, &newO);
580*11173SJonathan.Adams@Sun.COM 	ASSERT(!update || newO != -1);
581*11173SJonathan.Adams@Sun.COM 
582*11173SJonathan.Adams@Sun.COM 	/* If we have new data, recompute our priority. */
583*11173SJonathan.Adams@Sun.COM 	if ((O + R) != 0) {
584*11173SJonathan.Adams@Sun.COM 		sdc->sdc_cur_DC = (O * SYSDC_DC_MAX) / (O + R);
585*11173SJonathan.Adams@Sun.COM 
586*11173SJonathan.Adams@Sun.COM 		/* Adjust our priority to move our DC closer to the target. */
587*11173SJonathan.Adams@Sun.COM 		if (sdc->sdc_cur_DC < sdc->sdc_target_DC)
588*11173SJonathan.Adams@Sun.COM 			sdc->sdc_pri = sdc->sdc_maxpri;
589*11173SJonathan.Adams@Sun.COM 		else
590*11173SJonathan.Adams@Sun.COM 			sdc->sdc_pri = sdc->sdc_minpri;
591*11173SJonathan.Adams@Sun.COM 	}
592*11173SJonathan.Adams@Sun.COM 
593*11173SJonathan.Adams@Sun.COM 	/*
594*11173SJonathan.Adams@Sun.COM 	 * If our per-pset duty cycle goes over the max, we will take a break.
595*11173SJonathan.Adams@Sun.COM 	 * This forces all sysdc threads in the pset to minimum priority, in
596*11173SJonathan.Adams@Sun.COM 	 * order to let everyone else have a chance at the CPU.
597*11173SJonathan.Adams@Sun.COM 	 */
598*11173SJonathan.Adams@Sun.COM 	if (sdc->sdc_pset->sdp_need_break) {
599*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_compute_pri_breaking);
600*11173SJonathan.Adams@Sun.COM 		sdc->sdc_epri = sdc->sdc_minpri;
601*11173SJonathan.Adams@Sun.COM 	} else {
602*11173SJonathan.Adams@Sun.COM 		sdc->sdc_epri = sdc->sdc_pri;
603*11173SJonathan.Adams@Sun.COM 	}
604*11173SJonathan.Adams@Sun.COM 
605*11173SJonathan.Adams@Sun.COM 	DTRACE_PROBE4(sysdc__compute__pri,
606*11173SJonathan.Adams@Sun.COM 	    kthread_t *, t, pri_t, sdc->sdc_epri, uint_t, sdc->sdc_cur_DC,
607*11173SJonathan.Adams@Sun.COM 	    uint_t, sdc->sdc_target_DC);
608*11173SJonathan.Adams@Sun.COM 
609*11173SJonathan.Adams@Sun.COM 	/*
610*11173SJonathan.Adams@Sun.COM 	 * For sysdc_update(), we compute the ONPROC time for high-priority
611*11173SJonathan.Adams@Sun.COM 	 * threads, which is used to calculate the per-pset duty cycle.  We
612*11173SJonathan.Adams@Sun.COM 	 * will always tell our callers to update the thread's priority,
613*11173SJonathan.Adams@Sun.COM 	 * since we want to force a cpu_surrender().
614*11173SJonathan.Adams@Sun.COM 	 *
615*11173SJonathan.Adams@Sun.COM 	 * We reset sdc_update_ticks so that sysdc_tick() will only update
616*11173SJonathan.Adams@Sun.COM 	 * the thread's priority if our timeout is delayed by a tick or
617*11173SJonathan.Adams@Sun.COM 	 * more.
618*11173SJonathan.Adams@Sun.COM 	 */
619*11173SJonathan.Adams@Sun.COM 	if (update) {
620*11173SJonathan.Adams@Sun.COM 		/* SDC threads are not allowed to change cpupart bindings. */
621*11173SJonathan.Adams@Sun.COM 		ASSERT(t->t_cpupart == sdc->sdc_pset->sdp_cpupart);
622*11173SJonathan.Adams@Sun.COM 
623*11173SJonathan.Adams@Sun.COM 		/* If we were at MAXPRI, account for our onproc time. */
624*11173SJonathan.Adams@Sun.COM 		if (t->t_pri == sdc->sdc_maxpri &&
625*11173SJonathan.Adams@Sun.COM 		    sdc->sdc_last_base_O != 0 &&
626*11173SJonathan.Adams@Sun.COM 		    sdc->sdc_last_base_O < newO) {
627*11173SJonathan.Adams@Sun.COM 			sdc->sdc_last_O = newO - sdc->sdc_last_base_O;
628*11173SJonathan.Adams@Sun.COM 			sdc->sdc_pset->sdp_onproc_time +=
629*11173SJonathan.Adams@Sun.COM 			    (uint64_t)sdc->sdc_last_O;
630*11173SJonathan.Adams@Sun.COM 			sdc->sdc_pset->sdp_onproc_threads++;
631*11173SJonathan.Adams@Sun.COM 		} else {
632*11173SJonathan.Adams@Sun.COM 			sdc->sdc_last_O = 0;
633*11173SJonathan.Adams@Sun.COM 		}
634*11173SJonathan.Adams@Sun.COM 		sdc->sdc_last_base_O = newO;
635*11173SJonathan.Adams@Sun.COM 
636*11173SJonathan.Adams@Sun.COM 		sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks + 1;
637*11173SJonathan.Adams@Sun.COM 		return (1);
638*11173SJonathan.Adams@Sun.COM 	}
639*11173SJonathan.Adams@Sun.COM 
640*11173SJonathan.Adams@Sun.COM 	/*
641*11173SJonathan.Adams@Sun.COM 	 * Like sysdc_update(), sysdc_tick() always wants to update the
642*11173SJonathan.Adams@Sun.COM 	 * thread's priority, so that the CPU is surrendered if necessary.
643*11173SJonathan.Adams@Sun.COM 	 * We reset sdc_update_ticks so that if the timeout continues to be
644*11173SJonathan.Adams@Sun.COM 	 * delayed, we'll update at the regular interval.
645*11173SJonathan.Adams@Sun.COM 	 */
646*11173SJonathan.Adams@Sun.COM 	if (tick) {
647*11173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_ticks == sdc->sdc_update_ticks);
648*11173SJonathan.Adams@Sun.COM 		sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks;
649*11173SJonathan.Adams@Sun.COM 		return (1);
650*11173SJonathan.Adams@Sun.COM 	}
651*11173SJonathan.Adams@Sun.COM 
652*11173SJonathan.Adams@Sun.COM 	/*
653*11173SJonathan.Adams@Sun.COM 	 * Otherwise, only tell our callers to update the priority if it has
654*11173SJonathan.Adams@Sun.COM 	 * changed.
655*11173SJonathan.Adams@Sun.COM 	 */
656*11173SJonathan.Adams@Sun.COM 	return (sdc->sdc_epri != t->t_pri);
657*11173SJonathan.Adams@Sun.COM }
658*11173SJonathan.Adams@Sun.COM 
659*11173SJonathan.Adams@Sun.COM static void
660*11173SJonathan.Adams@Sun.COM sysdc_update_pri(sysdc_t *sdc, uint_t flags)
661*11173SJonathan.Adams@Sun.COM {
662*11173SJonathan.Adams@Sun.COM 	kthread_t *t = sdc->sdc_thread;
663*11173SJonathan.Adams@Sun.COM 
664*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
665*11173SJonathan.Adams@Sun.COM 
666*11173SJonathan.Adams@Sun.COM 	if (sysdc_compute_pri(sdc, flags)) {
667*11173SJonathan.Adams@Sun.COM 		if (!thread_change_pri(t, sdc->sdc_epri, 0)) {
668*11173SJonathan.Adams@Sun.COM 			cpu_surrender(t);
669*11173SJonathan.Adams@Sun.COM 		}
670*11173SJonathan.Adams@Sun.COM 	}
671*11173SJonathan.Adams@Sun.COM }
672*11173SJonathan.Adams@Sun.COM 
673*11173SJonathan.Adams@Sun.COM /*
674*11173SJonathan.Adams@Sun.COM  * Add a thread onto the active list.  It will only be removed by
675*11173SJonathan.Adams@Sun.COM  * sysdc_update().
676*11173SJonathan.Adams@Sun.COM  */
677*11173SJonathan.Adams@Sun.COM static void
678*11173SJonathan.Adams@Sun.COM sysdc_activate(sysdc_t *sdc)
679*11173SJonathan.Adams@Sun.COM {
680*11173SJonathan.Adams@Sun.COM 	sysdc_t *volatile *headp = &SYSDC_LIST(sdc)->sdl_list;
681*11173SJonathan.Adams@Sun.COM 	sysdc_t		*head;
682*11173SJonathan.Adams@Sun.COM 	kthread_t	*t = sdc->sdc_thread;
683*11173SJonathan.Adams@Sun.COM 
684*11173SJonathan.Adams@Sun.COM 	SYSDC_INC_STAT(sysdc_activate_enter);
685*11173SJonathan.Adams@Sun.COM 
686*11173SJonathan.Adams@Sun.COM 	ASSERT(sdc->sdc_next == NULL);
687*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
688*11173SJonathan.Adams@Sun.COM 
689*11173SJonathan.Adams@Sun.COM 	do {
690*11173SJonathan.Adams@Sun.COM 		head = *headp;
691*11173SJonathan.Adams@Sun.COM 		sdc->sdc_next = head;
692*11173SJonathan.Adams@Sun.COM 	} while (atomic_cas_ptr(headp, head, sdc) != head);
693*11173SJonathan.Adams@Sun.COM }
694*11173SJonathan.Adams@Sun.COM 
695*11173SJonathan.Adams@Sun.COM /*
696*11173SJonathan.Adams@Sun.COM  * sysdc_update() has two jobs:
697*11173SJonathan.Adams@Sun.COM  *
698*11173SJonathan.Adams@Sun.COM  *	1. It updates the priorities of all active SDC threads on the system.
699*11173SJonathan.Adams@Sun.COM  *	2. It measures pset CPU usage and enforces sysdc_max_pset_DC.
700*11173SJonathan.Adams@Sun.COM  */
701*11173SJonathan.Adams@Sun.COM static void
702*11173SJonathan.Adams@Sun.COM sysdc_update(void *arg)
703*11173SJonathan.Adams@Sun.COM {
704*11173SJonathan.Adams@Sun.COM 	int		idx;
705*11173SJonathan.Adams@Sun.COM 	sysdc_t		*freelist = NULL;
706*11173SJonathan.Adams@Sun.COM 	sysdc_pset_t	*cur;
707*11173SJonathan.Adams@Sun.COM 	hrtime_t	now, diff;
708*11173SJonathan.Adams@Sun.COM 	uint_t		redeploy = 1;
709*11173SJonathan.Adams@Sun.COM 
710*11173SJonathan.Adams@Sun.COM 	SYSDC_INC_STAT(sysdc_update_enter);
711*11173SJonathan.Adams@Sun.COM 
712*11173SJonathan.Adams@Sun.COM 	ASSERT(sysdc_update_timeout_started);
713*11173SJonathan.Adams@Sun.COM 
714*11173SJonathan.Adams@Sun.COM 	/*
715*11173SJonathan.Adams@Sun.COM 	 * If this is our first time through, diff will be gigantic, and
716*11173SJonathan.Adams@Sun.COM 	 * no breaks will be necessary.
717*11173SJonathan.Adams@Sun.COM 	 */
718*11173SJonathan.Adams@Sun.COM 	now = gethrtime();
719*11173SJonathan.Adams@Sun.COM 	diff = now - sysdc_last_update;
720*11173SJonathan.Adams@Sun.COM 	sysdc_last_update = now;
721*11173SJonathan.Adams@Sun.COM 
722*11173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
723*11173SJonathan.Adams@Sun.COM 	for (cur = list_head(&sysdc_psets); cur != NULL;
724*11173SJonathan.Adams@Sun.COM 	    cur = list_next(&sysdc_psets, cur)) {
725*11173SJonathan.Adams@Sun.COM 		boolean_t breaking = (cur->sdp_should_break != 0);
726*11173SJonathan.Adams@Sun.COM 
727*11173SJonathan.Adams@Sun.COM 		if (cur->sdp_need_break != breaking) {
728*11173SJonathan.Adams@Sun.COM 			DTRACE_PROBE2(sdc__pset__break, sysdc_pset_t *, cur,
729*11173SJonathan.Adams@Sun.COM 			    boolean_t, breaking);
730*11173SJonathan.Adams@Sun.COM 		}
731*11173SJonathan.Adams@Sun.COM 		cur->sdp_onproc_time = 0;
732*11173SJonathan.Adams@Sun.COM 		cur->sdp_onproc_threads = 0;
733*11173SJonathan.Adams@Sun.COM 		cur->sdp_need_break = breaking;
734*11173SJonathan.Adams@Sun.COM 	}
735*11173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
736*11173SJonathan.Adams@Sun.COM 
737*11173SJonathan.Adams@Sun.COM 	for (idx = 0; idx < SYSDC_NLISTS; idx++) {
738*11173SJonathan.Adams@Sun.COM 		sysdc_list_t		*sdl = &sysdc_active[idx];
739*11173SJonathan.Adams@Sun.COM 		sysdc_t *volatile	*headp = &sdl->sdl_list;
740*11173SJonathan.Adams@Sun.COM 		sysdc_t			*head, *tail;
741*11173SJonathan.Adams@Sun.COM 		sysdc_t			**prevptr;
742*11173SJonathan.Adams@Sun.COM 
743*11173SJonathan.Adams@Sun.COM 		if (*headp == &sysdc_dummy)
744*11173SJonathan.Adams@Sun.COM 			continue;
745*11173SJonathan.Adams@Sun.COM 
746*11173SJonathan.Adams@Sun.COM 		/* Prevent any threads from exiting while we're poking them. */
747*11173SJonathan.Adams@Sun.COM 		mutex_enter(&sdl->sdl_lock);
748*11173SJonathan.Adams@Sun.COM 
749*11173SJonathan.Adams@Sun.COM 		/*
750*11173SJonathan.Adams@Sun.COM 		 * Each sdl_list contains a singly-linked list of active
751*11173SJonathan.Adams@Sun.COM 		 * threads. Threads which become active while we are
752*11173SJonathan.Adams@Sun.COM 		 * processing the list will be added to sdl_list.  Since we
753*11173SJonathan.Adams@Sun.COM 		 * don't want that to interfere with our own processing, we
754*11173SJonathan.Adams@Sun.COM 		 * swap in an empty list.  Any newly active threads will
755*11173SJonathan.Adams@Sun.COM 		 * go on to this empty list.  When finished, we'll put any
756*11173SJonathan.Adams@Sun.COM 		 * such threads at the end of the processed list.
757*11173SJonathan.Adams@Sun.COM 		 */
758*11173SJonathan.Adams@Sun.COM 		head = atomic_swap_ptr(headp, &sysdc_dummy);
759*11173SJonathan.Adams@Sun.COM 		prevptr = &head;
760*11173SJonathan.Adams@Sun.COM 		while (*prevptr != &sysdc_dummy) {
761*11173SJonathan.Adams@Sun.COM 			sysdc_t		*const	sdc = *prevptr;
762*11173SJonathan.Adams@Sun.COM 			kthread_t	*const	t = sdc->sdc_thread;
763*11173SJonathan.Adams@Sun.COM 
764*11173SJonathan.Adams@Sun.COM 			/*
765*11173SJonathan.Adams@Sun.COM 			 * If the thread has exited, move its sysdc_t onto
766*11173SJonathan.Adams@Sun.COM 			 * freelist, to be freed later.
767*11173SJonathan.Adams@Sun.COM 			 */
768*11173SJonathan.Adams@Sun.COM 			if (t == NULL) {
769*11173SJonathan.Adams@Sun.COM 				*prevptr = sdc->sdc_next;
770*11173SJonathan.Adams@Sun.COM 				SYSDC_INC_STAT(sysdc_update_exited);
771*11173SJonathan.Adams@Sun.COM 				sdc->sdc_next = freelist;
772*11173SJonathan.Adams@Sun.COM 				freelist = sdc;
773*11173SJonathan.Adams@Sun.COM 				continue;
774*11173SJonathan.Adams@Sun.COM 			}
775*11173SJonathan.Adams@Sun.COM 
776*11173SJonathan.Adams@Sun.COM 			thread_lock(t);
777*11173SJonathan.Adams@Sun.COM 			if (t->t_cid != sysdccid) {
778*11173SJonathan.Adams@Sun.COM 				thread_unlock(t);
779*11173SJonathan.Adams@Sun.COM 				prevptr = &sdc->sdc_next;
780*11173SJonathan.Adams@Sun.COM 				SYSDC_INC_STAT(sysdc_update_not_sdc);
781*11173SJonathan.Adams@Sun.COM 				continue;
782*11173SJonathan.Adams@Sun.COM 			}
783*11173SJonathan.Adams@Sun.COM 			ASSERT(t->t_cldata == sdc);
784*11173SJonathan.Adams@Sun.COM 
785*11173SJonathan.Adams@Sun.COM 			/*
786*11173SJonathan.Adams@Sun.COM 			 * If the thread has been sleeping for longer
787*11173SJonathan.Adams@Sun.COM 			 * than sysdc_prune_interval, make it inactive by
788*11173SJonathan.Adams@Sun.COM 			 * removing it from the list.
789*11173SJonathan.Adams@Sun.COM 			 */
790*11173SJonathan.Adams@Sun.COM 			if (!(t->t_state & (TS_RUN | TS_ONPROC)) &&
791*11173SJonathan.Adams@Sun.COM 			    sdc->sdc_sleep_updates != 0 &&
792*11173SJonathan.Adams@Sun.COM 			    (sdc->sdc_sleep_updates - sdc->sdc_nupdates) >
793*11173SJonathan.Adams@Sun.COM 			    sysdc_prune_updates) {
794*11173SJonathan.Adams@Sun.COM 				*prevptr = sdc->sdc_next;
795*11173SJonathan.Adams@Sun.COM 				SYSDC_INC_STAT(sysdc_update_idle);
796*11173SJonathan.Adams@Sun.COM 				sdc->sdc_next = NULL;
797*11173SJonathan.Adams@Sun.COM 				thread_unlock(t);
798*11173SJonathan.Adams@Sun.COM 				continue;
799*11173SJonathan.Adams@Sun.COM 			}
800*11173SJonathan.Adams@Sun.COM 			sysdc_update_pri(sdc, SDC_UPDATE_TIMEOUT);
801*11173SJonathan.Adams@Sun.COM 			thread_unlock(t);
802*11173SJonathan.Adams@Sun.COM 
803*11173SJonathan.Adams@Sun.COM 			prevptr = &sdc->sdc_next;
804*11173SJonathan.Adams@Sun.COM 		}
805*11173SJonathan.Adams@Sun.COM 
806*11173SJonathan.Adams@Sun.COM 		/*
807*11173SJonathan.Adams@Sun.COM 		 * Add our list to the bucket, putting any new entries
808*11173SJonathan.Adams@Sun.COM 		 * added while we were working at the tail of the list.
809*11173SJonathan.Adams@Sun.COM 		 */
810*11173SJonathan.Adams@Sun.COM 		do {
811*11173SJonathan.Adams@Sun.COM 			tail = *headp;
812*11173SJonathan.Adams@Sun.COM 			*prevptr = tail;
813*11173SJonathan.Adams@Sun.COM 		} while (atomic_cas_ptr(headp, tail, head) != tail);
814*11173SJonathan.Adams@Sun.COM 
815*11173SJonathan.Adams@Sun.COM 		mutex_exit(&sdl->sdl_lock);
816*11173SJonathan.Adams@Sun.COM 	}
817*11173SJonathan.Adams@Sun.COM 
818*11173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
819*11173SJonathan.Adams@Sun.COM 	for (cur = list_head(&sysdc_psets); cur != NULL;
820*11173SJonathan.Adams@Sun.COM 	    cur = list_next(&sysdc_psets, cur)) {
821*11173SJonathan.Adams@Sun.COM 
822*11173SJonathan.Adams@Sun.COM 		cur->sdp_vtime_last_interval =
823*11173SJonathan.Adams@Sun.COM 		    diff * cur->sdp_cpupart->cp_ncpus;
824*11173SJonathan.Adams@Sun.COM 		cur->sdp_DC_last_interval =
825*11173SJonathan.Adams@Sun.COM 		    (cur->sdp_onproc_time * SYSDC_DC_MAX) /
826*11173SJonathan.Adams@Sun.COM 		    cur->sdp_vtime_last_interval;
827*11173SJonathan.Adams@Sun.COM 
828*11173SJonathan.Adams@Sun.COM 		if (cur->sdp_should_break > 0) {
829*11173SJonathan.Adams@Sun.COM 			cur->sdp_should_break--;	/* breaking */
830*11173SJonathan.Adams@Sun.COM 			continue;
831*11173SJonathan.Adams@Sun.COM 		}
832*11173SJonathan.Adams@Sun.COM 		if (cur->sdp_dont_break > 0) {
833*11173SJonathan.Adams@Sun.COM 			cur->sdp_dont_break--;	/* waiting before checking */
834*11173SJonathan.Adams@Sun.COM 			continue;
835*11173SJonathan.Adams@Sun.COM 		}
836*11173SJonathan.Adams@Sun.COM 		if (cur->sdp_DC_last_interval > sysdc_max_pset_DC) {
837*11173SJonathan.Adams@Sun.COM 			cur->sdp_should_break = sysdc_break_updates;
838*11173SJonathan.Adams@Sun.COM 			cur->sdp_dont_break = sysdc_nobreak_updates;
839*11173SJonathan.Adams@Sun.COM 			SYSDC_INC_STAT(sysdc_update_take_break);
840*11173SJonathan.Adams@Sun.COM 		}
841*11173SJonathan.Adams@Sun.COM 	}
842*11173SJonathan.Adams@Sun.COM 
843*11173SJonathan.Adams@Sun.COM 	/*
844*11173SJonathan.Adams@Sun.COM 	 * If there are no sysdc_psets, there can be no threads, so
845*11173SJonathan.Adams@Sun.COM 	 * we can stop doing our timeout.  Since we're holding the
846*11173SJonathan.Adams@Sun.COM 	 * sysdc_pset_lock, no new sysdc_psets can come in, which will
847*11173SJonathan.Adams@Sun.COM 	 * prevent anyone from racing with this and dropping our timeout
848*11173SJonathan.Adams@Sun.COM 	 * on the floor.
849*11173SJonathan.Adams@Sun.COM 	 */
850*11173SJonathan.Adams@Sun.COM 	if (list_is_empty(&sysdc_psets)) {
851*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_no_psets);
852*11173SJonathan.Adams@Sun.COM 		ASSERT(sysdc_update_timeout_started);
853*11173SJonathan.Adams@Sun.COM 		sysdc_update_timeout_started = 0;
854*11173SJonathan.Adams@Sun.COM 
855*11173SJonathan.Adams@Sun.COM 		redeploy = 0;
856*11173SJonathan.Adams@Sun.COM 	}
857*11173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
858*11173SJonathan.Adams@Sun.COM 
859*11173SJonathan.Adams@Sun.COM 	while (freelist != NULL) {
860*11173SJonathan.Adams@Sun.COM 		sysdc_t *cur = freelist;
861*11173SJonathan.Adams@Sun.COM 		freelist = cur->sdc_next;
862*11173SJonathan.Adams@Sun.COM 		kmem_free(cur, sizeof (*cur));
863*11173SJonathan.Adams@Sun.COM 	}
864*11173SJonathan.Adams@Sun.COM 
865*11173SJonathan.Adams@Sun.COM 	if (redeploy) {
866*11173SJonathan.Adams@Sun.COM 		(void) timeout(sysdc_update, arg, sysdc_update_ticks);
867*11173SJonathan.Adams@Sun.COM 	}
868*11173SJonathan.Adams@Sun.COM }
869*11173SJonathan.Adams@Sun.COM 
870*11173SJonathan.Adams@Sun.COM static void
871*11173SJonathan.Adams@Sun.COM sysdc_preempt(kthread_t *t)
872*11173SJonathan.Adams@Sun.COM {
873*11173SJonathan.Adams@Sun.COM 	ASSERT(t == curthread);
874*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
875*11173SJonathan.Adams@Sun.COM 
876*11173SJonathan.Adams@Sun.COM 	setbackdq(t);		/* give others a chance to run */
877*11173SJonathan.Adams@Sun.COM }
878*11173SJonathan.Adams@Sun.COM 
879*11173SJonathan.Adams@Sun.COM static void
880*11173SJonathan.Adams@Sun.COM sysdc_tick(kthread_t *t)
881*11173SJonathan.Adams@Sun.COM {
882*11173SJonathan.Adams@Sun.COM 	sysdc_t *sdc;
883*11173SJonathan.Adams@Sun.COM 
884*11173SJonathan.Adams@Sun.COM 	thread_lock(t);
885*11173SJonathan.Adams@Sun.COM 	if (t->t_cid != sysdccid) {
886*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_tick_not_sdc);
887*11173SJonathan.Adams@Sun.COM 		thread_unlock(t);
888*11173SJonathan.Adams@Sun.COM 		return;
889*11173SJonathan.Adams@Sun.COM 	}
890*11173SJonathan.Adams@Sun.COM 	sdc = t->t_cldata;
891*11173SJonathan.Adams@Sun.COM 	if (t->t_state == TS_ONPROC &&
892*11173SJonathan.Adams@Sun.COM 	    t->t_pri < t->t_disp_queue->disp_maxrunpri) {
893*11173SJonathan.Adams@Sun.COM 		cpu_surrender(t);
894*11173SJonathan.Adams@Sun.COM 	}
895*11173SJonathan.Adams@Sun.COM 
896*11173SJonathan.Adams@Sun.COM 	if (t->t_state == TS_ONPROC || t->t_state == TS_RUN) {
897*11173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_sleep_updates == 0);
898*11173SJonathan.Adams@Sun.COM 	}
899*11173SJonathan.Adams@Sun.COM 
900*11173SJonathan.Adams@Sun.COM 	ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks);
901*11173SJonathan.Adams@Sun.COM 	sdc->sdc_ticks++;
902*11173SJonathan.Adams@Sun.COM 	if (sdc->sdc_ticks == sdc->sdc_update_ticks) {
903*11173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_tick_quantum_expired);
904*11173SJonathan.Adams@Sun.COM 		sysdc_update_pri(sdc, SDC_UPDATE_TICK);
905*11173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks);
906*11173SJonathan.Adams@Sun.COM 	}
907*11173SJonathan.Adams@Sun.COM 	thread_unlock(t);
908*11173SJonathan.Adams@Sun.COM }
909*11173SJonathan.Adams@Sun.COM 
910*11173SJonathan.Adams@Sun.COM static void
911*11173SJonathan.Adams@Sun.COM sysdc_setrun(kthread_t *t)
912*11173SJonathan.Adams@Sun.COM {
913*11173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = t->t_cldata;
914*11173SJonathan.Adams@Sun.COM 
915*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));	/* t should be in transition */
916*11173SJonathan.Adams@Sun.COM 
917*11173SJonathan.Adams@Sun.COM 	sdc->sdc_sleep_updates = 0;
918*11173SJonathan.Adams@Sun.COM 
919*11173SJonathan.Adams@Sun.COM 	if (sdc->sdc_next == NULL) {
920*11173SJonathan.Adams@Sun.COM 		/*
921*11173SJonathan.Adams@Sun.COM 		 * Since we're in transition, we don't want to use the
922*11173SJonathan.Adams@Sun.COM 		 * full thread_update_pri().
923*11173SJonathan.Adams@Sun.COM 		 */
924*11173SJonathan.Adams@Sun.COM 		if (sysdc_compute_pri(sdc, 0)) {
925*11173SJonathan.Adams@Sun.COM 			THREAD_CHANGE_PRI(t, sdc->sdc_epri);
926*11173SJonathan.Adams@Sun.COM 		}
927*11173SJonathan.Adams@Sun.COM 		sysdc_activate(sdc);
928*11173SJonathan.Adams@Sun.COM 
929*11173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_next != NULL);
930*11173SJonathan.Adams@Sun.COM 	}
931*11173SJonathan.Adams@Sun.COM 
932*11173SJonathan.Adams@Sun.COM 	setbackdq(t);
933*11173SJonathan.Adams@Sun.COM }
934*11173SJonathan.Adams@Sun.COM 
935*11173SJonathan.Adams@Sun.COM static void
936*11173SJonathan.Adams@Sun.COM sysdc_wakeup(kthread_t *t)
937*11173SJonathan.Adams@Sun.COM {
938*11173SJonathan.Adams@Sun.COM 	sysdc_setrun(t);
939*11173SJonathan.Adams@Sun.COM }
940*11173SJonathan.Adams@Sun.COM 
941*11173SJonathan.Adams@Sun.COM static void
942*11173SJonathan.Adams@Sun.COM sysdc_sleep(kthread_t *t)
943*11173SJonathan.Adams@Sun.COM {
944*11173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = t->t_cldata;
945*11173SJonathan.Adams@Sun.COM 
946*11173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));	/* t should be in transition */
947*11173SJonathan.Adams@Sun.COM 
948*11173SJonathan.Adams@Sun.COM 	sdc->sdc_sleep_updates = sdc->sdc_nupdates;
949*11173SJonathan.Adams@Sun.COM }
950*11173SJonathan.Adams@Sun.COM 
951*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
952*11173SJonathan.Adams@Sun.COM static int
953*11173SJonathan.Adams@Sun.COM sysdc_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp,
954*11173SJonathan.Adams@Sun.COM     void *bufp)
955*11173SJonathan.Adams@Sun.COM {
956*11173SJonathan.Adams@Sun.COM 	cpupart_t *const cpupart = t->t_cpupart;
957*11173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = bufp;
958*11173SJonathan.Adams@Sun.COM 	sysdc_params_t *sdpp = parmsp;
959*11173SJonathan.Adams@Sun.COM 	sysdc_pset_t *newpset = sdc->sdc_pset;
960*11173SJonathan.Adams@Sun.COM 	sysdc_pset_t *pset;
961*11173SJonathan.Adams@Sun.COM 	int start_timeout;
962*11173SJonathan.Adams@Sun.COM 
963*11173SJonathan.Adams@Sun.COM 	if (t->t_cid != syscid)
964*11173SJonathan.Adams@Sun.COM 		return (EPERM);
965*11173SJonathan.Adams@Sun.COM 
966*11173SJonathan.Adams@Sun.COM 	ASSERT(ttolwp(t) != NULL);
967*11173SJonathan.Adams@Sun.COM 	ASSERT(sdpp != NULL);
968*11173SJonathan.Adams@Sun.COM 	ASSERT(newpset != NULL);
969*11173SJonathan.Adams@Sun.COM 	ASSERT(sysdc_param_init);
970*11173SJonathan.Adams@Sun.COM 
971*11173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_minpri >= sysdc_minpri);
972*11173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_maxpri <= sysdc_maxpri);
973*11173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_DC >= sysdc_minDC);
974*11173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_DC <= sysdc_maxDC);
975*11173SJonathan.Adams@Sun.COM 
976*11173SJonathan.Adams@Sun.COM 	sdc->sdc_thread = t;
977*11173SJonathan.Adams@Sun.COM 	sdc->sdc_pri = sdpp->sdp_maxpri;	/* start off maximally */
978*11173SJonathan.Adams@Sun.COM 	sdc->sdc_minpri = sdpp->sdp_minpri;
979*11173SJonathan.Adams@Sun.COM 	sdc->sdc_maxpri = sdpp->sdp_maxpri;
980*11173SJonathan.Adams@Sun.COM 	sdc->sdc_target_DC = sdpp->sdp_DC;
981*11173SJonathan.Adams@Sun.COM 	sdc->sdc_ticks = 0;
982*11173SJonathan.Adams@Sun.COM 	sdc->sdc_update_ticks = sysdc_update_ticks + 1;
983*11173SJonathan.Adams@Sun.COM 
984*11173SJonathan.Adams@Sun.COM 	/* Assign ourselves to the appropriate pset. */
985*11173SJonathan.Adams@Sun.COM 	sdc->sdc_pset = NULL;
986*11173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
987*11173SJonathan.Adams@Sun.COM 	for (pset = list_head(&sysdc_psets); pset != NULL;
988*11173SJonathan.Adams@Sun.COM 	    pset = list_next(&sysdc_psets, pset)) {
989*11173SJonathan.Adams@Sun.COM 		if (pset->sdp_cpupart == cpupart) {
990*11173SJonathan.Adams@Sun.COM 			break;
991*11173SJonathan.Adams@Sun.COM 		}
992*11173SJonathan.Adams@Sun.COM 	}
993*11173SJonathan.Adams@Sun.COM 	if (pset == NULL) {
994*11173SJonathan.Adams@Sun.COM 		pset = newpset;
995*11173SJonathan.Adams@Sun.COM 		newpset = NULL;
996*11173SJonathan.Adams@Sun.COM 		pset->sdp_cpupart = cpupart;
997*11173SJonathan.Adams@Sun.COM 		list_insert_tail(&sysdc_psets, pset);
998*11173SJonathan.Adams@Sun.COM 	}
999*11173SJonathan.Adams@Sun.COM 	pset->sdp_nthreads++;
1000*11173SJonathan.Adams@Sun.COM 	ASSERT(pset->sdp_nthreads > 0);
1001*11173SJonathan.Adams@Sun.COM 
1002*11173SJonathan.Adams@Sun.COM 	sdc->sdc_pset = pset;
1003*11173SJonathan.Adams@Sun.COM 
1004*11173SJonathan.Adams@Sun.COM 	start_timeout = (sysdc_update_timeout_started == 0);
1005*11173SJonathan.Adams@Sun.COM 	sysdc_update_timeout_started = 1;
1006*11173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
1007*11173SJonathan.Adams@Sun.COM 
1008*11173SJonathan.Adams@Sun.COM 	if (newpset != NULL)
1009*11173SJonathan.Adams@Sun.COM 		kmem_free(newpset, sizeof (*newpset));
1010*11173SJonathan.Adams@Sun.COM 
1011*11173SJonathan.Adams@Sun.COM 	/* Update t's scheduling class and priority. */
1012*11173SJonathan.Adams@Sun.COM 	thread_lock(t);
1013*11173SJonathan.Adams@Sun.COM 	t->t_clfuncs = &(sclass[cid].cl_funcs->thread);
1014*11173SJonathan.Adams@Sun.COM 	t->t_cid = cid;
1015*11173SJonathan.Adams@Sun.COM 	t->t_cldata = sdc;
1016*11173SJonathan.Adams@Sun.COM 	t->t_schedflag |= TS_RUNQMATCH;
1017*11173SJonathan.Adams@Sun.COM 
1018*11173SJonathan.Adams@Sun.COM 	sysdc_update_pri(sdc, SDC_UPDATE_INITIAL);
1019*11173SJonathan.Adams@Sun.COM 	thread_unlock(t);
1020*11173SJonathan.Adams@Sun.COM 
1021*11173SJonathan.Adams@Sun.COM 	/* Kick off the thread timeout if we're the first one in. */
1022*11173SJonathan.Adams@Sun.COM 	if (start_timeout) {
1023*11173SJonathan.Adams@Sun.COM 		(void) timeout(sysdc_update, NULL, sysdc_update_ticks);
1024*11173SJonathan.Adams@Sun.COM 	}
1025*11173SJonathan.Adams@Sun.COM 
1026*11173SJonathan.Adams@Sun.COM 	return (0);
1027*11173SJonathan.Adams@Sun.COM }
1028*11173SJonathan.Adams@Sun.COM 
1029*11173SJonathan.Adams@Sun.COM static void
1030*11173SJonathan.Adams@Sun.COM sysdc_leave(sysdc_t *sdc)
1031*11173SJonathan.Adams@Sun.COM {
1032*11173SJonathan.Adams@Sun.COM 	sysdc_pset_t *sdp = sdc->sdc_pset;
1033*11173SJonathan.Adams@Sun.COM 	sysdc_list_t *sdl = SYSDC_LIST(sdc);
1034*11173SJonathan.Adams@Sun.COM 	uint_t freedc;
1035*11173SJonathan.Adams@Sun.COM 
1036*11173SJonathan.Adams@Sun.COM 	mutex_enter(&sdl->sdl_lock);		/* block sysdc_update() */
1037*11173SJonathan.Adams@Sun.COM 	sdc->sdc_thread = NULL;
1038*11173SJonathan.Adams@Sun.COM 	freedc = (sdc->sdc_next == NULL);
1039*11173SJonathan.Adams@Sun.COM 	mutex_exit(&sdl->sdl_lock);
1040*11173SJonathan.Adams@Sun.COM 
1041*11173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
1042*11173SJonathan.Adams@Sun.COM 	sdp = sdc->sdc_pset;
1043*11173SJonathan.Adams@Sun.COM 	ASSERT(sdp != NULL);
1044*11173SJonathan.Adams@Sun.COM 	ASSERT(sdp->sdp_nthreads > 0);
1045*11173SJonathan.Adams@Sun.COM 	--sdp->sdp_nthreads;
1046*11173SJonathan.Adams@Sun.COM 	if (sdp->sdp_nthreads == 0) {
1047*11173SJonathan.Adams@Sun.COM 		list_remove(&sysdc_psets, sdp);
1048*11173SJonathan.Adams@Sun.COM 	} else {
1049*11173SJonathan.Adams@Sun.COM 		sdp = NULL;
1050*11173SJonathan.Adams@Sun.COM 	}
1051*11173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
1052*11173SJonathan.Adams@Sun.COM 
1053*11173SJonathan.Adams@Sun.COM 	if (freedc)
1054*11173SJonathan.Adams@Sun.COM 		kmem_free(sdc, sizeof (*sdc));
1055*11173SJonathan.Adams@Sun.COM 	if (sdp != NULL)
1056*11173SJonathan.Adams@Sun.COM 		kmem_free(sdp, sizeof (*sdp));
1057*11173SJonathan.Adams@Sun.COM }
1058*11173SJonathan.Adams@Sun.COM 
1059*11173SJonathan.Adams@Sun.COM static void
1060*11173SJonathan.Adams@Sun.COM sysdc_exitclass(void *buf)
1061*11173SJonathan.Adams@Sun.COM {
1062*11173SJonathan.Adams@Sun.COM 	sysdc_leave((sysdc_t *)buf);
1063*11173SJonathan.Adams@Sun.COM }
1064*11173SJonathan.Adams@Sun.COM 
1065*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1066*11173SJonathan.Adams@Sun.COM static int
1067*11173SJonathan.Adams@Sun.COM sysdc_canexit(kthread_t *t, cred_t *reqpcredp)
1068*11173SJonathan.Adams@Sun.COM {
1069*11173SJonathan.Adams@Sun.COM 	/* Threads cannot exit SDC once joined, except in a body bag. */
1070*11173SJonathan.Adams@Sun.COM 	return (EPERM);
1071*11173SJonathan.Adams@Sun.COM }
1072*11173SJonathan.Adams@Sun.COM 
1073*11173SJonathan.Adams@Sun.COM static void
1074*11173SJonathan.Adams@Sun.COM sysdc_exit(kthread_t *t)
1075*11173SJonathan.Adams@Sun.COM {
1076*11173SJonathan.Adams@Sun.COM 	sysdc_t *sdc;
1077*11173SJonathan.Adams@Sun.COM 
1078*11173SJonathan.Adams@Sun.COM 	/* We're exiting, so we just rejoin the SYS class. */
1079*11173SJonathan.Adams@Sun.COM 	thread_lock(t);
1080*11173SJonathan.Adams@Sun.COM 	ASSERT(t->t_cid == sysdccid);
1081*11173SJonathan.Adams@Sun.COM 	sdc = t->t_cldata;
1082*11173SJonathan.Adams@Sun.COM 	t->t_cid = syscid;
1083*11173SJonathan.Adams@Sun.COM 	t->t_cldata = NULL;
1084*11173SJonathan.Adams@Sun.COM 	t->t_clfuncs = &(sclass[syscid].cl_funcs->thread);
1085*11173SJonathan.Adams@Sun.COM 	(void) thread_change_pri(t, maxclsyspri, 0);
1086*11173SJonathan.Adams@Sun.COM 	t->t_schedflag &= ~TS_RUNQMATCH;
1087*11173SJonathan.Adams@Sun.COM 	thread_unlock_nopreempt(t);
1088*11173SJonathan.Adams@Sun.COM 
1089*11173SJonathan.Adams@Sun.COM 	/* Unlink the sdc from everything. */
1090*11173SJonathan.Adams@Sun.COM 	sysdc_leave(sdc);
1091*11173SJonathan.Adams@Sun.COM }
1092*11173SJonathan.Adams@Sun.COM 
1093*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1094*11173SJonathan.Adams@Sun.COM static int
1095*11173SJonathan.Adams@Sun.COM sysdc_fork(kthread_t *t, kthread_t *ct, void *bufp)
1096*11173SJonathan.Adams@Sun.COM {
1097*11173SJonathan.Adams@Sun.COM 	/*
1098*11173SJonathan.Adams@Sun.COM 	 * Threads cannot be created with SDC as their class; they must
1099*11173SJonathan.Adams@Sun.COM 	 * be created as SYS and then added with sysdc_thread_enter().
1100*11173SJonathan.Adams@Sun.COM 	 * Because of this restriction, sysdc_fork() should never be called.
1101*11173SJonathan.Adams@Sun.COM 	 */
1102*11173SJonathan.Adams@Sun.COM 	panic("sysdc cannot be forked");
1103*11173SJonathan.Adams@Sun.COM 
1104*11173SJonathan.Adams@Sun.COM 	return (ENOSYS);
1105*11173SJonathan.Adams@Sun.COM }
1106*11173SJonathan.Adams@Sun.COM 
1107*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1108*11173SJonathan.Adams@Sun.COM static void
1109*11173SJonathan.Adams@Sun.COM sysdc_forkret(kthread_t *t, kthread_t *ct)
1110*11173SJonathan.Adams@Sun.COM {
1111*11173SJonathan.Adams@Sun.COM 	/* SDC threads are part of system processes, which never fork. */
1112*11173SJonathan.Adams@Sun.COM 	panic("sysdc cannot be forked");
1113*11173SJonathan.Adams@Sun.COM }
1114*11173SJonathan.Adams@Sun.COM 
1115*11173SJonathan.Adams@Sun.COM static pri_t
1116*11173SJonathan.Adams@Sun.COM sysdc_globpri(kthread_t *t)
1117*11173SJonathan.Adams@Sun.COM {
1118*11173SJonathan.Adams@Sun.COM 	return (t->t_epri);
1119*11173SJonathan.Adams@Sun.COM }
1120*11173SJonathan.Adams@Sun.COM 
1121*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1122*11173SJonathan.Adams@Sun.COM static pri_t
1123*11173SJonathan.Adams@Sun.COM sysdc_no_swap(kthread_t *t, int flags)
1124*11173SJonathan.Adams@Sun.COM {
1125*11173SJonathan.Adams@Sun.COM 	/* SDC threads cannot be swapped. */
1126*11173SJonathan.Adams@Sun.COM 	return (-1);
1127*11173SJonathan.Adams@Sun.COM }
1128*11173SJonathan.Adams@Sun.COM 
1129*11173SJonathan.Adams@Sun.COM /*
1130*11173SJonathan.Adams@Sun.COM  * Get maximum and minimum priorities enjoyed by SDC threads.
1131*11173SJonathan.Adams@Sun.COM  */
1132*11173SJonathan.Adams@Sun.COM static int
1133*11173SJonathan.Adams@Sun.COM sysdc_getclpri(pcpri_t *pcprip)
1134*11173SJonathan.Adams@Sun.COM {
1135*11173SJonathan.Adams@Sun.COM 	pcprip->pc_clpmax = sysdc_maxpri;
1136*11173SJonathan.Adams@Sun.COM 	pcprip->pc_clpmin = sysdc_minpri;
1137*11173SJonathan.Adams@Sun.COM 	return (0);
1138*11173SJonathan.Adams@Sun.COM }
1139*11173SJonathan.Adams@Sun.COM 
1140*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1141*11173SJonathan.Adams@Sun.COM static int
1142*11173SJonathan.Adams@Sun.COM sysdc_getclinfo(void *arg)
1143*11173SJonathan.Adams@Sun.COM {
1144*11173SJonathan.Adams@Sun.COM 	return (0);		/* no class-specific info */
1145*11173SJonathan.Adams@Sun.COM }
1146*11173SJonathan.Adams@Sun.COM 
1147*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1148*11173SJonathan.Adams@Sun.COM static int
1149*11173SJonathan.Adams@Sun.COM sysdc_alloc(void **p, int flag)
1150*11173SJonathan.Adams@Sun.COM {
1151*11173SJonathan.Adams@Sun.COM 	sysdc_t *new;
1152*11173SJonathan.Adams@Sun.COM 
1153*11173SJonathan.Adams@Sun.COM 	*p = NULL;
1154*11173SJonathan.Adams@Sun.COM 	if ((new = kmem_zalloc(sizeof (*new), flag)) == NULL) {
1155*11173SJonathan.Adams@Sun.COM 		return (ENOMEM);
1156*11173SJonathan.Adams@Sun.COM 	}
1157*11173SJonathan.Adams@Sun.COM 	if ((new->sdc_pset = kmem_zalloc(sizeof (*new->sdc_pset), flag)) ==
1158*11173SJonathan.Adams@Sun.COM 	    NULL) {
1159*11173SJonathan.Adams@Sun.COM 		kmem_free(new, sizeof (*new));
1160*11173SJonathan.Adams@Sun.COM 		return (ENOMEM);
1161*11173SJonathan.Adams@Sun.COM 	}
1162*11173SJonathan.Adams@Sun.COM 	*p = new;
1163*11173SJonathan.Adams@Sun.COM 	return (0);
1164*11173SJonathan.Adams@Sun.COM }
1165*11173SJonathan.Adams@Sun.COM 
1166*11173SJonathan.Adams@Sun.COM static void
1167*11173SJonathan.Adams@Sun.COM sysdc_free(void *p)
1168*11173SJonathan.Adams@Sun.COM {
1169*11173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = p;
1170*11173SJonathan.Adams@Sun.COM 
1171*11173SJonathan.Adams@Sun.COM 	if (sdc != NULL) {
1172*11173SJonathan.Adams@Sun.COM 		/*
1173*11173SJonathan.Adams@Sun.COM 		 * We must have failed CL_ENTERCLASS(), so our pset should be
1174*11173SJonathan.Adams@Sun.COM 		 * there and unused.
1175*11173SJonathan.Adams@Sun.COM 		 */
1176*11173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_pset != NULL);
1177*11173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_pset->sdp_cpupart == NULL);
1178*11173SJonathan.Adams@Sun.COM 		kmem_free(sdc->sdc_pset, sizeof (*sdc->sdc_pset));
1179*11173SJonathan.Adams@Sun.COM 		kmem_free(sdc, sizeof (*sdc));
1180*11173SJonathan.Adams@Sun.COM 	}
1181*11173SJonathan.Adams@Sun.COM }
1182*11173SJonathan.Adams@Sun.COM 
1183*11173SJonathan.Adams@Sun.COM static int sysdc_enosys();	/* Boy, ANSI-C's K&R compatibility is weird. */
1184*11173SJonathan.Adams@Sun.COM static int sysdc_einval();
1185*11173SJonathan.Adams@Sun.COM static void sysdc_nullsys();
1186*11173SJonathan.Adams@Sun.COM 
1187*11173SJonathan.Adams@Sun.COM static struct classfuncs sysdc_classfuncs = {
1188*11173SJonathan.Adams@Sun.COM 	/* messages to class manager */
1189*11173SJonathan.Adams@Sun.COM 	{
1190*11173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* admin */
1191*11173SJonathan.Adams@Sun.COM 		sysdc_getclinfo,
1192*11173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* parmsin */
1193*11173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* parmsout */
1194*11173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* vaparmsin */
1195*11173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* vaparmsout */
1196*11173SJonathan.Adams@Sun.COM 		sysdc_getclpri,
1197*11173SJonathan.Adams@Sun.COM 		sysdc_alloc,
1198*11173SJonathan.Adams@Sun.COM 		sysdc_free,
1199*11173SJonathan.Adams@Sun.COM 	},
1200*11173SJonathan.Adams@Sun.COM 	/* operations on threads */
1201*11173SJonathan.Adams@Sun.COM 	{
1202*11173SJonathan.Adams@Sun.COM 		sysdc_enterclass,
1203*11173SJonathan.Adams@Sun.COM 		sysdc_exitclass,
1204*11173SJonathan.Adams@Sun.COM 		sysdc_canexit,
1205*11173SJonathan.Adams@Sun.COM 		sysdc_fork,
1206*11173SJonathan.Adams@Sun.COM 		sysdc_forkret,
1207*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* parmsget */
1208*11173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* parmsset */
1209*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* stop */
1210*11173SJonathan.Adams@Sun.COM 		sysdc_exit,
1211*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* active */
1212*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* inactive */
1213*11173SJonathan.Adams@Sun.COM 		sysdc_no_swap,	/* swapin */
1214*11173SJonathan.Adams@Sun.COM 		sysdc_no_swap,	/* swapout */
1215*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* trapret */
1216*11173SJonathan.Adams@Sun.COM 		sysdc_preempt,
1217*11173SJonathan.Adams@Sun.COM 		sysdc_setrun,
1218*11173SJonathan.Adams@Sun.COM 		sysdc_sleep,
1219*11173SJonathan.Adams@Sun.COM 		sysdc_tick,
1220*11173SJonathan.Adams@Sun.COM 		sysdc_wakeup,
1221*11173SJonathan.Adams@Sun.COM 		sysdc_einval,	/* donice */
1222*11173SJonathan.Adams@Sun.COM 		sysdc_globpri,
1223*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* set_process_group */
1224*11173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* yield */
1225*11173SJonathan.Adams@Sun.COM 		sysdc_einval,	/* doprio */
1226*11173SJonathan.Adams@Sun.COM 	}
1227*11173SJonathan.Adams@Sun.COM };
1228*11173SJonathan.Adams@Sun.COM 
1229*11173SJonathan.Adams@Sun.COM static int
1230*11173SJonathan.Adams@Sun.COM sysdc_enosys()
1231*11173SJonathan.Adams@Sun.COM {
1232*11173SJonathan.Adams@Sun.COM 	return (ENOSYS);
1233*11173SJonathan.Adams@Sun.COM }
1234*11173SJonathan.Adams@Sun.COM 
1235*11173SJonathan.Adams@Sun.COM static int
1236*11173SJonathan.Adams@Sun.COM sysdc_einval()
1237*11173SJonathan.Adams@Sun.COM {
1238*11173SJonathan.Adams@Sun.COM 	return (EINVAL);
1239*11173SJonathan.Adams@Sun.COM }
1240*11173SJonathan.Adams@Sun.COM 
1241*11173SJonathan.Adams@Sun.COM static void
1242*11173SJonathan.Adams@Sun.COM sysdc_nullsys()
1243*11173SJonathan.Adams@Sun.COM {
1244*11173SJonathan.Adams@Sun.COM }
1245*11173SJonathan.Adams@Sun.COM 
1246*11173SJonathan.Adams@Sun.COM /*ARGSUSED*/
1247*11173SJonathan.Adams@Sun.COM static pri_t
1248*11173SJonathan.Adams@Sun.COM sysdc_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp)
1249*11173SJonathan.Adams@Sun.COM {
1250*11173SJonathan.Adams@Sun.COM 	int idx;
1251*11173SJonathan.Adams@Sun.COM 
1252*11173SJonathan.Adams@Sun.COM 	list_create(&sysdc_psets, sizeof (sysdc_pset_t),
1253*11173SJonathan.Adams@Sun.COM 	    offsetof(sysdc_pset_t, sdp_node));
1254*11173SJonathan.Adams@Sun.COM 
1255*11173SJonathan.Adams@Sun.COM 	for (idx = 0; idx < SYSDC_NLISTS; idx++) {
1256*11173SJonathan.Adams@Sun.COM 		sysdc_active[idx].sdl_list = &sysdc_dummy;
1257*11173SJonathan.Adams@Sun.COM 	}
1258*11173SJonathan.Adams@Sun.COM 
1259*11173SJonathan.Adams@Sun.COM 	sysdc_initparam();
1260*11173SJonathan.Adams@Sun.COM 
1261*11173SJonathan.Adams@Sun.COM 	sysdccid = cid;
1262*11173SJonathan.Adams@Sun.COM 	*clfuncspp = &sysdc_classfuncs;
1263*11173SJonathan.Adams@Sun.COM 
1264*11173SJonathan.Adams@Sun.COM 	return ((pri_t)v.v_maxsyspri);
1265*11173SJonathan.Adams@Sun.COM }
1266*11173SJonathan.Adams@Sun.COM 
1267*11173SJonathan.Adams@Sun.COM static struct sclass csw = {
1268*11173SJonathan.Adams@Sun.COM 	"SDC",
1269*11173SJonathan.Adams@Sun.COM 	sysdc_init,
1270*11173SJonathan.Adams@Sun.COM 	0
1271*11173SJonathan.Adams@Sun.COM };
1272*11173SJonathan.Adams@Sun.COM 
1273*11173SJonathan.Adams@Sun.COM static struct modlsched modlsched = {
1274*11173SJonathan.Adams@Sun.COM 	&mod_schedops, "system duty cycle scheduling class", &csw
1275*11173SJonathan.Adams@Sun.COM };
1276*11173SJonathan.Adams@Sun.COM 
1277*11173SJonathan.Adams@Sun.COM static struct modlinkage modlinkage = {
1278*11173SJonathan.Adams@Sun.COM 	MODREV_1, (void *)&modlsched, NULL
1279*11173SJonathan.Adams@Sun.COM };
1280*11173SJonathan.Adams@Sun.COM 
1281*11173SJonathan.Adams@Sun.COM int
1282*11173SJonathan.Adams@Sun.COM _init()
1283*11173SJonathan.Adams@Sun.COM {
1284*11173SJonathan.Adams@Sun.COM 	return (mod_install(&modlinkage));
1285*11173SJonathan.Adams@Sun.COM }
1286*11173SJonathan.Adams@Sun.COM 
1287*11173SJonathan.Adams@Sun.COM int
1288*11173SJonathan.Adams@Sun.COM _fini()
1289*11173SJonathan.Adams@Sun.COM {
1290*11173SJonathan.Adams@Sun.COM 	return (EBUSY);		/* can't unload for now */
1291*11173SJonathan.Adams@Sun.COM }
1292*11173SJonathan.Adams@Sun.COM 
1293*11173SJonathan.Adams@Sun.COM int
1294*11173SJonathan.Adams@Sun.COM _info(struct modinfo *modinfop)
1295*11173SJonathan.Adams@Sun.COM {
1296*11173SJonathan.Adams@Sun.COM 	return (mod_info(&modlinkage, modinfop));
1297*11173SJonathan.Adams@Sun.COM }
1298*11173SJonathan.Adams@Sun.COM 
1299*11173SJonathan.Adams@Sun.COM /* --- consolidation-private interfaces --- */
1300*11173SJonathan.Adams@Sun.COM void
1301*11173SJonathan.Adams@Sun.COM sysdc_thread_enter(kthread_t *t, uint_t dc, uint_t flags)
1302*11173SJonathan.Adams@Sun.COM {
1303*11173SJonathan.Adams@Sun.COM 	void *buf = NULL;
1304*11173SJonathan.Adams@Sun.COM 	sysdc_params_t sdp;
1305*11173SJonathan.Adams@Sun.COM 
1306*11173SJonathan.Adams@Sun.COM 	SYSDC_INC_STAT(sysdc_thread_enter_enter);
1307*11173SJonathan.Adams@Sun.COM 
1308*11173SJonathan.Adams@Sun.COM 	ASSERT(sysdc_param_init);
1309*11173SJonathan.Adams@Sun.COM 	ASSERT(sysdccid >= 0);
1310*11173SJonathan.Adams@Sun.COM 
1311*11173SJonathan.Adams@Sun.COM 	ASSERT((flags & ~SYSDC_THREAD_BATCH) == 0);
1312*11173SJonathan.Adams@Sun.COM 
1313*11173SJonathan.Adams@Sun.COM 	sdp.sdp_minpri = sysdc_minpri;
1314*11173SJonathan.Adams@Sun.COM 	sdp.sdp_maxpri = sysdc_maxpri;
1315*11173SJonathan.Adams@Sun.COM 	sdp.sdp_DC = MAX(MIN(dc, sysdc_maxDC), sysdc_minDC);
1316*11173SJonathan.Adams@Sun.COM 
1317*11173SJonathan.Adams@Sun.COM 	if (flags & SYSDC_THREAD_BATCH)
1318*11173SJonathan.Adams@Sun.COM 		sdp.sdp_maxpri -= sysdc_batch_niceness;
1319*11173SJonathan.Adams@Sun.COM 
1320*11173SJonathan.Adams@Sun.COM 	VERIFY3U(CL_ALLOC(&buf, sysdccid, KM_SLEEP), ==, 0);
1321*11173SJonathan.Adams@Sun.COM 
1322*11173SJonathan.Adams@Sun.COM 	ASSERT(t->t_lwp != NULL);
1323*11173SJonathan.Adams@Sun.COM 	ASSERT(t->t_cid == syscid);
1324*11173SJonathan.Adams@Sun.COM 	ASSERT(t->t_cldata == NULL);
1325*11173SJonathan.Adams@Sun.COM 	VERIFY3U(CL_CANEXIT(t, NULL), ==, 0);
1326*11173SJonathan.Adams@Sun.COM 	VERIFY3U(CL_ENTERCLASS(t, sysdccid, &sdp, kcred, buf), ==, 0);
1327*11173SJonathan.Adams@Sun.COM 	CL_EXITCLASS(syscid, NULL);
1328*11173SJonathan.Adams@Sun.COM }
1329