perf comm: Add reference count checking to 'struct comm_str'
Reference count checking of an rbtree is troublesome as each pointer should have a reference, switch to using a sorted array. Remove an indirection by embedding the reference count with the string. Use pthread_once to safely initialize the comm_strs and reader writer mutex. Signed-off-by: Ian Rogers <irogers@google.com> Cc: Adrian Hunter <adrian.hunter@intel.com> Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Athira Rajeev <atrajeev@linux.vnet.ibm.com> Cc: Ben Gainey <ben.gainey@arm.com> Cc: Ingo Molnar <mingo@redhat.com> Cc: James Clark <james.clark@arm.com> Cc: Jiri Olsa <jolsa@kernel.org> Cc: K Prateek Nayak <kprateek.nayak@amd.com> Cc: Kajol Jain <kjain@linux.ibm.com> Cc: Kan Liang <kan.liang@linux.intel.com> Cc: Li Dong <lidong@vivo.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Namhyung Kim <namhyung@kernel.org> Cc: Oliver Upton <oliver.upton@linux.dev> Cc: Paran Lee <p4ranlee@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Ravi Bangoria <ravi.bangoria@amd.com> Cc: Sun Haiyong <sunhaiyong@loongson.cn> Cc: Tim Chen <tim.c.chen@linux.intel.com> Cc: Yanteng Si <siyanteng@loongson.cn> Cc: Yicong Yang <yangyicong@hisilicon.com> Link: https://lore.kernel.org/r/20240507183545.1236093-6-irogers@google.com Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
This commit is contained in:
parent
a8cd4766d9
commit
13ca628716
@ -1,108 +1,164 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include "comm.h"
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <internal/rc_check.h>
|
||||
#include <linux/refcount.h>
|
||||
#include <linux/rbtree.h>
|
||||
#include <linux/zalloc.h>
|
||||
#include "rwsem.h"
|
||||
|
||||
struct comm_str {
|
||||
char *str;
|
||||
struct rb_node rb_node;
|
||||
DECLARE_RC_STRUCT(comm_str) {
|
||||
refcount_t refcnt;
|
||||
char str[];
|
||||
};
|
||||
|
||||
/* Should perhaps be moved to struct machine */
|
||||
static struct rb_root comm_str_root;
|
||||
static struct rw_semaphore comm_str_lock = {.lock = PTHREAD_RWLOCK_INITIALIZER,};
|
||||
static struct comm_strs {
|
||||
struct rw_semaphore lock;
|
||||
struct comm_str **strs;
|
||||
int num_strs;
|
||||
int capacity;
|
||||
} _comm_strs;
|
||||
|
||||
static void comm_strs__init(void)
|
||||
{
|
||||
init_rwsem(&_comm_strs.lock);
|
||||
_comm_strs.capacity = 16;
|
||||
_comm_strs.num_strs = 0;
|
||||
_comm_strs.strs = calloc(16, sizeof(*_comm_strs.strs));
|
||||
}
|
||||
|
||||
static struct comm_strs *comm_strs__get(void)
|
||||
{
|
||||
static pthread_once_t comm_strs_type_once = PTHREAD_ONCE_INIT;
|
||||
|
||||
pthread_once(&comm_strs_type_once, comm_strs__init);
|
||||
|
||||
return &_comm_strs;
|
||||
}
|
||||
|
||||
static refcount_t *comm_str__refcnt(struct comm_str *cs)
|
||||
{
|
||||
return &RC_CHK_ACCESS(cs)->refcnt;
|
||||
}
|
||||
|
||||
static const char *comm_str__str(const struct comm_str *cs)
|
||||
{
|
||||
return &RC_CHK_ACCESS(cs)->str[0];
|
||||
}
|
||||
|
||||
static struct comm_str *comm_str__get(struct comm_str *cs)
|
||||
{
|
||||
if (cs && refcount_inc_not_zero(&cs->refcnt))
|
||||
return cs;
|
||||
struct comm_str *result;
|
||||
|
||||
return NULL;
|
||||
if (RC_CHK_GET(result, cs))
|
||||
refcount_inc_not_zero(comm_str__refcnt(cs));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void comm_str__put(struct comm_str *cs)
|
||||
{
|
||||
if (cs && refcount_dec_and_test(&cs->refcnt)) {
|
||||
down_write(&comm_str_lock);
|
||||
rb_erase(&cs->rb_node, &comm_str_root);
|
||||
up_write(&comm_str_lock);
|
||||
zfree(&cs->str);
|
||||
free(cs);
|
||||
if (cs && refcount_dec_and_test(comm_str__refcnt(cs))) {
|
||||
struct comm_strs *comm_strs = comm_strs__get();
|
||||
int i;
|
||||
|
||||
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);
|
||||
RC_CHK_FREE(cs);
|
||||
} else {
|
||||
RC_CHK_PUT(cs);
|
||||
}
|
||||
}
|
||||
|
||||
static struct comm_str *comm_str__alloc(const char *str)
|
||||
static struct comm_str *comm_str__new(const char *str)
|
||||
{
|
||||
struct comm_str *cs;
|
||||
struct comm_str *result = NULL;
|
||||
RC_STRUCT(comm_str) *cs;
|
||||
|
||||
cs = zalloc(sizeof(*cs));
|
||||
if (!cs)
|
||||
return NULL;
|
||||
|
||||
cs->str = strdup(str);
|
||||
if (!cs->str) {
|
||||
free(cs);
|
||||
return NULL;
|
||||
cs = malloc(sizeof(*cs) + strlen(str) + 1);
|
||||
if (ADD_RC_CHK(result, cs)) {
|
||||
refcount_set(comm_str__refcnt(result), 1);
|
||||
strcpy(&cs->str[0], str);
|
||||
}
|
||||
|
||||
refcount_set(&cs->refcnt, 1);
|
||||
|
||||
return cs;
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
struct comm_str *__comm_str__findnew(const char *str, struct rb_root *root)
|
||||
static int comm_str__cmp(const void *_lhs, const void *_rhs)
|
||||
{
|
||||
struct rb_node **p = &root->rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct comm_str *iter, *new;
|
||||
int cmp;
|
||||
const struct comm_str *lhs = *(const struct comm_str * const *)_lhs;
|
||||
const struct comm_str *rhs = *(const struct comm_str * const *)_rhs;
|
||||
|
||||
while (*p != NULL) {
|
||||
parent = *p;
|
||||
iter = rb_entry(parent, struct comm_str, rb_node);
|
||||
|
||||
/*
|
||||
* If we race with comm_str__put, iter->refcnt is 0
|
||||
* and it will be removed within comm_str__put call
|
||||
* shortly, ignore it in this search.
|
||||
*/
|
||||
cmp = strcmp(str, iter->str);
|
||||
if (!cmp && comm_str__get(iter))
|
||||
return iter;
|
||||
|
||||
if (cmp < 0)
|
||||
p = &(*p)->rb_left;
|
||||
else
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
new = comm_str__alloc(str);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
rb_link_node(&new->rb_node, parent, p);
|
||||
rb_insert_color(&new->rb_node, root);
|
||||
|
||||
return new;
|
||||
return strcmp(comm_str__str(lhs), comm_str__str(rhs));
|
||||
}
|
||||
|
||||
static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
|
||||
static int comm_str__search(const void *_key, const void *_member)
|
||||
{
|
||||
struct comm_str *cs;
|
||||
const char *key = _key;
|
||||
const struct comm_str *member = *(const struct comm_str * const *)_member;
|
||||
|
||||
down_write(&comm_str_lock);
|
||||
cs = __comm_str__findnew(str, root);
|
||||
up_write(&comm_str_lock);
|
||||
return strcmp(key, comm_str__str(member));
|
||||
}
|
||||
|
||||
return cs;
|
||||
static struct comm_str *__comm_strs__find(struct comm_strs *comm_strs, const char *str)
|
||||
{
|
||||
struct comm_str **result;
|
||||
|
||||
result = bsearch(str, comm_strs->strs, comm_strs->num_strs, sizeof(struct comm_str *),
|
||||
comm_str__search);
|
||||
|
||||
if (!result)
|
||||
return NULL;
|
||||
|
||||
return comm_str__get(*result);
|
||||
}
|
||||
|
||||
static struct comm_str *comm_strs__findnew(const char *str)
|
||||
{
|
||||
struct comm_strs *comm_strs = comm_strs__get();
|
||||
struct comm_str *result;
|
||||
|
||||
if (!comm_strs)
|
||||
return NULL;
|
||||
|
||||
down_read(&comm_strs->lock);
|
||||
result = __comm_strs__find(comm_strs, str);
|
||||
up_read(&comm_strs->lock);
|
||||
if (result)
|
||||
return result;
|
||||
|
||||
down_write(&comm_strs->lock);
|
||||
result = __comm_strs__find(comm_strs, str);
|
||||
if (!result) {
|
||||
if (comm_strs->num_strs == comm_strs->capacity) {
|
||||
struct comm_str **tmp;
|
||||
|
||||
tmp = reallocarray(comm_strs->strs,
|
||||
comm_strs->capacity + 16,
|
||||
sizeof(*comm_strs->strs));
|
||||
if (!tmp) {
|
||||
up_write(&comm_strs->lock);
|
||||
return NULL;
|
||||
}
|
||||
comm_strs->strs = tmp;
|
||||
comm_strs->capacity += 16;
|
||||
}
|
||||
result = comm_str__new(str);
|
||||
if (result) {
|
||||
comm_strs->strs[comm_strs->num_strs++] = result;
|
||||
qsort(comm_strs->strs, comm_strs->num_strs, sizeof(struct comm_str *),
|
||||
comm_str__cmp);
|
||||
}
|
||||
}
|
||||
up_write(&comm_strs->lock);
|
||||
return result;
|
||||
}
|
||||
|
||||
struct comm *comm__new(const char *str, u64 timestamp, bool exec)
|
||||
@ -115,7 +171,7 @@ struct comm *comm__new(const char *str, u64 timestamp, bool exec)
|
||||
comm->start = timestamp;
|
||||
comm->exec = exec;
|
||||
|
||||
comm->comm_str = comm_str__findnew(str, &comm_str_root);
|
||||
comm->comm_str = comm_strs__findnew(str);
|
||||
if (!comm->comm_str) {
|
||||
free(comm);
|
||||
return NULL;
|
||||
@ -128,7 +184,7 @@ int comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec)
|
||||
{
|
||||
struct comm_str *new, *old = comm->comm_str;
|
||||
|
||||
new = comm_str__findnew(str, &comm_str_root);
|
||||
new = comm_strs__findnew(str);
|
||||
if (!new)
|
||||
return -ENOMEM;
|
||||
|
||||
@ -149,5 +205,5 @@ void comm__free(struct comm *comm)
|
||||
|
||||
const char *comm__str(const struct comm *comm)
|
||||
{
|
||||
return comm->comm_str->str;
|
||||
return comm_str__str(comm->comm_str);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user