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