Merge tag 'io_uring-6.19-20260122' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux

Pull io_uring fixes from Jens Axboe:

- Fix for a potential leak of an iovec, if a specific cleanup path is
used and the rw_cache is full at the time of the call

- Fix for a regression added in this cycle, where waitid should be
using prober release/acquire semantics for updating the wait queue
head

- Check for the cancelation bit being set for every work item processed
by io-wq, not just at the start of the loop. Has no real practical
implications other than to shut up syzbot doing crazy things that
grossly overload a system, hence slowing down ring exit

- A few selftest additions, updating the mini_liburing that selftests
use

* tag 'io_uring-6.19-20260122' of git://git.kernel.org/pub/scm/linux/kernel/git/axboe/linux:
selftests/io_uring: support NO_SQARRAY in miniliburing
selftests/io_uring: add io_uring_queue_init_params
io_uring/io-wq: check IO_WQ_BIT_EXIT inside work run loop
io_uring/waitid: fix KCSAN warning on io_waitid->head
io_uring/rw: free potentially allocated iovec on cache put failure

+60 -24
+1 -1
io_uring/io-wq.c
··· 598 598 __releases(&acct->lock) 599 599 { 600 600 struct io_wq *wq = worker->wq; 601 - bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); 602 601 603 602 do { 603 + bool do_kill = test_bit(IO_WQ_BIT_EXIT, &wq->state); 604 604 struct io_wq_work *work; 605 605 606 606 /*
+11 -4
io_uring/rw.c
··· 144 144 return 0; 145 145 } 146 146 147 - static void io_rw_recycle(struct io_kiocb *req, unsigned int issue_flags) 147 + static bool io_rw_recycle(struct io_kiocb *req, unsigned int issue_flags) 148 148 { 149 149 struct io_async_rw *rw = req->async_data; 150 150 151 151 if (unlikely(issue_flags & IO_URING_F_UNLOCKED)) 152 - return; 152 + return false; 153 153 154 154 io_alloc_cache_vec_kasan(&rw->vec); 155 155 if (rw->vec.nr > IO_VEC_CACHE_SOFT_CAP) 156 156 io_vec_free(&rw->vec); 157 157 158 - if (io_alloc_cache_put(&req->ctx->rw_cache, rw)) 158 + if (io_alloc_cache_put(&req->ctx->rw_cache, rw)) { 159 159 io_req_async_data_clear(req, 0); 160 + return true; 161 + } 162 + return false; 160 163 } 161 164 162 165 static void io_req_rw_cleanup(struct io_kiocb *req, unsigned int issue_flags) ··· 193 190 */ 194 191 if (!(req->flags & (REQ_F_REISSUE | REQ_F_REFCOUNT))) { 195 192 req->flags &= ~REQ_F_NEED_CLEANUP; 196 - io_rw_recycle(req, issue_flags); 193 + if (!io_rw_recycle(req, issue_flags)) { 194 + struct io_async_rw *rw = req->async_data; 195 + 196 + io_vec_free(&rw->vec); 197 + } 197 198 } 198 199 } 199 200
+3 -3
io_uring/waitid.c
··· 114 114 struct io_waitid *iw = io_kiocb_to_cmd(req, struct io_waitid); 115 115 struct wait_queue_head *head; 116 116 117 - head = READ_ONCE(iw->head); 117 + head = smp_load_acquire(&iw->head); 118 118 if (head) { 119 119 struct io_waitid_async *iwa = req->async_data; 120 120 121 - iw->head = NULL; 121 + smp_store_release(&iw->head, NULL); 122 122 spin_lock_irq(&head->lock); 123 123 list_del_init(&iwa->wo.child_wait.entry); 124 124 spin_unlock_irq(&head->lock); ··· 246 246 return 0; 247 247 248 248 list_del_init(&wait->entry); 249 - iw->head = NULL; 249 + smp_store_release(&iw->head, NULL); 250 250 251 251 /* cancel is in progress */ 252 252 if (atomic_fetch_inc(&iw->refs) & IO_WAITID_REF_MASK)
+45 -16
tools/include/io_uring/mini_liburing.h
··· 6 6 #include <stdio.h> 7 7 #include <string.h> 8 8 #include <unistd.h> 9 + #include <sys/uio.h> 9 10 10 11 struct io_sq_ring { 11 12 unsigned int *head; ··· 56 55 struct io_uring_sq sq; 57 56 struct io_uring_cq cq; 58 57 int ring_fd; 58 + unsigned flags; 59 59 }; 60 60 61 61 #if defined(__x86_64) || defined(__i386__) ··· 74 72 void *ptr; 75 73 int ret; 76 74 77 - sq->ring_sz = p->sq_off.array + p->sq_entries * sizeof(unsigned int); 75 + if (p->flags & IORING_SETUP_NO_SQARRAY) { 76 + sq->ring_sz = p->cq_off.cqes; 77 + sq->ring_sz += p->cq_entries * sizeof(struct io_uring_cqe); 78 + } else { 79 + sq->ring_sz = p->sq_off.array; 80 + sq->ring_sz += p->sq_entries * sizeof(unsigned int); 81 + } 82 + 78 83 ptr = mmap(0, sq->ring_sz, PROT_READ | PROT_WRITE, 79 84 MAP_SHARED | MAP_POPULATE, fd, IORING_OFF_SQ_RING); 80 85 if (ptr == MAP_FAILED) ··· 92 83 sq->kring_entries = ptr + p->sq_off.ring_entries; 93 84 sq->kflags = ptr + p->sq_off.flags; 94 85 sq->kdropped = ptr + p->sq_off.dropped; 95 - sq->array = ptr + p->sq_off.array; 86 + if (!(p->flags & IORING_SETUP_NO_SQARRAY)) 87 + sq->array = ptr + p->sq_off.array; 96 88 97 89 size = p->sq_entries * sizeof(struct io_uring_sqe); 98 90 sq->sqes = mmap(0, size, PROT_READ | PROT_WRITE, ··· 136 126 flags, sig, _NSIG / 8); 137 127 } 138 128 129 + static inline int io_uring_queue_init_params(unsigned int entries, 130 + struct io_uring *ring, 131 + struct io_uring_params *p) 132 + { 133 + int fd, ret; 134 + 135 + memset(ring, 0, sizeof(*ring)); 136 + 137 + fd = io_uring_setup(entries, p); 138 + if (fd < 0) 139 + return fd; 140 + ret = io_uring_mmap(fd, p, &ring->sq, &ring->cq); 141 + if (!ret) { 142 + ring->ring_fd = fd; 143 + ring->flags = p->flags; 144 + } else { 145 + close(fd); 146 + } 147 + return ret; 148 + } 149 + 139 150 static inline int io_uring_queue_init(unsigned int entries, 140 151 struct io_uring *ring, 141 152 unsigned int flags) 142 153 { 143 154 struct io_uring_params p; 144 - int fd, ret; 145 155 146 - memset(ring, 0, sizeof(*ring)); 147 156 memset(&p, 0, sizeof(p)); 148 157 p.flags = flags; 149 158 150 - fd = io_uring_setup(entries, &p); 151 - if (fd < 0) 152 - return fd; 153 - ret = io_uring_mmap(fd, &p, &ring->sq, &ring->cq); 154 - if (!ret) 155 - ring->ring_fd = fd; 156 - else 157 - close(fd); 158 - return ret; 159 + return io_uring_queue_init_params(entries, ring, &p); 159 160 } 160 161 161 162 /* Get a sqe */ ··· 220 199 221 200 ktail = *sq->ktail; 222 201 to_submit = sq->sqe_tail - sq->sqe_head; 223 - for (submitted = 0; submitted < to_submit; submitted++) { 224 - read_barrier(); 225 - sq->array[ktail++ & mask] = sq->sqe_head++ & mask; 202 + 203 + if (!(ring->flags & IORING_SETUP_NO_SQARRAY)) { 204 + for (submitted = 0; submitted < to_submit; submitted++) { 205 + read_barrier(); 206 + sq->array[ktail++ & mask] = sq->sqe_head++ & mask; 207 + } 208 + } else { 209 + ktail += to_submit; 210 + sq->sqe_head += to_submit; 211 + submitted = to_submit; 226 212 } 213 + 227 214 if (!submitted) 228 215 return 0; 229 216