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