Add cc: stable@vger.kernel tags and bugzilla tags
[ext4-patch-queue.git] / fix-jbd2-txn-extension-in-ext4_ext_truncate_extend_restart
blob0db5e90892ac0acd7a293fb314efaefbd3d5dbda
1 ext4: fix jbd2 handle extension in ext4_ext_truncate_extend_restart()
3 The function jbd2_journal_extend() takes as its argument the number of
4 new credits to be added to the handle.  We weren't taking into account
5 the currently unused handle credits; worse, we would try to extend the
6 handle by N credits when it had N credits available.
8 In the case where jbd2_journal_extend() fails because the transaction
9 is too large, when jbd2_journal_restart() gets called, the N credits
10 owned by the handle gets returned to the transaction, and the
11 transaction commit is asynchronously requested, and then
12 start_this_handle() will be able to successfully attach the handle to
13 the current transaction since the required credits are now available.
15 This is mostly harmless, but since ext4_ext_truncate_extend_restart()
16 returns EAGAIN, the truncate machinery will once again try to call
17 ext4_ext_truncate_extend_restart(), which will do the above sequence
18 over and over again until the transaction has committed.
20 This was found while I was debugging a lockup in caused by running
21 xfstests generic/074 in the data=journal case.  I'm still not sure why
22 we ended up looping forever, which suggests there may still be another
23 bug hiding in the transaction accounting machinery, but this commit
24 prevents us from looping in the first place.
26 Signed-off-by: Theodore Ts'o <tytso@mit.edu>
27 ---
28  fs/ext4/extents.c | 9 +++++++--
29  1 file changed, 7 insertions(+), 2 deletions(-)
31 diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
32 index 95bf467..ba2be53 100644
33 --- a/fs/ext4/extents.c
34 +++ b/fs/ext4/extents.c
35 @@ -120,9 +120,14 @@ static int ext4_ext_truncate_extend_restart(handle_t *handle,
37         if (!ext4_handle_valid(handle))
38                 return 0;
39 -       if (handle->h_buffer_credits > needed)
40 +       if (handle->h_buffer_credits >= needed)
41                 return 0;
42 -       err = ext4_journal_extend(handle, needed);
43 +       /*
44 +        * If we need to extend the journal get a few extra blocks
45 +        * while we're at it for efficiency's sake.
46 +        */
47 +       needed += 3;
48 +       err = ext4_journal_extend(handle, needed - handle->h_buffer_credits);
49         if (err <= 0)
50                 return err;
51         err = ext4_truncate_restart_trans(handle, inode, needed);