Skip to content

Commit 502717f

Browse files
Chen, Kenneth WLinus Torvalds
Chen, Kenneth W
authored and
Linus Torvalds
committed
[PATCH] hugetlb: fix linked list corruption in unmap_hugepage_range()
commit fe1668a causes kernel to oops with libhugetlbfs test suite. The problem is that hugetlb pages can be shared by multiple mappings. Multiple threads can fight over page->lru in the unmap path and bad things happen. We now serialize __unmap_hugepage_range to void concurrent linked list manipulation. Such serialization is also needed for shared page table page on hugetlb area. This patch will fixed the bug and also serve as a prepatch for shared page table. Signed-off-by: Ken Chen <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: David Gibson <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 97c7801 commit 502717f

File tree

3 files changed

+22
-3
lines changed

3 files changed

+22
-3
lines changed

fs/hugetlbfs/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ hugetlb_vmtruncate_list(struct prio_tree_root *root, unsigned long h_pgoff)
293293
if (h_vm_pgoff >= h_pgoff)
294294
v_offset = 0;
295295

296-
unmap_hugepage_range(vma,
296+
__unmap_hugepage_range(vma,
297297
vma->vm_start + v_offset, vma->vm_end);
298298
}
299299
}

include/linux/hugetlb.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ int hugetlb_sysctl_handler(struct ctl_table *, int, struct file *, void __user *
1717
int copy_hugetlb_page_range(struct mm_struct *, struct mm_struct *, struct vm_area_struct *);
1818
int follow_hugetlb_page(struct mm_struct *, struct vm_area_struct *, struct page **, struct vm_area_struct **, unsigned long *, int *, int);
1919
void unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long);
20+
void __unmap_hugepage_range(struct vm_area_struct *, unsigned long, unsigned long);
2021
int hugetlb_prefault(struct address_space *, struct vm_area_struct *);
2122
int hugetlb_report_meminfo(char *);
2223
int hugetlb_report_node_meminfo(int, char *);

mm/hugetlb.c

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,8 +356,8 @@ int copy_hugetlb_page_range(struct mm_struct *dst, struct mm_struct *src,
356356
return -ENOMEM;
357357
}
358358

359-
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
360-
unsigned long end)
359+
void __unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
360+
unsigned long end)
361361
{
362362
struct mm_struct *mm = vma->vm_mm;
363363
unsigned long address;
@@ -398,6 +398,24 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
398398
}
399399
}
400400

401+
void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start,
402+
unsigned long end)
403+
{
404+
/*
405+
* It is undesirable to test vma->vm_file as it should be non-null
406+
* for valid hugetlb area. However, vm_file will be NULL in the error
407+
* cleanup path of do_mmap_pgoff. When hugetlbfs ->mmap method fails,
408+
* do_mmap_pgoff() nullifies vma->vm_file before calling this function
409+
* to clean up. Since no pte has actually been setup, it is safe to
410+
* do nothing in this case.
411+
*/
412+
if (vma->vm_file) {
413+
spin_lock(&vma->vm_file->f_mapping->i_mmap_lock);
414+
__unmap_hugepage_range(vma, start, end);
415+
spin_unlock(&vma->vm_file->f_mapping->i_mmap_lock);
416+
}
417+
}
418+
401419
static int hugetlb_cow(struct mm_struct *mm, struct vm_area_struct *vma,
402420
unsigned long address, pte_t *ptep, pte_t pte)
403421
{

0 commit comments

Comments
 (0)