linux/tools/virtio/virtio-trace/trace-agent-rw.c
<<
>>
Prefs
   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
  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        } 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
  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 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
 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