Skip to content

Commit

Permalink
new iov_iter flavour - ITER_UBUF
Browse files Browse the repository at this point in the history
Equivalent of single-segment iovec.  Initialized by iov_iter_ubuf(),
checked for by iter_is_ubuf(), otherwise behaves like ITER_IOVEC
ones.

We are going to expose the things like ->write_iter() et.al. to those
in subsequent commits.

New predicate (user_backed_iter()) that is true for ITER_IOVEC and
ITER_UBUF; places like direct-IO handling should use that for
checking that pages we modify after getting them from iov_iter_get_pages()
would need to be dirtied.

DO NOT assume that replacing iter_is_iovec() with user_backed_iter()
will solve all problems - there's code that uses iter_is_iovec() to
decide how to poke around in iov_iter guts and for that the predicate
replacement obviously won't suffice.

Signed-off-by: Al Viro <[email protected]>
  • Loading branch information
Al Viro committed Aug 9, 2022
1 parent fa9db65 commit fcb14cb
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 31 deletions.
6 changes: 3 additions & 3 deletions block/fops.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ static ssize_t __blkdev_direct_IO_simple(struct kiocb *iocb,

if (iov_iter_rw(iter) == READ) {
bio_init(&bio, bdev, vecs, nr_pages, REQ_OP_READ);
if (iter_is_iovec(iter))
if (user_backed_iter(iter))
should_dirty = true;
} else {
bio_init(&bio, bdev, vecs, nr_pages, dio_bio_write_op(iocb));
Expand Down Expand Up @@ -204,7 +204,7 @@ static ssize_t __blkdev_direct_IO(struct kiocb *iocb, struct iov_iter *iter,
}

dio->size = 0;
if (is_read && iter_is_iovec(iter))
if (is_read && user_backed_iter(iter))
dio->flags |= DIO_SHOULD_DIRTY;

blk_start_plug(&plug);
Expand Down Expand Up @@ -335,7 +335,7 @@ static ssize_t __blkdev_direct_IO_async(struct kiocb *iocb,
dio->size = bio->bi_iter.bi_size;

if (is_read) {
if (iter_is_iovec(iter)) {
if (user_backed_iter(iter)) {
dio->flags |= DIO_SHOULD_DIRTY;
bio_set_pages_dirty(bio);
}
Expand Down
2 changes: 1 addition & 1 deletion fs/ceph/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1262,7 +1262,7 @@ ceph_direct_read_write(struct kiocb *iocb, struct iov_iter *iter,
size_t count = iov_iter_count(iter);
loff_t pos = iocb->ki_pos;
bool write = iov_iter_rw(iter) == WRITE;
bool should_dirty = !write && iter_is_iovec(iter);
bool should_dirty = !write && user_backed_iter(iter);

if (write && ceph_snap(file_inode(file)) != CEPH_NOSNAP)
return -EROFS;
Expand Down
2 changes: 1 addition & 1 deletion fs/cifs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -4004,7 +4004,7 @@ static ssize_t __cifs_readv(
if (!is_sync_kiocb(iocb))
ctx->iocb = iocb;

if (iter_is_iovec(to))
if (user_backed_iter(to))
ctx->should_dirty = true;

if (direct) {
Expand Down
2 changes: 1 addition & 1 deletion fs/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ ssize_t __blockdev_direct_IO(struct kiocb *iocb, struct inode *inode,
spin_lock_init(&dio->bio_lock);
dio->refcount = 1;

dio->should_dirty = iter_is_iovec(iter) && iov_iter_rw(iter) == READ;
dio->should_dirty = user_backed_iter(iter) && iov_iter_rw(iter) == READ;
sdio.iter = iter;
sdio.final_block_in_request = end >> blkbits;

Expand Down
4 changes: 2 additions & 2 deletions fs/fuse/dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,7 @@ static ssize_t fuse_dev_read(struct kiocb *iocb, struct iov_iter *to)
if (!fud)
return -EPERM;

if (!iter_is_iovec(to))
if (!user_backed_iter(to))
return -EINVAL;

fuse_copy_init(&cs, 1, to);
Expand Down Expand Up @@ -1949,7 +1949,7 @@ static ssize_t fuse_dev_write(struct kiocb *iocb, struct iov_iter *from)
if (!fud)
return -EPERM;

if (!iter_is_iovec(from))
if (!user_backed_iter(from))
return -EINVAL;

fuse_copy_init(&cs, 0, from);
Expand Down
2 changes: 1 addition & 1 deletion fs/fuse/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1465,7 +1465,7 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter,
inode_unlock(inode);
}

io->should_dirty = !write && iter_is_iovec(iter);
io->should_dirty = !write && user_backed_iter(iter);
while (count) {
ssize_t nres;
fl_owner_t owner = current->files;
Expand Down
2 changes: 1 addition & 1 deletion fs/gfs2/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -780,7 +780,7 @@ static inline bool should_fault_in_pages(struct iov_iter *i,

if (!count)
return false;
if (!iter_is_iovec(i))
if (!user_backed_iter(i))
return false;

size = PAGE_SIZE;
Expand Down
2 changes: 1 addition & 1 deletion fs/iomap/direct-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ __iomap_dio_rw(struct kiocb *iocb, struct iov_iter *iter,
iomi.flags |= IOMAP_NOWAIT;
}

if (iter_is_iovec(iter))
if (user_backed_iter(iter))
dio->flags |= IOMAP_DIO_DIRTY;
} else {
iomi.flags |= IOMAP_WRITE;
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/direct.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@ ssize_t nfs_file_direct_read(struct kiocb *iocb, struct iov_iter *iter,
if (!is_sync_kiocb(iocb))
dreq->iocb = iocb;

if (iter_is_iovec(iter))
if (user_backed_iter(iter))
dreq->flags = NFS_ODIRECT_SHOULD_DIRTY;

if (!swap)
Expand Down
26 changes: 26 additions & 0 deletions include/linux/uio.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ enum iter_type {
ITER_PIPE,
ITER_XARRAY,
ITER_DISCARD,
ITER_UBUF,
};

struct iov_iter_state {
Expand All @@ -38,6 +39,7 @@ struct iov_iter {
u8 iter_type;
bool nofault;
bool data_source;
bool user_backed;
size_t iov_offset;
size_t count;
union {
Expand All @@ -46,6 +48,7 @@ struct iov_iter {
const struct bio_vec *bvec;
struct xarray *xarray;
struct pipe_inode_info *pipe;
void __user *ubuf;
};
union {
unsigned long nr_segs;
Expand All @@ -70,6 +73,11 @@ static inline void iov_iter_save_state(struct iov_iter *iter,
state->nr_segs = iter->nr_segs;
}

static inline bool iter_is_ubuf(const struct iov_iter *i)
{
return iov_iter_type(i) == ITER_UBUF;
}

static inline bool iter_is_iovec(const struct iov_iter *i)
{
return iov_iter_type(i) == ITER_IOVEC;
Expand Down Expand Up @@ -105,6 +113,11 @@ static inline unsigned char iov_iter_rw(const struct iov_iter *i)
return i->data_source ? WRITE : READ;
}

static inline bool user_backed_iter(const struct iov_iter *i)
{
return i->user_backed;
}

/*
* Total number of bytes covered by an iovec.
*
Expand Down Expand Up @@ -322,4 +335,17 @@ 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);

static inline void iov_iter_ubuf(struct iov_iter *i, unsigned int direction,
void __user *buf, size_t count)
{
WARN_ON(direction & ~(READ | WRITE));
*i = (struct iov_iter) {
.iter_type = ITER_UBUF,
.user_backed = true,
.data_source = direction,
.ubuf = buf,
.count = count
};
}

#endif
Loading

0 comments on commit fcb14cb

Please sign in to comment.