Commit f47491d7 authored by Sean Christopherson's avatar Sean Christopherson Committed by Paolo Bonzini

KVM: x86/mmu: Handle "default" period when selectively waking kthread

Account for the '0' being a default, "let KVM choose" period, when
determining whether or not the recovery worker needs to be awakened in
response to userspace reducing the period.  Failure to do so results in
the worker not being awakened properly, e.g. when changing the period
from '0' to any small-ish value.

Fixes: 4dfe4f40 ("kvm: x86: mmu: Make NX huge page recovery period configurable")
Cc: stable@vger.kernel.org
Cc: Junaid Shahid <junaids@google.com>
Signed-off-by: default avatarSean Christopherson <seanjc@google.com>
Message-Id: <20211120015706.3830341-1-seanjc@google.com>
Signed-off-by: default avatarPaolo Bonzini <pbonzini@redhat.com>
parent 28f091bc
...@@ -6171,23 +6171,46 @@ void kvm_mmu_module_exit(void) ...@@ -6171,23 +6171,46 @@ void kvm_mmu_module_exit(void)
mmu_audit_disable(); mmu_audit_disable();
} }
/*
* Calculate the effective recovery period, accounting for '0' meaning "let KVM
* select a halving time of 1 hour". Returns true if recovery is enabled.
*/
static bool calc_nx_huge_pages_recovery_period(uint *period)
{
/*
* Use READ_ONCE to get the params, this may be called outside of the
* param setters, e.g. by the kthread to compute its next timeout.
*/
bool enabled = READ_ONCE(nx_huge_pages);
uint ratio = READ_ONCE(nx_huge_pages_recovery_ratio);
if (!enabled || !ratio)
return false;
*period = READ_ONCE(nx_huge_pages_recovery_period_ms);
if (!*period) {
/* Make sure the period is not less than one second. */
ratio = min(ratio, 3600u);
*period = 60 * 60 * 1000 / ratio;
}
return true;
}
static int set_nx_huge_pages_recovery_param(const char *val, const struct kernel_param *kp) static int set_nx_huge_pages_recovery_param(const char *val, const struct kernel_param *kp)
{ {
bool was_recovery_enabled, is_recovery_enabled; bool was_recovery_enabled, is_recovery_enabled;
uint old_period, new_period; uint old_period, new_period;
int err; int err;
was_recovery_enabled = nx_huge_pages_recovery_ratio; was_recovery_enabled = calc_nx_huge_pages_recovery_period(&old_period);
old_period = nx_huge_pages_recovery_period_ms;
err = param_set_uint(val, kp); err = param_set_uint(val, kp);
if (err) if (err)
return err; return err;
is_recovery_enabled = nx_huge_pages_recovery_ratio; is_recovery_enabled = calc_nx_huge_pages_recovery_period(&new_period);
new_period = nx_huge_pages_recovery_period_ms;
if (READ_ONCE(nx_huge_pages) && is_recovery_enabled && if (is_recovery_enabled &&
(!was_recovery_enabled || old_period > new_period)) { (!was_recovery_enabled || old_period > new_period)) {
struct kvm *kvm; struct kvm *kvm;
...@@ -6251,17 +6274,12 @@ static void kvm_recover_nx_lpages(struct kvm *kvm) ...@@ -6251,17 +6274,12 @@ static void kvm_recover_nx_lpages(struct kvm *kvm)
static long get_nx_lpage_recovery_timeout(u64 start_time) static long get_nx_lpage_recovery_timeout(u64 start_time)
{ {
uint ratio = READ_ONCE(nx_huge_pages_recovery_ratio); bool enabled;
uint period = READ_ONCE(nx_huge_pages_recovery_period_ms); uint period;
if (!period && ratio) { enabled = calc_nx_huge_pages_recovery_period(&period);
/* Make sure the period is not less than one second. */
ratio = min(ratio, 3600u);
period = 60 * 60 * 1000 / ratio;
}
return READ_ONCE(nx_huge_pages) && ratio return enabled ? start_time + msecs_to_jiffies(period) - get_jiffies_64()
? start_time + msecs_to_jiffies(period) - get_jiffies_64()
: MAX_SCHEDULE_TIMEOUT; : MAX_SCHEDULE_TIMEOUT;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment