xref: /titanic_51/usr/src/uts/common/disp/sysdc.c (revision fb09f5aad449c97fe309678f3f604982b563a96f)
135a5a358SJonathan Adams /*
235a5a358SJonathan Adams  * CDDL HEADER START
335a5a358SJonathan Adams  *
435a5a358SJonathan Adams  * The contents of this file are subject to the terms of the
535a5a358SJonathan Adams  * Common Development and Distribution License (the "License").
635a5a358SJonathan Adams  * You may not use this file except in compliance with the License.
735a5a358SJonathan Adams  *
835a5a358SJonathan Adams  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
935a5a358SJonathan Adams  * or http://www.opensolaris.org/os/licensing.
1035a5a358SJonathan Adams  * See the License for the specific language governing permissions
1135a5a358SJonathan Adams  * and limitations under the License.
1235a5a358SJonathan Adams  *
1335a5a358SJonathan Adams  * When distributing Covered Code, include this CDDL HEADER in each
1435a5a358SJonathan Adams  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1535a5a358SJonathan Adams  * If applicable, add the following below this CDDL HEADER, with the
1635a5a358SJonathan Adams  * fields enclosed by brackets "[]" replaced with your own identifying
1735a5a358SJonathan Adams  * information: Portions Copyright [yyyy] [name of copyright owner]
1835a5a358SJonathan Adams  *
1935a5a358SJonathan Adams  * CDDL HEADER END
2035a5a358SJonathan Adams  */
2135a5a358SJonathan Adams /*
22647ea195SJonathan Adams  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
23*fb09f5aaSMadhav Suresh  * Copyright (c) 2012 by Delphix. All rights reserved.
2435a5a358SJonathan Adams  */
2535a5a358SJonathan Adams 
2635a5a358SJonathan Adams /*
2735a5a358SJonathan Adams  * The System Duty Cycle (SDC) scheduling class
2835a5a358SJonathan Adams  * --------------------------------------------
2935a5a358SJonathan Adams  *
3035a5a358SJonathan Adams  * Background
3135a5a358SJonathan Adams  *
3235a5a358SJonathan Adams  * Kernel threads in Solaris have traditionally not been large consumers
3335a5a358SJonathan Adams  * of CPU time.  They typically wake up, perform a small amount of
3435a5a358SJonathan Adams  * work, then go back to sleep waiting for either a timeout or another
3535a5a358SJonathan Adams  * signal.  On the assumption that the small amount of work that they do
3635a5a358SJonathan Adams  * is important for the behavior of the whole system, these threads are
3735a5a358SJonathan Adams  * treated kindly by the dispatcher and the SYS scheduling class: they run
3835a5a358SJonathan Adams  * without preemption from anything other than real-time and interrupt
3935a5a358SJonathan Adams  * threads; when preempted, they are put at the front of the queue, so they
4035a5a358SJonathan Adams  * generally do not migrate between CPUs; and they are allowed to stay
4135a5a358SJonathan Adams  * running until they voluntarily give up the CPU.
4235a5a358SJonathan Adams  *
4335a5a358SJonathan Adams  * As Solaris has evolved, new workloads have emerged which require the
4435a5a358SJonathan Adams  * kernel to perform significant amounts of CPU-intensive work.  One
4535a5a358SJonathan Adams  * example of such a workload is ZFS's transaction group sync processing.
4635a5a358SJonathan Adams  * Each sync operation generates a large batch of I/Os, and each I/O
4735a5a358SJonathan Adams  * may need to be compressed and/or checksummed before it is written to
4835a5a358SJonathan Adams  * storage.  The taskq threads which perform the compression and checksums
4935a5a358SJonathan Adams  * will run nonstop as long as they have work to do; a large sync operation
5035a5a358SJonathan Adams  * on a compression-heavy dataset can keep them busy for seconds on end.
5135a5a358SJonathan Adams  * This causes human-time-scale dispatch latency bubbles for any other
5235a5a358SJonathan Adams  * threads which have the misfortune to share a CPU with the taskq threads.
5335a5a358SJonathan Adams  *
5435a5a358SJonathan Adams  * The SDC scheduling class is a solution to this problem.
5535a5a358SJonathan Adams  *
5635a5a358SJonathan Adams  *
5735a5a358SJonathan Adams  * Overview
5835a5a358SJonathan Adams  *
5935a5a358SJonathan Adams  * SDC is centered around the concept of a thread's duty cycle (DC):
6035a5a358SJonathan Adams  *
6135a5a358SJonathan Adams  *			      ONPROC time
6235a5a358SJonathan Adams  *	Duty Cycle =	----------------------
6335a5a358SJonathan Adams  *			ONPROC + Runnable time
6435a5a358SJonathan Adams  *
6535a5a358SJonathan Adams  * This is the ratio of the time that the thread spent running on a CPU
6635a5a358SJonathan Adams  * divided by the time it spent running or trying to run.  It is unaffected
6735a5a358SJonathan Adams  * by any time the thread spent sleeping, stopped, etc.
6835a5a358SJonathan Adams  *
6935a5a358SJonathan Adams  * A thread joining the SDC class specifies a "target" DC that it wants
7035a5a358SJonathan Adams  * to run at.  To implement this policy, the routine sysdc_update() scans
7135a5a358SJonathan Adams  * the list of active SDC threads every few ticks and uses each thread's
7235a5a358SJonathan Adams  * microstate data to compute the actual duty cycle that that thread
7335a5a358SJonathan Adams  * has experienced recently.  If the thread is under its target DC, its
7435a5a358SJonathan Adams  * priority is increased to the maximum available (sysdc_maxpri, which is
7535a5a358SJonathan Adams  * 99 by default).  If the thread is over its target DC, its priority is
7635a5a358SJonathan Adams  * reduced to the minimum available (sysdc_minpri, 0 by default).  This
7735a5a358SJonathan Adams  * is a fairly primitive approach, in that it doesn't use any of the
7835a5a358SJonathan Adams  * intermediate priorities, but it's not completely inappropriate.  Even
7935a5a358SJonathan Adams  * though threads in the SDC class might take a while to do their job, they
8035a5a358SJonathan Adams  * are by some definition important if they're running inside the kernel,
8135a5a358SJonathan Adams  * so it is reasonable that they should get to run at priority 99.
8235a5a358SJonathan Adams  *
8335a5a358SJonathan Adams  * If a thread is running when sysdc_update() calculates its actual duty
8435a5a358SJonathan Adams  * cycle, and there are other threads of equal or greater priority on its
8535a5a358SJonathan Adams  * CPU's dispatch queue, sysdc_update() preempts that thread.  The thread
8635a5a358SJonathan Adams  * acknowledges the preemption by calling sysdc_preempt(), which calls
8735a5a358SJonathan Adams  * setbackdq(), which gives other threads with the same priority a chance
8835a5a358SJonathan Adams  * to run.  This creates a de facto time quantum for threads in the SDC
8935a5a358SJonathan Adams  * scheduling class.
9035a5a358SJonathan Adams  *
9135a5a358SJonathan Adams  * An SDC thread which is assigned priority 0 can continue to run if
9235a5a358SJonathan Adams  * nothing else needs to use the CPU that it's running on.  Similarly, an
9335a5a358SJonathan Adams  * SDC thread at priority 99 might not get to run as much as it wants to
9435a5a358SJonathan Adams  * if there are other priority-99 or higher threads on its CPU.  These
9535a5a358SJonathan Adams  * situations would cause the thread to get ahead of or behind its target
9635a5a358SJonathan Adams  * DC; the longer the situations lasted, the further ahead or behind the
9735a5a358SJonathan Adams  * thread would get.  Rather than condemning a thread to a lifetime of
9835a5a358SJonathan Adams  * paying for its youthful indiscretions, SDC keeps "base" values for
9935a5a358SJonathan Adams  * ONPROC and Runnable times in each thread's sysdc data, and updates these
10035a5a358SJonathan Adams  * values periodically.  The duty cycle is then computed using the elapsed
10135a5a358SJonathan Adams  * amount of ONPROC and Runnable times since those base times.
10235a5a358SJonathan Adams  *
10335a5a358SJonathan Adams  * Since sysdc_update() scans SDC threads fairly frequently, it tries to
10435a5a358SJonathan Adams  * keep the list of "active" threads small by pruning out threads which
10535a5a358SJonathan Adams  * have been asleep for a brief time.  They are not pruned immediately upon
10635a5a358SJonathan Adams  * going to sleep, since some threads may bounce back and forth between
10735a5a358SJonathan Adams  * sleeping and being runnable.
10835a5a358SJonathan Adams  *
10935a5a358SJonathan Adams  *
11035a5a358SJonathan Adams  * Interfaces
11135a5a358SJonathan Adams  *
11235a5a358SJonathan Adams  * void sysdc_thread_enter(t, dc, flags)
11335a5a358SJonathan Adams  *
11435a5a358SJonathan Adams  *	Moves a kernel thread from the SYS scheduling class to the
11535a5a358SJonathan Adams  *	SDC class. t must have an associated LWP (created by calling
11635a5a358SJonathan Adams  *	lwp_kernel_create()).  The thread will have a target DC of dc.
11735a5a358SJonathan Adams  *	Flags should be either 0 or SYSDC_THREAD_BATCH.  If
118aab2fe41SJonathan Adams  *	SYSDC_THREAD_BATCH is specified, the thread is expected to be
119aab2fe41SJonathan Adams  *	doing large amounts of processing.
12035a5a358SJonathan Adams  *
12135a5a358SJonathan Adams  *
12235a5a358SJonathan Adams  * Complications
12335a5a358SJonathan Adams  *
12435a5a358SJonathan Adams  * - Run queue balancing
12535a5a358SJonathan Adams  *
12635a5a358SJonathan Adams  *	The Solaris dispatcher is biased towards letting a thread run
12735a5a358SJonathan Adams  *	on the same CPU which it last ran on, if no more than 3 ticks
12835a5a358SJonathan Adams  *	(i.e. rechoose_interval) have passed since the thread last ran.
12935a5a358SJonathan Adams  *	This helps to preserve cache warmth.  On the other hand, it also
13035a5a358SJonathan Adams  *	tries to keep the per-CPU run queues fairly balanced; if the CPU
13135a5a358SJonathan Adams  *	chosen for a runnable thread has a run queue which is three or
13235a5a358SJonathan Adams  *	more threads longer than a neighboring CPU's queue, the runnable
13335a5a358SJonathan Adams  *	thread is dispatched onto the neighboring CPU instead.
13435a5a358SJonathan Adams  *
13535a5a358SJonathan Adams  *	These policies work well for some workloads, but not for many SDC
13635a5a358SJonathan Adams  *	threads.  The taskq client of SDC, for example, has many discrete
13735a5a358SJonathan Adams  *	units of work to do.  The work units are largely independent, so
13835a5a358SJonathan Adams  *	cache warmth is not an important consideration.  It is important
13935a5a358SJonathan Adams  *	that the threads fan out quickly to different CPUs, since the
14035a5a358SJonathan Adams  *	amount of work these threads have to do (a few seconds worth at a
14135a5a358SJonathan Adams  *	time) doesn't leave much time to correct thread placement errors
14235a5a358SJonathan Adams  *	(i.e. two SDC threads being dispatched to the same CPU).
14335a5a358SJonathan Adams  *
14435a5a358SJonathan Adams  *	To fix this, SDC uses the TS_RUNQMATCH flag introduced for FSS.
14535a5a358SJonathan Adams  *	This tells the dispatcher to keep neighboring run queues' lengths
14635a5a358SJonathan Adams  *	more evenly matched, which allows SDC threads to migrate more
14735a5a358SJonathan Adams  *	easily.
14835a5a358SJonathan Adams  *
14935a5a358SJonathan Adams  * - LWPs and system processes
15035a5a358SJonathan Adams  *
15135a5a358SJonathan Adams  *	SDC can only be used for kernel threads.  Since SDC uses microstate
15235a5a358SJonathan Adams  *	accounting data to compute each thread's actual duty cycle, all
15335a5a358SJonathan Adams  *	threads entering the SDC class must have associated LWPs (which
15435a5a358SJonathan Adams  *	store the microstate data).  This means that the threads have to
15535a5a358SJonathan Adams  *	be associated with an SSYS process, i.e. one created by newproc().
15635a5a358SJonathan Adams  *	If the microstate accounting information is ever moved into the
15735a5a358SJonathan Adams  *	kthread_t, this restriction could be lifted.
15835a5a358SJonathan Adams  *
15935a5a358SJonathan Adams  * - Dealing with oversubscription
16035a5a358SJonathan Adams  *
16135a5a358SJonathan Adams  *	Since SDC duty cycles are per-thread, it is possible that the
16235a5a358SJonathan Adams  *	aggregate requested duty cycle of all SDC threads in a processor
16335a5a358SJonathan Adams  *	set could be greater than the total CPU time available in that set.
16435a5a358SJonathan Adams  *	The FSS scheduling class has an analogous situation, which it deals
16535a5a358SJonathan Adams  *	with by reducing each thread's allotted CPU time proportionally.
16635a5a358SJonathan Adams  *	Since SDC doesn't need to be as precise as FSS, it uses a simpler
16735a5a358SJonathan Adams  *	solution to the oversubscription problem.
16835a5a358SJonathan Adams  *
16935a5a358SJonathan Adams  *	sysdc_update() accumulates the amount of time that max-priority SDC
17035a5a358SJonathan Adams  *	threads have spent on-CPU in each processor set, and uses that sum
17135a5a358SJonathan Adams  *	to create an implied duty cycle for that processor set:
17235a5a358SJonathan Adams  *
17335a5a358SJonathan Adams  *				accumulated CPU time
17435a5a358SJonathan Adams  *	   pset DC =	-----------------------------------
17535a5a358SJonathan Adams  *			 (# CPUs) * time since last update
17635a5a358SJonathan Adams  *
17735a5a358SJonathan Adams  *	If this implied duty cycle is above a maximum pset duty cycle (90%
17835a5a358SJonathan Adams  *	by default), sysdc_update() sets the priority of all SDC threads
17935a5a358SJonathan Adams  *	in that processor set to sysdc_minpri for a "break" period.  After
18035a5a358SJonathan Adams  *	the break period, it waits for a "nobreak" period before trying to
18135a5a358SJonathan Adams  *	enforce the pset duty cycle limit again.
18235a5a358SJonathan Adams  *
18335a5a358SJonathan Adams  * - Processor sets
18435a5a358SJonathan Adams  *
18535a5a358SJonathan Adams  *	As the above implies, SDC is processor set aware, but it does not
18635a5a358SJonathan Adams  *	currently allow threads to change processor sets while in the SDC
18735a5a358SJonathan Adams  *	class.  Instead, those threads must join the desired processor set
18835a5a358SJonathan Adams  *	before entering SDC. [1]
18935a5a358SJonathan Adams  *
19035a5a358SJonathan Adams  * - Batch threads
19135a5a358SJonathan Adams  *
19235a5a358SJonathan Adams  *	A thread joining the SDC class can specify the SDC_THREAD_BATCH
193aab2fe41SJonathan Adams  *	flag.  This flag currently has no effect, but marks threads which
194aab2fe41SJonathan Adams  *	do bulk processing.
19535a5a358SJonathan Adams  *
19635a5a358SJonathan Adams  * - t_kpri_req
19735a5a358SJonathan Adams  *
19835a5a358SJonathan Adams  *	The TS and FSS scheduling classes pay attention to t_kpri_req,
19935a5a358SJonathan Adams  *	which provides a simple form of priority inheritance for
20035a5a358SJonathan Adams  *	synchronization primitives (such as rwlocks held as READER) which
20135a5a358SJonathan Adams  *	cannot be traced to a unique thread.  The SDC class does not honor
20235a5a358SJonathan Adams  *	t_kpri_req, for a few reasons:
20335a5a358SJonathan Adams  *
20435a5a358SJonathan Adams  *	1.  t_kpri_req is notoriously inaccurate.  A measure of its
20535a5a358SJonathan Adams  *	    inaccuracy is that it needs to be cleared every time a thread
20635a5a358SJonathan Adams  *	    returns to user mode, because it is frequently non-zero at that
20735a5a358SJonathan Adams  *	    point.  This can happen because "ownership" of synchronization
20835a5a358SJonathan Adams  *	    primitives that use t_kpri_req can be silently handed off,
20935a5a358SJonathan Adams  *	    leaving no opportunity to will the t_kpri_req inheritance.
21035a5a358SJonathan Adams  *
21135a5a358SJonathan Adams  *	2.  Unlike in TS and FSS, threads in SDC *will* eventually run at
21235a5a358SJonathan Adams  *	    kernel priority.  This means that even if an SDC thread
21335a5a358SJonathan Adams  *	    is holding a synchronization primitive and running at low
21435a5a358SJonathan Adams  *	    priority, its priority will eventually be raised above 60,
21535a5a358SJonathan Adams  *	    allowing it to drive on and release the resource.
21635a5a358SJonathan Adams  *
21735a5a358SJonathan Adams  *	3.  The first consumer of SDC uses the taskq subsystem, which holds
21835a5a358SJonathan Adams  *	    a reader lock for the duration of the task's execution.  This
21935a5a358SJonathan Adams  *	    would mean that SDC threads would never drop below kernel
22035a5a358SJonathan Adams  *	    priority in practice, which defeats one of the purposes of SDC.
22135a5a358SJonathan Adams  *
22235a5a358SJonathan Adams  * - Why not FSS?
22335a5a358SJonathan Adams  *
22435a5a358SJonathan Adams  *	It might seem that the existing FSS scheduling class could solve
22535a5a358SJonathan Adams  *	the problems that SDC is attempting to solve.  FSS's more precise
22635a5a358SJonathan Adams  *	solution to the oversubscription problem would hardly cause
22735a5a358SJonathan Adams  *	trouble, as long as it performed well.  SDC is implemented as
22835a5a358SJonathan Adams  *	a separate scheduling class for two main reasons: the initial
22935a5a358SJonathan Adams  *	consumer of SDC does not map well onto the "project" abstraction
23035a5a358SJonathan Adams  *	that is central to FSS, and FSS does not expect to run at kernel
23135a5a358SJonathan Adams  *	priorities.
23235a5a358SJonathan Adams  *
23335a5a358SJonathan Adams  *
23435a5a358SJonathan Adams  * Tunables
23535a5a358SJonathan Adams  *
23635a5a358SJonathan Adams  * - sysdc_update_interval_msec:  Number of milliseconds between
23735a5a358SJonathan Adams  *	consecutive thread priority updates.
23835a5a358SJonathan Adams  *
23935a5a358SJonathan Adams  * - sysdc_reset_interval_msec:  Number of milliseconds between
24035a5a358SJonathan Adams  *	consecutive resets of a thread's base ONPROC and Runnable
24135a5a358SJonathan Adams  *	times.
24235a5a358SJonathan Adams  *
24335a5a358SJonathan Adams  * - sysdc_prune_interval_msec:  Number of milliseconds of sleeping
24435a5a358SJonathan Adams  *	before a thread is pruned from the active list.
24535a5a358SJonathan Adams  *
24635a5a358SJonathan Adams  * - sysdc_max_pset_DC:  Allowable percentage of a processor set's
24735a5a358SJonathan Adams  *	CPU time which SDC can give to its high-priority threads.
24835a5a358SJonathan Adams  *
24935a5a358SJonathan Adams  * - sysdc_break_msec:  Number of milliseconds of "break" taken when
25035a5a358SJonathan Adams  *	sysdc_max_pset_DC is exceeded.
25135a5a358SJonathan Adams  *
25235a5a358SJonathan Adams  *
25335a5a358SJonathan Adams  * Future work (in SDC and related subsystems)
25435a5a358SJonathan Adams  *
25535a5a358SJonathan Adams  * - Per-thread rechoose interval (0 for SDC)
25635a5a358SJonathan Adams  *
25735a5a358SJonathan Adams  *	Allow each thread to specify its own rechoose interval.  SDC
25835a5a358SJonathan Adams  *	threads would specify an interval of zero, which would rechoose
25935a5a358SJonathan Adams  *	the CPU with the lowest priority once per update.
26035a5a358SJonathan Adams  *
26135a5a358SJonathan Adams  * - Allow threads to change processor sets after joining the SDC class
26235a5a358SJonathan Adams  *
26335a5a358SJonathan Adams  * - Thread groups and per-group DC
26435a5a358SJonathan Adams  *
26535a5a358SJonathan Adams  *	It might be nice to be able to specify a duty cycle which applies
26635a5a358SJonathan Adams  *	to a group of threads in aggregate.
26735a5a358SJonathan Adams  *
26835a5a358SJonathan Adams  * - Per-group DC callback to allow dynamic DC tuning
26935a5a358SJonathan Adams  *
27035a5a358SJonathan Adams  *	Currently, DCs are assigned when the thread joins SDC.  Some
27135a5a358SJonathan Adams  *	workloads could benefit from being able to tune their DC using
27235a5a358SJonathan Adams  *	subsystem-specific knowledge about the workload.
27335a5a358SJonathan Adams  *
27435a5a358SJonathan Adams  * - Finer-grained priority updates
27535a5a358SJonathan Adams  *
27635a5a358SJonathan Adams  * - More nuanced management of oversubscription
27735a5a358SJonathan Adams  *
27835a5a358SJonathan Adams  * - Moving other CPU-intensive threads into SDC
27935a5a358SJonathan Adams  *
28035a5a358SJonathan Adams  * - Move msacct data into kthread_t
28135a5a358SJonathan Adams  *
28235a5a358SJonathan Adams  *	This would allow kernel threads without LWPs to join SDC.
28335a5a358SJonathan Adams  *
28435a5a358SJonathan Adams  *
28535a5a358SJonathan Adams  * Footnotes
28635a5a358SJonathan Adams  *
28735a5a358SJonathan Adams  * [1] The details of doing so are left as an exercise for the reader.
28835a5a358SJonathan Adams  */
28935a5a358SJonathan Adams 
29035a5a358SJonathan Adams #include <sys/types.h>
29135a5a358SJonathan Adams #include <sys/sysdc.h>
29235a5a358SJonathan Adams #include <sys/sysdc_impl.h>
29335a5a358SJonathan Adams 
29435a5a358SJonathan Adams #include <sys/class.h>
29535a5a358SJonathan Adams #include <sys/cmn_err.h>
29635a5a358SJonathan Adams #include <sys/cpuvar.h>
29735a5a358SJonathan Adams #include <sys/cpupart.h>
29835a5a358SJonathan Adams #include <sys/debug.h>
29935a5a358SJonathan Adams #include <sys/disp.h>
30035a5a358SJonathan Adams #include <sys/errno.h>
30135a5a358SJonathan Adams #include <sys/inline.h>
30235a5a358SJonathan Adams #include <sys/kmem.h>
30335a5a358SJonathan Adams #include <sys/modctl.h>
30435a5a358SJonathan Adams #include <sys/schedctl.h>
30535a5a358SJonathan Adams #include <sys/sdt.h>
30635a5a358SJonathan Adams #include <sys/sunddi.h>
30735a5a358SJonathan Adams #include <sys/sysmacros.h>
30835a5a358SJonathan Adams #include <sys/systm.h>
30935a5a358SJonathan Adams #include <sys/var.h>
31035a5a358SJonathan Adams 
31135a5a358SJonathan Adams /*
31235a5a358SJonathan Adams  * Tunables - loaded into the internal state at module load time
31335a5a358SJonathan Adams  */
31435a5a358SJonathan Adams uint_t		sysdc_update_interval_msec = 20;
31535a5a358SJonathan Adams uint_t		sysdc_reset_interval_msec = 400;
31635a5a358SJonathan Adams uint_t		sysdc_prune_interval_msec = 100;
31735a5a358SJonathan Adams uint_t		sysdc_max_pset_DC = 90;
31835a5a358SJonathan Adams uint_t		sysdc_break_msec = 80;
31935a5a358SJonathan Adams 
32035a5a358SJonathan Adams /*
32135a5a358SJonathan Adams  * Internal state - constants set up by sysdc_initparam()
32235a5a358SJonathan Adams  */
32335a5a358SJonathan Adams static clock_t	sysdc_update_ticks;	/* ticks between updates */
32435a5a358SJonathan Adams static uint_t	sysdc_prune_updates;	/* updates asleep before pruning */
32535a5a358SJonathan Adams static uint_t	sysdc_reset_updates;	/* # of updates before reset */
32635a5a358SJonathan Adams static uint_t	sysdc_break_updates;	/* updates to break */
32735a5a358SJonathan Adams static uint_t	sysdc_nobreak_updates;	/* updates to not check */
32835a5a358SJonathan Adams static uint_t	sysdc_minDC;		/* minimum allowed DC */
32935a5a358SJonathan Adams static uint_t	sysdc_maxDC;		/* maximum allowed DC */
33035a5a358SJonathan Adams static pri_t	sysdc_minpri;		/* minimum allowed priority */
33135a5a358SJonathan Adams static pri_t	sysdc_maxpri;		/* maximum allowed priority */
33235a5a358SJonathan Adams 
33335a5a358SJonathan Adams /*
33435a5a358SJonathan Adams  * Internal state
33535a5a358SJonathan Adams  */
33635a5a358SJonathan Adams static kmutex_t	sysdc_pset_lock;	/* lock protecting pset data */
33735a5a358SJonathan Adams static list_t	sysdc_psets;		/* list of psets with SDC threads */
33835a5a358SJonathan Adams static uint_t	sysdc_param_init;	/* sysdc_initparam() has been called */
33935a5a358SJonathan Adams static uint_t	sysdc_update_timeout_started; /* update timeout is active */
34035a5a358SJonathan Adams static hrtime_t	sysdc_last_update;	/* time of last sysdc_update() */
34135a5a358SJonathan Adams static sysdc_t	sysdc_dummy;		/* used to terminate active lists */
34235a5a358SJonathan Adams 
34335a5a358SJonathan Adams /*
34435a5a358SJonathan Adams  * Internal state - active hash table
34535a5a358SJonathan Adams  */
34635a5a358SJonathan Adams #define	SYSDC_NLISTS	8
34735a5a358SJonathan Adams #define	SYSDC_HASH(sdc)	(((uintptr_t)(sdc) >> 6) & (SYSDC_NLISTS - 1))
34835a5a358SJonathan Adams static sysdc_list_t	sysdc_active[SYSDC_NLISTS];
34935a5a358SJonathan Adams #define	SYSDC_LIST(sdc)		(&sysdc_active[SYSDC_HASH(sdc)])
35035a5a358SJonathan Adams 
35135a5a358SJonathan Adams #ifdef DEBUG
35235a5a358SJonathan Adams static struct {
35335a5a358SJonathan Adams 	uint64_t	sysdc_update_times_asleep;
35435a5a358SJonathan Adams 	uint64_t	sysdc_update_times_base_ran_backwards;
35535a5a358SJonathan Adams 	uint64_t	sysdc_update_times_already_done;
35635a5a358SJonathan Adams 	uint64_t	sysdc_update_times_cur_ran_backwards;
35735a5a358SJonathan Adams 	uint64_t	sysdc_compute_pri_breaking;
35835a5a358SJonathan Adams 	uint64_t	sysdc_activate_enter;
35935a5a358SJonathan Adams 	uint64_t	sysdc_update_enter;
36035a5a358SJonathan Adams 	uint64_t	sysdc_update_exited;
36135a5a358SJonathan Adams 	uint64_t	sysdc_update_not_sdc;
36235a5a358SJonathan Adams 	uint64_t	sysdc_update_idle;
36335a5a358SJonathan Adams 	uint64_t	sysdc_update_take_break;
36435a5a358SJonathan Adams 	uint64_t	sysdc_update_no_psets;
36535a5a358SJonathan Adams 	uint64_t	sysdc_tick_not_sdc;
36635a5a358SJonathan Adams 	uint64_t	sysdc_tick_quantum_expired;
36735a5a358SJonathan Adams 	uint64_t	sysdc_thread_enter_enter;
36835a5a358SJonathan Adams } sysdc_stats;
36935a5a358SJonathan Adams 
37035a5a358SJonathan Adams #define	SYSDC_INC_STAT(x)	(sysdc_stats.x++)
37135a5a358SJonathan Adams #else
37235a5a358SJonathan Adams #define	SYSDC_INC_STAT(x)	((void)0)
37335a5a358SJonathan Adams #endif
37435a5a358SJonathan Adams 
37535a5a358SJonathan Adams /* macros are UPPER CASE */
37635a5a358SJonathan Adams #define	HOWMANY(a, b)	howmany((a), (b))
37735a5a358SJonathan Adams #define	MSECTOTICKS(a)	HOWMANY((a) * 1000, usec_per_tick)
37835a5a358SJonathan Adams 
37935a5a358SJonathan Adams static void
38035a5a358SJonathan Adams sysdc_initparam(void)
38135a5a358SJonathan Adams {
38235a5a358SJonathan Adams 	uint_t sysdc_break_ticks;
38335a5a358SJonathan Adams 
38435a5a358SJonathan Adams 	/* update / prune intervals */
38535a5a358SJonathan Adams 	sysdc_update_ticks = MSECTOTICKS(sysdc_update_interval_msec);
38635a5a358SJonathan Adams 
38735a5a358SJonathan Adams 	sysdc_prune_updates = HOWMANY(sysdc_prune_interval_msec,
38835a5a358SJonathan Adams 	    sysdc_update_interval_msec);
38935a5a358SJonathan Adams 	sysdc_reset_updates = HOWMANY(sysdc_reset_interval_msec,
39035a5a358SJonathan Adams 	    sysdc_update_interval_msec);
39135a5a358SJonathan Adams 
39235a5a358SJonathan Adams 	/* We must get at least a little time on CPU. */
39335a5a358SJonathan Adams 	sysdc_minDC = 1;
39435a5a358SJonathan Adams 	sysdc_maxDC = SYSDC_DC_MAX;
39535a5a358SJonathan Adams 	sysdc_minpri = 0;
39635a5a358SJonathan Adams 	sysdc_maxpri = maxclsyspri;
39735a5a358SJonathan Adams 
39835a5a358SJonathan Adams 	/* break parameters */
39935a5a358SJonathan Adams 	if (sysdc_max_pset_DC > SYSDC_DC_MAX) {
40035a5a358SJonathan Adams 		sysdc_max_pset_DC = SYSDC_DC_MAX;
40135a5a358SJonathan Adams 	}
40235a5a358SJonathan Adams 	sysdc_break_ticks = MSECTOTICKS(sysdc_break_msec);
40335a5a358SJonathan Adams 	sysdc_break_updates = HOWMANY(sysdc_break_ticks, sysdc_update_ticks);
40435a5a358SJonathan Adams 
40535a5a358SJonathan Adams 	/*
40635a5a358SJonathan Adams 	 * We want:
40735a5a358SJonathan Adams 	 *
40835a5a358SJonathan Adams 	 *	sysdc_max_pset_DC = (nobreak / (break + nobreak))
40935a5a358SJonathan Adams 	 *
41035a5a358SJonathan Adams 	 *	==>	  nobreak = sysdc_max_pset_DC * (break + nobreak)
41135a5a358SJonathan Adams 	 *
41235a5a358SJonathan Adams 	 *			    sysdc_max_pset_DC * break
41335a5a358SJonathan Adams 	 *	==>	  nobreak = -------------------------
41435a5a358SJonathan Adams 	 *			    1 - sysdc_max_pset_DC
41535a5a358SJonathan Adams 	 */
41635a5a358SJonathan Adams 	sysdc_nobreak_updates =
41735a5a358SJonathan Adams 	    HOWMANY((uint64_t)sysdc_break_updates * sysdc_max_pset_DC,
41835a5a358SJonathan Adams 	    (SYSDC_DC_MAX - sysdc_max_pset_DC));
41935a5a358SJonathan Adams 
42035a5a358SJonathan Adams 	sysdc_param_init = 1;
42135a5a358SJonathan Adams }
42235a5a358SJonathan Adams 
42335a5a358SJonathan Adams #undef HOWMANY
42435a5a358SJonathan Adams #undef MSECTOTICKS
42535a5a358SJonathan Adams 
42635a5a358SJonathan Adams #define	SDC_UPDATE_INITIAL	0x1	/* for the initial update */
42735a5a358SJonathan Adams #define	SDC_UPDATE_TIMEOUT	0x2	/* from sysdc_update() */
42835a5a358SJonathan Adams #define	SDC_UPDATE_TICK		0x4	/* from sysdc_tick(), on expiry */
42935a5a358SJonathan Adams 
43035a5a358SJonathan Adams /*
43135a5a358SJonathan Adams  * Updates the recorded times in the sdc, and returns the elapsed ONPROC
43235a5a358SJonathan Adams  * and Runnable times since the last reset.
43335a5a358SJonathan Adams  *
43435a5a358SJonathan Adams  * newO is the thread's actual ONPROC time; it's used during sysdc_update()
43535a5a358SJonathan Adams  * to track processor set usage.
43635a5a358SJonathan Adams  */
43735a5a358SJonathan Adams static void
43835a5a358SJonathan Adams sysdc_update_times(sysdc_t *sdc, uint_t flags,
43935a5a358SJonathan Adams     hrtime_t *O, hrtime_t *R, hrtime_t *newO)
44035a5a358SJonathan Adams {
44135a5a358SJonathan Adams 	kthread_t *const t = sdc->sdc_thread;
44235a5a358SJonathan Adams 	const uint_t	initial = (flags & SDC_UPDATE_INITIAL);
44335a5a358SJonathan Adams 	const uint_t	update = (flags & SDC_UPDATE_TIMEOUT);
44435a5a358SJonathan Adams 	const clock_t	now = ddi_get_lbolt();
44535a5a358SJonathan Adams 	uint_t		do_reset;
44635a5a358SJonathan Adams 
44735a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));
44835a5a358SJonathan Adams 
44935a5a358SJonathan Adams 	*O = *R = 0;
45035a5a358SJonathan Adams 
45135a5a358SJonathan Adams 	/* If we've been sleeping, we know we haven't had any ONPROC time. */
45235a5a358SJonathan Adams 	if (sdc->sdc_sleep_updates != 0 &&
45335a5a358SJonathan Adams 	    sdc->sdc_sleep_updates != sdc->sdc_nupdates) {
45435a5a358SJonathan Adams 		*newO = sdc->sdc_last_base_O;
45535a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_update_times_asleep);
45635a5a358SJonathan Adams 		return;
45735a5a358SJonathan Adams 	}
45835a5a358SJonathan Adams 
45935a5a358SJonathan Adams 	/*
46035a5a358SJonathan Adams 	 * If this is our first update, or we've hit the reset point,
46135a5a358SJonathan Adams 	 * we need to reset our base_{O,R}.  Once we've updated them, we
46235a5a358SJonathan Adams 	 * report O and R for the entire prior interval.
46335a5a358SJonathan Adams 	 */
46435a5a358SJonathan Adams 	do_reset = initial;
46535a5a358SJonathan Adams 	if (update) {
46635a5a358SJonathan Adams 		++sdc->sdc_nupdates;
46735a5a358SJonathan Adams 		if ((sdc->sdc_nupdates % sysdc_reset_updates) == 0)
46835a5a358SJonathan Adams 			do_reset = 1;
46935a5a358SJonathan Adams 	}
47035a5a358SJonathan Adams 	if (do_reset) {
47135a5a358SJonathan Adams 		hrtime_t baseO, baseR;
47235a5a358SJonathan Adams 		if (initial) {
47335a5a358SJonathan Adams 			/*
47435a5a358SJonathan Adams 			 * Start off our cycle count somewhere in the middle,
47535a5a358SJonathan Adams 			 * to keep the resets from all happening at once.
47635a5a358SJonathan Adams 			 *
47735a5a358SJonathan Adams 			 * 4999 is a handy prime much larger than
47835a5a358SJonathan Adams 			 * sysdc_reset_updates, so that we don't run into
47935a5a358SJonathan Adams 			 * trouble if the resolution is a multiple of
48035a5a358SJonathan Adams 			 * sysdc_reset_updates.
48135a5a358SJonathan Adams 			 */
48235a5a358SJonathan Adams 			sdc->sdc_nupdates = (uint_t)((gethrtime() % 4999) %
48335a5a358SJonathan Adams 			    sysdc_reset_updates);
48435a5a358SJonathan Adams 			baseO = baseR = 0;
48535a5a358SJonathan Adams 		} else {
48635a5a358SJonathan Adams 			baseO = sdc->sdc_base_O;
48735a5a358SJonathan Adams 			baseR = sdc->sdc_base_R;
48835a5a358SJonathan Adams 		}
48935a5a358SJonathan Adams 
49035a5a358SJonathan Adams 		mstate_systhread_times(t, &sdc->sdc_base_O, &sdc->sdc_base_R);
49135a5a358SJonathan Adams 		*newO = sdc->sdc_base_O;
49235a5a358SJonathan Adams 
49335a5a358SJonathan Adams 		sdc->sdc_reset = now;
49435a5a358SJonathan Adams 		sdc->sdc_pri_check = -1; /* force mismatch below */
49535a5a358SJonathan Adams 
49635a5a358SJonathan Adams 		/*
49735a5a358SJonathan Adams 		 * See below for rationale.
49835a5a358SJonathan Adams 		 */
49935a5a358SJonathan Adams 		if (baseO > sdc->sdc_base_O || baseR > sdc->sdc_base_R) {
50035a5a358SJonathan Adams 			SYSDC_INC_STAT(sysdc_update_times_base_ran_backwards);
50135a5a358SJonathan Adams 			baseO = sdc->sdc_base_O;
50235a5a358SJonathan Adams 			baseR = sdc->sdc_base_R;
50335a5a358SJonathan Adams 		}
50435a5a358SJonathan Adams 
50535a5a358SJonathan Adams 		/* compute based on the entire interval */
50635a5a358SJonathan Adams 		*O = (sdc->sdc_base_O - baseO);
50735a5a358SJonathan Adams 		*R = (sdc->sdc_base_R - baseR);
50835a5a358SJonathan Adams 		return;
50935a5a358SJonathan Adams 	}
51035a5a358SJonathan Adams 
51135a5a358SJonathan Adams 	/*
51235a5a358SJonathan Adams 	 * If we're called from sysdc_update(), we *must* return a value
51335a5a358SJonathan Adams 	 * for newO, so we always call mstate_systhread_times().
51435a5a358SJonathan Adams 	 *
51535a5a358SJonathan Adams 	 * Otherwise, if we've already done a pri check this tick,
51635a5a358SJonathan Adams 	 * we can skip it.
51735a5a358SJonathan Adams 	 */
51835a5a358SJonathan Adams 	if (!update && sdc->sdc_pri_check == now) {
51935a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_update_times_already_done);
52035a5a358SJonathan Adams 		return;
52135a5a358SJonathan Adams 	}
52235a5a358SJonathan Adams 
52335a5a358SJonathan Adams 	/* Get the current times from the thread */
52435a5a358SJonathan Adams 	sdc->sdc_pri_check = now;
52535a5a358SJonathan Adams 	mstate_systhread_times(t, &sdc->sdc_cur_O, &sdc->sdc_cur_R);
52635a5a358SJonathan Adams 	*newO = sdc->sdc_cur_O;
52735a5a358SJonathan Adams 
52835a5a358SJonathan Adams 	/*
52935a5a358SJonathan Adams 	 * The updating of microstate accounting is not done under a
53035a5a358SJonathan Adams 	 * consistent set of locks, particularly the t_waitrq field.  This
53135a5a358SJonathan Adams 	 * can lead to narrow windows in which we account for time in the
53235a5a358SJonathan Adams 	 * wrong bucket, which on the next read will be accounted for
53335a5a358SJonathan Adams 	 * correctly.
53435a5a358SJonathan Adams 	 *
53535a5a358SJonathan Adams 	 * If our sdc_base_* fields were affected by one of these blips, we
53635a5a358SJonathan Adams 	 * throw away the old data, and pretend this tick didn't happen.
53735a5a358SJonathan Adams 	 */
53835a5a358SJonathan Adams 	if (sdc->sdc_cur_O < sdc->sdc_base_O ||
53935a5a358SJonathan Adams 	    sdc->sdc_cur_R < sdc->sdc_base_R) {
54035a5a358SJonathan Adams 
54135a5a358SJonathan Adams 		sdc->sdc_base_O = sdc->sdc_cur_O;
54235a5a358SJonathan Adams 		sdc->sdc_base_R = sdc->sdc_cur_R;
54335a5a358SJonathan Adams 
54435a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_update_times_cur_ran_backwards);
54535a5a358SJonathan Adams 		return;
54635a5a358SJonathan Adams 	}
54735a5a358SJonathan Adams 
54835a5a358SJonathan Adams 	*O = sdc->sdc_cur_O - sdc->sdc_base_O;
54935a5a358SJonathan Adams 	*R = sdc->sdc_cur_R - sdc->sdc_base_R;
55035a5a358SJonathan Adams }
55135a5a358SJonathan Adams 
55235a5a358SJonathan Adams /*
55335a5a358SJonathan Adams  * sysdc_compute_pri()
55435a5a358SJonathan Adams  *
55535a5a358SJonathan Adams  *	Recomputes the priority of the thread, leaving the result in
55635a5a358SJonathan Adams  *	sdc->sdc_epri.  Returns 1 if a priority update should occur
55735a5a358SJonathan Adams  *	(which will also trigger a cpu_surrender()), otherwise
55835a5a358SJonathan Adams  *	returns 0.
55935a5a358SJonathan Adams  */
56035a5a358SJonathan Adams static uint_t
56135a5a358SJonathan Adams sysdc_compute_pri(sysdc_t *sdc, uint_t flags)
56235a5a358SJonathan Adams {
56335a5a358SJonathan Adams 	kthread_t *const t = sdc->sdc_thread;
56435a5a358SJonathan Adams 	const uint_t	update = (flags & SDC_UPDATE_TIMEOUT);
56535a5a358SJonathan Adams 	const uint_t	tick = (flags & SDC_UPDATE_TICK);
56635a5a358SJonathan Adams 
56735a5a358SJonathan Adams 	hrtime_t	O, R;
56835a5a358SJonathan Adams 	hrtime_t	newO = -1;
56935a5a358SJonathan Adams 
57035a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));
57135a5a358SJonathan Adams 
57235a5a358SJonathan Adams 	sysdc_update_times(sdc, flags, &O, &R, &newO);
57335a5a358SJonathan Adams 	ASSERT(!update || newO != -1);
57435a5a358SJonathan Adams 
57535a5a358SJonathan Adams 	/* If we have new data, recompute our priority. */
57635a5a358SJonathan Adams 	if ((O + R) != 0) {
57735a5a358SJonathan Adams 		sdc->sdc_cur_DC = (O * SYSDC_DC_MAX) / (O + R);
57835a5a358SJonathan Adams 
57935a5a358SJonathan Adams 		/* Adjust our priority to move our DC closer to the target. */
58035a5a358SJonathan Adams 		if (sdc->sdc_cur_DC < sdc->sdc_target_DC)
58135a5a358SJonathan Adams 			sdc->sdc_pri = sdc->sdc_maxpri;
58235a5a358SJonathan Adams 		else
58335a5a358SJonathan Adams 			sdc->sdc_pri = sdc->sdc_minpri;
58435a5a358SJonathan Adams 	}
58535a5a358SJonathan Adams 
58635a5a358SJonathan Adams 	/*
58735a5a358SJonathan Adams 	 * If our per-pset duty cycle goes over the max, we will take a break.
58835a5a358SJonathan Adams 	 * This forces all sysdc threads in the pset to minimum priority, in
58935a5a358SJonathan Adams 	 * order to let everyone else have a chance at the CPU.
59035a5a358SJonathan Adams 	 */
59135a5a358SJonathan Adams 	if (sdc->sdc_pset->sdp_need_break) {
59235a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_compute_pri_breaking);
59335a5a358SJonathan Adams 		sdc->sdc_epri = sdc->sdc_minpri;
59435a5a358SJonathan Adams 	} else {
59535a5a358SJonathan Adams 		sdc->sdc_epri = sdc->sdc_pri;
59635a5a358SJonathan Adams 	}
59735a5a358SJonathan Adams 
59835a5a358SJonathan Adams 	DTRACE_PROBE4(sysdc__compute__pri,
59935a5a358SJonathan Adams 	    kthread_t *, t, pri_t, sdc->sdc_epri, uint_t, sdc->sdc_cur_DC,
60035a5a358SJonathan Adams 	    uint_t, sdc->sdc_target_DC);
60135a5a358SJonathan Adams 
60235a5a358SJonathan Adams 	/*
60335a5a358SJonathan Adams 	 * For sysdc_update(), we compute the ONPROC time for high-priority
60435a5a358SJonathan Adams 	 * threads, which is used to calculate the per-pset duty cycle.  We
60535a5a358SJonathan Adams 	 * will always tell our callers to update the thread's priority,
60635a5a358SJonathan Adams 	 * since we want to force a cpu_surrender().
60735a5a358SJonathan Adams 	 *
60835a5a358SJonathan Adams 	 * We reset sdc_update_ticks so that sysdc_tick() will only update
60935a5a358SJonathan Adams 	 * the thread's priority if our timeout is delayed by a tick or
61035a5a358SJonathan Adams 	 * more.
61135a5a358SJonathan Adams 	 */
61235a5a358SJonathan Adams 	if (update) {
61335a5a358SJonathan Adams 		/* SDC threads are not allowed to change cpupart bindings. */
61435a5a358SJonathan Adams 		ASSERT(t->t_cpupart == sdc->sdc_pset->sdp_cpupart);
61535a5a358SJonathan Adams 
61635a5a358SJonathan Adams 		/* If we were at MAXPRI, account for our onproc time. */
61735a5a358SJonathan Adams 		if (t->t_pri == sdc->sdc_maxpri &&
61835a5a358SJonathan Adams 		    sdc->sdc_last_base_O != 0 &&
61935a5a358SJonathan Adams 		    sdc->sdc_last_base_O < newO) {
62035a5a358SJonathan Adams 			sdc->sdc_last_O = newO - sdc->sdc_last_base_O;
62135a5a358SJonathan Adams 			sdc->sdc_pset->sdp_onproc_time +=
62235a5a358SJonathan Adams 			    (uint64_t)sdc->sdc_last_O;
62335a5a358SJonathan Adams 			sdc->sdc_pset->sdp_onproc_threads++;
62435a5a358SJonathan Adams 		} else {
62535a5a358SJonathan Adams 			sdc->sdc_last_O = 0;
62635a5a358SJonathan Adams 		}
62735a5a358SJonathan Adams 		sdc->sdc_last_base_O = newO;
62835a5a358SJonathan Adams 
62935a5a358SJonathan Adams 		sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks + 1;
63035a5a358SJonathan Adams 		return (1);
63135a5a358SJonathan Adams 	}
63235a5a358SJonathan Adams 
63335a5a358SJonathan Adams 	/*
63435a5a358SJonathan Adams 	 * Like sysdc_update(), sysdc_tick() always wants to update the
63535a5a358SJonathan Adams 	 * thread's priority, so that the CPU is surrendered if necessary.
63635a5a358SJonathan Adams 	 * We reset sdc_update_ticks so that if the timeout continues to be
63735a5a358SJonathan Adams 	 * delayed, we'll update at the regular interval.
63835a5a358SJonathan Adams 	 */
63935a5a358SJonathan Adams 	if (tick) {
64035a5a358SJonathan Adams 		ASSERT(sdc->sdc_ticks == sdc->sdc_update_ticks);
64135a5a358SJonathan Adams 		sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks;
64235a5a358SJonathan Adams 		return (1);
64335a5a358SJonathan Adams 	}
64435a5a358SJonathan Adams 
64535a5a358SJonathan Adams 	/*
64635a5a358SJonathan Adams 	 * Otherwise, only tell our callers to update the priority if it has
64735a5a358SJonathan Adams 	 * changed.
64835a5a358SJonathan Adams 	 */
64935a5a358SJonathan Adams 	return (sdc->sdc_epri != t->t_pri);
65035a5a358SJonathan Adams }
65135a5a358SJonathan Adams 
65235a5a358SJonathan Adams static void
65335a5a358SJonathan Adams sysdc_update_pri(sysdc_t *sdc, uint_t flags)
65435a5a358SJonathan Adams {
65535a5a358SJonathan Adams 	kthread_t *t = sdc->sdc_thread;
65635a5a358SJonathan Adams 
65735a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));
65835a5a358SJonathan Adams 
65935a5a358SJonathan Adams 	if (sysdc_compute_pri(sdc, flags)) {
66035a5a358SJonathan Adams 		if (!thread_change_pri(t, sdc->sdc_epri, 0)) {
66135a5a358SJonathan Adams 			cpu_surrender(t);
66235a5a358SJonathan Adams 		}
66335a5a358SJonathan Adams 	}
66435a5a358SJonathan Adams }
66535a5a358SJonathan Adams 
66635a5a358SJonathan Adams /*
66735a5a358SJonathan Adams  * Add a thread onto the active list.  It will only be removed by
66835a5a358SJonathan Adams  * sysdc_update().
66935a5a358SJonathan Adams  */
67035a5a358SJonathan Adams static void
67135a5a358SJonathan Adams sysdc_activate(sysdc_t *sdc)
67235a5a358SJonathan Adams {
67335a5a358SJonathan Adams 	sysdc_t *volatile *headp = &SYSDC_LIST(sdc)->sdl_list;
67435a5a358SJonathan Adams 	sysdc_t		*head;
67535a5a358SJonathan Adams 	kthread_t	*t = sdc->sdc_thread;
67635a5a358SJonathan Adams 
67735a5a358SJonathan Adams 	SYSDC_INC_STAT(sysdc_activate_enter);
67835a5a358SJonathan Adams 
67935a5a358SJonathan Adams 	ASSERT(sdc->sdc_next == NULL);
68035a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));
68135a5a358SJonathan Adams 
68235a5a358SJonathan Adams 	do {
68335a5a358SJonathan Adams 		head = *headp;
68435a5a358SJonathan Adams 		sdc->sdc_next = head;
68535a5a358SJonathan Adams 	} while (atomic_cas_ptr(headp, head, sdc) != head);
68635a5a358SJonathan Adams }
68735a5a358SJonathan Adams 
68835a5a358SJonathan Adams /*
68935a5a358SJonathan Adams  * sysdc_update() has two jobs:
69035a5a358SJonathan Adams  *
69135a5a358SJonathan Adams  *	1. It updates the priorities of all active SDC threads on the system.
69235a5a358SJonathan Adams  *	2. It measures pset CPU usage and enforces sysdc_max_pset_DC.
69335a5a358SJonathan Adams  */
69435a5a358SJonathan Adams static void
69535a5a358SJonathan Adams sysdc_update(void *arg)
69635a5a358SJonathan Adams {
69735a5a358SJonathan Adams 	int		idx;
69835a5a358SJonathan Adams 	sysdc_t		*freelist = NULL;
69935a5a358SJonathan Adams 	sysdc_pset_t	*cur;
70035a5a358SJonathan Adams 	hrtime_t	now, diff;
70135a5a358SJonathan Adams 	uint_t		redeploy = 1;
70235a5a358SJonathan Adams 
70335a5a358SJonathan Adams 	SYSDC_INC_STAT(sysdc_update_enter);
70435a5a358SJonathan Adams 
70535a5a358SJonathan Adams 	ASSERT(sysdc_update_timeout_started);
70635a5a358SJonathan Adams 
70735a5a358SJonathan Adams 	/*
70835a5a358SJonathan Adams 	 * If this is our first time through, diff will be gigantic, and
70935a5a358SJonathan Adams 	 * no breaks will be necessary.
71035a5a358SJonathan Adams 	 */
71135a5a358SJonathan Adams 	now = gethrtime();
71235a5a358SJonathan Adams 	diff = now - sysdc_last_update;
71335a5a358SJonathan Adams 	sysdc_last_update = now;
71435a5a358SJonathan Adams 
71535a5a358SJonathan Adams 	mutex_enter(&sysdc_pset_lock);
71635a5a358SJonathan Adams 	for (cur = list_head(&sysdc_psets); cur != NULL;
71735a5a358SJonathan Adams 	    cur = list_next(&sysdc_psets, cur)) {
71835a5a358SJonathan Adams 		boolean_t breaking = (cur->sdp_should_break != 0);
71935a5a358SJonathan Adams 
72035a5a358SJonathan Adams 		if (cur->sdp_need_break != breaking) {
72135a5a358SJonathan Adams 			DTRACE_PROBE2(sdc__pset__break, sysdc_pset_t *, cur,
72235a5a358SJonathan Adams 			    boolean_t, breaking);
72335a5a358SJonathan Adams 		}
72435a5a358SJonathan Adams 		cur->sdp_onproc_time = 0;
72535a5a358SJonathan Adams 		cur->sdp_onproc_threads = 0;
72635a5a358SJonathan Adams 		cur->sdp_need_break = breaking;
72735a5a358SJonathan Adams 	}
72835a5a358SJonathan Adams 	mutex_exit(&sysdc_pset_lock);
72935a5a358SJonathan Adams 
73035a5a358SJonathan Adams 	for (idx = 0; idx < SYSDC_NLISTS; idx++) {
73135a5a358SJonathan Adams 		sysdc_list_t		*sdl = &sysdc_active[idx];
73235a5a358SJonathan Adams 		sysdc_t *volatile	*headp = &sdl->sdl_list;
73335a5a358SJonathan Adams 		sysdc_t			*head, *tail;
73435a5a358SJonathan Adams 		sysdc_t			**prevptr;
73535a5a358SJonathan Adams 
73635a5a358SJonathan Adams 		if (*headp == &sysdc_dummy)
73735a5a358SJonathan Adams 			continue;
73835a5a358SJonathan Adams 
73935a5a358SJonathan Adams 		/* Prevent any threads from exiting while we're poking them. */
74035a5a358SJonathan Adams 		mutex_enter(&sdl->sdl_lock);
74135a5a358SJonathan Adams 
74235a5a358SJonathan Adams 		/*
74335a5a358SJonathan Adams 		 * Each sdl_list contains a singly-linked list of active
74435a5a358SJonathan Adams 		 * threads. Threads which become active while we are
74535a5a358SJonathan Adams 		 * processing the list will be added to sdl_list.  Since we
74635a5a358SJonathan Adams 		 * don't want that to interfere with our own processing, we
74735a5a358SJonathan Adams 		 * swap in an empty list.  Any newly active threads will
74835a5a358SJonathan Adams 		 * go on to this empty list.  When finished, we'll put any
74935a5a358SJonathan Adams 		 * such threads at the end of the processed list.
75035a5a358SJonathan Adams 		 */
75135a5a358SJonathan Adams 		head = atomic_swap_ptr(headp, &sysdc_dummy);
75235a5a358SJonathan Adams 		prevptr = &head;
75335a5a358SJonathan Adams 		while (*prevptr != &sysdc_dummy) {
75435a5a358SJonathan Adams 			sysdc_t		*const	sdc = *prevptr;
75535a5a358SJonathan Adams 			kthread_t	*const	t = sdc->sdc_thread;
75635a5a358SJonathan Adams 
75735a5a358SJonathan Adams 			/*
75835a5a358SJonathan Adams 			 * If the thread has exited, move its sysdc_t onto
75935a5a358SJonathan Adams 			 * freelist, to be freed later.
76035a5a358SJonathan Adams 			 */
76135a5a358SJonathan Adams 			if (t == NULL) {
76235a5a358SJonathan Adams 				*prevptr = sdc->sdc_next;
76335a5a358SJonathan Adams 				SYSDC_INC_STAT(sysdc_update_exited);
76435a5a358SJonathan Adams 				sdc->sdc_next = freelist;
76535a5a358SJonathan Adams 				freelist = sdc;
76635a5a358SJonathan Adams 				continue;
76735a5a358SJonathan Adams 			}
76835a5a358SJonathan Adams 
76935a5a358SJonathan Adams 			thread_lock(t);
77035a5a358SJonathan Adams 			if (t->t_cid != sysdccid) {
77135a5a358SJonathan Adams 				thread_unlock(t);
77235a5a358SJonathan Adams 				prevptr = &sdc->sdc_next;
77335a5a358SJonathan Adams 				SYSDC_INC_STAT(sysdc_update_not_sdc);
77435a5a358SJonathan Adams 				continue;
77535a5a358SJonathan Adams 			}
77635a5a358SJonathan Adams 			ASSERT(t->t_cldata == sdc);
77735a5a358SJonathan Adams 
77835a5a358SJonathan Adams 			/*
77935a5a358SJonathan Adams 			 * If the thread has been sleeping for longer
78035a5a358SJonathan Adams 			 * than sysdc_prune_interval, make it inactive by
78135a5a358SJonathan Adams 			 * removing it from the list.
78235a5a358SJonathan Adams 			 */
78335a5a358SJonathan Adams 			if (!(t->t_state & (TS_RUN | TS_ONPROC)) &&
78435a5a358SJonathan Adams 			    sdc->sdc_sleep_updates != 0 &&
78535a5a358SJonathan Adams 			    (sdc->sdc_sleep_updates - sdc->sdc_nupdates) >
78635a5a358SJonathan Adams 			    sysdc_prune_updates) {
78735a5a358SJonathan Adams 				*prevptr = sdc->sdc_next;
78835a5a358SJonathan Adams 				SYSDC_INC_STAT(sysdc_update_idle);
78935a5a358SJonathan Adams 				sdc->sdc_next = NULL;
79035a5a358SJonathan Adams 				thread_unlock(t);
79135a5a358SJonathan Adams 				continue;
79235a5a358SJonathan Adams 			}
79335a5a358SJonathan Adams 			sysdc_update_pri(sdc, SDC_UPDATE_TIMEOUT);
79435a5a358SJonathan Adams 			thread_unlock(t);
79535a5a358SJonathan Adams 
79635a5a358SJonathan Adams 			prevptr = &sdc->sdc_next;
79735a5a358SJonathan Adams 		}
79835a5a358SJonathan Adams 
79935a5a358SJonathan Adams 		/*
80035a5a358SJonathan Adams 		 * Add our list to the bucket, putting any new entries
80135a5a358SJonathan Adams 		 * added while we were working at the tail of the list.
80235a5a358SJonathan Adams 		 */
80335a5a358SJonathan Adams 		do {
80435a5a358SJonathan Adams 			tail = *headp;
80535a5a358SJonathan Adams 			*prevptr = tail;
80635a5a358SJonathan Adams 		} while (atomic_cas_ptr(headp, tail, head) != tail);
80735a5a358SJonathan Adams 
80835a5a358SJonathan Adams 		mutex_exit(&sdl->sdl_lock);
80935a5a358SJonathan Adams 	}
81035a5a358SJonathan Adams 
81135a5a358SJonathan Adams 	mutex_enter(&sysdc_pset_lock);
81235a5a358SJonathan Adams 	for (cur = list_head(&sysdc_psets); cur != NULL;
81335a5a358SJonathan Adams 	    cur = list_next(&sysdc_psets, cur)) {
81435a5a358SJonathan Adams 
81535a5a358SJonathan Adams 		cur->sdp_vtime_last_interval =
81635a5a358SJonathan Adams 		    diff * cur->sdp_cpupart->cp_ncpus;
81735a5a358SJonathan Adams 		cur->sdp_DC_last_interval =
81835a5a358SJonathan Adams 		    (cur->sdp_onproc_time * SYSDC_DC_MAX) /
81935a5a358SJonathan Adams 		    cur->sdp_vtime_last_interval;
82035a5a358SJonathan Adams 
82135a5a358SJonathan Adams 		if (cur->sdp_should_break > 0) {
82235a5a358SJonathan Adams 			cur->sdp_should_break--;	/* breaking */
82335a5a358SJonathan Adams 			continue;
82435a5a358SJonathan Adams 		}
82535a5a358SJonathan Adams 		if (cur->sdp_dont_break > 0) {
82635a5a358SJonathan Adams 			cur->sdp_dont_break--;	/* waiting before checking */
82735a5a358SJonathan Adams 			continue;
82835a5a358SJonathan Adams 		}
82935a5a358SJonathan Adams 		if (cur->sdp_DC_last_interval > sysdc_max_pset_DC) {
83035a5a358SJonathan Adams 			cur->sdp_should_break = sysdc_break_updates;
83135a5a358SJonathan Adams 			cur->sdp_dont_break = sysdc_nobreak_updates;
83235a5a358SJonathan Adams 			SYSDC_INC_STAT(sysdc_update_take_break);
83335a5a358SJonathan Adams 		}
83435a5a358SJonathan Adams 	}
83535a5a358SJonathan Adams 
83635a5a358SJonathan Adams 	/*
83735a5a358SJonathan Adams 	 * If there are no sysdc_psets, there can be no threads, so
83835a5a358SJonathan Adams 	 * we can stop doing our timeout.  Since we're holding the
83935a5a358SJonathan Adams 	 * sysdc_pset_lock, no new sysdc_psets can come in, which will
84035a5a358SJonathan Adams 	 * prevent anyone from racing with this and dropping our timeout
84135a5a358SJonathan Adams 	 * on the floor.
84235a5a358SJonathan Adams 	 */
84335a5a358SJonathan Adams 	if (list_is_empty(&sysdc_psets)) {
84435a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_update_no_psets);
84535a5a358SJonathan Adams 		ASSERT(sysdc_update_timeout_started);
84635a5a358SJonathan Adams 		sysdc_update_timeout_started = 0;
84735a5a358SJonathan Adams 
84835a5a358SJonathan Adams 		redeploy = 0;
84935a5a358SJonathan Adams 	}
85035a5a358SJonathan Adams 	mutex_exit(&sysdc_pset_lock);
85135a5a358SJonathan Adams 
85235a5a358SJonathan Adams 	while (freelist != NULL) {
85335a5a358SJonathan Adams 		sysdc_t *cur = freelist;
85435a5a358SJonathan Adams 		freelist = cur->sdc_next;
85535a5a358SJonathan Adams 		kmem_free(cur, sizeof (*cur));
85635a5a358SJonathan Adams 	}
85735a5a358SJonathan Adams 
85835a5a358SJonathan Adams 	if (redeploy) {
85935a5a358SJonathan Adams 		(void) timeout(sysdc_update, arg, sysdc_update_ticks);
86035a5a358SJonathan Adams 	}
86135a5a358SJonathan Adams }
86235a5a358SJonathan Adams 
86335a5a358SJonathan Adams static void
86435a5a358SJonathan Adams sysdc_preempt(kthread_t *t)
86535a5a358SJonathan Adams {
86635a5a358SJonathan Adams 	ASSERT(t == curthread);
86735a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));
86835a5a358SJonathan Adams 
86935a5a358SJonathan Adams 	setbackdq(t);		/* give others a chance to run */
87035a5a358SJonathan Adams }
87135a5a358SJonathan Adams 
87235a5a358SJonathan Adams static void
87335a5a358SJonathan Adams sysdc_tick(kthread_t *t)
87435a5a358SJonathan Adams {
87535a5a358SJonathan Adams 	sysdc_t *sdc;
87635a5a358SJonathan Adams 
87735a5a358SJonathan Adams 	thread_lock(t);
87835a5a358SJonathan Adams 	if (t->t_cid != sysdccid) {
87935a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_tick_not_sdc);
88035a5a358SJonathan Adams 		thread_unlock(t);
88135a5a358SJonathan Adams 		return;
88235a5a358SJonathan Adams 	}
88335a5a358SJonathan Adams 	sdc = t->t_cldata;
88435a5a358SJonathan Adams 	if (t->t_state == TS_ONPROC &&
88535a5a358SJonathan Adams 	    t->t_pri < t->t_disp_queue->disp_maxrunpri) {
88635a5a358SJonathan Adams 		cpu_surrender(t);
88735a5a358SJonathan Adams 	}
88835a5a358SJonathan Adams 
88935a5a358SJonathan Adams 	if (t->t_state == TS_ONPROC || t->t_state == TS_RUN) {
89035a5a358SJonathan Adams 		ASSERT(sdc->sdc_sleep_updates == 0);
89135a5a358SJonathan Adams 	}
89235a5a358SJonathan Adams 
89335a5a358SJonathan Adams 	ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks);
89435a5a358SJonathan Adams 	sdc->sdc_ticks++;
89535a5a358SJonathan Adams 	if (sdc->sdc_ticks == sdc->sdc_update_ticks) {
89635a5a358SJonathan Adams 		SYSDC_INC_STAT(sysdc_tick_quantum_expired);
89735a5a358SJonathan Adams 		sysdc_update_pri(sdc, SDC_UPDATE_TICK);
89835a5a358SJonathan Adams 		ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks);
89935a5a358SJonathan Adams 	}
90035a5a358SJonathan Adams 	thread_unlock(t);
90135a5a358SJonathan Adams }
90235a5a358SJonathan Adams 
90335a5a358SJonathan Adams static void
90435a5a358SJonathan Adams sysdc_setrun(kthread_t *t)
90535a5a358SJonathan Adams {
90635a5a358SJonathan Adams 	sysdc_t *sdc = t->t_cldata;
90735a5a358SJonathan Adams 
90835a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));	/* t should be in transition */
90935a5a358SJonathan Adams 
91035a5a358SJonathan Adams 	sdc->sdc_sleep_updates = 0;
91135a5a358SJonathan Adams 
91235a5a358SJonathan Adams 	if (sdc->sdc_next == NULL) {
91335a5a358SJonathan Adams 		/*
91435a5a358SJonathan Adams 		 * Since we're in transition, we don't want to use the
91535a5a358SJonathan Adams 		 * full thread_update_pri().
91635a5a358SJonathan Adams 		 */
91735a5a358SJonathan Adams 		if (sysdc_compute_pri(sdc, 0)) {
91835a5a358SJonathan Adams 			THREAD_CHANGE_PRI(t, sdc->sdc_epri);
91935a5a358SJonathan Adams 		}
92035a5a358SJonathan Adams 		sysdc_activate(sdc);
92135a5a358SJonathan Adams 
92235a5a358SJonathan Adams 		ASSERT(sdc->sdc_next != NULL);
92335a5a358SJonathan Adams 	}
92435a5a358SJonathan Adams 
92535a5a358SJonathan Adams 	setbackdq(t);
92635a5a358SJonathan Adams }
92735a5a358SJonathan Adams 
92835a5a358SJonathan Adams static void
92935a5a358SJonathan Adams sysdc_wakeup(kthread_t *t)
93035a5a358SJonathan Adams {
93135a5a358SJonathan Adams 	sysdc_setrun(t);
93235a5a358SJonathan Adams }
93335a5a358SJonathan Adams 
93435a5a358SJonathan Adams static void
93535a5a358SJonathan Adams sysdc_sleep(kthread_t *t)
93635a5a358SJonathan Adams {
93735a5a358SJonathan Adams 	sysdc_t *sdc = t->t_cldata;
93835a5a358SJonathan Adams 
93935a5a358SJonathan Adams 	ASSERT(THREAD_LOCK_HELD(t));	/* t should be in transition */
94035a5a358SJonathan Adams 
94135a5a358SJonathan Adams 	sdc->sdc_sleep_updates = sdc->sdc_nupdates;
94235a5a358SJonathan Adams }
94335a5a358SJonathan Adams 
94435a5a358SJonathan Adams /*ARGSUSED*/
94535a5a358SJonathan Adams static int
94635a5a358SJonathan Adams sysdc_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp,
94735a5a358SJonathan Adams     void *bufp)
94835a5a358SJonathan Adams {
94935a5a358SJonathan Adams 	cpupart_t *const cpupart = t->t_cpupart;
95035a5a358SJonathan Adams 	sysdc_t *sdc = bufp;
95135a5a358SJonathan Adams 	sysdc_params_t *sdpp = parmsp;
95235a5a358SJonathan Adams 	sysdc_pset_t *newpset = sdc->sdc_pset;
95335a5a358SJonathan Adams 	sysdc_pset_t *pset;
95435a5a358SJonathan Adams 	int start_timeout;
95535a5a358SJonathan Adams 
95635a5a358SJonathan Adams 	if (t->t_cid != syscid)
95735a5a358SJonathan Adams 		return (EPERM);
95835a5a358SJonathan Adams 
95935a5a358SJonathan Adams 	ASSERT(ttolwp(t) != NULL);
96035a5a358SJonathan Adams 	ASSERT(sdpp != NULL);
96135a5a358SJonathan Adams 	ASSERT(newpset != NULL);
96235a5a358SJonathan Adams 	ASSERT(sysdc_param_init);
96335a5a358SJonathan Adams 
96435a5a358SJonathan Adams 	ASSERT(sdpp->sdp_minpri >= sysdc_minpri);
96535a5a358SJonathan Adams 	ASSERT(sdpp->sdp_maxpri <= sysdc_maxpri);
96635a5a358SJonathan Adams 	ASSERT(sdpp->sdp_DC >= sysdc_minDC);
96735a5a358SJonathan Adams 	ASSERT(sdpp->sdp_DC <= sysdc_maxDC);
96835a5a358SJonathan Adams 
96935a5a358SJonathan Adams 	sdc->sdc_thread = t;
97035a5a358SJonathan Adams 	sdc->sdc_pri = sdpp->sdp_maxpri;	/* start off maximally */
97135a5a358SJonathan Adams 	sdc->sdc_minpri = sdpp->sdp_minpri;
97235a5a358SJonathan Adams 	sdc->sdc_maxpri = sdpp->sdp_maxpri;
97335a5a358SJonathan Adams 	sdc->sdc_target_DC = sdpp->sdp_DC;
97435a5a358SJonathan Adams 	sdc->sdc_ticks = 0;
97535a5a358SJonathan Adams 	sdc->sdc_update_ticks = sysdc_update_ticks + 1;
97635a5a358SJonathan Adams 
97735a5a358SJonathan Adams 	/* Assign ourselves to the appropriate pset. */
97835a5a358SJonathan Adams 	sdc->sdc_pset = NULL;
97935a5a358SJonathan Adams 	mutex_enter(&sysdc_pset_lock);
98035a5a358SJonathan Adams 	for (pset = list_head(&sysdc_psets); pset != NULL;
98135a5a358SJonathan Adams 	    pset = list_next(&sysdc_psets, pset)) {
98235a5a358SJonathan Adams 		if (pset->sdp_cpupart == cpupart) {
98335a5a358SJonathan Adams 			break;
98435a5a358SJonathan Adams 		}
98535a5a358SJonathan Adams 	}
98635a5a358SJonathan Adams 	if (pset == NULL) {
98735a5a358SJonathan Adams 		pset = newpset;
98835a5a358SJonathan Adams 		newpset = NULL;
98935a5a358SJonathan Adams 		pset->sdp_cpupart = cpupart;
99035a5a358SJonathan Adams 		list_insert_tail(&sysdc_psets, pset);
99135a5a358SJonathan Adams 	}
99235a5a358SJonathan Adams 	pset->sdp_nthreads++;
99335a5a358SJonathan Adams 	ASSERT(pset->sdp_nthreads > 0);
99435a5a358SJonathan Adams 
99535a5a358SJonathan Adams 	sdc->sdc_pset = pset;
99635a5a358SJonathan Adams 
99735a5a358SJonathan Adams 	start_timeout = (sysdc_update_timeout_started == 0);
99835a5a358SJonathan Adams 	sysdc_update_timeout_started = 1;
99935a5a358SJonathan Adams 	mutex_exit(&sysdc_pset_lock);
100035a5a358SJonathan Adams 
100135a5a358SJonathan Adams 	if (newpset != NULL)
100235a5a358SJonathan Adams 		kmem_free(newpset, sizeof (*newpset));
100335a5a358SJonathan Adams 
100435a5a358SJonathan Adams 	/* Update t's scheduling class and priority. */
100535a5a358SJonathan Adams 	thread_lock(t);
100635a5a358SJonathan Adams 	t->t_clfuncs = &(sclass[cid].cl_funcs->thread);
100735a5a358SJonathan Adams 	t->t_cid = cid;
100835a5a358SJonathan Adams 	t->t_cldata = sdc;
100935a5a358SJonathan Adams 	t->t_schedflag |= TS_RUNQMATCH;
101035a5a358SJonathan Adams 
101135a5a358SJonathan Adams 	sysdc_update_pri(sdc, SDC_UPDATE_INITIAL);
101235a5a358SJonathan Adams 	thread_unlock(t);
101335a5a358SJonathan Adams 
101435a5a358SJonathan Adams 	/* Kick off the thread timeout if we're the first one in. */
101535a5a358SJonathan Adams 	if (start_timeout) {
101635a5a358SJonathan Adams 		(void) timeout(sysdc_update, NULL, sysdc_update_ticks);
101735a5a358SJonathan Adams 	}
101835a5a358SJonathan Adams 
101935a5a358SJonathan Adams 	return (0);
102035a5a358SJonathan Adams }
102135a5a358SJonathan Adams 
102235a5a358SJonathan Adams static void
102335a5a358SJonathan Adams sysdc_leave(sysdc_t *sdc)
102435a5a358SJonathan Adams {
102535a5a358SJonathan Adams 	sysdc_pset_t *sdp = sdc->sdc_pset;
102635a5a358SJonathan Adams 	sysdc_list_t *sdl = SYSDC_LIST(sdc);
102735a5a358SJonathan Adams 	uint_t freedc;
102835a5a358SJonathan Adams 
102935a5a358SJonathan Adams 	mutex_enter(&sdl->sdl_lock);		/* block sysdc_update() */
103035a5a358SJonathan Adams 	sdc->sdc_thread = NULL;
103135a5a358SJonathan Adams 	freedc = (sdc->sdc_next == NULL);
103235a5a358SJonathan Adams 	mutex_exit(&sdl->sdl_lock);
103335a5a358SJonathan Adams 
103435a5a358SJonathan Adams 	mutex_enter(&sysdc_pset_lock);
103535a5a358SJonathan Adams 	ASSERT(sdp != NULL);
103635a5a358SJonathan Adams 	ASSERT(sdp->sdp_nthreads > 0);
103735a5a358SJonathan Adams 	--sdp->sdp_nthreads;
103835a5a358SJonathan Adams 	if (sdp->sdp_nthreads == 0) {
103935a5a358SJonathan Adams 		list_remove(&sysdc_psets, sdp);
104035a5a358SJonathan Adams 	} else {
104135a5a358SJonathan Adams 		sdp = NULL;
104235a5a358SJonathan Adams 	}
104335a5a358SJonathan Adams 	mutex_exit(&sysdc_pset_lock);
104435a5a358SJonathan Adams 
104535a5a358SJonathan Adams 	if (freedc)
104635a5a358SJonathan Adams 		kmem_free(sdc, sizeof (*sdc));
104735a5a358SJonathan Adams 	if (sdp != NULL)
104835a5a358SJonathan Adams 		kmem_free(sdp, sizeof (*sdp));
104935a5a358SJonathan Adams }
105035a5a358SJonathan Adams 
105135a5a358SJonathan Adams static void
105235a5a358SJonathan Adams sysdc_exitclass(void *buf)
105335a5a358SJonathan Adams {
105435a5a358SJonathan Adams 	sysdc_leave((sysdc_t *)buf);
105535a5a358SJonathan Adams }
105635a5a358SJonathan Adams 
105735a5a358SJonathan Adams /*ARGSUSED*/
105835a5a358SJonathan Adams static int
105935a5a358SJonathan Adams sysdc_canexit(kthread_t *t, cred_t *reqpcredp)
106035a5a358SJonathan Adams {
106135a5a358SJonathan Adams 	/* Threads cannot exit SDC once joined, except in a body bag. */
106235a5a358SJonathan Adams 	return (EPERM);
106335a5a358SJonathan Adams }
106435a5a358SJonathan Adams 
106535a5a358SJonathan Adams static void
106635a5a358SJonathan Adams sysdc_exit(kthread_t *t)
106735a5a358SJonathan Adams {
106835a5a358SJonathan Adams 	sysdc_t *sdc;
106935a5a358SJonathan Adams 
107035a5a358SJonathan Adams 	/* We're exiting, so we just rejoin the SYS class. */
107135a5a358SJonathan Adams 	thread_lock(t);
107235a5a358SJonathan Adams 	ASSERT(t->t_cid == sysdccid);
107335a5a358SJonathan Adams 	sdc = t->t_cldata;
107435a5a358SJonathan Adams 	t->t_cid = syscid;
107535a5a358SJonathan Adams 	t->t_cldata = NULL;
107635a5a358SJonathan Adams 	t->t_clfuncs = &(sclass[syscid].cl_funcs->thread);
107735a5a358SJonathan Adams 	(void) thread_change_pri(t, maxclsyspri, 0);
107835a5a358SJonathan Adams 	t->t_schedflag &= ~TS_RUNQMATCH;
107935a5a358SJonathan Adams 	thread_unlock_nopreempt(t);
108035a5a358SJonathan Adams 
108135a5a358SJonathan Adams 	/* Unlink the sdc from everything. */
108235a5a358SJonathan Adams 	sysdc_leave(sdc);
108335a5a358SJonathan Adams }
108435a5a358SJonathan Adams 
108535a5a358SJonathan Adams /*ARGSUSED*/
108635a5a358SJonathan Adams static int
108735a5a358SJonathan Adams sysdc_fork(kthread_t *t, kthread_t *ct, void *bufp)
108835a5a358SJonathan Adams {
108935a5a358SJonathan Adams 	/*
109035a5a358SJonathan Adams 	 * Threads cannot be created with SDC as their class; they must
109135a5a358SJonathan Adams 	 * be created as SYS and then added with sysdc_thread_enter().
109235a5a358SJonathan Adams 	 * Because of this restriction, sysdc_fork() should never be called.
109335a5a358SJonathan Adams 	 */
109435a5a358SJonathan Adams 	panic("sysdc cannot be forked");
109535a5a358SJonathan Adams 
109635a5a358SJonathan Adams 	return (ENOSYS);
109735a5a358SJonathan Adams }
109835a5a358SJonathan Adams 
109935a5a358SJonathan Adams /*ARGSUSED*/
110035a5a358SJonathan Adams static void
110135a5a358SJonathan Adams sysdc_forkret(kthread_t *t, kthread_t *ct)
110235a5a358SJonathan Adams {
110335a5a358SJonathan Adams 	/* SDC threads are part of system processes, which never fork. */
110435a5a358SJonathan Adams 	panic("sysdc cannot be forked");
110535a5a358SJonathan Adams }
110635a5a358SJonathan Adams 
110735a5a358SJonathan Adams static pri_t
110835a5a358SJonathan Adams sysdc_globpri(kthread_t *t)
110935a5a358SJonathan Adams {
111035a5a358SJonathan Adams 	return (t->t_epri);
111135a5a358SJonathan Adams }
111235a5a358SJonathan Adams 
111335a5a358SJonathan Adams /*ARGSUSED*/
111435a5a358SJonathan Adams static pri_t
111535a5a358SJonathan Adams sysdc_no_swap(kthread_t *t, int flags)
111635a5a358SJonathan Adams {
111735a5a358SJonathan Adams 	/* SDC threads cannot be swapped. */
111835a5a358SJonathan Adams 	return (-1);
111935a5a358SJonathan Adams }
112035a5a358SJonathan Adams 
112135a5a358SJonathan Adams /*
112235a5a358SJonathan Adams  * Get maximum and minimum priorities enjoyed by SDC threads.
112335a5a358SJonathan Adams  */
112435a5a358SJonathan Adams static int
112535a5a358SJonathan Adams sysdc_getclpri(pcpri_t *pcprip)
112635a5a358SJonathan Adams {
112735a5a358SJonathan Adams 	pcprip->pc_clpmax = sysdc_maxpri;
112835a5a358SJonathan Adams 	pcprip->pc_clpmin = sysdc_minpri;
112935a5a358SJonathan Adams 	return (0);
113035a5a358SJonathan Adams }
113135a5a358SJonathan Adams 
113235a5a358SJonathan Adams /*ARGSUSED*/
113335a5a358SJonathan Adams static int
113435a5a358SJonathan Adams sysdc_getclinfo(void *arg)
113535a5a358SJonathan Adams {
113635a5a358SJonathan Adams 	return (0);		/* no class-specific info */
113735a5a358SJonathan Adams }
113835a5a358SJonathan Adams 
113935a5a358SJonathan Adams /*ARGSUSED*/
114035a5a358SJonathan Adams static int
114135a5a358SJonathan Adams sysdc_alloc(void **p, int flag)
114235a5a358SJonathan Adams {
114335a5a358SJonathan Adams 	sysdc_t *new;
114435a5a358SJonathan Adams 
114535a5a358SJonathan Adams 	*p = NULL;
114635a5a358SJonathan Adams 	if ((new = kmem_zalloc(sizeof (*new), flag)) == NULL) {
114735a5a358SJonathan Adams 		return (ENOMEM);
114835a5a358SJonathan Adams 	}
114935a5a358SJonathan Adams 	if ((new->sdc_pset = kmem_zalloc(sizeof (*new->sdc_pset), flag)) ==
115035a5a358SJonathan Adams 	    NULL) {
115135a5a358SJonathan Adams 		kmem_free(new, sizeof (*new));
115235a5a358SJonathan Adams 		return (ENOMEM);
115335a5a358SJonathan Adams 	}
115435a5a358SJonathan Adams 	*p = new;
115535a5a358SJonathan Adams 	return (0);
115635a5a358SJonathan Adams }
115735a5a358SJonathan Adams 
115835a5a358SJonathan Adams static void
115935a5a358SJonathan Adams sysdc_free(void *p)
116035a5a358SJonathan Adams {
116135a5a358SJonathan Adams 	sysdc_t *sdc = p;
116235a5a358SJonathan Adams 
116335a5a358SJonathan Adams 	if (sdc != NULL) {
116435a5a358SJonathan Adams 		/*
116535a5a358SJonathan Adams 		 * We must have failed CL_ENTERCLASS(), so our pset should be
116635a5a358SJonathan Adams 		 * there and unused.
116735a5a358SJonathan Adams 		 */
116835a5a358SJonathan Adams 		ASSERT(sdc->sdc_pset != NULL);
116935a5a358SJonathan Adams 		ASSERT(sdc->sdc_pset->sdp_cpupart == NULL);
117035a5a358SJonathan Adams 		kmem_free(sdc->sdc_pset, sizeof (*sdc->sdc_pset));
117135a5a358SJonathan Adams 		kmem_free(sdc, sizeof (*sdc));
117235a5a358SJonathan Adams 	}
117335a5a358SJonathan Adams }
117435a5a358SJonathan Adams 
117535a5a358SJonathan Adams static int sysdc_enosys();	/* Boy, ANSI-C's K&R compatibility is weird. */
117635a5a358SJonathan Adams static int sysdc_einval();
117735a5a358SJonathan Adams static void sysdc_nullsys();
117835a5a358SJonathan Adams 
117935a5a358SJonathan Adams static struct classfuncs sysdc_classfuncs = {
118035a5a358SJonathan Adams 	/* messages to class manager */
118135a5a358SJonathan Adams 	{
118235a5a358SJonathan Adams 		sysdc_enosys,	/* admin */
118335a5a358SJonathan Adams 		sysdc_getclinfo,
118435a5a358SJonathan Adams 		sysdc_enosys,	/* parmsin */
118535a5a358SJonathan Adams 		sysdc_enosys,	/* parmsout */
118635a5a358SJonathan Adams 		sysdc_enosys,	/* vaparmsin */
118735a5a358SJonathan Adams 		sysdc_enosys,	/* vaparmsout */
118835a5a358SJonathan Adams 		sysdc_getclpri,
118935a5a358SJonathan Adams 		sysdc_alloc,
119035a5a358SJonathan Adams 		sysdc_free,
119135a5a358SJonathan Adams 	},
119235a5a358SJonathan Adams 	/* operations on threads */
119335a5a358SJonathan Adams 	{
119435a5a358SJonathan Adams 		sysdc_enterclass,
119535a5a358SJonathan Adams 		sysdc_exitclass,
119635a5a358SJonathan Adams 		sysdc_canexit,
119735a5a358SJonathan Adams 		sysdc_fork,
119835a5a358SJonathan Adams 		sysdc_forkret,
119935a5a358SJonathan Adams 		sysdc_nullsys,	/* parmsget */
120035a5a358SJonathan Adams 		sysdc_enosys,	/* parmsset */
120135a5a358SJonathan Adams 		sysdc_nullsys,	/* stop */
120235a5a358SJonathan Adams 		sysdc_exit,
120335a5a358SJonathan Adams 		sysdc_nullsys,	/* active */
120435a5a358SJonathan Adams 		sysdc_nullsys,	/* inactive */
120535a5a358SJonathan Adams 		sysdc_no_swap,	/* swapin */
120635a5a358SJonathan Adams 		sysdc_no_swap,	/* swapout */
120735a5a358SJonathan Adams 		sysdc_nullsys,	/* trapret */
120835a5a358SJonathan Adams 		sysdc_preempt,
120935a5a358SJonathan Adams 		sysdc_setrun,
121035a5a358SJonathan Adams 		sysdc_sleep,
121135a5a358SJonathan Adams 		sysdc_tick,
121235a5a358SJonathan Adams 		sysdc_wakeup,
121335a5a358SJonathan Adams 		sysdc_einval,	/* donice */
121435a5a358SJonathan Adams 		sysdc_globpri,
121535a5a358SJonathan Adams 		sysdc_nullsys,	/* set_process_group */
121635a5a358SJonathan Adams 		sysdc_nullsys,	/* yield */
121735a5a358SJonathan Adams 		sysdc_einval,	/* doprio */
121835a5a358SJonathan Adams 	}
121935a5a358SJonathan Adams };
122035a5a358SJonathan Adams 
122135a5a358SJonathan Adams static int
122235a5a358SJonathan Adams sysdc_enosys()
122335a5a358SJonathan Adams {
122435a5a358SJonathan Adams 	return (ENOSYS);
122535a5a358SJonathan Adams }
122635a5a358SJonathan Adams 
122735a5a358SJonathan Adams static int
122835a5a358SJonathan Adams sysdc_einval()
122935a5a358SJonathan Adams {
123035a5a358SJonathan Adams 	return (EINVAL);
123135a5a358SJonathan Adams }
123235a5a358SJonathan Adams 
123335a5a358SJonathan Adams static void
123435a5a358SJonathan Adams sysdc_nullsys()
123535a5a358SJonathan Adams {
123635a5a358SJonathan Adams }
123735a5a358SJonathan Adams 
123835a5a358SJonathan Adams /*ARGSUSED*/
123935a5a358SJonathan Adams static pri_t
124035a5a358SJonathan Adams sysdc_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp)
124135a5a358SJonathan Adams {
124235a5a358SJonathan Adams 	int idx;
124335a5a358SJonathan Adams 
124435a5a358SJonathan Adams 	list_create(&sysdc_psets, sizeof (sysdc_pset_t),
124535a5a358SJonathan Adams 	    offsetof(sysdc_pset_t, sdp_node));
124635a5a358SJonathan Adams 
124735a5a358SJonathan Adams 	for (idx = 0; idx < SYSDC_NLISTS; idx++) {
124835a5a358SJonathan Adams 		sysdc_active[idx].sdl_list = &sysdc_dummy;
124935a5a358SJonathan Adams 	}
125035a5a358SJonathan Adams 
125135a5a358SJonathan Adams 	sysdc_initparam();
125235a5a358SJonathan Adams 
125335a5a358SJonathan Adams 	sysdccid = cid;
125435a5a358SJonathan Adams 	*clfuncspp = &sysdc_classfuncs;
125535a5a358SJonathan Adams 
125635a5a358SJonathan Adams 	return ((pri_t)v.v_maxsyspri);
125735a5a358SJonathan Adams }
125835a5a358SJonathan Adams 
125935a5a358SJonathan Adams static struct sclass csw = {
126035a5a358SJonathan Adams 	"SDC",
126135a5a358SJonathan Adams 	sysdc_init,
126235a5a358SJonathan Adams 	0
126335a5a358SJonathan Adams };
126435a5a358SJonathan Adams 
126535a5a358SJonathan Adams static struct modlsched modlsched = {
126635a5a358SJonathan Adams 	&mod_schedops, "system duty cycle scheduling class", &csw
126735a5a358SJonathan Adams };
126835a5a358SJonathan Adams 
126935a5a358SJonathan Adams static struct modlinkage modlinkage = {
127035a5a358SJonathan Adams 	MODREV_1, (void *)&modlsched, NULL
127135a5a358SJonathan Adams };
127235a5a358SJonathan Adams 
127335a5a358SJonathan Adams int
127435a5a358SJonathan Adams _init()
127535a5a358SJonathan Adams {
127635a5a358SJonathan Adams 	return (mod_install(&modlinkage));
127735a5a358SJonathan Adams }
127835a5a358SJonathan Adams 
127935a5a358SJonathan Adams int
128035a5a358SJonathan Adams _fini()
128135a5a358SJonathan Adams {
128235a5a358SJonathan Adams 	return (EBUSY);		/* can't unload for now */
128335a5a358SJonathan Adams }
128435a5a358SJonathan Adams 
128535a5a358SJonathan Adams int
128635a5a358SJonathan Adams _info(struct modinfo *modinfop)
128735a5a358SJonathan Adams {
128835a5a358SJonathan Adams 	return (mod_info(&modlinkage, modinfop));
128935a5a358SJonathan Adams }
129035a5a358SJonathan Adams 
129135a5a358SJonathan Adams /* --- consolidation-private interfaces --- */
129235a5a358SJonathan Adams void
129335a5a358SJonathan Adams sysdc_thread_enter(kthread_t *t, uint_t dc, uint_t flags)
129435a5a358SJonathan Adams {
129535a5a358SJonathan Adams 	void *buf = NULL;
129635a5a358SJonathan Adams 	sysdc_params_t sdp;
129735a5a358SJonathan Adams 
129835a5a358SJonathan Adams 	SYSDC_INC_STAT(sysdc_thread_enter_enter);
129935a5a358SJonathan Adams 
130035a5a358SJonathan Adams 	ASSERT(sysdc_param_init);
130135a5a358SJonathan Adams 	ASSERT(sysdccid >= 0);
130235a5a358SJonathan Adams 
130335a5a358SJonathan Adams 	ASSERT((flags & ~SYSDC_THREAD_BATCH) == 0);
130435a5a358SJonathan Adams 
130535a5a358SJonathan Adams 	sdp.sdp_minpri = sysdc_minpri;
130635a5a358SJonathan Adams 	sdp.sdp_maxpri = sysdc_maxpri;
130735a5a358SJonathan Adams 	sdp.sdp_DC = MAX(MIN(dc, sysdc_maxDC), sysdc_minDC);
130835a5a358SJonathan Adams 
1309*fb09f5aaSMadhav Suresh 	VERIFY0(CL_ALLOC(&buf, sysdccid, KM_SLEEP));
131035a5a358SJonathan Adams 
131135a5a358SJonathan Adams 	ASSERT(t->t_lwp != NULL);
131235a5a358SJonathan Adams 	ASSERT(t->t_cid == syscid);
131335a5a358SJonathan Adams 	ASSERT(t->t_cldata == NULL);
1314*fb09f5aaSMadhav Suresh 	VERIFY0(CL_CANEXIT(t, NULL));
1315*fb09f5aaSMadhav Suresh 	VERIFY0(CL_ENTERCLASS(t, sysdccid, &sdp, kcred, buf));
131635a5a358SJonathan Adams 	CL_EXITCLASS(syscid, NULL);
131735a5a358SJonathan Adams }
1318