1 /**
2 * libf2fs_zoned.c
3 *
4 * Copyright (c) 2016 Western Digital Corporation.
5 * Written by: Damien Le Moal <damien.lemoal@wdc.com>
6 *
7 * Dual licensed under the GPL or LGPL version 2 licenses.
8 */
9 #define _LARGEFILE64_SOURCE
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <sys/stat.h>
18 #include <sys/ioctl.h>
19 #include <libgen.h>
20
21 #include <f2fs_fs.h>
22
23 #ifdef HAVE_LINUX_BLKZONED_H
24
f2fs_get_zoned_model(int i)25 void f2fs_get_zoned_model(int i)
26 {
27 struct device_info *dev = c.devices + i;
28 char str[128];
29 FILE *file;
30 int res;
31
32 /* Check that this is a zoned block device */
33 snprintf(str, sizeof(str),
34 "/sys/block/%s/queue/zoned",
35 basename(dev->path));
36 file = fopen(str, "r");
37 if (!file)
38 goto not_zoned;
39
40 memset(str, 0, sizeof(str));
41 res = fscanf(file, "%s", str);
42 fclose(file);
43
44 if (res != 1)
45 goto not_zoned;
46
47 if (strcmp(str, "host-aware") == 0) {
48 dev->zoned_model = F2FS_ZONED_HA;
49 return;
50 }
51 if (strcmp(str, "host-managed") == 0) {
52 dev->zoned_model = F2FS_ZONED_HM;
53 return;
54 }
55
56 not_zoned:
57 dev->zoned_model = F2FS_ZONED_NONE;
58 }
59
f2fs_get_zone_blocks(int i)60 int f2fs_get_zone_blocks(int i)
61 {
62 struct device_info *dev = c.devices + i;
63 uint64_t sectors;
64 char str[128];
65 FILE *file;
66 int res;
67
68 /* Get zone size */
69 dev->zone_blocks = 0;
70
71 snprintf(str, sizeof(str),
72 "/sys/block/%s/queue/chunk_sectors",
73 basename(dev->path));
74 file = fopen(str, "r");
75 if (!file)
76 return -1;
77
78 memset(str, 0, sizeof(str));
79 res = fscanf(file, "%s", str);
80 fclose(file);
81
82 if (res != 1)
83 return -1;
84
85 sectors = atol(str);
86 if (!sectors)
87 return -1;
88
89 dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - 9);
90 sectors = (sectors << 9) / c.sector_size;
91
92 /*
93 * Total number of zones: there may
94 * be a last smaller runt zone.
95 */
96 dev->nr_zones = dev->total_sectors / sectors;
97 if (dev->total_sectors % sectors)
98 dev->nr_zones++;
99
100 return 0;
101 }
102
103 #define F2FS_REPORT_ZONES_BUFSZ 524288
104
f2fs_check_zones(int j)105 int f2fs_check_zones(int j)
106 {
107 struct device_info *dev = c.devices + j;
108 struct blk_zone_report *rep;
109 struct blk_zone *blkz;
110 unsigned int i, n = 0;
111 u_int64_t total_sectors;
112 u_int64_t sector;
113 int last_is_conv = 1;
114 int ret = -1;
115
116 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
117 if (!rep) {
118 ERR_MSG("No memory for report zones\n");
119 return -ENOMEM;
120 }
121
122 dev->nr_rnd_zones = 0;
123 sector = 0;
124 total_sectors = (dev->total_sectors * c.sector_size) >> 9;
125
126 while (sector < total_sectors) {
127
128 /* Get zone info */
129 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
130 rep->sector = sector;
131 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
132 / sizeof(struct blk_zone);
133
134 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
135 if (ret != 0) {
136 ret = -errno;
137 ERR_MSG("ioctl BLKREPORTZONE failed\n");
138 goto out;
139 }
140
141 if (!rep->nr_zones)
142 break;
143
144 blkz = (struct blk_zone *)(rep + 1);
145 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
146
147 if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY ||
148 blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE)
149 last_is_conv = 0;
150 if (blk_zone_conv(blkz) ||
151 blk_zone_seq_pref(blkz)) {
152 if (last_is_conv)
153 dev->nr_rnd_zones++;
154 } else {
155 last_is_conv = 0;
156 }
157
158 if (blk_zone_conv(blkz)) {
159 DBG(2,
160 "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n",
161 n,
162 blk_zone_cond(blkz),
163 blk_zone_cond_str(blkz),
164 blk_zone_sector(blkz),
165 blk_zone_length(blkz));
166 } else {
167 DBG(2,
168 "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, "
169 "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n",
170 n,
171 blk_zone_type(blkz),
172 blk_zone_type_str(blkz),
173 blk_zone_cond(blkz),
174 blk_zone_cond_str(blkz),
175 blk_zone_need_reset(blkz),
176 blk_zone_non_seq(blkz),
177 blk_zone_sector(blkz),
178 blk_zone_length(blkz),
179 blk_zone_wp_sector(blkz));
180 }
181
182 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
183 n++;
184 blkz++;
185 }
186 }
187
188 if (sector != total_sectors) {
189 ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n",
190 (unsigned long long)(sector << 9) / c.sector_size,
191 (unsigned long long)dev->total_sectors);
192 ret = -1;
193 goto out;
194 }
195
196 if (n != dev->nr_zones) {
197 ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n",
198 dev->nr_zones, n);
199 ret = -1;
200 goto out;
201 }
202
203 if (dev->zoned_model == F2FS_ZONED_HM &&
204 !dev->nr_rnd_zones) {
205 ERR_MSG("No conventional zone for super block\n");
206 ret = -1;
207 }
208 out:
209 free(rep);
210 return ret;
211 }
212
f2fs_reset_zones(int j)213 int f2fs_reset_zones(int j)
214 {
215 struct device_info *dev = c.devices + j;
216 struct blk_zone_report *rep;
217 struct blk_zone *blkz;
218 struct blk_zone_range range;
219 u_int64_t total_sectors;
220 u_int64_t sector;
221 unsigned int i;
222 int ret = -1;
223
224 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
225 if (!rep) {
226 ERR_MSG("No memory for report zones\n");
227 return -1;
228 }
229
230 sector = 0;
231 total_sectors = (dev->total_sectors * c.sector_size) >> 9;
232 while (sector < total_sectors) {
233
234 /* Get zone info */
235 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
236 rep->sector = sector;
237 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
238 / sizeof(struct blk_zone);
239
240 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
241 if (ret != 0) {
242 ret = -errno;
243 ERR_MSG("ioctl BLKREPORTZONES failed\n");
244 goto out;
245 }
246
247 if (!rep->nr_zones)
248 break;
249
250 blkz = (struct blk_zone *)(rep + 1);
251 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
252 if (blk_zone_seq(blkz) &&
253 !blk_zone_empty(blkz)) {
254 /* Non empty sequential zone: reset */
255 range.sector = blk_zone_sector(blkz);
256 range.nr_sectors = blk_zone_length(blkz);
257 ret = ioctl(dev->fd, BLKRESETZONE, &range);
258 if (ret != 0) {
259 ret = -errno;
260 ERR_MSG("ioctl BLKRESETZONE failed\n");
261 goto out;
262 }
263 }
264 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
265 blkz++;
266 }
267 }
268 out:
269 free(rep);
270 if (!ret)
271 MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20);
272 return ret;
273 }
274
275 #else
276
f2fs_get_zoned_model(int i)277 void f2fs_get_zoned_model(int i)
278 {
279 struct device_info *dev = c.devices + i;
280
281 c.zoned_mode = 0;
282 dev->zoned_model = F2FS_ZONED_NONE;
283 }
284
f2fs_get_zone_blocks(int i)285 int f2fs_get_zone_blocks(int i)
286 {
287 struct device_info *dev = c.devices + i;
288
289 c.zoned_mode = 0;
290 dev->nr_zones = 0;
291 dev->zone_blocks = 0;
292 dev->zoned_model = F2FS_ZONED_NONE;
293
294 return 0;
295 }
296
f2fs_check_zones(int i)297 int f2fs_check_zones(int i)
298 {
299 ERR_MSG("%d: Zoned block devices are not supported\n", i);
300 return -1;
301 }
302
f2fs_reset_zones(int i)303 int f2fs_reset_zones(int i)
304 {
305 ERR_MSG("%d: Zoned block devices are not supported\n", i);
306 return -1;
307 }
308
309 #endif
310
311