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 #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_chunk_sectors(struct device_info * dev)151 uint32_t f2fs_get_zone_chunk_sectors(struct device_info *dev)
152 {
153 uint32_t sectors;
154 char str[PATH_MAX];
155 FILE *file;
156 int res;
157
158 res = get_sysfs_path(dev, "queue/chunk_sectors", str, sizeof(str));
159 if (res != 0) {
160 MSG(0, "\tError: Failed to get device sysfs attribute path\n");
161 return 0;
162 }
163
164 file = fopen(str, "r");
165 if (!file)
166 return 0;
167
168 memset(str, 0, sizeof(str));
169 res = fscanf(file, "%s", str);
170 fclose(file);
171
172 if (res != 1)
173 return 0;
174
175 sectors = atoi(str);
176
177 return sectors;
178 }
179
f2fs_get_zone_blocks(int i)180 int f2fs_get_zone_blocks(int i)
181 {
182 struct device_info *dev = c.devices + i;
183 uint64_t sectors;
184
185 /* Get zone size */
186 dev->zone_blocks = 0;
187
188 sectors = f2fs_get_zone_chunk_sectors(dev);
189 if (!sectors)
190 return -1;
191
192 dev->zone_size = sectors << SECTOR_SHIFT;
193 dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
194 sectors = dev->zone_size / c.sector_size;
195
196 /*
197 * Total number of zones: there may
198 * be a last smaller runt zone.
199 */
200 dev->nr_zones = dev->total_sectors / sectors;
201 if (dev->total_sectors % sectors)
202 dev->nr_zones++;
203
204 return 0;
205 }
206
f2fs_report_zone(int i,uint64_t sector,void * blkzone)207 int f2fs_report_zone(int i, uint64_t sector, void *blkzone)
208 {
209 struct blk_zone *blkz = (struct blk_zone *)blkzone;
210 struct blk_zone_report *rep;
211 int ret = -1;
212
213 rep = malloc(sizeof(struct blk_zone_report) + sizeof(struct blk_zone));
214 if (!rep) {
215 ERR_MSG("No memory for report zones\n");
216 return -ENOMEM;
217 }
218
219 rep->sector = sector;
220 rep->nr_zones = 1;
221 ret = ioctl(c.devices[i].fd, BLKREPORTZONE, rep);
222 if (ret != 0) {
223 ret = -errno;
224 ERR_MSG("ioctl BLKREPORTZONE failed: errno=%d\n", errno);
225 goto out;
226 }
227
228 *blkz = *(struct blk_zone *)(rep + 1);
229 out:
230 free(rep);
231 return ret;
232 }
233
234 #define F2FS_REPORT_ZONES_BUFSZ 524288
235
f2fs_report_zones(int j,report_zones_cb_t * report_zones_cb,void * opaque)236 int f2fs_report_zones(int j, report_zones_cb_t *report_zones_cb, void *opaque)
237 {
238 struct device_info *dev = c.devices + j;
239 struct blk_zone_report *rep;
240 struct blk_zone *blkz;
241 unsigned int i, n = 0;
242 uint64_t total_sectors = (dev->total_sectors * c.sector_size)
243 >> SECTOR_SHIFT;
244 uint64_t sector = 0;
245 int ret = -1;
246
247 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
248 if (!rep) {
249 ERR_MSG("No memory for report zones\n");
250 return -ENOMEM;
251 }
252
253 while (sector < total_sectors) {
254
255 /* Get zone info */
256 rep->sector = sector;
257 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
258 / sizeof(struct blk_zone);
259
260 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
261 if (ret != 0) {
262 ret = -errno;
263 ERR_MSG("ioctl BLKREPORTZONE failed: errno=%d\n",
264 errno);
265 goto out;
266 }
267
268 if (!rep->nr_zones) {
269 ret = -EIO;
270 ERR_MSG("Unexpected ioctl BLKREPORTZONE result\n");
271 goto out;
272 }
273
274 blkz = (struct blk_zone *)(rep + 1);
275 for (i = 0; i < rep->nr_zones; i++) {
276 ret = report_zones_cb(n, blkz, opaque);
277 if (ret)
278 goto out;
279 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
280 n++;
281 blkz++;
282 }
283 }
284 out:
285 free(rep);
286 return ret;
287 }
288
f2fs_check_zones(int j)289 int f2fs_check_zones(int j)
290 {
291 struct device_info *dev = c.devices + j;
292 struct blk_zone_report *rep;
293 struct blk_zone *blkz;
294 unsigned int i, n = 0;
295 uint64_t total_sectors;
296 uint64_t sector;
297 int last_is_conv = 1;
298 int ret = -1;
299
300 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
301 if (!rep) {
302 ERR_MSG("No memory for report zones\n");
303 return -ENOMEM;
304 }
305
306 dev->zone_cap_blocks = malloc(dev->nr_zones * sizeof(size_t));
307 if (!dev->zone_cap_blocks) {
308 ERR_MSG("No memory for zone capacity list.\n");
309 return -ENOMEM;
310 }
311 memset(dev->zone_cap_blocks, 0, (dev->nr_zones * sizeof(size_t)));
312
313 dev->nr_rnd_zones = 0;
314 sector = 0;
315 total_sectors = (dev->total_sectors * c.sector_size) >> 9;
316
317 while (sector < total_sectors) {
318
319 /* Get zone info */
320 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
321 rep->sector = sector;
322 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
323 / sizeof(struct blk_zone);
324
325 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
326 if (ret != 0) {
327 ret = -errno;
328 ERR_MSG("ioctl BLKREPORTZONE failed\n");
329 goto out;
330 }
331
332 if (!rep->nr_zones)
333 break;
334
335 blkz = (struct blk_zone *)(rep + 1);
336 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
337
338 if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY ||
339 blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE)
340 last_is_conv = 0;
341 if (blk_zone_conv(blkz) ||
342 blk_zone_seq_pref(blkz)) {
343 if (last_is_conv)
344 dev->nr_rnd_zones++;
345 } else {
346 last_is_conv = 0;
347 }
348
349 if (blk_zone_conv(blkz)) {
350 DBG(2,
351 "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n",
352 n,
353 blk_zone_cond(blkz),
354 blk_zone_cond_str(blkz),
355 blk_zone_sector(blkz),
356 blk_zone_length(blkz));
357 dev->zone_cap_blocks[n] =
358 blk_zone_length(blkz) >>
359 (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
360 } else {
361 DBG(2,
362 "Zone %05u: type 0x%x (%s), cond 0x%x (%s),"
363 " need_reset %d, non_seq %d, sector %llu,"
364 " %llu sectors, capacity %llu,"
365 " wp sector %llu\n",
366 n,
367 blk_zone_type(blkz),
368 blk_zone_type_str(blkz),
369 blk_zone_cond(blkz),
370 blk_zone_cond_str(blkz),
371 blk_zone_need_reset(blkz),
372 blk_zone_non_seq(blkz),
373 blk_zone_sector(blkz),
374 blk_zone_length(blkz),
375 blk_zone_capacity(blkz, rep->flags),
376 blk_zone_wp_sector(blkz));
377 dev->zone_cap_blocks[n] =
378 blk_zone_capacity(blkz, rep->flags) >>
379 (F2FS_BLKSIZE_BITS - SECTOR_SHIFT);
380 }
381
382 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
383 n++;
384 blkz++;
385 }
386 }
387
388 if (sector != total_sectors) {
389 ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n",
390 (unsigned long long)(sector << 9) / c.sector_size,
391 (unsigned long long)dev->total_sectors);
392 ret = -1;
393 goto out;
394 }
395
396 if (n != dev->nr_zones) {
397 ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n",
398 dev->nr_zones, n);
399 ret = -1;
400 goto out;
401 }
402
403 /*
404 * For a multi-device volume, fixed position metadata blocks are
405 * stored * only on the first device of the volume. Checking for the
406 * presence of * conventional zones (randomly writeabl zones) for
407 * storing these blocks * on a host-managed device is thus needed only
408 * for the device index 0.
409 */
410 if (j == 0 && dev->zoned_model == F2FS_ZONED_HM &&
411 !dev->nr_rnd_zones) {
412 ERR_MSG("No conventional zone for super block\n");
413 ret = -1;
414 }
415 out:
416 free(rep);
417 return ret;
418 }
419
f2fs_reset_zone(int i,void * blkzone)420 int f2fs_reset_zone(int i, void *blkzone)
421 {
422 struct blk_zone *blkz = (struct blk_zone *)blkzone;
423 struct device_info *dev = c.devices + i;
424 struct blk_zone_range range;
425 int ret;
426
427 if (!blk_zone_seq(blkz) || blk_zone_empty(blkz))
428 return 0;
429
430 /* Non empty sequential zone: reset */
431 range.sector = blk_zone_sector(blkz);
432 range.nr_sectors = blk_zone_length(blkz);
433 ret = ioctl(dev->fd, BLKRESETZONE, &range);
434 if (ret != 0) {
435 ret = -errno;
436 ERR_MSG("ioctl BLKRESETZONE failed: errno=%d\n", errno);
437 }
438
439 return ret;
440 }
441
f2fs_reset_zones(int j)442 int f2fs_reset_zones(int j)
443 {
444 struct device_info *dev = c.devices + j;
445 struct blk_zone_report *rep;
446 struct blk_zone *blkz;
447 struct blk_zone_range range;
448 uint64_t total_sectors;
449 uint64_t sector;
450 unsigned int i;
451 int ret = -1;
452
453 rep = malloc(F2FS_REPORT_ZONES_BUFSZ);
454 if (!rep) {
455 ERR_MSG("No memory for report zones\n");
456 return -1;
457 }
458
459 sector = 0;
460 total_sectors = (dev->total_sectors * c.sector_size) >> 9;
461 while (sector < total_sectors) {
462
463 /* Get zone info */
464 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ);
465 rep->sector = sector;
466 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report))
467 / sizeof(struct blk_zone);
468
469 ret = ioctl(dev->fd, BLKREPORTZONE, rep);
470 if (ret != 0) {
471 ret = -errno;
472 ERR_MSG("ioctl BLKREPORTZONES failed\n");
473 goto out;
474 }
475
476 if (!rep->nr_zones)
477 break;
478
479 blkz = (struct blk_zone *)(rep + 1);
480 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) {
481 if (blk_zone_seq(blkz) &&
482 !blk_zone_empty(blkz)) {
483 /* Non empty sequential zone: reset */
484 range.sector = blk_zone_sector(blkz);
485 range.nr_sectors = blk_zone_length(blkz);
486 ret = ioctl(dev->fd, BLKRESETZONE, &range);
487 if (ret != 0) {
488 ret = -errno;
489 ERR_MSG("ioctl BLKRESETZONE failed\n");
490 goto out;
491 }
492 }
493 sector = blk_zone_sector(blkz) + blk_zone_length(blkz);
494 blkz++;
495 }
496 }
497 out:
498 free(rep);
499 if (!ret)
500 MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20);
501 return ret;
502 }
503
f2fs_get_usable_segments(struct f2fs_super_block * sb)504 uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
505 {
506 #ifdef HAVE_BLK_ZONE_REP_V2
507 int i, j;
508 uint32_t usable_segs = 0, zone_segs;
509
510 if (c.func == RESIZE)
511 return get_sb(segment_count_main);
512
513 for (i = 0; i < c.ndevs; i++) {
514 if (c.devices[i].zoned_model != F2FS_ZONED_HM) {
515 usable_segs += c.devices[i].total_segments;
516 continue;
517 }
518 for (j = 0; j < c.devices[i].nr_zones; j++) {
519 zone_segs = c.devices[i].zone_cap_blocks[j] >>
520 get_sb(log_blocks_per_seg);
521 if (c.devices[i].zone_cap_blocks[j] %
522 DEFAULT_BLOCKS_PER_SEGMENT)
523 usable_segs += zone_segs + 1;
524 else
525 usable_segs += zone_segs;
526 }
527 }
528 usable_segs -= (get_sb(main_blkaddr) - get_sb(segment0_blkaddr)) >>
529 get_sb(log_blocks_per_seg);
530 return usable_segs;
531 #endif
532 return get_sb(segment_count_main);
533 }
534
535 #else
536
f2fs_report_zone(int i,uint64_t UNUSED (sector),void * UNUSED (blkzone))537 int f2fs_report_zone(int i, uint64_t UNUSED(sector), void *UNUSED(blkzone))
538 {
539 ERR_MSG("%d: Unsupported zoned block device\n", i);
540 return -1;
541 }
542
f2fs_report_zones(int i,report_zones_cb_t * UNUSED (report_zones_cb),void * UNUSED (opaque))543 int f2fs_report_zones(int i, report_zones_cb_t *UNUSED(report_zones_cb),
544 void *UNUSED(opaque))
545 {
546 ERR_MSG("%d: Unsupported zoned block device\n", i);
547 return -1;
548 }
549
f2fs_get_zoned_model(int i)550 int f2fs_get_zoned_model(int i)
551 {
552 struct device_info *dev = c.devices + i;
553
554 c.zoned_mode = 0;
555 dev->zoned_model = F2FS_ZONED_NONE;
556 return 0;
557 }
558
f2fs_get_zone_blocks(int i)559 int f2fs_get_zone_blocks(int i)
560 {
561 struct device_info *dev = c.devices + i;
562
563 c.zoned_mode = 0;
564 dev->nr_zones = 0;
565 dev->zone_blocks = 0;
566 dev->zoned_model = F2FS_ZONED_NONE;
567
568 return 0;
569 }
570
f2fs_check_zones(int i)571 int f2fs_check_zones(int i)
572 {
573 ERR_MSG("%d: Unsupported zoned block device\n", i);
574 return -1;
575 }
576
f2fs_reset_zone(int i,void * UNUSED (blkzone))577 int f2fs_reset_zone(int i, void *UNUSED(blkzone))
578 {
579 ERR_MSG("%d: Unsupported zoned block device\n", i);
580 return -1;
581 }
582
f2fs_reset_zones(int i)583 int f2fs_reset_zones(int i)
584 {
585 ERR_MSG("%d: Unsupported zoned block device\n", i);
586 return -1;
587 }
588
f2fs_get_usable_segments(struct f2fs_super_block * sb)589 uint32_t f2fs_get_usable_segments(struct f2fs_super_block *sb)
590 {
591 return get_sb(segment_count_main);
592 }
593 #endif
594
595