Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm: fix pfn_t vs highmem

The pfn_t type uses an unsigned long to store a pfn + flags value. On a
64-bit platform the upper 12 bits of an unsigned long are never used for
storing the value of a pfn. However, this is not true on highmem
platforms, all 32-bits of a pfn value are used to address a 44-bit
physical address space. A pfn_t needs to store a 64-bit value.

Link: https://bugzilla.kernel.org/show_bug.cgi?id=112211
Fixes: 01c8f1c44b83 ("mm, dax, gpu: convert vm_insert_mixed to pfn_t")
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reported-by: Stuart Foster <smf.linux@ntlworld.com>
Reported-by: Julian Margetson <runaway@candw.ms>
Tested-by: Julian Margetson <runaway@candw.ms>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Dan Williams and committed by
Linus Torvalds
db78c222 4a389810

+10 -11
+1 -1
include/linux/pfn.h
··· 10 10 * backing is indicated by flags in the high bits of the value. 11 11 */ 12 12 typedef struct { 13 - unsigned long val; 13 + u64 val; 14 14 } pfn_t; 15 15 #endif 16 16
+8 -9
include/linux/pfn_t.h
··· 9 9 * PFN_DEV - pfn is not covered by system memmap by default 10 10 * PFN_MAP - pfn has a dynamic page mapping established by a device driver 11 11 */ 12 - #define PFN_FLAGS_MASK (((unsigned long) ~PAGE_MASK) \ 13 - << (BITS_PER_LONG - PAGE_SHIFT)) 14 - #define PFN_SG_CHAIN (1UL << (BITS_PER_LONG - 1)) 15 - #define PFN_SG_LAST (1UL << (BITS_PER_LONG - 2)) 16 - #define PFN_DEV (1UL << (BITS_PER_LONG - 3)) 17 - #define PFN_MAP (1UL << (BITS_PER_LONG - 4)) 12 + #define PFN_FLAGS_MASK (((u64) ~PAGE_MASK) << (BITS_PER_LONG_LONG - PAGE_SHIFT)) 13 + #define PFN_SG_CHAIN (1ULL << (BITS_PER_LONG_LONG - 1)) 14 + #define PFN_SG_LAST (1ULL << (BITS_PER_LONG_LONG - 2)) 15 + #define PFN_DEV (1ULL << (BITS_PER_LONG_LONG - 3)) 16 + #define PFN_MAP (1ULL << (BITS_PER_LONG_LONG - 4)) 18 17 19 - static inline pfn_t __pfn_to_pfn_t(unsigned long pfn, unsigned long flags) 18 + static inline pfn_t __pfn_to_pfn_t(unsigned long pfn, u64 flags) 20 19 { 21 20 pfn_t pfn_t = { .val = pfn | (flags & PFN_FLAGS_MASK), }; 22 21 ··· 28 29 return __pfn_to_pfn_t(pfn, 0); 29 30 } 30 31 31 - extern pfn_t phys_to_pfn_t(phys_addr_t addr, unsigned long flags); 32 + extern pfn_t phys_to_pfn_t(phys_addr_t addr, u64 flags); 32 33 33 34 static inline bool pfn_t_has_page(pfn_t pfn) 34 35 { ··· 86 87 #ifdef __HAVE_ARCH_PTE_DEVMAP 87 88 static inline bool pfn_t_devmap(pfn_t pfn) 88 89 { 89 - const unsigned long flags = PFN_DEV|PFN_MAP; 90 + const u64 flags = PFN_DEV|PFN_MAP; 90 91 91 92 return (pfn.val & flags) == flags; 92 93 }
+1 -1
kernel/memremap.c
··· 150 150 } 151 151 EXPORT_SYMBOL(devm_memunmap); 152 152 153 - pfn_t phys_to_pfn_t(phys_addr_t addr, unsigned long flags) 153 + pfn_t phys_to_pfn_t(phys_addr_t addr, u64 flags) 154 154 { 155 155 return __pfn_to_pfn_t(addr >> PAGE_SHIFT, flags); 156 156 }