diff options
author | Fred Isaman <iisaman@netapp.com> | 2012-04-20 14:47:44 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-04-27 14:10:37 -0400 |
commit | cd841605f7a721878d8a2d1362484723d8abf569 (patch) | |
tree | b5c37db575cd545a183577249909e042fe38d646 /fs/nfs/write.c | |
parent | b5542849764aa56fd3f05c0041195b637b9d2ac2 (diff) |
NFS: create common nfs_pgio_header for both read and write
In order to avoid duplicating all the data in nfs_read_data whenever we
split it up into multiple RPC calls (either due to a short read result
or due to rsize < PAGE_SIZE), we split out the bits that are the same
per RPC call into a separate "header" structure.
The goal this patch moves towards is to have a single header
refcounted by several rpc_data structures. Thus, want to always refer
from rpc_data to the header, and not the other way. This patch comes
close to that ideal, but the directio code currently needs some
special casing, isolated in the nfs_direct_[read_write]hdr_release()
functions. This will be dealt with in a future patch.
Signed-off-by: Fred Isaman <iisaman@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/write.c')
-rw-r--r-- | fs/nfs/write.c | 104 |
1 files changed, 58 insertions, 46 deletions
diff --git a/fs/nfs/write.c b/fs/nfs/write.c index 76735dd8c9a7..dbb5c0a613b8 100644 --- a/fs/nfs/write.c +++ b/fs/nfs/write.c | |||
@@ -69,19 +69,24 @@ void nfs_commit_free(struct nfs_commit_data *p) | |||
69 | } | 69 | } |
70 | EXPORT_SYMBOL_GPL(nfs_commit_free); | 70 | EXPORT_SYMBOL_GPL(nfs_commit_free); |
71 | 71 | ||
72 | struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) | 72 | struct nfs_write_header *nfs_writehdr_alloc(unsigned int pagecount) |
73 | { | 73 | { |
74 | struct nfs_write_data *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); | 74 | struct nfs_write_header *p = mempool_alloc(nfs_wdata_mempool, GFP_NOFS); |
75 | 75 | ||
76 | if (p) { | 76 | if (p) { |
77 | struct nfs_pgio_header *hdr = &p->header; | ||
78 | struct nfs_write_data *data = &p->rpc_data; | ||
79 | |||
77 | memset(p, 0, sizeof(*p)); | 80 | memset(p, 0, sizeof(*p)); |
78 | INIT_LIST_HEAD(&p->pages); | 81 | INIT_LIST_HEAD(&hdr->pages); |
79 | p->npages = pagecount; | 82 | INIT_LIST_HEAD(&data->list); |
80 | if (pagecount <= ARRAY_SIZE(p->page_array)) | 83 | data->npages = pagecount; |
81 | p->pagevec = p->page_array; | 84 | data->header = hdr; |
85 | if (pagecount <= ARRAY_SIZE(data->page_array)) | ||
86 | data->pagevec = data->page_array; | ||
82 | else { | 87 | else { |
83 | p->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); | 88 | data->pagevec = kcalloc(pagecount, sizeof(struct page *), GFP_NOFS); |
84 | if (!p->pagevec) { | 89 | if (!data->pagevec) { |
85 | mempool_free(p, nfs_wdata_mempool); | 90 | mempool_free(p, nfs_wdata_mempool); |
86 | p = NULL; | 91 | p = NULL; |
87 | } | 92 | } |
@@ -90,17 +95,18 @@ struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) | |||
90 | return p; | 95 | return p; |
91 | } | 96 | } |
92 | 97 | ||
93 | void nfs_writedata_free(struct nfs_write_data *p) | 98 | void nfs_writehdr_free(struct nfs_pgio_header *hdr) |
94 | { | 99 | { |
95 | if (p && (p->pagevec != &p->page_array[0])) | 100 | struct nfs_write_header *whdr = container_of(hdr, struct nfs_write_header, header); |
96 | kfree(p->pagevec); | 101 | mempool_free(whdr, nfs_wdata_mempool); |
97 | mempool_free(p, nfs_wdata_mempool); | ||
98 | } | 102 | } |
99 | 103 | ||
100 | void nfs_writedata_release(struct nfs_write_data *wdata) | 104 | void nfs_writedata_release(struct nfs_write_data *wdata) |
101 | { | 105 | { |
102 | put_nfs_open_context(wdata->args.context); | 106 | put_nfs_open_context(wdata->args.context); |
103 | nfs_writedata_free(wdata); | 107 | if (wdata->pagevec != wdata->page_array) |
108 | kfree(wdata->pagevec); | ||
109 | nfs_writehdr_free(wdata->header); | ||
104 | } | 110 | } |
105 | 111 | ||
106 | static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) | 112 | static void nfs_context_set_write_error(struct nfs_open_context *ctx, int error) |
@@ -507,9 +513,8 @@ static inline | |||
507 | int nfs_write_need_commit(struct nfs_write_data *data) | 513 | int nfs_write_need_commit(struct nfs_write_data *data) |
508 | { | 514 | { |
509 | if (data->verf.committed == NFS_DATA_SYNC) | 515 | if (data->verf.committed == NFS_DATA_SYNC) |
510 | return data->lseg == NULL; | 516 | return data->header->lseg == NULL; |
511 | else | 517 | return data->verf.committed != NFS_FILE_SYNC; |
512 | return data->verf.committed != NFS_FILE_SYNC; | ||
513 | } | 518 | } |
514 | 519 | ||
515 | static inline | 520 | static inline |
@@ -517,7 +522,7 @@ int nfs_reschedule_unstable_write(struct nfs_page *req, | |||
517 | struct nfs_write_data *data) | 522 | struct nfs_write_data *data) |
518 | { | 523 | { |
519 | if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { | 524 | if (test_and_clear_bit(PG_NEED_COMMIT, &req->wb_flags)) { |
520 | nfs_mark_request_commit(req, data->lseg); | 525 | nfs_mark_request_commit(req, data->header->lseg); |
521 | return 1; | 526 | return 1; |
522 | } | 527 | } |
523 | if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) { | 528 | if (test_and_clear_bit(PG_NEED_RESCHED, &req->wb_flags)) { |
@@ -841,13 +846,13 @@ int nfs_initiate_write(struct rpc_clnt *clnt, | |||
841 | const struct rpc_call_ops *call_ops, | 846 | const struct rpc_call_ops *call_ops, |
842 | int how) | 847 | int how) |
843 | { | 848 | { |
844 | struct inode *inode = data->inode; | 849 | struct inode *inode = data->header->inode; |
845 | int priority = flush_task_priority(how); | 850 | int priority = flush_task_priority(how); |
846 | struct rpc_task *task; | 851 | struct rpc_task *task; |
847 | struct rpc_message msg = { | 852 | struct rpc_message msg = { |
848 | .rpc_argp = &data->args, | 853 | .rpc_argp = &data->args, |
849 | .rpc_resp = &data->res, | 854 | .rpc_resp = &data->res, |
850 | .rpc_cred = data->cred, | 855 | .rpc_cred = data->header->cred, |
851 | }; | 856 | }; |
852 | struct rpc_task_setup task_setup_data = { | 857 | struct rpc_task_setup task_setup_data = { |
853 | .rpc_client = clnt, | 858 | .rpc_client = clnt, |
@@ -896,14 +901,15 @@ static void nfs_write_rpcsetup(struct nfs_page *req, | |||
896 | unsigned int count, unsigned int offset, | 901 | unsigned int count, unsigned int offset, |
897 | int how) | 902 | int how) |
898 | { | 903 | { |
904 | struct nfs_pgio_header *hdr = data->header; | ||
899 | struct inode *inode = req->wb_context->dentry->d_inode; | 905 | struct inode *inode = req->wb_context->dentry->d_inode; |
900 | 906 | ||
901 | /* Set up the RPC argument and reply structs | 907 | /* Set up the RPC argument and reply structs |
902 | * NB: take care not to mess about with data->commit et al. */ | 908 | * NB: take care not to mess about with data->commit et al. */ |
903 | 909 | ||
904 | data->req = req; | 910 | hdr->req = req; |
905 | data->inode = inode = req->wb_context->dentry->d_inode; | 911 | hdr->inode = inode = req->wb_context->dentry->d_inode; |
906 | data->cred = req->wb_context->cred; | 912 | hdr->cred = req->wb_context->cred; |
907 | 913 | ||
908 | data->args.fh = NFS_FH(inode); | 914 | data->args.fh = NFS_FH(inode); |
909 | data->args.offset = req_offset(req) + offset; | 915 | data->args.offset = req_offset(req) + offset; |
@@ -935,7 +941,7 @@ static int nfs_do_write(struct nfs_write_data *data, | |||
935 | const struct rpc_call_ops *call_ops, | 941 | const struct rpc_call_ops *call_ops, |
936 | int how) | 942 | int how) |
937 | { | 943 | { |
938 | struct inode *inode = data->args.context->dentry->d_inode; | 944 | struct inode *inode = data->header->inode; |
939 | 945 | ||
940 | return nfs_initiate_write(NFS_CLIENT(inode), data, call_ops, how); | 946 | return nfs_initiate_write(NFS_CLIENT(inode), data, call_ops, how); |
941 | } | 947 | } |
@@ -981,6 +987,7 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head | |||
981 | { | 987 | { |
982 | struct nfs_page *req = nfs_list_entry(desc->pg_list.next); | 988 | struct nfs_page *req = nfs_list_entry(desc->pg_list.next); |
983 | struct page *page = req->wb_page; | 989 | struct page *page = req->wb_page; |
990 | struct nfs_write_header *whdr; | ||
984 | struct nfs_write_data *data; | 991 | struct nfs_write_data *data; |
985 | size_t wsize = desc->pg_bsize, nbytes; | 992 | size_t wsize = desc->pg_bsize, nbytes; |
986 | unsigned int offset; | 993 | unsigned int offset; |
@@ -1000,9 +1007,10 @@ static int nfs_flush_multi(struct nfs_pageio_descriptor *desc, struct list_head | |||
1000 | do { | 1007 | do { |
1001 | size_t len = min(nbytes, wsize); | 1008 | size_t len = min(nbytes, wsize); |
1002 | 1009 | ||
1003 | data = nfs_writedata_alloc(1); | 1010 | whdr = nfs_writehdr_alloc(1); |
1004 | if (!data) | 1011 | if (!whdr) |
1005 | goto out_bad; | 1012 | goto out_bad; |
1013 | data = &whdr->rpc_data; | ||
1006 | data->pagevec[0] = page; | 1014 | data->pagevec[0] = page; |
1007 | nfs_write_rpcsetup(req, data, len, offset, desc->pg_ioflags); | 1015 | nfs_write_rpcsetup(req, data, len, offset, desc->pg_ioflags); |
1008 | list_add(&data->list, res); | 1016 | list_add(&data->list, res); |
@@ -1036,13 +1044,14 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *r | |||
1036 | { | 1044 | { |
1037 | struct nfs_page *req; | 1045 | struct nfs_page *req; |
1038 | struct page **pages; | 1046 | struct page **pages; |
1047 | struct nfs_write_header *whdr; | ||
1039 | struct nfs_write_data *data; | 1048 | struct nfs_write_data *data; |
1040 | struct list_head *head = &desc->pg_list; | 1049 | struct list_head *head = &desc->pg_list; |
1041 | int ret = 0; | 1050 | int ret = 0; |
1042 | 1051 | ||
1043 | data = nfs_writedata_alloc(nfs_page_array_len(desc->pg_base, | 1052 | whdr = nfs_writehdr_alloc(nfs_page_array_len(desc->pg_base, |
1044 | desc->pg_count)); | 1053 | desc->pg_count)); |
1045 | if (!data) { | 1054 | if (!whdr) { |
1046 | while (!list_empty(head)) { | 1055 | while (!list_empty(head)) { |
1047 | req = nfs_list_entry(head->next); | 1056 | req = nfs_list_entry(head->next); |
1048 | nfs_list_remove_request(req); | 1057 | nfs_list_remove_request(req); |
@@ -1051,14 +1060,15 @@ static int nfs_flush_one(struct nfs_pageio_descriptor *desc, struct list_head *r | |||
1051 | ret = -ENOMEM; | 1060 | ret = -ENOMEM; |
1052 | goto out; | 1061 | goto out; |
1053 | } | 1062 | } |
1063 | data = &whdr->rpc_data; | ||
1054 | pages = data->pagevec; | 1064 | pages = data->pagevec; |
1055 | while (!list_empty(head)) { | 1065 | while (!list_empty(head)) { |
1056 | req = nfs_list_entry(head->next); | 1066 | req = nfs_list_entry(head->next); |
1057 | nfs_list_remove_request(req); | 1067 | nfs_list_remove_request(req); |
1058 | nfs_list_add_request(req, &data->pages); | 1068 | nfs_list_add_request(req, &whdr->header.pages); |
1059 | *pages++ = req->wb_page; | 1069 | *pages++ = req->wb_page; |
1060 | } | 1070 | } |
1061 | req = nfs_list_entry(data->pages.next); | 1071 | req = nfs_list_entry(whdr->header.pages.next); |
1062 | 1072 | ||
1063 | if ((desc->pg_ioflags & FLUSH_COND_STABLE) && | 1073 | if ((desc->pg_ioflags & FLUSH_COND_STABLE) && |
1064 | (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit)) | 1074 | (desc->pg_moreio || NFS_I(desc->pg_inode)->ncommit)) |
@@ -1126,10 +1136,11 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) | |||
1126 | 1136 | ||
1127 | dprintk("NFS: %5u write(%s/%lld %d@%lld)", | 1137 | dprintk("NFS: %5u write(%s/%lld %d@%lld)", |
1128 | task->tk_pid, | 1138 | task->tk_pid, |
1129 | data->req->wb_context->dentry->d_inode->i_sb->s_id, | 1139 | data->header->inode->i_sb->s_id, |
1130 | (long long) | 1140 | (long long) |
1131 | NFS_FILEID(data->req->wb_context->dentry->d_inode), | 1141 | NFS_FILEID(data->header->inode), |
1132 | data->req->wb_bytes, (long long)req_offset(data->req)); | 1142 | data->header->req->wb_bytes, |
1143 | (long long)req_offset(data->header->req)); | ||
1133 | 1144 | ||
1134 | nfs_writeback_done(task, data); | 1145 | nfs_writeback_done(task, data); |
1135 | } | 1146 | } |
@@ -1137,7 +1148,7 @@ static void nfs_writeback_done_partial(struct rpc_task *task, void *calldata) | |||
1137 | static void nfs_writeback_release_partial(void *calldata) | 1148 | static void nfs_writeback_release_partial(void *calldata) |
1138 | { | 1149 | { |
1139 | struct nfs_write_data *data = calldata; | 1150 | struct nfs_write_data *data = calldata; |
1140 | struct nfs_page *req = data->req; | 1151 | struct nfs_page *req = data->header->req; |
1141 | struct page *page = req->wb_page; | 1152 | struct page *page = req->wb_page; |
1142 | int status = data->task.tk_status; | 1153 | int status = data->task.tk_status; |
1143 | 1154 | ||
@@ -1169,13 +1180,13 @@ static void nfs_writeback_release_partial(void *calldata) | |||
1169 | out: | 1180 | out: |
1170 | if (atomic_dec_and_test(&req->wb_complete)) | 1181 | if (atomic_dec_and_test(&req->wb_complete)) |
1171 | nfs_writepage_release(req, data); | 1182 | nfs_writepage_release(req, data); |
1172 | nfs_writedata_release(calldata); | 1183 | nfs_writedata_release(data); |
1173 | } | 1184 | } |
1174 | 1185 | ||
1175 | void nfs_write_prepare(struct rpc_task *task, void *calldata) | 1186 | void nfs_write_prepare(struct rpc_task *task, void *calldata) |
1176 | { | 1187 | { |
1177 | struct nfs_write_data *data = calldata; | 1188 | struct nfs_write_data *data = calldata; |
1178 | NFS_PROTO(data->inode)->write_rpc_prepare(task, data); | 1189 | NFS_PROTO(data->header->inode)->write_rpc_prepare(task, data); |
1179 | } | 1190 | } |
1180 | 1191 | ||
1181 | void nfs_commit_prepare(struct rpc_task *task, void *calldata) | 1192 | void nfs_commit_prepare(struct rpc_task *task, void *calldata) |
@@ -1208,11 +1219,12 @@ static void nfs_writeback_done_full(struct rpc_task *task, void *calldata) | |||
1208 | static void nfs_writeback_release_full(void *calldata) | 1219 | static void nfs_writeback_release_full(void *calldata) |
1209 | { | 1220 | { |
1210 | struct nfs_write_data *data = calldata; | 1221 | struct nfs_write_data *data = calldata; |
1222 | struct nfs_pgio_header *hdr = data->header; | ||
1211 | int status = data->task.tk_status; | 1223 | int status = data->task.tk_status; |
1212 | 1224 | ||
1213 | /* Update attributes as result of writeback. */ | 1225 | /* Update attributes as result of writeback. */ |
1214 | while (!list_empty(&data->pages)) { | 1226 | while (!list_empty(&hdr->pages)) { |
1215 | struct nfs_page *req = nfs_list_entry(data->pages.next); | 1227 | struct nfs_page *req = nfs_list_entry(hdr->pages.next); |
1216 | struct page *page = req->wb_page; | 1228 | struct page *page = req->wb_page; |
1217 | 1229 | ||
1218 | nfs_list_remove_request(req); | 1230 | nfs_list_remove_request(req); |
@@ -1233,7 +1245,7 @@ static void nfs_writeback_release_full(void *calldata) | |||
1233 | 1245 | ||
1234 | if (nfs_write_need_commit(data)) { | 1246 | if (nfs_write_need_commit(data)) { |
1235 | memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); | 1247 | memcpy(&req->wb_verf, &data->verf, sizeof(req->wb_verf)); |
1236 | nfs_mark_request_commit(req, data->lseg); | 1248 | nfs_mark_request_commit(req, hdr->lseg); |
1237 | dprintk(" marked for commit\n"); | 1249 | dprintk(" marked for commit\n"); |
1238 | goto next; | 1250 | goto next; |
1239 | } | 1251 | } |
@@ -1244,7 +1256,7 @@ remove_request: | |||
1244 | nfs_unlock_request(req); | 1256 | nfs_unlock_request(req); |
1245 | nfs_end_page_writeback(page); | 1257 | nfs_end_page_writeback(page); |
1246 | } | 1258 | } |
1247 | nfs_writedata_release(calldata); | 1259 | nfs_writedata_release(data); |
1248 | } | 1260 | } |
1249 | 1261 | ||
1250 | static const struct rpc_call_ops nfs_write_full_ops = { | 1262 | static const struct rpc_call_ops nfs_write_full_ops = { |
@@ -1261,6 +1273,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1261 | { | 1273 | { |
1262 | struct nfs_writeargs *argp = &data->args; | 1274 | struct nfs_writeargs *argp = &data->args; |
1263 | struct nfs_writeres *resp = &data->res; | 1275 | struct nfs_writeres *resp = &data->res; |
1276 | struct inode *inode = data->header->inode; | ||
1264 | int status; | 1277 | int status; |
1265 | 1278 | ||
1266 | dprintk("NFS: %5u nfs_writeback_done (status %d)\n", | 1279 | dprintk("NFS: %5u nfs_writeback_done (status %d)\n", |
@@ -1273,10 +1286,10 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1273 | * another writer had changed the file, but some applications | 1286 | * another writer had changed the file, but some applications |
1274 | * depend on tighter cache coherency when writing. | 1287 | * depend on tighter cache coherency when writing. |
1275 | */ | 1288 | */ |
1276 | status = NFS_PROTO(data->inode)->write_done(task, data); | 1289 | status = NFS_PROTO(inode)->write_done(task, data); |
1277 | if (status != 0) | 1290 | if (status != 0) |
1278 | return; | 1291 | return; |
1279 | nfs_add_stats(data->inode, NFSIOS_SERVERWRITTENBYTES, resp->count); | 1292 | nfs_add_stats(inode, NFSIOS_SERVERWRITTENBYTES, resp->count); |
1280 | 1293 | ||
1281 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) | 1294 | #if defined(CONFIG_NFS_V3) || defined(CONFIG_NFS_V4) |
1282 | if (resp->verf->committed < argp->stable && task->tk_status >= 0) { | 1295 | if (resp->verf->committed < argp->stable && task->tk_status >= 0) { |
@@ -1294,7 +1307,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1294 | if (time_before(complain, jiffies)) { | 1307 | if (time_before(complain, jiffies)) { |
1295 | dprintk("NFS: faulty NFS server %s:" | 1308 | dprintk("NFS: faulty NFS server %s:" |
1296 | " (committed = %d) != (stable = %d)\n", | 1309 | " (committed = %d) != (stable = %d)\n", |
1297 | NFS_SERVER(data->inode)->nfs_client->cl_hostname, | 1310 | NFS_SERVER(inode)->nfs_client->cl_hostname, |
1298 | resp->verf->committed, argp->stable); | 1311 | resp->verf->committed, argp->stable); |
1299 | complain = jiffies + 300 * HZ; | 1312 | complain = jiffies + 300 * HZ; |
1300 | } | 1313 | } |
@@ -1304,7 +1317,7 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1304 | if (task->tk_status >= 0 && resp->count < argp->count) { | 1317 | if (task->tk_status >= 0 && resp->count < argp->count) { |
1305 | static unsigned long complain; | 1318 | static unsigned long complain; |
1306 | 1319 | ||
1307 | nfs_inc_stats(data->inode, NFSIOS_SHORTWRITE); | 1320 | nfs_inc_stats(inode, NFSIOS_SHORTWRITE); |
1308 | 1321 | ||
1309 | /* Has the server at least made some progress? */ | 1322 | /* Has the server at least made some progress? */ |
1310 | if (resp->count != 0) { | 1323 | if (resp->count != 0) { |
@@ -1333,7 +1346,6 @@ void nfs_writeback_done(struct rpc_task *task, struct nfs_write_data *data) | |||
1333 | /* Can't do anything about it except throw an error. */ | 1346 | /* Can't do anything about it except throw an error. */ |
1334 | task->tk_status = -EIO; | 1347 | task->tk_status = -EIO; |
1335 | } | 1348 | } |
1336 | return; | ||
1337 | } | 1349 | } |
1338 | 1350 | ||
1339 | 1351 | ||
@@ -1745,7 +1757,7 @@ int nfs_migrate_page(struct address_space *mapping, struct page *newpage, | |||
1745 | int __init nfs_init_writepagecache(void) | 1757 | int __init nfs_init_writepagecache(void) |
1746 | { | 1758 | { |
1747 | nfs_wdata_cachep = kmem_cache_create("nfs_write_data", | 1759 | nfs_wdata_cachep = kmem_cache_create("nfs_write_data", |
1748 | sizeof(struct nfs_write_data), | 1760 | sizeof(struct nfs_write_header), |
1749 | 0, SLAB_HWCACHE_ALIGN, | 1761 | 0, SLAB_HWCACHE_ALIGN, |
1750 | NULL); | 1762 | NULL); |
1751 | if (nfs_wdata_cachep == NULL) | 1763 | if (nfs_wdata_cachep == NULL) |