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

selftests: ublk: enable zero copy for null target

Enable zero copy for null target so that we can evaluate performance
from zero copy or not.

Also this should be the simplest ublk zero copy implementation, which
can be served as zc example.

Add test for covering 'add -t null -z'.

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

authored by

Ming Lei and committed by
Jens Axboe
8cb9b971 8842b72a

+95 -1
+1
tools/testing/selftests/ublk/Makefile
··· 6 6 TEST_PROGS := test_generic_01.sh 7 7 8 8 TEST_PROGS += test_null_01.sh 9 + TEST_PROGS += test_null_02.sh 9 10 TEST_PROGS += test_loop_01.sh 10 11 TEST_PROGS += test_loop_02.sh 11 12 TEST_PROGS += test_loop_03.sh
+5
tools/testing/selftests/ublk/kublk.h
··· 198 198 return (user_data >> 24) & 0xffff; 199 199 } 200 200 201 + static inline unsigned short ublk_cmd_op_nr(unsigned int op) 202 + { 203 + return _IOC_NR(op); 204 + } 205 + 201 206 static inline void ublk_err(const char *fmt, ...) 202 207 { 203 208 va_list ap;
+69 -1
tools/testing/selftests/ublk/null.c
··· 2 2 3 3 #include "kublk.h" 4 4 5 + #ifndef IORING_NOP_INJECT_RESULT 6 + #define IORING_NOP_INJECT_RESULT (1U << 0) 7 + #endif 8 + 9 + #ifndef IORING_NOP_FIXED_BUFFER 10 + #define IORING_NOP_FIXED_BUFFER (1U << 3) 11 + #endif 12 + 5 13 static int ublk_null_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev) 6 14 { 7 15 const struct ublksrv_ctrl_dev_info *info = &dev->dev_info; ··· 28 20 }, 29 21 }; 30 22 23 + if (info->flags & UBLK_F_SUPPORT_ZERO_COPY) 24 + dev->tgt.sq_depth = dev->tgt.cq_depth = 2 * info->queue_depth; 31 25 return 0; 26 + } 27 + 28 + static int null_queue_zc_io(struct ublk_queue *q, int tag) 29 + { 30 + const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); 31 + unsigned ublk_op = ublksrv_get_op(iod); 32 + struct io_uring_sqe *sqe[3]; 33 + 34 + ublk_queue_alloc_sqes(q, sqe, 3); 35 + 36 + io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, tag); 37 + sqe[0]->user_data = build_user_data(tag, 38 + ublk_cmd_op_nr(sqe[0]->cmd_op), 0, 1); 39 + sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK; 40 + 41 + io_uring_prep_nop(sqe[1]); 42 + sqe[1]->buf_index = tag; 43 + sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK; 44 + sqe[1]->rw_flags = IORING_NOP_FIXED_BUFFER | IORING_NOP_INJECT_RESULT; 45 + sqe[1]->len = iod->nr_sectors << 9; /* injected result */ 46 + sqe[1]->user_data = build_user_data(tag, ublk_op, 0, 1); 47 + 48 + io_uring_prep_buf_unregister(sqe[2], 0, tag, q->q_id, tag); 49 + sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, 1); 50 + 51 + // buf register is marked as IOSQE_CQE_SKIP_SUCCESS 52 + return 2; 53 + } 54 + 55 + static void ublk_null_io_done(struct ublk_queue *q, int tag, 56 + const struct io_uring_cqe *cqe) 57 + { 58 + unsigned op = user_data_to_op(cqe->user_data); 59 + struct ublk_io *io = ublk_get_io(q, tag); 60 + 61 + if (cqe->res < 0 || op != ublk_cmd_op_nr(UBLK_U_IO_UNREGISTER_IO_BUF)) { 62 + if (!io->result) 63 + io->result = cqe->res; 64 + if (cqe->res < 0) 65 + ublk_err("%s: io failed op %x user_data %lx\n", 66 + __func__, op, cqe->user_data); 67 + } 68 + 69 + /* buffer register op is IOSQE_CQE_SKIP_SUCCESS */ 70 + if (op == ublk_cmd_op_nr(UBLK_U_IO_REGISTER_IO_BUF)) 71 + io->tgt_ios += 1; 72 + 73 + if (ublk_completed_tgt_io(q, tag)) 74 + ublk_complete_io(q, tag, io->result); 32 75 } 33 76 34 77 static int ublk_null_queue_io(struct ublk_queue *q, int tag) 35 78 { 36 79 const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag); 80 + int zc = ublk_queue_use_zc(q); 81 + int queued; 37 82 38 - ublk_complete_io(q, tag, iod->nr_sectors << 9); 83 + if (!zc) { 84 + ublk_complete_io(q, tag, iod->nr_sectors << 9); 85 + return 0; 86 + } 87 + 88 + queued = null_queue_zc_io(q, tag); 89 + ublk_queued_tgt_io(q, tag, queued); 39 90 return 0; 40 91 } 41 92 ··· 102 35 .name = "null", 103 36 .init_tgt = ublk_null_tgt_init, 104 37 .queue_io = ublk_null_queue_io, 38 + .tgt_io_done = ublk_null_io_done, 105 39 };
+20
tools/testing/selftests/ublk/test_null_02.sh
··· 1 + #!/bin/bash 2 + # SPDX-License-Identifier: GPL-2.0 3 + 4 + . "$(cd "$(dirname "$0")" && pwd)"/test_common.sh 5 + 6 + TID="null_02" 7 + ERR_CODE=0 8 + 9 + _prep_test "null" "basic IO test with zero copy" 10 + 11 + dev_id=$(_add_ublk_dev -t null -z) 12 + _check_add_dev $TID $? 13 + 14 + # run fio over the two disks 15 + fio --name=job1 --filename=/dev/ublkb"${dev_id}" --ioengine=libaio --rw=readwrite --iodepth=32 --size=256M > /dev/null 2>&1 16 + ERR_CODE=$? 17 + 18 + _cleanup_test "null" 19 + 20 + _show_result $TID $ERR_CODE