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, int pw);
41 static int trim_volume(struct volume_info *volume);
42 static void format_volume(struct volume_info *volume, int nvols,const char *label);
43 static hammer_off_t format_root_directory(const char *label);
44 static uint64_t nowtime(void);
45 static void print_volume(const struct volume_info *volume);
46 static void usage(int exit_code);
47 static void test_header_junk_size(int64_t size);
48 static void test_boot_area_size(int64_t size);
49 static void test_memory_log_size(int64_t size);
50 static void test_undo_buffer_size(int64_t size);
53 static int64_t HeaderJunkSize = -1;
54 static int64_t BootAreaSize = -1;
55 static int64_t MemoryLogSize = -1;
56 static int64_t UndoBufferSize;
58 #define GIG (1024LL*1024*1024)
61 main(int ac, char **av)
70 const char *label = NULL;
71 struct volume_info *volume;
74 * Sanity check basic filesystem structures. No cookies for us
77 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
78 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_MIN_VOL_JUNK);
79 assert(sizeof(struct hammer_blockmap_layer1) == 32);
80 assert(sizeof(struct hammer_blockmap_layer2) == 16);
85 while ((ch = getopt(ac, av, "dfEL:j:b:m:u:hC:V:")) != -1) {
99 case 'j': /* Not mentioned in newfs_hammer(8) */
100 HeaderJunkSize = getsize(optarg, 2);
101 test_header_junk_size(HeaderJunkSize);
104 BootAreaSize = getsize(optarg, 2);
105 test_boot_area_size(BootAreaSize);
108 MemoryLogSize = getsize(optarg, 2);
109 test_memory_log_size(MemoryLogSize);
112 UndoBufferSize = getsize(optarg, 2);
113 test_undo_buffer_size(UndoBufferSize);
119 if (hammer_parse_cache_size(optarg) == -1)
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 "
141 if (HammerVersion == (uint32_t)-1) {
142 size_t olen = sizeof(HammerVersion);
143 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
145 if (sysctlbyname("vfs.hammer.supported_version",
146 &HammerVersion, &olen, NULL, 0)) {
147 hwarn("HAMMER VFS not loaded, cannot get version info, "
150 } else if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
151 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
152 hwarn("HAMMER VFS supports higher version than "
153 "I understand, using version %d",
159 errx(1, "You must specify at least one special file (volume)");
160 if (nvols > HAMMER_MAX_VOLUMES)
161 errx(1, "The maximum number of volumes is %d",
165 hwarnx("A filesystem label must be specified");
170 * Generate a filesystem id and lookup the filesystem type
172 uuidgen(&Hammer_FSId, 1);
173 uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING, &status);
174 if (status != uuid_s_ok) {
175 errx(1, "uuids file does not have the DragonFly "
176 "HAMMER filesystem type");
180 for (i = 0; i < nvols; ++i) {
181 volume = init_volume(av[i], O_RDWR, i);
182 printf("Volume %d %s %-15s size %s\n",
183 volume->vol_no, volume->type, volume->name,
184 sizetostr(volume->size));
187 if (trim_volume(volume) == -1 && ForceOpt == 0)
188 errx(1, "Use -f option to proceed");
190 total += volume->size;
194 * Reserve space for (future) header junk, setup our poor-man's
195 * big-block allocator. Note that the header junk space includes
196 * volume header which is 1928 bytes.
198 if (HeaderJunkSize == -1)
199 HeaderJunkSize = HAMMER_VOL_JUNK_SIZE;
200 else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk))
201 HeaderJunkSize = sizeof(struct hammer_volume_ondisk);
202 HeaderJunkSize = HAMMER_BUFSIZE_DOALIGN(HeaderJunkSize);
205 * Calculate defaults for the boot area and memory log sizes,
206 * only if not specified by -b or -m option.
208 avg_vol_size = total / nvols;
209 if (BootAreaSize == -1)
210 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
211 if (MemoryLogSize == -1)
212 MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size);
215 * Format the volumes. Format the root volume first so we can
216 * bootstrap the freemap.
218 format_volume(get_root_volume(), nvols, label);
219 for (i = 0; i < nvols; ++i) {
220 if (i != HAMMER_ROOT_VOLNO)
221 format_volume(get_volume(i), nvols, label);
224 print_volume(get_root_volume());
232 print_volume(const struct volume_info *volume)
234 hammer_volume_ondisk_t ondisk;
235 hammer_blockmap_t blockmap;
236 hammer_off_t total = 0;
239 const char *name = NULL;
242 ondisk = volume->ondisk;
243 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
245 nvols = ondisk->vol_count;
246 for (i = 0; i < nvols; ++i) {
247 struct volume_info *p = get_volume(i);
249 if (p->vol_no == HAMMER_ROOT_VOLNO) {
250 assert(name == NULL);
255 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
257 printf("---------------------------------------------\n");
258 printf("HAMMER version %d\n", HammerVersion);
259 printf("%d volume%s total size %s\n",
260 nvols, (nvols == 1 ? "" : "s"), sizetostr(total));
261 printf("root-volume: %s\n", name);
263 printf("header-junk-size: %s\n",
264 sizetostr(ondisk->vol_bot_beg));
265 printf("boot-area-size: %s\n",
266 sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg));
267 printf("memory-log-size: %s\n",
268 sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg));
269 printf("undo-buffer-size: %s\n",
270 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset)));
271 printf("total-pre-allocated: %s\n",
272 sizetostr(HAMMER_OFF_SHORT_ENCODE(volume->vol_free_off)));
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 %s, "
283 "you really should not\n"
284 "try to format a HAMMER filesystem this small.\n",
285 sizetostr(HAMMER_BIGBLOCK_SIZE *
286 HAMMER_MIN_UNDO_BIGBLOCKS));
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");
302 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
303 " [-C cachesize[:readahead]] [-V version] special ...\n"
309 test_header_junk_size(int64_t size)
311 if (size < HAMMER_MIN_VOL_JUNK) {
313 errx(1, "The minimum header junk size is %s",
314 sizetostr(HAMMER_MIN_VOL_JUNK));
316 hwarnx("You have specified header junk size less than %s",
317 sizetostr(HAMMER_MIN_VOL_JUNK));
319 } else if (size > HAMMER_MAX_VOL_JUNK) {
320 errx(1, "The maximum header junk size is %s",
321 sizetostr(HAMMER_MAX_VOL_JUNK));
326 test_boot_area_size(int64_t size)
328 if (size < HAMMER_BOOT_MINBYTES) {
330 errx(1, "The minimum boot area size is %s",
331 sizetostr(HAMMER_BOOT_MINBYTES));
333 hwarnx("You have specified boot area size less than %s",
334 sizetostr(HAMMER_BOOT_MINBYTES));
336 } else if (size > HAMMER_BOOT_MAXBYTES) {
337 errx(1, "The maximum boot area size is %s",
338 sizetostr(HAMMER_BOOT_MAXBYTES));
343 test_memory_log_size(int64_t size)
345 if (size < HAMMER_MEM_MINBYTES) {
347 errx(1, "The minimum memory log size is %s",
348 sizetostr(HAMMER_MEM_MINBYTES));
350 hwarnx("You have specified memory log size less than %s",
351 sizetostr(HAMMER_MEM_MINBYTES));
353 } else if (size > HAMMER_MEM_MAXBYTES) {
354 errx(1, "The maximum memory log size is %s",
355 sizetostr(HAMMER_MEM_MAXBYTES));
360 test_undo_buffer_size(int64_t size)
362 int64_t minbuf, maxbuf;
364 minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
365 maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
369 errx(1, "The minimum UNDO/REDO FIFO size is %s",
372 hwarnx("You have specified an UNDO/REDO FIFO size less "
373 "than %s, which may lead to VFS panics",
376 } else if (size > maxbuf) {
377 errx(1, "The maximum UNDO/REDO FIFO size is %s",
383 * Convert a string to a 64 bit signed integer with various requirements.
386 getsize(const char *str, int powerof2)
391 val = strtoll(str, &ptr, 0);
410 errx(1, "Unknown suffix in number '%s'", str);
415 errx(1, "Unknown suffix in number '%s'", str);
418 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
419 errx(1, "Value not power of 2: %s", str);
422 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
423 errx(1, "Value not an integral multiple of %dK: %s",
424 HAMMER_BUFSIZE / 1024, str);
431 * Generate a transaction id. Transaction ids are no longer time-based.
432 * Put the nail in the coffin by not making the first one time-based.
434 * We could start at 1 here but start at 2^32 to reserve a small domain for
435 * possible future use.
440 static hammer_tid_t lasttid;
443 lasttid = 0x0000000100000000ULL;
453 gettimeofday(&tv, NULL);
454 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
459 * TRIM the volume, but only if the backing store is a DEVICE
463 trim_volume(struct volume_info *volume)
467 char sysctl_name[64];
471 if (strcmp(volume->type, "DEVICE")) {
472 hwarnx("Cannot TRIM regular file %s", volume->name);
475 if (strncmp(volume->name, "/dev/da", 7)) {
476 hwarnx("%s does not support the TRIM command", volume->name);
480 /* Extract a number from /dev/da?s? */
481 dev_name = strdup(volume->name);
482 p = strtok(dev_name + strlen("/dev/da"), "s");
483 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
487 olen = sizeof(trim_enabled);
489 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
490 hwarnx("%s (%s) does not support the TRIM command",
491 volume->name, sysctl_name);
495 hwarnx("Erase device option selected, but sysctl (%s) "
496 "is not enabled", sysctl_name);
500 ioarg[0] = volume->device_offset;
501 ioarg[1] = volume->size;
503 printf("Trimming %s %s, sectors %llu-%llu\n",
504 volume->type, volume->name,
505 (unsigned long long)ioarg[0] / 512,
506 (unsigned long long)ioarg[1] / 512);
508 if (ioctl(volume->fd, IOCTLTRIM, ioarg) == -1)
509 err(1, "Trimming %s failed", volume->name);
515 * Format a HAMMER volume.
519 format_volume(struct volume_info *volume, int nvols, const char *label)
521 struct volume_info *root_vol;
522 hammer_volume_ondisk_t ondisk;
525 int64_t vol_buf_size;
526 hammer_off_t vol_alloc;
530 * Initialize basic information in the on-disk volume structure.
532 ondisk = volume->ondisk;
534 ondisk->vol_fsid = Hammer_FSId;
535 ondisk->vol_fstype = Hammer_FSType;
536 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
537 ondisk->vol_no = volume->vol_no;
538 ondisk->vol_count = nvols;
539 ondisk->vol_version = HammerVersion;
541 vol_alloc = HeaderJunkSize;
542 ondisk->vol_bot_beg = vol_alloc;
543 vol_alloc += BootAreaSize;
544 ondisk->vol_mem_beg = vol_alloc;
545 vol_alloc += MemoryLogSize;
548 * The remaining area is the zone 2 buffer allocation area.
550 ondisk->vol_buf_beg = vol_alloc;
551 ondisk->vol_buf_end = volume->size & ~(int64_t)HAMMER_BUFMASK;
552 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
554 if (vol_buf_size < (int64_t)sizeof(*ondisk)) {
555 errx(1, "volume %d %s is too small to hold the volume header",
556 volume->vol_no, volume->name);
558 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0)
559 errx(1, "volume %d %s is too large", volume->vol_no, volume->name);
561 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
562 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
564 volume->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
565 volume->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no,
566 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
569 * Format the root volume.
571 if (volume->vol_no == HAMMER_ROOT_VOLNO) {
573 * Check freemap counts before formatting
575 freeblks = count_freemap(volume);
576 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
577 if (freebytes < 10*GIG && ForceOpt == 0) {
578 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
579 "unless you use -f\n(for the size of Volume %d). "
580 "HAMMER filesystems less than 50GB are not "
581 "recommended.", HAMMER_ROOT_VOLNO);
587 ondisk->vol0_next_tid = createtid();
590 * Format freemap. vol0_stat_freebigblocks is
591 * the number of big-blocks available for anything
592 * other than freemap zone at this point.
594 format_freemap(volume);
595 assert(ondisk->vol0_stat_freebigblocks == 0);
596 ondisk->vol0_stat_freebigblocks = initialize_freemap(volume);
599 * Format zones that are mapped to zone-2.
601 for (i = 0; i < HAMMER_MAX_ZONES; ++i) {
602 if (hammer_is_index_record(i))
603 format_blockmap(volume, i, 0);
607 * Format undo zone. Formatting decrements
608 * vol0_stat_freebigblocks whenever a new big-block
609 * is allocated for undo zone.
611 format_undomap(volume, &UndoBufferSize);
612 assert(ondisk->vol0_stat_bigblocks == 0);
613 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
616 * Format the root directory. Formatting decrements
617 * vol0_stat_freebigblocks whenever a new big-block
618 * is allocated for required zones.
620 ondisk->vol0_btree_root = format_root_directory(label);
621 ++ondisk->vol0_stat_inodes; /* root inode */
623 freeblks = initialize_freemap(volume);
624 root_vol = get_root_volume();
625 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
626 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
631 * Format the root directory.
635 format_root_directory(const char *label)
637 hammer_off_t btree_off;
638 hammer_off_t idata_off;
639 hammer_off_t pfsd_off;
640 hammer_tid_t create_tid;
641 hammer_node_ondisk_t bnode;
642 hammer_inode_data_t idata;
643 hammer_pseudofs_data_t pfsd;
644 struct buffer_info *data_buffer0 = NULL;
645 struct buffer_info *data_buffer1 = NULL;
646 struct buffer_info *data_buffer2 = NULL;
647 hammer_btree_elm_t elm;
651 * Allocate zero-filled root btree node, inode and pfs
653 bnode = alloc_btree_node(&btree_off, &data_buffer0);
654 idata = alloc_meta_element(&idata_off, sizeof(*idata), &data_buffer1);
655 pfsd = alloc_meta_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
656 create_tid = createtid();
660 * Populate the inode data and inode record for the root directory.
662 idata->version = HAMMER_INODE_DATA_VERSION;
664 idata->ctime = xtime;
665 idata->mtime = xtime;
666 idata->atime = xtime;
667 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
670 if (HammerVersion >= HAMMER_VOL_VERSION_TWO)
671 idata->cap_flags |= HAMMER_INODE_CAP_DIR_LOCAL_INO;
672 if (HammerVersion >= HAMMER_VOL_VERSION_SIX)
673 idata->cap_flags |= HAMMER_INODE_CAP_DIRHASH_ALG1;
676 * Populate the PFS data for the root PFS.
678 pfsd->sync_low_tid = 1;
679 pfsd->sync_beg_tid = 0;
680 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on root PFS */
681 pfsd->shared_uuid = Hammer_FSId;
682 pfsd->unique_uuid = Hammer_FSId;
683 pfsd->mirror_flags = 0;
684 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
687 * Create the root of the B-Tree. The root is a leaf node so we
688 * do not have to worry about boundary elements.
690 bnode->parent = 0; /* no parent */
692 bnode->type = HAMMER_BTREE_TYPE_LEAF;
693 bnode->mirror_tid = 0;
696 * Create the first node element for the inode.
698 elm = &bnode->elms[0];
699 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
700 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
701 HAMMER_LOCALIZE_INODE;
702 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
703 elm->leaf.base.key = 0;
704 elm->leaf.base.create_tid = create_tid;
705 elm->leaf.base.delete_tid = 0;
706 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
707 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
708 elm->leaf.create_ts = (uint32_t)time(NULL);
710 elm->leaf.data_offset = idata_off;
711 elm->leaf.data_len = sizeof(*idata);
712 hammer_crc_set_leaf(HammerVersion, idata, &elm->leaf);
715 * Create the second node element for the PFS data.
716 * This is supposed to be a record part of the root ip (inode),
717 * so it should have the same obj_type value as above.
719 elm = &bnode->elms[1];
720 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
721 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
722 HAMMER_LOCALIZE_MISC;
723 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
724 elm->leaf.base.key = 0;
725 elm->leaf.base.create_tid = create_tid;
726 elm->leaf.base.delete_tid = 0;
727 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
728 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
729 elm->leaf.create_ts = (uint32_t)time(NULL);
731 elm->leaf.data_offset = pfsd_off;
732 elm->leaf.data_len = sizeof(*pfsd);
733 hammer_crc_set_leaf(HammerVersion, pfsd, &elm->leaf);
735 hammer_crc_set_btree(HammerVersion, bnode);
737 rel_buffer(data_buffer0);
738 rel_buffer(data_buffer1);
739 rel_buffer(data_buffer2);