47980c98fda70e1d3cf58092f40e5e39cb464904
[dragonfly.git] / sbin / hammer / misc.c
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 void
40 hammer_key_beg_init(hammer_base_elm_t base)
41 {
42         bzero(base, sizeof(*base));
43
44         base->localization = HAMMER_MIN_LOCALIZATION;
45         base->obj_id = HAMMER_MIN_OBJID;
46         base->key = HAMMER_MIN_KEY;
47         base->create_tid = 1;
48         base->rec_type = HAMMER_MIN_RECTYPE;
49 }
50
51 void
52 hammer_key_end_init(hammer_base_elm_t base)
53 {
54         bzero(base, sizeof(*base));
55
56         base->localization = HAMMER_MAX_LOCALIZATION;
57         base->obj_id = HAMMER_MAX_OBJID;
58         base->key = HAMMER_MAX_KEY;
59         base->create_tid = HAMMER_MAX_TID;
60         base->rec_type = HAMMER_MAX_RECTYPE;
61 }
62
63 int
64 getyn(void)
65 {
66         char buf[256];
67         int len;
68
69         if (fgets(buf, sizeof(buf), stdin) == NULL)
70                 return(0);
71         len = strlen(buf);
72         while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
73                 --len;
74         buf[len] = 0;
75         if (strcmp(buf, "y") == 0 ||
76             strcmp(buf, "yes") == 0 ||
77             strcmp(buf, "Y") == 0 ||
78             strcmp(buf, "YES") == 0) {
79                 return(1);
80         }
81         return(0);
82 }
83
84 const char *
85 sizetostr(off_t size)
86 {
87         static char buf[32];
88
89         if (size < 1024 / 2) {
90                 snprintf(buf, sizeof(buf), "%6.2fB", (double)size);
91         } else if (size < 1024 * 1024 / 2) {
92                 snprintf(buf, sizeof(buf), "%6.2fKB",
93                         (double)size / 1024);
94         } else if (size < 1024 * 1024 * 1024LL / 2) {
95                 snprintf(buf, sizeof(buf), "%6.2fMB",
96                         (double)size / (1024 * 1024));
97         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
98                 snprintf(buf, sizeof(buf), "%6.2fGB",
99                         (double)size / (1024 * 1024 * 1024LL));
100         } else {
101                 snprintf(buf, sizeof(buf), "%6.2fTB",
102                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
103         }
104         return(buf);
105 }
106
107 int
108 hammer_fs_to_vol(const char *fs, struct hammer_ioc_volume_list *p)
109 {
110         struct hammer_ioc_volume_list ioc;
111         int fd;
112
113         fd = open(fs, O_RDONLY);
114         if (fd < 0) {
115                 perror("open");
116                 return(-1);
117         }
118
119         bzero(&ioc, sizeof(ioc));
120         ioc.nvols = HAMMER_MAX_VOLUMES;
121         ioc.vols = malloc(ioc.nvols * sizeof(*ioc.vols));
122         if (ioc.vols == NULL) {
123                 perror("malloc");
124                 close(fd);
125                 return(-1);
126         }
127
128         if (ioctl(fd, HAMMERIOC_LIST_VOLUMES, &ioc) < 0) {
129                 perror("ioctl");
130                 close(fd);
131                 free(ioc.vols);
132                 return(-1);
133         }
134
135         bcopy(&ioc, p, sizeof(ioc));
136         close(fd);
137
138         return(0);
139 }
140
141 int
142 hammer_fs_to_rootvol(const char *fs, char *buf, int len)
143 {
144         struct hammer_ioc_volume_list ioc;
145         int i;
146
147         if (hammer_fs_to_vol(fs, &ioc) == -1)
148                 return(-1);
149
150         for (i = 0; i < ioc.nvols; i++) {
151                 if (ioc.vols[i].vol_no == HAMMER_ROOT_VOLNO) {
152                         strlcpy(buf, ioc.vols[i].device_name, len);
153                         break;
154                 }
155         }
156         assert(i != ioc.nvols);  /* root volume must exist */
157
158         free(ioc.vols);
159         return(0);
160 }
161
162 /*
163  * Functions and data structure for zone statistics
164  */
165 /*
166  * Each layer1 needs ((2^19) / 64) = 8192 uint64_t.
167  */
168 #define HAMMER_LAYER1_UINT64 8192
169 #define HAMMER_LAYER1_BYTES (HAMMER_LAYER1_UINT64 * sizeof(uint64_t))
170
171 static int *l1_max = NULL;
172 static uint64_t **l1_bits = NULL;
173
174 static __inline
175 int
176 hammer_set_layer_bits(uint64_t *bits, int i)
177 {
178         int q, r;
179
180         q = i >> 6;
181         r = i & ((1 << 6) - 1);
182
183         bits += q;
184         if (!((*bits) & ((uint64_t)1 << r))) {
185                 (*bits) |= ((uint64_t)1 << r);
186                 return(1);
187         }
188         return(0);  /* already seen this block */
189 }
190
191 static
192 void
193 hammer_extend_layer1_bits(int vol, int newsiz, int oldsiz)
194 {
195         uint64_t *p;
196
197         assert(newsiz > oldsiz);
198         assert(newsiz > 0 && oldsiz >= 0);
199
200         p = l1_bits[vol];
201         if (p == NULL)
202                 p = malloc(HAMMER_LAYER1_BYTES * newsiz);
203         else
204                 p = realloc(p, HAMMER_LAYER1_BYTES * newsiz);
205         if (p == NULL)
206                 err(1, "alloc");
207         l1_bits[vol] = p;
208
209         p += HAMMER_LAYER1_UINT64 * oldsiz;
210         bzero(p, HAMMER_LAYER1_BYTES * (newsiz - oldsiz));
211 }
212
213 struct zone_stat*
214 hammer_init_zone_stat(void)
215 {
216         return(calloc(HAMMER_MAX_ZONES, sizeof(struct zone_stat)));
217 }
218
219 struct zone_stat*
220 hammer_init_zone_stat_bits(void)
221 {
222         int i;
223
224         l1_max = calloc(HAMMER_MAX_VOLUMES, sizeof(int));
225         if (l1_max == NULL)
226                 err(1, "calloc");
227
228         l1_bits = calloc(HAMMER_MAX_VOLUMES, sizeof(uint64_t*));
229         if (l1_bits == NULL)
230                 err(1, "calloc");
231
232         for (i = 0; i < HAMMER_MAX_VOLUMES; i++) {
233                 l1_max[i] = -1;  /* +1 needs to be 0 */
234                 l1_bits[i] = NULL;
235         }
236         return(hammer_init_zone_stat());
237 }
238
239 void
240 hammer_cleanup_zone_stat(struct zone_stat *stats)
241 {
242         int i;
243
244         if (l1_bits) {
245                 for (i = 0; i < HAMMER_MAX_VOLUMES; i++) {
246                         free(l1_bits[i]);
247                         l1_bits[i] = NULL;
248                 }
249         }
250
251         free(l1_bits);
252         l1_bits = NULL;
253
254         free(l1_max);
255         l1_max = NULL;
256
257         free(stats);
258 }
259
260 static
261 void
262 _hammer_add_zone_stat(struct zone_stat *stats, int zone, int bytes,
263         int new_block, int new_item)
264 {
265         struct zone_stat *sp = stats + zone;
266
267         if (new_block)
268                 sp->blocks++;
269         if (new_item)
270                 sp->items++;
271         sp->used += bytes;
272 }
273
274 void
275 hammer_add_zone_stat(struct zone_stat *stats, hammer_off_t offset, int bytes)
276 {
277         int zone, vol, i, j, new_block;
278         uint64_t *p;
279
280         offset &= ~HAMMER_BIGBLOCK_MASK64;
281         zone = HAMMER_ZONE_DECODE(offset);
282         vol = HAMMER_VOL_DECODE(offset);
283
284         offset &= HAMMER_OFF_SHORT_MASK;  /* cut off volume bits from layer1 */
285         i = HAMMER_BLOCKMAP_LAYER1_INDEX(offset);
286         j = HAMMER_BLOCKMAP_LAYER2_INDEX(offset);
287
288         if (i > l1_max[vol]) {
289                 assert(i < (HAMMER_BLOCKMAP_RADIX1 / HAMMER_MAX_VOLUMES));
290                 hammer_extend_layer1_bits(vol, i + 1, l1_max[vol] + 1);
291                 l1_max[vol] = i;
292         }
293
294         p = l1_bits[vol] + i * HAMMER_LAYER1_UINT64;
295         new_block = hammer_set_layer_bits(p, j);
296         _hammer_add_zone_stat(stats, zone, bytes, new_block, 1);
297 }
298
299 /*
300  * If the same layer2 is used more than once the result will be wrong.
301  */
302 void
303 hammer_add_zone_stat_layer2(struct zone_stat *stats,
304         hammer_blockmap_layer2_t layer2)
305 {
306         _hammer_add_zone_stat(stats, layer2->zone,
307                 HAMMER_BIGBLOCK_SIZE - layer2->bytes_free, 1, 0);
308 }
309
310 static __inline
311 double
312 _calc_used_percentage(int64_t blocks, int64_t used)
313 {
314         double res;
315
316         if (blocks)
317                 res = ((double)(used * 100)) / (blocks << HAMMER_BIGBLOCK_BITS);
318         else
319                 res = 0;
320         return(res);
321 }
322
323 void
324 hammer_print_zone_stat(const struct zone_stat *stats)
325 {
326         int i;
327         int64_t total_blocks = 0;
328         int64_t total_items = 0;
329         int64_t total_used = 0;
330         const struct zone_stat *p = stats;
331 #define INDENT ""
332
333         printf("HAMMER zone statistics\n");
334         printf(INDENT"zone #             "
335                 "blocks       items              used[B]             used[%%]\n");
336         for (i = 0; i < HAMMER_MAX_ZONES; i++) {
337                 printf(INDENT"zone %-2d %-10s %-12ju %-18ju %-19ju %g\n",
338                         i, zone_labels[i], p->blocks, p->items, p->used,
339                         _calc_used_percentage(p->blocks, p->used));
340                 total_blocks += p->blocks;
341                 total_items += p->items;
342                 total_used += p->used;
343                 p++;
344         }
345
346         /*
347          * Remember that zone0 is always 0% used and zone15 is
348          * always 100% used.
349          */
350         printf(INDENT"--------------------------------------------------------------------------------\n");
351         printf(INDENT"total              %-12ju %-18ju %-19ju %g\n",
352                 (uintmax_t)total_blocks,
353                 (uintmax_t)total_items,
354                 (uintmax_t)total_used,
355                 _calc_used_percentage(total_blocks, total_used));
356 }