| 1 | /* | 
| 2 |  * Copyright (c) 2016 Apple Inc. All rights reserved. | 
| 3 |  * | 
| 4 |  * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | 
| 5 |  * | 
| 6 |  * This file contains Original Code and/or Modifications of Original Code | 
| 7 |  * as defined in and that are subject to the Apple Public Source License | 
| 8 |  * Version 2.0 (the 'License'). You may not use this file except in | 
| 9 |  * compliance with the License. The rights granted to you under the License | 
| 10 |  * may not be used to create, or enable the creation or redistribution of, | 
| 11 |  * unlawful or unlicensed copies of an Apple operating system, or to | 
| 12 |  * circumvent, violate, or enable the circumvention or violation of, any | 
| 13 |  * terms of an Apple operating system software license agreement. | 
| 14 |  * | 
| 15 |  * Please obtain a copy of the License at | 
| 16 |  * http://www.opensource.apple.com/apsl/ and read it before using this file. | 
| 17 |  * | 
| 18 |  * The Original Code and all software distributed under the License are | 
| 19 |  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | 
| 20 |  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | 
| 21 |  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | 
| 22 |  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | 
| 23 |  * Please see the License for the specific language governing rights and | 
| 24 |  * limitations under the License. | 
| 25 |  * | 
| 26 |  * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | 
| 27 |  */ | 
| 28 | #include <stddef.h> | 
| 29 | #include <kern/debug.h> | 
| 30 | #include <kern/locks.h> | 
| 31 | #include <kern/thread.h> | 
| 32 | #include <kern/thread_call.h> | 
| 33 | #include <net/nwk_wq.h> | 
| 34 | #include <sys/proc_internal.h> | 
| 35 | #include <sys/systm.h> | 
| 36 | #include <sys/mcache.h> | 
| 37 |  | 
| 38 | static TAILQ_HEAD(, nwk_wq_entry) nwk_wq_head = | 
| 39 |     TAILQ_HEAD_INITIALIZER(nwk_wq_head); | 
| 40 | static LCK_GRP_DECLARE(nwk_wq_lock_group, "Network work queue lock" ); | 
| 41 | static LCK_MTX_DECLARE(nwk_wq_lock, &nwk_wq_lock_group); | 
| 42 |  | 
| 43 | /* Wait channel for Network work queue */ | 
| 44 | static void *nwk_wq_waitch = NULL; | 
| 45 | static void nwk_wq_thread_func(void *, wait_result_t); | 
| 46 |  | 
| 47 | static int nwk_wq_thread_cont(int err); | 
| 48 | static void nwk_wq_thread_func(void *v, wait_result_t w); | 
| 49 |  | 
| 50 | void | 
| 51 | nwk_wq_init(void) | 
| 52 | { | 
| 53 | 	thread_t nwk_wq_thread __single = THREAD_NULL; | 
| 54 |  | 
| 55 | 	if (kernel_thread_start(continuation: nwk_wq_thread_func, | 
| 56 | 	    NULL, new_thread: &nwk_wq_thread) != KERN_SUCCESS) { | 
| 57 | 		panic_plain("%s: couldn't create network work queue thread" , __func__); | 
| 58 | 		/* NOTREACHED */ | 
| 59 | 	} | 
| 60 | 	thread_deallocate(thread: nwk_wq_thread); | 
| 61 | } | 
| 62 |  | 
| 63 | static int | 
| 64 | nwk_wq_thread_cont(int err) | 
| 65 | { | 
| 66 | 	TAILQ_HEAD(, nwk_wq_entry) temp_nwk_wq_head; | 
| 67 | 	struct nwk_wq_entry *nwk_item __single; | 
| 68 | 	struct nwk_wq_entry *nwk_item_next __single; | 
| 69 |  | 
| 70 | #pragma unused(err) | 
| 71 | 	for (;;) { | 
| 72 | 		nwk_item = NULL; | 
| 73 | 		nwk_item_next = NULL; | 
| 74 | 		TAILQ_INIT(&temp_nwk_wq_head); | 
| 75 |  | 
| 76 | 		LCK_MTX_ASSERT(&nwk_wq_lock, LCK_MTX_ASSERT_OWNED); | 
| 77 | 		while (TAILQ_FIRST(&nwk_wq_head) == NULL) { | 
| 78 | 			(void) msleep0(chan: &nwk_wq_waitch, mtx: &nwk_wq_lock, | 
| 79 | 			    pri: (PZERO - 1), wmesg: "nwk_wq_thread_cont" , timo: 0, | 
| 80 | 			    continuation: nwk_wq_thread_cont); | 
| 81 | 			/* NOTREACHED */ | 
| 82 | 		} | 
| 83 |  | 
| 84 | 		TAILQ_SWAP(&temp_nwk_wq_head, &nwk_wq_head, nwk_wq_entry, nwk_wq_link); | 
| 85 | 		VERIFY(TAILQ_EMPTY(&nwk_wq_head)); | 
| 86 | 		lck_mtx_unlock(lck: &nwk_wq_lock); | 
| 87 |  | 
| 88 | 		VERIFY(TAILQ_FIRST(&temp_nwk_wq_head) != NULL); | 
| 89 | 		TAILQ_FOREACH_SAFE(nwk_item, &temp_nwk_wq_head, nwk_wq_link, nwk_item_next) { | 
| 90 | 			nwk_item->func(nwk_item); | 
| 91 | 			/* nwk_item has been freed by the callback */ | 
| 92 | 		} | 
| 93 | 		lck_mtx_lock(lck: &nwk_wq_lock); | 
| 94 | 	} | 
| 95 | } | 
| 96 |  | 
| 97 | __dead2 | 
| 98 | static void | 
| 99 | nwk_wq_thread_func(void *v, wait_result_t w) | 
| 100 | { | 
| 101 | #pragma unused(v, w) | 
| 102 | 	lck_mtx_lock(lck: &nwk_wq_lock); | 
| 103 | 	(void) msleep0(chan: &nwk_wq_waitch, mtx: &nwk_wq_lock, | 
| 104 | 	    pri: (PZERO - 1), wmesg: "nwk_wq_thread_func" , timo: 0, continuation: nwk_wq_thread_cont); | 
| 105 | 	/* | 
| 106 | 	 * msleep0() shouldn't have returned as PCATCH was not set; | 
| 107 | 	 * therefore assert in this case. | 
| 108 | 	 */ | 
| 109 | 	lck_mtx_unlock(lck: &nwk_wq_lock); | 
| 110 | 	VERIFY(0); | 
| 111 | } | 
| 112 |  | 
| 113 | void | 
| 114 | nwk_wq_enqueue(struct nwk_wq_entry *nwk_item) | 
| 115 | { | 
| 116 | 	lck_mtx_lock(lck: &nwk_wq_lock); | 
| 117 | 	TAILQ_INSERT_TAIL(&nwk_wq_head, nwk_item, nwk_wq_link); | 
| 118 | 	lck_mtx_unlock(lck: &nwk_wq_lock); | 
| 119 | 	wakeup(chan: (caddr_t)&nwk_wq_waitch); | 
| 120 | } | 
| 121 |  |