trace-agent.c 5.11 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0-only
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
/*
 * Guest agent for virtio-trace
 *
 * Copyright (C) 2012 Hitachi, Ltd.
 * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
 *            Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
 */

#define _GNU_SOURCE
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "trace-agent.h"

#define PAGE_SIZE		(sysconf(_SC_PAGE_SIZE))
#define PIPE_DEF_BUFS		16
#define PIPE_MIN_SIZE		(PAGE_SIZE*PIPE_DEF_BUFS)
#define PIPE_MAX_SIZE		(1024*1024)
#define READ_PATH_FMT	\
		"/sys/kernel/debug/tracing/per_cpu/cpu%d/trace_pipe_raw"
#define WRITE_PATH_FMT		"/dev/virtio-ports/trace-path-cpu%d"
#define CTL_PATH		"/dev/virtio-ports/agent-ctl-path"

pthread_mutex_t mutex_notify = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond_wakeup = PTHREAD_COND_INITIALIZER;

static int get_total_cpus(void)
{
	int nr_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);

	if (nr_cpus <= 0) {
		pr_err("Could not read cpus\n");
		goto error;
	} else if (nr_cpus > MAX_CPUS) {
		pr_err("Exceed max cpus(%d)\n", (int)MAX_CPUS);
		goto error;
	}

	return nr_cpus;

error:
	exit(EXIT_FAILURE);
}

static void *agent_info_new(void)
{
	struct agent_info *s;
	int i;

	s = zalloc(sizeof(struct agent_info));
	if (s == NULL) {
		pr_err("agent_info zalloc error\n");
		exit(EXIT_FAILURE);
	}

	s->pipe_size = PIPE_INIT;
	s->use_stdout = false;
	s->cpus = get_total_cpus();
	s->ctl_fd = -1;

	/* read/write threads init */
	for (i = 0; i < s->cpus; i++)
		s->rw_ti[i] = rw_thread_info_new();

	return s;
}

static unsigned long parse_size(const char *arg)
{
	unsigned long value, round;
	char *ptr;

	value = strtoul(arg, &ptr, 10);
	switch (*ptr) {
	case 'K': case 'k':
		value <<= 10;
		break;
	case 'M': case 'm':
		value <<= 20;
		break;
	default:
		break;
	}

	if (value > PIPE_MAX_SIZE) {
		pr_err("Pipe size must be less than 1MB\n");
		goto error;
	} else if (value < PIPE_MIN_SIZE) {
		pr_err("Pipe size must be over 64KB\n");
		goto error;
	}

	/* Align buffer size with page unit */
	round = value & (PAGE_SIZE - 1);
	value = value - round;

	return value;
error:
	return 0;
}

static void usage(char const *prg)
{
	pr_err("usage: %s [-h] [-o] [-s <size of pipe>]\n", prg);
}

static const char *make_path(int cpu_num, bool this_is_write_path)
{
	int ret;
	char *buf;

	buf = zalloc(PATH_MAX);
	if (buf == NULL) {
		pr_err("Could not allocate buffer\n");
		goto error;
	}

	if (this_is_write_path)
		/* write(output) path */
		ret = snprintf(buf, PATH_MAX, WRITE_PATH_FMT, cpu_num);
	else
		/* read(input) path */
		ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, cpu_num);

	if (ret <= 0) {
		pr_err("Failed to generate %s path(CPU#%d):%d\n",
			this_is_write_path ? "read" : "write", cpu_num, ret);
		goto error;
	}

	return buf;

error:
	free(buf);
	return NULL;
}

static const char *make_input_path(int cpu_num)
{
	return make_path(cpu_num, false);
}

static const char *make_output_path(int cpu_num)
{
	return make_path(cpu_num, true);
}

static void *agent_info_init(struct agent_info *s)
{
	int cpu;
	const char *in_path = NULL;
	const char *out_path = NULL;

	/* init read/write threads */
	for (cpu = 0; cpu < s->cpus; cpu++) {
		/* set read(input) path per read/write thread */
		in_path = make_input_path(cpu);
		if (in_path == NULL)
			goto error;

		/* set write(output) path per read/write thread*/
		if (!s->use_stdout) {
			out_path = make_output_path(cpu);
			if (out_path == NULL)
				goto error;
		} else
			/* stdout mode */
			pr_debug("stdout mode\n");

		rw_thread_init(cpu, in_path, out_path, s->use_stdout,
						s->pipe_size, s->rw_ti[cpu]);
	}

	/* init controller of read/write threads */
	s->ctl_fd = rw_ctl_init((const char *)CTL_PATH);

	return NULL;

error:
	exit(EXIT_FAILURE);
}

static void *parse_args(int argc, char *argv[], struct agent_info *s)
{
	int cmd;
	unsigned long size;

	while ((cmd = getopt(argc, argv, "hos:")) != -1) {
		switch (cmd) {
		/* stdout mode */
		case 'o':
			s->use_stdout = true;
			break;
		/* size of pipe */
		case 's':
			size = parse_size(optarg);
			if (size == 0)
				goto error;
			s->pipe_size = size;
			break;
		case 'h':
		default:
			usage(argv[0]);
			goto error;
		}
	}

	agent_info_init(s);

	return NULL;

error:
	exit(EXIT_FAILURE);
}

static void agent_main_loop(struct agent_info *s)
{
	int cpu;
	pthread_t rw_thread_per_cpu[MAX_CPUS];

	/* Start all read/write threads */
	for (cpu = 0; cpu < s->cpus; cpu++)
		rw_thread_per_cpu[cpu] = rw_thread_run(s->rw_ti[cpu]);

	rw_ctl_loop(s->ctl_fd);

	/* Finish all read/write threads */
	for (cpu = 0; cpu < s->cpus; cpu++) {
		int ret;

		ret = pthread_join(rw_thread_per_cpu[cpu], NULL);
		if (ret != 0) {
			pr_err("pthread_join() error:%d (cpu %d)\n", ret, cpu);
			exit(EXIT_FAILURE);
		}
	}
}

static void agent_info_free(struct agent_info *s)
{
	int i;

	close(s->ctl_fd);
	for (i = 0; i < s->cpus; i++) {
		close(s->rw_ti[i]->in_fd);
		close(s->rw_ti[i]->out_fd);
		close(s->rw_ti[i]->read_pipe);
		close(s->rw_ti[i]->write_pipe);
		free(s->rw_ti[i]);
	}
	free(s);
}

int main(int argc, char *argv[])
{
	struct agent_info *s = NULL;

	s = agent_info_new();
	parse_args(argc, argv, s);

	agent_main_loop(s);

	agent_info_free(s);

	return 0;
}