11fbeb3a8SPaul E. McKenney // SPDX-License-Identifier: GPL-2.0+ 21fbeb3a8SPaul E. McKenney // 31fbeb3a8SPaul E. McKenney // Scalability test comparing RCU vs other mechanisms 41fbeb3a8SPaul E. McKenney // for acquiring references on objects. 51fbeb3a8SPaul E. McKenney // 61fbeb3a8SPaul E. McKenney // Copyright (C) Google, 2020. 71fbeb3a8SPaul E. McKenney // 81fbeb3a8SPaul E. McKenney // Author: Joel Fernandes <[email protected]> 91fbeb3a8SPaul E. McKenney 101fbeb3a8SPaul E. McKenney #define pr_fmt(fmt) fmt 111fbeb3a8SPaul E. McKenney 121fbeb3a8SPaul E. McKenney #include <linux/atomic.h> 131fbeb3a8SPaul E. McKenney #include <linux/bitops.h> 141fbeb3a8SPaul E. McKenney #include <linux/completion.h> 151fbeb3a8SPaul E. McKenney #include <linux/cpu.h> 161fbeb3a8SPaul E. McKenney #include <linux/delay.h> 171fbeb3a8SPaul E. McKenney #include <linux/err.h> 181fbeb3a8SPaul E. McKenney #include <linux/init.h> 191fbeb3a8SPaul E. McKenney #include <linux/interrupt.h> 201fbeb3a8SPaul E. McKenney #include <linux/kthread.h> 211fbeb3a8SPaul E. McKenney #include <linux/kernel.h> 221fbeb3a8SPaul E. McKenney #include <linux/mm.h> 231fbeb3a8SPaul E. McKenney #include <linux/module.h> 241fbeb3a8SPaul E. McKenney #include <linux/moduleparam.h> 251fbeb3a8SPaul E. McKenney #include <linux/notifier.h> 261fbeb3a8SPaul E. McKenney #include <linux/percpu.h> 271fbeb3a8SPaul E. McKenney #include <linux/rcupdate.h> 281fbeb3a8SPaul E. McKenney #include <linux/rcupdate_trace.h> 291fbeb3a8SPaul E. McKenney #include <linux/reboot.h> 301fbeb3a8SPaul E. McKenney #include <linux/sched.h> 314e39bb49SChristophe JAILLET #include <linux/seq_buf.h> 321fbeb3a8SPaul E. McKenney #include <linux/spinlock.h> 331fbeb3a8SPaul E. McKenney #include <linux/smp.h> 341fbeb3a8SPaul E. McKenney #include <linux/stat.h> 351fbeb3a8SPaul E. McKenney #include <linux/srcu.h> 361fbeb3a8SPaul E. McKenney #include <linux/slab.h> 371fbeb3a8SPaul E. McKenney #include <linux/torture.h> 381fbeb3a8SPaul E. McKenney #include <linux/types.h> 391fbeb3a8SPaul E. McKenney 401fbeb3a8SPaul E. McKenney #include "rcu.h" 411fbeb3a8SPaul E. McKenney 421fbeb3a8SPaul E. McKenney #define SCALE_FLAG "-ref-scale: " 431fbeb3a8SPaul E. McKenney 441fbeb3a8SPaul E. McKenney #define SCALEOUT(s, x...) \ 451fbeb3a8SPaul E. McKenney pr_alert("%s" SCALE_FLAG s, scale_type, ## x) 461fbeb3a8SPaul E. McKenney 471fbeb3a8SPaul E. McKenney #define VERBOSE_SCALEOUT(s, x...) \ 48f71f22b6SLi Zhijian do { \ 49f71f22b6SLi Zhijian if (verbose) \ 50f71f22b6SLi Zhijian pr_alert("%s" SCALE_FLAG s "\n", scale_type, ## x); \ 51f71f22b6SLi Zhijian } while (0) 521fbeb3a8SPaul E. McKenney 53e76506f0SPaul E. McKenney static atomic_t verbose_batch_ctr; 54e76506f0SPaul E. McKenney 55e76506f0SPaul E. McKenney #define VERBOSE_SCALEOUT_BATCH(s, x...) \ 56e76506f0SPaul E. McKenney do { \ 57e76506f0SPaul E. McKenney if (verbose && \ 58e76506f0SPaul E. McKenney (verbose_batched <= 0 || \ 59414c116eSPaul E. McKenney !(atomic_inc_return(&verbose_batch_ctr) % verbose_batched))) { \ 60414c116eSPaul E. McKenney schedule_timeout_uninterruptible(1); \ 61f71f22b6SLi Zhijian pr_alert("%s" SCALE_FLAG s "\n", scale_type, ## x); \ 62414c116eSPaul E. McKenney } \ 63e76506f0SPaul E. McKenney } while (0) 64e76506f0SPaul E. McKenney 65f71f22b6SLi Zhijian #define SCALEOUT_ERRSTRING(s, x...) pr_alert("%s" SCALE_FLAG "!!! " s "\n", scale_type, ## x) 661fbeb3a8SPaul E. McKenney 67b9f147cdSJeff Johnson MODULE_DESCRIPTION("Scalability test for object reference mechanisms"); 681fbeb3a8SPaul E. McKenney MODULE_LICENSE("GPL"); 691fbeb3a8SPaul E. McKenney MODULE_AUTHOR("Joel Fernandes (Google) <[email protected]>"); 701fbeb3a8SPaul E. McKenney 711fbeb3a8SPaul E. McKenney static char *scale_type = "rcu"; 721fbeb3a8SPaul E. McKenney module_param(scale_type, charp, 0444); 731fbeb3a8SPaul E. McKenney MODULE_PARM_DESC(scale_type, "Type of test (rcu, srcu, refcnt, rwsem, rwlock."); 741fbeb3a8SPaul E. McKenney 751fbeb3a8SPaul E. McKenney torture_param(int, verbose, 0, "Enable verbose debugging printk()s"); 76e76506f0SPaul E. McKenney torture_param(int, verbose_batched, 0, "Batch verbose debugging printk()s"); 771fbeb3a8SPaul E. McKenney 781fbeb3a8SPaul E. McKenney // Wait until there are multiple CPUs before starting test. 791fbeb3a8SPaul E. McKenney torture_param(int, holdoff, IS_BUILTIN(CONFIG_RCU_REF_SCALE_TEST) ? 10 : 0, 801fbeb3a8SPaul E. McKenney "Holdoff time before test start (s)"); 81a6889becSPaul E. McKenney // Number of typesafe_lookup structures, that is, the degree of concurrency. 82a6889becSPaul E. McKenney torture_param(long, lookup_instances, 0, "Number of typesafe_lookup structures."); 831fbeb3a8SPaul E. McKenney // Number of loops per experiment, all readers execute operations concurrently. 841fbeb3a8SPaul E. McKenney torture_param(long, loops, 10000, "Number of loops per experiment."); 851fbeb3a8SPaul E. McKenney // Number of readers, with -1 defaulting to about 75% of the CPUs. 861fbeb3a8SPaul E. McKenney torture_param(int, nreaders, -1, "Number of readers, -1 for 75% of CPUs."); 871fbeb3a8SPaul E. McKenney // Number of runs. 881fbeb3a8SPaul E. McKenney torture_param(int, nruns, 30, "Number of experiments to run."); 891fbeb3a8SPaul E. McKenney // Reader delay in nanoseconds, 0 for no delay. 901fbeb3a8SPaul E. McKenney torture_param(int, readdelay, 0, "Read-side delay in nanoseconds."); 911fbeb3a8SPaul E. McKenney 921fbeb3a8SPaul E. McKenney #ifdef MODULE 931fbeb3a8SPaul E. McKenney # define REFSCALE_SHUTDOWN 0 941fbeb3a8SPaul E. McKenney #else 951fbeb3a8SPaul E. McKenney # define REFSCALE_SHUTDOWN 1 961fbeb3a8SPaul E. McKenney #endif 971fbeb3a8SPaul E. McKenney 981fbeb3a8SPaul E. McKenney torture_param(bool, shutdown, REFSCALE_SHUTDOWN, 991fbeb3a8SPaul E. McKenney "Shutdown at end of scalability tests."); 1001fbeb3a8SPaul E. McKenney 1011fbeb3a8SPaul E. McKenney struct reader_task { 1021fbeb3a8SPaul E. McKenney struct task_struct *task; 1031fbeb3a8SPaul E. McKenney int start_reader; 1041fbeb3a8SPaul E. McKenney wait_queue_head_t wq; 1051fbeb3a8SPaul E. McKenney u64 last_duration_ns; 1061fbeb3a8SPaul E. McKenney }; 1071fbeb3a8SPaul E. McKenney 1081fbeb3a8SPaul E. McKenney static struct task_struct *shutdown_task; 1091fbeb3a8SPaul E. McKenney static wait_queue_head_t shutdown_wq; 1101fbeb3a8SPaul E. McKenney 1111fbeb3a8SPaul E. McKenney static struct task_struct *main_task; 1121fbeb3a8SPaul E. McKenney static wait_queue_head_t main_wq; 1131fbeb3a8SPaul E. McKenney static int shutdown_start; 1141fbeb3a8SPaul E. McKenney 1151fbeb3a8SPaul E. McKenney static struct reader_task *reader_tasks; 1161fbeb3a8SPaul E. McKenney 1171fbeb3a8SPaul E. McKenney // Number of readers that are part of the current experiment. 1181fbeb3a8SPaul E. McKenney static atomic_t nreaders_exp; 1191fbeb3a8SPaul E. McKenney 1201fbeb3a8SPaul E. McKenney // Use to wait for all threads to start. 1211fbeb3a8SPaul E. McKenney static atomic_t n_init; 1221fbeb3a8SPaul E. McKenney static atomic_t n_started; 1231fbeb3a8SPaul E. McKenney static atomic_t n_warmedup; 1241fbeb3a8SPaul E. McKenney static atomic_t n_cooleddown; 1251fbeb3a8SPaul E. McKenney 1261fbeb3a8SPaul E. McKenney // Track which experiment is currently running. 1271fbeb3a8SPaul E. McKenney static int exp_idx; 1281fbeb3a8SPaul E. McKenney 1291fbeb3a8SPaul E. McKenney // Operations vector for selecting different types of tests. 1301fbeb3a8SPaul E. McKenney struct ref_scale_ops { 1313c6496c8SPaul E. McKenney bool (*init)(void); 1321fbeb3a8SPaul E. McKenney void (*cleanup)(void); 1331fbeb3a8SPaul E. McKenney void (*readsection)(const int nloops); 1341fbeb3a8SPaul E. McKenney void (*delaysection)(const int nloops, const int udl, const int ndl); 1351fbeb3a8SPaul E. McKenney const char *name; 1361fbeb3a8SPaul E. McKenney }; 1371fbeb3a8SPaul E. McKenney 1388f35fefaSChristophe JAILLET static const struct ref_scale_ops *cur_ops; 1391fbeb3a8SPaul E. McKenney 1401fbeb3a8SPaul E. McKenney static void un_delay(const int udl, const int ndl) 1411fbeb3a8SPaul E. McKenney { 1421fbeb3a8SPaul E. McKenney if (udl) 1431fbeb3a8SPaul E. McKenney udelay(udl); 1441fbeb3a8SPaul E. McKenney if (ndl) 1451fbeb3a8SPaul E. McKenney ndelay(ndl); 1461fbeb3a8SPaul E. McKenney } 1471fbeb3a8SPaul E. McKenney 1481fbeb3a8SPaul E. McKenney static void ref_rcu_read_section(const int nloops) 1491fbeb3a8SPaul E. McKenney { 1501fbeb3a8SPaul E. McKenney int i; 1511fbeb3a8SPaul E. McKenney 1521fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 1531fbeb3a8SPaul E. McKenney rcu_read_lock(); 1541fbeb3a8SPaul E. McKenney rcu_read_unlock(); 1551fbeb3a8SPaul E. McKenney } 1561fbeb3a8SPaul E. McKenney } 1571fbeb3a8SPaul E. McKenney 1581fbeb3a8SPaul E. McKenney static void ref_rcu_delay_section(const int nloops, const int udl, const int ndl) 1591fbeb3a8SPaul E. McKenney { 1601fbeb3a8SPaul E. McKenney int i; 1611fbeb3a8SPaul E. McKenney 1621fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 1631fbeb3a8SPaul E. McKenney rcu_read_lock(); 1641fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 1651fbeb3a8SPaul E. McKenney rcu_read_unlock(); 1661fbeb3a8SPaul E. McKenney } 1671fbeb3a8SPaul E. McKenney } 1681fbeb3a8SPaul E. McKenney 1693c6496c8SPaul E. McKenney static bool rcu_sync_scale_init(void) 1701fbeb3a8SPaul E. McKenney { 1713c6496c8SPaul E. McKenney return true; 1721fbeb3a8SPaul E. McKenney } 1731fbeb3a8SPaul E. McKenney 1748f35fefaSChristophe JAILLET static const struct ref_scale_ops rcu_ops = { 1751fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 1761fbeb3a8SPaul E. McKenney .readsection = ref_rcu_read_section, 1771fbeb3a8SPaul E. McKenney .delaysection = ref_rcu_delay_section, 1781fbeb3a8SPaul E. McKenney .name = "rcu" 1791fbeb3a8SPaul E. McKenney }; 1801fbeb3a8SPaul E. McKenney 1811fbeb3a8SPaul E. McKenney // Definitions for SRCU ref scale testing. 1821fbeb3a8SPaul E. McKenney DEFINE_STATIC_SRCU(srcu_refctl_scale); 1831fbeb3a8SPaul E. McKenney static struct srcu_struct *srcu_ctlp = &srcu_refctl_scale; 1841fbeb3a8SPaul E. McKenney 1851fbeb3a8SPaul E. McKenney static void srcu_ref_scale_read_section(const int nloops) 1861fbeb3a8SPaul E. McKenney { 1871fbeb3a8SPaul E. McKenney int i; 1881fbeb3a8SPaul E. McKenney int idx; 1891fbeb3a8SPaul E. McKenney 1901fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 1911fbeb3a8SPaul E. McKenney idx = srcu_read_lock(srcu_ctlp); 1921fbeb3a8SPaul E. McKenney srcu_read_unlock(srcu_ctlp, idx); 1931fbeb3a8SPaul E. McKenney } 1941fbeb3a8SPaul E. McKenney } 1951fbeb3a8SPaul E. McKenney 1961fbeb3a8SPaul E. McKenney static void srcu_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 1971fbeb3a8SPaul E. McKenney { 1981fbeb3a8SPaul E. McKenney int i; 1991fbeb3a8SPaul E. McKenney int idx; 2001fbeb3a8SPaul E. McKenney 2011fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 2021fbeb3a8SPaul E. McKenney idx = srcu_read_lock(srcu_ctlp); 2031fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 2041fbeb3a8SPaul E. McKenney srcu_read_unlock(srcu_ctlp, idx); 2051fbeb3a8SPaul E. McKenney } 2061fbeb3a8SPaul E. McKenney } 2071fbeb3a8SPaul E. McKenney 2088f35fefaSChristophe JAILLET static const struct ref_scale_ops srcu_ops = { 2091fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 2101fbeb3a8SPaul E. McKenney .readsection = srcu_ref_scale_read_section, 2111fbeb3a8SPaul E. McKenney .delaysection = srcu_ref_scale_delay_section, 2121fbeb3a8SPaul E. McKenney .name = "srcu" 2131fbeb3a8SPaul E. McKenney }; 2141fbeb3a8SPaul E. McKenney 215*6a2c0255SPaul E. McKenney static void srcu_lite_ref_scale_read_section(const int nloops) 216*6a2c0255SPaul E. McKenney { 217*6a2c0255SPaul E. McKenney int i; 218*6a2c0255SPaul E. McKenney int idx; 219*6a2c0255SPaul E. McKenney 220*6a2c0255SPaul E. McKenney for (i = nloops; i >= 0; i--) { 221*6a2c0255SPaul E. McKenney idx = srcu_read_lock_lite(srcu_ctlp); 222*6a2c0255SPaul E. McKenney srcu_read_unlock_lite(srcu_ctlp, idx); 223*6a2c0255SPaul E. McKenney } 224*6a2c0255SPaul E. McKenney } 225*6a2c0255SPaul E. McKenney 226*6a2c0255SPaul E. McKenney static void srcu_lite_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 227*6a2c0255SPaul E. McKenney { 228*6a2c0255SPaul E. McKenney int i; 229*6a2c0255SPaul E. McKenney int idx; 230*6a2c0255SPaul E. McKenney 231*6a2c0255SPaul E. McKenney for (i = nloops; i >= 0; i--) { 232*6a2c0255SPaul E. McKenney idx = srcu_read_lock_lite(srcu_ctlp); 233*6a2c0255SPaul E. McKenney un_delay(udl, ndl); 234*6a2c0255SPaul E. McKenney srcu_read_unlock_lite(srcu_ctlp, idx); 235*6a2c0255SPaul E. McKenney } 236*6a2c0255SPaul E. McKenney } 237*6a2c0255SPaul E. McKenney 238*6a2c0255SPaul E. McKenney static const struct ref_scale_ops srcu_lite_ops = { 239*6a2c0255SPaul E. McKenney .init = rcu_sync_scale_init, 240*6a2c0255SPaul E. McKenney .readsection = srcu_lite_ref_scale_read_section, 241*6a2c0255SPaul E. McKenney .delaysection = srcu_lite_ref_scale_delay_section, 242*6a2c0255SPaul E. McKenney .name = "srcu-lite" 243*6a2c0255SPaul E. McKenney }; 244*6a2c0255SPaul E. McKenney 2455f654af1SPaul E. McKenney #ifdef CONFIG_TASKS_RCU 2465f654af1SPaul E. McKenney 2471fbeb3a8SPaul E. McKenney // Definitions for RCU Tasks ref scale testing: Empty read markers. 2481fbeb3a8SPaul E. McKenney // These definitions also work for RCU Rude readers. 2491fbeb3a8SPaul E. McKenney static void rcu_tasks_ref_scale_read_section(const int nloops) 2501fbeb3a8SPaul E. McKenney { 2511fbeb3a8SPaul E. McKenney int i; 2521fbeb3a8SPaul E. McKenney 2531fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) 2541fbeb3a8SPaul E. McKenney continue; 2551fbeb3a8SPaul E. McKenney } 2561fbeb3a8SPaul E. McKenney 2571fbeb3a8SPaul E. McKenney static void rcu_tasks_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 2581fbeb3a8SPaul E. McKenney { 2591fbeb3a8SPaul E. McKenney int i; 2601fbeb3a8SPaul E. McKenney 2611fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) 2621fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 2631fbeb3a8SPaul E. McKenney } 2641fbeb3a8SPaul E. McKenney 2658f35fefaSChristophe JAILLET static const struct ref_scale_ops rcu_tasks_ops = { 2661fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 2671fbeb3a8SPaul E. McKenney .readsection = rcu_tasks_ref_scale_read_section, 2681fbeb3a8SPaul E. McKenney .delaysection = rcu_tasks_ref_scale_delay_section, 2691fbeb3a8SPaul E. McKenney .name = "rcu-tasks" 2701fbeb3a8SPaul E. McKenney }; 2711fbeb3a8SPaul E. McKenney 2725f654af1SPaul E. McKenney #define RCU_TASKS_OPS &rcu_tasks_ops, 2735f654af1SPaul E. McKenney 2745f654af1SPaul E. McKenney #else // #ifdef CONFIG_TASKS_RCU 2755f654af1SPaul E. McKenney 2765f654af1SPaul E. McKenney #define RCU_TASKS_OPS 2775f654af1SPaul E. McKenney 2785f654af1SPaul E. McKenney #endif // #else // #ifdef CONFIG_TASKS_RCU 2795f654af1SPaul E. McKenney 280dec86781SPaul E. McKenney #ifdef CONFIG_TASKS_TRACE_RCU 281dec86781SPaul E. McKenney 2821fbeb3a8SPaul E. McKenney // Definitions for RCU Tasks Trace ref scale testing. 2831fbeb3a8SPaul E. McKenney static void rcu_trace_ref_scale_read_section(const int nloops) 2841fbeb3a8SPaul E. McKenney { 2851fbeb3a8SPaul E. McKenney int i; 2861fbeb3a8SPaul E. McKenney 2871fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 2881fbeb3a8SPaul E. McKenney rcu_read_lock_trace(); 2891fbeb3a8SPaul E. McKenney rcu_read_unlock_trace(); 2901fbeb3a8SPaul E. McKenney } 2911fbeb3a8SPaul E. McKenney } 2921fbeb3a8SPaul E. McKenney 2931fbeb3a8SPaul E. McKenney static void rcu_trace_ref_scale_delay_section(const int nloops, const int udl, const int ndl) 2941fbeb3a8SPaul E. McKenney { 2951fbeb3a8SPaul E. McKenney int i; 2961fbeb3a8SPaul E. McKenney 2971fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 2981fbeb3a8SPaul E. McKenney rcu_read_lock_trace(); 2991fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 3001fbeb3a8SPaul E. McKenney rcu_read_unlock_trace(); 3011fbeb3a8SPaul E. McKenney } 3021fbeb3a8SPaul E. McKenney } 3031fbeb3a8SPaul E. McKenney 3048f35fefaSChristophe JAILLET static const struct ref_scale_ops rcu_trace_ops = { 3051fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 3061fbeb3a8SPaul E. McKenney .readsection = rcu_trace_ref_scale_read_section, 3071fbeb3a8SPaul E. McKenney .delaysection = rcu_trace_ref_scale_delay_section, 3081fbeb3a8SPaul E. McKenney .name = "rcu-trace" 3091fbeb3a8SPaul E. McKenney }; 3101fbeb3a8SPaul E. McKenney 311dec86781SPaul E. McKenney #define RCU_TRACE_OPS &rcu_trace_ops, 312dec86781SPaul E. McKenney 313dec86781SPaul E. McKenney #else // #ifdef CONFIG_TASKS_TRACE_RCU 314dec86781SPaul E. McKenney 315dec86781SPaul E. McKenney #define RCU_TRACE_OPS 316dec86781SPaul E. McKenney 317dec86781SPaul E. McKenney #endif // #else // #ifdef CONFIG_TASKS_TRACE_RCU 318dec86781SPaul E. McKenney 3191fbeb3a8SPaul E. McKenney // Definitions for reference count 3201fbeb3a8SPaul E. McKenney static atomic_t refcnt; 3211fbeb3a8SPaul E. McKenney 3221fbeb3a8SPaul E. McKenney static void ref_refcnt_section(const int nloops) 3231fbeb3a8SPaul E. McKenney { 3241fbeb3a8SPaul E. McKenney int i; 3251fbeb3a8SPaul E. McKenney 3261fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 3271fbeb3a8SPaul E. McKenney atomic_inc(&refcnt); 3281fbeb3a8SPaul E. McKenney atomic_dec(&refcnt); 3291fbeb3a8SPaul E. McKenney } 3301fbeb3a8SPaul E. McKenney } 3311fbeb3a8SPaul E. McKenney 3321fbeb3a8SPaul E. McKenney static void ref_refcnt_delay_section(const int nloops, const int udl, const int ndl) 3331fbeb3a8SPaul E. McKenney { 3341fbeb3a8SPaul E. McKenney int i; 3351fbeb3a8SPaul E. McKenney 3361fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 3371fbeb3a8SPaul E. McKenney atomic_inc(&refcnt); 3381fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 3391fbeb3a8SPaul E. McKenney atomic_dec(&refcnt); 3401fbeb3a8SPaul E. McKenney } 3411fbeb3a8SPaul E. McKenney } 3421fbeb3a8SPaul E. McKenney 3438f35fefaSChristophe JAILLET static const struct ref_scale_ops refcnt_ops = { 3441fbeb3a8SPaul E. McKenney .init = rcu_sync_scale_init, 3451fbeb3a8SPaul E. McKenney .readsection = ref_refcnt_section, 3461fbeb3a8SPaul E. McKenney .delaysection = ref_refcnt_delay_section, 3471fbeb3a8SPaul E. McKenney .name = "refcnt" 3481fbeb3a8SPaul E. McKenney }; 3491fbeb3a8SPaul E. McKenney 3501fbeb3a8SPaul E. McKenney // Definitions for rwlock 3511fbeb3a8SPaul E. McKenney static rwlock_t test_rwlock; 3521fbeb3a8SPaul E. McKenney 3533c6496c8SPaul E. McKenney static bool ref_rwlock_init(void) 3541fbeb3a8SPaul E. McKenney { 3551fbeb3a8SPaul E. McKenney rwlock_init(&test_rwlock); 3563c6496c8SPaul E. McKenney return true; 3571fbeb3a8SPaul E. McKenney } 3581fbeb3a8SPaul E. McKenney 3591fbeb3a8SPaul E. McKenney static void ref_rwlock_section(const int nloops) 3601fbeb3a8SPaul E. McKenney { 3611fbeb3a8SPaul E. McKenney int i; 3621fbeb3a8SPaul E. McKenney 3631fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 3641fbeb3a8SPaul E. McKenney read_lock(&test_rwlock); 3651fbeb3a8SPaul E. McKenney read_unlock(&test_rwlock); 3661fbeb3a8SPaul E. McKenney } 3671fbeb3a8SPaul E. McKenney } 3681fbeb3a8SPaul E. McKenney 3691fbeb3a8SPaul E. McKenney static void ref_rwlock_delay_section(const int nloops, const int udl, const int ndl) 3701fbeb3a8SPaul E. McKenney { 3711fbeb3a8SPaul E. McKenney int i; 3721fbeb3a8SPaul E. McKenney 3731fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 3741fbeb3a8SPaul E. McKenney read_lock(&test_rwlock); 3751fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 3761fbeb3a8SPaul E. McKenney read_unlock(&test_rwlock); 3771fbeb3a8SPaul E. McKenney } 3781fbeb3a8SPaul E. McKenney } 3791fbeb3a8SPaul E. McKenney 3808f35fefaSChristophe JAILLET static const struct ref_scale_ops rwlock_ops = { 3811fbeb3a8SPaul E. McKenney .init = ref_rwlock_init, 3821fbeb3a8SPaul E. McKenney .readsection = ref_rwlock_section, 3831fbeb3a8SPaul E. McKenney .delaysection = ref_rwlock_delay_section, 3841fbeb3a8SPaul E. McKenney .name = "rwlock" 3851fbeb3a8SPaul E. McKenney }; 3861fbeb3a8SPaul E. McKenney 3871fbeb3a8SPaul E. McKenney // Definitions for rwsem 3881fbeb3a8SPaul E. McKenney static struct rw_semaphore test_rwsem; 3891fbeb3a8SPaul E. McKenney 3903c6496c8SPaul E. McKenney static bool ref_rwsem_init(void) 3911fbeb3a8SPaul E. McKenney { 3921fbeb3a8SPaul E. McKenney init_rwsem(&test_rwsem); 3933c6496c8SPaul E. McKenney return true; 3941fbeb3a8SPaul E. McKenney } 3951fbeb3a8SPaul E. McKenney 3961fbeb3a8SPaul E. McKenney static void ref_rwsem_section(const int nloops) 3971fbeb3a8SPaul E. McKenney { 3981fbeb3a8SPaul E. McKenney int i; 3991fbeb3a8SPaul E. McKenney 4001fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 4011fbeb3a8SPaul E. McKenney down_read(&test_rwsem); 4021fbeb3a8SPaul E. McKenney up_read(&test_rwsem); 4031fbeb3a8SPaul E. McKenney } 4041fbeb3a8SPaul E. McKenney } 4051fbeb3a8SPaul E. McKenney 4061fbeb3a8SPaul E. McKenney static void ref_rwsem_delay_section(const int nloops, const int udl, const int ndl) 4071fbeb3a8SPaul E. McKenney { 4081fbeb3a8SPaul E. McKenney int i; 4091fbeb3a8SPaul E. McKenney 4101fbeb3a8SPaul E. McKenney for (i = nloops; i >= 0; i--) { 4111fbeb3a8SPaul E. McKenney down_read(&test_rwsem); 4121fbeb3a8SPaul E. McKenney un_delay(udl, ndl); 4131fbeb3a8SPaul E. McKenney up_read(&test_rwsem); 4141fbeb3a8SPaul E. McKenney } 4151fbeb3a8SPaul E. McKenney } 4161fbeb3a8SPaul E. McKenney 4178f35fefaSChristophe JAILLET static const struct ref_scale_ops rwsem_ops = { 4181fbeb3a8SPaul E. McKenney .init = ref_rwsem_init, 4191fbeb3a8SPaul E. McKenney .readsection = ref_rwsem_section, 4201fbeb3a8SPaul E. McKenney .delaysection = ref_rwsem_delay_section, 4211fbeb3a8SPaul E. McKenney .name = "rwsem" 4221fbeb3a8SPaul E. McKenney }; 4231fbeb3a8SPaul E. McKenney 424e9b800dbSPaul E. McKenney // Definitions for global spinlock 4257bf336fbSZqiang static DEFINE_RAW_SPINLOCK(test_lock); 426e9b800dbSPaul E. McKenney 427e9b800dbSPaul E. McKenney static void ref_lock_section(const int nloops) 428e9b800dbSPaul E. McKenney { 429e9b800dbSPaul E. McKenney int i; 430e9b800dbSPaul E. McKenney 431e9b800dbSPaul E. McKenney preempt_disable(); 432e9b800dbSPaul E. McKenney for (i = nloops; i >= 0; i--) { 4337bf336fbSZqiang raw_spin_lock(&test_lock); 4347bf336fbSZqiang raw_spin_unlock(&test_lock); 435e9b800dbSPaul E. McKenney } 436e9b800dbSPaul E. McKenney preempt_enable(); 437e9b800dbSPaul E. McKenney } 438e9b800dbSPaul E. McKenney 439e9b800dbSPaul E. McKenney static void ref_lock_delay_section(const int nloops, const int udl, const int ndl) 440e9b800dbSPaul E. McKenney { 441e9b800dbSPaul E. McKenney int i; 442e9b800dbSPaul E. McKenney 443e9b800dbSPaul E. McKenney preempt_disable(); 444e9b800dbSPaul E. McKenney for (i = nloops; i >= 0; i--) { 4457bf336fbSZqiang raw_spin_lock(&test_lock); 446e9b800dbSPaul E. McKenney un_delay(udl, ndl); 4477bf336fbSZqiang raw_spin_unlock(&test_lock); 448e9b800dbSPaul E. McKenney } 449e9b800dbSPaul E. McKenney preempt_enable(); 450e9b800dbSPaul E. McKenney } 451e9b800dbSPaul E. McKenney 4528f35fefaSChristophe JAILLET static const struct ref_scale_ops lock_ops = { 453e9b800dbSPaul E. McKenney .readsection = ref_lock_section, 454e9b800dbSPaul E. McKenney .delaysection = ref_lock_delay_section, 455e9b800dbSPaul E. McKenney .name = "lock" 456e9b800dbSPaul E. McKenney }; 457e9b800dbSPaul E. McKenney 458e9b800dbSPaul E. McKenney // Definitions for global irq-save spinlock 459e9b800dbSPaul E. McKenney 460e9b800dbSPaul E. McKenney static void ref_lock_irq_section(const int nloops) 461e9b800dbSPaul E. McKenney { 462e9b800dbSPaul E. McKenney unsigned long flags; 463e9b800dbSPaul E. McKenney int i; 464e9b800dbSPaul E. McKenney 465e9b800dbSPaul E. McKenney preempt_disable(); 466e9b800dbSPaul E. McKenney for (i = nloops; i >= 0; i--) { 4677bf336fbSZqiang raw_spin_lock_irqsave(&test_lock, flags); 4687bf336fbSZqiang raw_spin_unlock_irqrestore(&test_lock, flags); 469e9b800dbSPaul E. McKenney } 470e9b800dbSPaul E. McKenney preempt_enable(); 471e9b800dbSPaul E. McKenney } 472e9b800dbSPaul E. McKenney 473e9b800dbSPaul E. McKenney static void ref_lock_irq_delay_section(const int nloops, const int udl, const int ndl) 474e9b800dbSPaul E. McKenney { 475e9b800dbSPaul E. McKenney unsigned long flags; 476e9b800dbSPaul E. McKenney int i; 477e9b800dbSPaul E. McKenney 478e9b800dbSPaul E. McKenney preempt_disable(); 479e9b800dbSPaul E. McKenney for (i = nloops; i >= 0; i--) { 4807bf336fbSZqiang raw_spin_lock_irqsave(&test_lock, flags); 481e9b800dbSPaul E. McKenney un_delay(udl, ndl); 4827bf336fbSZqiang raw_spin_unlock_irqrestore(&test_lock, flags); 483e9b800dbSPaul E. McKenney } 484e9b800dbSPaul E. McKenney preempt_enable(); 485e9b800dbSPaul E. McKenney } 486e9b800dbSPaul E. McKenney 4878f35fefaSChristophe JAILLET static const struct ref_scale_ops lock_irq_ops = { 488e9b800dbSPaul E. McKenney .readsection = ref_lock_irq_section, 489e9b800dbSPaul E. McKenney .delaysection = ref_lock_irq_delay_section, 490e9b800dbSPaul E. McKenney .name = "lock-irq" 491e9b800dbSPaul E. McKenney }; 492e9b800dbSPaul E. McKenney 493e9b800dbSPaul E. McKenney // Definitions acquire-release. 494e9b800dbSPaul E. McKenney static DEFINE_PER_CPU(unsigned long, test_acqrel); 495e9b800dbSPaul E. McKenney 496e9b800dbSPaul E. McKenney static void ref_acqrel_section(const int nloops) 497e9b800dbSPaul E. McKenney { 498e9b800dbSPaul E. McKenney unsigned long x; 499e9b800dbSPaul E. McKenney int i; 500e9b800dbSPaul E. McKenney 501e9b800dbSPaul E. McKenney preempt_disable(); 502e9b800dbSPaul E. McKenney for (i = nloops; i >= 0; i--) { 503e9b800dbSPaul E. McKenney x = smp_load_acquire(this_cpu_ptr(&test_acqrel)); 504e9b800dbSPaul E. McKenney smp_store_release(this_cpu_ptr(&test_acqrel), x + 1); 505e9b800dbSPaul E. McKenney } 506e9b800dbSPaul E. McKenney preempt_enable(); 507e9b800dbSPaul E. McKenney } 508e9b800dbSPaul E. McKenney 509e9b800dbSPaul E. McKenney static void ref_acqrel_delay_section(const int nloops, const int udl, const int ndl) 510e9b800dbSPaul E. McKenney { 511e9b800dbSPaul E. McKenney unsigned long x; 512e9b800dbSPaul E. McKenney int i; 513e9b800dbSPaul E. McKenney 514e9b800dbSPaul E. McKenney preempt_disable(); 515e9b800dbSPaul E. McKenney for (i = nloops; i >= 0; i--) { 516e9b800dbSPaul E. McKenney x = smp_load_acquire(this_cpu_ptr(&test_acqrel)); 517e9b800dbSPaul E. McKenney un_delay(udl, ndl); 518e9b800dbSPaul E. McKenney smp_store_release(this_cpu_ptr(&test_acqrel), x + 1); 519e9b800dbSPaul E. McKenney } 520e9b800dbSPaul E. McKenney preempt_enable(); 521e9b800dbSPaul E. McKenney } 522e9b800dbSPaul E. McKenney 5238f35fefaSChristophe JAILLET static const struct ref_scale_ops acqrel_ops = { 524e9b800dbSPaul E. McKenney .readsection = ref_acqrel_section, 525e9b800dbSPaul E. McKenney .delaysection = ref_acqrel_delay_section, 526e9b800dbSPaul E. McKenney .name = "acqrel" 527e9b800dbSPaul E. McKenney }; 528e9b800dbSPaul E. McKenney 52925f6fa53SPaul E. McKenney static volatile u64 stopopts; 53025f6fa53SPaul E. McKenney 53125f6fa53SPaul E. McKenney static void ref_clock_section(const int nloops) 53225f6fa53SPaul E. McKenney { 53325f6fa53SPaul E. McKenney u64 x = 0; 53425f6fa53SPaul E. McKenney int i; 53525f6fa53SPaul E. McKenney 53625f6fa53SPaul E. McKenney preempt_disable(); 53725f6fa53SPaul E. McKenney for (i = nloops; i >= 0; i--) 53825f6fa53SPaul E. McKenney x += ktime_get_real_fast_ns(); 53925f6fa53SPaul E. McKenney preempt_enable(); 54025f6fa53SPaul E. McKenney stopopts = x; 54125f6fa53SPaul E. McKenney } 54225f6fa53SPaul E. McKenney 54325f6fa53SPaul E. McKenney static void ref_clock_delay_section(const int nloops, const int udl, const int ndl) 54425f6fa53SPaul E. McKenney { 54525f6fa53SPaul E. McKenney u64 x = 0; 54625f6fa53SPaul E. McKenney int i; 54725f6fa53SPaul E. McKenney 54825f6fa53SPaul E. McKenney preempt_disable(); 54925f6fa53SPaul E. McKenney for (i = nloops; i >= 0; i--) { 55025f6fa53SPaul E. McKenney x += ktime_get_real_fast_ns(); 55125f6fa53SPaul E. McKenney un_delay(udl, ndl); 55225f6fa53SPaul E. McKenney } 55325f6fa53SPaul E. McKenney preempt_enable(); 55425f6fa53SPaul E. McKenney stopopts = x; 55525f6fa53SPaul E. McKenney } 55625f6fa53SPaul E. McKenney 5578f35fefaSChristophe JAILLET static const struct ref_scale_ops clock_ops = { 55825f6fa53SPaul E. McKenney .readsection = ref_clock_section, 55925f6fa53SPaul E. McKenney .delaysection = ref_clock_delay_section, 56025f6fa53SPaul E. McKenney .name = "clock" 56125f6fa53SPaul E. McKenney }; 56225f6fa53SPaul E. McKenney 563b5a2801fSPaul E. McKenney static void ref_jiffies_section(const int nloops) 564b5a2801fSPaul E. McKenney { 565b5a2801fSPaul E. McKenney u64 x = 0; 566b5a2801fSPaul E. McKenney int i; 567b5a2801fSPaul E. McKenney 568b5a2801fSPaul E. McKenney preempt_disable(); 569b5a2801fSPaul E. McKenney for (i = nloops; i >= 0; i--) 570b5a2801fSPaul E. McKenney x += jiffies; 571b5a2801fSPaul E. McKenney preempt_enable(); 572b5a2801fSPaul E. McKenney stopopts = x; 573b5a2801fSPaul E. McKenney } 574b5a2801fSPaul E. McKenney 575b5a2801fSPaul E. McKenney static void ref_jiffies_delay_section(const int nloops, const int udl, const int ndl) 576b5a2801fSPaul E. McKenney { 577b5a2801fSPaul E. McKenney u64 x = 0; 578b5a2801fSPaul E. McKenney int i; 579b5a2801fSPaul E. McKenney 580b5a2801fSPaul E. McKenney preempt_disable(); 581b5a2801fSPaul E. McKenney for (i = nloops; i >= 0; i--) { 582b5a2801fSPaul E. McKenney x += jiffies; 583b5a2801fSPaul E. McKenney un_delay(udl, ndl); 584b5a2801fSPaul E. McKenney } 585b5a2801fSPaul E. McKenney preempt_enable(); 586b5a2801fSPaul E. McKenney stopopts = x; 587b5a2801fSPaul E. McKenney } 588b5a2801fSPaul E. McKenney 5898f35fefaSChristophe JAILLET static const struct ref_scale_ops jiffies_ops = { 590b5a2801fSPaul E. McKenney .readsection = ref_jiffies_section, 591b5a2801fSPaul E. McKenney .delaysection = ref_jiffies_delay_section, 592b5a2801fSPaul E. McKenney .name = "jiffies" 593b5a2801fSPaul E. McKenney }; 594b5a2801fSPaul E. McKenney 595a6889becSPaul E. McKenney //////////////////////////////////////////////////////////////////////// 596a6889becSPaul E. McKenney // 597a6889becSPaul E. McKenney // Methods leveraging SLAB_TYPESAFE_BY_RCU. 598a6889becSPaul E. McKenney // 599a6889becSPaul E. McKenney 600a6889becSPaul E. McKenney // Item to look up in a typesafe manner. Array of pointers to these. 601a6889becSPaul E. McKenney struct refscale_typesafe { 602a6889becSPaul E. McKenney atomic_t rts_refctr; // Used by all flavors 603a6889becSPaul E. McKenney spinlock_t rts_lock; 604a6889becSPaul E. McKenney seqlock_t rts_seqlock; 605a6889becSPaul E. McKenney unsigned int a; 606a6889becSPaul E. McKenney unsigned int b; 607a6889becSPaul E. McKenney }; 608a6889becSPaul E. McKenney 609a6889becSPaul E. McKenney static struct kmem_cache *typesafe_kmem_cachep; 610a6889becSPaul E. McKenney static struct refscale_typesafe **rtsarray; 611a6889becSPaul E. McKenney static long rtsarray_size; 612a6889becSPaul E. McKenney static DEFINE_TORTURE_RANDOM_PERCPU(refscale_rand); 613a6889becSPaul E. McKenney static bool (*rts_acquire)(struct refscale_typesafe *rtsp, unsigned int *start); 614a6889becSPaul E. McKenney static bool (*rts_release)(struct refscale_typesafe *rtsp, unsigned int start); 615a6889becSPaul E. McKenney 616a6889becSPaul E. McKenney // Conditionally acquire an explicit in-structure reference count. 617a6889becSPaul E. McKenney static bool typesafe_ref_acquire(struct refscale_typesafe *rtsp, unsigned int *start) 618a6889becSPaul E. McKenney { 619a6889becSPaul E. McKenney return atomic_inc_not_zero(&rtsp->rts_refctr); 620a6889becSPaul E. McKenney } 621a6889becSPaul E. McKenney 622a6889becSPaul E. McKenney // Unconditionally release an explicit in-structure reference count. 623a6889becSPaul E. McKenney static bool typesafe_ref_release(struct refscale_typesafe *rtsp, unsigned int start) 624a6889becSPaul E. McKenney { 625a6889becSPaul E. McKenney if (!atomic_dec_return(&rtsp->rts_refctr)) { 626a6889becSPaul E. McKenney WRITE_ONCE(rtsp->a, rtsp->a + 1); 627a6889becSPaul E. McKenney kmem_cache_free(typesafe_kmem_cachep, rtsp); 628a6889becSPaul E. McKenney } 629a6889becSPaul E. McKenney return true; 630a6889becSPaul E. McKenney } 631a6889becSPaul E. McKenney 632a6889becSPaul E. McKenney // Unconditionally acquire an explicit in-structure spinlock. 633a6889becSPaul E. McKenney static bool typesafe_lock_acquire(struct refscale_typesafe *rtsp, unsigned int *start) 634a6889becSPaul E. McKenney { 635a6889becSPaul E. McKenney spin_lock(&rtsp->rts_lock); 636a6889becSPaul E. McKenney return true; 637a6889becSPaul E. McKenney } 638a6889becSPaul E. McKenney 639a6889becSPaul E. McKenney // Unconditionally release an explicit in-structure spinlock. 640a6889becSPaul E. McKenney static bool typesafe_lock_release(struct refscale_typesafe *rtsp, unsigned int start) 641a6889becSPaul E. McKenney { 642a6889becSPaul E. McKenney spin_unlock(&rtsp->rts_lock); 643a6889becSPaul E. McKenney return true; 644a6889becSPaul E. McKenney } 645a6889becSPaul E. McKenney 646a6889becSPaul E. McKenney // Unconditionally acquire an explicit in-structure sequence lock. 647a6889becSPaul E. McKenney static bool typesafe_seqlock_acquire(struct refscale_typesafe *rtsp, unsigned int *start) 648a6889becSPaul E. McKenney { 649a6889becSPaul E. McKenney *start = read_seqbegin(&rtsp->rts_seqlock); 650a6889becSPaul E. McKenney return true; 651a6889becSPaul E. McKenney } 652a6889becSPaul E. McKenney 653a6889becSPaul E. McKenney // Conditionally release an explicit in-structure sequence lock. Return 654a6889becSPaul E. McKenney // true if this release was successful, that is, if no retry is required. 655a6889becSPaul E. McKenney static bool typesafe_seqlock_release(struct refscale_typesafe *rtsp, unsigned int start) 656a6889becSPaul E. McKenney { 657a6889becSPaul E. McKenney return !read_seqretry(&rtsp->rts_seqlock, start); 658a6889becSPaul E. McKenney } 659a6889becSPaul E. McKenney 660a6889becSPaul E. McKenney // Do a read-side critical section with the specified delay in 661a6889becSPaul E. McKenney // microseconds and nanoseconds inserted so as to increase probability 662a6889becSPaul E. McKenney // of failure. 663a6889becSPaul E. McKenney static void typesafe_delay_section(const int nloops, const int udl, const int ndl) 664a6889becSPaul E. McKenney { 665a6889becSPaul E. McKenney unsigned int a; 666a6889becSPaul E. McKenney unsigned int b; 667a6889becSPaul E. McKenney int i; 668a6889becSPaul E. McKenney long idx; 669a6889becSPaul E. McKenney struct refscale_typesafe *rtsp; 670a6889becSPaul E. McKenney unsigned int start; 671a6889becSPaul E. McKenney 672a6889becSPaul E. McKenney for (i = nloops; i >= 0; i--) { 673a6889becSPaul E. McKenney preempt_disable(); 674a6889becSPaul E. McKenney idx = torture_random(this_cpu_ptr(&refscale_rand)) % rtsarray_size; 675a6889becSPaul E. McKenney preempt_enable(); 676a6889becSPaul E. McKenney retry: 677a6889becSPaul E. McKenney rcu_read_lock(); 678a6889becSPaul E. McKenney rtsp = rcu_dereference(rtsarray[idx]); 679a6889becSPaul E. McKenney a = READ_ONCE(rtsp->a); 680a6889becSPaul E. McKenney if (!rts_acquire(rtsp, &start)) { 681a6889becSPaul E. McKenney rcu_read_unlock(); 682a6889becSPaul E. McKenney goto retry; 683a6889becSPaul E. McKenney } 684a6889becSPaul E. McKenney if (a != READ_ONCE(rtsp->a)) { 685a6889becSPaul E. McKenney (void)rts_release(rtsp, start); 686a6889becSPaul E. McKenney rcu_read_unlock(); 687a6889becSPaul E. McKenney goto retry; 688a6889becSPaul E. McKenney } 689a6889becSPaul E. McKenney un_delay(udl, ndl); 690730c3ed4SPaul E. McKenney b = READ_ONCE(rtsp->a); 691a6889becSPaul E. McKenney // Remember, seqlock read-side release can fail. 692a6889becSPaul E. McKenney if (!rts_release(rtsp, start)) { 693a6889becSPaul E. McKenney rcu_read_unlock(); 694a6889becSPaul E. McKenney goto retry; 695a6889becSPaul E. McKenney } 696a6889becSPaul E. McKenney WARN_ONCE(a != b, "Re-read of ->a changed from %u to %u.\n", a, b); 697a6889becSPaul E. McKenney b = rtsp->b; 698a6889becSPaul E. McKenney rcu_read_unlock(); 699a6889becSPaul E. McKenney WARN_ON_ONCE(a * a != b); 700a6889becSPaul E. McKenney } 701a6889becSPaul E. McKenney } 702a6889becSPaul E. McKenney 703a6889becSPaul E. McKenney // Because the acquisition and release methods are expensive, there 704a6889becSPaul E. McKenney // is no point in optimizing away the un_delay() function's two checks. 705a6889becSPaul E. McKenney // Thus simply define typesafe_read_section() as a simple wrapper around 706a6889becSPaul E. McKenney // typesafe_delay_section(). 707a6889becSPaul E. McKenney static void typesafe_read_section(const int nloops) 708a6889becSPaul E. McKenney { 709a6889becSPaul E. McKenney typesafe_delay_section(nloops, 0, 0); 710a6889becSPaul E. McKenney } 711a6889becSPaul E. McKenney 712a6889becSPaul E. McKenney // Allocate and initialize one refscale_typesafe structure. 713a6889becSPaul E. McKenney static struct refscale_typesafe *typesafe_alloc_one(void) 714a6889becSPaul E. McKenney { 715a6889becSPaul E. McKenney struct refscale_typesafe *rtsp; 716a6889becSPaul E. McKenney 717a6889becSPaul E. McKenney rtsp = kmem_cache_alloc(typesafe_kmem_cachep, GFP_KERNEL); 718a6889becSPaul E. McKenney if (!rtsp) 719a6889becSPaul E. McKenney return NULL; 720a6889becSPaul E. McKenney atomic_set(&rtsp->rts_refctr, 1); 721a6889becSPaul E. McKenney WRITE_ONCE(rtsp->a, rtsp->a + 1); 722a6889becSPaul E. McKenney WRITE_ONCE(rtsp->b, rtsp->a * rtsp->a); 723a6889becSPaul E. McKenney return rtsp; 724a6889becSPaul E. McKenney } 725a6889becSPaul E. McKenney 726a6889becSPaul E. McKenney // Slab-allocator constructor for refscale_typesafe structures created 727a6889becSPaul E. McKenney // out of a new slab of system memory. 728a6889becSPaul E. McKenney static void refscale_typesafe_ctor(void *rtsp_in) 729a6889becSPaul E. McKenney { 730a6889becSPaul E. McKenney struct refscale_typesafe *rtsp = rtsp_in; 731a6889becSPaul E. McKenney 732a6889becSPaul E. McKenney spin_lock_init(&rtsp->rts_lock); 733a6889becSPaul E. McKenney seqlock_init(&rtsp->rts_seqlock); 734a6889becSPaul E. McKenney preempt_disable(); 735a6889becSPaul E. McKenney rtsp->a = torture_random(this_cpu_ptr(&refscale_rand)); 736a6889becSPaul E. McKenney preempt_enable(); 737a6889becSPaul E. McKenney } 738a6889becSPaul E. McKenney 7398f35fefaSChristophe JAILLET static const struct ref_scale_ops typesafe_ref_ops; 7408f35fefaSChristophe JAILLET static const struct ref_scale_ops typesafe_lock_ops; 7418f35fefaSChristophe JAILLET static const struct ref_scale_ops typesafe_seqlock_ops; 742a6889becSPaul E. McKenney 743a6889becSPaul E. McKenney // Initialize for a typesafe test. 744a6889becSPaul E. McKenney static bool typesafe_init(void) 745a6889becSPaul E. McKenney { 746a6889becSPaul E. McKenney long idx; 747a6889becSPaul E. McKenney long si = lookup_instances; 748a6889becSPaul E. McKenney 749a6889becSPaul E. McKenney typesafe_kmem_cachep = kmem_cache_create("refscale_typesafe", 750a6889becSPaul E. McKenney sizeof(struct refscale_typesafe), sizeof(void *), 751a6889becSPaul E. McKenney SLAB_TYPESAFE_BY_RCU, refscale_typesafe_ctor); 752a6889becSPaul E. McKenney if (!typesafe_kmem_cachep) 753a6889becSPaul E. McKenney return false; 754a6889becSPaul E. McKenney if (si < 0) 755a6889becSPaul E. McKenney si = -si * nr_cpu_ids; 756a6889becSPaul E. McKenney else if (si == 0) 757a6889becSPaul E. McKenney si = nr_cpu_ids; 758a6889becSPaul E. McKenney rtsarray_size = si; 759a6889becSPaul E. McKenney rtsarray = kcalloc(si, sizeof(*rtsarray), GFP_KERNEL); 760a6889becSPaul E. McKenney if (!rtsarray) 761a6889becSPaul E. McKenney return false; 762a6889becSPaul E. McKenney for (idx = 0; idx < rtsarray_size; idx++) { 763a6889becSPaul E. McKenney rtsarray[idx] = typesafe_alloc_one(); 764a6889becSPaul E. McKenney if (!rtsarray[idx]) 765a6889becSPaul E. McKenney return false; 766a6889becSPaul E. McKenney } 767a6889becSPaul E. McKenney if (cur_ops == &typesafe_ref_ops) { 768a6889becSPaul E. McKenney rts_acquire = typesafe_ref_acquire; 769a6889becSPaul E. McKenney rts_release = typesafe_ref_release; 770a6889becSPaul E. McKenney } else if (cur_ops == &typesafe_lock_ops) { 771a6889becSPaul E. McKenney rts_acquire = typesafe_lock_acquire; 772a6889becSPaul E. McKenney rts_release = typesafe_lock_release; 773a6889becSPaul E. McKenney } else if (cur_ops == &typesafe_seqlock_ops) { 774a6889becSPaul E. McKenney rts_acquire = typesafe_seqlock_acquire; 775a6889becSPaul E. McKenney rts_release = typesafe_seqlock_release; 776a6889becSPaul E. McKenney } else { 777a6889becSPaul E. McKenney WARN_ON_ONCE(1); 778a6889becSPaul E. McKenney return false; 779a6889becSPaul E. McKenney } 780a6889becSPaul E. McKenney return true; 781a6889becSPaul E. McKenney } 782a6889becSPaul E. McKenney 783a6889becSPaul E. McKenney // Clean up after a typesafe test. 784a6889becSPaul E. McKenney static void typesafe_cleanup(void) 785a6889becSPaul E. McKenney { 786a6889becSPaul E. McKenney long idx; 787a6889becSPaul E. McKenney 788a6889becSPaul E. McKenney if (rtsarray) { 789a6889becSPaul E. McKenney for (idx = 0; idx < rtsarray_size; idx++) 790a6889becSPaul E. McKenney kmem_cache_free(typesafe_kmem_cachep, rtsarray[idx]); 791a6889becSPaul E. McKenney kfree(rtsarray); 792a6889becSPaul E. McKenney rtsarray = NULL; 793a6889becSPaul E. McKenney rtsarray_size = 0; 794a6889becSPaul E. McKenney } 795a6889becSPaul E. McKenney kmem_cache_destroy(typesafe_kmem_cachep); 796a6889becSPaul E. McKenney typesafe_kmem_cachep = NULL; 797a6889becSPaul E. McKenney rts_acquire = NULL; 798a6889becSPaul E. McKenney rts_release = NULL; 799a6889becSPaul E. McKenney } 800a6889becSPaul E. McKenney 801a6889becSPaul E. McKenney // The typesafe_init() function distinguishes these structures by address. 8028f35fefaSChristophe JAILLET static const struct ref_scale_ops typesafe_ref_ops = { 803a6889becSPaul E. McKenney .init = typesafe_init, 804a6889becSPaul E. McKenney .cleanup = typesafe_cleanup, 805a6889becSPaul E. McKenney .readsection = typesafe_read_section, 806a6889becSPaul E. McKenney .delaysection = typesafe_delay_section, 807a6889becSPaul E. McKenney .name = "typesafe_ref" 808a6889becSPaul E. McKenney }; 809a6889becSPaul E. McKenney 8108f35fefaSChristophe JAILLET static const struct ref_scale_ops typesafe_lock_ops = { 811a6889becSPaul E. McKenney .init = typesafe_init, 812a6889becSPaul E. McKenney .cleanup = typesafe_cleanup, 813a6889becSPaul E. McKenney .readsection = typesafe_read_section, 814a6889becSPaul E. McKenney .delaysection = typesafe_delay_section, 815a6889becSPaul E. McKenney .name = "typesafe_lock" 816a6889becSPaul E. McKenney }; 817a6889becSPaul E. McKenney 8188f35fefaSChristophe JAILLET static const struct ref_scale_ops typesafe_seqlock_ops = { 819a6889becSPaul E. McKenney .init = typesafe_init, 820a6889becSPaul E. McKenney .cleanup = typesafe_cleanup, 821a6889becSPaul E. McKenney .readsection = typesafe_read_section, 822a6889becSPaul E. McKenney .delaysection = typesafe_delay_section, 823a6889becSPaul E. McKenney .name = "typesafe_seqlock" 824a6889becSPaul E. McKenney }; 825a6889becSPaul E. McKenney 8261fbeb3a8SPaul E. McKenney static void rcu_scale_one_reader(void) 8271fbeb3a8SPaul E. McKenney { 8281fbeb3a8SPaul E. McKenney if (readdelay <= 0) 8291fbeb3a8SPaul E. McKenney cur_ops->readsection(loops); 8301fbeb3a8SPaul E. McKenney else 8311fbeb3a8SPaul E. McKenney cur_ops->delaysection(loops, readdelay / 1000, readdelay % 1000); 8321fbeb3a8SPaul E. McKenney } 8331fbeb3a8SPaul E. McKenney 8341fbeb3a8SPaul E. McKenney // Reader kthread. Repeatedly does empty RCU read-side 8351fbeb3a8SPaul E. McKenney // critical section, minimizing update-side interference. 8361fbeb3a8SPaul E. McKenney static int 8371fbeb3a8SPaul E. McKenney ref_scale_reader(void *arg) 8381fbeb3a8SPaul E. McKenney { 8391fbeb3a8SPaul E. McKenney unsigned long flags; 8401fbeb3a8SPaul E. McKenney long me = (long)arg; 8411fbeb3a8SPaul E. McKenney struct reader_task *rt = &(reader_tasks[me]); 8421fbeb3a8SPaul E. McKenney u64 start; 8431fbeb3a8SPaul E. McKenney s64 duration; 8441fbeb3a8SPaul E. McKenney 845e76506f0SPaul E. McKenney VERBOSE_SCALEOUT_BATCH("ref_scale_reader %ld: task started", me); 84605bc276cSPaul E. McKenney WARN_ON_ONCE(set_cpus_allowed_ptr(current, cpumask_of(me % nr_cpu_ids))); 8471fbeb3a8SPaul E. McKenney set_user_nice(current, MAX_NICE); 8481fbeb3a8SPaul E. McKenney atomic_inc(&n_init); 8491fbeb3a8SPaul E. McKenney if (holdoff) 8501fbeb3a8SPaul E. McKenney schedule_timeout_interruptible(holdoff * HZ); 8511fbeb3a8SPaul E. McKenney repeat: 85205bc276cSPaul E. McKenney VERBOSE_SCALEOUT_BATCH("ref_scale_reader %ld: waiting to start next experiment on cpu %d", me, raw_smp_processor_id()); 8531fbeb3a8SPaul E. McKenney 8541fbeb3a8SPaul E. McKenney // Wait for signal that this reader can start. 8551fbeb3a8SPaul E. McKenney wait_event(rt->wq, (atomic_read(&nreaders_exp) && smp_load_acquire(&rt->start_reader)) || 8561fbeb3a8SPaul E. McKenney torture_must_stop()); 8571fbeb3a8SPaul E. McKenney 8581fbeb3a8SPaul E. McKenney if (torture_must_stop()) 8591fbeb3a8SPaul E. McKenney goto end; 8601fbeb3a8SPaul E. McKenney 8611fbeb3a8SPaul E. McKenney // Make sure that the CPU is affinitized appropriately during testing. 86205bc276cSPaul E. McKenney WARN_ON_ONCE(raw_smp_processor_id() != me); 8631fbeb3a8SPaul E. McKenney 8641fbeb3a8SPaul E. McKenney WRITE_ONCE(rt->start_reader, 0); 8651fbeb3a8SPaul E. McKenney if (!atomic_dec_return(&n_started)) 8661fbeb3a8SPaul E. McKenney while (atomic_read_acquire(&n_started)) 8671fbeb3a8SPaul E. McKenney cpu_relax(); 8681fbeb3a8SPaul E. McKenney 869e76506f0SPaul E. McKenney VERBOSE_SCALEOUT_BATCH("ref_scale_reader %ld: experiment %d started", me, exp_idx); 8701fbeb3a8SPaul E. McKenney 8711fbeb3a8SPaul E. McKenney 8721fbeb3a8SPaul E. McKenney // To reduce noise, do an initial cache-warming invocation, check 8731fbeb3a8SPaul E. McKenney // in, and then keep warming until everyone has checked in. 8741fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 8751fbeb3a8SPaul E. McKenney if (!atomic_dec_return(&n_warmedup)) 8761fbeb3a8SPaul E. McKenney while (atomic_read_acquire(&n_warmedup)) 8771fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 8781fbeb3a8SPaul E. McKenney // Also keep interrupts disabled. This also has the effect 8791fbeb3a8SPaul E. McKenney // of preventing entries into slow path for rcu_read_unlock(). 8801fbeb3a8SPaul E. McKenney local_irq_save(flags); 8811fbeb3a8SPaul E. McKenney start = ktime_get_mono_fast_ns(); 8821fbeb3a8SPaul E. McKenney 8831fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 8841fbeb3a8SPaul E. McKenney 8851fbeb3a8SPaul E. McKenney duration = ktime_get_mono_fast_ns() - start; 8861fbeb3a8SPaul E. McKenney local_irq_restore(flags); 8871fbeb3a8SPaul E. McKenney 8881fbeb3a8SPaul E. McKenney rt->last_duration_ns = WARN_ON_ONCE(duration < 0) ? 0 : duration; 8891fbeb3a8SPaul E. McKenney // To reduce runtime-skew noise, do maintain-load invocations until 8901fbeb3a8SPaul E. McKenney // everyone is done. 8911fbeb3a8SPaul E. McKenney if (!atomic_dec_return(&n_cooleddown)) 8921fbeb3a8SPaul E. McKenney while (atomic_read_acquire(&n_cooleddown)) 8931fbeb3a8SPaul E. McKenney rcu_scale_one_reader(); 8941fbeb3a8SPaul E. McKenney 8951fbeb3a8SPaul E. McKenney if (atomic_dec_and_test(&nreaders_exp)) 8961fbeb3a8SPaul E. McKenney wake_up(&main_wq); 8971fbeb3a8SPaul E. McKenney 898e76506f0SPaul E. McKenney VERBOSE_SCALEOUT_BATCH("ref_scale_reader %ld: experiment %d ended, (readers remaining=%d)", 8991fbeb3a8SPaul E. McKenney me, exp_idx, atomic_read(&nreaders_exp)); 9001fbeb3a8SPaul E. McKenney 9011fbeb3a8SPaul E. McKenney if (!torture_must_stop()) 9021fbeb3a8SPaul E. McKenney goto repeat; 9031fbeb3a8SPaul E. McKenney end: 9041fbeb3a8SPaul E. McKenney torture_kthread_stopping("ref_scale_reader"); 9051fbeb3a8SPaul E. McKenney return 0; 9061fbeb3a8SPaul E. McKenney } 9071fbeb3a8SPaul E. McKenney 9081fbeb3a8SPaul E. McKenney static void reset_readers(void) 9091fbeb3a8SPaul E. McKenney { 9101fbeb3a8SPaul E. McKenney int i; 9111fbeb3a8SPaul E. McKenney struct reader_task *rt; 9121fbeb3a8SPaul E. McKenney 9131fbeb3a8SPaul E. McKenney for (i = 0; i < nreaders; i++) { 9141fbeb3a8SPaul E. McKenney rt = &(reader_tasks[i]); 9151fbeb3a8SPaul E. McKenney 9161fbeb3a8SPaul E. McKenney rt->last_duration_ns = 0; 9171fbeb3a8SPaul E. McKenney } 9181fbeb3a8SPaul E. McKenney } 9191fbeb3a8SPaul E. McKenney 9201fbeb3a8SPaul E. McKenney // Print the results of each reader and return the sum of all their durations. 9211fbeb3a8SPaul E. McKenney static u64 process_durations(int n) 9221fbeb3a8SPaul E. McKenney { 9231fbeb3a8SPaul E. McKenney int i; 9241fbeb3a8SPaul E. McKenney struct reader_task *rt; 9254e39bb49SChristophe JAILLET struct seq_buf s; 9261fbeb3a8SPaul E. McKenney char *buf; 9271fbeb3a8SPaul E. McKenney u64 sum = 0; 9281fbeb3a8SPaul E. McKenney 9299880eb87SLi Zhijian buf = kmalloc(800 + 64, GFP_KERNEL); 9301fbeb3a8SPaul E. McKenney if (!buf) 9311fbeb3a8SPaul E. McKenney return 0; 9324e39bb49SChristophe JAILLET seq_buf_init(&s, buf, 800 + 64); 9334e39bb49SChristophe JAILLET 9344e39bb49SChristophe JAILLET seq_buf_printf(&s, "Experiment #%d (Format: <THREAD-NUM>:<Total loop time in ns>)", 9351fbeb3a8SPaul E. McKenney exp_idx); 9361fbeb3a8SPaul E. McKenney 9371fbeb3a8SPaul E. McKenney for (i = 0; i < n && !torture_must_stop(); i++) { 9381fbeb3a8SPaul E. McKenney rt = &(reader_tasks[i]); 9391fbeb3a8SPaul E. McKenney 9401fbeb3a8SPaul E. McKenney if (i % 5 == 0) 9414e39bb49SChristophe JAILLET seq_buf_putc(&s, '\n'); 9424e39bb49SChristophe JAILLET 9434e39bb49SChristophe JAILLET if (seq_buf_used(&s) >= 800) { 9444e39bb49SChristophe JAILLET pr_alert("%s", seq_buf_str(&s)); 9454e39bb49SChristophe JAILLET seq_buf_clear(&s); 9469880eb87SLi Zhijian } 9474e39bb49SChristophe JAILLET 9484e39bb49SChristophe JAILLET seq_buf_printf(&s, "%d: %llu\t", i, rt->last_duration_ns); 9491fbeb3a8SPaul E. McKenney 9501fbeb3a8SPaul E. McKenney sum += rt->last_duration_ns; 9511fbeb3a8SPaul E. McKenney } 9524e39bb49SChristophe JAILLET pr_alert("%s\n", seq_buf_str(&s)); 9531fbeb3a8SPaul E. McKenney 9541fbeb3a8SPaul E. McKenney kfree(buf); 9551fbeb3a8SPaul E. McKenney return sum; 9561fbeb3a8SPaul E. McKenney } 9571fbeb3a8SPaul E. McKenney 9581fbeb3a8SPaul E. McKenney // The main_func is the main orchestrator, it performs a bunch of 9591fbeb3a8SPaul E. McKenney // experiments. For every experiment, it orders all the readers 9601fbeb3a8SPaul E. McKenney // involved to start and waits for them to finish the experiment. It 9611fbeb3a8SPaul E. McKenney // then reads their timestamps and starts the next experiment. Each 9621fbeb3a8SPaul E. McKenney // experiment progresses from 1 concurrent reader to N of them at which 9631fbeb3a8SPaul E. McKenney // point all the timestamps are printed. 9641fbeb3a8SPaul E. McKenney static int main_func(void *arg) 9651fbeb3a8SPaul E. McKenney { 9661fbeb3a8SPaul E. McKenney int exp, r; 9671fbeb3a8SPaul E. McKenney char buf1[64]; 9681fbeb3a8SPaul E. McKenney char *buf; 9691fbeb3a8SPaul E. McKenney u64 *result_avg; 9701fbeb3a8SPaul E. McKenney 9711fbeb3a8SPaul E. McKenney set_cpus_allowed_ptr(current, cpumask_of(nreaders % nr_cpu_ids)); 9721fbeb3a8SPaul E. McKenney set_user_nice(current, MAX_NICE); 9731fbeb3a8SPaul E. McKenney 9741fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("main_func task started"); 9751fbeb3a8SPaul E. McKenney result_avg = kzalloc(nruns * sizeof(*result_avg), GFP_KERNEL); 9769880eb87SLi Zhijian buf = kzalloc(800 + 64, GFP_KERNEL); 9771fbeb3a8SPaul E. McKenney if (!result_avg || !buf) { 9784feeb9d5SLi Zhijian SCALEOUT_ERRSTRING("out of memory"); 979c30c8763SLi Zhijian goto oom_exit; 9801fbeb3a8SPaul E. McKenney } 9811fbeb3a8SPaul E. McKenney if (holdoff) 9821fbeb3a8SPaul E. McKenney schedule_timeout_interruptible(holdoff * HZ); 9831fbeb3a8SPaul E. McKenney 9841fbeb3a8SPaul E. McKenney // Wait for all threads to start. 9851fbeb3a8SPaul E. McKenney atomic_inc(&n_init); 9861fbeb3a8SPaul E. McKenney while (atomic_read(&n_init) < nreaders + 1) 9871fbeb3a8SPaul E. McKenney schedule_timeout_uninterruptible(1); 9881fbeb3a8SPaul E. McKenney 9891fbeb3a8SPaul E. McKenney // Start exp readers up per experiment 9901fbeb3a8SPaul E. McKenney for (exp = 0; exp < nruns && !torture_must_stop(); exp++) { 9911fbeb3a8SPaul E. McKenney if (torture_must_stop()) 9921fbeb3a8SPaul E. McKenney goto end; 9931fbeb3a8SPaul E. McKenney 9941fbeb3a8SPaul E. McKenney reset_readers(); 9951fbeb3a8SPaul E. McKenney atomic_set(&nreaders_exp, nreaders); 9961fbeb3a8SPaul E. McKenney atomic_set(&n_started, nreaders); 9971fbeb3a8SPaul E. McKenney atomic_set(&n_warmedup, nreaders); 9981fbeb3a8SPaul E. McKenney atomic_set(&n_cooleddown, nreaders); 9991fbeb3a8SPaul E. McKenney 10001fbeb3a8SPaul E. McKenney exp_idx = exp; 10011fbeb3a8SPaul E. McKenney 10021fbeb3a8SPaul E. McKenney for (r = 0; r < nreaders; r++) { 10031fbeb3a8SPaul E. McKenney smp_store_release(&reader_tasks[r].start_reader, 1); 10041fbeb3a8SPaul E. McKenney wake_up(&reader_tasks[r].wq); 10051fbeb3a8SPaul E. McKenney } 10061fbeb3a8SPaul E. McKenney 10071fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("main_func: experiment started, waiting for %d readers", 10081fbeb3a8SPaul E. McKenney nreaders); 10091fbeb3a8SPaul E. McKenney 10101fbeb3a8SPaul E. McKenney wait_event(main_wq, 10111fbeb3a8SPaul E. McKenney !atomic_read(&nreaders_exp) || torture_must_stop()); 10121fbeb3a8SPaul E. McKenney 10131fbeb3a8SPaul E. McKenney VERBOSE_SCALEOUT("main_func: experiment ended"); 10141fbeb3a8SPaul E. McKenney 10151fbeb3a8SPaul E. McKenney if (torture_must_stop()) 10161fbeb3a8SPaul E. McKenney goto end; 10171fbeb3a8SPaul E. McKenney 10181fbeb3a8SPaul E. McKenney result_avg[exp] = div_u64(1000 * process_durations(nreaders), nreaders * loops); 10191fbeb3a8SPaul E. McKenney } 10201fbeb3a8SPaul E. McKenney 10211fbeb3a8SPaul E. McKenney // Print the average of all experiments 10221fbeb3a8SPaul E. McKenney SCALEOUT("END OF TEST. Calculating average duration per loop (nanoseconds)...\n"); 10231fbeb3a8SPaul E. McKenney 10249880eb87SLi Zhijian pr_alert("Runs\tTime(ns)\n"); 10251fbeb3a8SPaul E. McKenney for (exp = 0; exp < nruns; exp++) { 10261fbeb3a8SPaul E. McKenney u64 avg; 10271fbeb3a8SPaul E. McKenney u32 rem; 10281fbeb3a8SPaul E. McKenney 10291fbeb3a8SPaul E. McKenney avg = div_u64_rem(result_avg[exp], 1000, &rem); 10301fbeb3a8SPaul E. McKenney sprintf(buf1, "%d\t%llu.%03u\n", exp + 1, avg, rem); 10311fbeb3a8SPaul E. McKenney strcat(buf, buf1); 10329880eb87SLi Zhijian if (strlen(buf) >= 800) { 10339880eb87SLi Zhijian pr_alert("%s", buf); 10349880eb87SLi Zhijian buf[0] = 0; 10359880eb87SLi Zhijian } 10361fbeb3a8SPaul E. McKenney } 10371fbeb3a8SPaul E. McKenney 10389880eb87SLi Zhijian pr_alert("%s", buf); 10391fbeb3a8SPaul E. McKenney 1040c30c8763SLi Zhijian oom_exit: 10411fbeb3a8SPaul E. McKenney // This will shutdown everything including us. 10421fbeb3a8SPaul E. McKenney if (shutdown) { 10431fbeb3a8SPaul E. McKenney shutdown_start = 1; 10441fbeb3a8SPaul E. McKenney wake_up(&shutdown_wq); 10451fbeb3a8SPaul E. McKenney } 10461fbeb3a8SPaul E. McKenney 10471fbeb3a8SPaul E. McKenney // Wait for torture to stop us 10481fbeb3a8SPaul E. McKenney while (!torture_must_stop()) 10491fbeb3a8SPaul E. McKenney schedule_timeout_uninterruptible(1); 10501fbeb3a8SPaul E. McKenney 10511fbeb3a8SPaul E. McKenney end: 10521fbeb3a8SPaul E. McKenney torture_kthread_stopping("main_func"); 10531fbeb3a8SPaul E. McKenney kfree(result_avg); 10541fbeb3a8SPaul E. McKenney kfree(buf); 10551fbeb3a8SPaul E. McKenney return 0; 10561fbeb3a8SPaul E. McKenney } 10571fbeb3a8SPaul E. McKenney 10581fbeb3a8SPaul E. McKenney static void 10598f35fefaSChristophe JAILLET ref_scale_print_module_parms(const struct ref_scale_ops *cur_ops, const char *tag) 10601fbeb3a8SPaul E. McKenney { 10611fbeb3a8SPaul E. McKenney pr_alert("%s" SCALE_FLAG 1062d6fea1ddSPaul E. McKenney "--- %s: verbose=%d verbose_batched=%d shutdown=%d holdoff=%d lookup_instances=%ld loops=%ld nreaders=%d nruns=%d readdelay=%d\n", scale_type, tag, 1063d6fea1ddSPaul E. McKenney verbose, verbose_batched, shutdown, holdoff, lookup_instances, loops, nreaders, nruns, readdelay); 10641fbeb3a8SPaul E. McKenney } 10651fbeb3a8SPaul E. McKenney 10661fbeb3a8SPaul E. McKenney static void 10671fbeb3a8SPaul E. McKenney ref_scale_cleanup(void) 10681fbeb3a8SPaul E. McKenney { 10691fbeb3a8SPaul E. McKenney int i; 10701fbeb3a8SPaul E. McKenney 10711fbeb3a8SPaul E. McKenney if (torture_cleanup_begin()) 10721fbeb3a8SPaul E. McKenney return; 10731fbeb3a8SPaul E. McKenney 10741fbeb3a8SPaul E. McKenney if (!cur_ops) { 10751fbeb3a8SPaul E. McKenney torture_cleanup_end(); 10761fbeb3a8SPaul E. McKenney return; 10771fbeb3a8SPaul E. McKenney } 10781fbeb3a8SPaul E. McKenney 10791fbeb3a8SPaul E. McKenney if (reader_tasks) { 10801fbeb3a8SPaul E. McKenney for (i = 0; i < nreaders; i++) 10811fbeb3a8SPaul E. McKenney torture_stop_kthread("ref_scale_reader", 10821fbeb3a8SPaul E. McKenney reader_tasks[i].task); 10831fbeb3a8SPaul E. McKenney } 10841fbeb3a8SPaul E. McKenney kfree(reader_tasks); 10851fbeb3a8SPaul E. McKenney 10861fbeb3a8SPaul E. McKenney torture_stop_kthread("main_task", main_task); 10871fbeb3a8SPaul E. McKenney kfree(main_task); 10881fbeb3a8SPaul E. McKenney 10891fbeb3a8SPaul E. McKenney // Do scale-type-specific cleanup operations. 10901fbeb3a8SPaul E. McKenney if (cur_ops->cleanup != NULL) 10911fbeb3a8SPaul E. McKenney cur_ops->cleanup(); 10921fbeb3a8SPaul E. McKenney 10931fbeb3a8SPaul E. McKenney torture_cleanup_end(); 10941fbeb3a8SPaul E. McKenney } 10951fbeb3a8SPaul E. McKenney 10961fbeb3a8SPaul E. McKenney // Shutdown kthread. Just waits to be awakened, then shuts down system. 10971fbeb3a8SPaul E. McKenney static int 10981fbeb3a8SPaul E. McKenney ref_scale_shutdown(void *arg) 10991fbeb3a8SPaul E. McKenney { 11006bc6e6b2SPaul E. McKenney wait_event_idle(shutdown_wq, shutdown_start); 11011fbeb3a8SPaul E. McKenney 11021fbeb3a8SPaul E. McKenney smp_mb(); // Wake before output. 11031fbeb3a8SPaul E. McKenney ref_scale_cleanup(); 11041fbeb3a8SPaul E. McKenney kernel_power_off(); 11051fbeb3a8SPaul E. McKenney 11061fbeb3a8SPaul E. McKenney return -EINVAL; 11071fbeb3a8SPaul E. McKenney } 11081fbeb3a8SPaul E. McKenney 11091fbeb3a8SPaul E. McKenney static int __init 11101fbeb3a8SPaul E. McKenney ref_scale_init(void) 11111fbeb3a8SPaul E. McKenney { 11121fbeb3a8SPaul E. McKenney long i; 11131fbeb3a8SPaul E. McKenney int firsterr = 0; 11148f35fefaSChristophe JAILLET static const struct ref_scale_ops *scale_ops[] = { 1115*6a2c0255SPaul E. McKenney &rcu_ops, &srcu_ops, &srcu_lite_ops, RCU_TRACE_OPS RCU_TASKS_OPS 1116*6a2c0255SPaul E. McKenney &refcnt_ops, &rwlock_ops, &rwsem_ops, &lock_ops, &lock_irq_ops, &acqrel_ops, 1117*6a2c0255SPaul E. McKenney &clock_ops, &jiffies_ops, &typesafe_ref_ops, &typesafe_lock_ops, 1118*6a2c0255SPaul E. McKenney &typesafe_seqlock_ops, 11191fbeb3a8SPaul E. McKenney }; 11201fbeb3a8SPaul E. McKenney 11211fbeb3a8SPaul E. McKenney if (!torture_init_begin(scale_type, verbose)) 11221fbeb3a8SPaul E. McKenney return -EBUSY; 11231fbeb3a8SPaul E. McKenney 11241fbeb3a8SPaul E. McKenney for (i = 0; i < ARRAY_SIZE(scale_ops); i++) { 11251fbeb3a8SPaul E. McKenney cur_ops = scale_ops[i]; 11261fbeb3a8SPaul E. McKenney if (strcmp(scale_type, cur_ops->name) == 0) 11271fbeb3a8SPaul E. McKenney break; 11281fbeb3a8SPaul E. McKenney } 11291fbeb3a8SPaul E. McKenney if (i == ARRAY_SIZE(scale_ops)) { 11301fbeb3a8SPaul E. McKenney pr_alert("rcu-scale: invalid scale type: \"%s\"\n", scale_type); 11311fbeb3a8SPaul E. McKenney pr_alert("rcu-scale types:"); 11321fbeb3a8SPaul E. McKenney for (i = 0; i < ARRAY_SIZE(scale_ops); i++) 11331fbeb3a8SPaul E. McKenney pr_cont(" %s", scale_ops[i]->name); 11341fbeb3a8SPaul E. McKenney pr_cont("\n"); 11351fbeb3a8SPaul E. McKenney firsterr = -EINVAL; 11361fbeb3a8SPaul E. McKenney cur_ops = NULL; 11371fbeb3a8SPaul E. McKenney goto unwind; 11381fbeb3a8SPaul E. McKenney } 11391fbeb3a8SPaul E. McKenney if (cur_ops->init) 11403c6496c8SPaul E. McKenney if (!cur_ops->init()) { 11413c6496c8SPaul E. McKenney firsterr = -EUCLEAN; 11423c6496c8SPaul E. McKenney goto unwind; 11433c6496c8SPaul E. McKenney } 11441fbeb3a8SPaul E. McKenney 11451fbeb3a8SPaul E. McKenney ref_scale_print_module_parms(cur_ops, "Start of test"); 11461fbeb3a8SPaul E. McKenney 11471fbeb3a8SPaul E. McKenney // Shutdown task 11481fbeb3a8SPaul E. McKenney if (shutdown) { 11491fbeb3a8SPaul E. McKenney init_waitqueue_head(&shutdown_wq); 11501fbeb3a8SPaul E. McKenney firsterr = torture_create_kthread(ref_scale_shutdown, NULL, 11511fbeb3a8SPaul E. McKenney shutdown_task); 1152ed60ad73SPaul E. McKenney if (torture_init_error(firsterr)) 11531fbeb3a8SPaul E. McKenney goto unwind; 11541fbeb3a8SPaul E. McKenney schedule_timeout_uninterruptible(1); 11551fbeb3a8SPaul E. McKenney } 11561fbeb3a8SPaul E. McKenney 11571fbeb3a8SPaul E. McKenney // Reader tasks (default to ~75% of online CPUs). 11581fbeb3a8SPaul E. McKenney if (nreaders < 0) 11591fbeb3a8SPaul E. McKenney nreaders = (num_online_cpus() >> 1) + (num_online_cpus() >> 2); 11600c6d18d8SPaul E. McKenney if (WARN_ONCE(loops <= 0, "%s: loops = %ld, adjusted to 1\n", __func__, loops)) 11610c6d18d8SPaul E. McKenney loops = 1; 11620c6d18d8SPaul E. McKenney if (WARN_ONCE(nreaders <= 0, "%s: nreaders = %d, adjusted to 1\n", __func__, nreaders)) 11630c6d18d8SPaul E. McKenney nreaders = 1; 11640c6d18d8SPaul E. McKenney if (WARN_ONCE(nruns <= 0, "%s: nruns = %d, adjusted to 1\n", __func__, nruns)) 11650c6d18d8SPaul E. McKenney nruns = 1; 11661fbeb3a8SPaul E. McKenney reader_tasks = kcalloc(nreaders, sizeof(reader_tasks[0]), 11671fbeb3a8SPaul E. McKenney GFP_KERNEL); 11681fbeb3a8SPaul E. McKenney if (!reader_tasks) { 11694feeb9d5SLi Zhijian SCALEOUT_ERRSTRING("out of memory"); 11701fbeb3a8SPaul E. McKenney firsterr = -ENOMEM; 11711fbeb3a8SPaul E. McKenney goto unwind; 11721fbeb3a8SPaul E. McKenney } 11731fbeb3a8SPaul E. McKenney 1174f71f22b6SLi Zhijian VERBOSE_SCALEOUT("Starting %d reader threads", nreaders); 11751fbeb3a8SPaul E. McKenney 11761fbeb3a8SPaul E. McKenney for (i = 0; i < nreaders; i++) { 1177f5063e89SWaiman Long init_waitqueue_head(&reader_tasks[i].wq); 11781fbeb3a8SPaul E. McKenney firsterr = torture_create_kthread(ref_scale_reader, (void *)i, 11791fbeb3a8SPaul E. McKenney reader_tasks[i].task); 1180ed60ad73SPaul E. McKenney if (torture_init_error(firsterr)) 11811fbeb3a8SPaul E. McKenney goto unwind; 11821fbeb3a8SPaul E. McKenney } 11831fbeb3a8SPaul E. McKenney 11841fbeb3a8SPaul E. McKenney // Main Task 11851fbeb3a8SPaul E. McKenney init_waitqueue_head(&main_wq); 11861fbeb3a8SPaul E. McKenney firsterr = torture_create_kthread(main_func, NULL, main_task); 1187ed60ad73SPaul E. McKenney if (torture_init_error(firsterr)) 11881fbeb3a8SPaul E. McKenney goto unwind; 11891fbeb3a8SPaul E. McKenney 11901fbeb3a8SPaul E. McKenney torture_init_end(); 11911fbeb3a8SPaul E. McKenney return 0; 11921fbeb3a8SPaul E. McKenney 11931fbeb3a8SPaul E. McKenney unwind: 11941fbeb3a8SPaul E. McKenney torture_init_end(); 11951fbeb3a8SPaul E. McKenney ref_scale_cleanup(); 1196bc80d353SPaul E. McKenney if (shutdown) { 1197bc80d353SPaul E. McKenney WARN_ON(!IS_MODULE(CONFIG_RCU_REF_SCALE_TEST)); 1198bc80d353SPaul E. McKenney kernel_power_off(); 1199bc80d353SPaul E. McKenney } 12001fbeb3a8SPaul E. McKenney return firsterr; 12011fbeb3a8SPaul E. McKenney } 12021fbeb3a8SPaul E. McKenney 12031fbeb3a8SPaul E. McKenney module_init(ref_scale_init); 12041fbeb3a8SPaul E. McKenney module_exit(ref_scale_cleanup); 1205