Merge branch 'unified-merge' of https://github.com/jeffreyalien/fio
[fio.git] / oslib / linux-blkzoned.c
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
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
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;
156         blkz = (void *) hdr + sizeof(*hdr);
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;
162                 z->capacity = zone_capacity(hdr, blkz);
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                         z->wp = z->start;
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         int fd, ret = 0;
226
227         /* If the file is not yet opened, open it for this function. */
228         fd = f->fd;
229         if (fd < 0) {
230                 fd = open(f->file_name, O_RDWR | O_LARGEFILE);
231                 if (fd < 0)
232                         return -errno;
233         }
234
235         if (ioctl(fd, BLKRESETZONE, &zr) < 0)
236                 ret = -errno;
237
238         if (f->fd < 0)
239                 close(fd);
240
241         return ret;
242 }