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 "newfs_hammer.h"
37 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
38 static const char *sizetostr(off_t size);
39 static void trim_volume(struct volume_info *vol);
40 static void check_volume(struct volume_info *vol);
41 static void format_volume(struct volume_info *vol, int nvols,const char *label,
43 static hammer_off_t format_root(const char *label);
44 static u_int64_t nowtime(void);
45 static void usage(void);
47 static int ForceOpt = 0;
48 static int HammerVersion = -1;
51 #define GIG (1024LL*1024*1024)
54 main(int ac, char **av)
61 const char *label = NULL;
62 struct volume_info *vol;
66 * Sanity check basic filesystem structures. No cookies for us
69 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
70 assert(sizeof(struct hammer_blockmap_layer1) == 32);
71 assert(sizeof(struct hammer_blockmap_layer2) == 16);
74 * Generate a filesystem id and lookup the filesystem type
76 uuidgen(&Hammer_FSId, 1);
77 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
78 if (status != uuid_s_ok) {
79 errx(1, "uuids file does not have the DragonFly "
80 "HAMMER filesystem type");
86 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
98 BootAreaSize = getsize(optarg,
100 HAMMER_BOOT_MAXBYTES, 2);
103 MemAreaSize = getsize(optarg,
105 HAMMER_MEM_MAXBYTES, 2);
108 UndoBufferSize = getsize(optarg,
109 HAMMER_BIGBLOCK_SIZE,
110 HAMMER_BIGBLOCK_SIZE *
111 HAMMER_UNDO_LAYER2, 2);
112 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
113 errx(1, "The minimum UNDO/REDO FIFO size is "
115 if (UndoBufferSize < 500*1024*1024) {
117 "WARNING: you have specified an "
118 "UNDO/REDO FIFO size less than 500MB,\n"
119 "which may lead to VFS panics.\n");
123 HammerVersion = strtol(optarg, NULL, 0);
124 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
125 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
127 "I don't understand how to format "
128 "HAMMER version %d\n",
140 "newfs_hammer: A filesystem label must be specified\n");
144 if (HammerVersion < 0) {
145 size_t olen = sizeof(HammerVersion);
146 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
147 if (sysctlbyname("vfs.hammer.supported_version",
148 &HammerVersion, &olen, NULL, 0) == 0) {
149 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
150 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
152 "newfs_hammer: WARNING: HAMMER VFS "
153 "supports higher version than I "
155 "using version %d\n",
160 "newfs_hammer: WARNING: HAMMER VFS not "
161 "loaded, cannot get version info.\n"
162 "Using version %d\n",
163 HAMMER_VOL_VERSION_DEFAULT);
168 * Collect volume information
175 if (NumVolumes == 0) {
177 "newfs_hammer: You must specify at least one "
178 "special file (volume)\n");
182 if (NumVolumes > HAMMER_MAX_VOLUMES) {
184 "newfs_hammer: The maximum number of volumes is %d\n",
190 for (i = 0; i < NumVolumes; ++i) {
191 vol = setup_volume(i, av[i], 1, O_RDWR);
194 * Load up information on the volume and initialize
195 * its remaining fields.
199 char sysctl_name[64];
200 int trim_enabled = 0;
201 size_t olen = sizeof(trim_enabled);
202 char *dev_name = strdup(vol->name);
203 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
205 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
208 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
209 if(errno == ENOENT) {
210 printf("Device:%s (%s) does not support the "
211 "TRIM command\n", vol->name,sysctl_name);
215 printf("Erase device option selected, but "
216 "sysctl (%s) is not enabled\n", sysctl_name);
226 * Calculate defaults for the boot and memory area sizes.
228 avg_vol_size = total / NumVolumes;
229 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
230 MemAreaSize = init_mem_area_size(MemAreaSize, avg_vol_size);
233 * Format the volumes. Format the root volume first so we can
234 * bootstrap the freemap.
236 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
237 for (i = 0; i < NumVolumes; ++i) {
239 format_volume(get_volume(i), NumVolumes, label, total);
243 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
244 * limit. If we do this the filesystem does not have to allocate
245 * new layer2 blocks which reduces the chances of the reblocker
246 * having to fallback to an extremely inefficient algorithm.
248 vol = get_volume(RootVolNo);
249 vol->cache.modified = 1;
250 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
252 printf("---------------------------------------------\n");
253 printf("%d volume%s total size %s version %d\n",
254 NumVolumes, (NumVolumes == 1 ? "" : "s"),
255 sizetostr(total), HammerVersion);
256 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
257 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
258 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
259 printf("total-pre-allocated: %s\n",
260 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
261 printf("fsid: %s\n", fsidstr);
263 printf("NOTE: Please remember that you may have to manually set up a\n"
264 "cron(8) job to prune and reblock the filesystem regularly.\n"
265 "By default, the system automatically runs 'hammer cleanup'\n"
266 "on a nightly basis. The periodic.conf(5) variable\n"
267 "'daily_clean_hammer_enable' can be unset to disable this.\n"
268 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
269 if (total < 10*GIG) {
270 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
271 "really should not\n"
272 "try to format a HAMMER filesystem this small.\n");
274 if (total < 50*GIG) {
275 printf("\nWARNING: HAMMER filesystems less than 50GB are "
277 "You may have to run 'hammer prune-everything' and "
279 "quite often, even if using a nohistory mount.\n");
290 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
291 " [-V version] special ...\n"
297 * Convert the size in bytes to a human readable string.
301 sizetostr(off_t size)
305 if (size < 1024 / 2) {
306 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
307 } else if (size < 1024 * 1024 / 2) {
308 snprintf(buf, sizeof(buf), "%6.2fKB",
309 (double)size / 1024);
310 } else if (size < 1024 * 1024 * 1024LL / 2) {
311 snprintf(buf, sizeof(buf), "%6.2fMB",
312 (double)size / (1024 * 1024));
313 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
314 snprintf(buf, sizeof(buf), "%6.2fGB",
315 (double)size / (1024 * 1024 * 1024LL));
317 snprintf(buf, sizeof(buf), "%6.2fTB",
318 (double)size / (1024 * 1024 * 1024LL * 1024LL));
324 * Convert a string to a 64 bit signed integer with various requirements.
327 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
332 val = strtoll(str, &ptr, 0);
351 errx(1, "Unknown suffix in number '%s'\n", str);
355 errx(1, "Unknown suffix in number '%s'\n", str);
359 errx(1, "Value too small: %s, min is %s\n",
360 str, sizetostr(minval));
364 errx(1, "Value too large: %s, max is %s\n",
365 str, sizetostr(maxval));
368 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
369 errx(1, "Value not power of 2: %s\n", str);
372 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
373 errx(1, "Value not an integral multiple of %dK: %s",
374 HAMMER_BUFSIZE / 1024, str);
381 * Generate a transaction id. Transaction ids are no longer time-based.
382 * Put the nail in the coffin by not making the first one time-based.
384 * We could start at 1 here but start at 2^32 to reserve a small domain for
385 * possible future use.
390 static hammer_tid_t lasttid;
393 lasttid = 0x0000000100000000ULL;
403 gettimeofday(&tv, NULL);
404 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
409 * TRIM the volume, but only if the backing store is a DEVICE
413 trim_volume(struct volume_info *vol)
415 if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
418 /* 1MB offset to prevent destroying disk-reserved area */
419 ioarg[0] = vol->device_offset;
420 ioarg[1] = vol->size;
421 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
422 (unsigned long long)ioarg[0]/512,
423 (unsigned long long)ioarg[1]/512);
424 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
425 printf("Device trim failed\n");
432 * Check basic volume characteristics. HAMMER filesystems use a minimum
433 * of a 16KB filesystem buffer size.
437 check_volume(struct volume_info *vol)
439 struct partinfo pinfo;
443 * Get basic information about the volume
445 vol->fd = open(vol->name, O_RDWR);
447 err(1, "Unable to open %s R+W", vol->name);
448 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
450 * Allow the formatting of regular files as HAMMER volumes
452 if (fstat(vol->fd, &st) < 0)
453 err(1, "Unable to stat %s", vol->name);
454 vol->size = st.st_size;
455 vol->type = "REGFILE";
458 errx(1,"Cannot TRIM regular file %s\n", vol->name);
462 * When formatting a block device as a HAMMER volume the
463 * sector size must be compatible. HAMMER uses 16384 byte
464 * filesystem buffers.
466 if (pinfo.reserved_blocks) {
467 errx(1, "HAMMER cannot be placed in a partition "
468 "which overlaps the disklabel or MBR");
470 if (pinfo.media_blksize > HAMMER_BUFSIZE ||
471 HAMMER_BUFSIZE % pinfo.media_blksize) {
472 errx(1, "A media sector size of %d is not supported",
473 pinfo.media_blksize);
476 vol->size = pinfo.media_size;
477 vol->device_offset = pinfo.media_offset;
478 vol->type = "DEVICE";
480 printf("Volume %d %s %-15s size %s\n",
481 vol->vol_no, vol->type, vol->name,
482 sizetostr(vol->size));
485 * Reserve space for (future) header junk, setup our poor-man's
486 * big-block allocator.
488 vol->vol_alloc = HAMMER_BUFSIZE * 16;
492 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
496 format_volume(struct volume_info *vol, int nvols, const char *label,
497 off_t total_size __unused)
499 struct volume_info *root_vol;
500 struct hammer_volume_ondisk *ondisk;
506 * Initialize basic information in the on-disk volume structure.
508 ondisk = vol->ondisk;
510 ondisk->vol_fsid = Hammer_FSId;
511 ondisk->vol_fstype = Hammer_FSType;
512 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
513 ondisk->vol_no = vol->vol_no;
514 ondisk->vol_count = nvols;
515 ondisk->vol_version = HammerVersion;
517 ondisk->vol_bot_beg = vol->vol_alloc;
518 vol->vol_alloc += BootAreaSize;
519 ondisk->vol_mem_beg = vol->vol_alloc;
520 vol->vol_alloc += MemAreaSize;
523 * The remaining area is the zone 2 buffer allocation area.
525 ondisk->vol_buf_beg = vol->vol_alloc;
526 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
528 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
529 errx(1, "volume %d %s is too small to hold the volume header",
530 vol->vol_no, vol->name);
533 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
535 ondisk->vol_blocksize = HAMMER_BUFSIZE;
537 ondisk->vol_rootvol = RootVolNo;
538 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
540 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
541 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
542 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
543 ~HAMMER_BIGBLOCK_MASK64);
546 * Format the root volume.
548 if (vol->vol_no == RootVolNo) {
550 * Check freemap counts before formatting
552 freeblks = count_freemap(vol);
553 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
554 if (freebytes < 10*GIG && ForceOpt == 0) {
555 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
556 "unless you use -f\n(for the size of Volume %d). "
557 "HAMMER filesystems less than 50GB are not "
558 "recommended.\n", RootVolNo);
564 ondisk->vol0_next_tid = createtid();
567 * Format freemap. vol0_stat_freebigblocks is
568 * the number of big-blocks available for anything
569 * other than freemap zone at this point.
572 assert(ondisk->vol0_stat_freebigblocks == 0);
573 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
576 * Format zones that are mapped to zone-2.
578 for (i = HAMMER_ZONE2_MAPPED_INDEX; i < HAMMER_MAX_ZONES; ++i) {
579 format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
583 * Format undo zone. Formatting decrements
584 * vol0_stat_freebigblocks whenever a new big-block
585 * is allocated for undo zone.
588 assert(ondisk->vol0_stat_bigblocks == 0);
589 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
592 * Format the root directory. Formatting decrements
593 * vol0_stat_freebigblocks whenever a new big-block
594 * is allocated for required zones.
596 ondisk->vol0_btree_root = format_root(label);
597 ++ondisk->vol0_stat_inodes; /* root inode */
601 freeblks = initialize_freemap(vol);
602 root_vol = get_volume(RootVolNo);
603 root_vol->cache.modified = 1;
604 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
605 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
606 rel_volume(root_vol);
611 * Format the root directory.
615 format_root(const char *label)
617 hammer_off_t btree_off;
618 hammer_off_t pfsd_off;
619 hammer_off_t data_off;
620 hammer_tid_t create_tid;
621 hammer_node_ondisk_t bnode;
622 struct hammer_inode_data *idata;
623 hammer_pseudofs_data_t pfsd;
624 struct buffer_info *data_buffer0 = NULL;
625 struct buffer_info *data_buffer1 = NULL;
626 struct buffer_info *data_buffer2 = NULL;
627 hammer_btree_elm_t elm;
631 * Allocate zero-filled root btree node, inode and pfs
633 bnode = alloc_btree_element(&btree_off, &data_buffer0);
634 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
635 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
636 create_tid = createtid();
640 * Populate the inode data and inode record for the root directory.
642 idata->version = HAMMER_INODE_DATA_VERSION;
644 idata->ctime = xtime;
645 idata->mtime = xtime;
646 idata->atime = xtime;
647 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
650 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
651 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
652 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
653 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
655 pfsd->sync_low_tid = 1;
656 pfsd->sync_beg_tid = 0;
657 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
658 pfsd->shared_uuid = Hammer_FSId;
659 pfsd->unique_uuid = Hammer_FSId;
660 pfsd->reserved01 = 0;
661 pfsd->mirror_flags = 0;
662 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
665 * Create the root of the B-Tree. The root is a leaf node so we
666 * do not have to worry about boundary elements.
668 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
670 bnode->type = HAMMER_BTREE_TYPE_LEAF;
672 elm = &bnode->elms[0];
673 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
674 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
675 HAMMER_LOCALIZE_INODE;
676 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
677 elm->leaf.base.key = 0;
678 elm->leaf.base.create_tid = create_tid;
679 elm->leaf.base.delete_tid = 0;
680 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
681 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
682 elm->leaf.create_ts = (u_int32_t)time(NULL);
684 elm->leaf.data_offset = data_off;
685 elm->leaf.data_len = sizeof(*idata);
686 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
688 elm = &bnode->elms[1];
689 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
690 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
691 HAMMER_LOCALIZE_MISC;
692 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
693 elm->leaf.base.key = 0;
694 elm->leaf.base.create_tid = create_tid;
695 elm->leaf.base.delete_tid = 0;
696 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
697 elm->leaf.base.obj_type = 0;
698 elm->leaf.create_ts = (u_int32_t)time(NULL);
700 elm->leaf.data_offset = pfsd_off;
701 elm->leaf.data_len = sizeof(*pfsd);
702 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
704 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);