VFIO_DEVICE_RESET is implemented in the kernel with
pci_try_reset_function(), which uses pci_dev_trylock() and returns
-EAGAIN when the PCI device lock is already held.

That lock is taken by other kernel paths, in particular sysfs
uevent_show(). After Linux restored device_lock() in uevent_show()
(revert of "driver core: Fix uevent_show() vs driver detach race"),
udev or any reader of /sys/.../uevent can race with DPDK VFIO
setup, and the ioctl returns errno 11 (EAGAIN).

Without a retry, pci_rte_vfio_setup_device() fails, EAL skips the
PCI device, and the application cannot use it:

  EAL: Unable to reset device! Error: 11 (Resource temporarily unavailable)
  EAL: 0000:f4:02.3 setup device failed
  EAL: Requested device 0000:f4:02.3 cannot be used

The device lock is held only for the sysfs/uevent critical section,
typically well under 20 ms. Retry the ioctl up to 5 times with a
20 ms delay (100 ms in total). That was enough for the transient
failure we observed; a persistent EAGAIN still fails device setup.

Fixes: f25f8f367644 ("bus/pci: check VFIO reset ioctl error")
Cc: [email protected]

Signed-off-by: Thanushree Sreerama <[email protected]>
---
Cc: [email protected]
Cc: Anatoly Burakov <[email protected]>

Changes since v1:
- Rebased on main
- Drop Change-Id
- Document EAGAIN condition, functional impact, and retry values
- Fail setup if retries are exhausted
- Match pci_vfio.c declaration style

 drivers/bus/pci/linux/pci_vfio.c | 20 ++++++++++++++++----
 1 file changed, 16 insertions(+), 4 deletions(-)

diff --git a/drivers/bus/pci/linux/pci_vfio.c b/drivers/bus/pci/linux/pci_vfio.c
index bc5c5c249..3ec5af8ed 100644
--- a/drivers/bus/pci/linux/pci_vfio.c
+++ b/drivers/bus/pci/linux/pci_vfio.c
@@ -420,6 +420,10 @@ pci_vfio_is_ioport_bar(const struct rte_pci_device *dev, 
int vfio_dev_fd,
 static int
 pci_rte_vfio_setup_device(struct rte_pci_device *dev, int vfio_dev_fd)
 {
+       const int max_retries = 5;
+       const int retry_delay_ms = 20;
+       int i, ret;
+
        if (pci_vfio_setup_interrupts(dev, vfio_dev_fd) != 0) {
                PCI_LOG(ERR, "Error setting up interrupts!");
                return -1;
@@ -439,12 +443,20 @@ pci_rte_vfio_setup_device(struct rte_pci_device *dev, int 
vfio_dev_fd)
         * Reset the device. If the device is not capable of resetting,
         * then it updates errno as EINVAL.
         */
-       if (ioctl(vfio_dev_fd, VFIO_DEVICE_RESET) && errno != EINVAL) {
-               PCI_LOG(ERR, "Unable to reset device! Error: %d (%s)", errno, 
strerror(errno));
-               return -1;
+       for (i = 0; i < max_retries; i++) {
+               errno = 0;
+               ret = ioctl(vfio_dev_fd, VFIO_DEVICE_RESET);
+               if (ret == 0 || errno == EINVAL)
+                       return 0;
+               if (errno != EAGAIN || i + 1 == max_retries)
+                       break;
+               PCI_LOG(DEBUG, "Device reset EAGAIN, retry %d/%d after %d ms",
+                       i + 1, max_retries, retry_delay_ms);
+               usleep(retry_delay_ms * 1000);
        }
 
-       return 0;
+       PCI_LOG(ERR, "Unable to reset device! Error: %d (%s)", errno, 
strerror(errno));
+       return -1;
 }
 
 static int
-- 
2.45.0

Reply via email to