1*35a5a358SJonathan Adams /* 2*35a5a358SJonathan Adams * CDDL HEADER START 3*35a5a358SJonathan Adams * 4*35a5a358SJonathan Adams * The contents of this file are subject to the terms of the 5*35a5a358SJonathan Adams * Common Development and Distribution License (the "License"). 6*35a5a358SJonathan Adams * You may not use this file except in compliance with the License. 7*35a5a358SJonathan Adams * 8*35a5a358SJonathan Adams * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*35a5a358SJonathan Adams * or http://www.opensolaris.org/os/licensing. 10*35a5a358SJonathan Adams * See the License for the specific language governing permissions 11*35a5a358SJonathan Adams * and limitations under the License. 12*35a5a358SJonathan Adams * 13*35a5a358SJonathan Adams * When distributing Covered Code, include this CDDL HEADER in each 14*35a5a358SJonathan Adams * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*35a5a358SJonathan Adams * If applicable, add the following below this CDDL HEADER, with the 16*35a5a358SJonathan Adams * fields enclosed by brackets "[]" replaced with your own identifying 17*35a5a358SJonathan Adams * information: Portions Copyright [yyyy] [name of copyright owner] 18*35a5a358SJonathan Adams * 19*35a5a358SJonathan Adams * CDDL HEADER END 20*35a5a358SJonathan Adams */ 21*35a5a358SJonathan Adams /* 22*35a5a358SJonathan Adams * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23*35a5a358SJonathan Adams * Use is subject to license terms. 24*35a5a358SJonathan Adams */ 25*35a5a358SJonathan Adams 26*35a5a358SJonathan Adams /* 27*35a5a358SJonathan Adams * The System Duty Cycle (SDC) scheduling class 28*35a5a358SJonathan Adams * -------------------------------------------- 29*35a5a358SJonathan Adams * 30*35a5a358SJonathan Adams * Background 31*35a5a358SJonathan Adams * 32*35a5a358SJonathan Adams * Kernel threads in Solaris have traditionally not been large consumers 33*35a5a358SJonathan Adams * of CPU time. They typically wake up, perform a small amount of 34*35a5a358SJonathan Adams * work, then go back to sleep waiting for either a timeout or another 35*35a5a358SJonathan Adams * signal. On the assumption that the small amount of work that they do 36*35a5a358SJonathan Adams * is important for the behavior of the whole system, these threads are 37*35a5a358SJonathan Adams * treated kindly by the dispatcher and the SYS scheduling class: they run 38*35a5a358SJonathan Adams * without preemption from anything other than real-time and interrupt 39*35a5a358SJonathan Adams * threads; when preempted, they are put at the front of the queue, so they 40*35a5a358SJonathan Adams * generally do not migrate between CPUs; and they are allowed to stay 41*35a5a358SJonathan Adams * running until they voluntarily give up the CPU. 42*35a5a358SJonathan Adams * 43*35a5a358SJonathan Adams * As Solaris has evolved, new workloads have emerged which require the 44*35a5a358SJonathan Adams * kernel to perform significant amounts of CPU-intensive work. One 45*35a5a358SJonathan Adams * example of such a workload is ZFS's transaction group sync processing. 46*35a5a358SJonathan Adams * Each sync operation generates a large batch of I/Os, and each I/O 47*35a5a358SJonathan Adams * may need to be compressed and/or checksummed before it is written to 48*35a5a358SJonathan Adams * storage. The taskq threads which perform the compression and checksums 49*35a5a358SJonathan Adams * will run nonstop as long as they have work to do; a large sync operation 50*35a5a358SJonathan Adams * on a compression-heavy dataset can keep them busy for seconds on end. 51*35a5a358SJonathan Adams * This causes human-time-scale dispatch latency bubbles for any other 52*35a5a358SJonathan Adams * threads which have the misfortune to share a CPU with the taskq threads. 53*35a5a358SJonathan Adams * 54*35a5a358SJonathan Adams * The SDC scheduling class is a solution to this problem. 55*35a5a358SJonathan Adams * 56*35a5a358SJonathan Adams * 57*35a5a358SJonathan Adams * Overview 58*35a5a358SJonathan Adams * 59*35a5a358SJonathan Adams * SDC is centered around the concept of a thread's duty cycle (DC): 60*35a5a358SJonathan Adams * 61*35a5a358SJonathan Adams * ONPROC time 62*35a5a358SJonathan Adams * Duty Cycle = ---------------------- 63*35a5a358SJonathan Adams * ONPROC + Runnable time 64*35a5a358SJonathan Adams * 65*35a5a358SJonathan Adams * This is the ratio of the time that the thread spent running on a CPU 66*35a5a358SJonathan Adams * divided by the time it spent running or trying to run. It is unaffected 67*35a5a358SJonathan Adams * by any time the thread spent sleeping, stopped, etc. 68*35a5a358SJonathan Adams * 69*35a5a358SJonathan Adams * A thread joining the SDC class specifies a "target" DC that it wants 70*35a5a358SJonathan Adams * to run at. To implement this policy, the routine sysdc_update() scans 71*35a5a358SJonathan Adams * the list of active SDC threads every few ticks and uses each thread's 72*35a5a358SJonathan Adams * microstate data to compute the actual duty cycle that that thread 73*35a5a358SJonathan Adams * has experienced recently. If the thread is under its target DC, its 74*35a5a358SJonathan Adams * priority is increased to the maximum available (sysdc_maxpri, which is 75*35a5a358SJonathan Adams * 99 by default). If the thread is over its target DC, its priority is 76*35a5a358SJonathan Adams * reduced to the minimum available (sysdc_minpri, 0 by default). This 77*35a5a358SJonathan Adams * is a fairly primitive approach, in that it doesn't use any of the 78*35a5a358SJonathan Adams * intermediate priorities, but it's not completely inappropriate. Even 79*35a5a358SJonathan Adams * though threads in the SDC class might take a while to do their job, they 80*35a5a358SJonathan Adams * are by some definition important if they're running inside the kernel, 81*35a5a358SJonathan Adams * so it is reasonable that they should get to run at priority 99. 82*35a5a358SJonathan Adams * 83*35a5a358SJonathan Adams * If a thread is running when sysdc_update() calculates its actual duty 84*35a5a358SJonathan Adams * cycle, and there are other threads of equal or greater priority on its 85*35a5a358SJonathan Adams * CPU's dispatch queue, sysdc_update() preempts that thread. The thread 86*35a5a358SJonathan Adams * acknowledges the preemption by calling sysdc_preempt(), which calls 87*35a5a358SJonathan Adams * setbackdq(), which gives other threads with the same priority a chance 88*35a5a358SJonathan Adams * to run. This creates a de facto time quantum for threads in the SDC 89*35a5a358SJonathan Adams * scheduling class. 90*35a5a358SJonathan Adams * 91*35a5a358SJonathan Adams * An SDC thread which is assigned priority 0 can continue to run if 92*35a5a358SJonathan Adams * nothing else needs to use the CPU that it's running on. Similarly, an 93*35a5a358SJonathan Adams * SDC thread at priority 99 might not get to run as much as it wants to 94*35a5a358SJonathan Adams * if there are other priority-99 or higher threads on its CPU. These 95*35a5a358SJonathan Adams * situations would cause the thread to get ahead of or behind its target 96*35a5a358SJonathan Adams * DC; the longer the situations lasted, the further ahead or behind the 97*35a5a358SJonathan Adams * thread would get. Rather than condemning a thread to a lifetime of 98*35a5a358SJonathan Adams * paying for its youthful indiscretions, SDC keeps "base" values for 99*35a5a358SJonathan Adams * ONPROC and Runnable times in each thread's sysdc data, and updates these 100*35a5a358SJonathan Adams * values periodically. The duty cycle is then computed using the elapsed 101*35a5a358SJonathan Adams * amount of ONPROC and Runnable times since those base times. 102*35a5a358SJonathan Adams * 103*35a5a358SJonathan Adams * Since sysdc_update() scans SDC threads fairly frequently, it tries to 104*35a5a358SJonathan Adams * keep the list of "active" threads small by pruning out threads which 105*35a5a358SJonathan Adams * have been asleep for a brief time. They are not pruned immediately upon 106*35a5a358SJonathan Adams * going to sleep, since some threads may bounce back and forth between 107*35a5a358SJonathan Adams * sleeping and being runnable. 108*35a5a358SJonathan Adams * 109*35a5a358SJonathan Adams * 110*35a5a358SJonathan Adams * Interfaces 111*35a5a358SJonathan Adams * 112*35a5a358SJonathan Adams * void sysdc_thread_enter(t, dc, flags) 113*35a5a358SJonathan Adams * 114*35a5a358SJonathan Adams * Moves a kernel thread from the SYS scheduling class to the 115*35a5a358SJonathan Adams * SDC class. t must have an associated LWP (created by calling 116*35a5a358SJonathan Adams * lwp_kernel_create()). The thread will have a target DC of dc. 117*35a5a358SJonathan Adams * Flags should be either 0 or SYSDC_THREAD_BATCH. If 118*35a5a358SJonathan Adams * SYSDC_THREAD_BATCH is specified, the thread will run with a 119*35a5a358SJonathan Adams * slightly lower priority (see "Batch threads", below). 120*35a5a358SJonathan Adams * 121*35a5a358SJonathan Adams * 122*35a5a358SJonathan Adams * Complications 123*35a5a358SJonathan Adams * 124*35a5a358SJonathan Adams * - Run queue balancing 125*35a5a358SJonathan Adams * 126*35a5a358SJonathan Adams * The Solaris dispatcher is biased towards letting a thread run 127*35a5a358SJonathan Adams * on the same CPU which it last ran on, if no more than 3 ticks 128*35a5a358SJonathan Adams * (i.e. rechoose_interval) have passed since the thread last ran. 129*35a5a358SJonathan Adams * This helps to preserve cache warmth. On the other hand, it also 130*35a5a358SJonathan Adams * tries to keep the per-CPU run queues fairly balanced; if the CPU 131*35a5a358SJonathan Adams * chosen for a runnable thread has a run queue which is three or 132*35a5a358SJonathan Adams * more threads longer than a neighboring CPU's queue, the runnable 133*35a5a358SJonathan Adams * thread is dispatched onto the neighboring CPU instead. 134*35a5a358SJonathan Adams * 135*35a5a358SJonathan Adams * These policies work well for some workloads, but not for many SDC 136*35a5a358SJonathan Adams * threads. The taskq client of SDC, for example, has many discrete 137*35a5a358SJonathan Adams * units of work to do. The work units are largely independent, so 138*35a5a358SJonathan Adams * cache warmth is not an important consideration. It is important 139*35a5a358SJonathan Adams * that the threads fan out quickly to different CPUs, since the 140*35a5a358SJonathan Adams * amount of work these threads have to do (a few seconds worth at a 141*35a5a358SJonathan Adams * time) doesn't leave much time to correct thread placement errors 142*35a5a358SJonathan Adams * (i.e. two SDC threads being dispatched to the same CPU). 143*35a5a358SJonathan Adams * 144*35a5a358SJonathan Adams * To fix this, SDC uses the TS_RUNQMATCH flag introduced for FSS. 145*35a5a358SJonathan Adams * This tells the dispatcher to keep neighboring run queues' lengths 146*35a5a358SJonathan Adams * more evenly matched, which allows SDC threads to migrate more 147*35a5a358SJonathan Adams * easily. 148*35a5a358SJonathan Adams * 149*35a5a358SJonathan Adams * - LWPs and system processes 150*35a5a358SJonathan Adams * 151*35a5a358SJonathan Adams * SDC can only be used for kernel threads. Since SDC uses microstate 152*35a5a358SJonathan Adams * accounting data to compute each thread's actual duty cycle, all 153*35a5a358SJonathan Adams * threads entering the SDC class must have associated LWPs (which 154*35a5a358SJonathan Adams * store the microstate data). This means that the threads have to 155*35a5a358SJonathan Adams * be associated with an SSYS process, i.e. one created by newproc(). 156*35a5a358SJonathan Adams * If the microstate accounting information is ever moved into the 157*35a5a358SJonathan Adams * kthread_t, this restriction could be lifted. 158*35a5a358SJonathan Adams * 159*35a5a358SJonathan Adams * - Dealing with oversubscription 160*35a5a358SJonathan Adams * 161*35a5a358SJonathan Adams * Since SDC duty cycles are per-thread, it is possible that the 162*35a5a358SJonathan Adams * aggregate requested duty cycle of all SDC threads in a processor 163*35a5a358SJonathan Adams * set could be greater than the total CPU time available in that set. 164*35a5a358SJonathan Adams * The FSS scheduling class has an analogous situation, which it deals 165*35a5a358SJonathan Adams * with by reducing each thread's allotted CPU time proportionally. 166*35a5a358SJonathan Adams * Since SDC doesn't need to be as precise as FSS, it uses a simpler 167*35a5a358SJonathan Adams * solution to the oversubscription problem. 168*35a5a358SJonathan Adams * 169*35a5a358SJonathan Adams * sysdc_update() accumulates the amount of time that max-priority SDC 170*35a5a358SJonathan Adams * threads have spent on-CPU in each processor set, and uses that sum 171*35a5a358SJonathan Adams * to create an implied duty cycle for that processor set: 172*35a5a358SJonathan Adams * 173*35a5a358SJonathan Adams * accumulated CPU time 174*35a5a358SJonathan Adams * pset DC = ----------------------------------- 175*35a5a358SJonathan Adams * (# CPUs) * time since last update 176*35a5a358SJonathan Adams * 177*35a5a358SJonathan Adams * If this implied duty cycle is above a maximum pset duty cycle (90% 178*35a5a358SJonathan Adams * by default), sysdc_update() sets the priority of all SDC threads 179*35a5a358SJonathan Adams * in that processor set to sysdc_minpri for a "break" period. After 180*35a5a358SJonathan Adams * the break period, it waits for a "nobreak" period before trying to 181*35a5a358SJonathan Adams * enforce the pset duty cycle limit again. 182*35a5a358SJonathan Adams * 183*35a5a358SJonathan Adams * - Processor sets 184*35a5a358SJonathan Adams * 185*35a5a358SJonathan Adams * As the above implies, SDC is processor set aware, but it does not 186*35a5a358SJonathan Adams * currently allow threads to change processor sets while in the SDC 187*35a5a358SJonathan Adams * class. Instead, those threads must join the desired processor set 188*35a5a358SJonathan Adams * before entering SDC. [1] 189*35a5a358SJonathan Adams * 190*35a5a358SJonathan Adams * - Batch threads 191*35a5a358SJonathan Adams * 192*35a5a358SJonathan Adams * A thread joining the SDC class can specify the SDC_THREAD_BATCH 193*35a5a358SJonathan Adams * flag. This flag causes the maximum priority for that thread to be 194*35a5a358SJonathan Adams * reduced (by default, the maximum is reduced by 1). This allows 195*35a5a358SJonathan Adams * longer-running, batch-oriented SDC threads to be interrupted by 196*35a5a358SJonathan Adams * more immediate, higher-priority work. 197*35a5a358SJonathan Adams * 198*35a5a358SJonathan Adams * - t_kpri_req 199*35a5a358SJonathan Adams * 200*35a5a358SJonathan Adams * The TS and FSS scheduling classes pay attention to t_kpri_req, 201*35a5a358SJonathan Adams * which provides a simple form of priority inheritance for 202*35a5a358SJonathan Adams * synchronization primitives (such as rwlocks held as READER) which 203*35a5a358SJonathan Adams * cannot be traced to a unique thread. The SDC class does not honor 204*35a5a358SJonathan Adams * t_kpri_req, for a few reasons: 205*35a5a358SJonathan Adams * 206*35a5a358SJonathan Adams * 1. t_kpri_req is notoriously inaccurate. A measure of its 207*35a5a358SJonathan Adams * inaccuracy is that it needs to be cleared every time a thread 208*35a5a358SJonathan Adams * returns to user mode, because it is frequently non-zero at that 209*35a5a358SJonathan Adams * point. This can happen because "ownership" of synchronization 210*35a5a358SJonathan Adams * primitives that use t_kpri_req can be silently handed off, 211*35a5a358SJonathan Adams * leaving no opportunity to will the t_kpri_req inheritance. 212*35a5a358SJonathan Adams * 213*35a5a358SJonathan Adams * 2. Unlike in TS and FSS, threads in SDC *will* eventually run at 214*35a5a358SJonathan Adams * kernel priority. This means that even if an SDC thread 215*35a5a358SJonathan Adams * is holding a synchronization primitive and running at low 216*35a5a358SJonathan Adams * priority, its priority will eventually be raised above 60, 217*35a5a358SJonathan Adams * allowing it to drive on and release the resource. 218*35a5a358SJonathan Adams * 219*35a5a358SJonathan Adams * 3. The first consumer of SDC uses the taskq subsystem, which holds 220*35a5a358SJonathan Adams * a reader lock for the duration of the task's execution. This 221*35a5a358SJonathan Adams * would mean that SDC threads would never drop below kernel 222*35a5a358SJonathan Adams * priority in practice, which defeats one of the purposes of SDC. 223*35a5a358SJonathan Adams * 224*35a5a358SJonathan Adams * - Why not FSS? 225*35a5a358SJonathan Adams * 226*35a5a358SJonathan Adams * It might seem that the existing FSS scheduling class could solve 227*35a5a358SJonathan Adams * the problems that SDC is attempting to solve. FSS's more precise 228*35a5a358SJonathan Adams * solution to the oversubscription problem would hardly cause 229*35a5a358SJonathan Adams * trouble, as long as it performed well. SDC is implemented as 230*35a5a358SJonathan Adams * a separate scheduling class for two main reasons: the initial 231*35a5a358SJonathan Adams * consumer of SDC does not map well onto the "project" abstraction 232*35a5a358SJonathan Adams * that is central to FSS, and FSS does not expect to run at kernel 233*35a5a358SJonathan Adams * priorities. 234*35a5a358SJonathan Adams * 235*35a5a358SJonathan Adams * 236*35a5a358SJonathan Adams * Tunables 237*35a5a358SJonathan Adams * 238*35a5a358SJonathan Adams * - sysdc_batch_niceness: The amount below sysdc_maxpri that 239*35a5a358SJonathan Adams * SDC_THREAD_BATCH threads should use as their per-thread 240*35a5a358SJonathan Adams * maximum priority. 241*35a5a358SJonathan Adams * 242*35a5a358SJonathan Adams * - sysdc_update_interval_msec: Number of milliseconds between 243*35a5a358SJonathan Adams * consecutive thread priority updates. 244*35a5a358SJonathan Adams * 245*35a5a358SJonathan Adams * - sysdc_reset_interval_msec: Number of milliseconds between 246*35a5a358SJonathan Adams * consecutive resets of a thread's base ONPROC and Runnable 247*35a5a358SJonathan Adams * times. 248*35a5a358SJonathan Adams * 249*35a5a358SJonathan Adams * - sysdc_prune_interval_msec: Number of milliseconds of sleeping 250*35a5a358SJonathan Adams * before a thread is pruned from the active list. 251*35a5a358SJonathan Adams * 252*35a5a358SJonathan Adams * - sysdc_max_pset_DC: Allowable percentage of a processor set's 253*35a5a358SJonathan Adams * CPU time which SDC can give to its high-priority threads. 254*35a5a358SJonathan Adams * 255*35a5a358SJonathan Adams * - sysdc_break_msec: Number of milliseconds of "break" taken when 256*35a5a358SJonathan Adams * sysdc_max_pset_DC is exceeded. 257*35a5a358SJonathan Adams * 258*35a5a358SJonathan Adams * 259*35a5a358SJonathan Adams * Future work (in SDC and related subsystems) 260*35a5a358SJonathan Adams * 261*35a5a358SJonathan Adams * - Per-thread rechoose interval (0 for SDC) 262*35a5a358SJonathan Adams * 263*35a5a358SJonathan Adams * Allow each thread to specify its own rechoose interval. SDC 264*35a5a358SJonathan Adams * threads would specify an interval of zero, which would rechoose 265*35a5a358SJonathan Adams * the CPU with the lowest priority once per update. 266*35a5a358SJonathan Adams * 267*35a5a358SJonathan Adams * - Allow threads to change processor sets after joining the SDC class 268*35a5a358SJonathan Adams * 269*35a5a358SJonathan Adams * - Thread groups and per-group DC 270*35a5a358SJonathan Adams * 271*35a5a358SJonathan Adams * It might be nice to be able to specify a duty cycle which applies 272*35a5a358SJonathan Adams * to a group of threads in aggregate. 273*35a5a358SJonathan Adams * 274*35a5a358SJonathan Adams * - Per-group DC callback to allow dynamic DC tuning 275*35a5a358SJonathan Adams * 276*35a5a358SJonathan Adams * Currently, DCs are assigned when the thread joins SDC. Some 277*35a5a358SJonathan Adams * workloads could benefit from being able to tune their DC using 278*35a5a358SJonathan Adams * subsystem-specific knowledge about the workload. 279*35a5a358SJonathan Adams * 280*35a5a358SJonathan Adams * - Finer-grained priority updates 281*35a5a358SJonathan Adams * 282*35a5a358SJonathan Adams * - More nuanced management of oversubscription 283*35a5a358SJonathan Adams * 284*35a5a358SJonathan Adams * - Moving other CPU-intensive threads into SDC 285*35a5a358SJonathan Adams * 286*35a5a358SJonathan Adams * - Move msacct data into kthread_t 287*35a5a358SJonathan Adams * 288*35a5a358SJonathan Adams * This would allow kernel threads without LWPs to join SDC. 289*35a5a358SJonathan Adams * 290*35a5a358SJonathan Adams * 291*35a5a358SJonathan Adams * Footnotes 292*35a5a358SJonathan Adams * 293*35a5a358SJonathan Adams * [1] The details of doing so are left as an exercise for the reader. 294*35a5a358SJonathan Adams */ 295*35a5a358SJonathan Adams 296*35a5a358SJonathan Adams #include <sys/types.h> 297*35a5a358SJonathan Adams #include <sys/sysdc.h> 298*35a5a358SJonathan Adams #include <sys/sysdc_impl.h> 299*35a5a358SJonathan Adams 300*35a5a358SJonathan Adams #include <sys/class.h> 301*35a5a358SJonathan Adams #include <sys/cmn_err.h> 302*35a5a358SJonathan Adams #include <sys/cpuvar.h> 303*35a5a358SJonathan Adams #include <sys/cpupart.h> 304*35a5a358SJonathan Adams #include <sys/debug.h> 305*35a5a358SJonathan Adams #include <sys/disp.h> 306*35a5a358SJonathan Adams #include <sys/errno.h> 307*35a5a358SJonathan Adams #include <sys/inline.h> 308*35a5a358SJonathan Adams #include <sys/kmem.h> 309*35a5a358SJonathan Adams #include <sys/modctl.h> 310*35a5a358SJonathan Adams #include <sys/schedctl.h> 311*35a5a358SJonathan Adams #include <sys/sdt.h> 312*35a5a358SJonathan Adams #include <sys/sunddi.h> 313*35a5a358SJonathan Adams #include <sys/sysmacros.h> 314*35a5a358SJonathan Adams #include <sys/systm.h> 315*35a5a358SJonathan Adams #include <sys/var.h> 316*35a5a358SJonathan Adams 317*35a5a358SJonathan Adams /* 318*35a5a358SJonathan Adams * Tunables - loaded into the internal state at module load time 319*35a5a358SJonathan Adams */ 320*35a5a358SJonathan Adams uint_t sysdc_update_interval_msec = 20; 321*35a5a358SJonathan Adams uint_t sysdc_reset_interval_msec = 400; 322*35a5a358SJonathan Adams uint_t sysdc_prune_interval_msec = 100; 323*35a5a358SJonathan Adams uint_t sysdc_max_pset_DC = 90; 324*35a5a358SJonathan Adams uint_t sysdc_break_msec = 80; 325*35a5a358SJonathan Adams pri_t sysdc_batch_niceness = 1; 326*35a5a358SJonathan Adams 327*35a5a358SJonathan Adams /* 328*35a5a358SJonathan Adams * Internal state - constants set up by sysdc_initparam() 329*35a5a358SJonathan Adams */ 330*35a5a358SJonathan Adams static clock_t sysdc_update_ticks; /* ticks between updates */ 331*35a5a358SJonathan Adams static uint_t sysdc_prune_updates; /* updates asleep before pruning */ 332*35a5a358SJonathan Adams static uint_t sysdc_reset_updates; /* # of updates before reset */ 333*35a5a358SJonathan Adams static uint_t sysdc_break_updates; /* updates to break */ 334*35a5a358SJonathan Adams static uint_t sysdc_nobreak_updates; /* updates to not check */ 335*35a5a358SJonathan Adams static uint_t sysdc_minDC; /* minimum allowed DC */ 336*35a5a358SJonathan Adams static uint_t sysdc_maxDC; /* maximum allowed DC */ 337*35a5a358SJonathan Adams static pri_t sysdc_minpri; /* minimum allowed priority */ 338*35a5a358SJonathan Adams static pri_t sysdc_maxpri; /* maximum allowed priority */ 339*35a5a358SJonathan Adams 340*35a5a358SJonathan Adams /* 341*35a5a358SJonathan Adams * Internal state 342*35a5a358SJonathan Adams */ 343*35a5a358SJonathan Adams static kmutex_t sysdc_pset_lock; /* lock protecting pset data */ 344*35a5a358SJonathan Adams static list_t sysdc_psets; /* list of psets with SDC threads */ 345*35a5a358SJonathan Adams static uint_t sysdc_param_init; /* sysdc_initparam() has been called */ 346*35a5a358SJonathan Adams static uint_t sysdc_update_timeout_started; /* update timeout is active */ 347*35a5a358SJonathan Adams static hrtime_t sysdc_last_update; /* time of last sysdc_update() */ 348*35a5a358SJonathan Adams static sysdc_t sysdc_dummy; /* used to terminate active lists */ 349*35a5a358SJonathan Adams 350*35a5a358SJonathan Adams /* 351*35a5a358SJonathan Adams * Internal state - active hash table 352*35a5a358SJonathan Adams */ 353*35a5a358SJonathan Adams #define SYSDC_NLISTS 8 354*35a5a358SJonathan Adams #define SYSDC_HASH(sdc) (((uintptr_t)(sdc) >> 6) & (SYSDC_NLISTS - 1)) 355*35a5a358SJonathan Adams static sysdc_list_t sysdc_active[SYSDC_NLISTS]; 356*35a5a358SJonathan Adams #define SYSDC_LIST(sdc) (&sysdc_active[SYSDC_HASH(sdc)]) 357*35a5a358SJonathan Adams 358*35a5a358SJonathan Adams #ifdef DEBUG 359*35a5a358SJonathan Adams static struct { 360*35a5a358SJonathan Adams uint64_t sysdc_update_times_asleep; 361*35a5a358SJonathan Adams uint64_t sysdc_update_times_base_ran_backwards; 362*35a5a358SJonathan Adams uint64_t sysdc_update_times_already_done; 363*35a5a358SJonathan Adams uint64_t sysdc_update_times_cur_ran_backwards; 364*35a5a358SJonathan Adams uint64_t sysdc_compute_pri_breaking; 365*35a5a358SJonathan Adams uint64_t sysdc_activate_enter; 366*35a5a358SJonathan Adams uint64_t sysdc_update_enter; 367*35a5a358SJonathan Adams uint64_t sysdc_update_exited; 368*35a5a358SJonathan Adams uint64_t sysdc_update_not_sdc; 369*35a5a358SJonathan Adams uint64_t sysdc_update_idle; 370*35a5a358SJonathan Adams uint64_t sysdc_update_take_break; 371*35a5a358SJonathan Adams uint64_t sysdc_update_no_psets; 372*35a5a358SJonathan Adams uint64_t sysdc_tick_not_sdc; 373*35a5a358SJonathan Adams uint64_t sysdc_tick_quantum_expired; 374*35a5a358SJonathan Adams uint64_t sysdc_thread_enter_enter; 375*35a5a358SJonathan Adams } sysdc_stats; 376*35a5a358SJonathan Adams 377*35a5a358SJonathan Adams #define SYSDC_INC_STAT(x) (sysdc_stats.x++) 378*35a5a358SJonathan Adams #else 379*35a5a358SJonathan Adams #define SYSDC_INC_STAT(x) ((void)0) 380*35a5a358SJonathan Adams #endif 381*35a5a358SJonathan Adams 382*35a5a358SJonathan Adams /* macros are UPPER CASE */ 383*35a5a358SJonathan Adams #define HOWMANY(a, b) howmany((a), (b)) 384*35a5a358SJonathan Adams #define MSECTOTICKS(a) HOWMANY((a) * 1000, usec_per_tick) 385*35a5a358SJonathan Adams 386*35a5a358SJonathan Adams static void 387*35a5a358SJonathan Adams sysdc_initparam(void) 388*35a5a358SJonathan Adams { 389*35a5a358SJonathan Adams uint_t sysdc_break_ticks; 390*35a5a358SJonathan Adams 391*35a5a358SJonathan Adams /* update / prune intervals */ 392*35a5a358SJonathan Adams sysdc_update_ticks = MSECTOTICKS(sysdc_update_interval_msec); 393*35a5a358SJonathan Adams 394*35a5a358SJonathan Adams sysdc_prune_updates = HOWMANY(sysdc_prune_interval_msec, 395*35a5a358SJonathan Adams sysdc_update_interval_msec); 396*35a5a358SJonathan Adams sysdc_reset_updates = HOWMANY(sysdc_reset_interval_msec, 397*35a5a358SJonathan Adams sysdc_update_interval_msec); 398*35a5a358SJonathan Adams 399*35a5a358SJonathan Adams /* We must get at least a little time on CPU. */ 400*35a5a358SJonathan Adams sysdc_minDC = 1; 401*35a5a358SJonathan Adams sysdc_maxDC = SYSDC_DC_MAX; 402*35a5a358SJonathan Adams sysdc_minpri = 0; 403*35a5a358SJonathan Adams sysdc_maxpri = maxclsyspri; 404*35a5a358SJonathan Adams 405*35a5a358SJonathan Adams /* break parameters */ 406*35a5a358SJonathan Adams if (sysdc_max_pset_DC > SYSDC_DC_MAX) { 407*35a5a358SJonathan Adams sysdc_max_pset_DC = SYSDC_DC_MAX; 408*35a5a358SJonathan Adams } 409*35a5a358SJonathan Adams sysdc_break_ticks = MSECTOTICKS(sysdc_break_msec); 410*35a5a358SJonathan Adams sysdc_break_updates = HOWMANY(sysdc_break_ticks, sysdc_update_ticks); 411*35a5a358SJonathan Adams 412*35a5a358SJonathan Adams /* 413*35a5a358SJonathan Adams * We want: 414*35a5a358SJonathan Adams * 415*35a5a358SJonathan Adams * sysdc_max_pset_DC = (nobreak / (break + nobreak)) 416*35a5a358SJonathan Adams * 417*35a5a358SJonathan Adams * ==> nobreak = sysdc_max_pset_DC * (break + nobreak) 418*35a5a358SJonathan Adams * 419*35a5a358SJonathan Adams * sysdc_max_pset_DC * break 420*35a5a358SJonathan Adams * ==> nobreak = ------------------------- 421*35a5a358SJonathan Adams * 1 - sysdc_max_pset_DC 422*35a5a358SJonathan Adams */ 423*35a5a358SJonathan Adams sysdc_nobreak_updates = 424*35a5a358SJonathan Adams HOWMANY((uint64_t)sysdc_break_updates * sysdc_max_pset_DC, 425*35a5a358SJonathan Adams (SYSDC_DC_MAX - sysdc_max_pset_DC)); 426*35a5a358SJonathan Adams 427*35a5a358SJonathan Adams sysdc_param_init = 1; 428*35a5a358SJonathan Adams } 429*35a5a358SJonathan Adams 430*35a5a358SJonathan Adams #undef HOWMANY 431*35a5a358SJonathan Adams #undef MSECTOTICKS 432*35a5a358SJonathan Adams 433*35a5a358SJonathan Adams #define SDC_UPDATE_INITIAL 0x1 /* for the initial update */ 434*35a5a358SJonathan Adams #define SDC_UPDATE_TIMEOUT 0x2 /* from sysdc_update() */ 435*35a5a358SJonathan Adams #define SDC_UPDATE_TICK 0x4 /* from sysdc_tick(), on expiry */ 436*35a5a358SJonathan Adams 437*35a5a358SJonathan Adams /* 438*35a5a358SJonathan Adams * Updates the recorded times in the sdc, and returns the elapsed ONPROC 439*35a5a358SJonathan Adams * and Runnable times since the last reset. 440*35a5a358SJonathan Adams * 441*35a5a358SJonathan Adams * newO is the thread's actual ONPROC time; it's used during sysdc_update() 442*35a5a358SJonathan Adams * to track processor set usage. 443*35a5a358SJonathan Adams */ 444*35a5a358SJonathan Adams static void 445*35a5a358SJonathan Adams sysdc_update_times(sysdc_t *sdc, uint_t flags, 446*35a5a358SJonathan Adams hrtime_t *O, hrtime_t *R, hrtime_t *newO) 447*35a5a358SJonathan Adams { 448*35a5a358SJonathan Adams kthread_t *const t = sdc->sdc_thread; 449*35a5a358SJonathan Adams const uint_t initial = (flags & SDC_UPDATE_INITIAL); 450*35a5a358SJonathan Adams const uint_t update = (flags & SDC_UPDATE_TIMEOUT); 451*35a5a358SJonathan Adams const clock_t now = ddi_get_lbolt(); 452*35a5a358SJonathan Adams uint_t do_reset; 453*35a5a358SJonathan Adams 454*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 455*35a5a358SJonathan Adams 456*35a5a358SJonathan Adams *O = *R = 0; 457*35a5a358SJonathan Adams 458*35a5a358SJonathan Adams /* If we've been sleeping, we know we haven't had any ONPROC time. */ 459*35a5a358SJonathan Adams if (sdc->sdc_sleep_updates != 0 && 460*35a5a358SJonathan Adams sdc->sdc_sleep_updates != sdc->sdc_nupdates) { 461*35a5a358SJonathan Adams *newO = sdc->sdc_last_base_O; 462*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_asleep); 463*35a5a358SJonathan Adams return; 464*35a5a358SJonathan Adams } 465*35a5a358SJonathan Adams 466*35a5a358SJonathan Adams /* 467*35a5a358SJonathan Adams * If this is our first update, or we've hit the reset point, 468*35a5a358SJonathan Adams * we need to reset our base_{O,R}. Once we've updated them, we 469*35a5a358SJonathan Adams * report O and R for the entire prior interval. 470*35a5a358SJonathan Adams */ 471*35a5a358SJonathan Adams do_reset = initial; 472*35a5a358SJonathan Adams if (update) { 473*35a5a358SJonathan Adams ++sdc->sdc_nupdates; 474*35a5a358SJonathan Adams if ((sdc->sdc_nupdates % sysdc_reset_updates) == 0) 475*35a5a358SJonathan Adams do_reset = 1; 476*35a5a358SJonathan Adams } 477*35a5a358SJonathan Adams if (do_reset) { 478*35a5a358SJonathan Adams hrtime_t baseO, baseR; 479*35a5a358SJonathan Adams if (initial) { 480*35a5a358SJonathan Adams /* 481*35a5a358SJonathan Adams * Start off our cycle count somewhere in the middle, 482*35a5a358SJonathan Adams * to keep the resets from all happening at once. 483*35a5a358SJonathan Adams * 484*35a5a358SJonathan Adams * 4999 is a handy prime much larger than 485*35a5a358SJonathan Adams * sysdc_reset_updates, so that we don't run into 486*35a5a358SJonathan Adams * trouble if the resolution is a multiple of 487*35a5a358SJonathan Adams * sysdc_reset_updates. 488*35a5a358SJonathan Adams */ 489*35a5a358SJonathan Adams sdc->sdc_nupdates = (uint_t)((gethrtime() % 4999) % 490*35a5a358SJonathan Adams sysdc_reset_updates); 491*35a5a358SJonathan Adams baseO = baseR = 0; 492*35a5a358SJonathan Adams } else { 493*35a5a358SJonathan Adams baseO = sdc->sdc_base_O; 494*35a5a358SJonathan Adams baseR = sdc->sdc_base_R; 495*35a5a358SJonathan Adams } 496*35a5a358SJonathan Adams 497*35a5a358SJonathan Adams mstate_systhread_times(t, &sdc->sdc_base_O, &sdc->sdc_base_R); 498*35a5a358SJonathan Adams *newO = sdc->sdc_base_O; 499*35a5a358SJonathan Adams 500*35a5a358SJonathan Adams sdc->sdc_reset = now; 501*35a5a358SJonathan Adams sdc->sdc_pri_check = -1; /* force mismatch below */ 502*35a5a358SJonathan Adams 503*35a5a358SJonathan Adams /* 504*35a5a358SJonathan Adams * See below for rationale. 505*35a5a358SJonathan Adams */ 506*35a5a358SJonathan Adams if (baseO > sdc->sdc_base_O || baseR > sdc->sdc_base_R) { 507*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_base_ran_backwards); 508*35a5a358SJonathan Adams baseO = sdc->sdc_base_O; 509*35a5a358SJonathan Adams baseR = sdc->sdc_base_R; 510*35a5a358SJonathan Adams } 511*35a5a358SJonathan Adams 512*35a5a358SJonathan Adams /* compute based on the entire interval */ 513*35a5a358SJonathan Adams *O = (sdc->sdc_base_O - baseO); 514*35a5a358SJonathan Adams *R = (sdc->sdc_base_R - baseR); 515*35a5a358SJonathan Adams return; 516*35a5a358SJonathan Adams } 517*35a5a358SJonathan Adams 518*35a5a358SJonathan Adams /* 519*35a5a358SJonathan Adams * If we're called from sysdc_update(), we *must* return a value 520*35a5a358SJonathan Adams * for newO, so we always call mstate_systhread_times(). 521*35a5a358SJonathan Adams * 522*35a5a358SJonathan Adams * Otherwise, if we've already done a pri check this tick, 523*35a5a358SJonathan Adams * we can skip it. 524*35a5a358SJonathan Adams */ 525*35a5a358SJonathan Adams if (!update && sdc->sdc_pri_check == now) { 526*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_already_done); 527*35a5a358SJonathan Adams return; 528*35a5a358SJonathan Adams } 529*35a5a358SJonathan Adams 530*35a5a358SJonathan Adams /* Get the current times from the thread */ 531*35a5a358SJonathan Adams sdc->sdc_pri_check = now; 532*35a5a358SJonathan Adams mstate_systhread_times(t, &sdc->sdc_cur_O, &sdc->sdc_cur_R); 533*35a5a358SJonathan Adams *newO = sdc->sdc_cur_O; 534*35a5a358SJonathan Adams 535*35a5a358SJonathan Adams /* 536*35a5a358SJonathan Adams * The updating of microstate accounting is not done under a 537*35a5a358SJonathan Adams * consistent set of locks, particularly the t_waitrq field. This 538*35a5a358SJonathan Adams * can lead to narrow windows in which we account for time in the 539*35a5a358SJonathan Adams * wrong bucket, which on the next read will be accounted for 540*35a5a358SJonathan Adams * correctly. 541*35a5a358SJonathan Adams * 542*35a5a358SJonathan Adams * If our sdc_base_* fields were affected by one of these blips, we 543*35a5a358SJonathan Adams * throw away the old data, and pretend this tick didn't happen. 544*35a5a358SJonathan Adams */ 545*35a5a358SJonathan Adams if (sdc->sdc_cur_O < sdc->sdc_base_O || 546*35a5a358SJonathan Adams sdc->sdc_cur_R < sdc->sdc_base_R) { 547*35a5a358SJonathan Adams 548*35a5a358SJonathan Adams sdc->sdc_base_O = sdc->sdc_cur_O; 549*35a5a358SJonathan Adams sdc->sdc_base_R = sdc->sdc_cur_R; 550*35a5a358SJonathan Adams 551*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_cur_ran_backwards); 552*35a5a358SJonathan Adams return; 553*35a5a358SJonathan Adams } 554*35a5a358SJonathan Adams 555*35a5a358SJonathan Adams *O = sdc->sdc_cur_O - sdc->sdc_base_O; 556*35a5a358SJonathan Adams *R = sdc->sdc_cur_R - sdc->sdc_base_R; 557*35a5a358SJonathan Adams } 558*35a5a358SJonathan Adams 559*35a5a358SJonathan Adams /* 560*35a5a358SJonathan Adams * sysdc_compute_pri() 561*35a5a358SJonathan Adams * 562*35a5a358SJonathan Adams * Recomputes the priority of the thread, leaving the result in 563*35a5a358SJonathan Adams * sdc->sdc_epri. Returns 1 if a priority update should occur 564*35a5a358SJonathan Adams * (which will also trigger a cpu_surrender()), otherwise 565*35a5a358SJonathan Adams * returns 0. 566*35a5a358SJonathan Adams */ 567*35a5a358SJonathan Adams static uint_t 568*35a5a358SJonathan Adams sysdc_compute_pri(sysdc_t *sdc, uint_t flags) 569*35a5a358SJonathan Adams { 570*35a5a358SJonathan Adams kthread_t *const t = sdc->sdc_thread; 571*35a5a358SJonathan Adams const uint_t update = (flags & SDC_UPDATE_TIMEOUT); 572*35a5a358SJonathan Adams const uint_t tick = (flags & SDC_UPDATE_TICK); 573*35a5a358SJonathan Adams 574*35a5a358SJonathan Adams hrtime_t O, R; 575*35a5a358SJonathan Adams hrtime_t newO = -1; 576*35a5a358SJonathan Adams 577*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 578*35a5a358SJonathan Adams 579*35a5a358SJonathan Adams sysdc_update_times(sdc, flags, &O, &R, &newO); 580*35a5a358SJonathan Adams ASSERT(!update || newO != -1); 581*35a5a358SJonathan Adams 582*35a5a358SJonathan Adams /* If we have new data, recompute our priority. */ 583*35a5a358SJonathan Adams if ((O + R) != 0) { 584*35a5a358SJonathan Adams sdc->sdc_cur_DC = (O * SYSDC_DC_MAX) / (O + R); 585*35a5a358SJonathan Adams 586*35a5a358SJonathan Adams /* Adjust our priority to move our DC closer to the target. */ 587*35a5a358SJonathan Adams if (sdc->sdc_cur_DC < sdc->sdc_target_DC) 588*35a5a358SJonathan Adams sdc->sdc_pri = sdc->sdc_maxpri; 589*35a5a358SJonathan Adams else 590*35a5a358SJonathan Adams sdc->sdc_pri = sdc->sdc_minpri; 591*35a5a358SJonathan Adams } 592*35a5a358SJonathan Adams 593*35a5a358SJonathan Adams /* 594*35a5a358SJonathan Adams * If our per-pset duty cycle goes over the max, we will take a break. 595*35a5a358SJonathan Adams * This forces all sysdc threads in the pset to minimum priority, in 596*35a5a358SJonathan Adams * order to let everyone else have a chance at the CPU. 597*35a5a358SJonathan Adams */ 598*35a5a358SJonathan Adams if (sdc->sdc_pset->sdp_need_break) { 599*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_compute_pri_breaking); 600*35a5a358SJonathan Adams sdc->sdc_epri = sdc->sdc_minpri; 601*35a5a358SJonathan Adams } else { 602*35a5a358SJonathan Adams sdc->sdc_epri = sdc->sdc_pri; 603*35a5a358SJonathan Adams } 604*35a5a358SJonathan Adams 605*35a5a358SJonathan Adams DTRACE_PROBE4(sysdc__compute__pri, 606*35a5a358SJonathan Adams kthread_t *, t, pri_t, sdc->sdc_epri, uint_t, sdc->sdc_cur_DC, 607*35a5a358SJonathan Adams uint_t, sdc->sdc_target_DC); 608*35a5a358SJonathan Adams 609*35a5a358SJonathan Adams /* 610*35a5a358SJonathan Adams * For sysdc_update(), we compute the ONPROC time for high-priority 611*35a5a358SJonathan Adams * threads, which is used to calculate the per-pset duty cycle. We 612*35a5a358SJonathan Adams * will always tell our callers to update the thread's priority, 613*35a5a358SJonathan Adams * since we want to force a cpu_surrender(). 614*35a5a358SJonathan Adams * 615*35a5a358SJonathan Adams * We reset sdc_update_ticks so that sysdc_tick() will only update 616*35a5a358SJonathan Adams * the thread's priority if our timeout is delayed by a tick or 617*35a5a358SJonathan Adams * more. 618*35a5a358SJonathan Adams */ 619*35a5a358SJonathan Adams if (update) { 620*35a5a358SJonathan Adams /* SDC threads are not allowed to change cpupart bindings. */ 621*35a5a358SJonathan Adams ASSERT(t->t_cpupart == sdc->sdc_pset->sdp_cpupart); 622*35a5a358SJonathan Adams 623*35a5a358SJonathan Adams /* If we were at MAXPRI, account for our onproc time. */ 624*35a5a358SJonathan Adams if (t->t_pri == sdc->sdc_maxpri && 625*35a5a358SJonathan Adams sdc->sdc_last_base_O != 0 && 626*35a5a358SJonathan Adams sdc->sdc_last_base_O < newO) { 627*35a5a358SJonathan Adams sdc->sdc_last_O = newO - sdc->sdc_last_base_O; 628*35a5a358SJonathan Adams sdc->sdc_pset->sdp_onproc_time += 629*35a5a358SJonathan Adams (uint64_t)sdc->sdc_last_O; 630*35a5a358SJonathan Adams sdc->sdc_pset->sdp_onproc_threads++; 631*35a5a358SJonathan Adams } else { 632*35a5a358SJonathan Adams sdc->sdc_last_O = 0; 633*35a5a358SJonathan Adams } 634*35a5a358SJonathan Adams sdc->sdc_last_base_O = newO; 635*35a5a358SJonathan Adams 636*35a5a358SJonathan Adams sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks + 1; 637*35a5a358SJonathan Adams return (1); 638*35a5a358SJonathan Adams } 639*35a5a358SJonathan Adams 640*35a5a358SJonathan Adams /* 641*35a5a358SJonathan Adams * Like sysdc_update(), sysdc_tick() always wants to update the 642*35a5a358SJonathan Adams * thread's priority, so that the CPU is surrendered if necessary. 643*35a5a358SJonathan Adams * We reset sdc_update_ticks so that if the timeout continues to be 644*35a5a358SJonathan Adams * delayed, we'll update at the regular interval. 645*35a5a358SJonathan Adams */ 646*35a5a358SJonathan Adams if (tick) { 647*35a5a358SJonathan Adams ASSERT(sdc->sdc_ticks == sdc->sdc_update_ticks); 648*35a5a358SJonathan Adams sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks; 649*35a5a358SJonathan Adams return (1); 650*35a5a358SJonathan Adams } 651*35a5a358SJonathan Adams 652*35a5a358SJonathan Adams /* 653*35a5a358SJonathan Adams * Otherwise, only tell our callers to update the priority if it has 654*35a5a358SJonathan Adams * changed. 655*35a5a358SJonathan Adams */ 656*35a5a358SJonathan Adams return (sdc->sdc_epri != t->t_pri); 657*35a5a358SJonathan Adams } 658*35a5a358SJonathan Adams 659*35a5a358SJonathan Adams static void 660*35a5a358SJonathan Adams sysdc_update_pri(sysdc_t *sdc, uint_t flags) 661*35a5a358SJonathan Adams { 662*35a5a358SJonathan Adams kthread_t *t = sdc->sdc_thread; 663*35a5a358SJonathan Adams 664*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 665*35a5a358SJonathan Adams 666*35a5a358SJonathan Adams if (sysdc_compute_pri(sdc, flags)) { 667*35a5a358SJonathan Adams if (!thread_change_pri(t, sdc->sdc_epri, 0)) { 668*35a5a358SJonathan Adams cpu_surrender(t); 669*35a5a358SJonathan Adams } 670*35a5a358SJonathan Adams } 671*35a5a358SJonathan Adams } 672*35a5a358SJonathan Adams 673*35a5a358SJonathan Adams /* 674*35a5a358SJonathan Adams * Add a thread onto the active list. It will only be removed by 675*35a5a358SJonathan Adams * sysdc_update(). 676*35a5a358SJonathan Adams */ 677*35a5a358SJonathan Adams static void 678*35a5a358SJonathan Adams sysdc_activate(sysdc_t *sdc) 679*35a5a358SJonathan Adams { 680*35a5a358SJonathan Adams sysdc_t *volatile *headp = &SYSDC_LIST(sdc)->sdl_list; 681*35a5a358SJonathan Adams sysdc_t *head; 682*35a5a358SJonathan Adams kthread_t *t = sdc->sdc_thread; 683*35a5a358SJonathan Adams 684*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_activate_enter); 685*35a5a358SJonathan Adams 686*35a5a358SJonathan Adams ASSERT(sdc->sdc_next == NULL); 687*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 688*35a5a358SJonathan Adams 689*35a5a358SJonathan Adams do { 690*35a5a358SJonathan Adams head = *headp; 691*35a5a358SJonathan Adams sdc->sdc_next = head; 692*35a5a358SJonathan Adams } while (atomic_cas_ptr(headp, head, sdc) != head); 693*35a5a358SJonathan Adams } 694*35a5a358SJonathan Adams 695*35a5a358SJonathan Adams /* 696*35a5a358SJonathan Adams * sysdc_update() has two jobs: 697*35a5a358SJonathan Adams * 698*35a5a358SJonathan Adams * 1. It updates the priorities of all active SDC threads on the system. 699*35a5a358SJonathan Adams * 2. It measures pset CPU usage and enforces sysdc_max_pset_DC. 700*35a5a358SJonathan Adams */ 701*35a5a358SJonathan Adams static void 702*35a5a358SJonathan Adams sysdc_update(void *arg) 703*35a5a358SJonathan Adams { 704*35a5a358SJonathan Adams int idx; 705*35a5a358SJonathan Adams sysdc_t *freelist = NULL; 706*35a5a358SJonathan Adams sysdc_pset_t *cur; 707*35a5a358SJonathan Adams hrtime_t now, diff; 708*35a5a358SJonathan Adams uint_t redeploy = 1; 709*35a5a358SJonathan Adams 710*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_enter); 711*35a5a358SJonathan Adams 712*35a5a358SJonathan Adams ASSERT(sysdc_update_timeout_started); 713*35a5a358SJonathan Adams 714*35a5a358SJonathan Adams /* 715*35a5a358SJonathan Adams * If this is our first time through, diff will be gigantic, and 716*35a5a358SJonathan Adams * no breaks will be necessary. 717*35a5a358SJonathan Adams */ 718*35a5a358SJonathan Adams now = gethrtime(); 719*35a5a358SJonathan Adams diff = now - sysdc_last_update; 720*35a5a358SJonathan Adams sysdc_last_update = now; 721*35a5a358SJonathan Adams 722*35a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 723*35a5a358SJonathan Adams for (cur = list_head(&sysdc_psets); cur != NULL; 724*35a5a358SJonathan Adams cur = list_next(&sysdc_psets, cur)) { 725*35a5a358SJonathan Adams boolean_t breaking = (cur->sdp_should_break != 0); 726*35a5a358SJonathan Adams 727*35a5a358SJonathan Adams if (cur->sdp_need_break != breaking) { 728*35a5a358SJonathan Adams DTRACE_PROBE2(sdc__pset__break, sysdc_pset_t *, cur, 729*35a5a358SJonathan Adams boolean_t, breaking); 730*35a5a358SJonathan Adams } 731*35a5a358SJonathan Adams cur->sdp_onproc_time = 0; 732*35a5a358SJonathan Adams cur->sdp_onproc_threads = 0; 733*35a5a358SJonathan Adams cur->sdp_need_break = breaking; 734*35a5a358SJonathan Adams } 735*35a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 736*35a5a358SJonathan Adams 737*35a5a358SJonathan Adams for (idx = 0; idx < SYSDC_NLISTS; idx++) { 738*35a5a358SJonathan Adams sysdc_list_t *sdl = &sysdc_active[idx]; 739*35a5a358SJonathan Adams sysdc_t *volatile *headp = &sdl->sdl_list; 740*35a5a358SJonathan Adams sysdc_t *head, *tail; 741*35a5a358SJonathan Adams sysdc_t **prevptr; 742*35a5a358SJonathan Adams 743*35a5a358SJonathan Adams if (*headp == &sysdc_dummy) 744*35a5a358SJonathan Adams continue; 745*35a5a358SJonathan Adams 746*35a5a358SJonathan Adams /* Prevent any threads from exiting while we're poking them. */ 747*35a5a358SJonathan Adams mutex_enter(&sdl->sdl_lock); 748*35a5a358SJonathan Adams 749*35a5a358SJonathan Adams /* 750*35a5a358SJonathan Adams * Each sdl_list contains a singly-linked list of active 751*35a5a358SJonathan Adams * threads. Threads which become active while we are 752*35a5a358SJonathan Adams * processing the list will be added to sdl_list. Since we 753*35a5a358SJonathan Adams * don't want that to interfere with our own processing, we 754*35a5a358SJonathan Adams * swap in an empty list. Any newly active threads will 755*35a5a358SJonathan Adams * go on to this empty list. When finished, we'll put any 756*35a5a358SJonathan Adams * such threads at the end of the processed list. 757*35a5a358SJonathan Adams */ 758*35a5a358SJonathan Adams head = atomic_swap_ptr(headp, &sysdc_dummy); 759*35a5a358SJonathan Adams prevptr = &head; 760*35a5a358SJonathan Adams while (*prevptr != &sysdc_dummy) { 761*35a5a358SJonathan Adams sysdc_t *const sdc = *prevptr; 762*35a5a358SJonathan Adams kthread_t *const t = sdc->sdc_thread; 763*35a5a358SJonathan Adams 764*35a5a358SJonathan Adams /* 765*35a5a358SJonathan Adams * If the thread has exited, move its sysdc_t onto 766*35a5a358SJonathan Adams * freelist, to be freed later. 767*35a5a358SJonathan Adams */ 768*35a5a358SJonathan Adams if (t == NULL) { 769*35a5a358SJonathan Adams *prevptr = sdc->sdc_next; 770*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_exited); 771*35a5a358SJonathan Adams sdc->sdc_next = freelist; 772*35a5a358SJonathan Adams freelist = sdc; 773*35a5a358SJonathan Adams continue; 774*35a5a358SJonathan Adams } 775*35a5a358SJonathan Adams 776*35a5a358SJonathan Adams thread_lock(t); 777*35a5a358SJonathan Adams if (t->t_cid != sysdccid) { 778*35a5a358SJonathan Adams thread_unlock(t); 779*35a5a358SJonathan Adams prevptr = &sdc->sdc_next; 780*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_not_sdc); 781*35a5a358SJonathan Adams continue; 782*35a5a358SJonathan Adams } 783*35a5a358SJonathan Adams ASSERT(t->t_cldata == sdc); 784*35a5a358SJonathan Adams 785*35a5a358SJonathan Adams /* 786*35a5a358SJonathan Adams * If the thread has been sleeping for longer 787*35a5a358SJonathan Adams * than sysdc_prune_interval, make it inactive by 788*35a5a358SJonathan Adams * removing it from the list. 789*35a5a358SJonathan Adams */ 790*35a5a358SJonathan Adams if (!(t->t_state & (TS_RUN | TS_ONPROC)) && 791*35a5a358SJonathan Adams sdc->sdc_sleep_updates != 0 && 792*35a5a358SJonathan Adams (sdc->sdc_sleep_updates - sdc->sdc_nupdates) > 793*35a5a358SJonathan Adams sysdc_prune_updates) { 794*35a5a358SJonathan Adams *prevptr = sdc->sdc_next; 795*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_idle); 796*35a5a358SJonathan Adams sdc->sdc_next = NULL; 797*35a5a358SJonathan Adams thread_unlock(t); 798*35a5a358SJonathan Adams continue; 799*35a5a358SJonathan Adams } 800*35a5a358SJonathan Adams sysdc_update_pri(sdc, SDC_UPDATE_TIMEOUT); 801*35a5a358SJonathan Adams thread_unlock(t); 802*35a5a358SJonathan Adams 803*35a5a358SJonathan Adams prevptr = &sdc->sdc_next; 804*35a5a358SJonathan Adams } 805*35a5a358SJonathan Adams 806*35a5a358SJonathan Adams /* 807*35a5a358SJonathan Adams * Add our list to the bucket, putting any new entries 808*35a5a358SJonathan Adams * added while we were working at the tail of the list. 809*35a5a358SJonathan Adams */ 810*35a5a358SJonathan Adams do { 811*35a5a358SJonathan Adams tail = *headp; 812*35a5a358SJonathan Adams *prevptr = tail; 813*35a5a358SJonathan Adams } while (atomic_cas_ptr(headp, tail, head) != tail); 814*35a5a358SJonathan Adams 815*35a5a358SJonathan Adams mutex_exit(&sdl->sdl_lock); 816*35a5a358SJonathan Adams } 817*35a5a358SJonathan Adams 818*35a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 819*35a5a358SJonathan Adams for (cur = list_head(&sysdc_psets); cur != NULL; 820*35a5a358SJonathan Adams cur = list_next(&sysdc_psets, cur)) { 821*35a5a358SJonathan Adams 822*35a5a358SJonathan Adams cur->sdp_vtime_last_interval = 823*35a5a358SJonathan Adams diff * cur->sdp_cpupart->cp_ncpus; 824*35a5a358SJonathan Adams cur->sdp_DC_last_interval = 825*35a5a358SJonathan Adams (cur->sdp_onproc_time * SYSDC_DC_MAX) / 826*35a5a358SJonathan Adams cur->sdp_vtime_last_interval; 827*35a5a358SJonathan Adams 828*35a5a358SJonathan Adams if (cur->sdp_should_break > 0) { 829*35a5a358SJonathan Adams cur->sdp_should_break--; /* breaking */ 830*35a5a358SJonathan Adams continue; 831*35a5a358SJonathan Adams } 832*35a5a358SJonathan Adams if (cur->sdp_dont_break > 0) { 833*35a5a358SJonathan Adams cur->sdp_dont_break--; /* waiting before checking */ 834*35a5a358SJonathan Adams continue; 835*35a5a358SJonathan Adams } 836*35a5a358SJonathan Adams if (cur->sdp_DC_last_interval > sysdc_max_pset_DC) { 837*35a5a358SJonathan Adams cur->sdp_should_break = sysdc_break_updates; 838*35a5a358SJonathan Adams cur->sdp_dont_break = sysdc_nobreak_updates; 839*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_take_break); 840*35a5a358SJonathan Adams } 841*35a5a358SJonathan Adams } 842*35a5a358SJonathan Adams 843*35a5a358SJonathan Adams /* 844*35a5a358SJonathan Adams * If there are no sysdc_psets, there can be no threads, so 845*35a5a358SJonathan Adams * we can stop doing our timeout. Since we're holding the 846*35a5a358SJonathan Adams * sysdc_pset_lock, no new sysdc_psets can come in, which will 847*35a5a358SJonathan Adams * prevent anyone from racing with this and dropping our timeout 848*35a5a358SJonathan Adams * on the floor. 849*35a5a358SJonathan Adams */ 850*35a5a358SJonathan Adams if (list_is_empty(&sysdc_psets)) { 851*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_no_psets); 852*35a5a358SJonathan Adams ASSERT(sysdc_update_timeout_started); 853*35a5a358SJonathan Adams sysdc_update_timeout_started = 0; 854*35a5a358SJonathan Adams 855*35a5a358SJonathan Adams redeploy = 0; 856*35a5a358SJonathan Adams } 857*35a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 858*35a5a358SJonathan Adams 859*35a5a358SJonathan Adams while (freelist != NULL) { 860*35a5a358SJonathan Adams sysdc_t *cur = freelist; 861*35a5a358SJonathan Adams freelist = cur->sdc_next; 862*35a5a358SJonathan Adams kmem_free(cur, sizeof (*cur)); 863*35a5a358SJonathan Adams } 864*35a5a358SJonathan Adams 865*35a5a358SJonathan Adams if (redeploy) { 866*35a5a358SJonathan Adams (void) timeout(sysdc_update, arg, sysdc_update_ticks); 867*35a5a358SJonathan Adams } 868*35a5a358SJonathan Adams } 869*35a5a358SJonathan Adams 870*35a5a358SJonathan Adams static void 871*35a5a358SJonathan Adams sysdc_preempt(kthread_t *t) 872*35a5a358SJonathan Adams { 873*35a5a358SJonathan Adams ASSERT(t == curthread); 874*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 875*35a5a358SJonathan Adams 876*35a5a358SJonathan Adams setbackdq(t); /* give others a chance to run */ 877*35a5a358SJonathan Adams } 878*35a5a358SJonathan Adams 879*35a5a358SJonathan Adams static void 880*35a5a358SJonathan Adams sysdc_tick(kthread_t *t) 881*35a5a358SJonathan Adams { 882*35a5a358SJonathan Adams sysdc_t *sdc; 883*35a5a358SJonathan Adams 884*35a5a358SJonathan Adams thread_lock(t); 885*35a5a358SJonathan Adams if (t->t_cid != sysdccid) { 886*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_tick_not_sdc); 887*35a5a358SJonathan Adams thread_unlock(t); 888*35a5a358SJonathan Adams return; 889*35a5a358SJonathan Adams } 890*35a5a358SJonathan Adams sdc = t->t_cldata; 891*35a5a358SJonathan Adams if (t->t_state == TS_ONPROC && 892*35a5a358SJonathan Adams t->t_pri < t->t_disp_queue->disp_maxrunpri) { 893*35a5a358SJonathan Adams cpu_surrender(t); 894*35a5a358SJonathan Adams } 895*35a5a358SJonathan Adams 896*35a5a358SJonathan Adams if (t->t_state == TS_ONPROC || t->t_state == TS_RUN) { 897*35a5a358SJonathan Adams ASSERT(sdc->sdc_sleep_updates == 0); 898*35a5a358SJonathan Adams } 899*35a5a358SJonathan Adams 900*35a5a358SJonathan Adams ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks); 901*35a5a358SJonathan Adams sdc->sdc_ticks++; 902*35a5a358SJonathan Adams if (sdc->sdc_ticks == sdc->sdc_update_ticks) { 903*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_tick_quantum_expired); 904*35a5a358SJonathan Adams sysdc_update_pri(sdc, SDC_UPDATE_TICK); 905*35a5a358SJonathan Adams ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks); 906*35a5a358SJonathan Adams } 907*35a5a358SJonathan Adams thread_unlock(t); 908*35a5a358SJonathan Adams } 909*35a5a358SJonathan Adams 910*35a5a358SJonathan Adams static void 911*35a5a358SJonathan Adams sysdc_setrun(kthread_t *t) 912*35a5a358SJonathan Adams { 913*35a5a358SJonathan Adams sysdc_t *sdc = t->t_cldata; 914*35a5a358SJonathan Adams 915*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); /* t should be in transition */ 916*35a5a358SJonathan Adams 917*35a5a358SJonathan Adams sdc->sdc_sleep_updates = 0; 918*35a5a358SJonathan Adams 919*35a5a358SJonathan Adams if (sdc->sdc_next == NULL) { 920*35a5a358SJonathan Adams /* 921*35a5a358SJonathan Adams * Since we're in transition, we don't want to use the 922*35a5a358SJonathan Adams * full thread_update_pri(). 923*35a5a358SJonathan Adams */ 924*35a5a358SJonathan Adams if (sysdc_compute_pri(sdc, 0)) { 925*35a5a358SJonathan Adams THREAD_CHANGE_PRI(t, sdc->sdc_epri); 926*35a5a358SJonathan Adams } 927*35a5a358SJonathan Adams sysdc_activate(sdc); 928*35a5a358SJonathan Adams 929*35a5a358SJonathan Adams ASSERT(sdc->sdc_next != NULL); 930*35a5a358SJonathan Adams } 931*35a5a358SJonathan Adams 932*35a5a358SJonathan Adams setbackdq(t); 933*35a5a358SJonathan Adams } 934*35a5a358SJonathan Adams 935*35a5a358SJonathan Adams static void 936*35a5a358SJonathan Adams sysdc_wakeup(kthread_t *t) 937*35a5a358SJonathan Adams { 938*35a5a358SJonathan Adams sysdc_setrun(t); 939*35a5a358SJonathan Adams } 940*35a5a358SJonathan Adams 941*35a5a358SJonathan Adams static void 942*35a5a358SJonathan Adams sysdc_sleep(kthread_t *t) 943*35a5a358SJonathan Adams { 944*35a5a358SJonathan Adams sysdc_t *sdc = t->t_cldata; 945*35a5a358SJonathan Adams 946*35a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); /* t should be in transition */ 947*35a5a358SJonathan Adams 948*35a5a358SJonathan Adams sdc->sdc_sleep_updates = sdc->sdc_nupdates; 949*35a5a358SJonathan Adams } 950*35a5a358SJonathan Adams 951*35a5a358SJonathan Adams /*ARGSUSED*/ 952*35a5a358SJonathan Adams static int 953*35a5a358SJonathan Adams sysdc_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp, 954*35a5a358SJonathan Adams void *bufp) 955*35a5a358SJonathan Adams { 956*35a5a358SJonathan Adams cpupart_t *const cpupart = t->t_cpupart; 957*35a5a358SJonathan Adams sysdc_t *sdc = bufp; 958*35a5a358SJonathan Adams sysdc_params_t *sdpp = parmsp; 959*35a5a358SJonathan Adams sysdc_pset_t *newpset = sdc->sdc_pset; 960*35a5a358SJonathan Adams sysdc_pset_t *pset; 961*35a5a358SJonathan Adams int start_timeout; 962*35a5a358SJonathan Adams 963*35a5a358SJonathan Adams if (t->t_cid != syscid) 964*35a5a358SJonathan Adams return (EPERM); 965*35a5a358SJonathan Adams 966*35a5a358SJonathan Adams ASSERT(ttolwp(t) != NULL); 967*35a5a358SJonathan Adams ASSERT(sdpp != NULL); 968*35a5a358SJonathan Adams ASSERT(newpset != NULL); 969*35a5a358SJonathan Adams ASSERT(sysdc_param_init); 970*35a5a358SJonathan Adams 971*35a5a358SJonathan Adams ASSERT(sdpp->sdp_minpri >= sysdc_minpri); 972*35a5a358SJonathan Adams ASSERT(sdpp->sdp_maxpri <= sysdc_maxpri); 973*35a5a358SJonathan Adams ASSERT(sdpp->sdp_DC >= sysdc_minDC); 974*35a5a358SJonathan Adams ASSERT(sdpp->sdp_DC <= sysdc_maxDC); 975*35a5a358SJonathan Adams 976*35a5a358SJonathan Adams sdc->sdc_thread = t; 977*35a5a358SJonathan Adams sdc->sdc_pri = sdpp->sdp_maxpri; /* start off maximally */ 978*35a5a358SJonathan Adams sdc->sdc_minpri = sdpp->sdp_minpri; 979*35a5a358SJonathan Adams sdc->sdc_maxpri = sdpp->sdp_maxpri; 980*35a5a358SJonathan Adams sdc->sdc_target_DC = sdpp->sdp_DC; 981*35a5a358SJonathan Adams sdc->sdc_ticks = 0; 982*35a5a358SJonathan Adams sdc->sdc_update_ticks = sysdc_update_ticks + 1; 983*35a5a358SJonathan Adams 984*35a5a358SJonathan Adams /* Assign ourselves to the appropriate pset. */ 985*35a5a358SJonathan Adams sdc->sdc_pset = NULL; 986*35a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 987*35a5a358SJonathan Adams for (pset = list_head(&sysdc_psets); pset != NULL; 988*35a5a358SJonathan Adams pset = list_next(&sysdc_psets, pset)) { 989*35a5a358SJonathan Adams if (pset->sdp_cpupart == cpupart) { 990*35a5a358SJonathan Adams break; 991*35a5a358SJonathan Adams } 992*35a5a358SJonathan Adams } 993*35a5a358SJonathan Adams if (pset == NULL) { 994*35a5a358SJonathan Adams pset = newpset; 995*35a5a358SJonathan Adams newpset = NULL; 996*35a5a358SJonathan Adams pset->sdp_cpupart = cpupart; 997*35a5a358SJonathan Adams list_insert_tail(&sysdc_psets, pset); 998*35a5a358SJonathan Adams } 999*35a5a358SJonathan Adams pset->sdp_nthreads++; 1000*35a5a358SJonathan Adams ASSERT(pset->sdp_nthreads > 0); 1001*35a5a358SJonathan Adams 1002*35a5a358SJonathan Adams sdc->sdc_pset = pset; 1003*35a5a358SJonathan Adams 1004*35a5a358SJonathan Adams start_timeout = (sysdc_update_timeout_started == 0); 1005*35a5a358SJonathan Adams sysdc_update_timeout_started = 1; 1006*35a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 1007*35a5a358SJonathan Adams 1008*35a5a358SJonathan Adams if (newpset != NULL) 1009*35a5a358SJonathan Adams kmem_free(newpset, sizeof (*newpset)); 1010*35a5a358SJonathan Adams 1011*35a5a358SJonathan Adams /* Update t's scheduling class and priority. */ 1012*35a5a358SJonathan Adams thread_lock(t); 1013*35a5a358SJonathan Adams t->t_clfuncs = &(sclass[cid].cl_funcs->thread); 1014*35a5a358SJonathan Adams t->t_cid = cid; 1015*35a5a358SJonathan Adams t->t_cldata = sdc; 1016*35a5a358SJonathan Adams t->t_schedflag |= TS_RUNQMATCH; 1017*35a5a358SJonathan Adams 1018*35a5a358SJonathan Adams sysdc_update_pri(sdc, SDC_UPDATE_INITIAL); 1019*35a5a358SJonathan Adams thread_unlock(t); 1020*35a5a358SJonathan Adams 1021*35a5a358SJonathan Adams /* Kick off the thread timeout if we're the first one in. */ 1022*35a5a358SJonathan Adams if (start_timeout) { 1023*35a5a358SJonathan Adams (void) timeout(sysdc_update, NULL, sysdc_update_ticks); 1024*35a5a358SJonathan Adams } 1025*35a5a358SJonathan Adams 1026*35a5a358SJonathan Adams return (0); 1027*35a5a358SJonathan Adams } 1028*35a5a358SJonathan Adams 1029*35a5a358SJonathan Adams static void 1030*35a5a358SJonathan Adams sysdc_leave(sysdc_t *sdc) 1031*35a5a358SJonathan Adams { 1032*35a5a358SJonathan Adams sysdc_pset_t *sdp = sdc->sdc_pset; 1033*35a5a358SJonathan Adams sysdc_list_t *sdl = SYSDC_LIST(sdc); 1034*35a5a358SJonathan Adams uint_t freedc; 1035*35a5a358SJonathan Adams 1036*35a5a358SJonathan Adams mutex_enter(&sdl->sdl_lock); /* block sysdc_update() */ 1037*35a5a358SJonathan Adams sdc->sdc_thread = NULL; 1038*35a5a358SJonathan Adams freedc = (sdc->sdc_next == NULL); 1039*35a5a358SJonathan Adams mutex_exit(&sdl->sdl_lock); 1040*35a5a358SJonathan Adams 1041*35a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 1042*35a5a358SJonathan Adams sdp = sdc->sdc_pset; 1043*35a5a358SJonathan Adams ASSERT(sdp != NULL); 1044*35a5a358SJonathan Adams ASSERT(sdp->sdp_nthreads > 0); 1045*35a5a358SJonathan Adams --sdp->sdp_nthreads; 1046*35a5a358SJonathan Adams if (sdp->sdp_nthreads == 0) { 1047*35a5a358SJonathan Adams list_remove(&sysdc_psets, sdp); 1048*35a5a358SJonathan Adams } else { 1049*35a5a358SJonathan Adams sdp = NULL; 1050*35a5a358SJonathan Adams } 1051*35a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 1052*35a5a358SJonathan Adams 1053*35a5a358SJonathan Adams if (freedc) 1054*35a5a358SJonathan Adams kmem_free(sdc, sizeof (*sdc)); 1055*35a5a358SJonathan Adams if (sdp != NULL) 1056*35a5a358SJonathan Adams kmem_free(sdp, sizeof (*sdp)); 1057*35a5a358SJonathan Adams } 1058*35a5a358SJonathan Adams 1059*35a5a358SJonathan Adams static void 1060*35a5a358SJonathan Adams sysdc_exitclass(void *buf) 1061*35a5a358SJonathan Adams { 1062*35a5a358SJonathan Adams sysdc_leave((sysdc_t *)buf); 1063*35a5a358SJonathan Adams } 1064*35a5a358SJonathan Adams 1065*35a5a358SJonathan Adams /*ARGSUSED*/ 1066*35a5a358SJonathan Adams static int 1067*35a5a358SJonathan Adams sysdc_canexit(kthread_t *t, cred_t *reqpcredp) 1068*35a5a358SJonathan Adams { 1069*35a5a358SJonathan Adams /* Threads cannot exit SDC once joined, except in a body bag. */ 1070*35a5a358SJonathan Adams return (EPERM); 1071*35a5a358SJonathan Adams } 1072*35a5a358SJonathan Adams 1073*35a5a358SJonathan Adams static void 1074*35a5a358SJonathan Adams sysdc_exit(kthread_t *t) 1075*35a5a358SJonathan Adams { 1076*35a5a358SJonathan Adams sysdc_t *sdc; 1077*35a5a358SJonathan Adams 1078*35a5a358SJonathan Adams /* We're exiting, so we just rejoin the SYS class. */ 1079*35a5a358SJonathan Adams thread_lock(t); 1080*35a5a358SJonathan Adams ASSERT(t->t_cid == sysdccid); 1081*35a5a358SJonathan Adams sdc = t->t_cldata; 1082*35a5a358SJonathan Adams t->t_cid = syscid; 1083*35a5a358SJonathan Adams t->t_cldata = NULL; 1084*35a5a358SJonathan Adams t->t_clfuncs = &(sclass[syscid].cl_funcs->thread); 1085*35a5a358SJonathan Adams (void) thread_change_pri(t, maxclsyspri, 0); 1086*35a5a358SJonathan Adams t->t_schedflag &= ~TS_RUNQMATCH; 1087*35a5a358SJonathan Adams thread_unlock_nopreempt(t); 1088*35a5a358SJonathan Adams 1089*35a5a358SJonathan Adams /* Unlink the sdc from everything. */ 1090*35a5a358SJonathan Adams sysdc_leave(sdc); 1091*35a5a358SJonathan Adams } 1092*35a5a358SJonathan Adams 1093*35a5a358SJonathan Adams /*ARGSUSED*/ 1094*35a5a358SJonathan Adams static int 1095*35a5a358SJonathan Adams sysdc_fork(kthread_t *t, kthread_t *ct, void *bufp) 1096*35a5a358SJonathan Adams { 1097*35a5a358SJonathan Adams /* 1098*35a5a358SJonathan Adams * Threads cannot be created with SDC as their class; they must 1099*35a5a358SJonathan Adams * be created as SYS and then added with sysdc_thread_enter(). 1100*35a5a358SJonathan Adams * Because of this restriction, sysdc_fork() should never be called. 1101*35a5a358SJonathan Adams */ 1102*35a5a358SJonathan Adams panic("sysdc cannot be forked"); 1103*35a5a358SJonathan Adams 1104*35a5a358SJonathan Adams return (ENOSYS); 1105*35a5a358SJonathan Adams } 1106*35a5a358SJonathan Adams 1107*35a5a358SJonathan Adams /*ARGSUSED*/ 1108*35a5a358SJonathan Adams static void 1109*35a5a358SJonathan Adams sysdc_forkret(kthread_t *t, kthread_t *ct) 1110*35a5a358SJonathan Adams { 1111*35a5a358SJonathan Adams /* SDC threads are part of system processes, which never fork. */ 1112*35a5a358SJonathan Adams panic("sysdc cannot be forked"); 1113*35a5a358SJonathan Adams } 1114*35a5a358SJonathan Adams 1115*35a5a358SJonathan Adams static pri_t 1116*35a5a358SJonathan Adams sysdc_globpri(kthread_t *t) 1117*35a5a358SJonathan Adams { 1118*35a5a358SJonathan Adams return (t->t_epri); 1119*35a5a358SJonathan Adams } 1120*35a5a358SJonathan Adams 1121*35a5a358SJonathan Adams /*ARGSUSED*/ 1122*35a5a358SJonathan Adams static pri_t 1123*35a5a358SJonathan Adams sysdc_no_swap(kthread_t *t, int flags) 1124*35a5a358SJonathan Adams { 1125*35a5a358SJonathan Adams /* SDC threads cannot be swapped. */ 1126*35a5a358SJonathan Adams return (-1); 1127*35a5a358SJonathan Adams } 1128*35a5a358SJonathan Adams 1129*35a5a358SJonathan Adams /* 1130*35a5a358SJonathan Adams * Get maximum and minimum priorities enjoyed by SDC threads. 1131*35a5a358SJonathan Adams */ 1132*35a5a358SJonathan Adams static int 1133*35a5a358SJonathan Adams sysdc_getclpri(pcpri_t *pcprip) 1134*35a5a358SJonathan Adams { 1135*35a5a358SJonathan Adams pcprip->pc_clpmax = sysdc_maxpri; 1136*35a5a358SJonathan Adams pcprip->pc_clpmin = sysdc_minpri; 1137*35a5a358SJonathan Adams return (0); 1138*35a5a358SJonathan Adams } 1139*35a5a358SJonathan Adams 1140*35a5a358SJonathan Adams /*ARGSUSED*/ 1141*35a5a358SJonathan Adams static int 1142*35a5a358SJonathan Adams sysdc_getclinfo(void *arg) 1143*35a5a358SJonathan Adams { 1144*35a5a358SJonathan Adams return (0); /* no class-specific info */ 1145*35a5a358SJonathan Adams } 1146*35a5a358SJonathan Adams 1147*35a5a358SJonathan Adams /*ARGSUSED*/ 1148*35a5a358SJonathan Adams static int 1149*35a5a358SJonathan Adams sysdc_alloc(void **p, int flag) 1150*35a5a358SJonathan Adams { 1151*35a5a358SJonathan Adams sysdc_t *new; 1152*35a5a358SJonathan Adams 1153*35a5a358SJonathan Adams *p = NULL; 1154*35a5a358SJonathan Adams if ((new = kmem_zalloc(sizeof (*new), flag)) == NULL) { 1155*35a5a358SJonathan Adams return (ENOMEM); 1156*35a5a358SJonathan Adams } 1157*35a5a358SJonathan Adams if ((new->sdc_pset = kmem_zalloc(sizeof (*new->sdc_pset), flag)) == 1158*35a5a358SJonathan Adams NULL) { 1159*35a5a358SJonathan Adams kmem_free(new, sizeof (*new)); 1160*35a5a358SJonathan Adams return (ENOMEM); 1161*35a5a358SJonathan Adams } 1162*35a5a358SJonathan Adams *p = new; 1163*35a5a358SJonathan Adams return (0); 1164*35a5a358SJonathan Adams } 1165*35a5a358SJonathan Adams 1166*35a5a358SJonathan Adams static void 1167*35a5a358SJonathan Adams sysdc_free(void *p) 1168*35a5a358SJonathan Adams { 1169*35a5a358SJonathan Adams sysdc_t *sdc = p; 1170*35a5a358SJonathan Adams 1171*35a5a358SJonathan Adams if (sdc != NULL) { 1172*35a5a358SJonathan Adams /* 1173*35a5a358SJonathan Adams * We must have failed CL_ENTERCLASS(), so our pset should be 1174*35a5a358SJonathan Adams * there and unused. 1175*35a5a358SJonathan Adams */ 1176*35a5a358SJonathan Adams ASSERT(sdc->sdc_pset != NULL); 1177*35a5a358SJonathan Adams ASSERT(sdc->sdc_pset->sdp_cpupart == NULL); 1178*35a5a358SJonathan Adams kmem_free(sdc->sdc_pset, sizeof (*sdc->sdc_pset)); 1179*35a5a358SJonathan Adams kmem_free(sdc, sizeof (*sdc)); 1180*35a5a358SJonathan Adams } 1181*35a5a358SJonathan Adams } 1182*35a5a358SJonathan Adams 1183*35a5a358SJonathan Adams static int sysdc_enosys(); /* Boy, ANSI-C's K&R compatibility is weird. */ 1184*35a5a358SJonathan Adams static int sysdc_einval(); 1185*35a5a358SJonathan Adams static void sysdc_nullsys(); 1186*35a5a358SJonathan Adams 1187*35a5a358SJonathan Adams static struct classfuncs sysdc_classfuncs = { 1188*35a5a358SJonathan Adams /* messages to class manager */ 1189*35a5a358SJonathan Adams { 1190*35a5a358SJonathan Adams sysdc_enosys, /* admin */ 1191*35a5a358SJonathan Adams sysdc_getclinfo, 1192*35a5a358SJonathan Adams sysdc_enosys, /* parmsin */ 1193*35a5a358SJonathan Adams sysdc_enosys, /* parmsout */ 1194*35a5a358SJonathan Adams sysdc_enosys, /* vaparmsin */ 1195*35a5a358SJonathan Adams sysdc_enosys, /* vaparmsout */ 1196*35a5a358SJonathan Adams sysdc_getclpri, 1197*35a5a358SJonathan Adams sysdc_alloc, 1198*35a5a358SJonathan Adams sysdc_free, 1199*35a5a358SJonathan Adams }, 1200*35a5a358SJonathan Adams /* operations on threads */ 1201*35a5a358SJonathan Adams { 1202*35a5a358SJonathan Adams sysdc_enterclass, 1203*35a5a358SJonathan Adams sysdc_exitclass, 1204*35a5a358SJonathan Adams sysdc_canexit, 1205*35a5a358SJonathan Adams sysdc_fork, 1206*35a5a358SJonathan Adams sysdc_forkret, 1207*35a5a358SJonathan Adams sysdc_nullsys, /* parmsget */ 1208*35a5a358SJonathan Adams sysdc_enosys, /* parmsset */ 1209*35a5a358SJonathan Adams sysdc_nullsys, /* stop */ 1210*35a5a358SJonathan Adams sysdc_exit, 1211*35a5a358SJonathan Adams sysdc_nullsys, /* active */ 1212*35a5a358SJonathan Adams sysdc_nullsys, /* inactive */ 1213*35a5a358SJonathan Adams sysdc_no_swap, /* swapin */ 1214*35a5a358SJonathan Adams sysdc_no_swap, /* swapout */ 1215*35a5a358SJonathan Adams sysdc_nullsys, /* trapret */ 1216*35a5a358SJonathan Adams sysdc_preempt, 1217*35a5a358SJonathan Adams sysdc_setrun, 1218*35a5a358SJonathan Adams sysdc_sleep, 1219*35a5a358SJonathan Adams sysdc_tick, 1220*35a5a358SJonathan Adams sysdc_wakeup, 1221*35a5a358SJonathan Adams sysdc_einval, /* donice */ 1222*35a5a358SJonathan Adams sysdc_globpri, 1223*35a5a358SJonathan Adams sysdc_nullsys, /* set_process_group */ 1224*35a5a358SJonathan Adams sysdc_nullsys, /* yield */ 1225*35a5a358SJonathan Adams sysdc_einval, /* doprio */ 1226*35a5a358SJonathan Adams } 1227*35a5a358SJonathan Adams }; 1228*35a5a358SJonathan Adams 1229*35a5a358SJonathan Adams static int 1230*35a5a358SJonathan Adams sysdc_enosys() 1231*35a5a358SJonathan Adams { 1232*35a5a358SJonathan Adams return (ENOSYS); 1233*35a5a358SJonathan Adams } 1234*35a5a358SJonathan Adams 1235*35a5a358SJonathan Adams static int 1236*35a5a358SJonathan Adams sysdc_einval() 1237*35a5a358SJonathan Adams { 1238*35a5a358SJonathan Adams return (EINVAL); 1239*35a5a358SJonathan Adams } 1240*35a5a358SJonathan Adams 1241*35a5a358SJonathan Adams static void 1242*35a5a358SJonathan Adams sysdc_nullsys() 1243*35a5a358SJonathan Adams { 1244*35a5a358SJonathan Adams } 1245*35a5a358SJonathan Adams 1246*35a5a358SJonathan Adams /*ARGSUSED*/ 1247*35a5a358SJonathan Adams static pri_t 1248*35a5a358SJonathan Adams sysdc_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp) 1249*35a5a358SJonathan Adams { 1250*35a5a358SJonathan Adams int idx; 1251*35a5a358SJonathan Adams 1252*35a5a358SJonathan Adams list_create(&sysdc_psets, sizeof (sysdc_pset_t), 1253*35a5a358SJonathan Adams offsetof(sysdc_pset_t, sdp_node)); 1254*35a5a358SJonathan Adams 1255*35a5a358SJonathan Adams for (idx = 0; idx < SYSDC_NLISTS; idx++) { 1256*35a5a358SJonathan Adams sysdc_active[idx].sdl_list = &sysdc_dummy; 1257*35a5a358SJonathan Adams } 1258*35a5a358SJonathan Adams 1259*35a5a358SJonathan Adams sysdc_initparam(); 1260*35a5a358SJonathan Adams 1261*35a5a358SJonathan Adams sysdccid = cid; 1262*35a5a358SJonathan Adams *clfuncspp = &sysdc_classfuncs; 1263*35a5a358SJonathan Adams 1264*35a5a358SJonathan Adams return ((pri_t)v.v_maxsyspri); 1265*35a5a358SJonathan Adams } 1266*35a5a358SJonathan Adams 1267*35a5a358SJonathan Adams static struct sclass csw = { 1268*35a5a358SJonathan Adams "SDC", 1269*35a5a358SJonathan Adams sysdc_init, 1270*35a5a358SJonathan Adams 0 1271*35a5a358SJonathan Adams }; 1272*35a5a358SJonathan Adams 1273*35a5a358SJonathan Adams static struct modlsched modlsched = { 1274*35a5a358SJonathan Adams &mod_schedops, "system duty cycle scheduling class", &csw 1275*35a5a358SJonathan Adams }; 1276*35a5a358SJonathan Adams 1277*35a5a358SJonathan Adams static struct modlinkage modlinkage = { 1278*35a5a358SJonathan Adams MODREV_1, (void *)&modlsched, NULL 1279*35a5a358SJonathan Adams }; 1280*35a5a358SJonathan Adams 1281*35a5a358SJonathan Adams int 1282*35a5a358SJonathan Adams _init() 1283*35a5a358SJonathan Adams { 1284*35a5a358SJonathan Adams return (mod_install(&modlinkage)); 1285*35a5a358SJonathan Adams } 1286*35a5a358SJonathan Adams 1287*35a5a358SJonathan Adams int 1288*35a5a358SJonathan Adams _fini() 1289*35a5a358SJonathan Adams { 1290*35a5a358SJonathan Adams return (EBUSY); /* can't unload for now */ 1291*35a5a358SJonathan Adams } 1292*35a5a358SJonathan Adams 1293*35a5a358SJonathan Adams int 1294*35a5a358SJonathan Adams _info(struct modinfo *modinfop) 1295*35a5a358SJonathan Adams { 1296*35a5a358SJonathan Adams return (mod_info(&modlinkage, modinfop)); 1297*35a5a358SJonathan Adams } 1298*35a5a358SJonathan Adams 1299*35a5a358SJonathan Adams /* --- consolidation-private interfaces --- */ 1300*35a5a358SJonathan Adams void 1301*35a5a358SJonathan Adams sysdc_thread_enter(kthread_t *t, uint_t dc, uint_t flags) 1302*35a5a358SJonathan Adams { 1303*35a5a358SJonathan Adams void *buf = NULL; 1304*35a5a358SJonathan Adams sysdc_params_t sdp; 1305*35a5a358SJonathan Adams 1306*35a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_thread_enter_enter); 1307*35a5a358SJonathan Adams 1308*35a5a358SJonathan Adams ASSERT(sysdc_param_init); 1309*35a5a358SJonathan Adams ASSERT(sysdccid >= 0); 1310*35a5a358SJonathan Adams 1311*35a5a358SJonathan Adams ASSERT((flags & ~SYSDC_THREAD_BATCH) == 0); 1312*35a5a358SJonathan Adams 1313*35a5a358SJonathan Adams sdp.sdp_minpri = sysdc_minpri; 1314*35a5a358SJonathan Adams sdp.sdp_maxpri = sysdc_maxpri; 1315*35a5a358SJonathan Adams sdp.sdp_DC = MAX(MIN(dc, sysdc_maxDC), sysdc_minDC); 1316*35a5a358SJonathan Adams 1317*35a5a358SJonathan Adams if (flags & SYSDC_THREAD_BATCH) 1318*35a5a358SJonathan Adams sdp.sdp_maxpri -= sysdc_batch_niceness; 1319*35a5a358SJonathan Adams 1320*35a5a358SJonathan Adams VERIFY3U(CL_ALLOC(&buf, sysdccid, KM_SLEEP), ==, 0); 1321*35a5a358SJonathan Adams 1322*35a5a358SJonathan Adams ASSERT(t->t_lwp != NULL); 1323*35a5a358SJonathan Adams ASSERT(t->t_cid == syscid); 1324*35a5a358SJonathan Adams ASSERT(t->t_cldata == NULL); 1325*35a5a358SJonathan Adams VERIFY3U(CL_CANEXIT(t, NULL), ==, 0); 1326*35a5a358SJonathan Adams VERIFY3U(CL_ENTERCLASS(t, sysdccid, &sdp, kcred, buf), ==, 0); 1327*35a5a358SJonathan Adams CL_EXITCLASS(syscid, NULL); 1328*35a5a358SJonathan Adams } 1329