|
@@ -195,12 +195,10 @@ void rcu_note_context_switch(int cpu)
|
|
}
|
|
}
|
|
EXPORT_SYMBOL_GPL(rcu_note_context_switch);
|
|
EXPORT_SYMBOL_GPL(rcu_note_context_switch);
|
|
|
|
|
|
-#ifdef CONFIG_NO_HZ
|
|
|
|
DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {
|
|
DEFINE_PER_CPU(struct rcu_dynticks, rcu_dynticks) = {
|
|
- .dynticks_nesting = 1,
|
|
|
|
|
|
+ .dynticks_nesting = LLONG_MAX / 2,
|
|
.dynticks = ATOMIC_INIT(1),
|
|
.dynticks = ATOMIC_INIT(1),
|
|
};
|
|
};
|
|
-#endif /* #ifdef CONFIG_NO_HZ */
|
|
|
|
|
|
|
|
static int blimit = 10; /* Maximum callbacks per rcu_do_batch. */
|
|
static int blimit = 10; /* Maximum callbacks per rcu_do_batch. */
|
|
static int qhimark = 10000; /* If this many pending, ignore blimit. */
|
|
static int qhimark = 10000; /* If this many pending, ignore blimit. */
|
|
@@ -328,11 +326,11 @@ static int rcu_implicit_offline_qs(struct rcu_data *rdp)
|
|
return 1;
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
- /* If preemptible RCU, no point in sending reschedule IPI. */
|
|
|
|
- if (rdp->preemptible)
|
|
|
|
- return 0;
|
|
|
|
-
|
|
|
|
- /* The CPU is online, so send it a reschedule IPI. */
|
|
|
|
|
|
+ /*
|
|
|
|
+ * The CPU is online, so send it a reschedule IPI. This forces
|
|
|
|
+ * it through the scheduler, and (inefficiently) also handles cases
|
|
|
|
+ * where idle loops fail to inform RCU about the CPU being idle.
|
|
|
|
+ */
|
|
if (rdp->cpu != smp_processor_id())
|
|
if (rdp->cpu != smp_processor_id())
|
|
smp_send_reschedule(rdp->cpu);
|
|
smp_send_reschedule(rdp->cpu);
|
|
else
|
|
else
|
|
@@ -343,51 +341,97 @@ static int rcu_implicit_offline_qs(struct rcu_data *rdp)
|
|
|
|
|
|
#endif /* #ifdef CONFIG_SMP */
|
|
#endif /* #ifdef CONFIG_SMP */
|
|
|
|
|
|
-#ifdef CONFIG_NO_HZ
|
|
|
|
|
|
+/*
|
|
|
|
+ * rcu_idle_enter_common - inform RCU that current CPU is moving towards idle
|
|
|
|
+ *
|
|
|
|
+ * If the new value of the ->dynticks_nesting counter now is zero,
|
|
|
|
+ * we really have entered idle, and must do the appropriate accounting.
|
|
|
|
+ * The caller must have disabled interrupts.
|
|
|
|
+ */
|
|
|
|
+static void rcu_idle_enter_common(struct rcu_dynticks *rdtp)
|
|
|
|
+{
|
|
|
|
+ if (rdtp->dynticks_nesting) {
|
|
|
|
+ trace_rcu_dyntick("--=", rdtp->dynticks_nesting);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ trace_rcu_dyntick("Start", rdtp->dynticks_nesting);
|
|
|
|
+ if (!idle_cpu(smp_processor_id())) {
|
|
|
|
+ WARN_ON_ONCE(1); /* must be idle task! */
|
|
|
|
+ trace_rcu_dyntick("Error on entry: not idle task",
|
|
|
|
+ rdtp->dynticks_nesting);
|
|
|
|
+ ftrace_dump(DUMP_ALL);
|
|
|
|
+ }
|
|
|
|
+ /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */
|
|
|
|
+ smp_mb__before_atomic_inc(); /* See above. */
|
|
|
|
+ atomic_inc(&rdtp->dynticks);
|
|
|
|
+ smp_mb__after_atomic_inc(); /* Force ordering with next sojourn. */
|
|
|
|
+ WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
|
|
|
|
+}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * rcu_enter_nohz - inform RCU that current CPU is entering nohz
|
|
|
|
|
|
+ * rcu_idle_enter - inform RCU that current CPU is entering idle
|
|
*
|
|
*
|
|
- * Enter nohz mode, in other words, -leave- the mode in which RCU
|
|
|
|
|
|
+ * Enter idle mode, in other words, -leave- the mode in which RCU
|
|
* read-side critical sections can occur. (Though RCU read-side
|
|
* read-side critical sections can occur. (Though RCU read-side
|
|
- * critical sections can occur in irq handlers in nohz mode, a possibility
|
|
|
|
- * handled by rcu_irq_enter() and rcu_irq_exit()).
|
|
|
|
|
|
+ * critical sections can occur in irq handlers in idle, a possibility
|
|
|
|
+ * handled by irq_enter() and irq_exit().)
|
|
|
|
+ *
|
|
|
|
+ * We crowbar the ->dynticks_nesting field to zero to allow for
|
|
|
|
+ * the possibility of usermode upcalls having messed up our count
|
|
|
|
+ * of interrupt nesting level during the prior busy period.
|
|
*/
|
|
*/
|
|
-void rcu_enter_nohz(void)
|
|
|
|
|
|
+void rcu_idle_enter(void)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
struct rcu_dynticks *rdtp;
|
|
struct rcu_dynticks *rdtp;
|
|
|
|
|
|
local_irq_save(flags);
|
|
local_irq_save(flags);
|
|
rdtp = &__get_cpu_var(rcu_dynticks);
|
|
rdtp = &__get_cpu_var(rcu_dynticks);
|
|
- if (--rdtp->dynticks_nesting) {
|
|
|
|
- local_irq_restore(flags);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- trace_rcu_dyntick("Start");
|
|
|
|
- /* CPUs seeing atomic_inc() must see prior RCU read-side crit sects */
|
|
|
|
- smp_mb__before_atomic_inc(); /* See above. */
|
|
|
|
- atomic_inc(&rdtp->dynticks);
|
|
|
|
- smp_mb__after_atomic_inc(); /* Force ordering with next sojourn. */
|
|
|
|
- WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
|
|
|
|
|
|
+ rdtp->dynticks_nesting = 0;
|
|
|
|
+ rcu_idle_enter_common(rdtp);
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
}
|
|
}
|
|
|
|
|
|
-/*
|
|
|
|
- * rcu_exit_nohz - inform RCU that current CPU is leaving nohz
|
|
|
|
|
|
+/**
|
|
|
|
+ * rcu_irq_exit - inform RCU that current CPU is exiting irq towards idle
|
|
|
|
+ *
|
|
|
|
+ * Exit from an interrupt handler, which might possibly result in entering
|
|
|
|
+ * idle mode, in other words, leaving the mode in which read-side critical
|
|
|
|
+ * sections can occur.
|
|
*
|
|
*
|
|
- * Exit nohz mode, in other words, -enter- the mode in which RCU
|
|
|
|
- * read-side critical sections normally occur.
|
|
|
|
|
|
+ * This code assumes that the idle loop never does anything that might
|
|
|
|
+ * result in unbalanced calls to irq_enter() and irq_exit(). If your
|
|
|
|
+ * architecture violates this assumption, RCU will give you what you
|
|
|
|
+ * deserve, good and hard. But very infrequently and irreproducibly.
|
|
|
|
+ *
|
|
|
|
+ * Use things like work queues to work around this limitation.
|
|
|
|
+ *
|
|
|
|
+ * You have been warned.
|
|
*/
|
|
*/
|
|
-void rcu_exit_nohz(void)
|
|
|
|
|
|
+void rcu_irq_exit(void)
|
|
{
|
|
{
|
|
unsigned long flags;
|
|
unsigned long flags;
|
|
struct rcu_dynticks *rdtp;
|
|
struct rcu_dynticks *rdtp;
|
|
|
|
|
|
local_irq_save(flags);
|
|
local_irq_save(flags);
|
|
rdtp = &__get_cpu_var(rcu_dynticks);
|
|
rdtp = &__get_cpu_var(rcu_dynticks);
|
|
- if (rdtp->dynticks_nesting++) {
|
|
|
|
- local_irq_restore(flags);
|
|
|
|
|
|
+ rdtp->dynticks_nesting--;
|
|
|
|
+ WARN_ON_ONCE(rdtp->dynticks_nesting < 0);
|
|
|
|
+ rcu_idle_enter_common(rdtp);
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/*
|
|
|
|
+ * rcu_idle_exit_common - inform RCU that current CPU is moving away from idle
|
|
|
|
+ *
|
|
|
|
+ * If the new value of the ->dynticks_nesting counter was previously zero,
|
|
|
|
+ * we really have exited idle, and must do the appropriate accounting.
|
|
|
|
+ * The caller must have disabled interrupts.
|
|
|
|
+ */
|
|
|
|
+static void rcu_idle_exit_common(struct rcu_dynticks *rdtp, long long oldval)
|
|
|
|
+{
|
|
|
|
+ if (oldval) {
|
|
|
|
+ trace_rcu_dyntick("++=", rdtp->dynticks_nesting);
|
|
return;
|
|
return;
|
|
}
|
|
}
|
|
smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */
|
|
smp_mb__before_atomic_inc(); /* Force ordering w/previous sojourn. */
|
|
@@ -395,7 +439,71 @@ void rcu_exit_nohz(void)
|
|
/* CPUs seeing atomic_inc() must see later RCU read-side crit sects */
|
|
/* CPUs seeing atomic_inc() must see later RCU read-side crit sects */
|
|
smp_mb__after_atomic_inc(); /* See above. */
|
|
smp_mb__after_atomic_inc(); /* See above. */
|
|
WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1));
|
|
WARN_ON_ONCE(!(atomic_read(&rdtp->dynticks) & 0x1));
|
|
- trace_rcu_dyntick("End");
|
|
|
|
|
|
+ trace_rcu_dyntick("End", oldval);
|
|
|
|
+ if (!idle_cpu(smp_processor_id())) {
|
|
|
|
+ WARN_ON_ONCE(1); /* must be idle task! */
|
|
|
|
+ trace_rcu_dyntick("Error on exit: not idle task", oldval);
|
|
|
|
+ ftrace_dump(DUMP_ALL);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * rcu_idle_exit - inform RCU that current CPU is leaving idle
|
|
|
|
+ *
|
|
|
|
+ * Exit idle mode, in other words, -enter- the mode in which RCU
|
|
|
|
+ * read-side critical sections can occur.
|
|
|
|
+ *
|
|
|
|
+ * We crowbar the ->dynticks_nesting field to LLONG_MAX/2 to allow for
|
|
|
|
+ * the possibility of usermode upcalls messing up our count
|
|
|
|
+ * of interrupt nesting level during the busy period that is just
|
|
|
|
+ * now starting.
|
|
|
|
+ */
|
|
|
|
+void rcu_idle_exit(void)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ struct rcu_dynticks *rdtp;
|
|
|
|
+ long long oldval;
|
|
|
|
+
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+ rdtp = &__get_cpu_var(rcu_dynticks);
|
|
|
|
+ oldval = rdtp->dynticks_nesting;
|
|
|
|
+ WARN_ON_ONCE(oldval != 0);
|
|
|
|
+ rdtp->dynticks_nesting = LLONG_MAX / 2;
|
|
|
|
+ rcu_idle_exit_common(rdtp, oldval);
|
|
|
|
+ local_irq_restore(flags);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * rcu_irq_enter - inform RCU that current CPU is entering irq away from idle
|
|
|
|
+ *
|
|
|
|
+ * Enter an interrupt handler, which might possibly result in exiting
|
|
|
|
+ * idle mode, in other words, entering the mode in which read-side critical
|
|
|
|
+ * sections can occur.
|
|
|
|
+ *
|
|
|
|
+ * Note that the Linux kernel is fully capable of entering an interrupt
|
|
|
|
+ * handler that it never exits, for example when doing upcalls to
|
|
|
|
+ * user mode! This code assumes that the idle loop never does upcalls to
|
|
|
|
+ * user mode. If your architecture does do upcalls from the idle loop (or
|
|
|
|
+ * does anything else that results in unbalanced calls to the irq_enter()
|
|
|
|
+ * and irq_exit() functions), RCU will give you what you deserve, good
|
|
|
|
+ * and hard. But very infrequently and irreproducibly.
|
|
|
|
+ *
|
|
|
|
+ * Use things like work queues to work around this limitation.
|
|
|
|
+ *
|
|
|
|
+ * You have been warned.
|
|
|
|
+ */
|
|
|
|
+void rcu_irq_enter(void)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ struct rcu_dynticks *rdtp;
|
|
|
|
+ long long oldval;
|
|
|
|
+
|
|
|
|
+ local_irq_save(flags);
|
|
|
|
+ rdtp = &__get_cpu_var(rcu_dynticks);
|
|
|
|
+ oldval = rdtp->dynticks_nesting;
|
|
|
|
+ rdtp->dynticks_nesting++;
|
|
|
|
+ WARN_ON_ONCE(rdtp->dynticks_nesting == 0);
|
|
|
|
+ rcu_idle_exit_common(rdtp, oldval);
|
|
local_irq_restore(flags);
|
|
local_irq_restore(flags);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -442,27 +550,32 @@ void rcu_nmi_exit(void)
|
|
WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
|
|
WARN_ON_ONCE(atomic_read(&rdtp->dynticks) & 0x1);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#ifdef CONFIG_PROVE_RCU
|
|
|
|
+
|
|
/**
|
|
/**
|
|
- * rcu_irq_enter - inform RCU of entry to hard irq context
|
|
|
|
|
|
+ * rcu_is_cpu_idle - see if RCU thinks that the current CPU is idle
|
|
*
|
|
*
|
|
- * If the CPU was idle with dynamic ticks active, this updates the
|
|
|
|
- * rdtp->dynticks to let the RCU handling know that the CPU is active.
|
|
|
|
|
|
+ * If the current CPU is in its idle loop and is neither in an interrupt
|
|
|
|
+ * or NMI handler, return true. The caller must have at least disabled
|
|
|
|
+ * preemption.
|
|
*/
|
|
*/
|
|
-void rcu_irq_enter(void)
|
|
|
|
|
|
+int rcu_is_cpu_idle(void)
|
|
{
|
|
{
|
|
- rcu_exit_nohz();
|
|
|
|
|
|
+ return (atomic_read(&__get_cpu_var(rcu_dynticks).dynticks) & 0x1) == 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+#endif /* #ifdef CONFIG_PROVE_RCU */
|
|
|
|
+
|
|
/**
|
|
/**
|
|
- * rcu_irq_exit - inform RCU of exit from hard irq context
|
|
|
|
|
|
+ * rcu_is_cpu_rrupt_from_idle - see if idle or immediately interrupted from idle
|
|
*
|
|
*
|
|
- * If the CPU was idle with dynamic ticks active, update the rdp->dynticks
|
|
|
|
- * to put let the RCU handling be aware that the CPU is going back to idle
|
|
|
|
- * with no ticks.
|
|
|
|
|
|
+ * If the current CPU is idle or running at a first-level (not nested)
|
|
|
|
+ * interrupt from idle, return true. The caller must have at least
|
|
|
|
+ * disabled preemption.
|
|
*/
|
|
*/
|
|
-void rcu_irq_exit(void)
|
|
|
|
|
|
+int rcu_is_cpu_rrupt_from_idle(void)
|
|
{
|
|
{
|
|
- rcu_enter_nohz();
|
|
|
|
|
|
+ return __get_cpu_var(rcu_dynticks).dynticks_nesting <= 1;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SMP
|
|
#ifdef CONFIG_SMP
|
|
@@ -512,24 +625,6 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
|
|
|
|
|
|
#endif /* #ifdef CONFIG_SMP */
|
|
#endif /* #ifdef CONFIG_SMP */
|
|
|
|
|
|
-#else /* #ifdef CONFIG_NO_HZ */
|
|
|
|
-
|
|
|
|
-#ifdef CONFIG_SMP
|
|
|
|
-
|
|
|
|
-static int dyntick_save_progress_counter(struct rcu_data *rdp)
|
|
|
|
-{
|
|
|
|
- return 0;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-static int rcu_implicit_dynticks_qs(struct rcu_data *rdp)
|
|
|
|
-{
|
|
|
|
- return rcu_implicit_offline_qs(rdp);
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-#endif /* #ifdef CONFIG_SMP */
|
|
|
|
-
|
|
|
|
-#endif /* #else #ifdef CONFIG_NO_HZ */
|
|
|
|
-
|
|
|
|
int rcu_cpu_stall_suppress __read_mostly;
|
|
int rcu_cpu_stall_suppress __read_mostly;
|
|
|
|
|
|
static void record_gp_stall_check_time(struct rcu_state *rsp)
|
|
static void record_gp_stall_check_time(struct rcu_state *rsp)
|
|
@@ -1334,16 +1429,14 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)
|
|
* (user mode or idle loop for rcu, non-softirq execution for rcu_bh).
|
|
* (user mode or idle loop for rcu, non-softirq execution for rcu_bh).
|
|
* Also schedule RCU core processing.
|
|
* Also schedule RCU core processing.
|
|
*
|
|
*
|
|
- * This function must be called with hardirqs disabled. It is normally
|
|
|
|
|
|
+ * This function must be called from hardirq context. It is normally
|
|
* invoked from the scheduling-clock interrupt. If rcu_pending returns
|
|
* invoked from the scheduling-clock interrupt. If rcu_pending returns
|
|
* false, there is no point in invoking rcu_check_callbacks().
|
|
* false, there is no point in invoking rcu_check_callbacks().
|
|
*/
|
|
*/
|
|
void rcu_check_callbacks(int cpu, int user)
|
|
void rcu_check_callbacks(int cpu, int user)
|
|
{
|
|
{
|
|
trace_rcu_utilization("Start scheduler-tick");
|
|
trace_rcu_utilization("Start scheduler-tick");
|
|
- if (user ||
|
|
|
|
- (idle_cpu(cpu) && rcu_scheduler_active &&
|
|
|
|
- !in_softirq() && hardirq_count() <= (1 << HARDIRQ_SHIFT))) {
|
|
|
|
|
|
+ if (user || rcu_is_cpu_rrupt_from_idle()) {
|
|
|
|
|
|
/*
|
|
/*
|
|
* Get here if this CPU took its interrupt from user
|
|
* Get here if this CPU took its interrupt from user
|
|
@@ -1913,9 +2006,9 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp)
|
|
for (i = 0; i < RCU_NEXT_SIZE; i++)
|
|
for (i = 0; i < RCU_NEXT_SIZE; i++)
|
|
rdp->nxttail[i] = &rdp->nxtlist;
|
|
rdp->nxttail[i] = &rdp->nxtlist;
|
|
rdp->qlen = 0;
|
|
rdp->qlen = 0;
|
|
-#ifdef CONFIG_NO_HZ
|
|
|
|
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
|
|
rdp->dynticks = &per_cpu(rcu_dynticks, cpu);
|
|
-#endif /* #ifdef CONFIG_NO_HZ */
|
|
|
|
|
|
+ WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != LLONG_MAX / 2);
|
|
|
|
+ WARN_ON_ONCE(atomic_read(&rdp->dynticks->dynticks) != 1);
|
|
rdp->cpu = cpu;
|
|
rdp->cpu = cpu;
|
|
rdp->rsp = rsp;
|
|
rdp->rsp = rsp;
|
|
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
|
raw_spin_unlock_irqrestore(&rnp->lock, flags);
|
|
@@ -1942,6 +2035,8 @@ rcu_init_percpu_data(int cpu, struct rcu_state *rsp, int preemptible)
|
|
rdp->qlen_last_fqs_check = 0;
|
|
rdp->qlen_last_fqs_check = 0;
|
|
rdp->n_force_qs_snap = rsp->n_force_qs;
|
|
rdp->n_force_qs_snap = rsp->n_force_qs;
|
|
rdp->blimit = blimit;
|
|
rdp->blimit = blimit;
|
|
|
|
+ WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != LLONG_MAX / 2);
|
|
|
|
+ WARN_ON_ONCE((atomic_read(&rdp->dynticks->dynticks) & 0x1) != 1);
|
|
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
|
raw_spin_unlock(&rnp->lock); /* irqs remain disabled. */
|
|
|
|
|
|
/*
|
|
/*
|