Commit b9f0a94c authored by Caleb Sander Mateos's avatar Caleb Sander Mateos Committed by Jens Axboe
Browse files

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: default avatarCaleb Sander Mateos <csander@purestorage.com>
Reviewed-by: default avatarMing Lei <ming.lei@redhat.com>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 52bc4837
Loading
Loading
Loading
Loading
+4 −3
Original line number Diff line number Diff line
@@ -34,8 +34,9 @@ static int loop_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
	unsigned zc = ublk_queue_use_zc(q);
	unsigned auto_zc = ublk_queue_use_auto_zc(q);
	enum io_uring_op op = ublk_to_uring_op(iod, zc | auto_zc);
	struct ublk_io *io = ublk_get_io(q, tag);
	struct io_uring_sqe *sqe[3];
	void *addr = (zc | auto_zc) ? NULL : (void *)iod->addr;
	void *addr = io->buf_addr;

	if (!zc || auto_zc) {
		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,

	ublk_io_alloc_sqes(t, sqe, 3);

	io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, ublk_get_io(q, tag)->buf_index);
	io_uring_prep_buf_register(sqe[0], q, tag, q->q_id, io->buf_index);
	sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS | IOSQE_IO_HARDLINK;
	sqe[0]->user_data = build_user_data(tag,
			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,
	sqe[1]->flags |= IOSQE_FIXED_FILE | IOSQE_IO_HARDLINK;
	sqe[1]->user_data = build_user_data(tag, ublk_op, 0, q->q_id, 1);

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

	return 2;
+48 −5
Original line number Diff line number Diff line
@@ -596,6 +596,38 @@ static void ublk_set_auto_buf_reg(const struct ublk_queue *q,
	sqe->addr = ublk_auto_buf_reg_to_sqe_addr(&buf);
}

/* Copy in pieces to test the buffer offset logic */
#define UBLK_USER_COPY_LEN 2048

static void ublk_user_copy(const struct ublk_io *io, __u8 match_ublk_op)
{
	const struct ublk_queue *q = ublk_io_to_queue(io);
	const struct ublksrv_io_desc *iod = ublk_get_iod(q, io->tag);
	__u64 off = ublk_user_copy_offset(q->q_id, io->tag);
	__u8 ublk_op = ublksrv_get_op(iod);
	__u32 len = iod->nr_sectors << 9;
	void *addr = io->buf_addr;

	if (ublk_op != match_ublk_op)
		return;

	while (len) {
		__u32 copy_len = min(len, UBLK_USER_COPY_LEN);
		ssize_t copied;

		if (ublk_op == UBLK_IO_OP_WRITE)
			copied = pread(q->ublk_fd, addr, copy_len, off);
		else if (ublk_op == UBLK_IO_OP_READ)
			copied = pwrite(q->ublk_fd, addr, copy_len, off);
		else
			assert(0);
		assert(copied == (ssize_t)copy_len);
		addr += copy_len;
		off += copy_len;
		len -= copy_len;
	}
}

int ublk_queue_io_cmd(struct ublk_thread *t, struct ublk_io *io)
{
	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)

	if (io->flags & UBLKS_IO_NEED_GET_DATA)
		cmd_op = UBLK_U_IO_NEED_GET_DATA;
	else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP)
	else if (io->flags & UBLKS_IO_NEED_COMMIT_RQ_COMP) {
		if (ublk_queue_use_user_copy(q))
			ublk_user_copy(io, UBLK_IO_OP_READ);

		cmd_op = UBLK_U_IO_COMMIT_AND_FETCH_REQ;
	else if (io->flags & UBLKS_IO_NEED_FETCH_RQ)
	} else if (io->flags & UBLKS_IO_NEED_FETCH_RQ)
		cmd_op = UBLK_U_IO_FETCH_REQ;

	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)
	sqe[0]->rw_flags	= 0;
	cmd->tag	= io->tag;
	cmd->q_id	= q->q_id;
	if (!ublk_queue_no_buf(q))
	if (!ublk_queue_no_buf(q) && !ublk_queue_use_user_copy(q))
		cmd->addr	= (__u64) (uintptr_t) io->buf_addr;
	else
		cmd->addr	= 0;
@@ -751,6 +786,10 @@ static void ublk_handle_uring_cmd(struct ublk_thread *t,

	if (cqe->res == UBLK_IO_RES_OK) {
		assert(tag < q->q_depth);

		if (ublk_queue_use_user_copy(q))
			ublk_user_copy(io, UBLK_IO_OP_WRITE);

		if (q->tgt_ops->queue_io)
			q->tgt_ops->queue_io(t, q, tag);
	} else if (cqe->res == UBLK_IO_RES_NEED_GET_DATA) {
@@ -1507,7 +1546,7 @@ static void __cmd_create_help(char *exe, bool recovery)

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

	opterr = 0;
	optind = 2;
	while ((opt = getopt_long(argc, argv, "t:n:d:q:r:e:i:s:gaz",
	while ((opt = getopt_long(argc, argv, "t:n:d:q:r:e:i:s:gazu",
				  longopts, &option_idx)) != -1) {
		switch (opt) {
		case 'a':
@@ -1633,6 +1673,9 @@ int main(int argc, char *argv[])
		case 'g':
			ctx.flags |= UBLK_F_NEED_GET_DATA;
			break;
		case 'u':
			ctx.flags |= UBLK_F_USER_COPY;
			break;
		case 's':
			ctx.size = strtoull(optarg, NULL, 10);
			break;
+11 −0
Original line number Diff line number Diff line
@@ -208,6 +208,12 @@ static inline int ublk_io_auto_zc_fallback(const struct ublksrv_io_desc *iod)
	return !!(iod->op_flags & UBLK_IO_F_NEED_REG_BUF);
}

static inline __u64 ublk_user_copy_offset(unsigned q_id, unsigned tag)
{
	return UBLKSRV_IO_BUF_OFFSET +
	       ((__u64)q_id << UBLK_QID_OFF | (__u64)tag << UBLK_TAG_OFF);
}

static inline int is_target_io(__u64 user_data)
{
	return (user_data & (1ULL << 63)) != 0;
@@ -405,6 +411,11 @@ static inline bool ublk_queue_auto_zc_fallback(const struct ublk_queue *q)
	return !!(q->flags & UBLKS_Q_AUTO_BUF_REG_FALLBACK);
}

static inline bool ublk_queue_use_user_copy(const struct ublk_queue *q)
{
	return !!(q->flags & UBLK_F_USER_COPY);
}

static inline int ublk_queue_no_buf(const struct ublk_queue *q)
{
	return ublk_queue_use_zc(q) || ublk_queue_use_auto_zc(q);
+1 −1
Original line number Diff line number Diff line
@@ -134,7 +134,7 @@ static int stripe_queue_tgt_rw_io(struct ublk_thread *t, struct ublk_queue *q,
	struct stripe_array *s = alloc_stripe_array(conf, iod);
	struct ublk_io *io = ublk_get_io(q, tag);
	int i, extra = zc ? 2 : 0;
	void *base = (zc | auto_zc) ? NULL : (void *)iod->addr;
	void *base = io->buf_addr;

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