From 75568f8094eb0333e9c2109b23cbc8b82d318a3c Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 8 Mar 2010 10:24:29 -0700 Subject: PCI: create function symlinks in /sys/bus/pci/slots/N/ Create convenience symlinks in sysfs, linking slots to device functions, and vice versa. These links make it easier for users to figure out which devices actually live in what slots. For example: sapphire:/sys/bus/pci/slots # ls 1 10 2 3 4 5 6 7 8 9 sapphire:/sys/bus/pci/slots # ls -l 3 total 0 -r--r--r-- 1 root root 65536 Aug 18 14:10 address lrwxrwxrwx 1 root root 0 Aug 18 14:10 function0 -> ../../../../devices/pci0000:23/0000:23:01.0 lrwxrwxrwx 1 root root 0 Aug 18 14:10 function1 -> ../../../../devices/pci0000:23/0000:23:01.1 sapphire:/sys/bus/pci/slots # ls -l 3/function0/slot lrwxrwxrwx 1 root root 0 Aug 18 14:13 3/function0/slot -> ../../../bus/pci/slots/3 The original form of this patch was written by Matthew Wilcox, and was enhanced to include links from the sysfs slots/ directory pointing back at the device functions. Cc: willy@linux.intel.com Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/pci/slot.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index fad93983bfed..941e939d1da9 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -1011,6 +1011,39 @@ error: return retval; } +static void pci_remove_slot_links(struct pci_dev *dev) +{ + char func[10]; + struct pci_slot *slot; + + sysfs_remove_link(&dev->dev.kobj, "slot"); + list_for_each_entry(slot, &dev->bus->slots, list) { + if (slot->number != PCI_SLOT(dev->devfn)) + continue; + snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn)); + sysfs_remove_link(&slot->kobj, func); + } +} + +static int pci_create_slot_links(struct pci_dev *dev) +{ + int result = 0; + char func[10]; + struct pci_slot *slot; + + list_for_each_entry(slot, &dev->bus->slots, list) { + if (slot->number != PCI_SLOT(dev->devfn)) + continue; + result = sysfs_create_link(&dev->dev.kobj, &slot->kobj, "slot"); + if (result) + goto out; + snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn)); + result = sysfs_create_link(&slot->kobj, &dev->dev.kobj, func); + } +out: + return result; +} + int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) { int retval; @@ -1073,6 +1106,8 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) if (retval) goto err_vga_file; + pci_create_slot_links(pdev); + return 0; err_vga_file: @@ -1122,6 +1157,8 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev) if (!sysfs_initialized) return; + pci_remove_slot_links(pdev); + pci_remove_capabilities_sysfs(pdev); if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index 659eaa0fc48f..e0189cf7c558 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -97,6 +97,50 @@ static ssize_t cur_speed_read_file(struct pci_slot *slot, char *buf) return bus_speed_read(slot->bus->cur_bus_speed, buf); } +static void remove_sysfs_files(struct pci_slot *slot) +{ + char func[10]; + struct list_head *tmp; + + list_for_each(tmp, &slot->bus->devices) { + struct pci_dev *dev = pci_dev_b(tmp); + if (PCI_SLOT(dev->devfn) != slot->number) + continue; + sysfs_remove_link(&dev->dev.kobj, "slot"); + + snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn)); + sysfs_remove_link(&slot->kobj, func); + } +} + +static int create_sysfs_files(struct pci_slot *slot) +{ + int result; + char func[10]; + struct list_head *tmp; + + list_for_each(tmp, &slot->bus->devices) { + struct pci_dev *dev = pci_dev_b(tmp); + if (PCI_SLOT(dev->devfn) != slot->number) + continue; + + result = sysfs_create_link(&dev->dev.kobj, &slot->kobj, "slot"); + if (result) + goto fail; + + snprintf(func, 10, "function%d", PCI_FUNC(dev->devfn)); + result = sysfs_create_link(&slot->kobj, &dev->dev.kobj, func); + if (result) + goto fail; + } + + return 0; + +fail: + remove_sysfs_files(slot); + return result; +} + static void pci_slot_release(struct kobject *kobj) { struct pci_dev *dev; @@ -109,6 +153,8 @@ static void pci_slot_release(struct kobject *kobj) if (PCI_SLOT(dev->devfn) == slot->number) dev->slot = NULL; + remove_sysfs_files(slot); + list_del(&slot->list); kfree(slot); @@ -300,6 +346,8 @@ placeholder: INIT_LIST_HEAD(&slot->list); list_add(&slot->list, &parent->slots); + create_sysfs_files(slot); + list_for_each_entry(dev, &parent->devices, bus_list) if (PCI_SLOT(dev->devfn) == slot_nr) dev->slot = slot; -- cgit v1.2.3 From 52b265a12768b9a72679bec825eb82c784116464 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Mon, 8 Mar 2010 16:48:49 -0500 Subject: PCI: clearing wakeup flags not needed This patch (as1353) removes a couple of unnecessary assignments from the PCI core. The should_wakeup flag is naturally initialized to 0; there's no need to clear it. Acked-by: Rafael J. Wysocki Signed-off-by: Alan Stern Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 37499127c801..60fcb6f02c91 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1631,7 +1631,6 @@ void pci_pm_init(struct pci_dev *dev) * let the user space enable it to wake up the system as needed. */ device_set_wakeup_capable(&dev->dev, true); - device_set_wakeup_enable(&dev->dev, false); /* Disable the PME# generation functionality */ pci_pme_active(dev, false); } else { @@ -1655,7 +1654,6 @@ void platform_pci_wakeup_init(struct pci_dev *dev) return; device_set_wakeup_capable(&dev->dev, true); - device_set_wakeup_enable(&dev->dev, false); platform_pci_sleep_wake(dev, false); } -- cgit v1.2.3 From 511dd98ce8cf6dc4f8f2cb32a8af31ce9f4ba4a1 Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Wed, 17 Feb 2010 14:35:19 +0000 Subject: PCI: Convert pci_lock to raw_spinlock pci_lock must be a real spinlock in preempt-rt. Convert it to raw_spinlock. No change for !RT kernels. Signed-off-by: Thomas Gleixner Signed-off-by: Jesse Barnes --- drivers/pci/access.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 2f646fe1260f..affb83b42ebb 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -13,7 +13,7 @@ * configuration space. */ -static DEFINE_SPINLOCK(pci_lock); +static DEFINE_RAW_SPINLOCK(pci_lock); /* * Wrappers for all PCI configuration access functions. They just check @@ -33,10 +33,10 @@ int pci_bus_read_config_##size \ unsigned long flags; \ u32 data = 0; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ - spin_lock_irqsave(&pci_lock, flags); \ + raw_spin_lock_irqsave(&pci_lock, flags); \ res = bus->ops->read(bus, devfn, pos, len, &data); \ *value = (type)data; \ - spin_unlock_irqrestore(&pci_lock, flags); \ + raw_spin_unlock_irqrestore(&pci_lock, flags); \ return res; \ } @@ -47,9 +47,9 @@ int pci_bus_write_config_##size \ int res; \ unsigned long flags; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ - spin_lock_irqsave(&pci_lock, flags); \ + raw_spin_lock_irqsave(&pci_lock, flags); \ res = bus->ops->write(bus, devfn, pos, len, value); \ - spin_unlock_irqrestore(&pci_lock, flags); \ + raw_spin_unlock_irqrestore(&pci_lock, flags); \ return res; \ } @@ -79,10 +79,10 @@ struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops) struct pci_ops *old_ops; unsigned long flags; - spin_lock_irqsave(&pci_lock, flags); + raw_spin_lock_irqsave(&pci_lock, flags); old_ops = bus->ops; bus->ops = ops; - spin_unlock_irqrestore(&pci_lock, flags); + raw_spin_unlock_irqrestore(&pci_lock, flags); return old_ops; } EXPORT_SYMBOL(pci_bus_set_ops); @@ -136,9 +136,9 @@ static noinline void pci_wait_ucfg(struct pci_dev *dev) __add_wait_queue(&pci_ucfg_wait, &wait); do { set_current_state(TASK_UNINTERRUPTIBLE); - spin_unlock_irq(&pci_lock); + raw_spin_unlock_irq(&pci_lock); schedule(); - spin_lock_irq(&pci_lock); + raw_spin_lock_irq(&pci_lock); } while (dev->block_ucfg_access); __remove_wait_queue(&pci_ucfg_wait, &wait); } @@ -150,11 +150,11 @@ int pci_user_read_config_##size \ int ret = 0; \ u32 data = -1; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ - spin_lock_irq(&pci_lock); \ + raw_spin_lock_irq(&pci_lock); \ if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \ ret = dev->bus->ops->read(dev->bus, dev->devfn, \ pos, sizeof(type), &data); \ - spin_unlock_irq(&pci_lock); \ + raw_spin_unlock_irq(&pci_lock); \ *val = (type)data; \ return ret; \ } @@ -165,11 +165,11 @@ int pci_user_write_config_##size \ { \ int ret = -EIO; \ if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ - spin_lock_irq(&pci_lock); \ + raw_spin_lock_irq(&pci_lock); \ if (unlikely(dev->block_ucfg_access)) pci_wait_ucfg(dev); \ ret = dev->bus->ops->write(dev->bus, dev->devfn, \ pos, sizeof(type), val); \ - spin_unlock_irq(&pci_lock); \ + raw_spin_unlock_irq(&pci_lock); \ return ret; \ } @@ -396,10 +396,10 @@ void pci_block_user_cfg_access(struct pci_dev *dev) unsigned long flags; int was_blocked; - spin_lock_irqsave(&pci_lock, flags); + raw_spin_lock_irqsave(&pci_lock, flags); was_blocked = dev->block_ucfg_access; dev->block_ucfg_access = 1; - spin_unlock_irqrestore(&pci_lock, flags); + raw_spin_unlock_irqrestore(&pci_lock, flags); /* If we BUG() inside the pci_lock, we're guaranteed to hose * the machine */ @@ -417,7 +417,7 @@ void pci_unblock_user_cfg_access(struct pci_dev *dev) { unsigned long flags; - spin_lock_irqsave(&pci_lock, flags); + raw_spin_lock_irqsave(&pci_lock, flags); /* This indicates a problem in the caller, but we don't need * to kill them, unlike a double-block above. */ @@ -425,6 +425,6 @@ void pci_unblock_user_cfg_access(struct pci_dev *dev) dev->block_ucfg_access = 0; wake_up_all(&pci_ucfg_wait); - spin_unlock_irqrestore(&pci_lock, flags); + raw_spin_unlock_irqrestore(&pci_lock, flags); } EXPORT_SYMBOL_GPL(pci_unblock_user_cfg_access); -- cgit v1.2.3 From 3196180a54b593838c0b6496e5b524a2f69bb190 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Thu, 8 Apr 2010 09:38:47 -0700 Subject: PCI: change PCI_MSI help text to recommend enabling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most current machines have no problem with this, and in fact many devices and features work best (or only!) with MSI. Reported-by: Petteri Räty Signed-off-by: Jesse Barnes --- drivers/pci/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 7858a117e80b..34ef70d562b2 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -19,7 +19,7 @@ config PCI_MSI by using the 'pci=nomsi' option. This disables MSI for the entire system. - If you don't know what to do here, say N. + If you don't know what to do here, say Y. config PCI_DEBUG bool "PCI Debugging" -- cgit v1.2.3 From 447c5dd7338638f526e9bcf7dcf69b4da5835c7d Mon Sep 17 00:00:00 2001 From: Michal Schmidt Date: Tue, 11 May 2010 11:44:54 +0200 Subject: PCI: return correct value when writing to the "reset" attribute A successful write() to the "reset" sysfs attribute should return the number of bytes written, not 0. Otherwise userspace (bash) retries the write over and over again. Acked-by: Michael S. Tsirkin Acked-by: Greg Kroah-Hartman Signed-off-by: Michal Schmidt Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 941e939d1da9..89a08ed39c94 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -960,7 +960,12 @@ static ssize_t reset_store(struct device *dev, if (val != 1) return -EINVAL; - return pci_reset_function(pdev); + + result = pci_reset_function(pdev); + if (result < 0) + return result; + + return count; } static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store); -- cgit v1.2.3 From c6d34eddecb34fd84f9fb2ea26a63cfde5662f49 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:09:13 +0900 Subject: PCI: aerdrv: RsvdP of PCI_ERR_ROOT_COMMAND Handle preserved bits properly. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 18 +++++++++++------- drivers/pci/pcie/aer/aerdrv_core.c | 10 ++++++---- 2 files changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 7a711ee314b7..4e845ab18643 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -234,13 +234,15 @@ static int __devinit aer_probe(struct pcie_device *dev) static pci_ers_result_t aer_root_reset(struct pci_dev *dev) { u16 p2p_ctrl; - u32 status; + u32 reg32; int pos; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); /* Disable Root's interrupt in response to error messages */ - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0); + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); /* Assert Secondary Bus Reset */ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); @@ -265,12 +267,14 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) msleep(200); dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); + /* Clear Root Error Status */ + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, ®32); + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32); + /* Enable Root Port's interrupt in response to error messages */ - pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status); - pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status); - pci_write_config_dword(dev, - pos + PCI_ERR_ROOT_COMMAND, - ROOT_PORT_INTR_ON_MESG_MASK); + pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); return PCI_ERS_RESULT_RECOVERED; } diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index aceb04b67b60..9754a09bf20e 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -623,9 +623,9 @@ void aer_enable_rootport(struct aer_rpc *rpc) set_downstream_devices_error_reporting(pdev, true); /* Enable Root Port's interrupt in response to error messages */ - pci_write_config_dword(pdev, - aer_pos + PCI_ERR_ROOT_COMMAND, - ROOT_PORT_INTR_ON_MESG_MASK); + pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32); } /** @@ -648,7 +648,9 @@ static void disable_root_aer(struct aer_rpc *rpc) pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); /* Disable Root's interrupt in response to error messages */ - pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); + pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32); /* Clear Root's error status reg */ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); -- cgit v1.2.3 From 460d298d521910483dcdc09920ca4c4a63b16730 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:10:03 +0900 Subject: PCI: aerdrv: cleanup inconsistent functions This cleanup solves some minor naming issues by removing unuseful function aer_delete_rootport() and by renaming disable_root_aer() to aer_disable_rootport(). - Inconsistent location of alloc & free: The struct rpc is allocated in aer_alloc_rpc() at aerdrv.c while it is implicitly freed in aer_delete_rootport() at aerdrv_core.c. - Inconsistent function name: It makes a bit confusion that aer_delete_rootport() is seemed to be paired with aer_enable_rootport(), i.e. there is neither "add" against "delete" nor "disable" against "enable". Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 3 ++- drivers/pci/pcie/aer/aerdrv.h | 2 +- drivers/pci/pcie/aer/aerdrv_core.c | 18 ++---------------- 3 files changed, 5 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 4e845ab18643..14081f807e50 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -179,7 +179,8 @@ static void aer_remove(struct pcie_device *dev) wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx); - aer_delete_rootport(rpc); + aer_disable_rootport(rpc); + kfree(rpc); set_service_data(dev, NULL); } } diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index bd833ea3ba49..b6fc5389dd09 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -118,7 +118,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, extern struct bus_type pcie_port_bus_type; extern void aer_enable_rootport(struct aer_rpc *rpc); -extern void aer_delete_rootport(struct aer_rpc *rpc); +extern void aer_disable_rootport(struct aer_rpc *rpc); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 9754a09bf20e..0dcbae126834 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -629,12 +629,12 @@ void aer_enable_rootport(struct aer_rpc *rpc) } /** - * disable_root_aer - disable Root Port's interrupts when receiving messages + * aer_disable_rootport - disable Root Port's interrupts when receiving messages * @rpc: pointer to a Root Port data structure * * Invoked when PCIe bus unloads AER service driver. */ -static void disable_root_aer(struct aer_rpc *rpc) +void aer_disable_rootport(struct aer_rpc *rpc) { struct pci_dev *pdev = rpc->rpd->port; u32 reg32; @@ -839,20 +839,6 @@ void aer_isr(struct work_struct *work) wake_up(&rpc->wait_release); } -/** - * aer_delete_rootport - disable root port aer and delete service data - * @rpc: pointer to a root port device being deleted - * - * Invoked when AER service unloaded on a specific Root Port - */ -void aer_delete_rootport(struct aer_rpc *rpc) -{ - /* Disable root port AER itself */ - disable_root_aer(rpc); - - kfree(rpc); -} - /** * aer_init - provide AER initialization * @dev: pointer to AER pcie device -- cgit v1.2.3 From 843f4697eea576c24f057bbdb199115bbb6b10bc Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:10:53 +0900 Subject: PCI: aerdrv: make aer_{en,dis}able_rootport static These functions are only called from init/remove path of aerdrv, so move them from aerdrv_core.c to aerdrv.c, to make them static. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 107 +++++++++++++++++++++++++++++++++++++ drivers/pci/pcie/aer/aerdrv.h | 2 - drivers/pci/pcie/aer/aerdrv_core.c | 107 ------------------------------------- 3 files changed, 107 insertions(+), 109 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 14081f807e50..b69dbdc36817 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -72,6 +72,113 @@ void pci_no_aer(void) pcie_aer_disable = 1; /* has priority over 'forceload' */ } +static int set_device_error_reporting(struct pci_dev *dev, void *data) +{ + bool enable = *((bool *)data); + + if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) || + (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) || + (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) { + if (enable) + pci_enable_pcie_error_reporting(dev); + else + pci_disable_pcie_error_reporting(dev); + } + + if (enable) + pcie_set_ecrc_checking(dev); + + return 0; +} + +/** + * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports. + * @dev: pointer to root port's pci_dev data structure + * @enable: true = enable error reporting, false = disable error reporting. + */ +static void set_downstream_devices_error_reporting(struct pci_dev *dev, + bool enable) +{ + set_device_error_reporting(dev, &enable); + + if (!dev->subordinate) + return; + pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable); +} + +/** + * aer_enable_rootport - enable Root Port's interrupts when receiving messages + * @rpc: pointer to a Root Port data structure + * + * Invoked when PCIe bus loads AER service driver. + */ +static void aer_enable_rootport(struct aer_rpc *rpc) +{ + struct pci_dev *pdev = rpc->rpd->port; + int pos, aer_pos; + u16 reg16; + u32 reg32; + + pos = pci_pcie_cap(pdev); + /* Clear PCIe Capability's Device Status */ + pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); + pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); + + /* Disable system error generation in response to error messages */ + pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); + reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); + pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); + + aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + /* Clear error status */ + pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); + pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); + pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); + pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); + pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); + pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); + + /* + * Enable error reporting for the root port device and downstream port + * devices. + */ + set_downstream_devices_error_reporting(pdev, true); + + /* Enable Root Port's interrupt in response to error messages */ + pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32); +} + +/** + * aer_disable_rootport - disable Root Port's interrupts when receiving messages + * @rpc: pointer to a Root Port data structure + * + * Invoked when PCIe bus unloads AER service driver. + */ +static void aer_disable_rootport(struct aer_rpc *rpc) +{ + struct pci_dev *pdev = rpc->rpd->port; + u32 reg32; + int pos; + + /* + * Disable error reporting for the root port device and downstream port + * devices. + */ + set_downstream_devices_error_reporting(pdev, false); + + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); + /* Disable Root's interrupt in response to error messages */ + pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); + reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; + pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32); + + /* Clear Root's error status reg */ + pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); + pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); +} + /** * aer_irq - Root Port's ISR * @irq: IRQ assigned to Root Port diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index b6fc5389dd09..2f345405e823 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -117,8 +117,6 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, } extern struct bus_type pcie_port_bus_type; -extern void aer_enable_rootport(struct aer_rpc *rpc); -extern void aer_disable_rootport(struct aer_rpc *rpc); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 0dcbae126834..ad15eea54fd0 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -99,40 +99,6 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); -static int set_device_error_reporting(struct pci_dev *dev, void *data) -{ - bool enable = *((bool *)data); - - if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) || - (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) || - (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) { - if (enable) - pci_enable_pcie_error_reporting(dev); - else - pci_disable_pcie_error_reporting(dev); - } - - if (enable) - pcie_set_ecrc_checking(dev); - - return 0; -} - -/** - * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports. - * @dev: pointer to root port's pci_dev data structure - * @enable: true = enable error reporting, false = disable error reporting. - */ -static void set_downstream_devices_error_reporting(struct pci_dev *dev, - bool enable) -{ - set_device_error_reporting(dev, &enable); - - if (!dev->subordinate) - return; - pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable); -} - static inline int compare_device_id(struct pci_dev *dev, struct aer_err_info *e_info) { @@ -584,79 +550,6 @@ static void handle_error_source(struct pcie_device *aerdev, } } -/** - * aer_enable_rootport - enable Root Port's interrupts when receiving messages - * @rpc: pointer to a Root Port data structure - * - * Invoked when PCIe bus loads AER service driver. - */ -void aer_enable_rootport(struct aer_rpc *rpc) -{ - struct pci_dev *pdev = rpc->rpd->port; - int pos, aer_pos; - u16 reg16; - u32 reg32; - - pos = pci_pcie_cap(pdev); - /* Clear PCIe Capability's Device Status */ - pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); - pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16); - - /* Disable system error generation in response to error messages */ - pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, ®16); - reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); - pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); - - aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); - /* Clear error status */ - pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); - pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); - pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); - pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); - pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); - pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32); - - /* - * Enable error reporting for the root port device and downstream port - * devices. - */ - set_downstream_devices_error_reporting(pdev, true); - - /* Enable Root Port's interrupt in response to error messages */ - pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ®32); - reg32 |= ROOT_PORT_INTR_ON_MESG_MASK; - pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32); -} - -/** - * aer_disable_rootport - disable Root Port's interrupts when receiving messages - * @rpc: pointer to a Root Port data structure - * - * Invoked when PCIe bus unloads AER service driver. - */ -void aer_disable_rootport(struct aer_rpc *rpc) -{ - struct pci_dev *pdev = rpc->rpd->port; - u32 reg32; - int pos; - - /* - * Disable error reporting for the root port device and downstream port - * devices. - */ - set_downstream_devices_error_reporting(pdev, false); - - pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR); - /* Disable Root's interrupt in response to error messages */ - pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, ®32); - reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; - pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32); - - /* Clear Root's error status reg */ - pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); - pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); -} - /** * get_e_source - retrieve an error source * @rpc: pointer to the root port which holds an error -- cgit v1.2.3 From 98ca3964fe8da0d742331af80952443af5cff464 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:11:42 +0900 Subject: PCI: aerdrv: rework find_source_device Return bool to indicate that the source device is found or not. This allows us to skip calling aer_process_err_devices() if we can. And move dev_printk for debug into this function. v2: return bool instead of int Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index ad15eea54fd0..5fa5c76719b0 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -215,11 +215,13 @@ added: /** * find_source_device - search through device hierarchy for source device * @parent: pointer to Root Port pci_dev data structure - * @err_info: including detailed error information such like id + * @e_info: including detailed error information such like id * - * Invoked when error is detected at the Root Port. + * Return true if found. + * + * Invoked by DPC when error is detected at the Root Port. */ -static void find_source_device(struct pci_dev *parent, +static bool find_source_device(struct pci_dev *parent, struct aer_err_info *e_info) { struct pci_dev *dev = parent; @@ -228,9 +230,17 @@ static void find_source_device(struct pci_dev *parent, /* Is Root Port an agent that sends error message? */ result = find_device_iter(dev, e_info); if (result) - return; + return true; pci_walk_bus(parent->subordinate, find_device_iter, e_info); + + if (!e_info->error_dev_num) { + dev_printk(KERN_DEBUG, &parent->dev, + "can't find device of ID%04x\n", + e_info->id); + return false; + } + return true; } static int report_error_detected(struct pci_dev *dev, void *data) @@ -639,12 +649,6 @@ static inline void aer_process_err_devices(struct pcie_device *p_device, { int i; - if (!e_info->dev[0]) { - dev_printk(KERN_DEBUG, &p_device->port->dev, - "can't find device of ID%04x\n", - e_info->id); - } - /* Report all before handle them, not to lost records by reset etc. */ for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { if (get_device_error_info(e_info->dev[i], e_info)) @@ -702,8 +706,8 @@ static void aer_isr_one_error(struct pcie_device *p_device, aer_print_port_info(p_device->port, e_info); - find_source_device(p_device->port, e_info); - aer_process_err_devices(p_device, e_info); + if (find_source_device(p_device->port, e_info)) + aer_process_err_devices(p_device, e_info); } kfree(e_info); -- cgit v1.2.3 From c887275e6a5b857b72c798e4a6019160a860e2ef Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:12:21 +0900 Subject: PCI: aerdrv: introduce is_error_source Take core part of find_device_iter() to make a new function is_error_source() that checks given device has report an error or not. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 74 ++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 40 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 5fa5c76719b0..5b02f62cdc47 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -126,14 +126,17 @@ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) #define PCI_BUS(x) (((x) >> 8) & 0xff) -static int find_device_iter(struct pci_dev *dev, void *data) +/** + * is_error_source - check whether the device is source of reported error + * @dev: pointer to pci_dev to be checked + * @e_info: pointer to reported error info + */ +static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) { int pos; - u32 status; - u32 mask; + u32 status, mask; u16 reg16; int result; - struct aer_err_info *e_info = (struct aer_err_info *)data; /* * When bus id is equal to 0, it might be a bad id @@ -142,22 +145,11 @@ static int find_device_iter(struct pci_dev *dev, void *data) if (!nosourceid && (PCI_BUS(e_info->id) != 0)) { result = compare_device_id(dev, e_info); if (result) - add_error_device(e_info, dev); + return true; - /* - * If there is no multiple error, we stop - * or continue based on the id comparing. - */ + /* Continue id comparing if there is no multiple error */ if (!e_info->multi_error_valid) - return result; - - /* - * If there are multiple errors and id does match, - * We need continue to search other devices under - * the root port. Return 0 means that. - */ - if (result) - return 0; + return false; } /* @@ -166,50 +158,52 @@ static int find_device_iter(struct pci_dev *dev, void *data) * 2) bus id is equal to 0. Some ports might lose the bus * id of error source id; * 3) There are multiple errors and prior id comparing fails; - * We check AER status registers to find the initial reporter. + * We check AER status registers to find possible reporter. */ if (atomic_read(&dev->enable_cnt) == 0) - return 0; + return false; pos = pci_pcie_cap(dev); if (!pos) - return 0; + return false; + /* Check if AER is enabled */ - pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16); if (!(reg16 & ( PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE))) - return 0; + return false; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (!pos) - return 0; + return false; - status = 0; - mask = 0; + /* Check if error is recorded */ if (e_info->severity == AER_CORRECTABLE) { pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); - if (status & ~mask) { - add_error_device(e_info, dev); - goto added; - } } else { pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); - if (status & ~mask) { - add_error_device(e_info, dev); - goto added; - } } + if (status & ~mask) + return true; - return 0; + return false; +} -added: - if (e_info->multi_error_valid) - return 0; - else - return 1; +static int find_device_iter(struct pci_dev *dev, void *data) +{ + struct aer_err_info *e_info = (struct aer_err_info *)data; + + if (is_error_source(dev, e_info)) { + add_error_device(e_info, dev); + + /* If there is only a single error, stop iteration */ + if (!e_info->multi_error_valid) + return 1; + } + return 0; } /** -- cgit v1.2.3 From bd17d4742d5a8cbedd41a1d44c0cdee84a532363 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:13:41 +0900 Subject: PCI: aerdrv: remove compare_device_id Inline too-simple subroutine only used here. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 5b02f62cdc47..f8ffa47503b7 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -99,19 +99,6 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); -static inline int compare_device_id(struct pci_dev *dev, - struct aer_err_info *e_info) -{ - if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) { - /* - * Device ID match - */ - return 1; - } - - return 0; -} - static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) { if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { @@ -136,15 +123,14 @@ static bool is_error_source(struct pci_dev *dev, struct aer_err_info *e_info) int pos; u32 status, mask; u16 reg16; - int result; /* * When bus id is equal to 0, it might be a bad id * reported by root port. */ if (!nosourceid && (PCI_BUS(e_info->id) != 0)) { - result = compare_device_id(dev, e_info); - if (result) + /* Device ID match? */ + if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) return true; /* Continue id comparing if there is no multiple error */ -- cgit v1.2.3 From 4a0c096efd4383fc98aa40e195363f600ba814f8 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:14:17 +0900 Subject: PCI: aerdrv: rework add_error_device Stop iteration if we cannot register any more. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index f8ffa47503b7..f5eb69f532e7 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -99,18 +99,21 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); +/** + * add_error_device - list device to be handled + * @e_info: pointer to error info + * @dev: pointer to pci_dev to be added + */ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) { if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { e_info->dev[e_info->error_dev_num] = dev; e_info->error_dev_num++; - return 1; + return 0; } - - return 0; + return -ENOSPC; } - #define PCI_BUS(x) (((x) >> 8) & 0xff) /** @@ -183,7 +186,12 @@ static int find_device_iter(struct pci_dev *dev, void *data) struct aer_err_info *e_info = (struct aer_err_info *)data; if (is_error_source(dev, e_info)) { - add_error_device(e_info, dev); + /* List this device */ + if (add_error_device(e_info, dev)) { + /* We cannot handle more... Stop iteration */ + /* TODO: Should print error message here? */ + return 1; + } /* If there is only a single error, stop iteration */ if (!e_info->multi_error_valid) -- cgit v1.2.3 From 7c4ec94f72cefec1c1b42219469794a34864a1ee Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:15:08 +0900 Subject: PCI: aerdrv: rework aer_isr_one_error() Divide tricky for-loop into readable if-blocks. The logic to set multi_error_valid (to force walking pci bus hierarchy to find 2nd~ error devices) is changed too, to check MULTI_{,_UN}COR_RCV bit individually and to force walk only when it is required. And rework setting e_info->severity for uncorrectable, not to use magic numbers. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 57 ++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index f5eb69f532e7..ca140580199c 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -208,6 +208,9 @@ static int find_device_iter(struct pci_dev *dev, void *data) * Return true if found. * * Invoked by DPC when error is detected at the Root Port. + * Caller of this function must set id, severity, and multi_error_valid of + * struct aer_err_info pointed by @e_info properly. This function must fill + * e_info->error_dev_num and e_info->dev[], based on the given information. */ static bool find_source_device(struct pci_dev *parent, struct aer_err_info *e_info) @@ -215,6 +218,9 @@ static bool find_source_device(struct pci_dev *parent, struct pci_dev *dev = parent; int result; + /* Must reset in this function */ + e_info->error_dev_num = 0; + /* Is Root Port an agent that sends error message? */ result = find_device_iter(dev, e_info); if (result) @@ -580,11 +586,14 @@ static struct aer_err_source *get_e_source(struct aer_rpc *rpc) * @info: pointer to structure to store the error record * * Return 1 on success, 0 on error. + * + * Note that @info is reused among all error devices. Clear fields properly. */ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) { int pos, temp; + /* Must reset in this function */ info->status = 0; info->tlp_header_valid = 0; @@ -657,11 +666,10 @@ static void aer_isr_one_error(struct pcie_device *p_device, struct aer_err_source *e_src) { struct aer_err_info *e_info; - int i; /* struct aer_err_info might be big, so we allocate it with slab */ e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL); - if (e_info == NULL) { + if (!e_info) { dev_printk(KERN_DEBUG, &p_device->port->dev, "Can't allocate mem when processing AER errors\n"); return; @@ -671,26 +679,33 @@ static void aer_isr_one_error(struct pcie_device *p_device, * There is a possibility that both correctable error and * uncorrectable error being logged. Report correctable error first. */ - for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) { - if (i > 4) - break; - if (!(e_src->status & i)) - continue; - - memset(e_info, 0, sizeof(struct aer_err_info)); - - /* Init comprehensive error information */ - if (i & PCI_ERR_ROOT_COR_RCV) { - e_info->id = ERR_COR_ID(e_src->id); - e_info->severity = AER_CORRECTABLE; - } else { - e_info->id = ERR_UNCOR_ID(e_src->id); - e_info->severity = ((e_src->status >> 6) & 1); - } - if (e_src->status & - (PCI_ERR_ROOT_MULTI_COR_RCV | - PCI_ERR_ROOT_MULTI_UNCOR_RCV)) + if (e_src->status & PCI_ERR_ROOT_COR_RCV) { + e_info->id = ERR_COR_ID(e_src->id); + e_info->severity = AER_CORRECTABLE; + + if (e_src->status & PCI_ERR_ROOT_MULTI_COR_RCV) + e_info->multi_error_valid = 1; + else + e_info->multi_error_valid = 0; + + aer_print_port_info(p_device->port, e_info); + + if (find_source_device(p_device->port, e_info)) + aer_process_err_devices(p_device, e_info); + } + + if (e_src->status & PCI_ERR_ROOT_UNCOR_RCV) { + e_info->id = ERR_UNCOR_ID(e_src->id); + + if (e_src->status & PCI_ERR_ROOT_FATAL_RCV) + e_info->severity = AER_FATAL; + else + e_info->severity = AER_NONFATAL; + + if (e_src->status & PCI_ERR_ROOT_MULTI_UNCOR_RCV) e_info->multi_error_valid = 1; + else + e_info->multi_error_valid = 0; aer_print_port_info(p_device->port, e_info); -- cgit v1.2.3 From 88da13bfabbffb8f89574eb168b9da9a0abc693f Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:16:16 +0900 Subject: PCI: aerdrv: rework get_e_source() Current get_e_source() returns pointer to an element of array. However since it also progress consume counter, it is possible that the element is overwritten by newly produced data before the element is really consumed. This patch changes get_e_source() to copy contents of the element to address pointed by its caller. Once copied the element in array can be consumed. And relocate this function to more innocuous place. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 63 +++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index ca140580199c..210e53c2fdc1 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -554,32 +554,6 @@ static void handle_error_source(struct pcie_device *aerdev, } } -/** - * get_e_source - retrieve an error source - * @rpc: pointer to the root port which holds an error - * - * Invoked by DPC handler to consume an error. - */ -static struct aer_err_source *get_e_source(struct aer_rpc *rpc) -{ - struct aer_err_source *e_source; - unsigned long flags; - - /* Lock access to Root error producer/consumer index */ - spin_lock_irqsave(&rpc->e_lock, flags); - if (rpc->prod_idx == rpc->cons_idx) { - spin_unlock_irqrestore(&rpc->e_lock, flags); - return NULL; - } - e_source = &rpc->e_sources[rpc->cons_idx]; - rpc->cons_idx++; - if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) - rpc->cons_idx = 0; - spin_unlock_irqrestore(&rpc->e_lock, flags); - - return e_source; -} - /** * get_device_error_info - read error status from dev and store it to info * @dev: pointer to the device expected to have a error record @@ -716,6 +690,34 @@ static void aer_isr_one_error(struct pcie_device *p_device, kfree(e_info); } +/** + * get_e_source - retrieve an error source + * @rpc: pointer to the root port which holds an error + * @e_src: pointer to store retrieved error source + * + * Return 1 if an error source is retrieved, otherwise 0. + * + * Invoked by DPC handler to consume an error. + */ +static int get_e_source(struct aer_rpc *rpc, struct aer_err_source *e_src) +{ + unsigned long flags; + int ret = 0; + + /* Lock access to Root error producer/consumer index */ + spin_lock_irqsave(&rpc->e_lock, flags); + if (rpc->prod_idx != rpc->cons_idx) { + *e_src = rpc->e_sources[rpc->cons_idx]; + rpc->cons_idx++; + if (rpc->cons_idx == AER_ERROR_SOURCES_MAX) + rpc->cons_idx = 0; + ret = 1; + } + spin_unlock_irqrestore(&rpc->e_lock, flags); + + return ret; +} + /** * aer_isr - consume errors detected by root port * @work: definition of this work item @@ -726,14 +728,11 @@ void aer_isr(struct work_struct *work) { struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); struct pcie_device *p_device = rpc->rpd; - struct aer_err_source *e_src; + struct aer_err_source e_src; mutex_lock(&rpc->rpc_mutex); - e_src = get_e_source(rpc); - while (e_src) { - aer_isr_one_error(p_device, e_src); - e_src = get_e_source(rpc); - } + while (get_e_source(rpc, &e_src)) + aer_isr_one_error(p_device, &e_src); mutex_unlock(&rpc->rpc_mutex); wake_up(&rpc->wait_release); -- cgit v1.2.3 From 17e21854bd59862f4ee47d1c7e828549f782711b Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:16:52 +0900 Subject: PCI: aerdrv: rework do_recovery Move dev_printks for debug into do_recovery(). This allows do_recovery() to return void. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 38 +++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 210e53c2fdc1..9dcd3aeeafb6 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -465,8 +465,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, * error detected message to all downstream drivers within a hierarchy in * question and return the returned code. */ -static pci_ers_result_t do_recovery(struct pcie_device *aerdev, - struct pci_dev *dev, +static void do_recovery(struct pcie_device *aerdev, struct pci_dev *dev, int severity) { pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED; @@ -484,10 +483,8 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev, if (severity == AER_FATAL) { result = reset_link(aerdev, dev); - if (result != PCI_ERS_RESULT_RECOVERED) { - /* TODO: Should panic here? */ - return result; - } + if (result != PCI_ERS_RESULT_RECOVERED) + goto failed; } if (status == PCI_ERS_RESULT_CAN_RECOVER) @@ -508,13 +505,22 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev, report_slot_reset); } - if (status == PCI_ERS_RESULT_RECOVERED) - broadcast_error_message(dev, + if (status != PCI_ERS_RESULT_RECOVERED) + goto failed; + + broadcast_error_message(dev, state, "resume", report_resume); - return status; + dev_printk(KERN_DEBUG, &dev->dev, + "AER driver successfully recovered\n"); + return; + +failed: + /* TODO: Should kernel panic here? */ + dev_printk(KERN_DEBUG, &dev->dev, + "AER driver didn't recover\n"); } /** @@ -529,7 +535,6 @@ static void handle_error_source(struct pcie_device *aerdev, struct pci_dev *dev, struct aer_err_info *info) { - pci_ers_result_t status = 0; int pos; if (info->severity == AER_CORRECTABLE) { @@ -541,17 +546,8 @@ static void handle_error_source(struct pcie_device *aerdev, if (pos) pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, info->status); - } else { - status = do_recovery(aerdev, dev, info->severity); - if (status == PCI_ERS_RESULT_RECOVERED) { - dev_printk(KERN_DEBUG, &dev->dev, "AER driver " - "successfully recovered\n"); - } else { - /* TODO: Should kernel panic here? */ - dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't " - "recover\n"); - } - } + } else + do_recovery(aerdev, dev, info->severity); } /** -- cgit v1.2.3 From f647a44f5725b0e6c8211096f4b49900164123ee Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:17:33 +0900 Subject: PCI: aerdrv: redefine PCI_ERR_ROOT_*_SRC The Error Source Identification Register (Offset 34h) is 4 byte which contains a couple of 2 byte field, "[15:0] ERR_COR Source Identification" and "[31:16] ERR_FATAL/NONFATAL Source Identification." This patch defines PCI_ERR_ROOT_ERR_SRC to make dword access sensible. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aer_inject.c | 2 +- drivers/pci/pcie/aer/aerdrv.c | 2 +- include/linux/pci_regs.h | 3 +-- 3 files changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index f8f425b8731d..909924692b8a 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -168,7 +168,7 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where, target = &err->root_status; rw1cs = 1; break; - case PCI_ERR_ROOT_COR_SRC: + case PCI_ERR_ROOT_ERR_SRC: target = &err->source_id; break; } diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index b69dbdc36817..1a55c16e2f3f 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -210,7 +210,7 @@ irqreturn_t aer_irq(int irq, void *context) } /* Read error source and clear error status */ - pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id); + pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id); pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); /* Store error source for later DPC handler */ diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index c8f302991b66..dd0dd873f637 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -563,8 +563,7 @@ #define PCI_ERR_ROOT_FIRST_FATAL 0x00000010 /* First Fatal */ #define PCI_ERR_ROOT_NONFATAL_RCV 0x00000020 /* Non-Fatal Received */ #define PCI_ERR_ROOT_FATAL_RCV 0x00000040 /* Fatal Received */ -#define PCI_ERR_ROOT_COR_SRC 52 -#define PCI_ERR_ROOT_SRC 54 +#define PCI_ERR_ROOT_ERR_SRC 52 /* Error Source Identification */ /* Virtual Channel */ #define PCI_VC_PORT_REG1 4 -- cgit v1.2.3 From e167bfcaa4cd44b4c66206a3c06b2aafb3f1260e Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:18:26 +0900 Subject: PCI: aerdrv: remove magical ROOT_ERR_STATUS_MASKS Make it clear that we only interest in 2 *_RCV bits. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 2 +- drivers/pci/pcie/aer/aerdrv.h | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 1a55c16e2f3f..cbc7cc77b2c3 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -204,7 +204,7 @@ irqreturn_t aer_irq(int irq, void *context) /* Read error status */ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); - if (!(status & ROOT_ERR_STATUS_MASKS)) { + if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) { spin_unlock_irqrestore(&rpc->e_lock, flags); return IRQ_NONE; } diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 2f345405e823..d0f8291c5ca0 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -17,9 +17,6 @@ #define AER_FATAL 1 #define AER_CORRECTABLE 2 -/* Root Error Status Register Bits */ -#define ROOT_ERR_STATUS_MASKS 0x0f - #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ PCI_EXP_RTCTL_SEFEE) -- cgit v1.2.3 From 4f7ccf6a6085eefd2517b8c7090608c64b01ab67 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:19:48 +0900 Subject: PCI: aerdrv: remove is_downstream The pcie->port of port service device points the port associated the service with. The find_aer_service iterates over children of given port udev. So it is clear that the pcie->port of port service of given port udev must always point the udev. Therefore we can know the type of udev without checking its children. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 9dcd3aeeafb6..8d458a03afc9 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -375,30 +375,20 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, struct find_aer_service_data { struct pcie_port_service_driver *aer_driver; - int is_downstream; }; static int find_aer_service_iter(struct device *device, void *data) { - struct device_driver *driver; struct pcie_port_service_driver *service_driver; struct find_aer_service_data *result; result = (struct find_aer_service_data *) data; - if (device->bus == &pcie_port_bus_type) { - struct pcie_device *pcie = to_pcie_device(device); - - if (pcie->port->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) - result->is_downstream = 1; - - driver = device->driver; - if (driver) { - service_driver = to_service_driver(driver); - if (service_driver->service == PCIE_PORT_SERVICE_AER) { - result->aer_driver = service_driver; - return 1; - } + if (device->bus == &pcie_port_bus_type && device->driver) { + service_driver = to_service_driver(device->driver); + if (service_driver->service == PCIE_PORT_SERVICE_AER) { + result->aer_driver = service_driver; + return 1; } } @@ -424,7 +414,6 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, else udev = dev->bus->self; - data.is_downstream = 0; data.aer_driver = NULL; find_aer_service(udev, &data); @@ -433,22 +422,24 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, * If it hasn't the aer driver, use the root port's */ if (!data.aer_driver || !data.aer_driver->reset_link) { - if (data.is_downstream && + if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && aerdev->device.driver && to_service_driver(aerdev->device.driver)->reset_link) { data.aer_driver = to_service_driver(aerdev->device.driver); } else { - dev_printk(KERN_DEBUG, &dev->dev, "no link-reset " - "support\n"); + dev_printk(KERN_DEBUG, &dev->dev, + "no link-reset support at upstream device %s\n", + pci_name(udev)); return PCI_ERS_RESULT_DISCONNECT; } } status = data.aer_driver->reset_link(udev); if (status != PCI_ERS_RESULT_RECOVERED) { - dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream " - "device %s failed\n", pci_name(udev)); + dev_printk(KERN_DEBUG, &dev->dev, + "link reset at upstream device %s failed\n", + pci_name(udev)); return PCI_ERS_RESULT_DISCONNECT; } -- cgit v1.2.3 From 517cae3829ae8cc3033c24f60e64eb251b2f0d14 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:20:43 +0900 Subject: PCI: aerdrv: rework find_aer_service The structure find_aer_service_data is no longer useful. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Reviewed-by: Jin Dongming Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 8d458a03afc9..8fb14aeb74dd 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -373,21 +373,16 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, return result_data.result; } -struct find_aer_service_data { - struct pcie_port_service_driver *aer_driver; -}; - static int find_aer_service_iter(struct device *device, void *data) { - struct pcie_port_service_driver *service_driver; - struct find_aer_service_data *result; + struct pcie_port_service_driver *service_driver, **drv; - result = (struct find_aer_service_data *) data; + drv = (struct pcie_port_service_driver **) data; if (device->bus == &pcie_port_bus_type && device->driver) { service_driver = to_service_driver(device->driver); if (service_driver->service == PCIE_PORT_SERVICE_AER) { - result->aer_driver = service_driver; + *drv = service_driver; return 1; } } @@ -395,11 +390,13 @@ static int find_aer_service_iter(struct device *device, void *data) return 0; } -static void find_aer_service(struct pci_dev *dev, - struct find_aer_service_data *data) +static struct pcie_port_service_driver *find_aer_service(struct pci_dev *dev) { - int retval; - retval = device_for_each_child(&dev->dev, data, find_aer_service_iter); + struct pcie_port_service_driver *drv = NULL; + + device_for_each_child(&dev->dev, &drv, find_aer_service_iter); + + return drv; } static pci_ers_result_t reset_link(struct pcie_device *aerdev, @@ -407,26 +404,24 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, { struct pci_dev *udev; pci_ers_result_t status; - struct find_aer_service_data data; + struct pcie_port_service_driver *driver; if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) udev = dev; else udev = dev->bus->self; - data.aer_driver = NULL; - find_aer_service(udev, &data); + /* Use the aer driver of the component firstly */ + driver = find_aer_service(udev); /* - * Use the aer driver of the error agent firstly. - * If it hasn't the aer driver, use the root port's + * If it hasn't the driver and is downstream port, use the root port's */ - if (!data.aer_driver || !data.aer_driver->reset_link) { + if (!driver || !driver->reset_link) { if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && aerdev->device.driver && to_service_driver(aerdev->device.driver)->reset_link) { - data.aer_driver = - to_service_driver(aerdev->device.driver); + driver = to_service_driver(aerdev->device.driver); } else { dev_printk(KERN_DEBUG, &dev->dev, "no link-reset support at upstream device %s\n", @@ -435,7 +430,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, } } - status = data.aer_driver->reset_link(udev); + status = driver->reset_link(udev); if (status != PCI_ERS_RESULT_RECOVERED) { dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream device %s failed\n", -- cgit v1.2.3 From 89713422a768458a0d375f0c2f3586cd5ccde6a1 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:21:27 +0900 Subject: PCI: aerdrv: introduce default_downstream_reset_link I noticed that when I inject a fatal error to an endpoint via aer-inject, aer_root_reset() is called as reset_link for a downstream port at upstream of the endpoint: pcieport 0000:00:06.0: AER: Uncorrected (Fatal) error received: id=5401 : pcieport 0000:52:02.0: Root Port link has been reset It externally appears to be working, but internally issues some accesses to PCI_ERR_ROOT_COMMAND/STATUS registers that is for root port so not available on downstream port. This patch introduces default_downstream_reset_link that is a version of aer_root_reset() with no accesses to root port's register. It is used for downstream ports that has no reset_link function its specific. This patch also updates related description in pcieaer-howto.txt. Some minor fixes are included. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- Documentation/PCI/pcieaer-howto.txt | 15 +++---- drivers/pci/pcie/aer/aerdrv.c | 23 +---------- drivers/pci/pcie/aer/aerdrv.h | 1 + drivers/pci/pcie/aer/aerdrv_core.c | 78 +++++++++++++++++++++++++++++-------- 4 files changed, 71 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/Documentation/PCI/pcieaer-howto.txt b/Documentation/PCI/pcieaer-howto.txt index 8c406a496799..26d3d945c3c2 100644 --- a/Documentation/PCI/pcieaer-howto.txt +++ b/Documentation/PCI/pcieaer-howto.txt @@ -13,7 +13,7 @@ Reporting (AER) driver and provides information on how to use it, as well as how to enable the drivers of endpoint devices to conform with PCI Express AER driver. -1.2 Copyright © Intel Corporation 2006. +1.2 Copyright (C) Intel Corporation 2006. 1.3 What is the PCI Express AER Driver? @@ -108,7 +108,7 @@ but the PCI Express link itself is fully functional. Fatal errors, on the other hand, cause the link to be unreliable. When AER is enabled, a PCI Express device will automatically send an -error message to the PCIE root port above it when the device captures +error message to the PCIe root port above it when the device captures an error. The Root Port, upon receiving an error reporting message, internally processes and logs the error message in its PCI Express capability structure. Error information being logged includes storing @@ -194,8 +194,9 @@ to reset link, AER port service driver is required to provide the function to reset link. Firstly, kernel looks for if the upstream component has an aer driver. If it has, kernel uses the reset_link callback of the aer driver. If the upstream component has no aer driver -and the port is downstream port, we will use the aer driver of the -root port who reports the AER error. As for upstream ports, +and the port is downstream port, we will perform a hot reset as the +default by setting the Secondary Bus Reset bit of the Bridge Control +register associated with the downstream port. As for upstream ports, they should provide their own aer service drivers with reset_link function. If error_detected returns PCI_ERS_RESULT_CAN_RECOVER and reset_link returns PCI_ERS_RESULT_RECOVERED, the error handling goes @@ -249,11 +250,11 @@ cleanup uncorrectable status register. Pls. refer to section 3.3. 4. Software error injection -Debugging PCIE AER error recovery code is quite difficult because it +Debugging PCIe AER error recovery code is quite difficult because it is hard to trigger real hardware errors. Software based error -injection can be used to fake various kinds of PCIE errors. +injection can be used to fake various kinds of PCIe errors. -First you should enable PCIE AER software error injection in kernel +First you should enable PCIe AER software error injection in kernel configuration, that is, following item should be in your .config. CONFIG_PCIEAER_INJECT=y or CONFIG_PCIEAER_INJECT=m diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index cbc7cc77b2c3..a225d58c1ac8 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -341,7 +341,6 @@ static int __devinit aer_probe(struct pcie_device *dev) **/ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) { - u16 p2p_ctrl; u32 reg32; int pos; @@ -352,27 +351,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK; pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32); - /* Assert Secondary Bus Reset */ - pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); - p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); - - /* - * we should send hot reset message for 2ms to allow it time to - * propogate to all downstream ports - */ - msleep(2); - - /* De-assert Secondary Bus Reset */ - p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; - pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); - - /* - * System software must wait for at least 100ms from the end - * of a reset of one or more device before it is permitted - * to issue Configuration Requests to those devices. - */ - msleep(200); + aer_do_secondary_bus_reset(dev); dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n"); /* Clear Root Error Status */ diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index d0f8291c5ca0..7aaae2d2bd67 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -114,6 +114,7 @@ static inline pci_ers_result_t merge_result(enum pci_ers_result orig, } extern struct bus_type pcie_port_bus_type; +extern void aer_do_secondary_bus_reset(struct pci_dev *dev); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 8fb14aeb74dd..ce42cac99dd3 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -373,6 +373,53 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, return result_data.result; } +/** + * aer_do_secondary_bus_reset - perform secondary bus reset + * @dev: pointer to bridge's pci_dev data structure + * + * Invoked when performing link reset at Root Port or Downstream Port. + */ +void aer_do_secondary_bus_reset(struct pci_dev *dev) +{ + u16 p2p_ctrl; + + /* Assert Secondary Bus Reset */ + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl); + p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); + + /* + * we should send hot reset message for 2ms to allow it time to + * propagate to all downstream ports + */ + msleep(2); + + /* De-assert Secondary Bus Reset */ + p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl); + + /* + * System software must wait for at least 100ms from the end + * of a reset of one or more device before it is permitted + * to issue Configuration Requests to those devices. + */ + msleep(200); +} + +/** + * default_downstream_reset_link - default reset function for Downstream Port + * @dev: pointer to downstream port's pci_dev data structure + * + * Invoked when performing link reset at Downstream Port w/ no aer driver. + */ +static pci_ers_result_t default_downstream_reset_link(struct pci_dev *dev) +{ + aer_do_secondary_bus_reset(dev); + dev_printk(KERN_DEBUG, &dev->dev, + "Downstream Port link has been reset\n"); + return PCI_ERS_RESULT_RECOVERED; +} + static int find_aer_service_iter(struct device *device, void *data) { struct pcie_port_service_driver *service_driver, **drv; @@ -406,31 +453,28 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, pci_ers_result_t status; struct pcie_port_service_driver *driver; - if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) + if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { + /* Reset this port for all subordinates */ udev = dev; - else + } else { + /* Reset the upstream component (likely downstream port) */ udev = dev->bus->self; + } /* Use the aer driver of the component firstly */ driver = find_aer_service(udev); - /* - * If it hasn't the driver and is downstream port, use the root port's - */ - if (!driver || !driver->reset_link) { - if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM && - aerdev->device.driver && - to_service_driver(aerdev->device.driver)->reset_link) { - driver = to_service_driver(aerdev->device.driver); - } else { - dev_printk(KERN_DEBUG, &dev->dev, - "no link-reset support at upstream device %s\n", - pci_name(udev)); - return PCI_ERS_RESULT_DISCONNECT; - } + if (driver && driver->reset_link) { + status = driver->reset_link(udev); + } else if (udev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) { + status = default_downstream_reset_link(udev); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + "no link-reset support at upstream device %s\n", + pci_name(udev)); + return PCI_ERS_RESULT_DISCONNECT; } - status = driver->reset_link(udev); if (status != PCI_ERS_RESULT_RECOVERED) { dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream device %s failed\n", -- cgit v1.2.3 From f6d3780061283039de33b402c35c3bf9322afe14 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:22:11 +0900 Subject: PCI: aerdrv: trivial cleanup for aerdrv.c Skip zero-ing in aer_alloc_rpc() since it is allocated by kzalloc(). The closing comment marker "*/" is recommended for kernel-doc comments. Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index a225d58c1ac8..484cc55194b8 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -185,7 +185,7 @@ static void aer_disable_rootport(struct aer_rpc *rpc) * @context: pointer to Root Port data structure * * Invoked when Root Port detects AER messages. - **/ + */ irqreturn_t aer_irq(int irq, void *context) { unsigned int status, id; @@ -242,7 +242,7 @@ EXPORT_SYMBOL_GPL(aer_irq); * @dev: pointer to the pcie_dev data structure * * Invoked when Root Port's AER service is loaded. - **/ + */ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) { struct aer_rpc *rpc; @@ -251,15 +251,11 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) if (!rpc) return NULL; - /* - * Initialize Root lock access, e_lock, to Root Error Status Reg, - * Root Error ID Reg, and Root error producer/consumer index. - */ + /* Initialize Root lock access, e_lock, to Root Error Status Reg */ spin_lock_init(&rpc->e_lock); rpc->rpd = dev; INIT_WORK(&rpc->dpc_handler, aer_isr); - rpc->prod_idx = rpc->cons_idx = 0; mutex_init(&rpc->rpc_mutex); init_waitqueue_head(&rpc->wait_release); @@ -274,7 +270,7 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) * @dev: pointer to the pcie_dev data structure * * Invoked when PCI Express bus unloads or AER probe fails. - **/ + */ static void aer_remove(struct pcie_device *dev) { struct aer_rpc *rpc = get_service_data(dev); @@ -298,7 +294,7 @@ static void aer_remove(struct pcie_device *dev) * @id: pointer to the service id data structure * * Invoked when PCI Express bus loads AER service driver. - **/ + */ static int __devinit aer_probe(struct pcie_device *dev) { int status; @@ -338,7 +334,7 @@ static int __devinit aer_probe(struct pcie_device *dev) * @dev: pointer to Root Port's pci_dev data structure * * Invoked by Port Bus driver when performing link reset at Root Port. - **/ + */ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) { u32 reg32; @@ -372,7 +368,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev) * @error: error severity being notified by port bus * * Invoked by Port Bus driver during error recovery. - **/ + */ static pci_ers_result_t aer_error_detected(struct pci_dev *dev, enum pci_channel_state error) { @@ -385,7 +381,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev, * @dev: pointer to Root Port's pci_dev data structure * * Invoked by Port Bus driver during nonfatal recovery. - **/ + */ static void aer_error_resume(struct pci_dev *dev) { int pos; @@ -412,7 +408,7 @@ static void aer_error_resume(struct pci_dev *dev) * aer_service_init - register AER root service driver * * Invoked when AER root service driver is loaded. - **/ + */ static int __init aer_service_init(void) { if (pcie_aer_disable) @@ -426,7 +422,7 @@ static int __init aer_service_init(void) * aer_service_exit - unregister AER root service driver * * Invoked when AER root service driver is unloaded. - **/ + */ static void __exit aer_service_exit(void) { pcie_port_service_unregister(&aerdriver); -- cgit v1.2.3 From caa5afbd4831c649b951ae1227a7985f47547e31 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 15 Apr 2010 13:23:17 +0900 Subject: PCI: aerdrv: trivial cleanup for aerdrv_core.c Style cleanup for pci_{en,dis}able_pcie_error_reporting(). Signed-off-by: Hidetoshi Seto Reviewed-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index ce42cac99dd3..df2d686fe3dd 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -47,13 +47,12 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev) if (!pos) return -EIO; - pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); - reg16 = reg16 | - PCI_EXP_DEVCTL_CERE | + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16); + reg16 |= (PCI_EXP_DEVCTL_CERE | PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_URRE; - pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); + PCI_EXP_DEVCTL_URRE); + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16); return 0; } @@ -71,12 +70,12 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev) if (!pos) return -EIO; - pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); - reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE | - PCI_EXP_DEVCTL_NFERE | - PCI_EXP_DEVCTL_FERE | - PCI_EXP_DEVCTL_URRE); - pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16); + reg16 &= ~(PCI_EXP_DEVCTL_CERE | + PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_URRE); + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16); return 0; } -- cgit v1.2.3 From e4146bb9088c01c8b6e82be11f0c371f8aff023c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sun, 16 May 2010 02:28:49 +0100 Subject: PCI: Disable MSI for MCP55 on P5N32-E SLI As reported in , MSI appears to be broken for this on-board device. We already have a quirk for the P5N32-SLI Premium; extend it to cover both variants of the board. Reported-by: Romain DEGEZ Signed-off-by: Ben Hutchings Cc: stable@kernel.org Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 27c0e6eb7136..480782530218 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2218,15 +2218,16 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_SERVERWORKS, DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, ht_enable_msi_mapping); -/* The P5N32-SLI Premium motherboard from Asus has a problem with msi +/* The P5N32-SLI motherboards from Asus have a problem with msi * for the MCP55 NIC. It is not yet determined whether the msi problem * also affects other devices. As for now, turn off msi for this device. */ static void __devinit nvenet_msi_disable(struct pci_dev *dev) { - if (dmi_name_in_vendors("P5N32-SLI PREMIUM")) { + if (dmi_name_in_vendors("P5N32-SLI PREMIUM") || + dmi_name_in_vendors("P5N32-E SLI")) { dev_info(&dev->dev, - "Disabling msi for MCP55 NIC on P5N32-SLI Premium\n"); + "Disabling msi for MCP55 NIC on P5N32-SLI\n"); dev->no_msi = 1; } } -- cgit v1.2.3 From 9313ff450400e6a2ab10fe6b9bdb12a828329410 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Tue, 18 May 2010 10:42:53 -0400 Subject: PCI quirks: disable msi on AMD rs4xx internal gfx bridges Doesn't work reliably for internal gfx. Fixes kernel bug https://bugzilla.kernel.org/show_bug.cgi?id=15626. Signed-off-by: Alex Deucher Cc: Stable Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 480782530218..e5861d5a2e4d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2127,6 +2127,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, 0x9602, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ASUSTEK, 0x9602, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AI, 0x9602, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0xa238, quirk_disable_msi); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x5a3f, quirk_disable_msi); /* Go through the list of Hypertransport capabilities and * return 1 if a HT MSI capability is found and enabled */ -- cgit v1.2.3 From ee6583f6e8f8dad4a53985dbabcd7c242d66a6b6 Mon Sep 17 00:00:00 2001 From: Roman Fietze Date: Tue, 18 May 2010 14:45:47 +0200 Subject: PCI: fix typos pci_device_dis/enable to pci_dis/enable_device in comments This fixes all occurrences of pci_enable_device and pci_disable_device in all comments. There are no code changes involved. Signed-off-by: Roman Fietze Signed-off-by: Jesse Barnes --- drivers/edac/amd76x_edac.c | 2 +- drivers/edac/i82443bxgx_edac.c | 2 +- drivers/edac/r82600_edac.c | 2 +- drivers/pci/pci.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/edac/amd76x_edac.c b/drivers/edac/amd76x_edac.c index f2330f81cb5e..cace0a7b707a 100644 --- a/drivers/edac/amd76x_edac.c +++ b/drivers/edac/amd76x_edac.c @@ -294,7 +294,7 @@ static int __devinit amd76x_init_one(struct pci_dev *pdev, { debugf0("%s()\n", __func__); - /* don't need to call pci_device_enable() */ + /* don't need to call pci_enable_device() */ return amd76x_probe1(pdev, ent->driver_data); } diff --git a/drivers/edac/i82443bxgx_edac.c b/drivers/edac/i82443bxgx_edac.c index 7f3884fcbd46..2bf2c5051bfe 100644 --- a/drivers/edac/i82443bxgx_edac.c +++ b/drivers/edac/i82443bxgx_edac.c @@ -354,7 +354,7 @@ static int __devinit i82443bxgx_edacmc_init_one(struct pci_dev *pdev, debugf0("MC: " __FILE__ ": %s()\n", __func__); - /* don't need to call pci_device_enable() */ + /* don't need to call pci_enable_device() */ rc = i82443bxgx_edacmc_probe1(pdev, ent->driver_data); if (mci_pdev == NULL) diff --git a/drivers/edac/r82600_edac.c b/drivers/edac/r82600_edac.c index d55f8e9de788..6a822c631ef5 100644 --- a/drivers/edac/r82600_edac.c +++ b/drivers/edac/r82600_edac.c @@ -354,7 +354,7 @@ static int __devinit r82600_init_one(struct pci_dev *pdev, { debugf0("%s()\n", __func__); - /* don't need to call pci_device_enable() */ + /* don't need to call pci_enable_device() */ return r82600_probe1(pdev, ent->driver_data); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 60fcb6f02c91..264c3f6b8476 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1193,7 +1193,7 @@ void pci_disable_enabled_device(struct pci_dev *dev) * anymore. This only involves disabling PCI bus-mastering, if active. * * Note we don't actually disable the device until all callers of - * pci_device_enable() have called pci_device_disable(). + * pci_enable_device() have called pci_disable_device(). */ void pci_disable_device(struct pci_dev *dev) -- cgit v1.2.3 From 5030718ee465c759c2a851851e79039f58b9efb3 Mon Sep 17 00:00:00 2001 From: Prarit Bhargava Date: Mon, 17 May 2010 14:25:14 -0400 Subject: PCI: output FW warning in pci_read/write_vpd pci_read/write_vpd() can fail due to a timeout. Usually the command times out because of firmware issues (incorrect vpd length, etc.) on the PCI card. Currently, the timeout occurs silently. Output a message to the user indicating that they should check with their vendor for new firmware. Reviewed-by: Randy Dunlap Signed-off-by: Prarit Bhargava Signed-off-by: Jesse Barnes --- drivers/pci/access.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index affb83b42ebb..531bc697d800 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -220,8 +220,13 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev) return 0; } - if (time_after(jiffies, timeout)) + 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."); return -ETIMEDOUT; + } if (fatal_signal_pending(current)) return -EINTR; if (!cond_resched()) -- cgit v1.2.3 From 8f6bce3c4f48bd79b57d6ac9f337f5aabee43ea7 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 May 2010 23:18:16 +0200 Subject: PCI hotplug: Use kmemdup Use kmemdup when some other buffer is immediately copied into the allocated region. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression from,to,size,flag; statement S; @@ - to = \(kmalloc\|kzalloc\)(size,flag); + to = kmemdup(from,size,flag); if (to==NULL || ...) S - memcpy(to, from, size); // Signed-off-by: Julia Lawall Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/cpqphp_core.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index f184d1d2ecbe..cb7818ffd5cf 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -1075,13 +1075,12 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* make our own copy of the pci bus structure, * as we like tweaking it a lot */ - ctrl->pci_bus = kmalloc(sizeof(*ctrl->pci_bus), GFP_KERNEL); + ctrl->pci_bus = kmemdup(pdev->bus, sizeof(*ctrl->pci_bus), GFP_KERNEL); if (!ctrl->pci_bus) { err("out of memory\n"); rc = -ENOMEM; goto err_free_ctrl; } - memcpy(ctrl->pci_bus, pdev->bus, sizeof(*ctrl->pci_bus)); ctrl->bus = pdev->bus->number; ctrl->rev = pdev->revision; -- cgit v1.2.3 From 3322340a9db2251ac9d09bc7b8d49e872298ae95 Mon Sep 17 00:00:00 2001 From: Felix Radensky Date: Sun, 28 Mar 2010 16:02:02 +0300 Subject: PCI: Allow manual resource allocation for PCI hotplug bridges At the moment only PCI-E briges can be flagged as hotplug, thus allowing manual resource preallocation via pci=hpmemsize=nnM and pci=hpiosize=nnM kernel parameters. Some PCI hotplug bridges, e.g. PLX 6254 can also benefit from this functionalily, as kernel fails to properly allocate their resources when hotplug device is added and PCI bus is rescanned. This patch adds header quirk for PLX 6254 that marks this bridge as hotplug. Other PCI bridges with similar problems can use it as well. Signed-off-by: Felix Radensky Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e5861d5a2e4d..b7512cf08c58 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2554,6 +2554,19 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1518, quirk_i82576_sriov); #endif /* CONFIG_PCI_IOV */ +/* Allow manual resource allocation for PCI hotplug bridges + * via pci=hpmemsize=nnM and pci=hpiosize=nnM parameters. For + * some PCI-PCI hotplug bridges, like PLX 6254 (former HINT HB6), + * kernel fails to allocate resources when hotplug device is + * inserted and PCI bus is rescanned. + */ +static void __devinit quirk_hotplug_bridge(struct pci_dev *dev) +{ + dev->is_hotplug_bridge = 1; +} + +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_HINT, 0x0020, quirk_hotplug_bridge); + /* * This is a quirk for the Ricoh MMC controller found as a part of * some mulifunction chips. -- cgit v1.2.3 From ac81860ea073daed50246af54db706c6e491f240 Mon Sep 17 00:00:00 2001 From: Praveen Kalamegham Date: Wed, 19 May 2010 17:03:12 -0500 Subject: PCI: hotplug: pciehp: Removed check for hotplug of display devices Removed check to prevent hotplug of display devices within pciehp. Originally this was thought to have been required within the PCI Hotplug specification for some legacy devices. However there is no such requirement in the most recent revision. The check prevents hotplug of not only display devices but also computational GPUs which require serviceability. Signed-off-by: Praveen Kalamegham Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_pci.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 0a16444c14c9..2fce726758d2 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -84,12 +84,6 @@ int pciehp_configure_device(struct slot *p_slot) dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); if (!dev) continue; - if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { - ctrl_err(ctrl, "Cannot hot-add display device %s\n", - pci_name(dev)); - pci_dev_put(dev); - continue; - } if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { pciehp_add_bridge(dev); @@ -133,15 +127,9 @@ int pciehp_unconfigure_device(struct slot *p_slot) presence = 0; for (j = 0; j < 8; j++) { - struct pci_dev* temp = pci_get_slot(parent, PCI_DEVFN(0, j)); + struct pci_dev *temp = pci_get_slot(parent, PCI_DEVFN(0, j)); if (!temp) continue; - if ((temp->class >> 16) == PCI_BASE_CLASS_DISPLAY) { - ctrl_err(ctrl, "Cannot remove display device %s\n", - pci_name(temp)); - pci_dev_put(temp); - continue; - } if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); if (bctl & PCI_BRIDGE_CTL_VGA) { @@ -149,7 +137,8 @@ int pciehp_unconfigure_device(struct slot *p_slot) "Cannot remove display device %s\n", pci_name(temp)); pci_dev_put(temp); - continue; + rc = EINVAL; + break; } } pci_remove_bus_device(temp); -- cgit v1.2.3