Commit e5f84dba authored by Dong Jia Shi's avatar Dong Jia Shi Committed by Cornelia Huck

vfio: ccw: return I/O results asynchronously

Introduce a singlethreaded workqueue to handle the I/O interrupts.
With the work added to this queue, we store the I/O results to the
io_region of the subchannel, then signal the userspace program to
handle the results.
Signed-off-by: default avatarDong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
Message-Id: <20170317031743.40128-13-bjsdjshi@linux.vnet.ibm.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
parent 120e214e
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
#include "css.h" #include "css.h"
#include "vfio_ccw_private.h" #include "vfio_ccw_private.h"
struct workqueue_struct *vfio_ccw_work_q;
/* /*
* Helpers * Helpers
*/ */
...@@ -52,6 +54,7 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch) ...@@ -52,6 +54,7 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
spin_lock_irq(sch->lock); spin_lock_irq(sch->lock);
private->completion = NULL; private->completion = NULL;
flush_workqueue(vfio_ccw_work_q);
ret = cio_cancel_halt_clear(sch, &iretry); ret = cio_cancel_halt_clear(sch, &iretry);
}; };
...@@ -63,18 +66,12 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch) ...@@ -63,18 +66,12 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
return ret; return ret;
} }
static int doing_io(struct vfio_ccw_private *private, u32 intparm)
{
return (private->intparm == intparm);
}
static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private) static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
{ {
struct subchannel *sch; struct subchannel *sch;
union orb *orb; union orb *orb;
int ccode; int ccode;
__u8 lpm; __u8 lpm;
u32 intparm;
sch = private->sch; sch = private->sch;
...@@ -89,7 +86,7 @@ static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private) ...@@ -89,7 +86,7 @@ static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
* Initialize device status information * Initialize device status information
*/ */
sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND; sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
break; return 0;
case 1: /* Status pending */ case 1: /* Status pending */
case 2: /* Busy */ case 2: /* Busy */
return -EBUSY; return -EBUSY;
...@@ -109,15 +106,26 @@ static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private) ...@@ -109,15 +106,26 @@ static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
default: default:
return ccode; return ccode;
} }
}
intparm = (u32)(addr_t)sch; static void vfio_ccw_sch_io_todo(struct work_struct *work)
private->intparm = 0; {
wait_event(private->wait_q, doing_io(private, intparm)); struct vfio_ccw_private *private;
struct subchannel *sch;
struct irb *irb;
if (scsw_is_solicited(&private->irb.scsw)) private = container_of(work, struct vfio_ccw_private, io_work);
cp_update_scsw(&private->cp, &private->irb.scsw); irb = &private->irb;
sch = private->sch;
return 0; if (scsw_is_solicited(&irb->scsw)) {
cp_update_scsw(&private->cp, &irb->scsw);
cp_free(&private->cp);
}
memcpy(private->io_region.irb_area, irb, sizeof(*irb));
if (private->io_trigger)
eventfd_signal(private->io_trigger, 1);
} }
/* Deal with the ccw command request from the userspace. */ /* Deal with the ccw command request from the userspace. */
...@@ -126,7 +134,6 @@ int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private) ...@@ -126,7 +134,6 @@ int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private)
struct mdev_device *mdev = private->mdev; struct mdev_device *mdev = private->mdev;
union orb *orb; union orb *orb;
union scsw *scsw = &private->scsw; union scsw *scsw = &private->scsw;
struct irb *irb = &private->irb;
struct ccw_io_region *io_region = &private->io_region; struct ccw_io_region *io_region = &private->io_region;
int ret; int ret;
...@@ -147,12 +154,8 @@ int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private) ...@@ -147,12 +154,8 @@ int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private)
/* Start channel program and wait for I/O interrupt. */ /* Start channel program and wait for I/O interrupt. */
ret = vfio_ccw_sch_io_helper(private); ret = vfio_ccw_sch_io_helper(private);
if (!ret) { if (!ret)
/* Get irb info and copy it to irb_area. */ cp_free(&private->cp);
memcpy(io_region->irb_area, irb, sizeof(*irb));
}
cp_free(&private->cp);
} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) { } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
/* XXX: Handle halt. */ /* XXX: Handle halt. */
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
...@@ -229,8 +232,8 @@ static void vfio_ccw_sch_irq(struct subchannel *sch) ...@@ -229,8 +232,8 @@ static void vfio_ccw_sch_irq(struct subchannel *sch)
irb = this_cpu_ptr(&cio_irb); irb = this_cpu_ptr(&cio_irb);
memcpy(&private->irb, irb, sizeof(*irb)); memcpy(&private->irb, irb, sizeof(*irb));
private->intparm = (u32)(addr_t)sch;
wake_up(&private->wait_q); queue_work(vfio_ccw_work_q, &private->io_work);
if (private->completion) if (private->completion)
complete(private->completion); complete(private->completion);
...@@ -269,7 +272,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch) ...@@ -269,7 +272,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
if (ret) if (ret)
goto out_rm_group; goto out_rm_group;
init_waitqueue_head(&private->wait_q); INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
atomic_set(&private->avail, 1); atomic_set(&private->avail, 1);
return 0; return 0;
...@@ -367,10 +370,16 @@ static int __init vfio_ccw_sch_init(void) ...@@ -367,10 +370,16 @@ static int __init vfio_ccw_sch_init(void)
{ {
int ret; int ret;
vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
if (!vfio_ccw_work_q)
return -ENOMEM;
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);
destroy_workqueue(vfio_ccw_work_q);
}
return ret; return ret;
} }
...@@ -379,6 +388,7 @@ static void __exit vfio_ccw_sch_exit(void) ...@@ -379,6 +388,7 @@ static void __exit vfio_ccw_sch_exit(void)
{ {
css_driver_unregister(&vfio_ccw_sch_driver); css_driver_unregister(&vfio_ccw_sch_driver);
isc_unregister(VFIO_CCW_ISC); isc_unregister(VFIO_CCW_ISC);
destroy_workqueue(vfio_ccw_work_q);
} }
module_init(vfio_ccw_sch_init); module_init(vfio_ccw_sch_init);
module_exit(vfio_ccw_sch_exit); module_exit(vfio_ccw_sch_exit);
......
...@@ -202,9 +202,6 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev, ...@@ -202,9 +202,6 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
if (region->ret_code != 0) if (region->ret_code != 0)
return region->ret_code; return region->ret_code;
if (private->io_trigger)
eventfd_signal(private->io_trigger, 1);
return count; return count;
} }
......
...@@ -12,6 +12,7 @@ ...@@ -12,6 +12,7 @@
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/eventfd.h> #include <linux/eventfd.h>
#include <linux/workqueue.h>
#include <linux/vfio_ccw.h> #include <linux/vfio_ccw.h>
#include "css.h" #include "css.h"
...@@ -25,12 +26,11 @@ ...@@ -25,12 +26,11 @@
* @mdev: pointer to the mediated device * @mdev: pointer to the mediated device
* @nb: notifier for vfio events * @nb: notifier for vfio events
* @io_region: MMIO region to input/output I/O arguments/results * @io_region: MMIO region to input/output I/O arguments/results
* @wait_q: wait for interrupt
* @intparm: record current interrupt parameter, used for wait interrupt
* @cp: channel program for the current I/O operation * @cp: channel program for the current I/O operation
* @irb: irb info received from interrupt * @irb: irb info received from interrupt
* @scsw: scsw info * @scsw: scsw info
* @io_trigger: eventfd ctx for signaling userspace I/O results * @io_trigger: eventfd ctx for signaling userspace I/O results
* @io_work: work for deferral process of I/O handling
*/ */
struct vfio_ccw_private { struct vfio_ccw_private {
struct subchannel *sch; struct subchannel *sch;
...@@ -40,13 +40,12 @@ struct vfio_ccw_private { ...@@ -40,13 +40,12 @@ struct vfio_ccw_private {
struct notifier_block nb; struct notifier_block nb;
struct ccw_io_region io_region; struct ccw_io_region io_region;
wait_queue_head_t wait_q;
u32 intparm;
struct channel_program cp; struct channel_program cp;
struct irb irb; struct irb irb;
union scsw scsw; union scsw scsw;
struct eventfd_ctx *io_trigger; struct eventfd_ctx *io_trigger;
struct work_struct io_work;
} __aligned(8); } __aligned(8);
extern int vfio_ccw_mdev_reg(struct subchannel *sch); extern int vfio_ccw_mdev_reg(struct subchannel *sch);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment