History

numa: fix /proc/<pid>/numa_maps for THP

In gather_pte_stats() a THP pmd is cast into a pte, which is wrong because the layouts may differ depending on the architecture. On s390 this will lead to inaccurate numa_maps accounting in /proc because of misguided pte_present() and pte_dirty() checks on the fake pte. On other architectures pte_present() and pte_dirty() may work by chance, but there may be an issue with direct-access (dax) mappings w/o underlying struct pages when HAVE_PTE_SPECIAL is set and THP is available. In vm_normal_page() the fake pte will be checked with pte_special() and because there is no "special" bit in a pmd, this will always return false and the VM_PFNMAP | VM_MIXEDMAP checking will be skipped. On dax mappings w/o struct pages, an invalid struct page pointer would then be returned that can crash the kernel. This patch fixes the numa_maps THP handling by introducing new "_pmd" variants of the can_gather_numa_stats() and vm_normal_page() functions.

Gerald Schaefer committed 10 years ago

Commit 28093f91 parent 3486b853 files · +72 −3Browse files

Unified · Split

3 files
  1. fs/proc/task_mmu.c +30 −3
  2. include/linux/mm.h +2 −0
  3. mm/memory.c +40 −0
fs/proc/task_mmu.c+30−3
@@ -1518,6 +1518,32 @@ static struct page *can_gather_numa_stats(pte_t pte, struct vm_area_struct *vma, · 1517 unmodified lines
15181518 return page;
15191519 }
15201520
1521+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
1522+static struct page *can_gather_numa_stats_pmd(pmd_t pmd,
1523+ struct vm_area_struct *vma,
1524+ unsigned long addr)
1525+{
1526+ struct page *page;
1527+ int nid;
1528+
1529+ if (!pmd_present(pmd))
1530+ return NULL;
1531+
1532+ page = vm_normal_page_pmd(vma, addr, pmd);
1533+ if (!page)
1534+ return NULL;
1535+
1536+ if (PageReserved(page))
1537+ return NULL;
1538+
1539+ nid = page_to_nid(page);
1540+ if (!node_isset(nid, node_states[N_MEMORY]))
1541+ return NULL;
1542+
1543+ return page;
1544+}
1545+#endif
1546+
15211547 static int gather_pte_stats(pmd_t *pmd, unsigned long addr,
15221548 unsigned long end, struct mm_walk *walk)
15231549 {
@@ -1527,14 +1553,14 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr, · 3 unmodified lines
15271553 pte_t *orig_pte;
15281554 pte_t *pte;
15291555
1556+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
15301557 ptl = pmd_trans_huge_lock(pmd, vma);
15311558 if (ptl) {
1532− pte_t huge_pte = *(pte_t *)pmd;
15331559 struct page *page;
15341560
1535− page = can_gather_numa_stats(huge_pte, vma, addr);
1561+ page = can_gather_numa_stats_pmd(*pmd, vma, addr);
15361562 if (page)
1537− gather_stats(page, md, pte_dirty(huge_pte),
1563+ gather_stats(page, md, pmd_dirty(*pmd),
15381564 HPAGE_PMD_SIZE/PAGE_SIZE);
15391565 spin_unlock(ptl);
15401566 return 0;
@@ -1542,6 +1568,7 @@ static int gather_pte_stats(pmd_t *pmd, unsigned long addr, · 1 unmodified line
15421568
15431569 if (pmd_trans_unstable(pmd))
15441570 return 0;
1571+#endif
15451572 orig_pte = pte = pte_offset_map_lock(walk->mm, pmd, addr, &ptl);
15461573 do {
15471574 struct page *page = can_gather_numa_stats(*pte, vma, addr);
include/linux/mm.h+2−0
@@ -1140,6 +1140,8 @@ struct zap_details { · 1139 unmodified lines
11401140
11411141 struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr,
11421142 pte_t pte);
1143+struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
1144+ pmd_t pmd);
11431145
11441146 int zap_vma_ptes(struct vm_area_struct *vma, unsigned long address,
11451147 unsigned long size);
mm/memory.c+40−0
@@ -789,6 +789,46 @@ out: · 788 unmodified lines
789789 return pfn_to_page(pfn);
790790 }
791791
792+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
793+struct page *vm_normal_page_pmd(struct vm_area_struct *vma, unsigned long addr,
794+ pmd_t pmd)
795+{
796+ unsigned long pfn = pmd_pfn(pmd);
797+
798+ /*
799+ * There is no pmd_special() but there may be special pmds, e.g.
800+ * in a direct-access (dax) mapping, so let's just replicate the
801+ * !HAVE_PTE_SPECIAL case from vm_normal_page() here.
802+ */
803+ if (unlikely(vma->vm_flags & (VM_PFNMAP|VM_MIXEDMAP))) {
804+ if (vma->vm_flags & VM_MIXEDMAP) {
805+ if (!pfn_valid(pfn))
806+ return NULL;
807+ goto out;
808+ } else {
809+ unsigned long off;
810+ off = (addr - vma->vm_start) >> PAGE_SHIFT;
811+ if (pfn == vma->vm_pgoff + off)
812+ return NULL;
813+ if (!is_cow_mapping(vma->vm_flags))
814+ return NULL;
815+ }
816+ }
817+
818+ if (is_zero_pfn(pfn))
819+ return NULL;
820+ if (unlikely(pfn > highest_memmap_pfn))
821+ return NULL;
822+
823+ /*
824+ * NOTE! We still have PageReserved() pages in the page tables.
825+ * eg. VDSO mappings can cause them to exist.
826+ */
827+out:
828+ return pfn_to_page(pfn);
829+}
830+#endif
831+
792832 /*
793833 * copy one vm_area from one task to the other. Assumes the page tables
794834 * already present in the new task to be cleared in the whole range

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t