xref: /dragonfly/sys/vfs/hammer/hammer_volume.c (revision 23b3ef78)
1 /*
2  * Copyright (c) 2009 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> and
6  * Michael Neumann <mneumann@ntecs.de>
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in
16  *    the documentation and/or other materials provided with the
17  *    distribution.
18  * 3. Neither the name of The DragonFly Project nor the names of its
19  *    contributors may be used to endorse or promote products derived
20  *    from this software without specific, prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33  * SUCH DAMAGE.
34  *
35  */
36 
37 #include "hammer.h"
38 
39 struct bigblock_stat {
40 	int64_t total_bigblocks;
41 	int64_t total_free_bigblocks;
42 	int64_t counter;
43 };
44 
45 static int
46 hammer_format_volume_header(struct hammer_mount *hmp,
47 	struct hammer_volume_ondisk *ondisk,
48 	const char *vol_name, int vol_no, int vol_count,
49 	int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size);
50 
51 static int
52 hammer_update_volumes_header(hammer_transaction_t trans,
53 	struct bigblock_stat *stat);
54 
55 static int
56 hammer_do_reblock(hammer_transaction_t trans, hammer_inode_t ip);
57 
58 static int
59 hammer_format_freemap(hammer_transaction_t trans, hammer_volume_t volume,
60 	struct bigblock_stat *stat);
61 
62 static int
63 hammer_free_freemap(hammer_transaction_t trans, hammer_volume_t volume,
64 	struct bigblock_stat *stat);
65 
66 static int
67 hammer_test_free_freemap(hammer_transaction_t trans, hammer_volume_t volume);
68 
69 int
70 hammer_ioc_volume_add(hammer_transaction_t trans, hammer_inode_t ip,
71 		struct hammer_ioc_volume *ioc)
72 {
73 	struct hammer_mount *hmp = trans->hmp;
74 	struct mount *mp = hmp->mp;
75 	struct hammer_volume_ondisk ondisk;
76 	struct bigblock_stat stat;
77 	hammer_volume_t volume;
78 	int free_vol_no = 0;
79 	int error;
80 
81 	if (mp->mnt_flag & MNT_RDONLY) {
82 		hmkprintf(hmp, "Cannot add volume to read-only HAMMER filesystem\n");
83 		return (EINVAL);
84 	}
85 
86 	if (hmp->nvolumes >= HAMMER_MAX_VOLUMES) {
87 		hmkprintf(hmp, "Max number of HAMMER volumes exceeded\n");
88 		return (EINVAL);
89 	}
90 
91 	if (hammer_lock_ex_try(&hmp->volume_lock) != 0) {
92 		hmkprintf(hmp, "Another volume operation is in progress!\n");
93 		return (EAGAIN);
94 	}
95 
96 	/*
97 	 * Find an unused volume number.
98 	 */
99 	while (free_vol_no < HAMMER_MAX_VOLUMES &&
100 		HAMMER_VOLUME_NUMBER_IS_SET(hmp, free_vol_no)) {
101 		++free_vol_no;
102 	}
103 	if (free_vol_no >= HAMMER_MAX_VOLUMES) {
104 		hmkprintf(hmp, "Max number of HAMMER volumes exceeded\n");
105 		error = EINVAL;
106 		goto end;
107 	}
108 
109 	error = hammer_format_volume_header(
110 		hmp,
111 		&ondisk,
112 		hmp->rootvol->ondisk->vol_name,
113 		free_vol_no,
114 		hmp->nvolumes+1,
115 		ioc->vol_size,
116 		ioc->boot_area_size,
117 		ioc->mem_area_size);
118 	if (error)
119 		goto end;
120 
121 	error = hammer_install_volume(hmp, ioc->device_name, NULL, &ondisk);
122 	if (error)
123 		goto end;
124 
125 	hammer_sync_lock_sh(trans);
126 	hammer_lock_ex(&hmp->blkmap_lock);
127 
128 	volume = hammer_get_volume(hmp, free_vol_no, &error);
129 	KKASSERT(volume != NULL && error == 0);
130 
131 	error =	hammer_format_freemap(trans, volume, &stat);
132 	KKASSERT(error == 0);
133 	hammer_rel_volume(volume, 0);
134 
135 	++hmp->nvolumes;
136 	error = hammer_update_volumes_header(trans, &stat);
137 	KKASSERT(error == 0);
138 
139 	hammer_unlock(&hmp->blkmap_lock);
140 	hammer_sync_unlock(trans);
141 
142 	KKASSERT(error == 0);
143 end:
144 	hammer_unlock(&hmp->volume_lock);
145 	if (error)
146 		hmkprintf(hmp, "An error occurred: %d\n", error);
147 	return (error);
148 }
149 
150 
151 /*
152  * Remove a volume.
153  */
154 int
155 hammer_ioc_volume_del(hammer_transaction_t trans, hammer_inode_t ip,
156 		struct hammer_ioc_volume *ioc)
157 {
158 	struct hammer_mount *hmp = trans->hmp;
159 	struct mount *mp = hmp->mp;
160 	struct hammer_volume_ondisk *ondisk;
161 	struct bigblock_stat stat;
162 	hammer_volume_t volume;
163 	int vol_no;
164 	int error = 0;
165 
166 	if (mp->mnt_flag & MNT_RDONLY) {
167 		hmkprintf(hmp, "Cannot del volume from read-only HAMMER filesystem\n");
168 		return (EINVAL);
169 	}
170 
171 	if (hmp->nvolumes <= 1) {
172 		hmkprintf(hmp, "No HAMMER volume to delete\n");
173 		return (EINVAL);
174 	}
175 
176 	if (hammer_lock_ex_try(&hmp->volume_lock) != 0) {
177 		hmkprintf(hmp, "Another volume operation is in progress!\n");
178 		return (EAGAIN);
179 	}
180 
181 	/*
182 	 * find volume by volname
183 	 */
184 	volume = NULL;
185 	HAMMER_VOLUME_NUMBER_FOREACH(hmp, vol_no) {
186 		volume = hammer_get_volume(hmp, vol_no, &error);
187 		KKASSERT(volume != NULL && error == 0);
188 		if (strcmp(volume->vol_name, ioc->device_name) == 0) {
189 			break;
190 		}
191 		hammer_rel_volume(volume, 0);
192 		volume = NULL;
193 	}
194 
195 	if (volume == NULL) {
196 		hmkprintf(hmp, "Couldn't find volume\n");
197 		error = EINVAL;
198 		goto end;
199 	}
200 
201 	if (volume == trans->rootvol) {
202 		hmkprintf(hmp, "Cannot remove root-volume\n");
203 		hammer_rel_volume(volume, 0);
204 		error = EINVAL;
205 		goto end;
206 	}
207 
208 	/*
209 	 * Reblock filesystem if the volume is not empty
210 	 */
211 	hmp->volume_to_remove = volume->vol_no;
212 
213 	if (hammer_test_free_freemap(trans, volume)) {
214 		error = hammer_do_reblock(trans, ip);
215 		if (error) {
216 			hmp->volume_to_remove = -1;
217 			hammer_rel_volume(volume, 0);
218 			goto end;
219 		}
220 	}
221 
222 	/*
223 	 * Sync filesystem
224 	 */
225 	hammer_flush_dirty(hmp, 30);
226 
227 	hammer_sync_lock_sh(trans);
228 	hammer_lock_ex(&hmp->blkmap_lock);
229 
230 	/*
231 	 * We use stat later to update rootvol's big-block stats
232 	 */
233 	error = hammer_free_freemap(trans, volume, &stat);
234 	if (error) {
235 		hmkprintf(hmp, "Failed to free volume: ");
236 		if (error == EBUSY)
237 			kprintf("Volume %d not empty\n", volume->vol_no);
238 		else
239 			kprintf("%d\n", error);
240 		hmp->volume_to_remove = -1;
241 		hammer_rel_volume(volume, 0);
242 		goto end1;
243 	}
244 	hammer_rel_volume(volume, 0);
245 
246 	/*
247 	 * Unload buffers
248 	 */
249         RB_SCAN(hammer_buf_rb_tree, &hmp->rb_bufs_root, NULL,
250 		hammer_unload_buffer, volume);
251 
252 	bzero(&ondisk, sizeof(ondisk));
253 	error = hammer_unload_volume(volume, &ondisk);
254 	if (error == -1) {
255 		hmkprintf(hmp, "Failed to unload volume\n");
256 		goto end1;
257 	}
258 
259 	--hmp->nvolumes;
260 	error = hammer_update_volumes_header(trans, &stat);
261 	KKASSERT(error == 0);
262 	hmp->volume_to_remove = -1;
263 
264 end1:
265 	hammer_unlock(&hmp->blkmap_lock);
266 	hammer_sync_unlock(trans);
267 
268 end:
269 	hammer_unlock(&hmp->volume_lock);
270 	if (error)
271 		hmkprintf(hmp, "An error occurred: %d\n", error);
272 	return (error);
273 }
274 
275 
276 int
277 hammer_ioc_volume_list(hammer_transaction_t trans, hammer_inode_t ip,
278     struct hammer_ioc_volume_list *ioc)
279 {
280 	struct hammer_mount *hmp = trans->hmp;
281 	hammer_volume_t volume;
282 	int error = 0;
283 	int i, len, cnt = 0;
284 
285 	if (hammer_lock_ex_try(&hmp->volume_lock) != 0) {
286 		hmkprintf(hmp, "Another volume operation is in progress!\n");
287 		return (EAGAIN);
288 	}
289 
290 	HAMMER_VOLUME_NUMBER_FOREACH(hmp, i) {
291 		if (cnt >= ioc->nvols)
292 			break;
293 		volume = hammer_get_volume(hmp, i, &error);
294 		KKASSERT(volume != NULL && error == 0);
295 
296 		len = strlen(volume->vol_name) + 1;
297 		KKASSERT(len <= MAXPATHLEN);
298 
299 		error = copyout(volume->vol_name, ioc->vols[cnt].device_name,
300 				len);
301 		hammer_rel_volume(volume, 0);
302 		if (error)
303 			goto end;
304 		cnt++;
305 	}
306 	ioc->nvols = cnt;
307 
308 end:
309 	hammer_unlock(&hmp->volume_lock);
310 	return (error);
311 }
312 
313 static
314 int
315 hammer_do_reblock(hammer_transaction_t trans, hammer_inode_t ip)
316 {
317 	struct hammer_mount *hmp = trans->hmp;
318 	int error;
319 	int vol_no;
320 
321 	struct hammer_ioc_reblock reblock;
322 	bzero(&reblock, sizeof(reblock));
323 
324 	vol_no = trans->hmp->volume_to_remove;
325 	KKASSERT(vol_no != -1);
326 
327 	reblock.key_beg.localization = HAMMER_MIN_LOCALIZATION;
328 	reblock.key_beg.obj_id = HAMMER_MIN_OBJID;
329 	reblock.key_end.localization = HAMMER_MAX_LOCALIZATION;
330 	reblock.key_end.obj_id = HAMMER_MAX_OBJID;
331 	reblock.head.flags = HAMMER_IOC_DO_FLAGS;
332 	reblock.free_level = 0;	/* reblock all big-blocks */
333 	reblock.allpfs = 1;	/* reblock all PFS */
334 	reblock.vol_no = vol_no;
335 
336 	hmkprintf(hmp, "reblock started\n");
337 	error = hammer_ioc_reblock(trans, ip, &reblock);
338 
339 	if (reblock.head.flags & HAMMER_IOC_HEAD_INTR) {
340 		error = EINTR;
341 	}
342 
343 	if (error) {
344 		if (error == EINTR) {
345 			hmkprintf(hmp, "reblock was interrupted\n");
346 		} else {
347 			hmkprintf(hmp, "reblock failed: %d\n", error);
348 		}
349 		return(error);
350 	}
351 
352 	return(0);
353 }
354 
355 /*
356  * Iterate over all usable L1 entries of the volume and
357  * the corresponding L2 entries.
358  */
359 static int
360 hammer_iterate_l1l2_entries(hammer_transaction_t trans, hammer_volume_t volume,
361 	int (*callback)(hammer_transaction_t, hammer_volume_t, hammer_buffer_t*,
362 		struct hammer_blockmap_layer1*, struct hammer_blockmap_layer2*,
363 		hammer_off_t, hammer_off_t, void*),
364 	void *data)
365 {
366 	struct hammer_mount *hmp = trans->hmp;
367 	hammer_blockmap_t freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
368 	int error = 0;
369 	hammer_off_t phys_off;
370 	hammer_off_t block_off;
371 	hammer_off_t layer1_off;
372 	hammer_off_t layer2_off;
373 	hammer_off_t aligned_buf_end_off;
374 	hammer_off_t aligned_vol_end_off;
375 	struct hammer_blockmap_layer1 *layer1;
376 	struct hammer_blockmap_layer2 *layer2;
377 	hammer_buffer_t buffer1 = NULL;
378 	hammer_buffer_t buffer2 = NULL;
379 
380 	/*
381 	 * Calculate the usable size of the volume, which
382 	 * must be aligned at a big-block (8 MB) boundary.
383 	 */
384 	aligned_buf_end_off = HAMMER_ENCODE_RAW_BUFFER(volume->ondisk->vol_no,
385 		(volume->ondisk->vol_buf_end - volume->ondisk->vol_buf_beg)
386 		& ~HAMMER_BIGBLOCK_MASK64);
387 	aligned_vol_end_off = (aligned_buf_end_off + HAMMER_BLOCKMAP_LAYER2_MASK)
388 		& ~HAMMER_BLOCKMAP_LAYER2_MASK;
389 
390 	/*
391 	 * Iterate the volume's address space in chunks of 4 TB, where each
392 	 * chunk consists of at least one physically available 8 MB big-block.
393 	 *
394 	 * For each chunk we need one L1 entry and one L2 big-block.
395 	 * We use the first big-block of each chunk as L2 block.
396 	 */
397 	for (phys_off = HAMMER_ENCODE_RAW_BUFFER(volume->ondisk->vol_no, 0);
398 	     phys_off < aligned_vol_end_off;
399 	     phys_off += HAMMER_BLOCKMAP_LAYER2) {
400 		for (block_off = 0;
401 		     block_off < HAMMER_BLOCKMAP_LAYER2;
402 		     block_off += HAMMER_BIGBLOCK_SIZE) {
403 			layer2_off = phys_off +
404 				HAMMER_BLOCKMAP_LAYER2_OFFSET(block_off);
405 			layer2 = hammer_bread(hmp, layer2_off, &error, &buffer2);
406 			if (error)
407 				goto end;
408 
409 			error = callback(trans, volume, &buffer2, NULL,
410 					 layer2, phys_off, block_off, data);
411 			if (error)
412 				goto end;
413 		}
414 
415 		layer1_off = freemap->phys_offset +
416 				HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_off);
417 		layer1 = hammer_bread(hmp, layer1_off, &error, &buffer1);
418 		if (error)
419 			goto end;
420 
421 		error = callback(trans, volume, &buffer1, layer1, NULL,
422 				 phys_off, 0, data);
423 		if (error)
424 			goto end;
425 	}
426 
427 end:
428 	if (buffer1)
429 		hammer_rel_buffer(buffer1, 0);
430 	if (buffer2)
431 		hammer_rel_buffer(buffer2, 0);
432 
433 	return error;
434 }
435 
436 
437 static int
438 format_callback(hammer_transaction_t trans, hammer_volume_t volume,
439 	hammer_buffer_t *bufferp,
440 	struct hammer_blockmap_layer1 *layer1,
441 	struct hammer_blockmap_layer2 *layer2,
442 	hammer_off_t phys_off,
443 	hammer_off_t block_off,
444 	void *data)
445 {
446 	struct bigblock_stat *stat = (struct bigblock_stat*)data;
447 
448 	/*
449 	 * Calculate the usable size of the volume, which must be aligned
450 	 * at a big-block (8 MB) boundary.
451 	 */
452 	hammer_off_t aligned_buf_end_off;
453 	aligned_buf_end_off = HAMMER_ENCODE_RAW_BUFFER(volume->ondisk->vol_no,
454 		(volume->ondisk->vol_buf_end - volume->ondisk->vol_buf_beg)
455 		& ~HAMMER_BIGBLOCK_MASK64);
456 
457 	if (layer1) {
458 		KKASSERT(layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL);
459 
460 		hammer_modify_buffer(trans, *bufferp, layer1, sizeof(*layer1));
461 		bzero(layer1, sizeof(*layer1));
462 		layer1->phys_offset = phys_off;
463 		layer1->blocks_free = stat->counter;
464 		layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE);
465 		hammer_modify_buffer_done(*bufferp);
466 		stat->counter = 0; /* reset */
467 	} else if (layer2) {
468 		hammer_modify_buffer(trans, *bufferp, layer2, sizeof(*layer2));
469 		bzero(layer2, sizeof(*layer2));
470 
471 		if (block_off == 0) {
472 			/*
473 			 * The first entry represents the L2 big-block itself.
474 			 * Note that the first entry represents the L1 big-block
475 			 * and the second entry represents the L2 big-block for
476 			 * root volume, but this function assumes the volume is
477 			 * non-root given that we can't add a new root volume.
478 			 */
479 			KKASSERT(trans->rootvol && trans->rootvol != volume);
480 			layer2->zone = HAMMER_ZONE_FREEMAP_INDEX;
481 			layer2->append_off = HAMMER_BIGBLOCK_SIZE;
482 			layer2->bytes_free = 0;
483 		} else if (phys_off + block_off < aligned_buf_end_off) {
484 			/*
485 			 * Available big-block
486 			 */
487 			layer2->zone = 0;
488 			layer2->append_off = 0;
489 			layer2->bytes_free = HAMMER_BIGBLOCK_SIZE;
490 			++stat->total_bigblocks;
491 			++stat->total_free_bigblocks;
492 			++stat->counter;
493 		} else {
494 			/*
495 			 * Big-block outside of physically available
496 			 * space
497 			 */
498 			layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX;
499 			layer2->append_off = HAMMER_BIGBLOCK_SIZE;
500 			layer2->bytes_free = 0;
501 		}
502 
503 		layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE);
504 		hammer_modify_buffer_done(*bufferp);
505 	} else {
506 		KKASSERT(0);
507 	}
508 
509 	return 0;
510 }
511 
512 static int
513 hammer_format_freemap(hammer_transaction_t trans, hammer_volume_t volume,
514 	struct bigblock_stat *stat)
515 {
516 	stat->total_bigblocks = 0;
517 	stat->total_free_bigblocks = 0;
518 	stat->counter = 0;
519 	return hammer_iterate_l1l2_entries(trans, volume, format_callback, stat);
520 }
521 
522 static int
523 free_callback(hammer_transaction_t trans, hammer_volume_t volume __unused,
524 	hammer_buffer_t *bufferp,
525 	struct hammer_blockmap_layer1 *layer1,
526 	struct hammer_blockmap_layer2 *layer2,
527 	hammer_off_t phys_off,
528 	hammer_off_t block_off __unused,
529 	void *data)
530 {
531 	struct bigblock_stat *stat = (struct bigblock_stat*)data;
532 
533 	if (layer1) {
534 		if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) {
535 			/*
536 			 * This layer1 entry is already free.
537 			 */
538 			return 0;
539 		}
540 
541 		KKASSERT(HAMMER_VOL_DECODE(layer1->phys_offset) ==
542 			trans->hmp->volume_to_remove);
543 
544 		/*
545 		 * Free the L1 entry
546 		 */
547 		hammer_modify_buffer(trans, *bufferp, layer1, sizeof(*layer1));
548 		bzero(layer1, sizeof(*layer1));
549 		layer1->phys_offset = HAMMER_BLOCKMAP_UNAVAIL;
550 		layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE);
551 		hammer_modify_buffer_done(*bufferp);
552 
553 		return 0;
554 	} else if (layer2) {
555 		if (layer2->zone == HAMMER_ZONE_UNAVAIL_INDEX) {
556 			return 0;
557 		}
558 
559 		if (layer2->zone == HAMMER_ZONE_FREEMAP_INDEX) {
560 			return 0;
561 		}
562 
563 		if (layer2->append_off == 0 &&
564 		    layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) {
565 			--stat->total_bigblocks;
566 			--stat->total_free_bigblocks;
567 			return 0;
568 		}
569 
570 		/*
571 		 * We found a layer2 entry that is not empty!
572 		 */
573 		return EBUSY;
574 	} else {
575 		KKASSERT(0);
576 	}
577 
578 	return EINVAL;
579 }
580 
581 /*
582  * Non-zero return value means we can't free the volume.
583  */
584 static int
585 test_free_callback(hammer_transaction_t trans, hammer_volume_t volume __unused,
586 	hammer_buffer_t *bufferp,
587 	struct hammer_blockmap_layer1 *layer1,
588 	struct hammer_blockmap_layer2 *layer2,
589 	hammer_off_t phys_off,
590 	hammer_off_t block_off __unused,
591 	void *data)
592 {
593 	if (layer2 == NULL) {
594 		return(0);  /* only layer2 needs to be tested */
595 	}
596 
597 	if (layer2->zone == HAMMER_ZONE_UNAVAIL_INDEX) {
598 		return(0);  /* beyond physically available space */
599 	}
600 	if (layer2->zone == HAMMER_ZONE_FREEMAP_INDEX) {
601 		return(0);  /* big-block for layer1/2 */
602 	}
603 	if (layer2->append_off == 0 &&
604 	    layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) {
605 		return(0);  /* big-block is 0% used */
606 	}
607 
608 	return(EBUSY);  /* big-block has data */
609 }
610 
611 static int
612 hammer_free_freemap(hammer_transaction_t trans, hammer_volume_t volume,
613 	struct bigblock_stat *stat)
614 {
615 	int error;
616 
617 	error = hammer_test_free_freemap(trans, volume);
618 	if (error)
619 		return error;  /* not ready to free */
620 
621 	stat->total_bigblocks = 0;
622 	stat->total_free_bigblocks = 0;
623 	stat->counter = 0;
624 	return hammer_iterate_l1l2_entries(trans, volume, free_callback, stat);
625 }
626 
627 static int
628 hammer_test_free_freemap(hammer_transaction_t trans, hammer_volume_t volume)
629 {
630 	return hammer_iterate_l1l2_entries(trans, volume, test_free_callback, NULL);
631 }
632 
633 static int
634 hammer_format_volume_header(struct hammer_mount *hmp,
635 	struct hammer_volume_ondisk *ondisk,
636 	const char *vol_name, int vol_no, int vol_count,
637 	int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size)
638 {
639 	int64_t vol_alloc;
640 
641 	KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
642 
643 	bzero(ondisk, sizeof(struct hammer_volume_ondisk));
644 	ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
645 	ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
646 	ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
647 	ondisk->vol_fsid = hmp->fsid;
648 	ondisk->vol_rootvol = hmp->rootvol->vol_no;
649 	ondisk->vol_no = vol_no;
650 	ondisk->vol_count = vol_count;
651 	ondisk->vol_version = hmp->version;
652 
653 	/*
654 	 * Reserve space for (future) header junk, copy volume relative
655 	 * offset from the existing root volume.
656 	 */
657 	vol_alloc = hmp->rootvol->ondisk->vol_bot_beg;
658 	ondisk->vol_bot_beg = vol_alloc;
659 	vol_alloc += boot_area_size;
660 	ondisk->vol_mem_beg = vol_alloc;
661 	vol_alloc += mem_area_size;
662 
663 	/*
664 	 * The remaining area is the zone 2 buffer allocation area.
665 	 */
666 	ondisk->vol_buf_beg = vol_alloc;
667 	ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
668 
669 	if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
670 		hmkprintf(hmp, "volume %d %s is too small to hold the volume header\n",
671 		     ondisk->vol_no, ondisk->vol_name);
672 		return(EFTYPE);
673 	}
674 
675 	ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
676 			      HAMMER_BUFSIZE;
677 	ondisk->vol_blocksize = HAMMER_BUFSIZE;
678 	return(0);
679 }
680 
681 static int
682 hammer_update_volumes_header(hammer_transaction_t trans,
683 	struct bigblock_stat *stat)
684 {
685 	struct hammer_mount *hmp = trans->hmp;
686 	struct mount *mp = hmp->mp;
687 	hammer_volume_t volume;
688 	int vol_no;
689 	int error = 0;
690 
691 	/*
692 	 * Set each volume's new value of the vol_count field.
693 	 */
694 	HAMMER_VOLUME_NUMBER_FOREACH(hmp, vol_no) {
695 		volume = hammer_get_volume(hmp, vol_no, &error);
696 		KKASSERT(volume != NULL && error == 0);
697 		hammer_modify_volume_field(trans, volume, vol_count);
698 		volume->ondisk->vol_count = hmp->nvolumes;
699 		hammer_modify_volume_done(volume);
700 
701 		/*
702 		 * Only changes to the header of the root volume
703 		 * are automatically flushed to disk. For all
704 		 * other volumes that we modify we do it here.
705 		 *
706 		 * No interlock is needed, volume buffers are not
707 		 * messed with by bioops.
708 		 */
709 		if (volume != trans->rootvol && volume->io.modified) {
710 			hammer_crc_set_volume(volume->ondisk);
711 			hammer_io_flush(&volume->io, 0);
712 		}
713 
714 		hammer_rel_volume(volume, 0);
715 	}
716 
717 	/*
718 	 * Update the total number of big-blocks.
719 	 */
720 	hammer_modify_volume_field(trans, trans->rootvol, vol0_stat_bigblocks);
721 	trans->rootvol->ondisk->vol0_stat_bigblocks += stat->total_bigblocks;
722 	hammer_modify_volume_done(trans->rootvol);
723 
724 	/*
725 	 * Big-block count changed so recompute the total number of blocks.
726 	 */
727 	mp->mnt_stat.f_blocks = trans->rootvol->ondisk->vol0_stat_bigblocks *
728 				HAMMER_BUFFERS_PER_BIGBLOCK;
729 	mp->mnt_vstat.f_blocks = trans->rootvol->ondisk->vol0_stat_bigblocks *
730 				HAMMER_BUFFERS_PER_BIGBLOCK;
731 
732 	/*
733 	 * Update the total number of free big-blocks.
734 	 */
735 	hammer_modify_volume_field(trans, trans->rootvol,
736 		vol0_stat_freebigblocks);
737 	trans->rootvol->ondisk->vol0_stat_freebigblocks +=
738 		stat->total_free_bigblocks;
739 	hammer_modify_volume_done(trans->rootvol);
740 
741 	/*
742 	 * Update the copy in hmp.
743 	 */
744 	hmp->copy_stat_freebigblocks =
745 		trans->rootvol->ondisk->vol0_stat_freebigblocks;
746 
747 	return(error);
748 }
749