1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Read/write thread of a guest agent for virtio-trace |
4 | * |
5 | * Copyright (C) 2012 Hitachi, Ltd. |
6 | * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com> |
7 | * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> |
8 | */ |
9 | |
10 | #define _GNU_SOURCE |
11 | #include <fcntl.h> |
12 | #include <stdio.h> |
13 | #include <stdlib.h> |
14 | #include <unistd.h> |
15 | #include <sys/syscall.h> |
16 | #include "trace-agent.h" |
17 | |
18 | #define READ_WAIT_USEC 100000 |
19 | |
20 | void *rw_thread_info_new(void) |
21 | { |
22 | struct rw_thread_info *rw_ti; |
23 | |
24 | rw_ti = zalloc(sizeof(struct rw_thread_info)); |
25 | if (rw_ti == NULL) { |
26 | pr_err("rw_thread_info zalloc error\n" ); |
27 | exit(EXIT_FAILURE); |
28 | } |
29 | |
30 | rw_ti->cpu_num = -1; |
31 | rw_ti->in_fd = -1; |
32 | rw_ti->out_fd = -1; |
33 | rw_ti->read_pipe = -1; |
34 | rw_ti->write_pipe = -1; |
35 | rw_ti->pipe_size = PIPE_INIT; |
36 | |
37 | return rw_ti; |
38 | } |
39 | |
40 | void *rw_thread_init(int cpu, const char *in_path, const char *out_path, |
41 | bool stdout_flag, unsigned long pipe_size, |
42 | struct rw_thread_info *rw_ti) |
43 | { |
44 | int data_pipe[2]; |
45 | |
46 | rw_ti->cpu_num = cpu; |
47 | |
48 | /* set read(input) fd */ |
49 | rw_ti->in_fd = open(in_path, O_RDONLY); |
50 | if (rw_ti->in_fd == -1) { |
51 | pr_err("Could not open in_fd (CPU:%d)\n" , cpu); |
52 | goto error; |
53 | } |
54 | |
55 | /* set write(output) fd */ |
56 | if (!stdout_flag) { |
57 | /* virtio-serial output mode */ |
58 | rw_ti->out_fd = open(out_path, O_WRONLY); |
59 | if (rw_ti->out_fd == -1) { |
60 | pr_err("Could not open out_fd (CPU:%d)\n" , cpu); |
61 | goto error; |
62 | } |
63 | } else |
64 | /* stdout mode */ |
65 | rw_ti->out_fd = STDOUT_FILENO; |
66 | |
67 | if (pipe2(data_pipe, O_NONBLOCK) < 0) { |
68 | pr_err("Could not create pipe in rw-thread(%d)\n" , cpu); |
69 | goto error; |
70 | } |
71 | |
72 | /* |
73 | * Size of pipe is 64kB in default based on fs/pipe.c. |
74 | * To read/write trace data speedy, pipe size is changed. |
75 | */ |
76 | if (fcntl(*data_pipe, F_SETPIPE_SZ, pipe_size) < 0) { |
77 | pr_err("Could not change pipe size in rw-thread(%d)\n" , cpu); |
78 | goto error; |
79 | } |
80 | |
81 | rw_ti->read_pipe = data_pipe[1]; |
82 | rw_ti->write_pipe = data_pipe[0]; |
83 | rw_ti->pipe_size = pipe_size; |
84 | |
85 | return NULL; |
86 | |
87 | error: |
88 | exit(EXIT_FAILURE); |
89 | } |
90 | |
91 | /* Bind a thread to a cpu */ |
92 | static void bind_cpu(int cpu_num) |
93 | { |
94 | cpu_set_t mask; |
95 | |
96 | CPU_ZERO(&mask); |
97 | CPU_SET(cpu_num, &mask); |
98 | |
99 | /* bind my thread to cpu_num by assigning zero to the first argument */ |
100 | if (sched_setaffinity(0, sizeof(mask), &mask) == -1) |
101 | pr_err("Could not set CPU#%d affinity\n" , (int)cpu_num); |
102 | } |
103 | |
104 | static void *rw_thread_main(void *thread_info) |
105 | { |
106 | ssize_t rlen, wlen; |
107 | ssize_t ret; |
108 | struct rw_thread_info *ts = (struct rw_thread_info *)thread_info; |
109 | |
110 | bind_cpu(cpu_num: ts->cpu_num); |
111 | |
112 | while (1) { |
113 | /* Wait for a read order of trace data by Host OS */ |
114 | if (!global_run_operation) { |
115 | pthread_mutex_lock(&mutex_notify); |
116 | pthread_cond_wait(&cond_wakeup, &mutex_notify); |
117 | pthread_mutex_unlock(&mutex_notify); |
118 | } |
119 | |
120 | if (global_sig_receive) |
121 | break; |
122 | |
123 | /* |
124 | * Each thread read trace_pipe_raw of each cpu bounding the |
125 | * thread, so contention of multi-threads does not occur. |
126 | */ |
127 | rlen = splice(ts->in_fd, NULL, ts->read_pipe, NULL, |
128 | ts->pipe_size, SPLICE_F_MOVE | SPLICE_F_MORE); |
129 | |
130 | if (rlen < 0) { |
131 | pr_err("Splice_read in rw-thread(%d)\n" , ts->cpu_num); |
132 | goto error; |
133 | } else if (rlen == 0) { |
134 | /* |
135 | * If trace data do not exist or are unreadable not |
136 | * for exceeding the page size, splice_read returns |
137 | * NULL. Then, this waits for being filled the data in a |
138 | * ring-buffer. |
139 | */ |
140 | usleep(READ_WAIT_USEC); |
141 | pr_debug("Read retry(cpu:%d)\n" , ts->cpu_num); |
142 | continue; |
143 | } |
144 | |
145 | wlen = 0; |
146 | |
147 | do { |
148 | ret = splice(ts->write_pipe, NULL, ts->out_fd, NULL, |
149 | rlen - wlen, |
150 | SPLICE_F_MOVE | SPLICE_F_MORE); |
151 | |
152 | if (ret < 0) { |
153 | pr_err("Splice_write in rw-thread(%d)\n" , |
154 | ts->cpu_num); |
155 | goto error; |
156 | } else if (ret == 0) |
157 | /* |
158 | * When host reader is not in time for reading |
159 | * trace data, guest will be stopped. This is |
160 | * because char dev in QEMU is not supported |
161 | * non-blocking mode. Then, writer might be |
162 | * sleep in that case. |
163 | * This sleep will be removed by supporting |
164 | * non-blocking mode. |
165 | */ |
166 | sleep(1); |
167 | wlen += ret; |
168 | } while (wlen < rlen); |
169 | } |
170 | |
171 | return NULL; |
172 | |
173 | error: |
174 | exit(EXIT_FAILURE); |
175 | } |
176 | |
177 | |
178 | pthread_t rw_thread_run(struct rw_thread_info *rw_ti) |
179 | { |
180 | int ret; |
181 | pthread_t rw_thread_per_cpu; |
182 | |
183 | ret = pthread_create(&rw_thread_per_cpu, NULL, rw_thread_main, rw_ti); |
184 | if (ret != 0) { |
185 | pr_err("Could not create a rw thread(%d)\n" , rw_ti->cpu_num); |
186 | exit(EXIT_FAILURE); |
187 | } |
188 | |
189 | return rw_thread_per_cpu; |
190 | } |
191 | |