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>
This commit is contained in:
		
							parent
							
								
									b1bd58e448
								
							
						
					
					
						commit
						ba518e3c17
					
				| @ -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; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user