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, int pw);
41 static int trim_volume(struct volume_info *vol);
42 static void format_volume(struct volume_info *vol, int nvols,const char *label);
43 static hammer_off_t format_root_directory(const char *label);
44 static uint64_t nowtime(void);
45 static void print_volume(const struct volume_info *vol);
46 static void usage(int exit_code);
47 static void test_header_junk_size(int64_t size);
48 static void test_boot_area_size(int64_t size);
49 static void test_memory_log_size(int64_t size);
50 static void test_undo_buffer_size(int64_t size);
53 static int64_t HeaderJunkSize = -1;
54 static int64_t BootAreaSize = -1;
55 static int64_t MemoryLogSize = -1;
56 static int64_t UndoBufferSize;
57 static int HammerVersion = -1;
59 #define GIG (1024LL*1024*1024)
62 main(int ac, char **av)
71 const char *label = NULL;
72 struct volume_info *vol;
75 * Sanity check basic filesystem structures. No cookies for us
78 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
79 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_MIN_VOL_JUNK);
80 assert(sizeof(struct hammer_blockmap_layer1) == 32);
81 assert(sizeof(struct hammer_blockmap_layer2) == 16);
86 while ((ch = getopt(ac, av, "dfEL:j:b:m:u:hC:V:")) != -1) {
100 case 'j': /* Not mentioned in newfs_hammer(8) */
101 HeaderJunkSize = getsize(optarg, 2);
102 test_header_junk_size(HeaderJunkSize);
105 BootAreaSize = getsize(optarg, 2);
106 test_boot_area_size(BootAreaSize);
109 MemoryLogSize = getsize(optarg, 2);
110 test_memory_log_size(MemoryLogSize);
113 UndoBufferSize = getsize(optarg, 2);
114 test_undo_buffer_size(UndoBufferSize);
120 if (hammer_parse_cache_size(optarg) == -1)
124 HammerVersion = strtol(optarg, NULL, 0);
125 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
126 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
128 "I don't understand how to format "
144 "newfs_hammer: A filesystem label must be specified\n");
148 if (HammerVersion < 0) {
149 size_t olen = sizeof(HammerVersion);
150 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
151 if (sysctlbyname("vfs.hammer.supported_version",
152 &HammerVersion, &olen, NULL, 0) == 0) {
153 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
154 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
156 "newfs_hammer: WARNING: HAMMER VFS "
157 "supports higher version than I "
159 "using version %d\n",
164 "newfs_hammer: WARNING: HAMMER VFS not "
165 "loaded, cannot get version info.\n"
166 "Using version %d\n",
167 HAMMER_VOL_VERSION_DEFAULT);
173 "newfs_hammer: You must specify at least one "
174 "special file (volume)\n");
178 if (nvols > HAMMER_MAX_VOLUMES) {
180 "newfs_hammer: The maximum number of volumes is %d\n",
186 * Generate a filesystem id and lookup the filesystem type
188 uuidgen(&Hammer_FSId, 1);
189 uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING, &status);
190 if (status != uuid_s_ok) {
191 errx(1, "uuids file does not have the DragonFly "
192 "HAMMER filesystem type");
196 for (i = 0; i < nvols; ++i) {
197 vol = init_volume(av[i], O_RDWR, i);
198 printf("Volume %d %s %-15s size %s\n",
199 vol->vol_no, vol->type, vol->name,
200 sizetostr(vol->size));
203 int res = trim_volume(vol);
204 if (res == -1 || (res == 1 && ForceOpt == 0))
211 * Reserve space for (future) header junk, setup our poor-man's
212 * big-block allocator. Note that the header junk space includes
213 * volume header which is 1928 bytes.
215 if (HeaderJunkSize == -1)
216 HeaderJunkSize = HAMMER_VOL_JUNK_SIZE;
217 else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk))
218 HeaderJunkSize = sizeof(struct hammer_volume_ondisk);
219 HeaderJunkSize = (HeaderJunkSize + HAMMER_BUFMASK) & ~HAMMER_BUFMASK;
222 * Calculate defaults for the boot area and memory log sizes,
223 * only if not specified by -b or -m option.
225 avg_vol_size = total / nvols;
226 if (BootAreaSize == -1)
227 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
228 if (MemoryLogSize == -1)
229 MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size);
232 * Format the volumes. Format the root volume first so we can
233 * bootstrap the freemap.
235 format_volume(get_root_volume(), nvols, label);
236 for (i = 0; i < nvols; ++i) {
237 if (i != HAMMER_ROOT_VOLNO)
238 format_volume(get_volume(i), nvols, label);
241 print_volume(get_root_volume());
249 print_volume(const struct volume_info *vol)
251 hammer_volume_ondisk_t ondisk;
252 hammer_blockmap_t blockmap;
253 hammer_off_t total = 0;
256 const char *name = NULL;
259 ondisk = vol->ondisk;
260 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
262 nvols = ondisk->vol_count;
263 for (i = 0; i < nvols; ++i) {
264 struct volume_info *p = get_volume(i);
266 if (p->vol_no == HAMMER_ROOT_VOLNO) {
267 assert(name == NULL);
272 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
274 printf("---------------------------------------------\n");
275 printf("HAMMER version %d\n", HammerVersion);
276 printf("%d volume%s total size %s\n",
277 nvols, (nvols == 1 ? "" : "s"), sizetostr(total));
278 printf("root-volume: %s\n", name);
280 printf("header-junk-size: %s\n",
281 sizetostr(ondisk->vol_bot_beg));
282 printf("boot-area-size: %s\n",
283 sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg));
284 printf("memory-log-size: %s\n",
285 sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg));
286 printf("undo-buffer-size: %s\n",
287 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset)));
288 printf("total-pre-allocated: %s\n",
289 sizetostr(HAMMER_OFF_SHORT_ENCODE(vol->vol_free_off)));
290 printf("fsid: %s\n", fsidstr);
292 printf("NOTE: Please remember that you may have to manually set up a\n"
293 "cron(8) job to prune and reblock the filesystem regularly.\n"
294 "By default, the system automatically runs 'hammer cleanup'\n"
295 "on a nightly basis. The periodic.conf(5) variable\n"
296 "'daily_clean_hammer_enable' can be unset to disable this.\n"
297 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
298 if (total < 10*GIG) {
299 printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, "
300 "you really should not\n"
301 "try to format a HAMMER filesystem this small.\n",
302 sizetostr(HAMMER_BIGBLOCK_SIZE *
303 HAMMER_MIN_UNDO_BIGBLOCKS));
305 if (total < 50*GIG) {
306 printf("\nWARNING: HAMMER filesystems less than 50GB are "
308 "You may have to run 'hammer prune-everything' and "
310 "quite often, even if using a nohistory mount.\n");
319 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
320 " [-C cachesize[:readahead]] [-V version] special ...\n"
326 test_header_junk_size(int64_t size)
328 if (size < HAMMER_MIN_VOL_JUNK) {
330 errx(1, "The minimum header junk size is %s",
331 sizetostr(HAMMER_MIN_VOL_JUNK));
334 "WARNING: you have specified "
335 "header junk size less than %s.\n",
336 sizetostr(HAMMER_MIN_VOL_JUNK));
338 } else if (size > HAMMER_MAX_VOL_JUNK) {
339 errx(1, "The maximum header junk size is %s",
340 sizetostr(HAMMER_MAX_VOL_JUNK));
345 test_boot_area_size(int64_t size)
347 if (size < HAMMER_BOOT_MINBYTES) {
349 errx(1, "The minimum boot area size is %s",
350 sizetostr(HAMMER_BOOT_MINBYTES));
353 "WARNING: you have specified "
354 "boot area size less than %s.\n",
355 sizetostr(HAMMER_BOOT_MINBYTES));
357 } else if (size > HAMMER_BOOT_MAXBYTES) {
358 errx(1, "The maximum boot area size is %s",
359 sizetostr(HAMMER_BOOT_MAXBYTES));
364 test_memory_log_size(int64_t size)
366 if (size < HAMMER_MEM_MINBYTES) {
368 errx(1, "The minimum memory log size is %s",
369 sizetostr(HAMMER_MEM_MINBYTES));
372 "WARNING: you have specified "
373 "memory log size less than %s.\n",
374 sizetostr(HAMMER_MEM_MINBYTES));
376 } else if (size > HAMMER_MEM_MAXBYTES) {
377 errx(1, "The maximum memory log size is %s",
378 sizetostr(HAMMER_MEM_MAXBYTES));
383 test_undo_buffer_size(int64_t size)
385 int64_t minbuf, maxbuf;
387 minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
388 maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
392 errx(1, "The minimum UNDO/REDO FIFO size is %s",
396 "WARNING: you have specified an "
397 "UNDO/REDO FIFO size less than %s,\n"
398 "which may lead to VFS panics.\n",
401 } else if (size > maxbuf) {
402 errx(1, "The maximum UNDO/REDO FIFO size is %s",
408 * Convert a string to a 64 bit signed integer with various requirements.
411 getsize(const char *str, int powerof2)
416 val = strtoll(str, &ptr, 0);
435 errx(1, "Unknown suffix in number '%s'", str);
440 errx(1, "Unknown suffix in number '%s'", str);
443 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
444 errx(1, "Value not power of 2: %s", str);
447 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
448 errx(1, "Value not an integral multiple of %dK: %s",
449 HAMMER_BUFSIZE / 1024, str);
456 * Generate a transaction id. Transaction ids are no longer time-based.
457 * Put the nail in the coffin by not making the first one time-based.
459 * We could start at 1 here but start at 2^32 to reserve a small domain for
460 * possible future use.
465 static hammer_tid_t lasttid;
468 lasttid = 0x0000000100000000ULL;
478 gettimeofday(&tv, NULL);
479 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
484 * TRIM the volume, but only if the backing store is a DEVICE
488 trim_volume(struct volume_info *vol)
492 char sysctl_name[64];
496 if (strcmp(vol->type, "DEVICE")) {
497 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name);
500 if (strncmp(vol->name, "/dev/da", 7)) {
501 fprintf(stderr, "%s does not support the TRIM command\n",
506 /* Extract a number from /dev/da?s? */
507 dev_name = strdup(vol->name);
508 p = strtok(dev_name + strlen("/dev/da"), "s");
509 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
513 olen = sizeof(trim_enabled);
515 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
516 fprintf(stderr, "%s (%s) does not support the TRIM command\n",
517 vol->name, sysctl_name);
521 fprintf(stderr, "Erase device option selected, but sysctl (%s) "
522 "is not enabled\n", sysctl_name);
526 ioarg[0] = vol->device_offset;
527 ioarg[1] = vol->size;
529 printf("Trimming %s %s, sectors %llu-%llu\n",
530 vol->type, vol->name,
531 (unsigned long long)ioarg[0] / 512,
532 (unsigned long long)ioarg[1] / 512);
534 if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) {
535 fprintf(stderr, "Device trim failed\n");
543 * Format a HAMMER volume.
547 format_volume(struct volume_info *vol, int nvols, const char *label)
549 struct volume_info *root_vol;
550 hammer_volume_ondisk_t ondisk;
553 int64_t vol_buf_size;
554 hammer_off_t vol_alloc;
558 * Initialize basic information in the on-disk volume structure.
560 ondisk = vol->ondisk;
562 ondisk->vol_fsid = Hammer_FSId;
563 ondisk->vol_fstype = Hammer_FSType;
564 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
565 ondisk->vol_no = vol->vol_no;
566 ondisk->vol_count = nvols;
567 ondisk->vol_version = HammerVersion;
569 vol_alloc = HeaderJunkSize;
570 ondisk->vol_bot_beg = vol_alloc;
571 vol_alloc += BootAreaSize;
572 ondisk->vol_mem_beg = vol_alloc;
573 vol_alloc += MemoryLogSize;
576 * The remaining area is the zone 2 buffer allocation area.
578 ondisk->vol_buf_beg = vol_alloc;
579 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
580 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
582 if (vol_buf_size < 0) {
583 errx(1, "volume %d %s is too small to hold the volume header",
584 vol->vol_no, vol->name);
587 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
588 errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
591 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
592 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
594 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
595 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
596 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
599 * Format the root volume.
601 if (vol->vol_no == HAMMER_ROOT_VOLNO) {
603 * Check freemap counts before formatting
605 freeblks = count_freemap(vol);
606 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
607 if (freebytes < 10*GIG && ForceOpt == 0) {
608 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
609 "unless you use -f\n(for the size of Volume %d). "
610 "HAMMER filesystems less than 50GB are not "
611 "recommended.", HAMMER_ROOT_VOLNO);
617 ondisk->vol0_next_tid = createtid();
620 * Format freemap. vol0_stat_freebigblocks is
621 * the number of big-blocks available for anything
622 * other than freemap zone at this point.
625 assert(ondisk->vol0_stat_freebigblocks == 0);
626 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
629 * Format zones that are mapped to zone-2.
631 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
632 if (hammer_is_zone2_mapped_index(i))
633 format_blockmap(vol, i, 0);
637 * Format undo zone. Formatting decrements
638 * vol0_stat_freebigblocks whenever a new big-block
639 * is allocated for undo zone.
641 format_undomap(vol, &UndoBufferSize);
642 assert(ondisk->vol0_stat_bigblocks == 0);
643 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
646 * Format the root directory. Formatting decrements
647 * vol0_stat_freebigblocks whenever a new big-block
648 * is allocated for required zones.
650 ondisk->vol0_btree_root = format_root_directory(label);
651 ++ondisk->vol0_stat_inodes; /* root inode */
653 freeblks = initialize_freemap(vol);
654 root_vol = get_root_volume();
655 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
656 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
661 * Format the root directory.
665 format_root_directory(const char *label)
667 hammer_off_t btree_off;
668 hammer_off_t idata_off;
669 hammer_off_t pfsd_off;
670 hammer_tid_t create_tid;
671 hammer_node_ondisk_t bnode;
672 hammer_inode_data_t idata;
673 hammer_pseudofs_data_t pfsd;
674 struct buffer_info *data_buffer0 = NULL;
675 struct buffer_info *data_buffer1 = NULL;
676 struct buffer_info *data_buffer2 = NULL;
677 hammer_btree_elm_t elm;
681 * Allocate zero-filled root btree node, inode and pfs
683 bnode = alloc_btree_node(&btree_off, &data_buffer0);
684 idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
685 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
686 create_tid = createtid();
690 * Populate the inode data and inode record for the root directory.
692 idata->version = HAMMER_INODE_DATA_VERSION;
694 idata->ctime = xtime;
695 idata->mtime = xtime;
696 idata->atime = xtime;
697 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
700 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
701 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
702 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
703 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
706 * Populate the PFS data for the root PFS.
708 pfsd->sync_low_tid = 1;
709 pfsd->sync_beg_tid = 0;
710 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
711 pfsd->shared_uuid = Hammer_FSId;
712 pfsd->unique_uuid = Hammer_FSId;
713 pfsd->mirror_flags = 0;
714 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
717 * Create the root of the B-Tree. The root is a leaf node so we
718 * do not have to worry about boundary elements.
720 bnode->parent = 0; /* no parent */
722 bnode->type = HAMMER_BTREE_TYPE_LEAF;
723 bnode->mirror_tid = 0;
726 * Create the first node element for the inode.
728 elm = &bnode->elms[0];
729 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
730 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
731 HAMMER_LOCALIZE_INODE;
732 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
733 elm->leaf.base.key = 0;
734 elm->leaf.base.create_tid = create_tid;
735 elm->leaf.base.delete_tid = 0;
736 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
737 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
738 elm->leaf.create_ts = (uint32_t)time(NULL);
740 elm->leaf.data_offset = idata_off;
741 elm->leaf.data_len = sizeof(*idata);
742 hammer_crc_set_leaf(idata, &elm->leaf);
745 * Create the second node element for the PFS data.
746 * This is supposed to be a record part of the root ip (inode),
747 * so it should have the same obj_type value as above.
749 elm = &bnode->elms[1];
750 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
751 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
752 HAMMER_LOCALIZE_MISC;
753 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
754 elm->leaf.base.key = 0;
755 elm->leaf.base.create_tid = create_tid;
756 elm->leaf.base.delete_tid = 0;
757 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
758 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
759 elm->leaf.create_ts = (uint32_t)time(NULL);
761 elm->leaf.data_offset = pfsd_off;
762 elm->leaf.data_len = sizeof(*pfsd);
763 hammer_crc_set_leaf(pfsd, &elm->leaf);
765 hammer_crc_set_btree(bnode);
767 rel_buffer(data_buffer0);
768 rel_buffer(data_buffer1);
769 rel_buffer(data_buffer2);