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