• 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 #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