diff options
Diffstat (limited to 'drivers/s390/cio/vfio_ccw_ops.c')
-rw-r--r-- | drivers/s390/cio/vfio_ccw_ops.c | 28 |
1 files changed, 25 insertions, 3 deletions
diff --git a/drivers/s390/cio/vfio_ccw_ops.c b/drivers/s390/cio/vfio_ccw_ops.c index d754d3d90574..b2e615404034 100644 --- a/drivers/s390/cio/vfio_ccw_ops.c +++ b/drivers/s390/cio/vfio_ccw_ops.c @@ -35,7 +35,11 @@ static int vfio_ccw_mdev_reset(struct mdev_device *mdev) if (ret) return ret; - return cio_enable_subchannel(sch, (u32)(unsigned long)sch); + ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch); + if (!ret) + private->state = VFIO_CCW_STATE_IDLE; + + return ret; } static int vfio_ccw_mdev_notifier(struct notifier_block *nb, @@ -112,10 +116,14 @@ static int vfio_ccw_mdev_create(struct kobject *kobj, struct mdev_device *mdev) struct vfio_ccw_private *private = dev_get_drvdata(mdev_parent_dev(mdev)); + if (private->state == VFIO_CCW_STATE_NOT_OPER) + return -ENODEV; + if (atomic_dec_if_positive(&private->avail) < 0) return -EPERM; private->mdev = mdev; + private->state = VFIO_CCW_STATE_IDLE; return 0; } @@ -126,10 +134,20 @@ static int vfio_ccw_mdev_remove(struct mdev_device *mdev) dev_get_drvdata(mdev_parent_dev(mdev)); int ret; + if (!private) + goto out; + + if ((private->state == VFIO_CCW_STATE_NOT_OPER) || + (private->state == VFIO_CCW_STATE_STANDBY)) + goto out; + ret = vfio_ccw_mdev_reset(mdev); if (ret) return ret; + private->state = VFIO_CCW_STATE_STANDBY; + +out: private->mdev = NULL; atomic_inc(&private->avail); @@ -193,14 +211,18 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev, private = dev_get_drvdata(mdev_parent_dev(mdev)); if (!private) return -ENODEV; + if (private->state != VFIO_CCW_STATE_IDLE) + return -EACCES; region = &private->io_region; if (copy_from_user((void *)region + *ppos, buf, count)) return -EFAULT; - region->ret_code = vfio_ccw_sch_cmd_request(private); - if (region->ret_code != 0) + vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ); + if (region->ret_code != 0) { + private->state = VFIO_CCW_STATE_IDLE; return region->ret_code; + } return count; } |