MFC adjustments to newfs_hammer for minimum UNDO FIFO space.
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
blobe2662dd1056a8a86a7adb6a0f8381f28677a4679
1 /*
2 * Copyright (c) 2007 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:
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.
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.
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.39.2.3 2008/07/24 05:41:54 dillon Exp $
37 #include "newfs_hammer.h"
39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
40 static const char *sizetostr(off_t size);
41 static void check_volume(struct volume_info *vol);
42 static void format_volume(struct volume_info *vol, int nvols,const char *label,
43 off_t total_size);
44 static hammer_off_t format_root(const char *label);
45 static u_int64_t nowtime(void);
46 static void usage(void);
48 static int ForceOpt = 0;
50 #define GIG (1024LL*1024*1024)
52 int
53 main(int ac, char **av)
55 u_int32_t status;
56 off_t total;
57 int ch;
58 int i;
59 const char *label = NULL;
60 struct volume_info *vol;
61 char *fsidstr;
64 * Sanity check basic filesystem structures. No cookies for us
65 * if it gets broken!
67 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
68 assert(sizeof(struct hammer_blockmap_layer1) == 32);
69 assert(sizeof(struct hammer_blockmap_layer2) == 16);
72 * Generate a filesysem id and lookup the filesystem type
74 uuidgen(&Hammer_FSId, 1);
75 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
76 if (status != uuid_s_ok) {
77 errx(1, "uuids file does not have the DragonFly "
78 "HAMMER filesystem type");
82 * Parse arguments
84 while ((ch = getopt(ac, av, "fL:b:m:u:")) != -1) {
85 switch(ch) {
86 case 'f':
87 ForceOpt = 1;
88 break;
89 case 'L':
90 label = optarg;
91 break;
92 case 'b':
93 BootAreaSize = getsize(optarg,
94 HAMMER_BUFSIZE,
95 HAMMER_BOOT_MAXBYTES, 2);
96 break;
97 case 'm':
98 MemAreaSize = getsize(optarg,
99 HAMMER_BUFSIZE,
100 HAMMER_MEM_MAXBYTES, 2);
101 break;
102 case 'u':
103 UndoBufferSize = getsize(optarg,
104 HAMMER_LARGEBLOCK_SIZE,
105 HAMMER_LARGEBLOCK_SIZE *
106 HAMMER_UNDO_LAYER2, 2);
107 if (UndoBufferSize < 100*1024*1024 && ForceOpt == 0)
108 errx(1, "The minimum UNDO fifo size is 100M\n");
109 if (UndoBufferSize < 100*1024*1024) {
110 fprintf(stderr,
111 "WARNING: you have specified an UNDO "
112 "FIFO size less then 100M, which may\n"
113 "lead to VFS panics.\n");
115 break;
116 default:
117 usage();
118 break;
122 if (label == NULL) {
123 fprintf(stderr,
124 "newfs_hammer: A filesystem label must be specified\n");
125 exit(1);
129 * Collect volume information
131 ac -= optind;
132 av += optind;
133 NumVolumes = ac;
134 RootVolNo = 0;
136 if (NumVolumes == 0) {
137 fprintf(stderr,
138 "newfs_hammer: You must specify at least one special file (volume)\n");
139 exit(1);
142 total = 0;
143 for (i = 0; i < NumVolumes; ++i) {
144 vol = setup_volume(i, av[i], 1, O_RDWR);
147 * Load up information on the volume and initialize
148 * its remaining fields.
150 check_volume(vol);
151 total += vol->size;
155 * Calculate defaults for the boot and memory area sizes.
157 if (BootAreaSize == 0) {
158 BootAreaSize = HAMMER_BOOT_NOMBYTES;
159 while (BootAreaSize > total / NumVolumes / 256)
160 BootAreaSize >>= 1;
161 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
162 BootAreaSize = 0;
163 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
164 BootAreaSize = HAMMER_BOOT_MINBYTES;
166 if (MemAreaSize == 0) {
167 MemAreaSize = HAMMER_MEM_NOMBYTES;
168 while (MemAreaSize > total / NumVolumes / 256)
169 MemAreaSize >>= 1;
170 if (MemAreaSize < HAMMER_MEM_MINBYTES)
171 MemAreaSize = 0;
172 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
173 MemAreaSize = HAMMER_MEM_MINBYTES;
177 * Format the volumes. Format the root volume first so we can
178 * bootstrap the freemap.
180 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
181 for (i = 0; i < NumVolumes; ++i) {
182 if (i != RootVolNo)
183 format_volume(get_volume(i), NumVolumes, label, total);
187 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
188 * limit. If we do this the filesystem does not have to allocate
189 * new layer2 blocks which reduces the chances of the reblocker
190 * having to fallback to an extremely inefficient algorithm.
192 vol = get_volume(RootVolNo);
193 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
194 vol->cache.modified = 1;
195 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
197 printf("---------------------------------------------\n");
198 printf("%d volume%s total size %s\n",
199 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
200 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
201 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
202 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
203 printf("total-pre-allocated: %s\n",
204 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
205 printf("fsid: %s\n", fsidstr);
206 printf("\n");
207 printf("NOTE: Please remember to set up a cron job to prune and\n"
208 "reblock the filesystem regularly, see 'man hammer' for\n"
209 "more information.\n");
210 if (total < 50*GIG) {
211 printf("\nWARNING: HAMMER filesystems less then 50G are "
212 "not recommended!\n"
213 "You may have to run 'hammer prune-everything' and "
214 "'hammer reblock'\n"
215 "quite often, even if using a nohistory mount.\n");
217 flush_all_volumes();
218 return(0);
221 static
222 void
223 usage(void)
225 fprintf(stderr,
226 "newfs_hammer -L label [-b bootsize] [-m savesize] [ -u undosize] "
227 "special ...\n"
229 exit(1);
233 * Convert the size in bytes to a human readable string.
235 static
236 const char *
237 sizetostr(off_t size)
239 static char buf[32];
241 if (size < 1024 / 2) {
242 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
243 } else if (size < 1024 * 1024 / 2) {
244 snprintf(buf, sizeof(buf), "%6.2fKB",
245 (double)size / 1024);
246 } else if (size < 1024 * 1024 * 1024LL / 2) {
247 snprintf(buf, sizeof(buf), "%6.2fMB",
248 (double)size / (1024 * 1024));
249 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
250 snprintf(buf, sizeof(buf), "%6.2fGB",
251 (double)size / (1024 * 1024 * 1024LL));
252 } else {
253 snprintf(buf, sizeof(buf), "%6.2fTB",
254 (double)size / (1024 * 1024 * 1024LL * 1024LL));
256 return(buf);
260 * Convert a string to a 64 bit signed integer with various requirements.
262 static int64_t
263 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
265 int64_t val;
266 char *ptr;
268 val = strtoll(str, &ptr, 0);
269 switch(*ptr) {
270 case 't':
271 case 'T':
272 val *= 1024;
273 /* fall through */
274 case 'g':
275 case 'G':
276 val *= 1024;
277 /* fall through */
278 case 'm':
279 case 'M':
280 val *= 1024;
281 /* fall through */
282 case 'k':
283 case 'K':
284 val *= 1024;
285 break;
286 default:
287 errx(1, "Unknown suffix in number '%s'\n", str);
288 /* not reached */
290 if (ptr[1]) {
291 errx(1, "Unknown suffix in number '%s'\n", str);
292 /* not reached */
294 if (val < minval) {
295 errx(1, "Value too small: %s, min is %s\n",
296 str, sizetostr(minval));
297 /* not reached */
299 if (val > maxval) {
300 errx(1, "Value too large: %s, max is %s\n",
301 str, sizetostr(maxval));
302 /* not reached */
304 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
305 errx(1, "Value not power of 2: %s\n", str);
306 /* not reached */
308 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
309 errx(1, "Value not an integral multiple of %dK: %s",
310 HAMMER_BUFSIZE / 1024, str);
311 /* not reached */
313 return(val);
317 * Generate a transaction id. Transaction ids are no longer time-based.
318 * Put the nail in the coffin by not making the first one time-based.
320 * We could start at 1 here but start at 2^32 to reserve a small domain for
321 * possible future use.
323 static hammer_tid_t
324 createtid(void)
326 static hammer_tid_t lasttid;
328 if (lasttid == 0)
329 lasttid = 0x0000000100000000ULL;
330 return(lasttid++);
333 static u_int64_t
334 nowtime(void)
336 struct timeval tv;
337 u_int64_t xtime;
339 gettimeofday(&tv, NULL);
340 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
341 return(xtime);
345 * Check basic volume characteristics. HAMMER filesystems use a minimum
346 * of a 16KB filesystem buffer size.
348 static
349 void
350 check_volume(struct volume_info *vol)
352 struct partinfo pinfo;
353 struct stat st;
356 * Get basic information about the volume
358 vol->fd = open(vol->name, O_RDWR);
359 if (vol->fd < 0)
360 err(1, "Unable to open %s R+W", vol->name);
361 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
363 * Allow the formatting of regular files as HAMMER volumes
365 if (fstat(vol->fd, &st) < 0)
366 err(1, "Unable to stat %s", vol->name);
367 vol->size = st.st_size;
368 vol->type = "REGFILE";
369 } else {
371 * When formatting a block device as a HAMMER volume the
372 * sector size must be compatible. HAMMER uses 16384 byte
373 * filesystem buffers.
375 if (pinfo.reserved_blocks) {
376 errx(1, "HAMMER cannot be placed in a partition "
377 "which overlaps the disklabel or MBR");
379 if (pinfo.media_blksize > 16384 ||
380 16384 % pinfo.media_blksize) {
381 errx(1, "A media sector size of %d is not supported",
382 pinfo.media_blksize);
385 vol->size = pinfo.media_size;
386 vol->type = "DEVICE";
388 printf("Volume %d %s %-15s size %s\n",
389 vol->vol_no, vol->type, vol->name,
390 sizetostr(vol->size));
393 * Reserve space for (future) header junk, setup our poor-man's
394 * bigblock allocator.
396 vol->vol_alloc = HAMMER_BUFSIZE * 16;
400 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
402 static
403 void
404 format_volume(struct volume_info *vol, int nvols, const char *label,
405 off_t total_size __unused)
407 struct volume_info *root_vol;
408 struct hammer_volume_ondisk *ondisk;
409 int64_t freeblks;
410 int64_t freebytes;
411 int i;
414 * Initialize basic information in the on-disk volume structure.
416 ondisk = vol->ondisk;
418 ondisk->vol_fsid = Hammer_FSId;
419 ondisk->vol_fstype = Hammer_FSType;
420 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
421 ondisk->vol_no = vol->vol_no;
422 ondisk->vol_count = nvols;
423 ondisk->vol_version = 1;
425 ondisk->vol_bot_beg = vol->vol_alloc;
426 vol->vol_alloc += BootAreaSize;
427 ondisk->vol_mem_beg = vol->vol_alloc;
428 vol->vol_alloc += MemAreaSize;
431 * The remaining area is the zone 2 buffer allocation area. These
432 * buffers
434 ondisk->vol_buf_beg = vol->vol_alloc;
435 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
437 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
438 errx(1, "volume %d %s is too small to hold the volume header",
439 vol->vol_no, vol->name);
442 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
443 HAMMER_BUFSIZE;
444 ondisk->vol_blocksize = HAMMER_BUFSIZE;
446 ondisk->vol_rootvol = RootVolNo;
447 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
449 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
450 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
453 * Format the root volume.
455 if (vol->vol_no == RootVolNo) {
457 * Starting TID
459 ondisk->vol0_next_tid = createtid();
461 format_freemap(vol,
462 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
464 freeblks = initialize_freemap(vol);
465 ondisk->vol0_stat_freebigblocks = freeblks;
467 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
468 if (freebytes < 1*GIG && ForceOpt == 0) {
469 errx(1, "Cannot create a HAMMER filesystem less then "
470 "1GB unless you use -f. HAMMER filesystems\n"
471 "less then 50G are not recommended\n");
474 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
475 format_blockmap(&ondisk->vol0_blockmap[i],
476 HAMMER_ZONE_ENCODE(i, 0));
478 format_undomap(ondisk);
480 ondisk->vol0_btree_root = format_root(label);
481 ++ondisk->vol0_stat_inodes; /* root inode */
482 } else {
483 freeblks = initialize_freemap(vol);
484 root_vol = get_volume(RootVolNo);
485 root_vol->cache.modified = 1;
486 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
487 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
488 rel_volume(root_vol);
493 * Format the root directory.
495 static
496 hammer_off_t
497 format_root(const char *label)
499 hammer_off_t btree_off;
500 hammer_off_t pfsd_off;
501 hammer_off_t data_off;
502 hammer_tid_t create_tid;
503 hammer_node_ondisk_t bnode;
504 struct hammer_inode_data *idata;
505 hammer_pseudofs_data_t pfsd;
506 struct buffer_info *data_buffer1 = NULL;
507 struct buffer_info *data_buffer2 = NULL;
508 hammer_btree_elm_t elm;
509 u_int64_t xtime;
511 bnode = alloc_btree_element(&btree_off);
512 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
513 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
514 create_tid = createtid();
515 xtime = nowtime();
518 * Populate the inode data and inode record for the root directory.
520 idata->version = HAMMER_INODE_DATA_VERSION;
521 idata->mode = 0755;
522 idata->ctime = xtime;
523 idata->mtime = xtime;
524 idata->atime = xtime;
525 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
526 idata->size = 0;
527 idata->nlinks = 1;
529 pfsd->sync_low_tid = 1;
530 pfsd->sync_beg_tid = 0;
531 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
532 pfsd->shared_uuid = Hammer_FSId;
533 pfsd->unique_uuid = Hammer_FSId;
534 pfsd->reserved01 = 0;
535 pfsd->mirror_flags = 0;
536 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
539 * Create the root of the B-Tree. The root is a leaf node so we
540 * do not have to worry about boundary elements.
542 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
543 bnode->count = 2;
544 bnode->type = HAMMER_BTREE_TYPE_LEAF;
546 elm = &bnode->elms[0];
547 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
548 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
549 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
550 elm->leaf.base.key = 0;
551 elm->leaf.base.create_tid = create_tid;
552 elm->leaf.base.delete_tid = 0;
553 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
554 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
555 elm->leaf.create_ts = (u_int32_t)time(NULL);
557 elm->leaf.data_offset = data_off;
558 elm->leaf.data_len = sizeof(*idata);
559 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
561 elm = &bnode->elms[1];
562 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
563 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
564 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
565 elm->leaf.base.key = 0;
566 elm->leaf.base.create_tid = create_tid;
567 elm->leaf.base.delete_tid = 0;
568 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
569 elm->leaf.base.obj_type = 0;
570 elm->leaf.create_ts = (u_int32_t)time(NULL);
572 elm->leaf.data_offset = pfsd_off;
573 elm->leaf.data_len = sizeof(*pfsd);
574 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
576 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
578 return(btree_off);