Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

selftests: ublk: handle UBLK_U_IO_COMMIT_IO_CMDS

Implement UBLK_U_IO_COMMIT_IO_CMDS to enable efficient batched
completion of I/O operations in the batch I/O framework.

This completes the batch I/O infrastructure by adding the commit
phase that notifies the kernel about completed I/O operations:

Key features:
- Batch multiple I/O completions into single UBLK_U_IO_COMMIT_IO_CMDS
- Dynamic commit buffer allocation and management per thread
- Automatic commit buffer preparation before processing events
- Commit buffer submission after processing completed I/Os
- Integration with existing completion workflows

Implementation details:
- ublk_batch_prep_commit() allocates and initializes commit buffers
- ublk_batch_complete_io() adds completed I/Os to current batch
- ublk_batch_commit_io_cmds() submits batched completions to kernel
- Modified ublk_process_io() to handle batch commit lifecycle
- Enhanced ublk_complete_io() to route to batch or legacy completion

The commit buffer stores completion information (tag, result, buffer
details) for multiple I/Os, then submits them all at once, significantly
reducing syscall overhead compared to individual I/O completions.

Signed-off-by: Ming Lei <ming.lei@redhat.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
dee7024f d468930a

+122 -29
+70 -4
tools/testing/selftests/ublk/batch.c
··· 174 174 cmd->elem_bytes = elem_bytes; 175 175 cmd->nr_elem = nr_elem; 176 176 177 - user_data = build_user_data(buf_idx, _IOC_NR(op), 0, q_id, 0); 177 + user_data = build_user_data(buf_idx, _IOC_NR(op), nr_elem, q_id, 0); 178 178 io_uring_sqe_set_data64(sqe, user_data); 179 179 180 180 t->cmd_inflight += 1; ··· 244 244 245 245 if (op == _IOC_NR(UBLK_U_IO_PREP_IO_CMDS)) 246 246 ublk_assert(cqe->res == 0); 247 - else if (op == _IOC_NR(UBLK_U_IO_COMMIT_IO_CMDS)) 248 - ;//assert(cqe->res == t->commit_buf_size); 249 - else 247 + else if (op == _IOC_NR(UBLK_U_IO_COMMIT_IO_CMDS)) { 248 + int nr_elem = user_data_to_tgt_data(cqe->user_data); 249 + 250 + ublk_assert(cqe->res == t->commit_buf_elem_size * nr_elem); 251 + } else 250 252 ublk_assert(0); 251 253 252 254 ublk_free_commit_buf(t, buf_idx); ··· 265 263 ublk_batch_compl_commit_cmd(t, cqe, op); 266 264 return; 267 265 } 266 + } 267 + 268 + void ublk_batch_commit_io_cmds(struct ublk_thread *t) 269 + { 270 + struct io_uring_sqe *sqe; 271 + unsigned short buf_idx; 272 + unsigned short nr_elem = t->commit.done; 273 + 274 + /* nothing to commit */ 275 + if (!nr_elem) { 276 + ublk_free_commit_buf(t, t->commit.buf_idx); 277 + return; 278 + } 279 + 280 + ublk_io_alloc_sqes(t, &sqe, 1); 281 + buf_idx = t->commit.buf_idx; 282 + sqe->addr = (__u64)t->commit.elem; 283 + sqe->len = nr_elem * t->commit_buf_elem_size; 284 + 285 + /* commit isn't per-queue command */ 286 + ublk_init_batch_cmd(t, t->commit.q_id, sqe, UBLK_U_IO_COMMIT_IO_CMDS, 287 + t->commit_buf_elem_size, nr_elem, buf_idx); 288 + ublk_setup_commit_sqe(t, sqe, buf_idx); 289 + } 290 + 291 + static void ublk_batch_init_commit(struct ublk_thread *t, 292 + unsigned short buf_idx) 293 + { 294 + /* so far only support 1:1 queue/thread mapping */ 295 + t->commit.q_id = t->idx; 296 + t->commit.buf_idx = buf_idx; 297 + t->commit.elem = ublk_get_commit_buf(t, buf_idx); 298 + t->commit.done = 0; 299 + t->commit.count = t->commit_buf_size / 300 + t->commit_buf_elem_size; 301 + } 302 + 303 + void ublk_batch_prep_commit(struct ublk_thread *t) 304 + { 305 + unsigned short buf_idx = ublk_alloc_commit_buf(t); 306 + 307 + ublk_assert(buf_idx != UBLKS_T_COMMIT_BUF_INV_IDX); 308 + ublk_batch_init_commit(t, buf_idx); 309 + } 310 + 311 + void ublk_batch_complete_io(struct ublk_thread *t, struct ublk_queue *q, 312 + unsigned tag, int res) 313 + { 314 + struct batch_commit_buf *cb = &t->commit; 315 + struct ublk_batch_elem *elem = (struct ublk_batch_elem *)(cb->elem + 316 + cb->done * t->commit_buf_elem_size); 317 + struct ublk_io *io = &q->ios[tag]; 318 + 319 + ublk_assert(q->q_id == t->commit.q_id); 320 + 321 + elem->tag = tag; 322 + elem->buf_index = ublk_batch_io_buf_idx(t, q, tag); 323 + elem->result = res; 324 + 325 + if (!ublk_queue_no_buf(q)) 326 + elem->buf_addr = (__u64) (uintptr_t) io->buf_addr; 327 + 328 + cb->done += 1; 329 + ublk_assert(cb->done <= cb->count); 268 330 }
+7 -1
tools/testing/selftests/ublk/kublk.c
··· 931 931 return -ENODEV; 932 932 933 933 ret = io_uring_submit_and_wait(&t->ring, 1); 934 - reapped = ublk_reap_events_uring(t); 934 + if (ublk_thread_batch_io(t)) { 935 + ublk_batch_prep_commit(t); 936 + reapped = ublk_reap_events_uring(t); 937 + ublk_batch_commit_io_cmds(t); 938 + } else { 939 + reapped = ublk_reap_events_uring(t); 940 + } 935 941 936 942 ublk_dbg(UBLK_DBG_THREAD, "submit result %d, reapped %d stop %d idle %d\n", 937 943 ret, reapped, (t->state & UBLKS_T_STOPPING),
+45 -24
tools/testing/selftests/ublk/kublk.h
··· 190 190 __u64 buf_addr; 191 191 }; 192 192 193 + struct batch_commit_buf { 194 + unsigned short q_id; 195 + unsigned short buf_idx; 196 + void *elem; 197 + unsigned short done; 198 + unsigned short count; 199 + }; 200 + 193 201 struct ublk_thread { 194 202 struct ublk_dev *dev; 195 203 unsigned idx; ··· 223 215 void *commit_buf; 224 216 #define UBLKS_T_COMMIT_BUF_INV_IDX ((unsigned short)-1) 225 217 struct allocator commit_buf_alloc; 218 + struct batch_commit_buf commit; 226 219 227 220 struct io_uring ring; 228 221 }; ··· 467 458 return &q->ios[tag]; 468 459 } 469 460 470 - static inline int ublk_complete_io(struct ublk_thread *t, struct ublk_queue *q, 471 - unsigned tag, int res) 472 - { 473 - struct ublk_io *io = &q->ios[tag]; 474 - 475 - ublk_mark_io_done(io, res); 476 - 477 - return ublk_queue_io_cmd(t, io); 478 - } 479 - 480 - static inline void ublk_queued_tgt_io(struct ublk_thread *t, struct ublk_queue *q, 481 - unsigned tag, int queued) 482 - { 483 - if (queued < 0) 484 - ublk_complete_io(t, q, tag, queued); 485 - else { 486 - struct ublk_io *io = ublk_get_io(q, tag); 487 - 488 - t->io_inflight += queued; 489 - io->tgt_ios = queued; 490 - io->result = 0; 491 - } 492 - } 493 - 494 461 static inline int ublk_completed_tgt_io(struct ublk_thread *t, 495 462 struct ublk_queue *q, unsigned tag) 496 463 { ··· 524 539 int ublk_batch_alloc_buf(struct ublk_thread *t); 525 540 /* Free commit buffers and cleanup batch allocator */ 526 541 void ublk_batch_free_buf(struct ublk_thread *t); 542 + 543 + /* Prepare a new commit buffer for batching completed I/O operations */ 544 + void ublk_batch_prep_commit(struct ublk_thread *t); 545 + /* Submit UBLK_U_IO_COMMIT_IO_CMDS with batched completed I/O operations */ 546 + void ublk_batch_commit_io_cmds(struct ublk_thread *t); 547 + /* Add a completed I/O operation to the current batch commit buffer */ 548 + void ublk_batch_complete_io(struct ublk_thread *t, struct ublk_queue *q, 549 + unsigned tag, int res); 550 + 551 + static inline int ublk_complete_io(struct ublk_thread *t, struct ublk_queue *q, 552 + unsigned tag, int res) 553 + { 554 + if (ublk_queue_batch_io(q)) { 555 + ublk_batch_complete_io(t, q, tag, res); 556 + return 0; 557 + } else { 558 + struct ublk_io *io = &q->ios[tag]; 559 + 560 + ublk_mark_io_done(io, res); 561 + return ublk_queue_io_cmd(t, io); 562 + } 563 + } 564 + 565 + static inline void ublk_queued_tgt_io(struct ublk_thread *t, struct ublk_queue *q, 566 + unsigned tag, int queued) 567 + { 568 + if (queued < 0) 569 + ublk_complete_io(t, q, tag, queued); 570 + else { 571 + struct ublk_io *io = ublk_get_io(q, tag); 572 + 573 + t->io_inflight += queued; 574 + io->tgt_ios = queued; 575 + io->result = 0; 576 + } 577 + } 527 578 528 579 extern const struct ublk_tgt_ops null_tgt_ops; 529 580 extern const struct ublk_tgt_ops loop_tgt_ops;