xref: /linux/tools/virtio/virtio-trace/trace-agent-rw.c (revision bd628c1bed7902ec1f24ba0fe70758949146abbe)
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 
22 void *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 
42 void *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 
89 error:
90 	exit(EXIT_FAILURE);
91 }
92 
93 /* Bind a thread to a cpu */
94 static 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 
106 static 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 
175 error:
176 	exit(EXIT_FAILURE);
177 }
178 
179 
180 pthread_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 }
193