aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@citi.umich.edu>2010-03-03 14:52:55 -0500
committerJ. Bruce Fields <bfields@citi.umich.edu>2010-04-22 11:34:01 -0400
commitb5a1a81e5c25fb6bb3fdc1812ba69ff6ab638fcf (patch)
treec524a75d111f4060eb985161478362ac18c17169
parent3c4ab2aaa90826060b1e8d4036f9bb8325f8759e (diff)
nfsd4: don't sleep in lease-break callback
The NFSv4 server's fl_break callback can sleep (dropping the BKL), in order to allocate a new rpc task to send a recall to the client. As far as I can tell this doesn't cause any races in the current code, but the analysis is difficult. Also, the sleep here may complicate the move away from the BKL. So, just schedule some work to do the job for us instead. The work will later also prove useful for restarting a call after the callback information is changed. Signed-off-by: J. Bruce Fields <bfields@citi.umich.edu>
-rw-r--r--fs/nfsd/nfs4callback.c54
-rw-r--r--fs/nfsd/nfs4state.c34
-rw-r--r--fs/nfsd/state.h5
3 files changed, 73 insertions, 20 deletions
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 91eb2ea9ef0a..e078c747f49d 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -32,6 +32,7 @@
32 */ 32 */
33 33
34#include <linux/sunrpc/clnt.h> 34#include <linux/sunrpc/clnt.h>
35#include <linux/sunrpc/svc_xprt.h>
35#include "nfsd.h" 36#include "nfsd.h"
36#include "state.h" 37#include "state.h"
37 38
@@ -692,11 +693,41 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = {
692 .rpc_release = nfsd4_cb_recall_release, 693 .rpc_release = nfsd4_cb_recall_release,
693}; 694};
694 695
696static struct workqueue_struct *callback_wq;
697
698int nfsd4_create_callback_queue(void)
699{
700 callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
701 if (!callback_wq)
702 return -ENOMEM;
703 return 0;
704}
705
706void nfsd4_destroy_callback_queue(void)
707{
708 destroy_workqueue(callback_wq);
709}
710
711void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt
712*new)
713{
714 struct rpc_clnt *old = clp->cl_cb_conn.cb_client;
715
716 clp->cl_cb_conn.cb_client = new;
717 /*
718 * After this, any work that saw the old value of cb_client will
719 * be gone:
720 */
721 flush_workqueue(callback_wq);
722 /* So we can safely shut it down: */
723 if (old)
724 rpc_shutdown_client(old);
725}
726
695/* 727/*
696 * called with dp->dl_count inc'ed. 728 * called with dp->dl_count inc'ed.
697 */ 729 */
698void 730static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
699nfsd4_cb_recall(struct nfs4_delegation *dp)
700{ 731{
701 struct nfs4_client *clp = dp->dl_client; 732 struct nfs4_client *clp = dp->dl_client;
702 struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client; 733 struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
@@ -707,6 +738,9 @@ nfsd4_cb_recall(struct nfs4_delegation *dp)
707 }; 738 };
708 int status; 739 int status;
709 740
741 if (clnt == NULL)
742 return; /* Client is shutting down; give up. */
743
710 args->args_op = dp; 744 args->args_op = dp;
711 msg.rpc_argp = args; 745 msg.rpc_argp = args;
712 dp->dl_retries = 1; 746 dp->dl_retries = 1;
@@ -717,3 +751,19 @@ nfsd4_cb_recall(struct nfs4_delegation *dp)
717 nfs4_put_delegation(dp); 751 nfs4_put_delegation(dp);
718 } 752 }
719} 753}
754
755void nfsd4_do_callback_rpc(struct work_struct *w)
756{
757 /* XXX: for now, just send off delegation recall. */
758 /* In future, generalize to handle any sort of callback. */
759 struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work);
760 struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall);
761
762 _nfsd4_cb_recall(dp);
763}
764
765
766void nfsd4_cb_recall(struct nfs4_delegation *dp)
767{
768 queue_work(callback_wq, &dp->dl_recall.cb_work);
769}
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 5051ade30dfb..adc51d10d435 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -198,6 +198,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
198 atomic_set(&dp->dl_count, 1); 198 atomic_set(&dp->dl_count, 1);
199 list_add(&dp->dl_perfile, &fp->fi_delegations); 199 list_add(&dp->dl_perfile, &fp->fi_delegations);
200 list_add(&dp->dl_perclnt, &clp->cl_delegations); 200 list_add(&dp->dl_perclnt, &clp->cl_delegations);
201 INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc);
201 return dp; 202 return dp;
202} 203}
203 204
@@ -679,21 +680,6 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
679 return clp; 680 return clp;
680} 681}
681 682
682static void
683shutdown_callback_client(struct nfs4_client *clp)
684{
685 struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
686
687 if (clnt) {
688 /*
689 * Callback threads take a reference on the client, so there
690 * should be no outstanding callbacks at this point.
691 */
692 clp->cl_cb_conn.cb_client = NULL;
693 rpc_shutdown_client(clnt);
694 }
695}
696
697static inline void 683static inline void
698free_client(struct nfs4_client *clp) 684free_client(struct nfs4_client *clp)
699{ 685{
@@ -746,7 +732,7 @@ expire_client(struct nfs4_client *clp)
746 se_perclnt); 732 se_perclnt);
747 release_session(ses); 733 release_session(ses);
748 } 734 }
749 shutdown_callback_client(clp); 735 nfsd4_set_callback_client(clp, NULL);
750 if (clp->cl_cb_xprt) 736 if (clp->cl_cb_xprt)
751 svc_xprt_put(clp->cl_cb_xprt); 737 svc_xprt_put(clp->cl_cb_xprt);
752 put_nfs4_client(clp); 738 put_nfs4_client(clp);
@@ -1392,7 +1378,7 @@ nfsd4_destroy_session(struct svc_rqst *r,
1392 spin_unlock(&sessionid_lock); 1378 spin_unlock(&sessionid_lock);
1393 1379
1394 /* wait for callbacks */ 1380 /* wait for callbacks */
1395 shutdown_callback_client(ses->se_client); 1381 nfsd4_set_callback_client(ses->se_client, NULL);
1396 nfsd4_put_session(ses); 1382 nfsd4_put_session(ses);
1397 status = nfs_ok; 1383 status = nfs_ok;
1398out: 1384out:
@@ -4004,16 +3990,27 @@ set_max_delegations(void)
4004static int 3990static int
4005__nfs4_state_start(void) 3991__nfs4_state_start(void)
4006{ 3992{
3993 int ret;
3994
4007 boot_time = get_seconds(); 3995 boot_time = get_seconds();
4008 locks_start_grace(&nfsd4_manager); 3996 locks_start_grace(&nfsd4_manager);
4009 printk(KERN_INFO "NFSD: starting %ld-second grace period\n", 3997 printk(KERN_INFO "NFSD: starting %ld-second grace period\n",
4010 nfsd4_grace); 3998 nfsd4_grace);
3999 ret = set_callback_cred();
4000 if (ret)
4001 return -ENOMEM;
4011 laundry_wq = create_singlethread_workqueue("nfsd4"); 4002 laundry_wq = create_singlethread_workqueue("nfsd4");
4012 if (laundry_wq == NULL) 4003 if (laundry_wq == NULL)
4013 return -ENOMEM; 4004 return -ENOMEM;
4005 ret = nfsd4_create_callback_queue();
4006 if (ret)
4007 goto out_free_laundry;
4014 queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); 4008 queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ);
4015 set_max_delegations(); 4009 set_max_delegations();
4016 return set_callback_cred(); 4010 return 0;
4011out_free_laundry:
4012 destroy_workqueue(laundry_wq);
4013 return ret;
4017} 4014}
4018 4015
4019int 4016int
@@ -4075,6 +4072,7 @@ nfs4_state_shutdown(void)
4075 nfs4_lock_state(); 4072 nfs4_lock_state();
4076 nfs4_release_reclaim(); 4073 nfs4_release_reclaim();
4077 __nfs4_state_shutdown(); 4074 __nfs4_state_shutdown();
4075 nfsd4_destroy_callback_queue();
4078 nfs4_unlock_state(); 4076 nfs4_unlock_state();
4079} 4077}
4080 4078
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index b85437982a8d..c4c92aea8f39 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -77,6 +77,7 @@ struct nfs4_rpc_args {
77 77
78struct nfsd4_callback { 78struct nfsd4_callback {
79 struct nfs4_rpc_args cb_args; 79 struct nfs4_rpc_args cb_args;
80 struct work_struct cb_work;
80}; 81};
81 82
82struct nfs4_delegation { 83struct nfs4_delegation {
@@ -391,7 +392,11 @@ extern void put_nfs4_client(struct nfs4_client *clp);
391extern void nfs4_free_stateowner(struct kref *kref); 392extern void nfs4_free_stateowner(struct kref *kref);
392extern int set_callback_cred(void); 393extern int set_callback_cred(void);
393extern void nfsd4_probe_callback(struct nfs4_client *clp); 394extern void nfsd4_probe_callback(struct nfs4_client *clp);
395extern void nfsd4_do_callback_rpc(struct work_struct *);
394extern void nfsd4_cb_recall(struct nfs4_delegation *dp); 396extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
397extern int nfsd4_create_callback_queue(void);
398extern void nfsd4_destroy_callback_queue(void);
399extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *);
395extern void nfs4_put_delegation(struct nfs4_delegation *dp); 400extern void nfs4_put_delegation(struct nfs4_delegation *dp);
396extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); 401extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
397extern void nfsd4_init_recdir(char *recdir_name); 402extern void nfsd4_init_recdir(char *recdir_name);