Commit 1a6d80ff authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull arm64 fix from Will Deacon:
 "We received a report this week that the generic version of
  pfn_valid(), which we switched to this merge window in 16c9afc7
  ("arm64/mm: drop HAVE_ARCH_PFN_VALID"), interacts badly with
  dma_map_resource() due to the following check:

        /* Don't allow RAM to be mapped */
        if (WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr))))
                return DMA_MAPPING_ERROR;

  Since the ongoing saga to determine the semantics of pfn_valid() is
  unlikely to be resolved this week (does it indicate valid memory, or
  just the presence of a struct page, or whether that struct page has
  been initialised?), just revert back to our old version of pfn_valid()
  for 5.14.

  Summary:

   - Fix dma_map_resource() by reverting back to old pfn_valid() code"

* tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux:
  Partially revert "arm64/mm: drop HAVE_ARCH_PFN_VALID"
parents 97d8cc20 3eb9cdff
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -156,6 +156,7 @@ config ARM64
	select HAVE_ARCH_KGDB
	select HAVE_ARCH_MMAP_RND_BITS
	select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT
	select HAVE_ARCH_PFN_VALID
	select HAVE_ARCH_PREL32_RELOCATIONS
	select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET
	select HAVE_ARCH_SECCOMP_FILTER
+1 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ void tag_clear_highpage(struct page *to);

typedef struct page *pgtable_t;

int pfn_valid(unsigned long pfn);
int pfn_is_map_memory(unsigned long pfn);

#include <asm/memory.h>
+37 −0
Original line number Diff line number Diff line
@@ -219,6 +219,43 @@ static void __init zone_sizes_init(unsigned long min, unsigned long max)
	free_area_init(max_zone_pfns);
}

int pfn_valid(unsigned long pfn)
{
	phys_addr_t addr = PFN_PHYS(pfn);
	struct mem_section *ms;

	/*
	 * Ensure the upper PAGE_SHIFT bits are clear in the
	 * pfn. Else it might lead to false positives when
	 * some of the upper bits are set, but the lower bits
	 * match a valid pfn.
	 */
	if (PHYS_PFN(addr) != pfn)
		return 0;

	if (pfn_to_section_nr(pfn) >= NR_MEM_SECTIONS)
		return 0;

	ms = __pfn_to_section(pfn);
	if (!valid_section(ms))
		return 0;

	/*
	 * ZONE_DEVICE memory does not have the memblock entries.
	 * memblock_is_map_memory() check for ZONE_DEVICE based
	 * addresses will always fail. Even the normal hotplugged
	 * memory will never have MEMBLOCK_NOMAP flag set in their
	 * memblock entries. Skip memblock search for all non early
	 * memory sections covering all of hotplug memory including
	 * both normal and ZONE_DEVICE based.
	 */
	if (!early_section(ms))
		return pfn_section_valid(ms, pfn);

	return memblock_is_memory(addr);
}
EXPORT_SYMBOL(pfn_valid);

int pfn_is_map_memory(unsigned long pfn)
{
	phys_addr_t addr = PFN_PHYS(pfn);