From f88a39f7e2e5b2b0d15119e6390da7ef9b7fe6e1 Mon Sep 17 00:00:00 2001 From: Jinshan Xiong Date: Tue, 17 Apr 2012 21:40:24 -0700 Subject: [PATCH] LU-1320 llite: fix a race between readpage and releasepage This is a race between page stealing and readpage. If a just read page is stolen, readpage will find the page is not uptodate, this makes it panic so -EIO is returned to the reading application. Signed-off-by: Jinshan Xiong Change-Id: Ib16d12d3bc3cc8c0545aa27f0836e4fd89c3a809 Reviewed-on: http://review.whamcloud.com/2591 Reviewed-by: Oleg Drokin Tested-by: Hudson Reviewed-by: Bobi Jam Tested-by: Maloo --- lustre/llite/rw26.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/lustre/llite/rw26.c b/lustre/llite/rw26.c index 3f8d583..4577bc8 100644 --- a/lustre/llite/rw26.c +++ b/lustre/llite/rw26.c @@ -139,14 +139,51 @@ static void ll_invalidatepage(struct page *page, unsigned long offset) #else #define RELEASEPAGE_ARG_TYPE gfp_t #endif -static int ll_releasepage(struct page *page, RELEASEPAGE_ARG_TYPE gfp_mask) +static int ll_releasepage(struct page *vmpage, RELEASEPAGE_ARG_TYPE gfp_mask) { - void *cookie; + struct cl_env_nest nest; + struct lu_env *env; + struct cl_object *obj; + struct cl_page *page; + struct address_space *mapping; + int result; - cookie = cl_env_reenter(); - ll_invalidatepage(page, 0); - cl_env_reexit(cookie); - return 1; + LASSERT(PageLocked(vmpage)); + if (PageWriteback(vmpage) || PageDirty(vmpage)) + return 0; + + mapping = vmpage->mapping; + if (mapping == NULL) + return 1; + + obj = ll_i2info(mapping->host)->lli_clob; + if (obj == NULL) + return 1; + + /* 1 for page allocator, 1 for cl_page and 1 for page cache */ + if (page_count(vmpage) > 3) + return 0; + + /* TODO: determine what gfp should be used by @gfp_mask. */ + env = cl_env_nested_get(&nest); + if (IS_ERR(env)) + /* If we can't allocate an env we won't call cl_page_put() + * later on which further means it's impossible to drop + * page refcount by cl_page, so ask kernel to not free + * this page. */ + return 0; + + page = cl_vmpage_page(vmpage, obj); + result = page == NULL; + if (page != NULL) { + if (cfs_atomic_read(&page->cp_ref) == 1) { + result = 1; + cl_page_delete(env, page); + } + cl_page_put(env, page); + } + cl_env_nested_put(&nest, env); + return result; } static int ll_set_page_dirty(struct page *vmpage) -- 1.8.3.1