xref: /linux/tools/testing/selftests/ublk/file_backed.c (revision f2639ed11e256b957690e241bb04ec9912367d60)
15d95bfb5SMing Lei // SPDX-License-Identifier: GPL-2.0
25d95bfb5SMing Lei 
35d95bfb5SMing Lei #include "kublk.h"
45d95bfb5SMing Lei 
55d95bfb5SMing Lei static void backing_file_tgt_deinit(struct ublk_dev *dev)
65d95bfb5SMing Lei {
75d95bfb5SMing Lei 	int i;
85d95bfb5SMing Lei 
95d95bfb5SMing Lei 	for (i = 1; i < dev->nr_fds; i++) {
105d95bfb5SMing Lei 		fsync(dev->fds[i]);
115d95bfb5SMing Lei 		close(dev->fds[i]);
125d95bfb5SMing Lei 	}
135d95bfb5SMing Lei }
145d95bfb5SMing Lei 
155d95bfb5SMing Lei static int backing_file_tgt_init(struct ublk_dev *dev)
165d95bfb5SMing Lei {
175d95bfb5SMing Lei 	int fd, i;
185d95bfb5SMing Lei 
195d95bfb5SMing Lei 	assert(dev->nr_fds == 1);
205d95bfb5SMing Lei 
215d95bfb5SMing Lei 	for (i = 0; i < dev->tgt.nr_backing_files; i++) {
225d95bfb5SMing Lei 		char *file = dev->tgt.backing_file[i];
235d95bfb5SMing Lei 		unsigned long bytes;
245d95bfb5SMing Lei 		struct stat st;
255d95bfb5SMing Lei 
265d95bfb5SMing Lei 		ublk_dbg(UBLK_DBG_DEV, "%s: file %d: %s\n", __func__, i, file);
275d95bfb5SMing Lei 
285d95bfb5SMing Lei 		fd = open(file, O_RDWR | O_DIRECT);
295d95bfb5SMing Lei 		if (fd < 0) {
305d95bfb5SMing Lei 			ublk_err("%s: backing file %s can't be opened: %s\n",
315d95bfb5SMing Lei 					__func__, file, strerror(errno));
325d95bfb5SMing Lei 			return -EBADF;
335d95bfb5SMing Lei 		}
345d95bfb5SMing Lei 
355d95bfb5SMing Lei 		if (fstat(fd, &st) < 0) {
365d95bfb5SMing Lei 			close(fd);
375d95bfb5SMing Lei 			return -EBADF;
385d95bfb5SMing Lei 		}
395d95bfb5SMing Lei 
405d95bfb5SMing Lei 		if (S_ISREG(st.st_mode))
415d95bfb5SMing Lei 			bytes = st.st_size;
425d95bfb5SMing Lei 		else if (S_ISBLK(st.st_mode)) {
435d95bfb5SMing Lei 			if (ioctl(fd, BLKGETSIZE64, &bytes) != 0)
445d95bfb5SMing Lei 				return -1;
455d95bfb5SMing Lei 		} else {
465d95bfb5SMing Lei 			return -EINVAL;
475d95bfb5SMing Lei 		}
485d95bfb5SMing Lei 
495d95bfb5SMing Lei 		dev->tgt.backing_file_size[i] = bytes;
505d95bfb5SMing Lei 		dev->fds[dev->nr_fds] = fd;
515d95bfb5SMing Lei 		dev->nr_fds += 1;
525d95bfb5SMing Lei 	}
535d95bfb5SMing Lei 
545d95bfb5SMing Lei 	return 0;
555d95bfb5SMing Lei }
565d95bfb5SMing Lei 
57bedc9cbcSMing Lei static enum io_uring_op ublk_to_uring_op(const struct ublksrv_io_desc *iod, int zc)
58bedc9cbcSMing Lei {
59bedc9cbcSMing Lei 	unsigned ublk_op = ublksrv_get_op(iod);
60bedc9cbcSMing Lei 
61bedc9cbcSMing Lei 	if (ublk_op == UBLK_IO_OP_READ)
62bedc9cbcSMing Lei 		return zc ? IORING_OP_READ_FIXED : IORING_OP_READ;
63bedc9cbcSMing Lei 	else if (ublk_op == UBLK_IO_OP_WRITE)
64bedc9cbcSMing Lei 		return zc ? IORING_OP_WRITE_FIXED : IORING_OP_WRITE;
65bedc9cbcSMing Lei 	assert(0);
66bedc9cbcSMing Lei }
67bedc9cbcSMing Lei 
68bedc9cbcSMing Lei static int loop_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag)
69bedc9cbcSMing Lei {
70bedc9cbcSMing Lei 	int zc = ublk_queue_use_zc(q);
71bedc9cbcSMing Lei 	enum io_uring_op op = ublk_to_uring_op(iod, zc);
72*f2639ed1SMing Lei 	struct io_uring_sqe *sqe[3];
73bedc9cbcSMing Lei 
74bedc9cbcSMing Lei 	if (!zc) {
75*f2639ed1SMing Lei 		ublk_queue_alloc_sqes(q, sqe, 1);
76*f2639ed1SMing Lei 		if (!sqe[0])
77bedc9cbcSMing Lei 			return -ENOMEM;
78bedc9cbcSMing Lei 
79*f2639ed1SMing Lei 		io_uring_prep_rw(op, sqe[0], 1 /*fds[1]*/,
80bedc9cbcSMing Lei 				(void *)iod->addr,
81bedc9cbcSMing Lei 				iod->nr_sectors << 9,
82bedc9cbcSMing Lei 				iod->start_sector << 9);
83*f2639ed1SMing Lei 		io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE);
84bedc9cbcSMing Lei 		q->io_inflight++;
85bedc9cbcSMing Lei 		/* bit63 marks us as tgt io */
86*f2639ed1SMing Lei 		sqe[0]->user_data = build_user_data(tag, op, UBLK_IO_TGT_NORMAL, 1);
87bedc9cbcSMing Lei 		return 0;
88bedc9cbcSMing Lei 	}
89bedc9cbcSMing Lei 
90*f2639ed1SMing Lei 	ublk_queue_alloc_sqes(q, sqe, 3);
91bedc9cbcSMing Lei 
92*f2639ed1SMing Lei 	io_uring_prep_buf_register(sqe[0], 0, tag, q->q_id, tag);
93*f2639ed1SMing Lei 	sqe[0]->user_data = build_user_data(tag, 0xfe, 1, 1);
94*f2639ed1SMing Lei 	sqe[0]->flags |= IOSQE_CQE_SKIP_SUCCESS;
95*f2639ed1SMing Lei 	sqe[0]->flags |= IOSQE_IO_LINK;
96bedc9cbcSMing Lei 
97*f2639ed1SMing Lei 	io_uring_prep_rw(op, sqe[1], 1 /*fds[1]*/, 0,
98bedc9cbcSMing Lei 		iod->nr_sectors << 9,
99bedc9cbcSMing Lei 		iod->start_sector << 9);
100*f2639ed1SMing Lei 	sqe[1]->buf_index = tag;
101*f2639ed1SMing Lei 	sqe[1]->flags |= IOSQE_FIXED_FILE;
102*f2639ed1SMing Lei 	sqe[1]->flags |= IOSQE_IO_LINK;
103*f2639ed1SMing Lei 	sqe[1]->user_data = build_user_data(tag, op, UBLK_IO_TGT_ZC_OP, 1);
104bedc9cbcSMing Lei 	q->io_inflight++;
105bedc9cbcSMing Lei 
106*f2639ed1SMing Lei 	io_uring_prep_buf_unregister(sqe[2], 0, tag, q->q_id, tag);
107*f2639ed1SMing Lei 	sqe[2]->user_data = build_user_data(tag, 0xff, UBLK_IO_TGT_ZC_BUF, 1);
108bedc9cbcSMing Lei 	q->io_inflight++;
109bedc9cbcSMing Lei 
110bedc9cbcSMing Lei 	return 0;
111bedc9cbcSMing Lei }
112bedc9cbcSMing Lei 
1135d95bfb5SMing Lei static int loop_queue_tgt_io(struct ublk_queue *q, int tag)
1145d95bfb5SMing Lei {
1155d95bfb5SMing Lei 	const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag);
1165d95bfb5SMing Lei 	unsigned ublk_op = ublksrv_get_op(iod);
117*f2639ed1SMing Lei 	struct io_uring_sqe *sqe[1];
1185d95bfb5SMing Lei 
1195d95bfb5SMing Lei 	switch (ublk_op) {
1205d95bfb5SMing Lei 	case UBLK_IO_OP_FLUSH:
121*f2639ed1SMing Lei 		ublk_queue_alloc_sqes(q, sqe, 1);
122*f2639ed1SMing Lei 		if (!sqe[0])
123bedc9cbcSMing Lei 			return -ENOMEM;
124*f2639ed1SMing Lei 		io_uring_prep_fsync(sqe[0], 1 /*fds[1]*/, IORING_FSYNC_DATASYNC);
125*f2639ed1SMing Lei 		io_uring_sqe_set_flags(sqe[0], IOSQE_FIXED_FILE);
126bedc9cbcSMing Lei 		q->io_inflight++;
127*f2639ed1SMing Lei 		sqe[0]->user_data = build_user_data(tag, ublk_op, UBLK_IO_TGT_NORMAL, 1);
1285d95bfb5SMing Lei 		break;
1295d95bfb5SMing Lei 	case UBLK_IO_OP_WRITE_ZEROES:
1305d95bfb5SMing Lei 	case UBLK_IO_OP_DISCARD:
1315d95bfb5SMing Lei 		return -ENOTSUP;
1325d95bfb5SMing Lei 	case UBLK_IO_OP_READ:
1335d95bfb5SMing Lei 	case UBLK_IO_OP_WRITE:
134bedc9cbcSMing Lei 		loop_queue_tgt_rw_io(q, iod, tag);
1355d95bfb5SMing Lei 		break;
1365d95bfb5SMing Lei 	default:
1375d95bfb5SMing Lei 		return -EINVAL;
1385d95bfb5SMing Lei 	}
1395d95bfb5SMing Lei 
1405d95bfb5SMing Lei 	ublk_dbg(UBLK_DBG_IO, "%s: tag %d ublk io %x %llx %u\n", __func__, tag,
1415d95bfb5SMing Lei 			iod->op_flags, iod->start_sector, iod->nr_sectors << 9);
1425d95bfb5SMing Lei 	return 1;
1435d95bfb5SMing Lei }
1445d95bfb5SMing Lei 
1455d95bfb5SMing Lei static int ublk_loop_queue_io(struct ublk_queue *q, int tag)
1465d95bfb5SMing Lei {
1475d95bfb5SMing Lei 	int queued = loop_queue_tgt_io(q, tag);
1485d95bfb5SMing Lei 
1495d95bfb5SMing Lei 	if (queued < 0)
1505d95bfb5SMing Lei 		ublk_complete_io(q, tag, queued);
1515d95bfb5SMing Lei 
1525d95bfb5SMing Lei 	return 0;
1535d95bfb5SMing Lei }
1545d95bfb5SMing Lei 
1555d95bfb5SMing Lei static void ublk_loop_io_done(struct ublk_queue *q, int tag,
1565d95bfb5SMing Lei 		const struct io_uring_cqe *cqe)
1575d95bfb5SMing Lei {
1585d95bfb5SMing Lei 	int cqe_tag = user_data_to_tag(cqe->user_data);
159bedc9cbcSMing Lei 	unsigned tgt_data = user_data_to_tgt_data(cqe->user_data);
160bedc9cbcSMing Lei 	int res = cqe->res;
1615d95bfb5SMing Lei 
162bedc9cbcSMing Lei 	if (res < 0 || tgt_data == UBLK_IO_TGT_NORMAL)
163bedc9cbcSMing Lei 		goto complete;
164bedc9cbcSMing Lei 
165bedc9cbcSMing Lei 	if (tgt_data == UBLK_IO_TGT_ZC_OP) {
166bedc9cbcSMing Lei 		ublk_set_io_res(q, tag, cqe->res);
167bedc9cbcSMing Lei 		goto exit;
168bedc9cbcSMing Lei 	}
169bedc9cbcSMing Lei 	assert(tgt_data == UBLK_IO_TGT_ZC_BUF);
170bedc9cbcSMing Lei 	res = ublk_get_io_res(q, tag);
171bedc9cbcSMing Lei complete:
1725d95bfb5SMing Lei 	assert(tag == cqe_tag);
173bedc9cbcSMing Lei 	ublk_complete_io(q, tag, res);
174bedc9cbcSMing Lei exit:
1755d95bfb5SMing Lei 	q->io_inflight--;
1765d95bfb5SMing Lei }
1775d95bfb5SMing Lei 
1785d95bfb5SMing Lei static int ublk_loop_tgt_init(struct ublk_dev *dev)
1795d95bfb5SMing Lei {
1805d95bfb5SMing Lei 	unsigned long long bytes;
1815d95bfb5SMing Lei 	int ret;
1825d95bfb5SMing Lei 	struct ublk_params p = {
183bedc9cbcSMing Lei 		.types = UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DMA_ALIGN,
1845d95bfb5SMing Lei 		.basic = {
18596af5af4SMing Lei 			.attrs = UBLK_ATTR_VOLATILE_CACHE,
1865d95bfb5SMing Lei 			.logical_bs_shift	= 9,
1875d95bfb5SMing Lei 			.physical_bs_shift	= 12,
1885d95bfb5SMing Lei 			.io_opt_shift	= 12,
1895d95bfb5SMing Lei 			.io_min_shift	= 9,
1905d95bfb5SMing Lei 			.max_sectors = dev->dev_info.max_io_buf_bytes >> 9,
1915d95bfb5SMing Lei 		},
192bedc9cbcSMing Lei 		.dma = {
193bedc9cbcSMing Lei 			.alignment = 511,
194bedc9cbcSMing Lei 		},
1955d95bfb5SMing Lei 	};
1965d95bfb5SMing Lei 
1975d95bfb5SMing Lei 	ret = backing_file_tgt_init(dev);
1985d95bfb5SMing Lei 	if (ret)
1995d95bfb5SMing Lei 		return ret;
2005d95bfb5SMing Lei 
201ffde32a4SMing Lei 	if (dev->tgt.nr_backing_files != 1)
202ffde32a4SMing Lei 		return -EINVAL;
203ffde32a4SMing Lei 
2045d95bfb5SMing Lei 	bytes = dev->tgt.backing_file_size[0];
2055d95bfb5SMing Lei 	dev->tgt.dev_size = bytes;
2065d95bfb5SMing Lei 	p.basic.dev_sectors = bytes >> 9;
2075d95bfb5SMing Lei 	dev->tgt.params = p;
2085d95bfb5SMing Lei 
2095d95bfb5SMing Lei 	return 0;
2105d95bfb5SMing Lei }
2115d95bfb5SMing Lei 
2125d95bfb5SMing Lei const struct ublk_tgt_ops loop_tgt_ops = {
2135d95bfb5SMing Lei 	.name = "loop",
2145d95bfb5SMing Lei 	.init_tgt = ublk_loop_tgt_init,
2155d95bfb5SMing Lei 	.deinit_tgt = backing_file_tgt_deinit,
2165d95bfb5SMing Lei 	.queue_io = ublk_loop_queue_io,
2175d95bfb5SMing Lei 	.tgt_io_done = ublk_loop_io_done,
2185d95bfb5SMing Lei };
219