Skip to content

Commit 3eb9cdf

Browse files
committed
Partially revert "arm64/mm: drop HAVE_ARCH_PFN_VALID"
This partially reverts commit 16c9afc. Alex Bee reports a regression in 5.14 on their RK3328 SoC when configuring the PL330 DMA controller: | ------------[ cut here ]------------ | WARNING: CPU: 2 PID: 373 at kernel/dma/mapping.c:235 dma_map_resource+0x68/0xc0 | Modules linked in: spi_rockchip(+) fuse | CPU: 2 PID: 373 Comm: systemd-udevd Not tainted 5.14.0-rc7 #1 | Hardware name: Pine64 Rock64 (DT) | pstate: 80000005 (Nzcv daif -PAN -UAO -TCO BTYPE=--) | pc : dma_map_resource+0x68/0xc0 | lr : pl330_prep_slave_fifo+0x78/0xd0 This appears to be because dma_map_resource() is being called for a physical address which does not correspond to a memory address yet does have a valid 'struct page' due to the way in which the vmemmap is constructed. Prior to 16c9afc ("arm64/mm: drop HAVE_ARCH_PFN_VALID"), the arm64 implementation of pfn_valid() called memblock_is_memory() to return 'false' for such regions and the DMA mapping request would proceed. However, now that we are using the generic implementation where only the presence of the memory map entry is considered, we return 'true' and erroneously fail with DMA_MAPPING_ERROR because we identify the region as DRAM. Although fixing this in the DMA mapping code is arguably the right fix, it is a risky, cross-architecture change at this stage in the cycle. So just revert arm64 back to its old pfn_valid() implementation for v5.14. The change to the generic pfn_valid() code is preserved from the original patch, so as to avoid impacting other architectures. Cc: Catalin Marinas <[email protected]> Cc: Robin Murphy <[email protected]> Cc: Mike Rapoport <[email protected]> Cc: Anshuman Khandual <[email protected]> Cc: Christoph Hellwig <[email protected]> Reported-by: Alex Bee <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Will Deacon <[email protected]>
1 parent bde8fff commit 3eb9cdf

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

arch/arm64/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ config ARM64
156156
select HAVE_ARCH_KGDB
157157
select HAVE_ARCH_MMAP_RND_BITS
158158
select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
159+
select HAVE_ARCH_PFN_VALID
159160
select HAVE_ARCH_PREL32_RELOCATIONS
160161
select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
161162
select HAVE_ARCH_SECCOMP_FILTER

arch/arm64/include/asm/page.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ void tag_clear_highpage(struct page *to);
4141

4242
typedef struct page *pgtable_t;
4343

44+
int pfn_valid(unsigned long pfn);
4445
int pfn_is_map_memory(unsigned long pfn);
4546

4647
#include <asm/memory.h>

arch/arm64/mm/init.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,43 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
219219
free_area_init(max_zone_pfns);
220220
}
221221

222+
int pfn_valid(unsigned long pfn)
223+
{
224+
phys_addr_t addr = PFN_PHYS(pfn);
225+
struct mem_section *ms;
226+
227+
/*
228+
* Ensure the upper PAGE_SHIFT bits are clear in the
229+
* pfn. Else it might lead to false positives when
230+
* some of the upper bits are set, but the lower bits
231+
* match a valid pfn.
232+
*/
233+
if (PHYS_PFN(addr) != pfn)
234+
return 0;
235+
236+
if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
237+
return 0;
238+
239+
ms = __pfn_to_section(pfn);
240+
if (!valid_section(ms))
241+
return 0;
242+
243+
/*
244+
* ZONE_DEVICE memory does not have the memblock entries.
245+
* memblock_is_map_memory() check for ZONE_DEVICE based
246+
* addresses will always fail. Even the normal hotplugged
247+
* memory will never have MEMBLOCK_NOMAP flag set in their
248+
* memblock entries. Skip memblock search for all non early
249+
* memory sections covering all of hotplug memory including
250+
* both normal and ZONE_DEVICE based.
251+
*/
252+
if (!early_section(ms))
253+
return pfn_section_valid(ms, pfn);
254+
255+
return memblock_is_memory(addr);
256+
}
257+
EXPORT_SYMBOL(pfn_valid);
258+
222259
int pfn_is_map_memory(unsigned long pfn)
223260
{
224261
phys_addr_t addr = PFN_PHYS(pfn);

0 commit comments

Comments
 (0)