We need a convenient way to manage the fact that a START SUBCHANNEL command is synchronous, the HALT SUBCHANNEL and CLEAR SUBCHANNEL commands are asynchronous, and the interrupts for all three are also asynchronous and unstacked from a workqueue. Fortunately, the POPS does provide a mechanism to serialize the operations, in the form of the activity control flags of the SCSW. Since we initialize the private->scsw from the guest io_region for each new START (done under the protection of the io_mutex), and then never touch it again, we can use that as a space to indicate which commands are active at the device. For a START SUBCHANNEL command, the POPS states: > Condition code 2 is set, and no other action is > taken, when a start, halt, or clear function is currently > in progress at the subchannel So, mark START PENDING in this copy of the SCSW Activity Controls, and use it to track when a command has started versus when its interrupt has been unstacked from the workqueue and processed. It's a bit unnatural, in that this doesn't transition the flags to Subchannel/Device Active once the command has been accepted. Since this is only in our local copy of the SCSW, and not the actual contents of the SCHIB, this is fine enough. Signed-off-by: Eric Farman <farman@xxxxxxxxxxxxx> --- drivers/s390/cio/vfio_ccw_drv.c | 4 +++- drivers/s390/cio/vfio_ccw_fsm.c | 12 ++++++++++++ drivers/s390/cio/vfio_ccw_ops.c | 4 +++- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/s390/cio/vfio_ccw_drv.c b/drivers/s390/cio/vfio_ccw_drv.c index 7dd3efa1ccb8..ee153fa72a0f 100644 --- a/drivers/s390/cio/vfio_ccw_drv.c +++ b/drivers/s390/cio/vfio_ccw_drv.c @@ -98,8 +98,10 @@ static void vfio_ccw_sch_io_todo(struct work_struct *work) memcpy(private->io_region->irb_area, irb, sizeof(*irb)); mutex_unlock(&private->io_mutex); - if (private->mdev && scsw_is_solicited(&irb->scsw) && is_final) + if (private->mdev && scsw_is_solicited(&irb->scsw) && is_final) { private->state = VFIO_CCW_STATE_IDLE; + private->scsw.cmd.actl &= ~SCSW_ACTL_START_PEND; + } if (private->io_trigger) eventfd_signal(private->io_trigger, 1); diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c index 23e61aa638e4..258ce32549f3 100644 --- a/drivers/s390/cio/vfio_ccw_fsm.c +++ b/drivers/s390/cio/vfio_ccw_fsm.c @@ -246,8 +246,20 @@ static void fsm_io_request(struct vfio_ccw_private *private, char *errstr = "request"; struct subchannel_id schid = get_schid(private); + if (scsw_actl(scsw) & SCSW_ACTL_START_PEND) { + io_region->ret_code = -EBUSY; + VFIO_CCW_MSG_EVENT(2, + "%pUl (%x.%x.%04x): actl %x pending\n", + mdev_uuid(mdev), schid.cssid, + schid.ssid, schid.sch_no, + scsw_actl(scsw)); + errstr = "pending"; + goto err_out; + } + private->state = VFIO_CCW_STATE_CP_PROCESSING; memcpy(scsw, io_region->scsw_area, sizeof(*scsw)); + scsw->cmd.actl |= SCSW_ACTL_START_PEND; if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) { orb = (union orb *)io_region->orb_area; diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index f0d71ab77c50..d2f9babb751c 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -269,8 +269,10 @@ static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private, } vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ); - if (region->ret_code != 0) + if (region->ret_code != 0) { private->state = VFIO_CCW_STATE_IDLE; + private->scsw.cmd.actl &= ~SCSW_ACTL_START_PEND; + } ret = (region->ret_code != 0) ? region->ret_code : count; out_unlock: -- 2.17.1