sch->schid.cssid, sch->schid.ssid,
@@ -222,10 +214,14 @@ static void vfio_ccw_sch_remove(struct
subchannel *sch)
static void vfio_ccw_sch_shutdown(struct subchannel *sch)
{
- struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
+ struct vfio_ccw_private *private = vfio_ccw_get_priv(sch);
+
+ if (!private)
+ return;
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_BROKEN);
+ vfio_device_put(&private->vdev);
}
/**
@@ -240,14 +236,14 @@ 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);
+ struct vfio_ccw_private *private = vfio_ccw_get_priv(sch);
unsigned long flags;
int rc = -EAGAIN;
- spin_lock_irqsave(sch->lock, flags);
- if (!device_is_registered(&sch->dev))
- goto out_unlock;
+ if (!private)
+ return -EAGAIN;
+ spin_lock_irqsave(sch->lock, flags);
if (work_pending(&sch->todo_work))
goto out_unlock;
@@ -260,7 +256,7 @@ static int vfio_ccw_sch_event(struct subchannel
*sch, int process)
out_unlock:
spin_unlock_irqrestore(sch->lock, flags);
-
+ vfio_device_put(&private->vdev);
return rc;
}
@@ -294,7 +290,7 @@ static void vfio_ccw_queue_crw(struct
vfio_ccw_private *private,
static int vfio_ccw_chp_event(struct subchannel *sch,
struct chp_link *link, int event)
{
- struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
+ struct vfio_ccw_private *private = vfio_ccw_get_priv(sch);
int mask = chp_ssd_get_mask(&sch->ssd_info, link);
int retry = 255;
@@ -307,8 +303,10 @@ static int vfio_ccw_chp_event(struct
subchannel *sch,
sch->schid.ssid, sch->schid.sch_no,
mask, event);
- if (cio_update_schib(sch))
+ if (cio_update_schib(sch)) {
+ vfio_device_put(&private->vdev);
return -ENODEV;
+ }
switch (event) {
case CHP_VARY_OFF:
@@ -338,6 +336,7 @@ static int vfio_ccw_chp_event(struct subchannel
*sch,
break;
}
+ vfio_device_put(&private->vdev);
return 0;
}
diff --git a/drivers/s390/cio/vfio_ccw_ops.c
b/drivers/s390/cio/vfio_ccw_ops.c
index 68aae25a0a4be0..414b11ea7eebf9 100644
--- a/drivers/s390/cio/vfio_ccw_ops.c
+++ b/drivers/s390/cio/vfio_ccw_ops.c
@@ -17,8 +17,6 @@
#include "vfio_ccw_private.h"
-static const struct vfio_device_ops vfio_ccw_dev_ops;
-
static int vfio_ccw_mdev_reset(struct vfio_ccw_private *private)
{
/*
@@ -88,26 +86,27 @@ static struct attribute_group
*mdev_type_groups[] = {
static int vfio_ccw_mdev_probe(struct mdev_device *mdev)
{
- struct vfio_ccw_private *private = dev_get_drvdata(mdev-
dev.parent);
+ struct subchannel *sch = to_subchannel(mdev->dev.parent);
+ struct vfio_ccw_private *private;
int ret;
- memset(&private->vdev, 0, sizeof(private->vdev));
- vfio_init_group_dev(&private->vdev, &mdev->dev,
- &vfio_ccw_dev_ops);
+ private = vfio_ccw_alloc_private(mdev, sch);
+ if (IS_ERR(private))
+ return PTR_ERR(private);
VFIO_CCW_MSG_EVENT(2, "mdev %s, sch %x.%x.%04x: create\n",
- dev_name(private->vdev.dev),
- private->sch->schid.cssid, private->sch-
schid.ssid,
- private->sch->schid.sch_no);
+ dev_name(private->vdev.dev), sch-
schid.cssid,
+ sch->schid.ssid, sch->schid.sch_no);
ret = vfio_register_emulated_iommu_dev(&private->vdev);
if (ret)
- goto err_init;
+ goto err_alloc;
dev_set_drvdata(&mdev->dev, private);
+ dev_set_drvdata(&sch->dev, private);
return 0;
-err_init:
- vfio_uninit_group_dev(&private->vdev);
+err_alloc:
+ vfio_ccw_free_private(private);
return ret;
}
@@ -120,8 +119,9 @@ static void vfio_ccw_mdev_remove(struct
mdev_device *mdev)
private->sch->schid.cssid, private->sch-
schid.ssid,
private->sch->schid.sch_no);
+ dev_set_drvdata(&private->sch->dev, NULL);
vfio_unregister_group_dev(&private->vdev);
- vfio_uninit_group_dev(&private->vdev);
+ vfio_ccw_free_private(private);
}
static int vfio_ccw_mdev_open_device(struct vfio_device *vdev)
@@ -595,7 +595,7 @@ static unsigned int
vfio_ccw_get_available(struct mdev_type *mtype)
return 1;
}
-static const struct vfio_device_ops vfio_ccw_dev_ops = {
+const struct vfio_device_ops vfio_ccw_dev_ops = {
.open_device = vfio_ccw_mdev_open_device,
.close_device = vfio_ccw_mdev_close_device,
.read = vfio_ccw_mdev_read,
@@ -615,19 +615,9 @@ struct mdev_driver vfio_ccw_mdev_driver = {
.get_available = vfio_ccw_get_available,
};
-static const struct mdev_parent_ops vfio_ccw_mdev_ops = {
+const struct mdev_parent_ops vfio_ccw_mdev_ops = {
.owner = THIS_MODULE,
.device_driver = &vfio_ccw_mdev_driver,
.device_api = VFIO_DEVICE_API_CCW_STRING,
.supported_type_groups = mdev_type_groups,
};
-
-int vfio_ccw_mdev_reg(struct subchannel *sch)
-{
- return mdev_register_device(&sch->dev, &vfio_ccw_mdev_ops);
-}
-
-void vfio_ccw_mdev_unreg(struct subchannel *sch)
-{
- mdev_unregister_device(&sch->dev);
-}
diff --git a/drivers/s390/cio/vfio_ccw_private.h
b/drivers/s390/cio/vfio_ccw_private.h
index 67ee9c624393b0..852ff94fc107d6 100644
--- a/drivers/s390/cio/vfio_ccw_private.h
+++ b/drivers/s390/cio/vfio_ccw_private.h
@@ -24,6 +24,8 @@
#include "css.h"
#include "vfio_ccw_cp.h"
+struct mdev_device;
+
#define VFIO_CCW_OFFSET_SHIFT 10
#define VFIO_CCW_OFFSET_TO_INDEX(off) (off >>
VFIO_CCW_OFFSET_SHIFT)
#define VFIO_CCW_INDEX_TO_OFFSET(index) ((u64)(index) <<
VFIO_CCW_OFFSET_SHIFT)
@@ -69,6 +71,7 @@ struct vfio_ccw_crw {
/**
* struct vfio_ccw_private
* @vdev: Embedded VFIO device
+ * @rcu: head for kfree_rcu()
* @sch: pointer to the subchannel
* @state: internal state of the device
* @completion: synchronization helper of the I/O completion
@@ -91,6 +94,7 @@ struct vfio_ccw_crw {
*/
struct vfio_ccw_private {
struct vfio_device vdev;
+ struct rcu_head rcu;
struct subchannel *sch;
int state;
struct completion *completion;
@@ -115,10 +119,25 @@ struct vfio_ccw_private {
struct work_struct crw_work;
} __aligned(8);
-extern int vfio_ccw_mdev_reg(struct subchannel *sch);
-extern void vfio_ccw_mdev_unreg(struct subchannel *sch);
+struct vfio_ccw_private *vfio_ccw_alloc_private(struct mdev_device
*mdev,
+ struct subchannel
*sch);
+void vfio_ccw_free_private(struct vfio_ccw_private *private);
extern struct mdev_driver vfio_ccw_mdev_driver;
+extern const struct mdev_parent_ops vfio_ccw_mdev_ops;
+extern const struct vfio_device_ops vfio_ccw_dev_ops;
+
+static inline struct vfio_ccw_private *vfio_ccw_get_priv(struct
subchannel *sch)
+{
+ struct vfio_ccw_private *private;
+
+ rcu_read_lock();
+ private = dev_get_drvdata(&sch->dev);
+ if (private && !vfio_device_try_get(&private->vdev))
+ private = NULL;
+ rcu_read_unlock();
+ return private;
+}
/*
* States of the device statemachine.
--
2.33.0