mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'rcu-next-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux
Pull RCU updates from Boqun Feng:
"Documentation:
- Add broken-timing possibility to stallwarn.rst
- Improve discussion of this_cpu_ptr(), add raw_cpu_ptr()
- Document self-propagating callbacks
- Point call_srcu() to call_rcu() for detailed memory ordering
- Add CONFIG_RCU_LAZY delays to call_rcu() kernel-doc header
- Clarify RCU_LAZY and RCU_LAZY_DEFAULT_OFF help text
- Remove references to old grace-period-wait primitives
srcu:
- Introduce srcu_read_{un,}lock_fast(), which is similar to
srcu_read_{un,}lock_lite(): avoid smp_mb()s in lock and unlock
at the cost of calling synchronize_rcu() in synchronize_srcu()
Moreover, by returning the percpu offset of the counter at
srcu_read_lock_fast() time, srcu_read_unlock_fast() can avoid
extra pointer dereferencing, which makes it faster than
srcu_read_{un,}lock_lite()
srcu_read_{un,}lock_fast() are intended to replace
rcu_read_{un,}lock_trace() if possible
RCU torture:
- Add get_torture_init_jiffies() to return the start time of the test
- Add a test_boost_holdoff module parameter to allow delaying
boosting tests when building rcutorture as built-in
- Add grace period sequence number logging at the beginning and end
of failure/close-call results
- Switch to hexadecimal for the expedited grace period sequence
number in the rcu_exp_grace_period trace point
- Make cur_ops->format_gp_seqs take buffer length
- Move RCU_TORTURE_TEST_{CHK_RDR_STATE,LOG_CPU} to bool
- Complain when invalid SRCU reader_flavor is specified
- Add FORCE_NEED_SRCU_NMI_SAFE Kconfig for testing, which forces SRCU
uses atomics even when percpu ops are NMI safe, and use the Kconfig
for SRCU lockdep testing
Misc:
- Split rcu_report_exp_cpu_mult() mask parameter and use for tracing
- Remove READ_ONCE() for rdp->gpwrap access in __note_gp_changes()
- Fix get_state_synchronize_rcu_full() GP-start detection
- Move RCU Tasks self-tests to core_initcall()
- Print segment lengths in show_rcu_nocb_gp_state()
- Make RCU watch ct_kernel_exit_state() warning
- Flush console log from kernel_power_off()
- rcutorture: Allow a negative value for nfakewriters
- rcu: Update TREE05.boot to test normal synchronize_rcu()
- rcu: Use _full() API to debug synchronize_rcu()
Make RCU handle PREEMPT_LAZY better:
- Fix header guard for rcu_all_qs()
- rcu: Rename PREEMPT_AUTO to PREEMPT_LAZY
- Update __cond_resched comment about RCU quiescent states
- Handle unstable rdp in rcu_read_unlock_strict()
- Handle quiescent states for PREEMPT_RCU=n, PREEMPT_COUNT=y
- osnoise: Provide quiescent states
- Adjust rcutorture with possible PREEMPT_RCU=n && PREEMPT_COUNT=y
combination
- Limit PREEMPT_RCU configurations
- Make rcutorture senario TREE07 and senario TREE10 use
PREEMPT_LAZY=y"
* tag 'rcu-next-v6.15' of git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux: (59 commits)
rcutorture: Make scenario TREE07 build CONFIG_PREEMPT_LAZY=y
rcutorture: Make scenario TREE10 build CONFIG_PREEMPT_LAZY=y
rcu: limit PREEMPT_RCU configurations
rcutorture: Update ->extendables check for lazy preemption
rcutorture: Update rcutorture_one_extend_check() for lazy preemption
osnoise: provide quiescent states
rcu: Use _full() API to debug synchronize_rcu()
rcu: Update TREE05.boot to test normal synchronize_rcu()
rcutorture: Allow a negative value for nfakewriters
Flush console log from kernel_power_off()
context_tracking: Make RCU watch ct_kernel_exit_state() warning
rcu/nocb: Print segment lengths in show_rcu_nocb_gp_state()
rcu-tasks: Move RCU Tasks self-tests to core_initcall()
rcu: Fix get_state_synchronize_rcu_full() GP-start detection
torture: Make SRCU lockdep testing use srcu_read_lock_nmisafe()
srcu: Add FORCE_NEED_SRCU_NMI_SAFE Kconfig for testing
rcutorture: Complain when invalid SRCU reader_flavor is specified
rcutorture: Move RCU_TORTURE_TEST_{CHK_RDR_STATE,LOG_CPU} to bool
rcutorture: Make cur_ops->format_gp_seqs take buffer length
rcutorture: Add ftrace-compatible timestamp to GP# failure/close-call output
...
This commit is contained in:
@@ -329,10 +329,7 @@ Answer:
|
||||
was first added back in 2005. This is because on_each_cpu()
|
||||
disables preemption, which acted as an RCU read-side critical
|
||||
section, thus preventing CPU 0's grace period from completing
|
||||
until on_each_cpu() had dealt with all of the CPUs. However,
|
||||
with the advent of preemptible RCU, rcu_barrier() no longer
|
||||
waited on nonpreemptible regions of code in preemptible kernels,
|
||||
that being the job of the new rcu_barrier_sched() function.
|
||||
until on_each_cpu() had dealt with all of the CPUs.
|
||||
|
||||
However, with the RCU flavor consolidation around v4.20, this
|
||||
possibility was once again ruled out, because the consolidated
|
||||
|
||||
@@ -96,6 +96,13 @@ warnings:
|
||||
the ``rcu_.*timer wakeup didn't happen for`` console-log message,
|
||||
which will include additional debugging information.
|
||||
|
||||
- A timer issue causes time to appear to jump forward, so that RCU
|
||||
believes that the RCU CPU stall-warning timeout has been exceeded
|
||||
when in fact much less time has passed. This could be due to
|
||||
timer hardware bugs, timer driver bugs, or even corruption of
|
||||
the "jiffies" global variable. These sorts of timer hardware
|
||||
and driver bugs are not uncommon when testing new hardware.
|
||||
|
||||
- A low-level kernel issue that either fails to invoke one of the
|
||||
variants of rcu_eqs_enter(true), rcu_eqs_exit(true), ct_idle_enter(),
|
||||
ct_idle_exit(), ct_irq_enter(), or ct_irq_exit() on the one
|
||||
|
||||
@@ -5760,6 +5760,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.
|
||||
|
||||
|
||||
@@ -138,12 +138,22 @@ get_cpu/put_cpu sequence requires. No processor number is
|
||||
available. Instead, the offset of the local per cpu area is simply
|
||||
added to the per cpu offset.
|
||||
|
||||
Note that this operation is usually used in a code segment when
|
||||
preemption has been disabled. The pointer is then used to
|
||||
access local per cpu data in a critical section. When preemption
|
||||
is re-enabled this pointer is usually no longer useful since it may
|
||||
no longer point to per cpu data of the current processor.
|
||||
Note that this operation can only be used in code segments where
|
||||
smp_processor_id() may be used, for example, where preemption has been
|
||||
disabled. The pointer is then used to access local per cpu data in a
|
||||
critical section. When preemption is re-enabled this pointer is usually
|
||||
no longer useful since it may no longer point to per cpu data of the
|
||||
current processor.
|
||||
|
||||
The special cases where it makes sense to obtain a per-CPU pointer in
|
||||
preemptible code are addressed by raw_cpu_ptr(), but such use cases need
|
||||
to handle cases where two different CPUs are accessing the same per cpu
|
||||
variable, which might well be that of a third CPU. These use cases are
|
||||
typically performance optimizations. For example, SRCU implements a pair
|
||||
of counters as a pair of per-CPU variables, and rcu_read_lock_nmisafe()
|
||||
uses raw_cpu_ptr() to get a pointer to some CPU's counter, and uses
|
||||
atomic_inc_long() to handle migration between the raw_cpu_ptr() and
|
||||
the atomic_inc_long().
|
||||
|
||||
Per cpu variables and offsets
|
||||
-----------------------------
|
||||
|
||||
@@ -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);
|
||||
@@ -806,11 +800,9 @@ do { \
|
||||
* sections, invocation of the corresponding RCU callback is deferred
|
||||
* until after the all the other CPUs exit their critical sections.
|
||||
*
|
||||
* In v5.0 and later kernels, synchronize_rcu() and call_rcu() also
|
||||
* wait for regions of code with preemption disabled, including regions of
|
||||
* code with interrupts or softirqs disabled. In pre-v5.0 kernels, which
|
||||
* define synchronize_sched(), only code enclosed within rcu_read_lock()
|
||||
* and rcu_read_unlock() are guaranteed to be waited for.
|
||||
* Both synchronize_rcu() and call_rcu() also wait for regions of code
|
||||
* with preemption disabled, including regions of code with interrupts or
|
||||
* softirqs disabled.
|
||||
*
|
||||
* Note, however, that RCU callbacks are permitted to run concurrently
|
||||
* with new RCU read-side critical sections. One way that this can happen
|
||||
@@ -865,11 +857,10 @@ static __always_inline void rcu_read_lock(void)
|
||||
* rcu_read_unlock() - marks the end of an RCU read-side critical section.
|
||||
*
|
||||
* In almost all situations, rcu_read_unlock() is immune from deadlock.
|
||||
* In recent kernels that have consolidated synchronize_sched() and
|
||||
* synchronize_rcu_bh() into synchronize_rcu(), this deadlock immunity
|
||||
* also extends to the scheduler's runqueue and priority-inheritance
|
||||
* spinlocks, courtesy of the quiescent-state deferral that is carried
|
||||
* out when rcu_read_unlock() is invoked with interrupts disabled.
|
||||
* This deadlock immunity also extends to the scheduler's runqueue
|
||||
* and priority-inheritance spinlocks, courtesy of the quiescent-state
|
||||
* deferral that is carried out when rcu_read_unlock() is invoked with
|
||||
* interrupts disabled.
|
||||
*
|
||||
* See rcu_read_lock() for more information.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -100,7 +100,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
|
||||
|
||||
|
||||
+88
-14
@@ -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,
|
||||
|
||||
+86
-12
@@ -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);
|
||||
}
|
||||
|
||||
+26
-9
@@ -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
|
||||
@@ -323,21 +334,27 @@ config RCU_LAZY
|
||||
depends on RCU_NOCB_CPU
|
||||
default n
|
||||
help
|
||||
To save power, batch RCU callbacks and flush after delay, memory
|
||||
pressure, or callback list growing too big.
|
||||
To save power, batch RCU callbacks and delay starting the
|
||||
corresponding grace period for multiple seconds. The grace
|
||||
period will be started after this delay, in case of memory
|
||||
pressure, or if the corresponding CPU's callback list grows
|
||||
too large.
|
||||
|
||||
Requires rcu_nocbs=all to be set.
|
||||
These delays happen only on rcu_nocbs CPUs, that is, CPUs
|
||||
whose callbacks have been offloaded.
|
||||
|
||||
Use rcutree.enable_rcu_lazy=0 to turn it off at boot time.
|
||||
Use the rcutree.enable_rcu_lazy=0 kernel-boot parameter to
|
||||
globally disable these delays.
|
||||
|
||||
config RCU_LAZY_DEFAULT_OFF
|
||||
bool "Turn RCU lazy invocation off by default"
|
||||
depends on RCU_LAZY
|
||||
default n
|
||||
help
|
||||
Allows building the kernel with CONFIG_RCU_LAZY=y yet keep it default
|
||||
off. Boot time param rcutree.enable_rcu_lazy=1 can be used to switch
|
||||
it back on.
|
||||
Build the kernel with CONFIG_RCU_LAZY=y, but cause the kernel
|
||||
to boot with these energy-efficiency delays disabled. Use the
|
||||
rcutree.enable_rcu_lazy=0 kernel-boot parameter to override
|
||||
the this option at boot time, thus re-enabling these delays.
|
||||
|
||||
config RCU_DOUBLE_CHECK_CB_TIME
|
||||
bool "RCU callback-batch backup time check"
|
||||
|
||||
@@ -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
|
||||
|
||||
+9
-4
@@ -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
|
||||
|
||||
+110
-14
@@ -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))
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user