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
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.44 2008/08/21 23:32:27 thomas Exp $
37 #include "newfs_hammer.h"
39 static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
40 static const char *sizetostr(off_t size);
41 static void trim_volume(struct volume_info *vol);
42 static void check_volume(struct volume_info *vol);
43 static void format_volume(struct volume_info *vol, int nvols,const char *label,
45 static hammer_off_t format_root(const char *label);
46 static u_int64_t nowtime(void);
47 static void usage(void);
49 static int ForceOpt = 0;
50 static int HammerVersion = -1;
53 #define GIG (1024LL*1024*1024)
56 main(int ac, char **av)
62 const char *label = NULL;
63 struct volume_info *vol;
67 * Sanity check basic filesystem structures. No cookies for us
70 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
71 assert(sizeof(struct hammer_blockmap_layer1) == 32);
72 assert(sizeof(struct hammer_blockmap_layer2) == 16);
75 * Generate a filesystem id and lookup the filesystem type
77 uuidgen(&Hammer_FSId, 1);
78 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
79 if (status != uuid_s_ok) {
80 errx(1, "uuids file does not have the DragonFly "
81 "HAMMER filesystem type");
87 while ((ch = getopt(ac, av, "fEL:b:m:u:V:")) != -1) {
99 BootAreaSize = getsize(optarg,
101 HAMMER_BOOT_MAXBYTES, 2);
104 MemAreaSize = getsize(optarg,
106 HAMMER_MEM_MAXBYTES, 2);
109 UndoBufferSize = getsize(optarg,
110 HAMMER_LARGEBLOCK_SIZE,
111 HAMMER_LARGEBLOCK_SIZE *
112 HAMMER_UNDO_LAYER2, 2);
113 if (UndoBufferSize < 500*1024*1024 && ForceOpt == 0)
114 errx(1, "The minimum UNDO/REDO FIFO size is "
116 if (UndoBufferSize < 500*1024*1024) {
118 "WARNING: you have specified an "
119 "UNDO/REDO FIFO size less than 500MB,\n"
120 "which may lead to VFS panics.\n");
124 HammerVersion = strtol(optarg, NULL, 0);
125 if (HammerVersion < HAMMER_VOL_VERSION_MIN ||
126 HammerVersion >= HAMMER_VOL_VERSION_WIP) {
128 "I don't understand how to format "
129 "HAMMER version %d\n",
141 "newfs_hammer: A filesystem label must be specified\n");
145 if (HammerVersion < 0) {
146 size_t olen = sizeof(HammerVersion);
147 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
148 if (sysctlbyname("vfs.hammer.supported_version",
149 &HammerVersion, &olen, NULL, 0) == 0) {
150 if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
151 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
153 "newfs_hammer: WARNING: HAMMER VFS "
154 "supports higher version than I "
156 "using version %d\n",
161 "newfs_hammer: WARNING: HAMMER VFS not "
162 "loaded, cannot get version info.\n"
163 "Using version %d\n",
164 HAMMER_VOL_VERSION_DEFAULT);
169 * Collect volume information
176 if (NumVolumes == 0) {
178 "newfs_hammer: You must specify at least one special file (volume)\n");
182 if (NumVolumes > HAMMER_MAX_VOLUMES) {
184 "newfs_hammer: The maximum number of volumes is %d\n", HAMMER_MAX_VOLUMES);
189 for (i = 0; i < NumVolumes; ++i) {
190 vol = setup_volume(i, av[i], 1, O_RDWR);
193 * Load up information on the volume and initialize
194 * its remaining fields.
198 char sysctl_name[64];
199 int trim_enabled = 0;
200 size_t olen = sizeof(trim_enabled);
201 char *dev_name = strdup(vol->name);
202 dev_name = strtok(dev_name + strlen("/dev/da"),"s");
204 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled",
207 sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0);
208 if(errno == ENOENT) {
209 printf("Device:%s (%s) does not support the "
210 "TRIM command\n", vol->name,sysctl_name);
214 printf("Erase device option selected, but "
215 "sysctl (%s) is not enabled\n", sysctl_name);
225 * Calculate defaults for the boot and memory area sizes.
227 if (BootAreaSize == 0) {
228 BootAreaSize = HAMMER_BOOT_NOMBYTES;
229 while (BootAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
231 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
233 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
234 BootAreaSize = HAMMER_BOOT_MINBYTES;
236 if (MemAreaSize == 0) {
237 MemAreaSize = HAMMER_MEM_NOMBYTES;
238 while (MemAreaSize > total / NumVolumes / HAMMER_MAX_VOLUMES)
240 if (MemAreaSize < HAMMER_MEM_MINBYTES)
242 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
243 MemAreaSize = HAMMER_MEM_MINBYTES;
247 * Format the volumes. Format the root volume first so we can
248 * bootstrap the freemap.
250 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
251 for (i = 0; i < NumVolumes; ++i) {
253 format_volume(get_volume(i), NumVolumes, label, total);
257 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
258 * limit. If we do this the filesystem does not have to allocate
259 * new layer2 blocks which reduces the chances of the reblocker
260 * having to fallback to an extremely inefficient algorithm.
262 vol = get_volume(RootVolNo);
263 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
264 vol->cache.modified = 1;
265 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
267 printf("---------------------------------------------\n");
268 printf("%d volume%s total size %s version %d\n",
269 NumVolumes, (NumVolumes == 1 ? "" : "s"),
270 sizetostr(total), HammerVersion);
271 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
272 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
273 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
274 printf("total-pre-allocated: %s\n",
275 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
276 printf("fsid: %s\n", fsidstr);
278 printf("NOTE: Please remember that you may have to manually set up a\n"
279 "cron(8) job to prune and reblock the filesystem regularly.\n"
280 "By default, the system automatically runs 'hammer cleanup'\n"
281 "on a nightly basis. The periodic.conf(5) variable\n"
282 "'daily_clean_hammer_enable' can be unset to disable this.\n"
283 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
284 if (total < 10*GIG) {
285 printf("\nWARNING: The minimum UNDO/REDO FIFO is 500MB, you "
286 "really should not\n"
287 "try to format a HAMMER filesystem this small.\n");
289 if (total < 50*GIG) {
290 printf("\nWARNING: HAMMER filesystems less than 50GB are "
292 "You may have to run 'hammer prune-everything' and "
294 "quite often, even if using a nohistory mount.\n");
305 "usage: newfs_hammer -L label [-fE] [-b bootsize] [-m savesize] [-u undosize]\n"
306 " [-V version] special ...\n"
312 * Convert the size in bytes to a human readable string.
316 sizetostr(off_t size)
320 if (size < 1024 / 2) {
321 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
322 } else if (size < 1024 * 1024 / 2) {
323 snprintf(buf, sizeof(buf), "%6.2fKB",
324 (double)size / 1024);
325 } else if (size < 1024 * 1024 * 1024LL / 2) {
326 snprintf(buf, sizeof(buf), "%6.2fMB",
327 (double)size / (1024 * 1024));
328 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
329 snprintf(buf, sizeof(buf), "%6.2fGB",
330 (double)size / (1024 * 1024 * 1024LL));
332 snprintf(buf, sizeof(buf), "%6.2fTB",
333 (double)size / (1024 * 1024 * 1024LL * 1024LL));
339 * Convert a string to a 64 bit signed integer with various requirements.
342 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
347 val = strtoll(str, &ptr, 0);
366 errx(1, "Unknown suffix in number '%s'\n", str);
370 errx(1, "Unknown suffix in number '%s'\n", str);
374 errx(1, "Value too small: %s, min is %s\n",
375 str, sizetostr(minval));
379 errx(1, "Value too large: %s, max is %s\n",
380 str, sizetostr(maxval));
383 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
384 errx(1, "Value not power of 2: %s\n", str);
387 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
388 errx(1, "Value not an integral multiple of %dK: %s",
389 HAMMER_BUFSIZE / 1024, str);
396 * Generate a transaction id. Transaction ids are no longer time-based.
397 * Put the nail in the coffin by not making the first one time-based.
399 * We could start at 1 here but start at 2^32 to reserve a small domain for
400 * possible future use.
405 static hammer_tid_t lasttid;
408 lasttid = 0x0000000100000000ULL;
418 gettimeofday(&tv, NULL);
419 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
424 * TRIM the volume, but only if the backing store is a DEVICE
428 trim_volume(struct volume_info *vol)
430 if (strncmp(vol->type, "DEVICE", sizeof(vol->type)) == 0) {
433 /* 1MB offset to prevent destroying disk-reserved area */
434 ioarg[0] = vol->device_offset;
435 ioarg[1] = vol->size;
436 printf("Trimming Device:%s, sectors (%llu -%llu)\n",vol->name,
437 (unsigned long long)ioarg[0]/512,
438 (unsigned long long)ioarg[1]/512);
439 if (ioctl(vol->fd, IOCTLTRIM, ioarg) < 0) {
440 printf("Device trim failed\n");
447 * Check basic volume characteristics. HAMMER filesystems use a minimum
448 * of a 16KB filesystem buffer size.
452 check_volume(struct volume_info *vol)
454 struct partinfo pinfo;
458 * Get basic information about the volume
460 vol->fd = open(vol->name, O_RDWR);
462 err(1, "Unable to open %s R+W", vol->name);
463 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
465 * Allow the formatting of regular files as HAMMER volumes
467 if (fstat(vol->fd, &st) < 0)
468 err(1, "Unable to stat %s", vol->name);
469 vol->size = st.st_size;
470 vol->type = "REGFILE";
473 errx(1,"Cannot TRIM regular file %s\n", vol->name);
477 * When formatting a block device as a HAMMER volume the
478 * sector size must be compatible. HAMMER uses 16384 byte
479 * filesystem buffers.
481 if (pinfo.reserved_blocks) {
482 errx(1, "HAMMER cannot be placed in a partition "
483 "which overlaps the disklabel or MBR");
485 if (pinfo.media_blksize > 16384 ||
486 16384 % pinfo.media_blksize) {
487 errx(1, "A media sector size of %d is not supported",
488 pinfo.media_blksize);
491 vol->size = pinfo.media_size;
492 vol->device_offset = pinfo.media_offset;
493 vol->type = "DEVICE";
495 printf("Volume %d %s %-15s size %s\n",
496 vol->vol_no, vol->type, vol->name,
497 sizetostr(vol->size));
500 * Reserve space for (future) header junk, setup our poor-man's
501 * bigblock allocator.
503 vol->vol_alloc = HAMMER_BUFSIZE * 16;
507 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
511 format_volume(struct volume_info *vol, int nvols, const char *label,
512 off_t total_size __unused)
514 struct volume_info *root_vol;
515 struct hammer_volume_ondisk *ondisk;
521 * Initialize basic information in the on-disk volume structure.
523 ondisk = vol->ondisk;
525 ondisk->vol_fsid = Hammer_FSId;
526 ondisk->vol_fstype = Hammer_FSType;
527 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
528 ondisk->vol_no = vol->vol_no;
529 ondisk->vol_count = nvols;
530 ondisk->vol_version = HammerVersion;
532 ondisk->vol_bot_beg = vol->vol_alloc;
533 vol->vol_alloc += BootAreaSize;
534 ondisk->vol_mem_beg = vol->vol_alloc;
535 vol->vol_alloc += MemAreaSize;
538 * The remaining area is the zone 2 buffer allocation area. These
541 ondisk->vol_buf_beg = vol->vol_alloc;
542 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
544 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
545 errx(1, "volume %d %s is too small to hold the volume header",
546 vol->vol_no, vol->name);
549 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
551 ondisk->vol_blocksize = HAMMER_BUFSIZE;
553 ondisk->vol_rootvol = RootVolNo;
554 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
556 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
557 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no,
558 (ondisk->vol_buf_end - ondisk->vol_buf_beg) &
559 ~HAMMER_LARGEBLOCK_MASK64);
562 * Format the root volume.
564 if (vol->vol_no == RootVolNo) {
568 ondisk->vol0_next_tid = createtid();
571 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
573 freeblks = initialize_freemap(vol);
574 ondisk->vol0_stat_freebigblocks = freeblks;
576 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
577 if (freebytes < 10*GIG && ForceOpt == 0) {
578 errx(1, "Cannot create a HAMMER filesystem less than "
579 "10GB unless you use -f. HAMMER filesystems\n"
580 "less than 50GB are not recommended\n");
583 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
584 format_blockmap(&ondisk->vol0_blockmap[i],
585 HAMMER_ZONE_ENCODE(i, 0));
587 format_undomap(ondisk);
589 ondisk->vol0_btree_root = format_root(label);
590 ++ondisk->vol0_stat_inodes; /* root inode */
592 freeblks = initialize_freemap(vol);
593 root_vol = get_volume(RootVolNo);
594 root_vol->cache.modified = 1;
595 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
596 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
597 rel_volume(root_vol);
602 * Format the root directory.
606 format_root(const char *label)
608 hammer_off_t btree_off;
609 hammer_off_t pfsd_off;
610 hammer_off_t data_off;
611 hammer_tid_t create_tid;
612 hammer_node_ondisk_t bnode;
613 struct hammer_inode_data *idata;
614 hammer_pseudofs_data_t pfsd;
615 struct buffer_info *data_buffer1 = NULL;
616 struct buffer_info *data_buffer2 = NULL;
617 hammer_btree_elm_t elm;
620 bnode = alloc_btree_element(&btree_off);
621 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
622 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
623 create_tid = createtid();
627 * Populate the inode data and inode record for the root directory.
629 idata->version = HAMMER_INODE_DATA_VERSION;
631 idata->ctime = xtime;
632 idata->mtime = xtime;
633 idata->atime = xtime;
634 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
637 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
638 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
639 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
640 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
642 pfsd->sync_low_tid = 1;
643 pfsd->sync_beg_tid = 0;
644 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
645 pfsd->shared_uuid = Hammer_FSId;
646 pfsd->unique_uuid = Hammer_FSId;
647 pfsd->reserved01 = 0;
648 pfsd->mirror_flags = 0;
649 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
652 * Create the root of the B-Tree. The root is a leaf node so we
653 * do not have to worry about boundary elements.
655 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
657 bnode->type = HAMMER_BTREE_TYPE_LEAF;
659 elm = &bnode->elms[0];
660 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
661 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
662 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
663 elm->leaf.base.key = 0;
664 elm->leaf.base.create_tid = create_tid;
665 elm->leaf.base.delete_tid = 0;
666 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
667 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
668 elm->leaf.create_ts = (u_int32_t)time(NULL);
670 elm->leaf.data_offset = data_off;
671 elm->leaf.data_len = sizeof(*idata);
672 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
674 elm = &bnode->elms[1];
675 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
676 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
677 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
678 elm->leaf.base.key = 0;
679 elm->leaf.base.create_tid = create_tid;
680 elm->leaf.base.delete_tid = 0;
681 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
682 elm->leaf.base.obj_type = 0;
683 elm->leaf.create_ts = (u_int32_t)time(NULL);
685 elm->leaf.data_offset = pfsd_off;
686 elm->leaf.data_len = sizeof(*pfsd);
687 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
689 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);