aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2008-04-15 16:56:39 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2008-04-19 16:53:05 -0400
commitc9d8f89d9816c1d16ada492aa547a4d692508c0d (patch)
treed85339019cff084c11d4fceaf194fc5e34588d61
parentfdd1e74c89fe39259a29c494209abad63ff76f82 (diff)
NFS: Ensure that the write code cleans up properly when rpc_run_task() fails
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--fs/nfs/direct.c50
-rw-r--r--fs/nfs/write.c67
-rw-r--r--include/linux/nfs_fs.h4
3 files changed, 73 insertions, 48 deletions
diff --git a/fs/nfs/direct.c b/fs/nfs/direct.c
index 9d9085b93a32..abf8e0286e35 100644
--- a/fs/nfs/direct.c
+++ b/fs/nfs/direct.c
@@ -508,27 +508,34 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
508static void nfs_direct_commit_result(struct rpc_task *task, void *calldata) 508static void nfs_direct_commit_result(struct rpc_task *task, void *calldata)
509{ 509{
510 struct nfs_write_data *data = calldata; 510 struct nfs_write_data *data = calldata;
511 struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
512 511
513 /* Call the NFS version-specific code */ 512 /* Call the NFS version-specific code */
514 if (NFS_PROTO(data->inode)->commit_done(task, data) != 0) 513 NFS_PROTO(data->inode)->commit_done(task, data);
515 return; 514}
516 if (unlikely(task->tk_status < 0)) { 515
516static void nfs_direct_commit_release(void *calldata)
517{
518 struct nfs_write_data *data = calldata;
519 struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
520 int status = data->task.tk_status;
521
522 if (status < 0) {
517 dprintk("NFS: %5u commit failed with error %d.\n", 523 dprintk("NFS: %5u commit failed with error %d.\n",
518 task->tk_pid, task->tk_status); 524 data->task.tk_pid, status);
519 dreq->flags = NFS_ODIRECT_RESCHED_WRITES; 525 dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
520 } else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) { 526 } else if (memcmp(&dreq->verf, &data->verf, sizeof(data->verf))) {
521 dprintk("NFS: %5u commit verify failed\n", task->tk_pid); 527 dprintk("NFS: %5u commit verify failed\n", data->task.tk_pid);
522 dreq->flags = NFS_ODIRECT_RESCHED_WRITES; 528 dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
523 } 529 }
524 530
525 dprintk("NFS: %5u commit returned %d\n", task->tk_pid, task->tk_status); 531 dprintk("NFS: %5u commit returned %d\n", data->task.tk_pid, status);
526 nfs_direct_write_complete(dreq, data->inode); 532 nfs_direct_write_complete(dreq, data->inode);
533 nfs_commitdata_release(calldata);
527} 534}
528 535
529static const struct rpc_call_ops nfs_commit_direct_ops = { 536static const struct rpc_call_ops nfs_commit_direct_ops = {
530 .rpc_call_done = nfs_direct_commit_result, 537 .rpc_call_done = nfs_direct_commit_result,
531 .rpc_release = nfs_commit_release, 538 .rpc_release = nfs_direct_commit_release,
532}; 539};
533 540
534static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq) 541static void nfs_direct_commit_schedule(struct nfs_direct_req *dreq)
@@ -596,7 +603,7 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
596 603
597static void nfs_alloc_commit_data(struct nfs_direct_req *dreq) 604static void nfs_alloc_commit_data(struct nfs_direct_req *dreq)
598{ 605{
599 dreq->commit_data = nfs_commit_alloc(); 606 dreq->commit_data = nfs_commitdata_alloc();
600 if (dreq->commit_data != NULL) 607 if (dreq->commit_data != NULL)
601 dreq->commit_data->req = (struct nfs_page *) dreq; 608 dreq->commit_data->req = (struct nfs_page *) dreq;
602} 609}
@@ -617,11 +624,20 @@ static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode
617static void nfs_direct_write_result(struct rpc_task *task, void *calldata) 624static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
618{ 625{
619 struct nfs_write_data *data = calldata; 626 struct nfs_write_data *data = calldata;
620 struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
621 int status = task->tk_status;
622 627
623 if (nfs_writeback_done(task, data) != 0) 628 if (nfs_writeback_done(task, data) != 0)
624 return; 629 return;
630}
631
632/*
633 * NB: Return the value of the first error return code. Subsequent
634 * errors after the first one are ignored.
635 */
636static void nfs_direct_write_release(void *calldata)
637{
638 struct nfs_write_data *data = calldata;
639 struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
640 int status = data->task.tk_status;
625 641
626 spin_lock(&dreq->lock); 642 spin_lock(&dreq->lock);
627 643
@@ -643,23 +659,13 @@ static void nfs_direct_write_result(struct rpc_task *task, void *calldata)
643 break; 659 break;
644 case NFS_ODIRECT_DO_COMMIT: 660 case NFS_ODIRECT_DO_COMMIT:
645 if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) { 661 if (memcmp(&dreq->verf, &data->verf, sizeof(dreq->verf))) {
646 dprintk("NFS: %5u write verify failed\n", task->tk_pid); 662 dprintk("NFS: %5u write verify failed\n", data->task.tk_pid);
647 dreq->flags = NFS_ODIRECT_RESCHED_WRITES; 663 dreq->flags = NFS_ODIRECT_RESCHED_WRITES;
648 } 664 }
649 } 665 }
650 } 666 }
651out_unlock: 667out_unlock:
652 spin_unlock(&dreq->lock); 668 spin_unlock(&dreq->lock);
653}
654
655/*
656 * NB: Return the value of the first error return code. Subsequent
657 * errors after the first one are ignored.
658 */
659static void nfs_direct_write_release(void *calldata)
660{
661 struct nfs_write_data *data = calldata;
662 struct nfs_direct_req *dreq = (struct nfs_direct_req *) data->req;
663 669
664 if (put_dreq(dreq)) 670 if (put_dreq(dreq))
665 nfs_direct_write_complete(dreq, data->inode); 671 nfs_direct_write_complete(dreq, data->inode);
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index 997b42aa3702..31681bb5e4c1 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -48,7 +48,7 @@ static struct kmem_cache *nfs_wdata_cachep;
48static mempool_t *nfs_wdata_mempool; 48static mempool_t *nfs_wdata_mempool;
49static mempool_t *nfs_commit_mempool; 49static mempool_t *nfs_commit_mempool;
50 50
51struct nfs_write_data *nfs_commit_alloc(void) 51struct nfs_write_data *nfs_commitdata_alloc(void)
52{ 52{
53 struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS); 53 struct nfs_write_data *p = mempool_alloc(nfs_commit_mempool, GFP_NOFS);
54 54
@@ -973,7 +973,6 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
973{ 973{
974 struct nfs_write_data *data = calldata; 974 struct nfs_write_data *data = calldata;
975 struct nfs_page *req = data->req; 975 struct nfs_page *req = data->req;
976 struct page *page = req->wb_page;
977 976
978 dprintk("NFS: write (%s/%Ld %d@%Ld)", 977 dprintk("NFS: write (%s/%Ld %d@%Ld)",
979 req->wb_context->path.dentry->d_inode->i_sb->s_id, 978 req->wb_context->path.dentry->d_inode->i_sb->s_id,
@@ -981,13 +980,20 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
981 req->wb_bytes, 980 req->wb_bytes,
982 (long long)req_offset(req)); 981 (long long)req_offset(req));
983 982
984 if (nfs_writeback_done(task, data) != 0) 983 nfs_writeback_done(task, data);
985 return; 984}
986 985
987 if (task->tk_status < 0) { 986static void nfs_writeback_release_partial(void *calldata)
987{
988 struct nfs_write_data *data = calldata;
989 struct nfs_page *req = data->req;
990 struct page *page = req->wb_page;
991 int status = data->task.tk_status;
992
993 if (status < 0) {
988 nfs_set_pageerror(page); 994 nfs_set_pageerror(page);
989 nfs_context_set_write_error(req->wb_context, task->tk_status); 995 nfs_context_set_write_error(req->wb_context, status);
990 dprintk(", error = %d\n", task->tk_status); 996 dprintk(", error = %d\n", status);
991 goto out; 997 goto out;
992 } 998 }
993 999
@@ -1012,11 +1018,12 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata)
1012out: 1018out:
1013 if (atomic_dec_and_test(&req->wb_complete)) 1019 if (atomic_dec_and_test(&req->wb_complete))
1014 nfs_writepage_release(req); 1020 nfs_writepage_release(req);
1021 nfs_writedata_release(calldata);
1015} 1022}
1016 1023
1017static const struct rpc_call_ops nfs_write_partial_ops = { 1024static const struct rpc_call_ops nfs_write_partial_ops = {
1018 .rpc_call_done = nfs_writeback_done_partial, 1025 .rpc_call_done = nfs_writeback_done_partial,
1019 .rpc_release = nfs_writedata_release, 1026 .rpc_release = nfs_writeback_release_partial,
1020}; 1027};
1021 1028
1022/* 1029/*
@@ -1029,17 +1036,21 @@ static const struct rpc_call_ops nfs_write_partial_ops = {
1029static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) 1036static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
1030{ 1037{
1031 struct nfs_write_data *data = calldata; 1038 struct nfs_write_data *data = calldata;
1032 struct nfs_page *req;
1033 struct page *page;
1034 1039
1035 if (nfs_writeback_done(task, data) != 0) 1040 nfs_writeback_done(task, data);
1036 return; 1041}
1042
1043static void nfs_writeback_release_full(void *calldata)
1044{
1045 struct nfs_write_data *data = calldata;
1046 int status = data->task.tk_status;
1037 1047
1038 /* Update attributes as result of writeback. */ 1048 /* Update attributes as result of writeback. */
1039 while (!list_empty(&data->pages)) { 1049 while (!list_empty(&data->pages)) {
1040 req = nfs_list_entry(data->pages.next); 1050 struct nfs_page *req = nfs_list_entry(data->pages.next);
1051 struct page *page = req->wb_page;
1052
1041 nfs_list_remove_request(req); 1053 nfs_list_remove_request(req);
1042 page = req->wb_page;
1043 1054
1044 dprintk("NFS: write (%s/%Ld %d@%Ld)", 1055 dprintk("NFS: write (%s/%Ld %d@%Ld)",
1045 req->wb_context->path.dentry->d_inode->i_sb->s_id, 1056 req->wb_context->path.dentry->d_inode->i_sb->s_id,
@@ -1047,10 +1058,10 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata)
1047 req->wb_bytes, 1058 req->wb_bytes,
1048 (long long)req_offset(req)); 1059 (long long)req_offset(req));
1049 1060
1050 if (task->tk_status < 0) { 1061 if (status < 0) {
1051 nfs_set_pageerror(page); 1062 nfs_set_pageerror(page);
1052 nfs_context_set_write_error(req->wb_context, task->tk_status); 1063 nfs_context_set_write_error(req->wb_context, status);
1053 dprintk(", error = %d\n", task->tk_status); 1064 dprintk(", error = %d\n", status);
1054 goto remove_request; 1065 goto remove_request;
1055 } 1066 }
1056 1067
@@ -1070,11 +1081,12 @@ remove_request:
1070 next: 1081 next:
1071 nfs_clear_page_tag_locked(req); 1082 nfs_clear_page_tag_locked(req);
1072 } 1083 }
1084 nfs_writedata_release(calldata);
1073} 1085}
1074 1086
1075static const struct rpc_call_ops nfs_write_full_ops = { 1087static const struct rpc_call_ops nfs_write_full_ops = {
1076 .rpc_call_done = nfs_writeback_done_full, 1088 .rpc_call_done = nfs_writeback_done_full,
1077 .rpc_release = nfs_writedata_release, 1089 .rpc_release = nfs_writeback_release_full,
1078}; 1090};
1079 1091
1080 1092
@@ -1160,7 +1172,7 @@ int nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data)
1160 1172
1161 1173
1162#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) 1174#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
1163void nfs_commit_release(void *data) 1175void nfs_commitdata_release(void *data)
1164{ 1176{
1165 struct nfs_write_data *wdata = data; 1177 struct nfs_write_data *wdata = data;
1166 1178
@@ -1233,7 +1245,7 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
1233 struct nfs_write_data *data; 1245 struct nfs_write_data *data;
1234 struct nfs_page *req; 1246 struct nfs_page *req;
1235 1247
1236 data = nfs_commit_alloc(); 1248 data = nfs_commitdata_alloc();
1237 1249
1238 if (!data) 1250 if (!data)
1239 goto out_bad; 1251 goto out_bad;
@@ -1261,7 +1273,6 @@ nfs_commit_list(struct inode *inode, struct list_head *head, int how)
1261static void nfs_commit_done(struct rpc_task *task, void *calldata) 1273static void nfs_commit_done(struct rpc_task *task, void *calldata)
1262{ 1274{
1263 struct nfs_write_data *data = calldata; 1275 struct nfs_write_data *data = calldata;
1264 struct nfs_page *req;
1265 1276
1266 dprintk("NFS: %5u nfs_commit_done (status %d)\n", 1277 dprintk("NFS: %5u nfs_commit_done (status %d)\n",
1267 task->tk_pid, task->tk_status); 1278 task->tk_pid, task->tk_status);
@@ -1269,6 +1280,13 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
1269 /* Call the NFS version-specific code */ 1280 /* Call the NFS version-specific code */
1270 if (NFS_PROTO(data->inode)->commit_done(task, data) != 0) 1281 if (NFS_PROTO(data->inode)->commit_done(task, data) != 0)
1271 return; 1282 return;
1283}
1284
1285static void nfs_commit_release(void *calldata)
1286{
1287 struct nfs_write_data *data = calldata;
1288 struct nfs_page *req;
1289 int status = data->task.tk_status;
1272 1290
1273 while (!list_empty(&data->pages)) { 1291 while (!list_empty(&data->pages)) {
1274 req = nfs_list_entry(data->pages.next); 1292 req = nfs_list_entry(data->pages.next);
@@ -1283,10 +1301,10 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
1283 (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode), 1301 (long long)NFS_FILEID(req->wb_context->path.dentry->d_inode),
1284 req->wb_bytes, 1302 req->wb_bytes,
1285 (long long)req_offset(req)); 1303 (long long)req_offset(req));
1286 if (task->tk_status < 0) { 1304 if (status < 0) {
1287 nfs_context_set_write_error(req->wb_context, task->tk_status); 1305 nfs_context_set_write_error(req->wb_context, status);
1288 nfs_inode_remove_request(req); 1306 nfs_inode_remove_request(req);
1289 dprintk(", error = %d\n", task->tk_status); 1307 dprintk(", error = %d\n", status);
1290 goto next; 1308 goto next;
1291 } 1309 }
1292 1310
@@ -1307,6 +1325,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
1307 next: 1325 next:
1308 nfs_clear_page_tag_locked(req); 1326 nfs_clear_page_tag_locked(req);
1309 } 1327 }
1328 nfs_commitdata_release(calldata);
1310} 1329}
1311 1330
1312static const struct rpc_call_ops nfs_commit_ops = { 1331static const struct rpc_call_ops nfs_commit_ops = {
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index f4a0e4c218df..7f0602c87712 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -466,9 +466,9 @@ extern int nfs_wb_page(struct inode *inode, struct page* page);
466extern int nfs_wb_page_cancel(struct inode *inode, struct page* page); 466extern int nfs_wb_page_cancel(struct inode *inode, struct page* page);
467#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) 467#if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4)
468extern int nfs_commit_inode(struct inode *, int); 468extern int nfs_commit_inode(struct inode *, int);
469extern struct nfs_write_data *nfs_commit_alloc(void); 469extern struct nfs_write_data *nfs_commitdata_alloc(void);
470extern void nfs_commit_free(struct nfs_write_data *wdata); 470extern void nfs_commit_free(struct nfs_write_data *wdata);
471extern void nfs_commit_release(void *wdata); 471extern void nfs_commitdata_release(void *wdata);
472#else 472#else
473static inline int 473static inline int
474nfs_commit_inode(struct inode *inode, int how) 474nfs_commit_inode(struct inode *inode, int how)