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

selftests: ublk: add test for covering UBLK_AUTO_BUF_REG_FALLBACK

Add test for covering UBLK_AUTO_BUF_REG_FALLBACK:

- pass '--auto_zc_fallback' to null target, which requires both F_AUTO_BUF_REG
and F_SUPPORT_ZERO_COPY for handling UBLK_AUTO_BUF_REG_FALLBACK

- add ->buf_index() method for returning invalid buffer index to trigger
UBLK_AUTO_BUF_REG_FALLBACK

- add generic_09 for running the test

- add --auto_zc_fallback test in stress_03/stress_04/stress_05

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

authored by

Ming Lei and committed by
Jens Axboe
6f1a182a 8ccebc19

+108 -13
+1
tools/testing/selftests/ublk/Makefile
··· 16 16 TEST_PROGS += test_generic_07.sh 17 17 18 18 TEST_PROGS += test_generic_08.sh 19 + TEST_PROGS += test_generic_09.sh 19 20 20 21 TEST_PROGS += test_null_01.sh 21 22 TEST_PROGS += test_null_02.sh
+5
tools/testing/selftests/ublk/fault_inject.c
··· 16 16 const struct ublksrv_ctrl_dev_info *info = &dev->dev_info; 17 17 unsigned long dev_size = 250UL << 30; 18 18 19 + if (ctx->auto_zc_fallback) { 20 + ublk_err("%s: not support auto_zc_fallback\n", __func__); 21 + return -EINVAL; 22 + } 23 + 19 24 dev->tgt.dev_size = dev_size; 20 25 dev->tgt.params = (struct ublk_params) { 21 26 .types = UBLK_PARAM_TYPE_BASIC,
+5
tools/testing/selftests/ublk/file_backed.c
··· 149 149 }, 150 150 }; 151 151 152 + if (ctx->auto_zc_fallback) { 153 + ublk_err("%s: not support auto_zc_fallback\n", __func__); 154 + return -EINVAL; 155 + } 156 + 152 157 ret = backing_file_tgt_init(dev); 153 158 if (ret) 154 159 return ret;
+37 -12
tools/testing/selftests/ublk/kublk.c
··· 405 405 free(q->ios[i].buf_addr); 406 406 } 407 407 408 - static int ublk_queue_init(struct ublk_queue *q) 408 + static int ublk_queue_init(struct ublk_queue *q, unsigned extra_flags) 409 409 { 410 410 struct ublk_dev *dev = q->dev; 411 411 int depth = dev->dev_info.queue_depth; ··· 427 427 if (dev->dev_info.flags & UBLK_F_AUTO_BUF_REG) 428 428 q->state |= UBLKSRV_AUTO_BUF_REG; 429 429 } 430 + q->state |= extra_flags; 430 431 431 432 cmd_buf_size = ublk_queue_cmd_buf_sz(q); 432 433 off = UBLKSRV_CMD_BUF_OFFSET + q->q_id * ublk_queue_max_cmd_buf_sz(); ··· 529 528 close(dev->fds[0]); 530 529 } 531 530 532 - static void ublk_set_auto_buf_reg(struct io_uring_sqe *sqe, 533 - unsigned short buf_idx, 534 - unsigned char flags) 531 + static void ublk_set_auto_buf_reg(const struct ublk_queue *q, 532 + struct io_uring_sqe *sqe, 533 + unsigned short tag) 535 534 { 536 - struct ublk_auto_buf_reg buf = { 537 - .index = buf_idx, 538 - .flags = flags, 539 - }; 535 + struct ublk_auto_buf_reg buf = {}; 536 + 537 + if (q->tgt_ops->buf_index) 538 + buf.index = q->tgt_ops->buf_index(q, tag); 539 + else 540 + buf.index = tag; 541 + 542 + if (q->state & UBLKSRV_AUTO_BUF_REG_FALLBACK) 543 + buf.flags = UBLK_AUTO_BUF_REG_FALLBACK; 540 544 541 545 sqe->addr = ublk_auto_buf_reg_to_sqe_addr(&buf); 542 546 } ··· 601 595 cmd->addr = 0; 602 596 603 597 if (q->state & UBLKSRV_AUTO_BUF_REG) 604 - ublk_set_auto_buf_reg(sqe[0], tag, 0); 598 + ublk_set_auto_buf_reg(q, sqe[0], tag); 605 599 606 600 user_data = build_user_data(tag, _IOC_NR(cmd_op), 0, 0); 607 601 io_uring_sqe_set_data64(sqe[0], user_data); ··· 753 747 struct ublk_queue *q; 754 748 sem_t *queue_sem; 755 749 cpu_set_t *affinity; 750 + unsigned char auto_zc_fallback; 756 751 }; 757 752 758 753 static void *ublk_io_handler_fn(void *data) ··· 761 754 struct ublk_queue_info *info = data; 762 755 struct ublk_queue *q = info->q; 763 756 int dev_id = q->dev->dev_info.dev_id; 757 + unsigned extra_flags = 0; 764 758 int ret; 765 759 766 - ret = ublk_queue_init(q); 760 + if (info->auto_zc_fallback) 761 + extra_flags = UBLKSRV_AUTO_BUF_REG_FALLBACK; 762 + 763 + ret = ublk_queue_init(q, extra_flags); 767 764 if (ret) { 768 765 ublk_err("ublk dev %d queue %d init queue failed\n", 769 766 dev_id, q->q_id); ··· 860 849 qinfo[i].q = &dev->q[i]; 861 850 qinfo[i].queue_sem = &queue_sem; 862 851 qinfo[i].affinity = &affinity_buf[i]; 852 + qinfo[i].auto_zc_fallback = ctx->auto_zc_fallback; 863 853 pthread_create(&dev->q[i].thread, NULL, 864 854 ublk_io_handler_fn, 865 855 &qinfo[i]); ··· 1276 1264 1277 1265 printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n", 1278 1266 exe, recovery ? "recover" : "add"); 1279 - printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--debug_mask mask] [-r 0|1 ] [-g]\n"); 1267 + printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1 ] [-g]\n"); 1280 1268 printf("\t[-e 0|1 ] [-i 0|1]\n"); 1281 1269 printf("\t[target options] [backfile1] [backfile2] ...\n"); 1282 1270 printf("\tdefault: nr_queues=2(max 32), depth=128(max 1024), dev_id=-1(auto allocation)\n"); ··· 1331 1319 { "recovery_fail_io", 1, NULL, 'e'}, 1332 1320 { "recovery_reissue", 1, NULL, 'i'}, 1333 1321 { "get_data", 1, NULL, 'g'}, 1334 - { "auto_zc", 0, NULL, 0}, 1322 + { "auto_zc", 0, NULL, 0 }, 1323 + { "auto_zc_fallback", 0, NULL, 0 }, 1335 1324 { 0, 0, 0, 0 } 1336 1325 }; 1337 1326 const struct ublk_tgt_ops *ops = NULL; ··· 1403 1390 ctx.fg = 1; 1404 1391 if (!strcmp(longopts[option_idx].name, "auto_zc")) 1405 1392 ctx.flags |= UBLK_F_AUTO_BUF_REG; 1393 + if (!strcmp(longopts[option_idx].name, "auto_zc_fallback")) 1394 + ctx.auto_zc_fallback = 1; 1406 1395 break; 1407 1396 case '?': 1408 1397 /* ··· 1426 1411 optind += 1; 1427 1412 break; 1428 1413 } 1414 + } 1415 + 1416 + /* auto_zc_fallback depends on F_AUTO_BUF_REG & F_SUPPORT_ZERO_COPY */ 1417 + if (ctx.auto_zc_fallback && 1418 + !((ctx.flags & UBLK_F_AUTO_BUF_REG) && 1419 + (ctx.flags & UBLK_F_SUPPORT_ZERO_COPY))) { 1420 + ublk_err("%s: auto_zc_fallback is set but neither " 1421 + "F_AUTO_BUF_REG nor F_SUPPORT_ZERO_COPY is enabled\n", 1422 + __func__); 1423 + return -EINVAL; 1429 1424 } 1430 1425 1431 1426 i = optind;
+11
tools/testing/selftests/ublk/kublk.h
··· 84 84 unsigned int all:1; 85 85 unsigned int fg:1; 86 86 unsigned int recovery:1; 87 + unsigned int auto_zc_fallback:1; 87 88 88 89 int _evtfd; 89 90 int _shmid; ··· 142 141 */ 143 142 void (*parse_cmd_line)(struct dev_ctx *ctx, int argc, char *argv[]); 144 143 void (*usage)(const struct ublk_tgt_ops *ops); 144 + 145 + /* return buffer index for UBLK_F_AUTO_BUF_REG */ 146 + unsigned short (*buf_index)(const struct ublk_queue *, int tag); 145 147 }; 146 148 147 149 struct ublk_tgt { ··· 174 170 #define UBLKSRV_NO_BUF (1U << 2) 175 171 #define UBLKSRV_ZC (1U << 3) 176 172 #define UBLKSRV_AUTO_BUF_REG (1U << 4) 173 + #define UBLKSRV_AUTO_BUF_REG_FALLBACK (1U << 5) 177 174 unsigned state; 178 175 pid_t tid; 179 176 pthread_t thread; ··· 209 204 210 205 extern unsigned int ublk_dbg_mask; 211 206 extern int ublk_queue_io_cmd(struct ublk_queue *q, struct ublk_io *io, unsigned tag); 207 + 208 + 209 + static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod) 210 + { 211 + return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF); 212 + } 212 213 213 214 static inline int is_target_io(__u64 user_data) 214 215 {
+13 -1
tools/testing/selftests/ublk/null.c
··· 116 116 unsigned zc = ublk_queue_use_zc(q); 117 117 int queued; 118 118 119 - if (auto_zc) 119 + if (auto_zc && !ublk_io_auto_zc_fallback(iod)) 120 120 queued = null_queue_auto_zc_io(q, tag); 121 121 else if (zc) 122 122 queued = null_queue_zc_io(q, tag); ··· 128 128 return 0; 129 129 } 130 130 131 + /* 132 + * return invalid buffer index for triggering auto buffer register failure, 133 + * then UBLK_IO_RES_NEED_REG_BUF handling is covered 134 + */ 135 + static unsigned short ublk_null_buf_index(const struct ublk_queue *q, int tag) 136 + { 137 + if (q->state & UBLKSRV_AUTO_BUF_REG_FALLBACK) 138 + return (unsigned short)-1; 139 + return tag; 140 + } 141 + 131 142 const struct ublk_tgt_ops null_tgt_ops = { 132 143 .name = "null", 133 144 .init_tgt = ublk_null_tgt_init, 134 145 .queue_io = ublk_null_queue_io, 135 146 .tgt_io_done = ublk_null_io_done, 147 + .buf_index = ublk_null_buf_index, 136 148 };
+5
tools/testing/selftests/ublk/stripe.c
··· 288 288 loff_t bytes = 0; 289 289 int ret, i, mul = 1; 290 290 291 + if (ctx->auto_zc_fallback) { 292 + ublk_err("%s: not support auto_zc_fallback\n", __func__); 293 + return -EINVAL; 294 + } 295 + 291 296 if ((chunk_size & (chunk_size - 1)) || !chunk_size) { 292 297 ublk_err("invalid chunk size %u\n", chunk_size); 293 298 return -EINVAL;
+28
tools/testing/selftests/ublk/test_generic_09.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + . "$(cd "$(dirname "$0")" && pwd)"/test_common.sh 5 + 6 + TID="generic_09" 7 + ERR_CODE=0 8 + 9 + if ! _have_feature "AUTO_BUF_REG"; then 10 + exit "$UBLK_SKIP_CODE" 11 + fi 12 + 13 + if ! _have_program fio; then 14 + exit "$UBLK_SKIP_CODE" 15 + fi 16 + 17 + _prep_test "null" "basic IO test" 18 + 19 + dev_id=$(_add_ublk_dev -t null -z --auto_zc --auto_zc_fallback) 20 + _check_add_dev $TID $? 21 + 22 + # run fio over the two disks 23 + fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1 24 + ERR_CODE=$? 25 + 26 + _cleanup_test "null" 27 + 28 + _show_result $TID $ERR_CODE
+1
tools/testing/selftests/ublk/test_stress_03.sh
··· 37 37 ublk_io_and_remove 8G -t null -q 4 --auto_zc & 38 38 ublk_io_and_remove 256M -t loop -q 4 --auto_zc "${UBLK_BACKFILES[0]}" & 39 39 ublk_io_and_remove 256M -t stripe -q 4 --auto_zc "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & 40 + ublk_io_and_remove 8G -t null -q 4 -z --auto_zc --auto_zc_fallback & 40 41 fi 41 42 wait 42 43
+1
tools/testing/selftests/ublk/test_stress_04.sh
··· 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 38 ublk_io_and_kill_daemon 256M -t stripe -q 4 --auto_zc "${UBLK_BACKFILES[1]}" "${UBLK_BACKFILES[2]}" & 39 + ublk_io_and_kill_daemon 8G -t null -q 4 -z --auto_zc --auto_zc_fallback & 39 40 fi 40 41 wait 41 42
+1
tools/testing/selftests/ublk/test_stress_05.sh
··· 64 64 for reissue in $(seq 0 1); do 65 65 ublk_io_and_remove 8G -t null -q 4 -g --auto_zc -r 1 -i "$reissue" & 66 66 ublk_io_and_remove 256M -t loop -q 4 -g --auto_zc -r 1 -i "$reissue" "${UBLK_BACKFILES[1]}" & 67 + ublk_io_and_remove 8G -t null -q 4 -g -z --auto_zc --auto_zc_fallback -r 1 -i "$reissue" & 67 68 wait 68 69 done 69 70 fi