Linux Perf Users
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Ian Rogers" <irogers@google.com>
Cc: linux-perf-users@vger.kernel.org
Subject: Re: [PATCH v11 15/20] perf intel-tpebs: Fix race in tpebs_stop() and tpebs_cmd.out fd handling
Date: Mon, 09 Nov 2026 21:50:07 +0000	[thread overview]
Message-ID: <sashiko-outbox-205695@kernel.org> (raw)
In-Reply-To: <20261107044102.1506626-16-irogers@google.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issues to consider:
- [Medium] perf intel-tpebs: control pipe command coalescing deadlock in
  tpebs_stop()
- [Low] perf intel-tpebs: race in process_sample_event() during
  tpebs_stop()
--

commit e2203763a4bf43ed152a64f29917746045712cad
Author: Ian Rogers <irogers@google.com>

perf intel-tpebs: Fix race in tpebs_stop() and tpebs_cmd.out fd handling

This commit serializes control command writes with teardown, transfers
ownership of tpebs_cmd.out to __sample_reader(), and guards
evsel__tpebs_open() against a concurrent tpebs_stop().

> diff --git a/tools/perf/util/intel-tpebs.c b/tools/perf/util/intel-tpebs.c
> index 7c0b8b15bd10e..4b7464cfa0eb3 100644
> --- a/tools/perf/util/intel-tpebs.c
> +++ b/tools/perf/util/intel-tpebs.c

[ ... ]

> @@ -262,20 +291,20 @@ static int tpebs_send_record_cmd(const char *msg) __must_hold(tpebs_mtx_get())
>  
>  	/* Send perf record command.*/
>  	len = strlen(msg);
> -	ret = write(control_fd[1], msg, len);
> +	ret = write(ctrl_fd, msg, len);
>  	if (ret != len) {
>  		pr_err("perf record control write control message '%s' failed\n", msg);
>  		ret = -EPIPE;
>  		goto out;
>  	}
>  
> -	if (!strcmp(msg, EVLIST_CTL_CMD_STOP_TAG)) {
> +	if (is_stop) {
>  		ret = 0;
>  		goto out;
>  	}
>  
>  	/* Wait for an ack. */
> -	pollfd.fd = ack_fd[0];
> +	pollfd.fd = resp_fd;
>  
>  	/*
>  	 * We need this poll to ensure the ack_fd PIPE will not hang
> @@ -284,7 +313,17 @@ static int tpebs_send_record_cmd(const char *msg) __must_hold(tpebs_mtx_get())
>  	 */
>  again:
>  	if (!poll(&pollfd, 1, 500)) {
> -		if (check_if_command_finished(&tpebs_cmd)) {
> +		bool stopping, finished;
> +
> +		mutex_lock(tpebs_mtx_get());
> +		stopping = tpebs_stopping;
> +		finished = !stopping && check_if_command_finished(&tpebs_cmd);
> +		mutex_unlock(tpebs_mtx_get());
> +		if (stopping) {
> +			ret = -EBUSY;
> +			goto out;
> +		}

[Severity: Medium]
Can aborting the poll() wait on tpebs_stopping coalesce the in-flight
command with EVLIST_CTL_CMD_STOP_TAG in control_fd and hang tpebs_stop()
in pthread_join()?

tpebs_send_record_cmd() writes msg without a trailing newline or NUL
delimiter:

Thread 1                          Thread 2
evsel__tpebs_open()
  tpebs_send_record_cmd("enable")
    write(ctrl_fd, "enable", 6)
    poll(&pollfd, 1, 500)
                                  evsel__tpebs_close()
                                    tpebs_stop()
                                      tpebs_stopping = true
                                      tpebs_gen++
                                      tpebs_send_record_cmd("stop")
                                        cond_wait(&tpebs_cmd_cond, ...)
    /* poll() times out */
    stopping == true -> goto out
    tpebs_cmd_in_flight = false
    cond_broadcast(&tpebs_cmd_cond)
  /* tpebs_gen != open_gen, skips SIGTERM */
                                        write(ctrl_fd, "stop", 4)
                                      pthread_join(reader_thread, NULL)
                                        <-- hangs forever

When perf record reads from the control pipe, evlist__ctlfd_recv() reads
all available bytes until '\n', '\0', or EAGAIN into a single command
buffer:

tools/perf/util/evlist.c:evlist__ctlfd_recv() {
	do {
		err = read(evlist__ctl_fd_fd(evlist), &c, 1);
		if (err > 0) {
			if (c == '\n' || c == '\0')
				break;
			cmd_data[bytes_read++] = c;
    ...
}

If perf record has not consumed "enable" (or "ping") before "stop" is
written, it reads "enablestop" (or "pingstop") as one command and drops
the stop request.  Because evsel__tpebs_open() skips kill(..., SIGTERM)
once tpebs_gen has changed and tpebs_stop() waits in pthread_join()
before closing the pipes or killing the child, perf record never exits
and pthread_join() blocks forever.

[ ... ]

> @@ -331,12 +382,21 @@ static int tpebs_stop(void) __must_hold(tpebs_mtx_get())
>  	/* Like tpebs_start, we should only run tpebs_end once. */
>  	if (tpebs_cmd.pid != 0) {
>  		pid_t actual_pid = tpebs_cmd.pid;
> +		pthread_t reader_thread;
>  
>  		tpebs_stopping = true;
> +		tpebs_gen++;
> +		cond_broadcast(&tpebs_cmd_cond);
>  		tpebs_send_record_cmd(EVLIST_CTL_CMD_STOP_TAG);
> +		/*
> +		 * tpebs_results is empty when stopping; clear pid so
> +		 * process_sample_event() ignores any late samples while
> +		 * draining the pipe.
> +		 */
>  		tpebs_cmd.pid = 0;

[Severity: Low]
Can process_sample_event() still see a non-zero tpebs_cmd.pid and return
-EINVAL while tpebs_send_record_cmd(EVLIST_CTL_CMD_STOP_TAG) drops
tpebs_mtx?

evsel__tpebs_close() empties tpebs_results before calling tpebs_stop(),
and tpebs_send_record_cmd() releases tpebs_mtx in cond_wait() and around
write().  Because tpebs_cmd.pid is only cleared after
tpebs_send_record_cmd() returns and process_sample_event() does not check
tpebs_stopping:

tools/perf/util/intel-tpebs.c:process_sample_event() {
	mutex_lock(tpebs_mtx_get());
	if (tpebs_cmd.pid == 0) {
		/* Record has terminated. */
		mutex_unlock(tpebs_mtx_get());
		return 0;
	}
	t = tpebs_retire_lat__find(sample->evsel);
	if (!t) {
		mutex_unlock(tpebs_mtx_get());
		return -EINVAL;
	}
    ...
}

Any sample processed by tpebs_reader_thread during that window fails the
lookup in the empty tpebs_results list and returns -EINVAL, causing
__perf_session__process_pipe_events() to log an error and abort draining
the pipe early.  Checking tpebs_stopping in process_sample_event() would
avoid this race.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261107044102.1506626-1-irogers@google.com?part=15

  reply	other threads:[~2026-11-09 21:50 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-07  4:40 [PATCH v11 00/20] tools/perf: Adopt kernel compiler-context-analysis.h annotations Ian Rogers
2026-11-07  4:40 ` [PATCH v11 01/20] compiler-context-analysis: Avoid unused parameter warning in _context_unsafe_alias() Ian Rogers
2026-11-09 19:55   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 02/20] tools headers compiler: Synchronize compiler_types.h with the kernel sources Ian Rogers
2026-11-09 20:01   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 03/20] tools headers: Synchronize linux/compiler-context-analysis.h " Ian Rogers
2026-11-09 20:04   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 04/20] perf mutex: Use kernel style compiler-context-analysis.h annotations Ian Rogers
2026-11-09 20:11   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 05/20] perf build: Enable additional Clang thread-safety flags Ian Rogers
2026-11-09 20:23   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 06/20] perf comm: Add __guarded_by annotations to struct comm_strs Ian Rogers
2026-11-09 20:37   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 07/20] perf vdso: Fix unlocked __dsos__add() and race in __machine__addnew_vdso() Ian Rogers
2026-11-09 20:41   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 08/20] perf dsos: Skip BPF and OOL DSOs in dsos__find_kernel_dso() Ian Rogers
2026-11-09 20:59   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 09/20] perf dsos: Ensure DSOs in auxtrace, machine, and symbol are in dsos Ian Rogers
2026-11-09 21:08   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 10/20] perf dso: Pass dso_data to close_data_fd() and dso__list_del() Ian Rogers
2026-11-09 21:13   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 11/20] perf dso: Use DEFINE_MUTEX for dso__data_open_lock Ian Rogers
2026-11-09 21:14   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 12/20] perf dsos: Use read lock fast path in dsos__findnew_id() Ian Rogers
2026-11-09 21:19   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 13/20] perf dso: Expand context analysis annotations Ian Rogers
2026-11-09 21:21   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 14/20] perf thread: Expand context analysis annotations to threads and namespaces_lock Ian Rogers
2026-11-09 21:25   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 15/20] perf intel-tpebs: Fix race in tpebs_stop() and tpebs_cmd.out fd handling Ian Rogers
2026-11-09 21:50   ` sashiko-bot [this message]
2026-11-07  4:40 ` [PATCH v11 16/20] perf intel-tpebs: Use DEFINE_MUTEX for tpebs_mtx Ian Rogers
2026-11-09 21:58   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 17/20] perf config, intel-tpebs, env: Add __guarded_by and __must_hold annotations Ian Rogers
2026-11-09 22:06   ` sashiko-bot
2026-11-07  4:40 ` [PATCH v11 18/20] perf maps: Fix lock inversion in maps__load_first() Ian Rogers
2026-11-09 22:10   ` sashiko-bot
2026-11-07  4:41 ` [PATCH v11 19/20] perf maps: Fix map index lookup fallback and removal Ian Rogers
2026-11-09 22:17   ` sashiko-bot
2026-11-07  4:41 ` [PATCH v11 20/20] perf maps: Add context analysis annotations Ian Rogers
2026-11-09 22:24   ` sashiko-bot
2026-11-10 22:58 ` [PATCH v11 00/20] tools/perf: Adopt kernel compiler-context-analysis.h annotations Namhyung Kim
2026-11-10 23:14   ` Ian Rogers

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=sashiko-outbox-205695@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=irogers@google.com \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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