xref: /dflybsd-src/sbin/hammer/misc.c (revision d4b0d6715086a6d05c21e8e4dd1cfd5da335cf79)
1 /*
2  * Copyright (c) 2008 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Matthew Dillon <dillon@backplane.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  *
34  * $DragonFly: src/sbin/hammer/misc.c,v 1.5 2008/06/26 04:07:57 dillon Exp $
35  */
36 
37 #include "hammer_util.h"
38 
39 /*
40  * (taken from /usr/src/sys/vfs/hammer/hammer_btree.c)
41  *
42  * Compare two B-Tree elements, return -N, 0, or +N (e.g. similar to strcmp).
43  *
44  * Note that for this particular function a return value of -1, 0, or +1
45  * can denote a match if delete_tid is otherwise discounted.  A delete_tid
46  * of zero is considered to be 'infinity' in comparisons.
47  *
48  * See also hammer_rec_rb_compare() and hammer_rec_cmp() in hammer_object.c.
49  */
50 int
51 hammer_btree_cmp(hammer_base_elm_t key1, hammer_base_elm_t key2)
52 {
53 	if (key1->localization < key2->localization)
54 		return(-5);
55 	if (key1->localization > key2->localization)
56 		return(5);
57 
58 	if (key1->obj_id < key2->obj_id)
59 		return(-4);
60 	if (key1->obj_id > key2->obj_id)
61 		return(4);
62 
63 	if (key1->rec_type < key2->rec_type)
64 		return(-3);
65 	if (key1->rec_type > key2->rec_type)
66 		return(3);
67 
68 	if (key1->key < key2->key)
69 		return(-2);
70 	if (key1->key > key2->key)
71 		return(2);
72 
73 	if (key1->create_tid == 0) {
74 		if (key2->create_tid == 0)
75 			return(0);
76 		return(1);
77 	}
78 	if (key2->create_tid == 0)
79 		return(-1);
80 	if (key1->create_tid < key2->create_tid)
81 		return(-1);
82 	if (key1->create_tid > key2->create_tid)
83 		return(1);
84 	return(0);
85 }
86 
87 void
88 hammer_key_beg_init(hammer_base_elm_t base)
89 {
90 	bzero(base, sizeof(*base));
91 
92 	base->localization = HAMMER_MIN_LOCALIZATION;
93 	base->obj_id = HAMMER_MIN_OBJID;
94 	base->key = HAMMER_MIN_KEY;
95 	base->create_tid = 1;
96 	base->rec_type = HAMMER_MIN_RECTYPE;
97 }
98 
99 void
100 hammer_key_end_init(hammer_base_elm_t base)
101 {
102 	bzero(base, sizeof(*base));
103 
104 	base->localization = HAMMER_MAX_LOCALIZATION;
105 	base->obj_id = HAMMER_MAX_OBJID;
106 	base->key = HAMMER_MAX_KEY;
107 	base->create_tid = HAMMER_MAX_TID;
108 	base->rec_type = HAMMER_MAX_RECTYPE;
109 }
110 
111 int
112 getyn(void)
113 {
114 	char buf[256];
115 	int len;
116 
117 	if (fgets(buf, sizeof(buf), stdin) == NULL)
118 		return(0);
119 	len = strlen(buf);
120 	while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
121 		--len;
122 	buf[len] = 0;
123 	if (strcmp(buf, "y") == 0 ||
124 	    strcmp(buf, "yes") == 0 ||
125 	    strcmp(buf, "Y") == 0 ||
126 	    strcmp(buf, "YES") == 0) {
127 		return(1);
128 	}
129 	return(0);
130 }
131 
132 const char *
133 sizetostr(off_t size)
134 {
135 	static char buf[32];
136 
137 	if (size < 1024 / 2) {
138 		snprintf(buf, sizeof(buf), "%6.2fB", (double)size);
139 	} else if (size < 1024 * 1024 / 2) {
140 		snprintf(buf, sizeof(buf), "%6.2fKB",
141 			(double)size / 1024);
142 	} else if (size < 1024 * 1024 * 1024LL / 2) {
143 		snprintf(buf, sizeof(buf), "%6.2fMB",
144 			(double)size / (1024 * 1024));
145 	} else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
146 		snprintf(buf, sizeof(buf), "%6.2fGB",
147 			(double)size / (1024 * 1024 * 1024LL));
148 	} else {
149 		snprintf(buf, sizeof(buf), "%6.2fTB",
150 			(double)size / (1024 * 1024 * 1024LL * 1024LL));
151 	}
152 	return(buf);
153 }
154 
155 int
156 hammer_fs_to_vol(const char *fs, struct hammer_ioc_volume_list *p)
157 {
158 	struct hammer_ioc_volume_list ioc;
159 	int fd;
160 
161 	fd = open(fs, O_RDONLY);
162 	if (fd < 0) {
163 		perror("open");
164 		return(-1);
165 	}
166 
167 	bzero(&ioc, sizeof(ioc));
168 	ioc.nvols = HAMMER_MAX_VOLUMES;
169 	ioc.vols = malloc(ioc.nvols * sizeof(*ioc.vols));
170 	if (ioc.vols == NULL) {
171 		perror("malloc");
172 		close(fd);
173 		return(-1);
174 	}
175 
176 	if (ioctl(fd, HAMMERIOC_LIST_VOLUMES, &ioc) < 0) {
177 		perror("ioctl");
178 		close(fd);
179 		free(ioc.vols);
180 		return(-1);
181 	}
182 
183 	bcopy(&ioc, p, sizeof(ioc));
184 	close(fd);
185 
186 	return(0);
187 }
188 
189 int
190 hammer_fs_to_rootvol(const char *fs, char *buf, int len)
191 {
192 	struct hammer_ioc_volume_list ioc;
193 	int i;
194 
195 	if (hammer_fs_to_vol(fs, &ioc) == -1)
196 		return(-1);
197 
198 	for (i = 0; i < ioc.nvols; i++) {
199 		if (ioc.vols[i].vol_no == HAMMER_ROOT_VOLNO) {
200 			strlcpy(buf, ioc.vols[i].device_name, len);
201 			break;
202 		}
203 	}
204 	assert(i != ioc.nvols);  /* root volume must exist */
205 
206 	free(ioc.vols);
207 	return(0);
208 }
209 
210 /*
211  * Functions and data structure for zone statistics
212  */
213 /*
214  * Each layer1 needs ((2^19) / 64) = 8192 uint64_t.
215  */
216 #define HAMMER_LAYER1_UINT64 8192
217 #define HAMMER_LAYER1_BYTES (HAMMER_LAYER1_UINT64 * sizeof(uint64_t))
218 
219 static int *l1_max = NULL;
220 static uint64_t **l1_bits = NULL;
221 
222 static __inline
223 int
224 hammer_set_layer_bits(uint64_t *bits, int i)
225 {
226 	int q, r;
227 
228 	q = i >> 6;
229 	r = i & ((1 << 6) - 1);
230 
231 	bits += q;
232 	if (!((*bits) & ((uint64_t)1 << r))) {
233 		(*bits) |= ((uint64_t)1 << r);
234 		return(1);
235 	}
236 	return(0);  /* already seen this block */
237 }
238 
239 static
240 void
241 hammer_extend_layer1_bits(int vol, int newsiz, int oldsiz)
242 {
243 	uint64_t *p;
244 
245 	assert(newsiz > oldsiz);
246 	assert(newsiz > 0 && oldsiz >= 0);
247 
248 	p = l1_bits[vol];
249 	if (p == NULL)
250 		p = malloc(HAMMER_LAYER1_BYTES * newsiz);
251 	else
252 		p = realloc(p, HAMMER_LAYER1_BYTES * newsiz);
253 	if (p == NULL)
254 		err(1, "alloc");
255 	l1_bits[vol] = p;
256 
257 	p += HAMMER_LAYER1_UINT64 * oldsiz;
258 	bzero(p, HAMMER_LAYER1_BYTES * (newsiz - oldsiz));
259 }
260 
261 struct zone_stat*
262 hammer_init_zone_stat(void)
263 {
264 	return(calloc(HAMMER_MAX_ZONES, sizeof(struct zone_stat)));
265 }
266 
267 struct zone_stat*
268 hammer_init_zone_stat_bits(void)
269 {
270 	int i;
271 
272 	l1_max = calloc(HAMMER_MAX_VOLUMES, sizeof(int));
273 	if (l1_max == NULL)
274 		err(1, "calloc");
275 
276 	l1_bits = calloc(HAMMER_MAX_VOLUMES, sizeof(uint64_t*));
277 	if (l1_bits == NULL)
278 		err(1, "calloc");
279 
280 	for (i = 0; i < HAMMER_MAX_VOLUMES; i++) {
281 		l1_max[i] = -1;  /* +1 needs to be 0 */
282 		l1_bits[i] = NULL;
283 	}
284 	return(hammer_init_zone_stat());
285 }
286 
287 void
288 hammer_cleanup_zone_stat(struct zone_stat *stats)
289 {
290 	int i;
291 
292 	if (l1_bits) {
293 		for (i = 0; i < HAMMER_MAX_VOLUMES; i++) {
294 			free(l1_bits[i]);
295 			l1_bits[i] = NULL;
296 		}
297 	}
298 
299 	free(l1_bits);
300 	l1_bits = NULL;
301 
302 	free(l1_max);
303 	l1_max = NULL;
304 
305 	free(stats);
306 }
307 
308 static
309 void
310 _hammer_add_zone_stat(struct zone_stat *stats, int zone, int bytes,
311 	int new_block, int new_item)
312 {
313 	struct zone_stat *sp = stats + zone;
314 
315 	if (new_block)
316 		sp->blocks++;
317 	if (new_item)
318 		sp->items++;
319 	sp->used += bytes;
320 }
321 
322 void
323 hammer_add_zone_stat(struct zone_stat *stats, hammer_off_t offset, int bytes)
324 {
325 	int zone, vol, i, j, new_block;
326 	uint64_t *p;
327 
328 	offset &= ~HAMMER_BIGBLOCK_MASK64;
329 	zone = HAMMER_ZONE_DECODE(offset);
330 	vol = HAMMER_VOL_DECODE(offset);
331 
332 	offset &= HAMMER_OFF_SHORT_MASK;  /* cut off volume bits from layer1 */
333 	i = HAMMER_BLOCKMAP_LAYER1_INDEX(offset);
334 	j = HAMMER_BLOCKMAP_LAYER2_INDEX(offset);
335 
336 	if (i > l1_max[vol]) {
337 		assert(i < (HAMMER_BLOCKMAP_RADIX1 / HAMMER_MAX_VOLUMES));
338 		hammer_extend_layer1_bits(vol, i + 1, l1_max[vol] + 1);
339 		l1_max[vol] = i;
340 	}
341 
342 	p = l1_bits[vol] + i * HAMMER_LAYER1_UINT64;
343 	new_block = hammer_set_layer_bits(p, j);
344 	_hammer_add_zone_stat(stats, zone, bytes, new_block, 1);
345 }
346 
347 /*
348  * If the same layer2 is used more than once the result will be wrong.
349  */
350 void
351 hammer_add_zone_stat_layer2(struct zone_stat *stats,
352 	hammer_blockmap_layer2_t layer2)
353 {
354 	_hammer_add_zone_stat(stats, layer2->zone,
355 		HAMMER_BIGBLOCK_SIZE - layer2->bytes_free, 1, 0);
356 }
357 
358 static __inline
359 double
360 _calc_used_percentage(int64_t blocks, int64_t used)
361 {
362 	double res;
363 
364 	if (blocks)
365 		res = ((double)(used * 100)) / (blocks << HAMMER_BIGBLOCK_BITS);
366 	else
367 		res = 0;
368 	return(res);
369 }
370 
371 void
372 hammer_print_zone_stat(const struct zone_stat *stats)
373 {
374 	int i;
375 	int64_t total_blocks = 0;
376 	int64_t total_items = 0;
377 	int64_t total_used = 0;
378 	const struct zone_stat *p = stats;
379 #define INDENT ""
380 
381 	printf("HAMMER zone statistics\n");
382 	printf(INDENT"zone #             "
383 		"blocks       items              used[B]             used[%%]\n");
384 	for (i = 0; i < HAMMER_MAX_ZONES; i++) {
385 		printf(INDENT"zone %-2d %-10s %-12ju %-18ju %-19ju %g\n",
386 			i, zone_labels[i], p->blocks, p->items, p->used,
387 			_calc_used_percentage(p->blocks, p->used));
388 		total_blocks += p->blocks;
389 		total_items += p->items;
390 		total_used += p->used;
391 		p++;
392 	}
393 
394 	/*
395 	 * Remember that zone0 is always 0% used and zone15 is
396 	 * always 100% used.
397 	 */
398 	printf(INDENT"--------------------------------------------------------------------------------\n");
399 	printf(INDENT"total              %-12ju %-18ju %-19ju %g\n",
400 		(uintmax_t)total_blocks,
401 		(uintmax_t)total_items,
402 		(uintmax_t)total_used,
403 		_calc_used_percentage(total_blocks, total_used));
404 }
405