Commit 13ca6287 authored by Ian Rogers's avatar Ian Rogers Committed by Arnaldo Carvalho de Melo
Browse files

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: default avatarIan 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: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
parent a8cd4766
Loading
Loading
Loading
Loading
+126 −70
Original line number Diff line number Diff line
// 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 = malloc(sizeof(*cs) + strlen(str) + 1);
	if (ADD_RC_CHK(result, cs)) {
		refcount_set(comm_str__refcnt(result), 1);
		strcpy(&cs->str[0], str);
	}
	return result;
}

	cs->str = strdup(str);
	if (!cs->str) {
		free(cs);
		return NULL;
static int comm_str__cmp(const void *_lhs, const void *_rhs)
{
	const struct comm_str *lhs = *(const struct comm_str * const *)_lhs;
	const struct comm_str *rhs = *(const struct comm_str * const *)_rhs;

	return strcmp(comm_str__str(lhs), comm_str__str(rhs));
}

	refcount_set(&cs->refcnt, 1);
static int comm_str__search(const void *_key, const void *_member)
{
	const char *key = _key;
	const struct comm_str *member = *(const struct comm_str * const *)_member;

	return cs;
	return strcmp(key, comm_str__str(member));
}

static
struct comm_str *__comm_str__findnew(const char *str, struct rb_root *root)
static struct comm_str *__comm_strs__find(struct comm_strs *comm_strs, const char *str)
{
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;
	struct comm_str *iter, *new;
	int cmp;

	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;
	struct comm_str **result;

	rb_link_node(&new->rb_node, parent, p);
	rb_insert_color(&new->rb_node, root);
	result = bsearch(str, comm_strs->strs, comm_strs->num_strs, sizeof(struct comm_str *),
			 comm_str__search);

	return new;
	if (!result)
		return NULL;

	return comm_str__get(*result);
}

static struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
static struct comm_str *comm_strs__findnew(const char *str)
{
	struct comm_str *cs;
	struct comm_strs *comm_strs = comm_strs__get();
	struct comm_str *result;

	down_write(&comm_str_lock);
	cs = __comm_str__findnew(str, root);
	up_write(&comm_str_lock);
	if (!comm_strs)
		return NULL;

	return cs;
	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);
}