Source to osfmk/kern/mk_sp.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @[email protected]
 */
/*
 * @[email protected]
 * 
 */

/***
 *** ??? The following lines were picked up when code was incorporated
 *** into this file from `kern/syscall_subr.c.'  These should be moved
 *** with the code if it moves again.  Otherwise, they should be trimmed,
 *** based on the files included above.
 ***/

#include <mach/boolean.h>
#include <mach/thread_switch.h>
#include <ipc/ipc_port.h>
#include <ipc/ipc_space.h>
#include <kern/counters.h>
#include <kern/ipc_kobject.h>
#include <kern/processor.h>
#include <kern/sched.h>
#include <kern/sched_prim.h>
#include <kern/spl.h>
#include <kern/task.h>
#include <kern/thread.h>
#include <kern/ast.h>
#include <mach/policy.h>

#include <kern/syscall_subr.h>
#include <mach/mach_host_server.h>
#include <mach/mach_syscalls.h>

/***
 *** ??? End of lines picked up when code was incorporated
 *** into this file from `kern/syscall_subr.c.'
 ***/

#include <kern/sf.h>
#include <kern/mk_sp.h>
#include <kern/misc_protos.h>
#include <kern/spl.h>
#include <kern/sched.h>
#include <kern/sched_prim.h>
#include <kern/assert.h>
#include <kern/thread.h>
#include <mach/mach_host_server.h>

/* Forwards */
void	_mk_sp_thread_depress_priority(
			sf_object_t			policy,
			mach_msg_timeout_t	depress_time);

/***
 *** ??? The next two files supply the prototypes for `thread_set_policy()'
 *** and `thread_policy.'  These routines cannot stay here if they are
 *** exported Mach system calls.
 ***/
#include <mach/thread_act_server.h>
#include <mach/host_priv_server.h>

/*
 * Vector containing standard scheduling policy operations
 */
sp_ops_t	mk_sp_ops = {
		    _mk_sp_init,
		    _mk_sp_enable_processor_set,
		    _mk_sp_disable_processor_set,
		    _mk_sp_enable_processor,
		    _mk_sp_disable_processor,
		    _mk_sp_thread_update_mpri,
		    _mk_sp_thread_unblock,
		    _mk_sp_thread_done,
		    _mk_sp_thread_begin,
		    _mk_sp_thread_dispatch,
		    _mk_sp_thread_attach,
		    _mk_sp_thread_detach,
		    _mk_sp_thread_processor,
		    _mk_sp_thread_processor_set,
		    _mk_sp_thread_set,
		    _mk_sp_thread_get,
		    _mk_sp_db_print_sched_stats,
		    _mk_sp_swtch_pri,
		    _mk_sp_thread_switch,
		    _mk_sp_thread_depress_abort,
		    _mk_sp_thread_depress_timeout,
		    _mk_sp_task_attach,
		    _mk_sp_task_detach,
		    _mk_sp_task_policy,
		    _mk_sp_task_set_policy,
		    _mk_sp_task_set_sched,
		    _mk_sp_task_get_sched,
		    _mk_sp_thread_runnable,
		    _mk_sp_alarm_expired
};

/* Forwards */
kern_return_t	thread_policy_common(
					thread_t		thread,
					int				policy,
					int				data,
					processor_set_t	pset);

void		_mk_sp_thread_dump(
					sched_thread_t	thread);

/*
 * Local variables
 */
#ifdef	MACH_ASSERT
boolean_t	mk_sp_first_thread = TRUE;
#endif	/* MACH_ASSERT */

/*
 * Counters to monitor policy-related events
 */
mach_counter_t	c_mk_sp_init = 0,
		c_mk_sp_enable_processor_set = 0,
		c_mk_sp_disable_processor_set = 0,
		c_mk_sp_enable_processor = 0,
		c_mk_sp_disable_processor = 0,
		c_mk_sp_thread_update_mpri = 0,
		c_mk_sp_thread_unblock = 0,
		c_mk_sp_thread_done = 0,
		c_mk_sp_thread_begin = 0,
		c_mk_sp_thread_dispatch = 0,
		c_mk_sp_thread_attach = 0,
		c_mk_sp_thread_detach = 0,
		c_mk_sp_thread_processor = 0,
		c_mk_sp_thread_processor_set = 0,
		c_mk_sp_thread_set = 0,
		c_mk_sp_thread_get = 0,
		c_mk_sp_db_print_sched_stats = 0,
		c_mk_sp_swtch_pri = 0,
		c_mk_sp_thread_switch = 0,
		c_mk_sp_thread_depress_abort = 0,
		c_mk_sp_thread_depress_timeout = 0,
		c_mk_sp_task_attach = 0,
		c_mk_sp_task_detach = 0,
		c_mk_sp_task_policy = 0,
		c_mk_sp_task_set_policy = 0,
		c_mk_sp_task_set_sched = 0,
		c_mk_sp_task_get_sched = 0,
		c_mk_sp_thread_runnable = 0,
		c_mk_sp_alarm_expired = 0;

/*
 * Table to convert computed priorities into allocated priority range.
 */
int	convert_pri[NRQS];

/*
 * Lowest (normal -- i.e., not idle and not depressed) priority
 */
int	lpri = -1;

natural_t	min_quantum_ms;

/*
 * Standard operations for MK Scheduling Policy
 */

sf_return_t
_mk_sp_init(
	sf_object_t			policy,
	int					policy_id,
	int					priority_mask_length,
	sf_priority_mask_t	priority_mask)
{
	int					i;
	int					first_metapri;
	int					last_metapri;

	counter(c_mk_sp_init++);

	/*
	 * Initialize fields in the structure
	 */
	policy->policy_id = policy_id;
	policy->priority_mask_length = priority_mask_length;
	policy->priority_mask = *priority_mask;
	/*** ??? should something more dynamic be done here for mask? ***/
	policy->sched_attributes_size = sizeof (mk_sp_attribute_struct_t);
	policy->sched_info_size = sizeof (mk_sp_info_struct_t);

	/*
	 * Initialize table that maps computed priority values into
	 * allocated metapriority values.
	 */

	/* Find first metapriority value allocated to this policy */
	first_metapri = MAXPRI - ffsbit(priority_mask->bitmap);
	/*** ??? how to test for no set bits? ***/

	/* Map computed priorities higher than this to first metapriority */
	for (i = MAXPRI; i > first_metapri; i--)
		convert_pri[i] = first_metapri;
 
	/* For each remaining computed priority value... */
	for (i = first_metapri; i >= 0; i--) {
		/* ... is it a metapriority value assigned to this policy ?*/
		if (testbit(MAXPRI - i, priority_mask->bitmap)) {
			/* Yes; just use it */
			convert_pri[i] = i;
			last_metapri = i;
		}
		else {
			/* No, bump it up to the last value assigned */
			convert_pri[i] = last_metapri;
		}
	}

	/* Remember lowest "normal" priority available to this policy */
	lpri = convert_pri[LPRI];

	min_quantum_ms = (1000 / hz) * min_quantum;

#if 0
	/* Initialize operation entries in "jump table" */
	/***
	 *** ??? These must be done elsewhere.  Otherwise, there is a
	 *** circularity: How does the framework find this init operation?
	 ***/
	policy->sp_ops = mk_sp_ops;

	policy->sp_ops.sp_enable_processor_set = sp_mk_enable_processor_set;
	policy->sp_ops.sp_disable_processor_set = sp_mk_disable_processor_set;
	policy->sp_ops.sp_enable_processor = sp_mk_enable_processor;
	policy->sp_ops.sp_disable_processor = sp_mk_disable_processor;
	policy->sp_ops.sp_thread_unblock = sp_mk_thread_unblock;
	policy->sp_ops.sp_thread_done = sp_mk_thread_done;
	policy->sp_ops.sp_thread_begin = sp_mk_thread_begin;
	policy->sp_ops.sp_thread_attach = sp_mk_thread_attach;
	policy->sp_ops.sp_thread_detach = sp_mk_thread_detach;
	policy->sp_ops.sp_thread_processor = sp_mk_thread_processor;
	policy->sp_ops.sp_thread_processor_set = sp_mk_thread_processor_set;
	policy->sp_ops.sp_thread_set = sp_mk_thread_set;
	policy->sp_ops.sp_thread_get = sp_mk_thread_get;
	policy->sp_ops.sp_db_print_sched_stats = sp_mk_db_print_sched_stats;
#endif

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_enable_processor_set(
	sf_object_t			policy,
	processor_set_t		processor_set)
{
	kern_return_t		result;

	counter(c_mk_sp_enable_processor_set++);

	result = processor_set_policy_enable(processor_set, policy->policy_id);
	assert(result == KERN_SUCCESS);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_disable_processor_set(
	sf_object_t			policy,
	processor_set_t		processor_set)
{
	kern_return_t		result;

	counter(c_mk_sp_disable_processor_set++);

	result = processor_set_policy_disable(
								processor_set, policy->policy_id, TRUE);
	assert(result == KERN_SUCCESS);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_enable_processor(
	sf_object_t		policy,
	processor_t		processor)
{
	counter(c_mk_sp_enable_processor++);

	panic("not yet implemented???");

	return(SF_FAILURE);
}

sf_return_t
_mk_sp_disable_processor(
	sf_object_t		policy,
	processor_t		processor)
{
	counter(c_mk_sp_disable_processor++);

	panic("not yet implemented???");

	return(SF_FAILURE);
}

sf_return_t
_mk_sp_thread_update_mpri(
	sf_object_t			policy,
	sched_thread_t		thread)
{
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_thread_update_mpri++);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	if (sp_info->sched_stamp != sched_tick)
		update_priority(thread);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_unblock(
	sf_object_t			policy,
	sched_thread_t		thread)
{
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_thread_unblock++);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	counter(sp_info->c_mk_sp_thread_unblock++);

	/*
	 * The following assert is removed because we're hacking the
	 * MK_SP_BLOCKED bit and removing it early to get SMP to work
	 * with this gosh darned scheduler.  The whole danged thing is lousy and 
	 * needs to be removed.  Hopefully for now, no one tries to
	 * unblock a really runnable guy - HACKALERT!!!
	 */		
				
	/* check for legal thread states */
#if 0
	assert(sp_info->th_state == MK_SP_BLOCKED ||
	       sp_info->th_state == MK_SP_ATTACHED); /* ??? work to remove*/
#endif
	/* indicate thread is now runnable */
	sp_info->th_state = MK_SP_RUNNABLE;

	/* place thread at end of appropriate run queue */
	thread_setrun(thread, TRUE, TAIL_Q);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_done(
	sf_object_t			policy,
	sched_thread_t		old_thread)
{
	processor_t			myprocessor = cpu_to_processor(cpu_number());
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_thread_done++);

	/*
	 * A running thread is being taken off a processor:
	 *
	 *   - update the thread's `unconsumed_quantum' field
	 *   - update the thread's state field
	 */

	/* get pointer to scheduling policy-specific data */
	assert(old_thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)old_thread->sp_info;

	counter(sp_info->c_mk_sp_thread_done++);
	assert((sp_info->c_mk_sp_thread_done
		   == sp_info->c_mk_sp_thread_begin + 1) ||
	       (sp_info->c_mk_sp_thread_done
		   == sp_info->c_mk_sp_thread_begin));

	sp_info->unconsumed_quantum = myprocessor->quantum;

	/* check thread's current state */
	assert(mk_sp_first_thread || (sp_info->th_state & MK_SP_RUNNABLE));
#ifdef	MACH_ASSERT
	mk_sp_first_thread = FALSE;
#endif	/* MACH_ASSERT */

	/* update thread's state field based on reason for stopping now */
	/*** ??? is this condition correct? should it be more selective? ***/
	/*** ??? if ((old_thread->reason != AST_NONE) ||
	    (old_thread->wait_event != NO_EVENT)) { ***/
	if (old_thread->state & TH_WAIT) {
		sp_info->th_state = MK_SP_BLOCKED;
	}

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_begin(
	sf_object_t			policy,
	sched_thread_t		thread)
{

	processor_t			myprocessor = cpu_to_processor(cpu_number());
	processor_set_t		pset;
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_thread_begin++);

	pset = myprocessor->processor_set;
	/*
	 * The designated thread is about to begin execution:
	 *
	 *   - update the processor's `quantum' field
	 */

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	counter(sp_info->c_mk_sp_thread_begin++);
	assert((sp_info->c_mk_sp_thread_done
		   == sp_info->c_mk_sp_thread_begin) ||
	       (sp_info->c_mk_sp_thread_done + 1
		   == sp_info->c_mk_sp_thread_begin));

	/* check for legal thread state */
	assert(sp_info->th_state == MK_SP_RUNNABLE);

	if (thread->policy & (POLICY_RR|POLICY_FIFO))
		myprocessor->quantum = sp_info->unconsumed_quantum;
	else
		myprocessor->quantum = (thread->bound_processor ?
										min_quantum : pset->set_quantum);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_dispatch(
	sf_object_t			policy,
	sched_thread_t		old_thread)
{
	mk_sp_info_t	sp_info;

	counter(c_mk_sp_thread_dispatch++);

	/* get pointer to scheduling policy-specific data */
	assert(old_thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)old_thread->sp_info;

	counter(sp_info->c_mk_sp_thread_dispatch++);

	if (sp_info->th_state & MK_SP_RUNNABLE) {
		if (old_thread->reason & AST_QUANTUM) {
			thread_setrun(old_thread, FALSE, TAIL_Q);
			sp_info->unconsumed_quantum = sp_info->sched_data;
		}
		else
			thread_setrun(old_thread, FALSE, HEAD_Q);
	}

	if (sp_info->th_state & MK_SP_ATTACHED) {
		/* indicate thread is now runnable */
		sp_info->th_state = MK_SP_RUNNABLE;

		/* place thread at end of appropriate run queue */
		thread_setrun(old_thread, FALSE, TAIL_Q);
	}

	return(SF_SUCCESS);
}

/*
 * Thread must already be locked.
 */
sf_return_t
_mk_sp_thread_attach(
	sf_object_t			policy,
	sched_thread_t		thread)
{
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_thread_attach++);

	/* Make sure there is a place for policy-specific state */
	assert(thread->sp_info != SP_INFO_NULL);

	sp_info = (mk_sp_info_t)thread->sp_info;
	sp_info->th_state = MK_SP_ATTACHED;
	/*** ??? Do these need to be done here? ***/
	/*** ??? If so, could fill from template for speed -- maybe ***/
	sp_info->priority = MINPRI;		/*** ??? ***/
	sp_info->max_priority = BASEPRI_USER;
	/*** ??? sp_info->sched_pri =
			(later - compute_priority); (for now, at least) ***/
	sp_info->sched_data = 0;
	sp_info->policy = policy->policy_id;	/*** ??? ***/
	sp_info->depress_priority = -1;
	sp_info->cpu_usage = 0;
	sp_info->sched_usage = 0;
	sp_info->sched_stamp = 0;		/*** ??? ***/
	sp_info->unconsumed_quantum = 0;	/*** ??? ***/

#ifdef	MACH_ASSERT
	sp_info->c_mk_sp_thread_attach = 1;
	sp_info->c_mk_sp_thread_detach = 0;
	sp_info->c_mk_sp_thread_begin = 0;
	sp_info->c_mk_sp_thread_done = 0;
	sp_info->c_mk_sp_thread_dispatch = 0;
	sp_info->c_mk_sp_thread_unblock = 0;
	sp_info->c_mk_sp_thread_set = 0;
	sp_info->c_mk_sp_thread_get = 0;
#endif	/* MACH_ASSERT */

	/* Reflect this policy in thread data structure */
	thread->policy = policy->policy_id;

	return(SF_SUCCESS);
}

/*
 * Check to make sure that thread is removed from run
 * queues and active execution; and clean up the
 * policy-specific data structure for thread.
 *
 * Thread must already be locked.
 */
sf_return_t
_mk_sp_thread_detach(
	sf_object_t			policy,
	sched_thread_t		thread)
{
	mk_sp_info_t		sp_info;
	struct run_queue	*rq;

	assert(thread->policy == policy->policy_id);

	counter(c_mk_sp_thread_detach++);
	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	counter(sp_info->c_mk_sp_thread_detach++);

	/* make sure that the thread has stopped executing */
	/*** ??? look at machine's version of RUN bit, too? ***/
	/***
	 *** Fix for SMP case.
	 ***/

	/* make sure that the thread is no longer on any run queue */
	if (thread->runq != RUN_QUEUE_NULL) {
		rq = rem_runq(thread);
		if (rq == RUN_QUEUE_NULL) {
			panic("mk_sp_thread_detach: missed thread");
		}
	}

	/* (optionally) clear policy-specific data structure for the thread */

	if (sp_info->depress_priority >= 0) {
		sp_info->priority = sp_info->depress_priority;
		sp_info->depress_priority = -1;
		if (thread_call_cancel(&thread->depress_timer))
			thread_call_enter(&thread->depress_timer);
	}

	/* clear the thread's policy field */
	thread->policy = POLICY_NULL;

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_processor(
	sf_object_t			policy,
	sched_thread_t		*thread,
	processor_t			processor)
{
	counter(c_mk_sp_thread_processor++);

	panic("not yet implemented???");

	return(SF_FAILURE);
}

sf_return_t
_mk_sp_thread_processor_set(
	sf_object_t			policy,
	sched_thread_t		thread,
	processor_set_t		processor_set)
{
	counter(c_mk_sp_thread_processor_set++);

	pset_add_thread(processor_set, thread);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_set(
	sf_object_t			policy,
	sched_thread_t		thread,
	sp_attributes_t		policy_attributes)
{
	mk_sp_info_t		sp_info;
	mk_sp_attributes_t	attributes;

	counter(c_mk_sp_thread_set++);

	/* check that attributes are for this policy */
	assert(policy_attributes->policy_id == policy->policy_id);

	attributes = (mk_sp_attributes_t)policy_attributes;

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	counter(sp_info->c_mk_sp_thread_set++);

	/* update scheduling parameters */
	sp_info->priority = attributes->priority;
	sp_info->max_priority = attributes->max_priority;
	sp_info->sched_data = attributes->sched_data;
	sp_info->unconsumed_quantum = attributes->unconsumed_quantum;

	/*
	 * No check is needed against the pset limit.  If the task could raise
	 * its priority above the limit, then it must have had permisssion.
	 */

	/*
	 * Determine thread's state.  (It may be an "older" thread
	 * that has just been associated with this policy.)
	 */
	if (thread->state & TH_WAIT) {
	    sp_info->th_state = MK_SP_BLOCKED;
	}

	/* recompute priority */
	sp_info->sched_stamp = sched_tick;
	compute_priority(thread, TRUE);

	return(SF_SUCCESS);
}

sf_return_t
_mk_sp_thread_get(
	sf_object_t				policy,
	sched_thread_t			thread,
	sp_attributes_t			policy_attributes,
	sp_attributes_size_t	size)
	/*
	 * Assumes that thread is locked.
	 */
	/***
	 *** ??? Does it assume `splsched()'?
	 ***/
{
	mk_sp_info_t		sp_info;
	mk_sp_attributes_t	attributes;

	counter(c_mk_sp_thread_get++);

	/* check size of attribute structure provided by caller */
	if (policy->sched_attributes_size > size)
		return(SF_FAILURE);

	attributes = (mk_sp_attributes_t)policy_attributes;


	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	counter(sp_info->c_mk_sp_thread_get++);

	/* copy scheduling attributes */
	attributes->policy_id = policy->policy_id;
	attributes->priority = sp_info->priority;
	attributes->max_priority = sp_info->max_priority;
	attributes->sched_data = sp_info->sched_data;
	attributes->unconsumed_quantum = sp_info->unconsumed_quantum;

	return(SF_SUCCESS);
}

int
_mk_sp_db_print_sched_stats(
	sf_object_t			policy,
	sched_thread_t		thread)
{
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_db_print_sched_stats++);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	/* print one field -- `sched_pri' */
	return (thread->sched_pri);
}

/*
 *	thread_priority:
 *
 *	Set priority (and possibly max priority) for thread.
 */
kern_return_t
thread_priority(
	thread_act_t		thr_act,
	int			priority,
	boolean_t		set_max)
{
	kern_return_t		result;
	thread_t		thread;

	if (thr_act == THR_ACT_NULL)
		return (KERN_INVALID_ARGUMENT);

	thread = act_lock_thread(thr_act);
	if (thread == THREAD_NULL || invalid_pri(priority)) {
		act_unlock_thread(thr_act);
		return (KERN_INVALID_ARGUMENT);
	}

	result = thread_priority_locked(thread, priority, set_max);
	act_unlock_thread(thr_act);

	return (result);
}

/*
 *	thread_priority_locked:
 *
 *	Kernel-internal work function for thread_priority().  Called
 *	with thread "properly locked" to ensure synchrony with RPC
 *	(see act_lock_thread()).
 */
kern_return_t
thread_priority_locked(
	thread_t		thread,
	int			priority,
	boolean_t		set_max)
{
	kern_return_t		result = KERN_SUCCESS;
	mk_sp_info_t		sched_info;
	spl_t			s;

	s = splsched();
	thread_lock(thread);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sched_info = (mk_sp_info_t)thread->sp_info;

	/*
	 *	Check for violation of max priority
	 */
	if (priority > sched_info->max_priority)
		result = KERN_FAILURE;
	else {
		/*
		 *	Set priorities.  If a depression is in progress,
		 *	change the priority to restore.
		 */
		if (sched_info->depress_priority >= 0) {
			sched_info->depress_priority = priority;
		}
		else {
			sched_info->priority = priority;
			compute_priority(thread, TRUE);

			/*
			 * If the current thread has changed its
			 * priority let the ast code decide whether
			 * a different thread should run.
			 */
			if (thread == current_thread())
				ast_on(AST_BLOCK);
		}

		if (set_max)
			sched_info->max_priority = priority;
	}

	thread_unlock(thread);
	splx(s);

	return(result);
}

/*
 *	thread_set_own_priority:
 *
 *	Internal use only; sets the priority of the calling thread.
 *	Will adjust max_priority if necessary.
 */
void
thread_set_own_priority(
	int		priority)
{
	thread_t	thread = current_thread();
	mk_sp_info_t	sched_info;
	spl_t		s;

	s = splsched();
	thread_lock(thread);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sched_info = (mk_sp_info_t)thread->sp_info;

	if (priority > sched_info->max_priority)
		sched_info->max_priority = priority;
	sched_info->priority = priority;
	compute_priority(thread, TRUE);

	thread_unlock(thread);
	splx(s);
}

/*
 *	thread_max_priority:
 *
 *	Reset the max priority for a thread.
 */
kern_return_t
thread_max_priority(
	thread_act_t	thr_act,
	processor_set_t	pset,
	int		max_priority)
{
	thread_t	thread;
	kern_return_t	result = KERN_SUCCESS;

	if (thr_act == THR_ACT_NULL)
		return(KERN_INVALID_ARGUMENT);

	thread = act_lock_thread(thr_act);
	if (thread == THREAD_NULL || pset == PROCESSOR_SET_NULL ||
			invalid_pri(max_priority)) {
		act_unlock_thread(thr_act);
		return(KERN_INVALID_ARGUMENT);
	}

	result = thread_max_priority_locked(thread, pset, max_priority);
	act_unlock_thread(thr_act);

	return (result);
}

/*
 *	thread_max_priority_locked:
 *
 *	Work function for thread_max_priority.
 *
 *	Called with all RPC-related locks held for thread (see
 *	act_lock_thread()).
 */
kern_return_t
thread_max_priority_locked(
	thread_t	thread,
	processor_set_t	pset,
	int		max_priority)
{
	kern_return_t	result = KERN_SUCCESS;
	mk_sp_info_t	sched_info;
	spl_t		s;

	s = splsched();
	thread_lock(thread);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sched_info = (mk_sp_info_t)thread->sp_info;

#if	MACH_HOST
	/*
	 *	Check for wrong processor set.
	 */
	if (pset != thread->processor_set)
		result = KERN_FAILURE;
	else {
#endif	/* MACH_HOST */
		sched_info->max_priority = max_priority;

		/*
		 *	Reset priority if it violates new max priority
		 */
		if (max_priority < sched_info->priority) {
			sched_info->priority = max_priority;

			compute_priority(thread, TRUE);
		}
		else if (sched_info->depress_priority >= 0 &&
			 	max_priority < sched_info->depress_priority)
			sched_info->depress_priority = max_priority;
#if	MACH_HOST
	}
#endif	/* MACH_HOST */

	thread_unlock(thread);
	splx(s);

	return(result);
}

/*
 *	thread_policy_common:
 *
 *	Set scheduling policy for thread. If pset == PROCESSOR_SET_NULL,
 * 	policy will be checked to make sure it is enabled.
 */
kern_return_t
thread_policy_common(
	thread_t	thread,
	int		policy,
	int		data,
	processor_set_t	pset)
{
	kern_return_t	result = KERN_SUCCESS;
	mk_sp_info_t	sched_info;
	register int	temp;
	spl_t		s;

	if (thread == THREAD_NULL || invalid_policy(policy))
		return(KERN_INVALID_ARGUMENT);

	s = splsched();
	thread_lock(thread);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sched_info = (mk_sp_info_t)thread->sp_info;

	/*
	 *	Check if changing policy.
	 */
	if (policy == thread->policy) {
	    /*
	     *	Just changing data.  This is meaningless for
	     *	timeshareing, quantum for fixed priority (but
	     *	has no effect until current quantum runs out).
	     */
	    if (policy == POLICY_FIFO || policy == POLICY_RR) {
		temp = data * 1000;
		if (temp % tick)
		    temp += tick;
		sched_info->sched_data = temp/tick;
		sched_info->unconsumed_quantum = temp/tick;
	    }
	}
	else {
	    /*
	     *	Changing policy.  Check if new policy is allowed.
	     */
	    if (pset == PROCESSOR_SET_NULL && 
			(thread->processor_set->policies & policy) == 0) {
		result = KERN_FAILURE;
	    }
	    else {
		if (pset != thread->processor_set) {
		    result = KERN_FAILURE;
		}
		else {
		    /*
		     *	Changing policy.  Save data and calculate new
		     *	priority.
		     */
		    thread->policy = policy;
		    sched_info->policy = policy;
		    if (policy == POLICY_FIFO || policy == POLICY_RR) {
			temp = data * 1000;
			if (temp % tick)
			    temp += tick;
			sched_info->sched_data = temp/tick;
			sched_info->unconsumed_quantum = temp/tick;
		    }
		    compute_priority(thread, TRUE);
		}
	    }
	}

	thread_unlock(thread);
	splx(s);

	return(result);
}

/*
 *	thread_set_policy
 *
 *	Set scheduling policy and parameters, both base and limit, for 
 *	the given thread. Policy can be any policy implemented by the
 *	processor set, whether enabled or not. 
 */
kern_return_t
thread_set_policy(
	thread_act_t			thr_act,
	processor_set_t			pset,
	policy_t			policy,
	policy_base_t			base,
	mach_msg_type_number_t		base_count,
	policy_limit_t			limit,
	mach_msg_type_number_t		limit_count)
{
	thread_t			thread;
	int 				max, bas, dat;
	kern_return_t			result = KERN_SUCCESS;

	if (thr_act == THR_ACT_NULL || pset == PROCESSOR_SET_NULL)
		return (KERN_INVALID_ARGUMENT);

	thread = act_lock_thread(thr_act);
	if (thread == THREAD_NULL) {
		act_unlock_thread(thr_act);
		return(KERN_INVALID_ARGUMENT);
	}

	if (pset != thread->processor_set) {
		act_unlock_thread(thr_act);
		return(KERN_FAILURE);
	}

	switch (policy) {
	case POLICY_RR:
	{
                policy_rr_base_t rr_base = (policy_rr_base_t) base;
                policy_rr_limit_t rr_limit = (policy_rr_limit_t) limit;

                if (base_count != POLICY_RR_BASE_COUNT ||
                    limit_count != POLICY_RR_LIMIT_COUNT) {
                        result = KERN_INVALID_ARGUMENT;
                        break;
                }
		dat = rr_base->quantum;
		bas = rr_base->base_priority;
		max = rr_limit->max_priority;
		if (invalid_pri(bas) || invalid_pri(max)) {
			result = KERN_INVALID_ARGUMENT;
			break;
		}
		break;
	}

	case POLICY_FIFO:
	{
                policy_fifo_base_t fifo_base = (policy_fifo_base_t) base;
                policy_fifo_limit_t fifo_limit = (policy_fifo_limit_t) limit;

                if (base_count != POLICY_FIFO_BASE_COUNT ||
                    limit_count != POLICY_FIFO_LIMIT_COUNT) {
                        result = KERN_INVALID_ARGUMENT;
                        break;
                }
		dat = 0;
		bas = fifo_base->base_priority;
		max = fifo_limit->max_priority;
		if (invalid_pri(bas) || invalid_pri(max)) {
			result = KERN_INVALID_ARGUMENT;
                        break;
                }
                break;
	}

	case POLICY_TIMESHARE:
        {
                policy_timeshare_base_t ts_base =
                                        (policy_timeshare_base_t) base;
                policy_timeshare_limit_t ts_limit =
                                        (policy_timeshare_limit_t) limit;

                if (base_count != POLICY_TIMESHARE_BASE_COUNT ||
                    limit_count != POLICY_TIMESHARE_LIMIT_COUNT) {
                        result = KERN_INVALID_ARGUMENT;
                        break;
                }
		dat = 0;
		bas = ts_base->base_priority;
		max = ts_limit->max_priority;
		if (invalid_pri(bas) || invalid_pri(max)) {
			result = KERN_INVALID_ARGUMENT;
                        break;
                }
                break;
	}

	default:
		result = KERN_INVALID_POLICY;
	}

	if (result != KERN_SUCCESS) {
		act_unlock_thread(thr_act);
		return(result);
	}

	/*	
	 *	We've already checked the inputs for thread_max_priority,
	 *	so it's safe to ignore the return value.
	 */
	(void) thread_max_priority_locked(thread, pset, max);	
	result = thread_priority_locked(thread, bas, FALSE);
	if (result == KERN_SUCCESS)
		result = thread_policy_common(thread, policy, dat, pset);
	act_unlock_thread(thr_act);
	return(result);
}


/*
 * 	thread_policy
 *
 *	Set scheduling policy and parameters, both base and limit, for
 *	the given thread. Policy must be a policy which is enabled for the
 *	processor set. Change contained threads if requested. 
 */
kern_return_t
thread_policy(
	thread_act_t			thr_act,
        policy_t			policy,
        policy_base_t			base,
	mach_msg_type_number_t		count,
        boolean_t			set_limit)
{
	thread_t			thread;
        processor_set_t			pset;
	mk_sp_info_t			sched_info;
        kern_return_t			result = KERN_SUCCESS;
	policy_limit_t			limit;
        policy_rr_limit_data_t		rr_limit;
        policy_fifo_limit_data_t	fifo_limit;
        policy_timeshare_limit_data_t	ts_limit;
	int				limcount;
	
	if (thr_act == THR_ACT_NULL)
		return (KERN_INVALID_ARGUMENT);

	thread = act_lock_thread(thr_act);
	pset = thread->processor_set;
	if (thread == THREAD_NULL || pset == PROCESSOR_SET_NULL) {
		act_unlock_thread(thr_act);
		return(KERN_INVALID_ARGUMENT);
	}

	if (invalid_policy(policy) || (pset->policies & policy) == 0) {
		act_unlock_thread(thr_act);
		return(KERN_INVALID_POLICY);
	}

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sched_info = (mk_sp_info_t)thread->sp_info;

	if (set_limit) {
		/*
	 	 * 	Set scheduling limits to base priority.
		 */
		switch (policy) {
		case POLICY_RR:
		{
                        policy_rr_base_t rr_base;

                        if (count != POLICY_RR_BASE_COUNT) {
                                result = KERN_INVALID_ARGUMENT;
				break;
			}
                        limcount = POLICY_RR_LIMIT_COUNT;
                        rr_base = (policy_rr_base_t) base;
                        rr_limit.max_priority = rr_base->base_priority;
                        limit = (policy_limit_t) &rr_limit;
			break;
		}

		case POLICY_FIFO:
                {
                       	policy_fifo_base_t fifo_base;

                       	if (count != POLICY_FIFO_BASE_COUNT) {
                                result = KERN_INVALID_ARGUMENT;
				break;
			}
                       	limcount = POLICY_FIFO_LIMIT_COUNT;
                        fifo_base = (policy_fifo_base_t) base;
                        fifo_limit.max_priority = fifo_base->base_priority;
                        limit = (policy_limit_t) &fifo_limit;
			break;
		}

		case POLICY_TIMESHARE:
                {
                        policy_timeshare_base_t ts_base;

                        if (count != POLICY_TIMESHARE_BASE_COUNT) {
                                result = KERN_INVALID_ARGUMENT;
				break;
			}
                        limcount = POLICY_TIMESHARE_LIMIT_COUNT;
                        ts_base = (policy_timeshare_base_t) base;
                        ts_limit.max_priority = ts_base->base_priority;
                        limit = (policy_limit_t) &ts_limit;
			break;
		}

		default:
			result = KERN_INVALID_POLICY;
			break;
		}

	} else {
		/*
		 *	Use current scheduling limits. Ensure that the
		 *	new base priority will not exceed current limits.
		 */
                switch (policy) {
                case POLICY_RR:
                {
                        policy_rr_base_t rr_base;

                        if (count != POLICY_RR_BASE_COUNT) {
                                result = KERN_INVALID_ARGUMENT;
				break;
			}
                        limcount = POLICY_RR_LIMIT_COUNT;
                        rr_base = (policy_rr_base_t) base;
			if (rr_base->base_priority >
					sched_info->max_priority) {
				result = KERN_POLICY_LIMIT;
				break;
			}
                        rr_limit.max_priority = sched_info->max_priority;
                        limit = (policy_limit_t) &rr_limit;
                        break;
		}

                case POLICY_FIFO:
                {
                        policy_fifo_base_t fifo_base;

                        if (count != POLICY_FIFO_BASE_COUNT) {
                                result = KERN_INVALID_ARGUMENT;
				break;
			}
                        limcount = POLICY_FIFO_LIMIT_COUNT;
                        fifo_base = (policy_fifo_base_t) base;
			if (fifo_base->base_priority >
					sched_info->max_priority) {
				result = KERN_POLICY_LIMIT;
				break;
			}
                        fifo_limit.max_priority = sched_info->max_priority;
                        limit = (policy_limit_t) &fifo_limit;
                        break;
		}

                case POLICY_TIMESHARE:
                {
                        policy_timeshare_base_t ts_base;

                        if (count != POLICY_TIMESHARE_BASE_COUNT) {
                                result = KERN_INVALID_ARGUMENT;
				break;
			}
                        limcount = POLICY_TIMESHARE_LIMIT_COUNT;
                        ts_base = (policy_timeshare_base_t) base;
			if (ts_base->base_priority >
					sched_info->max_priority) {
				result = KERN_POLICY_LIMIT;
				break;
			}
                        ts_limit.max_priority = sched_info->max_priority;
                        limit = (policy_limit_t) &ts_limit;
                        break;
		}

                default:
			result = KERN_INVALID_POLICY;
			break;
                }

	}

	act_unlock_thread(thr_act);

	if (result == KERN_SUCCESS)
	    result = thread_set_policy(thr_act, pset,
					 policy, base, count, limit, limcount);

	return(result);
}


/*
 *	Define shifts for simulating (5/8)**n
 */

shift_data_t	wait_shift[32] = {
	{1,1},{1,3},{1,-3},{2,-7},{3,5},{3,-5},{4,-8},{5,7},
	{5,-7},{6,-10},{7,10},{7,-9},{8,-11},{9,12},{9,-11},{10,-13},
	{11,14},{11,-13},{12,-15},{13,17},{13,-15},{14,-17},{15,19},{16,18},
	{16,-19},{17,22},{18,20},{18,-20},{19,26},{20,22},{20,-22},{21,-27}};

/*
 *	do_priority_computation:
 *
 *	Calculate new priority for thread based on its base priority plus
 *	accumulated usage.  PRI_SHIFT and PRI_SHIFT_2 convert from
 *	usage to priorities.  SCHED_SHIFT converts for the scaling
 *	of the sched_usage field by SCHED_SCALE.  This scaling comes
 *	from the multiplication by sched_load (thread_timer_delta)
 *	in sched.h.  sched_load is calculated as a scaled overload
 *	factor in compute_mach_factor (mach_factor.c).
 */
#ifdef	PRI_SHIFT_2
#if	PRI_SHIFT_2 > 0
#define do_priority_computation(sp_info, pri)						\
	MACRO_BEGIN														\
	(pri) = (sp_info)->priority		/* start with base priority */	\
	    - ((sp_info)->sched_usage >> (PRI_SHIFT + SCHED_SHIFT))		\
	    - ((sp_info)->sched_usage >> (PRI_SHIFT_2 + SCHED_SHIFT));	\
	if ((pri) < lpri) (pri) = lpri;									\
	(pri) = convert_pri[(pri)];										\
	MACRO_END
#else	/* PRI_SHIFT_2 */
#define do_priority_computation(sp_info, pri)						\
	MACRO_BEGIN														\
	(pri) = (sp_info)->priority		/* start with base priority */	\
	    - ((sp_info)->sched_usage >> (PRI_SHIFT + SCHED_SHIFT))		\
	    + ((sp_info)->sched_usage >> (SCHED_SHIFT - PRI_SHIFT_2));	\
	if ((pri) < lpri) (pri) = lpri;									\
	(pri) = convert_pri[(pri)];										\
	MACRO_END
#endif	/* PRI_SHIFT_2 */
#else	/* defined(PRI_SHIFT_2) */
#define do_priority_computation(sp_info, pri)						\
	MACRO_BEGIN														\
	(pri) = (sp_info)->priority		/* start with base priority */	\
	    - ((sp_info)->sched_usage >> (PRI_SHIFT + SCHED_SHIFT));	\
	if ((pri) < lpri) (pri) = lpri;									\
	(pri) = convert_pri[(pri)];										\
	MACRO_END
#endif	/* defined(PRI_SHIFT_2) */

/*
 *	compute_priority:
 *
 *	Compute the effective priority of the specified thread.
 *	The effective priority computation is as follows:
 *
 *	Take the base priority for this thread and add
 *	to it an increment derived from its cpu_usage.
 *
 *	The thread *must* be locked by the caller. 
 */

void
compute_priority(
	register thread_t	thread,
	boolean_t			resched)
{
	mk_sp_info_t		sp_info = (mk_sp_info_t)thread->sp_info;
	register int		pri;

	/* make sure policy-specific data are present */
	assert(sp_info != SP_INFO_NULL);

	if (thread->policy == POLICY_TIMESHARE) {
	    do_priority_computation(sp_info, pri);
	    if (sp_info->depress_priority < 0)
			set_pri(thread, pri, resched);
	    else
			sp_info->depress_priority = pri;
	}
	else
	    set_pri(thread, sp_info->priority, resched);
}

/*
 *	compute_my_priority:
 *
 *	Version of compute priority for current thread or thread
 *	being manipulated by scheduler (going on or off a runq).
 *	Only used for priority updates.  Policy or priority changes
 *	must call compute_priority above.  Caller must have thread
 *	locked and know it is timesharing and not depressed.
 */

void
compute_my_priority(
	register thread_t	thread)
{
	mk_sp_info_t	sp_info = (mk_sp_info_t)thread->sp_info;
	register int	temp_pri;

	/* make sure policy-specific data are present */
	assert(thread->sp_info != SP_INFO_NULL);

	do_priority_computation(sp_info,temp_pri);
	assert(thread->runq == RUN_QUEUE_NULL);
	thread->sched_pri = temp_pri;
}

struct mk_sp_usage {
	natural_t	cpu_delta, sched_delta;
	natural_t	sched_tick, ticks;
	natural_t	cpu_usage, sched_usage,
				aged_cpu, aged_sched;
	thread_t	thread;
} idled_info, loaded_info;

/*
 *	update_priority
 *
 *	Cause the priority computation of a thread that has been 
 *	sleeping or suspended to "catch up" with the system.  Thread
 *	*MUST* be locked by caller.  If thread is running, then this
 *	can only be called by the thread on itself.
 */
void
update_priority(
	register thread_t		thread)
{
	mk_sp_info_t			sp_info = (mk_sp_info_t)thread->sp_info;
	register unsigned int	ticks;
	register shift_t		shiftp;

	/* make sure policy-specific data are present */
	assert(sp_info != SP_INFO_NULL);

	ticks = sched_tick - sp_info->sched_stamp;
	assert(ticks != 0);

	/*
	 *	If asleep for more than 30 seconds forget all
	 *	cpu_usage, else catch up on missed aging.
	 *	5/8 ** n is approximated by the two shifts
	 *	in the wait_shift array.
	 */
	sp_info->sched_stamp += ticks;
	thread_timer_delta(thread);
	if (ticks >  30) {
		sp_info->cpu_usage = 0;
		sp_info->sched_usage = 0;
	}
	else {
		struct mk_sp_usage *sp_usage;

		sp_info->cpu_usage += thread->cpu_delta;
		sp_info->sched_usage += thread->sched_delta;

		if (thread->state & TH_IDLE)
			sp_usage = &idled_info;
		else
		if (thread == loaded_info.thread)
			sp_usage = &loaded_info;
		else
			sp_usage = NULL;

		if (sp_usage != NULL) {
			sp_usage->cpu_delta = thread->cpu_delta;
			sp_usage->sched_delta = thread->sched_delta;
			sp_usage->sched_tick = sp_info->sched_stamp;
			sp_usage->ticks = ticks;
			sp_usage->cpu_usage = sp_info->cpu_usage;
			sp_usage->sched_usage = sp_info->sched_usage;
			sp_usage->thread = thread;
		}

		shiftp = &wait_shift[ticks];
		if (shiftp->shift2 > 0) {
		    sp_info->cpu_usage =
						(sp_info->cpu_usage >> shiftp->shift1) +
						(sp_info->cpu_usage >> shiftp->shift2);
		    sp_info->sched_usage =
						(sp_info->sched_usage >> shiftp->shift1) +
						(sp_info->sched_usage >> shiftp->shift2);
		}
		else {
		    sp_info->cpu_usage =
						(sp_info->cpu_usage >> shiftp->shift1) -
						(sp_info->cpu_usage >> -(shiftp->shift2));
		    sp_info->sched_usage =
						(sp_info->sched_usage >> shiftp->shift1) -
						(sp_info->sched_usage >> -(shiftp->shift2));
		}

		if (sp_usage != NULL) {
			sp_usage->aged_cpu = sp_info->cpu_usage;
			sp_usage->aged_sched = sp_info->sched_usage;
		}
	}
	thread->cpu_delta = 0;
	thread->sched_delta = 0;

	/*
	 *	Recompute priority if appropriate.
	 */
	if (	thread->policy == POLICY_TIMESHARE		&&
			sp_info->depress_priority < 0			) {
		register int		new_pri;
		run_queue_t			runq;

		do_priority_computation(sp_info, new_pri);
		runq = rem_runq(thread);
		thread->sched_pri = new_pri;
		if (runq != RUN_QUEUE_NULL)
			thread_setrun(thread, TRUE, TAIL_Q);
	}
}

/*
 *	`mk_sp_swtch_pri()' attempts to context switch (logic in
 *	thread_block no-ops the context switch if nothing would happen).
 *	A boolean is returned that indicates whether there is anything
 *	else runnable.
 *
 *	This boolean can be used by a thread waiting on a
 *	lock or condition:  If FALSE is returned, the thread is justified
 *	in becoming a resource hog by continuing to spin because there's
 *	nothing else useful that the processor could do.  If TRUE is
 *	returned, the thread should make one more check on the
 *	lock and then be a good citizen and really suspend.
 */

void
_mk_sp_swtch_pri(
	sf_object_t			policy,
	int					pri)
{
	register thread_t	self = current_thread();

#ifdef	lint
	pri++;
#endif	/* lint */

	counter(c_mk_sp_swtch_pri++);
	/*
	 *	XXX need to think about depression duration.
	 *	XXX currently using min quantum.
	 */
	_mk_sp_thread_depress_priority(policy, min_quantum_ms);

	counter(c_swtch_pri_block++);
	thread_block((void (*)(void)) 0);

	_mk_sp_thread_depress_abort(policy, self);
}

/*
 *	thread_switch:
 *
 *	Context switch.  User may supply thread hint.
 *
 *	Fixed priority threads that call this get what they asked for
 *	even if that violates priority order.
 */
kern_return_t
_mk_sp_thread_switch(
	sf_object_t				policy,
	thread_act_t			hint_act,
	int						option,
	mach_msg_timeout_t		option_time)
{
    register thread_t		self = current_thread();
    register processor_t	myprocessor;
    mk_sp_info_t			sp_info;
	int						s;

    counter(c_mk_sp_thread_switch++);

    /*
     *	Process option.
     */
    switch (option) {

	case SWITCH_OPTION_NONE:
	    /*
	     *	Nothing to do.
	     */
	    break;

	case SWITCH_OPTION_DEPRESS:
	    /*
	     *	Depress priority for a given time.
	     */
	    _mk_sp_thread_depress_priority(policy, option_time);
	    break;

	case SWITCH_OPTION_WAIT:
	    assert_wait_timeout(option_time, THREAD_ABORTSAFE);
	    break;

	default:
	    return (KERN_INVALID_ARGUMENT);
    }
    
    /*
     *	Check and use thr_act hint if appropriate.  It is not
     *  appropriate to give a hint that shares the current shuttle.
     *
     *  JMM - Is it really appropriate to give a hint that isn't
     *  the top activation for a thread?
     */
    if (hint_act->thread != self) {
    	register thread_t		thread = hint_act->thread;
	
		/*
		 *	Check if the thread is in the right pset. Then
		 *	pull it off its run queue.  If it
		 *	doesn't come, then it's not eligible.
		 */
		if (thread) {
			s = splsched();
			thread_lock(thread);

			if (	thread->processor_set == self->processor_set	&&
					rem_runq(thread) != RUN_QUEUE_NULL					) {
				/*
				 *	Hah, got it!!
				 */
				if (thread->policy & (POLICY_FIFO|POLICY_RR)) {
					myprocessor = current_processor();

					assert(thread->sp_info != SP_INFO_NULL);
					sp_info = (mk_sp_info_t)thread->sp_info;

					myprocessor->quantum = sp_info->unconsumed_quantum;
					myprocessor->first_quantum = TRUE;
				}
				thread_unlock(thread);
				splx(s);
				act_unlock_thread(hint_act);
				
				counter(c_thread_switch_handoff++);
				thread_run((void(*)(void)) 0, thread);

				goto out;
			}

			thread_unlock(thread);
			splx(s);
		}

		act_unlock_thread(hint_act);
    }

    /*
     *	No handoff hint supplied, or hint was wrong.  Call thread_block() in
     *	hopes of running something else.  If nothing else is runnable,
     *	thread_block will detect this.  WARNING: thread_switch with no
     *	option will not do anything useful if the thread calling it is the
     *	highest priority thread (can easily happen with a collection
     *	of timesharing threads).
     */
	mp_disable_preemption();
    myprocessor = current_processor();
    if (	option != SWITCH_OPTION_NONE					||
			myprocessor->processor_set->runq.count > 0		||
			myprocessor->runq.count > 0							) {
		myprocessor->first_quantum = FALSE;
		mp_enable_preemption();
	  
		counter(c_thread_switch_block++);
		thread_block((void (*)(void)) 0);
	}
	else
		mp_enable_preemption();

out:
	if (option == SWITCH_OPTION_WAIT)
		thread_cancel_timer();
	else if (option == SWITCH_OPTION_DEPRESS)
		_mk_sp_thread_depress_abort(policy, self);

    return (KERN_SUCCESS);
}

/*
 *	mk_sp_thread_depress_priority
 *
 *	Depress thread's priority to lowest possible for specified period.
 *	Intended for use when thread wants a lock but doesn't know which
 *	other thread is holding it.  As with thread_switch, fixed
 *	priority threads get exactly what they asked for.  Users access
 *	this by the SWITCH_OPTION_DEPRESS option to thread_switch.  A Time
 *      of zero will result in no timeout being scheduled.
 */
void
_mk_sp_thread_depress_priority(
	sf_object_t				policy,
	mach_msg_timeout_t		interval)
{
	register thread_t		self = current_thread();
    mk_sp_info_t			sp_info;
	AbsoluteTime			deadline;
	boolean_t				release = FALSE;
    spl_t					s;

    s = splsched();
    thread_lock(self);

	if (self->policy == policy->policy_id) {
		/* get pointer to scheduling policy-specific data */
		assert(self->sp_info != SP_INFO_NULL);
		sp_info = (mk_sp_info_t)self->sp_info;

		/*
		 * If we haven't already saved the priority to be restored
		 * (depress_priority), then save it.
		 */
		if (sp_info->depress_priority < 0)
			sp_info->depress_priority = sp_info->priority;
		else if (thread_call_cancel(&self->depress_timer))
			release = TRUE;

		self->sched_pri = sp_info->priority = DEPRESSPRI;

		if (interval != 0) {
			clock_interval_to_deadline(
								interval, 1000*NSEC_PER_USEC, &deadline);
			thread_call_enter_delayed(&self->depress_timer, deadline);
			if (!release)
				self->ref_count++;
			else
				release = FALSE;
		}
	}

    thread_unlock(self);
    splx(s);

	if (release)
		thread_deallocate(self);
}	

/*
 *	mk_sp_thread_depress_timeout:
 *
 *	Timeout routine for priority depression.
 */
void
_mk_sp_thread_depress_timeout(
	sf_object_t				policy,
	register thread_t		thread)
{
    mk_sp_info_t			sp_info;
    spl_t					s;

    counter(c_mk_sp_thread_depress_timeout++);

    s = splsched();
    thread_lock(thread);
	if (thread->policy == policy->policy_id) {
		/* get pointer to scheduling policy-specific data */
		assert(thread->sp_info != SP_INFO_NULL);
		sp_info = (mk_sp_info_t)thread->sp_info;

		/*
		 *	If we lose a race with mk_sp_thread_depress_abort,
		 *	then depress_priority might be -1.
		 */
		if (	sp_info->depress_priority >= 0						&&
				!thread_call_is_delayed(&thread->depress_timer, NULL)		) {
			sp_info->priority = sp_info->depress_priority;
			sp_info->depress_priority = -1;
			compute_priority(thread, FALSE);
		}
		else
		if (sp_info->depress_priority == -2) {
			/*
			 * Thread was temporarily undepressed by thread_suspend, to
			 * be redepressed in special_handler as it blocks.  We need to
			 * prevent special_handler from redepressing it, since depression
			 * has timed out:
			 */
			sp_info->depress_priority = -1;
		}
	}
	thread_unlock(thread);
	splx(s);
}

/*
 *	mk_sp_thread_depress_abort:
 *
 *	Prematurely abort priority depression if there is one.
 */
kern_return_t
_mk_sp_thread_depress_abort(
	sf_object_t				policy,
	register thread_t		thread)
{
    mk_sp_info_t			sp_info;
    kern_return_t 			result = KERN_SUCCESS;
	boolean_t				release = FALSE;
    spl_t					s;

    s = splsched();
    thread_lock(thread);

	if (thread->policy == policy->policy_id) {
		/* get pointer to scheduling policy-specific data */
		assert(thread->sp_info != SP_INFO_NULL);
		sp_info = (mk_sp_info_t)thread->sp_info;

		if (sp_info->depress_priority >= 0) {
			if (thread_call_cancel(&thread->depress_timer))
				release = TRUE;
			sp_info->priority = sp_info->depress_priority;
			sp_info->depress_priority = -1;
			compute_priority(thread, FALSE);
		}
		else
			result = KERN_NOT_DEPRESSED;
	}

    thread_unlock(thread);
    splx(s);

	if (release)
		thread_deallocate(thread);

    return (result);
}

/*
 *	mk_sp_thread_runnable:
 *
 *	Return TRUE iff policy believes thread is runnable
 */
boolean_t
_mk_sp_thread_runnable(
	sf_object_t			policy,
	sched_thread_t		thread)
{
	mk_sp_info_t		sp_info;

	counter(c_mk_sp_thread_runnable++);

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	counter(sp_info->c_mk_sp_thread_runnable++);

	return (sp_info->th_state == MK_SP_RUNNABLE);
}

sf_return_t
_mk_sp_alarm_expired(
	sf_object_t			policy,
	long				alarm_seqno,
	kern_return_t		result,
	int					alarm_type,
	mach_timespec_t		wakeup_time,
	void				*alarm_data)
{
	counter(c_mk_sp_alarm_expired++);

	/* this should NEVER happen */
	assert(0);

	return (SF_FAILURE);
}


void
_mk_sp_thread_dump(
	sched_thread_t		thread)		/* thread */
{
	mk_sp_info_t		sp_info;
	int			count;

	/* get pointer to scheduling policy-specific data */
	assert(thread->sp_info != SP_INFO_NULL);
	sp_info = (mk_sp_info_t)thread->sp_info;

	/* dump the data in a pretty format */
	printf("\nthread shuttle(0x%x):\n", thread);

	printf("\tpolicy: ");
	switch(thread->policy) {
		case POLICY_TIMESHARE:
			printf("POLICY_TIMESHARE\n");
			break;

		case POLICY_RR:
			printf("POLICY_RR\n");
			break;

		case POLICY_FIFO:
			printf("POLICY_FIFO\n");
			break;

		default:
			printf("NOT an MK Standard Policy\n");
			return;
	}

	printf("\tth_state: ");
	if (sp_info->th_state == 0) {
		printf("NULL");
	}
	else {
		count = 0;
		if (sp_info->th_state & MK_SP_ATTACHED) {
			printf("MK_SP_ATTACHED ");
			count++;
		}
		if (sp_info->th_state & MK_SP_RUNNABLE) {
			if (count > 0) printf(" | ");
			printf("MK_SP_RUNNABLE ");
			count++;
		}
		if (sp_info->th_state & MK_SP_BLOCKED) {
			if (count > 0) printf(" | ");
			printf("MK_SP_BLOCKED ");
			count++;
		}
	}
	printf("\n");

	printf("\tpriority: %d\n", sp_info->priority);
	printf("\tmax_priority: %d\n", sp_info->max_priority);
	printf("\tsched_data: %d\n", sp_info->sched_data);
	printf("\tdepress_priority: %d\n", sp_info->depress_priority);
	printf("\tcpu_usage: %d\n", sp_info->cpu_usage);
	printf("\tsched_usage: %d\n", sp_info->sched_usage);
	printf("\tsched_stamp: %d\n", sp_info->sched_stamp);
	printf("\tunconsumed_quantum: %d\n", sp_info->unconsumed_quantum);
	printf("\tsched_pri: %d\n", thread->sched_pri);

#ifdef	MACH_ASSERT
	printf("\tc_mk_sp_thread_attach: %d\n",
	       sp_info->c_mk_sp_thread_attach);
	printf("\tc_mk_sp_thread_detach: %d\n",
	       sp_info->c_mk_sp_thread_detach);
	printf("\tc_mk_sp_thread_begin: %d\n",
	       sp_info->c_mk_sp_thread_begin);
	printf("\tc_mk_sp_thread_done: %d\n",
	       sp_info->c_mk_sp_thread_done);
	printf("\tc_mk_sp_thread_dispatch: %d\n",
	       sp_info->c_mk_sp_thread_dispatch);
	printf("\tc_mk_sp_thread_unblock: %d\n",
	       sp_info->c_mk_sp_thread_unblock);
	printf("\tc_mk_sp_thread_set: %d\n",
	       sp_info->c_mk_sp_thread_set);
	printf("\tc_mk_sp_thread_get: %d\n",
	       sp_info->c_mk_sp_thread_get);
#endif	/* MACH_ASSERT */

	return;
}