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> 32*5e68a3dfSMatt 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; 7006bf2a6aSMatt Macy 7106bf2a6aSMatt Macy startticks = ticks; 7206bf2a6aSMatt Macy i = 0; 7306bf2a6aSMatt Macy if (eti->threadid & 0x1) 7406bf2a6aSMatt Macy mtxp = &mutexA; 7506bf2a6aSMatt Macy else 7606bf2a6aSMatt Macy mtxp = &mutexB; 7706bf2a6aSMatt Macy 7806bf2a6aSMatt Macy while (i < iterations) { 7906bf2a6aSMatt Macy epoch_enter(test_epoch); 8006bf2a6aSMatt Macy mtx_lock(mtxp); 8106bf2a6aSMatt Macy i++; 8206bf2a6aSMatt Macy mtx_unlock(mtxp); 8306bf2a6aSMatt Macy epoch_exit(test_epoch); 8406bf2a6aSMatt Macy epoch_wait(test_epoch); 8506bf2a6aSMatt Macy } 8606bf2a6aSMatt Macy printf("test1: thread: %d took %d ticks to complete %d iterations\n", 8706bf2a6aSMatt Macy eti->threadid, ticks - startticks, iterations); 8806bf2a6aSMatt Macy } 8906bf2a6aSMatt Macy 9006bf2a6aSMatt Macy static void 9106bf2a6aSMatt Macy epoch_testcase2(struct epoch_test_instance *eti) 9206bf2a6aSMatt Macy { 9306bf2a6aSMatt Macy int i, startticks; 9406bf2a6aSMatt Macy struct mtx *mtxp; 9506bf2a6aSMatt Macy 9606bf2a6aSMatt Macy startticks = ticks; 9706bf2a6aSMatt Macy i = 0; 9806bf2a6aSMatt Macy mtxp = &mutexA; 9906bf2a6aSMatt Macy 10006bf2a6aSMatt Macy while (i < iterations) { 10106bf2a6aSMatt Macy epoch_enter(test_epoch); 10206bf2a6aSMatt Macy mtx_lock(mtxp); 10306bf2a6aSMatt Macy DELAY(1); 10406bf2a6aSMatt Macy i++; 10506bf2a6aSMatt Macy mtx_unlock(mtxp); 10606bf2a6aSMatt Macy epoch_exit(test_epoch); 10706bf2a6aSMatt Macy epoch_wait(test_epoch); 10806bf2a6aSMatt Macy } 10906bf2a6aSMatt Macy printf("test2: thread: %d took %d ticks to complete %d iterations\n", 11006bf2a6aSMatt Macy eti->threadid, ticks - startticks, iterations); 11106bf2a6aSMatt Macy } 11206bf2a6aSMatt Macy 11306bf2a6aSMatt Macy static void 11406bf2a6aSMatt Macy testloop(void *arg) { 11506bf2a6aSMatt Macy 11606bf2a6aSMatt Macy mtx_lock(&state_mtx); 11706bf2a6aSMatt Macy while ((state_flags & ET_EXITING) == 0) { 11806bf2a6aSMatt Macy msleep(&state_mtx, &state_mtx, 0, "epoch start wait", 0); 11906bf2a6aSMatt Macy if (state_flags & ET_EXITING) 12006bf2a6aSMatt Macy goto out; 12106bf2a6aSMatt Macy mtx_unlock(&state_mtx); 12206bf2a6aSMatt Macy epoch_testcase2(arg); 12306bf2a6aSMatt Macy pause("W", 500); 12406bf2a6aSMatt Macy epoch_testcase1(arg); 12506bf2a6aSMatt Macy mtx_lock(&state_mtx); 12606bf2a6aSMatt Macy } 12706bf2a6aSMatt Macy out: 12806bf2a6aSMatt Macy mtx_unlock(&state_mtx); 12906bf2a6aSMatt Macy kthread_exit(); 13006bf2a6aSMatt Macy } 13106bf2a6aSMatt Macy 13206bf2a6aSMatt Macy static struct thread *testthreads[MAXCPU]; 13306bf2a6aSMatt Macy static struct epoch_test_instance etilist[MAXCPU]; 13406bf2a6aSMatt Macy 13506bf2a6aSMatt Macy static int 13606bf2a6aSMatt Macy test_modinit(void) 13706bf2a6aSMatt Macy { 138ef7f29d8SMatt Macy struct thread *td; 139ef7f29d8SMatt Macy int i, error, pri_range, pri_off; 14006bf2a6aSMatt Macy 141ef7f29d8SMatt Macy pri_range = PRI_MIN_TIMESHARE - PRI_MIN_REALTIME; 142*5e68a3dfSMatt Macy test_epoch = epoch_alloc(0); 143ef7f29d8SMatt Macy for (i = 0; i < mp_ncpus*2; i++) { 14406bf2a6aSMatt Macy etilist[i].threadid = i; 14506bf2a6aSMatt Macy error = kthread_add(testloop, &etilist[i], NULL, &testthreads[i], 14606bf2a6aSMatt Macy 0, 0, "epoch_test_%d", i); 14706bf2a6aSMatt Macy if (error) { 14806bf2a6aSMatt Macy printf("%s: kthread_add(epoch_test): error %d", __func__, 14906bf2a6aSMatt Macy error); 150ef7f29d8SMatt Macy } else { 151ef7f29d8SMatt Macy pri_off = (i*4)%pri_range; 152ef7f29d8SMatt Macy td = testthreads[i]; 153ef7f29d8SMatt Macy thread_lock(td); 154ef7f29d8SMatt Macy sched_prio(td, PRI_MIN_REALTIME + pri_off); 155ef7f29d8SMatt Macy thread_unlock(td); 15606bf2a6aSMatt Macy } 15706bf2a6aSMatt Macy } 15806bf2a6aSMatt Macy inited = 1; 15906bf2a6aSMatt Macy return (0); 16006bf2a6aSMatt Macy } 16106bf2a6aSMatt Macy 16206bf2a6aSMatt Macy static int 16306bf2a6aSMatt Macy epochtest_execute(SYSCTL_HANDLER_ARGS) 16406bf2a6aSMatt Macy { 16506bf2a6aSMatt Macy int error, v; 16606bf2a6aSMatt Macy 16706bf2a6aSMatt Macy if (inited == 0) 16806bf2a6aSMatt Macy return (ENOENT); 16906bf2a6aSMatt Macy 17006bf2a6aSMatt Macy v = 0; 17106bf2a6aSMatt Macy error = sysctl_handle_int(oidp, &v, 0, req); 17206bf2a6aSMatt Macy if (error) 17306bf2a6aSMatt Macy return (error); 17406bf2a6aSMatt Macy if (req->newptr == NULL) 17506bf2a6aSMatt Macy return (error); 17606bf2a6aSMatt Macy if (v == 0) 17706bf2a6aSMatt Macy return (0); 17806bf2a6aSMatt Macy mtx_lock(&state_mtx); 17906bf2a6aSMatt Macy iterations = v; 18006bf2a6aSMatt Macy wakeup(&state_mtx); 18106bf2a6aSMatt Macy mtx_unlock(&state_mtx); 18206bf2a6aSMatt Macy 18306bf2a6aSMatt Macy return (0); 18406bf2a6aSMatt Macy } 18506bf2a6aSMatt Macy 18606bf2a6aSMatt Macy SYSCTL_NODE(_kern, OID_AUTO, epochtest, CTLFLAG_RW, 0, "Epoch Test Framework"); 18706bf2a6aSMatt Macy SYSCTL_PROC(_kern_epochtest, OID_AUTO, runtest, (CTLTYPE_INT | CTLFLAG_RW), 18806bf2a6aSMatt Macy 0, 0, epochtest_execute, "I", "Execute an epoch test"); 18906bf2a6aSMatt Macy 19006bf2a6aSMatt Macy static int 19106bf2a6aSMatt Macy epoch_test_module_event_handler(module_t mod, int what, void *arg __unused) 19206bf2a6aSMatt Macy { 19306bf2a6aSMatt Macy int err; 19406bf2a6aSMatt Macy 19506bf2a6aSMatt Macy switch (what) { 19606bf2a6aSMatt Macy case MOD_LOAD: 19706bf2a6aSMatt Macy if ((err = test_modinit()) != 0) 19806bf2a6aSMatt Macy return (err); 19906bf2a6aSMatt Macy break; 20006bf2a6aSMatt Macy case MOD_UNLOAD: 20106bf2a6aSMatt Macy mtx_lock(&state_mtx); 20206bf2a6aSMatt Macy state_flags = ET_EXITING; 20306bf2a6aSMatt Macy wakeup(&state_mtx); 20406bf2a6aSMatt Macy mtx_unlock(&state_mtx); 20506bf2a6aSMatt Macy /* yes --- gross */ 20606bf2a6aSMatt Macy pause("epoch unload", 3*hz); 20706bf2a6aSMatt Macy break; 20806bf2a6aSMatt Macy default: 20906bf2a6aSMatt Macy return (EOPNOTSUPP); 21006bf2a6aSMatt Macy } 21106bf2a6aSMatt Macy 21206bf2a6aSMatt Macy return (0); 21306bf2a6aSMatt Macy } 21406bf2a6aSMatt Macy 21506bf2a6aSMatt Macy static moduledata_t epoch_test_moduledata = { 21606bf2a6aSMatt Macy "epoch_test", 21706bf2a6aSMatt Macy epoch_test_module_event_handler, 21806bf2a6aSMatt Macy NULL 21906bf2a6aSMatt Macy }; 22006bf2a6aSMatt Macy 22106bf2a6aSMatt Macy MODULE_VERSION(epoch_test, 1); 22206bf2a6aSMatt Macy DECLARE_MODULE(epoch_test, epoch_test_moduledata, SI_SUB_PSEUDO, SI_ORDER_ANY); 223