Skip to content

Commit

Permalink
Merge tag 'iter-ubuf.2-2023-04-21' of git://git.kernel.dk/linux
Browse files Browse the repository at this point in the history
Pull ITER_UBUF updates from Jens Axboe:
 "This turns singe vector imports into ITER_UBUF, rather than
  ITER_IOVEC.

  The former is more trivial to iterate and advance, and hence a bit
  more efficient. From some very unscientific testing, ~60% of all iovec
  imports are single vector"

* tag 'iter-ubuf.2-2023-04-21' of git://git.kernel.dk/linux:
  iov_iter: Mark copy_compat_iovec_from_user() noinline
  iov_iter: import single vector iovecs as ITER_UBUF
  iov_iter: convert import_single_range() to ITER_UBUF
  iov_iter: overlay struct iovec and ubuf/len
  iov_iter: set nr_segs = 1 for ITER_UBUF
  iov_iter: remove iov_iter_iovec()
  iov_iter: add iter_iov_addr() and iter_iov_len() helpers
  ALSA: pcm: check for user backed iterator, not specific iterator type
  IB/qib: check for user backed iterator, not specific iterator type
  IB/hfi1: check for user backed iterator, not specific iterator type
  iov_iter: add iter_iovec() helper
  block: ensure bio_alloc_map_data() deals with ITER_UBUF correctly
  • Loading branch information
torvalds committed Apr 24, 2023
2 parents d88867a + 50f9a76 commit b9dff21
Show file tree
Hide file tree
Showing 14 changed files with 165 additions and 105 deletions.
7 changes: 4 additions & 3 deletions block/blk-map.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ static struct bio_map_data *bio_alloc_map_data(struct iov_iter *data,
bmd = kmalloc(struct_size(bmd, iov, data->nr_segs), gfp_mask);
if (!bmd)
return NULL;
memcpy(bmd->iov, data->iov, sizeof(struct iovec) * data->nr_segs);
bmd->iter = *data;
if (iter_is_iovec(data))
bmd->iter.iov = bmd->iov;
if (iter_is_iovec(data)) {
memcpy(bmd->iov, iter_iov(data), sizeof(struct iovec) * data->nr_segs);
bmd->iter.__iov = bmd->iov;
}
return bmd;
}

Expand Down
10 changes: 4 additions & 6 deletions drivers/infiniband/hw/hfi1/file_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,18 +267,15 @@ static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from)

if (!HFI1_CAP_IS_KSET(SDMA))
return -EINVAL;
if (!from->user_backed)
return -EINVAL;
idx = srcu_read_lock(&fd->pq_srcu);
pq = srcu_dereference(fd->pq, &fd->pq_srcu);
if (!cq || !pq) {
srcu_read_unlock(&fd->pq_srcu, idx);
return -EIO;
}

if (!iter_is_iovec(from) || !dim) {
srcu_read_unlock(&fd->pq_srcu, idx);
return -EINVAL;
}

trace_hfi1_sdma_request(fd->dd, fd->uctxt->ctxt, fd->subctxt, dim);

if (atomic_read(&pq->n_reqs) == pq->n_max_reqs) {
Expand All @@ -287,11 +284,12 @@ static ssize_t hfi1_write_iter(struct kiocb *kiocb, struct iov_iter *from)
}

while (dim) {
const struct iovec *iov = iter_iov(from);
int ret;
unsigned long count = 0;

ret = hfi1_user_sdma_process_request(
fd, (struct iovec *)(from->iov + done),
fd, (struct iovec *)(iov + done),
dim, &count);
if (ret) {
reqs = ret;
Expand Down
4 changes: 2 additions & 2 deletions drivers/infiniband/hw/qib/qib_file_ops.c
Original file line number Diff line number Diff line change
Expand Up @@ -2245,10 +2245,10 @@ static ssize_t qib_write_iter(struct kiocb *iocb, struct iov_iter *from)
struct qib_ctxtdata *rcd = ctxt_fp(iocb->ki_filp);
struct qib_user_sdma_queue *pq = fp->pq;

if (!iter_is_iovec(from) || !from->nr_segs || !pq)
if (!from->user_backed || !from->nr_segs || !pq)
return -EINVAL;

return qib_user_sdma_writev(rcd, pq, from->iov, from->nr_segs);
return qib_user_sdma_writev(rcd, pq, iter_iov(from), from->nr_segs);
}

static struct class *qib_class;
Expand Down
3 changes: 2 additions & 1 deletion drivers/net/tun.c
Original file line number Diff line number Diff line change
Expand Up @@ -1486,7 +1486,8 @@ static struct sk_buff *tun_napi_alloc_frags(struct tun_file *tfile,
skb->truesize += skb->data_len;

for (i = 1; i < it->nr_segs; i++) {
size_t fragsz = it->iov[i].iov_len;
const struct iovec *iov = iter_iov(it);
size_t fragsz = iov->iov_len;
struct page *page;
void *frag;

Expand Down
2 changes: 1 addition & 1 deletion drivers/vhost/scsi.c
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ vhost_scsi_calc_sgls(struct iov_iter *iter, size_t bytes, int max_sgls)
{
int sgl_count = 0;

if (!iter || !iter->iov) {
if (!iter || !iter_iov(iter)) {
pr_err("%s: iter->iov is NULL, but expected bytes: %zu"
" present\n", __func__, bytes);
return -EINVAL;
Expand Down
11 changes: 8 additions & 3 deletions fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -3730,10 +3730,15 @@ static int check_direct_read(struct btrfs_fs_info *fs_info,
if (!iter_is_iovec(iter))
return 0;

for (seg = 0; seg < iter->nr_segs; seg++)
for (i = seg + 1; i < iter->nr_segs; i++)
if (iter->iov[seg].iov_base == iter->iov[i].iov_base)
for (seg = 0; seg < iter->nr_segs; seg++) {
for (i = seg + 1; i < iter->nr_segs; i++) {
const struct iovec *iov1 = iter_iov(iter) + seg;
const struct iovec *iov2 = iter_iov(iter) + i;

if (iov1->iov_base == iov2->iov_base)
return -EINVAL;
}
}
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion fs/fuse/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)

static inline unsigned long fuse_get_user_addr(const struct iov_iter *ii)
{
return (unsigned long)ii->iov->iov_base + ii->iov_offset;
return (unsigned long)iter_iov(ii)->iov_base + ii->iov_offset;
}

static inline size_t fuse_get_frag_size(const struct iov_iter *ii,
Expand Down
11 changes: 5 additions & 6 deletions fs/read_write.c
Original file line number Diff line number Diff line change
Expand Up @@ -749,15 +749,14 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
return -EOPNOTSUPP;

while (iov_iter_count(iter)) {
struct iovec iovec = iov_iter_iovec(iter);
ssize_t nr;

if (type == READ) {
nr = filp->f_op->read(filp, iovec.iov_base,
iovec.iov_len, ppos);
nr = filp->f_op->read(filp, iter_iov_addr(iter),
iter_iov_len(iter), ppos);
} else {
nr = filp->f_op->write(filp, iovec.iov_base,
iovec.iov_len, ppos);
nr = filp->f_op->write(filp, iter_iov_addr(iter),
iter_iov_len(iter), ppos);
}

if (nr < 0) {
Expand All @@ -766,7 +765,7 @@ static ssize_t do_loop_readv_writev(struct file *filp, struct iov_iter *iter,
break;
}
ret += nr;
if (nr != iovec.iov_len)
if (nr != iter_iov_len(iter))
break;
iov_iter_advance(iter, nr);
}
Expand Down
57 changes: 40 additions & 17 deletions include/linux/uio.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,35 @@ struct iov_iter {
size_t iov_offset;
int last_offset;
};
size_t count;
/*
* Hack alert: overlay ubuf_iovec with iovec + count, so
* that the members resolve correctly regardless of the type
* of iterator used. This means that you can use:
*
* &iter->__ubuf_iovec or iter->__iov
*
* interchangably for the user_backed cases, hence simplifying
* some of the cases that need to deal with both.
*/
union {
const struct iovec *iov;
const struct kvec *kvec;
const struct bio_vec *bvec;
struct xarray *xarray;
struct pipe_inode_info *pipe;
void __user *ubuf;
/*
* This really should be a const, but we cannot do that without
* also modifying any of the zero-filling iter init functions.
* Leave it non-const for now, but it should be treated as such.
*/
struct iovec __ubuf_iovec;
struct {
union {
/* use iter_iov() to get the current vec */
const struct iovec *__iov;
const struct kvec *kvec;
const struct bio_vec *bvec;
struct xarray *xarray;
struct pipe_inode_info *pipe;
void __user *ubuf;
};
size_t count;
};
};
union {
unsigned long nr_segs;
Expand All @@ -68,6 +89,16 @@ struct iov_iter {
};
};

static inline const struct iovec *iter_iov(const struct iov_iter *iter)
{
if (iter->iter_type == ITER_UBUF)
return (const struct iovec *) &iter->__ubuf_iovec;
return iter->__iov;
}

#define iter_iov_addr(iter) (iter_iov(iter)->iov_base + (iter)->iov_offset)
#define iter_iov_len(iter) (iter_iov(iter)->iov_len - (iter)->iov_offset)

static inline enum iter_type iov_iter_type(const struct iov_iter *i)
{
return i->iter_type;
Expand Down Expand Up @@ -143,15 +174,6 @@ static inline size_t iov_length(const struct iovec *iov, unsigned long nr_segs)
return ret;
}

static inline struct iovec iov_iter_iovec(const struct iov_iter *iter)
{
return (struct iovec) {
.iov_base = iter->iov->iov_base + iter->iov_offset,
.iov_len = min(iter->count,
iter->iov->iov_len - iter->iov_offset),
};
}

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);
Expand Down Expand Up @@ -359,7 +381,8 @@ static inline void iov_iter_ubuf(struct iov_iter *i, unsigned int direction,
.user_backed = true,
.data_source = direction,
.ubuf = buf,
.count = count
.count = count,
.nr_segs = 1
};
}
/* Flags for iov_iter_get/extract_pages*() */
Expand Down
4 changes: 2 additions & 2 deletions io_uring/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,8 @@ static int io_setup_async_msg(struct io_kiocb *req,
async_msg->msg.msg_name = &async_msg->addr;
/* if were using fast_iov, set it to the new one */
if (iter_is_iovec(&kmsg->msg.msg_iter) && !kmsg->free_iov) {
size_t fast_idx = kmsg->msg.msg_iter.iov - kmsg->fast_iov;
async_msg->msg.msg_iter.iov = &async_msg->fast_iov[fast_idx];
size_t fast_idx = iter_iov(&kmsg->msg.msg_iter) - kmsg->fast_iov;
async_msg->msg.msg_iter.__iov = &async_msg->fast_iov[fast_idx];
}

return -EAGAIN;
Expand Down
35 changes: 17 additions & 18 deletions io_uring/rw.c
Original file line number Diff line number Diff line change
Expand Up @@ -447,26 +447,25 @@ static ssize_t loop_rw_iter(int ddir, struct io_rw *rw, struct iov_iter *iter)
ppos = io_kiocb_ppos(kiocb);

while (iov_iter_count(iter)) {
struct iovec iovec;
void __user *addr;
size_t len;
ssize_t nr;

if (iter_is_ubuf(iter)) {
iovec.iov_base = iter->ubuf + iter->iov_offset;
iovec.iov_len = iov_iter_count(iter);
addr = iter->ubuf + iter->iov_offset;
len = iov_iter_count(iter);
} else if (!iov_iter_is_bvec(iter)) {
iovec = iov_iter_iovec(iter);
addr = iter_iov_addr(iter);
len = iter_iov_len(iter);
} else {
iovec.iov_base = u64_to_user_ptr(rw->addr);
iovec.iov_len = rw->len;
addr = u64_to_user_ptr(rw->addr);
len = rw->len;
}

if (ddir == READ) {
nr = file->f_op->read(file, iovec.iov_base,
iovec.iov_len, ppos);
} else {
nr = file->f_op->write(file, iovec.iov_base,
iovec.iov_len, ppos);
}
if (ddir == READ)
nr = file->f_op->read(file, addr, len, ppos);
else
nr = file->f_op->write(file, addr, len, ppos);

if (nr < 0) {
if (!ret)
Expand All @@ -482,7 +481,7 @@ static ssize_t loop_rw_iter(int ddir, struct io_rw *rw, struct iov_iter *iter)
if (!rw->len)
break;
}
if (nr != iovec.iov_len)
if (nr != len)
break;
}

Expand All @@ -503,10 +502,10 @@ static void io_req_map_rw(struct io_kiocb *req, const struct iovec *iovec,
if (!iovec) {
unsigned iov_off = 0;

io->s.iter.iov = io->s.fast_iov;
if (iter->iov != fast_iov) {
iov_off = iter->iov - fast_iov;
io->s.iter.iov += iov_off;
io->s.iter.__iov = io->s.fast_iov;
if (iter->__iov != fast_iov) {
iov_off = iter_iov(iter) - fast_iov;
io->s.iter.__iov += iov_off;
}
if (io->s.fast_iov != fast_iov)
memcpy(io->s.fast_iov + iov_off, fast_iov + iov_off,
Expand Down
Loading

0 comments on commit b9dff21

Please sign in to comment.