162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Guest agent for virtio-trace
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Hitachi, Ltd.
662306a36Sopenharmony_ci * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
762306a36Sopenharmony_ci *            Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define _GNU_SOURCE
1162306a36Sopenharmony_ci#include <limits.h>
1262306a36Sopenharmony_ci#include <stdio.h>
1362306a36Sopenharmony_ci#include <stdlib.h>
1462306a36Sopenharmony_ci#include <unistd.h>
1562306a36Sopenharmony_ci#include "trace-agent.h"
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#define PAGE_SIZE		(sysconf(_SC_PAGE_SIZE))
1862306a36Sopenharmony_ci#define PIPE_DEF_BUFS		16
1962306a36Sopenharmony_ci#define PIPE_MIN_SIZE		(PAGE_SIZE*PIPE_DEF_BUFS)
2062306a36Sopenharmony_ci#define PIPE_MAX_SIZE		(1024*1024)
2162306a36Sopenharmony_ci#define TRACEFS 		"/sys/kernel/tracing"
2262306a36Sopenharmony_ci#define DEBUGFS 		"/sys/kernel/debug/tracing"
2362306a36Sopenharmony_ci#define READ_PATH_FMT		"%s/per_cpu/cpu%d/trace_pipe_raw"
2462306a36Sopenharmony_ci#define WRITE_PATH_FMT		"/dev/virtio-ports/trace-path-cpu%d"
2562306a36Sopenharmony_ci#define CTL_PATH		"/dev/virtio-ports/agent-ctl-path"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cipthread_mutex_t mutex_notify = PTHREAD_MUTEX_INITIALIZER;
2862306a36Sopenharmony_cipthread_cond_t cond_wakeup = PTHREAD_COND_INITIALIZER;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistatic int get_total_cpus(void)
3162306a36Sopenharmony_ci{
3262306a36Sopenharmony_ci	int nr_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	if (nr_cpus <= 0) {
3562306a36Sopenharmony_ci		pr_err("Could not read cpus\n");
3662306a36Sopenharmony_ci		goto error;
3762306a36Sopenharmony_ci	} else if (nr_cpus > MAX_CPUS) {
3862306a36Sopenharmony_ci		pr_err("Exceed max cpus(%d)\n", (int)MAX_CPUS);
3962306a36Sopenharmony_ci		goto error;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return nr_cpus;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cierror:
4562306a36Sopenharmony_ci	exit(EXIT_FAILURE);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic void *agent_info_new(void)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct agent_info *s;
5162306a36Sopenharmony_ci	int i;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	s = zalloc(sizeof(struct agent_info));
5462306a36Sopenharmony_ci	if (s == NULL) {
5562306a36Sopenharmony_ci		pr_err("agent_info zalloc error\n");
5662306a36Sopenharmony_ci		exit(EXIT_FAILURE);
5762306a36Sopenharmony_ci	}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	s->pipe_size = PIPE_INIT;
6062306a36Sopenharmony_ci	s->use_stdout = false;
6162306a36Sopenharmony_ci	s->cpus = get_total_cpus();
6262306a36Sopenharmony_ci	s->ctl_fd = -1;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* read/write threads init */
6562306a36Sopenharmony_ci	for (i = 0; i < s->cpus; i++)
6662306a36Sopenharmony_ci		s->rw_ti[i] = rw_thread_info_new();
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	return s;
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_cistatic unsigned long parse_size(const char *arg)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	unsigned long value, round;
7462306a36Sopenharmony_ci	char *ptr;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	value = strtoul(arg, &ptr, 10);
7762306a36Sopenharmony_ci	switch (*ptr) {
7862306a36Sopenharmony_ci	case 'K': case 'k':
7962306a36Sopenharmony_ci		value <<= 10;
8062306a36Sopenharmony_ci		break;
8162306a36Sopenharmony_ci	case 'M': case 'm':
8262306a36Sopenharmony_ci		value <<= 20;
8362306a36Sopenharmony_ci		break;
8462306a36Sopenharmony_ci	default:
8562306a36Sopenharmony_ci		break;
8662306a36Sopenharmony_ci	}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	if (value > PIPE_MAX_SIZE) {
8962306a36Sopenharmony_ci		pr_err("Pipe size must be less than 1MB\n");
9062306a36Sopenharmony_ci		goto error;
9162306a36Sopenharmony_ci	} else if (value < PIPE_MIN_SIZE) {
9262306a36Sopenharmony_ci		pr_err("Pipe size must be over 64KB\n");
9362306a36Sopenharmony_ci		goto error;
9462306a36Sopenharmony_ci	}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Align buffer size with page unit */
9762306a36Sopenharmony_ci	round = value & (PAGE_SIZE - 1);
9862306a36Sopenharmony_ci	value = value - round;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	return value;
10162306a36Sopenharmony_cierror:
10262306a36Sopenharmony_ci	return 0;
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic void usage(char const *prg)
10662306a36Sopenharmony_ci{
10762306a36Sopenharmony_ci	pr_err("usage: %s [-h] [-o] [-s <size of pipe>]\n", prg);
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_cistatic const char *make_path(int cpu_num, bool this_is_write_path)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	int ret;
11362306a36Sopenharmony_ci	char *buf;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	buf = zalloc(PATH_MAX);
11662306a36Sopenharmony_ci	if (buf == NULL) {
11762306a36Sopenharmony_ci		pr_err("Could not allocate buffer\n");
11862306a36Sopenharmony_ci		goto error;
11962306a36Sopenharmony_ci	}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	if (this_is_write_path)
12262306a36Sopenharmony_ci		/* write(output) path */
12362306a36Sopenharmony_ci		ret = snprintf(buf, PATH_MAX, WRITE_PATH_FMT, cpu_num);
12462306a36Sopenharmony_ci	else {
12562306a36Sopenharmony_ci		/* read(input) path */
12662306a36Sopenharmony_ci		ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, TRACEFS, cpu_num);
12762306a36Sopenharmony_ci		if (ret > 0 && access(buf, F_OK) != 0)
12862306a36Sopenharmony_ci			ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, DEBUGFS, cpu_num);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (ret <= 0) {
13262306a36Sopenharmony_ci		pr_err("Failed to generate %s path(CPU#%d):%d\n",
13362306a36Sopenharmony_ci			this_is_write_path ? "read" : "write", cpu_num, ret);
13462306a36Sopenharmony_ci		goto error;
13562306a36Sopenharmony_ci	}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	return buf;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_cierror:
14062306a36Sopenharmony_ci	free(buf);
14162306a36Sopenharmony_ci	return NULL;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic const char *make_input_path(int cpu_num)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	return make_path(cpu_num, false);
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic const char *make_output_path(int cpu_num)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	return make_path(cpu_num, true);
15262306a36Sopenharmony_ci}
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_cistatic void *agent_info_init(struct agent_info *s)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	int cpu;
15762306a36Sopenharmony_ci	const char *in_path = NULL;
15862306a36Sopenharmony_ci	const char *out_path = NULL;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* init read/write threads */
16162306a36Sopenharmony_ci	for (cpu = 0; cpu < s->cpus; cpu++) {
16262306a36Sopenharmony_ci		/* set read(input) path per read/write thread */
16362306a36Sopenharmony_ci		in_path = make_input_path(cpu);
16462306a36Sopenharmony_ci		if (in_path == NULL)
16562306a36Sopenharmony_ci			goto error;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci		/* set write(output) path per read/write thread*/
16862306a36Sopenharmony_ci		if (!s->use_stdout) {
16962306a36Sopenharmony_ci			out_path = make_output_path(cpu);
17062306a36Sopenharmony_ci			if (out_path == NULL)
17162306a36Sopenharmony_ci				goto error;
17262306a36Sopenharmony_ci		} else
17362306a36Sopenharmony_ci			/* stdout mode */
17462306a36Sopenharmony_ci			pr_debug("stdout mode\n");
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci		rw_thread_init(cpu, in_path, out_path, s->use_stdout,
17762306a36Sopenharmony_ci						s->pipe_size, s->rw_ti[cpu]);
17862306a36Sopenharmony_ci	}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci	/* init controller of read/write threads */
18162306a36Sopenharmony_ci	s->ctl_fd = rw_ctl_init((const char *)CTL_PATH);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	return NULL;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cierror:
18662306a36Sopenharmony_ci	exit(EXIT_FAILURE);
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic void *parse_args(int argc, char *argv[], struct agent_info *s)
19062306a36Sopenharmony_ci{
19162306a36Sopenharmony_ci	int cmd;
19262306a36Sopenharmony_ci	unsigned long size;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	while ((cmd = getopt(argc, argv, "hos:")) != -1) {
19562306a36Sopenharmony_ci		switch (cmd) {
19662306a36Sopenharmony_ci		/* stdout mode */
19762306a36Sopenharmony_ci		case 'o':
19862306a36Sopenharmony_ci			s->use_stdout = true;
19962306a36Sopenharmony_ci			break;
20062306a36Sopenharmony_ci		/* size of pipe */
20162306a36Sopenharmony_ci		case 's':
20262306a36Sopenharmony_ci			size = parse_size(optarg);
20362306a36Sopenharmony_ci			if (size == 0)
20462306a36Sopenharmony_ci				goto error;
20562306a36Sopenharmony_ci			s->pipe_size = size;
20662306a36Sopenharmony_ci			break;
20762306a36Sopenharmony_ci		case 'h':
20862306a36Sopenharmony_ci		default:
20962306a36Sopenharmony_ci			usage(argv[0]);
21062306a36Sopenharmony_ci			goto error;
21162306a36Sopenharmony_ci		}
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	agent_info_init(s);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	return NULL;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cierror:
21962306a36Sopenharmony_ci	exit(EXIT_FAILURE);
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic void agent_main_loop(struct agent_info *s)
22362306a36Sopenharmony_ci{
22462306a36Sopenharmony_ci	int cpu;
22562306a36Sopenharmony_ci	pthread_t rw_thread_per_cpu[MAX_CPUS];
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	/* Start all read/write threads */
22862306a36Sopenharmony_ci	for (cpu = 0; cpu < s->cpus; cpu++)
22962306a36Sopenharmony_ci		rw_thread_per_cpu[cpu] = rw_thread_run(s->rw_ti[cpu]);
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	rw_ctl_loop(s->ctl_fd);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	/* Finish all read/write threads */
23462306a36Sopenharmony_ci	for (cpu = 0; cpu < s->cpus; cpu++) {
23562306a36Sopenharmony_ci		int ret;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci		ret = pthread_join(rw_thread_per_cpu[cpu], NULL);
23862306a36Sopenharmony_ci		if (ret != 0) {
23962306a36Sopenharmony_ci			pr_err("pthread_join() error:%d (cpu %d)\n", ret, cpu);
24062306a36Sopenharmony_ci			exit(EXIT_FAILURE);
24162306a36Sopenharmony_ci		}
24262306a36Sopenharmony_ci	}
24362306a36Sopenharmony_ci}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_cistatic void agent_info_free(struct agent_info *s)
24662306a36Sopenharmony_ci{
24762306a36Sopenharmony_ci	int i;
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	close(s->ctl_fd);
25062306a36Sopenharmony_ci	for (i = 0; i < s->cpus; i++) {
25162306a36Sopenharmony_ci		close(s->rw_ti[i]->in_fd);
25262306a36Sopenharmony_ci		close(s->rw_ti[i]->out_fd);
25362306a36Sopenharmony_ci		close(s->rw_ti[i]->read_pipe);
25462306a36Sopenharmony_ci		close(s->rw_ti[i]->write_pipe);
25562306a36Sopenharmony_ci		free(s->rw_ti[i]);
25662306a36Sopenharmony_ci	}
25762306a36Sopenharmony_ci	free(s);
25862306a36Sopenharmony_ci}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ciint main(int argc, char *argv[])
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	struct agent_info *s = NULL;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	s = agent_info_new();
26562306a36Sopenharmony_ci	parse_args(argc, argv, s);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	agent_main_loop(s);
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	agent_info_free(s);
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return 0;
27262306a36Sopenharmony_ci}
273