diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-03-16 13:52:45 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2012-03-17 11:17:42 -0400 |
commit | 9390f42546339cf111edd23c16d6cf74ca41974c (patch) | |
tree | f281aa1e7bed7fbf41c441eab605d6d7a1a9050b /fs/nfs | |
parent | 8dd3775889345850ecddd689b5c200cdd91bd8c9 (diff) |
NFSv4.1: Fix a few issues in filelayout_commit_pagelist
- Fix a race in which NFS_I(inode)->commits_outstanding could potentially
go to zero (triggering a call to nfs_commit_clear_lock()) before we're
done sending out all the commit RPC calls.
- If nfs_commitdata_alloc fails, there is no reason why we shouldn't
try to send off all the commits-to-ds.
- Simplify the error handling.
- Change pnfs_commit_list() to always return either
PNFS_ATTEMPTED or PNFS_NOT_ATTEMPTED.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Cc: Fred Isaman <iisaman@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/nfs4filelayout.c | 47 |
1 files changed, 24 insertions, 23 deletions
diff --git a/fs/nfs/nfs4filelayout.c b/fs/nfs/nfs4filelayout.c index c24e077c2820..e0bdbf4fe454 100644 --- a/fs/nfs/nfs4filelayout.c +++ b/fs/nfs/nfs4filelayout.c | |||
@@ -980,12 +980,14 @@ out_done: | |||
980 | return rv; | 980 | return rv; |
981 | } | 981 | } |
982 | 982 | ||
983 | static int alloc_ds_commits(struct inode *inode, struct list_head *list) | 983 | static unsigned int |
984 | alloc_ds_commits(struct inode *inode, struct list_head *list) | ||
984 | { | 985 | { |
985 | struct pnfs_layout_segment *lseg; | 986 | struct pnfs_layout_segment *lseg; |
986 | struct nfs4_filelayout_segment *fl; | 987 | struct nfs4_filelayout_segment *fl; |
987 | struct nfs_write_data *data; | 988 | struct nfs_write_data *data; |
988 | int i, j; | 989 | int i, j; |
990 | unsigned int nreq = 0; | ||
989 | 991 | ||
990 | /* Won't need this when non-whole file layout segments are supported | 992 | /* Won't need this when non-whole file layout segments are supported |
991 | * instead we will use a pnfs_layout_hdr structure */ | 993 | * instead we will use a pnfs_layout_hdr structure */ |
@@ -998,15 +1000,14 @@ static int alloc_ds_commits(struct inode *inode, struct list_head *list) | |||
998 | continue; | 1000 | continue; |
999 | data = nfs_commitdata_alloc(); | 1001 | data = nfs_commitdata_alloc(); |
1000 | if (!data) | 1002 | if (!data) |
1001 | goto out_bad; | 1003 | break; |
1002 | data->ds_commit_index = i; | 1004 | data->ds_commit_index = i; |
1003 | data->lseg = lseg; | 1005 | data->lseg = lseg; |
1004 | list_add(&data->pages, list); | 1006 | list_add(&data->pages, list); |
1007 | nreq++; | ||
1005 | } | 1008 | } |
1006 | put_lseg(lseg); | ||
1007 | return 0; | ||
1008 | 1009 | ||
1009 | out_bad: | 1010 | /* Clean up on error */ |
1010 | for (j = i; j < fl->number_of_buckets; j++) { | 1011 | for (j = i; j < fl->number_of_buckets; j++) { |
1011 | if (list_empty(&fl->commit_buckets[i].committing)) | 1012 | if (list_empty(&fl->commit_buckets[i].committing)) |
1012 | continue; | 1013 | continue; |
@@ -1015,7 +1016,7 @@ out_bad: | |||
1015 | } | 1016 | } |
1016 | put_lseg(lseg); | 1017 | put_lseg(lseg); |
1017 | /* Caller will clean up entries put on list */ | 1018 | /* Caller will clean up entries put on list */ |
1018 | return -ENOMEM; | 1019 | return nreq; |
1019 | } | 1020 | } |
1020 | 1021 | ||
1021 | /* This follows nfs_commit_list pretty closely */ | 1022 | /* This follows nfs_commit_list pretty closely */ |
@@ -1025,21 +1026,29 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, | |||
1025 | { | 1026 | { |
1026 | struct nfs_write_data *data, *tmp; | 1027 | struct nfs_write_data *data, *tmp; |
1027 | LIST_HEAD(list); | 1028 | LIST_HEAD(list); |
1029 | unsigned int nreq = 0; | ||
1028 | 1030 | ||
1029 | if (!list_empty(mds_pages)) { | 1031 | if (!list_empty(mds_pages)) { |
1030 | data = nfs_commitdata_alloc(); | 1032 | data = nfs_commitdata_alloc(); |
1031 | if (!data) | 1033 | if (data != NULL) { |
1032 | goto out_bad; | 1034 | data->lseg = NULL; |
1033 | data->lseg = NULL; | 1035 | list_add(&data->pages, &list); |
1034 | list_add(&data->pages, &list); | 1036 | nreq++; |
1037 | } else | ||
1038 | nfs_retry_commit(mds_pages, NULL); | ||
1035 | } | 1039 | } |
1036 | 1040 | ||
1037 | if (alloc_ds_commits(inode, &list)) | 1041 | nreq += alloc_ds_commits(inode, &list); |
1038 | goto out_bad; | 1042 | |
1043 | if (nreq == 0) { | ||
1044 | nfs_commit_clear_lock(NFS_I(inode)); | ||
1045 | goto out; | ||
1046 | } | ||
1047 | |||
1048 | atomic_add(nreq, &NFS_I(inode)->commits_outstanding); | ||
1039 | 1049 | ||
1040 | list_for_each_entry_safe(data, tmp, &list, pages) { | 1050 | list_for_each_entry_safe(data, tmp, &list, pages) { |
1041 | list_del_init(&data->pages); | 1051 | list_del_init(&data->pages); |
1042 | atomic_inc(&NFS_I(inode)->commits_outstanding); | ||
1043 | if (!data->lseg) { | 1052 | if (!data->lseg) { |
1044 | nfs_init_commit(data, mds_pages, NULL); | 1053 | nfs_init_commit(data, mds_pages, NULL); |
1045 | nfs_initiate_commit(data, NFS_CLIENT(inode), | 1054 | nfs_initiate_commit(data, NFS_CLIENT(inode), |
@@ -1049,16 +1058,8 @@ filelayout_commit_pagelist(struct inode *inode, struct list_head *mds_pages, | |||
1049 | filelayout_initiate_commit(data, how); | 1058 | filelayout_initiate_commit(data, how); |
1050 | } | 1059 | } |
1051 | } | 1060 | } |
1052 | return 0; | 1061 | out: |
1053 | out_bad: | 1062 | return PNFS_ATTEMPTED; |
1054 | list_for_each_entry_safe(data, tmp, &list, pages) { | ||
1055 | nfs_retry_commit(&data->pages, data->lseg); | ||
1056 | list_del_init(&data->pages); | ||
1057 | nfs_commit_free(data); | ||
1058 | } | ||
1059 | nfs_retry_commit(mds_pages, NULL); | ||
1060 | nfs_commit_clear_lock(NFS_I(inode)); | ||
1061 | return -ENOMEM; | ||
1062 | } | 1063 | } |
1063 | 1064 | ||
1064 | static void | 1065 | static void |