191 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Read/write thread of a guest agent for virtio-trace4 *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_SOURCE11#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 10000019 20void *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 40void *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 } else64 /* 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 87error:88 exit(EXIT_FAILURE);89}90 91/* Bind a thread to a cpu */92static 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 104static 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(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 the125 * 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 not136 * for exceeding the page size, splice_read returns137 * NULL. Then, this waits for being filled the data in a138 * 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 reading159 * trace data, guest will be stopped. This is160 * because char dev in QEMU is not supported161 * non-blocking mode. Then, writer might be162 * sleep in that case.163 * This sleep will be removed by supporting164 * non-blocking mode.165 */166 sleep(1);167 wlen += ret;168 } while (wlen < rlen);169 }170 171 return NULL;172 173error:174 exit(EXIT_FAILURE);175}176 177 178pthread_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