iov_iter: saner helper for page array allocation
All call sites of get_pages_array() are essenitally identical now. Replace with common helper... Returns number of slots available in resulting array or 0 on OOM; it's up to the caller to make sure it doesn't ask to zero-entry array (i.e. neither maxpages nor size are allowed to be zero). Reviewed-by: Jeff Layton <jlayton@kernel.org> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
parent
8520008417
commit
3cf42da327
|
@ -1284,9 +1284,20 @@ unsigned long iov_iter_gap_alignment(const struct iov_iter *i)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(iov_iter_gap_alignment);
|
EXPORT_SYMBOL(iov_iter_gap_alignment);
|
||||||
|
|
||||||
static struct page **get_pages_array(size_t n)
|
static int want_pages_array(struct page ***res, size_t size,
|
||||||
|
size_t start, unsigned int maxpages)
|
||||||
{
|
{
|
||||||
return kvmalloc_array(n, sizeof(struct page *), GFP_KERNEL);
|
unsigned int count = DIV_ROUND_UP(size + start, PAGE_SIZE);
|
||||||
|
|
||||||
|
if (count > maxpages)
|
||||||
|
count = maxpages;
|
||||||
|
WARN_ON(!count); // caller should've prevented that
|
||||||
|
if (!*res) {
|
||||||
|
*res = kvmalloc_array(count, sizeof(struct page *), GFP_KERNEL);
|
||||||
|
if (!*res)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t pipe_get_pages(struct iov_iter *i,
|
static ssize_t pipe_get_pages(struct iov_iter *i,
|
||||||
|
@ -1294,27 +1305,20 @@ static ssize_t pipe_get_pages(struct iov_iter *i,
|
||||||
size_t *start)
|
size_t *start)
|
||||||
{
|
{
|
||||||
struct pipe_inode_info *pipe = i->pipe;
|
struct pipe_inode_info *pipe = i->pipe;
|
||||||
unsigned int npages, off;
|
unsigned int npages, off, count;
|
||||||
struct page **p;
|
struct page **p;
|
||||||
ssize_t left;
|
ssize_t left;
|
||||||
int count;
|
|
||||||
|
|
||||||
if (!sanity(i))
|
if (!sanity(i))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
*start = off = pipe_npages(i, &npages);
|
*start = off = pipe_npages(i, &npages);
|
||||||
count = DIV_ROUND_UP(maxsize + off, PAGE_SIZE);
|
if (!npages)
|
||||||
if (count > npages)
|
return -EFAULT;
|
||||||
count = npages;
|
count = want_pages_array(pages, maxsize, off, min(npages, maxpages));
|
||||||
if (count > maxpages)
|
if (!count)
|
||||||
count = maxpages;
|
return -ENOMEM;
|
||||||
p = *pages;
|
p = *pages;
|
||||||
if (!p) {
|
|
||||||
*pages = p = get_pages_array(count);
|
|
||||||
if (!p)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
left = maxsize;
|
left = maxsize;
|
||||||
npages = 0;
|
npages = 0;
|
||||||
if (off) {
|
if (off) {
|
||||||
|
@ -1377,9 +1381,8 @@ static ssize_t iter_xarray_get_pages(struct iov_iter *i,
|
||||||
struct page ***pages, size_t maxsize,
|
struct page ***pages, size_t maxsize,
|
||||||
unsigned maxpages, size_t *_start_offset)
|
unsigned maxpages, size_t *_start_offset)
|
||||||
{
|
{
|
||||||
unsigned nr, offset;
|
unsigned nr, offset, count;
|
||||||
pgoff_t index, count;
|
pgoff_t index;
|
||||||
size_t size = maxsize;
|
|
||||||
loff_t pos;
|
loff_t pos;
|
||||||
|
|
||||||
pos = i->xarray_start + i->iov_offset;
|
pos = i->xarray_start + i->iov_offset;
|
||||||
|
@ -1387,16 +1390,9 @@ static ssize_t iter_xarray_get_pages(struct iov_iter *i,
|
||||||
offset = pos & ~PAGE_MASK;
|
offset = pos & ~PAGE_MASK;
|
||||||
*_start_offset = offset;
|
*_start_offset = offset;
|
||||||
|
|
||||||
count = DIV_ROUND_UP(size + offset, PAGE_SIZE);
|
count = want_pages_array(pages, maxsize, offset, maxpages);
|
||||||
if (count > maxpages)
|
if (!count)
|
||||||
count = maxpages;
|
return -ENOMEM;
|
||||||
|
|
||||||
if (!*pages) {
|
|
||||||
*pages = get_pages_array(count);
|
|
||||||
if (!*pages)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
nr = iter_xarray_populate_pages(*pages, i->xarray, index, count);
|
nr = iter_xarray_populate_pages(*pages, i->xarray, index, count);
|
||||||
if (nr == 0)
|
if (nr == 0)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1445,7 +1441,7 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,
|
||||||
struct page ***pages, size_t maxsize,
|
struct page ***pages, size_t maxsize,
|
||||||
unsigned int maxpages, size_t *start)
|
unsigned int maxpages, size_t *start)
|
||||||
{
|
{
|
||||||
int n, res;
|
unsigned int n;
|
||||||
|
|
||||||
if (maxsize > i->count)
|
if (maxsize > i->count)
|
||||||
maxsize = i->count;
|
maxsize = i->count;
|
||||||
|
@ -1457,6 +1453,7 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,
|
||||||
if (likely(user_backed_iter(i))) {
|
if (likely(user_backed_iter(i))) {
|
||||||
unsigned int gup_flags = 0;
|
unsigned int gup_flags = 0;
|
||||||
unsigned long addr;
|
unsigned long addr;
|
||||||
|
int res;
|
||||||
|
|
||||||
if (iov_iter_rw(i) != WRITE)
|
if (iov_iter_rw(i) != WRITE)
|
||||||
gup_flags |= FOLL_WRITE;
|
gup_flags |= FOLL_WRITE;
|
||||||
|
@ -1466,14 +1463,9 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,
|
||||||
addr = first_iovec_segment(i, &maxsize);
|
addr = first_iovec_segment(i, &maxsize);
|
||||||
*start = addr % PAGE_SIZE;
|
*start = addr % PAGE_SIZE;
|
||||||
addr &= PAGE_MASK;
|
addr &= PAGE_MASK;
|
||||||
n = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE);
|
n = want_pages_array(pages, maxsize, *start, maxpages);
|
||||||
if (n > maxpages)
|
if (!n)
|
||||||
n = maxpages;
|
return -ENOMEM;
|
||||||
if (!*pages) {
|
|
||||||
*pages = get_pages_array(n);
|
|
||||||
if (!*pages)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
res = get_user_pages_fast(addr, n, gup_flags, *pages);
|
res = get_user_pages_fast(addr, n, gup_flags, *pages);
|
||||||
if (unlikely(res <= 0))
|
if (unlikely(res <= 0))
|
||||||
return res;
|
return res;
|
||||||
|
@ -1484,15 +1476,10 @@ static ssize_t __iov_iter_get_pages_alloc(struct iov_iter *i,
|
||||||
struct page *page;
|
struct page *page;
|
||||||
|
|
||||||
page = first_bvec_segment(i, &maxsize, start);
|
page = first_bvec_segment(i, &maxsize, start);
|
||||||
n = DIV_ROUND_UP(maxsize + *start, PAGE_SIZE);
|
n = want_pages_array(pages, maxsize, *start, maxpages);
|
||||||
if (n > maxpages)
|
if (!n)
|
||||||
n = maxpages;
|
return -ENOMEM;
|
||||||
p = *pages;
|
p = *pages;
|
||||||
if (!p) {
|
|
||||||
*pages = p = get_pages_array(n);
|
|
||||||
if (!p)
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
for (int k = 0; k < n; k++)
|
for (int k = 0; k < n; k++)
|
||||||
get_page(*p++ = page++);
|
get_page(*p++ = page++);
|
||||||
return min_t(size_t, maxsize, n * PAGE_SIZE - *start);
|
return min_t(size_t, maxsize, n * PAGE_SIZE - *start);
|
||||||
|
|
Loading…
Reference in New Issue