|
|
@@ -213,6 +213,50 @@ static inline int stack_guard_page(struct vm_area_struct *vma, unsigned long add
|
|
|
stack_guard_page_end(vma, addr+PAGE_SIZE);
|
|
|
}
|
|
|
|
|
|
+static int get_gate_page(struct mm_struct *mm, unsigned long address,
|
|
|
+ unsigned int gup_flags, struct vm_area_struct **vma,
|
|
|
+ struct page **page)
|
|
|
+{
|
|
|
+ pgd_t *pgd;
|
|
|
+ pud_t *pud;
|
|
|
+ pmd_t *pmd;
|
|
|
+ pte_t *pte;
|
|
|
+ int ret = -EFAULT;
|
|
|
+
|
|
|
+ /* user gate pages are read-only */
|
|
|
+ if (gup_flags & FOLL_WRITE)
|
|
|
+ return -EFAULT;
|
|
|
+ if (address > TASK_SIZE)
|
|
|
+ pgd = pgd_offset_k(address);
|
|
|
+ else
|
|
|
+ pgd = pgd_offset_gate(mm, address);
|
|
|
+ BUG_ON(pgd_none(*pgd));
|
|
|
+ pud = pud_offset(pgd, address);
|
|
|
+ BUG_ON(pud_none(*pud));
|
|
|
+ pmd = pmd_offset(pud, address);
|
|
|
+ if (pmd_none(*pmd))
|
|
|
+ return -EFAULT;
|
|
|
+ VM_BUG_ON(pmd_trans_huge(*pmd));
|
|
|
+ pte = pte_offset_map(pmd, address);
|
|
|
+ if (pte_none(*pte))
|
|
|
+ goto unmap;
|
|
|
+ *vma = get_gate_vma(mm);
|
|
|
+ if (!page)
|
|
|
+ goto out;
|
|
|
+ *page = vm_normal_page(*vma, address, *pte);
|
|
|
+ if (!*page) {
|
|
|
+ if ((gup_flags & FOLL_DUMP) || !is_zero_pfn(pte_pfn(*pte)))
|
|
|
+ goto unmap;
|
|
|
+ *page = pte_page(*pte);
|
|
|
+ }
|
|
|
+ get_page(*page);
|
|
|
+out:
|
|
|
+ ret = 0;
|
|
|
+unmap:
|
|
|
+ pte_unmap(pte);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __get_user_pages() - pin user pages in memory
|
|
|
* @tsk: task_struct of target task
|
|
|
@@ -291,49 +335,11 @@ long __get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
|
|
|
|
|
|
vma = find_extend_vma(mm, start);
|
|
|
if (!vma && in_gate_area(mm, start)) {
|
|
|
- unsigned long pg = start & PAGE_MASK;
|
|
|
- pgd_t *pgd;
|
|
|
- pud_t *pud;
|
|
|
- pmd_t *pmd;
|
|
|
- pte_t *pte;
|
|
|
-
|
|
|
- /* user gate pages are read-only */
|
|
|
- if (gup_flags & FOLL_WRITE)
|
|
|
- goto efault;
|
|
|
- if (pg > TASK_SIZE)
|
|
|
- pgd = pgd_offset_k(pg);
|
|
|
- else
|
|
|
- pgd = pgd_offset_gate(mm, pg);
|
|
|
- BUG_ON(pgd_none(*pgd));
|
|
|
- pud = pud_offset(pgd, pg);
|
|
|
- BUG_ON(pud_none(*pud));
|
|
|
- pmd = pmd_offset(pud, pg);
|
|
|
- if (pmd_none(*pmd))
|
|
|
+ int ret;
|
|
|
+ ret = get_gate_page(mm, start & PAGE_MASK, gup_flags,
|
|
|
+ &vma, pages ? &pages[i] : NULL);
|
|
|
+ if (ret)
|
|
|
goto efault;
|
|
|
- VM_BUG_ON(pmd_trans_huge(*pmd));
|
|
|
- pte = pte_offset_map(pmd, pg);
|
|
|
- if (pte_none(*pte)) {
|
|
|
- pte_unmap(pte);
|
|
|
- goto efault;
|
|
|
- }
|
|
|
- vma = get_gate_vma(mm);
|
|
|
- if (pages) {
|
|
|
- struct page *page;
|
|
|
-
|
|
|
- page = vm_normal_page(vma, start, *pte);
|
|
|
- if (!page) {
|
|
|
- if (!(gup_flags & FOLL_DUMP) &&
|
|
|
- is_zero_pfn(pte_pfn(*pte)))
|
|
|
- page = pte_page(*pte);
|
|
|
- else {
|
|
|
- pte_unmap(pte);
|
|
|
- goto efault;
|
|
|
- }
|
|
|
- }
|
|
|
- pages[i] = page;
|
|
|
- get_page(page);
|
|
|
- }
|
|
|
- pte_unmap(pte);
|
|
|
page_mask = 0;
|
|
|
goto next_page;
|
|
|
}
|