mirror of https://gitee.com/openkylin/linux.git
powerpc/pseries: close DDW race between functions of adapter
Given a PCI device with multiple functions in a DDW capable slot, the following situation can be encountered: When the first function sets a 64-bit DMA mask, enable_ddw() will be called and we can fail to properly configure DDW (the most common reason being the new DMA window's size is not large enough to map all of an LPAR's memory). With the recent changes to DDW, we remove the base window in order to determine if the new window is of sufficient size to cover an LPAR's memory. We correctly replace the base window if we find that not to be the case. However, once we go through and re-configured 32-bit DMA via the IOMMU, the next function of the adapter will go through the same process. And since DDW is a characteristic of the slot itself, we are most likely going to fail again. But to determine we are going to fail the second slot, we again remove the base window -- but that is now in-use by the first function/driver, which might be issuing I/O already. To close this window, keep a list of all the failed struct device_nodes that have failed to configure DDW. If the current device_node is in that list, just fail out immediately and fall back to 32-bit DMA without doing any DDW manipulation. Signed-off-by: Nishanth Aravamudan <nacc@linux.vnet.ibm.com> Signed-off-by: Michael Ellerman <michael@ellerman.id.au>
This commit is contained in:
parent
fb1b55d654
commit
61435690a9
|
@ -924,6 +924,13 @@ static void restore_default_window(struct pci_dev *dev,
|
|||
__restore_default_window(pci_dev_to_eeh_dev(dev), ddw_restore_token);
|
||||
}
|
||||
|
||||
struct failed_ddw_pdn {
|
||||
struct device_node *pdn;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
static LIST_HEAD(failed_ddw_pdn_list);
|
||||
|
||||
/*
|
||||
* If the PE supports dynamic dma windows, and there is space for a table
|
||||
* that can map all pages in a linear offset, then setup such a table,
|
||||
|
@ -951,6 +958,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
|
|||
struct dynamic_dma_window_prop *ddwprop;
|
||||
const void *dma_window = NULL;
|
||||
unsigned long liobn, offset, size;
|
||||
struct failed_ddw_pdn *fpdn;
|
||||
|
||||
mutex_lock(&direct_window_init_mutex);
|
||||
|
||||
|
@ -958,6 +966,18 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
|
|||
if (dma_addr != 0)
|
||||
goto out_unlock;
|
||||
|
||||
/*
|
||||
* If we already went through this for a previous function of
|
||||
* the same device and failed, we don't want to muck with the
|
||||
* DMA window again, as it will race with in-flight operations
|
||||
* and can lead to EEHs. The above mutex protects access to the
|
||||
* list.
|
||||
*/
|
||||
list_for_each_entry(fpdn, &failed_ddw_pdn_list, list) {
|
||||
if (!strcmp(fpdn->pdn->full_name, pdn->full_name))
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
/*
|
||||
* the ibm,ddw-applicable property holds the tokens for:
|
||||
* ibm,query-pe-dma-window
|
||||
|
@ -1114,6 +1134,12 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
|
|||
if (ddw_restore_token)
|
||||
restore_default_window(dev, ddw_restore_token);
|
||||
|
||||
fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL);
|
||||
if (!fpdn)
|
||||
goto out_unlock;
|
||||
fpdn->pdn = pdn;
|
||||
list_add(&fpdn->list, &failed_ddw_pdn_list);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&direct_window_init_mutex);
|
||||
return dma_addr;
|
||||
|
|
Loading…
Reference in New Issue