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 71 startticks = ticks; 72 i = 0; 73 if (eti->threadid & 0x1) 74 mtxp = &mutexA; 75 else 76 mtxp = &mutexB; 77 78 while (i < iterations) { 79 epoch_enter_preempt(test_epoch); 80 mtx_lock(mtxp); 81 i++; 82 mtx_unlock(mtxp); 83 epoch_exit_preempt(test_epoch); 84 epoch_wait_preempt(test_epoch); 85 } 86 printf("test1: thread: %d took %d ticks to complete %d iterations\n", 87 eti->threadid, ticks - startticks, iterations); 88 } 89 90 static void 91 epoch_testcase2(struct epoch_test_instance *eti) 92 { 93 int i, startticks; 94 struct mtx *mtxp; 95 96 startticks = ticks; 97 i = 0; 98 mtxp = &mutexA; 99 100 while (i < iterations) { 101 epoch_enter_preempt(test_epoch); 102 mtx_lock(mtxp); 103 DELAY(1); 104 i++; 105 mtx_unlock(mtxp); 106 epoch_exit_preempt(test_epoch); 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(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, 0, "Epoch Test Framework"); 187 SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, (CTLTYPE_INT | CTLFLAG_RW), 188 0, 0, epochtest_execute, "I", "Execute an epoch test"); 189 190 static int 191 epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 192 { 193 int err; 194 195 switch (what) { 196 case MOD_LOAD: 197 if ((err = test_modinit()) != 0) 198 return (err); 199 break; 200 case MOD_UNLOAD: 201 mtx_lock(&state_mtx); 202 state_flags = ET_EXITING; 203 wakeup(&state_mtx); 204 mtx_unlock(&state_mtx); 205 /* yes --- gross */ 206 pause("epoch unload", 3*hz); 207 break; 208 default: 209 return (EOPNOTSUPP); 210 } 211 212 return (0); 213 } 214 215 static moduledata_t epoch_test_moduledata = { 216 "epoch_test", 217 epoch_test_module_event_handler, 218 NULL 219 }; 220 221 MODULE_VERSION(epoch_test, 1); 222 DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 223