]> Pileus Git - ~andy/linux/blobdiff - net/sunrpc/xprtsock.c
Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
[~andy/linux] / net / sunrpc / xprtsock.c
index 62438f3a914d6cb9c6f8c47b89a6cd5e026dd933..37c5475ba258b51b22c6722aeb605741c2732051 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/tcp.h>
 #include <linux/sunrpc/clnt.h>
 #include <linux/sunrpc/sched.h>
+#include <linux/sunrpc/svcsock.h>
 #include <linux/sunrpc/xprtsock.h>
 #include <linux/file.h>
 #ifdef CONFIG_NFS_V4_1
@@ -43,6 +44,7 @@
 #include <net/udp.h>
 #include <net/tcp.h>
 
+#include "sunrpc.h"
 /*
  * xprtsock tunables
  */
@@ -771,6 +773,7 @@ static void xs_close(struct rpc_xprt *xprt)
        dprintk("RPC:       xs_close xprt %p\n", xprt);
 
        xs_reset_transport(transport);
+       xprt->reestablish_timeout = 0;
 
        smp_mb__before_clear_bit();
        clear_bit(XPRT_CONNECTION_ABORT, &xprt->state);
@@ -1262,6 +1265,12 @@ static void xs_tcp_data_ready(struct sock *sk, int bytes)
        if (xprt->shutdown)
                goto out;
 
+       /* Any data means we had a useful conversation, so
+        * the we don't need to delay the next reconnect
+        */
+       if (xprt->reestablish_timeout)
+               xprt->reestablish_timeout = 0;
+
        /* We use rd_desc to pass struct xprt to xs_tcp_data_recv */
        rd_desc.arg.data = xprt;
        do {
@@ -2032,6 +2041,8 @@ static void xs_connect(struct rpc_task *task)
                                   &transport->connect_worker,
                                   xprt->reestablish_timeout);
                xprt->reestablish_timeout <<= 1;
+               if (xprt->reestablish_timeout < XS_TCP_INIT_REEST_TO)
+                       xprt->reestablish_timeout = XS_TCP_INIT_REEST_TO;
                if (xprt->reestablish_timeout > XS_TCP_MAX_REEST_TO)
                        xprt->reestablish_timeout = XS_TCP_MAX_REEST_TO;
        } else {
@@ -2098,6 +2109,134 @@ static void xs_tcp_print_stats(struct rpc_xprt *xprt, struct seq_file *seq)
                        xprt->stat.bklog_u);
 }
 
+/*
+ * Allocate a bunch of pages for a scratch buffer for the rpc code. The reason
+ * we allocate pages instead doing a kmalloc like rpc_malloc is because we want
+ * to use the server side send routines.
+ */
+void *bc_malloc(struct rpc_task *task, size_t size)
+{
+       struct page *page;
+       struct rpc_buffer *buf;
+
+       BUG_ON(size > PAGE_SIZE - sizeof(struct rpc_buffer));
+       page = alloc_page(GFP_KERNEL);
+
+       if (!page)
+               return NULL;
+
+       buf = page_address(page);
+       buf->len = PAGE_SIZE;
+
+       return buf->data;
+}
+
+/*
+ * Free the space allocated in the bc_alloc routine
+ */
+void bc_free(void *buffer)
+{
+       struct rpc_buffer *buf;
+
+       if (!buffer)
+               return;
+
+       buf = container_of(buffer, struct rpc_buffer, data);
+       free_page((unsigned long)buf);
+}
+
+/*
+ * Use the svc_sock to send the callback. Must be called with svsk->sk_mutex
+ * held. Borrows heavily from svc_tcp_sendto and xs_tcp_send_request.
+ */
+static int bc_sendto(struct rpc_rqst *req)
+{
+       int len;
+       struct xdr_buf *xbufp = &req->rq_snd_buf;
+       struct rpc_xprt *xprt = req->rq_xprt;
+       struct sock_xprt *transport =
+                               container_of(xprt, struct sock_xprt, xprt);
+       struct socket *sock = transport->sock;
+       unsigned long headoff;
+       unsigned long tailoff;
+
+       /*
+        * Set up the rpc header and record marker stuff
+        */
+       xs_encode_tcp_record_marker(xbufp);
+
+       tailoff = (unsigned long)xbufp->tail[0].iov_base & ~PAGE_MASK;
+       headoff = (unsigned long)xbufp->head[0].iov_base & ~PAGE_MASK;
+       len = svc_send_common(sock, xbufp,
+                             virt_to_page(xbufp->head[0].iov_base), headoff,
+                             xbufp->tail[0].iov_base, tailoff);
+
+       if (len != xbufp->len) {
+               printk(KERN_NOTICE "Error sending entire callback!\n");
+               len = -EAGAIN;
+       }
+
+       return len;
+}
+
+/*
+ * The send routine. Borrows from svc_send
+ */
+static int bc_send_request(struct rpc_task *task)
+{
+       struct rpc_rqst *req = task->tk_rqstp;
+       struct svc_xprt *xprt;
+       struct svc_sock         *svsk;
+       u32                     len;
+
+       dprintk("sending request with xid: %08x\n", ntohl(req->rq_xid));
+       /*
+        * Get the server socket associated with this callback xprt
+        */
+       xprt = req->rq_xprt->bc_xprt;
+       svsk = container_of(xprt, struct svc_sock, sk_xprt);
+
+       /*
+        * Grab the mutex to serialize data as the connection is shared
+        * with the fore channel
+        */
+       if (!mutex_trylock(&xprt->xpt_mutex)) {
+               rpc_sleep_on(&xprt->xpt_bc_pending, task, NULL);
+               if (!mutex_trylock(&xprt->xpt_mutex))
+                       return -EAGAIN;
+               rpc_wake_up_queued_task(&xprt->xpt_bc_pending, task);
+       }
+       if (test_bit(XPT_DEAD, &xprt->xpt_flags))
+               len = -ENOTCONN;
+       else
+               len = bc_sendto(req);
+       mutex_unlock(&xprt->xpt_mutex);
+
+       if (len > 0)
+               len = 0;
+
+       return len;
+}
+
+/*
+ * The close routine. Since this is client initiated, we do nothing
+ */
+
+static void bc_close(struct rpc_xprt *xprt)
+{
+       return;
+}
+
+/*
+ * The xprt destroy routine. Again, because this connection is client
+ * initiated, we do nothing
+ */
+
+static void bc_destroy(struct rpc_xprt *xprt)
+{
+       return;
+}
+
 static struct rpc_xprt_ops xs_udp_ops = {
        .set_buffer_size        = xs_udp_set_buffer_size,
        .reserve_xprt           = xprt_reserve_xprt_cong,
@@ -2134,6 +2273,22 @@ static struct rpc_xprt_ops xs_tcp_ops = {
        .print_stats            = xs_tcp_print_stats,
 };
 
+/*
+ * The rpc_xprt_ops for the server backchannel
+ */
+
+static struct rpc_xprt_ops bc_tcp_ops = {
+       .reserve_xprt           = xprt_reserve_xprt,
+       .release_xprt           = xprt_release_xprt,
+       .buf_alloc              = bc_malloc,
+       .buf_free               = bc_free,
+       .send_request           = bc_send_request,
+       .set_retrans_timeout    = xprt_set_retrans_timeout_def,
+       .close                  = bc_close,
+       .destroy                = bc_destroy,
+       .print_stats            = xs_tcp_print_stats,
+};
+
 static struct rpc_xprt *xs_setup_xprt(struct xprt_create *args,
                                      unsigned int slot_table_size)
 {
@@ -2322,11 +2477,93 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
        return ERR_PTR(-EINVAL);
 }
 
+/**
+ * xs_setup_bc_tcp - Set up transport to use a TCP backchannel socket
+ * @args: rpc transport creation arguments
+ *
+ */
+static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
+{
+       struct sockaddr *addr = args->dstaddr;
+       struct rpc_xprt *xprt;
+       struct sock_xprt *transport;
+       struct svc_sock *bc_sock;
+
+       if (!args->bc_xprt)
+               ERR_PTR(-EINVAL);
+
+       xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);
+       if (IS_ERR(xprt))
+               return xprt;
+       transport = container_of(xprt, struct sock_xprt, xprt);
+
+       xprt->prot = IPPROTO_TCP;
+       xprt->tsh_size = sizeof(rpc_fraghdr) / sizeof(u32);
+       xprt->max_payload = RPC_MAX_FRAGMENT_SIZE;
+       xprt->timeout = &xs_tcp_default_timeout;
+
+       /* backchannel */
+       xprt_set_bound(xprt);
+       xprt->bind_timeout = 0;
+       xprt->connect_timeout = 0;
+       xprt->reestablish_timeout = 0;
+       xprt->idle_timeout = 0;
+
+       /*
+        * The backchannel uses the same socket connection as the
+        * forechannel
+        */
+       xprt->bc_xprt = args->bc_xprt;
+       bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt);
+       bc_sock->sk_bc_xprt = xprt;
+       transport->sock = bc_sock->sk_sock;
+       transport->inet = bc_sock->sk_sk;
+
+       xprt->ops = &bc_tcp_ops;
+
+       switch (addr->sa_family) {
+       case AF_INET:
+               xs_format_peer_addresses(xprt, "tcp",
+                                        RPCBIND_NETID_TCP);
+               break;
+       case AF_INET6:
+               xs_format_peer_addresses(xprt, "tcp",
+                                  RPCBIND_NETID_TCP6);
+               break;
+       default:
+               kfree(xprt);
+               return ERR_PTR(-EAFNOSUPPORT);
+       }
+
+       if (xprt_bound(xprt))
+               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]);
+       else
+               dprintk("RPC:       set up xprt to %s (autobind) via %s\n",
+                               xprt->address_strings[RPC_DISPLAY_ADDR],
+                               xprt->address_strings[RPC_DISPLAY_PROTO]);
+
+       /*
+        * Since we don't want connections for the backchannel, we set
+        * the xprt status to connected
+        */
+       xprt_set_connected(xprt);
+
+
+       if (try_module_get(THIS_MODULE))
+               return xprt;
+       kfree(xprt->slot);
+       kfree(xprt);
+       return ERR_PTR(-EINVAL);
+}
+
 static struct xprt_class       xs_udp_transport = {
        .list           = LIST_HEAD_INIT(xs_udp_transport.list),
        .name           = "udp",
        .owner          = THIS_MODULE,
-       .ident          = IPPROTO_UDP,
+       .ident          = XPRT_TRANSPORT_UDP,
        .setup          = xs_setup_udp,
 };
 
@@ -2334,10 +2571,18 @@ static struct xprt_class        xs_tcp_transport = {
        .list           = LIST_HEAD_INIT(xs_tcp_transport.list),
        .name           = "tcp",
        .owner          = THIS_MODULE,
-       .ident          = IPPROTO_TCP,
+       .ident          = XPRT_TRANSPORT_TCP,
        .setup          = xs_setup_tcp,
 };
 
+static struct xprt_class       xs_bc_tcp_transport = {
+       .list           = LIST_HEAD_INIT(xs_bc_tcp_transport.list),
+       .name           = "tcp NFSv4.1 backchannel",
+       .owner          = THIS_MODULE,
+       .ident          = XPRT_TRANSPORT_BC_TCP,
+       .setup          = xs_setup_bc_tcp,
+};
+
 /**
  * init_socket_xprt - set up xprtsock's sysctls, register with RPC client
  *
@@ -2351,6 +2596,7 @@ int init_socket_xprt(void)
 
        xprt_register_transport(&xs_udp_transport);
        xprt_register_transport(&xs_tcp_transport);
+       xprt_register_transport(&xs_bc_tcp_transport);
 
        return 0;
 }
@@ -2370,6 +2616,7 @@ void cleanup_socket_xprt(void)
 
        xprt_unregister_transport(&xs_udp_transport);
        xprt_unregister_transport(&xs_tcp_transport);
+       xprt_unregister_transport(&xs_bc_tcp_transport);
 }
 
 static int param_set_uint_minmax(const char *val, struct kernel_param *kp,