Commit 6e58e79d authored by Al Viro's avatar Al Viro

introduce copy_page_to_iter, kill loop over iovec in generic_file_aio_read()

generic_file_aio_read() was looping over the target iovec, with loop over
(source) pages nested inside that.  Just set an iov_iter up and pass *that*
to do_generic_file_aio_read().  With copy_page_to_iter() doing all work
of mapping and copying a page to iovec and advancing iov_iter.

Switch shmem_file_aio_read() to the same and kill file_read_actor(), while
we are at it.
Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent 92236878
...@@ -2390,7 +2390,6 @@ extern int generic_file_mmap(struct file *, struct vm_area_struct *); ...@@ -2390,7 +2390,6 @@ extern int generic_file_mmap(struct file *, struct vm_area_struct *);
extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *); extern int generic_file_readonly_mmap(struct file *, struct vm_area_struct *);
extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr, extern int generic_file_remap_pages(struct vm_area_struct *, unsigned long addr,
unsigned long size, pgoff_t pgoff); unsigned long size, pgoff_t pgoff);
extern int file_read_actor(read_descriptor_t * desc, struct page *page, unsigned long offset, unsigned long size);
int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk); int generic_write_checks(struct file *file, loff_t *pos, size_t *count, int isblk);
extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t); extern ssize_t generic_file_aio_read(struct kiocb *, const struct iovec *, unsigned long, loff_t);
extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long, extern ssize_t __generic_file_aio_write(struct kiocb *, const struct iovec *, unsigned long,
......
...@@ -67,6 +67,8 @@ size_t iov_iter_copy_from_user(struct page *page, ...@@ -67,6 +67,8 @@ size_t iov_iter_copy_from_user(struct page *page,
void iov_iter_advance(struct iov_iter *i, size_t bytes); void iov_iter_advance(struct iov_iter *i, size_t bytes);
int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes); int iov_iter_fault_in_readable(struct iov_iter *i, size_t bytes);
size_t iov_iter_single_seg_count(const struct iov_iter *i); size_t iov_iter_single_seg_count(const struct iov_iter *i);
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i);
static inline void iov_iter_init(struct iov_iter *i, static inline void iov_iter_init(struct iov_iter *i,
const struct iovec *iov, unsigned long nr_segs, const struct iovec *iov, unsigned long nr_segs,
......
...@@ -1085,11 +1085,90 @@ static void shrink_readahead_size_eio(struct file *filp, ...@@ -1085,11 +1085,90 @@ static void shrink_readahead_size_eio(struct file *filp,
ra->ra_pages /= 4; ra->ra_pages /= 4;
} }
size_t copy_page_to_iter(struct page *page, size_t offset, size_t bytes,
struct iov_iter *i)
{
size_t skip, copy, left, wanted;
const struct iovec *iov;
char __user *buf;
void *kaddr, *from;
if (unlikely(bytes > i->count))
bytes = i->count;
if (unlikely(!bytes))
return 0;
wanted = bytes;
iov = i->iov;
skip = i->iov_offset;
buf = iov->iov_base + skip;
copy = min(bytes, iov->iov_len - skip);
if (!fault_in_pages_writeable(buf, copy)) {
kaddr = kmap_atomic(page);
from = kaddr + offset;
/* first chunk, usually the only one */
left = __copy_to_user_inatomic(buf, from, copy);
copy -= left;
skip += copy;
from += copy;
bytes -= copy;
while (unlikely(!left && bytes)) {
iov++;
buf = iov->iov_base;
copy = min(bytes, iov->iov_len);
left = __copy_to_user_inatomic(buf, from, copy);
copy -= left;
skip = copy;
from += copy;
bytes -= copy;
}
if (likely(!bytes)) {
kunmap_atomic(kaddr);
goto done;
}
offset = from - kaddr;
buf += copy;
kunmap_atomic(kaddr);
copy = min(bytes, iov->iov_len - skip);
}
/* Too bad - revert to non-atomic kmap */
kaddr = kmap(page);
from = kaddr + offset;
left = __copy_to_user(buf, from, copy);
copy -= left;
skip += copy;
from += copy;
bytes -= copy;
while (unlikely(!left && bytes)) {
iov++;
buf = iov->iov_base;
copy = min(bytes, iov->iov_len);
left = __copy_to_user(buf, from, copy);
copy -= left;
skip = copy;
from += copy;
bytes -= copy;
}
kunmap(page);
done:
i->count -= wanted - bytes;
i->nr_segs -= iov - i->iov;
i->iov = iov;
i->iov_offset = skip;
return wanted - bytes;
}
EXPORT_SYMBOL(copy_page_to_iter);
/** /**
* do_generic_file_read - generic file read routine * do_generic_file_read - generic file read routine
* @filp: the file to read * @filp: the file to read
* @ppos: current file position * @ppos: current file position
* @desc: read_descriptor * @iter: data destination
* @written: already copied
* *
* This is a generic file read routine, and uses the * This is a generic file read routine, and uses the
* mapping->a_ops->readpage() function for the actual low-level stuff. * mapping->a_ops->readpage() function for the actual low-level stuff.
...@@ -1097,8 +1176,8 @@ static void shrink_readahead_size_eio(struct file *filp, ...@@ -1097,8 +1176,8 @@ static void shrink_readahead_size_eio(struct file *filp,
* This is really ugly. But the goto's actually try to clarify some * This is really ugly. But the goto's actually try to clarify some
* of the logic when it comes to error handling etc. * of the logic when it comes to error handling etc.
*/ */
static void do_generic_file_read(struct file *filp, loff_t *ppos, static ssize_t do_generic_file_read(struct file *filp, loff_t *ppos,
read_descriptor_t *desc) struct iov_iter *iter, ssize_t written)
{ {
struct address_space *mapping = filp->f_mapping; struct address_space *mapping = filp->f_mapping;
struct inode *inode = mapping->host; struct inode *inode = mapping->host;
...@@ -1108,12 +1187,12 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1108,12 +1187,12 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
pgoff_t prev_index; pgoff_t prev_index;
unsigned long offset; /* offset into pagecache page */ unsigned long offset; /* offset into pagecache page */
unsigned int prev_offset; unsigned int prev_offset;
int error; int error = 0;
index = *ppos >> PAGE_CACHE_SHIFT; index = *ppos >> PAGE_CACHE_SHIFT;
prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT; prev_index = ra->prev_pos >> PAGE_CACHE_SHIFT;
prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1); prev_offset = ra->prev_pos & (PAGE_CACHE_SIZE-1);
last_index = (*ppos + desc->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; last_index = (*ppos + iter->count + PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT;
offset = *ppos & ~PAGE_CACHE_MASK; offset = *ppos & ~PAGE_CACHE_MASK;
for (;;) { for (;;) {
...@@ -1148,7 +1227,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1148,7 +1227,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
if (!page->mapping) if (!page->mapping)
goto page_not_up_to_date_locked; goto page_not_up_to_date_locked;
if (!mapping->a_ops->is_partially_uptodate(page, if (!mapping->a_ops->is_partially_uptodate(page,
offset, desc->count)) offset, iter->count))
goto page_not_up_to_date_locked; goto page_not_up_to_date_locked;
unlock_page(page); unlock_page(page);
} }
...@@ -1198,24 +1277,23 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1198,24 +1277,23 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
/* /*
* Ok, we have the page, and it's up-to-date, so * Ok, we have the page, and it's up-to-date, so
* now we can copy it to user space... * now we can copy it to user space...
*
* The file_read_actor routine returns how many bytes were
* actually used..
* NOTE! This may not be the same as how much of a user buffer
* we filled up (we may be padding etc), so we can only update
* "pos" here (the actor routine has to update the user buffer
* pointers and the remaining count).
*/ */
ret = file_read_actor(desc, page, offset, nr);
ret = copy_page_to_iter(page, offset, nr, iter);
offset += ret; offset += ret;
index += offset >> PAGE_CACHE_SHIFT; index += offset >> PAGE_CACHE_SHIFT;
offset &= ~PAGE_CACHE_MASK; offset &= ~PAGE_CACHE_MASK;
prev_offset = offset; prev_offset = offset;
page_cache_release(page); page_cache_release(page);
if (ret == nr && desc->count) written += ret;
continue; if (!iov_iter_count(iter))
goto out; goto out;
if (ret < nr) {
error = -EFAULT;
goto out;
}
continue;
page_not_up_to_date: page_not_up_to_date:
/* Get exclusive access to the page ... */ /* Get exclusive access to the page ... */
...@@ -1250,6 +1328,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1250,6 +1328,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
if (unlikely(error)) { if (unlikely(error)) {
if (error == AOP_TRUNCATED_PAGE) { if (error == AOP_TRUNCATED_PAGE) {
page_cache_release(page); page_cache_release(page);
error = 0;
goto find_page; goto find_page;
} }
goto readpage_error; goto readpage_error;
...@@ -1280,7 +1359,6 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1280,7 +1359,6 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
readpage_error: readpage_error:
/* UHHUH! A synchronous read error occurred. Report it */ /* UHHUH! A synchronous read error occurred. Report it */
desc->error = error;
page_cache_release(page); page_cache_release(page);
goto out; goto out;
...@@ -1291,16 +1369,17 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1291,16 +1369,17 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
*/ */
page = page_cache_alloc_cold(mapping); page = page_cache_alloc_cold(mapping);
if (!page) { if (!page) {
desc->error = -ENOMEM; error = -ENOMEM;
goto out; goto out;
} }
error = add_to_page_cache_lru(page, mapping, error = add_to_page_cache_lru(page, mapping,
index, GFP_KERNEL); index, GFP_KERNEL);
if (error) { if (error) {
page_cache_release(page); page_cache_release(page);
if (error == -EEXIST) if (error == -EEXIST) {
error = 0;
goto find_page; goto find_page;
desc->error = error; }
goto out; goto out;
} }
goto readpage; goto readpage;
...@@ -1313,44 +1392,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos, ...@@ -1313,44 +1392,7 @@ static void do_generic_file_read(struct file *filp, loff_t *ppos,
*ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset; *ppos = ((loff_t)index << PAGE_CACHE_SHIFT) + offset;
file_accessed(filp); file_accessed(filp);
} return written ? written : error;
int file_read_actor(read_descriptor_t *desc, struct page *page,
unsigned long offset, unsigned long size)
{
char *kaddr;
unsigned long left, count = desc->count;
if (size > count)
size = count;
/*
* Faults on the destination of a read are common, so do it before
* taking the kmap.
*/
if (!fault_in_pages_writeable(desc->arg.buf, size)) {
kaddr = kmap_atomic(page);
left = __copy_to_user_inatomic(desc->arg.buf,
kaddr + offset, size);
kunmap_atomic(kaddr);
if (left == 0)
goto success;
}
/* Do it the slow way */
kaddr = kmap(page);
left = __copy_to_user(desc->arg.buf, kaddr + offset, size);
kunmap(page);
if (left) {
size -= left;
desc->error = -EFAULT;
}
success:
desc->count = count - size;
desc->written += size;
desc->arg.buf += size;
return size;
} }
/* /*
...@@ -1408,14 +1450,15 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1408,14 +1450,15 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
{ {
struct file *filp = iocb->ki_filp; struct file *filp = iocb->ki_filp;
ssize_t retval; ssize_t retval;
unsigned long seg = 0;
size_t count; size_t count;
loff_t *ppos = &iocb->ki_pos; loff_t *ppos = &iocb->ki_pos;
struct iov_iter i;
count = 0; count = 0;
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE); retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
if (retval) if (retval)
return retval; return retval;
iov_iter_init(&i, iov, nr_segs, count, 0);
/* coalesce the iovecs and go direct-to-BIO for O_DIRECT */ /* coalesce the iovecs and go direct-to-BIO for O_DIRECT */
if (filp->f_flags & O_DIRECT) { if (filp->f_flags & O_DIRECT) {
...@@ -1437,6 +1480,11 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1437,6 +1480,11 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
if (retval > 0) { if (retval > 0) {
*ppos = pos + retval; *ppos = pos + retval;
count -= retval; count -= retval;
/*
* If we did a short DIO read we need to skip the
* section of the iov that we've already read data into.
*/
iov_iter_advance(&i, retval);
} }
/* /*
...@@ -1453,39 +1501,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov, ...@@ -1453,39 +1501,7 @@ generic_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
} }
} }
count = retval; retval = do_generic_file_read(filp, ppos, &i, retval);
for (seg = 0; seg < nr_segs; seg++) {
read_descriptor_t desc;
loff_t offset = 0;
/*
* If we did a short DIO read we need to skip the section of the
* iov that we've already read data into.
*/
if (count) {
if (count > iov[seg].iov_len) {
count -= iov[seg].iov_len;
continue;
}
offset = count;
count = 0;
}
desc.written = 0;
desc.arg.buf = iov[seg].iov_base + offset;
desc.count = iov[seg].iov_len - offset;
if (desc.count == 0)
continue;
desc.error = 0;
do_generic_file_read(filp, ppos, &desc);
retval += desc.written;
if (desc.error) {
retval = retval ?: desc.error;
break;
}
if (desc.count > 0)
break;
}
out: out:
return retval; return retval;
} }
......
...@@ -1462,13 +1462,25 @@ shmem_write_end(struct file *file, struct address_space *mapping, ...@@ -1462,13 +1462,25 @@ shmem_write_end(struct file *file, struct address_space *mapping,
return copied; return copied;
} }
static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_t *desc) static ssize_t shmem_file_aio_read(struct kiocb *iocb,
const struct iovec *iov, unsigned long nr_segs, loff_t pos)
{ {
struct inode *inode = file_inode(filp); struct file *file = iocb->ki_filp;
struct inode *inode = file_inode(file);
struct address_space *mapping = inode->i_mapping; struct address_space *mapping = inode->i_mapping;
pgoff_t index; pgoff_t index;
unsigned long offset; unsigned long offset;
enum sgp_type sgp = SGP_READ; enum sgp_type sgp = SGP_READ;
int error;
ssize_t retval;
size_t count;
loff_t *ppos = &iocb->ki_pos;
struct iov_iter iter;
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
if (retval)
return retval;
iov_iter_init(&iter, iov, nr_segs, count, 0);
/* /*
* Might this read be for a stacking filesystem? Then when reading * Might this read be for a stacking filesystem? Then when reading
...@@ -1496,10 +1508,10 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_ ...@@ -1496,10 +1508,10 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_
break; break;
} }
desc->error = shmem_getpage(inode, index, &page, sgp, NULL); error = shmem_getpage(inode, index, &page, sgp, NULL);
if (desc->error) { if (error) {
if (desc->error == -EINVAL) if (error == -EINVAL)
desc->error = 0; error = 0;
break; break;
} }
if (page) if (page)
...@@ -1543,61 +1555,26 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_ ...@@ -1543,61 +1555,26 @@ static void do_shmem_file_read(struct file *filp, loff_t *ppos, read_descriptor_
/* /*
* Ok, we have the page, and it's up-to-date, so * Ok, we have the page, and it's up-to-date, so
* now we can copy it to user space... * now we can copy it to user space...
*
* The actor routine returns how many bytes were actually used..
* NOTE! This may not be the same as how much of a user buffer
* we filled up (we may be padding etc), so we can only update
* "pos" here (the actor routine has to update the user buffer
* pointers and the remaining count).
*/ */
ret = file_read_actor(desc, page, offset, nr); ret = copy_page_to_iter(page, offset, nr, &iter);
retval += ret;
offset += ret; offset += ret;
index += offset >> PAGE_CACHE_SHIFT; index += offset >> PAGE_CACHE_SHIFT;
offset &= ~PAGE_CACHE_MASK; offset &= ~PAGE_CACHE_MASK;
page_cache_release(page); page_cache_release(page);
if (ret != nr || !desc->count) if (!iov_iter_count(&iter))
break; break;
if (ret < nr) {
error = -EFAULT;
break;
}
cond_resched(); cond_resched();
} }
*ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset; *ppos = ((loff_t) index << PAGE_CACHE_SHIFT) + offset;
file_accessed(filp); file_accessed(file);
} return retval ? retval : error;
static ssize_t shmem_file_aio_read(struct kiocb *iocb,
const struct iovec *iov, unsigned long nr_segs, loff_t pos)
{
struct file *filp = iocb->ki_filp;
ssize_t retval;
unsigned long seg;
size_t count;
loff_t *ppos = &iocb->ki_pos;
retval = generic_segment_checks(iov, &nr_segs, &count, VERIFY_WRITE);
if (retval)
return retval;
for (seg = 0; seg < nr_segs; seg++) {
read_descriptor_t desc;
desc.written = 0;
desc.arg.buf = iov[seg].iov_base;
desc.count = iov[seg].iov_len;
if (desc.count == 0)
continue;
desc.error = 0;
do_shmem_file_read(filp, ppos, &desc);
retval += desc.written;
if (desc.error) {
retval = retval ?: desc.error;
break;
}
if (desc.count > 0)
break;
}
return retval;
} }
static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos,
......
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