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.

ublk selftests: add --no_ublk_fixed_fd for not using registered ublk char device

Add a new command line option --no_ublk_fixed_fd that excludes the ublk
control device (/dev/ublkcN) from io_uring's registered files array.
When this option is used, only backing files are registered starting
from index 1, while the ublk control device is accessed using its raw
file descriptor.

Add ublk_get_registered_fd() helper function that returns the appropriate
file descriptor for use with io_uring operations.

Key optimizations implemented:
- Cache UBLKS_Q_NO_UBLK_FIXED_FD flag in ublk_queue.flags to avoid
reading dev->no_ublk_fixed_fd in fast path
- Cache ublk char device fd in ublk_queue.ublk_fd for fast access
- Update ublk_get_registered_fd() to use ublk_queue * parameter
- Update io_uring_prep_buf_register/unregister() to use ublk_queue *
- Replace ublk_device * access with ublk_queue * access in fast paths

Also pass --no_ublk_fixed_fd to test_stress_04.sh for covering
plain ublk char device mode.

Signed-off-by: Ming Lei <ming.lei@redhat.com>
Link: https://lore.kernel.org/r/20250827121602.2619736-3-ming.lei@redhat.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>

authored by

Ming Lei and committed by
Jens Axboe
9b2785ea c5c5eb24

+74 -33
+5 -5
tools/testing/selftests/ublk/file_backed.c
··· 20 20 struct io_uring_sqe *sqe[1]; 21 21 22 22 ublk_io_alloc_sqes(t, sqe, 1); 23 - io_uring_prep_fsync(sqe[0], 1 /*fds[1]*/, IORING_FSYNC_DATASYNC); 23 + io_uring_prep_fsync(sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/, IORING_FSYNC_DATASYNC); 24 24 io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE); 25 25 /* bit63 marks us as tgt io */ 26 26 sqe[0]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1); ··· 42 42 if (!sqe[0]) 43 43 return -ENOMEM; 44 44 45 - io_uring_prep_rw(op, sqe[0], 1 /*fds[1]*/, 45 + io_uring_prep_rw(op, sqe[0], ublk_get_registered_fd(q, 1) /*fds[1]*/, 46 46 addr, 47 47 iod->nr_sectors << 9, 48 48 iod->start_sector << 9); ··· 56 56 57 57 ublk_io_alloc_sqes(t, sqe, 3); 58 58 59 - io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 59 + io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 60 60 sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; 61 61 sqe[0]->user_data = build_user_data(tag, 62 62 ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); 63 63 64 - io_uring_prep_rw(op, sqe[1], 1 /*fds[1]*/, 0, 64 + io_uring_prep_rw(op, sqe[1], ublk_get_registered_fd(q, 1) /*fds[1]*/, 0, 65 65 iod->nr_sectors << 9, 66 66 iod->start_sector << 9); 67 67 sqe[1]->buf_index = tag; 68 68 sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK; 69 69 sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1); 70 70 71 - io_uring_prep_buf_unregister(sqe[2], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 71 + io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 72 72 sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1); 73 73 74 74 return 2;
+31 -7
tools/testing/selftests/ublk/kublk.c
··· 432 432 } 433 433 } 434 434 435 - static int ublk_queue_init(struct ublk_queue *q, unsigned extra_flags) 435 + static int ublk_queue_init(struct ublk_queue *q, unsigned long long extra_flags) 436 436 { 437 437 struct ublk_dev *dev = q->dev; 438 438 int depth = dev->dev_info.queue_depth; ··· 445 445 q->q_depth = depth; 446 446 q->flags = dev->dev_info.flags; 447 447 q->flags |= extra_flags; 448 + 449 + /* Cache fd in queue for fast path access */ 450 + q->ublk_fd = dev->fds[0]; 448 451 449 452 cmd_buf_size = ublk_queue_cmd_buf_sz(q); 450 453 off = UBLKSRV_CMD_BUF_OFFSET + q->q_id * ublk_queue_max_cmd_buf_sz(); ··· 484 481 return -ENOMEM; 485 482 } 486 483 487 - static int ublk_thread_init(struct ublk_thread *t) 484 + static int ublk_thread_init(struct ublk_thread *t, unsigned long long extra_flags) 488 485 { 489 486 struct ublk_dev *dev = t->dev; 487 + unsigned long long flags = dev->dev_info.flags | extra_flags; 490 488 int ring_depth = dev->tgt.sq_depth, cq_depth = dev->tgt.cq_depth; 491 489 int ret; 492 490 ··· 516 512 517 513 io_uring_register_ring_fd(&t->ring); 518 514 519 - ret = io_uring_register_files(&t->ring, dev->fds, dev->nr_fds); 515 + if (flags & UBLKS_Q_NO_UBLK_FIXED_FD) { 516 + /* Register only backing files starting from index 1, exclude ublk control device */ 517 + if (dev->nr_fds > 1) { 518 + ret = io_uring_register_files(&t->ring, &dev->fds[1], dev->nr_fds - 1); 519 + } else { 520 + /* No backing files to register, skip file registration */ 521 + ret = 0; 522 + } 523 + } else { 524 + ret = io_uring_register_files(&t->ring, dev->fds, dev->nr_fds); 525 + } 520 526 if (ret) { 521 527 ublk_err("ublk dev %d thread %d register files failed %d\n", 522 528 t->dev->dev_info.dev_id, t->idx, ret); ··· 640 626 641 627 /* These fields should be written once, never change */ 642 628 ublk_set_sqe_cmd_op(sqe[0], cmd_op); 643 - sqe[0]->fd = 0; /* dev->fds[0] */ 629 + sqe[0]->fd = ublk_get_registered_fd(q, 0); /* dev->fds[0] */ 644 630 sqe[0]->opcode = IORING_OP_URING_CMD; 645 - sqe[0]->flags = IOSQE_FIXED_FILE; 631 + if (q->flags & UBLKS_Q_NO_UBLK_FIXED_FD) 632 + sqe[0]->flags = 0; /* Use raw FD, not fixed file */ 633 + else 634 + sqe[0]->flags = IOSQE_FIXED_FILE; 646 635 sqe[0]->rw_flags = 0; 647 636 cmd->tag = io->tag; 648 637 cmd->q_id = q->q_id; ··· 849 832 unsigned idx; 850 833 sem_t *ready; 851 834 cpu_set_t *affinity; 835 + unsigned long long extra_flags; 852 836 }; 853 837 854 838 static void *ublk_io_handler_fn(void *data) ··· 862 844 t->dev = info->dev; 863 845 t->idx = info->idx; 864 846 865 - ret = ublk_thread_init(t); 847 + ret = ublk_thread_init(t, info->extra_flags); 866 848 if (ret) { 867 849 ublk_err("ublk dev %d thread %u init failed\n", 868 850 dev_id, t->idx); ··· 952 934 953 935 if (ctx->auto_zc_fallback) 954 936 extra_flags = UBLKS_Q_AUTO_BUF_REG_FALLBACK; 937 + if (ctx->no_ublk_fixed_fd) 938 + extra_flags |= UBLKS_Q_NO_UBLK_FIXED_FD; 955 939 956 940 for (i = 0; i < dinfo->nr_hw_queues; i++) { 957 941 dev->q[i].dev = dev; ··· 971 951 tinfo[i].dev = dev; 972 952 tinfo[i].idx = i; 973 953 tinfo[i].ready = &ready; 954 + tinfo[i].extra_flags = extra_flags; 974 955 975 956 /* 976 957 * If threads are not tied 1:1 to queues, setting thread ··· 1492 1471 printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n", 1493 1472 exe, recovery ? "recover" : "add"); 1494 1473 printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1 ] [-g]\n"); 1495 - printf("\t[-e 0|1 ] [-i 0|1]\n"); 1474 + printf("\t[-e 0|1 ] [-i 0|1] [--no_ublk_fixed_fd]\n"); 1496 1475 printf("\t[--nthreads threads] [--per_io_tasks]\n"); 1497 1476 printf("\t[target options] [backfile1] [backfile2] ...\n"); 1498 1477 printf("\tdefault: nr_queues=2(max 32), depth=128(max 1024), dev_id=-1(auto allocation)\n"); ··· 1555 1534 { "size", 1, NULL, 's'}, 1556 1535 { "nthreads", 1, NULL, 0 }, 1557 1536 { "per_io_tasks", 0, NULL, 0 }, 1537 + { "no_ublk_fixed_fd", 0, NULL, 0 }, 1558 1538 { 0, 0, 0, 0 } 1559 1539 }; 1560 1540 const struct ublk_tgt_ops *ops = NULL; ··· 1635 1613 ctx.nthreads = strtol(optarg, NULL, 10); 1636 1614 if (!strcmp(longopts[option_idx].name, "per_io_tasks")) 1637 1615 ctx.per_io_tasks = 1; 1616 + if (!strcmp(longopts[option_idx].name, "no_ublk_fixed_fd")) 1617 + ctx.no_ublk_fixed_fd = 1; 1638 1618 break; 1639 1619 case '?': 1640 1620 /*
+31 -14
tools/testing/selftests/ublk/kublk.h
··· 77 77 unsigned int recovery:1; 78 78 unsigned int auto_zc_fallback:1; 79 79 unsigned int per_io_tasks:1; 80 + unsigned int no_ublk_fixed_fd:1; 80 81 81 82 int _evtfd; 82 83 int _shmid; ··· 167 166 168 167 /* borrow one bit of ublk uapi flags, which may never be used */ 169 168 #define UBLKS_Q_AUTO_BUF_REG_FALLBACK (1ULL << 63) 169 + #define UBLKS_Q_NO_UBLK_FIXED_FD (1ULL << 62) 170 170 __u64 flags; 171 + int ublk_fd; /* cached ublk char device fd */ 171 172 struct ublk_io ios[UBLK_QUEUE_DEPTH]; 172 173 }; 173 174 ··· 276 273 return nr_sqes; 277 274 } 278 275 279 - static inline void io_uring_prep_buf_register(struct io_uring_sqe *sqe, 280 - int dev_fd, int tag, int q_id, __u64 index) 276 + static inline int ublk_get_registered_fd(struct ublk_queue *q, int fd_index) 277 + { 278 + if (q->flags & UBLKS_Q_NO_UBLK_FIXED_FD) { 279 + if (fd_index == 0) 280 + /* Return the raw ublk FD for index 0 */ 281 + return q->ublk_fd; 282 + /* Adjust index for backing files (index 1 becomes 0, etc.) */ 283 + return fd_index - 1; 284 + } 285 + return fd_index; 286 + } 287 + 288 + static inline void __io_uring_prep_buf_reg_unreg(struct io_uring_sqe *sqe, 289 + struct ublk_queue *q, int tag, int q_id, __u64 index) 281 290 { 282 291 struct ublksrv_io_cmd *cmd = (struct ublksrv_io_cmd *)sqe->cmd; 292 + int dev_fd = ublk_get_registered_fd(q, 0); 283 293 284 294 io_uring_prep_read(sqe, dev_fd, 0, 0, 0); 285 295 sqe->opcode = IORING_OP_URING_CMD; 286 - sqe->flags |= IOSQE_FIXED_FILE; 287 - sqe->cmd_op = UBLK_U_IO_REGISTER_IO_BUF; 296 + if (q->flags & UBLKS_Q_NO_UBLK_FIXED_FD) 297 + sqe->flags &= ~IOSQE_FIXED_FILE; 298 + else 299 + sqe->flags |= IOSQE_FIXED_FILE; 288 300 289 301 cmd->tag = tag; 290 302 cmd->addr = index; 291 303 cmd->q_id = q_id; 292 304 } 293 305 294 - static inline void io_uring_prep_buf_unregister(struct io_uring_sqe *sqe, 295 - int dev_fd, int tag, int q_id, __u64 index) 306 + static inline void io_uring_prep_buf_register(struct io_uring_sqe *sqe, 307 + struct ublk_queue *q, int tag, int q_id, __u64 index) 296 308 { 297 - struct ublksrv_io_cmd *cmd = (struct ublksrv_io_cmd *)sqe->cmd; 309 + __io_uring_prep_buf_reg_unreg(sqe, q, tag, q_id, index); 310 + sqe->cmd_op = UBLK_U_IO_REGISTER_IO_BUF; 311 + } 298 312 299 - io_uring_prep_read(sqe, dev_fd, 0, 0, 0); 300 - sqe->opcode = IORING_OP_URING_CMD; 301 - sqe->flags |= IOSQE_FIXED_FILE; 313 + static inline void io_uring_prep_buf_unregister(struct io_uring_sqe *sqe, 314 + struct ublk_queue *q, int tag, int q_id, __u64 index) 315 + { 316 + __io_uring_prep_buf_reg_unreg(sqe, q, tag, q_id, index); 302 317 sqe->cmd_op = UBLK_U_IO_UNREGISTER_IO_BUF; 303 - 304 - cmd->tag = tag; 305 - cmd->addr = index; 306 - cmd->q_id = q_id; 307 318 } 308 319 309 320 static inline void *ublk_get_sqe_cmd(const struct io_uring_sqe *sqe)
+2 -2
tools/testing/selftests/ublk/null.c
··· 63 63 64 64 ublk_io_alloc_sqes(t, sqe, 3); 65 65 66 - io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 66 + io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 67 67 sqe[0]->user_data = build_user_data(tag, 68 68 ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); 69 69 sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; ··· 71 71 __setup_nop_io(tag, iod, sqe[1], q->q_id); 72 72 sqe[1]->flags |= IOSQE_IO_HARDLINK; 73 73 74 - io_uring_prep_buf_unregister(sqe[2], 0, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 74 + io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index); 75 75 sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1); 76 76 77 77 // buf register is marked as IOSQE_CQE_SKIP_SUCCESS
+2 -2
tools/testing/selftests/ublk/stripe.c
··· 142 142 ublk_io_alloc_sqes(t, sqe, s->nr + extra); 143 143 144 144 if (zc) { 145 - io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, io->buf_index); 145 + io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index); 146 146 sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; 147 147 sqe[0]->user_data = build_user_data(tag, 148 148 ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1); ··· 168 168 if (zc) { 169 169 struct io_uring_sqe *unreg = sqe[s->nr + 1]; 170 170 171 - io_uring_prep_buf_unregister(unreg, 0, tag, q->q_id, io->buf_index); 171 + io_uring_prep_buf_unregister(unreg, q, tag, q->q_id, io->buf_index); 172 172 unreg->user_data = build_user_data( 173 173 tag, ublk_cmd_op_nr(unreg->cmd_op), 0, q->q_id, 1); 174 174 }
+3 -3
tools/testing/selftests/ublk/test_stress_04.sh
··· 28 28 _create_backfile 1 128M 29 29 _create_backfile 2 128M 30 30 31 - ublk_io_and_kill_daemon 8G -t null -q 4 -z & 32 - ublk_io_and_kill_daemon 256M -t loop -q 4 -z "${UBLK_BACKFILES[0]}" & 31 + ublk_io_and_kill_daemon 8G -t null -q 4 -z --no_ublk_fixed_fd & 32 + ublk_io_and_kill_daemon 256M -t loop -q 4 -z --no_ublk_fixed_fd "${UBLK_BACKFILES[0]}" & 33 33 ublk_io_and_kill_daemon 256M -t stripe -q 4 -z "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & 34 34 35 35 if _have_feature "AUTO_BUF_REG"; then 36 36 ublk_io_and_kill_daemon 8G -t null -q 4 --auto_zc & 37 37 ublk_io_and_kill_daemon 256M -t loop -q 4 --auto_zc "${UBLK_BACKFILES[0]}" & 38 - ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & 38 + ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc --no_ublk_fixed_fd "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & 39 39 ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback & 40 40 fi 41 41