From: NeilBrown The delegation recall callback is setting the REAP_DELEGATION state when it drops the reference count to zero instead of just freeing the thing itself, which is needlessly complicated and bug-prone. It's simpler just to define a nfs4_put_delegation() which works in the usual way and have the delegation recall code do call that itself. Eventually I'll convert all the nfsd4 state reference counts to struct krefs which will be harder to abuse in this way.... Signed-off-by: J. Bruce Fields Signed-off-by: Neil Brown Signed-off-by: Andrew Morton --- 25-akpm/fs/nfsd/nfs4callback.c | 6 +----- 25-akpm/fs/nfsd/nfs4state.c | 27 ++++++++++----------------- 25-akpm/include/linux/nfsd/state.h | 1 + 3 files changed, 12 insertions(+), 22 deletions(-) diff -puN fs/nfsd/nfs4callback.c~nfsd4-fix-delegation-refcounting fs/nfsd/nfs4callback.c --- 25/fs/nfsd/nfs4callback.c~nfsd4-fix-delegation-refcounting 2005-03-07 23:55:54.000000000 -0800 +++ 25-akpm/fs/nfsd/nfs4callback.c 2005-03-07 23:55:54.000000000 -0800 @@ -541,11 +541,7 @@ nfsd4_cb_recall(struct nfs4_delegation * /* Success or failure, now we're either waiting for lease expiration * or deleg_return. */ atomic_set(&dp->dl_state, NFS4_RECALL_COMPLETE); - - if (atomic_dec_and_test(&dp->dl_count)) - atomic_set(&dp->dl_state, NFS4_REAP_DELEG); - BUG_ON(atomic_read(&dp->dl_count) < 0); - + nfs4_put_delegation(dp); dprintk("NFSD: nfs4_cb_recall: dp %p dl_flock %p dl_count %d\n",dp, dp->dl_flock, atomic_read(&dp->dl_count)); put_rpccred(msg.rpc_cred); return; diff -puN fs/nfsd/nfs4state.c~nfsd4-fix-delegation-refcounting fs/nfsd/nfs4state.c --- 25/fs/nfsd/nfs4state.c~nfsd4-fix-delegation-refcounting 2005-03-07 23:55:54.000000000 -0800 +++ 25-akpm/fs/nfsd/nfs4state.c 2005-03-07 23:55:54.000000000 -0800 @@ -167,17 +167,14 @@ alloc_init_deleg(struct nfs4_client *clp return dp; } -/* - * Free the delegation structure. - * Called with the recall_lock held. - */ -static void -nfs4_free_delegation(struct nfs4_delegation *dp) +void +nfs4_put_delegation(struct nfs4_delegation *dp) { - dprintk("NFSD: nfs4_free_delegation freeing dp %p\n",dp); - list_del(&dp->dl_recall_lru); - kfree(dp); - free_delegation++; + if (atomic_dec_and_test(&dp->dl_count)) { + dprintk("NFSD: freeing dp %p\n",dp); + kfree(dp); + free_delegation++; + } } /* release_delegation: @@ -219,12 +216,8 @@ release_delegation(struct nfs4_delegatio remove_lease(dp->dl_flock); list_del_init(&dp->dl_del_perfile); list_del_init(&dp->dl_del_perclnt); - /* dl_count > 0 => outstanding recall rpc */ - dprintk("NFSD: release_delegation free deleg dl_count %d\n", - atomic_read(&dp->dl_count)); - if ((atomic_read(&dp->dl_state) == NFS4_REAP_DELEG) - || atomic_dec_and_test(&dp->dl_count)) - nfs4_free_delegation(dp); + list_del_init(&dp->dl_recall_lru); + nfs4_put_delegation(dp); } } @@ -1692,7 +1685,7 @@ nfs4_open_delegation(struct svc_fh *fh, dprintk("NFSD: setlease failed [%d], no delegation\n", status); list_del(&dp->dl_del_perfile); list_del(&dp->dl_del_perclnt); - kfree(dp); + nfs4_put_delegation(dp); free_delegation++; *flag = NFS4_OPEN_DELEGATE_NONE; return; diff -puN include/linux/nfsd/state.h~nfsd4-fix-delegation-refcounting include/linux/nfsd/state.h --- 25/include/linux/nfsd/state.h~nfsd4-fix-delegation-refcounting 2005-03-07 23:55:54.000000000 -0800 +++ 25-akpm/include/linux/nfsd/state.h 2005-03-07 23:55:54.000000000 -0800 @@ -288,6 +288,7 @@ extern void put_nfs4_client(struct nfs4_ extern void nfs4_free_stateowner(struct kref *kref); extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_cb_recall(struct nfs4_delegation *dp); +extern void nfs4_put_delegation(struct nfs4_delegation *dp); static inline void nfs4_put_stateowner(struct nfs4_stateowner *so) _