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