diff options
Diffstat (limited to 'net/sunrpc/svcsock.c')
-rw-r--r-- | net/sunrpc/svcsock.c | 123 |
1 files changed, 71 insertions, 52 deletions
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 63ae94771b8e..22f61aee4824 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -82,6 +82,7 @@ static void svc_delete_socket(struct svc_sock *svsk); 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_close_socket(struct svc_sock *svsk); static struct svc_deferred_req *svc_deferred_dequeue(struct svc_sock *svsk); static int svc_deferred_recv(struct svc_rqst *rqstp); @@ -131,13 +132,13 @@ static char *__svc_print_addr(struct sockaddr *addr, char *buf, size_t len) NIPQUAD(((struct sockaddr_in *) addr)->sin_addr), htons(((struct sockaddr_in *) addr)->sin_port)); break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: snprintf(buf, len, "%x:%x:%x:%x:%x:%x:%x:%x, port=%u", NIP6(((struct sockaddr_in6 *) addr)->sin6_addr), htons(((struct sockaddr_in6 *) addr)->sin6_port)); break; -#endif + default: snprintf(buf, len, "unknown address type: %d", addr->sa_family); break; @@ -449,10 +450,10 @@ svc_wake_up(struct svc_serv *serv) union svc_pktinfo_u { struct in_pktinfo pkti; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) struct in6_pktinfo pkti6; -#endif }; +#define SVC_PKTINFO_SPACE \ + CMSG_SPACE(sizeof(union svc_pktinfo_u)) static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh) { @@ -467,7 +468,7 @@ static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh) cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); } break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) + case AF_INET6: { struct in6_pktinfo *pki = CMSG_DATA(cmh); @@ -479,7 +480,6 @@ static void svc_set_cmsg_data(struct svc_rqst *rqstp, struct cmsghdr *cmh) cmh->cmsg_len = CMSG_LEN(sizeof(*pki)); } break; -#endif } return; } @@ -493,8 +493,11 @@ svc_sendto(struct svc_rqst *rqstp, struct xdr_buf *xdr) struct svc_sock *svsk = rqstp->rq_sock; struct socket *sock = svsk->sk_sock; int slen; - char buffer[CMSG_SPACE(sizeof(union svc_pktinfo_u))]; - struct cmsghdr *cmh = (struct cmsghdr *)buffer; + union { + struct cmsghdr hdr; + long all[SVC_PKTINFO_SPACE / sizeof(long)]; + } buffer; + struct cmsghdr *cmh = &buffer.hdr; int len = 0; int result; int size; @@ -721,45 +724,21 @@ svc_write_space(struct sock *sk) } } -static void svc_udp_get_sender_address(struct svc_rqst *rqstp, - struct sk_buff *skb) +static inline void svc_udp_get_dest_address(struct svc_rqst *rqstp, + struct cmsghdr *cmh) { switch (rqstp->rq_sock->sk_sk->sk_family) { case AF_INET: { - /* this seems to come from net/ipv4/udp.c:udp_recvmsg */ - struct sockaddr_in *sin = svc_addr_in(rqstp); - - sin->sin_family = AF_INET; - sin->sin_port = skb->h.uh->source; - sin->sin_addr.s_addr = skb->nh.iph->saddr; - rqstp->rq_addrlen = sizeof(struct sockaddr_in); - /* Remember which interface received this request */ - rqstp->rq_daddr.addr.s_addr = skb->nh.iph->daddr; - } + struct in_pktinfo *pki = CMSG_DATA(cmh); + rqstp->rq_daddr.addr.s_addr = pki->ipi_spec_dst.s_addr; break; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - case AF_INET6: { - /* this is derived from net/ipv6/udp.c:udpv6_recvmesg */ - struct sockaddr_in6 *sin6 = svc_addr_in6(rqstp); - - sin6->sin6_family = AF_INET6; - sin6->sin6_port = skb->h.uh->source; - sin6->sin6_flowinfo = 0; - sin6->sin6_scope_id = 0; - if (ipv6_addr_type(&sin6->sin6_addr) & - IPV6_ADDR_LINKLOCAL) - sin6->sin6_scope_id = IP6CB(skb)->iif; - ipv6_addr_copy(&sin6->sin6_addr, - &skb->nh.ipv6h->saddr); - rqstp->rq_addrlen = sizeof(struct sockaddr_in); - /* Remember which interface received this request */ - ipv6_addr_copy(&rqstp->rq_daddr.addr6, - &skb->nh.ipv6h->saddr); } + case AF_INET6: { + struct in6_pktinfo *pki = CMSG_DATA(cmh); + ipv6_addr_copy(&rqstp->rq_daddr.addr6, &pki->ipi6_addr); break; -#endif + } } - return; } /* @@ -771,7 +750,18 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) struct svc_sock *svsk = rqstp->rq_sock; struct svc_serv *serv = svsk->sk_server; struct sk_buff *skb; + union { + struct cmsghdr hdr; + 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, + }; if (test_and_clear_bit(SK_CHNGBUF, &svsk->sk_flags)) /* udp sockets need large rcvbuf as all pending @@ -797,7 +787,9 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) } clear_bit(SK_DATA, &svsk->sk_flags); - while ((skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { + while ((err = kernel_recvmsg(svsk->sk_sock, &msg, NULL, + 0, 0, MSG_PEEK | MSG_DONTWAIT)) < 0 || + (skb = skb_recv_datagram(svsk->sk_sk, 0, 1, &err)) == NULL) { if (err == -EAGAIN) { svc_sock_received(svsk); return err; @@ -805,16 +797,13 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) /* possibly an icmp error */ dprintk("svc: recvfrom returned error %d\n", -err); } - if (skb->tstamp.off_sec == 0) { - struct timeval tv; - - tv.tv_sec = xtime.tv_sec; - tv.tv_usec = xtime.tv_nsec / NSEC_PER_USEC; - skb_set_timestamp(skb, &tv); + rqstp->rq_addrlen = sizeof(rqstp->rq_addr); + if (skb->tstamp.tv64 == 0) { + skb->tstamp = ktime_get_real(); /* Don't enable netstamp, sunrpc doesn't need that much accuracy */ } - skb_get_timestamp(skb, &svsk->sk_sk->sk_stamp); + svsk->sk_sk->sk_stamp = skb->tstamp; set_bit(SK_DATA, &svsk->sk_flags); /* there may be more data... */ /* @@ -827,7 +816,16 @@ svc_udp_recvfrom(struct svc_rqst *rqstp) rqstp->rq_prot = IPPROTO_UDP; - svc_udp_get_sender_address(rqstp, skb); + if (cmh->cmsg_level != IPPROTO_IP || + cmh->cmsg_type != IP_PKTINFO) { + if (net_ratelimit()) + printk("rpcsvc: received unknown control message:" + "%d/%d\n", + cmh->cmsg_level, cmh->cmsg_type); + skb_free_datagram(svsk->sk_sk, skb); + return 0; + } + svc_udp_get_dest_address(rqstp, cmh); if (skb_is_nonlinear(skb)) { /* we have to copy */ @@ -884,6 +882,9 @@ svc_udp_sendto(struct svc_rqst *rqstp) static void svc_udp_init(struct svc_sock *svsk) { + int one = 1; + mm_segment_t oldfs; + svsk->sk_sk->sk_data_ready = svc_udp_data_ready; svsk->sk_sk->sk_write_space = svc_write_space; svsk->sk_recvfrom = svc_udp_recvfrom; @@ -899,6 +900,13 @@ svc_udp_init(struct svc_sock *svsk) set_bit(SK_DATA, &svsk->sk_flags); /* might have come in before data_ready set up */ set_bit(SK_CHNGBUF, &svsk->sk_flags); + + oldfs = get_fs(); + set_fs(KERNEL_DS); + /* make sure we get destination address info */ + svsk->sk_sock->ops->setsockopt(svsk->sk_sock, IPPROTO_IP, IP_PKTINFO, + (char __user *)&one, sizeof(one)); + set_fs(oldfs); } /* @@ -977,11 +985,9 @@ static inline int svc_port_is_privileged(struct sockaddr *sin) case AF_INET: return ntohs(((struct sockaddr_in *)sin)->sin_port) < PROT_SOCK; -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) case AF_INET6: return ntohs(((struct sockaddr_in6 *)sin)->sin6_port) < PROT_SOCK; -#endif default: return 0; } @@ -1786,7 +1792,7 @@ svc_delete_socket(struct svc_sock *svsk) spin_unlock_bh(&serv->sv_lock); } -void svc_close_socket(struct svc_sock *svsk) +static void svc_close_socket(struct svc_sock *svsk) { set_bit(SK_CLOSE, &svsk->sk_flags); if (test_and_set_bit(SK_BUSY, &svsk->sk_flags)) @@ -1799,6 +1805,19 @@ void svc_close_socket(struct svc_sock *svsk) svc_sock_put(svsk); } +void svc_force_close_socket(struct svc_sock *svsk) +{ + set_bit(SK_CLOSE, &svsk->sk_flags); + if (test_bit(SK_BUSY, &svsk->sk_flags)) { + /* Waiting to be processed, but no threads left, + * So just remove it from the waiting list + */ + list_del_init(&svsk->sk_ready); + clear_bit(SK_BUSY, &svsk->sk_flags); + } + svc_close_socket(svsk); +} + /** * svc_makesock - Make a socket for nfsd and lockd * @serv: RPC server structure |