diff options
author | Yijing Wang <wangyijing@huawei.com> | 2013-01-15 11:12:19 +0800 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2013-01-25 09:21:10 -0700 |
commit | ba518e3c177547dfebf7fa7252cea0c850e7ce25 (patch) | |
tree | d4095f6395b8159a8301ee155ce81a5a33f9193b | |
parent | b1bd58e448f28531207124eea3fd43b81d6f8d06 (diff) |
PCI: pciehp: Iterate over all devices in slot, not functions 0-7
Currently, we enumerate devices in a slot with pci_scan_slot(), then
iterate through all the devices we found by looking for functions 0-7. But
that's wrong for ARI devices, which may have function numbers up to 255.
This means that when we hot-add an ARI device, pciehp only initializes
functions 0-7, and other functions don't work correctly. Additionally, if
we hot-remove the device, pciehp only removes functions 0-7, leaving stale
pci_dev structures for any other functions.
This patch fixes the problem by iterating over devices in a slot by using
the upstream bridge's "bus->devices" list instead.
[bhelgaas: changelog]
Signed-off-by: Yijing Wang <wangyijing@huawei.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
-rw-r--r-- | drivers/pci/hotplug/pciehp_pci.c | 44 |
1 files changed, 16 insertions, 28 deletions
diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 09cecaf450c5..aac7a40e4a4a 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -39,7 +39,7 @@ int pciehp_configure_device(struct slot *p_slot) struct pci_dev *dev; struct pci_dev *bridge = p_slot->ctrl->pcie->port; struct pci_bus *parent = bridge->subordinate; - int num, fn; + int num; struct controller *ctrl = p_slot->ctrl; dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); @@ -57,28 +57,18 @@ int pciehp_configure_device(struct slot *p_slot) return -ENODEV; } - for (fn = 0; fn < 8; fn++) { - dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); - if (!dev) - continue; + list_for_each_entry(dev, &parent->devices, bus_list) if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) pci_hp_add_bridge(dev); - pci_dev_put(dev); - } pci_assign_unassigned_bridge_resources(bridge); - for (fn = 0; fn < 8; fn++) { - dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); - if (!dev) + list_for_each_entry(dev, &parent->devices, bus_list) { + if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) continue; - if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { - pci_dev_put(dev); - continue; - } + pci_configure_slot(dev); - pci_dev_put(dev); } pci_bus_add_devices(parent); @@ -89,9 +79,9 @@ int pciehp_configure_device(struct slot *p_slot) int pciehp_unconfigure_device(struct slot *p_slot) { int ret, rc = 0; - int j; u8 bctl = 0; u8 presence = 0; + struct pci_dev *dev, *temp; struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; u16 command; struct controller *ctrl = p_slot->ctrl; @@ -102,33 +92,31 @@ int pciehp_unconfigure_device(struct slot *p_slot) if (ret) presence = 0; - for (j = 0; j < 8; j++) { - struct pci_dev *temp = pci_get_slot(parent, PCI_DEVFN(0, j)); - if (!temp) - continue; - if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { - pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); + list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) { + pci_dev_get(dev); + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { + pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl); if (bctl & PCI_BRIDGE_CTL_VGA) { ctrl_err(ctrl, "Cannot remove display device %s\n", - pci_name(temp)); - pci_dev_put(temp); + pci_name(dev)); + pci_dev_put(dev); rc = -EINVAL; break; } } - pci_stop_and_remove_bus_device(temp); + pci_stop_and_remove_bus_device(dev); /* * Ensure that no new Requests will be generated from * the device. */ if (presence) { - pci_read_config_word(temp, PCI_COMMAND, &command); + pci_read_config_word(dev, PCI_COMMAND, &command); command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); command |= PCI_COMMAND_INTX_DISABLE; - pci_write_config_word(temp, PCI_COMMAND, command); + pci_write_config_word(dev, PCI_COMMAND, command); } - pci_dev_put(temp); + pci_dev_put(dev); } return rc; |