summaryrefslogtreecommitdiff
path: root/include/linux/call_once.h
diff options
context:
space:
mode:
authorKeith Busch <kbusch@kernel.org>2025-01-23 07:35:43 -0800
committerPaolo Bonzini <pbonzini@redhat.com>2025-01-24 10:53:56 -0500
commit931656b9e2ff7029aee0b36e17780621948a6ac1 (patch)
tree6f2c1558a03bc4ed547b12bd33e614fd014201ba /include/linux/call_once.h
parent86eb1aef7279ec68fe9b7a44685efc09aa56a8f0 (diff)
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 <seanjc@google.com> Cc: Paolo Bonzini <pbonzini@redhat.com> Tested-by: Alyssa Ross <hi@alyssa.is> Signed-off-by: Keith Busch <kbusch@kernel.org> Message-ID: <20250123153543.2769928-1-kbusch@meta.com> [Move call_once API to include/linux. - Paolo] Cc: stable@vger.kernel.org Fixes: d96c77bd4eeb ("KVM: x86: switch hugepage recovery thread to vhost_task") Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'include/linux/call_once.h')
-rw-r--r--include/linux/call_once.h45
1 files changed, 45 insertions, 0 deletions
diff --git a/include/linux/call_once.h b/include/linux/call_once.h
new file mode 100644
index 000000000000..6261aa0b3fb0
--- /dev/null
+++ b/include/linux/call_once.h
@@ -0,0 +1,45 @@
+#ifndef _LINUX_CALL_ONCE_H
+#define _LINUX_CALL_ONCE_H
+
+#include <linux/types.h>
+#include <linux/mutex.h>
+
+#define ONCE_NOT_STARTED 0
+#define ONCE_RUNNING 1
+#define ONCE_COMPLETED 2
+
+struct once {
+ atomic_t state;
+ struct mutex lock;
+};
+
+static inline void __once_init(struct once *once, const char *name,
+ struct lock_class_key *key)
+{
+ atomic_set(&once->state, ONCE_NOT_STARTED);
+ __mutex_init(&once->lock, name, key);
+}
+
+#define once_init(once) \
+do { \
+ static struct lock_class_key __key; \
+ __once_init((once), #once, &__key); \
+} while (0)
+
+static inline void call_once(struct once *once, void (*cb)(struct once *))
+{
+ /* Pairs with atomic_set_release() below. */
+ if (atomic_read_acquire(&once->state) == ONCE_COMPLETED)
+ return;
+
+ guard(mutex)(&once->lock);
+ WARN_ON(atomic_read(&once->state) == ONCE_RUNNING);
+ if (atomic_read(&once->state) != ONCE_NOT_STARTED)
+ return;
+
+ atomic_set(&once->state, ONCE_RUNNING);
+ cb(once);
+ atomic_set_release(&once->state, ONCE_COMPLETED);
+}
+
+#endif /* _LINUX_CALL_ONCE_H */