Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=67a391d72ca7efb387c30ec761a487e50a3ff085
Commit:     67a391d72ca7efb387c30ec761a487e50a3ff085
Parent:     66af1e558538137080615e7ad6d1f2f80862de01
Author:     Trond Myklebust <[EMAIL PROTECTED]>
AuthorDate: Mon Nov 5 17:40:58 2007 -0500
Committer:  Trond Myklebust <[EMAIL PROTECTED]>
CommitDate: Wed Jan 30 02:05:25 2008 -0500

    SUNRPC: Fix TCP rebinding logic
    
    Currently the TCP rebinding logic assumes that if we're not using a
    reserved port, then we don't need to reconnect on the same port if a
    disconnection event occurs. This breaks most RPC duplicate reply cache
    implementations.
    
    Also take into account the fact that xprt_min_resvport and
    xprt_max_resvport may change while we're reconnecting, since the user may
    change them at any time via the sysctls. Ensure that we check the port
    boundaries every time we loop in xs_bind4/xs_bind6. Also ensure that if the
    boundaries change, we only scan the ports a maximum of 2 times.
    
    Signed-off-by: Trond Myklebust <[EMAIL PROTECTED]>
---
 net/sunrpc/xprtsock.c |   59 +++++++++++++++++++++++++++++++-----------------
 1 files changed, 38 insertions(+), 21 deletions(-)

diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c
index abb40c1..b554451 100644
--- a/net/sunrpc/xprtsock.c
+++ b/net/sunrpc/xprtsock.c
@@ -1276,34 +1276,53 @@ static void xs_set_port(struct rpc_xprt *xprt, unsigned 
short port)
        }
 }
 
+static unsigned short xs_get_srcport(struct sock_xprt *transport, struct 
socket *sock)
+{
+       unsigned short port = transport->port;
+
+       if (port == 0 && transport->xprt.resvport)
+               port = xs_get_random_port();
+       return port;
+}
+
+static unsigned short xs_next_srcport(struct sock_xprt *transport, struct 
socket *sock, unsigned short port)
+{
+       if (transport->port != 0)
+               transport->port = 0;
+       if (!transport->xprt.resvport)
+               return 0;
+       if (port <= xprt_min_resvport || port > xprt_max_resvport)
+               return xprt_max_resvport;
+       return --port;
+}
+
 static int xs_bind4(struct sock_xprt *transport, struct socket *sock)
 {
        struct sockaddr_in myaddr = {
                .sin_family = AF_INET,
        };
        struct sockaddr_in *sa;
-       int err;
-       unsigned short port = transport->port;
+       int err, nloop = 0;
+       unsigned short port = xs_get_srcport(transport, sock);
+       unsigned short last;
 
-       if (!transport->xprt.resvport)
-               port = 0;
        sa = (struct sockaddr_in *)&transport->addr;
        myaddr.sin_addr = sa->sin_addr;
        do {
                myaddr.sin_port = htons(port);
                err = kernel_bind(sock, (struct sockaddr *) &myaddr,
                                                sizeof(myaddr));
-               if (!transport->xprt.resvport)
+               if (port == 0)
                        break;
                if (err == 0) {
                        transport->port = port;
                        break;
                }
-               if (port <= xprt_min_resvport)
-                       port = xprt_max_resvport;
-               else
-                       port--;
-       } while (err == -EADDRINUSE && port != transport->port);
+               last = port;
+               port = xs_next_srcport(transport, sock, port);
+               if (port > last)
+                       nloop++;
+       } while (err == -EADDRINUSE && nloop != 2);
        dprintk("RPC:       %s "NIPQUAD_FMT":%u: %s (%d)\n",
                        __FUNCTION__, NIPQUAD(myaddr.sin_addr),
                        port, err ? "failed" : "ok", err);
@@ -1316,28 +1335,27 @@ static int xs_bind6(struct sock_xprt *transport, struct 
socket *sock)
                .sin6_family = AF_INET6,
        };
        struct sockaddr_in6 *sa;
-       int err;
-       unsigned short port = transport->port;
+       int err, nloop = 0;
+       unsigned short port = xs_get_srcport(transport, sock);
+       unsigned short last;
 
-       if (!transport->xprt.resvport)
-               port = 0;
        sa = (struct sockaddr_in6 *)&transport->addr;
        myaddr.sin6_addr = sa->sin6_addr;
        do {
                myaddr.sin6_port = htons(port);
                err = kernel_bind(sock, (struct sockaddr *) &myaddr,
                                                sizeof(myaddr));
-               if (!transport->xprt.resvport)
+               if (port == 0)
                        break;
                if (err == 0) {
                        transport->port = port;
                        break;
                }
-               if (port <= xprt_min_resvport)
-                       port = xprt_max_resvport;
-               else
-                       port--;
-       } while (err == -EADDRINUSE && port != transport->port);
+               last = port;
+               port = xs_next_srcport(transport, sock, port);
+               if (port > last)
+                       nloop++;
+       } while (err == -EADDRINUSE && nloop != 2);
        dprintk("RPC:       xs_bind6 "NIP6_FMT":%u: %s (%d)\n",
                NIP6(myaddr.sin6_addr), port, err ? "failed" : "ok", err);
        return err;
@@ -1819,7 +1837,6 @@ static struct rpc_xprt *xs_setup_xprt(struct xprt_create 
*args,
        xprt->addrlen = args->addrlen;
        if (args->srcaddr)
                memcpy(&new->addr, args->srcaddr, args->addrlen);
-       new->port = xs_get_random_port();
 
        return xprt;
 }
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to