zbd: introduce zone_unlock()
[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;
976fb54e 50 int ret, flags = OS_O_DIRECT;
56a19325
DF
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
976fb54e 64 if (td_write(td)) {
56a19325
DF
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");
976fb54e
DF
74 log_err("%s: libzbc does not support trim\n", f->file_name);
75 return -EINVAL;
56a19325
DF
76 }
77
976fb54e 78 if (td->o.oatomic) {
56a19325 79 td_verror(td, EINVAL, "libzbc does not support O_ATOMIC");
976fb54e
DF
80 log_err("%s: libzbc does not support O_ATOMIC\n", f->file_name);
81 return -EINVAL;
82 }
56a19325
DF
83
84 ld = calloc(1, sizeof(*ld));
85 if (!ld)
86 return -ENOMEM;
87
88 ret = zbc_open(f->file_name,
89 flags | ZBC_O_DRV_SCSI | ZBC_O_DRV_ATA, &ld->zdev);
90 if (ret) {
91 log_err("%s: zbc_open() failed, err=%d\n",
92 f->file_name, ret);
891660e4 93 goto err;
56a19325
DF
94 }
95
96 ret = libzbc_get_dev_info(ld, f);
891660e4
DF
97 if (ret)
98 goto err_close;
56a19325
DF
99
100 td->io_ops_data = ld;
101out:
102 if (p_ld)
103 *p_ld = ld;
104
105 return 0;
891660e4
DF
106
107err_close:
108 zbc_close(ld->zdev);
109err:
110 free(ld);
111 return ret;
56a19325
DF
112}
113
114static 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}
128static int libzbc_open_file(struct thread_data *td, struct fio_file *f)
129{
130 return libzbc_open_dev(td, f, NULL);
131}
132
133static 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
145static void libzbc_cleanup(struct thread_data *td)
146{
147 libzbc_close_dev(td);
148}
149
150static 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
156static 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
174static 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 *model = ZBD_IGNORE;
182 return 0;
183 }
184
185 ret = libzbc_open_dev(td, f, &ld);
186 if (ret)
187 return ret;
188
189 switch (ld->model) {
190 case ZBC_DM_HOST_AWARE:
191 *model = ZBD_HOST_AWARE;
192 break;
193 case ZBC_DM_HOST_MANAGED:
194 *model = ZBD_HOST_MANAGED;
195 break;
196 default:
197 *model = ZBD_NONE;
198 break;
199 }
200
201 return 0;
202}
203
204static int libzbc_report_zones(struct thread_data *td, struct fio_file *f,
205 uint64_t offset, struct zbd_zone *zbdz,
206 unsigned int nr_zones)
207{
208 struct libzbc_data *ld;
209 uint64_t sector = offset >> 9;
210 struct zbc_zone *zones;
211 unsigned int i;
212 int ret;
213
214 ret = libzbc_open_dev(td, f, &ld);
215 if (ret)
216 return ret;
217
218 if (sector >= ld->nr_sectors)
219 return 0;
220
221 zones = calloc(nr_zones, sizeof(struct zbc_zone));
222 if (!zones) {
223 ret = -ENOMEM;
224 goto out;
225 }
226
227 ret = zbc_report_zones(ld->zdev, sector, ZBC_RO_ALL, zones, &nr_zones);
228 if (ret < 0) {
229 log_err("%s: zbc_report_zones failed, err=%d\n",
230 f->file_name, ret);
231 goto out;
232 }
233
234 for (i = 0; i < nr_zones; i++, zbdz++) {
235 zbdz->start = zones[i].zbz_start << 9;
236 zbdz->len = zones[i].zbz_length << 9;
237 zbdz->wp = zones[i].zbz_write_pointer << 9;
236d23a8
SK
238 /*
239 * ZBC/ZAC do not define zone capacity, so use the zone size as
240 * the zone capacity.
241 */
242 zbdz->capacity = zbdz->len;
56a19325
DF
243
244 switch (zones[i].zbz_type) {
245 case ZBC_ZT_CONVENTIONAL:
246 zbdz->type = ZBD_ZONE_TYPE_CNV;
247 break;
248 case ZBC_ZT_SEQUENTIAL_REQ:
249 zbdz->type = ZBD_ZONE_TYPE_SWR;
250 break;
251 case ZBC_ZT_SEQUENTIAL_PREF:
252 zbdz->type = ZBD_ZONE_TYPE_SWP;
253 break;
254 default:
255 td_verror(td, errno, "invalid zone type");
256 log_err("%s: invalid type for zone at sector %llu.\n",
257 f->file_name, (unsigned long long)zbdz->start);
258 ret = -EIO;
259 goto out;
260 }
261
262 switch (zones[i].zbz_condition) {
263 case ZBC_ZC_NOT_WP:
264 zbdz->cond = ZBD_ZONE_COND_NOT_WP;
265 break;
266 case ZBC_ZC_EMPTY:
267 zbdz->cond = ZBD_ZONE_COND_EMPTY;
268 break;
269 case ZBC_ZC_IMP_OPEN:
270 zbdz->cond = ZBD_ZONE_COND_IMP_OPEN;
271 break;
272 case ZBC_ZC_EXP_OPEN:
273 zbdz->cond = ZBD_ZONE_COND_EXP_OPEN;
274 break;
275 case ZBC_ZC_CLOSED:
276 zbdz->cond = ZBD_ZONE_COND_CLOSED;
277 break;
278 case ZBC_ZC_FULL:
279 zbdz->cond = ZBD_ZONE_COND_FULL;
280 break;
281 case ZBC_ZC_RDONLY:
282 case ZBC_ZC_OFFLINE:
283 default:
284 /* Treat all these conditions as offline (don't use!) */
285 zbdz->cond = ZBD_ZONE_COND_OFFLINE;
286 break;
287 }
288 }
289
290 ret = nr_zones;
291out:
292 free(zones);
293 return ret;
294}
295
296static int libzbc_reset_wp(struct thread_data *td, struct fio_file *f,
297 uint64_t offset, uint64_t length)
298{
299 struct libzbc_data *ld = td->io_ops_data;
300 uint64_t sector = offset >> 9;
301 uint64_t end_sector = (offset + length) >> 9;
302 unsigned int nr_zones;
303 struct zbc_errno err;
304 int i, ret;
305
306 assert(ld);
307 assert(ld->zdev);
308
309 nr_zones = (length + td->o.zone_size - 1) / td->o.zone_size;
310 if (!sector && end_sector >= ld->nr_sectors) {
311 /* Reset all zones */
312 ret = zbc_reset_zone(ld->zdev, 0, ZBC_OP_ALL_ZONES);
313 if (ret)
314 goto err;
315
316 return 0;
317 }
318
319 for (i = 0; i < nr_zones; i++, sector += td->o.zone_size >> 9) {
320 ret = zbc_reset_zone(ld->zdev, sector, 0);
321 if (ret)
322 goto err;
323 }
324
325 return 0;
326
327err:
328 zbc_errno(ld->zdev, &err);
329 td_verror(td, errno, "zbc_reset_zone failed");
330 if (err.sk)
331 log_err("%s: reset wp failed %s:%s\n",
332 f->file_name,
333 zbc_sk_str(err.sk), zbc_asc_ascq_str(err.asc_ascq));
334 return -ret;
335}
336
337ssize_t libzbc_rw(struct thread_data *td, struct io_u *io_u)
338{
339 struct libzbc_data *ld = td->io_ops_data;
340 struct fio_file *f = io_u->file;
341 uint64_t sector = io_u->offset >> 9;
342 size_t count = io_u->xfer_buflen >> 9;
343 struct zbc_errno err;
344 ssize_t ret;
345
346 if (io_u->ddir == DDIR_WRITE)
347 ret = zbc_pwrite(ld->zdev, io_u->xfer_buf, count, sector);
348 else
349 ret = zbc_pread(ld->zdev, io_u->xfer_buf, count, sector);
350 if (ret == count)
351 return ret;
352
353 if (ret > 0) {
354 log_err("Short %s, len=%zu, ret=%zd\n",
355 io_u->ddir == DDIR_READ ? "read" : "write",
356 count << 9, ret << 9);
357 return -EIO;
358 }
359
360 /* I/O error */
361 zbc_errno(ld->zdev, &err);
362 td_verror(td, errno, "libzbc i/o failed");
363 if (err.sk) {
364 log_err("%s: op %u offset %llu+%llu failed (%s:%s), err %zd\n",
365 f->file_name, io_u->ddir,
366 io_u->offset, io_u->xfer_buflen,
367 zbc_sk_str(err.sk),
368 zbc_asc_ascq_str(err.asc_ascq), ret);
369 } else {
370 log_err("%s: op %u offset %llu+%llu failed, err %zd\n",
371 f->file_name, io_u->ddir,
372 io_u->offset, io_u->xfer_buflen, ret);
373 }
374
375 return -EIO;
376}
377
378static enum fio_q_status libzbc_queue(struct thread_data *td, struct io_u *io_u)
379{
380 struct libzbc_data *ld = td->io_ops_data;
381 struct fio_file *f = io_u->file;
382 ssize_t ret = 0;
383
384 fio_ro_check(td, io_u);
385
386 dprint(FD_ZBD, "%p:%s: libzbc queue %llu\n",
387 td, f->file_name, io_u->offset);
388
389 if (io_u->ddir == DDIR_READ || io_u->ddir == DDIR_WRITE) {
390 ret = libzbc_rw(td, io_u);
391 } else if (ddir_sync(io_u->ddir)) {
392 ret = zbc_flush(ld->zdev);
393 if (ret)
394 log_err("zbc_flush error %zd\n", ret);
395 } else if (io_u->ddir != DDIR_TRIM) {
396 log_err("Unsupported operation %u\n", io_u->ddir);
397 ret = -EINVAL;
398 }
399 if (ret < 0)
400 io_u->error = -ret;
401
402 return FIO_Q_COMPLETED;
403}
404
5a8a6a03 405FIO_STATIC struct ioengine_ops ioengine = {
56a19325
DF
406 .name = "libzbc",
407 .version = FIO_IOOPS_VERSION,
408 .open_file = libzbc_open_file,
409 .close_file = libzbc_close_file,
410 .cleanup = libzbc_cleanup,
411 .invalidate = libzbc_invalidate,
412 .get_file_size = libzbc_get_file_size,
413 .get_zoned_model = libzbc_get_zoned_model,
414 .report_zones = libzbc_report_zones,
415 .reset_wp = libzbc_reset_wp,
416 .queue = libzbc_queue,
417 .flags = FIO_SYNCIO | FIO_NOEXTEND | FIO_RAWIO,
418};
419
420static void fio_init fio_libzbc_register(void)
421{
422 register_ioengine(&ioengine);
423}
424
425static void fio_exit fio_libzbc_unregister(void)
426{
427 unregister_ioengine(&ioengine);
428}