| From 39c04153fda8c32e85b51c96eb5511a326ad7609 Mon Sep 17 00:00:00 2001 |
| From: Theodore Ts'o <tytso@mit.edu> |
| Date: Mon, 1 Jul 2013 08:12:40 -0400 |
| Subject: jbd2: fix theoretical race in jbd2__journal_restart |
| |
| From: Theodore Ts'o <tytso@mit.edu> |
| |
| commit 39c04153fda8c32e85b51c96eb5511a326ad7609 upstream. |
| |
| Once we decrement transaction->t_updates, if this is the last handle |
| holding the transaction from closing, and once we release the |
| t_handle_lock spinlock, it's possible for the transaction to commit |
| and be released. In practice with normal kernels, this probably won't |
| happen, since the commit happens in a separate kernel thread and it's |
| unlikely this could all happen within the space of a few CPU cycles. |
| |
| On the other hand, with a real-time kernel, this could potentially |
| happen, so save the tid found in transaction->t_tid before we release |
| t_handle_lock. It would require an insane configuration, such as one |
| where the jbd2 thread was set to a very high real-time priority, |
| perhaps because a high priority real-time thread is trying to read or |
| write to a file system. But some people who use real-time kernels |
| have been known to do insane things, including controlling |
| laser-wielding industrial robots. :-) |
| |
| Signed-off-by: "Theodore Ts'o" <tytso@mit.edu> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/jbd2/transaction.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/fs/jbd2/transaction.c |
| +++ b/fs/jbd2/transaction.c |
| @@ -518,10 +518,10 @@ int jbd2__journal_restart(handle_t *hand |
| &transaction->t_outstanding_credits); |
| if (atomic_dec_and_test(&transaction->t_updates)) |
| wake_up(&journal->j_wait_updates); |
| + tid = transaction->t_tid; |
| spin_unlock(&transaction->t_handle_lock); |
| |
| jbd_debug(2, "restarting handle %p\n", handle); |
| - tid = transaction->t_tid; |
| need_to_start = !tid_geq(journal->j_commit_request, tid); |
| read_unlock(&journal->j_state_lock); |
| if (need_to_start) |