xref: /onnv-gate/usr/src/uts/common/disp/sysdc.c (revision 11331:4b4b77f945a7)
111173SJonathan.Adams@Sun.COM /*
211173SJonathan.Adams@Sun.COM  * CDDL HEADER START
311173SJonathan.Adams@Sun.COM  *
411173SJonathan.Adams@Sun.COM  * The contents of this file are subject to the terms of the
511173SJonathan.Adams@Sun.COM  * Common Development and Distribution License (the "License").
611173SJonathan.Adams@Sun.COM  * You may not use this file except in compliance with the License.
711173SJonathan.Adams@Sun.COM  *
811173SJonathan.Adams@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
911173SJonathan.Adams@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1011173SJonathan.Adams@Sun.COM  * See the License for the specific language governing permissions
1111173SJonathan.Adams@Sun.COM  * and limitations under the License.
1211173SJonathan.Adams@Sun.COM  *
1311173SJonathan.Adams@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1411173SJonathan.Adams@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1511173SJonathan.Adams@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1611173SJonathan.Adams@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1711173SJonathan.Adams@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1811173SJonathan.Adams@Sun.COM  *
1911173SJonathan.Adams@Sun.COM  * CDDL HEADER END
2011173SJonathan.Adams@Sun.COM  */
2111173SJonathan.Adams@Sun.COM /*
2211173SJonathan.Adams@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
2311173SJonathan.Adams@Sun.COM  * Use is subject to license terms.
2411173SJonathan.Adams@Sun.COM  */
2511173SJonathan.Adams@Sun.COM 
2611173SJonathan.Adams@Sun.COM /*
2711173SJonathan.Adams@Sun.COM  * The System Duty Cycle (SDC) scheduling class
2811173SJonathan.Adams@Sun.COM  * --------------------------------------------
2911173SJonathan.Adams@Sun.COM  *
3011173SJonathan.Adams@Sun.COM  * Background
3111173SJonathan.Adams@Sun.COM  *
3211173SJonathan.Adams@Sun.COM  * Kernel threads in Solaris have traditionally not been large consumers
3311173SJonathan.Adams@Sun.COM  * of CPU time.  They typically wake up, perform a small amount of
3411173SJonathan.Adams@Sun.COM  * work, then go back to sleep waiting for either a timeout or another
3511173SJonathan.Adams@Sun.COM  * signal.  On the assumption that the small amount of work that they do
3611173SJonathan.Adams@Sun.COM  * is important for the behavior of the whole system, these threads are
3711173SJonathan.Adams@Sun.COM  * treated kindly by the dispatcher and the SYS scheduling class: they run
3811173SJonathan.Adams@Sun.COM  * without preemption from anything other than real-time and interrupt
3911173SJonathan.Adams@Sun.COM  * threads; when preempted, they are put at the front of the queue, so they
4011173SJonathan.Adams@Sun.COM  * generally do not migrate between CPUs; and they are allowed to stay
4111173SJonathan.Adams@Sun.COM  * running until they voluntarily give up the CPU.
4211173SJonathan.Adams@Sun.COM  *
4311173SJonathan.Adams@Sun.COM  * As Solaris has evolved, new workloads have emerged which require the
4411173SJonathan.Adams@Sun.COM  * kernel to perform significant amounts of CPU-intensive work.  One
4511173SJonathan.Adams@Sun.COM  * example of such a workload is ZFS's transaction group sync processing.
4611173SJonathan.Adams@Sun.COM  * Each sync operation generates a large batch of I/Os, and each I/O
4711173SJonathan.Adams@Sun.COM  * may need to be compressed and/or checksummed before it is written to
4811173SJonathan.Adams@Sun.COM  * storage.  The taskq threads which perform the compression and checksums
4911173SJonathan.Adams@Sun.COM  * will run nonstop as long as they have work to do; a large sync operation
5011173SJonathan.Adams@Sun.COM  * on a compression-heavy dataset can keep them busy for seconds on end.
5111173SJonathan.Adams@Sun.COM  * This causes human-time-scale dispatch latency bubbles for any other
5211173SJonathan.Adams@Sun.COM  * threads which have the misfortune to share a CPU with the taskq threads.
5311173SJonathan.Adams@Sun.COM  *
5411173SJonathan.Adams@Sun.COM  * The SDC scheduling class is a solution to this problem.
5511173SJonathan.Adams@Sun.COM  *
5611173SJonathan.Adams@Sun.COM  *
5711173SJonathan.Adams@Sun.COM  * Overview
5811173SJonathan.Adams@Sun.COM  *
5911173SJonathan.Adams@Sun.COM  * SDC is centered around the concept of a thread's duty cycle (DC):
6011173SJonathan.Adams@Sun.COM  *
6111173SJonathan.Adams@Sun.COM  *			      ONPROC time
6211173SJonathan.Adams@Sun.COM  *	Duty Cycle =	----------------------
6311173SJonathan.Adams@Sun.COM  *			ONPROC + Runnable time
6411173SJonathan.Adams@Sun.COM  *
6511173SJonathan.Adams@Sun.COM  * This is the ratio of the time that the thread spent running on a CPU
6611173SJonathan.Adams@Sun.COM  * divided by the time it spent running or trying to run.  It is unaffected
6711173SJonathan.Adams@Sun.COM  * by any time the thread spent sleeping, stopped, etc.
6811173SJonathan.Adams@Sun.COM  *
6911173SJonathan.Adams@Sun.COM  * A thread joining the SDC class specifies a "target" DC that it wants
7011173SJonathan.Adams@Sun.COM  * to run at.  To implement this policy, the routine sysdc_update() scans
7111173SJonathan.Adams@Sun.COM  * the list of active SDC threads every few ticks and uses each thread's
7211173SJonathan.Adams@Sun.COM  * microstate data to compute the actual duty cycle that that thread
7311173SJonathan.Adams@Sun.COM  * has experienced recently.  If the thread is under its target DC, its
7411173SJonathan.Adams@Sun.COM  * priority is increased to the maximum available (sysdc_maxpri, which is
7511173SJonathan.Adams@Sun.COM  * 99 by default).  If the thread is over its target DC, its priority is
7611173SJonathan.Adams@Sun.COM  * reduced to the minimum available (sysdc_minpri, 0 by default).  This
7711173SJonathan.Adams@Sun.COM  * is a fairly primitive approach, in that it doesn't use any of the
7811173SJonathan.Adams@Sun.COM  * intermediate priorities, but it's not completely inappropriate.  Even
7911173SJonathan.Adams@Sun.COM  * though threads in the SDC class might take a while to do their job, they
8011173SJonathan.Adams@Sun.COM  * are by some definition important if they're running inside the kernel,
8111173SJonathan.Adams@Sun.COM  * so it is reasonable that they should get to run at priority 99.
8211173SJonathan.Adams@Sun.COM  *
8311173SJonathan.Adams@Sun.COM  * If a thread is running when sysdc_update() calculates its actual duty
8411173SJonathan.Adams@Sun.COM  * cycle, and there are other threads of equal or greater priority on its
8511173SJonathan.Adams@Sun.COM  * CPU's dispatch queue, sysdc_update() preempts that thread.  The thread
8611173SJonathan.Adams@Sun.COM  * acknowledges the preemption by calling sysdc_preempt(), which calls
8711173SJonathan.Adams@Sun.COM  * setbackdq(), which gives other threads with the same priority a chance
8811173SJonathan.Adams@Sun.COM  * to run.  This creates a de facto time quantum for threads in the SDC
8911173SJonathan.Adams@Sun.COM  * scheduling class.
9011173SJonathan.Adams@Sun.COM  *
9111173SJonathan.Adams@Sun.COM  * An SDC thread which is assigned priority 0 can continue to run if
9211173SJonathan.Adams@Sun.COM  * nothing else needs to use the CPU that it's running on.  Similarly, an
9311173SJonathan.Adams@Sun.COM  * SDC thread at priority 99 might not get to run as much as it wants to
9411173SJonathan.Adams@Sun.COM  * if there are other priority-99 or higher threads on its CPU.  These
9511173SJonathan.Adams@Sun.COM  * situations would cause the thread to get ahead of or behind its target
9611173SJonathan.Adams@Sun.COM  * DC; the longer the situations lasted, the further ahead or behind the
9711173SJonathan.Adams@Sun.COM  * thread would get.  Rather than condemning a thread to a lifetime of
9811173SJonathan.Adams@Sun.COM  * paying for its youthful indiscretions, SDC keeps "base" values for
9911173SJonathan.Adams@Sun.COM  * ONPROC and Runnable times in each thread's sysdc data, and updates these
10011173SJonathan.Adams@Sun.COM  * values periodically.  The duty cycle is then computed using the elapsed
10111173SJonathan.Adams@Sun.COM  * amount of ONPROC and Runnable times since those base times.
10211173SJonathan.Adams@Sun.COM  *
10311173SJonathan.Adams@Sun.COM  * Since sysdc_update() scans SDC threads fairly frequently, it tries to
10411173SJonathan.Adams@Sun.COM  * keep the list of "active" threads small by pruning out threads which
10511173SJonathan.Adams@Sun.COM  * have been asleep for a brief time.  They are not pruned immediately upon
10611173SJonathan.Adams@Sun.COM  * going to sleep, since some threads may bounce back and forth between
10711173SJonathan.Adams@Sun.COM  * sleeping and being runnable.
10811173SJonathan.Adams@Sun.COM  *
10911173SJonathan.Adams@Sun.COM  *
11011173SJonathan.Adams@Sun.COM  * Interfaces
11111173SJonathan.Adams@Sun.COM  *
11211173SJonathan.Adams@Sun.COM  * void sysdc_thread_enter(t, dc, flags)
11311173SJonathan.Adams@Sun.COM  *
11411173SJonathan.Adams@Sun.COM  *	Moves a kernel thread from the SYS scheduling class to the
11511173SJonathan.Adams@Sun.COM  *	SDC class. t must have an associated LWP (created by calling
11611173SJonathan.Adams@Sun.COM  *	lwp_kernel_create()).  The thread will have a target DC of dc.
11711173SJonathan.Adams@Sun.COM  *	Flags should be either 0 or SYSDC_THREAD_BATCH.  If
118*11331SJonathan.Adams@Sun.COM  *	SYSDC_THREAD_BATCH is specified, the thread is expected to be
119*11331SJonathan.Adams@Sun.COM  *	doing large amounts of processing.
12011173SJonathan.Adams@Sun.COM  *
12111173SJonathan.Adams@Sun.COM  *
12211173SJonathan.Adams@Sun.COM  * Complications
12311173SJonathan.Adams@Sun.COM  *
12411173SJonathan.Adams@Sun.COM  * - Run queue balancing
12511173SJonathan.Adams@Sun.COM  *
12611173SJonathan.Adams@Sun.COM  *	The Solaris dispatcher is biased towards letting a thread run
12711173SJonathan.Adams@Sun.COM  *	on the same CPU which it last ran on, if no more than 3 ticks
12811173SJonathan.Adams@Sun.COM  *	(i.e. rechoose_interval) have passed since the thread last ran.
12911173SJonathan.Adams@Sun.COM  *	This helps to preserve cache warmth.  On the other hand, it also
13011173SJonathan.Adams@Sun.COM  *	tries to keep the per-CPU run queues fairly balanced; if the CPU
13111173SJonathan.Adams@Sun.COM  *	chosen for a runnable thread has a run queue which is three or
13211173SJonathan.Adams@Sun.COM  *	more threads longer than a neighboring CPU's queue, the runnable
13311173SJonathan.Adams@Sun.COM  *	thread is dispatched onto the neighboring CPU instead.
13411173SJonathan.Adams@Sun.COM  *
13511173SJonathan.Adams@Sun.COM  *	These policies work well for some workloads, but not for many SDC
13611173SJonathan.Adams@Sun.COM  *	threads.  The taskq client of SDC, for example, has many discrete
13711173SJonathan.Adams@Sun.COM  *	units of work to do.  The work units are largely independent, so
13811173SJonathan.Adams@Sun.COM  *	cache warmth is not an important consideration.  It is important
13911173SJonathan.Adams@Sun.COM  *	that the threads fan out quickly to different CPUs, since the
14011173SJonathan.Adams@Sun.COM  *	amount of work these threads have to do (a few seconds worth at a
14111173SJonathan.Adams@Sun.COM  *	time) doesn't leave much time to correct thread placement errors
14211173SJonathan.Adams@Sun.COM  *	(i.e. two SDC threads being dispatched to the same CPU).
14311173SJonathan.Adams@Sun.COM  *
14411173SJonathan.Adams@Sun.COM  *	To fix this, SDC uses the TS_RUNQMATCH flag introduced for FSS.
14511173SJonathan.Adams@Sun.COM  *	This tells the dispatcher to keep neighboring run queues' lengths
14611173SJonathan.Adams@Sun.COM  *	more evenly matched, which allows SDC threads to migrate more
14711173SJonathan.Adams@Sun.COM  *	easily.
14811173SJonathan.Adams@Sun.COM  *
14911173SJonathan.Adams@Sun.COM  * - LWPs and system processes
15011173SJonathan.Adams@Sun.COM  *
15111173SJonathan.Adams@Sun.COM  *	SDC can only be used for kernel threads.  Since SDC uses microstate
15211173SJonathan.Adams@Sun.COM  *	accounting data to compute each thread's actual duty cycle, all
15311173SJonathan.Adams@Sun.COM  *	threads entering the SDC class must have associated LWPs (which
15411173SJonathan.Adams@Sun.COM  *	store the microstate data).  This means that the threads have to
15511173SJonathan.Adams@Sun.COM  *	be associated with an SSYS process, i.e. one created by newproc().
15611173SJonathan.Adams@Sun.COM  *	If the microstate accounting information is ever moved into the
15711173SJonathan.Adams@Sun.COM  *	kthread_t, this restriction could be lifted.
15811173SJonathan.Adams@Sun.COM  *
15911173SJonathan.Adams@Sun.COM  * - Dealing with oversubscription
16011173SJonathan.Adams@Sun.COM  *
16111173SJonathan.Adams@Sun.COM  *	Since SDC duty cycles are per-thread, it is possible that the
16211173SJonathan.Adams@Sun.COM  *	aggregate requested duty cycle of all SDC threads in a processor
16311173SJonathan.Adams@Sun.COM  *	set could be greater than the total CPU time available in that set.
16411173SJonathan.Adams@Sun.COM  *	The FSS scheduling class has an analogous situation, which it deals
16511173SJonathan.Adams@Sun.COM  *	with by reducing each thread's allotted CPU time proportionally.
16611173SJonathan.Adams@Sun.COM  *	Since SDC doesn't need to be as precise as FSS, it uses a simpler
16711173SJonathan.Adams@Sun.COM  *	solution to the oversubscription problem.
16811173SJonathan.Adams@Sun.COM  *
16911173SJonathan.Adams@Sun.COM  *	sysdc_update() accumulates the amount of time that max-priority SDC
17011173SJonathan.Adams@Sun.COM  *	threads have spent on-CPU in each processor set, and uses that sum
17111173SJonathan.Adams@Sun.COM  *	to create an implied duty cycle for that processor set:
17211173SJonathan.Adams@Sun.COM  *
17311173SJonathan.Adams@Sun.COM  *				accumulated CPU time
17411173SJonathan.Adams@Sun.COM  *	   pset DC =	-----------------------------------
17511173SJonathan.Adams@Sun.COM  *			 (# CPUs) * time since last update
17611173SJonathan.Adams@Sun.COM  *
17711173SJonathan.Adams@Sun.COM  *	If this implied duty cycle is above a maximum pset duty cycle (90%
17811173SJonathan.Adams@Sun.COM  *	by default), sysdc_update() sets the priority of all SDC threads
17911173SJonathan.Adams@Sun.COM  *	in that processor set to sysdc_minpri for a "break" period.  After
18011173SJonathan.Adams@Sun.COM  *	the break period, it waits for a "nobreak" period before trying to
18111173SJonathan.Adams@Sun.COM  *	enforce the pset duty cycle limit again.
18211173SJonathan.Adams@Sun.COM  *
18311173SJonathan.Adams@Sun.COM  * - Processor sets
18411173SJonathan.Adams@Sun.COM  *
18511173SJonathan.Adams@Sun.COM  *	As the above implies, SDC is processor set aware, but it does not
18611173SJonathan.Adams@Sun.COM  *	currently allow threads to change processor sets while in the SDC
18711173SJonathan.Adams@Sun.COM  *	class.  Instead, those threads must join the desired processor set
18811173SJonathan.Adams@Sun.COM  *	before entering SDC. [1]
18911173SJonathan.Adams@Sun.COM  *
19011173SJonathan.Adams@Sun.COM  * - Batch threads
19111173SJonathan.Adams@Sun.COM  *
19211173SJonathan.Adams@Sun.COM  *	A thread joining the SDC class can specify the SDC_THREAD_BATCH
193*11331SJonathan.Adams@Sun.COM  *	flag.  This flag currently has no effect, but marks threads which
194*11331SJonathan.Adams@Sun.COM  *	do bulk processing.
19511173SJonathan.Adams@Sun.COM  *
19611173SJonathan.Adams@Sun.COM  * - t_kpri_req
19711173SJonathan.Adams@Sun.COM  *
19811173SJonathan.Adams@Sun.COM  *	The TS and FSS scheduling classes pay attention to t_kpri_req,
19911173SJonathan.Adams@Sun.COM  *	which provides a simple form of priority inheritance for
20011173SJonathan.Adams@Sun.COM  *	synchronization primitives (such as rwlocks held as READER) which
20111173SJonathan.Adams@Sun.COM  *	cannot be traced to a unique thread.  The SDC class does not honor
20211173SJonathan.Adams@Sun.COM  *	t_kpri_req, for a few reasons:
20311173SJonathan.Adams@Sun.COM  *
20411173SJonathan.Adams@Sun.COM  *	1.  t_kpri_req is notoriously inaccurate.  A measure of its
20511173SJonathan.Adams@Sun.COM  *	    inaccuracy is that it needs to be cleared every time a thread
20611173SJonathan.Adams@Sun.COM  *	    returns to user mode, because it is frequently non-zero at that
20711173SJonathan.Adams@Sun.COM  *	    point.  This can happen because "ownership" of synchronization
20811173SJonathan.Adams@Sun.COM  *	    primitives that use t_kpri_req can be silently handed off,
20911173SJonathan.Adams@Sun.COM  *	    leaving no opportunity to will the t_kpri_req inheritance.
21011173SJonathan.Adams@Sun.COM  *
21111173SJonathan.Adams@Sun.COM  *	2.  Unlike in TS and FSS, threads in SDC *will* eventually run at
21211173SJonathan.Adams@Sun.COM  *	    kernel priority.  This means that even if an SDC thread
21311173SJonathan.Adams@Sun.COM  *	    is holding a synchronization primitive and running at low
21411173SJonathan.Adams@Sun.COM  *	    priority, its priority will eventually be raised above 60,
21511173SJonathan.Adams@Sun.COM  *	    allowing it to drive on and release the resource.
21611173SJonathan.Adams@Sun.COM  *
21711173SJonathan.Adams@Sun.COM  *	3.  The first consumer of SDC uses the taskq subsystem, which holds
21811173SJonathan.Adams@Sun.COM  *	    a reader lock for the duration of the task's execution.  This
21911173SJonathan.Adams@Sun.COM  *	    would mean that SDC threads would never drop below kernel
22011173SJonathan.Adams@Sun.COM  *	    priority in practice, which defeats one of the purposes of SDC.
22111173SJonathan.Adams@Sun.COM  *
22211173SJonathan.Adams@Sun.COM  * - Why not FSS?
22311173SJonathan.Adams@Sun.COM  *
22411173SJonathan.Adams@Sun.COM  *	It might seem that the existing FSS scheduling class could solve
22511173SJonathan.Adams@Sun.COM  *	the problems that SDC is attempting to solve.  FSS's more precise
22611173SJonathan.Adams@Sun.COM  *	solution to the oversubscription problem would hardly cause
22711173SJonathan.Adams@Sun.COM  *	trouble, as long as it performed well.  SDC is implemented as
22811173SJonathan.Adams@Sun.COM  *	a separate scheduling class for two main reasons: the initial
22911173SJonathan.Adams@Sun.COM  *	consumer of SDC does not map well onto the "project" abstraction
23011173SJonathan.Adams@Sun.COM  *	that is central to FSS, and FSS does not expect to run at kernel
23111173SJonathan.Adams@Sun.COM  *	priorities.
23211173SJonathan.Adams@Sun.COM  *
23311173SJonathan.Adams@Sun.COM  *
23411173SJonathan.Adams@Sun.COM  * Tunables
23511173SJonathan.Adams@Sun.COM  *
23611173SJonathan.Adams@Sun.COM  * - sysdc_update_interval_msec:  Number of milliseconds between
23711173SJonathan.Adams@Sun.COM  *	consecutive thread priority updates.
23811173SJonathan.Adams@Sun.COM  *
23911173SJonathan.Adams@Sun.COM  * - sysdc_reset_interval_msec:  Number of milliseconds between
24011173SJonathan.Adams@Sun.COM  *	consecutive resets of a thread's base ONPROC and Runnable
24111173SJonathan.Adams@Sun.COM  *	times.
24211173SJonathan.Adams@Sun.COM  *
24311173SJonathan.Adams@Sun.COM  * - sysdc_prune_interval_msec:  Number of milliseconds of sleeping
24411173SJonathan.Adams@Sun.COM  *	before a thread is pruned from the active list.
24511173SJonathan.Adams@Sun.COM  *
24611173SJonathan.Adams@Sun.COM  * - sysdc_max_pset_DC:  Allowable percentage of a processor set's
24711173SJonathan.Adams@Sun.COM  *	CPU time which SDC can give to its high-priority threads.
24811173SJonathan.Adams@Sun.COM  *
24911173SJonathan.Adams@Sun.COM  * - sysdc_break_msec:  Number of milliseconds of "break" taken when
25011173SJonathan.Adams@Sun.COM  *	sysdc_max_pset_DC is exceeded.
25111173SJonathan.Adams@Sun.COM  *
25211173SJonathan.Adams@Sun.COM  *
25311173SJonathan.Adams@Sun.COM  * Future work (in SDC and related subsystems)
25411173SJonathan.Adams@Sun.COM  *
25511173SJonathan.Adams@Sun.COM  * - Per-thread rechoose interval (0 for SDC)
25611173SJonathan.Adams@Sun.COM  *
25711173SJonathan.Adams@Sun.COM  *	Allow each thread to specify its own rechoose interval.  SDC
25811173SJonathan.Adams@Sun.COM  *	threads would specify an interval of zero, which would rechoose
25911173SJonathan.Adams@Sun.COM  *	the CPU with the lowest priority once per update.
26011173SJonathan.Adams@Sun.COM  *
26111173SJonathan.Adams@Sun.COM  * - Allow threads to change processor sets after joining the SDC class
26211173SJonathan.Adams@Sun.COM  *
26311173SJonathan.Adams@Sun.COM  * - Thread groups and per-group DC
26411173SJonathan.Adams@Sun.COM  *
26511173SJonathan.Adams@Sun.COM  *	It might be nice to be able to specify a duty cycle which applies
26611173SJonathan.Adams@Sun.COM  *	to a group of threads in aggregate.
26711173SJonathan.Adams@Sun.COM  *
26811173SJonathan.Adams@Sun.COM  * - Per-group DC callback to allow dynamic DC tuning
26911173SJonathan.Adams@Sun.COM  *
27011173SJonathan.Adams@Sun.COM  *	Currently, DCs are assigned when the thread joins SDC.  Some
27111173SJonathan.Adams@Sun.COM  *	workloads could benefit from being able to tune their DC using
27211173SJonathan.Adams@Sun.COM  *	subsystem-specific knowledge about the workload.
27311173SJonathan.Adams@Sun.COM  *
27411173SJonathan.Adams@Sun.COM  * - Finer-grained priority updates
27511173SJonathan.Adams@Sun.COM  *
27611173SJonathan.Adams@Sun.COM  * - More nuanced management of oversubscription
27711173SJonathan.Adams@Sun.COM  *
27811173SJonathan.Adams@Sun.COM  * - Moving other CPU-intensive threads into SDC
27911173SJonathan.Adams@Sun.COM  *
28011173SJonathan.Adams@Sun.COM  * - Move msacct data into kthread_t
28111173SJonathan.Adams@Sun.COM  *
28211173SJonathan.Adams@Sun.COM  *	This would allow kernel threads without LWPs to join SDC.
28311173SJonathan.Adams@Sun.COM  *
28411173SJonathan.Adams@Sun.COM  *
28511173SJonathan.Adams@Sun.COM  * Footnotes
28611173SJonathan.Adams@Sun.COM  *
28711173SJonathan.Adams@Sun.COM  * [1] The details of doing so are left as an exercise for the reader.
28811173SJonathan.Adams@Sun.COM  */
28911173SJonathan.Adams@Sun.COM 
29011173SJonathan.Adams@Sun.COM #include <sys/types.h>
29111173SJonathan.Adams@Sun.COM #include <sys/sysdc.h>
29211173SJonathan.Adams@Sun.COM #include <sys/sysdc_impl.h>
29311173SJonathan.Adams@Sun.COM 
29411173SJonathan.Adams@Sun.COM #include <sys/class.h>
29511173SJonathan.Adams@Sun.COM #include <sys/cmn_err.h>
29611173SJonathan.Adams@Sun.COM #include <sys/cpuvar.h>
29711173SJonathan.Adams@Sun.COM #include <sys/cpupart.h>
29811173SJonathan.Adams@Sun.COM #include <sys/debug.h>
29911173SJonathan.Adams@Sun.COM #include <sys/disp.h>
30011173SJonathan.Adams@Sun.COM #include <sys/errno.h>
30111173SJonathan.Adams@Sun.COM #include <sys/inline.h>
30211173SJonathan.Adams@Sun.COM #include <sys/kmem.h>
30311173SJonathan.Adams@Sun.COM #include <sys/modctl.h>
30411173SJonathan.Adams@Sun.COM #include <sys/schedctl.h>
30511173SJonathan.Adams@Sun.COM #include <sys/sdt.h>
30611173SJonathan.Adams@Sun.COM #include <sys/sunddi.h>
30711173SJonathan.Adams@Sun.COM #include <sys/sysmacros.h>
30811173SJonathan.Adams@Sun.COM #include <sys/systm.h>
30911173SJonathan.Adams@Sun.COM #include <sys/var.h>
31011173SJonathan.Adams@Sun.COM 
31111173SJonathan.Adams@Sun.COM /*
31211173SJonathan.Adams@Sun.COM  * Tunables - loaded into the internal state at module load time
31311173SJonathan.Adams@Sun.COM  */
31411173SJonathan.Adams@Sun.COM uint_t		sysdc_update_interval_msec = 20;
31511173SJonathan.Adams@Sun.COM uint_t		sysdc_reset_interval_msec = 400;
31611173SJonathan.Adams@Sun.COM uint_t		sysdc_prune_interval_msec = 100;
31711173SJonathan.Adams@Sun.COM uint_t		sysdc_max_pset_DC = 90;
31811173SJonathan.Adams@Sun.COM uint_t		sysdc_break_msec = 80;
31911173SJonathan.Adams@Sun.COM 
32011173SJonathan.Adams@Sun.COM /*
32111173SJonathan.Adams@Sun.COM  * Internal state - constants set up by sysdc_initparam()
32211173SJonathan.Adams@Sun.COM  */
32311173SJonathan.Adams@Sun.COM static clock_t	sysdc_update_ticks;	/* ticks between updates */
32411173SJonathan.Adams@Sun.COM static uint_t	sysdc_prune_updates;	/* updates asleep before pruning */
32511173SJonathan.Adams@Sun.COM static uint_t	sysdc_reset_updates;	/* # of updates before reset */
32611173SJonathan.Adams@Sun.COM static uint_t	sysdc_break_updates;	/* updates to break */
32711173SJonathan.Adams@Sun.COM static uint_t	sysdc_nobreak_updates;	/* updates to not check */
32811173SJonathan.Adams@Sun.COM static uint_t	sysdc_minDC;		/* minimum allowed DC */
32911173SJonathan.Adams@Sun.COM static uint_t	sysdc_maxDC;		/* maximum allowed DC */
33011173SJonathan.Adams@Sun.COM static pri_t	sysdc_minpri;		/* minimum allowed priority */
33111173SJonathan.Adams@Sun.COM static pri_t	sysdc_maxpri;		/* maximum allowed priority */
33211173SJonathan.Adams@Sun.COM 
33311173SJonathan.Adams@Sun.COM /*
33411173SJonathan.Adams@Sun.COM  * Internal state
33511173SJonathan.Adams@Sun.COM  */
33611173SJonathan.Adams@Sun.COM static kmutex_t	sysdc_pset_lock;	/* lock protecting pset data */
33711173SJonathan.Adams@Sun.COM static list_t	sysdc_psets;		/* list of psets with SDC threads */
33811173SJonathan.Adams@Sun.COM static uint_t	sysdc_param_init;	/* sysdc_initparam() has been called */
33911173SJonathan.Adams@Sun.COM static uint_t	sysdc_update_timeout_started; /* update timeout is active */
34011173SJonathan.Adams@Sun.COM static hrtime_t	sysdc_last_update;	/* time of last sysdc_update() */
34111173SJonathan.Adams@Sun.COM static sysdc_t	sysdc_dummy;		/* used to terminate active lists */
34211173SJonathan.Adams@Sun.COM 
34311173SJonathan.Adams@Sun.COM /*
34411173SJonathan.Adams@Sun.COM  * Internal state - active hash table
34511173SJonathan.Adams@Sun.COM  */
34611173SJonathan.Adams@Sun.COM #define	SYSDC_NLISTS	8
34711173SJonathan.Adams@Sun.COM #define	SYSDC_HASH(sdc)	(((uintptr_t)(sdc) >> 6) & (SYSDC_NLISTS - 1))
34811173SJonathan.Adams@Sun.COM static sysdc_list_t	sysdc_active[SYSDC_NLISTS];
34911173SJonathan.Adams@Sun.COM #define	SYSDC_LIST(sdc)		(&sysdc_active[SYSDC_HASH(sdc)])
35011173SJonathan.Adams@Sun.COM 
35111173SJonathan.Adams@Sun.COM #ifdef DEBUG
35211173SJonathan.Adams@Sun.COM static struct {
35311173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_asleep;
35411173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_base_ran_backwards;
35511173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_already_done;
35611173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_times_cur_ran_backwards;
35711173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_compute_pri_breaking;
35811173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_activate_enter;
35911173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_enter;
36011173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_exited;
36111173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_not_sdc;
36211173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_idle;
36311173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_take_break;
36411173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_update_no_psets;
36511173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_tick_not_sdc;
36611173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_tick_quantum_expired;
36711173SJonathan.Adams@Sun.COM 	uint64_t	sysdc_thread_enter_enter;
36811173SJonathan.Adams@Sun.COM } sysdc_stats;
36911173SJonathan.Adams@Sun.COM 
37011173SJonathan.Adams@Sun.COM #define	SYSDC_INC_STAT(x)	(sysdc_stats.x++)
37111173SJonathan.Adams@Sun.COM #else
37211173SJonathan.Adams@Sun.COM #define	SYSDC_INC_STAT(x)	((void)0)
37311173SJonathan.Adams@Sun.COM #endif
37411173SJonathan.Adams@Sun.COM 
37511173SJonathan.Adams@Sun.COM /* macros are UPPER CASE */
37611173SJonathan.Adams@Sun.COM #define	HOWMANY(a, b)	howmany((a), (b))
37711173SJonathan.Adams@Sun.COM #define	MSECTOTICKS(a)	HOWMANY((a) * 1000, usec_per_tick)
37811173SJonathan.Adams@Sun.COM 
37911173SJonathan.Adams@Sun.COM static void
38011173SJonathan.Adams@Sun.COM sysdc_initparam(void)
38111173SJonathan.Adams@Sun.COM {
38211173SJonathan.Adams@Sun.COM 	uint_t sysdc_break_ticks;
38311173SJonathan.Adams@Sun.COM 
38411173SJonathan.Adams@Sun.COM 	/* update / prune intervals */
38511173SJonathan.Adams@Sun.COM 	sysdc_update_ticks = MSECTOTICKS(sysdc_update_interval_msec);
38611173SJonathan.Adams@Sun.COM 
38711173SJonathan.Adams@Sun.COM 	sysdc_prune_updates = HOWMANY(sysdc_prune_interval_msec,
38811173SJonathan.Adams@Sun.COM 	    sysdc_update_interval_msec);
38911173SJonathan.Adams@Sun.COM 	sysdc_reset_updates = HOWMANY(sysdc_reset_interval_msec,
39011173SJonathan.Adams@Sun.COM 	    sysdc_update_interval_msec);
39111173SJonathan.Adams@Sun.COM 
39211173SJonathan.Adams@Sun.COM 	/* We must get at least a little time on CPU. */
39311173SJonathan.Adams@Sun.COM 	sysdc_minDC = 1;
39411173SJonathan.Adams@Sun.COM 	sysdc_maxDC = SYSDC_DC_MAX;
39511173SJonathan.Adams@Sun.COM 	sysdc_minpri = 0;
39611173SJonathan.Adams@Sun.COM 	sysdc_maxpri = maxclsyspri;
39711173SJonathan.Adams@Sun.COM 
39811173SJonathan.Adams@Sun.COM 	/* break parameters */
39911173SJonathan.Adams@Sun.COM 	if (sysdc_max_pset_DC > SYSDC_DC_MAX) {
40011173SJonathan.Adams@Sun.COM 		sysdc_max_pset_DC = SYSDC_DC_MAX;
40111173SJonathan.Adams@Sun.COM 	}
40211173SJonathan.Adams@Sun.COM 	sysdc_break_ticks = MSECTOTICKS(sysdc_break_msec);
40311173SJonathan.Adams@Sun.COM 	sysdc_break_updates = HOWMANY(sysdc_break_ticks, sysdc_update_ticks);
40411173SJonathan.Adams@Sun.COM 
40511173SJonathan.Adams@Sun.COM 	/*
40611173SJonathan.Adams@Sun.COM 	 * We want:
40711173SJonathan.Adams@Sun.COM 	 *
40811173SJonathan.Adams@Sun.COM 	 *	sysdc_max_pset_DC = (nobreak / (break + nobreak))
40911173SJonathan.Adams@Sun.COM 	 *
41011173SJonathan.Adams@Sun.COM 	 *	==>	  nobreak = sysdc_max_pset_DC * (break + nobreak)
41111173SJonathan.Adams@Sun.COM 	 *
41211173SJonathan.Adams@Sun.COM 	 *			    sysdc_max_pset_DC * break
41311173SJonathan.Adams@Sun.COM 	 *	==>	  nobreak = -------------------------
41411173SJonathan.Adams@Sun.COM 	 *			    1 - sysdc_max_pset_DC
41511173SJonathan.Adams@Sun.COM 	 */
41611173SJonathan.Adams@Sun.COM 	sysdc_nobreak_updates =
41711173SJonathan.Adams@Sun.COM 	    HOWMANY((uint64_t)sysdc_break_updates * sysdc_max_pset_DC,
41811173SJonathan.Adams@Sun.COM 	    (SYSDC_DC_MAX - sysdc_max_pset_DC));
41911173SJonathan.Adams@Sun.COM 
42011173SJonathan.Adams@Sun.COM 	sysdc_param_init = 1;
42111173SJonathan.Adams@Sun.COM }
42211173SJonathan.Adams@Sun.COM 
42311173SJonathan.Adams@Sun.COM #undef HOWMANY
42411173SJonathan.Adams@Sun.COM #undef MSECTOTICKS
42511173SJonathan.Adams@Sun.COM 
42611173SJonathan.Adams@Sun.COM #define	SDC_UPDATE_INITIAL	0x1	/* for the initial update */
42711173SJonathan.Adams@Sun.COM #define	SDC_UPDATE_TIMEOUT	0x2	/* from sysdc_update() */
42811173SJonathan.Adams@Sun.COM #define	SDC_UPDATE_TICK		0x4	/* from sysdc_tick(), on expiry */
42911173SJonathan.Adams@Sun.COM 
43011173SJonathan.Adams@Sun.COM /*
43111173SJonathan.Adams@Sun.COM  * Updates the recorded times in the sdc, and returns the elapsed ONPROC
43211173SJonathan.Adams@Sun.COM  * and Runnable times since the last reset.
43311173SJonathan.Adams@Sun.COM  *
43411173SJonathan.Adams@Sun.COM  * newO is the thread's actual ONPROC time; it's used during sysdc_update()
43511173SJonathan.Adams@Sun.COM  * to track processor set usage.
43611173SJonathan.Adams@Sun.COM  */
43711173SJonathan.Adams@Sun.COM static void
43811173SJonathan.Adams@Sun.COM sysdc_update_times(sysdc_t *sdc, uint_t flags,
43911173SJonathan.Adams@Sun.COM     hrtime_t *O, hrtime_t *R, hrtime_t *newO)
44011173SJonathan.Adams@Sun.COM {
44111173SJonathan.Adams@Sun.COM 	kthread_t *const t = sdc->sdc_thread;
44211173SJonathan.Adams@Sun.COM 	const uint_t	initial = (flags & SDC_UPDATE_INITIAL);
44311173SJonathan.Adams@Sun.COM 	const uint_t	update = (flags & SDC_UPDATE_TIMEOUT);
44411173SJonathan.Adams@Sun.COM 	const clock_t	now = ddi_get_lbolt();
44511173SJonathan.Adams@Sun.COM 	uint_t		do_reset;
44611173SJonathan.Adams@Sun.COM 
44711173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
44811173SJonathan.Adams@Sun.COM 
44911173SJonathan.Adams@Sun.COM 	*O = *R = 0;
45011173SJonathan.Adams@Sun.COM 
45111173SJonathan.Adams@Sun.COM 	/* If we've been sleeping, we know we haven't had any ONPROC time. */
45211173SJonathan.Adams@Sun.COM 	if (sdc->sdc_sleep_updates != 0 &&
45311173SJonathan.Adams@Sun.COM 	    sdc->sdc_sleep_updates != sdc->sdc_nupdates) {
45411173SJonathan.Adams@Sun.COM 		*newO = sdc->sdc_last_base_O;
45511173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_times_asleep);
45611173SJonathan.Adams@Sun.COM 		return;
45711173SJonathan.Adams@Sun.COM 	}
45811173SJonathan.Adams@Sun.COM 
45911173SJonathan.Adams@Sun.COM 	/*
46011173SJonathan.Adams@Sun.COM 	 * If this is our first update, or we've hit the reset point,
46111173SJonathan.Adams@Sun.COM 	 * we need to reset our base_{O,R}.  Once we've updated them, we
46211173SJonathan.Adams@Sun.COM 	 * report O and R for the entire prior interval.
46311173SJonathan.Adams@Sun.COM 	 */
46411173SJonathan.Adams@Sun.COM 	do_reset = initial;
46511173SJonathan.Adams@Sun.COM 	if (update) {
46611173SJonathan.Adams@Sun.COM 		++sdc->sdc_nupdates;
46711173SJonathan.Adams@Sun.COM 		if ((sdc->sdc_nupdates % sysdc_reset_updates) == 0)
46811173SJonathan.Adams@Sun.COM 			do_reset = 1;
46911173SJonathan.Adams@Sun.COM 	}
47011173SJonathan.Adams@Sun.COM 	if (do_reset) {
47111173SJonathan.Adams@Sun.COM 		hrtime_t baseO, baseR;
47211173SJonathan.Adams@Sun.COM 		if (initial) {
47311173SJonathan.Adams@Sun.COM 			/*
47411173SJonathan.Adams@Sun.COM 			 * Start off our cycle count somewhere in the middle,
47511173SJonathan.Adams@Sun.COM 			 * to keep the resets from all happening at once.
47611173SJonathan.Adams@Sun.COM 			 *
47711173SJonathan.Adams@Sun.COM 			 * 4999 is a handy prime much larger than
47811173SJonathan.Adams@Sun.COM 			 * sysdc_reset_updates, so that we don't run into
47911173SJonathan.Adams@Sun.COM 			 * trouble if the resolution is a multiple of
48011173SJonathan.Adams@Sun.COM 			 * sysdc_reset_updates.
48111173SJonathan.Adams@Sun.COM 			 */
48211173SJonathan.Adams@Sun.COM 			sdc->sdc_nupdates = (uint_t)((gethrtime() % 4999) %
48311173SJonathan.Adams@Sun.COM 			    sysdc_reset_updates);
48411173SJonathan.Adams@Sun.COM 			baseO = baseR = 0;
48511173SJonathan.Adams@Sun.COM 		} else {
48611173SJonathan.Adams@Sun.COM 			baseO = sdc->sdc_base_O;
48711173SJonathan.Adams@Sun.COM 			baseR = sdc->sdc_base_R;
48811173SJonathan.Adams@Sun.COM 		}
48911173SJonathan.Adams@Sun.COM 
49011173SJonathan.Adams@Sun.COM 		mstate_systhread_times(t, &sdc->sdc_base_O, &sdc->sdc_base_R);
49111173SJonathan.Adams@Sun.COM 		*newO = sdc->sdc_base_O;
49211173SJonathan.Adams@Sun.COM 
49311173SJonathan.Adams@Sun.COM 		sdc->sdc_reset = now;
49411173SJonathan.Adams@Sun.COM 		sdc->sdc_pri_check = -1; /* force mismatch below */
49511173SJonathan.Adams@Sun.COM 
49611173SJonathan.Adams@Sun.COM 		/*
49711173SJonathan.Adams@Sun.COM 		 * See below for rationale.
49811173SJonathan.Adams@Sun.COM 		 */
49911173SJonathan.Adams@Sun.COM 		if (baseO > sdc->sdc_base_O || baseR > sdc->sdc_base_R) {
50011173SJonathan.Adams@Sun.COM 			SYSDC_INC_STAT(sysdc_update_times_base_ran_backwards);
50111173SJonathan.Adams@Sun.COM 			baseO = sdc->sdc_base_O;
50211173SJonathan.Adams@Sun.COM 			baseR = sdc->sdc_base_R;
50311173SJonathan.Adams@Sun.COM 		}
50411173SJonathan.Adams@Sun.COM 
50511173SJonathan.Adams@Sun.COM 		/* compute based on the entire interval */
50611173SJonathan.Adams@Sun.COM 		*O = (sdc->sdc_base_O - baseO);
50711173SJonathan.Adams@Sun.COM 		*R = (sdc->sdc_base_R - baseR);
50811173SJonathan.Adams@Sun.COM 		return;
50911173SJonathan.Adams@Sun.COM 	}
51011173SJonathan.Adams@Sun.COM 
51111173SJonathan.Adams@Sun.COM 	/*
51211173SJonathan.Adams@Sun.COM 	 * If we're called from sysdc_update(), we *must* return a value
51311173SJonathan.Adams@Sun.COM 	 * for newO, so we always call mstate_systhread_times().
51411173SJonathan.Adams@Sun.COM 	 *
51511173SJonathan.Adams@Sun.COM 	 * Otherwise, if we've already done a pri check this tick,
51611173SJonathan.Adams@Sun.COM 	 * we can skip it.
51711173SJonathan.Adams@Sun.COM 	 */
51811173SJonathan.Adams@Sun.COM 	if (!update && sdc->sdc_pri_check == now) {
51911173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_times_already_done);
52011173SJonathan.Adams@Sun.COM 		return;
52111173SJonathan.Adams@Sun.COM 	}
52211173SJonathan.Adams@Sun.COM 
52311173SJonathan.Adams@Sun.COM 	/* Get the current times from the thread */
52411173SJonathan.Adams@Sun.COM 	sdc->sdc_pri_check = now;
52511173SJonathan.Adams@Sun.COM 	mstate_systhread_times(t, &sdc->sdc_cur_O, &sdc->sdc_cur_R);
52611173SJonathan.Adams@Sun.COM 	*newO = sdc->sdc_cur_O;
52711173SJonathan.Adams@Sun.COM 
52811173SJonathan.Adams@Sun.COM 	/*
52911173SJonathan.Adams@Sun.COM 	 * The updating of microstate accounting is not done under a
53011173SJonathan.Adams@Sun.COM 	 * consistent set of locks, particularly the t_waitrq field.  This
53111173SJonathan.Adams@Sun.COM 	 * can lead to narrow windows in which we account for time in the
53211173SJonathan.Adams@Sun.COM 	 * wrong bucket, which on the next read will be accounted for
53311173SJonathan.Adams@Sun.COM 	 * correctly.
53411173SJonathan.Adams@Sun.COM 	 *
53511173SJonathan.Adams@Sun.COM 	 * If our sdc_base_* fields were affected by one of these blips, we
53611173SJonathan.Adams@Sun.COM 	 * throw away the old data, and pretend this tick didn't happen.
53711173SJonathan.Adams@Sun.COM 	 */
53811173SJonathan.Adams@Sun.COM 	if (sdc->sdc_cur_O < sdc->sdc_base_O ||
53911173SJonathan.Adams@Sun.COM 	    sdc->sdc_cur_R < sdc->sdc_base_R) {
54011173SJonathan.Adams@Sun.COM 
54111173SJonathan.Adams@Sun.COM 		sdc->sdc_base_O = sdc->sdc_cur_O;
54211173SJonathan.Adams@Sun.COM 		sdc->sdc_base_R = sdc->sdc_cur_R;
54311173SJonathan.Adams@Sun.COM 
54411173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_times_cur_ran_backwards);
54511173SJonathan.Adams@Sun.COM 		return;
54611173SJonathan.Adams@Sun.COM 	}
54711173SJonathan.Adams@Sun.COM 
54811173SJonathan.Adams@Sun.COM 	*O = sdc->sdc_cur_O - sdc->sdc_base_O;
54911173SJonathan.Adams@Sun.COM 	*R = sdc->sdc_cur_R - sdc->sdc_base_R;
55011173SJonathan.Adams@Sun.COM }
55111173SJonathan.Adams@Sun.COM 
55211173SJonathan.Adams@Sun.COM /*
55311173SJonathan.Adams@Sun.COM  * sysdc_compute_pri()
55411173SJonathan.Adams@Sun.COM  *
55511173SJonathan.Adams@Sun.COM  *	Recomputes the priority of the thread, leaving the result in
55611173SJonathan.Adams@Sun.COM  *	sdc->sdc_epri.  Returns 1 if a priority update should occur
55711173SJonathan.Adams@Sun.COM  *	(which will also trigger a cpu_surrender()), otherwise
55811173SJonathan.Adams@Sun.COM  *	returns 0.
55911173SJonathan.Adams@Sun.COM  */
56011173SJonathan.Adams@Sun.COM static uint_t
56111173SJonathan.Adams@Sun.COM sysdc_compute_pri(sysdc_t *sdc, uint_t flags)
56211173SJonathan.Adams@Sun.COM {
56311173SJonathan.Adams@Sun.COM 	kthread_t *const t = sdc->sdc_thread;
56411173SJonathan.Adams@Sun.COM 	const uint_t	update = (flags & SDC_UPDATE_TIMEOUT);
56511173SJonathan.Adams@Sun.COM 	const uint_t	tick = (flags & SDC_UPDATE_TICK);
56611173SJonathan.Adams@Sun.COM 
56711173SJonathan.Adams@Sun.COM 	hrtime_t	O, R;
56811173SJonathan.Adams@Sun.COM 	hrtime_t	newO = -1;
56911173SJonathan.Adams@Sun.COM 
57011173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
57111173SJonathan.Adams@Sun.COM 
57211173SJonathan.Adams@Sun.COM 	sysdc_update_times(sdc, flags, &O, &R, &newO);
57311173SJonathan.Adams@Sun.COM 	ASSERT(!update || newO != -1);
57411173SJonathan.Adams@Sun.COM 
57511173SJonathan.Adams@Sun.COM 	/* If we have new data, recompute our priority. */
57611173SJonathan.Adams@Sun.COM 	if ((O + R) != 0) {
57711173SJonathan.Adams@Sun.COM 		sdc->sdc_cur_DC = (O * SYSDC_DC_MAX) / (O + R);
57811173SJonathan.Adams@Sun.COM 
57911173SJonathan.Adams@Sun.COM 		/* Adjust our priority to move our DC closer to the target. */
58011173SJonathan.Adams@Sun.COM 		if (sdc->sdc_cur_DC < sdc->sdc_target_DC)
58111173SJonathan.Adams@Sun.COM 			sdc->sdc_pri = sdc->sdc_maxpri;
58211173SJonathan.Adams@Sun.COM 		else
58311173SJonathan.Adams@Sun.COM 			sdc->sdc_pri = sdc->sdc_minpri;
58411173SJonathan.Adams@Sun.COM 	}
58511173SJonathan.Adams@Sun.COM 
58611173SJonathan.Adams@Sun.COM 	/*
58711173SJonathan.Adams@Sun.COM 	 * If our per-pset duty cycle goes over the max, we will take a break.
58811173SJonathan.Adams@Sun.COM 	 * This forces all sysdc threads in the pset to minimum priority, in
58911173SJonathan.Adams@Sun.COM 	 * order to let everyone else have a chance at the CPU.
59011173SJonathan.Adams@Sun.COM 	 */
59111173SJonathan.Adams@Sun.COM 	if (sdc->sdc_pset->sdp_need_break) {
59211173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_compute_pri_breaking);
59311173SJonathan.Adams@Sun.COM 		sdc->sdc_epri = sdc->sdc_minpri;
59411173SJonathan.Adams@Sun.COM 	} else {
59511173SJonathan.Adams@Sun.COM 		sdc->sdc_epri = sdc->sdc_pri;
59611173SJonathan.Adams@Sun.COM 	}
59711173SJonathan.Adams@Sun.COM 
59811173SJonathan.Adams@Sun.COM 	DTRACE_PROBE4(sysdc__compute__pri,
59911173SJonathan.Adams@Sun.COM 	    kthread_t *, t, pri_t, sdc->sdc_epri, uint_t, sdc->sdc_cur_DC,
60011173SJonathan.Adams@Sun.COM 	    uint_t, sdc->sdc_target_DC);
60111173SJonathan.Adams@Sun.COM 
60211173SJonathan.Adams@Sun.COM 	/*
60311173SJonathan.Adams@Sun.COM 	 * For sysdc_update(), we compute the ONPROC time for high-priority
60411173SJonathan.Adams@Sun.COM 	 * threads, which is used to calculate the per-pset duty cycle.  We
60511173SJonathan.Adams@Sun.COM 	 * will always tell our callers to update the thread's priority,
60611173SJonathan.Adams@Sun.COM 	 * since we want to force a cpu_surrender().
60711173SJonathan.Adams@Sun.COM 	 *
60811173SJonathan.Adams@Sun.COM 	 * We reset sdc_update_ticks so that sysdc_tick() will only update
60911173SJonathan.Adams@Sun.COM 	 * the thread's priority if our timeout is delayed by a tick or
61011173SJonathan.Adams@Sun.COM 	 * more.
61111173SJonathan.Adams@Sun.COM 	 */
61211173SJonathan.Adams@Sun.COM 	if (update) {
61311173SJonathan.Adams@Sun.COM 		/* SDC threads are not allowed to change cpupart bindings. */
61411173SJonathan.Adams@Sun.COM 		ASSERT(t->t_cpupart == sdc->sdc_pset->sdp_cpupart);
61511173SJonathan.Adams@Sun.COM 
61611173SJonathan.Adams@Sun.COM 		/* If we were at MAXPRI, account for our onproc time. */
61711173SJonathan.Adams@Sun.COM 		if (t->t_pri == sdc->sdc_maxpri &&
61811173SJonathan.Adams@Sun.COM 		    sdc->sdc_last_base_O != 0 &&
61911173SJonathan.Adams@Sun.COM 		    sdc->sdc_last_base_O < newO) {
62011173SJonathan.Adams@Sun.COM 			sdc->sdc_last_O = newO - sdc->sdc_last_base_O;
62111173SJonathan.Adams@Sun.COM 			sdc->sdc_pset->sdp_onproc_time +=
62211173SJonathan.Adams@Sun.COM 			    (uint64_t)sdc->sdc_last_O;
62311173SJonathan.Adams@Sun.COM 			sdc->sdc_pset->sdp_onproc_threads++;
62411173SJonathan.Adams@Sun.COM 		} else {
62511173SJonathan.Adams@Sun.COM 			sdc->sdc_last_O = 0;
62611173SJonathan.Adams@Sun.COM 		}
62711173SJonathan.Adams@Sun.COM 		sdc->sdc_last_base_O = newO;
62811173SJonathan.Adams@Sun.COM 
62911173SJonathan.Adams@Sun.COM 		sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks + 1;
63011173SJonathan.Adams@Sun.COM 		return (1);
63111173SJonathan.Adams@Sun.COM 	}
63211173SJonathan.Adams@Sun.COM 
63311173SJonathan.Adams@Sun.COM 	/*
63411173SJonathan.Adams@Sun.COM 	 * Like sysdc_update(), sysdc_tick() always wants to update the
63511173SJonathan.Adams@Sun.COM 	 * thread's priority, so that the CPU is surrendered if necessary.
63611173SJonathan.Adams@Sun.COM 	 * We reset sdc_update_ticks so that if the timeout continues to be
63711173SJonathan.Adams@Sun.COM 	 * delayed, we'll update at the regular interval.
63811173SJonathan.Adams@Sun.COM 	 */
63911173SJonathan.Adams@Sun.COM 	if (tick) {
64011173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_ticks == sdc->sdc_update_ticks);
64111173SJonathan.Adams@Sun.COM 		sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks;
64211173SJonathan.Adams@Sun.COM 		return (1);
64311173SJonathan.Adams@Sun.COM 	}
64411173SJonathan.Adams@Sun.COM 
64511173SJonathan.Adams@Sun.COM 	/*
64611173SJonathan.Adams@Sun.COM 	 * Otherwise, only tell our callers to update the priority if it has
64711173SJonathan.Adams@Sun.COM 	 * changed.
64811173SJonathan.Adams@Sun.COM 	 */
64911173SJonathan.Adams@Sun.COM 	return (sdc->sdc_epri != t->t_pri);
65011173SJonathan.Adams@Sun.COM }
65111173SJonathan.Adams@Sun.COM 
65211173SJonathan.Adams@Sun.COM static void
65311173SJonathan.Adams@Sun.COM sysdc_update_pri(sysdc_t *sdc, uint_t flags)
65411173SJonathan.Adams@Sun.COM {
65511173SJonathan.Adams@Sun.COM 	kthread_t *t = sdc->sdc_thread;
65611173SJonathan.Adams@Sun.COM 
65711173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
65811173SJonathan.Adams@Sun.COM 
65911173SJonathan.Adams@Sun.COM 	if (sysdc_compute_pri(sdc, flags)) {
66011173SJonathan.Adams@Sun.COM 		if (!thread_change_pri(t, sdc->sdc_epri, 0)) {
66111173SJonathan.Adams@Sun.COM 			cpu_surrender(t);
66211173SJonathan.Adams@Sun.COM 		}
66311173SJonathan.Adams@Sun.COM 	}
66411173SJonathan.Adams@Sun.COM }
66511173SJonathan.Adams@Sun.COM 
66611173SJonathan.Adams@Sun.COM /*
66711173SJonathan.Adams@Sun.COM  * Add a thread onto the active list.  It will only be removed by
66811173SJonathan.Adams@Sun.COM  * sysdc_update().
66911173SJonathan.Adams@Sun.COM  */
67011173SJonathan.Adams@Sun.COM static void
67111173SJonathan.Adams@Sun.COM sysdc_activate(sysdc_t *sdc)
67211173SJonathan.Adams@Sun.COM {
67311173SJonathan.Adams@Sun.COM 	sysdc_t *volatile *headp = &SYSDC_LIST(sdc)->sdl_list;
67411173SJonathan.Adams@Sun.COM 	sysdc_t		*head;
67511173SJonathan.Adams@Sun.COM 	kthread_t	*t = sdc->sdc_thread;
67611173SJonathan.Adams@Sun.COM 
67711173SJonathan.Adams@Sun.COM 	SYSDC_INC_STAT(sysdc_activate_enter);
67811173SJonathan.Adams@Sun.COM 
67911173SJonathan.Adams@Sun.COM 	ASSERT(sdc->sdc_next == NULL);
68011173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
68111173SJonathan.Adams@Sun.COM 
68211173SJonathan.Adams@Sun.COM 	do {
68311173SJonathan.Adams@Sun.COM 		head = *headp;
68411173SJonathan.Adams@Sun.COM 		sdc->sdc_next = head;
68511173SJonathan.Adams@Sun.COM 	} while (atomic_cas_ptr(headp, head, sdc) != head);
68611173SJonathan.Adams@Sun.COM }
68711173SJonathan.Adams@Sun.COM 
68811173SJonathan.Adams@Sun.COM /*
68911173SJonathan.Adams@Sun.COM  * sysdc_update() has two jobs:
69011173SJonathan.Adams@Sun.COM  *
69111173SJonathan.Adams@Sun.COM  *	1. It updates the priorities of all active SDC threads on the system.
69211173SJonathan.Adams@Sun.COM  *	2. It measures pset CPU usage and enforces sysdc_max_pset_DC.
69311173SJonathan.Adams@Sun.COM  */
69411173SJonathan.Adams@Sun.COM static void
69511173SJonathan.Adams@Sun.COM sysdc_update(void *arg)
69611173SJonathan.Adams@Sun.COM {
69711173SJonathan.Adams@Sun.COM 	int		idx;
69811173SJonathan.Adams@Sun.COM 	sysdc_t		*freelist = NULL;
69911173SJonathan.Adams@Sun.COM 	sysdc_pset_t	*cur;
70011173SJonathan.Adams@Sun.COM 	hrtime_t	now, diff;
70111173SJonathan.Adams@Sun.COM 	uint_t		redeploy = 1;
70211173SJonathan.Adams@Sun.COM 
70311173SJonathan.Adams@Sun.COM 	SYSDC_INC_STAT(sysdc_update_enter);
70411173SJonathan.Adams@Sun.COM 
70511173SJonathan.Adams@Sun.COM 	ASSERT(sysdc_update_timeout_started);
70611173SJonathan.Adams@Sun.COM 
70711173SJonathan.Adams@Sun.COM 	/*
70811173SJonathan.Adams@Sun.COM 	 * If this is our first time through, diff will be gigantic, and
70911173SJonathan.Adams@Sun.COM 	 * no breaks will be necessary.
71011173SJonathan.Adams@Sun.COM 	 */
71111173SJonathan.Adams@Sun.COM 	now = gethrtime();
71211173SJonathan.Adams@Sun.COM 	diff = now - sysdc_last_update;
71311173SJonathan.Adams@Sun.COM 	sysdc_last_update = now;
71411173SJonathan.Adams@Sun.COM 
71511173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
71611173SJonathan.Adams@Sun.COM 	for (cur = list_head(&sysdc_psets); cur != NULL;
71711173SJonathan.Adams@Sun.COM 	    cur = list_next(&sysdc_psets, cur)) {
71811173SJonathan.Adams@Sun.COM 		boolean_t breaking = (cur->sdp_should_break != 0);
71911173SJonathan.Adams@Sun.COM 
72011173SJonathan.Adams@Sun.COM 		if (cur->sdp_need_break != breaking) {
72111173SJonathan.Adams@Sun.COM 			DTRACE_PROBE2(sdc__pset__break, sysdc_pset_t *, cur,
72211173SJonathan.Adams@Sun.COM 			    boolean_t, breaking);
72311173SJonathan.Adams@Sun.COM 		}
72411173SJonathan.Adams@Sun.COM 		cur->sdp_onproc_time = 0;
72511173SJonathan.Adams@Sun.COM 		cur->sdp_onproc_threads = 0;
72611173SJonathan.Adams@Sun.COM 		cur->sdp_need_break = breaking;
72711173SJonathan.Adams@Sun.COM 	}
72811173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
72911173SJonathan.Adams@Sun.COM 
73011173SJonathan.Adams@Sun.COM 	for (idx = 0; idx < SYSDC_NLISTS; idx++) {
73111173SJonathan.Adams@Sun.COM 		sysdc_list_t		*sdl = &sysdc_active[idx];
73211173SJonathan.Adams@Sun.COM 		sysdc_t *volatile	*headp = &sdl->sdl_list;
73311173SJonathan.Adams@Sun.COM 		sysdc_t			*head, *tail;
73411173SJonathan.Adams@Sun.COM 		sysdc_t			**prevptr;
73511173SJonathan.Adams@Sun.COM 
73611173SJonathan.Adams@Sun.COM 		if (*headp == &sysdc_dummy)
73711173SJonathan.Adams@Sun.COM 			continue;
73811173SJonathan.Adams@Sun.COM 
73911173SJonathan.Adams@Sun.COM 		/* Prevent any threads from exiting while we're poking them. */
74011173SJonathan.Adams@Sun.COM 		mutex_enter(&sdl->sdl_lock);
74111173SJonathan.Adams@Sun.COM 
74211173SJonathan.Adams@Sun.COM 		/*
74311173SJonathan.Adams@Sun.COM 		 * Each sdl_list contains a singly-linked list of active
74411173SJonathan.Adams@Sun.COM 		 * threads. Threads which become active while we are
74511173SJonathan.Adams@Sun.COM 		 * processing the list will be added to sdl_list.  Since we
74611173SJonathan.Adams@Sun.COM 		 * don't want that to interfere with our own processing, we
74711173SJonathan.Adams@Sun.COM 		 * swap in an empty list.  Any newly active threads will
74811173SJonathan.Adams@Sun.COM 		 * go on to this empty list.  When finished, we'll put any
74911173SJonathan.Adams@Sun.COM 		 * such threads at the end of the processed list.
75011173SJonathan.Adams@Sun.COM 		 */
75111173SJonathan.Adams@Sun.COM 		head = atomic_swap_ptr(headp, &sysdc_dummy);
75211173SJonathan.Adams@Sun.COM 		prevptr = &head;
75311173SJonathan.Adams@Sun.COM 		while (*prevptr != &sysdc_dummy) {
75411173SJonathan.Adams@Sun.COM 			sysdc_t		*const	sdc = *prevptr;
75511173SJonathan.Adams@Sun.COM 			kthread_t	*const	t = sdc->sdc_thread;
75611173SJonathan.Adams@Sun.COM 
75711173SJonathan.Adams@Sun.COM 			/*
75811173SJonathan.Adams@Sun.COM 			 * If the thread has exited, move its sysdc_t onto
75911173SJonathan.Adams@Sun.COM 			 * freelist, to be freed later.
76011173SJonathan.Adams@Sun.COM 			 */
76111173SJonathan.Adams@Sun.COM 			if (t == NULL) {
76211173SJonathan.Adams@Sun.COM 				*prevptr = sdc->sdc_next;
76311173SJonathan.Adams@Sun.COM 				SYSDC_INC_STAT(sysdc_update_exited);
76411173SJonathan.Adams@Sun.COM 				sdc->sdc_next = freelist;
76511173SJonathan.Adams@Sun.COM 				freelist = sdc;
76611173SJonathan.Adams@Sun.COM 				continue;
76711173SJonathan.Adams@Sun.COM 			}
76811173SJonathan.Adams@Sun.COM 
76911173SJonathan.Adams@Sun.COM 			thread_lock(t);
77011173SJonathan.Adams@Sun.COM 			if (t->t_cid != sysdccid) {
77111173SJonathan.Adams@Sun.COM 				thread_unlock(t);
77211173SJonathan.Adams@Sun.COM 				prevptr = &sdc->sdc_next;
77311173SJonathan.Adams@Sun.COM 				SYSDC_INC_STAT(sysdc_update_not_sdc);
77411173SJonathan.Adams@Sun.COM 				continue;
77511173SJonathan.Adams@Sun.COM 			}
77611173SJonathan.Adams@Sun.COM 			ASSERT(t->t_cldata == sdc);
77711173SJonathan.Adams@Sun.COM 
77811173SJonathan.Adams@Sun.COM 			/*
77911173SJonathan.Adams@Sun.COM 			 * If the thread has been sleeping for longer
78011173SJonathan.Adams@Sun.COM 			 * than sysdc_prune_interval, make it inactive by
78111173SJonathan.Adams@Sun.COM 			 * removing it from the list.
78211173SJonathan.Adams@Sun.COM 			 */
78311173SJonathan.Adams@Sun.COM 			if (!(t->t_state & (TS_RUN | TS_ONPROC)) &&
78411173SJonathan.Adams@Sun.COM 			    sdc->sdc_sleep_updates != 0 &&
78511173SJonathan.Adams@Sun.COM 			    (sdc->sdc_sleep_updates - sdc->sdc_nupdates) >
78611173SJonathan.Adams@Sun.COM 			    sysdc_prune_updates) {
78711173SJonathan.Adams@Sun.COM 				*prevptr = sdc->sdc_next;
78811173SJonathan.Adams@Sun.COM 				SYSDC_INC_STAT(sysdc_update_idle);
78911173SJonathan.Adams@Sun.COM 				sdc->sdc_next = NULL;
79011173SJonathan.Adams@Sun.COM 				thread_unlock(t);
79111173SJonathan.Adams@Sun.COM 				continue;
79211173SJonathan.Adams@Sun.COM 			}
79311173SJonathan.Adams@Sun.COM 			sysdc_update_pri(sdc, SDC_UPDATE_TIMEOUT);
79411173SJonathan.Adams@Sun.COM 			thread_unlock(t);
79511173SJonathan.Adams@Sun.COM 
79611173SJonathan.Adams@Sun.COM 			prevptr = &sdc->sdc_next;
79711173SJonathan.Adams@Sun.COM 		}
79811173SJonathan.Adams@Sun.COM 
79911173SJonathan.Adams@Sun.COM 		/*
80011173SJonathan.Adams@Sun.COM 		 * Add our list to the bucket, putting any new entries
80111173SJonathan.Adams@Sun.COM 		 * added while we were working at the tail of the list.
80211173SJonathan.Adams@Sun.COM 		 */
80311173SJonathan.Adams@Sun.COM 		do {
80411173SJonathan.Adams@Sun.COM 			tail = *headp;
80511173SJonathan.Adams@Sun.COM 			*prevptr = tail;
80611173SJonathan.Adams@Sun.COM 		} while (atomic_cas_ptr(headp, tail, head) != tail);
80711173SJonathan.Adams@Sun.COM 
80811173SJonathan.Adams@Sun.COM 		mutex_exit(&sdl->sdl_lock);
80911173SJonathan.Adams@Sun.COM 	}
81011173SJonathan.Adams@Sun.COM 
81111173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
81211173SJonathan.Adams@Sun.COM 	for (cur = list_head(&sysdc_psets); cur != NULL;
81311173SJonathan.Adams@Sun.COM 	    cur = list_next(&sysdc_psets, cur)) {
81411173SJonathan.Adams@Sun.COM 
81511173SJonathan.Adams@Sun.COM 		cur->sdp_vtime_last_interval =
81611173SJonathan.Adams@Sun.COM 		    diff * cur->sdp_cpupart->cp_ncpus;
81711173SJonathan.Adams@Sun.COM 		cur->sdp_DC_last_interval =
81811173SJonathan.Adams@Sun.COM 		    (cur->sdp_onproc_time * SYSDC_DC_MAX) /
81911173SJonathan.Adams@Sun.COM 		    cur->sdp_vtime_last_interval;
82011173SJonathan.Adams@Sun.COM 
82111173SJonathan.Adams@Sun.COM 		if (cur->sdp_should_break > 0) {
82211173SJonathan.Adams@Sun.COM 			cur->sdp_should_break--;	/* breaking */
82311173SJonathan.Adams@Sun.COM 			continue;
82411173SJonathan.Adams@Sun.COM 		}
82511173SJonathan.Adams@Sun.COM 		if (cur->sdp_dont_break > 0) {
82611173SJonathan.Adams@Sun.COM 			cur->sdp_dont_break--;	/* waiting before checking */
82711173SJonathan.Adams@Sun.COM 			continue;
82811173SJonathan.Adams@Sun.COM 		}
82911173SJonathan.Adams@Sun.COM 		if (cur->sdp_DC_last_interval > sysdc_max_pset_DC) {
83011173SJonathan.Adams@Sun.COM 			cur->sdp_should_break = sysdc_break_updates;
83111173SJonathan.Adams@Sun.COM 			cur->sdp_dont_break = sysdc_nobreak_updates;
83211173SJonathan.Adams@Sun.COM 			SYSDC_INC_STAT(sysdc_update_take_break);
83311173SJonathan.Adams@Sun.COM 		}
83411173SJonathan.Adams@Sun.COM 	}
83511173SJonathan.Adams@Sun.COM 
83611173SJonathan.Adams@Sun.COM 	/*
83711173SJonathan.Adams@Sun.COM 	 * If there are no sysdc_psets, there can be no threads, so
83811173SJonathan.Adams@Sun.COM 	 * we can stop doing our timeout.  Since we're holding the
83911173SJonathan.Adams@Sun.COM 	 * sysdc_pset_lock, no new sysdc_psets can come in, which will
84011173SJonathan.Adams@Sun.COM 	 * prevent anyone from racing with this and dropping our timeout
84111173SJonathan.Adams@Sun.COM 	 * on the floor.
84211173SJonathan.Adams@Sun.COM 	 */
84311173SJonathan.Adams@Sun.COM 	if (list_is_empty(&sysdc_psets)) {
84411173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_update_no_psets);
84511173SJonathan.Adams@Sun.COM 		ASSERT(sysdc_update_timeout_started);
84611173SJonathan.Adams@Sun.COM 		sysdc_update_timeout_started = 0;
84711173SJonathan.Adams@Sun.COM 
84811173SJonathan.Adams@Sun.COM 		redeploy = 0;
84911173SJonathan.Adams@Sun.COM 	}
85011173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
85111173SJonathan.Adams@Sun.COM 
85211173SJonathan.Adams@Sun.COM 	while (freelist != NULL) {
85311173SJonathan.Adams@Sun.COM 		sysdc_t *cur = freelist;
85411173SJonathan.Adams@Sun.COM 		freelist = cur->sdc_next;
85511173SJonathan.Adams@Sun.COM 		kmem_free(cur, sizeof (*cur));
85611173SJonathan.Adams@Sun.COM 	}
85711173SJonathan.Adams@Sun.COM 
85811173SJonathan.Adams@Sun.COM 	if (redeploy) {
85911173SJonathan.Adams@Sun.COM 		(void) timeout(sysdc_update, arg, sysdc_update_ticks);
86011173SJonathan.Adams@Sun.COM 	}
86111173SJonathan.Adams@Sun.COM }
86211173SJonathan.Adams@Sun.COM 
86311173SJonathan.Adams@Sun.COM static void
86411173SJonathan.Adams@Sun.COM sysdc_preempt(kthread_t *t)
86511173SJonathan.Adams@Sun.COM {
86611173SJonathan.Adams@Sun.COM 	ASSERT(t == curthread);
86711173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));
86811173SJonathan.Adams@Sun.COM 
86911173SJonathan.Adams@Sun.COM 	setbackdq(t);		/* give others a chance to run */
87011173SJonathan.Adams@Sun.COM }
87111173SJonathan.Adams@Sun.COM 
87211173SJonathan.Adams@Sun.COM static void
87311173SJonathan.Adams@Sun.COM sysdc_tick(kthread_t *t)
87411173SJonathan.Adams@Sun.COM {
87511173SJonathan.Adams@Sun.COM 	sysdc_t *sdc;
87611173SJonathan.Adams@Sun.COM 
87711173SJonathan.Adams@Sun.COM 	thread_lock(t);
87811173SJonathan.Adams@Sun.COM 	if (t->t_cid != sysdccid) {
87911173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_tick_not_sdc);
88011173SJonathan.Adams@Sun.COM 		thread_unlock(t);
88111173SJonathan.Adams@Sun.COM 		return;
88211173SJonathan.Adams@Sun.COM 	}
88311173SJonathan.Adams@Sun.COM 	sdc = t->t_cldata;
88411173SJonathan.Adams@Sun.COM 	if (t->t_state == TS_ONPROC &&
88511173SJonathan.Adams@Sun.COM 	    t->t_pri < t->t_disp_queue->disp_maxrunpri) {
88611173SJonathan.Adams@Sun.COM 		cpu_surrender(t);
88711173SJonathan.Adams@Sun.COM 	}
88811173SJonathan.Adams@Sun.COM 
88911173SJonathan.Adams@Sun.COM 	if (t->t_state == TS_ONPROC || t->t_state == TS_RUN) {
89011173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_sleep_updates == 0);
89111173SJonathan.Adams@Sun.COM 	}
89211173SJonathan.Adams@Sun.COM 
89311173SJonathan.Adams@Sun.COM 	ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks);
89411173SJonathan.Adams@Sun.COM 	sdc->sdc_ticks++;
89511173SJonathan.Adams@Sun.COM 	if (sdc->sdc_ticks == sdc->sdc_update_ticks) {
89611173SJonathan.Adams@Sun.COM 		SYSDC_INC_STAT(sysdc_tick_quantum_expired);
89711173SJonathan.Adams@Sun.COM 		sysdc_update_pri(sdc, SDC_UPDATE_TICK);
89811173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks);
89911173SJonathan.Adams@Sun.COM 	}
90011173SJonathan.Adams@Sun.COM 	thread_unlock(t);
90111173SJonathan.Adams@Sun.COM }
90211173SJonathan.Adams@Sun.COM 
90311173SJonathan.Adams@Sun.COM static void
90411173SJonathan.Adams@Sun.COM sysdc_setrun(kthread_t *t)
90511173SJonathan.Adams@Sun.COM {
90611173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = t->t_cldata;
90711173SJonathan.Adams@Sun.COM 
90811173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));	/* t should be in transition */
90911173SJonathan.Adams@Sun.COM 
91011173SJonathan.Adams@Sun.COM 	sdc->sdc_sleep_updates = 0;
91111173SJonathan.Adams@Sun.COM 
91211173SJonathan.Adams@Sun.COM 	if (sdc->sdc_next == NULL) {
91311173SJonathan.Adams@Sun.COM 		/*
91411173SJonathan.Adams@Sun.COM 		 * Since we're in transition, we don't want to use the
91511173SJonathan.Adams@Sun.COM 		 * full thread_update_pri().
91611173SJonathan.Adams@Sun.COM 		 */
91711173SJonathan.Adams@Sun.COM 		if (sysdc_compute_pri(sdc, 0)) {
91811173SJonathan.Adams@Sun.COM 			THREAD_CHANGE_PRI(t, sdc->sdc_epri);
91911173SJonathan.Adams@Sun.COM 		}
92011173SJonathan.Adams@Sun.COM 		sysdc_activate(sdc);
92111173SJonathan.Adams@Sun.COM 
92211173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_next != NULL);
92311173SJonathan.Adams@Sun.COM 	}
92411173SJonathan.Adams@Sun.COM 
92511173SJonathan.Adams@Sun.COM 	setbackdq(t);
92611173SJonathan.Adams@Sun.COM }
92711173SJonathan.Adams@Sun.COM 
92811173SJonathan.Adams@Sun.COM static void
92911173SJonathan.Adams@Sun.COM sysdc_wakeup(kthread_t *t)
93011173SJonathan.Adams@Sun.COM {
93111173SJonathan.Adams@Sun.COM 	sysdc_setrun(t);
93211173SJonathan.Adams@Sun.COM }
93311173SJonathan.Adams@Sun.COM 
93411173SJonathan.Adams@Sun.COM static void
93511173SJonathan.Adams@Sun.COM sysdc_sleep(kthread_t *t)
93611173SJonathan.Adams@Sun.COM {
93711173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = t->t_cldata;
93811173SJonathan.Adams@Sun.COM 
93911173SJonathan.Adams@Sun.COM 	ASSERT(THREAD_LOCK_HELD(t));	/* t should be in transition */
94011173SJonathan.Adams@Sun.COM 
94111173SJonathan.Adams@Sun.COM 	sdc->sdc_sleep_updates = sdc->sdc_nupdates;
94211173SJonathan.Adams@Sun.COM }
94311173SJonathan.Adams@Sun.COM 
94411173SJonathan.Adams@Sun.COM /*ARGSUSED*/
94511173SJonathan.Adams@Sun.COM static int
94611173SJonathan.Adams@Sun.COM sysdc_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp,
94711173SJonathan.Adams@Sun.COM     void *bufp)
94811173SJonathan.Adams@Sun.COM {
94911173SJonathan.Adams@Sun.COM 	cpupart_t *const cpupart = t->t_cpupart;
95011173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = bufp;
95111173SJonathan.Adams@Sun.COM 	sysdc_params_t *sdpp = parmsp;
95211173SJonathan.Adams@Sun.COM 	sysdc_pset_t *newpset = sdc->sdc_pset;
95311173SJonathan.Adams@Sun.COM 	sysdc_pset_t *pset;
95411173SJonathan.Adams@Sun.COM 	int start_timeout;
95511173SJonathan.Adams@Sun.COM 
95611173SJonathan.Adams@Sun.COM 	if (t->t_cid != syscid)
95711173SJonathan.Adams@Sun.COM 		return (EPERM);
95811173SJonathan.Adams@Sun.COM 
95911173SJonathan.Adams@Sun.COM 	ASSERT(ttolwp(t) != NULL);
96011173SJonathan.Adams@Sun.COM 	ASSERT(sdpp != NULL);
96111173SJonathan.Adams@Sun.COM 	ASSERT(newpset != NULL);
96211173SJonathan.Adams@Sun.COM 	ASSERT(sysdc_param_init);
96311173SJonathan.Adams@Sun.COM 
96411173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_minpri >= sysdc_minpri);
96511173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_maxpri <= sysdc_maxpri);
96611173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_DC >= sysdc_minDC);
96711173SJonathan.Adams@Sun.COM 	ASSERT(sdpp->sdp_DC <= sysdc_maxDC);
96811173SJonathan.Adams@Sun.COM 
96911173SJonathan.Adams@Sun.COM 	sdc->sdc_thread = t;
97011173SJonathan.Adams@Sun.COM 	sdc->sdc_pri = sdpp->sdp_maxpri;	/* start off maximally */
97111173SJonathan.Adams@Sun.COM 	sdc->sdc_minpri = sdpp->sdp_minpri;
97211173SJonathan.Adams@Sun.COM 	sdc->sdc_maxpri = sdpp->sdp_maxpri;
97311173SJonathan.Adams@Sun.COM 	sdc->sdc_target_DC = sdpp->sdp_DC;
97411173SJonathan.Adams@Sun.COM 	sdc->sdc_ticks = 0;
97511173SJonathan.Adams@Sun.COM 	sdc->sdc_update_ticks = sysdc_update_ticks + 1;
97611173SJonathan.Adams@Sun.COM 
97711173SJonathan.Adams@Sun.COM 	/* Assign ourselves to the appropriate pset. */
97811173SJonathan.Adams@Sun.COM 	sdc->sdc_pset = NULL;
97911173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
98011173SJonathan.Adams@Sun.COM 	for (pset = list_head(&sysdc_psets); pset != NULL;
98111173SJonathan.Adams@Sun.COM 	    pset = list_next(&sysdc_psets, pset)) {
98211173SJonathan.Adams@Sun.COM 		if (pset->sdp_cpupart == cpupart) {
98311173SJonathan.Adams@Sun.COM 			break;
98411173SJonathan.Adams@Sun.COM 		}
98511173SJonathan.Adams@Sun.COM 	}
98611173SJonathan.Adams@Sun.COM 	if (pset == NULL) {
98711173SJonathan.Adams@Sun.COM 		pset = newpset;
98811173SJonathan.Adams@Sun.COM 		newpset = NULL;
98911173SJonathan.Adams@Sun.COM 		pset->sdp_cpupart = cpupart;
99011173SJonathan.Adams@Sun.COM 		list_insert_tail(&sysdc_psets, pset);
99111173SJonathan.Adams@Sun.COM 	}
99211173SJonathan.Adams@Sun.COM 	pset->sdp_nthreads++;
99311173SJonathan.Adams@Sun.COM 	ASSERT(pset->sdp_nthreads > 0);
99411173SJonathan.Adams@Sun.COM 
99511173SJonathan.Adams@Sun.COM 	sdc->sdc_pset = pset;
99611173SJonathan.Adams@Sun.COM 
99711173SJonathan.Adams@Sun.COM 	start_timeout = (sysdc_update_timeout_started == 0);
99811173SJonathan.Adams@Sun.COM 	sysdc_update_timeout_started = 1;
99911173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
100011173SJonathan.Adams@Sun.COM 
100111173SJonathan.Adams@Sun.COM 	if (newpset != NULL)
100211173SJonathan.Adams@Sun.COM 		kmem_free(newpset, sizeof (*newpset));
100311173SJonathan.Adams@Sun.COM 
100411173SJonathan.Adams@Sun.COM 	/* Update t's scheduling class and priority. */
100511173SJonathan.Adams@Sun.COM 	thread_lock(t);
100611173SJonathan.Adams@Sun.COM 	t->t_clfuncs = &(sclass[cid].cl_funcs->thread);
100711173SJonathan.Adams@Sun.COM 	t->t_cid = cid;
100811173SJonathan.Adams@Sun.COM 	t->t_cldata = sdc;
100911173SJonathan.Adams@Sun.COM 	t->t_schedflag |= TS_RUNQMATCH;
101011173SJonathan.Adams@Sun.COM 
101111173SJonathan.Adams@Sun.COM 	sysdc_update_pri(sdc, SDC_UPDATE_INITIAL);
101211173SJonathan.Adams@Sun.COM 	thread_unlock(t);
101311173SJonathan.Adams@Sun.COM 
101411173SJonathan.Adams@Sun.COM 	/* Kick off the thread timeout if we're the first one in. */
101511173SJonathan.Adams@Sun.COM 	if (start_timeout) {
101611173SJonathan.Adams@Sun.COM 		(void) timeout(sysdc_update, NULL, sysdc_update_ticks);
101711173SJonathan.Adams@Sun.COM 	}
101811173SJonathan.Adams@Sun.COM 
101911173SJonathan.Adams@Sun.COM 	return (0);
102011173SJonathan.Adams@Sun.COM }
102111173SJonathan.Adams@Sun.COM 
102211173SJonathan.Adams@Sun.COM static void
102311173SJonathan.Adams@Sun.COM sysdc_leave(sysdc_t *sdc)
102411173SJonathan.Adams@Sun.COM {
102511173SJonathan.Adams@Sun.COM 	sysdc_pset_t *sdp = sdc->sdc_pset;
102611173SJonathan.Adams@Sun.COM 	sysdc_list_t *sdl = SYSDC_LIST(sdc);
102711173SJonathan.Adams@Sun.COM 	uint_t freedc;
102811173SJonathan.Adams@Sun.COM 
102911173SJonathan.Adams@Sun.COM 	mutex_enter(&sdl->sdl_lock);		/* block sysdc_update() */
103011173SJonathan.Adams@Sun.COM 	sdc->sdc_thread = NULL;
103111173SJonathan.Adams@Sun.COM 	freedc = (sdc->sdc_next == NULL);
103211173SJonathan.Adams@Sun.COM 	mutex_exit(&sdl->sdl_lock);
103311173SJonathan.Adams@Sun.COM 
103411173SJonathan.Adams@Sun.COM 	mutex_enter(&sysdc_pset_lock);
103511173SJonathan.Adams@Sun.COM 	sdp = sdc->sdc_pset;
103611173SJonathan.Adams@Sun.COM 	ASSERT(sdp != NULL);
103711173SJonathan.Adams@Sun.COM 	ASSERT(sdp->sdp_nthreads > 0);
103811173SJonathan.Adams@Sun.COM 	--sdp->sdp_nthreads;
103911173SJonathan.Adams@Sun.COM 	if (sdp->sdp_nthreads == 0) {
104011173SJonathan.Adams@Sun.COM 		list_remove(&sysdc_psets, sdp);
104111173SJonathan.Adams@Sun.COM 	} else {
104211173SJonathan.Adams@Sun.COM 		sdp = NULL;
104311173SJonathan.Adams@Sun.COM 	}
104411173SJonathan.Adams@Sun.COM 	mutex_exit(&sysdc_pset_lock);
104511173SJonathan.Adams@Sun.COM 
104611173SJonathan.Adams@Sun.COM 	if (freedc)
104711173SJonathan.Adams@Sun.COM 		kmem_free(sdc, sizeof (*sdc));
104811173SJonathan.Adams@Sun.COM 	if (sdp != NULL)
104911173SJonathan.Adams@Sun.COM 		kmem_free(sdp, sizeof (*sdp));
105011173SJonathan.Adams@Sun.COM }
105111173SJonathan.Adams@Sun.COM 
105211173SJonathan.Adams@Sun.COM static void
105311173SJonathan.Adams@Sun.COM sysdc_exitclass(void *buf)
105411173SJonathan.Adams@Sun.COM {
105511173SJonathan.Adams@Sun.COM 	sysdc_leave((sysdc_t *)buf);
105611173SJonathan.Adams@Sun.COM }
105711173SJonathan.Adams@Sun.COM 
105811173SJonathan.Adams@Sun.COM /*ARGSUSED*/
105911173SJonathan.Adams@Sun.COM static int
106011173SJonathan.Adams@Sun.COM sysdc_canexit(kthread_t *t, cred_t *reqpcredp)
106111173SJonathan.Adams@Sun.COM {
106211173SJonathan.Adams@Sun.COM 	/* Threads cannot exit SDC once joined, except in a body bag. */
106311173SJonathan.Adams@Sun.COM 	return (EPERM);
106411173SJonathan.Adams@Sun.COM }
106511173SJonathan.Adams@Sun.COM 
106611173SJonathan.Adams@Sun.COM static void
106711173SJonathan.Adams@Sun.COM sysdc_exit(kthread_t *t)
106811173SJonathan.Adams@Sun.COM {
106911173SJonathan.Adams@Sun.COM 	sysdc_t *sdc;
107011173SJonathan.Adams@Sun.COM 
107111173SJonathan.Adams@Sun.COM 	/* We're exiting, so we just rejoin the SYS class. */
107211173SJonathan.Adams@Sun.COM 	thread_lock(t);
107311173SJonathan.Adams@Sun.COM 	ASSERT(t->t_cid == sysdccid);
107411173SJonathan.Adams@Sun.COM 	sdc = t->t_cldata;
107511173SJonathan.Adams@Sun.COM 	t->t_cid = syscid;
107611173SJonathan.Adams@Sun.COM 	t->t_cldata = NULL;
107711173SJonathan.Adams@Sun.COM 	t->t_clfuncs = &(sclass[syscid].cl_funcs->thread);
107811173SJonathan.Adams@Sun.COM 	(void) thread_change_pri(t, maxclsyspri, 0);
107911173SJonathan.Adams@Sun.COM 	t->t_schedflag &= ~TS_RUNQMATCH;
108011173SJonathan.Adams@Sun.COM 	thread_unlock_nopreempt(t);
108111173SJonathan.Adams@Sun.COM 
108211173SJonathan.Adams@Sun.COM 	/* Unlink the sdc from everything. */
108311173SJonathan.Adams@Sun.COM 	sysdc_leave(sdc);
108411173SJonathan.Adams@Sun.COM }
108511173SJonathan.Adams@Sun.COM 
108611173SJonathan.Adams@Sun.COM /*ARGSUSED*/
108711173SJonathan.Adams@Sun.COM static int
108811173SJonathan.Adams@Sun.COM sysdc_fork(kthread_t *t, kthread_t *ct, void *bufp)
108911173SJonathan.Adams@Sun.COM {
109011173SJonathan.Adams@Sun.COM 	/*
109111173SJonathan.Adams@Sun.COM 	 * Threads cannot be created with SDC as their class; they must
109211173SJonathan.Adams@Sun.COM 	 * be created as SYS and then added with sysdc_thread_enter().
109311173SJonathan.Adams@Sun.COM 	 * Because of this restriction, sysdc_fork() should never be called.
109411173SJonathan.Adams@Sun.COM 	 */
109511173SJonathan.Adams@Sun.COM 	panic("sysdc cannot be forked");
109611173SJonathan.Adams@Sun.COM 
109711173SJonathan.Adams@Sun.COM 	return (ENOSYS);
109811173SJonathan.Adams@Sun.COM }
109911173SJonathan.Adams@Sun.COM 
110011173SJonathan.Adams@Sun.COM /*ARGSUSED*/
110111173SJonathan.Adams@Sun.COM static void
110211173SJonathan.Adams@Sun.COM sysdc_forkret(kthread_t *t, kthread_t *ct)
110311173SJonathan.Adams@Sun.COM {
110411173SJonathan.Adams@Sun.COM 	/* SDC threads are part of system processes, which never fork. */
110511173SJonathan.Adams@Sun.COM 	panic("sysdc cannot be forked");
110611173SJonathan.Adams@Sun.COM }
110711173SJonathan.Adams@Sun.COM 
110811173SJonathan.Adams@Sun.COM static pri_t
110911173SJonathan.Adams@Sun.COM sysdc_globpri(kthread_t *t)
111011173SJonathan.Adams@Sun.COM {
111111173SJonathan.Adams@Sun.COM 	return (t->t_epri);
111211173SJonathan.Adams@Sun.COM }
111311173SJonathan.Adams@Sun.COM 
111411173SJonathan.Adams@Sun.COM /*ARGSUSED*/
111511173SJonathan.Adams@Sun.COM static pri_t
111611173SJonathan.Adams@Sun.COM sysdc_no_swap(kthread_t *t, int flags)
111711173SJonathan.Adams@Sun.COM {
111811173SJonathan.Adams@Sun.COM 	/* SDC threads cannot be swapped. */
111911173SJonathan.Adams@Sun.COM 	return (-1);
112011173SJonathan.Adams@Sun.COM }
112111173SJonathan.Adams@Sun.COM 
112211173SJonathan.Adams@Sun.COM /*
112311173SJonathan.Adams@Sun.COM  * Get maximum and minimum priorities enjoyed by SDC threads.
112411173SJonathan.Adams@Sun.COM  */
112511173SJonathan.Adams@Sun.COM static int
112611173SJonathan.Adams@Sun.COM sysdc_getclpri(pcpri_t *pcprip)
112711173SJonathan.Adams@Sun.COM {
112811173SJonathan.Adams@Sun.COM 	pcprip->pc_clpmax = sysdc_maxpri;
112911173SJonathan.Adams@Sun.COM 	pcprip->pc_clpmin = sysdc_minpri;
113011173SJonathan.Adams@Sun.COM 	return (0);
113111173SJonathan.Adams@Sun.COM }
113211173SJonathan.Adams@Sun.COM 
113311173SJonathan.Adams@Sun.COM /*ARGSUSED*/
113411173SJonathan.Adams@Sun.COM static int
113511173SJonathan.Adams@Sun.COM sysdc_getclinfo(void *arg)
113611173SJonathan.Adams@Sun.COM {
113711173SJonathan.Adams@Sun.COM 	return (0);		/* no class-specific info */
113811173SJonathan.Adams@Sun.COM }
113911173SJonathan.Adams@Sun.COM 
114011173SJonathan.Adams@Sun.COM /*ARGSUSED*/
114111173SJonathan.Adams@Sun.COM static int
114211173SJonathan.Adams@Sun.COM sysdc_alloc(void **p, int flag)
114311173SJonathan.Adams@Sun.COM {
114411173SJonathan.Adams@Sun.COM 	sysdc_t *new;
114511173SJonathan.Adams@Sun.COM 
114611173SJonathan.Adams@Sun.COM 	*p = NULL;
114711173SJonathan.Adams@Sun.COM 	if ((new = kmem_zalloc(sizeof (*new), flag)) == NULL) {
114811173SJonathan.Adams@Sun.COM 		return (ENOMEM);
114911173SJonathan.Adams@Sun.COM 	}
115011173SJonathan.Adams@Sun.COM 	if ((new->sdc_pset = kmem_zalloc(sizeof (*new->sdc_pset), flag)) ==
115111173SJonathan.Adams@Sun.COM 	    NULL) {
115211173SJonathan.Adams@Sun.COM 		kmem_free(new, sizeof (*new));
115311173SJonathan.Adams@Sun.COM 		return (ENOMEM);
115411173SJonathan.Adams@Sun.COM 	}
115511173SJonathan.Adams@Sun.COM 	*p = new;
115611173SJonathan.Adams@Sun.COM 	return (0);
115711173SJonathan.Adams@Sun.COM }
115811173SJonathan.Adams@Sun.COM 
115911173SJonathan.Adams@Sun.COM static void
116011173SJonathan.Adams@Sun.COM sysdc_free(void *p)
116111173SJonathan.Adams@Sun.COM {
116211173SJonathan.Adams@Sun.COM 	sysdc_t *sdc = p;
116311173SJonathan.Adams@Sun.COM 
116411173SJonathan.Adams@Sun.COM 	if (sdc != NULL) {
116511173SJonathan.Adams@Sun.COM 		/*
116611173SJonathan.Adams@Sun.COM 		 * We must have failed CL_ENTERCLASS(), so our pset should be
116711173SJonathan.Adams@Sun.COM 		 * there and unused.
116811173SJonathan.Adams@Sun.COM 		 */
116911173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_pset != NULL);
117011173SJonathan.Adams@Sun.COM 		ASSERT(sdc->sdc_pset->sdp_cpupart == NULL);
117111173SJonathan.Adams@Sun.COM 		kmem_free(sdc->sdc_pset, sizeof (*sdc->sdc_pset));
117211173SJonathan.Adams@Sun.COM 		kmem_free(sdc, sizeof (*sdc));
117311173SJonathan.Adams@Sun.COM 	}
117411173SJonathan.Adams@Sun.COM }
117511173SJonathan.Adams@Sun.COM 
117611173SJonathan.Adams@Sun.COM static int sysdc_enosys();	/* Boy, ANSI-C's K&R compatibility is weird. */
117711173SJonathan.Adams@Sun.COM static int sysdc_einval();
117811173SJonathan.Adams@Sun.COM static void sysdc_nullsys();
117911173SJonathan.Adams@Sun.COM 
118011173SJonathan.Adams@Sun.COM static struct classfuncs sysdc_classfuncs = {
118111173SJonathan.Adams@Sun.COM 	/* messages to class manager */
118211173SJonathan.Adams@Sun.COM 	{
118311173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* admin */
118411173SJonathan.Adams@Sun.COM 		sysdc_getclinfo,
118511173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* parmsin */
118611173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* parmsout */
118711173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* vaparmsin */
118811173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* vaparmsout */
118911173SJonathan.Adams@Sun.COM 		sysdc_getclpri,
119011173SJonathan.Adams@Sun.COM 		sysdc_alloc,
119111173SJonathan.Adams@Sun.COM 		sysdc_free,
119211173SJonathan.Adams@Sun.COM 	},
119311173SJonathan.Adams@Sun.COM 	/* operations on threads */
119411173SJonathan.Adams@Sun.COM 	{
119511173SJonathan.Adams@Sun.COM 		sysdc_enterclass,
119611173SJonathan.Adams@Sun.COM 		sysdc_exitclass,
119711173SJonathan.Adams@Sun.COM 		sysdc_canexit,
119811173SJonathan.Adams@Sun.COM 		sysdc_fork,
119911173SJonathan.Adams@Sun.COM 		sysdc_forkret,
120011173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* parmsget */
120111173SJonathan.Adams@Sun.COM 		sysdc_enosys,	/* parmsset */
120211173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* stop */
120311173SJonathan.Adams@Sun.COM 		sysdc_exit,
120411173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* active */
120511173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* inactive */
120611173SJonathan.Adams@Sun.COM 		sysdc_no_swap,	/* swapin */
120711173SJonathan.Adams@Sun.COM 		sysdc_no_swap,	/* swapout */
120811173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* trapret */
120911173SJonathan.Adams@Sun.COM 		sysdc_preempt,
121011173SJonathan.Adams@Sun.COM 		sysdc_setrun,
121111173SJonathan.Adams@Sun.COM 		sysdc_sleep,
121211173SJonathan.Adams@Sun.COM 		sysdc_tick,
121311173SJonathan.Adams@Sun.COM 		sysdc_wakeup,
121411173SJonathan.Adams@Sun.COM 		sysdc_einval,	/* donice */
121511173SJonathan.Adams@Sun.COM 		sysdc_globpri,
121611173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* set_process_group */
121711173SJonathan.Adams@Sun.COM 		sysdc_nullsys,	/* yield */
121811173SJonathan.Adams@Sun.COM 		sysdc_einval,	/* doprio */
121911173SJonathan.Adams@Sun.COM 	}
122011173SJonathan.Adams@Sun.COM };
122111173SJonathan.Adams@Sun.COM 
122211173SJonathan.Adams@Sun.COM static int
122311173SJonathan.Adams@Sun.COM sysdc_enosys()
122411173SJonathan.Adams@Sun.COM {
122511173SJonathan.Adams@Sun.COM 	return (ENOSYS);
122611173SJonathan.Adams@Sun.COM }
122711173SJonathan.Adams@Sun.COM 
122811173SJonathan.Adams@Sun.COM static int
122911173SJonathan.Adams@Sun.COM sysdc_einval()
123011173SJonathan.Adams@Sun.COM {
123111173SJonathan.Adams@Sun.COM 	return (EINVAL);
123211173SJonathan.Adams@Sun.COM }
123311173SJonathan.Adams@Sun.COM 
123411173SJonathan.Adams@Sun.COM static void
123511173SJonathan.Adams@Sun.COM sysdc_nullsys()
123611173SJonathan.Adams@Sun.COM {
123711173SJonathan.Adams@Sun.COM }
123811173SJonathan.Adams@Sun.COM 
123911173SJonathan.Adams@Sun.COM /*ARGSUSED*/
124011173SJonathan.Adams@Sun.COM static pri_t
124111173SJonathan.Adams@Sun.COM sysdc_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp)
124211173SJonathan.Adams@Sun.COM {
124311173SJonathan.Adams@Sun.COM 	int idx;
124411173SJonathan.Adams@Sun.COM 
124511173SJonathan.Adams@Sun.COM 	list_create(&sysdc_psets, sizeof (sysdc_pset_t),
124611173SJonathan.Adams@Sun.COM 	    offsetof(sysdc_pset_t, sdp_node));
124711173SJonathan.Adams@Sun.COM 
124811173SJonathan.Adams@Sun.COM 	for (idx = 0; idx < SYSDC_NLISTS; idx++) {
124911173SJonathan.Adams@Sun.COM 		sysdc_active[idx].sdl_list = &sysdc_dummy;
125011173SJonathan.Adams@Sun.COM 	}
125111173SJonathan.Adams@Sun.COM 
125211173SJonathan.Adams@Sun.COM 	sysdc_initparam();
125311173SJonathan.Adams@Sun.COM 
125411173SJonathan.Adams@Sun.COM 	sysdccid = cid;
125511173SJonathan.Adams@Sun.COM 	*clfuncspp = &sysdc_classfuncs;
125611173SJonathan.Adams@Sun.COM 
125711173SJonathan.Adams@Sun.COM 	return ((pri_t)v.v_maxsyspri);
125811173SJonathan.Adams@Sun.COM }
125911173SJonathan.Adams@Sun.COM 
126011173SJonathan.Adams@Sun.COM static struct sclass csw = {
126111173SJonathan.Adams@Sun.COM 	"SDC",
126211173SJonathan.Adams@Sun.COM 	sysdc_init,
126311173SJonathan.Adams@Sun.COM 	0
126411173SJonathan.Adams@Sun.COM };
126511173SJonathan.Adams@Sun.COM 
126611173SJonathan.Adams@Sun.COM static struct modlsched modlsched = {
126711173SJonathan.Adams@Sun.COM 	&mod_schedops, "system duty cycle scheduling class", &csw
126811173SJonathan.Adams@Sun.COM };
126911173SJonathan.Adams@Sun.COM 
127011173SJonathan.Adams@Sun.COM static struct modlinkage modlinkage = {
127111173SJonathan.Adams@Sun.COM 	MODREV_1, (void *)&modlsched, NULL
127211173SJonathan.Adams@Sun.COM };
127311173SJonathan.Adams@Sun.COM 
127411173SJonathan.Adams@Sun.COM int
127511173SJonathan.Adams@Sun.COM _init()
127611173SJonathan.Adams@Sun.COM {
127711173SJonathan.Adams@Sun.COM 	return (mod_install(&modlinkage));
127811173SJonathan.Adams@Sun.COM }
127911173SJonathan.Adams@Sun.COM 
128011173SJonathan.Adams@Sun.COM int
128111173SJonathan.Adams@Sun.COM _fini()
128211173SJonathan.Adams@Sun.COM {
128311173SJonathan.Adams@Sun.COM 	return (EBUSY);		/* can't unload for now */
128411173SJonathan.Adams@Sun.COM }
128511173SJonathan.Adams@Sun.COM 
128611173SJonathan.Adams@Sun.COM int
128711173SJonathan.Adams@Sun.COM _info(struct modinfo *modinfop)
128811173SJonathan.Adams@Sun.COM {
128911173SJonathan.Adams@Sun.COM 	return (mod_info(&modlinkage, modinfop));
129011173SJonathan.Adams@Sun.COM }
129111173SJonathan.Adams@Sun.COM 
129211173SJonathan.Adams@Sun.COM /* --- consolidation-private interfaces --- */
129311173SJonathan.Adams@Sun.COM void
129411173SJonathan.Adams@Sun.COM sysdc_thread_enter(kthread_t *t, uint_t dc, uint_t flags)
129511173SJonathan.Adams@Sun.COM {
129611173SJonathan.Adams@Sun.COM 	void *buf = NULL;
129711173SJonathan.Adams@Sun.COM 	sysdc_params_t sdp;
129811173SJonathan.Adams@Sun.COM 
129911173SJonathan.Adams@Sun.COM 	SYSDC_INC_STAT(sysdc_thread_enter_enter);
130011173SJonathan.Adams@Sun.COM 
130111173SJonathan.Adams@Sun.COM 	ASSERT(sysdc_param_init);
130211173SJonathan.Adams@Sun.COM 	ASSERT(sysdccid >= 0);
130311173SJonathan.Adams@Sun.COM 
130411173SJonathan.Adams@Sun.COM 	ASSERT((flags & ~SYSDC_THREAD_BATCH) == 0);
130511173SJonathan.Adams@Sun.COM 
130611173SJonathan.Adams@Sun.COM 	sdp.sdp_minpri = sysdc_minpri;
130711173SJonathan.Adams@Sun.COM 	sdp.sdp_maxpri = sysdc_maxpri;
130811173SJonathan.Adams@Sun.COM 	sdp.sdp_DC = MAX(MIN(dc, sysdc_maxDC), sysdc_minDC);
130911173SJonathan.Adams@Sun.COM 
131011173SJonathan.Adams@Sun.COM 	VERIFY3U(CL_ALLOC(&buf, sysdccid, KM_SLEEP), ==, 0);
131111173SJonathan.Adams@Sun.COM 
131211173SJonathan.Adams@Sun.COM 	ASSERT(t->t_lwp != NULL);
131311173SJonathan.Adams@Sun.COM 	ASSERT(t->t_cid == syscid);
131411173SJonathan.Adams@Sun.COM 	ASSERT(t->t_cldata == NULL);
131511173SJonathan.Adams@Sun.COM 	VERIFY3U(CL_CANEXIT(t, NULL), ==, 0);
131611173SJonathan.Adams@Sun.COM 	VERIFY3U(CL_ENTERCLASS(t, sysdccid, &sdp, kcred, buf), ==, 0);
131711173SJonathan.Adams@Sun.COM 	CL_EXITCLASS(syscid, NULL);
131811173SJonathan.Adams@Sun.COM }
1319