diff options
author | Bob Peterson <rpeterso@redhat.com> | 2020-08-19 08:55:18 -0500 |
---|---|---|
committer | Andreas Gruenbacher <agruenba@redhat.com> | 2020-10-15 14:29:03 +0200 |
commit | 6302d6f43e3550495747ffbaeb75a46ea8c15b32 (patch) | |
tree | b7709bfa3bac61f211b9aea4540b905f93c9fc97 /fs | |
parent | 249ffe18c68ee74dcfb51a16e151b45ee07e1c10 (diff) | |
download | linux-stable-6302d6f43e3550495747ffbaeb75a46ea8c15b32.tar.gz linux-stable-6302d6f43e3550495747ffbaeb75a46ea8c15b32.tar.bz2 linux-stable-6302d6f43e3550495747ffbaeb75a46ea8c15b32.zip |
gfs2: Only set PageChecked if we have a transaction
With jdata writes, we frequently got into situations where gfs2 deadlocked
because of this calling sequence:
gfs2_ail1_start
gfs2_ail1_flush - for every tr on the sd_ail1_list:
gfs2_ail1_start_one - for every bd on the tr's tr_ail1_list:
generic_writepages
write_cache_pages passing __writepage()
calls clear_page_dirty_for_io which calls set_page_dirty:
which calls jdata_set_page_dirty which sets PageChecked.
__writepage() calls
mapping->a_ops->writepage AKA gfs2_jdata_writepage
However, gfs2_jdata_writepage checks if PageChecked is set, and if so, it
ignores the write and redirties the page. The problem is that write_cache_pages
calls clear_page_dirty_for_io, which often calls set_page_dirty(). See comments
in page-writeback.c starting with "Yes, Virginia". If it's jdata,
set_page_dirty will call jdata_set_page_dirty which will set PageChecked.
That causes a conflict because it makes it look like the page has been
redirtied by another writer, in which case we need to skip writing it and
redirty the page. That ends up in a deadlock because it isn't a "real" writer
and nothing will ever clear PageChecked.
If we do have a real writer, it will have started a transaction. So this
patch checks if a transaction is in use, and if not, it skips setting
PageChecked. That way, the page will be dirtied, cleaned, and written
appropriately.
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/gfs2/aops.c | 3 |
1 files changed, 2 insertions, 1 deletions
diff --git a/fs/gfs2/aops.c b/fs/gfs2/aops.c index 1e7ab519bfea..9cd2ecad07db 100644 --- a/fs/gfs2/aops.c +++ b/fs/gfs2/aops.c @@ -623,7 +623,8 @@ out: static int jdata_set_page_dirty(struct page *page) { - SetPageChecked(page); + if (current->journal_info) + SetPageChecked(page); return __set_page_dirty_buffers(page); } |