Skip to content

Commit ee05f45

Browse files
author
Trond Myklebust
committed
NFSv4: Fix races between open and delegreturn
If the server returns the same delegation in an open that we just used in a delegreturn, we need to ensure we don't apply that stateid if the delegreturn has freed it on the server. To do so, we ensure that we do not free the storage for the delegation until either it is replaced by a new one, or we throw the inode out of cache. Signed-off-by: Trond Myklebust <[email protected]>
1 parent 42c304c commit ee05f45

File tree

1 file changed

+29
-35
lines changed

1 file changed

+29
-35
lines changed

fs/nfs/delegation.c

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,6 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
229229
delegation->cred,
230230
&delegation->stateid,
231231
issync);
232-
nfs_free_delegation(delegation);
233232
return res;
234233
}
235234

@@ -302,7 +301,6 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi,
302301
spin_unlock(&delegation->lock);
303302
return NULL;
304303
}
305-
set_bit(NFS_DELEGATION_RETURNING, &delegation->flags);
306304
list_del_rcu(&delegation->super_list);
307305
delegation->inode = NULL;
308306
rcu_assign_pointer(nfsi->delegation, NULL);
@@ -329,10 +327,12 @@ nfs_inode_detach_delegation(struct inode *inode)
329327
struct nfs_server *server = NFS_SERVER(inode);
330328
struct nfs_delegation *delegation;
331329

332-
delegation = nfs_start_delegation_return(nfsi);
333-
if (delegation == NULL)
334-
return NULL;
335-
return nfs_detach_delegation(nfsi, delegation, server);
330+
rcu_read_lock();
331+
delegation = rcu_dereference(nfsi->delegation);
332+
if (delegation != NULL)
333+
delegation = nfs_detach_delegation(nfsi, delegation, server);
334+
rcu_read_unlock();
335+
return delegation;
336336
}
337337

338338
static void
@@ -384,16 +384,18 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
384384
spin_lock(&clp->cl_lock);
385385
old_delegation = rcu_dereference_protected(nfsi->delegation,
386386
lockdep_is_held(&clp->cl_lock));
387-
if (old_delegation != NULL) {
388-
/* Is this an update of the existing delegation? */
389-
if (nfs4_stateid_match_other(&old_delegation->stateid,
390-
&delegation->stateid)) {
391-
spin_lock(&old_delegation->lock);
392-
nfs_update_inplace_delegation(old_delegation,
393-
delegation);
394-
spin_unlock(&old_delegation->lock);
395-
goto out;
396-
}
387+
if (old_delegation == NULL)
388+
goto add_new;
389+
/* Is this an update of the existing delegation? */
390+
if (nfs4_stateid_match_other(&old_delegation->stateid,
391+
&delegation->stateid)) {
392+
spin_lock(&old_delegation->lock);
393+
nfs_update_inplace_delegation(old_delegation,
394+
delegation);
395+
spin_unlock(&old_delegation->lock);
396+
goto out;
397+
}
398+
if (!test_bit(NFS_DELEGATION_REVOKED, &old_delegation->flags)) {
397399
/*
398400
* Deal with broken servers that hand out two
399401
* delegations for the same file.
@@ -412,11 +414,11 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
412414
if (test_and_set_bit(NFS_DELEGATION_RETURNING,
413415
&old_delegation->flags))
414416
goto out;
415-
freeme = nfs_detach_delegation_locked(nfsi,
416-
old_delegation, clp);
417-
if (freeme == NULL)
418-
goto out;
419417
}
418+
freeme = nfs_detach_delegation_locked(nfsi, old_delegation, clp);
419+
if (freeme == NULL)
420+
goto out;
421+
add_new:
420422
list_add_tail_rcu(&delegation->super_list, &server->delegations);
421423
rcu_assign_pointer(nfsi->delegation, delegation);
422424
delegation = NULL;
@@ -431,8 +433,10 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
431433
spin_unlock(&clp->cl_lock);
432434
if (delegation != NULL)
433435
nfs_free_delegation(delegation);
434-
if (freeme != NULL)
436+
if (freeme != NULL) {
435437
nfs_do_return_delegation(inode, freeme, 0);
438+
nfs_free_delegation(freeme);
439+
}
436440
return status;
437441
}
438442

@@ -442,7 +446,6 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
442446
static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync)
443447
{
444448
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
445-
struct nfs_inode *nfsi = NFS_I(inode);
446449
int err = 0;
447450

448451
if (delegation == NULL)
@@ -464,8 +467,6 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
464467
nfs_abort_delegation_return(delegation, clp);
465468
goto out;
466469
}
467-
if (!nfs_detach_delegation(nfsi, delegation, NFS_SERVER(inode)))
468-
goto out;
469470

470471
err = nfs_do_return_delegation(inode, delegation, issync);
471472
out:
@@ -608,6 +609,7 @@ void nfs_inode_evict_delegation(struct inode *inode)
608609
if (delegation != NULL) {
609610
set_bit(NFS_DELEGATION_INODE_FREEING, &delegation->flags);
610611
nfs_do_return_delegation(inode, delegation, 1);
612+
nfs_free_delegation(delegation);
611613
}
612614
}
613615

@@ -763,10 +765,9 @@ static void nfs_mark_delegation_revoked(struct nfs_server *server,
763765
{
764766
set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
765767
delegation->stateid.type = NFS4_INVALID_STATEID_TYPE;
766-
nfs_mark_return_delegation(server, delegation);
767768
}
768769

769-
static bool nfs_revoke_delegation(struct inode *inode,
770+
static void nfs_revoke_delegation(struct inode *inode,
770771
const nfs4_stateid *stateid)
771772
{
772773
struct nfs_delegation *delegation;
@@ -799,19 +800,12 @@ static bool nfs_revoke_delegation(struct inode *inode,
799800
rcu_read_unlock();
800801
if (ret)
801802
nfs_inode_find_state_and_recover(inode, stateid);
802-
return ret;
803803
}
804804

805805
void nfs_remove_bad_delegation(struct inode *inode,
806806
const nfs4_stateid *stateid)
807807
{
808-
struct nfs_delegation *delegation;
809-
810-
if (!nfs_revoke_delegation(inode, stateid))
811-
return;
812-
delegation = nfs_inode_detach_delegation(inode);
813-
if (delegation)
814-
nfs_free_delegation(delegation);
808+
nfs_revoke_delegation(inode, stateid);
815809
}
816810
EXPORT_SYMBOL_GPL(nfs_remove_bad_delegation);
817811

@@ -839,7 +833,7 @@ void nfs_delegation_mark_returned(struct inode *inode,
839833
delegation->stateid.seqid = stateid->seqid;
840834
}
841835

842-
set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
836+
nfs_mark_delegation_revoked(NFS_SERVER(inode), delegation);
843837

844838
out_clear_returning:
845839
clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags);

0 commit comments

Comments
 (0)