Skip to content

Commit b9f0a94

Browse files
calebsanderaxboe
authored andcommitted
selftests: ublk: add support for user copy to kublk
The ublk selftests mock ublk server kublk supports every data copy mode except user copy. Add support for user copy to kublk, enabled via the --user_copy (-u) command line argument. On writes, issue pread() calls to copy the write data into the ublk_io's buffer before dispatching the write to the target implementation. On reads, issue pwrite() calls to copy read data from the ublk_io's buffer before committing the request. Copy in 2 KB chunks to provide some coverage of the offseting logic. Signed-off-by: Caleb Sander Mateos <csander@purestorage.com> Reviewed-by: Ming Lei <ming.lei@redhat.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 52bc483 commit b9f0a94

4 files changed

Lines changed: 64 additions & 9 deletions

File tree

tools/testing/selftests/ublk/file_backed.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
3434
unsigned zc = ublk_queue_use_zc(q);
3535
unsigned auto_zc = ublk_queue_use_auto_zc(q);
3636
enum io_uring_op op = ublk_to_uring_op(iod, zc | auto_zc);
37+
struct ublk_io *io = ublk_get_io(q, tag);
3738
struct io_uring_sqe *sqe[3];
38-
void *addr = (zc | auto_zc) ? NULL : (void *)iod->addr;
39+
void *addr = io->buf_addr;
3940

4041
if (!zc || auto_zc) {
4142
ublk_io_alloc_sqes(t, sqe, 1);
@@ -56,7 +57,7 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
5657

5758
ublk_io_alloc_sqes(t, sqe, 3);
5859

59-
io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index);
60+
io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index);
6061
sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK;
6162
sqe[0]->user_data = build_user_data(tag,
6263
ublk_cmd_op_nr(sqe[0]->cmd_op), 0, q->q_id, 1);
@@ -68,7 +69,7 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
6869
sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK;
6970
sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);
7071

71-
io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index);
72+
io_uring_prep_buf_unregister(sqe[2], q, tag, q->q_id, io->buf_index);
7273
sqe[2]->user_data = build_user_data(tag, ublk_cmd_op_nr(sqe[2]->cmd_op), 0, q->q_id, 1);
7374

7475
return 2;

tools/testing/selftests/ublk/kublk.c

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,38 @@ static void ublk_set_auto_buf_reg(const struct ublk_queue *q,
596596
sqe->addr = ublk_auto_buf_reg_to_sqe_addr(&buf);
597597
}
598598

599+
/* Copy in pieces to test the buffer offset logic */
600+
#define UBLK_USER_COPY_LEN 2048
601+
602+
static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
603+
{
604+
const struct ublk_queue *q = ublk_io_to_queue(io);
605+
const struct ublksrv_io_desc *iod = ublk_get_iod(q, io->tag);
606+
__u64 off = ublk_user_copy_offset(q->q_id, io->tag);
607+
__u8 ublk_op = ublksrv_get_op(iod);
608+
__u32 len = iod->nr_sectors << 9;
609+
void *addr = io->buf_addr;
610+
611+
if (ublk_op != match_ublk_op)
612+
return;
613+
614+
while (len) {
615+
__u32 copy_len = min(len, UBLK_USER_COPY_LEN);
616+
ssize_t copied;
617+
618+
if (ublk_op == UBLK_IO_OP_WRITE)
619+
copied = pread(q->ublk_fd, addr, copy_len, off);
620+
else if (ublk_op == UBLK_IO_OP_READ)
621+
copied = pwrite(q->ublk_fd, addr, copy_len, off);
622+
else
623+
assert(0);
624+
assert(copied == (ssize_t)copy_len);
625+
addr += copy_len;
626+
off += copy_len;
627+
len -= copy_len;
628+
}
629+
}
630+
599631
int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
600632
{
601633
struct ublk_queue *q = ublk_io_to_queue(io);
@@ -618,9 +650,12 @@ int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
618650

619651
if (io->flags & UBLKS_IO_NEED_GET_DATA)
620652
cmd_op = UBLK_U_IO_NEED_GET_DATA;
621-
else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP)
653+
else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP) {
654+
if (ublk_queue_use_user_copy(q))
655+
ublk_user_copy(io, UBLK_IO_OP_READ);
656+
622657
cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ;
623-
else if (io->flags & UBLKS_IO_NEED_FETCH_RQ)
658+
} else if (io->flags & UBLKS_IO_NEED_FETCH_RQ)
624659
cmd_op = UBLK_U_IO_FETCH_REQ;
625660

626661
if (io_uring_sq_space_left(&t->ring) < 1)
@@ -649,7 +684,7 @@ int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
649684
sqe[0]->rw_flags = 0;
650685
cmd->tag = io->tag;
651686
cmd->q_id = q->q_id;
652-
if (!ublk_queue_no_buf(q))
687+
if (!ublk_queue_no_buf(q) && !ublk_queue_use_user_copy(q))
653688
cmd->addr = (__u64) (uintptr_t) io->buf_addr;
654689
else
655690
cmd->addr = 0;
@@ -751,6 +786,10 @@ static void ublk_handle_uring_cmd(struct ublk_thread *t,
751786

752787
if (cqe->res == UBLK_IO_RES_OK) {
753788
assert(tag < q->q_depth);
789+
790+
if (ublk_queue_use_user_copy(q))
791+
ublk_user_copy(io, UBLK_IO_OP_WRITE);
792+
754793
if (q->tgt_ops->queue_io)
755794
q->tgt_ops->queue_io(t, q, tag);
756795
} else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) {
@@ -1507,7 +1546,7 @@ static void __cmd_create_help(char *exe, bool recovery)
15071546

15081547
printf("%s %s -t [null|loop|stripe|fault_inject] [-q nr_queues] [-d depth] [-n dev_id]\n",
15091548
exe, recovery ? "recover" : "add");
1510-
printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1 ] [-g]\n");
1549+
printf("\t[--foreground] [--quiet] [-z] [--auto_zc] [--auto_zc_fallback] [--debug_mask mask] [-r 0|1] [-g] [-u]\n");
15111550
printf("\t[-e 0|1 ] [-i 0|1] [--no_ublk_fixed_fd]\n");
15121551
printf("\t[--nthreads threads] [--per_io_tasks]\n");
15131552
printf("\t[target options] [backfile1] [backfile2] ...\n");
@@ -1568,6 +1607,7 @@ int main(int argc, char *argv[])
15681607
{ "get_data", 1, NULL, 'g'},
15691608
{ "auto_zc", 0, NULL, 0 },
15701609
{ "auto_zc_fallback", 0, NULL, 0 },
1610+
{ "user_copy", 0, NULL, 'u'},
15711611
{ "size", 1, NULL, 's'},
15721612
{ "nthreads", 1, NULL, 0 },
15731613
{ "per_io_tasks", 0, NULL, 0 },
@@ -1593,7 +1633,7 @@ int main(int argc, char *argv[])
15931633

15941634
opterr = 0;
15951635
optind = 2;
1596-
while ((opt = getopt_long(argc, argv, "t:n:d:q:r:e:i:s:gaz",
1636+
while ((opt = getopt_long(argc, argv, "t:n:d:q:r:e:i:s:gazu",
15971637
longopts, &option_idx)) != -1) {
15981638
switch (opt) {
15991639
case 'a':
@@ -1633,6 +1673,9 @@ int main(int argc, char *argv[])
16331673
case 'g':
16341674
ctx.flags |= UBLK_F_NEED_GET_DATA;
16351675
break;
1676+
case 'u':
1677+
ctx.flags |= UBLK_F_USER_COPY;
1678+
break;
16361679
case 's':
16371680
ctx.size = strtoull(optarg, NULL, 10);
16381681
break;

tools/testing/selftests/ublk/kublk.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,12 @@ static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod)
208208
return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF);
209209
}
210210

211+
static inline __u64 ublk_user_copy_offset(unsigned q_id, unsigned tag)
212+
{
213+
return UBLKSRV_IO_BUF_OFFSET +
214+
((__u64)q_id << UBLK_QID_OFF | (__u64)tag << UBLK_TAG_OFF);
215+
}
216+
211217
static inline int is_target_io(__u64 user_data)
212218
{
213219
return (user_data & (1ULL << 63)) != 0;
@@ -405,6 +411,11 @@ static inline bool ublk_queue_auto_zc_fallback(const struct ublk_queue *q)
405411
return !!(q->flags & UBLKS_Q_AUTO_BUF_REG_FALLBACK);
406412
}
407413

414+
static inline bool ublk_queue_use_user_copy(const struct ublk_queue *q)
415+
{
416+
return !!(q->flags & UBLK_F_USER_COPY);
417+
}
418+
408419
static inline int ublk_queue_no_buf(const struct ublk_queue *q)
409420
{
410421
return ublk_queue_use_zc(q) || ublk_queue_use_auto_zc(q);

tools/testing/selftests/ublk/stripe.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ static int stripe_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
134134
struct stripe_array *s = alloc_stripe_array(conf, iod);
135135
struct ublk_io *io = ublk_get_io(q, tag);
136136
int i, extra = zc ? 2 : 0;
137-
void *base = (zc | auto_zc) ? NULL : (void *)iod->addr;
137+
void *base = io->buf_addr;
138138

139139
io->private_data = s;
140140
calculate_stripe_array(conf, iod, s, base);

0 commit comments

Comments
 (0)