Skip to content

Commit 7bab8df

Browse files
yuanchu-xieakpm00
authored andcommitted
mm: pagemap: restrict pagewalk to the requested range
The pagewalk in pagemap_read reads one PTE past the end of the requested range, and stops when the buffer runs out of space. While it produces the right result, the extra read is unnecessary and less performant. I timed the following command before and after this patch: dd count=100000 if=/proc/self/pagemap of=/dev/null The results are consistently within 0.001s across 5 runs. Before: 100000+0 records in 100000+0 records out 51200000 bytes (51 MB) copied, 0.0763159 s, 671 MB/s real 0m0.078s user 0m0.012s sys 0m0.065s After: 100000+0 records in 100000+0 records out 51200000 bytes (51 MB) copied, 0.0487928 s, 1.0 GB/s real 0m0.050s user 0m0.011s sys 0m0.039s Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Yuanchu Xie <[email protected]> Acked-by: Peter Xu <[email protected]> Reviewed-by: Yang Shi <[email protected]> Acked-by: David Rientjes <[email protected]> Cc: Kirill A. Shutemov <[email protected]> Cc: Liam R. Howlett <[email protected]> Cc: Matthew Wilcox <[email protected]> Cc: Pavel Tatashin <[email protected]> Cc: Zach O'Keefe <[email protected]> Signed-off-by: Andrew Morton <[email protected]>
1 parent 4822acb commit 7bab8df

File tree

1 file changed

+6
-6
lines changed

1 file changed

+6
-6
lines changed

fs/proc/task_mmu.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1689,23 +1689,23 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
16891689
/* watch out for wraparound */
16901690
start_vaddr = end_vaddr;
16911691
if (svpfn <= (ULONG_MAX >> PAGE_SHIFT)) {
1692+
unsigned long end;
1693+
16921694
ret = mmap_read_lock_killable(mm);
16931695
if (ret)
16941696
goto out_free;
16951697
start_vaddr = untagged_addr_remote(mm, svpfn << PAGE_SHIFT);
16961698
mmap_read_unlock(mm);
1699+
1700+
end = start_vaddr + ((count / PM_ENTRY_BYTES) << PAGE_SHIFT);
1701+
if (end >= start_vaddr && end < mm->task_size)
1702+
end_vaddr = end;
16971703
}
16981704

16991705
/* Ensure the address is inside the task */
17001706
if (start_vaddr > mm->task_size)
17011707
start_vaddr = end_vaddr;
17021708

1703-
/*
1704-
* The odds are that this will stop walking way
1705-
* before end_vaddr, because the length of the
1706-
* user buffer is tracked in "pm", and the walk
1707-
* will stop when we hit the end of the buffer.
1708-
*/
17091709
ret = 0;
17101710
while (count && (start_vaddr < end_vaddr)) {
17111711
int len;

0 commit comments

Comments
 (0)