Skip to content

Commit

Permalink
PCI/VPD: Defer VPD sizing until first access
Browse files Browse the repository at this point in the history
7bac544 ("PCI/VPD: Determine VPD size in pci_vpd_init()") reads VPD at
enumeration-time to find the size.  But this is quite slow, and we don't
need the size until we actually need data from VPD.  Dave reported a boot
slowdown of more than two minutes [1].

Defer the VPD sizing until a driver or the user (via sysfs) requests
information from VPD.

If devices are quirked because VPD is known not to work, don't bother even
looking for the VPD capability.  The VPD will not be accessible at all.

[1] https://lore.kernel.org/r/20210913141818.GA27911@codemonkey.org.uk/
Link: https://lore.kernel.org/r/20210914215543.GA1437800@bjorn-Precision-5520
Fixes: 7bac544 ("PCI/VPD: Determine VPD size in pci_vpd_init()")
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
  • Loading branch information
Bjorn Helgaas committed Sep 15, 2021
1 parent 6880fa6 commit 00e1a5d
Showing 1 changed file with 26 additions and 10 deletions.
36 changes: 26 additions & 10 deletions drivers/pci/vpd.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,24 @@ static size_t pci_vpd_size(struct pci_dev *dev)
return off ?: PCI_VPD_SZ_INVALID;
}

static bool pci_vpd_available(struct pci_dev *dev)
{
struct pci_vpd *vpd = &dev->vpd;

if (!vpd->cap)
return false;

if (vpd->len == 0) {
vpd->len = pci_vpd_size(dev);
if (vpd->len == PCI_VPD_SZ_INVALID) {
vpd->cap = 0;
return false;
}
}

return true;
}

/*
* Wait for last operation to complete.
* This code has to spin since there is no other notification from the PCI
Expand Down Expand Up @@ -145,7 +163,7 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count,
loff_t end = pos + count;
u8 *buf = arg;

if (!vpd->cap)
if (!pci_vpd_available(dev))
return -ENODEV;

if (pos < 0)
Expand Down Expand Up @@ -206,7 +224,7 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
loff_t end = pos + count;
int ret = 0;

if (!vpd->cap)
if (!pci_vpd_available(dev))
return -ENODEV;

if (pos < 0 || (pos & 3) || (count & 3))
Expand Down Expand Up @@ -242,14 +260,11 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,

void pci_vpd_init(struct pci_dev *dev)
{
if (dev->vpd.len == PCI_VPD_SZ_INVALID)
return;

dev->vpd.cap = pci_find_capability(dev, PCI_CAP_ID_VPD);
mutex_init(&dev->vpd.lock);

if (!dev->vpd.len)
dev->vpd.len = pci_vpd_size(dev);

if (dev->vpd.len == PCI_VPD_SZ_INVALID)
dev->vpd.cap = 0;
}

static ssize_t vpd_read(struct file *filp, struct kobject *kobj,
Expand Down Expand Up @@ -294,13 +309,14 @@ const struct attribute_group pci_dev_vpd_attr_group = {

void *pci_vpd_alloc(struct pci_dev *dev, unsigned int *size)
{
unsigned int len = dev->vpd.len;
unsigned int len;
void *buf;
int cnt;

if (!dev->vpd.cap)
if (!pci_vpd_available(dev))
return ERR_PTR(-ENODEV);

len = dev->vpd.len;
buf = kmalloc(len, GFP_KERNEL);
if (!buf)
return ERR_PTR(-ENOMEM);
Expand Down

0 comments on commit 00e1a5d

Please sign in to comment.