提交 3ec0c3ec 编写于 作者: L Logan Gunthorpe 提交者: Bjorn Helgaas

PCI/P2PDMA: Avoid pci_get_slot(), which may sleep

In order to use upstream_bridge_distance_warn() from a dma_map function, it
must not sleep. However, pci_get_slot() takes the pci_bus_sem so it might
sleep.

In order to avoid this, try to get the host bridge's device from the first
element in the device list. It should be impossible for the host bridge's
device to go away while references are held on child devices, so the first
element should not be able to change and, thus, this should be safe.

Introduce a static function called pci_host_bridge_dev() to obtain the host
bridge's root device.

Link: https://lore.kernel.org/r/20210610160609.28447-7-logang@deltatee.comSigned-off-by: NLogan Gunthorpe <logang@deltatee.com>
Signed-off-by: NBjorn Helgaas <bhelgaas@google.com>
上级 7e2faa17
...@@ -308,10 +308,41 @@ static const struct pci_p2pdma_whitelist_entry { ...@@ -308,10 +308,41 @@ static const struct pci_p2pdma_whitelist_entry {
{} {}
}; };
/*
* This lookup function tries to find the PCI device corresponding to a given
* host bridge.
*
* It assumes the host bridge device is the first PCI device in the
* bus->devices list and that the devfn is 00.0. These assumptions should hold
* for all the devices in the whitelist above.
*
* This function is equivalent to pci_get_slot(host->bus, 0), however it does
* not take the pci_bus_sem lock seeing __host_bridge_whitelist() must not
* sleep.
*
* For this to be safe, the caller should hold a reference to a device on the
* bridge, which should ensure the host_bridge device will not be freed
* or removed from the head of the devices list.
*/
static struct pci_dev *pci_host_bridge_dev(struct pci_host_bridge *host)
{
struct pci_dev *root;
root = list_first_entry_or_null(&host->bus->devices,
struct pci_dev, bus_list);
if (!root)
return NULL;
if (root->devfn != PCI_DEVFN(0, 0))
return NULL;
return root;
}
static bool __host_bridge_whitelist(struct pci_host_bridge *host, static bool __host_bridge_whitelist(struct pci_host_bridge *host,
bool same_host_bridge, bool warn) bool same_host_bridge, bool warn)
{ {
struct pci_dev *root = pci_get_slot(host->bus, PCI_DEVFN(0, 0)); struct pci_dev *root = pci_host_bridge_dev(host);
const struct pci_p2pdma_whitelist_entry *entry; const struct pci_p2pdma_whitelist_entry *entry;
unsigned short vendor, device; unsigned short vendor, device;
...@@ -320,7 +351,6 @@ static bool __host_bridge_whitelist(struct pci_host_bridge *host, ...@@ -320,7 +351,6 @@ static bool __host_bridge_whitelist(struct pci_host_bridge *host,
vendor = root->vendor; vendor = root->vendor;
device = root->device; device = root->device;
pci_dev_put(root);
for (entry = pci_p2pdma_whitelist; entry->vendor; entry++) { for (entry = pci_p2pdma_whitelist; entry->vendor; entry++) {
if (vendor != entry->vendor || device != entry->device) if (vendor != entry->vendor || device != entry->device)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册