Blame view
fs/nilfs2/recovery.c
24.4 KB
0f3e1c7f2 nilfs2: recovery ... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/* * recovery.c - NILFS recovery logic * * Copyright (C) 2005-2008 Nippon Telegraph and Telephone Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * Written by Ryusuke Konishi <ryusuke@osrg.net> */ #include <linux/buffer_head.h> #include <linux/blkdev.h> #include <linux/swap.h> |
5a0e3ad6a include cleanup: ... |
26 |
#include <linux/slab.h> |
0f3e1c7f2 nilfs2: recovery ... |
27 28 29 30 31 |
#include <linux/crc32.h> #include "nilfs.h" #include "segment.h" #include "sufile.h" #include "page.h" |
0f3e1c7f2 nilfs2: recovery ... |
32 33 34 35 36 37 38 39 40 41 42 |
#include "segbuf.h" /* * Segment check result */ enum { NILFS_SEG_VALID, NILFS_SEG_NO_SUPER_ROOT, NILFS_SEG_FAIL_IO, NILFS_SEG_FAIL_MAGIC, NILFS_SEG_FAIL_SEQ, |
0f3e1c7f2 nilfs2: recovery ... |
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT, NILFS_SEG_FAIL_CHECKSUM_FULL, NILFS_SEG_FAIL_CONSISTENCY, }; /* work structure for recovery */ struct nilfs_recovery_block { ino_t ino; /* Inode number of the file that this block belongs to */ sector_t blocknr; /* block number */ __u64 vblocknr; /* virtual block number */ unsigned long blkoff; /* File offset of the data block (per block) */ struct list_head list; }; static int nilfs_warn_segment_error(int err) { switch (err) { case NILFS_SEG_FAIL_IO: printk(KERN_WARNING "NILFS warning: I/O error on loading last segment "); return -EIO; case NILFS_SEG_FAIL_MAGIC: printk(KERN_WARNING "NILFS warning: Segment magic number invalid "); break; case NILFS_SEG_FAIL_SEQ: printk(KERN_WARNING "NILFS warning: Sequence number mismatch "); break; |
0f3e1c7f2 nilfs2: recovery ... |
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
case NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT: printk(KERN_WARNING "NILFS warning: Checksum error in super root "); break; case NILFS_SEG_FAIL_CHECKSUM_FULL: printk(KERN_WARNING "NILFS warning: Checksum error in segment payload "); break; case NILFS_SEG_FAIL_CONSISTENCY: printk(KERN_WARNING "NILFS warning: Inconsistent segment "); break; case NILFS_SEG_NO_SUPER_ROOT: printk(KERN_WARNING "NILFS warning: No super root in the last segment "); break; |
0f3e1c7f2 nilfs2: recovery ... |
97 98 99 |
} return -EINVAL; } |
0f3e1c7f2 nilfs2: recovery ... |
100 |
/** |
8b94025c0 nilfs2: refactor ... |
101 102 |
* nilfs_compute_checksum - compute checksum of blocks continuously * @nilfs: nilfs object |
0f3e1c7f2 nilfs2: recovery ... |
103 104 105 106 107 108 109 |
* @bhs: buffer head of start block * @sum: place to store result * @offset: offset bytes in the first block * @check_bytes: number of bytes to be checked * @start: DBN of start block * @nblock: number of blocks to be checked */ |
8b94025c0 nilfs2: refactor ... |
110 111 112 113 |
static int nilfs_compute_checksum(struct the_nilfs *nilfs, struct buffer_head *bhs, u32 *sum, unsigned long offset, u64 check_bytes, sector_t start, unsigned long nblock) |
0f3e1c7f2 nilfs2: recovery ... |
114 |
{ |
8b94025c0 nilfs2: refactor ... |
115 |
unsigned int blocksize = nilfs->ns_blocksize; |
0f3e1c7f2 nilfs2: recovery ... |
116 117 118 119 120 121 |
unsigned long size; u32 crc; BUG_ON(offset >= blocksize); check_bytes -= offset; size = min_t(u64, check_bytes, blocksize - offset); |
8b94025c0 nilfs2: refactor ... |
122 |
crc = crc32_le(nilfs->ns_crc_seed, |
0f3e1c7f2 nilfs2: recovery ... |
123 124 125 |
(unsigned char *)bhs->b_data + offset, size); if (--nblock > 0) { do { |
8b94025c0 nilfs2: refactor ... |
126 127 128 |
struct buffer_head *bh; bh = __bread(nilfs->ns_bdev, ++start, blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
129 130 131 132 133 134 135 136 137 138 139 140 141 142 |
if (!bh) return -EIO; check_bytes -= size; size = min_t(u64, check_bytes, blocksize); crc = crc32_le(crc, bh->b_data, size); brelse(bh); } while (--nblock > 0); } *sum = crc; return 0; } /** * nilfs_read_super_root_block - read super root block |
8b94025c0 nilfs2: refactor ... |
143 |
* @nilfs: nilfs object |
0f3e1c7f2 nilfs2: recovery ... |
144 145 146 147 |
* @sr_block: disk block number of the super root block * @pbh: address of a buffer_head pointer to return super root buffer * @check: CRC check flag */ |
8b94025c0 nilfs2: refactor ... |
148 |
int nilfs_read_super_root_block(struct the_nilfs *nilfs, sector_t sr_block, |
0f3e1c7f2 nilfs2: recovery ... |
149 150 151 152 153 154 155 156 |
struct buffer_head **pbh, int check) { struct buffer_head *bh_sr; struct nilfs_super_root *sr; u32 crc; int ret; *pbh = NULL; |
8b94025c0 nilfs2: refactor ... |
157 |
bh_sr = __bread(nilfs->ns_bdev, sr_block, nilfs->ns_blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
158 159 160 161 162 163 164 165 |
if (unlikely(!bh_sr)) { ret = NILFS_SEG_FAIL_IO; goto failed; } sr = (struct nilfs_super_root *)bh_sr->b_data; if (check) { unsigned bytes = le16_to_cpu(sr->sr_bytes); |
8b94025c0 nilfs2: refactor ... |
166 |
if (bytes == 0 || bytes > nilfs->ns_blocksize) { |
0f3e1c7f2 nilfs2: recovery ... |
167 168 169 |
ret = NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT; goto failed_bh; } |
8b94025c0 nilfs2: refactor ... |
170 171 172 |
if (nilfs_compute_checksum( nilfs, bh_sr, &crc, sizeof(sr->sr_sum), bytes, sr_block, 1)) { |
0f3e1c7f2 nilfs2: recovery ... |
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 |
ret = NILFS_SEG_FAIL_IO; goto failed_bh; } if (crc != le32_to_cpu(sr->sr_sum)) { ret = NILFS_SEG_FAIL_CHECKSUM_SUPER_ROOT; goto failed_bh; } } *pbh = bh_sr; return 0; failed_bh: brelse(bh_sr); failed: return nilfs_warn_segment_error(ret); } /** |
354fa8be2 nilfs2: divide lo... |
192 |
* nilfs_read_log_header - read summary header of the specified log |
8b94025c0 nilfs2: refactor ... |
193 |
* @nilfs: nilfs object |
354fa8be2 nilfs2: divide lo... |
194 195 |
* @start_blocknr: start block number of the log * @sum: pointer to return segment summary structure |
0f3e1c7f2 nilfs2: recovery ... |
196 |
*/ |
354fa8be2 nilfs2: divide lo... |
197 198 199 |
static struct buffer_head * nilfs_read_log_header(struct the_nilfs *nilfs, sector_t start_blocknr, struct nilfs_segment_summary **sum) |
0f3e1c7f2 nilfs2: recovery ... |
200 201 |
{ struct buffer_head *bh_sum; |
354fa8be2 nilfs2: divide lo... |
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
bh_sum = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); if (bh_sum) *sum = (struct nilfs_segment_summary *)bh_sum->b_data; return bh_sum; } /** * nilfs_validate_log - verify consistency of log * @nilfs: nilfs object * @seg_seq: sequence number of segment * @bh_sum: buffer head of summary block * @sum: segment summary struct */ static int nilfs_validate_log(struct the_nilfs *nilfs, u64 seg_seq, struct buffer_head *bh_sum, struct nilfs_segment_summary *sum) { |
03f29365e nilfs2: delete un... |
220 221 |
unsigned long nblock; u32 crc; |
354fa8be2 nilfs2: divide lo... |
222 |
int ret; |
0f3e1c7f2 nilfs2: recovery ... |
223 |
|
354fa8be2 nilfs2: divide lo... |
224 225 |
ret = NILFS_SEG_FAIL_MAGIC; if (le32_to_cpu(sum->ss_magic) != NILFS_SEGSUM_MAGIC) |
0f3e1c7f2 nilfs2: recovery ... |
226 |
goto out; |
354fa8be2 nilfs2: divide lo... |
227 228 229 |
ret = NILFS_SEG_FAIL_SEQ; if (le64_to_cpu(sum->ss_seq) != seg_seq) goto out; |
0f3e1c7f2 nilfs2: recovery ... |
230 |
|
354fa8be2 nilfs2: divide lo... |
231 232 233 |
nblock = le32_to_cpu(sum->ss_nblocks); ret = NILFS_SEG_FAIL_CONSISTENCY; if (unlikely(nblock == 0 || nblock > nilfs->ns_blocks_per_segment)) |
0f3e1c7f2 nilfs2: recovery ... |
234 |
/* This limits the number of blocks read in the CRC check */ |
354fa8be2 nilfs2: divide lo... |
235 236 237 |
goto out; ret = NILFS_SEG_FAIL_IO; |
8b94025c0 nilfs2: refactor ... |
238 239 |
if (nilfs_compute_checksum(nilfs, bh_sum, &crc, sizeof(sum->ss_datasum), ((u64)nblock << nilfs->ns_blocksize_bits), |
354fa8be2 nilfs2: divide lo... |
240 241 242 243 244 245 246 247 |
bh_sum->b_blocknr, nblock)) goto out; ret = NILFS_SEG_FAIL_CHECKSUM_FULL; if (crc != le32_to_cpu(sum->ss_datasum)) goto out; ret = 0; out: |
0f3e1c7f2 nilfs2: recovery ... |
248 249 |
return ret; } |
8b94025c0 nilfs2: refactor ... |
250 251 252 253 254 255 256 257 258 259 |
/** * nilfs_read_summary_info - read an item on summary blocks of a log * @nilfs: nilfs object * @pbh: the current buffer head on summary blocks [in, out] * @offset: the current byte offset on summary blocks [in, out] * @bytes: byte size of the item to be read */ static void *nilfs_read_summary_info(struct the_nilfs *nilfs, struct buffer_head **pbh, unsigned int *offset, unsigned int bytes) |
0f3e1c7f2 nilfs2: recovery ... |
260 261 262 263 264 265 266 267 |
{ void *ptr; sector_t blocknr; BUG_ON((*pbh)->b_size < *offset); if (bytes > (*pbh)->b_size - *offset) { blocknr = (*pbh)->b_blocknr; brelse(*pbh); |
8b94025c0 nilfs2: refactor ... |
268 269 |
*pbh = __bread(nilfs->ns_bdev, blocknr + 1, nilfs->ns_blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
270 271 272 273 274 275 276 277 |
if (unlikely(!*pbh)) return NULL; *offset = 0; } ptr = (*pbh)->b_data + *offset; *offset += bytes; return ptr; } |
8b94025c0 nilfs2: refactor ... |
278 279 280 281 282 283 284 285 286 287 288 289 |
/** * nilfs_skip_summary_info - skip items on summary blocks of a log * @nilfs: nilfs object * @pbh: the current buffer head on summary blocks [in, out] * @offset: the current byte offset on summary blocks [in, out] * @bytes: byte size of the item to be skipped * @count: number of items to be skipped */ static void nilfs_skip_summary_info(struct the_nilfs *nilfs, struct buffer_head **pbh, unsigned int *offset, unsigned int bytes, unsigned long count) |
0f3e1c7f2 nilfs2: recovery ... |
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 |
{ unsigned int rest_item_in_current_block = ((*pbh)->b_size - *offset) / bytes; if (count <= rest_item_in_current_block) { *offset += bytes * count; } else { sector_t blocknr = (*pbh)->b_blocknr; unsigned int nitem_per_block = (*pbh)->b_size / bytes; unsigned int bcnt; count -= rest_item_in_current_block; bcnt = DIV_ROUND_UP(count, nitem_per_block); *offset = bytes * (count - (bcnt - 1) * nitem_per_block); brelse(*pbh); |
8b94025c0 nilfs2: refactor ... |
306 307 |
*pbh = __bread(nilfs->ns_bdev, blocknr + bcnt, nilfs->ns_blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
308 309 |
} } |
8b94025c0 nilfs2: refactor ... |
310 311 312 313 |
/** * nilfs_scan_dsync_log - get block information of a log written for data sync * @nilfs: nilfs object * @start_blocknr: start block number of the log |
85655484f nilfs2: do not us... |
314 |
* @sum: log summary information |
8b94025c0 nilfs2: refactor ... |
315 316 317 |
* @head: list head to add nilfs_recovery_block struct */ static int nilfs_scan_dsync_log(struct the_nilfs *nilfs, sector_t start_blocknr, |
85655484f nilfs2: do not us... |
318 |
struct nilfs_segment_summary *sum, |
8b94025c0 nilfs2: refactor ... |
319 |
struct list_head *head) |
0f3e1c7f2 nilfs2: recovery ... |
320 321 322 |
{ struct buffer_head *bh; unsigned int offset; |
85655484f nilfs2: do not us... |
323 324 |
u32 nfinfo, sumbytes; sector_t blocknr; |
0f3e1c7f2 nilfs2: recovery ... |
325 326 |
ino_t ino; int err = -EIO; |
85655484f nilfs2: do not us... |
327 |
nfinfo = le32_to_cpu(sum->ss_nfinfo); |
0f3e1c7f2 nilfs2: recovery ... |
328 329 |
if (!nfinfo) return 0; |
85655484f nilfs2: do not us... |
330 331 |
sumbytes = le32_to_cpu(sum->ss_sumbytes); blocknr = start_blocknr + DIV_ROUND_UP(sumbytes, nilfs->ns_blocksize); |
8b94025c0 nilfs2: refactor ... |
332 |
bh = __bread(nilfs->ns_bdev, start_blocknr, nilfs->ns_blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
333 334 |
if (unlikely(!bh)) goto out; |
85655484f nilfs2: do not us... |
335 |
offset = le16_to_cpu(sum->ss_bytes); |
0f3e1c7f2 nilfs2: recovery ... |
336 337 338 |
for (;;) { unsigned long nblocks, ndatablk, nnodeblk; struct nilfs_finfo *finfo; |
8b94025c0 nilfs2: refactor ... |
339 340 |
finfo = nilfs_read_summary_info(nilfs, &bh, &offset, sizeof(*finfo)); |
0f3e1c7f2 nilfs2: recovery ... |
341 342 343 344 345 346 347 348 349 350 351 |
if (unlikely(!finfo)) goto out; ino = le64_to_cpu(finfo->fi_ino); nblocks = le32_to_cpu(finfo->fi_nblocks); ndatablk = le32_to_cpu(finfo->fi_ndatablk); nnodeblk = nblocks - ndatablk; while (ndatablk-- > 0) { struct nilfs_recovery_block *rb; struct nilfs_binfo_v *binfo; |
8b94025c0 nilfs2: refactor ... |
352 353 |
binfo = nilfs_read_summary_info(nilfs, &bh, &offset, sizeof(*binfo)); |
0f3e1c7f2 nilfs2: recovery ... |
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
if (unlikely(!binfo)) goto out; rb = kmalloc(sizeof(*rb), GFP_NOFS); if (unlikely(!rb)) { err = -ENOMEM; goto out; } rb->ino = ino; rb->blocknr = blocknr++; rb->vblocknr = le64_to_cpu(binfo->bi_vblocknr); rb->blkoff = le64_to_cpu(binfo->bi_blkoff); /* INIT_LIST_HEAD(&rb->list); */ list_add_tail(&rb->list, head); } if (--nfinfo == 0) break; |
8b94025c0 nilfs2: refactor ... |
371 372 373 |
blocknr += nnodeblk; /* always 0 for data sync logs */ nilfs_skip_summary_info(nilfs, &bh, &offset, sizeof(__le64), nnodeblk); |
0f3e1c7f2 nilfs2: recovery ... |
374 375 376 377 378 379 380 381 382 383 384 385 |
if (unlikely(!bh)) goto out; } err = 0; out: brelse(bh); /* brelse(NULL) is just ignored */ return err; } static void dispose_recovery_list(struct list_head *head) { while (!list_empty(head)) { |
0cc128388 nilfs2: use list_... |
386 387 388 |
struct nilfs_recovery_block *rb; rb = list_first_entry(head, struct nilfs_recovery_block, list); |
0f3e1c7f2 nilfs2: recovery ... |
389 390 391 392 |
list_del(&rb->list); kfree(rb); } } |
654137dd4 nilfs2: remove he... |
393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 |
struct nilfs_segment_entry { struct list_head list; __u64 segnum; }; static int nilfs_segment_list_add(struct list_head *head, __u64 segnum) { struct nilfs_segment_entry *ent = kmalloc(sizeof(*ent), GFP_NOFS); if (unlikely(!ent)) return -ENOMEM; ent->segnum = segnum; INIT_LIST_HEAD(&ent->list); list_add_tail(&ent->list, head); return 0; } |
0f3e1c7f2 nilfs2: recovery ... |
410 411 412 |
void nilfs_dispose_segment_list(struct list_head *head) { while (!list_empty(head)) { |
0cc128388 nilfs2: use list_... |
413 414 415 |
struct nilfs_segment_entry *ent; ent = list_first_entry(head, struct nilfs_segment_entry, list); |
0f3e1c7f2 nilfs2: recovery ... |
416 |
list_del(&ent->list); |
654137dd4 nilfs2: remove he... |
417 |
kfree(ent); |
0f3e1c7f2 nilfs2: recovery ... |
418 419 420 421 |
} } static int nilfs_prepare_segment_for_recovery(struct the_nilfs *nilfs, |
f7545144c nilfs2: use sb in... |
422 |
struct super_block *sb, |
0f3e1c7f2 nilfs2: recovery ... |
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
struct nilfs_recovery_info *ri) { struct list_head *head = &ri->ri_used_segments; struct nilfs_segment_entry *ent, *n; struct inode *sufile = nilfs->ns_sufile; __u64 segnum[4]; int err; int i; segnum[0] = nilfs->ns_segnum; segnum[1] = nilfs->ns_nextnum; segnum[2] = ri->ri_segnum; segnum[3] = ri->ri_nextnum; /* * Releasing the next segment of the latest super root. * The next segment is invalidated by this recovery. */ err = nilfs_sufile_free(sufile, segnum[1]); if (unlikely(err)) goto failed; |
0f3e1c7f2 nilfs2: recovery ... |
444 |
for (i = 1; i < 4; i++) { |
654137dd4 nilfs2: remove he... |
445 446 |
err = nilfs_segment_list_add(head, segnum[i]); if (unlikely(err)) |
0f3e1c7f2 nilfs2: recovery ... |
447 |
goto failed; |
0f3e1c7f2 nilfs2: recovery ... |
448 449 450 451 |
} /* * Collecting segments written after the latest super root. |
2c2e52fc4 nilfs2: extend ni... |
452 |
* These are marked dirty to avoid being reallocated in the next write. |
0f3e1c7f2 nilfs2: recovery ... |
453 454 |
*/ list_for_each_entry_safe(ent, n, head, list) { |
c85399c2d nilfs2: fix possi... |
455 456 457 458 |
if (ent->segnum != segnum[0]) { err = nilfs_sufile_scrap(sufile, ent->segnum); if (unlikely(err)) goto failed; |
0f3e1c7f2 nilfs2: recovery ... |
459 |
} |
2c2e52fc4 nilfs2: extend ni... |
460 |
list_del(&ent->list); |
654137dd4 nilfs2: remove he... |
461 |
kfree(ent); |
0f3e1c7f2 nilfs2: recovery ... |
462 |
} |
0f3e1c7f2 nilfs2: recovery ... |
463 |
|
0f3e1c7f2 nilfs2: recovery ... |
464 465 466 467 468 469 470 471 |
/* Allocate new segments for recovery */ err = nilfs_sufile_alloc(sufile, &segnum[0]); if (unlikely(err)) goto failed; nilfs->ns_pseg_offset = 0; nilfs->ns_seg_seq = ri->ri_seq + 2; nilfs->ns_nextnum = nilfs->ns_segnum = segnum[0]; |
0f3e1c7f2 nilfs2: recovery ... |
472 473 474 475 476 |
failed: /* No need to recover sufile because it will be destroyed on error */ return err; } |
8b94025c0 nilfs2: refactor ... |
477 |
static int nilfs_recovery_copy_block(struct the_nilfs *nilfs, |
0f3e1c7f2 nilfs2: recovery ... |
478 479 480 481 482 |
struct nilfs_recovery_block *rb, struct page *page) { struct buffer_head *bh_org; void *kaddr; |
8b94025c0 nilfs2: refactor ... |
483 |
bh_org = __bread(nilfs->ns_bdev, rb->blocknr, nilfs->ns_blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
484 485 486 487 488 489 490 491 492 |
if (unlikely(!bh_org)) return -EIO; kaddr = kmap_atomic(page, KM_USER0); memcpy(kaddr + bh_offset(bh_org), bh_org->b_data, bh_org->b_size); kunmap_atomic(kaddr, KM_USER0); brelse(bh_org); return 0; } |
8b94025c0 nilfs2: refactor ... |
493 |
static int nilfs_recover_dsync_blocks(struct the_nilfs *nilfs, |
f7545144c nilfs2: use sb in... |
494 |
struct super_block *sb, |
4d8d9293d nilfs2: set point... |
495 |
struct nilfs_root *root, |
8b94025c0 nilfs2: refactor ... |
496 497 |
struct list_head *head, unsigned long *nr_salvaged_blocks) |
0f3e1c7f2 nilfs2: recovery ... |
498 499 500 |
{ struct inode *inode; struct nilfs_recovery_block *rb, *n; |
8b94025c0 nilfs2: refactor ... |
501 |
unsigned blocksize = nilfs->ns_blocksize; |
0f3e1c7f2 nilfs2: recovery ... |
502 503 504 505 506 |
struct page *page; loff_t pos; int err = 0, err2 = 0; list_for_each_entry_safe(rb, n, head, list) { |
f7545144c nilfs2: use sb in... |
507 |
inode = nilfs_iget(sb, root, rb->ino); |
0f3e1c7f2 nilfs2: recovery ... |
508 509 510 511 512 513 514 |
if (IS_ERR(inode)) { err = PTR_ERR(inode); inode = NULL; goto failed_inode; } pos = rb->blkoff << inode->i_blkbits; |
155130a4f get rid of block_... |
515 516 517 518 519 520 |
err = block_write_begin(inode->i_mapping, pos, blocksize, 0, &page, nilfs_get_block); if (unlikely(err)) { loff_t isize = inode->i_size; if (pos + blocksize > isize) vmtruncate(inode, isize); |
0f3e1c7f2 nilfs2: recovery ... |
521 |
goto failed_inode; |
155130a4f get rid of block_... |
522 |
} |
0f3e1c7f2 nilfs2: recovery ... |
523 |
|
8b94025c0 nilfs2: refactor ... |
524 |
err = nilfs_recovery_copy_block(nilfs, rb, page); |
0f3e1c7f2 nilfs2: recovery ... |
525 526 |
if (unlikely(err)) goto failed_page; |
bcbc8c648 nilfs2: do not pa... |
527 |
err = nilfs_set_file_dirty(inode, 1); |
0f3e1c7f2 nilfs2: recovery ... |
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 |
if (unlikely(err)) goto failed_page; block_write_end(NULL, inode->i_mapping, pos, blocksize, blocksize, page, NULL); unlock_page(page); page_cache_release(page); (*nr_salvaged_blocks)++; goto next; failed_page: unlock_page(page); page_cache_release(page); failed_inode: printk(KERN_WARNING "NILFS warning: error recovering data block " "(err=%d, ino=%lu, block-offset=%llu) ", |
b5696e5e0 nilfs2: fix forma... |
549 550 |
err, (unsigned long)rb->ino, (unsigned long long)rb->blkoff); |
0f3e1c7f2 nilfs2: recovery ... |
551 552 553 554 555 556 557 558 559 560 561 562 563 |
if (!err2) err2 = err; next: iput(inode); /* iput(NULL) is just ignored */ list_del_init(&rb->list); kfree(rb); } return err2; } /** * nilfs_do_roll_forward - salvage logical segments newer than the latest * checkpoint |
8b94025c0 nilfs2: refactor ... |
564 |
* @nilfs: nilfs object |
f7545144c nilfs2: use sb in... |
565 |
* @sb: super block instance |
0f3e1c7f2 nilfs2: recovery ... |
566 567 568 |
* @ri: pointer to a nilfs_recovery_info */ static int nilfs_do_roll_forward(struct the_nilfs *nilfs, |
f7545144c nilfs2: use sb in... |
569 |
struct super_block *sb, |
4d8d9293d nilfs2: set point... |
570 |
struct nilfs_root *root, |
0f3e1c7f2 nilfs2: recovery ... |
571 572 |
struct nilfs_recovery_info *ri) { |
354fa8be2 nilfs2: divide lo... |
573 574 |
struct buffer_head *bh_sum = NULL; struct nilfs_segment_summary *sum; |
0f3e1c7f2 nilfs2: recovery ... |
575 576 577 |
sector_t pseg_start; sector_t seg_start, seg_end; /* Starting/ending DBN of full segment */ unsigned long nsalvaged_blocks = 0; |
85655484f nilfs2: do not us... |
578 |
unsigned int flags; |
0f3e1c7f2 nilfs2: recovery ... |
579 580 581 582 583 584 585 586 587 588 |
u64 seg_seq; __u64 segnum, nextnum = 0; int empty_seg = 0; int err = 0, ret; LIST_HEAD(dsync_blocks); /* list of data blocks to be recovered */ enum { RF_INIT_ST, RF_DSYNC_ST, /* scanning data-sync segments */ }; int state = RF_INIT_ST; |
0f3e1c7f2 nilfs2: recovery ... |
589 590 591 592 593 594 |
pseg_start = ri->ri_lsegs_start; seg_seq = ri->ri_lsegs_start_seq; segnum = nilfs_get_segnum_of_block(nilfs, pseg_start); nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); while (segnum != ri->ri_segnum || pseg_start <= ri->ri_pseg_start) { |
354fa8be2 nilfs2: divide lo... |
595 596 597 598 599 600 |
brelse(bh_sum); bh_sum = nilfs_read_log_header(nilfs, pseg_start, &sum); if (!bh_sum) { err = -EIO; goto failed; } |
0f3e1c7f2 nilfs2: recovery ... |
601 |
|
354fa8be2 nilfs2: divide lo... |
602 |
ret = nilfs_validate_log(nilfs, seg_seq, bh_sum, sum); |
0f3e1c7f2 nilfs2: recovery ... |
603 604 605 606 607 608 609 |
if (ret) { if (ret == NILFS_SEG_FAIL_IO) { err = -EIO; goto failed; } goto strayed; } |
354fa8be2 nilfs2: divide lo... |
610 |
|
85655484f nilfs2: do not us... |
611 612 |
flags = le16_to_cpu(sum->ss_flags); if (flags & NILFS_SS_SR) |
0f3e1c7f2 nilfs2: recovery ... |
613 614 615 |
goto confused; /* Found a valid partial segment; do recovery actions */ |
85655484f nilfs2: do not us... |
616 617 |
nextnum = nilfs_get_segnum_of_block(nilfs, le64_to_cpu(sum->ss_next)); |
0f3e1c7f2 nilfs2: recovery ... |
618 |
empty_seg = 0; |
85655484f nilfs2: do not us... |
619 620 621 |
nilfs->ns_ctime = le64_to_cpu(sum->ss_create); if (!(flags & NILFS_SS_GC)) nilfs->ns_nongc_ctime = nilfs->ns_ctime; |
0f3e1c7f2 nilfs2: recovery ... |
622 623 624 |
switch (state) { case RF_INIT_ST: |
85655484f nilfs2: do not us... |
625 626 |
if (!(flags & NILFS_SS_LOGBGN) || !(flags & NILFS_SS_SYNDT)) |
0f3e1c7f2 nilfs2: recovery ... |
627 628 629 630 |
goto try_next_pseg; state = RF_DSYNC_ST; /* Fall through */ case RF_DSYNC_ST: |
85655484f nilfs2: do not us... |
631 |
if (!(flags & NILFS_SS_SYNDT)) |
0f3e1c7f2 nilfs2: recovery ... |
632 |
goto confused; |
85655484f nilfs2: do not us... |
633 |
err = nilfs_scan_dsync_log(nilfs, pseg_start, sum, |
8b94025c0 nilfs2: refactor ... |
634 |
&dsync_blocks); |
0f3e1c7f2 nilfs2: recovery ... |
635 636 |
if (unlikely(err)) goto failed; |
85655484f nilfs2: do not us... |
637 |
if (flags & NILFS_SS_LOGEND) { |
8b94025c0 nilfs2: refactor ... |
638 |
err = nilfs_recover_dsync_blocks( |
f7545144c nilfs2: use sb in... |
639 |
nilfs, sb, root, &dsync_blocks, |
8b94025c0 nilfs2: refactor ... |
640 |
&nsalvaged_blocks); |
0f3e1c7f2 nilfs2: recovery ... |
641 642 643 644 645 646 647 648 649 650 |
if (unlikely(err)) goto failed; state = RF_INIT_ST; } break; /* Fall through to try_next_pseg */ } try_next_pseg: if (pseg_start == ri->ri_lsegs_end) break; |
85655484f nilfs2: do not us... |
651 |
pseg_start += le32_to_cpu(sum->ss_nblocks); |
0f3e1c7f2 nilfs2: recovery ... |
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 |
if (pseg_start < seg_end) continue; goto feed_segment; strayed: if (pseg_start == ri->ri_lsegs_end) break; feed_segment: /* Looking to the next full segment */ if (empty_seg++) break; seg_seq++; segnum = nextnum; nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); pseg_start = seg_start; } if (nsalvaged_blocks) { printk(KERN_INFO "NILFS (device %s): salvaged %lu blocks ", |
f7545144c nilfs2: use sb in... |
673 |
sb->s_id, nsalvaged_blocks); |
0f3e1c7f2 nilfs2: recovery ... |
674 675 676 |
ri->ri_need_recovery = NILFS_RECOVERY_ROLLFORWARD_DONE; } out: |
354fa8be2 nilfs2: divide lo... |
677 |
brelse(bh_sum); |
0f3e1c7f2 nilfs2: recovery ... |
678 |
dispose_recovery_list(&dsync_blocks); |
0f3e1c7f2 nilfs2: recovery ... |
679 680 681 682 683 684 685 686 |
return err; confused: err = -EINVAL; failed: printk(KERN_ERR "NILFS (device %s): Error roll-forwarding " "(err=%d, pseg block=%llu). ", |
f7545144c nilfs2: use sb in... |
687 |
sb->s_id, err, (unsigned long long)pseg_start); |
0f3e1c7f2 nilfs2: recovery ... |
688 689 690 691 |
goto out; } static void nilfs_finish_roll_forward(struct the_nilfs *nilfs, |
0f3e1c7f2 nilfs2: recovery ... |
692 693 694 695 696 697 698 699 |
struct nilfs_recovery_info *ri) { struct buffer_head *bh; int err; if (nilfs_get_segnum_of_block(nilfs, ri->ri_lsegs_start) != nilfs_get_segnum_of_block(nilfs, ri->ri_super_root)) return; |
8b94025c0 nilfs2: refactor ... |
700 |
bh = __getblk(nilfs->ns_bdev, ri->ri_lsegs_start, nilfs->ns_blocksize); |
0f3e1c7f2 nilfs2: recovery ... |
701 702 703 704 705 706 707 708 709 710 711 712 713 |
BUG_ON(!bh); memset(bh->b_data, 0, bh->b_size); set_buffer_dirty(bh); err = sync_dirty_buffer(bh); if (unlikely(err)) printk(KERN_WARNING "NILFS warning: buffer sync write failed during " "post-cleaning of recovery. "); brelse(bh); } /** |
aee5ce2f5 nilfs2: rename ni... |
714 715 |
* nilfs_salvage_orphan_logs - salvage logs written after the latest checkpoint * @nilfs: nilfs object |
f7545144c nilfs2: use sb in... |
716 |
* @sb: super block instance |
0f3e1c7f2 nilfs2: recovery ... |
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 |
* @ri: pointer to a nilfs_recovery_info struct to store search results. * * Return Value: On success, 0 is returned. On error, one of the following * negative error code is returned. * * %-EINVAL - Inconsistent filesystem state. * * %-EIO - I/O error * * %-ENOSPC - No space left on device (only in a panic state). * * %-ERESTARTSYS - Interrupted. * * %-ENOMEM - Insufficient memory available. */ |
aee5ce2f5 nilfs2: rename ni... |
732 |
int nilfs_salvage_orphan_logs(struct the_nilfs *nilfs, |
f7545144c nilfs2: use sb in... |
733 |
struct super_block *sb, |
aee5ce2f5 nilfs2: rename ni... |
734 |
struct nilfs_recovery_info *ri) |
0f3e1c7f2 nilfs2: recovery ... |
735 |
{ |
4d8d9293d nilfs2: set point... |
736 |
struct nilfs_root *root; |
0f3e1c7f2 nilfs2: recovery ... |
737 738 739 740 |
int err; if (ri->ri_lsegs_start == 0 || ri->ri_lsegs_end == 0) return 0; |
f7545144c nilfs2: use sb in... |
741 |
err = nilfs_attach_checkpoint(sb, ri->ri_cno, true, &root); |
0f3e1c7f2 nilfs2: recovery ... |
742 743 744 745 746 747 |
if (unlikely(err)) { printk(KERN_ERR "NILFS: error loading the latest checkpoint. "); return err; } |
f7545144c nilfs2: use sb in... |
748 |
err = nilfs_do_roll_forward(nilfs, sb, root, ri); |
0f3e1c7f2 nilfs2: recovery ... |
749 750 751 752 |
if (unlikely(err)) goto failed; if (ri->ri_need_recovery == NILFS_RECOVERY_ROLLFORWARD_DONE) { |
f7545144c nilfs2: use sb in... |
753 |
err = nilfs_prepare_segment_for_recovery(nilfs, sb, ri); |
0f3e1c7f2 nilfs2: recovery ... |
754 755 756 757 758 759 |
if (unlikely(err)) { printk(KERN_ERR "NILFS: Error preparing segments for " "recovery. "); goto failed; } |
f7545144c nilfs2: use sb in... |
760 |
err = nilfs_attach_log_writer(sb, root); |
0f3e1c7f2 nilfs2: recovery ... |
761 762 763 764 |
if (unlikely(err)) goto failed; set_nilfs_discontinued(nilfs); |
f7545144c nilfs2: use sb in... |
765 766 |
err = nilfs_construct_segment(sb); nilfs_detach_log_writer(sb); |
0f3e1c7f2 nilfs2: recovery ... |
767 768 769 770 771 772 773 |
if (unlikely(err)) { printk(KERN_ERR "NILFS: Oops! recovery failed. " "(err=%d) ", err); goto failed; } |
8b94025c0 nilfs2: refactor ... |
774 |
nilfs_finish_roll_forward(nilfs, ri); |
0f3e1c7f2 nilfs2: recovery ... |
775 |
} |
0f3e1c7f2 nilfs2: recovery ... |
776 |
failed: |
4d8d9293d nilfs2: set point... |
777 |
nilfs_put_root(root); |
0f3e1c7f2 nilfs2: recovery ... |
778 779 780 781 782 783 |
return err; } /** * nilfs_search_super_root - search the latest valid super root * @nilfs: the_nilfs |
0f3e1c7f2 nilfs2: recovery ... |
784 785 786 787 788 789 790 791 792 793 794 795 |
* @ri: pointer to a nilfs_recovery_info struct to store search results. * * nilfs_search_super_root() looks for the latest super-root from a partial * segment pointed by the superblock. It sets up struct the_nilfs through * this search. It fills nilfs_recovery_info (ri) required for recovery. * * Return Value: On success, 0 is returned. On error, one of the following * negative error code is returned. * * %-EINVAL - No valid segment found * * %-EIO - I/O error |
2d72b99ec nilfs2: add missi... |
796 797 |
* * %-ENOMEM - Insufficient memory available. |
0f3e1c7f2 nilfs2: recovery ... |
798 |
*/ |
8b94025c0 nilfs2: refactor ... |
799 |
int nilfs_search_super_root(struct the_nilfs *nilfs, |
0f3e1c7f2 nilfs2: recovery ... |
800 801 |
struct nilfs_recovery_info *ri) { |
354fa8be2 nilfs2: divide lo... |
802 803 |
struct buffer_head *bh_sum = NULL; struct nilfs_segment_summary *sum; |
0f3e1c7f2 nilfs2: recovery ... |
804 805 |
sector_t pseg_start, pseg_end, sr_pseg_start = 0; sector_t seg_start, seg_end; /* range of full segment (block number) */ |
050b4142c nilfs2: apply rea... |
806 |
sector_t b, end; |
85655484f nilfs2: do not us... |
807 808 |
unsigned long nblocks; unsigned int flags; |
0f3e1c7f2 nilfs2: recovery ... |
809 810 811 |
u64 seg_seq; __u64 segnum, nextnum = 0; __u64 cno; |
0f3e1c7f2 nilfs2: recovery ... |
812 813 814 815 816 817 818 819 820 821 822 |
LIST_HEAD(segments); int empty_seg = 0, scan_newer = 0; int ret; pseg_start = nilfs->ns_last_pseg; seg_seq = nilfs->ns_last_seq; cno = nilfs->ns_last_cno; segnum = nilfs_get_segnum_of_block(nilfs, pseg_start); /* Calculate range of segment */ nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); |
050b4142c nilfs2: apply rea... |
823 824 825 |
/* Read ahead segment */ b = seg_start; while (b <= seg_end) |
8b94025c0 nilfs2: refactor ... |
826 |
__breadahead(nilfs->ns_bdev, b++, nilfs->ns_blocksize); |
050b4142c nilfs2: apply rea... |
827 |
|
0f3e1c7f2 nilfs2: recovery ... |
828 |
for (;;) { |
354fa8be2 nilfs2: divide lo... |
829 830 831 832 833 834 835 |
brelse(bh_sum); ret = NILFS_SEG_FAIL_IO; bh_sum = nilfs_read_log_header(nilfs, pseg_start, &sum); if (!bh_sum) goto failed; ret = nilfs_validate_log(nilfs, seg_seq, bh_sum, sum); |
0f3e1c7f2 nilfs2: recovery ... |
836 837 838 839 840 |
if (ret) { if (ret == NILFS_SEG_FAIL_IO) goto failed; goto strayed; } |
354fa8be2 nilfs2: divide lo... |
841 |
|
85655484f nilfs2: do not us... |
842 843 |
nblocks = le32_to_cpu(sum->ss_nblocks); pseg_end = pseg_start + nblocks - 1; |
0f3e1c7f2 nilfs2: recovery ... |
844 845 846 847 848 849 850 851 852 |
if (unlikely(pseg_end > seg_end)) { ret = NILFS_SEG_FAIL_CONSISTENCY; goto strayed; } /* A valid partial segment */ ri->ri_pseg_start = pseg_start; ri->ri_seq = seg_seq; ri->ri_segnum = segnum; |
85655484f nilfs2: do not us... |
853 854 |
nextnum = nilfs_get_segnum_of_block(nilfs, le64_to_cpu(sum->ss_next)); |
0f3e1c7f2 nilfs2: recovery ... |
855 856 |
ri->ri_nextnum = nextnum; empty_seg = 0; |
85655484f nilfs2: do not us... |
857 858 |
flags = le16_to_cpu(sum->ss_flags); if (!(flags & NILFS_SS_SR) && !scan_newer) { |
050b4142c nilfs2: apply rea... |
859 860 861 862 863 864 865 866 867 868 |
/* This will never happen because a superblock (last_segment) always points to a pseg having a super root. */ ret = NILFS_SEG_FAIL_CONSISTENCY; goto failed; } if (pseg_start == seg_start) { nilfs_get_segment_range(nilfs, nextnum, &b, &end); while (b <= end) |
8b94025c0 nilfs2: refactor ... |
869 870 |
__breadahead(nilfs->ns_bdev, b++, nilfs->ns_blocksize); |
050b4142c nilfs2: apply rea... |
871 |
} |
85655484f nilfs2: do not us... |
872 873 |
if (!(flags & NILFS_SS_SR)) { if (!ri->ri_lsegs_start && (flags & NILFS_SS_LOGBGN)) { |
0f3e1c7f2 nilfs2: recovery ... |
874 875 876 |
ri->ri_lsegs_start = pseg_start; ri->ri_lsegs_start_seq = seg_seq; } |
85655484f nilfs2: do not us... |
877 |
if (flags & NILFS_SS_LOGEND) |
0f3e1c7f2 nilfs2: recovery ... |
878 879 880 881 882 883 884 885 886 887 |
ri->ri_lsegs_end = pseg_start; goto try_next_pseg; } /* A valid super root was found. */ ri->ri_cno = cno++; ri->ri_super_root = pseg_end; ri->ri_lsegs_start = ri->ri_lsegs_end = 0; nilfs_dispose_segment_list(&segments); |
85655484f nilfs2: do not us... |
888 889 |
sr_pseg_start = pseg_start; nilfs->ns_pseg_offset = pseg_start + nblocks - seg_start; |
0f3e1c7f2 nilfs2: recovery ... |
890 891 892 |
nilfs->ns_seg_seq = seg_seq; nilfs->ns_segnum = segnum; nilfs->ns_cno = cno; /* nilfs->ns_cno = ri->ri_cno + 1 */ |
85655484f nilfs2: do not us... |
893 |
nilfs->ns_ctime = le64_to_cpu(sum->ss_create); |
0f3e1c7f2 nilfs2: recovery ... |
894 895 896 897 |
nilfs->ns_nextnum = nextnum; if (scan_newer) ri->ri_need_recovery = NILFS_RECOVERY_SR_UPDATED; |
2c2e52fc4 nilfs2: extend ni... |
898 |
else { |
2c2e52fc4 nilfs2: extend ni... |
899 900 901 902 |
if (nilfs->ns_mount_state & NILFS_VALID_FS) goto super_root_found; scan_newer = 1; } |
0f3e1c7f2 nilfs2: recovery ... |
903 |
|
0f3e1c7f2 nilfs2: recovery ... |
904 905 |
try_next_pseg: /* Standing on a course, or met an inconsistent state */ |
85655484f nilfs2: do not us... |
906 |
pseg_start += nblocks; |
0f3e1c7f2 nilfs2: recovery ... |
907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 |
if (pseg_start < seg_end) continue; goto feed_segment; strayed: /* Off the trail */ if (!scan_newer) /* * This can happen if a checkpoint was written without * barriers, or as a result of an I/O failure. */ goto failed; feed_segment: /* Looking to the next full segment */ if (empty_seg++) goto super_root_found; /* found a valid super root */ |
654137dd4 nilfs2: remove he... |
924 925 |
ret = nilfs_segment_list_add(&segments, segnum); if (unlikely(ret)) |
0f3e1c7f2 nilfs2: recovery ... |
926 |
goto failed; |
0f3e1c7f2 nilfs2: recovery ... |
927 928 929 930 931 932 933 934 935 |
seg_seq++; segnum = nextnum; nilfs_get_segment_range(nilfs, segnum, &seg_start, &seg_end); pseg_start = seg_start; } super_root_found: /* Updating pointers relating to the latest checkpoint */ |
354fa8be2 nilfs2: divide lo... |
936 |
brelse(bh_sum); |
0935db747 nilfs2: use list_... |
937 |
list_splice_tail(&segments, &ri->ri_used_segments); |
0f3e1c7f2 nilfs2: recovery ... |
938 939 940 941 942 943 |
nilfs->ns_last_pseg = sr_pseg_start; nilfs->ns_last_seq = nilfs->ns_seg_seq; nilfs->ns_last_cno = ri->ri_cno; return 0; failed: |
354fa8be2 nilfs2: divide lo... |
944 |
brelse(bh_sum); |
0f3e1c7f2 nilfs2: recovery ... |
945 946 947 |
nilfs_dispose_segment_list(&segments); return (ret < 0) ? ret : nilfs_warn_segment_error(ret); } |