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)
60 const char *label = NULL;
61 struct volume_info *vol;
65 * Sanity check basic filesystem structures. No cookies for us
68 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
69 assert(sizeof(struct hammer_blockmap_layer1) == 32);
70 assert(sizeof(struct hammer_blockmap_layer2) == 16);
73 * Generate a filesystem id and lookup the filesystem type
75 uuidgen(&Hammer_FSId, 1);
76 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
77 if (status != uuid_s_ok) {
78 errx(1, "uuids file does not have the DragonFly "
79 "HAMMER filesystem type");
85 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
97 BootAreaSize = getsize(optarg,
99 HAMMER_BOOT_MAXBYTES, 2);
102 MemAreaSize = getsize(optarg,
104 HAMMER_MEM_MAXBYTES, 2);
107 UndoBufferSize = getsize(optarg,
108 HAMMER_BIGBLOCK_SIZE,
109 HAMMER_BIGBLOCK_SIZE *
110 HAMMER_UNDO_LAYER2, 2);
111 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
112 errx(1, "The minimum UNDO/REDO FIFO size is "
114 if (UndoBufferSize < 500*1024*1024) {
116 "WARNING: you have specified an "
117 "UNDO/REDO FIFO size less than 500MB,\n"
118 "which may lead to VFS panics.\n");
122 HammerVersion = strtol(optarg, NULL, 0);
123 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
124 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
126 "I don't understand how to format "
127 "HAMMER version %d\n",
139 "newfs_hammer: A filesystem label must be specified\n");
143 if (HammerVersion < 0) {
144 size_t olen = sizeof(HammerVersion);
145 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
146 if (sysctlbyname("vfs.hammer.supported_version",
147 &HammerVersion, &olen, NULL, 0) == 0) {
148 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
149 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
151 "newfs_hammer: WARNING: HAMMER VFS "
152 "supports higher version than I "
154 "using version %d\n",
159 "newfs_hammer: WARNING: HAMMER VFS not "
160 "loaded, cannot get version info.\n"
161 "Using version %d\n",
162 HAMMER_VOL_VERSION_DEFAULT);
167 * Collect volume information
174 if (NumVolumes == 0) {
176 "newfs_hammer: You must specify at least one special file (volume)\n");
180 if (NumVolumes > HAMMER_MAX_VOLUMES) {
182 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
187 for (i = 0; i < NumVolumes; ++i) {
188 vol = setup_volume(i, av[i], 1, O_RDWR);
191 * Load up information on the volume and initialize
192 * its remaining fields.
196 char sysctl_name[64];
197 int trim_enabled = 0;
198 size_t olen = sizeof(trim_enabled);
199 char *dev_name = strdup(vol->name);
200 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
202 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
205 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
206 if(errno == ENOENT) {
207 printf("Device:%s (%s) does not support the "
208 "TRIM command\n", vol->name,sysctl_name);
212 printf("Erase device option selected, but "
213 "sysctl (%s) is not enabled\n", sysctl_name);
223 * Calculate defaults for the boot and memory area sizes.
225 if (BootAreaSize == 0) {
226 BootAreaSize = HAMMER_BOOT_NOMBYTES;
227 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
229 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
231 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
232 BootAreaSize = HAMMER_BOOT_MINBYTES;
234 if (MemAreaSize == 0) {
235 MemAreaSize = HAMMER_MEM_NOMBYTES;
236 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
238 if (MemAreaSize < HAMMER_MEM_MINBYTES)
240 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
241 MemAreaSize = HAMMER_MEM_MINBYTES;
245 * Format the volumes. Format the root volume first so we can
246 * bootstrap the freemap.
248 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
249 for (i = 0; i < NumVolumes; ++i) {
251 format_volume(get_volume(i), NumVolumes, label, total);
255 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
256 * limit. If we do this the filesystem does not have to allocate
257 * new layer2 blocks which reduces the chances of the reblocker
258 * having to fallback to an extremely inefficient algorithm.
260 vol = get_volume(RootVolNo);
261 vol->cache.modified = 1;
262 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
264 printf("---------------------------------------------\n");
265 printf("%d volume%s total size %s version %d\n",
266 NumVolumes, (NumVolumes == 1 ? "" : "s"),
267 sizetostr(total), HammerVersion);
268 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
269 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
270 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
271 printf("total-pre-allocated: %s\n",
272 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
273 printf("fsid: %s\n", fsidstr);
275 printf("NOTE: Please remember that you may have to manually set up a\n"
276 "cron(8) job to prune and reblock the filesystem regularly.\n"
277 "By default, the system automatically runs 'hammer cleanup'\n"
278 "on a nightly basis. The periodic.conf(5) variable\n"
279 "'daily_clean_hammer_enable' can be unset to disable this.\n"
280 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
281 if (total < 10*GIG) {
282 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
283 "really should not\n"
284 "try to format a HAMMER filesystem this small.\n");
286 if (total < 50*GIG) {
287 printf("\nWARNING: HAMMER filesystems less than 50GB are "
289 "You may have to run 'hammer prune-everything' and "
291 "quite often, even if using a nohistory mount.\n");
302 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
303 " [-V version] special ...\n"
309 * Convert the size in bytes to a human readable string.
313 sizetostr(off_t size)
317 if (size < 1024 / 2) {
318 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
319 } else if (size < 1024 * 1024 / 2) {
320 snprintf(buf, sizeof(buf), "%6.2fKB",
321 (double)size / 1024);
322 } else if (size < 1024 * 1024 * 1024LL / 2) {
323 snprintf(buf, sizeof(buf), "%6.2fMB",
324 (double)size / (1024 * 1024));
325 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
326 snprintf(buf, sizeof(buf), "%6.2fGB",
327 (double)size / (1024 * 1024 * 1024LL));
329 snprintf(buf, sizeof(buf), "%6.2fTB",
330 (double)size / (1024 * 1024 * 1024LL * 1024LL));
336 * Convert a string to a 64 bit signed integer with various requirements.
339 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
344 val = strtoll(str, &ptr, 0);
363 errx(1, "Unknown suffix in number '%s'\n", str);
367 errx(1, "Unknown suffix in number '%s'\n", str);
371 errx(1, "Value too small: %s, min is %s\n",
372 str, sizetostr(minval));
376 errx(1, "Value too large: %s, max is %s\n",
377 str, sizetostr(maxval));
380 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
381 errx(1, "Value not power of 2: %s\n", str);
384 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
385 errx(1, "Value not an integral multiple of %dK: %s",
386 HAMMER_BUFSIZE / 1024, str);
393 * Generate a transaction id. Transaction ids are no longer time-based.
394 * Put the nail in the coffin by not making the first one time-based.
396 * We could start at 1 here but start at 2^32 to reserve a small domain for
397 * possible future use.
402 static hammer_tid_t lasttid;
405 lasttid = 0x0000000100000000ULL;
415 gettimeofday(&tv, NULL);
416 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
421 * TRIM the volume, but only if the backing store is a DEVICE
425 trim_volume(struct volume_info *vol)
427 if (strncmp(vol->type, "DEVICE", sizeof("DEVICE")) == 0) {
430 /* 1MB offset to prevent destroying disk-reserved area */
431 ioarg[0] = vol->device_offset;
432 ioarg[1] = vol->size;
433 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
434 (unsigned long long)ioarg[0]/512,
435 (unsigned long long)ioarg[1]/512);
436 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
437 printf("Device trim failed\n");
444 * Check basic volume characteristics. HAMMER filesystems use a minimum
445 * of a 16KB filesystem buffer size.
449 check_volume(struct volume_info *vol)
451 struct partinfo pinfo;
455 * Get basic information about the volume
457 vol->fd = open(vol->name, O_RDWR);
459 err(1, "Unable to open %s R+W", vol->name);
460 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
462 * Allow the formatting of regular files as HAMMER volumes
464 if (fstat(vol->fd, &st) < 0)
465 err(1, "Unable to stat %s", vol->name);
466 vol->size = st.st_size;
467 vol->type = "REGFILE";
470 errx(1,"Cannot TRIM regular file %s\n", vol->name);
474 * When formatting a block device as a HAMMER volume the
475 * sector size must be compatible. HAMMER uses 16384 byte
476 * filesystem buffers.
478 if (pinfo.reserved_blocks) {
479 errx(1, "HAMMER cannot be placed in a partition "
480 "which overlaps the disklabel or MBR");
482 if (pinfo.media_blksize > HAMMER_BUFSIZE ||
483 HAMMER_BUFSIZE % pinfo.media_blksize) {
484 errx(1, "A media sector size of %d is not supported",
485 pinfo.media_blksize);
488 vol->size = pinfo.media_size;
489 vol->device_offset = pinfo.media_offset;
490 vol->type = "DEVICE";
492 printf("Volume %d %s %-15s size %s\n",
493 vol->vol_no, vol->type, vol->name,
494 sizetostr(vol->size));
497 * Reserve space for (future) header junk, setup our poor-man's
498 * big-block allocator.
500 vol->vol_alloc = HAMMER_BUFSIZE * 16;
504 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
508 format_volume(struct volume_info *vol, int nvols, const char *label,
509 off_t total_size __unused)
511 struct volume_info *root_vol;
512 struct hammer_volume_ondisk *ondisk;
518 * Initialize basic information in the on-disk volume structure.
520 ondisk = vol->ondisk;
522 ondisk->vol_fsid = Hammer_FSId;
523 ondisk->vol_fstype = Hammer_FSType;
524 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
525 ondisk->vol_no = vol->vol_no;
526 ondisk->vol_count = nvols;
527 ondisk->vol_version = HammerVersion;
529 ondisk->vol_bot_beg = vol->vol_alloc;
530 vol->vol_alloc += BootAreaSize;
531 ondisk->vol_mem_beg = vol->vol_alloc;
532 vol->vol_alloc += MemAreaSize;
535 * The remaining area is the zone 2 buffer allocation area.
537 ondisk->vol_buf_beg = vol->vol_alloc;
538 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
540 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
541 errx(1, "volume %d %s is too small to hold the volume header",
542 vol->vol_no, vol->name);
545 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
547 ondisk->vol_blocksize = HAMMER_BUFSIZE;
549 ondisk->vol_rootvol = RootVolNo;
550 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
552 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
553 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
554 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
555 ~HAMMER_BIGBLOCK_MASK64);
558 * Format the root volume.
560 if (vol->vol_no == RootVolNo) {
562 * Check freemap counts before formatting
564 freeblks = count_freemap(vol);
565 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
566 if (freebytes < 10*GIG && ForceOpt == 0) {
567 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
568 "unless you use -f\n(for the size of Volume %d). "
569 "HAMMER filesystems less than 50GB are not "
570 "recommended.\n", RootVolNo);
576 ondisk->vol0_next_tid = createtid();
579 * Format freemap. vol0_stat_freebigblocks is
580 * the number of big-blocks available for anything
581 * other than freemap zone at this point.
584 assert(ondisk->vol0_stat_freebigblocks == 0);
585 ondisk->vol0_stat_freebigblocks = initialize_freemap(vol);
588 * Format zones that are mapped to zone-2.
590 for (i = HAMMER_ZONE2_MAPPED_INDEX; i < HAMMER_MAX_ZONES; ++i) {
591 format_blockmap(&ondisk->vol0_blockmap[i], i, 0);
595 * Format undo zone. Formatting decrements
596 * vol0_stat_freebigblocks whenever a new big-block
597 * is allocated for undo zone.
600 assert(ondisk->vol0_stat_bigblocks == 0);
601 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
604 * Format the root directory. Formatting decrements
605 * vol0_stat_freebigblocks whenever a new big-block
606 * is allocated for required zones.
608 ondisk->vol0_btree_root = format_root(label);
609 ++ondisk->vol0_stat_inodes; /* root inode */
613 freeblks = initialize_freemap(vol);
614 root_vol = get_volume(RootVolNo);
615 root_vol->cache.modified = 1;
616 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
617 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
618 rel_volume(root_vol);
623 * Format the root directory.
627 format_root(const char *label)
629 hammer_off_t btree_off;
630 hammer_off_t pfsd_off;
631 hammer_off_t data_off;
632 hammer_tid_t create_tid;
633 hammer_node_ondisk_t bnode;
634 struct hammer_inode_data *idata;
635 hammer_pseudofs_data_t pfsd;
636 struct buffer_info *data_buffer0 = NULL;
637 struct buffer_info *data_buffer1 = NULL;
638 struct buffer_info *data_buffer2 = NULL;
639 hammer_btree_elm_t elm;
643 * Allocate zero-filled root btree node, inode and pfs
645 bnode = alloc_btree_element(&btree_off, &data_buffer0);
646 idata = alloc_meta_element(&data_off, sizeof(*idata), &data_buffer1);
647 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
648 create_tid = createtid();
652 * Populate the inode data and inode record for the root directory.
654 idata->version = HAMMER_INODE_DATA_VERSION;
656 idata->ctime = xtime;
657 idata->mtime = xtime;
658 idata->atime = xtime;
659 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
662 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
663 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
664 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
665 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
667 pfsd->sync_low_tid = 1;
668 pfsd->sync_beg_tid = 0;
669 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
670 pfsd->shared_uuid = Hammer_FSId;
671 pfsd->unique_uuid = Hammer_FSId;
672 pfsd->reserved01 = 0;
673 pfsd->mirror_flags = 0;
674 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
677 * Create the root of the B-Tree. The root is a leaf node so we
678 * do not have to worry about boundary elements.
680 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
682 bnode->type = HAMMER_BTREE_TYPE_LEAF;
684 elm = &bnode->elms[0];
685 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
686 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
687 HAMMER_LOCALIZE_INODE;
688 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
689 elm->leaf.base.key = 0;
690 elm->leaf.base.create_tid = create_tid;
691 elm->leaf.base.delete_tid = 0;
692 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
693 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
694 elm->leaf.create_ts = (u_int32_t)time(NULL);
696 elm->leaf.data_offset = data_off;
697 elm->leaf.data_len = sizeof(*idata);
698 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
700 elm = &bnode->elms[1];
701 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
702 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION +
703 HAMMER_LOCALIZE_MISC;
704 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
705 elm->leaf.base.key = 0;
706 elm->leaf.base.create_tid = create_tid;
707 elm->leaf.base.delete_tid = 0;
708 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
709 elm->leaf.base.obj_type = 0;
710 elm->leaf.create_ts = (u_int32_t)time(NULL);
712 elm->leaf.data_offset = pfsd_off;
713 elm->leaf.data_len = sizeof(*pfsd);
714 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
716 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);