Merge branches 'consolidate.2019.05.28a', 'doc.2019.05.28a', 'fixes.2019.06.13a', 'srcu.2019.05.28a', 'sync.2019.05.28a' and 'torture.2019.05.28a' into HEAD
consolidate.2019.05.28a: RCU flavor consolidation cleanups and optmizations. doc.2019.05.28a: Documentation updates. fixes.2019.06.13a: Miscellaneous fixes. srcu.2019.05.28a: SRCU updates. sync.2019.05.28a: RCU-sync flavor consolidation. torture.2019.05.28a: Torture-test updates.
This commit is contained in:
commit
11ca7a9d54
|
@ -12,6 +12,7 @@ please read on.
|
|||
Reference counting on elements of lists which are protected by traditional
|
||||
reader/writer spinlocks or semaphores are straightforward:
|
||||
|
||||
CODE LISTING A:
|
||||
1. 2.
|
||||
add() search_and_reference()
|
||||
{ {
|
||||
|
@ -28,7 +29,8 @@ add() search_and_reference()
|
|||
release_referenced() delete()
|
||||
{ {
|
||||
... write_lock(&list_lock);
|
||||
atomic_dec(&el->rc, relfunc) ...
|
||||
if(atomic_dec_and_test(&el->rc)) ...
|
||||
kfree(el);
|
||||
... remove_element
|
||||
} write_unlock(&list_lock);
|
||||
...
|
||||
|
@ -44,6 +46,7 @@ search_and_reference() could potentially hold reference to an element which
|
|||
has already been deleted from the list/array. Use atomic_inc_not_zero()
|
||||
in this scenario as follows:
|
||||
|
||||
CODE LISTING B:
|
||||
1. 2.
|
||||
add() search_and_reference()
|
||||
{ {
|
||||
|
@ -79,6 +82,7 @@ search_and_reference() code path. In such cases, the
|
|||
atomic_dec_and_test() may be moved from delete() to el_free()
|
||||
as follows:
|
||||
|
||||
CODE LISTING C:
|
||||
1. 2.
|
||||
add() search_and_reference()
|
||||
{ {
|
||||
|
@ -114,6 +118,17 @@ element can therefore safely be freed. This in turn guarantees that if
|
|||
any reader finds the element, that reader may safely acquire a reference
|
||||
without checking the value of the reference counter.
|
||||
|
||||
A clear advantage of the RCU-based pattern in listing C over the one
|
||||
in listing B is that any call to search_and_reference() that locates
|
||||
a given object will succeed in obtaining a reference to that object,
|
||||
even given a concurrent invocation of delete() for that same object.
|
||||
Similarly, a clear advantage of both listings B and C over listing A is
|
||||
that a call to delete() is not delayed even if there are an arbitrarily
|
||||
large number of calls to search_and_reference() searching for the same
|
||||
object that delete() was invoked on. Instead, all that is delayed is
|
||||
the eventual invocation of kfree(), which is usually not a problem on
|
||||
modern computer systems, even the small ones.
|
||||
|
||||
In cases where delete() can sleep, synchronize_rcu() can be called from
|
||||
delete(), so that el_free() can be subsumed into delete as follows:
|
||||
|
||||
|
@ -130,3 +145,7 @@ delete()
|
|||
kfree(el);
|
||||
...
|
||||
}
|
||||
|
||||
As additional examples in the kernel, the pattern in listing C is used by
|
||||
reference counting of struct pid, while the pattern in listing B is used by
|
||||
struct posix_acl.
|
||||
|
|
|
@ -153,7 +153,7 @@ rcupdate.rcu_task_stall_timeout
|
|||
This boot/sysfs parameter controls the RCU-tasks stall warning
|
||||
interval. A value of zero or less suppresses RCU-tasks stall
|
||||
warnings. A positive value sets the stall-warning interval
|
||||
in jiffies. An RCU-tasks stall warning starts with the line:
|
||||
in seconds. An RCU-tasks stall warning starts with the line:
|
||||
|
||||
INFO: rcu_tasks detected stalls on tasks:
|
||||
|
||||
|
|
|
@ -212,7 +212,7 @@ synchronize_rcu()
|
|||
|
||||
rcu_assign_pointer()
|
||||
|
||||
typeof(p) rcu_assign_pointer(p, typeof(p) v);
|
||||
void rcu_assign_pointer(p, typeof(p) v);
|
||||
|
||||
Yes, rcu_assign_pointer() -is- implemented as a macro, though it
|
||||
would be cool to be able to declare a function in this manner.
|
||||
|
@ -220,9 +220,9 @@ rcu_assign_pointer()
|
|||
|
||||
The updater uses this function to assign a new value to an
|
||||
RCU-protected pointer, in order to safely communicate the change
|
||||
in value from the updater to the reader. This function returns
|
||||
the new value, and also executes any memory-barrier instructions
|
||||
required for a given CPU architecture.
|
||||
in value from the updater to the reader. This macro does not
|
||||
evaluate to an rvalue, but it does execute any memory-barrier
|
||||
instructions required for a given CPU architecture.
|
||||
|
||||
Perhaps just as important, it serves to document (1) which
|
||||
pointers are protected by RCU and (2) the point at which a
|
||||
|
|
|
@ -3,7 +3,7 @@ Circular Buffers
|
|||
================
|
||||
|
||||
:Author: David Howells <dhowells@redhat.com>
|
||||
:Author: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
|
||||
:Author: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
||||
|
||||
Linux provides a number of features that can be used to implement circular
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
============================
|
||||
|
||||
By: David Howells <dhowells@redhat.com>
|
||||
Paul E. McKenney <paulmck@linux.vnet.ibm.com>
|
||||
Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
Will Deacon <will.deacon@arm.com>
|
||||
Peter Zijlstra <peterz@infradead.org>
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ Documentation/memory-barriers.txt
|
|||
=========================
|
||||
|
||||
저자: David Howells <dhowells@redhat.com>
|
||||
Paul E. McKenney <paulmck@linux.vnet.ibm.com>
|
||||
Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
Will Deacon <will.deacon@arm.com>
|
||||
Peter Zijlstra <peterz@infradead.org>
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/rbtree_latch.h>
|
||||
#include <linux/error-injection.h>
|
||||
#include <linux/tracepoint-defs.h>
|
||||
#include <linux/srcu.h>
|
||||
|
||||
#include <linux/percpu.h>
|
||||
#include <asm/module.h>
|
||||
|
@ -450,6 +451,10 @@ struct module {
|
|||
unsigned int num_tracepoints;
|
||||
tracepoint_ptr_t *tracepoints_ptrs;
|
||||
#endif
|
||||
#ifdef CONFIG_TREE_SRCU
|
||||
unsigned int num_srcu_structs;
|
||||
struct srcu_struct **srcu_struct_ptrs;
|
||||
#endif
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
unsigned int num_bpf_raw_events;
|
||||
struct bpf_raw_event_map *bpf_raw_events;
|
||||
|
|
|
@ -17,14 +17,18 @@ struct percpu_rw_semaphore {
|
|||
int readers_block;
|
||||
};
|
||||
|
||||
#define DEFINE_STATIC_PERCPU_RWSEM(name) \
|
||||
#define __DEFINE_PERCPU_RWSEM(name, is_static) \
|
||||
static DEFINE_PER_CPU(unsigned int, __percpu_rwsem_rc_##name); \
|
||||
static struct percpu_rw_semaphore name = { \
|
||||
.rss = __RCU_SYNC_INITIALIZER(name.rss, RCU_SCHED_SYNC), \
|
||||
is_static struct percpu_rw_semaphore name = { \
|
||||
.rss = __RCU_SYNC_INITIALIZER(name.rss), \
|
||||
.read_count = &__percpu_rwsem_rc_##name, \
|
||||
.rw_sem = __RWSEM_INITIALIZER(name.rw_sem), \
|
||||
.writer = __RCUWAIT_INITIALIZER(name.writer), \
|
||||
}
|
||||
#define DEFINE_PERCPU_RWSEM(name) \
|
||||
__DEFINE_PERCPU_RWSEM(name, /* not static */)
|
||||
#define DEFINE_STATIC_PERCPU_RWSEM(name) \
|
||||
__DEFINE_PERCPU_RWSEM(name, static)
|
||||
|
||||
extern int __percpu_down_read(struct percpu_rw_semaphore *, int);
|
||||
extern void __percpu_up_read(struct percpu_rw_semaphore *);
|
||||
|
|
|
@ -13,62 +13,44 @@
|
|||
#include <linux/wait.h>
|
||||
#include <linux/rcupdate.h>
|
||||
|
||||
enum rcu_sync_type { RCU_SYNC, RCU_SCHED_SYNC, RCU_BH_SYNC };
|
||||
|
||||
/* Structure to mediate between updaters and fastpath-using readers. */
|
||||
struct rcu_sync {
|
||||
int gp_state;
|
||||
int gp_count;
|
||||
wait_queue_head_t gp_wait;
|
||||
|
||||
int cb_state;
|
||||
struct rcu_head cb_head;
|
||||
|
||||
enum rcu_sync_type gp_type;
|
||||
};
|
||||
|
||||
extern void rcu_sync_lockdep_assert(struct rcu_sync *);
|
||||
|
||||
/**
|
||||
* rcu_sync_is_idle() - Are readers permitted to use their fastpaths?
|
||||
* @rsp: Pointer to rcu_sync structure to use for synchronization
|
||||
*
|
||||
* Returns true if readers are permitted to use their fastpaths.
|
||||
* Must be invoked within an RCU read-side critical section whose
|
||||
* flavor matches that of the rcu_sync struture.
|
||||
* Returns true if readers are permitted to use their fastpaths. Must be
|
||||
* invoked within some flavor of RCU read-side critical section.
|
||||
*/
|
||||
static inline bool rcu_sync_is_idle(struct rcu_sync *rsp)
|
||||
{
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
rcu_sync_lockdep_assert(rsp);
|
||||
#endif
|
||||
return !rsp->gp_state; /* GP_IDLE */
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held() &&
|
||||
!rcu_read_lock_bh_held() &&
|
||||
!rcu_read_lock_sched_held(),
|
||||
"suspicious rcu_sync_is_idle() usage");
|
||||
return !READ_ONCE(rsp->gp_state); /* GP_IDLE */
|
||||
}
|
||||
|
||||
extern void rcu_sync_init(struct rcu_sync *, enum rcu_sync_type);
|
||||
extern void rcu_sync_init(struct rcu_sync *);
|
||||
extern void rcu_sync_enter_start(struct rcu_sync *);
|
||||
extern void rcu_sync_enter(struct rcu_sync *);
|
||||
extern void rcu_sync_exit(struct rcu_sync *);
|
||||
extern void rcu_sync_dtor(struct rcu_sync *);
|
||||
|
||||
#define __RCU_SYNC_INITIALIZER(name, type) { \
|
||||
#define __RCU_SYNC_INITIALIZER(name) { \
|
||||
.gp_state = 0, \
|
||||
.gp_count = 0, \
|
||||
.gp_wait = __WAIT_QUEUE_HEAD_INITIALIZER(name.gp_wait), \
|
||||
.cb_state = 0, \
|
||||
.gp_type = type, \
|
||||
}
|
||||
|
||||
#define __DEFINE_RCU_SYNC(name, type) \
|
||||
struct rcu_sync_struct name = __RCU_SYNC_INITIALIZER(name, type)
|
||||
|
||||
#define DEFINE_RCU_SYNC(name) \
|
||||
__DEFINE_RCU_SYNC(name, RCU_SYNC)
|
||||
|
||||
#define DEFINE_RCU_SCHED_SYNC(name) \
|
||||
__DEFINE_RCU_SYNC(name, RCU_SCHED_SYNC)
|
||||
|
||||
#define DEFINE_RCU_BH_SYNC(name) \
|
||||
__DEFINE_RCU_SYNC(name, RCU_BH_SYNC)
|
||||
#define DEFINE_RCU_SYNC(name) \
|
||||
struct rcu_sync name = __RCU_SYNC_INITIALIZER(name)
|
||||
|
||||
#endif /* _LINUX_RCU_SYNC_H_ */
|
||||
|
|
|
@ -367,16 +367,15 @@ static inline void rcu_preempt_sleep_check(void) { }
|
|||
* other macros that it invokes.
|
||||
*/
|
||||
#define rcu_assign_pointer(p, v) \
|
||||
({ \
|
||||
do { \
|
||||
uintptr_t _r_a_p__v = (uintptr_t)(v); \
|
||||
rcu_check_sparse(p, __rcu); \
|
||||
rcu_check_sparse(p, __rcu); \
|
||||
\
|
||||
if (__builtin_constant_p(v) && (_r_a_p__v) == (uintptr_t)NULL) \
|
||||
WRITE_ONCE((p), (typeof(p))(_r_a_p__v)); \
|
||||
else \
|
||||
smp_store_release(&p, RCU_INITIALIZER((typeof(p))_r_a_p__v)); \
|
||||
_r_a_p__v; \
|
||||
})
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* rcu_swap_protected() - swap an RCU and a regular pointer
|
||||
|
@ -588,7 +587,7 @@ static inline void rcu_preempt_sleep_check(void) { }
|
|||
* read-side critical sections may be preempted and they may also block, but
|
||||
* only when acquiring spinlocks that are subject to priority inheritance.
|
||||
*/
|
||||
static inline void rcu_read_lock(void)
|
||||
static __always_inline void rcu_read_lock(void)
|
||||
{
|
||||
__rcu_read_lock();
|
||||
__acquire(RCU);
|
||||
|
@ -805,7 +804,7 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
|
|||
/**
|
||||
* kfree_rcu() - kfree an object after a grace period.
|
||||
* @ptr: pointer to kfree
|
||||
* @rcu_head: the name of the struct rcu_head within the type of @ptr.
|
||||
* @rhf: the name of the struct rcu_head within the type of @ptr.
|
||||
*
|
||||
* Many rcu callbacks functions just call kfree() on the base structure.
|
||||
* These functions are trivial, but their size adds up, and furthermore
|
||||
|
@ -828,9 +827,13 @@ static inline notrace void rcu_read_unlock_sched_notrace(void)
|
|||
* The BUILD_BUG_ON check must not involve any function calls, hence the
|
||||
* checks are done in macros here.
|
||||
*/
|
||||
#define kfree_rcu(ptr, rcu_head) \
|
||||
__kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head))
|
||||
|
||||
#define kfree_rcu(ptr, rhf) \
|
||||
do { \
|
||||
typeof (ptr) ___p = (ptr); \
|
||||
\
|
||||
if (___p) \
|
||||
__kfree_rcu(&((___p)->rhf), offsetof(typeof(*(ptr)), rhf)); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* Place this after a lock-acquisition primitive to guarantee that
|
||||
|
|
|
@ -120,9 +120,17 @@ struct srcu_struct {
|
|||
*
|
||||
* See include/linux/percpu-defs.h for the rules on per-CPU variables.
|
||||
*/
|
||||
#define __DEFINE_SRCU(name, is_static) \
|
||||
static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data);\
|
||||
is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name, name##_srcu_data)
|
||||
#ifdef MODULE
|
||||
# define __DEFINE_SRCU(name, is_static) \
|
||||
is_static struct srcu_struct name; \
|
||||
struct srcu_struct * const __srcu_struct_##name \
|
||||
__section("___srcu_struct_ptrs") = &name
|
||||
#else
|
||||
# define __DEFINE_SRCU(name, is_static) \
|
||||
static DEFINE_PER_CPU(struct srcu_data, name##_srcu_data); \
|
||||
is_static struct srcu_struct name = \
|
||||
__SRCU_STRUCT_INIT(name, name##_srcu_data)
|
||||
#endif
|
||||
#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */)
|
||||
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static)
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ int torture_shutdown_init(int ssecs, void (*cleanup)(void));
|
|||
|
||||
/* Task stuttering, which forces load/no-load transitions. */
|
||||
bool stutter_wait(const char *title);
|
||||
int torture_stutter_init(int s);
|
||||
int torture_stutter_init(int s, int sgap);
|
||||
|
||||
/* Initialization and cleanup. */
|
||||
bool torture_init_begin(char *ttype, int v);
|
||||
|
|
|
@ -101,7 +101,7 @@ static DEFINE_SPINLOCK(cgroup_idr_lock);
|
|||
*/
|
||||
static DEFINE_SPINLOCK(cgroup_file_kn_lock);
|
||||
|
||||
struct percpu_rw_semaphore cgroup_threadgroup_rwsem;
|
||||
DEFINE_PERCPU_RWSEM(cgroup_threadgroup_rwsem);
|
||||
|
||||
#define cgroup_assert_mutex_or_rcu_locked() \
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held() && \
|
||||
|
@ -5616,7 +5616,6 @@ int __init cgroup_init(void)
|
|||
int ssid;
|
||||
|
||||
BUILD_BUG_ON(CGROUP_SUBSYS_COUNT > 16);
|
||||
BUG_ON(percpu_init_rwsem(&cgroup_threadgroup_rwsem));
|
||||
BUG_ON(cgroup_init_cftypes(NULL, cgroup_base_files));
|
||||
BUG_ON(cgroup_init_cftypes(NULL, cgroup1_base_files));
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */
|
|||
static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];
|
||||
#define uprobes_mmap_hash(v) (&uprobes_mmap_mutex[((unsigned long)(v)) % UPROBES_HASH_SZ])
|
||||
|
||||
static struct percpu_rw_semaphore dup_mmap_sem;
|
||||
DEFINE_STATIC_PERCPU_RWSEM(dup_mmap_sem);
|
||||
|
||||
/* Have a copy of original instruction */
|
||||
#define UPROBE_COPY_INSN 0
|
||||
|
@ -2302,7 +2302,5 @@ void __init uprobes_init(void)
|
|||
for (i = 0; i < UPROBES_HASH_SZ; i++)
|
||||
mutex_init(&uprobes_mmap_mutex[i]);
|
||||
|
||||
BUG_ON(percpu_init_rwsem(&dup_mmap_sem));
|
||||
|
||||
BUG_ON(register_die_notifier(&uprobe_exception_nb));
|
||||
}
|
||||
|
|
|
@ -975,7 +975,7 @@ static int __init lock_torture_init(void)
|
|||
goto unwind;
|
||||
}
|
||||
if (stutter > 0) {
|
||||
firsterr = torture_stutter_init(stutter);
|
||||
firsterr = torture_stutter_init(stutter, stutter);
|
||||
if (firsterr)
|
||||
goto unwind;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ int __percpu_init_rwsem(struct percpu_rw_semaphore *sem,
|
|||
return -ENOMEM;
|
||||
|
||||
/* ->rw_sem represents the whole percpu_rw_semaphore for lockdep */
|
||||
rcu_sync_init(&sem->rss, RCU_SCHED_SYNC);
|
||||
rcu_sync_init(&sem->rss);
|
||||
__init_rwsem(&sem->rw_sem, name, rwsem_key);
|
||||
rcuwait_init(&sem->writer);
|
||||
sem->readers_block = 0;
|
||||
|
|
|
@ -3095,6 +3095,11 @@ static int find_module_sections(struct module *mod, struct load_info *info)
|
|||
sizeof(*mod->tracepoints_ptrs),
|
||||
&mod->num_tracepoints);
|
||||
#endif
|
||||
#ifdef CONFIG_TREE_SRCU
|
||||
mod->srcu_struct_ptrs = section_objs(info, "___srcu_struct_ptrs",
|
||||
sizeof(*mod->srcu_struct_ptrs),
|
||||
&mod->num_srcu_structs);
|
||||
#endif
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
mod->bpf_raw_events = section_objs(info, "__bpf_raw_tp_map",
|
||||
sizeof(*mod->bpf_raw_events),
|
||||
|
|
|
@ -446,6 +446,7 @@ void rcu_request_urgent_qs_task(struct task_struct *t);
|
|||
enum rcutorture_type {
|
||||
RCU_FLAVOR,
|
||||
RCU_TASKS_FLAVOR,
|
||||
RCU_TRIVIAL_FLAVOR,
|
||||
SRCU_FLAVOR,
|
||||
INVALID_RCU_FLAVOR
|
||||
};
|
||||
|
@ -479,6 +480,10 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
|
|||
#endif
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)
|
||||
long rcutorture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_TINY_SRCU
|
||||
|
||||
static inline void srcutorture_get_gp_data(enum rcutorture_type test_type,
|
||||
|
|
|
@ -299,6 +299,7 @@ struct rcu_torture_ops {
|
|||
int irq_capable;
|
||||
int can_boost;
|
||||
int extendables;
|
||||
int slow_gps;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
|
@ -667,9 +668,51 @@ static struct rcu_torture_ops tasks_ops = {
|
|||
.fqs = NULL,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.slow_gps = 1,
|
||||
.name = "tasks"
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions for trivial CONFIG_PREEMPT=n-only torture testing.
|
||||
* This implementation does not necessarily work well with CPU hotplug.
|
||||
*/
|
||||
|
||||
static void synchronize_rcu_trivial(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
for_each_online_cpu(cpu) {
|
||||
rcutorture_sched_setaffinity(current->pid, cpumask_of(cpu));
|
||||
WARN_ON_ONCE(raw_smp_processor_id() != cpu);
|
||||
}
|
||||
}
|
||||
|
||||
static int rcu_torture_read_lock_trivial(void) __acquires(RCU)
|
||||
{
|
||||
preempt_disable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void rcu_torture_read_unlock_trivial(int idx) __releases(RCU)
|
||||
{
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static struct rcu_torture_ops trivial_ops = {
|
||||
.ttype = RCU_TRIVIAL_FLAVOR,
|
||||
.init = rcu_sync_torture_init,
|
||||
.readlock = rcu_torture_read_lock_trivial,
|
||||
.read_delay = rcu_read_delay, /* just reuse rcu's version. */
|
||||
.readunlock = rcu_torture_read_unlock_trivial,
|
||||
.get_gp_seq = rcu_no_completed,
|
||||
.sync = synchronize_rcu_trivial,
|
||||
.exp_sync = synchronize_rcu_trivial,
|
||||
.fqs = NULL,
|
||||
.stats = NULL,
|
||||
.irq_capable = 1,
|
||||
.name = "trivial"
|
||||
};
|
||||
|
||||
static unsigned long rcutorture_seq_diff(unsigned long new, unsigned long old)
|
||||
{
|
||||
if (!cur_ops->gp_diff)
|
||||
|
@ -1010,10 +1053,17 @@ rcu_torture_writer(void *arg)
|
|||
!rcu_gp_is_normal();
|
||||
}
|
||||
rcu_torture_writer_state = RTWS_STUTTER;
|
||||
if (stutter_wait("rcu_torture_writer"))
|
||||
if (stutter_wait("rcu_torture_writer") &&
|
||||
!READ_ONCE(rcu_fwd_cb_nodelay) &&
|
||||
!cur_ops->slow_gps &&
|
||||
!torture_must_stop())
|
||||
for (i = 0; i < ARRAY_SIZE(rcu_tortures); i++)
|
||||
if (list_empty(&rcu_tortures[i].rtort_free))
|
||||
WARN_ON_ONCE(1);
|
||||
if (list_empty(&rcu_tortures[i].rtort_free) &&
|
||||
rcu_access_pointer(rcu_torture_current) !=
|
||||
&rcu_tortures[i]) {
|
||||
rcu_ftrace_dump(DUMP_ALL);
|
||||
WARN(1, "%s: rtort_pipe_count: %d\n", __func__, rcu_tortures[i].rtort_pipe_count);
|
||||
}
|
||||
} while (!torture_must_stop());
|
||||
/* Reset expediting back to unexpedited. */
|
||||
if (expediting > 0)
|
||||
|
@ -1358,8 +1408,9 @@ rcu_torture_stats_print(void)
|
|||
}
|
||||
|
||||
pr_alert("%s%s ", torture_type, TORTURE_FLAG);
|
||||
pr_cont("rtc: %p ver: %lu tfle: %d rta: %d rtaf: %d rtf: %d ",
|
||||
pr_cont("rtc: %p %s: %lu tfle: %d rta: %d rtaf: %d rtf: %d ",
|
||||
rcu_torture_current,
|
||||
rcu_torture_current ? "ver" : "VER",
|
||||
rcu_torture_current_version,
|
||||
list_empty(&rcu_torture_freelist),
|
||||
atomic_read(&n_rcu_torture_alloc),
|
||||
|
@ -1661,6 +1712,17 @@ static void rcu_torture_fwd_cb_cr(struct rcu_head *rhp)
|
|||
spin_unlock_irqrestore(&rcu_fwd_lock, flags);
|
||||
}
|
||||
|
||||
// Give the scheduler a chance, even on nohz_full CPUs.
|
||||
static void rcu_torture_fwd_prog_cond_resched(void)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_PREEMPT) && IS_ENABLED(CONFIG_NO_HZ_FULL)) {
|
||||
if (need_resched())
|
||||
schedule();
|
||||
} else {
|
||||
cond_resched();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Free all callbacks on the rcu_fwd_cb_head list, either because the
|
||||
* test is over or because we hit an OOM event.
|
||||
|
@ -1674,16 +1736,18 @@ static unsigned long rcu_torture_fwd_prog_cbfree(void)
|
|||
for (;;) {
|
||||
spin_lock_irqsave(&rcu_fwd_lock, flags);
|
||||
rfcp = rcu_fwd_cb_head;
|
||||
if (!rfcp)
|
||||
if (!rfcp) {
|
||||
spin_unlock_irqrestore(&rcu_fwd_lock, flags);
|
||||
break;
|
||||
}
|
||||
rcu_fwd_cb_head = rfcp->rfc_next;
|
||||
if (!rcu_fwd_cb_head)
|
||||
rcu_fwd_cb_tail = &rcu_fwd_cb_head;
|
||||
spin_unlock_irqrestore(&rcu_fwd_lock, flags);
|
||||
kfree(rfcp);
|
||||
freed++;
|
||||
rcu_torture_fwd_prog_cond_resched();
|
||||
}
|
||||
spin_unlock_irqrestore(&rcu_fwd_lock, flags);
|
||||
return freed;
|
||||
}
|
||||
|
||||
|
@ -1707,6 +1771,8 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries)
|
|||
}
|
||||
|
||||
/* Tight loop containing cond_resched(). */
|
||||
WRITE_ONCE(rcu_fwd_cb_nodelay, true);
|
||||
cur_ops->sync(); /* Later readers see above write. */
|
||||
if (selfpropcb) {
|
||||
WRITE_ONCE(fcs.stop, 0);
|
||||
cur_ops->call(&fcs.rh, rcu_torture_fwd_prog_cb);
|
||||
|
@ -1724,7 +1790,7 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries)
|
|||
udelay(10);
|
||||
cur_ops->readunlock(idx);
|
||||
if (!fwd_progress_need_resched || need_resched())
|
||||
cond_resched();
|
||||
rcu_torture_fwd_prog_cond_resched();
|
||||
}
|
||||
(*tested_tries)++;
|
||||
if (!time_before(jiffies, stopat) &&
|
||||
|
@ -1745,6 +1811,8 @@ static void rcu_torture_fwd_prog_nr(int *tested, int *tested_tries)
|
|||
WARN_ON(READ_ONCE(fcs.stop) != 2);
|
||||
destroy_rcu_head_on_stack(&fcs.rh);
|
||||
}
|
||||
schedule_timeout_uninterruptible(HZ / 10); /* Let kthreads recover. */
|
||||
WRITE_ONCE(rcu_fwd_cb_nodelay, false);
|
||||
}
|
||||
|
||||
/* Carry out call_rcu() forward-progress testing. */
|
||||
|
@ -1765,6 +1833,8 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
|
||||
if (READ_ONCE(rcu_fwd_emergency_stop))
|
||||
return; /* Get out of the way quickly, no GP wait! */
|
||||
if (!cur_ops->call)
|
||||
return; /* Can't do call_rcu() fwd prog without ->call. */
|
||||
|
||||
/* Loop continuously posting RCU callbacks. */
|
||||
WRITE_ONCE(rcu_fwd_cb_nodelay, true);
|
||||
|
@ -1805,7 +1875,7 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
rfcp->rfc_gps = 0;
|
||||
}
|
||||
cur_ops->call(&rfcp->rh, rcu_torture_fwd_cb_cr);
|
||||
cond_resched();
|
||||
rcu_torture_fwd_prog_cond_resched();
|
||||
}
|
||||
stoppedat = jiffies;
|
||||
n_launders_cb_snap = READ_ONCE(n_launders_cb);
|
||||
|
@ -1814,7 +1884,6 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */
|
||||
(void)rcu_torture_fwd_prog_cbfree();
|
||||
|
||||
WRITE_ONCE(rcu_fwd_cb_nodelay, false);
|
||||
if (!torture_must_stop() && !READ_ONCE(rcu_fwd_emergency_stop)) {
|
||||
WARN_ON(n_max_gps < MIN_FWD_CBS_LAUNDERED);
|
||||
pr_alert("%s Duration %lu barrier: %lu pending %ld n_launders: %ld n_launders_sa: %ld n_max_gps: %ld n_max_cbs: %ld cver %ld gps %ld\n",
|
||||
|
@ -1825,6 +1894,8 @@ static void rcu_torture_fwd_prog_cr(void)
|
|||
n_max_gps, n_max_cbs, cver, gps);
|
||||
rcu_torture_fwd_cb_hist();
|
||||
}
|
||||
schedule_timeout_uninterruptible(HZ); /* Let CBs drain. */
|
||||
WRITE_ONCE(rcu_fwd_cb_nodelay, false);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2240,7 +2311,7 @@ rcu_torture_init(void)
|
|||
int firsterr = 0;
|
||||
static struct rcu_torture_ops *torture_ops[] = {
|
||||
&rcu_ops, &rcu_busted_ops, &srcu_ops, &srcud_ops,
|
||||
&busted_srcud_ops, &tasks_ops,
|
||||
&busted_srcud_ops, &tasks_ops, &trivial_ops,
|
||||
};
|
||||
|
||||
if (!torture_init_begin(torture_type, verbose))
|
||||
|
@ -2363,7 +2434,10 @@ rcu_torture_init(void)
|
|||
if (stutter < 0)
|
||||
stutter = 0;
|
||||
if (stutter) {
|
||||
firsterr = torture_stutter_init(stutter * HZ);
|
||||
int t;
|
||||
|
||||
t = cur_ops->stall_dur ? cur_ops->stall_dur() : stutter * HZ;
|
||||
firsterr = torture_stutter_init(stutter * HZ, t);
|
||||
if (firsterr)
|
||||
goto unwind;
|
||||
}
|
||||
|
|
|
@ -831,8 +831,8 @@ static void srcu_leak_callback(struct rcu_head *rhp)
|
|||
* srcu_read_lock(), and srcu_read_unlock() that are all passed the same
|
||||
* srcu_struct structure.
|
||||
*/
|
||||
void __call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
|
||||
rcu_callback_t func, bool do_norm)
|
||||
static void __call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
|
||||
rcu_callback_t func, bool do_norm)
|
||||
{
|
||||
unsigned long flags;
|
||||
int idx;
|
||||
|
@ -1310,3 +1310,68 @@ void __init srcu_init(void)
|
|||
queue_work(rcu_gp_wq, &ssp->work.work);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
|
||||
/* Initialize any global-scope srcu_struct structures used by this module. */
|
||||
static int srcu_module_coming(struct module *mod)
|
||||
{
|
||||
int i;
|
||||
struct srcu_struct **sspp = mod->srcu_struct_ptrs;
|
||||
int ret;
|
||||
|
||||
for (i = 0; i < mod->num_srcu_structs; i++) {
|
||||
ret = init_srcu_struct(*(sspp++));
|
||||
if (WARN_ON_ONCE(ret))
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Clean up any global-scope srcu_struct structures used by this module. */
|
||||
static void srcu_module_going(struct module *mod)
|
||||
{
|
||||
int i;
|
||||
struct srcu_struct **sspp = mod->srcu_struct_ptrs;
|
||||
|
||||
for (i = 0; i < mod->num_srcu_structs; i++)
|
||||
cleanup_srcu_struct(*(sspp++));
|
||||
}
|
||||
|
||||
/* Handle one module, either coming or going. */
|
||||
static int srcu_module_notify(struct notifier_block *self,
|
||||
unsigned long val, void *data)
|
||||
{
|
||||
struct module *mod = data;
|
||||
int ret = 0;
|
||||
|
||||
switch (val) {
|
||||
case MODULE_STATE_COMING:
|
||||
ret = srcu_module_coming(mod);
|
||||
break;
|
||||
case MODULE_STATE_GOING:
|
||||
srcu_module_going(mod);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct notifier_block srcu_module_nb = {
|
||||
.notifier_call = srcu_module_notify,
|
||||
.priority = 0,
|
||||
};
|
||||
|
||||
static __init int init_srcu_module_notifier(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = register_module_notifier(&srcu_module_nb);
|
||||
if (ret)
|
||||
pr_warn("Failed to register srcu module notifier\n");
|
||||
return ret;
|
||||
}
|
||||
late_initcall(init_srcu_module_notifier);
|
||||
|
||||
#endif /* #ifdef CONFIG_MODULES */
|
||||
|
|
|
@ -10,65 +10,18 @@
|
|||
#include <linux/rcu_sync.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
#define __INIT_HELD(func) .held = func,
|
||||
#else
|
||||
#define __INIT_HELD(func)
|
||||
#endif
|
||||
|
||||
static const struct {
|
||||
void (*sync)(void);
|
||||
void (*call)(struct rcu_head *, void (*)(struct rcu_head *));
|
||||
void (*wait)(void);
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
int (*held)(void);
|
||||
#endif
|
||||
} gp_ops[] = {
|
||||
[RCU_SYNC] = {
|
||||
.sync = synchronize_rcu,
|
||||
.call = call_rcu,
|
||||
.wait = rcu_barrier,
|
||||
__INIT_HELD(rcu_read_lock_held)
|
||||
},
|
||||
[RCU_SCHED_SYNC] = {
|
||||
.sync = synchronize_rcu,
|
||||
.call = call_rcu,
|
||||
.wait = rcu_barrier,
|
||||
__INIT_HELD(rcu_read_lock_sched_held)
|
||||
},
|
||||
[RCU_BH_SYNC] = {
|
||||
.sync = synchronize_rcu,
|
||||
.call = call_rcu,
|
||||
.wait = rcu_barrier,
|
||||
__INIT_HELD(rcu_read_lock_bh_held)
|
||||
},
|
||||
};
|
||||
|
||||
enum { GP_IDLE = 0, GP_PENDING, GP_PASSED };
|
||||
enum { CB_IDLE = 0, CB_PENDING, CB_REPLAY };
|
||||
enum { GP_IDLE = 0, GP_ENTER, GP_PASSED, GP_EXIT, GP_REPLAY };
|
||||
|
||||
#define rss_lock gp_wait.lock
|
||||
|
||||
#ifdef CONFIG_PROVE_RCU
|
||||
void rcu_sync_lockdep_assert(struct rcu_sync *rsp)
|
||||
{
|
||||
RCU_LOCKDEP_WARN(!gp_ops[rsp->gp_type].held(),
|
||||
"suspicious rcu_sync_is_idle() usage");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL_GPL(rcu_sync_lockdep_assert);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* rcu_sync_init() - Initialize an rcu_sync structure
|
||||
* @rsp: Pointer to rcu_sync structure to be initialized
|
||||
* @type: Flavor of RCU with which to synchronize rcu_sync structure
|
||||
*/
|
||||
void rcu_sync_init(struct rcu_sync *rsp, enum rcu_sync_type type)
|
||||
void rcu_sync_init(struct rcu_sync *rsp)
|
||||
{
|
||||
memset(rsp, 0, sizeof(*rsp));
|
||||
init_waitqueue_head(&rsp->gp_wait);
|
||||
rsp->gp_type = type;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,6 +39,70 @@ void rcu_sync_enter_start(struct rcu_sync *rsp)
|
|||
rsp->gp_state = GP_PASSED;
|
||||
}
|
||||
|
||||
|
||||
static void rcu_sync_func(struct rcu_head *rhp);
|
||||
|
||||
static void rcu_sync_call(struct rcu_sync *rsp)
|
||||
{
|
||||
call_rcu(&rsp->cb_head, rcu_sync_func);
|
||||
}
|
||||
|
||||
/**
|
||||
* rcu_sync_func() - Callback function managing reader access to fastpath
|
||||
* @rhp: Pointer to rcu_head in rcu_sync structure to use for synchronization
|
||||
*
|
||||
* This function is passed to call_rcu() function by rcu_sync_enter() and
|
||||
* rcu_sync_exit(), so that it is invoked after a grace period following the
|
||||
* that invocation of enter/exit.
|
||||
*
|
||||
* If it is called by rcu_sync_enter() it signals that all the readers were
|
||||
* switched onto slow path.
|
||||
*
|
||||
* If it is called by rcu_sync_exit() it takes action based on events that
|
||||
* have taken place in the meantime, so that closely spaced rcu_sync_enter()
|
||||
* and rcu_sync_exit() pairs need not wait for a grace period.
|
||||
*
|
||||
* If another rcu_sync_enter() is invoked before the grace period
|
||||
* ended, reset state to allow the next rcu_sync_exit() to let the
|
||||
* readers back onto their fastpaths (after a grace period). If both
|
||||
* another rcu_sync_enter() and its matching rcu_sync_exit() are invoked
|
||||
* before the grace period ended, re-invoke call_rcu() on behalf of that
|
||||
* rcu_sync_exit(). Otherwise, set all state back to idle so that readers
|
||||
* can again use their fastpaths.
|
||||
*/
|
||||
static void rcu_sync_func(struct rcu_head *rhp)
|
||||
{
|
||||
struct rcu_sync *rsp = container_of(rhp, struct rcu_sync, cb_head);
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_IDLE);
|
||||
WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_PASSED);
|
||||
|
||||
spin_lock_irqsave(&rsp->rss_lock, flags);
|
||||
if (rsp->gp_count) {
|
||||
/*
|
||||
* We're at least a GP after the GP_IDLE->GP_ENTER transition.
|
||||
*/
|
||||
WRITE_ONCE(rsp->gp_state, GP_PASSED);
|
||||
wake_up_locked(&rsp->gp_wait);
|
||||
} else if (rsp->gp_state == GP_REPLAY) {
|
||||
/*
|
||||
* A new rcu_sync_exit() has happened; requeue the callback to
|
||||
* catch a later GP.
|
||||
*/
|
||||
WRITE_ONCE(rsp->gp_state, GP_EXIT);
|
||||
rcu_sync_call(rsp);
|
||||
} else {
|
||||
/*
|
||||
* We're at least a GP after the last rcu_sync_exit(); eveybody
|
||||
* will now have observed the write side critical section.
|
||||
* Let 'em rip!.
|
||||
*/
|
||||
WRITE_ONCE(rsp->gp_state, GP_IDLE);
|
||||
}
|
||||
spin_unlock_irqrestore(&rsp->rss_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* rcu_sync_enter() - Force readers onto slowpath
|
||||
* @rsp: Pointer to rcu_sync structure to use for synchronization
|
||||
|
@ -103,84 +120,43 @@ void rcu_sync_enter_start(struct rcu_sync *rsp)
|
|||
*/
|
||||
void rcu_sync_enter(struct rcu_sync *rsp)
|
||||
{
|
||||
bool need_wait, need_sync;
|
||||
int gp_state;
|
||||
|
||||
spin_lock_irq(&rsp->rss_lock);
|
||||
need_wait = rsp->gp_count++;
|
||||
need_sync = rsp->gp_state == GP_IDLE;
|
||||
if (need_sync)
|
||||
rsp->gp_state = GP_PENDING;
|
||||
gp_state = rsp->gp_state;
|
||||
if (gp_state == GP_IDLE) {
|
||||
WRITE_ONCE(rsp->gp_state, GP_ENTER);
|
||||
WARN_ON_ONCE(rsp->gp_count);
|
||||
/*
|
||||
* Note that we could simply do rcu_sync_call(rsp) here and
|
||||
* avoid the "if (gp_state == GP_IDLE)" block below.
|
||||
*
|
||||
* However, synchronize_rcu() can be faster if rcu_expedited
|
||||
* or rcu_blocking_is_gp() is true.
|
||||
*
|
||||
* Another reason is that we can't wait for rcu callback if
|
||||
* we are called at early boot time but this shouldn't happen.
|
||||
*/
|
||||
}
|
||||
rsp->gp_count++;
|
||||
spin_unlock_irq(&rsp->rss_lock);
|
||||
|
||||
WARN_ON_ONCE(need_wait && need_sync);
|
||||
if (need_sync) {
|
||||
gp_ops[rsp->gp_type].sync();
|
||||
rsp->gp_state = GP_PASSED;
|
||||
wake_up_all(&rsp->gp_wait);
|
||||
} else if (need_wait) {
|
||||
wait_event(rsp->gp_wait, rsp->gp_state == GP_PASSED);
|
||||
} else {
|
||||
if (gp_state == GP_IDLE) {
|
||||
/*
|
||||
* Possible when there's a pending CB from a rcu_sync_exit().
|
||||
* Nobody has yet been allowed the 'fast' path and thus we can
|
||||
* avoid doing any sync(). The callback will get 'dropped'.
|
||||
* See the comment above, this simply does the "synchronous"
|
||||
* call_rcu(rcu_sync_func) which does GP_ENTER -> GP_PASSED.
|
||||
*/
|
||||
WARN_ON_ONCE(rsp->gp_state != GP_PASSED);
|
||||
synchronize_rcu();
|
||||
rcu_sync_func(&rsp->cb_head);
|
||||
/* Not really needed, wait_event() would see GP_PASSED. */
|
||||
return;
|
||||
}
|
||||
|
||||
wait_event(rsp->gp_wait, READ_ONCE(rsp->gp_state) >= GP_PASSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* rcu_sync_func() - Callback function managing reader access to fastpath
|
||||
* @rhp: Pointer to rcu_head in rcu_sync structure to use for synchronization
|
||||
*
|
||||
* This function is passed to one of the call_rcu() functions by
|
||||
* rcu_sync_exit(), so that it is invoked after a grace period following the
|
||||
* that invocation of rcu_sync_exit(). It takes action based on events that
|
||||
* have taken place in the meantime, so that closely spaced rcu_sync_enter()
|
||||
* and rcu_sync_exit() pairs need not wait for a grace period.
|
||||
*
|
||||
* If another rcu_sync_enter() is invoked before the grace period
|
||||
* ended, reset state to allow the next rcu_sync_exit() to let the
|
||||
* readers back onto their fastpaths (after a grace period). If both
|
||||
* another rcu_sync_enter() and its matching rcu_sync_exit() are invoked
|
||||
* before the grace period ended, re-invoke call_rcu() on behalf of that
|
||||
* rcu_sync_exit(). Otherwise, set all state back to idle so that readers
|
||||
* can again use their fastpaths.
|
||||
*/
|
||||
static void rcu_sync_func(struct rcu_head *rhp)
|
||||
{
|
||||
struct rcu_sync *rsp = container_of(rhp, struct rcu_sync, cb_head);
|
||||
unsigned long flags;
|
||||
|
||||
WARN_ON_ONCE(rsp->gp_state != GP_PASSED);
|
||||
WARN_ON_ONCE(rsp->cb_state == CB_IDLE);
|
||||
|
||||
spin_lock_irqsave(&rsp->rss_lock, flags);
|
||||
if (rsp->gp_count) {
|
||||
/*
|
||||
* A new rcu_sync_begin() has happened; drop the callback.
|
||||
*/
|
||||
rsp->cb_state = CB_IDLE;
|
||||
} else if (rsp->cb_state == CB_REPLAY) {
|
||||
/*
|
||||
* A new rcu_sync_exit() has happened; requeue the callback
|
||||
* to catch a later GP.
|
||||
*/
|
||||
rsp->cb_state = CB_PENDING;
|
||||
gp_ops[rsp->gp_type].call(&rsp->cb_head, rcu_sync_func);
|
||||
} else {
|
||||
/*
|
||||
* We're at least a GP after rcu_sync_exit(); eveybody will now
|
||||
* have observed the write side critical section. Let 'em rip!.
|
||||
*/
|
||||
rsp->cb_state = CB_IDLE;
|
||||
rsp->gp_state = GP_IDLE;
|
||||
}
|
||||
spin_unlock_irqrestore(&rsp->rss_lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* rcu_sync_exit() - Allow readers back onto fast patch after grace period
|
||||
* rcu_sync_exit() - Allow readers back onto fast path after grace period
|
||||
* @rsp: Pointer to rcu_sync structure to use for synchronization
|
||||
*
|
||||
* This function is used by updaters who have completed, and can therefore
|
||||
|
@ -191,13 +167,16 @@ static void rcu_sync_func(struct rcu_head *rhp)
|
|||
*/
|
||||
void rcu_sync_exit(struct rcu_sync *rsp)
|
||||
{
|
||||
WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_IDLE);
|
||||
WARN_ON_ONCE(READ_ONCE(rsp->gp_count) == 0);
|
||||
|
||||
spin_lock_irq(&rsp->rss_lock);
|
||||
if (!--rsp->gp_count) {
|
||||
if (rsp->cb_state == CB_IDLE) {
|
||||
rsp->cb_state = CB_PENDING;
|
||||
gp_ops[rsp->gp_type].call(&rsp->cb_head, rcu_sync_func);
|
||||
} else if (rsp->cb_state == CB_PENDING) {
|
||||
rsp->cb_state = CB_REPLAY;
|
||||
if (rsp->gp_state == GP_PASSED) {
|
||||
WRITE_ONCE(rsp->gp_state, GP_EXIT);
|
||||
rcu_sync_call(rsp);
|
||||
} else if (rsp->gp_state == GP_EXIT) {
|
||||
WRITE_ONCE(rsp->gp_state, GP_REPLAY);
|
||||
}
|
||||
}
|
||||
spin_unlock_irq(&rsp->rss_lock);
|
||||
|
@ -209,18 +188,19 @@ void rcu_sync_exit(struct rcu_sync *rsp)
|
|||
*/
|
||||
void rcu_sync_dtor(struct rcu_sync *rsp)
|
||||
{
|
||||
int cb_state;
|
||||
int gp_state;
|
||||
|
||||
WARN_ON_ONCE(rsp->gp_count);
|
||||
WARN_ON_ONCE(READ_ONCE(rsp->gp_count));
|
||||
WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_PASSED);
|
||||
|
||||
spin_lock_irq(&rsp->rss_lock);
|
||||
if (rsp->cb_state == CB_REPLAY)
|
||||
rsp->cb_state = CB_PENDING;
|
||||
cb_state = rsp->cb_state;
|
||||
if (rsp->gp_state == GP_REPLAY)
|
||||
WRITE_ONCE(rsp->gp_state, GP_EXIT);
|
||||
gp_state = rsp->gp_state;
|
||||
spin_unlock_irq(&rsp->rss_lock);
|
||||
|
||||
if (cb_state != CB_IDLE) {
|
||||
gp_ops[rsp->gp_type].wait();
|
||||
WARN_ON_ONCE(rsp->cb_state != CB_IDLE);
|
||||
if (gp_state != GP_IDLE) {
|
||||
rcu_barrier();
|
||||
WARN_ON_ONCE(rsp->gp_state != GP_IDLE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -401,7 +401,8 @@ static int rcu_is_cpu_rrupt_from_idle(void)
|
|||
return __this_cpu_read(rcu_data.dynticks_nesting) == 0;
|
||||
}
|
||||
|
||||
#define DEFAULT_RCU_BLIMIT 10 /* Maximum callbacks per rcu_do_batch. */
|
||||
#define DEFAULT_RCU_BLIMIT 10 /* Maximum callbacks per rcu_do_batch ... */
|
||||
#define DEFAULT_MAX_RCU_BLIMIT 10000 /* ... even during callback flood. */
|
||||
static long blimit = DEFAULT_RCU_BLIMIT;
|
||||
#define DEFAULT_RCU_QHIMARK 10000 /* If this many pending, ignore blimit. */
|
||||
static long qhimark = DEFAULT_RCU_QHIMARK;
|
||||
|
@ -2134,7 +2135,7 @@ static void rcu_do_batch(struct rcu_data *rdp)
|
|||
|
||||
/* Reinstate batch limit if we have worked down the excess. */
|
||||
count = rcu_segcblist_n_cbs(&rdp->cblist);
|
||||
if (rdp->blimit == LONG_MAX && count <= qlowmark)
|
||||
if (rdp->blimit >= DEFAULT_MAX_RCU_BLIMIT && count <= qlowmark)
|
||||
rdp->blimit = blimit;
|
||||
|
||||
/* Reset ->qlen_last_fqs_check trigger if enough CBs have drained. */
|
||||
|
@ -2464,7 +2465,7 @@ static void __call_rcu_core(struct rcu_data *rdp, struct rcu_head *head,
|
|||
rcu_accelerate_cbs_unlocked(rdp->mynode, rdp);
|
||||
} else {
|
||||
/* Give the grace period a kick. */
|
||||
rdp->blimit = LONG_MAX;
|
||||
rdp->blimit = DEFAULT_MAX_RCU_BLIMIT;
|
||||
if (rcu_state.n_force_qs == rdp->n_force_qs_snap &&
|
||||
rcu_segcblist_first_pend_cb(&rdp->cblist) != head)
|
||||
rcu_force_quiescent_state();
|
||||
|
|
|
@ -259,8 +259,7 @@ static bool sync_exp_work_done(unsigned long s)
|
|||
{
|
||||
if (rcu_exp_gp_seq_done(s)) {
|
||||
trace_rcu_exp_grace_period(rcu_state.name, s, TPS("done"));
|
||||
/* Ensure test happens before caller kfree(). */
|
||||
smp_mb__before_atomic(); /* ^^^ */
|
||||
smp_mb(); /* Ensure test happens before caller kfree(). */
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -777,7 +777,7 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck)
|
|||
i = 0;
|
||||
list_for_each(lhp, &rnp->blkd_tasks) {
|
||||
pr_cont(" %p", lhp);
|
||||
if (++i >= 10)
|
||||
if (++i >= ncheck)
|
||||
break;
|
||||
}
|
||||
pr_cont("\n");
|
||||
|
|
|
@ -630,7 +630,9 @@ static void rcu_check_gp_start_stall(struct rcu_node *rnp, struct rcu_data *rdp,
|
|||
time_before(j, rcu_state.gp_req_activity + gpssdelay) ||
|
||||
time_before(j, rcu_state.gp_activity + gpssdelay) ||
|
||||
atomic_xchg(&warned, 1)) {
|
||||
raw_spin_unlock_rcu_node(rnp_root); /* irqs remain disabled. */
|
||||
if (rnp_root != rnp)
|
||||
/* irqs remain disabled. */
|
||||
raw_spin_unlock_rcu_node(rnp_root);
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -423,6 +423,19 @@ EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
|
|||
do { } while (0)
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST) || IS_MODULE(CONFIG_RCU_TORTURE_TEST)
|
||||
/* Get rcutorture access to sched_setaffinity(). */
|
||||
long rcutorture_sched_setaffinity(pid_t pid, const struct cpumask *in_mask)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = sched_setaffinity(pid, in_mask);
|
||||
WARN_ONCE(ret, "%s: sched_setaffinity() returned %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcutorture_sched_setaffinity);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RCU_STALL_COMMON
|
||||
int rcu_cpu_stall_suppress __read_mostly; /* 1 = suppress stall warnings. */
|
||||
EXPORT_SYMBOL_GPL(rcu_cpu_stall_suppress);
|
||||
|
|
|
@ -570,6 +570,7 @@ static void torture_shutdown_cleanup(void)
|
|||
static struct task_struct *stutter_task;
|
||||
static int stutter_pause_test;
|
||||
static int stutter;
|
||||
static int stutter_gap;
|
||||
|
||||
/*
|
||||
* Block until the stutter interval ends. This must be called periodically
|
||||
|
@ -578,10 +579,12 @@ static int stutter;
|
|||
bool stutter_wait(const char *title)
|
||||
{
|
||||
int spt;
|
||||
bool ret = false;
|
||||
|
||||
cond_resched_tasks_rcu_qs();
|
||||
spt = READ_ONCE(stutter_pause_test);
|
||||
for (; spt; spt = READ_ONCE(stutter_pause_test)) {
|
||||
ret = true;
|
||||
if (spt == 1) {
|
||||
schedule_timeout_interruptible(1);
|
||||
} else if (spt == 2) {
|
||||
|
@ -592,7 +595,7 @@ bool stutter_wait(const char *title)
|
|||
}
|
||||
torture_shutdown_absorb(title);
|
||||
}
|
||||
return !!spt;
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(stutter_wait);
|
||||
|
||||
|
@ -602,17 +605,24 @@ EXPORT_SYMBOL_GPL(stutter_wait);
|
|||
*/
|
||||
static int torture_stutter(void *arg)
|
||||
{
|
||||
int wtime;
|
||||
|
||||
VERBOSE_TOROUT_STRING("torture_stutter task started");
|
||||
do {
|
||||
if (!torture_must_stop() && stutter > 1) {
|
||||
WRITE_ONCE(stutter_pause_test, 1);
|
||||
schedule_timeout_interruptible(stutter - 1);
|
||||
wtime = stutter;
|
||||
if (stutter > HZ + 1) {
|
||||
WRITE_ONCE(stutter_pause_test, 1);
|
||||
wtime = stutter - HZ - 1;
|
||||
schedule_timeout_interruptible(wtime);
|
||||
wtime = HZ + 1;
|
||||
}
|
||||
WRITE_ONCE(stutter_pause_test, 2);
|
||||
schedule_timeout_interruptible(1);
|
||||
schedule_timeout_interruptible(wtime);
|
||||
}
|
||||
WRITE_ONCE(stutter_pause_test, 0);
|
||||
if (!torture_must_stop())
|
||||
schedule_timeout_interruptible(stutter);
|
||||
schedule_timeout_interruptible(stutter_gap);
|
||||
torture_shutdown_absorb("torture_stutter");
|
||||
} while (!torture_must_stop());
|
||||
torture_kthread_stopping("torture_stutter");
|
||||
|
@ -622,9 +632,10 @@ static int torture_stutter(void *arg)
|
|||
/*
|
||||
* Initialize and kick off the torture_stutter kthread.
|
||||
*/
|
||||
int torture_stutter_init(const int s)
|
||||
int torture_stutter_init(const int s, const int sgap)
|
||||
{
|
||||
stutter = s;
|
||||
stutter_gap = sgap;
|
||||
return torture_create_kthread(torture_stutter, NULL, stutter_task);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(torture_stutter_init);
|
||||
|
|
|
@ -19,7 +19,7 @@ static inline bool rcu_is_watching(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
#define rcu_assign_pointer(p, v) ((p) = (v))
|
||||
#define RCU_INIT_POINTER(p, v) p=(v)
|
||||
#define rcu_assign_pointer(p, v) do { (p) = (v); } while (0)
|
||||
#define RCU_INIT_POINTER(p, v) do { (p) = (v); } while (0)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -7,6 +7,6 @@
|
|||
#define rcu_dereference_raw(p) rcu_dereference(p)
|
||||
#define rcu_dereference_protected(p, cond) rcu_dereference(p)
|
||||
#define rcu_dereference_check(p, cond) rcu_dereference(p)
|
||||
#define RCU_INIT_POINTER(p, v) (p) = (v)
|
||||
#define RCU_INIT_POINTER(p, v) do { (p) = (v); } while (0)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# SPDX-License-Identifier: GPL-2.0+
|
||||
all:
|
||||
( cd ../../../..; tools/testing/selftests/rcutorture/bin/kvm.sh --duration 10 --configs TREE01 )
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0+
|
||||
#
|
||||
# Usage: configinit.sh config-spec-file build-output-dir results-dir
|
||||
# Usage: configinit.sh config-spec-file results-dir
|
||||
#
|
||||
# Create a .config file from the spec file. Run from the kernel source tree.
|
||||
# Exits with 0 if all went well, with 1 if all went well but the config
|
||||
|
@ -11,10 +11,6 @@
|
|||
# desired settings, for example, "CONFIG_NO_HZ=y". For best results,
|
||||
# this should be a full pathname.
|
||||
#
|
||||
# The second argument is a optional path to a build output directory,
|
||||
# for example, "O=/tmp/foo". If this argument is omitted, the .config
|
||||
# file will be generated directly in the current directory.
|
||||
#
|
||||
# Copyright (C) IBM Corporation, 2013
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
@ -26,34 +22,23 @@ mkdir $T
|
|||
# Capture config spec file.
|
||||
|
||||
c=$1
|
||||
buildloc=$2
|
||||
resdir=$3
|
||||
builddir=
|
||||
if echo $buildloc | grep -q '^O='
|
||||
then
|
||||
builddir=`echo $buildloc | sed -e 's/^O=//'`
|
||||
if test ! -d $builddir
|
||||
then
|
||||
mkdir $builddir
|
||||
fi
|
||||
else
|
||||
echo Bad build directory: \"$buildloc\"
|
||||
exit 2
|
||||
fi
|
||||
resdir=$2
|
||||
|
||||
sed -e 's/^\(CONFIG[0-9A-Z_]*\)=.*$/grep -v "^# \1" |/' < $c > $T/u.sh
|
||||
sed -e 's/^\(CONFIG[0-9A-Z_]*=\).*$/grep -v \1 |/' < $c >> $T/u.sh
|
||||
grep '^grep' < $T/u.sh > $T/upd.sh
|
||||
echo "cat - $c" >> $T/upd.sh
|
||||
make mrproper
|
||||
make $buildloc distclean > $resdir/Make.distclean 2>&1
|
||||
make $buildloc $TORTURE_DEFCONFIG > $resdir/Make.defconfig.out 2>&1
|
||||
mv $builddir/.config $builddir/.config.sav
|
||||
sh $T/upd.sh < $builddir/.config.sav > $builddir/.config
|
||||
cp $builddir/.config $builddir/.config.new
|
||||
yes '' | make $buildloc oldconfig > $resdir/Make.oldconfig.out 2> $resdir/Make.oldconfig.err
|
||||
if test -z "$TORTURE_TRUST_MAKE"
|
||||
then
|
||||
make clean > $resdir/Make.clean 2>&1
|
||||
fi
|
||||
make $TORTURE_DEFCONFIG > $resdir/Make.defconfig.out 2>&1
|
||||
mv .config .config.sav
|
||||
sh $T/upd.sh < .config.sav > .config
|
||||
cp .config .config.new
|
||||
yes '' | make oldconfig > $resdir/Make.oldconfig.out 2> $resdir/Make.oldconfig.err
|
||||
|
||||
# verify new config matches specification.
|
||||
configcheck.sh $builddir/.config $c
|
||||
configcheck.sh .config $c
|
||||
|
||||
exit 0
|
||||
|
|
|
@ -9,6 +9,11 @@
|
|||
#
|
||||
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
||||
if test -n "$TORTURE_ALLOTED_CPUS"
|
||||
then
|
||||
echo $TORTURE_ALLOTED_CPUS
|
||||
exit 0
|
||||
fi
|
||||
ncpus=`grep '^processor' /proc/cpuinfo | wc -l`
|
||||
idlecpus=`mpstat | tail -1 | \
|
||||
awk -v ncpus=$ncpus '{ print ncpus * ($7 + $NF) / 100 }'`
|
||||
|
|
|
@ -172,7 +172,7 @@ identify_qemu_append () {
|
|||
local console=ttyS0
|
||||
case "$1" in
|
||||
qemu-system-x86_64|qemu-system-i386)
|
||||
echo noapic selinux=0 initcall_debug debug
|
||||
echo selinux=0 initcall_debug debug
|
||||
;;
|
||||
qemu-system-aarch64)
|
||||
console=ttyAMA0
|
||||
|
@ -191,8 +191,19 @@ identify_qemu_append () {
|
|||
# Output arguments for qemu arguments based on the TORTURE_QEMU_MAC
|
||||
# and TORTURE_QEMU_INTERACTIVE environment variables.
|
||||
identify_qemu_args () {
|
||||
local KVM_CPU=""
|
||||
case "$1" in
|
||||
qemu-system-x86_64)
|
||||
KVM_CPU=kvm64
|
||||
;;
|
||||
qemu-system-i386)
|
||||
KVM_CPU=kvm32
|
||||
;;
|
||||
esac
|
||||
case "$1" in
|
||||
qemu-system-x86_64|qemu-system-i386)
|
||||
echo -machine q35,accel=kvm
|
||||
echo -cpu ${KVM_CPU}
|
||||
;;
|
||||
qemu-system-aarch64)
|
||||
echo -machine virt,gic-version=host -cpu host
|
||||
|
|
|
@ -34,10 +34,15 @@ do
|
|||
exit 0;
|
||||
fi
|
||||
|
||||
# Set affinity to randomly selected CPU
|
||||
cpus=`ls /sys/devices/system/cpu/*/online |
|
||||
sed -e 's,/[^/]*$,,' -e 's/^[^0-9]*//' |
|
||||
grep -v '^0*$'`
|
||||
# Set affinity to randomly selected online CPU
|
||||
cpus=`grep 1 /sys/devices/system/cpu/*/online |
|
||||
sed -e 's,/[^/]*$,,' -e 's/^[^0-9]*//'`
|
||||
|
||||
# Do not leave out poor old cpu0 which may not be hot-pluggable
|
||||
if [ ! -f "/sys/devices/system/cpu/cpu0/online" ]; then
|
||||
cpus="0 $cpus"
|
||||
fi
|
||||
|
||||
cpumask=`awk -v cpus="$cpus" -v me=$me -v n=$n 'BEGIN {
|
||||
srand(n + me + systime());
|
||||
ncpus = split(cpus, ca);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# Build a kvm-ready Linux kernel from the tree in the current directory.
|
||||
#
|
||||
# Usage: kvm-build.sh config-template build-dir resdir
|
||||
# Usage: kvm-build.sh config-template resdir
|
||||
#
|
||||
# Copyright (C) IBM Corporation, 2011
|
||||
#
|
||||
|
@ -15,8 +15,7 @@ then
|
|||
echo "kvm-build.sh :$config_template: Not a readable file"
|
||||
exit 1
|
||||
fi
|
||||
builddir=${2}
|
||||
resdir=${3}
|
||||
resdir=${2}
|
||||
|
||||
T=${TMPDIR-/tmp}/test-linux.sh.$$
|
||||
trap 'rm -rf $T' 0
|
||||
|
@ -29,14 +28,14 @@ CONFIG_VIRTIO_PCI=y
|
|||
CONFIG_VIRTIO_CONSOLE=y
|
||||
___EOF___
|
||||
|
||||
configinit.sh $T/config O=$builddir $resdir
|
||||
configinit.sh $T/config $resdir
|
||||
retval=$?
|
||||
if test $retval -gt 1
|
||||
then
|
||||
exit 2
|
||||
fi
|
||||
ncpus=`cpus2use.sh`
|
||||
make O=$builddir -j$ncpus $TORTURE_KMAKE_ARG > $resdir/Make.out 2>&1
|
||||
make -j$ncpus $TORTURE_KMAKE_ARG > $resdir/Make.out 2>&1
|
||||
retval=$?
|
||||
if test $retval -ne 0 || grep "rcu[^/]*": < $resdir/Make.out | egrep -q "Stop|Error|error:|warning:" || egrep -q "Stop|Error|error:" < $resdir/Make.out
|
||||
then
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#
|
||||
# The "directory" above should end with the date/time directory, for example,
|
||||
# "tools/testing/selftests/rcutorture/res/2018.02.25-14:27:27".
|
||||
# Returns error status reflecting the success (or not) of the specified run.
|
||||
#
|
||||
# Copyright (C) IBM Corporation, 2018
|
||||
#
|
||||
|
@ -56,6 +57,8 @@ done
|
|||
if test -n "$files"
|
||||
then
|
||||
$editor $files
|
||||
exit 1
|
||||
else
|
||||
echo No errors in console logs.
|
||||
exit 0
|
||||
fi
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#
|
||||
# Usage: kvm-recheck.sh resdir ...
|
||||
#
|
||||
# Returns status reflecting the success or not of the last run specified.
|
||||
#
|
||||
# Copyright (C) IBM Corporation, 2011
|
||||
#
|
||||
# Authors: Paul E. McKenney <paulmck@linux.ibm.com>
|
||||
|
@ -28,8 +30,16 @@ do
|
|||
TORTURE_SUITE="`cat $i/../TORTURE_SUITE`"
|
||||
rm -f $i/console.log.*.diags
|
||||
kvm-recheck-${TORTURE_SUITE}.sh $i
|
||||
if test -f "$i/console.log"
|
||||
if test -f "$i/qemu-retval" && test "`cat $i/qemu-retval`" -ne 0 && test "`cat $i/qemu-retval`" -ne 137
|
||||
then
|
||||
echo QEMU error, output:
|
||||
cat $i/qemu-output
|
||||
elif test -f "$i/console.log"
|
||||
then
|
||||
if test -f "$i/qemu-retval" && test "`cat $i/qemu-retval`" -eq 137
|
||||
then
|
||||
echo QEMU killed
|
||||
fi
|
||||
configcheck.sh $i/.config $i/ConfigFragment
|
||||
if test -r $i/Make.oldconfig.err
|
||||
then
|
||||
|
@ -58,3 +68,4 @@ do
|
|||
fi
|
||||
done
|
||||
done
|
||||
EDITOR=echo kvm-find-errors.sh "${@: -1}" > /dev/null 2>&1
|
||||
|
|
|
@ -36,11 +36,6 @@ config_template=${1}
|
|||
config_dir=`echo $config_template | sed -e 's,/[^/]*$,,'`
|
||||
title=`echo $config_template | sed -e 's/^.*\///'`
|
||||
builddir=${2}
|
||||
if test -z "$builddir" -o ! -d "$builddir" -o ! -w "$builddir"
|
||||
then
|
||||
echo "kvm-test-1-run.sh :$builddir: Not a writable directory, cannot build into it"
|
||||
exit 1
|
||||
fi
|
||||
resdir=${3}
|
||||
if test -z "$resdir" -o ! -d "$resdir" -o ! -w "$resdir"
|
||||
then
|
||||
|
@ -85,18 +80,18 @@ then
|
|||
ln -s $base_resdir/.config $resdir # for kvm-recheck.sh
|
||||
# Arch-independent indicator
|
||||
touch $resdir/builtkernel
|
||||
elif kvm-build.sh $T/Kc2 $builddir $resdir
|
||||
elif kvm-build.sh $T/Kc2 $resdir
|
||||
then
|
||||
# Had to build a kernel for this test.
|
||||
QEMU="`identify_qemu $builddir/vmlinux`"
|
||||
QEMU="`identify_qemu vmlinux`"
|
||||
BOOT_IMAGE="`identify_boot_image $QEMU`"
|
||||
cp $builddir/vmlinux $resdir
|
||||
cp $builddir/.config $resdir
|
||||
cp $builddir/Module.symvers $resdir > /dev/null || :
|
||||
cp $builddir/System.map $resdir > /dev/null || :
|
||||
cp vmlinux $resdir
|
||||
cp .config $resdir
|
||||
cp Module.symvers $resdir > /dev/null || :
|
||||
cp System.map $resdir > /dev/null || :
|
||||
if test -n "$BOOT_IMAGE"
|
||||
then
|
||||
cp $builddir/$BOOT_IMAGE $resdir
|
||||
cp $BOOT_IMAGE $resdir
|
||||
KERNEL=$resdir/${BOOT_IMAGE##*/}
|
||||
# Arch-independent indicator
|
||||
touch $resdir/builtkernel
|
||||
|
@ -107,7 +102,7 @@ then
|
|||
parse-build.sh $resdir/Make.out $title
|
||||
else
|
||||
# Build failed.
|
||||
cp $builddir/.config $resdir || :
|
||||
cp .config $resdir || :
|
||||
echo Build failed, not running KVM, see $resdir.
|
||||
if test -f $builddir.wait
|
||||
then
|
||||
|
@ -165,7 +160,7 @@ then
|
|||
fi
|
||||
echo "NOTE: $QEMU either did not run or was interactive" > $resdir/console.log
|
||||
echo $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append \"$qemu_append $boot_args\" > $resdir/qemu-cmd
|
||||
( $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append "$qemu_append $boot_args"& echo $! > $resdir/qemu_pid; wait `cat $resdir/qemu_pid`; echo $? > $resdir/qemu-retval ) &
|
||||
( $QEMU $qemu_args -m $TORTURE_QEMU_MEM -kernel $KERNEL -append "$qemu_append $boot_args" > $resdir/qemu-output 2>&1 & echo $! > $resdir/qemu_pid; wait `cat $resdir/qemu_pid`; echo $? > $resdir/qemu-retval ) &
|
||||
commandcompleted=0
|
||||
sleep 10 # Give qemu's pid a chance to reach the file
|
||||
if test -s "$resdir/qemu_pid"
|
||||
|
|
|
@ -24,6 +24,7 @@ dur=$((30*60))
|
|||
dryrun=""
|
||||
KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM
|
||||
PATH=${KVM}/bin:$PATH; export PATH
|
||||
TORTURE_ALLOTED_CPUS=""
|
||||
TORTURE_DEFCONFIG=defconfig
|
||||
TORTURE_BOOT_IMAGE=""
|
||||
TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD
|
||||
|
@ -32,6 +33,7 @@ TORTURE_KMAKE_ARG=""
|
|||
TORTURE_QEMU_MEM=512
|
||||
TORTURE_SHUTDOWN_GRACE=180
|
||||
TORTURE_SUITE=rcu
|
||||
TORTURE_TRUST_MAKE=""
|
||||
resdir=""
|
||||
configs=""
|
||||
cpus=0
|
||||
|
@ -62,6 +64,7 @@ usage () {
|
|||
echo " --qemu-cmd qemu-system-..."
|
||||
echo " --results absolute-pathname"
|
||||
echo " --torture rcu"
|
||||
echo " --trust-make"
|
||||
exit 1
|
||||
}
|
||||
|
||||
|
@ -89,6 +92,7 @@ do
|
|||
--cpus)
|
||||
checkarg --cpus "(number)" "$#" "$2" '^[0-9]*$' '^--'
|
||||
cpus=$2
|
||||
TORTURE_ALLOTED_CPUS="$2"
|
||||
shift
|
||||
;;
|
||||
--datestamp)
|
||||
|
@ -173,6 +177,9 @@ do
|
|||
jitter=0
|
||||
fi
|
||||
;;
|
||||
--trust-make)
|
||||
TORTURE_TRUST_MAKE="y"
|
||||
;;
|
||||
*)
|
||||
echo Unknown argument $1
|
||||
usage
|
||||
|
@ -285,6 +292,7 @@ cat << ___EOF___ > $T/script
|
|||
CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG
|
||||
KVM="$KVM"; export KVM
|
||||
PATH="$PATH"; export PATH
|
||||
TORTURE_ALLOTED_CPUS="$TORTURE_ALLOTED_CPUS"; export TORTURE_ALLOTED_CPUS
|
||||
TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE
|
||||
TORTURE_BUILDONLY="$TORTURE_BUILDONLY"; export TORTURE_BUILDONLY
|
||||
TORTURE_DEFCONFIG="$TORTURE_DEFCONFIG"; export TORTURE_DEFCONFIG
|
||||
|
@ -297,6 +305,7 @@ TORTURE_QEMU_MAC="$TORTURE_QEMU_MAC"; export TORTURE_QEMU_MAC
|
|||
TORTURE_QEMU_MEM="$TORTURE_QEMU_MEM"; export TORTURE_QEMU_MEM
|
||||
TORTURE_SHUTDOWN_GRACE="$TORTURE_SHUTDOWN_GRACE"; export TORTURE_SHUTDOWN_GRACE
|
||||
TORTURE_SUITE="$TORTURE_SUITE"; export TORTURE_SUITE
|
||||
TORTURE_TRUST_MAKE="$TORTURE_TRUST_MAKE"; export TORTURE_TRUST_MAKE
|
||||
if ! test -e $resdir
|
||||
then
|
||||
mkdir -p "$resdir" || :
|
||||
|
@ -342,7 +351,7 @@ function dump(first, pastlast, batchnum)
|
|||
print "needqemurun="
|
||||
jn=1
|
||||
for (j = first; j < pastlast; j++) {
|
||||
builddir=KVM "/b1"
|
||||
builddir=KVM "/b" j - first + 1
|
||||
cpusr[jn] = cpus[j];
|
||||
if (cfrep[cf[j]] == "") {
|
||||
cfr[jn] = cf[j];
|
||||
|
@ -358,7 +367,6 @@ function dump(first, pastlast, batchnum)
|
|||
print "echo ", cfr[jn], cpusr[jn] ovf ": Starting build. `date` | tee -a " rd "log";
|
||||
print "rm -f " builddir ".*";
|
||||
print "touch " builddir ".wait";
|
||||
print "mkdir " builddir " > /dev/null 2>&1 || :";
|
||||
print "mkdir " rd cfr[jn] " || :";
|
||||
print "kvm-test-1-run.sh " CONFIGDIR cf[j], builddir, rd cfr[jn], dur " \"" TORTURE_QEMU_ARG "\" \"" TORTURE_BOOTARGS "\" > " rd cfr[jn] "/kvm-test-1-run.sh.out 2>&1 &"
|
||||
print "echo ", cfr[jn], cpusr[jn] ovf ": Waiting for build to complete. `date` | tee -a " rd "log";
|
||||
|
@ -464,3 +472,5 @@ else
|
|||
fi
|
||||
|
||||
# Tracing: trace_event=rcu:rcu_grace_period,rcu:rcu_future_grace_period,rcu:rcu_grace_period_init,rcu:rcu_nocb_wake,rcu:rcu_preempt_task,rcu:rcu_unlock_preempted_task,rcu:rcu_quiescent_state_report,rcu:rcu_fqs,rcu:rcu_callback,rcu:rcu_kfree_callback,rcu:rcu_batch_start,rcu:rcu_invoke_callback,rcu:rcu_invoke_kfree_callback,rcu:rcu_batch_end,rcu:rcu_torture_read,rcu:rcu_barrier
|
||||
# Function-graph tracing: ftrace=function_graph ftrace_graph_filter=sched_setaffinity,migration_cpu_stop
|
||||
# Also --kconfig "CONFIG_FUNCTION_TRACER=y CONFIG_FUNCTION_GRAPH_TRACER=y"
|
||||
|
|
|
@ -21,7 +21,7 @@ mkdir $T
|
|||
|
||||
. functions.sh
|
||||
|
||||
if grep -q CC < $F
|
||||
if grep -q CC < $F || test -n "$TORTURE_TRUST_MAKE"
|
||||
then
|
||||
:
|
||||
else
|
||||
|
|
|
@ -106,6 +106,7 @@ fi | tee -a $file.diags
|
|||
|
||||
egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|detected stalls on CPUs/tasks:|self-detected stall on CPU|Stall ended before state dump start|\?\?\? Writer stall state|rcu_.*kthread starved for' < $file |
|
||||
grep -v 'ODEBUG: ' |
|
||||
grep -v 'This means that this is a DEBUG kernel and it is' |
|
||||
grep -v 'Warning: unable to open an initial console' > $T.diags
|
||||
if test -s $T.diags
|
||||
then
|
||||
|
|
|
@ -1,2 +1,5 @@
|
|||
CONFIG_RCU_TORTURE_TEST=y
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_HYPERVISOR_GUEST=y
|
||||
CONFIG_PARAVIRT=y
|
||||
CONFIG_KVM_GUEST=y
|
||||
|
|
|
@ -3,3 +3,4 @@ rcutree.gp_preinit_delay=3
|
|||
rcutree.gp_init_delay=3
|
||||
rcutree.gp_cleanup_delay=3
|
||||
rcu_nocbs=0
|
||||
rcutorture.fwd_progress=0
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=8
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
CONFIG_PREEMPT_VOLUNTARY=n
|
||||
CONFIG_PREEMPT=n
|
||||
CONFIG_HZ_PERIODIC=n
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_NO_HZ_FULL=n
|
||||
CONFIG_HOTPLUG_CPU=n
|
||||
CONFIG_SUSPEND=n
|
||||
CONFIG_HIBERNATION=n
|
||||
CONFIG_DEBUG_LOCK_ALLOC=n
|
||||
CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
|
||||
CONFIG_RCU_EXPERT=y
|
|
@ -0,0 +1,3 @@
|
|||
rcutorture.torture_type=trivial
|
||||
rcutorture.onoff_interval=0
|
||||
rcutorture.shuffle_interval=0
|
Loading…
Reference in New Issue