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