• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <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 
f2fs_get_zoned_model(int i)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 
f2fs_get_zone_blocks(int i)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 
f2fs_check_zones(int j)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 
f2fs_reset_zones(int j)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 
f2fs_get_zoned_model(int i)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 
f2fs_get_zone_blocks(int i)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 
f2fs_check_zones(int i)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 
f2fs_reset_zones(int i)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