Skip to content

Instantly share code, notes, and snippets.

@avagin
Created August 10, 2023 18:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save avagin/2e465e7c362c515ec84d72a201a28de4 to your computer and use it in GitHub Desktop.
Save avagin/2e465e7c362c515ec84d72a201a28de4 to your computer and use it in GitHub Desktop.
diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
index feeb8a07012c..b4b15a4f53e3 100644
--- a/fs/proc/task_mmu.c
+++ b/fs/proc/task_mmu.c
@@ -1766,7 +1766,7 @@ static int pagemap_release(struct inode *inode, struct file *file)
struct pagemap_scan_private {
struct pm_scan_arg arg;
unsigned long masks_of_interest, cur_vma_category;
- struct page_region *vec_buf, cur_buf;
+ struct page_region *vec_buf;
unsigned long vec_buf_len, vec_buf_index, found_pages, walk_end_addr;
struct page_region __user *vec_out;
};
@@ -1958,7 +1958,7 @@ static bool pagemap_scan_push_range(unsigned long categories,
struct pagemap_scan_private *p,
unsigned long addr, unsigned long end)
{
- struct page_region *cur_buf = &p->cur_buf;
+ struct page_region *cur_buf = &p->vec_buf[p->vec_buf_index];
/*
* When there is no output buffer provided at all, the sentinel values
@@ -1971,10 +1971,11 @@ static bool pagemap_scan_push_range(unsigned long categories,
}
if (cur_buf->end) {
- if (p->vec_buf_index >= p->vec_buf_len)
+ if (p->vec_buf_index >= p->vec_buf_len - 1)
return false;
- p->vec_buf[p->vec_buf_index++] = *cur_buf;
+ p->vec_buf_index++;
+ cur_buf = &p->vec_buf[p->vec_buf_index];
}
cur_buf->start = addr;
@@ -1988,12 +1989,15 @@ static void pagemap_scan_backout_range(struct pagemap_scan_private *p,
unsigned long addr, unsigned long end,
unsigned long walk_end_addr)
{
- struct page_region *cur_buf = &p->cur_buf;
+ struct page_region *cur_buf = &p->vec_buf[p->vec_buf_index];
if (cur_buf->start != addr)
cur_buf->end = addr;
- else
+ else {
cur_buf->start = cur_buf->end = 0;
+ if (p->vec_buf_index > 0)
+ p->vec_buf_index--;
+ }
p->walk_end_addr = walk_end_addr;
p->found_pages -= (end - addr) / PAGE_SIZE;
@@ -2006,9 +2010,6 @@ static int pagemap_scan_output(unsigned long categories,
unsigned long n_pages, total_pages;
int ret = 0;
- if (!p->vec_buf)
- return 0;
-
categories &= p->arg.return_mask;
n_pages = (*end - addr) / PAGE_SIZE;
@@ -2326,13 +2327,13 @@ static int pagemap_scan_writeback_args(struct pm_scan_arg *arg,
static int pagemap_scan_init_bounce_buffer(struct pagemap_scan_private *p)
{
if (!p->arg.vec_len) {
+ static struct page_region nop = {.start = ULLONG_MAX, .end = ULLONG_MAX};
/*
* An arbitrary non-page-aligned sentinel value for
* pagemap_scan_push_range().
*/
- p->cur_buf.start = p->cur_buf.end = ULLONG_MAX;
if (p->arg.vec)
- p->vec_buf = ZERO_SIZE_PTR;
+ p->vec_buf = &nop;
return 0;
}
@@ -2344,12 +2345,13 @@ static int pagemap_scan_init_bounce_buffer(struct pagemap_scan_private *p)
* walk_page_range() calls.
*/
p->vec_buf_len = min_t(size_t, PAGEMAP_WALK_SIZE >> PAGE_SHIFT,
- p->arg.vec_len - 1);
+ p->arg.vec_len);
p->vec_buf = kmalloc_array(p->vec_buf_len, sizeof(*p->vec_buf),
GFP_KERNEL);
if (!p->vec_buf)
return -ENOMEM;
+ p->vec_buf[0].end = 0;
p->vec_out = (struct page_region __user *)p->arg.vec;
return 0;
@@ -2360,6 +2362,12 @@ static int pagemap_scan_flush_buffer(struct pagemap_scan_private *p)
const struct page_region *buf = p->vec_buf;
int n = (int)p->vec_buf_index;
+ if (p->arg.vec_len == 0)
+ return 0;
+
+ if (buf[n].end && buf[n].end != buf[n].start)
+ n++;
+
if (!n)
return 0;
@@ -2370,7 +2378,8 @@ static int pagemap_scan_flush_buffer(struct pagemap_scan_private *p)
p->vec_out += n;
p->vec_buf_index = 0;
- p->vec_buf_len = min_t(size_t, p->vec_buf_len, p->arg.vec_len - 1);
+ p->vec_buf_len = min_t(size_t, p->vec_buf_len, p->arg.vec_len);
+ p->vec_buf[0].end = 0;
return n;
}
@@ -2425,18 +2434,11 @@ static long do_pagemap_scan(struct mm_struct *mm, unsigned long uarg)
if (!ret)
p.walk_end_addr = p.arg.end;
- if (ret != -ENOSPC || p.arg.vec_len - 1 == 0 ||
+ if (ret != -ENOSPC || p.arg.vec_len == 0 ||
p.found_pages == p.arg.max_pages)
break;
}
- if (p.cur_buf.start != p.cur_buf.end) {
- if (copy_to_user(p.vec_out, &p.cur_buf, sizeof(p.cur_buf)))
- ret = -EFAULT;
- else
- ++n_ranges_out;
- }
-
/* ENOSPC signifies early stop (buffer full) from the walk. */
if (!ret || ret == -ENOSPC)
ret = n_ranges_out;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment