diff options
-rw-r--r-- | fs/nfs/Makefile | 2 | ||||
-rw-r--r-- | fs/nfs/filelayout/filelayout.c | 291 | ||||
-rw-r--r-- | fs/nfs/filelayout/filelayout.h | 11 | ||||
-rw-r--r-- | fs/nfs/filelayout/filelayoutdev.c | 2 | ||||
-rw-r--r-- | fs/nfs/pnfs.h | 23 | ||||
-rw-r--r-- | fs/nfs/pnfs_nfs.c | 291 |
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 |
28 | nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o | 28 | nfsv4-$(CONFIG_NFS_USE_LEGACY_DNS) += cache_lib.o |
29 | nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o | 29 | nfsv4-$(CONFIG_SYSCTL) += nfs4sysctl.o |
30 | nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o | 30 | nfsv4-$(CONFIG_NFS_V4_1) += pnfs.o pnfs_dev.o pnfs_nfs.o |
31 | nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o | 31 | nfsv4-$(CONFIG_NFS_V4_2) += nfs42proc.o |
32 | 32 | ||
33 | obj-$(CONFIG_PNFS_FILE_LAYOUT) += filelayout/ | 33 | obj-$(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 | ||
121 | static 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 | |||
128 | static int filelayout_async_handle_error(struct rpc_task *task, | 121 | static 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 | ||
342 | static 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 | |||
352 | static int filelayout_write_done_cb(struct rpc_task *task, | 335 | static 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. */ | ||
375 | static 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 | |||
385 | static int filelayout_commit_done_cb(struct rpc_task *task, | 357 | static 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 | ||
454 | static 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 | |||
464 | static void filelayout_commit_prepare(struct rpc_task *task, void *data) | 426 | static 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 | ||
474 | static 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 | |||
482 | static void filelayout_commit_count_stats(struct rpc_task *task, void *data) | 436 | static 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 | ||
489 | static 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 | |||
499 | static const struct rpc_call_ops filelayout_read_call_ops = { | 443 | static 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 | ||
506 | static const struct rpc_call_ops filelayout_write_call_ops = { | 450 | static 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 | ||
513 | static const struct rpc_call_ops filelayout_commit_call_ops = { | 457 | static 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 | ||
520 | static enum pnfs_try_status | 464 | static 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 | */ | ||
1011 | static void | ||
1012 | filelayout_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 | } | ||
1029 | out: | ||
1030 | nfs_request_remove_commit_list(req, cinfo); | ||
1031 | pnfs_put_lseg_locked(freeme); | ||
1032 | } | ||
1033 | |||
1034 | static void | 951 | static void |
1035 | filelayout_mark_request_commit(struct nfs_page *req, | 952 | filelayout_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); |
1144 | out_err: | 1061 | out_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 | ||
1150 | static int | ||
1151 | transfer_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. */ | ||
1174 | static int | ||
1175 | filelayout_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 | */ | ||
1199 | static 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 */ | ||
1214 | static 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 | |||
1221 | restart: | ||
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 | ||
1266 | static 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 | |||
1286 | static unsigned int | ||
1287 | alloc_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 */ | ||
1319 | static int | 1097 | static int |
1320 | filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, | 1098 | filelayout_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 | } | ||
1364 | out: | ||
1365 | cinfo->ds->ncommitting = 0; | ||
1366 | return PNFS_ATTEMPTED; | ||
1367 | } | 1103 | } |
1104 | |||
1368 | static struct nfs4_deviceid_node * | 1105 | static struct nfs4_deviceid_node * |
1369 | filelayout_alloc_deviceid_node(struct nfs_server *server, | 1106 | filelayout_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 | ||
122 | static inline void | ||
123 | filelayout_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 | |||
133 | static inline bool | 122 | static inline bool |
134 | filelayout_test_devid_invalid(struct nfs4_deviceid_node *node) | 123 | filelayout_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); | |||
275 | bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node); | 275 | bool nfs4_test_deviceid_unavailable(struct nfs4_deviceid_node *node); |
276 | void nfs4_deviceid_purge_client(const struct nfs_client *); | 276 | void nfs4_deviceid_purge_client(const struct nfs_client *); |
277 | 277 | ||
278 | /* pnfs_nfs.c */ | ||
279 | void pnfs_generic_clear_request_commit(struct nfs_page *req, | ||
280 | struct nfs_commit_info *cinfo); | ||
281 | void pnfs_generic_commit_release(void *calldata); | ||
282 | void pnfs_generic_prepare_to_resend_writes(struct nfs_commit_data *data); | ||
283 | void pnfs_generic_rw_release(void *data); | ||
284 | void pnfs_generic_recover_commit_reqs(struct list_head *dst, | ||
285 | struct nfs_commit_info *cinfo); | ||
286 | int 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)); | ||
292 | int pnfs_generic_scan_commit_lists(struct nfs_commit_info *cinfo, int max); | ||
293 | void pnfs_generic_write_commit_done(struct rpc_task *task, void *data); | ||
294 | |||
278 | static inline struct nfs4_deviceid_node * | 295 | static inline struct nfs4_deviceid_node * |
279 | nfs4_get_deviceid(struct nfs4_deviceid_node *d) | 296 | nfs4_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 | ||
337 | static inline void | ||
338 | pnfs_generic_mark_devid_invalid(struct nfs4_deviceid_node *node) | ||
339 | { | ||
340 | set_bit(NFS_DEVICEID_INVALID, &node->flags); | ||
341 | } | ||
342 | |||
320 | static inline bool | 343 | static inline bool |
321 | pnfs_mark_request_commit(struct nfs_page *req, struct pnfs_layout_segment *lseg, | 344 | pnfs_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 | |||
16 | static 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 | |||
24 | void 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 | } | ||
33 | EXPORT_SYMBOL_GPL(pnfs_generic_rw_release); | ||
34 | |||
35 | /* Fake up some data that will cause nfs_commit_release to retry the writes. */ | ||
36 | void 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 | } | ||
45 | EXPORT_SYMBOL_GPL(pnfs_generic_prepare_to_resend_writes); | ||
46 | |||
47 | void 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 | } | ||
54 | EXPORT_SYMBOL_GPL(pnfs_generic_write_commit_done); | ||
55 | |||
56 | void 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 | } | ||
65 | EXPORT_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 | */ | ||
71 | void | ||
72 | pnfs_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 | } | ||
89 | out: | ||
90 | nfs_request_remove_commit_list(req, cinfo); | ||
91 | pnfs_put_lseg_locked(freeme); | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(pnfs_generic_clear_request_commit); | ||
94 | |||
95 | static int | ||
96 | pnfs_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. */ | ||
119 | static int | ||
120 | pnfs_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 | */ | ||
144 | int 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 | } | ||
157 | EXPORT_SYMBOL_GPL(pnfs_generic_scan_commit_lists); | ||
158 | |||
159 | /* Pull everything off the committing lists and dump into @dst */ | ||
160 | void 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 | |||
167 | restart: | ||
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 | } | ||
182 | EXPORT_SYMBOL_GPL(pnfs_generic_recover_commit_reqs); | ||
183 | |||
184 | static 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 | |||
204 | static unsigned int | ||
205 | pnfs_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 */ | ||
237 | int | ||
238 | pnfs_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 | } | ||
287 | out: | ||
288 | cinfo->ds->ncommitting = 0; | ||
289 | return PNFS_ATTEMPTED; | ||
290 | } | ||
291 | EXPORT_SYMBOL_GPL(pnfs_generic_commit_pagelist); | ||