18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include "comm.h"
38c2ecf20Sopenharmony_ci#include <errno.h>
48c2ecf20Sopenharmony_ci#include <stdlib.h>
58c2ecf20Sopenharmony_ci#include <stdio.h>
68c2ecf20Sopenharmony_ci#include <string.h>
78c2ecf20Sopenharmony_ci#include <linux/refcount.h>
88c2ecf20Sopenharmony_ci#include <linux/rbtree.h>
98c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
108c2ecf20Sopenharmony_ci#include "rwsem.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_cistruct comm_str {
138c2ecf20Sopenharmony_ci	char *str;
148c2ecf20Sopenharmony_ci	struct rb_node rb_node;
158c2ecf20Sopenharmony_ci	refcount_t refcnt;
168c2ecf20Sopenharmony_ci};
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci/* Should perhaps be moved to struct machine */
198c2ecf20Sopenharmony_cistatic struct rb_root comm_str_root;
208c2ecf20Sopenharmony_cistatic struct rw_semaphore comm_str_lock = {.lock = PTHREAD_RWLOCK_INITIALIZER,};
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic struct comm_str *comm_str__get(struct comm_str *cs)
238c2ecf20Sopenharmony_ci{
248c2ecf20Sopenharmony_ci	if (cs && refcount_inc_not_zero(&cs->refcnt))
258c2ecf20Sopenharmony_ci		return cs;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	return NULL;
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void comm_str__put(struct comm_str *cs)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	if (cs && refcount_dec_and_test(&cs->refcnt)) {
338c2ecf20Sopenharmony_ci		down_write(&comm_str_lock);
348c2ecf20Sopenharmony_ci		rb_erase(&cs->rb_node, &comm_str_root);
358c2ecf20Sopenharmony_ci		up_write(&comm_str_lock);
368c2ecf20Sopenharmony_ci		zfree(&cs->str);
378c2ecf20Sopenharmony_ci		free(cs);
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct comm_str *comm_str__alloc(const char *str)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	struct comm_str *cs;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	cs = zalloc(sizeof(*cs));
468c2ecf20Sopenharmony_ci	if (!cs)
478c2ecf20Sopenharmony_ci		return NULL;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	cs->str = strdup(str);
508c2ecf20Sopenharmony_ci	if (!cs->str) {
518c2ecf20Sopenharmony_ci		free(cs);
528c2ecf20Sopenharmony_ci		return NULL;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	refcount_set(&cs->refcnt, 1);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return cs;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic
618c2ecf20Sopenharmony_cistruct comm_str *__comm_str__findnew(const char *str, struct rb_root *root)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct rb_node **p = &root->rb_node;
648c2ecf20Sopenharmony_ci	struct rb_node *parent = NULL;
658c2ecf20Sopenharmony_ci	struct comm_str *iter, *new;
668c2ecf20Sopenharmony_ci	int cmp;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	while (*p != NULL) {
698c2ecf20Sopenharmony_ci		parent = *p;
708c2ecf20Sopenharmony_ci		iter = rb_entry(parent, struct comm_str, rb_node);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci		/*
738c2ecf20Sopenharmony_ci		 * If we race with comm_str__put, iter->refcnt is 0
748c2ecf20Sopenharmony_ci		 * and it will be removed within comm_str__put call
758c2ecf20Sopenharmony_ci		 * shortly, ignore it in this search.
768c2ecf20Sopenharmony_ci		 */
778c2ecf20Sopenharmony_ci		cmp = strcmp(str, iter->str);
788c2ecf20Sopenharmony_ci		if (!cmp && comm_str__get(iter))
798c2ecf20Sopenharmony_ci			return iter;
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci		if (cmp < 0)
828c2ecf20Sopenharmony_ci			p = &(*p)->rb_left;
838c2ecf20Sopenharmony_ci		else
848c2ecf20Sopenharmony_ci			p = &(*p)->rb_right;
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	new = comm_str__alloc(str);
888c2ecf20Sopenharmony_ci	if (!new)
898c2ecf20Sopenharmony_ci		return NULL;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	rb_link_node(&new->rb_node, parent, p);
928c2ecf20Sopenharmony_ci	rb_insert_color(&new->rb_node, root);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return new;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic struct comm_str *comm_str__findnew(const char *str, struct rb_root *root)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct comm_str *cs;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	down_write(&comm_str_lock);
1028c2ecf20Sopenharmony_ci	cs = __comm_str__findnew(str, root);
1038c2ecf20Sopenharmony_ci	up_write(&comm_str_lock);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	return cs;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_cistruct comm *comm__new(const char *str, u64 timestamp, bool exec)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct comm *comm = zalloc(sizeof(*comm));
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	if (!comm)
1138c2ecf20Sopenharmony_ci		return NULL;
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	comm->start = timestamp;
1168c2ecf20Sopenharmony_ci	comm->exec = exec;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	comm->comm_str = comm_str__findnew(str, &comm_str_root);
1198c2ecf20Sopenharmony_ci	if (!comm->comm_str) {
1208c2ecf20Sopenharmony_ci		free(comm);
1218c2ecf20Sopenharmony_ci		return NULL;
1228c2ecf20Sopenharmony_ci	}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	return comm;
1258c2ecf20Sopenharmony_ci}
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ciint comm__override(struct comm *comm, const char *str, u64 timestamp, bool exec)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	struct comm_str *new, *old = comm->comm_str;
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	new = comm_str__findnew(str, &comm_str_root);
1328c2ecf20Sopenharmony_ci	if (!new)
1338c2ecf20Sopenharmony_ci		return -ENOMEM;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	comm_str__put(old);
1368c2ecf20Sopenharmony_ci	comm->comm_str = new;
1378c2ecf20Sopenharmony_ci	comm->start = timestamp;
1388c2ecf20Sopenharmony_ci	if (exec)
1398c2ecf20Sopenharmony_ci		comm->exec = true;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_civoid comm__free(struct comm *comm)
1458c2ecf20Sopenharmony_ci{
1468c2ecf20Sopenharmony_ci	comm_str__put(comm->comm_str);
1478c2ecf20Sopenharmony_ci	free(comm);
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ciconst char *comm__str(const struct comm *comm)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	return comm->comm_str->str;
1538c2ecf20Sopenharmony_ci}
154