t/nvmept_trim: increase transfer size for some tests
[fio.git] / oslib / linux-blkzoned.c
CommitLineData
b7694961
DLM
1/*
2 * Copyright (C) 2020 Western Digital Corporation or its affiliates.
3 *
4 * This file is released under the GPL.
5 */
6#include <errno.h>
7#include <string.h>
8#include <stdlib.h>
9#include <dirent.h>
10#include <fcntl.h>
11#include <sys/ioctl.h>
12#include <sys/stat.h>
13#include <unistd.h>
14
15#include "file.h"
16#include "fio.h"
17#include "lib/pow2.h"
18#include "log.h"
19#include "oslib/asprintf.h"
20#include "smalloc.h"
21#include "verify.h"
22#include "zbd_types.h"
23
24#include <linux/blkzoned.h>
8e2b81b8
BVA
25#ifndef BLKFINISHZONE
26#define BLKFINISHZONE _IOW(0x12, 136, struct blk_zone_range)
27#endif
b7694961 28
6ee607ba
NC
29/*
30 * If the uapi headers installed on the system lacks zone capacity support,
31 * use our local versions. If the installed headers are recent enough to
32 * support zone capacity, do not redefine any structs.
33 */
34#ifndef CONFIG_HAVE_REP_CAPACITY
35#define BLK_ZONE_REP_CAPACITY (1 << 0)
36
37struct blk_zone_v2 {
38 __u64 start; /* Zone start sector */
39 __u64 len; /* Zone length in number of sectors */
40 __u64 wp; /* Zone write pointer position */
41 __u8 type; /* Zone type */
42 __u8 cond; /* Zone condition */
43 __u8 non_seq; /* Non-sequential write resources active */
44 __u8 reset; /* Reset write pointer recommended */
45 __u8 resv[4];
46 __u64 capacity; /* Zone capacity in number of sectors */
47 __u8 reserved[24];
48};
49#define blk_zone blk_zone_v2
50
51struct blk_zone_report_v2 {
52 __u64 sector;
53 __u32 nr_zones;
54 __u32 flags;
55struct blk_zone zones[0];
56};
57#define blk_zone_report blk_zone_report_v2
58#endif /* CONFIG_HAVE_REP_CAPACITY */
59
b7694961
DLM
60/*
61 * Read up to 255 characters from the first line of a file. Strip the trailing
62 * newline.
63 */
64static char *read_file(const char *path)
65{
66 char line[256], *p = line;
67 FILE *f;
68
69 f = fopen(path, "rb");
70 if (!f)
71 return NULL;
72 if (!fgets(line, sizeof(line), f))
73 line[0] = '\0';
74 strsep(&p, "\n");
75 fclose(f);
76
77 return strdup(line);
78}
79
eaa45783
NC
80/*
81 * Get the value of a sysfs attribute for a block device.
82 *
83 * Returns NULL on failure.
84 * Returns a pointer to a string on success.
85 * The caller is responsible for freeing the memory.
86 */
87static char *blkzoned_get_sysfs_attr(const char *file_name, const char *attr)
b7694961 88{
eaa45783 89 char *attr_path = NULL;
b7694961
DLM
90 struct stat statbuf;
91 char *sys_devno_path = NULL;
92 char *part_attr_path = NULL;
93 char *part_str = NULL;
94 char sys_path[PATH_MAX];
95 ssize_t sz;
96 char *delim = NULL;
eaa45783 97 char *attr_str = NULL;
b7694961
DLM
98
99 if (stat(file_name, &statbuf) < 0)
100 goto out;
101
102 if (asprintf(&sys_devno_path, "/sys/dev/block/%d:%d",
103 major(statbuf.st_rdev), minor(statbuf.st_rdev)) < 0)
104 goto out;
105
106 sz = readlink(sys_devno_path, sys_path, sizeof(sys_path) - 1);
107 if (sz < 0)
108 goto out;
109 sys_path[sz] = '\0';
110
111 /*
112 * If the device is a partition device, cut the device name in the
113 * canonical sysfs path to obtain the sysfs path of the holder device.
114 * e.g.: /sys/devices/.../sda/sda1 -> /sys/devices/.../sda
115 */
116 if (asprintf(&part_attr_path, "/sys/dev/block/%s/partition",
117 sys_path) < 0)
118 goto out;
119 part_str = read_file(part_attr_path);
120 if (part_str && *part_str == '1') {
121 delim = strrchr(sys_path, '/');
122 if (!delim)
123 goto out;
124 *delim = '\0';
125 }
126
eaa45783
NC
127 if (asprintf(&attr_path,
128 "/sys/dev/block/%s/%s", sys_path, attr) < 0)
b7694961
DLM
129 goto out;
130
eaa45783
NC
131 attr_str = read_file(attr_path);
132out:
133 free(attr_path);
134 free(part_str);
135 free(part_attr_path);
136 free(sys_devno_path);
137
138 return attr_str;
139}
140
141int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
142 enum zbd_zoned_model *model)
143{
144 char *model_str = NULL;
145
2c7dd23e
NC
146 if (f->filetype != FIO_TYPE_BLOCK)
147 return -EINVAL;
eaa45783
NC
148
149 *model = ZBD_NONE;
150
151 model_str = blkzoned_get_sysfs_attr(f->file_name, "queue/zoned");
b7694961 152 if (!model_str)
eaa45783
NC
153 return 0;
154
155 dprint(FD_ZBD, "%s: zbd model string: %s\n", f->file_name, model_str);
b7694961
DLM
156 if (strcmp(model_str, "host-aware") == 0)
157 *model = ZBD_HOST_AWARE;
158 else if (strcmp(model_str, "host-managed") == 0)
159 *model = ZBD_HOST_MANAGED;
eaa45783 160
b7694961 161 free(model_str);
eaa45783 162
b7694961
DLM
163 return 0;
164}
165
d2f442bc
NC
166int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
167 unsigned int *max_open_zones)
168{
169 char *max_open_str;
170
171 if (f->filetype != FIO_TYPE_BLOCK)
172 return -EIO;
173
174 max_open_str = blkzoned_get_sysfs_attr(f->file_name, "queue/max_open_zones");
f3463241
DLM
175 if (!max_open_str) {
176 *max_open_zones = 0;
d2f442bc 177 return 0;
f3463241 178 }
d2f442bc
NC
179
180 dprint(FD_ZBD, "%s: max open zones supported by device: %s\n",
181 f->file_name, max_open_str);
182 *max_open_zones = atoll(max_open_str);
183
184 free(max_open_str);
185
186 return 0;
187}
188
9e523ef8
SK
189int blkzoned_get_max_active_zones(struct thread_data *td, struct fio_file *f,
190 unsigned int *max_active_zones)
191{
192 char *max_active_str;
193
194 if (f->filetype != FIO_TYPE_BLOCK)
195 return -EIO;
196
197 max_active_str = blkzoned_get_sysfs_attr(f->file_name, "queue/max_active_zones");
198 if (!max_active_str) {
199 *max_active_zones = 0;
200 return 0;
201 }
202
203 dprint(FD_ZBD, "%s: max active zones supported by device: %s\n",
204 f->file_name, max_active_str);
205 *max_active_zones = atoll(max_active_str);
206
207 free(max_active_str);
208
209 return 0;
210}
211
236d23a8
SK
212static uint64_t zone_capacity(struct blk_zone_report *hdr,
213 struct blk_zone *blkz)
214{
236d23a8
SK
215 if (hdr->flags & BLK_ZONE_REP_CAPACITY)
216 return blkz->capacity << 9;
236d23a8
SK
217 return blkz->len << 9;
218}
219
b7694961
DLM
220int blkzoned_report_zones(struct thread_data *td, struct fio_file *f,
221 uint64_t offset, struct zbd_zone *zones,
222 unsigned int nr_zones)
223{
224 struct blk_zone_report *hdr = NULL;
225 struct blk_zone *blkz;
226 struct zbd_zone *z;
227 unsigned int i;
228 int fd = -1, ret;
229
230 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
231 if (fd < 0)
232 return -errno;
233
234 hdr = calloc(1, sizeof(struct blk_zone_report) +
235 nr_zones * sizeof(struct blk_zone));
236 if (!hdr) {
237 ret = -ENOMEM;
238 goto out;
239 }
240
241 hdr->nr_zones = nr_zones;
242 hdr->sector = offset >> 9;
243 ret = ioctl(fd, BLKREPORTZONE, hdr);
244 if (ret) {
db46d820
DF
245 log_err("%s: BLKREPORTZONE ioctl failed, ret=%d, err=%d.\n",
246 f->file_name, ret, -errno);
b7694961
DLM
247 ret = -errno;
248 goto out;
249 }
250
251 nr_zones = hdr->nr_zones;
f8779edf 252 blkz = (void *) hdr + sizeof(*hdr);
b7694961
DLM
253 z = &zones[0];
254 for (i = 0; i < nr_zones; i++, z++, blkz++) {
255 z->start = blkz->start << 9;
256 z->wp = blkz->wp << 9;
257 z->len = blkz->len << 9;
236d23a8 258 z->capacity = zone_capacity(hdr, blkz);
b7694961
DLM
259
260 switch (blkz->type) {
261 case BLK_ZONE_TYPE_CONVENTIONAL:
262 z->type = ZBD_ZONE_TYPE_CNV;
263 break;
264 case BLK_ZONE_TYPE_SEQWRITE_REQ:
265 z->type = ZBD_ZONE_TYPE_SWR;
266 break;
267 case BLK_ZONE_TYPE_SEQWRITE_PREF:
268 z->type = ZBD_ZONE_TYPE_SWP;
269 break;
270 default:
271 td_verror(td, errno, "invalid zone type");
272 log_err("%s: invalid type for zone at sector %llu.\n",
273 f->file_name, (unsigned long long)offset >> 9);
274 ret = -EIO;
275 goto out;
276 }
277
278 switch (blkz->cond) {
279 case BLK_ZONE_COND_NOT_WP:
280 z->cond = ZBD_ZONE_COND_NOT_WP;
281 break;
282 case BLK_ZONE_COND_EMPTY:
283 z->cond = ZBD_ZONE_COND_EMPTY;
284 break;
285 case BLK_ZONE_COND_IMP_OPEN:
286 z->cond = ZBD_ZONE_COND_IMP_OPEN;
287 break;
288 case BLK_ZONE_COND_EXP_OPEN:
289 z->cond = ZBD_ZONE_COND_EXP_OPEN;
290 break;
291 case BLK_ZONE_COND_CLOSED:
292 z->cond = ZBD_ZONE_COND_CLOSED;
293 break;
294 case BLK_ZONE_COND_FULL:
295 z->cond = ZBD_ZONE_COND_FULL;
296 break;
297 case BLK_ZONE_COND_READONLY:
298 case BLK_ZONE_COND_OFFLINE:
299 default:
300 /* Treat all these conditions as offline (don't use!) */
301 z->cond = ZBD_ZONE_COND_OFFLINE;
59b67452 302 z->wp = z->start;
b7694961
DLM
303 }
304 }
305
306 ret = nr_zones;
307out:
308 free(hdr);
309 close(fd);
310
311 return ret;
312}
313
314int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
315 uint64_t offset, uint64_t length)
316{
317 struct blk_zone_range zr = {
318 .sector = offset >> 9,
319 .nr_sectors = length >> 9,
320 };
b4824992
SK
321 int fd, ret = 0;
322
323 /* If the file is not yet opened, open it for this function. */
324 fd = f->fd;
325 if (fd < 0) {
326 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
327 if (fd < 0)
328 return -errno;
329 }
b7694961 330
b4824992
SK
331 if (ioctl(fd, BLKRESETZONE, &zr) < 0)
332 ret = -errno;
b7694961 333
b4824992
SK
334 if (f->fd < 0)
335 close(fd);
336
337 return ret;
b7694961 338}
f8ec93e4
SK
339
340int blkzoned_finish_zone(struct thread_data *td, struct fio_file *f,
341 uint64_t offset, uint64_t length)
342{
f8ec93e4
SK
343 struct blk_zone_range zr = {
344 .sector = offset >> 9,
345 .nr_sectors = length >> 9,
346 };
347 int fd, ret = 0;
348
349 /* If the file is not yet opened, open it for this function. */
350 fd = f->fd;
351 if (fd < 0) {
352 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
353 if (fd < 0)
354 return -errno;
355 }
356
8e2b81b8 357 if (ioctl(fd, BLKFINISHZONE, &zr) < 0) {
f8ec93e4 358 ret = -errno;
8e2b81b8
BVA
359 /*
360 * Kernel versions older than 5.5 do not support BLKFINISHZONE
361 * and return the ENOTTY error code. These old kernels only
362 * support block devices that close zones automatically.
363 */
364 if (ret == ENOTTY)
365 ret = 0;
366 }
f8ec93e4
SK
367
368 if (f->fd < 0)
369 close(fd);
370
371 return ret;
f8ec93e4 372}