162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Read/write thread of a guest agent for virtio-trace
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2012 Hitachi, Ltd.
662306a36Sopenharmony_ci * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
762306a36Sopenharmony_ci *            Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define _GNU_SOURCE
1162306a36Sopenharmony_ci#include <fcntl.h>
1262306a36Sopenharmony_ci#include <stdio.h>
1362306a36Sopenharmony_ci#include <stdlib.h>
1462306a36Sopenharmony_ci#include <unistd.h>
1562306a36Sopenharmony_ci#include <sys/syscall.h>
1662306a36Sopenharmony_ci#include "trace-agent.h"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#define READ_WAIT_USEC	100000
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_civoid *rw_thread_info_new(void)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	struct rw_thread_info *rw_ti;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	rw_ti = zalloc(sizeof(struct rw_thread_info));
2562306a36Sopenharmony_ci	if (rw_ti == NULL) {
2662306a36Sopenharmony_ci		pr_err("rw_thread_info zalloc error\n");
2762306a36Sopenharmony_ci		exit(EXIT_FAILURE);
2862306a36Sopenharmony_ci	}
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	rw_ti->cpu_num = -1;
3162306a36Sopenharmony_ci	rw_ti->in_fd = -1;
3262306a36Sopenharmony_ci	rw_ti->out_fd = -1;
3362306a36Sopenharmony_ci	rw_ti->read_pipe = -1;
3462306a36Sopenharmony_ci	rw_ti->write_pipe = -1;
3562306a36Sopenharmony_ci	rw_ti->pipe_size = PIPE_INIT;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return rw_ti;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_civoid *rw_thread_init(int cpu, const char *in_path, const char *out_path,
4162306a36Sopenharmony_ci				bool stdout_flag, unsigned long pipe_size,
4262306a36Sopenharmony_ci				struct rw_thread_info *rw_ti)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	int data_pipe[2];
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	rw_ti->cpu_num = cpu;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	/* set read(input) fd */
4962306a36Sopenharmony_ci	rw_ti->in_fd = open(in_path, O_RDONLY);
5062306a36Sopenharmony_ci	if (rw_ti->in_fd == -1) {
5162306a36Sopenharmony_ci		pr_err("Could not open in_fd (CPU:%d)\n", cpu);
5262306a36Sopenharmony_ci		goto error;
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* set write(output) fd */
5662306a36Sopenharmony_ci	if (!stdout_flag) {
5762306a36Sopenharmony_ci		/* virtio-serial output mode */
5862306a36Sopenharmony_ci		rw_ti->out_fd = open(out_path, O_WRONLY);
5962306a36Sopenharmony_ci		if (rw_ti->out_fd == -1) {
6062306a36Sopenharmony_ci			pr_err("Could not open out_fd (CPU:%d)\n", cpu);
6162306a36Sopenharmony_ci			goto error;
6262306a36Sopenharmony_ci		}
6362306a36Sopenharmony_ci	} else
6462306a36Sopenharmony_ci		/* stdout mode */
6562306a36Sopenharmony_ci		rw_ti->out_fd = STDOUT_FILENO;
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (pipe2(data_pipe, O_NONBLOCK) < 0) {
6862306a36Sopenharmony_ci		pr_err("Could not create pipe in rw-thread(%d)\n", cpu);
6962306a36Sopenharmony_ci		goto error;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/*
7362306a36Sopenharmony_ci	 * Size of pipe is 64kB in default based on fs/pipe.c.
7462306a36Sopenharmony_ci	 * To read/write trace data speedy, pipe size is changed.
7562306a36Sopenharmony_ci	 */
7662306a36Sopenharmony_ci	if (fcntl(*data_pipe, F_SETPIPE_SZ, pipe_size) < 0) {
7762306a36Sopenharmony_ci		pr_err("Could not change pipe size in rw-thread(%d)\n", cpu);
7862306a36Sopenharmony_ci		goto error;
7962306a36Sopenharmony_ci	}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	rw_ti->read_pipe = data_pipe[1];
8262306a36Sopenharmony_ci	rw_ti->write_pipe = data_pipe[0];
8362306a36Sopenharmony_ci	rw_ti->pipe_size = pipe_size;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return NULL;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cierror:
8862306a36Sopenharmony_ci	exit(EXIT_FAILURE);
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci/* Bind a thread to a cpu */
9262306a36Sopenharmony_cistatic void bind_cpu(int cpu_num)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	cpu_set_t mask;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	CPU_ZERO(&mask);
9762306a36Sopenharmony_ci	CPU_SET(cpu_num, &mask);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	/* bind my thread to cpu_num by assigning zero to the first argument */
10062306a36Sopenharmony_ci	if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
10162306a36Sopenharmony_ci		pr_err("Could not set CPU#%d affinity\n", (int)cpu_num);
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_cistatic void *rw_thread_main(void *thread_info)
10562306a36Sopenharmony_ci{
10662306a36Sopenharmony_ci	ssize_t rlen, wlen;
10762306a36Sopenharmony_ci	ssize_t ret;
10862306a36Sopenharmony_ci	struct rw_thread_info *ts = (struct rw_thread_info *)thread_info;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	bind_cpu(ts->cpu_num);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	while (1) {
11362306a36Sopenharmony_ci		/* Wait for a read order of trace data by Host OS */
11462306a36Sopenharmony_ci		if (!global_run_operation) {
11562306a36Sopenharmony_ci			pthread_mutex_lock(&mutex_notify);
11662306a36Sopenharmony_ci			pthread_cond_wait(&cond_wakeup, &mutex_notify);
11762306a36Sopenharmony_ci			pthread_mutex_unlock(&mutex_notify);
11862306a36Sopenharmony_ci		}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		if (global_sig_receive)
12162306a36Sopenharmony_ci			break;
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci		/*
12462306a36Sopenharmony_ci		 * Each thread read trace_pipe_raw of each cpu bounding the
12562306a36Sopenharmony_ci		 * thread, so contention of multi-threads does not occur.
12662306a36Sopenharmony_ci		 */
12762306a36Sopenharmony_ci		rlen = splice(ts->in_fd, NULL, ts->read_pipe, NULL,
12862306a36Sopenharmony_ci				ts->pipe_size, SPLICE_F_MOVE | SPLICE_F_MORE);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci		if (rlen < 0) {
13162306a36Sopenharmony_ci			pr_err("Splice_read in rw-thread(%d)\n", ts->cpu_num);
13262306a36Sopenharmony_ci			goto error;
13362306a36Sopenharmony_ci		} else if (rlen == 0) {
13462306a36Sopenharmony_ci			/*
13562306a36Sopenharmony_ci			 * If trace data do not exist or are unreadable not
13662306a36Sopenharmony_ci			 * for exceeding the page size, splice_read returns
13762306a36Sopenharmony_ci			 * NULL. Then, this waits for being filled the data in a
13862306a36Sopenharmony_ci			 * ring-buffer.
13962306a36Sopenharmony_ci			 */
14062306a36Sopenharmony_ci			usleep(READ_WAIT_USEC);
14162306a36Sopenharmony_ci			pr_debug("Read retry(cpu:%d)\n", ts->cpu_num);
14262306a36Sopenharmony_ci			continue;
14362306a36Sopenharmony_ci		}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_ci		wlen = 0;
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci		do {
14862306a36Sopenharmony_ci			ret = splice(ts->write_pipe, NULL, ts->out_fd, NULL,
14962306a36Sopenharmony_ci					rlen - wlen,
15062306a36Sopenharmony_ci					SPLICE_F_MOVE | SPLICE_F_MORE);
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci			if (ret < 0) {
15362306a36Sopenharmony_ci				pr_err("Splice_write in rw-thread(%d)\n",
15462306a36Sopenharmony_ci								ts->cpu_num);
15562306a36Sopenharmony_ci				goto error;
15662306a36Sopenharmony_ci			} else if (ret == 0)
15762306a36Sopenharmony_ci				/*
15862306a36Sopenharmony_ci				 * When host reader is not in time for reading
15962306a36Sopenharmony_ci				 * trace data, guest will be stopped. This is
16062306a36Sopenharmony_ci				 * because char dev in QEMU is not supported
16162306a36Sopenharmony_ci				 * non-blocking mode. Then, writer might be
16262306a36Sopenharmony_ci				 * sleep in that case.
16362306a36Sopenharmony_ci				 * This sleep will be removed by supporting
16462306a36Sopenharmony_ci				 * non-blocking mode.
16562306a36Sopenharmony_ci				 */
16662306a36Sopenharmony_ci				sleep(1);
16762306a36Sopenharmony_ci			wlen += ret;
16862306a36Sopenharmony_ci		} while (wlen < rlen);
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return NULL;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_cierror:
17462306a36Sopenharmony_ci	exit(EXIT_FAILURE);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_cipthread_t rw_thread_run(struct rw_thread_info *rw_ti)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	int ret;
18162306a36Sopenharmony_ci	pthread_t rw_thread_per_cpu;
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	ret = pthread_create(&rw_thread_per_cpu, NULL, rw_thread_main, rw_ti);
18462306a36Sopenharmony_ci	if (ret != 0) {
18562306a36Sopenharmony_ci		pr_err("Could not create a rw thread(%d)\n", rw_ti->cpu_num);
18662306a36Sopenharmony_ci		exit(EXIT_FAILURE);
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	return rw_thread_per_cpu;
19062306a36Sopenharmony_ci}
191