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 "
140 if (HammerVersion == (uint32_t)-1) {
141 size_t olen = sizeof(HammerVersion);
142 HammerVersion = HAMMER_VOL_VERSION_DEFAULT;
144 if (sysctlbyname("vfs.hammer.supported_version",
145 &HammerVersion, &olen, NULL, 0)) {
146 hwarn("HAMMER VFS not loaded, cannot get version info, "
149 } else if (HammerVersion >= HAMMER_VOL_VERSION_WIP) {
150 HammerVersion = HAMMER_VOL_VERSION_WIP - 1;
151 hwarn("HAMMER VFS supports higher version than "
152 "I understand, using version %d",
158 errx(1, "You must specify at least one special file (volume)");
159 if (nvols > HAMMER_MAX_VOLUMES)
160 errx(1, "The maximum number of volumes is %d",
164 hwarnx("A filesystem label must be specified");
169 * Generate a filesystem id and lookup the filesystem type
171 uuidgen(&Hammer_FSId, 1);
172 uuid_name_lookup(&Hammer_FSType, HAMMER_FSTYPE_STRING, &status);
173 if (status != uuid_s_ok)
174 errx(1, "uuids file does not have the DragonFly "
175 "HAMMER filesystem type");
178 for (i = 0; i < nvols; ++i) {
179 volume = init_volume(av[i], O_RDWR, i);
180 printf("Volume %d %s %-15s size %s\n",
181 volume->vol_no, volume->type, volume->name,
182 sizetostr(volume->size));
185 if (trim_volume(volume) == -1 && ForceOpt == 0)
186 errx(1, "Use -f option to proceed");
187 total += volume->size;
191 * Reserve space for (future) header junk, setup our poor-man's
192 * big-block allocator. Note that the header junk space includes
193 * volume header which is 1928 bytes.
195 if (HeaderJunkSize == -1)
196 HeaderJunkSize = HAMMER_VOL_JUNK_SIZE;
197 else if (HeaderJunkSize < (int64_t)sizeof(struct hammer_volume_ondisk))
198 HeaderJunkSize = sizeof(struct hammer_volume_ondisk);
199 HeaderJunkSize = HAMMER_BUFSIZE_DOALIGN(HeaderJunkSize);
202 * Calculate defaults for the boot area and memory log sizes,
203 * only if not specified by -b or -m option.
205 avg_vol_size = total / nvols;
206 if (BootAreaSize == -1)
207 BootAreaSize = init_boot_area_size(BootAreaSize, avg_vol_size);
208 if (MemoryLogSize == -1)
209 MemoryLogSize = init_memory_log_size(MemoryLogSize, avg_vol_size);
212 * Format the volumes. Format the root volume first so we can
213 * bootstrap the freemap.
215 format_volume(get_root_volume(), nvols, label);
216 for (i = 0; i < nvols; ++i)
217 if (i != HAMMER_ROOT_VOLNO)
218 format_volume(get_volume(i), nvols, label);
220 print_volume(get_root_volume());
228 print_volume(const struct volume_info *volume)
230 hammer_volume_ondisk_t ondisk;
231 hammer_blockmap_t blockmap;
232 hammer_off_t total = 0;
235 const char *name = NULL;
238 ondisk = volume->ondisk;
239 blockmap = &ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
241 nvols = ondisk->vol_count;
242 for (i = 0; i < nvols; ++i) {
243 struct volume_info *p = get_volume(i);
245 if (p->vol_no == HAMMER_ROOT_VOLNO) {
246 assert(name == NULL);
251 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
253 printf("---------------------------------------------\n");
254 printf("HAMMER version %d\n", HammerVersion);
255 printf("%d volume%s total size %s\n",
256 nvols, (nvols == 1 ? "" : "s"), sizetostr(total));
257 printf("root-volume: %s\n", name);
259 printf("header-junk-size: %s\n",
260 sizetostr(ondisk->vol_bot_beg));
261 printf("boot-area-size: %s\n",
262 sizetostr(ondisk->vol_mem_beg - ondisk->vol_bot_beg));
263 printf("memory-log-size: %s\n",
264 sizetostr(ondisk->vol_buf_beg - ondisk->vol_mem_beg));
265 printf("undo-buffer-size: %s\n",
266 sizetostr(HAMMER_OFF_LONG_ENCODE(blockmap->alloc_offset)));
267 printf("total-pre-allocated: %s\n",
268 sizetostr(HAMMER_OFF_SHORT_ENCODE(volume->vol_free_off)));
269 printf("fsid: %s\n", fsidstr);
271 printf("NOTE: Please remember that you may have to manually set up a\n"
272 "cron(8) job to prune and reblock the filesystem regularly.\n"
273 "By default, the system automatically runs 'hammer cleanup'\n"
274 "on a nightly basis. The periodic.conf(5) variable\n"
275 "'daily_clean_hammer_enable' can be unset to disable this.\n"
276 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
278 printf("\nWARNING: The minimum UNDO/REDO FIFO is %s, "
279 "you really should not\n"
280 "try to format a HAMMER filesystem this small.\n",
281 sizetostr(HAMMER_BIGBLOCK_SIZE *
282 HAMMER_MIN_UNDO_BIGBLOCKS));
284 printf("\nWARNING: HAMMER filesystems less than 50GB are "
286 "You may have to run 'hammer prune-everything' and "
288 "quite often, even if using a nohistory mount.\n");
296 "usage: newfs_hammer -L label [-Efh] [-b bootsize] [-m savesize] [-u undosize]\n"
297 " [-C cachesize[:readahead]] [-V version] special ...\n"
303 test_header_junk_size(int64_t size)
305 if (size < HAMMER_MIN_VOL_JUNK) {
307 errx(1, "The minimum header junk size is %s",
308 sizetostr(HAMMER_MIN_VOL_JUNK));
310 hwarnx("You have specified header junk size less than %s",
311 sizetostr(HAMMER_MIN_VOL_JUNK));
312 } else if (size > HAMMER_MAX_VOL_JUNK) {
313 errx(1, "The maximum header junk size is %s",
314 sizetostr(HAMMER_MAX_VOL_JUNK));
319 test_boot_area_size(int64_t size)
321 if (size < HAMMER_BOOT_MINBYTES) {
323 errx(1, "The minimum boot area size is %s",
324 sizetostr(HAMMER_BOOT_MINBYTES));
326 hwarnx("You have specified boot area size less than %s",
327 sizetostr(HAMMER_BOOT_MINBYTES));
328 } else if (size > HAMMER_BOOT_MAXBYTES) {
329 errx(1, "The maximum boot area size is %s",
330 sizetostr(HAMMER_BOOT_MAXBYTES));
335 test_memory_log_size(int64_t size)
337 if (size < HAMMER_MEM_MINBYTES) {
339 errx(1, "The minimum memory log size is %s",
340 sizetostr(HAMMER_MEM_MINBYTES));
342 hwarnx("You have specified memory log size less than %s",
343 sizetostr(HAMMER_MEM_MINBYTES));
344 } else if (size > HAMMER_MEM_MAXBYTES) {
345 errx(1, "The maximum memory log size is %s",
346 sizetostr(HAMMER_MEM_MAXBYTES));
351 test_undo_buffer_size(int64_t size)
353 int64_t minbuf, maxbuf;
355 minbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MIN_UNDO_BIGBLOCKS;
356 maxbuf = HAMMER_BIGBLOCK_SIZE * HAMMER_MAX_UNDO_BIGBLOCKS;
360 errx(1, "The minimum UNDO/REDO FIFO size is %s",
363 hwarnx("You have specified an UNDO/REDO FIFO size less "
364 "than %s, which may lead to VFS panics",
366 } else if (size > maxbuf) {
367 errx(1, "The maximum UNDO/REDO FIFO size is %s",
373 * Convert a string to a 64 bit signed integer with various requirements.
376 getsize(const char *str, int powerof2)
381 val = strtoll(str, &ptr, 0);
400 errx(1, "Unknown suffix in number '%s'", str);
404 errx(1, "Unknown suffix in number '%s'", str);
405 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1))
406 errx(1, "Value not power of 2: %s", str);
407 if ((powerof2 & 2) && (val & HAMMER_BUFMASK))
408 errx(1, "Value not an integral multiple of %dK: %s",
409 HAMMER_BUFSIZE / 1024, str);
414 * Generate a transaction id. Transaction ids are no longer time-based.
415 * Put the nail in the coffin by not making the first one time-based.
417 * We could start at 1 here but start at 2^32 to reserve a small domain for
418 * possible future use.
423 static hammer_tid_t lasttid;
426 lasttid = 0x0000000100000000ULL;
436 gettimeofday(&tv, NULL);
437 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
442 * TRIM the volume, but only if the backing store is a DEVICE
446 trim_volume(struct volume_info *volume)
450 char sysctl_name[64];
454 if (strcmp(volume->type, "DEVICE")) {
455 hwarnx("Cannot TRIM regular file %s", volume->name);
458 if (strncmp(volume->name, "/dev/da", 7)) {
459 hwarnx("%s does not support the TRIM command", volume->name);
463 /* Extract a number from /dev/da?s? */
464 dev_name = strdup(volume->name);
465 p = strtok(dev_name + strlen("/dev/da"), "s");
466 sprintf(sysctl_name, "kern.cam.da.%s.trim_enabled", p);
470 olen = sizeof(trim_enabled);
472 if (sysctlbyname(sysctl_name, &trim_enabled, &olen, NULL, 0) == -1) {
473 hwarnx("%s (%s) does not support the TRIM command",
474 volume->name, sysctl_name);
478 hwarnx("Erase device option selected, but sysctl (%s) "
479 "is not enabled", sysctl_name);
483 ioarg[0] = volume->device_offset;
484 ioarg[1] = volume->size;
486 printf("Trimming %s %s, sectors %llu-%llu\n",
487 volume->type, volume->name,
488 (unsigned long long)ioarg[0] / 512,
489 (unsigned long long)ioarg[1] / 512);
491 if (ioctl(volume->fd, IOCTLTRIM, ioarg) == -1)
492 err(1, "Trimming %s failed", volume->name);
498 * Format a HAMMER volume.
502 format_volume(struct volume_info *volume, int nvols, const char *label)
504 struct volume_info *root_vol;
505 hammer_volume_ondisk_t ondisk;
508 int64_t vol_buf_size;
509 hammer_off_t vol_alloc;
513 * Initialize basic information in the on-disk volume structure.
515 ondisk = volume->ondisk;
517 ondisk->vol_fsid = Hammer_FSId;
518 ondisk->vol_fstype = Hammer_FSType;
519 snprintf(ondisk->vol_label, sizeof(ondisk->vol_label), "%s", label);
520 ondisk->vol_no = volume->vol_no;
521 ondisk->vol_count = nvols;
522 ondisk->vol_version = HammerVersion;
524 vol_alloc = HeaderJunkSize;
525 ondisk->vol_bot_beg = vol_alloc;
526 vol_alloc += BootAreaSize;
527 ondisk->vol_mem_beg = vol_alloc;
528 vol_alloc += MemoryLogSize;
531 * The remaining area is the zone 2 buffer allocation area.
533 ondisk->vol_buf_beg = vol_alloc;
534 ondisk->vol_buf_end = volume->size & ~(int64_t)HAMMER_BUFMASK;
535 vol_buf_size = HAMMER_VOL_BUF_SIZE(ondisk);
537 if (vol_buf_size < (int64_t)sizeof(*ondisk))
538 errx(1, "volume %d %s is too small to hold the volume header",
539 volume->vol_no, volume->name);
540 if ((vol_buf_size & ~HAMMER_OFF_SHORT_MASK) != 0)
541 errx(1, "volume %d %s is too large", volume->vol_no, volume->name);
543 ondisk->vol_rootvol = HAMMER_ROOT_VOLNO;
544 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
546 volume->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no, 0);
547 volume->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(volume->vol_no,
548 vol_buf_size & ~HAMMER_BIGBLOCK_MASK64);
551 * Format the root volume.
553 if (volume->vol_no == HAMMER_ROOT_VOLNO) {
555 * Check freemap counts before formatting
557 freeblks = count_freemap(volume);
558 freebytes = freeblks * HAMMER_BIGBLOCK_SIZE64;
559 if (freebytes < 10*GIG && ForceOpt == 0)
560 errx(1, "Cannot create a HAMMER filesystem less than 10GB "
561 "unless you use -f\n(for the size of Volume %d). "
562 "HAMMER filesystems less than 50GB are not "
563 "recommended.", HAMMER_ROOT_VOLNO);
568 ondisk->vol0_next_tid = createtid();
571 * Format freemap. vol0_stat_freebigblocks is
572 * the number of big-blocks available for anything
573 * other than freemap zone at this point.
575 format_freemap(volume);
576 assert(ondisk->vol0_stat_freebigblocks == 0);
577 ondisk->vol0_stat_freebigblocks = initialize_freemap(volume);
580 * Format zones that are mapped to zone-2.
582 for (i = 0; i < HAMMER_MAX_ZONES; ++i)
583 if (hammer_is_index_record(i))
584 format_blockmap(volume, i, 0);
587 * Format undo zone. Formatting decrements
588 * vol0_stat_freebigblocks whenever a new big-block
589 * is allocated for undo zone.
591 format_undomap(volume, &UndoBufferSize);
592 assert(ondisk->vol0_stat_bigblocks == 0);
593 ondisk->vol0_stat_bigblocks = ondisk->vol0_stat_freebigblocks;
596 * Format the root directory. Formatting decrements
597 * vol0_stat_freebigblocks whenever a new big-block
598 * is allocated for required zones.
600 ondisk->vol0_btree_root = format_root_directory(label);
601 ++ondisk->vol0_stat_inodes; /* root inode */
603 freeblks = initialize_freemap(volume);
604 root_vol = get_root_volume();
605 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
606 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
611 * Format the root directory.
615 format_root_directory(const char *label)
617 hammer_off_t btree_off;
618 hammer_off_t idata_off;
619 hammer_off_t pfsd_off;
620 hammer_tid_t create_tid;
621 hammer_node_ondisk_t bnode;
622 hammer_inode_data_t 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_node(&btree_off, &data_buffer0);
634 idata = alloc_meta_element(&idata_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;
656 * Populate the PFS data for the root PFS.
658 pfsd->sync_low_tid = 1;
659 pfsd->sync_beg_tid = 0;
660 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on root PFS */
661 pfsd->shared_uuid = Hammer_FSId;
662 pfsd->unique_uuid = Hammer_FSId;
663 pfsd->mirror_flags = 0;
664 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
667 * Create the root of the B-Tree. The root is a leaf node so we
668 * do not have to worry about boundary elements.
670 bnode->parent = 0; /* no parent */
672 bnode->type = HAMMER_BTREE_TYPE_LEAF;
673 bnode->mirror_tid = 0;
676 * Create the first node element for the inode.
678 elm = &bnode->elms[0];
679 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
680 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
681 HAMMER_LOCALIZE_INODE;
682 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
683 elm->leaf.base.key = 0;
684 elm->leaf.base.create_tid = create_tid;
685 elm->leaf.base.delete_tid = 0;
686 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
687 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
688 elm->leaf.create_ts = (uint32_t)time(NULL);
690 elm->leaf.data_offset = idata_off;
691 elm->leaf.data_len = sizeof(*idata);
692 hammer_crc_set_leaf(HammerVersion, idata, &elm->leaf);
695 * Create the second node element for the PFS data.
696 * This is supposed to be a record part of the root ip (inode),
697 * so it should have the same obj_type value as above.
699 elm = &bnode->elms[1];
700 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
701 elm->leaf.base.localization = HAMMER_DEF_LOCALIZATION |
702 HAMMER_LOCALIZE_MISC;
703 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
704 elm->leaf.base.key = 0;
705 elm->leaf.base.create_tid = create_tid;
706 elm->leaf.base.delete_tid = 0;
707 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
708 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
709 elm->leaf.create_ts = (uint32_t)time(NULL);
711 elm->leaf.data_offset = pfsd_off;
712 elm->leaf.data_len = sizeof(*pfsd);
713 hammer_crc_set_leaf(HammerVersion, pfsd, &elm->leaf);
715 hammer_crc_set_btree(HammerVersion, bnode);
717 rel_buffer(data_buffer0);
718 rel_buffer(data_buffer1);
719 rel_buffer(data_buffer2);