ext4: fix xfstest generic/269 double revoked buffer bug with bigalloc
authorDaeho Jeong <daeho.jeong@samsung.com>
Sun, 18 Oct 2015 02:28:21 +0000 (22:28 -0400)
committerTheodore Ts'o <tytso@mit.edu>
Sun, 18 Oct 2015 02:28:21 +0000 (22:28 -0400)
When you repeatly execute xfstest generic/269 with bigalloc_1k option
enabled using the below command:

"./kvm-xfstests -c bigalloc_1k -m nodelalloc -C 1000 generic/269"

you can easily see the below bug message.

"JBD2 unexpected failure: jbd2_journal_revoke: !buffer_revoked(bh);"

This means that an already revoked buffer is erroneously revoked again
and it is caused by doing revoke for the buffer at the wrong position
in ext4_free_blocks(). We need to re-position the buffer revoke
procedure for an unspecified buffer after checking the cluster boundary
for bigalloc option. If not, some part of the cluster can be doubly
revoked.

Signed-off-by: Daeho Jeong <daeho.jeong@samsung.com>
fs/ext4/mballoc.c

index 6794ff47cfafa141a37f182b4528f2b66d8b3616..61eaf74dca3794b122a9e782e87c568ac99a8a65 100644 (file)
@@ -4687,22 +4687,11 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
        ext4_debug("freeing block %llu\n", block);
        trace_ext4_free_blocks(inode, block, count, flags);
 
-       if (flags & EXT4_FREE_BLOCKS_FORGET) {
-               struct buffer_head *tbh = bh;
-               int i;
-
-               BUG_ON(bh && (count > 1));
+       if (bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
+               BUG_ON(count > 1);
 
-               for (i = 0; i < count; i++) {
-                       cond_resched();
-                       if (!bh)
-                               tbh = sb_find_get_block(inode->i_sb,
-                                                       block + i);
-                       if (!tbh)
-                               continue;
-                       ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
-                                   inode, tbh, block + i);
-               }
+               ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
+                           inode, bh, block);
        }
 
        /*
@@ -4747,6 +4736,19 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
                        count += sbi->s_cluster_ratio - overflow;
        }
 
+       if (!bh && (flags & EXT4_FREE_BLOCKS_FORGET)) {
+               int i;
+
+               for (i = 0; i < count; i++) {
+                       cond_resched();
+                       bh = sb_find_get_block(inode->i_sb, block + i);
+                       if (!bh)
+                               continue;
+                       ext4_forget(handle, flags & EXT4_FREE_BLOCKS_METADATA,
+                                   inode, bh, block + i);
+               }
+       }
+
 do_more:
        overflow = 0;
        ext4_get_group_no_and_offset(sb, block, &block_group, &bit);