summaryrefslogtreecommitdiff
path: root/arch/powerpc/platforms/pseries
diff options
context:
space:
mode:
authorGaurav Batra <gbatra@linux.vnet.ibm.com>2023-06-13 12:16:41 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2023-06-26 14:57:31 +1000
commitd61cd13e732c0eaa7d66b45edb2d0de8eab65a1e (patch)
treed963c5d50e6222c8d3d5d57cef0670d6cfbd144a /arch/powerpc/platforms/pseries
parentbfd8d989210cb6bb1c8e87b7c525831dceb91418 (diff)
powerpc/iommu: TCEs are incorrectly manipulated with DLPAR add/remove of memory
When memory is dynamically added/removed, iommu_mem_notifier() is invoked. This routine traverses through all the DMA windows (DDW only, not default windows) to add/remove "direct" TCE mappings. The routines for this purpose are tce_clearrange_multi_pSeriesLP() and tce_clearrange_multi_pSeriesLP(). Both these routines are designed for Direct mapped DMA windows only. The issue is that there could be some DMA windows in the list which are not "direct" mapped. Calling these routines will either, 1) remove some dynamically mapped TCEs, Or 2) try to add TCEs which are out of bounds and HCALL returns H_PARAMETER Here are the side affects when these routines are incorrectly invoked for "dynamically" mapped DMA windows. tce_setrange_multi_pSeriesLP() This adds direct mapped TCEs. Now, this could invoke HCALL to add TCEs with out-of-bound range. In this scenario, HCALL will return H_PARAMETER and DLAR ADD of memory will fail. tce_clearrange_multi_pSeriesLP() This will remove range of TCEs. The TCE range that is calculated, depending on the memory range being added, could infact be mapping some other memory address (for dynamic DMA window scenario). This will wipe out those TCEs. The solution is for iommu_mem_notifier() to only invoke these routines for "direct" mapped DMA windows. Signed-off-by: Gaurav Batra <gbatra@linux.vnet.ibm.com> Reviewed-by: Brian King <brking@linux.vnet.ibm.com> [mpe: Initialise direct at allocation time in ddw_list_new_entry()] Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://msgid.link/20230613171641.15641-1-gbatra@linux.vnet.ibm.com
Diffstat (limited to 'arch/powerpc/platforms/pseries')
-rw-r--r--arch/powerpc/platforms/pseries/iommu.c18
1 files changed, 14 insertions, 4 deletions
diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c
index d59e8a98a200..d593a7227dc9 100644
--- a/arch/powerpc/platforms/pseries/iommu.c
+++ b/arch/powerpc/platforms/pseries/iommu.c
@@ -372,6 +372,7 @@ struct dynamic_dma_window_prop {
struct dma_win {
struct device_node *device;
const struct dynamic_dma_window_prop *prop;
+ bool direct;
struct list_head list;
};
@@ -948,6 +949,7 @@ static struct dma_win *ddw_list_new_entry(struct device_node *pdn,
window->device = pdn;
window->prop = dma64;
+ window->direct = false;
return window;
}
@@ -1418,6 +1420,8 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn)
goto out_del_prop;
if (direct_mapping) {
+ window->direct = true;
+
/* DDW maps the whole partition, so enable direct DMA mapping */
ret = walk_system_ram_range(0, memblock_end_of_DRAM() >> PAGE_SHIFT,
win64->value, tce_setrange_multi_pSeriesLP_walk);
@@ -1434,6 +1438,8 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn)
int i;
unsigned long start = 0, end = 0;
+ window->direct = false;
+
for (i = 0; i < ARRAY_SIZE(pci->phb->mem_resources); i++) {
const unsigned long mask = IORESOURCE_MEM_64 | IORESOURCE_MEM;
@@ -1596,8 +1602,10 @@ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action,
case MEM_GOING_ONLINE:
spin_lock(&dma_win_list_lock);
list_for_each_entry(window, &dma_win_list, list) {
- ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn,
- arg->nr_pages, window->prop);
+ if (window->direct) {
+ ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn,
+ arg->nr_pages, window->prop);
+ }
/* XXX log error */
}
spin_unlock(&dma_win_list_lock);
@@ -1606,8 +1614,10 @@ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action,
case MEM_OFFLINE:
spin_lock(&dma_win_list_lock);
list_for_each_entry(window, &dma_win_list, list) {
- ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn,
- arg->nr_pages, window->prop);
+ if (window->direct) {
+ ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn,
+ arg->nr_pages, window->prop);
+ }
/* XXX log error */
}
spin_unlock(&dma_win_list_lock);