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 #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