1*1fbeb3a8SPaul E. McKenney // SPDX-License-Identifier: GPL-2.0+ 2*1fbeb3a8SPaul E. McKenney // 3*1fbeb3a8SPaul E. McKenney // Scalability test comparing RCU vs other mechanisms 4*1fbeb3a8SPaul E. McKenney // for acquiring references on objects. 5*1fbeb3a8SPaul E. McKenney // 6*1fbeb3a8SPaul E. McKenney // Copyright (C) Google, 2020. 7*1fbeb3a8SPaul E. McKenney // 8*1fbeb3a8SPaul E. McKenney // Author: Joel Fernandes <[email protected]> 9*1fbeb3a8SPaul E. McKenney 10*1fbeb3a8SPaul E. McKenney #define pr_fmt(fmt) fmt 11*1fbeb3a8SPaul E. McKenney 12*1fbeb3a8SPaul E. McKenney #include <linux/atomic.h> 13*1fbeb3a8SPaul E. McKenney #include <linux/bitops.h> 14*1fbeb3a8SPaul E. McKenney #include <linux/completion.h> 15*1fbeb3a8SPaul E. McKenney #include <linux/cpu.h> 16*1fbeb3a8SPaul E. McKenney #include <linux/delay.h> 17*1fbeb3a8SPaul E. McKenney #include <linux/err.h> 18*1fbeb3a8SPaul E. McKenney #include <linux/init.h> 19*1fbeb3a8SPaul E. McKenney #include <linux/interrupt.h> 20*1fbeb3a8SPaul E. McKenney #include <linux/kthread.h> 21*1fbeb3a8SPaul E. McKenney #include <linux/kernel.h> 22*1fbeb3a8SPaul E. McKenney #include <linux/mm.h> 23*1fbeb3a8SPaul E. McKenney #include <linux/module.h> 24*1fbeb3a8SPaul E. McKenney #include <linux/moduleparam.h> 25*1fbeb3a8SPaul E. McKenney #include <linux/notifier.h> 26*1fbeb3a8SPaul E. McKenney #include <linux/percpu.h> 27*1fbeb3a8SPaul E. McKenney #include <linux/rcupdate.h> 28*1fbeb3a8SPaul E. McKenney #include <linux/rcupdate_trace.h> 29*1fbeb3a8SPaul E. McKenney #include <linux/reboot.h> 30*1fbeb3a8SPaul E. McKenney #include <linux/sched.h> 31*1fbeb3a8SPaul E. McKenney #include <linux/spinlock.h> 32*1fbeb3a8SPaul E. McKenney #include <linux/smp.h> 33*1fbeb3a8SPaul E. McKenney #include <linux/stat.h> 34*1fbeb3a8SPaul E. McKenney #include <linux/srcu.h> 35*1fbeb3a8SPaul E. McKenney #include <linux/slab.h> 36*1fbeb3a8SPaul E. McKenney #include <linux/torture.h> 37*1fbeb3a8SPaul E. McKenney #include <linux/types.h> 38*1fbeb3a8SPaul E. McKenney 39*1fbeb3a8SPaul E. McKenney #include "rcu.h" 40*1fbeb3a8SPaul E. McKenney 41*1fbeb3a8SPaul E. McKenney #define SCALE_FLAG "-ref-scale: " 42*1fbeb3a8SPaul E. McKenney 43*1fbeb3a8SPaul E. McKenney #define SCALEOUT(s, x...) \ 44*1fbeb3a8SPaul E. McKenney pr_alert("%s" SCALE_FLAG s, scale_type, ## x) 45*1fbeb3a8SPaul E. McKenney 46*1fbeb3a8SPaul E. McKenney #define VERBOSE_SCALEOUT(s, x...) \ 47*1fbeb3a8SPaul E. McKenney do { if (verbose) pr_alert("%s" SCALE_FLAG s, scale_type, ## x); } while (0) 48*1fbeb3a8SPaul E. McKenney 49*1fbeb3a8SPaul E. McKenney #define VERBOSE_SCALEOUT_ERRSTRING(s, x...) \ 50*1fbeb3a8SPaul E. McKenney do { if (verbose) pr_alert("%s" SCALE_FLAG "!!! " s, scale_type, ## x); } while (0) 51*1fbeb3a8SPaul E. McKenney 52*1fbeb3a8SPaul E. McKenney MODULE_LICENSE("GPL"); 53*1fbeb3a8SPaul E. McKenney MODULE_AUTHOR("Joel Fernandes (Google) <[email protected]>"); 54*1fbeb3a8SPaul E. McKenney 55*1fbeb3a8SPaul E. McKenney static char *scale_type = "rcu"; 56*1fbeb3a8SPaul E. McKenney module_param(scale_type, charp, 0444); 57*1fbeb3a8SPaul E. McKenney MODULE_PARM_DESC(scale_type, "Type of test (rcu, srcu, refcnt, rwsem, rwlock."); 58*1fbeb3a8SPaul E. McKenney 59*1fbeb3a8SPaul E. McKenney torture_param(int, verbose, 0, "Enable verbose debugging printk()s"); 60*1fbeb3a8SPaul E. McKenney 61*1fbeb3a8SPaul E. McKenney // Wait until there are multiple CPUs before starting test. 62*1fbeb3a8SPaul E. McKenney torture_param(int, holdoff, IS_BUILTIN(CONFIG_RCU_REF_SCALE_TEST) ? 10 : 0, 63*1fbeb3a8SPaul E. McKenney "Holdoff time before test start (s)"); 64*1fbeb3a8SPaul E. McKenney // Number of loops per experiment, all readers execute operations concurrently. 65*1fbeb3a8SPaul E. McKenney torture_param(long, loops, 10000, "Number of loops per experiment."); 66*1fbeb3a8SPaul E. McKenney // Number of readers, with -1 defaulting to about 75% of the CPUs. 67*1fbeb3a8SPaul E. McKenney torture_param(int, nreaders, -1, "Number of readers, -1 for 75% of CPUs."); 68*1fbeb3a8SPaul E. McKenney // Number of runs. 69*1fbeb3a8SPaul E. McKenney torture_param(int, nruns, 30, "Number of experiments to run."); 70*1fbeb3a8SPaul E. McKenney // Reader delay in nanoseconds, 0 for no delay. 71*1fbeb3a8SPaul E. McKenney torture_param(int, readdelay, 0, "Read-side delay in nanoseconds."); 72*1fbeb3a8SPaul E. McKenney 73*1fbeb3a8SPaul E. McKenney #ifdef MODULE 74*1fbeb3a8SPaul E. McKenney # define REFSCALE_SHUTDOWN 0 75*1fbeb3a8SPaul E. McKenney #else 76*1fbeb3a8SPaul E. McKenney # define REFSCALE_SHUTDOWN 1 77*1fbeb3a8SPaul E. McKenney #endif 78*1fbeb3a8SPaul E. McKenney 79*1fbeb3a8SPaul E. McKenney torture_param(bool, shutdown, REFSCALE_SHUTDOWN, 80*1fbeb3a8SPaul E. McKenney "Shutdown at end of scalability tests."); 81*1fbeb3a8SPaul E. McKenney 82*1fbeb3a8SPaul E. McKenney struct reader_task { 83*1fbeb3a8SPaul E. McKenney struct task_struct *task; 84*1fbeb3a8SPaul E. McKenney int start_reader; 85*1fbeb3a8SPaul E. McKenney wait_queue_head_t wq; 86*1fbeb3a8SPaul E. McKenney u64 last_duration_ns; 87*1fbeb3a8SPaul E. McKenney }; 88*1fbeb3a8SPaul E. McKenney 89*1fbeb3a8SPaul E. McKenney static struct task_struct *shutdown_task; 90*1fbeb3a8SPaul E. McKenney static wait_queue_head_t shutdown_wq; 91*1fbeb3a8SPaul E. McKenney 92*1fbeb3a8SPaul E. McKenney static struct task_struct *main_task; 93*1fbeb3a8SPaul E. McKenney static wait_queue_head_t main_wq; 94*1fbeb3a8SPaul E. McKenney static int shutdown_start; 95*1fbeb3a8SPaul E. McKenney 96*1fbeb3a8SPaul E. McKenney static struct reader_task *reader_tasks; 97*1fbeb3a8SPaul E. McKenney 98*1fbeb3a8SPaul E. McKenney // Number of readers that are part of the current experiment. 99*1fbeb3a8SPaul E. McKenney static atomic_t nreaders_exp; 100*1fbeb3a8SPaul E. McKenney 101*1fbeb3a8SPaul E. McKenney // Use to wait for all threads to start. 102*1fbeb3a8SPaul E. McKenney static atomic_t n_init; 103*1fbeb3a8SPaul E. McKenney static atomic_t n_started; 104*1fbeb3a8SPaul E. McKenney static atomic_t n_warmedup; 105*1fbeb3a8SPaul E. McKenney static atomic_t n_cooleddown; 106*1fbeb3a8SPaul E. McKenney 107*1fbeb3a8SPaul E. McKenney // Track which experiment is currently running. 108*1fbeb3a8SPaul E. McKenney static int exp_idx; 109*1fbeb3a8SPaul E. McKenney 110*1fbeb3a8SPaul E. McKenney // Operations vector for selecting different types of tests. 111*1fbeb3a8SPaul E. McKenney struct ref_scale_ops { 112*1fbeb3a8SPaul E. McKenney void (*init)(void); 113*1fbeb3a8SPaul E. McKenney void (*cleanup)(void); 114*1fbeb3a8SPaul E. McKenney void (*readsection)(const int nloops); 115*1fbeb3a8SPaul E. McKenney void (*delaysection)(const int nloops, const int udl, const int ndl); 116*1fbeb3a8SPaul E. McKenney const char *name; 117*1fbeb3a8SPaul E. McKenney }; 118*1fbeb3a8SPaul E. McKenney 119*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops *cur_ops; 120*1fbeb3a8SPaul E. McKenney 121*1fbeb3a8SPaul E. McKenney static void un_delay(const int udl, const int ndl) 122*1fbeb3a8SPaul E. McKenney { 123*1fbeb3a8SPaul E. McKenney if (udl) 124*1fbeb3a8SPaul E. McKenney udelay(udl); 125*1fbeb3a8SPaul E. McKenney if (ndl) 126*1fbeb3a8SPaul E. McKenney ndelay(ndl); 127*1fbeb3a8SPaul E. McKenney } 128*1fbeb3a8SPaul E. McKenney 129*1fbeb3a8SPaul E. McKenney static void ref_rcu_read_section(const int nloops) 130*1fbeb3a8SPaul E. McKenney { 131*1fbeb3a8SPaul E. McKenney int i; 132*1fbeb3a8SPaul E. McKenney 133*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 134*1fbeb3a8SPaul E. McKenney rcu_read_lock(); 135*1fbeb3a8SPaul E. McKenney rcu_read_unlock(); 136*1fbeb3a8SPaul E. McKenney } 137*1fbeb3a8SPaul E. McKenney } 138*1fbeb3a8SPaul E. McKenney 139*1fbeb3a8SPaul E. McKenney static void ref_rcu_delay_section(const int nloops, const int udl, const int ndl) 140*1fbeb3a8SPaul E. McKenney { 141*1fbeb3a8SPaul E. McKenney int i; 142*1fbeb3a8SPaul E. McKenney 143*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 144*1fbeb3a8SPaul E. McKenney rcu_read_lock(); 145*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 146*1fbeb3a8SPaul E. McKenney rcu_read_unlock(); 147*1fbeb3a8SPaul E. McKenney } 148*1fbeb3a8SPaul E. McKenney } 149*1fbeb3a8SPaul E. McKenney 150*1fbeb3a8SPaul E. McKenney static void rcu_sync_scale_init(void) 151*1fbeb3a8SPaul E. McKenney { 152*1fbeb3a8SPaul E. McKenney } 153*1fbeb3a8SPaul E. McKenney 154*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rcu_ops = { 155*1fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 156*1fbeb3a8SPaul E. McKenney .readsection = ref_rcu_read_section, 157*1fbeb3a8SPaul E. McKenney .delaysection = ref_rcu_delay_section, 158*1fbeb3a8SPaul E. McKenney .name = "rcu" 159*1fbeb3a8SPaul E. McKenney }; 160*1fbeb3a8SPaul E. McKenney 161*1fbeb3a8SPaul E. McKenney // Definitions for SRCU ref scale testing. 162*1fbeb3a8SPaul E. McKenney DEFINE_STATIC_SRCU(srcu_refctl_scale); 163*1fbeb3a8SPaul E. McKenney static struct srcu_struct *srcu_ctlp = &srcu_refctl_scale; 164*1fbeb3a8SPaul E. McKenney 165*1fbeb3a8SPaul E. McKenney static void srcu_ref_scale_read_section(const int nloops) 166*1fbeb3a8SPaul E. McKenney { 167*1fbeb3a8SPaul E. McKenney int i; 168*1fbeb3a8SPaul E. McKenney int idx; 169*1fbeb3a8SPaul E. McKenney 170*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 171*1fbeb3a8SPaul E. McKenney idx = srcu_read_lock(srcu_ctlp); 172*1fbeb3a8SPaul E. McKenney srcu_read_unlock(srcu_ctlp, idx); 173*1fbeb3a8SPaul E. McKenney } 174*1fbeb3a8SPaul E. McKenney } 175*1fbeb3a8SPaul E. McKenney 176*1fbeb3a8SPaul E. McKenney static void srcu_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 177*1fbeb3a8SPaul E. McKenney { 178*1fbeb3a8SPaul E. McKenney int i; 179*1fbeb3a8SPaul E. McKenney int idx; 180*1fbeb3a8SPaul E. McKenney 181*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 182*1fbeb3a8SPaul E. McKenney idx = srcu_read_lock(srcu_ctlp); 183*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 184*1fbeb3a8SPaul E. McKenney srcu_read_unlock(srcu_ctlp, idx); 185*1fbeb3a8SPaul E. McKenney } 186*1fbeb3a8SPaul E. McKenney } 187*1fbeb3a8SPaul E. McKenney 188*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops srcu_ops = { 189*1fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 190*1fbeb3a8SPaul E. McKenney .readsection = srcu_ref_scale_read_section, 191*1fbeb3a8SPaul E. McKenney .delaysection = srcu_ref_scale_delay_section, 192*1fbeb3a8SPaul E. McKenney .name = "srcu" 193*1fbeb3a8SPaul E. McKenney }; 194*1fbeb3a8SPaul E. McKenney 195*1fbeb3a8SPaul E. McKenney // Definitions for RCU Tasks ref scale testing: Empty read markers. 196*1fbeb3a8SPaul E. McKenney // These definitions also work for RCU Rude readers. 197*1fbeb3a8SPaul E. McKenney static void rcu_tasks_ref_scale_read_section(const int nloops) 198*1fbeb3a8SPaul E. McKenney { 199*1fbeb3a8SPaul E. McKenney int i; 200*1fbeb3a8SPaul E. McKenney 201*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) 202*1fbeb3a8SPaul E. McKenney continue; 203*1fbeb3a8SPaul E. McKenney } 204*1fbeb3a8SPaul E. McKenney 205*1fbeb3a8SPaul E. McKenney static void rcu_tasks_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 206*1fbeb3a8SPaul E. McKenney { 207*1fbeb3a8SPaul E. McKenney int i; 208*1fbeb3a8SPaul E. McKenney 209*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) 210*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 211*1fbeb3a8SPaul E. McKenney } 212*1fbeb3a8SPaul E. McKenney 213*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rcu_tasks_ops = { 214*1fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 215*1fbeb3a8SPaul E. McKenney .readsection = rcu_tasks_ref_scale_read_section, 216*1fbeb3a8SPaul E. McKenney .delaysection = rcu_tasks_ref_scale_delay_section, 217*1fbeb3a8SPaul E. McKenney .name = "rcu-tasks" 218*1fbeb3a8SPaul E. McKenney }; 219*1fbeb3a8SPaul E. McKenney 220*1fbeb3a8SPaul E. McKenney // Definitions for RCU Tasks Trace ref scale testing. 221*1fbeb3a8SPaul E. McKenney static void rcu_trace_ref_scale_read_section(const int nloops) 222*1fbeb3a8SPaul E. McKenney { 223*1fbeb3a8SPaul E. McKenney int i; 224*1fbeb3a8SPaul E. McKenney 225*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 226*1fbeb3a8SPaul E. McKenney rcu_read_lock_trace(); 227*1fbeb3a8SPaul E. McKenney rcu_read_unlock_trace(); 228*1fbeb3a8SPaul E. McKenney } 229*1fbeb3a8SPaul E. McKenney } 230*1fbeb3a8SPaul E. McKenney 231*1fbeb3a8SPaul E. McKenney static void rcu_trace_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 232*1fbeb3a8SPaul E. McKenney { 233*1fbeb3a8SPaul E. McKenney int i; 234*1fbeb3a8SPaul E. McKenney 235*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 236*1fbeb3a8SPaul E. McKenney rcu_read_lock_trace(); 237*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 238*1fbeb3a8SPaul E. McKenney rcu_read_unlock_trace(); 239*1fbeb3a8SPaul E. McKenney } 240*1fbeb3a8SPaul E. McKenney } 241*1fbeb3a8SPaul E. McKenney 242*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rcu_trace_ops = { 243*1fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 244*1fbeb3a8SPaul E. McKenney .readsection = rcu_trace_ref_scale_read_section, 245*1fbeb3a8SPaul E. McKenney .delaysection = rcu_trace_ref_scale_delay_section, 246*1fbeb3a8SPaul E. McKenney .name = "rcu-trace" 247*1fbeb3a8SPaul E. McKenney }; 248*1fbeb3a8SPaul E. McKenney 249*1fbeb3a8SPaul E. McKenney // Definitions for reference count 250*1fbeb3a8SPaul E. McKenney static atomic_t refcnt; 251*1fbeb3a8SPaul E. McKenney 252*1fbeb3a8SPaul E. McKenney static void ref_refcnt_section(const int nloops) 253*1fbeb3a8SPaul E. McKenney { 254*1fbeb3a8SPaul E. McKenney int i; 255*1fbeb3a8SPaul E. McKenney 256*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 257*1fbeb3a8SPaul E. McKenney atomic_inc(&refcnt); 258*1fbeb3a8SPaul E. McKenney atomic_dec(&refcnt); 259*1fbeb3a8SPaul E. McKenney } 260*1fbeb3a8SPaul E. McKenney } 261*1fbeb3a8SPaul E. McKenney 262*1fbeb3a8SPaul E. McKenney static void ref_refcnt_delay_section(const int nloops, const int udl, const int ndl) 263*1fbeb3a8SPaul E. McKenney { 264*1fbeb3a8SPaul E. McKenney int i; 265*1fbeb3a8SPaul E. McKenney 266*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 267*1fbeb3a8SPaul E. McKenney atomic_inc(&refcnt); 268*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 269*1fbeb3a8SPaul E. McKenney atomic_dec(&refcnt); 270*1fbeb3a8SPaul E. McKenney } 271*1fbeb3a8SPaul E. McKenney } 272*1fbeb3a8SPaul E. McKenney 273*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops refcnt_ops = { 274*1fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 275*1fbeb3a8SPaul E. McKenney .readsection = ref_refcnt_section, 276*1fbeb3a8SPaul E. McKenney .delaysection = ref_refcnt_delay_section, 277*1fbeb3a8SPaul E. McKenney .name = "refcnt" 278*1fbeb3a8SPaul E. McKenney }; 279*1fbeb3a8SPaul E. McKenney 280*1fbeb3a8SPaul E. McKenney // Definitions for rwlock 281*1fbeb3a8SPaul E. McKenney static rwlock_t test_rwlock; 282*1fbeb3a8SPaul E. McKenney 283*1fbeb3a8SPaul E. McKenney static void ref_rwlock_init(void) 284*1fbeb3a8SPaul E. McKenney { 285*1fbeb3a8SPaul E. McKenney rwlock_init(&test_rwlock); 286*1fbeb3a8SPaul E. McKenney } 287*1fbeb3a8SPaul E. McKenney 288*1fbeb3a8SPaul E. McKenney static void ref_rwlock_section(const int nloops) 289*1fbeb3a8SPaul E. McKenney { 290*1fbeb3a8SPaul E. McKenney int i; 291*1fbeb3a8SPaul E. McKenney 292*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 293*1fbeb3a8SPaul E. McKenney read_lock(&test_rwlock); 294*1fbeb3a8SPaul E. McKenney read_unlock(&test_rwlock); 295*1fbeb3a8SPaul E. McKenney } 296*1fbeb3a8SPaul E. McKenney } 297*1fbeb3a8SPaul E. McKenney 298*1fbeb3a8SPaul E. McKenney static void ref_rwlock_delay_section(const int nloops, const int udl, const int ndl) 299*1fbeb3a8SPaul E. McKenney { 300*1fbeb3a8SPaul E. McKenney int i; 301*1fbeb3a8SPaul E. McKenney 302*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 303*1fbeb3a8SPaul E. McKenney read_lock(&test_rwlock); 304*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 305*1fbeb3a8SPaul E. McKenney read_unlock(&test_rwlock); 306*1fbeb3a8SPaul E. McKenney } 307*1fbeb3a8SPaul E. McKenney } 308*1fbeb3a8SPaul E. McKenney 309*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rwlock_ops = { 310*1fbeb3a8SPaul E. McKenney .init = ref_rwlock_init, 311*1fbeb3a8SPaul E. McKenney .readsection = ref_rwlock_section, 312*1fbeb3a8SPaul E. McKenney .delaysection = ref_rwlock_delay_section, 313*1fbeb3a8SPaul E. McKenney .name = "rwlock" 314*1fbeb3a8SPaul E. McKenney }; 315*1fbeb3a8SPaul E. McKenney 316*1fbeb3a8SPaul E. McKenney // Definitions for rwsem 317*1fbeb3a8SPaul E. McKenney static struct rw_semaphore test_rwsem; 318*1fbeb3a8SPaul E. McKenney 319*1fbeb3a8SPaul E. McKenney static void ref_rwsem_init(void) 320*1fbeb3a8SPaul E. McKenney { 321*1fbeb3a8SPaul E. McKenney init_rwsem(&test_rwsem); 322*1fbeb3a8SPaul E. McKenney } 323*1fbeb3a8SPaul E. McKenney 324*1fbeb3a8SPaul E. McKenney static void ref_rwsem_section(const int nloops) 325*1fbeb3a8SPaul E. McKenney { 326*1fbeb3a8SPaul E. McKenney int i; 327*1fbeb3a8SPaul E. McKenney 328*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 329*1fbeb3a8SPaul E. McKenney down_read(&test_rwsem); 330*1fbeb3a8SPaul E. McKenney up_read(&test_rwsem); 331*1fbeb3a8SPaul E. McKenney } 332*1fbeb3a8SPaul E. McKenney } 333*1fbeb3a8SPaul E. McKenney 334*1fbeb3a8SPaul E. McKenney static void ref_rwsem_delay_section(const int nloops, const int udl, const int ndl) 335*1fbeb3a8SPaul E. McKenney { 336*1fbeb3a8SPaul E. McKenney int i; 337*1fbeb3a8SPaul E. McKenney 338*1fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 339*1fbeb3a8SPaul E. McKenney down_read(&test_rwsem); 340*1fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 341*1fbeb3a8SPaul E. McKenney up_read(&test_rwsem); 342*1fbeb3a8SPaul E. McKenney } 343*1fbeb3a8SPaul E. McKenney } 344*1fbeb3a8SPaul E. McKenney 345*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops rwsem_ops = { 346*1fbeb3a8SPaul E. McKenney .init = ref_rwsem_init, 347*1fbeb3a8SPaul E. McKenney .readsection = ref_rwsem_section, 348*1fbeb3a8SPaul E. McKenney .delaysection = ref_rwsem_delay_section, 349*1fbeb3a8SPaul E. McKenney .name = "rwsem" 350*1fbeb3a8SPaul E. McKenney }; 351*1fbeb3a8SPaul E. McKenney 352*1fbeb3a8SPaul E. McKenney static void rcu_scale_one_reader(void) 353*1fbeb3a8SPaul E. McKenney { 354*1fbeb3a8SPaul E. McKenney if (readdelay <= 0) 355*1fbeb3a8SPaul E. McKenney cur_ops->readsection(loops); 356*1fbeb3a8SPaul E. McKenney else 357*1fbeb3a8SPaul E. McKenney cur_ops->delaysection(loops, readdelay / 1000, readdelay % 1000); 358*1fbeb3a8SPaul E. McKenney } 359*1fbeb3a8SPaul E. McKenney 360*1fbeb3a8SPaul E. McKenney // Reader kthread. Repeatedly does empty RCU read-side 361*1fbeb3a8SPaul E. McKenney // critical section, minimizing update-side interference. 362*1fbeb3a8SPaul E. McKenney static int 363*1fbeb3a8SPaul E. McKenney ref_scale_reader(void *arg) 364*1fbeb3a8SPaul E. McKenney { 365*1fbeb3a8SPaul E. McKenney unsigned long flags; 366*1fbeb3a8SPaul E. McKenney long me = (long)arg; 367*1fbeb3a8SPaul E. McKenney struct reader_task *rt = &(reader_tasks[me]); 368*1fbeb3a8SPaul E. McKenney u64 start; 369*1fbeb3a8SPaul E. McKenney s64 duration; 370*1fbeb3a8SPaul E. McKenney 371*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("ref_scale_reader %ld: task started", me); 372*1fbeb3a8SPaul E. McKenney set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids)); 373*1fbeb3a8SPaul E. McKenney set_user_nice(current, MAX_NICE); 374*1fbeb3a8SPaul E. McKenney atomic_inc(&n_init); 375*1fbeb3a8SPaul E. McKenney if (holdoff) 376*1fbeb3a8SPaul E. McKenney schedule_timeout_interruptible(holdoff * HZ); 377*1fbeb3a8SPaul E. McKenney repeat: 378*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("ref_scale_reader %ld: waiting to start next experiment on cpu %d", me, smp_processor_id()); 379*1fbeb3a8SPaul E. McKenney 380*1fbeb3a8SPaul E. McKenney // Wait for signal that this reader can start. 381*1fbeb3a8SPaul E. McKenney wait_event(rt->wq, (atomic_read(&nreaders_exp) && smp_load_acquire(&rt->start_reader)) || 382*1fbeb3a8SPaul E. McKenney torture_must_stop()); 383*1fbeb3a8SPaul E. McKenney 384*1fbeb3a8SPaul E. McKenney if (torture_must_stop()) 385*1fbeb3a8SPaul E. McKenney goto end; 386*1fbeb3a8SPaul E. McKenney 387*1fbeb3a8SPaul E. McKenney // Make sure that the CPU is affinitized appropriately during testing. 388*1fbeb3a8SPaul E. McKenney WARN_ON_ONCE(smp_processor_id() != me); 389*1fbeb3a8SPaul E. McKenney 390*1fbeb3a8SPaul E. McKenney WRITE_ONCE(rt->start_reader, 0); 391*1fbeb3a8SPaul E. McKenney if (!atomic_dec_return(&n_started)) 392*1fbeb3a8SPaul E. McKenney while (atomic_read_acquire(&n_started)) 393*1fbeb3a8SPaul E. McKenney cpu_relax(); 394*1fbeb3a8SPaul E. McKenney 395*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("ref_scale_reader %ld: experiment %d started", me, exp_idx); 396*1fbeb3a8SPaul E. McKenney 397*1fbeb3a8SPaul E. McKenney 398*1fbeb3a8SPaul E. McKenney // To reduce noise, do an initial cache-warming invocation, check 399*1fbeb3a8SPaul E. McKenney // in, and then keep warming until everyone has checked in. 400*1fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 401*1fbeb3a8SPaul E. McKenney if (!atomic_dec_return(&n_warmedup)) 402*1fbeb3a8SPaul E. McKenney while (atomic_read_acquire(&n_warmedup)) 403*1fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 404*1fbeb3a8SPaul E. McKenney // Also keep interrupts disabled. This also has the effect 405*1fbeb3a8SPaul E. McKenney // of preventing entries into slow path for rcu_read_unlock(). 406*1fbeb3a8SPaul E. McKenney local_irq_save(flags); 407*1fbeb3a8SPaul E. McKenney start = ktime_get_mono_fast_ns(); 408*1fbeb3a8SPaul E. McKenney 409*1fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 410*1fbeb3a8SPaul E. McKenney 411*1fbeb3a8SPaul E. McKenney duration = ktime_get_mono_fast_ns() - start; 412*1fbeb3a8SPaul E. McKenney local_irq_restore(flags); 413*1fbeb3a8SPaul E. McKenney 414*1fbeb3a8SPaul E. McKenney rt->last_duration_ns = WARN_ON_ONCE(duration < 0) ? 0 : duration; 415*1fbeb3a8SPaul E. McKenney // To reduce runtime-skew noise, do maintain-load invocations until 416*1fbeb3a8SPaul E. McKenney // everyone is done. 417*1fbeb3a8SPaul E. McKenney if (!atomic_dec_return(&n_cooleddown)) 418*1fbeb3a8SPaul E. McKenney while (atomic_read_acquire(&n_cooleddown)) 419*1fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 420*1fbeb3a8SPaul E. McKenney 421*1fbeb3a8SPaul E. McKenney if (atomic_dec_and_test(&nreaders_exp)) 422*1fbeb3a8SPaul E. McKenney wake_up(&main_wq); 423*1fbeb3a8SPaul E. McKenney 424*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("ref_scale_reader %ld: experiment %d ended, (readers remaining=%d)", 425*1fbeb3a8SPaul E. McKenney me, exp_idx, atomic_read(&nreaders_exp)); 426*1fbeb3a8SPaul E. McKenney 427*1fbeb3a8SPaul E. McKenney if (!torture_must_stop()) 428*1fbeb3a8SPaul E. McKenney goto repeat; 429*1fbeb3a8SPaul E. McKenney end: 430*1fbeb3a8SPaul E. McKenney torture_kthread_stopping("ref_scale_reader"); 431*1fbeb3a8SPaul E. McKenney return 0; 432*1fbeb3a8SPaul E. McKenney } 433*1fbeb3a8SPaul E. McKenney 434*1fbeb3a8SPaul E. McKenney static void reset_readers(void) 435*1fbeb3a8SPaul E. McKenney { 436*1fbeb3a8SPaul E. McKenney int i; 437*1fbeb3a8SPaul E. McKenney struct reader_task *rt; 438*1fbeb3a8SPaul E. McKenney 439*1fbeb3a8SPaul E. McKenney for (i = 0; i < nreaders; i++) { 440*1fbeb3a8SPaul E. McKenney rt = &(reader_tasks[i]); 441*1fbeb3a8SPaul E. McKenney 442*1fbeb3a8SPaul E. McKenney rt->last_duration_ns = 0; 443*1fbeb3a8SPaul E. McKenney } 444*1fbeb3a8SPaul E. McKenney } 445*1fbeb3a8SPaul E. McKenney 446*1fbeb3a8SPaul E. McKenney // Print the results of each reader and return the sum of all their durations. 447*1fbeb3a8SPaul E. McKenney static u64 process_durations(int n) 448*1fbeb3a8SPaul E. McKenney { 449*1fbeb3a8SPaul E. McKenney int i; 450*1fbeb3a8SPaul E. McKenney struct reader_task *rt; 451*1fbeb3a8SPaul E. McKenney char buf1[64]; 452*1fbeb3a8SPaul E. McKenney char *buf; 453*1fbeb3a8SPaul E. McKenney u64 sum = 0; 454*1fbeb3a8SPaul E. McKenney 455*1fbeb3a8SPaul E. McKenney buf = kmalloc(128 + nreaders * 32, GFP_KERNEL); 456*1fbeb3a8SPaul E. McKenney if (!buf) 457*1fbeb3a8SPaul E. McKenney return 0; 458*1fbeb3a8SPaul E. McKenney buf[0] = 0; 459*1fbeb3a8SPaul E. McKenney sprintf(buf, "Experiment #%d (Format: <THREAD-NUM>:<Total loop time in ns>)", 460*1fbeb3a8SPaul E. McKenney exp_idx); 461*1fbeb3a8SPaul E. McKenney 462*1fbeb3a8SPaul E. McKenney for (i = 0; i < n && !torture_must_stop(); i++) { 463*1fbeb3a8SPaul E. McKenney rt = &(reader_tasks[i]); 464*1fbeb3a8SPaul E. McKenney sprintf(buf1, "%d: %llu\t", i, rt->last_duration_ns); 465*1fbeb3a8SPaul E. McKenney 466*1fbeb3a8SPaul E. McKenney if (i % 5 == 0) 467*1fbeb3a8SPaul E. McKenney strcat(buf, "\n"); 468*1fbeb3a8SPaul E. McKenney strcat(buf, buf1); 469*1fbeb3a8SPaul E. McKenney 470*1fbeb3a8SPaul E. McKenney sum += rt->last_duration_ns; 471*1fbeb3a8SPaul E. McKenney } 472*1fbeb3a8SPaul E. McKenney strcat(buf, "\n"); 473*1fbeb3a8SPaul E. McKenney 474*1fbeb3a8SPaul E. McKenney SCALEOUT("%s\n", buf); 475*1fbeb3a8SPaul E. McKenney 476*1fbeb3a8SPaul E. McKenney kfree(buf); 477*1fbeb3a8SPaul E. McKenney return sum; 478*1fbeb3a8SPaul E. McKenney } 479*1fbeb3a8SPaul E. McKenney 480*1fbeb3a8SPaul E. McKenney // The main_func is the main orchestrator, it performs a bunch of 481*1fbeb3a8SPaul E. McKenney // experiments. For every experiment, it orders all the readers 482*1fbeb3a8SPaul E. McKenney // involved to start and waits for them to finish the experiment. It 483*1fbeb3a8SPaul E. McKenney // then reads their timestamps and starts the next experiment. Each 484*1fbeb3a8SPaul E. McKenney // experiment progresses from 1 concurrent reader to N of them at which 485*1fbeb3a8SPaul E. McKenney // point all the timestamps are printed. 486*1fbeb3a8SPaul E. McKenney static int main_func(void *arg) 487*1fbeb3a8SPaul E. McKenney { 488*1fbeb3a8SPaul E. McKenney bool errexit = false; 489*1fbeb3a8SPaul E. McKenney int exp, r; 490*1fbeb3a8SPaul E. McKenney char buf1[64]; 491*1fbeb3a8SPaul E. McKenney char *buf; 492*1fbeb3a8SPaul E. McKenney u64 *result_avg; 493*1fbeb3a8SPaul E. McKenney 494*1fbeb3a8SPaul E. McKenney set_cpus_allowed_ptr(current, cpumask_of(nreaders % nr_cpu_ids)); 495*1fbeb3a8SPaul E. McKenney set_user_nice(current, MAX_NICE); 496*1fbeb3a8SPaul E. McKenney 497*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("main_func task started"); 498*1fbeb3a8SPaul E. McKenney result_avg = kzalloc(nruns * sizeof(*result_avg), GFP_KERNEL); 499*1fbeb3a8SPaul E. McKenney buf = kzalloc(64 + nruns * 32, GFP_KERNEL); 500*1fbeb3a8SPaul E. McKenney if (!result_avg || !buf) { 501*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT_ERRSTRING("out of memory"); 502*1fbeb3a8SPaul E. McKenney errexit = true; 503*1fbeb3a8SPaul E. McKenney } 504*1fbeb3a8SPaul E. McKenney if (holdoff) 505*1fbeb3a8SPaul E. McKenney schedule_timeout_interruptible(holdoff * HZ); 506*1fbeb3a8SPaul E. McKenney 507*1fbeb3a8SPaul E. McKenney // Wait for all threads to start. 508*1fbeb3a8SPaul E. McKenney atomic_inc(&n_init); 509*1fbeb3a8SPaul E. McKenney while (atomic_read(&n_init) < nreaders + 1) 510*1fbeb3a8SPaul E. McKenney schedule_timeout_uninterruptible(1); 511*1fbeb3a8SPaul E. McKenney 512*1fbeb3a8SPaul E. McKenney // Start exp readers up per experiment 513*1fbeb3a8SPaul E. McKenney for (exp = 0; exp < nruns && !torture_must_stop(); exp++) { 514*1fbeb3a8SPaul E. McKenney if (errexit) 515*1fbeb3a8SPaul E. McKenney break; 516*1fbeb3a8SPaul E. McKenney if (torture_must_stop()) 517*1fbeb3a8SPaul E. McKenney goto end; 518*1fbeb3a8SPaul E. McKenney 519*1fbeb3a8SPaul E. McKenney reset_readers(); 520*1fbeb3a8SPaul E. McKenney atomic_set(&nreaders_exp, nreaders); 521*1fbeb3a8SPaul E. McKenney atomic_set(&n_started, nreaders); 522*1fbeb3a8SPaul E. McKenney atomic_set(&n_warmedup, nreaders); 523*1fbeb3a8SPaul E. McKenney atomic_set(&n_cooleddown, nreaders); 524*1fbeb3a8SPaul E. McKenney 525*1fbeb3a8SPaul E. McKenney exp_idx = exp; 526*1fbeb3a8SPaul E. McKenney 527*1fbeb3a8SPaul E. McKenney for (r = 0; r < nreaders; r++) { 528*1fbeb3a8SPaul E. McKenney smp_store_release(&reader_tasks[r].start_reader, 1); 529*1fbeb3a8SPaul E. McKenney wake_up(&reader_tasks[r].wq); 530*1fbeb3a8SPaul E. McKenney } 531*1fbeb3a8SPaul E. McKenney 532*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("main_func: experiment started, waiting for %d readers", 533*1fbeb3a8SPaul E. McKenney nreaders); 534*1fbeb3a8SPaul E. McKenney 535*1fbeb3a8SPaul E. McKenney wait_event(main_wq, 536*1fbeb3a8SPaul E. McKenney !atomic_read(&nreaders_exp) || torture_must_stop()); 537*1fbeb3a8SPaul E. McKenney 538*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("main_func: experiment ended"); 539*1fbeb3a8SPaul E. McKenney 540*1fbeb3a8SPaul E. McKenney if (torture_must_stop()) 541*1fbeb3a8SPaul E. McKenney goto end; 542*1fbeb3a8SPaul E. McKenney 543*1fbeb3a8SPaul E. McKenney result_avg[exp] = div_u64(1000 * process_durations(nreaders), nreaders * loops); 544*1fbeb3a8SPaul E. McKenney } 545*1fbeb3a8SPaul E. McKenney 546*1fbeb3a8SPaul E. McKenney // Print the average of all experiments 547*1fbeb3a8SPaul E. McKenney SCALEOUT("END OF TEST. Calculating average duration per loop (nanoseconds)...\n"); 548*1fbeb3a8SPaul E. McKenney 549*1fbeb3a8SPaul E. McKenney buf[0] = 0; 550*1fbeb3a8SPaul E. McKenney strcat(buf, "\n"); 551*1fbeb3a8SPaul E. McKenney strcat(buf, "Runs\tTime(ns)\n"); 552*1fbeb3a8SPaul E. McKenney 553*1fbeb3a8SPaul E. McKenney for (exp = 0; exp < nruns; exp++) { 554*1fbeb3a8SPaul E. McKenney u64 avg; 555*1fbeb3a8SPaul E. McKenney u32 rem; 556*1fbeb3a8SPaul E. McKenney 557*1fbeb3a8SPaul E. McKenney if (errexit) 558*1fbeb3a8SPaul E. McKenney break; 559*1fbeb3a8SPaul E. McKenney avg = div_u64_rem(result_avg[exp], 1000, &rem); 560*1fbeb3a8SPaul E. McKenney sprintf(buf1, "%d\t%llu.%03u\n", exp + 1, avg, rem); 561*1fbeb3a8SPaul E. McKenney strcat(buf, buf1); 562*1fbeb3a8SPaul E. McKenney } 563*1fbeb3a8SPaul E. McKenney 564*1fbeb3a8SPaul E. McKenney if (!errexit) 565*1fbeb3a8SPaul E. McKenney SCALEOUT("%s", buf); 566*1fbeb3a8SPaul E. McKenney 567*1fbeb3a8SPaul E. McKenney // This will shutdown everything including us. 568*1fbeb3a8SPaul E. McKenney if (shutdown) { 569*1fbeb3a8SPaul E. McKenney shutdown_start = 1; 570*1fbeb3a8SPaul E. McKenney wake_up(&shutdown_wq); 571*1fbeb3a8SPaul E. McKenney } 572*1fbeb3a8SPaul E. McKenney 573*1fbeb3a8SPaul E. McKenney // Wait for torture to stop us 574*1fbeb3a8SPaul E. McKenney while (!torture_must_stop()) 575*1fbeb3a8SPaul E. McKenney schedule_timeout_uninterruptible(1); 576*1fbeb3a8SPaul E. McKenney 577*1fbeb3a8SPaul E. McKenney end: 578*1fbeb3a8SPaul E. McKenney torture_kthread_stopping("main_func"); 579*1fbeb3a8SPaul E. McKenney kfree(result_avg); 580*1fbeb3a8SPaul E. McKenney kfree(buf); 581*1fbeb3a8SPaul E. McKenney return 0; 582*1fbeb3a8SPaul E. McKenney } 583*1fbeb3a8SPaul E. McKenney 584*1fbeb3a8SPaul E. McKenney static void 585*1fbeb3a8SPaul E. McKenney ref_scale_print_module_parms(struct ref_scale_ops *cur_ops, const char *tag) 586*1fbeb3a8SPaul E. McKenney { 587*1fbeb3a8SPaul E. McKenney pr_alert("%s" SCALE_FLAG 588*1fbeb3a8SPaul E. McKenney "--- %s: verbose=%d shutdown=%d holdoff=%d loops=%ld nreaders=%d nruns=%d readdelay=%d\n", scale_type, tag, 589*1fbeb3a8SPaul E. McKenney verbose, shutdown, holdoff, loops, nreaders, nruns, readdelay); 590*1fbeb3a8SPaul E. McKenney } 591*1fbeb3a8SPaul E. McKenney 592*1fbeb3a8SPaul E. McKenney static void 593*1fbeb3a8SPaul E. McKenney ref_scale_cleanup(void) 594*1fbeb3a8SPaul E. McKenney { 595*1fbeb3a8SPaul E. McKenney int i; 596*1fbeb3a8SPaul E. McKenney 597*1fbeb3a8SPaul E. McKenney if (torture_cleanup_begin()) 598*1fbeb3a8SPaul E. McKenney return; 599*1fbeb3a8SPaul E. McKenney 600*1fbeb3a8SPaul E. McKenney if (!cur_ops) { 601*1fbeb3a8SPaul E. McKenney torture_cleanup_end(); 602*1fbeb3a8SPaul E. McKenney return; 603*1fbeb3a8SPaul E. McKenney } 604*1fbeb3a8SPaul E. McKenney 605*1fbeb3a8SPaul E. McKenney if (reader_tasks) { 606*1fbeb3a8SPaul E. McKenney for (i = 0; i < nreaders; i++) 607*1fbeb3a8SPaul E. McKenney torture_stop_kthread("ref_scale_reader", 608*1fbeb3a8SPaul E. McKenney reader_tasks[i].task); 609*1fbeb3a8SPaul E. McKenney } 610*1fbeb3a8SPaul E. McKenney kfree(reader_tasks); 611*1fbeb3a8SPaul E. McKenney 612*1fbeb3a8SPaul E. McKenney torture_stop_kthread("main_task", main_task); 613*1fbeb3a8SPaul E. McKenney kfree(main_task); 614*1fbeb3a8SPaul E. McKenney 615*1fbeb3a8SPaul E. McKenney // Do scale-type-specific cleanup operations. 616*1fbeb3a8SPaul E. McKenney if (cur_ops->cleanup != NULL) 617*1fbeb3a8SPaul E. McKenney cur_ops->cleanup(); 618*1fbeb3a8SPaul E. McKenney 619*1fbeb3a8SPaul E. McKenney torture_cleanup_end(); 620*1fbeb3a8SPaul E. McKenney } 621*1fbeb3a8SPaul E. McKenney 622*1fbeb3a8SPaul E. McKenney // Shutdown kthread. Just waits to be awakened, then shuts down system. 623*1fbeb3a8SPaul E. McKenney static int 624*1fbeb3a8SPaul E. McKenney ref_scale_shutdown(void *arg) 625*1fbeb3a8SPaul E. McKenney { 626*1fbeb3a8SPaul E. McKenney wait_event(shutdown_wq, shutdown_start); 627*1fbeb3a8SPaul E. McKenney 628*1fbeb3a8SPaul E. McKenney smp_mb(); // Wake before output. 629*1fbeb3a8SPaul E. McKenney ref_scale_cleanup(); 630*1fbeb3a8SPaul E. McKenney kernel_power_off(); 631*1fbeb3a8SPaul E. McKenney 632*1fbeb3a8SPaul E. McKenney return -EINVAL; 633*1fbeb3a8SPaul E. McKenney } 634*1fbeb3a8SPaul E. McKenney 635*1fbeb3a8SPaul E. McKenney static int __init 636*1fbeb3a8SPaul E. McKenney ref_scale_init(void) 637*1fbeb3a8SPaul E. McKenney { 638*1fbeb3a8SPaul E. McKenney long i; 639*1fbeb3a8SPaul E. McKenney int firsterr = 0; 640*1fbeb3a8SPaul E. McKenney static struct ref_scale_ops *scale_ops[] = { 641*1fbeb3a8SPaul E. McKenney &rcu_ops, &srcu_ops, &rcu_trace_ops, &rcu_tasks_ops, 642*1fbeb3a8SPaul E. McKenney &refcnt_ops, &rwlock_ops, &rwsem_ops, 643*1fbeb3a8SPaul E. McKenney }; 644*1fbeb3a8SPaul E. McKenney 645*1fbeb3a8SPaul E. McKenney if (!torture_init_begin(scale_type, verbose)) 646*1fbeb3a8SPaul E. McKenney return -EBUSY; 647*1fbeb3a8SPaul E. McKenney 648*1fbeb3a8SPaul E. McKenney for (i = 0; i < ARRAY_SIZE(scale_ops); i++) { 649*1fbeb3a8SPaul E. McKenney cur_ops = scale_ops[i]; 650*1fbeb3a8SPaul E. McKenney if (strcmp(scale_type, cur_ops->name) == 0) 651*1fbeb3a8SPaul E. McKenney break; 652*1fbeb3a8SPaul E. McKenney } 653*1fbeb3a8SPaul E. McKenney if (i == ARRAY_SIZE(scale_ops)) { 654*1fbeb3a8SPaul E. McKenney pr_alert("rcu-scale: invalid scale type: \"%s\"\n", scale_type); 655*1fbeb3a8SPaul E. McKenney pr_alert("rcu-scale types:"); 656*1fbeb3a8SPaul E. McKenney for (i = 0; i < ARRAY_SIZE(scale_ops); i++) 657*1fbeb3a8SPaul E. McKenney pr_cont(" %s", scale_ops[i]->name); 658*1fbeb3a8SPaul E. McKenney pr_cont("\n"); 659*1fbeb3a8SPaul E. McKenney WARN_ON(!IS_MODULE(CONFIG_RCU_REF_SCALE_TEST)); 660*1fbeb3a8SPaul E. McKenney firsterr = -EINVAL; 661*1fbeb3a8SPaul E. McKenney cur_ops = NULL; 662*1fbeb3a8SPaul E. McKenney goto unwind; 663*1fbeb3a8SPaul E. McKenney } 664*1fbeb3a8SPaul E. McKenney if (cur_ops->init) 665*1fbeb3a8SPaul E. McKenney cur_ops->init(); 666*1fbeb3a8SPaul E. McKenney 667*1fbeb3a8SPaul E. McKenney ref_scale_print_module_parms(cur_ops, "Start of test"); 668*1fbeb3a8SPaul E. McKenney 669*1fbeb3a8SPaul E. McKenney // Shutdown task 670*1fbeb3a8SPaul E. McKenney if (shutdown) { 671*1fbeb3a8SPaul E. McKenney init_waitqueue_head(&shutdown_wq); 672*1fbeb3a8SPaul E. McKenney firsterr = torture_create_kthread(ref_scale_shutdown, NULL, 673*1fbeb3a8SPaul E. McKenney shutdown_task); 674*1fbeb3a8SPaul E. McKenney if (firsterr) 675*1fbeb3a8SPaul E. McKenney goto unwind; 676*1fbeb3a8SPaul E. McKenney schedule_timeout_uninterruptible(1); 677*1fbeb3a8SPaul E. McKenney } 678*1fbeb3a8SPaul E. McKenney 679*1fbeb3a8SPaul E. McKenney // Reader tasks (default to ~75% of online CPUs). 680*1fbeb3a8SPaul E. McKenney if (nreaders < 0) 681*1fbeb3a8SPaul E. McKenney nreaders = (num_online_cpus() >> 1) + (num_online_cpus() >> 2); 682*1fbeb3a8SPaul E. McKenney reader_tasks = kcalloc(nreaders, sizeof(reader_tasks[0]), 683*1fbeb3a8SPaul E. McKenney GFP_KERNEL); 684*1fbeb3a8SPaul E. McKenney if (!reader_tasks) { 685*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT_ERRSTRING("out of memory"); 686*1fbeb3a8SPaul E. McKenney firsterr = -ENOMEM; 687*1fbeb3a8SPaul E. McKenney goto unwind; 688*1fbeb3a8SPaul E. McKenney } 689*1fbeb3a8SPaul E. McKenney 690*1fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("Starting %d reader threads\n", nreaders); 691*1fbeb3a8SPaul E. McKenney 692*1fbeb3a8SPaul E. McKenney for (i = 0; i < nreaders; i++) { 693*1fbeb3a8SPaul E. McKenney firsterr = torture_create_kthread(ref_scale_reader, (void *)i, 694*1fbeb3a8SPaul E. McKenney reader_tasks[i].task); 695*1fbeb3a8SPaul E. McKenney if (firsterr) 696*1fbeb3a8SPaul E. McKenney goto unwind; 697*1fbeb3a8SPaul E. McKenney 698*1fbeb3a8SPaul E. McKenney init_waitqueue_head(&(reader_tasks[i].wq)); 699*1fbeb3a8SPaul E. McKenney } 700*1fbeb3a8SPaul E. McKenney 701*1fbeb3a8SPaul E. McKenney // Main Task 702*1fbeb3a8SPaul E. McKenney init_waitqueue_head(&main_wq); 703*1fbeb3a8SPaul E. McKenney firsterr = torture_create_kthread(main_func, NULL, main_task); 704*1fbeb3a8SPaul E. McKenney if (firsterr) 705*1fbeb3a8SPaul E. McKenney goto unwind; 706*1fbeb3a8SPaul E. McKenney 707*1fbeb3a8SPaul E. McKenney torture_init_end(); 708*1fbeb3a8SPaul E. McKenney return 0; 709*1fbeb3a8SPaul E. McKenney 710*1fbeb3a8SPaul E. McKenney unwind: 711*1fbeb3a8SPaul E. McKenney torture_init_end(); 712*1fbeb3a8SPaul E. McKenney ref_scale_cleanup(); 713*1fbeb3a8SPaul E. McKenney return firsterr; 714*1fbeb3a8SPaul E. McKenney } 715*1fbeb3a8SPaul E. McKenney 716*1fbeb3a8SPaul E. McKenney module_init(ref_scale_init); 717*1fbeb3a8SPaul E. McKenney module_exit(ref_scale_cleanup); 718