106bf2a6aSMatt Macy /*- 206bf2a6aSMatt Macy * Copyright (c) 2018, Matthew Macy <mmacy@freebsd.org> 306bf2a6aSMatt Macy * 406bf2a6aSMatt Macy * Redistribution and use in source and binary forms, with or without 506bf2a6aSMatt Macy * modification, are permitted provided that the following conditions are met: 606bf2a6aSMatt Macy * 706bf2a6aSMatt Macy * 1. Redistributions of source code must retain the above copyright notice, 806bf2a6aSMatt Macy * this list of conditions and the following disclaimer. 906bf2a6aSMatt Macy * 1006bf2a6aSMatt Macy * 2. Neither the name of Matthew Macy nor the names of its 1106bf2a6aSMatt Macy * contributors may be used to endorse or promote products derived from 1206bf2a6aSMatt Macy * this software without specific prior written permission. 1306bf2a6aSMatt Macy * 1406bf2a6aSMatt Macy * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1506bf2a6aSMatt Macy * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1606bf2a6aSMatt Macy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1706bf2a6aSMatt Macy * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 1806bf2a6aSMatt Macy * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1906bf2a6aSMatt Macy * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2006bf2a6aSMatt Macy * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2106bf2a6aSMatt Macy * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2206bf2a6aSMatt Macy * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2306bf2a6aSMatt Macy * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2406bf2a6aSMatt Macy * POSSIBILITY OF SUCH DAMAGE. 2506bf2a6aSMatt Macy */ 2606bf2a6aSMatt Macy 2706bf2a6aSMatt Macy #include <sys/param.h> 2806bf2a6aSMatt Macy #include <sys/types.h> 295e68a3dfSMatt Macy #include <sys/proc.h> 3006bf2a6aSMatt Macy #include <sys/counter.h> 3106bf2a6aSMatt Macy #include <sys/epoch.h> 3206bf2a6aSMatt Macy #include <sys/gtaskqueue.h> 3306bf2a6aSMatt Macy #include <sys/kernel.h> 3406bf2a6aSMatt Macy #include <sys/kthread.h> 3506bf2a6aSMatt Macy #include <sys/lock.h> 3606bf2a6aSMatt Macy #include <sys/malloc.h> 3706bf2a6aSMatt Macy #include <sys/module.h> 3806bf2a6aSMatt Macy #include <sys/mutex.h> 39ef7f29d8SMatt Macy #include <sys/proc.h> 40ef7f29d8SMatt Macy #include <sys/sched.h> 4106bf2a6aSMatt Macy #include <sys/smp.h> 4206bf2a6aSMatt Macy #include <sys/sysctl.h> 4306bf2a6aSMatt Macy #include <sys/systm.h> 4406bf2a6aSMatt Macy 4506bf2a6aSMatt Macy 4606bf2a6aSMatt Macy struct epoch_test_instance { 4706bf2a6aSMatt Macy int threadid; 4806bf2a6aSMatt Macy }; 4906bf2a6aSMatt Macy 5006bf2a6aSMatt Macy static int inited; 5106bf2a6aSMatt Macy static int iterations; 5206bf2a6aSMatt Macy #define ET_EXITING 0x1 5306bf2a6aSMatt Macy static volatile int state_flags; 5406bf2a6aSMatt Macy static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2); 5506bf2a6aSMatt Macy MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF); 5606bf2a6aSMatt Macy static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2); 5706bf2a6aSMatt Macy MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF); 5806bf2a6aSMatt Macy static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2); 5906bf2a6aSMatt Macy MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF); 6006bf2a6aSMatt Macy epoch_t test_epoch; 6106bf2a6aSMatt Macy 6206bf2a6aSMatt Macy static void 6306bf2a6aSMatt Macy epoch_testcase1(struct epoch_test_instance *eti) 6406bf2a6aSMatt Macy { 6506bf2a6aSMatt Macy int i, startticks; 6606bf2a6aSMatt Macy struct mtx *mtxp; 67471826c2SMatt Macy struct epoch_tracker et; 6806bf2a6aSMatt Macy 6906bf2a6aSMatt Macy startticks = ticks; 7006bf2a6aSMatt Macy i = 0; 7106bf2a6aSMatt Macy if (eti->threadid & 0x1) 7206bf2a6aSMatt Macy mtxp = &mutexA; 7306bf2a6aSMatt Macy else 7406bf2a6aSMatt Macy mtxp = &mutexB; 7506bf2a6aSMatt Macy 7606bf2a6aSMatt Macy while (i < iterations) { 77471826c2SMatt Macy epoch_enter_preempt(test_epoch, &et); 7806bf2a6aSMatt Macy mtx_lock(mtxp); 7906bf2a6aSMatt Macy i++; 8006bf2a6aSMatt Macy mtx_unlock(mtxp); 81471826c2SMatt Macy epoch_exit_preempt(test_epoch, &et); 8270398c2fSMatt Macy epoch_wait_preempt(test_epoch); 8306bf2a6aSMatt Macy } 8406bf2a6aSMatt Macy printf("test1: thread: %d took %d ticks to complete %d iterations\n", 8506bf2a6aSMatt Macy eti->threadid, ticks - startticks, iterations); 8606bf2a6aSMatt Macy } 8706bf2a6aSMatt Macy 8806bf2a6aSMatt Macy static void 8906bf2a6aSMatt Macy epoch_testcase2(struct epoch_test_instance *eti) 9006bf2a6aSMatt Macy { 9106bf2a6aSMatt Macy int i, startticks; 9206bf2a6aSMatt Macy struct mtx *mtxp; 93471826c2SMatt Macy struct epoch_tracker et; 9406bf2a6aSMatt Macy 9506bf2a6aSMatt Macy startticks = ticks; 9606bf2a6aSMatt Macy i = 0; 9706bf2a6aSMatt Macy mtxp = &mutexA; 9806bf2a6aSMatt Macy 9906bf2a6aSMatt Macy while (i < iterations) { 100471826c2SMatt Macy epoch_enter_preempt(test_epoch, &et); 10106bf2a6aSMatt Macy mtx_lock(mtxp); 10206bf2a6aSMatt Macy DELAY(1); 10306bf2a6aSMatt Macy i++; 10406bf2a6aSMatt Macy mtx_unlock(mtxp); 105471826c2SMatt Macy epoch_exit_preempt(test_epoch, &et); 10670398c2fSMatt Macy epoch_wait_preempt(test_epoch); 10706bf2a6aSMatt Macy } 10806bf2a6aSMatt Macy printf("test2: thread: %d took %d ticks to complete %d iterations\n", 10906bf2a6aSMatt Macy eti->threadid, ticks - startticks, iterations); 11006bf2a6aSMatt Macy } 11106bf2a6aSMatt Macy 11206bf2a6aSMatt Macy static void 11306bf2a6aSMatt Macy testloop(void *arg) { 11406bf2a6aSMatt Macy 11506bf2a6aSMatt Macy mtx_lock(&state_mtx); 11606bf2a6aSMatt Macy while ((state_flags & ET_EXITING) == 0) { 11706bf2a6aSMatt Macy msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0); 11806bf2a6aSMatt Macy if (state_flags & ET_EXITING) 11906bf2a6aSMatt Macy goto out; 12006bf2a6aSMatt Macy mtx_unlock(&state_mtx); 12106bf2a6aSMatt Macy epoch_testcase2(arg); 12206bf2a6aSMatt Macy pause("W", 500); 12306bf2a6aSMatt Macy epoch_testcase1(arg); 12406bf2a6aSMatt Macy mtx_lock(&state_mtx); 12506bf2a6aSMatt Macy } 12606bf2a6aSMatt Macy out: 12706bf2a6aSMatt Macy mtx_unlock(&state_mtx); 12806bf2a6aSMatt Macy kthread_exit(); 12906bf2a6aSMatt Macy } 13006bf2a6aSMatt Macy 13106bf2a6aSMatt Macy static struct thread *testthreads[MAXCPU]; 13206bf2a6aSMatt Macy static struct epoch_test_instance etilist[MAXCPU]; 13306bf2a6aSMatt Macy 13406bf2a6aSMatt Macy static int 13506bf2a6aSMatt Macy test_modinit(void) 13606bf2a6aSMatt Macy { 137ef7f29d8SMatt Macy struct thread *td; 138ef7f29d8SMatt Macy int i, error, pri_range, pri_off; 13906bf2a6aSMatt Macy 140ef7f29d8SMatt Macy pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME; 141*b1acfb24SHans Petter Selasky test_epoch = epoch_alloc("test_epoch", EPOCH_PREEMPT); 142ef7f29d8SMatt Macy for (i = 0; i < mp_ncpus*2; i++) { 14306bf2a6aSMatt Macy etilist[i].threadid = i; 14406bf2a6aSMatt Macy error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i], 14506bf2a6aSMatt Macy 0, 0, "epoch_test_%d", i); 14606bf2a6aSMatt Macy if (error) { 14706bf2a6aSMatt Macy printf("%s: kthread_add(epoch_test): error %d", __func__, 14806bf2a6aSMatt Macy error); 149ef7f29d8SMatt Macy } else { 150ef7f29d8SMatt Macy pri_off = (i*4)%pri_range; 151ef7f29d8SMatt Macy td = testthreads[i]; 152ef7f29d8SMatt Macy thread_lock(td); 153ef7f29d8SMatt Macy sched_prio(td, PRI_MIN_REALTIME + pri_off); 154ef7f29d8SMatt Macy thread_unlock(td); 15506bf2a6aSMatt Macy } 15606bf2a6aSMatt Macy } 15706bf2a6aSMatt Macy inited = 1; 15806bf2a6aSMatt Macy return (0); 15906bf2a6aSMatt Macy } 16006bf2a6aSMatt Macy 16106bf2a6aSMatt Macy static int 16206bf2a6aSMatt Macy epochtest_execute(SYSCTL_HANDLER_ARGS) 16306bf2a6aSMatt Macy { 16406bf2a6aSMatt Macy int error, v; 16506bf2a6aSMatt Macy 16606bf2a6aSMatt Macy if (inited == 0) 16706bf2a6aSMatt Macy return (ENOENT); 16806bf2a6aSMatt Macy 16906bf2a6aSMatt Macy v = 0; 17006bf2a6aSMatt Macy error = sysctl_handle_int(oidp, &v, 0, req); 17106bf2a6aSMatt Macy if (error) 17206bf2a6aSMatt Macy return (error); 17306bf2a6aSMatt Macy if (req->newptr == NULL) 17406bf2a6aSMatt Macy return (error); 17506bf2a6aSMatt Macy if (v == 0) 17606bf2a6aSMatt Macy return (0); 17706bf2a6aSMatt Macy mtx_lock(&state_mtx); 17806bf2a6aSMatt Macy iterations = v; 17906bf2a6aSMatt Macy wakeup(&state_mtx); 18006bf2a6aSMatt Macy mtx_unlock(&state_mtx); 18106bf2a6aSMatt Macy 18206bf2a6aSMatt Macy return (0); 18306bf2a6aSMatt Macy } 18406bf2a6aSMatt Macy 1857029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1867029da5cSPawel Biernacki "Epoch Test Framework"); 1877029da5cSPawel Biernacki SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, 1887029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 1897029da5cSPawel Biernacki 0, 0, epochtest_execute, "I", 1907029da5cSPawel Biernacki "Execute an epoch test"); 19106bf2a6aSMatt Macy 19206bf2a6aSMatt Macy static int 19306bf2a6aSMatt Macy epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 19406bf2a6aSMatt Macy { 19506bf2a6aSMatt Macy int err; 19606bf2a6aSMatt Macy 19706bf2a6aSMatt Macy switch (what) { 19806bf2a6aSMatt Macy case MOD_LOAD: 19906bf2a6aSMatt Macy if ((err = test_modinit()) != 0) 20006bf2a6aSMatt Macy return (err); 20106bf2a6aSMatt Macy break; 20206bf2a6aSMatt Macy case MOD_UNLOAD: 20306bf2a6aSMatt Macy mtx_lock(&state_mtx); 20406bf2a6aSMatt Macy state_flags = ET_EXITING; 20506bf2a6aSMatt Macy wakeup(&state_mtx); 20606bf2a6aSMatt Macy mtx_unlock(&state_mtx); 20706bf2a6aSMatt Macy /* yes --- gross */ 20806bf2a6aSMatt Macy pause("epoch unload", 3*hz); 209*b1acfb24SHans Petter Selasky epoch_free(test_epoch); 21006bf2a6aSMatt Macy break; 21106bf2a6aSMatt Macy default: 21206bf2a6aSMatt Macy return (EOPNOTSUPP); 21306bf2a6aSMatt Macy } 21406bf2a6aSMatt Macy 21506bf2a6aSMatt Macy return (0); 21606bf2a6aSMatt Macy } 21706bf2a6aSMatt Macy 21806bf2a6aSMatt Macy static moduledata_t epoch_test_moduledata = { 21906bf2a6aSMatt Macy "epoch_test", 22006bf2a6aSMatt Macy epoch_test_module_event_handler, 22106bf2a6aSMatt Macy NULL 22206bf2a6aSMatt Macy }; 22306bf2a6aSMatt Macy 22406bf2a6aSMatt Macy MODULE_VERSION(epoch_test, 1); 22506bf2a6aSMatt Macy DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 226