Merge commit 'v2.6.30' into for-2.6.31
[safe/jmp/linux-2.6] / net / sunrpc / svcsock.c
index f91377c..004a2f9 100644 (file)
@@ -59,6 +59,7 @@ static void           svc_udp_data_ready(struct sock *, int);
 static int             svc_udp_recvfrom(struct svc_rqst *);
 static int             svc_udp_sendto(struct svc_rqst *);
 static void            svc_sock_detach(struct svc_xprt *);
+static void            svc_tcp_sock_detach(struct svc_xprt *);
 static void            svc_sock_free(struct svc_xprt *);
 
 static struct svc_xprt *svc_create_socket(struct svc_serv *, int,
@@ -102,7 +103,6 @@ static void svc_reclassify_socket(struct socket *sock)
 static void svc_release_skb(struct svc_rqst *rqstp)
 {
        struct sk_buff *skb = rqstp->rq_xprt_ctxt;
-       struct svc_deferred_req *dr = rqstp->rq_deferred;
 
        if (skb) {
                struct svc_sock *svsk =
@@ -112,10 +112,6 @@ static void svc_release_skb(struct svc_rqst *rqstp)
                dprintk("svc: service %p, releasing skb %p\n", rqstp, skb);
                skb_free_datagram(svsk->sk_sk, skb);
        }
-       if (dr) {
-               rqstp->rq_deferred = NULL;
-               kfree(dr);
-       }
 }
 
 union svc_pktinfo_u {
@@ -244,42 +240,76 @@ out:
 /*
  * Report socket names for nfsdfs
  */
-static int one_sock_name(char *buf, struct svc_sock *svsk)
+static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
 {
+       const struct sock *sk = svsk->sk_sk;
+       const char *proto_name = sk->sk_protocol == IPPROTO_UDP ?
+                                                       "udp" : "tcp";
        int len;
 
-       switch(svsk->sk_sk->sk_family) {
-       case AF_INET:
-               len = sprintf(buf, "ipv4 %s %u.%u.%u.%u %d\n",
-                             svsk->sk_sk->sk_protocol==IPPROTO_UDP?
-                             "udp" : "tcp",
-                             NIPQUAD(inet_sk(svsk->sk_sk)->rcv_saddr),
-                             inet_sk(svsk->sk_sk)->num);
+       switch (sk->sk_family) {
+       case PF_INET:
+               len = snprintf(buf, remaining, "ipv4 %s %pI4 %d\n",
+                               proto_name,
+                               &inet_sk(sk)->rcv_saddr,
+                               inet_sk(sk)->num);
+               break;
+       case PF_INET6:
+               len = snprintf(buf, remaining, "ipv6 %s %pI6 %d\n",
+                               proto_name,
+                               &inet6_sk(sk)->rcv_saddr,
+                               inet_sk(sk)->num);
                break;
        default:
-               len = sprintf(buf, "*unknown-%d*\n",
-                              svsk->sk_sk->sk_family);
+               len = snprintf(buf, remaining, "*unknown-%d*\n",
+                               sk->sk_family);
+       }
+
+       if (len >= remaining) {
+               *buf = '\0';
+               return -ENAMETOOLONG;
        }
        return len;
 }
 
-int
-svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
+/**
+ * svc_sock_names - construct a list of listener names in a string
+ * @serv: pointer to RPC service
+ * @buf: pointer to a buffer to fill in with socket names
+ * @buflen: size of the buffer to be filled
+ * @toclose: pointer to '\0'-terminated C string containing the name
+ *             of a listener to be closed
+ *
+ * Fills in @buf with a '\n'-separated list of names of listener
+ * sockets.  If @toclose is not NULL, the socket named by @toclose
+ * is closed, and is not included in the output list.
+ *
+ * Returns positive length of the socket name string, or a negative
+ * errno value on error.
+ */
+int svc_sock_names(struct svc_serv *serv, char *buf, const size_t buflen,
+                  const char *toclose)
 {
        struct svc_sock *svsk, *closesk = NULL;
        int len = 0;
 
        if (!serv)
                return 0;
+
        spin_lock_bh(&serv->sv_lock);
        list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list) {
-               int onelen = one_sock_name(buf+len, svsk);
-               if (toclose && strcmp(toclose, buf+len) == 0)
+               int onelen = svc_one_sock_name(svsk, buf + len, buflen - len);
+               if (onelen < 0) {
+                       len = onelen;
+                       break;
+               }
+               if (toclose && strcmp(toclose, buf + len) == 0)
                        closesk = svsk;
                else
                        len += onelen;
        }
        spin_unlock_bh(&serv->sv_lock);
+
        if (closesk)
                /* Should unregister with portmap, but you cannot
                 * unregister just one protocol...
@@ -289,7 +319,7 @@ svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
                return -ENOENT;
        return len;
 }
-EXPORT_SYMBOL(svc_sock_names);
+EXPORT_SYMBOL_GPL(svc_sock_names);
 
 /*
  * Check input queue length
@@ -431,13 +461,14 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
                long            all[SVC_PKTINFO_SPACE / sizeof(long)];
        } buffer;
        struct cmsghdr *cmh = &buffer.hdr;
-       int             err, len;
        struct msghdr msg = {
                .msg_name = svc_addr(rqstp),
                .msg_control = cmh,
                .msg_controllen = sizeof(buffer),
                .msg_flags = MSG_DONTWAIT,
        };
+       size_t len;
+       int err;
 
        if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags))
            /* udp sockets need large rcvbuf as all pending
@@ -469,8 +500,8 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
                return -EAGAIN;
        }
        len = svc_addr_len(svc_addr(rqstp));
-       if (len < 0)
-               return len;
+       if (len == 0)
+               return -EAFNOSUPPORT;
        rqstp->rq_addrlen = len;
        if (skb->tstamp.tv64 == 0) {
                skb->tstamp = ktime_get_real();
@@ -1017,7 +1048,7 @@ static struct svc_xprt_ops svc_tcp_ops = {
        .xpo_recvfrom = svc_tcp_recvfrom,
        .xpo_sendto = svc_tcp_sendto,
        .xpo_release_rqst = svc_release_skb,
-       .xpo_detach = svc_sock_detach,
+       .xpo_detach = svc_tcp_sock_detach,
        .xpo_free = svc_sock_free,
        .xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr,
        .xpo_has_wspace = svc_tcp_has_wspace,
@@ -1101,7 +1132,7 @@ void svc_sock_update_bufs(struct svc_serv *serv)
        }
        spin_unlock_bh(&serv->sv_lock);
 }
-EXPORT_SYMBOL(svc_sock_update_bufs);
+EXPORT_SYMBOL_GPL(svc_sock_update_bufs);
 
 /*
  * Initialize socket for RPC use and create svc_sock struct
@@ -1114,7 +1145,6 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
        struct svc_sock *svsk;
        struct sock     *inet;
        int             pmap_register = !(flags & SVC_SOCK_ANONYMOUS);
-       int             val;
 
        dprintk("svc: svc_setup_socket %p\n", sock);
        if (!(svsk = kzalloc(sizeof(*svsk), GFP_KERNEL))) {
@@ -1126,7 +1156,7 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
 
        /* Register socket with portmapper */
        if (*errp >= 0 && pmap_register)
-               *errp = svc_register(serv, inet->sk_protocol,
+               *errp = svc_register(serv, inet->sk_family, inet->sk_protocol,
                                     ntohs(inet_sk(inet)->sport));
 
        if (*errp < 0) {
@@ -1147,28 +1177,25 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
        else
                svc_tcp_init(svsk, serv);
 
-       /*
-        * We start one listener per sv_serv.  We want AF_INET
-        * requests to be automatically shunted to our AF_INET6
-        * listener using a mapped IPv4 address.  Make sure
-        * no-one starts an equivalent IPv4 listener, which
-        * would steal our incoming connections.
-        */
-       val = 0;
-       if (serv->sv_family == AF_INET6)
-               kernel_setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
-                                       (char *)&val, sizeof(val));
-
        dprintk("svc: svc_setup_socket created %p (inet %p)\n",
                                svsk, svsk->sk_sk);
 
        return svsk;
 }
 
-int svc_addsock(struct svc_serv *serv,
-               int fd,
-               char *name_return,
-               int *proto)
+/**
+ * svc_addsock - add a listener socket to an RPC service
+ * @serv: pointer to RPC service to which to add a new listener
+ * @fd: file descriptor of the new listener
+ * @name_return: pointer to buffer to fill in with name of listener
+ * @len: size of the buffer
+ *
+ * Fills in socket name and returns positive length of name if successful.
+ * Name is terminated with '\n'.  On error, returns a negative errno
+ * value.
+ */
+int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
+               const size_t len)
 {
        int err = 0;
        struct socket *so = sockfd_lookup(fd, &err);
@@ -1184,7 +1211,11 @@ int svc_addsock(struct svc_serv *serv,
        else if (so->state > SS_UNCONNECTED)
                err = -EISCONN;
        else {
-               svsk = svc_setup_socket(serv, so, &err, SVC_SOCK_DEFAULTS);
+               if (!try_module_get(THIS_MODULE))
+                       err = -ENOENT;
+               else
+                       svsk = svc_setup_socket(serv, so, &err,
+                                               SVC_SOCK_DEFAULTS);
                if (svsk) {
                        struct sockaddr_storage addr;
                        struct sockaddr *sin = (struct sockaddr *)&addr;
@@ -1197,14 +1228,14 @@ int svc_addsock(struct svc_serv *serv,
                        spin_unlock_bh(&serv->sv_lock);
                        svc_xprt_received(&svsk->sk_xprt);
                        err = 0;
-               }
+               } else
+                       module_put(THIS_MODULE);
        }
        if (err) {
                sockfd_put(so);
                return err;
        }
-       if (proto) *proto = so->sk->sk_protocol;
-       return one_sock_name(name_return, svsk);
+       return svc_one_sock_name(svsk, name_return, len);
 }
 EXPORT_SYMBOL_GPL(svc_addsock);
 
@@ -1223,6 +1254,8 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
        struct sockaddr_storage addr;
        struct sockaddr *newsin = (struct sockaddr *)&addr;
        int             newlen;
+       int             family;
+       int             val;
        RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
 
        dprintk("svc: svc_create_socket(%s, %d, %s)\n",
@@ -1234,14 +1267,35 @@ static struct svc_xprt *svc_create_socket(struct svc_serv *serv,
                                "sockets supported\n");
                return ERR_PTR(-EINVAL);
        }
+
        type = (protocol == IPPROTO_UDP)? SOCK_DGRAM : SOCK_STREAM;
+       switch (sin->sa_family) {
+       case AF_INET6:
+               family = PF_INET6;
+               break;
+       case AF_INET:
+               family = PF_INET;
+               break;
+       default:
+               return ERR_PTR(-EINVAL);
+       }
 
-       error = sock_create_kern(sin->sa_family, type, protocol, &sock);
+       error = sock_create_kern(family, type, protocol, &sock);
        if (error < 0)
                return ERR_PTR(error);
 
        svc_reclassify_socket(sock);
 
+       /*
+        * If this is an PF_INET6 listener, we want to avoid
+        * getting requests from IPv4 remotes.  Those should
+        * be shunted to a PF_INET listener via rpcbind.
+        */
+       val = 1;
+       if (family == PF_INET6)
+               kernel_setsockopt(sock, SOL_IPV6, IPV6_V6ONLY,
+                                       (char *)&val, sizeof(val));
+
        if (type == SOCK_STREAM)
                sock->sk->sk_reuse = 1;         /* allow address reuse */
        error = kernel_bind(sock, sin, len);
@@ -1284,6 +1338,24 @@ static void svc_sock_detach(struct svc_xprt *xprt)
        sk->sk_state_change = svsk->sk_ostate;
        sk->sk_data_ready = svsk->sk_odata;
        sk->sk_write_space = svsk->sk_owspace;
+
+       if (sk->sk_sleep && waitqueue_active(sk->sk_sleep))
+               wake_up_interruptible(sk->sk_sleep);
+}
+
+/*
+ * Disconnect the socket, and reset the callbacks
+ */
+static void svc_tcp_sock_detach(struct svc_xprt *xprt)
+{
+       struct svc_sock *svsk = container_of(xprt, struct svc_sock, sk_xprt);
+
+       dprintk("svc: svc_tcp_sock_detach(%p)\n", svsk);
+
+       svc_sock_detach(xprt);
+
+       if (!test_bit(XPT_LISTENER, &xprt->xpt_flags))
+               kernel_sock_shutdown(svsk->sk_sock, SHUT_RDWR);
 }
 
 /*