162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Controller of read/write threads 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 <poll.h>
1362306a36Sopenharmony_ci#include <signal.h>
1462306a36Sopenharmony_ci#include <stdio.h>
1562306a36Sopenharmony_ci#include <stdlib.h>
1662306a36Sopenharmony_ci#include <unistd.h>
1762306a36Sopenharmony_ci#include "trace-agent.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define HOST_MSG_SIZE		256
2062306a36Sopenharmony_ci#define EVENT_WAIT_MSEC		100
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistatic volatile sig_atomic_t global_signal_val;
2362306a36Sopenharmony_cibool global_sig_receive;	/* default false */
2462306a36Sopenharmony_cibool global_run_operation;	/* default false*/
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/* Handle SIGTERM/SIGINT/SIGQUIT to exit */
2762306a36Sopenharmony_cistatic void signal_handler(int sig)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	global_signal_val = sig;
3062306a36Sopenharmony_ci}
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ciint rw_ctl_init(const char *ctl_path)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	int ctl_fd;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	ctl_fd = open(ctl_path, O_RDONLY);
3762306a36Sopenharmony_ci	if (ctl_fd == -1) {
3862306a36Sopenharmony_ci		pr_err("Cannot open ctl_fd\n");
3962306a36Sopenharmony_ci		goto error;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return ctl_fd;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cierror:
4562306a36Sopenharmony_ci	exit(EXIT_FAILURE);
4662306a36Sopenharmony_ci}
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic int wait_order(int ctl_fd)
4962306a36Sopenharmony_ci{
5062306a36Sopenharmony_ci	struct pollfd poll_fd;
5162306a36Sopenharmony_ci	int ret = 0;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	while (!global_sig_receive) {
5462306a36Sopenharmony_ci		poll_fd.fd = ctl_fd;
5562306a36Sopenharmony_ci		poll_fd.events = POLLIN;
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci		ret = poll(&poll_fd, 1, EVENT_WAIT_MSEC);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci		if (global_signal_val) {
6062306a36Sopenharmony_ci			global_sig_receive = true;
6162306a36Sopenharmony_ci			pr_info("Receive interrupt %d\n", global_signal_val);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci			/* Wakes rw-threads when they are sleeping */
6462306a36Sopenharmony_ci			if (!global_run_operation)
6562306a36Sopenharmony_ci				pthread_cond_broadcast(&cond_wakeup);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci			ret = -1;
6862306a36Sopenharmony_ci			break;
6962306a36Sopenharmony_ci		}
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci		if (ret < 0) {
7262306a36Sopenharmony_ci			pr_err("Polling error\n");
7362306a36Sopenharmony_ci			goto error;
7462306a36Sopenharmony_ci		}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci		if (ret)
7762306a36Sopenharmony_ci			break;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return ret;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_cierror:
8362306a36Sopenharmony_ci	exit(EXIT_FAILURE);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci/*
8762306a36Sopenharmony_ci * contol read/write threads by handling global_run_operation
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_civoid *rw_ctl_loop(int ctl_fd)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	ssize_t rlen;
9262306a36Sopenharmony_ci	char buf[HOST_MSG_SIZE];
9362306a36Sopenharmony_ci	int ret;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	/* Setup signal handlers */
9662306a36Sopenharmony_ci	signal(SIGTERM, signal_handler);
9762306a36Sopenharmony_ci	signal(SIGINT, signal_handler);
9862306a36Sopenharmony_ci	signal(SIGQUIT, signal_handler);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	while (!global_sig_receive) {
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci		ret = wait_order(ctl_fd);
10362306a36Sopenharmony_ci		if (ret < 0)
10462306a36Sopenharmony_ci			break;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci		rlen = read(ctl_fd, buf, sizeof(buf));
10762306a36Sopenharmony_ci		if (rlen < 0) {
10862306a36Sopenharmony_ci			pr_err("read data error in ctl thread\n");
10962306a36Sopenharmony_ci			goto error;
11062306a36Sopenharmony_ci		}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		if (rlen == 2 && buf[0] == '1') {
11362306a36Sopenharmony_ci			/*
11462306a36Sopenharmony_ci			 * If host writes '1' to a control path,
11562306a36Sopenharmony_ci			 * this controller wakes all read/write threads.
11662306a36Sopenharmony_ci			 */
11762306a36Sopenharmony_ci			global_run_operation = true;
11862306a36Sopenharmony_ci			pthread_cond_broadcast(&cond_wakeup);
11962306a36Sopenharmony_ci			pr_debug("Wake up all read/write threads\n");
12062306a36Sopenharmony_ci		} else if (rlen == 2 && buf[0] == '0') {
12162306a36Sopenharmony_ci			/*
12262306a36Sopenharmony_ci			 * If host writes '0' to a control path, read/write
12362306a36Sopenharmony_ci			 * threads will wait for notification from Host.
12462306a36Sopenharmony_ci			 */
12562306a36Sopenharmony_ci			global_run_operation = false;
12662306a36Sopenharmony_ci			pr_debug("Stop all read/write threads\n");
12762306a36Sopenharmony_ci		} else
12862306a36Sopenharmony_ci			pr_info("Invalid host notification: %s\n", buf);
12962306a36Sopenharmony_ci	}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	return NULL;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_cierror:
13462306a36Sopenharmony_ci	exit(EXIT_FAILURE);
13562306a36Sopenharmony_ci}
136