1 /*- 2 * Copyright (c) 2018, Matthew Macy <mmacy@freebsd.org> 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * 10 * 2. Neither the name of Matthew Macy nor the names of its 11 * contributors may be used to endorse or promote products derived from 12 * this software without specific prior written permission. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 18 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 * POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/types.h> 32 #include <sys/counter.h> 33 #include <sys/epoch.h> 34 #include <sys/gtaskqueue.h> 35 #include <sys/kernel.h> 36 #include <sys/kthread.h> 37 #include <sys/lock.h> 38 #include <sys/malloc.h> 39 #include <sys/module.h> 40 #include <sys/mutex.h> 41 #include <sys/proc.h> 42 #include <sys/sched.h> 43 #include <sys/smp.h> 44 #include <sys/sysctl.h> 45 #include <sys/systm.h> 46 47 48 struct epoch_test_instance { 49 int threadid; 50 }; 51 52 static int inited; 53 static int iterations; 54 #define ET_EXITING 0x1 55 static volatile int state_flags; 56 static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2); 57 MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF); 58 static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2); 59 MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF); 60 static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2); 61 MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF); 62 epoch_t test_epoch; 63 64 static void 65 epoch_testcase1(struct epoch_test_instance *eti) 66 { 67 int i, startticks; 68 struct mtx *mtxp; 69 70 startticks = ticks; 71 i = 0; 72 if (eti->threadid & 0x1) 73 mtxp = &mutexA; 74 else 75 mtxp = &mutexB; 76 77 while (i < iterations) { 78 epoch_enter(test_epoch); 79 mtx_lock(mtxp); 80 i++; 81 mtx_unlock(mtxp); 82 epoch_exit(test_epoch); 83 epoch_wait(test_epoch); 84 } 85 printf("test1: thread: %d took %d ticks to complete %d iterations\n", 86 eti->threadid, ticks - startticks, iterations); 87 } 88 89 static void 90 epoch_testcase2(struct epoch_test_instance *eti) 91 { 92 int i, startticks; 93 struct mtx *mtxp; 94 95 startticks = ticks; 96 i = 0; 97 mtxp = &mutexA; 98 99 while (i < iterations) { 100 epoch_enter(test_epoch); 101 mtx_lock(mtxp); 102 DELAY(1); 103 i++; 104 mtx_unlock(mtxp); 105 epoch_exit(test_epoch); 106 epoch_wait(test_epoch); 107 } 108 printf("test2: thread: %d took %d ticks to complete %d iterations\n", 109 eti->threadid, ticks - startticks, iterations); 110 } 111 112 static void 113 testloop(void *arg) { 114 115 mtx_lock(&state_mtx); 116 while ((state_flags & ET_EXITING) == 0) { 117 msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0); 118 if (state_flags & ET_EXITING) 119 goto out; 120 mtx_unlock(&state_mtx); 121 epoch_testcase2(arg); 122 pause("W", 500); 123 epoch_testcase1(arg); 124 mtx_lock(&state_mtx); 125 } 126 out: 127 mtx_unlock(&state_mtx); 128 kthread_exit(); 129 } 130 131 static struct thread *testthreads[MAXCPU]; 132 static struct epoch_test_instance etilist[MAXCPU]; 133 134 static int 135 test_modinit(void) 136 { 137 struct thread *td; 138 int i, error, pri_range, pri_off; 139 140 pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME; 141 test_epoch = epoch_alloc(); 142 for (i = 0; i < mp_ncpus*2; i++) { 143 etilist[i].threadid = i; 144 error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i], 145 0, 0, "epoch_test_%d", i); 146 if (error) { 147 printf("%s: kthread_add(epoch_test): error %d", __func__, 148 error); 149 } else { 150 pri_off = (i*4)%pri_range; 151 td = testthreads[i]; 152 thread_lock(td); 153 sched_prio(td, PRI_MIN_REALTIME + pri_off); 154 thread_unlock(td); 155 } 156 } 157 inited = 1; 158 return (0); 159 } 160 161 static int 162 epochtest_execute(SYSCTL_HANDLER_ARGS) 163 { 164 int error, v; 165 166 if (inited == 0) 167 return (ENOENT); 168 169 v = 0; 170 error = sysctl_handle_int(oidp, &v, 0, req); 171 if (error) 172 return (error); 173 if (req->newptr == NULL) 174 return (error); 175 if (v == 0) 176 return (0); 177 mtx_lock(&state_mtx); 178 iterations = v; 179 wakeup(&state_mtx); 180 mtx_unlock(&state_mtx); 181 182 return (0); 183 } 184 185 SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW, 0, "Epoch Test Framework"); 186 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, (CTLTYPE_INT | CTLFLAG_RW), 187 0, 0, epochtest_execute, "I", "Execute an epoch test"); 188 189 static int 190 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 191 { 192 int err; 193 194 switch (what) { 195 case MOD_LOAD: 196 if ((err = test_modinit()) != 0) 197 return (err); 198 break; 199 case MOD_UNLOAD: 200 mtx_lock(&state_mtx); 201 state_flags = ET_EXITING; 202 wakeup(&state_mtx); 203 mtx_unlock(&state_mtx); 204 /* yes --- gross */ 205 pause("epoch unload", 3*hz); 206 break; 207 default: 208 return (EOPNOTSUPP); 209 } 210 211 return (0); 212 } 213 214 static moduledata_t epoch_test_moduledata = { 215 "epoch_test", 216 epoch_test_module_event_handler, 217 NULL 218 }; 219 220 MODULE_VERSION(epoch_test, 1); 221 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 222