/* * Routine: wait_queue_member_locked * Purpose: * Indicate if this set queue is a member of the queue * Conditions: * The wait queue is locked * The set queue is just that, a set queue */ static boolean_t wait_queue_member_locked( wait_queue_t wq, wait_queue_set_t wq_set) { wait_queue_element_t wq_element; queue_t q; assert(wait_queue_held(wq)); assert(wait_queue_is_set(wq_set)); q = &wq->wq_queue; wq_element = (wait_queue_element_t) queue_first(q); while (!queue_end(q, (queue_entry_t)wq_element)) { WAIT_QUEUE_ELEMENT_CHECK(wq, wq_element); if ((wq_element->wqe_type == WAIT_QUEUE_LINK) || (wq_element->wqe_type == WAIT_QUEUE_LINK_NOALLOC)) { wait_queue_link_t wql = (wait_queue_link_t)wq_element; if (wql->wql_setqueue == wq_set) return TRUE; } wq_element = (wait_queue_element_t) queue_next((queue_t) wq_element); } return FALSE; }
/* * Routine: wait_queue_link_internal * Purpose: * Insert a set wait queue into a wait queue. This * requires us to link the two together using a wait_queue_link * structure that was provided. * Conditions: * The wait queue being inserted must be inited as a set queue * The wait_queue_link structure must already be properly typed */ static kern_return_t wait_queue_link_internal( wait_queue_t wq, wait_queue_set_t wq_set, wait_queue_link_t wql) { wait_queue_element_t wq_element; queue_t q; spl_t s; if (!wait_queue_is_valid(wq) || !wait_queue_is_set(wq_set)) return KERN_INVALID_ARGUMENT; /* * There are probably fewer threads and sets associated with * the wait queue than there are wait queues associated with * the set. So let's validate it that way. */ s = splsched(); wait_queue_lock(wq); q = &wq->wq_queue; wq_element = (wait_queue_element_t) queue_first(q); while (!queue_end(q, (queue_entry_t)wq_element)) { WAIT_QUEUE_ELEMENT_CHECK(wq, wq_element); if ((wq_element->wqe_type == WAIT_QUEUE_LINK || wq_element->wqe_type == WAIT_QUEUE_LINK_NOALLOC) && ((wait_queue_link_t)wq_element)->wql_setqueue == wq_set) { wait_queue_unlock(wq); splx(s); return KERN_ALREADY_IN_SET; } wq_element = (wait_queue_element_t) queue_next((queue_t) wq_element); } /* * Not already a member, so we can add it. */ wqs_lock(wq_set); WAIT_QUEUE_SET_CHECK(wq_set); assert(wql->wql_type == WAIT_QUEUE_LINK || wql->wql_type == WAIT_QUEUE_LINK_NOALLOC); wql->wql_queue = wq; wql_clear_prepost(wql); queue_enter(&wq->wq_queue, wql, wait_queue_link_t, wql_links); wql->wql_setqueue = wq_set; queue_enter(&wq_set->wqs_setlinks, wql, wait_queue_link_t, wql_setlinks); wqs_unlock(wq_set); wait_queue_unlock(wq); splx(s); return KERN_SUCCESS; }
kern_return_t wait_queue_sub_clearrefs( wait_queue_set_t wq_set) { if (!wait_queue_is_set(wq_set)) return KERN_INVALID_ARGUMENT; wqs_lock(wq_set); wq_set->wqs_refcount = 0; wqs_unlock(wq_set); return KERN_SUCCESS; }
/* * Routine: wait_queue_set_unlink_all * Purpose: * Remove the linkage between a set wait queue and all its * member wait queues. The link structures are freed for those * links which were dynamically allocated. * Conditions: * The wait queue must be a set */ kern_return_t wait_queue_set_unlink_all( wait_queue_set_t wq_set) { wait_queue_link_t wql; wait_queue_t wq; queue_t q; queue_head_t links_queue_head; queue_t links = &links_queue_head; spl_t s; if (!wait_queue_is_set(wq_set)) { return KERN_INVALID_ARGUMENT; } queue_init(links); retry: s = splsched(); wqs_lock(wq_set); q = &wq_set->wqs_setlinks; wql = (wait_queue_link_t)queue_first(q); while (!queue_end(q, (queue_entry_t)wql)) { WAIT_QUEUE_SET_LINK_CHECK(wq_set, wql); wq = wql->wql_queue; if (wait_queue_lock_try(wq)) { boolean_t alloced; alloced = (wql->wql_type == WAIT_QUEUE_LINK); wait_queue_unlink_locked(wq, wq_set, wql); wait_queue_unlock(wq); if (alloced) enqueue(links, &wql->wql_links); wql = (wait_queue_link_t)queue_first(q); } else { wqs_unlock(wq_set); splx(s); delay(1); goto retry; } } wqs_unlock(wq_set); splx(s); while (!queue_empty (links)) { wql = (wait_queue_link_t) dequeue(links); zfree(_wait_queue_link_zone, wql); } return(KERN_SUCCESS); }
/* * Routine: wait_queue_set_free * Purpose: * Free an allocated wait queue set * Conditions: * May block. */ kern_return_t wait_queue_set_free( wait_queue_set_t wq_set) { if (!wait_queue_is_set(wq_set)) return KERN_INVALID_ARGUMENT; if (!queue_empty(&wq_set->wqs_wait_queue.wq_queue)) return KERN_FAILURE; zfree(_wait_queue_set_zone, wq_set); return KERN_SUCCESS; }
/* * Routine: wait_queue_set_free * Purpose: * Free an allocated wait queue set * Conditions: * May block. */ kern_return_t wait_queue_set_free( wait_queue_set_t wq_set) { if (!wait_queue_is_set(wq_set)) return KERN_INVALID_ARGUMENT; if (!queue_empty(&wq_set->wqs_wait_queue.wq_queue)) return KERN_FAILURE; kfree((vm_offset_t)wq_set, sizeof(struct wait_queue_set)); return KERN_SUCCESS; }
/* * Routine: wait_queue_set_unlink_all * Purpose: * Remove the linkage between a set wait queue and all its * member wait queues. The link structures are freed. * Conditions: * The wait queue must be a set */ kern_return_t wait_queue_set_unlink_all( wait_queue_set_t wq_set) { wait_queue_link_t wql; wait_queue_t wq; queue_t q; queue_head_t links_queue_head; queue_t links = &links_queue_head; kern_return_t kret; spl_t s; if (!wait_queue_is_set(wq_set)) { return KERN_INVALID_ARGUMENT; } queue_init(links); retry: s = splsched(); wqs_lock(wq_set); q = &wq_set->wqs_setlinks; wql = (wait_queue_link_t)queue_first(q); while (!queue_end(q, (queue_entry_t)wql)) { WAIT_QUEUE_SET_LINK_CHECK(wq_set, wql); wq = wql->wql_queue; if (wait_queue_lock_try(wq)) { wait_queue_unlink_locked(wq, wq_set, wql); wait_queue_unlock(wq); enqueue(links, &wql->wql_links); wql = (wait_queue_link_t)queue_first(q); } else { wqs_unlock(wq_set); splx(s); delay(1); goto retry; } } wqs_unlock(wq_set); splx(s); while (!queue_empty (links)) { wql = (wait_queue_link_t) dequeue(links); kfree((vm_offset_t)wql, sizeof(struct wait_queue_link)); } return(KERN_SUCCESS); }
/* * Routine: wait_queue_unlink * Purpose: * Remove the linkage between a wait queue and a set, * freeing the linkage structure. * Conditions: * The wait queue being must be a member set queue */ kern_return_t wait_queue_unlink( wait_queue_t wq, wait_queue_set_t wq_set) { wait_queue_element_t wq_element; wait_queue_link_t wql; queue_t q; spl_t s; if (!wait_queue_is_valid(wq) || !wait_queue_is_set(wq_set)) { return KERN_INVALID_ARGUMENT; } s = splsched(); wait_queue_lock(wq); q = &wq->wq_queue; wq_element = (wait_queue_element_t) queue_first(q); while (!queue_end(q, (queue_entry_t)wq_element)) { WAIT_QUEUE_ELEMENT_CHECK(wq, wq_element); if (wq_element->wqe_type == WAIT_QUEUE_LINK || wq_element->wqe_type == WAIT_QUEUE_LINK_NOALLOC) { wql = (wait_queue_link_t)wq_element; if (wql->wql_setqueue == wq_set) { boolean_t alloced; alloced = (wql->wql_type == WAIT_QUEUE_LINK); wqs_lock(wq_set); wait_queue_unlink_locked(wq, wq_set, wql); wqs_unlock(wq_set); wait_queue_unlock(wq); splx(s); if (alloced) zfree(_wait_queue_link_zone, wql); return KERN_SUCCESS; } } wq_element = (wait_queue_element_t) queue_next((queue_t) wq_element); } wait_queue_unlock(wq); splx(s); return KERN_NOT_IN_SET; }
/* * Routine: wait_queue_member * Purpose: * Indicate if this set queue is a member of the queue * Conditions: * The set queue is just that, a set queue */ boolean_t wait_queue_member( wait_queue_t wq, wait_queue_set_t wq_set) { boolean_t ret; spl_t s; if (!wait_queue_is_set(wq_set)) return FALSE; s = splsched(); wait_queue_lock(wq); ret = wait_queue_member_locked(wq, wq_set); wait_queue_unlock(wq); splx(s); return ret; }
kern_return_t wait_queue_set_unlink_one( wait_queue_set_t wq_set, wait_queue_link_t wql) { wait_queue_t wq; spl_t s; assert(wait_queue_is_set(wq_set)); retry: s = splsched(); wqs_lock(wq_set); WAIT_QUEUE_SET_CHECK(wq_set); /* Already unlinked, e.g. by selclearthread() */ if (wql->wql_type == WAIT_QUEUE_UNLINKED) { goto out; } WAIT_QUEUE_SET_LINK_CHECK(wq_set, wql); /* On a wait queue, and we hold set queue lock ... */ wq = wql->wql_queue; if (wait_queue_lock_try(wq)) { wait_queue_unlink_locked(wq, wq_set, wql); wait_queue_unlock(wq); } else { wqs_unlock(wq_set); splx(s); delay(1); goto retry; } out: wqs_unlock(wq_set); splx(s); return KERN_SUCCESS; }
/* * Routine: wait_queue_set_unlink_all_nofree * Purpose: * Remove the linkage between a set wait queue and all its * member wait queues. The link structures are not freed, nor * returned. It is the caller's responsibility to track and free * them. * Conditions: * The wait queue being must be a member set queue */ kern_return_t wait_queue_set_unlink_all_nofree( wait_queue_set_t wq_set) { wait_queue_link_t wql; wait_queue_t wq; queue_t q; kern_return_t kret; spl_t s; if (!wait_queue_is_set(wq_set)) { return KERN_INVALID_ARGUMENT; } retry: s = splsched(); wqs_lock(wq_set); q = &wq_set->wqs_setlinks; wql = (wait_queue_link_t)queue_first(q); while (!queue_end(q, (queue_entry_t)wql)) { WAIT_QUEUE_SET_LINK_CHECK(wq_set, wql); wq = wql->wql_queue; if (wait_queue_lock_try(wq)) { wait_queue_unlink_locked(wq, wq_set, wql); wait_queue_unlock(wq); wql = (wait_queue_link_t)queue_first(q); } else { wqs_unlock(wq_set); splx(s); delay(1); goto retry; } } wqs_unlock(wq_set); splx(s); return(KERN_SUCCESS); }
/* * Routine: wait_queue_unlink * Purpose: * Remove the linkage between a wait queue and a set, * freeing the linkage structure. * Conditions: * The wait queue being must be a member set queue */ kern_return_t wait_queue_unlink( wait_queue_t wq, wait_queue_set_t wq_set) { wait_queue_element_t wq_element; wait_queue_link_t wql; queue_t q; spl_t s; if (!wait_queue_is_queue(wq) || !wait_queue_is_set(wq_set)) { return KERN_INVALID_ARGUMENT; } s = splsched(); wait_queue_lock(wq); q = &wq->wq_queue; wq_element = (wait_queue_element_t) queue_first(q); while (!queue_end(q, (queue_entry_t)wq_element)) { WAIT_QUEUE_ELEMENT_CHECK(wq, wq_element); if (wq_element->wqe_type == WAIT_QUEUE_LINK) { wql = (wait_queue_link_t)wq_element; if (wql->wql_setqueue == wq_set) { wqs_lock(wq_set); wait_queue_unlink_locked(wq, wq_set, wql); wqs_unlock(wq_set); wait_queue_unlock(wq); splx(s); kfree((vm_offset_t)wql, sizeof(struct wait_queue_link)); return KERN_SUCCESS; } } wq_element = (wait_queue_element_t) queue_next((queue_t) wq_element); } wait_queue_unlock(wq); splx(s); return KERN_NOT_IN_SET; }
kern_return_t wait_queue_sub_clearrefs( wait_queue_set_t wq_set) { wait_queue_link_t wql; queue_t q; spl_t s; if (!wait_queue_is_set(wq_set)) return KERN_INVALID_ARGUMENT; s = splsched(); wqs_lock(wq_set); q = &wq_set->wqs_preposts; while (!queue_empty(q)) { queue_remove_first(q, wql, wait_queue_link_t, wql_preposts); assert(!wql_is_preposted(wql)); } wqs_unlock(wq_set); splx(s); return KERN_SUCCESS; }