cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cgroup.c (11756B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <subcmd/parse-options.h>
      3#include "evsel.h"
      4#include "cgroup.h"
      5#include "evlist.h"
      6#include "rblist.h"
      7#include "metricgroup.h"
      8#include "stat.h"
      9#include <linux/zalloc.h>
     10#include <sys/types.h>
     11#include <sys/stat.h>
     12#include <sys/statfs.h>
     13#include <fcntl.h>
     14#include <stdlib.h>
     15#include <string.h>
     16#include <api/fs/fs.h>
     17#include <ftw.h>
     18#include <regex.h>
     19
     20int nr_cgroups;
     21bool cgrp_event_expanded;
     22
     23/* used to match cgroup name with patterns */
     24struct cgroup_name {
     25	struct list_head list;
     26	bool used;
     27	char name[];
     28};
     29static LIST_HEAD(cgroup_list);
     30
     31static int open_cgroup(const char *name)
     32{
     33	char path[PATH_MAX + 1];
     34	char mnt[PATH_MAX + 1];
     35	int fd;
     36
     37
     38	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
     39		return -1;
     40
     41	scnprintf(path, PATH_MAX, "%s/%s", mnt, name);
     42
     43	fd = open(path, O_RDONLY);
     44	if (fd == -1)
     45		fprintf(stderr, "no access to cgroup %s\n", path);
     46
     47	return fd;
     48}
     49
     50#ifdef HAVE_FILE_HANDLE
     51int read_cgroup_id(struct cgroup *cgrp)
     52{
     53	char path[PATH_MAX + 1];
     54	char mnt[PATH_MAX + 1];
     55	struct {
     56		struct file_handle fh;
     57		uint64_t cgroup_id;
     58	} handle;
     59	int mount_id;
     60
     61	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, "perf_event"))
     62		return -1;
     63
     64	scnprintf(path, PATH_MAX, "%s/%s", mnt, cgrp->name);
     65
     66	handle.fh.handle_bytes = sizeof(handle.cgroup_id);
     67	if (name_to_handle_at(AT_FDCWD, path, &handle.fh, &mount_id, 0) < 0)
     68		return -1;
     69
     70	cgrp->id = handle.cgroup_id;
     71	return 0;
     72}
     73#endif  /* HAVE_FILE_HANDLE */
     74
     75#ifndef CGROUP2_SUPER_MAGIC
     76#define CGROUP2_SUPER_MAGIC  0x63677270
     77#endif
     78
     79int cgroup_is_v2(const char *subsys)
     80{
     81	char mnt[PATH_MAX + 1];
     82	struct statfs stbuf;
     83
     84	if (cgroupfs_find_mountpoint(mnt, PATH_MAX + 1, subsys))
     85		return -1;
     86
     87	if (statfs(mnt, &stbuf) < 0)
     88		return -1;
     89
     90	return (stbuf.f_type == CGROUP2_SUPER_MAGIC);
     91}
     92
     93static struct cgroup *evlist__find_cgroup(struct evlist *evlist, const char *str)
     94{
     95	struct evsel *counter;
     96	/*
     97	 * check if cgrp is already defined, if so we reuse it
     98	 */
     99	evlist__for_each_entry(evlist, counter) {
    100		if (!counter->cgrp)
    101			continue;
    102		if (!strcmp(counter->cgrp->name, str))
    103			return cgroup__get(counter->cgrp);
    104	}
    105
    106	return NULL;
    107}
    108
    109static struct cgroup *cgroup__new(const char *name, bool do_open)
    110{
    111	struct cgroup *cgroup = zalloc(sizeof(*cgroup));
    112
    113	if (cgroup != NULL) {
    114		refcount_set(&cgroup->refcnt, 1);
    115
    116		cgroup->name = strdup(name);
    117		if (!cgroup->name)
    118			goto out_err;
    119
    120		if (do_open) {
    121			cgroup->fd = open_cgroup(name);
    122			if (cgroup->fd == -1)
    123				goto out_free_name;
    124		} else {
    125			cgroup->fd = -1;
    126		}
    127	}
    128
    129	return cgroup;
    130
    131out_free_name:
    132	zfree(&cgroup->name);
    133out_err:
    134	free(cgroup);
    135	return NULL;
    136}
    137
    138struct cgroup *evlist__findnew_cgroup(struct evlist *evlist, const char *name)
    139{
    140	struct cgroup *cgroup = evlist__find_cgroup(evlist, name);
    141
    142	return cgroup ?: cgroup__new(name, true);
    143}
    144
    145static int add_cgroup(struct evlist *evlist, const char *str)
    146{
    147	struct evsel *counter;
    148	struct cgroup *cgrp = evlist__findnew_cgroup(evlist, str);
    149	int n;
    150
    151	if (!cgrp)
    152		return -1;
    153	/*
    154	 * find corresponding event
    155	 * if add cgroup N, then need to find event N
    156	 */
    157	n = 0;
    158	evlist__for_each_entry(evlist, counter) {
    159		if (n == nr_cgroups)
    160			goto found;
    161		n++;
    162	}
    163
    164	cgroup__put(cgrp);
    165	return -1;
    166found:
    167	counter->cgrp = cgrp;
    168	return 0;
    169}
    170
    171static void cgroup__delete(struct cgroup *cgroup)
    172{
    173	if (cgroup->fd >= 0)
    174		close(cgroup->fd);
    175	zfree(&cgroup->name);
    176	free(cgroup);
    177}
    178
    179void cgroup__put(struct cgroup *cgrp)
    180{
    181	if (cgrp && refcount_dec_and_test(&cgrp->refcnt)) {
    182		cgroup__delete(cgrp);
    183	}
    184}
    185
    186struct cgroup *cgroup__get(struct cgroup *cgroup)
    187{
    188       if (cgroup)
    189		refcount_inc(&cgroup->refcnt);
    190       return cgroup;
    191}
    192
    193static void evsel__set_default_cgroup(struct evsel *evsel, struct cgroup *cgroup)
    194{
    195	if (evsel->cgrp == NULL)
    196		evsel->cgrp = cgroup__get(cgroup);
    197}
    198
    199void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup)
    200{
    201	struct evsel *evsel;
    202
    203	evlist__for_each_entry(evlist, evsel)
    204		evsel__set_default_cgroup(evsel, cgroup);
    205}
    206
    207/* helper function for ftw() in match_cgroups and list_cgroups */
    208static int add_cgroup_name(const char *fpath, const struct stat *sb __maybe_unused,
    209			   int typeflag, struct FTW *ftwbuf __maybe_unused)
    210{
    211	struct cgroup_name *cn;
    212
    213	if (typeflag != FTW_D)
    214		return 0;
    215
    216	cn = malloc(sizeof(*cn) + strlen(fpath) + 1);
    217	if (cn == NULL)
    218		return -1;
    219
    220	cn->used = false;
    221	strcpy(cn->name, fpath);
    222
    223	list_add_tail(&cn->list, &cgroup_list);
    224	return 0;
    225}
    226
    227static void release_cgroup_list(void)
    228{
    229	struct cgroup_name *cn;
    230
    231	while (!list_empty(&cgroup_list)) {
    232		cn = list_first_entry(&cgroup_list, struct cgroup_name, list);
    233		list_del(&cn->list);
    234		free(cn);
    235	}
    236}
    237
    238/* collect given cgroups only */
    239static int list_cgroups(const char *str)
    240{
    241	const char *p, *e, *eos = str + strlen(str);
    242	struct cgroup_name *cn;
    243	char *s;
    244
    245	/* use given name as is - for testing purpose */
    246	for (;;) {
    247		p = strchr(str, ',');
    248		e = p ? p : eos;
    249
    250		if (e - str) {
    251			int ret;
    252
    253			s = strndup(str, e - str);
    254			if (!s)
    255				return -1;
    256			/* pretend if it's added by ftw() */
    257			ret = add_cgroup_name(s, NULL, FTW_D, NULL);
    258			free(s);
    259			if (ret)
    260				return -1;
    261		} else {
    262			if (add_cgroup_name("", NULL, FTW_D, NULL) < 0)
    263				return -1;
    264		}
    265
    266		if (!p)
    267			break;
    268		str = p+1;
    269	}
    270
    271	/* these groups will be used */
    272	list_for_each_entry(cn, &cgroup_list, list)
    273		cn->used = true;
    274
    275	return 0;
    276}
    277
    278/* collect all cgroups first and then match with the pattern */
    279static int match_cgroups(const char *str)
    280{
    281	char mnt[PATH_MAX];
    282	const char *p, *e, *eos = str + strlen(str);
    283	struct cgroup_name *cn;
    284	regex_t reg;
    285	int prefix_len;
    286	char *s;
    287
    288	if (cgroupfs_find_mountpoint(mnt, sizeof(mnt), "perf_event"))
    289		return -1;
    290
    291	/* cgroup_name will have a full path, skip the root directory */
    292	prefix_len = strlen(mnt);
    293
    294	/* collect all cgroups in the cgroup_list */
    295	if (nftw(mnt, add_cgroup_name, 20, 0) < 0)
    296		return -1;
    297
    298	for (;;) {
    299		p = strchr(str, ',');
    300		e = p ? p : eos;
    301
    302		/* allow empty cgroups, i.e., skip */
    303		if (e - str) {
    304			/* termination added */
    305			s = strndup(str, e - str);
    306			if (!s)
    307				return -1;
    308			if (regcomp(&reg, s, REG_NOSUB)) {
    309				free(s);
    310				return -1;
    311			}
    312
    313			/* check cgroup name with the pattern */
    314			list_for_each_entry(cn, &cgroup_list, list) {
    315				char *name = cn->name + prefix_len;
    316
    317				if (name[0] == '/' && name[1])
    318					name++;
    319				if (!regexec(&reg, name, 0, NULL, 0))
    320					cn->used = true;
    321			}
    322			regfree(&reg);
    323			free(s);
    324		} else {
    325			/* first entry to root cgroup */
    326			cn = list_first_entry(&cgroup_list, struct cgroup_name,
    327					      list);
    328			cn->used = true;
    329		}
    330
    331		if (!p)
    332			break;
    333		str = p+1;
    334	}
    335	return prefix_len;
    336}
    337
    338int parse_cgroups(const struct option *opt, const char *str,
    339		  int unset __maybe_unused)
    340{
    341	struct evlist *evlist = *(struct evlist **)opt->value;
    342	struct evsel *counter;
    343	struct cgroup *cgrp = NULL;
    344	const char *p, *e, *eos = str + strlen(str);
    345	char *s;
    346	int ret, i;
    347
    348	if (list_empty(&evlist->core.entries)) {
    349		fprintf(stderr, "must define events before cgroups\n");
    350		return -1;
    351	}
    352
    353	for (;;) {
    354		p = strchr(str, ',');
    355		e = p ? p : eos;
    356
    357		/* allow empty cgroups, i.e., skip */
    358		if (e - str) {
    359			/* termination added */
    360			s = strndup(str, e - str);
    361			if (!s)
    362				return -1;
    363			ret = add_cgroup(evlist, s);
    364			free(s);
    365			if (ret)
    366				return -1;
    367		}
    368		/* nr_cgroups is increased een for empty cgroups */
    369		nr_cgroups++;
    370		if (!p)
    371			break;
    372		str = p+1;
    373	}
    374	/* for the case one cgroup combine to multiple events */
    375	i = 0;
    376	if (nr_cgroups == 1) {
    377		evlist__for_each_entry(evlist, counter) {
    378			if (i == 0)
    379				cgrp = counter->cgrp;
    380			else {
    381				counter->cgrp = cgrp;
    382				refcount_inc(&cgrp->refcnt);
    383			}
    384			i++;
    385		}
    386	}
    387	return 0;
    388}
    389
    390static bool has_pattern_string(const char *str)
    391{
    392	return !!strpbrk(str, "{}[]()|*+?^$");
    393}
    394
    395int evlist__expand_cgroup(struct evlist *evlist, const char *str,
    396			  struct rblist *metric_events, bool open_cgroup)
    397{
    398	struct evlist *orig_list, *tmp_list;
    399	struct evsel *pos, *evsel, *leader;
    400	struct rblist orig_metric_events;
    401	struct cgroup *cgrp = NULL;
    402	struct cgroup_name *cn;
    403	int ret = -1;
    404	int prefix_len;
    405
    406	if (evlist->core.nr_entries == 0) {
    407		fprintf(stderr, "must define events before cgroups\n");
    408		return -EINVAL;
    409	}
    410
    411	orig_list = evlist__new();
    412	tmp_list = evlist__new();
    413	if (orig_list == NULL || tmp_list == NULL) {
    414		fprintf(stderr, "memory allocation failed\n");
    415		return -ENOMEM;
    416	}
    417
    418	/* save original events and init evlist */
    419	evlist__splice_list_tail(orig_list, &evlist->core.entries);
    420	evlist->core.nr_entries = 0;
    421
    422	if (metric_events) {
    423		orig_metric_events = *metric_events;
    424		rblist__init(metric_events);
    425	} else {
    426		rblist__init(&orig_metric_events);
    427	}
    428
    429	if (has_pattern_string(str))
    430		prefix_len = match_cgroups(str);
    431	else
    432		prefix_len = list_cgroups(str);
    433
    434	if (prefix_len < 0)
    435		goto out_err;
    436
    437	list_for_each_entry(cn, &cgroup_list, list) {
    438		char *name;
    439
    440		if (!cn->used)
    441			continue;
    442
    443		/* cgroup_name might have a full path, skip the prefix */
    444		name = cn->name + prefix_len;
    445		if (name[0] == '/' && name[1])
    446			name++;
    447		cgrp = cgroup__new(name, open_cgroup);
    448		if (cgrp == NULL)
    449			goto out_err;
    450
    451		leader = NULL;
    452		evlist__for_each_entry(orig_list, pos) {
    453			evsel = evsel__clone(pos);
    454			if (evsel == NULL)
    455				goto out_err;
    456
    457			cgroup__put(evsel->cgrp);
    458			evsel->cgrp = cgroup__get(cgrp);
    459
    460			if (evsel__is_group_leader(pos))
    461				leader = evsel;
    462			evsel__set_leader(evsel, leader);
    463
    464			evlist__add(tmp_list, evsel);
    465		}
    466		/* cgroup__new() has a refcount, release it here */
    467		cgroup__put(cgrp);
    468		nr_cgroups++;
    469
    470		if (metric_events) {
    471			perf_stat__collect_metric_expr(tmp_list);
    472			if (metricgroup__copy_metric_events(tmp_list, cgrp,
    473							    metric_events,
    474							    &orig_metric_events) < 0)
    475				goto out_err;
    476		}
    477
    478		evlist__splice_list_tail(evlist, &tmp_list->core.entries);
    479		tmp_list->core.nr_entries = 0;
    480	}
    481
    482	if (list_empty(&evlist->core.entries)) {
    483		fprintf(stderr, "no cgroup matched: %s\n", str);
    484		goto out_err;
    485	}
    486
    487	ret = 0;
    488	cgrp_event_expanded = true;
    489
    490out_err:
    491	evlist__delete(orig_list);
    492	evlist__delete(tmp_list);
    493	rblist__exit(&orig_metric_events);
    494	release_cgroup_list();
    495
    496	return ret;
    497}
    498
    499static struct cgroup *__cgroup__findnew(struct rb_root *root, uint64_t id,
    500					bool create, const char *path)
    501{
    502	struct rb_node **p = &root->rb_node;
    503	struct rb_node *parent = NULL;
    504	struct cgroup *cgrp;
    505
    506	while (*p != NULL) {
    507		parent = *p;
    508		cgrp = rb_entry(parent, struct cgroup, node);
    509
    510		if (cgrp->id == id)
    511			return cgrp;
    512
    513		if (cgrp->id < id)
    514			p = &(*p)->rb_left;
    515		else
    516			p = &(*p)->rb_right;
    517	}
    518
    519	if (!create)
    520		return NULL;
    521
    522	cgrp = malloc(sizeof(*cgrp));
    523	if (cgrp == NULL)
    524		return NULL;
    525
    526	cgrp->name = strdup(path);
    527	if (cgrp->name == NULL) {
    528		free(cgrp);
    529		return NULL;
    530	}
    531
    532	cgrp->fd = -1;
    533	cgrp->id = id;
    534	refcount_set(&cgrp->refcnt, 1);
    535
    536	rb_link_node(&cgrp->node, parent, p);
    537	rb_insert_color(&cgrp->node, root);
    538
    539	return cgrp;
    540}
    541
    542struct cgroup *cgroup__findnew(struct perf_env *env, uint64_t id,
    543			       const char *path)
    544{
    545	struct cgroup *cgrp;
    546
    547	down_write(&env->cgroups.lock);
    548	cgrp = __cgroup__findnew(&env->cgroups.tree, id, true, path);
    549	up_write(&env->cgroups.lock);
    550	return cgrp;
    551}
    552
    553struct cgroup *cgroup__find(struct perf_env *env, uint64_t id)
    554{
    555	struct cgroup *cgrp;
    556
    557	down_read(&env->cgroups.lock);
    558	cgrp = __cgroup__findnew(&env->cgroups.tree, id, false, NULL);
    559	up_read(&env->cgroups.lock);
    560	return cgrp;
    561}
    562
    563void perf_env__purge_cgroups(struct perf_env *env)
    564{
    565	struct rb_node *node;
    566	struct cgroup *cgrp;
    567
    568	down_write(&env->cgroups.lock);
    569	while (!RB_EMPTY_ROOT(&env->cgroups.tree)) {
    570		node = rb_first(&env->cgroups.tree);
    571		cgrp = rb_entry(node, struct cgroup, node);
    572
    573		rb_erase(node, &env->cgroups.tree);
    574		cgroup__put(cgrp);
    575	}
    576	up_write(&env->cgroups.lock);
    577}