From: Yang Jihong yangjihong1@huawei.com
[ Upstream commit e16c2ce7c5ed5de881066c1fd10ba5c09af69559 ]
Commit da231338ec9c0987 ("perf record: Use an eventfd to wakeup when done") uses eventfd() to solve a rare race where the setting and checking of 'done' which add done_fd to pollfd. When draining buffer, revents of done_fd is 0 and evlist__filter_pollfd function returns a non-zero value. As a result, perf record does not stop profiling.
The following simple scenarios can trigger this condition:
# sleep 10 & # perf record -p $!
After the sleep process exits, perf record should stop profiling and exit. However, perf record keeps running.
If pollfd revents contains only POLLERR or POLLHUP, perf record indicates that buffer is draining and need to stop profiling. Use fdarray_flag__nonfilterable() to set done eventfd to nonfilterable objects, so that evlist__filter_pollfd() does not filter and check done eventfd.
Fixes: da231338ec9c0987 ("perf record: Use an eventfd to wakeup when done") Signed-off-by: Yang Jihong yangjihong1@huawei.com Tested-by: Arnaldo Carvalho de Melo acme@redhat.com Tested-by: Jiri Olsa jolsa@redhat.com Cc: Adrian Hunter adrian.hunter@intel.com Cc: Alexander Shishkin alexander.shishkin@linux.intel.com Cc: Alexey Budankov alexey.budankov@linux.intel.com Cc: Mark Rutland mark.rutland@arm.com Cc: Namhyung Kim namhyung@kernel.org Cc: Peter Zijlstra peterz@infradead.org Cc: zhangjinhao2@huawei.com Link: http://lore.kernel.org/lkml/20210205065001.23252-1-yangjihong1@huawei.com Signed-off-by: Arnaldo Carvalho de Melo acme@redhat.com Signed-off-by: Sasha Levin sashal@kernel.org --- tools/perf/builtin-record.c | 2 +- tools/perf/util/evlist.c | 8 ++++++++ tools/perf/util/evlist.h | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index fd39116506123..51e593e896ea5 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -1663,7 +1663,7 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) status = -1; goto out_delete_session; } - err = evlist__add_pollfd(rec->evlist, done_fd); + err = evlist__add_wakeup_eventfd(rec->evlist, done_fd); if (err < 0) { pr_err("Failed to add wakeup eventfd to poll list\n"); status = err; diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index 05363a7247c41..fea4c1e8010d9 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -572,6 +572,14 @@ int evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask) return perf_evlist__filter_pollfd(&evlist->core, revents_and_mask); }
+#ifdef HAVE_EVENTFD_SUPPORT +int evlist__add_wakeup_eventfd(struct evlist *evlist, int fd) +{ + return perf_evlist__add_pollfd(&evlist->core, fd, NULL, POLLIN, + fdarray_flag__nonfilterable); +} +#endif + int evlist__poll(struct evlist *evlist, int timeout) { return perf_evlist__poll(&evlist->core, timeout); diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 1aae75895dea0..6d4d62151bc89 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -142,6 +142,10 @@ struct evsel *evlist__find_tracepoint_by_name(struct evlist *evlist, const char int evlist__add_pollfd(struct evlist *evlist, int fd); int evlist__filter_pollfd(struct evlist *evlist, short revents_and_mask);
+#ifdef HAVE_EVENTFD_SUPPORT +int evlist__add_wakeup_eventfd(struct evlist *evlist, int fd); +#endif + int evlist__poll(struct evlist *evlist, int timeout);
struct evsel *evlist__id2evsel(struct evlist *evlist, u64 id);