ts.c revision 75d94465dbafa487b716482dc36d5150a4ec9853
* Class specific code for the time-sharing class * Extern declarations for variables defined in the ts master file /* sleeping or running in kernel after sleep */ * The tsproc_t structures are kept in an array of circular doubly linked * lists. A hash on the thread pointer is used to determine which list * each thread should be placed. Each list has a dummy "head" which is * never removed, so the list is never empty. ts_update traverses these * lists to update the priorities of threads that have been waiting on #
define TS_LISTS 16 /* number of lists, must be power of 2 *//* hash function, argument is a thread pointer */ /* iterate to the next list */ * Insert thread into the appropriate tsproc list. * Remove thread from tsproc list. * ia_classfuncs is used for interactive class threads; IA threads are stored * on the same class list as TS threads, and most of the class functions are * identical, but a few have different enough functionality to require their * Time sharing class initialization. Called by dispinit() at boot time. * We can ignore the clparmsz argument since we know that the smallest * possible parameter buffer is big enough for us. * Initialize the tsproc lists. * We're required to return a pointer to our classfuncs * structure and the highest global priority value we use. * Interactive class scheduler initialization * We're required to return a pointer to our classfuncs * structure and the highest global priority value we use. * Get or reset the ts_dptbl values per the user's request. /* get tsadmin struct from ILP32 caller */ #
endif /* _SYSCALL32_IMPL */ /* return tsadmin struct to ILP32 caller */ #
endif /* _SYSCALL32_IMPL */ /* return tsadmin struct to ILP32 callers */ #
endif /* _SYSCALL32_IMPL */ * We require that the requesting process has sufficient * priveleges. We also require that the table supplied by * the user exactly match the current ts_dptbl in size. * We read the user supplied table into a temporary buffer * where it is validated before being copied over the * Validate the user supplied values. All we are doing * here is verifying that the values are within their * allowable ranges and will not panic the system. We * make no attempt to ensure that the resulting * configuration makes sense or results in reasonable * Copy the user supplied values over the current ts_dptbl * values. The ts_globpri member is read-only so we don't * Allocate a time-sharing class specific thread structure and * initialize it with the parameters supplied. Also move the thread * to specified time-sharing priority. /* a time-sharing process */ * Initialize the tsproc structure. * Check to make sure caller is either privileged or the * window system. When the window system is converted * to using privileges, the second check can go away. * Belongs to IA "class", so set appropriate flags. * Mark as 'on' so it will not be a swap victim * Set the user priority to the requested value * or the upri limit, whichever is lower. * Reset priority. Process goes to a "user mode" priority * here regardless of whether or not it has slept since * Link new structure into tsproc list. * If this is the first time-sharing thread to occur since * boot we set up the initial call to ts_update() here. * Use an atomic compare-and-swap since that's easier and * faster than a mutex (but check with an ordinary load first * since most of the time this will already be done). * Free tsproc structure of thread. /* Remove tsproc_t structure from list */ * A thread can always leave a TS/IA class * Initialize child's tsproc structure. * Link new structure into tsproc list. * Child is placed at back of dispatcher queue and parent gives * up processor so that the child runs first after the fork. * This allows the child immediately execing to break the multiple * use of copy on write pages with no disk home. The parent will * get to steal them back rather than uselessly copying them. * Grab the child's p_lock before dropping pidlock to ensure * the process does not disappear before we set it running. * Safe to drop p_lock now since since it is safe to change * the scheduling class after this point. * Get information about the time-sharing class into the buffer * pointed to by tsinfop. The maximum configured user priority * is the only information we supply. ts_getclinfo() is called * for TS threads, and ia_getclinfo() is called for IA threads. * Return the user mode scheduling priority range. * Get the time-sharing parameters of the thread pointed to by * tsprocp into the buffer pointed to by tsparmsp. ts_parmsget() * is called for TS threads, and ia_parmsget() is called for IA * Check the validity of the time-sharing parameters in the buffer * pointed to by tsparmsp. * ts_parmsin() is called for TS threads, and ia_parmsin() is called * Check validity of parameters. * Check the validity of the time-sharing parameters in the pc_vaparms_t * structure vaparmsp and put them in the buffer pointed to by tsparmsp. * pc_vaparms_t contains (key, value) pairs of parameter. * ts_vaparmsin() is called for TS threads, and ia_vaparmsin() is called * for IA threads. ts_vaparmsin() is the variable parameter version of * ts_parmsin() and ia_vaparmsin() is the variable parameter version of * TS_NOCHANGE (-32768) is outside of the range of values for * ts_uprilim and ts_upri. If the structure tsparms_t is changed, * TS_NOCHANGE should be replaced by a flag word (in the same manner * Get the varargs parameter and check validity of parameters. * Use default parameters. * IA_NOCHANGE (-32768) is outside of the range of values for * ia_uprilim, ia_upri and ia_mode. If the structure iaparms_t is * changed, IA_NOCHANGE should be replaced by a flag word (in the * same manner as in rt.c). * Get the varargs parameter and check validity of parameters. * Use default parameters. * Nothing to do here but return success. * Copy all selected time-sharing class parameters to the user. * The parameters are specified by a key. * Copy all selected interactive class parameters to the user. * The parameters are specified by a key. * Set the scheduling parameters of the thread pointed to by tsprocp * to those specified in the buffer pointed to by tsparmsp. * ts_parmsset() is called for TS threads, and ia_parmsset() is * Make sure the user priority doesn't exceed the upri limit. * Basic permissions enforced by generic kernel code * for all classes require that a thread attempting * to change the scheduling parameters of a target * thread be privileged or have a real or effective * UID matching that of the target thread. We are not * called unless these basic permission checks have * already passed. The time-sharing class requires in * addition that the calling thread be privileged if it * is attempting to raise the upri limit above its current * value This may have been checked previously but if our * caller passed us a non-NULL credential pointer we assume * it hasn't and we check it here. * Set ts_nice to the nice value corresponding to the user * priority we are setting. Note that setting the nice field * of the parameter struct won't affect upri or nice. * Handle user priority changes * Check permissions for changing modes. * Silently fail in case this is just a priocntl * call with upri and uprilim set to IA_NOCHANGE. * session leaders must be turned on now so all processes * in the group controlling the tty will be turned on or off. * if the ia_mode is off for the session leader, * ia_set_process_group will return without setting the * processes in the group controlling the tty on. "active chain:pid %d gid %d %p",
* A thread could be exiting in between clock ticks, * so we need to calculate how much CPU time it used * since it was charged last time. * CPU caps are not enforced on exiting processes - it is * usually desirable to exit as soon as possible to free * Return the global scheduling priority that would be assigned * to a thread entering the time-sharing class with the ts_upri. * Arrange for thread to be placed in appropriate location * This is called with the current thread in TS_ONPROC and locked. * If preempted in the kernel, make sure the thread has * a kernel priority if needed. t->
t_trapret =
1;
/* so ts_trapret will run */ * This thread may be placed on wait queue by CPU Caps. In this case we * do not need to do anything until it is removed from the wait queue. * Do not enforce CPU caps on threads running at a kernel priority * If thread got preempted in the user-land then we know * it isn't holding any locks. Mark it as swappable. * Check to see if we're doing "preemption control" here. If * we are, and if the user has requested that this thread not * be preempted, and if preemptions haven't been put off for * too long, let the preemption happen here but try to make * sure the thread is rescheduled as soon as possible. We do * this by putting it on the front of the highest priority run * queue in the TS class. If the preemption has been put off * for too long, clear the "nopreempt" bit and let the thread * If not already remembered, remember current * priority for restoration in ts_yield(). * Fall through and be preempted below. "preempt:tid %p old pri %d", t,
oldpri);
* Prepare thread for sleep. We reset the thread priority so it will * run at the kernel priority level when it wakes up. * Account for time spent on CPU before going to sleep. t->
t_trapret =
1;
/* so ts_trapret will run */ * If thread has blocked in the kernel (as opposed to * being merely preempted), recompute the user mode priority. "sleep:tid %p old pri %d", t,
old_pri);
* -1 if the thread is loaded or is not eligible to be swapped in. * effective priority of the specified thread based on swapout time * and size of process (epri >= 0 , epri <= SHRT_MAX). * We know that pri_t is a short. * Be sure not to overrun its range. * Threads which have been out for a long time, * have high user mode priority and are associated * with a small address space are more deserving * Scale epri so SHRT_MAX/2 represents zero priority. * -1 if the thread isn't loaded or is not eligible to be swapped out. * effective priority of the specified thread based on if the swapper * is in softswap or hardswap mode. * Softswap: Return a low effective priority for threads * sleeping for more than maxslp secs. * Hardswap: Return an effective priority such that threads * which have been in memory for a while and are * associated with a small address space are swapped * (epri >= 0 , epri <= SHRT_MAX). * We know that pri_t is a short. * Be sure not to overrun its range. * Scale epri so SHRT_MAX/2 represents zero priority. * Check for time slice expiration. If time slice has expired * move thread to priority specified in tsdptbl for time slice expiration * and set runrun to cause preemption. * Keep track of thread's project CPU usage. Note that projects * get charged even when threads are running in the kernel. * If we're doing preemption control and trying to * avoid preempting this thread, just note that * the thread should yield soon and let it keep * running (unless it's been a while). "schedctl TS ts_tick",
/* CSTYLED */,
* When the priority of a thread is changed, * it may be necessary to adjust its position * on a sleep queue or dispatch queue. * The function thread_change_pri accomplishes "tick:tid %p old pri %d", t,
oldpri);
* If thread is currently at a kernel mode priority (has slept) * we assign it the appropriate user mode priority and time quantum * here. If we are lowering the thread's priority below that of * other runnable threads we will normally set runrun via cpu_surrender() to * If thread has blocked in the kernel (as opposed to * being merely preempted), recompute the user mode priority. * If thread has blocked in the kernel (as opposed to * being merely preempted), recompute the user mode priority. * Swapout lwp if the swapper is waiting for this thread to "trapret:tid %p old pri %d", t,
old_pri);
* Update the ts_dispwait values of all time sharing threads that * are currently runnable at a user mode priority and bump the priority * if ts_dispwait exceeds ts_maxwait. Called once per second via * timeout which we reset here. * There are several lists of time sharing threads broken up by a hash on * the thread pointer. Each list has its own lock. This avoids blocking * all ts_enterclass, ts_fork, and ts_exitclass operations while ts_update * runs. ts_update traverses each list in turn. * If multiple threads have their priorities updated to the same value, * the system implicitly favors the one that is updated first (since it * winds up first on the run queue). To avoid this unfairness, the * traversal of threads starts at the list indicated by a marker. When * threads in more than one list have their priorities updated, the marker * is moved. This changes the order the threads will be placed on the run * queue the next time ts_update is called and preserves fairness over the * long run. The marker doesn't need to be protected by a lock since it's * only accessed by ts_update, which is inherently single-threaded (only * one instance can be running at a time). * Start with the ts_update_marker list, then do the rest. * If this is the first list after the current marker to * have threads with priorities updated, advance the marker * to this list for the next time ts_update runs. /* advance marker for next ts_update call */ * Updates priority for a list of threads. Returns 1 if the priority of * one of the threads was actually updated, 0 if none were for various * reasons (thread is no longer in the TS or IA class, isn't runnable, * hasn't waited long enough, has the preemption control no-preempt bit * Lock the thread and verify state. * Skip the thread if it is no longer in the TS (or IA) class. * Only dequeue it if needs to move; otherwise it should "update:tid %p old pri %d",
tx,
oldpri);
* Processes waking up go to the back of their queue. We don't * need to assign a time quantum here because thread is still * at a kernel mode priority and the time slicing is not done * for threads running in the kernel after sleeping. The proper * time quantum will be assigned by ts_trapret before the thread * Give thread a priority boost if we were asked. t->
t_trapret =
1;
/* so that ts_trapret will run */ * When a thread yields, put it on the back of the run queue. * Collect CPU usage spent before yielding * Clear the preemption control "yield" bit since the user is * If ts_preempt() artifically increased the thread's priority * to avoid preemption, restore the original priority now. * Time slice was artificially extended to avoid * preemption, so pretend we're preempting it now. * Increment the nice value of the specified thread by incr and * return the new value in *retvalp. /* If there's no change to priority, just return current setting */ * Specifying a nice increment greater than the upper limit of * 2 * NZERO - 1 will result in the thread's nice value being * set to the upper limit. We check for this before computing * the new value because otherwise we could get overflow * if a privileged process specified some ridiculous increment. * Reset the uprilim and upri values of the thread. * Call ts_parmsset even if thread is interactive since we're * Although ts_parmsset already reset ts_nice it may * not have been set to precisely the value calculated above * because ts_parmsset determines the nice value from the * user priority and we may have truncated during the integer * conversion from nice value to user priority and back. * We reset ts_nice to the value we calculated above. * Increment the priority of the specified thread by incr and * return the new value in *retvalp. /* If there's no change to the priority, just return current setting */ * Reset the uprilim and upri values of the thread. * Call ts_parmsset even if thread is interactive since we're * ia_set_process_group marks foreground processes as interactive * and background processes as non-interactive iff the session * leader is interactive. This routine is called from two places: * strioctl:SPGRP when a new process group gets * ia_parmsset-when the process in question is a session leader. * ia_set_process_group assumes that pidlock is held by the caller, * either strioctl or priocntlsys. If the caller is priocntlsys * (via ia_parmsset) then the p_lock of the session leader is held * and the code needs to be careful about acquiring other p_locks. * see if the session leader is interactive AND * if it is currently "on" AND controlling a tty * iff it is then make the processes in the foreground * group interactive and the processes in the background * XXX do all the threads in the leader * session leaders that are not interactive need not have * any processing done for them. They are typically shells * that do not have focus and are changing the process group * attatched to the tty, e.g. a process that is exiting * If we're already holding the leader's p_lock, we should use * mutex_tryenter instead of mutex_enter to avoid deadlocks from * lock ordering violations. * now look for all processes in the foreground group and * if the process is SIDL it's begin forked, ignore it * sesssion leaders must be turned on/off explicitly * not implicitly as happens to other members of * if this thread is not interactive continue * sesssion leaders must be turned off explicitly * not implicitly as happens to other members of "group off:proc %p",
bg);
* if this thread is not interactive continue /* curthread is always onproc */ * When the priority of a thread is changed, * it may be necessary to adjust its position * on a sleep queue or dispatch queue. * The function thread_change_pri accomplishes * The thread was on a run queue. Reset * its CPU timeleft from the quantum * associated with the new priority.