|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Xen-changelog] [xen master] RCU: let the RCU idle timer handler run
commit 9e0a3a2a9143c17319660e22dcf4b7a59fa9473e
Author: Dario Faggioli <dario.faggioli@xxxxxxxxxx>
AuthorDate: Mon Oct 9 13:22:07 2017 +0200
Commit: Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Mon Oct 9 13:22:07 2017 +0200
RCU: let the RCU idle timer handler run
If stop_timer() is called between when the RCU
idle timer's interrupt arrives (and TIMER_SOFTIRQ is
raised) and when softirqs are checked and handled, the
timer is deactivated, and the handler never runs.
This happens to the RCU idle timer because stop_timer()
is called on it during the wakeup from idle (e.g., C-states,
on x86) path.
To fix that, we avoid calling stop_timer(), in case we see
that the timer itself is:
- still active,
- expired (i.e., it's expiry time is in the past).
In fact, that indicates (for this particular timer) that
it has fired, and we are just about to handle the TIMER_SOFTIRQ
(which will perform the timer deactivation and run its handler).
Signed-off-by: Dario Faggioli <dario.faggioli@xxxxxxxxxx>
Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
---
xen/common/rcupdate.c | 19 ++++++++++++++++++-
xen/common/timer.c | 14 ++++++++++++++
xen/include/xen/timer.h | 5 +++++
3 files changed, 37 insertions(+), 1 deletion(-)
diff --git a/xen/common/rcupdate.c b/xen/common/rcupdate.c
index 871936f..252e01b 100644
--- a/xen/common/rcupdate.c
+++ b/xen/common/rcupdate.c
@@ -465,7 +465,24 @@ void rcu_idle_timer_stop()
return;
rdp->idle_timer_active = false;
- stop_timer(&rdp->idle_timer);
+
+ /*
+ * In general, as the CPU is becoming active again, we don't need the
+ * idle timer, and so we want to stop it.
+ *
+ * However, in case we are here because idle_timer has (just) fired and
+ * has woken up the CPU, we skip stop_timer() now. In fact, when a CPU
+ * wakes up from idle, this code always runs before do_softirq() has the
+ * chance to check and deal with TIMER_SOFTIRQ. And if we stop the timer
+ * now, the TIMER_SOFTIRQ handler will see it as inactive, and will not
+ * call rcu_idle_timer_handler().
+ *
+ * Therefore, if we see that the timer is expired already, we leave it
+ * alone. The TIMER_SOFTIRQ handler will then run the timer routine, and
+ * deactivate it.
+ */
+ if ( !timer_is_expired(&rdp->idle_timer) )
+ stop_timer(&rdp->idle_timer);
}
static void rcu_idle_timer_handler(void* data)
diff --git a/xen/common/timer.c b/xen/common/timer.c
index d9ff669..376581b 100644
--- a/xen/common/timer.c
+++ b/xen/common/timer.c
@@ -331,6 +331,20 @@ void stop_timer(struct timer *timer)
timer_unlock_irqrestore(timer, flags);
}
+bool timer_expires_before(struct timer *timer, s_time_t t)
+{
+ unsigned long flags;
+ bool ret;
+
+ if ( !timer_lock_irqsave(timer, flags) )
+ return false;
+
+ ret = active_timer(timer) && timer->expires <= t;
+
+ timer_unlock_irqrestore(timer, flags);
+
+ return ret;
+}
void migrate_timer(struct timer *timer, unsigned int new_cpu)
{
diff --git a/xen/include/xen/timer.h b/xen/include/xen/timer.h
index 9531800..4513260 100644
--- a/xen/include/xen/timer.h
+++ b/xen/include/xen/timer.h
@@ -70,6 +70,11 @@ void set_timer(struct timer *timer, s_time_t expires);
*/
void stop_timer(struct timer *timer);
+/* True if a timer is active, and its expiry time is earlier than t. */
+bool timer_expires_before(struct timer *timer, s_time_t t);
+
+#define timer_is_expired(t) timer_expires_before(t, NOW())
+
/* Migrate a timer to a different CPU. The timer may be currently active. */
void migrate_timer(struct timer *timer, unsigned int new_cpu);
--
generated by git-patchbot for /home/xen/git/xen.git#master
_______________________________________________
Xen-changelog mailing list
Xen-changelog@xxxxxxxxxxxxx
https://lists.xenproject.org/xen-changelog
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |