]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
powerpc/pseries/iommu: memory notifier incorrectly adds TCEs for pmemory
authorGaurav Batra <gbatra@linux.ibm.com>
Thu, 30 Jan 2025 18:38:54 +0000 (12:38 -0600)
committerMadhavan Srinivasan <maddy@linux.ibm.com>
Tue, 11 Feb 2025 06:09:10 +0000 (11:39 +0530)
iommu_mem_notifier() is invoked when RAM is dynamically added/removed. This
notifier call is responsible to add/remove TCEs from the Dynamic DMA Window
(DDW) when TCEs are pre-mapped. TCEs are pre-mapped only for RAM and not
for persistent memory (pmemory). For DMA buffers in pmemory, TCEs are
dynamically mapped when the device driver instructs to do so.

The issue is 'daxctl' command is capable of adding pmemory as "System RAM"
after LPAR boot. The command to do so is -

daxctl reconfigure-device --mode=system-ram dax0.0 --force

This will dynamically add pmemory range to LPAR RAM eventually invoking
iommu_mem_notifier(). The address range of pmemory is way beyond the Max
RAM that the LPAR can have. Which means, this range is beyond the DDW
created for the device, at device initialization time.

As a result when TCEs are pre-mapped for the pmemory range, by
iommu_mem_notifier(), PHYP HCALL returns H_PARAMETER. This failed the
command, daxctl, to add pmemory as RAM.

The solution is to not pre-map TCEs for pmemory.

Signed-off-by: Gaurav Batra <gbatra@linux.ibm.com>
Tested-by: Donet Tom <donettom@linux.ibm.com>
Reviewed-by: Donet Tom <donettom@linux.ibm.com>
Signed-off-by: Madhavan Srinivasan <maddy@linux.ibm.com>
Link: https://patch.msgid.link/20250130183854.92258-1-gbatra@linux.ibm.com
arch/powerpc/include/asm/mmzone.h
arch/powerpc/mm/numa.c
arch/powerpc/platforms/pseries/iommu.c

index d99863cd6cde48b51242056562c1db4e535c0592..049152f8d597a6948f458044634679057e3577b1 100644 (file)
@@ -29,6 +29,7 @@ extern cpumask_var_t node_to_cpumask_map[];
 #ifdef CONFIG_MEMORY_HOTPLUG
 extern unsigned long max_pfn;
 u64 memory_hotplug_max(void);
+u64 hot_add_drconf_memory_max(void);
 #else
 #define memory_hotplug_max() memblock_end_of_DRAM()
 #endif
index 3c1da08304d0329a016cbf6535883a3e2b002421..603a0f652ba61cdad7aa23bb3d4589e672b45410 100644 (file)
@@ -1336,7 +1336,7 @@ int hot_add_scn_to_nid(unsigned long scn_addr)
        return nid;
 }
 
-static u64 hot_add_drconf_memory_max(void)
+u64 hot_add_drconf_memory_max(void)
 {
        struct device_node *memory = NULL;
        struct device_node *dn = NULL;
index eb0199cc2fdfb8f99406bd6ccd875bdc96294eb1..d6ebc19fb99c5179aba715b85f1b284b54406258 100644 (file)
@@ -1285,17 +1285,13 @@ static LIST_HEAD(failed_ddw_pdn_list);
 
 static phys_addr_t ddw_memory_hotplug_max(void)
 {
-       resource_size_t max_addr = memory_hotplug_max();
-       struct device_node *memory;
+       resource_size_t max_addr;
 
-       for_each_node_by_type(memory, "memory") {
-               struct resource res;
-
-               if (of_address_to_resource(memory, 0, &res))
-                       continue;
-
-               max_addr = max_t(resource_size_t, max_addr, res.end + 1);
-       }
+#if defined(CONFIG_NUMA) && defined(CONFIG_MEMORY_HOTPLUG)
+       max_addr = hot_add_drconf_memory_max();
+#else
+       max_addr = memblock_end_of_DRAM();
+#endif
 
        return max_addr;
 }
@@ -1672,7 +1668,7 @@ static bool enable_ddw(struct pci_dev *dev, struct device_node *pdn, u64 dma_mas
 
        if (direct_mapping) {
                /* DDW maps the whole partition, so enable direct DMA mapping */
-               ret = walk_system_ram_range(0, memblock_end_of_DRAM() >> PAGE_SHIFT,
+               ret = walk_system_ram_range(0, ddw_memory_hotplug_max() >> PAGE_SHIFT,
                                            win64->value, tce_setrange_multi_pSeriesLP_walk);
                if (ret) {
                        dev_info(&dev->dev, "failed to map DMA window for %pOF: %d\n",
@@ -2427,11 +2423,17 @@ static int iommu_mem_notifier(struct notifier_block *nb, unsigned long action,
        struct memory_notify *arg = data;
        int ret = 0;
 
+       /* This notifier can get called when onlining persistent memory as well.
+        * TCEs are not pre-mapped for persistent memory. Persistent memory will
+        * always be above ddw_memory_hotplug_max()
+        */
+
        switch (action) {
        case MEM_GOING_ONLINE:
                spin_lock(&dma_win_list_lock);
                list_for_each_entry(window, &dma_win_list, list) {
-                       if (window->direct) {
+                       if (window->direct && (arg->start_pfn << PAGE_SHIFT) <
+                               ddw_memory_hotplug_max()) {
                                ret |= tce_setrange_multi_pSeriesLP(arg->start_pfn,
                                                arg->nr_pages, window->prop);
                        }
@@ -2443,7 +2445,8 @@ 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) {
-                       if (window->direct) {
+                       if (window->direct && (arg->start_pfn << PAGE_SHIFT) <
+                               ddw_memory_hotplug_max()) {
                                ret |= tce_clearrange_multi_pSeriesLP(arg->start_pfn,
                                                arg->nr_pages, window->prop);
                        }