aboutsummaryrefslogtreecommitdiffstats
path: root/tools/virtio
diff options
context:
space:
mode:
authorYoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com>2012-08-09 08:31:30 -0400
committerRusty Russell <rusty@rustcorp.com.au>2012-09-28 01:35:13 -0400
commit108fc82596e3b66b819df9d28c1ebbc9ab5de14c (patch)
tree66c051fac35849764818a47aa278d073af72b182 /tools/virtio
parent8ca84a50e5b39487ea1de8809d0ee1c8474f6a5c (diff)
tools: Add guest trace agent as a user tool
This patch adds a user tool, "trace agent" for sending trace data of a guest to a Host in low overhead. This agent has the following functions: - splice a page of ring-buffer to read_pipe without memory copying - splice the page from write_pipe to virtio-console without memory copying - write trace data to stdout by using -o option - controlled by start/stop orders from a Host Changes in v2: - Cleanup (change fprintf() to pr_err() and an include guard) Signed-off-by: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com> Acked-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Diffstat (limited to 'tools/virtio')
-rw-r--r--tools/virtio/virtio-trace/Makefile14
-rw-r--r--tools/virtio/virtio-trace/README118
-rw-r--r--tools/virtio/virtio-trace/trace-agent-ctl.c137
-rw-r--r--tools/virtio/virtio-trace/trace-agent-rw.c192
-rw-r--r--tools/virtio/virtio-trace/trace-agent.c270
-rw-r--r--tools/virtio/virtio-trace/trace-agent.h75
6 files changed, 806 insertions, 0 deletions
diff --git a/tools/virtio/virtio-trace/Makefile b/tools/virtio/virtio-trace/Makefile
new file mode 100644
index 000000000000..ef3adfce594c
--- /dev/null
+++ b/tools/virtio/virtio-trace/Makefile
@@ -0,0 +1,14 @@
1CC = gcc
2CFLAGS = -O2 -Wall
3LFLAG = -lpthread
4
5all: trace-agent
6
7.c.o:
8 $(CC) $(CFLAGS) $(LFLAG) -c $^ -o $@
9
10trace-agent: trace-agent.o trace-agent-ctl.o trace-agent-rw.o
11 $(CC) $(CFLAGS) $(LFLAG) -o $@ $^
12
13clean:
14 rm -f *.o trace-agent
diff --git a/tools/virtio/virtio-trace/README b/tools/virtio/virtio-trace/README
new file mode 100644
index 000000000000..b64845b823ab
--- /dev/null
+++ b/tools/virtio/virtio-trace/README
@@ -0,0 +1,118 @@
1Trace Agent for virtio-trace
2============================
3
4Trace agent is a user tool for sending trace data of a guest to a Host in low
5overhead. Trace agent has the following functions:
6 - splice a page of ring-buffer to read_pipe without memory copying
7 - splice the page from write_pipe to virtio-console without memory copying
8 - write trace data to stdout by using -o option
9 - controlled by start/stop orders from a Host
10
11The trace agent operates as follows:
12 1) Initialize all structures.
13 2) Create a read/write thread per CPU. Each thread is bound to a CPU.
14 The read/write threads hold it.
15 3) A controller thread does poll() for a start order of a host.
16 4) After the controller of the trace agent receives a start order from a host,
17 the controller wake read/write threads.
18 5) The read/write threads start to read trace data from ring-buffers and
19 write the data to virtio-serial.
20 6) If the controller receives a stop order from a host, the read/write threads
21 stop to read trace data.
22
23
24Files
25=====
26
27README: this file
28Makefile: Makefile of trace agent for virtio-trace
29trace-agent.c: includes main function, sets up for operating trace agent
30trace-agent.h: includes all structures and some macros
31trace-agent-ctl.c: includes controller function for read/write threads
32trace-agent-rw.c: includes read/write threads function
33
34
35Setup
36=====
37
38To use this trace agent for virtio-trace, we need to prepare some virtio-serial
39I/Fs.
40
411) Make FIFO in a host
42 virtio-trace uses virtio-serial pipe as trace data paths as to the number
43of CPUs and a control path, so FIFO (named pipe) should be created as follows:
44 # mkdir /tmp/virtio-trace/
45 # mkfifo /tmp/virtio-trace/trace-path-cpu{0,1,2,...,X}.{in,out}
46 # mkfifo /tmp/virtio-trace/agent-ctl-path.{in,out}
47
48For example, if a guest use three CPUs, the names are
49 trace-path-cpu{0,1,2}.{in.out}
50and
51 agent-ctl-path.{in,out}.
52
532) Set up of virtio-serial pipe in a host
54 Add qemu option to use virtio-serial pipe.
55
56 ##virtio-serial device##
57 -device virtio-serial-pci,id=virtio-serial0\
58 ##control path##
59 -chardev pipe,id=charchannel0,path=/tmp/virtio-trace/agent-ctl-path\
60 -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,\
61 id=channel0,name=agent-ctl-path\
62 ##data path##
63 -chardev pipe,id=charchannel1,path=/tmp/virtio-trace/trace-path-cpu0\
64 -device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel0,\
65 id=channel1,name=trace-path-cpu0\
66 ...
67
68If you manage guests with libvirt, add the following tags to domain XML files.
69Then, libvirt passes the same command option to qemu.
70
71 <channel type='pipe'>
72 <source path='/tmp/virtio-trace/agent-ctl-path'/>
73 <target type='virtio' name='agent-ctl-path'/>
74 <address type='virtio-serial' controller='0' bus='0' port='0'/>
75 </channel>
76 <channel type='pipe'>
77 <source path='/tmp/virtio-trace/trace-path-cpu0'/>
78 <target type='virtio' name='trace-path-cpu0'/>
79 <address type='virtio-serial' controller='0' bus='0' port='1'/>
80 </channel>
81 ...
82Here, chardev names are restricted to trace-path-cpuX and agent-ctl-path. For
83example, if a guest use three CPUs, chardev names should be trace-path-cpu0,
84trace-path-cpu1, trace-path-cpu2, and agent-ctl-path.
85
863) Boot the guest
87 You can find some chardev in /dev/virtio-ports/ in the guest.
88
89
90Run
91===
92
930) Build trace agent in a guest
94 $ make
95
961) Enable ftrace in the guest
97 <Example>
98 # echo 1 > /sys/kernel/debug/tracing/events/sched/enable
99
1002) Run trace agent in the guest
101 This agent must be operated as root.
102 # ./trace-agent
103read/write threads in the agent wait for start order from host. If you add -o
104option, trace data are output via stdout in the guest.
105
1063) Open FIFO in a host
107 # cat /tmp/virtio-trace/trace-path-cpu0.out
108If a host does not open these, trace data get stuck in buffers of virtio. Then,
109the guest will stop by specification of chardev in QEMU. This blocking mode may
110be solved in the future.
111
1124) Start to read trace data by ordering from a host
113 A host injects read start order to the guest via virtio-serial.
114 # echo 1 > /tmp/virtio-trace/agent-ctl-path.in
115
1165) Stop to read trace data by ordering from a host
117 A host injects read stop order to the guest via virtio-serial.
118 # echo 0 > /tmp/virtio-trace/agent-ctl-path.in
diff --git a/tools/virtio/virtio-trace/trace-agent-ctl.c b/tools/virtio/virtio-trace/trace-agent-ctl.c
new file mode 100644
index 000000000000..a2d0403c4f94
--- /dev/null
+++ b/tools/virtio/virtio-trace/trace-agent-ctl.c
@@ -0,0 +1,137 @@
1/*
2 * Controller of read/write threads for virtio-trace
3 *
4 * Copyright (C) 2012 Hitachi, Ltd.
5 * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
6 * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
7 *
8 * Licensed under GPL version 2 only.
9 *
10 */
11
12#define _GNU_SOURCE
13#include <fcntl.h>
14#include <poll.h>
15#include <signal.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <unistd.h>
19#include "trace-agent.h"
20
21#define HOST_MSG_SIZE 256
22#define EVENT_WAIT_MSEC 100
23
24static volatile sig_atomic_t global_signal_val;
25bool global_sig_receive; /* default false */
26bool global_run_operation; /* default false*/
27
28/* Handle SIGTERM/SIGINT/SIGQUIT to exit */
29static void signal_handler(int sig)
30{
31 global_signal_val = sig;
32}
33
34int rw_ctl_init(const char *ctl_path)
35{
36 int ctl_fd;
37
38 ctl_fd = open(ctl_path, O_RDONLY);
39 if (ctl_fd == -1) {
40 pr_err("Cannot open ctl_fd\n");
41 goto error;
42 }
43
44 return ctl_fd;
45
46error:
47 exit(EXIT_FAILURE);
48}
49
50static int wait_order(int ctl_fd)
51{
52 struct pollfd poll_fd;
53 int ret = 0;
54
55 while (!global_sig_receive) {
56 poll_fd.fd = ctl_fd;
57 poll_fd.events = POLLIN;
58
59 ret = poll(&poll_fd, 1, EVENT_WAIT_MSEC);
60
61 if (global_signal_val) {
62 global_sig_receive = true;
63 pr_info("Receive interrupt %d\n", global_signal_val);
64
65 /* Wakes rw-threads when they are sleeping */
66 if (!global_run_operation)
67 pthread_cond_broadcast(&cond_wakeup);
68
69 ret = -1;
70 break;
71 }
72
73 if (ret < 0) {
74 pr_err("Polling error\n");
75 goto error;
76 }
77
78 if (ret)
79 break;
80 };
81
82 return ret;
83
84error:
85 exit(EXIT_FAILURE);
86}
87
88/*
89 * contol read/write threads by handling global_run_operation
90 */
91void *rw_ctl_loop(int ctl_fd)
92{
93 ssize_t rlen;
94 char buf[HOST_MSG_SIZE];
95 int ret;
96
97 /* Setup signal handlers */
98 signal(SIGTERM, signal_handler);
99 signal(SIGINT, signal_handler);
100 signal(SIGQUIT, signal_handler);
101
102 while (!global_sig_receive) {
103
104 ret = wait_order(ctl_fd);
105 if (ret < 0)
106 break;
107
108 rlen = read(ctl_fd, buf, sizeof(buf));
109 if (rlen < 0) {
110 pr_err("read data error in ctl thread\n");
111 goto error;
112 }
113
114 if (rlen == 2 && buf[0] == '1') {
115 /*
116 * If host writes '1' to a control path,
117 * this controller wakes all read/write threads.
118 */
119 global_run_operation = true;
120 pthread_cond_broadcast(&cond_wakeup);
121 pr_debug("Wake up all read/write threads\n");
122 } else if (rlen == 2 && buf[0] == '0') {
123 /*
124 * If host writes '0' to a control path, read/write
125 * threads will wait for notification from Host.
126 */
127 global_run_operation = false;
128 pr_debug("Stop all read/write threads\n");
129 } else
130 pr_info("Invalid host notification: %s\n", buf);
131 }
132
133 return NULL;
134
135error:
136 exit(EXIT_FAILURE);
137}
diff --git a/tools/virtio/virtio-trace/trace-agent-rw.c b/tools/virtio/virtio-trace/trace-agent-rw.c
new file mode 100644
index 000000000000..3aace5ea4842
--- /dev/null
+++ b/tools/virtio/virtio-trace/trace-agent-rw.c
@@ -0,0 +1,192 @@
1/*
2 * Read/write thread of a guest agent for virtio-trace
3 *
4 * Copyright (C) 2012 Hitachi, Ltd.
5 * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
6 * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
7 *
8 * Licensed under GPL version 2 only.
9 *
10 */
11
12#define _GNU_SOURCE
13#include <fcntl.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include <sys/syscall.h>
18#include "trace-agent.h"
19
20#define READ_WAIT_USEC 100000
21
22void *rw_thread_info_new(void)
23{
24 struct rw_thread_info *rw_ti;
25
26 rw_ti = zalloc(sizeof(struct rw_thread_info));
27 if (rw_ti == NULL) {
28 pr_err("rw_thread_info zalloc error\n");
29 exit(EXIT_FAILURE);
30 }
31
32 rw_ti->cpu_num = -1;
33 rw_ti->in_fd = -1;
34 rw_ti->out_fd = -1;
35 rw_ti->read_pipe = -1;
36 rw_ti->write_pipe = -1;
37 rw_ti->pipe_size = PIPE_INIT;
38
39 return rw_ti;
40}
41
42void *rw_thread_init(int cpu, const char *in_path, const char *out_path,
43 bool stdout_flag, unsigned long pipe_size,
44 struct rw_thread_info *rw_ti)
45{
46 int data_pipe[2];
47
48 rw_ti->cpu_num = cpu;
49
50 /* set read(input) fd */
51 rw_ti->in_fd = open(in_path, O_RDONLY);
52 if (rw_ti->in_fd == -1) {
53 pr_err("Could not open in_fd (CPU:%d)\n", cpu);
54 goto error;
55 }
56
57 /* set write(output) fd */
58 if (!stdout_flag) {
59 /* virtio-serial output mode */
60 rw_ti->out_fd = open(out_path, O_WRONLY);
61 if (rw_ti->out_fd == -1) {
62 pr_err("Could not open out_fd (CPU:%d)\n", cpu);
63 goto error;
64 }
65 } else
66 /* stdout mode */
67 rw_ti->out_fd = STDOUT_FILENO;
68
69 if (pipe2(data_pipe, O_NONBLOCK) < 0) {
70 pr_err("Could not create pipe in rw-thread(%d)\n", cpu);
71 goto error;
72 }
73
74 /*
75 * Size of pipe is 64kB in default based on fs/pipe.c.
76 * To read/write trace data speedy, pipe size is changed.
77 */
78 if (fcntl(*data_pipe, F_SETPIPE_SZ, pipe_size) < 0) {
79 pr_err("Could not change pipe size in rw-thread(%d)\n", cpu);
80 goto error;
81 }
82
83 rw_ti->read_pipe = data_pipe[1];
84 rw_ti->write_pipe = data_pipe[0];
85 rw_ti->pipe_size = pipe_size;
86
87 return NULL;
88
89error:
90 exit(EXIT_FAILURE);
91}
92
93/* Bind a thread to a cpu */
94static void bind_cpu(int cpu_num)
95{
96 cpu_set_t mask;
97
98 CPU_ZERO(&mask);
99 CPU_SET(cpu_num, &mask);
100
101 /* bind my thread to cpu_num by assigning zero to the first argument */
102 if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
103 pr_err("Could not set CPU#%d affinity\n", (int)cpu_num);
104}
105
106static void *rw_thread_main(void *thread_info)
107{
108 ssize_t rlen, wlen;
109 ssize_t ret;
110 struct rw_thread_info *ts = (struct rw_thread_info *)thread_info;
111
112 bind_cpu(ts->cpu_num);
113
114 while (1) {
115 /* Wait for a read order of trace data by Host OS */
116 if (!global_run_operation) {
117 pthread_mutex_lock(&mutex_notify);
118 pthread_cond_wait(&cond_wakeup, &mutex_notify);
119 pthread_mutex_unlock(&mutex_notify);
120 }
121
122 if (global_sig_receive)
123 break;
124
125 /*
126 * Each thread read trace_pipe_raw of each cpu bounding the
127 * thread, so contention of multi-threads does not occur.
128 */
129 rlen = splice(ts->in_fd, NULL, ts->read_pipe, NULL,
130 ts->pipe_size, SPLICE_F_MOVE | SPLICE_F_MORE);
131
132 if (rlen < 0) {
133 pr_err("Splice_read in rw-thread(%d)\n", ts->cpu_num);
134 goto error;
135 } else if (rlen == 0) {
136 /*
137 * If trace data do not exist or are unreadable not
138 * for exceeding the page size, splice_read returns
139 * NULL. Then, this waits for being filled the data in a
140 * ring-buffer.
141 */
142 usleep(READ_WAIT_USEC);
143 pr_debug("Read retry(cpu:%d)\n", ts->cpu_num);
144 continue;
145 }
146
147 wlen = 0;
148
149 do {
150 ret = splice(ts->write_pipe, NULL, ts->out_fd, NULL,
151 rlen - wlen,
152 SPLICE_F_MOVE | SPLICE_F_MORE);
153
154 if (ret < 0) {
155 pr_err("Splice_write in rw-thread(%d)\n",
156 ts->cpu_num);
157 goto error;
158 } else if (ret == 0)
159 /*
160 * When host reader is not in time for reading
161 * trace data, guest will be stopped. This is
162 * because char dev in QEMU is not supported
163 * non-blocking mode. Then, writer might be
164 * sleep in that case.
165 * This sleep will be removed by supporting
166 * non-blocking mode.
167 */
168 sleep(1);
169 wlen += ret;
170 } while (wlen < rlen);
171 }
172
173 return NULL;
174
175error:
176 exit(EXIT_FAILURE);
177}
178
179
180pthread_t rw_thread_run(struct rw_thread_info *rw_ti)
181{
182 int ret;
183 pthread_t rw_thread_per_cpu;
184
185 ret = pthread_create(&rw_thread_per_cpu, NULL, rw_thread_main, rw_ti);
186 if (ret != 0) {
187 pr_err("Could not create a rw thread(%d)\n", rw_ti->cpu_num);
188 exit(EXIT_FAILURE);
189 }
190
191 return rw_thread_per_cpu;
192}
diff --git a/tools/virtio/virtio-trace/trace-agent.c b/tools/virtio/virtio-trace/trace-agent.c
new file mode 100644
index 000000000000..0a0a7dd4eff7
--- /dev/null
+++ b/tools/virtio/virtio-trace/trace-agent.c
@@ -0,0 +1,270 @@
1/*
2 * Guest agent for virtio-trace
3 *
4 * Copyright (C) 2012 Hitachi, Ltd.
5 * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
6 * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
7 *
8 * Licensed under GPL version 2 only.
9 *
10 */
11
12#define _GNU_SOURCE
13#include <limits.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <unistd.h>
17#include "trace-agent.h"
18
19#define PAGE_SIZE (sysconf(_SC_PAGE_SIZE))
20#define PIPE_DEF_BUFS 16
21#define PIPE_MIN_SIZE (PAGE_SIZE*PIPE_DEF_BUFS)
22#define PIPE_MAX_SIZE (1024*1024)
23#define READ_PATH_FMT \
24 "/sys/kernel/debug/tracing/per_cpu/cpu%d/trace_pipe_raw"
25#define WRITE_PATH_FMT "/dev/virtio-ports/trace-path-cpu%d"
26#define CTL_PATH "/dev/virtio-ports/agent-ctl-path"
27
28pthread_mutex_t mutex_notify = PTHREAD_MUTEX_INITIALIZER;
29pthread_cond_t cond_wakeup = PTHREAD_COND_INITIALIZER;
30
31static int get_total_cpus(void)
32{
33 int nr_cpus = (int)sysconf(_SC_NPROCESSORS_CONF);
34
35 if (nr_cpus <= 0) {
36 pr_err("Could not read cpus\n");
37 goto error;
38 } else if (nr_cpus > MAX_CPUS) {
39 pr_err("Exceed max cpus(%d)\n", (int)MAX_CPUS);
40 goto error;
41 }
42
43 return nr_cpus;
44
45error:
46 exit(EXIT_FAILURE);
47}
48
49static void *agent_info_new(void)
50{
51 struct agent_info *s;
52 int i;
53
54 s = zalloc(sizeof(struct agent_info));
55 if (s == NULL) {
56 pr_err("agent_info zalloc error\n");
57 exit(EXIT_FAILURE);
58 }
59
60 s->pipe_size = PIPE_INIT;
61 s->use_stdout = false;
62 s->cpus = get_total_cpus();
63 s->ctl_fd = -1;
64
65 /* read/write threads init */
66 for (i = 0; i < s->cpus; i++)
67 s->rw_ti[i] = rw_thread_info_new();
68
69 return s;
70}
71
72static unsigned long parse_size(const char *arg)
73{
74 unsigned long value, round;
75 char *ptr;
76
77 value = strtoul(arg, &ptr, 10);
78 switch (*ptr) {
79 case 'K': case 'k':
80 value <<= 10;
81 break;
82 case 'M': case 'm':
83 value <<= 20;
84 break;
85 default:
86 break;
87 }
88
89 if (value > PIPE_MAX_SIZE) {
90 pr_err("Pipe size must be less than 1MB\n");
91 goto error;
92 } else if (value < PIPE_MIN_SIZE) {
93 pr_err("Pipe size must be over 64KB\n");
94 goto error;
95 }
96
97 /* Align buffer size with page unit */
98 round = value & (PAGE_SIZE - 1);
99 value = value - round;
100
101 return value;
102error:
103 return 0;
104}
105
106static void usage(char const *prg)
107{
108 pr_err("usage: %s [-h] [-o] [-s <size of pipe>]\n", prg);
109}
110
111static const char *make_path(int cpu_num, bool this_is_write_path)
112{
113 int ret;
114 char *buf;
115
116 buf = zalloc(PATH_MAX);
117 if (buf == NULL) {
118 pr_err("Could not allocate buffer\n");
119 goto error;
120 }
121
122 if (this_is_write_path)
123 /* write(output) path */
124 ret = snprintf(buf, PATH_MAX, WRITE_PATH_FMT, cpu_num);
125 else
126 /* read(input) path */
127 ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, cpu_num);
128
129 if (ret <= 0) {
130 pr_err("Failed to generate %s path(CPU#%d):%d\n",
131 this_is_write_path ? "read" : "write", cpu_num, ret);
132 goto error;
133 }
134
135 return buf;
136
137error:
138 free(buf);
139 return NULL;
140}
141
142static const char *make_input_path(int cpu_num)
143{
144 return make_path(cpu_num, false);
145}
146
147static const char *make_output_path(int cpu_num)
148{
149 return make_path(cpu_num, true);
150}
151
152static void *agent_info_init(struct agent_info *s)
153{
154 int cpu;
155 const char *in_path = NULL;
156 const char *out_path = NULL;
157
158 /* init read/write threads */
159 for (cpu = 0; cpu < s->cpus; cpu++) {
160 /* set read(input) path per read/write thread */
161 in_path = make_input_path(cpu);
162 if (in_path == NULL)
163 goto error;
164
165 /* set write(output) path per read/write thread*/
166 if (!s->use_stdout) {
167 out_path = make_output_path(cpu);
168 if (out_path == NULL)
169 goto error;
170 } else
171 /* stdout mode */
172 pr_debug("stdout mode\n");
173
174 rw_thread_init(cpu, in_path, out_path, s->use_stdout,
175 s->pipe_size, s->rw_ti[cpu]);
176 }
177
178 /* init controller of read/write threads */
179 s->ctl_fd = rw_ctl_init((const char *)CTL_PATH);
180
181 return NULL;
182
183error:
184 exit(EXIT_FAILURE);
185}
186
187static void *parse_args(int argc, char *argv[], struct agent_info *s)
188{
189 int cmd;
190 unsigned long size;
191
192 while ((cmd = getopt(argc, argv, "hos:")) != -1) {
193 switch (cmd) {
194 /* stdout mode */
195 case 'o':
196 s->use_stdout = true;
197 break;
198 /* size of pipe */
199 case 's':
200 size = parse_size(optarg);
201 if (size == 0)
202 goto error;
203 s->pipe_size = size;
204 break;
205 case 'h':
206 default:
207 usage(argv[0]);
208 goto error;
209 }
210 }
211
212 agent_info_init(s);
213
214 return NULL;
215
216error:
217 exit(EXIT_FAILURE);
218}
219
220static void agent_main_loop(struct agent_info *s)
221{
222 int cpu;
223 pthread_t rw_thread_per_cpu[MAX_CPUS];
224
225 /* Start all read/write threads */
226 for (cpu = 0; cpu < s->cpus; cpu++)
227 rw_thread_per_cpu[cpu] = rw_thread_run(s->rw_ti[cpu]);
228
229 rw_ctl_loop(s->ctl_fd);
230
231 /* Finish all read/write threads */
232 for (cpu = 0; cpu < s->cpus; cpu++) {
233 int ret;
234
235 ret = pthread_join(rw_thread_per_cpu[cpu], NULL);
236 if (ret != 0) {
237 pr_err("pthread_join() error:%d (cpu %d)\n", ret, cpu);
238 exit(EXIT_FAILURE);
239 }
240 }
241}
242
243static void agent_info_free(struct agent_info *s)
244{
245 int i;
246
247 close(s->ctl_fd);
248 for (i = 0; i < s->cpus; i++) {
249 close(s->rw_ti[i]->in_fd);
250 close(s->rw_ti[i]->out_fd);
251 close(s->rw_ti[i]->read_pipe);
252 close(s->rw_ti[i]->write_pipe);
253 free(s->rw_ti[i]);
254 }
255 free(s);
256}
257
258int main(int argc, char *argv[])
259{
260 struct agent_info *s = NULL;
261
262 s = agent_info_new();
263 parse_args(argc, argv, s);
264
265 agent_main_loop(s);
266
267 agent_info_free(s);
268
269 return 0;
270}
diff --git a/tools/virtio/virtio-trace/trace-agent.h b/tools/virtio/virtio-trace/trace-agent.h
new file mode 100644
index 000000000000..8de79bfeaa73
--- /dev/null
+++ b/tools/virtio/virtio-trace/trace-agent.h
@@ -0,0 +1,75 @@
1#ifndef __TRACE_AGENT_H__
2#define __TRACE_AGENT_H__
3#include <pthread.h>
4#include <stdbool.h>
5
6#define MAX_CPUS 256
7#define PIPE_INIT (1024*1024)
8
9/*
10 * agent_info - structure managing total information of guest agent
11 * @pipe_size: size of pipe (default 1MB)
12 * @use_stdout: set to true when o option is added (default false)
13 * @cpus: total number of CPUs
14 * @ctl_fd: fd of control path, /dev/virtio-ports/agent-ctl-path
15 * @rw_ti: structure managing information of read/write threads
16 */
17struct agent_info {
18 unsigned long pipe_size;
19 bool use_stdout;
20 int cpus;
21 int ctl_fd;
22 struct rw_thread_info *rw_ti[MAX_CPUS];
23};
24
25/*
26 * rw_thread_info - structure managing a read/write thread a cpu
27 * @cpu_num: cpu number operating this read/write thread
28 * @in_fd: fd of reading trace data path in cpu_num
29 * @out_fd: fd of writing trace data path in cpu_num
30 * @read_pipe: fd of read pipe
31 * @write_pipe: fd of write pipe
32 * @pipe_size: size of pipe (default 1MB)
33 */
34struct rw_thread_info {
35 int cpu_num;
36 int in_fd;
37 int out_fd;
38 int read_pipe;
39 int write_pipe;
40 unsigned long pipe_size;
41};
42
43/* use for stopping rw threads */
44extern bool global_sig_receive;
45
46/* use for notification */
47extern bool global_run_operation;
48extern pthread_mutex_t mutex_notify;
49extern pthread_cond_t cond_wakeup;
50
51/* for controller of read/write threads */
52extern int rw_ctl_init(const char *ctl_path);
53extern void *rw_ctl_loop(int ctl_fd);
54
55/* for trace read/write thread */
56extern void *rw_thread_info_new(void);
57extern void *rw_thread_init(int cpu, const char *in_path, const char *out_path,
58 bool stdout_flag, unsigned long pipe_size,
59 struct rw_thread_info *rw_ti);
60extern pthread_t rw_thread_run(struct rw_thread_info *rw_ti);
61
62static inline void *zalloc(size_t size)
63{
64 return calloc(1, size);
65}
66
67#define pr_err(format, ...) fprintf(stderr, format, ## __VA_ARGS__)
68#define pr_info(format, ...) fprintf(stdout, format, ## __VA_ARGS__)
69#ifdef DEBUG
70#define pr_debug(format, ...) fprintf(stderr, format, ## __VA_ARGS__)
71#else
72#define pr_debug(format, ...) do {} while (0)
73#endif
74
75#endif /*__TRACE_AGENT_H__*/