2010-09-30 16:19:28

by J. Bruce Fields

[permalink] [raw]
Subject: 4.1 patches

I've been working on fixing the server's sessions to bring the sessions
and trunking requirements up to spec.

I'm still implementing and debugging the patches that do that, but
the following patches do some initial cleanup (amd minor bugfixes) that
I found along the way, so I'm probably ready to queue these up for
2.6.37 absent any objections.

They also add a list of connections associated with each session, though
that's not yet used for anything important. (I do use it to enforce the
requirement that DESTROY_SESSION be sent over a connection already
associated with that session. As has been pointed out on the ietf list,
the basis for that requirement is a little murky, so I may back out that
last patch--for now it at least gives me an easy way to check (using a
pynfs41 DESTROY_SESSION test) that the list of connections is correct).)

--b.


2010-09-30 21:33:31

by Benny Halevy

[permalink] [raw]
Subject: Re: [PATCH 15/16] nfsd4: add new connections to session

On 2010-09-30 18:19, J. Bruce Fields wrote:
> As long as we're not implementing any session security, we should just
> automatically add any new connections that come along to the list of
> sessions associated with the session.

Doesn't the client need to send BIND_CONN_TO_SESSION?

18.34. Operation 41: BIND_CONN_TO_SESSION - Associate Connection with Session:

BIND_CONN_TO_SESSION is used to associate additional connections with
a session. It MUST be used on the connection being associated with
the session.

Also, with these patches, can we set SEQ4_STATUS_CB_PATH_DOWN if there's
no backchannel?

Benny

>
> Signed-off-by: J. Bruce Fields <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 47 insertions(+), 2 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index 3b4d74c..596702e 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -658,13 +658,18 @@ static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp)
> return conn;
> }
>
> +static void __nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
> +{
> + conn->cn_session = ses;
> + list_add(&conn->cn_persession, &ses->se_conns);
> +}
> +
> static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
> {
> struct nfs4_client *clp = ses->se_client;
>
> spin_lock(&clp->cl_lock);
> - conn->cn_session = ses;
> - list_add(&conn->cn_persession, &ses->se_conns);
> + __nfsd4_hash_conn(conn, ses);
> spin_unlock(&clp->cl_lock);
> }
>
> @@ -1612,6 +1617,44 @@ out:
> return status;
> }
>
> +static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s)
> +{
> + struct nfsd4_conn *c;
> +
> + list_for_each_entry(c, &s->se_conns, cn_persession) {
> + if (c->cn_xprt == r->rq_xprt) {
> + return c;
> + }
> + }
> + return NULL;
> +}
> +
> +static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
> +{
> + struct nfs4_client *clp = ses->se_client;
> + struct nfsd4_conn *c, *new = NULL;
> +
> + spin_lock(&clp->cl_lock);
> + c = __nfsd4_find_conn(rqstp, ses);
> + spin_unlock(&clp->cl_lock);
> + if (c)
> + return;
> +
> + new = alloc_conn(rqstp);
> +
> + spin_lock(&clp->cl_lock);
> + c = __nfsd4_find_conn(rqstp, ses);
> + if (c) {
> + spin_unlock(&clp->cl_lock);
> + free_conn(new);
> + return;
> + }
> + __nfsd4_hash_conn(new, ses);
> + spin_unlock(&clp->cl_lock);
> + nfsd4_register_conn(new);
> + return;
> +}
> +
> __be32
> nfsd4_sequence(struct svc_rqst *rqstp,
> struct nfsd4_compound_state *cstate,
> @@ -1656,6 +1699,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
> if (status)
> goto out;
>
> + nfsd4_sequence_check_conn(rqstp, session);
> +
> /* Success! bump slot seqid */
> slot->sl_inuse = true;
> slot->sl_seqid = seq->seqid;

2010-09-30 16:19:40

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 09/16] nfsd4: fix alloc_init_session return type

This returns an nfs error, not -ERRNO.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 4 +---
1 files changed, 1 insertions(+), 3 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index e305794..ebddcc1 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -639,9 +639,7 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca)
return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
}

-static int
-alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
- struct nfsd4_create_session *cses)
+static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
{
struct nfsd4_session *new, tmp;
struct nfsd4_slot *sp;
--
1.7.0.4


2010-09-30 16:19:42

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 10/16] nfsd4: clean up session allocation

Changes:
- make sure session memory reservation is released on failure
path.
- use min_t()/min() for more compact code in several places.
- break alloc_init_session into smaller pieces.
- miscellaneous other cleanup.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 211 +++++++++++++++++++++-----------------------------
1 files changed, 89 insertions(+), 122 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index ebddcc1..f86476c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -533,94 +533,6 @@ gen_sessionid(struct nfsd4_session *ses)
*/
#define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)

-/*
- * Give the client the number of ca_maxresponsesize_cached slots it
- * requests, of size bounded by NFSD_SLOT_CACHE_SIZE,
- * NFSD_MAX_MEM_PER_SESSION, and nfsd_drc_max_mem. Do not allow more
- * than NFSD_MAX_SLOTS_PER_SESSION.
- *
- * If we run out of reserved DRC memory we should (up to a point)
- * re-negotiate active sessions and reduce their slot usage to make
- * rooom for new connections. For now we just fail the create session.
- */
-static int set_forechannel_drc_size(struct nfsd4_channel_attrs *fchan)
-{
- int mem, size = fchan->maxresp_cached;
-
- if (fchan->maxreqs < 1)
- return nfserr_inval;
-
- if (size < NFSD_MIN_HDR_SEQ_SZ)
- size = NFSD_MIN_HDR_SEQ_SZ;
- size -= NFSD_MIN_HDR_SEQ_SZ;
- if (size > NFSD_SLOT_CACHE_SIZE)
- size = NFSD_SLOT_CACHE_SIZE;
-
- /* bound the maxreqs by NFSD_MAX_MEM_PER_SESSION */
- mem = fchan->maxreqs * size;
- if (mem > NFSD_MAX_MEM_PER_SESSION) {
- fchan->maxreqs = NFSD_MAX_MEM_PER_SESSION / size;
- if (fchan->maxreqs > NFSD_MAX_SLOTS_PER_SESSION)
- fchan->maxreqs = NFSD_MAX_SLOTS_PER_SESSION;
- mem = fchan->maxreqs * size;
- }
-
- spin_lock(&nfsd_drc_lock);
- /* bound the total session drc memory ussage */
- if (mem + nfsd_drc_mem_used > nfsd_drc_max_mem) {
- fchan->maxreqs = (nfsd_drc_max_mem - nfsd_drc_mem_used) / size;
- mem = fchan->maxreqs * size;
- }
- nfsd_drc_mem_used += mem;
- spin_unlock(&nfsd_drc_lock);
-
- if (fchan->maxreqs == 0)
- return nfserr_jukebox;
-
- fchan->maxresp_cached = size + NFSD_MIN_HDR_SEQ_SZ;
- return 0;
-}
-
-/*
- * fchan holds the client values on input, and the server values on output
- * sv_max_mesg is the maximum payload plus one page for overhead.
- */
-static int init_forechannel_attrs(struct svc_rqst *rqstp,
- struct nfsd4_channel_attrs *session_fchan,
- struct nfsd4_channel_attrs *fchan)
-{
- int status = 0;
- __u32 maxcount = nfsd_serv->sv_max_mesg;
-
- /* headerpadsz set to zero in encode routine */
-
- /* Use the client's max request and max response size if possible */
- if (fchan->maxreq_sz > maxcount)
- fchan->maxreq_sz = maxcount;
- session_fchan->maxreq_sz = fchan->maxreq_sz;
-
- if (fchan->maxresp_sz > maxcount)
- fchan->maxresp_sz = maxcount;
- session_fchan->maxresp_sz = fchan->maxresp_sz;
-
- /* Use the client's maxops if possible */
- if (fchan->maxops > NFSD_MAX_OPS_PER_COMPOUND)
- fchan->maxops = NFSD_MAX_OPS_PER_COMPOUND;
- session_fchan->maxops = fchan->maxops;
-
- /* FIXME: Error means no more DRC pages so the server should
- * recover pages from existing sessions. For now fail session
- * creation.
- */
- status = set_forechannel_drc_size(fchan);
-
- session_fchan->maxresp_cached = fchan->maxresp_cached;
- session_fchan->maxreqs = fchan->maxreqs;
-
- dprintk("%s status %d\n", __func__, status);
- return status;
-}
-
static void
free_session_slots(struct nfsd4_session *ses)
{
@@ -639,63 +551,118 @@ static inline int slot_bytes(struct nfsd4_channel_attrs *ca)
return ca->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
}

-static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+static int nfsd4_sanitize_slot_size(u32 size)
{
- struct nfsd4_session *new, tmp;
- struct nfsd4_slot *sp;
- int idx, slotsize, cachesize, i;
- int status;
+ size -= NFSD_MIN_HDR_SEQ_SZ; /* We don't cache the rpc header */
+ size = min_t(u32, size, NFSD_SLOT_CACHE_SIZE);

- memset(&tmp, 0, sizeof(tmp));
+ return size;
+}

- /* FIXME: For now, we just accept the client back channel attributes. */
- tmp.se_bchannel = cses->back_channel;
- status = init_forechannel_attrs(rqstp, &tmp.se_fchannel,
- &cses->fore_channel);
- if (status)
- goto out;
+/*
+ * XXX: If we run out of reserved DRC memory we could (up to a point)
+ * re-negotiate active sessions and reduce their slot usage to make
+ * rooom for new connections. For now we just fail the create session.
+ */
+static int nfsd4_get_drc_mem(int slotsize, u32 num)
+{
+ int avail;

- BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
- + sizeof(struct nfsd4_session) > PAGE_SIZE);
+ num = min_t(u32, num, NFSD_MAX_SLOTS_PER_SESSION);

- status = nfserr_jukebox;
- /* allocate struct nfsd4_session and slot table pointers in one piece */
- slotsize = tmp.se_fchannel.maxreqs * sizeof(struct nfsd4_slot *);
- new = kzalloc(sizeof(*new) + slotsize, GFP_KERNEL);
- if (!new)
- goto out;
+ spin_lock(&nfsd_drc_lock);
+ avail = min_t(int, NFSD_MAX_MEM_PER_SESSION,
+ nfsd_drc_max_mem - nfsd_drc_mem_used);
+ num = min_t(int, num, avail / slotsize);
+ nfsd_drc_mem_used += num * slotsize;
+ spin_unlock(&nfsd_drc_lock);
+
+ return num;
+}
+
+static void nfsd4_put_drc_mem(int slotsize, int num)
+{
+ spin_lock(&nfsd_drc_lock);
+ nfsd_drc_mem_used -= slotsize * num;
+ spin_unlock(&nfsd_drc_lock);
+}
+
+static struct nfsd4_session *alloc_session(int slotsize, int numslots)
+{
+ struct nfsd4_session *new;
+ int mem, i;

- memcpy(new, &tmp, sizeof(*new));
+ BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
+ + sizeof(struct nfsd4_session) > PAGE_SIZE);
+ mem = numslots * sizeof(struct nfsd4_slot *);

+ new = kzalloc(sizeof(*new) + mem, GFP_KERNEL);
+ if (!new)
+ return NULL;
/* allocate each struct nfsd4_slot and data cache in one piece */
- cachesize = slot_bytes(&new->se_fchannel);
- for (i = 0; i < new->se_fchannel.maxreqs; i++) {
- sp = kzalloc(sizeof(*sp) + cachesize, GFP_KERNEL);
- if (!sp)
+ for (i = 0; i < numslots; i++) {
+ mem = sizeof(struct nfsd4_slot) + slotsize;
+ new->se_slots[i] = kzalloc(mem, GFP_KERNEL);
+ if (!new->se_slots[i])
goto out_free;
- new->se_slots[i] = sp;
}
+ return new;
+out_free:
+ while (i--)
+ kfree(new->se_slots[i]);
+ kfree(new);
+ return NULL;
+}
+
+static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize)
+{
+ u32 maxrpc = nfsd_serv->sv_max_mesg;
+
+ new->maxreqs = numslots;
+ new->maxresp_cached = slotsize + NFSD_MIN_HDR_SEQ_SZ;
+ new->maxreq_sz = min_t(u32, req->maxreq_sz, maxrpc);
+ new->maxresp_sz = min_t(u32, req->maxresp_sz, maxrpc);
+ new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
+}
+
+static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
+{
+ struct nfsd4_session *new;
+ struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
+ int numslots, slotsize;
+ int idx;
+
+ /*
+ * Note decreasing slot size below client's request may
+ * make it difficult for client to function correctly, whereas
+ * decreasing the number of slots will (just?) affect
+ * performance. When short on memory we therefore prefer to
+ * decrease number of slots instead of their size.
+ */
+ slotsize = nfsd4_sanitize_slot_size(fchan->maxresp_cached);
+ numslots = nfsd4_get_drc_mem(slotsize, fchan->maxreqs);
+
+ new = alloc_session(slotsize, numslots);
+ if (!new) {
+ nfsd4_put_drc_mem(slotsize, fchan->maxreqs);
+ return nfserr_jukebox;
+ }
+ init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize);

new->se_client = clp;
gen_sessionid(new);
- idx = hash_sessionid(&new->se_sessionid);
memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);

new->se_flags = cses->flags;
kref_init(&new->se_ref);
+ idx = hash_sessionid(&new->se_sessionid);
spin_lock(&client_lock);
list_add(&new->se_hash, &sessionid_hashtbl[idx]);
list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&client_lock);

- status = nfs_ok;
-out:
- return status;
-out_free:
- free_session_slots(new);
- kfree(new);
- goto out;
+ return nfs_ok;
}

/* caller must hold client_lock */
--
1.7.0.4


2010-09-30 16:19:45

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 16/16] nfsd4: enforce DESTROY_SESSION connection requirement

From: J. Bruce Fields <[email protected]>

This is the first MUST of the second paragraph of 18.37.3: a
destroy_session should arrive over a connection associated with the
session being destroyed.

XXX: The motivation for that must is somewhat unclear. We may relax
this check eventually. For now it at least gives a simple way to test
whether the connection list implementation is correct.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 41 +++++++++++++++++++++++++++++------------
1 files changed, 29 insertions(+), 12 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 596702e..01fd1b7 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -1573,6 +1573,29 @@ static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4
return !memcmp(sid, &session->se_sessionid, sizeof(*sid));
}

+static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s)
+{
+ struct nfsd4_conn *c;
+
+ list_for_each_entry(c, &s->se_conns, cn_persession) {
+ if (c->cn_xprt == r->rq_xprt) {
+ return c;
+ }
+ }
+ return NULL;
+}
+
+static bool nfsd4_associated_connection(struct svc_rqst *r, struct nfsd4_session *s)
+{
+ struct nfs4_client *clp = s->se_client;
+ struct nfsd4_conn *c;
+
+ spin_lock(&clp->cl_lock);
+ c = __nfsd4_find_conn(r, s);
+ spin_unlock(&clp->cl_lock);
+ return c;
+}
+
__be32
nfsd4_destroy_session(struct svc_rqst *r,
struct nfsd4_compound_state *cstate,
@@ -1600,6 +1623,12 @@ nfsd4_destroy_session(struct svc_rqst *r,
goto out;
}

+ if (!nfsd4_associated_connection(r, ses)) {
+ spin_unlock(&client_lock);
+ status = nfserr_conn_not_bound_to_session;
+ goto out;
+ }
+
unhash_session(ses);
spin_unlock(&client_lock);

@@ -1617,18 +1646,6 @@ out:
return status;
}

-static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s)
-{
- struct nfsd4_conn *c;
-
- list_for_each_entry(c, &s->se_conns, cn_persession) {
- if (c->cn_xprt == r->rq_xprt) {
- return c;
- }
- }
- return NULL;
-}
-
static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
--
1.7.0.4


2010-09-30 16:19:45

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 15/16] nfsd4: add new connections to session

As long as we're not implementing any session security, we should just
automatically add any new connections that come along to the list of
sessions associated with the session.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
1 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 3b4d74c..596702e 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -658,13 +658,18 @@ static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp)
return conn;
}

+static void __nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
+{
+ conn->cn_session = ses;
+ list_add(&conn->cn_persession, &ses->se_conns);
+}
+
static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;

spin_lock(&clp->cl_lock);
- conn->cn_session = ses;
- list_add(&conn->cn_persession, &ses->se_conns);
+ __nfsd4_hash_conn(conn, ses);
spin_unlock(&clp->cl_lock);
}

@@ -1612,6 +1617,44 @@ out:
return status;
}

+static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s)
+{
+ struct nfsd4_conn *c;
+
+ list_for_each_entry(c, &s->se_conns, cn_persession) {
+ if (c->cn_xprt == r->rq_xprt) {
+ return c;
+ }
+ }
+ return NULL;
+}
+
+static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+{
+ struct nfs4_client *clp = ses->se_client;
+ struct nfsd4_conn *c, *new = NULL;
+
+ spin_lock(&clp->cl_lock);
+ c = __nfsd4_find_conn(rqstp, ses);
+ spin_unlock(&clp->cl_lock);
+ if (c)
+ return;
+
+ new = alloc_conn(rqstp);
+
+ spin_lock(&clp->cl_lock);
+ c = __nfsd4_find_conn(rqstp, ses);
+ if (c) {
+ spin_unlock(&clp->cl_lock);
+ free_conn(new);
+ return;
+ }
+ __nfsd4_hash_conn(new, ses);
+ spin_unlock(&clp->cl_lock);
+ nfsd4_register_conn(new);
+ return;
+}
+
__be32
nfsd4_sequence(struct svc_rqst *rqstp,
struct nfsd4_compound_state *cstate,
@@ -1656,6 +1699,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
if (status)
goto out;

+ nfsd4_sequence_check_conn(rqstp, session);
+
/* Success! bump slot seqid */
slot->sl_inuse = true;
slot->sl_seqid = seq->seqid;
--
1.7.0.4


2010-09-30 16:19:39

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 03/16] nfsd4: rename nfs4_rpc_args->nfsd4_cb_args

From: J. Bruce Fields <[email protected]>

With apologies for the gratuitous rename, the new name seems more
helpful to me.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 14 +++++++-------
fs/nfsd/state.h | 4 ++--
2 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 02afb71..ae63f69 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -247,7 +247,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
}

static void
-encode_cb_sequence(struct xdr_stream *xdr, struct nfs4_rpc_args *args,
+encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *args,
struct nfs4_cb_compound_hdr *hdr)
{
__be32 *p;
@@ -279,7 +279,7 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)

static int
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
- struct nfs4_rpc_args *rpc_args)
+ struct nfsd4_cb_args *rpc_args)
{
struct xdr_stream xdr;
struct nfs4_delegation *args = rpc_args->args_op;
@@ -338,7 +338,7 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
* with a single slot.
*/
static int
-decode_cb_sequence(struct xdr_stream *xdr, struct nfs4_rpc_args *res,
+decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *res,
struct rpc_rqst *rqstp)
{
struct nfs4_sessionid id;
@@ -392,7 +392,7 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)

static int
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
- struct nfs4_rpc_args *args)
+ struct nfsd4_cb_args *args)
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr;
@@ -584,7 +584,7 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
struct rpc_task *task)
{
- struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
+ struct nfsd4_cb_args *args = task->tk_msg.rpc_argp;
u32 *ptr = (u32 *)clp->cl_sessionid.data;
int status = 0;

@@ -618,7 +618,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
{
struct nfs4_delegation *dp = calldata;
struct nfs4_client *clp = dp->dl_client;
- struct nfs4_rpc_args *args = task->tk_msg.rpc_argp;
+ struct nfsd4_cb_args *args = task->tk_msg.rpc_argp;
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
int status = 0;

@@ -755,7 +755,7 @@ static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
{
struct nfs4_client *clp = dp->dl_client;
struct rpc_clnt *clnt = clp->cl_cb_client;
- struct nfs4_rpc_args *args = &dp->dl_recall.cb_args;
+ struct nfsd4_cb_args *args = &dp->dl_recall.cb_args;
struct rpc_message msg = {
.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
.rpc_cred = callback_cred
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 59313f1..f988b90 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -64,14 +64,14 @@ typedef struct {
(s)->si_fileid, \
(s)->si_generation

-struct nfs4_rpc_args {
+struct nfsd4_cb_args {
void *args_op;
struct nfs4_client *args_clp;
u32 args_minorversion;
};

struct nfsd4_callback {
- struct nfs4_rpc_args cb_args;
+ struct nfsd4_cb_args cb_args;
struct work_struct cb_work;
};

--
1.7.0.4


2010-09-30 21:57:10

by J. Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 15/16] nfsd4: add new connections to session

On Thu, Sep 30, 2010 at 11:33:29PM +0200, Benny Halevy wrote:
> On 2010-09-30 18:19, J. Bruce Fields wrote:
> > As long as we're not implementing any session security, we should just
> > automatically add any new connections that come along to the list of
> > sessions associated with the session.
>
> Doesn't the client need to send BIND_CONN_TO_SESSION?
>
> 18.34. Operation 41: BIND_CONN_TO_SESSION - Associate Connection with Session:
>
> BIND_CONN_TO_SESSION is used to associate additional connections with
> a session. It MUST be used on the connection being associated with
> the session.

As I understand it, we just add them automatically, unless some sort of
state protection is in force. (Which we haven't implemented yet.)

> Also, with these patches, can we set SEQ4_STATUS_CB_PATH_DOWN if there's
> no backchannel?

Just debugging that part.... Tomorrow, hopefully!

--b.

>
> Benny
>
> >
> > Signed-off-by: J. Bruce Fields <[email protected]>
> > ---
> > fs/nfsd/nfs4state.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++--
> > 1 files changed, 47 insertions(+), 2 deletions(-)
> >
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index 3b4d74c..596702e 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -658,13 +658,18 @@ static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp)
> > return conn;
> > }
> >
> > +static void __nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
> > +{
> > + conn->cn_session = ses;
> > + list_add(&conn->cn_persession, &ses->se_conns);
> > +}
> > +
> > static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
> > {
> > struct nfs4_client *clp = ses->se_client;
> >
> > spin_lock(&clp->cl_lock);
> > - conn->cn_session = ses;
> > - list_add(&conn->cn_persession, &ses->se_conns);
> > + __nfsd4_hash_conn(conn, ses);
> > spin_unlock(&clp->cl_lock);
> > }
> >
> > @@ -1612,6 +1617,44 @@ out:
> > return status;
> > }
> >
> > +static struct nfsd4_conn *__nfsd4_find_conn(struct svc_rqst *r, struct nfsd4_session *s)
> > +{
> > + struct nfsd4_conn *c;
> > +
> > + list_for_each_entry(c, &s->se_conns, cn_persession) {
> > + if (c->cn_xprt == r->rq_xprt) {
> > + return c;
> > + }
> > + }
> > + return NULL;
> > +}
> > +
> > +static void nfsd4_sequence_check_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
> > +{
> > + struct nfs4_client *clp = ses->se_client;
> > + struct nfsd4_conn *c, *new = NULL;
> > +
> > + spin_lock(&clp->cl_lock);
> > + c = __nfsd4_find_conn(rqstp, ses);
> > + spin_unlock(&clp->cl_lock);
> > + if (c)
> > + return;
> > +
> > + new = alloc_conn(rqstp);
> > +
> > + spin_lock(&clp->cl_lock);
> > + c = __nfsd4_find_conn(rqstp, ses);
> > + if (c) {
> > + spin_unlock(&clp->cl_lock);
> > + free_conn(new);
> > + return;
> > + }
> > + __nfsd4_hash_conn(new, ses);
> > + spin_unlock(&clp->cl_lock);
> > + nfsd4_register_conn(new);
> > + return;
> > +}
> > +
> > __be32
> > nfsd4_sequence(struct svc_rqst *rqstp,
> > struct nfsd4_compound_state *cstate,
> > @@ -1656,6 +1699,8 @@ nfsd4_sequence(struct svc_rqst *rqstp,
> > if (status)
> > goto out;
> >
> > + nfsd4_sequence_check_conn(rqstp, session);
> > +
> > /* Success! bump slot seqid */
> > slot->sl_inuse = true;
> > slot->sl_seqid = seq->seqid;

2010-09-30 21:38:59

by J.Bruce Fields

[permalink] [raw]
Subject: Re: [PATCH 11/16] nfsd4: keep per-session list of connections

On Thu, Sep 30, 2010 at 11:21:50PM +0200, Benny Halevy wrote:
> On 2010-09-30 18:19, J. Bruce Fields wrote:
> > From: J. Bruce Fields <[email protected]>
> >
> > The spec requires us in various places to keep track of the connections
> > associated with each session.
> >
> > Signed-off-by: J. Bruce Fields <[email protected]>
> > ---
> > fs/nfsd/nfs4state.c | 69 +++++++++++++++++++++++++++++++++++++++-----------
> > fs/nfsd/state.h | 8 ++++++
> > include/linux/nfs4.h | 3 ++
> > 3 files changed, 65 insertions(+), 15 deletions(-)
> >
> > diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> > index f86476c..c7c1a7a 100644
> > --- a/fs/nfsd/nfs4state.c
> > +++ b/fs/nfsd/nfs4state.c
> > @@ -625,11 +625,58 @@ static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4
> > new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
> > }
> >
> > +
> > +static void free_conn(struct nfsd4_conn *c)
> > +{
> > + svc_xprt_put(c->cn_xprt);
> > + kfree(c);
> > +}
> > +
> > +void free_session(struct kref *kref)
> > +{
> > + struct nfsd4_session *ses;
> > + int mem;
> > +
> > + ses = container_of(kref, struct nfsd4_session, se_ref);
> > + while (!list_empty(&ses->se_conns)) {
> > + struct nfsd4_conn *c;
> > + c = list_first_entry(&ses->se_conns, struct nfsd4_conn, cn_persession);
> > + list_del(&c->cn_persession);
> > + free_conn(c);
> > + }
> > + spin_lock(&nfsd_drc_lock);
> > + mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
> > + nfsd_drc_mem_used -= mem;
> > + spin_unlock(&nfsd_drc_lock);
> > + free_session_slots(ses);
> > + kfree(ses);
> > +}
> > +
> > +
> > static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
> > {
> > struct nfsd4_session *new;
> > struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
> > int numslots, slotsize;
> > + int status;
> > int idx;
> >
> > /*
> > @@ -654,6 +701,8 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp
> > memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
> > NFS4_MAX_SESSIONID_LEN);
> >
> > + INIT_LIST_HEAD(&new->se_conns);
> > +
> > new->se_flags = cses->flags;
> > kref_init(&new->se_ref);
> > idx = hash_sessionid(&new->se_sessionid);
> > @@ -662,6 +711,11 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp
> > list_add(&new->se_perclnt, &clp->cl_sessions);
> > spin_unlock(&client_lock);
> >
> > + status = nfsd4_new_conn(rqstp, new);
> > + if (status) {
> > + free_session(&new->se_ref);
> > + return nfserr_jukebox;
>
> why not return status?

The status is sort of bogus; all that can really happen is an allocation
failure:

> > +static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
> > +{
> > + struct nfs4_client *clp = ses->se_client;
> > + struct nfsd4_conn *conn;
> > +
> > + conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
> > + if (!conn)
> > + return nfserr_jukebox;
> > + conn->cn_flags = NFS4_CDFC4_FORE;
> > + svc_xprt_get(rqstp->rq_xprt);
> > + conn->cn_xprt = rqstp->rq_xprt;
> > +
> > + spin_lock(&clp->cl_lock);
> > + list_add(&conn->cn_persession, &ses->se_conns);
> > + spin_unlock(&clp->cl_lock);
> > +
> > + return nfs_ok;
> > +}

I'd actually rather change alloc_init_session() to return NULL or the
new session. But, yeah, it grates a bit to have a status return that's
ignored. Hm.

> > diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> > index 07e40c6..79b15fb 100644
> > --- a/include/linux/nfs4.h
> > +++ b/include/linux/nfs4.h
> > @@ -61,6 +61,9 @@
> > #define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000
> > #define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED 0x20000
> >
> > +#define NFS4_CDFC4_FORE 0x1
> > +#define NFS4_CDFC4_BACK 0x2
> > +
>
> nit: I think that defining flags shifted bits is clearer, e.g.:
>
> enum nfsd4_channel_dir {
> NFS4_CDFC4_FORE = 1 << 0,
> NFS4_CDFC4_BACK = 1 << 1,

I see what you mean.

I was just taking the definitions from the spec. And I should add
FORE_OR_BOTH and BACK_OR_BOTH some time, neither powers of two. I
dunno.

--b.

> };
>
> Benny
>
> > #define NFS4_SET_TO_SERVER_TIME 0
> > #define NFS4_SET_TO_CLIENT_TIME 1
> >
>

2010-09-30 16:19:44

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 13/16] nfsd4: use callbacks on svc_xprt_deletion

From: J. Bruce Fields <[email protected]>

Remove connections from the list when they go down.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 51 ++++++++++++++++++++++++++++++++++++++++++---------
fs/nfsd/state.h | 3 +++
2 files changed, 45 insertions(+), 9 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index c7c1a7a..b7e9793 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -625,6 +625,25 @@ static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4
new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
}

+static void free_conn(struct nfsd4_conn *c)
+{
+ svc_xprt_put(c->cn_xprt);
+ kfree(c);
+}
+
+static void nfsd4_conn_lost(struct svc_xpt_user *u)
+{
+ struct nfsd4_conn *c = container_of(u, struct nfsd4_conn, cn_xpt_user);
+ struct nfs4_client *clp = c->cn_session->se_client;
+
+ spin_lock(&clp->cl_lock);
+ if (!list_empty(&c->cn_persession)) {
+ list_del(&c->cn_persession);
+ free_conn(c);
+ }
+ spin_unlock(&clp->cl_lock);
+}
+
static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
{
struct nfs4_client *clp = ses->se_client;
@@ -636,18 +655,34 @@ static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
conn->cn_flags = NFS4_CDFC4_FORE;
svc_xprt_get(rqstp->rq_xprt);
conn->cn_xprt = rqstp->rq_xprt;
+ conn->cn_session = ses;

spin_lock(&clp->cl_lock);
list_add(&conn->cn_persession, &ses->se_conns);
spin_unlock(&clp->cl_lock);

+ conn->cn_xpt_user.callback = nfsd4_conn_lost;
+ register_xpt_user(rqstp->rq_xprt, &conn->cn_xpt_user);
return nfs_ok;
}

-static void free_conn(struct nfsd4_conn *c)
+static void nfsd4_del_conns(struct nfsd4_session *s)
{
- svc_xprt_put(c->cn_xprt);
- kfree(c);
+ struct nfs4_client *clp = s->se_client;
+ struct nfsd4_conn *c;
+
+ spin_lock(&clp->cl_lock);
+ while (!list_empty(&s->se_conns)) {
+ c = list_first_entry(&s->se_conns, struct nfsd4_conn, cn_persession);
+ list_del_init(&c->cn_persession);
+ spin_unlock(&clp->cl_lock);
+
+ unregister_xpt_user(c->cn_xprt, &c->cn_xpt_user);
+ free_conn(c);
+
+ spin_lock(&clp->cl_lock);
+ }
+ spin_unlock(&clp->cl_lock);
}

void free_session(struct kref *kref)
@@ -656,12 +691,7 @@ void free_session(struct kref *kref)
int mem;

ses = container_of(kref, struct nfsd4_session, se_ref);
- while (!list_empty(&ses->se_conns)) {
- struct nfsd4_conn *c;
- c = list_first_entry(&ses->se_conns, struct nfsd4_conn, cn_persession);
- list_del(&c->cn_persession);
- free_conn(c);
- }
+ nfsd4_del_conns(ses);
spin_lock(&nfsd_drc_lock);
mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
nfsd_drc_mem_used -= mem;
@@ -1552,6 +1582,9 @@ nfsd4_destroy_session(struct svc_rqst *r,
/* wait for callbacks */
nfsd4_shutdown_callback(ses->se_client);
nfs4_unlock_state();
+
+ nfsd4_del_conns(ses);
+
nfsd4_put_session(ses);
status = nfs_ok;
out:
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 29413c2..8d5e237 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -35,6 +35,7 @@
#ifndef _NFSD4_STATE_H
#define _NFSD4_STATE_H

+#include <linux/sunrpc/svc_xprt.h>
#include <linux/nfsd/nfsfh.h>
#include "nfsfh.h"

@@ -155,6 +156,8 @@ struct nfsd4_clid_slot {
struct nfsd4_conn {
struct list_head cn_persession;
struct svc_xprt *cn_xprt;
+ struct svc_xpt_user cn_xpt_user;
+ struct nfsd4_session *cn_session;
/* CDFC4_FORE, CDFC4_BACK: */
unsigned char cn_flags;
};
--
1.7.0.4


2010-09-30 16:19:38

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 04/16] nfsd4: generic callback code

From: J. Bruce Fields <[email protected]>

Make the recall callback code more generic, so that other callbacks
will be able to use it too.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 70 +++++++++++++++++++++--------------------------
fs/nfsd/state.h | 2 +
2 files changed, 33 insertions(+), 39 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index ae63f69..2289ad5 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -584,7 +584,6 @@ void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
struct rpc_task *task)
{
- struct nfsd4_cb_args *args = task->tk_msg.rpc_argp;
u32 *ptr = (u32 *)clp->cl_sessionid.data;
int status = 0;

@@ -597,14 +596,6 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
status = -EAGAIN;
goto out;
}
-
- /*
- * We'll need the clp during XDR encoding and decoding,
- * and the sequence during decoding to verify the reply
- */
- args->args_clp = clp;
- task->tk_msg.rpc_resp = args;
-
out:
dprintk("%s status=%d\n", __func__, status);
return status;
@@ -616,7 +607,8 @@ out:
*/
static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
{
- struct nfs4_delegation *dp = calldata;
+ struct nfsd4_callback *cb = calldata;
+ struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;
struct nfsd4_cb_args *args = task->tk_msg.rpc_argp;
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
@@ -639,7 +631,8 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)

static void nfsd4_cb_done(struct rpc_task *task, void *calldata)
{
- struct nfs4_delegation *dp = calldata;
+ struct nfsd4_callback *cb = calldata;
+ struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;

dprintk("%s: minorversion=%d\n", __func__,
@@ -661,7 +654,8 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata)

static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
{
- struct nfs4_delegation *dp = calldata;
+ struct nfsd4_callback *cb = calldata;
+ struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;
struct rpc_clnt *current_rpc_client = clp->cl_cb_client;

@@ -706,7 +700,8 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)

static void nfsd4_cb_recall_release(void *calldata)
{
- struct nfs4_delegation *dp = calldata;
+ struct nfsd4_callback *cb = calldata;
+ struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);

nfs4_put_delegation(dp);
}
@@ -748,42 +743,39 @@ void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
rpc_shutdown_client(old);
}

-/*
- * called with dp->dl_count inc'ed.
- */
-static void _nfsd4_cb_recall(struct nfs4_delegation *dp)
+void nfsd4_release_cb(struct nfsd4_callback *cb)
{
- struct nfs4_client *clp = dp->dl_client;
+ if (cb->cb_ops->rpc_release)
+ cb->cb_ops->rpc_release(cb);
+}
+
+void nfsd4_do_callback_rpc(struct work_struct *w)
+{
+ struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
+ struct nfs4_client *clp = cb->cb_args.args_clp;
struct rpc_clnt *clnt = clp->cl_cb_client;
- struct nfsd4_cb_args *args = &dp->dl_recall.cb_args;
- struct rpc_message msg = {
- .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
- .rpc_cred = callback_cred
- };

if (clnt == NULL) {
- nfs4_put_delegation(dp);
+ nfsd4_release_cb(cb);
return; /* Client is shutting down; give up. */
}
-
- args->args_op = dp;
- msg.rpc_argp = args;
- dp->dl_retries = 1;
- rpc_call_async(clnt, &msg, RPC_TASK_SOFT, &nfsd4_cb_recall_ops, dp);
+ rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT, cb->cb_ops, cb);
}

-void nfsd4_do_callback_rpc(struct work_struct *w)
+void nfsd4_cb_recall(struct nfs4_delegation *dp)
{
- /* XXX: for now, just send off delegation recall. */
- /* In future, generalize to handle any sort of callback. */
- struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work);
- struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall);
-
- _nfsd4_cb_recall(dp);
-}
+ struct nfsd4_callback *cb = &dp->dl_recall;

+ dp->dl_retries = 1;
+ cb->cb_args.args_op = dp;
+ cb->cb_args.args_clp = dp->dl_client;
+ cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
+ cb->cb_msg.rpc_argp = &cb->cb_args;
+ cb->cb_msg.rpc_resp = &cb->cb_args;
+ cb->cb_msg.rpc_cred = callback_cred;
+
+ cb->cb_ops = &nfsd4_cb_recall_ops;
+ dp->dl_retries = 1;

-void nfsd4_cb_recall(struct nfs4_delegation *dp)
-{
queue_work(callback_wq, &dp->dl_recall.cb_work);
}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index f988b90..6e59214 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -72,6 +72,8 @@ struct nfsd4_cb_args {

struct nfsd4_callback {
struct nfsd4_cb_args cb_args;
+ struct rpc_message cb_msg;
+ const struct rpc_call_ops *cb_ops;
struct work_struct cb_work;
};

--
1.7.0.4


2010-09-30 16:19:39

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 06/16] nfsd4: remove separate cb_args struct

From: J. Bruce Fields <[email protected]>

I don't see the point of the separate struct. It seems to just be
getting in the way.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 45 ++++++++++++++++++++++-----------------------
fs/nfsd/state.h | 10 +++-------
2 files changed, 25 insertions(+), 30 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 3b06212..384a990 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -247,7 +247,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
}

static void
-encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *args,
+encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
struct nfs4_cb_compound_hdr *hdr)
{
__be32 *p;
@@ -258,8 +258,8 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *args,
RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);

WRITE32(OP_CB_SEQUENCE);
- WRITEMEM(args->args_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
- WRITE32(args->args_clp->cl_cb_seq_nr);
+ WRITEMEM(cb->cb_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
+ WRITE32(cb->cb_clp->cl_cb_seq_nr);
WRITE32(0); /* slotid, always 0 */
WRITE32(0); /* highest slotid always 0 */
WRITE32(0); /* cachethis always 0 */
@@ -279,18 +279,18 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)

static int
nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
- struct nfsd4_cb_args *rpc_args)
+ struct nfsd4_callback *cb)
{
struct xdr_stream xdr;
- struct nfs4_delegation *args = rpc_args->args_op;
+ struct nfs4_delegation *args = cb->cb_op;
struct nfs4_cb_compound_hdr hdr = {
.ident = args->dl_ident,
- .minorversion = rpc_args->args_minorversion,
+ .minorversion = cb->cb_minorversion,
};

xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_cb_compound_hdr(&xdr, &hdr);
- encode_cb_sequence(&xdr, rpc_args, &hdr);
+ encode_cb_sequence(&xdr, cb, &hdr);
encode_cb_recall(&xdr, args, &hdr);
encode_cb_nops(&hdr);
return 0;
@@ -338,7 +338,7 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
* with a single slot.
*/
static int
-decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *res,
+decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_callback *cb,
struct rpc_rqst *rqstp)
{
struct nfs4_sessionid id;
@@ -346,7 +346,7 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *res,
u32 dummy;
__be32 *p;

- if (res->args_minorversion == 0)
+ if (cb->cb_minorversion == 0)
return 0;

status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
@@ -362,13 +362,13 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_args *res,
READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
- if (memcmp(id.data, res->args_clp->cl_sessionid.data,
+ if (memcmp(id.data, cb->cb_clp->cl_sessionid.data,
NFS4_MAX_SESSIONID_LEN)) {
dprintk("%s Invalid session id\n", __func__);
goto out;
}
READ32(dummy);
- if (dummy != res->args_clp->cl_cb_seq_nr) {
+ if (dummy != cb->cb_clp->cl_cb_seq_nr) {
dprintk("%s Invalid sequence number\n", __func__);
goto out;
}
@@ -392,7 +392,7 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)

static int
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
- struct nfsd4_cb_args *args)
+ struct nfsd4_callback *cb)
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr;
@@ -402,8 +402,8 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
status = decode_cb_compound_hdr(&xdr, &hdr);
if (status)
goto out;
- if (args) {
- status = decode_cb_sequence(&xdr, args, rqstp);
+ if (cb) {
+ status = decode_cb_sequence(&xdr, cb, rqstp);
if (status)
goto out;
}
@@ -550,8 +550,8 @@ void do_probe_callback(struct nfs4_client *clp)
{
struct nfsd4_callback *cb = &clp->cl_cb_null;

- cb->cb_args.args_op = NULL;
- cb->cb_args.args_clp = clp;
+ cb->cb_op = NULL;
+ cb->cb_clp = clp;

cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
cb->cb_msg.rpc_argp = NULL;
@@ -614,11 +614,10 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
struct nfsd4_callback *cb = calldata;
struct nfs4_delegation *dp = container_of(cb, struct nfs4_delegation, dl_recall);
struct nfs4_client *clp = dp->dl_client;
- struct nfsd4_cb_args *args = task->tk_msg.rpc_argp;
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
int status = 0;

- args->args_minorversion = minorversion;
+ cb->cb_minorversion = minorversion;
if (minorversion) {
status = nfsd41_cb_setup_sequence(clp, task);
if (status) {
@@ -754,7 +753,7 @@ void nfsd4_release_cb(struct nfsd4_callback *cb)
void nfsd4_do_callback_rpc(struct work_struct *w)
{
struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
- struct nfs4_client *clp = cb->cb_args.args_clp;
+ struct nfs4_client *clp = cb->cb_clp;
struct rpc_clnt *clnt = clp->cl_cb_client;

if (clnt == NULL) {
@@ -770,11 +769,11 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp)
struct nfsd4_callback *cb = &dp->dl_recall;

dp->dl_retries = 1;
- cb->cb_args.args_op = dp;
- cb->cb_args.args_clp = dp->dl_client;
+ cb->cb_op = dp;
+ cb->cb_clp = dp->dl_client;
cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL];
- cb->cb_msg.rpc_argp = &cb->cb_args;
- cb->cb_msg.rpc_resp = &cb->cb_args;
+ cb->cb_msg.rpc_argp = cb;
+ cb->cb_msg.rpc_resp = cb;
cb->cb_msg.rpc_cred = callback_cred;

cb->cb_ops = &nfsd4_cb_recall_ops;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 19732d5..2ece6be 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -64,14 +64,10 @@ typedef struct {
(s)->si_fileid, \
(s)->si_generation

-struct nfsd4_cb_args {
- void *args_op;
- struct nfs4_client *args_clp;
- u32 args_minorversion;
-};
-
struct nfsd4_callback {
- struct nfsd4_cb_args cb_args;
+ void *cb_op;
+ struct nfs4_client *cb_clp;
+ u32 cb_minorversion;
struct rpc_message cb_msg;
const struct rpc_call_ops *cb_ops;
struct work_struct cb_work;
--
1.7.0.4


2010-09-30 21:21:53

by Benny Halevy

[permalink] [raw]
Subject: Re: [PATCH 11/16] nfsd4: keep per-session list of connections

On 2010-09-30 18:19, J. Bruce Fields wrote:
> From: J. Bruce Fields <[email protected]>
>
> The spec requires us in various places to keep track of the connections
> associated with each session.
>
> Signed-off-by: J. Bruce Fields <[email protected]>
> ---
> fs/nfsd/nfs4state.c | 69 +++++++++++++++++++++++++++++++++++++++-----------
> fs/nfsd/state.h | 8 ++++++
> include/linux/nfs4.h | 3 ++
> 3 files changed, 65 insertions(+), 15 deletions(-)
>
> diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
> index f86476c..c7c1a7a 100644
> --- a/fs/nfsd/nfs4state.c
> +++ b/fs/nfsd/nfs4state.c
> @@ -625,11 +625,58 @@ static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4
> new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
> }
>
> +static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
> +{
> + struct nfs4_client *clp = ses->se_client;
> + struct nfsd4_conn *conn;
> +
> + conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
> + if (!conn)
> + return nfserr_jukebox;
> + conn->cn_flags = NFS4_CDFC4_FORE;
> + svc_xprt_get(rqstp->rq_xprt);
> + conn->cn_xprt = rqstp->rq_xprt;
> +
> + spin_lock(&clp->cl_lock);
> + list_add(&conn->cn_persession, &ses->se_conns);
> + spin_unlock(&clp->cl_lock);
> +
> + return nfs_ok;
> +}
> +
> +static void free_conn(struct nfsd4_conn *c)
> +{
> + svc_xprt_put(c->cn_xprt);
> + kfree(c);
> +}
> +
> +void free_session(struct kref *kref)
> +{
> + struct nfsd4_session *ses;
> + int mem;
> +
> + ses = container_of(kref, struct nfsd4_session, se_ref);
> + while (!list_empty(&ses->se_conns)) {
> + struct nfsd4_conn *c;
> + c = list_first_entry(&ses->se_conns, struct nfsd4_conn, cn_persession);
> + list_del(&c->cn_persession);
> + free_conn(c);
> + }
> + spin_lock(&nfsd_drc_lock);
> + mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
> + nfsd_drc_mem_used -= mem;
> + spin_unlock(&nfsd_drc_lock);
> + free_session_slots(ses);
> + kfree(ses);
> +}
> +
> +
> static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
> {
> struct nfsd4_session *new;
> struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
> int numslots, slotsize;
> + int status;
> int idx;
>
> /*
> @@ -654,6 +701,8 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp
> memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
> NFS4_MAX_SESSIONID_LEN);
>
> + INIT_LIST_HEAD(&new->se_conns);
> +
> new->se_flags = cses->flags;
> kref_init(&new->se_ref);
> idx = hash_sessionid(&new->se_sessionid);
> @@ -662,6 +711,11 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp
> list_add(&new->se_perclnt, &clp->cl_sessions);
> spin_unlock(&client_lock);
>
> + status = nfsd4_new_conn(rqstp, new);
> + if (status) {
> + free_session(&new->se_ref);
> + return nfserr_jukebox;

why not return status?

> + }
> return nfs_ok;
> }
>
> @@ -694,21 +748,6 @@ unhash_session(struct nfsd4_session *ses)
> list_del(&ses->se_perclnt);
> }
>
> -void
> -free_session(struct kref *kref)
> -{
> - struct nfsd4_session *ses;
> - int mem;
> -
> - ses = container_of(kref, struct nfsd4_session, se_ref);
> - spin_lock(&nfsd_drc_lock);
> - mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
> - nfsd_drc_mem_used -= mem;
> - spin_unlock(&nfsd_drc_lock);
> - free_session_slots(ses);
> - kfree(ses);
> -}
> -
> /* must be called under the client_lock */
> static inline void
> renew_client_locked(struct nfs4_client *clp)
> diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
> index 58bc2a6..29413c2 100644
> --- a/fs/nfsd/state.h
> +++ b/fs/nfsd/state.h
> @@ -152,6 +152,13 @@ struct nfsd4_clid_slot {
> struct nfsd4_create_session sl_cr_ses;
> };
>
> +struct nfsd4_conn {
> + struct list_head cn_persession;
> + struct svc_xprt *cn_xprt;
> +/* CDFC4_FORE, CDFC4_BACK: */
> + unsigned char cn_flags;
> +};
> +
> struct nfsd4_session {
> struct kref se_ref;
> struct list_head se_hash; /* hash by sessionid */
> @@ -161,6 +168,7 @@ struct nfsd4_session {
> struct nfs4_sessionid se_sessionid;
> struct nfsd4_channel_attrs se_fchannel;
> struct nfsd4_channel_attrs se_bchannel;
> + struct list_head se_conns;
> struct nfsd4_slot *se_slots[]; /* forward channel slots */
> };
>
> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> index 07e40c6..79b15fb 100644
> --- a/include/linux/nfs4.h
> +++ b/include/linux/nfs4.h
> @@ -61,6 +61,9 @@
> #define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000
> #define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED 0x20000
>
> +#define NFS4_CDFC4_FORE 0x1
> +#define NFS4_CDFC4_BACK 0x2
> +

nit: I think that defining flags shifted bits is clearer, e.g.:

enum nfsd4_channel_dir {
NFS4_CDFC4_FORE = 1 << 0,
NFS4_CDFC4_BACK = 1 << 1,
};

Benny

> #define NFS4_SET_TO_SERVER_TIME 0
> #define NFS4_SET_TO_CLIENT_TIME 1
>

2010-09-30 16:19:39

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 07/16] nfsd4: Move callback setup to callback queue

From: J. Bruce Fields <[email protected]>

Instead of creating the new rpc client from a regular server thread,
set a flag, kick off a null call, and allow the null call to do the work
of setting up the client on the callback workqueue.

Use a spinlock to ensure the callback work gets a consistent view of the
callback parameters.

This allows, for example, changing the callback from contexts where
sleeping is not allowed. I hope it will also keep the locking simple as
we add more session and trunking features, by serializing most of the
callback-specific work.

This also closes a small race where the the new cb_ident could be used
with an old connection (or vice-versa).

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 73 ++++++++++++++++++++++++++++++++++--------------
fs/nfsd/nfs4state.c | 7 ++--
fs/nfsd/state.h | 10 +++++-
3 files changed, 63 insertions(+), 27 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 384a990..4019b9a 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -284,7 +284,7 @@ nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
struct xdr_stream xdr;
struct nfs4_delegation *args = cb->cb_op;
struct nfs4_cb_compound_hdr hdr = {
- .ident = args->dl_ident,
+ .ident = cb->cb_clp->cl_cb_ident,
.minorversion = cb->cb_minorversion,
};

@@ -505,7 +505,8 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
PTR_ERR(client));
return PTR_ERR(client);
}
- nfsd4_set_callback_client(clp, client);
+ clp->cl_cb_ident = conn->cb_ident;
+ clp->cl_cb_client = client;
return 0;

}
@@ -568,15 +569,12 @@ void do_probe_callback(struct nfs4_client *clp)
*/
void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{
- int status;
-
BUG_ON(atomic_read(&clp->cl_cb_set));

- status = setup_callback_client(clp, conn);
- if (status) {
- warn_no_callback_path(clp, status);
- return;
- }
+ spin_lock(&clp->cl_lock);
+ memcpy(&clp->cl_cb_conn, conn, sizeof(struct nfs4_cb_conn));
+ set_bit(NFSD4_CLIENT_CB_UPDATE, &clp->cl_cb_flags);
+ spin_unlock(&clp->cl_lock);
do_probe_callback(clp);
}

@@ -729,19 +727,16 @@ void nfsd4_destroy_callback_queue(void)
}

/* must be called under the state lock */
-void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new)
+void nfsd4_shutdown_callback(struct nfs4_client *clp)
{
- struct rpc_clnt *old = clp->cl_cb_client;
-
- clp->cl_cb_client = new;
+ set_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags);
/*
- * After this, any work that saw the old value of cl_cb_client will
- * be gone:
+ * Note this won't actually result in a null callback;
+ * instead, nfsd4_do_callback_rpc() will detect the killed
+ * client, destroy the rpc client, and stop:
*/
+ do_probe_callback(clp);
flush_workqueue(callback_wq);
- /* So we can safely shut it down: */
- if (old)
- rpc_shutdown_client(old);
}

void nfsd4_release_cb(struct nfsd4_callback *cb)
@@ -750,15 +745,51 @@ void nfsd4_release_cb(struct nfsd4_callback *cb)
cb->cb_ops->rpc_release(cb);
}

+void nfsd4_process_cb_update(struct nfsd4_callback *cb)
+{
+ struct nfs4_cb_conn conn;
+ struct nfs4_client *clp = cb->cb_clp;
+ int err;
+
+ /*
+ * This is either an update, or the client dying; in either case,
+ * kill the old client:
+ */
+ if (clp->cl_cb_client) {
+ rpc_shutdown_client(clp->cl_cb_client);
+ clp->cl_cb_client = NULL;
+ }
+ if (test_bit(NFSD4_CLIENT_KILL, &clp->cl_cb_flags))
+ return;
+ spin_lock(&clp->cl_lock);
+ /*
+ * Only serialized callback code is allowed to clear these
+ * flags; main nfsd code can only set them:
+ */
+ 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));
+ spin_unlock(&clp->cl_lock);
+
+ err = setup_callback_client(clp, &conn);
+ if (err)
+ warn_no_callback_path(clp, err);
+}
+
void nfsd4_do_callback_rpc(struct work_struct *w)
{
struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work);
struct nfs4_client *clp = cb->cb_clp;
- struct rpc_clnt *clnt = clp->cl_cb_client;
+ struct rpc_clnt *clnt;

- if (clnt == NULL) {
+ if (clp->cl_cb_flags)
+ nfsd4_process_cb_update(cb);
+
+ clnt = clp->cl_cb_client;
+ if (!clnt) {
+ /* Callback channel broken, or client killed; give up: */
nfsd4_release_cb(cb);
- return; /* Client is shutting down; give up. */
+ return;
}
rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
cb->cb_ops, cb);
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 2f464fb..d3f12dc 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -207,7 +207,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
{
struct nfs4_delegation *dp;
struct nfs4_file *fp = stp->st_file;
- struct nfs4_cb_conn *conn = &stp->st_stateowner->so_client->cl_cb_conn;

dprintk("NFSD alloc_init_deleg\n");
/*
@@ -234,7 +233,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
nfs4_file_get_access(fp, O_RDONLY);
dp->dl_flock = NULL;
dp->dl_type = type;
- dp->dl_ident = conn->cb_ident;
dp->dl_stateid.si_boot = boot_time;
dp->dl_stateid.si_stateownerid = current_delegid++;
dp->dl_stateid.si_fileid = 0;
@@ -875,7 +873,7 @@ expire_client(struct nfs4_client *clp)
sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient);
release_openowner(sop);
}
- nfsd4_set_callback_client(clp, NULL);
+ nfsd4_shutdown_callback(clp);
if (clp->cl_cb_conn.cb_xprt)
svc_xprt_put(clp->cl_cb_conn.cb_xprt);
list_del(&clp->cl_idhash);
@@ -978,6 +976,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_sessions);
INIT_LIST_HEAD(&clp->cl_lru);
+ spin_lock_init(&clp->cl_lock);
INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
clp->cl_time = get_seconds();
clear_bit(0, &clp->cl_cb_slot_busy);
@@ -1547,7 +1546,7 @@ nfsd4_destroy_session(struct svc_rqst *r,

nfs4_lock_state();
/* wait for callbacks */
- nfsd4_set_callback_client(ses->se_client, NULL);
+ nfsd4_shutdown_callback(ses->se_client);
nfs4_unlock_state();
nfsd4_put_session(ses);
status = nfs_ok;
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 2ece6be..58bc2a6 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -84,7 +84,6 @@ struct nfs4_delegation {
u32 dl_type;
time_t dl_time;
/* For recall: */
- u32 dl_ident;
stateid_t dl_stateid;
struct knfsd_fh dl_fh;
int dl_retries;
@@ -217,10 +216,17 @@ struct nfs4_client {

/* for v4.0 and v4.1 callbacks: */
struct nfs4_cb_conn cl_cb_conn;
+#define NFSD4_CLIENT_CB_UPDATE 1
+#define NFSD4_CLIENT_KILL 2
+ unsigned long cl_cb_flags;
struct rpc_clnt *cl_cb_client;
+ u32 cl_cb_ident;
atomic_t cl_cb_set;
struct nfsd4_callback cl_cb_null;

+ /* for all client information that callback code might need: */
+ spinlock_t cl_lock;
+
/* for nfs41 */
struct list_head cl_sessions;
struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */
@@ -439,7 +445,7 @@ extern void nfsd4_do_callback_rpc(struct work_struct *);
extern void nfsd4_cb_recall(struct nfs4_delegation *dp);
extern int nfsd4_create_callback_queue(void);
extern void nfsd4_destroy_callback_queue(void);
-extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *);
+extern void nfsd4_shutdown_callback(struct nfs4_client *);
extern void nfs4_put_delegation(struct nfs4_delegation *dp);
extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname);
extern void nfsd4_init_recdir(char *recdir_name);
--
1.7.0.4


2010-09-30 16:19:37

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 02/16] nfsd4: combine nfs4_rpc_args and nfsd4_cb_sequence

From: J. Bruce Fields <[email protected]>

These two structs don't really need to be distinct as far as I can tell.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 30 +++++++++++++++---------------
fs/nfsd/state.h | 11 +++--------
2 files changed, 18 insertions(+), 23 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 30adfad..02afb71 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -247,7 +247,7 @@ encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp,
}

static void
-encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
+encode_cb_sequence(struct xdr_stream *xdr, struct nfs4_rpc_args *args,
struct nfs4_cb_compound_hdr *hdr)
{
__be32 *p;
@@ -258,8 +258,8 @@ encode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *args,
RESERVE_SPACE(1 + NFS4_MAX_SESSIONID_LEN + 20);

WRITE32(OP_CB_SEQUENCE);
- WRITEMEM(args->cbs_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
- WRITE32(args->cbs_clp->cl_cb_seq_nr);
+ WRITEMEM(args->args_clp->cl_sessionid.data, NFS4_MAX_SESSIONID_LEN);
+ WRITE32(args->args_clp->cl_cb_seq_nr);
WRITE32(0); /* slotid, always 0 */
WRITE32(0); /* highest slotid always 0 */
WRITE32(0); /* cachethis always 0 */
@@ -285,12 +285,12 @@ nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p,
struct nfs4_delegation *args = rpc_args->args_op;
struct nfs4_cb_compound_hdr hdr = {
.ident = args->dl_ident,
- .minorversion = rpc_args->args_seq.cbs_minorversion,
+ .minorversion = rpc_args->args_minorversion,
};

xdr_init_encode(&xdr, &req->rq_snd_buf, p);
encode_cb_compound_hdr(&xdr, &hdr);
- encode_cb_sequence(&xdr, &rpc_args->args_seq, &hdr);
+ encode_cb_sequence(&xdr, rpc_args, &hdr);
encode_cb_recall(&xdr, args, &hdr);
encode_cb_nops(&hdr);
return 0;
@@ -338,7 +338,7 @@ decode_cb_op_hdr(struct xdr_stream *xdr, enum nfs_opnum4 expected)
* with a single slot.
*/
static int
-decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
+decode_cb_sequence(struct xdr_stream *xdr, struct nfs4_rpc_args *res,
struct rpc_rqst *rqstp)
{
struct nfs4_sessionid id;
@@ -346,7 +346,7 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
u32 dummy;
__be32 *p;

- if (res->cbs_minorversion == 0)
+ if (res->args_minorversion == 0)
return 0;

status = decode_cb_op_hdr(xdr, OP_CB_SEQUENCE);
@@ -362,13 +362,13 @@ decode_cb_sequence(struct xdr_stream *xdr, struct nfsd4_cb_sequence *res,
READ_BUF(NFS4_MAX_SESSIONID_LEN + 16);
memcpy(id.data, p, NFS4_MAX_SESSIONID_LEN);
p += XDR_QUADLEN(NFS4_MAX_SESSIONID_LEN);
- if (memcmp(id.data, res->cbs_clp->cl_sessionid.data,
+ if (memcmp(id.data, res->args_clp->cl_sessionid.data,
NFS4_MAX_SESSIONID_LEN)) {
dprintk("%s Invalid session id\n", __func__);
goto out;
}
READ32(dummy);
- if (dummy != res->cbs_clp->cl_cb_seq_nr) {
+ if (dummy != res->args_clp->cl_cb_seq_nr) {
dprintk("%s Invalid sequence number\n", __func__);
goto out;
}
@@ -392,7 +392,7 @@ nfs4_xdr_dec_cb_null(struct rpc_rqst *req, __be32 *p)

static int
nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
- struct nfsd4_cb_sequence *seq)
+ struct nfs4_rpc_args *args)
{
struct xdr_stream xdr;
struct nfs4_cb_compound_hdr hdr;
@@ -402,8 +402,8 @@ nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp, __be32 *p,
status = decode_cb_compound_hdr(&xdr, &hdr);
if (status)
goto out;
- if (seq) {
- status = decode_cb_sequence(&xdr, seq, rqstp);
+ if (args) {
+ status = decode_cb_sequence(&xdr, args, rqstp);
if (status)
goto out;
}
@@ -602,8 +602,8 @@ static int nfsd41_cb_setup_sequence(struct nfs4_client *clp,
* We'll need the clp during XDR encoding and decoding,
* and the sequence during decoding to verify the reply
*/
- args->args_seq.cbs_clp = clp;
- task->tk_msg.rpc_resp = &args->args_seq;
+ args->args_clp = clp;
+ task->tk_msg.rpc_resp = args;

out:
dprintk("%s status=%d\n", __func__, status);
@@ -622,7 +622,7 @@ static void nfsd4_cb_prepare(struct rpc_task *task, void *calldata)
u32 minorversion = clp->cl_cb_conn.cb_minorversion;
int status = 0;

- args->args_seq.cbs_minorversion = minorversion;
+ args->args_minorversion = minorversion;
if (minorversion) {
status = nfsd41_cb_setup_sequence(clp, task);
if (status) {
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 322518c..59313f1 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -64,15 +64,10 @@ typedef struct {
(s)->si_fileid, \
(s)->si_generation

-struct nfsd4_cb_sequence {
- /* args/res */
- u32 cbs_minorversion;
- struct nfs4_client *cbs_clp;
-};
-
struct nfs4_rpc_args {
- void *args_op;
- struct nfsd4_cb_sequence args_seq;
+ void *args_op;
+ struct nfs4_client *args_clp;
+ u32 args_minorversion;
};

struct nfsd4_callback {
--
1.7.0.4


2010-09-30 16:19:44

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 12/16] nfsd: provide callbacks on svc_xprt deletion

From: J. Bruce Fields <[email protected]>

NFSv4.1 needs warning when a client tcp connection goes down, if that
connection is being used as a backchannel, so that it can warn the
client that it has lost the backchannel connection.

Signed-off-by: J. Bruce Fields <[email protected]>
---
include/linux/sunrpc/svc_xprt.h | 25 +++++++++++++++++++++++++
net/sunrpc/svc_xprt.c | 15 +++++++++++++++
2 files changed, 40 insertions(+), 0 deletions(-)

diff --git a/include/linux/sunrpc/svc_xprt.h b/include/linux/sunrpc/svc_xprt.h
index e50e3ec..cc90cce 100644
--- a/include/linux/sunrpc/svc_xprt.h
+++ b/include/linux/sunrpc/svc_xprt.h
@@ -32,6 +32,16 @@ struct svc_xprt_class {
u32 xcl_max_payload;
};

+/*
+ * This is embedded in an object that wants a callback before deleting
+ * an xprt; intended for use by NFSv4.1, which needs to know when a
+ * client's tcp connection (and hence possibly a backchannel) goes away.
+ */
+struct svc_xpt_user {
+ struct list_head list;
+ void (*callback)(struct svc_xpt_user *);
+};
+
struct svc_xprt {
struct svc_xprt_class *xpt_class;
struct svc_xprt_ops *xpt_ops;
@@ -66,10 +76,25 @@ struct svc_xprt {
struct sockaddr_storage xpt_remote; /* remote peer's address */
size_t xpt_remotelen; /* length of address */
struct rpc_wait_queue xpt_bc_pending; /* backchannel wait queue */
+ struct list_head xpt_users; /* callbacks on free */

struct net *xpt_net;
};

+static inline void register_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+{
+ spin_lock(&xpt->xpt_lock);
+ list_add(&u->list, &xpt->xpt_users);
+ spin_unlock(&xpt->xpt_lock);
+}
+
+static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
+{
+ spin_lock(&xpt->xpt_lock);
+ list_del_init(&u->list);
+ spin_unlock(&xpt->xpt_lock);
+}
+
int svc_reg_xprt_class(struct svc_xprt_class *);
void svc_unreg_xprt_class(struct svc_xprt_class *);
void svc_xprt_init(struct svc_xprt_class *, struct svc_xprt *,
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c
index f7e8915..b37fd3a 100644
--- a/net/sunrpc/svc_xprt.c
+++ b/net/sunrpc/svc_xprt.c
@@ -156,6 +156,7 @@ void svc_xprt_init(struct svc_xprt_class *xcl, struct svc_xprt *xprt,
INIT_LIST_HEAD(&xprt->xpt_list);
INIT_LIST_HEAD(&xprt->xpt_ready);
INIT_LIST_HEAD(&xprt->xpt_deferred);
+ INIT_LIST_HEAD(&xprt->xpt_users);
mutex_init(&xprt->xpt_mutex);
spin_lock_init(&xprt->xpt_lock);
set_bit(XPT_BUSY, &xprt->xpt_flags);
@@ -880,6 +881,19 @@ static void svc_age_temp_xprts(unsigned long closure)
mod_timer(&serv->sv_temptimer, jiffies + svc_conn_age_period * HZ);
}

+static void call_xpt_users(struct svc_xprt *xprt)
+{
+ struct svc_xpt_user *u;
+
+ spin_lock(&xprt->xpt_lock);
+ while (!list_empty(&xprt->xpt_users)) {
+ u = list_first_entry(&xprt->xpt_users, struct svc_xpt_user, list);
+ list_del(&u->list);
+ u->callback(u);
+ }
+ spin_unlock(&xprt->xpt_lock);
+}
+
/*
* Remove a dead transport
*/
@@ -912,6 +926,7 @@ void svc_delete_xprt(struct svc_xprt *xprt)
while ((dr = svc_deferred_dequeue(xprt)) != NULL)
kfree(dr);

+ call_xpt_users(xprt);
svc_xprt_put(xprt);
}

--
1.7.0.4


2010-09-30 16:19:38

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 05/16] nfsd4: use generic callback code in null case

From: J. Bruce Fields <[email protected]>

This will eventually allow us, for example, to kick off null callback
from contexts where we can't sleep.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 33 ++++++++++++++++++---------------
fs/nfsd/nfs4state.c | 1 +
fs/nfsd/state.h | 1 +
3 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 2289ad5..3b06212 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -518,7 +518,7 @@ static void warn_no_callback_path(struct nfs4_client *clp, int reason)

static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
{
- struct nfs4_client *clp = 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);
@@ -527,6 +527,8 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
}

static const struct rpc_call_ops nfsd4_cb_probe_ops = {
+ /* XXX: release method to ensure we set the cb channel down if
+ * necessary on early failure? */
.rpc_call_done = nfsd4_cb_probe_done,
};

@@ -542,21 +544,23 @@ int set_callback_cred(void)
return 0;
}

+static struct workqueue_struct *callback_wq;

void do_probe_callback(struct nfs4_client *clp)
{
- struct rpc_message msg = {
- .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
- .rpc_argp = clp,
- .rpc_cred = callback_cred
- };
- int status;
+ struct nfsd4_callback *cb = &clp->cl_cb_null;

- status = rpc_call_async(clp->cl_cb_client, &msg,
- RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
- &nfsd4_cb_probe_ops, (void *)clp);
- if (status)
- warn_no_callback_path(clp, status);
+ cb->cb_args.args_op = NULL;
+ cb->cb_args.args_clp = clp;
+
+ cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL];
+ cb->cb_msg.rpc_argp = NULL;
+ cb->cb_msg.rpc_resp = NULL;
+ cb->cb_msg.rpc_cred = callback_cred;
+
+ cb->cb_ops = &nfsd4_cb_probe_ops;
+
+ queue_work(callback_wq, &cb->cb_work);
}

/*
@@ -712,8 +716,6 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = {
.rpc_release = nfsd4_cb_recall_release,
};

-static struct workqueue_struct *callback_wq;
-
int nfsd4_create_callback_queue(void)
{
callback_wq = create_singlethread_workqueue("nfsd4_callbacks");
@@ -759,7 +761,8 @@ void nfsd4_do_callback_rpc(struct work_struct *w)
nfsd4_release_cb(cb);
return; /* Client is shutting down; give up. */
}
- rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT, cb->cb_ops, cb);
+ rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN,
+ cb->cb_ops, cb);
}

void nfsd4_cb_recall(struct nfs4_delegation *dp)
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d347180..2f464fb 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -978,6 +978,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir,
INIT_LIST_HEAD(&clp->cl_delegations);
INIT_LIST_HEAD(&clp->cl_sessions);
INIT_LIST_HEAD(&clp->cl_lru);
+ INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc);
clp->cl_time = get_seconds();
clear_bit(0, &clp->cl_cb_slot_busy);
rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table");
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 6e59214..19732d5 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -223,6 +223,7 @@ struct nfs4_client {
struct nfs4_cb_conn cl_cb_conn;
struct rpc_clnt *cl_cb_client;
atomic_t cl_cb_set;
+ struct nfsd4_callback cl_cb_null;

/* for nfs41 */
struct list_head cl_sessions;
--
1.7.0.4


2010-09-30 16:19:35

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 01/16] nfsd4: minor variable renaming (cb -> conn)

From: J. Bruce Fields <[email protected]>

Now that we have both nfsd4_callback and nfsd4_cb_conn structures, I get
confused if variables of both types are always named cb....

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4callback.c | 16 ++++++++--------
fs/nfsd/nfs4state.c | 28 ++++++++++++++--------------
2 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 014482c..30adfad 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -472,7 +472,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? */

-int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
+int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{
struct rpc_timeout timeparms = {
.to_initval = max_cb_time(),
@@ -480,11 +480,11 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
};
struct rpc_create_args args = {
.protocol = XPRT_TRANSPORT_TCP,
- .address = (struct sockaddr *) &cb->cb_addr,
- .addrsize = cb->cb_addrlen,
+ .address = (struct sockaddr *) &conn->cb_addr,
+ .addrsize = conn->cb_addrlen,
.timeout = &timeparms,
.program = &cb_program,
- .prognumber = cb->cb_prog,
+ .prognumber = conn->cb_prog,
.version = 0,
.authflavor = clp->cl_flavor,
.flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
@@ -494,8 +494,8 @@ int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb)

if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
return -EINVAL;
- if (cb->cb_minorversion) {
- args.bc_xprt = cb->cb_xprt;
+ if (conn->cb_minorversion) {
+ args.bc_xprt = conn->cb_xprt;
args.protocol = XPRT_TRANSPORT_BC_TCP;
}
/* Create RPC client */
@@ -562,13 +562,13 @@ void do_probe_callback(struct nfs4_client *clp)
/*
* Set up the callback client and put a NFSPROC4_CB_NULL on the wire...
*/
-void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb)
+void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *conn)
{
int status;

BUG_ON(atomic_read(&clp->cl_cb_set));

- status = setup_callback_client(clp, cb);
+ status = setup_callback_client(clp, conn);
if (status) {
warn_no_callback_path(clp, status);
return;
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index cf0d2ff..d347180 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -207,7 +207,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
{
struct nfs4_delegation *dp;
struct nfs4_file *fp = stp->st_file;
- struct nfs4_cb_conn *cb = &stp->st_stateowner->so_client->cl_cb_conn;
+ struct nfs4_cb_conn *conn = &stp->st_stateowner->so_client->cl_cb_conn;

dprintk("NFSD alloc_init_deleg\n");
/*
@@ -234,7 +234,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
nfs4_file_get_access(fp, O_RDONLY);
dp->dl_flock = NULL;
dp->dl_type = type;
- dp->dl_ident = cb->cb_ident;
+ dp->dl_ident = conn->cb_ident;
dp->dl_stateid.si_boot = boot_time;
dp->dl_stateid.si_stateownerid = current_delegid++;
dp->dl_stateid.si_fileid = 0;
@@ -1098,7 +1098,7 @@ find_unconfirmed_client_by_str(const char *dname, unsigned int hashval,
static void
gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
{
- struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
+ struct nfs4_cb_conn *conn = &clp->cl_cb_conn;
unsigned short expected_family;

/* Currently, we only support tcp and tcp6 for the callback channel */
@@ -1111,24 +1111,24 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, u32 scopeid)
else
goto out_err;

- cb->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
+ conn->cb_addrlen = rpc_uaddr2sockaddr(se->se_callback_addr_val,
se->se_callback_addr_len,
- (struct sockaddr *) &cb->cb_addr,
- sizeof(cb->cb_addr));
+ (struct sockaddr *)&conn->cb_addr,
+ sizeof(conn->cb_addr));

- if (!cb->cb_addrlen || cb->cb_addr.ss_family != expected_family)
+ if (!conn->cb_addrlen || conn->cb_addr.ss_family != expected_family)
goto out_err;

- if (cb->cb_addr.ss_family == AF_INET6)
- ((struct sockaddr_in6 *) &cb->cb_addr)->sin6_scope_id = scopeid;
+ if (conn->cb_addr.ss_family == AF_INET6)
+ ((struct sockaddr_in6 *)&conn->cb_addr)->sin6_scope_id = scopeid;

- cb->cb_minorversion = 0;
- cb->cb_prog = se->se_callback_prog;
- cb->cb_ident = se->se_callback_ident;
+ conn->cb_minorversion = 0;
+ conn->cb_prog = se->se_callback_prog;
+ conn->cb_ident = se->se_callback_ident;
return;
out_err:
- cb->cb_addr.ss_family = AF_UNSPEC;
- cb->cb_addrlen = 0;
+ conn->cb_addr.ss_family = AF_UNSPEC;
+ conn->cb_addrlen = 0;
dprintk(KERN_INFO "NFSD: this client (clientid %08x/%08x) "
"will not receive delegations\n",
clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
--
1.7.0.4


2010-09-30 16:19:40

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 08/16] nfsd4: fix alloc_init_session BUILD_BUG_ON()

Note we're allocating an array of nfsd4_slot *'s, not nfsd4_slot's.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index d3f12dc..e305794 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -657,7 +657,7 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp,
if (status)
goto out;

- BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot)
+ BUILD_BUG_ON(NFSD_MAX_SLOTS_PER_SESSION * sizeof(struct nfsd4_slot *)
+ sizeof(struct nfsd4_session) > PAGE_SIZE);

status = nfserr_jukebox;
--
1.7.0.4


2010-09-30 16:19:43

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 11/16] nfsd4: keep per-session list of connections

From: J. Bruce Fields <[email protected]>

The spec requires us in various places to keep track of the connections
associated with each session.

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 69 +++++++++++++++++++++++++++++++++++++++-----------
fs/nfsd/state.h | 8 ++++++
include/linux/nfs4.h | 3 ++
3 files changed, 65 insertions(+), 15 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index f86476c..c7c1a7a 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -625,11 +625,58 @@ static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4
new->maxops = min_t(u32, req->maxops, NFSD_MAX_OPS_PER_COMPOUND);
}

+static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+{
+ struct nfs4_client *clp = ses->se_client;
+ struct nfsd4_conn *conn;
+
+ conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
+ if (!conn)
+ return nfserr_jukebox;
+ conn->cn_flags = NFS4_CDFC4_FORE;
+ svc_xprt_get(rqstp->rq_xprt);
+ conn->cn_xprt = rqstp->rq_xprt;
+
+ spin_lock(&clp->cl_lock);
+ list_add(&conn->cn_persession, &ses->se_conns);
+ spin_unlock(&clp->cl_lock);
+
+ return nfs_ok;
+}
+
+static void free_conn(struct nfsd4_conn *c)
+{
+ svc_xprt_put(c->cn_xprt);
+ kfree(c);
+}
+
+void free_session(struct kref *kref)
+{
+ struct nfsd4_session *ses;
+ int mem;
+
+ ses = container_of(kref, struct nfsd4_session, se_ref);
+ while (!list_empty(&ses->se_conns)) {
+ struct nfsd4_conn *c;
+ c = list_first_entry(&ses->se_conns, struct nfsd4_conn, cn_persession);
+ list_del(&c->cn_persession);
+ free_conn(c);
+ }
+ spin_lock(&nfsd_drc_lock);
+ mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
+ nfsd_drc_mem_used -= mem;
+ spin_unlock(&nfsd_drc_lock);
+ free_session_slots(ses);
+ kfree(ses);
+}
+
+
static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, struct nfsd4_create_session *cses)
{
struct nfsd4_session *new;
struct nfsd4_channel_attrs *fchan = &cses->fore_channel;
int numslots, slotsize;
+ int status;
int idx;

/*
@@ -654,6 +701,8 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp
memcpy(clp->cl_sessionid.data, new->se_sessionid.data,
NFS4_MAX_SESSIONID_LEN);

+ INIT_LIST_HEAD(&new->se_conns);
+
new->se_flags = cses->flags;
kref_init(&new->se_ref);
idx = hash_sessionid(&new->se_sessionid);
@@ -662,6 +711,11 @@ static __be32 alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp
list_add(&new->se_perclnt, &clp->cl_sessions);
spin_unlock(&client_lock);

+ status = nfsd4_new_conn(rqstp, new);
+ if (status) {
+ free_session(&new->se_ref);
+ return nfserr_jukebox;
+ }
return nfs_ok;
}

@@ -694,21 +748,6 @@ unhash_session(struct nfsd4_session *ses)
list_del(&ses->se_perclnt);
}

-void
-free_session(struct kref *kref)
-{
- struct nfsd4_session *ses;
- int mem;
-
- ses = container_of(kref, struct nfsd4_session, se_ref);
- spin_lock(&nfsd_drc_lock);
- mem = ses->se_fchannel.maxreqs * slot_bytes(&ses->se_fchannel);
- nfsd_drc_mem_used -= mem;
- spin_unlock(&nfsd_drc_lock);
- free_session_slots(ses);
- kfree(ses);
-}
-
/* must be called under the client_lock */
static inline void
renew_client_locked(struct nfs4_client *clp)
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 58bc2a6..29413c2 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -152,6 +152,13 @@ struct nfsd4_clid_slot {
struct nfsd4_create_session sl_cr_ses;
};

+struct nfsd4_conn {
+ struct list_head cn_persession;
+ struct svc_xprt *cn_xprt;
+/* CDFC4_FORE, CDFC4_BACK: */
+ unsigned char cn_flags;
+};
+
struct nfsd4_session {
struct kref se_ref;
struct list_head se_hash; /* hash by sessionid */
@@ -161,6 +168,7 @@ struct nfsd4_session {
struct nfs4_sessionid se_sessionid;
struct nfsd4_channel_attrs se_fchannel;
struct nfsd4_channel_attrs se_bchannel;
+ struct list_head se_conns;
struct nfsd4_slot *se_slots[]; /* forward channel slots */
};

diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
index 07e40c6..79b15fb 100644
--- a/include/linux/nfs4.h
+++ b/include/linux/nfs4.h
@@ -61,6 +61,9 @@
#define NFS4_SHARE_SIGNAL_DELEG_WHEN_RESRC_AVAIL 0x10000
#define NFS4_SHARE_PUSH_DELEG_WHEN_UNCONTENDED 0x20000

+#define NFS4_CDFC4_FORE 0x1
+#define NFS4_CDFC4_BACK 0x2
+
#define NFS4_SET_TO_SERVER_TIME 0
#define NFS4_SET_TO_CLIENT_TIME 1

--
1.7.0.4


2010-09-30 16:19:45

by J. Bruce Fields

[permalink] [raw]
Subject: [PATCH 14/16] nfsd4: refactor connection allocation

Signed-off-by: J. Bruce Fields <[email protected]>
---
fs/nfsd/nfs4state.c | 32 ++++++++++++++++++++++++++------
1 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index b7e9793..3b4d74c 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -644,25 +644,45 @@ static void nfsd4_conn_lost(struct svc_xpt_user *u)
spin_unlock(&clp->cl_lock);
}

-static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+static struct nfsd4_conn *alloc_conn(struct svc_rqst *rqstp)
{
- struct nfs4_client *clp = ses->se_client;
struct nfsd4_conn *conn;

conn = kmalloc(sizeof(struct nfsd4_conn), GFP_KERNEL);
if (!conn)
- return nfserr_jukebox;
- conn->cn_flags = NFS4_CDFC4_FORE;
+ return NULL;
svc_xprt_get(rqstp->rq_xprt);
conn->cn_xprt = rqstp->rq_xprt;
- conn->cn_session = ses;
+ conn->cn_flags = NFS4_CDFC4_FORE;
+ INIT_LIST_HEAD(&conn->cn_xpt_user.list);
+ return conn;
+}
+
+static void nfsd4_hash_conn(struct nfsd4_conn *conn, struct nfsd4_session *ses)
+{
+ struct nfs4_client *clp = ses->se_client;

spin_lock(&clp->cl_lock);
+ conn->cn_session = ses;
list_add(&conn->cn_persession, &ses->se_conns);
spin_unlock(&clp->cl_lock);
+}

+static void nfsd4_register_conn(struct nfsd4_conn *conn)
+{
conn->cn_xpt_user.callback = nfsd4_conn_lost;
- register_xpt_user(rqstp->rq_xprt, &conn->cn_xpt_user);
+ register_xpt_user(conn->cn_xprt, &conn->cn_xpt_user);
+}
+
+static __be32 nfsd4_new_conn(struct svc_rqst *rqstp, struct nfsd4_session *ses)
+{
+ struct nfsd4_conn *conn;
+
+ conn = alloc_conn(rqstp);
+ if (!conn)
+ return nfserr_jukebox;
+ nfsd4_hash_conn(conn, ses);
+ nfsd4_register_conn(conn);
return nfs_ok;
}

--
1.7.0.4