794 lines · 20.8 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 "rcu/rcu.h" MODULE_LICENSE("GPL");MODULE_AUTHOR("Paul E. McKenney <paulmck@linux.ibm.com>"); 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); #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; /* * 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;	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 = cpu_down(cpu);	if (ret) {		if (verbose)			pr_alert("%s" TORTURE_FLAG				 "torture_onoff task: offline %d failed: errno %d\n",				 torture_type, cpu, 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;	} 	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;	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 = cpu_up(cpu);	if (ret) {		if (verbose)			pr_alert("%s" TORTURE_FLAG				 "torture_onoff task: online %d failed: errno %d\n",				 torture_type, cpu, 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;	} 	return true;}EXPORT_SYMBOL_GPL(torture_online); /* * 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);	int ret; 	VERBOSE_TOROUT_STRING("torture_onoff task started");	for_each_online_cpu(cpu)		maxcpu = cpu;	WARN_ON(maxcpu < 0);	if (!IS_MODULE(CONFIG_TORTURE_TEST))		for_each_possible_cpu(cpu) {			if (cpu_online(cpu))				continue;			ret = cpu_up(cpu);			if (ret && verbose) {				pr_alert("%s" TORTURE_FLAG					 "%s: Initial online %d: errno %d\n",					 __func__, torture_type, cpu, ret);			}		} 	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");		schedule_timeout_interruptible(onoff_holdoff);		VERBOSE_TOROUT_STRING("torture_onoff end holdoff");	}	while (!torture_must_stop()) {		cpu = (torture_random(&rand) >> 4) % (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);		schedule_timeout_interruptible(onoff_interval);	} stop:	torture_kthread_stopping("torture_onoff");	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 */}EXPORT_SYMBOL_GPL(torture_onoff_cleanup); /* * 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();		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(sizeof(*stp), GFP_KERNEL);	if (WARN_ON_ONCE(stp == NULL))		return;	stp->st_t = tp;	mutex_lock(&shuffle_task_mutex);	list_add(&stp->st_l, &shuffle_task_list);	mutex_unlock(&shuffle_task_mutex);}EXPORT_SYMBOL_GPL(torture_shuffle_task_register); /* * Unregister all tasks, for example, at the end of the torture run. */static void torture_shuffle_task_unregister_all(void){	struct shuffle_task *stp;	struct shuffle_task *p; 	mutex_lock(&shuffle_task_mutex);	list_for_each_entry_safe(stp, p, &shuffle_task_list, st_l) {		list_del(&stp->st_l);		kfree(stp);	}	mutex_unlock(&shuffle_task_mutex);} /* Shuffle tasks such that we allow shuffle_idle_cpu to become idle. * A special case is when shuffle_idle_cpu = -1, in which case we allow * the tasks to run on all CPUs. */static void torture_shuffle_tasks(void){	struct shuffle_task *stp; 	cpumask_setall(shuffle_tmp_mask);	get_online_cpus(); 	/* No point in shuffling if there is only one online CPU (ex: UP) */	if (num_online_cpus() == 1) {		put_online_cpus();		return;	} 	/* Advance to the next CPU.  Upon overflow, don't idle any CPUs. */	shuffle_idle_cpu = cpumask_next(shuffle_idle_cpu, shuffle_tmp_mask);	if (shuffle_idle_cpu >= nr_cpu_ids)		shuffle_idle_cpu = -1;	else		cpumask_clear_cpu(shuffle_idle_cpu, shuffle_tmp_mask); 	mutex_lock(&shuffle_task_mutex);	list_for_each_entry(stp, &shuffle_task_list, st_l)		set_cpus_allowed_ptr(stp->st_t, shuffle_tmp_mask);	mutex_unlock(&shuffle_task_mutex); 	put_online_cpus();} /* Shuffle tasks across CPUs, with the intent of allowing each CPU in the * system to become idle at a time and cut off its timer ticks. This is meant * to test the support for such tickless idle CPU in RCU. */static int torture_shuffle(void *arg){	VERBOSE_TOROUT_STRING("torture_shuffle task started");	do {		schedule_timeout_interruptible(shuffle_interval);		torture_shuffle_tasks();		torture_shutdown_absorb("torture_shuffle");	} while (!torture_must_stop());	torture_kthread_stopping("torture_shuffle");	return 0;} /* * Start the shuffler, with shuffint in jiffies. */int torture_shuffle_init(long shuffint){	shuffle_interval = shuffint; 	shuffle_idle_cpu = -1; 	if (!alloc_cpumask_var(&shuffle_tmp_mask, GFP_KERNEL)) {		VERBOSE_TOROUT_ERRSTRING("Failed to alloc mask");		return -ENOMEM;	} 	/* Create the shuffler thread */	return torture_create_kthread(torture_shuffle, NULL, shuffler_task);}EXPORT_SYMBOL_GPL(torture_shuffle_init); /* * Stop the shuffling. */static void torture_shuffle_cleanup(void){	torture_shuffle_task_unregister_all();	if (shuffler_task) {		VERBOSE_TOROUT_STRING("Stopping torture_shuffle task");		kthread_stop(shuffler_task);		free_cpumask_var(shuffle_tmp_mask);	}	shuffler_task = NULL;}EXPORT_SYMBOL_GPL(torture_shuffle_cleanup); /* * Variables for auto-shutdown.  This allows "lights out" torture runs * to be fully scripted. */static struct task_struct *shutdown_task;static ktime_t shutdown_time;		/* time to system shutdown. */static void (*torture_shutdown_hook)(void); /* * Absorb kthreads into a kernel function that won't return, so that * they won't ever access module text or data again. */void torture_shutdown_absorb(const char *title){	while (READ_ONCE(fullstop) == FULLSTOP_SHUTDOWN) {		pr_notice("torture thread %s parking due to system shutdown\n",			  title);		schedule_timeout_uninterruptible(MAX_SCHEDULE_TIMEOUT);	}}EXPORT_SYMBOL_GPL(torture_shutdown_absorb); /* * Cause the torture test to shutdown the system after the test has * run for the time specified by the shutdown_secs parameter. */static int torture_shutdown(void *arg){	ktime_t ktime_snap; 	VERBOSE_TOROUT_STRING("torture_shutdown task started");	ktime_snap = ktime_get();	while (ktime_before(ktime_snap, shutdown_time) &&	       !torture_must_stop()) {		if (verbose)			pr_alert("%s" TORTURE_FLAG				 "torture_shutdown task: %llu ms remaining\n",				 torture_type,				 ktime_ms_delta(shutdown_time, ktime_snap));		set_current_state(TASK_INTERRUPTIBLE);		schedule_hrtimeout(&shutdown_time, HRTIMER_MODE_ABS);		ktime_snap = ktime_get();	}	if (torture_must_stop()) {		torture_kthread_stopping("torture_shutdown");		return 0;	}

Lines 1–500 of 794.