summaryrefslogtreecommitdiff
path: root/tools/perf
diff options
context:
space:
mode:
authorIan Rogers <irogers@google.com>2024-05-08 06:52:59 +0300
committerArnaldo Carvalho de Melo <acme@redhat.com>2024-05-10 00:13:22 +0300
commitde6a908384fb1a8327ba46a2baec67d1dfe9a3e1 (patch)
tree608eba1de49211bcddadc00918d3094110a0740b /tools/perf
parent90f01afb0dfafbc9b094bb61e61a4ac297d9d0d2 (diff)
downloadlinux-de6a908384fb1a8327ba46a2baec67d1dfe9a3e1.tar.xz
perf comm: Fix comm_str__put() for reference count checking
Searching for the entry in the array needs to avoid the intermediate pointer with reference count checking. Refactor the array removal to binary search for the entry. Change the array to hold an entry with a reference count (so the intermediate pointer can work) and remove from the array when the reference count on a comm_str falls to 1. Fixes: 13ca628716c6f2c3 ("perf comm: Add reference count checking to 'struct comm_str'") Signed-off-by: Ian Rogers <irogers@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@arm.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Leo Yan <leo.yan@linux.dev> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Peter Zijlstra <peterz@infradead.org> Link: https://lore.kernel.org/r/20240508035301.1554434-3-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/util/comm.c45
1 files changed, 31 insertions, 14 deletions
diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c
index 1aa9a08e5b03..233f2b6edf52 100644
--- a/tools/perf/util/comm.c
+++ b/tools/perf/util/comm.c
@@ -19,6 +19,8 @@ static struct comm_strs {
int capacity;
} _comm_strs;
+static void comm_strs__remove_if_last(struct comm_str *cs);
+
static void comm_strs__init(void)
{
init_rwsem(&_comm_strs.lock);
@@ -58,22 +60,15 @@ static struct comm_str *comm_str__get(struct comm_str *cs)
static void comm_str__put(struct comm_str *cs)
{
- if (cs && refcount_dec_and_test(comm_str__refcnt(cs))) {
- struct comm_strs *comm_strs = comm_strs__get();
- int i;
+ if (!cs)
+ return;
- down_write(&comm_strs->lock);
- for (i = 0; i < comm_strs->num_strs; i++) {
- if (comm_strs->strs[i] == cs)
- break;
- }
- for (; i < comm_strs->num_strs - 1; i++)
- comm_strs->strs[i] = comm_strs->strs[i + 1];
-
- comm_strs->num_strs--;
- up_write(&comm_strs->lock);
+ if (refcount_dec_and_test(comm_str__refcnt(cs))) {
RC_CHK_FREE(cs);
} else {
+ if (refcount_read(comm_str__refcnt(cs)) == 1)
+ comm_strs__remove_if_last(cs);
+
RC_CHK_PUT(cs);
}
}
@@ -107,6 +102,28 @@ static int comm_str__search(const void *_key, const void *_member)
return strcmp(key, comm_str__str(member));
}
+static void comm_strs__remove_if_last(struct comm_str *cs)
+{
+ struct comm_strs *comm_strs = comm_strs__get();
+
+ down_write(&comm_strs->lock);
+ /*
+ * Are there only references from the array, if so remove the array
+ * reference under the write lock so that we don't race with findnew.
+ */
+ if (refcount_read(comm_str__refcnt(cs)) == 1) {
+ struct comm_str **entry;
+
+ entry = bsearch(comm_str__str(cs), comm_strs->strs, comm_strs->num_strs,
+ sizeof(struct comm_str *), comm_str__search);
+ comm_str__put(*entry);
+ for (int i = entry - comm_strs->strs; i < comm_strs->num_strs - 1; i++)
+ comm_strs->strs[i] = comm_strs->strs[i + 1];
+ comm_strs->num_strs--;
+ }
+ up_write(&comm_strs->lock);
+}
+
static struct comm_str *__comm_strs__find(struct comm_strs *comm_strs, const char *str)
{
struct comm_str **result;
@@ -158,7 +175,7 @@ static struct comm_str *comm_strs__findnew(const char *str)
}
}
up_write(&comm_strs->lock);
- return result;
+ return comm_str__get(result);
}
struct comm *comm__new(const char *str, u64 timestamp, bool exec)