From d905c5df9aef38d63df268f6f5e7b13894f626d3 Mon Sep 17 00:00:00 2001 From: Alexey Kardashevskiy Date: Thu, 21 Nov 2013 17:43:14 +1100 Subject: PPC: POWERNV: move iommu_add_device earlier The current implementation of IOMMU on sPAPR does not use iommu_ops and therefore does not call IOMMU API's bus_set_iommu() which 1) sets iommu_ops for a bus 2) registers a bus notifier Instead, PCI devices are added to IOMMU groups from subsys_initcall_sync(tce_iommu_init) which does basically the same thing without using iommu_ops callbacks. However Freescale PAMU driver (https://lkml.org/lkml/2013/7/1/158) implements iommu_ops and when tce_iommu_init is called, every PCI device is already added to some group so there is a conflict. This patch does 2 things: 1. removes the loop in which PCI devices were added to groups and adds explicit iommu_add_device() calls to add devices as soon as they get the iommu_table pointer assigned to them. 2. moves a bus notifier to powernv code in order to avoid conflict with the notifier from Freescale driver. iommu_add_device() and iommu_del_device() are public now. Signed-off-by: Alexey Kardashevskiy Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'arch/powerpc/platforms/pseries/iommu.c') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index f253361552ae..a80af6c20cba 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -687,7 +687,8 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev) iommu_table_setparms(phb, dn, tbl); PCI_DN(dn)->iommu_table = iommu_init_table(tbl, phb->node); iommu_register_group(tbl, pci_domain_nr(phb->bus), 0); - set_iommu_table_base(&dev->dev, PCI_DN(dn)->iommu_table); + set_iommu_table_base_and_group(&dev->dev, + PCI_DN(dn)->iommu_table); return; } @@ -699,7 +700,8 @@ static void pci_dma_dev_setup_pSeries(struct pci_dev *dev) dn = dn->parent; if (dn && PCI_DN(dn)) - set_iommu_table_base(&dev->dev, PCI_DN(dn)->iommu_table); + set_iommu_table_base_and_group(&dev->dev, + PCI_DN(dn)->iommu_table); else printk(KERN_WARNING "iommu: Device %s has no iommu table\n", pci_name(dev)); @@ -1193,7 +1195,7 @@ static void pci_dma_dev_setup_pSeriesLP(struct pci_dev *dev) pr_debug(" found DMA window, table: %p\n", pci->iommu_table); } - set_iommu_table_base(&dev->dev, pci->iommu_table); + set_iommu_table_base_and_group(&dev->dev, pci->iommu_table); } static int dma_set_mask_pSeriesLP(struct device *dev, u64 dma_mask) -- cgit From e589a4404fa06730355de204d3d136ed9bbc7dea Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Mon, 9 Dec 2013 18:17:01 +1100 Subject: powerpc/iommu: Update constant names to reflect their hardcoded page size The powerpc iommu uses a hardcoded page size of 4K. This patch changes the name of the IOMMU_PAGE_* macros to reflect the hardcoded values. A future patch will use the existing names to support dynamic page sizes. Signed-off-by: Alistair Popple Signed-off-by: Alexey Kardashevskiy Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'arch/powerpc/platforms/pseries/iommu.c') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index a80af6c20cba..1b7531ce0c0c 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -488,7 +488,7 @@ static void iommu_table_setparms(struct pci_controller *phb, tbl->it_busno = phb->bus->number; /* Units of tce entries */ - tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT; + tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT_4K; /* Test if we are going over 2GB of DMA space */ if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) { @@ -499,7 +499,7 @@ static void iommu_table_setparms(struct pci_controller *phb, phb->dma_window_base_cur += phb->dma_window_size; /* Set the tce table size - measured in entries */ - tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT; + tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT_4K; tbl->it_index = 0; tbl->it_blocksize = 16; @@ -540,8 +540,8 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb, tbl->it_base = 0; tbl->it_blocksize = 16; tbl->it_type = TCE_PCI; - tbl->it_offset = offset >> IOMMU_PAGE_SHIFT; - tbl->it_size = size >> IOMMU_PAGE_SHIFT; + tbl->it_offset = offset >> IOMMU_PAGE_SHIFT_4K; + tbl->it_size = size >> IOMMU_PAGE_SHIFT_4K; } static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) -- cgit From 3a553170d35d69bea3877bffa508489dfa6f133d Mon Sep 17 00:00:00 2001 From: Alistair Popple Date: Mon, 9 Dec 2013 18:17:02 +1100 Subject: powerpc/iommu: Add it_page_shift field to determine iommu page size This patch adds a it_page_shift field to struct iommu_table and initiliases it to 4K for all platforms. Signed-off-by: Alistair Popple Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'arch/powerpc/platforms/pseries/iommu.c') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index 1b7531ce0c0c..e0299183ae54 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -486,9 +486,10 @@ static void iommu_table_setparms(struct pci_controller *phb, memset((void *)tbl->it_base, 0, *sizep); tbl->it_busno = phb->bus->number; + tbl->it_page_shift = IOMMU_PAGE_SHIFT_4K; /* Units of tce entries */ - tbl->it_offset = phb->dma_window_base_cur >> IOMMU_PAGE_SHIFT_4K; + tbl->it_offset = phb->dma_window_base_cur >> tbl->it_page_shift; /* Test if we are going over 2GB of DMA space */ if (phb->dma_window_base_cur + phb->dma_window_size > 0x80000000ul) { @@ -499,7 +500,7 @@ static void iommu_table_setparms(struct pci_controller *phb, phb->dma_window_base_cur += phb->dma_window_size; /* Set the tce table size - measured in entries */ - tbl->it_size = phb->dma_window_size >> IOMMU_PAGE_SHIFT_4K; + tbl->it_size = phb->dma_window_size >> tbl->it_page_shift; tbl->it_index = 0; tbl->it_blocksize = 16; @@ -537,11 +538,12 @@ static void iommu_table_setparms_lpar(struct pci_controller *phb, of_parse_dma_window(dn, dma_window, &tbl->it_index, &offset, &size); tbl->it_busno = phb->bus->number; + tbl->it_page_shift = IOMMU_PAGE_SHIFT_4K; tbl->it_base = 0; tbl->it_blocksize = 16; tbl->it_type = TCE_PCI; - tbl->it_offset = offset >> IOMMU_PAGE_SHIFT_4K; - tbl->it_size = size >> IOMMU_PAGE_SHIFT_4K; + tbl->it_offset = offset >> tbl->it_page_shift; + tbl->it_size = size >> tbl->it_page_shift; } static void pci_dma_bus_setup_pSeries(struct pci_bus *bus) -- cgit From ae69e1eddc646ff8dc1d5439005d1f82c33f9ae7 Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Fri, 10 Jan 2014 15:09:38 -0800 Subject: Revert "powerpc/pseries/iommu: remove default window before attempting DDW manipulation" Ben rightfully pointed out that there is a race in the "newer" DDW code. Presuming we are running on recent enough firmware that supports the "reset" DDW manipulation call, we currently always remove the base 32-bit DMA window in order to maximize the resources for Phyp when creating the 64-bit window. However, this can be problematic for the case where multiple functions are in the same PE (partitionable endpoint), where some funtions might be 32-bit DMA only. All of a sudden, the only functional DMA window for such functions is gone. We will have serious errors in such situations. The best solution is simply to revert the extension to the DDW code where we ever remove the base DMA window. This reverts commit 25ebc45b93452d0bc60271f178237123c4b26808. Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 86 +++++++--------------------------- 1 file changed, 17 insertions(+), 69 deletions(-) (limited to 'arch/powerpc/platforms/pseries/iommu.c') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index e0299183ae54..bb2e16c5ae27 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -721,21 +721,6 @@ static int __init disable_ddw_setup(char *str) 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) { struct dynamic_dma_window_prop *dwp; @@ -765,7 +750,15 @@ static void remove_ddw(struct device_node *np) pr_debug("%s successfully cleared tces in window.\n", np->full_name); - __remove_ddw(np, ddw_avail, liobn); + ret = rtas_call(ddw_avail[2], 1, 1, NULL, 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: ret = of_remove_property(np, win64); @@ -925,12 +918,6 @@ static int create_ddw(struct pci_dev *dev, const u32 *ddw_avail, return ret; } -static void restore_default_window(struct pci_dev *dev, - u32 ddw_restore_token) -{ - __restore_default_window(pci_dev_to_eeh_dev(dev), ddw_restore_token); -} - struct failed_ddw_pdn { struct device_node *pdn; struct list_head list; @@ -958,13 +945,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) u64 dma_addr, max_addr; struct device_node *dn; const u32 *uninitialized_var(ddw_avail); - const u32 *uninitialized_var(ddw_extensions); - u32 ddw_restore_token = 0; struct direct_window *window; struct property *win64; 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); @@ -995,42 +978,9 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) */ ddw_avail = of_get_property(pdn, "ibm,ddw-applicable", &len); if (!ddw_avail || len < 3 * sizeof(u32)) - 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; - } - } + goto out_failed; - /* - * 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 * whole partition. Query returns number of windows, largest * block assigned to PE (partition endpoint), and two bitmasks @@ -1039,7 +989,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) dn = pci_device_to_OF_node(dev); ret = query_ddw(dev, ddw_avail, &query); if (ret != 0) - goto out_restore_window; + goto out_failed; if (query.windows_available == 0) { /* @@ -1048,7 +998,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) * trading in for a larger page size. */ dev_dbg(&dev->dev, "no free dynamic windows"); - goto out_restore_window; + goto out_failed; } if (be32_to_cpu(query.page_size) & 4) { page_shift = 24; /* 16MB */ @@ -1059,7 +1009,7 @@ static u64 enable_ddw(struct pci_dev *dev, struct device_node *pdn) } else { dev_dbg(&dev->dev, "no supported direct page size in mask %x", query.page_size); - goto out_restore_window; + goto out_failed; } /* verify the window * number of ptes will map the partition */ /* check largest block * page size > max memory hotplug addr */ @@ -1068,14 +1018,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 " "%llu-sized pages\n", max_addr, query.largest_available_block, 1ULL << page_shift); - goto out_restore_window; + goto out_failed; } len = order_base_2(max_addr); win64 = kzalloc(sizeof(struct property), GFP_KERNEL); if (!win64) { dev_info(&dev->dev, "couldn't allocate property for 64bit dma window\n"); - goto out_restore_window; + goto out_failed; } win64->name = kstrdup(DIRECT64_PROPNAME, GFP_KERNEL); win64->value = ddwprop = kmalloc(sizeof(*ddwprop), GFP_KERNEL); @@ -1137,9 +1087,7 @@ out_free_prop: kfree(win64->value); kfree(win64); -out_restore_window: - if (ddw_restore_token) - restore_default_window(dev, ddw_restore_token); +out_failed: fpdn = kzalloc(sizeof(*fpdn), GFP_KERNEL); if (!fpdn) -- cgit From 97e7dc523acaa335d44517b06ef5609b3ee65c6a Mon Sep 17 00:00:00 2001 From: Nishanth Aravamudan Date: Fri, 10 Jan 2014 15:10:41 -0800 Subject: Revert "pseries/iommu: Remove DDW on kexec" After reverting 25ebc45b93452d0bc60271f178237123c4b26808 ("powerpc/pseries/iommu: remove default window before attempting DDW manipulation"), we no longer remove the base window in enable_ddw. Therefore, we no longer need to reset the DMA window state in find_existing_ddw_windows(). We can instead go back to what was done before, which simply reuses the previous configuration, if any. Further, this removes the final caller of the reset-pe-dma-windows call, so remove those functions. This fixes an EEH on kdump with the ipr driver. The EEH occurs, because the initcall removes the DDW configuration (64-bit DMA window), but doesn't ensure the ops are via the IOMMU -- a DMA operation occurs during probe (still investigating this) and we EEH. This reverts commit 14b6f00f8a4fdec5ccd45a0710284de301a61628. Signed-off-by: Nishanth Aravamudan Signed-off-by: Benjamin Herrenschmidt --- arch/powerpc/platforms/pseries/iommu.c | 63 ++++++++-------------------------- 1 file changed, 14 insertions(+), 49 deletions(-) (limited to 'arch/powerpc/platforms/pseries/iommu.c') diff --git a/arch/powerpc/platforms/pseries/iommu.c b/arch/powerpc/platforms/pseries/iommu.c index bb2e16c5ae27..33b552ffbe57 100644 --- a/arch/powerpc/platforms/pseries/iommu.c +++ b/arch/powerpc/platforms/pseries/iommu.c @@ -787,68 +787,33 @@ static u64 find_existing_ddw(struct device_node *pdn) return dma_addr; } -static void __restore_default_window(struct eeh_dev *edev, - u32 ddw_restore_token) -{ - 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 - */ - 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)); - pr_info("ibm,reset-pe-dma-windows(%x) %x %x %x returned %d\n", - ddw_restore_token, cfg_addr, BUID_HI(buid), BUID_LO(buid), ret); -} - static int find_existing_ddw_windows(void) { + int len; struct device_node *pdn; + struct direct_window *window; const struct dynamic_dma_window_prop *direct64; - const u32 *ddw_extensions; if (!firmware_has_feature(FW_FEATURE_LPAR)) return 0; for_each_node_with_property(pdn, DIRECT64_PROPNAME) { - direct64 = of_get_property(pdn, DIRECT64_PROPNAME, NULL); + direct64 = of_get_property(pdn, DIRECT64_PROPNAME, &len); if (!direct64) continue; - /* - * We need to ensure the IOMMU table is active when we - * return from the IOMMU setup so that the common code - * can clear the table or find the holes. To that end, - * first, remove any existing DDW configuration. - */ - remove_ddw(pdn); + window = kzalloc(sizeof(*window), GFP_KERNEL); + if (!window || len < sizeof(struct dynamic_dma_window_prop)) { + kfree(window); + remove_ddw(pdn); + continue; + } - /* - * Second, if we are running on a new enough level of - * firmware where the restore API is present, use it to - * restore the 32-bit window, which was removed in - * create_ddw. - * If the API is not present, then create_ddw couldn't - * have removed the 32-bit window in the first place, so - * removing the DDW configuration should be sufficient. - */ - ddw_extensions = of_get_property(pdn, "ibm,ddw-extensions", - NULL); - if (ddw_extensions && ddw_extensions[0] > 0) - __restore_default_window(of_node_to_eeh_dev(pdn), - ddw_extensions[1]); + window->device = pdn; + window->prop = direct64; + spin_lock(&direct_window_list_lock); + list_add(&window->list, &direct_window_list); + spin_unlock(&direct_window_list_lock); } return 0; -- cgit