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 #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 
f2fs_get_zoned_model(int i)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 
f2fs_get_zone_blocks(int i)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 
f2fs_check_zones(int j)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 
f2fs_reset_zones(int j)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 
f2fs_get_zoned_model(int i)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 
f2fs_get_zone_blocks(int i)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 
f2fs_check_zones(int i)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 
f2fs_reset_zones(int i)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