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 #ifndef _LARGEFILE64_SOURCE
10 #define _LARGEFILE64_SOURCE
11 #endif
12
13 #include <f2fs_fs.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #ifdef HAVE_SYS_SYSMACROS_H
22 #include <sys/sysmacros.h>
23 #endif
24 #ifdef HAVE_LINUX_LIMITS_H
25 #include <linux/limits.h>
26 #endif
27 #ifndef ANDROID_WINDOWS_HOST
28 #include <sys/ioctl.h>
29 #endif
30 #include <libgen.h>
31
32 #include <f2fs_fs.h>
33
34 #ifdef HAVE_LINUX_BLKZONED_H
35
get_sysfs_path(struct device_info * dev,const char * attr,char * buf,size_t buflen)36 int get_sysfs_path(struct device_info *dev, const char *attr,
37 char *buf, size_t buflen)
38 {
39 struct stat statbuf;
40 char str[PATH_MAX];
41 char sysfs_path[PATH_MAX];
42 ssize_t len;
43 char *delim;
44 int ret;
45
46 if (stat(dev->path, &statbuf) < 0)
47 return -1;
48
49 snprintf(str, sizeof(str), "/sys/dev/block/%d:%d",
50 major(statbuf.st_rdev), minor(statbuf.st_rdev));
51 len = readlink(str, buf, buflen - 1);
52 if (len < 0)
53 return -1;
54 buf[len] = '\0';
55
56 ret = snprintf(sysfs_path, sizeof(sysfs_path),
57 "/sys/dev/block/%s", buf);
58 if (ret >= sizeof(sysfs_path))
59 return -1;
60
61 /* Test if the device is a partition */
62 ret = snprintf(str, sizeof(str), "%s/partition", sysfs_path);
63 if (ret >= sizeof(str))
64 return -1;
65 ret = stat(str, &statbuf);
66 if (ret) {
67 if (errno == ENOENT) {
68 /* Not a partition */
69 goto out;
70 }
71 return -1;
72 }
73
74 /*
75 * The device is a partition: remove the device name from the
76 * attribute file path to obtain the sysfs path of the holder device.
77 * e.g.: /sys/dev/block/.../sda/sda1 -> /sys/dev/block/.../sda
78 */
79 delim = strrchr(sysfs_path, '/');
80 if (!delim)
81 return -1;
82 *delim = '\0';
83
84 out:
85 ret = snprintf(buf, buflen, "%s/%s", sysfs_path, attr);
86 if (ret >= buflen)
87 return -1;
88
89 return 0;
90 }
91
f2fs_get_zoned_model(int i)92 int f2fs_get_zoned_model(int i)
93 {
94 struct device_info *dev = c.devices + i;
95 char str[PATH_MAX];
96 FILE *file;
97 int res;
98
99 /* Check that this is a zoned block device */
100 res = get_sysfs_path(dev, "queue/zoned", str, sizeof(str));
101 if (res != 0) {
102 MSG(0, "\tInfo: can't find /sys, assuming normal block device\n");
103 dev->zoned_model = F2FS_ZONED_NONE;
104 return 0;
105 }
106
107 file = fopen(str, "r");
108 if (!file) {
109 /*
110 * The kernel does not support zoned block devices, but we have
111 * a block device file. This means that if the zoned file is
112 * not found, then the device is not zoned or is zoned but can
113 * be randomly written (i.e. host-aware zoned model).
114 * Treat the device as a regular block device. Otherwise, signal
115 * the failure to verify the disk zone model.
116 */
117 if (errno == ENOENT) {
118 dev->zoned_model = F2FS_ZONED_NONE;
119 return 0;
120 }
121 MSG(0, "\tError: Failed to check the device zoned model\n");
122 return -1;
123 }
124
125 memset(str, 0, sizeof(str));
126 res = fscanf(file, "%s", str);
127 fclose(file);
128
129 if (res != 1) {
130 MSG(0, "\tError: Failed to parse the device zoned model\n");
131 return -1;
132 }
133
134 if (strcmp(str, "none") == 0) {
135 /* Regular block device */
136 dev->zoned_model = F2FS_ZONED_NONE;
137 } else if (strcmp(str, "host-aware") == 0) {
138 /* Host-aware zoned block device: can be randomly written */
139 dev->zoned_model = F2FS_ZONED_HA;
140 } else if (strcmp(str, "host-managed") == 0) {
141 /* Host-managed zoned block device: sequential writes needed */
142 dev->zoned_model = F2FS_ZONED_HM;
143 } else {
144 MSG(0, "\tError: Unsupported device zoned model\n");
145 return -1;
146 }
147
148 return 0;
149 }
150
f2fs_get_zone_blocks(int i)151 int f2fs_get_zone_blocks(int i)
152 {
153 struct device_info *dev = c.devices + i;
154 uint64_t sectors;
155 char str[PATH_MAX];
156 FILE *file;
157 int res;
158
159 /* Get zone size */
160 dev->zone_blocks = 0;
161
162 res = get_sysfs_path(dev, "queue/chunk_sectors", str, sizeof(str));
163 if (res != 0) {
164 MSG(0, "\tError: Failed to get device sysfs attribute path\n");
165 return -1;
166 }
167
168 file = fopen(str, "r");
169 if (!file)
170 return -1;
171
172 memset(str, 0, sizeof(str));
173 res = fscanf(file, "%s", str);
174 fclose(file);
175
176 if (res != 1)
177 return -1;
178
179 sectors = atol(str);
180 if (!sectors)
181 return -1;
182
183 dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - 9);
184 sectors = (sectors << 9) / c.sector_size;
185
186 /*
187 * Total number of zones: there may
188 * be a last smaller runt zone.
189 */
190 dev->nr_zones = dev->total_sectors / sectors;
191 if (dev->total_sectors % sectors)
192 dev->nr_zones++;
193
194 return 0;
195 }
196
f2fs_report_zone(int i,u_int64_t sector,void * blkzone)197 int f2fs_report_zone(int i, u_int64_t sector, void *blkzone)
198 {
199 struct blk_zone *blkz = (struct blk_zone *)blkzone;
200 struct blk_zone_report *rep;
201 int ret = -1;
202
203 rep = malloc(sizeof(struct blk_zone_report) + sizeof(struct blk_zone));
204 if (!rep) {
205 ERR_MSG("No memory for report zones\n");
206 return -ENOMEM;
207 }
208
209 rep->sector = sector;
210 rep->nr_zones = 1;
211 ret = ioctl(c.devices[i].fd, BLKREPORTZONE, rep);
212 if (ret != 0) {
213 ret = -errno;
214 ERR_MSG("ioctl BLKREPORTZONE failed: errno=%d\n", errno);
215 goto out;
216 }
217
218 *blkz = *(struct blk_zone *)(rep + 1);
219 out:
220 free(rep);
221 return ret;
222 }
223
224 #define F2FS_REPORT_ZONES_BUFSZ 524288
225
f2fs_report_zones(int j,report_zones_cb_t * report_zones_cb,void * opaque)226 int f2fs_report_zones(int j, report_zones_cb_t *report_zones_cb, void *opaque)
227 {
228 struct device_info *dev = c.devices + j;
229 struct blk_zone_report *rep;
230 struct blk_zone *blkz;
231 unsigned int i, n = 0;
232 u_int64_t total_sectors = (dev->total_sectors * c.sector_size)
233 >> SECTOR_SHIFT;
234 u_int64_t sector = 0;
235 int ret = -1;
236
237 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
238 if (!rep) {
239 ERR_MSG("No memory for report zones\n");
240 return -ENOMEM;
241 }
242
243 while (sector < total_sectors) {
244
245 /* Get zone info */
246 rep->sector = sector;
247 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
248 / sizeof(struct blk_zone);
249
250 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
251 if (ret != 0) {
252 ret = -errno;
253 ERR_MSG("ioctl BLKREPORTZONE failed: errno=%d\n",
254 errno);
255 goto out;
256 }
257
258 if (!rep->nr_zones) {
259 ret = -EIO;
260 ERR_MSG("Unexpected ioctl BLKREPORTZONE result\n");
261 goto out;
262 }
263
264 blkz = (struct blk_zone *)(rep + 1);
265 for (i = 0; i < rep->nr_zones; i++) {
266 ret = report_zones_cb(n, blkz, opaque);
267 if (ret)
268 goto out;
269 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
270 n++;
271 blkz++;
272 }
273 }
274 out:
275 free(rep);
276 return ret;
277 }
278
f2fs_check_zones(int j)279 int f2fs_check_zones(int j)
280 {
281 struct device_info *dev = c.devices + j;
282 struct blk_zone_report *rep;
283 struct blk_zone *blkz;
284 unsigned int i, n = 0;
285 u_int64_t total_sectors;
286 u_int64_t sector;
287 int last_is_conv = 1;
288 int ret = -1;
289
290 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
291 if (!rep) {
292 ERR_MSG("No memory for report zones\n");
293 return -ENOMEM;
294 }
295
296 dev->zone_cap_blocks = malloc(dev->nr_zones * sizeof(size_t));
297 if (!dev->zone_cap_blocks) {
298 ERR_MSG("No memory for zone capacity list.\n");
299 return -ENOMEM;
300 }
301 memset(dev->zone_cap_blocks, 0, (dev->nr_zones * sizeof(size_t)));
302
303 dev->nr_rnd_zones = 0;
304 sector = 0;
305 total_sectors = (dev->total_sectors * c.sector_size) >> 9;
306
307 while (sector < total_sectors) {
308
309 /* Get zone info */
310 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
311 rep->sector = sector;
312 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
313 / sizeof(struct blk_zone);
314
315 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
316 if (ret != 0) {
317 ret = -errno;
318 ERR_MSG("ioctl BLKREPORTZONE failed\n");
319 goto out;
320 }
321
322 if (!rep->nr_zones)
323 break;
324
325 blkz = (struct blk_zone *)(rep + 1);
326 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
327
328 if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY ||
329 blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE)
330 last_is_conv = 0;
331 if (blk_zone_conv(blkz) ||
332 blk_zone_seq_pref(blkz)) {
333 if (last_is_conv)
334 dev->nr_rnd_zones++;
335 } else {
336 last_is_conv = 0;
337 }
338
339 if (blk_zone_conv(blkz)) {
340 DBG(2,
341 "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n",
342 n,
343 blk_zone_cond(blkz),
344 blk_zone_cond_str(blkz),
345 blk_zone_sector(blkz),
346 blk_zone_length(blkz));
347 dev->zone_cap_blocks[n] =
348 blk_zone_length(blkz) >>
349 (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
350 } else {
351 DBG(2,
352 "Zone %05u: type 0x%x (%s), cond 0x%x (%s),"
353 " need_reset %d, non_seq %d, sector %llu,"
354 " %llu sectors, capacity %llu,"
355 " wp sector %llu\n",
356 n,
357 blk_zone_type(blkz),
358 blk_zone_type_str(blkz),
359 blk_zone_cond(blkz),
360 blk_zone_cond_str(blkz),
361 blk_zone_need_reset(blkz),
362 blk_zone_non_seq(blkz),
363 blk_zone_sector(blkz),
364 blk_zone_length(blkz),
365 blk_zone_capacity(blkz, rep->flags),
366 blk_zone_wp_sector(blkz));
367 dev->zone_cap_blocks[n] =
368 blk_zone_capacity(blkz, rep->flags) >>
369 (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
370 }
371
372 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
373 n++;
374 blkz++;
375 }
376 }
377
378 if (sector != total_sectors) {
379 ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n",
380 (unsigned long long)(sector << 9) / c.sector_size,
381 (unsigned long long)dev->total_sectors);
382 ret = -1;
383 goto out;
384 }
385
386 if (n != dev->nr_zones) {
387 ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n",
388 dev->nr_zones, n);
389 ret = -1;
390 goto out;
391 }
392
393 /*
394 * For a multi-device volume, fixed position metadata blocks are
395 * stored * only on the first device of the volume. Checking for the
396 * presence of * conventional zones (randomly writeabl zones) for
397 * storing these blocks * on a host-managed device is thus needed only
398 * for the device index 0.
399 */
400 if (j == 0 && dev->zoned_model == F2FS_ZONED_HM &&
401 !dev->nr_rnd_zones) {
402 ERR_MSG("No conventional zone for super block\n");
403 ret = -1;
404 }
405 out:
406 free(rep);
407 return ret;
408 }
409
f2fs_reset_zone(int i,void * blkzone)410 int f2fs_reset_zone(int i, void *blkzone)
411 {
412 struct blk_zone *blkz = (struct blk_zone *)blkzone;
413 struct device_info *dev = c.devices + i;
414 struct blk_zone_range range;
415 int ret;
416
417 if (!blk_zone_seq(blkz) || blk_zone_empty(blkz))
418 return 0;
419
420 /* Non empty sequential zone: reset */
421 range.sector = blk_zone_sector(blkz);
422 range.nr_sectors = blk_zone_length(blkz);
423 ret = ioctl(dev->fd, BLKRESETZONE, &range);
424 if (ret != 0) {
425 ret = -errno;
426 ERR_MSG("ioctl BLKRESETZONE failed: errno=%d\n", errno);
427 }
428
429 return ret;
430 }
431
f2fs_reset_zones(int j)432 int f2fs_reset_zones(int j)
433 {
434 struct device_info *dev = c.devices + j;
435 struct blk_zone_report *rep;
436 struct blk_zone *blkz;
437 struct blk_zone_range range;
438 u_int64_t total_sectors;
439 u_int64_t sector;
440 unsigned int i;
441 int ret = -1;
442
443 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
444 if (!rep) {
445 ERR_MSG("No memory for report zones\n");
446 return -1;
447 }
448
449 sector = 0;
450 total_sectors = (dev->total_sectors * c.sector_size) >> 9;
451 while (sector < total_sectors) {
452
453 /* Get zone info */
454 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
455 rep->sector = sector;
456 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
457 / sizeof(struct blk_zone);
458
459 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
460 if (ret != 0) {
461 ret = -errno;
462 ERR_MSG("ioctl BLKREPORTZONES failed\n");
463 goto out;
464 }
465
466 if (!rep->nr_zones)
467 break;
468
469 blkz = (struct blk_zone *)(rep + 1);
470 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
471 if (blk_zone_seq(blkz) &&
472 !blk_zone_empty(blkz)) {
473 /* Non empty sequential zone: reset */
474 range.sector = blk_zone_sector(blkz);
475 range.nr_sectors = blk_zone_length(blkz);
476 ret = ioctl(dev->fd, BLKRESETZONE, &range);
477 if (ret != 0) {
478 ret = -errno;
479 ERR_MSG("ioctl BLKRESETZONE failed\n");
480 goto out;
481 }
482 }
483 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
484 blkz++;
485 }
486 }
487 out:
488 free(rep);
489 if (!ret)
490 MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20);
491 return ret;
492 }
493
f2fs_get_usable_segments(struct f2fs_super_block * sb)494 uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
495 {
496 #ifdef HAVE_BLK_ZONE_REP_V2
497 int i, j;
498 uint32_t usable_segs = 0, zone_segs;
499
500 for (i = 0; i < c.ndevs; i++) {
501 if (c.devices[i].zoned_model != F2FS_ZONED_HM) {
502 usable_segs += c.devices[i].total_segments;
503 continue;
504 }
505 for (j = 0; j < c.devices[i].nr_zones; j++) {
506 zone_segs = c.devices[i].zone_cap_blocks[j] >>
507 get_sb(log_blocks_per_seg);
508 if (c.devices[i].zone_cap_blocks[j] %
509 DEFAULT_BLOCKS_PER_SEGMENT)
510 usable_segs += zone_segs + 1;
511 else
512 usable_segs += zone_segs;
513 }
514 }
515 usable_segs -= (get_sb(main_blkaddr) - get_sb(segment0_blkaddr)) >>
516 get_sb(log_blocks_per_seg);
517 return usable_segs;
518 #endif
519 return get_sb(segment_count_main);
520 }
521
522 #else
523
f2fs_report_zone(int i,u_int64_t UNUSED (sector),void * UNUSED (blkzone))524 int f2fs_report_zone(int i, u_int64_t UNUSED(sector), void *UNUSED(blkzone))
525 {
526 ERR_MSG("%d: Unsupported zoned block device\n", i);
527 return -1;
528 }
529
f2fs_report_zones(int i,report_zones_cb_t * UNUSED (report_zones_cb),void * UNUSED (opaque))530 int f2fs_report_zones(int i, report_zones_cb_t *UNUSED(report_zones_cb),
531 void *UNUSED(opaque))
532 {
533 ERR_MSG("%d: Unsupported zoned block device\n", i);
534 return -1;
535 }
536
f2fs_get_zoned_model(int i)537 int f2fs_get_zoned_model(int i)
538 {
539 struct device_info *dev = c.devices + i;
540
541 c.zoned_mode = 0;
542 dev->zoned_model = F2FS_ZONED_NONE;
543 return 0;
544 }
545
f2fs_get_zone_blocks(int i)546 int f2fs_get_zone_blocks(int i)
547 {
548 struct device_info *dev = c.devices + i;
549
550 c.zoned_mode = 0;
551 dev->nr_zones = 0;
552 dev->zone_blocks = 0;
553 dev->zoned_model = F2FS_ZONED_NONE;
554
555 return 0;
556 }
557
f2fs_check_zones(int i)558 int f2fs_check_zones(int i)
559 {
560 ERR_MSG("%d: Unsupported zoned block device\n", i);
561 return -1;
562 }
563
f2fs_reset_zone(int i,void * UNUSED (blkzone))564 int f2fs_reset_zone(int i, void *UNUSED(blkzone))
565 {
566 ERR_MSG("%d: Unsupported zoned block device\n", i);
567 return -1;
568 }
569
f2fs_reset_zones(int i)570 int f2fs_reset_zones(int i)
571 {
572 ERR_MSG("%d: Unsupported zoned block device\n", i);
573 return -1;
574 }
575
f2fs_get_usable_segments(struct f2fs_super_block * sb)576 uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
577 {
578 return get_sb(segment_count_main);
579 }
580 #endif
581
582