| // 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); |
| } |