You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
rcu: add call_rcu_sched()
Fourth cut of patch to provide the call_rcu_sched(). This is again to synchronize_sched() as call_rcu() is to synchronize_rcu(). Should be fine for experimental and -rt use, but not ready for inclusion. With some luck, I will be able to tell Andrew to come out of hiding on the next round. Passes multi-day rcutorture sessions with concurrent CPU hotplugging. Fixes since the first version include a bug that could result in indefinite blocking (spotted by Gautham Shenoy), better resiliency against CPU-hotplug operations, and other minor fixes. Fixes since the second version include reworking grace-period detection to avoid deadlocks that could happen when running concurrently with CPU hotplug, adding Mathieu's fix to avoid the softlockup messages, as well as Mathieu's fix to allow use earlier in boot. Fixes since the third version include a wrong-CPU bug spotted by Andrew, getting rid of the obsolete synchronize_kernel API that somehow snuck back in, merging spin_unlock() and local_irq_restore() in a few places, commenting the code that checks for quiescent states based on interrupting from user-mode execution or the idle loop, removing some inline attributes, and some code-style changes. Known/suspected shortcomings: o I still do not entirely trust the sleep/wakeup logic. Next step will be to use a private snapshot of the CPU online mask in rcu_sched_grace_period() -- if the CPU wasn't there at the start of the grace period, we don't need to hear from it. And the bit about accounting for changes in online CPUs inside of rcu_sched_grace_period() is ugly anyway. o It might be good for rcu_sched_grace_period() to invoke resched_cpu() when a given CPU wasn't responding quickly, but resched_cpu() is declared static... This patch also fixes a long-standing bug in the earlier preemptable-RCU implementation of synchronize_rcu() that could result in loss of concurrent external changes to a task's CPU affinity mask. I still cannot remember who reported this... Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
committed by
Ingo Molnar
parent
8b09dee67f
commit
4446a36ff8
@@ -151,7 +151,10 @@ extern struct lockdep_map rcu_lock_map;
|
||||
|
||||
#define __synchronize_sched() synchronize_rcu()
|
||||
|
||||
#define call_rcu_sched(head, func) call_rcu(head, func)
|
||||
|
||||
extern void __rcu_init(void);
|
||||
#define rcu_init_sched() do { } while (0)
|
||||
extern void rcu_check_callbacks(int cpu, int user);
|
||||
extern void rcu_restart_cpu(int cpu);
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/lockdep.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
/**
|
||||
* struct rcu_head - callback structure for use with RCU
|
||||
@@ -168,6 +169,27 @@ struct rcu_head {
|
||||
(p) = (v); \
|
||||
})
|
||||
|
||||
/* Infrastructure to implement the synchronize_() primitives. */
|
||||
|
||||
struct rcu_synchronize {
|
||||
struct rcu_head head;
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
extern void wakeme_after_rcu(struct rcu_head *head);
|
||||
|
||||
#define synchronize_rcu_xxx(name, func) \
|
||||
void name(void) \
|
||||
{ \
|
||||
struct rcu_synchronize rcu; \
|
||||
\
|
||||
init_completion(&rcu.completion); \
|
||||
/* Will wake me after RCU finished. */ \
|
||||
func(&rcu.head, wakeme_after_rcu); \
|
||||
/* Wait for it. */ \
|
||||
wait_for_completion(&rcu.completion); \
|
||||
}
|
||||
|
||||
/**
|
||||
* synchronize_sched - block until all CPUs have exited any non-preemptive
|
||||
* kernel code sequences.
|
||||
|
||||
@@ -40,10 +40,39 @@
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/seqlock.h>
|
||||
|
||||
#define rcu_qsctr_inc(cpu)
|
||||
struct rcu_dyntick_sched {
|
||||
int dynticks;
|
||||
int dynticks_snap;
|
||||
int sched_qs;
|
||||
int sched_qs_snap;
|
||||
int sched_dynticks_snap;
|
||||
};
|
||||
|
||||
DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
|
||||
|
||||
static inline void rcu_qsctr_inc(int cpu)
|
||||
{
|
||||
struct rcu_dyntick_sched *rdssp = &per_cpu(rcu_dyntick_sched, cpu);
|
||||
|
||||
rdssp->sched_qs++;
|
||||
}
|
||||
#define rcu_bh_qsctr_inc(cpu)
|
||||
#define call_rcu_bh(head, rcu) call_rcu(head, rcu)
|
||||
|
||||
/**
|
||||
* call_rcu_sched - Queue RCU callback for invocation after sched grace period.
|
||||
* @head: structure to be used for queueing the RCU updates.
|
||||
* @func: actual update function to be invoked after the grace period
|
||||
*
|
||||
* The update function will be invoked some time after a full
|
||||
* synchronize_sched()-style grace period elapses, in other words after
|
||||
* all currently executing preempt-disabled sections of code (including
|
||||
* hardirq handlers, NMI handlers, and local_irq_save() blocks) have
|
||||
* completed.
|
||||
*/
|
||||
extern void call_rcu_sched(struct rcu_head *head,
|
||||
void (*func)(struct rcu_head *head));
|
||||
|
||||
extern void __rcu_read_lock(void) __acquires(RCU);
|
||||
extern void __rcu_read_unlock(void) __releases(RCU);
|
||||
extern int rcu_pending(int cpu);
|
||||
@@ -55,6 +84,7 @@ extern int rcu_needs_cpu(int cpu);
|
||||
extern void __synchronize_sched(void);
|
||||
|
||||
extern void __rcu_init(void);
|
||||
extern void rcu_init_sched(void);
|
||||
extern void rcu_check_callbacks(int cpu, int user);
|
||||
extern void rcu_restart_cpu(int cpu);
|
||||
extern long rcu_batches_completed(void);
|
||||
@@ -81,20 +111,20 @@ extern struct rcupreempt_trace *rcupreempt_trace_cpu(int cpu);
|
||||
struct softirq_action;
|
||||
|
||||
#ifdef CONFIG_NO_HZ
|
||||
DECLARE_PER_CPU(long, dynticks_progress_counter);
|
||||
DECLARE_PER_CPU(struct rcu_dyntick_sched, rcu_dyntick_sched);
|
||||
|
||||
static inline void rcu_enter_nohz(void)
|
||||
{
|
||||
smp_mb(); /* CPUs seeing ++ must see prior RCU read-side crit sects */
|
||||
__get_cpu_var(dynticks_progress_counter)++;
|
||||
WARN_ON(__get_cpu_var(dynticks_progress_counter) & 0x1);
|
||||
__get_cpu_var(rcu_dyntick_sched).dynticks++;
|
||||
WARN_ON(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1);
|
||||
}
|
||||
|
||||
static inline void rcu_exit_nohz(void)
|
||||
{
|
||||
__get_cpu_var(dynticks_progress_counter)++;
|
||||
smp_mb(); /* CPUs seeing ++ must see later RCU read-side crit sects */
|
||||
WARN_ON(!(__get_cpu_var(dynticks_progress_counter) & 0x1));
|
||||
__get_cpu_var(rcu_dyntick_sched).dynticks++;
|
||||
WARN_ON(!(__get_cpu_var(rcu_dyntick_sched).dynticks & 0x1));
|
||||
}
|
||||
|
||||
#else /* CONFIG_NO_HZ */
|
||||
|
||||
@@ -758,6 +758,7 @@ static void __init do_initcalls(void)
|
||||
*/
|
||||
static void __init do_basic_setup(void)
|
||||
{
|
||||
rcu_init_sched(); /* needed by module_init stage. */
|
||||
/* drivers will send hotplug events */
|
||||
init_workqueues();
|
||||
usermodehelper_init();
|
||||
|
||||
+2
-18
@@ -39,18 +39,12 @@
|
||||
#include <linux/sched.h>
|
||||
#include <asm/atomic.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
struct rcu_synchronize {
|
||||
struct rcu_head head;
|
||||
struct completion completion;
|
||||
};
|
||||
|
||||
static DEFINE_PER_CPU(struct rcu_head, rcu_barrier_head) = {NULL};
|
||||
static atomic_t rcu_barrier_cpu_count;
|
||||
static DEFINE_MUTEX(rcu_barrier_mutex);
|
||||
@@ -60,7 +54,7 @@ static struct completion rcu_barrier_completion;
|
||||
* Awaken the corresponding synchronize_rcu() instance now that a
|
||||
* grace period has elapsed.
|
||||
*/
|
||||
static void wakeme_after_rcu(struct rcu_head *head)
|
||||
void wakeme_after_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct rcu_synchronize *rcu;
|
||||
|
||||
@@ -77,17 +71,7 @@ static void wakeme_after_rcu(struct rcu_head *head)
|
||||
* sections are delimited by rcu_read_lock() and rcu_read_unlock(),
|
||||
* and may be nested.
|
||||
*/
|
||||
void synchronize_rcu(void)
|
||||
{
|
||||
struct rcu_synchronize rcu;
|
||||
|
||||
init_completion(&rcu.completion);
|
||||
/* Will wake me after RCU finished */
|
||||
call_rcu(&rcu.head, wakeme_after_rcu);
|
||||
|
||||
/* Wait for it */
|
||||
wait_for_completion(&rcu.completion);
|
||||
}
|
||||
synchronize_rcu_xxx(synchronize_rcu, call_rcu)
|
||||
EXPORT_SYMBOL_GPL(synchronize_rcu);
|
||||
|
||||
static void rcu_barrier_callback(struct rcu_head *notused)
|
||||
|
||||
+373
-47
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user