Commit 25ebc45b authored by Nishanth Aravamudan's avatar Nishanth Aravamudan Committed by Benjamin Herrenschmidt

powerpc/pseries/iommu: remove default window before attempting DDW manipulation

An upcoming release of firmware will add DDW extensions, in particular
an API to "reset" the DMA window to the original configuration (32-bit,
2GB in size). With that API available, we can safely remove the default
window, increasing the resources available to firmware for creation of
larger windows for the slot in question -- if we encounter an error, we
can use the new API to reset the state of the slot.

Further, this same release of firmware will make it a hard requirement
for OSes to release the existing window before any other windows will be
shown as available, to avoid conflicts in addressing between the two
windows.

In anticipation of these changes, always remove the default window
before we do any DDW manipulations.
Signed-off-by: default avatarNishanth Aravamudan <nacc@us.ibm.com>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent 65b8c722
...@@ -707,6 +707,21 @@ static int __init disable_ddw_setup(char *str) ...@@ -707,6 +707,21 @@ static int __init disable_ddw_setup(char *str)
early_param("disable_ddw", disable_ddw_setup); early_param("disable_ddw", disable_ddw_setup);
static inline void __remove_ddw(struct device_node *np, const u32 *ddw_avail, u64 liobn)
{
int ret;
ret = rtas_call(ddw_avail[2], 1, 1, NULL, liobn);
if (ret)
pr_warning("%s: failed to remove DMA window: rtas returned "
"%d to ibm,remove-pe-dma-window(%x) %llx\n",
np->full_name, ret, ddw_avail[2], liobn);
else
pr_debug("%s: successfully removed DMA window: rtas returned "
"%d to ibm,remove-pe-dma-window(%x) %llx\n",
np->full_name, ret, ddw_avail[2], liobn);
}
static void remove_ddw(struct device_node *np) static void remove_ddw(struct device_node *np)
{ {
struct dynamic_dma_window_prop *dwp; struct dynamic_dma_window_prop *dwp;
...@@ -736,15 +751,7 @@ static void remove_ddw(struct device_node *np) ...@@ -736,15 +751,7 @@ static void remove_ddw(struct device_node *np)
pr_debug("%s successfully cleared tces in window.\n", pr_debug("%s successfully cleared tces in window.\n",
np->full_name); np->full_name);
ret = rtas_call(ddw_avail[2], 1, 1, NULL, liobn); __remove_ddw(np, ddw_avail, liobn);
if (ret)
pr_warning("%s: failed to remove direct window: rtas returned "
"%d to ibm,remove-pe-dma-window(%x) %llx\n",
np->full_name, ret, ddw_avail[2], liobn);
else
pr_debug("%s: successfully removed direct window: rtas returned "
"%d to ibm,remove-pe-dma-window(%x) %llx\n",
np->full_name, ret, ddw_avail[2], liobn);
delprop: delprop:
ret = prom_remove_property(np, win64); ret = prom_remove_property(np, win64);
...@@ -869,6 +876,35 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail, ...@@ -869,6 +876,35 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail,
return ret; return ret;
} }
static void restore_default_window(struct pci_dev *dev,
u32 ddw_restore_token, unsigned long liobn)
{
struct eeh_dev *edev;
u32 cfg_addr;
u64 buid;
int ret;
/*
* Get the config address and phb buid of the PE window.
* Rely on eeh to retrieve this for us.
* Retrieve them from the pci device, not the node with the
* dma-window property
*/
edev = pci_dev_to_eeh_dev(dev);
cfg_addr = edev->config_addr;
if (edev->pe_config_addr)
cfg_addr = edev->pe_config_addr;
buid = edev->phb->buid;
do {
ret = rtas_call(ddw_restore_token, 3, 1, NULL, cfg_addr,
BUID_HI(buid), BUID_LO(buid));
} while (rtas_busy_delay(ret));
dev_info(&dev->dev,
"ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n",
ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret);
}
/* /*
* If the PE supports dynamic dma windows, and there is space for a table * 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, * that can map all pages in a linear offset, then setup such a table,
...@@ -889,9 +925,13 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -889,9 +925,13 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
u64 dma_addr, max_addr; u64 dma_addr, max_addr;
struct device_node *dn; struct device_node *dn;
const u32 *uninitialized_var(ddw_avail); const u32 *uninitialized_var(ddw_avail);
const u32 *uninitialized_var(ddw_extensions);
u32 ddw_restore_token = 0;
struct direct_window *window; struct direct_window *window;
struct property *win64; struct property *win64;
struct dynamic_dma_window_prop *ddwprop; struct dynamic_dma_window_prop *ddwprop;
const void *dma_window = NULL;
unsigned long liobn, offset, size;
mutex_lock(&direct_window_init_mutex); mutex_lock(&direct_window_init_mutex);
...@@ -911,7 +951,40 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -911,7 +951,40 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
if (!ddw_avail || len < 3 * sizeof(u32)) if (!ddw_avail || len < 3 * sizeof(u32))
goto out_unlock; goto out_unlock;
/* /*
* the extensions property is only required to exist in certain
* levels of firmware and later
* the ibm,ddw-extensions property is a list with the first
* element containing the number of extensions and each
* subsequent entry is a value corresponding to that extension
*/
ddw_extensions = of_get_property(pdn, "ibm,ddw-extensions", &len);
if (ddw_extensions) {
/*
* each new defined extension length should be added to
* the top of the switch so the "earlier" entries also
* get picked up
*/
switch (ddw_extensions[0]) {
/* ibm,reset-pe-dma-windows */
case 1:
ddw_restore_token = ddw_extensions[1];
break;
}
}
/*
* Only remove the existing DMA window if we can restore back to
* the default state. Removing the existing window maximizes the
* resources available to firmware for dynamic window creation.
*/
if (ddw_restore_token) {
dma_window = of_get_property(pdn, "ibm,dma-window", NULL);
of_parse_dma_window(pdn, dma_window, &liobn, &offset, &size);
__remove_ddw(pdn, ddw_avail, liobn);
}
/*
* Query if there is a second window of size to map the * Query if there is a second window of size to map the
* whole partition. Query returns number of windows, largest * whole partition. Query returns number of windows, largest
* block assigned to PE (partition endpoint), and two bitmasks * block assigned to PE (partition endpoint), and two bitmasks
...@@ -920,7 +993,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -920,7 +993,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
dn = pci_device_to_OF_node(dev); dn = pci_device_to_OF_node(dev);
ret = query_ddw(dev, ddw_avail, &query); ret = query_ddw(dev, ddw_avail, &query);
if (ret != 0) if (ret != 0)
goto out_unlock; goto out_restore_window;
if (query.windows_available == 0) { if (query.windows_available == 0) {
/* /*
...@@ -929,7 +1002,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -929,7 +1002,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
* trading in for a larger page size. * trading in for a larger page size.
*/ */
dev_dbg(&dev->dev, "no free dynamic windows"); dev_dbg(&dev->dev, "no free dynamic windows");
goto out_unlock; goto out_restore_window;
} }
if (query.page_size & 4) { if (query.page_size & 4) {
page_shift = 24; /* 16MB */ page_shift = 24; /* 16MB */
...@@ -940,7 +1013,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -940,7 +1013,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
} else { } else {
dev_dbg(&dev->dev, "no supported direct page size in mask %x", dev_dbg(&dev->dev, "no supported direct page size in mask %x",
query.page_size); query.page_size);
goto out_unlock; goto out_restore_window;
} }
/* verify the window * number of ptes will map the partition */ /* verify the window * number of ptes will map the partition */
/* check largest block * page size > max memory hotplug addr */ /* check largest block * page size > max memory hotplug addr */
...@@ -949,14 +1022,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -949,14 +1022,14 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u " dev_dbg(&dev->dev, "can't map partiton max 0x%llx with %u "
"%llu-sized pages\n", max_addr, query.largest_available_block, "%llu-sized pages\n", max_addr, query.largest_available_block,
1ULL << page_shift); 1ULL << page_shift);
goto out_unlock; goto out_restore_window;
} }
len = order_base_2(max_addr); len = order_base_2(max_addr);
win64 = kzalloc(sizeof(struct property), GFP_KERNEL); win64 = kzalloc(sizeof(struct property), GFP_KERNEL);
if (!win64) { if (!win64) {
dev_info(&dev->dev, dev_info(&dev->dev,
"couldn't allocate property for 64bit dma window\n"); "couldn't allocate property for 64bit dma window\n");
goto out_unlock; goto out_restore_window;
} }
win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL); win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL);
win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL); win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL);
...@@ -1018,6 +1091,10 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) ...@@ -1018,6 +1091,10 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn)
kfree(win64->value); kfree(win64->value);
kfree(win64); kfree(win64);
out_restore_window:
if (ddw_restore_token)
restore_default_window(dev, ddw_restore_token, liobn);
out_unlock: out_unlock:
mutex_unlock(&direct_window_init_mutex); mutex_unlock(&direct_window_init_mutex);
return dma_addr; return dma_addr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment