Copyright (c) 2006 The Regents of the University of Michigan All rights reserved. Permission is granted to use, copy, create derivative works and redistribute this software and such derivative works for any purpose, so long as the name of the University of Michigan is not used in any advertising or publicity pertaining to the use or distribution of this software without specific, written prior authorization. If the above copyright notice or any other identification of the university of michigan is included in any copy of any portion of this software, then the disclaimer below must also be included. This software is provided as is, without representation from the University of Michigan as to its fitness for any purpose, and without warranty by the university of michigan of any kind, either express or implied, including without limitation the implied warranties of merchantability and fitness for a particular purpose. The Regents of the University of Michigan shall not be liable for any damages, including special, indirect, incidental, or consequential damages, with respect to any claim arising out or in connection with the use of the software, even if it has been or is hereafter advised of the possibility of such damages. Signed-off-by: George Dunlap Index: linux-sessions-2.6.14/fs/nfs/callback.c =================================================================== --- linux-sessions-2.6.14.orig/fs/nfs/callback.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/fs/nfs/callback.c 2005-11-30 11:12:24.000000000 -0500 @@ -166,6 +166,13 @@ return SVC_OK; } +void +nfs_callback_init_backchannel(struct rpc_xprt *xprt) +{ + xprt_attach_backchannel(xprt, &nfs4_callback_program); + return; +} + /* * Define NFS4 callback program */ Index: linux-sessions-2.6.14/fs/nfs/nfs4_fs.h =================================================================== --- linux-sessions-2.6.14.orig/fs/nfs/nfs4_fs.h 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/fs/nfs/nfs4_fs.h 2005-11-30 11:12:24.000000000 -0500 @@ -311,7 +311,7 @@ extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t); extern const nfs4_stateid zero_stateid; -extern int nfs4_session_cons(struct nfs4_client *); +extern int nfs4_session_cons(struct nfs4_client *, u32); /* nfs4xdr.c */ extern uint32_t *nfs4_decode_dirent(uint32_t *p, struct nfs_entry *entry, int plus); Index: linux-sessions-2.6.14/fs/nfs/nfs4proc.c =================================================================== --- linux-sessions-2.6.14.orig/fs/nfs/nfs4proc.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/fs/nfs/nfs4proc.c 2005-11-30 11:12:24.000000000 -0500 @@ -50,6 +50,7 @@ #include "nfs4_fs.h" #include "delegation.h" +#include "callback.h" #define NFSDBG_FACILITY NFSDBG_PROC @@ -1107,7 +1108,7 @@ return err; } -int nfs4_session_cons(struct nfs4_client *clp) +int nfs4_session_cons(struct nfs4_client *clp, u32 program) { struct nfs4_client_session *s = clp->cl_session; int status; @@ -1166,6 +1167,7 @@ if (status) { printk("nfs4_session_cons: NFS4.0\n"); s->cs_nfs4version = 0; + s->cs_flags |= NFS4_CLSESS_FAILED; goto out; } printk("nfs4_session_cons: NFS4.1 -- sessions enabled\n"); @@ -1202,6 +1204,7 @@ status = rpc_call_sync(clp->cl_rpcclient, &session, 0); if (status) { + s->cs_flags |= NFS4_CLSESS_FAILED; goto out; } @@ -1225,6 +1228,49 @@ printk("nfs4_session_cons: bytes: %d\n", (sizeof(s->cs_sessions)+4+4+4)); printk("nfs4_session_cons: words: %d\n", s->cs_xdr_inline); +#ifdef CONFIG_NFS_V41_BACKCHANNEL + /* + * attempt to bind the backchannel... + * if it succeds, then we tell the transport layer we've attached it. + * XXX argument values for the backchannel... + */ + struct nfs4_bind_backchannel_args bind_bc_args = { + .maxreqsize = 2 * 1024, + .maxrespsize = 2 * 1024, + .maxreqs = 4, + .mode = 0, + }; + struct nfs4_bind_backchannel_res bind_bc_res = { + .session = s, + }; + struct rpc_message bind_bc = { + .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_BIND_BACKCHANNEL], + .rpc_argp = &bind_bc_args, + .rpc_resp = &bind_bc_res, + }; + /* + * bind-backchannel-args + */ + memcpy(&bind_bc_args.clientid, &clp->cl_clientid, sizeof(clp->cl_clientid)); + bind_bc_args.callback_prog = program; + bind_bc_args.callback_ident = 0; /* XXX make better use of... */ + + nfs_seq_setup(s, &bind_bc_args.sessargs); + status = rpc_call_sync(clp->cl_rpcclient, &bind_bc, 0); + if (status) { + printk("nfs4_session_cons: bind_bc failed %d\n", status); + s->cs_flags |= NFS4_CLSESS_FAILED; /* don't do it again */ + goto out; + } else { + printk("nfs4_session_cons: backchannel bound\n"); + } + /* + * use the returned values? + */ + nfs_callback_init_backchannel(clp->cl_rpcclient->cl_xprt); +#endif + + out: return status; @@ -1259,7 +1305,7 @@ /* * Try to cons up a session */ - nfs4_session_cons(server->nfs4_state); + nfs4_session_cons(server->nfs4_state, NFS4_CALLBACK); /* * don't forget to stuff the session into the result struct */ Index: linux-sessions-2.6.14/fs/nfs/nfs4state.c =================================================================== --- linux-sessions-2.6.14.orig/fs/nfs/nfs4state.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/fs/nfs/nfs4state.c 2005-11-30 11:12:24.000000000 -0500 @@ -203,7 +203,7 @@ static int __nfs4_init_client(struct nfs4_client *clp) { - int status = nfs4_session_cons(clp); + int status = nfs4_session_cons(clp, NFS4_CALLBACK); /* * fallback to non session based code */ Index: linux-sessions-2.6.14/fs/nfs/nfs4xdr.c =================================================================== --- linux-sessions-2.6.14.orig/fs/nfs/nfs4xdr.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/fs/nfs/nfs4xdr.c 2005-11-30 11:12:24.000000000 -0500 @@ -71,8 +71,8 @@ * we currently use size 1 (u32) out of (NFS4_OPAQUE_LIMIT >> 2) */ #define owner_id_maxsz (1 + 1) -#define encode_sequence_maxsz (6) -#define decode_sequence_maxsz (5) +#define encode_sequence_maxsz (10) +#define decode_sequence_maxsz (11) #define compound_encode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) #define compound_decode_hdr_maxsz (3 + (NFS4_MAXTAGLEN >> 2)) #define op_encode_hdr_maxsz (1) @@ -138,111 +138,158 @@ #define decode_create_maxsz (op_decode_hdr_maxsz + 8) #define encode_delegreturn_maxsz (op_encode_hdr_maxsz + 4) #define decode_delegreturn_maxsz (op_decode_hdr_maxsz) +#define encode_create_clientid_maxsz (op_encode_hdr_maxsz + \ + nfs4_name_maxsz + 4 \ + + NFS4_VERIFIER_SIZE) +#define decode_create_clientid_maxsz (op_encode_hdr_maxsz + \ + 8 + NFS4_VERIFIER_SIZE) +#define encode_create_session_maxsz (op_encode_hdr_maxsz + \ + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4) +#define decode_create_session_maxsz (op_encode_hdr_maxsz + \ + 4 + 4 + 4 + 4 + 4 + 4 + 4 + \ + 16 /* sessionid size */) +#define encode_bind_backchannel_maxsz (op_encode_hdr_maxsz + \ + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4) +#define decode_bind_backchannel_maxsz (op_encode_hdr_maxsz + \ + 4 + 4 + 4 + 4) +#define encode_destroy_session_maxsz (op_encode_hdr_maxsz) +#define decode_destroy_session_maxsz (op_encode_hdr_maxsz + 16) #define NFS4_enc_compound_sz (1024) /* XXX: large enough? */ #define NFS4_dec_compound_sz (1024) /* XXX: large enough? */ #define NFS4_enc_read_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 7) #define NFS4_dec_read_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 2) #define NFS4_enc_readlink_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz) #define NFS4_dec_readlink_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz) #define NFS4_enc_readdir_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 9) #define NFS4_dec_readdir_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 2) #define NFS4_enc_write_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 8) #define NFS4_dec_write_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4) #define NFS4_enc_commit_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 3) #define NFS4_dec_commit_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 2) #define NFS4_enc_open_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + \ 13 + 3 + 2 + 64 + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_open_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4 + 5 + 2 + 3 + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_open_confirm_sz \ (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5) #define NFS4_dec_open_confirm_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4) #define NFS4_enc_open_noattr_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + \ 11) #define NFS4_dec_open_noattr_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + \ 4 + 5 + 2 + 3) #define NFS4_enc_open_downgrade_sz \ (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 7) #define NFS4_dec_open_downgrade_sz \ (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4) #define NFS4_enc_close_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 5) #define NFS4_dec_close_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 4) #define NFS4_enc_setattr_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 4 + \ nfs4_fattr_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_setattr_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 3) #define NFS4_enc_fsinfo_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_fsinfo_maxsz) #define NFS4_dec_fsinfo_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_fsinfo_maxsz) #define NFS4_enc_renew_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_renew_maxsz) #define NFS4_dec_renew_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_renew_maxsz) #define NFS4_enc_setclientid_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_setclientid_maxsz) #define NFS4_dec_setclientid_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_setclientid_maxsz) #define NFS4_enc_setclientid_confirm_sz \ (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_setclientid_confirm_maxsz + \ encode_putrootfh_maxsz + \ encode_fsinfo_maxsz) #define NFS4_dec_setclientid_confirm_sz \ (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_setclientid_confirm_maxsz + \ decode_putrootfh_maxsz + \ decode_fsinfo_maxsz) #define NFS4_enc_lock_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz + \ op_encode_hdr_maxsz + \ @@ -250,12 +297,14 @@ 1 + 4 + 1 + 2 + \ owner_id_maxsz) #define NFS4_dec_lock_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz + \ op_decode_hdr_maxsz + \ 2 + 2 + 1 + 2 + \ owner_id_maxsz) #define NFS4_enc_lockt_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz + \ op_encode_hdr_maxsz + \ @@ -263,141 +312,178 @@ owner_id_maxsz) #define NFS4_dec_lockt_sz (NFS4_dec_lock_sz) #define NFS4_enc_locku_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz + \ op_encode_hdr_maxsz + \ 1 + 1 + 4 + 2 + 2) #define NFS4_dec_locku_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz + \ op_decode_hdr_maxsz + 4) #define NFS4_enc_access_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 1) #define NFS4_dec_access_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 2) #define NFS4_enc_getattr_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_getattr_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_lookup_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_lookup_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_lookup_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_lookup_root_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putrootfh_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_lookup_root_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putrootfh_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) -#if 0 -#define NFS4_enc_remove_sz (compound_encode_hdr_maxsz + \ - encode_putfh_maxsz) -#else #define NFS4_enc_remove_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_remove_maxsz) -#endif #define NFS4_dec_remove_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 5) #define NFS4_enc_rename_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_putfh_maxsz + \ encode_rename_maxsz) #define NFS4_dec_rename_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_putfh_maxsz + \ decode_rename_maxsz) #define NFS4_enc_link_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_savefh_maxsz + \ encode_putfh_maxsz + \ encode_link_maxsz) #define NFS4_dec_link_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_savefh_maxsz + \ decode_putfh_maxsz + \ decode_link_maxsz) #define NFS4_enc_symlink_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_symlink_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_symlink_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_symlink_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_create_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_create_maxsz + \ encode_getattr_maxsz + \ encode_getfh_maxsz) #define NFS4_dec_create_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_create_maxsz + \ decode_getattr_maxsz + \ decode_getfh_maxsz) #define NFS4_enc_pathconf_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_pathconf_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_statfs_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_statfs_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + 12) #define NFS4_enc_server_caps_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_server_caps_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_getattr_maxsz) #define NFS4_enc_delegreturn_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_delegreturn_maxsz) #define NFS4_dec_delegreturn_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_delegreturn_maxsz) #define NFS4_enc_getacl_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ encode_getattr_maxsz) #define NFS4_dec_getacl_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + \ nfs4_fattr_bitmap_maxsz + 1) #define NFS4_enc_setacl_sz (compound_encode_hdr_maxsz + \ + encode_sequence_maxsz + \ encode_putfh_maxsz + \ op_encode_hdr_maxsz + 4 + \ nfs4_fattr_bitmap_maxsz + 1) #define NFS4_dec_setacl_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ decode_putfh_maxsz + \ op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz) -/* XXXX */ - -#define NFS4_dec_create_clientid_sz compound_decode_hdr_maxsz + 4 -#define NFS4_enc_create_clientid_sz compound_decode_hdr_maxsz + 4 -#define NFS4_dec_create_session_sz compound_decode_hdr_maxsz + 4 -#define NFS4_enc_create_session_sz compound_decode_hdr_maxsz + 4 -#define NFS4_dec_bind_backchannel_sz compound_decode_hdr_maxsz + 4 -#define NFS4_enc_bind_backchannel_sz compound_decode_hdr_maxsz + 4 -#define NFS4_dec_destroy_session_sz compound_decode_hdr_maxsz + 4 -#define NFS4_enc_destroy_session_sz compound_decode_hdr_maxsz + 4 +#define NFS4_dec_create_clientid_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_create_clientid_maxsz) +#define NFS4_enc_create_clientid_sz (compound_decode_hdr_maxsz + \ + encode_create_clientid_maxsz) +#define NFS4_dec_create_session_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_create_session_maxsz) +#define NFS4_enc_create_session_sz (compound_decode_hdr_maxsz + \ + encode_create_session_maxsz) +#define NFS4_dec_bind_backchannel_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_bind_backchannel_maxsz) +#define NFS4_enc_bind_backchannel_sz (compound_decode_hdr_maxsz + \ + encode_bind_backchannel_maxsz) +#define NFS4_dec_destroy_session_sz (compound_decode_hdr_maxsz + \ + decode_sequence_maxsz + \ + decode_destroy_session_maxsz) +#define NFS4_enc_destroy_session_sz (compound_decode_hdr_maxsz + \ + encode_destroy_session_maxsz) static struct { @@ -457,6 +543,12 @@ xdr_encode_opaque(p, str, len); } +static uint32_t *encode_clientid(uint32_t *p, clientid4 clientid) +{ + WRITEMEM(&clientid, 8); + return p; +} + static int encode_sequence(struct xdr_stream *xdr, const struct nfs4_sequence_args *sa) { @@ -769,17 +861,6 @@ return 0; } -static -uint32_t * -encode_clientid(uint32_t *p, clientid4 clientid) -{ - WRITEMEM(&clientid, 8); - - return p; - -} - - /* * opcode,type,reclaim,offset,length,new_lock_owner = 32 * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40 @@ -4381,7 +4462,7 @@ uint32_t *p; int status; - status = decode_op_hdr(xdr, OP_BIND_BACKCHANNEL, 0); + status = decode_op_hdr(xdr, OP_BIND_BACKCHANNEL, res->session); if (status) return status; Index: linux-sessions-2.6.14/include/linux/nfs_xdr.h =================================================================== --- linux-sessions-2.6.14.orig/include/linux/nfs_xdr.h 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/include/linux/nfs_xdr.h 2005-11-30 11:12:24.000000000 -0500 @@ -868,6 +868,7 @@ enum nfs4_channel_mode mode; /* maxrdmareads only occurs when mode == NFS4_RDMA */ u32 maxrdmareads; + struct nfs4_client_session * session; }; Index: linux-sessions-2.6.14/include/linux/sunrpc/debug.h =================================================================== --- linux-sessions-2.6.14.orig/include/linux/sunrpc/debug.h 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/include/linux/sunrpc/debug.h 2005-11-30 11:12:24.000000000 -0500 @@ -37,6 +37,7 @@ #define RPCDBG_SVCDSP 0x0200 #define RPCDBG_MISC 0x0400 #define RPCDBG_CACHE 0x0800 +#define RPCDBG_BCHAN 0x1000 #define RPCDBG_ALL 0x7fff #ifdef __KERNEL__ Index: linux-sessions-2.6.14/include/linux/sunrpc/xprt.h =================================================================== --- linux-sessions-2.6.14.orig/include/linux/sunrpc/xprt.h 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/include/linux/sunrpc/xprt.h 2005-11-30 11:12:24.000000000 -0500 @@ -162,6 +162,12 @@ unsigned long connect_timeout, bind_timeout, reestablish_timeout; + + struct rpc_task *bc_task; /* backchannel task/request */ + struct svc_program *bc_svc; /* backchannel service */ + struct list_head bc_queue; /* backchannel queue */ + spinlock_t bc_lock; /* backchannel lock */ + wait_queue_head_t bc_wq;; /* backchannel waitqueue */ /* * Disconnection of idle transports Index: linux-sessions-2.6.14/net/sunrpc/Makefile =================================================================== --- linux-sessions-2.6.14.orig/net/sunrpc/Makefile 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/net/sunrpc/Makefile 2005-11-30 11:12:24.000000000 -0500 @@ -11,6 +11,6 @@ auth.o auth_null.o auth_unix.o \ svc.o svcsock.o svcauth.o svcauth_unix.o \ rpcb_clnt.o timer.o xdr.o \ - sunrpc_syms.o cache.o rpc_pipe.o + sunrpc_syms.o cache.o rpc_pipe.o backchannel.o sunrpc-$(CONFIG_PROC_FS) += stats.o sunrpc-$(CONFIG_SYSCTL) += sysctl.o Index: linux-sessions-2.6.14/net/sunrpc/sched.c =================================================================== --- linux-sessions-2.6.14.orig/net/sunrpc/sched.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/net/sunrpc/sched.c 2005-11-30 11:12:24.000000000 -0500 @@ -89,7 +89,7 @@ * without calling del_timer_sync(). The latter could cause a * deadlock if called while we're holding spinlocks... */ -static void rpc_run_timer(struct rpc_task *task) +void rpc_run_timer(struct rpc_task *task) { void (*callback)(struct rpc_task *); @@ -590,7 +590,6 @@ dprintk("RPC: %5u rpc_execute flgs %x\n", task->tk_pid, task->tk_flags); - BUG_ON(RPC_IS_QUEUED(task)); for (;;) { @@ -599,6 +598,7 @@ */ rpc_delete_timer(task); + /* * Execute any pending callback. */ @@ -626,7 +626,7 @@ if (!RPC_IS_QUEUED(task)) { if (task->tk_action != NULL) task->tk_action(task); - else if (__rpc_do_exit(task)) + else if (__rpc_do_exit(task)) break; } @@ -752,6 +752,31 @@ } EXPORT_SYMBOL_GPL(rpc_free); + /* + * hook for backchannel to throw send of request into the scheduler. + * handy to use instead of trying to re-expose static stuff. + */ + +struct rpc_task * +rpc_add_all_tasks(struct rpc_task *task) +{ + /* Initialize workqueue for async tasks */ + task->tk_workqueue = rpciod_workqueue; + + /* timer init XXX? */ + init_timer(&task->tk_timer); + task->tk_timer.data = (unsigned long) task; + task->tk_timer.function = (void (*)(unsigned long)) rpc_run_timer; + + /* Add to global list of all tasks */ + spin_lock(&rpc_sched_lock); + list_add(&task->tk_task, &all_tasks); + spin_unlock(&rpc_sched_lock); + return task; +} + +EXPORT_SYMBOL(rpc_add_all_tasks); + /* * Creation and deletion of RPC task structures */ @@ -762,6 +787,7 @@ task->tk_timer.data = (unsigned long) task; task->tk_timer.function = (void (*)(unsigned long)) rpc_run_timer; task->tk_client = clnt; + task->tk_xprt = clnt->cl_xprt; task->tk_flags = flags; task->tk_exit = callback; @@ -805,12 +831,20 @@ return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_NOFS); } -static void +struct rpc_task * +rpc_alloc_task_atomic(void) +{ + return (struct rpc_task *)mempool_alloc(rpc_task_mempool, GFP_ATOMIC); +} +EXPORT_SYMBOL(rpc_alloc_task_atomic); + +void rpc_default_free_task(struct rpc_task *task) { dprintk("RPC: %5u freeing task\n", task->tk_pid); mempool_free(task, rpc_task_mempool); } +EXPORT_SYMBOL(rpc_default_free_task); /* * Create a new task for the specified client. We have to @@ -867,8 +901,9 @@ rpc_delete_timer(task); /* Release resources */ - if (task->tk_rqstp) + if (task->tk_rqstp) { xprt_release(task); + } if (task->tk_msg.rpc_cred) rpcauth_unbindcred(task); if (task->tk_client) { Index: linux-sessions-2.6.14/net/sunrpc/xprt.c =================================================================== --- linux-sessions-2.6.14.orig/net/sunrpc/xprt.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/net/sunrpc/xprt.c 2005-11-30 11:12:24.000000000 -0500 @@ -823,6 +823,38 @@ return err; } +/* + * xprt_bc_prepare_transmit + * ... incredibly similar to xprt_prepare_transmit, + * 'cept doesn't notice the rq_received counts. + */ + +int +xprt_bc_prepare_transmit(struct rpc_task *task) +{ + struct rpc_xprt *xprt = task->tk_xprt; + int err = 0; + + dprintk("RPC: %5u xprt_prepare_transmit\n", task->tk_pid); + + if (xprt->shutdown) + return -EIO; + + spin_lock_bh(&xprt->transport_lock); + if (!xprt->ops->reserve_xprt(task)) { + err = -EAGAIN; + goto out_unlock; + } + + if (!xprt_connected(xprt)) { + err = -ENOTCONN; + goto out_unlock; + } +out_unlock: + spin_unlock_bh(&xprt->transport_lock); + return err; +} + /** * xprt_transmit - send an RPC request on a transport * @task: controlling RPC task Index: linux-sessions-2.6.14/net/sunrpc/xprtsock.c =================================================================== --- linux-sessions-2.6.14.orig/net/sunrpc/xprtsock.c 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/net/sunrpc/xprtsock.c 2005-11-30 11:12:24.000000000 -0500 @@ -98,7 +98,8 @@ #define XS_LAST_FRAG (1UL << 0) #define XS_COPY_FRGH (1UL << 1) #define XS_COPY_XID (1UL << 2) -#define XS_COPY_DATA (1UL << 3) +#define XS_COPY_HDIR (1UL << 3) +#define XS_COPY_DATA (1UL << 4) typedef ssize_t (*sendpage_t)(struct socket *, struct page *, int, size_t, int); @@ -116,7 +117,8 @@ u32 tcp_fraghdr, /* fragment header */ tcp_reclen, /* fragment length */ tcp_offset, /* fragment offset */ - tcp_xid; /* current XID */ + tcp_xid, /* current XID */ + tcp_hdir; /* hdr direction CALL/REPLY */ unsigned long tcp_copied, /* bytes copied to request */ tcp_flags; @@ -148,8 +150,9 @@ void (*old_write_space)(struct sock *); }; +#define RPC_DEBUG_DATA #ifdef RPC_DEBUG_DATA -static void xs_pktdump(char *msg, u32 *packet, unsigned int count) +void xs_pktdump(char *msg, u32 *packet, unsigned int count) { u8 *buf = (u8 *) packet; int j; @@ -600,6 +603,11 @@ module_put(THIS_MODULE); } + + +extern int xprt_backchannel_complete(struct rpc_task *, int copied); +extern struct rpc_task *xprt_backchannel_task(struct rpc_xprt *xprt, int xid); + /** * xs_udp_data_ready - "data ready" callback for UDP sockets * @sk: socket with data to read @@ -614,6 +622,7 @@ struct sk_buff *skb; int err, repsize, copied; u32 _xid, *xp; + u32 _hdir, *hp; read_lock(&sk->sk_callback_lock); dprintk("RPC: xs_udp_data_ready...\n"); @@ -639,11 +648,34 @@ if (xp == NULL) goto dropit; + /* Get the hdir from the xkb... */ + hp = skb_header_pointer(skb, sizeof(struct udphdr) + sizeof(_xid), + sizeof(_hdir), &_hdir); + if (hp == NULL) + goto dropit; + /* Look up and lock the request corresponding to the given XID */ spin_lock(&xprt->transport_lock); rovr = xprt_lookup_rqst(xprt, *xp); - if (!rovr) - goto out_unlock; + if (!rovr) { + if (*hp == RPC_CALL) { + /* + * this is a REQUEST ... backchannel + * + * FIXME -- deal with multiple requests outstanding + */ + if (xprt->bc_task == NULL) + xprt->bc_task = xprt_backchannel_task(xprt, *xp); + if (xprt->bc_task) { + rovr = xprt->bc_task->tk_rqstp; + rovr->rq_xid = *xp; + } else + goto out_unlock; + } + goto out_unlock; + } + + task = rovr->rq_task; if ((copied = rovr->rq_private_buf.buflen) > repsize) @@ -657,6 +689,13 @@ /* Something worked... */ dst_confirm(skb->dst); + /* check for RPC_CALL */ + if (*hp == RPC_CALL) { + xprt_backchannel_complete(task, copied); + goto out_unlock; + + } + xprt_adjust_cwnd(task, copied); xprt_update_rtt(task); xprt_complete_rqst(task, copied); @@ -712,6 +751,8 @@ /* XXX: should print name of server that's screwing up */ if (unlikely(priv->tcp_reclen < 4)) { dprintk("RPC: invalid TCP record fragment length\n"); + printk("RPC: offset %d len %d\n", + priv->tcp_offset, priv->tcp_reclen); xprt_disconnect(xprt); return; } @@ -726,7 +767,7 @@ priv->tcp_offset = 0; if (priv->tcp_flags & XS_LAST_FRAG) { priv->tcp_flags &= ~XS_COPY_DATA; - priv->tcp_flags |= XS_COPY_XID; + priv->tcp_flags |= XS_COPY_XID|XS_COPY_HDIR; priv->tcp_copied = 0; } } @@ -745,13 +786,43 @@ if (used != len) return; priv->tcp_flags &= ~XS_COPY_XID; - priv->tcp_flags |= XS_COPY_DATA; + priv->tcp_flags |= XS_COPY_HDIR; priv->tcp_copied = 4; dprintk("RPC: reading reply for XID %08x\n", ntohl(priv->tcp_xid)); xs_tcp_check_fraghdr(priv); } +/** + * xs_tcp_read_hdir + * @priv: xprt sock private context + * @desc: peek at the call/reply, but don't consume. + * we want the hdir in the buffer for the scheduler to see... + * + */ +static inline void xs_tcp_read_hdir(struct xs_private *priv, skb_reader_t *desc) +{ + size_t len, used; + char *p; + + len = sizeof(priv->tcp_hdir) + sizeof(priv->tcp_xid) - priv->tcp_offset; + p = ((char *) &priv->tcp_hdir) + + (priv->tcp_offset - sizeof(priv->tcp_xid)); + if (len > desc->count) + len = desc->count; + if (skb_copy_bits(desc->skb, desc->offset, p, len)) + return; + dprintk("RPC: reading HDIR (%Zu bytes)\n", len); + priv->tcp_hdir = ntohl(priv->tcp_hdir); + + priv->tcp_offset = sizeof(priv->tcp_xid); + priv->tcp_flags &= ~XS_COPY_HDIR; + priv->tcp_flags |= XS_COPY_DATA; + priv->tcp_copied = 4; + dprintk("RPC: HDIR %08x\n", priv->tcp_hdir); + xs_tcp_check_fraghdr(priv); +} + static void xs_tcp_read_request(struct rpc_xprt *xprt, skb_reader_t *desc) { struct xs_private *priv = xs_private_data(xprt); @@ -764,14 +835,46 @@ spin_lock(&xprt->transport_lock); req = xprt_lookup_rqst(xprt, priv->tcp_xid); if (!req) { - priv->tcp_flags &= ~XS_COPY_DATA; - dprintk("RPC: XID %08x request not found!\n", - ntohl(priv->tcp_xid)); - spin_unlock(&xprt->transport_lock); - return; + /* + * is this a request? we perform this test here + * since the COMMON path through the code is to + * get replies. + */ + if (priv->tcp_hdir == RPC_CALL) { + /* + * request from the REPLY channel? + * backchannel enabled? + */ + struct rpc_task *task; + //dprintk("rpc-call %x\n", task); + if ((task = xprt->bc_task) == NULL) { + xprt->bc_task = xprt_backchannel_task(xprt, priv->tcp_xid); + if ((task = xprt->bc_task) != NULL) { + dprintk("new task %p req %p\n", + task, task->tk_rqstp); + req = task->tk_rqstp; + } + dprintk("using task %p %p req %p %d\n", + task, task->tk_xprt, + req, req->rq_private_buf.buflen); + } + } + + if (req == NULL) { + priv->tcp_flags &= ~XS_COPY_DATA; + dprintk("RPC: XID %08x request not found!\n", + ntohl(priv->tcp_xid)); + spin_unlock(&xprt->transport_lock); + return; + } } rcvbuf = &req->rq_private_buf; + dprintk("recv buf %x/%x/%d copy %d/%d desc %d/%d\n", + rcvbuf, rcvbuf->head[0].iov_base, + rcvbuf->buflen, + priv->tcp_reclen, priv->tcp_offset, + desc->count, desc->offset); len = desc->count; if (len > priv->tcp_reclen - priv->tcp_offset) { skb_reader_t my_desc; @@ -786,6 +889,10 @@ } else r = xdr_partial_copy_from_skb(rcvbuf, priv->tcp_copied, desc, xs_tcp_copy_data); + + dprintk("copy len %d xdr->len %d\n", len, rcvbuf->len); + xs_pktdump("target:", rcvbuf->head[0].iov_base, len); + if (r > 0) { priv->tcp_offset += r; @@ -822,8 +929,12 @@ } out: - if (!(priv->tcp_flags & XS_COPY_DATA)) - xprt_complete_rqst(req->rq_task, priv->tcp_copied); + if (!(priv->tcp_flags & XS_COPY_DATA)) { + if(priv->tcp_hdir == RPC_CALL) + xprt_backchannel_complete(req->rq_task, priv->tcp_copied); + else + xprt_complete_rqst(req->rq_task, priv->tcp_copied); + } spin_unlock(&xprt->transport_lock); xs_tcp_check_fraghdr(priv); } @@ -868,6 +979,11 @@ xs_tcp_read_xid(priv, &desc); continue; } + /* Read in the RPC_CALL/RPC_REPLY direction */ + if( priv->tcp_flags & XS_COPY_HDIR) { + xs_tcp_read_hdir(priv, &desc); + continue; + } /* Read in the request data */ if (priv->tcp_flags & XS_COPY_DATA) { xs_tcp_read_request(xprt, &desc); @@ -936,7 +1052,7 @@ priv->tcp_offset = 0; priv->tcp_reclen = 0; priv->tcp_copied = 0; - priv->tcp_flags = XS_COPY_FRGH | XS_COPY_XID; + priv->tcp_flags = XS_COPY_FRGH | XS_COPY_XID | XS_COPY_HDIR; xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO; xprt_wake_pending_tasks(xprt, 0); } @@ -1534,6 +1650,7 @@ priv = xs_alloc_private(); if (priv == NULL) return -ENOMEM; + priv->tcp_hdir = RPC_REPLY; xprt->transport_data = priv; xprt->max_reqs = xprt_tcp_slot_table_entries; Index: linux-sessions-2.6.14/fs/Kconfig =================================================================== --- linux-sessions-2.6.14.orig/fs/Kconfig 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/fs/Kconfig 2005-11-30 11:12:24.000000000 -0500 @@ -1350,6 +1350,15 @@ If unsure, say N. +config NFS_V41_BACKCHANNEL + bool "Implement Client Backchannel call (EXPERIMENTAL)" + depends on NFS_V4 && EXPERIMENTAL + help + Say Y here if you want your NFS 4.1 Sessions client to attempt to + make the BACKCHANNEL call. + + If unsure, say N. + config NFS_DIRECTIO bool "Allow direct I/O on NFS files (EXPERIMENTAL)" depends on NFS_FS && EXPERIMENTAL Index: linux-sessions-2.6.14/include/linux/sunrpc/sched.h =================================================================== --- linux-sessions-2.6.14.orig/include/linux/sunrpc/sched.h 2005-11-30 11:12:22.000000000 -0500 +++ linux-sessions-2.6.14/include/linux/sunrpc/sched.h 2005-11-30 11:12:24.000000000 -0500 @@ -45,6 +45,7 @@ struct list_head tk_task; /* global list of tasks */ struct rpc_clnt * tk_client; /* RPC client */ struct rpc_rqst * tk_rqstp; /* RPC request */ + struct rpc_xprt * tk_xprt; int tk_status; /* result of last operation */ /* @@ -101,7 +102,7 @@ #endif }; #define tk_auth tk_client->cl_auth -#define tk_xprt tk_client->cl_xprt +//#define tk_xprt tk_client->cl_xprt /* support walking a list of tasks on a wait queue */ #define task_for_each(task, pos, head) \ Index: linux-sessions-2.6.14/net/sunrpc/backchannel.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-sessions-2.6.14/net/sunrpc/backchannel.c 2005-11-30 11:12:35.000000000 -0500 @@ -0,0 +1,736 @@ +/* + * Copyright (c) 2006 The Regents of the University of Michigan + * All rights reserved. + * + * Permission is granted to use, copy, create derivative works and + * redistribute this software and such derivative works for any purpose, + * so long as the name of the University of Michigan is not used in + * any advertising or publicity pertaining to the use or distribution + * of this software without specific, written prior authorization. If + * the above copyright notice or any other identification of the + * university of michigan is included in any copy of any portion of + * this software, then the disclaimer below must also be included. + * + * This software is provided as is, without representation from the + * University of Michigan as to its fitness for any purpose, and without + * warranty by the university of michigan of any kind, either express + * or implied, including without limitation the implied warranties of + * merchantability and fitness for a particular purpose. The Regents + * of the University of Michigan shall not be liable for any damages, + * including special, indirect, incidental, or consequential damages, + * with respect to any claim arising out or in connection with the use + * of the software, even if it has been or is hereafter advised of the + * possibility of such damages. + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef RPC_DEBUG +# define RPCDBG_FACILITY RPCDBG_BCHAN +#endif + +/* + * backchannel. + * support to recieve a request on a "reply" channel, + * build a response using existing _proc code, then + * send a reply down the request channel. + * + * notes: + * the backchannel code uses several properties + * of the client and server rpc implementations. + * this leads to code, with unclearly described + * requirements. + * + * for example, the svc_program of the server is + * re-used, along with the same procs. these procs + * typically run in a context which allows sleeps + * since svc side code runs using a complete process. + * the client side, however, runs using rpciod, and + * request reception within soft-irq. the rpciod context + * does not allow any long term sleeps since that's + * inconsistent with the implementation of the scheduler's + * state machine, but we want to re-use the client's transport + * for call reception then transmission of the response. + * that leads to an implementation where we switch + * contexts: once from the client side rpc reception + * within softirq, then to a kernel thread meant to run + * the _proc, then back to the client's scheduler/state + * machine to manage transmission of the reply, to remain + * sensitive to cogestion. + */ + + +extern struct rpc_task *rpc_alloc_task_atomic(void); +extern void rpc_default_free_task(struct rpc_task *task); +extern void rpc_add_all_tasks(struct rpc_task *task); +extern int xprt_bc_prepare_transmit(struct rpc_task *task); + + + +void +xprt_backchannel_req_reset(struct rpc_rqst *req) +{ + struct xdr_buf *sndbuf = &req->rq_snd_buf; + struct xdr_buf *rcvbuf = &req->rq_rcv_buf; + struct xdr_buf *privbuf = &req->rq_private_buf; + u32 bufsiz; + + bufsiz = req->rq_bufsize >> 1; + + sndbuf->head[0].iov_base = (void *)req->rq_buffer; + sndbuf->head[0].iov_len = 0; + sndbuf->tail[0].iov_len = 0; + sndbuf->page_len = 0; + sndbuf->len = 0; + sndbuf->buflen = bufsiz; + + rcvbuf->head[0].iov_base = (void *)((char *)req->rq_buffer + bufsiz); + rcvbuf->head[0].iov_len = bufsiz; + rcvbuf->tail[0].iov_len = 0; + rcvbuf->page_len = 0; + rcvbuf->len = 0; + rcvbuf->buflen = bufsiz; + + privbuf->head[0].iov_base = (void *)rcvbuf->head[0].iov_base; + privbuf->head[0].iov_len = bufsiz; + privbuf->tail[0].iov_len = 0; + privbuf->page_len = 0; + privbuf->len = 0; + privbuf->buflen = bufsiz; + dprintk("xprt_backchannel_req_reset: snd %p rcv %p priv %p\n", + sndbuf->head[0].iov_base, + rcvbuf->head[0].iov_base, + privbuf->head[0].iov_base); + + return; +} + +/* + * stolen from rpc_init_task + * we don't use rpc_init_task, since it keeps all the + * tasks available for scheduling. we're not part of + * that vision. + * (XXX do we need to init any of the rpc_actions?) + */ + +static struct rpc_task * +bc_task_init(struct rpc_task * task, struct rpc_xprt *xprt) +{ + memset(task, 0, sizeof(*task)); + return task; +} + +/* + * xprt_backchannel_alloc + * allocate space to manage a single request use the "service" vector + * to determine size. allocates memory during softirq... + * possbily dramatic failures. + * + * XXX maybe somehow reuse rpc_buffer_mempool in sched.c + * + */ + +#define RPC_BUFFER_MAXSIZE (2048) /* borrowed from sched.c */ + +int +xprt_backchennel_alloc(struct rpc_xprt *xprt, struct rpc_task *task) +{ + struct rpc_rqst *req; + int size = 0; + int status; + + status = -ENOMEM; + if (task == NULL) + goto out; + /* + * XXX move this to initialization? + */ + { + int i; + for (i=0; ibc_svc->pg_nvers; ++i) { + if (xprt->bc_svc->pg_vers[i]) { + if (size < xprt->bc_svc->pg_vers[i]->vs_xdrsize) + size = xprt->bc_svc->pg_vers[i]->vs_xdrsize; + } + } + dprintk("xprt_backchennel_alloc: size %d\n", size); + } + + /* FIXME -- call xprtsock function...? */ + if(!task->tk_rqstp) { + if(!list_empty(&xprt->free)) { + req = list_entry(xprt->free.next, struct rpc_rqst, rq_list); + list_del_init(&req->rq_list); + task->tk_rqstp = req; + } else { + /* Allocate more memory...? */ + BUG_ON(list_empty(&xprt->free)); + } + } + + req = task->tk_rqstp; + memset(req, 0, sizeof(*req)); + req->rq_task = task; + req->rq_xprt = xprt; + + /* rq_buffer must be allocated from rpc_buffer_mempool, in an + * GPF_ATOMIC type manner. The easiest way to do that is to + * call rpc_malloc(), with the RPC_TASK_SWAPPER flag set */ + task->tk_flags |= RPC_TASK_SWAPPER; + rpc_malloc(task, size); + task->tk_flags &= RPC_TASK_SWAPPER; + + if (req->rq_buffer) { + //req->rq_bufsize = size; + status = 0; + xprt_backchannel_req_reset(req); + } + /* FIXME: release tk_rqstp if rq_buffer returns NULL */ + +out: + return status; +} + +static void +bc_free_task(struct rpc_task *task) +{ + rpc_default_free_task(task); +} + + +static inline void bc_putu32(struct kvec *iov, u32 val) +{ + u32 *vp = iov->iov_base + iov->iov_len; + *vp = val; + iov->iov_len += sizeof(u32); +} + +static inline u32 bc_getu32(struct kvec *iov) +{ + u32 val, *vp; + vp = iov->iov_base; + val = *vp++; + iov->iov_base = (void*)vp; + iov->iov_len -= sizeof(u32); + return val; +} + + +/* + * bc_clnt2svc + * convert the client side request... into a server side request + * + * ONLY populate fields which the proc would + * likely use. + * + * we also need to allocate space for the + * xdr decode target, and the result of the + * execution of the procedure. + * + * this is in no way a generic conversion routine. + * + * svc_serv is part of the machinery to make null_auth work. + * + */ +static inline struct svc_rqst * +bc_clnt2svc(struct svc_rqst *rqst, struct rpc_rqst *req, struct svc_procedure *procp, struct svc_serv *serv) +{ + memset(rqst, 0, sizeof(*rqst)); + + /* + * associate a server struct to get to the program for null_auth + */ + rqst->rq_server = serv; + serv->sv_program = req->rq_xprt->bc_svc; + + rqst->rq_xid = req->rq_xid; + rqst->rq_addrlen = min(sizeof(rqst->rq_addr), sizeof(req->rq_xprt->addr)); + memcpy(&rqst->rq_addr, &req->rq_xprt->addr, rqst->rq_addrlen); + + memcpy(&rqst->rq_arg, &req->rq_rcv_buf, sizeof(struct xdr_buf)); + memcpy(&rqst->rq_res, &req->rq_snd_buf, sizeof(struct xdr_buf)); + + rqst->rq_argp = (u32 *) kmalloc(procp->pc_argsize, GFP_KERNEL); + if (rqst->rq_argp == NULL) { + return 0; + } + rqst->rq_resp = (u32 *) kmalloc(procp->pc_ressize, GFP_KERNEL); + if (rqst->rq_resp == NULL) { + kfree(rqst->rq_argp); + rqst->rq_argp = 0; + return 0; + } + + memset(rqst->rq_argp, 0, procp->pc_argsize); + memset(rqst->rq_resp, 0, procp->pc_ressize); + + return rqst; +} + +/* + * bc_svc2clnt + * convert the server side request... BACK into a client side + * + * length of the result needs to be moved back... + */ + +static inline struct rpc_rqst * +bc_svc2clnt(struct rpc_rqst *req, struct svc_rqst *rqst) +{ + if (rqst->rq_argp) { + kfree(rqst->rq_argp); + rqst->rq_argp = 0; + } + if (rqst->rq_resp) { + kfree(rqst->rq_resp); + rqst->rq_resp = 0; + } + memcpy(&req->rq_snd_buf, &rqst->rq_res, sizeof(struct xdr_buf)); + return req; +} + +/* + * note: + * to write a request back to the server, we must be sensitive to + * both the congestion and write locking issues associated with + * the output data stream. to manage those, we re-parent the + * task to the sceduler, but must provide the right callout routines + * to get the data to the other side. it could be possible to use + * the callouts for the client (clnt.c) BUT, that's a REQUEST based + * set of transisitions, and we need a reply based set. + */ + +static void +bc_status(struct rpc_task *task) +{ + int status = task->tk_status; + task->tk_status = 0; + + switch(status) { + case -EAGAIN: + break; + default: + rpc_exit(task, status); + break; + } +} + + +/* + * bc_transmit + */ + +extern void xprt_timer(struct rpc_task *task); + +static void +bc_transmit(struct rpc_task *task) +{ + dprintk("BC: %u bc_transmit status %d\n", + task->tk_pid, task->tk_status); + + if (task->tk_status < 0) + return; + task->tk_status = xprt_bc_prepare_transmit(task); + if (task->tk_status != 0) { + bc_status(task); + return; + } + + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_xprt * xprt = req->rq_xprt; + int status; + smp_rmb(); + dprintk("RPC: %5u bc_transmit(%u)\n", task->tk_pid, req->rq_slen); + status = xprt->ops->send_request(task); + switch(status) { + case -EAGAIN: + case -ENOTCONN: + break; + default: + spin_lock_bh(&xprt->transport_lock); + xprt->ops->release_xprt(xprt, task); + spin_unlock_bh(&xprt->transport_lock); + } + task->tk_status = status; + + if (task->tk_status < 0) { + bc_status(task); + return; + } + + /* + * to clean up, we must first set the next action step + * to NULL... but we also need to wake the task up, + * after all, the transmit expects that there will be + * a response; that's what the client DOES. we're sending + * a REPLY, there will be no other wakeup. + */ + task->tk_action = NULL; + rpc_wake_up_task(task); +} + + +/* + * bc_reparent + * hand this task over to the scheduler + * borrowed heavily from rpc_init_task + */ +static int bc_task_pid = -1; + +static void +bc_reparent(struct rpc_task *task) +{ +#ifdef RPC_DEBUG + task->tk_magic = 0xf00baa; + task->tk_pid = bc_task_pid--; +#endif + + task->tk_release = bc_free_task; + task->tk_flags |= RPC_TASK_DYNAMIC|RPC_TASK_ASYNC; + + /* retry counters */ + task->tk_garb_retry = 2; + task->tk_cred_retry = 2; + + task->tk_priority = RPC_PRIORITY_NORMAL; + task->tk_cookie = (unsigned long)current; + + /* scheduler callout setup */ + task->tk_action = bc_transmit; + task->tk_status = 0; + + /* reparent to the scheduler */ + BUG_ON(!list_empty(&task->tk_rqstp->rq_list)); + rpc_add_all_tasks(task); + BUG_ON(!list_empty(&task->tk_rqstp->rq_list)); + + { + int status = rpc_execute(task); /* rpc_async_schedule? */ + if (status) + printk("bc_reparent: status %d\n", status); + } + +} + +/* + * process a request from the backchannel + */ + +int +xprt_backchannel_process(struct rpc_xprt *xprt, struct rpc_rqst *req) +{ + + struct svc_program *progp; + struct svc_version *versp = NULL; + struct svc_procedure *procp = NULL; + struct kvec * argv = &req->rq_rcv_buf.head[0]; + struct kvec * resv = &req->rq_snd_buf.head[0]; + u32 prog, vers, proc; + u32 dir, rpc_stat, auth_stat; + u32 *statp = 0; /* happify compiler */ + kxdrproc_t xdr; + + /* + * don't forget to skip the header portion when its needed + */ + resv->iov_len += xprt->tsh_size; + /* + * + */ + bc_getu32(argv); /* discard XID (not there) */ + bc_putu32(resv, req->rq_xid); + + dir = ntohl(bc_getu32(argv)); + vers = ntohl(bc_getu32(argv)); + + bc_putu32(resv, xdr_one); /* REPLY */ + + if (dir != 0) /* direction != CALL */ + goto err_bad_dir; /* panic?!? */ + if (vers != 2) /* RPC version number */ + goto err_bad_rpc; + + bc_putu32(resv, xdr_zero); /* ACCEPT */ + prog = ntohl(bc_getu32(argv)); + vers = ntohl(bc_getu32(argv)); + proc = ntohl(bc_getu32(argv)); + + progp = xprt->bc_svc; /* program/service */ + + if (prog != progp->pg_prog) + goto err_bad_prog; + + if ((vers >= progp->pg_prog) || + (((versp = progp->pg_vers[vers])) == NULL)) + goto err_bad_vers; + + procp = versp->vs_proc + proc; + + if (proc >= versp->vs_nproc || !procp->pc_func) + goto err_bad_proc; + + /* + * what about the COUNTING of the service calls? + */ + + /* + * process the request + */ + if (versp->vs_dispatch == 0){ + struct svc_rqst rqst[1]; + struct svc_serv serv[1]; + int auth_res; + + if (bc_clnt2svc(rqst, req, procp, serv) == NULL) { + /* XXX kmalloc failure */ + goto err_system_error; + } + rqst->rq_prog = prog; + rqst->rq_vers = vers; + rqst->rq_proc = 0; /* CB_NULL LIE! for authentication */ + + auth_res = svc_authenticate(rqst, &auth_stat); + dprintk("xprt_backchannel_process: auth_res %d\n", auth_res); + switch (auth_res) { + case SVC_OK: + break; + case SVC_GARBAGE: + goto err_garbage; + break; + case SVC_SYSERR: + goto err_system_error; + break; + case SVC_DENIED: + goto err_bad_auth; + break; + case SVC_DROP: /* ??? */ + break; + case SVC_COMPLETE: + break; + } + + statp = rqst->rq_res.head[0].iov_base + + rqst->rq_res.head[0].iov_len; + rqst->rq_res.head[0].iov_len += 4; + *statp = rpc_success; /* hold space for final error */ + + xdr = procp->pc_decode; + if (xdr && !xdr(rqst, argv->iov_base, rqst->rq_argp)) + goto err_garbage; + + *statp = procp->pc_func(rqst, rqst->rq_argp, rqst->rq_resp); + if (*statp == rpc_success && (xdr = procp->pc_encode) + && !xdr(rqst, resv->iov_base+rqst->rq_res.len, rqst->rq_resp)) { + dprintk("svc: failed to encode reply\n"); + *statp = rpc_system_err; + } else { + svc_authorise(rqst); /* clean up */ + bc_svc2clnt(req, rqst); + } + } else { + /* huh? what the dispatcher? */ + } + + if (*statp != rpc_success) + resv->iov_len = ((void*)statp) - resv->iov_base + 4; + + return *statp; + +err_bad_dir: + /* don't know how it would ever get here... */ + /* fall through to ... */ +err_system_error: + rpc_stat = rpc_system_err; + bc_putu32(resv, rpc_stat); + return 0; + +err_bad_rpc: + bc_putu32(resv, xdr_one); /* REJECT */ + bc_putu32(resv, xdr_zero); /* RPC_MISMATCH */ + bc_putu32(resv, xdr_two); /* Only RPCv2 supported */ + bc_putu32(resv, xdr_two); + return 0; + +err_bad_auth: + dprintk("bc: authentication failed (%d)\n", ntohl(auth_stat)); + resv->iov_len -= 4; /* ??? */ + svc_putu32(resv, xdr_one); /* REJECT */ + svc_putu32(resv, xdr_one); /* AUTH_ERROR */ + svc_putu32(resv, auth_stat); /* status */ + return auth_stat; + +err_bad_prog: + rpc_stat = rpc_prog_unavail; + bc_putu32(resv, rpc_stat); + return rpc_stat; + +err_bad_vers: + rpc_stat = rpc_prog_mismatch; + svc_putu32(resv, rpc_stat); + svc_putu32(resv, htonl(progp->pg_lovers)); + svc_putu32(resv, htonl(progp->pg_hivers)); + return rpc_stat; + +err_bad_proc: + rpc_stat = rpc_proc_unavail; + bc_putu32(resv, rpc_stat); + return rpc_stat; + +err_garbage: + rpc_stat = rpc_garbage_args; + bc_putu32(resv, rpc_stat); + return rpc_stat; +} + +struct backchannel_threadargs { + struct rpc_xprt *xprt; + struct completion started; +}; +/* + * + */ + +static int xprt_backchannel_schedule(void *data) +{ + struct backchannel_threadargs *bc_args = (struct backchannel_threadargs *)data; + struct rpc_xprt *xprt = bc_args->xprt; + + daemonize("xprt-backchannel"); + complete(&bc_args->started); + + while(!xprt->shutdown) { + + spin_lock(&xprt->bc_lock); + while(list_empty(&xprt->bc_queue)) { + /* sleep */ + DECLARE_WAITQUEUE(wq, current); + add_wait_queue_exclusive(&xprt->bc_wq, &wq); + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&xprt->bc_lock); + schedule(); + set_current_state(TASK_RUNNING); + remove_wait_queue(&xprt->bc_wq, &wq); + } + /* ... LOCKS XXX ... */ + while (!list_empty(&xprt->bc_queue)) { + struct rpc_task *task; + struct rpc_rqst *rqst; + int status; + rqst = list_entry(xprt->bc_queue.next, struct rpc_rqst, rq_list); + task = rqst->rq_task; + BUG_ON(task->tk_rqstp != rqst); + list_del_init(&rqst->rq_list); + + BUG_ON(!list_empty(&rqst->rq_list)); + + /* + * we don't use xprt_transmit, since it + * assumes we're sending a REQUEST, and + * sets retransmission timers. we are ONLY + * concerned here with trying to acquire all + * the resources we need to queue to outgoing + * reply... + */ + status = xprt_backchannel_process(xprt, rqst); + BUG_ON(!list_empty(&rqst->rq_list)); + bc_reparent(task); + } + } + /* + * be sure all the requests are drained + */ + do_exit(0); +} + +/* + * stuff the req on the list of activities we're gonna do, + * and poke the backchannel scheduler. + */ +int +xprt_backchannel_complete(struct rpc_task *task, int copied) +{ + struct rpc_rqst *req = task->tk_rqstp; + struct rpc_xprt *xprt = req->rq_xprt; + req->rq_received = req->rq_private_buf.len = req->rq_rcv_buf.len = copied; + if (task == xprt->bc_task) { + xprt->bc_task = NULL; + } + /* signal backchannel thread */ + list_add_tail(&req->rq_list, &xprt->bc_queue); + wake_up(&xprt->bc_wq); + return 0; +} + +/* + * xprt_backchannel_task + * create a new task for backchannel processing, but only + * when there's a backchannel service registered. + */ + +struct rpc_task * +xprt_backchannel_task(struct rpc_xprt *xprt, int xid) +{ + struct rpc_task *task = NULL; + if (xprt->bc_svc) { + /* + * create a new task entry, + * return the reques within that entry... + */ + task = rpc_alloc_task_atomic(); + BUG_ON(!task); + bc_task_init(task, xprt); + if( xprt_backchennel_alloc(xprt, task) < 0) { + printk("xprt_backchannel_task: xprt_backchennel_alloc failed\n"); + rpc_default_free_task(task); + return 0; + } + task->tk_xprt = xprt; + task->tk_rqstp->rq_xid = xid; + return task; + } + return 0; +} + +/* + * Attach backchannel service. + */ + +void +xprt_attach_backchannel(struct rpc_xprt *xprt, struct svc_program *sp) +{ + struct backchannel_threadargs bc_args = { + .xprt = xprt, + }; + int status; + + dprintk("xprt_attach_backchannel: xprt %p sp %p\n", xprt, sp); + /* + * register the service... should be verify its NOT set yet? + */ + xprt->bc_svc = sp; + + /* + * + */ + INIT_LIST_HEAD(&xprt->bc_queue); + spin_lock_init(&xprt->bc_lock); + init_waitqueue_head(&xprt->bc_wq); + + /* + * create the thread which services the request... + */ + init_completion(&bc_args.started); + status = kernel_thread(xprt_backchannel_schedule, &bc_args, CLONE_KERNEL); + wait_for_completion(&bc_args.started); +} +