[RFC PATCH 3/7] tsm: vfio: Add tsm bind/unbind support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This will be used to bind a TDI to the VM instance.

Signed-off-by: Aneesh Kumar K.V (Arm) <aneesh.kumar@xxxxxxxxxx>
---
 drivers/pci/tsm.c           |  48 +++++++++++++++
 drivers/vfio/pci/vfio_pci.c |  13 ++++
 drivers/vfio/vfio_main.c    |  31 ++++++++++
 include/linux/pci-tsm.h     |  17 ++++++
 include/linux/vfio.h        |   6 ++
 include/uapi/linux/kvm.h    |  17 ++++++
 virt/kvm/vfio.c             | 116 ++++++++++++++++++++++++++++++++++++
 7 files changed, 248 insertions(+)

diff --git a/drivers/pci/tsm.c b/drivers/pci/tsm.c
index 720b54d422b7..1a071130dea3 100644
--- a/drivers/pci/tsm.c
+++ b/drivers/pci/tsm.c
@@ -292,3 +292,51 @@ int pci_tsm_doe_transfer(struct pci_dev *pdev, enum pci_doe_proto type,
 		       req_sz, resp, resp_sz);
 }
 EXPORT_SYMBOL_GPL(pci_tsm_doe_transfer);
+
+static int __pci_tsm_bind(struct vfio_device *vfio_dev, u32 guest_rid)
+{
+	int rc;
+	struct pci_dev *pdev = to_pci_dev(vfio_dev->dev);
+	struct pci_tsm *pci_tsm = pdev->tsm;
+
+	scoped_cond_guard(mutex_intr, return -EINTR, &pci_tsm->lock) {
+		if (pci_tsm->state != PCI_TSM_CONNECT)
+			return -ENXIO;
+
+		/* This should hold a reference to the module providing tsm_ops */
+		rc = tsm_ops->bind(vfio_dev, guest_rid);
+		if (rc)
+			return rc;
+	}
+	return 0;
+}
+
+int pci_tsm_bind(struct vfio_device *vfio_dev, u32 guest_rid)
+{
+	int ret = -ENXIO;
+
+	scoped_cond_guard(rwsem_read_intr, return -EINTR, &pci_tsm_rwsem) {
+		if (tsm_ops)
+			ret = __pci_tsm_bind(vfio_dev, guest_rid);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pci_tsm_bind);
+
+/*
+ * pci_tsm_ops can't be NULL since we hold a module reference during bind.
+ * Hence No pci_tsm_rwsem locking needed.
+ */
+void pci_tsm_unbind(struct vfio_device *vfio_dev)
+{
+	struct pci_dev *pdev = to_pci_dev(vfio_dev->dev);
+	struct pci_tsm *pci_tsm = pdev->tsm;
+
+	scoped_cond_guard(mutex_intr, return, &pci_tsm->lock) {
+		if (pci_tsm->state != PCI_TSM_BOUND)
+			return;
+
+		tsm_ops->unbind(vfio_dev);
+	}
+}
+EXPORT_SYMBOL_GPL(pci_tsm_unbind);
diff --git a/drivers/vfio/pci/vfio_pci.c b/drivers/vfio/pci/vfio_pci.c
index e727941f589d..a1e1eb4c26db 100644
--- a/drivers/vfio/pci/vfio_pci.c
+++ b/drivers/vfio/pci/vfio_pci.c
@@ -24,6 +24,7 @@
 #include <linux/slab.h>
 #include <linux/types.h>
 #include <linux/uaccess.h>
+#include <linux/pci-tsm.h>
 
 #include "vfio_pci_priv.h"
 
@@ -127,6 +128,16 @@ static int vfio_pci_open_device(struct vfio_device *core_vdev)
 	return 0;
 }
 
+static int vfio_pci_tsm_bind(struct vfio_device *core_vdev, u32 guest_rid)
+{
+	return pci_tsm_bind(core_vdev, guest_rid);
+}
+
+static void vfio_pci_tsm_unbind(struct vfio_device *core_vdev)
+{
+	return pci_tsm_unbind(core_vdev);
+}
+
 static const struct vfio_device_ops vfio_pci_ops = {
 	.name		= "vfio-pci",
 	.init		= vfio_pci_core_init_dev,
@@ -144,6 +155,8 @@ static const struct vfio_device_ops vfio_pci_ops = {
 	.unbind_iommufd	= vfio_iommufd_physical_unbind,
 	.attach_ioas	= vfio_iommufd_physical_attach_ioas,
 	.detach_ioas	= vfio_iommufd_physical_detach_ioas,
+	.tsm_bind	= vfio_pci_tsm_bind,
+	.tsm_unbind	= vfio_pci_tsm_unbind,
 };
 
 static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
diff --git a/drivers/vfio/vfio_main.c b/drivers/vfio/vfio_main.c
index 1fd261efc582..b24644c9c841 100644
--- a/drivers/vfio/vfio_main.c
+++ b/drivers/vfio/vfio_main.c
@@ -462,6 +462,21 @@ void vfio_device_get_kvm_safe(struct vfio_device *device, struct kvm *kvm)
 	device->kvm = kvm;
 }
 
+int vfio_tsm_bind(struct kvm *kvm, struct vfio_device *vdev, u32 guest_rid)
+{
+	if (vdev->ops->tsm_bind)
+		return vdev->ops->tsm_bind(vdev, guest_rid);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(vfio_tsm_bind);
+
+void vfio_tsm_unbind(struct kvm *kvm, struct vfio_device *vdev)
+{
+	if (vdev->ops->tsm_bind)
+		return vdev->ops->tsm_unbind(vdev);
+}
+EXPORT_SYMBOL_GPL(vfio_tsm_unbind);
+
 void vfio_device_put_kvm(struct vfio_device *device)
 {
 	lockdep_assert_held(&device->dev_set->lock);
@@ -472,6 +487,11 @@ void vfio_device_put_kvm(struct vfio_device *device)
 	if (WARN_ON(!device->put_kvm))
 		goto clear;
 
+	/* Unbind TDI here */
+	vfio_tsm_unbind(device->kvm, device);
+	/* drop the reference held in kvm_dev_tsm_bind */
+	vfio_put_device(device);
+
 	device->put_kvm(device->kvm);
 	device->put_kvm = NULL;
 	symbol_put(kvm_put_kvm);
@@ -1447,6 +1467,17 @@ void vfio_file_set_kvm(struct file *file, struct kvm *kvm)
 }
 EXPORT_SYMBOL_GPL(vfio_file_set_kvm);
 
+struct vfio_device *vfio_file_device(struct file *filep)
+{
+	struct vfio_device_file *df = filep->private_data;
+
+	if (filep->f_op != &vfio_device_fops)
+		return NULL;
+
+	return df->device;
+}
+EXPORT_SYMBOL_GPL(vfio_file_device);
+
 /*
  * Sub-module support
  */
diff --git a/include/linux/pci-tsm.h b/include/linux/pci-tsm.h
index beb0d68129bc..774496d7b37e 100644
--- a/include/linux/pci-tsm.h
+++ b/include/linux/pci-tsm.h
@@ -2,6 +2,7 @@
 #ifndef __PCI_TSM_H
 #define __PCI_TSM_H
 #include <linux/mutex.h>
+#include <linux/vfio_pci_core.h>
 
 struct pci_dev;
 
@@ -17,6 +18,7 @@ enum pci_tsm_state {
 	PCI_TSM_ERR = -1,
 	PCI_TSM_INIT,
 	PCI_TSM_CONNECT,
+	PCI_TSM_BOUND,
 };
 
 /**
@@ -49,6 +51,8 @@ struct pci_tsm_ops {
 	void (*remove)(struct pci_dsm *dsm);
 	int (*connect)(struct pci_dev *pdev);
 	void (*disconnect)(struct pci_dev *pdev);
+	int (*bind)(struct vfio_device *vfio_dev, u32 guest_rid);
+	void (*unbind)(struct vfio_device *vfio_dev);
 };
 
 enum pci_doe_proto {
@@ -63,6 +67,9 @@ void pci_tsm_unregister(const struct pci_tsm_ops *ops);
 int pci_tsm_doe_transfer(struct pci_dev *pdev, enum pci_doe_proto type,
 			 const void *req, size_t req_sz, void *resp,
 			 size_t resp_sz);
+int pci_tsm_bind(struct vfio_device *vfio_dev, u32 guest_rid);
+void pci_tsm_unbind(struct vfio_device *vfio_dev);
+
 #else
 static inline int pci_tsm_register(const struct pci_tsm_ops *ops,
 				   const struct attribute_group *grp)
@@ -79,5 +86,15 @@ static inline int pci_tsm_doe_transfer(struct pci_dev *pdev,
 {
 	return -ENOENT;
 }
+
+static inline int pci_tsm_bind(struct vfio_device *vifo_dev, u32 guest_rid);
+{
+	return -EINVAL;
+}
+
+static inline void pci_tsm_unbind(struct vfio_device *vfio_dev)
+{
+	return -EINVAL;
+}
 #endif
 #endif /*__PCI_TSM_H */
diff --git a/include/linux/vfio.h b/include/linux/vfio.h
index 000a6cab2d31..a177dcade4aa 100644
--- a/include/linux/vfio.h
+++ b/include/linux/vfio.h
@@ -129,6 +129,8 @@ struct vfio_device_ops {
 	void	(*dma_unmap)(struct vfio_device *vdev, u64 iova, u64 length);
 	int	(*device_feature)(struct vfio_device *device, u32 flags,
 				  void __user *arg, size_t argsz);
+	int	(*tsm_bind)(struct vfio_device *vdev, u32 guest_rid);
+	void	(*tsm_unbind)(struct vfio_device *vdev);
 };
 
 #if IS_ENABLED(CONFIG_IOMMUFD)
@@ -316,6 +318,10 @@ static inline bool vfio_file_has_dev(struct file *file, struct vfio_device *devi
 bool vfio_file_is_valid(struct file *file);
 bool vfio_file_enforced_coherent(struct file *file);
 void vfio_file_set_kvm(struct file *file, struct kvm *kvm);
+struct vfio_device *vfio_file_device(struct file *file);
+void vfio_tsm_unbind(struct kvm *kvm, struct vfio_device *vdev);
+int vfio_tsm_bind(struct kvm *kvm, struct vfio_device *vdev, u32 guest_rid);
+
 
 #define VFIO_PIN_PAGES_MAX_ENTRIES	(PAGE_SIZE/sizeof(unsigned long))
 
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 9cabf9b6a9b4..6c251f04c5dd 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1604,4 +1604,21 @@ struct kvm_arm_rmm_psci_complete {
 /* FIXME: Update nr (0xd2) when merging */
 #define KVM_ARM_VCPU_RMM_PSCI_COMPLETE	_IOW(KVMIO, 0xd2, struct kvm_arm_rmm_psci_complete)
 
+#define  KVM_DEV_VFIO_DEVICE			2
+#define  KVM_DEV_VFIO_DEVICE_TDI_BIND		1
+#define  KVM_DEV_VFIO_DEVICE_TDI_UNBIND		2
+
+/*
+ * struct kvm_vfio_tsm_bind
+ *
+ * @guest_rid: Hypervisor provided identifier used by the guest to identify
+ *             the TDI in guest messages
+ * @devfd: a fd of VFIO device
+ */
+struct kvm_vfio_tsm_bind {
+	__u32 guest_rid;
+	__s32 devfd;
+} __packed;
+
+
 #endif /* __LINUX_KVM_H */
diff --git a/virt/kvm/vfio.c b/virt/kvm/vfio.c
index 196a102e34fb..525aeccfaf2b 100644
--- a/virt/kvm/vfio.c
+++ b/virt/kvm/vfio.c
@@ -15,6 +15,7 @@
 #include <linux/slab.h>
 #include <linux/uaccess.h>
 #include <linux/vfio.h>
+#include <linux/tsm.h>
 #include "vfio.h"
 
 #ifdef CONFIG_SPAPR_TCE_IOMMU
@@ -80,6 +81,23 @@ static bool kvm_vfio_file_is_valid(struct file *file)
 	return ret;
 }
 
+static struct vfio_device *kvm_vfio_file_device(struct file *file)
+{
+	struct vfio_device *(*fn)(struct file *file);
+	struct vfio_device *ret;
+
+	fn = symbol_get(vfio_file_device);
+	if (!fn)
+		return NULL;
+
+	ret = fn(file);
+
+	symbol_put(vfio_file_device);
+
+	return ret;
+}
+
+
 #ifdef CONFIG_SPAPR_TCE_IOMMU
 static struct iommu_group *kvm_vfio_file_iommu_group(struct file *file)
 {
@@ -291,6 +309,94 @@ static int kvm_vfio_set_file(struct kvm_device *dev, long attr,
 	return -ENXIO;
 }
 
+static int kvm_dev_tsm_bind(struct kvm_device *dev, void __user *arg)
+{
+	int (*tsm_bind)(struct kvm *kvm, struct vfio_device *vdev, u32 guest_rid);
+	struct kvm_vfio *kv = dev->private;
+	struct kvm_vfio_tsm_bind tb;
+	struct vfio_device *vdev;
+	struct file *filp;
+	int ret;
+
+	if (copy_from_user(&tb, arg, sizeof(tb)))
+		return -EFAULT;
+
+	filp = fget(tb.devfd);
+	if (!filp)
+		return -EBADF;
+
+	ret = -ENOENT;
+
+	tsm_bind = symbol_get(vfio_tsm_bind);
+	if (!tsm_bind)
+		goto err_out;
+
+	mutex_lock(&kv->lock);
+	vdev = kvm_vfio_file_device(filp);
+	if (vdev) {
+		/* hold the reference to the vfio device file when we bind. */
+		get_device(&vdev->device);
+		ret = (*tsm_bind)(dev->kvm, vdev, tb.guest_rid);
+		if (ret)
+			vfio_put_device(vdev);
+	}
+	mutex_unlock(&kv->lock);
+	symbol_put(vfio_tsm_bind);
+err_out:
+	fput(filp);
+	return ret;
+}
+
+static int kvm_dev_tsm_unbind(struct kvm_device *dev, void __user *arg)
+{
+	void (*tsm_unbind)(struct kvm *kvm, struct vfio_device *vdev);
+	struct kvm_vfio *kv = dev->private;
+	struct kvm_vfio_tsm_bind tb;
+	struct vfio_device *vdev;
+	struct file *filp;
+	int ret;
+
+	if (copy_from_user(&tb, arg, sizeof(tb)))
+		return -EFAULT;
+
+	filp = fget(tb.devfd);
+	if (!filp)
+		return -EBADF;
+
+	ret = -ENOENT;
+
+	tsm_unbind = symbol_get(vfio_tsm_unbind);
+	if (!tsm_unbind)
+		goto err_out;
+
+	mutex_lock(&kv->lock);
+	vdev = kvm_vfio_file_device(filp);
+	if (vdev) {
+		(*tsm_unbind)(dev->kvm, vdev);
+		/* drop the reference held in kvm_dev_tsm_bind */
+		vfio_put_device(vdev);
+		ret = 0;
+	}
+	mutex_unlock(&kv->lock);
+	symbol_put(vfio_tsm_unbind);
+err_out:
+	fput(filp);
+	return ret;
+}
+
+static int kvm_vfio_set_device(struct kvm_device *dev, long attr,
+			       void __user *arg)
+{
+	switch (attr) {
+	case KVM_DEV_VFIO_DEVICE_TDI_BIND:
+		return kvm_dev_tsm_bind(dev, arg);
+	case KVM_DEV_VFIO_DEVICE_TDI_UNBIND:
+		return kvm_dev_tsm_unbind(dev, arg);
+	}
+
+	return -ENXIO;
+}
+
 static int kvm_vfio_set_attr(struct kvm_device *dev,
 			     struct kvm_device_attr *attr)
 {
@@ -298,6 +404,9 @@ static int kvm_vfio_set_attr(struct kvm_device *dev,
 	case KVM_DEV_VFIO_FILE:
 		return kvm_vfio_set_file(dev, attr->attr,
 					 u64_to_user_ptr(attr->addr));
+	case KVM_DEV_VFIO_DEVICE:
+		return kvm_vfio_set_device(dev, attr->attr,
+					   u64_to_user_ptr(attr->addr));
 	}
 
 	return -ENXIO;
@@ -317,6 +426,13 @@ static int kvm_vfio_has_attr(struct kvm_device *dev,
 			return 0;
 		}
 
+		break;
+	case KVM_DEV_VFIO_DEVICE:
+		switch (attr->attr) {
+		case KVM_DEV_VFIO_DEVICE_TDI_BIND:
+		case KVM_DEV_VFIO_DEVICE_TDI_UNBIND:
+			return 0;
+		}
 		break;
 	}
 
-- 
2.43.0





[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux