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 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 usage(int exit_code);
48 static int64_t BootAreaSize;
49 static int64_t MemAreaSize;
50 static int64_t UndoBufferSize;
51 static int HammerVersion = -1;
53 #define GIG (1024LL*1024*1024)
56 main(int ac, char **av)
65 const char *label = NULL;
66 struct volume_info *vol;
70 * Sanity check basic filesystem structures. No cookies for us
73 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
74 assert(sizeof(struct hammer_blockmap_layer1) == 32);
75 assert(sizeof(struct hammer_blockmap_layer2) == 16);
78 * Generate a filesystem id and lookup the filesystem type
80 uuidgen(&Hammer_FSId, 1);
81 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
82 if (status != uuid_s_ok) {
83 errx(1, "uuids file does not have the DragonFly "
84 "HAMMER filesystem type");
90 while ((ch = getopt(ac, av, "dfEL:b:m:u:hC:V:")) != -1) {
105 BootAreaSize = getsize(optarg,
107 HAMMER_BOOT_MAXBYTES, 2);
110 MemAreaSize = getsize(optarg,
112 HAMMER_MEM_MAXBYTES, 2);
115 UndoBufferSize = getsize(optarg,
116 HAMMER_BIGBLOCK_SIZE,
117 HAMMER_BIGBLOCK_SIZE *
118 HAMMER_UNDO_LAYER2, 2);
119 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
120 errx(1, "The minimum UNDO/REDO FIFO size is "
122 if (UndoBufferSize < 500*1024*1024) {
124 "WARNING: you have specified an "
125 "UNDO/REDO FIFO size less than 500MB,\n"
126 "which may lead to VFS panics.\n");
133 if (hammer_parse_cache_size(optarg) == -1)
137 HammerVersion = strtol(optarg, NULL, 0);
138 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
139 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
141 "I don't understand how to format "
142 "HAMMER version %d\n",
154 "newfs_hammer: A filesystem label must be specified\n");
158 if (HammerVersion < 0) {
159 size_t olen = sizeof(HammerVersion);
160 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
161 if (sysctlbyname("vfs.hammer.supported_version",
162 &HammerVersion, &olen, NULL, 0) == 0) {
163 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
164 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
166 "newfs_hammer: WARNING: HAMMER VFS "
167 "supports higher version than I "
169 "using version %d\n",
174 "newfs_hammer: WARNING: HAMMER VFS not "
175 "loaded, cannot get version info.\n"
176 "Using version %d\n",
177 HAMMER_VOL_VERSION_DEFAULT);
182 * Collect volume information
190 "newfs_hammer: You must specify at least one "
191 "special file (volume)\n");
195 if (nvols > HAMMER_MAX_VOLUMES) {
197 "newfs_hammer: The maximum number of volumes is %d\n",
203 for (i = 0; i < nvols; ++i) {
204 vol = init_volume(i, av[i], O_RDWR);
205 printf("Volume %d %s %-15s size %s\n",
206 vol->vol_no, vol->type, vol->name,
207 sizetostr(vol->size));
210 int res = trim_volume(vol);
211 if (res == -1 || (res == 1 && ForceOpt == 0))
218 * Calculate defaults for the boot and memory area sizes.
220 avg_vol_size = total / nvols;
221 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
222 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
225 * Format the volumes. Format the root volume first so we can
226 * bootstrap the freemap.
228 format_volume(get_root_volume(), nvols, label);
229 for (i = 0; i < nvols; ++i) {
230 if (i != HAMMER_ROOT_VOLNO)
231 format_volume(get_volume(i), nvols, label);
235 * Print information stored in the root volume header.
237 vol = get_root_volume();
238 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
240 printf("---------------------------------------------\n");
241 printf("%d volume%s total size %s version %d\n",
242 nvols, (nvols == 1 ? "" : "s"),
243 sizetostr(total), HammerVersion);
244 printf("root-volume: %s\n", vol->name);
245 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
246 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
247 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
248 printf("total-pre-allocated: %s\n",
249 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
250 printf("fsid: %s\n", fsidstr);
252 printf("NOTE: Please remember that you may have to manually set up a\n"
253 "cron(8) job to prune and reblock the filesystem regularly.\n"
254 "By default, the system automatically runs 'hammer cleanup'\n"
255 "on a nightly basis. The periodic.conf(5) variable\n"
256 "'daily_clean_hammer_enable' can be unset to disable this.\n"
257 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
258 if (total < 10*GIG) {
259 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
260 "really should not\n"
261 "try to format a HAMMER filesystem this small.\n");
263 if (total < 50*GIG) {
264 printf("\nWARNING: HAMMER filesystems less than 50GB are "
266 "You may have to run 'hammer prune-everything' and "
268 "quite often, even if using a nohistory mount.\n");
279 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
280 " [-C cachesize[:readahead]] [-V version] special ...\n"
286 * Convert a string to a 64 bit signed integer with various requirements.
289 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
294 val = strtoll(str, &ptr, 0);
313 errx(1, "Unknown suffix in number '%s'\n", str);
317 errx(1, "Unknown suffix in number '%s'\n", str);
321 errx(1, "Value too small: %s, min is %s\n",
322 str, sizetostr(minval));
326 errx(1, "Value too large: %s, max is %s\n",
327 str, sizetostr(maxval));
330 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
331 errx(1, "Value not power of 2: %s\n", str);
334 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
335 errx(1, "Value not an integral multiple of %dK: %s",
336 HAMMER_BUFSIZE / 1024, str);
343 * Generate a transaction id. Transaction ids are no longer time-based.
344 * Put the nail in the coffin by not making the first one time-based.
346 * We could start at 1 here but start at 2^32 to reserve a small domain for
347 * possible future use.
352 static hammer_tid_t lasttid;
355 lasttid = 0x0000000100000000ULL;
365 gettimeofday(&tv, NULL);
366 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
371 * TRIM the volume, but only if the backing store is a DEVICE
375 trim_volume(struct volume_info *vol)
379 char sysctl_name[64];
383 if (strcmp(vol->type, "DEVICE")) {
384 fprintf(stderr, "Cannot TRIM regular file %s\n", vol->name);
387 if (strncmp(vol->name, "/dev/da", 7)) {
388 fprintf(stderr, "%s does not support the TRIM command\n",
393 /* Extract a number from /dev/da?s? */
394 dev_name = strdup(vol->name);
395 p = strtok(dev_name + strlen("/dev/da"), "s");
396 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
400 olen = sizeof(trim_enabled);
402 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
403 fprintf(stderr, "%s (%s) does not support the TRIM command\n",
404 vol->name, sysctl_name);
408 fprintf(stderr, "Erase device option selected, but sysctl (%s) "
409 "is not enabled\n", sysctl_name);
413 ioarg[0] = vol->device_offset;
414 ioarg[1] = vol->size;
416 printf("Trimming %s %s, sectors %llu-%llu\n",
417 vol->type, vol->name,
418 (unsigned long long)ioarg[0] / 512,
419 (unsigned long long)ioarg[1] / 512);
421 if (ioctl(vol->fd, IOCTLTRIM, ioarg) == -1) {
422 fprintf(stderr, "Device trim failed\n");
430 * Format a HAMMER volume.
434 format_volume(struct volume_info *vol, int nvols, const char *label)
436 struct volume_info *root_vol;
437 hammer_volume_ondisk_t ondisk;
440 int64_t vol_buf_size;
441 hammer_off_t vol_alloc;
445 * Initialize basic information in the on-disk volume structure.
447 ondisk = vol->ondisk;
449 ondisk->vol_fsid = Hammer_FSId;
450 ondisk->vol_fstype = Hammer_FSType;
451 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
452 ondisk->vol_no = vol->vol_no;
453 ondisk->vol_count = nvols;
454 ondisk->vol_version = HammerVersion;
457 * Reserve space for (future) header junk, setup our poor-man's
458 * big-block allocator.
460 vol_alloc = HAMMER_VOL_ALLOC;
462 ondisk->vol_bot_beg = vol_alloc;
463 vol_alloc += BootAreaSize;
464 ondisk->vol_mem_beg = vol_alloc;
465 vol_alloc += MemAreaSize;
468 * The remaining area is the zone 2 buffer allocation area.
470 ondisk->vol_buf_beg = vol_alloc;
471 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
472 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
474 if (vol_buf_size < 0) {
475 errx(1, "volume %d %s is too small to hold the volume header",
476 vol->vol_no, vol->name);
479 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0) {
480 errx(1, "volume %d %s is too large", vol->vol_no, vol->name);
483 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
484 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
486 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
487 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
488 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
491 * Format the root volume.
493 if (vol->vol_no == HAMMER_ROOT_VOLNO) {
495 * Check freemap counts before formatting
497 freeblks = count_freemap(vol);
498 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
499 if (freebytes < 10*GIG && ForceOpt == 0) {
500 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
501 "unless you use -f\n(for the size of Volume %d). "
502 "HAMMER filesystems less than 50GB are not "
503 "recommended.\n", HAMMER_ROOT_VOLNO);
509 ondisk->vol0_next_tid = createtid();
512 * Format freemap. vol0_stat_freebigblocks is
513 * the number of big-blocks available for anything
514 * other than freemap zone at this point.
517 assert(ondisk->vol0_stat_freebigblocks == 0);
518 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
521 * Format zones that are mapped to zone-2.
523 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
524 if (hammer_is_zone2_mapped_index(i))
525 format_blockmap(vol, i, 0);
529 * Format undo zone. Formatting decrements
530 * vol0_stat_freebigblocks whenever a new big-block
531 * is allocated for undo zone.
533 format_undomap(vol, &UndoBufferSize);
534 assert(ondisk->vol0_stat_bigblocks == 0);
535 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
538 * Format the root directory. Formatting decrements
539 * vol0_stat_freebigblocks whenever a new big-block
540 * is allocated for required zones.
542 ondisk->vol0_btree_root = format_root_directory(label);
543 ++ondisk->vol0_stat_inodes; /* root inode */
545 freeblks = initialize_freemap(vol);
546 root_vol = get_root_volume();
547 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
548 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
553 * Format the root directory.
557 format_root_directory(const char *label)
559 hammer_off_t btree_off;
560 hammer_off_t idata_off;
561 hammer_off_t pfsd_off;
562 hammer_tid_t create_tid;
563 hammer_node_ondisk_t bnode;
564 hammer_inode_data_t idata;
565 hammer_pseudofs_data_t pfsd;
566 struct buffer_info *data_buffer0 = NULL;
567 struct buffer_info *data_buffer1 = NULL;
568 struct buffer_info *data_buffer2 = NULL;
569 hammer_btree_elm_t elm;
573 * Allocate zero-filled root btree node, inode and pfs
575 bnode = alloc_btree_element(&btree_off, &data_buffer0);
576 idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
577 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
578 create_tid = createtid();
582 * Populate the inode data and inode record for the root directory.
584 idata->version = HAMMER_INODE_DATA_VERSION;
586 idata->ctime = xtime;
587 idata->mtime = xtime;
588 idata->atime = xtime;
589 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
592 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
593 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
594 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
595 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
598 * Populate the PFS data for the root PFS.
600 pfsd->sync_low_tid = 1;
601 pfsd->sync_beg_tid = 0;
602 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
603 pfsd->shared_uuid = Hammer_FSId;
604 pfsd->unique_uuid = Hammer_FSId;
605 pfsd->mirror_flags = 0;
606 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
609 * Create the root of the B-Tree. The root is a leaf node so we
610 * do not have to worry about boundary elements.
612 bnode->parent = 0; /* no parent */
614 bnode->type = HAMMER_BTREE_TYPE_LEAF;
615 bnode->mirror_tid = 0;
618 * Create the first node element for the inode.
620 elm = &bnode->elms[0];
621 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
622 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
623 HAMMER_LOCALIZE_INODE;
624 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
625 elm->leaf.base.key = 0;
626 elm->leaf.base.create_tid = create_tid;
627 elm->leaf.base.delete_tid = 0;
628 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
629 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
630 elm->leaf.create_ts = (uint32_t)time(NULL);
632 elm->leaf.data_offset = idata_off;
633 elm->leaf.data_len = sizeof(*idata);
634 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
637 * Create the second node element for the PFS data.
638 * This is supposed to be a record part of the root ip (inode),
639 * so it should have the same obj_type value as above.
641 elm = &bnode->elms[1];
642 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
643 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
644 HAMMER_LOCALIZE_MISC;
645 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
646 elm->leaf.base.key = 0;
647 elm->leaf.base.create_tid = create_tid;
648 elm->leaf.base.delete_tid = 0;
649 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
650 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
651 elm->leaf.create_ts = (uint32_t)time(NULL);
653 elm->leaf.data_offset = pfsd_off;
654 elm->leaf.data_len = sizeof(*pfsd);
655 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
657 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
659 rel_buffer(data_buffer0);
660 rel_buffer(data_buffer1);
661 rel_buffer(data_buffer2);