mmc: enable runtime PM by default
[pandora-kernel.git] / fs / nilfs2 / super.c
index 062cca0..8351c44 100644 (file)
@@ -56,6 +56,7 @@
 #include "btnode.h"
 #include "page.h"
 #include "cpfile.h"
+#include "sufile.h" /* nilfs_sufile_resize(), nilfs_sufile_set_alloc_range() */
 #include "ifile.h"
 #include "dat.h"
 #include "segment.h"
@@ -165,7 +166,7 @@ struct inode *nilfs_alloc_inode(struct super_block *sb)
        ii->i_state = 0;
        ii->i_cno = 0;
        ii->vfs_inode.i_version = 1;
-       nilfs_btnode_cache_init(&ii->i_btnode_cache, sb->s_bdi);
+       nilfs_mapping_init(&ii->i_btnode_cache, &ii->vfs_inode, sb->s_bdi);
        return &ii->vfs_inode;
 }
 
@@ -347,6 +348,134 @@ int nilfs_cleanup_super(struct super_block *sb)
        return ret;
 }
 
+/**
+ * nilfs_move_2nd_super - relocate secondary super block
+ * @sb: super block instance
+ * @sb2off: new offset of the secondary super block (in bytes)
+ */
+static int nilfs_move_2nd_super(struct super_block *sb, loff_t sb2off)
+{
+       struct the_nilfs *nilfs = sb->s_fs_info;
+       struct buffer_head *nsbh;
+       struct nilfs_super_block *nsbp;
+       sector_t blocknr, newblocknr;
+       unsigned long offset;
+       int sb2i = -1;  /* array index of the secondary superblock */
+       int ret = 0;
+
+       /* nilfs->ns_sem must be locked by the caller. */
+       if (nilfs->ns_sbh[1] &&
+           nilfs->ns_sbh[1]->b_blocknr > nilfs->ns_first_data_block) {
+               sb2i = 1;
+               blocknr = nilfs->ns_sbh[1]->b_blocknr;
+       } else if (nilfs->ns_sbh[0]->b_blocknr > nilfs->ns_first_data_block) {
+               sb2i = 0;
+               blocknr = nilfs->ns_sbh[0]->b_blocknr;
+       }
+       if (sb2i >= 0 && (u64)blocknr << nilfs->ns_blocksize_bits == sb2off)
+               goto out;  /* super block location is unchanged */
+
+       /* Get new super block buffer */
+       newblocknr = sb2off >> nilfs->ns_blocksize_bits;
+       offset = sb2off & (nilfs->ns_blocksize - 1);
+       nsbh = sb_getblk(sb, newblocknr);
+       if (!nsbh) {
+               printk(KERN_WARNING
+                      "NILFS warning: unable to move secondary superblock "
+                      "to block %llu\n", (unsigned long long)newblocknr);
+               ret = -EIO;
+               goto out;
+       }
+       nsbp = (void *)nsbh->b_data + offset;
+       memset(nsbp, 0, nilfs->ns_blocksize);
+
+       if (sb2i >= 0) {
+               memcpy(nsbp, nilfs->ns_sbp[sb2i], nilfs->ns_sbsize);
+               brelse(nilfs->ns_sbh[sb2i]);
+               nilfs->ns_sbh[sb2i] = nsbh;
+               nilfs->ns_sbp[sb2i] = nsbp;
+       } else if (nilfs->ns_sbh[0]->b_blocknr < nilfs->ns_first_data_block) {
+               /* secondary super block will be restored to index 1 */
+               nilfs->ns_sbh[1] = nsbh;
+               nilfs->ns_sbp[1] = nsbp;
+       } else {
+               brelse(nsbh);
+       }
+out:
+       return ret;
+}
+
+/**
+ * nilfs_resize_fs - resize the filesystem
+ * @sb: super block instance
+ * @newsize: new size of the filesystem (in bytes)
+ */
+int nilfs_resize_fs(struct super_block *sb, __u64 newsize)
+{
+       struct the_nilfs *nilfs = sb->s_fs_info;
+       struct nilfs_super_block **sbp;
+       __u64 devsize, newnsegs;
+       loff_t sb2off;
+       int ret;
+
+       ret = -ERANGE;
+       devsize = i_size_read(sb->s_bdev->bd_inode);
+       if (newsize > devsize)
+               goto out;
+
+       /*
+        * Write lock is required to protect some functions depending
+        * on the number of segments, the number of reserved segments,
+        * and so forth.
+        */
+       down_write(&nilfs->ns_segctor_sem);
+
+       sb2off = NILFS_SB2_OFFSET_BYTES(newsize);
+       newnsegs = sb2off >> nilfs->ns_blocksize_bits;
+       do_div(newnsegs, nilfs->ns_blocks_per_segment);
+
+       ret = nilfs_sufile_resize(nilfs->ns_sufile, newnsegs);
+       up_write(&nilfs->ns_segctor_sem);
+       if (ret < 0)
+               goto out;
+
+       ret = nilfs_construct_segment(sb);
+       if (ret < 0)
+               goto out;
+
+       down_write(&nilfs->ns_sem);
+       nilfs_move_2nd_super(sb, sb2off);
+       ret = -EIO;
+       sbp = nilfs_prepare_super(sb, 0);
+       if (likely(sbp)) {
+               nilfs_set_log_cursor(sbp[0], nilfs);
+               /*
+                * Drop NILFS_RESIZE_FS flag for compatibility with
+                * mount-time resize which may be implemented in a
+                * future release.
+                */
+               sbp[0]->s_state = cpu_to_le16(le16_to_cpu(sbp[0]->s_state) &
+                                             ~NILFS_RESIZE_FS);
+               sbp[0]->s_dev_size = cpu_to_le64(newsize);
+               sbp[0]->s_nsegments = cpu_to_le64(nilfs->ns_nsegments);
+               if (sbp[1])
+                       memcpy(sbp[1], sbp[0], nilfs->ns_sbsize);
+               ret = nilfs_commit_super(sb, NILFS_SB_COMMIT_ALL);
+       }
+       up_write(&nilfs->ns_sem);
+
+       /*
+        * Reset the range of allocatable segments last.  This order
+        * is important in the case of expansion because the secondary
+        * superblock must be protected from log write until migration
+        * completes.
+        */
+       if (!ret)
+               nilfs_sufile_set_alloc_range(nilfs->ns_sufile, 0, newnsegs - 1);
+out:
+       return ret;
+}
+
 static void nilfs_put_super(struct super_block *sb)
 {
        struct the_nilfs *nilfs = sb->s_fs_info;