BPF List
 help / color / mirror / Atom feed
From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
To: bpf@vger.kernel.org
Cc: Alexei Starovoitov <ast@kernel.org>,
	Andrii Nakryiko <andrii@kernel.org>,
	Daniel Borkmann <daniel@iogearbox.net>,
	Eduard Zingerman <eddyz87@gmail.com>,
	Emil Tsalapatis <emil@etsalapatis.com>, Tejun Heo <tj@kernel.org>,
	kkd@meta.com, kernel-team@meta.com
Subject: [PATCH bpf-next v2 4/5] bpftool: Read program streams through file descriptors
Date: Thu, 24 Sep 2026 18:26:36 +0200	[thread overview]
Message-ID: <20260924162641.1922423-5-memxor@gmail.com> (raw)
In-Reply-To: <20260924162641.1922423-1-memxor@gmail.com>

Use bpf_prog_stream_open() in its default blocking mode when following a
program stream. This lets bpftool wait for later output instead of exiting
as soon as it drains the currently buffered bytes. Flush each chunk as it
arrives so redirected output is not held in stdio buffers while the next
read blocks.

A stream descriptor does not keep its program alive, so bpftool drops the
program descriptor once the stream is open. The command then ends with EOF
when the program is unloaded and otherwise runs until interrupted, as
bpftool prog tracelog already does for the trace pipe. Install SIGINT,
SIGHUP and SIGTERM handlers without SA_RESTART so an interrupted read ends
the loop, check the stop flag before each read so a signal that lands while
output is being printed ends it as well, and exit successfully in both
cases. Restore the previous dispositions afterwards so batch mode keeps its
usual signal behavior. Report errors from the fallback read path too.

Fall back to BPF_PROG_STREAM_READ_BY_FD when the open command returns
EINVAL. The bpf() syscall uses EINVAL for unknown commands, so this keeps a
new bpftool compatible with kernels that provide program streams but
predate their file descriptor interface. On such kernels the command still
dumps the buffered output and exits. Document both behaviors.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
---
 .../bpftool/Documentation/bpftool-prog.rst    |  5 ++
 tools/bpf/bpftool/prog.c                      | 62 ++++++++++++++++---
 2 files changed, 60 insertions(+), 7 deletions(-)

diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
index 90fe8c61bf42..ef7e00eb5e47 100644
--- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
+++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
@@ -186,6 +186,11 @@ bpftool prog tracelog { stdout | stderr } *PROG*
     error messages to the standard error stream. This facility should be used
     only for debugging purposes.
 
+    On kernels that support opening a stream as a file descriptor, bpftool
+    keeps printing new output as the program produces it, until the program is
+    unloaded or <Ctrl+C> is hit. Older kernels only allow dumping the output
+    buffered so far, after which bpftool exits.
+
 bpftool prog run *PROG* data_in *FILE* [data_out *FILE* [data_size_out *L*]] [ctx_in *FILE* [ctx_out *FILE* [ctx_size_out *M*]]] [repeat *N*]
     Run BPF program *PROG* in the kernel testing infrastructure for BPF,
     meaning that the program works on the data and context provided by the
diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
index 24e40dfab469..f0241dada548 100644
--- a/tools/bpf/bpftool/prog.c
+++ b/tools/bpf/bpftool/prog.c
@@ -1119,21 +1119,69 @@ enum prog_tracelog_mode {
 	TRACE_STDERR,
 };
 
+static volatile sig_atomic_t stream_stop;
+
+static void stop_stream(int signo)
+{
+	stream_stop = 1;
+}
+
+/* Consumes prog_fd. */
 static int
 prog_tracelog_stream(int prog_fd, enum prog_tracelog_mode mode)
 {
+	/* No SA_RESTART: an interrupted read() must return EINTR to end the loop. */
+	const struct sigaction act = { .sa_handler = stop_stream };
+	const int signals[] = { SIGHUP, SIGINT, SIGTERM };
+	struct sigaction old[ARRAY_SIZE(signals)];
 	FILE *file = mode == TRACE_STDOUT ? stdout : stderr;
 	int stream_id = mode == TRACE_STDOUT ? 1 : 2;
 	char buf[512];
-	int ret;
+	unsigned int i;
+	int fd, ret;
+
+	fd = bpf_prog_stream_open(prog_fd, stream_id, NULL);
+	if (fd == -EINVAL) {
+		/* Kernel predates BPF_PROG_STREAM_OPEN: dump buffered output and exit. */
+		do {
+			ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL);
+			if (ret > 0)
+				fwrite(buf, sizeof(buf[0]), ret, file);
+		} while (ret > 0);
+		if (ret < 0)
+			p_err("failed to read stream: %s", strerror(-ret));
+		close(prog_fd);
+		goto out;
+	}
+	/*
+	 * The stream descriptor does not keep the program alive. Drop the
+	 * program reference so that reads return EOF once the program is gone.
+	 */
+	close(prog_fd);
+	if (fd < 0) {
+		p_err("failed to open stream: %s", strerror(-fd));
+		return -1;
+	}
 
+	stream_stop = 0;
+	for (i = 0; i < ARRAY_SIZE(signals); i++)
+		sigaction(signals[i], &act, &old[i]);
 	ret = 0;
-	do {
-		ret = bpf_prog_stream_read(prog_fd, stream_id, buf, sizeof(buf), NULL);
-		if (ret > 0)
-			fwrite(buf, sizeof(buf[0]), ret, file);
-	} while (ret > 0);
-
+	while (!stream_stop) {
+		ret = read(fd, buf, sizeof(buf));
+		if (ret <= 0)
+			break;
+		fwrite(buf, sizeof(buf[0]), ret, file);
+		fflush(file);
+	}
+	if (ret < 0 && !(stream_stop && errno == EINTR))
+		p_err("failed to read stream: %s", strerror(errno));
+	else
+		ret = 0;
+	for (i = 0; i < ARRAY_SIZE(signals); i++)
+		sigaction(signals[i], &old[i], NULL);
+	close(fd);
+out:
 	fflush(file);
 	return ret ? -1 : 0;
 }
-- 
2.53.0


  parent reply	other threads:[~2026-09-24 16:26 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-24 16:26 [PATCH bpf-next v2 0/5] File descriptor interface for BPF streams Kumar Kartikeya Dwivedi
2026-09-24 16:26 ` [PATCH bpf-next v2 1/5] bpf: Skip zero-length stream writes Kumar Kartikeya Dwivedi
2026-09-24 16:26 ` [PATCH bpf-next v2 2/5] bpf: Add file descriptor interface for program streams Kumar Kartikeya Dwivedi
2026-09-24 21:11   ` Alexei Starovoitov
2026-09-24 16:26 ` [PATCH bpf-next v2 3/5] libbpf: Add bpf_prog_stream_open() Kumar Kartikeya Dwivedi
2026-09-24 16:26 ` Kumar Kartikeya Dwivedi [this message]
2026-09-24 21:12   ` [PATCH bpf-next v2 4/5] bpftool: Read program streams through file descriptors Alexei Starovoitov
2026-09-24 16:26 ` [PATCH bpf-next v2 5/5] selftests/bpf: Test program stream " Kumar Kartikeya Dwivedi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260924162641.1922423-5-memxor@gmail.com \
    --to=memxor@gmail.com \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=eddyz87@gmail.com \
    --cc=emil@etsalapatis.com \
    --cc=kernel-team@meta.com \
    --cc=kkd@meta.com \
    --cc=tj@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox