zbd: Decrement open zones count at write command completion
[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>
25
26/*
27 * Read up to 255 characters from the first line of a file. Strip the trailing
28 * newline.
29 */
30static char *read_file(const char *path)
31{
32 char line[256], *p = line;
33 FILE *f;
34
35 f = fopen(path, "rb");
36 if (!f)
37 return NULL;
38 if (!fgets(line, sizeof(line), f))
39 line[0] = '\0';
40 strsep(&p, "\n");
41 fclose(f);
42
43 return strdup(line);
44}
45
46int blkzoned_get_zoned_model(struct thread_data *td, struct fio_file *f,
47 enum zbd_zoned_model *model)
48{
49 const char *file_name = f->file_name;
50 char *zoned_attr_path = NULL;
51 char *model_str = NULL;
52 struct stat statbuf;
53 char *sys_devno_path = NULL;
54 char *part_attr_path = NULL;
55 char *part_str = NULL;
56 char sys_path[PATH_MAX];
57 ssize_t sz;
58 char *delim = NULL;
59
60 if (f->filetype != FIO_TYPE_BLOCK) {
61 *model = ZBD_IGNORE;
62 return 0;
63 }
64
65 *model = ZBD_NONE;
66
67 if (stat(file_name, &statbuf) < 0)
68 goto out;
69
70 if (asprintf(&sys_devno_path, "/sys/dev/block/%d:%d",
71 major(statbuf.st_rdev), minor(statbuf.st_rdev)) < 0)
72 goto out;
73
74 sz = readlink(sys_devno_path, sys_path, sizeof(sys_path) - 1);
75 if (sz < 0)
76 goto out;
77 sys_path[sz] = '\0';
78
79 /*
80 * If the device is a partition device, cut the device name in the
81 * canonical sysfs path to obtain the sysfs path of the holder device.
82 * e.g.: /sys/devices/.../sda/sda1 -> /sys/devices/.../sda
83 */
84 if (asprintf(&part_attr_path, "/sys/dev/block/%s/partition",
85 sys_path) < 0)
86 goto out;
87 part_str = read_file(part_attr_path);
88 if (part_str && *part_str == '1') {
89 delim = strrchr(sys_path, '/');
90 if (!delim)
91 goto out;
92 *delim = '\0';
93 }
94
95 if (asprintf(&zoned_attr_path,
96 "/sys/dev/block/%s/queue/zoned", sys_path) < 0)
97 goto out;
98
99 model_str = read_file(zoned_attr_path);
100 if (!model_str)
101 goto out;
102 dprint(FD_ZBD, "%s: zbd model string: %s\n", file_name, model_str);
103 if (strcmp(model_str, "host-aware") == 0)
104 *model = ZBD_HOST_AWARE;
105 else if (strcmp(model_str, "host-managed") == 0)
106 *model = ZBD_HOST_MANAGED;
107out:
108 free(model_str);
109 free(zoned_attr_path);
110 free(part_str);
111 free(part_attr_path);
112 free(sys_devno_path);
113 return 0;
114}
115
236d23a8
SK
116static uint64_t zone_capacity(struct blk_zone_report *hdr,
117 struct blk_zone *blkz)
118{
119#ifdef CONFIG_HAVE_REP_CAPACITY
120 if (hdr->flags & BLK_ZONE_REP_CAPACITY)
121 return blkz->capacity << 9;
122#endif
123 return blkz->len << 9;
124}
125
b7694961
DLM
126int blkzoned_report_zones(struct thread_data *td, struct fio_file *f,
127 uint64_t offset, struct zbd_zone *zones,
128 unsigned int nr_zones)
129{
130 struct blk_zone_report *hdr = NULL;
131 struct blk_zone *blkz;
132 struct zbd_zone *z;
133 unsigned int i;
134 int fd = -1, ret;
135
136 fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
137 if (fd < 0)
138 return -errno;
139
140 hdr = calloc(1, sizeof(struct blk_zone_report) +
141 nr_zones * sizeof(struct blk_zone));
142 if (!hdr) {
143 ret = -ENOMEM;
144 goto out;
145 }
146
147 hdr->nr_zones = nr_zones;
148 hdr->sector = offset >> 9;
149 ret = ioctl(fd, BLKREPORTZONE, hdr);
150 if (ret) {
151 ret = -errno;
152 goto out;
153 }
154
155 nr_zones = hdr->nr_zones;
f8779edf 156 blkz = (void *) hdr + sizeof(*hdr);
b7694961
DLM
157 z = &zones[0];
158 for (i = 0; i < nr_zones; i++, z++, blkz++) {
159 z->start = blkz->start << 9;
160 z->wp = blkz->wp << 9;
161 z->len = blkz->len << 9;
236d23a8 162 z->capacity = zone_capacity(hdr, blkz);
b7694961
DLM
163
164 switch (blkz->type) {
165 case BLK_ZONE_TYPE_CONVENTIONAL:
166 z->type = ZBD_ZONE_TYPE_CNV;
167 break;
168 case BLK_ZONE_TYPE_SEQWRITE_REQ:
169 z->type = ZBD_ZONE_TYPE_SWR;
170 break;
171 case BLK_ZONE_TYPE_SEQWRITE_PREF:
172 z->type = ZBD_ZONE_TYPE_SWP;
173 break;
174 default:
175 td_verror(td, errno, "invalid zone type");
176 log_err("%s: invalid type for zone at sector %llu.\n",
177 f->file_name, (unsigned long long)offset >> 9);
178 ret = -EIO;
179 goto out;
180 }
181
182 switch (blkz->cond) {
183 case BLK_ZONE_COND_NOT_WP:
184 z->cond = ZBD_ZONE_COND_NOT_WP;
185 break;
186 case BLK_ZONE_COND_EMPTY:
187 z->cond = ZBD_ZONE_COND_EMPTY;
188 break;
189 case BLK_ZONE_COND_IMP_OPEN:
190 z->cond = ZBD_ZONE_COND_IMP_OPEN;
191 break;
192 case BLK_ZONE_COND_EXP_OPEN:
193 z->cond = ZBD_ZONE_COND_EXP_OPEN;
194 break;
195 case BLK_ZONE_COND_CLOSED:
196 z->cond = ZBD_ZONE_COND_CLOSED;
197 break;
198 case BLK_ZONE_COND_FULL:
199 z->cond = ZBD_ZONE_COND_FULL;
200 break;
201 case BLK_ZONE_COND_READONLY:
202 case BLK_ZONE_COND_OFFLINE:
203 default:
204 /* Treat all these conditions as offline (don't use!) */
205 z->cond = ZBD_ZONE_COND_OFFLINE;
206 break;
207 }
208 }
209
210 ret = nr_zones;
211out:
212 free(hdr);
213 close(fd);
214
215 return ret;
216}
217
218int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
219 uint64_t offset, uint64_t length)
220{
221 struct blk_zone_range zr = {
222 .sector = offset >> 9,
223 .nr_sectors = length >> 9,
224 };
225
226 if (ioctl(f->fd, BLKRESETZONE, &zr) < 0)
227 return -errno;
228
229 return 0;
230}