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_LARGEBLOCK_SIZE,
109 HAMMER_LARGEBLOCK_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->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
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 NumVolumes, (NumVolumes == 1 ? "" : "s"),
268 sizetostr(total), HammerVersion);
269 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
270 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
271 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
272 printf("total-pre-allocated: %s\n",
273 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
274 printf("fsid: %s\n", fsidstr);
276 printf("NOTE: Please remember that you may have to manually set up a\n"
277 "cron(8) job to prune and reblock the filesystem regularly.\n"
278 "By default, the system automatically runs 'hammer cleanup'\n"
279 "on a nightly basis. The periodic.conf(5) variable\n"
280 "'daily_clean_hammer_enable' can be unset to disable this.\n"
281 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
282 if (total < 10*GIG) {
283 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
284 "really should not\n"
285 "try to format a HAMMER filesystem this small.\n");
287 if (total < 50*GIG) {
288 printf("\nWARNING: HAMMER filesystems less than 50GB are "
290 "You may have to run 'hammer prune-everything' and "
292 "quite often, even if using a nohistory mount.\n");
303 "usage: newfs_hammer -L label [-Ef] [-b bootsize] [-m savesize] [-u undosize]\n"
304 " [-V version] special ...\n"
310 * Convert the size in bytes to a human readable string.
314 sizetostr(off_t size)
318 if (size < 1024 / 2) {
319 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
320 } else if (size < 1024 * 1024 / 2) {
321 snprintf(buf, sizeof(buf), "%6.2fKB",
322 (double)size / 1024);
323 } else if (size < 1024 * 1024 * 1024LL / 2) {
324 snprintf(buf, sizeof(buf), "%6.2fMB",
325 (double)size / (1024 * 1024));
326 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
327 snprintf(buf, sizeof(buf), "%6.2fGB",
328 (double)size / (1024 * 1024 * 1024LL));
330 snprintf(buf, sizeof(buf), "%6.2fTB",
331 (double)size / (1024 * 1024 * 1024LL * 1024LL));
337 * Convert a string to a 64 bit signed integer with various requirements.
340 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
345 val = strtoll(str, &ptr, 0);
364 errx(1, "Unknown suffix in number '%s'\n", str);
368 errx(1, "Unknown suffix in number '%s'\n", str);
372 errx(1, "Value too small: %s, min is %s\n",
373 str, sizetostr(minval));
377 errx(1, "Value too large: %s, max is %s\n",
378 str, sizetostr(maxval));
381 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
382 errx(1, "Value not power of 2: %s\n", str);
385 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
386 errx(1, "Value not an integral multiple of %dK: %s",
387 HAMMER_BUFSIZE / 1024, str);
394 * Generate a transaction id. Transaction ids are no longer time-based.
395 * Put the nail in the coffin by not making the first one time-based.
397 * We could start at 1 here but start at 2^32 to reserve a small domain for
398 * possible future use.
403 static hammer_tid_t lasttid;
406 lasttid = 0x0000000100000000ULL;
416 gettimeofday(&tv, NULL);
417 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
422 * TRIM the volume, but only if the backing store is a DEVICE
426 trim_volume(struct volume_info *vol)
428 if (strncmp(vol->type, "DEVICE", sizeof(vol->type)) == 0) {
431 /* 1MB offset to prevent destroying disk-reserved area */
432 ioarg[0] = vol->device_offset;
433 ioarg[1] = vol->size;
434 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
435 (unsigned long long)ioarg[0]/512,
436 (unsigned long long)ioarg[1]/512);
437 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
438 printf("Device trim failed\n");
445 * Check basic volume characteristics. HAMMER filesystems use a minimum
446 * of a 16KB filesystem buffer size.
450 check_volume(struct volume_info *vol)
452 struct partinfo pinfo;
456 * Get basic information about the volume
458 vol->fd = open(vol->name, O_RDWR);
460 err(1, "Unable to open %s R+W", vol->name);
461 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
463 * Allow the formatting of regular files as HAMMER volumes
465 if (fstat(vol->fd, &st) < 0)
466 err(1, "Unable to stat %s", vol->name);
467 vol->size = st.st_size;
468 vol->type = "REGFILE";
471 errx(1,"Cannot TRIM regular file %s\n", vol->name);
475 * When formatting a block device as a HAMMER volume the
476 * sector size must be compatible. HAMMER uses 16384 byte
477 * filesystem buffers.
479 if (pinfo.reserved_blocks) {
480 errx(1, "HAMMER cannot be placed in a partition "
481 "which overlaps the disklabel or MBR");
483 if (pinfo.media_blksize > 16384 ||
484 16384 % pinfo.media_blksize) {
485 errx(1, "A media sector size of %d is not supported",
486 pinfo.media_blksize);
489 vol->size = pinfo.media_size;
490 vol->device_offset = pinfo.media_offset;
491 vol->type = "DEVICE";
493 printf("Volume %d %s %-15s size %s\n",
494 vol->vol_no, vol->type, vol->name,
495 sizetostr(vol->size));
498 * Reserve space for (future) header junk, setup our poor-man's
499 * bigblock allocator.
501 vol->vol_alloc = HAMMER_BUFSIZE * 16;
505 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
509 format_volume(struct volume_info *vol, int nvols, const char *label,
510 off_t total_size __unused)
512 struct volume_info *root_vol;
513 struct hammer_volume_ondisk *ondisk;
519 * Initialize basic information in the on-disk volume structure.
521 ondisk = vol->ondisk;
523 ondisk->vol_fsid = Hammer_FSId;
524 ondisk->vol_fstype = Hammer_FSType;
525 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
526 ondisk->vol_no = vol->vol_no;
527 ondisk->vol_count = nvols;
528 ondisk->vol_version = HammerVersion;
530 ondisk->vol_bot_beg = vol->vol_alloc;
531 vol->vol_alloc += BootAreaSize;
532 ondisk->vol_mem_beg = vol->vol_alloc;
533 vol->vol_alloc += MemAreaSize;
536 * The remaining area is the zone 2 buffer allocation area. These
539 ondisk->vol_buf_beg = vol->vol_alloc;
540 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
542 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
543 errx(1, "volume %d %s is too small to hold the volume header",
544 vol->vol_no, vol->name);
547 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
549 ondisk->vol_blocksize = HAMMER_BUFSIZE;
551 ondisk->vol_rootvol = RootVolNo;
552 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
554 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
555 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
556 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
557 ~HAMMER_LARGEBLOCK_MASK64);
560 * Format the root volume.
562 if (vol->vol_no == RootVolNo) {
566 ondisk->vol0_next_tid = createtid();
569 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
571 freeblks = initialize_freemap(vol);
572 ondisk->vol0_stat_freebigblocks = freeblks;
574 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
575 if (freebytes < 10*GIG && ForceOpt == 0) {
576 errx(1, "Cannot create a HAMMER filesystem less than "
577 "10GB unless you use -f. HAMMER filesystems\n"
578 "less than 50GB are not recommended\n");
581 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
582 format_blockmap(&ondisk->vol0_blockmap[i],
583 HAMMER_ZONE_ENCODE(i, 0));
585 format_undomap(ondisk);
587 ondisk->vol0_btree_root = format_root(label);
588 ++ondisk->vol0_stat_inodes; /* root inode */
590 freeblks = initialize_freemap(vol);
591 root_vol = get_volume(RootVolNo);
592 root_vol->cache.modified = 1;
593 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
594 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
595 rel_volume(root_vol);
600 * Format the root directory.
604 format_root(const char *label)
606 hammer_off_t btree_off;
607 hammer_off_t pfsd_off;
608 hammer_off_t data_off;
609 hammer_tid_t create_tid;
610 hammer_node_ondisk_t bnode;
611 struct hammer_inode_data *idata;
612 hammer_pseudofs_data_t pfsd;
613 struct buffer_info *data_buffer1 = NULL;
614 struct buffer_info *data_buffer2 = NULL;
615 hammer_btree_elm_t elm;
618 bnode = alloc_btree_element(&btree_off);
619 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
620 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
621 create_tid = createtid();
625 * Populate the inode data and inode record for the root directory.
627 idata->version = HAMMER_INODE_DATA_VERSION;
629 idata->ctime = xtime;
630 idata->mtime = xtime;
631 idata->atime = xtime;
632 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
635 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
636 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
637 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
638 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
640 pfsd->sync_low_tid = 1;
641 pfsd->sync_beg_tid = 0;
642 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
643 pfsd->shared_uuid = Hammer_FSId;
644 pfsd->unique_uuid = Hammer_FSId;
645 pfsd->reserved01 = 0;
646 pfsd->mirror_flags = 0;
647 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
650 * Create the root of the B-Tree. The root is a leaf node so we
651 * do not have to worry about boundary elements.
653 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
655 bnode->type = HAMMER_BTREE_TYPE_LEAF;
657 elm = &bnode->elms[0];
658 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
659 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
660 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
661 elm->leaf.base.key = 0;
662 elm->leaf.base.create_tid = create_tid;
663 elm->leaf.base.delete_tid = 0;
664 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
665 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
666 elm->leaf.create_ts = (u_int32_t)time(NULL);
668 elm->leaf.data_offset = data_off;
669 elm->leaf.data_len = sizeof(*idata);
670 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
672 elm = &bnode->elms[1];
673 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
674 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
675 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
676 elm->leaf.base.key = 0;
677 elm->leaf.base.create_tid = create_tid;
678 elm->leaf.base.delete_tid = 0;
679 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
680 elm->leaf.base.obj_type = 0;
681 elm->leaf.create_ts = (u_int32_t)time(NULL);
683 elm->leaf.data_offset = pfsd_off;
684 elm->leaf.data_len = sizeof(*pfsd);
685 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
687 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);