forked from Minki/linux
Merge branch 'work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull iov_iter updates from Al Viro: "iov_iter cleanups and fixes. There are followups, but this is what had sat in -next this cycle. IMO the macro forest in there became much thinner and easier to follow..." * 'work.iov_iter' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (37 commits) csum_and_copy_to_pipe_iter(): leave handling of csum_state to caller clean up copy_mc_pipe_to_iter() pipe_zero(): we don't need no stinkin' kmap_atomic()... iov_iter: clean csum_and_copy_...() primitives up a bit copy_page_from_iter(): don't need kmap_atomic() for kvec/bvec cases copy_page_to_iter(): don't bother with kmap_atomic() for bvec/kvec cases iterate_xarray(): only of the first iteration we might get offset != 0 pull handling of ->iov_offset into iterate_{iovec,bvec,xarray} iov_iter: make iterator callbacks use base and len instead of iovec iov_iter: make the amount already copied available to iterator callbacks iov_iter: get rid of separate bvec and xarray callbacks iov_iter: teach iterate_{bvec,xarray}() about possible short copies iterate_bvec(): expand bvec.h macro forest, massage a bit iov_iter: unify iterate_iovec and iterate_kvec iov_iter: massage iterate_iovec and iterate_kvec to logics similar to iterate_bvec iterate_and_advance(): get rid of magic in case when n is 0 csum_and_copy_to_iter(): massage into form closer to csum_and_copy_from_iter() iov_iter: replace iov_iter_copy_from_user_atomic() with iterator-advancing variant [xarray] iov_iter_npages(): just use DIV_ROUND_UP() iov_iter_npages(): don't bother with iterate_all_kinds() ...
This commit is contained in:
commit
d3acb15a3a
@ -890,3 +890,12 @@ been called or returned with non -EIOCBQUEUED code.
|
||||
|
||||
mnt_want_write_file() can now only be paired with mnt_drop_write_file(),
|
||||
whereas previously it could be paired with mnt_drop_write() as well.
|
||||
|
||||
---
|
||||
|
||||
**mandatory**
|
||||
|
||||
iov_iter_copy_from_user_atomic() is gone; use copy_page_from_iter_atomic().
|
||||
The difference is copy_page_from_iter_atomic() advances the iterator and
|
||||
you don't need iov_iter_advance() after it. However, if you decide to use
|
||||
only a part of obtained data, you should do iov_iter_revert().
|
||||
|
@ -399,7 +399,7 @@ static noinline int btrfs_copy_from_user(loff_t pos, size_t write_bytes,
|
||||
/*
|
||||
* Copy data from userspace to the current page
|
||||
*/
|
||||
copied = iov_iter_copy_from_user_atomic(page, i, offset, count);
|
||||
copied = copy_page_from_iter_atomic(page, offset, count, i);
|
||||
|
||||
/* Flush processor's dcache for this page */
|
||||
flush_dcache_page(page);
|
||||
@ -413,20 +413,19 @@ static noinline int btrfs_copy_from_user(loff_t pos, size_t write_bytes,
|
||||
* The rest of the btrfs_file_write code will fall
|
||||
* back to page at a time copies after we return 0.
|
||||
*/
|
||||
if (!PageUptodate(page) && copied < count)
|
||||
copied = 0;
|
||||
if (unlikely(copied < count)) {
|
||||
if (!PageUptodate(page)) {
|
||||
iov_iter_revert(i, copied);
|
||||
copied = 0;
|
||||
}
|
||||
if (!copied)
|
||||
break;
|
||||
}
|
||||
|
||||
iov_iter_advance(i, copied);
|
||||
write_bytes -= copied;
|
||||
total_copied += copied;
|
||||
|
||||
/* Return to btrfs_file_write_iter to fault page */
|
||||
if (unlikely(copied == 0))
|
||||
break;
|
||||
|
||||
if (copied < PAGE_SIZE - offset) {
|
||||
offset += copied;
|
||||
} else {
|
||||
offset += copied;
|
||||
if (offset == PAGE_SIZE) {
|
||||
pg++;
|
||||
offset = 0;
|
||||
}
|
||||
|
@ -1171,14 +1171,12 @@ static ssize_t fuse_fill_write_pages(struct fuse_io_args *ia,
|
||||
if (mapping_writably_mapped(mapping))
|
||||
flush_dcache_page(page);
|
||||
|
||||
tmp = iov_iter_copy_from_user_atomic(page, ii, offset, bytes);
|
||||
tmp = copy_page_from_iter_atomic(page, offset, bytes, ii);
|
||||
flush_dcache_page(page);
|
||||
|
||||
iov_iter_advance(ii, tmp);
|
||||
if (!tmp) {
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
bytes = min(bytes, iov_iter_single_seg_count(ii));
|
||||
goto again;
|
||||
}
|
||||
|
||||
|
@ -746,10 +746,6 @@ again:
|
||||
* Otherwise there's a nasty deadlock on copying from the
|
||||
* same page as we're writing to, without it being marked
|
||||
* up-to-date.
|
||||
*
|
||||
* Not only is this an optimisation, but it is also required
|
||||
* to check that the address is actually valid, when atomic
|
||||
* usercopies are used, below.
|
||||
*/
|
||||
if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
|
||||
status = -EFAULT;
|
||||
@ -764,30 +760,29 @@ again:
|
||||
if (mapping_writably_mapped(inode->i_mapping))
|
||||
flush_dcache_page(page);
|
||||
|
||||
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
|
||||
copied = copy_page_from_iter_atomic(page, offset, bytes, i);
|
||||
|
||||
copied = iomap_write_end(inode, pos, bytes, copied, page, iomap,
|
||||
status = iomap_write_end(inode, pos, bytes, copied, page, iomap,
|
||||
srcmap);
|
||||
|
||||
cond_resched();
|
||||
if (unlikely(copied != status))
|
||||
iov_iter_revert(i, copied - status);
|
||||
|
||||
iov_iter_advance(i, copied);
|
||||
if (unlikely(copied == 0)) {
|
||||
cond_resched();
|
||||
if (unlikely(status == 0)) {
|
||||
/*
|
||||
* If we were unable to copy any data at all, we must
|
||||
* fall back to a single segment length write.
|
||||
*
|
||||
* If we didn't fallback here, we could livelock
|
||||
* because not all segments in the iov can be copied at
|
||||
* once without a pagefault.
|
||||
* A short copy made iomap_write_end() reject the
|
||||
* thing entirely. Might be memory poisoning
|
||||
* halfway through, might be a race with munmap,
|
||||
* might be severe memory pressure.
|
||||
*/
|
||||
bytes = min_t(unsigned long, PAGE_SIZE - offset,
|
||||
iov_iter_single_seg_count(i));
|
||||
if (copied)
|
||||
bytes = copied;
|
||||
goto again;
|
||||
}
|
||||
pos += copied;
|
||||
written += copied;
|
||||
length -= copied;
|
||||
pos += status;
|
||||
written += status;
|
||||
length -= status;
|
||||
|
||||
balance_dirty_pages_ratelimited(inode->i_mapping);
|
||||
} while (iov_iter_count(i) && length);
|
||||
|
@ -1684,20 +1684,17 @@ static size_t ntfs_copy_from_user_iter(struct page **pages, unsigned nr_pages,
|
||||
{
|
||||
struct page **last_page = pages + nr_pages;
|
||||
size_t total = 0;
|
||||
struct iov_iter data = *i;
|
||||
unsigned len, copied;
|
||||
|
||||
do {
|
||||
len = PAGE_SIZE - ofs;
|
||||
if (len > bytes)
|
||||
len = bytes;
|
||||
copied = iov_iter_copy_from_user_atomic(*pages, &data, ofs,
|
||||
len);
|
||||
copied = copy_page_from_iter_atomic(*pages, ofs, len, i);
|
||||
total += copied;
|
||||
bytes -= copied;
|
||||
if (!bytes)
|
||||
break;
|
||||
iov_iter_advance(&data, copied);
|
||||
if (copied < len)
|
||||
goto err;
|
||||
ofs = 0;
|
||||
@ -1866,34 +1863,24 @@ again:
|
||||
if (likely(copied == bytes)) {
|
||||
status = ntfs_commit_pages_after_write(pages, do_pages,
|
||||
pos, bytes);
|
||||
if (!status)
|
||||
status = bytes;
|
||||
}
|
||||
do {
|
||||
unlock_page(pages[--do_pages]);
|
||||
put_page(pages[do_pages]);
|
||||
} while (do_pages);
|
||||
if (unlikely(status < 0))
|
||||
if (unlikely(status < 0)) {
|
||||
iov_iter_revert(i, copied);
|
||||
break;
|
||||
copied = status;
|
||||
}
|
||||
cond_resched();
|
||||
if (unlikely(!copied)) {
|
||||
size_t sc;
|
||||
|
||||
/*
|
||||
* We failed to copy anything. Fall back to single
|
||||
* segment length write.
|
||||
*
|
||||
* This is needed to avoid possible livelock in the
|
||||
* case that all segments in the iov cannot be copied
|
||||
* at once without a pagefault.
|
||||
*/
|
||||
sc = iov_iter_single_seg_count(i);
|
||||
if (bytes > sc)
|
||||
bytes = sc;
|
||||
if (unlikely(copied < bytes)) {
|
||||
iov_iter_revert(i, copied);
|
||||
if (copied)
|
||||
bytes = copied;
|
||||
else if (bytes > PAGE_SIZE - ofs)
|
||||
bytes = PAGE_SIZE - ofs;
|
||||
goto again;
|
||||
}
|
||||
iov_iter_advance(i, copied);
|
||||
pos += copied;
|
||||
written += copied;
|
||||
balance_dirty_pages_ratelimited(mapping);
|
||||
|
@ -19,21 +19,17 @@ struct kvec {
|
||||
|
||||
enum iter_type {
|
||||
/* iter types */
|
||||
ITER_IOVEC = 4,
|
||||
ITER_KVEC = 8,
|
||||
ITER_BVEC = 16,
|
||||
ITER_PIPE = 32,
|
||||
ITER_DISCARD = 64,
|
||||
ITER_XARRAY = 128,
|
||||
ITER_IOVEC,
|
||||
ITER_KVEC,
|
||||
ITER_BVEC,
|
||||
ITER_PIPE,
|
||||
ITER_XARRAY,
|
||||
ITER_DISCARD,
|
||||
};
|
||||
|
||||
struct iov_iter {
|
||||
/*
|
||||
* Bit 0 is the read/write bit, set if we're writing.
|
||||
* Bit 1 is the BVEC_FLAG_NO_REF bit, set if type is a bvec and
|
||||
* the caller isn't expecting to drop a page reference when done.
|
||||
*/
|
||||
unsigned int type;
|
||||
u8 iter_type;
|
||||
bool data_source;
|
||||
size_t iov_offset;
|
||||
size_t count;
|
||||
union {
|
||||
@ -55,7 +51,7 @@ struct iov_iter {
|
||||
|
||||
static inline enum iter_type iov_iter_type(const struct iov_iter *i)
|
||||
{
|
||||
return i->type & ~(READ | WRITE);
|
||||
return i->iter_type;
|
||||
}
|
||||
|
||||
static inline bool iter_is_iovec(const struct iov_iter *i)
|
||||
@ -90,7 +86,7 @@ static inline bool iov_iter_is_xarray(const struct iov_iter *i)
|
||||
|
||||
static inline unsigned char iov_iter_rw(const struct iov_iter *i)
|
||||
{
|
||||
return i->type & (READ | WRITE);
|
||||
return i->data_source ? WRITE : READ;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -119,11 +115,11 @@ static inline struct iovec iov_iter_iovec(const struct iov_iter *iter)
|
||||
};
|
||||
}
|
||||
|
||||
size_t iov_iter_copy_from_user_atomic(struct page *page,
|
||||
struct iov_iter *i, unsigned long offset, size_t bytes);
|
||||
size_t copy_page_from_iter_atomic(struct page *page, unsigned offset,
|
||||
size_t bytes, struct iov_iter *i);
|
||||
void iov_iter_advance(struct iov_iter *i, size_t bytes);
|
||||
void iov_iter_revert(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(const struct iov_iter *i, size_t bytes);
|
||||
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);
|
||||
@ -132,9 +128,7 @@ size_t copy_page_from_iter(struct page *page, size_t offset, size_t bytes,
|
||||
|
||||
size_t _copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i);
|
||||
size_t _copy_from_iter(void *addr, size_t bytes, struct iov_iter *i);
|
||||
bool _copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i);
|
||||
size_t _copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i);
|
||||
bool _copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i);
|
||||
|
||||
static __always_inline __must_check
|
||||
size_t copy_to_iter(const void *addr, size_t bytes, struct iov_iter *i)
|
||||
@ -157,10 +151,11 @@ size_t copy_from_iter(void *addr, size_t bytes, struct iov_iter *i)
|
||||
static __always_inline __must_check
|
||||
bool copy_from_iter_full(void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
if (unlikely(!check_copy_size(addr, bytes, false)))
|
||||
return false;
|
||||
else
|
||||
return _copy_from_iter_full(addr, bytes, i);
|
||||
size_t copied = copy_from_iter(addr, bytes, i);
|
||||
if (likely(copied == bytes))
|
||||
return true;
|
||||
iov_iter_revert(i, copied);
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline __must_check
|
||||
@ -175,10 +170,11 @@ size_t copy_from_iter_nocache(void *addr, size_t bytes, struct iov_iter *i)
|
||||
static __always_inline __must_check
|
||||
bool copy_from_iter_full_nocache(void *addr, size_t bytes, struct iov_iter *i)
|
||||
{
|
||||
if (unlikely(!check_copy_size(addr, bytes, false)))
|
||||
return false;
|
||||
else
|
||||
return _copy_from_iter_full_nocache(addr, bytes, i);
|
||||
size_t copied = copy_from_iter_nocache(addr, bytes, i);
|
||||
if (likely(copied == bytes))
|
||||
return true;
|
||||
iov_iter_revert(i, copied);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE
|
||||
@ -278,7 +274,17 @@ struct csum_state {
|
||||
|
||||
size_t csum_and_copy_to_iter(const void *addr, size_t bytes, void *csstate, struct iov_iter *i);
|
||||
size_t csum_and_copy_from_iter(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
|
||||
bool csum_and_copy_from_iter_full(void *addr, size_t bytes, __wsum *csum, struct iov_iter *i);
|
||||
|
||||
static __always_inline __must_check
|
||||
bool csum_and_copy_from_iter_full(void *addr, size_t bytes,
|
||||
__wsum *csum, struct iov_iter *i)
|
||||
{
|
||||
size_t copied = csum_and_copy_from_iter(addr, bytes, csum, i);
|
||||
if (likely(copied == bytes))
|
||||
return true;
|
||||
iov_iter_revert(i, copied);
|
||||
return false;
|
||||
}
|
||||
size_t hash_and_copy_to_iter(const void *addr, size_t bytes, void *hashp,
|
||||
struct iov_iter *i);
|
||||
|
||||
@ -294,8 +300,4 @@ ssize_t __import_iovec(int type, const struct iovec __user *uvec,
|
||||
int import_single_range(int type, void __user *buf, size_t len,
|
||||
struct iovec *iov, struct iov_iter *i);
|
||||
|
||||
int iov_iter_for_each_range(struct iov_iter *i, size_t bytes,
|
||||
int (*f)(struct kvec *vec, void *context),
|
||||
void *context);
|
||||
|
||||
#endif
|
||||
|
@ -80,16 +80,18 @@ static inline __sum16 csum16_sub(__sum16 csum, __be16 addend)
|
||||
return csum16_add(csum, ~addend);
|
||||
}
|
||||
|
||||
static inline __wsum csum_shift(__wsum sum, int offset)
|
||||
{
|
||||
/* rotate sum to align it with a 16b boundary */
|
||||
if (offset & 1)
|
||||
return (__force __wsum)ror32((__force u32)sum, 8);
|
||||
return sum;
|
||||
}
|
||||
|
||||
static inline __wsum
|
||||
csum_block_add(__wsum csum, __wsum csum2, int offset)
|
||||
{
|
||||
u32 sum = (__force u32)csum2;
|
||||
|
||||
/* rotate sum to align it with a 16b boundary */
|
||||
if (offset & 1)
|
||||
sum = ror32(sum, 8);
|
||||
|
||||
return csum_add(csum, (__force __wsum)sum);
|
||||
return csum_add(csum, csum_shift(csum2, offset));
|
||||
}
|
||||
|
||||
static inline __wsum
|
||||
|
1227
lib/iov_iter.c
1227
lib/iov_iter.c
File diff suppressed because it is too large
Load Diff
36
mm/filemap.c
36
mm/filemap.c
@ -3642,10 +3642,6 @@ again:
|
||||
* Otherwise there's a nasty deadlock on copying from the
|
||||
* same page as we're writing to, without it being marked
|
||||
* up-to-date.
|
||||
*
|
||||
* Not only is this an optimisation, but it is also required
|
||||
* to check that the address is actually valid, when atomic
|
||||
* usercopies are used, below.
|
||||
*/
|
||||
if (unlikely(iov_iter_fault_in_readable(i, bytes))) {
|
||||
status = -EFAULT;
|
||||
@ -3665,33 +3661,31 @@ again:
|
||||
if (mapping_writably_mapped(mapping))
|
||||
flush_dcache_page(page);
|
||||
|
||||
copied = iov_iter_copy_from_user_atomic(page, i, offset, bytes);
|
||||
copied = copy_page_from_iter_atomic(page, offset, bytes, i);
|
||||
flush_dcache_page(page);
|
||||
|
||||
status = a_ops->write_end(file, mapping, pos, bytes, copied,
|
||||
page, fsdata);
|
||||
if (unlikely(status < 0))
|
||||
break;
|
||||
copied = status;
|
||||
|
||||
if (unlikely(status != copied)) {
|
||||
iov_iter_revert(i, copied - max(status, 0L));
|
||||
if (unlikely(status < 0))
|
||||
break;
|
||||
}
|
||||
cond_resched();
|
||||
|
||||
iov_iter_advance(i, copied);
|
||||
if (unlikely(copied == 0)) {
|
||||
if (unlikely(status == 0)) {
|
||||
/*
|
||||
* If we were unable to copy any data at all, we must
|
||||
* fall back to a single segment length write.
|
||||
*
|
||||
* If we didn't fallback here, we could livelock
|
||||
* because not all segments in the iov can be copied at
|
||||
* once without a pagefault.
|
||||
* A short copy made ->write_end() reject the
|
||||
* thing entirely. Might be memory poisoning
|
||||
* halfway through, might be a race with munmap,
|
||||
* might be severe memory pressure.
|
||||
*/
|
||||
bytes = min_t(unsigned long, PAGE_SIZE - offset,
|
||||
iov_iter_single_seg_count(i));
|
||||
if (copied)
|
||||
bytes = copied;
|
||||
goto again;
|
||||
}
|
||||
pos += copied;
|
||||
written += copied;
|
||||
pos += status;
|
||||
written += status;
|
||||
|
||||
balance_dirty_pages_ratelimited(mapping);
|
||||
} while (iov_iter_count(i));
|
||||
|
Loading…
Reference in New Issue
Block a user