Home | History | Annotate | Download | only in lib
      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