Example #1
0
int rxrpc_recvmsg(struct kiocb *iocb, struct socket *sock,
		  struct msghdr *msg, size_t len, int flags)
{
	struct rxrpc_skb_priv *sp;
	struct rxrpc_call *call = NULL, *continue_call = NULL;
	struct rxrpc_sock *rx = rxrpc_sk(sock->sk);
	struct sk_buff *skb;
	long timeo;
	int copy, ret, ullen, offset, copied = 0;
	u32 abort_code;

	DEFINE_WAIT(wait);

	_enter(",,,%zu,%d", len, flags);

	if (flags & (MSG_OOB | MSG_TRUNC))
		return -EOPNOTSUPP;

	ullen = msg->msg_flags & MSG_CMSG_COMPAT ? 4 : sizeof(unsigned long);

	timeo = sock_rcvtimeo(&rx->sk, flags & MSG_DONTWAIT);
	msg->msg_flags |= MSG_MORE;

	lock_sock(&rx->sk);

	for (;;) {
		/* return immediately if a client socket has no outstanding
		 * calls */
		if (RB_EMPTY_ROOT(&rx->calls)) {
			if (copied)
				goto out;
			if (rx->sk.sk_state != RXRPC_SERVER_LISTENING) {
				release_sock(&rx->sk);
				if (continue_call)
					rxrpc_put_call(continue_call);
				return -ENODATA;
			}
		}

		/* get the next message on the Rx queue */
		skb = skb_peek(&rx->sk.sk_receive_queue);
		if (!skb) {
			/* nothing remains on the queue */
			if (copied &&
			    (msg->msg_flags & MSG_PEEK || timeo == 0))
				goto out;

			/* wait for a message to turn up */
			release_sock(&rx->sk);
			prepare_to_wait_exclusive(sk_sleep(&rx->sk), &wait,
						  TASK_INTERRUPTIBLE);
			ret = sock_error(&rx->sk);
			if (ret)
				goto wait_error;

			if (skb_queue_empty(&rx->sk.sk_receive_queue)) {
				if (signal_pending(current))
					goto wait_interrupted;
				timeo = schedule_timeout(timeo);
			}
			finish_wait(sk_sleep(&rx->sk), &wait);
			lock_sock(&rx->sk);
			continue;
		}

	peek_next_packet:
		sp = rxrpc_skb(skb);
		call = sp->call;
		ASSERT(call != NULL);

		_debug("next pkt %s", rxrpc_pkts[sp->hdr.type]);

		/* make sure we wait for the state to be updated in this call */
		spin_lock_bh(&call->lock);
		spin_unlock_bh(&call->lock);

		if (test_bit(RXRPC_CALL_RELEASED, &call->flags)) {
			_debug("packet from released call");
			if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
				BUG();
			rxrpc_free_skb(skb);
			continue;
		}

		/* determine whether to continue last data receive */
		if (continue_call) {
			_debug("maybe cont");
			if (call != continue_call ||
			    skb->mark != RXRPC_SKB_MARK_DATA) {
				release_sock(&rx->sk);
				rxrpc_put_call(continue_call);
				_leave(" = %d [noncont]", copied);
				return copied;
			}
		}

		rxrpc_get_call(call);

		/* copy the peer address and timestamp */
		if (!continue_call) {
			if (msg->msg_name && msg->msg_namelen > 0)
				memcpy(msg->msg_name,
				       &call->conn->trans->peer->srx,
				       sizeof(call->conn->trans->peer->srx));
			sock_recv_ts_and_drops(msg, &rx->sk, skb);
		}

		/* receive the message */
		if (skb->mark != RXRPC_SKB_MARK_DATA)
			goto receive_non_data_message;

		_debug("recvmsg DATA #%u { %d, %d }",
		       ntohl(sp->hdr.seq), skb->len, sp->offset);

		if (!continue_call) {
			/* only set the control data once per recvmsg() */
			ret = put_cmsg(msg, SOL_RXRPC, RXRPC_USER_CALL_ID,
				       ullen, &call->user_call_ID);
			if (ret < 0)
				goto copy_error;
			ASSERT(test_bit(RXRPC_CALL_HAS_USERID, &call->flags));
		}

		ASSERTCMP(ntohl(sp->hdr.seq), >=, call->rx_data_recv);
		ASSERTCMP(ntohl(sp->hdr.seq), <=, call->rx_data_recv + 1);
		call->rx_data_recv = ntohl(sp->hdr.seq);

		ASSERTCMP(ntohl(sp->hdr.seq), >, call->rx_data_eaten);

		offset = sp->offset;
		copy = skb->len - offset;
		if (copy > len - copied)
			copy = len - copied;

		if (skb->ip_summed == CHECKSUM_UNNECESSARY) {
			ret = skb_copy_datagram_iovec(skb, offset,
						      msg->msg_iov, copy);
		} else {
			ret = skb_copy_and_csum_datagram_iovec(skb, offset,
							       msg->msg_iov);
			if (ret == -EINVAL)
				goto csum_copy_error;
		}

		if (ret < 0)
			goto copy_error;

		/* handle piecemeal consumption of data packets */
		_debug("copied %d+%d", copy, copied);

		offset += copy;
		copied += copy;

		if (!(flags & MSG_PEEK))
			sp->offset = offset;

		if (sp->offset < skb->len) {
			_debug("buffer full");
			ASSERTCMP(copied, ==, len);
			break;
		}

		/* we transferred the whole data packet */
		if (sp->hdr.flags & RXRPC_LAST_PACKET) {
			_debug("last");
			if (call->conn->out_clientflag) {
				 /* last byte of reply received */
				ret = copied;
				goto terminal_message;
			}

			/* last bit of request received */
			if (!(flags & MSG_PEEK)) {
				_debug("eat packet");
				if (skb_dequeue(&rx->sk.sk_receive_queue) !=
				    skb)
					BUG();
				rxrpc_free_skb(skb);
			}
			msg->msg_flags &= ~MSG_MORE;
			break;
		}

		/* move on to the next data message */
		_debug("next");
		if (!continue_call)
			continue_call = sp->call;
		else
			rxrpc_put_call(call);
		call = NULL;

		if (flags & MSG_PEEK) {
			_debug("peek next");
			skb = skb->next;
			if (skb == (struct sk_buff *) &rx->sk.sk_receive_queue)
				break;
			goto peek_next_packet;
		}

		_debug("eat packet");
		if (skb_dequeue(&rx->sk.sk_receive_queue) != skb)
			BUG();
		rxrpc_free_skb(skb);
	}
Example #2
0
int udpv6_recvmsg(struct sock *sk, struct msghdr *msg, int len,
                  int noblock, int flags, int *addr_len)
{
    struct sk_buff *skb;
    int copied, err;

    if (addr_len)
        *addr_len=sizeof(struct sockaddr_in6);

    if (flags & MSG_ERRQUEUE)
        return ipv6_recv_error(sk, msg, len);

    skb = skb_recv_datagram(sk, flags, noblock, &err);
    if (!skb)
        goto out;

    copied = skb->len - sizeof(struct udphdr);
    if (copied > len) {
        copied = len;
        msg->msg_flags |= MSG_TRUNC;
    }

    if (skb->ip_summed==CHECKSUM_UNNECESSARY) {
        err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
                                      copied);
    } else if (msg->msg_flags&MSG_TRUNC) {
        if ((unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum)))
            goto csum_copy_err;
        err = skb_copy_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov,
                                      copied);
    } else {
        err = skb_copy_and_csum_datagram_iovec(skb, sizeof(struct udphdr), msg->msg_iov);
        if (err == -EINVAL)
            goto csum_copy_err;
    }
    if (err)
        goto out_free;

    sock_recv_timestamp(msg, sk, skb);

    /* Copy the address. */
    if (msg->msg_name) {
        struct sockaddr_in6 *sin6;

        sin6 = (struct sockaddr_in6 *) msg->msg_name;
        sin6->sin6_family = AF_INET6;
        sin6->sin6_port = skb->h.uh->source;
        sin6->sin6_flowinfo = 0;
        sin6->sin6_scope_id = 0;

        if (skb->protocol == __constant_htons(ETH_P_IP)) {
            ipv6_addr_set(&sin6->sin6_addr, 0, 0,
                          __constant_htonl(0xffff), skb->nh.iph->saddr);
            if (sk->protinfo.af_inet.cmsg_flags)
                ip_cmsg_recv(msg, skb);
        } else {
            memcpy(&sin6->sin6_addr, &skb->nh.ipv6h->saddr,
                   sizeof(struct in6_addr));

            if (sk->net_pinfo.af_inet6.rxopt.all)
                datagram_recv_ctl(sk, msg, skb);
            if (ipv6_addr_type(&sin6->sin6_addr) & IPV6_ADDR_LINKLOCAL) {
                struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb;
                sin6->sin6_scope_id = opt->iif;
            }
        }
    }
    err = copied;

out_free:
    skb_free_datagram(sk, skb);
out:
    return err;

csum_copy_err:
    /* Clear queue. */
    if (flags&MSG_PEEK) {
        int clear = 0;
        spin_lock_irq(&sk->receive_queue.lock);
        if (skb == skb_peek(&sk->receive_queue)) {
            __skb_unlink(skb, &sk->receive_queue);
            clear = 1;
        }
        spin_unlock_irq(&sk->receive_queue.lock);
        if (clear)
            kfree_skb(skb);
    }

    /* Error for blocking case is chosen to masquerade
       as some normal condition.
     */
    err = (flags&MSG_DONTWAIT) ? -EAGAIN : -EHOSTUNREACH;
    UDP6_INC_STATS_USER(UdpInErrors);
    goto out_free;
}