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