135a5a358SJonathan Adams /* 235a5a358SJonathan Adams * CDDL HEADER START 335a5a358SJonathan Adams * 435a5a358SJonathan Adams * The contents of this file are subject to the terms of the 535a5a358SJonathan Adams * Common Development and Distribution License (the "License"). 635a5a358SJonathan Adams * You may not use this file except in compliance with the License. 735a5a358SJonathan Adams * 835a5a358SJonathan Adams * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 935a5a358SJonathan Adams * or http://www.opensolaris.org/os/licensing. 1035a5a358SJonathan Adams * See the License for the specific language governing permissions 1135a5a358SJonathan Adams * and limitations under the License. 1235a5a358SJonathan Adams * 1335a5a358SJonathan Adams * When distributing Covered Code, include this CDDL HEADER in each 1435a5a358SJonathan Adams * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1535a5a358SJonathan Adams * If applicable, add the following below this CDDL HEADER, with the 1635a5a358SJonathan Adams * fields enclosed by brackets "[]" replaced with your own identifying 1735a5a358SJonathan Adams * information: Portions Copyright [yyyy] [name of copyright owner] 1835a5a358SJonathan Adams * 1935a5a358SJonathan Adams * CDDL HEADER END 2035a5a358SJonathan Adams */ 2135a5a358SJonathan Adams /* 22647ea195SJonathan Adams * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 23*47bb2664SMatthew Ahrens * Copyright (c) 2012, 2015 by Delphix. All rights reserved. 2435a5a358SJonathan Adams */ 2535a5a358SJonathan Adams 2635a5a358SJonathan Adams /* 2735a5a358SJonathan Adams * The System Duty Cycle (SDC) scheduling class 2835a5a358SJonathan Adams * -------------------------------------------- 2935a5a358SJonathan Adams * 3035a5a358SJonathan Adams * Background 3135a5a358SJonathan Adams * 3235a5a358SJonathan Adams * Kernel threads in Solaris have traditionally not been large consumers 3335a5a358SJonathan Adams * of CPU time. They typically wake up, perform a small amount of 3435a5a358SJonathan Adams * work, then go back to sleep waiting for either a timeout or another 3535a5a358SJonathan Adams * signal. On the assumption that the small amount of work that they do 3635a5a358SJonathan Adams * is important for the behavior of the whole system, these threads are 3735a5a358SJonathan Adams * treated kindly by the dispatcher and the SYS scheduling class: they run 3835a5a358SJonathan Adams * without preemption from anything other than real-time and interrupt 3935a5a358SJonathan Adams * threads; when preempted, they are put at the front of the queue, so they 4035a5a358SJonathan Adams * generally do not migrate between CPUs; and they are allowed to stay 4135a5a358SJonathan Adams * running until they voluntarily give up the CPU. 4235a5a358SJonathan Adams * 4335a5a358SJonathan Adams * As Solaris has evolved, new workloads have emerged which require the 4435a5a358SJonathan Adams * kernel to perform significant amounts of CPU-intensive work. One 4535a5a358SJonathan Adams * example of such a workload is ZFS's transaction group sync processing. 4635a5a358SJonathan Adams * Each sync operation generates a large batch of I/Os, and each I/O 4735a5a358SJonathan Adams * may need to be compressed and/or checksummed before it is written to 4835a5a358SJonathan Adams * storage. The taskq threads which perform the compression and checksums 4935a5a358SJonathan Adams * will run nonstop as long as they have work to do; a large sync operation 5035a5a358SJonathan Adams * on a compression-heavy dataset can keep them busy for seconds on end. 5135a5a358SJonathan Adams * This causes human-time-scale dispatch latency bubbles for any other 5235a5a358SJonathan Adams * threads which have the misfortune to share a CPU with the taskq threads. 5335a5a358SJonathan Adams * 5435a5a358SJonathan Adams * The SDC scheduling class is a solution to this problem. 5535a5a358SJonathan Adams * 5635a5a358SJonathan Adams * 5735a5a358SJonathan Adams * Overview 5835a5a358SJonathan Adams * 5935a5a358SJonathan Adams * SDC is centered around the concept of a thread's duty cycle (DC): 6035a5a358SJonathan Adams * 6135a5a358SJonathan Adams * ONPROC time 6235a5a358SJonathan Adams * Duty Cycle = ---------------------- 6335a5a358SJonathan Adams * ONPROC + Runnable time 6435a5a358SJonathan Adams * 6535a5a358SJonathan Adams * This is the ratio of the time that the thread spent running on a CPU 6635a5a358SJonathan Adams * divided by the time it spent running or trying to run. It is unaffected 6735a5a358SJonathan Adams * by any time the thread spent sleeping, stopped, etc. 6835a5a358SJonathan Adams * 6935a5a358SJonathan Adams * A thread joining the SDC class specifies a "target" DC that it wants 7035a5a358SJonathan Adams * to run at. To implement this policy, the routine sysdc_update() scans 7135a5a358SJonathan Adams * the list of active SDC threads every few ticks and uses each thread's 7235a5a358SJonathan Adams * microstate data to compute the actual duty cycle that that thread 7335a5a358SJonathan Adams * has experienced recently. If the thread is under its target DC, its 7435a5a358SJonathan Adams * priority is increased to the maximum available (sysdc_maxpri, which is 7535a5a358SJonathan Adams * 99 by default). If the thread is over its target DC, its priority is 7635a5a358SJonathan Adams * reduced to the minimum available (sysdc_minpri, 0 by default). This 7735a5a358SJonathan Adams * is a fairly primitive approach, in that it doesn't use any of the 7835a5a358SJonathan Adams * intermediate priorities, but it's not completely inappropriate. Even 7935a5a358SJonathan Adams * though threads in the SDC class might take a while to do their job, they 8035a5a358SJonathan Adams * are by some definition important if they're running inside the kernel, 8135a5a358SJonathan Adams * so it is reasonable that they should get to run at priority 99. 8235a5a358SJonathan Adams * 8335a5a358SJonathan Adams * If a thread is running when sysdc_update() calculates its actual duty 8435a5a358SJonathan Adams * cycle, and there are other threads of equal or greater priority on its 8535a5a358SJonathan Adams * CPU's dispatch queue, sysdc_update() preempts that thread. The thread 8635a5a358SJonathan Adams * acknowledges the preemption by calling sysdc_preempt(), which calls 8735a5a358SJonathan Adams * setbackdq(), which gives other threads with the same priority a chance 8835a5a358SJonathan Adams * to run. This creates a de facto time quantum for threads in the SDC 8935a5a358SJonathan Adams * scheduling class. 9035a5a358SJonathan Adams * 9135a5a358SJonathan Adams * An SDC thread which is assigned priority 0 can continue to run if 9235a5a358SJonathan Adams * nothing else needs to use the CPU that it's running on. Similarly, an 9335a5a358SJonathan Adams * SDC thread at priority 99 might not get to run as much as it wants to 9435a5a358SJonathan Adams * if there are other priority-99 or higher threads on its CPU. These 9535a5a358SJonathan Adams * situations would cause the thread to get ahead of or behind its target 9635a5a358SJonathan Adams * DC; the longer the situations lasted, the further ahead or behind the 9735a5a358SJonathan Adams * thread would get. Rather than condemning a thread to a lifetime of 9835a5a358SJonathan Adams * paying for its youthful indiscretions, SDC keeps "base" values for 9935a5a358SJonathan Adams * ONPROC and Runnable times in each thread's sysdc data, and updates these 10035a5a358SJonathan Adams * values periodically. The duty cycle is then computed using the elapsed 10135a5a358SJonathan Adams * amount of ONPROC and Runnable times since those base times. 10235a5a358SJonathan Adams * 10335a5a358SJonathan Adams * Since sysdc_update() scans SDC threads fairly frequently, it tries to 10435a5a358SJonathan Adams * keep the list of "active" threads small by pruning out threads which 10535a5a358SJonathan Adams * have been asleep for a brief time. They are not pruned immediately upon 10635a5a358SJonathan Adams * going to sleep, since some threads may bounce back and forth between 10735a5a358SJonathan Adams * sleeping and being runnable. 10835a5a358SJonathan Adams * 10935a5a358SJonathan Adams * 11035a5a358SJonathan Adams * Interfaces 11135a5a358SJonathan Adams * 11235a5a358SJonathan Adams * void sysdc_thread_enter(t, dc, flags) 11335a5a358SJonathan Adams * 11435a5a358SJonathan Adams * Moves a kernel thread from the SYS scheduling class to the 11535a5a358SJonathan Adams * SDC class. t must have an associated LWP (created by calling 11635a5a358SJonathan Adams * lwp_kernel_create()). The thread will have a target DC of dc. 11735a5a358SJonathan Adams * Flags should be either 0 or SYSDC_THREAD_BATCH. If 118aab2fe41SJonathan Adams * SYSDC_THREAD_BATCH is specified, the thread is expected to be 119aab2fe41SJonathan Adams * doing large amounts of processing. 12035a5a358SJonathan Adams * 12135a5a358SJonathan Adams * 12235a5a358SJonathan Adams * Complications 12335a5a358SJonathan Adams * 12435a5a358SJonathan Adams * - Run queue balancing 12535a5a358SJonathan Adams * 12635a5a358SJonathan Adams * The Solaris dispatcher is biased towards letting a thread run 12735a5a358SJonathan Adams * on the same CPU which it last ran on, if no more than 3 ticks 12835a5a358SJonathan Adams * (i.e. rechoose_interval) have passed since the thread last ran. 12935a5a358SJonathan Adams * This helps to preserve cache warmth. On the other hand, it also 13035a5a358SJonathan Adams * tries to keep the per-CPU run queues fairly balanced; if the CPU 13135a5a358SJonathan Adams * chosen for a runnable thread has a run queue which is three or 13235a5a358SJonathan Adams * more threads longer than a neighboring CPU's queue, the runnable 13335a5a358SJonathan Adams * thread is dispatched onto the neighboring CPU instead. 13435a5a358SJonathan Adams * 13535a5a358SJonathan Adams * These policies work well for some workloads, but not for many SDC 13635a5a358SJonathan Adams * threads. The taskq client of SDC, for example, has many discrete 13735a5a358SJonathan Adams * units of work to do. The work units are largely independent, so 13835a5a358SJonathan Adams * cache warmth is not an important consideration. It is important 13935a5a358SJonathan Adams * that the threads fan out quickly to different CPUs, since the 14035a5a358SJonathan Adams * amount of work these threads have to do (a few seconds worth at a 14135a5a358SJonathan Adams * time) doesn't leave much time to correct thread placement errors 14235a5a358SJonathan Adams * (i.e. two SDC threads being dispatched to the same CPU). 14335a5a358SJonathan Adams * 14435a5a358SJonathan Adams * To fix this, SDC uses the TS_RUNQMATCH flag introduced for FSS. 14535a5a358SJonathan Adams * This tells the dispatcher to keep neighboring run queues' lengths 14635a5a358SJonathan Adams * more evenly matched, which allows SDC threads to migrate more 14735a5a358SJonathan Adams * easily. 14835a5a358SJonathan Adams * 14935a5a358SJonathan Adams * - LWPs and system processes 15035a5a358SJonathan Adams * 15135a5a358SJonathan Adams * SDC can only be used for kernel threads. Since SDC uses microstate 15235a5a358SJonathan Adams * accounting data to compute each thread's actual duty cycle, all 15335a5a358SJonathan Adams * threads entering the SDC class must have associated LWPs (which 15435a5a358SJonathan Adams * store the microstate data). This means that the threads have to 15535a5a358SJonathan Adams * be associated with an SSYS process, i.e. one created by newproc(). 15635a5a358SJonathan Adams * If the microstate accounting information is ever moved into the 15735a5a358SJonathan Adams * kthread_t, this restriction could be lifted. 15835a5a358SJonathan Adams * 15935a5a358SJonathan Adams * - Dealing with oversubscription 16035a5a358SJonathan Adams * 16135a5a358SJonathan Adams * Since SDC duty cycles are per-thread, it is possible that the 16235a5a358SJonathan Adams * aggregate requested duty cycle of all SDC threads in a processor 16335a5a358SJonathan Adams * set could be greater than the total CPU time available in that set. 16435a5a358SJonathan Adams * The FSS scheduling class has an analogous situation, which it deals 16535a5a358SJonathan Adams * with by reducing each thread's allotted CPU time proportionally. 16635a5a358SJonathan Adams * Since SDC doesn't need to be as precise as FSS, it uses a simpler 16735a5a358SJonathan Adams * solution to the oversubscription problem. 16835a5a358SJonathan Adams * 16935a5a358SJonathan Adams * sysdc_update() accumulates the amount of time that max-priority SDC 17035a5a358SJonathan Adams * threads have spent on-CPU in each processor set, and uses that sum 17135a5a358SJonathan Adams * to create an implied duty cycle for that processor set: 17235a5a358SJonathan Adams * 17335a5a358SJonathan Adams * accumulated CPU time 17435a5a358SJonathan Adams * pset DC = ----------------------------------- 17535a5a358SJonathan Adams * (# CPUs) * time since last update 17635a5a358SJonathan Adams * 17735a5a358SJonathan Adams * If this implied duty cycle is above a maximum pset duty cycle (90% 17835a5a358SJonathan Adams * by default), sysdc_update() sets the priority of all SDC threads 17935a5a358SJonathan Adams * in that processor set to sysdc_minpri for a "break" period. After 18035a5a358SJonathan Adams * the break period, it waits for a "nobreak" period before trying to 18135a5a358SJonathan Adams * enforce the pset duty cycle limit again. 18235a5a358SJonathan Adams * 18335a5a358SJonathan Adams * - Processor sets 18435a5a358SJonathan Adams * 18535a5a358SJonathan Adams * As the above implies, SDC is processor set aware, but it does not 18635a5a358SJonathan Adams * currently allow threads to change processor sets while in the SDC 18735a5a358SJonathan Adams * class. Instead, those threads must join the desired processor set 18835a5a358SJonathan Adams * before entering SDC. [1] 18935a5a358SJonathan Adams * 19035a5a358SJonathan Adams * - Batch threads 19135a5a358SJonathan Adams * 19235a5a358SJonathan Adams * A thread joining the SDC class can specify the SDC_THREAD_BATCH 193aab2fe41SJonathan Adams * flag. This flag currently has no effect, but marks threads which 194aab2fe41SJonathan Adams * do bulk processing. 19535a5a358SJonathan Adams * 19635a5a358SJonathan Adams * - Why not FSS? 19735a5a358SJonathan Adams * 19835a5a358SJonathan Adams * It might seem that the existing FSS scheduling class could solve 19935a5a358SJonathan Adams * the problems that SDC is attempting to solve. FSS's more precise 20035a5a358SJonathan Adams * solution to the oversubscription problem would hardly cause 20135a5a358SJonathan Adams * trouble, as long as it performed well. SDC is implemented as 20235a5a358SJonathan Adams * a separate scheduling class for two main reasons: the initial 20335a5a358SJonathan Adams * consumer of SDC does not map well onto the "project" abstraction 20435a5a358SJonathan Adams * that is central to FSS, and FSS does not expect to run at kernel 20535a5a358SJonathan Adams * priorities. 20635a5a358SJonathan Adams * 20735a5a358SJonathan Adams * 20835a5a358SJonathan Adams * Tunables 20935a5a358SJonathan Adams * 21035a5a358SJonathan Adams * - sysdc_update_interval_msec: Number of milliseconds between 21135a5a358SJonathan Adams * consecutive thread priority updates. 21235a5a358SJonathan Adams * 21335a5a358SJonathan Adams * - sysdc_reset_interval_msec: Number of milliseconds between 21435a5a358SJonathan Adams * consecutive resets of a thread's base ONPROC and Runnable 21535a5a358SJonathan Adams * times. 21635a5a358SJonathan Adams * 21735a5a358SJonathan Adams * - sysdc_prune_interval_msec: Number of milliseconds of sleeping 21835a5a358SJonathan Adams * before a thread is pruned from the active list. 21935a5a358SJonathan Adams * 22035a5a358SJonathan Adams * - sysdc_max_pset_DC: Allowable percentage of a processor set's 22135a5a358SJonathan Adams * CPU time which SDC can give to its high-priority threads. 22235a5a358SJonathan Adams * 22335a5a358SJonathan Adams * - sysdc_break_msec: Number of milliseconds of "break" taken when 22435a5a358SJonathan Adams * sysdc_max_pset_DC is exceeded. 22535a5a358SJonathan Adams * 22635a5a358SJonathan Adams * 22735a5a358SJonathan Adams * Future work (in SDC and related subsystems) 22835a5a358SJonathan Adams * 22935a5a358SJonathan Adams * - Per-thread rechoose interval (0 for SDC) 23035a5a358SJonathan Adams * 23135a5a358SJonathan Adams * Allow each thread to specify its own rechoose interval. SDC 23235a5a358SJonathan Adams * threads would specify an interval of zero, which would rechoose 23335a5a358SJonathan Adams * the CPU with the lowest priority once per update. 23435a5a358SJonathan Adams * 23535a5a358SJonathan Adams * - Allow threads to change processor sets after joining the SDC class 23635a5a358SJonathan Adams * 23735a5a358SJonathan Adams * - Thread groups and per-group DC 23835a5a358SJonathan Adams * 23935a5a358SJonathan Adams * It might be nice to be able to specify a duty cycle which applies 24035a5a358SJonathan Adams * to a group of threads in aggregate. 24135a5a358SJonathan Adams * 24235a5a358SJonathan Adams * - Per-group DC callback to allow dynamic DC tuning 24335a5a358SJonathan Adams * 24435a5a358SJonathan Adams * Currently, DCs are assigned when the thread joins SDC. Some 24535a5a358SJonathan Adams * workloads could benefit from being able to tune their DC using 24635a5a358SJonathan Adams * subsystem-specific knowledge about the workload. 24735a5a358SJonathan Adams * 24835a5a358SJonathan Adams * - Finer-grained priority updates 24935a5a358SJonathan Adams * 25035a5a358SJonathan Adams * - More nuanced management of oversubscription 25135a5a358SJonathan Adams * 25235a5a358SJonathan Adams * - Moving other CPU-intensive threads into SDC 25335a5a358SJonathan Adams * 25435a5a358SJonathan Adams * - Move msacct data into kthread_t 25535a5a358SJonathan Adams * 25635a5a358SJonathan Adams * This would allow kernel threads without LWPs to join SDC. 25735a5a358SJonathan Adams * 25835a5a358SJonathan Adams * 25935a5a358SJonathan Adams * Footnotes 26035a5a358SJonathan Adams * 26135a5a358SJonathan Adams * [1] The details of doing so are left as an exercise for the reader. 26235a5a358SJonathan Adams */ 26335a5a358SJonathan Adams 26435a5a358SJonathan Adams #include <sys/types.h> 26535a5a358SJonathan Adams #include <sys/sysdc.h> 26635a5a358SJonathan Adams #include <sys/sysdc_impl.h> 26735a5a358SJonathan Adams 26835a5a358SJonathan Adams #include <sys/class.h> 26935a5a358SJonathan Adams #include <sys/cmn_err.h> 27035a5a358SJonathan Adams #include <sys/cpuvar.h> 27135a5a358SJonathan Adams #include <sys/cpupart.h> 27235a5a358SJonathan Adams #include <sys/debug.h> 27335a5a358SJonathan Adams #include <sys/disp.h> 27435a5a358SJonathan Adams #include <sys/errno.h> 27535a5a358SJonathan Adams #include <sys/inline.h> 27635a5a358SJonathan Adams #include <sys/kmem.h> 27735a5a358SJonathan Adams #include <sys/modctl.h> 27835a5a358SJonathan Adams #include <sys/schedctl.h> 27935a5a358SJonathan Adams #include <sys/sdt.h> 28035a5a358SJonathan Adams #include <sys/sunddi.h> 28135a5a358SJonathan Adams #include <sys/sysmacros.h> 28235a5a358SJonathan Adams #include <sys/systm.h> 28335a5a358SJonathan Adams #include <sys/var.h> 28435a5a358SJonathan Adams 28535a5a358SJonathan Adams /* 28635a5a358SJonathan Adams * Tunables - loaded into the internal state at module load time 28735a5a358SJonathan Adams */ 28835a5a358SJonathan Adams uint_t sysdc_update_interval_msec = 20; 28935a5a358SJonathan Adams uint_t sysdc_reset_interval_msec = 400; 29035a5a358SJonathan Adams uint_t sysdc_prune_interval_msec = 100; 29135a5a358SJonathan Adams uint_t sysdc_max_pset_DC = 90; 29235a5a358SJonathan Adams uint_t sysdc_break_msec = 80; 29335a5a358SJonathan Adams 29435a5a358SJonathan Adams /* 29535a5a358SJonathan Adams * Internal state - constants set up by sysdc_initparam() 29635a5a358SJonathan Adams */ 29735a5a358SJonathan Adams static clock_t sysdc_update_ticks; /* ticks between updates */ 29835a5a358SJonathan Adams static uint_t sysdc_prune_updates; /* updates asleep before pruning */ 29935a5a358SJonathan Adams static uint_t sysdc_reset_updates; /* # of updates before reset */ 30035a5a358SJonathan Adams static uint_t sysdc_break_updates; /* updates to break */ 30135a5a358SJonathan Adams static uint_t sysdc_nobreak_updates; /* updates to not check */ 30235a5a358SJonathan Adams static uint_t sysdc_minDC; /* minimum allowed DC */ 30335a5a358SJonathan Adams static uint_t sysdc_maxDC; /* maximum allowed DC */ 30435a5a358SJonathan Adams static pri_t sysdc_minpri; /* minimum allowed priority */ 30535a5a358SJonathan Adams static pri_t sysdc_maxpri; /* maximum allowed priority */ 30635a5a358SJonathan Adams 30735a5a358SJonathan Adams /* 30835a5a358SJonathan Adams * Internal state 30935a5a358SJonathan Adams */ 31035a5a358SJonathan Adams static kmutex_t sysdc_pset_lock; /* lock protecting pset data */ 31135a5a358SJonathan Adams static list_t sysdc_psets; /* list of psets with SDC threads */ 31235a5a358SJonathan Adams static uint_t sysdc_param_init; /* sysdc_initparam() has been called */ 31335a5a358SJonathan Adams static uint_t sysdc_update_timeout_started; /* update timeout is active */ 31435a5a358SJonathan Adams static hrtime_t sysdc_last_update; /* time of last sysdc_update() */ 31535a5a358SJonathan Adams static sysdc_t sysdc_dummy; /* used to terminate active lists */ 31635a5a358SJonathan Adams 31735a5a358SJonathan Adams /* 31835a5a358SJonathan Adams * Internal state - active hash table 31935a5a358SJonathan Adams */ 32035a5a358SJonathan Adams #define SYSDC_NLISTS 8 32135a5a358SJonathan Adams #define SYSDC_HASH(sdc) (((uintptr_t)(sdc) >> 6) & (SYSDC_NLISTS - 1)) 32235a5a358SJonathan Adams static sysdc_list_t sysdc_active[SYSDC_NLISTS]; 32335a5a358SJonathan Adams #define SYSDC_LIST(sdc) (&sysdc_active[SYSDC_HASH(sdc)]) 32435a5a358SJonathan Adams 32535a5a358SJonathan Adams #ifdef DEBUG 32635a5a358SJonathan Adams static struct { 32735a5a358SJonathan Adams uint64_t sysdc_update_times_asleep; 32835a5a358SJonathan Adams uint64_t sysdc_update_times_base_ran_backwards; 32935a5a358SJonathan Adams uint64_t sysdc_update_times_already_done; 33035a5a358SJonathan Adams uint64_t sysdc_update_times_cur_ran_backwards; 33135a5a358SJonathan Adams uint64_t sysdc_compute_pri_breaking; 33235a5a358SJonathan Adams uint64_t sysdc_activate_enter; 33335a5a358SJonathan Adams uint64_t sysdc_update_enter; 33435a5a358SJonathan Adams uint64_t sysdc_update_exited; 33535a5a358SJonathan Adams uint64_t sysdc_update_not_sdc; 33635a5a358SJonathan Adams uint64_t sysdc_update_idle; 33735a5a358SJonathan Adams uint64_t sysdc_update_take_break; 33835a5a358SJonathan Adams uint64_t sysdc_update_no_psets; 33935a5a358SJonathan Adams uint64_t sysdc_tick_not_sdc; 34035a5a358SJonathan Adams uint64_t sysdc_tick_quantum_expired; 34135a5a358SJonathan Adams uint64_t sysdc_thread_enter_enter; 34235a5a358SJonathan Adams } sysdc_stats; 34335a5a358SJonathan Adams 34435a5a358SJonathan Adams #define SYSDC_INC_STAT(x) (sysdc_stats.x++) 34535a5a358SJonathan Adams #else 34635a5a358SJonathan Adams #define SYSDC_INC_STAT(x) ((void)0) 34735a5a358SJonathan Adams #endif 34835a5a358SJonathan Adams 34935a5a358SJonathan Adams /* macros are UPPER CASE */ 35035a5a358SJonathan Adams #define HOWMANY(a, b) howmany((a), (b)) 35135a5a358SJonathan Adams #define MSECTOTICKS(a) HOWMANY((a) * 1000, usec_per_tick) 35235a5a358SJonathan Adams 35335a5a358SJonathan Adams static void 35435a5a358SJonathan Adams sysdc_initparam(void) 35535a5a358SJonathan Adams { 35635a5a358SJonathan Adams uint_t sysdc_break_ticks; 35735a5a358SJonathan Adams 35835a5a358SJonathan Adams /* update / prune intervals */ 35935a5a358SJonathan Adams sysdc_update_ticks = MSECTOTICKS(sysdc_update_interval_msec); 36035a5a358SJonathan Adams 36135a5a358SJonathan Adams sysdc_prune_updates = HOWMANY(sysdc_prune_interval_msec, 36235a5a358SJonathan Adams sysdc_update_interval_msec); 36335a5a358SJonathan Adams sysdc_reset_updates = HOWMANY(sysdc_reset_interval_msec, 36435a5a358SJonathan Adams sysdc_update_interval_msec); 36535a5a358SJonathan Adams 36635a5a358SJonathan Adams /* We must get at least a little time on CPU. */ 36735a5a358SJonathan Adams sysdc_minDC = 1; 36835a5a358SJonathan Adams sysdc_maxDC = SYSDC_DC_MAX; 36935a5a358SJonathan Adams sysdc_minpri = 0; 370*47bb2664SMatthew Ahrens sysdc_maxpri = maxclsyspri - 1; 37135a5a358SJonathan Adams 37235a5a358SJonathan Adams /* break parameters */ 37335a5a358SJonathan Adams if (sysdc_max_pset_DC > SYSDC_DC_MAX) { 37435a5a358SJonathan Adams sysdc_max_pset_DC = SYSDC_DC_MAX; 37535a5a358SJonathan Adams } 37635a5a358SJonathan Adams sysdc_break_ticks = MSECTOTICKS(sysdc_break_msec); 37735a5a358SJonathan Adams sysdc_break_updates = HOWMANY(sysdc_break_ticks, sysdc_update_ticks); 37835a5a358SJonathan Adams 37935a5a358SJonathan Adams /* 38035a5a358SJonathan Adams * We want: 38135a5a358SJonathan Adams * 38235a5a358SJonathan Adams * sysdc_max_pset_DC = (nobreak / (break + nobreak)) 38335a5a358SJonathan Adams * 38435a5a358SJonathan Adams * ==> nobreak = sysdc_max_pset_DC * (break + nobreak) 38535a5a358SJonathan Adams * 38635a5a358SJonathan Adams * sysdc_max_pset_DC * break 38735a5a358SJonathan Adams * ==> nobreak = ------------------------- 38835a5a358SJonathan Adams * 1 - sysdc_max_pset_DC 38935a5a358SJonathan Adams */ 39035a5a358SJonathan Adams sysdc_nobreak_updates = 39135a5a358SJonathan Adams HOWMANY((uint64_t)sysdc_break_updates * sysdc_max_pset_DC, 39235a5a358SJonathan Adams (SYSDC_DC_MAX - sysdc_max_pset_DC)); 39335a5a358SJonathan Adams 39435a5a358SJonathan Adams sysdc_param_init = 1; 39535a5a358SJonathan Adams } 39635a5a358SJonathan Adams 39735a5a358SJonathan Adams #undef HOWMANY 39835a5a358SJonathan Adams #undef MSECTOTICKS 39935a5a358SJonathan Adams 40035a5a358SJonathan Adams #define SDC_UPDATE_INITIAL 0x1 /* for the initial update */ 40135a5a358SJonathan Adams #define SDC_UPDATE_TIMEOUT 0x2 /* from sysdc_update() */ 40235a5a358SJonathan Adams #define SDC_UPDATE_TICK 0x4 /* from sysdc_tick(), on expiry */ 40335a5a358SJonathan Adams 40435a5a358SJonathan Adams /* 40535a5a358SJonathan Adams * Updates the recorded times in the sdc, and returns the elapsed ONPROC 40635a5a358SJonathan Adams * and Runnable times since the last reset. 40735a5a358SJonathan Adams * 40835a5a358SJonathan Adams * newO is the thread's actual ONPROC time; it's used during sysdc_update() 40935a5a358SJonathan Adams * to track processor set usage. 41035a5a358SJonathan Adams */ 41135a5a358SJonathan Adams static void 41235a5a358SJonathan Adams sysdc_update_times(sysdc_t *sdc, uint_t flags, 41335a5a358SJonathan Adams hrtime_t *O, hrtime_t *R, hrtime_t *newO) 41435a5a358SJonathan Adams { 41535a5a358SJonathan Adams kthread_t *const t = sdc->sdc_thread; 41635a5a358SJonathan Adams const uint_t initial = (flags & SDC_UPDATE_INITIAL); 41735a5a358SJonathan Adams const uint_t update = (flags & SDC_UPDATE_TIMEOUT); 41835a5a358SJonathan Adams const clock_t now = ddi_get_lbolt(); 41935a5a358SJonathan Adams uint_t do_reset; 42035a5a358SJonathan Adams 42135a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 42235a5a358SJonathan Adams 42335a5a358SJonathan Adams *O = *R = 0; 42435a5a358SJonathan Adams 42535a5a358SJonathan Adams /* If we've been sleeping, we know we haven't had any ONPROC time. */ 42635a5a358SJonathan Adams if (sdc->sdc_sleep_updates != 0 && 42735a5a358SJonathan Adams sdc->sdc_sleep_updates != sdc->sdc_nupdates) { 42835a5a358SJonathan Adams *newO = sdc->sdc_last_base_O; 42935a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_asleep); 43035a5a358SJonathan Adams return; 43135a5a358SJonathan Adams } 43235a5a358SJonathan Adams 43335a5a358SJonathan Adams /* 43435a5a358SJonathan Adams * If this is our first update, or we've hit the reset point, 43535a5a358SJonathan Adams * we need to reset our base_{O,R}. Once we've updated them, we 43635a5a358SJonathan Adams * report O and R for the entire prior interval. 43735a5a358SJonathan Adams */ 43835a5a358SJonathan Adams do_reset = initial; 43935a5a358SJonathan Adams if (update) { 44035a5a358SJonathan Adams ++sdc->sdc_nupdates; 44135a5a358SJonathan Adams if ((sdc->sdc_nupdates % sysdc_reset_updates) == 0) 44235a5a358SJonathan Adams do_reset = 1; 44335a5a358SJonathan Adams } 44435a5a358SJonathan Adams if (do_reset) { 44535a5a358SJonathan Adams hrtime_t baseO, baseR; 44635a5a358SJonathan Adams if (initial) { 44735a5a358SJonathan Adams /* 44835a5a358SJonathan Adams * Start off our cycle count somewhere in the middle, 44935a5a358SJonathan Adams * to keep the resets from all happening at once. 45035a5a358SJonathan Adams * 45135a5a358SJonathan Adams * 4999 is a handy prime much larger than 45235a5a358SJonathan Adams * sysdc_reset_updates, so that we don't run into 45335a5a358SJonathan Adams * trouble if the resolution is a multiple of 45435a5a358SJonathan Adams * sysdc_reset_updates. 45535a5a358SJonathan Adams */ 45635a5a358SJonathan Adams sdc->sdc_nupdates = (uint_t)((gethrtime() % 4999) % 45735a5a358SJonathan Adams sysdc_reset_updates); 45835a5a358SJonathan Adams baseO = baseR = 0; 45935a5a358SJonathan Adams } else { 46035a5a358SJonathan Adams baseO = sdc->sdc_base_O; 46135a5a358SJonathan Adams baseR = sdc->sdc_base_R; 46235a5a358SJonathan Adams } 46335a5a358SJonathan Adams 46435a5a358SJonathan Adams mstate_systhread_times(t, &sdc->sdc_base_O, &sdc->sdc_base_R); 46535a5a358SJonathan Adams *newO = sdc->sdc_base_O; 46635a5a358SJonathan Adams 46735a5a358SJonathan Adams sdc->sdc_reset = now; 46835a5a358SJonathan Adams sdc->sdc_pri_check = -1; /* force mismatch below */ 46935a5a358SJonathan Adams 47035a5a358SJonathan Adams /* 47135a5a358SJonathan Adams * See below for rationale. 47235a5a358SJonathan Adams */ 47335a5a358SJonathan Adams if (baseO > sdc->sdc_base_O || baseR > sdc->sdc_base_R) { 47435a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_base_ran_backwards); 47535a5a358SJonathan Adams baseO = sdc->sdc_base_O; 47635a5a358SJonathan Adams baseR = sdc->sdc_base_R; 47735a5a358SJonathan Adams } 47835a5a358SJonathan Adams 47935a5a358SJonathan Adams /* compute based on the entire interval */ 48035a5a358SJonathan Adams *O = (sdc->sdc_base_O - baseO); 48135a5a358SJonathan Adams *R = (sdc->sdc_base_R - baseR); 48235a5a358SJonathan Adams return; 48335a5a358SJonathan Adams } 48435a5a358SJonathan Adams 48535a5a358SJonathan Adams /* 48635a5a358SJonathan Adams * If we're called from sysdc_update(), we *must* return a value 48735a5a358SJonathan Adams * for newO, so we always call mstate_systhread_times(). 48835a5a358SJonathan Adams * 48935a5a358SJonathan Adams * Otherwise, if we've already done a pri check this tick, 49035a5a358SJonathan Adams * we can skip it. 49135a5a358SJonathan Adams */ 49235a5a358SJonathan Adams if (!update && sdc->sdc_pri_check == now) { 49335a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_already_done); 49435a5a358SJonathan Adams return; 49535a5a358SJonathan Adams } 49635a5a358SJonathan Adams 49735a5a358SJonathan Adams /* Get the current times from the thread */ 49835a5a358SJonathan Adams sdc->sdc_pri_check = now; 49935a5a358SJonathan Adams mstate_systhread_times(t, &sdc->sdc_cur_O, &sdc->sdc_cur_R); 50035a5a358SJonathan Adams *newO = sdc->sdc_cur_O; 50135a5a358SJonathan Adams 50235a5a358SJonathan Adams /* 50335a5a358SJonathan Adams * The updating of microstate accounting is not done under a 50435a5a358SJonathan Adams * consistent set of locks, particularly the t_waitrq field. This 50535a5a358SJonathan Adams * can lead to narrow windows in which we account for time in the 50635a5a358SJonathan Adams * wrong bucket, which on the next read will be accounted for 50735a5a358SJonathan Adams * correctly. 50835a5a358SJonathan Adams * 50935a5a358SJonathan Adams * If our sdc_base_* fields were affected by one of these blips, we 51035a5a358SJonathan Adams * throw away the old data, and pretend this tick didn't happen. 51135a5a358SJonathan Adams */ 51235a5a358SJonathan Adams if (sdc->sdc_cur_O < sdc->sdc_base_O || 51335a5a358SJonathan Adams sdc->sdc_cur_R < sdc->sdc_base_R) { 51435a5a358SJonathan Adams 51535a5a358SJonathan Adams sdc->sdc_base_O = sdc->sdc_cur_O; 51635a5a358SJonathan Adams sdc->sdc_base_R = sdc->sdc_cur_R; 51735a5a358SJonathan Adams 51835a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_times_cur_ran_backwards); 51935a5a358SJonathan Adams return; 52035a5a358SJonathan Adams } 52135a5a358SJonathan Adams 52235a5a358SJonathan Adams *O = sdc->sdc_cur_O - sdc->sdc_base_O; 52335a5a358SJonathan Adams *R = sdc->sdc_cur_R - sdc->sdc_base_R; 52435a5a358SJonathan Adams } 52535a5a358SJonathan Adams 52635a5a358SJonathan Adams /* 52735a5a358SJonathan Adams * sysdc_compute_pri() 52835a5a358SJonathan Adams * 52935a5a358SJonathan Adams * Recomputes the priority of the thread, leaving the result in 53035a5a358SJonathan Adams * sdc->sdc_epri. Returns 1 if a priority update should occur 53135a5a358SJonathan Adams * (which will also trigger a cpu_surrender()), otherwise 53235a5a358SJonathan Adams * returns 0. 53335a5a358SJonathan Adams */ 53435a5a358SJonathan Adams static uint_t 53535a5a358SJonathan Adams sysdc_compute_pri(sysdc_t *sdc, uint_t flags) 53635a5a358SJonathan Adams { 53735a5a358SJonathan Adams kthread_t *const t = sdc->sdc_thread; 53835a5a358SJonathan Adams const uint_t update = (flags & SDC_UPDATE_TIMEOUT); 53935a5a358SJonathan Adams const uint_t tick = (flags & SDC_UPDATE_TICK); 54035a5a358SJonathan Adams 54135a5a358SJonathan Adams hrtime_t O, R; 54235a5a358SJonathan Adams hrtime_t newO = -1; 54335a5a358SJonathan Adams 54435a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 54535a5a358SJonathan Adams 54635a5a358SJonathan Adams sysdc_update_times(sdc, flags, &O, &R, &newO); 54735a5a358SJonathan Adams ASSERT(!update || newO != -1); 54835a5a358SJonathan Adams 54935a5a358SJonathan Adams /* If we have new data, recompute our priority. */ 55035a5a358SJonathan Adams if ((O + R) != 0) { 55135a5a358SJonathan Adams sdc->sdc_cur_DC = (O * SYSDC_DC_MAX) / (O + R); 55235a5a358SJonathan Adams 55335a5a358SJonathan Adams /* Adjust our priority to move our DC closer to the target. */ 55435a5a358SJonathan Adams if (sdc->sdc_cur_DC < sdc->sdc_target_DC) 55535a5a358SJonathan Adams sdc->sdc_pri = sdc->sdc_maxpri; 55635a5a358SJonathan Adams else 55735a5a358SJonathan Adams sdc->sdc_pri = sdc->sdc_minpri; 55835a5a358SJonathan Adams } 55935a5a358SJonathan Adams 56035a5a358SJonathan Adams /* 56135a5a358SJonathan Adams * If our per-pset duty cycle goes over the max, we will take a break. 56235a5a358SJonathan Adams * This forces all sysdc threads in the pset to minimum priority, in 56335a5a358SJonathan Adams * order to let everyone else have a chance at the CPU. 56435a5a358SJonathan Adams */ 56535a5a358SJonathan Adams if (sdc->sdc_pset->sdp_need_break) { 56635a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_compute_pri_breaking); 56735a5a358SJonathan Adams sdc->sdc_epri = sdc->sdc_minpri; 56835a5a358SJonathan Adams } else { 56935a5a358SJonathan Adams sdc->sdc_epri = sdc->sdc_pri; 57035a5a358SJonathan Adams } 57135a5a358SJonathan Adams 57235a5a358SJonathan Adams DTRACE_PROBE4(sysdc__compute__pri, 57335a5a358SJonathan Adams kthread_t *, t, pri_t, sdc->sdc_epri, uint_t, sdc->sdc_cur_DC, 57435a5a358SJonathan Adams uint_t, sdc->sdc_target_DC); 57535a5a358SJonathan Adams 57635a5a358SJonathan Adams /* 57735a5a358SJonathan Adams * For sysdc_update(), we compute the ONPROC time for high-priority 57835a5a358SJonathan Adams * threads, which is used to calculate the per-pset duty cycle. We 57935a5a358SJonathan Adams * will always tell our callers to update the thread's priority, 58035a5a358SJonathan Adams * since we want to force a cpu_surrender(). 58135a5a358SJonathan Adams * 58235a5a358SJonathan Adams * We reset sdc_update_ticks so that sysdc_tick() will only update 58335a5a358SJonathan Adams * the thread's priority if our timeout is delayed by a tick or 58435a5a358SJonathan Adams * more. 58535a5a358SJonathan Adams */ 58635a5a358SJonathan Adams if (update) { 58735a5a358SJonathan Adams /* SDC threads are not allowed to change cpupart bindings. */ 58835a5a358SJonathan Adams ASSERT(t->t_cpupart == sdc->sdc_pset->sdp_cpupart); 58935a5a358SJonathan Adams 59035a5a358SJonathan Adams /* If we were at MAXPRI, account for our onproc time. */ 59135a5a358SJonathan Adams if (t->t_pri == sdc->sdc_maxpri && 59235a5a358SJonathan Adams sdc->sdc_last_base_O != 0 && 59335a5a358SJonathan Adams sdc->sdc_last_base_O < newO) { 59435a5a358SJonathan Adams sdc->sdc_last_O = newO - sdc->sdc_last_base_O; 59535a5a358SJonathan Adams sdc->sdc_pset->sdp_onproc_time += 59635a5a358SJonathan Adams (uint64_t)sdc->sdc_last_O; 59735a5a358SJonathan Adams sdc->sdc_pset->sdp_onproc_threads++; 59835a5a358SJonathan Adams } else { 59935a5a358SJonathan Adams sdc->sdc_last_O = 0; 60035a5a358SJonathan Adams } 60135a5a358SJonathan Adams sdc->sdc_last_base_O = newO; 60235a5a358SJonathan Adams 60335a5a358SJonathan Adams sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks + 1; 60435a5a358SJonathan Adams return (1); 60535a5a358SJonathan Adams } 60635a5a358SJonathan Adams 60735a5a358SJonathan Adams /* 60835a5a358SJonathan Adams * Like sysdc_update(), sysdc_tick() always wants to update the 60935a5a358SJonathan Adams * thread's priority, so that the CPU is surrendered if necessary. 61035a5a358SJonathan Adams * We reset sdc_update_ticks so that if the timeout continues to be 61135a5a358SJonathan Adams * delayed, we'll update at the regular interval. 61235a5a358SJonathan Adams */ 61335a5a358SJonathan Adams if (tick) { 61435a5a358SJonathan Adams ASSERT(sdc->sdc_ticks == sdc->sdc_update_ticks); 61535a5a358SJonathan Adams sdc->sdc_update_ticks = sdc->sdc_ticks + sysdc_update_ticks; 61635a5a358SJonathan Adams return (1); 61735a5a358SJonathan Adams } 61835a5a358SJonathan Adams 61935a5a358SJonathan Adams /* 62035a5a358SJonathan Adams * Otherwise, only tell our callers to update the priority if it has 62135a5a358SJonathan Adams * changed. 62235a5a358SJonathan Adams */ 62335a5a358SJonathan Adams return (sdc->sdc_epri != t->t_pri); 62435a5a358SJonathan Adams } 62535a5a358SJonathan Adams 62635a5a358SJonathan Adams static void 62735a5a358SJonathan Adams sysdc_update_pri(sysdc_t *sdc, uint_t flags) 62835a5a358SJonathan Adams { 62935a5a358SJonathan Adams kthread_t *t = sdc->sdc_thread; 63035a5a358SJonathan Adams 63135a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 63235a5a358SJonathan Adams 63335a5a358SJonathan Adams if (sysdc_compute_pri(sdc, flags)) { 63435a5a358SJonathan Adams if (!thread_change_pri(t, sdc->sdc_epri, 0)) { 63535a5a358SJonathan Adams cpu_surrender(t); 63635a5a358SJonathan Adams } 63735a5a358SJonathan Adams } 63835a5a358SJonathan Adams } 63935a5a358SJonathan Adams 64035a5a358SJonathan Adams /* 64135a5a358SJonathan Adams * Add a thread onto the active list. It will only be removed by 64235a5a358SJonathan Adams * sysdc_update(). 64335a5a358SJonathan Adams */ 64435a5a358SJonathan Adams static void 64535a5a358SJonathan Adams sysdc_activate(sysdc_t *sdc) 64635a5a358SJonathan Adams { 64735a5a358SJonathan Adams sysdc_t *volatile *headp = &SYSDC_LIST(sdc)->sdl_list; 64835a5a358SJonathan Adams sysdc_t *head; 64935a5a358SJonathan Adams kthread_t *t = sdc->sdc_thread; 65035a5a358SJonathan Adams 65135a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_activate_enter); 65235a5a358SJonathan Adams 65335a5a358SJonathan Adams ASSERT(sdc->sdc_next == NULL); 65435a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 65535a5a358SJonathan Adams 65635a5a358SJonathan Adams do { 65735a5a358SJonathan Adams head = *headp; 65835a5a358SJonathan Adams sdc->sdc_next = head; 65935a5a358SJonathan Adams } while (atomic_cas_ptr(headp, head, sdc) != head); 66035a5a358SJonathan Adams } 66135a5a358SJonathan Adams 66235a5a358SJonathan Adams /* 66335a5a358SJonathan Adams * sysdc_update() has two jobs: 66435a5a358SJonathan Adams * 66535a5a358SJonathan Adams * 1. It updates the priorities of all active SDC threads on the system. 66635a5a358SJonathan Adams * 2. It measures pset CPU usage and enforces sysdc_max_pset_DC. 66735a5a358SJonathan Adams */ 66835a5a358SJonathan Adams static void 66935a5a358SJonathan Adams sysdc_update(void *arg) 67035a5a358SJonathan Adams { 67135a5a358SJonathan Adams int idx; 67235a5a358SJonathan Adams sysdc_t *freelist = NULL; 67335a5a358SJonathan Adams sysdc_pset_t *cur; 67435a5a358SJonathan Adams hrtime_t now, diff; 67535a5a358SJonathan Adams uint_t redeploy = 1; 67635a5a358SJonathan Adams 67735a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_enter); 67835a5a358SJonathan Adams 67935a5a358SJonathan Adams ASSERT(sysdc_update_timeout_started); 68035a5a358SJonathan Adams 68135a5a358SJonathan Adams /* 68235a5a358SJonathan Adams * If this is our first time through, diff will be gigantic, and 68335a5a358SJonathan Adams * no breaks will be necessary. 68435a5a358SJonathan Adams */ 68535a5a358SJonathan Adams now = gethrtime(); 68635a5a358SJonathan Adams diff = now - sysdc_last_update; 68735a5a358SJonathan Adams sysdc_last_update = now; 68835a5a358SJonathan Adams 68935a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 69035a5a358SJonathan Adams for (cur = list_head(&sysdc_psets); cur != NULL; 69135a5a358SJonathan Adams cur = list_next(&sysdc_psets, cur)) { 69235a5a358SJonathan Adams boolean_t breaking = (cur->sdp_should_break != 0); 69335a5a358SJonathan Adams 69435a5a358SJonathan Adams if (cur->sdp_need_break != breaking) { 69535a5a358SJonathan Adams DTRACE_PROBE2(sdc__pset__break, sysdc_pset_t *, cur, 69635a5a358SJonathan Adams boolean_t, breaking); 69735a5a358SJonathan Adams } 69835a5a358SJonathan Adams cur->sdp_onproc_time = 0; 69935a5a358SJonathan Adams cur->sdp_onproc_threads = 0; 70035a5a358SJonathan Adams cur->sdp_need_break = breaking; 70135a5a358SJonathan Adams } 70235a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 70335a5a358SJonathan Adams 70435a5a358SJonathan Adams for (idx = 0; idx < SYSDC_NLISTS; idx++) { 70535a5a358SJonathan Adams sysdc_list_t *sdl = &sysdc_active[idx]; 70635a5a358SJonathan Adams sysdc_t *volatile *headp = &sdl->sdl_list; 70735a5a358SJonathan Adams sysdc_t *head, *tail; 70835a5a358SJonathan Adams sysdc_t **prevptr; 70935a5a358SJonathan Adams 71035a5a358SJonathan Adams if (*headp == &sysdc_dummy) 71135a5a358SJonathan Adams continue; 71235a5a358SJonathan Adams 71335a5a358SJonathan Adams /* Prevent any threads from exiting while we're poking them. */ 71435a5a358SJonathan Adams mutex_enter(&sdl->sdl_lock); 71535a5a358SJonathan Adams 71635a5a358SJonathan Adams /* 71735a5a358SJonathan Adams * Each sdl_list contains a singly-linked list of active 71835a5a358SJonathan Adams * threads. Threads which become active while we are 71935a5a358SJonathan Adams * processing the list will be added to sdl_list. Since we 72035a5a358SJonathan Adams * don't want that to interfere with our own processing, we 72135a5a358SJonathan Adams * swap in an empty list. Any newly active threads will 72235a5a358SJonathan Adams * go on to this empty list. When finished, we'll put any 72335a5a358SJonathan Adams * such threads at the end of the processed list. 72435a5a358SJonathan Adams */ 72535a5a358SJonathan Adams head = atomic_swap_ptr(headp, &sysdc_dummy); 72635a5a358SJonathan Adams prevptr = &head; 72735a5a358SJonathan Adams while (*prevptr != &sysdc_dummy) { 72835a5a358SJonathan Adams sysdc_t *const sdc = *prevptr; 72935a5a358SJonathan Adams kthread_t *const t = sdc->sdc_thread; 73035a5a358SJonathan Adams 73135a5a358SJonathan Adams /* 73235a5a358SJonathan Adams * If the thread has exited, move its sysdc_t onto 73335a5a358SJonathan Adams * freelist, to be freed later. 73435a5a358SJonathan Adams */ 73535a5a358SJonathan Adams if (t == NULL) { 73635a5a358SJonathan Adams *prevptr = sdc->sdc_next; 73735a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_exited); 73835a5a358SJonathan Adams sdc->sdc_next = freelist; 73935a5a358SJonathan Adams freelist = sdc; 74035a5a358SJonathan Adams continue; 74135a5a358SJonathan Adams } 74235a5a358SJonathan Adams 74335a5a358SJonathan Adams thread_lock(t); 74435a5a358SJonathan Adams if (t->t_cid != sysdccid) { 74535a5a358SJonathan Adams thread_unlock(t); 74635a5a358SJonathan Adams prevptr = &sdc->sdc_next; 74735a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_not_sdc); 74835a5a358SJonathan Adams continue; 74935a5a358SJonathan Adams } 75035a5a358SJonathan Adams ASSERT(t->t_cldata == sdc); 75135a5a358SJonathan Adams 75235a5a358SJonathan Adams /* 75335a5a358SJonathan Adams * If the thread has been sleeping for longer 75435a5a358SJonathan Adams * than sysdc_prune_interval, make it inactive by 75535a5a358SJonathan Adams * removing it from the list. 75635a5a358SJonathan Adams */ 75735a5a358SJonathan Adams if (!(t->t_state & (TS_RUN | TS_ONPROC)) && 75835a5a358SJonathan Adams sdc->sdc_sleep_updates != 0 && 75935a5a358SJonathan Adams (sdc->sdc_sleep_updates - sdc->sdc_nupdates) > 76035a5a358SJonathan Adams sysdc_prune_updates) { 76135a5a358SJonathan Adams *prevptr = sdc->sdc_next; 76235a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_idle); 76335a5a358SJonathan Adams sdc->sdc_next = NULL; 76435a5a358SJonathan Adams thread_unlock(t); 76535a5a358SJonathan Adams continue; 76635a5a358SJonathan Adams } 76735a5a358SJonathan Adams sysdc_update_pri(sdc, SDC_UPDATE_TIMEOUT); 76835a5a358SJonathan Adams thread_unlock(t); 76935a5a358SJonathan Adams 77035a5a358SJonathan Adams prevptr = &sdc->sdc_next; 77135a5a358SJonathan Adams } 77235a5a358SJonathan Adams 77335a5a358SJonathan Adams /* 77435a5a358SJonathan Adams * Add our list to the bucket, putting any new entries 77535a5a358SJonathan Adams * added while we were working at the tail of the list. 77635a5a358SJonathan Adams */ 77735a5a358SJonathan Adams do { 77835a5a358SJonathan Adams tail = *headp; 77935a5a358SJonathan Adams *prevptr = tail; 78035a5a358SJonathan Adams } while (atomic_cas_ptr(headp, tail, head) != tail); 78135a5a358SJonathan Adams 78235a5a358SJonathan Adams mutex_exit(&sdl->sdl_lock); 78335a5a358SJonathan Adams } 78435a5a358SJonathan Adams 78535a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 78635a5a358SJonathan Adams for (cur = list_head(&sysdc_psets); cur != NULL; 78735a5a358SJonathan Adams cur = list_next(&sysdc_psets, cur)) { 78835a5a358SJonathan Adams 78935a5a358SJonathan Adams cur->sdp_vtime_last_interval = 79035a5a358SJonathan Adams diff * cur->sdp_cpupart->cp_ncpus; 79135a5a358SJonathan Adams cur->sdp_DC_last_interval = 79235a5a358SJonathan Adams (cur->sdp_onproc_time * SYSDC_DC_MAX) / 79335a5a358SJonathan Adams cur->sdp_vtime_last_interval; 79435a5a358SJonathan Adams 79535a5a358SJonathan Adams if (cur->sdp_should_break > 0) { 79635a5a358SJonathan Adams cur->sdp_should_break--; /* breaking */ 79735a5a358SJonathan Adams continue; 79835a5a358SJonathan Adams } 79935a5a358SJonathan Adams if (cur->sdp_dont_break > 0) { 80035a5a358SJonathan Adams cur->sdp_dont_break--; /* waiting before checking */ 80135a5a358SJonathan Adams continue; 80235a5a358SJonathan Adams } 80335a5a358SJonathan Adams if (cur->sdp_DC_last_interval > sysdc_max_pset_DC) { 80435a5a358SJonathan Adams cur->sdp_should_break = sysdc_break_updates; 80535a5a358SJonathan Adams cur->sdp_dont_break = sysdc_nobreak_updates; 80635a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_take_break); 80735a5a358SJonathan Adams } 80835a5a358SJonathan Adams } 80935a5a358SJonathan Adams 81035a5a358SJonathan Adams /* 81135a5a358SJonathan Adams * If there are no sysdc_psets, there can be no threads, so 81235a5a358SJonathan Adams * we can stop doing our timeout. Since we're holding the 81335a5a358SJonathan Adams * sysdc_pset_lock, no new sysdc_psets can come in, which will 81435a5a358SJonathan Adams * prevent anyone from racing with this and dropping our timeout 81535a5a358SJonathan Adams * on the floor. 81635a5a358SJonathan Adams */ 81735a5a358SJonathan Adams if (list_is_empty(&sysdc_psets)) { 81835a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_update_no_psets); 81935a5a358SJonathan Adams ASSERT(sysdc_update_timeout_started); 82035a5a358SJonathan Adams sysdc_update_timeout_started = 0; 82135a5a358SJonathan Adams 82235a5a358SJonathan Adams redeploy = 0; 82335a5a358SJonathan Adams } 82435a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 82535a5a358SJonathan Adams 82635a5a358SJonathan Adams while (freelist != NULL) { 82735a5a358SJonathan Adams sysdc_t *cur = freelist; 82835a5a358SJonathan Adams freelist = cur->sdc_next; 82935a5a358SJonathan Adams kmem_free(cur, sizeof (*cur)); 83035a5a358SJonathan Adams } 83135a5a358SJonathan Adams 83235a5a358SJonathan Adams if (redeploy) { 83335a5a358SJonathan Adams (void) timeout(sysdc_update, arg, sysdc_update_ticks); 83435a5a358SJonathan Adams } 83535a5a358SJonathan Adams } 83635a5a358SJonathan Adams 83735a5a358SJonathan Adams static void 83835a5a358SJonathan Adams sysdc_preempt(kthread_t *t) 83935a5a358SJonathan Adams { 84035a5a358SJonathan Adams ASSERT(t == curthread); 84135a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); 84235a5a358SJonathan Adams 84335a5a358SJonathan Adams setbackdq(t); /* give others a chance to run */ 84435a5a358SJonathan Adams } 84535a5a358SJonathan Adams 84635a5a358SJonathan Adams static void 84735a5a358SJonathan Adams sysdc_tick(kthread_t *t) 84835a5a358SJonathan Adams { 84935a5a358SJonathan Adams sysdc_t *sdc; 85035a5a358SJonathan Adams 85135a5a358SJonathan Adams thread_lock(t); 85235a5a358SJonathan Adams if (t->t_cid != sysdccid) { 85335a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_tick_not_sdc); 85435a5a358SJonathan Adams thread_unlock(t); 85535a5a358SJonathan Adams return; 85635a5a358SJonathan Adams } 85735a5a358SJonathan Adams sdc = t->t_cldata; 85835a5a358SJonathan Adams if (t->t_state == TS_ONPROC && 85935a5a358SJonathan Adams t->t_pri < t->t_disp_queue->disp_maxrunpri) { 86035a5a358SJonathan Adams cpu_surrender(t); 86135a5a358SJonathan Adams } 86235a5a358SJonathan Adams 86335a5a358SJonathan Adams if (t->t_state == TS_ONPROC || t->t_state == TS_RUN) { 86435a5a358SJonathan Adams ASSERT(sdc->sdc_sleep_updates == 0); 86535a5a358SJonathan Adams } 86635a5a358SJonathan Adams 86735a5a358SJonathan Adams ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks); 86835a5a358SJonathan Adams sdc->sdc_ticks++; 86935a5a358SJonathan Adams if (sdc->sdc_ticks == sdc->sdc_update_ticks) { 87035a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_tick_quantum_expired); 87135a5a358SJonathan Adams sysdc_update_pri(sdc, SDC_UPDATE_TICK); 87235a5a358SJonathan Adams ASSERT(sdc->sdc_ticks != sdc->sdc_update_ticks); 87335a5a358SJonathan Adams } 87435a5a358SJonathan Adams thread_unlock(t); 87535a5a358SJonathan Adams } 87635a5a358SJonathan Adams 87735a5a358SJonathan Adams static void 87835a5a358SJonathan Adams sysdc_setrun(kthread_t *t) 87935a5a358SJonathan Adams { 88035a5a358SJonathan Adams sysdc_t *sdc = t->t_cldata; 88135a5a358SJonathan Adams 88235a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); /* t should be in transition */ 88335a5a358SJonathan Adams 88435a5a358SJonathan Adams sdc->sdc_sleep_updates = 0; 88535a5a358SJonathan Adams 88635a5a358SJonathan Adams if (sdc->sdc_next == NULL) { 88735a5a358SJonathan Adams /* 88835a5a358SJonathan Adams * Since we're in transition, we don't want to use the 88935a5a358SJonathan Adams * full thread_update_pri(). 89035a5a358SJonathan Adams */ 89135a5a358SJonathan Adams if (sysdc_compute_pri(sdc, 0)) { 89235a5a358SJonathan Adams THREAD_CHANGE_PRI(t, sdc->sdc_epri); 89335a5a358SJonathan Adams } 89435a5a358SJonathan Adams sysdc_activate(sdc); 89535a5a358SJonathan Adams 89635a5a358SJonathan Adams ASSERT(sdc->sdc_next != NULL); 89735a5a358SJonathan Adams } 89835a5a358SJonathan Adams 89935a5a358SJonathan Adams setbackdq(t); 90035a5a358SJonathan Adams } 90135a5a358SJonathan Adams 90235a5a358SJonathan Adams static void 90335a5a358SJonathan Adams sysdc_wakeup(kthread_t *t) 90435a5a358SJonathan Adams { 90535a5a358SJonathan Adams sysdc_setrun(t); 90635a5a358SJonathan Adams } 90735a5a358SJonathan Adams 90835a5a358SJonathan Adams static void 90935a5a358SJonathan Adams sysdc_sleep(kthread_t *t) 91035a5a358SJonathan Adams { 91135a5a358SJonathan Adams sysdc_t *sdc = t->t_cldata; 91235a5a358SJonathan Adams 91335a5a358SJonathan Adams ASSERT(THREAD_LOCK_HELD(t)); /* t should be in transition */ 91435a5a358SJonathan Adams 91535a5a358SJonathan Adams sdc->sdc_sleep_updates = sdc->sdc_nupdates; 91635a5a358SJonathan Adams } 91735a5a358SJonathan Adams 91835a5a358SJonathan Adams /*ARGSUSED*/ 91935a5a358SJonathan Adams static int 92035a5a358SJonathan Adams sysdc_enterclass(kthread_t *t, id_t cid, void *parmsp, cred_t *reqpcredp, 92135a5a358SJonathan Adams void *bufp) 92235a5a358SJonathan Adams { 92335a5a358SJonathan Adams cpupart_t *const cpupart = t->t_cpupart; 92435a5a358SJonathan Adams sysdc_t *sdc = bufp; 92535a5a358SJonathan Adams sysdc_params_t *sdpp = parmsp; 92635a5a358SJonathan Adams sysdc_pset_t *newpset = sdc->sdc_pset; 92735a5a358SJonathan Adams sysdc_pset_t *pset; 92835a5a358SJonathan Adams int start_timeout; 92935a5a358SJonathan Adams 93035a5a358SJonathan Adams if (t->t_cid != syscid) 93135a5a358SJonathan Adams return (EPERM); 93235a5a358SJonathan Adams 93335a5a358SJonathan Adams ASSERT(ttolwp(t) != NULL); 93435a5a358SJonathan Adams ASSERT(sdpp != NULL); 93535a5a358SJonathan Adams ASSERT(newpset != NULL); 93635a5a358SJonathan Adams ASSERT(sysdc_param_init); 93735a5a358SJonathan Adams 93835a5a358SJonathan Adams ASSERT(sdpp->sdp_minpri >= sysdc_minpri); 93935a5a358SJonathan Adams ASSERT(sdpp->sdp_maxpri <= sysdc_maxpri); 94035a5a358SJonathan Adams ASSERT(sdpp->sdp_DC >= sysdc_minDC); 94135a5a358SJonathan Adams ASSERT(sdpp->sdp_DC <= sysdc_maxDC); 94235a5a358SJonathan Adams 94335a5a358SJonathan Adams sdc->sdc_thread = t; 94435a5a358SJonathan Adams sdc->sdc_pri = sdpp->sdp_maxpri; /* start off maximally */ 94535a5a358SJonathan Adams sdc->sdc_minpri = sdpp->sdp_minpri; 94635a5a358SJonathan Adams sdc->sdc_maxpri = sdpp->sdp_maxpri; 94735a5a358SJonathan Adams sdc->sdc_target_DC = sdpp->sdp_DC; 94835a5a358SJonathan Adams sdc->sdc_ticks = 0; 94935a5a358SJonathan Adams sdc->sdc_update_ticks = sysdc_update_ticks + 1; 95035a5a358SJonathan Adams 95135a5a358SJonathan Adams /* Assign ourselves to the appropriate pset. */ 95235a5a358SJonathan Adams sdc->sdc_pset = NULL; 95335a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 95435a5a358SJonathan Adams for (pset = list_head(&sysdc_psets); pset != NULL; 95535a5a358SJonathan Adams pset = list_next(&sysdc_psets, pset)) { 95635a5a358SJonathan Adams if (pset->sdp_cpupart == cpupart) { 95735a5a358SJonathan Adams break; 95835a5a358SJonathan Adams } 95935a5a358SJonathan Adams } 96035a5a358SJonathan Adams if (pset == NULL) { 96135a5a358SJonathan Adams pset = newpset; 96235a5a358SJonathan Adams newpset = NULL; 96335a5a358SJonathan Adams pset->sdp_cpupart = cpupart; 96435a5a358SJonathan Adams list_insert_tail(&sysdc_psets, pset); 96535a5a358SJonathan Adams } 96635a5a358SJonathan Adams pset->sdp_nthreads++; 96735a5a358SJonathan Adams ASSERT(pset->sdp_nthreads > 0); 96835a5a358SJonathan Adams 96935a5a358SJonathan Adams sdc->sdc_pset = pset; 97035a5a358SJonathan Adams 97135a5a358SJonathan Adams start_timeout = (sysdc_update_timeout_started == 0); 97235a5a358SJonathan Adams sysdc_update_timeout_started = 1; 97335a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 97435a5a358SJonathan Adams 97535a5a358SJonathan Adams if (newpset != NULL) 97635a5a358SJonathan Adams kmem_free(newpset, sizeof (*newpset)); 97735a5a358SJonathan Adams 97835a5a358SJonathan Adams /* Update t's scheduling class and priority. */ 97935a5a358SJonathan Adams thread_lock(t); 98035a5a358SJonathan Adams t->t_clfuncs = &(sclass[cid].cl_funcs->thread); 98135a5a358SJonathan Adams t->t_cid = cid; 98235a5a358SJonathan Adams t->t_cldata = sdc; 98335a5a358SJonathan Adams t->t_schedflag |= TS_RUNQMATCH; 98435a5a358SJonathan Adams 98535a5a358SJonathan Adams sysdc_update_pri(sdc, SDC_UPDATE_INITIAL); 98635a5a358SJonathan Adams thread_unlock(t); 98735a5a358SJonathan Adams 98835a5a358SJonathan Adams /* Kick off the thread timeout if we're the first one in. */ 98935a5a358SJonathan Adams if (start_timeout) { 99035a5a358SJonathan Adams (void) timeout(sysdc_update, NULL, sysdc_update_ticks); 99135a5a358SJonathan Adams } 99235a5a358SJonathan Adams 99335a5a358SJonathan Adams return (0); 99435a5a358SJonathan Adams } 99535a5a358SJonathan Adams 99635a5a358SJonathan Adams static void 99735a5a358SJonathan Adams sysdc_leave(sysdc_t *sdc) 99835a5a358SJonathan Adams { 99935a5a358SJonathan Adams sysdc_pset_t *sdp = sdc->sdc_pset; 100035a5a358SJonathan Adams sysdc_list_t *sdl = SYSDC_LIST(sdc); 100135a5a358SJonathan Adams uint_t freedc; 100235a5a358SJonathan Adams 100335a5a358SJonathan Adams mutex_enter(&sdl->sdl_lock); /* block sysdc_update() */ 100435a5a358SJonathan Adams sdc->sdc_thread = NULL; 100535a5a358SJonathan Adams freedc = (sdc->sdc_next == NULL); 100635a5a358SJonathan Adams mutex_exit(&sdl->sdl_lock); 100735a5a358SJonathan Adams 100835a5a358SJonathan Adams mutex_enter(&sysdc_pset_lock); 100935a5a358SJonathan Adams ASSERT(sdp != NULL); 101035a5a358SJonathan Adams ASSERT(sdp->sdp_nthreads > 0); 101135a5a358SJonathan Adams --sdp->sdp_nthreads; 101235a5a358SJonathan Adams if (sdp->sdp_nthreads == 0) { 101335a5a358SJonathan Adams list_remove(&sysdc_psets, sdp); 101435a5a358SJonathan Adams } else { 101535a5a358SJonathan Adams sdp = NULL; 101635a5a358SJonathan Adams } 101735a5a358SJonathan Adams mutex_exit(&sysdc_pset_lock); 101835a5a358SJonathan Adams 101935a5a358SJonathan Adams if (freedc) 102035a5a358SJonathan Adams kmem_free(sdc, sizeof (*sdc)); 102135a5a358SJonathan Adams if (sdp != NULL) 102235a5a358SJonathan Adams kmem_free(sdp, sizeof (*sdp)); 102335a5a358SJonathan Adams } 102435a5a358SJonathan Adams 102535a5a358SJonathan Adams static void 102635a5a358SJonathan Adams sysdc_exitclass(void *buf) 102735a5a358SJonathan Adams { 102835a5a358SJonathan Adams sysdc_leave((sysdc_t *)buf); 102935a5a358SJonathan Adams } 103035a5a358SJonathan Adams 103135a5a358SJonathan Adams /*ARGSUSED*/ 103235a5a358SJonathan Adams static int 103335a5a358SJonathan Adams sysdc_canexit(kthread_t *t, cred_t *reqpcredp) 103435a5a358SJonathan Adams { 103535a5a358SJonathan Adams /* Threads cannot exit SDC once joined, except in a body bag. */ 103635a5a358SJonathan Adams return (EPERM); 103735a5a358SJonathan Adams } 103835a5a358SJonathan Adams 103935a5a358SJonathan Adams static void 104035a5a358SJonathan Adams sysdc_exit(kthread_t *t) 104135a5a358SJonathan Adams { 104235a5a358SJonathan Adams sysdc_t *sdc; 104335a5a358SJonathan Adams 104435a5a358SJonathan Adams /* We're exiting, so we just rejoin the SYS class. */ 104535a5a358SJonathan Adams thread_lock(t); 104635a5a358SJonathan Adams ASSERT(t->t_cid == sysdccid); 104735a5a358SJonathan Adams sdc = t->t_cldata; 104835a5a358SJonathan Adams t->t_cid = syscid; 104935a5a358SJonathan Adams t->t_cldata = NULL; 105035a5a358SJonathan Adams t->t_clfuncs = &(sclass[syscid].cl_funcs->thread); 105135a5a358SJonathan Adams (void) thread_change_pri(t, maxclsyspri, 0); 105235a5a358SJonathan Adams t->t_schedflag &= ~TS_RUNQMATCH; 105335a5a358SJonathan Adams thread_unlock_nopreempt(t); 105435a5a358SJonathan Adams 105535a5a358SJonathan Adams /* Unlink the sdc from everything. */ 105635a5a358SJonathan Adams sysdc_leave(sdc); 105735a5a358SJonathan Adams } 105835a5a358SJonathan Adams 105935a5a358SJonathan Adams /*ARGSUSED*/ 106035a5a358SJonathan Adams static int 106135a5a358SJonathan Adams sysdc_fork(kthread_t *t, kthread_t *ct, void *bufp) 106235a5a358SJonathan Adams { 106335a5a358SJonathan Adams /* 106435a5a358SJonathan Adams * Threads cannot be created with SDC as their class; they must 106535a5a358SJonathan Adams * be created as SYS and then added with sysdc_thread_enter(). 106635a5a358SJonathan Adams * Because of this restriction, sysdc_fork() should never be called. 106735a5a358SJonathan Adams */ 106835a5a358SJonathan Adams panic("sysdc cannot be forked"); 106935a5a358SJonathan Adams 107035a5a358SJonathan Adams return (ENOSYS); 107135a5a358SJonathan Adams } 107235a5a358SJonathan Adams 107335a5a358SJonathan Adams /*ARGSUSED*/ 107435a5a358SJonathan Adams static void 107535a5a358SJonathan Adams sysdc_forkret(kthread_t *t, kthread_t *ct) 107635a5a358SJonathan Adams { 107735a5a358SJonathan Adams /* SDC threads are part of system processes, which never fork. */ 107835a5a358SJonathan Adams panic("sysdc cannot be forked"); 107935a5a358SJonathan Adams } 108035a5a358SJonathan Adams 108135a5a358SJonathan Adams static pri_t 108235a5a358SJonathan Adams sysdc_globpri(kthread_t *t) 108335a5a358SJonathan Adams { 108435a5a358SJonathan Adams return (t->t_epri); 108535a5a358SJonathan Adams } 108635a5a358SJonathan Adams 108735a5a358SJonathan Adams /*ARGSUSED*/ 108835a5a358SJonathan Adams static pri_t 108935a5a358SJonathan Adams sysdc_no_swap(kthread_t *t, int flags) 109035a5a358SJonathan Adams { 109135a5a358SJonathan Adams /* SDC threads cannot be swapped. */ 109235a5a358SJonathan Adams return (-1); 109335a5a358SJonathan Adams } 109435a5a358SJonathan Adams 109535a5a358SJonathan Adams /* 109635a5a358SJonathan Adams * Get maximum and minimum priorities enjoyed by SDC threads. 109735a5a358SJonathan Adams */ 109835a5a358SJonathan Adams static int 109935a5a358SJonathan Adams sysdc_getclpri(pcpri_t *pcprip) 110035a5a358SJonathan Adams { 110135a5a358SJonathan Adams pcprip->pc_clpmax = sysdc_maxpri; 110235a5a358SJonathan Adams pcprip->pc_clpmin = sysdc_minpri; 110335a5a358SJonathan Adams return (0); 110435a5a358SJonathan Adams } 110535a5a358SJonathan Adams 110635a5a358SJonathan Adams /*ARGSUSED*/ 110735a5a358SJonathan Adams static int 110835a5a358SJonathan Adams sysdc_getclinfo(void *arg) 110935a5a358SJonathan Adams { 111035a5a358SJonathan Adams return (0); /* no class-specific info */ 111135a5a358SJonathan Adams } 111235a5a358SJonathan Adams 111335a5a358SJonathan Adams /*ARGSUSED*/ 111435a5a358SJonathan Adams static int 111535a5a358SJonathan Adams sysdc_alloc(void **p, int flag) 111635a5a358SJonathan Adams { 111735a5a358SJonathan Adams sysdc_t *new; 111835a5a358SJonathan Adams 111935a5a358SJonathan Adams *p = NULL; 112035a5a358SJonathan Adams if ((new = kmem_zalloc(sizeof (*new), flag)) == NULL) { 112135a5a358SJonathan Adams return (ENOMEM); 112235a5a358SJonathan Adams } 112335a5a358SJonathan Adams if ((new->sdc_pset = kmem_zalloc(sizeof (*new->sdc_pset), flag)) == 112435a5a358SJonathan Adams NULL) { 112535a5a358SJonathan Adams kmem_free(new, sizeof (*new)); 112635a5a358SJonathan Adams return (ENOMEM); 112735a5a358SJonathan Adams } 112835a5a358SJonathan Adams *p = new; 112935a5a358SJonathan Adams return (0); 113035a5a358SJonathan Adams } 113135a5a358SJonathan Adams 113235a5a358SJonathan Adams static void 113335a5a358SJonathan Adams sysdc_free(void *p) 113435a5a358SJonathan Adams { 113535a5a358SJonathan Adams sysdc_t *sdc = p; 113635a5a358SJonathan Adams 113735a5a358SJonathan Adams if (sdc != NULL) { 113835a5a358SJonathan Adams /* 113935a5a358SJonathan Adams * We must have failed CL_ENTERCLASS(), so our pset should be 114035a5a358SJonathan Adams * there and unused. 114135a5a358SJonathan Adams */ 114235a5a358SJonathan Adams ASSERT(sdc->sdc_pset != NULL); 114335a5a358SJonathan Adams ASSERT(sdc->sdc_pset->sdp_cpupart == NULL); 114435a5a358SJonathan Adams kmem_free(sdc->sdc_pset, sizeof (*sdc->sdc_pset)); 114535a5a358SJonathan Adams kmem_free(sdc, sizeof (*sdc)); 114635a5a358SJonathan Adams } 114735a5a358SJonathan Adams } 114835a5a358SJonathan Adams 114935a5a358SJonathan Adams static int sysdc_enosys(); /* Boy, ANSI-C's K&R compatibility is weird. */ 115035a5a358SJonathan Adams static int sysdc_einval(); 115135a5a358SJonathan Adams static void sysdc_nullsys(); 115235a5a358SJonathan Adams 115335a5a358SJonathan Adams static struct classfuncs sysdc_classfuncs = { 115435a5a358SJonathan Adams /* messages to class manager */ 115535a5a358SJonathan Adams { 115635a5a358SJonathan Adams sysdc_enosys, /* admin */ 115735a5a358SJonathan Adams sysdc_getclinfo, 115835a5a358SJonathan Adams sysdc_enosys, /* parmsin */ 115935a5a358SJonathan Adams sysdc_enosys, /* parmsout */ 116035a5a358SJonathan Adams sysdc_enosys, /* vaparmsin */ 116135a5a358SJonathan Adams sysdc_enosys, /* vaparmsout */ 116235a5a358SJonathan Adams sysdc_getclpri, 116335a5a358SJonathan Adams sysdc_alloc, 116435a5a358SJonathan Adams sysdc_free, 116535a5a358SJonathan Adams }, 116635a5a358SJonathan Adams /* operations on threads */ 116735a5a358SJonathan Adams { 116835a5a358SJonathan Adams sysdc_enterclass, 116935a5a358SJonathan Adams sysdc_exitclass, 117035a5a358SJonathan Adams sysdc_canexit, 117135a5a358SJonathan Adams sysdc_fork, 117235a5a358SJonathan Adams sysdc_forkret, 117335a5a358SJonathan Adams sysdc_nullsys, /* parmsget */ 117435a5a358SJonathan Adams sysdc_enosys, /* parmsset */ 117535a5a358SJonathan Adams sysdc_nullsys, /* stop */ 117635a5a358SJonathan Adams sysdc_exit, 117735a5a358SJonathan Adams sysdc_nullsys, /* active */ 117835a5a358SJonathan Adams sysdc_nullsys, /* inactive */ 117935a5a358SJonathan Adams sysdc_no_swap, /* swapin */ 118035a5a358SJonathan Adams sysdc_no_swap, /* swapout */ 118135a5a358SJonathan Adams sysdc_nullsys, /* trapret */ 118235a5a358SJonathan Adams sysdc_preempt, 118335a5a358SJonathan Adams sysdc_setrun, 118435a5a358SJonathan Adams sysdc_sleep, 118535a5a358SJonathan Adams sysdc_tick, 118635a5a358SJonathan Adams sysdc_wakeup, 118735a5a358SJonathan Adams sysdc_einval, /* donice */ 118835a5a358SJonathan Adams sysdc_globpri, 118935a5a358SJonathan Adams sysdc_nullsys, /* set_process_group */ 119035a5a358SJonathan Adams sysdc_nullsys, /* yield */ 119135a5a358SJonathan Adams sysdc_einval, /* doprio */ 119235a5a358SJonathan Adams } 119335a5a358SJonathan Adams }; 119435a5a358SJonathan Adams 119535a5a358SJonathan Adams static int 119635a5a358SJonathan Adams sysdc_enosys() 119735a5a358SJonathan Adams { 119835a5a358SJonathan Adams return (ENOSYS); 119935a5a358SJonathan Adams } 120035a5a358SJonathan Adams 120135a5a358SJonathan Adams static int 120235a5a358SJonathan Adams sysdc_einval() 120335a5a358SJonathan Adams { 120435a5a358SJonathan Adams return (EINVAL); 120535a5a358SJonathan Adams } 120635a5a358SJonathan Adams 120735a5a358SJonathan Adams static void 120835a5a358SJonathan Adams sysdc_nullsys() 120935a5a358SJonathan Adams { 121035a5a358SJonathan Adams } 121135a5a358SJonathan Adams 121235a5a358SJonathan Adams /*ARGSUSED*/ 121335a5a358SJonathan Adams static pri_t 121435a5a358SJonathan Adams sysdc_init(id_t cid, int clparmsz, classfuncs_t **clfuncspp) 121535a5a358SJonathan Adams { 121635a5a358SJonathan Adams int idx; 121735a5a358SJonathan Adams 121835a5a358SJonathan Adams list_create(&sysdc_psets, sizeof (sysdc_pset_t), 121935a5a358SJonathan Adams offsetof(sysdc_pset_t, sdp_node)); 122035a5a358SJonathan Adams 122135a5a358SJonathan Adams for (idx = 0; idx < SYSDC_NLISTS; idx++) { 122235a5a358SJonathan Adams sysdc_active[idx].sdl_list = &sysdc_dummy; 122335a5a358SJonathan Adams } 122435a5a358SJonathan Adams 122535a5a358SJonathan Adams sysdc_initparam(); 122635a5a358SJonathan Adams 122735a5a358SJonathan Adams sysdccid = cid; 122835a5a358SJonathan Adams *clfuncspp = &sysdc_classfuncs; 122935a5a358SJonathan Adams 123035a5a358SJonathan Adams return ((pri_t)v.v_maxsyspri); 123135a5a358SJonathan Adams } 123235a5a358SJonathan Adams 123335a5a358SJonathan Adams static struct sclass csw = { 123435a5a358SJonathan Adams "SDC", 123535a5a358SJonathan Adams sysdc_init, 123635a5a358SJonathan Adams 0 123735a5a358SJonathan Adams }; 123835a5a358SJonathan Adams 123935a5a358SJonathan Adams static struct modlsched modlsched = { 124035a5a358SJonathan Adams &mod_schedops, "system duty cycle scheduling class", &csw 124135a5a358SJonathan Adams }; 124235a5a358SJonathan Adams 124335a5a358SJonathan Adams static struct modlinkage modlinkage = { 124435a5a358SJonathan Adams MODREV_1, (void *)&modlsched, NULL 124535a5a358SJonathan Adams }; 124635a5a358SJonathan Adams 124735a5a358SJonathan Adams int 124835a5a358SJonathan Adams _init() 124935a5a358SJonathan Adams { 125035a5a358SJonathan Adams return (mod_install(&modlinkage)); 125135a5a358SJonathan Adams } 125235a5a358SJonathan Adams 125335a5a358SJonathan Adams int 125435a5a358SJonathan Adams _fini() 125535a5a358SJonathan Adams { 125635a5a358SJonathan Adams return (EBUSY); /* can't unload for now */ 125735a5a358SJonathan Adams } 125835a5a358SJonathan Adams 125935a5a358SJonathan Adams int 126035a5a358SJonathan Adams _info(struct modinfo *modinfop) 126135a5a358SJonathan Adams { 126235a5a358SJonathan Adams return (mod_info(&modlinkage, modinfop)); 126335a5a358SJonathan Adams } 126435a5a358SJonathan Adams 126535a5a358SJonathan Adams /* --- consolidation-private interfaces --- */ 126635a5a358SJonathan Adams void 126735a5a358SJonathan Adams sysdc_thread_enter(kthread_t *t, uint_t dc, uint_t flags) 126835a5a358SJonathan Adams { 126935a5a358SJonathan Adams void *buf = NULL; 127035a5a358SJonathan Adams sysdc_params_t sdp; 127135a5a358SJonathan Adams 127235a5a358SJonathan Adams SYSDC_INC_STAT(sysdc_thread_enter_enter); 127335a5a358SJonathan Adams 127435a5a358SJonathan Adams ASSERT(sysdc_param_init); 127535a5a358SJonathan Adams ASSERT(sysdccid >= 0); 127635a5a358SJonathan Adams 127735a5a358SJonathan Adams ASSERT((flags & ~SYSDC_THREAD_BATCH) == 0); 127835a5a358SJonathan Adams 127935a5a358SJonathan Adams sdp.sdp_minpri = sysdc_minpri; 128035a5a358SJonathan Adams sdp.sdp_maxpri = sysdc_maxpri; 128135a5a358SJonathan Adams sdp.sdp_DC = MAX(MIN(dc, sysdc_maxDC), sysdc_minDC); 128235a5a358SJonathan Adams 1283fb09f5aaSMadhav Suresh VERIFY0(CL_ALLOC(&buf, sysdccid, KM_SLEEP)); 128435a5a358SJonathan Adams 128535a5a358SJonathan Adams ASSERT(t->t_lwp != NULL); 128635a5a358SJonathan Adams ASSERT(t->t_cid == syscid); 128735a5a358SJonathan Adams ASSERT(t->t_cldata == NULL); 1288fb09f5aaSMadhav Suresh VERIFY0(CL_CANEXIT(t, NULL)); 1289fb09f5aaSMadhav Suresh VERIFY0(CL_ENTERCLASS(t, sysdccid, &sdp, kcred, buf)); 129035a5a358SJonathan Adams CL_EXITCLASS(syscid, NULL); 129135a5a358SJonathan Adams } 1292