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_format_volume_header(struct hammer_mount *hmp, const char *dev_path,
44 const char *vol_name, int vol_no, int vol_count,
45 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size,
46 uint64_t *num_layer1_entries_p);
49 hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
50 struct hammer_ioc_expand *expand)
52 struct hammer_mount *hmp = trans->hmp;
53 struct mount *mp = hmp->mp;
56 if (mp->mnt_flag & MNT_RDONLY) {
57 kprintf("Cannot expand read-only HAMMER filesystem\n");
61 if (hmp->nvolumes + 1 >= HAMMER_MAX_VOLUMES) {
62 kprintf("Max number of HAMMER volumes exceeded\n");
67 * Find an unused volume number.
70 while (free_vol_no < HAMMER_MAX_VOLUMES &&
71 RB_LOOKUP(hammer_vol_rb_tree, &hmp->rb_vols_root, free_vol_no)) {
74 if (free_vol_no >= HAMMER_MAX_VOLUMES) {
75 kprintf("Max number of HAMMER volumes exceeded\n");
79 uint64_t num_layer1_entries = 0;
80 error = hammer_format_volume_header(
83 hmp->rootvol->ondisk->vol_name,
87 expand->boot_area_size,
88 expand->mem_area_size,
89 &num_layer1_entries /* out param */);
93 error = hammer_install_volume(hmp, expand->device_name, NULL);
98 hammer_sync_lock_sh(trans);
99 hammer_lock_ex(&hmp->blkmap_lock);
102 * Set each volumes new value of the vol_count field.
104 for (int vol_no = 0; vol_no < HAMMER_MAX_VOLUMES; ++vol_no) {
105 hammer_volume_t volume;
106 volume = hammer_get_volume(hmp, vol_no, &error);
107 if (volume == NULL && error == ENOENT) {
109 * Skip unused volume numbers
114 KKASSERT(error == 0);
115 hammer_modify_volume_field(trans, volume, vol_count);
116 volume->ondisk->vol_count = hmp->nvolumes;
117 hammer_modify_volume_done(volume);
118 hammer_rel_volume(volume, 0);
122 * Assign Layer1 entries
124 for (uint64_t i_layer1 = 0; i_layer1 < num_layer1_entries; i_layer1++) {
128 hammer_unlock(&hmp->blkmap_lock);
129 hammer_sync_unlock(trans);
133 kprintf("An error occured: %d\n", error);
139 hammer_format_volume_header(struct hammer_mount *hmp, const char *dev_path,
140 const char *vol_name, int vol_no, int vol_count,
141 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size,
142 uint64_t *num_layer1_entries_p)
144 struct vnode *devvp = NULL;
145 struct buf *bp = NULL;
146 struct nlookupdata nd;
147 struct hammer_volume_ondisk *ondisk;
151 * Get the device vnode
153 error = nlookup_init(&nd, dev_path, UIO_SYSSPACE, NLC_FOLLOW);
155 error = nlookup(&nd);
157 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
161 if (vn_isdisk(devvp, &error)) {
162 error = vfs_mountedon(devvp);
166 count_udev(devvp->v_umajor, devvp->v_uminor) > 0) {
170 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
171 error = vinvalbuf(devvp, V_SAVE, 0, 0);
173 error = VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL);
184 * Extract the volume number from the volume header and do various
187 KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
188 error = bread(devvp, 0LL, HAMMER_BUFSIZE, &bp);
189 if (error || bp->b_bcount < sizeof(struct hammer_volume_ondisk))
192 ondisk = (struct hammer_volume_ondisk*) bp->b_data;
195 * Note that we do NOT allow to use a device that contains
196 * a valid HAMMER signature. It has to be cleaned up with dd
199 if (ondisk->vol_signature == HAMMER_FSBUF_VOLUME) {
200 kprintf("hammer_expand: Formatting of valid HAMMER volume "
201 "%s denied. Erase with dd!\n", vol_name);
206 bzero(ondisk, sizeof(struct hammer_volume_ondisk));
207 ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
208 ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
209 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
210 ondisk->vol_fsid = hmp->fsid;
211 ondisk->vol_rootvol = hmp->rootvol->vol_no;
212 ondisk->vol_no = vol_no;
213 ondisk->vol_count = vol_count;
214 ondisk->vol_version = hmp->version;
217 * Reserve space for (future) header junk, setup our poor-man's
218 * bigblock allocator.
220 int64_t vol_alloc = HAMMER_BUFSIZE * 16;
222 ondisk->vol_bot_beg = vol_alloc;
223 vol_alloc += boot_area_size;
224 ondisk->vol_mem_beg = vol_alloc;
225 vol_alloc += mem_area_size;
228 * The remaining area is the zone 2 buffer allocation area. These
231 ondisk->vol_buf_beg = vol_alloc;
232 ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
234 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
235 kprintf("volume %d %s is too small to hold the volume header",
236 ondisk->vol_no, ondisk->vol_name);
241 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
243 ondisk->vol_blocksize = HAMMER_BUFSIZE;
246 * Write volume header to disk
252 * Initialize layer2 freemap
256 * Determine the number of L1 entries we need to represent the
257 * space of the whole volume. Each L1 entry covers 4 TB of space
258 * (8MB * 2**19) and we need one L2 big block for each L1 entry.
259 * L1 entries are stored in the root volume.
261 hammer_off_t off_end = (ondisk->vol_buf_end - ondisk->vol_buf_beg)
262 & ~HAMMER_LARGEBLOCK_MASK64;
263 uint64_t num_layer1_entries = (off_end / HAMMER_BLOCKMAP_LAYER2) +
264 ((off_end & HAMMER_BLOCKMAP_LAYER2_MASK) == 0 ? 0 : 1);
265 *num_layer1_entries_p = num_layer1_entries;
267 kprintf("num_layer1_entries: %d\n", num_layer1_entries);
270 * We allocate all L2 big blocks sequentially from the start of
273 KKASSERT(off_end / HAMMER_LARGEBLOCK_SIZE >= num_layer1_entries);
275 hammer_off_t layer2_end = num_layer1_entries * HAMMER_LARGEBLOCK_SIZE;
276 hammer_off_t off = 0;
277 while (off < layer2_end) {
278 error = bread(devvp, ondisk->vol_buf_beg + off,
279 HAMMER_BUFSIZE, &bp);
280 if (error || bp->b_bcount != HAMMER_BUFSIZE)
282 struct hammer_blockmap_layer2 *layer2 = (void*)bp->b_data;
284 for (int i = 0; i < HAMMER_BUFSIZE / sizeof(*layer2); ++i) {
286 /* the bigblock described by the layer2 entry */
287 hammer_off_t bigblock_off = HAMMER_LARGEBLOCK_SIZE *
288 (off / sizeof(*layer2));
290 bzero(layer2, sizeof(*layer2));
292 if ((off & HAMMER_LARGEBLOCK_SIZE) == bigblock_off) {
294 * Bigblock is part of the layer2 freemap
296 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX;
297 layer2->append_off = HAMMER_LARGEBLOCK_SIZE;
298 layer2->bytes_free = 0;
299 } else if (bigblock_off < off_end) {
301 layer2->append_off = 0;
302 layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE;
304 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX;
305 layer2->append_off = HAMMER_LARGEBLOCK_SIZE;
306 layer2->bytes_free = 0;
308 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE);
309 off += sizeof(*layer2);
322 VOP_CLOSE(devvp, FREAD|FWRITE);