===================================================================
@@ -1254,14 +1254,27 @@ void ext4_set_bits(void *bm, int cur, in
}
}
+static inline int mb_buddy_adjust_border(int* bit, void* bitmap, int side)
+{
+ if (mb_test_bit(*bit + side, bitmap)) {
+ mb_clear_bit(*bit, bitmap);
+ (*bit) -= side;
+ return 1;
+ }
+ else {
+ mb_set_bit(*bit += side, bitmap);
+ return -1;
+ }
+}
+
static void mb_free_blocks(struct inode *inode, struct ext4_buddy *e4b,
int first, int count)
{
int block = 0;
int max = 0;
- int order;
+ int order = 1;
+ int start = first, end = first + count - 1;
void *buddy;
- void *buddy2;
struct super_block *sb = e4b->bd_sb;
BUG_ON(first + count > (sb->s_blocksize << 3));
@@ -1283,57 +1296,42 @@ static void mb_free_blocks(struct inode
else if (!block && !max)
e4b->bd_info->bb_fragments++;
- /* let's maintain buddy itself */
- while (count-- > 0) {
- block = first++;
- order = 0;
-
- if (!mb_test_bit(block, e4b->bd_bitmap)) {
- ext4_fsblk_t blocknr;
-
- blocknr = ext4_group_first_block_no(sb, e4b->bd_group);
- blocknr += EXT4_C2B(EXT4_SB(sb), block);
- ext4_grp_locked_error(sb, e4b->bd_group,
- inode ? inode->i_ino : 0,
- blocknr,
- "freeing already freed block "
- "(bit %u)", block);
+ if ((start & 1) && mb_test_bit(start - 1, e4b->bd_bitmap))
+ start++;
+ mb_clear_bits(e4b->bd_bitmap, first, count);
+ if (!(end & 1) && mb_test_bit(end + 1, e4b->bd_bitmap))
+ end--;
+
+ e4b->bd_info->bb_counters[0] += count;
+
+ if (start > end) goto done;
+
+ start >>= 1;
+ end >>= 1;
+ buddy = mb_find_buddy(e4b, order, &max);
+
+ while (buddy) {
+ void *buddy2;
+
+ if (start & 1)
+ e4b->bd_info->bb_counters[order] += mb_buddy_adjust_border(&start,
buddy, -1);
+ if (!(end & 1))
+ e4b->bd_info->bb_counters[order] += mb_buddy_adjust_border(&end, buddy, 1);
+ if (start > end)
+ break;
+ order++;
+
+ if (start == end || !(buddy2 = mb_find_buddy(e4b, order, &max))) {
+ mb_clear_bits(buddy, start, end - start + 1);
+ e4b->bd_info->bb_counters[order - 1] += end - start + 1;
+ break;
}
- mb_clear_bit(block, e4b->bd_bitmap);
- e4b->bd_info->bb_counters[order]++;
-
- /* start of the buddy */
- buddy = mb_find_buddy(e4b, order, &max);
-
- do {
- block &= ~1UL;
- if (mb_test_bit(block, buddy) ||
- mb_test_bit(block + 1, buddy))
- break;
-
- /* both the buddies are free, try to coalesce them */
- buddy2 = mb_find_buddy(e4b, order + 1, &max);
-
- if (!buddy2)
- break;
-
- if (order > 0) {
- /* for special purposes, we don't set
- * free bits in bitmap */
- mb_set_bit(block, buddy);
- mb_set_bit(block + 1, buddy);
- }
- e4b->bd_info->bb_counters[order]--;
- e4b->bd_info->bb_counters[order]--;
-
- block = block >> 1;
- order++;
- e4b->bd_info->bb_counters[order]++;
-
- mb_clear_bit(block, buddy2);
- buddy = buddy2;
- } while (1);
+ start >>= 1;
+ end >>= 1;
+ buddy = buddy2;
}
+
+done:
mb_set_largest_free_order(sb, e4b->bd_info);
mb_check_buddy(e4b);