Merge branch 'patch-1' of https://github.com/avlasov-mos-de/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 int blkzoned_report_zones(struct thread_data *td, struct fio_file *f,
117                           uint64_t offset, struct zbd_zone *zones,
118                           unsigned int nr_zones)
119 {
120         struct blk_zone_report *hdr = NULL;
121         struct blk_zone *blkz;
122         struct zbd_zone *z;
123         unsigned int i;
124         int fd = -1, ret;
125
126         fd = open(f->file_name, O_RDONLY | O_LARGEFILE);
127         if (fd < 0)
128                 return -errno;
129
130         hdr = calloc(1, sizeof(struct blk_zone_report) +
131                         nr_zones * sizeof(struct blk_zone));
132         if (!hdr) {
133                 ret = -ENOMEM;
134                 goto out;
135         }
136
137         hdr->nr_zones = nr_zones;
138         hdr->sector = offset >> 9;
139         ret = ioctl(fd, BLKREPORTZONE, hdr);
140         if (ret) {
141                 ret = -errno;
142                 goto out;
143         }
144
145         nr_zones = hdr->nr_zones;
146         blkz = (void *) hdr + sizeof(*hdr);
147         z = &zones[0];
148         for (i = 0; i < nr_zones; i++, z++, blkz++) {
149                 z->start = blkz->start << 9;
150                 z->wp = blkz->wp << 9;
151                 z->len = blkz->len << 9;
152
153                 switch (blkz->type) {
154                 case BLK_ZONE_TYPE_CONVENTIONAL:
155                         z->type = ZBD_ZONE_TYPE_CNV;
156                         break;
157                 case BLK_ZONE_TYPE_SEQWRITE_REQ:
158                         z->type = ZBD_ZONE_TYPE_SWR;
159                         break;
160                 case BLK_ZONE_TYPE_SEQWRITE_PREF:
161                         z->type = ZBD_ZONE_TYPE_SWP;
162                         break;
163                 default:
164                         td_verror(td, errno, "invalid zone type");
165                         log_err("%s: invalid type for zone at sector %llu.\n",
166                                 f->file_name, (unsigned long long)offset >> 9);
167                         ret = -EIO;
168                         goto out;
169                 }
170
171                 switch (blkz->cond) {
172                 case BLK_ZONE_COND_NOT_WP:
173                         z->cond = ZBD_ZONE_COND_NOT_WP;
174                         break;
175                 case BLK_ZONE_COND_EMPTY:
176                         z->cond = ZBD_ZONE_COND_EMPTY;
177                         break;
178                 case BLK_ZONE_COND_IMP_OPEN:
179                         z->cond = ZBD_ZONE_COND_IMP_OPEN;
180                         break;
181                 case BLK_ZONE_COND_EXP_OPEN:
182                         z->cond = ZBD_ZONE_COND_EXP_OPEN;
183                         break;
184                 case BLK_ZONE_COND_CLOSED:
185                         z->cond = ZBD_ZONE_COND_CLOSED;
186                         break;
187                 case BLK_ZONE_COND_FULL:
188                         z->cond = ZBD_ZONE_COND_FULL;
189                         break;
190                 case BLK_ZONE_COND_READONLY:
191                 case BLK_ZONE_COND_OFFLINE:
192                 default:
193                         /* Treat all these conditions as offline (don't use!) */
194                         z->cond = ZBD_ZONE_COND_OFFLINE;
195                         break;
196                 }
197         }
198
199         ret = nr_zones;
200 out:
201         free(hdr);
202         close(fd);
203
204         return ret;
205 }
206
207 int blkzoned_reset_wp(struct thread_data *td, struct fio_file *f,
208                       uint64_t offset, uint64_t length)
209 {
210         struct blk_zone_range zr = {
211                 .sector         = offset >> 9,
212                 .nr_sectors     = length >> 9,
213         };
214
215         if (ioctl(f->fd, BLKRESETZONE, &zr) < 0)
216                 return -errno;
217
218         return 0;
219 }