mirror of
https://github.com/torvalds/linux.git
synced 2025-04-06 09:13:43 +00:00
Merge branches 'docs.2025.02.04a', 'lazypreempt.2025.03.04a', 'misc.2025.03.04a', 'srcu.2025.02.05a' and 'torture.2025.02.05a'
This commit is contained in:
commit
467c890f2d
@ -5758,6 +5758,11 @@
|
||||
rcutorture.test_boost_duration= [KNL]
|
||||
Duration (s) of each individual boost test.
|
||||
|
||||
rcutorture.test_boost_holdoff= [KNL]
|
||||
Holdoff time (s) from start of test to the start
|
||||
of RCU priority-boost testing. Defaults to zero,
|
||||
that is, no holdoff.
|
||||
|
||||
rcutorture.test_boost_interval= [KNL]
|
||||
Interval (s) between each boost test.
|
||||
|
||||
|
@ -207,6 +207,7 @@ void printk_legacy_allow_panic_sync(void);
|
||||
extern bool nbcon_device_try_acquire(struct console *con);
|
||||
extern void nbcon_device_release(struct console *con);
|
||||
void nbcon_atomic_flush_unsafe(void);
|
||||
bool pr_flush(int timeout_ms, bool reset_on_progress);
|
||||
#else
|
||||
static inline __printf(1, 0)
|
||||
int vprintk(const char *s, va_list args)
|
||||
@ -315,6 +316,11 @@ static inline void nbcon_atomic_flush_unsafe(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline bool pr_flush(int timeout_ms, bool reset_on_progress)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
bool this_cpu_in_panic(void);
|
||||
|
@ -95,9 +95,9 @@ static inline void __rcu_read_lock(void)
|
||||
|
||||
static inline void __rcu_read_unlock(void)
|
||||
{
|
||||
preempt_enable();
|
||||
if (IS_ENABLED(CONFIG_RCU_STRICT_GRACE_PERIOD))
|
||||
rcu_read_unlock_strict();
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
static inline int rcu_preempt_depth(void)
|
||||
@ -121,12 +121,6 @@ void rcu_init(void);
|
||||
extern int rcu_scheduler_active;
|
||||
void rcu_sched_clock_irq(int user);
|
||||
|
||||
#ifdef CONFIG_TASKS_RCU_GENERIC
|
||||
void rcu_init_tasks_generic(void);
|
||||
#else
|
||||
static inline void rcu_init_tasks_generic(void) { }
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RCU_STALL_COMMON
|
||||
void rcu_sysrq_start(void);
|
||||
void rcu_sysrq_end(void);
|
||||
|
@ -16,6 +16,9 @@
|
||||
struct rcu_synchronize {
|
||||
struct rcu_head head;
|
||||
struct completion completion;
|
||||
|
||||
/* This is for debugging. */
|
||||
struct rcu_gp_oldstate oldstate;
|
||||
};
|
||||
void wakeme_after_rcu(struct rcu_head *head);
|
||||
|
||||
|
@ -103,7 +103,7 @@ extern int rcu_scheduler_active;
|
||||
void rcu_end_inkernel_boot(void);
|
||||
bool rcu_inkernel_boot_has_ended(void);
|
||||
bool rcu_is_watching(void);
|
||||
#ifndef CONFIG_PREEMPTION
|
||||
#ifndef CONFIG_PREEMPT_RCU
|
||||
void rcu_all_qs(void);
|
||||
#endif
|
||||
|
||||
|
@ -47,7 +47,13 @@ int init_srcu_struct(struct srcu_struct *ssp);
|
||||
#define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().
|
||||
#define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
|
||||
#define SRCU_READ_FLAVOR_LITE 0x4 // srcu_read_lock_lite().
|
||||
#define SRCU_READ_FLAVOR_ALL 0x7 // All of the above.
|
||||
#define SRCU_READ_FLAVOR_FAST 0x8 // srcu_read_lock_fast().
|
||||
#define SRCU_READ_FLAVOR_ALL (SRCU_READ_FLAVOR_NORMAL | SRCU_READ_FLAVOR_NMI | \
|
||||
SRCU_READ_FLAVOR_LITE | SRCU_READ_FLAVOR_FAST) // All of the above.
|
||||
#define SRCU_READ_FLAVOR_SLOWGP (SRCU_READ_FLAVOR_LITE | SRCU_READ_FLAVOR_FAST)
|
||||
// Flavors requiring synchronize_rcu()
|
||||
// instead of smp_mb().
|
||||
void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp);
|
||||
|
||||
#ifdef CONFIG_TINY_SRCU
|
||||
#include <linux/srcutiny.h>
|
||||
@ -60,15 +66,6 @@ int init_srcu_struct(struct srcu_struct *ssp);
|
||||
void call_srcu(struct srcu_struct *ssp, struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *head));
|
||||
void cleanup_srcu_struct(struct srcu_struct *ssp);
|
||||
int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp);
|
||||
void __srcu_read_unlock(struct srcu_struct *ssp, int idx) __releases(ssp);
|
||||
#ifdef CONFIG_TINY_SRCU
|
||||
#define __srcu_read_lock_lite __srcu_read_lock
|
||||
#define __srcu_read_unlock_lite __srcu_read_unlock
|
||||
#else // #ifdef CONFIG_TINY_SRCU
|
||||
int __srcu_read_lock_lite(struct srcu_struct *ssp) __acquires(ssp);
|
||||
void __srcu_read_unlock_lite(struct srcu_struct *ssp, int idx) __releases(ssp);
|
||||
#endif // #else // #ifdef CONFIG_TINY_SRCU
|
||||
void synchronize_srcu(struct srcu_struct *ssp);
|
||||
|
||||
#define SRCU_GET_STATE_COMPLETED 0x1
|
||||
@ -257,6 +254,51 @@ static inline int srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp)
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_read_lock_fast - register a new reader for an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to register the new reader.
|
||||
*
|
||||
* Enter an SRCU read-side critical section, but for a light-weight
|
||||
* smp_mb()-free reader. See srcu_read_lock() for more information.
|
||||
*
|
||||
* If srcu_read_lock_fast() is ever used on an srcu_struct structure,
|
||||
* then none of the other flavors may be used, whether before, during,
|
||||
* or after. Note that grace-period auto-expediting is disabled for _fast
|
||||
* srcu_struct structures because auto-expedited grace periods invoke
|
||||
* synchronize_rcu_expedited(), IPIs and all.
|
||||
*
|
||||
* Note that srcu_read_lock_fast() can be invoked only from those contexts
|
||||
* where RCU is watching, that is, from contexts where it would be legal
|
||||
* to invoke rcu_read_lock(). Otherwise, lockdep will complain.
|
||||
*/
|
||||
static inline struct srcu_ctr __percpu *srcu_read_lock_fast(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
struct srcu_ctr __percpu *retval;
|
||||
|
||||
srcu_check_read_flavor_force(ssp, SRCU_READ_FLAVOR_FAST);
|
||||
retval = __srcu_read_lock_fast(ssp);
|
||||
rcu_try_lock_acquire(&ssp->dep_map);
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_down_read_fast - register a new reader for an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to register the new reader.
|
||||
*
|
||||
* Enter a semaphore-like SRCU read-side critical section, but for
|
||||
* a light-weight smp_mb()-free reader. See srcu_read_lock_fast() and
|
||||
* srcu_down_read() for more information.
|
||||
*
|
||||
* The same srcu_struct may be used concurrently by srcu_down_read_fast()
|
||||
* and srcu_read_lock_fast().
|
||||
*/
|
||||
static inline struct srcu_ctr __percpu *srcu_down_read_fast(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
|
||||
srcu_check_read_flavor_force(ssp, SRCU_READ_FLAVOR_FAST);
|
||||
return __srcu_read_lock_fast(ssp);
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_read_lock_lite - register a new reader for an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to register the new reader.
|
||||
@ -278,7 +320,7 @@ static inline int srcu_read_lock_lite(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
int retval;
|
||||
|
||||
srcu_check_read_flavor_lite(ssp);
|
||||
srcu_check_read_flavor_force(ssp, SRCU_READ_FLAVOR_LITE);
|
||||
retval = __srcu_read_lock_lite(ssp);
|
||||
rcu_try_lock_acquire(&ssp->dep_map);
|
||||
return retval;
|
||||
@ -335,7 +377,8 @@ srcu_read_lock_notrace(struct srcu_struct *ssp) __acquires(ssp)
|
||||
* srcu_down_read() nor srcu_up_read() may be invoked from an NMI handler.
|
||||
*
|
||||
* Calls to srcu_down_read() may be nested, similar to the manner in
|
||||
* which calls to down_read() may be nested.
|
||||
* which calls to down_read() may be nested. The same srcu_struct may be
|
||||
* used concurrently by srcu_down_read() and srcu_read_lock().
|
||||
*/
|
||||
static inline int srcu_down_read(struct srcu_struct *ssp) __acquires(ssp)
|
||||
{
|
||||
@ -360,10 +403,41 @@ static inline void srcu_read_unlock(struct srcu_struct *ssp, int idx)
|
||||
__srcu_read_unlock(ssp, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_read_unlock_fast - unregister a old reader from an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to unregister the old reader.
|
||||
* @scp: return value from corresponding srcu_read_lock_fast().
|
||||
*
|
||||
* Exit a light-weight SRCU read-side critical section.
|
||||
*/
|
||||
static inline void srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
|
||||
__releases(ssp)
|
||||
{
|
||||
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST);
|
||||
srcu_lock_release(&ssp->dep_map);
|
||||
__srcu_read_unlock_fast(ssp, scp);
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_up_read_fast - unregister a old reader from an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to unregister the old reader.
|
||||
* @scp: return value from corresponding srcu_read_lock_fast().
|
||||
*
|
||||
* Exit an SRCU read-side critical section, but not necessarily from
|
||||
* the same context as the maching srcu_down_read_fast().
|
||||
*/
|
||||
static inline void srcu_up_read_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
|
||||
__releases(ssp)
|
||||
{
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && in_nmi());
|
||||
srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_FAST);
|
||||
__srcu_read_unlock_fast(ssp, scp);
|
||||
}
|
||||
|
||||
/**
|
||||
* srcu_read_unlock_lite - unregister a old reader from an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to unregister the old reader.
|
||||
* @idx: return value from corresponding srcu_read_lock().
|
||||
* @idx: return value from corresponding srcu_read_lock_lite().
|
||||
*
|
||||
* Exit a light-weight SRCU read-side critical section.
|
||||
*/
|
||||
@ -379,7 +453,7 @@ static inline void srcu_read_unlock_lite(struct srcu_struct *ssp, int idx)
|
||||
/**
|
||||
* srcu_read_unlock_nmisafe - unregister a old reader from an SRCU-protected structure.
|
||||
* @ssp: srcu_struct in which to unregister the old reader.
|
||||
* @idx: return value from corresponding srcu_read_lock().
|
||||
* @idx: return value from corresponding srcu_read_lock_nmisafe().
|
||||
*
|
||||
* Exit an SRCU read-side critical section, but in an NMI-safe manner.
|
||||
*/
|
||||
|
@ -64,13 +64,38 @@ static inline int __srcu_read_lock(struct srcu_struct *ssp)
|
||||
{
|
||||
int idx;
|
||||
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
idx = ((READ_ONCE(ssp->srcu_idx) + 1) & 0x2) >> 1;
|
||||
WRITE_ONCE(ssp->srcu_lock_nesting[idx], READ_ONCE(ssp->srcu_lock_nesting[idx]) + 1);
|
||||
preempt_enable();
|
||||
return idx;
|
||||
}
|
||||
|
||||
struct srcu_ctr;
|
||||
|
||||
static inline bool __srcu_ptr_to_ctr(struct srcu_struct *ssp, struct srcu_ctr __percpu *scpp)
|
||||
{
|
||||
return (int)(intptr_t)(struct srcu_ctr __force __kernel *)scpp;
|
||||
}
|
||||
|
||||
static inline struct srcu_ctr __percpu *__srcu_ctr_to_ptr(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
return (struct srcu_ctr __percpu *)(intptr_t)idx;
|
||||
}
|
||||
|
||||
static inline struct srcu_ctr __percpu *__srcu_read_lock_fast(struct srcu_struct *ssp)
|
||||
{
|
||||
return __srcu_ctr_to_ptr(ssp, __srcu_read_lock(ssp));
|
||||
}
|
||||
|
||||
static inline void __srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
|
||||
{
|
||||
__srcu_read_unlock(ssp, __srcu_ptr_to_ctr(ssp, scp));
|
||||
}
|
||||
|
||||
#define __srcu_read_lock_lite __srcu_read_lock
|
||||
#define __srcu_read_unlock_lite __srcu_read_unlock
|
||||
|
||||
static inline void synchronize_srcu_expedited(struct srcu_struct *ssp)
|
||||
{
|
||||
synchronize_srcu(ssp);
|
||||
@ -82,7 +107,7 @@ static inline void srcu_barrier(struct srcu_struct *ssp)
|
||||
}
|
||||
|
||||
#define srcu_check_read_flavor(ssp, read_flavor) do { } while (0)
|
||||
#define srcu_check_read_flavor_lite(ssp) do { } while (0)
|
||||
#define srcu_check_read_flavor_force(ssp, read_flavor) do { } while (0)
|
||||
|
||||
/* Defined here to avoid size increase for non-torture kernels. */
|
||||
static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
|
||||
|
@ -17,14 +17,19 @@
|
||||
struct srcu_node;
|
||||
struct srcu_struct;
|
||||
|
||||
/* One element of the srcu_data srcu_ctrs array. */
|
||||
struct srcu_ctr {
|
||||
atomic_long_t srcu_locks; /* Locks per CPU. */
|
||||
atomic_long_t srcu_unlocks; /* Unlocks per CPU. */
|
||||
};
|
||||
|
||||
/*
|
||||
* Per-CPU structure feeding into leaf srcu_node, similar in function
|
||||
* to rcu_node.
|
||||
*/
|
||||
struct srcu_data {
|
||||
/* Read-side state. */
|
||||
atomic_long_t srcu_lock_count[2]; /* Locks per CPU. */
|
||||
atomic_long_t srcu_unlock_count[2]; /* Unlocks per CPU. */
|
||||
struct srcu_ctr srcu_ctrs[2]; /* Locks and unlocks per CPU. */
|
||||
int srcu_reader_flavor; /* Reader flavor for srcu_struct structure? */
|
||||
/* Values: SRCU_READ_FLAVOR_.* */
|
||||
|
||||
@ -95,7 +100,7 @@ struct srcu_usage {
|
||||
* Per-SRCU-domain structure, similar in function to rcu_state.
|
||||
*/
|
||||
struct srcu_struct {
|
||||
unsigned int srcu_idx; /* Current rdr array element. */
|
||||
struct srcu_ctr __percpu *srcu_ctrp;
|
||||
struct srcu_data __percpu *sda; /* Per-CPU srcu_data array. */
|
||||
struct lockdep_map dep_map;
|
||||
struct srcu_usage *srcu_sup; /* Update-side data. */
|
||||
@ -162,6 +167,7 @@ struct srcu_struct {
|
||||
#define __SRCU_STRUCT_INIT(name, usage_name, pcpu_name) \
|
||||
{ \
|
||||
.sda = &pcpu_name, \
|
||||
.srcu_ctrp = &pcpu_name.srcu_ctrs[0], \
|
||||
__SRCU_STRUCT_INIT_COMMON(name, usage_name) \
|
||||
}
|
||||
|
||||
@ -201,10 +207,77 @@ struct srcu_struct {
|
||||
#define DEFINE_SRCU(name) __DEFINE_SRCU(name, /* not static */)
|
||||
#define DEFINE_STATIC_SRCU(name) __DEFINE_SRCU(name, static)
|
||||
|
||||
int __srcu_read_lock(struct srcu_struct *ssp) __acquires(ssp);
|
||||
void synchronize_srcu_expedited(struct srcu_struct *ssp);
|
||||
void srcu_barrier(struct srcu_struct *ssp);
|
||||
void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf);
|
||||
|
||||
// Converts a per-CPU pointer to an ->srcu_ctrs[] array element to that
|
||||
// element's index.
|
||||
static inline bool __srcu_ptr_to_ctr(struct srcu_struct *ssp, struct srcu_ctr __percpu *scpp)
|
||||
{
|
||||
return scpp - &ssp->sda->srcu_ctrs[0];
|
||||
}
|
||||
|
||||
// Converts an integer to a per-CPU pointer to the corresponding
|
||||
// ->srcu_ctrs[] array element.
|
||||
static inline struct srcu_ctr __percpu *__srcu_ctr_to_ptr(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
return &ssp->sda->srcu_ctrs[idx];
|
||||
}
|
||||
|
||||
/*
|
||||
* Counts the new reader in the appropriate per-CPU element of the
|
||||
* srcu_struct. Returns a pointer that must be passed to the matching
|
||||
* srcu_read_unlock_fast().
|
||||
*
|
||||
* Note that both this_cpu_inc() and atomic_long_inc() are RCU read-side
|
||||
* critical sections either because they disables interrupts, because they
|
||||
* are a single instruction, or because they are a read-modify-write atomic
|
||||
* operation, depending on the whims of the architecture.
|
||||
*
|
||||
* This means that __srcu_read_lock_fast() is not all that fast
|
||||
* on architectures that support NMIs but do not supply NMI-safe
|
||||
* implementations of this_cpu_inc().
|
||||
*/
|
||||
static inline struct srcu_ctr __percpu *__srcu_read_lock_fast(struct srcu_struct *ssp)
|
||||
{
|
||||
struct srcu_ctr __percpu *scp = READ_ONCE(ssp->srcu_ctrp);
|
||||
|
||||
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_lock_fast().");
|
||||
if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
|
||||
this_cpu_inc(scp->srcu_locks.counter); /* Y */
|
||||
else
|
||||
atomic_long_inc(raw_cpu_ptr(&scp->srcu_locks)); /* Z */
|
||||
barrier(); /* Avoid leaking the critical section. */
|
||||
return scp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes the count for the old reader from the appropriate
|
||||
* per-CPU element of the srcu_struct. Note that this may well be a
|
||||
* different CPU than that which was incremented by the corresponding
|
||||
* srcu_read_lock_fast(), but it must be within the same task.
|
||||
*
|
||||
* Note that both this_cpu_inc() and atomic_long_inc() are RCU read-side
|
||||
* critical sections either because they disables interrupts, because they
|
||||
* are a single instruction, or because they are a read-modify-write atomic
|
||||
* operation, depending on the whims of the architecture.
|
||||
*
|
||||
* This means that __srcu_read_unlock_fast() is not all that fast
|
||||
* on architectures that support NMIs but do not supply NMI-safe
|
||||
* implementations of this_cpu_inc().
|
||||
*/
|
||||
static inline void __srcu_read_unlock_fast(struct srcu_struct *ssp, struct srcu_ctr __percpu *scp)
|
||||
{
|
||||
barrier(); /* Avoid leaking the critical section. */
|
||||
if (!IS_ENABLED(CONFIG_NEED_SRCU_NMI_SAFE))
|
||||
this_cpu_inc(scp->srcu_unlocks.counter); /* Z */
|
||||
else
|
||||
atomic_long_inc(raw_cpu_ptr(&scp->srcu_unlocks)); /* Z */
|
||||
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_unlock_fast().");
|
||||
}
|
||||
|
||||
/*
|
||||
* Counts the new reader in the appropriate per-CPU element of the
|
||||
* srcu_struct. Returns an index that must be passed to the matching
|
||||
@ -217,13 +290,12 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf);
|
||||
*/
|
||||
static inline int __srcu_read_lock_lite(struct srcu_struct *ssp)
|
||||
{
|
||||
int idx;
|
||||
struct srcu_ctr __percpu *scp = READ_ONCE(ssp->srcu_ctrp);
|
||||
|
||||
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_lock_lite().");
|
||||
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
|
||||
this_cpu_inc(ssp->sda->srcu_lock_count[idx].counter); /* Y */
|
||||
this_cpu_inc(scp->srcu_locks.counter); /* Y */
|
||||
barrier(); /* Avoid leaking the critical section. */
|
||||
return idx;
|
||||
return __srcu_ptr_to_ctr(ssp, scp);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -240,22 +312,24 @@ static inline int __srcu_read_lock_lite(struct srcu_struct *ssp)
|
||||
static inline void __srcu_read_unlock_lite(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
barrier(); /* Avoid leaking the critical section. */
|
||||
this_cpu_inc(ssp->sda->srcu_unlock_count[idx].counter); /* Z */
|
||||
this_cpu_inc(__srcu_ctr_to_ptr(ssp, idx)->srcu_unlocks.counter); /* Z */
|
||||
RCU_LOCKDEP_WARN(!rcu_is_watching(), "RCU must be watching srcu_read_unlock_lite().");
|
||||
}
|
||||
|
||||
void __srcu_check_read_flavor(struct srcu_struct *ssp, int read_flavor);
|
||||
|
||||
// Record _lite() usage even for CONFIG_PROVE_RCU=n kernels.
|
||||
static inline void srcu_check_read_flavor_lite(struct srcu_struct *ssp)
|
||||
// Record reader usage even for CONFIG_PROVE_RCU=n kernels. This is
|
||||
// needed only for flavors that require grace-period smp_mb() calls to be
|
||||
// promoted to synchronize_rcu().
|
||||
static inline void srcu_check_read_flavor_force(struct srcu_struct *ssp, int read_flavor)
|
||||
{
|
||||
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
|
||||
|
||||
if (likely(READ_ONCE(sdp->srcu_reader_flavor) & SRCU_READ_FLAVOR_LITE))
|
||||
if (likely(READ_ONCE(sdp->srcu_reader_flavor) & read_flavor))
|
||||
return;
|
||||
|
||||
// Note that the cmpxchg() in __srcu_check_read_flavor() is fully ordered.
|
||||
__srcu_check_read_flavor(ssp, SRCU_READ_FLAVOR_LITE);
|
||||
__srcu_check_read_flavor(ssp, read_flavor);
|
||||
}
|
||||
|
||||
// Record non-_lite() usage only for CONFIG_PROVE_RCU=y kernels.
|
||||
|
@ -104,6 +104,7 @@ int torture_stutter_init(int s, int sgap);
|
||||
/* Initialization and cleanup. */
|
||||
bool torture_init_begin(char *ttype, int v);
|
||||
void torture_init_end(void);
|
||||
unsigned long get_torture_init_jiffies(void);
|
||||
bool torture_cleanup_begin(void);
|
||||
void torture_cleanup_end(void);
|
||||
bool torture_must_stop(void);
|
||||
|
@ -207,7 +207,7 @@ TRACE_EVENT_RCU(rcu_exp_grace_period,
|
||||
__entry->gpevent = gpevent;
|
||||
),
|
||||
|
||||
TP_printk("%s %ld %s",
|
||||
TP_printk("%s %#lx %s",
|
||||
__entry->rcuname, __entry->gpseq, __entry->gpevent)
|
||||
);
|
||||
|
||||
|
@ -1553,7 +1553,6 @@ static noinline void __init kernel_init_freeable(void)
|
||||
|
||||
init_mm_internals();
|
||||
|
||||
rcu_init_tasks_generic();
|
||||
do_pre_smp_initcalls();
|
||||
lockup_detector_init();
|
||||
|
||||
|
@ -80,17 +80,16 @@ static __always_inline void rcu_task_trace_heavyweight_exit(void)
|
||||
*/
|
||||
static noinstr void ct_kernel_exit_state(int offset)
|
||||
{
|
||||
int seq;
|
||||
|
||||
/*
|
||||
* CPUs seeing atomic_add_return() must see prior RCU read-side
|
||||
* critical sections, and we also must force ordering with the
|
||||
* next idle sojourn.
|
||||
*/
|
||||
rcu_task_trace_heavyweight_enter(); // Before CT state update!
|
||||
seq = ct_state_inc(offset);
|
||||
// RCU is no longer watching. Better be in extended quiescent state!
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && (seq & CT_RCU_WATCHING));
|
||||
// RCU is still watching. Better not be in extended quiescent state!
|
||||
WARN_ON_ONCE(IS_ENABLED(CONFIG_RCU_EQS_DEBUG) && !rcu_is_watching_curr_cpu());
|
||||
(void)ct_state_inc(offset);
|
||||
// RCU is no longer watching.
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2461,7 +2461,6 @@ asmlinkage __visible int _printk(const char *fmt, ...)
|
||||
}
|
||||
EXPORT_SYMBOL(_printk);
|
||||
|
||||
static bool pr_flush(int timeout_ms, bool reset_on_progress);
|
||||
static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress);
|
||||
|
||||
#else /* CONFIG_PRINTK */
|
||||
@ -2474,7 +2473,6 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
|
||||
|
||||
static u64 syslog_seq;
|
||||
|
||||
static bool pr_flush(int timeout_ms, bool reset_on_progress) { return true; }
|
||||
static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progress) { return true; }
|
||||
|
||||
#endif /* CONFIG_PRINTK */
|
||||
@ -4466,7 +4464,7 @@ static bool __pr_flush(struct console *con, int timeout_ms, bool reset_on_progre
|
||||
* Context: Process context. May sleep while acquiring console lock.
|
||||
* Return: true if all usable printers are caught up.
|
||||
*/
|
||||
static bool pr_flush(int timeout_ms, bool reset_on_progress)
|
||||
bool pr_flush(int timeout_ms, bool reset_on_progress)
|
||||
{
|
||||
return __pr_flush(NULL, timeout_ms, reset_on_progress);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ config TREE_RCU
|
||||
|
||||
config PREEMPT_RCU
|
||||
bool
|
||||
default y if PREEMPTION
|
||||
default y if (PREEMPT || PREEMPT_RT || PREEMPT_DYNAMIC)
|
||||
select TREE_RCU
|
||||
help
|
||||
This option selects the RCU implementation that is
|
||||
@ -65,6 +65,17 @@ config TREE_SRCU
|
||||
help
|
||||
This option selects the full-fledged version of SRCU.
|
||||
|
||||
config FORCE_NEED_SRCU_NMI_SAFE
|
||||
bool "Force selection of NEED_SRCU_NMI_SAFE"
|
||||
depends on !TINY_SRCU
|
||||
select NEED_SRCU_NMI_SAFE
|
||||
default n
|
||||
help
|
||||
This option forces selection of the NEED_SRCU_NMI_SAFE
|
||||
Kconfig option, allowing testing of srcu_read_lock_nmisafe()
|
||||
and srcu_read_unlock_nmisafe() on architectures (like x86)
|
||||
that select the ARCH_HAS_NMI_SAFE_THIS_CPU_OPS Kconfig option.
|
||||
|
||||
config NEED_SRCU_NMI_SAFE
|
||||
def_bool HAVE_NMI && !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS && !TINY_SRCU
|
||||
|
||||
@ -91,7 +102,7 @@ config NEED_TASKS_RCU
|
||||
|
||||
config TASKS_RCU
|
||||
bool
|
||||
default NEED_TASKS_RCU && (PREEMPTION || PREEMPT_AUTO)
|
||||
default NEED_TASKS_RCU && PREEMPTION
|
||||
select IRQ_WORK
|
||||
|
||||
config FORCE_TASKS_RUDE_RCU
|
||||
|
@ -54,7 +54,7 @@ config RCU_TORTURE_TEST
|
||||
Say N if you are unsure.
|
||||
|
||||
config RCU_TORTURE_TEST_CHK_RDR_STATE
|
||||
tristate "Check rcutorture reader state"
|
||||
bool "Check rcutorture reader state"
|
||||
depends on RCU_TORTURE_TEST
|
||||
default n
|
||||
help
|
||||
@ -70,7 +70,7 @@ config RCU_TORTURE_TEST_CHK_RDR_STATE
|
||||
Say N if you are unsure.
|
||||
|
||||
config RCU_TORTURE_TEST_LOG_CPU
|
||||
tristate "Log CPU for rcutorture failures"
|
||||
bool "Log CPU for rcutorture failures"
|
||||
depends on RCU_TORTURE_TEST
|
||||
default n
|
||||
help
|
||||
@ -84,6 +84,20 @@ config RCU_TORTURE_TEST_LOG_CPU
|
||||
Say Y here if you want CPU IDs logged.
|
||||
Say N if you are unsure.
|
||||
|
||||
config RCU_TORTURE_TEST_LOG_GP
|
||||
bool "Log grace-period numbers for rcutorture failures"
|
||||
depends on RCU_TORTURE_TEST
|
||||
default n
|
||||
help
|
||||
This option causes rcutorture to decorate each entry of its
|
||||
log of failure/close-call rcutorture reader segments with the
|
||||
corresponding grace-period sequence numbers. This information
|
||||
can be useful, but it does incur additional overhead, overhead
|
||||
that can make both failures and close calls less probable.
|
||||
|
||||
Say Y here if you want grace-period sequence numbers logged.
|
||||
Say N if you are unsure.
|
||||
|
||||
config RCU_REF_SCALE_TEST
|
||||
tristate "Scalability tests for read-side synchronization (RCU and others)"
|
||||
depends on DEBUG_KERNEL
|
||||
|
@ -162,7 +162,7 @@ static inline bool rcu_seq_done_exact(unsigned long *sp, unsigned long s)
|
||||
{
|
||||
unsigned long cur_s = READ_ONCE(*sp);
|
||||
|
||||
return ULONG_CMP_GE(cur_s, s) || ULONG_CMP_LT(cur_s, s - (2 * RCU_SEQ_STATE_MASK + 1));
|
||||
return ULONG_CMP_GE(cur_s, s) || ULONG_CMP_LT(cur_s, s - (3 * RCU_SEQ_STATE_MASK + 1));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -590,6 +590,8 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
|
||||
#endif
|
||||
static inline void rcu_gp_set_torture_wait(int duration) { }
|
||||
#endif
|
||||
unsigned long long rcutorture_gather_gp_seqs(void);
|
||||
void rcutorture_format_gp_seqs(unsigned long long seqs, char *cp, size_t len);
|
||||
|
||||
#ifdef CONFIG_TINY_SRCU
|
||||
|
||||
@ -611,8 +613,6 @@ void srcutorture_get_gp_data(struct srcu_struct *sp, int *flags,
|
||||
static inline bool rcu_watching_zero_in_eqs(int cpu, int *vp) { return false; }
|
||||
static inline unsigned long rcu_get_gp_seq(void) { return 0; }
|
||||
static inline unsigned long rcu_exp_batches_completed(void) { return 0; }
|
||||
static inline unsigned long
|
||||
srcu_batches_completed(struct srcu_struct *sp) { return 0; }
|
||||
static inline void rcu_force_quiescent_state(void) { }
|
||||
static inline bool rcu_check_boost_fail(unsigned long gp_state, int *cpup) { return true; }
|
||||
static inline void show_rcu_gp_kthreads(void) { }
|
||||
@ -624,7 +624,6 @@ static inline void rcu_gp_slow_unregister(atomic_t *rgssp) { }
|
||||
bool rcu_watching_zero_in_eqs(int cpu, int *vp);
|
||||
unsigned long rcu_get_gp_seq(void);
|
||||
unsigned long rcu_exp_batches_completed(void);
|
||||
unsigned long srcu_batches_completed(struct srcu_struct *sp);
|
||||
bool rcu_check_boost_fail(unsigned long gp_state, int *cpup);
|
||||
void show_rcu_gp_kthreads(void);
|
||||
int rcu_get_gp_kthreads_prio(void);
|
||||
@ -636,6 +635,12 @@ void rcu_gp_slow_register(atomic_t *rgssp);
|
||||
void rcu_gp_slow_unregister(atomic_t *rgssp);
|
||||
#endif /* #else #ifdef CONFIG_TINY_RCU */
|
||||
|
||||
#ifdef CONFIG_TINY_SRCU
|
||||
static inline unsigned long srcu_batches_completed(struct srcu_struct *sp) { return 0; }
|
||||
#else // #ifdef CONFIG_TINY_SRCU
|
||||
unsigned long srcu_batches_completed(struct srcu_struct *sp);
|
||||
#endif // #else // #ifdef CONFIG_TINY_SRCU
|
||||
|
||||
#ifdef CONFIG_RCU_NOCB_CPU
|
||||
void rcu_bind_current_to_nocb(void);
|
||||
#else
|
||||
|
@ -135,6 +135,7 @@ torture_param(int, stat_interval, 60, "Number of seconds between stats printk()s
|
||||
torture_param(int, stutter, 5, "Number of seconds to run/halt test");
|
||||
torture_param(int, test_boost, 1, "Test RCU prio boost: 0=no, 1=maybe, 2=yes.");
|
||||
torture_param(int, test_boost_duration, 4, "Duration of each boost test, seconds.");
|
||||
torture_param(int, test_boost_holdoff, 0, "Holdoff time from rcutorture start, seconds.");
|
||||
torture_param(int, test_boost_interval, 7, "Interval between boost tests, seconds.");
|
||||
torture_param(int, test_nmis, 0, "End-test NMI tests, 0 to disable.");
|
||||
torture_param(bool, test_no_idle_hz, true, "Test support for tickless idle CPUs");
|
||||
@ -147,6 +148,7 @@ MODULE_PARM_DESC(torture_type, "Type of RCU to torture (rcu, srcu, ...)");
|
||||
|
||||
static int nrealnocbers;
|
||||
static int nrealreaders;
|
||||
static int nrealfakewriters;
|
||||
static struct task_struct *writer_task;
|
||||
static struct task_struct **fakewriter_tasks;
|
||||
static struct task_struct **reader_tasks;
|
||||
@ -272,6 +274,9 @@ struct rt_read_seg {
|
||||
bool rt_preempted;
|
||||
int rt_cpu;
|
||||
int rt_end_cpu;
|
||||
unsigned long long rt_gp_seq;
|
||||
unsigned long long rt_gp_seq_end;
|
||||
u64 rt_ts;
|
||||
};
|
||||
static int err_segs_recorded;
|
||||
static struct rt_read_seg err_segs[RCUTORTURE_RDR_MAX_SEGS];
|
||||
@ -406,6 +411,8 @@ struct rcu_torture_ops {
|
||||
void (*gp_slow_register)(atomic_t *rgssp);
|
||||
void (*gp_slow_unregister)(atomic_t *rgssp);
|
||||
bool (*reader_blocked)(void);
|
||||
unsigned long long (*gather_gp_seqs)(void);
|
||||
void (*format_gp_seqs)(unsigned long long seqs, char *cp, size_t len);
|
||||
long cbflood_max;
|
||||
int irq_capable;
|
||||
int can_boost;
|
||||
@ -610,6 +617,8 @@ static struct rcu_torture_ops rcu_ops = {
|
||||
.reader_blocked = IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_CPU)
|
||||
? has_rcu_reader_blocked
|
||||
: NULL,
|
||||
.gather_gp_seqs = rcutorture_gather_gp_seqs,
|
||||
.format_gp_seqs = rcutorture_format_gp_seqs,
|
||||
.irq_capable = 1,
|
||||
.can_boost = IS_ENABLED(CONFIG_RCU_BOOST),
|
||||
.extendables = RCUTORTURE_MAX_EXTEND,
|
||||
@ -655,6 +664,8 @@ static struct rcu_torture_ops rcu_busted_ops = {
|
||||
.sync = synchronize_rcu_busted,
|
||||
.exp_sync = synchronize_rcu_busted,
|
||||
.call = call_rcu_busted,
|
||||
.gather_gp_seqs = rcutorture_gather_gp_seqs,
|
||||
.format_gp_seqs = rcutorture_format_gp_seqs,
|
||||
.irq_capable = 1,
|
||||
.extendables = RCUTORTURE_MAX_EXTEND,
|
||||
.name = "busted"
|
||||
@ -677,8 +688,11 @@ static void srcu_get_gp_data(int *flags, unsigned long *gp_seq)
|
||||
static int srcu_torture_read_lock(void)
|
||||
{
|
||||
int idx;
|
||||
struct srcu_ctr __percpu *scp;
|
||||
int ret = 0;
|
||||
|
||||
WARN_ON_ONCE(reader_flavor & ~SRCU_READ_FLAVOR_ALL);
|
||||
|
||||
if ((reader_flavor & SRCU_READ_FLAVOR_NORMAL) || !(reader_flavor & SRCU_READ_FLAVOR_ALL)) {
|
||||
idx = srcu_read_lock(srcu_ctlp);
|
||||
WARN_ON_ONCE(idx & ~0x1);
|
||||
@ -694,6 +708,12 @@ static int srcu_torture_read_lock(void)
|
||||
WARN_ON_ONCE(idx & ~0x1);
|
||||
ret += idx << 2;
|
||||
}
|
||||
if (reader_flavor & SRCU_READ_FLAVOR_FAST) {
|
||||
scp = srcu_read_lock_fast(srcu_ctlp);
|
||||
idx = __srcu_ptr_to_ctr(srcu_ctlp, scp);
|
||||
WARN_ON_ONCE(idx & ~0x1);
|
||||
ret += idx << 3;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -719,6 +739,8 @@ srcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp)
|
||||
static void srcu_torture_read_unlock(int idx)
|
||||
{
|
||||
WARN_ON_ONCE((reader_flavor && (idx & ~reader_flavor)) || (!reader_flavor && (idx & ~0x1)));
|
||||
if (reader_flavor & SRCU_READ_FLAVOR_FAST)
|
||||
srcu_read_unlock_fast(srcu_ctlp, __srcu_ctr_to_ptr(srcu_ctlp, (idx & 0x8) >> 3));
|
||||
if (reader_flavor & SRCU_READ_FLAVOR_LITE)
|
||||
srcu_read_unlock_lite(srcu_ctlp, (idx & 0x4) >> 2);
|
||||
if (reader_flavor & SRCU_READ_FLAVOR_NMI)
|
||||
@ -791,6 +813,7 @@ static struct rcu_torture_ops srcu_ops = {
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
.readlock_held = torture_srcu_read_lock_held,
|
||||
.get_gp_seq = srcu_torture_completed,
|
||||
.gp_diff = rcu_seq_diff,
|
||||
.deferred_free = srcu_torture_deferred_free,
|
||||
.sync = srcu_torture_synchronize,
|
||||
.exp_sync = srcu_torture_synchronize_expedited,
|
||||
@ -834,6 +857,7 @@ static struct rcu_torture_ops srcud_ops = {
|
||||
.readunlock = srcu_torture_read_unlock,
|
||||
.readlock_held = torture_srcu_read_lock_held,
|
||||
.get_gp_seq = srcu_torture_completed,
|
||||
.gp_diff = rcu_seq_diff,
|
||||
.deferred_free = srcu_torture_deferred_free,
|
||||
.sync = srcu_torture_synchronize,
|
||||
.exp_sync = srcu_torture_synchronize_expedited,
|
||||
@ -1148,8 +1172,19 @@ static int rcu_torture_boost(void *arg)
|
||||
unsigned long gp_state;
|
||||
unsigned long gp_state_time;
|
||||
unsigned long oldstarttime;
|
||||
unsigned long booststarttime = get_torture_init_jiffies() + test_boost_holdoff * HZ;
|
||||
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_boost started");
|
||||
if (test_boost_holdoff <= 0 || time_after(jiffies, booststarttime)) {
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_boost started");
|
||||
} else {
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_boost started holdoff period");
|
||||
while (time_before(jiffies, booststarttime)) {
|
||||
schedule_timeout_idle(HZ);
|
||||
if (kthread_should_stop())
|
||||
goto cleanup;
|
||||
}
|
||||
VERBOSE_TOROUT_STRING("rcu_torture_boost finished holdoff period");
|
||||
}
|
||||
|
||||
/* Set real-time priority. */
|
||||
sched_set_fifo_low(current);
|
||||
@ -1225,6 +1260,7 @@ checkwait: if (stutter_wait("rcu_torture_boost"))
|
||||
sched_set_fifo_low(current);
|
||||
} while (!torture_must_stop());
|
||||
|
||||
cleanup:
|
||||
/* Clean up and exit. */
|
||||
while (!kthread_should_stop()) {
|
||||
torture_shutdown_absorb("rcu_torture_boost");
|
||||
@ -1728,7 +1764,7 @@ rcu_torture_fakewriter(void *arg)
|
||||
do {
|
||||
torture_hrtimeout_jiffies(torture_random(&rand) % 10, &rand);
|
||||
if (cur_ops->cb_barrier != NULL &&
|
||||
torture_random(&rand) % (nfakewriters * 8) == 0) {
|
||||
torture_random(&rand) % (nrealfakewriters * 8) == 0) {
|
||||
cur_ops->cb_barrier();
|
||||
} else {
|
||||
switch (synctype[torture_random(&rand) % nsynctypes]) {
|
||||
@ -1873,6 +1909,8 @@ static void rcu_torture_reader_do_mbchk(long myid, struct rcu_torture *rtp,
|
||||
#define ROEC_ARGS "%s %s: Current %#x To add %#x To remove %#x preempt_count() %#x\n", __func__, s, curstate, new, old, preempt_count()
|
||||
static void rcutorture_one_extend_check(char *s, int curstate, int new, int old, bool insoftirq)
|
||||
{
|
||||
int mask;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_RCU_TORTURE_TEST_CHK_RDR_STATE))
|
||||
return;
|
||||
|
||||
@ -1899,11 +1937,27 @@ static void rcutorture_one_extend_check(char *s, int curstate, int new, int old,
|
||||
WARN_ONCE(cur_ops->extendables &&
|
||||
!(curstate & (RCUTORTURE_RDR_BH | RCUTORTURE_RDR_RBH)) &&
|
||||
(preempt_count() & SOFTIRQ_MASK), ROEC_ARGS);
|
||||
WARN_ONCE(cur_ops->extendables &&
|
||||
!(curstate & (RCUTORTURE_RDR_PREEMPT | RCUTORTURE_RDR_SCHED)) &&
|
||||
|
||||
/*
|
||||
* non-preemptible RCU in a preemptible kernel uses preempt_disable()
|
||||
* as rcu_read_lock().
|
||||
*/
|
||||
mask = RCUTORTURE_RDR_PREEMPT | RCUTORTURE_RDR_SCHED;
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RCU))
|
||||
mask |= RCUTORTURE_RDR_RCU_1 | RCUTORTURE_RDR_RCU_2;
|
||||
|
||||
WARN_ONCE(cur_ops->extendables && !(curstate & mask) &&
|
||||
(preempt_count() & PREEMPT_MASK), ROEC_ARGS);
|
||||
WARN_ONCE(cur_ops->readlock_nesting &&
|
||||
!(curstate & (RCUTORTURE_RDR_RCU_1 | RCUTORTURE_RDR_RCU_2)) &&
|
||||
|
||||
/*
|
||||
* non-preemptible RCU in a preemptible kernel uses "preempt_count() &
|
||||
* PREEMPT_MASK" as ->readlock_nesting().
|
||||
*/
|
||||
mask = RCUTORTURE_RDR_RCU_1 | RCUTORTURE_RDR_RCU_2;
|
||||
if (!IS_ENABLED(CONFIG_PREEMPT_RCU))
|
||||
mask |= RCUTORTURE_RDR_PREEMPT | RCUTORTURE_RDR_SCHED;
|
||||
|
||||
WARN_ONCE(cur_ops->readlock_nesting && !(curstate & mask) &&
|
||||
cur_ops->readlock_nesting() > 0, ROEC_ARGS);
|
||||
}
|
||||
|
||||
@ -1965,6 +2019,13 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
|
||||
rtrsp[-1].rt_preempted = cur_ops->reader_blocked();
|
||||
}
|
||||
}
|
||||
// Sample grace-period sequence number, as good a place as any.
|
||||
if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP) && cur_ops->gather_gp_seqs) {
|
||||
rtrsp->rt_gp_seq = cur_ops->gather_gp_seqs();
|
||||
rtrsp->rt_ts = ktime_get_mono_fast_ns();
|
||||
if (!first)
|
||||
rtrsp[-1].rt_gp_seq_end = rtrsp->rt_gp_seq;
|
||||
}
|
||||
|
||||
/*
|
||||
* Next, remove old protection, in decreasing order of strength
|
||||
@ -2512,7 +2573,7 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
|
||||
"shuffle_interval=%d stutter=%d irqreader=%d "
|
||||
"fqs_duration=%d fqs_holdoff=%d fqs_stutter=%d "
|
||||
"test_boost=%d/%d test_boost_interval=%d "
|
||||
"test_boost_duration=%d shutdown_secs=%d "
|
||||
"test_boost_duration=%d test_boost_holdoff=%d shutdown_secs=%d "
|
||||
"stall_cpu=%d stall_cpu_holdoff=%d stall_cpu_irqsoff=%d "
|
||||
"stall_cpu_block=%d stall_cpu_repeat=%d "
|
||||
"n_barrier_cbs=%d "
|
||||
@ -2522,11 +2583,11 @@ rcu_torture_print_module_parms(struct rcu_torture_ops *cur_ops, const char *tag)
|
||||
"nocbs_nthreads=%d nocbs_toggle=%d "
|
||||
"test_nmis=%d "
|
||||
"preempt_duration=%d preempt_interval=%d\n",
|
||||
torture_type, tag, nrealreaders, nfakewriters,
|
||||
torture_type, tag, nrealreaders, nrealfakewriters,
|
||||
stat_interval, verbose, test_no_idle_hz, shuffle_interval,
|
||||
stutter, irqreader, fqs_duration, fqs_holdoff, fqs_stutter,
|
||||
test_boost, cur_ops->can_boost,
|
||||
test_boost_interval, test_boost_duration, shutdown_secs,
|
||||
test_boost_interval, test_boost_duration, test_boost_holdoff, shutdown_secs,
|
||||
stall_cpu, stall_cpu_holdoff, stall_cpu_irqsoff,
|
||||
stall_cpu_block, stall_cpu_repeat,
|
||||
n_barrier_cbs,
|
||||
@ -3553,6 +3614,7 @@ rcu_torture_cleanup(void)
|
||||
int flags = 0;
|
||||
unsigned long gp_seq = 0;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
if (torture_cleanup_begin()) {
|
||||
if (cur_ops->cb_barrier != NULL) {
|
||||
@ -3597,7 +3659,7 @@ rcu_torture_cleanup(void)
|
||||
rcu_torture_reader_mbchk = NULL;
|
||||
|
||||
if (fakewriter_tasks) {
|
||||
for (i = 0; i < nfakewriters; i++)
|
||||
for (i = 0; i < nrealfakewriters; i++)
|
||||
torture_stop_kthread(rcu_torture_fakewriter,
|
||||
fakewriter_tasks[i]);
|
||||
kfree(fakewriter_tasks);
|
||||
@ -3635,7 +3697,11 @@ rcu_torture_cleanup(void)
|
||||
pr_alert("\t: No segments recorded!!!\n");
|
||||
firsttime = 1;
|
||||
for (i = 0; i < rt_read_nsegs; i++) {
|
||||
pr_alert("\t%d: %#4x", i, err_segs[i].rt_readstate);
|
||||
if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP))
|
||||
pr_alert("\t%lluus ", div64_u64(err_segs[i].rt_ts, 1000ULL));
|
||||
else
|
||||
pr_alert("\t");
|
||||
pr_cont("%d: %#4x", i, err_segs[i].rt_readstate);
|
||||
if (err_segs[i].rt_delay_jiffies != 0) {
|
||||
pr_cont("%s%ldjiffies", firsttime ? "" : "+",
|
||||
err_segs[i].rt_delay_jiffies);
|
||||
@ -3648,6 +3714,27 @@ rcu_torture_cleanup(void)
|
||||
else
|
||||
pr_cont(" ...");
|
||||
}
|
||||
if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP) &&
|
||||
cur_ops->gather_gp_seqs && cur_ops->format_gp_seqs) {
|
||||
char buf1[20+1];
|
||||
char buf2[20+1];
|
||||
char sepchar = '-';
|
||||
|
||||
cur_ops->format_gp_seqs(err_segs[i].rt_gp_seq,
|
||||
buf1, ARRAY_SIZE(buf1));
|
||||
cur_ops->format_gp_seqs(err_segs[i].rt_gp_seq_end,
|
||||
buf2, ARRAY_SIZE(buf2));
|
||||
if (err_segs[i].rt_gp_seq == err_segs[i].rt_gp_seq_end) {
|
||||
if (buf2[0]) {
|
||||
for (j = 0; buf2[j]; j++)
|
||||
buf2[j] = '.';
|
||||
if (j)
|
||||
buf2[j - 1] = ' ';
|
||||
}
|
||||
sepchar = ' ';
|
||||
}
|
||||
pr_cont(" %s%c%s", buf1, sepchar, buf2);
|
||||
}
|
||||
if (err_segs[i].rt_delay_ms != 0) {
|
||||
pr_cont(" %s%ldms", firsttime ? "" : "+",
|
||||
err_segs[i].rt_delay_ms);
|
||||
@ -3994,6 +4081,14 @@ rcu_torture_init(void)
|
||||
|
||||
rcu_torture_init_srcu_lockdep();
|
||||
|
||||
if (nfakewriters >= 0) {
|
||||
nrealfakewriters = nfakewriters;
|
||||
} else {
|
||||
nrealfakewriters = num_online_cpus() - 2 - nfakewriters;
|
||||
if (nrealfakewriters <= 0)
|
||||
nrealfakewriters = 1;
|
||||
}
|
||||
|
||||
if (nreaders >= 0) {
|
||||
nrealreaders = nreaders;
|
||||
} else {
|
||||
@ -4050,8 +4145,9 @@ rcu_torture_init(void)
|
||||
writer_task);
|
||||
if (torture_init_error(firsterr))
|
||||
goto unwind;
|
||||
if (nfakewriters > 0) {
|
||||
fakewriter_tasks = kcalloc(nfakewriters,
|
||||
|
||||
if (nrealfakewriters > 0) {
|
||||
fakewriter_tasks = kcalloc(nrealfakewriters,
|
||||
sizeof(fakewriter_tasks[0]),
|
||||
GFP_KERNEL);
|
||||
if (fakewriter_tasks == NULL) {
|
||||
@ -4060,7 +4156,7 @@ rcu_torture_init(void)
|
||||
goto unwind;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < nfakewriters; i++) {
|
||||
for (i = 0; i < nrealfakewriters; i++) {
|
||||
firsterr = torture_create_kthread(rcu_torture_fakewriter,
|
||||
NULL, fakewriter_tasks[i]);
|
||||
if (torture_init_error(firsterr))
|
||||
|
@ -216,6 +216,36 @@ static const struct ref_scale_ops srcu_ops = {
|
||||
.name = "srcu"
|
||||
};
|
||||
|
||||
static void srcu_fast_ref_scale_read_section(const int nloops)
|
||||
{
|
||||
int i;
|
||||
struct srcu_ctr __percpu *scp;
|
||||
|
||||
for (i = nloops; i >= 0; i--) {
|
||||
scp = srcu_read_lock_fast(srcu_ctlp);
|
||||
srcu_read_unlock_fast(srcu_ctlp, scp);
|
||||
}
|
||||
}
|
||||
|
||||
static void srcu_fast_ref_scale_delay_section(const int nloops, const int udl, const int ndl)
|
||||
{
|
||||
int i;
|
||||
struct srcu_ctr __percpu *scp;
|
||||
|
||||
for (i = nloops; i >= 0; i--) {
|
||||
scp = srcu_read_lock_fast(srcu_ctlp);
|
||||
un_delay(udl, ndl);
|
||||
srcu_read_unlock_fast(srcu_ctlp, scp);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct ref_scale_ops srcu_fast_ops = {
|
||||
.init = rcu_sync_scale_init,
|
||||
.readsection = srcu_fast_ref_scale_read_section,
|
||||
.delaysection = srcu_fast_ref_scale_delay_section,
|
||||
.name = "srcu-fast"
|
||||
};
|
||||
|
||||
static void srcu_lite_ref_scale_read_section(const int nloops)
|
||||
{
|
||||
int i;
|
||||
@ -1163,7 +1193,7 @@ ref_scale_init(void)
|
||||
long i;
|
||||
int firsterr = 0;
|
||||
static const struct ref_scale_ops *scale_ops[] = {
|
||||
&rcu_ops, &srcu_ops, &srcu_lite_ops, RCU_TRACE_OPS RCU_TASKS_OPS
|
||||
&rcu_ops, &srcu_ops, &srcu_fast_ops, &srcu_lite_ops, RCU_TRACE_OPS RCU_TASKS_OPS
|
||||
&refcnt_ops, &rwlock_ops, &rwsem_ops, &lock_ops, &lock_irq_ops,
|
||||
&acqrel_ops, &sched_clock_ops, &clock_ops, &jiffies_ops,
|
||||
&typesafe_ref_ops, &typesafe_lock_ops, &typesafe_seqlock_ops,
|
||||
|
@ -20,7 +20,11 @@
|
||||
#include "rcu_segcblist.h"
|
||||
#include "rcu.h"
|
||||
|
||||
#ifndef CONFIG_TREE_RCU
|
||||
int rcu_scheduler_active __read_mostly;
|
||||
#else // #ifndef CONFIG_TREE_RCU
|
||||
extern int rcu_scheduler_active;
|
||||
#endif // #else // #ifndef CONFIG_TREE_RCU
|
||||
static LIST_HEAD(srcu_boot_list);
|
||||
static bool srcu_init_done;
|
||||
|
||||
@ -98,7 +102,7 @@ void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
int newval;
|
||||
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
newval = READ_ONCE(ssp->srcu_lock_nesting[idx]) - 1;
|
||||
WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval);
|
||||
preempt_enable();
|
||||
@ -120,7 +124,7 @@ void srcu_drive_gp(struct work_struct *wp)
|
||||
struct srcu_struct *ssp;
|
||||
|
||||
ssp = container_of(wp, struct srcu_struct, srcu_work);
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
if (ssp->srcu_gp_running || ULONG_CMP_GE(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max))) {
|
||||
preempt_enable();
|
||||
return; /* Already running or nothing to do. */
|
||||
@ -138,7 +142,7 @@ void srcu_drive_gp(struct work_struct *wp)
|
||||
WRITE_ONCE(ssp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */
|
||||
preempt_enable();
|
||||
swait_event_exclusive(ssp->srcu_wq, !READ_ONCE(ssp->srcu_lock_nesting[idx]));
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
WRITE_ONCE(ssp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */
|
||||
WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1);
|
||||
preempt_enable();
|
||||
@ -159,7 +163,7 @@ void srcu_drive_gp(struct work_struct *wp)
|
||||
* at interrupt level, but the ->srcu_gp_running checks will
|
||||
* straighten that out.
|
||||
*/
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
WRITE_ONCE(ssp->srcu_gp_running, false);
|
||||
idx = ULONG_CMP_LT(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max));
|
||||
preempt_enable();
|
||||
@ -172,7 +176,7 @@ static void srcu_gp_start_if_needed(struct srcu_struct *ssp)
|
||||
{
|
||||
unsigned long cookie;
|
||||
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
cookie = get_state_synchronize_srcu(ssp);
|
||||
if (ULONG_CMP_GE(READ_ONCE(ssp->srcu_idx_max), cookie)) {
|
||||
preempt_enable();
|
||||
@ -199,7 +203,7 @@ void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp,
|
||||
|
||||
rhp->func = func;
|
||||
rhp->next = NULL;
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
local_irq_save(flags);
|
||||
*ssp->srcu_cb_tail = rhp;
|
||||
ssp->srcu_cb_tail = &rhp->next;
|
||||
@ -261,7 +265,7 @@ unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp)
|
||||
{
|
||||
unsigned long ret;
|
||||
|
||||
preempt_disable(); // Needed for PREEMPT_AUTO
|
||||
preempt_disable(); // Needed for PREEMPT_LAZY
|
||||
ret = get_state_synchronize_srcu(ssp);
|
||||
srcu_gp_start_if_needed(ssp);
|
||||
preempt_enable();
|
||||
@ -282,11 +286,13 @@ bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(poll_state_synchronize_srcu);
|
||||
|
||||
#ifndef CONFIG_TREE_RCU
|
||||
/* Lockdep diagnostics. */
|
||||
void __init rcu_scheduler_starting(void)
|
||||
{
|
||||
rcu_scheduler_active = RCU_SCHEDULER_RUNNING;
|
||||
}
|
||||
#endif // #ifndef CONFIG_TREE_RCU
|
||||
|
||||
/*
|
||||
* Queue work for srcu_struct structures with early boot callbacks.
|
||||
|
@ -116,8 +116,9 @@ do { \
|
||||
/*
|
||||
* Initialize SRCU per-CPU data. Note that statically allocated
|
||||
* srcu_struct structures might already have srcu_read_lock() and
|
||||
* srcu_read_unlock() running against them. So if the is_static parameter
|
||||
* is set, don't initialize ->srcu_lock_count[] and ->srcu_unlock_count[].
|
||||
* srcu_read_unlock() running against them. So if the is_static
|
||||
* parameter is set, don't initialize ->srcu_ctrs[].srcu_locks and
|
||||
* ->srcu_ctrs[].srcu_unlocks.
|
||||
*/
|
||||
static void init_srcu_struct_data(struct srcu_struct *ssp)
|
||||
{
|
||||
@ -128,8 +129,6 @@ static void init_srcu_struct_data(struct srcu_struct *ssp)
|
||||
* Initialize the per-CPU srcu_data array, which feeds into the
|
||||
* leaves of the srcu_node tree.
|
||||
*/
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sdp->srcu_lock_count) !=
|
||||
ARRAY_SIZE(sdp->srcu_unlock_count));
|
||||
for_each_possible_cpu(cpu) {
|
||||
sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
spin_lock_init(&ACCESS_PRIVATE(sdp, lock));
|
||||
@ -247,15 +246,16 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp, bool is_static)
|
||||
ssp->srcu_sup->node = NULL;
|
||||
mutex_init(&ssp->srcu_sup->srcu_cb_mutex);
|
||||
mutex_init(&ssp->srcu_sup->srcu_gp_mutex);
|
||||
ssp->srcu_idx = 0;
|
||||
ssp->srcu_sup->srcu_gp_seq = SRCU_GP_SEQ_INITIAL_VAL;
|
||||
ssp->srcu_sup->srcu_barrier_seq = 0;
|
||||
mutex_init(&ssp->srcu_sup->srcu_barrier_mutex);
|
||||
atomic_set(&ssp->srcu_sup->srcu_barrier_cpu_cnt, 0);
|
||||
INIT_DELAYED_WORK(&ssp->srcu_sup->work, process_srcu);
|
||||
ssp->srcu_sup->sda_is_static = is_static;
|
||||
if (!is_static)
|
||||
if (!is_static) {
|
||||
ssp->sda = alloc_percpu(struct srcu_data);
|
||||
ssp->srcu_ctrp = &ssp->sda->srcu_ctrs[0];
|
||||
}
|
||||
if (!ssp->sda)
|
||||
goto err_free_sup;
|
||||
init_srcu_struct_data(ssp);
|
||||
@ -429,10 +429,10 @@ static bool srcu_gp_is_expedited(struct srcu_struct *ssp)
|
||||
}
|
||||
|
||||
/*
|
||||
* Computes approximate total of the readers' ->srcu_lock_count[] values
|
||||
* for the rank of per-CPU counters specified by idx, and returns true if
|
||||
* the caller did the proper barrier (gp), and if the count of the locks
|
||||
* matches that of the unlocks passed in.
|
||||
* Computes approximate total of the readers' ->srcu_ctrs[].srcu_locks
|
||||
* values for the rank of per-CPU counters specified by idx, and returns
|
||||
* true if the caller did the proper barrier (gp), and if the count of
|
||||
* the locks matches that of the unlocks passed in.
|
||||
*/
|
||||
static bool srcu_readers_lock_idx(struct srcu_struct *ssp, int idx, bool gp, unsigned long unlocks)
|
||||
{
|
||||
@ -443,20 +443,20 @@ static bool srcu_readers_lock_idx(struct srcu_struct *ssp, int idx, bool gp, uns
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += atomic_long_read(&sdp->srcu_lock_count[idx]);
|
||||
sum += atomic_long_read(&sdp->srcu_ctrs[idx].srcu_locks);
|
||||
if (IS_ENABLED(CONFIG_PROVE_RCU))
|
||||
mask = mask | READ_ONCE(sdp->srcu_reader_flavor);
|
||||
}
|
||||
WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && (mask & (mask - 1)),
|
||||
"Mixed reader flavors for srcu_struct at %ps.\n", ssp);
|
||||
if (mask & SRCU_READ_FLAVOR_LITE && !gp)
|
||||
if (mask & SRCU_READ_FLAVOR_SLOWGP && !gp)
|
||||
return false;
|
||||
return sum == unlocks;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns approximate total of the readers' ->srcu_unlock_count[] values
|
||||
* for the rank of per-CPU counters specified by idx.
|
||||
* Returns approximate total of the readers' ->srcu_ctrs[].srcu_unlocks
|
||||
* values for the rank of per-CPU counters specified by idx.
|
||||
*/
|
||||
static unsigned long srcu_readers_unlock_idx(struct srcu_struct *ssp, int idx, unsigned long *rdm)
|
||||
{
|
||||
@ -467,7 +467,7 @@ static unsigned long srcu_readers_unlock_idx(struct srcu_struct *ssp, int idx, u
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += atomic_long_read(&sdp->srcu_unlock_count[idx]);
|
||||
sum += atomic_long_read(&sdp->srcu_ctrs[idx].srcu_unlocks);
|
||||
mask = mask | READ_ONCE(sdp->srcu_reader_flavor);
|
||||
}
|
||||
WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) && (mask & (mask - 1)),
|
||||
@ -487,7 +487,7 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
|
||||
unsigned long unlocks;
|
||||
|
||||
unlocks = srcu_readers_unlock_idx(ssp, idx, &rdm);
|
||||
did_gp = !!(rdm & SRCU_READ_FLAVOR_LITE);
|
||||
did_gp = !!(rdm & SRCU_READ_FLAVOR_SLOWGP);
|
||||
|
||||
/*
|
||||
* Make sure that a lock is always counted if the corresponding
|
||||
@ -509,48 +509,49 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
|
||||
* If the locks are the same as the unlocks, then there must have
|
||||
* been no readers on this index at some point in this function.
|
||||
* But there might be more readers, as a task might have read
|
||||
* the current ->srcu_idx but not yet have incremented its CPU's
|
||||
* ->srcu_lock_count[idx] counter. In fact, it is possible
|
||||
* the current ->srcu_ctrp but not yet have incremented its CPU's
|
||||
* ->srcu_ctrs[idx].srcu_locks counter. In fact, it is possible
|
||||
* that most of the tasks have been preempted between fetching
|
||||
* ->srcu_idx and incrementing ->srcu_lock_count[idx]. And there
|
||||
* could be almost (ULONG_MAX / sizeof(struct task_struct)) tasks
|
||||
* in a system whose address space was fully populated with memory.
|
||||
* Call this quantity Nt.
|
||||
* ->srcu_ctrp and incrementing ->srcu_ctrs[idx].srcu_locks. And
|
||||
* there could be almost (ULONG_MAX / sizeof(struct task_struct))
|
||||
* tasks in a system whose address space was fully populated
|
||||
* with memory. Call this quantity Nt.
|
||||
*
|
||||
* So suppose that the updater is preempted at this point in the
|
||||
* code for a long time. That now-preempted updater has already
|
||||
* flipped ->srcu_idx (possibly during the preceding grace period),
|
||||
* done an smp_mb() (again, possibly during the preceding grace
|
||||
* period), and summed up the ->srcu_unlock_count[idx] counters.
|
||||
* How many times can a given one of the aforementioned Nt tasks
|
||||
* increment the old ->srcu_idx value's ->srcu_lock_count[idx]
|
||||
* counter, in the absence of nesting?
|
||||
* So suppose that the updater is preempted at this
|
||||
* point in the code for a long time. That now-preempted
|
||||
* updater has already flipped ->srcu_ctrp (possibly during
|
||||
* the preceding grace period), done an smp_mb() (again,
|
||||
* possibly during the preceding grace period), and summed up
|
||||
* the ->srcu_ctrs[idx].srcu_unlocks counters. How many times
|
||||
* can a given one of the aforementioned Nt tasks increment the
|
||||
* old ->srcu_ctrp value's ->srcu_ctrs[idx].srcu_locks counter,
|
||||
* in the absence of nesting?
|
||||
*
|
||||
* It can clearly do so once, given that it has already fetched
|
||||
* the old value of ->srcu_idx and is just about to use that value
|
||||
* to index its increment of ->srcu_lock_count[idx]. But as soon as
|
||||
* it leaves that SRCU read-side critical section, it will increment
|
||||
* ->srcu_unlock_count[idx], which must follow the updater's above
|
||||
* read from that same value. Thus, as soon the reading task does
|
||||
* an smp_mb() and a later fetch from ->srcu_idx, that task will be
|
||||
* guaranteed to get the new index. Except that the increment of
|
||||
* ->srcu_unlock_count[idx] in __srcu_read_unlock() is after the
|
||||
* smp_mb(), and the fetch from ->srcu_idx in __srcu_read_lock()
|
||||
* is before the smp_mb(). Thus, that task might not see the new
|
||||
* value of ->srcu_idx until the -second- __srcu_read_lock(),
|
||||
* which in turn means that this task might well increment
|
||||
* ->srcu_lock_count[idx] for the old value of ->srcu_idx twice,
|
||||
* not just once.
|
||||
* the old value of ->srcu_ctrp and is just about to use that
|
||||
* value to index its increment of ->srcu_ctrs[idx].srcu_locks.
|
||||
* But as soon as it leaves that SRCU read-side critical section,
|
||||
* it will increment ->srcu_ctrs[idx].srcu_unlocks, which must
|
||||
* follow the updater's above read from that same value. Thus,
|
||||
as soon the reading task does an smp_mb() and a later fetch from
|
||||
* ->srcu_ctrp, that task will be guaranteed to get the new index.
|
||||
* Except that the increment of ->srcu_ctrs[idx].srcu_unlocks
|
||||
* in __srcu_read_unlock() is after the smp_mb(), and the fetch
|
||||
* from ->srcu_ctrp in __srcu_read_lock() is before the smp_mb().
|
||||
* Thus, that task might not see the new value of ->srcu_ctrp until
|
||||
* the -second- __srcu_read_lock(), which in turn means that this
|
||||
* task might well increment ->srcu_ctrs[idx].srcu_locks for the
|
||||
* old value of ->srcu_ctrp twice, not just once.
|
||||
*
|
||||
* However, it is important to note that a given smp_mb() takes
|
||||
* effect not just for the task executing it, but also for any
|
||||
* later task running on that same CPU.
|
||||
*
|
||||
* That is, there can be almost Nt + Nc further increments of
|
||||
* ->srcu_lock_count[idx] for the old index, where Nc is the number
|
||||
* of CPUs. But this is OK because the size of the task_struct
|
||||
* structure limits the value of Nt and current systems limit Nc
|
||||
* to a few thousand.
|
||||
* That is, there can be almost Nt + Nc further increments
|
||||
* of ->srcu_ctrs[idx].srcu_locks for the old index, where Nc
|
||||
* is the number of CPUs. But this is OK because the size of
|
||||
* the task_struct structure limits the value of Nt and current
|
||||
* systems limit Nc to a few thousand.
|
||||
*
|
||||
* OK, but what about nesting? This does impose a limit on
|
||||
* nesting of half of the size of the task_struct structure
|
||||
@ -581,10 +582,10 @@ static bool srcu_readers_active(struct srcu_struct *ssp)
|
||||
for_each_possible_cpu(cpu) {
|
||||
struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
|
||||
sum += atomic_long_read(&sdp->srcu_lock_count[0]);
|
||||
sum += atomic_long_read(&sdp->srcu_lock_count[1]);
|
||||
sum -= atomic_long_read(&sdp->srcu_unlock_count[0]);
|
||||
sum -= atomic_long_read(&sdp->srcu_unlock_count[1]);
|
||||
sum += atomic_long_read(&sdp->srcu_ctrs[0].srcu_locks);
|
||||
sum += atomic_long_read(&sdp->srcu_ctrs[1].srcu_locks);
|
||||
sum -= atomic_long_read(&sdp->srcu_ctrs[0].srcu_unlocks);
|
||||
sum -= atomic_long_read(&sdp->srcu_ctrs[1].srcu_unlocks);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@ -647,6 +648,7 @@ static unsigned long srcu_get_delay(struct srcu_struct *ssp)
|
||||
unsigned long jbase = SRCU_INTERVAL;
|
||||
struct srcu_usage *sup = ssp->srcu_sup;
|
||||
|
||||
lockdep_assert_held(&ACCESS_PRIVATE(ssp->srcu_sup, lock));
|
||||
if (srcu_gp_is_expedited(ssp))
|
||||
jbase = 0;
|
||||
if (rcu_seq_state(READ_ONCE(sup->srcu_gp_seq))) {
|
||||
@ -674,9 +676,13 @@ static unsigned long srcu_get_delay(struct srcu_struct *ssp)
|
||||
void cleanup_srcu_struct(struct srcu_struct *ssp)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long delay;
|
||||
struct srcu_usage *sup = ssp->srcu_sup;
|
||||
|
||||
if (WARN_ON(!srcu_get_delay(ssp)))
|
||||
spin_lock_irq_rcu_node(ssp->srcu_sup);
|
||||
delay = srcu_get_delay(ssp);
|
||||
spin_unlock_irq_rcu_node(ssp->srcu_sup);
|
||||
if (WARN_ON(!delay))
|
||||
return; /* Just leak it! */
|
||||
if (WARN_ON(srcu_readers_active(ssp)))
|
||||
return; /* Just leak it! */
|
||||
@ -743,12 +749,11 @@ EXPORT_SYMBOL_GPL(__srcu_check_read_flavor);
|
||||
*/
|
||||
int __srcu_read_lock(struct srcu_struct *ssp)
|
||||
{
|
||||
int idx;
|
||||
struct srcu_ctr __percpu *scp = READ_ONCE(ssp->srcu_ctrp);
|
||||
|
||||
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
|
||||
this_cpu_inc(ssp->sda->srcu_lock_count[idx].counter);
|
||||
this_cpu_inc(scp->srcu_locks.counter);
|
||||
smp_mb(); /* B */ /* Avoid leaking the critical section. */
|
||||
return idx;
|
||||
return __srcu_ptr_to_ctr(ssp, scp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_lock);
|
||||
|
||||
@ -760,7 +765,7 @@ EXPORT_SYMBOL_GPL(__srcu_read_lock);
|
||||
void __srcu_read_unlock(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
smp_mb(); /* C */ /* Avoid leaking the critical section. */
|
||||
this_cpu_inc(ssp->sda->srcu_unlock_count[idx].counter);
|
||||
this_cpu_inc(__srcu_ctr_to_ptr(ssp, idx)->srcu_unlocks.counter);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_unlock);
|
||||
|
||||
@ -773,13 +778,12 @@ EXPORT_SYMBOL_GPL(__srcu_read_unlock);
|
||||
*/
|
||||
int __srcu_read_lock_nmisafe(struct srcu_struct *ssp)
|
||||
{
|
||||
int idx;
|
||||
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
|
||||
struct srcu_ctr __percpu *scpp = READ_ONCE(ssp->srcu_ctrp);
|
||||
struct srcu_ctr *scp = raw_cpu_ptr(scpp);
|
||||
|
||||
idx = READ_ONCE(ssp->srcu_idx) & 0x1;
|
||||
atomic_long_inc(&sdp->srcu_lock_count[idx]);
|
||||
atomic_long_inc(&scp->srcu_locks);
|
||||
smp_mb__after_atomic(); /* B */ /* Avoid leaking the critical section. */
|
||||
return idx;
|
||||
return __srcu_ptr_to_ctr(ssp, scpp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_lock_nmisafe);
|
||||
|
||||
@ -790,10 +794,8 @@ EXPORT_SYMBOL_GPL(__srcu_read_lock_nmisafe);
|
||||
*/
|
||||
void __srcu_read_unlock_nmisafe(struct srcu_struct *ssp, int idx)
|
||||
{
|
||||
struct srcu_data *sdp = raw_cpu_ptr(ssp->sda);
|
||||
|
||||
smp_mb__before_atomic(); /* C */ /* Avoid leaking the critical section. */
|
||||
atomic_long_inc(&sdp->srcu_unlock_count[idx]);
|
||||
atomic_long_inc(&raw_cpu_ptr(__srcu_ctr_to_ptr(ssp, idx))->srcu_unlocks);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__srcu_read_unlock_nmisafe);
|
||||
|
||||
@ -1096,13 +1098,15 @@ static void srcu_funnel_gp_start(struct srcu_struct *ssp, struct srcu_data *sdp,
|
||||
/*
|
||||
* Wait until all readers counted by array index idx complete, but
|
||||
* loop an additional time if there is an expedited grace period pending.
|
||||
* The caller must ensure that ->srcu_idx is not changed while checking.
|
||||
* The caller must ensure that ->srcu_ctrp is not changed while checking.
|
||||
*/
|
||||
static bool try_check_zero(struct srcu_struct *ssp, int idx, int trycount)
|
||||
{
|
||||
unsigned long curdelay;
|
||||
|
||||
spin_lock_irq_rcu_node(ssp->srcu_sup);
|
||||
curdelay = !srcu_get_delay(ssp);
|
||||
spin_unlock_irq_rcu_node(ssp->srcu_sup);
|
||||
|
||||
for (;;) {
|
||||
if (srcu_readers_active_idx_check(ssp, idx))
|
||||
@ -1114,30 +1118,30 @@ static bool try_check_zero(struct srcu_struct *ssp, int idx, int trycount)
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment the ->srcu_idx counter so that future SRCU readers will
|
||||
* Increment the ->srcu_ctrp counter so that future SRCU readers will
|
||||
* use the other rank of the ->srcu_(un)lock_count[] arrays. This allows
|
||||
* us to wait for pre-existing readers in a starvation-free manner.
|
||||
*/
|
||||
static void srcu_flip(struct srcu_struct *ssp)
|
||||
{
|
||||
/*
|
||||
* Because the flip of ->srcu_idx is executed only if the
|
||||
* Because the flip of ->srcu_ctrp is executed only if the
|
||||
* preceding call to srcu_readers_active_idx_check() found that
|
||||
* the ->srcu_unlock_count[] and ->srcu_lock_count[] sums matched
|
||||
* and because that summing uses atomic_long_read(), there is
|
||||
* ordering due to a control dependency between that summing and
|
||||
* the WRITE_ONCE() in this call to srcu_flip(). This ordering
|
||||
* ensures that if this updater saw a given reader's increment from
|
||||
* __srcu_read_lock(), that reader was using a value of ->srcu_idx
|
||||
* from before the previous call to srcu_flip(), which should be
|
||||
* quite rare. This ordering thus helps forward progress because
|
||||
* the grace period could otherwise be delayed by additional
|
||||
* calls to __srcu_read_lock() using that old (soon to be new)
|
||||
* value of ->srcu_idx.
|
||||
* the ->srcu_ctrs[].srcu_unlocks and ->srcu_ctrs[].srcu_locks sums
|
||||
* matched and because that summing uses atomic_long_read(),
|
||||
* there is ordering due to a control dependency between that
|
||||
* summing and the WRITE_ONCE() in this call to srcu_flip().
|
||||
* This ordering ensures that if this updater saw a given reader's
|
||||
* increment from __srcu_read_lock(), that reader was using a value
|
||||
* of ->srcu_ctrp from before the previous call to srcu_flip(),
|
||||
* which should be quite rare. This ordering thus helps forward
|
||||
* progress because the grace period could otherwise be delayed
|
||||
* by additional calls to __srcu_read_lock() using that old (soon
|
||||
* to be new) value of ->srcu_ctrp.
|
||||
*
|
||||
* This sum-equality check and ordering also ensures that if
|
||||
* a given call to __srcu_read_lock() uses the new value of
|
||||
* ->srcu_idx, this updater's earlier scans cannot have seen
|
||||
* ->srcu_ctrp, this updater's earlier scans cannot have seen
|
||||
* that reader's increments, which is all to the good, because
|
||||
* this grace period need not wait on that reader. After all,
|
||||
* if those earlier scans had seen that reader, there would have
|
||||
@ -1152,7 +1156,8 @@ static void srcu_flip(struct srcu_struct *ssp)
|
||||
*/
|
||||
smp_mb(); /* E */ /* Pairs with B and C. */
|
||||
|
||||
WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); // Flip the counter.
|
||||
WRITE_ONCE(ssp->srcu_ctrp,
|
||||
&ssp->sda->srcu_ctrs[!(ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0])]);
|
||||
|
||||
/*
|
||||
* Ensure that if the updater misses an __srcu_read_unlock()
|
||||
@ -1198,7 +1203,7 @@ static bool srcu_should_expedite(struct srcu_struct *ssp)
|
||||
|
||||
check_init_srcu_struct(ssp);
|
||||
/* If _lite() readers, don't do unsolicited expediting. */
|
||||
if (this_cpu_read(ssp->sda->srcu_reader_flavor) & SRCU_READ_FLAVOR_LITE)
|
||||
if (this_cpu_read(ssp->sda->srcu_reader_flavor) & SRCU_READ_FLAVOR_SLOWGP)
|
||||
return false;
|
||||
/* If the local srcu_data structure has callbacks, not idle. */
|
||||
sdp = raw_cpu_ptr(ssp->sda);
|
||||
@ -1469,8 +1474,9 @@ EXPORT_SYMBOL_GPL(synchronize_srcu_expedited);
|
||||
*
|
||||
* Wait for the count to drain to zero of both indexes. To avoid the
|
||||
* possible starvation of synchronize_srcu(), it waits for the count of
|
||||
* the index=((->srcu_idx & 1) ^ 1) to drain to zero at first,
|
||||
* and then flip the srcu_idx and wait for the count of the other index.
|
||||
* the index=!(ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0]) to drain to zero
|
||||
* at first, and then flip the ->srcu_ctrp and wait for the count of the
|
||||
* other index.
|
||||
*
|
||||
* Can block; must be called from process context.
|
||||
*
|
||||
@ -1679,7 +1685,7 @@ EXPORT_SYMBOL_GPL(srcu_barrier);
|
||||
*/
|
||||
unsigned long srcu_batches_completed(struct srcu_struct *ssp)
|
||||
{
|
||||
return READ_ONCE(ssp->srcu_idx);
|
||||
return READ_ONCE(ssp->srcu_sup->srcu_gp_seq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(srcu_batches_completed);
|
||||
|
||||
@ -1696,7 +1702,7 @@ static void srcu_advance_state(struct srcu_struct *ssp)
|
||||
|
||||
/*
|
||||
* Because readers might be delayed for an extended period after
|
||||
* fetching ->srcu_idx for their index, at any point in time there
|
||||
* fetching ->srcu_ctrp for their index, at any point in time there
|
||||
* might well be readers using both idx=0 and idx=1. We therefore
|
||||
* need to wait for readers to clear from both index values before
|
||||
* invoking a callback.
|
||||
@ -1724,7 +1730,7 @@ static void srcu_advance_state(struct srcu_struct *ssp)
|
||||
}
|
||||
|
||||
if (rcu_seq_state(READ_ONCE(ssp->srcu_sup->srcu_gp_seq)) == SRCU_STATE_SCAN1) {
|
||||
idx = 1 ^ (ssp->srcu_idx & 1);
|
||||
idx = !(ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0]);
|
||||
if (!try_check_zero(ssp, idx, 1)) {
|
||||
mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
|
||||
return; /* readers present, retry later. */
|
||||
@ -1742,7 +1748,7 @@ static void srcu_advance_state(struct srcu_struct *ssp)
|
||||
* SRCU read-side critical sections are normally short,
|
||||
* so check at least twice in quick succession after a flip.
|
||||
*/
|
||||
idx = 1 ^ (ssp->srcu_idx & 1);
|
||||
idx = !(ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0]);
|
||||
if (!try_check_zero(ssp, idx, 2)) {
|
||||
mutex_unlock(&ssp->srcu_sup->srcu_gp_mutex);
|
||||
return; /* readers present, retry later. */
|
||||
@ -1853,7 +1859,9 @@ static void process_srcu(struct work_struct *work)
|
||||
ssp = sup->srcu_ssp;
|
||||
|
||||
srcu_advance_state(ssp);
|
||||
spin_lock_irq_rcu_node(ssp->srcu_sup);
|
||||
curdelay = srcu_get_delay(ssp);
|
||||
spin_unlock_irq_rcu_node(ssp->srcu_sup);
|
||||
if (curdelay) {
|
||||
WRITE_ONCE(sup->reschedule_count, 0);
|
||||
} else {
|
||||
@ -1900,7 +1908,7 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
|
||||
int ss_state = READ_ONCE(ssp->srcu_sup->srcu_size_state);
|
||||
int ss_state_idx = ss_state;
|
||||
|
||||
idx = ssp->srcu_idx & 0x1;
|
||||
idx = ssp->srcu_ctrp - &ssp->sda->srcu_ctrs[0];
|
||||
if (ss_state < 0 || ss_state >= ARRAY_SIZE(srcu_size_state_name))
|
||||
ss_state_idx = ARRAY_SIZE(srcu_size_state_name) - 1;
|
||||
pr_alert("%s%s Tree SRCU g%ld state %d (%s)",
|
||||
@ -1918,8 +1926,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
|
||||
struct srcu_data *sdp;
|
||||
|
||||
sdp = per_cpu_ptr(ssp->sda, cpu);
|
||||
u0 = data_race(atomic_long_read(&sdp->srcu_unlock_count[!idx]));
|
||||
u1 = data_race(atomic_long_read(&sdp->srcu_unlock_count[idx]));
|
||||
u0 = data_race(atomic_long_read(&sdp->srcu_ctrs[!idx].srcu_unlocks));
|
||||
u1 = data_race(atomic_long_read(&sdp->srcu_ctrs[idx].srcu_unlocks));
|
||||
|
||||
/*
|
||||
* Make sure that a lock is always counted if the corresponding
|
||||
@ -1927,8 +1935,8 @@ void srcu_torture_stats_print(struct srcu_struct *ssp, char *tt, char *tf)
|
||||
*/
|
||||
smp_rmb();
|
||||
|
||||
l0 = data_race(atomic_long_read(&sdp->srcu_lock_count[!idx]));
|
||||
l1 = data_race(atomic_long_read(&sdp->srcu_lock_count[idx]));
|
||||
l0 = data_race(atomic_long_read(&sdp->srcu_ctrs[!idx].srcu_locks));
|
||||
l1 = data_race(atomic_long_read(&sdp->srcu_ctrs[idx].srcu_locks));
|
||||
|
||||
c0 = l0 - u0;
|
||||
c1 = l1 - u1;
|
||||
@ -2005,6 +2013,7 @@ static int srcu_module_coming(struct module *mod)
|
||||
ssp->sda = alloc_percpu(struct srcu_data);
|
||||
if (WARN_ON_ONCE(!ssp->sda))
|
||||
return -ENOMEM;
|
||||
ssp->srcu_ctrp = &ssp->sda->srcu_ctrs[0];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -2256,7 +2256,7 @@ void __init tasks_cblist_init_generic(void)
|
||||
#endif
|
||||
}
|
||||
|
||||
void __init rcu_init_tasks_generic(void)
|
||||
static int __init rcu_init_tasks_generic(void)
|
||||
{
|
||||
#ifdef CONFIG_TASKS_RCU
|
||||
rcu_spawn_tasks_kthread();
|
||||
@ -2272,7 +2272,10 @@ void __init rcu_init_tasks_generic(void)
|
||||
|
||||
// Run the self-tests.
|
||||
rcu_tasks_initiate_self_tests();
|
||||
|
||||
return 0;
|
||||
}
|
||||
core_initcall(rcu_init_tasks_generic);
|
||||
|
||||
#else /* #ifdef CONFIG_TASKS_RCU_GENERIC */
|
||||
static inline void rcu_tasks_bootup_oddness(void) {}
|
||||
|
@ -257,6 +257,20 @@ void kvfree_call_rcu(struct rcu_head *head, void *ptr)
|
||||
EXPORT_SYMBOL_GPL(kvfree_call_rcu);
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST)
|
||||
unsigned long long rcutorture_gather_gp_seqs(void)
|
||||
{
|
||||
return READ_ONCE(rcu_ctrlblk.gp_seq) & 0xffffULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcutorture_gather_gp_seqs);
|
||||
|
||||
void rcutorture_format_gp_seqs(unsigned long long seqs, char *cp, size_t len)
|
||||
{
|
||||
snprintf(cp, len, "g%04llx", seqs & 0xffffULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcutorture_format_gp_seqs);
|
||||
#endif
|
||||
|
||||
void __init rcu_init(void)
|
||||
{
|
||||
open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
|
||||
|
@ -538,6 +538,26 @@ void rcutorture_get_gp_data(int *flags, unsigned long *gp_seq)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcutorture_get_gp_data);
|
||||
|
||||
/* Gather grace-period sequence numbers for rcutorture diagnostics. */
|
||||
unsigned long long rcutorture_gather_gp_seqs(void)
|
||||
{
|
||||
return ((READ_ONCE(rcu_state.gp_seq) & 0xffffULL) << 40) |
|
||||
((READ_ONCE(rcu_state.expedited_sequence) & 0xffffffULL) << 16) |
|
||||
(READ_ONCE(rcu_state.gp_seq_polled) & 0xffffULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcutorture_gather_gp_seqs);
|
||||
|
||||
/* Format grace-period sequence numbers for rcutorture diagnostics. */
|
||||
void rcutorture_format_gp_seqs(unsigned long long seqs, char *cp, size_t len)
|
||||
{
|
||||
unsigned int egp = (seqs >> 16) & 0xffffffULL;
|
||||
unsigned int ggp = (seqs >> 40) & 0xffffULL;
|
||||
unsigned int pgp = seqs & 0xffffULL;
|
||||
|
||||
snprintf(cp, len, "g%04x:e%06x:p%04x", ggp, egp, pgp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rcutorture_format_gp_seqs);
|
||||
|
||||
#if defined(CONFIG_NO_HZ_FULL) && (!defined(CONFIG_GENERIC_ENTRY) || !defined(CONFIG_KVM_XFER_TO_GUEST_WORK))
|
||||
/*
|
||||
* An empty function that will trigger a reschedule on
|
||||
@ -1254,7 +1274,7 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
|
||||
/* Handle the ends of any preceding grace periods first. */
|
||||
if (rcu_seq_completed_gp(rdp->gp_seq, rnp->gp_seq) ||
|
||||
unlikely(READ_ONCE(rdp->gpwrap))) {
|
||||
unlikely(rdp->gpwrap)) {
|
||||
if (!offloaded)
|
||||
ret = rcu_advance_cbs(rnp, rdp); /* Advance CBs. */
|
||||
rdp->core_needs_qs = false;
|
||||
@ -1268,7 +1288,7 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
|
||||
/* Now handle the beginnings of any new-to-this-CPU grace periods. */
|
||||
if (rcu_seq_new_gp(rdp->gp_seq, rnp->gp_seq) ||
|
||||
unlikely(READ_ONCE(rdp->gpwrap))) {
|
||||
unlikely(rdp->gpwrap)) {
|
||||
/*
|
||||
* If the current grace period is waiting for this CPU,
|
||||
* set up to detect a quiescent state, otherwise don't
|
||||
@ -1283,7 +1303,7 @@ static bool __note_gp_changes(struct rcu_node *rnp, struct rcu_data *rdp)
|
||||
rdp->gp_seq = rnp->gp_seq; /* Remember new grace-period state. */
|
||||
if (ULONG_CMP_LT(rdp->gp_seq_needed, rnp->gp_seq_needed) || rdp->gpwrap)
|
||||
WRITE_ONCE(rdp->gp_seq_needed, rnp->gp_seq_needed);
|
||||
if (IS_ENABLED(CONFIG_PROVE_RCU) && READ_ONCE(rdp->gpwrap))
|
||||
if (IS_ENABLED(CONFIG_PROVE_RCU) && rdp->gpwrap)
|
||||
WRITE_ONCE(rdp->last_sched_clock, jiffies);
|
||||
WRITE_ONCE(rdp->gpwrap, false);
|
||||
rcu_gpnum_ovf(rnp, rdp);
|
||||
@ -1612,12 +1632,10 @@ static void rcu_sr_normal_complete(struct llist_node *node)
|
||||
{
|
||||
struct rcu_synchronize *rs = container_of(
|
||||
(struct rcu_head *) node, struct rcu_synchronize, head);
|
||||
unsigned long oldstate = (unsigned long) rs->head.func;
|
||||
|
||||
WARN_ONCE(IS_ENABLED(CONFIG_PROVE_RCU) &&
|
||||
!poll_state_synchronize_rcu(oldstate),
|
||||
"A full grace period is not passed yet: %lu",
|
||||
rcu_seq_diff(get_state_synchronize_rcu(), oldstate));
|
||||
!poll_state_synchronize_rcu_full(&rs->oldstate),
|
||||
"A full grace period is not passed yet!\n");
|
||||
|
||||
/* Finally. */
|
||||
complete(&rs->completion);
|
||||
@ -1801,10 +1819,14 @@ static noinline_for_stack bool rcu_gp_init(void)
|
||||
|
||||
/* Advance to a new grace period and initialize state. */
|
||||
record_gp_stall_check_time();
|
||||
/*
|
||||
* A new wait segment must be started before gp_seq advanced, so
|
||||
* that previous gp waiters won't observe the new gp_seq.
|
||||
*/
|
||||
start_new_poll = rcu_sr_normal_gp_init();
|
||||
/* Record GP times before starting GP, hence rcu_seq_start(). */
|
||||
rcu_seq_start(&rcu_state.gp_seq);
|
||||
ASSERT_EXCLUSIVE_WRITER(rcu_state.gp_seq);
|
||||
start_new_poll = rcu_sr_normal_gp_init();
|
||||
trace_rcu_grace_period(rcu_state.name, rcu_state.gp_seq, TPS("start"));
|
||||
rcu_poll_gp_seq_start(&rcu_state.gp_seq_polled_snap);
|
||||
raw_spin_unlock_irq_rcu_node(rnp);
|
||||
@ -3227,7 +3249,7 @@ static void synchronize_rcu_normal(void)
|
||||
* snapshot before adding a request.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PROVE_RCU))
|
||||
rs.head.func = (void *) get_state_synchronize_rcu();
|
||||
get_state_synchronize_rcu_full(&rs.oldstate);
|
||||
|
||||
rcu_sr_normal_add_req(&rs);
|
||||
|
||||
@ -3370,14 +3392,17 @@ EXPORT_SYMBOL_GPL(get_state_synchronize_rcu);
|
||||
*/
|
||||
void get_state_synchronize_rcu_full(struct rcu_gp_oldstate *rgosp)
|
||||
{
|
||||
struct rcu_node *rnp = rcu_get_root();
|
||||
|
||||
/*
|
||||
* Any prior manipulation of RCU-protected data must happen
|
||||
* before the loads from ->gp_seq and ->expedited_sequence.
|
||||
*/
|
||||
smp_mb(); /* ^^^ */
|
||||
rgosp->rgos_norm = rcu_seq_snap(&rnp->gp_seq);
|
||||
|
||||
// Yes, rcu_state.gp_seq, not rnp_root->gp_seq, the latter's use
|
||||
// in poll_state_synchronize_rcu_full() notwithstanding. Use of
|
||||
// the latter here would result in too-short grace periods due to
|
||||
// interactions with newly onlined CPUs.
|
||||
rgosp->rgos_norm = rcu_seq_snap(&rcu_state.gp_seq);
|
||||
rgosp->rgos_exp = rcu_seq_snap(&rcu_state.expedited_sequence);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_state_synchronize_rcu_full);
|
||||
|
@ -230,17 +230,19 @@ static void __maybe_unused rcu_report_exp_rnp(struct rcu_node *rnp, bool wake)
|
||||
* specified leaf rcu_node structure, which is acquired by the caller.
|
||||
*/
|
||||
static void rcu_report_exp_cpu_mult(struct rcu_node *rnp, unsigned long flags,
|
||||
unsigned long mask, bool wake)
|
||||
unsigned long mask_in, bool wake)
|
||||
__releases(rnp->lock)
|
||||
{
|
||||
int cpu;
|
||||
unsigned long mask;
|
||||
struct rcu_data *rdp;
|
||||
|
||||
raw_lockdep_assert_held_rcu_node(rnp);
|
||||
if (!(rnp->expmask & mask)) {
|
||||
if (!(rnp->expmask & mask_in)) {
|
||||
raw_spin_unlock_irqrestore_rcu_node(rnp, flags);
|
||||
return;
|
||||
}
|
||||
mask = mask_in & rnp->expmask;
|
||||
WRITE_ONCE(rnp->expmask, rnp->expmask & ~mask);
|
||||
for_each_leaf_node_cpu_mask(rnp, cpu, mask) {
|
||||
rdp = per_cpu_ptr(&rcu_data, cpu);
|
||||
|
@ -1557,8 +1557,11 @@ static void show_rcu_nocb_gp_state(struct rcu_data *rdp)
|
||||
/* Dump out nocb kthread state for the specified rcu_data structure. */
|
||||
static void show_rcu_nocb_state(struct rcu_data *rdp)
|
||||
{
|
||||
char bufw[20];
|
||||
char bufr[20];
|
||||
char bufd[22];
|
||||
char bufw[45];
|
||||
char bufr[45];
|
||||
char bufn[22];
|
||||
char bufb[22];
|
||||
struct rcu_data *nocb_next_rdp;
|
||||
struct rcu_segcblist *rsclp = &rdp->cblist;
|
||||
bool waslocked;
|
||||
@ -1572,9 +1575,13 @@ static void show_rcu_nocb_state(struct rcu_data *rdp)
|
||||
typeof(*rdp),
|
||||
nocb_entry_rdp);
|
||||
|
||||
sprintf(bufw, "%ld", rsclp->gp_seq[RCU_WAIT_TAIL]);
|
||||
sprintf(bufr, "%ld", rsclp->gp_seq[RCU_NEXT_READY_TAIL]);
|
||||
pr_info(" CB %d^%d->%d %c%c%c%c%c F%ld L%ld C%d %c%c%s%c%s%c%c q%ld %c CPU %d%s\n",
|
||||
sprintf(bufd, "%ld", rsclp->seglen[RCU_DONE_TAIL]);
|
||||
sprintf(bufw, "%ld(%ld)", rsclp->seglen[RCU_WAIT_TAIL], rsclp->gp_seq[RCU_WAIT_TAIL]);
|
||||
sprintf(bufr, "%ld(%ld)", rsclp->seglen[RCU_NEXT_READY_TAIL],
|
||||
rsclp->gp_seq[RCU_NEXT_READY_TAIL]);
|
||||
sprintf(bufn, "%ld", rsclp->seglen[RCU_NEXT_TAIL]);
|
||||
sprintf(bufb, "%ld", rcu_cblist_n_cbs(&rdp->nocb_bypass));
|
||||
pr_info(" CB %d^%d->%d %c%c%c%c%c F%ld L%ld C%d %c%s%c%s%c%s%c%s%c%s q%ld %c CPU %d%s\n",
|
||||
rdp->cpu, rdp->nocb_gp_rdp->cpu,
|
||||
nocb_next_rdp ? nocb_next_rdp->cpu : -1,
|
||||
"kK"[!!rdp->nocb_cb_kthread],
|
||||
@ -1586,12 +1593,15 @@ static void show_rcu_nocb_state(struct rcu_data *rdp)
|
||||
jiffies - rdp->nocb_nobypass_last,
|
||||
rdp->nocb_nobypass_count,
|
||||
".D"[rcu_segcblist_ready_cbs(rsclp)],
|
||||
rcu_segcblist_segempty(rsclp, RCU_DONE_TAIL) ? "" : bufd,
|
||||
".W"[!rcu_segcblist_segempty(rsclp, RCU_WAIT_TAIL)],
|
||||
rcu_segcblist_segempty(rsclp, RCU_WAIT_TAIL) ? "" : bufw,
|
||||
".R"[!rcu_segcblist_segempty(rsclp, RCU_NEXT_READY_TAIL)],
|
||||
rcu_segcblist_segempty(rsclp, RCU_NEXT_READY_TAIL) ? "" : bufr,
|
||||
".N"[!rcu_segcblist_segempty(rsclp, RCU_NEXT_TAIL)],
|
||||
rcu_segcblist_segempty(rsclp, RCU_NEXT_TAIL) ? "" : bufn,
|
||||
".B"[!!rcu_cblist_n_cbs(&rdp->nocb_bypass)],
|
||||
!rcu_cblist_n_cbs(&rdp->nocb_bypass) ? "" : bufb,
|
||||
rcu_segcblist_n_cbs(&rdp->cblist),
|
||||
rdp->nocb_cb_kthread ? task_state_to_char(rdp->nocb_cb_kthread) : '.',
|
||||
rdp->nocb_cb_kthread ? (int)task_cpu(rdp->nocb_cb_kthread) : -1,
|
||||
|
@ -833,8 +833,17 @@ void rcu_read_unlock_strict(void)
|
||||
{
|
||||
struct rcu_data *rdp;
|
||||
|
||||
if (irqs_disabled() || preempt_count() || !rcu_state.gp_kthread)
|
||||
if (irqs_disabled() || in_atomic_preempt_off() || !rcu_state.gp_kthread)
|
||||
return;
|
||||
|
||||
/*
|
||||
* rcu_report_qs_rdp() can only be invoked with a stable rdp and
|
||||
* from the local CPU.
|
||||
*
|
||||
* The in_atomic_preempt_off() check ensures that we come here holding
|
||||
* the last preempt_count (which will get dropped once we return to
|
||||
* __rcu_read_unlock().
|
||||
*/
|
||||
rdp = this_cpu_ptr(&rcu_data);
|
||||
rdp->cpu_no_qs.b.norm = false;
|
||||
rcu_report_qs_rdp(rdp);
|
||||
@ -975,13 +984,16 @@ static void rcu_preempt_check_blocked_tasks(struct rcu_node *rnp)
|
||||
*/
|
||||
static void rcu_flavor_sched_clock_irq(int user)
|
||||
{
|
||||
if (user || rcu_is_cpu_rrupt_from_idle()) {
|
||||
if (user || rcu_is_cpu_rrupt_from_idle() ||
|
||||
(IS_ENABLED(CONFIG_PREEMPT_COUNT) &&
|
||||
(preempt_count() == HARDIRQ_OFFSET))) {
|
||||
|
||||
/*
|
||||
* Get here if this CPU took its interrupt from user
|
||||
* mode or from the idle loop, and if this is not a
|
||||
* nested interrupt. In this case, the CPU is in
|
||||
* a quiescent state, so note it.
|
||||
* mode, from the idle loop without this being a nested
|
||||
* interrupt, or while not holding the task preempt count
|
||||
* (with PREEMPT_COUNT=y). In this case, the CPU is in a
|
||||
* quiescent state, so note it.
|
||||
*
|
||||
* No memory barrier is required here because rcu_qs()
|
||||
* references only CPU-local variables that other CPUs
|
||||
|
@ -704,6 +704,7 @@ void kernel_power_off(void)
|
||||
migrate_to_reboot_cpu();
|
||||
syscore_shutdown();
|
||||
pr_emerg("Power down\n");
|
||||
pr_flush(1000, true);
|
||||
kmsg_dump(KMSG_DUMP_SHUTDOWN);
|
||||
machine_power_off();
|
||||
}
|
||||
|
@ -7289,7 +7289,7 @@ int __sched __cond_resched(void)
|
||||
return 1;
|
||||
}
|
||||
/*
|
||||
* In preemptible kernels, ->rcu_read_lock_nesting tells the tick
|
||||
* In PREEMPT_RCU kernels, ->rcu_read_lock_nesting tells the tick
|
||||
* whether the current CPU is in an RCU read-side critical section,
|
||||
* so the tick can report quiescent states even for CPUs looping
|
||||
* in kernel context. In contrast, in non-preemptible kernels,
|
||||
@ -7298,6 +7298,8 @@ int __sched __cond_resched(void)
|
||||
* RCU quiescent state. Therefore, the following code causes
|
||||
* cond_resched() to report a quiescent state, but only when RCU
|
||||
* is in urgent need of one.
|
||||
* A third case, preemptible, but non-PREEMPT_RCU provides for
|
||||
* urgently needed quiescent states via rcu_flavor_sched_clock_irq().
|
||||
*/
|
||||
#ifndef CONFIG_PREEMPT_RCU
|
||||
rcu_all_qs();
|
||||
|
@ -792,6 +792,8 @@ static void torture_stutter_cleanup(void)
|
||||
stutter_task = NULL;
|
||||
}
|
||||
|
||||
static unsigned long torture_init_jiffies;
|
||||
|
||||
static void
|
||||
torture_print_module_parms(void)
|
||||
{
|
||||
@ -821,6 +823,7 @@ bool torture_init_begin(char *ttype, int v)
|
||||
torture_type = ttype;
|
||||
verbose = v;
|
||||
fullstop = FULLSTOP_DONTSTOP;
|
||||
WRITE_ONCE(torture_init_jiffies, jiffies); // Lockless reads.
|
||||
torture_print_module_parms();
|
||||
return true;
|
||||
}
|
||||
@ -836,6 +839,15 @@ void torture_init_end(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(torture_init_end);
|
||||
|
||||
/*
|
||||
* Get the torture_init_begin()-time value of the jiffies counter.
|
||||
*/
|
||||
unsigned long get_torture_init_jiffies(void)
|
||||
{
|
||||
return READ_ONCE(torture_init_jiffies);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_torture_init_jiffies);
|
||||
|
||||
/*
|
||||
* Clean up torture module. Please note that this is -not- invoked via
|
||||
* the usual module_exit() mechanism, but rather by an explicit call from
|
||||
|
@ -1542,27 +1542,25 @@ static int run_osnoise(void)
|
||||
|
||||
/*
|
||||
* In some cases, notably when running on a nohz_full CPU with
|
||||
* a stopped tick PREEMPT_RCU has no way to account for QSs.
|
||||
* This will eventually cause unwarranted noise as PREEMPT_RCU
|
||||
* will force preemption as the means of ending the current
|
||||
* grace period. We avoid this problem by calling
|
||||
* rcu_momentary_eqs(), which performs a zero duration
|
||||
* EQS allowing PREEMPT_RCU to end the current grace period.
|
||||
* This call shouldn't be wrapped inside an RCU critical
|
||||
* section.
|
||||
* a stopped tick PREEMPT_RCU or PREEMPT_LAZY have no way to
|
||||
* account for QSs. This will eventually cause unwarranted
|
||||
* noise as RCU forces preemption as the means of ending the
|
||||
* current grace period. We avoid this by calling
|
||||
* rcu_momentary_eqs(), which performs a zero duration EQS
|
||||
* allowing RCU to end the current grace period. This call
|
||||
* shouldn't be wrapped inside an RCU critical section.
|
||||
*
|
||||
* Note that in non PREEMPT_RCU kernels QSs are handled through
|
||||
* cond_resched()
|
||||
* Normally QSs for other cases are handled through cond_resched().
|
||||
* For simplicity, however, we call rcu_momentary_eqs() for all
|
||||
* configurations here.
|
||||
*/
|
||||
if (IS_ENABLED(CONFIG_PREEMPT_RCU)) {
|
||||
if (!disable_irq)
|
||||
local_irq_disable();
|
||||
if (!disable_irq)
|
||||
local_irq_disable();
|
||||
|
||||
rcu_momentary_eqs();
|
||||
rcu_momentary_eqs();
|
||||
|
||||
if (!disable_irq)
|
||||
local_irq_enable();
|
||||
}
|
||||
if (!disable_irq)
|
||||
local_irq_enable();
|
||||
|
||||
/*
|
||||
* For the non-preemptive kernel config: let threads runs, if
|
||||
|
@ -49,7 +49,7 @@ do
|
||||
do
|
||||
err=
|
||||
val=$((d*1000+t*10+c))
|
||||
tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 5s --configs "SRCU-P" --bootargs "rcutorture.test_srcu_lockdep=$val" --trust-make --datestamp "$ds/$val" > "$T/kvm.sh.out" 2>&1
|
||||
tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 5s --configs "SRCU-P" --kconfig "CONFIG_FORCE_NEED_SRCU_NMI_SAFE=y" --bootargs "rcutorture.test_srcu_lockdep=$val rcutorture.reader_flavor=0x2" --trust-make --datestamp "$ds/$val" > "$T/kvm.sh.out" 2>&1
|
||||
ret=$?
|
||||
mv "$T/kvm.sh.out" "$RCUTORTURE/res/$ds/$val"
|
||||
if test "$d" -ne 0 && test "$ret" -eq 0
|
||||
|
@ -2,3 +2,4 @@ rcutorture.torture_type=srcud
|
||||
rcupdate.rcu_self_test=1
|
||||
rcutorture.fwd_progress=3
|
||||
srcutree.big_cpu_lim=5
|
||||
rcutorture.reader_flavor=0x8
|
||||
|
@ -2,3 +2,9 @@ rcutree.gp_preinit_delay=3
|
||||
rcutree.gp_init_delay=3
|
||||
rcutree.gp_cleanup_delay=3
|
||||
rcupdate.rcu_self_test=1
|
||||
|
||||
# This part is for synchronize_rcu() testing
|
||||
rcutorture.nfakewriters=-1
|
||||
rcutorture.gp_sync=1
|
||||
rcupdate.rcu_normal=1
|
||||
rcutree.rcu_normal_wake_from_gp=1
|
||||
|
@ -1,7 +1,8 @@
|
||||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=16
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
CONFIG_PREEMPT_NONE=n
|
||||
CONFIG_PREEMPT_VOLUNTARY=n
|
||||
CONFIG_PREEMPT_LAZY=y
|
||||
CONFIG_PREEMPT=n
|
||||
CONFIG_PREEMPT_DYNAMIC=n
|
||||
#CHECK#CONFIG_TREE_RCU=y
|
||||
|
@ -1,6 +1,7 @@
|
||||
CONFIG_SMP=y
|
||||
CONFIG_NR_CPUS=74
|
||||
CONFIG_PREEMPT_NONE=y
|
||||
CONFIG_PREEMPT_NONE=n
|
||||
CONFIG_PREEMPT_LAZY=y
|
||||
CONFIG_PREEMPT_VOLUNTARY=n
|
||||
CONFIG_PREEMPT=n
|
||||
CONFIG_PREEMPT_DYNAMIC=n
|
||||
|
Loading…
x
Reference in New Issue
Block a user