Skip to content

Commit 931656b

Browse files
keithbuschbonzini
authored andcommitted
kvm: defer huge page recovery vhost task to later
Some libraries want to ensure they are single threaded before forking, so making the kernel's kvm huge page recovery process a vhost task of the user process breaks those. The minijail library used by crosvm is one such affected application. Defer the task to after the first VM_RUN call, which occurs after the parent process has forked all its jailed processes. This needs to happen only once for the kvm instance, so introduce some general-purpose infrastructure for that, too. It's similar in concept to pthread_once; except it is actually usable, because the callback takes a parameter. Cc: Sean Christopherson <[email protected]> Cc: Paolo Bonzini <[email protected]> Tested-by: Alyssa Ross <[email protected]> Signed-off-by: Keith Busch <[email protected]> Message-ID: <[email protected]> [Move call_once API to include/linux. - Paolo] Cc: [email protected] Fixes: d96c77b ("KVM: x86: switch hugepage recovery thread to vhost_task") Signed-off-by: Paolo Bonzini <[email protected]>
1 parent 86eb1ae commit 931656b

File tree

4 files changed

+66
-6
lines changed

4 files changed

+66
-6
lines changed

arch/x86/include/asm/kvm_host.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/hyperv.h>
2828
#include <linux/kfifo.h>
2929
#include <linux/sched/vhost_task.h>
30+
#include <linux/call_once.h>
3031

3132
#include <asm/apic.h>
3233
#include <asm/pvclock-abi.h>
@@ -1466,6 +1467,7 @@ struct kvm_arch {
14661467
struct kvm_x86_pmu_event_filter __rcu *pmu_event_filter;
14671468
struct vhost_task *nx_huge_page_recovery_thread;
14681469
u64 nx_huge_page_last;
1470+
struct once nx_once;
14691471

14701472
#ifdef CONFIG_X86_64
14711473
/* The number of TDP MMU pages across all roots. */

arch/x86/kvm/mmu/mmu.c

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7447,20 +7447,28 @@ static bool kvm_nx_huge_page_recovery_worker(void *data)
74477447
return true;
74487448
}
74497449

7450-
int kvm_mmu_post_init_vm(struct kvm *kvm)
7450+
static void kvm_mmu_start_lpage_recovery(struct once *once)
74517451
{
7452-
if (nx_hugepage_mitigation_hard_disabled)
7453-
return 0;
7452+
struct kvm_arch *ka = container_of(once, struct kvm_arch, nx_once);
7453+
struct kvm *kvm = container_of(ka, struct kvm, arch);
74547454

74557455
kvm->arch.nx_huge_page_last = get_jiffies_64();
74567456
kvm->arch.nx_huge_page_recovery_thread = vhost_task_create(
74577457
kvm_nx_huge_page_recovery_worker, kvm_nx_huge_page_recovery_worker_kill,
74587458
kvm, "kvm-nx-lpage-recovery");
74597459

7460+
if (kvm->arch.nx_huge_page_recovery_thread)
7461+
vhost_task_start(kvm->arch.nx_huge_page_recovery_thread);
7462+
}
7463+
7464+
int kvm_mmu_post_init_vm(struct kvm *kvm)
7465+
{
7466+
if (nx_hugepage_mitigation_hard_disabled)
7467+
return 0;
7468+
7469+
call_once(&kvm->arch.nx_once, kvm_mmu_start_lpage_recovery);
74607470
if (!kvm->arch.nx_huge_page_recovery_thread)
74617471
return -ENOMEM;
7462-
7463-
vhost_task_start(kvm->arch.nx_huge_page_recovery_thread);
74647472
return 0;
74657473
}
74667474

arch/x86/kvm/x86.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11471,6 +11471,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
1147111471
struct kvm_run *kvm_run = vcpu->run;
1147211472
int r;
1147311473

11474+
r = kvm_mmu_post_init_vm(vcpu->kvm);
11475+
if (r)
11476+
return r;
11477+
1147411478
vcpu_load(vcpu);
1147511479
kvm_sigset_activate(vcpu);
1147611480
kvm_run->flags = 0;
@@ -12748,7 +12752,8 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
1274812752

1274912753
int kvm_arch_post_init_vm(struct kvm *kvm)
1275012754
{
12751-
return kvm_mmu_post_init_vm(kvm);
12755+
once_init(&kvm->arch.nx_once);
12756+
return 0;
1275212757
}
1275312758

1275412759
static void kvm_unload_vcpu_mmu(struct kvm_vcpu *vcpu)

include/linux/call_once.h

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#ifndef _LINUX_CALL_ONCE_H
2+
#define _LINUX_CALL_ONCE_H
3+
4+
#include <linux/types.h>
5+
#include <linux/mutex.h>
6+
7+
#define ONCE_NOT_STARTED 0
8+
#define ONCE_RUNNING 1
9+
#define ONCE_COMPLETED 2
10+
11+
struct once {
12+
atomic_t state;
13+
struct mutex lock;
14+
};
15+
16+
static inline void __once_init(struct once *once, const char *name,
17+
struct lock_class_key *key)
18+
{
19+
atomic_set(&once->state, ONCE_NOT_STARTED);
20+
__mutex_init(&once->lock, name, key);
21+
}
22+
23+
#define once_init(once) \
24+
do { \
25+
static struct lock_class_key __key; \
26+
__once_init((once), #once, &__key); \
27+
} while (0)
28+
29+
static inline void call_once(struct once *once, void (*cb)(struct once *))
30+
{
31+
/* Pairs with atomic_set_release() below. */
32+
if (atomic_read_acquire(&once->state) == ONCE_COMPLETED)
33+
return;
34+
35+
guard(mutex)(&once->lock);
36+
WARN_ON(atomic_read(&once->state) == ONCE_RUNNING);
37+
if (atomic_read(&once->state) != ONCE_NOT_STARTED)
38+
return;
39+
40+
atomic_set(&once->state, ONCE_RUNNING);
41+
cb(once);
42+
atomic_set_release(&once->state, ONCE_COMPLETED);
43+
}
44+
45+
#endif /* _LINUX_CALL_ONCE_H */

0 commit comments

Comments
 (0)