|
@@ -4481,19 +4481,34 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int
|
|
|
int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
|
|
|
{
|
|
|
struct sk_buff *skb;
|
|
|
+ int err = -ENOMEM;
|
|
|
+ int data_len = 0;
|
|
|
bool fragstolen;
|
|
|
|
|
|
if (size == 0)
|
|
|
return 0;
|
|
|
|
|
|
- skb = alloc_skb(size, sk->sk_allocation);
|
|
|
+ if (size > PAGE_SIZE) {
|
|
|
+ int npages = min_t(size_t, size >> PAGE_SHIFT, MAX_SKB_FRAGS);
|
|
|
+
|
|
|
+ data_len = npages << PAGE_SHIFT;
|
|
|
+ size = data_len + (size & ~PAGE_MASK);
|
|
|
+ }
|
|
|
+ skb = alloc_skb_with_frags(size - data_len, data_len,
|
|
|
+ PAGE_ALLOC_COSTLY_ORDER,
|
|
|
+ &err, sk->sk_allocation);
|
|
|
if (!skb)
|
|
|
goto err;
|
|
|
|
|
|
+ skb_put(skb, size - data_len);
|
|
|
+ skb->data_len = data_len;
|
|
|
+ skb->len = size;
|
|
|
+
|
|
|
if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
|
|
|
goto err_free;
|
|
|
|
|
|
- if (memcpy_from_msg(skb_put(skb, size), msg, size))
|
|
|
+ err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, size);
|
|
|
+ if (err)
|
|
|
goto err_free;
|
|
|
|
|
|
TCP_SKB_CB(skb)->seq = tcp_sk(sk)->rcv_nxt;
|
|
@@ -4509,7 +4524,8 @@ int tcp_send_rcvq(struct sock *sk, struct msghdr *msg, size_t size)
|
|
|
err_free:
|
|
|
kfree_skb(skb);
|
|
|
err:
|
|
|
- return -ENOMEM;
|
|
|
+ return err;
|
|
|
+
|
|
|
}
|
|
|
|
|
|
static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
|