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/proc.h> 33 #include <sys/counter.h> 34 #include <sys/epoch.h> 35 #include <sys/gtaskqueue.h> 36 #include <sys/kernel.h> 37 #include <sys/kthread.h> 38 #include <sys/lock.h> 39 #include <sys/malloc.h> 40 #include <sys/module.h> 41 #include <sys/mutex.h> 42 #include <sys/proc.h> 43 #include <sys/sched.h> 44 #include <sys/smp.h> 45 #include <sys/sysctl.h> 46 #include <sys/systm.h> 47 48 49 struct epoch_test_instance { 50 int threadid; 51 }; 52 53 static int inited; 54 static int iterations; 55 #define ET_EXITING 0x1 56 static volatile int state_flags; 57 static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2); 58 MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF); 59 static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2); 60 MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF); 61 static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2); 62 MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF); 63 epoch_t test_epoch; 64 65 static void 66 epoch_testcase1(struct epoch_test_instance *eti) 67 { 68 int i, startticks; 69 struct mtx *mtxp; 70 struct epoch_tracker et; 71 72 startticks = ticks; 73 i = 0; 74 if (eti->threadid & 0x1) 75 mtxp = &mutexA; 76 else 77 mtxp = &mutexB; 78 79 while (i < iterations) { 80 epoch_enter_preempt(test_epoch, &et); 81 mtx_lock(mtxp); 82 i++; 83 mtx_unlock(mtxp); 84 epoch_exit_preempt(test_epoch, &et); 85 epoch_wait_preempt(test_epoch); 86 } 87 printf("test1: thread: %d took %d ticks to complete %d iterations\n", 88 eti->threadid, ticks - startticks, iterations); 89 } 90 91 static void 92 epoch_testcase2(struct epoch_test_instance *eti) 93 { 94 int i, startticks; 95 struct mtx *mtxp; 96 struct epoch_tracker et; 97 98 startticks = ticks; 99 i = 0; 100 mtxp = &mutexA; 101 102 while (i < iterations) { 103 epoch_enter_preempt(test_epoch, &et); 104 mtx_lock(mtxp); 105 DELAY(1); 106 i++; 107 mtx_unlock(mtxp); 108 epoch_exit_preempt(test_epoch, &et); 109 epoch_wait_preempt(test_epoch); 110 } 111 printf("test2: thread: %d took %d ticks to complete %d iterations\n", 112 eti->threadid, ticks - startticks, iterations); 113 } 114 115 static void 116 testloop(void *arg) { 117 118 mtx_lock(&state_mtx); 119 while ((state_flags & ET_EXITING) == 0) { 120 msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0); 121 if (state_flags & ET_EXITING) 122 goto out; 123 mtx_unlock(&state_mtx); 124 epoch_testcase2(arg); 125 pause("W", 500); 126 epoch_testcase1(arg); 127 mtx_lock(&state_mtx); 128 } 129 out: 130 mtx_unlock(&state_mtx); 131 kthread_exit(); 132 } 133 134 static struct thread *testthreads[MAXCPU]; 135 static struct epoch_test_instance etilist[MAXCPU]; 136 137 static int 138 test_modinit(void) 139 { 140 struct thread *td; 141 int i, error, pri_range, pri_off; 142 143 pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME; 144 test_epoch = epoch_alloc(EPOCH_PREEMPT); 145 for (i = 0; i < mp_ncpus*2; i++) { 146 etilist[i].threadid = i; 147 error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i], 148 0, 0, "epoch_test_%d", i); 149 if (error) { 150 printf("%s: kthread_add(epoch_test): error %d", __func__, 151 error); 152 } else { 153 pri_off = (i*4)%pri_range; 154 td = testthreads[i]; 155 thread_lock(td); 156 sched_prio(td, PRI_MIN_REALTIME + pri_off); 157 thread_unlock(td); 158 } 159 } 160 inited = 1; 161 return (0); 162 } 163 164 static int 165 epochtest_execute(SYSCTL_HANDLER_ARGS) 166 { 167 int error, v; 168 169 if (inited == 0) 170 return (ENOENT); 171 172 v = 0; 173 error = sysctl_handle_int(oidp, &v, 0, req); 174 if (error) 175 return (error); 176 if (req->newptr == NULL) 177 return (error); 178 if (v == 0) 179 return (0); 180 mtx_lock(&state_mtx); 181 iterations = v; 182 wakeup(&state_mtx); 183 mtx_unlock(&state_mtx); 184 185 return (0); 186 } 187 188 SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 189 "Epoch Test Framework"); 190 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, 191 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 192 0, 0, epochtest_execute, "I", 193 "Execute an epoch test"); 194 195 static int 196 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 197 { 198 int err; 199 200 switch (what) { 201 case MOD_LOAD: 202 if ((err = test_modinit()) != 0) 203 return (err); 204 break; 205 case MOD_UNLOAD: 206 mtx_lock(&state_mtx); 207 state_flags = ET_EXITING; 208 wakeup(&state_mtx); 209 mtx_unlock(&state_mtx); 210 /* yes --- gross */ 211 pause("epoch unload", 3*hz); 212 break; 213 default: 214 return (EOPNOTSUPP); 215 } 216 217 return (0); 218 } 219 220 static moduledata_t epoch_test_moduledata = { 221 "epoch_test", 222 epoch_test_module_event_handler, 223 NULL 224 }; 225 226 MODULE_VERSION(epoch_test, 1); 227 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 228