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