From: Matt Mackall When CONFIG_HIGHPTE is enabled, use double-buffering in pagemap to avoid calling copy_to_user while preemption is disabled. Tested on x86 with HIGHPTE with DEBUG_SPINLOCK_SLEEP and PROVE_LOCKING. Signed-off-by: Matt Mackall Cc: Alexey Dobriyan Signed-off-by: Andrew Morton --- fs/proc/task_mmu.c | 28 ++++++++++++++++++++++++++-- 1 files changed, 26 insertions(+), 2 deletions(-) diff -puN fs/proc/task_mmu.c~maps2-add-proc-pid-pagemap-interface-fix fs/proc/task_mmu.c --- a/fs/proc/task_mmu.c~maps2-add-proc-pid-pagemap-interface-fix +++ a/fs/proc/task_mmu.c @@ -520,6 +520,7 @@ struct pagemapread { struct mm_struct *mm; unsigned long next; unsigned long *buf; + pte_t *ptebuf; unsigned long pos; size_t count; int index; @@ -558,6 +559,14 @@ static int pagemap_pte_range(pmd_t *pmd, int err; pte = pte_offset_map(pmd, addr); + +#ifdef CONFIG_HIGHPTE + /* copy PTE directory to temporary buffer and unmap it */ + memcpy(pm->ptebuf, pte, PAGE_ALIGN((unsigned long)pte) - (unsigned long)pte); + pte_unmap(pte); + pte = pm->ptebuf; +#endif + for (; addr != end; pte++, addr += PAGE_SIZE) { if (addr < pm->next) continue; @@ -568,7 +577,11 @@ static int pagemap_pte_range(pmd_t *pmd, if (err) return err; } + +#ifndef CONFIG_HIGHPTE pte_unmap(pte - 1); +#endif + return 0; } @@ -640,10 +653,16 @@ static ssize_t pagemap_read(struct file if (!page) goto out; +#ifdef CONFIG_HIGHPTE + pm.ptebuf = kzalloc(PAGE_SIZE, GFP_USER); + if (!pm.ptebuf) + goto out_free; +#endif + ret = 0; mm = get_task_mm(task); if (!mm) - goto out_free; + goto out_freepte; pm.mm = mm; pm.next = addr; @@ -666,7 +685,7 @@ static ssize_t pagemap_read(struct file while (pm.count > 0 && vma) { if (!ptrace_may_attach(task)) { ret = -EIO; - goto out; + goto out_mm; } vend = min(vma->vm_start - 1, end - 1) + 1; ret = pagemap_fill(&pm, vend); @@ -685,8 +704,13 @@ static ssize_t pagemap_read(struct file if (!ret) ret = pm.pos - src; +out_mm: mmput(mm); +out_freepte: +#ifdef CONFIG_HIGHPTE + kfree(pm.ptebuf); out_free: +#endif kfree(page); out: put_task_struct(task); _