xref: /linux/tools/testing/selftests/ublk/stripe.c (revision 91928e0d3cc29789f4483bffee5f36218f23942b)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 #include "kublk.h"
4 
5 #define NR_STRIPE  MAX_BACK_FILES
6 
7 struct stripe_conf {
8 	unsigned nr_files;
9 	unsigned shift;
10 };
11 
12 struct stripe {
13 	loff_t 		start;
14 	unsigned 	nr_sects;
15 	int 		seq;
16 
17 	struct iovec 	*vec;
18 	unsigned 	nr_vec;
19 	unsigned 	cap;
20 };
21 
22 struct stripe_array {
23 	struct stripe 	s[NR_STRIPE];
24 	unsigned 	nr;
25 	struct iovec 	_vec[];
26 };
27 
get_chunk_shift(const struct ublk_queue * q)28 static inline const struct stripe_conf *get_chunk_shift(const struct ublk_queue *q)
29 {
30 	return (struct stripe_conf *)q->dev->private_data;
31 }
32 
calculate_nr_vec(const struct stripe_conf * conf,const struct ublksrv_io_desc * iod)33 static inline unsigned calculate_nr_vec(const struct stripe_conf *conf,
34 		const struct ublksrv_io_desc *iod)
35 {
36 	const unsigned shift = conf->shift - 9;
37 	const unsigned unit_sects = conf->nr_files << shift;
38 	loff_t start = iod->start_sector;
39 	loff_t end = start + iod->nr_sectors;
40 
41 	return (end / unit_sects) - (start / unit_sects) + 1;
42 }
43 
alloc_stripe_array(const struct stripe_conf * conf,const struct ublksrv_io_desc * iod)44 static struct stripe_array *alloc_stripe_array(const struct stripe_conf *conf,
45 		const struct ublksrv_io_desc *iod)
46 {
47 	unsigned nr_vecs = calculate_nr_vec(conf, iod);
48 	unsigned total = nr_vecs * conf->nr_files;
49 	struct stripe_array *s;
50 	int i;
51 
52 	s = malloc(sizeof(*s) + total * sizeof(struct iovec));
53 
54 	s->nr = 0;
55 	for (i = 0; i < conf->nr_files; i++) {
56 		struct stripe *t = &s->s[i];
57 
58 		t->nr_vec = 0;
59 		t->vec = &s->_vec[i * nr_vecs];
60 		t->nr_sects = 0;
61 		t->cap = nr_vecs;
62 	}
63 
64 	return s;
65 }
66 
free_stripe_array(struct stripe_array * s)67 static void free_stripe_array(struct stripe_array *s)
68 {
69 	free(s);
70 }
71 
calculate_stripe_array(const struct stripe_conf * conf,const struct ublksrv_io_desc * iod,struct stripe_array * s)72 static void calculate_stripe_array(const struct stripe_conf *conf,
73 		const struct ublksrv_io_desc *iod, struct stripe_array *s)
74 {
75 	const unsigned shift = conf->shift - 9;
76 	const unsigned chunk_sects = 1 << shift;
77 	const unsigned unit_sects = conf->nr_files << shift;
78 	off64_t start = iod->start_sector;
79 	off64_t end = start + iod->nr_sectors;
80 	unsigned long done = 0;
81 	unsigned idx = 0;
82 
83 	while (start < end) {
84 		unsigned nr_sects = chunk_sects - (start & (chunk_sects - 1));
85 		loff_t unit_off = (start / unit_sects) * unit_sects;
86 		unsigned seq = (start - unit_off) >> shift;
87 		struct stripe *this = &s->s[idx];
88 		loff_t stripe_off = (unit_off / conf->nr_files) +
89 			(start & (chunk_sects - 1));
90 
91 		if (nr_sects > end - start)
92 			nr_sects = end - start;
93 		if (this->nr_sects == 0) {
94 			this->nr_sects = nr_sects;
95 			this->start = stripe_off;
96 			this->seq = seq;
97 			s->nr += 1;
98 		} else {
99 			assert(seq == this->seq);
100 			assert(this->start + this->nr_sects == stripe_off);
101 			this->nr_sects += nr_sects;
102 		}
103 
104 		assert(this->nr_vec < this->cap);
105 		this->vec[this->nr_vec].iov_base = (void *)(iod->addr + done);
106 		this->vec[this->nr_vec++].iov_len = nr_sects << 9;
107 
108 		start += nr_sects;
109 		done += nr_sects << 9;
110 		idx = (idx + 1) % conf->nr_files;
111 	}
112 }
113 
stripe_to_uring_op(const struct ublksrv_io_desc * iod)114 static inline enum io_uring_op stripe_to_uring_op(const struct ublksrv_io_desc *iod)
115 {
116 	unsigned ublk_op = ublksrv_get_op(iod);
117 
118 	if (ublk_op == UBLK_IO_OP_READ)
119 		return IORING_OP_READV;
120 	else if (ublk_op == UBLK_IO_OP_WRITE)
121 		return IORING_OP_WRITEV;
122 	assert(0);
123 }
124 
stripe_queue_tgt_rw_io(struct ublk_queue * q,const struct ublksrv_io_desc * iod,int tag)125 static int stripe_queue_tgt_rw_io(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag)
126 {
127 	const struct stripe_conf *conf = get_chunk_shift(q);
128 	enum io_uring_op op = stripe_to_uring_op(iod);
129 	struct io_uring_sqe *sqe[NR_STRIPE];
130 	struct stripe_array *s = alloc_stripe_array(conf, iod);
131 	struct ublk_io *io = ublk_get_io(q, tag);
132 	int i;
133 
134 	io->private_data = s;
135 	calculate_stripe_array(conf, iod, s);
136 
137 	ublk_queue_alloc_sqes(q, sqe, s->nr);
138 	for (i = 0; i < s->nr; i++) {
139 		struct stripe *t = &s->s[i];
140 
141 		io_uring_prep_rw(op, sqe[i],
142 				t->seq + 1,
143 				(void *)t->vec,
144 				t->nr_vec,
145 				t->start << 9);
146 		io_uring_sqe_set_flags(sqe[i], IOSQE_FIXED_FILE);
147 		/* bit63 marks us as tgt io */
148 		sqe[i]->user_data = build_user_data(tag, ublksrv_get_op(iod), i, 1);
149 	}
150 	return s->nr;
151 }
152 
handle_flush(struct ublk_queue * q,const struct ublksrv_io_desc * iod,int tag)153 static int handle_flush(struct ublk_queue *q, const struct ublksrv_io_desc *iod, int tag)
154 {
155 	const struct stripe_conf *conf = get_chunk_shift(q);
156 	struct io_uring_sqe *sqe[NR_STRIPE];
157 	int i;
158 
159 	ublk_queue_alloc_sqes(q, sqe, conf->nr_files);
160 	for (i = 0; i < conf->nr_files; i++) {
161 		io_uring_prep_fsync(sqe[i], i + 1, IORING_FSYNC_DATASYNC);
162 		io_uring_sqe_set_flags(sqe[i], IOSQE_FIXED_FILE);
163 		sqe[i]->user_data = build_user_data(tag, UBLK_IO_OP_FLUSH, 0, 1);
164 	}
165 	return conf->nr_files;
166 }
167 
stripe_queue_tgt_io(struct ublk_queue * q,int tag)168 static int stripe_queue_tgt_io(struct ublk_queue *q, int tag)
169 {
170 	const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag);
171 	unsigned ublk_op = ublksrv_get_op(iod);
172 	int ret = 0;
173 
174 	switch (ublk_op) {
175 	case UBLK_IO_OP_FLUSH:
176 		ret = handle_flush(q, iod, tag);
177 		break;
178 	case UBLK_IO_OP_WRITE_ZEROES:
179 	case UBLK_IO_OP_DISCARD:
180 		ret = -ENOTSUP;
181 		break;
182 	case UBLK_IO_OP_READ:
183 	case UBLK_IO_OP_WRITE:
184 		ret = stripe_queue_tgt_rw_io(q, iod, tag);
185 		break;
186 	default:
187 		ret = -EINVAL;
188 		break;
189 	}
190 	ublk_dbg(UBLK_DBG_IO, "%s: tag %d ublk io %x %llx %u ret %d\n", __func__, tag,
191 			iod->op_flags, iod->start_sector, iod->nr_sectors << 9, ret);
192 	return ret;
193 }
194 
ublk_stripe_queue_io(struct ublk_queue * q,int tag)195 static int ublk_stripe_queue_io(struct ublk_queue *q, int tag)
196 {
197 	int queued = stripe_queue_tgt_io(q, tag);
198 
199 	ublk_queued_tgt_io(q, tag, queued);
200 	return 0;
201 }
202 
ublk_stripe_io_done(struct ublk_queue * q,int tag,const struct io_uring_cqe * cqe)203 static void ublk_stripe_io_done(struct ublk_queue *q, int tag,
204 		const struct io_uring_cqe *cqe)
205 {
206 	const struct ublksrv_io_desc *iod = ublk_get_iod(q, tag);
207 	unsigned op = user_data_to_op(cqe->user_data);
208 	struct ublk_io *io = ublk_get_io(q, tag);
209 	int res = cqe->res;
210 
211 	if (res < 0) {
212 		if (!io->result)
213 			io->result = res;
214 		ublk_err("%s: io failure %d tag %u\n", __func__, res, tag);
215 	}
216 
217 	/* fail short READ/WRITE simply */
218 	if (op == UBLK_IO_OP_READ || op == UBLK_IO_OP_WRITE) {
219 		unsigned seq = user_data_to_tgt_data(cqe->user_data);
220 		struct stripe_array *s = io->private_data;
221 
222 		if (res < s->s[seq].vec->iov_len)
223 			io->result = -EIO;
224 	}
225 
226 	if (ublk_completed_tgt_io(q, tag)) {
227 		int res = io->result;
228 
229 		if (!res)
230 			res = iod->nr_sectors << 9;
231 
232 		ublk_complete_io(q, tag, res);
233 
234 		free_stripe_array(io->private_data);
235 		io->private_data = NULL;
236 	}
237 }
238 
ublk_stripe_tgt_init(const struct dev_ctx * ctx,struct ublk_dev * dev)239 static int ublk_stripe_tgt_init(const struct dev_ctx *ctx, struct ublk_dev *dev)
240 {
241 	struct ublk_params p = {
242 		.types = UBLK_PARAM_TYPE_BASIC,
243 		.basic = {
244 			.attrs = UBLK_ATTR_VOLATILE_CACHE,
245 			.logical_bs_shift	= 9,
246 			.physical_bs_shift	= 12,
247 			.io_opt_shift	= 12,
248 			.io_min_shift	= 9,
249 			.max_sectors = dev->dev_info.max_io_buf_bytes >> 9,
250 		},
251 	};
252 	unsigned chunk_size = ctx->chunk_size;
253 	struct stripe_conf *conf;
254 	unsigned chunk_shift;
255 	loff_t bytes = 0;
256 	int ret, i;
257 
258 	if ((chunk_size & (chunk_size - 1)) || !chunk_size) {
259 		ublk_err("invalid chunk size %u\n", chunk_size);
260 		return -EINVAL;
261 	}
262 
263 	if (chunk_size < 4096 || chunk_size > 512 * 1024) {
264 		ublk_err("invalid chunk size %u\n", chunk_size);
265 		return -EINVAL;
266 	}
267 
268 	chunk_shift = ilog2(chunk_size);
269 
270 	ret = backing_file_tgt_init(dev);
271 	if (ret)
272 		return ret;
273 
274 	if (!dev->tgt.nr_backing_files || dev->tgt.nr_backing_files > NR_STRIPE)
275 		return -EINVAL;
276 
277 	assert(dev->nr_fds == dev->tgt.nr_backing_files + 1);
278 
279 	for (i = 0; i < dev->tgt.nr_backing_files; i++)
280 		dev->tgt.backing_file_size[i] &= ~((1 << chunk_shift) - 1);
281 
282 	for (i = 0; i < dev->tgt.nr_backing_files; i++) {
283 		unsigned long size = dev->tgt.backing_file_size[i];
284 
285 		if (size != dev->tgt.backing_file_size[0])
286 			return -EINVAL;
287 		bytes += size;
288 	}
289 
290 	conf = malloc(sizeof(*conf));
291 	conf->shift = chunk_shift;
292 	conf->nr_files = dev->tgt.nr_backing_files;
293 
294 	dev->private_data = conf;
295 	dev->tgt.dev_size = bytes;
296 	p.basic.dev_sectors = bytes >> 9;
297 	dev->tgt.params = p;
298 	dev->tgt.sq_depth = dev->dev_info.queue_depth * conf->nr_files;
299 	dev->tgt.cq_depth = dev->dev_info.queue_depth * conf->nr_files;
300 
301 	printf("%s: shift %u files %u\n", __func__, conf->shift, conf->nr_files);
302 
303 	return 0;
304 }
305 
ublk_stripe_tgt_deinit(struct ublk_dev * dev)306 static void ublk_stripe_tgt_deinit(struct ublk_dev *dev)
307 {
308 	free(dev->private_data);
309 	backing_file_tgt_deinit(dev);
310 }
311 
312 const struct ublk_tgt_ops stripe_tgt_ops = {
313 	.name = "stripe",
314 	.init_tgt = ublk_stripe_tgt_init,
315 	.deinit_tgt = ublk_stripe_tgt_deinit,
316 	.queue_io = ublk_stripe_queue_io,
317 	.tgt_io_done = ublk_stripe_io_done,
318 };
319