summaryrefslogtreecommitdiffstats
path: root/drivers/s390/cio/vfio_ccw_drv.c
diff options
context:
space:
mode:
authorDong Jia Shi2017-03-17 04:17:40 +0100
committerCornelia Huck2017-03-31 12:55:11 +0200
commitbbe37e4cb89702aa78e0f44618c5af7f9aaa33f6 (patch)
tree46269ef030a61c19f629701e53678e0311b2d2c7 /drivers/s390/cio/vfio_ccw_drv.c
parentvfio: ccw: return I/O results asynchronously (diff)
downloadkernel-qcow2-linux-bbe37e4cb89702aa78e0f44618c5af7f9aaa33f6.tar.gz
kernel-qcow2-linux-bbe37e4cb89702aa78e0f44618c5af7f9aaa33f6.tar.xz
kernel-qcow2-linux-bbe37e4cb89702aa78e0f44618c5af7f9aaa33f6.zip
vfio: ccw: introduce a finite state machine
The current implementation doesn't check if the subchannel is in a proper device state when handling an event. Let's introduce a finite state machine to manage the state/event change. Signed-off-by: Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com> Message-Id: <20170317031743.40128-14-bjsdjshi@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/vfio_ccw_drv.c')
-rw-r--r--drivers/s390/cio/vfio_ccw_drv.c116
1 files changed, 14 insertions, 102 deletions
diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c
index 3e0a40ec8ade..e90dd43d2a55 100644
--- a/drivers/s390/cio/vfio_ccw_drv.c
+++ b/drivers/s390/cio/vfio_ccw_drv.c
@@ -60,54 +60,12 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
ret = cio_disable_subchannel(sch);
} while (ret == -EBUSY);
-
out_unlock:
+ private->state = VFIO_CCW_STATE_NOT_OPER;
spin_unlock_irq(sch->lock);
return ret;
}
-static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
-{
- struct subchannel *sch;
- union orb *orb;
- int ccode;
- __u8 lpm;
-
- sch = private->sch;
-
- orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);
-
- /* Issue "Start Subchannel" */
- ccode = ssch(sch->schid, orb);
-
- switch (ccode) {
- case 0:
- /*
- * Initialize device status information
- */
- sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
- return 0;
- case 1: /* Status pending */
- case 2: /* Busy */
- return -EBUSY;
- case 3: /* Device/path not operational */
- {
- lpm = orb->cmd.lpm;
- if (lpm != 0)
- sch->lpm &= ~lpm;
- else
- sch->lpm = 0;
-
- if (cio_update_schib(sch))
- return -ENODEV;
-
- return sch->lpm ? -EACCES : -ENODEV;
- }
- default:
- return ccode;
- }
-}
-
static void vfio_ccw_sch_io_todo(struct work_struct *work)
{
struct vfio_ccw_private *private;
@@ -126,47 +84,9 @@ static void vfio_ccw_sch_io_todo(struct work_struct *work)
if (private->io_trigger)
eventfd_signal(private->io_trigger, 1);
-}
-
-/* Deal with the ccw command request from the userspace. */
-int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private)
-{
- struct mdev_device *mdev = private->mdev;
- union orb *orb;
- union scsw *scsw = &private->scsw;
- struct ccw_io_region *io_region = &private->io_region;
- int ret;
-
- memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
-
- if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
- orb = (union orb *)io_region->orb_area;
-
- ret = cp_init(&private->cp, mdev_dev(mdev), orb);
- if (ret)
- return ret;
-
- ret = cp_prefetch(&private->cp);
- if (ret) {
- cp_free(&private->cp);
- return ret;
- }
-
- /* Start channel program and wait for I/O interrupt. */
- ret = vfio_ccw_sch_io_helper(private);
- if (!ret)
- cp_free(&private->cp);
- } else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
- /* XXX: Handle halt. */
- ret = -EOPNOTSUPP;
- } else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
- /* XXX: Handle clear. */
- ret = -EOPNOTSUPP;
- } else {
- ret = -EOPNOTSUPP;
- }
- return ret;
+ if (private->mdev)
+ private->state = VFIO_CCW_STATE_IDLE;
}
/*
@@ -223,20 +143,9 @@ static struct attribute_group vfio_subchannel_attr_group = {
static void vfio_ccw_sch_irq(struct subchannel *sch)
{
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
- struct irb *irb;
inc_irq_stat(IRQIO_CIO);
-
- if (!private)
- return;
-
- irb = this_cpu_ptr(&cio_irb);
- memcpy(&private->irb, irb, sizeof(*irb));
-
- queue_work(vfio_ccw_work_q, &private->io_work);
-
- if (private->completion)
- complete(private->completion);
+ vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_INTERRUPT);
}
static int vfio_ccw_sch_probe(struct subchannel *sch)
@@ -258,6 +167,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
dev_set_drvdata(&sch->dev, private);
spin_lock_irq(sch->lock);
+ private->state = VFIO_CCW_STATE_NOT_OPER;
sch->isc = VFIO_CCW_ISC;
ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
spin_unlock_irq(sch->lock);
@@ -274,6 +184,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
atomic_set(&private->avail, 1);
+ private->state = VFIO_CCW_STATE_STANDBY;
return 0;
@@ -321,6 +232,7 @@ static void vfio_ccw_sch_shutdown(struct subchannel *sch)
*/
static int vfio_ccw_sch_event(struct subchannel *sch, int process)
{
+ struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
unsigned long flags;
spin_lock_irqsave(sch->lock, flags);
@@ -331,16 +243,16 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process)
goto out_unlock;
if (cio_update_schib(sch)) {
- /* Not operational. */
- css_sched_sch_todo(sch, SCH_TODO_UNREG);
-
- /*
- * TODO:
- * Probably we should send the machine check to the guest.
- */
+ vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
goto out_unlock;
}
+ private = dev_get_drvdata(&sch->dev);
+ if (private->state == VFIO_CCW_STATE_NOT_OPER) {
+ private->state = private->mdev ? VFIO_CCW_STATE_IDLE :
+ VFIO_CCW_STATE_STANDBY;
+ }
+
out_unlock:
spin_unlock_irqrestore(sch->lock, flags);