1 /* $NetBSD: npf_worker.c,v 1.1 2013/06/02 02:20:04 rmind Exp $ */ 2 3 /*- 4 * Copyright (c) 2010-2013 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This material is based upon work partially supported by The 8 * NetBSD Foundation under a contract with Mindaugas Rasiukevicius. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: npf_worker.c,v 1.1 2013/06/02 02:20:04 rmind Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 38 #include <sys/mutex.h> 39 #include <sys/kernel.h> 40 #include <sys/kthread.h> 41 42 #include "npf_impl.h" 43 44 #define NPF_MAX_WORKS 4 45 #define WORKER_INTERVAL mstohz(5 * 1000) 46 47 static kmutex_t worker_lock; 48 static kcondvar_t worker_cv; 49 static kcondvar_t worker_event_cv; 50 static lwp_t * worker_lwp; 51 static uint64_t worker_loop; 52 53 static npf_workfunc_t work_funcs[NPF_MAX_WORKS]; 54 55 static void npf_worker(void *) __dead; 56 57 int 58 npf_worker_sysinit(void) 59 { 60 mutex_init(&worker_lock, MUTEX_DEFAULT, IPL_SOFTNET); 61 cv_init(&worker_cv, "npfgccv"); 62 cv_init(&worker_event_cv, "npfevcv"); 63 worker_lwp = (lwp_t *)0xdeadbabe; 64 worker_loop = 1; 65 66 if (kthread_create(PRI_NONE, KTHREAD_MPSAFE | KTHREAD_MUSTJOIN, NULL, 67 npf_worker, NULL, &worker_lwp, "npfgc")) { 68 return ENOMEM; 69 } 70 return 0; 71 } 72 73 void 74 npf_worker_sysfini(void) 75 { 76 lwp_t *l = worker_lwp; 77 78 /* Notify the worker and wait for the exit. */ 79 mutex_enter(&worker_lock); 80 worker_lwp = NULL; 81 cv_broadcast(&worker_cv); 82 mutex_exit(&worker_lock); 83 kthread_join(l); 84 85 /* LWP has exited, destroy the structures. */ 86 cv_destroy(&worker_cv); 87 cv_destroy(&worker_event_cv); 88 mutex_destroy(&worker_lock); 89 } 90 91 void 92 npf_worker_signal(void) 93 { 94 mutex_enter(&worker_lock); 95 cv_signal(&worker_cv); 96 mutex_exit(&worker_lock); 97 } 98 99 static bool 100 npf_worker_testset(npf_workfunc_t find, npf_workfunc_t set) 101 { 102 for (u_int i = 0; i < NPF_MAX_WORKS; i++) { 103 if (work_funcs[i] == find) { 104 work_funcs[i] = set; 105 return true; 106 } 107 } 108 return false; 109 } 110 111 void 112 npf_worker_register(npf_workfunc_t func) 113 { 114 mutex_enter(&worker_lock); 115 npf_worker_testset(NULL, func); 116 mutex_exit(&worker_lock); 117 } 118 119 void 120 npf_worker_unregister(npf_workfunc_t func) 121 { 122 uint64_t l = worker_loop; 123 124 mutex_enter(&worker_lock); 125 npf_worker_testset(func, NULL); 126 while (worker_loop == l) { 127 cv_signal(&worker_cv); 128 cv_wait(&worker_event_cv, &worker_lock); 129 } 130 mutex_exit(&worker_lock); 131 } 132 133 static void 134 npf_worker(void *arg) 135 { 136 for (;;) { 137 const bool finish = (worker_lwp == NULL); 138 u_int i = NPF_MAX_WORKS; 139 npf_workfunc_t work; 140 141 /* Run the jobs. */ 142 while (i--) { 143 if ((work = work_funcs[i]) != NULL) { 144 work(); 145 } 146 } 147 148 /* Exit if requested and all jobs are done. */ 149 if (finish) { 150 break; 151 } 152 153 /* Sleep and periodically wake up, unless we get notified. */ 154 mutex_enter(&worker_lock); 155 worker_loop++; 156 cv_broadcast(&worker_event_cv); 157 cv_timedwait(&worker_cv, &worker_lock, WORKER_INTERVAL); 158 mutex_exit(&worker_lock); 159 } 160 kthread_exit(0); 161 } 162