Merge branch 'bkl/ioctl' of git://git.kernel.org/pub/scm/linux/kernel/git/frederic...
[safe/jmp/linux-2.6] / fs / nilfs2 / the_nilfs.c
index aea2b58..8c10973 100644 (file)
@@ -261,15 +261,17 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
        struct nilfs_recovery_info ri;
        unsigned int s_flags = sbi->s_super->s_flags;
        int really_read_only = bdev_read_only(nilfs->ns_bdev);
-       unsigned valid_fs;
+       int valid_fs = nilfs_valid_fs(nilfs);
        int err;
 
-       if (nilfs_loaded(nilfs))
-               return 0;
-
-       down_write(&nilfs->ns_sem);
-       valid_fs = (nilfs->ns_mount_state & NILFS_VALID_FS);
-       up_write(&nilfs->ns_sem);
+       if (nilfs_loaded(nilfs)) {
+               if (valid_fs ||
+                   ((s_flags & MS_RDONLY) && nilfs_test_opt(sbi, NORECOVERY)))
+                       return 0;
+               printk(KERN_ERR "NILFS: the filesystem is in an incomplete "
+                      "recovery state.\n");
+               return -EINVAL;
+       }
 
        if (!valid_fs) {
                printk(KERN_WARNING "NILFS warning: mounting unchecked fs\n");
@@ -299,6 +301,11 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
                goto skip_recovery;
 
        if (s_flags & MS_RDONLY) {
+               if (nilfs_test_opt(sbi, NORECOVERY)) {
+                       printk(KERN_INFO "NILFS: norecovery option specified. "
+                              "skipping roll-forward recovery\n");
+                       goto skip_recovery;
+               }
                if (really_read_only) {
                        printk(KERN_ERR "NILFS: write access "
                               "unavailable, cannot proceed.\n");
@@ -306,6 +313,11 @@ int load_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi)
                        goto failed_unload;
                }
                sbi->s_super->s_flags &= ~MS_RDONLY;
+       } else if (nilfs_test_opt(sbi, NORECOVERY)) {
+               printk(KERN_ERR "NILFS: recovery cancelled because norecovery "
+                      "option was specified for a read/write mount\n");
+               err = -EINVAL;
+               goto failed_unload;
        }
 
        err = nilfs_recover_logical_segments(nilfs, sbi, &ri);
@@ -374,7 +386,7 @@ static int nilfs_store_disk_layout(struct the_nilfs *nilfs,
 
        nilfs->ns_blocks_per_segment = le32_to_cpu(sbp->s_blocks_per_segment);
        if (nilfs->ns_blocks_per_segment < NILFS_SEG_MIN_BLOCKS) {
-               printk(KERN_ERR "NILFS: too short segment. \n");
+               printk(KERN_ERR "NILFS: too short segment.\n");
                return -EINVAL;
        }
 
@@ -474,11 +486,15 @@ static int nilfs_load_super_block(struct the_nilfs *nilfs,
                printk(KERN_WARNING
                       "NILFS warning: unable to read secondary superblock\n");
 
+       /*
+        * Compare two super blocks and set 1 in swp if the secondary
+        * super block is valid and newer.  Otherwise, set 0 in swp.
+        */
        valid[0] = nilfs_valid_sb(sbp[0]);
        valid[1] = nilfs_valid_sb(sbp[1]);
-       swp = valid[1] &&
-               (!valid[0] ||
-                le64_to_cpu(sbp[1]->s_wtime) > le64_to_cpu(sbp[0]->s_wtime));
+       swp = valid[1] && (!valid[0] ||
+                          le64_to_cpu(sbp[1]->s_last_cno) >
+                          le64_to_cpu(sbp[0]->s_last_cno));
 
        if (valid[swp] && nilfs_sb2_bad_offset(sbp[swp], sb2off)) {
                brelse(sbh[1]);
@@ -634,6 +650,44 @@ int init_nilfs(struct the_nilfs *nilfs, struct nilfs_sb_info *sbi, char *data)
        goto out;
 }
 
+int nilfs_discard_segments(struct the_nilfs *nilfs, __u64 *segnump,
+                           size_t nsegs)
+{
+       sector_t seg_start, seg_end;
+       sector_t start = 0, nblocks = 0;
+       unsigned int sects_per_block;
+       __u64 *sn;
+       int ret = 0;
+
+       sects_per_block = (1 << nilfs->ns_blocksize_bits) /
+               bdev_logical_block_size(nilfs->ns_bdev);
+       for (sn = segnump; sn < segnump + nsegs; sn++) {
+               nilfs_get_segment_range(nilfs, *sn, &seg_start, &seg_end);
+
+               if (!nblocks) {
+                       start = seg_start;
+                       nblocks = seg_end - seg_start + 1;
+               } else if (start + nblocks == seg_start) {
+                       nblocks += seg_end - seg_start + 1;
+               } else {
+                       ret = blkdev_issue_discard(nilfs->ns_bdev,
+                                                  start * sects_per_block,
+                                                  nblocks * sects_per_block,
+                                                  GFP_NOFS,
+                                                  BLKDEV_IFL_BARRIER);
+                       if (ret < 0)
+                               return ret;
+                       nblocks = 0;
+               }
+       }
+       if (nblocks)
+               ret = blkdev_issue_discard(nilfs->ns_bdev,
+                                          start * sects_per_block,
+                                          nblocks * sects_per_block,
+                                          GFP_NOFS, BLKDEV_IFL_BARRIER);
+       return ret;
+}
+
 int nilfs_count_free_blocks(struct the_nilfs *nilfs, sector_t *nblocks)
 {
        struct inode *dat = nilfs_dat_inode(nilfs);