diff options
author | Steven Whitehouse <swhiteho@redhat.com> | 2008-02-22 11:07:18 -0500 |
---|---|---|
committer | Steven Whitehouse <swhiteho@redhat.com> | 2008-03-31 05:41:12 -0400 |
commit | 7afd88d9166a752b52517648bcbe923e05d393fc (patch) | |
tree | 2fb945189e3cb1be7ad007088f8ec86e9f67ece6 /fs/gfs2/ops_address.c | |
parent | 60b779cfc1fa52034a996ee12a23b62d32e86000 (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>
Diffstat (limited to 'fs/gfs2/ops_address.c')
-rw-r--r-- | fs/gfs2/ops_address.c | 27 |
1 files changed, 15 insertions, 12 deletions
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) | |||
508 | static int gfs2_readpage(struct file *file, struct page *page) | 508 | static 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); |
522 | out: | 528 | out: |
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)) { |