1 /** 2 * libf2fs_zoned.c 3 * 4 * Copyright (c) 2016 Western Digital Corporation. 5 * Written by: Damien Le Moal <damien.lemoal (at) wdc.com> 6 * 7 * Dual licensed under the GPL or LGPL version 2 licenses. 8 */ 9 #define _LARGEFILE64_SOURCE 10 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 #include <sys/ioctl.h> 19 #include <libgen.h> 20 21 #include <f2fs_fs.h> 22 23 #ifdef HAVE_LINUX_BLKZONED_H 24 25 void f2fs_get_zoned_model(int i) 26 { 27 struct device_info *dev = c.devices + i; 28 char str[128]; 29 FILE *file; 30 int res; 31 32 /* Check that this is a zoned block device */ 33 snprintf(str, sizeof(str), 34 "/sys/block/%s/queue/zoned", 35 basename(dev->path)); 36 file = fopen(str, "r"); 37 if (!file) 38 goto not_zoned; 39 40 memset(str, 0, sizeof(str)); 41 res = fscanf(file, "%s", str); 42 fclose(file); 43 44 if (res != 1) 45 goto not_zoned; 46 47 if (strcmp(str, "host-aware") == 0) { 48 dev->zoned_model = F2FS_ZONED_HA; 49 return; 50 } 51 if (strcmp(str, "host-managed") == 0) { 52 dev->zoned_model = F2FS_ZONED_HM; 53 return; 54 } 55 56 not_zoned: 57 dev->zoned_model = F2FS_ZONED_NONE; 58 } 59 60 int f2fs_get_zone_blocks(int i) 61 { 62 struct device_info *dev = c.devices + i; 63 uint64_t sectors; 64 char str[128]; 65 FILE *file; 66 int res; 67 68 /* Get zone size */ 69 dev->zone_blocks = 0; 70 71 snprintf(str, sizeof(str), 72 "/sys/block/%s/queue/chunk_sectors", 73 basename(dev->path)); 74 file = fopen(str, "r"); 75 if (!file) 76 return -1; 77 78 memset(str, 0, sizeof(str)); 79 res = fscanf(file, "%s", str); 80 fclose(file); 81 82 if (res != 1) 83 return -1; 84 85 sectors = atol(str); 86 if (!sectors) 87 return -1; 88 89 dev->zone_blocks = sectors >> (F2FS_BLKSIZE_BITS - 9); 90 sectors = (sectors << 9) / c.sector_size; 91 92 /* 93 * Total number of zones: there may 94 * be a last smaller runt zone. 95 */ 96 dev->nr_zones = dev->total_sectors / sectors; 97 if (dev->total_sectors % sectors) 98 dev->nr_zones++; 99 100 return 0; 101 } 102 103 #define F2FS_REPORT_ZONES_BUFSZ 524288 104 105 int f2fs_check_zones(int j) 106 { 107 struct device_info *dev = c.devices + j; 108 struct blk_zone_report *rep; 109 struct blk_zone *blkz; 110 unsigned int i, n = 0; 111 u_int64_t total_sectors; 112 u_int64_t sector; 113 int last_is_conv = 1; 114 int ret = -1; 115 116 rep = malloc(F2FS_REPORT_ZONES_BUFSZ); 117 if (!rep) { 118 ERR_MSG("No memory for report zones\n"); 119 return -ENOMEM; 120 } 121 122 dev->nr_rnd_zones = 0; 123 sector = 0; 124 total_sectors = (dev->total_sectors * c.sector_size) >> 9; 125 126 while (sector < total_sectors) { 127 128 /* Get zone info */ 129 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ); 130 rep->sector = sector; 131 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report)) 132 / sizeof(struct blk_zone); 133 134 ret = ioctl(dev->fd, BLKREPORTZONE, rep); 135 if (ret != 0) { 136 ret = -errno; 137 ERR_MSG("ioctl BLKREPORTZONE failed\n"); 138 goto out; 139 } 140 141 if (!rep->nr_zones) 142 break; 143 144 blkz = (struct blk_zone *)(rep + 1); 145 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) { 146 147 if (blk_zone_cond(blkz) == BLK_ZONE_COND_READONLY || 148 blk_zone_cond(blkz) == BLK_ZONE_COND_OFFLINE) 149 last_is_conv = 0; 150 if (blk_zone_conv(blkz) || 151 blk_zone_seq_pref(blkz)) { 152 if (last_is_conv) 153 dev->nr_rnd_zones++; 154 } else { 155 last_is_conv = 0; 156 } 157 158 if (blk_zone_conv(blkz)) { 159 DBG(2, 160 "Zone %05u: Conventional, cond 0x%x (%s), sector %llu, %llu sectors\n", 161 n, 162 blk_zone_cond(blkz), 163 blk_zone_cond_str(blkz), 164 blk_zone_sector(blkz), 165 blk_zone_length(blkz)); 166 } else { 167 DBG(2, 168 "Zone %05u: type 0x%x (%s), cond 0x%x (%s), need_reset %d, " 169 "non_seq %d, sector %llu, %llu sectors, wp sector %llu\n", 170 n, 171 blk_zone_type(blkz), 172 blk_zone_type_str(blkz), 173 blk_zone_cond(blkz), 174 blk_zone_cond_str(blkz), 175 blk_zone_need_reset(blkz), 176 blk_zone_non_seq(blkz), 177 blk_zone_sector(blkz), 178 blk_zone_length(blkz), 179 blk_zone_wp_sector(blkz)); 180 } 181 182 sector = blk_zone_sector(blkz) + blk_zone_length(blkz); 183 n++; 184 blkz++; 185 } 186 } 187 188 if (sector != total_sectors) { 189 ERR_MSG("Invalid zones: last sector reported is %llu, expected %llu\n", 190 (unsigned long long)(sector << 9) / c.sector_size, 191 (unsigned long long)dev->total_sectors); 192 ret = -1; 193 goto out; 194 } 195 196 if (n != dev->nr_zones) { 197 ERR_MSG("Inconsistent number of zones: expected %u zones, got %u\n", 198 dev->nr_zones, n); 199 ret = -1; 200 goto out; 201 } 202 203 if (dev->zoned_model == F2FS_ZONED_HM && 204 !dev->nr_rnd_zones) { 205 ERR_MSG("No conventional zone for super block\n"); 206 ret = -1; 207 } 208 out: 209 free(rep); 210 return ret; 211 } 212 213 int f2fs_reset_zones(int j) 214 { 215 struct device_info *dev = c.devices + j; 216 struct blk_zone_report *rep; 217 struct blk_zone *blkz; 218 struct blk_zone_range range; 219 u_int64_t total_sectors; 220 u_int64_t sector; 221 unsigned int i; 222 int ret = -1; 223 224 rep = malloc(F2FS_REPORT_ZONES_BUFSZ); 225 if (!rep) { 226 ERR_MSG("No memory for report zones\n"); 227 return -1; 228 } 229 230 sector = 0; 231 total_sectors = (dev->total_sectors * c.sector_size) >> 9; 232 while (sector < total_sectors) { 233 234 /* Get zone info */ 235 memset(rep, 0, F2FS_REPORT_ZONES_BUFSZ); 236 rep->sector = sector; 237 rep->nr_zones = (F2FS_REPORT_ZONES_BUFSZ - sizeof(struct blk_zone_report)) 238 / sizeof(struct blk_zone); 239 240 ret = ioctl(dev->fd, BLKREPORTZONE, rep); 241 if (ret != 0) { 242 ret = -errno; 243 ERR_MSG("ioctl BLKREPORTZONES failed\n"); 244 goto out; 245 } 246 247 if (!rep->nr_zones) 248 break; 249 250 blkz = (struct blk_zone *)(rep + 1); 251 for (i = 0; i < rep->nr_zones && sector < total_sectors; i++) { 252 if (blk_zone_seq(blkz) && 253 !blk_zone_empty(blkz)) { 254 /* Non empty sequential zone: reset */ 255 range.sector = blk_zone_sector(blkz); 256 range.nr_sectors = blk_zone_length(blkz); 257 ret = ioctl(dev->fd, BLKRESETZONE, &range); 258 if (ret != 0) { 259 ret = -errno; 260 ERR_MSG("ioctl BLKRESETZONE failed\n"); 261 goto out; 262 } 263 } 264 sector = blk_zone_sector(blkz) + blk_zone_length(blkz); 265 blkz++; 266 } 267 } 268 out: 269 free(rep); 270 if (!ret) 271 MSG(0, "Info: Discarded %"PRIu64" MB\n", (sector << 9) >> 20); 272 return ret; 273 } 274 275 #else 276 277 void f2fs_get_zoned_model(int i) 278 { 279 struct device_info *dev = c.devices + i; 280 281 c.zoned_mode = 0; 282 dev->zoned_model = F2FS_ZONED_NONE; 283 } 284 285 int f2fs_get_zone_blocks(int i) 286 { 287 struct device_info *dev = c.devices + i; 288 289 c.zoned_mode = 0; 290 dev->nr_zones = 0; 291 dev->zone_blocks = 0; 292 dev->zoned_model = F2FS_ZONED_NONE; 293 294 return 0; 295 } 296 297 int f2fs_check_zones(int i) 298 { 299 ERR_MSG("%d: Zoned block devices are not supported\n", i); 300 return -1; 301 } 302 303 int f2fs_reset_zones(int i) 304 { 305 ERR_MSG("%d: Zoned block devices are not supported\n", i); 306 return -1; 307 } 308 309 #endif 310 311