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