diff options
author | J. Bruce Fields <bfields@citi.umich.edu> | 2010-03-03 14:52:55 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@citi.umich.edu> | 2010-04-22 11:34:01 -0400 |
commit | b5a1a81e5c25fb6bb3fdc1812ba69ff6ab638fcf (patch) | |
tree | c524a75d111f4060eb985161478362ac18c17169 | |
parent | 3c4ab2aaa90826060b1e8d4036f9bb8325f8759e (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.c | 54 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 34 | ||||
-rw-r--r-- | fs/nfsd/state.h | 5 |
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 | ||
696 | static struct workqueue_struct *callback_wq; | ||
697 | |||
698 | int 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 | |||
706 | void nfsd4_destroy_callback_queue(void) | ||
707 | { | ||
708 | destroy_workqueue(callback_wq); | ||
709 | } | ||
710 | |||
711 | void 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 | */ |
698 | void | 730 | static void _nfsd4_cb_recall(struct nfs4_delegation *dp) |
699 | nfsd4_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 | |||
755 | void 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 | |||
766 | void 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 | ||
682 | static void | ||
683 | shutdown_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 | |||
697 | static inline void | 683 | static inline void |
698 | free_client(struct nfs4_client *clp) | 684 | free_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; |
1398 | out: | 1384 | out: |
@@ -4004,16 +3990,27 @@ set_max_delegations(void) | |||
4004 | static int | 3990 | static 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; |
4011 | out_free_laundry: | ||
4012 | destroy_workqueue(laundry_wq); | ||
4013 | return ret; | ||
4017 | } | 4014 | } |
4018 | 4015 | ||
4019 | int | 4016 | int |
@@ -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 | ||
78 | struct nfsd4_callback { | 78 | struct 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 | ||
82 | struct nfs4_delegation { | 83 | struct nfs4_delegation { |
@@ -391,7 +392,11 @@ extern void put_nfs4_client(struct nfs4_client *clp); | |||
391 | extern void nfs4_free_stateowner(struct kref *kref); | 392 | extern void nfs4_free_stateowner(struct kref *kref); |
392 | extern int set_callback_cred(void); | 393 | extern int set_callback_cred(void); |
393 | extern void nfsd4_probe_callback(struct nfs4_client *clp); | 394 | extern void nfsd4_probe_callback(struct nfs4_client *clp); |
395 | extern void nfsd4_do_callback_rpc(struct work_struct *); | ||
394 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); | 396 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); |
397 | extern int nfsd4_create_callback_queue(void); | ||
398 | extern void nfsd4_destroy_callback_queue(void); | ||
399 | extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *); | ||
395 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); | 400 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); |
396 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); | 401 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); |
397 | extern void nfsd4_init_recdir(char *recdir_name); | 402 | extern void nfsd4_init_recdir(char *recdir_name); |