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