xref: /dpdk/drivers/power/kvm_vm/guest_channel.c (revision 6f987b594fa6751b49769755fe1d1bf9f9d15ac4)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2014 Intel Corporation
3  */
4 
5 #include <glob.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <limits.h>
9 #include <fcntl.h>
10 #include <string.h>
11 #include <errno.h>
12 #include <poll.h>
13 
14 
15 #include <rte_log.h>
16 #include <rte_power_guest_channel.h>
17 
18 #include "guest_channel.h"
19 
20 RTE_LOG_REGISTER_SUFFIX(guest_channel_logtype, guest_channel, INFO);
21 #define RTE_LOGTYPE_GUEST_CHANNEL guest_channel_logtype
22 #define GUEST_CHANNEL_LOG(level, ...) \
23 	RTE_LOG_LINE(level, GUEST_CHANNEL, "" __VA_ARGS__)
24 
25 /* Timeout for incoming message in milliseconds. */
26 #define TIMEOUT 10
27 
28 static int global_fds[RTE_MAX_LCORE] = { [0 ... RTE_MAX_LCORE-1] = -1 };
29 
30 int
31 guest_channel_host_check_exists(const char *path)
32 {
33 	char glob_path[PATH_MAX];
34 	glob_t g;
35 	int ret;
36 
37 	/* we cannot know in advance which cores have VM channels, so glob */
38 	snprintf(glob_path, PATH_MAX, "%s.*", path);
39 
40 	ret = glob(glob_path, GLOB_NOSORT, NULL, &g);
41 	if (ret != 0) {
42 		/* couldn't read anything */
43 		ret = 0;
44 		goto out;
45 	}
46 
47 	/* do we have at least one match? */
48 	ret = g.gl_pathc > 0;
49 
50 out:
51 	globfree(&g);
52 	return ret;
53 }
54 
55 int
56 guest_channel_host_connect(const char *path, unsigned int lcore_id)
57 {
58 	int flags, ret;
59 	struct rte_power_channel_packet pkt;
60 	char fd_path[PATH_MAX];
61 	int fd = -1;
62 
63 	if (lcore_id >= RTE_MAX_LCORE) {
64 		GUEST_CHANNEL_LOG(ERR, "Channel(%u) is out of range 0...%d",
65 				lcore_id, RTE_MAX_LCORE-1);
66 		return -1;
67 	}
68 	/* check if path is already open */
69 	if (global_fds[lcore_id] != -1) {
70 		GUEST_CHANNEL_LOG(ERR, "Channel(%u) is already open with fd %d",
71 				lcore_id, global_fds[lcore_id]);
72 		return -1;
73 	}
74 
75 	snprintf(fd_path, PATH_MAX, "%s.%u", path, lcore_id);
76 	GUEST_CHANNEL_LOG(INFO, "Opening channel '%s' for lcore %u",
77 			fd_path, lcore_id);
78 	fd = open(fd_path, O_RDWR);
79 	if (fd < 0) {
80 		GUEST_CHANNEL_LOG(ERR, "Unable to connect to '%s' with error "
81 				"%s", fd_path, strerror(errno));
82 		return -1;
83 	}
84 
85 	flags = fcntl(fd, F_GETFL, 0);
86 	if (flags < 0) {
87 		GUEST_CHANNEL_LOG(ERR, "Failed on fcntl get flags for file %s",
88 				fd_path);
89 		goto error;
90 	}
91 
92 	flags |= O_NONBLOCK;
93 	if (fcntl(fd, F_SETFL, flags) < 0) {
94 		GUEST_CHANNEL_LOG(ERR, "Failed on setting non-blocking mode for "
95 				"file %s", fd_path);
96 		goto error;
97 	}
98 	/* QEMU needs a delay after connection */
99 	sleep(1);
100 
101 	/* Send a test packet, this command is ignored by the host, but a successful
102 	 * send indicates that the host endpoint is monitoring.
103 	 */
104 	pkt.command = RTE_POWER_CPU_POWER_CONNECT;
105 	global_fds[lcore_id] = fd;
106 	ret = guest_channel_send_msg(&pkt, lcore_id);
107 	if (ret != 0) {
108 		GUEST_CHANNEL_LOG(ERR,
109 				"Error on channel '%s' communications test: %s",
110 				fd_path, ret > 0 ? strerror(ret) :
111 				"channel not connected");
112 		goto error;
113 	}
114 	GUEST_CHANNEL_LOG(INFO, "Channel '%s' is now connected", fd_path);
115 	return 0;
116 error:
117 	close(fd);
118 	global_fds[lcore_id] = -1;
119 	return -1;
120 }
121 
122 int
123 guest_channel_send_msg(struct rte_power_channel_packet *pkt,
124 		unsigned int lcore_id)
125 {
126 	int ret, buffer_len = sizeof(*pkt);
127 	void *buffer = pkt;
128 
129 	if (lcore_id >= RTE_MAX_LCORE) {
130 		GUEST_CHANNEL_LOG(ERR, "Channel(%u) is out of range 0...%d",
131 				lcore_id, RTE_MAX_LCORE-1);
132 		return -1;
133 	}
134 
135 	if (global_fds[lcore_id] < 0) {
136 		GUEST_CHANNEL_LOG(ERR, "Channel is not connected");
137 		return -1;
138 	}
139 	while (buffer_len > 0) {
140 		ret = write(global_fds[lcore_id], buffer, buffer_len);
141 		if (ret == buffer_len)
142 			return 0;
143 		if (ret == -1) {
144 			if (errno == EINTR)
145 				continue;
146 			return errno;
147 		}
148 		buffer = (char *)buffer + ret;
149 		buffer_len -= ret;
150 	}
151 	return 0;
152 }
153 
154 int rte_power_guest_channel_send_msg(struct rte_power_channel_packet *pkt,
155 			unsigned int lcore_id)
156 {
157 	return guest_channel_send_msg(pkt, lcore_id);
158 }
159 
160 int power_guest_channel_read_msg(void *pkt,
161 		size_t pkt_len,
162 		unsigned int lcore_id)
163 {
164 	int ret;
165 	struct pollfd fds;
166 
167 	if (pkt_len == 0 || pkt == NULL)
168 		return -1;
169 
170 	if (lcore_id >= RTE_MAX_LCORE) {
171 		GUEST_CHANNEL_LOG(ERR, "Channel(%u) is out of range 0...%d",
172 				lcore_id, RTE_MAX_LCORE-1);
173 		return -1;
174 	}
175 
176 	if (global_fds[lcore_id] < 0) {
177 		GUEST_CHANNEL_LOG(ERR, "Channel is not connected");
178 		return -1;
179 	}
180 
181 	fds.fd = global_fds[lcore_id];
182 	fds.events = POLLIN;
183 
184 	ret = poll(&fds, 1, TIMEOUT);
185 	if (ret == 0) {
186 		GUEST_CHANNEL_LOG(DEBUG, "Timeout occurred during poll function.");
187 		return -1;
188 	} else if (ret < 0) {
189 		GUEST_CHANNEL_LOG(ERR, "Error occurred during poll function: %s",
190 				strerror(errno));
191 		return -1;
192 	}
193 
194 	while (pkt_len > 0) {
195 		ret = read(global_fds[lcore_id],
196 				pkt, pkt_len);
197 
198 		if (ret < 0) {
199 			if (errno == EINTR)
200 				continue;
201 			return -1;
202 		}
203 
204 		if (ret == 0) {
205 			GUEST_CHANNEL_LOG(ERR, "Expected more data, but connection has been closed.");
206 			return -1;
207 		}
208 		pkt = (char *)pkt + ret;
209 		pkt_len -= ret;
210 	}
211 
212 	return 0;
213 }
214 
215 int rte_power_guest_channel_receive_msg(void *pkt,
216 		size_t pkt_len,
217 		unsigned int lcore_id)
218 {
219 	return power_guest_channel_read_msg(pkt, pkt_len, lcore_id);
220 }
221 
222 void
223 guest_channel_host_disconnect(unsigned int lcore_id)
224 {
225 	if (lcore_id >= RTE_MAX_LCORE) {
226 		GUEST_CHANNEL_LOG(ERR, "Channel(%u) is out of range 0...%d",
227 				lcore_id, RTE_MAX_LCORE-1);
228 		return;
229 	}
230 	if (global_fds[lcore_id] < 0)
231 		return;
232 	close(global_fds[lcore_id]);
233 	global_fds[lcore_id] = -1;
234 }
235