mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-29 23:24:11 +08:00
powerpc/pseries/iommu: Allow bigger 64bit window by removing default DMA window
On LoPAR "DMA Window Manipulation Calls", it's recommended to remove the default DMA window for the device, before attempting to configure a DDW, in order to make the maximum resources available for the next DDW to be created. This is a requirement for using DDW on devices in which hypervisor allows only one DMA window. If setting up a new DDW fails anywhere after the removal of this default DMA window, it's needed to restore the default DMA window. For this, an implementation of ibm,reset-pe-dma-windows rtas call is needed: Platforms supporting the DDW option starting with LoPAR level 2.7 implement ibm,ddw-extensions. The first extension available (index 2) carries the token for ibm,reset-pe-dma-windows rtas call, which is used to restore the default DMA window for a device, if it has been deleted. It does so by resetting the TCE table allocation for the PE to it's boot time value, available in "ibm,dma-window" device tree node. Signed-off-by: Leonardo Bras <leobras.c@gmail.com> Tested-by: David Dai <zdai@linux.vnet.ibm.com> Reviewed-by: Alexey Kardashevskiy <aik@ozlabs.ru> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://lore.kernel.org/r/20200805030455.123024-5-leobras.c@gmail.com
This commit is contained in:
parent
74d0b3994e
commit
8c0d51592f
@ -1066,6 +1066,38 @@ static phys_addr_t ddw_memory_hotplug_max(void)
|
||||
return max_addr;
|
||||
}
|
||||
|
||||
/*
|
||||
* Platforms supporting the DDW option starting with LoPAR level 2.7 implement
|
||||
* ibm,ddw-extensions, which carries the rtas token for
|
||||
* ibm,reset-pe-dma-windows.
|
||||
* That rtas-call can be used to restore the default DMA window for the device.
|
||||
*/
|
||||
static void reset_dma_window(struct pci_dev *dev, struct device_node *par_dn)
|
||||
{
|
||||
int ret;
|
||||
u32 cfg_addr, reset_dma_win;
|
||||
u64 buid;
|
||||
struct device_node *dn;
|
||||
struct pci_dn *pdn;
|
||||
|
||||
ret = ddw_read_ext(par_dn, DDW_EXT_RESET_DMA_WIN, &reset_dma_win);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
dn = pci_device_to_OF_node(dev);
|
||||
pdn = PCI_DN(dn);
|
||||
buid = pdn->phb->buid;
|
||||
cfg_addr = (pdn->busno << 16) | (pdn->devfn << 8);
|
||||
|
||||
ret = rtas_call(reset_dma_win, 3, 1, NULL, cfg_addr, BUID_HI(buid),
|
||||
BUID_LO(buid));
|
||||
if (ret)
|
||||
dev_info(&dev->dev,
|
||||
"ibm,reset-pe-dma-windows(%x) %x %x %x returned %d ",
|
||||
reset_dma_win, cfg_addr, BUID_HI(buid), BUID_LO(buid),
|
||||
ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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,
|
||||
@ -1090,6 +1122,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
|
||||
struct property *win64;
|
||||
struct dynamic_dma_window_prop *ddwprop;
|
||||
struct failed_ddw_pdn *fpdn;
|
||||
bool default_win_removed = false;
|
||||
|
||||
mutex_lock(&direct_window_init_mutex);
|
||||
|
||||
@ -1133,14 +1166,38 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
|
||||
if (ret != 0)
|
||||
goto out_failed;
|
||||
|
||||
/*
|
||||
* If there is no window available, remove the default DMA window,
|
||||
* if it's present. This will make all the resources available to the
|
||||
* new DDW window.
|
||||
* If anything fails after this, we need to restore it, so also check
|
||||
* for extensions presence.
|
||||
*/
|
||||
if (query.windows_available == 0) {
|
||||
/*
|
||||
* no additional windows are available for this device.
|
||||
* We might be able to reallocate the existing window,
|
||||
* trading in for a larger page size.
|
||||
*/
|
||||
dev_dbg(&dev->dev, "no free dynamic windows");
|
||||
goto out_failed;
|
||||
struct property *default_win;
|
||||
int reset_win_ext;
|
||||
|
||||
default_win = of_find_property(pdn, "ibm,dma-window", NULL);
|
||||
if (!default_win)
|
||||
goto out_failed;
|
||||
|
||||
reset_win_ext = ddw_read_ext(pdn, DDW_EXT_RESET_DMA_WIN, NULL);
|
||||
if (reset_win_ext)
|
||||
goto out_failed;
|
||||
|
||||
remove_dma_window(pdn, ddw_avail, default_win);
|
||||
default_win_removed = true;
|
||||
|
||||
/* Query again, to check if the window is available */
|
||||
ret = query_ddw(dev, ddw_avail, &query, pdn);
|
||||
if (ret != 0)
|
||||
goto out_failed;
|
||||
|
||||
if (query.windows_available == 0) {
|
||||
/* no windows are available for this device. */
|
||||
dev_dbg(&dev->dev, "no free dynamic windows");
|
||||
goto out_failed;
|
||||
}
|
||||
}
|
||||
if (query.page_size & 4) {
|
||||
page_shift = 24; /* 16MB */
|
||||
@ -1231,6 +1288,8 @@ out_free_prop:
|
||||
kfree(win64);
|
||||
|
||||
out_failed:
|
||||
if (default_win_removed)
|
||||
reset_dma_window(dev, pdn);
|
||||
|
||||
fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL);
|
||||
if (!fpdn)
|
||||
|
Loading…
Reference in New Issue
Block a user