NET, BLK and RNG remove their devices through virtio_reset(). Only the
modern PCI transport currently frees its virtqueues during reset, so
MMIO and legacy PCI retain queue allocations across remove and reprobe.

Wait for reset completion and release queues in the common helper.
Keep transport reset callbacks limited to resetting hardware only.

Signed-off-by: Bin Meng <[email protected]>
---

 drivers/virtio/virtio-uclass.c     | 18 +++++++++++++++++-
 drivers/virtio/virtio_pci_modern.c | 12 +-----------
 include/virtio.h                   |  7 ++++++-
 3 files changed, 24 insertions(+), 13 deletions(-)

diff --git a/drivers/virtio/virtio-uclass.c b/drivers/virtio/virtio-uclass.c
index c163b2fd7d5..978ef0d1ce7 100644
--- a/drivers/virtio/virtio-uclass.c
+++ b/drivers/virtio/virtio-uclass.c
@@ -25,6 +25,7 @@
 #include <virtio.h>
 #include <dm/lists.h>
 #include <linux/bug.h>
+#include <linux/delay.h>
 
 static const char *const virtio_drv_name[VIRTIO_ID_MAX_NUM] = {
        [VIRTIO_ID_NET]         = VIRTIO_NET_DRV_NAME,
@@ -85,10 +86,25 @@ int virtio_set_status(struct udevice *vdev, u8 status)
 int virtio_reset(struct udevice *vdev)
 {
        struct dm_virtio_ops *ops;
+       u8 status;
+       int ret;
 
        ops = virtio_get_ops(vdev->parent);
 
-       return ops->reset(vdev->parent);
+       ret = ops->reset(vdev->parent);
+       if (ret)
+               return ret;
+
+       /* Wait for the device to stop accessing the queues before freeing them 
*/
+       do {
+               ret = ops->get_status(vdev->parent, &status);
+               if (ret)
+                       return ret;
+               if (status)
+                       udelay(1000);
+       } while (status);
+
+       return ops->del_vqs(vdev->parent);
 }
 
 int virtio_get_features(struct udevice *vdev, u64 *features)
diff --git a/drivers/virtio/virtio_pci_modern.c 
b/drivers/virtio/virtio_pci_modern.c
index d309f9a9dc1..abc0163fa82 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -14,7 +14,6 @@
 #include <dm/device.h>
 #include <linux/bug.h>
 #include <linux/compat.h>
-#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include "virtio_pci.h"
@@ -350,16 +349,7 @@ static int virtio_pci_reset(struct udevice *udev)
        /* 0 status means a reset */
        iowrite8(0, &priv->common->device_status);
 
-       /*
-        * After writing 0 to device_status, the driver MUST wait for a read
-        * of device_status to return 0 before reinitializing the device.
-        * This will flush out the status write, and flush in device writes,
-        * including MSI-X interrupts, if any.
-        */
-       while (ioread8(&priv->common->device_status))
-               udelay(1000);
-
-       return virtio_pci_del_vqs(udev);
+       return 0;
 }
 
 static int virtio_pci_notify(struct udevice *udev, struct virtqueue *vq)
diff --git a/include/virtio.h b/include/virtio.h
index b981eead5d7..30e5005e9ad 100644
--- a/include/virtio.h
+++ b/include/virtio.h
@@ -160,6 +160,9 @@ struct dm_virtio_ops {
        /**
         * reset() - reset the device
         *
+        * The uclass waits for the status to become zero and frees the
+        * virtqueues after this operation succeeds.
+        *
         * @vdev:       the real virtio device
         * @return 0 if OK, -ve on error
         */
@@ -288,7 +291,9 @@ int virtio_get_status(struct udevice *vdev, u8 *status);
 int virtio_set_status(struct udevice *vdev, u8 status);
 
 /**
- * virtio_reset() - reset the device
+ * virtio_reset() - reset the device and free its virtqueues
+ *
+ * Wait for the device status to become zero before freeing the virtqueues.
  *
  * @vdev:      the real virtio device
  * Return: 0 if OK, -ve on error
-- 
2.53.0

Reply via email to