From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: passt.top; dmarc=none (p=none dis=none) header.from=gibson.dropbear.id.au Authentication-Results: passt.top; dkim=pass (2048-bit key; secure) header.d=gibson.dropbear.id.au header.i=@gibson.dropbear.id.au header.a=rsa-sha256 header.s=202602 header.b=ROTr6yxO; dkim-atps=neutral Received: from mail.ozlabs.org (mail.ozlabs.org [IPv6:2404:9400:2221:ea00::3]) by passt.top (Postfix) with ESMTPS id 4E6825A0262 for ; Fri, 05 Jun 2026 02:38:23 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gibson.dropbear.id.au; s=202602; t=1780619898; bh=NMAWussu5hsKa5NNZbYjvmMzqIJ04u51F/Svks6adSg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ROTr6yxOHOR5D16hnt5/642+5FpBGVgKQTa7UrL0nyX6SqLd0CKuOyvoY+v3qWqa/ WUbyaui2aAM8zEQlKTx3E0s/lFL6UBrj7+Hdk/OvfZxrTgstOYSEvw5XfROxShFNLe dkhfxLaLY7xKUwvblk50D9INZz2wyHZA4GezPnnmetK2r+qvx7TVC/xUSwc6sNXD8f z9iK7ltCw13KdXQzEBvX7t6VnrfxhtfPFQTDH6vLcrLNRA32VnMTqEQ/2v62cH5NDX ZBflPMqGjUO3OpVJdoT9CZHujN5lpcfwG3eqSydvKekrfJVdKdOmol98YlD9XQRLs1 vGYA4cemvCpyg== Received: by gandalf.ozlabs.org (Postfix, from userid 1007) id 4gWjGL446cz58lX; Fri, 05 Jun 2026 10:38:18 +1000 (AEST) From: David Gibson To: passt-dev@passt.top, Stefano Brivio Subject: [PATCH v2 8/8] tcp_splice: Improve EOF and read stall exit conditions Date: Fri, 5 Jun 2026 10:34:16 +1000 Message-ID: <20260605003808.1596631-1-david@gibson.dropbear.id.au> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260528050213.679685-9-david@gibson.dropbear.id.au> References: <20260528050213.679685-9-david@gibson.dropbear.id.au> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Message-ID-Hash: FH32MGVTJB5QN4T57KAKACKZHV7LLKM3 X-Message-ID-Hash: FH32MGVTJB5QN4T57KAKACKZHV7LLKM3 X-MailFrom: dgibson@gandalf.ozlabs.org X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header CC: David Gibson X-Mailman-Version: 3.3.8 Precedence: list List-Id: Development discussion and patches for passt Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: At the end of our loop we have a conditional 'break' that exits if we're at EOF on the read side and have nothing left in the pipe. This makes sense: at EOF there's nothing left to do read-side and with nothing in the pipe there's nothing to do write side either. The same is true if the read side hit an EAGAIN and the pipe is empty: there's nothing we can do (for now) read side, and with an empty pipe nothing write side either. So, generalise the condition to exit on either EOF or EAGAIN read side. Furthermore, if the read side is at EOF or EAGAIN and there's already nothing in the pipe before the write-side splice(), then that write side splice() can't accomplish anything, so exit the loop early in that case avoiding a harmless but unnecessary write-splice(). Signed-off-by: David Gibson --- tcp_splice.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) Changes in v2: * Duplicate rather than move the test, it's valuable in both places. * Make comments and commit message clearer diff --git a/tcp_splice.c b/tcp_splice.c index 565596d3..1e3c7749 100644 --- a/tcp_splice.c +++ b/tcp_splice.c @@ -497,9 +497,17 @@ static int tcp_splice_forward(struct ctx *c, flow_trace(conn, "%zi from read-side call", readlen); - if (!readlen) { - conn_event(conn, FIN_RCVD(fromsidei)); - } else if (readlen > 0) { + if (readlen <= 0) { + if (!readlen) /* EOF */ + conn_event(conn, FIN_RCVD(fromsidei)); + + /* We're either blocked or at EOF on the read side, and + * there's nothing in the pipe so there's nothing to do + * write side either. + */ + if (!conn->pending[fromsidei]) + break; + } else { conn->pending[fromsidei] += readlen; if (readlen >= (long)c->tcp.pipe_size * 90 / 100) @@ -531,9 +539,11 @@ static int tcp_splice_forward(struct ctx *c, conn->pending[fromsidei] -= written; - if (conn->events & FIN_RCVD(fromsidei) && - !conn->pending[fromsidei]) + if (!conn->pending[fromsidei] && readlen <= 0) { + /* Read side is EOF or EAGAIN, and we emptied the pipe. + * No more we can do for now, */ break; + } } /* We need write-side wakeups if and only if we have data in the pipe to -- 2.54.0