diff options
author | Idan Kedar <idank@tonian.com> | 2012-08-02 04:47:10 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-08-02 17:38:54 -0400 |
commit | 8554116e17eef055d9dd58a94b3427cb2ad1c317 (patch) | |
tree | 75cb529c3ab86a496536a285542d79d064284d48 /fs/nfs/nfs4proc.c | |
parent | 3dd4765fce04c0b4af1e0bc4c0b10f906f95fabc (diff) |
pnfs: defer release of pages in layoutget
we have encountered a bug whereby reading a lot of files (copying
fedora's /bin) from a pNFS mount and hitting Ctrl+C in the middle caused
a general protection fault in xdr_shrink_bufhead. this function is
called when decoding the response from LAYOUTGET. the decoding is done
by a worker thread, and the caller of LAYOUTGET waits for the worker
thread to complete.
hitting Ctrl+C caused the synchronous wait to end and the next thing the
caller does is to free the pages, so when the worker thread calls
xdr_shrink_bufhead, the pages are gone. therefore, the cleanup of these
pages has been moved to nfs4_layoutget_release.
Signed-off-by: Idan Kedar <idank@tonian.com>
Signed-off-by: Benny Halevy <bhalevy@tonian.com>
Cc: stable@vger.kernel.org
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index a99a8d948721..6a78d49da5c1 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -6223,11 +6223,58 @@ static void nfs4_layoutget_done(struct rpc_task *task, void *calldata) | |||
6223 | dprintk("<-- %s\n", __func__); | 6223 | dprintk("<-- %s\n", __func__); |
6224 | } | 6224 | } |
6225 | 6225 | ||
6226 | static size_t max_response_pages(struct nfs_server *server) | ||
6227 | { | ||
6228 | u32 max_resp_sz = server->nfs_client->cl_session->fc_attrs.max_resp_sz; | ||
6229 | return nfs_page_array_len(0, max_resp_sz); | ||
6230 | } | ||
6231 | |||
6232 | static void nfs4_free_pages(struct page **pages, size_t size) | ||
6233 | { | ||
6234 | int i; | ||
6235 | |||
6236 | if (!pages) | ||
6237 | return; | ||
6238 | |||
6239 | for (i = 0; i < size; i++) { | ||
6240 | if (!pages[i]) | ||
6241 | break; | ||
6242 | __free_page(pages[i]); | ||
6243 | } | ||
6244 | kfree(pages); | ||
6245 | } | ||
6246 | |||
6247 | static struct page **nfs4_alloc_pages(size_t size, gfp_t gfp_flags) | ||
6248 | { | ||
6249 | struct page **pages; | ||
6250 | int i; | ||
6251 | |||
6252 | pages = kcalloc(size, sizeof(struct page *), gfp_flags); | ||
6253 | if (!pages) { | ||
6254 | dprintk("%s: can't alloc array of %zu pages\n", __func__, size); | ||
6255 | return NULL; | ||
6256 | } | ||
6257 | |||
6258 | for (i = 0; i < size; i++) { | ||
6259 | pages[i] = alloc_page(gfp_flags); | ||
6260 | if (!pages[i]) { | ||
6261 | dprintk("%s: failed to allocate page\n", __func__); | ||
6262 | nfs4_free_pages(pages, size); | ||
6263 | return NULL; | ||
6264 | } | ||
6265 | } | ||
6266 | |||
6267 | return pages; | ||
6268 | } | ||
6269 | |||
6226 | static void nfs4_layoutget_release(void *calldata) | 6270 | static void nfs4_layoutget_release(void *calldata) |
6227 | { | 6271 | { |
6228 | struct nfs4_layoutget *lgp = calldata; | 6272 | struct nfs4_layoutget *lgp = calldata; |
6273 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); | ||
6274 | size_t max_pages = max_response_pages(server); | ||
6229 | 6275 | ||
6230 | dprintk("--> %s\n", __func__); | 6276 | dprintk("--> %s\n", __func__); |
6277 | nfs4_free_pages(lgp->args.layout.pages, max_pages); | ||
6231 | put_nfs_open_context(lgp->args.ctx); | 6278 | put_nfs_open_context(lgp->args.ctx); |
6232 | kfree(calldata); | 6279 | kfree(calldata); |
6233 | dprintk("<-- %s\n", __func__); | 6280 | dprintk("<-- %s\n", __func__); |
@@ -6239,9 +6286,10 @@ static const struct rpc_call_ops nfs4_layoutget_call_ops = { | |||
6239 | .rpc_release = nfs4_layoutget_release, | 6286 | .rpc_release = nfs4_layoutget_release, |
6240 | }; | 6287 | }; |
6241 | 6288 | ||
6242 | int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | 6289 | int nfs4_proc_layoutget(struct nfs4_layoutget *lgp, gfp_t gfp_flags) |
6243 | { | 6290 | { |
6244 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); | 6291 | struct nfs_server *server = NFS_SERVER(lgp->args.inode); |
6292 | size_t max_pages = max_response_pages(server); | ||
6245 | struct rpc_task *task; | 6293 | struct rpc_task *task; |
6246 | struct rpc_message msg = { | 6294 | struct rpc_message msg = { |
6247 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], | 6295 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_LAYOUTGET], |
@@ -6259,6 +6307,13 @@ int nfs4_proc_layoutget(struct nfs4_layoutget *lgp) | |||
6259 | 6307 | ||
6260 | dprintk("--> %s\n", __func__); | 6308 | dprintk("--> %s\n", __func__); |
6261 | 6309 | ||
6310 | lgp->args.layout.pages = nfs4_alloc_pages(max_pages, gfp_flags); | ||
6311 | if (!lgp->args.layout.pages) { | ||
6312 | nfs4_layoutget_release(lgp); | ||
6313 | return -ENOMEM; | ||
6314 | } | ||
6315 | lgp->args.layout.pglen = max_pages * PAGE_SIZE; | ||
6316 | |||
6262 | lgp->res.layoutp = &lgp->args.layout; | 6317 | lgp->res.layoutp = &lgp->args.layout; |
6263 | lgp->res.seq_res.sr_slot = NULL; | 6318 | lgp->res.seq_res.sr_slot = NULL; |
6264 | nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0); | 6319 | nfs41_init_sequence(&lgp->args.seq_args, &lgp->res.seq_res, 0); |