Commit a9bca018 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] ext2 block allocator fixes

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).
parent bc5de0cd
......@@ -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 @@ int ext2_new_block(struct inode *inode, unsigned long goal,
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;
}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment