]> Pileus Git - ~andy/linux/blobdiff - fs/ext4/ialloc.c
Merge master.kernel.org:/home/rmk/linux-2.6-arm
[~andy/linux] / fs / ext4 / ialloc.c
index 486e46a3918de44a0534db6553058c01e7cf6ef2..a92eb305344fe2fd299cd3c15928a033a1374d10 100644 (file)
@@ -15,8 +15,6 @@
 #include <linux/time.h>
 #include <linux/fs.h>
 #include <linux/jbd2.h>
-#include <linux/ext4_fs.h>
-#include <linux/ext4_jbd2.h>
 #include <linux/stat.h>
 #include <linux/string.h>
 #include <linux/quotaops.h>
@@ -25,7 +23,8 @@
 #include <linux/bitops.h>
 #include <linux/blkdev.h>
 #include <asm/byteorder.h>
-
+#include "ext4.h"
+#include "ext4_jbd2.h"
 #include "xattr.h"
 #include "acl.h"
 #include "group.h"
@@ -75,7 +74,7 @@ unsigned ext4_init_inode_bitmap(struct super_block *sb, struct buffer_head *bh,
        /* If checksum is bad mark all blocks and inodes use 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__, "Checksum bad for group %lu\n",
+               ext4_error(sb, __func__, "Checksum bad for group %lu\n",
                           block_group);
                gdp->bg_free_blocks_count = 0;
                gdp->bg_free_inodes_count = 0;
@@ -158,6 +157,7 @@ void ext4_free_inode (handle_t *handle, struct inode * inode)
        struct ext4_super_block * es;
        struct ext4_sb_info *sbi;
        int fatal = 0, err;
+       ext4_group_t flex_group;
 
        if (atomic_read(&inode->i_count) > 1) {
                printk ("ext4_free_inode: inode has count=%d\n",
@@ -223,11 +223,9 @@ void ext4_free_inode (handle_t *handle, struct inode * inode)
 
                if (gdp) {
                        spin_lock(sb_bgl_lock(sbi, block_group));
-                       gdp->bg_free_inodes_count = cpu_to_le16(
-                               le16_to_cpu(gdp->bg_free_inodes_count) + 1);
+                       le16_add_cpu(&gdp->bg_free_inodes_count, 1);
                        if (is_directory)
-                               gdp->bg_used_dirs_count = cpu_to_le16(
-                                 le16_to_cpu(gdp->bg_used_dirs_count) - 1);
+                               le16_add_cpu(&gdp->bg_used_dirs_count, -1);
                        gdp->bg_checksum = ext4_group_desc_csum(sbi,
                                                        block_group, gdp);
                        spin_unlock(sb_bgl_lock(sbi, block_group));
@@ -235,6 +233,12 @@ void ext4_free_inode (handle_t *handle, struct inode * inode)
                        if (is_directory)
                                percpu_counter_dec(&sbi->s_dirs_counter);
 
+                       if (sbi->s_log_groups_per_flex) {
+                               flex_group = ext4_flex_group(sbi, block_group);
+                               spin_lock(sb_bgl_lock(sbi, flex_group));
+                               sbi->s_flex_groups[flex_group].free_inodes++;
+                               spin_unlock(sb_bgl_lock(sbi, flex_group));
+                       }
                }
                BUFFER_TRACE(bh2, "call ext4_journal_dirty_metadata");
                err = ext4_journal_dirty_metadata(handle, bh2);
@@ -289,6 +293,80 @@ static int find_group_dir(struct super_block *sb, struct inode *parent,
        return ret;
 }
 
+#define free_block_ratio 10
+
+static int find_group_flex(struct super_block *sb, struct inode *parent,
+                          ext4_group_t *best_group)
+{
+       struct ext4_sb_info *sbi = EXT4_SB(sb);
+       struct ext4_group_desc *desc;
+       struct buffer_head *bh;
+       struct flex_groups *flex_group = sbi->s_flex_groups;
+       ext4_group_t parent_group = EXT4_I(parent)->i_block_group;
+       ext4_group_t parent_fbg_group = ext4_flex_group(sbi, parent_group);
+       ext4_group_t ngroups = sbi->s_groups_count;
+       int flex_size = ext4_flex_bg_size(sbi);
+       ext4_group_t best_flex = parent_fbg_group;
+       int blocks_per_flex = sbi->s_blocks_per_group * flex_size;
+       int flexbg_free_blocks;
+       int flex_freeb_ratio;
+       ext4_group_t n_fbg_groups;
+       ext4_group_t i;
+
+       n_fbg_groups = (sbi->s_groups_count + flex_size - 1) >>
+               sbi->s_log_groups_per_flex;
+
+find_close_to_parent:
+       flexbg_free_blocks = flex_group[best_flex].free_blocks;
+       flex_freeb_ratio = flexbg_free_blocks * 100 / blocks_per_flex;
+       if (flex_group[best_flex].free_inodes &&
+           flex_freeb_ratio > free_block_ratio)
+               goto found_flexbg;
+
+       if (best_flex && best_flex == parent_fbg_group) {
+               best_flex--;
+               goto find_close_to_parent;
+       }
+
+       for (i = 0; i < n_fbg_groups; i++) {
+               if (i == parent_fbg_group || i == parent_fbg_group - 1)
+                       continue;
+
+               flexbg_free_blocks = flex_group[i].free_blocks;
+               flex_freeb_ratio = flexbg_free_blocks * 100 / blocks_per_flex;
+
+               if (flex_freeb_ratio > free_block_ratio &&
+                   flex_group[i].free_inodes) {
+                       best_flex = i;
+                       goto found_flexbg;
+               }
+
+               if (best_flex < 0 ||
+                   (flex_group[i].free_blocks >
+                    flex_group[best_flex].free_blocks &&
+                    flex_group[i].free_inodes))
+                       best_flex = i;
+       }
+
+       if (!flex_group[best_flex].free_inodes ||
+           !flex_group[best_flex].free_blocks)
+               return -1;
+
+found_flexbg:
+       for (i = best_flex * flex_size; i < ngroups &&
+                    i < (best_flex + 1) * flex_size; i++) {
+               desc = ext4_get_group_desc(sb, i, &bh);
+               if (le16_to_cpu(desc->bg_free_inodes_count)) {
+                       *best_group = i;
+                       goto out;
+               }
+       }
+
+       return -1;
+out:
+       return 0;
+}
+
 /*
  * Orlov's allocator for directories.
  *
@@ -504,6 +582,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int mode)
        struct inode *ret;
        ext4_group_t i;
        int free = 0;
+       ext4_group_t flex_group;
 
        /* Cannot create files in a deleted directory */
        if (!dir || !dir->i_nlink)
@@ -517,6 +596,12 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int mode)
 
        sbi = EXT4_SB(sb);
        es = sbi->s_es;
+
+       if (sbi->s_log_groups_per_flex) {
+               ret2 = find_group_flex(sb, dir, &group);
+               goto got_group;
+       }
+
        if (S_ISDIR(mode)) {
                if (test_opt (sb, OLDALLOC))
                        ret2 = find_group_dir(sb, dir, &group);
@@ -525,6 +610,7 @@ struct inode *ext4_new_inode(handle_t *handle, struct inode * dir, int mode)
        } else
                ret2 = find_group_other(sb, dir, &group);
 
+got_group:
        err = -ENOSPC;
        if (ret2 == -1)
                goto out;
@@ -588,7 +674,7 @@ got:
        ino++;
        if ((group == 0 && ino < EXT4_FIRST_INO(sb)) ||
            ino > EXT4_INODES_PER_GROUP(sb)) {
-               ext4_error(sb, __FUNCTION__,
+               ext4_error(sb, __func__,
                           "reserved inode or inode > inodes count - "
                           "block_group = %lu, inode=%lu", group,
                           ino + group * EXT4_INODES_PER_GROUP(sb));
@@ -603,7 +689,7 @@ got:
        /* We may have to initialize the block bitmap if it isn't already */
        if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM) &&
            gdp->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) {
-               struct buffer_head *block_bh = read_block_bitmap(sb, group);
+               struct buffer_head *block_bh = ext4_read_block_bitmap(sb, group);
 
                BUFFER_TRACE(block_bh, "get block bitmap access");
                err = ext4_journal_get_write_access(handle, block_bh);
@@ -664,11 +750,9 @@ got:
                                cpu_to_le16(EXT4_INODES_PER_GROUP(sb) - ino);
        }
 
-       gdp->bg_free_inodes_count =
-               cpu_to_le16(le16_to_cpu(gdp->bg_free_inodes_count) - 1);
+       le16_add_cpu(&gdp->bg_free_inodes_count, -1);
        if (S_ISDIR(mode)) {
-               gdp->bg_used_dirs_count =
-                       cpu_to_le16(le16_to_cpu(gdp->bg_used_dirs_count) + 1);
+               le16_add_cpu(&gdp->bg_used_dirs_count, 1);
        }
        gdp->bg_checksum = ext4_group_desc_csum(sbi, group, gdp);
        spin_unlock(sb_bgl_lock(sbi, group));
@@ -681,6 +765,13 @@ got:
                percpu_counter_inc(&sbi->s_dirs_counter);
        sb->s_dirt = 1;
 
+       if (sbi->s_log_groups_per_flex) {
+               flex_group = ext4_flex_group(sbi, group);
+               spin_lock(sb_bgl_lock(sbi, flex_group));
+               sbi->s_flex_groups[flex_group].free_inodes--;
+               spin_unlock(sb_bgl_lock(sbi, flex_group));
+       }
+
        inode->i_uid = current->fsuid;
        if (test_opt (sb, GRPID))
                inode->i_gid = dir->i_gid;
@@ -744,23 +835,20 @@ got:
        if (err)
                goto fail_free_drop;
 
-       err = ext4_mark_inode_dirty(handle, inode);
-       if (err) {
-               ext4_std_error(sb, err);
-               goto fail_free_drop;
-       }
        if (test_opt(sb, EXTENTS)) {
-               /* set extent flag only for directory and file */
-               if (S_ISDIR(mode) || S_ISREG(mode)) {
+               /* set extent flag only for directory, file and normal symlink*/
+               if (S_ISDIR(mode) || S_ISREG(mode) || S_ISLNK(mode)) {
                        EXT4_I(inode)->i_flags |= EXT4_EXTENTS_FL;
                        ext4_ext_tree_init(handle, inode);
-                       err = ext4_update_incompat_feature(handle, sb,
-                                       EXT4_FEATURE_INCOMPAT_EXTENTS);
-                       if (err)
-                               goto fail;
                }
        }
 
+       err = ext4_mark_inode_dirty(handle, inode);
+       if (err) {
+               ext4_std_error(sb, err);
+               goto fail_free_drop;
+       }
+
        ext4_debug("allocating inode %lu\n", inode->i_ino);
        goto really_out;
 fail:
@@ -796,7 +884,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
 
        /* Error cases - e2fsck has already cleaned up for us */
        if (ino > max_ino) {
-               ext4_warning(sb, __FUNCTION__,
+               ext4_warning(sb, __func__,
                             "bad orphan ino %lu!  e2fsck was run?", ino);
                goto error;
        }
@@ -805,7 +893,7 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
        bit = (ino - 1) % EXT4_INODES_PER_GROUP(sb);
        bitmap_bh = read_inode_bitmap(sb, block_group);
        if (!bitmap_bh) {
-               ext4_warning(sb, __FUNCTION__,
+               ext4_warning(sb, __func__,
                             "inode bitmap error for orphan %lu", ino);
                goto error;
        }
@@ -821,6 +909,14 @@ struct inode *ext4_orphan_get(struct super_block *sb, unsigned long ino)
        if (IS_ERR(inode))
                goto iget_failed;
 
+       /*
+        * If the orphans has i_nlinks > 0 then it should be able to be
+        * truncated, otherwise it won't be removed from the orphan list
+        * during processing and an infinite loop will result.
+        */
+       if (inode->i_nlink && !ext4_can_truncate(inode))
+               goto bad_orphan;
+
        if (NEXT_ORPHAN(inode) > max_ino)
                goto bad_orphan;
        brelse(bitmap_bh);
@@ -830,7 +926,7 @@ iget_failed:
        err = PTR_ERR(inode);
        inode = NULL;
 bad_orphan:
-       ext4_warning(sb, __FUNCTION__,
+       ext4_warning(sb, __func__,
                     "bad orphan inode %lu!  e2fsck was run?", ino);
        printk(KERN_NOTICE "ext4_test_bit(bit=%d, block=%llu) = %d\n",
               bit, (unsigned long long)bitmap_bh->b_blocknr,
@@ -842,6 +938,7 @@ bad_orphan:
                printk(KERN_NOTICE "NEXT_ORPHAN(inode)=%u\n",
                       NEXT_ORPHAN(inode));
                printk(KERN_NOTICE "max_ino=%lu\n", max_ino);
+               printk(KERN_NOTICE "i_nlink=%u\n", inode->i_nlink);
                /* Avoid freeing blocks if we got a bad deleted inode */
                if (inode->i_nlink == 0)
                        inode->i_blocks = 0;