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 | ||
26 | /* | |
27 | * Read up to 255 characters from the first line of a file. Strip the trailing | |
28 | * newline. | |
29 | */ | |
30 | static 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 | ||
46 | int 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; | |
107 | out: | |
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 |
116 | static 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 |
126 | int 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; | |
211 | out: | |
212 | free(hdr); | |
213 | close(fd); | |
214 | ||
215 | return ret; | |
216 | } | |
217 | ||
218 | int 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 | } |