aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorFred Isaman <iisaman@netapp.com>2011-03-23 09:27:53 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-03-23 15:29:04 -0400
commite0c2b3801828aadb65dec9f67f7c6b7a675ad007 (patch)
tree547d70967ad8f41a18f2d3112dbd12e64f972af2 /fs
parent988b6dceb0ae6d642587c8594529b94f6be0c5ea (diff)
NFSv4.1: filelayout driver specific code for COMMIT
Implement all the hooks created in the previous patches. This requires exporting quite a few functions and adding a few structure fields. Signed-off-by: Fred Isaman <iisaman@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/inode.c1
-rw-r--r--fs/nfs/internal.h14
-rw-r--r--fs/nfs/nfs4filelayout.c235
-rw-r--r--fs/nfs/pnfs.c1
-rw-r--r--fs/nfs/write.c21
5 files changed, 265 insertions, 7 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 477a2e512b39..229e586b1a20 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1470,6 +1470,7 @@ static inline void nfs4_init_once(struct nfs_inode *nfsi)
1470 nfsi->delegation_state = 0; 1470 nfsi->delegation_state = 0;
1471 init_rwsem(&nfsi->rwsem); 1471 init_rwsem(&nfsi->rwsem);
1472 nfsi->layout = NULL; 1472 nfsi->layout = NULL;
1473 atomic_set(&nfsi->commits_outstanding, 0);
1473#endif 1474#endif
1474} 1475}
1475 1476
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index d1ddc23c404d..708705062216 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -276,11 +276,25 @@ extern int nfs_initiate_read(struct nfs_read_data *data, struct rpc_clnt *clnt,
276extern void nfs_read_prepare(struct rpc_task *task, void *calldata); 276extern void nfs_read_prepare(struct rpc_task *task, void *calldata);
277 277
278/* write.c */ 278/* write.c */
279extern void nfs_commit_free(struct nfs_write_data *p);
279extern int nfs_initiate_write(struct nfs_write_data *data, 280extern int nfs_initiate_write(struct nfs_write_data *data,
280 struct rpc_clnt *clnt, 281 struct rpc_clnt *clnt,
281 const struct rpc_call_ops *call_ops, 282 const struct rpc_call_ops *call_ops,
282 int how); 283 int how);
283extern void nfs_write_prepare(struct rpc_task *task, void *calldata); 284extern void nfs_write_prepare(struct rpc_task *task, void *calldata);
285extern int nfs_initiate_commit(struct nfs_write_data *data,
286 struct rpc_clnt *clnt,
287 const struct rpc_call_ops *call_ops,
288 int how);
289extern void nfs_init_commit(struct nfs_write_data *data,
290 struct list_head *head,
291 struct pnfs_layout_segment *lseg);
292void nfs_retry_commit(struct list_head *page_list,
293 struct pnfs_layout_segment *lseg);
294void nfs_commit_clear_lock(struct nfs_inode *nfsi);
295void nfs_commitdata_release(void *data);
296void nfs_commit_release_pages(struct nfs_write_data *data);
297
284#ifdef CONFIG_MIGRATION 298#ifdef CONFIG_MIGRATION
285extern int nfs_migrate_page(struct address_space *, 299extern int nfs_migrate_page(struct address_space *,
286 struct page *, struct page *); 300 struct page *, struct page *);
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c
index 03ff80c67c6e..97e75a22af72 100644
--- a/fs/nfs/nfs4filelayout.c
+++ b/fs/nfs/nfs4filelayout.c
@@ -213,6 +213,37 @@ static int filelayout_write_done_cb(struct rpc_task *task,
213 return 0; 213 return 0;
214} 214}
215 215
216/* Fake up some data that will cause nfs_commit_release to retry the writes. */
217static void prepare_to_resend_writes(struct nfs_write_data *data)
218{
219 struct nfs_page *first = nfs_list_entry(data->pages.next);
220
221 data->task.tk_status = 0;
222 memcpy(data->verf.verifier, first->wb_verf.verifier,
223 sizeof(first->wb_verf.verifier));
224 data->verf.verifier[0]++; /* ensure verifier mismatch */
225}
226
227static int filelayout_commit_done_cb(struct rpc_task *task,
228 struct nfs_write_data *data)
229{
230 int reset = 0;
231
232 if (filelayout_async_handle_error(task, data->args.context->state,
233 data->ds_clp, &reset) == -EAGAIN) {
234 dprintk("%s calling restart ds_clp %p ds_clp->cl_session %p\n",
235 __func__, data->ds_clp, data->ds_clp->cl_session);
236 if (reset) {
237 prepare_to_resend_writes(data);
238 filelayout_set_lo_fail(data->lseg);
239 } else
240 nfs_restart_rpc(task, data->ds_clp);
241 return -EAGAIN;
242 }
243
244 return 0;
245}
246
216static void filelayout_write_prepare(struct rpc_task *task, void *data) 247static void filelayout_write_prepare(struct rpc_task *task, void *data)
217{ 248{
218 struct nfs_write_data *wdata = (struct nfs_write_data *)data; 249 struct nfs_write_data *wdata = (struct nfs_write_data *)data;
@@ -240,6 +271,16 @@ static void filelayout_write_release(void *data)
240 wdata->mds_ops->rpc_release(data); 271 wdata->mds_ops->rpc_release(data);
241} 272}
242 273
274static void filelayout_commit_release(void *data)
275{
276 struct nfs_write_data *wdata = (struct nfs_write_data *)data;
277
278 nfs_commit_release_pages(wdata);
279 if (atomic_dec_and_test(&NFS_I(wdata->inode)->commits_outstanding))
280 nfs_commit_clear_lock(NFS_I(wdata->inode));
281 nfs_commitdata_release(wdata);
282}
283
243struct rpc_call_ops filelayout_read_call_ops = { 284struct rpc_call_ops filelayout_read_call_ops = {
244 .rpc_call_prepare = filelayout_read_prepare, 285 .rpc_call_prepare = filelayout_read_prepare,
245 .rpc_call_done = filelayout_read_call_done, 286 .rpc_call_done = filelayout_read_call_done,
@@ -252,6 +293,12 @@ struct rpc_call_ops filelayout_write_call_ops = {
252 .rpc_release = filelayout_write_release, 293 .rpc_release = filelayout_write_release,
253}; 294};
254 295
296struct rpc_call_ops filelayout_commit_call_ops = {
297 .rpc_call_prepare = filelayout_write_prepare,
298 .rpc_call_done = filelayout_write_call_done,
299 .rpc_release = filelayout_commit_release,
300};
301
255static enum pnfs_try_status 302static enum pnfs_try_status
256filelayout_read_pagelist(struct nfs_read_data *data) 303filelayout_read_pagelist(struct nfs_read_data *data)
257{ 304{
@@ -574,6 +621,191 @@ filelayout_pg_test(struct nfs_pageio_descriptor *pgio, struct nfs_page *prev,
574 return (p_stripe == r_stripe); 621 return (p_stripe == r_stripe);
575} 622}
576 623
624static bool filelayout_mark_pnfs_commit(struct pnfs_layout_segment *lseg)
625{
626 return !FILELAYOUT_LSEG(lseg)->commit_through_mds;
627}
628
629static u32 select_bucket_index(struct nfs4_filelayout_segment *fl, u32 j)
630{
631 if (fl->stripe_type == STRIPE_SPARSE)
632 return nfs4_fl_calc_ds_index(&fl->generic_hdr, j);
633 else
634 return j;
635}
636
637struct list_head *filelayout_choose_commit_list(struct nfs_page *req)
638{
639 struct pnfs_layout_segment *lseg = req->wb_commit_lseg;
640 struct nfs4_filelayout_segment *fl = FILELAYOUT_LSEG(lseg);
641 u32 i, j;
642 struct list_head *list;
643
644 /* Note that we are calling nfs4_fl_calc_j_index on each page
645 * that ends up being committed to a data server. An attractive
646 * alternative is to add a field to nfs_write_data and nfs_page
647 * to store the value calculated in filelayout_write_pagelist
648 * and just use that here.
649 */
650 j = nfs4_fl_calc_j_index(lseg,
651 (loff_t)req->wb_index << PAGE_CACHE_SHIFT);
652 i = select_bucket_index(fl, j);
653 list = &fl->commit_buckets[i];
654 if (list_empty(list)) {
655 /* Non-empty buckets hold a reference on the lseg */
656 get_lseg(lseg);
657 }
658 return list;
659}
660
661static u32 calc_ds_index_from_commit(struct pnfs_layout_segment *lseg, u32 i)
662{
663 struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg);
664
665 if (flseg->stripe_type == STRIPE_SPARSE)
666 return i;
667 else
668 return nfs4_fl_calc_ds_index(lseg, i);
669}
670
671static struct nfs_fh *
672select_ds_fh_from_commit(struct pnfs_layout_segment *lseg, u32 i)
673{
674 struct nfs4_filelayout_segment *flseg = FILELAYOUT_LSEG(lseg);
675
676 if (flseg->stripe_type == STRIPE_SPARSE) {
677 if (flseg->num_fh == 1)
678 i = 0;
679 else if (flseg->num_fh == 0)
680 /* Use the MDS OPEN fh set in nfs_read_rpcsetup */
681 return NULL;
682 }
683 return flseg->fh_array[i];
684}
685
686static int filelayout_initiate_commit(struct nfs_write_data *data, int how)
687{
688 struct pnfs_layout_segment *lseg = data->lseg;
689 struct nfs4_pnfs_ds *ds;
690 u32 idx;
691 struct nfs_fh *fh;
692
693 idx = calc_ds_index_from_commit(lseg, data->ds_commit_index);
694 ds = nfs4_fl_prepare_ds(lseg, idx);
695 if (!ds) {
696 printk(KERN_ERR "%s: prepare_ds failed, use MDS\n", __func__);
697 set_bit(lo_fail_bit(IOMODE_RW), &lseg->pls_layout->plh_flags);
698 set_bit(lo_fail_bit(IOMODE_READ), &lseg->pls_layout->plh_flags);
699 prepare_to_resend_writes(data);
700 data->mds_ops->rpc_release(data);
701 return -EAGAIN;
702 }
703 dprintk("%s ino %lu, how %d\n", __func__, data->inode->i_ino, how);
704 data->write_done_cb = filelayout_commit_done_cb;
705 data->ds_clp = ds->ds_clp;
706 fh = select_ds_fh_from_commit(lseg, data->ds_commit_index);
707 if (fh)
708 data->args.fh = fh;
709 return nfs_initiate_commit(data, ds->ds_clp->cl_rpcclient,
710 &filelayout_commit_call_ops, how);
711}
712
713/*
714 * This is only useful while we are using whole file layouts.
715 */
716static struct pnfs_layout_segment *find_only_write_lseg(struct inode *inode)
717{
718 struct pnfs_layout_segment *lseg, *rv = NULL;
719
720 spin_lock(&inode->i_lock);
721 list_for_each_entry(lseg, &NFS_I(inode)->layout->plh_segs, pls_list)
722 if (lseg->pls_range.iomode == IOMODE_RW)
723 rv = get_lseg(lseg);
724 spin_unlock(&inode->i_lock);
725 return rv;
726}
727
728static int alloc_ds_commits(struct inode *inode, struct list_head *list)
729{
730 struct pnfs_layout_segment *lseg;
731 struct nfs4_filelayout_segment *fl;
732 struct nfs_write_data *data;
733 int i, j;
734
735 /* Won't need this when non-whole file layout segments are supported
736 * instead we will use a pnfs_layout_hdr structure */
737 lseg = find_only_write_lseg(inode);
738 if (!lseg)
739 return 0;
740 fl = FILELAYOUT_LSEG(lseg);
741 for (i = 0; i < fl->number_of_buckets; i++) {
742 if (list_empty(&fl->commit_buckets[i]))
743 continue;
744 data = nfs_commitdata_alloc();
745 if (!data)
746 goto out_bad;
747 data->ds_commit_index = i;
748 data->lseg = lseg;
749 list_add(&data->pages, list);
750 }
751 put_lseg(lseg);
752 return 0;
753
754out_bad:
755 for (j = i; j < fl->number_of_buckets; j++) {
756 if (list_empty(&fl->commit_buckets[i]))
757 continue;
758 nfs_retry_commit(&fl->commit_buckets[i], lseg);
759 put_lseg(lseg); /* associated with emptying bucket */
760 }
761 put_lseg(lseg);
762 /* Caller will clean up entries put on list */
763 return -ENOMEM;
764}
765
766/* This follows nfs_commit_list pretty closely */
767static int
768filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages,
769 int how)
770{
771 struct nfs_write_data *data, *tmp;
772 LIST_HEAD(list);
773
774 if (!list_empty(mds_pages)) {
775 data = nfs_commitdata_alloc();
776 if (!data)
777 goto out_bad;
778 data->lseg = NULL;
779 list_add(&data->pages, &list);
780 }
781
782 if (alloc_ds_commits(inode, &list))
783 goto out_bad;
784
785 list_for_each_entry_safe(data, tmp, &list, pages) {
786 list_del_init(&data->pages);
787 atomic_inc(&NFS_I(inode)->commits_outstanding);
788 if (!data->lseg) {
789 nfs_init_commit(data, mds_pages, NULL);
790 nfs_initiate_commit(data, NFS_CLIENT(inode),
791 data->mds_ops, how);
792 } else {
793 nfs_init_commit(data, &FILELAYOUT_LSEG(data->lseg)->commit_buckets[data->ds_commit_index], data->lseg);
794 filelayout_initiate_commit(data, how);
795 }
796 }
797 return 0;
798 out_bad:
799 list_for_each_entry_safe(data, tmp, &list, pages) {
800 nfs_retry_commit(&data->pages, data->lseg);
801 list_del_init(&data->pages);
802 nfs_commit_free(data);
803 }
804 nfs_retry_commit(mds_pages, NULL);
805 nfs_commit_clear_lock(NFS_I(inode));
806 return -ENOMEM;
807}
808
577static struct pnfs_layoutdriver_type filelayout_type = { 809static struct pnfs_layoutdriver_type filelayout_type = {
578 .id = LAYOUT_NFSV4_1_FILES, 810 .id = LAYOUT_NFSV4_1_FILES,
579 .name = "LAYOUT_NFSV4_1_FILES", 811 .name = "LAYOUT_NFSV4_1_FILES",
@@ -581,6 +813,9 @@ static struct pnfs_layoutdriver_type filelayout_type = {
581 .alloc_lseg = filelayout_alloc_lseg, 813 .alloc_lseg = filelayout_alloc_lseg,
582 .free_lseg = filelayout_free_lseg, 814 .free_lseg = filelayout_free_lseg,
583 .pg_test = filelayout_pg_test, 815 .pg_test = filelayout_pg_test,
816 .mark_pnfs_commit = filelayout_mark_pnfs_commit,
817 .choose_commit_list = filelayout_choose_commit_list,
818 .commit_pagelist = filelayout_commit_pagelist,
584 .read_pagelist = filelayout_read_pagelist, 819 .read_pagelist = filelayout_read_pagelist,
585 .write_pagelist = filelayout_write_pagelist, 820 .write_pagelist = filelayout_write_pagelist,
586}; 821};
diff --git a/fs/nfs/pnfs.c b/fs/nfs/pnfs.c
index f38813a0a295..c67565965f2a 100644
--- a/fs/nfs/pnfs.c
+++ b/fs/nfs/pnfs.c
@@ -259,6 +259,7 @@ put_lseg(struct pnfs_layout_segment *lseg)
259 pnfs_free_lseg_list(&free_me); 259 pnfs_free_lseg_list(&free_me);
260 } 260 }
261} 261}
262EXPORT_SYMBOL_GPL(put_lseg);
262 263
263static bool 264static bool
264should_free_lseg(u32 lseg_iomode, u32 recall_iomode) 265should_free_lseg(u32 lseg_iomode, u32 recall_iomode)
diff --git a/fs/nfs/write.c b/fs/nfs/write.c
index cae5d160d835..e7aeda0663c5 100644
--- a/fs/nfs/write.c
+++ b/fs/nfs/write.c
@@ -59,6 +59,7 @@ struct nfs_write_data *nfs_commitdata_alloc(void)
59 } 59 }
60 return p; 60 return p;
61} 61}
62EXPORT_SYMBOL_GPL(nfs_commitdata_alloc);
62 63
63void nfs_commit_free(struct nfs_write_data *p) 64void nfs_commit_free(struct nfs_write_data *p)
64{ 65{
@@ -66,6 +67,7 @@ void nfs_commit_free(struct nfs_write_data *p)
66 kfree(p->pagevec); 67 kfree(p->pagevec);
67 mempool_free(p, nfs_commit_mempool); 68 mempool_free(p, nfs_commit_mempool);
68} 69}
70EXPORT_SYMBOL_GPL(nfs_commit_free);
69 71
70struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount) 72struct nfs_write_data *nfs_writedata_alloc(unsigned int pagecount)
71{ 73{
@@ -1283,15 +1285,15 @@ static int nfs_commit_set_lock(struct nfs_inode *nfsi, int may_wait)
1283 return (ret < 0) ? ret : 1; 1285 return (ret < 0) ? ret : 1;
1284} 1286}
1285 1287
1286static void nfs_commit_clear_lock(struct nfs_inode *nfsi) 1288void nfs_commit_clear_lock(struct nfs_inode *nfsi)
1287{ 1289{
1288 clear_bit(NFS_INO_COMMIT, &nfsi->flags); 1290 clear_bit(NFS_INO_COMMIT, &nfsi->flags);
1289 smp_mb__after_clear_bit(); 1291 smp_mb__after_clear_bit();
1290 wake_up_bit(&nfsi->flags, NFS_INO_COMMIT); 1292 wake_up_bit(&nfsi->flags, NFS_INO_COMMIT);
1291} 1293}
1294EXPORT_SYMBOL_GPL(nfs_commit_clear_lock);
1292 1295
1293 1296void nfs_commitdata_release(void *data)
1294static void nfs_commitdata_release(void *data)
1295{ 1297{
1296 struct nfs_write_data *wdata = data; 1298 struct nfs_write_data *wdata = data;
1297 1299
@@ -1299,8 +1301,9 @@ static void nfs_commitdata_release(void *data)
1299 put_nfs_open_context(wdata->args.context); 1301 put_nfs_open_context(wdata->args.context);
1300 nfs_commit_free(wdata); 1302 nfs_commit_free(wdata);
1301} 1303}
1304EXPORT_SYMBOL_GPL(nfs_commitdata_release);
1302 1305
1303static int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *clnt, 1306int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *clnt,
1304 const struct rpc_call_ops *call_ops, 1307 const struct rpc_call_ops *call_ops,
1305 int how) 1308 int how)
1306{ 1309{
@@ -1334,11 +1337,12 @@ static int nfs_initiate_commit(struct nfs_write_data *data, struct rpc_clnt *cln
1334 rpc_put_task(task); 1337 rpc_put_task(task);
1335 return 0; 1338 return 0;
1336} 1339}
1340EXPORT_SYMBOL_GPL(nfs_initiate_commit);
1337 1341
1338/* 1342/*
1339 * Set up the argument/result storage required for the RPC call. 1343 * Set up the argument/result storage required for the RPC call.
1340 */ 1344 */
1341static void nfs_init_commit(struct nfs_write_data *data, 1345void nfs_init_commit(struct nfs_write_data *data,
1342 struct list_head *head, 1346 struct list_head *head,
1343 struct pnfs_layout_segment *lseg) 1347 struct pnfs_layout_segment *lseg)
1344{ 1348{
@@ -1365,8 +1369,9 @@ static void nfs_init_commit(struct nfs_write_data *data,
1365 data->res.verf = &data->verf; 1369 data->res.verf = &data->verf;
1366 nfs_fattr_init(&data->fattr); 1370 nfs_fattr_init(&data->fattr);
1367} 1371}
1372EXPORT_SYMBOL_GPL(nfs_init_commit);
1368 1373
1369static void nfs_retry_commit(struct list_head *page_list, 1374void nfs_retry_commit(struct list_head *page_list,
1370 struct pnfs_layout_segment *lseg) 1375 struct pnfs_layout_segment *lseg)
1371{ 1376{
1372 struct nfs_page *req; 1377 struct nfs_page *req;
@@ -1381,6 +1386,7 @@ static void nfs_retry_commit(struct list_head *page_list,
1381 nfs_clear_page_tag_locked(req); 1386 nfs_clear_page_tag_locked(req);
1382 } 1387 }
1383} 1388}
1389EXPORT_SYMBOL_GPL(nfs_retry_commit);
1384 1390
1385/* 1391/*
1386 * Commit dirty pages 1392 * Commit dirty pages
@@ -1419,7 +1425,7 @@ static void nfs_commit_done(struct rpc_task *task, void *calldata)
1419 return; 1425 return;
1420} 1426}
1421 1427
1422static void nfs_commit_release_pages(struct nfs_write_data *data) 1428void nfs_commit_release_pages(struct nfs_write_data *data)
1423{ 1429{
1424 struct nfs_page *req; 1430 struct nfs_page *req;
1425 int status = data->task.tk_status; 1431 int status = data->task.tk_status;
@@ -1456,6 +1462,7 @@ static void nfs_commit_release_pages(struct nfs_write_data *data)
1456 nfs_clear_page_tag_locked(req); 1462 nfs_clear_page_tag_locked(req);
1457 } 1463 }
1458} 1464}
1465EXPORT_SYMBOL_GPL(nfs_commit_release_pages);
1459 1466
1460static void nfs_commit_release(void *calldata) 1467static void nfs_commit_release(void *calldata)
1461{ 1468{