aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2010-10-26 10:07:17 -0400
committerJ. Bruce Fields <bfields@redhat.com>2010-11-02 17:13:52 -0400
commit21b75b019983dfa5c2dda588f4b60b4ca69844a4 (patch)
treec1e0a8c4e136a0de35fc996a7ed6c5db1e6bb9cc
parentc8ddb2713c624f432fa5fe3c7ecffcdda46ea0d4 (diff)
nfsd4: fix 4.1 connection registration race
If a connection is closed just after a sequence or create_session is sent over it, we could end up trying to register a callback that will never get called since the xprt is already marked dead. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c16
-rw-r--r--include/linux/sunrpc/svc_xprt.h18
2 files changed, 26 insertions, 8 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f1e5ec6b5105..ad2bfa68d534 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -673,16 +673,17 @@ static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
673 spin_unlock(&clp->cl_lock); 673 spin_unlock(&clp->cl_lock);
674} 674}
675 675
676static void nfsd4_register_conn(struct nfsd4_conn *conn) 676static int nfsd4_register_conn(struct nfsd4_conn *conn)
677{ 677{
678 conn->cn_xpt_user.callback = nfsd4_conn_lost; 678 conn->cn_xpt_user.callback = nfsd4_conn_lost;
679 register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user); 679 return register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
680} 680}
681 681
682static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses) 682static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
683{ 683{
684 struct nfsd4_conn *conn; 684 struct nfsd4_conn *conn;
685 u32 flags = NFS4_CDFC4_FORE; 685 u32 flags = NFS4_CDFC4_FORE;
686 int ret;
686 687
687 if (ses->se_flags & SESSION4_BACK_CHAN) 688 if (ses->se_flags & SESSION4_BACK_CHAN)
688 flags |= NFS4_CDFC4_BACK; 689 flags |= NFS4_CDFC4_BACK;
@@ -690,7 +691,10 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
690 if (!conn) 691 if (!conn)
691 return nfserr_jukebox; 692 return nfserr_jukebox;
692 nfsd4_hash_conn(conn, ses); 693 nfsd4_hash_conn(conn, ses);
693 nfsd4_register_conn(conn); 694 ret = nfsd4_register_conn(conn);
695 if (ret)
696 /* oops; xprt is already down: */
697 nfsd4_conn_lost(&conn->cn_xpt_user);
694 return nfs_ok; 698 return nfs_ok;
695} 699}
696 700
@@ -1644,6 +1648,7 @@ static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_sessi
1644{ 1648{
1645 struct nfs4_client *clp = ses->se_client; 1649 struct nfs4_client *clp = ses->se_client;
1646 struct nfsd4_conn *c; 1650 struct nfsd4_conn *c;
1651 int ret;
1647 1652
1648 spin_lock(&clp->cl_lock); 1653 spin_lock(&clp->cl_lock);
1649 c = __nfsd4_find_conn(new->cn_xprt, ses); 1654 c = __nfsd4_find_conn(new->cn_xprt, ses);
@@ -1654,7 +1659,10 @@ static void nfsd4_sequence_check_conn(struct nfsd4_conn *new, struct nfsd4_sessi
1654 } 1659 }
1655 __nfsd4_hash_conn(new, ses); 1660 __nfsd4_hash_conn(new, ses);
1656 spin_unlock(&clp->cl_lock); 1661 spin_unlock(&clp->cl_lock);
1657 nfsd4_register_conn(new); 1662 ret = nfsd4_register_conn(new);
1663 if (ret)
1664 /* oops; xprt is already down: */
1665 nfsd4_conn_lost(&new->cn_xpt_user);
1658 return; 1666 return;
1659} 1667}
1660 1668
diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index bbdb680ffbe9..aea0d438e3c7 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -82,18 +82,28 @@ struct svc_xprt {
82 struct net *xpt_net; 82 struct net *xpt_net;
83}; 83};
84 84
85static inline void register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u) 85static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
86{ 86{
87 spin_lock(&xpt->xpt_lock); 87 spin_lock(&xpt->xpt_lock);
88 list_add(&u->list, &xpt->xpt_users); 88 list_del_init(&u->list);
89 spin_unlock(&xpt->xpt_lock); 89 spin_unlock(&xpt->xpt_lock);
90} 90}
91 91
92static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u) 92static inline int register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
93{ 93{
94 spin_lock(&xpt->xpt_lock); 94 spin_lock(&xpt->xpt_lock);
95 list_del_init(&u->list); 95 if (test_bit(XPT_CLOSE, &xpt->xpt_flags)) {
96 /*
97 * The connection is about to be deleted soon (or,
98 * worse, may already be deleted--in which case we've
99 * already notified the xpt_users).
100 */
101 spin_unlock(&xpt->xpt_lock);
102 return -ENOTCONN;
103 }
104 list_add(&u->list, &xpt->xpt_users);
96 spin_unlock(&xpt->xpt_lock); 105 spin_unlock(&xpt->xpt_lock);
106 return 0;
97} 107}
98 108
99int svc_reg_xprt_class(struct svc_xprt_class *); 109int svc_reg_xprt_class(struct svc_xprt_class *);