aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTom Haynes <loghyr@primarydata.com>2014-12-11 15:34:59 -0500
committerTom Haynes <loghyr@primarydata.com>2015-02-03 14:06:31 -0500
commitf54bcf2ecee982da47c2baf8bd87fd9ad9984651 (patch)
treec813d2cc90424e847452cec824cbcdbf74470b39
parentec6f34e5b552fb0a52e6aae1a5afbbb1605cc6cc (diff)
pnfs: Prepare for flexfiles by pulling out common code
The flexfilelayout driver will share some common code with the filelayout driver. This set of changes refactors that common code out to avoid any module depenencies. Signed-off-by: Tom Haynes <loghyr@primarydata.com>
-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);