Skip to content

Commit

Permalink
entry/kvm: Explicitly flush pending rcuog wakeup before last reschedu…
Browse files Browse the repository at this point in the history
…ling point

Following the idle loop model, cleanly check for pending rcuog wakeup
before the last rescheduling point upon resuming to guest mode. This
way we can avoid to do it from rcu_user_enter() with the last resort
self-IPI hack that enforces rescheduling.

Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Frederic Weisbecker <frederic@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: stable@vger.kernel.org
Link: https://lkml.kernel.org/r/20210131230548.32970-6-frederic@kernel.org
  • Loading branch information
Frederic Weisbecker authored and Ingo Molnar committed Feb 17, 2021
1 parent 47b8ff1 commit 4ae7dc9
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 10 deletions.
1 change: 1 addition & 0 deletions arch/x86/kvm/x86.c
Original file line number Diff line number Diff line change
Expand Up @@ -1782,6 +1782,7 @@ EXPORT_SYMBOL_GPL(kvm_emulate_wrmsr);

bool kvm_vcpu_exit_request(struct kvm_vcpu *vcpu)
{
xfer_to_guest_mode_prepare();
return vcpu->mode == EXITING_GUEST_MODE || kvm_request_pending(vcpu) ||
xfer_to_guest_mode_work_pending();
}
Expand Down
14 changes: 14 additions & 0 deletions include/linux/entry-kvm.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ static inline int arch_xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu,
*/
int xfer_to_guest_mode_handle_work(struct kvm_vcpu *vcpu);

/**
* xfer_to_guest_mode_prepare - Perform last minute preparation work that
* need to be handled while IRQs are disabled
* upon entering to guest.
*
* Has to be invoked with interrupts disabled before the last call
* to xfer_to_guest_mode_work_pending().
*/
static inline void xfer_to_guest_mode_prepare(void)
{
lockdep_assert_irqs_disabled();
rcu_nocb_flush_deferred_wakeup();
}

/**
* __xfer_to_guest_mode_work_pending - Check if work is pending
*
Expand Down
44 changes: 34 additions & 10 deletions kernel/rcu/tree.c
Original file line number Diff line number Diff line change
Expand Up @@ -678,9 +678,10 @@ EXPORT_SYMBOL_GPL(rcu_idle_enter);

#ifdef CONFIG_NO_HZ_FULL

#if !defined(CONFIG_GENERIC_ENTRY) || !defined(CONFIG_KVM_XFER_TO_GUEST_WORK)
/*
* An empty function that will trigger a reschedule on
* IRQ tail once IRQs get re-enabled on userspace resume.
* IRQ tail once IRQs get re-enabled on userspace/guest resume.
*/
static void late_wakeup_func(struct irq_work *work)
{
Expand All @@ -689,6 +690,37 @@ static void late_wakeup_func(struct irq_work *work)
static DEFINE_PER_CPU(struct irq_work, late_wakeup_work) =
IRQ_WORK_INIT(late_wakeup_func);

/*
* If either:
*
* 1) the task is about to enter in guest mode and $ARCH doesn't support KVM generic work
* 2) the task is about to enter in user mode and $ARCH doesn't support generic entry.
*
* In these cases the late RCU wake ups aren't supported in the resched loops and our
* last resort is to fire a local irq_work that will trigger a reschedule once IRQs
* get re-enabled again.
*/
noinstr static void rcu_irq_work_resched(void)
{
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);

if (IS_ENABLED(CONFIG_GENERIC_ENTRY) && !(current->flags & PF_VCPU))
return;

if (IS_ENABLED(CONFIG_KVM_XFER_TO_GUEST_WORK) && (current->flags & PF_VCPU))
return;

instrumentation_begin();
if (do_nocb_deferred_wakeup(rdp) && need_resched()) {
irq_work_queue(this_cpu_ptr(&late_wakeup_work));
}
instrumentation_end();
}

#else
static inline void rcu_irq_work_resched(void) { }
#endif

/**
* rcu_user_enter - inform RCU that we are resuming userspace.
*
Expand All @@ -702,22 +734,14 @@ static DEFINE_PER_CPU(struct irq_work, late_wakeup_work) =
*/
noinstr void rcu_user_enter(void)
{
struct rcu_data *rdp = this_cpu_ptr(&rcu_data);

lockdep_assert_irqs_disabled();

/*
* Other than generic entry implementation, we may be past the last
* rescheduling opportunity in the entry code. Trigger a self IPI
* that will fire and reschedule once we resume in user/guest mode.
*/
instrumentation_begin();
if (!IS_ENABLED(CONFIG_GENERIC_ENTRY) || (current->flags & PF_VCPU)) {
if (do_nocb_deferred_wakeup(rdp) && need_resched())
irq_work_queue(this_cpu_ptr(&late_wakeup_work));
}
instrumentation_end();

rcu_irq_work_resched();
rcu_eqs_enter(true);
}

Expand Down
1 change: 1 addition & 0 deletions kernel/rcu/tree_plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,7 @@ void rcu_nocb_flush_deferred_wakeup(void)
{
do_nocb_deferred_wakeup(this_cpu_ptr(&rcu_data));
}
EXPORT_SYMBOL_GPL(rcu_nocb_flush_deferred_wakeup);

void __init rcu_init_nohz(void)
{
Expand Down

0 comments on commit 4ae7dc9

Please sign in to comment.