summaryrefslogtreecommitdiffstats
path: root/net/devlink/sh_dev.c
diff options
context:
space:
mode:
authorSean Christopherson <seanjc@google.com>2026-09-23 09:37:21 -0700
committerPaolo Bonzini <pbonzini@redhat.com>2026-09-26 00:39:55 -0400
commit93de2a6a4b91b72607136dd656edf03fb399d27f (patch)
tree9220c76eb23cb39a146fac9692ee69afa4aa3135 /net/devlink/sh_dev.c
downloadlinux-stable-93de2a6a4b91b72607136dd656edf03fb399d27f.tar.gz
linux-stable-93de2a6a4b91b72607136dd656edf03fb399d27f.zip
KVM: SEV: Do cache maintenance on the source VM during intra-host migrationgrafted
Manually perform cache maintenance on the source VM during intra-host migration to ensure no stale data is left in CPU caches after the VM is destroyed. Because the source VM is "converted" to a non-SEV VM, KVM's memory reclaim flows won't trigger cache maintenance, e.g. when all guest memory is reclaimed in response to detaching from the mmu_notifier. Note, relying on the destination VM to do cache maintenance isn't an option as KVM doesn't require identical guest memory configurations, i.e. the source VM may have access to memory that the destination VM does not. Enforcing equivalent memory configurations is infeasible, as it would require a *deep* comparison of memslots, e.g. to verify that not only are the memslot identical, but what the memslots point at is also identical. Fixes: b56639318bb2 ("KVM: SEV: Add support for SEV intra host migration") Cc: stable@vger.kernel.org Reported-by: Stefan Teodorescu <fane@google.com> Signed-off-by: Sean Christopherson <seanjc@google.com> Message-ID: <20260923163721.1584779-3-seanjc@google.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'net/devlink/sh_dev.c')
-rw-r--r--net/devlink/sh_dev.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/net/devlink/sh_dev.c b/net/devlink/sh_dev.c
new file mode 100644
index 000000000..85acce97e
--- /dev/null
+++ b/net/devlink/sh_dev.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/* Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */
+
+#include <net/devlink.h>
+
+#include "devl_internal.h"
+
+static LIST_HEAD(shd_list);
+static DEFINE_MUTEX(shd_mutex); /* Protects shd_list and shd->list */
+
+/* This structure represents a shared devlink instance,
+ * there is one created per identifier (e.g., serial number).
+ */
+struct devlink_shd {
+ struct list_head list; /* Node in shd list */
+ const char *id; /* Identifier string (e.g., serial number) */
+ refcount_t refcount; /* Reference count */
+ size_t priv_size; /* Size of driver private data */
+ char priv[] __aligned(NETDEV_ALIGN) __counted_by(priv_size);
+};
+
+static struct devlink_shd *devlink_shd_lookup(const char *id)
+{
+ struct devlink_shd *shd;
+
+ list_for_each_entry(shd, &shd_list, list) {
+ if (!strcmp(shd->id, id))
+ return shd;
+ }
+
+ return NULL;
+}
+
+static struct devlink_shd *devlink_shd_create(const char *id,
+ const struct devlink_ops *ops,
+ size_t priv_size,
+ const struct device_driver *driver)
+{
+ struct devlink_shd *shd;
+ struct devlink *devlink;
+
+ devlink = __devlink_alloc(ops, sizeof(struct devlink_shd) + priv_size,
+ &init_net, NULL, driver);
+ if (!devlink)
+ return NULL;
+ shd = devlink_priv(devlink);
+
+ shd->id = kstrdup(id, GFP_KERNEL);
+ if (!shd->id)
+ goto err_devlink_free;
+ shd->priv_size = priv_size;
+ refcount_set(&shd->refcount, 1);
+
+ devl_lock(devlink);
+ devl_register(devlink);
+ devl_unlock(devlink);
+
+ list_add_tail(&shd->list, &shd_list);
+
+ return shd;
+
+err_devlink_free:
+ devlink_free(devlink);
+ return NULL;
+}
+
+static void devlink_shd_destroy(struct devlink_shd *shd)
+{
+ struct devlink *devlink = priv_to_devlink(shd);
+
+ list_del(&shd->list);
+ devl_lock(devlink);
+ devl_unregister(devlink);
+ devl_unlock(devlink);
+ kfree(shd->id);
+ devlink_free(devlink);
+}
+
+/**
+ * devlink_shd_get - Get or create a shared devlink instance
+ * @id: Identifier string (e.g., serial number) for the shared instance
+ * @ops: Devlink operations structure
+ * @priv_size: Size of private data structure
+ * @driver: Driver associated with the shared devlink instance
+ *
+ * Get an existing shared devlink instance identified by @id, or create
+ * a new one if it doesn't exist. Return the devlink instance with a
+ * reference held. The caller must call devlink_shd_put() when done.
+ *
+ * All callers sharing the same @id must pass identical @ops, @priv_size
+ * and @driver. A mismatch triggers a warning and returns NULL.
+ *
+ * Return: Pointer to the shared devlink instance on success,
+ * NULL on failure
+ */
+struct devlink *devlink_shd_get(const char *id,
+ const struct devlink_ops *ops,
+ size_t priv_size,
+ const struct device_driver *driver)
+{
+ struct devlink *devlink;
+ struct devlink_shd *shd;
+
+ mutex_lock(&shd_mutex);
+
+ shd = devlink_shd_lookup(id);
+ if (!shd) {
+ shd = devlink_shd_create(id, ops, priv_size, driver);
+ goto unlock;
+ }
+
+ devlink = priv_to_devlink(shd);
+ if (WARN_ON_ONCE(devlink->ops != ops ||
+ shd->priv_size != priv_size ||
+ devlink->dev_driver != driver)) {
+ shd = NULL;
+ goto unlock;
+ }
+ refcount_inc(&shd->refcount);
+
+unlock:
+ mutex_unlock(&shd_mutex);
+ return shd ? priv_to_devlink(shd) : NULL;
+}
+EXPORT_SYMBOL_GPL(devlink_shd_get);
+
+/**
+ * devlink_shd_put - Release a reference on a shared devlink instance
+ * @devlink: Shared devlink instance
+ *
+ * Release a reference on a shared devlink instance obtained via
+ * devlink_shd_get().
+ */
+void devlink_shd_put(struct devlink *devlink)
+{
+ struct devlink_shd *shd;
+
+ mutex_lock(&shd_mutex);
+ shd = devlink_priv(devlink);
+ if (refcount_dec_and_test(&shd->refcount))
+ devlink_shd_destroy(shd);
+ mutex_unlock(&shd_mutex);
+}
+EXPORT_SYMBOL_GPL(devlink_shd_put);
+
+/**
+ * devlink_shd_get_priv - Get private data from shared devlink instance
+ * @devlink: Devlink instance
+ *
+ * Returns a pointer to the driver's private data structure within
+ * the shared devlink instance.
+ *
+ * Return: Pointer to private data
+ */
+void *devlink_shd_get_priv(struct devlink *devlink)
+{
+ struct devlink_shd *shd = devlink_priv(devlink);
+
+ return shd->priv;
+}
+EXPORT_SYMBOL_GPL(devlink_shd_get_priv);