]> err.no Git - linux-2.6/blobdiff - fs/ext3/resize.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/sparc-2.6
[linux-2.6] / fs / ext3 / resize.c
index 82c678e926824ce0a43421b3e8677e11bfb7bebf..0e97b6e07cb03556437c55924003d103efd44f17 100644 (file)
@@ -8,12 +8,9 @@
  * This could probably be made into a module, because it is not often in use.
  */
 
-#include <linux/config.h>
 
 #define EXT3FS_DEBUG
 
-#include <linux/sched.h>
-#include <linux/smp_lock.h>
 #include <linux/ext3_jbd.h>
 
 #include <linux/errno.h>
@@ -116,7 +113,7 @@ static int verify_group_input(struct super_block *sb,
 }
 
 static struct buffer_head *bclean(handle_t *handle, struct super_block *sb,
-                                 unsigned long blk)
+                                 ext3_fsblk_t blk)
 {
        struct buffer_head *bh;
        int err;
@@ -156,6 +153,34 @@ static void mark_bitmap_end(int start_bit, int end_bit, char *bitmap)
                memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3);
 }
 
+/*
+ * If we have fewer than thresh credits, extend by EXT3_MAX_TRANS_DATA.
+ * If that fails, restart the transaction & regain write access for the
+ * buffer head which is used for block_bitmap modifications.
+ */
+static int extend_or_restart_transaction(handle_t *handle, int thresh,
+                                        struct buffer_head *bh)
+{
+       int err;
+
+       if (handle->h_buffer_credits >= thresh)
+               return 0;
+
+       err = ext3_journal_extend(handle, EXT3_MAX_TRANS_DATA);
+       if (err < 0)
+               return err;
+       if (err) {
+               err = ext3_journal_restart(handle, EXT3_MAX_TRANS_DATA);
+               if (err)
+                       return err;
+               err = ext3_journal_get_write_access(handle, bh);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
 /*
  * Set up the block and inode bitmaps, and the inode table for the new group.
  * This doesn't need to be part of the main transaction, since we are only
@@ -167,20 +192,20 @@ static int setup_new_group_blocks(struct super_block *sb,
                                  struct ext3_new_group_data *input)
 {
        struct ext3_sb_info *sbi = EXT3_SB(sb);
-       unsigned long start = input->group * sbi->s_blocks_per_group +
-               le32_to_cpu(sbi->s_es->s_first_data_block);
+       ext3_fsblk_t start = ext3_group_first_block_no(sb, input->group);
        int reserved_gdb = ext3_bg_has_super(sb, input->group) ?
                le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks) : 0;
        unsigned long gdblocks = ext3_bg_num_gdb(sb, input->group);
        struct buffer_head *bh;
        handle_t *handle;
-       unsigned long block;
+       ext3_fsblk_t block;
        ext3_grpblk_t bit;
        int i;
        int err = 0, err2;
 
-       handle = ext3_journal_start_sb(sb, reserved_gdb + gdblocks +
-                                      2 + sbi->s_itb_per_group);
+       /* This transaction may be extended/restarted along the way */
+       handle = ext3_journal_start_sb(sb, EXT3_MAX_TRANS_DATA);
+
        if (IS_ERR(handle))
                return PTR_ERR(handle);
 
@@ -207,6 +232,10 @@ static int setup_new_group_blocks(struct super_block *sb,
 
                ext3_debug("update backup group %#04lx (+%d)\n", block, bit);
 
+               err = extend_or_restart_transaction(handle, 1, bh);
+               if (err)
+                       goto exit_bh;
+
                gdb = sb_getblk(sb, block);
                if (!gdb) {
                        err = -EIO;
@@ -216,10 +245,10 @@ static int setup_new_group_blocks(struct super_block *sb,
                        brelse(gdb);
                        goto exit_bh;
                }
-               lock_buffer(bh);
-               memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, bh->b_size);
+               lock_buffer(gdb);
+               memcpy(gdb->b_data, sbi->s_group_desc[i]->b_data, gdb->b_size);
                set_buffer_uptodate(gdb);
-               unlock_buffer(bh);
+               unlock_buffer(gdb);
                ext3_journal_dirty_metadata(handle, gdb);
                ext3_set_bit(bit, bh->b_data);
                brelse(gdb);
@@ -232,6 +261,10 @@ static int setup_new_group_blocks(struct super_block *sb,
 
                ext3_debug("clear reserved block %#04lx (+%d)\n", block, bit);
 
+               err = extend_or_restart_transaction(handle, 1, bh);
+               if (err)
+                       goto exit_bh;
+
                if (IS_ERR(gdb = bclean(handle, sb, block))) {
                        err = PTR_ERR(bh);
                        goto exit_bh;
@@ -253,6 +286,11 @@ static int setup_new_group_blocks(struct super_block *sb,
                struct buffer_head *it;
 
                ext3_debug("clear inode block %#04lx (+%d)\n", block, bit);
+
+               err = extend_or_restart_transaction(handle, 1, bh);
+               if (err)
+                       goto exit_bh;
+
                if (IS_ERR(it = bclean(handle, sb, block))) {
                        err = PTR_ERR(it);
                        goto exit_bh;
@@ -261,6 +299,11 @@ static int setup_new_group_blocks(struct super_block *sb,
                brelse(it);
                ext3_set_bit(bit, bh->b_data);
        }
+
+       err = extend_or_restart_transaction(handle, 2, bh);
+       if (err)
+               goto exit_bh;
+
        mark_bitmap_end(input->blocks_count, EXT3_BLOCKS_PER_GROUP(sb),
                        bh->b_data);
        ext3_journal_dirty_metadata(handle, bh);
@@ -332,19 +375,20 @@ static unsigned ext3_list_backups(struct super_block *sb, unsigned *three,
 static int verify_reserved_gdb(struct super_block *sb,
                               struct buffer_head *primary)
 {
-       const unsigned long blk = primary->b_blocknr;
+       const ext3_fsblk_t blk = primary->b_blocknr;
        const unsigned long end = EXT3_SB(sb)->s_groups_count;
        unsigned three = 1;
        unsigned five = 5;
        unsigned seven = 7;
        unsigned grp;
-       __u32 *p = (__u32 *)primary->b_data;
+       __le32 *p = (__le32 *)primary->b_data;
        int gdbackups = 0;
 
        while ((grp = ext3_list_backups(sb, &three, &five, &seven)) < end) {
                if (le32_to_cpu(*p++) != grp * EXT3_BLOCKS_PER_GROUP(sb) + blk){
                        ext3_warning(sb, __FUNCTION__,
-                                    "reserved GDT %lu missing grp %d (%lu)",
+                                    "reserved GDT "E3FSBLK
+                                    " missing grp %d ("E3FSBLK")",
                                     blk, grp,
                                     grp * EXT3_BLOCKS_PER_GROUP(sb) + blk);
                        return -EINVAL;
@@ -376,12 +420,12 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        struct super_block *sb = inode->i_sb;
        struct ext3_super_block *es = EXT3_SB(sb)->s_es;
        unsigned long gdb_num = input->group / EXT3_DESC_PER_BLOCK(sb);
-       unsigned long gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
+       ext3_fsblk_t gdblock = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + gdb_num;
        struct buffer_head **o_group_desc, **n_group_desc;
        struct buffer_head *dind;
        int gdbackups;
        struct ext3_iloc iloc;
-       __u32 *data;
+       __le32 *data;
        int err;
 
        if (test_opt(sb, DEBUG))
@@ -418,10 +462,10 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
                goto exit_bh;
        }
 
-       data = (__u32 *)dind->b_data;
+       data = (__le32 *)dind->b_data;
        if (le32_to_cpu(data[gdb_num % EXT3_ADDR_PER_BLOCK(sb)]) != gdblock) {
                ext3_warning(sb, __FUNCTION__,
-                            "new group %u GDT block %lu not reserved",
+                            "new group %u GDT block "E3FSBLK" not reserved",
                             input->group, gdblock);
                err = -EINVAL;
                goto exit_dind;
@@ -440,8 +484,8 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        if ((err = ext3_reserve_inode_write(handle, inode, &iloc)))
                goto exit_dindj;
 
-       n_group_desc = (struct buffer_head **)kmalloc((gdb_num + 1) *
-                               sizeof(struct buffer_head *), GFP_KERNEL);
+       n_group_desc = kmalloc((gdb_num + 1) * sizeof(struct buffer_head *),
+                       GFP_NOFS);
        if (!n_group_desc) {
                err = -ENOMEM;
                ext3_warning (sb, __FUNCTION__,
@@ -474,8 +518,7 @@ static int add_new_gdb(handle_t *handle, struct inode *inode,
        EXT3_SB(sb)->s_gdb_count++;
        kfree(o_group_desc);
 
-       es->s_reserved_gdt_blocks =
-               cpu_to_le16(le16_to_cpu(es->s_reserved_gdt_blocks) - 1);
+       le16_add_cpu(&es->s_reserved_gdt_blocks, -1);
        ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
 
        return 0;
@@ -519,13 +562,13 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
        struct buffer_head **primary;
        struct buffer_head *dind;
        struct ext3_iloc iloc;
-       unsigned long blk;
-       __u32 *data, *end;
+       ext3_fsblk_t blk;
+       __le32 *data, *end;
        int gdbackups = 0;
        int res, i;
        int err;
 
-       primary = kmalloc(reserved_gdb * sizeof(*primary), GFP_KERNEL);
+       primary = kmalloc(reserved_gdb * sizeof(*primary), GFP_NOFS);
        if (!primary)
                return -ENOMEM;
 
@@ -537,15 +580,17 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
        }
 
        blk = EXT3_SB(sb)->s_sbh->b_blocknr + 1 + EXT3_SB(sb)->s_gdb_count;
-       data = (__u32 *)dind->b_data + EXT3_SB(sb)->s_gdb_count;
-       end = (__u32 *)dind->b_data + EXT3_ADDR_PER_BLOCK(sb);
+       data = (__le32 *)dind->b_data + EXT3_SB(sb)->s_gdb_count;
+       end = (__le32 *)dind->b_data + EXT3_ADDR_PER_BLOCK(sb);
 
        /* Get each reserved primary GDT block and verify it holds backups */
        for (res = 0; res < reserved_gdb; res++, blk++) {
                if (le32_to_cpu(*data) != blk) {
                        ext3_warning(sb, __FUNCTION__,
-                                    "reserved block %lu not at offset %ld",
-                                    blk, (long)(data - (__u32 *)dind->b_data));
+                                    "reserved block "E3FSBLK
+                                    " not at offset %ld",
+                                    blk,
+                                    (long)(data - (__le32 *)dind->b_data));
                        err = -EINVAL;
                        goto exit_bh;
                }
@@ -560,7 +605,7 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
                        goto exit_bh;
                }
                if (++data >= end)
-                       data = (__u32 *)dind->b_data;
+                       data = (__le32 *)dind->b_data;
        }
 
        for (i = 0; i < reserved_gdb; i++) {
@@ -584,7 +629,7 @@ static int reserve_backup_gdb(handle_t *handle, struct inode *inode,
        blk = input->group * EXT3_BLOCKS_PER_GROUP(sb);
        for (i = 0; i < reserved_gdb; i++) {
                int err2;
-               data = (__u32 *)primary[i]->b_data;
+               data = (__le32 *)primary[i]->b_data;
                /* printk("reserving backup %lu[%u] = %lu\n",
                       primary[i]->b_blocknr, gdbackups,
                       blk + primary[i]->b_blocknr); */
@@ -689,7 +734,7 @@ exit_err:
                             "can't update backup for group %d (err %d), "
                             "forcing fsck on next reboot", group, err);
                sbi->s_mount_state &= ~EXT3_VALID_FS;
-               sbi->s_es->s_state &= ~cpu_to_le16(EXT3_VALID_FS);
+               sbi->s_es->s_state &= cpu_to_le16(~EXT3_VALID_FS);
                mark_buffer_dirty(sbi->s_sbh);
        }
 }
@@ -730,6 +775,18 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input)
                return -EPERM;
        }
 
+       if (le32_to_cpu(es->s_blocks_count) + input->blocks_count <
+           le32_to_cpu(es->s_blocks_count)) {
+               ext3_warning(sb, __FUNCTION__, "blocks_count overflow\n");
+               return -EINVAL;
+       }
+
+       if (le32_to_cpu(es->s_inodes_count) + EXT3_INODES_PER_GROUP(sb) <
+           le32_to_cpu(es->s_inodes_count)) {
+               ext3_warning(sb, __FUNCTION__, "inodes_count overflow\n");
+               return -EINVAL;
+       }
+
        if (reserved_gdb || gdb_off == 0) {
                if (!EXT3_HAS_COMPAT_FEATURE(sb,
                                             EXT3_FEATURE_COMPAT_RESIZE_INODE)){
@@ -737,12 +794,11 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input)
                                     "No reserved GDT blocks, can't resize");
                        return -EPERM;
                }
-               inode = iget(sb, EXT3_RESIZE_INO);
-               if (!inode || is_bad_inode(inode)) {
+               inode = ext3_iget(sb, EXT3_RESIZE_INO);
+               if (IS_ERR(inode)) {
                        ext3_warning(sb, __FUNCTION__,
                                     "Error opening resize inode");
-                       iput(inode);
-                       return -ENOENT;
+                       return PTR_ERR(inode);
                }
        }
 
@@ -833,10 +889,8 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input)
         * blocks/inodes before the group is live won't actually let us
         * allocate the new space yet.
         */
-       es->s_blocks_count = cpu_to_le32(le32_to_cpu(es->s_blocks_count) +
-               input->blocks_count);
-       es->s_inodes_count = cpu_to_le32(le32_to_cpu(es->s_inodes_count) +
-               EXT3_INODES_PER_GROUP(sb));
+       le32_add_cpu(&es->s_blocks_count, input->blocks_count);
+       le32_add_cpu(&es->s_inodes_count, EXT3_INODES_PER_GROUP(sb));
 
        /*
         * We need to protect s_groups_count against other CPUs seeing
@@ -869,13 +923,12 @@ int ext3_group_add(struct super_block *sb, struct ext3_new_group_data *input)
 
        /* Update the reserved block counts only once the new group is
         * active. */
-       es->s_r_blocks_count = cpu_to_le32(le32_to_cpu(es->s_r_blocks_count) +
-               input->reserved_blocks);
+       le32_add_cpu(&es->s_r_blocks_count, input->reserved_blocks);
 
        /* Update the free space counts */
-       percpu_counter_mod(&sbi->s_freeblocks_counter,
+       percpu_counter_add(&sbi->s_freeblocks_counter,
                           input->free_blocks_count);
-       percpu_counter_mod(&sbi->s_freeinodes_counter,
+       percpu_counter_add(&sbi->s_freeinodes_counter,
                           EXT3_INODES_PER_GROUP(sb));
 
        ext3_journal_dirty_metadata(handle, sbi->s_sbh);
@@ -906,9 +959,9 @@ exit_put:
  * GDT blocks are reserved to grow to the desired size.
  */
 int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es,
-                     unsigned long n_blocks_count)
+                     ext3_fsblk_t n_blocks_count)
 {
-       unsigned long o_blocks_count;
+       ext3_fsblk_t o_blocks_count;
        unsigned long o_groups_count;
        ext3_grpblk_t last;
        ext3_grpblk_t add;
@@ -924,7 +977,7 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es,
        o_groups_count = EXT3_SB(sb)->s_groups_count;
 
        if (test_opt(sb, DEBUG))
-               printk(KERN_DEBUG "EXT3-fs: extending last group from %lu to %lu blocks\n",
+               printk(KERN_DEBUG "EXT3-fs: extending last group from "E3FSBLK" uto "E3FSBLK" blocks\n",
                       o_blocks_count, n_blocks_count);
 
        if (n_blocks_count == 0 || n_blocks_count == o_blocks_count)
@@ -958,12 +1011,18 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es,
 
        add = EXT3_BLOCKS_PER_GROUP(sb) - last;
 
+       if (o_blocks_count + add < o_blocks_count) {
+               ext3_warning(sb, __FUNCTION__, "blocks_count overflow");
+               return -EINVAL;
+       }
+
        if (o_blocks_count + add > n_blocks_count)
                add = n_blocks_count - o_blocks_count;
 
        if (o_blocks_count + add < n_blocks_count)
                ext3_warning(sb, __FUNCTION__,
-                            "will only finish group (%lu blocks, %u new)",
+                            "will only finish group ("E3FSBLK
+                            " blocks, %u new)",
                             o_blocks_count + add, add);
 
        /* See if the device is actually as big as what was requested */
@@ -1006,10 +1065,10 @@ int ext3_group_extend(struct super_block *sb, struct ext3_super_block *es,
        ext3_journal_dirty_metadata(handle, EXT3_SB(sb)->s_sbh);
        sb->s_dirt = 1;
        unlock_super(sb);
-       ext3_debug("freeing blocks %lu through %lu\n", o_blocks_count,
+       ext3_debug("freeing blocks %lu through "E3FSBLK"\n", o_blocks_count,
                   o_blocks_count + add);
        ext3_free_blocks_sb(handle, sb, o_blocks_count, add, &freed_blocks);
-       ext3_debug("freed blocks %lu through %lu\n", o_blocks_count,
+       ext3_debug("freed blocks "E3FSBLK" through "E3FSBLK"\n", o_blocks_count,
                   o_blocks_count + add);
        if ((err = ext3_journal_stop(handle)))
                goto exit_put;