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