kern_return_t thread_set_exception_ports( thread_t thread, exception_mask_t exception_mask, ipc_port_t new_port, exception_behavior_t new_behavior, thread_state_flavor_t new_flavor) { ipc_port_t old_port[EXC_TYPES_COUNT]; boolean_t privileged = current_task()->sec_token.val[0] == 0; register int i; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); if (exception_mask & ~EXC_MASK_VALID) return (KERN_INVALID_ARGUMENT); if (IP_VALID(new_port)) { switch (new_behavior & ~MACH_EXCEPTION_CODES) { case EXCEPTION_DEFAULT: case EXCEPTION_STATE: case EXCEPTION_STATE_IDENTITY: break; default: return (KERN_INVALID_ARGUMENT); } } /* * Check the validity of the thread_state_flavor by calling the * VALID_THREAD_STATE_FLAVOR architecture dependent macro defined in * osfmk/mach/ARCHITECTURE/thread_status.h */ if (new_flavor != 0 && !VALID_THREAD_STATE_FLAVOR(new_flavor)) return (KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_FAILURE); } if (thread->exc_actions == NULL) { ipc_thread_init_exc_actions(thread); } for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) { if (exception_mask & (1 << i)) { old_port[i] = thread->exc_actions[i].port; thread->exc_actions[i].port = ipc_port_copy_send(new_port); thread->exc_actions[i].behavior = new_behavior; thread->exc_actions[i].flavor = new_flavor; thread->exc_actions[i].privileged = privileged; } else old_port[i] = IP_NULL; } thread_mtx_unlock(thread); for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) if (IP_VALID(old_port[i])) ipc_port_release_send(old_port[i]); if (IP_VALID(new_port)) /* consume send right */ ipc_port_release_send(new_port); return (KERN_SUCCESS); }
kern_return_t thread_get_exception_ports( thread_t thread, exception_mask_t exception_mask, exception_mask_array_t masks, mach_msg_type_number_t *CountCnt, exception_port_array_t ports, exception_behavior_array_t behaviors, thread_state_flavor_array_t flavors) { unsigned int i, j, count; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); if (exception_mask & ~EXC_MASK_VALID) return (KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_FAILURE); } count = 0; if (thread->exc_actions == NULL) { goto done; } for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) { if (exception_mask & (1 << i)) { for (j = 0; j < count; ++j) { /* * search for an identical entry, if found * set corresponding mask for this exception. */ if ( thread->exc_actions[i].port == ports[j] && thread->exc_actions[i].behavior ==behaviors[j] && thread->exc_actions[i].flavor == flavors[j] ) { masks[j] |= (1 << i); break; } } if (j == count) { masks[j] = (1 << i); ports[j] = ipc_port_copy_send(thread->exc_actions[i].port); behaviors[j] = thread->exc_actions[i].behavior; flavors[j] = thread->exc_actions[i].flavor; ++count; if (count >= *CountCnt) break; } } } done: thread_mtx_unlock(thread); *CountCnt = count; return (KERN_SUCCESS); }
void ipc_thread_reset( thread_t thread) { ipc_port_t old_kport, new_kport; ipc_port_t old_sself; ipc_port_t old_exc_actions[EXC_TYPES_COUNT]; boolean_t has_old_exc_actions = FALSE; int i; new_kport = ipc_port_alloc_kernel(); if (new_kport == IP_NULL) panic("ipc_task_reset"); thread_mtx_lock(thread); old_kport = thread->ith_self; if (old_kport == IP_NULL) { /* the is already terminated (can this happen?) */ thread_mtx_unlock(thread); ipc_port_dealloc_kernel(new_kport); return; } thread->ith_self = new_kport; old_sself = thread->ith_sself; thread->ith_sself = ipc_port_make_send(new_kport); ipc_kobject_set(old_kport, IKO_NULL, IKOT_NONE); ipc_kobject_set(new_kport, (ipc_kobject_t) thread, IKOT_THREAD); /* * Only ports that were set by root-owned processes * (privileged ports) should survive */ if (thread->exc_actions != NULL) { has_old_exc_actions = TRUE; for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { if (thread->exc_actions[i].privileged) { old_exc_actions[i] = IP_NULL; } else { old_exc_actions[i] = thread->exc_actions[i].port; thread->exc_actions[i].port = IP_NULL; } } } thread_mtx_unlock(thread); /* release the naked send rights */ if (IP_VALID(old_sself)) ipc_port_release_send(old_sself); if (has_old_exc_actions) { for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) { ipc_port_release_send(old_exc_actions[i]); } } /* destroy the kernel port */ ipc_port_dealloc_kernel(old_kport); }
/* * thread_terminate_self: */ void thread_terminate_self(void) { thread_t thread = current_thread(); task_t task; spl_t s; int threadcnt; DTRACE_PROC(lwp__exit); thread_mtx_lock(thread); ulock_release_all(thread); ipc_thread_disable(thread); thread_mtx_unlock(thread); s = splsched(); thread_lock(thread); /* * Cancel priority depression, wait for concurrent expirations * on other processors. */ if (thread->sched_flags & TH_SFLAG_DEPRESSED_MASK) { thread->sched_flags &= ~TH_SFLAG_DEPRESSED_MASK; if (timer_call_cancel(&thread->depress_timer)) thread->depress_timer_active--; } while (thread->depress_timer_active > 0) { thread_unlock(thread); splx(s); delay(1); s = splsched(); thread_lock(thread); } thread_sched_call(thread, NULL); thread_unlock(thread); splx(s); thread_policy_reset(thread); #if CONFIG_EMBEDDED thead_remove_taskwatch(thread); #endif /* CONFIG_EMBEDDED */ task = thread->task; uthread_cleanup(task, thread->uthread, task->bsd_info); threadcnt = hw_atomic_sub(&task->active_thread_count, 1); /* * If we are the last thread to terminate and the task is * associated with a BSD process, perform BSD process exit. */ if (threadcnt == 0 && task->bsd_info != NULL) proc_exit(task->bsd_info); uthread_cred_free(thread->uthread); s = splsched(); thread_lock(thread); /* * Cancel wait timer, and wait for * concurrent expirations. */ if (thread->wait_timer_is_set) { thread->wait_timer_is_set = FALSE; if (timer_call_cancel(&thread->wait_timer)) thread->wait_timer_active--; } while (thread->wait_timer_active > 0) { thread_unlock(thread); splx(s); delay(1); s = splsched(); thread_lock(thread); } /* * If there is a reserved stack, release it. */ if (thread->reserved_stack != 0) { stack_free_reserved(thread); thread->reserved_stack = 0; } /* * Mark thread as terminating, and block. */ thread->state |= TH_TERMINATE; thread_mark_wait_locked(thread, THREAD_UNINT); thread_unlock(thread); /* splsched */ thread_block((thread_continue_t)thread_terminate_continue); /*NOTREACHED*/ }
kern_return_t thread_swap_exception_ports( thread_t thread, exception_mask_t exception_mask, ipc_port_t new_port, exception_behavior_t new_behavior, thread_state_flavor_t new_flavor, exception_mask_array_t masks, mach_msg_type_number_t *CountCnt, exception_port_array_t ports, exception_behavior_array_t behaviors, thread_state_flavor_array_t flavors) { ipc_port_t old_port[EXC_TYPES_COUNT]; boolean_t privileged = current_task()->sec_token.val[0] == 0; unsigned int i, j, count; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); if (exception_mask & ~EXC_MASK_VALID) return (KERN_INVALID_ARGUMENT); if (IP_VALID(new_port)) { switch (new_behavior & ~MACH_EXCEPTION_CODES) { case EXCEPTION_DEFAULT: case EXCEPTION_STATE: case EXCEPTION_STATE_IDENTITY: break; default: return (KERN_INVALID_ARGUMENT); } } thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_FAILURE); } if (thread->exc_actions == NULL) { ipc_thread_init_exc_actions(thread); } assert(EXC_TYPES_COUNT > FIRST_EXCEPTION); for (count = 0, i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT && count < *CountCnt; ++i) { if (exception_mask & (1 << i)) { for (j = 0; j < count; ++j) { /* * search for an identical entry, if found * set corresponding mask for this exception. */ if ( thread->exc_actions[i].port == ports[j] && thread->exc_actions[i].behavior == behaviors[j] && thread->exc_actions[i].flavor == flavors[j] ) { masks[j] |= (1 << i); break; } } if (j == count) { masks[j] = (1 << i); ports[j] = ipc_port_copy_send(thread->exc_actions[i].port); behaviors[j] = thread->exc_actions[i].behavior; flavors[j] = thread->exc_actions[i].flavor; ++count; } old_port[i] = thread->exc_actions[i].port; thread->exc_actions[i].port = ipc_port_copy_send(new_port); thread->exc_actions[i].behavior = new_behavior; thread->exc_actions[i].flavor = new_flavor; thread->exc_actions[i].privileged = privileged; } else old_port[i] = IP_NULL; } thread_mtx_unlock(thread); while (--i >= FIRST_EXCEPTION) { if (IP_VALID(old_port[i])) ipc_port_release_send(old_port[i]); } if (IP_VALID(new_port)) /* consume send right */ ipc_port_release_send(new_port); *CountCnt = count; return (KERN_SUCCESS); }
/* * 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_t thread, policy_t policy, policy_base_t base, mach_msg_type_number_t count, boolean_t set_limit) { kern_return_t result = KERN_SUCCESS; processor_set_t pset = &pset0; policy_limit_t limit = NULL; int limcount = 0; policy_rr_limit_data_t rr_limit; policy_fifo_limit_data_t fifo_limit; policy_timeshare_limit_data_t ts_limit; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); if ( invalid_policy(policy) || ((POLICY_TIMESHARE | POLICY_RR | POLICY_FIFO) & policy) == 0 ) { thread_mtx_unlock(thread); return (KERN_INVALID_POLICY); } 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 > thread->max_priority) { result = KERN_POLICY_LIMIT; break; } rr_limit.max_priority = thread->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 > thread->max_priority) { result = KERN_POLICY_LIMIT; break; } fifo_limit.max_priority = thread->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 > thread->max_priority) { result = KERN_POLICY_LIMIT; break; } ts_limit.max_priority = thread->max_priority; limit = (policy_limit_t) &ts_limit; break; } default: result = KERN_INVALID_POLICY; break; } } thread_mtx_unlock(thread); if (result == KERN_SUCCESS) result = thread_set_policy(thread, pset, policy, base, count, limit, limcount); 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_t thread, 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) { int max, bas; kern_return_t result = KERN_SUCCESS; if ( thread == THREAD_NULL || pset == PROCESSOR_SET_NULL || pset != &pset0) return (KERN_INVALID_ARGUMENT); if (invalid_policy(policy)) return(KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); 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; } 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; } 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; } 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) { thread_mtx_unlock(thread); return (result); } /* Note that we do not pass on max priority. */ if (result == KERN_SUCCESS) { result = thread_set_mode_and_absolute_pri(thread, policy, bas); } thread_mtx_unlock(thread); return (result); }
kern_return_t thread_policy_set( thread_t thread, thread_policy_flavor_t flavor, thread_policy_t policy_info, mach_msg_type_number_t count) { kern_return_t result = KERN_SUCCESS; spl_t s; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_TERMINATED); } if (thread->static_param) { thread_mtx_unlock(thread); return (KERN_SUCCESS); } switch (flavor) { case THREAD_EXTENDED_POLICY: { boolean_t timeshare = TRUE; if (count >= THREAD_EXTENDED_POLICY_COUNT) { thread_extended_policy_t info; info = (thread_extended_policy_t)policy_info; timeshare = info->timeshare; } s = splsched(); thread_lock(thread); if (!(thread->sched_mode & TH_MODE_FAILSAFE)) { integer_t oldmode = (thread->sched_mode & TH_MODE_TIMESHARE); thread->sched_mode &= ~TH_MODE_REALTIME; if (timeshare && !oldmode) { thread->sched_mode |= TH_MODE_TIMESHARE; if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN) sched_share_incr(); } else if (!timeshare && oldmode) { thread->sched_mode &= ~TH_MODE_TIMESHARE; if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN) sched_share_decr(); } thread_recompute_priority(thread); } else { thread->safe_mode &= ~TH_MODE_REALTIME; if (timeshare) thread->safe_mode |= TH_MODE_TIMESHARE; else thread->safe_mode &= ~TH_MODE_TIMESHARE; } thread_unlock(thread); splx(s); break; } case THREAD_TIME_CONSTRAINT_POLICY: { thread_time_constraint_policy_t info; if (count < THREAD_TIME_CONSTRAINT_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_time_constraint_policy_t)policy_info; if ( info->constraint < info->computation || info->computation > max_rt_quantum || info->computation < min_rt_quantum ) { result = KERN_INVALID_ARGUMENT; break; } s = splsched(); thread_lock(thread); thread->realtime.period = info->period; thread->realtime.computation = info->computation; thread->realtime.constraint = info->constraint; thread->realtime.preemptible = info->preemptible; if (!(thread->sched_mode & TH_MODE_FAILSAFE)) { if (thread->sched_mode & TH_MODE_TIMESHARE) { thread->sched_mode &= ~TH_MODE_TIMESHARE; if ((thread->state & (TH_RUN|TH_IDLE)) == TH_RUN) sched_share_decr(); } thread->sched_mode |= TH_MODE_REALTIME; thread_recompute_priority(thread); } else { thread->safe_mode &= ~TH_MODE_TIMESHARE; thread->safe_mode |= TH_MODE_REALTIME; } thread_unlock(thread); splx(s); break; } case THREAD_PRECEDENCE_POLICY: { thread_precedence_policy_t info; if (count < THREAD_PRECEDENCE_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_precedence_policy_t)policy_info; s = splsched(); thread_lock(thread); thread->importance = info->importance; thread_recompute_priority(thread); thread_unlock(thread); splx(s); break; } case THREAD_AFFINITY_POLICY: { thread_affinity_policy_t info; if (!thread_affinity_is_supported()) { result = KERN_NOT_SUPPORTED; break; } if (count < THREAD_AFFINITY_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_affinity_policy_t) policy_info; /* * Unlock the thread mutex here and * return directly after calling thread_affinity_set(). * This is necessary for correct lock ordering because * thread_affinity_set() takes the task lock. */ thread_mtx_unlock(thread); return thread_affinity_set(thread, info->affinity_tag); } default: result = KERN_INVALID_ARGUMENT; break; } thread_mtx_unlock(thread); return (result); }
kern_return_t thread_policy_get( thread_t thread, thread_policy_flavor_t flavor, thread_policy_t policy_info, mach_msg_type_number_t *count, boolean_t *get_default) { kern_return_t result = KERN_SUCCESS; spl_t s; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_TERMINATED); } switch (flavor) { case THREAD_EXTENDED_POLICY: { boolean_t timeshare = TRUE; if (!(*get_default)) { s = splsched(); thread_lock(thread); if ( !(thread->sched_mode & TH_MODE_REALTIME) && !(thread->safe_mode & TH_MODE_REALTIME) ) { if (!(thread->sched_mode & TH_MODE_FAILSAFE)) timeshare = (thread->sched_mode & TH_MODE_TIMESHARE) != 0; else timeshare = (thread->safe_mode & TH_MODE_TIMESHARE) != 0; } else *get_default = TRUE; thread_unlock(thread); splx(s); } if (*count >= THREAD_EXTENDED_POLICY_COUNT) { thread_extended_policy_t info; info = (thread_extended_policy_t)policy_info; info->timeshare = timeshare; } break; } case THREAD_TIME_CONSTRAINT_POLICY: { thread_time_constraint_policy_t info; if (*count < THREAD_TIME_CONSTRAINT_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_time_constraint_policy_t)policy_info; if (!(*get_default)) { s = splsched(); thread_lock(thread); if ( (thread->sched_mode & TH_MODE_REALTIME) || (thread->safe_mode & TH_MODE_REALTIME) ) { info->period = thread->realtime.period; info->computation = thread->realtime.computation; info->constraint = thread->realtime.constraint; info->preemptible = thread->realtime.preemptible; } else *get_default = TRUE; thread_unlock(thread); splx(s); } if (*get_default) { info->period = 0; info->computation = std_quantum / 2; info->constraint = std_quantum; info->preemptible = TRUE; } break; } case THREAD_PRECEDENCE_POLICY: { thread_precedence_policy_t info; if (*count < THREAD_PRECEDENCE_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_precedence_policy_t)policy_info; if (!(*get_default)) { s = splsched(); thread_lock(thread); info->importance = thread->importance; thread_unlock(thread); splx(s); } else info->importance = 0; break; } case THREAD_AFFINITY_POLICY: { thread_affinity_policy_t info; if (!thread_affinity_is_supported()) { result = KERN_NOT_SUPPORTED; break; } if (*count < THREAD_AFFINITY_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_affinity_policy_t)policy_info; if (!(*get_default)) info->affinity_tag = thread_affinity_get(thread); else info->affinity_tag = THREAD_AFFINITY_TAG_NULL; break; } default: result = KERN_INVALID_ARGUMENT; break; } thread_mtx_unlock(thread); return (result); }
kern_return_t thread_policy_get( thread_t thread, thread_policy_flavor_t flavor, thread_policy_t policy_info, mach_msg_type_number_t *count, boolean_t *get_default) { kern_return_t result = KERN_SUCCESS; spl_t s; if (thread == THREAD_NULL) return (KERN_INVALID_ARGUMENT); thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_TERMINATED); } switch (flavor) { case THREAD_EXTENDED_POLICY: { boolean_t timeshare = TRUE; if (!(*get_default)) { s = splsched(); thread_lock(thread); if ( (thread->sched_mode != TH_MODE_REALTIME) && (thread->saved_mode != TH_MODE_REALTIME) ) { if (!(thread->sched_flags & TH_SFLAG_DEMOTED_MASK)) timeshare = (thread->sched_mode == TH_MODE_TIMESHARE) != 0; else timeshare = (thread->saved_mode == TH_MODE_TIMESHARE) != 0; } else *get_default = TRUE; thread_unlock(thread); splx(s); } if (*count >= THREAD_EXTENDED_POLICY_COUNT) { thread_extended_policy_t info; info = (thread_extended_policy_t)policy_info; info->timeshare = timeshare; } break; } case THREAD_TIME_CONSTRAINT_POLICY: { thread_time_constraint_policy_t info; if (*count < THREAD_TIME_CONSTRAINT_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_time_constraint_policy_t)policy_info; if (!(*get_default)) { s = splsched(); thread_lock(thread); if ( (thread->sched_mode == TH_MODE_REALTIME) || (thread->saved_mode == TH_MODE_REALTIME) ) { info->period = thread->realtime.period; info->computation = thread->realtime.computation; info->constraint = thread->realtime.constraint; info->preemptible = thread->realtime.preemptible; } else *get_default = TRUE; thread_unlock(thread); splx(s); } if (*get_default) { info->period = 0; info->computation = default_timeshare_computation; info->constraint = default_timeshare_constraint; info->preemptible = TRUE; } break; } case THREAD_PRECEDENCE_POLICY: { thread_precedence_policy_t info; if (*count < THREAD_PRECEDENCE_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_precedence_policy_t)policy_info; if (!(*get_default)) { s = splsched(); thread_lock(thread); info->importance = thread->importance; thread_unlock(thread); splx(s); } else info->importance = 0; break; } case THREAD_AFFINITY_POLICY: { thread_affinity_policy_t info; if (!thread_affinity_is_supported()) { result = KERN_NOT_SUPPORTED; break; } if (*count < THREAD_AFFINITY_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_affinity_policy_t)policy_info; if (!(*get_default)) info->affinity_tag = thread_affinity_get(thread); else info->affinity_tag = THREAD_AFFINITY_TAG_NULL; break; } case THREAD_POLICY_STATE: { thread_policy_state_t info; if (*count < THREAD_POLICY_STATE_COUNT) { result = KERN_INVALID_ARGUMENT; break; } /* Only root can get this info */ if (current_task()->sec_token.val[0] != 0) { result = KERN_PROTECTION_FAILURE; break; } info = (thread_policy_state_t)policy_info; if (!(*get_default)) { info->flags = 0; info->flags |= (thread->static_param ? THREAD_POLICY_STATE_FLAG_STATIC_PARAM : 0); /* * Unlock the thread mutex and directly return. * This is necessary because proc_get_thread_policy() * takes the task lock. */ thread_mtx_unlock(thread); proc_get_thread_policy(thread, info); return (result); } else { info->requested = 0; info->effective = 0; info->pending = 0; } break; } case THREAD_LATENCY_QOS_POLICY: { thread_latency_qos_policy_t info = (thread_latency_qos_policy_t) policy_info; uint32_t plqos; if (*count < THREAD_LATENCY_QOS_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } if (*get_default) { plqos = 0; } else { plqos = thread->effective_policy.t_latency_qos; } info->thread_latency_qos_tier = qos_latency_policy_package(plqos); } break; case THREAD_THROUGHPUT_QOS_POLICY: { thread_throughput_qos_policy_t info = (thread_throughput_qos_policy_t) policy_info; uint32_t ptqos; if (*count < THREAD_THROUGHPUT_QOS_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } if (*get_default) { ptqos = 0; } else { ptqos = thread->effective_policy.t_through_qos; } info->thread_throughput_qos_tier = qos_throughput_policy_package(ptqos); } break; case THREAD_QOS_POLICY: case THREAD_QOS_POLICY_OVERRIDE: { thread_qos_policy_t info = (thread_qos_policy_t)policy_info; if (*count < THREAD_QOS_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } if (!(*get_default)) { if (flavor == THREAD_QOS_POLICY_OVERRIDE) { info->qos_tier = thread->requested_policy.thrp_qos_override; /* TODO: handle importance overrides */ info->tier_importance = 0; } else { info->qos_tier = thread->requested_policy.thrp_qos; info->tier_importance = thread->importance; } } else { info->qos_tier = THREAD_QOS_UNSPECIFIED; info->tier_importance = 0; } break; } default: result = KERN_INVALID_ARGUMENT; break; } thread_mtx_unlock(thread); return (result); }
/* * thread_set_mode_and_absolute_pri: * * Set scheduling policy & absolute priority for thread, for deprecated * thread_set_policy and thread_policy interfaces. * * Note that there is no implemented difference between POLICY_RR and POLICY_FIFO. * Both result in FIXED mode scheduling. * * Called with thread mutex locked. */ kern_return_t thread_set_mode_and_absolute_pri( thread_t thread, integer_t policy, integer_t priority) { spl_t s; sched_mode_t mode; kern_return_t kr = KERN_SUCCESS; if (thread_is_static_param(thread)) return (KERN_POLICY_STATIC); if (thread->policy_reset) return (KERN_SUCCESS); /* Setting legacy policies on threads kills the current QoS */ if (thread->requested_policy.thrp_qos != THREAD_QOS_UNSPECIFIED) { thread_mtx_unlock(thread); kr = thread_remove_qos_policy(thread); thread_mtx_lock(thread); if (!thread->active) { return (KERN_TERMINATED); } } switch (policy) { case POLICY_TIMESHARE: mode = TH_MODE_TIMESHARE; break; case POLICY_RR: case POLICY_FIFO: mode = TH_MODE_FIXED; break; default: panic("unexpected sched policy: %d", policy); break; } s = splsched(); thread_lock(thread); /* This path isn't allowed to change a thread out of realtime. */ if ((thread->sched_mode != TH_MODE_REALTIME) && (thread->saved_mode != TH_MODE_REALTIME)) { /* * Reverse engineer and apply the correct importance value * from the requested absolute priority value. */ if (priority >= thread->max_priority) priority = thread->max_priority - thread->task_priority; else if (priority >= MINPRI_KERNEL) priority -= MINPRI_KERNEL; else if (priority >= MINPRI_RESERVED) priority -= MINPRI_RESERVED; else priority -= BASEPRI_DEFAULT; priority += thread->task_priority; if (priority > thread->max_priority) priority = thread->max_priority; else if (priority < MINPRI) priority = MINPRI; thread->importance = priority - thread->task_priority; boolean_t removed = thread_run_queue_remove(thread); thread_set_user_sched_mode(thread, mode); thread_recompute_priority(thread); if (removed) thread_setrun(thread, SCHED_TAILQ); } thread_unlock(thread); splx(s); sfi_reevaluate(thread); return (kr); }
kern_return_t thread_policy_set_internal( thread_t thread, thread_policy_flavor_t flavor, thread_policy_t policy_info, mach_msg_type_number_t count) { kern_return_t result = KERN_SUCCESS; spl_t s; thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_TERMINATED); } switch (flavor) { case THREAD_EXTENDED_POLICY: { boolean_t timeshare = TRUE; if (count >= THREAD_EXTENDED_POLICY_COUNT) { thread_extended_policy_t info; info = (thread_extended_policy_t)policy_info; timeshare = info->timeshare; } sched_mode_t mode = (timeshare == TRUE) ? TH_MODE_TIMESHARE : TH_MODE_FIXED; s = splsched(); thread_lock(thread); boolean_t removed = thread_run_queue_remove(thread); thread_set_user_sched_mode(thread, mode); thread_recompute_priority(thread); if (removed) thread_setrun(thread, SCHED_TAILQ); thread_unlock(thread); splx(s); sfi_reevaluate(thread); break; } case THREAD_TIME_CONSTRAINT_POLICY: { thread_time_constraint_policy_t info; if (count < THREAD_TIME_CONSTRAINT_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_time_constraint_policy_t)policy_info; if ( info->constraint < info->computation || info->computation > max_rt_quantum || info->computation < min_rt_quantum ) { result = KERN_INVALID_ARGUMENT; break; } s = splsched(); thread_lock(thread); boolean_t removed = thread_run_queue_remove(thread); thread->realtime.period = info->period; thread->realtime.computation = info->computation; thread->realtime.constraint = info->constraint; thread->realtime.preemptible = info->preemptible; thread_set_user_sched_mode(thread, TH_MODE_REALTIME); thread_recompute_priority(thread); if (removed) thread_setrun(thread, SCHED_TAILQ); thread_unlock(thread); splx(s); sfi_reevaluate(thread); break; } case THREAD_PRECEDENCE_POLICY: { thread_precedence_policy_t info; if (count < THREAD_PRECEDENCE_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_precedence_policy_t)policy_info; s = splsched(); thread_lock(thread); thread->importance = info->importance; thread_recompute_priority(thread); thread_unlock(thread); splx(s); break; } case THREAD_AFFINITY_POLICY: { thread_affinity_policy_t info; if (!thread_affinity_is_supported()) { result = KERN_NOT_SUPPORTED; break; } if (count < THREAD_AFFINITY_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } info = (thread_affinity_policy_t) policy_info; /* * Unlock the thread mutex here and * return directly after calling thread_affinity_set(). * This is necessary for correct lock ordering because * thread_affinity_set() takes the task lock. */ thread_mtx_unlock(thread); return thread_affinity_set(thread, info->affinity_tag); } case THREAD_THROUGHPUT_QOS_POLICY: { thread_throughput_qos_policy_t info = (thread_throughput_qos_policy_t) policy_info; int tqos; if (count < THREAD_LATENCY_QOS_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } if ((result = qos_throughput_policy_validate(info->thread_throughput_qos_tier)) != KERN_SUCCESS) { break; } tqos = qos_extract(info->thread_throughput_qos_tier); thread->effective_policy.t_through_qos = tqos; } break; case THREAD_LATENCY_QOS_POLICY: { thread_latency_qos_policy_t info = (thread_latency_qos_policy_t) policy_info; int lqos; if (count < THREAD_THROUGHPUT_QOS_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } if ((result = qos_latency_policy_validate(info->thread_latency_qos_tier)) != KERN_SUCCESS) { break; } lqos = qos_extract(info->thread_latency_qos_tier); /* The expected use cases (opt-in) of per-thread latency QoS would seem to * preclude any requirement at present to re-evaluate timers on a thread level * latency QoS change. */ thread->effective_policy.t_latency_qos = lqos; } break; case THREAD_QOS_POLICY: case THREAD_QOS_POLICY_OVERRIDE: { thread_qos_policy_t info = (thread_qos_policy_t)policy_info; if (count < THREAD_QOS_POLICY_COUNT) { result = KERN_INVALID_ARGUMENT; break; } if (info->qos_tier < 0 || info->qos_tier >= THREAD_QOS_LAST) { result = KERN_INVALID_ARGUMENT; break; } if (info->tier_importance > 0 || info->tier_importance < THREAD_QOS_MIN_TIER_IMPORTANCE) { result = KERN_INVALID_ARGUMENT; break; } if (info->qos_tier == THREAD_QOS_UNSPECIFIED && info->tier_importance != 0) { result = KERN_INVALID_ARGUMENT; break; } /* * Going into task policy requires the task mutex, * because of the way synchronization against the IO policy * subsystem works. * * We need to move thread policy to the thread mutex instead. * <rdar://problem/15831652> separate thread policy from task policy */ if (flavor == THREAD_QOS_POLICY_OVERRIDE) { int strongest_override = info->qos_tier; if (info->qos_tier != THREAD_QOS_UNSPECIFIED && thread->requested_policy.thrp_qos_override != THREAD_QOS_UNSPECIFIED) strongest_override = MAX(thread->requested_policy.thrp_qos_override, info->qos_tier); thread_mtx_unlock(thread); /* There is a race here. To be closed in <rdar://problem/15831652> separate thread policy from task policy */ proc_set_task_policy(thread->task, thread, TASK_POLICY_ATTRIBUTE, TASK_POLICY_QOS_OVERRIDE, strongest_override); return (result); } thread_mtx_unlock(thread); proc_set_task_policy2(thread->task, thread, TASK_POLICY_ATTRIBUTE, TASK_POLICY_QOS_AND_RELPRIO, info->qos_tier, -info->tier_importance); thread_mtx_lock(thread); if (!thread->active) { thread_mtx_unlock(thread); return (KERN_TERMINATED); } break; } default: result = KERN_INVALID_ARGUMENT; break; } thread_mtx_unlock(thread); return (result); }