aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/Makefile2
-rw-r--r--fs/nfs/filelayout/filelayout.c291
-rw-r--r--fs/nfs/filelayout/filelayout.h11
-rw-r--r--fs/nfs/filelayout/filelayoutdev.c2
-rw-r--r--fs/nfs/pnfs.h23
-rw-r--r--fs/nfs/pnfs_nfs.c291
6 files changed, 330 insertions, 290 deletions
diff --git a/fs/nfs/Makefile b/fs/nfs/Makefile
index 04cb830fa09f..23abffa8a4ce 100644
--- a/fs/nfs/Makefile
+++ b/fs/nfs/Makefile
@@ -27,7 +27,7 @@ nfsv4-y := nfs4proc.o nfs4xdr.o nfs4state.o nfs4renewd.o nfs4super.o nfs4file.o
27 dns_resolve.o nfs4trace.o 27 dns_resolve.o nfs4trace.o
28nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o 28nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o
29nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o 29nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o
30nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o 30nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o
31nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o 31nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o
32 32
33obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/ 33obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/
diff --git a/fs/nfs/filelayout/filelayout.c b/fs/nfs/filelayout/filelayout.c
index 7afb52f6a25a..bc36ed350a68 100644
--- a/fs/nfs/filelayout/filelayout.c
+++ b/fs/nfs/filelayout/filelayout.c
@@ -118,13 +118,6 @@ static void filelayout_reset_read(struct nfs_pgio_header *hdr)
118 } 118 }
119} 119}
120 120
121static void filelayout_fenceme(struct inode *inode, struct pnfs_layout_hdr *lo)
122{
123 if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
124 return;
125 pnfs_return_layout(inode);
126}
127
128static int filelayout_async_handle_error(struct rpc_task *task, 121static int filelayout_async_handle_error(struct rpc_task *task,
129 struct nfs4_state *state, 122 struct nfs4_state *state,
130 struct nfs_client *clp, 123 struct nfs_client *clp,
@@ -339,16 +332,6 @@ static void filelayout_read_count_stats(struct rpc_task *task, void *data)
339 rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics); 332 rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics);
340} 333}
341 334
342static void filelayout_read_release(void *data)
343{
344 struct nfs_pgio_header *hdr = data;
345 struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
346
347 filelayout_fenceme(lo->plh_inode, lo);
348 nfs_put_client(hdr->ds_clp);
349 hdr->mds_ops->rpc_release(data);
350}
351
352static int filelayout_write_done_cb(struct rpc_task *task, 335static int filelayout_write_done_cb(struct rpc_task *task,
353 struct nfs_pgio_header *hdr) 336 struct nfs_pgio_header *hdr)
354{ 337{
@@ -371,17 +354,6 @@ static int filelayout_write_done_cb(struct rpc_task *task,
371 return 0; 354 return 0;
372} 355}
373 356
374/* Fake up some data that will cause nfs_commit_release to retry the writes. */
375static void prepare_to_resend_writes(struct nfs_commit_data *data)
376{
377 struct nfs_page *first = nfs_list_entry(data->pages.next);
378
379 data->task.tk_status = 0;
380 memcpy(&data->verf.verifier, &first->wb_verf,
381 sizeof(data->verf.verifier));
382 data->verf.verifier.data[0]++; /* ensure verifier mismatch */
383}
384
385static int filelayout_commit_done_cb(struct rpc_task *task, 357static int filelayout_commit_done_cb(struct rpc_task *task,
386 struct nfs_commit_data *data) 358 struct nfs_commit_data *data)
387{ 359{
@@ -393,7 +365,7 @@ static int filelayout_commit_done_cb(struct rpc_task *task,
393 365
394 switch (err) { 366 switch (err) {
395 case -NFS4ERR_RESET_TO_MDS: 367 case -NFS4ERR_RESET_TO_MDS:
396 prepare_to_resend_writes(data); 368 pnfs_generic_prepare_to_resend_writes(data);
397 return -EAGAIN; 369 return -EAGAIN;
398 case -EAGAIN: 370 case -EAGAIN:
399 rpc_restart_call_prepare(task); 371 rpc_restart_call_prepare(task);
@@ -451,16 +423,6 @@ static void filelayout_write_count_stats(struct rpc_task *task, void *data)
451 rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics); 423 rpc_count_iostats(task, NFS_SERVER(hdr->inode)->client->cl_metrics);
452} 424}
453 425
454static void filelayout_write_release(void *data)
455{
456 struct nfs_pgio_header *hdr = data;
457 struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
458
459 filelayout_fenceme(lo->plh_inode, lo);
460 nfs_put_client(hdr->ds_clp);
461 hdr->mds_ops->rpc_release(data);
462}
463
464static void filelayout_commit_prepare(struct rpc_task *task, void *data) 426static void filelayout_commit_prepare(struct rpc_task *task, void *data)
465{ 427{
466 struct nfs_commit_data *wdata = data; 428 struct nfs_commit_data *wdata = data;
@@ -471,14 +433,6 @@ static void filelayout_commit_prepare(struct rpc_task *task, void *data)
471 task); 433 task);
472} 434}
473 435
474static void filelayout_write_commit_done(struct rpc_task *task, void *data)
475{
476 struct nfs_commit_data *wdata = data;
477
478 /* Note this may cause RPC to be resent */
479 wdata->mds_ops->rpc_call_done(task, data);
480}
481
482static void filelayout_commit_count_stats(struct rpc_task *task, void *data) 436static void filelayout_commit_count_stats(struct rpc_task *task, void *data)
483{ 437{
484 struct nfs_commit_data *cdata = data; 438 struct nfs_commit_data *cdata = data;
@@ -486,35 +440,25 @@ static void filelayout_commit_count_stats(struct rpc_task *task, void *data)
486 rpc_count_iostats(task, NFS_SERVER(cdata->inode)->client->cl_metrics); 440 rpc_count_iostats(task, NFS_SERVER(cdata->inode)->client->cl_metrics);
487} 441}
488 442
489static void filelayout_commit_release(void *calldata)
490{
491 struct nfs_commit_data *data = calldata;
492
493 data->completion_ops->completion(data);
494 pnfs_put_lseg(data->lseg);
495 nfs_put_client(data->ds_clp);
496 nfs_commitdata_release(data);
497}
498
499static const struct rpc_call_ops filelayout_read_call_ops = { 443static const struct rpc_call_ops filelayout_read_call_ops = {
500 .rpc_call_prepare = filelayout_read_prepare, 444 .rpc_call_prepare = filelayout_read_prepare,
501 .rpc_call_done = filelayout_read_call_done, 445 .rpc_call_done = filelayout_read_call_done,
502 .rpc_count_stats = filelayout_read_count_stats, 446 .rpc_count_stats = filelayout_read_count_stats,
503 .rpc_release = filelayout_read_release, 447 .rpc_release = pnfs_generic_rw_release,
504}; 448};
505 449
506static const struct rpc_call_ops filelayout_write_call_ops = { 450static const struct rpc_call_ops filelayout_write_call_ops = {
507 .rpc_call_prepare = filelayout_write_prepare, 451 .rpc_call_prepare = filelayout_write_prepare,
508 .rpc_call_done = filelayout_write_call_done, 452 .rpc_call_done = filelayout_write_call_done,
509 .rpc_count_stats = filelayout_write_count_stats, 453 .rpc_count_stats = filelayout_write_count_stats,
510 .rpc_release = filelayout_write_release, 454 .rpc_release = pnfs_generic_rw_release,
511}; 455};
512 456
513static const struct rpc_call_ops filelayout_commit_call_ops = { 457static const struct rpc_call_ops filelayout_commit_call_ops = {
514 .rpc_call_prepare = filelayout_commit_prepare, 458 .rpc_call_prepare = filelayout_commit_prepare,
515 .rpc_call_done = filelayout_write_commit_done, 459 .rpc_call_done = pnfs_generic_write_commit_done,
516 .rpc_count_stats = filelayout_commit_count_stats, 460 .rpc_count_stats = filelayout_commit_count_stats,
517 .rpc_release = filelayout_commit_release, 461 .rpc_release = pnfs_generic_commit_release,
518}; 462};
519 463
520static enum pnfs_try_status 464static enum pnfs_try_status
@@ -1004,33 +948,6 @@ static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j)
1004 return j; 948 return j;
1005} 949}
1006 950
1007/* The generic layer is about to remove the req from the commit list.
1008 * If this will make the bucket empty, it will need to put the lseg reference.
1009 * Note this is must be called holding the inode (/cinfo) lock
1010 */
1011static void
1012filelayout_clear_request_commit(struct nfs_page *req,
1013 struct nfs_commit_info *cinfo)
1014{
1015 struct pnfs_layout_segment *freeme = NULL;
1016
1017 if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
1018 goto out;
1019 cinfo->ds->nwritten--;
1020 if (list_is_singular(&req->wb_list)) {
1021 struct pnfs_commit_bucket *bucket;
1022
1023 bucket = list_first_entry(&req->wb_list,
1024 struct pnfs_commit_bucket,
1025 written);
1026 freeme = bucket->wlseg;
1027 bucket->wlseg = NULL;
1028 }
1029out:
1030 nfs_request_remove_commit_list(req, cinfo);
1031 pnfs_put_lseg_locked(freeme);
1032}
1033
1034static void 951static void
1035filelayout_mark_request_commit(struct nfs_page *req, 952filelayout_mark_request_commit(struct nfs_page *req,
1036 struct pnfs_layout_segment *lseg, 953 struct pnfs_layout_segment *lseg,
@@ -1064,7 +981,7 @@ filelayout_mark_request_commit(struct nfs_page *req,
1064 * is normally transferred to the COMMIT call and released 981 * is normally transferred to the COMMIT call and released
1065 * there. It could also be released if the last req is pulled 982 * there. It could also be released if the last req is pulled
1066 * off due to a rewrite, in which case it will be done in 983 * off due to a rewrite, in which case it will be done in
1067 * filelayout_clear_request_commit 984 * pnfs_generic_clear_request_commit
1068 */ 985 */
1069 buckets[i].wlseg = pnfs_get_lseg(lseg); 986 buckets[i].wlseg = pnfs_get_lseg(lseg);
1070 } 987 }
@@ -1142,97 +1059,11 @@ static int filelayout_initiate_commit(struct nfs_commit_data *data, int how)
1142 &filelayout_commit_call_ops, how, 1059 &filelayout_commit_call_ops, how,
1143 RPC_TASK_SOFTCONN); 1060 RPC_TASK_SOFTCONN);
1144out_err: 1061out_err:
1145 prepare_to_resend_writes(data); 1062 pnfs_generic_prepare_to_resend_writes(data);
1146 filelayout_commit_release(data); 1063 pnfs_generic_commit_release(data);
1147 return -EAGAIN; 1064 return -EAGAIN;
1148} 1065}
1149 1066
1150static int
1151transfer_commit_list(struct list_head *src, struct list_head *dst,
1152 struct nfs_commit_info *cinfo, int max)
1153{
1154 struct nfs_page *req, *tmp;
1155 int ret = 0;
1156
1157 list_for_each_entry_safe(req, tmp, src, wb_list) {
1158 if (!nfs_lock_request(req))
1159 continue;
1160 kref_get(&req->wb_kref);
1161 if (cond_resched_lock(cinfo->lock))
1162 list_safe_reset_next(req, tmp, wb_list);
1163 nfs_request_remove_commit_list(req, cinfo);
1164 clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
1165 nfs_list_add_request(req, dst);
1166 ret++;
1167 if ((ret == max) && !cinfo->dreq)
1168 break;
1169 }
1170 return ret;
1171}
1172
1173/* Note called with cinfo->lock held. */
1174static int
1175filelayout_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
1176 struct nfs_commit_info *cinfo,
1177 int max)
1178{
1179 struct list_head *src = &bucket->written;
1180 struct list_head *dst = &bucket->committing;
1181 int ret;
1182
1183 ret = transfer_commit_list(src, dst, cinfo, max);
1184 if (ret) {
1185 cinfo->ds->nwritten -= ret;
1186 cinfo->ds->ncommitting += ret;
1187 bucket->clseg = bucket->wlseg;
1188 if (list_empty(src))
1189 bucket->wlseg = NULL;
1190 else
1191 pnfs_get_lseg(bucket->clseg);
1192 }
1193 return ret;
1194}
1195
1196/* Move reqs from written to committing lists, returning count of number moved.
1197 * Note called with cinfo->lock held.
1198 */
1199static int filelayout_scan_commit_lists(struct nfs_commit_info *cinfo,
1200 int max)
1201{
1202 int i, rv = 0, cnt;
1203
1204 for (i = 0; i < cinfo->ds->nbuckets && max != 0; i++) {
1205 cnt = filelayout_scan_ds_commit_list(&cinfo->ds->buckets[i],
1206 cinfo, max);
1207 max -= cnt;
1208 rv += cnt;
1209 }
1210 return rv;
1211}
1212
1213/* Pull everything off the committing lists and dump into @dst */
1214static void filelayout_recover_commit_reqs(struct list_head *dst,
1215 struct nfs_commit_info *cinfo)
1216{
1217 struct pnfs_commit_bucket *b;
1218 struct pnfs_layout_segment *freeme;
1219 int i;
1220
1221restart:
1222 spin_lock(cinfo->lock);
1223 for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
1224 if (transfer_commit_list(&b->written, dst, cinfo, 0)) {
1225 freeme = b->wlseg;
1226 b->wlseg = NULL;
1227 spin_unlock(cinfo->lock);
1228 pnfs_put_lseg(freeme);
1229 goto restart;
1230 }
1231 }
1232 cinfo->ds->nwritten = 0;
1233 spin_unlock(cinfo->lock);
1234}
1235
1236/* filelayout_search_commit_reqs - Search lists in @cinfo for the head reqest 1067/* filelayout_search_commit_reqs - Search lists in @cinfo for the head reqest
1237 * for @page 1068 * for @page
1238 * @cinfo - commit info for current inode 1069 * @cinfo - commit info for current inode
@@ -1263,108 +1094,14 @@ filelayout_search_commit_reqs(struct nfs_commit_info *cinfo, struct page *page)
1263 return NULL; 1094 return NULL;
1264} 1095}
1265 1096
1266static void filelayout_retry_commit(struct nfs_commit_info *cinfo, int idx)
1267{
1268 struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
1269 struct pnfs_commit_bucket *bucket;
1270 struct pnfs_layout_segment *freeme;
1271 int i;
1272
1273 for (i = idx; i < fl_cinfo->nbuckets; i++) {
1274 bucket = &fl_cinfo->buckets[i];
1275 if (list_empty(&bucket->committing))
1276 continue;
1277 nfs_retry_commit(&bucket->committing, bucket->clseg, cinfo);
1278 spin_lock(cinfo->lock);
1279 freeme = bucket->clseg;
1280 bucket->clseg = NULL;
1281 spin_unlock(cinfo->lock);
1282 pnfs_put_lseg(freeme);
1283 }
1284}
1285
1286static unsigned int
1287alloc_ds_commits(struct nfs_commit_info *cinfo, struct list_head *list)
1288{
1289 struct pnfs_ds_commit_info *fl_cinfo;
1290 struct pnfs_commit_bucket *bucket;
1291 struct nfs_commit_data *data;
1292 int i;
1293 unsigned int nreq = 0;
1294
1295 fl_cinfo = cinfo->ds;
1296 bucket = fl_cinfo->buckets;
1297 for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
1298 if (list_empty(&bucket->committing))
1299 continue;
1300 data = nfs_commitdata_alloc();
1301 if (!data)
1302 break;
1303 data->ds_commit_index = i;
1304 spin_lock(cinfo->lock);
1305 data->lseg = bucket->clseg;
1306 bucket->clseg = NULL;
1307 spin_unlock(cinfo->lock);
1308 list_add(&data->pages, list);
1309 nreq++;
1310 }
1311
1312 /* Clean up on error */
1313 filelayout_retry_commit(cinfo, i);
1314 /* Caller will clean up entries put on list */
1315 return nreq;
1316}
1317
1318/* This follows nfs_commit_list pretty closely */
1319static int 1097static int
1320filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, 1098filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
1321 int how, struct nfs_commit_info *cinfo) 1099 int how, struct nfs_commit_info *cinfo)
1322{ 1100{
1323 struct nfs_commit_data *data, *tmp; 1101 return pnfs_generic_commit_pagelist(inode, mds_pages, how, cinfo,
1324 LIST_HEAD(list); 1102 filelayout_initiate_commit);
1325 unsigned int nreq = 0;
1326
1327 if (!list_empty(mds_pages)) {
1328 data = nfs_commitdata_alloc();
1329 if (data != NULL) {
1330 data->lseg = NULL;
1331 list_add(&data->pages, &list);
1332 nreq++;
1333 } else {
1334 nfs_retry_commit(mds_pages, NULL, cinfo);
1335 filelayout_retry_commit(cinfo, 0);
1336 cinfo->completion_ops->error_cleanup(NFS_I(inode));
1337 return -ENOMEM;
1338 }
1339 }
1340
1341 nreq += alloc_ds_commits(cinfo, &list);
1342
1343 if (nreq == 0) {
1344 cinfo->completion_ops->error_cleanup(NFS_I(inode));
1345 goto out;
1346 }
1347
1348 atomic_add(nreq, &cinfo->mds->rpcs_out);
1349
1350 list_for_each_entry_safe(data, tmp, &list, pages) {
1351 list_del_init(&data->pages);
1352 if (!data->lseg) {
1353 nfs_init_commit(data, mds_pages, NULL, cinfo);
1354 nfs_initiate_commit(NFS_CLIENT(inode), data,
1355 data->mds_ops, how, 0);
1356 } else {
1357 struct pnfs_commit_bucket *buckets;
1358
1359 buckets = cinfo->ds->buckets;
1360 nfs_init_commit(data, &buckets[data->ds_commit_index].committing, data->lseg, cinfo);
1361 filelayout_initiate_commit(data, how);
1362 }
1363 }
1364out:
1365 cinfo->ds->ncommitting = 0;
1366 return PNFS_ATTEMPTED;
1367} 1103}
1104
1368static struct nfs4_deviceid_node * 1105static struct nfs4_deviceid_node *
1369filelayout_alloc_deviceid_node(struct nfs_server *server, 1106filelayout_alloc_deviceid_node(struct nfs_server *server,
1370 struct pnfs_device *pdev, gfp_t gfp_flags) 1107 struct pnfs_device *pdev, gfp_t gfp_flags)
@@ -1421,9 +1158,9 @@ static struct pnfs_layoutdriver_type filelayout_type = {
1421 .pg_write_ops = &filelayout_pg_write_ops, 1158 .pg_write_ops = &filelayout_pg_write_ops,
1422 .get_ds_info = &filelayout_get_ds_info, 1159 .get_ds_info = &filelayout_get_ds_info,
1423 .mark_request_commit = filelayout_mark_request_commit, 1160 .mark_request_commit = filelayout_mark_request_commit,
1424 .clear_request_commit = filelayout_clear_request_commit, 1161 .clear_request_commit = pnfs_generic_clear_request_commit,
1425 .scan_commit_lists = filelayout_scan_commit_lists, 1162 .scan_commit_lists = pnfs_generic_scan_commit_lists,
1426 .recover_commit_reqs = filelayout_recover_commit_reqs, 1163 .recover_commit_reqs = pnfs_generic_recover_commit_reqs,
1427 .search_commit_reqs = filelayout_search_commit_reqs, 1164 .search_commit_reqs = filelayout_search_commit_reqs,
1428 .commit_pagelist = filelayout_commit_pagelist, 1165 .commit_pagelist = filelayout_commit_pagelist,
1429 .read_pagelist = filelayout_read_pagelist, 1166 .read_pagelist = filelayout_read_pagelist,
diff --git a/fs/nfs/filelayout/filelayout.h b/fs/nfs/filelayout/filelayout.h
index 7c9f800c49d7..a5ce9b4bf2f8 100644
--- a/fs/nfs/filelayout/filelayout.h
+++ b/fs/nfs/filelayout/filelayout.h
@@ -119,17 +119,6 @@ FILELAYOUT_DEVID_NODE(struct pnfs_layout_segment *lseg)
119 return &FILELAYOUT_LSEG(lseg)->dsaddr->id_node; 119 return &FILELAYOUT_LSEG(lseg)->dsaddr->id_node;
120} 120}
121 121
122static inline void
123filelayout_mark_devid_invalid(struct nfs4_deviceid_node *node)
124{
125 u32 *p = (u32 *)&node->deviceid;
126
127 printk(KERN_WARNING "NFS: Deviceid [%x%x%x%x] marked out of use.\n",
128 p[0], p[1], p[2], p[3]);
129
130 set_bit(NFS_DEVICEID_INVALID, &node->flags);
131}
132
133static inline bool 122static inline bool
134filelayout_test_devid_invalid(struct nfs4_deviceid_node *node) 123filelayout_test_devid_invalid(struct nfs4_deviceid_node *node)
135{ 124{
diff --git a/fs/nfs/filelayout/filelayoutdev.c b/fs/nfs/filelayout/filelayoutdev.c
index bfecac781f19..d21080aed9b2 100644
--- a/fs/nfs/filelayout/filelayoutdev.c
+++ b/fs/nfs/filelayout/filelayoutdev.c
@@ -708,7 +708,7 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx)
708 if (ds == NULL) { 708 if (ds == NULL) {
709 printk(KERN_ERR "NFS: %s: No data server for offset index %d\n", 709 printk(KERN_ERR "NFS: %s: No data server for offset index %d\n",
710 __func__, ds_idx); 710 __func__, ds_idx);
711 filelayout_mark_devid_invalid(devid); 711 pnfs_generic_mark_devid_invalid(devid);
712 goto out; 712 goto out;
713 } 713 }
714 smp_rmb(); 714 smp_rmb();
diff --git a/fs/nfs/pnfs.h b/fs/nfs/pnfs.h
index 9ae5b765b073..f17663446acc 100644
--- a/fs/nfs/pnfs.h
+++ b/fs/nfs/pnfs.h
@@ -275,6 +275,23 @@ void nfs4_mark_deviceid_unavailable(struct nfs4_deviceid_node *node);
275bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node); 275bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node);
276void nfs4_deviceid_purge_client(const struct nfs_client *); 276void nfs4_deviceid_purge_client(const struct nfs_client *);
277 277
278/* pnfs_nfs.c */
279void pnfs_generic_clear_request_commit(struct nfs_page *req,
280 struct nfs_commit_info *cinfo);
281void pnfs_generic_commit_release(void *calldata);
282void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data);
283void pnfs_generic_rw_release(void *data);
284void pnfs_generic_recover_commit_reqs(struct list_head *dst,
285 struct nfs_commit_info *cinfo);
286int pnfs_generic_commit_pagelist(struct inode *inode,
287 struct list_head *mds_pages,
288 int how,
289 struct nfs_commit_info *cinfo,
290 int (*initiate_commit)(struct nfs_commit_data *data,
291 int how));
292int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max);
293void pnfs_generic_write_commit_done(struct rpc_task *task, void *data);
294
278static inline struct nfs4_deviceid_node * 295static inline struct nfs4_deviceid_node *
279nfs4_get_deviceid(struct nfs4_deviceid_node *d) 296nfs4_get_deviceid(struct nfs4_deviceid_node *d)
280{ 297{
@@ -317,6 +334,12 @@ pnfs_get_ds_info(struct inode *inode)
317 return ld->get_ds_info(inode); 334 return ld->get_ds_info(inode);
318} 335}
319 336
337static inline void
338pnfs_generic_mark_devid_invalid(struct nfs4_deviceid_node *node)
339{
340 set_bit(NFS_DEVICEID_INVALID, &node->flags);
341}
342
320static inline bool 343static inline bool
321pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, 344pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg,
322 struct nfs_commit_info *cinfo) 345 struct nfs_commit_info *cinfo)
diff --git a/fs/nfs/pnfs_nfs.c b/fs/nfs/pnfs_nfs.c
new file mode 100644
index 000000000000..e5f841cb6227
--- /dev/null
+++ b/fs/nfs/pnfs_nfs.c
@@ -0,0 +1,291 @@
1/*
2 * Common NFS I/O operations for the pnfs file based
3 * layout drivers.
4 *
5 * Copyright (c) 2014, Primary Data, Inc. All rights reserved.
6 *
7 * Tom Haynes <loghyr@primarydata.com>
8 */
9
10#include <linux/nfs_fs.h>
11#include <linux/nfs_page.h>
12
13#include "internal.h"
14#include "pnfs.h"
15
16static void pnfs_generic_fenceme(struct inode *inode,
17 struct pnfs_layout_hdr *lo)
18{
19 if (!test_and_clear_bit(NFS_LAYOUT_RETURN, &lo->plh_flags))
20 return;
21 pnfs_return_layout(inode);
22}
23
24void pnfs_generic_rw_release(void *data)
25{
26 struct nfs_pgio_header *hdr = data;
27 struct pnfs_layout_hdr *lo = hdr->lseg->pls_layout;
28
29 pnfs_generic_fenceme(lo->plh_inode, lo);
30 nfs_put_client(hdr->ds_clp);
31 hdr->mds_ops->rpc_release(data);
32}
33EXPORT_SYMBOL_GPL(pnfs_generic_rw_release);
34
35/* Fake up some data that will cause nfs_commit_release to retry the writes. */
36void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data)
37{
38 struct nfs_page *first = nfs_list_entry(data->pages.next);
39
40 data->task.tk_status = 0;
41 memcpy(&data->verf.verifier, &first->wb_verf,
42 sizeof(data->verf.verifier));
43 data->verf.verifier.data[0]++; /* ensure verifier mismatch */
44}
45EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes);
46
47void pnfs_generic_write_commit_done(struct rpc_task *task, void *data)
48{
49 struct nfs_commit_data *wdata = data;
50
51 /* Note this may cause RPC to be resent */
52 wdata->mds_ops->rpc_call_done(task, data);
53}
54EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done);
55
56void pnfs_generic_commit_release(void *calldata)
57{
58 struct nfs_commit_data *data = calldata;
59
60 data->completion_ops->completion(data);
61 pnfs_put_lseg(data->lseg);
62 nfs_put_client(data->ds_clp);
63 nfs_commitdata_release(data);
64}
65EXPORT_SYMBOL_GPL(pnfs_generic_commit_release);
66
67/* The generic layer is about to remove the req from the commit list.
68 * If this will make the bucket empty, it will need to put the lseg reference.
69 * Note this is must be called holding the inode (/cinfo) lock
70 */
71void
72pnfs_generic_clear_request_commit(struct nfs_page *req,
73 struct nfs_commit_info *cinfo)
74{
75 struct pnfs_layout_segment *freeme = NULL;
76
77 if (!test_and_clear_bit(PG_COMMIT_TO_DS, &req->wb_flags))
78 goto out;
79 cinfo->ds->nwritten--;
80 if (list_is_singular(&req->wb_list)) {
81 struct pnfs_commit_bucket *bucket;
82
83 bucket = list_first_entry(&req->wb_list,
84 struct pnfs_commit_bucket,
85 written);
86 freeme = bucket->wlseg;
87 bucket->wlseg = NULL;
88 }
89out:
90 nfs_request_remove_commit_list(req, cinfo);
91 pnfs_put_lseg_locked(freeme);
92}
93EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit);
94
95static int
96pnfs_generic_transfer_commit_list(struct list_head *src, struct list_head *dst,
97 struct nfs_commit_info *cinfo, int max)
98{
99 struct nfs_page *req, *tmp;
100 int ret = 0;
101
102 list_for_each_entry_safe(req, tmp, src, wb_list) {
103 if (!nfs_lock_request(req))
104 continue;
105 kref_get(&req->wb_kref);
106 if (cond_resched_lock(cinfo->lock))
107 list_safe_reset_next(req, tmp, wb_list);
108 nfs_request_remove_commit_list(req, cinfo);
109 clear_bit(PG_COMMIT_TO_DS, &req->wb_flags);
110 nfs_list_add_request(req, dst);
111 ret++;
112 if ((ret == max) && !cinfo->dreq)
113 break;
114 }
115 return ret;
116}
117
118/* Note called with cinfo->lock held. */
119static int
120pnfs_generic_scan_ds_commit_list(struct pnfs_commit_bucket *bucket,
121 struct nfs_commit_info *cinfo,
122 int max)
123{
124 struct list_head *src = &bucket->written;
125 struct list_head *dst = &bucket->committing;
126 int ret;
127
128 ret = pnfs_generic_transfer_commit_list(src, dst, cinfo, max);
129 if (ret) {
130 cinfo->ds->nwritten -= ret;
131 cinfo->ds->ncommitting += ret;
132 bucket->clseg = bucket->wlseg;
133 if (list_empty(src))
134 bucket->wlseg = NULL;
135 else
136 pnfs_get_lseg(bucket->clseg);
137 }
138 return ret;
139}
140
141/* Move reqs from written to committing lists, returning count of number moved.
142 * Note called with cinfo->lock held.
143 */
144int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo,
145 int max)
146{
147 int i, rv = 0, cnt;
148
149 for (i = 0; i < cinfo->ds->nbuckets && max != 0; i++) {
150 cnt = pnfs_generic_scan_ds_commit_list(&cinfo->ds->buckets[i],
151 cinfo, max);
152 max -= cnt;
153 rv += cnt;
154 }
155 return rv;
156}
157EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists);
158
159/* Pull everything off the committing lists and dump into @dst */
160void pnfs_generic_recover_commit_reqs(struct list_head *dst,
161 struct nfs_commit_info *cinfo)
162{
163 struct pnfs_commit_bucket *b;
164 struct pnfs_layout_segment *freeme;
165 int i;
166
167restart:
168 spin_lock(cinfo->lock);
169 for (i = 0, b = cinfo->ds->buckets; i < cinfo->ds->nbuckets; i++, b++) {
170 if (pnfs_generic_transfer_commit_list(&b->written, dst,
171 cinfo, 0)) {
172 freeme = b->wlseg;
173 b->wlseg = NULL;
174 spin_unlock(cinfo->lock);
175 pnfs_put_lseg(freeme);
176 goto restart;
177 }
178 }
179 cinfo->ds->nwritten = 0;
180 spin_unlock(cinfo->lock);
181}
182EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs);
183
184static void pnfs_generic_retry_commit(struct nfs_commit_info *cinfo, int idx)
185{
186 struct pnfs_ds_commit_info *fl_cinfo = cinfo->ds;
187 struct pnfs_commit_bucket *bucket;
188 struct pnfs_layout_segment *freeme;
189 int i;
190
191 for (i = idx; i < fl_cinfo->nbuckets; i++) {
192 bucket = &fl_cinfo->buckets[i];
193 if (list_empty(&bucket->committing))
194 continue;
195 nfs_retry_commit(&bucket->committing, bucket->clseg, cinfo);
196 spin_lock(cinfo->lock);
197 freeme = bucket->clseg;
198 bucket->clseg = NULL;
199 spin_unlock(cinfo->lock);
200 pnfs_put_lseg(freeme);
201 }
202}
203
204static unsigned int
205pnfs_generic_alloc_ds_commits(struct nfs_commit_info *cinfo,
206 struct list_head *list)
207{
208 struct pnfs_ds_commit_info *fl_cinfo;
209 struct pnfs_commit_bucket *bucket;
210 struct nfs_commit_data *data;
211 int i;
212 unsigned int nreq = 0;
213
214 fl_cinfo = cinfo->ds;
215 bucket = fl_cinfo->buckets;
216 for (i = 0; i < fl_cinfo->nbuckets; i++, bucket++) {
217 if (list_empty(&bucket->committing))
218 continue;
219 data = nfs_commitdata_alloc();
220 if (!data)
221 break;
222 data->ds_commit_index = i;
223 spin_lock(cinfo->lock);
224 data->lseg = bucket->clseg;
225 bucket->clseg = NULL;
226 spin_unlock(cinfo->lock);
227 list_add(&data->pages, list);
228 nreq++;
229 }
230
231 /* Clean up on error */
232 pnfs_generic_retry_commit(cinfo, i);
233 return nreq;
234}
235
236/* This follows nfs_commit_list pretty closely */
237int
238pnfs_generic_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
239 int how, struct nfs_commit_info *cinfo,
240 int (*initiate_commit)(struct nfs_commit_data *data,
241 int how))
242{
243 struct nfs_commit_data *data, *tmp;
244 LIST_HEAD(list);
245 unsigned int nreq = 0;
246
247 if (!list_empty(mds_pages)) {
248 data = nfs_commitdata_alloc();
249 if (data != NULL) {
250 data->lseg = NULL;
251 list_add(&data->pages, &list);
252 nreq++;
253 } else {
254 nfs_retry_commit(mds_pages, NULL, cinfo);
255 pnfs_generic_retry_commit(cinfo, 0);
256 cinfo->completion_ops->error_cleanup(NFS_I(inode));
257 return -ENOMEM;
258 }
259 }
260
261 nreq += pnfs_generic_alloc_ds_commits(cinfo, &list);
262
263 if (nreq == 0) {
264 cinfo->completion_ops->error_cleanup(NFS_I(inode));
265 goto out;
266 }
267
268 atomic_add(nreq, &cinfo->mds->rpcs_out);
269
270 list_for_each_entry_safe(data, tmp, &list, pages) {
271 list_del_init(&data->pages);
272 if (!data->lseg) {
273 nfs_init_commit(data, mds_pages, NULL, cinfo);
274 nfs_initiate_commit(NFS_CLIENT(inode), data,
275 data->mds_ops, how, 0);
276 } else {
277 struct pnfs_commit_bucket *buckets;
278
279 buckets = cinfo->ds->buckets;
280 nfs_init_commit(data,
281 &buckets[data->ds_commit_index].committing,
282 data->lseg,
283 cinfo);
284 initiate_commit(data, how);
285 }
286 }
287out:
288 cinfo->ds->ncommitting = 0;
289 return PNFS_ATTEMPTED;
290}
291EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist);