[RFC PATCH 06/42] KVM: Register/Unregister importers to KVM exported TDP

From: Yan Zhao
Date: Sat Dec 02 2023 - 04:46:42 EST


Each TDP exported by KVM has its own list of importers. External components
can register/unregister itself as an importer with a unique importer ops.

The sequence for external components to register/unregister as importer is
like:
1. call kvm_tdp_fd_get() to get a KVM TDP fd object.
2. call tdp_fd->ops->register_importer() to register itself as an importer.
3. call tdp_fd->ops->unregister_importer() to unregister itself as
importer.
4. call kvm_tdp_fd_put() to put the KVM TDP fd object.

When destroying a KVM TDP fd object, all importers are force-unregistered.
There's no extra notification to the importers at that time because the
force-unregister should only happen when importers calls kvm_tdp_fd_put()
without calling tdp_fd->ops->unregister_importer() first.

Signed-off-by: Yan Zhao <yan.y.zhao@xxxxxxxxx>
---
include/linux/kvm_host.h | 5 +++
virt/kvm/tdp_fd.c | 68 +++++++++++++++++++++++++++++++++++++++-
2 files changed, 72 insertions(+), 1 deletion(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 5a74b2b0ac81f..f73d32eef8833 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -2334,6 +2334,11 @@ struct kvm_exported_tdp {

struct kvm *kvm;
u32 as_id;
+
+ /* protect importers list */
+ spinlock_t importer_lock;
+ struct list_head importers;
+
/* head at kvm->exported_tdp_list */
struct list_head list_node;
};
diff --git a/virt/kvm/tdp_fd.c b/virt/kvm/tdp_fd.c
index 7e68199ea9643..3271da1a4b2c1 100644
--- a/virt/kvm/tdp_fd.c
+++ b/virt/kvm/tdp_fd.c
@@ -13,6 +13,13 @@ static inline int is_tdp_fd_file(struct file *file);
static const struct file_operations kvm_tdp_fd_fops;
static const struct kvm_exported_tdp_ops exported_tdp_ops;

+struct kvm_tdp_importer {
+ struct kvm_tdp_importer_ops *ops;
+ void *data;
+ struct list_head node;
+};
+static void kvm_tdp_unregister_all_importers(struct kvm_exported_tdp *tdp);
+
int kvm_create_tdp_fd(struct kvm *kvm, struct kvm_create_tdp_fd *ct)
{
struct kvm_exported_tdp *tdp;
@@ -56,6 +63,9 @@ int kvm_create_tdp_fd(struct kvm *kvm, struct kvm_create_tdp_fd *ct)
if (ret)
goto out;

+ INIT_LIST_HEAD(&tdp->importers);
+ spin_lock_init(&tdp->importer_lock);
+
tdp_fd->file = anon_inode_getfile("tdp_fd", &kvm_tdp_fd_fops,
tdp_fd, O_RDWR | O_CLOEXEC);
if (!tdp_fd->file) {
@@ -107,6 +117,7 @@ static int kvm_tdp_fd_release(struct inode *inode, struct file *file)
list_del(&tdp->list_node);
spin_unlock(&tdp->kvm->exported_tdplist_lock);

+ kvm_tdp_unregister_all_importers(tdp);
kvm_arch_exported_tdp_destroy(tdp);
kvm_put_kvm(tdp->kvm);
kfree(tdp);
@@ -141,12 +152,67 @@ static inline int is_tdp_fd_file(struct file *file)
static int kvm_tdp_register_importer(struct kvm_tdp_fd *tdp_fd,
struct kvm_tdp_importer_ops *ops, void *data)
{
- return -EOPNOTSUPP;
+ struct kvm_tdp_importer *importer, *tmp;
+ struct kvm_exported_tdp *tdp;
+
+ if (!tdp_fd || !tdp_fd->priv || !ops)
+ return -EINVAL;
+
+ tdp = tdp_fd->priv;
+ importer = kzalloc(sizeof(*importer), GFP_KERNEL);
+ if (!importer)
+ return -ENOMEM;
+
+ spin_lock(&tdp->importer_lock);
+ list_for_each_entry(tmp, &tdp->importers, node) {
+ if (tmp->ops != ops)
+ continue;
+
+ kfree(importer);
+ spin_unlock(&tdp->importer_lock);
+ return -EBUSY;
+ }
+
+ importer->ops = ops;
+ importer->data = data;
+ list_add(&importer->node, &tdp->importers);
+
+ spin_unlock(&tdp->importer_lock);
+
+ return 0;
}

static void kvm_tdp_unregister_importer(struct kvm_tdp_fd *tdp_fd,
struct kvm_tdp_importer_ops *ops)
{
+ struct kvm_tdp_importer *importer, *n;
+ struct kvm_exported_tdp *tdp;
+
+ if (!tdp_fd || !tdp_fd->priv)
+ return;
+
+ tdp = tdp_fd->priv;
+ spin_lock(&tdp->importer_lock);
+ list_for_each_entry_safe(importer, n, &tdp->importers, node) {
+ if (importer->ops != ops)
+ continue;
+
+ list_del(&importer->node);
+ kfree(importer);
+ }
+ spin_unlock(&tdp->importer_lock);
+}
+
+static void kvm_tdp_unregister_all_importers(struct kvm_exported_tdp *tdp)
+{
+ struct kvm_tdp_importer *importer, *n;
+
+ spin_lock(&tdp->importer_lock);
+ list_for_each_entry_safe(importer, n, &tdp->importers, node) {
+ list_del(&importer->node);
+ kfree(importer);
+ }
+ spin_unlock(&tdp->importer_lock);
}

static void *kvm_tdp_get_metadata(struct kvm_tdp_fd *tdp_fd)
--
2.17.1