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 void 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(const char *label);
45 static uint64_t nowtime(void);
46 static void usage(void);
48 static int ForceOpt = 0;
49 static int HammerVersion = -1;
51 #define GIG (1024LL*1024*1024)
54 main(int ac, char **av)
63 const char *label = NULL;
64 struct volume_info *vol;
68 * Sanity check basic filesystem structures. No cookies for us
71 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
72 assert(sizeof(struct hammer_blockmap_layer1) == 32);
73 assert(sizeof(struct hammer_blockmap_layer2) == 16);
76 * Generate a filesystem id and lookup the filesystem type
78 uuidgen(&Hammer_FSId, 1);
79 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
80 if (status != uuid_s_ok) {
81 errx(1, "uuids file does not have the DragonFly "
82 "HAMMER filesystem type");
88 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
100 BootAreaSize = getsize(optarg,
102 HAMMER_BOOT_MAXBYTES, 2);
105 MemAreaSize = getsize(optarg,
107 HAMMER_MEM_MAXBYTES, 2);
110 UndoBufferSize = getsize(optarg,
111 HAMMER_BIGBLOCK_SIZE,
112 HAMMER_BIGBLOCK_SIZE *
113 HAMMER_UNDO_LAYER2, 2);
114 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
115 errx(1, "The minimum UNDO/REDO FIFO size is "
117 if (UndoBufferSize < 500*1024*1024) {
119 "WARNING: you have specified an "
120 "UNDO/REDO FIFO size less than 500MB,\n"
121 "which may lead to VFS panics.\n");
125 HammerVersion = strtol(optarg, NULL, 0);
126 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
127 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
129 "I don't understand how to format "
130 "HAMMER version %d\n",
142 "newfs_hammer: A filesystem label must be specified\n");
146 if (HammerVersion < 0) {
147 size_t olen = sizeof(HammerVersion);
148 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
149 if (sysctlbyname("vfs.hammer.supported_version",
150 &HammerVersion, &olen, NULL, 0) == 0) {
151 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
152 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
154 "newfs_hammer: WARNING: HAMMER VFS "
155 "supports higher version than I "
157 "using version %d\n",
162 "newfs_hammer: WARNING: HAMMER VFS not "
163 "loaded, cannot get version info.\n"
164 "Using version %d\n",
165 HAMMER_VOL_VERSION_DEFAULT);
170 * Collect volume information
179 "newfs_hammer: You must specify at least one "
180 "special file (volume)\n");
184 if (nvols > HAMMER_MAX_VOLUMES) {
186 "newfs_hammer: The maximum number of volumes is %d\n",
192 for (i = 0; i < nvols; ++i) {
193 vol = setup_volume(i, av[i], 1, O_RDWR);
196 * Load up information on the volume and initialize
197 * its remaining fields.
200 printf("Volume %d %s %-15s size %s\n",
201 vol->vol_no, vol->type, vol->name,
202 sizetostr(vol->size));
205 if (strcmp(vol->type, "REGFILE") == 0) {
206 fprintf(stderr, "Cannot TRIM regular file %s\n",
211 char sysctl_name[64];
212 int trim_enabled = 0;
213 size_t olen = sizeof(trim_enabled);
214 char *dev_name = strdup(vol->name);
215 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
217 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
220 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
221 if(errno == ENOENT) {
222 printf("%s %s (%s) does not support the TRIM "
224 vol->type, vol->name, sysctl_name);
228 printf("Erase device option selected, but "
229 "sysctl (%s) is not enabled\n", sysctl_name);
239 * Calculate defaults for the boot and memory area sizes.
241 avg_vol_size = total / nvols;
242 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
243 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
246 * Format the volumes. Format the root volume first so we can
247 * bootstrap the freemap.
249 format_volume(get_volume(RootVolNo), nvols, label);
250 for (i = 0; i < nvols; ++i) {
252 format_volume(get_volume(i), nvols, label);
256 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
257 * limit. If we do this the filesystem does not have to allocate
258 * new layer2 blocks which reduces the chances of the reblocker
259 * having to fallback to an extremely inefficient algorithm.
261 vol = get_volume(RootVolNo);
262 vol->cache.modified = 1;
263 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
265 printf("---------------------------------------------\n");
266 printf("%d volume%s total size %s version %d\n",
267 nvols, (nvols == 1 ? "" : "s"),
268 sizetostr(total), HammerVersion);
269 printf("root-volume: %s\n", vol->name);
270 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
271 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
272 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
273 printf("total-pre-allocated: %s\n",
274 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
275 printf("fsid: %s\n", fsidstr);
277 printf("NOTE: Please remember that you may have to manually set up a\n"
278 "cron(8) job to prune and reblock the filesystem regularly.\n"
279 "By default, the system automatically runs 'hammer cleanup'\n"
280 "on a nightly basis. The periodic.conf(5) variable\n"
281 "'daily_clean_hammer_enable' can be unset to disable this.\n"
282 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
283 if (total < 10*GIG) {
284 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
285 "really should not\n"
286 "try to format a HAMMER filesystem this small.\n");
288 if (total < 50*GIG) {
289 printf("\nWARNING: HAMMER filesystems less than 50GB are "
291 "You may have to run 'hammer prune-everything' and "
293 "quite often, even if using a nohistory mount.\n");
304 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
305 " [-V version] special ...\n"
311 * Convert the size in bytes to a human readable string.
315 sizetostr(off_t size)
319 if (size < 1024 / 2) {
320 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
321 } else if (size < 1024 * 1024 / 2) {
322 snprintf(buf, sizeof(buf), "%6.2fKB",
323 (double)size / 1024);
324 } else if (size < 1024 * 1024 * 1024LL / 2) {
325 snprintf(buf, sizeof(buf), "%6.2fMB",
326 (double)size / (1024 * 1024));
327 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
328 snprintf(buf, sizeof(buf), "%6.2fGB",
329 (double)size / (1024 * 1024 * 1024LL));
331 snprintf(buf, sizeof(buf), "%6.2fTB",
332 (double)size / (1024 * 1024 * 1024LL * 1024LL));
338 * Convert a string to a 64 bit signed integer with various requirements.
341 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
346 val = strtoll(str, &ptr, 0);
365 errx(1, "Unknown suffix in number '%s'\n", str);
369 errx(1, "Unknown suffix in number '%s'\n", str);
373 errx(1, "Value too small: %s, min is %s\n",
374 str, sizetostr(minval));
378 errx(1, "Value too large: %s, max is %s\n",
379 str, sizetostr(maxval));
382 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
383 errx(1, "Value not power of 2: %s\n", str);
386 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
387 errx(1, "Value not an integral multiple of %dK: %s",
388 HAMMER_BUFSIZE / 1024, str);
395 * Generate a transaction id. Transaction ids are no longer time-based.
396 * Put the nail in the coffin by not making the first one time-based.
398 * We could start at 1 here but start at 2^32 to reserve a small domain for
399 * possible future use.
404 static hammer_tid_t lasttid;
407 lasttid = 0x0000000100000000ULL;
417 gettimeofday(&tv, NULL);
418 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
423 * TRIM the volume, but only if the backing store is a DEVICE
427 trim_volume(struct volume_info *vol)
429 if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
432 /* 1MB offset to prevent destroying disk-reserved area */
433 ioarg[0] = vol->device_offset;
434 ioarg[1] = vol->size;
436 printf("Trimming %s %s, sectors (%llu -%llu)\n",
437 vol->type, vol->name,
438 (unsigned long long)ioarg[0]/512,
439 (unsigned long long)ioarg[1]/512);
441 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
442 printf("Device trim failed\n");
449 * Format a HAMMER volume.
453 format_volume(struct volume_info *vol, int nvols, const char *label)
455 struct volume_info *root_vol;
456 struct hammer_volume_ondisk *ondisk;
459 hammer_off_t vol_alloc;
463 * Initialize basic information in the on-disk volume structure.
465 ondisk = vol->ondisk;
467 ondisk->vol_fsid = Hammer_FSId;
468 ondisk->vol_fstype = Hammer_FSType;
469 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
470 ondisk->vol_no = vol->vol_no;
471 ondisk->vol_count = nvols;
472 ondisk->vol_version = HammerVersion;
475 * Reserve space for (future) header junk, setup our poor-man's
476 * big-block allocator.
478 vol_alloc = HAMMER_BUFSIZE * 16; /* 262144 */
480 ondisk->vol_bot_beg = vol_alloc;
481 vol_alloc += BootAreaSize;
482 ondisk->vol_mem_beg = vol_alloc;
483 vol_alloc += MemAreaSize;
486 * The remaining area is the zone 2 buffer allocation area.
488 ondisk->vol_buf_beg = vol_alloc;
489 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
491 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
492 errx(1, "volume %d %s is too small to hold the volume header",
493 vol->vol_no, vol->name);
496 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
498 ondisk->vol_blocksize = HAMMER_BUFSIZE;
500 ondisk->vol_rootvol = RootVolNo;
501 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
503 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
504 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
505 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
506 ~HAMMER_BIGBLOCK_MASK64);
509 * Format the root volume.
511 if (vol->vol_no == RootVolNo) {
513 * Check freemap counts before formatting
515 freeblks = count_freemap(vol);
516 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
517 if (freebytes < 10*GIG && ForceOpt == 0) {
518 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
519 "unless you use -f\n(for the size of Volume %d). "
520 "HAMMER filesystems less than 50GB are not "
521 "recommended.\n", RootVolNo);
527 ondisk->vol0_next_tid = createtid();
530 * Format freemap. vol0_stat_freebigblocks is
531 * the number of big-blocks available for anything
532 * other than freemap zone at this point.
535 assert(ondisk->vol0_stat_freebigblocks == 0);
536 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
539 * Format zones that are mapped to zone-2.
541 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
542 if (hammer_is_zone2_mapped_index(i))
543 format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
547 * Format undo zone. Formatting decrements
548 * vol0_stat_freebigblocks whenever a new big-block
549 * is allocated for undo zone.
552 assert(ondisk->vol0_stat_bigblocks == 0);
553 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
556 * Format the root directory. Formatting decrements
557 * vol0_stat_freebigblocks whenever a new big-block
558 * is allocated for required zones.
560 ondisk->vol0_btree_root = format_root(label);
561 ++ondisk->vol0_stat_inodes; /* root inode */
565 freeblks = initialize_freemap(vol);
566 root_vol = get_volume(RootVolNo);
567 root_vol->cache.modified = 1;
568 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
569 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
570 rel_volume(root_vol);
575 * Format the root directory.
579 format_root(const char *label)
581 hammer_off_t btree_off;
582 hammer_off_t pfsd_off;
583 hammer_off_t data_off;
584 hammer_tid_t create_tid;
585 hammer_node_ondisk_t bnode;
586 struct hammer_inode_data *idata;
587 hammer_pseudofs_data_t pfsd;
588 struct buffer_info *data_buffer0 = NULL;
589 struct buffer_info *data_buffer1 = NULL;
590 struct buffer_info *data_buffer2 = NULL;
591 hammer_btree_elm_t elm;
595 * Allocate zero-filled root btree node, inode and pfs
597 bnode = alloc_btree_element(&btree_off, &data_buffer0);
598 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
599 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
600 create_tid = createtid();
604 * Populate the inode data and inode record for the root directory.
606 idata->version = HAMMER_INODE_DATA_VERSION;
608 idata->ctime = xtime;
609 idata->mtime = xtime;
610 idata->atime = xtime;
611 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
614 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
615 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
616 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
617 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
619 pfsd->sync_low_tid = 1;
620 pfsd->sync_beg_tid = 0;
621 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
622 pfsd->shared_uuid = Hammer_FSId;
623 pfsd->unique_uuid = Hammer_FSId;
624 pfsd->reserved01 = 0;
625 pfsd->mirror_flags = 0;
626 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
629 * Create the root of the B-Tree. The root is a leaf node so we
630 * do not have to worry about boundary elements.
633 bnode->type = HAMMER_BTREE_TYPE_LEAF;
635 elm = &bnode->elms[0];
636 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
637 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
638 HAMMER_LOCALIZE_INODE;
639 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
640 elm->leaf.base.key = 0;
641 elm->leaf.base.create_tid = create_tid;
642 elm->leaf.base.delete_tid = 0;
643 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
644 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
645 elm->leaf.create_ts = (uint32_t)time(NULL);
647 elm->leaf.data_offset = data_off;
648 elm->leaf.data_len = sizeof(*idata);
649 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
651 elm = &bnode->elms[1];
652 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
653 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
654 HAMMER_LOCALIZE_MISC;
655 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
656 elm->leaf.base.key = 0;
657 elm->leaf.base.create_tid = create_tid;
658 elm->leaf.base.delete_tid = 0;
659 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
660 elm->leaf.base.obj_type = 0;
661 elm->leaf.create_ts = (uint32_t)time(NULL);
663 elm->leaf.data_offset = pfsd_off;
664 elm->leaf.data_len = sizeof(*pfsd);
665 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
667 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);