HAMMER 46/Many: Performance pass, media changes, bug fixes.
[dragonfly.git] / sys / vfs / hammer / hammer_recover.c
blob872f302e0d4323179c8e9e59eccc634306017868
1 /*
2 * Copyright (c) 2008 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>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
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.
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.
34 * $DragonFly: src/sys/vfs/hammer/hammer_recover.c,v 1.18 2008/05/18 01:48:50 dillon Exp $
37 #include "hammer.h"
39 static int hammer_check_tail_signature(hammer_fifo_tail_t tail,
40 hammer_off_t end_off);
41 static void hammer_recover_copy_undo(hammer_off_t undo_offset,
42 char *src, char *dst, int bytes);
43 #if 0
44 static void hammer_recover_debug_dump(int w, char *buf, int bytes);
45 #endif
46 static int hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo,
47 int bytes);
50 * Recover a filesystem on mount
52 * NOTE: No information from the root volume has been cached in the
53 * hammer_mount structure yet, so we need to access the root volume's
54 * buffer directly.
56 int
57 hammer_recover(hammer_mount_t hmp, hammer_volume_t root_volume)
59 hammer_blockmap_t rootmap;
60 hammer_buffer_t buffer;
61 hammer_off_t scan_offset;
62 hammer_off_t bytes;
63 hammer_fifo_tail_t tail;
64 hammer_fifo_undo_t undo;
65 int error;
68 * Examine the UNDO FIFO. If it is empty the filesystem is clean
69 * and no action need be taken.
71 * NOTE: hmp->blockmap has not been initialized yet so use the
72 * root volume's ondisk buffer directly.
74 rootmap = &root_volume->ondisk->vol0_blockmap[HAMMER_ZONE_UNDO_INDEX];
75 hmp->flusher_undo_start = rootmap->next_offset;
77 if (rootmap->first_offset == rootmap->next_offset)
78 return(0);
80 if (rootmap->next_offset >= rootmap->first_offset) {
81 bytes = rootmap->next_offset - rootmap->first_offset;
82 } else {
83 bytes = rootmap->alloc_offset - rootmap->first_offset +
84 (rootmap->next_offset & HAMMER_OFF_LONG_MASK);
86 kprintf("HAMMER(%s) Start Recovery %016llx - %016llx "
87 "(%lld bytes of UNDO)\n",
88 root_volume->ondisk->vol_name,
89 rootmap->first_offset, rootmap->next_offset,
90 bytes);
91 if (bytes > (rootmap->alloc_offset & HAMMER_OFF_LONG_MASK)) {
92 kprintf("Undo size is absurd, unable to mount\n");
93 return(EIO);
97 * Scan the UNDOs backwards.
99 scan_offset = rootmap->next_offset;
100 buffer = NULL;
101 if (scan_offset > rootmap->alloc_offset) {
102 kprintf("HAMMER(%s) UNDO record at %016llx FIFO overflow\n",
103 root_volume->ondisk->vol_name,
104 scan_offset);
105 error = EIO;
106 goto done;
109 while ((int64_t)bytes > 0) {
110 if (hammer_debug_general & 0x0080)
111 kprintf("scan_offset %016llx\n", scan_offset);
112 if (scan_offset == HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
113 scan_offset = rootmap->alloc_offset;
114 continue;
116 if (scan_offset - sizeof(*tail) <
117 HAMMER_ZONE_ENCODE(HAMMER_ZONE_UNDO_INDEX, 0)) {
118 kprintf("HAMMER(%s) UNDO record at %016llx FIFO "
119 "underflow\n",
120 root_volume->ondisk->vol_name,
121 scan_offset);
122 error = EIO;
123 break;
125 tail = hammer_bread(hmp, scan_offset - sizeof(*tail),
126 &error, &buffer);
127 if (error) {
128 kprintf("HAMMER(%s) Unable to read UNDO TAIL "
129 "at %016llx\n",
130 root_volume->ondisk->vol_name,
131 scan_offset - sizeof(*tail));
132 break;
135 if (hammer_check_tail_signature(tail, scan_offset) != 0) {
136 kprintf("HAMMER(%s) Illegal UNDO TAIL signature "
137 "at %016llx\n",
138 root_volume->ondisk->vol_name,
139 scan_offset - sizeof(*tail));
140 error = EIO;
141 break;
143 undo = (void *)((char *)tail + sizeof(*tail) - tail->tail_size);
145 error = hammer_recover_undo(hmp, undo,
146 HAMMER_BUFSIZE -
147 (int)((char *)undo - (char *)buffer->ondisk));
148 if (error) {
149 kprintf("HAMMER(%s) UNDO record at %016llx failed\n",
150 root_volume->ondisk->vol_name,
151 scan_offset - tail->tail_size);
152 break;
154 scan_offset -= tail->tail_size;
155 bytes -= tail->tail_size;
157 done:
159 * Reload flusher_undo_start to kick off the UNDO sequencing.
161 hmp->flusher_undo_start = rootmap->next_offset;
162 if (buffer)
163 hammer_rel_buffer(buffer, 0);
164 return (error);
167 static int
168 hammer_check_tail_signature(hammer_fifo_tail_t tail, hammer_off_t end_off)
170 int max_bytes;
172 max_bytes = ((end_off - sizeof(*tail)) & HAMMER_BUFMASK);
173 max_bytes += sizeof(*tail);
176 * tail overlaps buffer boundary
178 if (((end_off - sizeof(*tail)) ^ (end_off - 1)) & ~HAMMER_BUFMASK64) {
179 return(1);
183 * signature check, the tail signature is allowed to be the head
184 * signature only for 8-byte PADs.
186 switch(tail->tail_signature) {
187 case HAMMER_TAIL_SIGNATURE:
188 break;
189 case HAMMER_HEAD_SIGNATURE:
190 if (tail->tail_type != HAMMER_HEAD_TYPE_PAD ||
191 tail->tail_size != sizeof(*tail)) {
192 return(2);
194 break;
198 * The undo structure must not overlap a buffer boundary.
200 if (tail->tail_size < 0 || tail->tail_size > max_bytes) {
201 return(3);
203 return(0);
206 static int
207 hammer_recover_undo(hammer_mount_t hmp, hammer_fifo_undo_t undo, int bytes)
209 hammer_fifo_tail_t tail;
210 hammer_volume_t volume;
211 hammer_buffer_t buffer;
212 hammer_off_t buf_offset;
213 int zone;
214 int error;
215 int vol_no;
216 int max_bytes;
217 u_int32_t offset;
218 u_int32_t crc;
221 * Basic sanity checks
223 if (bytes < HAMMER_HEAD_ALIGN) {
224 kprintf("HAMMER: Undo alignment error (%d)\n", bytes);
225 return(EIO);
227 if (undo->head.hdr_signature != HAMMER_HEAD_SIGNATURE) {
228 kprintf("HAMMER: Bad head signature %04x\n",
229 undo->head.hdr_signature);
230 return(EIO);
232 if (undo->head.hdr_size < HAMMER_HEAD_ALIGN ||
233 undo->head.hdr_size > bytes) {
234 kprintf("HAMMER: Bad size %d\n", bytes);
235 return(EIO);
239 * Skip PAD records. Note that PAD records also do not require
240 * a tail and may have a truncated structure.
242 if (undo->head.hdr_type == HAMMER_HEAD_TYPE_PAD)
243 return(0);
246 * Check the CRC
248 crc = crc32(undo, HAMMER_FIFO_HEAD_CRCOFF) ^
249 crc32(&undo->head + 1, undo->head.hdr_size - sizeof(undo->head));
250 if (undo->head.hdr_crc != crc) {
251 kprintf("HAMMER: Undo record CRC failed %08x %08x\n",
252 undo->head.hdr_crc, crc);
253 return(EIO);
258 * Check the tail
260 bytes = undo->head.hdr_size;
261 tail = (void *)((char *)undo + bytes - sizeof(*tail));
262 if (tail->tail_size != undo->head.hdr_size) {
263 kprintf("HAMMER: Bad tail size %d\n", tail->tail_size);
264 return(EIO);
266 if (tail->tail_type != undo->head.hdr_type) {
267 kprintf("HAMMER: Bad tail type %d\n", tail->tail_type);
268 return(EIO);
272 * Only process UNDO records
274 if (undo->head.hdr_type != HAMMER_HEAD_TYPE_UNDO)
275 return(0);
278 * Validate the UNDO record.
280 max_bytes = undo->head.hdr_size - sizeof(*undo) - sizeof(*tail);
281 if (undo->undo_data_bytes < 0 || undo->undo_data_bytes > max_bytes) {
282 kprintf("HAMMER: Corrupt UNDO record, undo_data_bytes %d/%d\n",
283 undo->undo_data_bytes, max_bytes);
284 return(EIO);
288 * The undo offset may only be a zone-1 or zone-2 offset.
290 * Currently we only support a zone-1 offset representing the
291 * volume header.
293 zone = HAMMER_ZONE_DECODE(undo->undo_offset);
294 offset = undo->undo_offset & HAMMER_BUFMASK;
296 if (offset + undo->undo_data_bytes > HAMMER_BUFSIZE) {
297 kprintf("HAMMER: Corrupt UNDO record, bad offset\n");
298 return (EIO);
301 switch(zone) {
302 case HAMMER_ZONE_RAW_VOLUME_INDEX:
303 vol_no = HAMMER_VOL_DECODE(undo->undo_offset);
304 volume = hammer_get_volume(hmp, vol_no, &error);
305 if (volume == NULL) {
306 kprintf("HAMMER: UNDO record, "
307 "cannot access volume %d\n", vol_no);
308 break;
310 hammer_modify_volume(NULL, volume, NULL, 0);
311 hammer_recover_copy_undo(undo->undo_offset,
312 (char *)(undo + 1),
313 (char *)volume->ondisk + offset,
314 undo->undo_data_bytes);
315 hammer_modify_volume_done(volume);
316 hammer_io_flush(&volume->io);
317 hammer_rel_volume(volume, 0);
318 break;
319 case HAMMER_ZONE_RAW_BUFFER_INDEX:
320 buf_offset = undo->undo_offset & ~HAMMER_BUFMASK64;
321 buffer = hammer_get_buffer(hmp, buf_offset, 0, &error);
322 if (buffer == NULL) {
323 kprintf("HAMMER: UNDO record, "
324 "cannot access buffer %016llx\n",
325 undo->undo_offset);
326 break;
328 hammer_modify_buffer(NULL, buffer, NULL, 0);
329 hammer_recover_copy_undo(undo->undo_offset,
330 (char *)(undo + 1),
331 (char *)buffer->ondisk + offset,
332 undo->undo_data_bytes);
333 hammer_modify_buffer_done(buffer);
334 hammer_io_flush(&buffer->io);
335 hammer_rel_buffer(buffer, 0);
336 break;
337 default:
338 kprintf("HAMMER: Corrupt UNDO record\n");
339 error = EIO;
341 return (error);
344 static void
345 hammer_recover_copy_undo(hammer_off_t undo_offset,
346 char *src, char *dst, int bytes)
348 hkprintf("U");
349 if (hammer_debug_general & 0x0080)
350 kprintf("NDO %016llx: %d\n", undo_offset, bytes);
351 #if 0
352 kprintf("UNDO %016llx:", undo_offset);
353 hammer_recover_debug_dump(22, dst, bytes);
354 kprintf("%22s", "to:");
355 hammer_recover_debug_dump(22, src, bytes);
356 #endif
357 bcopy(src, dst, bytes);
360 #if 0
362 static void
363 hammer_recover_debug_dump(int w, char *buf, int bytes)
365 int i;
367 for (i = 0; i < bytes; ++i) {
368 if (i && (i & 15) == 0)
369 kprintf("\n%*.*s", w, w, "");
370 kprintf(" %02x", (unsigned char)buf[i]);
372 kprintf("\n");
375 #endif