Skip to content

Commit 9167746

Browse files
committed
Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6: NFS: Fix RCU issues in the NFSv4 delegation code NFSv4: Fix the locking in nfs_inode_reclaim_delegation()
2 parents 4a22533 + 17d2c0a commit 9167746

File tree

1 file changed

+51
-35
lines changed

1 file changed

+51
-35
lines changed

fs/nfs/delegation.c

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
static void nfs_do_free_delegation(struct nfs_delegation *delegation)
2626
{
27+
if (delegation->cred)
28+
put_rpccred(delegation->cred);
2729
kfree(delegation);
2830
}
2931

@@ -36,13 +38,7 @@ static void nfs_free_delegation_callback(struct rcu_head *head)
3638

3739
static void nfs_free_delegation(struct nfs_delegation *delegation)
3840
{
39-
struct rpc_cred *cred;
40-
41-
cred = rcu_dereference(delegation->cred);
42-
rcu_assign_pointer(delegation->cred, NULL);
4341
call_rcu(&delegation->rcu, nfs_free_delegation_callback);
44-
if (cred)
45-
put_rpccred(cred);
4642
}
4743

4844
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
@@ -129,21 +125,35 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
129125
*/
130126
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
131127
{
132-
struct nfs_delegation *delegation = NFS_I(inode)->delegation;
133-
struct rpc_cred *oldcred;
128+
struct nfs_delegation *delegation;
129+
struct rpc_cred *oldcred = NULL;
134130

135-
if (delegation == NULL)
136-
return;
137-
memcpy(delegation->stateid.data, res->delegation.data,
138-
sizeof(delegation->stateid.data));
139-
delegation->type = res->delegation_type;
140-
delegation->maxsize = res->maxsize;
141-
oldcred = delegation->cred;
142-
delegation->cred = get_rpccred(cred);
143-
clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
144-
NFS_I(inode)->delegation_state = delegation->type;
145-
smp_wmb();
146-
put_rpccred(oldcred);
131+
rcu_read_lock();
132+
delegation = rcu_dereference(NFS_I(inode)->delegation);
133+
if (delegation != NULL) {
134+
spin_lock(&delegation->lock);
135+
if (delegation->inode != NULL) {
136+
memcpy(delegation->stateid.data, res->delegation.data,
137+
sizeof(delegation->stateid.data));
138+
delegation->type = res->delegation_type;
139+
delegation->maxsize = res->maxsize;
140+
oldcred = delegation->cred;
141+
delegation->cred = get_rpccred(cred);
142+
clear_bit(NFS_DELEGATION_NEED_RECLAIM,
143+
&delegation->flags);
144+
NFS_I(inode)->delegation_state = delegation->type;
145+
spin_unlock(&delegation->lock);
146+
put_rpccred(oldcred);
147+
rcu_read_unlock();
148+
} else {
149+
/* We appear to have raced with a delegation return. */
150+
spin_unlock(&delegation->lock);
151+
rcu_read_unlock();
152+
nfs_inode_set_delegation(inode, cred, res);
153+
}
154+
} else {
155+
rcu_read_unlock();
156+
}
147157
}
148158

149159
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
@@ -166,9 +176,13 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation
166176
return inode;
167177
}
168178

169-
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
179+
static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi,
180+
const nfs4_stateid *stateid,
181+
struct nfs_client *clp)
170182
{
171-
struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
183+
struct nfs_delegation *delegation =
184+
rcu_dereference_protected(nfsi->delegation,
185+
lockdep_is_held(&clp->cl_lock));
172186

173187
if (delegation == NULL)
174188
goto nomatch;
@@ -195,7 +209,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
195209
{
196210
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
197211
struct nfs_inode *nfsi = NFS_I(inode);
198-
struct nfs_delegation *delegation;
212+
struct nfs_delegation *delegation, *old_delegation;
199213
struct nfs_delegation *freeme = NULL;
200214
int status = 0;
201215

@@ -213,10 +227,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
213227
spin_lock_init(&delegation->lock);
214228

215229
spin_lock(&clp->cl_lock);
216-
if (rcu_dereference(nfsi->delegation) != NULL) {
217-
if (memcmp(&delegation->stateid, &nfsi->delegation->stateid,
218-
sizeof(delegation->stateid)) == 0 &&
219-
delegation->type == nfsi->delegation->type) {
230+
old_delegation = rcu_dereference_protected(nfsi->delegation,
231+
lockdep_is_held(&clp->cl_lock));
232+
if (old_delegation != NULL) {
233+
if (memcmp(&delegation->stateid, &old_delegation->stateid,
234+
sizeof(old_delegation->stateid)) == 0 &&
235+
delegation->type == old_delegation->type) {
220236
goto out;
221237
}
222238
/*
@@ -226,12 +242,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
226242
dfprintk(FILE, "%s: server %s handed out "
227243
"a duplicate delegation!\n",
228244
__func__, clp->cl_hostname);
229-
if (delegation->type <= nfsi->delegation->type) {
245+
if (delegation->type <= old_delegation->type) {
230246
freeme = delegation;
231247
delegation = NULL;
232248
goto out;
233249
}
234-
freeme = nfs_detach_delegation_locked(nfsi, NULL);
250+
freeme = nfs_detach_delegation_locked(nfsi, NULL, clp);
235251
}
236252
list_add_rcu(&delegation->super_list, &clp->cl_delegations);
237253
nfsi->delegation_state = delegation->type;
@@ -301,7 +317,7 @@ int nfs_client_return_marked_delegations(struct nfs_client *clp)
301317
if (inode == NULL)
302318
continue;
303319
spin_lock(&clp->cl_lock);
304-
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
320+
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
305321
spin_unlock(&clp->cl_lock);
306322
rcu_read_unlock();
307323
if (delegation != NULL) {
@@ -330,9 +346,9 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)
330346
struct nfs_inode *nfsi = NFS_I(inode);
331347
struct nfs_delegation *delegation;
332348

333-
if (rcu_dereference(nfsi->delegation) != NULL) {
349+
if (rcu_access_pointer(nfsi->delegation) != NULL) {
334350
spin_lock(&clp->cl_lock);
335-
delegation = nfs_detach_delegation_locked(nfsi, NULL);
351+
delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
336352
spin_unlock(&clp->cl_lock);
337353
if (delegation != NULL)
338354
nfs_do_return_delegation(inode, delegation, 0);
@@ -346,9 +362,9 @@ int nfs_inode_return_delegation(struct inode *inode)
346362
struct nfs_delegation *delegation;
347363
int err = 0;
348364

349-
if (rcu_dereference(nfsi->delegation) != NULL) {
365+
if (rcu_access_pointer(nfsi->delegation) != NULL) {
350366
spin_lock(&clp->cl_lock);
351-
delegation = nfs_detach_delegation_locked(nfsi, NULL);
367+
delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);
352368
spin_unlock(&clp->cl_lock);
353369
if (delegation != NULL) {
354370
nfs_msync_inode(inode);
@@ -526,7 +542,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
526542
if (inode == NULL)
527543
continue;
528544
spin_lock(&clp->cl_lock);
529-
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
545+
delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);
530546
spin_unlock(&clp->cl_lock);
531547
rcu_read_unlock();
532548
if (delegation != NULL)

0 commit comments

Comments
 (0)