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

Message ID 20231202091738.13770-1-yan.y.zhao@intel.com
State New
Headers
Series Sharing KVM TDP to IOMMU |

Commit Message

Yan Zhao Dec. 2, 2023, 9:17 a.m. UTC
  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@intel.com>
---
 include/linux/kvm_host.h |  5 +++
 virt/kvm/tdp_fd.c        | 68 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 72 insertions(+), 1 deletion(-)
  

Patch

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)