X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/d7e50217d7adf6e52786a38bcaa4cd698cb9a79e..d9a64523371fa019c4575bb400cbbc3a50ac9903:/osfmk/kern/sync_sema.c diff --git a/osfmk/kern/sync_sema.c b/osfmk/kern/sync_sema.c index 6ca013208..98f33ba8d 100644 --- a/osfmk/kern/sync_sema.c +++ b/osfmk/kern/sync_sema.c @@ -1,16 +1,19 @@ /* - * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2000-2009 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ - * - * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER @@ -20,7 +23,7 @@ * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* * @OSF_COPYRIGHT@ @@ -34,9 +37,11 @@ */ #include +#include #include #include #include +#include #include #include @@ -49,15 +54,85 @@ #include #include #include -#include +#include #include #include +#include + static unsigned int semaphore_event; -#define SEMAPHORE_EVENT ((event64_t)&semaphore_event) +#define SEMAPHORE_EVENT CAST_EVENT64_T(&semaphore_event) zone_t semaphore_zone; -unsigned int semaphore_max = SEMAPHORE_MAX; +unsigned int semaphore_max; + +os_refgrp_decl(static, sema_refgrp, "semaphore", NULL); + +/* Forward declarations */ + + +kern_return_t +semaphore_wait_trap_internal( + mach_port_name_t name, + void (*caller_cont)(kern_return_t)); + +kern_return_t +semaphore_wait_signal_trap_internal( + mach_port_name_t wait_name, + mach_port_name_t signal_name, + void (*caller_cont)(kern_return_t)); + +kern_return_t +semaphore_timedwait_trap_internal( + mach_port_name_t name, + unsigned int sec, + clock_res_t nsec, + void (*caller_cont)(kern_return_t)); + +kern_return_t +semaphore_timedwait_signal_trap_internal( + mach_port_name_t wait_name, + mach_port_name_t signal_name, + unsigned int sec, + clock_res_t nsec, + void (*caller_cont)(kern_return_t)); + +kern_return_t +semaphore_signal_internal_trap(mach_port_name_t sema_name); + +kern_return_t +semaphore_signal_internal( + semaphore_t semaphore, + thread_t thread, + int options); + +kern_return_t +semaphore_convert_wait_result( + int wait_result); + +void +semaphore_wait_continue(void); + +static kern_return_t +semaphore_wait_internal( + semaphore_t wait_semaphore, + semaphore_t signal_semaphore, + uint64_t deadline, + int option, + void (*caller_cont)(kern_return_t)); + +static __inline__ uint64_t +semaphore_deadline( + unsigned int sec, + clock_res_t nsec) +{ + uint64_t abstime; + + nanoseconds_to_absolutetime((uint64_t)sec * NSEC_PER_SEC + nsec, &abstime); + clock_absolutetime_interval_to_deadline(abstime, &abstime); + + return (abstime); +} /* * ROUTINE: semaphore_init [private] @@ -72,6 +147,7 @@ semaphore_init(void) semaphore_max * sizeof(struct semaphore), sizeof(struct semaphore), "semaphores"); + zone_change(semaphore_zone, Z_NOENCRYPT, TRUE); } /* @@ -88,50 +164,40 @@ semaphore_create( int value) { semaphore_t s = SEMAPHORE_NULL; + kern_return_t kret; - - if (task == TASK_NULL || value < 0 || policy > SYNC_POLICY_MAX) { - *new_semaphore = SEMAPHORE_NULL; + *new_semaphore = SEMAPHORE_NULL; + if (task == TASK_NULL || value < 0 || policy > SYNC_POLICY_MAX) return KERN_INVALID_ARGUMENT; - } s = (semaphore_t) zalloc (semaphore_zone); - if (s == SEMAPHORE_NULL) { - *new_semaphore = SEMAPHORE_NULL; + if (s == SEMAPHORE_NULL) return KERN_RESOURCE_SHORTAGE; - } - wait_queue_init(&s->wait_queue, policy); /* also inits lock */ - s->count = value; - s->ref_count = 1; + kret = waitq_init(&s->waitq, policy | SYNC_POLICY_DISABLE_IRQ); /* also inits lock */ + if (kret != KERN_SUCCESS) { + zfree(semaphore_zone, s); + return kret; + } /* - * Create and initialize the semaphore port + * Initialize the semaphore values. */ - s->port = ipc_port_alloc_kernel(); - if (s->port == IP_NULL) { - /* This will deallocate the semaphore */ - semaphore_dereference(s); - *new_semaphore = SEMAPHORE_NULL; - return KERN_RESOURCE_SHORTAGE; - } - - ipc_kobject_set (s->port, (ipc_kobject_t) s, IKOT_SEMAPHORE); + s->port = IP_NULL; + os_ref_init(&s->ref_count, &sema_refgrp); + s->count = value; + s->active = TRUE; + s->owner = task; /* * Associate the new semaphore with the task by adding * the new semaphore to the task's semaphore list. - * - * Associate the task with the new semaphore by having the - * semaphores task pointer point to the owning task's structure. */ task_lock(task); enqueue_head(&task->semaphore_list, (queue_entry_t) s); task->semaphores_owned++; - s->owner = task; - s->active = TRUE; task_unlock(task); *new_semaphore = s; @@ -140,43 +206,30 @@ semaphore_create( } /* - * Routine: semaphore_destroy + * Routine: semaphore_destroy_internal * - * Destroys a semaphore. This call will only succeed if the - * specified task is the SAME task name specified at the semaphore's - * creation. + * Disassociate a semaphore from its owning task, mark it inactive, + * and set any waiting threads running with THREAD_RESTART. * - * All threads currently blocked on the semaphore are awoken. These - * threads will return with the KERN_TERMINATED error. + * Conditions: + * task is locked + * semaphore is locked + * semaphore is owned by the specified task + * Returns: + * with semaphore unlocked */ -kern_return_t -semaphore_destroy( +static void +semaphore_destroy_internal( task_t task, semaphore_t semaphore) { - int old_count; - thread_t thread; - spl_t spl_level; - - - if (task == TASK_NULL || semaphore == SEMAPHORE_NULL) - return KERN_INVALID_ARGUMENT; + int old_count; - /* - * Disown semaphore - */ - task_lock(task); - if (semaphore->owner != task) { - task_unlock(task); - return KERN_INVALID_ARGUMENT; - } - remqueue(&task->semaphore_list, (queue_entry_t) semaphore); + /* unlink semaphore from owning task */ + assert(semaphore->owner == task); + remqueue((queue_entry_t) semaphore); semaphore->owner = TASK_NULL; task->semaphores_owned--; - task_unlock(task); - - spl_level = splsched(); - semaphore_lock(semaphore); /* * Deactivate semaphore @@ -191,26 +244,100 @@ semaphore_destroy( semaphore->count = 0; if (old_count < 0) { - wait_queue_wakeup64_all_locked(&semaphore->wait_queue, - SEMAPHORE_EVENT, - THREAD_RESTART, - TRUE); /* unlock? */ + waitq_wakeup64_all_locked(&semaphore->waitq, + SEMAPHORE_EVENT, + THREAD_RESTART, NULL, + WAITQ_ALL_PRIORITIES, + WAITQ_UNLOCK); + /* waitq/semaphore is unlocked */ } else { semaphore_unlock(semaphore); } +} + +/* + * Routine: semaphore_destroy + * + * Destroys a semaphore and consume the caller's reference on the + * semaphore. + */ +kern_return_t +semaphore_destroy( + task_t task, + semaphore_t semaphore) +{ + spl_t spl_level; + + if (semaphore == SEMAPHORE_NULL) + return KERN_INVALID_ARGUMENT; + + if (task == TASK_NULL) { + semaphore_dereference(semaphore); + return KERN_INVALID_ARGUMENT; + } + + task_lock(task); + spl_level = splsched(); + semaphore_lock(semaphore); + + if (semaphore->owner != task) { + semaphore_unlock(semaphore); + semaphore_dereference(semaphore); + splx(spl_level); + task_unlock(task); + return KERN_INVALID_ARGUMENT; + } + + semaphore_destroy_internal(task, semaphore); + /* semaphore unlocked */ + splx(spl_level); + task_unlock(task); - /* - * Deallocate - * - * Drop the semaphore reference, which in turn deallocates the - * semaphore structure if the reference count goes to zero. - */ - ipc_port_dealloc_kernel(semaphore->port); semaphore_dereference(semaphore); return KERN_SUCCESS; } +/* + * Routine: semaphore_destroy_all + * + * Destroy all the semaphores associated with a given task. + */ +#define SEMASPERSPL 20 /* max number of semaphores to destroy per spl hold */ + +void +semaphore_destroy_all( + task_t task) +{ + uint32_t count; + spl_t spl_level; + + count = 0; + task_lock(task); + while (!queue_empty(&task->semaphore_list)) { + semaphore_t semaphore; + + semaphore = (semaphore_t) queue_first(&task->semaphore_list); + + if (count == 0) + spl_level = splsched(); + semaphore_lock(semaphore); + + semaphore_destroy_internal(task, semaphore); + /* semaphore unlocked */ + + /* throttle number of semaphores per interrupt disablement */ + if (++count == SEMASPERSPL) { + count = 0; + splx(spl_level); + } + } + if (count != 0) + splx(spl_level); + + task_unlock(task); +} + /* * Routine: semaphore_signal_internal * @@ -221,8 +348,8 @@ semaphore_destroy( kern_return_t semaphore_signal_internal( semaphore_t semaphore, - thread_act_t thread_act, - int options) + thread_t thread, + int options) { kern_return_t kr; spl_t spl_level; @@ -236,17 +363,18 @@ semaphore_signal_internal( return KERN_TERMINATED; } - if (thread_act != THR_ACT_NULL) { + if (thread != THREAD_NULL) { if (semaphore->count < 0) { - kr = wait_queue_wakeup64_thread_locked( - &semaphore->wait_queue, + kr = waitq_wakeup64_thread_locked( + &semaphore->waitq, SEMAPHORE_EVENT, - thread_act->thread, + thread, THREAD_AWAKENED, - TRUE); /* unlock? */ + WAITQ_UNLOCK); + /* waitq/semaphore is unlocked */ } else { - semaphore_unlock(semaphore); kr = KERN_NOT_WAITING; + semaphore_unlock(semaphore); } splx(spl_level); return kr; @@ -255,34 +383,40 @@ semaphore_signal_internal( if (options & SEMAPHORE_SIGNAL_ALL) { int old_count = semaphore->count; + kr = KERN_NOT_WAITING; if (old_count < 0) { semaphore->count = 0; /* always reset */ - kr = wait_queue_wakeup64_all_locked( - &semaphore->wait_queue, + kr = waitq_wakeup64_all_locked( + &semaphore->waitq, SEMAPHORE_EVENT, - THREAD_AWAKENED, - TRUE); /* unlock? */ + THREAD_AWAKENED, NULL, + WAITQ_ALL_PRIORITIES, + WAITQ_UNLOCK); + /* waitq / semaphore is unlocked */ } else { if (options & SEMAPHORE_SIGNAL_PREPOST) semaphore->count++; - semaphore_unlock(semaphore); kr = KERN_SUCCESS; + semaphore_unlock(semaphore); } splx(spl_level); return kr; } if (semaphore->count < 0) { - if (wait_queue_wakeup64_one_locked( - &semaphore->wait_queue, + kr = waitq_wakeup64_one_locked( + &semaphore->waitq, SEMAPHORE_EVENT, - THREAD_AWAKENED, - FALSE) == KERN_SUCCESS) { + THREAD_AWAKENED, NULL, + WAITQ_ALL_PRIORITIES, + WAITQ_KEEP_LOCKED); + if (kr == KERN_SUCCESS) { semaphore_unlock(semaphore); splx(spl_level); return KERN_SUCCESS; - } else + } else { semaphore->count = 0; /* all waiters gone */ + } } if (options & SEMAPHORE_SIGNAL_PREPOST) { @@ -297,15 +431,15 @@ semaphore_signal_internal( /* * Routine: semaphore_signal_thread * - * If the specified thread_act is blocked on the semaphore, it is - * woken up. If a NULL thread_act was supplied, then any one + * If the specified thread is blocked on the semaphore, it is + * woken up. If a NULL thread was supplied, then any one * thread is woken up. Otherwise the caller gets KERN_NOT_WAITING * and the semaphore is unchanged. */ kern_return_t semaphore_signal_thread( semaphore_t semaphore, - thread_act_t thread_act) + thread_t thread) { kern_return_t ret; @@ -313,7 +447,7 @@ semaphore_signal_thread( return KERN_INVALID_ARGUMENT; ret = semaphore_signal_internal(semaphore, - thread_act, + thread, SEMAPHORE_OPTION_NONE); return ret; } @@ -325,12 +459,12 @@ semaphore_signal_thread( */ kern_return_t semaphore_signal_thread_trap( - mach_port_name_t sema_name, - mach_port_name_t thread_name) + struct semaphore_signal_thread_trap_args *args) { - + mach_port_name_t sema_name = args->signal_name; + mach_port_name_t thread_name = args->thread_name; semaphore_t semaphore; - thread_act_t thread_act; + thread_t thread; kern_return_t kr; /* @@ -339,22 +473,22 @@ semaphore_signal_thread_trap( * pre-post the semaphore. */ if (thread_name != MACH_PORT_NULL) { - thread_act = port_name_to_act(thread_name); - if (thread_act == THR_ACT_NULL) + thread = port_name_to_thread(thread_name); + if (thread == THREAD_NULL) return KERN_INVALID_ARGUMENT; } else - thread_act = THR_ACT_NULL; + thread = THREAD_NULL; kr = port_name_to_semaphore(sema_name, &semaphore); - if (kr != KERN_SUCCESS) { - act_deallocate(thread_act); - return kr; + if (kr == KERN_SUCCESS) { + kr = semaphore_signal_internal(semaphore, + thread, + SEMAPHORE_OPTION_NONE); + semaphore_dereference(semaphore); + } + if (thread != THREAD_NULL) { + thread_deallocate(thread); } - kr = semaphore_signal_internal(semaphore, - thread_act, - SEMAPHORE_OPTION_NONE); - semaphore_dereference(semaphore); - act_deallocate(thread_act); return kr; } @@ -381,7 +515,7 @@ semaphore_signal( return KERN_INVALID_ARGUMENT; kr = semaphore_signal_internal(semaphore, - THR_ACT_NULL, + THREAD_NULL, SEMAPHORE_SIGNAL_PREPOST); if (kr == KERN_NOT_WAITING) return KERN_SUCCESS; @@ -395,22 +529,28 @@ semaphore_signal( */ kern_return_t semaphore_signal_trap( - mach_port_name_t sema_name) + struct semaphore_signal_trap_args *args) +{ + mach_port_name_t sema_name = args->signal_name; + + return (semaphore_signal_internal_trap(sema_name)); +} + +kern_return_t +semaphore_signal_internal_trap(mach_port_name_t sema_name) { - semaphore_t semaphore; kern_return_t kr; kr = port_name_to_semaphore(sema_name, &semaphore); - if (kr != KERN_SUCCESS) { - return kr; + if (kr == KERN_SUCCESS) { + kr = semaphore_signal_internal(semaphore, + THREAD_NULL, + SEMAPHORE_SIGNAL_PREPOST); + semaphore_dereference(semaphore); + if (kr == KERN_NOT_WAITING) + kr = KERN_SUCCESS; } - kr = semaphore_signal_internal(semaphore, - THR_ACT_NULL, - SEMAPHORE_SIGNAL_PREPOST); - semaphore_dereference(semaphore); - if (kr == KERN_NOT_WAITING) - return KERN_SUCCESS; return kr; } @@ -430,7 +570,7 @@ semaphore_signal_all( return KERN_INVALID_ARGUMENT; kr = semaphore_signal_internal(semaphore, - THR_ACT_NULL, + THREAD_NULL, SEMAPHORE_SIGNAL_ALL); if (kr == KERN_NOT_WAITING) return KERN_SUCCESS; @@ -444,22 +584,21 @@ semaphore_signal_all( */ kern_return_t semaphore_signal_all_trap( - mach_port_name_t sema_name) + struct semaphore_signal_all_trap_args *args) { - + mach_port_name_t sema_name = args->signal_name; semaphore_t semaphore; kern_return_t kr; kr = port_name_to_semaphore(sema_name, &semaphore); - if (kr != KERN_SUCCESS) { - return kr; + if (kr == KERN_SUCCESS) { + kr = semaphore_signal_internal(semaphore, + THREAD_NULL, + SEMAPHORE_SIGNAL_ALL); + semaphore_dereference(semaphore); + if (kr == KERN_NOT_WAITING) + kr = KERN_SUCCESS; } - kr = semaphore_signal_internal(semaphore, - THR_ACT_NULL, - SEMAPHORE_SIGNAL_ALL); - semaphore_dereference(semaphore); - if (kr == KERN_NOT_WAITING) - return KERN_SUCCESS; return kr; } @@ -514,33 +653,6 @@ semaphore_wait_continue(void) (*caller_cont)(semaphore_convert_wait_result(wait_result)); } -/* - * Routine: semaphore_timedwait_continue - * - * Common continuation routine after doing a timed wait on a - * semaphore. It clears the timer before calling the semaphore - * routine saved in the thread struct. - */ -void -semaphore_timedwait_continue(void) -{ - thread_t self = current_thread(); - int wait_result = self->wait_result; - void (*caller_cont)(kern_return_t) = self->sth_continuation; - - if (wait_result != THREAD_TIMED_OUT) - thread_cancel_timer(); - - assert(self->sth_waitsemaphore != SEMAPHORE_NULL); - semaphore_dereference(self->sth_waitsemaphore); - if (self->sth_signalsemaphore != SEMAPHORE_NULL) - semaphore_dereference(self->sth_signalsemaphore); - - assert(caller_cont != (void (*)(kern_return_t))0); - (*caller_cont)(semaphore_convert_wait_result(wait_result)); -} - - /* * Routine: semaphore_wait_internal * @@ -552,44 +664,41 @@ semaphore_timedwait_continue(void) * The reference * A reference is held on the signal semaphore. */ -kern_return_t +static kern_return_t semaphore_wait_internal( semaphore_t wait_semaphore, semaphore_t signal_semaphore, - mach_timespec_t *wait_timep, + uint64_t deadline, + int option, void (*caller_cont)(kern_return_t)) { - void (*continuation)(void); - uint64_t abstime, nsinterval; - boolean_t nonblocking; - int wait_result; - spl_t spl_level; + int wait_result; + spl_t spl_level; kern_return_t kr = KERN_ALREADY_WAITING; spl_level = splsched(); semaphore_lock(wait_semaphore); - /* - * Decide if we really have to wait. - */ - nonblocking = (wait_timep != (mach_timespec_t *)0) ? - (wait_timep->tv_sec == 0 && wait_timep->tv_nsec == 0) : - FALSE; - if (!wait_semaphore->active) { kr = KERN_TERMINATED; } else if (wait_semaphore->count > 0) { wait_semaphore->count--; kr = KERN_SUCCESS; - } else if (nonblocking) { + } else if (option & SEMAPHORE_TIMEOUT_NOBLOCK) { kr = KERN_OPERATION_TIMED_OUT; - } else { + } else { + thread_t self = current_thread(); + wait_semaphore->count = -1; /* we don't keep an actual count */ - (void)wait_queue_assert_wait64_locked( - &wait_semaphore->wait_queue, + + thread_set_pending_block_hint(self, kThreadWaitSemaphore); + (void)waitq_assert_wait64_locked( + &wait_semaphore->waitq, SEMAPHORE_EVENT, THREAD_ABORTSAFE, - FALSE); /* unlock? */ + TIMEOUT_URGENCY_USER_NORMAL, + deadline, TIMEOUT_NO_LEEWAY, + self); } semaphore_unlock(wait_semaphore); splx(spl_level); @@ -607,7 +716,7 @@ semaphore_wait_internal( * our intention to wait above). */ signal_kr = semaphore_signal_internal(signal_semaphore, - THR_ACT_NULL, + THREAD_NULL, SEMAPHORE_SIGNAL_PREPOST); if (signal_kr == KERN_NOT_WAITING) @@ -641,24 +750,6 @@ semaphore_wait_internal( */ if (kr != KERN_ALREADY_WAITING) return kr; - - /* - * If it is a timed wait, go ahead and set up the timer. - */ - if (wait_timep != (mach_timespec_t *)0) { - clock_interval_to_absolutetime_interval(wait_timep->tv_sec, - NSEC_PER_SEC, - &abstime); - clock_interval_to_absolutetime_interval(wait_timep->tv_nsec, - 1, - &nsinterval); - abstime += nsinterval; - clock_absolutetime_interval_to_deadline(abstime, &abstime); - thread_set_timer_deadline(abstime); - continuation = semaphore_timedwait_continue; - } else { - continuation = semaphore_wait_continue; - } /* * Now, we can block. If the caller supplied a continuation @@ -673,20 +764,12 @@ semaphore_wait_internal( self->sth_continuation = caller_cont; self->sth_waitsemaphore = wait_semaphore; self->sth_signalsemaphore = signal_semaphore; - wait_result = thread_block(continuation); - } else { + wait_result = thread_block((thread_continue_t)semaphore_wait_continue); + } + else { wait_result = thread_block(THREAD_CONTINUE_NULL); } - /* - * If we came back here (not continuation case) cancel - * any pending timers, convert the wait result to an - * appropriate semaphore return value, and then return - * that. - */ - if (wait_timep && (wait_result != THREAD_TIMED_OUT)) - thread_cancel_timer(); - return (semaphore_convert_wait_result(wait_result)); } @@ -706,8 +789,37 @@ semaphore_wait( return KERN_INVALID_ARGUMENT; return(semaphore_wait_internal(semaphore, - SEMAPHORE_NULL, - (mach_timespec_t *)0, + SEMAPHORE_NULL, + 0ULL, SEMAPHORE_OPTION_NONE, + (void (*)(kern_return_t))0)); +} + +kern_return_t +semaphore_wait_noblock( + semaphore_t semaphore) +{ + + if (semaphore == SEMAPHORE_NULL) + return KERN_INVALID_ARGUMENT; + + return(semaphore_wait_internal(semaphore, + SEMAPHORE_NULL, + 0ULL, SEMAPHORE_TIMEOUT_NOBLOCK, + (void (*)(kern_return_t))0)); +} + +kern_return_t +semaphore_wait_deadline( + semaphore_t semaphore, + uint64_t deadline) +{ + + if (semaphore == SEMAPHORE_NULL) + return KERN_INVALID_ARGUMENT; + + return(semaphore_wait_internal(semaphore, + SEMAPHORE_NULL, + deadline, SEMAPHORE_OPTION_NONE, (void (*)(kern_return_t))0)); } @@ -717,22 +829,32 @@ semaphore_wait( * Trap version of semaphore wait. Called on behalf of user-level * clients. */ + kern_return_t semaphore_wait_trap( - mach_port_name_t name) + struct semaphore_wait_trap_args *args) +{ + return(semaphore_wait_trap_internal(args->wait_name, thread_syscall_return)); +} + + + +kern_return_t +semaphore_wait_trap_internal( + mach_port_name_t name, + void (*caller_cont)(kern_return_t)) { semaphore_t semaphore; kern_return_t kr; kr = port_name_to_semaphore(name, &semaphore); - if (kr != KERN_SUCCESS) - return kr; - - kr = semaphore_wait_internal(semaphore, - SEMAPHORE_NULL, - (mach_timespec_t *)0, - thread_syscall_return); - semaphore_dereference(semaphore); + if (kr == KERN_SUCCESS) { + kr = semaphore_wait_internal(semaphore, + SEMAPHORE_NULL, + 0ULL, SEMAPHORE_OPTION_NONE, + caller_cont); + semaphore_dereference(semaphore); + } return kr; } @@ -748,16 +870,24 @@ kern_return_t semaphore_timedwait( semaphore_t semaphore, mach_timespec_t wait_time) -{ +{ + int option = SEMAPHORE_OPTION_NONE; + uint64_t deadline = 0; + if (semaphore == SEMAPHORE_NULL) return KERN_INVALID_ARGUMENT; if(BAD_MACH_TIMESPEC(&wait_time)) return KERN_INVALID_VALUE; + + if (wait_time.tv_sec == 0 && wait_time.tv_nsec == 0) + option = SEMAPHORE_TIMEOUT_NOBLOCK; + else + deadline = semaphore_deadline(wait_time.tv_sec, wait_time.tv_nsec); return (semaphore_wait_internal(semaphore, SEMAPHORE_NULL, - &wait_time, + deadline, option, (void(*)(kern_return_t))0)); } @@ -775,10 +905,20 @@ semaphore_timedwait( */ kern_return_t semaphore_timedwait_trap( - mach_port_name_t name, - unsigned int sec, - clock_res_t nsec) + struct semaphore_timedwait_trap_args *args) { + + return(semaphore_timedwait_trap_internal(args->wait_name, args->sec, args->nsec, thread_syscall_return)); +} + + +kern_return_t +semaphore_timedwait_trap_internal( + mach_port_name_t name, + unsigned int sec, + clock_res_t nsec, + void (*caller_cont)(kern_return_t)) +{ semaphore_t semaphore; mach_timespec_t wait_time; kern_return_t kr; @@ -789,14 +929,21 @@ semaphore_timedwait_trap( return KERN_INVALID_VALUE; kr = port_name_to_semaphore(name, &semaphore); - if (kr != KERN_SUCCESS) - return kr; - - kr = semaphore_wait_internal(semaphore, - SEMAPHORE_NULL, - &wait_time, - thread_syscall_return); - semaphore_dereference(semaphore); + if (kr == KERN_SUCCESS) { + int option = SEMAPHORE_OPTION_NONE; + uint64_t deadline = 0; + + if (sec == 0 && nsec == 0) + option = SEMAPHORE_TIMEOUT_NOBLOCK; + else + deadline = semaphore_deadline(sec, nsec); + + kr = semaphore_wait_internal(semaphore, + SEMAPHORE_NULL, + deadline, option, + caller_cont); + semaphore_dereference(semaphore); + } return kr; } @@ -818,7 +965,7 @@ semaphore_wait_signal( return(semaphore_wait_internal(wait_semaphore, signal_semaphore, - (mach_timespec_t *)0, + 0ULL, SEMAPHORE_OPTION_NONE, (void(*)(kern_return_t))0)); } @@ -830,30 +977,33 @@ semaphore_wait_signal( */ kern_return_t semaphore_wait_signal_trap( - mach_port_name_t wait_name, - mach_port_name_t signal_name) + struct semaphore_wait_signal_trap_args *args) +{ + return(semaphore_wait_signal_trap_internal(args->wait_name, args->signal_name, thread_syscall_return)); +} + +kern_return_t +semaphore_wait_signal_trap_internal( + mach_port_name_t wait_name, + mach_port_name_t signal_name, + void (*caller_cont)(kern_return_t)) { semaphore_t wait_semaphore; semaphore_t signal_semaphore; kern_return_t kr; kr = port_name_to_semaphore(signal_name, &signal_semaphore); - if (kr != KERN_SUCCESS) - return kr; - - kr = port_name_to_semaphore(wait_name, &wait_semaphore); - if (kr != KERN_SUCCESS) { + if (kr == KERN_SUCCESS) { + kr = port_name_to_semaphore(wait_name, &wait_semaphore); + if (kr == KERN_SUCCESS) { + kr = semaphore_wait_internal(wait_semaphore, + signal_semaphore, + 0ULL, SEMAPHORE_OPTION_NONE, + caller_cont); + semaphore_dereference(wait_semaphore); + } semaphore_dereference(signal_semaphore); - return kr; } - - kr = semaphore_wait_internal(wait_semaphore, - signal_semaphore, - (mach_timespec_t *)0, - thread_syscall_return); - - semaphore_dereference(wait_semaphore); - semaphore_dereference(signal_semaphore); return kr; } @@ -873,15 +1023,23 @@ semaphore_timedwait_signal( semaphore_t signal_semaphore, mach_timespec_t wait_time) { + int option = SEMAPHORE_OPTION_NONE; + uint64_t deadline = 0; + if (wait_semaphore == SEMAPHORE_NULL) return KERN_INVALID_ARGUMENT; if(BAD_MACH_TIMESPEC(&wait_time)) return KERN_INVALID_VALUE; + + if (wait_time.tv_sec == 0 && wait_time.tv_nsec == 0) + option = SEMAPHORE_TIMEOUT_NOBLOCK; + else + deadline = semaphore_deadline(wait_time.tv_sec, wait_time.tv_nsec); return(semaphore_wait_internal(wait_semaphore, signal_semaphore, - &wait_time, + deadline, option, (void(*)(kern_return_t))0)); } @@ -893,10 +1051,18 @@ semaphore_timedwait_signal( */ kern_return_t semaphore_timedwait_signal_trap( - mach_port_name_t wait_name, - mach_port_name_t signal_name, - unsigned int sec, - clock_res_t nsec) + struct semaphore_timedwait_signal_trap_args *args) +{ + return(semaphore_timedwait_signal_trap_internal(args->wait_name, args->signal_name, args->sec, args->nsec, thread_syscall_return)); +} + +kern_return_t +semaphore_timedwait_signal_trap_internal( + mach_port_name_t wait_name, + mach_port_name_t signal_name, + unsigned int sec, + clock_res_t nsec, + void (*caller_cont)(kern_return_t)) { semaphore_t wait_semaphore; semaphore_t signal_semaphore; @@ -909,22 +1075,25 @@ semaphore_timedwait_signal_trap( return KERN_INVALID_VALUE; kr = port_name_to_semaphore(signal_name, &signal_semaphore); - if (kr != KERN_SUCCESS) - return kr; - - kr = port_name_to_semaphore(wait_name, &wait_semaphore); - if (kr != KERN_SUCCESS) { + if (kr == KERN_SUCCESS) { + kr = port_name_to_semaphore(wait_name, &wait_semaphore); + if (kr == KERN_SUCCESS) { + int option = SEMAPHORE_OPTION_NONE; + uint64_t deadline = 0; + + if (sec == 0 && nsec == 0) + option = SEMAPHORE_TIMEOUT_NOBLOCK; + else + deadline = semaphore_deadline(sec, nsec); + + kr = semaphore_wait_internal(wait_semaphore, + signal_semaphore, + deadline, option, + caller_cont); + semaphore_dereference(wait_semaphore); + } semaphore_dereference(signal_semaphore); - return kr; } - - kr = semaphore_wait_internal(wait_semaphore, - signal_semaphore, - &wait_time, - thread_syscall_return); - - semaphore_dereference(wait_semaphore); - semaphore_dereference(signal_semaphore); return kr; } @@ -939,15 +1108,7 @@ void semaphore_reference( semaphore_t semaphore) { - spl_t spl_level; - - spl_level = splsched(); - semaphore_lock(semaphore); - - semaphore->ref_count++; - - semaphore_unlock(semaphore); - splx(spl_level); + os_ref_retain(&semaphore->ref_count); } /* @@ -960,21 +1121,71 @@ void semaphore_dereference( semaphore_t semaphore) { - int ref_count; - spl_t spl_level; + uint32_t collisions; + spl_t spl_level; - if (semaphore != NULL) { - spl_level = splsched(); - semaphore_lock(semaphore); + if (semaphore == NULL) + return; - ref_count = --(semaphore->ref_count); + if (os_ref_release(&semaphore->ref_count) > 0) { + return; + } - semaphore_unlock(semaphore); - splx(spl_level); + /* + * Last ref, clean up the port [if any] + * associated with the semaphore, destroy + * it (if still active) and then free + * the semaphore. + */ + ipc_port_t port = semaphore->port; - if (ref_count == 0) { - assert(wait_queue_empty(&semaphore->wait_queue)); - zfree(semaphore_zone, (vm_offset_t)semaphore); - } + if (IP_VALID(port)) { + assert(!port->ip_srights); + ipc_port_dealloc_kernel(port); } + + /* + * Lock the semaphore to lock in the owner task reference. + * Then continue to try to lock the task (inverse order). + */ + spl_level = splsched(); + semaphore_lock(semaphore); + for (collisions = 0; semaphore->active; collisions++) { + task_t task = semaphore->owner; + + assert(task != TASK_NULL); + + if (task_lock_try(task)) { + semaphore_destroy_internal(task, semaphore); + /* semaphore unlocked */ + splx(spl_level); + task_unlock(task); + goto out; + } + + /* failed to get out-of-order locks */ + semaphore_unlock(semaphore); + splx(spl_level); + mutex_pause(collisions); + spl_level = splsched(); + semaphore_lock(semaphore); + } + semaphore_unlock(semaphore); + splx(spl_level); + + out: + zfree(semaphore_zone, semaphore); +} + +#define WAITQ_TO_SEMA(wq) ((semaphore_t) ((uintptr_t)(wq) - offsetof(struct semaphore, waitq))) +void +kdp_sema_find_owner(struct waitq * waitq, __assert_only event64_t event, thread_waitinfo_t * waitinfo) +{ + semaphore_t sem = WAITQ_TO_SEMA(waitq); + assert(event == SEMAPHORE_EVENT); + assert(kdp_is_in_zone(sem, "semaphores")); + + waitinfo->context = VM_KERNEL_UNSLIDE_OR_PERM(sem->port); + if (sem->owner) + waitinfo->owner = pid_from_task(sem->owner); }