From f07a4c844da9f0ecae5bbee1ab94be56505f26f7 Mon Sep 17 00:00:00 2001 From: Thomas Bushnell Date: Tue, 25 Feb 1997 21:28:37 +0000 Subject: Initial source --- kern/task.c | 1238 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1238 insertions(+) create mode 100644 kern/task.c (limited to 'kern/task.c') diff --git a/kern/task.c b/kern/task.c new file mode 100644 index 0000000..f72bb0f --- /dev/null +++ b/kern/task.c @@ -0,0 +1,1238 @@ +/* + * Mach Operating System + * Copyright (c) 1993-1988 Carnegie Mellon University + * 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 ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS 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. + */ +/* + * File: kern/task.c + * Author: Avadis Tevanian, Jr., Michael Wayne Young, David Golub, + * David Black + * + * Task management primitives implementation. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for thread_wakeup */ +#include +#include /* for kernel_map, ipc_kernel_map */ +#include /* for splsched */ + +#if NET_ATM +#include +#endif + +#if NORMA_TASK +#define task_create task_create_local +#endif /* NORMA_TASK */ + +task_t kernel_task = TASK_NULL; +zone_t task_zone; + +extern void eml_init(void); +extern void eml_task_reference(task_t, task_t); +extern void eml_task_deallocate(task_t); + +void task_init(void) +{ + task_zone = zinit( + sizeof(struct task), + TASK_MAX * sizeof(struct task), + TASK_CHUNK * sizeof(struct task), + 0, "tasks"); + + eml_init(); + + /* + * Create the kernel task as the first task. + * Task_create must assign to kernel_task as a side effect, + * for other initialization. (:-() + */ + (void) task_create(TASK_NULL, FALSE, &kernel_task); +} + +/* + * Create a task running in the kernel address space. It may + * have its own map of size mem_size (if 0, it uses the kernel map), + * and may have ipc privileges. + */ +task_t kernel_task_create( + task_t parent_task, + vm_size_t map_size) +{ + task_t new_task; + vm_offset_t min, max; + + /* + * Create the task. + */ + (void) task_create(parent_task, FALSE, &new_task); + + /* + * Task_create creates the task with a user-space map. + * Remove the map and replace it with the kernel map + * or a submap of the kernel map. + */ + vm_map_deallocate(new_task->map); + if (map_size == 0) + new_task->map = kernel_map; + else + new_task->map = kmem_suballoc(kernel_map, &min, &max, + map_size, FALSE); + + return new_task; +} + +kern_return_t task_create( + task_t parent_task, + boolean_t inherit_memory, + task_t *child_task) /* OUT */ +{ + register task_t new_task; + register processor_set_t pset; + int i; + + new_task = (task_t) zalloc(task_zone); + if (new_task == TASK_NULL) { + panic("task_create: no memory for task structure"); + } + + /* one ref for just being alive; one for our caller */ + new_task->ref_count = 2; + + if (child_task == &kernel_task) { + new_task->map = kernel_map; + } else if (inherit_memory) { + new_task->map = vm_map_fork(parent_task->map); + } else { + new_task->map = vm_map_create(pmap_create(0), + round_page(VM_MIN_ADDRESS), + trunc_page(VM_MAX_ADDRESS), TRUE); + } + + simple_lock_init(&new_task->lock); + queue_init(&new_task->thread_list); + new_task->suspend_count = 0; + new_task->active = TRUE; + new_task->user_stop_count = 0; + new_task->thread_count = 0; + + eml_task_reference(new_task, parent_task); + + ipc_task_init(new_task, parent_task); + +#if NET_ATM + new_task->nw_ep_owned = 0; +#endif + + new_task->total_user_time.seconds = 0; + new_task->total_user_time.microseconds = 0; + new_task->total_system_time.seconds = 0; + new_task->total_system_time.microseconds = 0; + + if (parent_task != TASK_NULL) { + task_lock(parent_task); + pset = parent_task->processor_set; + if (!pset->active) + pset = &default_pset; + pset_reference(pset); + new_task->priority = parent_task->priority; + task_unlock(parent_task); + } + else { + pset = &default_pset; + pset_reference(pset); + new_task->priority = BASEPRI_USER; + } + pset_lock(pset); + pset_add_task(pset, new_task); + pset_unlock(pset); + + new_task->may_assign = TRUE; + new_task->assign_active = FALSE; + +#if MACH_PCSAMPLE + new_task->pc_sample.buffer = 0; + new_task->pc_sample.seqno = 0; + new_task->pc_sample.sampletypes = 0; +#endif /* MACH_PCSAMPLE */ + +#if FAST_TAS + for (i = 0; i < TASK_FAST_TAS_NRAS; i++) { + if (inherit_memory) { + new_task->fast_tas_base[i] = parent_task->fast_tas_base[i]; + new_task->fast_tas_end[i] = parent_task->fast_tas_end[i]; + } else { + new_task->fast_tas_base[i] = (vm_offset_t)0; + new_task->fast_tas_end[i] = (vm_offset_t)0; + } + } +#endif /* FAST_TAS */ + + ipc_task_enable(new_task); + +#if NORMA_TASK + new_task->child_node = -1; +#endif /* NORMA_TASK */ + + *child_task = new_task; + return KERN_SUCCESS; +} + +/* + * task_deallocate: + * + * Give up a reference to the specified task and destroy it if there + * are no other references left. It is assumed that the current thread + * is never in this task. + */ +void task_deallocate( + register task_t task) +{ + register int c; + register processor_set_t pset; + + if (task == TASK_NULL) + return; + + task_lock(task); + c = --(task->ref_count); + task_unlock(task); + if (c != 0) + return; + +#if NORMA_TASK + if (task->map == VM_MAP_NULL) { + /* norma placeholder task */ + zfree(task_zone, (vm_offset_t) task); + return; + } +#endif /* NORMA_TASK */ + + eml_task_deallocate(task); + + pset = task->processor_set; + pset_lock(pset); + pset_remove_task(pset,task); + pset_unlock(pset); + pset_deallocate(pset); + vm_map_deallocate(task->map); + is_release(task->itk_space); + zfree(task_zone, (vm_offset_t) task); +} + +void task_reference( + register task_t task) +{ + if (task == TASK_NULL) + return; + + task_lock(task); + task->ref_count++; + task_unlock(task); +} + +/* + * task_terminate: + * + * Terminate the specified task. See comments on thread_terminate + * (kern/thread.c) about problems with terminating the "current task." + */ +kern_return_t task_terminate( + register task_t task) +{ + register thread_t thread, cur_thread; + register queue_head_t *list; + register task_t cur_task; + spl_t s; + + if (task == TASK_NULL) + return KERN_INVALID_ARGUMENT; + + list = &task->thread_list; + cur_task = current_task(); + cur_thread = current_thread(); + +#if NET_ATM + /* + * Shut down networking. + */ + mk_endpoint_collect(task); +#endif + + /* + * Deactivate task so that it can't be terminated again, + * and so lengthy operations in progress will abort. + * + * If the current thread is in this task, remove it from + * the task's thread list to keep the thread-termination + * loop simple. + */ + if (task == cur_task) { + task_lock(task); + if (!task->active) { + /* + * Task is already being terminated. + */ + task_unlock(task); + return KERN_FAILURE; + } + /* + * Make sure current thread is not being terminated. + */ + s = splsched(); + thread_lock(cur_thread); + if (!cur_thread->active) { + thread_unlock(cur_thread); + (void) splx(s); + task_unlock(task); + thread_terminate(cur_thread); + return KERN_FAILURE; + } + task->active = FALSE; + queue_remove(list, cur_thread, thread_t, thread_list); + thread_unlock(cur_thread); + (void) splx(s); + task_unlock(task); + + /* + * Shut down this thread's ipc now because it must + * be left alone to terminate the task. + */ + ipc_thread_disable(cur_thread); + ipc_thread_terminate(cur_thread); + } + else { + /* + * Lock both current and victim task to check for + * potential deadlock. + */ + if ((vm_offset_t)task < (vm_offset_t)cur_task) { + task_lock(task); + task_lock(cur_task); + } + else { + task_lock(cur_task); + task_lock(task); + } + /* + * Check if current thread or task is being terminated. + */ + s = splsched(); + thread_lock(cur_thread); + if ((!cur_task->active) ||(!cur_thread->active)) { + /* + * Current task or thread is being terminated. + */ + thread_unlock(cur_thread); + (void) splx(s); + task_unlock(task); + task_unlock(cur_task); + thread_terminate(cur_thread); + return KERN_FAILURE; + } + thread_unlock(cur_thread); + (void) splx(s); + task_unlock(cur_task); + + if (!task->active) { + /* + * Task is already being terminated. + */ + task_unlock(task); + return KERN_FAILURE; + } + task->active = FALSE; + task_unlock(task); + } + + /* + * Prevent further execution of the task. ipc_task_disable + * prevents further task operations via the task port. + * If this is the current task, the current thread will + * be left running. + */ + ipc_task_disable(task); + (void) task_hold(task); + (void) task_dowait(task,TRUE); /* may block */ + + /* + * Terminate each thread in the task. + * + * The task_port is closed down, so no more thread_create + * operations can be done. Thread_force_terminate closes the + * thread port for each thread; when that is done, the + * thread will eventually disappear. Thus the loop will + * terminate. Call thread_force_terminate instead of + * thread_terminate to avoid deadlock checks. Need + * to call thread_block() inside loop because some other + * thread (e.g., the reaper) may have to run to get rid + * of all references to the thread; it won't vanish from + * the task's thread list until the last one is gone. + */ + task_lock(task); + while (!queue_empty(list)) { + thread = (thread_t) queue_first(list); + thread_reference(thread); + task_unlock(task); + thread_force_terminate(thread); + thread_deallocate(thread); + thread_block((void (*)()) 0); + task_lock(task); + } + task_unlock(task); + + /* + * Shut down IPC. + */ + ipc_task_terminate(task); + + + /* + * Deallocate the task's reference to itself. + */ + task_deallocate(task); + + /* + * If the current thread is in this task, it has not yet + * been terminated (since it was removed from the task's + * thread-list). Put it back in the thread list (for + * completeness), and terminate it. Since it holds the + * last reference to the task, terminating it will deallocate + * the task. + */ + if (cur_thread->task == task) { + task_lock(task); + s = splsched(); + queue_enter(list, cur_thread, thread_t, thread_list); + (void) splx(s); + task_unlock(task); + (void) thread_terminate(cur_thread); + } + + return KERN_SUCCESS; +} + +/* + * task_hold: + * + * Suspend execution of the specified task. + * This is a recursive-style suspension of the task, a count of + * suspends is maintained. + */ +kern_return_t task_hold( + register task_t task) +{ + register queue_head_t *list; + register thread_t thread, cur_thread; + + cur_thread = current_thread(); + + task_lock(task); + if (!task->active) { + task_unlock(task); + return KERN_FAILURE; + } + + task->suspend_count++; + + /* + * Iterate through all the threads and hold them. + * Do not hold the current thread if it is within the + * task. + */ + list = &task->thread_list; + queue_iterate(list, thread, thread_t, thread_list) { + if (thread != cur_thread) + thread_hold(thread); + } + task_unlock(task); + return KERN_SUCCESS; +} + +/* + * task_dowait: + * + * Wait until the task has really been suspended (all of the threads + * are stopped). Skip the current thread if it is within the task. + * + * If task is deactivated while waiting, return a failure code unless + * must_wait is true. + */ +kern_return_t task_dowait( + register task_t task, + boolean_t must_wait) +{ + register queue_head_t *list; + register thread_t thread, cur_thread, prev_thread; + register kern_return_t ret = KERN_SUCCESS; + + /* + * Iterate through all the threads. + * While waiting for each thread, we gain a reference to it + * to prevent it from going away on us. This guarantees + * that the "next" thread in the list will be a valid thread. + * + * We depend on the fact that if threads are created while + * we are looping through the threads, they will be held + * automatically. We don't care about threads that get + * deallocated along the way (the reference prevents it + * from happening to the thread we are working with). + * + * If the current thread is in the affected task, it is skipped. + * + * If the task is deactivated before we're done, and we don't + * have to wait for it (must_wait is FALSE), just bail out. + */ + cur_thread = current_thread(); + + list = &task->thread_list; + prev_thread = THREAD_NULL; + task_lock(task); + queue_iterate(list, thread, thread_t, thread_list) { + if (!(task->active) && !(must_wait)) { + ret = KERN_FAILURE; + break; + } + if (thread != cur_thread) { + thread_reference(thread); + task_unlock(task); + if (prev_thread != THREAD_NULL) + thread_deallocate(prev_thread); + /* may block */ + (void) thread_dowait(thread, TRUE); /* may block */ + prev_thread = thread; + task_lock(task); + } + } + task_unlock(task); + if (prev_thread != THREAD_NULL) + thread_deallocate(prev_thread); /* may block */ + return ret; +} + +kern_return_t task_release( + register task_t task) +{ + register queue_head_t *list; + register thread_t thread, next; + + task_lock(task); + if (!task->active) { + task_unlock(task); + return KERN_FAILURE; + } + + task->suspend_count--; + + /* + * Iterate through all the threads and release them + */ + list = &task->thread_list; + thread = (thread_t) queue_first(list); + while (!queue_end(list, (queue_entry_t) thread)) { + next = (thread_t) queue_next(&thread->thread_list); + thread_release(thread); + thread = next; + } + task_unlock(task); + return KERN_SUCCESS; +} + +kern_return_t task_threads( + task_t task, + thread_array_t *thread_list, + natural_t *count) +{ + unsigned int actual; /* this many threads */ + thread_t thread; + thread_t *threads; + int i; + + vm_size_t size, size_needed; + vm_offset_t addr; + + if (task == TASK_NULL) + return KERN_INVALID_ARGUMENT; + + size = 0; addr = 0; + + for (;;) { + task_lock(task); + if (!task->active) { + task_unlock(task); + return KERN_FAILURE; + } + + actual = task->thread_count; + + /* do we have the memory we need? */ + + size_needed = actual * sizeof(mach_port_t); + if (size_needed <= size) + break; + + /* unlock the task and allocate more memory */ + task_unlock(task); + + if (size != 0) + kfree(addr, size); + + assert(size_needed > 0); + size = size_needed; + + addr = kalloc(size); + if (addr == 0) + return KERN_RESOURCE_SHORTAGE; + } + + /* OK, have memory and the task is locked & active */ + + threads = (thread_t *) addr; + + for (i = 0, thread = (thread_t) queue_first(&task->thread_list); + i < actual; + i++, thread = (thread_t) queue_next(&thread->thread_list)) { + /* take ref for convert_thread_to_port */ + thread_reference(thread); + threads[i] = thread; + } + assert(queue_end(&task->thread_list, (queue_entry_t) thread)); + + /* can unlock task now that we've got the thread refs */ + task_unlock(task); + + if (actual == 0) { + /* no threads, so return null pointer and deallocate memory */ + + *thread_list = 0; + *count = 0; + + if (size != 0) + kfree(addr, size); + } else { + /* if we allocated too much, must copy */ + + if (size_needed < size) { + vm_offset_t newaddr; + + newaddr = kalloc(size_needed); + if (newaddr == 0) { + for (i = 0; i < actual; i++) + thread_deallocate(threads[i]); + kfree(addr, size); + return KERN_RESOURCE_SHORTAGE; + } + + bcopy((char *) addr, (char *) newaddr, size_needed); + kfree(addr, size); + threads = (thread_t *) newaddr; + } + + *thread_list = (mach_port_t *) threads; + *count = actual; + + /* do the conversion that Mig should handle */ + + for (i = 0; i < actual; i++) + ((ipc_port_t *) threads)[i] = + convert_thread_to_port(threads[i]); + } + + return KERN_SUCCESS; +} + +kern_return_t task_suspend( + register task_t task) +{ + register boolean_t hold; + + if (task == TASK_NULL) + return KERN_INVALID_ARGUMENT; + + hold = FALSE; + task_lock(task); + if ((task->user_stop_count)++ == 0) + hold = TRUE; + task_unlock(task); + + /* + * If the stop count was positive, the task is + * already stopped and we can exit. + */ + if (!hold) { + return KERN_SUCCESS; + } + + /* + * Hold all of the threads in the task, and wait for + * them to stop. If the current thread is within + * this task, hold it separately so that all of the + * other threads can stop first. + */ + + if (task_hold(task) != KERN_SUCCESS) + return KERN_FAILURE; + + if (task_dowait(task, FALSE) != KERN_SUCCESS) + return KERN_FAILURE; + + if (current_task() == task) { + spl_t s; + + thread_hold(current_thread()); + /* + * We want to call thread_block on our way out, + * to stop running. + */ + s = splsched(); + ast_on(cpu_number(), AST_BLOCK); + (void) splx(s); + } + + return KERN_SUCCESS; +} + +kern_return_t task_resume( + register task_t task) +{ + register boolean_t release; + + if (task == TASK_NULL) + return KERN_INVALID_ARGUMENT; + + release = FALSE; + task_lock(task); + if (task->user_stop_count > 0) { + if (--(task->user_stop_count) == 0) + release = TRUE; + } + else { + task_unlock(task); + return KERN_FAILURE; + } + task_unlock(task); + + /* + * Release the task if necessary. + */ + if (release) + return task_release(task); + + return KERN_SUCCESS; +} + +kern_return_t task_info( + task_t task, + int flavor, + task_info_t task_info_out, /* pointer to OUT array */ + natural_t *task_info_count) /* IN/OUT */ +{ + vm_map_t map; + + if (task == TASK_NULL) + return KERN_INVALID_ARGUMENT; + + switch (flavor) { + case TASK_BASIC_INFO: + { + register task_basic_info_t basic_info; + + if (*task_info_count < TASK_BASIC_INFO_COUNT) { + return KERN_INVALID_ARGUMENT; + } + + basic_info = (task_basic_info_t) task_info_out; + + map = (task == kernel_task) ? kernel_map : task->map; + + basic_info->virtual_size = map->size; + basic_info->resident_size = pmap_resident_count(map->pmap) + * PAGE_SIZE; + + task_lock(task); + basic_info->base_priority = task->priority; + basic_info->suspend_count = task->user_stop_count; + basic_info->user_time.seconds + = task->total_user_time.seconds; + basic_info->user_time.microseconds + = task->total_user_time.microseconds; + basic_info->system_time.seconds + = task->total_system_time.seconds; + basic_info->system_time.microseconds + = task->total_system_time.microseconds; + task_unlock(task); + + *task_info_count = TASK_BASIC_INFO_COUNT; + break; + } + + case TASK_THREAD_TIMES_INFO: + { + register task_thread_times_info_t times_info; + register thread_t thread; + + if (*task_info_count < TASK_THREAD_TIMES_INFO_COUNT) { + return KERN_INVALID_ARGUMENT; + } + + times_info = (task_thread_times_info_t) task_info_out; + times_info->user_time.seconds = 0; + times_info->user_time.microseconds = 0; + times_info->system_time.seconds = 0; + times_info->system_time.microseconds = 0; + + task_lock(task); + queue_iterate(&task->thread_list, thread, + thread_t, thread_list) + { + time_value_t user_time, system_time; + spl_t s; + + s = splsched(); + thread_lock(thread); + + thread_read_times(thread, &user_time, &system_time); + + thread_unlock(thread); + splx(s); + + time_value_add(×_info->user_time, &user_time); + time_value_add(×_info->system_time, &system_time); + } + task_unlock(task); + + *task_info_count = TASK_THREAD_TIMES_INFO_COUNT; + break; + } + + default: + return KERN_INVALID_ARGUMENT; + } + + return KERN_SUCCESS; +} + +#if MACH_HOST +/* + * task_assign: + * + * Change the assigned processor set for the task + */ +kern_return_t +task_assign( + task_t task, + processor_set_t new_pset, + boolean_t assign_threads) +{ + kern_return_t ret = KERN_SUCCESS; + register thread_t thread, prev_thread; + register queue_head_t *list; + register processor_set_t pset; + + if (task == TASK_NULL || new_pset == PROCESSOR_SET_NULL) { + return KERN_INVALID_ARGUMENT; + } + + /* + * Freeze task`s assignment. Prelude to assigning + * task. Only one freeze may be held per task. + */ + + task_lock(task); + while (task->may_assign == FALSE) { + task->assign_active = TRUE; + assert_wait((event_t)&task->assign_active, TRUE); + task_unlock(task); + thread_block((void (*)()) 0); + task_lock(task); + } + + /* + * Avoid work if task already in this processor set. + */ + if (task->processor_set == new_pset) { + /* + * No need for task->assign_active wakeup: + * task->may_assign is still TRUE. + */ + task_unlock(task); + return KERN_SUCCESS; + } + + task->may_assign = FALSE; + task_unlock(task); + + /* + * Safe to get the task`s pset: it cannot change while + * task is frozen. + */ + pset = task->processor_set; + + /* + * Lock both psets now. Use ordering to avoid deadlock. + */ + Restart: + if ((vm_offset_t) pset < (vm_offset_t) new_pset) { + pset_lock(pset); + pset_lock(new_pset); + } + else { + pset_lock(new_pset); + pset_lock(pset); + } + + /* + * Check if new_pset is ok to assign to. If not, + * reassign to default_pset. + */ + if (!new_pset->active) { + pset_unlock(pset); + pset_unlock(new_pset); + new_pset = &default_pset; + goto Restart; + } + + pset_reference(new_pset); + + /* + * Now grab the task lock and move the task. + */ + + task_lock(task); + pset_remove_task(pset, task); + pset_add_task(new_pset, task); + + pset_unlock(pset); + pset_unlock(new_pset); + + if (assign_threads == FALSE) { + /* + * We leave existing threads at their + * old assignments. Unfreeze task`s + * assignment. + */ + task->may_assign = TRUE; + if (task->assign_active) { + task->assign_active = FALSE; + thread_wakeup((event_t) &task->assign_active); + } + task_unlock(task); + pset_deallocate(pset); + return KERN_SUCCESS; + } + + /* + * If current thread is in task, freeze its assignment. + */ + if (current_thread()->task == task) { + task_unlock(task); + thread_freeze(current_thread()); + task_lock(task); + } + + /* + * Iterate down the thread list reassigning all the threads. + * New threads pick up task's new processor set automatically. + * Do current thread last because new pset may be empty. + */ + list = &task->thread_list; + prev_thread = THREAD_NULL; + queue_iterate(list, thread, thread_t, thread_list) { + if (!(task->active)) { + ret = KERN_FAILURE; + break; + } + if (thread != current_thread()) { + thread_reference(thread); + task_unlock(task); + if (prev_thread != THREAD_NULL) + thread_deallocate(prev_thread); /* may block */ + thread_assign(thread,new_pset); /* may block */ + prev_thread = thread; + task_lock(task); + } + } + + /* + * Done, wakeup anyone waiting for us. + */ + task->may_assign = TRUE; + if (task->assign_active) { + task->assign_active = FALSE; + thread_wakeup((event_t)&task->assign_active); + } + task_unlock(task); + if (prev_thread != THREAD_NULL) + thread_deallocate(prev_thread); /* may block */ + + /* + * Finish assignment of current thread. + */ + if (current_thread()->task == task) + thread_doassign(current_thread(), new_pset, TRUE); + + pset_deallocate(pset); + + return ret; +} +#else /* MACH_HOST */ +/* + * task_assign: + * + * Change the assigned processor set for the task + */ +kern_return_t +task_assign( + task_t task, + processor_set_t new_pset, + boolean_t assign_threads) +{ + return KERN_FAILURE; +} +#endif /* MACH_HOST */ + + +/* + * task_assign_default: + * + * Version of task_assign to assign to default processor set. + */ +kern_return_t +task_assign_default( + task_t task, + boolean_t assign_threads) +{ + return task_assign(task, &default_pset, assign_threads); +} + +/* + * task_get_assignment + * + * Return name of processor set that task is assigned to. + */ +kern_return_t task_get_assignment( + task_t task, + processor_set_t *pset) +{ + if (!task->active) + return KERN_FAILURE; + + *pset = task->processor_set; + pset_reference(*pset); + return KERN_SUCCESS; +} + +/* + * task_priority + * + * Set priority of task; used only for newly created threads. + * Optionally change priorities of threads. + */ +kern_return_t +task_priority( + task_t task, + int priority, + boolean_t change_threads) +{ + kern_return_t ret = KERN_SUCCESS; + + if (task == TASK_NULL || invalid_pri(priority)) + return KERN_INVALID_ARGUMENT; + + task_lock(task); + task->priority = priority; + + if (change_threads) { + register thread_t thread; + register queue_head_t *list; + + list = &task->thread_list; + queue_iterate(list, thread, thread_t, thread_list) { + if (thread_priority(thread, priority, FALSE) + != KERN_SUCCESS) + ret = KERN_FAILURE; + } + } + + task_unlock(task); + return ret; +} + +/* + * task_collect_scan: + * + * Attempt to free resources owned by tasks. + */ + +void task_collect_scan(void) +{ + register task_t task, prev_task; + processor_set_t pset, prev_pset; + + prev_task = TASK_NULL; + prev_pset = PROCESSOR_SET_NULL; + + simple_lock(&all_psets_lock); + queue_iterate(&all_psets, pset, processor_set_t, all_psets) { + pset_lock(pset); + queue_iterate(&pset->tasks, task, task_t, pset_tasks) { + task_reference(task); + pset_reference(pset); + pset_unlock(pset); + simple_unlock(&all_psets_lock); + + pmap_collect(task->map->pmap); + + if (prev_task != TASK_NULL) + task_deallocate(prev_task); + prev_task = task; + + if (prev_pset != PROCESSOR_SET_NULL) + pset_deallocate(prev_pset); + prev_pset = pset; + + simple_lock(&all_psets_lock); + pset_lock(pset); + } + pset_unlock(pset); + } + simple_unlock(&all_psets_lock); + + if (prev_task != TASK_NULL) + task_deallocate(prev_task); + if (prev_pset != PROCESSOR_SET_NULL) + pset_deallocate(prev_pset); +} + +boolean_t task_collect_allowed = TRUE; +unsigned task_collect_last_tick = 0; +unsigned task_collect_max_rate = 0; /* in ticks */ + +/* + * consider_task_collect: + * + * Called by the pageout daemon when the system needs more free pages. + */ + +void consider_task_collect(void) +{ + /* + * By default, don't attempt task collection more frequently + * than once a second. + */ + + if (task_collect_max_rate == 0) + task_collect_max_rate = hz; + + if (task_collect_allowed && + (sched_tick > (task_collect_last_tick + task_collect_max_rate))) { + task_collect_last_tick = sched_tick; + task_collect_scan(); + } +} + +kern_return_t +task_ras_control( + task_t task, + vm_offset_t pc, + vm_offset_t endpc, + int flavor) +{ + kern_return_t ret = KERN_FAILURE; + +#if FAST_TAS + int i; + + ret = KERN_SUCCESS; + task_lock(task); + switch (flavor) { + case TASK_RAS_CONTROL_PURGE_ALL: /* remove all RAS */ + for (i = 0; i < TASK_FAST_TAS_NRAS; i++) { + task->fast_tas_base[i] = task->fast_tas_end[i] = 0; + } + break; + case TASK_RAS_CONTROL_PURGE_ONE: /* remove this RAS, collapse remaining */ + for (i = 0; i < TASK_FAST_TAS_NRAS; i++) { + if ( (task->fast_tas_base[i] == pc) + && (task->fast_tas_end[i] == endpc)) { + while (i < TASK_FAST_TAS_NRAS-1) { + task->fast_tas_base[i] = task->fast_tas_base[i+1]; + task->fast_tas_end[i] = task->fast_tas_end[i+1]; + i++; + } + task->fast_tas_base[TASK_FAST_TAS_NRAS-1] = 0; + task->fast_tas_end[TASK_FAST_TAS_NRAS-1] = 0; + break; + } + } + if (i == TASK_FAST_TAS_NRAS) { + ret = KERN_INVALID_ADDRESS; + } + break; + case TASK_RAS_CONTROL_PURGE_ALL_AND_INSTALL_ONE: + /* remove all RAS an install this RAS */ + for (i = 0; i < TASK_FAST_TAS_NRAS; i++) { + task->fast_tas_base[i] = task->fast_tas_end[i] = 0; + } + /* FALL THROUGH */ + case TASK_RAS_CONTROL_INSTALL_ONE: /* install this RAS */ + for (i = 0; i < TASK_FAST_TAS_NRAS; i++) { + if ( (task->fast_tas_base[i] == pc) + && (task->fast_tas_end[i] == endpc)) { + /* already installed */ + break; + } + if ((task->fast_tas_base[i] == 0) && (task->fast_tas_end[i] == 0)){ + task->fast_tas_base[i] = pc; + task->fast_tas_end[i] = endpc; + break; + } + } + if (i == TASK_FAST_TAS_NRAS) { + ret = KERN_RESOURCE_SHORTAGE; + } + break; + default: ret = KERN_INVALID_VALUE; + break; + } + task_unlock(task); +#endif + return ret; +} -- cgit v1.2.3