mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-23 09:19:51 +00:00
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: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> Message-Id: <20170317031743.40128-13-bjsdjshi@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
This commit is contained in:
parent
120e214e50
commit
e5f84dbaea
3 changed files with 37 additions and 31 deletions
|
@ -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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
* 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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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)
|
||||||
|
|
||||||
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)
|
||||||
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)
|
||||||
{
|
{
|
||||||
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)
|
||||||
{
|
{
|
||||||
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,
|
||||||
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 @@
|
||||||
|
|
||||||
#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 @@
|
||||||
* @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 {
|
||||||
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);
|
||||||
|
|
Loading…
Reference in a new issue