diff options
| author | Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> | 2017-03-17 04:17:38 +0100 | 
|---|---|---|
| committer | Cornelia Huck <cornelia.huck@de.ibm.com> | 2017-03-31 12:55:09 +0200 | 
| commit | 120e214e504fd6d3e33ec4b661193600b2faab95 (patch) | |
| tree | 781dcb51968c9f6e3860570b177a73306add0a47 /drivers/s390/cio | |
| parent | 83d1193a96dc78576c15f93cd70e0558763a85b3 (diff) | |
| download | linux-120e214e504fd6d3e33ec4b661193600b2faab95.tar.bz2 | |
vfio: ccw: realize VFIO_DEVICE_G(S)ET_IRQ_INFO ioctls
Realize VFIO_DEVICE_GET_IRQ_INFO ioctl to retrieve
VFIO_CCW_IO_IRQ information.
Realize VFIO_DEVICE_SET_IRQS ioctl to set an eventfd fd for
VFIO_CCW_IO_IRQ. Once a write operation to the ccw_io_region
was performed, trigger a signal on this fd.
Reviewed-by: Pierre Morel <pmorel@linux.vnet.ibm.com>
Signed-off-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
Acked-by: Alex Williamson <alex.williamson@redhat.com>
Message-Id: <20170317031743.40128-12-bjsdjshi@linux.vnet.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio')
| -rw-r--r-- | drivers/s390/cio/vfio_ccw_ops.c | 123 | ||||
| -rw-r--r-- | drivers/s390/cio/vfio_ccw_private.h | 4 | 
2 files changed, 126 insertions, 1 deletions
| diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index 125818cdf305..1294c5347410 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -202,6 +202,9 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,  	if (region->ret_code != 0)  		return region->ret_code; +	if (private->io_trigger) +		eventfd_signal(private->io_trigger, 1); +  	return count;  } @@ -209,7 +212,7 @@ static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info)  {  	info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;  	info->num_regions = VFIO_CCW_NUM_REGIONS; -	info->num_irqs = 0; +	info->num_irqs = VFIO_CCW_NUM_IRQS;  	return 0;  } @@ -230,6 +233,83 @@ static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,  	}  } +int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info) +{ +	if (info->index != VFIO_CCW_IO_IRQ_INDEX) +		return -EINVAL; + +	info->count = 1; +	info->flags = VFIO_IRQ_INFO_EVENTFD; + +	return 0; +} + +static int vfio_ccw_mdev_set_irqs(struct mdev_device *mdev, +				  uint32_t flags, +				  void __user *data) +{ +	struct vfio_ccw_private *private; +	struct eventfd_ctx **ctx; + +	if (!(flags & VFIO_IRQ_SET_ACTION_TRIGGER)) +		return -EINVAL; + +	private = dev_get_drvdata(mdev_parent_dev(mdev)); +	if (!private) +		return -ENODEV; + +	ctx = &private->io_trigger; + +	switch (flags & VFIO_IRQ_SET_DATA_TYPE_MASK) { +	case VFIO_IRQ_SET_DATA_NONE: +	{ +		if (*ctx) +			eventfd_signal(*ctx, 1); +		return 0; +	} +	case VFIO_IRQ_SET_DATA_BOOL: +	{ +		uint8_t trigger; + +		if (get_user(trigger, (uint8_t __user *)data)) +			return -EFAULT; + +		if (trigger && *ctx) +			eventfd_signal(*ctx, 1); +		return 0; +	} +	case VFIO_IRQ_SET_DATA_EVENTFD: +	{ +		int32_t fd; + +		if (get_user(fd, (int32_t __user *)data)) +			return -EFAULT; + +		if (fd == -1) { +			if (*ctx) +				eventfd_ctx_put(*ctx); +			*ctx = NULL; +		} else if (fd >= 0) { +			struct eventfd_ctx *efdctx; + +			efdctx = eventfd_ctx_fdget(fd); +			if (IS_ERR(efdctx)) +				return PTR_ERR(efdctx); + +			if (*ctx) +				eventfd_ctx_put(*ctx); + +			*ctx = efdctx; +		} else +			return -EINVAL; + +		return 0; +	} +	default: +		return -EINVAL; +	} +} +  static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,  				   unsigned int cmd,  				   unsigned long arg) @@ -277,6 +357,47 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,  		return copy_to_user((void __user *)arg, &info, minsz);  	} +	case VFIO_DEVICE_GET_IRQ_INFO: +	{ +		struct vfio_irq_info info; + +		minsz = offsetofend(struct vfio_irq_info, count); + +		if (copy_from_user(&info, (void __user *)arg, minsz)) +			return -EFAULT; + +		if (info.argsz < minsz || info.index >= VFIO_CCW_NUM_IRQS) +			return -EINVAL; + +		ret = vfio_ccw_mdev_get_irq_info(&info); +		if (ret) +			return ret; + +		if (info.count == -1) +			return -EINVAL; + +		return copy_to_user((void __user *)arg, &info, minsz); +	} +	case VFIO_DEVICE_SET_IRQS: +	{ +		struct vfio_irq_set hdr; +		size_t data_size; +		void __user *data; + +		minsz = offsetofend(struct vfio_irq_set, count); + +		if (copy_from_user(&hdr, (void __user *)arg, minsz)) +			return -EFAULT; + +		ret = vfio_set_irqs_validate_and_prepare(&hdr, 1, +							 VFIO_CCW_NUM_IRQS, +							 &data_size); +		if (ret) +			return ret; + +		data = (void __user *)(arg + minsz); +		return vfio_ccw_mdev_set_irqs(mdev, hdr.flags, data); +	}  	case VFIO_DEVICE_RESET:  		return vfio_ccw_mdev_reset(mdev);  	default: diff --git a/drivers/s390/cio/vfio_ccw_private.h b/drivers/s390/cio/vfio_ccw_private.h index 79e53378f212..dddab52913ed 100644 --- a/drivers/s390/cio/vfio_ccw_private.h +++ b/drivers/s390/cio/vfio_ccw_private.h @@ -11,6 +11,7 @@  #define _VFIO_CCW_PRIVATE_H_  #include <linux/completion.h> +#include <linux/eventfd.h>  #include <linux/vfio_ccw.h>  #include "css.h" @@ -29,6 +30,7 @@   * @cp: channel program for the current I/O operation   * @irb: irb info received from interrupt   * @scsw: scsw info + * @io_trigger: eventfd ctx for signaling userspace I/O results   */  struct vfio_ccw_private {  	struct subchannel	*sch; @@ -43,6 +45,8 @@ struct vfio_ccw_private {  	struct channel_program	cp;  	struct irb		irb;  	union scsw		scsw; + +	struct eventfd_ctx	*io_trigger;  } __aligned(8);  extern int vfio_ccw_mdev_reg(struct subchannel *sch); |