|
@@ -1165,6 +1165,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg,
|
|
int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
|
|
int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
|
|
{
|
|
{
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
struct tcp_sock *tp = tcp_sk(sk);
|
|
|
|
+ struct ubuf_info *uarg = NULL;
|
|
struct sk_buff *skb;
|
|
struct sk_buff *skb;
|
|
struct sockcm_cookie sockc;
|
|
struct sockcm_cookie sockc;
|
|
int flags, err, copied = 0;
|
|
int flags, err, copied = 0;
|
|
@@ -1174,6 +1175,26 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size)
|
|
long timeo;
|
|
long timeo;
|
|
|
|
|
|
flags = msg->msg_flags;
|
|
flags = msg->msg_flags;
|
|
|
|
+
|
|
|
|
+ if (flags & MSG_ZEROCOPY && size) {
|
|
|
|
+ if (sk->sk_state != TCP_ESTABLISHED) {
|
|
|
|
+ err = -EINVAL;
|
|
|
|
+ goto out_err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ skb = tcp_send_head(sk) ? tcp_write_queue_tail(sk) : NULL;
|
|
|
|
+ uarg = sock_zerocopy_realloc(sk, size, skb_zcopy(skb));
|
|
|
|
+ if (!uarg) {
|
|
|
|
+ err = -ENOBUFS;
|
|
|
|
+ goto out_err;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* skb may be freed in main loop, keep extra ref on uarg */
|
|
|
|
+ sock_zerocopy_get(uarg);
|
|
|
|
+ if (!(sk_check_csum_caps(sk) && sk->sk_route_caps & NETIF_F_SG))
|
|
|
|
+ uarg->zerocopy = 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) {
|
|
if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) {
|
|
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
|
|
err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
|
|
if (err == -EINPROGRESS && copied_syn > 0)
|
|
if (err == -EINPROGRESS && copied_syn > 0)
|
|
@@ -1297,7 +1318,7 @@ new_segment:
|
|
err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy);
|
|
err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy);
|
|
if (err)
|
|
if (err)
|
|
goto do_fault;
|
|
goto do_fault;
|
|
- } else {
|
|
|
|
|
|
+ } else if (!uarg || !uarg->zerocopy) {
|
|
bool merge = true;
|
|
bool merge = true;
|
|
int i = skb_shinfo(skb)->nr_frags;
|
|
int i = skb_shinfo(skb)->nr_frags;
|
|
struct page_frag *pfrag = sk_page_frag(sk);
|
|
struct page_frag *pfrag = sk_page_frag(sk);
|
|
@@ -1335,6 +1356,13 @@ new_segment:
|
|
page_ref_inc(pfrag->page);
|
|
page_ref_inc(pfrag->page);
|
|
}
|
|
}
|
|
pfrag->offset += copy;
|
|
pfrag->offset += copy;
|
|
|
|
+ } else {
|
|
|
|
+ err = skb_zerocopy_iter_stream(sk, skb, msg, copy, uarg);
|
|
|
|
+ if (err == -EMSGSIZE || err == -EEXIST)
|
|
|
|
+ goto new_segment;
|
|
|
|
+ if (err < 0)
|
|
|
|
+ goto do_error;
|
|
|
|
+ copy = err;
|
|
}
|
|
}
|
|
|
|
|
|
if (!copied)
|
|
if (!copied)
|
|
@@ -1381,6 +1409,7 @@ out:
|
|
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
|
|
tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
|
|
}
|
|
}
|
|
out_nopush:
|
|
out_nopush:
|
|
|
|
+ sock_zerocopy_put(uarg);
|
|
return copied + copied_syn;
|
|
return copied + copied_syn;
|
|
|
|
|
|
do_fault:
|
|
do_fault:
|
|
@@ -1397,6 +1426,7 @@ do_error:
|
|
if (copied + copied_syn)
|
|
if (copied + copied_syn)
|
|
goto out;
|
|
goto out;
|
|
out_err:
|
|
out_err:
|
|
|
|
+ sock_zerocopy_put_abort(uarg);
|
|
err = sk_stream_error(sk, flags, err);
|
|
err = sk_stream_error(sk, flags, err);
|
|
/* make sure we wake any epoll edge trigger waiter */
|
|
/* make sure we wake any epoll edge trigger waiter */
|
|
if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 &&
|
|
if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 &&
|