1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Controller of read/write threads for virtio-trace
4  *
5  * Copyright (C) 2012 Hitachi, Ltd.
6  * Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com>
7  *            Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
8  */
9 
10 #define _GNU_SOURCE
11 #include <fcntl.h>
12 #include <poll.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17 #include "trace-agent.h"
18 
19 #define HOST_MSG_SIZE		256
20 #define EVENT_WAIT_MSEC		100
21 
22 static volatile sig_atomic_t global_signal_val;
23 bool global_sig_receive;	/* default false */
24 bool global_run_operation;	/* default false*/
25 
26 /* Handle SIGTERM/SIGINT/SIGQUIT to exit */
27 static void signal_handler(int sig)
28 {
29 	global_signal_val = sig;
30 }
31 
32 int rw_ctl_init(const char *ctl_path)
33 {
34 	int ctl_fd;
35 
36 	ctl_fd = open(ctl_path, O_RDONLY);
37 	if (ctl_fd == -1) {
38 		pr_err("Cannot open ctl_fd\n");
39 		goto error;
40 	}
41 
42 	return ctl_fd;
43 
44 error:
45 	exit(EXIT_FAILURE);
46 }
47 
48 static int wait_order(int ctl_fd)
49 {
50 	struct pollfd poll_fd;
51 	int ret = 0;
52 
53 	while (!global_sig_receive) {
54 		poll_fd.fd = ctl_fd;
55 		poll_fd.events = POLLIN;
56 
57 		ret = poll(&poll_fd, 1, EVENT_WAIT_MSEC);
58 
59 		if (global_signal_val) {
60 			global_sig_receive = true;
61 			pr_info("Receive interrupt %d\n", global_signal_val);
62 
63 			/* Wakes rw-threads when they are sleeping */
64 			if (!global_run_operation)
65 				pthread_cond_broadcast(&cond_wakeup);
66 
67 			ret = -1;
68 			break;
69 		}
70 
71 		if (ret < 0) {
72 			pr_err("Polling error\n");
73 			goto error;
74 		}
75 
76 		if (ret)
77 			break;
78 	};
79 
80 	return ret;
81 
82 error:
83 	exit(EXIT_FAILURE);
84 }
85 
86 /*
87  * contol read/write threads by handling global_run_operation
88  */
89 void *rw_ctl_loop(int ctl_fd)
90 {
91 	ssize_t rlen;
92 	char buf[HOST_MSG_SIZE];
93 	int ret;
94 
95 	/* Setup signal handlers */
96 	signal(SIGTERM, signal_handler);
97 	signal(SIGINT, signal_handler);
98 	signal(SIGQUIT, signal_handler);
99 
100 	while (!global_sig_receive) {
101 
102 		ret = wait_order(ctl_fd);
103 		if (ret < 0)
104 			break;
105 
106 		rlen = read(ctl_fd, buf, sizeof(buf));
107 		if (rlen < 0) {
108 			pr_err("read data error in ctl thread\n");
109 			goto error;
110 		}
111 
112 		if (rlen == 2 && buf[0] == '1') {
113 			/*
114 			 * If host writes '1' to a control path,
115 			 * this controller wakes all read/write threads.
116 			 */
117 			global_run_operation = true;
118 			pthread_cond_broadcast(&cond_wakeup);
119 			pr_debug("Wake up all read/write threads\n");
120 		} else if (rlen == 2 && buf[0] == '0') {
121 			/*
122 			 * If host writes '0' to a control path, read/write
123 			 * threads will wait for notification from Host.
124 			 */
125 			global_run_operation = false;
126 			pr_debug("Stop all read/write threads\n");
127 		} else
128 			pr_info("Invalid host notification: %s\n", buf);
129 	}
130 
131 	return NULL;
132 
133 error:
134 	exit(EXIT_FAILURE);
135 }
136