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