diff options
-rw-r--r-- | include/linux/mm.h | 2 | ||||
-rw-r--r-- | kernel/futex.c | 4 | ||||
-rw-r--r-- | mm/memory.c | 58 |
3 files changed, 61 insertions, 3 deletions
diff --git a/include/linux/mm.h b/include/linux/mm.h index 3cccd053850f..3172a1c0f08e 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
@@ -988,6 +988,8 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, | |||
988 | int get_user_pages_fast(unsigned long start, int nr_pages, int write, | 988 | int get_user_pages_fast(unsigned long start, int nr_pages, int write, |
989 | struct page **pages); | 989 | struct page **pages); |
990 | struct page *get_dump_page(unsigned long addr); | 990 | struct page *get_dump_page(unsigned long addr); |
991 | extern int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm, | ||
992 | unsigned long address, unsigned int fault_flags); | ||
991 | 993 | ||
992 | extern int try_to_release_page(struct page * page, gfp_t gfp_mask); | 994 | extern int try_to_release_page(struct page * page, gfp_t gfp_mask); |
993 | extern void do_invalidatepage(struct page *page, unsigned long offset); | 995 | extern void do_invalidatepage(struct page *page, unsigned long offset); |
diff --git a/kernel/futex.c b/kernel/futex.c index 3fbc76cbb9aa..0a308970c24a 100644 --- a/kernel/futex.c +++ b/kernel/futex.c | |||
@@ -355,8 +355,8 @@ static int fault_in_user_writeable(u32 __user *uaddr) | |||
355 | int ret; | 355 | int ret; |
356 | 356 | ||
357 | down_read(&mm->mmap_sem); | 357 | down_read(&mm->mmap_sem); |
358 | ret = get_user_pages(current, mm, (unsigned long)uaddr, | 358 | ret = fixup_user_fault(current, mm, (unsigned long)uaddr, |
359 | 1, 1, 0, NULL, NULL); | 359 | FAULT_FLAG_WRITE); |
360 | up_read(&mm->mmap_sem); | 360 | up_read(&mm->mmap_sem); |
361 | 361 | ||
362 | return ret < 0 ? ret : 0; | 362 | return ret < 0 ? ret : 0; |
diff --git a/mm/memory.c b/mm/memory.c index 3c9f3aa8332e..a56e3ba816b2 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -1805,7 +1805,63 @@ next_page: | |||
1805 | } | 1805 | } |
1806 | EXPORT_SYMBOL(__get_user_pages); | 1806 | EXPORT_SYMBOL(__get_user_pages); |
1807 | 1807 | ||
1808 | /** | 1808 | /* |
1809 | * fixup_user_fault() - manually resolve a user page fault | ||
1810 | * @tsk: the task_struct to use for page fault accounting, or | ||
1811 | * NULL if faults are not to be recorded. | ||
1812 | * @mm: mm_struct of target mm | ||
1813 | * @address: user address | ||
1814 | * @fault_flags:flags to pass down to handle_mm_fault() | ||
1815 | * | ||
1816 | * This is meant to be called in the specific scenario where for locking reasons | ||
1817 | * we try to access user memory in atomic context (within a pagefault_disable() | ||
1818 | * section), this returns -EFAULT, and we want to resolve the user fault before | ||
1819 | * trying again. | ||
1820 | * | ||
1821 | * Typically this is meant to be used by the futex code. | ||
1822 | * | ||
1823 | * The main difference with get_user_pages() is that this function will | ||
1824 | * unconditionally call handle_mm_fault() which will in turn perform all the | ||
1825 | * necessary SW fixup of the dirty and young bits in the PTE, while | ||
1826 | * handle_mm_fault() only guarantees to update these in the struct page. | ||
1827 | * | ||
1828 | * This is important for some architectures where those bits also gate the | ||
1829 | * access permission to the page because they are maintained in software. On | ||
1830 | * such architectures, gup() will not be enough to make a subsequent access | ||
1831 | * succeed. | ||
1832 | * | ||
1833 | * This should be called with the mm_sem held for read. | ||
1834 | */ | ||
1835 | int fixup_user_fault(struct task_struct *tsk, struct mm_struct *mm, | ||
1836 | unsigned long address, unsigned int fault_flags) | ||
1837 | { | ||
1838 | struct vm_area_struct *vma; | ||
1839 | int ret; | ||
1840 | |||
1841 | vma = find_extend_vma(mm, address); | ||
1842 | if (!vma || address < vma->vm_start) | ||
1843 | return -EFAULT; | ||
1844 | |||
1845 | ret = handle_mm_fault(mm, vma, address, fault_flags); | ||
1846 | if (ret & VM_FAULT_ERROR) { | ||
1847 | if (ret & VM_FAULT_OOM) | ||
1848 | return -ENOMEM; | ||
1849 | if (ret & (VM_FAULT_HWPOISON | VM_FAULT_HWPOISON_LARGE)) | ||
1850 | return -EHWPOISON; | ||
1851 | if (ret & VM_FAULT_SIGBUS) | ||
1852 | return -EFAULT; | ||
1853 | BUG(); | ||
1854 | } | ||
1855 | if (tsk) { | ||
1856 | if (ret & VM_FAULT_MAJOR) | ||
1857 | tsk->maj_flt++; | ||
1858 | else | ||
1859 | tsk->min_flt++; | ||
1860 | } | ||
1861 | return 0; | ||
1862 | } | ||
1863 | |||
1864 | /* | ||
1809 | * get_user_pages() - pin user pages in memory | 1865 | * get_user_pages() - pin user pages in memory |
1810 | * @tsk: the task_struct to use for page fault accounting, or | 1866 | * @tsk: the task_struct to use for page fault accounting, or |
1811 | * NULL if faults are not to be recorded. | 1867 | * NULL if faults are not to be recorded. |