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