]> git.hungrycats.org Git - linux/commitdiff
[PATCH] ext2 block allocator fixes
authorAndrew Morton <akpm@osdl.org>
Tue, 18 Nov 2003 16:16:07 +0000 (08:16 -0800)
committerLinus Torvalds <torvalds@home.osdl.org>
Tue, 18 Nov 2003 16:16:07 +0000 (08:16 -0800)
Fix a couple of problems which were introduced by a recent race fix in the
ext2 block allocator:

- if the allocation attempt raced, and lost the race then a new attempt is
  made.  But the earlier reservation must be put back first.

  Add a call to group_release_blocks() to fix this.

- if the filesystem is genuinely corrupted then the code as-is can get
  stuck in an infinite loop, thinking that a blockgroup has free blocks and
  then discovering that its bitmap is full.

  Fix this by baling out after having scanned all blockgroups twice.

  (Thanks Muli Ben-Yehuda <mulix@mulix.org> for spotting this).

fs/ext2/balloc.c

index a0aa0386f21faff42ae4de029a06ce6d726b338e..a34b3801336e44e7feabd1b34b12ccda8a34fe3a 100644 (file)
@@ -331,7 +331,7 @@ int ext2_new_block(struct inode *inode, unsigned long goal,
        struct ext2_group_desc *desc;
        int group_no;                   /* i */
        int ret_block;                  /* j */
-       int bit;                        /* k */
+       int group_idx;                  /* k */
        int target_block;               /* tmp */
        int block = 0;
        struct super_block *sb = inode->i_sb;
@@ -340,6 +340,7 @@ int ext2_new_block(struct inode *inode, unsigned long goal,
        unsigned group_size = EXT2_BLOCKS_PER_GROUP(sb);
        unsigned prealloc_goal = es->s_prealloc_blocks;
        unsigned group_alloc = 0, es_alloc, dq_alloc;
+       int nr_scanned_groups;
 
        if (!prealloc_goal--)
                prealloc_goal = EXT2_DEFAULT_PREALLOC_BLOCKS - 1;
@@ -402,9 +403,10 @@ int ext2_new_block(struct inode *inode, unsigned long goal,
         * Now search the rest of the groups.  We assume that 
         * i and desc correctly point to the last group visited.
         */
+       nr_scanned_groups = 0;
 retry:
-       for (bit = 0; !group_alloc &&
-                       bit < sbi->s_groups_count; bit++) {
+       for (group_idx = 0; !group_alloc &&
+                       group_idx < sbi->s_groups_count; group_idx++) {
                group_no++;
                if (group_no >= sbi->s_groups_count)
                        group_no = 0;
@@ -426,10 +428,21 @@ retry:
        ret_block = grab_block(sb_bgl_lock(sbi, group_no), bitmap_bh->b_data,
                                group_size, 0);
        if (ret_block < 0) {
+               /*
+                * If a free block counter is corrupted we can loop inifintely.
+                * Detect that here.
+                */
+               nr_scanned_groups++;
+               if (nr_scanned_groups > 2 * sbi->s_groups_count) {
+                       ext2_error(sb, "ext2_new_block",
+                               "corrupted free blocks counters");
+                       goto io_error;
+               }
                /*
                 * Someone else grabbed the last free block in this blockgroup
                 * before us.  Retry the scan.
                 */
+               group_release_blocks(sb, group_no, desc, gdp_bh, group_alloc);
                group_alloc = 0;
                goto retry;
        }