git: 07c057b5de44 - stable/14 - clnt_vc.c: Fix handling of broken TCP connections

From: Rick Macklem <rmacklem_at_FreeBSD.org>
Date: Sun, 11 Oct 2026 03:09:05 UTC
The branch stable/14 has been updated by rmacklem:

URL: https://cgit.FreeBSD.org/src/commit/?id=07c057b5de44e993fb445e70a9e2f77b30b83867

commit 07c057b5de44e993fb445e70a9e2f77b30b83867
Author:     Vinicius Ferrao <ferrao@versatushpc.com.br>
AuthorDate: 2026-10-04 21:07:30 +0000
Commit:     Rick Macklem <rmacklem@FreeBSD.org>
CommitDate: 2026-10-11 03:07:05 +0000

    clnt_vc.c: Fix handling of broken TCP connections
    
    After more than, I don't know, maybe 10k operations: mount, copy,
    remove, verify and unmount cycles, one cp command hung in
    close() / ncl_flush and never recovered. The machine and the mount
    continued to work normally through a new connection, but the writes
    using the old connection stayed frozen.
    
    I did not understand exactly what happened.  I traced what appears to
    be the issue in the code. My current understanding is that
    clnt_vc_soupcall() saw the EOF and woke the caller waiting for RPC
    replies, but one caller remained blocked in sosend().
    That thread continued holding a reference to the old client, preventing
    it from being completely cleaned up.
    
    The attached patch calls socantsendmore() when EOF is received, which
    should wake the blocked sender and let the normal reconnect code replace
    the connection.
    
    (cherry picked from commit 49bec8c3dc58cf8e944f9bbbbea07e7e856ad897)
---
 sys/rpc/clnt_vc.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/sys/rpc/clnt_vc.c b/sys/rpc/clnt_vc.c
index 03fb27e8be36..dbd42b04c63d 100644
--- a/sys/rpc/clnt_vc.c
+++ b/sys/rpc/clnt_vc.c
@@ -980,11 +980,8 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
 	mtx_unlock(&ct->ct_lock);
 
 	/*
-	 * If another thread is already here, it must be in
-	 * soreceive(), so just return to avoid races with it.
-	 * ct_upcallrefs is protected by the SOCKBUF_LOCK(),
-	 * which is held in this function, except when
-	 * soreceive() is called.
+	 * ct_upcallrefs prevents another upcall from processing the
+	 * socket while the receive buffer lock is dropped.
 	 */
 	if (ct->ct_upcallrefs > 0)
 		return (SU_OK);
@@ -1025,6 +1022,11 @@ clnt_vc_soupcall(struct socket *so, void *arg, int waitflag)
 			 * to read from the stream.
 			 */
 			error = ECONNRESET;
+
+			/* Avoid reversing the so_snd -> so_rcv lock order. */
+			SOCK_RECVBUF_UNLOCK(so);
+			socantsendmore(so);
+			SOCK_RECVBUF_LOCK(so);
 		}
 
 		/*