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/cdefs.h> 2806bf2a6aSMatt Macy __FBSDID("$FreeBSD$"); 2906bf2a6aSMatt Macy 3006bf2a6aSMatt Macy #include <sys/param.h> 3106bf2a6aSMatt Macy #include <sys/types.h> 325e68a3dfSMatt Macy #include <sys/proc.h> 3306bf2a6aSMatt Macy #include <sys/counter.h> 3406bf2a6aSMatt Macy #include <sys/epoch.h> 3506bf2a6aSMatt Macy #include <sys/gtaskqueue.h> 3606bf2a6aSMatt Macy #include <sys/kernel.h> 3706bf2a6aSMatt Macy #include <sys/kthread.h> 3806bf2a6aSMatt Macy #include <sys/lock.h> 3906bf2a6aSMatt Macy #include <sys/malloc.h> 4006bf2a6aSMatt Macy #include <sys/module.h> 4106bf2a6aSMatt Macy #include <sys/mutex.h> 42ef7f29d8SMatt Macy #include <sys/proc.h> 43ef7f29d8SMatt Macy #include <sys/sched.h> 4406bf2a6aSMatt Macy #include <sys/smp.h> 4506bf2a6aSMatt Macy #include <sys/sysctl.h> 4606bf2a6aSMatt Macy #include <sys/systm.h> 4706bf2a6aSMatt Macy 4806bf2a6aSMatt Macy 4906bf2a6aSMatt Macy struct epoch_test_instance { 5006bf2a6aSMatt Macy int threadid; 5106bf2a6aSMatt Macy }; 5206bf2a6aSMatt Macy 5306bf2a6aSMatt Macy static int inited; 5406bf2a6aSMatt Macy static int iterations; 5506bf2a6aSMatt Macy #define ET_EXITING 0x1 5606bf2a6aSMatt Macy static volatile int state_flags; 5706bf2a6aSMatt Macy static struct mtx state_mtx __aligned(CACHE_LINE_SIZE*2); 5806bf2a6aSMatt Macy MTX_SYSINIT(state_mtx, &state_mtx, "epoch state mutex", MTX_DEF); 5906bf2a6aSMatt Macy static struct mtx mutexA __aligned(CACHE_LINE_SIZE*2); 6006bf2a6aSMatt Macy MTX_SYSINIT(mutexA, &mutexA, "epoch mutexA", MTX_DEF); 6106bf2a6aSMatt Macy static struct mtx mutexB __aligned(CACHE_LINE_SIZE*2); 6206bf2a6aSMatt Macy MTX_SYSINIT(mutexB, &mutexB, "epoch mutexB", MTX_DEF); 6306bf2a6aSMatt Macy epoch_t test_epoch; 6406bf2a6aSMatt Macy 6506bf2a6aSMatt Macy static void 6606bf2a6aSMatt Macy epoch_testcase1(struct epoch_test_instance *eti) 6706bf2a6aSMatt Macy { 6806bf2a6aSMatt Macy int i, startticks; 6906bf2a6aSMatt Macy struct mtx *mtxp; 70471826c2SMatt Macy struct epoch_tracker et; 7106bf2a6aSMatt Macy 7206bf2a6aSMatt Macy startticks = ticks; 7306bf2a6aSMatt Macy i = 0; 7406bf2a6aSMatt Macy if (eti->threadid & 0x1) 7506bf2a6aSMatt Macy mtxp = &mutexA; 7606bf2a6aSMatt Macy else 7706bf2a6aSMatt Macy mtxp = &mutexB; 7806bf2a6aSMatt Macy 7906bf2a6aSMatt Macy while (i < iterations) { 80471826c2SMatt Macy epoch_enter_preempt(test_epoch, &et); 8106bf2a6aSMatt Macy mtx_lock(mtxp); 8206bf2a6aSMatt Macy i++; 8306bf2a6aSMatt Macy mtx_unlock(mtxp); 84471826c2SMatt Macy epoch_exit_preempt(test_epoch, &et); 8570398c2fSMatt Macy epoch_wait_preempt(test_epoch); 8606bf2a6aSMatt Macy } 8706bf2a6aSMatt Macy printf("test1: thread: %d took %d ticks to complete %d iterations\n", 8806bf2a6aSMatt Macy eti->threadid, ticks - startticks, iterations); 8906bf2a6aSMatt Macy } 9006bf2a6aSMatt Macy 9106bf2a6aSMatt Macy static void 9206bf2a6aSMatt Macy epoch_testcase2(struct epoch_test_instance *eti) 9306bf2a6aSMatt Macy { 9406bf2a6aSMatt Macy int i, startticks; 9506bf2a6aSMatt Macy struct mtx *mtxp; 96471826c2SMatt Macy struct epoch_tracker et; 9706bf2a6aSMatt Macy 9806bf2a6aSMatt Macy startticks = ticks; 9906bf2a6aSMatt Macy i = 0; 10006bf2a6aSMatt Macy mtxp = &mutexA; 10106bf2a6aSMatt Macy 10206bf2a6aSMatt Macy while (i < iterations) { 103471826c2SMatt Macy epoch_enter_preempt(test_epoch, &et); 10406bf2a6aSMatt Macy mtx_lock(mtxp); 10506bf2a6aSMatt Macy DELAY(1); 10606bf2a6aSMatt Macy i++; 10706bf2a6aSMatt Macy mtx_unlock(mtxp); 108471826c2SMatt Macy epoch_exit_preempt(test_epoch, &et); 10970398c2fSMatt Macy epoch_wait_preempt(test_epoch); 11006bf2a6aSMatt Macy } 11106bf2a6aSMatt Macy printf("test2: thread: %d took %d ticks to complete %d iterations\n", 11206bf2a6aSMatt Macy eti->threadid, ticks - startticks, iterations); 11306bf2a6aSMatt Macy } 11406bf2a6aSMatt Macy 11506bf2a6aSMatt Macy static void 11606bf2a6aSMatt Macy testloop(void *arg) { 11706bf2a6aSMatt Macy 11806bf2a6aSMatt Macy mtx_lock(&state_mtx); 11906bf2a6aSMatt Macy while ((state_flags & ET_EXITING) == 0) { 12006bf2a6aSMatt Macy msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0); 12106bf2a6aSMatt Macy if (state_flags & ET_EXITING) 12206bf2a6aSMatt Macy goto out; 12306bf2a6aSMatt Macy mtx_unlock(&state_mtx); 12406bf2a6aSMatt Macy epoch_testcase2(arg); 12506bf2a6aSMatt Macy pause("W", 500); 12606bf2a6aSMatt Macy epoch_testcase1(arg); 12706bf2a6aSMatt Macy mtx_lock(&state_mtx); 12806bf2a6aSMatt Macy } 12906bf2a6aSMatt Macy out: 13006bf2a6aSMatt Macy mtx_unlock(&state_mtx); 13106bf2a6aSMatt Macy kthread_exit(); 13206bf2a6aSMatt Macy } 13306bf2a6aSMatt Macy 13406bf2a6aSMatt Macy static struct thread *testthreads[MAXCPU]; 13506bf2a6aSMatt Macy static struct epoch_test_instance etilist[MAXCPU]; 13606bf2a6aSMatt Macy 13706bf2a6aSMatt Macy static int 13806bf2a6aSMatt Macy test_modinit(void) 13906bf2a6aSMatt Macy { 140ef7f29d8SMatt Macy struct thread *td; 141ef7f29d8SMatt Macy int i, error, pri_range, pri_off; 14206bf2a6aSMatt Macy 143ef7f29d8SMatt Macy pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME; 144*b1acfb24SHans Petter Selasky test_epoch = epoch_alloc("test_epoch", EPOCH_PREEMPT); 145ef7f29d8SMatt Macy for (i = 0; i < mp_ncpus*2; i++) { 14606bf2a6aSMatt Macy etilist[i].threadid = i; 14706bf2a6aSMatt Macy error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i], 14806bf2a6aSMatt Macy 0, 0, "epoch_test_%d", i); 14906bf2a6aSMatt Macy if (error) { 15006bf2a6aSMatt Macy printf("%s: kthread_add(epoch_test): error %d", __func__, 15106bf2a6aSMatt Macy error); 152ef7f29d8SMatt Macy } else { 153ef7f29d8SMatt Macy pri_off = (i*4)%pri_range; 154ef7f29d8SMatt Macy td = testthreads[i]; 155ef7f29d8SMatt Macy thread_lock(td); 156ef7f29d8SMatt Macy sched_prio(td, PRI_MIN_REALTIME + pri_off); 157ef7f29d8SMatt Macy thread_unlock(td); 15806bf2a6aSMatt Macy } 15906bf2a6aSMatt Macy } 16006bf2a6aSMatt Macy inited = 1; 16106bf2a6aSMatt Macy return (0); 16206bf2a6aSMatt Macy } 16306bf2a6aSMatt Macy 16406bf2a6aSMatt Macy static int 16506bf2a6aSMatt Macy epochtest_execute(SYSCTL_HANDLER_ARGS) 16606bf2a6aSMatt Macy { 16706bf2a6aSMatt Macy int error, v; 16806bf2a6aSMatt Macy 16906bf2a6aSMatt Macy if (inited == 0) 17006bf2a6aSMatt Macy return (ENOENT); 17106bf2a6aSMatt Macy 17206bf2a6aSMatt Macy v = 0; 17306bf2a6aSMatt Macy error = sysctl_handle_int(oidp, &v, 0, req); 17406bf2a6aSMatt Macy if (error) 17506bf2a6aSMatt Macy return (error); 17606bf2a6aSMatt Macy if (req->newptr == NULL) 17706bf2a6aSMatt Macy return (error); 17806bf2a6aSMatt Macy if (v == 0) 17906bf2a6aSMatt Macy return (0); 18006bf2a6aSMatt Macy mtx_lock(&state_mtx); 18106bf2a6aSMatt Macy iterations = v; 18206bf2a6aSMatt Macy wakeup(&state_mtx); 18306bf2a6aSMatt Macy mtx_unlock(&state_mtx); 18406bf2a6aSMatt Macy 18506bf2a6aSMatt Macy return (0); 18606bf2a6aSMatt Macy } 18706bf2a6aSMatt Macy 1887029da5cSPawel Biernacki SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW | CTLFLAG_MPSAFE, 0, 1897029da5cSPawel Biernacki "Epoch Test Framework"); 1907029da5cSPawel Biernacki SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, 1917029da5cSPawel Biernacki CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_MPSAFE, 1927029da5cSPawel Biernacki 0, 0, epochtest_execute, "I", 1937029da5cSPawel Biernacki "Execute an epoch test"); 19406bf2a6aSMatt Macy 19506bf2a6aSMatt Macy static int 19606bf2a6aSMatt Macy epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 19706bf2a6aSMatt Macy { 19806bf2a6aSMatt Macy int err; 19906bf2a6aSMatt Macy 20006bf2a6aSMatt Macy switch (what) { 20106bf2a6aSMatt Macy case MOD_LOAD: 20206bf2a6aSMatt Macy if ((err = test_modinit()) != 0) 20306bf2a6aSMatt Macy return (err); 20406bf2a6aSMatt Macy break; 20506bf2a6aSMatt Macy case MOD_UNLOAD: 20606bf2a6aSMatt Macy mtx_lock(&state_mtx); 20706bf2a6aSMatt Macy state_flags = ET_EXITING; 20806bf2a6aSMatt Macy wakeup(&state_mtx); 20906bf2a6aSMatt Macy mtx_unlock(&state_mtx); 21006bf2a6aSMatt Macy /* yes --- gross */ 21106bf2a6aSMatt Macy pause("epoch unload", 3*hz); 212*b1acfb24SHans Petter Selasky epoch_free(test_epoch); 21306bf2a6aSMatt Macy break; 21406bf2a6aSMatt Macy default: 21506bf2a6aSMatt Macy return (EOPNOTSUPP); 21606bf2a6aSMatt Macy } 21706bf2a6aSMatt Macy 21806bf2a6aSMatt Macy return (0); 21906bf2a6aSMatt Macy } 22006bf2a6aSMatt Macy 22106bf2a6aSMatt Macy static moduledata_t epoch_test_moduledata = { 22206bf2a6aSMatt Macy "epoch_test", 22306bf2a6aSMatt Macy epoch_test_module_event_handler, 22406bf2a6aSMatt Macy NULL 22506bf2a6aSMatt Macy }; 22606bf2a6aSMatt Macy 22706bf2a6aSMatt Macy MODULE_VERSION(epoch_test, 1); 22806bf2a6aSMatt Macy DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 229