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/param.h> 28 #include <sys/types.h> 29 #include <sys/proc.h> 30 #include <sys/counter.h> 31 #include <sys/epoch.h> 32 #include <sys/gtaskqueue.h> 33 #include <sys/kernel.h> 34 #include <sys/kthread.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/module.h> 38 #include <sys/mutex.h> 39 #include <sys/proc.h> 40 #include <sys/sched.h> 41 #include <sys/smp.h> 42 #include <sys/sysctl.h> 43 #include <sys/systm.h> 44 45 46 struct epoch_test_instance { 47 int threadid; 48 }; 49 50 static int inited; 51 static int iterations; 52 #define ET_EXITING 0x1 53 static volatile int state_flags; 54 static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2); 55 MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF); 56 static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2); 57 MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF); 58 static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2); 59 MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF); 60 epoch_t test_epoch; 61 62 static void 63 epoch_testcase1(struct epoch_test_instance *eti) 64 { 65 int i, startticks; 66 struct mtx *mtxp; 67 struct epoch_tracker et; 68 69 startticks = ticks; 70 i = 0; 71 if (eti->threadid & 0x1) 72 mtxp = &mutexA; 73 else 74 mtxp = &mutexB; 75 76 while (i < iterations) { 77 epoch_enter_preempt(test_epoch, &et); 78 mtx_lock(mtxp); 79 i++; 80 mtx_unlock(mtxp); 81 epoch_exit_preempt(test_epoch, &et); 82 epoch_wait_preempt(test_epoch); 83 } 84 printf("test1: thread: %d took %d ticks to complete %d iterations\n", 85 eti->threadid, ticks - startticks, iterations); 86 } 87 88 static void 89 epoch_testcase2(struct epoch_test_instance *eti) 90 { 91 int i, startticks; 92 struct mtx *mtxp; 93 struct epoch_tracker et; 94 95 startticks = ticks; 96 i = 0; 97 mtxp = &mutexA; 98 99 while (i < iterations) { 100 epoch_enter_preempt(test_epoch, &et); 101 mtx_lock(mtxp); 102 DELAY(1); 103 i++; 104 mtx_unlock(mtxp); 105 epoch_exit_preempt(test_epoch, &et); 106 epoch_wait_preempt(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("test_epoch", EPOCH_PREEMPT); 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 | CTLFLAG_MPSAFE, 0, 186 "Epoch Test Framework"); 187 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, 188 CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 189 0, 0, epochtest_execute, "I", 190 "Execute an epoch test"); 191 192 static int 193 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 194 { 195 int err; 196 197 switch (what) { 198 case MOD_LOAD: 199 if ((err = test_modinit()) != 0) 200 return (err); 201 break; 202 case MOD_UNLOAD: 203 mtx_lock(&state_mtx); 204 state_flags = ET_EXITING; 205 wakeup(&state_mtx); 206 mtx_unlock(&state_mtx); 207 /* yes --- gross */ 208 pause("epoch unload", 3*hz); 209 epoch_free(test_epoch); 210 break; 211 default: 212 return (EOPNOTSUPP); 213 } 214 215 return (0); 216 } 217 218 static moduledata_t epoch_test_moduledata = { 219 "epoch_test", 220 epoch_test_module_event_handler, 221 NULL 222 }; 223 224 MODULE_VERSION(epoch_test, 1); 225 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 226