Commit d62b1b87a7d1c3a21dddabed4251763090be3182

Authored by Chris Mason
Committed by Linus Torvalds
1 parent ec191574b9

[PATCH] resierfs: fix reiserfs_invalidatepage race against data=ordered

After a transaction has closed but before it has finished commit, there is
a window where data=ordered mode requires invalidatepage to pin pages
instead of freeing them.  This patch fixes a race between the
invalidatepage checks and data=ordered writeback, and it also adds a check
to the reiserfs write_ordered_buffers routines to write any anonymous
buffers that were dirtied after its first writeback loop.

That bug works like this:

proc1: transaction closes and a new one starts
proc1: write_ordered_buffers starts processing data=ordered list
proc1: buffer A is cleaned and written
proc2: buffer A is dirtied by another process
proc2: File is truncated to zero, page A goes through invalidatepage
proc2: reiserfs_invalidatepage sees dirty buffer A with reiserfs
       journal head, pins it
proc1: write_ordered_buffers frees the journal head on buffer A

At this point, buffer A stays dirty forever

Signed-off-by: Chris Mason <mason@suse.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 2 changed files with 16 additions and 1 deletions Side-by-side Diff

... ... @@ -2743,6 +2743,7 @@
2743 2743 int ret = 1;
2744 2744 struct reiserfs_journal *j = SB_JOURNAL(inode->i_sb);
2745 2745  
  2746 + lock_buffer(bh);
2746 2747 spin_lock(&j->j_dirty_buffers_lock);
2747 2748 if (!buffer_mapped(bh)) {
2748 2749 goto free_jh;
... ... @@ -2758,7 +2759,7 @@
2758 2759 if (buffer_journaled(bh) || buffer_journal_dirty(bh)) {
2759 2760 ret = 0;
2760 2761 }
2761   - } else if (buffer_dirty(bh) || buffer_locked(bh)) {
  2762 + } else if (buffer_dirty(bh)) {
2762 2763 struct reiserfs_journal_list *jl;
2763 2764 struct reiserfs_jh *jh = bh->b_private;
2764 2765  
... ... @@ -2784,6 +2785,7 @@
2784 2785 reiserfs_free_jh(bh);
2785 2786 }
2786 2787 spin_unlock(&j->j_dirty_buffers_lock);
  2788 + unlock_buffer(bh);
2787 2789 return ret;
2788 2790 }
2789 2791  
fs/reiserfs/journal.c
... ... @@ -877,6 +877,19 @@
877 877 if (!buffer_uptodate(bh)) {
878 878 ret = -EIO;
879 879 }
  880 + /* ugly interaction with invalidatepage here.
  881 + * reiserfs_invalidate_page will pin any buffer that has a valid
  882 + * journal head from an older transaction. If someone else sets
  883 + * our buffer dirty after we write it in the first loop, and
  884 + * then someone truncates the page away, nobody will ever write
  885 + * the buffer. We're safe if we write the page one last time
  886 + * after freeing the journal header.
  887 + */
  888 + if (buffer_dirty(bh) && unlikely(bh->b_page->mapping == NULL)) {
  889 + spin_unlock(lock);
  890 + ll_rw_block(WRITE, 1, &bh);
  891 + spin_lock(lock);
  892 + }
880 893 put_bh(bh);
881 894 cond_resched_lock(lock);
882 895 }