[GFS2] Fix lock ordering bug in page fault path
Mmapped files were able to trigger a lock ordering bug. Private
maps do not need to take the glock so early on. Shared maps do
unfortunately, however we can get around that by adding a flag
into the flags for the struct gfs2_file. This only works because
we are taking an exclusive lock at this point, so we know that
nobody else can be racing with us.
Fixes Red Hat bugzilla: #201196
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c
index fca69f1..bdd4d6b4 100644
--- a/fs/gfs2/ops_address.c
+++ b/fs/gfs2/ops_address.c
@@ -237,14 +237,22 @@
struct gfs2_sbd *sdp = GFS2_SB(page->mapping->host);
struct gfs2_holder gh;
int error;
+ int do_unlock = 0;
if (likely(file != &gfs2_internal_file_sentinal)) {
+ if (file) {
+ struct gfs2_file *gf = file->private_data;
+ if (test_bit(GFF_EXLOCK, &gf->f_flags))
+ goto skip_lock;
+ }
gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME|GL_AOP, &gh);
+ do_unlock = 1;
error = gfs2_glock_nq_m_atime(1, &gh);
if (unlikely(error))
goto out_unlock;
}
+skip_lock:
if (gfs2_is_stuffed(ip)) {
error = stuffed_readpage(ip, page);
unlock_page(page);
@@ -262,7 +270,7 @@
return error;
out_unlock:
unlock_page(page);
- if (file != &gfs2_internal_file_sentinal)
+ if (do_unlock)
gfs2_holder_uninit(&gh);
goto out;
}
@@ -291,17 +299,24 @@
struct gfs2_holder gh;
unsigned page_idx;
int ret;
+ int do_unlock = 0;
if (likely(file != &gfs2_internal_file_sentinal)) {
+ if (file) {
+ struct gfs2_file *gf = file->private_data;
+ if (test_bit(GFF_EXLOCK, &gf->f_flags))
+ goto skip_lock;
+ }
gfs2_holder_init(ip->i_gl, LM_ST_SHARED,
LM_FLAG_TRY_1CB|GL_ATIME|GL_AOP, &gh);
+ do_unlock = 1;
ret = gfs2_glock_nq_m_atime(1, &gh);
if (ret == GLR_TRYFAILED)
goto out_noerror;
if (unlikely(ret))
goto out_unlock;
}
-
+skip_lock:
if (gfs2_is_stuffed(ip)) {
struct pagevec lru_pvec;
pagevec_init(&lru_pvec, 0);
@@ -326,7 +341,7 @@
ret = mpage_readpages(mapping, pages, nr_pages, gfs2_get_block);
}
- if (likely(file != &gfs2_internal_file_sentinal)) {
+ if (do_unlock) {
gfs2_glock_dq_m(1, &gh);
gfs2_holder_uninit(&gh);
}
@@ -344,7 +359,7 @@
unlock_page(page);
page_cache_release(page);
}
- if (likely(file != &gfs2_internal_file_sentinal))
+ if (do_unlock)
gfs2_holder_uninit(&gh);
goto out;
}