diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-01-25 16:38:18 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2008-01-30 02:06:12 -0500 |
commit | 57bfa89171e50cddf51a4f62c90e47c6259857b4 (patch) | |
tree | 2c3f6d45a61b4f888544d492d78fedd797001bc8 | |
parent | 6f23e3872cff238589f9bf39c71db2ea880c9a26 (diff) |
NFSv4: Deal more correctly with duplicate delegations
If a (broken?) server hands out two different delegations for the same
file, then we should return one of them.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/delegation.c | 89 |
1 files changed, 51 insertions, 38 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 2dead8d1dd55..b9eadd18ba70 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -125,6 +125,32 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st | |||
125 | put_rpccred(oldcred); | 125 | put_rpccred(oldcred); |
126 | } | 126 | } |
127 | 127 | ||
128 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | ||
129 | { | ||
130 | int res = 0; | ||
131 | |||
132 | res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); | ||
133 | nfs_free_delegation(delegation); | ||
134 | return res; | ||
135 | } | ||
136 | |||
137 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | ||
138 | { | ||
139 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | ||
140 | |||
141 | if (delegation == NULL) | ||
142 | goto nomatch; | ||
143 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | ||
144 | sizeof(delegation->stateid.data)) != 0) | ||
145 | goto nomatch; | ||
146 | list_del_rcu(&delegation->super_list); | ||
147 | nfsi->delegation_state = 0; | ||
148 | rcu_assign_pointer(nfsi->delegation, NULL); | ||
149 | return delegation; | ||
150 | nomatch: | ||
151 | return NULL; | ||
152 | } | ||
153 | |||
128 | /* | 154 | /* |
129 | * Set up a delegation on an inode | 155 | * Set up a delegation on an inode |
130 | */ | 156 | */ |
@@ -133,6 +159,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
133 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 159 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
134 | struct nfs_inode *nfsi = NFS_I(inode); | 160 | struct nfs_inode *nfsi = NFS_I(inode); |
135 | struct nfs_delegation *delegation; | 161 | struct nfs_delegation *delegation; |
162 | struct nfs_delegation *freeme = NULL; | ||
136 | int status = 0; | 163 | int status = 0; |
137 | 164 | ||
138 | delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); | 165 | delegation = kmalloc(sizeof(*delegation), GFP_KERNEL); |
@@ -147,42 +174,45 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
147 | delegation->inode = inode; | 174 | delegation->inode = inode; |
148 | 175 | ||
149 | spin_lock(&clp->cl_lock); | 176 | spin_lock(&clp->cl_lock); |
150 | if (rcu_dereference(nfsi->delegation) == NULL) { | 177 | if (rcu_dereference(nfsi->delegation) != NULL) { |
151 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | ||
152 | nfsi->delegation_state = delegation->type; | ||
153 | rcu_assign_pointer(nfsi->delegation, delegation); | ||
154 | delegation = NULL; | ||
155 | } else { | ||
156 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, | 178 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, |
157 | sizeof(delegation->stateid)) != 0 || | 179 | sizeof(delegation->stateid)) == 0 && |
158 | delegation->type != nfsi->delegation->type) { | 180 | delegation->type == nfsi->delegation->type) { |
159 | printk(KERN_WARNING "%s: server %s handed out " | 181 | goto out; |
160 | "a duplicate delegation!\n", | 182 | } |
161 | __FUNCTION__, clp->cl_hostname); | 183 | /* |
162 | status = -EIO; | 184 | * Deal with broken servers that hand out two |
185 | * delegations for the same file. | ||
186 | */ | ||
187 | dfprintk(FILE, "%s: server %s handed out " | ||
188 | "a duplicate delegation!\n", | ||
189 | __FUNCTION__, clp->cl_hostname); | ||
190 | if (delegation->type <= nfsi->delegation->type) { | ||
191 | freeme = delegation; | ||
192 | delegation = NULL; | ||
193 | goto out; | ||
163 | } | 194 | } |
195 | freeme = nfs_detach_delegation_locked(nfsi, NULL); | ||
164 | } | 196 | } |
197 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | ||
198 | nfsi->delegation_state = delegation->type; | ||
199 | rcu_assign_pointer(nfsi->delegation, delegation); | ||
200 | delegation = NULL; | ||
165 | 201 | ||
166 | /* Ensure we revalidate the attributes and page cache! */ | 202 | /* Ensure we revalidate the attributes and page cache! */ |
167 | spin_lock(&inode->i_lock); | 203 | spin_lock(&inode->i_lock); |
168 | nfsi->cache_validity |= NFS_INO_REVAL_FORCED; | 204 | nfsi->cache_validity |= NFS_INO_REVAL_FORCED; |
169 | spin_unlock(&inode->i_lock); | 205 | spin_unlock(&inode->i_lock); |
170 | 206 | ||
207 | out: | ||
171 | spin_unlock(&clp->cl_lock); | 208 | spin_unlock(&clp->cl_lock); |
172 | if (delegation != NULL) | 209 | if (delegation != NULL) |
173 | nfs_free_delegation(delegation); | 210 | nfs_free_delegation(delegation); |
211 | if (freeme != NULL) | ||
212 | nfs_do_return_delegation(inode, freeme, 0); | ||
174 | return status; | 213 | return status; |
175 | } | 214 | } |
176 | 215 | ||
177 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | ||
178 | { | ||
179 | int res = 0; | ||
180 | |||
181 | res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); | ||
182 | nfs_free_delegation(delegation); | ||
183 | return res; | ||
184 | } | ||
185 | |||
186 | /* Sync all data to disk upon delegation return */ | 216 | /* Sync all data to disk upon delegation return */ |
187 | static void nfs_msync_inode(struct inode *inode) | 217 | static void nfs_msync_inode(struct inode *inode) |
188 | { | 218 | { |
@@ -211,23 +241,6 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat | |||
211 | return nfs_do_return_delegation(inode, delegation, 1); | 241 | return nfs_do_return_delegation(inode, delegation, 1); |
212 | } | 242 | } |
213 | 243 | ||
214 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | ||
215 | { | ||
216 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | ||
217 | |||
218 | if (delegation == NULL) | ||
219 | goto nomatch; | ||
220 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | ||
221 | sizeof(delegation->stateid.data)) != 0) | ||
222 | goto nomatch; | ||
223 | list_del_rcu(&delegation->super_list); | ||
224 | nfsi->delegation_state = 0; | ||
225 | rcu_assign_pointer(nfsi->delegation, NULL); | ||
226 | return delegation; | ||
227 | nomatch: | ||
228 | return NULL; | ||
229 | } | ||
230 | |||
231 | /* | 244 | /* |
232 | * This function returns the delegation without reclaiming opens | 245 | * This function returns the delegation without reclaiming opens |
233 | * or protecting against delegation reclaims. | 246 | * or protecting against delegation reclaims. |