2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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
35 #include <sys/sysctl.h>
36 #include <sys/ioctl_compat.h>
38 #include "hammer_util.h"
40 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
41 static const char *sizetostr(off_t size);
42 static int trim_volume(struct volume_info *vol);
43 static void format_volume(struct volume_info *vol, int nvols,const char *label);
44 static hammer_off_t format_root_directory(const char *label);
45 static uint64_t nowtime(void);
46 static void usage(void);
49 static int64_t BootAreaSize;
50 static int64_t MemAreaSize;
51 static int64_t UndoBufferSize;
52 static int HammerVersion = -1;
54 #define GIG (1024LL*1024*1024)
57 main(int ac, char **av)
66 const char *label = NULL;
67 struct volume_info *vol;
71 * Sanity check basic filesystem structures. No cookies for us
74 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
75 assert(sizeof(struct hammer_blockmap_layer1) == 32);
76 assert(sizeof(struct hammer_blockmap_layer2) == 16);
79 * Generate a filesystem id and lookup the filesystem type
81 uuidgen(&Hammer_FSId, 1);
82 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
83 if (status != uuid_s_ok) {
84 errx(1, "uuids file does not have the DragonFly "
85 "HAMMER filesystem type");
91 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
103 BootAreaSize = getsize(optarg,
105 HAMMER_BOOT_MAXBYTES, 2);
108 MemAreaSize = getsize(optarg,
110 HAMMER_MEM_MAXBYTES, 2);
113 UndoBufferSize = getsize(optarg,
114 HAMMER_BIGBLOCK_SIZE,
115 HAMMER_BIGBLOCK_SIZE *
116 HAMMER_UNDO_LAYER2, 2);
117 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
118 errx(1, "The minimum UNDO/REDO FIFO size is "
120 if (UndoBufferSize < 500*1024*1024) {
122 "WARNING: you have specified an "
123 "UNDO/REDO FIFO size less than 500MB,\n"
124 "which may lead to VFS panics.\n");
128 HammerVersion = strtol(optarg, NULL, 0);
129 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
130 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
132 "I don't understand how to format "
133 "HAMMER version %d\n",
145 "newfs_hammer: A filesystem label must be specified\n");
149 if (HammerVersion < 0) {
150 size_t olen = sizeof(HammerVersion);
151 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
152 if (sysctlbyname("vfs.hammer.supported_version",
153 &HammerVersion, &olen, NULL, 0) == 0) {
154 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
155 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
157 "newfs_hammer: WARNING: HAMMER VFS "
158 "supports higher version than I "
160 "using version %d\n",
165 "newfs_hammer: WARNING: HAMMER VFS not "
166 "loaded, cannot get version info.\n"
167 "Using version %d\n",
168 HAMMER_VOL_VERSION_DEFAULT);
173 * Collect volume information
181 "newfs_hammer: You must specify at least one "
182 "special file (volume)\n");
186 if (nvols > HAMMER_MAX_VOLUMES) {
188 "newfs_hammer: The maximum number of volumes is %d\n",
194 for (i = 0; i < nvols; ++i) {
195 vol = init_volume(i, av[i], O_RDWR);
198 * Load up information on the volume and initialize
199 * its remaining fields.
202 printf("Volume %d %s %-15s size %s\n",
203 vol->vol_no, vol->type, vol->name,
204 sizetostr(vol->size));
207 if (trim_volume(vol) == -1)
214 * Calculate defaults for the boot and memory area sizes.
216 avg_vol_size = total / nvols;
217 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
218 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
221 * Format the volumes. Format the root volume first so we can
222 * bootstrap the freemap.
224 format_volume(get_root_volume(), nvols, label);
225 for (i = 0; i < nvols; ++i) {
226 if (i != HAMMER_ROOT_VOLNO)
227 format_volume(get_volume(i), nvols, label);
231 * Print information stored in the root volume header.
233 vol = get_root_volume();
234 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
236 printf("---------------------------------------------\n");
237 printf("%d volume%s total size %s version %d\n",
238 nvols, (nvols == 1 ? "" : "s"),
239 sizetostr(total), HammerVersion);
240 printf("root-volume: %s\n", vol->name);
241 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
242 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
243 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
244 printf("total-pre-allocated: %s\n",
245 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
246 printf("fsid: %s\n", fsidstr);
248 printf("NOTE: Please remember that you may have to manually set up a\n"
249 "cron(8) job to prune and reblock the filesystem regularly.\n"
250 "By default, the system automatically runs 'hammer cleanup'\n"
251 "on a nightly basis. The periodic.conf(5) variable\n"
252 "'daily_clean_hammer_enable' can be unset to disable this.\n"
253 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
254 if (total < 10*GIG) {
255 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
256 "really should not\n"
257 "try to format a HAMMER filesystem this small.\n");
259 if (total < 50*GIG) {
260 printf("\nWARNING: HAMMER filesystems less than 50GB are "
262 "You may have to run 'hammer prune-everything' and "
264 "quite often, even if using a nohistory mount.\n");
275 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
276 " [-V version] special ...\n"
282 * Convert the size in bytes to a human readable string.
286 sizetostr(off_t size)
290 if (size < 1024 / 2) {
291 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
292 } else if (size < 1024 * 1024 / 2) {
293 snprintf(buf, sizeof(buf), "%6.2fKB",
294 (double)size / 1024);
295 } else if (size < 1024 * 1024 * 1024LL / 2) {
296 snprintf(buf, sizeof(buf), "%6.2fMB",
297 (double)size / (1024 * 1024));
298 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
299 snprintf(buf, sizeof(buf), "%6.2fGB",
300 (double)size / (1024 * 1024 * 1024LL));
302 snprintf(buf, sizeof(buf), "%6.2fTB",
303 (double)size / (1024 * 1024 * 1024LL * 1024LL));
309 * Convert a string to a 64 bit signed integer with various requirements.
312 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
317 val = strtoll(str, &ptr, 0);
336 errx(1, "Unknown suffix in number '%s'\n", str);
340 errx(1, "Unknown suffix in number '%s'\n", str);
344 errx(1, "Value too small: %s, min is %s\n",
345 str, sizetostr(minval));
349 errx(1, "Value too large: %s, max is %s\n",
350 str, sizetostr(maxval));
353 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
354 errx(1, "Value not power of 2: %s\n", str);
357 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
358 errx(1, "Value not an integral multiple of %dK: %s",
359 HAMMER_BUFSIZE / 1024, str);
366 * Generate a transaction id. Transaction ids are no longer time-based.
367 * Put the nail in the coffin by not making the first one time-based.
369 * We could start at 1 here but start at 2^32 to reserve a small domain for
370 * possible future use.
375 static hammer_tid_t lasttid;
378 lasttid = 0x0000000100000000ULL;
388 gettimeofday(&tv, NULL);
389 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
394 * TRIM the volume, but only if the backing store is a DEVICE
398 trim_volume(struct volume_info *vol)
402 char sysctl_name[64];
406 if (strcmp(vol->type, "DEVICE")) {
407 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name);
410 if (strncmp(vol->name, "/dev/da", 7)) {
411 fprintf(stderr, "%s does not support the TRIM command\n",
416 /* Extract a number from /dev/da?s? */
417 dev_name = strdup(vol->name);
418 p = strtok(dev_name + strlen("/dev/da"), "s");
419 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
423 olen = sizeof(trim_enabled);
425 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
426 fprintf(stderr, "%s (%s) does not support the TRIM command\n",
427 vol->name, sysctl_name);
431 fprintf(stderr, "Erase device option selected, but sysctl (%s) "
432 "is not enabled\n", sysctl_name);
436 ioarg[0] = vol->device_offset;
437 ioarg[1] = vol->size;
439 printf("Trimming %s %s, sectors %llu-%llu\n",
440 vol->type, vol->name,
441 (unsigned long long)ioarg[0] / 512,
442 (unsigned long long)ioarg[1] / 512);
444 if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) {
445 fprintf(stderr, "Device trim failed\n");
453 * Format a HAMMER volume.
457 format_volume(struct volume_info *vol, int nvols, const char *label)
459 struct volume_info *root_vol;
460 struct hammer_volume_ondisk *ondisk;
463 int64_t vol_buf_size;
464 hammer_off_t vol_alloc;
468 * Initialize basic information in the on-disk volume structure.
470 ondisk = vol->ondisk;
472 ondisk->vol_fsid = Hammer_FSId;
473 ondisk->vol_fstype = Hammer_FSType;
474 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
475 ondisk->vol_no = vol->vol_no;
476 ondisk->vol_count = nvols;
477 ondisk->vol_version = HammerVersion;
480 * Reserve space for (future) header junk, setup our poor-man's
481 * big-block allocator.
483 vol_alloc = HAMMER_VOL_ALLOC;
485 ondisk->vol_bot_beg = vol_alloc;
486 vol_alloc += BootAreaSize;
487 ondisk->vol_mem_beg = vol_alloc;
488 vol_alloc += MemAreaSize;
491 * The remaining area is the zone 2 buffer allocation area.
493 ondisk->vol_buf_beg = vol_alloc;
494 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
495 vol_buf_size = ondisk->vol_buf_end - ondisk->vol_buf_beg;
497 if (vol_buf_size < 0) {
498 errx(1, "volume %d %s is too small to hold the volume header",
499 vol->vol_no, vol->name);
502 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
503 errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
506 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
507 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
509 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
510 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
511 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
514 * Format the root volume.
516 if (vol->vol_no == HAMMER_ROOT_VOLNO) {
518 * Check freemap counts before formatting
520 freeblks = count_freemap(vol);
521 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
522 if (freebytes < 10*GIG && ForceOpt == 0) {
523 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
524 "unless you use -f\n(for the size of Volume %d). "
525 "HAMMER filesystems less than 50GB are not "
526 "recommended.\n", HAMMER_ROOT_VOLNO);
532 ondisk->vol0_next_tid = createtid();
535 * Format freemap. vol0_stat_freebigblocks is
536 * the number of big-blocks available for anything
537 * other than freemap zone at this point.
540 assert(ondisk->vol0_stat_freebigblocks == 0);
541 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
544 * Format zones that are mapped to zone-2.
546 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
547 if (hammer_is_zone2_mapped_index(i))
548 format_blockmap(vol, i, 0);
552 * Format undo zone. Formatting decrements
553 * vol0_stat_freebigblocks whenever a new big-block
554 * is allocated for undo zone.
556 format_undomap(vol, &UndoBufferSize);
557 assert(ondisk->vol0_stat_bigblocks == 0);
558 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
561 * Format the root directory. Formatting decrements
562 * vol0_stat_freebigblocks whenever a new big-block
563 * is allocated for required zones.
565 ondisk->vol0_btree_root = format_root_directory(label);
566 ++ondisk->vol0_stat_inodes; /* root inode */
570 freeblks = initialize_freemap(vol);
571 root_vol = get_root_volume();
572 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
573 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
574 rel_volume(root_vol);
579 * Format the root directory.
583 format_root_directory(const char *label)
585 hammer_off_t btree_off;
586 hammer_off_t pfsd_off;
587 hammer_off_t data_off;
588 hammer_tid_t create_tid;
589 hammer_node_ondisk_t bnode;
590 struct hammer_inode_data *idata;
591 hammer_pseudofs_data_t pfsd;
592 struct buffer_info *data_buffer0 = NULL;
593 struct buffer_info *data_buffer1 = NULL;
594 struct buffer_info *data_buffer2 = NULL;
595 hammer_btree_elm_t elm;
599 * Allocate zero-filled root btree node, inode and pfs
601 bnode = alloc_btree_element(&btree_off, &data_buffer0);
602 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
603 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
604 create_tid = createtid();
608 * Populate the inode data and inode record for the root directory.
610 idata->version = HAMMER_INODE_DATA_VERSION;
612 idata->ctime = xtime;
613 idata->mtime = xtime;
614 idata->atime = xtime;
615 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
618 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
619 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
620 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
621 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
624 * Populate the PFS data for the root PFS.
626 pfsd->sync_low_tid = 1;
627 pfsd->sync_beg_tid = 0;
628 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
629 pfsd->shared_uuid = Hammer_FSId;
630 pfsd->unique_uuid = Hammer_FSId;
631 pfsd->reserved01 = 0;
632 pfsd->mirror_flags = 0;
633 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
636 * Create the root of the B-Tree. The root is a leaf node so we
637 * do not have to worry about boundary elements.
640 bnode->type = HAMMER_BTREE_TYPE_LEAF;
643 * Create the first node element for the inode.
645 elm = &bnode->elms[0];
646 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
647 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
648 HAMMER_LOCALIZE_INODE;
649 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
650 elm->leaf.base.key = 0;
651 elm->leaf.base.create_tid = create_tid;
652 elm->leaf.base.delete_tid = 0;
653 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
654 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
655 elm->leaf.create_ts = (uint32_t)time(NULL);
657 elm->leaf.data_offset = data_off;
658 elm->leaf.data_len = sizeof(*idata);
659 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
662 * Create the second node element for the PFS data.
664 elm = &bnode->elms[1];
665 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
666 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
667 HAMMER_LOCALIZE_MISC;
668 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
669 elm->leaf.base.key = 0;
670 elm->leaf.base.create_tid = create_tid;
671 elm->leaf.base.delete_tid = 0;
672 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
673 elm->leaf.base.obj_type = 0;
674 elm->leaf.create_ts = (uint32_t)time(NULL);
676 elm->leaf.data_offset = pfsd_off;
677 elm->leaf.data_len = sizeof(*pfsd);
678 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
680 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
682 rel_buffer(data_buffer0);
683 rel_buffer(data_buffer1);
684 rel_buffer(data_buffer2);