~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/fs/nilfs2/recovery.c

Version: ~ [ linux-5.4-rc3 ] ~ [ linux-5.3.6 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.79 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.149 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.196 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.196 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.75 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp