2 * Copyright (C) 2020 Western Digital Corporation or its affiliates.
4 * This file is released under the GPL.
11 #include <sys/ioctl.h>
19 #include "oslib/asprintf.h"
22 #include "zbd_types.h"
24 #include <linux/blkzoned.h>
26 #define BLKFINISHZONE _IOW(0x12, 136, struct blk_zone_range)
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.
34 #ifndef CONFIG_HAVE_REP_CAPACITY
35 #define BLK_ZONE_REP_CAPACITY (1 << 0)
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 */
46 __u64 capacity; /* Zone capacity in number of sectors */
49 #define blk_zone blk_zone_v2
51 struct blk_zone_report_v2 {
55 struct blk_zone zones[0];
57 #define blk_zone_report blk_zone_report_v2
58 #endif /* CONFIG_HAVE_REP_CAPACITY */
61 * Read up to 255 characters from the first line of a file. Strip the trailing
64 static char *read_file(const char *path)
66 char line[256], *p = line;
69 f = fopen(path, "rb");
72 if (!fgets(line, sizeof(line), f))
81 * Get the value of a sysfs attribute for a block device.
83 * Returns NULL on failure.
84 * Returns a pointer to a string on success.
85 * The caller is responsible for freeing the memory.
87 static char *blkzoned_get_sysfs_attr(const char *file_name, const char *attr)
89 char *attr_path = NULL;
91 char *sys_devno_path = NULL;
92 char *part_attr_path = NULL;
93 char *part_str = NULL;
94 char sys_path[PATH_MAX];
97 char *attr_str = NULL;
99 if (stat(file_name, &statbuf) < 0)
102 if (asprintf(&sys_devno_path, "/sys/dev/block/%d:%d",
103 major(statbuf.st_rdev), minor(statbuf.st_rdev)) < 0)
106 sz = readlink(sys_devno_path, sys_path, sizeof(sys_path) - 1);
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
116 if (asprintf(&part_attr_path, "/sys/dev/block/%s/partition",
119 part_str = read_file(part_attr_path);
120 if (part_str && *part_str == '1') {
121 delim = strrchr(sys_path, '/');
127 if (asprintf(&attr_path,
128 "/sys/dev/block/%s/%s", sys_path, attr) < 0)
131 attr_str = read_file(attr_path);
135 free(part_attr_path);
136 free(sys_devno_path);
141 int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
142 enum zbd_zoned_model *model)
144 char *model_str = NULL;
146 if (f->filetype != FIO_TYPE_BLOCK)
151 model_str = blkzoned_get_sysfs_attr(f->file_name, "queue/zoned");
155 dprint(FD_ZBD, "%s: zbd model string: %s\n", f->file_name, model_str);
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;
166 int blkzoned_get_max_open_zones(struct thread_data *td, struct fio_file *f,
167 unsigned int *max_open_zones)
171 if (f->filetype != FIO_TYPE_BLOCK)
174 max_open_str = blkzoned_get_sysfs_attr(f->file_name, "queue/max_open_zones");
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);
189 int blkzoned_get_max_active_zones(struct thread_data *td, struct fio_file *f,
190 unsigned int *max_active_zones)
192 char *max_active_str;
194 if (f->filetype != FIO_TYPE_BLOCK)
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;
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);
207 free(max_active_str);
212 static uint64_t zone_capacity(struct blk_zone_report *hdr,
213 struct blk_zone *blkz)
215 if (hdr->flags & BLK_ZONE_REP_CAPACITY)
216 return blkz->capacity << 9;
217 return blkz->len << 9;
220 int blkzoned_report_zones(struct thread_data *td, struct fio_file *f,
221 uint64_t offset, struct zbd_zone *zones,
222 unsigned int nr_zones)
224 struct blk_zone_report *hdr = NULL;
225 struct blk_zone *blkz;
230 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
234 hdr = calloc(1, sizeof(struct blk_zone_report) +
235 nr_zones * sizeof(struct blk_zone));
241 hdr->nr_zones = nr_zones;
242 hdr->sector = offset >> 9;
243 ret = ioctl(fd, BLKREPORTZONE, hdr);
249 nr_zones = hdr->nr_zones;
250 blkz = (void *) hdr + sizeof(*hdr);
252 for (i = 0; i < nr_zones; i++, z++, blkz++) {
253 z->start = blkz->start << 9;
254 z->wp = blkz->wp << 9;
255 z->len = blkz->len << 9;
256 z->capacity = zone_capacity(hdr, blkz);
258 switch (blkz->type) {
259 case BLK_ZONE_TYPE_CONVENTIONAL:
260 z->type = ZBD_ZONE_TYPE_CNV;
262 case BLK_ZONE_TYPE_SEQWRITE_REQ:
263 z->type = ZBD_ZONE_TYPE_SWR;
265 case BLK_ZONE_TYPE_SEQWRITE_PREF:
266 z->type = ZBD_ZONE_TYPE_SWP;
269 td_verror(td, errno, "invalid zone type");
270 log_err("%s: invalid type for zone at sector %llu.\n",
271 f->file_name, (unsigned long long)offset >> 9);
276 switch (blkz->cond) {
277 case BLK_ZONE_COND_NOT_WP:
278 z->cond = ZBD_ZONE_COND_NOT_WP;
280 case BLK_ZONE_COND_EMPTY:
281 z->cond = ZBD_ZONE_COND_EMPTY;
283 case BLK_ZONE_COND_IMP_OPEN:
284 z->cond = ZBD_ZONE_COND_IMP_OPEN;
286 case BLK_ZONE_COND_EXP_OPEN:
287 z->cond = ZBD_ZONE_COND_EXP_OPEN;
289 case BLK_ZONE_COND_CLOSED:
290 z->cond = ZBD_ZONE_COND_CLOSED;
292 case BLK_ZONE_COND_FULL:
293 z->cond = ZBD_ZONE_COND_FULL;
295 case BLK_ZONE_COND_READONLY:
296 case BLK_ZONE_COND_OFFLINE:
298 /* Treat all these conditions as offline (don't use!) */
299 z->cond = ZBD_ZONE_COND_OFFLINE;
312 int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
313 uint64_t offset, uint64_t length)
315 struct blk_zone_range zr = {
316 .sector = offset >> 9,
317 .nr_sectors = length >> 9,
321 /* If the file is not yet opened, open it for this function. */
324 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
329 if (ioctl(fd, BLKRESETZONE, &zr) < 0)
338 int blkzoned_finish_zone(struct thread_data *td, struct fio_file *f,
339 uint64_t offset, uint64_t length)
341 struct blk_zone_range zr = {
342 .sector = offset >> 9,
343 .nr_sectors = length >> 9,
347 /* If the file is not yet opened, open it for this function. */
350 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
355 if (ioctl(fd, BLKFINISHZONE, &zr) < 0) {
358 * Kernel versions older than 5.5 do not support BLKFINISHZONE
359 * and return the ENOTTY error code. These old kernels only
360 * support block devices that close zones automatically.