hammer expand: Also increase number of total bigblocks
[dragonfly.git] / sys / vfs / hammer / hammer_expand.c
CommitLineData
e27700cf
MN
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
90ecab35
MN
5 * by Matthew Dillon <dillon@backplane.com> and
6 * Michael Neumann <mneumann@ntecs.de>
e27700cf
MN
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"
90ecab35
MN
38#include <sys/fcntl.h>
39#include <sys/nlookup.h>
40#include <sys/buf.h>
41
42static int
2c794fb2
MN
43hammer_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,
69e6d11c 46 uint64_t *num_layer1_entries_p, uint64_t *layer1_free_blocks);
e27700cf
MN
47
48int
49hammer_ioc_expand(hammer_transaction_t trans, hammer_inode_t ip,
50 struct hammer_ioc_expand *expand)
51{
90ecab35
MN
52 struct hammer_mount *hmp = trans->hmp;
53 struct mount *mp = hmp->mp;
54 int error;
55
56 if (mp->mnt_flag & MNT_RDONLY) {
57 kprintf("Cannot expand read-only HAMMER filesystem\n");
58 return (EINVAL);
59 }
60
61 if (hmp->nvolumes + 1 >= HAMMER_MAX_VOLUMES) {
62 kprintf("Max number of HAMMER volumes exceeded\n");
63 return (EINVAL);
64 }
65
93d839df
MN
66 /*
67 * Find an unused volume number.
68 */
69 int free_vol_no = 0;
70 while (free_vol_no < HAMMER_MAX_VOLUMES &&
71 RB_LOOKUP(hammer_vol_rb_tree, &hmp->rb_vols_root, free_vol_no)) {
72 ++free_vol_no;
73 }
74 if (free_vol_no >= HAMMER_MAX_VOLUMES) {
75 kprintf("Max number of HAMMER volumes exceeded\n");
76 return (EINVAL);
77 }
78
2c794fb2 79 uint64_t num_layer1_entries = 0;
69e6d11c
MN
80 uint64_t *layer1_free_blocks =
81 kmalloc(1024 * sizeof(uint64_t), M_TEMP, M_WAITOK|M_ZERO);
82
90ecab35
MN
83 error = hammer_format_volume_header(
84 hmp,
2c794fb2 85 expand->device_name,
90ecab35 86 hmp->rootvol->ondisk->vol_name,
93d839df 87 free_vol_no,
90ecab35
MN
88 hmp->nvolumes+1,
89 expand->vol_size,
90 expand->boot_area_size,
2c794fb2 91 expand->mem_area_size,
69e6d11c
MN
92 &num_layer1_entries /* out param */,
93 layer1_free_blocks);
94 KKASSERT(num_layer1_entries < 1024);
2c794fb2
MN
95 if (error)
96 goto end;
90ecab35 97
2c794fb2
MN
98 error = hammer_install_volume(hmp, expand->device_name, NULL);
99 if (error)
100 goto end;
90ecab35 101
2c794fb2 102 ++hmp->nvolumes;
69e6d11c 103
2c794fb2
MN
104 hammer_sync_lock_sh(trans);
105 hammer_lock_ex(&hmp->blkmap_lock);
106
107 /*
108 * Set each volumes new value of the vol_count field.
109 */
110 for (int vol_no = 0; vol_no < HAMMER_MAX_VOLUMES; ++vol_no) {
111 hammer_volume_t volume;
112 volume = hammer_get_volume(hmp, vol_no, &error);
113 if (volume == NULL && error == ENOENT) {
114 /*
115 * Skip unused volume numbers
116 */
117 error = 0;
118 continue;
90ecab35 119 }
2c794fb2
MN
120 KKASSERT(error == 0);
121 hammer_modify_volume_field(trans, volume, vol_count);
122 volume->ondisk->vol_count = hmp->nvolumes;
123 hammer_modify_volume_done(volume);
124 hammer_rel_volume(volume, 0);
125 }
90ecab35 126
2c794fb2
MN
127 /*
128 * Assign Layer1 entries
129 */
69e6d11c
MN
130
131 hammer_volume_t root_volume = NULL;
132 hammer_blockmap_t freemap;
133
134 freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX];
135 root_volume = hammer_get_root_volume(hmp, &error);
136 KKASSERT(root_volume && error == 0);
137
2c794fb2 138 for (uint64_t i_layer1 = 0; i_layer1 < num_layer1_entries; i_layer1++) {
69e6d11c
MN
139 hammer_buffer_t buffer1 = NULL;
140 struct hammer_blockmap_layer1 *layer1;
141 hammer_off_t layer1_offset;
142
143 layer1_offset = freemap->phys_offset +
144 (free_vol_no * 1024L) *
145 sizeof(struct hammer_blockmap_layer1) + i_layer1;
146
147 layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1);
148 KKASSERT(layer1 != NULL && error == 0);
149 KKASSERT(layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL);
150
151 hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1));
152 bzero(layer1, sizeof(*layer1));
153 layer1->phys_offset = HAMMER_ENCODE_RAW_BUFFER(free_vol_no,
154 i_layer1 * HAMMER_LARGEBLOCK_SIZE);
155
156 layer1->blocks_free = layer1_free_blocks[i_layer1];
157 layer1->layer1_crc = crc32(layer1, HAMMER_LAYER1_CRCSIZE);
158
159 hammer_modify_buffer_done(buffer1);
160 if (buffer1)
161 hammer_rel_buffer(buffer1, 0);
162
1f595ae4
MN
163 /*
164 * Increase the total number of bigblocks
165 */
69e6d11c 166 hammer_modify_volume_field(trans, root_volume,
1f595ae4
MN
167 vol0_stat_bigblocks);
168 root_volume->ondisk->vol0_stat_bigblocks +=
169 layer1_free_blocks[i_layer1];
170 hammer_modify_volume_done(root_volume);
69e6d11c 171
1f595ae4
MN
172 /*
173 * Increase the number of free bigblocks
174 * (including the copy in hmp)
175 */
176 hammer_modify_volume_field(trans, root_volume,
177 vol0_stat_freebigblocks);
69e6d11c
MN
178 root_volume->ondisk->vol0_stat_freebigblocks +=
179 layer1_free_blocks[i_layer1];
180 hmp->copy_stat_freebigblocks =
181 root_volume->ondisk->vol0_stat_freebigblocks;
182 hammer_modify_volume_done(root_volume);
183 } /* for */
184
185 hammer_rel_volume(root_volume, 0);
90ecab35 186
2c794fb2
MN
187 hammer_unlock(&hmp->blkmap_lock);
188 hammer_sync_unlock(trans);
189
190end:
90ecab35
MN
191 if (error) {
192 kprintf("An error occured: %d\n", error);
193 }
69e6d11c
MN
194 if (layer1_free_blocks)
195 kfree(layer1_free_blocks, M_TEMP);
90ecab35
MN
196 return (error);
197}
198
199static int
2c794fb2
MN
200hammer_format_volume_header(struct hammer_mount *hmp, const char *dev_path,
201 const char *vol_name, int vol_no, int vol_count,
202 int64_t vol_size, int64_t boot_area_size, int64_t mem_area_size,
69e6d11c 203 uint64_t *num_layer1_entries_p, uint64_t *layer1_free_blocks)
90ecab35
MN
204{
205 struct vnode *devvp = NULL;
206 struct buf *bp = NULL;
207 struct nlookupdata nd;
208 struct hammer_volume_ondisk *ondisk;
209 int error;
210
211 /*
212 * Get the device vnode
213 */
2c794fb2 214 error = nlookup_init(&nd, dev_path, UIO_SYSSPACE, NLC_FOLLOW);
90ecab35
MN
215 if (error == 0)
216 error = nlookup(&nd);
217 if (error == 0)
218 error = cache_vref(&nd.nl_nch, nd.nl_cred, &devvp);
219 nlookup_done(&nd);
220
221 if (error == 0) {
222 if (vn_isdisk(devvp, &error)) {
223 error = vfs_mountedon(devvp);
224 }
225 }
226 if (error == 0 &&
227 count_udev(devvp->v_umajor, devvp->v_uminor) > 0) {
228 error = EBUSY;
229 }
230 if (error == 0) {
231 vn_lock(devvp, LK_EXCLUSIVE | LK_RETRY);
232 error = vinvalbuf(devvp, V_SAVE, 0, 0);
233 if (error == 0) {
234 error = VOP_OPEN(devvp, FREAD|FWRITE, FSCRED, NULL);
235 }
236 vn_unlock(devvp);
237 }
238 if (error) {
239 if (devvp)
240 vrele(devvp);
241 return (error);
242 }
243
244 /*
245 * Extract the volume number from the volume header and do various
246 * sanity checks.
247 */
248 KKASSERT(HAMMER_BUFSIZE >= sizeof(struct hammer_volume_ondisk));
249 error = bread(devvp, 0LL, HAMMER_BUFSIZE, &bp);
250 if (error || bp->b_bcount < sizeof(struct hammer_volume_ondisk))
251 goto late_failure;
252
253 ondisk = (struct hammer_volume_ondisk*) bp->b_data;
254
255 /*
256 * Note that we do NOT allow to use a device that contains
257 * a valid HAMMER signature. It has to be cleaned up with dd
258 * before.
259 */
260 if (ondisk->vol_signature == HAMMER_FSBUF_VOLUME) {
261 kprintf("hammer_expand: Formatting of valid HAMMER volume "
262 "%s denied. Erase with dd!\n", vol_name);
263 error = EFTYPE;
264 goto late_failure;
265 }
266
267 bzero(ondisk, sizeof(struct hammer_volume_ondisk));
268 ksnprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", vol_name);
269 ondisk->vol_fstype = hmp->rootvol->ondisk->vol_fstype;
270 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
271 ondisk->vol_fsid = hmp->fsid;
272 ondisk->vol_rootvol = hmp->rootvol->vol_no;
273 ondisk->vol_no = vol_no;
274 ondisk->vol_count = vol_count;
275 ondisk->vol_version = hmp->version;
276
277 /*
278 * Reserve space for (future) header junk, setup our poor-man's
279 * bigblock allocator.
280 */
281 int64_t vol_alloc = HAMMER_BUFSIZE * 16;
282
283 ondisk->vol_bot_beg = vol_alloc;
284 vol_alloc += boot_area_size;
285 ondisk->vol_mem_beg = vol_alloc;
286 vol_alloc += mem_area_size;
287
288 /*
289 * The remaining area is the zone 2 buffer allocation area. These
290 * buffers
291 */
292 ondisk->vol_buf_beg = vol_alloc;
293 ondisk->vol_buf_end = vol_size & ~(int64_t)HAMMER_BUFMASK;
294
295 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
296 kprintf("volume %d %s is too small to hold the volume header",
297 ondisk->vol_no, ondisk->vol_name);
298 error = EFTYPE;
299 goto late_failure;
300 }
301
302 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
303 HAMMER_BUFSIZE;
304 ondisk->vol_blocksize = HAMMER_BUFSIZE;
305
306 /*
307 * Write volume header to disk
308 */
309 error = bwrite(bp);
310 bp = NULL;
311
2c794fb2
MN
312 /*
313 * Initialize layer2 freemap
314 */
315
316 /*
317 * Determine the number of L1 entries we need to represent the
318 * space of the whole volume. Each L1 entry covers 4 TB of space
319 * (8MB * 2**19) and we need one L2 big block for each L1 entry.
320 * L1 entries are stored in the root volume.
321 */
322 hammer_off_t off_end = (ondisk->vol_buf_end - ondisk->vol_buf_beg)
323 & ~HAMMER_LARGEBLOCK_MASK64;
324 uint64_t num_layer1_entries = (off_end / HAMMER_BLOCKMAP_LAYER2) +
325 ((off_end & HAMMER_BLOCKMAP_LAYER2_MASK) == 0 ? 0 : 1);
326 *num_layer1_entries_p = num_layer1_entries;
327
2c794fb2
MN
328 /*
329 * We allocate all L2 big blocks sequentially from the start of
330 * the volume.
331 */
332 KKASSERT(off_end / HAMMER_LARGEBLOCK_SIZE >= num_layer1_entries);
333
334 hammer_off_t layer2_end = num_layer1_entries * HAMMER_LARGEBLOCK_SIZE;
335 hammer_off_t off = 0;
336 while (off < layer2_end) {
337 error = bread(devvp, ondisk->vol_buf_beg + off,
338 HAMMER_BUFSIZE, &bp);
339 if (error || bp->b_bcount != HAMMER_BUFSIZE)
340 goto late_failure;
341 struct hammer_blockmap_layer2 *layer2 = (void*)bp->b_data;
342
343 for (int i = 0; i < HAMMER_BUFSIZE / sizeof(*layer2); ++i) {
344
345 /* the bigblock described by the layer2 entry */
346 hammer_off_t bigblock_off = HAMMER_LARGEBLOCK_SIZE *
347 (off / sizeof(*layer2));
348
69e6d11c
MN
349 /*
350 * To which layer1 entry does the current layer2
351 * big block belong?
352 *
353 * We need this to calculate the free bigblocks
354 * which is required for the layer1.
355 */
356 uint64_t i_layer1 = HAMMER_BLOCKMAP_LAYER1_OFFSET(off) /
357 sizeof(struct hammer_blockmap_layer1);
358 KKASSERT(i_layer1 < 1024);
359
2c794fb2
MN
360 bzero(layer2, sizeof(*layer2));
361
362 if ((off & HAMMER_LARGEBLOCK_SIZE) == bigblock_off) {
363 /*
364 * Bigblock is part of the layer2 freemap
365 */
366 layer2->zone = HAMMER_ZONE_FREEMAP_INDEX;
367 layer2->append_off = HAMMER_LARGEBLOCK_SIZE;
368 layer2->bytes_free = 0;
369 } else if (bigblock_off < off_end) {
370 layer2->zone = 0;
371 layer2->append_off = 0;
372 layer2->bytes_free = HAMMER_LARGEBLOCK_SIZE;
69e6d11c 373 ++layer1_free_blocks[i_layer1];
2c794fb2
MN
374 } else {
375 layer2->zone = HAMMER_ZONE_UNAVAIL_INDEX;
376 layer2->append_off = HAMMER_LARGEBLOCK_SIZE;
377 layer2->bytes_free = 0;
378 }
379 layer2->entry_crc = crc32(layer2, HAMMER_LAYER2_CRCSIZE);
380 off += sizeof(*layer2);
381 ++layer2;
382 }
383
384 error = bwrite(bp);
385 bp = NULL;
386 if (error)
387 goto late_failure;
388 }
389
90ecab35
MN
390late_failure:
391 if (bp)
392 brelse(bp);
393 VOP_CLOSE(devvp, FREAD|FWRITE);
394 if (devvp)
395 vrele(devvp);
396 return (error);
e27700cf 397}