aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHariprasad Shenai <hariprasad@chelsio.com>2016-04-15 14:00:11 -0400
committerBjorn Helgaas <bhelgaas@google.com>2016-04-15 14:00:11 -0400
commitcb92148b58a49455f3a7204eba3aee09a8b7683c (patch)
tree0da42a2722827d2b530fdf771d06af7b17cc0ec6
parentb2d7a9cd3ff8ec561348267c2ef7d47b2b91e801 (diff)
PCI: Add pci_set_vpd_size() to set VPD size
After 104daa71b396 ("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: 104daa71b396 ("PCI: Determine actual VPD size on first access") Tested-by: Steve Wise <swise@opengridcomputing.com> Signed-off-by: Casey Leedom <leedom@chelsio.com> Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r--drivers/pci/access.c42
-rw-r--r--drivers/pci/pci.h1
-rw-r--r--include/linux/pci.h1
3 files changed, 44 insertions, 0 deletions
diff --git a/drivers/pci/access.c b/drivers/pci/access.c
index 01b9d0a00abc..d11cdbb8fba3 100644
--- a/drivers/pci/access.c
+++ b/drivers/pci/access.c
@@ -275,6 +275,19 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void
275} 275}
276EXPORT_SYMBOL(pci_write_vpd); 276EXPORT_SYMBOL(pci_write_vpd);
277 277
278/**
279 * pci_set_vpd_size - Set size of Vital Product Data space
280 * @dev: pci device struct
281 * @len: size of vpd space
282 */
283int pci_set_vpd_size(struct pci_dev *dev, size_t len)
284{
285 if (!dev->vpd || !dev->vpd->ops)
286 return -ENODEV;
287 return dev->vpd->ops->set_size(dev, len);
288}
289EXPORT_SYMBOL(pci_set_vpd_size);
290
278#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) 291#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1)
279 292
280/** 293/**
@@ -498,9 +511,23 @@ out:
498 return ret ? ret : count; 511 return ret ? ret : count;
499} 512}
500 513
514static int pci_vpd_set_size(struct pci_dev *dev, size_t len)
515{
516 struct pci_vpd *vpd = dev->vpd;
517
518 if (len == 0 || len > PCI_VPD_MAX_SIZE)
519 return -EIO;
520
521 vpd->valid = 1;
522 vpd->len = len;
523
524 return 0;
525}
526
501static const struct pci_vpd_ops pci_vpd_ops = { 527static const struct pci_vpd_ops pci_vpd_ops = {
502 .read = pci_vpd_read, 528 .read = pci_vpd_read,
503 .write = pci_vpd_write, 529 .write = pci_vpd_write,
530 .set_size = pci_vpd_set_size,
504}; 531};
505 532
506static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, 533static 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 return ret; 560 return ret;
534} 561}
535 562
563static int pci_vpd_f0_set_size(struct pci_dev *dev, size_t len)
564{
565 struct pci_dev *tdev = pci_get_slot(dev->bus,
566 PCI_DEVFN(PCI_SLOT(dev->devfn), 0));
567 int ret;
568
569 if (!tdev)
570 return -ENODEV;
571
572 ret = pci_set_vpd_size(tdev, len);
573 pci_dev_put(tdev);
574 return ret;
575}
576
536static const struct pci_vpd_ops pci_vpd_f0_ops = { 577static const struct pci_vpd_ops pci_vpd_f0_ops = {
537 .read = pci_vpd_f0_read, 578 .read = pci_vpd_f0_read,
538 .write = pci_vpd_f0_write, 579 .write = pci_vpd_f0_write,
580 .set_size = pci_vpd_f0_set_size,
539}; 581};
540 582
541int pci_vpd_init(struct pci_dev *dev) 583int pci_vpd_init(struct pci_dev *dev)
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index d0fb93481573..a814bbb80fcb 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -97,6 +97,7 @@ static inline bool pci_has_subordinate(struct pci_dev *pci_dev)
97struct pci_vpd_ops { 97struct pci_vpd_ops {
98 ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); 98 ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
99 ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); 99 ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
100 int (*set_size)(struct pci_dev *dev, size_t len);
100}; 101};
101 102
102struct pci_vpd { 103struct pci_vpd {
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 004b8133417d..932ec74909c6 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1111,6 +1111,7 @@ void pci_unlock_rescan_remove(void);
1111/* Vital product data routines */ 1111/* Vital product data routines */
1112ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); 1112ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf);
1113ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); 1113ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf);
1114int pci_set_vpd_size(struct pci_dev *dev, size_t len);
1114 1115
1115/* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ 1116/* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */
1116resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx); 1117resource_size_t pcibios_retrieve_fw_addr(struct pci_dev *dev, int idx);