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