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