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 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 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 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 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 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 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->nr_rnd_zones = 0; 295 sector = 0; 296 total_sectors = (dev->total_sectors * c.sector_size) >> 9; 297 298 while (sector < total_sectors) { 299 300 /* Get zone info */ 301 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ); 302 rep->sector = sector; 303 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report)) 304 / sizeof(struct blk_zone); 305 306 ret = ioctl(dev->fd, BLKREPORTZONE, rep); 307 if (ret != 0) { 308 ret = -errno; 309 ERR_MSG("ioctl BLKREPORTZONE failed\n"); 310 goto out; 311 } 312 313 if (!rep->nr_zones) 314 break; 315 316 blkz = (struct blk_zone *)(rep + 1); 317 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) { 318 319 if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY || 320 blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE) 321 last_is_conv = 0; 322 if (blk_zone_conv(blkz) || 323 blk_zone_seq_pref(blkz)) { 324 if (last_is_conv) 325 dev->nr_rnd_zones++; 326 } else { 327 last_is_conv = 0; 328 } 329 330 if (blk_zone_conv(blkz)) { 331 DBG(2, 332 "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n", 333 n, 334 blk_zone_cond(blkz), 335 blk_zone_cond_str(blkz), 336 blk_zone_sector(blkz), 337 blk_zone_length(blkz)); 338 } else { 339 DBG(2, 340 "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, " 341 "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n", 342 n, 343 blk_zone_type(blkz), 344 blk_zone_type_str(blkz), 345 blk_zone_cond(blkz), 346 blk_zone_cond_str(blkz), 347 blk_zone_need_reset(blkz), 348 blk_zone_non_seq(blkz), 349 blk_zone_sector(blkz), 350 blk_zone_length(blkz), 351 blk_zone_wp_sector(blkz)); 352 } 353 354 sector = blk_zone_sector(blkz) + blk_zone_length(blkz); 355 n++; 356 blkz++; 357 } 358 } 359 360 if (sector != total_sectors) { 361 ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n", 362 (unsigned long long)(sector << 9) / c.sector_size, 363 (unsigned long long)dev->total_sectors); 364 ret = -1; 365 goto out; 366 } 367 368 if (n != dev->nr_zones) { 369 ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n", 370 dev->nr_zones, n); 371 ret = -1; 372 goto out; 373 } 374 375 /* 376 * For a multi-device volume, fixed position metadata blocks are 377 * stored * only on the first device of the volume. Checking for the 378 * presence of * conventional zones (randomly writeabl zones) for 379 * storing these blocks * on a host-managed device is thus needed only 380 * for the device index 0. 381 */ 382 if (j == 0 && dev->zoned_model == F2FS_ZONED_HM && 383 !dev->nr_rnd_zones) { 384 ERR_MSG("No conventional zone for super block\n"); 385 ret = -1; 386 } 387 out: 388 free(rep); 389 return ret; 390 } 391 392 int f2fs_reset_zone(int i, void *blkzone) 393 { 394 struct blk_zone *blkz = (struct blk_zone *)blkzone; 395 struct device_info *dev = c.devices + i; 396 struct blk_zone_range range; 397 int ret; 398 399 if (!blk_zone_seq(blkz) || blk_zone_empty(blkz)) 400 return 0; 401 402 /* Non empty sequential zone: reset */ 403 range.sector = blk_zone_sector(blkz); 404 range.nr_sectors = blk_zone_length(blkz); 405 ret = ioctl(dev->fd, BLKRESETZONE, &range); 406 if (ret != 0) { 407 ret = -errno; 408 ERR_MSG("ioctl BLKRESETZONE failed: errno=%d\n", errno); 409 } 410 411 return ret; 412 } 413 414 int f2fs_reset_zones(int j) 415 { 416 struct device_info *dev = c.devices + j; 417 struct blk_zone_report *rep; 418 struct blk_zone *blkz; 419 struct blk_zone_range range; 420 u_int64_t total_sectors; 421 u_int64_t sector; 422 unsigned int i; 423 int ret = -1; 424 425 rep = malloc(F2FS_REPORT_ZONES_BUFSZ); 426 if (!rep) { 427 ERR_MSG("No memory for report zones\n"); 428 return -1; 429 } 430 431 sector = 0; 432 total_sectors = (dev->total_sectors * c.sector_size) >> 9; 433 while (sector < total_sectors) { 434 435 /* Get zone info */ 436 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ); 437 rep->sector = sector; 438 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report)) 439 / sizeof(struct blk_zone); 440 441 ret = ioctl(dev->fd, BLKREPORTZONE, rep); 442 if (ret != 0) { 443 ret = -errno; 444 ERR_MSG("ioctl BLKREPORTZONES failed\n"); 445 goto out; 446 } 447 448 if (!rep->nr_zones) 449 break; 450 451 blkz = (struct blk_zone *)(rep + 1); 452 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) { 453 if (blk_zone_seq(blkz) && 454 !blk_zone_empty(blkz)) { 455 /* Non empty sequential zone: reset */ 456 range.sector = blk_zone_sector(blkz); 457 range.nr_sectors = blk_zone_length(blkz); 458 ret = ioctl(dev->fd, BLKRESETZONE, &range); 459 if (ret != 0) { 460 ret = -errno; 461 ERR_MSG("ioctl BLKRESETZONE failed\n"); 462 goto out; 463 } 464 } 465 sector = blk_zone_sector(blkz) + blk_zone_length(blkz); 466 blkz++; 467 } 468 } 469 out: 470 free(rep); 471 if (!ret) 472 MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20); 473 return ret; 474 } 475 476 #else 477 478 int f2fs_report_zone(int i, u_int64_t UNUSED(sector), void *UNUSED(blkzone)) 479 { 480 ERR_MSG("%d: Unsupported zoned block device\n", i); 481 return -1; 482 } 483 484 int f2fs_report_zones(int i, report_zones_cb_t *UNUSED(report_zones_cb), 485 void *UNUSED(opaque)) 486 { 487 ERR_MSG("%d: Unsupported zoned block device\n", i); 488 return -1; 489 } 490 491 int f2fs_get_zoned_model(int i) 492 { 493 struct device_info *dev = c.devices + i; 494 495 c.zoned_mode = 0; 496 dev->zoned_model = F2FS_ZONED_NONE; 497 return 0; 498 } 499 500 int f2fs_get_zone_blocks(int i) 501 { 502 struct device_info *dev = c.devices + i; 503 504 c.zoned_mode = 0; 505 dev->nr_zones = 0; 506 dev->zone_blocks = 0; 507 dev->zoned_model = F2FS_ZONED_NONE; 508 509 return 0; 510 } 511 512 int f2fs_check_zones(int i) 513 { 514 ERR_MSG("%d: Unsupported zoned block device\n", i); 515 return -1; 516 } 517 518 int f2fs_reset_zone(int i, void *UNUSED(blkzone)) 519 { 520 ERR_MSG("%d: Unsupported zoned block device\n", i); 521 return -1; 522 } 523 524 int f2fs_reset_zones(int i) 525 { 526 ERR_MSG("%d: Unsupported zoned block device\n", i); 527 return -1; 528 } 529 530 #endif 531 532