From 0a3d00b33b4ed873cbead8c241bd58b456d92d36 Mon Sep 17 00:00:00 2001 From: Kelly Zytaruk Date: Tue, 9 Feb 2016 13:08:58 -0500 Subject: PCI: Support SR-IOV on any function type Previously, we only supported SR-IOV on PCI Express Endpoints and Root Complex Integrated Endpoints. This restriction has been present since d1b054da8f59 ("PCI: initialize and release SR-IOV capability") added SR-IOV support, but the spec does not require it. In fact, the SR-IOV spec r1.1, sec 3.3, says the SR-IOV extended capability may be present for any Type 0 function. Remove the function type test, so we can support SR-IOV on any function. Some AMD GPUs have display outputs, use the VGA class code, are Legacy Endpoints, and support SR-IOV. This change allows Linux to enable SR-IOV on these devices. [bhelgaas: changelog] Link: https://bugzilla.kernel.org/show_bug.cgi?id=112221 Signed-off-by: Kelly Zytaruk Signed-off-by: Bjorn Helgaas --- drivers/pci/iov.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 31f31d460fc9..fe4bd0aa91a6 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -387,10 +387,6 @@ static int sriov_init(struct pci_dev *dev, int pos) struct resource *res; struct pci_dev *pdev; - if (pci_pcie_type(dev) != PCI_EXP_TYPE_RC_END && - pci_pcie_type(dev) != PCI_EXP_TYPE_ENDPOINT) - return -ENODEV; - pci_read_config_word(dev, pos + PCI_SRIOV_CTRL, &ctrl); if (ctrl & PCI_SRIOV_CTRL_VFE) { pci_write_config_word(dev, pos + PCI_SRIOV_CTRL, 0); -- cgit v1.2.3 From 6ca7227b3e24e4ba445464779a66558a8b9e0617 Mon Sep 17 00:00:00 2001 From: Naga Venkata Sai Indubhaskar Jupudi Date: Tue, 9 Feb 2016 14:10:16 -0800 Subject: PCI: Fix broken URL for Dell biosdevname Dell developed a way to consistently name devices, and their last proposal was accepted under the name biosdevname. Fix a broken URL to biosdevname documentation. Signed-off-by: Naga Venkata Sai Indubhaskar Jupudi Signed-off-by: Bjorn Helgaas --- drivers/pci/pci-label.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-label.c b/drivers/pci/pci-label.c index 0ae74d96ed85..51357377efbc 100644 --- a/drivers/pci/pci-label.c +++ b/drivers/pci/pci-label.c @@ -16,7 +16,7 @@ * the instance number and string from the type 41 record and exports * it to sysfs. * - * Please see http://linux.dell.com/wiki/index.php/Oss/libnetdevname for more + * Please see http://linux.dell.com/files/biosdevname/ for more * information. */ -- cgit v1.2.3 From f52e5629f66d5fd79ae8b8145d76ae0ddb47e142 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 15 Feb 2016 09:42:00 +0100 Subject: PCI: Allow access to VPD attributes with size 0 It is not always possible to determine the actual size of the VPD data, so allow access to them if the size is set to '0'. Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Hannes Reinecke Signed-off-by: Bjorn Helgaas Cc: Alexander Duyck --- drivers/pci/pci-sysfs.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 95d9e7bd933b..a730f54d1247 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -769,10 +769,12 @@ static ssize_t read_vpd_attr(struct file *filp, struct kobject *kobj, { struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; + if (bin_attr->size > 0) { + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + } return pci_read_vpd(dev, off, count, buf); } @@ -783,10 +785,12 @@ static ssize_t write_vpd_attr(struct file *filp, struct kobject *kobj, { struct pci_dev *dev = to_pci_dev(kobj_to_dev(kobj)); - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; + if (bin_attr->size > 0) { + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + } return pci_write_vpd(dev, off, count, buf); } -- cgit v1.2.3 From c5563887a96912f84efe5c1322bfbc4f9a30e882 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 14:04:07 -0600 Subject: PCI: Use bitfield instead of bool for struct pci_vpd_pci22.busy Make struct pci_vpd_pci22.busy a 1-bit field instead of a bool. We intend to add another flag, and two bitfields are cheaper than two bools. Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 8c05b5ceeaec..a7f00699d0cb 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -283,8 +283,8 @@ struct pci_vpd_pci22 { struct pci_vpd base; struct mutex lock; u16 flag; - bool busy; u8 cap; + u8 busy:1; }; /* @@ -313,7 +313,7 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev) return ret; if ((status & PCI_VPD_ADDR_F) == vpd->flag) { - vpd->busy = false; + vpd->busy = 0; return 0; } @@ -355,7 +355,7 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, pos & ~3); if (ret < 0) break; - vpd->busy = true; + vpd->busy = 1; vpd->flag = PCI_VPD_ADDR_F; ret = pci_vpd_pci22_wait(dev); if (ret < 0) @@ -415,7 +415,7 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count if (ret < 0) break; - vpd->busy = true; + vpd->busy = 1; vpd->flag = 0; ret = pci_vpd_pci22_wait(dev); if (ret < 0) @@ -495,7 +495,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) vpd->base.ops = &pci_vpd_pci22_ops; mutex_init(&vpd->lock); vpd->cap = cap; - vpd->busy = false; + vpd->busy = 0; dev->vpd = &vpd->base; return 0; } -- cgit v1.2.3 From 104daa71b39614343929e1982170d5fcb0569bb5 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Mon, 15 Feb 2016 09:42:01 +0100 Subject: PCI: Determine actual VPD size on first access PCI-2.2 VPD entries have a maximum size of 32k, but might actually be smaller than that. To figure out the actual size one has to read the VPD area until the 'end marker' is reached. Per spec, reading outside of the VPD space is "not allowed." In practice, it may cause simple read errors or even crash the card. To make matters worse not every PCI card implements this properly, leaving us with no 'end' marker or even completely invalid data. Try to determine the size of the VPD data when it's first accessed. If no valid data can be read an I/O error will be returned when reading or writing the sysfs attribute. As the amount of VPD data is unknown initially the size of the sysfs attribute will always be set to '0'. [bhelgaas: changelog, use 0/1 (not false/true) for bitfield, tweak pci_vpd_pci22_read() error checking] Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Hannes Reinecke Signed-off-by: Bjorn Helgaas Cc: Alexander Duyck --- drivers/pci/access.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++-- drivers/pci/pci-sysfs.c | 2 +- 2 files changed, 86 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index a7f00699d0cb..4850f0612268 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -285,8 +285,63 @@ struct pci_vpd_pci22 { u16 flag; u8 cap; u8 busy:1; + u8 valid:1; }; +/** + * pci_vpd_size - determine actual size of Vital Product Data + * @dev: pci device struct + * @old_size: current assumed size, also maximum allowed size + */ +static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size) +{ + size_t off = 0; + unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ + + while (off < old_size && + pci_read_vpd(dev, off, 1, header) == 1) { + unsigned char tag; + + if (header[0] & PCI_VPD_LRDT) { + /* Large Resource Data Type Tag */ + tag = pci_vpd_lrdt_tag(header); + /* Only read length from known tag items */ + if ((tag == PCI_VPD_LTIN_ID_STRING) || + (tag == PCI_VPD_LTIN_RO_DATA) || + (tag == PCI_VPD_LTIN_RW_DATA)) { + if (pci_read_vpd(dev, off+1, 2, + &header[1]) != 2) { + dev_warn(&dev->dev, + "invalid large VPD tag %02x size at offset %zu", + tag, off + 1); + return 0; + } + off += PCI_VPD_LRDT_TAG_SIZE + + pci_vpd_lrdt_size(header); + } + } else { + /* Short Resource Data Type Tag */ + off += PCI_VPD_SRDT_TAG_SIZE + + pci_vpd_srdt_size(header); + tag = pci_vpd_srdt_tag(header); + } + + if (tag == PCI_VPD_STIN_END) /* End tag descriptor */ + return off; + + if ((tag != PCI_VPD_LTIN_ID_STRING) && + (tag != PCI_VPD_LTIN_RO_DATA) && + (tag != PCI_VPD_LTIN_RW_DATA)) { + dev_warn(&dev->dev, + "invalid %s VPD tag %02x at offset %zu", + (header[0] & PCI_VPD_LRDT) ? "large" : "short", + tag, off); + return 0; + } + } + return 0; +} + /* * Wait for last operation to complete. * This code has to spin since there is no other notification from the PCI @@ -337,9 +392,25 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, loff_t end = pos + count; u8 *buf = arg; - if (pos < 0 || pos > vpd->base.len || end > vpd->base.len) + if (pos < 0) return -EINVAL; + if (!vpd->valid) { + vpd->valid = 1; + vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len); + } + + if (vpd->base.len == 0) + return -EIO; + + if (pos >= vpd->base.len) + return 0; + + if (end > vpd->base.len) { + end = vpd->base.len; + count = end - pos; + } + if (mutex_lock_killable(&vpd->lock)) return -EINTR; @@ -389,7 +460,18 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count loff_t end = pos + count; int ret = 0; - if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len) + if (pos < 0 || (pos & 3) || (count & 3)) + return -EINVAL; + + if (!vpd->valid) { + vpd->valid = 1; + vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len); + } + + if (vpd->base.len == 0) + return -EIO; + + if (end > vpd->base.len) return -EINVAL; if (mutex_lock_killable(&vpd->lock)) @@ -496,6 +578,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) mutex_init(&vpd->lock); vpd->cap = cap; vpd->busy = 0; + vpd->valid = 0; dev->vpd = &vpd->base; return 0; } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index a730f54d1247..ed39c093aa15 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1323,7 +1323,7 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) return -ENOMEM; sysfs_bin_attr_init(attr); - attr->size = dev->vpd->len; + attr->size = 0; attr->attr.name = "vpd"; attr->attr.mode = S_IRUSR | S_IWUSR; attr->read = read_vpd_attr; -- cgit v1.2.3 From fc0a407e9eb8667f93820298a06a85050c42354e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 13:57:50 -0600 Subject: PCI: Move pci_read_vpd() and pci_write_vpd() close to other VPD code pci_read_vpd() and pci_write_vpd() were stranded in the middle of config accessor functions. Move them close to the other VPD code in the file. No functional change. Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 62 +++++++++++++++++++++++++--------------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 4850f0612268..4c4c73420fb2 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -174,38 +174,6 @@ struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops) } EXPORT_SYMBOL(pci_bus_set_ops); -/** - * pci_read_vpd - Read one entry from Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to read - * @buf: pointer to where to store result - * - */ -ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->read(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_read_vpd); - -/** - * pci_write_vpd - Write entry to Vital Product Data - * @dev: pci device struct - * @pos: offset in vpd space - * @count: number of bytes to write - * @buf: buffer containing write data - * - */ -ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) -{ - if (!dev->vpd || !dev->vpd->ops) - return -ENODEV; - return dev->vpd->ops->write(dev, pos, count, buf); -} -EXPORT_SYMBOL(pci_write_vpd); - /* * The following routines are to prevent the user from accessing PCI config * space when it's unsafe to do so. Some devices require this during BIST and @@ -277,6 +245,36 @@ PCI_USER_WRITE_CONFIG(dword, u32) /* VPD access through PCI 2.2+ VPD capability */ +/** + * pci_read_vpd - Read one entry from Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @buf: pointer to where to store result + */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->read(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_read_vpd); + +/** + * pci_write_vpd - Write entry to Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to write + * @buf: buffer containing write data + */ +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->write(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_write_vpd); + #define PCI_VPD_PCI22_SIZE (PCI_VPD_ADDR_MASK + 1) struct pci_vpd_pci22 { -- cgit v1.2.3 From 64379079a9e37ab9e1c755921d7ff1c4fa3ac486 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 13:58:06 -0600 Subject: PCI: Move pci_vpd_release() from header file to pci/access.c Move pci_vpd_release() so it's next to the other VPD functions. This puts it next to pci_vpd_pci22_init(), which allocates the space freed by pci_vpd_release(). Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 6 ++++++ drivers/pci/pci.h | 6 +----- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 4c4c73420fb2..ca42a33bbb00 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -581,6 +581,12 @@ int pci_vpd_pci22_init(struct pci_dev *dev) return 0; } +void pci_vpd_release(struct pci_dev *dev) +{ + if (dev->vpd) + dev->vpd->ops->release(dev); +} + /** * pci_cfg_access_lock - Lock PCI config reads/writes * @dev: pci device struct diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9a1660f592ef..52e86b0d56d2 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -107,11 +107,7 @@ struct pci_vpd { }; int pci_vpd_pci22_init(struct pci_dev *dev); -static inline void pci_vpd_release(struct pci_dev *dev) -{ - if (dev->vpd) - dev->vpd->ops->release(dev); -} +void pci_vpd_release(struct pci_dev *dev); /* PCI /proc functions */ #ifdef CONFIG_PROC_FS -- cgit v1.2.3 From da006847231b2b2ce443b29f1d3df6df7415030d Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 13:58:20 -0600 Subject: PCI: Remove struct pci_vpd_ops.release function pointer The struct pci_vpd_ops.release function pointer is always pci_vpd_pci22_release(), so there's no need for the flexibility of a function pointer. Inline the pci_vpd_pci22_release() body into pci_vpd_release() and remove pci_vpd_pci22_release() and the struct pci_vpd_ops.release function pointer. Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 9 +-------- drivers/pci/pci.h | 1 - 2 files changed, 1 insertion(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index ca42a33bbb00..68ed22a4b116 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -508,15 +508,9 @@ out: return ret ? ret : count; } -static void pci_vpd_pci22_release(struct pci_dev *dev) -{ - kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); -} - static const struct pci_vpd_ops pci_vpd_pci22_ops = { .read = pci_vpd_pci22_read, .write = pci_vpd_pci22_write, - .release = pci_vpd_pci22_release, }; static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, @@ -552,7 +546,6 @@ static ssize_t pci_vpd_f0_write(struct pci_dev *dev, loff_t pos, size_t count, static const struct pci_vpd_ops pci_vpd_f0_ops = { .read = pci_vpd_f0_read, .write = pci_vpd_f0_write, - .release = pci_vpd_pci22_release, }; int pci_vpd_pci22_init(struct pci_dev *dev) @@ -584,7 +577,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) void pci_vpd_release(struct pci_dev *dev) { if (dev->vpd) - dev->vpd->ops->release(dev); + kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 52e86b0d56d2..b3e9daac67d4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -97,7 +97,6 @@ static inline bool pci_has_subordinate(struct pci_dev *pci_dev) struct pci_vpd_ops { 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); - void (*release)(struct pci_dev *dev); }; struct pci_vpd { -- cgit v1.2.3 From f1cd93f9aabe3b8dcac53de1c88279dbd8e529c3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 13:58:37 -0600 Subject: PCI: Rename VPD symbols to remove unnecessary "pci22" There's only one kind of VPD, so we don't need to qualify it as "the version described by PCI spec rev 2.2." Rename the following symbols to remove unnecessary "pci22": PCI_VPD_PCI22_SIZE -> PCI_VPD_MAX_SIZE pci_vpd_pci22_size() -> pci_vpd_size() pci_vpd_pci22_wait() -> pci_vpd_wait() pci_vpd_pci22_read() -> pci_vpd_read() pci_vpd_pci22_write() -> pci_vpd_write() pci_vpd_pci22_ops -> pci_vpd_ops pci_vpd_pci22_init() -> pci_vpd_init() Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 38 +++++++++++++++++++------------------- drivers/pci/pci.h | 2 +- drivers/pci/probe.c | 2 +- 3 files changed, 21 insertions(+), 21 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 68ed22a4b116..ee205deb95b4 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -275,7 +275,7 @@ ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void } EXPORT_SYMBOL(pci_write_vpd); -#define PCI_VPD_PCI22_SIZE (PCI_VPD_ADDR_MASK + 1) +#define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) struct pci_vpd_pci22 { struct pci_vpd base; @@ -291,7 +291,7 @@ struct pci_vpd_pci22 { * @dev: pci device struct * @old_size: current assumed size, also maximum allowed size */ -static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size) +static size_t pci_vpd_size(struct pci_dev *dev, size_t old_size) { size_t off = 0; unsigned char header[1+2]; /* 1 byte tag, 2 bytes length */ @@ -348,7 +348,7 @@ static size_t pci_vpd_pci22_size(struct pci_dev *dev, size_t old_size) * * Returns 0 on success, negative values indicate error. */ -static int pci_vpd_pci22_wait(struct pci_dev *dev) +static int pci_vpd_wait(struct pci_dev *dev) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); @@ -381,8 +381,8 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev) } } -static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, - void *arg) +static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); @@ -395,7 +395,7 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, if (!vpd->valid) { vpd->valid = 1; - vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len); + vpd->base.len = pci_vpd_size(dev, vpd->base.len); } if (vpd->base.len == 0) @@ -412,7 +412,7 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, if (mutex_lock_killable(&vpd->lock)) return -EINTR; - ret = pci_vpd_pci22_wait(dev); + ret = pci_vpd_wait(dev); if (ret < 0) goto out; @@ -426,7 +426,7 @@ static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, break; vpd->busy = 1; vpd->flag = PCI_VPD_ADDR_F; - ret = pci_vpd_pci22_wait(dev); + ret = pci_vpd_wait(dev); if (ret < 0) break; @@ -449,8 +449,8 @@ out: return ret ? ret : count; } -static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count, - const void *arg) +static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); @@ -463,7 +463,7 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count if (!vpd->valid) { vpd->valid = 1; - vpd->base.len = pci_vpd_pci22_size(dev, vpd->base.len); + vpd->base.len = pci_vpd_size(dev, vpd->base.len); } if (vpd->base.len == 0) @@ -475,7 +475,7 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count if (mutex_lock_killable(&vpd->lock)) return -EINTR; - ret = pci_vpd_pci22_wait(dev); + ret = pci_vpd_wait(dev); if (ret < 0) goto out; @@ -497,7 +497,7 @@ static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count vpd->busy = 1; vpd->flag = 0; - ret = pci_vpd_pci22_wait(dev); + ret = pci_vpd_wait(dev); if (ret < 0) break; @@ -508,9 +508,9 @@ out: return ret ? ret : count; } -static const struct pci_vpd_ops pci_vpd_pci22_ops = { - .read = pci_vpd_pci22_read, - .write = pci_vpd_pci22_write, +static const struct pci_vpd_ops pci_vpd_ops = { + .read = pci_vpd_read, + .write = pci_vpd_write, }; static ssize_t pci_vpd_f0_read(struct pci_dev *dev, loff_t pos, size_t count, @@ -548,7 +548,7 @@ static const struct pci_vpd_ops pci_vpd_f0_ops = { .write = pci_vpd_f0_write, }; -int pci_vpd_pci22_init(struct pci_dev *dev) +int pci_vpd_init(struct pci_dev *dev) { struct pci_vpd_pci22 *vpd; u8 cap; @@ -561,11 +561,11 @@ int pci_vpd_pci22_init(struct pci_dev *dev) if (!vpd) return -ENOMEM; - vpd->base.len = PCI_VPD_PCI22_SIZE; + vpd->base.len = PCI_VPD_MAX_SIZE; if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) vpd->base.ops = &pci_vpd_f0_ops; else - vpd->base.ops = &pci_vpd_pci22_ops; + vpd->base.ops = &pci_vpd_ops; mutex_init(&vpd->lock); vpd->cap = cap; vpd->busy = 0; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index b3e9daac67d4..61917038dbf4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -105,7 +105,7 @@ struct pci_vpd { struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; -int pci_vpd_pci22_init(struct pci_dev *dev); +int pci_vpd_init(struct pci_dev *dev); void pci_vpd_release(struct pci_dev *dev); /* PCI /proc functions */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 6d7ab9bb0d5a..39b017447b70 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1608,7 +1608,7 @@ static void pci_init_capabilities(struct pci_dev *dev) pci_pm_init(dev); /* Vital Product Data */ - pci_vpd_pci22_init(dev); + pci_vpd_init(dev); /* Alternative Routing-ID Forwarding */ pci_configure_ari(dev); -- cgit v1.2.3 From 408641e93aa5283e586fefd4dc72e67c92aae075 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 14:09:52 -0600 Subject: PCI: Fold struct pci_vpd_pci22 into struct pci_vpd We only support one flavor of VPD, so there's no need to complicate things by having a "generic" struct pci_vpd and a more specific struct pci_vpd_pci22. Fold struct pci_vpd_pci22 directly into struct pci_vpd. [bhelgaas: remove NULL check before kfree of dev->vpd (per kfreeaddr.cocci)] Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 47 +++++++++++++++++------------------------------ drivers/pci/pci.h | 7 ++++++- 2 files changed, 23 insertions(+), 31 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index ee205deb95b4..8449d6b58178 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -277,15 +277,6 @@ EXPORT_SYMBOL(pci_write_vpd); #define PCI_VPD_MAX_SIZE (PCI_VPD_ADDR_MASK + 1) -struct pci_vpd_pci22 { - struct pci_vpd base; - struct mutex lock; - u16 flag; - u8 cap; - u8 busy:1; - u8 valid:1; -}; - /** * pci_vpd_size - determine actual size of Vital Product Data * @dev: pci device struct @@ -350,8 +341,7 @@ 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_pci22 *vpd = - container_of(dev->vpd, struct pci_vpd_pci22, base); + struct pci_vpd *vpd = dev->vpd; unsigned long timeout = jiffies + HZ/20 + 2; u16 status; int ret; @@ -384,8 +374,7 @@ static int pci_vpd_wait(struct pci_dev *dev) static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, void *arg) { - struct pci_vpd_pci22 *vpd = - container_of(dev->vpd, struct pci_vpd_pci22, base); + struct pci_vpd *vpd = dev->vpd; int ret; loff_t end = pos + count; u8 *buf = arg; @@ -395,17 +384,17 @@ static ssize_t pci_vpd_read(struct pci_dev *dev, loff_t pos, size_t count, if (!vpd->valid) { vpd->valid = 1; - vpd->base.len = pci_vpd_size(dev, vpd->base.len); + vpd->len = pci_vpd_size(dev, vpd->len); } - if (vpd->base.len == 0) + if (vpd->len == 0) return -EIO; - if (pos >= vpd->base.len) + if (pos > vpd->len) return 0; - if (end > vpd->base.len) { - end = vpd->base.len; + if (end > vpd->len) { + end = vpd->len; count = end - pos; } @@ -452,8 +441,7 @@ out: static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, const void *arg) { - struct pci_vpd_pci22 *vpd = - container_of(dev->vpd, struct pci_vpd_pci22, base); + struct pci_vpd *vpd = dev->vpd; const u8 *buf = arg; loff_t end = pos + count; int ret = 0; @@ -463,13 +451,13 @@ static ssize_t pci_vpd_write(struct pci_dev *dev, loff_t pos, size_t count, if (!vpd->valid) { vpd->valid = 1; - vpd->base.len = pci_vpd_size(dev, vpd->base.len); + vpd->len = pci_vpd_size(dev, vpd->len); } - if (vpd->base.len == 0) + if (vpd->len == 0) return -EIO; - if (end > vpd->base.len) + if (end > vpd->len) return -EINVAL; if (mutex_lock_killable(&vpd->lock)) @@ -550,7 +538,7 @@ static const struct pci_vpd_ops pci_vpd_f0_ops = { int pci_vpd_init(struct pci_dev *dev) { - struct pci_vpd_pci22 *vpd; + struct pci_vpd *vpd; u8 cap; cap = pci_find_capability(dev, PCI_CAP_ID_VPD); @@ -561,23 +549,22 @@ int pci_vpd_init(struct pci_dev *dev) if (!vpd) return -ENOMEM; - vpd->base.len = PCI_VPD_MAX_SIZE; + vpd->len = PCI_VPD_MAX_SIZE; if (dev->dev_flags & PCI_DEV_FLAGS_VPD_REF_F0) - vpd->base.ops = &pci_vpd_f0_ops; + vpd->ops = &pci_vpd_f0_ops; else - vpd->base.ops = &pci_vpd_ops; + vpd->ops = &pci_vpd_ops; mutex_init(&vpd->lock); vpd->cap = cap; vpd->busy = 0; vpd->valid = 0; - dev->vpd = &vpd->base; + dev->vpd = vpd; return 0; } void pci_vpd_release(struct pci_dev *dev) { - if (dev->vpd) - kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); + kfree(dev->vpd); } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 61917038dbf4..d0fb93481573 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -100,9 +100,14 @@ struct pci_vpd_ops { }; struct pci_vpd { - unsigned int len; const struct pci_vpd_ops *ops; struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ + struct mutex lock; + unsigned int len; + u16 flag; + u8 cap; + u8 busy:1; + u8 valid:1; }; int pci_vpd_init(struct pci_dev *dev); -- cgit v1.2.3 From fe537670eab767157eecc50538bd28e8d9b0ce9f Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 7 Mar 2016 11:39:16 -0600 Subject: PCI: Consolidate PCI DMA constants and interfaces in linux/pci-dma-compat.h Christoph added a generic include/linux/pci-dma-compat.h, so now there's one place with most of the PCI DMA interfaces. Move more PCI DMA-related things there: - The PCI_DMA_* direction constants from linux/pci.h - The pci_set_dma_max_seg_size() and pci_set_dma_seg_boundary() CONFIG_PCI implementations from drivers/pci/pci.c - The pci_set_dma_max_seg_size() and pci_set_dma_seg_boundary() !CONFIG_PCI stubs from linux/pci.h - The pci_set_dma_mask() and pci_set_consistent_dma_mask() !CONFIG_PCI stubs from linux/pci.h Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 64c0a1215f84..0a9c8db51c08 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3385,18 +3385,6 @@ bool pci_check_and_unmask_intx(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_check_and_unmask_intx); -int pci_set_dma_max_seg_size(struct pci_dev *dev, unsigned int size) -{ - return dma_set_max_seg_size(&dev->dev, size); -} -EXPORT_SYMBOL(pci_set_dma_max_seg_size); - -int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) -{ - return dma_set_seg_boundary(&dev->dev, mask); -} -EXPORT_SYMBOL(pci_set_dma_seg_boundary); - /** * pci_wait_for_pending_transaction - waits for pending transaction * @dev: the PCI device to operate on -- cgit v1.2.3 From 5f8fc43217a01c98be88aa53794b4d1333a07a5a Mon Sep 17 00:00:00 2001 From: Bogicevic Sasa Date: Wed, 3 Feb 2016 13:24:22 -0800 Subject: PCI: Include pci/pcie/Kconfig directly from pci/Kconfig Include pci/pcie/Kconfig directly from pci/Kconfig, so arches don't have to source both pci/Kconfig and pci/pcie/Kconfig. Note that this effectively adds pci/pcie/Kconfig to the following arches, because they already sourced drivers/pci/Kconfig but they previously did not source drivers/pci/pcie/Kconfig: alpha avr32 blackfin frv m32r m68k microblaze mn10300 parisc sparc unicore32 xtensa [bhelgaas: changelog, source pci/pcie/Kconfig at top of pci/Kconfig, whitespace] Signed-off-by: Sasa Bogicevic Signed-off-by: Bjorn Helgaas --- drivers/pci/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 73de4efcbe6e..04e00923d178 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -1,6 +1,9 @@ # # PCI configuration # + +source "drivers/pci/pcie/Kconfig" + config PCI_BUS_ADDR_T_64BIT def_bool y if (ARCH_DMA_ADDR_T_64BIT || 64BIT) depends on PCI -- cgit v1.2.3 From e7e127e3c767094aca96ee976729dc5f756666df Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 8 Mar 2016 14:57:21 -0600 Subject: PCI: Include pci/hotplug Kconfig directly from pci/Kconfig Include pci/hotplug/Kconfig directly from pci/Kconfig, so arches don't have to source both pci/Kconfig and pci/hotplug/Kconfig. Note that this effectively adds pci/hotplug/Kconfig to the following arches, because they already sourced drivers/pci/Kconfig but they previously did not source drivers/pci/hotplug/Kconfig: alpha arm avr32 frv m68k microblaze mn10300 sparc unicore32 Inspired-by-patch-from: Bogicevic Sasa Signed-off-by: Bjorn Helgaas --- drivers/pci/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 04e00923d178..616165d72a17 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -121,4 +121,5 @@ config PCI_LABEL def_bool y if (DMI || ACPI) select NLS +source "drivers/pci/hotplug/Kconfig" source "drivers/pci/host/Kconfig" -- cgit v1.2.3 From 20ac75e563d09f56fb00caa25a3afdb88573472d Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 18 Feb 2016 13:50:45 +0100 Subject: PCI/AER: Fix aer_inject error codes EPERM means "Operation not permitted", which doesn't reflect the lack of support for AER. EPROTONOSUPPORT (Protocol not supported) is a better choice of error code if the device or its root port lack support for AER. Likewise, EINVAL means "Invalid argument", which is not suitable for cases where the AER error device is missing or unusable. ENODEV and EPROTONOSUPPORT, respectively, fit better. Suggested-by: Borislav Petkov Signed-off-by: Jean Delvare Signed-off-by: Bjorn Helgaas CC: Borislav Petkov CC: Prarit Bhargava --- drivers/pci/pcie/aer/aer_inject.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index e2760a39a98a..90bc3a6848a1 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -363,7 +363,7 @@ static int aer_inject(struct aer_error_inj *einj) pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos_cap_err) { - ret = -EPERM; + ret = -EPROTONOSUPPORT; goto out_put; } pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); @@ -373,7 +373,7 @@ static int aer_inject(struct aer_error_inj *einj) rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); if (!rp_pos_cap_err) { - ret = -EPERM; + ret = -EPROTONOSUPPORT; goto out_put; } @@ -481,12 +481,12 @@ static int aer_inject(struct aer_error_inj *einj) if (find_aer_device(rpdev, &edev)) { if (!get_service_data(edev)) { printk(KERN_WARNING "AER service is not initialized\n"); - ret = -EINVAL; + ret = -EPROTONOSUPPORT; goto out_put; } aer_irq(-1, edev); } else - ret = -EINVAL; + ret = -ENODEV; out_put: kfree(err_alloc); kfree(rperr_alloc); -- cgit v1.2.3 From 3bc11851413fcf58f6a2d8f69346609c9db339ed Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 18 Feb 2016 13:52:01 +0100 Subject: PCI/AER: Use dev_warn() in aer_inject dev_warn() is better than printk(LOG_WARNING...) as it records which device the message relates to. Also add a prefix "aer_inject:" to help differentiate real errors from injected errors. Signed-off-by: Jean Delvare Signed-off-by: Bjorn Helgaas CC: Borislav Petkov --- drivers/pci/pcie/aer/aer_inject.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 90bc3a6848a1..3e9fdd5f9330 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "aerdrv.h" /* Override the existing corrected and uncorrected error masks */ @@ -420,14 +421,16 @@ static int aer_inject(struct aer_error_inj *einj) if (!aer_mask_override && einj->cor_status && !(einj->cor_status & ~cor_mask)) { ret = -EINVAL; - printk(KERN_WARNING "The correctable error(s) is masked by device\n"); + dev_warn(&dev->dev, + "aer_inject: The correctable error(s) is masked by device\n"); spin_unlock_irqrestore(&inject_lock, flags); goto out_put; } if (!aer_mask_override && einj->uncor_status && !(einj->uncor_status & ~uncor_mask)) { ret = -EINVAL; - printk(KERN_WARNING "The uncorrectable error(s) is masked by device\n"); + dev_warn(&dev->dev, + "aer_inject: The uncorrectable error(s) is masked by device\n"); spin_unlock_irqrestore(&inject_lock, flags); goto out_put; } @@ -480,7 +483,8 @@ static int aer_inject(struct aer_error_inj *einj) if (find_aer_device(rpdev, &edev)) { if (!get_service_data(edev)) { - printk(KERN_WARNING "AER service is not initialized\n"); + dev_warn(&edev->device, + "aer_inject: AER service is not initialized\n"); ret = -EPROTONOSUPPORT; goto out_put; } -- cgit v1.2.3 From 96b45ea5dc19425ca04b53ab2b56af9e2bcc28c6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 18 Feb 2016 13:52:46 +0100 Subject: PCI/AER: Log actual error causes in aer_inject The aer_inject driver is very quiet. In most cases, it merely returns an error code to user-space, leaving the user with little clue about the actual reason for the failure. So, log error messages for 4 of the most frequent causes of failure: * Can't find the root port of the specified device. * Device doesn't support AER. * Root port doesn't support AER. * AER device not found. This gives the user a chance to understand why aer-inject failed. Based on a preliminary patch by Thomas Renninger. Signed-off-by: Jean Delvare Signed-off-by: Bjorn Helgaas CC: Borislav Petkov CC: Thomas Renninger --- drivers/pci/pcie/aer/aer_inject.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 3e9fdd5f9330..44ed4ded3846 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -358,12 +358,14 @@ static int aer_inject(struct aer_error_inj *einj) return -ENODEV; rpdev = pcie_find_root_port(dev); if (!rpdev) { + dev_err(&dev->dev, "aer_inject: Root port not found\n"); ret = -ENODEV; goto out_put; } pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos_cap_err) { + dev_err(&dev->dev, "aer_inject: Device doesn't support AER\n"); ret = -EPROTONOSUPPORT; goto out_put; } @@ -374,6 +376,8 @@ static int aer_inject(struct aer_error_inj *einj) rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); if (!rp_pos_cap_err) { + dev_err(&rpdev->dev, + "aer_inject: Root port doesn't support AER\n"); ret = -EPROTONOSUPPORT; goto out_put; } @@ -489,8 +493,10 @@ static int aer_inject(struct aer_error_inj *einj) goto out_put; } aer_irq(-1, edev); - } else + } else { + dev_err(&rpdev->dev, "aer_inject: AER device not found\n"); ret = -ENODEV; + } out_put: kfree(err_alloc); kfree(rperr_alloc); -- cgit v1.2.3 From 8e47e15e917fca36945c2f03de28cfda0c8499f0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 18 Feb 2016 13:54:02 +0100 Subject: PCI/AER: Log aer_inject error injections Log successful error injections so that injected errors can be differentiated from real errors. Suggested-by: Bjorn Helgaas Signed-off-by: Jean Delvare Signed-off-by: Bjorn Helgaas CC: Borislav Petkov --- drivers/pci/pcie/aer/aer_inject.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index 44ed4ded3846..db553dc22c8e 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -492,6 +492,9 @@ static int aer_inject(struct aer_error_inj *einj) ret = -EPROTONOSUPPORT; goto out_put; } + dev_info(&edev->device, + "aer_inject: Injecting errors %08x/%08x into device %s\n", + einj->cor_status, einj->uncor_status, pci_name(dev)); aer_irq(-1, edev); } else { dev_err(&rpdev->dev, "aer_inject: AER device not found\n"); -- cgit v1.2.3 From c521b014cd51da94ae16c3decf1c407c3e0518f6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 22 Feb 2016 14:58:18 -0600 Subject: PCI: Sleep rather than busy-wait for VPD access completion 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 Tested-by: Babu Moger Signed-off-by: Bjorn Helgaas Reviewed-by: Hannes Reinecke --- drivers/pci/access.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 8449d6b58178..01b9d0a00abc 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -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, -- cgit v1.2.3 From 7c20078a8197389eead62399419fdc4f8ac4a8a3 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Mon, 15 Feb 2016 09:42:02 +0100 Subject: PCI: Prevent VPD access for buggy devices On some devices, reading or writing VPD causes a system panic. This can be easily reproduced by running "lspci -vvv" or "cat /sys/bus/devices/XX../vpd". Blacklist these devices so we don't access VPD data at all. [bhelgaas: changelog, comment, drop pci/access.c changes] Link: https://bugzilla.kernel.org/show_bug.cgi?id=110681 Tested-by: Shane Seymour Tested-by: Babu Moger Signed-off-by: Babu Moger Signed-off-by: Hannes Reinecke Signed-off-by: Bjorn Helgaas Cc: Alexander Duyck --- drivers/pci/quirks.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 0575a1e026b4..626c3b22c053 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2134,6 +2134,35 @@ static void quirk_via_cx700_pci_parking_caching(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0x324e, quirk_via_cx700_pci_parking_caching); +/* + * If a device follows the VPD format spec, the PCI core will not read or + * write past the VPD End Tag. But some vendors do not follow the VPD + * format spec, so we can't tell how much data is safe to access. Devices + * may behave unpredictably if we access too much. Blacklist these devices + * so we don't touch VPD at all. + */ +static void quirk_blacklist_vpd(struct pci_dev *dev) +{ + if (dev->vpd) { + dev->vpd->len = 0; + dev_warn(&dev->dev, FW_BUG "VPD access disabled\n"); + } +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0060, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x007c, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0413, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0078, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0079, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0073, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x0071, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005b, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x002f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005d, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LSI_LOGIC, 0x005f, quirk_blacklist_vpd); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATTANSIC, PCI_ANY_ID, + quirk_blacklist_vpd); + /* * For Broadcom 5706, 5708, 5709 rev. A nics, any read beyond the * VPD end tag will hang the device. This problem was initially -- cgit v1.2.3 From 5adecf817dd630529d6565a242141db2df3239f7 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Mon, 22 Feb 2016 13:05:48 -0700 Subject: PCI: Wait for up to 1000ms after FLR reset Some devices take longer than the spec indicates to return from FLR reset, a notable case of this is Intel integrated graphics (IGD), which can often take an additional 300ms powering down an attached LCD panel as part of the FLR. Allow devices up to 1000ms, testing every 100ms whether the second dword of config space is read as -1. Signed-off-by: Alex Williamson Signed-off-by: Bjorn Helgaas --- drivers/pci/pci.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 602eb4223510..123408e5a1d6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3414,6 +3414,29 @@ int pci_wait_for_pending_transaction(struct pci_dev *dev) } EXPORT_SYMBOL(pci_wait_for_pending_transaction); +/* + * We should only need to wait 100ms after FLR, but some devices take longer. + * Wait for up to 1000ms for config space to return something other than -1. + * Intel IGD requires this when an LCD panel is attached. We read the 2nd + * dword because VFs don't implement the 1st dword. + */ +static void pci_flr_wait(struct pci_dev *dev) +{ + int i = 0; + u32 id; + + do { + msleep(100); + pci_read_config_dword(dev, PCI_COMMAND, &id); + } while (i++ < 10 && id == ~0); + + if (id == ~0) + dev_warn(&dev->dev, "Failed to return from FLR\n"); + else if (i > 1) + dev_info(&dev->dev, "Required additional %dms to return from FLR\n", + (i - 1) * 100); +} + static int pcie_flr(struct pci_dev *dev, int probe) { u32 cap; @@ -3429,7 +3452,7 @@ static int pcie_flr(struct pci_dev *dev, int probe) dev_err(&dev->dev, "timed out waiting for pending transaction; performing function level reset anyway\n"); pcie_capability_set_word(dev, PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); - msleep(100); + pci_flr_wait(dev); return 0; } @@ -3459,7 +3482,7 @@ static int pci_af_flr(struct pci_dev *dev, int probe) dev_err(&dev->dev, "timed out waiting for pending transaction; performing AF function level reset anyway\n"); pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); - msleep(100); + pci_flr_wait(dev); return 0; } -- cgit v1.2.3 From cc73176cc91b67f4f07385d0dcfde954e54f42c3 Mon Sep 17 00:00:00 2001 From: Andreas Ziegler Date: Tue, 15 Mar 2016 12:28:32 +0100 Subject: PCI: Cleanup pci/pcie/Kconfig whitespace Clean up style issues in drivers/pci/pcie/Kconfig, in particular all indentation is now done using tabs, not spaces, and the definition of PCIEASPM_DEBUG is now separated from the definition of PCIEASPM with a newline. Signed-off-by: Andreas Ziegler Signed-off-by: Bjorn Helgaas --- drivers/pci/pcie/Kconfig | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig index e294713c8143..72db7f4209ca 100644 --- a/drivers/pci/pcie/Kconfig +++ b/drivers/pci/pcie/Kconfig @@ -44,6 +44,7 @@ config PCIEASPM /sys/module/pcie_aspm/parameters/policy When in doubt, say Y. + config PCIEASPM_DEBUG bool "Debug PCI Express ASPM" depends on PCIEASPM @@ -58,20 +59,20 @@ choice depends on PCIEASPM config PCIEASPM_DEFAULT - bool "BIOS default" + bool "BIOS default" depends on PCIEASPM help Use the BIOS defaults for PCI Express ASPM. config PCIEASPM_POWERSAVE - bool "Powersave" + bool "Powersave" depends on PCIEASPM help Enable PCI Express ASPM L0s and L1 where possible, even if the BIOS did not. config PCIEASPM_PERFORMANCE - bool "Performance" + bool "Performance" depends on PCIEASPM help Disable PCI Express ASPM L0s and L1, even if the BIOS enabled them. -- cgit v1.2.3 From 7b78f48a0443eceae435870b14e86d586f8c2a3e Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Tue, 15 Mar 2016 14:06:00 +0200 Subject: PCI: Add PCI_CLASS_SERIAL_USB_DEVICE definition PCI-SIG has defined Interface FEh for Base Class 0Ch, Sub-Class 03h as "USB Device (not host controller)". It is already being used in various USB device controller drivers for matching, so add PCI_CLASS_SERIAL_USB_DEVICE and use it. Signed-off-by: Heikki Krogerus Signed-off-by: Bjorn Helgaas --- drivers/pci/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 0575a1e026b4..5714fcfde302 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -438,7 +438,7 @@ static void quirk_amd_nl_class(struct pci_dev *pdev) u32 class = pdev->class; /* Use "USB Device (not host controller)" class */ - pdev->class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe; + pdev->class = PCI_CLASS_SERIAL_USB_DEVICE; dev_info(&pdev->dev, "PCI class overridden (%#08x -> %#08x) so dwc3 driver can claim this instead of xhci\n", class, pdev->class); } -- cgit v1.2.3