server sessions patches
Subsystems (3)
Some more patches implementing BIND_CONN_TO_SESSION and related stuff.
The server should be able to switch to a different backchannel now on operations that explicitly add or remove sessions or connections, or when the rpc layer notices a connection has gone down.
Testing from the Windows people suggests we should also switch on callback timeouts to deal with cases where one connection is unreliable. I'm not quite sure what to do there.
I was planning on merging this for 2.6.38. I let it get a little late. But this seems low-risk for versions < 4.1, so I might still go ahead.
--b.
PATCH 1/14 nfsd4: modify session list under cl_lock
From: J. Bruce Fields <[email protected]>
We want to traverse this from the callback code.
--- fs/nfsd/nfs4state.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-)
fs/nfsd/nfs4state.c +6 -0
@@ -771,7 +771,9 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct n idx =hash_sessionid (&new->se_sessionid);spin_lock (&client_lock);list_add (&new->se_hash, &sessionid_hashtbl[idx]); +spin_lock (&clp->cl_lock);list_add (&new->se_perclnt, &clp->cl_sessions); +spin_unlock (&clp->cl_lock);spin_unlock (&client_lock); status =nfsd4_new_conn (rqstp, new);
@@ -819,7 +821,9 @@ static void unhash_session(struct nfsd4_session *ses) {list_del (&ses->se_hash); +spin_lock (&ses->se_client->cl_lock);list_del (&ses->se_perclnt); +spin_unlock (&ses->se_client->cl_lock); }/* must be called under the client_lock */
@@ -925,8 +929,10 @@ unhash_client_locked(struct nfs4_client *clp)mark_client_expired (clp);list_del (&clp->cl_lru); +spin_lock (&clp->cl_lock);list_for_each_entry (ses, &clp->cl_sessions, se_perclnt)list_del_init (&ses->se_hash); +spin_unlock (&clp->cl_lock); } static void
Signature · 1 line
1.7.1
PATCH 2/14 nfsd4: support BIND_CONN_TO_SESSION
In reply to J. Bruce FieldsBasic xdr and processing for BIND_CONN_TO_SESSION. This adds a connection to the list of connections associated with a session.
--- fs/nfsd/nfs4proc.c | 9 ++++++- fs/nfsd/nfs4state.c | 59 ++++++++++++++++++++++++++++++++++++++++++++----- fs/nfsd/nfs4xdr.c | 34 +++++++++++++++++++++++++++- fs/nfsd/state.h | 5 ++++ fs/nfsd/xdr4.h | 2 + include/linux/nfs4.h | 3 ++ 6 files changed, 102 insertions(+), 10 deletions(-)
fs/nfsd/nfs4proc.c +7 -2
@@ -1004,8 +1004,8 @@ static const char *nfsd4_op_name(unsigned opnum);* Also note, enforced elsewhere: * - SEQUENCE other than as first op results in * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) -* - BIND_CONN_TO_SESSION must be the only op in its compound -* (Will be enforced in nfsd4_bind_conn_to_session().) +* - BIND_CONN_TO_SESSION must be the only op in its compound. +* (Enforced in nfsd4_bind_conn_to_session().) * - DESTROY_SESSION must be the final operation in a compound, if * sessionid's in SEQUENCE and DESTROY_SESSION are the same. * (Enforced in nfsd4_destroy_session().)
@@ -1326,6 +1326,11 @@ static struct nfsd4_operation nfsd4_ops[] = { .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, .op_name = "OP_EXCHANGE_ID", }, + [OP_BIND_CONN_TO_SESSION] = { + .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, + .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, + .op_name = "OP_BIND_CONN_TO_SESSION", + }, [OP_CREATE_SESSION] = { .op_func = (nfsd4op_func)nfsd4_create_session, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP,
fs/nfsd/nfs4state.c +53 -6
87 lines not on this page · raw message
fs/nfsd/nfs4xdr.c +32 -2
@@ -421,6 +421,21 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *accessDECODE_TAIL ; } +static __be32 nfsd4_decode_bind_conn_to_session (struct nfsd4_compoundargs *argp,struct nfsd4_bind_conn_to_session *bcts) +{ +DECODE_HEAD ; +u32 dummy; + +READ_BUF (NFS4_MAX_SESSIONID_LEN +8 ); +COPYMEM (bcts->sessionid.data,NFS4_MAX_SESSIONID_LEN ); +READ32 (bcts->dir); +/* XXX: Perhaps Tom Tucker could help us figure out how we +* should be using ctsa_use_conn_in_rdma_mode: */ +READ32 (dummy); + +DECODE_TAIL ; +} +static __be32 nfsd4_decode_close (struct nfsd4_compoundargs *argp,struct nfsd4_close *close) {
@@ -1359,7 +1374,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { /* new operations for NFSv4.1 */ [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp, - [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_notsupp, + [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, [OP_DESTROY_SESSION] = (nfsd4_dec)nfsd4_decode_destroy_session,
@@ -2383,6 +2398,21 @@ nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_return nfserr; } +static __be32 nfsd4_encode_bind_conn_to_session (struct nfsd4_compoundres *resp,__be32 nfserr,struct nfsd4_bind_conn_to_session *bcts) +{ +__be32 *p; + +if (!nfserr) { +RESERVE_SPACE (NFS4_MAX_SESSIONID_LEN +8 ); +WRITEMEM (bcts->sessionid.data,NFS4_MAX_SESSIONID_LEN ); +WRITE32 (bcts->dir); +/* XXX: ? */ +WRITE32 (0 ); +ADJUST_ARGS (); + } +return nfserr; +} +static __be32 nfsd4_encode_close (struct nfsd4_compoundres *resp,__be32 nfserr,struct nfsd4_close *close) {
@@ -3174,7 +3204,7 @@ static nfsd4_enc nfsd4_enc_ops[] = { /* NFSv4.1 operations */ [OP_BACKCHANNEL_CTL] = (nfsd4_enc)nfsd4_encode_noop, - [OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_noop, + [OP_BIND_CONN_TO_SESSION] = (nfsd4_enc)nfsd4_encode_bind_conn_to_session, [OP_EXCHANGE_ID] = (nfsd4_enc)nfsd4_encode_exchange_id, [OP_CREATE_SESSION] = (nfsd4_enc)nfsd4_encode_create_session, [OP_DESTROY_SESSION] = (nfsd4_enc)nfsd4_encode_destroy_session,
fs/nfsd/state.h +5 -0
@@ -148,6 +148,11 @@ struct nfsd4_create_session {u32 gid; }; +struct nfsd4_bind_conn_to_session { +struct nfs4_sessionid sessionid; +u32 dir; +}; +/* The single slot clientid cache structure */ struct nfsd4_clid_slot {u32 sl_seqid;
fs/nfsd/xdr4.h +2 -0
@@ -427,6 +427,7 @@ struct nfsd4_op {/* NFSv4.1 */ struct nfsd4_exchange_id exchange_id; +struct nfsd4_bind_conn_to_session bind_conn_to_session;struct nfsd4_create_session create_session;struct nfsd4_destroy_session destroy_session;struct nfsd4_sequence sequence;
@@ -523,6 +524,7 @@ extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, struct nfsd4_sequence *seq);extern __be32 nfsd4_exchange_id (struct svc_rqst *rqstp,struct nfsd4_compound_state *,struct nfsd4_exchange_id *); +extern __be32 nfsd4_bind_conn_to_session (struct svc_rqst *,struct nfsd4_compound_state *,struct nfsd4_bind_conn_to_session *);extern __be32 nfsd4_create_session (struct svc_rqst *,struct nfsd4_compound_state *,struct nfsd4_create_session *);
include/linux/nfs4.h +3 -0
@@ -65,6 +65,9 @@#define NFS4_CDFC4_FORE 0x1#define NFS4_CDFC4_BACK 0x2 +#define NFS4_CDFC4_BOTH 0x3 +#define NFS4_CDFC4_FORE_OR_BOTH 0x3 +#define NFS4_CDFC4_BACK_OR_BOTH 0x7#define NFS4_SET_TO_SERVER_TIME 0#define NFS4_SET_TO_CLIENT_TIME 1
Signature · 1 line
1.7.1
PATCH 3/14 nfsd4: allow backchannel recovery
In reply to J. Bruce FieldsFrom: J. Bruce Fields <[email protected]>
Now that we have a list of connections to choose from, we can teach the callback code to just pick a suitable connection and use that, instead of insisting on forever using the connection that the first create_session was sent with.
--- fs/nfsd/nfs4callback.c | 36 +++++++++++++++++++++++++++++++++--- fs/nfsd/nfs4state.c | 16 ++++++++++------ 2 files changed, 43 insertions(+), 9 deletions(-)
fs/nfsd/nfs4callback.c +33 -3
@@ -473,8 +473,7 @@ static int max_cb_time(void)/* Reference counting, callback cleanup, etc., all look racy as heck. * And why is cl_cb_set an atomic? */ -static int setup_callback_client (struct nfs4_client *clp, -struct nfs4_cb_conn *conn) +static int setup_callback_client (struct nfs4_client *clp,struct nfs4_cb_conn *conn,struct nfsd4_session *ses) {struct rpc_timeout timeparms = { .to_initval =max_cb_time (),
@@ -501,6 +500,10 @@ static int setup_callback_client(struct nfs4_client *clp, args.protocol =XPRT_TRANSPORT_TCP ; clp->cl_cb_ident = conn->cb_ident; }else { +if (!conn->cb_xprt) +return -EINVAL ; + clp->cl_cb_conn.cb_xprt = conn->cb_xprt; + clp->cl_cb_session = ses; args.bc_xprt = conn->cb_xprt; args.prognumber = clp->cl_cb_session->se_cb_prog; args.protocol =XPRT_TRANSPORT_BC_TCP ;
@@ -756,10 +759,27 @@ static void nfsd4_release_cb(struct nfsd4_callback *cb) cb->cb_ops->rpc_release (cb); } +/* requires cl_lock: */ +static struct nfsd4_conn *__nfsd4_find_backchannel (struct nfs4_client *clp) +{ +struct nfsd4_session *s; +struct nfsd4_conn *c; + +list_for_each_entry (s, &clp->cl_sessions, se_perclnt) { +list_for_each_entry (c, &s->se_conns, cn_persession) { +if (c->cn_flags &NFS4_CDFC4_BACK ) +return c; + } + } +return NULL ; +} +static void nfsd4_process_cb_update (struct nfsd4_callback *cb) {struct nfs4_cb_conn conn;struct nfs4_client *clp = cb->cb_clp; +struct nfsd4_session *ses =NULL ; +struct nfsd4_conn *c;int err;/*
@@ -770,6 +790,10 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)rpc_shutdown_client (clp->cl_cb_client); clp->cl_cb_client =NULL ; } +if (clp->cl_cb_conn.cb_xprt) { +svc_xprt_put (clp->cl_cb_conn.cb_xprt); + clp->cl_cb_conn.cb_xprt =NULL ; + }if (test_bit (NFSD4_CLIENT_KILL , &clp->cl_cb_flags))return ;spin_lock (&clp->cl_lock);
@@ -780,9 +804,15 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)BUG_ON (!clp->cl_cb_flags);clear_bit (NFSD4_CLIENT_CB_UPDATE , &clp->cl_cb_flags);memcpy (&conn, &cb->cb_clp->cl_cb_conn,sizeof (struct nfs4_cb_conn )); + c =__nfsd4_find_backchannel (clp); +if (c) { +svc_xprt_get (c->cn_xprt); + conn.cb_xprt = c->cn_xprt; + ses = c->cn_session; + }spin_unlock (&clp->cl_lock); - err =setup_callback_client (clp, &conn); + err =setup_callback_client (clp, &conn, ses);if (err)warn_no_callback_path (clp, err); }
fs/nfsd/nfs4state.c +10 -6
@@ -642,6 +642,7 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u)free_conn (c); }spin_unlock (&clp->cl_lock); +/* XXX: mark callback for update, probe callback */ }static struct nfsd4_conn *alloc_conn (struct svc_rqst *rqstp,u32 flags)
@@ -790,16 +791,19 @@ static struct nfsd4_session *alloc_init_session(struct svc_rqst *rqstp, struct nfree_session (&new->se_ref);return NULL ; } -if (!clp->cl_cb_session && (cses->flags &SESSION4_BACK_CHAN )) { +if (cses->flags &SESSION4_BACK_CHAN ) {struct sockaddr *sa =svc_addr (rqstp); - - clp->cl_cb_session = new; - clp->cl_cb_conn.cb_xprt = rqstp->rq_xprt; -svc_xprt_get (rqstp->rq_xprt); +/* +* This is a little silly; with sessions there's no real +* use for the callback address. Use the peer address +* as a reasonable default for now, but consider fixing +* the rpc client not to require an address in the +* future: +*/ rpc_copy_addr ((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); clp->cl_cb_conn.cb_addrlen =svc_addr_len (sa); -nfsd4_probe_callback (clp); } +nfsd4_probe_callback (clp);return new; }
Signature · 1 line
1.7.1
PATCH 4/14 rpc: move sk_bc_xprt to svc_xprt
In reply to J. Bruce FieldsThis seems obviously transport-level information even if it's currently used only by the server socket code.
--- include/linux/sunrpc/svc_xprt.h | 1 + include/linux/sunrpc/svcsock.h | 1 - net/sunrpc/svcsock.c | 10 ++++++---- net/sunrpc/xprtsock.c | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-)
include/linux/sunrpc/svc_xprt.h +1 -0
@@ -79,6 +79,7 @@ struct svc_xprt {struct list_head xpt_users;/* callbacks on free */ struct net *xpt_net; +struct rpc_xprt *xpt_bc_xprt;/* NFSv4.1 backchannel */ };static inline void unregister_xpt_user (struct svc_xprt *xpt,struct svc_xpt_user *u)
include/linux/sunrpc/svcsock.h +0 -1
@@ -28,7 +28,6 @@ struct svc_sock {/* private TCP part */ u32 sk_reclen;/* length of record */ u32 sk_tcplen;/* current read length */ -struct rpc_xprt *sk_bc_xprt;/* NFSv4.1 backchannel xprt */ };/*
net/sunrpc/svcsock.c +6 -4
@@ -987,15 +987,17 @@ static int svc_process_calldir(struct svc_sock *svsk, struct svc_rqst *rqstp, vec[0 ] = rqstp->rq_arg.head[0 ]; }else {/* REPLY */ -if (svsk->sk_bc_xprt) - req =xprt_lookup_rqst (svsk->sk_bc_xprt, xid); +struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt; + +if (bc_xprt) + req =xprt_lookup_rqst (bc_xprt, xid);if (!req) {printk (KERN_NOTICE "%s: Got unrecognized reply: " -"calldir 0x%x sk_bc_xprt %p xid %08x\n" , +"calldir 0x%x xpt_bc_xprt %p xid %08x\n" , __func__,ntohl (calldir), - svsk->sk_bc_xprt, xid); + bc_xprt, xid); vec[0 ] = rqstp->rq_arg.head[0 ]; goto out; }
net/sunrpc/xprtsock.c +1 -1
@@ -2379,9 +2379,9 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)* The backchannel uses the same socket connection as the * forechannel */ + args->bc_xprt->xpt_bc_xprt = xprt; xprt->bc_xprt = args->bc_xprt; bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt); - bc_sock->sk_bc_xprt = xprt; transport->sock = bc_sock->sk_sock; transport->inet = bc_sock->sk_sk;
Signature · 1 line
1.7.1
PATCH 5/14 rpc: keep backchannel xprt as long as server connection
In reply to J. Bruce FieldsMultiple backchannels can share the same tcp connection; from rfc 5661 section 2.10.3.1:
A connection's association with a session is not exclusive. A connection associated with the channel(s) of one session may be simultaneously associated with the channel(s) of other sessions including sessions associated with other client IDs.
However, multiple backchannels share a connection, they must all share the same xid stream (hence the same rpc_xprt); the only way we have to match replies with calls at the rpc layer is using the xid.
So, keep the rpc_xprt around as long as the connection lasts, in case we're asked to use the connection as a backchannel again.
Requests to create new backchannel clients over a given server connection should results in creating new clients that reuse the existing rpc_xprt.
But to start, just reject attempts to associate multiple rpc_xprt's with the same underlying bc_xprt.
--- net/sunrpc/svc_xprt.c | 4 ++++ net/sunrpc/xprt.c | 2 +- net/sunrpc/xprtsock.c | 34 ++++++++++++++++++++++++---------- 3 files changed, 29 insertions(+), 11 deletions(-)
net/sunrpc/svc_xprt.c +4 -0
@@ -13,6 +13,7 @@#include <linux/sunrpc/stats.h> #include <linux/sunrpc/svc_xprt.h> #include <linux/sunrpc/svcsock.h> +#include <linux/sunrpc/xprt.h> #define RPCDBG_FACILITY RPCDBG_SVCXPRT
@@ -128,6 +129,9 @@ static void svc_xprt_free(struct kref *kref)if (test_bit (XPT_CACHE_AUTH , &xprt->xpt_flags))svcauth_unix_info_release (xprt);put_net (xprt->xpt_net); +/* See comment on corresponding get in xs_setup_bc_tcp(): */ +if (xprt->xpt_bc_xprt) +xprt_put (xprt->xpt_bc_xprt); xprt->xpt_ops->xpo_free (xprt);module_put (owner); }
net/sunrpc/xprt.c +1 -1
@@ -965,6 +965,7 @@ struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req) xprt =kzalloc (size,GFP_KERNEL );if (xprt ==NULL ) goto out; +kref_init (&xprt->kref); xprt->max_reqs = max_req; xprt->slot =kcalloc (max_req,sizeof (struct rpc_rqst ),GFP_KERNEL );
@@ -1102,7 +1103,6 @@ found:return xprt; } -kref_init (&xprt->kref);spin_lock_init (&xprt->transport_lock);spin_lock_init (&xprt->reserve_lock);
net/sunrpc/xprtsock.c +24 -10
@@ -2375,16 +2375,6 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args) xprt->reestablish_timeout =0 ; xprt->idle_timeout =0 ; -/* -* The backchannel uses the same socket connection as the -* forechannel -*/ - args->bc_xprt->xpt_bc_xprt = xprt; - xprt->bc_xprt = args->bc_xprt; - bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt); - transport->sock = bc_sock->sk_sock; - transport->inet = bc_sock->sk_sk; - xprt->ops = &bc_tcp_ops;switch (addr->sa_family) {
@@ -2407,6 +2397,29 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args) xprt->address_strings[RPC_DISPLAY_PROTO]);/* +* The backchannel uses the same socket connection as the +* forechannel +*/ +if (args->bc_xprt->xpt_bc_xprt) { +/* XXX: actually, want to catch this case... */ + ret =ERR_PTR (-EINVAL ); + goto out_err; + } +/* +* Once we've associated a backchannel xprt with a connection, +* we want to keep it around as long as long as the connection +* lasts, in case we need to start using it for a backchannel +* again; this reference won't be dropped until bc_xprt is +* destroyed. +*/ +xprt_get (xprt); + args->bc_xprt->xpt_bc_xprt = xprt; + xprt->bc_xprt = args->bc_xprt; + bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt); + transport->sock = bc_sock->sk_sock; + transport->inet = bc_sock->sk_sk; + +/* * Since we don't want connections for the backchannel, we set * the xprt status to connected */
@@ -2415,6 +2428,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)if (try_module_get (THIS_MODULE ))return xprt; +xprt_put (xprt); ret =ERR_PTR (-EINVAL ); out_err:xprt_free (xprt);
Signature · 1 line
1.7.1
PATCH 6/14 rpc: allow xprt_class->setup to return a preexisting xprt
In reply to J. Bruce FieldsThis allows us to reuse the xprt associated with a server connection if one has already been set up.
--- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/xprt.c | 3 +++ net/sunrpc/xprtsock.c | 18 +++++++++--------- 3 files changed, 13 insertions(+), 9 deletions(-)
include/linux/sunrpc/xprt.h +1 -0
@@ -321,6 +321,7 @@ void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);#define XPRT_CLOSING (6)#define XPRT_CONNECTION_ABORT (7)#define XPRT_CONNECTION_CLOSE (8) +#define XPRT_INITIALIZED (9)static inline void xprt_set_connected (struct rpc_xprt *xprt) {
net/sunrpc/xprt.c +3 -0
@@ -1102,6 +1102,9 @@ found: -PTR_ERR(xprt));return xprt; } +if (test_and_set_bit (XPRT_INITIALIZED , &xprt->state)) +/* ->setup returned a pre-initialized xprt: */ +return xprt;spin_lock_init (&xprt->transport_lock);spin_lock_init (&xprt->reserve_lock);
net/sunrpc/xprtsock.c +9 -9
@@ -2359,6 +2359,15 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)struct svc_sock *bc_sock;struct rpc_xprt *ret; +if (args->bc_xprt->xpt_bc_xprt) { +/* +* This server connection already has a backchannel +* export; we can't create a new one, as we wouldn't be +* able to match replies based on xid any more. So, +* reuse the already-existing one: +*/ +return args->bc_xprt->xpt_bc_xprt; + } xprt =xs_setup_xprt (args, xprt_tcp_slot_table_entries);if (IS_ERR (xprt))return xprt;
@@ -2397,15 +2406,6 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args) xprt->address_strings[RPC_DISPLAY_PROTO]);/* -* The backchannel uses the same socket connection as the -* forechannel -*/ -if (args->bc_xprt->xpt_bc_xprt) { -/* XXX: actually, want to catch this case... */ - ret =ERR_PTR (-EINVAL ); - goto out_err; - } -/* * Once we've associated a backchannel xprt with a connection, * we want to keep it around as long as long as the connection * lasts, in case we need to start using it for a backchannel
Signature · 1 line
1.7.1
PATCH 7/14 nfsd4: keep finer-grained callback status
In reply to J. Bruce FieldsFrom: J. Bruce Fields <[email protected]>
Distinguish between when the callback channel is known to be down, and when it is not yet confirmed. This will be useful in the 4.1 case.
Also, we don't seem to be using the fact that this field is atomic.
--- fs/nfsd/nfs4callback.c | 26 ++++++++++++++------------ fs/nfsd/nfs4state.c | 8 ++++---- fs/nfsd/state.h | 5 ++++- 3 files changed, 22 insertions(+), 17 deletions(-)
fs/nfsd/nfs4callback.c +14 -12
@@ -470,8 +470,6 @@ static int max_cb_time(void)return max (nfsd4_lease/10 , (time_t )1 ) *HZ ; } -/* Reference counting, callback cleanup, etc., all look racy as heck. -* And why is cl_cb_set an atomic? */ static int setup_callback_client (struct nfs4_client *clp,struct nfs4_cb_conn *conn,struct nfsd4_session *ses) {
@@ -526,14 +524,20 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason) (int)clp->cl_name.len, clp->cl_name.data, reason); } +static void nfsd4_mark_cb_down (struct nfs4_client *clp,int reason) +{ + clp->cl_cb_state =NFSD4_CB_DOWN ; +warn_no_callback_path (clp, reason); +} +static void nfsd4_cb_probe_done (struct rpc_task *task,void *calldata) { struct nfs4_client *clp = container_of(calldata, struct nfs4_client, cl_cb_null);if (task->tk_status) -warn_no_callback_path (clp, task->tk_status); +nfsd4_mark_cb_down (clp, task->tk_status);else -atomic_set (&clp->cl_cb_set,1 ); + clp->cl_cb_state =NFSD4_CB_UP ; }static const struct rpc_call_ops nfsd4_cb_probe_ops = {
@@ -579,14 +583,15 @@ static void do_probe_callback(struct nfs4_client *clp)*/ void nfsd4_probe_callback (struct nfs4_client *clp) { +/* XXX: atomicity? Also, should we be using cl_cb_flags? */ + clp->cl_cb_state =NFSD4_CB_UNKNOWN ;set_bit (NFSD4_CLIENT_CB_UPDATE , &clp->cl_cb_flags);do_probe_callback (clp); }void nfsd4_change_callback (struct nfs4_client *clp,struct nfs4_cb_conn *conn) { -BUG_ON (atomic_read (&clp->cl_cb_set)); - + clp->cl_cb_state =NFSD4_CB_UNKNOWN ;spin_lock (&clp->cl_lock);memcpy (&clp->cl_cb_conn, conn,sizeof (struct nfs4_cb_conn ));spin_unlock (&clp->cl_lock);
@@ -693,8 +698,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)break ;default :/* Network partition? */ -atomic_set (&clp->cl_cb_set,0 ); -warn_no_callback_path (clp, task->tk_status); +nfsd4_mark_cb_down (clp, task->tk_status);if (current_rpc_client != task->tk_client) {/* queue a callback on the new connection: */ atomic_inc (&dp->dl_count);
@@ -707,10 +711,8 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) task->tk_status =0 ;rpc_restart_call_prepare (task);return ; - }else { -atomic_set (&clp->cl_cb_set,0 ); -warn_no_callback_path (clp, task->tk_status); - } + }else +nfsd4_mark_cb_down (clp, task->tk_status); }static void nfsd4_cb_recall_release (void *calldata)
fs/nfsd/nfs4state.c +4 -4
@@ -1071,7 +1071,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,memcpy (clp->cl_recdir, recdir,HEXDIR_LEN );atomic_set (&clp->cl_refcount,0 ); -atomic_set (&clp->cl_cb_set,0 ); + clp->cl_cb_state =NFSD4_CB_UNKNOWN ;INIT_LIST_HEAD (&clp->cl_idhash);INIT_LIST_HEAD (&clp->cl_strhash);INIT_LIST_HEAD (&clp->cl_openowners);
@@ -2003,7 +2003,6 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,if (!same_creds (&conf->cl_cred, &unconf->cl_cred)) status = nfserr_clid_inuse;else { -atomic_set (&conf->cl_cb_set,0 );nfsd4_change_callback (conf, &unconf->cl_cb_conn);nfsd4_probe_callback (conf);expire_client (unconf);
@@ -2633,7 +2632,8 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta {struct nfs4_delegation *dp;struct nfs4_stateowner *sop = stp->st_stateowner; -int cb_up =atomic_read (&sop->so_client->cl_cb_set); +/* XXX: or unknown and nfsv4.1: */ +int cb_up = (sop->so_client->cl_cb_state ==NFSD4_CB_UP );struct file_lock *fl;int status, flag =0 ;
@@ -2823,7 +2823,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,renew_client (clp); status = nfserr_cb_path_down;if (!list_empty (&clp->cl_delegations) - && !atomic_read (&clp->cl_cb_set)) + && clp->cl_cb_state !=NFSD4_CB_UP ) goto out; status = nfs_ok; out:
fs/nfsd/state.h +4 -1
@@ -242,7 +242,10 @@ struct nfs4_client {unsigned long cl_cb_flags;struct rpc_clnt *cl_cb_client;u32 cl_cb_ident; -atomic_t cl_cb_set; +#define NFSD4_CB_UP 0 +#define NFSD4_CB_UNKNOWN 1 +#define NFSD4_CB_DOWN 2 +int cl_cb_state;struct nfsd4_callback cl_cb_null;struct nfsd4_session *cl_cb_session;
Signature · 1 line
1.7.1
PATCH 8/14 nfsd4: set sequence flag when backchannel is down
In reply to J. Bruce FieldsImplement the SEQ4_STATUS_CB_PATH_DOWN flag.
--- fs/nfsd/nfs4state.c | 6 +++++- fs/nfsd/nfs4xdr.c | 8 ++------ fs/nfsd/xdr4.h | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-)
fs/nfsd/nfs4state.c +5 -1
@@ -1800,8 +1800,12 @@ nfsd4_sequence(struct svc_rqst *rqstp, out:/* Hold a session reference until done processing the compound. */ if (cstate->session) { +struct nfs4_client *clp = session->se_client; +nfsd4_get_session (cstate->session); -atomic_inc (&session->se_client->cl_refcount); +atomic_inc (&clp->cl_refcount); +if (clp->cl_cb_state ==NFSD4_CB_DOWN ) + seq->status_flags |=SEQ4_STATUS_CB_PATH_DOWN ; }kfree (conn);spin_unlock (&client_lock);
fs/nfsd/nfs4xdr.c +2 -6
@@ -3137,13 +3137,9 @@ nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,WRITE32 (seq->seqid);WRITE32 (seq->slotid);WRITE32 (seq->maxslots); -/* -* FIXME: for now: -* target_maxslots = maxslots -* status_flags = 0 -*/ +/* For now: target_maxslots = maxslots */ WRITE32 (seq->maxslots); -WRITE32 (0 ); +WRITE32 (seq->status_flags);ADJUST_ARGS (); resp->cstate.datap = p;/* DRC cache data pointer */
fs/nfsd/xdr4.h +1 -1
@@ -378,8 +378,8 @@ struct nfsd4_sequence {u32 cachethis;/* request */ #if 0 u32 target_maxslots;/* response */ -u32 status_flags;/* response */ #endif /* not yet */ +u32 status_flags;/* response */ };struct nfsd4_destroy_session {
Signature · 1 line
1.7.1
PATCH 9/14 nfsd4: re-probe callback on connection loss
In reply to J. Bruce FieldsThis makes sure we set the sequence flag when necessary.
--- fs/nfsd/nfs4state.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
fs/nfsd/nfs4state.c +1 -1
@@ -642,7 +642,7 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u)free_conn (c); }spin_unlock (&clp->cl_lock); -/* XXX: mark callback for update, probe callback */ +nfsd4_probe_callback (clp); }static struct nfsd4_conn *alloc_conn (struct svc_rqst *rqstp,u32 flags)
Signature · 1 line
1.7.1
PATCH 10/14 nfsd4: make sure sequence flags are set after destroy_session
In reply to J. Bruce FieldsIf this loses any backchannel, make sure we have a chance to notice that and set the sequence flags.
--- fs/nfsd/nfs4callback.c | 6 ++++++ fs/nfsd/nfs4state.c | 3 +-- fs/nfsd/state.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-)
fs/nfsd/nfs4callback.c +6 -0
@@ -589,6 +589,12 @@ void nfsd4_probe_callback(struct nfs4_client *clp)do_probe_callback (clp); } +void nfsd4_probe_callback_sync (struct nfs4_client *clp) +{ +nfsd4_probe_callback (clp); +flush_workqueue (callback_wq); +} +void nfsd4_change_callback (struct nfs4_client *clp,struct nfs4_cb_conn *conn) { clp->cl_cb_state =NFSD4_CB_UNKNOWN ;
fs/nfsd/nfs4state.c +1 -2
@@ -1686,8 +1686,7 @@ nfsd4_destroy_session(struct svc_rqst *r,spin_unlock (&client_lock);nfs4_lock_state (); -/* wait for callbacks */ -nfsd4_shutdown_callback (ses->se_client); +nfsd4_probe_callback_sync (ses->se_client);nfs4_unlock_state ();nfsd4_del_conns (ses);
fs/nfsd/state.h +1 -0
@@ -464,6 +464,7 @@ extern __be32 nfs4_check_open_reclaim(clientid_t *clid);extern void nfs4_free_stateowner (struct kref *kref);extern int set_callback_cred (void );extern void nfsd4_probe_callback (struct nfs4_client *clp); +extern void nfsd4_probe_callback_sync (struct nfs4_client *clp);extern void nfsd4_change_callback (struct nfs4_client *clp,struct nfs4_cb_conn *);extern void nfsd4_do_callback_rpc (struct work_struct *);extern void nfsd4_cb_recall (struct nfs4_delegation *dp);
Signature · 1 line
1.7.1
PATCH 11/14 nfsd4: add helper function to run callbacks
In reply to J. Bruce Fields--- fs/nfsd/nfs4callback.c | 9 +++++++-- 1 files changed, 7 insertions(+), 2 deletions(-)
fs/nfsd/nfs4callback.c +7 -2
@@ -560,6 +560,11 @@ int set_callback_cred(void)static struct workqueue_struct *callback_wq; +static void run_nfsd4_cb (struct nfsd4_callback *cb) +{ +queue_work (callback_wq, &cb->cb_work); +} +static void do_probe_callback (struct nfs4_client *clp) {struct nfsd4_callback *cb = &clp->cl_cb_null;
@@ -574,7 +579,7 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_ops = &nfsd4_cb_probe_ops; -queue_work (callback_wq, &cb->cb_work); +run_nfsd4_cb (cb); }/*
@@ -859,5 +864,5 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) cb->cb_ops = &nfsd4_cb_recall_ops; dp->dl_retries =1 ; -queue_work (callback_wq, &dp->dl_recall.cb_work); +run_nfsd4_cb (&dp->dl_recall); }
Signature · 1 line
1.7.1
PATCH 12/14 nfsd4: give out delegations more quickly in 4.1 case
In reply to J. Bruce Fields--- fs/nfsd/nfs4state.c | 17 +++++++++++++++-- 1 files changed, 15 insertions(+), 2 deletions(-)
fs/nfsd/nfs4state.c +15 -2
@@ -2627,6 +2627,19 @@ nfs4_set_claim_prev(struct nfsd4_open *open) open->op_stateowner->so_client->cl_firststate =1 ; } +/* Should we give out recallable state?: */ +static bool nfsd4_cb_channel_good (struct nfs4_client *clp) +{ +if (clp->cl_cb_state ==NFSD4_CB_UP ) +return true; +/* +* In the sessions case, since we don't have to establish a +* separate connection for callbacks, we assume it's OK +* until we hear otherwise: +*/ +return clp->cl_minorversion && clp->cl_cb_state ==NFSD4_CB_UNKNOWN ; +} +/* * Attempt to hand out a delegation. */
@@ -2635,11 +2648,11 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta {struct nfs4_delegation *dp;struct nfs4_stateowner *sop = stp->st_stateowner; -/* XXX: or unknown and nfsv4.1: */ -int cb_up = (sop->so_client->cl_cb_state ==NFSD4_CB_UP ); +int cb_up;struct file_lock *fl;int status, flag =0 ; + cb_up =nfsd4_cb_channel_good (sop->so_client); flag =NFS4_OPEN_DELEGATE_NONE ; open->op_recall =0 ;switch (open->op_claim_type) {
Signature · 1 line
1.7.1
PATCH 13/14 nfsd4: simplify nfsd4_cb_prepare
In reply to J. Bruce FieldsRemove handling for a nonexistant case (status && !-EAGAIN).
--- fs/nfsd/nfs4callback.c | 26 ++++---------------------- 1 files changed, 4 insertions(+), 22 deletions(-)
fs/nfsd/nfs4callback.c +4 -22
@@ -613,24 +613,14 @@ void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)* If the slot is available, then mark it busy. Otherwise, set the * thread for sleeping on the callback RPC wait queue. */ -static int nfsd41_cb_setup_sequence (struct nfs4_client *clp, -struct rpc_task *task) +static bool nfsd41_cb_get_slot (struct nfs4_client *clp,struct rpc_task *task) { -u32 *ptr = (u32 *)clp->cl_cb_session->se_sessionid.data; -int status =0 ; - -dprintk ("%s: %u:%u:%u:%u\n" , __func__, - ptr[0 ], ptr[1 ], ptr[2 ], ptr[3 ]); -if (test_and_set_bit (0 , &clp->cl_cb_slot_busy) !=0 ) {rpc_sleep_on (&clp->cl_cb_waitq, task,NULL );dprintk ("%s slot is busy\n" , __func__); - status = -EAGAIN ; - goto out; +return false; } -out: -dprintk ("%s status=%d\n" , __func__, status); -return status; +return true; }/*
@@ -643,19 +633,11 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata) struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);struct nfs4_client *clp = dp->dl_client;u32 minorversion = clp->cl_minorversion; -int status =0 ; cb->cb_minorversion = minorversion;if (minorversion) { - status =nfsd41_cb_setup_sequence (clp, task); -if (status) { -if (status != -EAGAIN ) { -/* terminate rpc task */ - task->tk_status = status; - task->tk_action =NULL ; - } +if (!nfsd41_cb_get_slot (clp, task))return ; - } }rpc_call_start (task); }
Signature · 1 line
1.7.1
PATCH 14/14 nfsd4: allow restarting callbacks
In reply to J. Bruce FieldsIf we lose the backchannel and then the client repairs the problem, resend any callbacks.
We use a new cb_done flag to track whether there is still work to be done for the callback or whether it can be destroyed with the rpc.
--- fs/nfsd/nfs4callback.c | 34 ++++++++++++++++++++++++++++------ fs/nfsd/nfs4state.c | 1 + fs/nfsd/state.h | 3 +++ 3 files changed, 32 insertions(+), 6 deletions(-)
fs/nfsd/nfs4callback.c +28 -6
@@ -639,6 +639,10 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)if (!nfsd41_cb_get_slot (clp, task))return ; } + cb->cb_done = false; +spin_lock (&clp->cl_lock); +list_add (&cb->cb_per_client, &clp->cl_callbacks); +spin_unlock (&clp->cl_lock);rpc_call_start (task); }
@@ -681,8 +685,11 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)return ; } +if (cb->cb_done) +return ;switch (task->tk_status) {case 0 : + cb->cb_done = true;return ;case -EBADHANDLE :case -NFS4ERR_BAD_STATEID :
@@ -695,7 +702,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)if (current_rpc_client != task->tk_client) {/* queue a callback on the new connection: */ atomic_inc (&dp->dl_count); -nfsd4_cb_recall (dp); +run_nfsd4_cb (&dp->dl_recall);return ; } }
@@ -704,16 +711,23 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) task->tk_status =0 ;rpc_restart_call_prepare (task);return ; - }else -nfsd4_mark_cb_down (clp, task->tk_status); + } +nfsd4_mark_cb_down (clp, task->tk_status); + cb->cb_done = true; }static void nfsd4_cb_recall_release (void *calldata) {struct nfsd4_callback *cb = calldata; +struct nfs4_client *clp = cb->cb_clp; struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall); -nfs4_put_delegation (dp); +if (cb->cb_done) { +spin_lock (&clp->cl_lock); +list_del (&cb->cb_per_client); +spin_unlock (&clp->cl_lock); +nfs4_put_delegation (dp); + } }static const struct rpc_call_ops nfsd4_cb_recall_ops = {
@@ -808,8 +822,13 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb)spin_unlock (&clp->cl_lock); err =setup_callback_client (clp, &conn, ses); -if (err) +if (err) {warn_no_callback_path (clp, err); +return ; + } +/* Yay, the callback channel's back! Restart any callbacks: */ +list_for_each_entry (cb, &clp->cl_callbacks, cb_per_client) +run_nfsd4_cb (cb); }void nfsd4_do_callback_rpc (struct work_struct *w)
@@ -834,10 +853,11 @@ void nfsd4_do_callback_rpc(struct work_struct *w)void nfsd4_cb_recall (struct nfs4_delegation *dp) {struct nfsd4_callback *cb = &dp->dl_recall; +struct nfs4_client *clp = dp->dl_client; dp->dl_retries =1 ; cb->cb_op = dp; - cb->cb_clp = dp->dl_client; + cb->cb_clp = clp; cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL ]; cb->cb_msg.rpc_argp = cb; cb->cb_msg.rpc_resp = cb;
@@ -846,5 +866,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) cb->cb_ops = &nfsd4_cb_recall_ops; dp->dl_retries =1 ; + cb->cb_done = true; +run_nfsd4_cb (&dp->dl_recall); }
fs/nfsd/nfs4state.c +1 -0
@@ -1077,6 +1077,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,INIT_LIST_HEAD (&clp->cl_openowners);INIT_LIST_HEAD (&clp->cl_delegations);INIT_LIST_HEAD (&clp->cl_lru); +INIT_LIST_HEAD (&clp->cl_callbacks);spin_lock_init (&clp->cl_lock);INIT_WORK (&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); clp->cl_time =get_seconds ();
fs/nfsd/state.h +3 -0
@@ -68,10 +68,12 @@ typedef struct {struct nfsd4_callback {void *cb_op;struct nfs4_client *cb_clp; +struct list_head cb_per_client;u32 cb_minorversion;struct rpc_message cb_msg;const struct rpc_call_ops *cb_ops;struct work_struct cb_work; +bool cb_done; };struct nfs4_delegation {
@@ -248,6 +250,7 @@ struct nfs4_client {int cl_cb_state;struct nfsd4_callback cl_cb_null;struct nfsd4_session *cl_cb_session; +struct list_head cl_callbacks;/* list of in-progress callbacks */ /* for all client information that callback code might need: */ spinlock_t cl_lock;
Signature · 1 line
1.7.1