Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0 2 : : #include <linux/pci.h> 3 : : #include <linux/module.h> 4 : : #include "pci.h" 5 : : 6 : 0 : static void pci_free_resources(struct pci_dev *dev) 7 : : { 8 : 0 : int i; 9 : : 10 [ # # ]: 0 : for (i = 0; i < PCI_NUM_RESOURCES; i++) { 11 : 0 : struct resource *res = dev->resource + i; 12 [ # # ]: 0 : if (res->parent) 13 : 0 : release_resource(res); 14 : : } 15 : 0 : } 16 : : 17 : 0 : static void pci_stop_dev(struct pci_dev *dev) 18 : : { 19 : 0 : pci_pme_active(dev, false); 20 : : 21 [ # # ]: 0 : if (pci_dev_is_added(dev)) { 22 : 0 : device_release_driver(&dev->dev); 23 : 0 : pci_proc_detach_device(dev); 24 : 0 : pci_remove_sysfs_dev_files(dev); 25 : : 26 : 0 : pci_dev_assign_added(dev, false); 27 : : } 28 : 0 : } 29 : : 30 : 0 : static void pci_destroy_dev(struct pci_dev *dev) 31 : : { 32 [ # # ]: 0 : if (!dev->dev.kobj.parent) 33 : : return; 34 : : 35 : 0 : device_del(&dev->dev); 36 : : 37 : 0 : down_write(&pci_bus_sem); 38 : 0 : list_del(&dev->bus_list); 39 : 0 : up_write(&pci_bus_sem); 40 : : 41 : 0 : pcie_aspm_exit_link_state(dev); 42 : 0 : pci_bridge_d3_update(dev); 43 : 0 : pci_free_resources(dev); 44 : 0 : put_device(&dev->dev); 45 : : } 46 : : 47 : 0 : void pci_remove_bus(struct pci_bus *bus) 48 : : { 49 : 0 : pci_proc_detach_bus(bus); 50 : : 51 : 0 : down_write(&pci_bus_sem); 52 : 0 : list_del(&bus->node); 53 : 0 : pci_bus_release_busn_res(bus); 54 : 0 : up_write(&pci_bus_sem); 55 [ # # ]: 0 : pci_remove_legacy_files(bus); 56 : : 57 [ # # ]: 0 : if (bus->ops->remove_bus) 58 : 0 : bus->ops->remove_bus(bus); 59 : : 60 : 0 : pcibios_remove_bus(bus); 61 : 0 : device_unregister(&bus->dev); 62 : 0 : } 63 : : EXPORT_SYMBOL(pci_remove_bus); 64 : : 65 : 0 : static void pci_stop_bus_device(struct pci_dev *dev) 66 : : { 67 : 0 : struct pci_bus *bus = dev->subordinate; 68 : 0 : struct pci_dev *child, *tmp; 69 : : 70 : : /* 71 : : * Stopping an SR-IOV PF device removes all the associated VFs, 72 : : * which will update the bus->devices list and confuse the 73 : : * iterator. Therefore, iterate in reverse so we remove the VFs 74 : : * first, then the PF. 75 : : */ 76 [ # # ]: 0 : if (bus) { 77 [ # # ]: 0 : list_for_each_entry_safe_reverse(child, tmp, 78 : : &bus->devices, bus_list) 79 : 0 : pci_stop_bus_device(child); 80 : : } 81 : : 82 : 0 : pci_stop_dev(dev); 83 : 0 : } 84 : : 85 : 0 : static void pci_remove_bus_device(struct pci_dev *dev) 86 : : { 87 : 0 : struct pci_bus *bus = dev->subordinate; 88 : 0 : struct pci_dev *child, *tmp; 89 : : 90 [ # # ]: 0 : if (bus) { 91 [ # # ]: 0 : list_for_each_entry_safe(child, tmp, 92 : : &bus->devices, bus_list) 93 : 0 : pci_remove_bus_device(child); 94 : : 95 : 0 : pci_remove_bus(bus); 96 : 0 : dev->subordinate = NULL; 97 : : } 98 : : 99 : 0 : pci_destroy_dev(dev); 100 : 0 : } 101 : : 102 : : /** 103 : : * pci_stop_and_remove_bus_device - remove a PCI device and any children 104 : : * @dev: the device to remove 105 : : * 106 : : * Remove a PCI device from the device lists, informing the drivers 107 : : * that the device has been removed. We also remove any subordinate 108 : : * buses and children in a depth-first manner. 109 : : * 110 : : * For each device we remove, delete the device structure from the 111 : : * device lists, remove the /proc entry, and notify userspace 112 : : * (/sbin/hotplug). 113 : : */ 114 : 0 : void pci_stop_and_remove_bus_device(struct pci_dev *dev) 115 : : { 116 : 0 : pci_stop_bus_device(dev); 117 : 0 : pci_remove_bus_device(dev); 118 : 0 : } 119 : : EXPORT_SYMBOL(pci_stop_and_remove_bus_device); 120 : : 121 : 0 : void pci_stop_and_remove_bus_device_locked(struct pci_dev *dev) 122 : : { 123 : 0 : pci_lock_rescan_remove(); 124 : 0 : pci_stop_and_remove_bus_device(dev); 125 : 0 : pci_unlock_rescan_remove(); 126 : 0 : } 127 : : EXPORT_SYMBOL_GPL(pci_stop_and_remove_bus_device_locked); 128 : : 129 : 0 : void pci_stop_root_bus(struct pci_bus *bus) 130 : : { 131 : 0 : struct pci_dev *child, *tmp; 132 : 0 : struct pci_host_bridge *host_bridge; 133 : : 134 [ # # ]: 0 : if (!pci_is_root_bus(bus)) 135 : : return; 136 : : 137 : 0 : host_bridge = to_pci_host_bridge(bus->bridge); 138 [ # # ]: 0 : list_for_each_entry_safe_reverse(child, tmp, 139 : : &bus->devices, bus_list) 140 : 0 : pci_stop_bus_device(child); 141 : : 142 : : /* stop the host bridge */ 143 : 0 : device_release_driver(&host_bridge->dev); 144 : : } 145 : : EXPORT_SYMBOL_GPL(pci_stop_root_bus); 146 : : 147 : 0 : void pci_remove_root_bus(struct pci_bus *bus) 148 : : { 149 : 0 : struct pci_dev *child, *tmp; 150 : 0 : struct pci_host_bridge *host_bridge; 151 : : 152 [ # # ]: 0 : if (!pci_is_root_bus(bus)) 153 : : return; 154 : : 155 : 0 : host_bridge = to_pci_host_bridge(bus->bridge); 156 [ # # ]: 0 : list_for_each_entry_safe(child, tmp, 157 : : &bus->devices, bus_list) 158 : 0 : pci_remove_bus_device(child); 159 : 0 : pci_remove_bus(bus); 160 : 0 : host_bridge->bus = NULL; 161 : : 162 : : /* remove the host bridge */ 163 : 0 : device_unregister(&host_bridge->dev); 164 : : } 165 : : EXPORT_SYMBOL_GPL(pci_remove_root_bus);