diff options
Diffstat (limited to 'kern/syscall_subr.c')
-rw-r--r-- | kern/syscall_subr.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/kern/syscall_subr.c b/kern/syscall_subr.c new file mode 100644 index 0000000..a040d71 --- /dev/null +++ b/kern/syscall_subr.c @@ -0,0 +1,399 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University. + * Copyright (c) 1993,1994 The University of Utah and + * the Computer Systems Laboratory (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON, THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF + * THIS SOFTWARE IN ITS "AS IS" CONDITION, AND DISCLAIM ANY LIABILITY + * OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF + * THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +#include <mach_fixpri.h> +#include <cpus.h> + +#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/ipc_sched.h> +#include <kern/task.h> +#include <kern/thread.h> +#include <kern/time_out.h> +#include <machine/machspl.h> /* for splsched */ + +#if MACH_FIXPRI +#include <mach/policy.h> +#endif MACH_FIXPRI + + + +/* + * swtch and swtch_pri both attempt 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. + */ + +extern void thread_depress_priority(); +extern kern_return_t thread_depress_abort(); + +#ifdef CONTINUATIONS +void swtch_continue() +{ + register processor_t myprocessor; + + myprocessor = current_processor(); + thread_syscall_return(myprocessor->runq.count > 0 || + myprocessor->processor_set->runq.count > 0); + /*NOTREACHED*/ +} +#else /* not CONTINUATIONS */ +#define swtch_continue 0 +#endif /* not CONTINUATIONS */ + +boolean_t swtch() +{ + register processor_t myprocessor; + +#if NCPUS > 1 + myprocessor = current_processor(); + if (myprocessor->runq.count == 0 && + myprocessor->processor_set->runq.count == 0) + return(FALSE); +#endif NCPUS > 1 + + counter(c_swtch_block++); + thread_block(swtch_continue); + myprocessor = current_processor(); + return(myprocessor->runq.count > 0 || + myprocessor->processor_set->runq.count > 0); +} + +#ifdef CONTINUATIONS +void swtch_pri_continue() +{ + register thread_t thread = current_thread(); + register processor_t myprocessor; + + if (thread->depress_priority >= 0) + (void) thread_depress_abort(thread); + myprocessor = current_processor(); + thread_syscall_return(myprocessor->runq.count > 0 || + myprocessor->processor_set->runq.count > 0); + /*NOTREACHED*/ +} +#else /* not CONTINUATIONS */ +#define swtch_pri_continue 0 +#endif /* not CONTINUATIONS */ + +boolean_t swtch_pri(pri) + int pri; +{ + register thread_t thread = current_thread(); + register processor_t myprocessor; + +#ifdef lint + pri++; +#endif lint + +#if NCPUS > 1 + myprocessor = current_processor(); + if (myprocessor->runq.count == 0 && + myprocessor->processor_set->runq.count == 0) + return(FALSE); +#endif NCPUS > 1 + + /* + * XXX need to think about depression duration. + * XXX currently using min quantum. + */ + thread_depress_priority(thread, min_quantum); + + counter(c_swtch_pri_block++); + thread_block(swtch_pri_continue); + + if (thread->depress_priority >= 0) + (void) thread_depress_abort(thread); + myprocessor = current_processor(); + return(myprocessor->runq.count > 0 || + myprocessor->processor_set->runq.count > 0); +} + +extern int hz; + +#ifdef CONTINUATIONS +void thread_switch_continue() +{ + register thread_t cur_thread = current_thread(); + + /* + * Restore depressed priority + */ + if (cur_thread->depress_priority >= 0) + (void) thread_depress_abort(cur_thread); + thread_syscall_return(KERN_SUCCESS); + /*NOTREACHED*/ +} +#else /* not CONTINUATIONS */ +#define thread_switch_continue 0 +#endif /* not CONTINUATIONS */ + +/* + * 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 thread_switch(thread_name, option, option_time) +mach_port_t thread_name; +int option; +mach_msg_timeout_t option_time; +{ + register thread_t cur_thread = current_thread(); + register processor_t myprocessor; + ipc_port_t port; + + /* + * Process option. + */ + switch (option) { + case SWITCH_OPTION_NONE: + /* + * Nothing to do. + */ + break; + + case SWITCH_OPTION_DEPRESS: + /* + * Depress priority for given time. + */ + thread_depress_priority(cur_thread, option_time); + break; + + case SWITCH_OPTION_WAIT: + thread_will_wait_with_timeout(cur_thread, option_time); + break; + + default: + return(KERN_INVALID_ARGUMENT); + } + +#ifndef MIGRATING_THREADS /* XXX thread_run defunct */ + /* + * Check and act on thread hint if appropriate. + */ + if ((thread_name != 0) && + (ipc_port_translate_send(cur_thread->task->itk_space, + thread_name, &port) == KERN_SUCCESS)) { + /* port is locked, but it might not be active */ + + /* + * Get corresponding thread. + */ + if (ip_active(port) && (ip_kotype(port) == IKOT_THREAD)) { + register thread_t thread; + register spl_t s; + + thread = (thread_t) port->ip_kobject; + /* + * 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. + */ + s = splsched(); + thread_lock(thread); + if ((thread->processor_set == cur_thread->processor_set) + && (rem_runq(thread) != RUN_QUEUE_NULL)) { + /* + * Hah, got it!! + */ + thread_unlock(thread); + (void) splx(s); + ip_unlock(port); + /* XXX thread might disappear on us now? */ +#if MACH_FIXPRI + if (thread->policy == POLICY_FIXEDPRI) { + myprocessor = current_processor(); + myprocessor->quantum = thread->sched_data; + myprocessor->first_quantum = TRUE; + } +#endif MACH_FIXPRI + counter(c_thread_switch_handoff++); + thread_run(thread_switch_continue, thread); + /* + * Restore depressed priority + */ + if (cur_thread->depress_priority >= 0) + (void) thread_depress_abort(cur_thread); + + return(KERN_SUCCESS); + } + thread_unlock(thread); + (void) splx(s); + } + ip_unlock(port); + } +#endif /* not MIGRATING_THREADS */ + + /* + * 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). + */ +#if NCPUS > 1 + myprocessor = current_processor(); + if (myprocessor->processor_set->runq.count > 0 || + myprocessor->runq.count > 0) +#endif NCPUS > 1 + { + counter(c_thread_switch_block++); + thread_block(thread_switch_continue); + } + + /* + * Restore depressed priority + */ + if (cur_thread->depress_priority >= 0) + (void) thread_depress_abort(cur_thread); + return(KERN_SUCCESS); +} + +/* + * 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 +thread_depress_priority(thread, depress_time) +register thread_t thread; +mach_msg_timeout_t depress_time; +{ + unsigned int ticks; + spl_t s; + + /* convert from milliseconds to ticks */ + ticks = convert_ipc_timeout_to_ticks(depress_time); + + s = splsched(); + thread_lock(thread); + + /* + * If thread is already depressed, override previous depression. + */ + reset_timeout_check(&thread->depress_timer); + + /* + * Save current priority, then set priority and + * sched_pri to their lowest possible values. + */ + thread->depress_priority = thread->priority; + thread->priority = 31; + thread->sched_pri = 31; + if (ticks != 0) + set_timeout(&thread->depress_timer, ticks); + + thread_unlock(thread); + (void) splx(s); +} + +/* + * thread_depress_timeout: + * + * Timeout routine for priority depression. + */ +void +thread_depress_timeout(thread) +register thread_t thread; +{ + spl_t s; + + s = splsched(); + thread_lock(thread); + + /* + * If we lose a race with thread_depress_abort, + * then depress_priority might be -1. + */ + + if (thread->depress_priority >= 0) { + thread->priority = thread->depress_priority; + thread->depress_priority = -1; + compute_priority(thread, FALSE); + } + + thread_unlock(thread); + (void) splx(s); +} + +/* + * thread_depress_abort: + * + * Prematurely abort priority depression if there is one. + */ +kern_return_t +thread_depress_abort(thread) +register thread_t thread; +{ + spl_t s; + + if (thread == THREAD_NULL) + return(KERN_INVALID_ARGUMENT); + + s = splsched(); + thread_lock(thread); + + /* + * Only restore priority if thread is depressed. + */ + if (thread->depress_priority >= 0) { + reset_timeout_check(&thread->depress_timer); + thread->priority = thread->depress_priority; + thread->depress_priority = -1; + compute_priority(thread, FALSE); + } + + thread_unlock(thread); + (void) splx(s); + return(KERN_SUCCESS); +} |