nice(3): Return new nice value.
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
blobb6f4eb590229933a9a4fcd89c5ea2a9cd57f4983
1 /*
2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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
16 * distribution.
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
32 * SUCH DAMAGE.
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 check_volume(struct volume_info *vol);
42 static void format_volume(struct volume_info *vol, int nvols,const char *label,
43 off_t total_size);
44 static hammer_off_t format_root(const char *label);
45 static u_int64_t nowtime(void);
46 static void usage(void);
48 static int ForceOpt = 0;
50 #define GIG (1024LL*1024*1024)
52 int
53 main(int ac, char **av)
55 u_int32_t status;
56 off_t total;
57 int ch;
58 int i;
59 const char *label = NULL;
60 struct volume_info *vol;
61 char *fsidstr;
64 * Sanity check basic filesystem structures. No cookies for us
65 * if it gets broken!
67 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
68 assert(sizeof(struct hammer_blockmap_layer1) == 32);
69 assert(sizeof(struct hammer_blockmap_layer2) == 16);
72 * Generate a filesystem id and lookup the filesystem type
74 uuidgen(&Hammer_FSId, 1);
75 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
76 if (status != uuid_s_ok) {
77 errx(1, "uuids file does not have the DragonFly "
78 "HAMMER filesystem type");
82 * Parse arguments
84 while ((ch = getopt(ac, av, "fL:b:m:u:")) != -1) {
85 switch(ch) {
86 case 'f':
87 ForceOpt = 1;
88 break;
89 case 'L':
90 label = optarg;
91 break;
92 case 'b':
93 BootAreaSize = getsize(optarg,
94 HAMMER_BUFSIZE,
95 HAMMER_BOOT_MAXBYTES, 2);
96 break;
97 case 'm':
98 MemAreaSize = getsize(optarg,
99 HAMMER_BUFSIZE,
100 HAMMER_MEM_MAXBYTES, 2);
101 break;
102 case 'u':
103 UndoBufferSize = getsize(optarg,
104 HAMMER_LARGEBLOCK_SIZE,
105 HAMMER_LARGEBLOCK_SIZE *
106 HAMMER_UNDO_LAYER2, 2);
107 if (UndoBufferSize < 100*1024*1024 && ForceOpt == 0)
108 errx(1, "The minimum UNDO fifo size is 100M\n");
109 if (UndoBufferSize < 100*1024*1024) {
110 fprintf(stderr,
111 "WARNING: you have specified an UNDO "
112 "FIFO size less than 100M, which may\n"
113 "lead to VFS panics.\n");
115 break;
116 default:
117 usage();
118 break;
122 if (label == NULL) {
123 fprintf(stderr,
124 "newfs_hammer: A filesystem label must be specified\n");
125 exit(1);
129 * Collect volume information
131 ac -= optind;
132 av += optind;
133 NumVolumes = ac;
134 RootVolNo = 0;
136 if (NumVolumes == 0) {
137 fprintf(stderr,
138 "newfs_hammer: You must specify at least one special file (volume)\n");
139 exit(1);
142 total = 0;
143 for (i = 0; i < NumVolumes; ++i) {
144 vol = setup_volume(i, av[i], 1, O_RDWR);
147 * Load up information on the volume and initialize
148 * its remaining fields.
150 check_volume(vol);
151 total += vol->size;
155 * Calculate defaults for the boot and memory area sizes.
157 if (BootAreaSize == 0) {
158 BootAreaSize = HAMMER_BOOT_NOMBYTES;
159 while (BootAreaSize > total / NumVolumes / 256)
160 BootAreaSize >>= 1;
161 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
162 BootAreaSize = 0;
163 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
164 BootAreaSize = HAMMER_BOOT_MINBYTES;
166 if (MemAreaSize == 0) {
167 MemAreaSize = HAMMER_MEM_NOMBYTES;
168 while (MemAreaSize > total / NumVolumes / 256)
169 MemAreaSize >>= 1;
170 if (MemAreaSize < HAMMER_MEM_MINBYTES)
171 MemAreaSize = 0;
172 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
173 MemAreaSize = HAMMER_MEM_MINBYTES;
177 * Format the volumes. Format the root volume first so we can
178 * bootstrap the freemap.
180 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
181 for (i = 0; i < NumVolumes; ++i) {
182 if (i != RootVolNo)
183 format_volume(get_volume(i), NumVolumes, label, total);
187 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
188 * limit. If we do this the filesystem does not have to allocate
189 * new layer2 blocks which reduces the chances of the reblocker
190 * having to fallback to an extremely inefficient algorithm.
192 vol = get_volume(RootVolNo);
193 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
194 vol->cache.modified = 1;
195 uuid_to_string(&Hammer_FSId, &fsidstr, &status);
197 printf("---------------------------------------------\n");
198 printf("%d volume%s total size %s\n",
199 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
200 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
201 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
202 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
203 printf("total-pre-allocated: %s\n",
204 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
205 printf("fsid: %s\n", fsidstr);
206 printf("\n");
207 printf("NOTE: Please remember that you may have to manually set up a\n"
208 "cron job to prune and reblock the filesystem regularly.\n"
209 "By default, the system automatically runs 'hammer cleanup'\n"
210 "on a nightly basis. The periodic.conf(5) variable\n"
211 "'daily_clean_hammer_enable' can be unset to disable this.\n"
212 "Also see 'man hammer' and 'man HAMMER' for more information.\n");
213 if (total < 50*GIG) {
214 printf("\nWARNING: HAMMER filesystems less than 50G are "
215 "not recommended!\n"
216 "You may have to run 'hammer prune-everything' and "
217 "'hammer reblock'\n"
218 "quite often, even if using a nohistory mount.\n");
220 flush_all_volumes();
221 return(0);
224 static
225 void
226 usage(void)
228 fprintf(stderr,
229 "newfs_hammer -L label [-b bootsize] [-m savesize] [-u undosize] "
230 "special ...\n"
232 exit(1);
236 * Convert the size in bytes to a human readable string.
238 static
239 const char *
240 sizetostr(off_t size)
242 static char buf[32];
244 if (size < 1024 / 2) {
245 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
246 } else if (size < 1024 * 1024 / 2) {
247 snprintf(buf, sizeof(buf), "%6.2fKB",
248 (double)size / 1024);
249 } else if (size < 1024 * 1024 * 1024LL / 2) {
250 snprintf(buf, sizeof(buf), "%6.2fMB",
251 (double)size / (1024 * 1024));
252 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
253 snprintf(buf, sizeof(buf), "%6.2fGB",
254 (double)size / (1024 * 1024 * 1024LL));
255 } else {
256 snprintf(buf, sizeof(buf), "%6.2fTB",
257 (double)size / (1024 * 1024 * 1024LL * 1024LL));
259 return(buf);
263 * Convert a string to a 64 bit signed integer with various requirements.
265 static int64_t
266 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
268 int64_t val;
269 char *ptr;
271 val = strtoll(str, &ptr, 0);
272 switch(*ptr) {
273 case 't':
274 case 'T':
275 val *= 1024;
276 /* fall through */
277 case 'g':
278 case 'G':
279 val *= 1024;
280 /* fall through */
281 case 'm':
282 case 'M':
283 val *= 1024;
284 /* fall through */
285 case 'k':
286 case 'K':
287 val *= 1024;
288 break;
289 default:
290 errx(1, "Unknown suffix in number '%s'\n", str);
291 /* not reached */
293 if (ptr[1]) {
294 errx(1, "Unknown suffix in number '%s'\n", str);
295 /* not reached */
297 if (val < minval) {
298 errx(1, "Value too small: %s, min is %s\n",
299 str, sizetostr(minval));
300 /* not reached */
302 if (val > maxval) {
303 errx(1, "Value too large: %s, max is %s\n",
304 str, sizetostr(maxval));
305 /* not reached */
307 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
308 errx(1, "Value not power of 2: %s\n", str);
309 /* not reached */
311 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
312 errx(1, "Value not an integral multiple of %dK: %s",
313 HAMMER_BUFSIZE / 1024, str);
314 /* not reached */
316 return(val);
320 * Generate a transaction id. Transaction ids are no longer time-based.
321 * Put the nail in the coffin by not making the first one time-based.
323 * We could start at 1 here but start at 2^32 to reserve a small domain for
324 * possible future use.
326 static hammer_tid_t
327 createtid(void)
329 static hammer_tid_t lasttid;
331 if (lasttid == 0)
332 lasttid = 0x0000000100000000ULL;
333 return(lasttid++);
336 static u_int64_t
337 nowtime(void)
339 struct timeval tv;
340 u_int64_t xtime;
342 gettimeofday(&tv, NULL);
343 xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
344 return(xtime);
348 * Check basic volume characteristics. HAMMER filesystems use a minimum
349 * of a 16KB filesystem buffer size.
351 static
352 void
353 check_volume(struct volume_info *vol)
355 struct partinfo pinfo;
356 struct stat st;
359 * Get basic information about the volume
361 vol->fd = open(vol->name, O_RDWR);
362 if (vol->fd < 0)
363 err(1, "Unable to open %s R+W", vol->name);
364 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
366 * Allow the formatting of regular files as HAMMER volumes
368 if (fstat(vol->fd, &st) < 0)
369 err(1, "Unable to stat %s", vol->name);
370 vol->size = st.st_size;
371 vol->type = "REGFILE";
372 } else {
374 * When formatting a block device as a HAMMER volume the
375 * sector size must be compatible. HAMMER uses 16384 byte
376 * filesystem buffers.
378 if (pinfo.reserved_blocks) {
379 errx(1, "HAMMER cannot be placed in a partition "
380 "which overlaps the disklabel or MBR");
382 if (pinfo.media_blksize > 16384 ||
383 16384 % pinfo.media_blksize) {
384 errx(1, "A media sector size of %d is not supported",
385 pinfo.media_blksize);
388 vol->size = pinfo.media_size;
389 vol->type = "DEVICE";
391 printf("Volume %d %s %-15s size %s\n",
392 vol->vol_no, vol->type, vol->name,
393 sizetostr(vol->size));
396 * Reserve space for (future) header junk, setup our poor-man's
397 * bigblock allocator.
399 vol->vol_alloc = HAMMER_BUFSIZE * 16;
403 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
405 static
406 void
407 format_volume(struct volume_info *vol, int nvols, const char *label,
408 off_t total_size __unused)
410 struct volume_info *root_vol;
411 struct hammer_volume_ondisk *ondisk;
412 int64_t freeblks;
413 int64_t freebytes;
414 int i;
417 * Initialize basic information in the on-disk volume structure.
419 ondisk = vol->ondisk;
421 ondisk->vol_fsid = Hammer_FSId;
422 ondisk->vol_fstype = Hammer_FSType;
423 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
424 ondisk->vol_no = vol->vol_no;
425 ondisk->vol_count = nvols;
426 ondisk->vol_version = HAMMER_VOL_VERSION_DEFAULT;
428 ondisk->vol_bot_beg = vol->vol_alloc;
429 vol->vol_alloc += BootAreaSize;
430 ondisk->vol_mem_beg = vol->vol_alloc;
431 vol->vol_alloc += MemAreaSize;
434 * The remaining area is the zone 2 buffer allocation area. These
435 * buffers
437 ondisk->vol_buf_beg = vol->vol_alloc;
438 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
440 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
441 errx(1, "volume %d %s is too small to hold the volume header",
442 vol->vol_no, vol->name);
445 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
446 HAMMER_BUFSIZE;
447 ondisk->vol_blocksize = HAMMER_BUFSIZE;
449 ondisk->vol_rootvol = RootVolNo;
450 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
452 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
453 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
456 * Format the root volume.
458 if (vol->vol_no == RootVolNo) {
460 * Starting TID
462 ondisk->vol0_next_tid = createtid();
464 format_freemap(vol,
465 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
467 freeblks = initialize_freemap(vol);
468 ondisk->vol0_stat_freebigblocks = freeblks;
470 freebytes = freeblks * HAMMER_LARGEBLOCK_SIZE64;
471 if (freebytes < 1*GIG && ForceOpt == 0) {
472 errx(1, "Cannot create a HAMMER filesystem less than "
473 "1GB unless you use -f. HAMMER filesystems\n"
474 "less than 50G are not recommended\n");
477 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
478 format_blockmap(&ondisk->vol0_blockmap[i],
479 HAMMER_ZONE_ENCODE(i, 0));
481 format_undomap(ondisk);
483 ondisk->vol0_btree_root = format_root(label);
484 ++ondisk->vol0_stat_inodes; /* root inode */
485 } else {
486 freeblks = initialize_freemap(vol);
487 root_vol = get_volume(RootVolNo);
488 root_vol->cache.modified = 1;
489 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
490 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
491 rel_volume(root_vol);
496 * Format the root directory.
498 static
499 hammer_off_t
500 format_root(const char *label)
502 hammer_off_t btree_off;
503 hammer_off_t pfsd_off;
504 hammer_off_t data_off;
505 hammer_tid_t create_tid;
506 hammer_node_ondisk_t bnode;
507 struct hammer_inode_data *idata;
508 hammer_pseudofs_data_t pfsd;
509 struct buffer_info *data_buffer1 = NULL;
510 struct buffer_info *data_buffer2 = NULL;
511 hammer_btree_elm_t elm;
512 u_int64_t xtime;
514 bnode = alloc_btree_element(&btree_off);
515 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
516 pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
517 create_tid = createtid();
518 xtime = nowtime();
521 * Populate the inode data and inode record for the root directory.
523 idata->version = HAMMER_INODE_DATA_VERSION;
524 idata->mode = 0755;
525 idata->ctime = xtime;
526 idata->mtime = xtime;
527 idata->atime = xtime;
528 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
529 idata->size = 0;
530 idata->nlinks = 1;
532 pfsd->sync_low_tid = 1;
533 pfsd->sync_beg_tid = 0;
534 pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
535 pfsd->shared_uuid = Hammer_FSId;
536 pfsd->unique_uuid = Hammer_FSId;
537 pfsd->reserved01 = 0;
538 pfsd->mirror_flags = 0;
539 snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
542 * Create the root of the B-Tree. The root is a leaf node so we
543 * do not have to worry about boundary elements.
545 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
546 bnode->count = 2;
547 bnode->type = HAMMER_BTREE_TYPE_LEAF;
549 elm = &bnode->elms[0];
550 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
551 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
552 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
553 elm->leaf.base.key = 0;
554 elm->leaf.base.create_tid = create_tid;
555 elm->leaf.base.delete_tid = 0;
556 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
557 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
558 elm->leaf.create_ts = (u_int32_t)time(NULL);
560 elm->leaf.data_offset = data_off;
561 elm->leaf.data_len = sizeof(*idata);
562 elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
564 elm = &bnode->elms[1];
565 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
566 elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
567 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
568 elm->leaf.base.key = 0;
569 elm->leaf.base.create_tid = create_tid;
570 elm->leaf.base.delete_tid = 0;
571 elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
572 elm->leaf.base.obj_type = 0;
573 elm->leaf.create_ts = (u_int32_t)time(NULL);
575 elm->leaf.data_offset = pfsd_off;
576 elm->leaf.data_len = sizeof(*pfsd);
577 elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
579 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
581 return(btree_off);