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