Commit 3bf1311f authored by Jason Gunthorpe's avatar Jason Gunthorpe Committed by Alex Williamson
Browse files

vfio/ccw: Convert to use vfio_register_emulated_iommu_dev()



This is a more complicated conversion because vfio_ccw is sharing the
vfio_device between both the mdev_device, its vfio_device and the
css_driver.

The mdev is a singleton, and the reason for this sharing is so the extra
css_driver function callbacks to be delivered to the vfio_device
implementation.

This keeps things as they are, with the css_driver allocating the
singleton, not the mdev_driver.

Embed the vfio_device in the vfio_ccw_private and instantiate it as a
vfio_device when the mdev probes. The drvdata of both the css_device and
the mdev_device point at the private, and container_of is used to get it
back from the vfio_device.

Reviewed-by: default avatarEric Farman <farman@linux.ibm.com>
Signed-off-by: default avatarJason Gunthorpe <jgg@nvidia.com>
Link: https://lore.kernel.org/r/4-v4-cea4f5bd2c00+b52-ccw_mdev_jgg@nvidia.com


Signed-off-by: default avatarAlex Williamson <alex.williamson@redhat.com>
parent 39b6ee01
Loading
Loading
Loading
Loading
+14 −7
Original line number Original line Diff line number Diff line
@@ -468,7 +468,7 @@ static int __init vfio_ccw_sch_init(void)
	vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
	vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
	if (!vfio_ccw_work_q) {
	if (!vfio_ccw_work_q) {
		ret = -ENOMEM;
		ret = -ENOMEM;
		goto out_err;
		goto out_regions;
	}
	}


	vfio_ccw_io_region = kmem_cache_create_usercopy("vfio_ccw_io_region",
	vfio_ccw_io_region = kmem_cache_create_usercopy("vfio_ccw_io_region",
@@ -477,7 +477,7 @@ static int __init vfio_ccw_sch_init(void)
					sizeof(struct ccw_io_region), NULL);
					sizeof(struct ccw_io_region), NULL);
	if (!vfio_ccw_io_region) {
	if (!vfio_ccw_io_region) {
		ret = -ENOMEM;
		ret = -ENOMEM;
		goto out_err;
		goto out_regions;
	}
	}


	vfio_ccw_cmd_region = kmem_cache_create_usercopy("vfio_ccw_cmd_region",
	vfio_ccw_cmd_region = kmem_cache_create_usercopy("vfio_ccw_cmd_region",
@@ -486,7 +486,7 @@ static int __init vfio_ccw_sch_init(void)
					sizeof(struct ccw_cmd_region), NULL);
					sizeof(struct ccw_cmd_region), NULL);
	if (!vfio_ccw_cmd_region) {
	if (!vfio_ccw_cmd_region) {
		ret = -ENOMEM;
		ret = -ENOMEM;
		goto out_err;
		goto out_regions;
	}
	}


	vfio_ccw_schib_region = kmem_cache_create_usercopy("vfio_ccw_schib_region",
	vfio_ccw_schib_region = kmem_cache_create_usercopy("vfio_ccw_schib_region",
@@ -496,7 +496,7 @@ static int __init vfio_ccw_sch_init(void)


	if (!vfio_ccw_schib_region) {
	if (!vfio_ccw_schib_region) {
		ret = -ENOMEM;
		ret = -ENOMEM;
		goto out_err;
		goto out_regions;
	}
	}


	vfio_ccw_crw_region = kmem_cache_create_usercopy("vfio_ccw_crw_region",
	vfio_ccw_crw_region = kmem_cache_create_usercopy("vfio_ccw_crw_region",
@@ -506,19 +506,25 @@ static int __init vfio_ccw_sch_init(void)


	if (!vfio_ccw_crw_region) {
	if (!vfio_ccw_crw_region) {
		ret = -ENOMEM;
		ret = -ENOMEM;
		goto out_err;
		goto out_regions;
	}
	}


	ret = mdev_register_driver(&vfio_ccw_mdev_driver);
	if (ret)
		goto out_regions;

	isc_register(VFIO_CCW_ISC);
	isc_register(VFIO_CCW_ISC);
	ret = css_driver_register(&vfio_ccw_sch_driver);
	ret = css_driver_register(&vfio_ccw_sch_driver);
	if (ret) {
	if (ret) {
		isc_unregister(VFIO_CCW_ISC);
		isc_unregister(VFIO_CCW_ISC);
		goto out_err;
		goto out_driver;
	}
	}


	return ret;
	return ret;


out_err:
out_driver:
	mdev_unregister_driver(&vfio_ccw_mdev_driver);
out_regions:
	vfio_ccw_destroy_regions();
	vfio_ccw_destroy_regions();
	destroy_workqueue(vfio_ccw_work_q);
	destroy_workqueue(vfio_ccw_work_q);
	vfio_ccw_debug_exit();
	vfio_ccw_debug_exit();
@@ -528,6 +534,7 @@ static int __init vfio_ccw_sch_init(void)
static void __exit vfio_ccw_sch_exit(void)
static void __exit vfio_ccw_sch_exit(void)
{
{
	css_driver_unregister(&vfio_ccw_sch_driver);
	css_driver_unregister(&vfio_ccw_sch_driver);
	mdev_unregister_driver(&vfio_ccw_mdev_driver);
	isc_unregister(VFIO_CCW_ISC);
	isc_unregister(VFIO_CCW_ISC);
	vfio_ccw_destroy_regions();
	vfio_ccw_destroy_regions();
	destroy_workqueue(vfio_ccw_work_q);
	destroy_workqueue(vfio_ccw_work_q);
+66 −41
Original line number Original line Diff line number Diff line
@@ -17,6 +17,8 @@


#include "vfio_ccw_private.h"
#include "vfio_ccw_private.h"


static const struct vfio_device_ops vfio_ccw_dev_ops;

static int vfio_ccw_mdev_reset(struct vfio_ccw_private *private)
static int vfio_ccw_mdev_reset(struct vfio_ccw_private *private)
{
{
	struct subchannel *sch;
	struct subchannel *sch;
@@ -111,10 +113,10 @@ static struct attribute_group *mdev_type_groups[] = {
	NULL,
	NULL,
};
};


static int vfio_ccw_mdev_create(struct mdev_device *mdev)
static int vfio_ccw_mdev_probe(struct mdev_device *mdev)
{
{
	struct vfio_ccw_private *private =
	struct vfio_ccw_private *private = dev_get_drvdata(mdev->dev.parent);
		dev_get_drvdata(mdev_parent_dev(mdev));
	int ret;


	if (private->state == VFIO_CCW_STATE_NOT_OPER)
	if (private->state == VFIO_CCW_STATE_NOT_OPER)
		return -ENODEV;
		return -ENODEV;
@@ -122,6 +124,10 @@ static int vfio_ccw_mdev_create(struct mdev_device *mdev)
	if (atomic_dec_if_positive(&private->avail) < 0)
	if (atomic_dec_if_positive(&private->avail) < 0)
		return -EPERM;
		return -EPERM;


	memset(&private->vdev, 0, sizeof(private->vdev));
	vfio_init_group_dev(&private->vdev, &mdev->dev,
			    &vfio_ccw_dev_ops);

	private->mdev = mdev;
	private->mdev = mdev;
	private->state = VFIO_CCW_STATE_IDLE;
	private->state = VFIO_CCW_STATE_IDLE;


@@ -130,19 +136,31 @@ static int vfio_ccw_mdev_create(struct mdev_device *mdev)
			   private->sch->schid.ssid,
			   private->sch->schid.ssid,
			   private->sch->schid.sch_no);
			   private->sch->schid.sch_no);


	ret = vfio_register_emulated_iommu_dev(&private->vdev);
	if (ret)
		goto err_atomic;
	dev_set_drvdata(&mdev->dev, private);
	return 0;
	return 0;

err_atomic:
	vfio_uninit_group_dev(&private->vdev);
	atomic_inc(&private->avail);
	private->mdev = NULL;
	private->state = VFIO_CCW_STATE_IDLE;
	return ret;
}
}


static int vfio_ccw_mdev_remove(struct mdev_device *mdev)
static void vfio_ccw_mdev_remove(struct mdev_device *mdev)
{
{
	struct vfio_ccw_private *private =
	struct vfio_ccw_private *private = dev_get_drvdata(mdev->dev.parent);
		dev_get_drvdata(mdev_parent_dev(mdev));


	VFIO_CCW_MSG_EVENT(2, "mdev %pUl, sch %x.%x.%04x: remove\n",
	VFIO_CCW_MSG_EVENT(2, "mdev %pUl, sch %x.%x.%04x: remove\n",
			   mdev_uuid(mdev), private->sch->schid.cssid,
			   mdev_uuid(mdev), private->sch->schid.cssid,
			   private->sch->schid.ssid,
			   private->sch->schid.ssid,
			   private->sch->schid.sch_no);
			   private->sch->schid.sch_no);


	vfio_unregister_group_dev(&private->vdev);

	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
	    (private->state != VFIO_CCW_STATE_STANDBY)) {
	    (private->state != VFIO_CCW_STATE_STANDBY)) {
		if (!vfio_ccw_sch_quiesce(private->sch))
		if (!vfio_ccw_sch_quiesce(private->sch))
@@ -150,23 +168,22 @@ static int vfio_ccw_mdev_remove(struct mdev_device *mdev)
		/* The state will be NOT_OPER on error. */
		/* The state will be NOT_OPER on error. */
	}
	}


	vfio_uninit_group_dev(&private->vdev);
	cp_free(&private->cp);
	cp_free(&private->cp);
	private->mdev = NULL;
	private->mdev = NULL;
	atomic_inc(&private->avail);
	atomic_inc(&private->avail);

	return 0;
}
}


static int vfio_ccw_mdev_open_device(struct mdev_device *mdev)
static int vfio_ccw_mdev_open_device(struct vfio_device *vdev)
{
{
	struct vfio_ccw_private *private =
	struct vfio_ccw_private *private =
		dev_get_drvdata(mdev_parent_dev(mdev));
		container_of(vdev, struct vfio_ccw_private, vdev);
	unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
	unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
	int ret;
	int ret;


	private->nb.notifier_call = vfio_ccw_mdev_notifier;
	private->nb.notifier_call = vfio_ccw_mdev_notifier;


	ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
	ret = vfio_register_notifier(vdev->dev, VFIO_IOMMU_NOTIFY,
				     &events, &private->nb);
				     &events, &private->nb);
	if (ret)
	if (ret)
		return ret;
		return ret;
@@ -187,15 +204,15 @@ static int vfio_ccw_mdev_open_device(struct mdev_device *mdev)


out_unregister:
out_unregister:
	vfio_ccw_unregister_dev_regions(private);
	vfio_ccw_unregister_dev_regions(private);
	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
	vfio_unregister_notifier(vdev->dev, VFIO_IOMMU_NOTIFY,
				 &private->nb);
				 &private->nb);
	return ret;
	return ret;
}
}


static void vfio_ccw_mdev_close_device(struct mdev_device *mdev)
static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
{
{
	struct vfio_ccw_private *private =
	struct vfio_ccw_private *private =
		dev_get_drvdata(mdev_parent_dev(mdev));
		container_of(vdev, struct vfio_ccw_private, vdev);


	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
	if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
	    (private->state != VFIO_CCW_STATE_STANDBY)) {
	    (private->state != VFIO_CCW_STATE_STANDBY)) {
@@ -206,8 +223,7 @@ static void vfio_ccw_mdev_close_device(struct mdev_device *mdev)


	cp_free(&private->cp);
	cp_free(&private->cp);
	vfio_ccw_unregister_dev_regions(private);
	vfio_ccw_unregister_dev_regions(private);
	vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
	vfio_unregister_notifier(vdev->dev, VFIO_IOMMU_NOTIFY, &private->nb);
				 &private->nb);
}
}


static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
@@ -231,15 +247,14 @@ static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
	return ret;
	return ret;
}
}


static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev,
				  char __user *buf,
				  char __user *buf,
				  size_t count,
				  size_t count,
				  loff_t *ppos)
				  loff_t *ppos)
{
{
	struct vfio_ccw_private *private =
		container_of(vdev, struct vfio_ccw_private, vdev);
	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
	struct vfio_ccw_private *private;

	private = dev_get_drvdata(mdev_parent_dev(mdev));


	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
		return -EINVAL;
		return -EINVAL;
@@ -284,15 +299,14 @@ static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private,
	return ret;
	return ret;
}
}


static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev,
				   const char __user *buf,
				   const char __user *buf,
				   size_t count,
				   size_t count,
				   loff_t *ppos)
				   loff_t *ppos)
{
{
	struct vfio_ccw_private *private =
		container_of(vdev, struct vfio_ccw_private, vdev);
	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
	unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
	struct vfio_ccw_private *private;

	private = dev_get_drvdata(mdev_parent_dev(mdev));


	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
	if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
		return -EINVAL;
		return -EINVAL;
@@ -510,12 +524,12 @@ void vfio_ccw_unregister_dev_regions(struct vfio_ccw_private *private)
	private->region = NULL;
	private->region = NULL;
}
}


static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
static ssize_t vfio_ccw_mdev_ioctl(struct vfio_device *vdev,
				   unsigned int cmd,
				   unsigned int cmd,
				   unsigned long arg)
				   unsigned long arg)
{
{
	struct vfio_ccw_private *private =
	struct vfio_ccw_private *private =
		dev_get_drvdata(mdev_parent_dev(mdev));
		container_of(vdev, struct vfio_ccw_private, vdev);
	int ret = 0;
	int ret = 0;
	unsigned long minsz;
	unsigned long minsz;


@@ -606,31 +620,26 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
}
}


/* Request removal of the device*/
/* Request removal of the device*/
static void vfio_ccw_mdev_request(struct mdev_device *mdev, unsigned int count)
static void vfio_ccw_mdev_request(struct vfio_device *vdev, unsigned int count)
{
{
	struct vfio_ccw_private *private = dev_get_drvdata(mdev_parent_dev(mdev));
	struct vfio_ccw_private *private =

		container_of(vdev, struct vfio_ccw_private, vdev);
	if (!private)
	struct device *dev = vdev->dev;
		return;


	if (private->req_trigger) {
	if (private->req_trigger) {
		if (!(count % 10))
		if (!(count % 10))
			dev_notice_ratelimited(mdev_dev(private->mdev),
			dev_notice_ratelimited(dev,
					       "Relaying device request to user (#%u)\n",
					       "Relaying device request to user (#%u)\n",
					       count);
					       count);


		eventfd_signal(private->req_trigger, 1);
		eventfd_signal(private->req_trigger, 1);
	} else if (count == 0) {
	} else if (count == 0) {
		dev_notice(mdev_dev(private->mdev),
		dev_notice(dev,
			   "No device request channel registered, blocked until released by user\n");
			   "No device request channel registered, blocked until released by user\n");
	}
	}
}
}


static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
static const struct vfio_device_ops vfio_ccw_dev_ops = {
	.owner			= THIS_MODULE,
	.supported_type_groups  = mdev_type_groups,
	.create			= vfio_ccw_mdev_create,
	.remove			= vfio_ccw_mdev_remove,
	.open_device = vfio_ccw_mdev_open_device,
	.open_device = vfio_ccw_mdev_open_device,
	.close_device = vfio_ccw_mdev_close_device,
	.close_device = vfio_ccw_mdev_close_device,
	.read = vfio_ccw_mdev_read,
	.read = vfio_ccw_mdev_read,
@@ -639,6 +648,22 @@ static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
	.request = vfio_ccw_mdev_request,
	.request = vfio_ccw_mdev_request,
};
};


struct mdev_driver vfio_ccw_mdev_driver = {
	.driver = {
		.name = "vfio_ccw_mdev",
		.owner = THIS_MODULE,
		.mod_name = KBUILD_MODNAME,
	},
	.probe = vfio_ccw_mdev_probe,
	.remove = vfio_ccw_mdev_remove,
};

static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
	.owner			= THIS_MODULE,
	.device_driver		= &vfio_ccw_mdev_driver,
	.supported_type_groups  = mdev_type_groups,
};

int vfio_ccw_mdev_reg(struct subchannel *sch)
int vfio_ccw_mdev_reg(struct subchannel *sch)
{
{
	return mdev_register_device(&sch->dev, &vfio_ccw_mdev_ops);
	return mdev_register_device(&sch->dev, &vfio_ccw_mdev_ops);
+5 −0
Original line number Original line Diff line number Diff line
@@ -17,6 +17,7 @@
#include <linux/eventfd.h>
#include <linux/eventfd.h>
#include <linux/workqueue.h>
#include <linux/workqueue.h>
#include <linux/vfio_ccw.h>
#include <linux/vfio_ccw.h>
#include <linux/vfio.h>
#include <asm/crw.h>
#include <asm/crw.h>
#include <asm/debug.h>
#include <asm/debug.h>


@@ -67,6 +68,7 @@ struct vfio_ccw_crw {


/**
/**
 * struct vfio_ccw_private
 * struct vfio_ccw_private
 * @vdev: Embedded VFIO device
 * @sch: pointer to the subchannel
 * @sch: pointer to the subchannel
 * @state: internal state of the device
 * @state: internal state of the device
 * @completion: synchronization helper of the I/O completion
 * @completion: synchronization helper of the I/O completion
@@ -90,6 +92,7 @@ struct vfio_ccw_crw {
 * @crw_work: work for deferral process of CRW handling
 * @crw_work: work for deferral process of CRW handling
 */
 */
struct vfio_ccw_private {
struct vfio_ccw_private {
	struct vfio_device vdev;
	struct subchannel	*sch;
	struct subchannel	*sch;
	int			state;
	int			state;
	struct completion	*completion;
	struct completion	*completion;
@@ -121,6 +124,8 @@ extern void vfio_ccw_mdev_unreg(struct subchannel *sch);


extern int vfio_ccw_sch_quiesce(struct subchannel *sch);
extern int vfio_ccw_sch_quiesce(struct subchannel *sch);


extern struct mdev_driver vfio_ccw_mdev_driver;

/*
/*
 * States of the device statemachine.
 * States of the device statemachine.
 */
 */