diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-04-15 16:56:39 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-04-19 16:53:05 -0400 |
commit | c9d8f89d9816c1d16ada492aa547a4d692508c0d (patch) | |
tree | d85339019cff084c11d4fceaf194fc5e34588d61 /fs/nfs/write.c | |
parent | fdd1e74c89fe39259a29c494209abad63ff76f82 (diff) |
NFS: Ensure that the write code cleans up properly when rpc_run_task() fails
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 67 |
1 files changed, 43 insertions, 24 deletions
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; | |||
48 | static mempool_t *nfs_wdata_mempool; | 48 | static mempool_t *nfs_wdata_mempool; |
49 | static mempool_t *nfs_commit_mempool; | 49 | static mempool_t *nfs_commit_mempool; |
50 | 50 | ||
51 | struct nfs_write_data *nfs_commit_alloc(void) | 51 | struct 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) { | 986 | static 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) | |||
1012 | out: | 1018 | out: |
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 | ||
1017 | static const struct rpc_call_ops nfs_write_partial_ops = { | 1024 | static 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 = { | |||
1029 | static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) | 1036 | static 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 | |||
1043 | static 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 | ||
1075 | static const struct rpc_call_ops nfs_write_full_ops = { | 1087 | static 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) |
1163 | void nfs_commit_release(void *data) | 1175 | void 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) | |||
1261 | static void nfs_commit_done(struct rpc_task *task, void *calldata) | 1273 | static 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 | |||
1285 | static 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 | ||
1312 | static const struct rpc_call_ops nfs_commit_ops = { | 1331 | static const struct rpc_call_ops nfs_commit_ops = { |