|
@@ -912,6 +912,39 @@ static inline int select_size(const struct sock *sk, bool sg)
|
|
|
return tmp;
|
|
|
}
|
|
|
|
|
|
+static int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ struct tcp_skb_cb *cb;
|
|
|
+ struct tcphdr *th;
|
|
|
+
|
|
|
+ skb = alloc_skb(size + sizeof(*th), sk->sk_allocation);
|
|
|
+ if (!skb)
|
|
|
+ goto err;
|
|
|
+
|
|
|
+ th = (struct tcphdr *)skb_put(skb, sizeof(*th));
|
|
|
+ skb_reset_transport_header(skb);
|
|
|
+ memset(th, 0, sizeof(*th));
|
|
|
+
|
|
|
+ if (memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size))
|
|
|
+ goto err_free;
|
|
|
+
|
|
|
+ cb = TCP_SKB_CB(skb);
|
|
|
+
|
|
|
+ TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt;
|
|
|
+ TCP_SKB_CB(skb)->end_seq = TCP_SKB_CB(skb)->seq + size;
|
|
|
+ TCP_SKB_CB(skb)->ack_seq = tcp_sk(sk)->snd_una - 1;
|
|
|
+
|
|
|
+ tcp_queue_rcv(sk, skb, sizeof(*th));
|
|
|
+
|
|
|
+ return size;
|
|
|
+
|
|
|
+err_free:
|
|
|
+ kfree_skb(skb);
|
|
|
+err:
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|
|
size_t size)
|
|
|
{
|
|
@@ -933,6 +966,19 @@ int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|
|
if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
|
|
|
goto out_err;
|
|
|
|
|
|
+ if (unlikely(tp->repair)) {
|
|
|
+ if (tp->repair_queue == TCP_RECV_QUEUE) {
|
|
|
+ copied = tcp_send_rcvq(sk, msg, size);
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = -EINVAL;
|
|
|
+ if (tp->repair_queue == TCP_NO_QUEUE)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ /* 'common' sending to sendq */
|
|
|
+ }
|
|
|
+
|
|
|
/* This should be in poll */
|
|
|
clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
|
|
|
|
|
@@ -1089,7 +1135,7 @@ new_segment:
|
|
|
if ((seglen -= copy) == 0 && iovlen == 0)
|
|
|
goto out;
|
|
|
|
|
|
- if (skb->len < max || (flags & MSG_OOB))
|
|
|
+ if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
|
|
|
continue;
|
|
|
|
|
|
if (forced_push(tp)) {
|
|
@@ -1102,7 +1148,7 @@ new_segment:
|
|
|
wait_for_sndbuf:
|
|
|
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
|
|
|
wait_for_memory:
|
|
|
- if (copied)
|
|
|
+ if (copied && likely(!tp->repair))
|
|
|
tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
|
|
|
|
|
|
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
|
|
@@ -1113,7 +1159,7 @@ wait_for_memory:
|
|
|
}
|
|
|
|
|
|
out:
|
|
|
- if (copied)
|
|
|
+ if (copied && likely(!tp->repair))
|
|
|
tcp_push(sk, flags, mss_now, tp->nonagle);
|
|
|
release_sock(sk);
|
|
|
return copied;
|
|
@@ -1187,6 +1233,24 @@ static int tcp_recv_urg(struct sock *sk, struct msghdr *msg, int len, int flags)
|
|
|
return -EAGAIN;
|
|
|
}
|
|
|
|
|
|
+static int tcp_peek_sndq(struct sock *sk, struct msghdr *msg, int len)
|
|
|
+{
|
|
|
+ struct sk_buff *skb;
|
|
|
+ int copied = 0, err = 0;
|
|
|
+
|
|
|
+ /* XXX -- need to support SO_PEEK_OFF */
|
|
|
+
|
|
|
+ skb_queue_walk(&sk->sk_write_queue, skb) {
|
|
|
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, skb->len);
|
|
|
+ if (err)
|
|
|
+ break;
|
|
|
+
|
|
|
+ copied += skb->len;
|
|
|
+ }
|
|
|
+
|
|
|
+ return err ?: copied;
|
|
|
+}
|
|
|
+
|
|
|
/* Clean up the receive buffer for full frames taken by the user,
|
|
|
* then send an ACK if necessary. COPIED is the number of bytes
|
|
|
* tcp_recvmsg has given to the user so far, it speeds up the
|
|
@@ -1432,6 +1496,21 @@ int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
|
|
|
if (flags & MSG_OOB)
|
|
|
goto recv_urg;
|
|
|
|
|
|
+ if (unlikely(tp->repair)) {
|
|
|
+ err = -EPERM;
|
|
|
+ if (!(flags & MSG_PEEK))
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ if (tp->repair_queue == TCP_SEND_QUEUE)
|
|
|
+ goto recv_sndq;
|
|
|
+
|
|
|
+ err = -EINVAL;
|
|
|
+ if (tp->repair_queue == TCP_NO_QUEUE)
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ /* 'common' recv queue MSG_PEEK-ing */
|
|
|
+ }
|
|
|
+
|
|
|
seq = &tp->copied_seq;
|
|
|
if (flags & MSG_PEEK) {
|
|
|
peek_seq = tp->copied_seq;
|
|
@@ -1783,6 +1862,10 @@ out:
|
|
|
recv_urg:
|
|
|
err = tcp_recv_urg(sk, msg, len, flags);
|
|
|
goto out;
|
|
|
+
|
|
|
+recv_sndq:
|
|
|
+ err = tcp_peek_sndq(sk, msg, len);
|
|
|
+ goto out;
|
|
|
}
|
|
|
EXPORT_SYMBOL(tcp_recvmsg);
|
|
|
|