SUNRPC: Use IPv4 loopback for registering AF_INET6 kernel RPC services
authorChuck Lever <chuck.lever@oracle.com>
Thu, 19 Mar 2009 00:46:51 +0000 (20:46 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Sat, 28 Mar 2009 19:55:28 +0000 (15:55 -0400)
The kernel uses an IPv6 loopback address when registering its AF_INET6
RPC services so that it can tell whether the local portmapper is
actually IPv6-enabled.

Since the legacy portmapper doesn't listen on IPv6, however, this
causes a long timeout on older systems if the kernel happens to try
creating and registering an AF_INET6 RPC service.  Originally I wanted
to use a connected transport (either TCP or connected UDP) so that the
upcall would fail immediately if the portmapper wasn't listening on
IPv6, but we never agreed on what transport to use.

In the end, it's of little consequence to the kernel whether the local
portmapper is listening on IPv6.  It's only important whether the
portmapper supports rpcbind v4.  And the kernel can't tell that at all
if it is sending requests via IPv6 -- the portmapper will just ignore
them.

So, send both rpcbind v2 and v4 SET/UNSET requests via IPv4 loopback
to maintain better backwards compatibility between new kernels and
legacy user space, and prevent multi-second hangs in some cases when
the kernel attempts to register RPC services.

This patch is part of a series that addresses

   http://bugzilla.kernel.org/show_bug.cgi?id=12256

Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
net/sunrpc/rpcb_clnt.c

index 2caa7ed..ebce7a5 100644 (file)
@@ -124,12 +124,6 @@ static const struct sockaddr_in rpcb_inaddr_loopback = {
        .sin_port               = htons(RPCBIND_PORT),
 };
 
-static const struct sockaddr_in6 rpcb_in6addr_loopback = {
-       .sin6_family            = AF_INET6,
-       .sin6_addr              = IN6ADDR_LOOPBACK_INIT,
-       .sin6_port              = htons(RPCBIND_PORT),
-};
-
 static struct rpc_clnt *rpcb_create_local(struct sockaddr *addr,
                                          size_t addrlen, u32 version)
 {
@@ -176,9 +170,10 @@ static struct rpc_clnt *rpcb_create(char *hostname, struct sockaddr *srvaddr,
        return rpc_create(&args);
 }
 
-static int rpcb_register_call(struct sockaddr *addr, size_t addrlen,
-                             u32 version, struct rpc_message *msg)
+static int rpcb_register_call(u32 version, struct rpc_message *msg)
 {
+       struct sockaddr *addr = (struct sockaddr *)&rpcb_inaddr_loopback;
+       size_t addrlen = sizeof(rpcb_inaddr_loopback);
        struct rpc_clnt *rpcb_clnt;
        int result, error = 0;
 
@@ -254,9 +249,7 @@ int rpcb_register(u32 prog, u32 vers, int prot, unsigned short port)
        if (port)
                msg.rpc_proc = &rpcb_procedures2[RPCBPROC_SET];
 
-       return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
-                                       sizeof(rpcb_inaddr_loopback),
-                                       RPCBVERS_2, &msg);
+       return rpcb_register_call(RPCBVERS_2, &msg);
 }
 
 /*
@@ -284,9 +277,7 @@ static int rpcb_register_netid4(struct sockaddr_in *address_to_register,
        if (port)
                msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET];
 
-       return rpcb_register_call((struct sockaddr *)&rpcb_inaddr_loopback,
-                                       sizeof(rpcb_inaddr_loopback),
-                                       RPCBVERS_4, msg);
+       return rpcb_register_call(RPCBVERS_4, msg);
 }
 
 /*
@@ -318,9 +309,7 @@ static int rpcb_register_netid6(struct sockaddr_in6 *address_to_register,
        if (port)
                msg->rpc_proc = &rpcb_procedures4[RPCBPROC_SET];
 
-       return rpcb_register_call((struct sockaddr *)&rpcb_in6addr_loopback,
-                                       sizeof(rpcb_in6addr_loopback),
-                                       RPCBVERS_4, msg);
+       return rpcb_register_call(RPCBVERS_4, msg);
 }
 
 /**