Commit f3e8fccd authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds

mm: add get_dump_page

In preparation for the next patch, add a simple get_dump_page(addr)
interface for the CONFIG_ELF_CORE dumpers to use, instead of calling
get_user_pages() directly.  They're not interested in errors: they
just want to use holes as much as possible, to save space and make
sure that the data is aligned where the headers said it would be.

Oh, and don't use that horrid DUMP_SEEK(off) macro!
Signed-off-by: default avatarHugh Dickins <hugh.dickins@tiscali.co.uk>
Acked-by: default avatarRik van Riel <riel@redhat.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: Nick Piggin <npiggin@suse.de>
Cc: Mel Gorman <mel@csn.ul.ie>
Cc: Minchan Kim <minchan.kim@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1c3aff1c
...@@ -1280,9 +1280,6 @@ static int writenote(struct memelfnote *men, struct file *file, ...@@ -1280,9 +1280,6 @@ static int writenote(struct memelfnote *men, struct file *file,
#define DUMP_WRITE(addr, nr) \ #define DUMP_WRITE(addr, nr) \
if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \ if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
goto end_coredump; goto end_coredump;
#define DUMP_SEEK(off) \
if (!dump_seek(file, (off))) \
goto end_coredump;
static void fill_elf_header(struct elfhdr *elf, int segs, static void fill_elf_header(struct elfhdr *elf, int segs,
u16 machine, u32 flags, u8 osabi) u16 machine, u32 flags, u8 osabi)
...@@ -2016,7 +2013,8 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un ...@@ -2016,7 +2013,8 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
goto end_coredump; goto end_coredump;
/* Align to page */ /* Align to page */
DUMP_SEEK(dataoff - foffset); if (!dump_seek(file, dataoff - foffset))
goto end_coredump;
for (vma = first_vma(current, gate_vma); vma != NULL; for (vma = first_vma(current, gate_vma); vma != NULL;
vma = next_vma(vma, gate_vma)) { vma = next_vma(vma, gate_vma)) {
...@@ -2027,34 +2025,20 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un ...@@ -2027,34 +2025,20 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) { for (addr = vma->vm_start; addr < end; addr += PAGE_SIZE) {
struct page *page; struct page *page;
struct vm_area_struct *tmp_vma; int stop;
if (get_user_pages(current, current->mm, addr, 1, 0, 1, page = get_dump_page(addr);
&page, &tmp_vma) <= 0) { if (page) {
DUMP_SEEK(PAGE_SIZE); void *kaddr = kmap(page);
} else { stop = ((size += PAGE_SIZE) > limit) ||
if (page == ZERO_PAGE(0)) { !dump_write(file, kaddr, PAGE_SIZE);
if (!dump_seek(file, PAGE_SIZE)) {
page_cache_release(page);
goto end_coredump;
}
} else {
void *kaddr;
flush_cache_page(tmp_vma, addr,
page_to_pfn(page));
kaddr = kmap(page);
if ((size += PAGE_SIZE) > limit ||
!dump_write(file, kaddr,
PAGE_SIZE)) {
kunmap(page); kunmap(page);
page_cache_release(page); page_cache_release(page);
} else
stop = !dump_seek(file, PAGE_SIZE);
if (stop)
goto end_coredump; goto end_coredump;
} }
kunmap(page);
}
page_cache_release(page);
}
}
} }
#ifdef ELF_CORE_WRITE_EXTRA_DATA #ifdef ELF_CORE_WRITE_EXTRA_DATA
......
...@@ -1325,9 +1325,6 @@ static int writenote(struct memelfnote *men, struct file *file) ...@@ -1325,9 +1325,6 @@ static int writenote(struct memelfnote *men, struct file *file)
#define DUMP_WRITE(addr, nr) \ #define DUMP_WRITE(addr, nr) \
if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \ if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
goto end_coredump; goto end_coredump;
#define DUMP_SEEK(off) \
if (!dump_seek(file, (off))) \
goto end_coredump;
static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs) static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
{ {
...@@ -1518,6 +1515,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size, ...@@ -1518,6 +1515,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
unsigned long *limit, unsigned long mm_flags) unsigned long *limit, unsigned long mm_flags)
{ {
struct vm_area_struct *vma; struct vm_area_struct *vma;
int err = 0;
for (vma = current->mm->mmap; vma; vma = vma->vm_next) { for (vma = current->mm->mmap; vma; vma = vma->vm_next) {
unsigned long addr; unsigned long addr;
...@@ -1525,43 +1523,26 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size, ...@@ -1525,43 +1523,26 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
if (!maydump(vma, mm_flags)) if (!maydump(vma, mm_flags))
continue; continue;
for (addr = vma->vm_start; for (addr = vma->vm_start; addr < vma->vm_end;
addr < vma->vm_end; addr += PAGE_SIZE) {
addr += PAGE_SIZE struct page *page = get_dump_page(addr);
) { if (page) {
struct vm_area_struct *vma; void *kaddr = kmap(page);
struct page *page; *size += PAGE_SIZE;
if (*size > *limit)
if (get_user_pages(current, current->mm, addr, 1, 0, 1, err = -EFBIG;
&page, &vma) <= 0) { else if (!dump_write(file, kaddr, PAGE_SIZE))
DUMP_SEEK(file->f_pos + PAGE_SIZE); err = -EIO;
}
else if (page == ZERO_PAGE(0)) {
page_cache_release(page);
DUMP_SEEK(file->f_pos + PAGE_SIZE);
}
else {
void *kaddr;
flush_cache_page(vma, addr, page_to_pfn(page));
kaddr = kmap(page);
if ((*size += PAGE_SIZE) > *limit ||
!dump_write(file, kaddr, PAGE_SIZE)
) {
kunmap(page); kunmap(page);
page_cache_release(page); page_cache_release(page);
return -EIO; } else if (!dump_seek(file, file->f_pos + PAGE_SIZE))
} err = -EFBIG;
kunmap(page); if (err)
page_cache_release(page); goto out;
}
} }
} }
out:
return 0; return err;
end_coredump:
return -EFBIG;
} }
#endif #endif
...@@ -1802,7 +1783,8 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs, ...@@ -1802,7 +1783,8 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
goto end_coredump; goto end_coredump;
} }
DUMP_SEEK(dataoff); if (!dump_seek(file, dataoff))
goto end_coredump;
if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0) if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0)
goto end_coredump; goto end_coredump;
......
...@@ -817,6 +817,7 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, ...@@ -817,6 +817,7 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
struct page **pages, struct vm_area_struct **vmas); struct page **pages, struct vm_area_struct **vmas);
int get_user_pages_fast(unsigned long start, int nr_pages, int write, int get_user_pages_fast(unsigned long start, int nr_pages, int write,
struct page **pages); struct page **pages);
struct page *get_dump_page(unsigned long addr);
extern int try_to_release_page(struct page * page, gfp_t gfp_mask); extern int try_to_release_page(struct page * page, gfp_t gfp_mask);
extern void do_invalidatepage(struct page *page, unsigned long offset); extern void do_invalidatepage(struct page *page, unsigned long offset);
......
...@@ -1423,9 +1423,40 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, ...@@ -1423,9 +1423,40 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm,
return __get_user_pages(tsk, mm, start, nr_pages, flags, pages, vmas); return __get_user_pages(tsk, mm, start, nr_pages, flags, pages, vmas);
} }
EXPORT_SYMBOL(get_user_pages); EXPORT_SYMBOL(get_user_pages);
/**
* get_dump_page() - pin user page in memory while writing it to core dump
* @addr: user address
*
* Returns struct page pointer of user page pinned for dump,
* to be freed afterwards by page_cache_release() or put_page().
*
* Returns NULL on any kind of failure - a hole must then be inserted into
* the corefile, to preserve alignment with its headers; and also returns
* NULL wherever the ZERO_PAGE, or an anonymous pte_none, has been found -
* allowing a hole to be left in the corefile to save diskspace.
*
* Called without mmap_sem, but after all other threads have been killed.
*/
#ifdef CONFIG_ELF_CORE
struct page *get_dump_page(unsigned long addr)
{
struct vm_area_struct *vma;
struct page *page;
if (__get_user_pages(current, current->mm, addr, 1,
GUP_FLAGS_FORCE, &page, &vma) < 1)
return NULL;
if (page == ZERO_PAGE(0)) {
page_cache_release(page);
return NULL;
}
flush_cache_page(vma, addr, page_to_pfn(page));
return page;
}
#endif /* CONFIG_ELF_CORE */
pte_t *get_locked_pte(struct mm_struct *mm, unsigned long addr, pte_t *get_locked_pte(struct mm_struct *mm, unsigned long addr,
spinlock_t **ptl) spinlock_t **ptl)
{ {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment