]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dma-debug: refactor to use physical addresses for page mapping
authorLeon Romanovsky <leonro@nvidia.com>
Tue, 9 Sep 2025 13:27:31 +0000 (16:27 +0300)
committerMarek Szyprowski <m.szyprowski@samsung.com>
Thu, 11 Sep 2025 22:09:51 +0000 (00:09 +0200)
Convert the DMA debug infrastructure from page-based to physical address-based
mapping as a preparation to rely on physical address for DMA mapping routines.

The refactoring renames debug_dma_map_page() to debug_dma_map_phys() and
changes its signature to accept a phys_addr_t parameter instead of struct page
and offset. Similarly, debug_dma_unmap_page() becomes debug_dma_unmap_phys().
A new dma_debug_phy type is introduced to distinguish physical address mappings
from other debug entry types. All callers throughout the codebase are updated
to pass physical addresses directly, eliminating the need for page-to-physical
conversion in the debug layer.

This refactoring eliminates the need to convert between page pointers and
physical addresses in the debug layer, making the code more efficient and
consistent with the DMA mapping API's physical address focus.

Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
Signed-off-by: Leon Romanovsky <leonro@nvidia.com>
[mszyprow: added a fixup]
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Link: https://lore.kernel.org/r/56d1a6769b68dfcbf8b26a75a7329aeb8e3c3b6a.1757423202.git.leonro@nvidia.com
Link: https://lore.kernel.org/all/20250910052618.GH341237@unreal/
Documentation/core-api/dma-api.rst
include/linux/page-flags.h
kernel/dma/debug.c
kernel/dma/debug.h
kernel/dma/mapping.c

index 3087bea715ed200a5086dc256ae1d0a02448294a..ca75b354167924c5769aa7c1013e436f4f46dc74 100644 (file)
@@ -761,7 +761,7 @@ example warning message may look like this::
        [<ffffffff80235177>] find_busiest_group+0x207/0x8a0
        [<ffffffff8064784f>] _spin_lock_irqsave+0x1f/0x50
        [<ffffffff803c7ea3>] check_unmap+0x203/0x490
-       [<ffffffff803c8259>] debug_dma_unmap_page+0x49/0x50
+       [<ffffffff803c8259>] debug_dma_unmap_phys+0x49/0x50
        [<ffffffff80485f26>] nv_tx_done_optimized+0xc6/0x2c0
        [<ffffffff80486c13>] nv_nic_irq_optimized+0x73/0x2b0
        [<ffffffff8026df84>] handle_IRQ_event+0x34/0x70
@@ -855,7 +855,7 @@ that a driver may be leaking mappings.
 dma-debug interface debug_dma_mapping_error() to debug drivers that fail
 to check DMA mapping errors on addresses returned by dma_map_single() and
 dma_map_page() interfaces. This interface clears a flag set by
-debug_dma_map_page() to indicate that dma_mapping_error() has been called by
+debug_dma_map_phys() to indicate that dma_mapping_error() has been called by
 the driver. When driver does unmap, debug_dma_unmap() checks the flag and if
 this flag is still set, prints warning message that includes call trace that
 leads up to the unmap. This interface can be called from dma_mapping_error()
index 8d3fa3a91ce4739efebac7f61b9d306ca92062f6..2a1f3461780249720f463882d09175b43ee3920c 100644 (file)
@@ -618,6 +618,7 @@ FOLIO_FLAG(dropbehind, FOLIO_HEAD_PAGE)
 #else
 PAGEFLAG_FALSE(HighMem, highmem)
 #endif
+#define PhysHighMem(__p) (PageHighMem(phys_to_page(__p)))
 
 /* Does kmap_local_folio() only allow access to one page of the folio? */
 #ifdef CONFIG_DEBUG_KMAP_LOCAL_FORCE_MAP
index b82399437db03116a396c7d1cebd16b2e1af98c3..b275db9ca6a03065e7469e0b43fee2a43cedd71e 100644 (file)
@@ -40,6 +40,7 @@ enum {
        dma_debug_coherent,
        dma_debug_resource,
        dma_debug_noncoherent,
+       dma_debug_phy,
 };
 
 enum map_err_types {
@@ -143,6 +144,7 @@ static const char *type2name[] = {
        [dma_debug_coherent] = "coherent",
        [dma_debug_resource] = "resource",
        [dma_debug_noncoherent] = "noncoherent",
+       [dma_debug_phy] = "phy",
 };
 
 static const char *dir2name[] = {
@@ -1054,17 +1056,16 @@ static void check_unmap(struct dma_debug_entry *ref)
        dma_entry_free(entry);
 }
 
-static void check_for_stack(struct device *dev,
-                           struct page *page, size_t offset)
+static void check_for_stack(struct device *dev, phys_addr_t phys)
 {
        void *addr;
        struct vm_struct *stack_vm_area = task_stack_vm_area(current);
 
        if (!stack_vm_area) {
                /* Stack is direct-mapped. */
-               if (PageHighMem(page))
+               if (PhysHighMem(phys))
                        return;
-               addr = page_address(page) + offset;
+               addr = phys_to_virt(phys);
                if (object_is_on_stack(addr))
                        err_printk(dev, NULL, "device driver maps memory from stack [addr=%p]\n", addr);
        } else {
@@ -1072,10 +1073,12 @@ static void check_for_stack(struct device *dev,
                int i;
 
                for (i = 0; i < stack_vm_area->nr_pages; i++) {
-                       if (page != stack_vm_area->pages[i])
+                       if (__phys_to_pfn(phys) !=
+                           page_to_pfn(stack_vm_area->pages[i]))
                                continue;
 
-                       addr = (u8 *)current->stack + i * PAGE_SIZE + offset;
+                       addr = (u8 *)current->stack + i * PAGE_SIZE +
+                              (phys % PAGE_SIZE);
                        err_printk(dev, NULL, "device driver maps memory from stack [probable addr=%p]\n", addr);
                        break;
                }
@@ -1204,9 +1207,8 @@ void debug_dma_map_single(struct device *dev, const void *addr,
 }
 EXPORT_SYMBOL(debug_dma_map_single);
 
-void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
-                       size_t size, int direction, dma_addr_t dma_addr,
-                       unsigned long attrs)
+void debug_dma_map_phys(struct device *dev, phys_addr_t phys, size_t size,
+               int direction, dma_addr_t dma_addr, unsigned long attrs)
 {
        struct dma_debug_entry *entry;
 
@@ -1221,19 +1223,18 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset,
                return;
 
        entry->dev       = dev;
-       entry->type      = dma_debug_single;
-       entry->paddr     = page_to_phys(page) + offset;
+       entry->type      = dma_debug_phy;
+       entry->paddr     = phys;
        entry->dev_addr  = dma_addr;
        entry->size      = size;
        entry->direction = direction;
        entry->map_err_type = MAP_ERR_NOT_CHECKED;
 
-       check_for_stack(dev, page, offset);
+       if (!(attrs & DMA_ATTR_MMIO)) {
+               check_for_stack(dev, phys);
 
-       if (!PageHighMem(page)) {
-               void *addr = page_address(page) + offset;
-
-               check_for_illegal_area(dev, addr, size);
+               if (!PhysHighMem(phys))
+                       check_for_illegal_area(dev, phys_to_virt(phys), size);
        }
 
        add_dma_entry(entry, attrs);
@@ -1277,11 +1278,11 @@ void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)
 }
 EXPORT_SYMBOL(debug_dma_mapping_error);
 
-void debug_dma_unmap_page(struct device *dev, dma_addr_t dma_addr,
+void debug_dma_unmap_phys(struct device *dev, dma_addr_t dma_addr,
                          size_t size, int direction)
 {
        struct dma_debug_entry ref = {
-               .type           = dma_debug_single,
+               .type           = dma_debug_phy,
                .dev            = dev,
                .dev_addr       = dma_addr,
                .size           = size,
@@ -1305,7 +1306,7 @@ void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
                return;
 
        for_each_sg(sg, s, nents, i) {
-               check_for_stack(dev, sg_page(s), s->offset);
+               check_for_stack(dev, sg_phys(s));
                if (!PageHighMem(sg_page(s)))
                        check_for_illegal_area(dev, sg_virt(s), s->length);
        }
index 48757ca13f31403f58090e1015de828893b8f94f..bedae973e725dd2d8f3792324a5280b3d3316d45 100644 (file)
@@ -9,12 +9,11 @@
 #define _KERNEL_DMA_DEBUG_H
 
 #ifdef CONFIG_DMA_API_DEBUG
-extern void debug_dma_map_page(struct device *dev, struct page *page,
-                              size_t offset, size_t size,
-                              int direction, dma_addr_t dma_addr,
+extern void debug_dma_map_phys(struct device *dev, phys_addr_t phys,
+                              size_t size, int direction, dma_addr_t dma_addr,
                               unsigned long attrs);
 
-extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
+extern void debug_dma_unmap_phys(struct device *dev, dma_addr_t addr,
                                 size_t size, int direction);
 
 extern void debug_dma_map_sg(struct device *dev, struct scatterlist *sg,
@@ -62,14 +61,13 @@ extern void debug_dma_free_pages(struct device *dev, struct page *page,
                                 size_t size, int direction,
                                 dma_addr_t dma_addr);
 #else /* CONFIG_DMA_API_DEBUG */
-static inline void debug_dma_map_page(struct device *dev, struct page *page,
-                                     size_t offset, size_t size,
-                                     int direction, dma_addr_t dma_addr,
-                                     unsigned long attrs)
+static inline void debug_dma_map_phys(struct device *dev, phys_addr_t phys,
+                                     size_t size, int direction,
+                                     dma_addr_t dma_addr, unsigned long attrs)
 {
 }
 
-static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr,
+static inline void debug_dma_unmap_phys(struct device *dev, dma_addr_t addr,
                                        size_t size, int direction)
 {
 }
index 56de28a3b1799f7c18f7e3f8e5ecd438c5748677..0b7e16c69bf18d69f04314985cdcb80cf30f2ec8 100644 (file)
@@ -157,6 +157,7 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
                unsigned long attrs)
 {
        const struct dma_map_ops *ops = get_dma_ops(dev);
+       phys_addr_t phys = page_to_phys(page) + offset;
        dma_addr_t addr;
 
        BUG_ON(!valid_dma_direction(dir));
@@ -165,16 +166,15 @@ dma_addr_t dma_map_page_attrs(struct device *dev, struct page *page,
                return DMA_MAPPING_ERROR;
 
        if (dma_map_direct(dev, ops) ||
-           arch_dma_map_page_direct(dev, page_to_phys(page) + offset + size))
+           arch_dma_map_page_direct(dev, phys + size))
                addr = dma_direct_map_page(dev, page, offset, size, dir, attrs);
        else if (use_dma_iommu(dev))
                addr = iommu_dma_map_page(dev, page, offset, size, dir, attrs);
        else
                addr = ops->map_page(dev, page, offset, size, dir, attrs);
        kmsan_handle_dma(page, offset, size, dir);
-       trace_dma_map_page(dev, page_to_phys(page) + offset, addr, size, dir,
-                          attrs);
-       debug_dma_map_page(dev, page, offset, size, dir, addr, attrs);
+       trace_dma_map_page(dev, phys, addr, size, dir, attrs);
+       debug_dma_map_phys(dev, phys, size, dir, addr, attrs);
 
        return addr;
 }
@@ -194,7 +194,7 @@ void dma_unmap_page_attrs(struct device *dev, dma_addr_t addr, size_t size,
        else
                ops->unmap_page(dev, addr, size, dir, attrs);
        trace_dma_unmap_page(dev, addr, size, dir, attrs);
-       debug_dma_unmap_page(dev, addr, size, dir);
+       debug_dma_unmap_phys(dev, addr, size, dir);
 }
 EXPORT_SYMBOL(dma_unmap_page_attrs);