1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * 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 <limits.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <unistd.h> 15 #include "trace-agent.h" 16 17 #define PAGE_SIZE (sysconf(_SC_PAGE_SIZE)) 18 #define PIPE_DEF_BUFS 16 19 #define PIPE_MIN_SIZE (PAGE_SIZE*PIPE_DEF_BUFS) 20 #define PIPE_MAX_SIZE (1024*1024) 21 #define TRACEFS "/sys/kernel/tracing" 22 #define DEBUGFS "/sys/kernel/debug/tracing" 23 #define READ_PATH_FMT "%s/per_cpu/cpu%d/trace_pipe_raw" 24 #define WRITE_PATH_FMT "/dev/virtio-ports/trace-path-cpu%d" 25 #define CTL_PATH "/dev/virtio-ports/agent-ctl-path" 26 27 pthread_mutex_t mutex_notify = PTHREAD_MUTEX_INITIALIZER; 28 pthread_cond_t cond_wakeup = PTHREAD_COND_INITIALIZER; 29 30 static int get_total_cpus(void) 31 { 32 int nr_cpus = (int)sysconf(_SC_NPROCESSORS_CONF); 33 34 if (nr_cpus <= 0) { 35 pr_err("Could not read cpus\n"); 36 goto error; 37 } else if (nr_cpus > MAX_CPUS) { 38 pr_err("Exceed max cpus(%d)\n", (int)MAX_CPUS); 39 goto error; 40 } 41 42 return nr_cpus; 43 44 error: 45 exit(EXIT_FAILURE); 46 } 47 48 static void *agent_info_new(void) 49 { 50 struct agent_info *s; 51 int i; 52 53 s = zalloc(sizeof(struct agent_info)); 54 if (s == NULL) { 55 pr_err("agent_info zalloc error\n"); 56 exit(EXIT_FAILURE); 57 } 58 59 s->pipe_size = PIPE_INIT; 60 s->use_stdout = false; 61 s->cpus = get_total_cpus(); 62 s->ctl_fd = -1; 63 64 /* read/write threads init */ 65 for (i = 0; i < s->cpus; i++) 66 s->rw_ti[i] = rw_thread_info_new(); 67 68 return s; 69 } 70 71 static unsigned long parse_size(const char *arg) 72 { 73 unsigned long value, round; 74 char *ptr; 75 76 value = strtoul(arg, &ptr, 10); 77 switch (*ptr) { 78 case 'K': case 'k': 79 value <<= 10; 80 break; 81 case 'M': case 'm': 82 value <<= 20; 83 break; 84 default: 85 break; 86 } 87 88 if (value > PIPE_MAX_SIZE) { 89 pr_err("Pipe size must be less than 1MB\n"); 90 goto error; 91 } else if (value < PIPE_MIN_SIZE) { 92 pr_err("Pipe size must be over 64KB\n"); 93 goto error; 94 } 95 96 /* Align buffer size with page unit */ 97 round = value & (PAGE_SIZE - 1); 98 value = value - round; 99 100 return value; 101 error: 102 return 0; 103 } 104 105 static void usage(char const *prg) 106 { 107 pr_err("usage: %s [-h] [-o] [-s <size of pipe>]\n", prg); 108 } 109 110 static const char *make_path(int cpu_num, bool this_is_write_path) 111 { 112 int ret; 113 char *buf; 114 115 buf = zalloc(PATH_MAX); 116 if (buf == NULL) { 117 pr_err("Could not allocate buffer\n"); 118 goto error; 119 } 120 121 if (this_is_write_path) 122 /* write(output) path */ 123 ret = snprintf(buf, PATH_MAX, WRITE_PATH_FMT, cpu_num); 124 else { 125 /* read(input) path */ 126 ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, TRACEFS, cpu_num); 127 if (ret > 0 && access(buf, F_OK) != 0) 128 ret = snprintf(buf, PATH_MAX, READ_PATH_FMT, DEBUGFS, cpu_num); 129 } 130 131 if (ret <= 0) { 132 pr_err("Failed to generate %s path(CPU#%d):%d\n", 133 this_is_write_path ? "read" : "write", cpu_num, ret); 134 goto error; 135 } 136 137 return buf; 138 139 error: 140 free(buf); 141 return NULL; 142 } 143 144 static const char *make_input_path(int cpu_num) 145 { 146 return make_path(cpu_num, false); 147 } 148 149 static const char *make_output_path(int cpu_num) 150 { 151 return make_path(cpu_num, true); 152 } 153 154 static void *agent_info_init(struct agent_info *s) 155 { 156 int cpu; 157 const char *in_path = NULL; 158 const char *out_path = NULL; 159 160 /* init read/write threads */ 161 for (cpu = 0; cpu < s->cpus; cpu++) { 162 /* set read(input) path per read/write thread */ 163 in_path = make_input_path(cpu); 164 if (in_path == NULL) 165 goto error; 166 167 /* set write(output) path per read/write thread*/ 168 if (!s->use_stdout) { 169 out_path = make_output_path(cpu); 170 if (out_path == NULL) 171 goto error; 172 } else 173 /* stdout mode */ 174 pr_debug("stdout mode\n"); 175 176 rw_thread_init(cpu, in_path, out_path, s->use_stdout, 177 s->pipe_size, s->rw_ti[cpu]); 178 } 179 180 /* init controller of read/write threads */ 181 s->ctl_fd = rw_ctl_init((const char *)CTL_PATH); 182 183 return NULL; 184 185 error: 186 exit(EXIT_FAILURE); 187 } 188 189 static void *parse_args(int argc, char *argv[], struct agent_info *s) 190 { 191 int cmd; 192 unsigned long size; 193 194 while ((cmd = getopt(argc, argv, "hos:")) != -1) { 195 switch (cmd) { 196 /* stdout mode */ 197 case 'o': 198 s->use_stdout = true; 199 break; 200 /* size of pipe */ 201 case 's': 202 size = parse_size(optarg); 203 if (size == 0) 204 goto error; 205 s->pipe_size = size; 206 break; 207 case 'h': 208 default: 209 usage(argv[0]); 210 goto error; 211 } 212 } 213 214 agent_info_init(s); 215 216 return NULL; 217 218 error: 219 exit(EXIT_FAILURE); 220 } 221 222 static void agent_main_loop(struct agent_info *s) 223 { 224 int cpu; 225 pthread_t rw_thread_per_cpu[MAX_CPUS]; 226 227 /* Start all read/write threads */ 228 for (cpu = 0; cpu < s->cpus; cpu++) 229 rw_thread_per_cpu[cpu] = rw_thread_run(s->rw_ti[cpu]); 230 231 rw_ctl_loop(s->ctl_fd); 232 233 /* Finish all read/write threads */ 234 for (cpu = 0; cpu < s->cpus; cpu++) { 235 int ret; 236 237 ret = pthread_join(rw_thread_per_cpu[cpu], NULL); 238 if (ret != 0) { 239 pr_err("pthread_join() error:%d (cpu %d)\n", ret, cpu); 240 exit(EXIT_FAILURE); 241 } 242 } 243 } 244 245 static void agent_info_free(struct agent_info *s) 246 { 247 int i; 248 249 close(s->ctl_fd); 250 for (i = 0; i < s->cpus; i++) { 251 close(s->rw_ti[i]->in_fd); 252 close(s->rw_ti[i]->out_fd); 253 close(s->rw_ti[i]->read_pipe); 254 close(s->rw_ti[i]->write_pipe); 255 free(s->rw_ti[i]); 256 } 257 free(s); 258 } 259 260 int main(int argc, char *argv[]) 261 { 262 struct agent_info *s = NULL; 263 264 s = agent_info_new(); 265 parse_args(argc, argv, s); 266 267 agent_main_loop(s); 268 269 agent_info_free(s); 270 271 return 0; 272 } 273