From: NeilBrown Any task can call break_lease or time_out_leases on a v4 delegated file which results in a recall callback rpc sent to the NFSv4 client holding the delegation, and/or the FL_LEASE to be removed. Spawn a kernel thread to perform the callback. Signed-off-by: Andy Adamson Signed-off-by: Neil Brown Signed-off-by: Andrew Morton --- 25-akpm/fs/nfsd/nfs4state.c | 96 ++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 96 insertions(+) diff -puN fs/nfsd/nfs4state.c~knfsd-kernel-thread-for-delegation-callback fs/nfsd/nfs4state.c --- 25/fs/nfsd/nfs4state.c~knfsd-kernel-thread-for-delegation-callback Fri Dec 17 15:08:40 2004 +++ 25-akpm/fs/nfsd/nfs4state.c Fri Dec 17 15:08:40 2004 @@ -44,6 +44,7 @@ #include #include #include +#include #include #include #include @@ -1311,6 +1312,101 @@ nfs4_file_downgrade(struct file *filp, u } } +/* + * Recall a delegation + */ +static int +do_recall(void *__dp) +{ + struct nfs4_delegation *dp = __dp; + + daemonize("nfsv4-recall"); + + atomic_inc(&dp->dl_count); + nfsd4_cb_recall(dp); + return 0; +} + +/* + * Spawn a thread to perform a recall on the delegation represented + * by the lease (file_lock) + * + * Called from break_lease() with lock_kernel() held, + * + */ +static +void nfsd_break_deleg_cb(struct file_lock *fl) +{ + struct nfs4_delegation *dp= (struct nfs4_delegation *)fl->fl_owner; + struct task_struct *t; + + dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl); + if (!dp) + return; + + /* schedule delegation for recall */ + spin_lock(&recall_lock); + atomic_set(&dp->dl_state, NFS4_RECALL_IN_PROGRESS); + list_add_tail(&dp->dl_recall_lru, &del_recall_lru); + spin_unlock(&recall_lock); + + /* only place dl_time is set. protected by lock_kernel*/ + dp->dl_time = get_seconds(); + + /* XXX need to merge NFSD_LEASE_TIME with fs/locks.c:lease_break_time */ + fl->fl_break_time = jiffies + NFSD_LEASE_TIME * HZ; + + t = kthread_run(do_recall, dp, "%s", "nfs4_cb_recall"); + if (IS_ERR(t)) { + struct nfs4_client *clp = dp->dl_client; + + printk(KERN_INFO "NFSD: Callback thread failed for " + "for client (clientid %08x/%08x)\n", + clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); + } +} + +/* + * The file_lock is being reapd. + * + * Called by locks_free_lock() with lock_kernel() held. + */ +static +void nfsd_release_deleg_cb(struct file_lock *fl) +{ + struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; + + dprintk("NFSD nfsd_release_deleg_cb: fl %p dp %p dl_count %d, dl_state %d\n", fl,dp, atomic_read(&dp->dl_count), atomic_read(&dp->dl_state)); + + if (!(fl->fl_flags & FL_LEASE) || !dp) + return; + atomic_set(&dp->dl_state,NFS4_RECALL_COMPLETE); + dp->dl_flock = NULL; +} + +/* + * Set the delegation file_lock back pointer. + * + * Called from __setlease() with lock_kernel() held. + */ +static +void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) +{ + struct nfs4_delegation *dp = (struct nfs4_delegation *)new->fl_owner; + + dprintk("NFSD: nfsd_copy_lock_deleg_cb: new fl %p dp %p\n", new, dp); + if (!dp) + return; + dp->dl_flock = new; +} + +struct lock_manager_operations nfsd_lease_mng_ops = { + .fl_break = nfsd_break_deleg_cb, + .fl_release_private = nfsd_release_deleg_cb, + .fl_copy_lock = nfsd_copy_lock_deleg_cb, +}; + + /* * nfsd4_process_open1() _