1 /*
2  * Copyright (C) 2019 Western Digital Corporation or its affiliates.
3  *
4  * This file is released under the GPL.
5  *
6  * libzbc engine
7  * IO engine using libzbc library to talk to SMR disks.
8  */
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <libzbc/zbc.h>
13 
14 #include "fio.h"
15 #include "err.h"
16 #include "zbd_types.h"
17 #include "zbd.h"
18 
19 struct libzbc_data {
20 	struct zbc_device	*zdev;
21 	enum zbc_dev_model	model;
22 	uint64_t		nr_sectors;
23 	uint32_t		max_open_seq_req;
24 };
25 
libzbc_get_dev_info(struct libzbc_data * ld,struct fio_file * f)26 static int libzbc_get_dev_info(struct libzbc_data *ld, struct fio_file *f)
27 {
28 	struct zbc_device_info *zinfo;
29 
30 	zinfo = calloc(1, sizeof(*zinfo));
31 	if (!zinfo)
32 		return -ENOMEM;
33 
34 	zbc_get_device_info(ld->zdev, zinfo);
35 	ld->model = zinfo->zbd_model;
36 	ld->nr_sectors = zinfo->zbd_sectors;
37 	ld->max_open_seq_req = zinfo->zbd_max_nr_open_seq_req;
38 
39 	dprint(FD_ZBD, "%s: vendor_id:%s, type: %s, model: %s\n",
40 	       f->file_name, zinfo->zbd_vendor_id,
41 	       zbc_device_type_str(zinfo->zbd_type),
42 	       zbc_device_model_str(zinfo->zbd_model));
43 
44 	free(zinfo);
45 
46 	return 0;
47 }
48 
libzbc_open_dev(struct thread_data * td,struct fio_file * f,struct libzbc_data ** p_ld)49 static int libzbc_open_dev(struct thread_data *td, struct fio_file *f,
50 			   struct libzbc_data **p_ld)
51 {
52 	struct libzbc_data *ld = td->io_ops_data;
53 	int ret, flags = OS_O_DIRECT;
54 
55 	if (ld) {
56 		/* Already open */
57 		assert(ld->zdev);
58 		goto out;
59 	}
60 
61 	if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR) {
62 		td_verror(td, EINVAL, "wrong file type");
63 		log_err("ioengine libzbc only works on block or character devices\n");
64 		return -EINVAL;
65 	}
66 
67 	if (td_write(td) || td_trim(td)) {
68 		if (!read_only)
69 			flags |= O_RDWR;
70 	} else if (td_read(td)) {
71 		if (f->filetype == FIO_TYPE_CHAR && !read_only)
72 			flags |= O_RDWR;
73 		else
74 			flags |= O_RDONLY;
75 	}
76 
77 	if (td->o.oatomic) {
78 		td_verror(td, EINVAL, "libzbc does not support O_ATOMIC");
79 		log_err("%s: libzbc does not support O_ATOMIC\n", f->file_name);
80 		return -EINVAL;
81 	}
82 
83 	ld = calloc(1, sizeof(*ld));
84 	if (!ld)
85 		return -ENOMEM;
86 
87 	ret = zbc_open(f->file_name,
88 		       flags | ZBC_O_DRV_SCSI | ZBC_O_DRV_ATA,
89 		       &ld->zdev);
90 	if (ret) {
91 		log_err("%s: zbc_open() failed, err=%d\n",
92 			f->file_name, ret);
93 		goto err;
94 	}
95 
96 	ret = libzbc_get_dev_info(ld, f);
97 	if (ret)
98 		goto err_close;
99 
100 	td->io_ops_data = ld;
101 out:
102 	if (p_ld)
103 		*p_ld = ld;
104 
105 	return 0;
106 
107 err_close:
108 	zbc_close(ld->zdev);
109 err:
110 	free(ld);
111 	return ret;
112 }
113 
libzbc_close_dev(struct thread_data * td)114 static int libzbc_close_dev(struct thread_data *td)
115 {
116 	struct libzbc_data *ld = td->io_ops_data;
117 	int ret = 0;
118 
119 	td->io_ops_data = NULL;
120 	if (ld) {
121 		if (ld->zdev)
122 			ret = zbc_close(ld->zdev);
123 		free(ld);
124 	}
125 
126 	return ret;
127 }
libzbc_open_file(struct thread_data * td,struct fio_file * f)128 static int libzbc_open_file(struct thread_data *td, struct fio_file *f)
129 {
130 	return libzbc_open_dev(td, f, NULL);
131 }
132 
libzbc_close_file(struct thread_data * td,struct fio_file * f)133 static int libzbc_close_file(struct thread_data *td, struct fio_file *f)
134 {
135 	int ret;
136 
137 	ret = libzbc_close_dev(td);
138 	if (ret)
139 		log_err("%s: close device failed err %d\n",
140 			f->file_name, ret);
141 
142 	return ret;
143 }
144 
libzbc_cleanup(struct thread_data * td)145 static void libzbc_cleanup(struct thread_data *td)
146 {
147 	libzbc_close_dev(td);
148 }
149 
libzbc_invalidate(struct thread_data * td,struct fio_file * f)150 static int libzbc_invalidate(struct thread_data *td, struct fio_file *f)
151 {
152 	/* Passthrough IO do not cache data. Nothing to do */
153 	return 0;
154 }
155 
libzbc_get_file_size(struct thread_data * td,struct fio_file * f)156 static int libzbc_get_file_size(struct thread_data *td, struct fio_file *f)
157 {
158 	struct libzbc_data *ld;
159 	int ret;
160 
161 	if (fio_file_size_known(f))
162 		return 0;
163 
164 	ret = libzbc_open_dev(td, f, &ld);
165 	if (ret)
166 		return ret;
167 
168 	f->real_file_size = ld->nr_sectors << 9;
169 	fio_file_set_size_known(f);
170 
171 	return 0;
172 }
173 
libzbc_get_zoned_model(struct thread_data * td,struct fio_file * f,enum zbd_zoned_model * model)174 static int libzbc_get_zoned_model(struct thread_data *td, struct fio_file *f,
175 				  enum zbd_zoned_model *model)
176 {
177 	struct libzbc_data *ld;
178 	int ret;
179 
180 	if (f->filetype != FIO_TYPE_BLOCK && f->filetype != FIO_TYPE_CHAR)
181 		return -EINVAL;
182 
183 	ret = libzbc_open_dev(td, f, &ld);
184 	if (ret)
185 		return ret;
186 
187 	switch (ld->model) {
188 	case ZBC_DM_HOST_AWARE:
189 		*model = ZBD_HOST_AWARE;
190 		break;
191 	case ZBC_DM_HOST_MANAGED:
192 		*model = ZBD_HOST_MANAGED;
193 		break;
194 	default:
195 		*model = ZBD_NONE;
196 		break;
197 	}
198 
199 	return 0;
200 }
201 
libzbc_report_zones(struct thread_data * td,struct fio_file * f,uint64_t offset,struct zbd_zone * zbdz,unsigned int nr_zones)202 static int libzbc_report_zones(struct thread_data *td, struct fio_file *f,
203 			       uint64_t offset, struct zbd_zone *zbdz,
204 			       unsigned int nr_zones)
205 {
206 	struct libzbc_data *ld;
207 	uint64_t sector = offset >> 9;
208 	struct zbc_zone *zones;
209 	unsigned int i;
210 	int ret;
211 
212 	ret = libzbc_open_dev(td, f, &ld);
213 	if (ret)
214 		return ret;
215 
216 	if (sector >= ld->nr_sectors)
217 		return 0;
218 
219 	zones = calloc(nr_zones, sizeof(struct zbc_zone));
220 	if (!zones) {
221 		ret = -ENOMEM;
222 		goto out;
223 	}
224 
225 	ret = zbc_report_zones(ld->zdev, sector, ZBC_RO_ALL, zones, &nr_zones);
226 	if (ret < 0) {
227 		log_err("%s: zbc_report_zones failed, err=%d\n",
228 			f->file_name, ret);
229 		goto out;
230 	}
231 
232 	for (i = 0; i < nr_zones; i++, zbdz++) {
233 		zbdz->start = zones[i].zbz_start << 9;
234 		zbdz->len = zones[i].zbz_length << 9;
235 		zbdz->wp = zones[i].zbz_write_pointer << 9;
236 		/*
237 		 * ZBC/ZAC do not define zone capacity, so use the zone size as
238 		 * the zone capacity.
239 		 */
240 		zbdz->capacity = zbdz->len;
241 
242 		switch (zones[i].zbz_type) {
243 		case ZBC_ZT_CONVENTIONAL:
244 			zbdz->type = ZBD_ZONE_TYPE_CNV;
245 			break;
246 		case ZBC_ZT_SEQUENTIAL_REQ:
247 			zbdz->type = ZBD_ZONE_TYPE_SWR;
248 			break;
249 		case ZBC_ZT_SEQUENTIAL_PREF:
250 			zbdz->type = ZBD_ZONE_TYPE_SWP;
251 			break;
252 		default:
253 			td_verror(td, errno, "invalid zone type");
254 			log_err("%s: invalid type for zone at sector %llu.\n",
255 				f->file_name, (unsigned long long)zbdz->start);
256 			ret = -EIO;
257 			goto out;
258 		}
259 
260 		switch (zones[i].zbz_condition) {
261 		case ZBC_ZC_NOT_WP:
262 			zbdz->cond = ZBD_ZONE_COND_NOT_WP;
263 			break;
264 		case ZBC_ZC_EMPTY:
265 			zbdz->cond = ZBD_ZONE_COND_EMPTY;
266 			break;
267 		case ZBC_ZC_IMP_OPEN:
268 			zbdz->cond = ZBD_ZONE_COND_IMP_OPEN;
269 			break;
270 		case ZBC_ZC_EXP_OPEN:
271 			zbdz->cond = ZBD_ZONE_COND_EXP_OPEN;
272 			break;
273 		case ZBC_ZC_CLOSED:
274 			zbdz->cond = ZBD_ZONE_COND_CLOSED;
275 			break;
276 		case ZBC_ZC_FULL:
277 			zbdz->cond = ZBD_ZONE_COND_FULL;
278 			break;
279 		case ZBC_ZC_RDONLY:
280 		case ZBC_ZC_OFFLINE:
281 		default:
282 			/* Treat all these conditions as offline (don't use!) */
283 			zbdz->cond = ZBD_ZONE_COND_OFFLINE;
284 			zbdz->wp = zbdz->start;
285 		}
286 	}
287 
288 	ret = nr_zones;
289 out:
290 	free(zones);
291 	return ret;
292 }
293 
libzbc_reset_wp(struct thread_data * td,struct fio_file * f,uint64_t offset,uint64_t length)294 static int libzbc_reset_wp(struct thread_data *td, struct fio_file *f,
295 			   uint64_t offset, uint64_t length)
296 {
297 	struct libzbc_data *ld = td->io_ops_data;
298 	uint64_t sector = offset >> 9;
299 	uint64_t end_sector = (offset + length) >> 9;
300 	unsigned int nr_zones;
301 	struct zbc_errno err;
302 	int i, ret;
303 
304 	assert(ld);
305 	assert(ld->zdev);
306 
307 	nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
308 	if (!sector && end_sector >= ld->nr_sectors) {
309 		/* Reset all zones */
310 		ret = zbc_reset_zone(ld->zdev, 0, ZBC_OP_ALL_ZONES);
311 		if (ret)
312 			goto err;
313 
314 		return 0;
315 	}
316 
317 	for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
318 		ret = zbc_reset_zone(ld->zdev, sector, 0);
319 		if (ret)
320 			goto err;
321 	}
322 
323 	return 0;
324 
325 err:
326 	zbc_errno(ld->zdev, &err);
327 	td_verror(td, errno, "zbc_reset_zone failed");
328 	if (err.sk)
329 		log_err("%s: reset wp failed %s:%s\n",
330 			f->file_name,
331 			zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
332 	return -ret;
333 }
334 
libzbc_get_max_open_zones(struct thread_data * td,struct fio_file * f,unsigned int * max_open_zones)335 static int libzbc_get_max_open_zones(struct thread_data *td, struct fio_file *f,
336 				     unsigned int *max_open_zones)
337 {
338 	struct libzbc_data *ld;
339 	int ret;
340 
341 	ret = libzbc_open_dev(td, f, &ld);
342 	if (ret)
343 		return ret;
344 
345 	if (ld->max_open_seq_req == ZBC_NO_LIMIT)
346 		*max_open_zones = 0;
347 	else
348 		*max_open_zones = ld->max_open_seq_req;
349 
350 	return 0;
351 }
352 
libzbc_rw(struct thread_data * td,struct io_u * io_u)353 ssize_t libzbc_rw(struct thread_data *td, struct io_u *io_u)
354 {
355 	struct libzbc_data *ld = td->io_ops_data;
356 	struct fio_file *f = io_u->file;
357 	uint64_t sector = io_u->offset >> 9;
358 	size_t count = io_u->xfer_buflen >> 9;
359 	struct zbc_errno err;
360 	ssize_t ret;
361 
362 	if (io_u->ddir == DDIR_WRITE)
363 		ret = zbc_pwrite(ld->zdev, io_u->xfer_buf, count, sector);
364 	else
365 		ret = zbc_pread(ld->zdev, io_u->xfer_buf, count, sector);
366 	if (ret == count)
367 		return ret;
368 
369 	if (ret > 0) {
370 		log_err("Short %s, len=%zu, ret=%zd\n",
371 			io_u->ddir == DDIR_READ ? "read" : "write",
372 			count << 9, ret << 9);
373 		return -EIO;
374 	}
375 
376 	/* I/O error */
377 	zbc_errno(ld->zdev, &err);
378 	td_verror(td, errno, "libzbc i/o failed");
379 	if (err.sk) {
380 		log_err("%s: op %u offset %llu+%llu failed (%s:%s), err %zd\n",
381 			f->file_name, io_u->ddir,
382 			io_u->offset, io_u->xfer_buflen,
383 			zbc_sk_str(err.sk),
384 			zbc_asc_ascq_str(err.asc_ascq), ret);
385 	} else {
386 		log_err("%s: op %u offset %llu+%llu failed, err %zd\n",
387 			f->file_name, io_u->ddir,
388 			io_u->offset, io_u->xfer_buflen, ret);
389 	}
390 
391 	return -EIO;
392 }
393 
libzbc_queue(struct thread_data * td,struct io_u * io_u)394 static enum fio_q_status libzbc_queue(struct thread_data *td, struct io_u *io_u)
395 {
396 	struct libzbc_data *ld = td->io_ops_data;
397 	struct fio_file *f = io_u->file;
398 	ssize_t ret = 0;
399 
400 	fio_ro_check(td, io_u);
401 
402 	dprint(FD_ZBD, "%p:%s: libzbc queue %llu\n",
403 	       td, f->file_name, io_u->offset);
404 
405 	if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
406 		ret = libzbc_rw(td, io_u);
407 	} else if (ddir_sync(io_u->ddir)) {
408 		ret = zbc_flush(ld->zdev);
409 		if (ret)
410 			log_err("zbc_flush error %zd\n", ret);
411 	} else if (io_u->ddir == DDIR_TRIM) {
412 		ret = zbd_do_io_u_trim(td, io_u);
413 		if (!ret)
414 			ret = EINVAL;
415 	} else {
416 		log_err("Unsupported operation %u\n", io_u->ddir);
417 		ret = -EINVAL;
418 	}
419 	if (ret < 0)
420 		io_u->error = -ret;
421 
422 	return FIO_Q_COMPLETED;
423 }
424 
425 FIO_STATIC struct ioengine_ops ioengine = {
426 	.name			= "libzbc",
427 	.version		= FIO_IOOPS_VERSION,
428 	.open_file		= libzbc_open_file,
429 	.close_file		= libzbc_close_file,
430 	.cleanup		= libzbc_cleanup,
431 	.invalidate		= libzbc_invalidate,
432 	.get_file_size		= libzbc_get_file_size,
433 	.get_zoned_model	= libzbc_get_zoned_model,
434 	.report_zones		= libzbc_report_zones,
435 	.reset_wp		= libzbc_reset_wp,
436 	.get_max_open_zones	= libzbc_get_max_open_zones,
437 	.queue			= libzbc_queue,
438 	.flags			= FIO_SYNCIO | FIO_NOEXTEND | FIO_RAWIO,
439 };
440 
fio_libzbc_register(void)441 static void fio_init fio_libzbc_register(void)
442 {
443 	register_ioengine(&ioengine);
444 }
445 
fio_libzbc_unregister(void)446 static void fio_exit fio_libzbc_unregister(void)
447 {
448 	unregister_ioengine(&ioengine);
449 }
450