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