All the mail mirrored from lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB
@ 2009-09-27 16:50 Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 1/9] sunrpc: abstract out encoding function at rpc_clnt level Jeff Layton
                   ` (9 more replies)
  0 siblings, 10 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

This patchset is still preliminary and is just an RFC...

First, some background. When I was at Connectathon this year, Trond
mentioned an interesting idea to me. He said (paraphrasing):

"Why doesn't CIFS just use the RPC layer for transport? It's very
efficient at shoveling bits out onto the wire. You'd just need to
abstract out the XDR/RPC specific bits."

The idea has been floating around in the back of my head until recently,
when I decided to give it a go. This patchset represents a first, rough
draft at a patchset to do this. There's also a proof of concept module
that demonstrates that it works as expected.

The patchset is still very rough. Reconnect behavior is still very
"RPC-ish", for instance. There are doubtless other changes that will
need to be made before I had anything merge-worthy.

At this point, I'm just interested in feedback on the general approach.
Should I continue to pursue this or is it a non-starter for some
reason?

The next step here, obviously is to start building a fs on top of it.
I'd particularly be interested in using this as a foundation of a
new smb2fs.

I've also got this patchset up on my public git tree:

    http://git.samba.org/?p=jlayton/cifs.git;a=summary

Here are some questions I anticipate on this, and their answers:

------------------------------------------------------------------------
Q: Are you insane? Why would you attempt to do this?

A: Maybe...but in the last couple of years, I've spent a substantial
amount of time working on the CIFS code. Much of that time has been
spent fixing bugs. Many of those bugs exist in the low-level transport
code which has been hacked on, kludged around and hand tweaked to
where it is today. Unfortunately that's made it a very hard to work
on mess. This drives away potential developers.

CIFS in particular is also designed around synchronous ops, which
seriously limits throughput. Retrofitting it for asynchronous operation
will be adding even more kludges. The sunrpc code is already
fundamentally asynchronous.
------------------------------------------------------------------------
Q: Okay, what's the benefit of hooking it up to sunrpc rather than
building a new transport layer (or fixing the transport in the other two
filesystems)?

A: Using sunrpc allows us to share a lot of the rpc scheduler code with
sunrpc. At a high level, NFS/RPC and SMB aren't really very different.
Sure, they have different formats, and one is big endian on the wire and
the other isn't...still there are significant similarities.

We also get access to the great upcall mechanisms that sunrpc has, and
the possibility to share code like the gssapi upcalls. The sunrpc layer
has a credential and authentication management framework that we can
build on to make a truly multiuser CIFS/SMB filesystem.

I've heard it claimed before that Linux's sunrpc layer is
over-engineered, but in this case that works in our favor...
------------------------------------------------------------------------
Q: can we hook up cifs or smbfs to use this as a transport?

A: Not trivially. CIFS in particular is not designed with each call
having discrete encode and decode functions. They're sort of mashed
together. smbfs might be possible...I'm a little less familiar with it,
but it does have a transport layer that more closely resembles the
sunrpc one. Still though, it'd take significant work to make that
happen. I'm not opposed to the idea however.

In the end though, I think we'll probably need to design something new
to sit on top of this. We will probably be able to borrow code and
concepts from the other filesystems however.
------------------------------------------------------------------------
Q: could we use this as a transport layer for a smb2fs ?

A: Yes, I think so. This particular prototype is build around SMB1, but
SMB2 could be supported with only minor modifications. One of the
reasons for sending this patchset now before I've built a filesystem on
top of it is because I know that SMB2 work is in progress. I'd like to
see it based around a more asynchronous transport model, or at least
built with cleaner layering so that we can eventually bolt on a different
transport layer if we so choose.

Jeff Layton (9):
  sunrpc: abstract out encoding function at rpc_clnt level
  sunrpc: move check for too small reply size into rpc_verify_header
  sunrpc: abstract our call decoding routine
  sunrpc: move rpc_xdr_buf_init to clnt.h
  sunrpc: make call_bind non-static
  sunrpc: add new SMB transport class for sunrpc
  sunrpc: add encoding and decoding routines for SMB
  sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB
  smbtest: simple module for testing SMB/RPC code

 fs/Makefile                    |    2 +
 fs/lockd/host.c                |    4 +
 fs/lockd/mon.c                 |    4 +
 fs/nfs/client.c                |    4 +
 fs/nfs/mount_clnt.c            |    4 +
 fs/nfsd/nfs4callback.c         |    4 +
 fs/smbtest/Makefile            |    1 +
 fs/smbtest/smbtest.c           |  204 +++++
 include/linux/sunrpc/clnt.h    |   24 +-
 include/linux/sunrpc/smb.h     |   42 +
 include/linux/sunrpc/xprtsmb.h |   59 ++
 net/sunrpc/Kconfig             |   11 +
 net/sunrpc/Makefile            |    1 +
 net/sunrpc/clnt.c              |   98 ++-
 net/sunrpc/rpcb_clnt.c         |    8 +
 net/sunrpc/smb.c               |  120 +++
 net/sunrpc/sunrpc_syms.c       |    3 +
 net/sunrpc/xprtsmb.c           | 1723 ++++++++++++++++++++++++++++++++++++++++
 18 files changed, 2272 insertions(+), 44 deletions(-)
 create mode 100644 fs/smbtest/Makefile
 create mode 100644 fs/smbtest/smbtest.c
 create mode 100644 include/linux/sunrpc/smb.h
 create mode 100644 include/linux/sunrpc/xprtsmb.h
 create mode 100644 net/sunrpc/smb.c
 create mode 100644 net/sunrpc/xprtsmb.c


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC PATCH 1/9] sunrpc: abstract out encoding function at rpc_clnt level
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 2/9] sunrpc: move check for too small reply size into rpc_verify_header Jeff Layton
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

In order to add the ability to do an SMB call with the sunrpc layer, we
need to abstract out the call encoding. Add a function pointer that hangs
off of the rpc_clnt to do the encoding. For now, all of the existing
callers will simply set it to rpc_xdr_encode.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/lockd/host.c             |    3 +++
 fs/lockd/mon.c              |    3 +++
 fs/nfs/client.c             |    3 +++
 fs/nfs/mount_clnt.c         |    3 +++
 fs/nfsd/nfs4callback.c      |    3 +++
 include/linux/sunrpc/clnt.h |    7 +++++++
 net/sunrpc/clnt.c           |   14 ++++++++++----
 net/sunrpc/rpcb_clnt.c      |    6 ++++++
 8 files changed, 38 insertions(+), 4 deletions(-)

diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index 4600c20..b7189ce 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -362,6 +362,9 @@ nlm_bind_host(struct nlm_host *host)
 			.program	= &nlm_program,
 			.version	= host->h_version,
 			.authflavor	= RPC_AUTH_UNIX,
+			.encode		= rpc_xdr_encode,
+			.callhdr_size	= RPC_CALLHDRSIZE,
+			.replhdr_size	= RPC_REPHDRSIZE,
 			.flags		= (RPC_CLNT_CREATE_NOPING |
 					   RPC_CLNT_CREATE_AUTOBIND),
 		};
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index f956651..ea24301 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -75,6 +75,9 @@ static struct rpc_clnt *nsm_create(void)
 		.program		= &nsm_program,
 		.version		= NSM_VERSION,
 		.authflavor		= RPC_AUTH_NULL,
+		.encode			= rpc_xdr_encode,
+		.callhdr_size		= RPC_CALLHDRSIZE,
+		.replhdr_size		= RPC_REPHDRSIZE,
 		.flags			= RPC_CLNT_CREATE_NOPING,
 	};
 
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 63976c0..5505ddf 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -606,6 +606,9 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
 		.servername	= clp->cl_hostname,
 		.program	= &nfs_program,
 		.version	= clp->rpc_ops->version,
+		.encode		= rpc_xdr_encode,
+		.callhdr_size	= RPC_CALLHDRSIZE,
+		.replhdr_size	= RPC_REPHDRSIZE,
 		.authflavor	= flavor,
 	};
 
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 0adefc4..14f79e5 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -159,6 +159,9 @@ int nfs_mount(struct nfs_mount_request *info)
 		.servername	= info->hostname,
 		.program	= &mnt_program,
 		.version	= info->version,
+		.encode		= rpc_xdr_encode,
+		.callhdr_size	= RPC_CALLHDRSIZE,
+		.replhdr_size	= RPC_REPHDRSIZE,
 		.authflavor	= RPC_AUTH_UNIX,
 	};
 	struct rpc_clnt		*mnt_clnt;
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index 24e8d78..dbfd91c 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -494,6 +494,9 @@ int setup_callback_client(struct nfs4_client *clp)
 		.authflavor	= clp->cl_flavor,
 		.flags		= (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
 		.client_name    = clp->cl_principal,
+		.encode		= rpc_xdr_encode,
+		.callhdr_size	= RPC_CALLHDRSIZE,
+		.replhdr_size	= RPC_REPHDRSIZE,
 	};
 	struct rpc_clnt *client;
 
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 8ed9642..6209c39 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -63,6 +63,9 @@ struct rpc_clnt {
 	struct rpc_program *	cl_program;
 	char			cl_inline_name[32];
 	char			*cl_principal;	/* target to authenticate to */
+	void			(*cl_encode) (struct rpc_task *task);
+	size_t			cl_callhdr_sz; /* in quadwords */
+	size_t			cl_replhdr_sz; /* in quadwords */
 };
 
 /*
@@ -115,6 +118,9 @@ struct rpc_create_args {
 	unsigned long		flags;
 	char			*client_name;
 	struct svc_xprt		*bc_xprt;	/* NFSv4.1 backchannel */
+	size_t			callhdr_size;	/* in quadwords */
+	size_t			replhdr_size;	/* in quadwords */
+	void			(*encode) (struct rpc_task *task);
 };
 
 /* Values for "flags" field */
@@ -150,6 +156,7 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
 			       int flags);
 void		rpc_restart_call_prepare(struct rpc_task *);
 void		rpc_restart_call(struct rpc_task *);
+void		rpc_xdr_encode(struct rpc_task *);
 void		rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
 size_t		rpc_max_payload(struct rpc_clnt *);
 void		rpc_force_rebind(struct rpc_clnt *);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 38829e2..f675416 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -200,6 +200,10 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 	clnt->cl_vers     = version->number;
 	clnt->cl_stats    = program->stats;
 	clnt->cl_metrics  = rpc_alloc_iostats(clnt);
+	clnt->cl_encode   = args->encode;
+	clnt->cl_callhdr_sz	= args->callhdr_size;
+	clnt->cl_replhdr_sz	= args->replhdr_size;
+
 	err = -ENOMEM;
 	if (clnt->cl_metrics == NULL)
 		goto out_no_stats;
@@ -905,6 +909,7 @@ call_allocate(struct rpc_task *task)
 	unsigned int slack = task->tk_msg.rpc_cred->cr_auth->au_cslack;
 	struct rpc_rqst *req = task->tk_rqstp;
 	struct rpc_xprt *xprt = task->tk_xprt;
+	struct rpc_clnt *clnt = task->tk_client;
 	struct rpc_procinfo *proc = task->tk_msg.rpc_proc;
 
 	dprint_status(task);
@@ -926,9 +931,9 @@ call_allocate(struct rpc_task *task)
 	 * and reply headers, and convert both values
 	 * to byte sizes.
 	 */
-	req->rq_callsize = RPC_CALLHDRSIZE + (slack << 1) + proc->p_arglen;
+	req->rq_callsize = clnt->cl_callhdr_sz + (slack << 1) + proc->p_arglen;
 	req->rq_callsize <<= 2;
-	req->rq_rcvsize = RPC_REPHDRSIZE + slack + proc->p_replen;
+	req->rq_rcvsize = clnt->cl_replhdr_sz + slack + proc->p_replen;
 	req->rq_rcvsize <<= 2;
 
 	req->rq_buffer = xprt->ops->buf_alloc(task,
@@ -975,7 +980,7 @@ rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
 /*
  * 3.	Encode arguments of an RPC call
  */
-static void
+void
 rpc_xdr_encode(struct rpc_task *task)
 {
 	struct rpc_rqst	*req = task->tk_rqstp;
@@ -1005,6 +1010,7 @@ rpc_xdr_encode(struct rpc_task *task)
 	task->tk_status = rpcauth_wrap_req(task, encode, req, p,
 			task->tk_msg.rpc_argp);
 }
+EXPORT_SYMBOL_GPL(rpc_xdr_encode);
 
 /*
  * 4.	Get the server port number if not yet set
@@ -1148,7 +1154,7 @@ call_transmit(struct rpc_task *task)
 	/* Encode here so that rpcsec_gss can use correct sequence number. */
 	if (rpc_task_need_encode(task)) {
 		BUG_ON(task->tk_rqstp->rq_bytes_sent != 0);
-		rpc_xdr_encode(task);
+		task->tk_client->cl_encode(task);
 		/* Did the encode result in an error condition? */
 		if (task->tk_status != 0) {
 			/* Was the error nonfatal? */
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 830faf4..65764ba 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -174,6 +174,9 @@ static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
 		.program	= &rpcb_program,
 		.version	= version,
 		.authflavor	= RPC_AUTH_UNIX,
+		.encode		= rpc_xdr_encode,
+		.callhdr_size	= RPC_CALLHDRSIZE,
+		.replhdr_size	= RPC_REPHDRSIZE,
 		.flags		= RPC_CLNT_CREATE_NOPING,
 	};
 
@@ -191,6 +194,9 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
 		.program	= &rpcb_program,
 		.version	= version,
 		.authflavor	= RPC_AUTH_UNIX,
+		.encode		= rpc_xdr_encode,
+		.callhdr_size	= RPC_CALLHDRSIZE,
+		.replhdr_size	= RPC_REPHDRSIZE,
 		.flags		= (RPC_CLNT_CREATE_NOPING |
 					RPC_CLNT_CREATE_NONPRIVPORT),
 	};
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 2/9] sunrpc: move check for too small reply size into rpc_verify_header
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 1/9] sunrpc: abstract out encoding function at rpc_clnt level Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 3/9] sunrpc: abstract our call decoding routine Jeff Layton
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

...this should introduce no behavioral changes.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 net/sunrpc/clnt.c |   24 ++++++++++++------------
 1 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index f675416..e504b59 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -1411,18 +1411,6 @@ call_decode(struct rpc_task *task)
 	WARN_ON(memcmp(&req->rq_rcv_buf, &req->rq_private_buf,
 				sizeof(req->rq_rcv_buf)) != 0);
 
-	if (req->rq_rcv_buf.len < 12) {
-		if (!RPC_IS_SOFT(task)) {
-			task->tk_action = call_bind;
-			clnt->cl_stats->rpcretrans++;
-			goto out_retry;
-		}
-		dprintk("RPC:       %s: too small RPC reply size (%d bytes)\n",
-				clnt->cl_protname, task->tk_status);
-		task->tk_action = call_timeout;
-		goto out_retry;
-	}
-
 	p = rpc_verify_header(task);
 	if (IS_ERR(p)) {
 		if (p == ERR_PTR(-EAGAIN))
@@ -1518,6 +1506,18 @@ rpc_verify_header(struct rpc_task *task)
 	u32 n;
 	int error = -EACCES;
 
+	if (task->tk_rqstp->rq_rcv_buf.len < 12) {
+		if (!RPC_IS_SOFT(task)) {
+			task->tk_action = call_bind;
+			task->tk_client->cl_stats->rpcretrans++;
+			goto out_retry;
+		}
+		dprintk("RPC:       %s: too small RPC reply size (%d bytes)\n",
+			task->tk_client->cl_protname, task->tk_status);
+		task->tk_action = call_timeout;
+		goto out_retry;
+	}
+
 	if ((task->tk_rqstp->rq_rcv_buf.len & 3) != 0) {
 		/* RFC-1014 says that the representation of XDR data must be a
 		 * multiple of four bytes
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 3/9] sunrpc: abstract our call decoding routine
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 1/9] sunrpc: abstract out encoding function at rpc_clnt level Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 2/9] sunrpc: move check for too small reply size into rpc_verify_header Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 4/9] sunrpc: move rpc_xdr_buf_init to clnt.h Jeff Layton
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

...all of the existing rpc users will use the new rpc_xdr_decode function.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/lockd/host.c             |    1 +
 fs/lockd/mon.c              |    1 +
 fs/nfs/client.c             |    1 +
 fs/nfs/mount_clnt.c         |    1 +
 fs/nfsd/nfs4callback.c      |    1 +
 include/linux/sunrpc/clnt.h |    3 ++
 net/sunrpc/clnt.c           |   46 +++++++++++++++++++++++++++++-------------
 net/sunrpc/rpcb_clnt.c      |    2 +
 8 files changed, 42 insertions(+), 14 deletions(-)

diff --git a/fs/lockd/host.c b/fs/lockd/host.c
index b7189ce..2159ee2 100644
--- a/fs/lockd/host.c
+++ b/fs/lockd/host.c
@@ -363,6 +363,7 @@ nlm_bind_host(struct nlm_host *host)
 			.version	= host->h_version,
 			.authflavor	= RPC_AUTH_UNIX,
 			.encode		= rpc_xdr_encode,
+			.decode		= rpc_xdr_decode,
 			.callhdr_size	= RPC_CALLHDRSIZE,
 			.replhdr_size	= RPC_REPHDRSIZE,
 			.flags		= (RPC_CLNT_CREATE_NOPING |
diff --git a/fs/lockd/mon.c b/fs/lockd/mon.c
index ea24301..bb4e1ad 100644
--- a/fs/lockd/mon.c
+++ b/fs/lockd/mon.c
@@ -76,6 +76,7 @@ static struct rpc_clnt *nsm_create(void)
 		.version		= NSM_VERSION,
 		.authflavor		= RPC_AUTH_NULL,
 		.encode			= rpc_xdr_encode,
+		.decode			= rpc_xdr_decode,
 		.callhdr_size		= RPC_CALLHDRSIZE,
 		.replhdr_size		= RPC_REPHDRSIZE,
 		.flags			= RPC_CLNT_CREATE_NOPING,
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 5505ddf..5ec2a45 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -607,6 +607,7 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
 		.program	= &nfs_program,
 		.version	= clp->rpc_ops->version,
 		.encode		= rpc_xdr_encode,
+		.decode		= rpc_xdr_decode,
 		.callhdr_size	= RPC_CALLHDRSIZE,
 		.replhdr_size	= RPC_REPHDRSIZE,
 		.authflavor	= flavor,
diff --git a/fs/nfs/mount_clnt.c b/fs/nfs/mount_clnt.c
index 14f79e5..86a3b9e 100644
--- a/fs/nfs/mount_clnt.c
+++ b/fs/nfs/mount_clnt.c
@@ -160,6 +160,7 @@ int nfs_mount(struct nfs_mount_request *info)
 		.program	= &mnt_program,
 		.version	= info->version,
 		.encode		= rpc_xdr_encode,
+		.decode		= rpc_xdr_decode,
 		.callhdr_size	= RPC_CALLHDRSIZE,
 		.replhdr_size	= RPC_REPHDRSIZE,
 		.authflavor	= RPC_AUTH_UNIX,
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c
index dbfd91c..bb655a2 100644
--- a/fs/nfsd/nfs4callback.c
+++ b/fs/nfsd/nfs4callback.c
@@ -495,6 +495,7 @@ int setup_callback_client(struct nfs4_client *clp)
 		.flags		= (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
 		.client_name    = clp->cl_principal,
 		.encode		= rpc_xdr_encode,
+		.decode		= rpc_xdr_decode,
 		.callhdr_size	= RPC_CALLHDRSIZE,
 		.replhdr_size	= RPC_REPHDRSIZE,
 	};
diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 6209c39..00afedc 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -64,6 +64,7 @@ struct rpc_clnt {
 	char			cl_inline_name[32];
 	char			*cl_principal;	/* target to authenticate to */
 	void			(*cl_encode) (struct rpc_task *task);
+	int			(*cl_decode) (struct rpc_task *task);
 	size_t			cl_callhdr_sz; /* in quadwords */
 	size_t			cl_replhdr_sz; /* in quadwords */
 };
@@ -121,6 +122,7 @@ struct rpc_create_args {
 	size_t			callhdr_size;	/* in quadwords */
 	size_t			replhdr_size;	/* in quadwords */
 	void			(*encode) (struct rpc_task *task);
+	int			(*decode) (struct rpc_task *task);
 };
 
 /* Values for "flags" field */
@@ -157,6 +159,7 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred,
 void		rpc_restart_call_prepare(struct rpc_task *);
 void		rpc_restart_call(struct rpc_task *);
 void		rpc_xdr_encode(struct rpc_task *);
+int		rpc_xdr_decode(struct rpc_task *);
 void		rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int);
 size_t		rpc_max_payload(struct rpc_clnt *);
 void		rpc_force_rebind(struct rpc_clnt *);
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index e504b59..f05d289 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -201,6 +201,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 	clnt->cl_stats    = program->stats;
 	clnt->cl_metrics  = rpc_alloc_iostats(clnt);
 	clnt->cl_encode   = args->encode;
+	clnt->cl_decode   = args->decode;
 	clnt->cl_callhdr_sz	= args->callhdr_size;
 	clnt->cl_replhdr_sz	= args->replhdr_size;
 
@@ -1379,6 +1380,27 @@ retry:
 	task->tk_status = 0;
 }
 
+int
+rpc_xdr_decode(struct rpc_task *task)
+{
+	struct rpc_rqst	*req = task->tk_rqstp;
+	kxdrproc_t	decode = task->tk_msg.rpc_proc->p_decode;
+	__be32		*p;
+
+	p = rpc_verify_header(task);
+	if (IS_ERR(p))
+		return PTR_ERR(p);
+
+	if (decode)
+		task->tk_status = rpcauth_unwrap_resp(task, decode, req, p,
+						      task->tk_msg.rpc_resp);
+
+	dprintk("RPC: %5u %s result %d\n", task->tk_pid, __func__,
+			task->tk_status);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(rpc_xdr_decode);
+
 /*
  * 7.	Decode the RPC reply
  */
@@ -1387,8 +1409,7 @@ call_decode(struct rpc_task *task)
 {
 	struct rpc_clnt	*clnt = task->tk_client;
 	struct rpc_rqst	*req = task->tk_rqstp;
-	kxdrproc_t	decode = task->tk_msg.rpc_proc->p_decode;
-	__be32		*p;
+	int status;
 
 	dprintk("RPC: %5u call_decode (status %d)\n",
 			task->tk_pid, task->tk_status);
@@ -1411,22 +1432,19 @@ call_decode(struct rpc_task *task)
 	WARN_ON(memcmp(&req->rq_rcv_buf, &req->rq_private_buf,
 				sizeof(req->rq_rcv_buf)) != 0);
 
-	p = rpc_verify_header(task);
-	if (IS_ERR(p)) {
-		if (p == ERR_PTR(-EAGAIN))
-			goto out_retry;
+	if (!clnt->cl_decode)
+		BUG();
+
+	status = clnt->cl_decode(task);
+
+	if (status == -EAGAIN)
+		goto out_retry;
+	else if (status)
 		return;
-	}
 
 	task->tk_action = rpc_exit_task;
-
-	if (decode) {
-		task->tk_status = rpcauth_unwrap_resp(task, decode, req, p,
-						      task->tk_msg.rpc_resp);
-	}
-	dprintk("RPC: %5u call_decode result %d\n", task->tk_pid,
-			task->tk_status);
 	return;
+
 out_retry:
 	task->tk_status = 0;
 	/* Note: rpc_verify_header() may have freed the RPC slot */
diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c
index 65764ba..db053bf 100644
--- a/net/sunrpc/rpcb_clnt.c
+++ b/net/sunrpc/rpcb_clnt.c
@@ -175,6 +175,7 @@ static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
 		.version	= version,
 		.authflavor	= RPC_AUTH_UNIX,
 		.encode		= rpc_xdr_encode,
+		.decode		= rpc_xdr_decode,
 		.callhdr_size	= RPC_CALLHDRSIZE,
 		.replhdr_size	= RPC_REPHDRSIZE,
 		.flags		= RPC_CLNT_CREATE_NOPING,
@@ -195,6 +196,7 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
 		.version	= version,
 		.authflavor	= RPC_AUTH_UNIX,
 		.encode		= rpc_xdr_encode,
+		.decode		= rpc_xdr_decode,
 		.callhdr_size	= RPC_CALLHDRSIZE,
 		.replhdr_size	= RPC_REPHDRSIZE,
 		.flags		= (RPC_CLNT_CREATE_NOPING |
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 4/9] sunrpc: move rpc_xdr_buf_init to clnt.h
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (2 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 3/9] sunrpc: abstract our call decoding routine Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 5/9] sunrpc: make call_bind non-static Jeff Layton
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

...so that it can be used elsewhere.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/sunrpc/clnt.h |   13 ++++++++++++-
 net/sunrpc/clnt.c           |   12 ------------
 2 files changed, 12 insertions(+), 13 deletions(-)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 00afedc..307a3ec 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -165,7 +165,6 @@ size_t		rpc_max_payload(struct rpc_clnt *);
 void		rpc_force_rebind(struct rpc_clnt *);
 size_t		rpc_peeraddr(struct rpc_clnt *, struct sockaddr *, size_t);
 const char	*rpc_peeraddr2str(struct rpc_clnt *, enum rpc_display_format_t);
-
 size_t		rpc_ntop(const struct sockaddr *, char *, const size_t);
 size_t		rpc_pton(const char *, const size_t,
 			 struct sockaddr *, const size_t);
@@ -197,6 +196,18 @@ static inline void rpc_set_port(struct sockaddr *sap,
 	}
 }
 
+static inline void
+rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
+{
+	buf->head[0].iov_base = start;
+	buf->head[0].iov_len = len;
+	buf->tail[0].iov_len = 0;
+	buf->page_len = 0;
+	buf->flags = 0;
+	buf->len = 0;
+	buf->buflen = len;
+}
+
 #define IPV6_SCOPE_DELIMITER		'%'
 #define IPV6_SCOPE_ID_LEN		sizeof("%nnnnnnnnnn")
 
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index f05d289..42688af 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -966,18 +966,6 @@ rpc_task_force_reencode(struct rpc_task *task)
 	task->tk_rqstp->rq_bytes_sent = 0;
 }
 
-static inline void
-rpc_xdr_buf_init(struct xdr_buf *buf, void *start, size_t len)
-{
-	buf->head[0].iov_base = start;
-	buf->head[0].iov_len = len;
-	buf->tail[0].iov_len = 0;
-	buf->page_len = 0;
-	buf->flags = 0;
-	buf->len = 0;
-	buf->buflen = len;
-}
-
 /*
  * 3.	Encode arguments of an RPC call
  */
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 5/9] sunrpc: make call_bind non-static
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (3 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 4/9] sunrpc: move rpc_xdr_buf_init to clnt.h Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 6/9] sunrpc: add new SMB transport class for sunrpc Jeff Layton
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

...is there a better way to do this?

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/sunrpc/clnt.h |    1 +
 net/sunrpc/clnt.c           |    4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/include/linux/sunrpc/clnt.h b/include/linux/sunrpc/clnt.h
index 307a3ec..92ff85d 100644
--- a/include/linux/sunrpc/clnt.h
+++ b/include/linux/sunrpc/clnt.h
@@ -171,6 +171,7 @@ size_t		rpc_pton(const char *, const size_t,
 char *		rpc_sockaddr2uaddr(const struct sockaddr *);
 size_t		rpc_uaddr2sockaddr(const char *, const size_t,
 				   struct sockaddr *, const size_t);
+void		call_bind(struct rpc_task *task);
 
 static inline unsigned short rpc_get_port(const struct sockaddr *sap)
 {
diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c
index 42688af..f2b0815 100644
--- a/net/sunrpc/clnt.c
+++ b/net/sunrpc/clnt.c
@@ -63,7 +63,6 @@ static void	call_reserve(struct rpc_task *task);
 static void	call_reserveresult(struct rpc_task *task);
 static void	call_allocate(struct rpc_task *task);
 static void	call_decode(struct rpc_task *task);
-static void	call_bind(struct rpc_task *task);
 static void	call_bind_status(struct rpc_task *task);
 static void	call_transmit(struct rpc_task *task);
 #if defined(CONFIG_NFS_V4_1)
@@ -1004,7 +1003,7 @@ EXPORT_SYMBOL_GPL(rpc_xdr_encode);
 /*
  * 4.	Get the server port number if not yet set
  */
-static void
+void
 call_bind(struct rpc_task *task)
 {
 	struct rpc_xprt *xprt = task->tk_xprt;
@@ -1018,6 +1017,7 @@ call_bind(struct rpc_task *task)
 		xprt->ops->rpcbind(task);
 	}
 }
+EXPORT_SYMBOL_GPL(call_bind);
 
 /*
  * 4a.	Sort out bind result
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 6/9] sunrpc: add new SMB transport class for sunrpc
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (4 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 5/9] sunrpc: make call_bind non-static Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 7/9] sunrpc: add encoding and decoding routines for SMB Jeff Layton
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

Add a new transport class for SMB traffic that lives in xprtsmb.c and
a corresponding header file for making use of it.

With this, it's possible to use the sunrpc layer to send and receive
SMB traffic.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/sunrpc/xprtsmb.h |   59 ++
 net/sunrpc/xprtsmb.c           | 1720 ++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1779 insertions(+), 0 deletions(-)
 create mode 100644 include/linux/sunrpc/xprtsmb.h
 create mode 100644 net/sunrpc/xprtsmb.c

diff --git a/include/linux/sunrpc/xprtsmb.h b/include/linux/sunrpc/xprtsmb.h
new file mode 100644
index 0000000..d55e85b
--- /dev/null
+++ b/include/linux/sunrpc/xprtsmb.h
@@ -0,0 +1,59 @@
+/*
+ *   net/sunrpc/xprtsmb.h -- SMB transport for sunrpc
+ *
+ *   Copyright (c) 2009 Red Hat, Inc.
+ *   Author(s): Jeff Layton (jlayton@redhat.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * xprtsmb.h
+ *
+ * This file contains the public interfaces for the SMB transport for
+ * the sunrpc layer.
+ */
+
+#ifndef _LINUX_SUNRPC_XPRTSMB_H
+#define _LINUX_SUNRPC_XPRTSMB_H
+
+/*
+ * RPC transport identifier for SMB
+ */
+#define XPRT_TRANSPORT_SMB	1024
+
+int init_smb_xprt(void);
+void cleanup_smb_xprt(void);
+
+/* standard SMB header */
+struct smb_header {
+	__u8		protocol[4];
+	__u8		command;
+	__le32		status;
+	__u8		flags;
+	__le16		flags2;
+	__le16		pidhigh;
+	__u8		signature[8];
+	__u8		pad[2];
+	__le16		tid;
+	__le16		pid;
+	__le16		uid;
+	__le16		mid;
+} __attribute__((packed));
+
+/* SMB Header Flags of interest */
+#define SMBFLG_RESPONSE 0x80	/* response from server */
+
+#endif /* _LINUX_SUNRPC_XPRTSMB_H */
diff --git a/net/sunrpc/xprtsmb.c b/net/sunrpc/xprtsmb.c
new file mode 100644
index 0000000..585cf37
--- /dev/null
+++ b/net/sunrpc/xprtsmb.c
@@ -0,0 +1,1720 @@
+/*
+ * linux/net/sunrpc/xprtsmb.c
+ *
+ * Client-side transport implementation for SMB sockets.
+ *
+ * (C) 1998 Red Hat
+ * Initial revision by Jeff Layton <jlayton@redhat.com>
+ *
+ * Heavily based on net/sunrpc/xprtsock.c -- copyrights from that file below:
+ *
+ * TCP callback races fixes (C) 1998 Red Hat
+ * TCP send fixes (C) 1998 Red Hat
+ * TCP NFS related read + write fixes
+ *  (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied-cv59FeDIM0c@public.gmane.org>
+ *
+ * Rewrite of larges part of the code in order to stabilize TCP stuff.
+ * Fix behaviour when socket buffer is full.
+ *  (C) 1999 Trond Myklebust <trond.myklebust@fys.uio.no>
+ *
+ * IP socket transport implementation, (C) 2005 Chuck Lever <cel@netapp.com>
+ *
+ * IPv6 support contributed by Gilles Quillard, Bull Open Source, 2005.
+ *   <gilles.quillard@bull.net>
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/capability.h>
+#include <linux/pagemap.h>
+#include <linux/errno.h>
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/mm.h>
+#include <linux/tcp.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/xprtsmb.h>
+#include <linux/sunrpc/xprtsock.h>
+#include <linux/file.h>
+#ifdef CONFIG_NFS_V4_1
+#include <linux/sunrpc/bc_xprt.h>
+#endif
+
+#include <net/sock.h>
+#include <net/checksum.h>
+#include <net/tcp.h>
+
+/* CIFS currently defaults to allowing 50 outstanding calls at a time */
+#define SMB_DEF_SLOT_TABLE	50
+
+/*
+ * top byte of the 32-bit SMB "fragment header" is always 0. That gives a max
+ * size for a SMB packet of 2^24-1
+ */
+#define	SMB_FRAGMENT_SIZE_MASK	0x00ffffff
+#define	SMB_MAX_FRAGMENT_SIZE	SMB_FRAGMENT_SIZE_MASK
+
+/*
+ * xprtsock tunables
+ */
+unsigned int xprt_smb_slot_table_entries = SMB_DEF_SLOT_TABLE;
+
+#define XS_TCP_LINGER_TO	(15U * HZ)
+static unsigned int xs_smb_fin_timeout __read_mostly = XS_TCP_LINGER_TO;
+
+/*
+ * We can register our own files under /proc/sys/sunrpc by
+ * calling register_sysctl_table() again.  The files in that
+ * directory become the union of all files registered there.
+ *
+ * We simply need to make sure that we don't collide with
+ * someone else's file names!
+ */
+
+#ifdef RPC_DEBUG
+
+static unsigned int min_slot_table_size = RPC_MIN_SLOT_TABLE;
+static unsigned int max_slot_table_size = RPC_MAX_SLOT_TABLE;
+
+static struct ctl_table_header *sunrpc_table_header;
+
+/*
+ * FIXME: changing the UDP slot table size should also resize the UDP
+ *        socket buffers for existing UDP transports
+ */
+static ctl_table xs_tunables_table[] = {
+	{
+		.ctl_name	= CTL_UNNUMBERED,
+		.procname	= "smb_slot_table_entries",
+		.data		= &xprt_smb_slot_table_entries,
+		.maxlen		= sizeof(unsigned int),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_minmax,
+		.strategy	= &sysctl_intvec,
+		.extra1		= &min_slot_table_size,
+		.extra2		= &max_slot_table_size
+	},
+	{
+		.ctl_name	= CTL_UNNUMBERED,
+		.procname	= "smb_fin_timeout",
+		.data		= &xs_smb_fin_timeout,
+		.maxlen		= sizeof(xs_smb_fin_timeout),
+		.mode		= 0644,
+		.proc_handler	= &proc_dointvec_jiffies,
+		.strategy	= sysctl_jiffies
+	},
+	{
+		.ctl_name = 0,
+	},
+};
+
+static ctl_table sunrpc_table[] = {
+	{
+		.ctl_name	= CTL_SUNRPC,
+		.procname	= "sunrpc",
+		.mode		= 0555,
+		.child		= xs_tunables_table
+	},
+	{
+		.ctl_name = 0,
+	},
+};
+
+#endif
+
+/*
+ * Wait duration for an RPC TCP connection to be established.  Solaris
+ * NFS over TCP uses 60 seconds, for example, which is in line with how
+ * long a server takes to reboot.
+ */
+#define XS_TCP_CONN_TO		(60U * HZ)
+
+/*
+ * Wait duration for a reply from the RPC portmapper.
+ */
+#define XS_BIND_TO		(60U * HZ)
+
+/*
+ * The reestablish timeout allows clients to delay for a bit before attempting
+ * to reconnect to a server that just dropped our connection.
+ *
+ * We implement an exponential backoff when trying to reestablish a TCP
+ * transport connection with the server.  Some servers like to drop a TCP
+ * connection when they are overworked, so we start with a short timeout and
+ * increase over time if the server is down or not responding.
+ */
+#define XS_TCP_INIT_REEST_TO	(3U * HZ)
+#define XS_TCP_MAX_REEST_TO	(5U * 60 * HZ)
+
+/*
+ * reconnecting a SMB socket is expensive, so we don't do idle timeouts if
+ * we can help it.
+ */
+#define XS_SMB_IDLE_DISC_TO     (~0UL)
+
+
+#ifdef RPC_DEBUG
+# undef  RPC_DEBUG_DATA
+# define RPCDBG_FACILITY	RPCDBG_TRANS
+#endif
+
+#ifdef RPC_DEBUG_DATA
+static void xs_pktdump(char *msg, u32 *packet, unsigned int count)
+{
+	u8 *buf = (u8 *) packet;
+	int j;
+
+	dprintk("RPC:       %s\n", msg);
+	for (j = 0; j < count && j < 128; j += 4) {
+		if (!(j & 31)) {
+			if (j)
+				dprintk("\n");
+			dprintk("0x%04x ", j);
+		}
+		dprintk("%02x%02x%02x%02x ",
+			buf[j], buf[j+1], buf[j+2], buf[j+3]);
+	}
+	dprintk("\n");
+}
+#else
+static inline void xs_pktdump(char *msg, u32 *packet, unsigned int count)
+{
+	/* NOP */
+}
+#endif
+
+struct smb_xprt {
+	struct rpc_xprt		xprt;
+
+	/*
+	 * Network layer
+	 */
+	struct socket *		sock;
+	struct sock *		inet;
+
+	/*
+	 * State of TCP reply receive
+	 */
+	__be32			tcp_fraghdr;
+	struct smb_header	smb_header;
+
+	u32			tcp_offset,
+				tcp_reclen;
+
+	unsigned long		tcp_copied,
+				tcp_flags;
+
+	/*
+	 * Connection of transports
+	 */
+	struct delayed_work	connect_worker;
+	struct sockaddr_storage	addr;
+	unsigned short		port;
+
+	/*
+	 * Saved socket callback addresses
+	 */
+	void			(*old_data_ready)(struct sock *, int);
+	void			(*old_state_change)(struct sock *);
+	void			(*old_write_space)(struct sock *);
+	void			(*old_error_report)(struct sock *);
+};
+
+/*
+ * TCP receive state flags
+ */
+#define TCP_RCV_COPY_FRAGHDR	(1UL << 0)
+#define TCP_RCV_READ_SMBHDR	(1UL << 1)
+#define TCP_RCV_COPY_SMBHDR	(1UL << 2)
+#define TCP_RCV_COPY_DATA	(1UL << 3)
+
+static inline struct sockaddr *xs_addr(struct rpc_xprt *xprt)
+{
+	return (struct sockaddr *) &xprt->addr;
+}
+
+static inline struct sockaddr_in *xs_addr_in(struct rpc_xprt *xprt)
+{
+	return (struct sockaddr_in *) &xprt->addr;
+}
+
+static inline struct sockaddr_in6 *xs_addr_in6(struct rpc_xprt *xprt)
+{
+	return (struct sockaddr_in6 *) &xprt->addr;
+}
+
+static void xs_format_common_peer_addresses(struct rpc_xprt *xprt)
+{
+	struct sockaddr *sap = xs_addr(xprt);
+	struct sockaddr_in6 *sin6;
+	struct sockaddr_in *sin;
+	char buf[128];
+
+	(void)rpc_ntop(sap, buf, sizeof(buf));
+	xprt->address_strings[RPC_DISPLAY_ADDR] = kstrdup(buf, GFP_KERNEL);
+
+	switch (sap->sa_family) {
+	case AF_INET:
+		sin = xs_addr_in(xprt);
+		(void)snprintf(buf, sizeof(buf), "%02x%02x%02x%02x",
+					NIPQUAD(sin->sin_addr.s_addr));
+		break;
+	case AF_INET6:
+		sin6 = xs_addr_in6(xprt);
+		(void)snprintf(buf, sizeof(buf), "%pi6", &sin6->sin6_addr);
+		break;
+	default:
+		BUG();
+	}
+	xprt->address_strings[RPC_DISPLAY_HEX_ADDR] = kstrdup(buf, GFP_KERNEL);
+}
+
+static void xs_format_common_peer_ports(struct rpc_xprt *xprt)
+{
+	struct sockaddr *sap = xs_addr(xprt);
+	char buf[128];
+
+	(void)snprintf(buf, sizeof(buf), "%u", rpc_get_port(sap));
+	xprt->address_strings[RPC_DISPLAY_PORT] = kstrdup(buf, GFP_KERNEL);
+
+	(void)snprintf(buf, sizeof(buf), "%4hx", rpc_get_port(sap));
+	xprt->address_strings[RPC_DISPLAY_HEX_PORT] = kstrdup(buf, GFP_KERNEL);
+}
+
+static void xs_format_peer_addresses(struct rpc_xprt *xprt,
+				     const char *protocol,
+				     const char *netid)
+{
+	xprt->address_strings[RPC_DISPLAY_PROTO] = protocol;
+	xprt->address_strings[RPC_DISPLAY_NETID] = netid;
+	xs_format_common_peer_addresses(xprt);
+	xs_format_common_peer_ports(xprt);
+}
+
+static void xs_free_peer_addresses(struct rpc_xprt *xprt)
+{
+	unsigned int i;
+
+	for (i = 0; i < RPC_DISPLAY_MAX; i++)
+		switch (i) {
+		case RPC_DISPLAY_PROTO:
+		case RPC_DISPLAY_NETID:
+			continue;
+		default:
+			kfree(xprt->address_strings[i]);
+		}
+}
+
+#define XS_SENDMSG_FLAGS	(MSG_DONTWAIT | MSG_NOSIGNAL)
+
+static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen, struct kvec *vec, unsigned int base, int more)
+{
+	struct msghdr msg = {
+		.msg_name	= addr,
+		.msg_namelen	= addrlen,
+		.msg_flags	= XS_SENDMSG_FLAGS | (more ? MSG_MORE : 0),
+	};
+	struct kvec iov = {
+		.iov_base	= vec->iov_base + base,
+		.iov_len	= vec->iov_len - base,
+	};
+
+	if (iov.iov_len != 0)
+		return kernel_sendmsg(sock, &msg, &iov, 1, iov.iov_len);
+	return kernel_sendmsg(sock, &msg, NULL, 0, 0);
+}
+
+static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more)
+{
+	struct page **ppage;
+	unsigned int remainder;
+	int err, sent = 0;
+
+	remainder = xdr->page_len - base;
+	base += xdr->page_base;
+	ppage = xdr->pages + (base >> PAGE_SHIFT);
+	base &= ~PAGE_MASK;
+	for(;;) {
+		unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder);
+		int flags = XS_SENDMSG_FLAGS;
+
+		remainder -= len;
+		if (remainder != 0 || more)
+			flags |= MSG_MORE;
+		err = sock->ops->sendpage(sock, *ppage, base, len, flags);
+		if (remainder == 0 || err != len)
+			break;
+		sent += err;
+		ppage++;
+		base = 0;
+	}
+	if (sent == 0)
+		return err;
+	if (err > 0)
+		sent += err;
+	return sent;
+}
+
+/**
+ * xs_sendpages - write pages directly to a socket
+ * @sock: socket to send on
+ * @addr: UDP only -- address of destination
+ * @addrlen: UDP only -- length of destination address
+ * @xdr: buffer containing this request
+ * @base: starting position in the buffer
+ *
+ */
+static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base)
+{
+	unsigned int remainder = xdr->len - base;
+	int err, sent = 0;
+
+	if (unlikely(!sock))
+		return -ENOTSOCK;
+
+	clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags);
+	if (base != 0) {
+		addr = NULL;
+		addrlen = 0;
+	}
+
+	if (base < xdr->head[0].iov_len || addr != NULL) {
+		unsigned int len = xdr->head[0].iov_len - base;
+		remainder -= len;
+		err = xs_send_kvec(sock, addr, addrlen, &xdr->head[0], base, remainder != 0);
+		if (remainder == 0 || err != len)
+			goto out;
+		sent += err;
+		base = 0;
+	} else
+		base -= xdr->head[0].iov_len;
+
+	if (base < xdr->page_len) {
+		unsigned int len = xdr->page_len - base;
+		remainder -= len;
+		err = xs_send_pagedata(sock, xdr, base, remainder != 0);
+		if (remainder == 0 || err != len)
+			goto out;
+		sent += err;
+		base = 0;
+	} else
+		base -= xdr->page_len;
+
+	if (base >= xdr->tail[0].iov_len)
+		return sent;
+	err = xs_send_kvec(sock, NULL, 0, &xdr->tail[0], base, 0);
+out:
+	if (sent == 0)
+		return err;
+	if (err > 0)
+		sent += err;
+	return sent;
+}
+
+static void xs_nospace_callback(struct rpc_task *task)
+{
+	struct smb_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct smb_xprt, xprt);
+
+	transport->inet->sk_write_pending--;
+	clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+}
+
+/**
+ * xs_nospace - place task on wait queue if transmit was incomplete
+ * @task: task to put to sleep
+ *
+ */
+static int xs_nospace(struct rpc_task *task)
+{
+	struct rpc_rqst *req = task->tk_rqstp;
+	struct rpc_xprt *xprt = req->rq_xprt;
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+	int ret = 0;
+
+	dprintk("RPC: %5u xmit incomplete (%u left of %u)\n",
+			task->tk_pid, req->rq_slen - req->rq_bytes_sent,
+			req->rq_slen);
+
+	/* Protect against races with write_space */
+	spin_lock_bh(&xprt->transport_lock);
+
+	/* Don't race with disconnect */
+	if (xprt_connected(xprt)) {
+		if (test_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags)) {
+			ret = -EAGAIN;
+			/*
+			 * Notify TCP that we're limited by the application
+			 * window size
+			 */
+			set_bit(SOCK_NOSPACE, &transport->sock->flags);
+			transport->inet->sk_write_pending++;
+			/* ...and wait for more buffer space */
+			xprt_wait_for_buffer_space(task, xs_nospace_callback);
+		}
+	} else {
+		clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+		ret = -ENOTCONN;
+	}
+
+	spin_unlock_bh(&xprt->transport_lock);
+	return ret;
+}
+
+/**
+ * xs_tcp_shutdown - gracefully shut down a TCP socket
+ * @xprt: transport
+ *
+ * Initiates a graceful shutdown of the TCP socket by calling the
+ * equivalent of shutdown(SHUT_WR);
+ */
+static void xs_tcp_shutdown(struct rpc_xprt *xprt)
+{
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+	struct socket *sock = transport->sock;
+
+	if (sock != NULL)
+		kernel_sock_shutdown(sock, SHUT_WR);
+}
+
+static inline void xs_encode_smb_record_marker(struct xdr_buf *buf)
+{
+	u32 reclen = buf->len - sizeof(rpc_fraghdr);
+	rpc_fraghdr *base = buf->head[0].iov_base;
+	*base = htonl(SMB_FRAGMENT_SIZE_MASK & reclen);
+}
+
+/**
+ * xs_smb_send_request - write an RPC request to a TCP socket
+ * @task: address of RPC task that manages the state of an RPC request
+ *
+ * Return values:
+ *        0:	The request has been sent
+ *   EAGAIN:	The socket was blocked, please call again later to
+ *		complete the request
+ * ENOTCONN:	Caller needs to invoke connect logic then call again
+ *    other:	Some other error occured, the request was not sent
+ *
+ * XXX: In the case of soft timeouts, should we eventually give up
+ *	if sendmsg is not able to make progress?
+ */
+static int xs_smb_send_request(struct rpc_task *task)
+{
+	struct rpc_rqst *req = task->tk_rqstp;
+	struct rpc_xprt *xprt = req->rq_xprt;
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+	struct xdr_buf *xdr = &req->rq_snd_buf;
+	int status;
+
+	xs_encode_smb_record_marker(&req->rq_snd_buf);
+
+	xs_pktdump("packet data:",
+				req->rq_svec->iov_base,
+				req->rq_svec->iov_len);
+
+	/* Continue transmitting the packet/record. We must be careful
+	 * to cope with writespace callbacks arriving _after_ we have
+	 * called sendmsg(). */
+	while (1) {
+		status = xs_sendpages(transport->sock,
+					NULL, 0, xdr, req->rq_bytes_sent);
+
+		dprintk("RPC:       %s(%u) = %d\n", __func__,
+				xdr->len - req->rq_bytes_sent, status);
+
+		if (unlikely(status < 0))
+			break;
+
+		/* If we've sent the entire packet, immediately
+		 * reset the count of bytes sent. */
+		req->rq_bytes_sent += status;
+		task->tk_bytes_sent += status;
+		if (likely(req->rq_bytes_sent >= req->rq_slen)) {
+			req->rq_bytes_sent = 0;
+			return 0;
+		}
+
+		if (status != 0)
+			continue;
+		status = -EAGAIN;
+		break;
+	}
+	if (!transport->sock)
+		goto out;
+
+	switch (status) {
+	case -ENOTSOCK:
+		status = -ENOTCONN;
+		/* Should we call xs_close() here? */
+		break;
+	case -EAGAIN:
+		status = xs_nospace(task);
+		break;
+	default:
+		dprintk("RPC:       sendmsg returned unrecognized error %d\n",
+			-status);
+	case -ECONNRESET:
+	case -EPIPE:
+		xs_tcp_shutdown(xprt);
+	case -ECONNREFUSED:
+	case -ENOTCONN:
+		clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+	}
+out:
+	return status;
+}
+
+/**
+ * xs_tcp_release_xprt - clean up after a tcp transmission
+ * @xprt: transport
+ * @task: rpc task
+ *
+ * This cleans up if an error causes us to abort the transmission of a request.
+ * In this case, the socket may need to be reset in order to avoid confusing
+ * the server.
+ */
+static void xs_tcp_release_xprt(struct rpc_xprt *xprt, struct rpc_task *task)
+{
+	struct rpc_rqst *req;
+
+	if (task != xprt->snd_task)
+		return;
+	if (task == NULL)
+		goto out_release;
+	req = task->tk_rqstp;
+	if (req->rq_bytes_sent == 0)
+		goto out_release;
+	if (req->rq_bytes_sent == req->rq_snd_buf.len)
+		goto out_release;
+	set_bit(XPRT_CLOSE_WAIT, &task->tk_xprt->state);
+out_release:
+	xprt_release_xprt(xprt, task);
+}
+
+static void xs_save_old_callbacks(struct smb_xprt *transport, struct sock *sk)
+{
+	transport->old_data_ready = sk->sk_data_ready;
+	transport->old_state_change = sk->sk_state_change;
+	transport->old_write_space = sk->sk_write_space;
+	transport->old_error_report = sk->sk_error_report;
+}
+
+static void xs_restore_old_callbacks(struct smb_xprt *transport, struct sock *sk)
+{
+	sk->sk_data_ready = transport->old_data_ready;
+	sk->sk_state_change = transport->old_state_change;
+	sk->sk_write_space = transport->old_write_space;
+	sk->sk_error_report = transport->old_error_report;
+}
+
+static void xs_reset_transport(struct smb_xprt *transport)
+{
+	struct socket *sock = transport->sock;
+	struct sock *sk = transport->inet;
+
+	if (sk == NULL)
+		return;
+
+	write_lock_bh(&sk->sk_callback_lock);
+	transport->inet = NULL;
+	transport->sock = NULL;
+
+	sk->sk_user_data = NULL;
+
+	xs_restore_old_callbacks(transport, sk);
+	write_unlock_bh(&sk->sk_callback_lock);
+
+	sk->sk_no_check = 0;
+
+	sock_release(sock);
+}
+
+/**
+ * xs_close - close a socket
+ * @xprt: transport
+ *
+ * This is used when all requests are complete; ie, no DRC state remains
+ * on the server we want to save.
+ *
+ * The caller _must_ be holding XPRT_LOCKED in order to avoid issues with
+ * xs_reset_transport() zeroing the socket from underneath a writer.
+ */
+static void xs_close(struct rpc_xprt *xprt)
+{
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+
+	dprintk("RPC:       xs_close xprt %p\n", xprt);
+
+	xs_reset_transport(transport);
+
+	smp_mb__before_clear_bit();
+	clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
+	clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
+	clear_bit(XPRT_CLOSING, &xprt->state);
+	smp_mb__after_clear_bit();
+	xprt_disconnect_done(xprt);
+}
+
+static void xs_tcp_close(struct rpc_xprt *xprt)
+{
+	if (test_and_clear_bit(XPRT_CONNECTION_CLOSE, &xprt->state))
+		xs_close(xprt);
+	else
+		xs_tcp_shutdown(xprt);
+}
+
+/**
+ * xs_destroy - prepare to shutdown a transport
+ * @xprt: doomed transport
+ *
+ */
+static void xs_destroy(struct rpc_xprt *xprt)
+{
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+
+	dprintk("RPC:       xs_destroy xprt %p\n", xprt);
+
+	cancel_rearming_delayed_work(&transport->connect_worker);
+
+	xs_close(xprt);
+	xs_free_peer_addresses(xprt);
+	kfree(xprt->slot);
+	kfree(xprt);
+	module_put(THIS_MODULE);
+}
+
+static inline struct rpc_xprt *xprt_from_sock(struct sock *sk)
+{
+	return (struct rpc_xprt *) sk->sk_user_data;
+}
+
+static inline void xs_smb_read_fraghdr(struct rpc_xprt *xprt, struct xdr_skb_reader *desc)
+{
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+	size_t len, used;
+	char *p;
+
+	p = ((char *) &transport->tcp_fraghdr) + transport->tcp_offset;
+	len = sizeof(transport->tcp_fraghdr) - transport->tcp_offset;
+	used = xdr_skb_read_bits(desc, p, len);
+	transport->tcp_offset += used;
+	if (used != len)
+		return;
+
+	transport->tcp_reclen = ntohl(transport->tcp_fraghdr);
+	if (transport->tcp_reclen & ~SMB_FRAGMENT_SIZE_MASK) {
+		dprintk("SMB:       top byte of fragheader invalid\n");
+		xprt_force_disconnect(xprt);
+		return;
+	}
+
+	transport->tcp_reclen &= SMB_FRAGMENT_SIZE_MASK;
+
+	transport->tcp_flags = TCP_RCV_READ_SMBHDR;
+	transport->tcp_offset = 0;
+
+	/* Sanity check of the record length */
+	if (unlikely(transport->tcp_reclen < sizeof(struct smb_header))) {
+		dprintk("RPC:       invalid SMB record fragment length\n");
+		xprt_force_disconnect(xprt);
+		return;
+	}
+	dprintk("RPC:       reading SMB record fragment of length %d\n",
+			transport->tcp_reclen);
+}
+
+static void xs_smb_check_fraghdr(struct smb_xprt *transport)
+{
+	if (transport->tcp_offset == transport->tcp_reclen) {
+		transport->tcp_flags = TCP_RCV_COPY_FRAGHDR;
+		transport->tcp_offset = 0;
+		transport->tcp_copied = 0;
+	}
+}
+
+static inline void xs_smb_read_smbhdr(struct smb_xprt *transport, struct xdr_skb_reader *desc)
+{
+	size_t len, used;
+	char *p;
+
+	len = sizeof(transport->smb_header) - transport->tcp_offset;
+	dprintk("SMB:       reading SMB header (%Zu bytes)\n", len);
+	p = ((char *) &transport->smb_header) + transport->tcp_offset;
+	used = xdr_skb_read_bits(desc, p, len);
+	transport->tcp_offset += used;
+	if (used != len)
+		return;
+	transport->tcp_flags = TCP_RCV_COPY_SMBHDR | TCP_RCV_COPY_DATA;
+
+	if (transport->smb_header.flags & SMBFLG_RESPONSE)
+	dprintk("SMB:       reading %s MID 0x%04x\n",
+			(transport->smb_header.flags & SMBFLG_RESPONSE) ?
+			"reply for" : "request with",
+			le16_to_cpu(transport->smb_header.mid));
+
+	xs_smb_check_fraghdr(transport);
+}
+
+static inline void xs_smb_read_common(struct rpc_xprt *xprt,
+				     struct xdr_skb_reader *desc,
+				     struct rpc_rqst *req)
+{
+	struct smb_xprt *transport =
+				container_of(xprt, struct smb_xprt, xprt);
+	struct xdr_buf *rcvbuf;
+	size_t len;
+	ssize_t r;
+
+	rcvbuf = &req->rq_private_buf;
+
+	/* copy SMB header into buffer */
+	if (transport->tcp_flags & TCP_RCV_COPY_SMBHDR) {
+		memcpy(rcvbuf->head[0].iov_base, &transport->smb_header,
+			sizeof(transport->smb_header));
+		transport->tcp_copied += sizeof(transport->smb_header);
+		transport->tcp_flags &= ~TCP_RCV_COPY_SMBHDR;
+	}
+
+	len = desc->count;
+	if (len > transport->tcp_reclen - transport->tcp_offset) {
+		struct xdr_skb_reader my_desc;
+
+		len = transport->tcp_reclen - transport->tcp_offset;
+		memcpy(&my_desc, desc, sizeof(my_desc));
+		my_desc.count = len;
+		r = xdr_partial_copy_from_skb(rcvbuf, transport->tcp_copied,
+					  &my_desc, xdr_skb_read_bits);
+		desc->count -= r;
+		desc->offset += r;
+	} else
+		r = xdr_partial_copy_from_skb(rcvbuf, transport->tcp_copied,
+					  desc, xdr_skb_read_bits);
+
+	if (r > 0) {
+		transport->tcp_copied += r;
+		transport->tcp_offset += r;
+	}
+	if (r != len) {
+		/* Error when copying to the receive buffer,
+		 * usually because we weren't able to allocate
+		 * additional buffer pages. All we can do now
+		 * is turn off TCP_RCV_COPY_DATA, so the request
+		 * will not receive any additional updates,
+		 * and time out.
+		 * Any remaining data from this record will
+		 * be discarded.
+		 */
+		transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
+		dprintk("RPC:       XID %08x truncated request\n",
+				ntohl(transport->smb_header.mid));
+		dprintk("RPC:       xprt = %p, tcp_copied = %lu, "
+				"tcp_offset = %u, tcp_reclen = %u\n",
+				xprt, transport->tcp_copied,
+				transport->tcp_offset, transport->tcp_reclen);
+		return;
+	}
+
+	dprintk("RPC:       XID %08x read %Zd bytes\n",
+			ntohl(transport->smb_header.mid), r);
+	dprintk("RPC:       xprt = %p, tcp_copied = %lu, tcp_offset = %u, "
+			"tcp_reclen = %u\n", xprt, transport->tcp_copied,
+			transport->tcp_offset, transport->tcp_reclen);
+
+	if (transport->tcp_copied == req->rq_private_buf.buflen ||
+	    transport->tcp_offset == transport->tcp_reclen)
+		transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
+
+	return;
+}
+
+/*
+ * Finds the request corresponding to the RPC xid and invokes the common
+ * tcp read code to read the data.
+ */
+static inline int xs_smb_read_reply(struct rpc_xprt *xprt,
+				    struct xdr_skb_reader *desc)
+{
+	struct smb_xprt *transport =
+				container_of(xprt, struct smb_xprt, xprt);
+	struct rpc_rqst *req;
+
+	dprintk("RPC:       read reply XID %08x\n", ntohl(transport->smb_header.mid));
+
+	/* Find and lock the request corresponding to this xid */
+	spin_lock(&xprt->transport_lock);
+	req = xprt_lookup_rqst(xprt, transport->smb_header.mid);
+	if (!req) {
+		dprintk("RPC:       XID %08x request not found!\n",
+				ntohl(transport->smb_header.mid));
+		spin_unlock(&xprt->transport_lock);
+		return -1;
+	}
+
+	xs_smb_read_common(xprt, desc, req);
+
+	if (!(transport->tcp_flags & TCP_RCV_COPY_DATA))
+		xprt_complete_rqst(req->rq_task, transport->tcp_copied);
+
+	spin_unlock(&xprt->transport_lock);
+	return 0;
+}
+
+#if defined(CONFIG_NFS_V4_1)
+/*
+ * Obtains an rpc_rqst previously allocated and invokes the common
+ * tcp read code to read the data.  The result is placed in the callback
+ * queue.
+ * If we're unable to obtain the rpc_rqst we schedule the closing of the
+ * connection and return -1.
+ */
+static inline int xs_smb_read_callback(struct rpc_xprt *xprt,
+				       struct xdr_skb_reader *desc)
+{
+	struct smb_xprt *transport =
+				container_of(xprt, struct smb_xprt, xprt);
+	struct rpc_rqst *req;
+
+	req = xprt_alloc_bc_request(xprt);
+	if (req == NULL) {
+		printk(KERN_WARNING "Callback slot table overflowed\n");
+		xprt_force_disconnect(xprt);
+		return -1;
+	}
+
+	req->rq_xid = transport->smb_header.mid;
+	dprintk("RPC:       read callback  XID %08x\n", ntohl(req->rq_xid));
+	xs_smb_read_common(xprt, desc, req);
+
+	if (!(transport->tcp_flags & TCP_RCV_COPY_DATA)) {
+		struct svc_serv *bc_serv = xprt->bc_serv;
+
+		/*
+		 * Add callback request to callback list.  The callback
+		 * service sleeps on the sv_cb_waitq waiting for new
+		 * requests.  Wake it up after adding enqueing the
+		 * request.
+		 */
+		dprintk("RPC:       add callback request to list\n");
+		spin_lock(&bc_serv->sv_cb_lock);
+		list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
+		spin_unlock(&bc_serv->sv_cb_lock);
+		wake_up(&bc_serv->sv_cb_waitq);
+	}
+
+	req->rq_private_buf.len = transport->tcp_copied;
+
+	return 0;
+}
+
+static inline int _xs_smb_read_data(struct rpc_xprt *xprt,
+					struct xdr_skb_reader *desc)
+{
+	struct smb_xprt *transport =
+				container_of(xprt, struct smb_xprt, xprt);
+
+	return (transport->smb_header.flags & SMBFLG_RESPONSE) ?
+		xs_smb_read_reply(xprt, desc) :
+		xs_smb_read_callback(xprt, desc);
+}
+#else
+static inline int _xs_smb_read_data(struct rpc_xprt *xprt,
+					struct xdr_skb_reader *desc)
+{
+	return xs_smb_read_reply(xprt, desc);
+}
+#endif /* CONFIG_NFS_V4_1 */
+
+/*
+ * Read data off the transport.  This can be either an RPC_CALL or an
+ * RPC_REPLY.  Relay the processing to helper functions.
+ */
+static void xs_smb_read_data(struct rpc_xprt *xprt,
+				    struct xdr_skb_reader *desc)
+{
+	struct smb_xprt *transport =
+				container_of(xprt, struct smb_xprt, xprt);
+
+	if (_xs_smb_read_data(xprt, desc) == 0)
+		xs_smb_check_fraghdr(transport);
+	else {
+		/*
+		 * The transport_lock protects the request handling.
+		 * There's no need to hold it to update the tcp_flags.
+		 */
+		transport->tcp_flags &= ~TCP_RCV_COPY_DATA;
+	}
+}
+
+static inline void xs_smb_read_discard(struct smb_xprt *transport, struct xdr_skb_reader *desc)
+{
+	size_t len;
+
+	len = transport->tcp_reclen - transport->tcp_offset;
+	if (len > desc->count)
+		len = desc->count;
+	desc->count -= len;
+	desc->offset += len;
+	transport->tcp_offset += len;
+	dprintk("RPC:       discarded %Zu bytes\n", len);
+	xs_smb_check_fraghdr(transport);
+}
+
+static int xs_smb_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb, unsigned int offset, size_t len)
+{
+	struct rpc_xprt *xprt = rd_desc->arg.data;
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+	struct xdr_skb_reader desc = {
+		.skb	= skb,
+		.offset	= offset,
+		.count	= len,
+	};
+
+	dprintk("RPC:       xs_tcp_data_recv started\n");
+	do {
+		dprintk("SMB:       %s tcp_flags=0x%lx\n", __func__,
+			transport->tcp_flags);
+		/* Read in a new fragment marker if necessary */
+		/* Can we ever really expect to get completely empty fragments? */
+		if (transport->tcp_flags & TCP_RCV_COPY_FRAGHDR) {
+			xs_smb_read_fraghdr(xprt, &desc);
+			continue;
+		}
+		/* Read in the xid if necessary */
+		if (transport->tcp_flags & TCP_RCV_READ_SMBHDR) {
+			xs_smb_read_smbhdr(transport, &desc);
+			continue;
+		}
+		/* Read in the request data */
+		if (transport->tcp_flags & TCP_RCV_COPY_DATA) {
+			xs_smb_read_data(xprt, &desc);
+			continue;
+		}
+		/* Skip over any trailing bytes on short reads */
+		xs_smb_read_discard(transport, &desc);
+	} while (desc.count);
+	dprintk("RPC:       xs_tcp_data_recv done\n");
+	return len - desc.count;
+}
+
+/**
+ * xs_smb_data_ready - "data ready" callback for TCP sockets
+ * @sk: socket with data to read
+ * @bytes: how much data to read
+ *
+ */
+static void xs_smb_data_ready(struct sock *sk, int bytes)
+{
+	struct rpc_xprt *xprt;
+	read_descriptor_t rd_desc;
+	int read;
+
+	dprintk("SMB:       xs_smb_data_ready...\n");
+
+	read_lock(&sk->sk_callback_lock);
+	if (!(xprt = xprt_from_sock(sk)))
+		goto out;
+	if (xprt->shutdown)
+		goto out;
+
+	/* We use rd_desc to pass struct xprt to xs_tcp_data_recv */
+	rd_desc.arg.data = xprt;
+	do {
+		rd_desc.count = 65536;
+		read = tcp_read_sock(sk, &rd_desc, xs_smb_data_recv);
+	} while (read > 0);
+out:
+	read_unlock(&sk->sk_callback_lock);
+}
+
+/*
+ * Do the equivalent of linger/linger2 handling for dealing with
+ * broken servers that don't close the socket in a timely
+ * fashion
+ */
+static void xs_tcp_schedule_linger_timeout(struct rpc_xprt *xprt,
+		unsigned long timeout)
+{
+	struct smb_xprt *transport;
+
+	if (xprt_test_and_set_connecting(xprt))
+		return;
+	set_bit(XPRT_CONNECTION_ABORT, &xprt->state);
+	transport = container_of(xprt, struct smb_xprt, xprt);
+	queue_delayed_work(rpciod_workqueue, &transport->connect_worker,
+			   timeout);
+}
+
+static void xs_tcp_cancel_linger_timeout(struct rpc_xprt *xprt)
+{
+	struct smb_xprt *transport;
+
+	transport = container_of(xprt, struct smb_xprt, xprt);
+
+	if (!test_bit(XPRT_CONNECTION_ABORT, &xprt->state) ||
+	    !cancel_delayed_work(&transport->connect_worker))
+		return;
+	clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
+	xprt_clear_connecting(xprt);
+}
+
+static void xs_sock_mark_closed(struct rpc_xprt *xprt)
+{
+	smp_mb__before_clear_bit();
+	clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
+	clear_bit(XPRT_CLOSING, &xprt->state);
+	smp_mb__after_clear_bit();
+	/* Mark transport as closed and wake up all pending tasks */
+	xprt_disconnect_done(xprt);
+}
+
+/**
+ * xs_smb_state_change - callback to handle TCP socket state changes
+ * @sk: socket whose state has changed
+ *
+ */
+static void xs_smb_state_change(struct sock *sk)
+{
+	struct rpc_xprt *xprt;
+
+	read_lock(&sk->sk_callback_lock);
+	if (!(xprt = xprt_from_sock(sk)))
+		goto out;
+	dprintk("RPC:       xs_smb_state_change client %p...\n", xprt);
+	dprintk("RPC:       state %x conn %d dead %d zapped %d\n",
+			sk->sk_state, xprt_connected(xprt),
+			sock_flag(sk, SOCK_DEAD),
+			sock_flag(sk, SOCK_ZAPPED));
+
+	switch (sk->sk_state) {
+	case TCP_ESTABLISHED:
+		spin_lock_bh(&xprt->transport_lock);
+		if (!xprt_test_and_set_connected(xprt)) {
+			struct smb_xprt *transport = container_of(xprt,
+					struct smb_xprt, xprt);
+
+			/* Reset TCP record info */
+			transport->tcp_offset = 0;
+			transport->tcp_reclen = 0;
+			transport->tcp_copied = 0;
+			transport->tcp_flags = TCP_RCV_COPY_FRAGHDR;
+
+			xprt_wake_pending_tasks(xprt, -EAGAIN);
+		}
+		spin_unlock_bh(&xprt->transport_lock);
+		break;
+	case TCP_FIN_WAIT1:
+		/* The client initiated a shutdown of the socket */
+		xprt->connect_cookie++;
+		xprt->reestablish_timeout = 0;
+		set_bit(XPRT_CLOSING, &xprt->state);
+		smp_mb__before_clear_bit();
+		clear_bit(XPRT_CONNECTED, &xprt->state);
+		clear_bit(XPRT_CLOSE_WAIT, &xprt->state);
+		smp_mb__after_clear_bit();
+		xs_tcp_schedule_linger_timeout(xprt, xs_smb_fin_timeout);
+		break;
+	case TCP_CLOSE_WAIT:
+		/* The server initiated a shutdown of the socket */
+		xprt_force_disconnect(xprt);
+	case TCP_SYN_SENT:
+		xprt->connect_cookie++;
+	case TCP_CLOSING:
+		/*
+		 * If the server closed down the connection, make sure that
+		 * we back off before reconnecting
+		 */
+		if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
+			xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
+		break;
+	case TCP_LAST_ACK:
+		set_bit(XPRT_CLOSING, &xprt->state);
+		xs_tcp_schedule_linger_timeout(xprt, xs_smb_fin_timeout);
+		smp_mb__before_clear_bit();
+		clear_bit(XPRT_CONNECTED, &xprt->state);
+		smp_mb__after_clear_bit();
+		break;
+	case TCP_CLOSE:
+		xs_tcp_cancel_linger_timeout(xprt);
+		xs_sock_mark_closed(xprt);
+	}
+ out:
+	read_unlock(&sk->sk_callback_lock);
+}
+
+/**
+ * xs_error_report - callback mainly for catching socket errors
+ * @sk: socket
+ */
+static void xs_error_report(struct sock *sk)
+{
+	struct rpc_xprt *xprt;
+
+	read_lock(&sk->sk_callback_lock);
+	if (!(xprt = xprt_from_sock(sk)))
+		goto out;
+	dprintk("RPC:       %s client %p...\n"
+			"RPC:       error %d\n",
+			__func__, xprt, sk->sk_err);
+	xprt_wake_pending_tasks(xprt, -EAGAIN);
+out:
+	read_unlock(&sk->sk_callback_lock);
+}
+
+static void xs_write_space(struct sock *sk)
+{
+	struct socket *sock;
+	struct rpc_xprt *xprt;
+
+	if (unlikely(!(sock = sk->sk_socket)))
+		return;
+	clear_bit(SOCK_NOSPACE, &sock->flags);
+
+	if (unlikely(!(xprt = xprt_from_sock(sk))))
+		return;
+	if (test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags) == 0)
+		return;
+
+	xprt_write_space(xprt);
+}
+
+/**
+ * xs_tcp_write_space - callback invoked when socket buffer space
+ *                             becomes available
+ * @sk: socket whose state has changed
+ *
+ * Called when more output buffer space is available for this socket.
+ * We try not to wake our writers until they can make "significant"
+ * progress, otherwise we'll waste resources thrashing kernel_sendmsg
+ * with a bunch of small requests.
+ */
+static void xs_tcp_write_space(struct sock *sk)
+{
+	read_lock(&sk->sk_callback_lock);
+
+	/* from net/core/stream.c:sk_stream_write_space */
+	if (sk_stream_wspace(sk) >= sk_stream_min_wspace(sk))
+		xs_write_space(sk);
+
+	read_unlock(&sk->sk_callback_lock);
+}
+
+/**
+ * xs_smb_set_port - reset the port number in the remote endpoint address
+ * @xprt: generic transport
+ * @port: new port number
+ *
+ * Should never be needed for SMB transport so just BUG() here.
+ */
+static void
+xs_smb_set_port(struct rpc_xprt *xprt, unsigned short port)
+{
+	dprintk("RPC:       setting port for xprt %p to %u\n", xprt, port);
+	BUG();
+}
+
+/**
+ * xs_smb_rpcbind - attempt to rebind the transport
+ * @task: task
+ *
+ * Should never be needed for SMB transport so just BUG() here.
+ */
+static void
+xs_smb_rpcbind(struct rpc_task *task)
+{
+	dprintk("RPC:       rpcbind called on SMB task %p", task);
+	BUG();
+}
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+static struct lock_class_key xs_key[2];
+static struct lock_class_key xs_slock_key[2];
+
+static inline void xs_reclassify_socket4(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BUG_ON(sock_owned_by_user(sk));
+	sock_lock_init_class_and_name(sk, "slock-AF_INET-RPC",
+		&xs_slock_key[0], "sk_lock-AF_INET-RPC", &xs_key[0]);
+}
+
+static inline void xs_reclassify_socket6(struct socket *sock)
+{
+	struct sock *sk = sock->sk;
+
+	BUG_ON(sock_owned_by_user(sk));
+	sock_lock_init_class_and_name(sk, "slock-AF_INET6-RPC",
+		&xs_slock_key[1], "sk_lock-AF_INET6-RPC", &xs_key[1]);
+}
+#else
+static inline void xs_reclassify_socket4(struct socket *sock)
+{
+}
+
+static inline void xs_reclassify_socket6(struct socket *sock)
+{
+}
+#endif
+
+/*
+ * We need to preserve the port number so the reply cache on the server can
+ * find our cached RPC replies when we get around to reconnecting.
+ */
+static void xs_abort_connection(struct rpc_xprt *xprt, struct smb_xprt *transport)
+{
+	int result;
+	struct sockaddr any;
+
+	dprintk("RPC:       disconnecting xprt %p to reuse port\n", xprt);
+
+	/*
+	 * Disconnect the transport socket by doing a connect operation
+	 * with AF_UNSPEC.  This should return immediately...
+	 */
+	memset(&any, 0, sizeof(any));
+	any.sa_family = AF_UNSPEC;
+	result = kernel_connect(transport->sock, &any, sizeof(any), 0);
+	if (!result)
+		xs_sock_mark_closed(xprt);
+	else
+		dprintk("RPC:       AF_UNSPEC connect return code %d\n",
+				result);
+}
+
+static void xs_tcp_reuse_connection(struct rpc_xprt *xprt, struct smb_xprt *transport)
+{
+	unsigned int state = transport->inet->sk_state;
+
+	if (state == TCP_CLOSE && transport->sock->state == SS_UNCONNECTED)
+		return;
+	if ((1 << state) & (TCPF_ESTABLISHED|TCPF_SYN_SENT))
+		return;
+	xs_abort_connection(xprt, transport);
+}
+
+static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
+{
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+
+	if (!transport->inet) {
+		struct sock *sk = sock->sk;
+
+		write_lock_bh(&sk->sk_callback_lock);
+
+		xs_save_old_callbacks(transport, sk);
+
+		sk->sk_user_data = xprt;
+		sk->sk_data_ready = xs_smb_data_ready;
+		sk->sk_state_change = xs_smb_state_change;
+		sk->sk_write_space = xs_tcp_write_space;
+		sk->sk_error_report = xs_error_report;
+		sk->sk_allocation = GFP_ATOMIC;
+
+		/* socket options */
+		sk->sk_userlocks |= SOCK_BINDPORT_LOCK;
+		sock_reset_flag(sk, SOCK_LINGER);
+		tcp_sk(sk)->linger2 = 0;
+		tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF;
+
+		xprt_clear_connected(xprt);
+
+		/* Reset to new socket */
+		transport->sock = sock;
+		transport->inet = sk;
+
+		write_unlock_bh(&sk->sk_callback_lock);
+	}
+
+	if (!xprt_bound(xprt))
+		return -ENOTCONN;
+
+	/* Tell the socket layer to start connecting... */
+	xprt->stat.connect_count++;
+	xprt->stat.connect_start = jiffies;
+	return kernel_connect(sock, xs_addr(xprt), xprt->addrlen, O_NONBLOCK);
+}
+
+/**
+ * xs_tcp_setup_socket - create a TCP socket and connect to a remote endpoint
+ * @xprt: RPC transport to connect
+ * @transport: socket transport to connect
+ * @create_sock: function to create a socket of the correct type
+ *
+ * Invoked by a work queue tasklet.
+ */
+static void xs_tcp_setup_socket(struct rpc_xprt *xprt,
+		struct smb_xprt *transport,
+		struct socket *(*create_sock)(struct rpc_xprt *,
+			struct smb_xprt *))
+{
+	struct socket *sock = transport->sock;
+	int status = -EIO;
+
+	if (xprt->shutdown)
+		goto out;
+
+	if (!sock) {
+		clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
+		sock = create_sock(xprt, transport);
+		if (IS_ERR(sock)) {
+			status = PTR_ERR(sock);
+			goto out;
+		}
+	} else {
+		int abort_and_exit;
+
+		abort_and_exit = test_and_clear_bit(XPRT_CONNECTION_ABORT,
+				&xprt->state);
+		/* "close" the socket, preserving the local port */
+		xs_tcp_reuse_connection(xprt, transport);
+
+		if (abort_and_exit)
+			goto out_eagain;
+	}
+
+	dprintk("SMB:       worker connecting xprt %p via %s to "
+		"%s (port %s)\n", xprt,
+		xprt->address_strings[RPC_DISPLAY_PROTO],
+		xprt->address_strings[RPC_DISPLAY_ADDR],
+		xprt->address_strings[RPC_DISPLAY_PORT]);
+
+	status = xs_tcp_finish_connecting(xprt, sock);
+	dprintk("SMB:       %p connect status %d connected %d sock state %d\n",
+			xprt, -status, xprt_connected(xprt),
+			sock->sk->sk_state);
+	switch (status) {
+	default:
+		printk("%s: connect returned unhandled error %d\n",
+			__func__, status);
+	case -EADDRNOTAVAIL:
+		/* We're probably in TIME_WAIT. Get rid of existing socket,
+		 * and retry
+		 */
+		set_bit(XPRT_CONNECTION_CLOSE, &xprt->state);
+		xprt_force_disconnect(xprt);
+		break;
+	case -ECONNREFUSED:
+	case -ECONNRESET:
+	case -ENETUNREACH:
+		/* retry with existing socket, after a delay */
+	case 0:
+	case -EINPROGRESS:
+	case -EALREADY:
+		xprt_clear_connecting(xprt);
+		return;
+	}
+out_eagain:
+	status = -EAGAIN;
+out:
+	xprt_clear_connecting(xprt);
+	xprt_wake_pending_tasks(xprt, status);
+}
+
+static struct socket *xs_create_tcp_sock4(struct rpc_xprt *xprt,
+		struct smb_xprt *transport)
+{
+	struct socket *sock;
+	int err;
+
+	/* start from scratch */
+	err = sock_create_kern(PF_INET, SOCK_STREAM, IPPROTO_TCP, &sock);
+	if (err < 0) {
+		dprintk("RPC:       can't create TCP transport socket (%d).\n",
+				-err);
+		goto out_err;
+	}
+	xs_reclassify_socket4(sock);
+	return sock;
+out_err:
+	return ERR_PTR(-EIO);
+}
+
+/**
+ * xs_tcp_connect_worker4 - connect a TCP socket to a remote endpoint
+ * @work: RPC transport to connect
+ *
+ * Invoked by a work queue tasklet.
+ */
+static void xs_tcp_connect_worker4(struct work_struct *work)
+{
+	struct smb_xprt *transport =
+		container_of(work, struct smb_xprt, connect_worker.work);
+	struct rpc_xprt *xprt = &transport->xprt;
+
+	xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock4);
+}
+
+static struct socket *xs_create_tcp_sock6(struct rpc_xprt *xprt,
+		struct smb_xprt *transport)
+{
+	struct socket *sock;
+	int err;
+
+	/* start from scratch */
+	err = sock_create_kern(PF_INET6, SOCK_STREAM, IPPROTO_TCP, &sock);
+	if (err < 0) {
+		dprintk("RPC:       can't create TCP transport socket (%d).\n",
+				-err);
+		goto out_err;
+	}
+	xs_reclassify_socket6(sock);
+	return sock;
+out_err:
+	return ERR_PTR(-EIO);
+}
+
+/**
+ * xs_tcp_connect_worker6 - connect a TCP socket to a remote endpoint
+ * @work: RPC transport to connect
+ *
+ * Invoked by a work queue tasklet.
+ */
+static void xs_tcp_connect_worker6(struct work_struct *work)
+{
+	struct smb_xprt *transport =
+		container_of(work, struct smb_xprt, connect_worker.work);
+	struct rpc_xprt *xprt = &transport->xprt;
+
+	xs_tcp_setup_socket(xprt, transport, xs_create_tcp_sock6);
+}
+
+/**
+ * xs_connect - connect a socket to a remote endpoint
+ * @task: address of RPC task that manages state of connect request
+ *
+ * TCP: If the remote end dropped the connection, delay reconnecting.
+ *
+ * UDP socket connects are synchronous, but we use a work queue anyway
+ * to guarantee that even unprivileged user processes can set up a
+ * socket on a privileged port.
+ *
+ * If a UDP socket connect fails, the delay behavior here prevents
+ * retry floods (hard mounts).
+ */
+static void xs_connect(struct rpc_task *task)
+{
+	struct rpc_xprt *xprt = task->tk_xprt;
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+
+	if (xprt_test_and_set_connecting(xprt))
+		return;
+
+	if (transport->sock != NULL) {
+		dprintk("RPC:       xs_connect delayed xprt %p for %lu "
+				"seconds\n",
+				xprt, xprt->reestablish_timeout / HZ);
+		queue_delayed_work(rpciod_workqueue,
+				   &transport->connect_worker,
+				   xprt->reestablish_timeout);
+		xprt->reestablish_timeout <<= 1;
+		if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO)
+			xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO;
+	} else {
+		dprintk("RPC:       xs_connect scheduled xprt %p\n", xprt);
+		queue_delayed_work(rpciod_workqueue,
+				   &transport->connect_worker, 0);
+	}
+}
+
+static void xs_tcp_connect(struct rpc_task *task)
+{
+	struct rpc_xprt *xprt = task->tk_xprt;
+
+	/* Exit if we need to wait for socket shutdown to complete */
+	if (test_bit(XPRT_CLOSING, &xprt->state))
+		return;
+	xs_connect(task);
+}
+
+/**
+ * xs_tcp_print_stats - display TCP socket-specifc stats
+ * @xprt: rpc_xprt struct containing statistics
+ * @seq: output file
+ *
+ */
+static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
+{
+	struct smb_xprt *transport = container_of(xprt, struct smb_xprt, xprt);
+	long idle_time = 0;
+
+	if (xprt_connected(xprt))
+		idle_time = (long)(jiffies - xprt->last_used) / HZ;
+
+	seq_printf(seq, "\txprt:\ttcp %u %lu %lu %lu %ld %lu %lu %lu %Lu %Lu\n",
+			transport->port,
+			xprt->stat.bind_count,
+			xprt->stat.connect_count,
+			xprt->stat.connect_time,
+			idle_time,
+			xprt->stat.sends,
+			xprt->stat.recvs,
+			xprt->stat.bad_xids,
+			xprt->stat.req_u,
+			xprt->stat.bklog_u);
+}
+
+static struct rpc_xprt_ops xs_smb_ops = {
+	.reserve_xprt		= xprt_reserve_xprt,
+	.release_xprt		= xs_tcp_release_xprt,
+	.rpcbind		= xs_smb_rpcbind,
+	.set_port		= xs_smb_set_port,
+	.connect		= xs_tcp_connect,
+	.buf_alloc		= rpc_malloc,
+	.buf_free		= rpc_free,
+	.send_request		= xs_smb_send_request,
+	.set_retrans_timeout	= xprt_set_retrans_timeout_def,
+#if defined(CONFIG_NFS_V4_1)
+	.release_request	= bc_release_request,
+#endif /* CONFIG_NFS_V4_1 */
+	.close			= xs_tcp_close,
+	.destroy		= xs_destroy,
+	.print_stats		= xs_tcp_print_stats,
+};
+
+static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
+				      unsigned int slot_table_size)
+{
+	struct rpc_xprt *xprt;
+	struct smb_xprt *new;
+
+	if (args->addrlen > sizeof(xprt->addr)) {
+		dprintk("RPC:       xs_setup_xprt: address too large\n");
+		return ERR_PTR(-EBADF);
+	}
+
+	new = kzalloc(sizeof(*new), GFP_KERNEL);
+	if (new == NULL) {
+		dprintk("RPC:       xs_setup_xprt: couldn't allocate "
+				"rpc_xprt\n");
+		return ERR_PTR(-ENOMEM);
+	}
+	xprt = &new->xprt;
+
+	xprt->max_reqs = slot_table_size;
+	xprt->slot = kcalloc(xprt->max_reqs, sizeof(struct rpc_rqst), GFP_KERNEL);
+	if (xprt->slot == NULL) {
+		kfree(xprt);
+		dprintk("RPC:       xs_setup_xprt: couldn't allocate slot "
+				"table\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	memcpy(&xprt->addr, args->dstaddr, args->addrlen);
+	xprt->addrlen = args->addrlen;
+	if (args->srcaddr)
+		memcpy(&new->addr, args->srcaddr, args->addrlen);
+
+	return xprt;
+}
+
+static const struct rpc_timeout xs_tcp_default_timeout = {
+	.to_initval = 60 * HZ,
+	.to_maxval = 60 * HZ,
+	.to_retries = 2,
+};
+
+/**
+ * xs_setup_tcp - Set up transport to use a TCP socket
+ * @args: rpc transport creation arguments
+ *
+ */
+static struct rpc_xprt *xs_setup_smb(struct xprt_create *args)
+{
+	struct sockaddr *addr = args->dstaddr;
+	struct rpc_xprt *xprt;
+	struct smb_xprt *transport;
+
+	xprt = xs_setup_xprt(args, xprt_smb_slot_table_entries);
+	if (IS_ERR(xprt))
+		return xprt;
+	transport = container_of(xprt, struct smb_xprt, xprt);
+
+	xprt->prot = IPPROTO_TCP;
+	xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32);
+	xprt->max_payload = SMB_MAX_FRAGMENT_SIZE;
+
+	xprt->bind_timeout = XS_BIND_TO;
+	xprt->connect_timeout = XS_TCP_CONN_TO;
+	xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
+	xprt->idle_timeout = XS_SMB_IDLE_DISC_TO;
+
+	xprt->ops = &xs_smb_ops;
+	xprt->timeout = &xs_tcp_default_timeout;
+
+	switch (addr->sa_family) {
+	case AF_INET:
+		if (((struct sockaddr_in *)addr)->sin_port == htons(0))
+			return ERR_PTR(-EINVAL);
+
+		xprt_set_bound(xprt);
+		INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_connect_worker4);
+		xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP);
+		break;
+	case AF_INET6:
+		if (((struct sockaddr_in6 *)addr)->sin6_port == htons(0))
+			return ERR_PTR(-EINVAL);
+
+		xprt_set_bound(xprt);
+		INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_connect_worker6);
+		xs_format_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP6);
+		break;
+	default:
+		kfree(xprt);
+		return ERR_PTR(-EAFNOSUPPORT);
+	}
+
+	dprintk("RPC:       set up xprt to %s (port %s) via %s\n",
+			xprt->address_strings[RPC_DISPLAY_ADDR],
+			xprt->address_strings[RPC_DISPLAY_PORT],
+			xprt->address_strings[RPC_DISPLAY_PROTO]);
+
+	if (try_module_get(THIS_MODULE))
+		return xprt;
+
+	kfree(xprt->slot);
+	kfree(xprt);
+	return ERR_PTR(-EINVAL);
+}
+
+static struct xprt_class	xs_smb_transport = {
+	.list		= LIST_HEAD_INIT(xs_smb_transport.list),
+	.name		= "smb",
+	.owner		= THIS_MODULE,
+	.ident		= XPRT_TRANSPORT_SMB,
+	.setup		= xs_setup_smb,
+};
+
+/**
+ * init_socket_xprt - set up xprtsock's sysctls, register with RPC client
+ *
+ */
+int init_smb_xprt(void)
+{
+#ifdef RPC_DEBUG
+	if (!sunrpc_table_header)
+		sunrpc_table_header = register_sysctl_table(sunrpc_table);
+#endif
+
+	xprt_register_transport(&xs_smb_transport);
+
+	return 0;
+}
+
+/**
+ * cleanup_socket_xprt - remove xprtsock's sysctls, unregister
+ *
+ */
+void cleanup_smb_xprt(void)
+{
+#ifdef RPC_DEBUG
+	if (sunrpc_table_header) {
+		unregister_sysctl_table(sunrpc_table_header);
+		sunrpc_table_header = NULL;
+	}
+#endif
+
+	xprt_unregister_transport(&xs_smb_transport);
+}
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 7/9] sunrpc: add encoding and decoding routines for SMB
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (5 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 6/9] sunrpc: add new SMB transport class for sunrpc Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 8/9] sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB Jeff Layton
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

Add a new smb.c file to hold encode and decode routines for SMB packets.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 include/linux/sunrpc/smb.h     |   42 ++++++++++++++
 include/linux/sunrpc/xprtsmb.h |    6 +-
 net/sunrpc/smb.c               |  120 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 165 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/sunrpc/smb.h
 create mode 100644 net/sunrpc/smb.c

diff --git a/include/linux/sunrpc/smb.h b/include/linux/sunrpc/smb.h
new file mode 100644
index 0000000..304ab8c
--- /dev/null
+++ b/include/linux/sunrpc/smb.h
@@ -0,0 +1,42 @@
+/*
+ *   net/sunrpc/smb.h -- SMB transport for sunrpc
+ *
+ *   Copyright (c) 2009 Red Hat, Inc.
+ *   Author(s): Jeff Layton (jlayton@redhat.com)
+ *
+ *   This library is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU Lesser General Public License as published
+ *   by the Free Software Foundation; either version 2.1 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This library is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+ *   the GNU Lesser General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Lesser General Public License
+ *   along with this library; if not, write to the Free Software
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * xprtsmb.h
+ *
+ * This file contains the public interfaces for the SMB transport for
+ * the sunrpc layer.
+ */
+
+#ifndef _LINUX_SUNRPC_SMB_H
+#define _LINUX_SUNRPC_SMB_H
+
+/*
+ * This is the generic SMB function. rqstp is either a rpc_rqst (client
+ * side) or svc_rqst pointer (server side).
+ * Encode functions always assume there's enough room in the buffer.
+ */
+typedef int     (*ksmbproc_t)(void *rqstp, __le32 *data, void *obj);
+
+void smb_encode(struct rpc_task *task);
+int smb_decode(struct rpc_task *task);
+
+#endif /* _LINUX_SUNRPC_SMB_H */
diff --git a/include/linux/sunrpc/xprtsmb.h b/include/linux/sunrpc/xprtsmb.h
index d55e85b..731cce2 100644
--- a/include/linux/sunrpc/xprtsmb.h
+++ b/include/linux/sunrpc/xprtsmb.h
@@ -34,9 +34,6 @@
  */
 #define XPRT_TRANSPORT_SMB	1024
 
-int init_smb_xprt(void);
-void cleanup_smb_xprt(void);
-
 /* standard SMB header */
 struct smb_header {
 	__u8		protocol[4];
@@ -56,4 +53,7 @@ struct smb_header {
 /* SMB Header Flags of interest */
 #define SMBFLG_RESPONSE 0x80	/* response from server */
 
+int init_smb_xprt(void);
+void cleanup_smb_xprt(void);
+
 #endif /* _LINUX_SUNRPC_XPRTSMB_H */
diff --git a/net/sunrpc/smb.c b/net/sunrpc/smb.c
new file mode 100644
index 0000000..5511bb2
--- /dev/null
+++ b/net/sunrpc/smb.c
@@ -0,0 +1,120 @@
+/*
+ * net/sunrpc/smb.c -- smb encode and decode routines
+ *
+ * Copyright (C) 2009 Red Hat, Inc -- Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/types.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprtsmb.h>
+#include <linux/sunrpc/smb.h>
+#include <linux/smbno.h>
+
+static __le32 *
+smb_verify_header(struct rpc_task *task)
+{
+	struct kvec *iov = &task->tk_rqstp->rq_rcv_buf.head[0];
+	__le32	*p = iov->iov_base;
+
+	if (task->tk_rqstp->rq_rcv_buf.len < sizeof(struct smb_header)) {
+		task->tk_action = call_bind;
+		task->tk_client->cl_stats->rpcretrans++;
+		return ERR_PTR(-EAGAIN);
+	}
+
+	/* FIXME: check for errors and return them */
+
+	/*
+	 * with SMB, we occasionally need to refer back to the SMB header for
+	 * info (flags, etc). The header is fixed-length however, so just
+	 * return a pointer to the start of the SMB header.
+	 */
+	return p;
+}
+
+static __le32 *
+smb_encode_header(struct rpc_task *task)
+{
+	struct rpc_rqst *req = task->tk_rqstp;
+	u32 *p =  (u32 *) req->rq_svec[0].iov_base;
+	struct smb_header *h;
+
+	/* transport header is always 32 bits */
+	++p;
+	memset(p, 0, sizeof(*h));
+
+	h = (struct smb_header *) p;
+
+	h->protocol[0] = 0xFF;
+	h->protocol[1] = 'S';
+	h->protocol[2] = 'M';
+	h->protocol[3] = 'B';
+
+	h->command = task->tk_msg.rpc_proc->p_proc;
+	h->flags2 = SMB_FLAGS2_LONG_PATH_COMPONENTS;
+	h->pid = cpu_to_le16((u16) current->tgid);
+	h->pidhigh = cpu_to_le16((u16) (current->tgid >> 16));
+
+	/*
+	 * SMB MID's are similar to XID's in RPC, but they're only 16 bits.
+	 * For now we just use the xid field and mask off the upper bits.
+	 */
+	req->rq_xid &= 0x0000ffff;
+	h->mid = cpu_to_le16((u16) req->rq_xid);
+
+	req->rq_slen = xdr_adjust_iovec(&req->rq_svec[0], (__be32 *) (h + 1));
+
+	return (__le32 *) (h + 1);
+}
+
+void
+smb_encode(struct rpc_task *task)
+{
+	struct rpc_rqst *req = task->tk_rqstp;
+	__le32 *p;
+	ksmbproc_t encode = (ksmbproc_t) task->tk_msg.rpc_proc->p_encode;
+
+	rpc_xdr_buf_init(&req->rq_snd_buf, req->rq_buffer, req->rq_callsize);
+	rpc_xdr_buf_init(&req->rq_rcv_buf,
+			 (char *)req->rq_buffer + req->rq_callsize,
+			 req->rq_rcvsize);
+
+	p = smb_encode_header(task);
+
+	encode(req, p, NULL);
+	return;
+}
+EXPORT_SYMBOL_GPL(smb_encode);
+
+int
+smb_decode(struct rpc_task *task)
+{
+	struct rpc_rqst *req = task->tk_rqstp;
+	kxdrproc_t	decode = task->tk_msg.rpc_proc->p_decode;
+	__le32		*p;
+
+	p = smb_verify_header(task);
+	if (IS_ERR(p))
+		return  PTR_ERR(p);
+
+	if (decode)
+		task->tk_status = decode(req, p, task->tk_msg.rpc_resp);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(smb_decode);
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 8/9] sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (6 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 7/9] sunrpc: add encoding and decoding routines for SMB Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-27 16:50 ` [RFC PATCH 9/9] smbtest: simple module for testing SMB/RPC code Jeff Layton
  2009-09-28  2:21 ` [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Shirish Pargaonkar
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

...and the necessary Makefile bits so to compile support into the sunrpc
module.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 net/sunrpc/Kconfig       |   11 +++++++++++
 net/sunrpc/Makefile      |    1 +
 net/sunrpc/sunrpc_syms.c |    3 +++
 net/sunrpc/xprtsmb.c     |    7 +++++--
 4 files changed, 20 insertions(+), 2 deletions(-)

diff --git a/net/sunrpc/Kconfig b/net/sunrpc/Kconfig
index 443c161..63f5ee7 100644
--- a/net/sunrpc/Kconfig
+++ b/net/sunrpc/Kconfig
@@ -54,3 +54,14 @@ config RPCSEC_GSS_SPKM3
 	  available from http://linux-nfs.org/.
 
 	  If unsure, say N.
+
+config SUNRPC_SMB
+	bool "SMB/CIFS Transport for the SUNRPC Layer (EXPERIMENTAL)"
+	depends on SUNRPC && EXPERIMENTAL
+	help
+	    This option adds the ability for the kernels SUNRPC Layer to
+	    send and receive Sever Message Block (SMB) traffic. This
+	    protocol is widely used by Microsoft Windows servers for
+	    filesharing.
+
+	    If unsure, say N.
diff --git a/net/sunrpc/Makefile b/net/sunrpc/Makefile
index 9d2fca5..691e62f 100644
--- a/net/sunrpc/Makefile
+++ b/net/sunrpc/Makefile
@@ -16,3 +16,4 @@ sunrpc-y := clnt.o xprt.o socklib.o xprtsock.o sched.o \
 sunrpc-$(CONFIG_NFS_V4_1) += backchannel_rqst.o bc_svc.o
 sunrpc-$(CONFIG_PROC_FS) += stats.o
 sunrpc-$(CONFIG_SYSCTL) += sysctl.o
+sunrpc-$(CONFIG_SUNRPC_SMB) += xprtsmb.o smb.o
diff --git a/net/sunrpc/sunrpc_syms.c b/net/sunrpc/sunrpc_syms.c
index 8cce921..65b4dec 100644
--- a/net/sunrpc/sunrpc_syms.c
+++ b/net/sunrpc/sunrpc_syms.c
@@ -21,6 +21,7 @@
 #include <linux/workqueue.h>
 #include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/sunrpc/xprtsock.h>
+#include <linux/sunrpc/xprtsmb.h>
 
 extern struct cache_detail ip_map_cache, unix_gid_cache;
 
@@ -45,6 +46,7 @@ init_sunrpc(void)
 	cache_register(&unix_gid_cache);
 	svc_init_xprt_sock();	/* svc sock transport */
 	init_socket_xprt();	/* clnt sock transport */
+	init_smb_xprt();
 	rpcauth_init_module();
 out:
 	return err;
@@ -54,6 +56,7 @@ static void __exit
 cleanup_sunrpc(void)
 {
 	rpcauth_remove_module();
+	cleanup_smb_xprt();
 	cleanup_socket_xprt();
 	svc_cleanup_xprt_sock();
 	unregister_rpc_pipefs();
diff --git a/net/sunrpc/xprtsmb.c b/net/sunrpc/xprtsmb.c
index 585cf37..362600a 100644
--- a/net/sunrpc/xprtsmb.c
+++ b/net/sunrpc/xprtsmb.c
@@ -1691,7 +1691,8 @@ static struct xprt_class	xs_smb_transport = {
  * init_socket_xprt - set up xprtsock's sysctls, register with RPC client
  *
  */
-int init_smb_xprt(void)
+int
+init_smb_xprt(void)
 {
 #ifdef RPC_DEBUG
 	if (!sunrpc_table_header)
@@ -1707,7 +1708,8 @@ int init_smb_xprt(void)
  * cleanup_socket_xprt - remove xprtsock's sysctls, unregister
  *
  */
-void cleanup_smb_xprt(void)
+void
+cleanup_smb_xprt(void)
 {
 #ifdef RPC_DEBUG
 	if (sunrpc_table_header) {
@@ -1718,3 +1720,4 @@ void cleanup_smb_xprt(void)
 
 	xprt_unregister_transport(&xs_smb_transport);
 }
+
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [RFC PATCH 9/9] smbtest: simple module for testing SMB/RPC code
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (7 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 8/9] sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB Jeff Layton
@ 2009-09-27 16:50 ` Jeff Layton
  2009-09-28  2:21 ` [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Shirish Pargaonkar
  9 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-27 16:50 UTC (permalink / raw
  To: linux-nfs, linux-cifs-client

Proof of concept module. This just sends a NEGOTIATE_PROTOCOL request to
a server and verifies the response.

Signed-off-by: Jeff Layton <jlayton@redhat.com>
---
 fs/Makefile          |    2 +
 fs/smbtest/Makefile  |    1 +
 fs/smbtest/smbtest.c |  204 ++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 207 insertions(+), 0 deletions(-)
 create mode 100644 fs/smbtest/Makefile
 create mode 100644 fs/smbtest/smbtest.c

diff --git a/fs/Makefile b/fs/Makefile
index af6d047..0e9d1df 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -124,3 +124,5 @@ obj-$(CONFIG_OCFS2_FS)		+= ocfs2/
 obj-$(CONFIG_BTRFS_FS)		+= btrfs/
 obj-$(CONFIG_GFS2_FS)           += gfs2/
 obj-$(CONFIG_EXOFS_FS)          += exofs/
+
+obj-m				+= smbtest/
diff --git a/fs/smbtest/Makefile b/fs/smbtest/Makefile
new file mode 100644
index 0000000..d861f82
--- /dev/null
+++ b/fs/smbtest/Makefile
@@ -0,0 +1 @@
+obj-m += smbtest.o
diff --git a/fs/smbtest/smbtest.c b/fs/smbtest/smbtest.c
new file mode 100644
index 0000000..cfcb672
--- /dev/null
+++ b/fs/smbtest/smbtest.c
@@ -0,0 +1,204 @@
+/*
+ * fs/smbtest/smbtest.c -- proof of concept test for SMB code in sunrpc
+ *
+ * Copyright (C) 2009 Red Hat, Inc -- Jeff Layton <jlayton@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/inet.h>
+#include <linux/sched.h>
+#include <linux/smbno.h>
+#include <linux/sunrpc/clnt.h>
+#include <linux/sunrpc/xprt.h>
+#include <linux/sunrpc/xprtsmb.h>
+#include <linux/sunrpc/smb.h>
+
+#define CIFS_NEGOTIATE			0
+#define SMB_COM_NEGOTIATE		0x72
+
+static char server_address[INET6_ADDRSTRLEN];
+module_param_string(server_address, server_address, INET6_ADDRSTRLEN, 0);
+MODULE_PARM_DESC(server_address, "IPv4 address of server");
+
+typedef struct negotiate_req {
+        __u8 WordCount;
+        __le16 ByteCount;
+        unsigned char DialectsArray[1];
+} __attribute__((packed)) NEGOTIATE_REQ;
+
+typedef struct negotiate_rsp {
+        __u8 WordCount;
+        __le16 DialectIndex; /* 0xFFFF = no dialect acceptable */
+        __u8 SecurityMode;
+        __le16 MaxMpxCount;
+        __le16 MaxNumberVcs;
+        __le32 MaxBufferSize;
+        __le32 MaxRawSize;
+        __le32 SessionKey;
+        __le32 Capabilities;    /* see below */
+        __le32 SystemTimeLow;
+        __le32 SystemTimeHigh;
+        __le16 ServerTimeZone;
+        __u8 EncryptionKeyLength;
+        __u16 ByteCount;
+        union {
+                unsigned char EncryptionKey[1]; /* cap extended security off */
+                /* followed by Domain name - if extended security is off */
+                /* followed by 16 bytes of server GUID */
+                /* then security blob if cap_extended_security negotiated */
+                struct {
+                        unsigned char GUID[16];
+                        unsigned char SecurityBlob[1];
+                } __attribute__((packed)) extended_response;
+        } __attribute__((packed)) u;
+} __attribute__((packed)) NEGOTIATE_RSP;
+
+/*
+ * SMB doesn't mandate that buffers are always quadword aligned. XDR/RPC
+ * does however and that concept is pretty pervasive in the sunrpc code.
+ * Eventually, it might be better to specify sizes in bytes, but it doesn't
+ * really matter much.
+ */
+#define SMB_HDR_QUADS XDR_QUADLEN(sizeof(struct smb_header))
+
+static int
+smb_enc_negotiate(struct rpc_rqst *req, NEGOTIATE_REQ *buf, void *obj)
+{
+	char *dialects = buf->DialectsArray;
+
+	buf->WordCount = 0;
+	buf->ByteCount = 12;
+
+	dialects[0] = 0x02; /* buffer format byte */
+	strncpy(&dialects[1], "NT LM 0.12", 1000);
+
+	req->rq_slen = xdr_adjust_iovec(&req->rq_svec[0],
+					(__be32 *) &dialects[12]);
+
+	return 0;
+}
+
+static int
+smb_dec_negotiate(void *rqstp, char *data, void *obj)
+{
+	NEGOTIATE_RSP *buf = (NEGOTIATE_RSP *) (data + sizeof(struct smb_header));
+
+	printk("smbtest: Server wants dialect index %u\n",
+		le16_to_cpu(buf->DialectIndex));
+	return 0;
+}
+
+static struct rpc_procinfo	smbtest_procedures[] = {
+[CIFS_NEGOTIATE] = {
+		.p_proc		= SMB_COM_NEGOTIATE,
+		.p_encode	= (kxdrproc_t) smb_enc_negotiate,
+		.p_decode	= (kxdrproc_t) smb_dec_negotiate,
+		.p_arglen	= XDR_QUADLEN(sizeof(NEGOTIATE_REQ) + 1024),
+		.p_replen	= XDR_QUADLEN(sizeof(NEGOTIATE_RSP) + 1024),
+		.p_timer	= 0,
+		.p_statidx	= CIFS_NEGOTIATE,
+		.p_name		= "NEGOTIATE",
+	},
+};
+
+static struct rpc_version	smbtest_version1 = {
+	.number		= 1,
+	.nrprocs	= ARRAY_SIZE(smbtest_procedures),
+	.procs		= smbtest_procedures,
+};
+
+static struct rpc_version *	smbtest_version[] = {
+	[1] = &smbtest_version1,
+};
+
+static struct rpc_stat smbtest_stats;
+
+static struct rpc_program cifs_rpc_prog = {
+		.name			= "smbtest",
+		.number			= 0,
+		.nrvers			= ARRAY_SIZE(smbtest_version),
+		.version		= smbtest_version,
+		.stats			= &smbtest_stats,
+};
+
+static struct rpc_clnt	*smbtest_clnt;
+
+static int smbtest_init(void)
+{
+	struct sockaddr_in sin = {
+		.sin_family = AF_INET,
+		.sin_port = htons(445),
+	};
+	u8 *addr = (u8 *) &sin.sin_addr.s_addr;
+	int status;
+	struct rpc_create_args create_args = {
+		.protocol		= XPRT_TRANSPORT_SMB,
+		.address		= (struct sockaddr *) &sin,
+		.addrsize		= sizeof(sin),
+		.servername		= "cifs",
+		.program		= &cifs_rpc_prog,
+		.version		= 1,
+		.authflavor		= RPC_AUTH_NULL,
+		.encode			= smb_encode,
+		.decode			= smb_decode,
+		.callhdr_size		= SMB_HDR_QUADS,
+		.replhdr_size		= SMB_HDR_QUADS,
+		.flags			= RPC_CLNT_CREATE_NOPING |
+					  RPC_CLNT_CREATE_NONPRIVPORT,
+	};
+
+	NEGOTIATE_REQ		req = { };
+	NEGOTIATE_RSP		rsp = { };
+
+	struct rpc_message msg = {
+		.rpc_argp	= &req,
+		.rpc_resp	= &rsp,
+	};
+
+	if (!in4_pton(server_address, -1, addr, '\0', NULL)) {
+		printk("smbtest: in4_pton failed\n");
+		return -EINVAL;
+	}
+
+	smbtest_clnt = rpc_create(&create_args);
+	if (IS_ERR(smbtest_clnt)) {
+		printk("smbtest: rpc client creation failed\n");
+		return PTR_ERR(smbtest_clnt);
+	}
+	printk("smbtest: rpc client create succeeded\n");
+
+	msg.rpc_proc = &smbtest_clnt->cl_procinfo[CIFS_NEGOTIATE];
+	status = rpc_call_sync(smbtest_clnt, &msg, 0);
+	printk("smbtest: rpc_call_sync returned %d\n", status);
+
+	return status;
+}
+
+static void smbtest_exit(void)
+{
+	printk("%s\n", __func__);
+	rpc_shutdown_client(smbtest_clnt);
+}
+
+module_init(smbtest_init);
+module_exit(smbtest_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jeff Layton <jlayton@samba.org>");
-- 
1.6.0.6


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB
  2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
                   ` (8 preceding siblings ...)
  2009-09-27 16:50 ` [RFC PATCH 9/9] smbtest: simple module for testing SMB/RPC code Jeff Layton
@ 2009-09-28  2:21 ` Shirish Pargaonkar
       [not found]   ` <4a4634330909271921o125c8303p64974d850007ed8b-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
  9 siblings, 1 reply; 12+ messages in thread
From: Shirish Pargaonkar @ 2009-09-28  2:21 UTC (permalink / raw
  To: Jeff Layton; +Cc: linux-nfs, linux-cifs-client


[-- Attachment #1.1: Type: text/plain, Size: 6415 bytes --]

On Sun, Sep 27, 2009 at 11:50 AM, Jeff Layton <jlayton@redhat.com> wrote:

> This patchset is still preliminary and is just an RFC...
>
> First, some background. When I was at Connectathon this year, Trond
> mentioned an interesting idea to me. He said (paraphrasing):
>
> "Why doesn't CIFS just use the RPC layer for transport? It's very
> efficient at shoveling bits out onto the wire. You'd just need to
> abstract out the XDR/RPC specific bits."
>
> The idea has been floating around in the back of my head until recently,
> when I decided to give it a go. This patchset represents a first, rough
> draft at a patchset to do this. There's also a proof of concept module
> that demonstrates that it works as expected.
>
> The patchset is still very rough. Reconnect behavior is still very
> "RPC-ish", for instance. There are doubtless other changes that will
> need to be made before I had anything merge-worthy.
>
> At this point, I'm just interested in feedback on the general approach.
> Should I continue to pursue this or is it a non-starter for some
> reason?
>
> The next step here, obviously is to start building a fs on top of it.
> I'd particularly be interested in using this as a foundation of a
> new smb2fs.
>
> I've also got this patchset up on my public git tree:
>
>    http://git.samba.org/?p=jlayton/cifs.git;a=summary
>
> Here are some questions I anticipate on this, and their answers:
>
> ------------------------------------------------------------------------
> Q: Are you insane? Why would you attempt to do this?
>
> A: Maybe...but in the last couple of years, I've spent a substantial
> amount of time working on the CIFS code. Much of that time has been
> spent fixing bugs. Many of those bugs exist in the low-level transport
> code which has been hacked on, kludged around and hand tweaked to
> where it is today. Unfortunately that's made it a very hard to work
> on mess. This drives away potential developers.
>
> CIFS in particular is also designed around synchronous ops, which
> seriously limits throughput. Retrofitting it for asynchronous operation
> will be adding even more kludges. The sunrpc code is already
> fundamentally asynchronous.
> ------------------------------------------------------------------------
> Q: Okay, what's the benefit of hooking it up to sunrpc rather than
> building a new transport layer (or fixing the transport in the other two
> filesystems)?
>
> A: Using sunrpc allows us to share a lot of the rpc scheduler code with
> sunrpc. At a high level, NFS/RPC and SMB aren't really very different.
> Sure, they have different formats, and one is big endian on the wire and
> the other isn't...still there are significant similarities.
>
> We also get access to the great upcall mechanisms that sunrpc has, and
> the possibility to share code like the gssapi upcalls. The sunrpc layer
> has a credential and authentication management framework that we can
> build on to make a truly multiuser CIFS/SMB filesystem.
>
> I've heard it claimed before that Linux's sunrpc layer is
> over-engineered, but in this case that works in our favor...
> ------------------------------------------------------------------------
> Q: can we hook up cifs or smbfs to use this as a transport?
>
> A: Not trivially. CIFS in particular is not designed with each call
> having discrete encode and decode functions. They're sort of mashed
> together. smbfs might be possible...I'm a little less familiar with it,
> but it does have a transport layer that more closely resembles the
> sunrpc one. Still though, it'd take significant work to make that
> happen. I'm not opposed to the idea however.
>
> In the end though, I think we'll probably need to design something new
> to sit on top of this. We will probably be able to borrow code and
> concepts from the other filesystems however.
> ------------------------------------------------------------------------
> Q: could we use this as a transport layer for a smb2fs ?
>
> A: Yes, I think so. This particular prototype is build around SMB1, but
> SMB2 could be supported with only minor modifications. One of the
> reasons for sending this patchset now before I've built a filesystem on
> top of it is because I know that SMB2 work is in progress. I'd like to
> see it based around a more asynchronous transport model, or at least
> built with cleaner layering so that we can eventually bolt on a different
> transport layer if we so choose.
>
> Jeff Layton (9):
>  sunrpc: abstract out encoding function at rpc_clnt level
>  sunrpc: move check for too small reply size into rpc_verify_header
>  sunrpc: abstract our call decoding routine
>  sunrpc: move rpc_xdr_buf_init to clnt.h
>  sunrpc: make call_bind non-static
>  sunrpc: add new SMB transport class for sunrpc
>  sunrpc: add encoding and decoding routines for SMB
>  sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB
>  smbtest: simple module for testing SMB/RPC code
>
>  fs/Makefile                    |    2 +
>  fs/lockd/host.c                |    4 +
>  fs/lockd/mon.c                 |    4 +
>  fs/nfs/client.c                |    4 +
>  fs/nfs/mount_clnt.c            |    4 +
>  fs/nfsd/nfs4callback.c         |    4 +
>  fs/smbtest/Makefile            |    1 +
>  fs/smbtest/smbtest.c           |  204 +++++
>  include/linux/sunrpc/clnt.h    |   24 +-
>  include/linux/sunrpc/smb.h     |   42 +
>  include/linux/sunrpc/xprtsmb.h |   59 ++
>  net/sunrpc/Kconfig             |   11 +
>  net/sunrpc/Makefile            |    1 +
>  net/sunrpc/clnt.c              |   98 ++-
>  net/sunrpc/rpcb_clnt.c         |    8 +
>  net/sunrpc/smb.c               |  120 +++
>  net/sunrpc/sunrpc_syms.c       |    3 +
>  net/sunrpc/xprtsmb.c           | 1723
> ++++++++++++++++++++++++++++++++++++++++
>  18 files changed, 2272 insertions(+), 44 deletions(-)
>  create mode 100644 fs/smbtest/Makefile
>  create mode 100644 fs/smbtest/smbtest.c
>  create mode 100644 include/linux/sunrpc/smb.h
>  create mode 100644 include/linux/sunrpc/xprtsmb.h
>  create mode 100644 net/sunrpc/smb.c
>  create mode 100644 net/sunrpc/xprtsmb.c
>
> _______________________________________________
> linux-cifs-client mailing list
> linux-cifs-client@lists.samba.org
> https://lists.samba.org/mailman/listinfo/linux-cifs-client
>


Jeff,

So servers need to speak SUNRPC as well right?
Are_threre/how_many servers are out there that speak CIFS/SMB over SUNRPC or
SMB2 over SUNRPC?

[-- Attachment #1.2: Type: text/html, Size: 7291 bytes --]

[-- Attachment #2: Type: text/plain, Size: 172 bytes --]

_______________________________________________
linux-cifs-client mailing list
linux-cifs-client@lists.samba.org
https://lists.samba.org/mailman/listinfo/linux-cifs-client

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [linux-cifs-client] [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB
       [not found]   ` <4a4634330909271921o125c8303p64974d850007ed8b-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2009-09-28 13:06     ` Jeff Layton
  0 siblings, 0 replies; 12+ messages in thread
From: Jeff Layton @ 2009-09-28 13:06 UTC (permalink / raw
  To: Shirish Pargaonkar; +Cc: linux-nfs, linux-cifs-client

On Sun, 27 Sep 2009 21:21:40 -0500
Shirish Pargaonkar <shirishpargaonkar@gmail.com> wrote:

> On Sun, Sep 27, 2009 at 11:50 AM, Jeff Layton <jlayton@redhat.com> wrote:
> 
> > This patchset is still preliminary and is just an RFC...
> >
> > First, some background. When I was at Connectathon this year, Trond
> > mentioned an interesting idea to me. He said (paraphrasing):
> >
> > "Why doesn't CIFS just use the RPC layer for transport? It's very
> > efficient at shoveling bits out onto the wire. You'd just need to
> > abstract out the XDR/RPC specific bits."
> >
> > The idea has been floating around in the back of my head until recently,
> > when I decided to give it a go. This patchset represents a first, rough
> > draft at a patchset to do this. There's also a proof of concept module
> > that demonstrates that it works as expected.
> >
> > The patchset is still very rough. Reconnect behavior is still very
> > "RPC-ish", for instance. There are doubtless other changes that will
> > need to be made before I had anything merge-worthy.
> >
> > At this point, I'm just interested in feedback on the general approach.
> > Should I continue to pursue this or is it a non-starter for some
> > reason?
> >
> > The next step here, obviously is to start building a fs on top of it.
> > I'd particularly be interested in using this as a foundation of a
> > new smb2fs.
> >
> > I've also got this patchset up on my public git tree:
> >
> >    http://git.samba.org/?p=jlayton/cifs.git;a=summary
> >
> > Here are some questions I anticipate on this, and their answers:
> >
> > ------------------------------------------------------------------------
> > Q: Are you insane? Why would you attempt to do this?
> >
> > A: Maybe...but in the last couple of years, I've spent a substantial
> > amount of time working on the CIFS code. Much of that time has been
> > spent fixing bugs. Many of those bugs exist in the low-level transport
> > code which has been hacked on, kludged around and hand tweaked to
> > where it is today. Unfortunately that's made it a very hard to work
> > on mess. This drives away potential developers.
> >
> > CIFS in particular is also designed around synchronous ops, which
> > seriously limits throughput. Retrofitting it for asynchronous operation
> > will be adding even more kludges. The sunrpc code is already
> > fundamentally asynchronous.
> > ------------------------------------------------------------------------
> > Q: Okay, what's the benefit of hooking it up to sunrpc rather than
> > building a new transport layer (or fixing the transport in the other two
> > filesystems)?
> >
> > A: Using sunrpc allows us to share a lot of the rpc scheduler code with
> > sunrpc. At a high level, NFS/RPC and SMB aren't really very different.
> > Sure, they have different formats, and one is big endian on the wire and
> > the other isn't...still there are significant similarities.
> >
> > We also get access to the great upcall mechanisms that sunrpc has, and
> > the possibility to share code like the gssapi upcalls. The sunrpc layer
> > has a credential and authentication management framework that we can
> > build on to make a truly multiuser CIFS/SMB filesystem.
> >
> > I've heard it claimed before that Linux's sunrpc layer is
> > over-engineered, but in this case that works in our favor...
> > ------------------------------------------------------------------------
> > Q: can we hook up cifs or smbfs to use this as a transport?
> >
> > A: Not trivially. CIFS in particular is not designed with each call
> > having discrete encode and decode functions. They're sort of mashed
> > together. smbfs might be possible...I'm a little less familiar with it,
> > but it does have a transport layer that more closely resembles the
> > sunrpc one. Still though, it'd take significant work to make that
> > happen. I'm not opposed to the idea however.
> >
> > In the end though, I think we'll probably need to design something new
> > to sit on top of this. We will probably be able to borrow code and
> > concepts from the other filesystems however.
> > ------------------------------------------------------------------------
> > Q: could we use this as a transport layer for a smb2fs ?
> >
> > A: Yes, I think so. This particular prototype is build around SMB1, but
> > SMB2 could be supported with only minor modifications. One of the
> > reasons for sending this patchset now before I've built a filesystem on
> > top of it is because I know that SMB2 work is in progress. I'd like to
> > see it based around a more asynchronous transport model, or at least
> > built with cleaner layering so that we can eventually bolt on a different
> > transport layer if we so choose.
> >
> > Jeff Layton (9):
> >  sunrpc: abstract out encoding function at rpc_clnt level
> >  sunrpc: move check for too small reply size into rpc_verify_header
> >  sunrpc: abstract our call decoding routine
> >  sunrpc: move rpc_xdr_buf_init to clnt.h
> >  sunrpc: make call_bind non-static
> >  sunrpc: add new SMB transport class for sunrpc
> >  sunrpc: add encoding and decoding routines for SMB
> >  sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB
> >  smbtest: simple module for testing SMB/RPC code
> >
> >  fs/Makefile                    |    2 +
> >  fs/lockd/host.c                |    4 +
> >  fs/lockd/mon.c                 |    4 +
> >  fs/nfs/client.c                |    4 +
> >  fs/nfs/mount_clnt.c            |    4 +
> >  fs/nfsd/nfs4callback.c         |    4 +
> >  fs/smbtest/Makefile            |    1 +
> >  fs/smbtest/smbtest.c           |  204 +++++
> >  include/linux/sunrpc/clnt.h    |   24 +-
> >  include/linux/sunrpc/smb.h     |   42 +
> >  include/linux/sunrpc/xprtsmb.h |   59 ++
> >  net/sunrpc/Kconfig             |   11 +
> >  net/sunrpc/Makefile            |    1 +
> >  net/sunrpc/clnt.c              |   98 ++-
> >  net/sunrpc/rpcb_clnt.c         |    8 +
> >  net/sunrpc/smb.c               |  120 +++
> >  net/sunrpc/sunrpc_syms.c       |    3 +
> >  net/sunrpc/xprtsmb.c           | 1723
> > ++++++++++++++++++++++++++++++++++++++++
> >  18 files changed, 2272 insertions(+), 44 deletions(-)
> >  create mode 100644 fs/smbtest/Makefile
> >  create mode 100644 fs/smbtest/smbtest.c
> >  create mode 100644 include/linux/sunrpc/smb.h
> >  create mode 100644 include/linux/sunrpc/xprtsmb.h
> >  create mode 100644 net/sunrpc/smb.c
> >  create mode 100644 net/sunrpc/xprtsmb.c
> >
> > _______________________________________________
> > linux-cifs-client mailing list
> > linux-cifs-client@lists.samba.org
> > https://lists.samba.org/mailman/listinfo/linux-cifs-client
> >
> 
> 
> Jeff,
> 
> So servers need to speak SUNRPC as well right?
> Are_threre/how_many servers are out there that speak CIFS/SMB over SUNRPC or
> SMB2 over SUNRPC?

No...sorry if that wasn't clear. The on the wire protocol is still the
same as cifs or smbfs talks. You could use this to talk to windows
servers for instance. The idea is to use the existing code in the Linux
kernel's sunrpc engine and teach it how to speak a new "dialect" on the
wire...the SMB or SMB2 protocol.

For example, to test this, I sent a NegotiateProtocol request to a
samba server over its normal port 445. No ONC-RPC required.

-- 
Jeff Layton <jlayton@redhat.com>

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2009-09-28 11:05 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-09-27 16:50 [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 1/9] sunrpc: abstract out encoding function at rpc_clnt level Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 2/9] sunrpc: move check for too small reply size into rpc_verify_header Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 3/9] sunrpc: abstract our call decoding routine Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 4/9] sunrpc: move rpc_xdr_buf_init to clnt.h Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 5/9] sunrpc: make call_bind non-static Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 6/9] sunrpc: add new SMB transport class for sunrpc Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 7/9] sunrpc: add encoding and decoding routines for SMB Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 8/9] sunrpc: add Kconfig option for CONFIG_SUNRPC_SMB Jeff Layton
2009-09-27 16:50 ` [RFC PATCH 9/9] smbtest: simple module for testing SMB/RPC code Jeff Layton
2009-09-28  2:21 ` [RFC PATCH 0/9] sunrpc: teach the SUNRPC layer how to speak SMB Shirish Pargaonkar
     [not found]   ` <4a4634330909271921o125c8303p64974d850007ed8b-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2009-09-28 13:06     ` [linux-cifs-client] " Jeff Layton

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.