| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Controller of read/write threads for virtio-trace | |
| * | |
| * Copyright (C) 2012 Hitachi, Ltd. | |
| * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com> | |
| * Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | |
| */ | |
| #define _GNU_SOURCE | |
| #include <fcntl.h> | |
| #include <poll.h> | |
| #include <signal.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <unistd.h> | |
| #include "trace-agent.h" | |
| #define HOST_MSG_SIZE 256 | |
| #define EVENT_WAIT_MSEC 100 | |
| static volatile sig_atomic_t global_signal_val; | |
| bool global_sig_receive; /* default false */ | |
| bool global_run_operation; /* default false*/ | |
| /* Handle SIGTERM/SIGINT/SIGQUIT to exit */ | |
| static void signal_handler(int sig) | |
| { | |
| global_signal_val = sig; | |
| } | |
| int rw_ctl_init(const char *ctl_path) | |
| { | |
| int ctl_fd; | |
| ctl_fd = open(ctl_path, O_RDONLY); | |
| if (ctl_fd == -1) { | |
| pr_err("Cannot open ctl_fd\n"); | |
| goto error; | |
| } | |
| return ctl_fd; | |
| error: | |
| exit(EXIT_FAILURE); | |
| } | |
| static int wait_order(int ctl_fd) | |
| { | |
| struct pollfd poll_fd; | |
| int ret = 0; | |
| while (!global_sig_receive) { | |
| poll_fd.fd = ctl_fd; | |
| poll_fd.events = POLLIN; | |
| ret = poll(&poll_fd, 1, EVENT_WAIT_MSEC); | |
| if (global_signal_val) { | |
| global_sig_receive = true; | |
| pr_info("Receive interrupt %d\n", global_signal_val); | |
| /* Wakes rw-threads when they are sleeping */ | |
| if (!global_run_operation) | |
| pthread_cond_broadcast(&cond_wakeup); | |
| ret = -1; | |
| break; | |
| } | |
| if (ret < 0) { | |
| pr_err("Polling error\n"); | |
| goto error; | |
| } | |
| if (ret) | |
| break; | |
| } | |
| return ret; | |
| error: | |
| exit(EXIT_FAILURE); | |
| } | |
| /* | |
| * contol read/write threads by handling global_run_operation | |
| */ | |
| void *rw_ctl_loop(int ctl_fd) | |
| { | |
| ssize_t rlen; | |
| char buf[HOST_MSG_SIZE]; | |
| int ret; | |
| /* Setup signal handlers */ | |
| signal(SIGTERM, signal_handler); | |
| signal(SIGINT, signal_handler); | |
| signal(SIGQUIT, signal_handler); | |
| while (!global_sig_receive) { | |
| ret = wait_order(ctl_fd); | |
| if (ret < 0) | |
| break; | |
| rlen = read(ctl_fd, buf, sizeof(buf)); | |
| if (rlen < 0) { | |
| pr_err("read data error in ctl thread\n"); | |
| goto error; | |
| } | |
| if (rlen == 2 && buf[0] == '1') { | |
| /* | |
| * If host writes '1' to a control path, | |
| * this controller wakes all read/write threads. | |
| */ | |
| global_run_operation = true; | |
| pthread_cond_broadcast(&cond_wakeup); | |
| pr_debug("Wake up all read/write threads\n"); | |
| } else if (rlen == 2 && buf[0] == '0') { | |
| /* | |
| * If host writes '0' to a control path, read/write | |
| * threads will wait for notification from Host. | |
| */ | |
| global_run_operation = false; | |
| pr_debug("Stop all read/write threads\n"); | |
| } else | |
| pr_info("Invalid host notification: %s\n", buf); | |
| } | |
| return NULL; | |
| error: | |
| exit(EXIT_FAILURE); | |
| } |