aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Whitehouse <swhiteho@redhat.com>2008-02-22 11:07:18 -0500
committerSteven Whitehouse <swhiteho@redhat.com>2008-03-31 05:41:12 -0400
commit7afd88d9166a752b52517648bcbe923e05d393fc (patch)
tree2fb945189e3cb1be7ad007088f8ec86e9f67ece6
parent60b779cfc1fa52034a996ee12a23b62d32e86000 (diff)
[GFS2] Fix a page lock / glock deadlock
We've previously been using a "try lock" in readpage on the basis that it would prevent deadlocks due to the inverted lock ordering (our normal lock ordering is glock first and then page lock). Unfortunately tests have shown that this isn't enough. If the glock has a demote request queued such that run_queue() in the glock code tries to do a demote when its called under readpage then it will try and write out all the dirty pages which requires locking them. This then deadlocks with the page locked by readpage. The solution is to always require two calls into readpage. The first unlocks the page, gets the glock and returns AOP_TRUNCATED_PAGE, the second does the actual readpage and unlocks the glock & page as required. Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
-rw-r--r--fs/gfs2/glock.h13
-rw-r--r--fs/gfs2/inode.c2
-rw-r--r--fs/gfs2/ops_address.c27
-rw-r--r--fs/gfs2/ops_dentry.c4
-rw-r--r--fs/gfs2/ops_inode.c4
5 files changed, 26 insertions, 24 deletions
diff --git a/fs/gfs2/glock.h b/fs/gfs2/glock.h
index ace5770760ce..cdad3e6f8150 100644
--- a/fs/gfs2/glock.h
+++ b/fs/gfs2/glock.h
@@ -32,24 +32,23 @@
32#define GLR_TRYFAILED 13 32#define GLR_TRYFAILED 13
33#define GLR_CANCELED 14 33#define GLR_CANCELED 14
34 34
35static inline int gfs2_glock_is_locked_by_me(struct gfs2_glock *gl) 35static inline struct gfs2_holder *gfs2_glock_is_locked_by_me(struct gfs2_glock *gl)
36{ 36{
37 struct gfs2_holder *gh; 37 struct gfs2_holder *gh;
38 int locked = 0;
39 struct pid *pid; 38 struct pid *pid;
40 39
41 /* Look in glock's list of holders for one with current task as owner */ 40 /* Look in glock's list of holders for one with current task as owner */
42 spin_lock(&gl->gl_spin); 41 spin_lock(&gl->gl_spin);
43 pid = task_pid(current); 42 pid = task_pid(current);
44 list_for_each_entry(gh, &gl->gl_holders, gh_list) { 43 list_for_each_entry(gh, &gl->gl_holders, gh_list) {
45 if (gh->gh_owner_pid == pid) { 44 if (gh->gh_owner_pid == pid)
46 locked = 1; 45 goto out;
47 break;
48 }
49 } 46 }
47 gh = NULL;
48out:
50 spin_unlock(&gl->gl_spin); 49 spin_unlock(&gl->gl_spin);
51 50
52 return locked; 51 return gh;
53} 52}
54 53
55static inline int gfs2_glock_is_held_excl(struct gfs2_glock *gl) 54static inline int gfs2_glock_is_held_excl(struct gfs2_glock *gl)
diff --git a/fs/gfs2/inode.c b/fs/gfs2/inode.c
index 5f50dd53bf63..810ff023fb14 100644
--- a/fs/gfs2/inode.c
+++ b/fs/gfs2/inode.c
@@ -493,7 +493,7 @@ struct inode *gfs2_lookupi(struct inode *dir, const struct qstr *name,
493 return dir; 493 return dir;
494 } 494 }
495 495
496 if (gfs2_glock_is_locked_by_me(dip->i_gl) == 0) { 496 if (gfs2_glock_is_locked_by_me(dip->i_gl) == NULL) {
497 error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); 497 error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
498 if (error) 498 if (error)
499 return ERR_PTR(error); 499 return ERR_PTR(error);
diff --git a/fs/gfs2/ops_address.c b/fs/gfs2/ops_address.c
index 7523999afc53..fbb4a6aa1583 100644
--- a/fs/gfs2/ops_address.c
+++ b/fs/gfs2/ops_address.c
@@ -508,23 +508,26 @@ static int __gfs2_readpage(void *file, struct page *page)
508static int gfs2_readpage(struct file *file, struct page *page) 508static int gfs2_readpage(struct file *file, struct page *page)
509{ 509{
510 struct gfs2_inode *ip = GFS2_I(page->mapping->host); 510 struct gfs2_inode *ip = GFS2_I(page->mapping->host);
511 struct gfs2_holder gh; 511 struct gfs2_holder *gh;
512 int error; 512 int error;
513 513
514 gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME|LM_FLAG_TRY_1CB, &gh); 514 gh = gfs2_glock_is_locked_by_me(ip->i_gl);
515 error = gfs2_glock_nq_atime(&gh); 515 if (!gh) {
516 if (unlikely(error)) { 516 gh = kmalloc(sizeof(struct gfs2_holder), GFP_NOFS);
517 if (!gh)
518 return -ENOBUFS;
519 gfs2_holder_init(ip->i_gl, LM_ST_SHARED, GL_ATIME, gh);
517 unlock_page(page); 520 unlock_page(page);
518 goto out; 521 error = gfs2_glock_nq_atime(gh);
522 if (likely(error != 0))
523 goto out;
524 return AOP_TRUNCATED_PAGE;
519 } 525 }
520 error = __gfs2_readpage(file, page); 526 error = __gfs2_readpage(file, page);
521 gfs2_glock_dq(&gh); 527 gfs2_glock_dq(gh);
522out: 528out:
523 gfs2_holder_uninit(&gh); 529 gfs2_holder_uninit(gh);
524 if (error == GLR_TRYFAILED) { 530 kfree(gh);
525 yield();
526 return AOP_TRUNCATED_PAGE;
527 }
528 return error; 531 return error;
529} 532}
530 533
@@ -826,7 +829,7 @@ static int gfs2_write_end(struct file *file, struct address_space *mapping,
826 unsigned int to = from + len; 829 unsigned int to = from + len;
827 int ret; 830 int ret;
828 831
829 BUG_ON(gfs2_glock_is_locked_by_me(ip->i_gl) == 0); 832 BUG_ON(gfs2_glock_is_locked_by_me(ip->i_gl) == NULL);
830 833
831 ret = gfs2_meta_inode_buffer(ip, &dibh); 834 ret = gfs2_meta_inode_buffer(ip, &dibh);
832 if (unlikely(ret)) { 835 if (unlikely(ret)) {
diff --git a/fs/gfs2/ops_dentry.c b/fs/gfs2/ops_dentry.c
index 793e334d098e..4a5e676b4420 100644
--- a/fs/gfs2/ops_dentry.c
+++ b/fs/gfs2/ops_dentry.c
@@ -43,7 +43,7 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd)
43 struct gfs2_holder d_gh; 43 struct gfs2_holder d_gh;
44 struct gfs2_inode *ip = NULL; 44 struct gfs2_inode *ip = NULL;
45 int error; 45 int error;
46 int had_lock=0; 46 int had_lock = 0;
47 47
48 if (inode) { 48 if (inode) {
49 if (is_bad_inode(inode)) 49 if (is_bad_inode(inode))
@@ -54,7 +54,7 @@ static int gfs2_drevalidate(struct dentry *dentry, struct nameidata *nd)
54 if (sdp->sd_args.ar_localcaching) 54 if (sdp->sd_args.ar_localcaching)
55 goto valid; 55 goto valid;
56 56
57 had_lock = gfs2_glock_is_locked_by_me(dip->i_gl); 57 had_lock = (gfs2_glock_is_locked_by_me(dip->i_gl) != NULL);
58 if (!had_lock) { 58 if (!had_lock) {
59 error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh); 59 error = gfs2_glock_nq_init(dip->i_gl, LM_ST_SHARED, 0, &d_gh);
60 if (error) 60 if (error)
diff --git a/fs/gfs2/ops_inode.c b/fs/gfs2/ops_inode.c
index 301c94596678..af7097a514c1 100644
--- a/fs/gfs2/ops_inode.c
+++ b/fs/gfs2/ops_inode.c
@@ -898,7 +898,7 @@ static int gfs2_permission(struct inode *inode, int mask, struct nameidata *nd)
898 int error; 898 int error;
899 int unlock = 0; 899 int unlock = 0;
900 900
901 if (gfs2_glock_is_locked_by_me(ip->i_gl) == 0) { 901 if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
902 error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh); 902 error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &i_gh);
903 if (error) 903 if (error)
904 return error; 904 return error;
@@ -1065,7 +1065,7 @@ static int gfs2_getattr(struct vfsmount *mnt, struct dentry *dentry,
1065 int error; 1065 int error;
1066 int unlock = 0; 1066 int unlock = 0;
1067 1067
1068 if (gfs2_glock_is_locked_by_me(ip->i_gl) == 0) { 1068 if (gfs2_glock_is_locked_by_me(ip->i_gl) == NULL) {
1069 error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh); 1069 error = gfs2_glock_nq_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
1070 if (error) 1070 if (error)
1071 return error; 1071 return error;