aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/nfs/delegation.c89
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
128static 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
137static 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;
150nomatch:
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
207out:
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
177static 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 */
187static void nfs_msync_inode(struct inode *inode) 217static 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
214static 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;
227nomatch:
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.