Commit cb92148b authored by Hariprasad Shenai's avatar Hariprasad Shenai Committed by Bjorn Helgaas

PCI: Add pci_set_vpd_size() to set VPD size

After 104daa71 ("PCI: Determine actual VPD size on first access"), the
PCI core computes the valid VPD size by parsing the VPD starting at offset
0x0.  We don't attempt to read past that valid size because that causes
some devices to crash.

However, some devices do have data past that valid size.  For example,
Chelsio adapters contain two VPD structures, and the driver needs both of
them.

Add pci_set_vpd_size().  If a driver knows it is safe to read past the end
of the VPD data structure at offset 0, it can use pci_set_vpd_size() to
allow access to as much data as it needs.

[bhelgaas: changelog, split patches, rename to pci_set_vpd_size() and
return int (not ssize_t)]
Fixes: 104daa71 ("PCI: Determine actual VPD size on first access")
Tested-by: default avatarSteve Wise <swise@opengridcomputing.com>
Signed-off-by: default avatarCasey Leedom <leedom@chelsio.com>
Signed-off-by: default avatarHariprasad Shenai <hariprasad@chelsio.com>
Signed-off-by: default avatarBjorn Helgaas <bhelgaas@google.com>
parent b2d7a9cd
...@@ -275,6 +275,19 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void ...@@ -275,6 +275,19 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void
} }
EXPORT_SYMBOL(pci_write_vpd); EXPORT_SYMBOL(pci_write_vpd);
/**
* pci_set_vpd_size - Set size of Vital Product Data space
* @dev: pci device struct
* @len: size of vpd space
*/
int pci_set_vpd_size(struct pci_dev *dev, size_t len)
{
if (!dev->vpd || !dev->vpd->ops)
return -ENODEV;
return dev->vpd->ops->set_size(dev, len);
}
EXPORT_SYMBOL(pci_set_vpd_size);
#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) #define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
/** /**
...@@ -498,9 +511,23 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, ...@@ -498,9 +511,23 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count,
return ret ? ret : count; return ret ? ret : count;
} }
static int pci_vpd_set_size(struct pci_dev *dev, size_t len)
{
struct pci_vpd *vpd = dev->vpd;
if (len == 0 || len > PCI_VPD_MAX_SIZE)
return -EIO;
vpd->valid = 1;
vpd->len = len;
return 0;
}
static const struct pci_vpd_ops pci_vpd_ops = { static const struct pci_vpd_ops pci_vpd_ops = {
.read = pci_vpd_read, .read = pci_vpd_read,
.write = pci_vpd_write, .write = pci_vpd_write,
.set_size = pci_vpd_set_size,
}; };
static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count,
...@@ -533,9 +560,24 @@ static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, ...@@ -533,9 +560,24 @@ static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count,
return ret; return ret;
} }
static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len)
{
struct pci_dev *tdev = pci_get_slot(dev->bus,
PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
int ret;
if (!tdev)
return -ENODEV;
ret = pci_set_vpd_size(tdev, len);
pci_dev_put(tdev);
return ret;
}
static const struct pci_vpd_ops pci_vpd_f0_ops = { static const struct pci_vpd_ops pci_vpd_f0_ops = {
.read = pci_vpd_f0_read, .read = pci_vpd_f0_read,
.write = pci_vpd_f0_write, .write = pci_vpd_f0_write,
.set_size = pci_vpd_f0_set_size,
}; };
int pci_vpd_init(struct pci_dev *dev) int pci_vpd_init(struct pci_dev *dev)
......
...@@ -97,6 +97,7 @@ static inline bool pci_has_subordinate(struct pci_dev *pci_dev) ...@@ -97,6 +97,7 @@ static inline bool pci_has_subordinate(struct pci_dev *pci_dev)
struct pci_vpd_ops { struct pci_vpd_ops {
ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
int (*set_size)(struct pci_dev *dev, size_t len);
}; };
struct pci_vpd { struct pci_vpd {
......
...@@ -1111,6 +1111,7 @@ void pci_unlock_rescan_remove(void); ...@@ -1111,6 +1111,7 @@ void pci_unlock_rescan_remove(void);
/* Vital product data routines */ /* Vital product data routines */
ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
int pci_set_vpd_size(struct pci_dev *dev, size_t len);
/* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx); resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment