Commit | Line | Data |
---|---|---|
40043e7f MD |
1 | /* |
2 | * Copyright (c) 2008 The DragonFly Project. All rights reserved. | |
745703c7 | 3 | * |
40043e7f MD |
4 | * This code is derived from software contributed to The DragonFly Project |
5 | * by Matthew Dillon <dillon@backplane.com> | |
745703c7 | 6 | * |
40043e7f MD |
7 | * Redistribution and use in source and binary forms, with or without |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
745703c7 | 10 | * |
40043e7f MD |
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. | |
745703c7 | 20 | * |
40043e7f MD |
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. | |
40043e7f MD |
33 | */ |
34 | ||
35 | /* | |
36 | * HAMMER blockmap | |
37 | */ | |
97fb61c0 TK |
38 | #include <vm/vm_page2.h> |
39 | ||
40043e7f MD |
40 | #include "hammer.h" |
41 | ||
0832c9bb | 42 | static int hammer_res_rb_compare(hammer_reserve_t res1, hammer_reserve_t res2); |
362ec2dc | 43 | static void hammer_reserve_setdelay_offset(hammer_mount_t hmp, |
1ce12d35 | 44 | hammer_off_t base_offset, int zone, |
21e9e7d5 | 45 | hammer_blockmap_layer2_t layer2); |
362ec2dc | 46 | static void hammer_reserve_setdelay(hammer_mount_t hmp, hammer_reserve_t resv); |
0871ec5c | 47 | static int hammer_check_volume(hammer_mount_t, hammer_off_t*); |
ff2ee76c | 48 | static void hammer_skip_volume(hammer_off_t *offsetp); |
0832c9bb MD |
49 | |
50 | /* | |
51 | * Reserved big-blocks red-black tree support | |
52 | */ | |
53 | RB_GENERATE2(hammer_res_rb_tree, hammer_reserve, rb_node, | |
54 | hammer_res_rb_compare, hammer_off_t, zone_offset); | |
55 | ||
56 | static int | |
57 | hammer_res_rb_compare(hammer_reserve_t res1, hammer_reserve_t res2) | |
58 | { | |
59 | if (res1->zone_offset < res2->zone_offset) | |
60 | return(-1); | |
61 | if (res1->zone_offset > res2->zone_offset) | |
62 | return(1); | |
63 | return(0); | |
64 | } | |
bf686dbe | 65 | |
40043e7f MD |
66 | /* |
67 | * Allocate bytes from a zone | |
68 | */ | |
69 | hammer_off_t | |
df2ccbac MD |
70 | hammer_blockmap_alloc(hammer_transaction_t trans, int zone, int bytes, |
71 | hammer_off_t hint, int *errorp) | |
40043e7f | 72 | { |
0832c9bb | 73 | hammer_mount_t hmp; |
40043e7f | 74 | hammer_volume_t root_volume; |
cb51be26 MD |
75 | hammer_blockmap_t blockmap; |
76 | hammer_blockmap_t freemap; | |
0832c9bb | 77 | hammer_reserve_t resv; |
21e9e7d5 TK |
78 | hammer_blockmap_layer1_t layer1; |
79 | hammer_blockmap_layer2_t layer2; | |
f03c9cf4 MD |
80 | hammer_buffer_t buffer1 = NULL; |
81 | hammer_buffer_t buffer2 = NULL; | |
82 | hammer_buffer_t buffer3 = NULL; | |
c3be93f2 | 83 | hammer_off_t tmp_offset; |
f03c9cf4 | 84 | hammer_off_t next_offset; |
0832c9bb | 85 | hammer_off_t result_offset; |
c3be93f2 MD |
86 | hammer_off_t layer1_offset; |
87 | hammer_off_t layer2_offset; | |
cb51be26 | 88 | hammer_off_t base_off; |
f03c9cf4 | 89 | int loops = 0; |
df301614 | 90 | int offset; /* offset within big-block */ |
df2ccbac | 91 | int use_hint; |
40043e7f | 92 | |
0832c9bb | 93 | hmp = trans->hmp; |
40043e7f MD |
94 | |
95 | /* | |
96 | * Deal with alignment and buffer-boundary issues. | |
97 | * | |
98 | * Be careful, certain primary alignments are used below to allocate | |
99 | * new blockmap blocks. | |
100 | */ | |
f097bffe | 101 | bytes = HAMMER_DATA_DOALIGN(bytes); |
4a2796f3 | 102 | KKASSERT(bytes > 0 && bytes <= HAMMER_XBUFSIZE); |
f6d29b27 | 103 | KKASSERT(hammer_is_index_record(zone)); |
bf686dbe MD |
104 | |
105 | /* | |
cb51be26 | 106 | * Setup |
bf686dbe | 107 | */ |
cb51be26 MD |
108 | root_volume = trans->rootvol; |
109 | *errorp = 0; | |
110 | blockmap = &hmp->blockmap[zone]; | |
111 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; | |
112 | KKASSERT(HAMMER_ZONE_DECODE(blockmap->next_offset) == zone); | |
113 | ||
df2ccbac MD |
114 | /* |
115 | * Use the hint if we have one. | |
116 | */ | |
117 | if (hint && HAMMER_ZONE_DECODE(hint) == zone) { | |
f097bffe | 118 | next_offset = HAMMER_DATA_DOALIGN_WITH(hammer_off_t, hint); |
df2ccbac MD |
119 | use_hint = 1; |
120 | } else { | |
121 | next_offset = blockmap->next_offset; | |
122 | use_hint = 0; | |
123 | } | |
cb51be26 | 124 | again: |
df2ccbac MD |
125 | |
126 | /* | |
127 | * use_hint is turned off if we leave the hinted big-block. | |
128 | */ | |
129 | if (use_hint && ((next_offset ^ hint) & ~HAMMER_HINTBLOCK_MASK64)) { | |
130 | next_offset = blockmap->next_offset; | |
131 | use_hint = 0; | |
132 | } | |
133 | ||
0832c9bb | 134 | /* |
cb51be26 | 135 | * Check for wrap |
0832c9bb | 136 | */ |
4a2796f3 | 137 | if (next_offset == HAMMER_ZONE_ENCODE(zone + 1, 0)) { |
cb51be26 | 138 | if (++loops == 2) { |
0f243a86 TK |
139 | hmkprintf(hmp, "No space left for zone %d " |
140 | "allocation\n", zone); | |
cb51be26 MD |
141 | result_offset = 0; |
142 | *errorp = ENOSPC; | |
df301614 | 143 | goto failed; |
cb51be26 MD |
144 | } |
145 | next_offset = HAMMER_ZONE_ENCODE(zone, 0); | |
146 | } | |
0832c9bb | 147 | |
f03c9cf4 | 148 | /* |
4a2796f3 | 149 | * The allocation request may not cross a buffer boundary. Special |
e04ee2de | 150 | * large allocations must not cross a big-block boundary. |
f03c9cf4 | 151 | */ |
bf686dbe | 152 | tmp_offset = next_offset + bytes - 1; |
4a2796f3 MD |
153 | if (bytes <= HAMMER_BUFSIZE) { |
154 | if ((next_offset ^ tmp_offset) & ~HAMMER_BUFMASK64) { | |
155 | next_offset = tmp_offset & ~HAMMER_BUFMASK64; | |
156 | goto again; | |
157 | } | |
158 | } else { | |
e04ee2de TK |
159 | if ((next_offset ^ tmp_offset) & ~HAMMER_BIGBLOCK_MASK64) { |
160 | next_offset = tmp_offset & ~HAMMER_BIGBLOCK_MASK64; | |
4a2796f3 MD |
161 | goto again; |
162 | } | |
bf686dbe | 163 | } |
e04ee2de | 164 | offset = (int)next_offset & HAMMER_BIGBLOCK_MASK; |
40043e7f MD |
165 | |
166 | /* | |
cb51be26 | 167 | * Dive layer 1. |
40043e7f | 168 | */ |
cb51be26 | 169 | layer1_offset = freemap->phys_offset + |
f03c9cf4 | 170 | HAMMER_BLOCKMAP_LAYER1_OFFSET(next_offset); |
865c9609 | 171 | |
0832c9bb | 172 | layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer1); |
cdb6e4e6 MD |
173 | if (*errorp) { |
174 | result_offset = 0; | |
175 | goto failed; | |
176 | } | |
19619882 | 177 | |
c3be93f2 | 178 | /* |
cb51be26 | 179 | * Check CRC. |
c3be93f2 | 180 | */ |
4c09d9c4 | 181 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
db9f9d7f | 182 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 183 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 184 | hpanic("CRC FAILED: LAYER1"); |
db9f9d7f | 185 | hammer_unlock(&hmp->blkmap_lock); |
40043e7f | 186 | } |
40043e7f MD |
187 | |
188 | /* | |
745703c7 | 189 | * If we are at a big-block boundary and layer1 indicates no |
a981af19 | 190 | * free big-blocks, then we cannot allocate a new big-block in |
cb51be26 | 191 | * layer2, skip to the next layer1 entry. |
40043e7f | 192 | */ |
df301614 | 193 | if (offset == 0 && layer1->blocks_free == 0) { |
b7f51e3b | 194 | next_offset = HAMMER_ZONE_LAYER1_NEXT_OFFSET(next_offset); |
0871ec5c TK |
195 | if (hammer_check_volume(hmp, &next_offset)) { |
196 | result_offset = 0; | |
197 | goto failed; | |
198 | } | |
f03c9cf4 MD |
199 | goto again; |
200 | } | |
cb51be26 | 201 | KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); |
40043e7f | 202 | |
c47d84e8 | 203 | /* |
ff2ee76c | 204 | * Skip the whole volume if it is pointing to a layer2 big-block |
c47d84e8 MN |
205 | * on a volume that we are currently trying to remove from the |
206 | * file-system. This is used by the volume-del code together with | |
207 | * the reblocker to free up a volume. | |
208 | */ | |
afb71584 | 209 | if (HAMMER_VOL_DECODE(layer1->phys_offset) == hmp->volume_to_remove) { |
ff2ee76c | 210 | hammer_skip_volume(&next_offset); |
c47d84e8 MN |
211 | goto again; |
212 | } | |
213 | ||
c3be93f2 | 214 | /* |
e04ee2de | 215 | * Dive layer 2, each entry represents a big-block. |
c3be93f2 | 216 | */ |
f03c9cf4 MD |
217 | layer2_offset = layer1->phys_offset + |
218 | HAMMER_BLOCKMAP_LAYER2_OFFSET(next_offset); | |
0832c9bb | 219 | layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer2); |
cdb6e4e6 MD |
220 | if (*errorp) { |
221 | result_offset = 0; | |
222 | goto failed; | |
223 | } | |
f03c9cf4 | 224 | |
19619882 | 225 | /* |
db9f9d7f MD |
226 | * Check CRC. This can race another thread holding the lock |
227 | * and in the middle of modifying layer2. | |
19619882 | 228 | */ |
4c09d9c4 | 229 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
db9f9d7f | 230 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 231 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 232 | hpanic("CRC FAILED: LAYER2"); |
db9f9d7f | 233 | hammer_unlock(&hmp->blkmap_lock); |
19619882 MD |
234 | } |
235 | ||
cb51be26 | 236 | /* |
df301614 | 237 | * Skip the layer if the zone is owned by someone other then us. |
cb51be26 | 238 | */ |
df301614 | 239 | if (layer2->zone && layer2->zone != zone) { |
e04ee2de | 240 | next_offset += (HAMMER_BIGBLOCK_SIZE - offset); |
df301614 MD |
241 | goto again; |
242 | } | |
243 | if (offset < layer2->append_off) { | |
244 | next_offset += layer2->append_off - offset; | |
4a2796f3 MD |
245 | goto again; |
246 | } | |
247 | ||
b4f86ea3 | 248 | #if 0 |
df2ccbac MD |
249 | /* |
250 | * If operating in the current non-hint blockmap block, do not | |
251 | * allow it to get over-full. Also drop any active hinting so | |
252 | * blockmap->next_offset is updated at the end. | |
253 | * | |
254 | * We do this for B-Tree and meta-data allocations to provide | |
255 | * localization for updates. | |
256 | */ | |
91c2d0cb | 257 | if ((zone == HAMMER_ZONE_BTREE_INDEX || |
df2ccbac | 258 | zone == HAMMER_ZONE_META_INDEX) && |
e04ee2de | 259 | offset >= HAMMER_BIGBLOCK_OVERFILL && |
9a620123 | 260 | !((next_offset ^ blockmap->next_offset) & ~HAMMER_BIGBLOCK_MASK64)) { |
e04ee2de TK |
261 | if (offset >= HAMMER_BIGBLOCK_OVERFILL) { |
262 | next_offset += (HAMMER_BIGBLOCK_SIZE - offset); | |
df2ccbac MD |
263 | use_hint = 0; |
264 | goto again; | |
265 | } | |
266 | } | |
b4f86ea3 | 267 | #endif |
df2ccbac | 268 | |
4a2796f3 | 269 | /* |
df301614 MD |
270 | * We need the lock from this point on. We have to re-check zone |
271 | * ownership after acquiring the lock and also check for reservations. | |
272 | */ | |
273 | hammer_lock_ex(&hmp->blkmap_lock); | |
274 | ||
275 | if (layer2->zone && layer2->zone != zone) { | |
276 | hammer_unlock(&hmp->blkmap_lock); | |
e04ee2de | 277 | next_offset += (HAMMER_BIGBLOCK_SIZE - offset); |
df301614 MD |
278 | goto again; |
279 | } | |
280 | if (offset < layer2->append_off) { | |
281 | hammer_unlock(&hmp->blkmap_lock); | |
282 | next_offset += layer2->append_off - offset; | |
283 | goto again; | |
284 | } | |
285 | ||
286 | /* | |
a981af19 | 287 | * The big-block might be reserved by another zone. If it is reserved |
df301614 | 288 | * by our zone we may have to move next_offset past the append_off. |
4a2796f3 | 289 | */ |
d369f237 | 290 | base_off = hammer_xlate_to_zone2(next_offset & ~HAMMER_BIGBLOCK_MASK64); |
4a2796f3 MD |
291 | resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, base_off); |
292 | if (resv) { | |
4a2796f3 | 293 | if (resv->zone != zone) { |
df301614 | 294 | hammer_unlock(&hmp->blkmap_lock); |
c4e15ea8 | 295 | next_offset = HAMMER_ZONE_LAYER2_NEXT_OFFSET(next_offset); |
f03c9cf4 MD |
296 | goto again; |
297 | } | |
df301614 MD |
298 | if (offset < resv->append_off) { |
299 | hammer_unlock(&hmp->blkmap_lock); | |
300 | next_offset += resv->append_off - offset; | |
301 | goto again; | |
302 | } | |
1ce12d35 | 303 | ++resv->refs; |
cb51be26 MD |
304 | } |
305 | ||
4a2796f3 MD |
306 | /* |
307 | * Ok, we can allocate out of this layer2 big-block. Assume ownership | |
308 | * of the layer for real. At this point we've validated any | |
309 | * reservation that might exist and can just ignore resv. | |
310 | */ | |
cb51be26 | 311 | if (layer2->zone == 0) { |
f03c9cf4 | 312 | /* |
a981af19 | 313 | * Assign the big-block to our zone |
f03c9cf4 | 314 | */ |
653fa4cd | 315 | hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1)); |
cb51be26 | 316 | --layer1->blocks_free; |
4c09d9c4 | 317 | hammer_crc_set_layer1(hmp->version, layer1); |
cb51be26 | 318 | hammer_modify_buffer_done(buffer1); |
653fa4cd | 319 | hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2)); |
cb51be26 | 320 | layer2->zone = zone; |
e04ee2de | 321 | KKASSERT(layer2->bytes_free == HAMMER_BIGBLOCK_SIZE); |
cb51be26 MD |
322 | KKASSERT(layer2->append_off == 0); |
323 | hammer_modify_volume_field(trans, trans->rootvol, | |
324 | vol0_stat_freebigblocks); | |
325 | --root_volume->ondisk->vol0_stat_freebigblocks; | |
326 | hmp->copy_stat_freebigblocks = | |
327 | root_volume->ondisk->vol0_stat_freebigblocks; | |
328 | hammer_modify_volume_done(trans->rootvol); | |
cb51be26 | 329 | } else { |
653fa4cd | 330 | hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2)); |
40043e7f | 331 | } |
cb51be26 | 332 | KKASSERT(layer2->zone == zone); |
40043e7f | 333 | |
320a5c59 MD |
334 | /* |
335 | * NOTE: bytes_free can legally go negative due to de-dup. | |
336 | */ | |
c3be93f2 | 337 | layer2->bytes_free -= bytes; |
df301614 MD |
338 | KKASSERT(layer2->append_off <= offset); |
339 | layer2->append_off = offset + bytes; | |
4c09d9c4 | 340 | hammer_crc_set_layer2(hmp->version, layer2); |
10a5d1ba | 341 | hammer_modify_buffer_done(buffer2); |
40043e7f | 342 | |
1ce12d35 MD |
343 | /* |
344 | * We hold the blockmap lock and should be the only ones | |
345 | * capable of modifying resv->append_off. Track the allocation | |
346 | * as appropriate. | |
347 | */ | |
348 | KKASSERT(bytes != 0); | |
df301614 MD |
349 | if (resv) { |
350 | KKASSERT(resv->append_off <= offset); | |
351 | resv->append_off = offset + bytes; | |
5e435c92 | 352 | resv->flags &= ~HAMMER_RESF_LAYER2FREE; |
1ce12d35 | 353 | hammer_blockmap_reserve_complete(hmp, resv); |
df301614 MD |
354 | } |
355 | ||
40043e7f | 356 | /* |
0832c9bb | 357 | * If we are allocating from the base of a new buffer we can avoid |
e694ed2f | 358 | * a disk read by calling hammer_bnew_ext(). |
40043e7f | 359 | */ |
f03c9cf4 | 360 | if ((next_offset & HAMMER_BUFMASK) == 0) { |
4a2796f3 MD |
361 | hammer_bnew_ext(trans->hmp, next_offset, bytes, |
362 | errorp, &buffer3); | |
3d0e4c86 TK |
363 | if (*errorp) { |
364 | result_offset = 0; | |
365 | goto failed; | |
366 | } | |
40043e7f | 367 | } |
0832c9bb | 368 | result_offset = next_offset; |
40043e7f | 369 | |
c3be93f2 | 370 | /* |
df2ccbac MD |
371 | * If we weren't supplied with a hint or could not use the hint |
372 | * then we wound up using blockmap->next_offset as the hint and | |
373 | * need to save it. | |
c3be93f2 | 374 | */ |
df2ccbac | 375 | if (use_hint == 0) { |
f1c0ae53 | 376 | hammer_modify_volume_noundo(NULL, root_volume); |
df2ccbac MD |
377 | blockmap->next_offset = next_offset + bytes; |
378 | hammer_modify_volume_done(root_volume); | |
379 | } | |
d99d6bf5 | 380 | hammer_unlock(&hmp->blkmap_lock); |
df301614 | 381 | failed: |
0832c9bb MD |
382 | |
383 | /* | |
384 | * Cleanup | |
385 | */ | |
f03c9cf4 MD |
386 | if (buffer1) |
387 | hammer_rel_buffer(buffer1, 0); | |
388 | if (buffer2) | |
389 | hammer_rel_buffer(buffer2, 0); | |
390 | if (buffer3) | |
391 | hammer_rel_buffer(buffer3, 0); | |
0832c9bb MD |
392 | |
393 | return(result_offset); | |
40043e7f MD |
394 | } |
395 | ||
396 | /* | |
4a2796f3 | 397 | * Frontend function - Reserve bytes in a zone. |
47637bff MD |
398 | * |
399 | * This code reserves bytes out of a blockmap without committing to any | |
cb51be26 | 400 | * meta-data modifications, allowing the front-end to directly issue disk |
d165c90a | 401 | * write I/O for big-blocks of data |
4a2796f3 MD |
402 | * |
403 | * The backend later finalizes the reservation with hammer_blockmap_finalize() | |
404 | * upon committing the related record. | |
47637bff | 405 | */ |
0832c9bb MD |
406 | hammer_reserve_t |
407 | hammer_blockmap_reserve(hammer_mount_t hmp, int zone, int bytes, | |
408 | hammer_off_t *zone_offp, int *errorp) | |
47637bff MD |
409 | { |
410 | hammer_volume_t root_volume; | |
cb51be26 MD |
411 | hammer_blockmap_t blockmap; |
412 | hammer_blockmap_t freemap; | |
21e9e7d5 TK |
413 | hammer_blockmap_layer1_t layer1; |
414 | hammer_blockmap_layer2_t layer2; | |
47637bff MD |
415 | hammer_buffer_t buffer1 = NULL; |
416 | hammer_buffer_t buffer2 = NULL; | |
417 | hammer_buffer_t buffer3 = NULL; | |
418 | hammer_off_t tmp_offset; | |
419 | hammer_off_t next_offset; | |
420 | hammer_off_t layer1_offset; | |
421 | hammer_off_t layer2_offset; | |
cb51be26 | 422 | hammer_off_t base_off; |
0832c9bb | 423 | hammer_reserve_t resv; |
0b62a005 | 424 | hammer_reserve_t resx = NULL; |
47637bff | 425 | int loops = 0; |
df301614 | 426 | int offset; |
47637bff | 427 | |
0832c9bb MD |
428 | /* |
429 | * Setup | |
430 | */ | |
f6d29b27 | 431 | KKASSERT(hammer_is_index_record(zone)); |
47637bff MD |
432 | root_volume = hammer_get_root_volume(hmp, errorp); |
433 | if (*errorp) | |
0832c9bb | 434 | return(NULL); |
cb51be26 MD |
435 | blockmap = &hmp->blockmap[zone]; |
436 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; | |
437 | KKASSERT(HAMMER_ZONE_DECODE(blockmap->next_offset) == zone); | |
47637bff MD |
438 | |
439 | /* | |
440 | * Deal with alignment and buffer-boundary issues. | |
441 | * | |
442 | * Be careful, certain primary alignments are used below to allocate | |
443 | * new blockmap blocks. | |
444 | */ | |
f097bffe | 445 | bytes = HAMMER_DATA_DOALIGN(bytes); |
4a2796f3 | 446 | KKASSERT(bytes > 0 && bytes <= HAMMER_XBUFSIZE); |
47637bff | 447 | |
cb51be26 | 448 | next_offset = blockmap->next_offset; |
df301614 | 449 | again: |
fce862c7 | 450 | resv = NULL; |
47637bff | 451 | /* |
cb51be26 | 452 | * Check for wrap |
47637bff | 453 | */ |
4a2796f3 | 454 | if (next_offset == HAMMER_ZONE_ENCODE(zone + 1, 0)) { |
cb51be26 | 455 | if (++loops == 2) { |
0f243a86 TK |
456 | hmkprintf(hmp, "No space left for zone %d " |
457 | "reservation\n", zone); | |
47637bff | 458 | *errorp = ENOSPC; |
df301614 | 459 | goto failed; |
47637bff MD |
460 | } |
461 | next_offset = HAMMER_ZONE_ENCODE(zone, 0); | |
462 | } | |
463 | ||
464 | /* | |
4a2796f3 | 465 | * The allocation request may not cross a buffer boundary. Special |
e04ee2de | 466 | * large allocations must not cross a big-block boundary. |
47637bff MD |
467 | */ |
468 | tmp_offset = next_offset + bytes - 1; | |
4a2796f3 MD |
469 | if (bytes <= HAMMER_BUFSIZE) { |
470 | if ((next_offset ^ tmp_offset) & ~HAMMER_BUFMASK64) { | |
471 | next_offset = tmp_offset & ~HAMMER_BUFMASK64; | |
472 | goto again; | |
473 | } | |
474 | } else { | |
e04ee2de TK |
475 | if ((next_offset ^ tmp_offset) & ~HAMMER_BIGBLOCK_MASK64) { |
476 | next_offset = tmp_offset & ~HAMMER_BIGBLOCK_MASK64; | |
4a2796f3 MD |
477 | goto again; |
478 | } | |
47637bff | 479 | } |
e04ee2de | 480 | offset = (int)next_offset & HAMMER_BIGBLOCK_MASK; |
47637bff MD |
481 | |
482 | /* | |
483 | * Dive layer 1. | |
484 | */ | |
cb51be26 | 485 | layer1_offset = freemap->phys_offset + |
47637bff MD |
486 | HAMMER_BLOCKMAP_LAYER1_OFFSET(next_offset); |
487 | layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer1); | |
cdb6e4e6 MD |
488 | if (*errorp) |
489 | goto failed; | |
47637bff MD |
490 | |
491 | /* | |
cb51be26 | 492 | * Check CRC. |
47637bff | 493 | */ |
4c09d9c4 | 494 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
db9f9d7f | 495 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 496 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 497 | hpanic("CRC FAILED: LAYER1"); |
db9f9d7f | 498 | hammer_unlock(&hmp->blkmap_lock); |
47637bff | 499 | } |
47637bff MD |
500 | |
501 | /* | |
745703c7 | 502 | * If we are at a big-block boundary and layer1 indicates no |
a981af19 | 503 | * free big-blocks, then we cannot allocate a new big-block in |
cb51be26 | 504 | * layer2, skip to the next layer1 entry. |
47637bff | 505 | */ |
e04ee2de | 506 | if ((next_offset & HAMMER_BIGBLOCK_MASK) == 0 && |
cb51be26 | 507 | layer1->blocks_free == 0) { |
b7f51e3b | 508 | next_offset = HAMMER_ZONE_LAYER1_NEXT_OFFSET(next_offset); |
0871ec5c TK |
509 | if (hammer_check_volume(hmp, &next_offset)) |
510 | goto failed; | |
47637bff MD |
511 | goto again; |
512 | } | |
cb51be26 | 513 | KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); |
47637bff MD |
514 | |
515 | /* | |
e04ee2de | 516 | * Dive layer 2, each entry represents a big-block. |
47637bff MD |
517 | */ |
518 | layer2_offset = layer1->phys_offset + | |
519 | HAMMER_BLOCKMAP_LAYER2_OFFSET(next_offset); | |
520 | layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer2); | |
cdb6e4e6 MD |
521 | if (*errorp) |
522 | goto failed; | |
47637bff MD |
523 | |
524 | /* | |
0832c9bb MD |
525 | * Check CRC if not allocating into uninitialized space (which we |
526 | * aren't when reserving space). | |
47637bff | 527 | */ |
4c09d9c4 | 528 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
db9f9d7f | 529 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 530 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 531 | hpanic("CRC FAILED: LAYER2"); |
db9f9d7f | 532 | hammer_unlock(&hmp->blkmap_lock); |
47637bff MD |
533 | } |
534 | ||
0832c9bb | 535 | /* |
df301614 | 536 | * Skip the layer if the zone is owned by someone other then us. |
0832c9bb | 537 | */ |
df301614 | 538 | if (layer2->zone && layer2->zone != zone) { |
e04ee2de | 539 | next_offset += (HAMMER_BIGBLOCK_SIZE - offset); |
df301614 MD |
540 | goto again; |
541 | } | |
542 | if (offset < layer2->append_off) { | |
543 | next_offset += layer2->append_off - offset; | |
4a2796f3 MD |
544 | goto again; |
545 | } | |
546 | ||
547 | /* | |
df301614 MD |
548 | * We need the lock from this point on. We have to re-check zone |
549 | * ownership after acquiring the lock and also check for reservations. | |
550 | */ | |
551 | hammer_lock_ex(&hmp->blkmap_lock); | |
552 | ||
553 | if (layer2->zone && layer2->zone != zone) { | |
554 | hammer_unlock(&hmp->blkmap_lock); | |
e04ee2de | 555 | next_offset += (HAMMER_BIGBLOCK_SIZE - offset); |
df301614 MD |
556 | goto again; |
557 | } | |
558 | if (offset < layer2->append_off) { | |
559 | hammer_unlock(&hmp->blkmap_lock); | |
560 | next_offset += layer2->append_off - offset; | |
561 | goto again; | |
562 | } | |
563 | ||
564 | /* | |
a981af19 | 565 | * The big-block might be reserved by another zone. If it is reserved |
df301614 | 566 | * by our zone we may have to move next_offset past the append_off. |
4a2796f3 | 567 | */ |
d369f237 | 568 | base_off = hammer_xlate_to_zone2(next_offset & ~HAMMER_BIGBLOCK_MASK64); |
4a2796f3 MD |
569 | resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, base_off); |
570 | if (resv) { | |
4a2796f3 | 571 | if (resv->zone != zone) { |
df301614 | 572 | hammer_unlock(&hmp->blkmap_lock); |
c4e15ea8 | 573 | next_offset = HAMMER_ZONE_LAYER2_NEXT_OFFSET(next_offset); |
4a2796f3 MD |
574 | goto again; |
575 | } | |
df301614 MD |
576 | if (offset < resv->append_off) { |
577 | hammer_unlock(&hmp->blkmap_lock); | |
578 | next_offset += resv->append_off - offset; | |
579 | goto again; | |
580 | } | |
cb51be26 | 581 | ++resv->refs; |
cb51be26 | 582 | } else { |
bac808fe | 583 | resx = kmalloc(sizeof(*resv), hmp->m_misc, |
df301614 MD |
584 | M_WAITOK | M_ZERO | M_USE_RESERVE); |
585 | resx->refs = 1; | |
586 | resx->zone = zone; | |
587 | resx->zone_offset = base_off; | |
e04ee2de | 588 | if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) |
5e435c92 | 589 | resx->flags |= HAMMER_RESF_LAYER2FREE; |
df301614 MD |
590 | resv = RB_INSERT(hammer_res_rb_tree, &hmp->rb_resv_root, resx); |
591 | KKASSERT(resv == NULL); | |
592 | resv = resx; | |
a7e9bef1 | 593 | ++hammer_count_reservations; |
cb51be26 | 594 | } |
df301614 | 595 | resv->append_off = offset + bytes; |
cb51be26 | 596 | |
47637bff | 597 | /* |
0832c9bb MD |
598 | * If we are not reserving a whole buffer but are at the start of |
599 | * a new block, call hammer_bnew() to avoid a disk read. | |
600 | * | |
4a2796f3 MD |
601 | * If we are reserving a whole buffer (or more), the caller will |
602 | * probably use a direct read, so do nothing. | |
1ce32b2e MD |
603 | * |
604 | * If we do not have a whole lot of system memory we really can't | |
605 | * afford to block while holding the blkmap_lock! | |
47637bff | 606 | */ |
0832c9bb | 607 | if (bytes < HAMMER_BUFSIZE && (next_offset & HAMMER_BUFMASK) == 0) { |
e91e64c7 | 608 | if (!vm_paging_min_dnc(HAMMER_BUFSIZE / PAGE_SIZE)) { |
1ce32b2e | 609 | hammer_bnew(hmp, next_offset, errorp, &buffer3); |
3d0e4c86 TK |
610 | if (*errorp) |
611 | goto failed; | |
612 | } | |
0832c9bb MD |
613 | } |
614 | ||
df301614 MD |
615 | blockmap->next_offset = next_offset + bytes; |
616 | hammer_unlock(&hmp->blkmap_lock); | |
0832c9bb | 617 | |
df301614 | 618 | failed: |
47637bff MD |
619 | if (buffer1) |
620 | hammer_rel_buffer(buffer1, 0); | |
621 | if (buffer2) | |
622 | hammer_rel_buffer(buffer2, 0); | |
623 | if (buffer3) | |
624 | hammer_rel_buffer(buffer3, 0); | |
625 | hammer_rel_volume(root_volume, 0); | |
0832c9bb MD |
626 | *zone_offp = next_offset; |
627 | ||
628 | return(resv); | |
629 | } | |
630 | ||
631 | /* | |
5e435c92 MD |
632 | * Dereference a reservation structure. Upon the final release the |
633 | * underlying big-block is checked and if it is entirely free we delete | |
634 | * any related HAMMER buffers to avoid potential conflicts with future | |
635 | * reuse of the big-block. | |
0832c9bb MD |
636 | */ |
637 | void | |
638 | hammer_blockmap_reserve_complete(hammer_mount_t hmp, hammer_reserve_t resv) | |
639 | { | |
5e435c92 | 640 | hammer_off_t base_offset; |
362ec2dc | 641 | int error; |
1b0ab2c3 | 642 | |
0832c9bb | 643 | KKASSERT(resv->refs > 0); |
e1545c47 | 644 | KKASSERT(hammer_is_zone_raw_buffer(resv->zone_offset)); |
5e435c92 MD |
645 | |
646 | /* | |
647 | * Setting append_off to the max prevents any new allocations | |
648 | * from occuring while we are trying to dispose of the reservation, | |
649 | * allowing us to safely delete any related HAMMER buffers. | |
362ec2dc MD |
650 | * |
651 | * If we are unable to clean out all related HAMMER buffers we | |
652 | * requeue the delay. | |
5e435c92 MD |
653 | */ |
654 | if (resv->refs == 1 && (resv->flags & HAMMER_RESF_LAYER2FREE)) { | |
e04ee2de | 655 | resv->append_off = HAMMER_BIGBLOCK_SIZE; |
d0ea2047 | 656 | base_offset = hammer_xlate_to_zoneX(resv->zone, resv->zone_offset); |
362ec2dc MD |
657 | error = hammer_del_buffers(hmp, base_offset, |
658 | resv->zone_offset, | |
e04ee2de | 659 | HAMMER_BIGBLOCK_SIZE, |
f7d0505a MD |
660 | 1); |
661 | if (hammer_debug_general & 0x20000) { | |
d053aa8a | 662 | hkprintf("delbgblk %016jx error %d\n", |
f7d0505a MD |
663 | (intmax_t)base_offset, error); |
664 | } | |
362ec2dc MD |
665 | if (error) |
666 | hammer_reserve_setdelay(hmp, resv); | |
5e435c92 | 667 | } |
0832c9bb | 668 | if (--resv->refs == 0) { |
f7d0505a | 669 | if (hammer_debug_general & 0x20000) { |
d053aa8a | 670 | hkprintf("delresvr %016jx zone %02x\n", |
f7d0505a MD |
671 | (intmax_t)resv->zone_offset, resv->zone); |
672 | } | |
cb51be26 | 673 | KKASSERT((resv->flags & HAMMER_RESF_ONDELAY) == 0); |
0832c9bb | 674 | RB_REMOVE(hammer_res_rb_tree, &hmp->rb_resv_root, resv); |
bac808fe | 675 | kfree(resv, hmp->m_misc); |
0832c9bb MD |
676 | --hammer_count_reservations; |
677 | } | |
47637bff MD |
678 | } |
679 | ||
cb51be26 | 680 | /* |
5e435c92 MD |
681 | * Prevent a potentially free big-block from being reused until after |
682 | * the related flushes have completely cycled, otherwise crash recovery | |
683 | * could resurrect a data block that was already reused and overwritten. | |
684 | * | |
1ce12d35 MD |
685 | * The caller might reset the underlying layer2 entry's append_off to 0, so |
686 | * our covering append_off must be set to max to prevent any reallocation | |
687 | * until after the flush delays complete, not to mention proper invalidation | |
688 | * of any underlying cached blocks. | |
cb51be26 | 689 | */ |
5e435c92 | 690 | static void |
362ec2dc | 691 | hammer_reserve_setdelay_offset(hammer_mount_t hmp, hammer_off_t base_offset, |
21e9e7d5 | 692 | int zone, hammer_blockmap_layer2_t layer2) |
cb51be26 | 693 | { |
5e435c92 | 694 | hammer_reserve_t resv; |
df301614 | 695 | |
5e435c92 MD |
696 | /* |
697 | * Allocate the reservation if necessary. | |
1ce12d35 MD |
698 | * |
699 | * NOTE: need lock in future around resv lookup/allocation and | |
700 | * the setdelay call, currently refs is not bumped until the call. | |
5e435c92 MD |
701 | */ |
702 | again: | |
703 | resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, base_offset); | |
cb51be26 | 704 | if (resv == NULL) { |
bac808fe | 705 | resv = kmalloc(sizeof(*resv), hmp->m_misc, |
df301614 | 706 | M_WAITOK | M_ZERO | M_USE_RESERVE); |
1ce12d35 | 707 | resv->zone = zone; |
5e435c92 MD |
708 | resv->zone_offset = base_offset; |
709 | resv->refs = 0; | |
e04ee2de | 710 | resv->append_off = HAMMER_BIGBLOCK_SIZE; |
1ce12d35 | 711 | |
e04ee2de | 712 | if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) |
5e435c92 | 713 | resv->flags |= HAMMER_RESF_LAYER2FREE; |
df301614 | 714 | if (RB_INSERT(hammer_res_rb_tree, &hmp->rb_resv_root, resv)) { |
bac808fe | 715 | kfree(resv, hmp->m_misc); |
5e435c92 | 716 | goto again; |
df301614 | 717 | } |
5e435c92 | 718 | ++hammer_count_reservations; |
1ce12d35 | 719 | } else { |
e04ee2de | 720 | if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) |
1ce12d35 | 721 | resv->flags |= HAMMER_RESF_LAYER2FREE; |
5e435c92 | 722 | } |
1ce12d35 | 723 | hammer_reserve_setdelay(hmp, resv); |
362ec2dc | 724 | } |
5e435c92 | 725 | |
362ec2dc MD |
726 | /* |
727 | * Enter the reservation on the on-delay list, or move it if it | |
728 | * is already on the list. | |
729 | */ | |
730 | static void | |
731 | hammer_reserve_setdelay(hammer_mount_t hmp, hammer_reserve_t resv) | |
732 | { | |
5e435c92 | 733 | if (resv->flags & HAMMER_RESF_ONDELAY) { |
cb51be26 | 734 | TAILQ_REMOVE(&hmp->delay_list, resv, delay_entry); |
f8a7a900 | 735 | resv->flg_no = hmp->flusher.next + 1; |
5e435c92 | 736 | TAILQ_INSERT_TAIL(&hmp->delay_list, resv, delay_entry); |
cb51be26 | 737 | } else { |
5e435c92 | 738 | ++resv->refs; |
a7e9bef1 | 739 | ++hmp->rsv_fromdelay; |
df301614 | 740 | resv->flags |= HAMMER_RESF_ONDELAY; |
f8a7a900 | 741 | resv->flg_no = hmp->flusher.next + 1; |
df301614 MD |
742 | TAILQ_INSERT_TAIL(&hmp->delay_list, resv, delay_entry); |
743 | } | |
cb51be26 MD |
744 | } |
745 | ||
f7d0505a MD |
746 | /* |
747 | * Reserve has reached its flush point, remove it from the delay list | |
748 | * and finish it off. hammer_blockmap_reserve_complete() inherits | |
749 | * the ondelay reference. | |
750 | */ | |
cb51be26 MD |
751 | void |
752 | hammer_reserve_clrdelay(hammer_mount_t hmp, hammer_reserve_t resv) | |
753 | { | |
754 | KKASSERT(resv->flags & HAMMER_RESF_ONDELAY); | |
755 | resv->flags &= ~HAMMER_RESF_ONDELAY; | |
756 | TAILQ_REMOVE(&hmp->delay_list, resv, delay_entry); | |
a7e9bef1 | 757 | --hmp->rsv_fromdelay; |
cb51be26 MD |
758 | hammer_blockmap_reserve_complete(hmp, resv); |
759 | } | |
760 | ||
47637bff | 761 | /* |
4a2796f3 | 762 | * Backend function - free (offset, bytes) in a zone. |
cdb6e4e6 MD |
763 | * |
764 | * XXX error return | |
40043e7f | 765 | */ |
c3be93f2 | 766 | void |
36f82b23 | 767 | hammer_blockmap_free(hammer_transaction_t trans, |
cb51be26 | 768 | hammer_off_t zone_offset, int bytes) |
40043e7f | 769 | { |
0832c9bb | 770 | hammer_mount_t hmp; |
c3be93f2 | 771 | hammer_volume_t root_volume; |
cb51be26 | 772 | hammer_blockmap_t freemap; |
21e9e7d5 TK |
773 | hammer_blockmap_layer1_t layer1; |
774 | hammer_blockmap_layer2_t layer2; | |
f03c9cf4 MD |
775 | hammer_buffer_t buffer1 = NULL; |
776 | hammer_buffer_t buffer2 = NULL; | |
c3be93f2 MD |
777 | hammer_off_t layer1_offset; |
778 | hammer_off_t layer2_offset; | |
cb51be26 | 779 | hammer_off_t base_off; |
c3be93f2 MD |
780 | int error; |
781 | int zone; | |
782 | ||
cb51be26 MD |
783 | if (bytes == 0) |
784 | return; | |
0832c9bb MD |
785 | hmp = trans->hmp; |
786 | ||
cb51be26 MD |
787 | /* |
788 | * Alignment | |
789 | */ | |
f097bffe | 790 | bytes = HAMMER_DATA_DOALIGN(bytes); |
4a2796f3 | 791 | KKASSERT(bytes <= HAMMER_XBUFSIZE); |
745703c7 | 792 | KKASSERT(((zone_offset ^ (zone_offset + (bytes - 1))) & |
e04ee2de | 793 | ~HAMMER_BIGBLOCK_MASK64) == 0); |
f03c9cf4 | 794 | |
cb51be26 MD |
795 | /* |
796 | * Basic zone validation & locking | |
797 | */ | |
798 | zone = HAMMER_ZONE_DECODE(zone_offset); | |
f6d29b27 | 799 | KKASSERT(hammer_is_index_record(zone)); |
cb51be26 MD |
800 | root_volume = trans->rootvol; |
801 | error = 0; | |
f03c9cf4 | 802 | |
cb51be26 | 803 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; |
c3be93f2 MD |
804 | |
805 | /* | |
806 | * Dive layer 1. | |
807 | */ | |
cb51be26 MD |
808 | layer1_offset = freemap->phys_offset + |
809 | HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset); | |
0832c9bb | 810 | layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); |
cdb6e4e6 MD |
811 | if (error) |
812 | goto failed; | |
cb51be26 MD |
813 | KKASSERT(layer1->phys_offset && |
814 | layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); | |
4c09d9c4 | 815 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
db9f9d7f | 816 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 817 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 818 | hpanic("CRC FAILED: LAYER1"); |
db9f9d7f | 819 | hammer_unlock(&hmp->blkmap_lock); |
19619882 | 820 | } |
c3be93f2 MD |
821 | |
822 | /* | |
e04ee2de | 823 | * Dive layer 2, each entry represents a big-block. |
c3be93f2 MD |
824 | */ |
825 | layer2_offset = layer1->phys_offset + | |
cb51be26 | 826 | HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset); |
0832c9bb | 827 | layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2); |
cdb6e4e6 MD |
828 | if (error) |
829 | goto failed; | |
4c09d9c4 | 830 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
db9f9d7f | 831 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 832 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 833 | hpanic("CRC FAILED: LAYER2"); |
db9f9d7f | 834 | hammer_unlock(&hmp->blkmap_lock); |
19619882 MD |
835 | } |
836 | ||
df301614 MD |
837 | hammer_lock_ex(&hmp->blkmap_lock); |
838 | ||
36f82b23 | 839 | hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2)); |
4a2796f3 MD |
840 | |
841 | /* | |
5e435c92 | 842 | * Free space previously allocated via blockmap_alloc(). |
320a5c59 MD |
843 | * |
844 | * NOTE: bytes_free can be and remain negative due to de-dup ops | |
e04ee2de | 845 | * but can never become larger than HAMMER_BIGBLOCK_SIZE. |
4a2796f3 MD |
846 | */ |
847 | KKASSERT(layer2->zone == zone); | |
848 | layer2->bytes_free += bytes; | |
e04ee2de | 849 | KKASSERT(layer2->bytes_free <= HAMMER_BIGBLOCK_SIZE); |
5e435c92 MD |
850 | |
851 | /* | |
852 | * If a big-block becomes entirely free we must create a covering | |
853 | * reservation to prevent premature reuse. Note, however, that | |
854 | * the big-block and/or reservation may still have an append_off | |
855 | * that allows further (non-reused) allocations. | |
856 | * | |
857 | * Once the reservation has been made we re-check layer2 and if | |
858 | * the big-block is still entirely free we reset the layer2 entry. | |
859 | * The reservation will prevent premature reuse. | |
860 | * | |
861 | * NOTE: hammer_buffer's are only invalidated when the reservation | |
862 | * is completed, if the layer2 entry is still completely free at | |
863 | * that time. Any allocations from the reservation that may have | |
864 | * occured in the mean time, or active references on the reservation | |
865 | * from new pending allocations, will prevent the invalidation from | |
866 | * occuring. | |
867 | */ | |
e04ee2de | 868 | if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) { |
f4fe61c2 | 869 | base_off = hammer_xlate_to_zone2(zone_offset & |
e04ee2de | 870 | ~HAMMER_BIGBLOCK_MASK64); |
5e435c92 | 871 | |
1ce12d35 | 872 | hammer_reserve_setdelay_offset(hmp, base_off, zone, layer2); |
e04ee2de | 873 | if (layer2->bytes_free == HAMMER_BIGBLOCK_SIZE) { |
4a2796f3 MD |
874 | layer2->zone = 0; |
875 | layer2->append_off = 0; | |
36f82b23 MD |
876 | hammer_modify_buffer(trans, buffer1, |
877 | layer1, sizeof(*layer1)); | |
4a2796f3 | 878 | ++layer1->blocks_free; |
4c09d9c4 | 879 | hammer_crc_set_layer1(hmp->version, layer1); |
10a5d1ba | 880 | hammer_modify_buffer_done(buffer1); |
cb51be26 MD |
881 | hammer_modify_volume_field(trans, |
882 | trans->rootvol, | |
883 | vol0_stat_freebigblocks); | |
4a2796f3 | 884 | ++root_volume->ondisk->vol0_stat_freebigblocks; |
cb51be26 MD |
885 | hmp->copy_stat_freebigblocks = |
886 | root_volume->ondisk->vol0_stat_freebigblocks; | |
887 | hammer_modify_volume_done(trans->rootvol); | |
c3be93f2 MD |
888 | } |
889 | } | |
4c09d9c4 | 890 | hammer_crc_set_layer2(hmp->version, layer2); |
4a2796f3 MD |
891 | hammer_modify_buffer_done(buffer2); |
892 | hammer_unlock(&hmp->blkmap_lock); | |
893 | ||
cdb6e4e6 | 894 | failed: |
4a2796f3 MD |
895 | if (buffer1) |
896 | hammer_rel_buffer(buffer1, 0); | |
897 | if (buffer2) | |
898 | hammer_rel_buffer(buffer2, 0); | |
899 | } | |
900 | ||
bb29b5d8 MD |
901 | int |
902 | hammer_blockmap_dedup(hammer_transaction_t trans, | |
903 | hammer_off_t zone_offset, int bytes) | |
904 | { | |
905 | hammer_mount_t hmp; | |
bb29b5d8 | 906 | hammer_blockmap_t freemap; |
21e9e7d5 TK |
907 | hammer_blockmap_layer1_t layer1; |
908 | hammer_blockmap_layer2_t layer2; | |
bb29b5d8 MD |
909 | hammer_buffer_t buffer1 = NULL; |
910 | hammer_buffer_t buffer2 = NULL; | |
911 | hammer_off_t layer1_offset; | |
912 | hammer_off_t layer2_offset; | |
913 | int32_t temp; | |
914 | int error; | |
f31f6d84 | 915 | int zone __debugvar; |
bb29b5d8 MD |
916 | |
917 | if (bytes == 0) | |
918 | return (0); | |
919 | hmp = trans->hmp; | |
920 | ||
921 | /* | |
922 | * Alignment | |
923 | */ | |
f097bffe | 924 | bytes = HAMMER_DATA_DOALIGN(bytes); |
e04ee2de | 925 | KKASSERT(bytes <= HAMMER_BIGBLOCK_SIZE); |
bb29b5d8 | 926 | KKASSERT(((zone_offset ^ (zone_offset + (bytes - 1))) & |
e04ee2de | 927 | ~HAMMER_BIGBLOCK_MASK64) == 0); |
bb29b5d8 MD |
928 | |
929 | /* | |
930 | * Basic zone validation & locking | |
931 | */ | |
932 | zone = HAMMER_ZONE_DECODE(zone_offset); | |
f6d29b27 | 933 | KKASSERT(hammer_is_index_record(zone)); |
bb29b5d8 MD |
934 | error = 0; |
935 | ||
bb29b5d8 MD |
936 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; |
937 | ||
938 | /* | |
939 | * Dive layer 1. | |
940 | */ | |
941 | layer1_offset = freemap->phys_offset + | |
942 | HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset); | |
943 | layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); | |
944 | if (error) | |
945 | goto failed; | |
946 | KKASSERT(layer1->phys_offset && | |
947 | layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); | |
4c09d9c4 | 948 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
bb29b5d8 | 949 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 950 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 951 | hpanic("CRC FAILED: LAYER1"); |
bb29b5d8 MD |
952 | hammer_unlock(&hmp->blkmap_lock); |
953 | } | |
954 | ||
955 | /* | |
e04ee2de | 956 | * Dive layer 2, each entry represents a big-block. |
bb29b5d8 MD |
957 | */ |
958 | layer2_offset = layer1->phys_offset + | |
959 | HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset); | |
960 | layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2); | |
961 | if (error) | |
962 | goto failed; | |
4c09d9c4 | 963 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
bb29b5d8 | 964 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 965 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 966 | hpanic("CRC FAILED: LAYER2"); |
bb29b5d8 MD |
967 | hammer_unlock(&hmp->blkmap_lock); |
968 | } | |
969 | ||
970 | hammer_lock_ex(&hmp->blkmap_lock); | |
971 | ||
972 | hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2)); | |
973 | ||
974 | /* | |
975 | * Free space previously allocated via blockmap_alloc(). | |
976 | * | |
977 | * NOTE: bytes_free can be and remain negative due to de-dup ops | |
e04ee2de | 978 | * but can never become larger than HAMMER_BIGBLOCK_SIZE. |
bb29b5d8 MD |
979 | */ |
980 | KKASSERT(layer2->zone == zone); | |
e04ee2de | 981 | temp = layer2->bytes_free - HAMMER_BIGBLOCK_SIZE * 2; |
bb29b5d8 MD |
982 | cpu_ccfence(); /* prevent gcc from optimizing temp out */ |
983 | if (temp > layer2->bytes_free) { | |
984 | error = ERANGE; | |
985 | goto underflow; | |
986 | } | |
987 | layer2->bytes_free -= bytes; | |
988 | ||
e04ee2de | 989 | KKASSERT(layer2->bytes_free <= HAMMER_BIGBLOCK_SIZE); |
bb29b5d8 | 990 | |
4c09d9c4 | 991 | hammer_crc_set_layer2(hmp->version, layer2); |
bb29b5d8 MD |
992 | underflow: |
993 | hammer_modify_buffer_done(buffer2); | |
994 | hammer_unlock(&hmp->blkmap_lock); | |
995 | ||
996 | failed: | |
997 | if (buffer1) | |
998 | hammer_rel_buffer(buffer1, 0); | |
999 | if (buffer2) | |
1000 | hammer_rel_buffer(buffer2, 0); | |
1001 | return (error); | |
1002 | } | |
1003 | ||
4a2796f3 MD |
1004 | /* |
1005 | * Backend function - finalize (offset, bytes) in a zone. | |
1006 | * | |
1007 | * Allocate space that was previously reserved by the frontend. | |
1008 | */ | |
cdb6e4e6 | 1009 | int |
4a2796f3 | 1010 | hammer_blockmap_finalize(hammer_transaction_t trans, |
5e435c92 | 1011 | hammer_reserve_t resv, |
4a2796f3 MD |
1012 | hammer_off_t zone_offset, int bytes) |
1013 | { | |
1014 | hammer_mount_t hmp; | |
1015 | hammer_volume_t root_volume; | |
4a2796f3 | 1016 | hammer_blockmap_t freemap; |
21e9e7d5 TK |
1017 | hammer_blockmap_layer1_t layer1; |
1018 | hammer_blockmap_layer2_t layer2; | |
4a2796f3 MD |
1019 | hammer_buffer_t buffer1 = NULL; |
1020 | hammer_buffer_t buffer2 = NULL; | |
1021 | hammer_off_t layer1_offset; | |
1022 | hammer_off_t layer2_offset; | |
1023 | int error; | |
1024 | int zone; | |
df301614 | 1025 | int offset; |
4a2796f3 MD |
1026 | |
1027 | if (bytes == 0) | |
cdb6e4e6 | 1028 | return(0); |
4a2796f3 MD |
1029 | hmp = trans->hmp; |
1030 | ||
1031 | /* | |
1032 | * Alignment | |
1033 | */ | |
f097bffe | 1034 | bytes = HAMMER_DATA_DOALIGN(bytes); |
4a2796f3 MD |
1035 | KKASSERT(bytes <= HAMMER_XBUFSIZE); |
1036 | ||
1037 | /* | |
1038 | * Basic zone validation & locking | |
1039 | */ | |
1040 | zone = HAMMER_ZONE_DECODE(zone_offset); | |
f6d29b27 | 1041 | KKASSERT(hammer_is_index_record(zone)); |
4a2796f3 MD |
1042 | root_volume = trans->rootvol; |
1043 | error = 0; | |
4a2796f3 | 1044 | |
4a2796f3 MD |
1045 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; |
1046 | ||
1047 | /* | |
1048 | * Dive layer 1. | |
1049 | */ | |
1050 | layer1_offset = freemap->phys_offset + | |
1051 | HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset); | |
1052 | layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); | |
cdb6e4e6 MD |
1053 | if (error) |
1054 | goto failed; | |
4a2796f3 MD |
1055 | KKASSERT(layer1->phys_offset && |
1056 | layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); | |
4c09d9c4 | 1057 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
db9f9d7f | 1058 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 1059 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 1060 | hpanic("CRC FAILED: LAYER1"); |
db9f9d7f | 1061 | hammer_unlock(&hmp->blkmap_lock); |
4a2796f3 MD |
1062 | } |
1063 | ||
1064 | /* | |
e04ee2de | 1065 | * Dive layer 2, each entry represents a big-block. |
4a2796f3 MD |
1066 | */ |
1067 | layer2_offset = layer1->phys_offset + | |
1068 | HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset); | |
1069 | layer2 = hammer_bread(hmp, layer2_offset, &error, &buffer2); | |
cdb6e4e6 MD |
1070 | if (error) |
1071 | goto failed; | |
4c09d9c4 | 1072 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
db9f9d7f | 1073 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 1074 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 1075 | hpanic("CRC FAILED: LAYER2"); |
db9f9d7f | 1076 | hammer_unlock(&hmp->blkmap_lock); |
4a2796f3 MD |
1077 | } |
1078 | ||
df301614 MD |
1079 | hammer_lock_ex(&hmp->blkmap_lock); |
1080 | ||
4a2796f3 MD |
1081 | hammer_modify_buffer(trans, buffer2, layer2, sizeof(*layer2)); |
1082 | ||
1083 | /* | |
1084 | * Finalize some or all of the space covered by a current | |
1085 | * reservation. An allocation in the same layer may have | |
1086 | * already assigned ownership. | |
1087 | */ | |
1088 | if (layer2->zone == 0) { | |
653fa4cd | 1089 | hammer_modify_buffer(trans, buffer1, layer1, sizeof(*layer1)); |
4a2796f3 | 1090 | --layer1->blocks_free; |
4c09d9c4 | 1091 | hammer_crc_set_layer1(hmp->version, layer1); |
4a2796f3 MD |
1092 | hammer_modify_buffer_done(buffer1); |
1093 | layer2->zone = zone; | |
e04ee2de | 1094 | KKASSERT(layer2->bytes_free == HAMMER_BIGBLOCK_SIZE); |
4a2796f3 MD |
1095 | KKASSERT(layer2->append_off == 0); |
1096 | hammer_modify_volume_field(trans, | |
1097 | trans->rootvol, | |
1098 | vol0_stat_freebigblocks); | |
1099 | --root_volume->ondisk->vol0_stat_freebigblocks; | |
1100 | hmp->copy_stat_freebigblocks = | |
1101 | root_volume->ondisk->vol0_stat_freebigblocks; | |
1102 | hammer_modify_volume_done(trans->rootvol); | |
1103 | } | |
1104 | if (layer2->zone != zone) | |
33234d14 | 1105 | hdkprintf("layer2 zone mismatch %d %d\n", layer2->zone, zone); |
4a2796f3 | 1106 | KKASSERT(layer2->zone == zone); |
1ce12d35 | 1107 | KKASSERT(bytes != 0); |
4a2796f3 | 1108 | layer2->bytes_free -= bytes; |
43bc39fa | 1109 | if (resv) |
5e435c92 | 1110 | resv->flags &= ~HAMMER_RESF_LAYER2FREE; |
4a2796f3 MD |
1111 | |
1112 | /* | |
1113 | * Finalizations can occur out of order, or combined with allocations. | |
1114 | * append_off must be set to the highest allocated offset. | |
1115 | */ | |
e04ee2de | 1116 | offset = ((int)zone_offset & HAMMER_BIGBLOCK_MASK) + bytes; |
df301614 MD |
1117 | if (layer2->append_off < offset) |
1118 | layer2->append_off = offset; | |
4a2796f3 | 1119 | |
4c09d9c4 | 1120 | hammer_crc_set_layer2(hmp->version, layer2); |
10a5d1ba | 1121 | hammer_modify_buffer_done(buffer2); |
d99d6bf5 | 1122 | hammer_unlock(&hmp->blkmap_lock); |
f03c9cf4 | 1123 | |
cdb6e4e6 | 1124 | failed: |
f03c9cf4 MD |
1125 | if (buffer1) |
1126 | hammer_rel_buffer(buffer1, 0); | |
1127 | if (buffer2) | |
1128 | hammer_rel_buffer(buffer2, 0); | |
cdb6e4e6 | 1129 | return(error); |
40043e7f MD |
1130 | } |
1131 | ||
bf686dbe | 1132 | /* |
320a5c59 MD |
1133 | * Return the approximate number of free bytes in the big-block |
1134 | * containing the specified blockmap offset. | |
1135 | * | |
1136 | * WARNING: A negative number can be returned if data de-dup exists, | |
1137 | * and the result will also not represent he actual number | |
1138 | * of free bytes in this case. | |
1139 | * | |
1140 | * This code is used only by the reblocker. | |
bf686dbe MD |
1141 | */ |
1142 | int | |
cb51be26 | 1143 | hammer_blockmap_getfree(hammer_mount_t hmp, hammer_off_t zone_offset, |
bf686dbe MD |
1144 | int *curp, int *errorp) |
1145 | { | |
1146 | hammer_volume_t root_volume; | |
cb51be26 MD |
1147 | hammer_blockmap_t blockmap; |
1148 | hammer_blockmap_t freemap; | |
21e9e7d5 TK |
1149 | hammer_blockmap_layer1_t layer1; |
1150 | hammer_blockmap_layer2_t layer2; | |
bf686dbe MD |
1151 | hammer_buffer_t buffer = NULL; |
1152 | hammer_off_t layer1_offset; | |
1153 | hammer_off_t layer2_offset; | |
320a5c59 | 1154 | int32_t bytes; |
bf686dbe MD |
1155 | int zone; |
1156 | ||
cb51be26 | 1157 | zone = HAMMER_ZONE_DECODE(zone_offset); |
f6d29b27 | 1158 | KKASSERT(hammer_is_index_record(zone)); |
bf686dbe MD |
1159 | root_volume = hammer_get_root_volume(hmp, errorp); |
1160 | if (*errorp) { | |
1161 | *curp = 0; | |
1162 | return(0); | |
1163 | } | |
cb51be26 MD |
1164 | blockmap = &hmp->blockmap[zone]; |
1165 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; | |
bf686dbe MD |
1166 | |
1167 | /* | |
1168 | * Dive layer 1. | |
1169 | */ | |
cb51be26 MD |
1170 | layer1_offset = freemap->phys_offset + |
1171 | HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset); | |
bf686dbe | 1172 | layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer); |
cdb6e4e6 | 1173 | if (*errorp) { |
ebadcc6a | 1174 | *curp = 0; |
cdb6e4e6 MD |
1175 | bytes = 0; |
1176 | goto failed; | |
1177 | } | |
bf686dbe | 1178 | KKASSERT(layer1->phys_offset); |
4c09d9c4 | 1179 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
db9f9d7f | 1180 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 1181 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 1182 | hpanic("CRC FAILED: LAYER1"); |
db9f9d7f | 1183 | hammer_unlock(&hmp->blkmap_lock); |
19619882 | 1184 | } |
bf686dbe MD |
1185 | |
1186 | /* | |
e04ee2de | 1187 | * Dive layer 2, each entry represents a big-block. |
cdb6e4e6 MD |
1188 | * |
1189 | * (reuse buffer, layer1 pointer becomes invalid) | |
bf686dbe MD |
1190 | */ |
1191 | layer2_offset = layer1->phys_offset + | |
cb51be26 | 1192 | HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset); |
bf686dbe | 1193 | layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer); |
cdb6e4e6 | 1194 | if (*errorp) { |
ebadcc6a | 1195 | *curp = 0; |
cdb6e4e6 MD |
1196 | bytes = 0; |
1197 | goto failed; | |
1198 | } | |
4c09d9c4 | 1199 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
db9f9d7f | 1200 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 1201 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 1202 | hpanic("CRC FAILED: LAYER2"); |
db9f9d7f | 1203 | hammer_unlock(&hmp->blkmap_lock); |
19619882 | 1204 | } |
cb51be26 | 1205 | KKASSERT(layer2->zone == zone); |
bf686dbe MD |
1206 | |
1207 | bytes = layer2->bytes_free; | |
1208 | ||
4af7f537 TK |
1209 | /* |
1210 | * *curp becomes 1 only when no error and, | |
1211 | * next_offset and zone_offset are in the same big-block. | |
1212 | */ | |
e04ee2de | 1213 | if ((blockmap->next_offset ^ zone_offset) & ~HAMMER_BIGBLOCK_MASK64) |
4af7f537 | 1214 | *curp = 0; /* not same */ |
bf686dbe MD |
1215 | else |
1216 | *curp = 1; | |
cdb6e4e6 | 1217 | failed: |
bf686dbe MD |
1218 | if (buffer) |
1219 | hammer_rel_buffer(buffer, 0); | |
1220 | hammer_rel_volume(root_volume, 0); | |
4af7f537 | 1221 | if (hammer_debug_general & 0x4000) { |
35a5249b | 1222 | hdkprintf("%016jx -> %d\n", (intmax_t)zone_offset, bytes); |
bf686dbe MD |
1223 | } |
1224 | return(bytes); | |
1225 | } | |
1226 | ||
1227 | ||
40043e7f | 1228 | /* |
f4fe61c2 | 1229 | * Lookup a blockmap offset and verify blockmap layers. |
40043e7f MD |
1230 | */ |
1231 | hammer_off_t | |
f4fe61c2 MD |
1232 | hammer_blockmap_lookup_verify(hammer_mount_t hmp, hammer_off_t zone_offset, |
1233 | int *errorp) | |
40043e7f MD |
1234 | { |
1235 | hammer_volume_t root_volume; | |
cb51be26 | 1236 | hammer_blockmap_t freemap; |
21e9e7d5 TK |
1237 | hammer_blockmap_layer1_t layer1; |
1238 | hammer_blockmap_layer2_t layer2; | |
40043e7f | 1239 | hammer_buffer_t buffer = NULL; |
c3be93f2 MD |
1240 | hammer_off_t layer1_offset; |
1241 | hammer_off_t layer2_offset; | |
40043e7f | 1242 | hammer_off_t result_offset; |
cb51be26 | 1243 | hammer_off_t base_off; |
f31f6d84 | 1244 | hammer_reserve_t resv __debugvar; |
40043e7f | 1245 | int zone; |
40043e7f | 1246 | |
cb51be26 MD |
1247 | /* |
1248 | * Calculate the zone-2 offset. | |
1249 | */ | |
1250 | zone = HAMMER_ZONE_DECODE(zone_offset); | |
f4fe61c2 | 1251 | result_offset = hammer_xlate_to_zone2(zone_offset); |
cb51be26 MD |
1252 | |
1253 | /* | |
1254 | * Validate the allocation zone | |
1255 | */ | |
40043e7f MD |
1256 | root_volume = hammer_get_root_volume(hmp, errorp); |
1257 | if (*errorp) | |
1258 | return(0); | |
cb51be26 MD |
1259 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; |
1260 | KKASSERT(freemap->phys_offset != 0); | |
40043e7f MD |
1261 | |
1262 | /* | |
c3be93f2 | 1263 | * Dive layer 1. |
40043e7f | 1264 | */ |
cb51be26 MD |
1265 | layer1_offset = freemap->phys_offset + |
1266 | HAMMER_BLOCKMAP_LAYER1_OFFSET(zone_offset); | |
c3be93f2 | 1267 | layer1 = hammer_bread(hmp, layer1_offset, errorp, &buffer); |
cdb6e4e6 MD |
1268 | if (*errorp) |
1269 | goto failed; | |
cb51be26 | 1270 | KKASSERT(layer1->phys_offset != HAMMER_BLOCKMAP_UNAVAIL); |
4c09d9c4 | 1271 | if (!hammer_crc_test_layer1(hmp->version, layer1)) { |
db9f9d7f | 1272 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 1273 | if (!hammer_crc_test_layer1(hmp->version, layer1)) |
5134aacd | 1274 | hpanic("CRC FAILED: LAYER1"); |
db9f9d7f | 1275 | hammer_unlock(&hmp->blkmap_lock); |
19619882 | 1276 | } |
40043e7f MD |
1277 | |
1278 | /* | |
e04ee2de | 1279 | * Dive layer 2, each entry represents a big-block. |
40043e7f | 1280 | */ |
c3be93f2 | 1281 | layer2_offset = layer1->phys_offset + |
cb51be26 | 1282 | HAMMER_BLOCKMAP_LAYER2_OFFSET(zone_offset); |
c3be93f2 | 1283 | layer2 = hammer_bread(hmp, layer2_offset, errorp, &buffer); |
40043e7f | 1284 | |
cdb6e4e6 MD |
1285 | if (*errorp) |
1286 | goto failed; | |
cb51be26 | 1287 | if (layer2->zone == 0) { |
f4fe61c2 | 1288 | base_off = hammer_xlate_to_zone2(zone_offset & |
e04ee2de | 1289 | ~HAMMER_BIGBLOCK_MASK64); |
cb51be26 MD |
1290 | resv = RB_LOOKUP(hammer_res_rb_tree, &hmp->rb_resv_root, |
1291 | base_off); | |
1292 | KKASSERT(resv && resv->zone == zone); | |
1293 | ||
1294 | } else if (layer2->zone != zone) { | |
903fdd05 | 1295 | hpanic("bad zone %d/%d", layer2->zone, zone); |
cb51be26 | 1296 | } |
4c09d9c4 | 1297 | if (!hammer_crc_test_layer2(hmp->version, layer2)) { |
db9f9d7f | 1298 | hammer_lock_ex(&hmp->blkmap_lock); |
4c09d9c4 | 1299 | if (!hammer_crc_test_layer2(hmp->version, layer2)) |
5134aacd | 1300 | hpanic("CRC FAILED: LAYER2"); |
db9f9d7f | 1301 | hammer_unlock(&hmp->blkmap_lock); |
19619882 | 1302 | } |
c3be93f2 | 1303 | |
cdb6e4e6 | 1304 | failed: |
40043e7f MD |
1305 | if (buffer) |
1306 | hammer_rel_buffer(buffer, 0); | |
1307 | hammer_rel_volume(root_volume, 0); | |
1308 | if (hammer_debug_general & 0x0800) { | |
35a5249b TK |
1309 | hdkprintf("%016jx -> %016jx\n", |
1310 | (intmax_t)zone_offset, (intmax_t)result_offset); | |
40043e7f MD |
1311 | } |
1312 | return(result_offset); | |
1313 | } | |
1314 | ||
bf686dbe MD |
1315 | |
1316 | /* | |
cb51be26 | 1317 | * Check space availability |
b0aab9b9 MD |
1318 | * |
1319 | * MPSAFE - does not require fs_token | |
bf686dbe | 1320 | */ |
cb51be26 | 1321 | int |
0f65be10 | 1322 | _hammer_checkspace(hammer_mount_t hmp, int slop, int64_t *resp) |
bf686dbe | 1323 | { |
cb51be26 MD |
1324 | const int in_size = sizeof(struct hammer_inode_data) + |
1325 | sizeof(union hammer_btree_elm); | |
1326 | const int rec_size = (sizeof(union hammer_btree_elm) * 2); | |
a7e9bef1 | 1327 | int64_t usedbytes; |
cb51be26 | 1328 | |
a7e9bef1 MD |
1329 | usedbytes = hmp->rsv_inodes * in_size + |
1330 | hmp->rsv_recs * rec_size + | |
1331 | hmp->rsv_databytes + | |
e04ee2de | 1332 | ((int64_t)hmp->rsv_fromdelay << HAMMER_BIGBLOCK_BITS) + |
69906b47 | 1333 | ((int64_t)hammer_limit_dirtybufspace) + |
e04ee2de | 1334 | (slop << HAMMER_BIGBLOCK_BITS); |
a7e9bef1 | 1335 | |
0f65be10 MD |
1336 | if (resp) |
1337 | *resp = usedbytes; | |
a7e9bef1 | 1338 | |
7b6ccb11 | 1339 | if (hmp->copy_stat_freebigblocks >= |
e04ee2de | 1340 | (usedbytes >> HAMMER_BIGBLOCK_BITS)) { |
cb51be26 | 1341 | return(0); |
7b6ccb11 | 1342 | } |
0f243a86 | 1343 | |
cb51be26 | 1344 | return (ENOSPC); |
6f97fce3 MD |
1345 | } |
1346 | ||
0871ec5c TK |
1347 | static int |
1348 | hammer_check_volume(hammer_mount_t hmp, hammer_off_t *offsetp) | |
1349 | { | |
1350 | hammer_blockmap_t freemap; | |
21e9e7d5 | 1351 | hammer_blockmap_layer1_t layer1; |
0871ec5c | 1352 | hammer_buffer_t buffer1 = NULL; |
ff2ee76c TK |
1353 | hammer_off_t layer1_offset; |
1354 | int error = 0; | |
0871ec5c | 1355 | |
0871ec5c TK |
1356 | freemap = &hmp->blockmap[HAMMER_ZONE_FREEMAP_INDEX]; |
1357 | ||
1358 | layer1_offset = freemap->phys_offset + | |
ff2ee76c | 1359 | HAMMER_BLOCKMAP_LAYER1_OFFSET(*offsetp); |
0871ec5c TK |
1360 | layer1 = hammer_bread(hmp, layer1_offset, &error, &buffer1); |
1361 | if (error) | |
1362 | goto end; | |
1363 | ||
1364 | /* | |
ff2ee76c TK |
1365 | * No more physically available space in layer1s |
1366 | * of the current volume, go to the next volume. | |
0871ec5c | 1367 | */ |
ff2ee76c TK |
1368 | if (layer1->phys_offset == HAMMER_BLOCKMAP_UNAVAIL) |
1369 | hammer_skip_volume(offsetp); | |
0871ec5c TK |
1370 | end: |
1371 | if (buffer1) | |
1372 | hammer_rel_buffer(buffer1, 0); | |
1373 | return(error); | |
1374 | } | |
ff2ee76c TK |
1375 | |
1376 | static void | |
1377 | hammer_skip_volume(hammer_off_t *offsetp) | |
1378 | { | |
1379 | hammer_off_t offset; | |
1380 | int zone, vol_no; | |
1381 | ||
1382 | offset = *offsetp; | |
1383 | zone = HAMMER_ZONE_DECODE(offset); | |
1384 | vol_no = HAMMER_VOL_DECODE(offset) + 1; | |
1385 | KKASSERT(vol_no <= HAMMER_MAX_VOLUMES); | |
1386 | ||
1387 | if (vol_no == HAMMER_MAX_VOLUMES) { /* wrap */ | |
1388 | vol_no = 0; | |
1389 | ++zone; | |
1390 | } | |
1391 | ||
1392 | *offsetp = HAMMER_ENCODE(zone, vol_no, 0); | |
1393 | } |