Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Yijing Wang and committed by
Bjorn Helgaas
ba518e3c b1bd58e4

+16 -28
+16 -28
drivers/pci/hotplug/pciehp_pci.c
··· 39 39 struct pci_dev *dev; 40 40 struct pci_dev *bridge = p_slot->ctrl->pcie->port; 41 41 struct pci_bus *parent = bridge->subordinate; 42 - int num, fn; 42 + int num; 43 43 struct controller *ctrl = p_slot->ctrl; 44 44 45 45 dev = pci_get_slot(parent, PCI_DEVFN(0, 0)); ··· 57 57 return -ENODEV; 58 58 } 59 59 60 - for (fn = 0; fn < 8; fn++) { 61 - dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); 62 - if (!dev) 63 - continue; 60 + list_for_each_entry(dev, &parent->devices, bus_list) 64 61 if ((dev->hdr_type == PCI_HEADER_TYPE_BRIDGE) || 65 62 (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) 66 63 pci_hp_add_bridge(dev); 67 - pci_dev_put(dev); 68 - } 69 64 70 65 pci_assign_unassigned_bridge_resources(bridge); 71 66 72 - for (fn = 0; fn < 8; fn++) { 73 - dev = pci_get_slot(parent, PCI_DEVFN(0, fn)); 74 - if (!dev) 67 + list_for_each_entry(dev, &parent->devices, bus_list) { 68 + if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) 75 69 continue; 76 - if ((dev->class >> 16) == PCI_BASE_CLASS_DISPLAY) { 77 - pci_dev_put(dev); 78 - continue; 79 - } 70 + 80 71 pci_configure_slot(dev); 81 - pci_dev_put(dev); 82 72 } 83 73 84 74 pci_bus_add_devices(parent); ··· 79 89 int pciehp_unconfigure_device(struct slot *p_slot) 80 90 { 81 91 int ret, rc = 0; 82 - int j; 83 92 u8 bctl = 0; 84 93 u8 presence = 0; 94 + struct pci_dev *dev, *temp; 85 95 struct pci_bus *parent = p_slot->ctrl->pcie->port->subordinate; 86 96 u16 command; 87 97 struct controller *ctrl = p_slot->ctrl; ··· 92 102 if (ret) 93 103 presence = 0; 94 104 95 - for (j = 0; j < 8; j++) { 96 - struct pci_dev *temp = pci_get_slot(parent, PCI_DEVFN(0, j)); 97 - if (!temp) 98 - continue; 99 - if (temp->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { 100 - pci_read_config_byte(temp, PCI_BRIDGE_CONTROL, &bctl); 105 + list_for_each_entry_safe(dev, temp, &parent->devices, bus_list) { 106 + pci_dev_get(dev); 107 + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && presence) { 108 + pci_read_config_byte(dev, PCI_BRIDGE_CONTROL, &bctl); 101 109 if (bctl & PCI_BRIDGE_CTL_VGA) { 102 110 ctrl_err(ctrl, 103 111 "Cannot remove display device %s\n", 104 - pci_name(temp)); 105 - pci_dev_put(temp); 112 + pci_name(dev)); 113 + pci_dev_put(dev); 106 114 rc = -EINVAL; 107 115 break; 108 116 } 109 117 } 110 - pci_stop_and_remove_bus_device(temp); 118 + pci_stop_and_remove_bus_device(dev); 111 119 /* 112 120 * Ensure that no new Requests will be generated from 113 121 * the device. 114 122 */ 115 123 if (presence) { 116 - pci_read_config_word(temp, PCI_COMMAND, &command); 124 + pci_read_config_word(dev, PCI_COMMAND, &command); 117 125 command &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_SERR); 118 126 command |= PCI_COMMAND_INTX_DISABLE; 119 - pci_write_config_word(temp, PCI_COMMAND, command); 127 + pci_write_config_word(dev, PCI_COMMAND, command); 120 128 } 121 - pci_dev_put(temp); 129 + pci_dev_put(dev); 122 130 } 123 131 124 132 return rc;