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