Pull fuse updates from Miklos Szeredi: - Improve performance of the io-uring transport by introducing buffer pools and zero-copy (Joanne) - Fix lots of bugs (Baokun Li) - Fix io-uring initialization issues (Joanne, Bernd) - More prep work for large folios (Joanne) - Don't limit buffered read to 128k (Jim Harris) - Fix zeroing of page end (dirtied with mmap) on file size extension (Jimmy Zuber) - Improve performance in certain cases with wake_up_sync() when queuing request (Xuewen Yan) - Misc fixes and cleanups (Xuewen Yan) * tag 'fuse-update-7.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse: (35 commits) fuse: zero the partial EOF page when extending a file io_uring: Add missing include for ITER_SOURCE and ITER_DEST fuse: Fix the condition to enable over-io-uring fuse: invalidate the correct range after O_APPEND direct write selftests/fuse: test post-EOF page zeroing when a file is extended fuse: wake one waiter per freed slot when raising max_background fuse: use min_not_zero() in fuse_init_server_timeout() fuse: copy request headers via a stack buffer for io-uring fuse: give wakeup hints to the scheduler for synchronous requests fuse: check for NULL root inode in fuse_fill_super_submount fuse: reject a duplicate fd= mount option cuse: wait for pending RCU callbacks on module exit fuse: fix invalidate lock leak on open O_TRUNC DAX failure fuse: fix invalidate lock leak on setattr writeback failure fuse: wait for FR_FINISHED on abort_on_kill to prevent use-after-free fuse: make dentry_tree_work static docs: fuse: document io-uring buffer pool and zero-copy uapi fuse: add zero-copy over io-uring fuse: support registered buffer pools in io-uring fuse: add io-uring buffer pools ...
Unified · Split
| @@ -382,17 +382,17 @@ Zero copy · 381 unmodified lines | |||
| 382 | --------- | 382 | --------- |
| 383 | 383 | ||
| 384 | ublk zero copy relies on io_uring's fixed kernel buffer, which provides | 384 | ublk zero copy relies on io_uring's fixed kernel buffer, which provides |
| 385 | two APIs: `io_buffer_register_bvec()` and `io_buffer_unregister_bvec`. | 385 | two APIs: `io_buffer_register_request()` and `io_buffer_unregister`. |
| 386 | 386 | ||
| 387 | ublk adds IO command of `UBLK_IO_REGISTER_IO_BUF` to call | 387 | ublk adds IO command of `UBLK_IO_REGISTER_IO_BUF` to call |
| 388 | `io_buffer_register_bvec()` for ublk server to register client request | 388 | `io_buffer_register_request()` for ublk server to register client request |
| 389 | buffer into io_uring buffer table, then ublk server can submit io_uring | 389 | buffer into io_uring buffer table, then ublk server can submit io_uring |
| 390 | IOs with the registered buffer index. IO command of `UBLK_IO_UNREGISTER_IO_BUF` | 390 | IOs with the registered buffer index. IO command of `UBLK_IO_UNREGISTER_IO_BUF` |
| 391 | calls `io_buffer_unregister_bvec()` to unregister the buffer, which is | 391 | calls `io_buffer_unregister()` to unregister the buffer, which is guaranteed |
| 392 | guaranteed to be live between calling `io_buffer_register_bvec()` and | 392 | to be live between calling `io_buffer_register_request()` and |
| 393 | `io_buffer_unregister_bvec()`. Any io_uring operation which supports this | 393 | `io_buffer_unregister()`. Any io_uring operation which supports this kind of |
| 394 | kind of kernel buffer will grab one reference of the buffer until the | 394 | kernel buffer will grab one reference of the buffer until the operation is |
| 395 | operation is completed. | 395 | completed. |
| 396 | 396 | ||
| 397 | ublk server implementing zero copy or user copy has to be CAP_SYS_ADMIN and | 397 | ublk server implementing zero copy or user copy has to be CAP_SYS_ADMIN and |
| 398 | be trusted, because it is ublk server's responsibility to make sure IO buffer | 398 | be trusted, because it is ublk server's responsibility to make sure IO buffer |
| @@ -11,6 +11,9 @@ and works. For generic details about FUSE see fuse.rst. · 10 unmodified lines | |||
| 11 | This document also covers the current interface, which is | 11 | This document also covers the current interface, which is |
| 12 | still in development and might change. | 12 | still in development and might change. |
| 13 | 13 | ||
| 14 | For the userspace protocol, see | ||
| 15 | Documentation/filesystems/fuse/uapi/fuse-uapi-io-uring.rst. | ||
| 16 | |||
| 14 | Limitations | 17 | Limitations |
| 15 | =========== | 18 | =========== |
| 16 | As of now not all requests types are supported through io-uring, userspace | 19 | As of now not all requests types are supported through io-uring, userspace |
| @@ -95,5 +98,34 @@ Sending requests with CQEs · 78 unmodified lines | |||
| 95 | | <fuse_unlink() | | 98 | | <fuse_unlink() | |
| 96 | | <sys_unlink() | | 99 | | <sys_unlink() | |
| 97 | 100 | ||
| 98 | 101 | Buffer pools | |
| 99 | 102 | ============ | |
| 103 | |||
| 104 | Without a buffer pool, every entry needs to pass a dedicated payload buffer | ||
| 105 | large enough for the maximum payload size. A buffer pool decouples entries | ||
| 106 | from payload buffers. The server hands the kernel one contiguous buffer pool | ||
| 107 | of memory and when the kernel sends the server a request, it indicates the | ||
| 108 | offset into the pool for that request's payload. Internally, the kernel is | ||
| 109 | able to manage/optimize the buffer pool memory however it likes. | ||
| 110 | |||
| 111 | A server may also register the pool region with io_uring as a fixed buffer. | ||
| 112 | The backing pages are then pinned once, avoiding per-request pinning and | ||
| 113 | address translation. This also allows servers to use the same registered | ||
| 114 | buffers for subsequent backing store I/O through io-uring, keeping data | ||
| 115 | in the same pinned pages without additional pinning / mapping overhead. | ||
| 116 | |||
| 117 | Zero-copy | ||
| 118 | ========= | ||
| 119 | |||
| 120 | Zero-copy lets the server read from / write to the client's pages (pinned | ||
| 121 | user pages for direct I/O, or page-cache folios for buffered I/O) without an | ||
| 122 | intermediary payload copy. This requires CAP_SYS_ADMIN privileges. | ||
| 123 | |||
| 124 | When a fuse request arrives for a file that opted into zero-copy, the kernel | ||
| 125 | registers the relevant pages (pinned user pages for direct i/o or underlying | ||
| 126 | page cache folios for buffered i/o) into a sparse slot in the server's | ||
| 127 | io_uring registered buffer table. The server can then operate on these pages | ||
| 128 | directly using io-uring fixed buffer operations (eg read_fixed / write_fixed) | ||
| 129 | and the kernel unregisters these pages when the request completes. | ||
| 130 | Non-page-backed args (eg op out headers) will go through the payload buffer as | ||
| 131 | normal. | ||
| @@ -12,3 +12,4 @@ FUSE (Filesystem in Userspace) Technical Documentation · 11 unmodified lines | |||
| 12 | fuse-io | 12 | fuse-io |
| 13 | fuse-io-uring | 13 | fuse-io-uring |
| 14 | fuse-passthrough | 14 | fuse-passthrough |
| 15 | uapi/fuse-uapi-io-uring | ||
| @@ -0,0 +1,126 @@ | |||
| 1 | .. SPDX-License-Identifier: GPL-2.0 | ||
| 2 | |||
| 3 | ===================================== | ||
| 4 | FUSE-over-io-uring uapi documentation | ||
| 5 | ===================================== | ||
| 6 | |||
| 7 | Commands | ||
| 8 | ======== | ||
| 9 | |||
| 10 | ``enum fuse_uring_cmd``: | ||
| 11 | |||
| 12 | ``FUSE_IO_URING_CMD_ADD_QUEUE`` | ||
| 13 | Create a queue identified by ``fuse_uring_cmd_req.qid``. Queue-wide | ||
| 14 | options are passed in ``fuse_uring_cmd_req.flags``: | ||
| 15 | |||
| 16 | ``FUSE_URING_ZERO_COPY`` | ||
| 17 | Enable zero-copy on this queue. Requires ``CAP_SYS_ADMIN`` and a buffer | ||
| 18 | pool, which is added separately via ``ADD_BUFPOOL`` before registering | ||
| 19 | entries (see `Zero-copy`_). | ||
| 20 | |||
| 21 | ``FUSE_IO_URING_CMD_ADD_BUFPOOL`` | ||
| 22 | Register the payload buffer pool for an existing queue. The server provides | ||
| 23 | a single contiguous region in ``fuse_uring_cmd_req.bufpool.uaddr`` / | ||
| 24 | ``.len``. This command must be issued after ``ADD_QUEUE`` and before | ||
| 25 | registering any payload-carrying entries on that queue. | ||
| 26 | ``fuse_uring_cmd_req.flags`` must be 0. Submitting this command with | ||
| 27 | ``IORING_URING_CMD_FIXED`` marks the pool as registered, which avoids per | ||
| 28 | i/o pinning/unpinning and mapping overhead (see `Buffer pools`_). | ||
| 29 | |||
| 30 | ``FUSE_IO_URING_CMD_REGISTER`` | ||
| 31 | Register a ring entry (a long-lived SQE that carries the request header | ||
| 32 | iovec). For a zero-copy queue, ``fuse_uring_cmd_req.ent_zero_copy_buf_index`` | ||
| 33 | indicates the reserved registered buffer table slot this entry uses for | ||
| 34 | zero-copy (see `Zero-copy`_). | ||
| 35 | |||
| 36 | ``FUSE_IO_URING_CMD_COMMIT_AND_FETCH`` | ||
| 37 | Commit the reply for a completed request and fetch the next one. The | ||
| 38 | request is identified by ``fuse_uring_cmd_req.commit_id`` (the value the | ||
| 39 | kernel reported in ``fuse_uring_ent_in_out.commit_id``). | ||
| 40 | |||
| 41 | Structures | ||
| 42 | ========== | ||
| 43 | |||
| 44 | ``struct fuse_uring_cmd_req`` (80-byte SQE command area): | ||
| 45 | |||
| 46 | ============================ ================================================== | ||
| 47 | Field Meaning | ||
| 48 | ============================ ================================================== | ||
| 49 | ``flags`` Command-specific flags (see each command). | ||
| 50 | ``commit_id`` Request id, for ``COMMIT_AND_FETCH``. | ||
| 51 | ``qid`` Queue index. | ||
| 52 | ``bufpool.uaddr`` Pool base address, for ``ADD_BUFPOOL``. | ||
| 53 | ``bufpool.len`` Pool length in bytes, for ``ADD_BUFPOOL``. | ||
| 54 | ``bufpool.reserved`` Must be 0, for ``ADD_BUFPOOL``. | ||
| 55 | ``ent_zero_copy_buf_index`` Per-entry zero-copy slot, for ``REGISTER``. | ||
| 56 | ============================ ================================================== | ||
| 57 | |||
| 58 | ``struct fuse_uring_ent_in_out`` (reported by the kernel per request): | ||
| 59 | |||
| 60 | ============================ ================================================== | ||
| 61 | Field Meaning | ||
| 62 | ============================ ================================================== | ||
| 63 | ``flags`` ``FUSE_URING_ENT_ZERO_COPY`` if zero-copied. | ||
| 64 | ``commit_id`` Id to echo back in ``COMMIT_AND_FETCH``. | ||
| 65 | ``payload_sz`` Total payload size in bytes (see `Zero-copy`_). | ||
| 66 | ``offset`` Payload buffer offset within the pool. | ||
| 67 | ============================ ================================================== | ||
| 68 | |||
| 69 | Buffer pools | ||
| 70 | ============ | ||
| 71 | Setup: | ||
| 72 | |||
| 73 | * Issue ``ADD_QUEUE`` for the qid. | ||
| 74 | * Issue ``ADD_BUFPOOL`` with ``bufpool.uaddr`` and ``bufpool.len`` pointing | ||
| 75 | at the region. | ||
| 76 | * Register entries with ``REGISTER``. | ||
| 77 | |||
| 78 | For every request that has a payload, the kernel reports where the payload | ||
| 79 | lives in ``struct fuse_uring_ent_in_out`` (part of | ||
| 80 | ``struct fuse_uring_req_header``): | ||
| 81 | |||
| 82 | ``offset`` | ||
| 83 | Byte offset, within the pool region, for this request's payload buffer. | ||
| 84 | The server adds this to the pool base address to locate the payload. | ||
| 85 | |||
| 86 | ``payload_sz`` | ||
| 87 | Number of payload bytes for this request. | ||
| 88 | |||
| 89 | To use registered buffers, the server registers the pool region with io_uring | ||
| 90 | and submits ``ADD_BUFPOOL`` with ``IORING_URING_CMD_FIXED`` set in | ||
| 91 | ``sqe->uring_cmd_flags`` and the index of the registered bufpool in | ||
| 92 | ``sqe->buf_index``. Every SQE the server submits afterwards must follow the | ||
| 93 | same fixed-buffer protocol, carrying ``IORING_URING_CMD_FIXED`` and that same | ||
| 94 | ``sqe->buf_index``. The same registered buffer can be reused for the server's | ||
| 95 | backing-store I/O as well (e.g. ``IORING_OP_READ_FIXED`` / | ||
| 96 | ``IORING_OP_WRITE_FIXED``). | ||
| 97 | |||
| 98 | Zero-copy | ||
| 99 | ========= | ||
| 100 | Requirements: | ||
| 101 | |||
| 102 | * The server must be privileged (``CAP_SYS_ADMIN``). | ||
| 103 | * A zero-copy queue: ``ADD_QUEUE`` with the ``FUSE_URING_ZERO_COPY`` flag set. | ||
| 104 | * A buffer pool: ``ADD_BUFPOOL``. | ||
| 105 | * For each entry, ``REGISTER`` with ``ent_zero_copy_buf_index`` set to the | ||
| 106 | index this entry uses in the server's io_uring registered-buffer table. | ||
| 107 | This is where the kernel registers the request's pages for the server to | ||
| 108 | access (it is separate from the payload pool). On a non-zero-copy queue this | ||
| 109 | field must be 0. | ||
| 110 | |||
| 111 | Zero-copy is selected per open file. The server sets the open-file flag in | ||
| 112 | the ``FUSE_OPEN`` / ``FUSE_CREATE`` reply: | ||
| 113 | |||
| 114 | ``FOPEN_IO_URING_ZERO_COPY`` | ||
| 115 | Reads/writes on this open file should use zero-copy. | ||
| 116 | |||
| 117 | For a request that is zero-copied, the kernel sets ``FUSE_URING_ENT_ZERO_COPY`` | ||
| 118 | in ``fuse_uring_ent_in_out.flags`` and places the request's pages at the | ||
| 119 | entry's ``ent_zero_copy_buf_index``. The server then issues | ||
| 120 | ``IORING_OP_READ_FIXED`` / ``IORING_OP_WRITE_FIXED`` against that index to | ||
| 121 | transfer the data directly to/from the client's pages. | ||
| 122 | |||
| 123 | For such a request, ``payload_sz`` includes the zero-copied page bytes | ||
| 124 | (transferred via the registered buffer at ``ent_zero_copy_buf_index``). Any | ||
| 125 | non-page-backed args (e.g. op headers) are still copied through the pool | ||
| 126 | payload buffer at ``offset``. | ||
| @@ -1699,8 +1699,8 @@ ublk_auto_buf_register(const struct ublk_queue *ubq, struct request *req, · 1698 unmodified lines | |||
| 1699 | { | 1699 | { |
| 1700 | int ret; | 1700 | int ret; |
| 1701 | 1701 | ||
| 1702 | ret = io_buffer_register_bvec(cmd, req, ublk_io_release, | 1702 | ret = io_buffer_register_request(cmd, req, ublk_io_release, |
| 1703 | io->buf.auto_reg.index, issue_flags); | 1703 | io->buf.auto_reg.index, issue_flags); |
| 1704 | if (ret) { | 1704 | if (ret) { |
| 1705 | if (io->buf.auto_reg.flags & UBLK_AUTO_BUF_REG_FALLBACK) { | 1705 | if (io->buf.auto_reg.flags & UBLK_AUTO_BUF_REG_FALLBACK) { |
| 1706 | ublk_auto_buf_reg_fallback(ubq, req->tag); | 1706 | ublk_auto_buf_reg_fallback(ubq, req->tag); |
| @@ -1909,7 +1909,7 @@ static noinline void ublk_batch_dispatch_fail(struct ublk_queue *ubq, · 202 unmodified lines | |||
| 1909 | ublk_io_unlock(io); | 1909 | ublk_io_unlock(io); |
| 1910 | 1910 | ||
| 1911 | if (index != -1) | 1911 | if (index != -1) |
| 1912 | io_buffer_unregister_bvec(data->cmd, index, | 1912 | io_buffer_unregister(data->cmd, index, |
| 1913 | data->issue_flags); | 1913 | data->issue_flags); |
| 1914 | } | 1914 | } |
| 1915 | 1915 | ||
| @@ -3192,8 +3192,8 @@ static int ublk_register_io_buf(struct io_uring_cmd *cmd, · 1276 unmodified lines | |||
| 3192 | if (!req) | 3192 | if (!req) |
| 3193 | return -EINVAL; | 3193 | return -EINVAL; |
| 3194 | 3194 | ||
| 3195 | ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index, | 3195 | ret = io_buffer_register_request(cmd, req, ublk_io_release, index, |
| 3196 | issue_flags); | 3196 | issue_flags); |
| 3197 | if (ret) { | 3197 | if (ret) { |
| 3198 | ublk_put_req_ref(io, req); | 3198 | ublk_put_req_ref(io, req); |
| 3199 | return ret; | 3199 | return ret; |
| @@ -3224,8 +3224,8 @@ ublk_daemon_register_io_buf(struct io_uring_cmd *cmd, · 24 unmodified lines | |||
| 3224 | if (!ublk_dev_support_zero_copy(ub) || !blk_rq_has_data(req)) | 3224 | if (!ublk_dev_support_zero_copy(ub) || !blk_rq_has_data(req)) |
| 3225 | return -EINVAL; | 3225 | return -EINVAL; |
| 3226 | 3226 | ||
| 3227 | ret = io_buffer_register_bvec(cmd, req, ublk_io_release, index, | 3227 | ret = io_buffer_register_request(cmd, req, ublk_io_release, index, |
| 3228 | issue_flags); | 3228 | issue_flags); |
| 3229 | if (ret) | 3229 | if (ret) |
| 3230 | return ret; | 3230 | return ret; |
| 3231 | 3231 | ||
| @@ -3240,7 +3240,7 @@ static int ublk_unregister_io_buf(struct io_uring_cmd *cmd, · 8 unmodified lines | |||
| 3240 | if (!(ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY)) | 3240 | if (!(ub->dev_info.flags & UBLK_F_SUPPORT_ZERO_COPY)) |
| 3241 | return -EINVAL; | 3241 | return -EINVAL; |
| 3242 | 3242 | ||
| 3243 | return io_buffer_unregister_bvec(cmd, index, issue_flags); | 3243 | return io_buffer_unregister(cmd, index, issue_flags); |
| 3244 | } | 3244 | } |
| 3245 | 3245 | ||
| 3246 | static int ublk_check_fetch_buf(const struct ublk_device *ub, __u64 buf_addr) | 3246 | static int ublk_check_fetch_buf(const struct ublk_device *ub, __u64 buf_addr) |
| @@ -3384,7 +3384,7 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd, · 137 unmodified lines | |||
| 3384 | goto out; | 3384 | goto out; |
| 3385 | 3385 | ||
| 3386 | /* | 3386 | /* |
| 3387 | * io_buffer_unregister_bvec() doesn't access the ubq or io, | 3387 | * io_buffer_unregister() doesn't access the ubq or io, |
| 3388 | * so no need to validate the q_id, tag, or task | 3388 | * so no need to validate the q_id, tag, or task |
| 3389 | */ | 3389 | */ |
| 3390 | if (_IOC_NR(cmd_op) == UBLK_IO_UNREGISTER_IO_BUF) | 3390 | if (_IOC_NR(cmd_op) == UBLK_IO_UNREGISTER_IO_BUF) |
| @@ -3456,7 +3456,7 @@ static int ublk_ch_uring_cmd_local(struct io_uring_cmd *cmd, · 65 unmodified lines | |||
| 3456 | req = ublk_fill_io_cmd(io, cmd); | 3456 | req = ublk_fill_io_cmd(io, cmd); |
| 3457 | ublk_apply_io_buf(ub, io, cmd, addr, &auto_buf, &buf_idx); | 3457 | ublk_apply_io_buf(ub, io, cmd, addr, &auto_buf, &buf_idx); |
| 3458 | if (buf_idx != UBLK_INVALID_BUF_IDX) | 3458 | if (buf_idx != UBLK_INVALID_BUF_IDX) |
| 3459 | io_buffer_unregister_bvec(cmd, buf_idx, issue_flags); | 3459 | io_buffer_unregister(cmd, buf_idx, issue_flags); |
| 3460 | compl = ublk_need_complete_req(ub, io); | 3460 | compl = ublk_need_complete_req(ub, io); |
| 3461 | 3461 | ||
| 3462 | if (req_op(req) == REQ_OP_ZONE_APPEND) | 3462 | if (req_op(req) == REQ_OP_ZONE_APPEND) |
| @@ -3801,7 +3801,7 @@ static int ublk_batch_commit_io(struct ublk_queue *ubq, · 338 unmodified lines | |||
| 3801 | } | 3801 | } |
| 3802 | 3802 | ||
| 3803 | if (buf_idx != UBLK_INVALID_BUF_IDX) | 3803 | if (buf_idx != UBLK_INVALID_BUF_IDX) |
| 3804 | io_buffer_unregister_bvec(data->cmd, buf_idx, data->issue_flags); | 3804 | io_buffer_unregister(data->cmd, buf_idx, data->issue_flags); |
| 3805 | if (req_op(req) == REQ_OP_ZONE_APPEND) | 3805 | if (req_op(req) == REQ_OP_ZONE_APPEND) |
| 3806 | req->__sector = ublk_batch_zone_lba(uc, elem); | 3806 | req->__sector = ublk_batch_zone_lba(uc, elem); |
| 3807 | if (compl) | 3807 | if (compl) |
| @@ -42,6 +42,8 @@ struct fuse_args { · 41 unmodified lines | |||
| 42 | bool is_pinned:1; | 42 | bool is_pinned:1; |
| 43 | bool invalidate_vmap:1; | 43 | bool invalidate_vmap:1; |
| 44 | bool abort_on_kill:1; | 44 | bool abort_on_kill:1; |
| 45 | /* server requested io-uring zero-copy for this op */ | ||
| 46 | bool zero_copy:1; | ||
| 45 | struct fuse_in_arg in_args[4]; | 47 | struct fuse_in_arg in_args[4]; |
| 46 | struct fuse_arg out_args[2]; | 48 | struct fuse_arg out_args[2]; |
| 47 | void (*end)(struct fuse_args *args, int error); | 49 | void (*end)(struct fuse_args *args, int error); |
| @@ -530,7 +530,8 @@ static int cuse_channel_open(struct inode *inode, struct file *file) · 529 unmodified lines | |||
| 530 | 530 | ||
| 531 | INIT_LIST_HEAD(&cc->list); | 531 | INIT_LIST_HEAD(&cc->list); |
| 532 | 532 | ||
| 533 | cc->fc.chan->initialized = 1; | 533 | /* Pairs with smp_load_acquire() readers of fch->initialized */ |
| 534 | smp_store_release(&cc->fc.chan->initialized, 1); | ||
| 534 | rc = cuse_send_init(cc); | 535 | rc = cuse_send_init(cc); |
| 535 | if (rc) { | 536 | if (rc) { |
| 536 | fuse_dev_put(fud); | 537 | fuse_dev_put(fud); |
| @@ -653,6 +654,11 @@ static void __exit cuse_exit(void) · 116 unmodified lines | |||
| 653 | { | 654 | { |
| 654 | misc_deregister(&cuse_miscdev); | 655 | misc_deregister(&cuse_miscdev); |
| 655 | class_destroy(cuse_class); | 656 | class_destroy(cuse_class); |
| 657 | /* | ||
| 658 | * Wait for pending call_rcu() callbacks that call back into | ||
| 659 | * this module via fc->release (cuse_fc_release). | ||
| 660 | */ | ||
| 661 | rcu_barrier(); | ||
| 656 | } | 662 | } |
| 657 | 663 | ||
| 658 | module_init(cuse_init); | 664 | module_init(cuse_init); |
| @@ -75,17 +75,23 @@ void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *pa · 74 unmodified lines | |||
| 75 | fch->minor = param->minor; | 75 | fch->minor = param->minor; |
| 76 | fch->max_write = param->max_write; | 76 | fch->max_write = param->max_write; |
| 77 | fch->max_pages = param->max_pages; | 77 | fch->max_pages = param->max_pages; |
| 78 | |||
| 79 | if (param->io_uring_enabled) | ||
| 80 | fuse_uring_conn_init(fch); | ||
| 78 | } | 81 | } |
| 79 | 82 | ||
| 80 | /* Make sure stores before this are seen on another CPU */ | 83 | /* Pairs with smp_load_acquire() readers of fch->initialized */ |
| 81 | smp_wmb(); | 84 | smp_store_release(&fch->initialized, 1); |
| 82 | fch->initialized = 1; | ||
| 83 | wake_up_all(&fch->blocked_waitq); | 85 | wake_up_all(&fch->blocked_waitq); |
| 84 | } | 86 | } |
| 85 | 87 | ||
| 86 | static bool fuse_block_alloc(struct fuse_chan *fch, bool for_background) | 88 | static bool fuse_block_alloc(struct fuse_chan *fch, bool for_background) |
| 87 | { | 89 | { |
| 88 | return !fch->initialized || (for_background && fch->blocked) || | 90 | /* Pairs with smp_store_release() in fuse_chan_set_initialized() */ |
| 91 | if (!smp_load_acquire(&fch->initialized)) | ||
| 92 | return true; | ||
| 93 | |||
| 94 | return (for_background && fch->blocked) || | ||
| 89 | (fch->io_uring && fch->connected && !fuse_uring_ready(fch)); | 95 | (fch->io_uring && fch->connected && !fuse_uring_ready(fch)); |
| 90 | } | 96 | } |
| 91 | 97 | ||
| @@ -120,9 +126,6 @@ static struct fuse_req *fuse_get_req(struct fuse_chan *fch, bool for_background) · 28 unmodified lines | |||
| 120 | goto out; | 126 | goto out; |
| 121 | } | 127 | } |
| 122 | 128 | ||
| 123 | /* Matches smp_wmb() in fuse_chan_set_initialized() */ | ||
| 124 | smp_rmb(); | ||
| 125 | |||
| 126 | err = -ENOTCONN; | 129 | err = -ENOTCONN; |
| 127 | if (!fch->connected) | 130 | if (!fch->connected) |
| 128 | goto out; | 131 | goto out; |
| @@ -210,10 +213,13 @@ EXPORT_SYMBOL_GPL(fuse_req_hash); · 81 unmodified lines | |||
| 210 | /* | 213 | /* |
| 211 | * A new request is available, wake fiq->waitq | 214 | * A new request is available, wake fiq->waitq |
| 212 | */ | 215 | */ |
| 213 | static void fuse_dev_wake_and_unlock(struct fuse_iqueue *fiq) | 216 | static void fuse_dev_wake_and_unlock(struct fuse_iqueue *fiq, bool sync) |
| 214 | __releases(fiq->lock) | 217 | __releases(fiq->lock) |
| 215 | { | 218 | { |
| 216 | wake_up(&fiq->waitq); | 219 | if (sync) |
| 220 | wake_up_sync(&fiq->waitq); | ||
| 221 | else | ||
| 222 | wake_up(&fiq->waitq); | ||
| 217 | kill_fasync(&fiq->fasync, SIGIO, POLL_IN); | 223 | kill_fasync(&fiq->fasync, SIGIO, POLL_IN); |
| 218 | spin_unlock(&fiq->lock); | 224 | spin_unlock(&fiq->lock); |
| 219 | } | 225 | } |
| @@ -230,7 +236,7 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, · 10 unmodified lines | |||
| 230 | if (fiq->connected) { | 236 | if (fiq->connected) { |
| 231 | fiq->forget_list_tail->next = forget; | 237 | fiq->forget_list_tail->next = forget; |
| 232 | fiq->forget_list_tail = forget; | 238 | fiq->forget_list_tail = forget; |
| 233 | fuse_dev_wake_and_unlock(fiq); | 239 | fuse_dev_wake_and_unlock(fiq, false); |
| 234 | } else { | 240 | } else { |
| 235 | kfree(forget); | 241 | kfree(forget); |
| 236 | spin_unlock(&fiq->lock); | 242 | spin_unlock(&fiq->lock); |
| @@ -240,7 +246,8 @@ void fuse_dev_queue_forget(struct fuse_iqueue *fiq, · 3 unmodified lines | |||
| 240 | void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) | 246 | void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) |
| 241 | { | 247 | { |
| 242 | spin_lock(&fiq->lock); | 248 | spin_lock(&fiq->lock); |
| 243 | if (list_empty(&req->intr_entry)) { | 249 | /* Repeat FR_SENT test after obtaining the lock to prevent race with fuse_resend() */ |
| 250 | if (list_empty(&req->intr_entry) && test_bit(FR_SENT, &req->flags)) { | ||
| 244 | list_add_tail(&req->intr_entry, &fiq->interrupts); | 251 | list_add_tail(&req->intr_entry, &fiq->interrupts); |
| 245 | /* | 252 | /* |
| 246 | * Pairs with smp_mb() implied by test_and_set_bit() | 253 | * Pairs with smp_mb() implied by test_and_set_bit() |
| @@ -251,7 +258,7 @@ void fuse_dev_queue_interrupt(struct fuse_iqueue *fiq, struct fuse_req *req) · 4 unmodified lines | |||
| 251 | list_del_init(&req->intr_entry); | 258 | list_del_init(&req->intr_entry); |
| 252 | spin_unlock(&fiq->lock); | 259 | spin_unlock(&fiq->lock); |
| 253 | } else { | 260 | } else { |
| 254 | fuse_dev_wake_and_unlock(fiq); | 261 | fuse_dev_wake_and_unlock(fiq, false); |
| 255 | } | 262 | } |
| 256 | } else { | 263 | } else { |
| 257 | spin_unlock(&fiq->lock); | 264 | spin_unlock(&fiq->lock); |
| @@ -281,11 +288,13 @@ EXPORT_SYMBOL_GPL(fuse_request_assign_unique); · 23 unmodified lines | |||
| 281 | 288 | ||
| 282 | static void fuse_dev_queue_req(struct fuse_iqueue *fiq, struct fuse_req *req) | 289 | static void fuse_dev_queue_req(struct fuse_iqueue *fiq, struct fuse_req *req) |
| 283 | { | 290 | { |
| 291 | bool sync = test_and_clear_bit(FR_SYNC_WAKEUP, &req->flags); | ||
| 292 | |||
| 284 | spin_lock(&fiq->lock); | 293 | spin_lock(&fiq->lock); |
| 285 | if (fiq->connected) { | 294 | if (fiq->connected) { |
| 286 | fuse_request_assign_unique_locked(fiq, req); | 295 | fuse_request_assign_unique_locked(fiq, req); |
| 287 | list_add_tail(&req->list, &fiq->pending); | 296 | list_add_tail(&req->list, &fiq->pending); |
| 288 | fuse_dev_wake_and_unlock(fiq); | 297 | fuse_dev_wake_and_unlock(fiq, sync); |
| 289 | } else { | 298 | } else { |
| 290 | spin_unlock(&fiq->lock); | 299 | spin_unlock(&fiq->lock); |
| 291 | req->out.h.error = -ENOTCONN; | 300 | req->out.h.error = -ENOTCONN; |
| @@ -397,7 +406,8 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val) · 105 unmodified lines | |||
| 397 | fch->max_background = val; | 406 | fch->max_background = val; |
| 398 | fch->blocked = fch->num_background >= fch->max_background; | 407 | fch->blocked = fch->num_background >= fch->max_background; |
| 399 | if (!fch->blocked) | 408 | if (!fch->blocked) |
| 400 | wake_up(&fch->blocked_waitq); | 409 | wake_up_nr(&fch->blocked_waitq, |
| 410 | fch->max_background - fch->num_background); | ||
| 401 | spin_unlock(&fch->bg_lock); | 411 | spin_unlock(&fch->bg_lock); |
| 402 | } | 412 | } |
| 403 | 413 | ||
| @@ -411,11 +421,6 @@ void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc) · 7 unmodified lines | |||
| 411 | fch->conn = fc; | 421 | fch->conn = fc; |
| 412 | } | 422 | } |
| 413 | 423 | ||
| 414 | void fuse_chan_io_uring_enable(struct fuse_chan *fch) | ||
| 415 | { | ||
| 416 | fch->io_uring = 1; | ||
| 417 | } | ||
| 418 | |||
| 419 | void fuse_pqueue_init(struct fuse_pqueue *fpq) | 424 | void fuse_pqueue_init(struct fuse_pqueue *fpq) |
| 420 | { | 425 | { |
| 421 | spin_lock_init(&fpq->lock); | 426 | spin_lock_init(&fpq->lock); |
| @@ -725,7 +730,7 @@ static void request_wait_answer(struct fuse_req *req) · 303 unmodified lines | |||
| 725 | 730 | ||
| 726 | if (req->args->abort_on_kill) { | 731 | if (req->args->abort_on_kill) { |
| 727 | fuse_chan_abort(fch, false); | 732 | fuse_chan_abort(fch, false); |
| 728 | return; | 733 | goto wait_for_finish; |
| 729 | } | 734 | } |
| 730 | 735 | ||
| 731 | if (test_bit(FR_URING, &req->flags)) | 736 | if (test_bit(FR_URING, &req->flags)) |
| @@ -736,6 +741,7 @@ static void request_wait_answer(struct fuse_req *req) · 4 unmodified lines | |||
| 736 | return; | 741 | return; |
| 737 | } | 742 | } |
| 738 | 743 | ||
| 744 | wait_for_finish: | ||
| 739 | /* | 745 | /* |
| 740 | * Either request is already in userspace, or it was forced. | 746 | * Either request is already in userspace, or it was forced. |
| 741 | * Wait it out. | 747 | * Wait it out. |
| @@ -752,6 +758,11 @@ static void __fuse_request_send(struct fuse_req *req) · 10 unmodified lines | |||
| 752 | /* acquire extra reference, since request is still needed after | 758 | /* acquire extra reference, since request is still needed after |
| 753 | fuse_request_end() */ | 759 | fuse_request_end() */ |
| 754 | __fuse_get_request(req); | 760 | __fuse_get_request(req); |
| 761 | /* | ||
| 762 | * This is a synchronous request: the caller will block waiting for | ||
| 763 | * the answer. Hint the scheduler via wake_up_sync(). | ||
| 764 | */ | ||
| 765 | set_bit(FR_SYNC_WAKEUP, &req->flags); | ||
| 755 | fuse_send_one(fiq, req); | 766 | fuse_send_one(fiq, req); |
| 756 | 767 | ||
| 757 | request_wait_answer(req); | 768 | request_wait_answer(req); |
| @@ -1249,11 +1260,25 @@ int fuse_copy_folio(struct fuse_copy_state *cs, struct folio **foliop, · 491 unmodified lines | |||
| 1249 | 1260 | ||
| 1250 | if (folio) { | 1261 | if (folio) { |
| 1251 | size = folio_size(folio); | 1262 | size = folio_size(folio); |
| 1252 | if (zeroing && count < size) | 1263 | if (zeroing && count < size) { |
| 1253 | folio_zero_range(folio, 0, size); | 1264 | /* |
| 1265 | * When the copy is skipped the folio already holds the | ||
| 1266 | * payload, so only the bytes outside [offset, offset + | ||
| 1267 | * count) may be zeroed. | ||
| 1268 | * | ||
| 1269 | * Otherwise, the whole folio is cleared first so that a | ||
| 1270 | * failed copy leaves zeros rather than stale folio | ||
| 1271 | * contents. | ||
| 1272 | */ | ||
| 1273 | if (cs->skip_folio_copy) | ||
| 1274 | folio_zero_segments(folio, 0, offset, | ||
| 1275 | offset + count, size); | ||
| 1276 | else | ||
| 1277 | folio_zero_range(folio, 0, size); | ||
| 1278 | } | ||
| 1254 | } | 1279 | } |
| 1255 | 1280 | ||
| 1256 | while (count) { | 1281 | while (!cs->skip_folio_copy && count) { |
| 1257 | if (cs->write && cs->pipebufs && folio) { | 1282 | if (cs->write && cs->pipebufs && folio) { |
| 1258 | /* | 1283 | /* |
| 1259 | * Can't control lifetime of pipe buffers, so always | 1284 | * Can't control lifetime of pipe buffers, so always |
| @@ -1346,6 +1371,10 @@ int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs, · 86 unmodified lines | |||
| 1346 | for (i = 0; !err && i < numargs; i++) { | 1371 | for (i = 0; !err && i < numargs; i++) { |
| 1347 | struct fuse_arg *arg = &args[i]; | 1372 | struct fuse_arg *arg = &args[i]; |
| 1348 | if (i == numargs - 1 && argpages) | 1373 | if (i == numargs - 1 && argpages) |
| 1374 | /* | ||
| 1375 | * if cs->skip_folio_copy is set, this just does any | ||
| 1376 | * needed zeroing. No copying is involved. | ||
| 1377 | */ | ||
| 1349 | err = fuse_copy_folios(cs, arg->size, zeroing); | 1378 | err = fuse_copy_folios(cs, arg->size, zeroing); |
| 1350 | else | 1379 | else |
| 1351 | err = fuse_copy_one(cs, arg->value, arg->size); | 1380 | err = fuse_copy_one(cs, arg->value, arg->size); |
| @@ -1760,7 +1789,7 @@ out: · 408 unmodified lines | |||
| 1760 | void fuse_chan_resend(struct fuse_chan *fch) | 1789 | void fuse_chan_resend(struct fuse_chan *fch) |
| 1761 | { | 1790 | { |
| 1762 | struct fuse_dev *fud; | 1791 | struct fuse_dev *fud; |
| 1763 | struct fuse_req *req, *next; | 1792 | struct fuse_req *req; |
| 1764 | struct fuse_iqueue *fiq = &fch->iq; | 1793 | struct fuse_iqueue *fiq = &fch->iq; |
| 1765 | LIST_HEAD(to_queue); | 1794 | LIST_HEAD(to_queue); |
| 1766 | unsigned int i; | 1795 | unsigned int i; |
| @@ -1775,24 +1804,20 @@ void fuse_chan_resend(struct fuse_chan *fch) · 8 unmodified lines | |||
| 1775 | struct fuse_pqueue *fpq = &fud->pq; | 1804 | struct fuse_pqueue *fpq = &fud->pq; |
| 1776 | 1805 | ||
| 1777 | spin_lock(&fpq->lock); | 1806 | spin_lock(&fpq->lock); |
| 1778 | for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) | 1807 | for (i = 0; i < FUSE_PQ_HASH_SIZE; i++) { |
| 1779 | list_splice_tail_init(&fpq->processing[i], &to_queue); | 1808 | struct list_head *this_queue = &fpq->processing[i]; |
| 1809 | |||
| 1810 | list_for_each_entry(req, this_queue, list) | ||
| 1811 | clear_bit(FR_SENT, &req->flags); | ||
| 1812 | list_splice_tail_init(this_queue, &to_queue); | ||
| 1813 | } | ||
| 1780 | spin_unlock(&fpq->lock); | 1814 | spin_unlock(&fpq->lock); |
| 1781 | } | 1815 | } |
| 1782 | spin_unlock(&fch->lock); | 1816 | spin_unlock(&fch->lock); |
| 1783 | 1817 | ||
| 1784 | list_for_each_entry_safe(req, next, &to_queue, list) { | ||
| 1785 | set_bit(FR_PENDING, &req->flags); | ||
| 1786 | clear_bit(FR_SENT, &req->flags); | ||
| 1787 | /* mark the request as resend request */ | ||
| 1788 | req->in.h.unique |= FUSE_UNIQUE_RESEND; | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | spin_lock(&fiq->lock); | 1818 | spin_lock(&fiq->lock); |
| 1792 | if (!fiq->connected) { | 1819 | if (!fiq->connected) { |
| 1793 | spin_unlock(&fiq->lock); | 1820 | spin_unlock(&fiq->lock); |
| 1794 | list_for_each_entry(req, &to_queue, list) | ||
| 1795 | clear_bit(FR_PENDING, &req->flags); | ||
| 1796 | fuse_dev_end_requests(&to_queue); | 1821 | fuse_dev_end_requests(&to_queue); |
| 1797 | return; | 1822 | return; |
| 1798 | } | 1823 | } |
| @@ -1801,12 +1826,16 @@ void fuse_chan_resend(struct fuse_chan *fch) · 2 unmodified lines | |||
| 1801 | * intr_entry on fiq->interrupts after the request is re-queued. | 1826 | * intr_entry on fiq->interrupts after the request is re-queued. |
| 1802 | */ | 1827 | */ |
| 1803 | list_for_each_entry(req, &to_queue, list) { | 1828 | list_for_each_entry(req, &to_queue, list) { |
| 1829 | set_bit(FR_PENDING, &req->flags); | ||
| 1830 | /* mark the request as resend request */ | ||
| 1831 | req->in.h.unique |= FUSE_UNIQUE_RESEND; | ||
| 1832 | |||
| 1804 | if (test_bit(FR_INTERRUPTED, &req->flags)) | 1833 | if (test_bit(FR_INTERRUPTED, &req->flags)) |
| 1805 | list_del_init(&req->intr_entry); | 1834 | list_del_init(&req->intr_entry); |
| 1806 | } | 1835 | } |
| 1807 | /* iq and pq requests are both oldest to newest */ | 1836 | /* iq and pq requests are both oldest to newest */ |
| 1808 | list_splice(&to_queue, &fiq->pending); | 1837 | list_splice(&to_queue, &fiq->pending); |
| 1809 | fuse_dev_wake_and_unlock(fiq); | 1838 | fuse_dev_wake_and_unlock(fiq, false); |
| 1810 | } | 1839 | } |
| 1811 | 1840 | ||
| 1812 | /* Look up request on processing list by unique ID */ | 1841 | /* Look up request on processing list by unique ID */ |
| @@ -1888,7 +1917,8 @@ static ssize_t fuse_dev_do_write(struct fuse_dev *fud, · 75 unmodified lines | |||
| 1888 | * initialized and connected state | 1917 | * initialized and connected state |
| 1889 | */ | 1918 | */ |
| 1890 | err = -EINVAL; | 1919 | err = -EINVAL; |
| 1891 | if (!fch->initialized || !fch->connected) | 1920 | /* Pairs with smp_store_release() in fuse_chan_set_initialized() */ |
| 1921 | if (!smp_load_acquire(&fch->initialized) || !fch->connected) | ||
| 1892 | goto copy_finish; | 1922 | goto copy_finish; |
| 1893 | 1923 | ||
| 1894 | /* Don't try to move folios (yet) */ | 1924 | /* Don't try to move folios (yet) */ |
| @@ -22,6 +22,7 @@ struct fuse_chan_param { · 21 unmodified lines | |||
| 22 | unsigned int minor; | 22 | unsigned int minor; |
| 23 | unsigned int max_write; | 23 | unsigned int max_write; |
| 24 | unsigned int max_pages; | 24 | unsigned int max_pages; |
| 25 | bool io_uring_enabled; | ||
| 25 | }; | 26 | }; |
| 26 | 27 | ||
| 27 | struct fuse_chan *fuse_chan_new(void); | 28 | struct fuse_chan *fuse_chan_new(void); |
| @@ -34,7 +35,6 @@ void fuse_chan_max_background_set(struct fuse_chan *fch, unsigned int val); · 6 unmodified lines | |||
| 34 | unsigned int fuse_chan_num_waiting(struct fuse_chan *fch); | 35 | unsigned int fuse_chan_num_waiting(struct fuse_chan *fch); |
| 35 | void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc); | 36 | void fuse_chan_set_fc(struct fuse_chan *fch, struct fuse_conn *fc); |
| 36 | void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *param); | 37 | void fuse_chan_set_initialized(struct fuse_chan *fch, struct fuse_chan_param *param); |
| 37 | void fuse_chan_io_uring_enable(struct fuse_chan *fch); | ||
| 38 | ssize_t fuse_chan_send(struct fuse_chan *fch, struct fuse_args *args); | 38 | ssize_t fuse_chan_send(struct fuse_chan *fch, struct fuse_args *args); |
| 39 | int fuse_chan_send_bg(struct fuse_chan *fch, struct fuse_args *args, gfp_t gfp_flags); | 39 | int fuse_chan_send_bg(struct fuse_chan *fch, struct fuse_args *args, gfp_t gfp_flags); |
| 40 | int fuse_chan_send_notify_reply(struct fuse_chan *fch, struct fuse_args *args, u64 unique); | 40 | int fuse_chan_send_notify_reply(struct fuse_chan *fch, struct fuse_args *args, u64 unique); |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | |||
| 9 | #include "dev_uring_i.h" | 9 | #include "dev_uring_i.h" |
| 10 | #include "fuse_trace.h" | 10 | #include "fuse_trace.h" |
| 11 | 11 | ||
| 12 | #include <linux/bitmap.h> | ||
| 12 | #include <linux/fs.h> | 13 | #include <linux/fs.h> |
| 13 | #include <linux/io_uring/cmd.h> | 14 | #include <linux/io_uring/cmd.h> |
| 14 | 15 | ||
| @@ -21,6 +22,8 @@ MODULE_PARM_DESC(enable_uring, · 6 unmodified lines | |||
| 21 | #define FUSE_URING_IOV_HEADERS 0 | 22 | #define FUSE_URING_IOV_HEADERS 0 |
| 22 | #define FUSE_URING_IOV_PAYLOAD 1 | 23 | #define FUSE_URING_IOV_PAYLOAD 1 |
| 23 | 24 | ||
| 25 | #define FUSE_URING_ADD_QUEUE_FLAGS (FUSE_URING_ZERO_COPY) | ||
| 26 | |||
| 24 | bool fuse_uring_enabled(void) | 27 | bool fuse_uring_enabled(void) |
| 25 | { | 28 | { |
| 26 | return enable_uring; | 29 | return enable_uring; |
| @@ -30,6 +33,11 @@ struct fuse_uring_pdu { · 3 unmodified lines | |||
| 30 | struct fuse_ring_ent *ent; | 33 | struct fuse_ring_ent *ent; |
| 31 | }; | 34 | }; |
| 32 | 35 | ||
| 36 | struct fuse_zero_copy_bvs { | ||
| 37 | unsigned int nr_bvs; | ||
| 38 | struct bio_vec bvs[]; | ||
| 39 | }; | ||
| 40 | |||
| 33 | static const struct fuse_iqueue_ops fuse_io_uring_ops; | 41 | static const struct fuse_iqueue_ops fuse_io_uring_ops; |
| 34 | 42 | ||
| 35 | enum fuse_uring_header_type { | 43 | enum fuse_uring_header_type { |
| @@ -41,6 +49,32 @@ enum fuse_uring_header_type { · 5 unmodified lines | |||
| 41 | FUSE_URING_HEADER_RING_ENT, | 49 | FUSE_URING_HEADER_RING_ENT, |
| 42 | }; | 50 | }; |
| 43 | 51 | ||
| 52 | static inline bool bufpool_enabled(struct fuse_ring_queue *queue) | ||
| 53 | { | ||
| 54 | return queue->payload_mode == FUSE_PAYLOAD_BUFPOOL; | ||
| 55 | } | ||
| 56 | |||
| 57 | static inline bool bufpool_registered(struct fuse_ring_queue *queue) | ||
| 58 | { | ||
| 59 | return queue->bufpool && queue->bufpool->registered; | ||
| 60 | } | ||
| 61 | |||
| 62 | /* | ||
| 63 | * For a registered bufpool, every sqe that drives a payload import (REGISTER, | ||
| 64 | * COMMIT_AND_FETCH) must carry the registered buffer index of the pool. | ||
| 65 | * This also must be called from the command's issue handler, where cmd->sqe is | ||
| 66 | * still valid | ||
| 67 | */ | ||
| 68 | static inline bool fuse_uring_cmd_index_ok(struct io_uring_cmd *cmd, | ||
| 69 | struct fuse_ring_queue *queue) | ||
| 70 | { | ||
| 71 | if (!bufpool_registered(queue)) | ||
| 72 | return true; | ||
| 73 | |||
| 74 | return (cmd->flags & IORING_URING_CMD_FIXED) && | ||
| 75 | READ_ONCE(cmd->sqe->buf_index) == queue->bufpool->registered_index; | ||
| 76 | } | ||
| 77 | |||
| 44 | static void uring_cmd_set_ring_ent(struct io_uring_cmd *cmd, | 78 | static void uring_cmd_set_ring_ent(struct io_uring_cmd *cmd, |
| 45 | struct fuse_ring_ent *ring_ent) | 79 | struct fuse_ring_ent *ring_ent) |
| 46 | { | 80 | { |
| @@ -86,8 +120,36 @@ static void fuse_uring_flush_bg(struct fuse_ring_queue *queue) · 39 unmodified lines | |||
| 86 | } | 120 | } |
| 87 | } | 121 | } |
| 88 | 122 | ||
| 123 | static bool can_zero_copy_req(struct fuse_ring_ent *ent, struct fuse_req *req) | ||
| 124 | { | ||
| 125 | struct fuse_args *args = req->args; | ||
| 126 | |||
| 127 | if (!ent->queue->zero_copy || !args->zero_copy) | ||
| 128 | return false; | ||
| 129 | |||
| 130 | if (args->opcode != FUSE_READ && args->opcode != FUSE_WRITE) | ||
| 131 | return false; | ||
| 132 | |||
| 133 | return args->in_pages || args->out_pages; | ||
| 134 | } | ||
| 135 | |||
| 136 | static void zero_copy_unregister(struct io_uring_cmd *cmd, | ||
| 137 | struct fuse_ring_ent *ent, | ||
| 138 | unsigned int issue_flags) | ||
| 139 | { | ||
| 140 | if (ent->zero_copied) { | ||
| 141 | int err = io_buffer_unregister(cmd, ent->zero_copy_index, | ||
| 142 | issue_flags); | ||
| 143 | |||
| 144 | if (err) | ||
| 145 | pr_warn_ratelimited("qid=%d zero-copy unregister failed: %d\n", | ||
| 146 | ent->queue->qid, err); | ||
| 147 | ent->zero_copied = false; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 89 | static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, | 151 | static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, |
| 90 | int error) | 152 | int error, unsigned int issue_flags) |
| 91 | { | 153 | { |
| 92 | struct fuse_ring_queue *queue = ent->queue; | 154 | struct fuse_ring_queue *queue = ent->queue; |
| 93 | struct fuse_ring *ring = queue->ring; | 155 | struct fuse_ring *ring = queue->ring; |
| @@ -107,6 +169,8 @@ static void fuse_uring_req_end(struct fuse_ring_ent *ent, struct fuse_req *req, · 13 unmodified lines | |||
| 107 | 169 | ||
| 108 | spin_unlock(&queue->lock); | 170 | spin_unlock(&queue->lock); |
| 109 | 171 | ||
| 172 | zero_copy_unregister(ent->cmd, ent, issue_flags); | ||
| 173 | |||
| 110 | if (error) | 174 | if (error) |
| 111 | req->out.h.error = error; | 175 | req->out.h.error = error; |
| 112 | 176 | ||
| @@ -204,7 +268,7 @@ void fuse_uring_destruct(struct fuse_chan *fch) · 91 unmodified lines | |||
| 204 | return; | 268 | return; |
| 205 | 269 | ||
| 206 | for (qid = 0; qid < ring->nr_queues; qid++) { | 270 | for (qid = 0; qid < ring->nr_queues; qid++) { |
| 207 | struct fuse_ring_queue *queue = ring->queues[qid]; | 271 | struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]); |
| 208 | struct fuse_ring_ent *ent, *next; | 272 | struct fuse_ring_ent *ent, *next; |
| 209 | 273 | ||
| 210 | if (!queue) | 274 | if (!queue) |
| @@ -222,8 +286,9 @@ void fuse_uring_destruct(struct fuse_chan *fch) · 11 unmodified lines | |||
| 222 | } | 286 | } |
| 223 | 287 | ||
| 224 | kfree(queue->fpq.processing); | 288 | kfree(queue->fpq.processing); |
| 289 | kfree(queue->bufpool); | ||
| 225 | kfree(queue); | 290 | kfree(queue); |
| 226 | ring->queues[qid] = NULL; | 291 | WRITE_ONCE(ring->queues[qid], NULL); |
| 227 | } | 292 | } |
| 228 | 293 | ||
| 229 | kfree(ring->queues); | 294 | kfree(ring->queues); |
| @@ -238,7 +303,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch) · 8 unmodified lines | |||
| 238 | { | 303 | { |
| 239 | struct fuse_ring *ring; | 304 | struct fuse_ring *ring; |
| 240 | size_t nr_queues = num_possible_cpus(); | 305 | size_t nr_queues = num_possible_cpus(); |
| 241 | struct fuse_ring *res = NULL; | ||
| 242 | size_t max_payload_size; | 306 | size_t max_payload_size; |
| 243 | 307 | ||
| 244 | ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT); | 308 | ring = kzalloc_obj(*ring, GFP_KERNEL_ACCOUNT); |
| @@ -258,12 +322,6 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch) · 13 unmodified lines | |||
| 258 | spin_unlock(&fch->lock); | 322 | spin_unlock(&fch->lock); |
| 259 | goto out_err; | 323 | goto out_err; |
| 260 | } | 324 | } |
| 261 | if (fch->ring) { | ||
| 262 | /* race, another thread created the ring in the meantime */ | ||
| 263 | spin_unlock(&fch->lock); | ||
| 264 | res = fch->ring; | ||
| 265 | goto out_err; | ||
| 266 | } | ||
| 267 | 325 | ||
| 268 | init_waitqueue_head(&ring->stop_waitq); | 326 | init_waitqueue_head(&ring->stop_waitq); |
| 269 | 327 | ||
| @@ -278,11 +336,18 @@ static struct fuse_ring *fuse_uring_create(struct fuse_chan *fch) · 8 unmodified lines | |||
| 278 | out_err: | 336 | out_err: |
| 279 | kfree(ring->queues); | 337 | kfree(ring->queues); |
| 280 | kfree(ring); | 338 | kfree(ring); |
| 281 | return res; | 339 | return NULL; |
| 340 | } | ||
| 341 | |||
| 342 | void fuse_uring_conn_init(struct fuse_chan *fch) | ||
| 343 | { | ||
| 344 | if (fuse_uring_create(fch)) | ||
| 345 | fch->io_uring = 1; | ||
| 282 | } | 346 | } |
| 283 | 347 | ||
| 284 | static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, | 348 | static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, |
| 285 | int qid) | 349 | int qid, bool zero_copy, |
| 350 | bool fail_if_exists) | ||
| 286 | { | 351 | { |
| 287 | struct fuse_chan *fch = ring->chan; | 352 | struct fuse_chan *fch = ring->chan; |
| 288 | struct fuse_ring_queue *queue; | 353 | struct fuse_ring_queue *queue; |
| @@ -290,16 +355,17 @@ static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, · 1 unmodified line | |||
| 290 | 355 | ||
| 291 | queue = kzalloc_obj(*queue, GFP_KERNEL_ACCOUNT); | 356 | queue = kzalloc_obj(*queue, GFP_KERNEL_ACCOUNT); |
| 292 | if (!queue) | 357 | if (!queue) |
| 293 | return NULL; | 358 | return ERR_PTR(-ENOMEM); |
| 294 | pq = fuse_pqueue_alloc(); | 359 | pq = fuse_pqueue_alloc(); |
| 295 | if (!pq) { | 360 | if (!pq) { |
| 296 | kfree(queue); | 361 | kfree(queue); |
| 297 | return NULL; | 362 | return ERR_PTR(-ENOMEM); |
| 298 | } | 363 | } |
| 299 | 364 | ||
| 300 | queue->qid = qid; | 365 | queue->qid = qid; |
| 301 | queue->ring = ring; | 366 | queue->ring = ring; |
| 302 | spin_lock_init(&queue->lock); | 367 | spin_lock_init(&queue->lock); |
| 368 | queue->zero_copy = zero_copy; | ||
| 303 | 369 | ||
| 304 | INIT_LIST_HEAD(&queue->ent_avail_queue); | 370 | INIT_LIST_HEAD(&queue->ent_avail_queue); |
| 305 | INIT_LIST_HEAD(&queue->ent_commit_queue); | 371 | INIT_LIST_HEAD(&queue->ent_commit_queue); |
| @@ -316,14 +382,17 @@ static struct fuse_ring_queue *fuse_uring_create_queue(struct fuse_ring *ring, · 10 unmodified lines | |||
| 316 | if (ring->queues[qid]) { | 382 | if (ring->queues[qid]) { |
| 317 | spin_unlock(&fch->lock); | 383 | spin_unlock(&fch->lock); |
| 318 | kfree(queue->fpq.processing); | 384 | kfree(queue->fpq.processing); |
| 385 | kfree(queue->bufpool); | ||
| 319 | kfree(queue); | 386 | kfree(queue); |
| 320 | return ring->queues[qid]; | 387 | return fail_if_exists ? ERR_PTR(-EEXIST) : ring->queues[qid]; |
| 321 | } | 388 | } |
| 322 | 389 | ||
| 323 | /* | 390 | /* |
| 324 | * write_once and lock as the caller mostly doesn't take the lock at all | 391 | * fch->lock serializes concurrent creators for this qid. |
| 392 | * smp_store_release() are for the lockless readers who must see a | ||
| 393 | * fully initialized queue after &ring->queues[qid] is set | ||
| 325 | */ | 394 | */ |
| 326 | WRITE_ONCE(ring->queues[qid], queue); | 395 | smp_store_release(&ring->queues[qid], queue); |
| 327 | spin_unlock(&fch->lock); | 396 | spin_unlock(&fch->lock); |
| 328 | 397 | ||
| 329 | return queue; | 398 | return queue; |
| @@ -434,7 +503,7 @@ static void fuse_uring_log_ent_state(struct fuse_ring *ring) · 104 unmodified lines | |||
| 434 | struct fuse_ring_ent *ent; | 503 | struct fuse_ring_ent *ent; |
| 435 | 504 | ||
| 436 | for (qid = 0; qid < ring->nr_queues; qid++) { | 505 | for (qid = 0; qid < ring->nr_queues; qid++) { |
| 437 | struct fuse_ring_queue *queue = ring->queues[qid]; | 506 | struct fuse_ring_queue *queue = READ_ONCE(ring->queues[qid]); |
| 438 | 507 | ||
| 439 | if (!queue) | 508 | if (!queue) |
| 440 | continue; | 509 | continue; |
| @@ -643,30 +712,57 @@ static int copy_header_from_ring(struct fuse_ring_ent *ent, · 202 unmodified lines | |||
| 643 | return 0; | 712 | return 0; |
| 644 | } | 713 | } |
| 645 | 714 | ||
| 715 | static int fuse_uring_import_payload(struct fuse_ring_ent *ent, int dir, | ||
| 716 | struct iov_iter *iter, | ||
| 717 | unsigned int issue_flags) | ||
| 718 | { | ||
| 719 | void __user *base = ent->payload.iov_base; | ||
| 720 | size_t len = ent->payload.iov_len; | ||
| 721 | int err = 0; | ||
| 722 | |||
| 723 | if (!base) { | ||
| 724 | memset(iter, 0, sizeof(*iter)); | ||
| 725 | return 0; | ||
| 726 | } | ||
| 727 | |||
| 728 | if (bufpool_registered(ent->queue)) | ||
| 729 | err = io_uring_cmd_import_fixed((u64)(uintptr_t)base, len, dir, | ||
| 730 | iter, ent->cmd, issue_flags); | ||
| 731 | else | ||
| 732 | err = import_ubuf(dir, base, len, iter); | ||
| 733 | |||
| 734 | if (err) | ||
| 735 | pr_info_ratelimited("fuse: Import of user buffer failed\n"); | ||
| 736 | |||
| 737 | return err; | ||
| 738 | } | ||
| 739 | |||
| 646 | static int setup_fuse_copy_state(struct fuse_copy_state *cs, | 740 | static int setup_fuse_copy_state(struct fuse_copy_state *cs, |
| 647 | struct fuse_ring *ring, struct fuse_req *req, | 741 | struct fuse_req *req, |
| 648 | struct fuse_ring_ent *ent, int dir, | 742 | struct fuse_ring_ent *ent, int dir, |
| 649 | struct iov_iter *iter) | 743 | struct iov_iter *iter, |
| 744 | unsigned int issue_flags) | ||
| 650 | { | 745 | { |
| 651 | int err; | 746 | int err; |
| 652 | 747 | ||
| 653 | err = import_ubuf(dir, ent->payload, ring->max_payload_sz, iter); | 748 | err = fuse_uring_import_payload(ent, dir, iter, issue_flags); |
| 654 | if (err) { | 749 | if (err) |
| 655 | pr_info_ratelimited("fuse: Import of user buffer failed\n"); | ||
| 656 | return err; | 750 | return err; |
| 657 | } | ||
| 658 | 751 | ||
| 659 | fuse_copy_init(cs, dir == ITER_DEST, iter); | 752 | fuse_copy_init(cs, dir == ITER_DEST, iter); |
| 660 | 753 | ||
| 754 | if (ent->zero_copied) | ||
| 755 | cs->skip_folio_copy = true; | ||
| 756 | |||
| 661 | cs->is_uring = true; | 757 | cs->is_uring = true; |
| 662 | cs->req = req; | 758 | cs->req = req; |
| 663 | 759 | ||
| 664 | return 0; | 760 | return 0; |
| 665 | } | 761 | } |
| 666 | 762 | ||
| 667 | static int fuse_uring_copy_from_ring(struct fuse_ring *ring, | 763 | static int fuse_uring_copy_from_ring(struct fuse_req *req, |
| 668 | struct fuse_req *req, | 764 | struct fuse_ring_ent *ent, |
| 669 | struct fuse_ring_ent *ent) | 765 | unsigned int issue_flags) |
| 670 | { | 766 | { |
| 671 | struct fuse_copy_state cs; | 767 | struct fuse_copy_state cs; |
| 672 | struct fuse_args *args = req->args; | 768 | struct fuse_args *args = req->args; |
| @@ -679,7 +775,8 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, · 6 unmodified lines | |||
| 679 | if (err) | 775 | if (err) |
| 680 | return err; | 776 | return err; |
| 681 | 777 | ||
| 682 | err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_SOURCE, &iter); | 778 | err = setup_fuse_copy_state(&cs, req, ent, ITER_SOURCE, &iter, |
| 779 | issue_flags); | ||
| 683 | if (err) | 780 | if (err) |
| 684 | return err; | 781 | return err; |
| 685 | 782 | ||
| @@ -688,11 +785,68 @@ static int fuse_uring_copy_from_ring(struct fuse_ring *ring, · 2 unmodified lines | |||
| 688 | return err; | 785 | return err; |
| 689 | } | 786 | } |
| 690 | 787 | ||
| 788 | static void fuse_zero_copy_release(void *priv) | ||
| 789 | { | ||
| 790 | struct fuse_zero_copy_bvs *zc_bvs = priv; | ||
| 791 | unsigned int i; | ||
| 792 | |||
| 793 | for (i = 0; i < zc_bvs->nr_bvs; i++) | ||
| 794 | folio_put(page_folio(zc_bvs->bvs[i].bv_page)); | ||
| 795 | |||
| 796 | kvfree(zc_bvs); | ||
| 797 | } | ||
| 798 | |||
| 799 | static int fuse_uring_set_up_zero_copy(struct fuse_ring_ent *ent, | ||
| 800 | struct fuse_req *req, | ||
| 801 | unsigned int issue_flags) | ||
| 802 | { | ||
| 803 | struct fuse_args_pages *ap; | ||
| 804 | int err, i, ddir = 0; | ||
| 805 | struct fuse_zero_copy_bvs *zc_bvs; | ||
| 806 | struct bio_vec *bvs; | ||
| 807 | |||
| 808 | /* out_pages indicates a read, in_pages indicates a write */ | ||
| 809 | if (req->args->out_pages) | ||
| 810 | ddir |= IO_BUF_DEST; | ||
| 811 | if (req->args->in_pages) | ||
| 812 | ddir |= IO_BUF_SOURCE; | ||
| 813 | |||
| 814 | ap = container_of(req->args, typeof(*ap), args); | ||
| 815 | |||
| 816 | zc_bvs = kvmalloc_flex(*zc_bvs, bvs, ap->num_folios, | ||
| 817 | GFP_KERNEL_ACCOUNT); | ||
| 818 | if (!zc_bvs) | ||
| 819 | return -ENOMEM; | ||
| 820 | |||
| 821 | zc_bvs->nr_bvs = ap->num_folios; | ||
| 822 | bvs = zc_bvs->bvs; | ||
| 823 | for (i = 0; i < ap->num_folios; i++) { | ||
| 824 | bvs[i].bv_page = folio_page(ap->folios[i], 0); | ||
| 825 | bvs[i].bv_offset = ap->descs[i].offset; | ||
| 826 | bvs[i].bv_len = ap->descs[i].length; | ||
| 827 | folio_get(ap->folios[i]); | ||
| 828 | } | ||
| 829 | |||
| 830 | err = io_buffer_register_bvec(ent->cmd, bvs, ap->num_folios, | ||
| 831 | fuse_zero_copy_release, zc_bvs, | ||
| 832 | ddir, ent->zero_copy_index, | ||
| 833 | issue_flags); | ||
| 834 | if (err) { | ||
| 835 | fuse_zero_copy_release(zc_bvs); | ||
| 836 | return err; | ||
| 837 | } | ||
| 838 | |||
| 839 | ent->zero_copied = true; | ||
| 840 | |||
| 841 | return 0; | ||
| 842 | } | ||
| 843 | |||
| 691 | /* | 844 | /* |
| 692 | * Copy data from the req to the ring buffer | 845 | * Copy data from the req to the ring buffer |
| 693 | */ | 846 | */ |
| 694 | static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, | 847 | static int fuse_uring_args_to_ring(struct fuse_req *req, |
| 695 | struct fuse_ring_ent *ent) | 848 | struct fuse_ring_ent *ent, |
| 849 | unsigned int issue_flags) | ||
| 696 | { | 850 | { |
| 697 | struct fuse_copy_state cs; | 851 | struct fuse_copy_state cs; |
| 698 | struct fuse_args *args = req->args; | 852 | struct fuse_args *args = req->args; |
| @@ -705,7 +859,15 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, · 6 unmodified lines | |||
| 705 | .commit_id = req->in.h.unique, | 859 | .commit_id = req->in.h.unique, |
| 706 | }; | 860 | }; |
| 707 | 861 | ||
| 708 | err = setup_fuse_copy_state(&cs, ring, req, ent, ITER_DEST, &iter); | 862 | if (can_zero_copy_req(ent, req)) { |
| 863 | ent_in_out.flags |= FUSE_URING_ENT_ZERO_COPY; | ||
| 864 | err = fuse_uring_set_up_zero_copy(ent, req, issue_flags); | ||
| 865 | if (err) | ||
| 866 | return err; | ||
| 867 | } | ||
| 868 | |||
| 869 | err = setup_fuse_copy_state(&cs, req, ent, ITER_DEST, &iter, | ||
| 870 | issue_flags); | ||
| 709 | if (err) | 871 | if (err) |
| 710 | return err; | 872 | return err; |
| 711 | 873 | ||
| @@ -735,15 +897,32 @@ static int fuse_uring_args_to_ring(struct fuse_ring *ring, struct fuse_req *req, · 23 unmodified lines | |||
| 735 | } | 897 | } |
| 736 | 898 | ||
| 737 | ent_in_out.payload_sz = cs.ring.copied_sz; | 899 | ent_in_out.payload_sz = cs.ring.copied_sz; |
| 900 | /* | ||
| 901 | * on a zero-copied write the pages are registered for the server to | ||
| 902 | * read via a fixed-buffer op rather than copied into the payload | ||
| 903 | * buffer, so copied_sz does not account for it. The server still needs | ||
| 904 | * the total inbound size to know how many bytes to read from the | ||
| 905 | * registered buffer, so add the page arg (always the last in-arg) back | ||
| 906 | * in | ||
| 907 | */ | ||
| 908 | if (cs.skip_folio_copy && args->in_pages) | ||
| 909 | ent_in_out.payload_sz += | ||
| 910 | args->in_args[args->in_numargs - 1].size; | ||
| 911 | |||
| 912 | if (bufpool_enabled(ent->queue) && ent->payload.iov_base) | ||
| 913 | ent_in_out.offset = | ||
| 914 | (uintptr_t)ent->payload.iov_base - ent->queue->bufpool->base_uaddr; | ||
| 915 | |||
| 738 | return copy_header_to_ring(ent, FUSE_URING_HEADER_RING_ENT, | 916 | return copy_header_to_ring(ent, FUSE_URING_HEADER_RING_ENT, |
| 739 | &ent_in_out, sizeof(ent_in_out)); | 917 | &ent_in_out, sizeof(ent_in_out)); |
| 740 | } | 918 | } |
| 741 | 919 | ||
| 742 | static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, | 920 | static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, |
| 743 | struct fuse_req *req) | 921 | struct fuse_req *req, |
| 922 | unsigned int issue_flags) | ||
| 744 | { | 923 | { |
| 745 | struct fuse_ring_queue *queue = ent->queue; | 924 | struct fuse_ring_queue *queue = ent->queue; |
| 746 | struct fuse_ring *ring = queue->ring; | 925 | struct fuse_in_header in_header; |
| 747 | int err; | 926 | int err; |
| 748 | 927 | ||
| 749 | err = -EIO; | 928 | err = -EIO; |
| @@ -758,23 +937,124 @@ static int fuse_uring_copy_to_ring(struct fuse_ring_ent *ent, · 8 unmodified lines | |||
| 758 | return err; | 937 | return err; |
| 759 | 938 | ||
| 760 | /* copy the request */ | 939 | /* copy the request */ |
| 761 | err = fuse_uring_args_to_ring(ring, req, ent); | 940 | err = fuse_uring_args_to_ring(req, ent, issue_flags); |
| 762 | if (unlikely(err)) { | 941 | if (unlikely(err)) { |
| 763 | pr_info_ratelimited("Copy to ring failed: %d\n", err); | 942 | pr_info_ratelimited("Copy to ring failed: %d\n", err); |
| 764 | return err; | 943 | return err; |
| 765 | } | 944 | } |
| 766 | 945 | ||
| 767 | /* copy fuse_in_header */ | 946 | /* copy fuse_in_header */ |
| 768 | return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->in.h, | 947 | in_header = req->in.h; |
| 769 | sizeof(req->in.h)); | 948 | return copy_header_to_ring(ent, FUSE_URING_HEADER_IN_OUT, &in_header, |
| 949 | sizeof(in_header)); | ||
| 950 | } | ||
| 951 | |||
| 952 | static bool fuse_uring_req_has_copyable_payload(struct fuse_ring_ent *ent, | ||
| 953 | struct fuse_req *req) | ||
| 954 | { | ||
| 955 | struct fuse_args *args = req->args; | ||
| 956 | |||
| 957 | if (!can_zero_copy_req(ent, req)) | ||
| 958 | return args->in_numargs > 1 || args->out_numargs; | ||
| 959 | |||
| 960 | /* | ||
| 961 | * the asymmetry between in_numargs > 2 and out_numargs > 1 is because | ||
| 962 | * the per-op header is extracted before fuse_copy_args() for inargs but | ||
| 963 | * not for outargs | ||
| 964 | */ | ||
| 965 | if ((args->in_numargs > 1) && (!args->in_pages || args->in_numargs > 2)) | ||
| 966 | return true; | ||
| 967 | if (args->out_numargs && (!args->out_pages || args->out_numargs > 1)) | ||
| 968 | return true; | ||
| 969 | |||
| 970 | return false; | ||
| 971 | } | ||
| 972 | |||
| 973 | static int fuse_uring_select_buffer(struct fuse_ring_ent *ent) | ||
| 974 | { | ||
| 975 | struct fuse_ring_queue *queue = ent->queue; | ||
| 976 | struct fuse_bufpool *pool = queue->bufpool; | ||
| 977 | unsigned int id; | ||
| 978 | |||
| 979 | lockdep_assert_held(&queue->lock); | ||
| 980 | |||
| 981 | id = find_first_bit(pool->free_map, pool->nr_bufs); | ||
| 982 | if (id >= pool->nr_bufs) | ||
| 983 | return -ENOBUFS; | ||
| 984 | |||
| 985 | WARN_ON_ONCE(ent->payload.iov_base); | ||
| 986 | __clear_bit(id, pool->free_map); | ||
| 987 | |||
| 988 | ent->buf_id = id; | ||
| 989 | ent->payload.iov_base = | ||
| 990 | (void __user *)(pool->base_uaddr + id * pool->buf_size); | ||
| 991 | ent->payload.iov_len = pool->buf_size; | ||
| 992 | |||
| 993 | return 0; | ||
| 994 | } | ||
| 995 | |||
| 996 | static void fuse_uring_recycle_buffer(struct fuse_ring_ent *ent) | ||
| 997 | { | ||
| 998 | struct iovec *ent_payload = &ent->payload; | ||
| 999 | struct fuse_ring_queue *queue = ent->queue; | ||
| 1000 | struct fuse_bufpool *pool; | ||
| 1001 | |||
| 1002 | lockdep_assert_held(&queue->lock); | ||
| 1003 | |||
| 1004 | if (!bufpool_enabled(queue) || !ent_payload->iov_base) | ||
| 1005 | return; | ||
| 1006 | |||
| 1007 | pool = queue->bufpool; | ||
| 1008 | |||
| 1009 | /* a buffer should never be recycled twice */ | ||
| 1010 | WARN_ON_ONCE(test_bit(ent->buf_id, pool->free_map)); | ||
| 1011 | __set_bit(ent->buf_id, pool->free_map); | ||
| 1012 | |||
| 1013 | memset(ent_payload, 0, sizeof(*ent_payload)); | ||
| 1014 | ent->buf_id = 0; | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | static int fuse_uring_next_req_update_buffer(struct fuse_ring_ent *ent, | ||
| 1018 | struct fuse_req *req) | ||
| 1019 | { | ||
| 1020 | bool buffer_selected; | ||
| 1021 | bool has_payload; | ||
| 1022 | |||
| 1023 | if (!bufpool_enabled(ent->queue)) | ||
| 1024 | return 0; | ||
| 1025 | |||
| 1026 | buffer_selected = !!ent->payload.iov_base; | ||
| 1027 | has_payload = fuse_uring_req_has_copyable_payload(ent, req); | ||
| 1028 | |||
| 1029 | if (has_payload && !buffer_selected) | ||
| 1030 | return fuse_uring_select_buffer(ent); | ||
| 1031 | |||
| 1032 | if (!has_payload && buffer_selected) | ||
| 1033 | fuse_uring_recycle_buffer(ent); | ||
| 1034 | |||
| 1035 | return 0; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | static int fuse_uring_prep_buffer(struct fuse_ring_ent *ent, | ||
| 1039 | struct fuse_req *req) | ||
| 1040 | { | ||
| 1041 | if (!bufpool_enabled(ent->queue)) | ||
| 1042 | return 0; | ||
| 1043 | |||
| 1044 | /* no payload to copy, can skip selecting a buffer */ | ||
| 1045 | if (!fuse_uring_req_has_copyable_payload(ent, req)) | ||
| 1046 | return 0; | ||
| 1047 | |||
| 1048 | return fuse_uring_select_buffer(ent); | ||
| 770 | } | 1049 | } |
| 771 | 1050 | ||
| 772 | static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, | 1051 | static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, |
| 773 | struct fuse_req *req) | 1052 | struct fuse_req *req, |
| 1053 | unsigned int issue_flags) | ||
| 774 | { | 1054 | { |
| 775 | int err; | 1055 | int err; |
| 776 | 1056 | ||
| 777 | err = fuse_uring_copy_to_ring(ent, req); | 1057 | err = fuse_uring_copy_to_ring(ent, req, issue_flags); |
| 778 | if (!err) { | 1058 | if (!err) { |
| 779 | set_bit(FR_SENT, &req->flags); | 1059 | set_bit(FR_SENT, &req->flags); |
| 780 | trace_fuse_request_sent(req); | 1060 | trace_fuse_request_sent(req); |
| @@ -788,7 +1068,7 @@ static int fuse_uring_prepare_send(struct fuse_ring_ent *ent, · 7 unmodified lines | |||
| 788 | ent->state = FRRS_INVALID; | 1068 | ent->state = FRRS_INVALID; |
| 789 | spin_unlock(&ent->queue->lock); | 1069 | spin_unlock(&ent->queue->lock); |
| 790 | 1070 | ||
| 791 | fuse_uring_req_end(ent, req, err); | 1071 | fuse_uring_req_end(ent, req, err, issue_flags); |
| 792 | } | 1072 | } |
| 793 | 1073 | ||
| 794 | return err; | 1074 | return err; |
| @@ -856,9 +1136,12 @@ static struct fuse_req *fuse_uring_ent_assign_req(struct fuse_ring_ent *ent) · 61 unmodified lines | |||
| 856 | 1136 | ||
| 857 | /* get and assign the next entry while it is still holding the lock */ | 1137 | /* get and assign the next entry while it is still holding the lock */ |
| 858 | req = list_first_entry_or_null(req_queue, struct fuse_req, list); | 1138 | req = list_first_entry_or_null(req_queue, struct fuse_req, list); |
| 859 | if (req) | 1139 | if (!req || fuse_uring_next_req_update_buffer(ent, req)) { |
| 860 | fuse_uring_add_req_to_ring_ent(ent, req); | 1140 | fuse_uring_recycle_buffer(ent); |
| 1141 | return NULL; | ||
| 1142 | } | ||
| 861 | 1143 | ||
| 1144 | fuse_uring_add_req_to_ring_ent(ent, req); | ||
| 862 | return req; | 1145 | return req; |
| 863 | } | 1146 | } |
| 864 | 1147 | ||
| @@ -870,12 +1153,13 @@ static struct fuse_req *fuse_uring_ent_assign_req(struct fuse_ring_ent *ent) · 5 unmodified lines | |||
| 870 | static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, | 1153 | static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, |
| 871 | unsigned int issue_flags) | 1154 | unsigned int issue_flags) |
| 872 | { | 1155 | { |
| 873 | struct fuse_ring *ring = ent->queue->ring; | 1156 | struct fuse_out_header out_header; |
| 874 | ssize_t err = -EFAULT; | 1157 | ssize_t err = -EFAULT; |
| 875 | 1158 | ||
| 876 | if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &req->out.h, | 1159 | if (copy_header_from_ring(ent, FUSE_URING_HEADER_IN_OUT, &out_header, |
| 877 | sizeof(req->out.h))) | 1160 | sizeof(out_header))) |
| 878 | goto out; | 1161 | goto out; |
| 1162 | req->out.h = out_header; | ||
| 879 | 1163 | ||
| 880 | err = fuse_uring_out_header_has_err(&req->out.h, req); | 1164 | err = fuse_uring_out_header_has_err(&req->out.h, req); |
| 881 | if (err) { | 1165 | if (err) { |
| @@ -883,9 +1167,9 @@ static void fuse_uring_commit(struct fuse_ring_ent *ent, struct fuse_req *req, · 1 unmodified line | |||
| 883 | goto out; | 1167 | goto out; |
| 884 | } | 1168 | } |
| 885 | 1169 | ||
| 886 | err = fuse_uring_copy_from_ring(ring, req, ent); | 1170 | err = fuse_uring_copy_from_ring(req, ent, issue_flags); |
| 887 | out: | 1171 | out: |
| 888 | fuse_uring_req_end(ent, req, err); | 1172 | fuse_uring_req_end(ent, req, err, issue_flags); |
| 889 | } | 1173 | } |
| 890 | 1174 | ||
| 891 | /* | 1175 | /* |
| @@ -895,7 +1179,8 @@ out: · 3 unmodified lines | |||
| 895 | * Else, there is no next fuse request and this returns false. | 1179 | * Else, there is no next fuse request and this returns false. |
| 896 | */ | 1180 | */ |
| 897 | static bool fuse_uring_get_next_fuse_req(struct fuse_ring_ent *ent, | 1181 | static bool fuse_uring_get_next_fuse_req(struct fuse_ring_ent *ent, |
| 898 | struct fuse_ring_queue *queue) | 1182 | struct fuse_ring_queue *queue, |
| 1183 | unsigned int issue_flags) | ||
| 899 | { | 1184 | { |
| 900 | int err; | 1185 | int err; |
| 901 | struct fuse_req *req; | 1186 | struct fuse_req *req; |
| @@ -907,7 +1192,7 @@ retry: · 5 unmodified lines | |||
| 907 | spin_unlock(&queue->lock); | 1192 | spin_unlock(&queue->lock); |
| 908 | 1193 | ||
| 909 | if (req) { | 1194 | if (req) { |
| 910 | err = fuse_uring_prepare_send(ent, req); | 1195 | err = fuse_uring_prepare_send(ent, req, issue_flags); |
| 911 | if (err) | 1196 | if (err) |
| 912 | goto retry; | 1197 | goto retry; |
| 913 | } | 1198 | } |
| @@ -967,7 +1252,7 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, · 53 unmodified lines | |||
| 967 | if (qid >= ring->nr_queues) | 1252 | if (qid >= ring->nr_queues) |
| 968 | return -EINVAL; | 1253 | return -EINVAL; |
| 969 | 1254 | ||
| 970 | queue = ring->queues[qid]; | 1255 | queue = READ_ONCE(ring->queues[qid]); |
| 971 | if (!queue) | 1256 | if (!queue) |
| 972 | return err; | 1257 | return err; |
| 973 | fpq = &queue->fpq; | 1258 | fpq = &queue->fpq; |
| @@ -981,6 +1266,11 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, · 7 unmodified lines | |||
| 981 | return err; | 1266 | return err; |
| 982 | } | 1267 | } |
| 983 | 1268 | ||
| 1269 | if (!fuse_uring_cmd_index_ok(cmd, queue)) { | ||
| 1270 | spin_unlock(&queue->lock); | ||
| 1271 | return -EINVAL; | ||
| 1272 | } | ||
| 1273 | |||
| 984 | /* Find a request based on the unique ID of the fuse request | 1274 | /* Find a request based on the unique ID of the fuse request |
| 985 | * This should get revised, as it needs a hash calculation and list | 1275 | * This should get revised, as it needs a hash calculation and list |
| 986 | * search. And full struct fuse_pqueue is needed (memory overhead). | 1276 | * search. And full struct fuse_pqueue is needed (memory overhead). |
| @@ -1002,8 +1292,14 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, · 15 unmodified lines | |||
| 1002 | if (err != 0) { | 1292 | if (err != 0) { |
| 1003 | pr_info_ratelimited("qid=%d commit_id %llu state %d", | 1293 | pr_info_ratelimited("qid=%d commit_id %llu state %d", |
| 1004 | queue->qid, commit_id, ent->state); | 1294 | queue->qid, commit_id, ent->state); |
| 1295 | fuse_uring_recycle_buffer(ent); | ||
| 1005 | spin_unlock(&queue->lock); | 1296 | spin_unlock(&queue->lock); |
| 1006 | fuse_uring_req_end(ent, req, err); | 1297 | /* |
| 1298 | * Unregister any zero copyable pages since ent->cmd is null | ||
| 1299 | * when it hits fuse_uring_req_end() in this path | ||
| 1300 | */ | ||
| 1301 | zero_copy_unregister(cmd, ent, issue_flags); | ||
| 1302 | fuse_uring_req_end(ent, req, err, issue_flags); | ||
| 1007 | return err; | 1303 | return err; |
| 1008 | } | 1304 | } |
| 1009 | 1305 | ||
| @@ -1019,8 +1315,13 @@ static int fuse_uring_commit_fetch(struct io_uring_cmd *cmd, int issue_flags, · 9 unmodified lines | |||
| 1019 | * fuse requests would otherwise not get processed - committing | 1315 | * fuse requests would otherwise not get processed - committing |
| 1020 | * and fetching is done in one step vs legacy fuse, which has separated | 1316 | * and fetching is done in one step vs legacy fuse, which has separated |
| 1021 | * read (fetch request) and write (commit result). | 1317 | * read (fetch request) and write (commit result). |
| 1318 | * | ||
| 1319 | * If there is no next request or if all buffers are busy (if using a | ||
| 1320 | * bufpool), the cmd is not returned to userspace. The entry is left | ||
| 1321 | * available and the cmd only returns to userspace when there's a | ||
| 1322 | * next request and an available buffer. | ||
| 1022 | */ | 1323 | */ |
| 1023 | if (fuse_uring_get_next_fuse_req(ent, queue)) | 1324 | if (fuse_uring_get_next_fuse_req(ent, queue, issue_flags)) |
| 1024 | fuse_uring_send(ent, cmd, 0, issue_flags); | 1325 | fuse_uring_send(ent, cmd, 0, issue_flags); |
| 1025 | return 0; | 1326 | return 0; |
| 1026 | } | 1327 | } |
| @@ -1035,7 +1336,7 @@ static bool is_ring_ready(struct fuse_ring *ring, int current_qid) · 8 unmodified lines | |||
| 1035 | if (current_qid == qid) | 1336 | if (current_qid == qid) |
| 1036 | continue; | 1337 | continue; |
| 1037 | 1338 | ||
| 1038 | queue = ring->queues[qid]; | 1339 | queue = READ_ONCE(ring->queues[qid]); |
| 1039 | if (!queue) { | 1340 | if (!queue) { |
| 1040 | ready = false; | 1341 | ready = false; |
| 1041 | break; | 1342 | break; |
| @@ -1122,10 +1423,14 @@ static struct fuse_ring_ent * · 80 unmodified lines | |||
| 1122 | fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, | 1423 | fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, |
| 1123 | struct fuse_ring_queue *queue) | 1424 | struct fuse_ring_queue *queue) |
| 1124 | { | 1425 | { |
| 1426 | const struct fuse_uring_cmd_req *cmd_req = | ||
| 1427 | io_uring_sqe128_cmd(cmd->sqe, struct fuse_uring_cmd_req); | ||
| 1125 | struct fuse_ring *ring = queue->ring; | 1428 | struct fuse_ring *ring = queue->ring; |
| 1126 | struct fuse_ring_ent *ent; | 1429 | struct fuse_ring_ent *ent; |
| 1127 | struct iovec iov[FUSE_URING_IOV_SEGS]; | 1430 | struct iovec iov[FUSE_URING_IOV_SEGS]; |
| 1128 | struct iovec *headers, *payload; | 1431 | struct iovec *headers, *payload; |
| 1432 | unsigned int zero_copy_index; | ||
| 1433 | |||
| 1129 | int err; | 1434 | int err; |
| 1130 | 1435 | ||
| 1131 | err = fuse_uring_get_iovec_from_sqe(cmd->sqe, iov); | 1436 | err = fuse_uring_get_iovec_from_sqe(cmd->sqe, iov); |
| @@ -1135,6 +1440,10 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, · 3 unmodified lines | |||
| 1135 | return ERR_PTR(err); | 1440 | return ERR_PTR(err); |
| 1136 | } | 1441 | } |
| 1137 | 1442 | ||
| 1443 | zero_copy_index = READ_ONCE(cmd_req->ent_zero_copy_buf_index); | ||
| 1444 | if (zero_copy_index && !queue->zero_copy) | ||
| 1445 | return ERR_PTR(-EINVAL); | ||
| 1446 | |||
| 1138 | err = -EINVAL; | 1447 | err = -EINVAL; |
| 1139 | headers = &iov[FUSE_URING_IOV_HEADERS]; | 1448 | headers = &iov[FUSE_URING_IOV_HEADERS]; |
| 1140 | if (headers->iov_len < sizeof(struct fuse_uring_req_header)) { | 1449 | if (headers->iov_len < sizeof(struct fuse_uring_req_header)) { |
| @@ -1143,11 +1452,29 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, · 2 unmodified lines | |||
| 1143 | } | 1452 | } |
| 1144 | 1453 | ||
| 1145 | payload = &iov[FUSE_URING_IOV_PAYLOAD]; | 1454 | payload = &iov[FUSE_URING_IOV_PAYLOAD]; |
| 1146 | if (payload->iov_len < ring->max_payload_sz) { | 1455 | |
| 1147 | pr_info_ratelimited("Invalid req payload len %zu\n", | 1456 | spin_lock(&queue->lock); |
| 1148 | payload->iov_len); | 1457 | if (bufpool_enabled(queue)) { |
| 1149 | return ERR_PTR(err); | 1458 | if (payload->iov_base || payload->iov_len || |
| 1459 | !fuse_uring_cmd_index_ok(cmd, queue)) { | ||
| 1460 | spin_unlock(&queue->lock); | ||
| 1461 | return ERR_PTR(err); | ||
| 1462 | } | ||
| 1463 | } else { | ||
| 1464 | if (payload->iov_len < ring->max_payload_sz) { | ||
| 1465 | spin_unlock(&queue->lock); | ||
| 1466 | pr_info_ratelimited("Invalid req payload len %zu\n", | ||
| 1467 | payload->iov_len); | ||
| 1468 | return ERR_PTR(err); | ||
| 1469 | } | ||
| 1470 | if (queue->zero_copy) { | ||
| 1471 | spin_unlock(&queue->lock); | ||
| 1472 | pr_info_ratelimited("Can only use zero copy with bufpools\n"); | ||
| 1473 | return ERR_PTR(err); | ||
| 1474 | } | ||
| 1475 | queue->payload_mode = FUSE_PAYLOAD_PER_ENT; | ||
| 1150 | } | 1476 | } |
| 1477 | spin_unlock(&queue->lock); | ||
| 1151 | 1478 | ||
| 1152 | err = -ENOMEM; | 1479 | err = -ENOMEM; |
| 1153 | ent = kzalloc_obj(*ent, GFP_KERNEL_ACCOUNT); | 1480 | ent = kzalloc_obj(*ent, GFP_KERNEL_ACCOUNT); |
| @@ -1158,7 +1485,9 @@ fuse_uring_create_ring_ent(struct io_uring_cmd *cmd, · 4 unmodified lines | |||
| 1158 | 1485 | ||
| 1159 | ent->queue = queue; | 1486 | ent->queue = queue; |
| 1160 | ent->headers = headers->iov_base; | 1487 | ent->headers = headers->iov_base; |
| 1161 | ent->payload = payload->iov_base; | 1488 | if (queue->payload_mode == FUSE_PAYLOAD_PER_ENT) |
| 1489 | ent->payload = *payload; | ||
| 1490 | ent->zero_copy_index = zero_copy_index; | ||
| 1162 | 1491 | ||
| 1163 | atomic_inc(&ring->queue_refs); | 1492 | atomic_inc(&ring->queue_refs); |
| 1164 | return ent; | 1493 | return ent; |
| @@ -1176,26 +1505,21 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, · 11 unmodified lines | |||
| 1176 | struct fuse_ring *ring = smp_load_acquire(&fch->ring); | 1505 | struct fuse_ring *ring = smp_load_acquire(&fch->ring); |
| 1177 | struct fuse_ring_queue *queue; | 1506 | struct fuse_ring_queue *queue; |
| 1178 | struct fuse_ring_ent *ent; | 1507 | struct fuse_ring_ent *ent; |
| 1179 | int err; | ||
| 1180 | unsigned int qid = READ_ONCE(cmd_req->qid); | 1508 | unsigned int qid = READ_ONCE(cmd_req->qid); |
| 1181 | 1509 | ||
| 1182 | err = -ENOMEM; | 1510 | if (!ring) |
| 1183 | if (!ring) { | 1511 | return -EINVAL; |
| 1184 | ring = fuse_uring_create(fch); | ||
| 1185 | if (!ring) | ||
| 1186 | return err; | ||
| 1187 | } | ||
| 1188 | 1512 | ||
| 1189 | if (qid >= ring->nr_queues) { | 1513 | if (qid >= ring->nr_queues) { |
| 1190 | pr_info_ratelimited("fuse: Invalid ring qid %u\n", qid); | 1514 | pr_info_ratelimited("fuse: Invalid ring qid %u\n", qid); |
| 1191 | return -EINVAL; | 1515 | return -EINVAL; |
| 1192 | } | 1516 | } |
| 1193 | 1517 | ||
| 1194 | queue = ring->queues[qid]; | 1518 | queue = READ_ONCE(ring->queues[qid]); |
| 1195 | if (!queue) { | 1519 | if (!queue) { |
| 1196 | queue = fuse_uring_create_queue(ring, qid); | 1520 | queue = fuse_uring_create_queue(ring, qid, false, false); |
| 1197 | if (!queue) | 1521 | if (IS_ERR(queue)) |
| 1198 | return err; | 1522 | return PTR_ERR(queue); |
| 1199 | } | 1523 | } |
| 1200 | 1524 | ||
| 1201 | /* | 1525 | /* |
| @@ -1210,6 +1534,110 @@ static int fuse_uring_register(struct io_uring_cmd *cmd, · 8 unmodified lines | |||
| 1210 | return fuse_uring_do_register(ent, cmd, issue_flags); | 1534 | return fuse_uring_do_register(ent, cmd, issue_flags); |
| 1211 | } | 1535 | } |
| 1212 | 1536 | ||
| 1537 | static int fuse_uring_add_queue(struct io_uring_cmd *cmd, struct fuse_chan *fch) | ||
| 1538 | { | ||
| 1539 | const struct fuse_uring_cmd_req *cmd_req = | ||
| 1540 | io_uring_sqe128_cmd(cmd->sqe, struct fuse_uring_cmd_req); | ||
| 1541 | struct fuse_ring *ring = smp_load_acquire(&fch->ring); | ||
| 1542 | unsigned int qid = READ_ONCE(cmd_req->qid); | ||
| 1543 | uint64_t flags = READ_ONCE(cmd_req->flags); | ||
| 1544 | struct fuse_ring_queue *queue; | ||
| 1545 | bool zero_copy = flags & FUSE_URING_ZERO_COPY; | ||
| 1546 | |||
| 1547 | if (!ring) | ||
| 1548 | return -EINVAL; | ||
| 1549 | |||
| 1550 | if (qid >= ring->nr_queues) { | ||
| 1551 | pr_info_ratelimited("fuse: Invalid ring qid %u\n", qid); | ||
| 1552 | return -EINVAL; | ||
| 1553 | } | ||
| 1554 | |||
| 1555 | if (flags & ~FUSE_URING_ADD_QUEUE_FLAGS) | ||
| 1556 | return -EINVAL; | ||
| 1557 | |||
| 1558 | if (zero_copy && !capable(CAP_SYS_ADMIN)) | ||
| 1559 | return -EPERM; | ||
| 1560 | |||
| 1561 | queue = fuse_uring_create_queue(ring, qid, zero_copy, true); | ||
| 1562 | if (IS_ERR(queue)) | ||
| 1563 | return PTR_ERR(queue); | ||
| 1564 | |||
| 1565 | return 0; | ||
| 1566 | } | ||
| 1567 | |||
| 1568 | static int fuse_uring_add_bufpool(struct io_uring_cmd *cmd, | ||
| 1569 | struct fuse_chan *fch) | ||
| 1570 | { | ||
| 1571 | const struct fuse_uring_cmd_req *cmd_req = | ||
| 1572 | io_uring_sqe128_cmd(cmd->sqe, struct fuse_uring_cmd_req); | ||
| 1573 | unsigned int qid = READ_ONCE(cmd_req->qid); | ||
| 1574 | uint64_t flags = READ_ONCE(cmd_req->flags); | ||
| 1575 | /* paired with the smp_store_release() in fuse_uring_create */ | ||
| 1576 | struct fuse_ring *ring = smp_load_acquire(&fch->ring); | ||
| 1577 | struct fuse_ring_queue *queue; | ||
| 1578 | struct fuse_bufpool *pool; | ||
| 1579 | uintptr_t pool_uaddr; | ||
| 1580 | unsigned int pool_len, nr_bufs; | ||
| 1581 | size_t pool_size, buf_size; | ||
| 1582 | bool registered = cmd->flags & IORING_URING_CMD_FIXED; | ||
| 1583 | |||
| 1584 | if (!ring || qid >= ring->nr_queues || flags) | ||
| 1585 | return -EINVAL; | ||
| 1586 | |||
| 1587 | /* reserved for future use, must be zero */ | ||
| 1588 | if (READ_ONCE(cmd_req->bufpool.reserved)) | ||
| 1589 | return -EINVAL; | ||
| 1590 | |||
| 1591 | /* Pairs with smp_store_release() in fuse_uring_create_queue() */ | ||
| 1592 | queue = smp_load_acquire(&ring->queues[qid]); | ||
| 1593 | if (!queue) | ||
| 1594 | return -EINVAL; | ||
| 1595 | |||
| 1596 | pool_uaddr = READ_ONCE(cmd_req->bufpool.uaddr); | ||
| 1597 | pool_len = READ_ONCE(cmd_req->bufpool.len); | ||
| 1598 | |||
| 1599 | /* each buffer holds the max payload size */ | ||
| 1600 | buf_size = queue->ring->max_payload_sz; | ||
| 1601 | |||
| 1602 | nr_bufs = pool_len / buf_size; | ||
| 1603 | if (!nr_bufs) | ||
| 1604 | return -EINVAL; | ||
| 1605 | |||
| 1606 | pool_size = struct_size(pool, free_map, BITS_TO_LONGS(nr_bufs)); | ||
| 1607 | pool = kzalloc(pool_size, GFP_KERNEL_ACCOUNT); | ||
| 1608 | if (!pool) | ||
| 1609 | return -ENOMEM; | ||
| 1610 | |||
| 1611 | pool->base_uaddr = pool_uaddr; | ||
| 1612 | pool->buf_size = buf_size; | ||
| 1613 | pool->nr_bufs = nr_bufs; | ||
| 1614 | /* all buffers are free */ | ||
| 1615 | bitmap_set(pool->free_map, 0, nr_bufs); | ||
| 1616 | |||
| 1617 | /* | ||
| 1618 | * A registered bufpool is reached through an io_uring fixed buffer, so | ||
| 1619 | * the pool is registered iff this command was submitted with | ||
| 1620 | * IORING_URING_CMD_FIXED. The registered buffer index is taken from | ||
| 1621 | * sqe->buf_index. | ||
| 1622 | */ | ||
| 1623 | if (registered) { | ||
| 1624 | pool->registered = true; | ||
| 1625 | pool->registered_index = READ_ONCE(cmd->sqe->buf_index); | ||
| 1626 | } | ||
| 1627 | |||
| 1628 | spin_lock(&queue->lock); | ||
| 1629 | if (queue->payload_mode != FUSE_PAYLOAD_UNSET) { | ||
| 1630 | spin_unlock(&queue->lock); | ||
| 1631 | kfree(pool); | ||
| 1632 | return -EINVAL; | ||
| 1633 | } | ||
| 1634 | queue->bufpool = pool; | ||
| 1635 | queue->payload_mode = FUSE_PAYLOAD_BUFPOOL; | ||
| 1636 | spin_unlock(&queue->lock); | ||
| 1637 | |||
| 1638 | return 0; | ||
| 1639 | } | ||
| 1640 | |||
| 1213 | /* | 1641 | /* |
| 1214 | * Entry function from io_uring to handle the given passthrough command | 1642 | * Entry function from io_uring to handle the given passthrough command |
| 1215 | * (op code IORING_OP_URING_CMD) | 1643 | * (op code IORING_OP_URING_CMD) |
| @@ -1237,23 +1665,30 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) · 21 unmodified lines | |||
| 1237 | } | 1665 | } |
| 1238 | fch = fud->chan; | 1666 | fch = fud->chan; |
| 1239 | 1667 | ||
| 1240 | /* Once a connection has io-uring enabled on it, it can't be disabled */ | 1668 | /* |
| 1241 | if (!enable_uring && !fch->io_uring) { | 1669 | * The ring is sized from values negotiated by FUSE_INIT |
| 1242 | pr_info_ratelimited("fuse-io-uring is disabled\n"); | 1670 | * |
| 1243 | return -EOPNOTSUPP; | 1671 | * Pairs with smp_store_release() in fuse_chan_set_initialized() |
| 1244 | } | 1672 | */ |
| 1673 | if (!smp_load_acquire(&fch->initialized)) | ||
| 1674 | return -EAGAIN; | ||
| 1245 | 1675 | ||
| 1246 | if (fch->abort_with_err) | 1676 | if (fch->abort_with_err) |
| 1247 | return -ECONNABORTED; | 1677 | return -ECONNABORTED; |
| 1248 | if (!fch->connected) | 1678 | if (!fch->connected) |
| 1249 | return -ENOTCONN; | 1679 | return -ENOTCONN; |
| 1250 | 1680 | ||
| 1251 | /* | 1681 | /* Once a connection has io-uring enabled on it, it can't be disabled */ |
| 1252 | * fuse_uring_register() needs the ring to be initialized, | 1682 | if (!enable_uring && !fch->io_uring) { |
| 1253 | * we need to know the max payload size | 1683 | pr_info_ratelimited("fuse-io-uring is disabled by module parameter\n"); |
| 1254 | */ | 1684 | return -EOPNOTSUPP; |
| 1255 | if (!fch->initialized) | 1685 | } |
| 1256 | return -EAGAIN; | 1686 | |
| 1687 | if (!fch->io_uring) { | ||
| 1688 | pr_info_ratelimited( | ||
| 1689 | "fuse-io-uring not enabled on this connection\n"); | ||
| 1690 | return -EOPNOTSUPP; | ||
| 1691 | } | ||
| 1257 | 1692 | ||
| 1258 | switch (cmd_op) { | 1693 | switch (cmd_op) { |
| 1259 | case FUSE_IO_URING_CMD_REGISTER: | 1694 | case FUSE_IO_URING_CMD_REGISTER: |
| @@ -1274,6 +1709,18 @@ int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) · 14 unmodified lines | |||
| 1274 | return err; | 1709 | return err; |
| 1275 | } | 1710 | } |
| 1276 | break; | 1711 | break; |
| 1712 | case FUSE_IO_URING_CMD_ADD_QUEUE: | ||
| 1713 | err = fuse_uring_add_queue(cmd, fch); | ||
| 1714 | if (err) | ||
| 1715 | pr_info_once("FUSE_IO_URING_CMD_ADD_QUEUE failed err=%d\n", | ||
| 1716 | err); | ||
| 1717 | return err; | ||
| 1718 | case FUSE_IO_URING_CMD_ADD_BUFPOOL: | ||
| 1719 | err = fuse_uring_add_bufpool(cmd, fch); | ||
| 1720 | if (err) | ||
| 1721 | pr_info_once("FUSE_IO_URING_ADD_BUFPOOL failed err=%d\n", | ||
| 1722 | err); | ||
| 1723 | return err; | ||
| 1277 | default: | 1724 | default: |
| 1278 | return -EINVAL; | 1725 | return -EINVAL; |
| 1279 | } | 1726 | } |
| @@ -1295,9 +1742,10 @@ static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw) · 15 unmodified lines | |||
| 1295 | int err; | 1742 | int err; |
| 1296 | 1743 | ||
| 1297 | if (!tw.cancel) { | 1744 | if (!tw.cancel) { |
| 1298 | err = fuse_uring_prepare_send(ent, ent->fuse_req); | 1745 | err = fuse_uring_prepare_send(ent, ent->fuse_req, issue_flags); |
| 1299 | if (err) { | 1746 | if (err) { |
| 1300 | if (!fuse_uring_get_next_fuse_req(ent, queue)) | 1747 | if (!fuse_uring_get_next_fuse_req(ent, queue, |
| 1748 | issue_flags)) | ||
| 1301 | return; | 1749 | return; |
| 1302 | err = 0; | 1750 | err = 0; |
| 1303 | } | 1751 | } |
| @@ -1307,11 +1755,12 @@ static void fuse_uring_send_in_task(struct io_tw_req tw_req, io_tw_token_t tw) · 3 unmodified lines | |||
| 1307 | 1755 | ||
| 1308 | spin_lock(&queue->lock); | 1756 | spin_lock(&queue->lock); |
| 1309 | list_del_init(&ent->list); | 1757 | list_del_init(&ent->list); |
| 1758 | fuse_uring_recycle_buffer(ent); | ||
| 1310 | spin_unlock(&queue->lock); | 1759 | spin_unlock(&queue->lock); |
| 1311 | 1760 | ||
| 1312 | io_uring_cmd_done(cmd, err, issue_flags); | 1761 | io_uring_cmd_done(cmd, err, issue_flags); |
| 1313 | 1762 | ||
| 1314 | fuse_uring_req_end(ent, ent->fuse_req, err); | 1763 | fuse_uring_req_end(ent, ent->fuse_req, err, issue_flags); |
| 1315 | kfree(ent); | 1764 | kfree(ent); |
| 1316 | if (atomic_dec_and_test(&queue->ring->queue_refs)) | 1765 | if (atomic_dec_and_test(&queue->ring->queue_refs)) |
| 1317 | wake_up_all(&queue->ring->stop_waitq); | 1766 | wake_up_all(&queue->ring->stop_waitq); |
| @@ -1330,7 +1779,7 @@ static struct fuse_ring_queue *fuse_uring_task_to_queue(struct fuse_ring *ring) · 12 unmodified lines | |||
| 1330 | ring->nr_queues)) | 1779 | ring->nr_queues)) |
| 1331 | qid = 0; | 1780 | qid = 0; |
| 1332 | 1781 | ||
| 1333 | queue = ring->queues[qid]; | 1782 | queue = READ_ONCE(ring->queues[qid]); |
| 1334 | WARN_ONCE(!queue, "Missing queue for qid %d\n", qid); | 1783 | WARN_ONCE(!queue, "Missing queue for qid %d\n", qid); |
| 1335 | 1784 | ||
| 1336 | return queue; | 1785 | return queue; |
| @@ -1368,15 +1817,16 @@ void fuse_uring_queue_fuse_req(struct fuse_iqueue *fiq, struct fuse_req *req) · 31 unmodified lines | |||
| 1368 | req->ring_queue = queue; | 1817 | req->ring_queue = queue; |
| 1369 | ent = list_first_entry_or_null(&queue->ent_avail_queue, | 1818 | ent = list_first_entry_or_null(&queue->ent_avail_queue, |
| 1370 | struct fuse_ring_ent, list); | 1819 | struct fuse_ring_ent, list); |
| 1371 | if (ent) | ||
| 1372 | fuse_uring_add_req_to_ring_ent(ent, req); | ||
| 1373 | else | ||
| 1374 | list_add_tail(&req->list, &queue->fuse_req_queue); | ||
| 1375 | spin_unlock(&queue->lock); | ||
| 1376 | 1820 | ||
| 1377 | if (ent) | 1821 | if (!ent || fuse_uring_prep_buffer(ent, req)) { |
| 1378 | fuse_uring_dispatch_ent(ent); | 1822 | list_add_tail(&req->list, &queue->fuse_req_queue); |
| 1823 | spin_unlock(&queue->lock); | ||
| 1824 | return; | ||
| 1825 | } | ||
| 1379 | 1826 | ||
| 1827 | fuse_uring_add_req_to_ring_ent(ent, req); | ||
| 1828 | spin_unlock(&queue->lock); | ||
| 1829 | fuse_uring_dispatch_ent(ent); | ||
| 1380 | return; | 1830 | return; |
| 1381 | 1831 | ||
| 1382 | err_unlock: | 1832 | err_unlock: |
| @@ -1424,10 +1874,9 @@ bool fuse_uring_queue_bq_req(struct fuse_req *req) · 41 unmodified lines | |||
| 1424 | */ | 1874 | */ |
| 1425 | req = list_first_entry_or_null(&queue->fuse_req_queue, struct fuse_req, | 1875 | req = list_first_entry_or_null(&queue->fuse_req_queue, struct fuse_req, |
| 1426 | list); | 1876 | list); |
| 1427 | if (ent && req) { | 1877 | if (ent && req && !fuse_uring_prep_buffer(ent, req)) { |
| 1428 | fuse_uring_add_req_to_ring_ent(ent, req); | 1878 | fuse_uring_add_req_to_ring_ent(ent, req); |
| 1429 | spin_unlock(&queue->lock); | 1879 | spin_unlock(&queue->lock); |
| 1430 | |||
| 1431 | fuse_uring_dispatch_ent(ent); | 1880 | fuse_uring_dispatch_ent(ent); |
| 1432 | } else { | 1881 | } else { |
| 1433 | spin_unlock(&queue->lock); | 1882 | spin_unlock(&queue->lock); |
| @@ -7,6 +7,8 @@ · 6 unmodified lines | |||
| 7 | #ifndef _FS_FUSE_DEV_URING_I_H | 7 | #ifndef _FS_FUSE_DEV_URING_I_H |
| 8 | #define _FS_FUSE_DEV_URING_I_H | 8 | #define _FS_FUSE_DEV_URING_I_H |
| 9 | 9 | ||
| 10 | #include <linux/uio.h> | ||
| 11 | |||
| 10 | #include "fuse_dev_i.h" | 12 | #include "fuse_dev_i.h" |
| 11 | 13 | ||
| 12 | #ifdef CONFIG_FUSE_IO_URING | 14 | #ifdef CONFIG_FUSE_IO_URING |
| @@ -36,11 +38,50 @@ enum fuse_ring_req_state { · 23 unmodified lines | |||
| 36 | FRRS_RELEASED, | 38 | FRRS_RELEASED, |
| 37 | }; | 39 | }; |
| 38 | 40 | ||
| 41 | /* how a queue's payload buffers are provided */ | ||
| 42 | enum fuse_queue_payload_mode { | ||
| 43 | /* not yet committed (a bufpool may still be added) */ | ||
| 44 | FUSE_PAYLOAD_UNSET = 0, | ||
| 45 | /* each entry registers its own payload buffer */ | ||
| 46 | FUSE_PAYLOAD_PER_ENT, | ||
| 47 | /* each entry's payload buffer is assigned from a bufpool */ | ||
| 48 | FUSE_PAYLOAD_BUFPOOL, | ||
| 49 | }; | ||
| 50 | |||
| 51 | struct fuse_bufpool { | ||
| 52 | bool registered; | ||
| 53 | |||
| 54 | /* | ||
| 55 | * io_uring registered buffer table index for this pool, bound at | ||
| 56 | * ADD_BUFPOOL time. Only valid if the bufpool is registered | ||
| 57 | */ | ||
| 58 | u16 registered_index; | ||
| 59 | |||
| 60 | /* starting uaddr of the bufpool */ | ||
| 61 | uintptr_t base_uaddr; | ||
| 62 | |||
| 63 | /* size of each buffer in the pool */ | ||
| 64 | size_t buf_size; | ||
| 65 | |||
| 66 | /* total number of buffers in the pool */ | ||
| 67 | unsigned int nr_bufs; | ||
| 68 | |||
| 69 | /* bitmap tracking which buffers are free */ | ||
| 70 | unsigned long free_map[]; | ||
| 71 | }; | ||
| 72 | |||
| 39 | /** A fuse ring entry, part of the ring queue */ | 73 | /** A fuse ring entry, part of the ring queue */ |
| 40 | struct fuse_ring_ent { | 74 | struct fuse_ring_ent { |
| 41 | /* userspace buffer */ | 75 | /* userspace buffer */ |
| 42 | struct fuse_uring_req_header __user *headers; | 76 | struct fuse_uring_req_header __user *headers; |
| 43 | void __user *payload; | 77 | struct iovec payload; |
| 78 | |||
| 79 | /* buffer id in the pool, if bufpools are used. ignored otherwise */ | ||
| 80 | unsigned int buf_id; | ||
| 81 | |||
| 82 | /* true if the request's pages are being zero-copied */ | ||
| 83 | bool zero_copied; | ||
| 84 | unsigned int zero_copy_index; | ||
| 44 | 85 | ||
| 45 | /* the ring queue that owns the request */ | 86 | /* the ring queue that owns the request */ |
| 46 | struct fuse_ring_queue *queue; | 87 | struct fuse_ring_queue *queue; |
| @@ -99,6 +140,14 @@ struct fuse_ring_queue { · 52 unmodified lines | |||
| 99 | unsigned int active_background; | 140 | unsigned int active_background; |
| 100 | 141 | ||
| 101 | bool stopped; | 142 | bool stopped; |
| 143 | |||
| 144 | /* how this queue's payload buffers are provided */ | ||
| 145 | enum fuse_queue_payload_mode payload_mode; | ||
| 146 | |||
| 147 | /* only allocated when payload_mode == FUSE_PAYLOAD_BUFPOOL */ | ||
| 148 | struct fuse_bufpool *bufpool; | ||
| 149 | |||
| 150 | bool zero_copy; | ||
| 102 | }; | 151 | }; |
| 103 | 152 | ||
| 104 | /* | 153 | /* |
| @@ -135,6 +184,7 @@ struct fuse_ring { · 30 unmodified lines | |||
| 135 | bool ready; | 184 | bool ready; |
| 136 | }; | 185 | }; |
| 137 | 186 | ||
| 187 | void fuse_uring_conn_init(struct fuse_chan *fch); | ||
| 138 | void fuse_uring_stop_queues(struct fuse_ring *ring); | 188 | void fuse_uring_stop_queues(struct fuse_ring *ring); |
| 139 | void fuse_uring_abort_end_requests(struct fuse_ring *ring); | 189 | void fuse_uring_abort_end_requests(struct fuse_ring *ring); |
| 140 | int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags); | 190 | int fuse_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags); |
| @@ -174,6 +224,10 @@ static inline bool fuse_uring_ready(struct fuse_chan *fch) · 33 unmodified lines | |||
| 174 | 224 | ||
| 175 | #else /* CONFIG_FUSE_IO_URING */ | 225 | #else /* CONFIG_FUSE_IO_URING */ |
| 176 | 226 | ||
| 227 | static inline void fuse_uring_conn_init(struct fuse_chan *fch) | ||
| 228 | { | ||
| 229 | } | ||
| 230 | |||
| 177 | static inline void fuse_uring_abort(struct fuse_chan *fch) | 231 | static inline void fuse_uring_abort(struct fuse_chan *fch) |
| 178 | { | 232 | { |
| 179 | } | 233 | } |
| @@ -34,7 +34,7 @@ struct dentry_bucket { · 33 unmodified lines | |||
| 34 | #define FUSE_HASH_BITS 5 | 34 | #define FUSE_HASH_BITS 5 |
| 35 | #define FUSE_HASH_SIZE (1 << FUSE_HASH_BITS) | 35 | #define FUSE_HASH_SIZE (1 << FUSE_HASH_BITS) |
| 36 | static struct dentry_bucket dentry_hash[FUSE_HASH_SIZE]; | 36 | static struct dentry_bucket dentry_hash[FUSE_HASH_SIZE]; |
| 37 | struct delayed_work dentry_tree_work; | 37 | static struct delayed_work dentry_tree_work; |
| 38 | 38 | ||
| 39 | /* Minimum invalidation work queue frequency */ | 39 | /* Minimum invalidation work queue frequency */ |
| 40 | #define FUSE_DENTRY_INVAL_FREQ_MIN 5 | 40 | #define FUSE_DENTRY_INVAL_FREQ_MIN 5 |
| @@ -96,6 +96,7 @@ static void fuse_advise_use_readdirplus(struct inode *dir) · 55 unmodified lines | |||
| 96 | 96 | ||
| 97 | struct fuse_dentry { | 97 | struct fuse_dentry { |
| 98 | u64 time; | 98 | u64 time; |
| 99 | u64 epoch; | ||
| 99 | union { | 100 | union { |
| 100 | struct rcu_head rcu; | 101 | struct rcu_head rcu; |
| 101 | struct rb_node node; | 102 | struct rb_node node; |
| @@ -236,6 +237,13 @@ void fuse_dentry_tree_cleanup(void) · 134 unmodified lines | |||
| 236 | WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree)); | 237 | WARN_ON_ONCE(!RB_EMPTY_ROOT(&dentry_hash[i].tree)); |
| 237 | } | 238 | } |
| 238 | 239 | ||
| 240 | void fuse_dentry_set_epoch(struct dentry *dentry, u64 epoch) | ||
| 241 | { | ||
| 242 | struct fuse_dentry *fd = dentry->d_fsdata; | ||
| 243 | |||
| 244 | fd->epoch = epoch; | ||
| 245 | } | ||
| 246 | |||
| 239 | static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time) | 247 | static inline void __fuse_dentry_settime(struct dentry *dentry, u64 time) |
| 240 | { | 248 | { |
| 241 | ((struct fuse_dentry *) dentry->d_fsdata)->time = time; | 249 | ((struct fuse_dentry *) dentry->d_fsdata)->time = time; |
| @@ -387,10 +395,11 @@ static int fuse_dentry_revalidate(struct inode *dir, const struct qstr *name, · 145 unmodified lines | |||
| 387 | struct fuse_mount *fm; | 395 | struct fuse_mount *fm; |
| 388 | struct fuse_conn *fc; | 396 | struct fuse_conn *fc; |
| 389 | struct fuse_inode *fi; | 397 | struct fuse_inode *fi; |
| 398 | struct fuse_dentry *fd = entry->d_fsdata; | ||
| 390 | int ret; | 399 | int ret; |
| 391 | 400 | ||
| 392 | fc = get_fuse_conn_super(dir->i_sb); | 401 | fc = get_fuse_conn_super(dir->i_sb); |
| 393 | if (entry->d_time < atomic_read(&fc->epoch)) | 402 | if (fd->epoch < atomic_read(&fc->epoch)) |
| 394 | goto invalid; | 403 | goto invalid; |
| 395 | 404 | ||
| 396 | inode = d_inode_rcu(entry); | 405 | inode = d_inode_rcu(entry); |
| @@ -480,10 +489,10 @@ static int fuse_dentry_init(struct dentry *dentry) · 83 unmodified lines | |||
| 480 | RB_CLEAR_NODE(&fd->node); | 489 | RB_CLEAR_NODE(&fd->node); |
| 481 | dentry->d_fsdata = fd; | 490 | dentry->d_fsdata = fd; |
| 482 | /* | 491 | /* |
| 483 | * Initialising d_time (epoch) to '0' ensures the dentry is invalid | 492 | * Initialising epoch to '0' ensures the dentry is invalid |
| 484 | * if compared to fc->epoch, which is initialized to '1'. | 493 | * if compared to fc->epoch, which is initialized to '1'. |
| 485 | */ | 494 | */ |
| 486 | dentry->d_time = 0; | 495 | fuse_dentry_set_epoch(dentry, 0); |
| 487 | 496 | ||
| 488 | return 0; | 497 | return 0; |
| 489 | } | 498 | } |
| @@ -641,7 +650,7 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry, · 151 unmodified lines | |||
| 641 | goto out_err; | 650 | goto out_err; |
| 642 | 651 | ||
| 643 | entry = newent ? newent : entry; | 652 | entry = newent ? newent : entry; |
| 644 | entry->d_time = epoch; | 653 | fuse_dentry_set_epoch(entry, epoch); |
| 645 | if (outarg_valid) | 654 | if (outarg_valid) |
| 646 | fuse_change_entry_timeout(entry, &outarg); | 655 | fuse_change_entry_timeout(entry, &outarg); |
| 647 | else | 656 | else |
| @@ -898,7 +907,7 @@ static int fuse_create_open(struct mnt_idmap *idmap, struct inode *dir, · 250 unmodified lines | |||
| 898 | } | 907 | } |
| 899 | kfree(forget); | 908 | kfree(forget); |
| 900 | d_instantiate(entry, inode); | 909 | d_instantiate(entry, inode); |
| 901 | entry->d_time = epoch; | 910 | fuse_dentry_set_epoch(entry, epoch); |
| 902 | fuse_change_entry_timeout(entry, &outentry); | 911 | fuse_change_entry_timeout(entry, &outentry); |
| 903 | fuse_dir_changed(dir); | 912 | fuse_dir_changed(dir); |
| 904 | err = generic_file_open(inode, file); | 913 | err = generic_file_open(inode, file); |
| @@ -1028,10 +1037,10 @@ static struct dentry *create_new_entry(struct mnt_idmap *idmap, struct fuse_moun · 123 unmodified lines | |||
| 1028 | return d; | 1037 | return d; |
| 1029 | 1038 | ||
| 1030 | if (d) { | 1039 | if (d) { |
| 1031 | d->d_time = epoch; | 1040 | fuse_dentry_set_epoch(d, epoch); |
| 1032 | fuse_change_entry_timeout(d, &outarg); | 1041 | fuse_change_entry_timeout(d, &outarg); |
| 1033 | } else { | 1042 | } else { |
| 1034 | entry->d_time = epoch; | 1043 | fuse_dentry_set_epoch(entry, epoch); |
| 1035 | fuse_change_entry_timeout(entry, &outarg); | 1044 | fuse_change_entry_timeout(entry, &outarg); |
| 1036 | } | 1045 | } |
| 1037 | fuse_dir_changed(dir); | 1046 | fuse_dir_changed(dir); |
| @@ -2169,10 +2178,8 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, · 1131 unmodified lines | |||
| 2169 | filemap_invalidate_lock(mapping); | 2178 | filemap_invalidate_lock(mapping); |
| 2170 | fault_blocked = true; | 2179 | fault_blocked = true; |
| 2171 | err = fuse_dax_break_layouts(inode, 0, -1); | 2180 | err = fuse_dax_break_layouts(inode, 0, -1); |
| 2172 | if (err) { | 2181 | if (err) |
| 2173 | filemap_invalidate_unlock(mapping); | 2182 | goto unlock; |
| 2174 | return err; | ||
| 2175 | } | ||
| 2176 | } | 2183 | } |
| 2177 | 2184 | ||
| 2178 | if (attr->ia_valid & ATTR_OPEN) { | 2185 | if (attr->ia_valid & ATTR_OPEN) { |
| @@ -2199,7 +2206,7 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, · 20 unmodified lines | |||
| 2199 | ATTR_TIMES_SET)) { | 2206 | ATTR_TIMES_SET)) { |
| 2200 | err = write_inode_now(inode, true); | 2207 | err = write_inode_now(inode, true); |
| 2201 | if (err) | 2208 | if (err) |
| 2202 | return err; | 2209 | goto unlock; |
| 2203 | 2210 | ||
| 2204 | fuse_set_nowrite(inode); | 2211 | fuse_set_nowrite(inode); |
| 2205 | fuse_release_nowrite(inode); | 2212 | fuse_release_nowrite(inode); |
| @@ -2290,6 +2297,9 @@ int fuse_do_setattr(struct mnt_idmap *idmap, struct dentry *dentry, · 84 unmodified lines | |||
| 2290 | */ | 2297 | */ |
| 2291 | if ((is_truncate || !is_wb) && | 2298 | if ((is_truncate || !is_wb) && |
| 2292 | S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { | 2299 | S_ISREG(inode->i_mode) && oldsize != outarg.attr.size) { |
| 2300 | if (outarg.attr.size > oldsize) | ||
| 2301 | truncate_pagecache_range(inode, oldsize, | ||
| 2302 | outarg.attr.size - 1); | ||
| 2293 | truncate_pagecache(inode, outarg.attr.size); | 2303 | truncate_pagecache(inode, outarg.attr.size); |
| 2294 | invalidate_inode_pages2(mapping); | 2304 | invalidate_inode_pages2(mapping); |
| 2295 | } | 2305 | } |
| @@ -2307,6 +2317,7 @@ error: · 11 unmodified lines | |||
| 2307 | 2317 | ||
| 2308 | clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); | 2318 | clear_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); |
| 2309 | 2319 | ||
| 2320 | unlock: | ||
| 2310 | if (fault_blocked) | 2321 | if (fault_blocked) |
| 2311 | filemap_invalidate_unlock(mapping); | 2322 | filemap_invalidate_unlock(mapping); |
| 2312 | return err; | 2323 | return err; |
| @@ -272,7 +272,7 @@ static int fuse_open(struct inode *inode, struct file *file) · 271 unmodified lines | |||
| 272 | filemap_invalidate_lock(inode->i_mapping); | 272 | filemap_invalidate_lock(inode->i_mapping); |
| 273 | err = fuse_dax_break_layouts(inode, 0, -1); | 273 | err = fuse_dax_break_layouts(inode, 0, -1); |
| 274 | if (err) | 274 | if (err) |
| 275 | goto out_inode_unlock; | 275 | goto out_unlock; |
| 276 | } | 276 | } |
| 277 | 277 | ||
| 278 | if (is_wb_truncate || dax_truncate) | 278 | if (is_wb_truncate || dax_truncate) |
| @@ -296,9 +296,9 @@ static int fuse_open(struct inode *inode, struct file *file) · 17 unmodified lines | |||
| 296 | else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) | 296 | else if (!(ff->open_flags & FOPEN_KEEP_CACHE)) |
| 297 | invalidate_inode_pages2(inode->i_mapping); | 297 | invalidate_inode_pages2(inode->i_mapping); |
| 298 | } | 298 | } |
| 299 | out_unlock: | ||
| 299 | if (dax_truncate) | 300 | if (dax_truncate) |
| 300 | filemap_invalidate_unlock(inode->i_mapping); | 301 | filemap_invalidate_unlock(inode->i_mapping); |
| 301 | out_inode_unlock: | ||
| 302 | if (is_wb_truncate || dax_truncate) | 302 | if (is_wb_truncate || dax_truncate) |
| 303 | inode_unlock(inode); | 303 | inode_unlock(inode); |
| 304 | 304 | ||
| @@ -605,6 +605,7 @@ void fuse_read_args_fill(struct fuse_io_args *ia, struct file *file, loff_t pos, · 300 unmodified lines | |||
| 605 | args->out_argvar = true; | 605 | args->out_argvar = true; |
| 606 | args->out_numargs = 1; | 606 | args->out_numargs = 1; |
| 607 | args->out_args[0].size = count; | 607 | args->out_args[0].size = count; |
| 608 | args->zero_copy = ff->open_flags & FOPEN_IO_URING_ZERO_COPY; | ||
| 608 | } | 609 | } |
| 609 | 610 | ||
| 610 | static void fuse_release_user_pages(struct fuse_args_pages *ap, ssize_t nres, | 611 | static void fuse_release_user_pages(struct fuse_args_pages *ap, ssize_t nres, |
| @@ -1153,6 +1154,7 @@ static void fuse_write_args_fill(struct fuse_io_args *ia, struct fuse_file *ff, · 542 unmodified lines | |||
| 1153 | args->out_numargs = 1; | 1154 | args->out_numargs = 1; |
| 1154 | args->out_args[0].size = sizeof(ia->write.out); | 1155 | args->out_args[0].size = sizeof(ia->write.out); |
| 1155 | args->out_args[0].value = &ia->write.out; | 1156 | args->out_args[0].value = &ia->write.out; |
| 1157 | args->zero_copy = ff->open_flags & FOPEN_IO_URING_ZERO_COPY; | ||
| 1156 | } | 1158 | } |
| 1157 | 1159 | ||
| 1158 | static unsigned int fuse_write_flags(struct kiocb *iocb) | 1160 | static unsigned int fuse_write_flags(struct kiocb *iocb) |
| @@ -1346,9 +1348,13 @@ static ssize_t fuse_perform_write(struct kiocb *iocb, struct iov_iter *ii) · 187 unmodified lines | |||
| 1346 | struct fuse_conn *fc = get_fuse_conn(inode); | 1348 | struct fuse_conn *fc = get_fuse_conn(inode); |
| 1347 | struct fuse_inode *fi = get_fuse_inode(inode); | 1349 | struct fuse_inode *fi = get_fuse_inode(inode); |
| 1348 | loff_t pos = iocb->ki_pos; | 1350 | loff_t pos = iocb->ki_pos; |
| 1351 | loff_t old_size = i_size_read(inode); | ||
| 1349 | int err = 0; | 1352 | int err = 0; |
| 1350 | ssize_t res = 0; | 1353 | ssize_t res = 0; |
| 1351 | 1354 | ||
| 1355 | if (pos > old_size) | ||
| 1356 | truncate_pagecache_range(inode, old_size, pos - 1); | ||
| 1357 | |||
| 1352 | if (inode->i_size < pos + iov_iter_count(ii)) | 1358 | if (inode->i_size < pos + iov_iter_count(ii)) |
| 1353 | set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); | 1359 | set_bit(FUSE_I_SIZE_UNSTABLE, &fi->state); |
| 1354 | 1360 | ||
| @@ -1787,13 +1793,14 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) · 432 unmodified lines | |||
| 1787 | { | 1793 | { |
| 1788 | struct inode *inode = file_inode(iocb->ki_filp); | 1794 | struct inode *inode = file_inode(iocb->ki_filp); |
| 1789 | struct address_space *mapping = inode->i_mapping; | 1795 | struct address_space *mapping = inode->i_mapping; |
| 1790 | loff_t pos = iocb->ki_pos; | ||
| 1791 | ssize_t res; | 1796 | ssize_t res; |
| 1792 | bool exclusive; | 1797 | bool exclusive; |
| 1793 | 1798 | ||
| 1794 | fuse_dio_lock(iocb, from, &exclusive); | 1799 | fuse_dio_lock(iocb, from, &exclusive); |
| 1795 | res = generic_write_checks(iocb, from); | 1800 | res = generic_write_checks(iocb, from); |
| 1796 | if (res > 0) { | 1801 | if (res > 0) { |
| 1802 | loff_t pos = iocb->ki_pos; | ||
| 1803 | |||
| 1797 | task_io_account_write(res); | 1804 | task_io_account_write(res); |
| 1798 | if (!is_sync_kiocb(iocb)) { | 1805 | if (!is_sync_kiocb(iocb)) { |
| 1799 | res = fuse_direct_IO(iocb, from); | 1806 | res = fuse_direct_IO(iocb, from); |
| @@ -1808,7 +1815,7 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from) · 8 unmodified lines | |||
| 1808 | /* | 1815 | /* |
| 1809 | * As in generic_file_direct_write(), invalidate after | 1816 | * As in generic_file_direct_write(), invalidate after |
| 1810 | * write, to invalidate read-ahead cache that may have | 1817 | * write, to invalidate read-ahead cache that may have |
| 1811 | * with the write. | 1818 | * competed with the write. |
| 1812 | */ | 1819 | */ |
| 1813 | invalidate_inode_pages2_range(mapping, | 1820 | invalidate_inode_pages2_range(mapping, |
| 1814 | pos >> PAGE_SHIFT, | 1821 | pos >> PAGE_SHIFT, |
| @@ -2894,6 +2901,11 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, · 1079 unmodified lines | |||
| 2894 | 2901 | ||
| 2895 | /* we could have extended the file */ | 2902 | /* we could have extended the file */ |
| 2896 | if (!(mode & FALLOC_FL_KEEP_SIZE)) { | 2903 | if (!(mode & FALLOC_FL_KEEP_SIZE)) { |
| 2904 | loff_t oldsize = i_size_read(inode); | ||
| 2905 | |||
| 2906 | if (offset + length > oldsize) | ||
| 2907 | truncate_pagecache_range(inode, oldsize, | ||
| 2908 | offset + length - 1); | ||
| 2897 | if (fuse_write_update_attr(inode, offset + length, length)) | 2909 | if (fuse_write_update_attr(inode, offset + length, length)) |
| 2898 | file_update_time(file); | 2910 | file_update_time(file); |
| 2899 | } | 2911 | } |
| @@ -38,6 +38,8 @@ struct fuse_iqueue; · 37 unmodified lines | |||
| 38 | * @FR_PRIVATE: request is on private list | 38 | * @FR_PRIVATE: request is on private list |
| 39 | * @FR_ASYNC: request is asynchronous | 39 | * @FR_ASYNC: request is asynchronous |
| 40 | * @FR_URING: request is handled through fuse-io-uring | 40 | * @FR_URING: request is handled through fuse-io-uring |
| 41 | * @FR_SYNC_WAKEUP: use synchronous wakeup when queueing this request to | ||
| 42 | * give the scheduler a hint about the waker task | ||
| 41 | */ | 43 | */ |
| 42 | enum fuse_req_flag { | 44 | enum fuse_req_flag { |
| 43 | FR_ISREPLY, | 45 | FR_ISREPLY, |
| @@ -53,6 +55,7 @@ enum fuse_req_flag { · 9 unmodified lines | |||
| 53 | FR_PRIVATE, | 55 | FR_PRIVATE, |
| 54 | FR_ASYNC, | 56 | FR_ASYNC, |
| 55 | FR_URING, | 57 | FR_URING, |
| 58 | FR_SYNC_WAKEUP, | ||
| 56 | }; | 59 | }; |
| 57 | 60 | ||
| 58 | /** | 61 | /** |
| @@ -325,6 +328,8 @@ struct fuse_copy_state { · 266 unmodified lines | |||
| 325 | bool write:1; | 328 | bool write:1; |
| 326 | bool move_folios:1; | 329 | bool move_folios:1; |
| 327 | bool is_uring:1; | 330 | bool is_uring:1; |
| 331 | /* set when the payload is zero-copied. folios are filled in place */ | ||
| 332 | bool skip_folio_copy:1; | ||
| 328 | struct { | 333 | struct { |
| 329 | unsigned int copied_sz; /* copied size into the user buffer */ | 334 | unsigned int copied_sz; /* copied size into the user buffer */ |
| 330 | } ring; | 335 | } ring; |
| @@ -1054,6 +1054,8 @@ u64 fuse_time_to_jiffies(u64 sec, u32 nsec); · 1053 unmodified lines | |||
| 1054 | 1054 | ||
| 1055 | void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o); | 1055 | void fuse_change_entry_timeout(struct dentry *entry, struct fuse_entry_out *o); |
| 1056 | 1056 | ||
| 1057 | void fuse_dentry_set_epoch(struct dentry *dentry, u64 epoch); | ||
| 1058 | |||
| 1057 | /* | 1059 | /* |
| 1058 | * Initialize fuse_conn | 1060 | * Initialize fuse_conn |
| 1059 | */ | 1061 | */ |
| @@ -791,6 +791,9 @@ static int fuse_opt_fd(struct fs_context *fsc, struct file *file) · 790 unmodified lines | |||
| 791 | { | 791 | { |
| 792 | struct fuse_fs_context *ctx = fsc->fs_private; | 792 | struct fuse_fs_context *ctx = fsc->fs_private; |
| 793 | 793 | ||
| 794 | if (ctx->fud) | ||
| 795 | return invalfc(fsc, "Multiple fd specified"); | ||
| 796 | |||
| 794 | if (file->f_op != &fuse_dev_operations) | 797 | if (file->f_op != &fuse_dev_operations) |
| 795 | return invalfc(fsc, "fd is not a fuse device"); | 798 | return invalfc(fsc, "fd is not a fuse device"); |
| 796 | /* | 799 | /* |
| @@ -1272,6 +1275,7 @@ static void process_init_reply(struct fuse_args *args, int error) · 475 unmodified lines | |||
| 1272 | struct fuse_mount *fm = ia->fm; | 1275 | struct fuse_mount *fm = ia->fm; |
| 1273 | struct fuse_conn *fc = fm->fc; | 1276 | struct fuse_conn *fc = fm->fc; |
| 1274 | struct fuse_init_out *arg = &ia->out; | 1277 | struct fuse_init_out *arg = &ia->out; |
| 1278 | bool io_uring_enabled = false; | ||
| 1275 | bool ok = true; | 1279 | bool ok = true; |
| 1276 | 1280 | ||
| 1277 | if (error || arg->major != FUSE_KERNEL_VERSION) | 1281 | if (error || arg->major != FUSE_KERNEL_VERSION) |
| @@ -1402,7 +1406,7 @@ static void process_init_reply(struct fuse_args *args, int error) · 124 unmodified lines | |||
| 1402 | ok = false; | 1406 | ok = false; |
| 1403 | } | 1407 | } |
| 1404 | if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled()) | 1408 | if (flags & FUSE_OVER_IO_URING && fuse_uring_enabled()) |
| 1405 | fuse_chan_io_uring_enable(fc->chan); | 1409 | io_uring_enabled = true; |
| 1406 | 1410 | ||
| 1407 | if (flags & FUSE_REQUEST_TIMEOUT) | 1411 | if (flags & FUSE_REQUEST_TIMEOUT) |
| 1408 | timeout = arg->request_timeout; | 1412 | timeout = arg->request_timeout; |
| @@ -1416,6 +1420,7 @@ static void process_init_reply(struct fuse_args *args, int error) · 7 unmodified lines | |||
| 1416 | 1420 | ||
| 1417 | fm->sb->s_bdi->ra_pages = | 1421 | fm->sb->s_bdi->ra_pages = |
| 1418 | min(fm->sb->s_bdi->ra_pages, ra_pages); | 1422 | min(fm->sb->s_bdi->ra_pages, ra_pages); |
| 1423 | fm->sb->s_bdi->io_pages = fc->max_pages; | ||
| 1419 | fc->minor = arg->minor; | 1424 | fc->minor = arg->minor; |
| 1420 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; | 1425 | fc->max_write = arg->minor < 5 ? 4096 : arg->max_write; |
| 1421 | fc->max_write = max_t(unsigned, 4096, fc->max_write); | 1426 | fc->max_write = max_t(unsigned, 4096, fc->max_write); |
| @@ -1432,6 +1437,7 @@ static void process_init_reply(struct fuse_args *args, int error) · 10 unmodified lines | |||
| 1432 | .minor = fc->minor, | 1437 | .minor = fc->minor, |
| 1433 | .max_write = fc->max_write, | 1438 | .max_write = fc->max_write, |
| 1434 | .max_pages = fc->max_pages, | 1439 | .max_pages = fc->max_pages, |
| 1440 | .io_uring_enabled = io_uring_enabled, | ||
| 1435 | }; | 1441 | }; |
| 1436 | fuse_chan_set_initialized(fc->chan, &cp); | 1442 | fuse_chan_set_initialized(fc->chan, &cp); |
| 1437 | } | 1443 | } |
| @@ -1474,12 +1480,8 @@ static struct fuse_init_args *fuse_new_init(struct fuse_mount *fm) · 36 unmodified lines | |||
| 1474 | if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) | 1480 | if (IS_ENABLED(CONFIG_FUSE_PASSTHROUGH)) |
| 1475 | flags |= FUSE_PASSTHROUGH; | 1481 | flags |= FUSE_PASSTHROUGH; |
| 1476 | 1482 | ||
| 1477 | /* | ||
| 1478 | * This is just an information flag for fuse server. No need to check | ||
| 1479 | * the reply - server is either sending IORING_OP_URING_CMD or not. | ||
| 1480 | */ | ||
| 1481 | if (fuse_uring_enabled()) | 1483 | if (fuse_uring_enabled()) |
| 1482 | flags |= FUSE_OVER_IO_URING; | 1484 | flags |= FUSE_OVER_IO_URING | FUSE_HAS_IO_URING_BUFPOOL; |
| 1483 | 1485 | ||
| 1484 | ia->in.flags = flags; | 1486 | ia->in.flags = flags; |
| 1485 | ia->in.flags2 = flags >> 32; | 1487 | ia->in.flags2 = flags >> 32; |
| @@ -1639,6 +1641,8 @@ static int fuse_fill_super_submount(struct super_block *sb, · 153 unmodified lines | |||
| 1639 | fuse_fill_attr_from_inode(&root_attr, parent_fi); | 1641 | fuse_fill_attr_from_inode(&root_attr, parent_fi); |
| 1640 | root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0, | 1642 | root = fuse_iget(sb, parent_fi->nodeid, 0, &root_attr, 0, 0, |
| 1641 | fuse_get_evict_ctr(fm->fc)); | 1643 | fuse_get_evict_ctr(fm->fc)); |
| 1644 | if (!root) | ||
| 1645 | return -ENOMEM; | ||
| 1642 | /* | 1646 | /* |
| 1643 | * This inode is just a duplicate, so it is not looked up and | 1647 | * This inode is just a duplicate, so it is not looked up and |
| 1644 | * its nlookup should not be incremented. fuse_iget() does | 1648 | * its nlookup should not be incremented. fuse_iget() does |
| @@ -260,7 +260,7 @@ retry: · 259 unmodified lines | |||
| 260 | } | 260 | } |
| 261 | if (fc->readdirplus_auto) | 261 | if (fc->readdirplus_auto) |
| 262 | set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); | 262 | set_bit(FUSE_I_INIT_RDPLUS, &get_fuse_inode(inode)->state); |
| 263 | dentry->d_time = epoch; | 263 | fuse_dentry_set_epoch(dentry, epoch); |
| 264 | fuse_change_entry_timeout(dentry, o); | 264 | fuse_change_entry_timeout(dentry, o); |
| 265 | 265 | ||
| 266 | dput(dentry); | 266 | dput(dentry); |
| @@ -128,18 +128,12 @@ static void set_request_timeout(struct fuse_chan *fch, unsigned int timeout) · 127 unmodified lines | |||
| 128 | 128 | ||
| 129 | void fuse_init_server_timeout(struct fuse_chan *fch, unsigned int timeout) | 129 | void fuse_init_server_timeout(struct fuse_chan *fch, unsigned int timeout) |
| 130 | { | 130 | { |
| 131 | if (!timeout && !fuse_max_req_timeout && !fuse_default_req_timeout) | ||
| 132 | return; | ||
| 133 | |||
| 134 | if (!timeout) | 131 | if (!timeout) |
| 135 | timeout = fuse_default_req_timeout; | 132 | timeout = fuse_default_req_timeout; |
| 136 | 133 | ||
| 137 | if (fuse_max_req_timeout) { | 134 | timeout = min_not_zero(timeout, fuse_max_req_timeout); |
| 138 | if (timeout) | 135 | if (!timeout) |
| 139 | timeout = min(fuse_max_req_timeout, timeout); | 136 | return; |
| 140 | else | ||
| 141 | timeout = fuse_max_req_timeout; | ||
| 142 | } | ||
| 143 | 137 | ||
| 144 | timeout = max(FUSE_TIMEOUT_TIMER_FREQ, timeout); | 138 | timeout = max(FUSE_TIMEOUT_TIMER_FREQ, timeout); |
| 145 | 139 | ||
| @@ -91,6 +91,15 @@ struct io_br_sel io_uring_cmd_buffer_select(struct io_uring_cmd *ioucmd, · 90 unmodified lines | |||
| 91 | bool io_uring_mshot_cmd_post_cqe(struct io_uring_cmd *ioucmd, | 91 | bool io_uring_mshot_cmd_post_cqe(struct io_uring_cmd *ioucmd, |
| 92 | struct io_br_sel *sel, unsigned int issue_flags); | 92 | struct io_br_sel *sel, unsigned int issue_flags); |
| 93 | 93 | ||
| 94 | int io_buffer_register_request(struct io_uring_cmd *cmd, struct request *rq, | ||
| 95 | void (*release)(void *), unsigned int index, | ||
| 96 | unsigned int issue_flags); | ||
| 97 | int io_buffer_register_bvec(struct io_uring_cmd *cmd, const struct bio_vec *bvs, | ||
| 98 | unsigned int nr_bvecs, void (*release)(void *), | ||
| 99 | void *priv, u8 dir, unsigned int index, | ||
| 100 | unsigned int issue_flags); | ||
| 101 | int io_buffer_unregister(struct io_uring_cmd *cmd, unsigned int index, | ||
| 102 | unsigned int issue_flags); | ||
| 94 | #else | 103 | #else |
| 95 | static inline int | 104 | static inline int |
| 96 | io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw, | 105 | io_uring_cmd_import_fixed(u64 ubuf, unsigned long len, int rw, |
| @@ -133,6 +142,29 @@ static inline bool io_uring_mshot_cmd_post_cqe(struct io_uring_cmd *ioucmd, · 36 unmodified lines | |||
| 133 | { | 142 | { |
| 134 | return true; | 143 | return true; |
| 135 | } | 144 | } |
| 145 | static inline int io_buffer_register_request(struct io_uring_cmd *cmd, | ||
| 146 | struct request *rq, | ||
| 147 | void (*release)(void *), | ||
| 148 | unsigned int index, | ||
| 149 | unsigned int issue_flags) | ||
| 150 | { | ||
| 151 | return -EOPNOTSUPP; | ||
| 152 | } | ||
| 153 | static inline int io_buffer_register_bvec(struct io_uring_cmd *cmd, | ||
| 154 | const struct bio_vec *bvs, | ||
| 155 | unsigned int nr_bvecs, | ||
| 156 | void (*release)(void *), void *priv, | ||
| 157 | u8 dir, unsigned int index, | ||
| 158 | unsigned int issue_flags) | ||
| 159 | { | ||
| 160 | return -EOPNOTSUPP; | ||
| 161 | } | ||
| 162 | static inline int io_buffer_unregister(struct io_uring_cmd *cmd, | ||
| 163 | unsigned int index, | ||
| 164 | unsigned int issue_flags) | ||
| 165 | { | ||
| 166 | return -EOPNOTSUPP; | ||
| 167 | } | ||
| 136 | #endif | 168 | #endif |
| 137 | 169 | ||
| 138 | static inline struct io_uring_cmd *io_uring_cmd_from_tw(struct io_tw_req tw_req) | 170 | static inline struct io_uring_cmd *io_uring_cmd_from_tw(struct io_tw_req tw_req) |
| @@ -182,10 +214,4 @@ static inline void io_uring_cmd_done32(struct io_uring_cmd *ioucmd, s32 ret, · 43 unmodified lines | |||
| 182 | return __io_uring_cmd_done(ioucmd, ret, res2, issue_flags, true); | 214 | return __io_uring_cmd_done(ioucmd, ret, res2, issue_flags, true); |
| 183 | } | 215 | } |
| 184 | 216 | ||
| 185 | int io_buffer_register_bvec(struct io_uring_cmd *cmd, struct request *rq, | ||
| 186 | void (*release)(void *), unsigned int index, | ||
| 187 | unsigned int issue_flags); | ||
| 188 | int io_buffer_unregister_bvec(struct io_uring_cmd *cmd, unsigned int index, | ||
| 189 | unsigned int issue_flags); | ||
| 190 | |||
| 191 | #endif /* _LINUX_IO_URING_CMD_H */ | 217 | #endif /* _LINUX_IO_URING_CMD_H */ |
| @@ -53,6 +53,11 @@ enum io_uring_cmd_flags { · 52 unmodified lines | |||
| 53 | IO_URING_F_COMPAT = (1 << 12), | 53 | IO_URING_F_COMPAT = (1 << 12), |
| 54 | }; | 54 | }; |
| 55 | 55 | ||
| 56 | enum { | ||
| 57 | IO_BUF_DEST = 1 << ITER_DEST, | ||
| 58 | IO_BUF_SOURCE = 1 << ITER_SOURCE, | ||
| 59 | }; | ||
| 60 | |||
| 56 | struct iou_loop_params; | 61 | struct iou_loop_params; |
| 57 | 62 | ||
| 58 | struct io_wq_work_node { | 63 | struct io_wq_work_node { |
| @@ -240,6 +240,14 @@ · 239 unmodified lines | |||
| 240 | * - add FUSE_COPY_FILE_RANGE_64 | 240 | * - add FUSE_COPY_FILE_RANGE_64 |
| 241 | * - add struct fuse_copy_file_range_out | 241 | * - add struct fuse_copy_file_range_out |
| 242 | * - add FUSE_NOTIFY_PRUNE | 242 | * - add FUSE_NOTIFY_PRUNE |
| 243 | * | ||
| 244 | * 7.46 | ||
| 245 | * - add FUSE_IO_URING_CMD_ADD_QUEUE | ||
| 246 | * - add FUSE_HAS_IO_URING_BUFPOOL | ||
| 247 | * - add fuse_uring_cmd_req bufpool struct | ||
| 248 | * - add bufpool offset field to fuse_uring_ent_in_out struct | ||
| 249 | * - add FUSE_URING_ZERO_COPY, FUSE_URING_ENT_ZERO_COPY, and | ||
| 250 | * FOPEN_IO_URING_ZERO_COPY flag | ||
| 243 | */ | 251 | */ |
| 244 | 252 | ||
| 245 | #ifndef _LINUX_FUSE_H | 253 | #ifndef _LINUX_FUSE_H |
| @@ -275,7 +283,7 @@ · 29 unmodified lines | |||
| 275 | #define FUSE_KERNEL_VERSION 7 | 283 | #define FUSE_KERNEL_VERSION 7 |
| 276 | 284 | ||
| 277 | /** Minor version number of this interface */ | 285 | /** Minor version number of this interface */ |
| 278 | #define FUSE_KERNEL_MINOR_VERSION 45 | 286 | #define FUSE_KERNEL_MINOR_VERSION 46 |
| 279 | 287 | ||
| 280 | /** The node ID of the root inode */ | 288 | /** The node ID of the root inode */ |
| 281 | #define FUSE_ROOT_ID 1 | 289 | #define FUSE_ROOT_ID 1 |
| @@ -383,6 +391,12 @@ struct fuse_file_lock { · 101 unmodified lines | |||
| 383 | * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) | 391 | * FOPEN_NOFLUSH: don't flush data cache on close (unless FUSE_WRITEBACK_CACHE) |
| 384 | * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode | 392 | * FOPEN_PARALLEL_DIRECT_WRITES: Allow concurrent direct writes on the same inode |
| 385 | * FOPEN_PASSTHROUGH: passthrough read/write io for this open file | 393 | * FOPEN_PASSTHROUGH: passthrough read/write io for this open file |
| 394 | * FOPEN_IO_URING_ZERO_COPY: use io-uring zero-copy for reads/writes on this | ||
| 395 | * open file. Honored only when the serving io-uring | ||
| 396 | * queue was set up for zero-copy | ||
| 397 | * (FUSE_URING_ZERO_COPY) and the request carries page | ||
| 398 | * payload. Otherwise reads/writes fall back to | ||
| 399 | * copying. | ||
| 386 | */ | 400 | */ |
| 387 | #define FOPEN_DIRECT_IO (1 << 0) | 401 | #define FOPEN_DIRECT_IO (1 << 0) |
| 388 | #define FOPEN_KEEP_CACHE (1 << 1) | 402 | #define FOPEN_KEEP_CACHE (1 << 1) |
| @@ -392,6 +406,7 @@ struct fuse_file_lock { · 3 unmodified lines | |||
| 392 | #define FOPEN_NOFLUSH (1 << 5) | 406 | #define FOPEN_NOFLUSH (1 << 5) |
| 393 | #define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) | 407 | #define FOPEN_PARALLEL_DIRECT_WRITES (1 << 6) |
| 394 | #define FOPEN_PASSTHROUGH (1 << 7) | 408 | #define FOPEN_PASSTHROUGH (1 << 7) |
| 409 | #define FOPEN_IO_URING_ZERO_COPY (1 << 8) | ||
| 395 | 410 | ||
| 396 | /** | 411 | /** |
| 397 | * INIT request/reply flags | 412 | * INIT request/reply flags |
| @@ -448,6 +463,7 @@ struct fuse_file_lock { · 50 unmodified lines | |||
| 448 | * FUSE_OVER_IO_URING: Indicate that client supports io-uring | 463 | * FUSE_OVER_IO_URING: Indicate that client supports io-uring |
| 449 | * FUSE_REQUEST_TIMEOUT: kernel supports timing out requests. | 464 | * FUSE_REQUEST_TIMEOUT: kernel supports timing out requests. |
| 450 | * init_out.request_timeout contains the timeout (in secs) | 465 | * init_out.request_timeout contains the timeout (in secs) |
| 466 | * FUSE_HAS_IO_URING_BUFPOOL: kernel supports io-uring buffer pools | ||
| 451 | */ | 467 | */ |
| 452 | #define FUSE_ASYNC_READ (1 << 0) | 468 | #define FUSE_ASYNC_READ (1 << 0) |
| 453 | #define FUSE_POSIX_LOCKS (1 << 1) | 469 | #define FUSE_POSIX_LOCKS (1 << 1) |
| @@ -495,6 +511,7 @@ struct fuse_file_lock { · 41 unmodified lines | |||
| 495 | #define FUSE_ALLOW_IDMAP (1ULL << 40) | 511 | #define FUSE_ALLOW_IDMAP (1ULL << 40) |
| 496 | #define FUSE_OVER_IO_URING (1ULL << 41) | 512 | #define FUSE_OVER_IO_URING (1ULL << 41) |
| 497 | #define FUSE_REQUEST_TIMEOUT (1ULL << 42) | 513 | #define FUSE_REQUEST_TIMEOUT (1ULL << 42) |
| 514 | #define FUSE_HAS_IO_URING_BUFPOOL (1ULL << 43) | ||
| 498 | 515 | ||
| 499 | /** | 516 | /** |
| 500 | * CUSE INIT request/reply flags | 517 | * CUSE INIT request/reply flags |
| @@ -1251,6 +1268,13 @@ struct fuse_supp_groups { · 750 unmodified lines | |||
| 1251 | #define FUSE_URING_IN_OUT_HEADER_SZ 128 | 1268 | #define FUSE_URING_IN_OUT_HEADER_SZ 128 |
| 1252 | #define FUSE_URING_OP_IN_OUT_SZ 128 | 1269 | #define FUSE_URING_OP_IN_OUT_SZ 128 |
| 1253 | 1270 | ||
| 1271 | /** | ||
| 1272 | * fuse_uring_ent_in_out flags | ||
| 1273 | * | ||
| 1274 | * FUSE_URING_ENT_ZERO_COPY: Set if the ent's payload is zero-copied | ||
| 1275 | */ | ||
| 1276 | #define FUSE_URING_ENT_ZERO_COPY (1 << 0) | ||
| 1277 | |||
| 1254 | /* Used as part of the fuse_uring_req_header */ | 1278 | /* Used as part of the fuse_uring_req_header */ |
| 1255 | struct fuse_uring_ent_in_out { | 1279 | struct fuse_uring_ent_in_out { |
| 1256 | uint64_t flags; | 1280 | uint64_t flags; |
| @@ -1263,7 +1287,9 @@ struct fuse_uring_ent_in_out { · 6 unmodified lines | |||
| 1263 | 1287 | ||
| 1264 | /* size of user payload buffer */ | 1288 | /* size of user payload buffer */ |
| 1265 | uint32_t payload_sz; | 1289 | uint32_t payload_sz; |
| 1266 | uint32_t padding; | 1290 | |
| 1291 | /* Offset into the bufpool, if bufpools are used */ | ||
| 1292 | uint32_t offset; | ||
| 1267 | 1293 | ||
| 1268 | uint64_t reserved; | 1294 | uint64_t reserved; |
| 1269 | }; | 1295 | }; |
| @@ -1292,8 +1318,22 @@ enum fuse_uring_cmd { · 22 unmodified lines | |||
| 1292 | 1318 | ||
| 1293 | /* commit fuse request result and fetch next request */ | 1319 | /* commit fuse request result and fetch next request */ |
| 1294 | FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2, | 1320 | FUSE_IO_URING_CMD_COMMIT_AND_FETCH = 2, |
| 1321 | |||
| 1322 | /* add a queue */ | ||
| 1323 | FUSE_IO_URING_CMD_ADD_QUEUE = 3, | ||
| 1324 | |||
| 1325 | /* add a bufpool to a queue */ | ||
| 1326 | FUSE_IO_URING_CMD_ADD_BUFPOOL = 4, | ||
| 1295 | }; | 1327 | }; |
| 1296 | 1328 | ||
| 1329 | /* | ||
| 1330 | * fuse_uring_cmd_req flags for FUSE_IO_URING_CMD_ADD_QUEUE | ||
| 1331 | * | ||
| 1332 | * FUSE_URING_ZERO_COPY is only supported for queues with bufpools on privileged | ||
| 1333 | * servers | ||
| 1334 | */ | ||
| 1335 | #define FUSE_URING_ZERO_COPY (1 << 0) | ||
| 1336 | |||
| 1297 | /** | 1337 | /** |
| 1298 | * In the 80B command area of the SQE. | 1338 | * In the 80B command area of the SQE. |
| 1299 | */ | 1339 | */ |
| @@ -1306,6 +1346,25 @@ struct fuse_uring_cmd_req { · 6 unmodified lines | |||
| 1306 | /* queue the command is for (queue index) */ | 1346 | /* queue the command is for (queue index) */ |
| 1307 | uint16_t qid; | 1347 | uint16_t qid; |
| 1308 | uint8_t padding[6]; | 1348 | uint8_t padding[6]; |
| 1349 | |||
| 1350 | union { | ||
| 1351 | struct { | ||
| 1352 | /* base address of bufpool */ | ||
| 1353 | uint64_t uaddr; | ||
| 1354 | uint32_t len; | ||
| 1355 | uint32_t reserved; | ||
| 1356 | } bufpool; | ||
| 1357 | |||
| 1358 | /* | ||
| 1359 | * Index of this entry's slot in the server's io_uring | ||
| 1360 | * registered buffer table, where the kernel registers the | ||
| 1361 | * request's pages for zero-copy. Set for | ||
| 1362 | * FUSE_IO_URING_CMD_REGISTER cmds only, and only on queues | ||
| 1363 | * created with FUSE_URING_ZERO_COPY. On a non-zero-copy queue | ||
| 1364 | * this must be 0 | ||
| 1365 | */ | ||
| 1366 | uint16_t ent_zero_copy_buf_index; | ||
| 1367 | }; | ||
| 1309 | }; | 1368 | }; |
| 1310 | 1369 | ||
| 1311 | #endif /* _LINUX_FUSE_H */ | 1370 | #endif /* _LINUX_FUSE_H */ |
| @@ -3236,7 +3236,7 @@ static int __init io_uring_init(void) · 3235 unmodified lines | |||
| 3236 | io_uring_optable_init(); | 3236 | io_uring_optable_init(); |
| 3237 | 3237 | ||
| 3238 | /* imu->dir is u8 */ | 3238 | /* imu->dir is u8 */ |
| 3239 | BUILD_BUG_ON((IO_IMU_DEST | IO_IMU_SOURCE) > U8_MAX); | 3239 | BUILD_BUG_ON((IO_BUF_DEST | IO_BUF_SOURCE) > U8_MAX); |
| 3240 | 3240 | ||
| 3241 | /* | 3241 | /* |
| 3242 | * Allow user copy in the per-command field, which starts after the | 3242 | * Allow user copy in the per-command field, which starts after the |
| @@ -912,7 +912,7 @@ static struct io_rsrc_node *io_sqe_buffer_register(struct io_ring_ctx *ctx, · 911 unmodified lines | |||
| 912 | imu->release = io_release_ubuf; | 912 | imu->release = io_release_ubuf; |
| 913 | imu->priv = imu; | 913 | imu->priv = imu; |
| 914 | imu->flags = 0; | 914 | imu->flags = 0; |
| 915 | imu->dir = IO_IMU_DEST | IO_IMU_SOURCE; | 915 | imu->dir = IO_BUF_DEST | IO_BUF_SOURCE; |
| 916 | if (coalesced) | 916 | if (coalesced) |
| 917 | imu->folio_shift = data.folio_shift; | 917 | imu->folio_shift = data.folio_shift; |
| 918 | refcount_set(&imu->refs, 1); | 918 | refcount_set(&imu->refs, 1); |
| @@ -1015,71 +1015,124 @@ int io_sqe_buffers_register(struct io_ring_ctx *ctx, void __user *arg, · 96 unmodified lines | |||
| 1015 | return ret; | 1015 | return ret; |
| 1016 | } | 1016 | } |
| 1017 | 1017 | ||
| 1018 | int io_buffer_register_bvec(struct io_uring_cmd *cmd, struct request *rq, | 1018 | static struct io_mapped_ubuf *io_kernel_buffer_init(struct io_ring_ctx *ctx, |
| 1019 | void (*release)(void *), unsigned int index, | 1019 | unsigned int nr_bvecs, |
| 1020 | unsigned int issue_flags) | 1020 | unsigned int total_bytes, |
| 1021 | u8 dir, | ||
| 1022 | void (*release)(void *), | ||
| 1023 | void *priv, | ||
| 1024 | unsigned int index) | ||
| 1021 | { | 1025 | { |
| 1022 | struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; | ||
| 1023 | struct io_rsrc_data *data = &ctx->buf_table; | 1026 | struct io_rsrc_data *data = &ctx->buf_table; |
| 1024 | struct req_iterator rq_iter; | ||
| 1025 | struct io_mapped_ubuf *imu; | 1027 | struct io_mapped_ubuf *imu; |
| 1026 | struct io_rsrc_node *node; | 1028 | struct io_rsrc_node *node; |
| 1027 | struct bio_vec bv; | ||
| 1028 | unsigned int nr_bvecs = 0; | ||
| 1029 | int ret = 0; | ||
| 1030 | 1029 | ||
| 1031 | io_ring_submit_lock(ctx, issue_flags); | 1030 | if (index >= data->nr) |
| 1032 | if (index >= data->nr) { | 1031 | return ERR_PTR(-EINVAL); |
| 1033 | ret = -EINVAL; | ||
| 1034 | goto unlock; | ||
| 1035 | } | ||
| 1036 | index = array_index_nospec(index, data->nr); | 1032 | index = array_index_nospec(index, data->nr); |
| 1037 | 1033 | ||
| 1038 | if (data->nodes[index]) { | 1034 | if (data->nodes[index]) |
| 1039 | ret = -EBUSY; | 1035 | return ERR_PTR(-EBUSY); |
| 1040 | goto unlock; | ||
| 1041 | } | ||
| 1042 | 1036 | ||
| 1043 | node = io_rsrc_node_alloc(ctx, IORING_RSRC_BUFFER); | 1037 | node = io_rsrc_node_alloc(ctx, IORING_RSRC_BUFFER); |
| 1044 | if (!node) { | 1038 | if (!node) |
| 1045 | ret = -ENOMEM; | 1039 | return ERR_PTR(-ENOMEM); |
| 1046 | goto unlock; | ||
| 1047 | } | ||
| 1048 | 1040 | ||
| 1049 | /* | 1041 | imu = io_alloc_imu(ctx, nr_bvecs); |
| 1050 | * blk_rq_nr_phys_segments() may overestimate the number of bvecs | ||
| 1051 | * but avoids needing to iterate over the bvecs | ||
| 1052 | */ | ||
| 1053 | imu = io_alloc_imu(ctx, blk_rq_nr_phys_segments(rq)); | ||
| 1054 | if (!imu) { | 1042 | if (!imu) { |
| 1055 | io_cache_free(&ctx->node_cache, node); | 1043 | io_cache_free(&ctx->node_cache, node); |
| 1056 | ret = -ENOMEM; | 1044 | return ERR_PTR(-ENOMEM); |
| 1057 | goto unlock; | ||
| 1058 | } | 1045 | } |
| 1059 | 1046 | ||
| 1060 | imu->ubuf = 0; | 1047 | imu->ubuf = 0; |
| 1061 | imu->len = blk_rq_bytes(rq); | 1048 | imu->len = total_bytes; |
| 1062 | imu->folio_shift = PAGE_SHIFT; | 1049 | imu->folio_shift = PAGE_SHIFT; |
| 1050 | imu->nr_bvecs = nr_bvecs; | ||
| 1063 | refcount_set(&imu->refs, 1); | 1051 | refcount_set(&imu->refs, 1); |
| 1064 | imu->release = release; | 1052 | imu->release = release; |
| 1065 | imu->priv = rq; | 1053 | imu->priv = priv; |
| 1054 | imu->dir = dir; | ||
| 1066 | imu->flags = IO_REGBUF_F_KBUF; | 1055 | imu->flags = IO_REGBUF_F_KBUF; |
| 1067 | imu->dir = 1 << rq_data_dir(rq); | ||
| 1068 | 1056 | ||
| 1057 | node->buf = imu; | ||
| 1058 | data->nodes[index] = node; | ||
| 1059 | |||
| 1060 | return imu; | ||
| 1061 | } | ||
| 1062 | |||
| 1063 | int io_buffer_register_request(struct io_uring_cmd *cmd, struct request *rq, | ||
| 1064 | void (*release)(void *), unsigned int index, | ||
| 1065 | unsigned int issue_flags) | ||
| 1066 | { | ||
| 1067 | struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; | ||
| 1068 | struct req_iterator rq_iter; | ||
| 1069 | struct io_mapped_ubuf *imu; | ||
| 1070 | struct bio_vec bv; | ||
| 1071 | /* | ||
| 1072 | * blk_rq_nr_phys_segments() may overestimate the number of bvecs | ||
| 1073 | * but avoids needing to iterate over the bvecs | ||
| 1074 | */ | ||
| 1075 | unsigned int nr_bvecs = blk_rq_nr_phys_segments(rq); | ||
| 1076 | unsigned int total_bytes = blk_rq_bytes(rq); | ||
| 1077 | int ret = 0; | ||
| 1078 | |||
| 1079 | io_ring_submit_lock(ctx, issue_flags); | ||
| 1080 | |||
| 1081 | imu = io_kernel_buffer_init(ctx, nr_bvecs, total_bytes, | ||
| 1082 | 1 << rq_data_dir(rq), release, rq, index); | ||
| 1083 | if (IS_ERR(imu)) { | ||
| 1084 | ret = PTR_ERR(imu); | ||
| 1085 | goto unlock; | ||
| 1086 | } | ||
| 1087 | |||
| 1088 | nr_bvecs = 0; | ||
| 1069 | rq_for_each_bvec(bv, rq, rq_iter) | 1089 | rq_for_each_bvec(bv, rq, rq_iter) |
| 1070 | imu->bvec[nr_bvecs++] = bv; | 1090 | imu->bvec[nr_bvecs++] = bv; |
| 1071 | imu->nr_bvecs = nr_bvecs; | 1091 | imu->nr_bvecs = nr_bvecs; |
| 1072 | 1092 | ||
| 1073 | node->buf = imu; | 1093 | unlock: |
| 1074 | data->nodes[index] = node; | 1094 | io_ring_submit_unlock(ctx, issue_flags); |
| 1095 | return ret; | ||
| 1096 | } | ||
| 1097 | EXPORT_SYMBOL_GPL(io_buffer_register_request); | ||
| 1098 | |||
| 1099 | /* | ||
| 1100 | * bvs is copied internally. caller may free it on return. | ||
| 1101 | */ | ||
| 1102 | int io_buffer_register_bvec(struct io_uring_cmd *cmd, const struct bio_vec *bvs, | ||
| 1103 | unsigned int nr_bvecs, void (*release)(void *), | ||
| 1104 | void *priv, u8 dir, unsigned int index, | ||
| 1105 | unsigned int issue_flags) | ||
| 1106 | { | ||
| 1107 | struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; | ||
| 1108 | struct io_mapped_ubuf *imu; | ||
| 1109 | struct bio_vec *bvec; | ||
| 1110 | unsigned int i, total_bytes = 0; | ||
| 1111 | int ret = 0; | ||
| 1112 | |||
| 1113 | for (i = 0; i < nr_bvecs; i++) | ||
| 1114 | total_bytes += bvs[i].bv_len; | ||
| 1115 | |||
| 1116 | io_ring_submit_lock(ctx, issue_flags); | ||
| 1117 | imu = io_kernel_buffer_init(ctx, nr_bvecs, total_bytes, dir, release, | ||
| 1118 | priv, index); | ||
| 1119 | if (IS_ERR(imu)) { | ||
| 1120 | ret = PTR_ERR(imu); | ||
| 1121 | goto unlock; | ||
| 1122 | } | ||
| 1123 | |||
| 1124 | bvec = imu->bvec; | ||
| 1125 | for (i = 0; i < nr_bvecs; i++) | ||
| 1126 | bvec[i] = bvs[i]; | ||
| 1127 | |||
| 1075 | unlock: | 1128 | unlock: |
| 1076 | io_ring_submit_unlock(ctx, issue_flags); | 1129 | io_ring_submit_unlock(ctx, issue_flags); |
| 1077 | return ret; | 1130 | return ret; |
| 1078 | } | 1131 | } |
| 1079 | EXPORT_SYMBOL_GPL(io_buffer_register_bvec); | 1132 | EXPORT_SYMBOL_GPL(io_buffer_register_bvec); |
| 1080 | 1133 | ||
| 1081 | int io_buffer_unregister_bvec(struct io_uring_cmd *cmd, unsigned int index, | 1134 | int io_buffer_unregister(struct io_uring_cmd *cmd, unsigned int index, |
| 1082 | unsigned int issue_flags) | 1135 | unsigned int issue_flags) |
| 1083 | { | 1136 | { |
| 1084 | struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; | 1137 | struct io_ring_ctx *ctx = cmd_to_io_kiocb(cmd)->ctx; |
| 1085 | struct io_rsrc_data *data = &ctx->buf_table; | 1138 | struct io_rsrc_data *data = &ctx->buf_table; |
| @@ -1109,7 +1162,7 @@ unlock: · 23 unmodified lines | |||
| 1109 | io_ring_submit_unlock(ctx, issue_flags); | 1162 | io_ring_submit_unlock(ctx, issue_flags); |
| 1110 | return ret; | 1163 | return ret; |
| 1111 | } | 1164 | } |
| 1112 | EXPORT_SYMBOL_GPL(io_buffer_unregister_bvec); | 1165 | EXPORT_SYMBOL_GPL(io_buffer_unregister); |
| 1113 | 1166 | ||
| 1114 | static int validate_fixed_range(u64 buf_addr, size_t len, | 1167 | static int validate_fixed_range(u64 buf_addr, size_t len, |
| 1115 | const struct io_mapped_ubuf *imu) | 1168 | const struct io_mapped_ubuf *imu) |
| @@ -25,11 +25,6 @@ struct io_rsrc_node { · 24 unmodified lines | |||
| 25 | }; | 25 | }; |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | enum { | ||
| 29 | IO_IMU_DEST = 1 << ITER_DEST, | ||
| 30 | IO_IMU_SOURCE = 1 << ITER_SOURCE, | ||
| 31 | }; | ||
| 32 | |||
| 33 | enum { | 28 | enum { |
| 34 | IO_REGBUF_F_KBUF = 1, | 29 | IO_REGBUF_F_KBUF = 1, |
| 35 | }; | 30 | }; |
| @@ -1,3 +1,4 @@ | |||
| 1 | # SPDX-License-Identifier: GPL-2.0-only | 1 | # SPDX-License-Identifier: GPL-2.0-only |
| 2 | fuse_mnt | 2 | fuse_mnt |
| 3 | fusectl_test | 3 | fusectl_test |
| 4 | write_extend_eof_test | ||
| @@ -3,6 +3,7 @@ · 2 unmodified lines | |||
| 3 | CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) | 3 | CFLAGS += -Wall -O2 -g $(KHDR_INCLUDES) |
| 4 | 4 | ||
| 5 | TEST_GEN_PROGS := fusectl_test | 5 | TEST_GEN_PROGS := fusectl_test |
| 6 | TEST_GEN_PROGS += write_extend_eof_test | ||
| 6 | TEST_GEN_FILES := fuse_mnt | 7 | TEST_GEN_FILES := fuse_mnt |
| 7 | 8 | ||
| 8 | # fuse_acl_cache_test requires libfuse3; add it only when the library is present. | 9 | # fuse_acl_cache_test requires libfuse3; add it only when the library is present. |
| @@ -14,6 +15,8 @@ endif · 5 unmodified lines | |||
| 14 | 15 | ||
| 15 | include ../../lib.mk | 16 | include ../../lib.mk |
| 16 | 17 | ||
| 18 | $(OUTPUT)/write_extend_eof_test: LDLIBS += -lpthread | ||
| 19 | |||
| 17 | VAR_CFLAGS := $(shell pkg-config fuse --cflags 2>/dev/null) | 20 | VAR_CFLAGS := $(shell pkg-config fuse --cflags 2>/dev/null) |
| 18 | ifeq ($(VAR_CFLAGS),) | 21 | ifeq ($(VAR_CFLAGS),) |
| 19 | VAR_CFLAGS := -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse | 22 | VAR_CFLAGS := -D_FILE_OFFSET_BITS=64 -I/usr/include/fuse |
| @@ -0,0 +1,368 @@ | |||
| 1 | // SPDX-License-Identifier: GPL-2.0 | ||
| 2 | /* | ||
| 3 | * Regression test for the fuse write-extend partial-EOF-page zeroing bug. | ||
| 4 | * | ||
| 5 | * A buffered write that extends i_size past a non-page-aligned EOF must zero | ||
| 6 | * the tail of the old last page. If an application has mmap'd that page and | ||
| 7 | * stored into the post-EOF region (undefined until the file grows), the | ||
| 8 | * now-in-bounds tail must read back as zero, not as the stale stored bytes. | ||
| 9 | * | ||
| 10 | * The bug is exposed on a non-writeback_cache server that keeps the page cache | ||
| 11 | * across the write (FOPEN_KEEP_CACHE without FOPEN_DIRECT_IO). This test is a | ||
| 12 | * raw /dev/fuse server in that mode; the backing data is always zero in the | ||
| 13 | * hole, so any non-zero byte a read sees is stale page-cache data. | ||
| 14 | * | ||
| 15 | * Requires root to mount fuse. | ||
| 16 | */ | ||
| 17 | #define _GNU_SOURCE | ||
| 18 | #include <errno.h> | ||
| 19 | #include <fcntl.h> | ||
| 20 | #include <linux/falloc.h> | ||
| 21 | #include <pthread.h> | ||
| 22 | #include <stdint.h> | ||
| 23 | #include <string.h> | ||
| 24 | #include <unistd.h> | ||
| 25 | #include <sys/mman.h> | ||
| 26 | #include <sys/mount.h> | ||
| 27 | #include <sys/stat.h> | ||
| 28 | #include <sys/uio.h> | ||
| 29 | #include <linux/fuse.h> | ||
| 30 | |||
| 31 | #include "../../kselftest_harness.h" | ||
| 32 | |||
| 33 | #define FUSE_ROOT_ID 1 | ||
| 34 | #define FILE_INO 2 | ||
| 35 | #define MAX_WRITE (128 * 1024) | ||
| 36 | #define BACKING_SIZE (4 * 1024 * 1024) | ||
| 37 | #define POLLUTE 0xee | ||
| 38 | |||
| 39 | /* Server-side state, shared with the responder thread. */ | ||
| 40 | struct server { | ||
| 41 | int fd; | ||
| 42 | unsigned char backing[BACKING_SIZE]; /* authoritative bytes */ | ||
| 43 | uint64_t size; | ||
| 44 | }; | ||
| 45 | |||
| 46 | static void reply(int fd, uint64_t unique, int error, void *data, size_t len) | ||
| 47 | { | ||
| 48 | struct fuse_out_header oh = { | ||
| 49 | .len = sizeof(oh) + (data ? len : 0), | ||
| 50 | .error = error, | ||
| 51 | .unique = unique, | ||
| 52 | }; | ||
| 53 | struct iovec iov[2] = { { &oh, sizeof(oh) }, { data, len } }; | ||
| 54 | |||
| 55 | /* Errors here are teardown races (device closed on unmount); ignore. */ | ||
| 56 | if (writev(fd, iov, data ? 2 : 1) < 0) | ||
| 57 | return; | ||
| 58 | } | ||
| 59 | |||
| 60 | static void fill_attr(struct fuse_attr *a, uint64_t ino, uint32_t mode, | ||
| 61 | uint64_t size) | ||
| 62 | { | ||
| 63 | memset(a, 0, sizeof(*a)); | ||
| 64 | a->ino = ino; | ||
| 65 | a->mode = mode; | ||
| 66 | a->nlink = 1; | ||
| 67 | a->size = size; | ||
| 68 | a->blksize = sysconf(_SC_PAGESIZE); | ||
| 69 | } | ||
| 70 | |||
| 71 | static void *server_thread(void *arg) | ||
| 72 | { | ||
| 73 | struct server *s = arg; | ||
| 74 | static char buf[MAX_WRITE + 4096]; | ||
| 75 | |||
| 76 | for (;;) { | ||
| 77 | ssize_t n = read(s->fd, buf, sizeof(buf)); | ||
| 78 | struct fuse_in_header *ih = (void *)buf; | ||
| 79 | |||
| 80 | if (n < 0) { | ||
| 81 | if (errno == EINTR || errno == EAGAIN) | ||
| 82 | continue; | ||
| 83 | return NULL; /* device closed on unmount */ | ||
| 84 | } | ||
| 85 | if (n < (ssize_t)sizeof(*ih)) | ||
| 86 | continue; | ||
| 87 | |||
| 88 | switch (ih->opcode) { | ||
| 89 | case FUSE_INIT: { | ||
| 90 | struct fuse_init_in *in = (void *)(ih + 1); | ||
| 91 | struct fuse_init_out out = {0}; | ||
| 92 | |||
| 93 | /* No FUSE_WRITEBACK_CACHE: the exposed configuration. */ | ||
| 94 | out.major = FUSE_KERNEL_VERSION; | ||
| 95 | out.minor = FUSE_KERNEL_MINOR_VERSION; | ||
| 96 | out.max_readahead = in->max_readahead; | ||
| 97 | out.max_write = MAX_WRITE; | ||
| 98 | out.max_background = 16; | ||
| 99 | out.congestion_threshold = 12; | ||
| 100 | out.flags = FUSE_MAX_PAGES; | ||
| 101 | out.max_pages = MAX_WRITE / sysconf(_SC_PAGESIZE); | ||
| 102 | reply(s->fd, ih->unique, 0, &out, sizeof(out)); | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | case FUSE_GETATTR: { | ||
| 106 | struct fuse_attr_out out = {0}; | ||
| 107 | int root = ih->nodeid == FUSE_ROOT_ID; | ||
| 108 | |||
| 109 | out.attr_valid = 3600; | ||
| 110 | fill_attr(&out.attr, ih->nodeid, | ||
| 111 | root ? (S_IFDIR | 0755) : (S_IFREG | 0644), | ||
| 112 | root ? 0 : s->size); | ||
| 113 | reply(s->fd, ih->unique, 0, &out, sizeof(out)); | ||
| 114 | break; | ||
| 115 | } | ||
| 116 | case FUSE_LOOKUP: { | ||
| 117 | struct fuse_entry_out out = {0}; | ||
| 118 | |||
| 119 | out.nodeid = FILE_INO; | ||
| 120 | out.attr_valid = 3600; | ||
| 121 | out.entry_valid = 3600; | ||
| 122 | fill_attr(&out.attr, FILE_INO, S_IFREG | 0644, s->size); | ||
| 123 | reply(s->fd, ih->unique, 0, &out, sizeof(out)); | ||
| 124 | break; | ||
| 125 | } | ||
| 126 | case FUSE_OPEN: | ||
| 127 | case FUSE_OPENDIR: { | ||
| 128 | struct fuse_open_out out = {0}; | ||
| 129 | |||
| 130 | /* Keep the cache across the write, but not direct I/O. */ | ||
| 131 | out.open_flags = FOPEN_KEEP_CACHE; | ||
| 132 | reply(s->fd, ih->unique, 0, &out, sizeof(out)); | ||
| 133 | break; | ||
| 134 | } | ||
| 135 | case FUSE_READ: { | ||
| 136 | struct fuse_read_in *in = (void *)(ih + 1); | ||
| 137 | uint64_t off = in->offset; | ||
| 138 | uint32_t size = in->size; | ||
| 139 | |||
| 140 | if (off >= BACKING_SIZE) | ||
| 141 | size = 0; | ||
| 142 | else if (off + size > BACKING_SIZE) | ||
| 143 | size = BACKING_SIZE - off; | ||
| 144 | reply(s->fd, ih->unique, 0, s->backing + off, size); | ||
| 145 | break; | ||
| 146 | } | ||
| 147 | case FUSE_WRITE: { | ||
| 148 | struct fuse_write_in *in = (void *)(ih + 1); | ||
| 149 | struct fuse_write_out out = {0}; | ||
| 150 | uint64_t off = in->offset; | ||
| 151 | uint32_t size = in->size; | ||
| 152 | |||
| 153 | if (off < BACKING_SIZE) { | ||
| 154 | uint32_t c = size; | ||
| 155 | |||
| 156 | if (off + c > BACKING_SIZE) | ||
| 157 | c = BACKING_SIZE - off; | ||
| 158 | memcpy(s->backing + off, in + 1, c); | ||
| 159 | if (off + c > s->size) | ||
| 160 | s->size = off + c; | ||
| 161 | } | ||
| 162 | out.size = size; | ||
| 163 | reply(s->fd, ih->unique, 0, &out, sizeof(out)); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | case FUSE_SETATTR: { | ||
| 167 | struct fuse_setattr_in *in = (void *)(ih + 1); | ||
| 168 | struct fuse_attr_out out = {0}; | ||
| 169 | |||
| 170 | if ((in->valid & FATTR_SIZE) && in->size <= BACKING_SIZE) { | ||
| 171 | if (in->size > s->size) | ||
| 172 | memset(s->backing + s->size, 0, | ||
| 173 | in->size - s->size); | ||
| 174 | s->size = in->size; | ||
| 175 | } | ||
| 176 | out.attr_valid = 3600; | ||
| 177 | fill_attr(&out.attr, ih->nodeid, S_IFREG | 0644, s->size); | ||
| 178 | reply(s->fd, ih->unique, 0, &out, sizeof(out)); | ||
| 179 | break; | ||
| 180 | } | ||
| 181 | case FUSE_FALLOCATE: { | ||
| 182 | struct fuse_fallocate_in *in = (void *)(ih + 1); | ||
| 183 | uint64_t end = in->offset + in->length; | ||
| 184 | |||
| 185 | /* Only plain (size-extending) fallocate is used here. */ | ||
| 186 | if (!(in->mode & FALLOC_FL_KEEP_SIZE) && | ||
| 187 | end <= BACKING_SIZE && end > s->size) { | ||
| 188 | memset(s->backing + s->size, 0, end - s->size); | ||
| 189 | s->size = end; | ||
| 190 | } | ||
| 191 | reply(s->fd, ih->unique, 0, NULL, 0); | ||
| 192 | break; | ||
| 193 | } | ||
| 194 | case FUSE_FLUSH: | ||
| 195 | case FUSE_RELEASE: | ||
| 196 | case FUSE_RELEASEDIR: | ||
| 197 | case FUSE_FSYNC: | ||
| 198 | case FUSE_ACCESS: | ||
| 199 | reply(s->fd, ih->unique, 0, NULL, 0); | ||
| 200 | break; | ||
| 201 | case FUSE_FORGET: | ||
| 202 | break; | ||
| 203 | default: | ||
| 204 | reply(s->fd, ih->unique, -EOPNOTSUPP, NULL, 0); | ||
| 205 | break; | ||
| 206 | } | ||
| 207 | } | ||
| 208 | } | ||
| 209 | |||
| 210 | FIXTURE(fuse) | ||
| 211 | { | ||
| 212 | struct server *srv; | ||
| 213 | pthread_t thread; | ||
| 214 | char dir[64]; | ||
| 215 | long page; /* runtime page size */ | ||
| 216 | off_t eof; /* mid-page EOF, page-relative */ | ||
| 217 | int fd; /* open test file */ | ||
| 218 | char *map; /* mmap of the EOF page */ | ||
| 219 | int mounted; | ||
| 220 | }; | ||
| 221 | |||
| 222 | FIXTURE_SETUP(fuse) | ||
| 223 | { | ||
| 224 | char opts[128]; | ||
| 225 | pthread_t t; | ||
| 226 | |||
| 227 | if (geteuid() != 0) | ||
| 228 | SKIP(return, "need root to mount fuse"); | ||
| 229 | |||
| 230 | self->page = sysconf(_SC_PAGESIZE); | ||
| 231 | self->fd = -1; | ||
| 232 | self->map = MAP_FAILED; | ||
| 233 | |||
| 234 | self->srv = mmap(NULL, sizeof(*self->srv), PROT_READ | PROT_WRITE, | ||
| 235 | MAP_SHARED | MAP_ANONYMOUS, -1, 0); | ||
| 236 | ASSERT_NE(MAP_FAILED, self->srv); | ||
| 237 | |||
| 238 | self->srv->fd = open("/dev/fuse", O_RDWR); | ||
| 239 | ASSERT_GE(self->srv->fd, 0); | ||
| 240 | |||
| 241 | strcpy(self->dir, "/tmp/fuse_weof_XXXXXX"); | ||
| 242 | ASSERT_NE(NULL, mkdtemp(self->dir)); | ||
| 243 | |||
| 244 | snprintf(opts, sizeof(opts), | ||
| 245 | "fd=%d,rootmode=40000,user_id=0,group_id=0", | ||
| 246 | self->srv->fd); | ||
| 247 | ASSERT_EQ(0, mount("fuse", self->dir, "fuse", 0, opts)); | ||
| 248 | self->mounted = 1; | ||
| 249 | |||
| 250 | ASSERT_EQ(0, pthread_create(&t, NULL, server_thread, self->srv)); | ||
| 251 | self->thread = t; | ||
| 252 | } | ||
| 253 | |||
| 254 | FIXTURE_TEARDOWN(fuse) | ||
| 255 | { | ||
| 256 | if (self->map != MAP_FAILED) | ||
| 257 | munmap(self->map, self->page); | ||
| 258 | if (self->fd >= 0) | ||
| 259 | close(self->fd); | ||
| 260 | if (self->mounted) | ||
| 261 | umount2(self->dir, MNT_DETACH); | ||
| 262 | if (self->srv && self->srv != MAP_FAILED) { | ||
| 263 | if (self->srv->fd > 0) | ||
| 264 | close(self->srv->fd); | ||
| 265 | munmap(self->srv, sizeof(*self->srv)); | ||
| 266 | } | ||
| 267 | if (self->dir[0]) | ||
| 268 | rmdir(self->dir); | ||
| 269 | } | ||
| 270 | |||
| 271 | /* | ||
| 272 | * Create the test file with a mid-page EOF and mmap-store POLLUTE into its | ||
| 273 | * post-EOF tail (a legal store, undefined until the file grows). Leaves the | ||
| 274 | * file open and the EOF page mapped in the fixture for the caller to extend. | ||
| 275 | */ | ||
| 276 | static void pollute_eof_tail(struct __test_metadata *_metadata, | ||
| 277 | FIXTURE_DATA(fuse) * self) | ||
| 278 | { | ||
| 279 | off_t eof = 2 * self->page + self->page / 4; | ||
| 280 | char path[128]; | ||
| 281 | char *buf; | ||
| 282 | |||
| 283 | snprintf(path, sizeof(path), "%s/file", self->dir); | ||
| 284 | self->fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); | ||
| 285 | ASSERT_GE(self->fd, 0); | ||
| 286 | self->eof = eof; | ||
| 287 | |||
| 288 | buf = malloc(eof); | ||
| 289 | ASSERT_NE(NULL, buf); | ||
| 290 | memset(buf, 'A', eof); | ||
| 291 | ASSERT_EQ(eof, pwrite(self->fd, buf, eof, 0)); | ||
| 292 | free(buf); | ||
| 293 | |||
| 294 | self->map = mmap(NULL, self->page, PROT_READ | PROT_WRITE, MAP_SHARED, | ||
| 295 | self->fd, eof & ~(self->page - 1)); | ||
| 296 | ASSERT_NE(MAP_FAILED, self->map); | ||
| 297 | memset(self->map + (eof & (self->page - 1)), POLLUTE, | ||
| 298 | self->page - (eof & (self->page - 1))); | ||
| 299 | } | ||
| 300 | |||
| 301 | /* Assert the old post-EOF tail [eof, end of its page) now reads back as zero. */ | ||
| 302 | static void assert_tail_zeroed(struct __test_metadata *_metadata, | ||
| 303 | FIXTURE_DATA(fuse) * self) | ||
| 304 | { | ||
| 305 | off_t base = self->eof & ~(self->page - 1); | ||
| 306 | char *tail = malloc(self->page); | ||
| 307 | int i; | ||
| 308 | |||
| 309 | ASSERT_NE(NULL, tail); | ||
| 310 | ASSERT_EQ(self->page, pread(self->fd, tail, self->page, base)); | ||
| 311 | for (i = self->eof & (self->page - 1); i < self->page; i++) | ||
| 312 | ASSERT_EQ(0, tail[i]); | ||
| 313 | free(tail); | ||
| 314 | } | ||
| 315 | |||
| 316 | /* Basic: pollute the post-EOF tail, extend past it by a later write. */ | ||
| 317 | TEST_F(fuse, write_extend) | ||
| 318 | { | ||
| 319 | pollute_eof_tail(_metadata, self); | ||
| 320 | ASSERT_EQ(4, pwrite(self->fd, "data", 4, 5 * self->page + self->page / 3)); | ||
| 321 | assert_tail_zeroed(_metadata, self); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* Extend via ftruncate() rather than a write. */ | ||
| 325 | TEST_F(fuse, ftruncate_extend) | ||
| 326 | { | ||
| 327 | pollute_eof_tail(_metadata, self); | ||
| 328 | ASSERT_EQ(0, ftruncate(self->fd, 8 * self->page)); | ||
| 329 | assert_tail_zeroed(_metadata, self); | ||
| 330 | } | ||
| 331 | |||
| 332 | /* Extend via fallocate() starting at the old EOF. */ | ||
| 333 | TEST_F(fuse, fallocate_extend) | ||
| 334 | { | ||
| 335 | pollute_eof_tail(_metadata, self); | ||
| 336 | ASSERT_EQ(0, fallocate(self->fd, 0, self->eof, 4 * self->page)); | ||
| 337 | assert_tail_zeroed(_metadata, self); | ||
| 338 | } | ||
| 339 | |||
| 340 | /* A write landing inside the old EOF page must not clobber its own data. */ | ||
| 341 | TEST_F(fuse, extend_into_eof_page_preserves_data) | ||
| 342 | { | ||
| 343 | off_t base, wr; | ||
| 344 | char *buf, *rd; | ||
| 345 | int i; | ||
| 346 | |||
| 347 | pollute_eof_tail(_metadata, self); | ||
| 348 | base = self->eof & ~(self->page - 1); | ||
| 349 | wr = base + 3 * self->page / 4; /* starts in the EOF page */ | ||
| 350 | |||
| 351 | buf = malloc(2 * self->page); | ||
| 352 | ASSERT_NE(NULL, buf); | ||
| 353 | memset(buf, 'B', 2 * self->page); | ||
| 354 | ASSERT_EQ(2 * self->page, pwrite(self->fd, buf, 2 * self->page, wr)); | ||
| 355 | free(buf); | ||
| 356 | |||
| 357 | rd = malloc(self->page); | ||
| 358 | ASSERT_NE(NULL, rd); | ||
| 359 | ASSERT_EQ(self->page, pread(self->fd, rd, self->page, base)); | ||
| 360 | /* [eof, wr) is hole -> zero; [wr, page) is written data -> 'B'. */ | ||
| 361 | for (i = self->eof & (self->page - 1); i < wr - base; i++) | ||
| 362 | ASSERT_EQ(0, rd[i]); | ||
| 363 | for (i = wr - base; i < self->page; i++) | ||
| 364 | ASSERT_EQ('B', rd[i]); | ||
| 365 | free(rd); | ||
| 366 | } | ||
| 367 | |||
| 368 | TEST_HARNESS_MAIN | ||