Skip to contentSkip to conversation

TCP splice improvements

PATCH Superseded Pavel Begunkov 3 participants 8 messages May 19, 2023 – Jun 23, 2023

Subsystems (3)

v1 of 2 Superseded Latest is v2 →

lore
Share & tools
lore
#0 lore raw

cover TCP splice improvements

The main part is in Patch 1, which optimises locking for successful blocking TCP splice read, following with a clean up in Patch 2.

Pavel Begunkov (2):
net/tcp: optimise locking for blocking splice
net/tcp: optimise non error splice paths

 net/ipv4/tcp.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)
Signature · 1 line
2.40.0
#1 lore raw

PATCH 1/2 net/tcp: optimise locking for blocking splice

Even when tcp_splice_read() reads all it was asked for, for blocking sockets it'll release and immediately regrab the socket lock, loop around and break on the while check.

Check tss.len right after we adjust it, and return if we're done. That saves us one release_sock(); lock_sock(); pair per successful blocking splice read.

---
 net/ipv4/tcp.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +5 -3
index 4d6392c16b7a..bf7627f37e69 100644
@@ -789,13 +789,15 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 	 */
 	if (unlikely(*ppos))
 		return -ESPIPE;
+	if (unlikely(!tss.len))
+		return 0;
 
 	ret = spliced = 0;
 
 	lock_sock(sk);
 
 	timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
-	while (tss.len) {
+	while (true) {
 		ret = __tcp_splice_read(sk, &tss);
 		if (ret < 0)
 			break;
@@ -835,10 +837,10 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 			}
 			continue;
 		}
-		tss.len -= ret;
 		spliced += ret;
+		tss.len -= ret;
 
-		if (!timeo)
+		if (!tss.len || !timeo)
 			break;
 		release_sock(sk);
 		lock_sock(sk);
Signature · 1 line
2.40.0
maintainer
#2 lore raw

Re: [PATCH net-next 1/2] net/tcp: optimise locking for blocking splice

Quoted 35 lines · Pavel Begunkov

Even when tcp_splice_read() reads all it was asked for, for blocking sockets it'll release and immediately regrab the socket lock, loop around and break on the while check.

Check tss.len right after we adjust it, and return if we're done. That saves us one release_sock(); lock_sock(); pair per successful blocking splice read.

---
 net/ipv4/tcp.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +3 -1
index 4d6392c16b7a..bf7627f37e69 100644
@@ -789,13 +789,15 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 	 */
 	if (unlikely(*ppos))
 		return -ESPIPE;
+	if (unlikely(!tss.len))
+		return 0;
 
 	ret = spliced = 0;
 
 	lock_sock(sk);
 
 	timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
-	while (tss.len) {
+	while (true) {
 		ret = __tcp_splice_read(sk, &tss);
 		if (ret < 0)
 			break;

net/ipv4/tcp.c ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, · line 841

Quoted 6 lines
net/ipv4/tcp.c +1 -1
@@ -835,10 +837,10 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 			}
 			continue;
 		}
-		tss.len -= ret;
 		spliced += ret;
+		tss.len -= ret;

The patch LGTM. The only minor thing that I note is that the above chunk is not needed. Perhaps avoiding unneeded delta could be worthy.

Cheers,

Paolo

#3 lore raw

Re: [PATCH net-next 1/2] net/tcp: optimise locking for blocking splice

Quoted 29 lines · Paolo Abeni
Quoted 28 lines · Pavel Begunkov

Even when tcp_splice_read() reads all it was asked for, for blocking sockets it'll release and immediately regrab the socket lock, loop around and break on the while check.

Check tss.len right after we adjust it, and return if we're done. That saves us one release_sock(); lock_sock(); pair per successful blocking splice read.

---
  net/ipv4/tcp.c | 8 +++++---
  1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +2 -0
index 4d6392c16b7a..bf7627f37e69 100644
@@ -789,13 +789,15 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
  	 */
  	if (unlikely(*ppos))
  		return -ESPIPE;
+	if (unlikely(!tss.len))
+		return 0;
 
  	ret = spliced = 0;
 
  	lock_sock(sk);
 
Quoted 16 lines

net/ipv4/tcp.c ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, · line 841

Quoted 13 lines
net/ipv4/tcp.c +2 -2
  	timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
-	while (tss.len) {
+	while (true) {
  		ret = __tcp_splice_read(sk, &tss);
  		if (ret < 0)
  			break;
@@ -835,10 +837,10 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
  			}
  			continue;
  		}
-		tss.len -= ret;
  		spliced += ret;
+		tss.len -= ret;

The patch LGTM. The only minor thing that I note is that the above chunk is not needed. Perhaps avoiding unneeded delta could be worthy.

It keeps it closer to the tss.len test, so I'd leave it for that reason, but on the other hand the compiler should be perfectly able to optimise it regardless (i.e. sub;cmp;jcc; vs sub;jcc;). I don't have a hard feeling on that, can change if you want.

Signature · 1 line
Pavel Begunkov
#4 lore raw

Re: [PATCH net-next 1/2] net/tcp: optimise locking for blocking splice

Quoted 44 lines · Pavel Begunkov
Quoted 34 lines · Paolo Abeni
Quoted 33 lines · Pavel Begunkov

Even when tcp_splice_read() reads all it was asked for, for blocking sockets it'll release and immediately regrab the socket lock, loop around and break on the while check.

Check tss.len right after we adjust it, and return if we're done. That saves us one release_sock(); lock_sock(); pair per successful blocking splice read.

---
  net/ipv4/tcp.c | 8 +++++---

  1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +3 -1
index 4d6392c16b7a..bf7627f37e69 100644
@@ -789,13 +789,15 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
        */
       if (unlikely(*ppos))
           return -ESPIPE;
+    if (unlikely(!tss.len))
+        return 0;
       ret = spliced = 0;
       lock_sock(sk);
       timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
-    while (tss.len) {
+    while (true) {
           ret = __tcp_splice_read(sk, &tss);
           if (ret < 0)
               break;
@@ -835,10 +837,10 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
               }
Quoted 8 lines

net/ipv4/tcp.c ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, · line 841

Quoted 5 lines
net/ipv4/tcp.c +1 -1
               continue;
           }
-        tss.len -= ret;
           spliced += ret;
+        tss.len -= ret;

The patch LGTM. The only minor thing that I note is that the above chunk is not needed. Perhaps avoiding unneeded delta could be worthy.

Quoted 4 lines

It keeps it closer to the tss.len test, so I'd leave it for that reason, but on the other hand the compiler should be perfectly able to optimise it regardless (i.e. sub;cmp;jcc; vs sub;jcc;). I don't have a hard feeling on that, can change if you want.

Is there anything I can do to help here? I think the patch is fine, but can amend the change per Paolo's suggestion if required.

Signature · 1 line
Pavel Begunkov
#5 lore raw

Re: [PATCH net-next 1/2] net/tcp: optimise locking for blocking splice

Quoted 46 lines · Pavel Begunkov
Quoted 44 lines · Pavel Begunkov
Quoted 42 lines · Paolo Abeni
Quoted 32 lines · Pavel Begunkov

Even when tcp_splice_read() reads all it was asked for, for blocking sockets it'll release and immediately regrab the socket lock, loop around and break on the while check.

Check tss.len right after we adjust it, and return if we're done. That saves us one release_sock(); lock_sock(); pair per successful blocking splice read.

---
  net/ipv4/tcp.c | 8 +++++---
  1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +3 -1
index 4d6392c16b7a..bf7627f37e69 100644
@@ -789,13 +789,15 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
       */
      if (unlikely(*ppos))
          return -ESPIPE;
+    if (unlikely(!tss.len))
+        return 0;
      ret = spliced = 0;
      lock_sock(sk);
      timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
-    while (tss.len) {
+    while (true) {
          ret = __tcp_splice_read(sk, &tss);
          if (ret < 0)
              break;

net/ipv4/tcp.c ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, · line 841

Quoted 6 lines
net/ipv4/tcp.c +1 -1
@@ -835,10 +837,10 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
              }
              continue;
          }
-        tss.len -= ret;
          spliced += ret;
+        tss.len -= ret;

The patch LGTM. The only minor thing that I note is that the above chunk is not needed. Perhaps avoiding unneeded delta could be worthy.

Quoted 8 lines
Quoted 4 lines

It keeps it closer to the tss.len test, so I'd leave it for that reason, but on the other hand the compiler should be perfectly able to optimise it regardless (i.e. sub;cmp;jcc; vs sub;jcc;). I don't have a hard feeling on that, can change if you want.

Is there anything I can do to help here? I think the patch is fine, but can amend the change per Paolo's suggestion if required.

We prefer seeing patches focusing on the change, instead of also doing arbitrary changes
making future backports more likely to conflict.

Thanks.

#6 lore raw

Re: [PATCH net-next 1/2] net/tcp: optimise locking for blocking splice

Quoted 52 lines · Eric Dumazet
Quoted 51 lines · Pavel Begunkov
Quoted 48 lines · Pavel Begunkov
Quoted 34 lines · Paolo Abeni
Quoted 33 lines · Pavel Begunkov

Even when tcp_splice_read() reads all it was asked for, for blocking sockets it'll release and immediately regrab the socket lock, loop around and break on the while check.

Check tss.len right after we adjust it, and return if we're done. That saves us one release_sock(); lock_sock(); pair per successful blocking splice read.

---
   net/ipv4/tcp.c | 8 +++++---
   1 file changed, 5 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +3 -1
index 4d6392c16b7a..bf7627f37e69 100644
@@ -789,13 +789,15 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
        */
       if (unlikely(*ppos))
           return -ESPIPE;
+    if (unlikely(!tss.len))
+        return 0;
       ret = spliced = 0;
       lock_sock(sk);
       timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
-    while (tss.len) {
+    while (true) {
           ret = __tcp_splice_read(sk, &tss);
           if (ret < 0)
               break;
@@ -835,10 +837,10 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
               }
Quoted 8 lines

net/ipv4/tcp.c ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos, · line 841

Quoted 5 lines
net/ipv4/tcp.c +1 -1
               continue;
           }
-        tss.len -= ret;
           spliced += ret;
+        tss.len -= ret;

The patch LGTM. The only minor thing that I note is that the above chunk is not needed. Perhaps avoiding unneeded delta could be worthy.

It keeps it closer to the tss.len test, so I'd leave it for that reason, but on the other hand the compiler should be perfectly able to optimise it regardless (i.e. sub;cmp;jcc; vs sub;jcc;). I don't have a hard feeling on that, can change if you want.

Quoted 7 lines
Quoted 3 lines

Is there anything I can do to help here? I think the patch is fine, but can amend the change per Paolo's suggestion if required.

We prefer seeing patches focusing on the change, instead of also doing arbitrary changes
making future backports more likely to conflict.

Thank you for taking a look! I cut it down and resent.

I don't agree it's arbitrary, it's a clean up related to the change. I'm just trying to not make the death by a thousand cuts problem worse for networking, but I guess I'm worried for nothing.

Signature · 1 line
Pavel Begunkov
#7 lore raw

PATCH 2/2 net/tcp: optimise non error splice paths

In reply to Pavel Begunkov

Move post __tcp_splice_read() error checking inside the "nothing to read" block. That removes an extra if from the path where it has successfully spliced some bytes.

---
 net/ipv4/tcp.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
net/ipv4/tcp.c +3 -3
index bf7627f37e69..0139b2c70ed4 100644
@@ -799,9 +799,9 @@ ssize_t tcp_splice_read(struct socket *sock, loff_t *ppos,
 	timeo = sock_rcvtimeo(sk, sock->file->f_flags & O_NONBLOCK);
 	while (true) {
 		ret = __tcp_splice_read(sk, &tss);
-		if (ret < 0)
-			break;
-		else if (!ret) {
+		if (ret <= 0) {
+			if (unlikely(ret < 0))
+				break;
 			if (spliced)
 				break;
 			if (sock_flag(sk, SOCK_DONE))
Signature · 1 line
2.40.0