PCI: Sleep rather than busy-wait for VPD access completion
authorBjorn Helgaas <bhelgaas@google.com>
Mon, 22 Feb 2016 20:58:18 +0000 (14:58 -0600)
committerBjorn Helgaas <bhelgaas@google.com>
Thu, 10 Mar 2016 20:24:48 +0000 (14:24 -0600)
Use usleep_range() instead of udelay() while waiting for a VPD access to
complete.  This is not a performance path, so no need to hog the CPU.

Rationale for usleep_range() parameters:

  We clear PCI_VPD_ADDR_F for a read (or set it for a write), then wait for
  the device to change it.  For a device that updates PCI_VPD_ADDR between
  our config write and subsequent config read, we won't sleep at all and
  can get the device's maximum rate.

  Sleeping a minimum of 10 usec per 4-byte access limits throughput to
  about 400Kbytes/second.  VPD is small (32K bytes at most), and most
  devices use only a fraction of that.

  We back off exponentially up to 1024 usec per iteration.  If we reach
  1024, we've already waited up to 1008 usec (16 + 32 + ... + 512), so if
  we miss an update and wait an extra 1024 usec, we can still get about
  1/2 of the device's maximum rate.

Tested-by: Shane Seymour <shane.seymour@hpe.com>
Tested-by: Babu Moger <babu.moger@oracle.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Reviewed-by: Hannes Reinecke <hare@suse.com>
drivers/pci/access.c

index 8449d6b581782cff62428d9ef34f1bdc038eba9e..01b9d0a00abcb06186bdb80d44c5aa9e5f05838c 100644 (file)
@@ -342,14 +342,15 @@ static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size)
 static int pci_vpd_wait(struct pci_dev *dev)
 {
        struct pci_vpd *vpd = dev->vpd;
-       unsigned long timeout = jiffies + HZ/20 + 2;
+       unsigned long timeout = jiffies + msecs_to_jiffies(50);
+       unsigned long max_sleep = 16;
        u16 status;
        int ret;
 
        if (!vpd->busy)
                return 0;
 
-       for (;;) {
+       while (time_before(jiffies, timeout)) {
                ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR,
                                                &status);
                if (ret < 0)
@@ -360,15 +361,16 @@ static int pci_vpd_wait(struct pci_dev *dev)
                        return 0;
                }
 
-               if (time_after(jiffies, timeout)) {
-                       dev_printk(KERN_DEBUG, &dev->dev, "vpd r/w failed.  This is likely a firmware bug on this device.  Contact the card vendor for a firmware update\n");
-                       return -ETIMEDOUT;
-               }
                if (fatal_signal_pending(current))
                        return -EINTR;
-               if (!cond_resched())
-                       udelay(10);
+
+               usleep_range(10, max_sleep);
+               if (max_sleep < 1024)
+                       max_sleep *= 2;
        }
+
+       dev_warn(&dev->dev, "VPD access failed.  This is likely a firmware bug on this device.  Contact the card vendor for a firmware update\n");
+       return -ETIMEDOUT;
 }
 
 static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,