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