2 * Copyright (c) 2009 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> and
6 * Michael Neumann <mneumann@ntecs.de>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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
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.
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
38 #include <sys/fcntl.h>
39 #include <sys/nlookup.h>
43 hammer_setup_device(struct vnode **devvpp, const char *dev_path, int ronly);
46 hammer_close_device(struct vnode **devvpp, int ronly);
49 hammer_format_volume_header(struct hammer_mount *hmp, struct vnode *devvp,
50 const char *vol_name, int vol_no, int vol_count,
51 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size);
54 hammer_format_freemap(struct hammer_mount *hmp,
55 hammer_transaction_t trans,
56 hammer_volume_t volume);
59 hammer_format_layer2_chunk(struct hammer_mount *hmp,
60 hammer_transaction_t trans,
61 hammer_off_t phys_offset,
62 hammer_off_t aligned_buf_end_off,
63 hammer_buffer_t *bufferp,
67 hammer_set_layer1_entry(struct hammer_mount *hmp,
68 hammer_transaction_t trans,
69 hammer_off_t phys_offset,
70 uint64_t free_bigblocks,
71 hammer_blockmap_t freemap,
72 hammer_buffer_t *bufferp,
76 hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
77 struct hammer_ioc_expand *expand)
79 struct hammer_mount *hmp = trans->hmp;
80 struct mount *mp = hmp->mp;
81 hammer_volume_t volume;
82 hammer_volume_t root_volume;
85 if (mp->mnt_flag & MNT_RDONLY) {
86 kprintf("Cannot expand read-only HAMMER filesystem\n");
90 if (hmp->nvolumes + 1 >= HAMMER_MAX_VOLUMES) {
91 kprintf("Max number of HAMMER volumes exceeded\n");
96 * Find an unused volume number.
99 while (free_vol_no < HAMMER_MAX_VOLUMES &&
100 RB_LOOKUP(hammer_vol_rb_tree, &hmp->rb_vols_root, free_vol_no)) {
103 if (free_vol_no >= HAMMER_MAX_VOLUMES) {
104 kprintf("Max number of HAMMER volumes exceeded\n");
108 struct vnode *devvp = NULL;
109 error = hammer_setup_device(&devvp, expand->device_name, 0);
113 error = hammer_format_volume_header(
116 hmp->rootvol->ondisk->vol_name,
120 expand->boot_area_size,
121 expand->mem_area_size);
122 hammer_close_device(&devvp, 0);
126 error = hammer_install_volume(hmp, expand->device_name, NULL);
130 hammer_sync_lock_sh(trans);
131 hammer_lock_ex(&hmp->blkmap_lock);
136 * Set each volumes new value of the vol_count field.
138 for (int vol_no = 0; vol_no < HAMMER_MAX_VOLUMES; ++vol_no) {
139 if (vol_no == free_vol_no)
142 volume = hammer_get_volume(hmp, vol_no, &error);
143 if (volume == NULL && error == ENOENT) {
145 * Skip unused volume numbers
150 KKASSERT(volume != NULL && error == 0);
151 hammer_modify_volume_field(trans, volume, vol_count);
152 volume->ondisk->vol_count = hmp->nvolumes;
153 hammer_modify_volume_done(volume);
154 hammer_rel_volume(volume, 0);
157 volume = hammer_get_volume(hmp, free_vol_no, &error);
158 KKASSERT(volume != NULL && error == 0);
159 root_volume = hammer_get_root_volume(hmp, &error);
160 KKASSERT(root_volume != NULL && error == 0);
162 uint64_t total_free_bigblocks =
163 hammer_format_freemap(hmp, trans, volume);
166 * Increase the total number of bigblocks
168 hammer_modify_volume_field(trans, root_volume,
169 vol0_stat_bigblocks);
170 root_volume->ondisk->vol0_stat_bigblocks += total_free_bigblocks;
171 hammer_modify_volume_done(root_volume);
174 * Increase the number of free bigblocks
175 * (including the copy in hmp)
177 hammer_modify_volume_field(trans, root_volume,
178 vol0_stat_freebigblocks);
179 root_volume->ondisk->vol0_stat_freebigblocks += total_free_bigblocks;
180 hmp->copy_stat_freebigblocks =
181 root_volume->ondisk->vol0_stat_freebigblocks;
182 hammer_modify_volume_done(root_volume);
184 hammer_rel_volume(root_volume, 0);
185 hammer_rel_volume(volume, 0);
187 hammer_unlock(&hmp->blkmap_lock);
188 hammer_sync_unlock(trans);
192 kprintf("An error occurred: %d\n", error);
197 hammer_format_freemap(struct hammer_mount *hmp,
198 hammer_transaction_t trans,
199 hammer_volume_t volume)
201 hammer_off_t phys_offset;
202 hammer_buffer_t buffer = NULL;
203 hammer_blockmap_t freemap;
204 hammer_off_t aligned_buf_end_off;
205 uint64_t free_bigblocks;
206 uint64_t total_free_bigblocks;
209 total_free_bigblocks = 0;
212 * Calculate the usable size of the new volume, which
213 * must be aligned at a bigblock (8 MB) boundary.
215 aligned_buf_end_off = HAMMER_ENCODE_RAW_BUFFER(volume->ondisk->vol_no,
216 (volume->ondisk->vol_buf_end - volume->ondisk->vol_buf_beg)
217 & ~HAMMER_LARGEBLOCK_MASK64);
219 freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
222 * Iterate the volume's address space in chunks of 4 TB,
223 * where each chunk consists of at least one physically
224 * available 8 MB bigblock.
226 * For each chunk we need one L1 entry and one L2 bigblock.
227 * We use the first bigblock of each chunk as L2 block.
229 for (phys_offset = HAMMER_ENCODE_RAW_BUFFER(volume->ondisk->vol_no, 0);
230 phys_offset < aligned_buf_end_off;
231 phys_offset += HAMMER_BLOCKMAP_LAYER2) {
233 free_bigblocks = hammer_format_layer2_chunk(hmp, trans,
234 phys_offset, aligned_buf_end_off, &buffer, &error);
235 KKASSERT(error == 0);
237 hammer_set_layer1_entry(hmp, trans, phys_offset,
238 free_bigblocks, freemap, &buffer, &error);
239 KKASSERT(error == 0);
241 total_free_bigblocks += free_bigblocks;
245 hammer_rel_buffer(buffer, 0);
249 return total_free_bigblocks;
253 * Format the L2 bigblock representing a 4 TB chunk.
255 * Returns the number of free bigblocks.
258 hammer_format_layer2_chunk(struct hammer_mount *hmp,
259 hammer_transaction_t trans,
260 hammer_off_t phys_offset,
261 hammer_off_t aligned_buf_end_off,
262 hammer_buffer_t *bufferp,
265 uint64_t free_bigblocks = 0;
266 hammer_off_t block_off;
267 hammer_off_t layer2_offset;
268 struct hammer_blockmap_layer2 *layer2;
271 block_off < HAMMER_BLOCKMAP_LAYER2;
272 block_off += HAMMER_LARGEBLOCK_SIZE) {
273 layer2_offset = phys_offset +
274 HAMMER_BLOCKMAP_LAYER2_OFFSET(block_off);
275 layer2 = hammer_bread(hmp, layer2_offset, errorp, bufferp);
277 return free_bigblocks;
281 hammer_modify_buffer(trans, *bufferp, layer2, sizeof(*layer2));
282 bzero(layer2, sizeof(*layer2));
284 if (block_off == 0) {
286 * The first entry represents the L2 bigblock itself.
288 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX;
289 layer2->append_off = HAMMER_LARGEBLOCK_SIZE;
290 layer2->bytes_free = 0;
291 } else if (phys_offset + block_off < aligned_buf_end_off) {
293 layer2->append_off = 0;
294 layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE;
298 * Bigblock outside of physically available space
300 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX;
301 layer2->append_off = HAMMER_LARGEBLOCK_SIZE;
302 layer2->bytes_free = 0;
304 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE);
306 hammer_modify_buffer_done(*bufferp);
309 return free_bigblocks;
313 hammer_set_layer1_entry(struct hammer_mount *hmp,
314 hammer_transaction_t trans,
315 hammer_off_t phys_offset,
316 uint64_t free_bigblocks,
317 hammer_blockmap_t freemap,
318 hammer_buffer_t *bufferp,
321 struct hammer_blockmap_layer1 *layer1;
322 hammer_off_t layer1_offset;
324 layer1_offset = freemap->phys_offset +
325 HAMMER_BLOCKMAP_LAYER1_OFFSET(phys_offset);
326 layer1 = hammer_bread(hmp, layer1_offset, errorp, bufferp);
330 KKASSERT(layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL);
332 hammer_modify_buffer(trans, *bufferp, layer1, sizeof(*layer1));
333 bzero(layer1, sizeof(*layer1));
334 layer1->phys_offset = phys_offset;
335 layer1->blocks_free = free_bigblocks;
336 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE);
338 hammer_modify_buffer_done(*bufferp);
342 hammer_setup_device(struct vnode **devvpp, const char *dev_path, int ronly)
345 struct nlookupdata nd;
348 * Get the device vnode
350 if (*devvpp == NULL) {
351 error = nlookup_init(&nd, dev_path, UIO_SYSSPACE, NLC_FOLLOW);
353 error = nlookup(&nd);
355 error = cache_vref(&nd.nl_nch, nd.nl_cred, devvpp);
362 if (vn_isdisk(*devvpp, &error)) {
363 error = vfs_mountedon(*devvpp);
366 if (error == 0 && vcount(*devvpp) > 0)
369 vn_lock(*devvpp, LK_EXCLUSIVE | LK_RETRY);
370 error = vinvalbuf(*devvpp, V_SAVE, 0, 0);
372 error = VOP_OPEN(*devvpp,
373 (ronly ? FREAD : FREAD|FWRITE),
378 if (error && *devvpp) {
386 hammer_close_device(struct vnode **devvpp, int ronly)
388 VOP_CLOSE(*devvpp, (ronly ? FREAD : FREAD|FWRITE));
390 vinvalbuf(*devvpp, ronly ? 0 : V_SAVE, 0, 0);
397 hammer_format_volume_header(struct hammer_mount *hmp, struct vnode *devvp,
398 const char *vol_name, int vol_no, int vol_count,
399 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size)
401 struct buf *bp = NULL;
402 struct hammer_volume_ondisk *ondisk;
406 * Extract the volume number from the volume header and do various
409 KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
410 error = bread(devvp, 0LL, HAMMER_BUFSIZE, &bp);
411 if (error || bp->b_bcount < sizeof(struct hammer_volume_ondisk))
414 ondisk = (struct hammer_volume_ondisk*) bp->b_data;
417 * Note that we do NOT allow to use a device that contains
418 * a valid HAMMER signature. It has to be cleaned up with dd
421 if (ondisk->vol_signature == HAMMER_FSBUF_VOLUME) {
422 kprintf("hammer_expand: Formatting of valid HAMMER volume "
423 "%s denied. Erase with dd!\n", vol_name);
428 bzero(ondisk, sizeof(struct hammer_volume_ondisk));
429 ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
430 ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
431 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
432 ondisk->vol_fsid = hmp->fsid;
433 ondisk->vol_rootvol = hmp->rootvol->vol_no;
434 ondisk->vol_no = vol_no;
435 ondisk->vol_count = vol_count;
436 ondisk->vol_version = hmp->version;
439 * Reserve space for (future) header junk, setup our poor-man's
440 * bigblock allocator.
442 int64_t vol_alloc = HAMMER_BUFSIZE * 16;
444 ondisk->vol_bot_beg = vol_alloc;
445 vol_alloc += boot_area_size;
446 ondisk->vol_mem_beg = vol_alloc;
447 vol_alloc += mem_area_size;
450 * The remaining area is the zone 2 buffer allocation area. These
453 ondisk->vol_buf_beg = vol_alloc;
454 ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
456 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
457 kprintf("volume %d %s is too small to hold the volume header",
458 ondisk->vol_no, ondisk->vol_name);
463 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
465 ondisk->vol_blocksize = HAMMER_BUFSIZE;
468 * Write volume header to disk