]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/ext4/balloc.c
percpu_counter: new function percpu_counter_sum_and_set
[linux-2.6-omap-h63xx.git] / fs / ext4 / balloc.c
index 0737e05ba3dd22842429f287356ca1c500265943..6369bacf0dcb8594bb2e7bd469696b2b56d511e7 100644 (file)
 #include <linux/capability.h>
 #include <linux/fs.h>
 #include <linux/jbd2.h>
-#include <linux/ext4_fs.h>
-#include <linux/ext4_jbd2.h>
 #include <linux/quotaops.h>
 #include <linux/buffer_head.h>
-
+#include "ext4.h"
+#include "ext4_jbd2.h"
 #include "group.h"
+
 /*
  * balloc.c contains the blocks allocation and deallocation routines
  */
@@ -43,12 +43,51 @@ void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr,
 
 }
 
+static int ext4_block_in_group(struct super_block *sb, ext4_fsblk_t block,
+                       ext4_group_t block_group)
+{
+       ext4_group_t actual_group;
+       ext4_get_group_no_and_offset(sb, block, &actual_group, NULL);
+       if (actual_group == block_group)
+               return 1;
+       return 0;
+}
+
+static int ext4_group_used_meta_blocks(struct super_block *sb,
+                               ext4_group_t block_group)
+{
+       ext4_fsblk_t tmp;
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       /* block bitmap, inode bitmap, and inode table blocks */
+       int used_blocks = sbi->s_itb_per_group + 2;
+
+       if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
+               struct ext4_group_desc *gdp;
+               struct buffer_head *bh;
+
+               gdp = ext4_get_group_desc(sb, block_group, &bh);
+               if (!ext4_block_in_group(sb, ext4_block_bitmap(sb, gdp),
+                                       block_group))
+                       used_blocks--;
+
+               if (!ext4_block_in_group(sb, ext4_inode_bitmap(sb, gdp),
+                                       block_group))
+                       used_blocks--;
+
+               tmp = ext4_inode_table(sb, gdp);
+               for (; tmp < ext4_inode_table(sb, gdp) +
+                               sbi->s_itb_per_group; tmp++) {
+                       if (!ext4_block_in_group(sb, tmp, block_group))
+                               used_blocks -= 1;
+               }
+       }
+       return used_blocks;
+}
 /* Initializes an uninitialized block bitmap if given, and returns the
  * number of blocks free in the group. */
 unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
                 ext4_group_t block_group, struct ext4_group_desc *gdp)
 {
-       unsigned long start;
        int bit, bit_max;
        unsigned free_blocks, group_blocks;
        struct ext4_sb_info *sbi = EXT4_SB(sb);
@@ -59,7 +98,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
                /* If checksum is bad mark all blocks used to prevent allocation
                 * essentially implementing a per-group read-only flag. */
                if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) {
-                       ext4_error(sb, __FUNCTION__,
+                       ext4_error(sb, __func__,
                                  "Checksum bad for group %lu\n", block_group);
                        gdp->bg_free_blocks_count = 0;
                        gdp->bg_free_inodes_count = 0;
@@ -82,12 +121,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
                                le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks);
                }
        } else { /* For META_BG_BLOCK_GROUPS */
-               int group_rel = (block_group -
-                                le32_to_cpu(sbi->s_es->s_first_meta_bg)) %
-                               EXT4_DESC_PER_BLOCK(sb);
-               if (group_rel == 0 || group_rel == 1 ||
-                   (group_rel == EXT4_DESC_PER_BLOCK(sb) - 1))
-                       bit_max += 1;
+               bit_max += ext4_bg_num_gdb(sb, block_group);
        }
 
        if (block_group == sbi->s_groups_count - 1) {
@@ -106,19 +140,34 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
        free_blocks = group_blocks - bit_max;
 
        if (bh) {
+               ext4_fsblk_t start, tmp;
+               int flex_bg = 0;
+
                for (bit = 0; bit < bit_max; bit++)
                        ext4_set_bit(bit, bh->b_data);
 
-               start = block_group * EXT4_BLOCKS_PER_GROUP(sb) +
-                       le32_to_cpu(sbi->s_es->s_first_data_block);
+               start = ext4_group_first_block_no(sb, block_group);
 
-               /* Set bits for block and inode bitmaps, and inode table */
-               ext4_set_bit(ext4_block_bitmap(sb, gdp) - start, bh->b_data);
-               ext4_set_bit(ext4_inode_bitmap(sb, gdp) - start, bh->b_data);
-               for (bit = (ext4_inode_table(sb, gdp) - start),
-                    bit_max = bit + sbi->s_itb_per_group; bit < bit_max; bit++)
-                       ext4_set_bit(bit, bh->b_data);
+               if (EXT4_HAS_INCOMPAT_FEATURE(sb,
+                                             EXT4_FEATURE_INCOMPAT_FLEX_BG))
+                       flex_bg = 1;
 
+               /* Set bits for block and inode bitmaps, and inode table */
+               tmp = ext4_block_bitmap(sb, gdp);
+               if (!flex_bg || ext4_block_in_group(sb, tmp, block_group))
+                       ext4_set_bit(tmp - start, bh->b_data);
+
+               tmp = ext4_inode_bitmap(sb, gdp);
+               if (!flex_bg || ext4_block_in_group(sb, tmp, block_group))
+                       ext4_set_bit(tmp - start, bh->b_data);
+
+               tmp = ext4_inode_table(sb, gdp);
+               for (; tmp < ext4_inode_table(sb, gdp) +
+                               sbi->s_itb_per_group; tmp++) {
+                       if (!flex_bg ||
+                               ext4_block_in_group(sb, tmp, block_group))
+                               ext4_set_bit(tmp - start, bh->b_data);
+               }
                /*
                 * Also if the number of blocks within the group is
                 * less than the blocksize * 8 ( which is the size
@@ -126,8 +175,7 @@ unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh,
                 */
                mark_bitmap_end(group_blocks, sb->s_blocksize * 8, bh->b_data);
        }
-
-       return free_blocks - sbi->s_itb_per_group - 2;
+       return free_blocks - ext4_group_used_meta_blocks(sb, block_group);
 }
 
 
@@ -235,14 +283,14 @@ static int ext4_valid_block_bitmap(struct super_block *sb,
                return 1;
 
 err_out:
-       ext4_error(sb, __FUNCTION__,
+       ext4_error(sb, __func__,
                        "Invalid block bitmap - "
                        "block_group = %d, block = %llu",
                        block_group, bitmap_blk);
        return 0;
 }
 /**
- * read_block_bitmap()
+ * ext4_read_block_bitmap()
  * @sb:                        super block
  * @block_group:       given block group
  *
@@ -252,7 +300,7 @@ err_out:
  * Return buffer_head on success or NULL in case of failure.
  */
 struct buffer_head *
-read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
+ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
 {
        struct ext4_group_desc * desc;
        struct buffer_head * bh = NULL;
@@ -264,7 +312,7 @@ read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
        bitmap_blk = ext4_block_bitmap(sb, desc);
        bh = sb_getblk(sb, bitmap_blk);
        if (unlikely(!bh)) {
-               ext4_error(sb, __FUNCTION__,
+               ext4_error(sb, __func__,
                            "Cannot read block bitmap - "
                            "block_group = %d, block_bitmap = %llu",
                            (int)block_group, (unsigned long long)bitmap_blk);
@@ -281,17 +329,17 @@ read_block_bitmap(struct super_block *sb, ext4_group_t block_group)
        }
        if (bh_submit_read(bh) < 0) {
                put_bh(bh);
-               ext4_error(sb, __FUNCTION__,
+               ext4_error(sb, __func__,
                            "Cannot read block bitmap - "
                            "block_group = %d, block_bitmap = %llu",
                            (int)block_group, (unsigned long long)bitmap_blk);
                return NULL;
        }
-       if (!ext4_valid_block_bitmap(sb, desc, block_group, bh)) {
-               put_bh(bh);
-               return NULL;
-       }
-
+       ext4_valid_block_bitmap(sb, desc, block_group, bh);
+       /*
+        * file system mounted not to panic on error,
+        * continue with corrupt bitmap
+        */
        return bh;
 }
 /*
@@ -356,11 +404,10 @@ restart:
                prev = rsv;
        }
        printk("Window map complete.\n");
-       if (bad)
-               BUG();
+       BUG_ON(bad);
 }
 #define rsv_window_dump(root, verbose) \
-       __rsv_window_dump((root), (verbose), __FUNCTION__)
+       __rsv_window_dump((root), (verbose), __func__)
 #else
 #define rsv_window_dump(root, verbose) do {} while (0)
 #endif
@@ -641,7 +688,7 @@ do_more:
                count -= overflow;
        }
        brelse(bitmap_bh);
-       bitmap_bh = read_block_bitmap(sb, block_group);
+       bitmap_bh = ext4_read_block_bitmap(sb, block_group);
        if (!bitmap_bh)
                goto error_return;
        desc = ext4_get_group_desc (sb, block_group, &gd_bh);
@@ -740,7 +787,7 @@ do_more:
                if (!ext4_clear_bit_atomic(sb_bgl_lock(sbi, block_group),
                                                bit + i, bitmap_bh->b_data)) {
                        jbd_unlock_bh_state(bitmap_bh);
-                       ext4_error(sb, __FUNCTION__,
+                       ext4_error(sb, __func__,
                                   "bit already cleared for block %llu",
                                   (ext4_fsblk_t)(block + i));
                        jbd_lock_bh_state(bitmap_bh);
@@ -752,13 +799,18 @@ do_more:
        jbd_unlock_bh_state(bitmap_bh);
 
        spin_lock(sb_bgl_lock(sbi, block_group));
-       desc->bg_free_blocks_count =
-               cpu_to_le16(le16_to_cpu(desc->bg_free_blocks_count) +
-                       group_freed);
+       le16_add_cpu(&desc->bg_free_blocks_count, group_freed);
        desc->bg_checksum = ext4_group_desc_csum(sbi, block_group, desc);
        spin_unlock(sb_bgl_lock(sbi, block_group));
        percpu_counter_add(&sbi->s_freeblocks_counter, count);
 
+       if (sbi->s_log_groups_per_flex) {
+               ext4_group_t flex_group = ext4_flex_group(sbi, block_group);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_blocks += count;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        /* We dirtied the bitmap block */
        BUFFER_TRACE(bitmap_bh, "dirtied bitmap block");
        err = ext4_journal_dirty_metadata(handle, bitmap_bh);
@@ -1547,23 +1599,35 @@ out:
 
 /**
  * ext4_has_free_blocks()
- * @sbi:               in-core super block structure.
+ * @sbi:       in-core super block structure.
+ * @nblocks:   number of neeed blocks
  *
- * Check if filesystem has at least 1 free block available for allocation.
+ * Check if filesystem has free blocks available for allocation.
+ * Return the number of blocks avaible for allocation for this request
+ * On success, return nblocks
  */
-static int ext4_has_free_blocks(struct ext4_sb_info *sbi)
+ext4_fsblk_t ext4_has_free_blocks(struct ext4_sb_info *sbi,
+                                               ext4_fsblk_t nblocks)
 {
-       ext4_fsblk_t free_blocks, root_blocks;
+       ext4_fsblk_t free_blocks;
+       ext4_fsblk_t root_blocks = 0;
 
        free_blocks = percpu_counter_read_positive(&sbi->s_freeblocks_counter);
-       root_blocks = ext4_r_blocks_count(sbi->s_es);
-       if (free_blocks < root_blocks + 1 && !capable(CAP_SYS_RESOURCE) &&
+
+       if (!capable(CAP_SYS_RESOURCE) &&
                sbi->s_resuid != current->fsuid &&
-               (sbi->s_resgid == 0 || !in_group_p (sbi->s_resgid))) {
-               return 0;
-       }
-       return 1;
-}
+               (sbi->s_resgid == 0 || !in_group_p(sbi->s_resgid)))
+               root_blocks = ext4_r_blocks_count(sbi->s_es);
+#ifdef CONFIG_SMP
+       if (free_blocks - root_blocks < FBC_BATCH)
+               free_blocks =
+                       percpu_counter_sum_and_set(&sbi->s_freeblocks_counter);
+#endif
+       if (free_blocks - root_blocks < nblocks)
+               return free_blocks - root_blocks;
+       return nblocks;
+ }
+
 
 /**
  * ext4_should_retry_alloc()
@@ -1579,7 +1643,7 @@ static int ext4_has_free_blocks(struct ext4_sb_info *sbi)
  */
 int ext4_should_retry_alloc(struct super_block *sb, int *retries)
 {
-       if (!ext4_has_free_blocks(EXT4_SB(sb)) || (*retries)++ > 3)
+       if (!ext4_has_free_blocks(EXT4_SB(sb), 1) || (*retries)++ > 3)
                return 0;
 
        jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id);
@@ -1588,20 +1652,24 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries)
 }
 
 /**
- * ext4_new_blocks_old() -- core block(s) allocation function
+ * ext4_old_new_blocks() -- core block bitmap based block allocation function
+ *
  * @handle:            handle to this transaction
  * @inode:             file inode
  * @goal:              given target block(filesystem wide)
  * @count:             target number of blocks to allocate
  * @errp:              error code
  *
- * ext4_new_blocks uses a goal block to assist allocation.  It tries to
- * allocate block(s) from the block group contains the goal block first. If that
- * fails, it will try to allocate block(s) from other block groups without
- * any specific goal block.
+ * ext4_old_new_blocks uses a goal block to assist allocation and look up
+ * the block bitmap directly to do block allocation.  It tries to
+ * allocate block(s) from the block group contains the goal block first. If
+ * that fails, it will try to allocate block(s) from other block groups
+ * without any specific goal block.
+ *
+ * This function is called when -o nomballoc mount option is enabled
  *
  */
-ext4_fsblk_t ext4_new_blocks_old(handle_t *handle, struct inode *inode,
+ext4_fsblk_t ext4_old_new_blocks(handle_t *handle, struct inode *inode,
                        ext4_fsblk_t goal, unsigned long *count, int *errp)
 {
        struct buffer_head *bitmap_bh = NULL;
@@ -1625,13 +1693,21 @@ ext4_fsblk_t ext4_new_blocks_old(handle_t *handle, struct inode *inode,
        ext4_group_t ngroups;
        unsigned long num = *count;
 
-       *errp = -ENOSPC;
        sb = inode->i_sb;
        if (!sb) {
+               *errp = -ENODEV;
                printk("ext4_new_block: nonexistent device");
                return 0;
        }
 
+       sbi = EXT4_SB(sb);
+       *count = ext4_has_free_blocks(sbi, *count);
+       if (*count == 0) {
+               *errp = -ENOSPC;
+               return 0;       /*return with ENOSPC error */
+       }
+       num = *count;
+
        /*
         * Check quota for allocation of this block.
         */
@@ -1655,11 +1731,6 @@ ext4_fsblk_t ext4_new_blocks_old(handle_t *handle, struct inode *inode,
        if (block_i && ((windowsz = block_i->rsv_window_node.rsv_goal_size) > 0))
                my_rsv = &block_i->rsv_window_node;
 
-       if (!ext4_has_free_blocks(sbi)) {
-               *errp = -ENOSPC;
-               goto out;
-       }
-
        /*
         * First, test whether the goal block is free.
         */
@@ -1683,7 +1754,7 @@ retry_alloc:
                my_rsv = NULL;
 
        if (free_blocks > 0) {
-               bitmap_bh = read_block_bitmap(sb, group_no);
+               bitmap_bh = ext4_read_block_bitmap(sb, group_no);
                if (!bitmap_bh)
                        goto io_error;
                grp_alloc_blk = ext4_try_to_allocate_with_rsv(sb, handle,
@@ -1719,7 +1790,7 @@ retry_alloc:
                        continue;
 
                brelse(bitmap_bh);
-               bitmap_bh = read_block_bitmap(sb, group_no);
+               bitmap_bh = ext4_read_block_bitmap(sb, group_no);
                if (!bitmap_bh)
                        goto io_error;
                /*
@@ -1772,7 +1843,12 @@ allocated:
                            "Allocating block in system zone - "
                            "blocks from %llu, length %lu",
                             ret_block, num);
-               goto out;
+               /*
+                * claim_block marked the blocks we allocated
+                * as in use. So we may want to selectively
+                * mark some of the blocks as free
+                */
+               goto retry_alloc;
        }
 
        performed_allocation = 1;
@@ -1798,7 +1874,7 @@ allocated:
                        if (ext4_test_bit(grp_alloc_blk+i,
                                        bh2jh(bitmap_bh)->b_committed_data)) {
                                printk("%s: block was unexpectedly set in "
-                                       "b_committed_data\n", __FUNCTION__);
+                                       "b_committed_data\n", __func__);
                        }
                }
        }
@@ -1823,12 +1899,18 @@ allocated:
        spin_lock(sb_bgl_lock(sbi, group_no));
        if (gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT))
                gdp->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT);
-       gdp->bg_free_blocks_count =
-                       cpu_to_le16(le16_to_cpu(gdp->bg_free_blocks_count)-num);
+       le16_add_cpu(&gdp->bg_free_blocks_count, -num);
        gdp->bg_checksum = ext4_group_desc_csum(sbi, group_no, gdp);
        spin_unlock(sb_bgl_lock(sbi, group_no));
        percpu_counter_sub(&sbi->s_freeblocks_counter, num);
 
+       if (sbi->s_log_groups_per_flex) {
+               ext4_group_t flex_group = ext4_flex_group(sbi, group_no);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_blocks -= num;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        BUFFER_TRACE(gdp_bh, "journal_dirty_metadata for group descriptor");
        err = ext4_journal_dirty_metadata(handle, gdp_bh);
        if (!fatal)
@@ -1860,47 +1942,96 @@ out:
        return 0;
 }
 
-ext4_fsblk_t ext4_new_block(handle_t *handle, struct inode *inode,
-               ext4_fsblk_t goal, int *errp)
+#define EXT4_META_BLOCK 0x1
+
+static ext4_fsblk_t do_blk_alloc(handle_t *handle, struct inode *inode,
+                               ext4_lblk_t iblock, ext4_fsblk_t goal,
+                               unsigned long *count, int *errp, int flags)
 {
        struct ext4_allocation_request ar;
        ext4_fsblk_t ret;
 
        if (!test_opt(inode->i_sb, MBALLOC)) {
-               unsigned long count = 1;
-               ret = ext4_new_blocks_old(handle, inode, goal, &count, errp);
-               return ret;
+               return ext4_old_new_blocks(handle, inode, goal, count, errp);
        }
 
        memset(&ar, 0, sizeof(ar));
+       /* Fill with neighbour allocated blocks */
+
        ar.inode = inode;
        ar.goal = goal;
-       ar.len = 1;
+       ar.len = *count;
+       ar.logical = iblock;
+
+       if (S_ISREG(inode->i_mode) && !(flags & EXT4_META_BLOCK))
+               /* enable in-core preallocation for data block allocation */
+               ar.flags = EXT4_MB_HINT_DATA;
+       else
+               /* disable in-core preallocation for non-regular files */
+               ar.flags = 0;
+
        ret = ext4_mb_new_blocks(handle, &ar, errp);
+       *count = ar.len;
        return ret;
 }
 
-ext4_fsblk_t ext4_new_blocks(handle_t *handle, struct inode *inode,
+/*
+ * ext4_new_meta_block() -- allocate block for meta data (indexing) blocks
+ *
+ * @handle:             handle to this transaction
+ * @inode:              file inode
+ * @goal:               given target block(filesystem wide)
+ * @errp:               error code
+ *
+ * Return allocated block number on success
+ */
+ext4_fsblk_t ext4_new_meta_block(handle_t *handle, struct inode *inode,
+               ext4_fsblk_t goal, int *errp)
+{
+       unsigned long count = 1;
+       return do_blk_alloc(handle, inode, 0, goal,
+                       &count, errp, EXT4_META_BLOCK);
+}
+
+/*
+ * ext4_new_meta_blocks() -- allocate block for meta data (indexing) blocks
+ *
+ * @handle:             handle to this transaction
+ * @inode:              file inode
+ * @goal:               given target block(filesystem wide)
+ * @count:             total number of blocks need
+ * @errp:               error code
+ *
+ * Return 1st allocated block numberon success, *count stores total account
+ * error stores in errp pointer
+ */
+ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode,
                ext4_fsblk_t goal, unsigned long *count, int *errp)
 {
-       struct ext4_allocation_request ar;
-       ext4_fsblk_t ret;
+       return do_blk_alloc(handle, inode, 0, goal,
+                       count, errp, EXT4_META_BLOCK);
+}
 
-       if (!test_opt(inode->i_sb, MBALLOC)) {
-               ret = ext4_new_blocks_old(handle, inode, goal, count, errp);
-               return ret;
-       }
+/*
+ * ext4_new_blocks() -- allocate data blocks
+ *
+ * @handle:             handle to this transaction
+ * @inode:              file inode
+ * @goal:               given target block(filesystem wide)
+ * @count:             total number of blocks need
+ * @errp:               error code
+ *
+ * Return 1st allocated block numberon success, *count stores total account
+ * error stores in errp pointer
+ */
 
-       memset(&ar, 0, sizeof(ar));
-       ar.inode = inode;
-       ar.goal = goal;
-       ar.len = *count;
-       ret = ext4_mb_new_blocks(handle, &ar, errp);
-       *count = ar.len;
-       return ret;
+ext4_fsblk_t ext4_new_blocks(handle_t *handle, struct inode *inode,
+                               ext4_lblk_t iblock, ext4_fsblk_t goal,
+                               unsigned long *count, int *errp)
+{
+       return do_blk_alloc(handle, inode, iblock, goal, count, errp, 0);
 }
 
-
 /**
  * ext4_count_free_blocks() -- count filesystem free blocks
  * @sb:                superblock
@@ -1931,7 +2062,7 @@ ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb)
                        continue;
                desc_count += le16_to_cpu(gdp->bg_free_blocks_count);
                brelse(bitmap_bh);
-               bitmap_bh = read_block_bitmap(sb, i);
+               bitmap_bh = ext4_read_block_bitmap(sb, i);
                if (bitmap_bh == NULL)
                        continue;