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