From: Ricardo Labiaga <ricardo.labiaga@xxxxxxxxxx> Implement the create_session operation confoming to http://tools.ietf.org/html/draft-ietf-nfsv4-minorversion1-26 Look up the client id (generated by the server on exchange_id, given by the client on create_session). If neither a confirmed or unconfirmed client is found then the client id is stale If a confirmed cilent is found (i.e. we already received create_session for it) then compare the sequence id to determine if it's a replay or possibly a mis-ordered rpc. If the seqid is in order, update the confirmed client seqid and procedd with updating the session parameters. If an unconfirmed client_id is found then verify the creds and seqid. If both match move the client id to confirmed state and proceed with processing the create_session. Currently, we do not support persistent sessions, and RDMA. alloc_init_session generates a new sessionid and creates a session structure. TODO: improve resource pre-allocation and negotiate session parameters accordingly. Signed-off-by: Marc Eshel <eshel@xxxxxxxxxxxxxxx> Signed-off-by: Dean Hildebrand <dhildeb@xxxxxxxxxx> Signed-off-by: Andy Adamson<andros@xxxxxxxxxx> Signed-off-by: Benny Halevy <bhalevy@xxxxxxxxxxx> --- fs/nfsd/nfs4state.c | 139 +++++++++++++++++++++++++++++++++++++++++- fs/nfsd/nfs4xdr.c | 147 +++++++++++++++++++++++++++++++++++++++++++- include/linux/nfsd/state.h | 3 + include/linux/nfsd/xdr4.h | 21 ++++++- 4 files changed, 306 insertions(+), 4 deletions(-) diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index cbbb733..73bc8de 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -73,6 +73,9 @@ static u32 current_delegid = 1; static u32 nfs4_init; static stateid_t zerostateid; /* bits all 0 */ static stateid_t onestateid; /* bits all 1 */ +#if defined(CONFIG_NFSD_V4_1) +static u64 current_sessionid = 1; +#endif #define ZERO_STATEID(stateid) (!memcmp((stateid), &zerostateid, sizeof(stateid_t))) #define ONE_STATEID(stateid) (!memcmp((stateid), &onestateid, sizeof(stateid_t))) @@ -352,6 +355,72 @@ dump_sessionid(const char *fn, nfs41_sessionid *sessionid) dprintk("%s: %u:%u:%u:%u\n", fn, ptr[0], ptr[1], ptr[2], ptr[3]); } +static void +gen_sessionid(struct nfs41_session *ses) +{ + struct nfs4_client *clp = ses->se_client; + u32 *p = (u32 *)ses->se_sessionid; + + *p++ = clp->cl_clientid.cl_boot; + *p++ = clp->cl_clientid.cl_id; + *p++ = (u32)boot_time; + *p++ = current_sessionid++; + BUG_ON((char *)p - (char *)ses->se_sessionid != + sizeof(ses->se_sessionid)); +} + +static int +alloc_init_session(struct nfs4_client *clp, struct nfsd4_create_session *cses) +{ + struct nfs41_session *new; + int idx, status = nfserr_resource, slotsize, i; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + goto out; + + if (cses->fore_channel.maxreqs >= NFS41_MAX_SLOTS) + cses->fore_channel.maxreqs = NFS41_MAX_SLOTS; + new->se_fnumslots = cses->fore_channel.maxreqs; + slotsize = new->se_fnumslots * sizeof(struct nfs41_slot); + + new->se_slots = kzalloc(slotsize, GFP_KERNEL); + if (!new->se_slots) + goto out_free; + + for (i = 0; i < new->se_fnumslots; i++) { + new->se_slots[i].sl_session = new; + nfs41_set_slot_state(&new->se_slots[i], NFS4_SLOT_AVAILABLE); + } + + new->se_client = clp; + gen_sessionid(new); + idx = hash_sessionid(&new->se_sessionid); + memcpy(&clp->cl_sessionid, &new->se_sessionid, sizeof(nfs41_sessionid)); + + new->se_flags = cses->flags; + + /* for now, accept the client values */ + new->se_fheaderpad_sz = cses->fore_channel.headerpadsz; + new->se_fmaxreq_sz = cses->fore_channel.maxreq_sz; + new->se_fmaxresp_sz = cses->fore_channel.maxresp_sz; + new->se_fmaxresp_cached = cses->fore_channel.maxresp_cached; + new->se_fmaxops = cses->fore_channel.maxops; + + kref_init(&new->se_ref); + INIT_LIST_HEAD(&new->se_hash); + INIT_LIST_HEAD(&new->se_perclnt); + list_add(&new->se_hash, &sessionid_hashtbl[idx]); + list_add(&new->se_perclnt, &clp->cl_sessions); + + status = nfs_ok; +out: + return status; +out_free: + kfree(new); + goto out; +} + struct nfs41_session * find_in_sessionid_hashtbl(nfs41_sessionid *sessionid) { @@ -905,7 +974,75 @@ nfsd4_create_session(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_create_session *cr_ses) { - return -1; /* stub */ + u32 ip_addr = svc_addr_in(rqstp)->sin_addr.s_addr; + struct nfs4_client *conf, *unconf; + __u32 max_blocksize = svc_max_payload(rqstp); + int status = 0; + + if (STALE_CLIENTID(&cr_ses->clientid)) + return nfserr_stale_clientid; + + nfs4_lock_state(); + unconf = find_unconfirmed_client(&cr_ses->clientid); + conf = find_confirmed_client(&cr_ses->clientid); + + if (!conf && !unconf) { + status = nfserr_stale_clientid; + goto out; + } + if (conf) { + status = nfs_ok; + if (conf->cl_seqid == cr_ses->seqid) { + dprintk("Got a create_session replay! seqid= %d\n", + conf->cl_seqid); + goto out_replay; + } else if (cr_ses->seqid != conf->cl_seqid + 1) { + status = nfserr_seq_misordered; + dprintk("Sequence misordered!\n"); + dprintk("Expected seqid= %d but got seqid= %d\n", + conf->cl_seqid, cr_ses->seqid); + goto out; + } + conf->cl_seqid++; + } else if (unconf) { + if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || + (ip_addr != unconf->cl_addr)) { + status = nfserr_clid_inuse; + goto out; + } + + if (unconf->cl_seqid != cr_ses->seqid) { + status = nfserr_seq_misordered; + goto out; + } + + move_to_confirmed(unconf); + + /* + * We do not support RDMA or persistent sessions + */ + cr_ses->flags &= ~SESSION4_PERSIST; + cr_ses->flags &= ~SESSION4_RDMA; + + conf = unconf; + } + + status = alloc_init_session(conf, cr_ses); + +out_replay: + memcpy(cr_ses->sessionid, conf->cl_sessionid, 16); + cr_ses->seqid = conf->cl_seqid; + cr_ses->fore_channel.maxreq_sz = max_blocksize; + cr_ses->fore_channel.maxresp_sz = max_blocksize; + cr_ses->fore_channel.maxresp_cached = max_blocksize; + cr_ses->back_channel.maxreq_sz = max_blocksize; + cr_ses->back_channel.maxresp_sz = max_blocksize; + cr_ses->back_channel.maxresp_cached = max_blocksize; + +out: + nfs4_unlock_state(); + dprintk("%s returns %d\n", __func__, ntohl(status)); + return status; } __be32 diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 055f1ad..02c82d4 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c @@ -1119,7 +1119,108 @@ static __be32 nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, struct nfsd4_create_session *sess) { - return nfserr_opnotsupp; /* stub */ + DECODE_HEAD; + + u32 dummy; + char *machine_name; + int i; + int nr_secflavs; + + READ_BUF(16); + COPYMEM(&sess->clientid, 8); + READ32(sess->seqid); + READ32(sess->flags); + + /* Fore channel attrs */ + READ_BUF(28); + READ32(sess->fore_channel.headerpadsz); + READ32(sess->fore_channel.maxreq_sz); + READ32(sess->fore_channel.maxresp_sz); + READ32(sess->fore_channel.maxresp_cached); + READ32(sess->fore_channel.maxops); + READ32(sess->fore_channel.maxreqs); + READ32(sess->fore_channel.nr_rdma_attrs); + if (sess->fore_channel.nr_rdma_attrs == 1) { + READ_BUF(4); + READ32(sess->fore_channel.rdma_attrs); + } else if (sess->fore_channel.nr_rdma_attrs > 1) { + dprintk("Too many fore channel attr bitmaps!\n"); + goto xdr_error; + } + + /* Back channel attrs */ + READ_BUF(28); + READ32(sess->back_channel.headerpadsz); + READ32(sess->back_channel.maxreq_sz); + READ32(sess->back_channel.maxresp_sz); + READ32(sess->back_channel.maxresp_cached); + READ32(sess->back_channel.maxops); + READ32(sess->back_channel.maxreqs); + READ32(sess->back_channel.nr_rdma_attrs); + if (sess->back_channel.nr_rdma_attrs == 1) { + READ_BUF(4); + READ32(sess->back_channel.rdma_attrs); + } else if (sess->back_channel.nr_rdma_attrs > 1) { + dprintk("Too many back channel attr bitmaps!\n"); + goto xdr_error; + } + + READ_BUF(8); + READ32(sess->callback_prog); + + /* callback_sec_params4 */ + READ32(nr_secflavs); + for (i = 0; i < nr_secflavs; ++i) { + READ_BUF(4); + READ32(dummy); + switch (dummy) { + case RPC_AUTH_NULL: + /* Nothing to read */ + break; + case RPC_AUTH_UNIX: + READ_BUF(8); + /* stamp */ + READ32(dummy); + + /* machine name */ + READ32(dummy); + READ_BUF(dummy); + SAVEMEM(machine_name, dummy); + + /* uid, gid */ + READ_BUF(8); + READ32(sess->uid); + READ32(sess->gid); + + /* more gids */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy * 4); + for (i = 0; i < dummy; ++i) + READ32(dummy); + break; + case RPC_AUTH_GSS: + dprintk("RPC_AUTH_GSS callback secflavor " + "not supported!\n"); + READ_BUF(8); + /* gcbp_service */ + READ32(dummy); + /* gcbp_handle_from_server */ + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + /* gcbp_handle_from_client */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + break; + default: + dprintk("Illegal callback secflavor\n"); + return nfserr_inval; + } + } + DECODE_TAIL; } static __be32 @@ -2839,7 +2940,49 @@ static __be32 nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr, struct nfsd4_create_session *sess) { - /* stub */ + ENCODE_HEAD; + + if (nfserr) + goto out; + + RESERVE_SPACE(24); + WRITEMEM(sess->sessionid, sizeof(sess->sessionid)); + WRITE32(sess->seqid); + WRITE32(sess->flags); + ADJUST_ARGS(); + + RESERVE_SPACE(28); + WRITE32(sess->fore_channel.headerpadsz); + WRITE32(sess->fore_channel.maxreq_sz); + WRITE32(sess->fore_channel.maxresp_sz); + WRITE32(sess->fore_channel.maxresp_cached); + WRITE32(sess->fore_channel.maxops); + WRITE32(sess->fore_channel.maxreqs); + WRITE32(sess->fore_channel.nr_rdma_attrs); + ADJUST_ARGS(); + + if (sess->fore_channel.nr_rdma_attrs) { + RESERVE_SPACE(4); + WRITE32(sess->fore_channel.rdma_attrs); + ADJUST_ARGS(); + } + + RESERVE_SPACE(28); + WRITE32(sess->back_channel.headerpadsz); + WRITE32(sess->back_channel.maxreq_sz); + WRITE32(sess->back_channel.maxresp_sz); + WRITE32(sess->back_channel.maxresp_cached); + WRITE32(sess->back_channel.maxops); + WRITE32(sess->back_channel.maxreqs); + WRITE32(sess->back_channel.nr_rdma_attrs); + ADJUST_ARGS(); + + if (sess->back_channel.nr_rdma_attrs) { + RESERVE_SPACE(4); + WRITE32(sess->back_channel.rdma_attrs); + ADJUST_ARGS(); + } +out: return nfserr; } diff --git a/include/linux/nfsd/state.h b/include/linux/nfsd/state.h index 9bbfd88..94b5366 100644 --- a/include/linux/nfsd/state.h +++ b/include/linux/nfsd/state.h @@ -217,6 +217,9 @@ struct nfs4_client { #if defined(CONFIG_NFSD_V4_1) u32 cl_seqid; /* seqid for create_session */ u32 cl_exchange_flags; + nfs41_sessionid cl_sessionid; + + struct svc_xprt *cl_cb_xprt; /* 4.1 callback transport */ #endif /* CONFIG_NFSD_V4_1 */ }; diff --git a/include/linux/nfsd/xdr4.h b/include/linux/nfsd/xdr4.h index aee8996..75d93ab 100644 --- a/include/linux/nfsd/xdr4.h +++ b/include/linux/nfsd/xdr4.h @@ -362,8 +362,27 @@ struct nfsd4_exchange_id { u32 seqid; }; +struct nfsd4_channel { + u32 headerpadsz; + u32 maxreq_sz; + u32 maxresp_sz; + u32 maxresp_cached; + u32 maxops; + u32 maxreqs; + u32 nr_rdma_attrs; + u32 rdma_attrs; +}; + struct nfsd4_create_session { - int foo; /* stub */ + clientid_t clientid; + nfs41_sessionid sessionid; + u32 seqid; + u32 flags; + struct nfsd4_channel fore_channel; + struct nfsd4_channel back_channel; + u32 callback_prog; + u32 uid; + u32 gid; }; struct nfsd4_sequence { -- 1.6.0.2 -- To unsubscribe from this list: send the line "unsubscribe linux-nfs" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html