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