990 lines · 26.6 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0+/* * Common functions for in-kernel torture tests. * * Copyright (C) IBM Corporation, 2014 * * Author: Paul E. McKenney <paulmck@linux.ibm.com> *	Based on kernel/rcu/torture.c. */ #define pr_fmt(fmt) fmt #include <linux/types.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/kthread.h>#include <linux/err.h>#include <linux/spinlock.h>#include <linux/smp.h>#include <linux/interrupt.h>#include <linux/sched.h>#include <linux/sched/clock.h>#include <linux/atomic.h>#include <linux/bitops.h>#include <linux/completion.h>#include <linux/moduleparam.h>#include <linux/percpu.h>#include <linux/notifier.h>#include <linux/reboot.h>#include <linux/freezer.h>#include <linux/cpu.h>#include <linux/delay.h>#include <linux/stat.h>#include <linux/slab.h>#include <linux/trace_clock.h>#include <linux/ktime.h>#include <asm/byteorder.h>#include <linux/torture.h>#include <linux/sched/rt.h>#include "rcu/rcu.h" MODULE_DESCRIPTION("Common functions for in-kernel torture tests");MODULE_LICENSE("GPL");MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.ibm.com>"); static bool disable_onoff_at_boot;module_param(disable_onoff_at_boot, bool, 0444); static bool ftrace_dump_at_shutdown;module_param(ftrace_dump_at_shutdown, bool, 0444); static int verbose_sleep_frequency;module_param(verbose_sleep_frequency, int, 0444); static int verbose_sleep_duration = 1;module_param(verbose_sleep_duration, int, 0444); static int random_shuffle;module_param(random_shuffle, int, 0444); static char *torture_type;static int verbose; /* Mediate rmmod and system shutdown.  Concurrent rmmod & shutdown illegal! */#define FULLSTOP_DONTSTOP 0	/* Normal operation. */#define FULLSTOP_SHUTDOWN 1	/* System shutdown with torture running. */#define FULLSTOP_RMMOD    2	/* Normal rmmod of torture. */static int fullstop = FULLSTOP_RMMOD;static DEFINE_MUTEX(fullstop_mutex); static atomic_t verbose_sleep_counter; /* * Sleep if needed from VERBOSE_TOROUT*(). */void verbose_torout_sleep(void){	if (verbose_sleep_frequency > 0 &&	    verbose_sleep_duration > 0 &&	    !(atomic_inc_return(&verbose_sleep_counter) % verbose_sleep_frequency))		schedule_timeout_uninterruptible(verbose_sleep_duration);}EXPORT_SYMBOL_GPL(verbose_torout_sleep); /* * Schedule a high-resolution-timer sleep in nanoseconds, with a 32-bit * nanosecond random fuzz.  This function and its friends desynchronize * testing from the timer wheel. */int torture_hrtimeout_ns(ktime_t baset_ns, u32 fuzzt_ns, const enum hrtimer_mode mode,			 struct torture_random_state *trsp){	ktime_t hto = baset_ns; 	if (trsp && fuzzt_ns)		hto += torture_random(trsp) % fuzzt_ns;	set_current_state(TASK_IDLE);	return schedule_hrtimeout(&hto, mode);}EXPORT_SYMBOL_GPL(torture_hrtimeout_ns); /* * Schedule a high-resolution-timer sleep in microseconds, with a 32-bit * nanosecond (not microsecond!) random fuzz. */int torture_hrtimeout_us(u32 baset_us, u32 fuzzt_ns, struct torture_random_state *trsp){	ktime_t baset_ns = baset_us * NSEC_PER_USEC; 	return torture_hrtimeout_ns(baset_ns, fuzzt_ns, HRTIMER_MODE_REL, trsp);}EXPORT_SYMBOL_GPL(torture_hrtimeout_us); /* * Schedule a high-resolution-timer sleep in milliseconds, with a 32-bit * microsecond (not millisecond!) random fuzz. */int torture_hrtimeout_ms(u32 baset_ms, u32 fuzzt_us, struct torture_random_state *trsp){	ktime_t baset_ns = baset_ms * NSEC_PER_MSEC;	u32 fuzzt_ns; 	if ((u32)~0U / NSEC_PER_USEC < fuzzt_us)		fuzzt_ns = (u32)~0U;	else		fuzzt_ns = fuzzt_us * NSEC_PER_USEC;	return torture_hrtimeout_ns(baset_ns, fuzzt_ns, HRTIMER_MODE_REL, trsp);}EXPORT_SYMBOL_GPL(torture_hrtimeout_ms); /* * Schedule a high-resolution-timer sleep in jiffies, with an * implied one-jiffy random fuzz.  This is intended to replace calls to * schedule_timeout_interruptible() and friends. */int torture_hrtimeout_jiffies(u32 baset_j, struct torture_random_state *trsp){	ktime_t baset_ns = jiffies_to_nsecs(baset_j); 	return torture_hrtimeout_ns(baset_ns, jiffies_to_nsecs(1), HRTIMER_MODE_REL, trsp);}EXPORT_SYMBOL_GPL(torture_hrtimeout_jiffies); /* * Schedule a high-resolution-timer sleep in milliseconds, with a 32-bit * millisecond (not second!) random fuzz. */int torture_hrtimeout_s(u32 baset_s, u32 fuzzt_ms, struct torture_random_state *trsp){	ktime_t baset_ns = baset_s * NSEC_PER_SEC;	u32 fuzzt_ns; 	if ((u32)~0U / NSEC_PER_MSEC < fuzzt_ms)		fuzzt_ns = (u32)~0U;	else		fuzzt_ns = fuzzt_ms * NSEC_PER_MSEC;	return torture_hrtimeout_ns(baset_ns, fuzzt_ns, HRTIMER_MODE_REL, trsp);}EXPORT_SYMBOL_GPL(torture_hrtimeout_s); #ifdef CONFIG_HOTPLUG_CPU /* * Variables for online-offline handling.  Only present if CPU hotplug * is enabled, otherwise does nothing. */ static struct task_struct *onoff_task;static long onoff_holdoff;static long onoff_interval;static torture_ofl_func *onoff_f;static long n_offline_attempts;static long n_offline_successes;static unsigned long sum_offline;static int min_offline = -1;static int max_offline;static long n_online_attempts;static long n_online_successes;static unsigned long sum_online;static int min_online = -1;static int max_online; static int torture_online_cpus = NR_CPUS; /* * Some torture testing leverages confusion as to the number of online * CPUs.  This function returns the torture-testing view of this number, * which allows torture tests to load-balance appropriately. */int torture_num_online_cpus(void){	return READ_ONCE(torture_online_cpus);}EXPORT_SYMBOL_GPL(torture_num_online_cpus); /* * Attempt to take a CPU offline.  Return false if the CPU is already * offline or if it is not subject to CPU-hotplug operations.  The * caller can detect other failures by looking at the statistics. */bool torture_offline(int cpu, long *n_offl_attempts, long *n_offl_successes,		     unsigned long *sum_offl, int *min_offl, int *max_offl){	unsigned long delta;	int ret;	char *s;	unsigned long starttime; 	if (!cpu_online(cpu) || !cpu_is_hotpluggable(cpu))		return false;	if (num_online_cpus() <= 1)		return false;  /* Can't offline the last CPU. */ 	if (verbose > 1)		pr_alert("%s" TORTURE_FLAG			 "torture_onoff task: offlining %d\n",			 torture_type, cpu);	starttime = jiffies;	(*n_offl_attempts)++;	ret = remove_cpu(cpu);	if (ret) {		s = "";		if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) {			// PCI probe frequently disables hotplug during boot.			(*n_offl_attempts)--;			s = " (-EBUSY forgiven during boot)";		}		if (verbose)			pr_alert("%s" TORTURE_FLAG				 "torture_onoff task: offline %d failed%s: errno %d\n",				 torture_type, cpu, s, ret);	} else {		if (verbose > 1)			pr_alert("%s" TORTURE_FLAG				 "torture_onoff task: offlined %d\n",				 torture_type, cpu);		if (onoff_f)			onoff_f();		(*n_offl_successes)++;		delta = jiffies - starttime;		*sum_offl += delta;		if (*min_offl < 0) {			*min_offl = delta;			*max_offl = delta;		}		if (*min_offl > delta)			*min_offl = delta;		if (*max_offl < delta)			*max_offl = delta;		WRITE_ONCE(torture_online_cpus, torture_online_cpus - 1);		WARN_ON_ONCE(torture_online_cpus <= 0);	} 	return true;}EXPORT_SYMBOL_GPL(torture_offline); /* * Attempt to bring a CPU online.  Return false if the CPU is already * online or if it is not subject to CPU-hotplug operations.  The * caller can detect other failures by looking at the statistics. */bool torture_online(int cpu, long *n_onl_attempts, long *n_onl_successes,		    unsigned long *sum_onl, int *min_onl, int *max_onl){	unsigned long delta;	int ret;	char *s;	unsigned long starttime; 	if (cpu_online(cpu) || !cpu_is_hotpluggable(cpu))		return false; 	if (verbose > 1)		pr_alert("%s" TORTURE_FLAG			 "torture_onoff task: onlining %d\n",			 torture_type, cpu);	starttime = jiffies;	(*n_onl_attempts)++;	ret = add_cpu(cpu);	if (ret) {		s = "";		if (!rcu_inkernel_boot_has_ended() && ret == -EBUSY) {			// PCI probe frequently disables hotplug during boot.			(*n_onl_attempts)--;			s = " (-EBUSY forgiven during boot)";		}		if (verbose)			pr_alert("%s" TORTURE_FLAG				 "torture_onoff task: online %d failed%s: errno %d\n",				 torture_type, cpu, s, ret);	} else {		if (verbose > 1)			pr_alert("%s" TORTURE_FLAG				 "torture_onoff task: onlined %d\n",				 torture_type, cpu);		(*n_onl_successes)++;		delta = jiffies - starttime;		*sum_onl += delta;		if (*min_onl < 0) {			*min_onl = delta;			*max_onl = delta;		}		if (*min_onl > delta)			*min_onl = delta;		if (*max_onl < delta)			*max_onl = delta;		WRITE_ONCE(torture_online_cpus, torture_online_cpus + 1);	} 	return true;}EXPORT_SYMBOL_GPL(torture_online); /* * Get everything online at the beginning and ends of tests. */static void torture_online_all(char *phase){	int cpu;	int ret; 	for_each_possible_cpu(cpu) {		if (cpu_online(cpu))			continue;		ret = add_cpu(cpu);		if (ret && verbose) {			pr_alert("%s" TORTURE_FLAG				 "%s: %s online %d: errno %d\n",				 __func__, phase, torture_type, cpu, ret);		}	}} /* * Execute random CPU-hotplug operations at the interval specified * by the onoff_interval. */static inttorture_onoff(void *arg){	int cpu;	int maxcpu = -1;	DEFINE_TORTURE_RANDOM(rand); 	VERBOSE_TOROUT_STRING("torture_onoff task started");	for_each_online_cpu(cpu)		maxcpu = cpu;	WARN_ON(maxcpu < 0);	torture_online_all("Initial");	if (maxcpu == 0) {		VERBOSE_TOROUT_STRING("Only one CPU, so CPU-hotplug testing is disabled");		goto stop;	} 	if (onoff_holdoff > 0) {		VERBOSE_TOROUT_STRING("torture_onoff begin holdoff");		torture_hrtimeout_jiffies(onoff_holdoff, &rand);		VERBOSE_TOROUT_STRING("torture_onoff end holdoff");	}	while (!rcu_inkernel_boot_has_ended())		schedule_timeout_interruptible(HZ / 10);	while (!torture_must_stop()) {		if (disable_onoff_at_boot && !rcu_inkernel_boot_has_ended()) {			torture_hrtimeout_jiffies(HZ / 10, &rand);			continue;		}		cpu = torture_random(&rand) % (maxcpu + 1);		if (!torture_offline(cpu,				     &n_offline_attempts, &n_offline_successes,				     &sum_offline, &min_offline, &max_offline))			torture_online(cpu,				       &n_online_attempts, &n_online_successes,				       &sum_online, &min_online, &max_online);		torture_hrtimeout_jiffies(onoff_interval, &rand);	} stop:	torture_kthread_stopping("torture_onoff");	torture_online_all("Final");	return 0;} #endif /* #ifdef CONFIG_HOTPLUG_CPU */ /* * Initiate online-offline handling. */int torture_onoff_init(long ooholdoff, long oointerval, torture_ofl_func *f){#ifdef CONFIG_HOTPLUG_CPU	onoff_holdoff = ooholdoff;	onoff_interval = oointerval;	onoff_f = f;	if (onoff_interval <= 0)		return 0;	return torture_create_kthread(torture_onoff, NULL, onoff_task);#else /* #ifdef CONFIG_HOTPLUG_CPU */	return 0;#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */}EXPORT_SYMBOL_GPL(torture_onoff_init); /* * Clean up after online/offline testing. */static void torture_onoff_cleanup(void){#ifdef CONFIG_HOTPLUG_CPU	if (onoff_task == NULL)		return;	VERBOSE_TOROUT_STRING("Stopping torture_onoff task");	kthread_stop(onoff_task);	onoff_task = NULL;#endif /* #ifdef CONFIG_HOTPLUG_CPU */} /* * Print online/offline testing statistics. */void torture_onoff_stats(void){#ifdef CONFIG_HOTPLUG_CPU	pr_cont("onoff: %ld/%ld:%ld/%ld %d,%d:%d,%d %lu:%lu (HZ=%d) ",		n_online_successes, n_online_attempts,		n_offline_successes, n_offline_attempts,		min_online, max_online,		min_offline, max_offline,		sum_online, sum_offline, HZ);#endif /* #ifdef CONFIG_HOTPLUG_CPU */}EXPORT_SYMBOL_GPL(torture_onoff_stats); /* * Were all the online/offline operations successful? */bool torture_onoff_failures(void){#ifdef CONFIG_HOTPLUG_CPU	return n_online_successes != n_online_attempts ||	       n_offline_successes != n_offline_attempts;#else /* #ifdef CONFIG_HOTPLUG_CPU */	return false;#endif /* #else #ifdef CONFIG_HOTPLUG_CPU */}EXPORT_SYMBOL_GPL(torture_onoff_failures); #define TORTURE_RANDOM_MULT	39916801  /* prime */#define TORTURE_RANDOM_ADD	479001701 /* prime */#define TORTURE_RANDOM_REFRESH	10000 /* * Crude but fast random-number generator.  Uses a linear congruential * generator, with occasional help from cpu_clock(). */unsigned longtorture_random(struct torture_random_state *trsp){	if (--trsp->trs_count < 0) {		trsp->trs_state += (unsigned long)local_clock() + raw_smp_processor_id();		trsp->trs_count = TORTURE_RANDOM_REFRESH;	}	trsp->trs_state = trsp->trs_state * TORTURE_RANDOM_MULT +		TORTURE_RANDOM_ADD;	return swahw32(trsp->trs_state);}EXPORT_SYMBOL_GPL(torture_random); /* * Variables for shuffling.  The idea is to ensure that each CPU stays * idle for an extended period to test interactions with dyntick idle, * as well as interactions with any per-CPU variables. */struct shuffle_task {	struct list_head st_l;	struct task_struct *st_t;}; static long shuffle_interval;	/* In jiffies. */static struct task_struct *shuffler_task;static cpumask_var_t shuffle_tmp_mask;static int shuffle_idle_cpu;	/* Force all torture tasks off this CPU */static struct list_head shuffle_task_list = LIST_HEAD_INIT(shuffle_task_list);static DEFINE_MUTEX(shuffle_task_mutex); /* * Register a task to be shuffled.  If there is no memory, just splat * and don't bother registering. */void torture_shuffle_task_register(struct task_struct *tp){	struct shuffle_task *stp; 	if (WARN_ON_ONCE(tp == NULL))		return;	stp = kmalloc_obj(*stp);	if (WARN_ON_ONCE(stp == NULL))		return;	stp->st_t = tp;

Lines 1–500 of 990.