xref: /kvmtool/kvm-ipc.c (revision e21e8ff36e54eac082d510eb18c4b655a6c3261b)
14b1addaeSSasha Levin #include "kvm/kvm-ipc.h"
24b1addaeSSasha Levin #include "kvm/rwsem.h"
34b1addaeSSasha Levin #include "kvm/read-write.h"
44b1addaeSSasha Levin #include "kvm/util.h"
54b1addaeSSasha Levin 
64b1addaeSSasha Levin #include <sys/epoll.h>
74b1addaeSSasha Levin #include <sys/un.h>
84b1addaeSSasha Levin #include <sys/types.h>
94b1addaeSSasha Levin #include <sys/socket.h>
10c733c80bSSasha Levin #include <sys/eventfd.h>
114b1addaeSSasha Levin 
12a9aae6c5SLai Jiangshan struct kvm_ipc_head {
13a9aae6c5SLai Jiangshan 	u32 type;
14a9aae6c5SLai Jiangshan 	u32 len;
15a9aae6c5SLai Jiangshan };
16a9aae6c5SLai Jiangshan 
174b1addaeSSasha Levin #define KVM_IPC_MAX_MSGS 16
184b1addaeSSasha Levin 
194b1addaeSSasha Levin static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg);
204b1addaeSSasha Levin static DECLARE_RWSEM(msgs_rwlock);
21c733c80bSSasha Levin static int epoll_fd, server_fd, stop_fd;
22c733c80bSSasha Levin static pthread_t thread;
234b1addaeSSasha Levin 
244b1addaeSSasha Levin int kvm_ipc__register_handler(u32 type, void (*cb)(int fd, u32 type, u32 len, u8 *msg))
254b1addaeSSasha Levin {
264b1addaeSSasha Levin 	if (type >= KVM_IPC_MAX_MSGS)
274b1addaeSSasha Levin 		return -ENOSPC;
284b1addaeSSasha Levin 
294b1addaeSSasha Levin 	down_write(&msgs_rwlock);
304b1addaeSSasha Levin 	msgs[type] = cb;
314b1addaeSSasha Levin 	up_write(&msgs_rwlock);
324b1addaeSSasha Levin 
334b1addaeSSasha Levin 	return 0;
344b1addaeSSasha Levin }
354b1addaeSSasha Levin 
3650dc18aeSLai Jiangshan int kvm_ipc__send(int fd, u32 type)
3750dc18aeSLai Jiangshan {
3850dc18aeSLai Jiangshan 	struct kvm_ipc_head head = {.type = type, .len = 0,};
3950dc18aeSLai Jiangshan 
40ca088268SAsias He 	if (write_in_full(fd, &head, sizeof(head)) < 0)
4150dc18aeSLai Jiangshan 		return -1;
4250dc18aeSLai Jiangshan 
4350dc18aeSLai Jiangshan 	return 0;
4450dc18aeSLai Jiangshan }
4550dc18aeSLai Jiangshan 
4650dc18aeSLai Jiangshan int kvm_ipc__send_msg(int fd, u32 type, u32 len, u8 *msg)
4750dc18aeSLai Jiangshan {
4850dc18aeSLai Jiangshan 	struct kvm_ipc_head head = {.type = type, .len = len,};
4950dc18aeSLai Jiangshan 
50ca088268SAsias He 	if (write_in_full(fd, &head, sizeof(head)) < 0)
5150dc18aeSLai Jiangshan 		return -1;
5250dc18aeSLai Jiangshan 
53fd5b45a1SAsias He 	if (write_in_full(fd, msg, len) < 0)
5450dc18aeSLai Jiangshan 		return -1;
5550dc18aeSLai Jiangshan 
5650dc18aeSLai Jiangshan 	return 0;
5750dc18aeSLai Jiangshan }
5850dc18aeSLai Jiangshan 
5944a56bfdSLai Jiangshan static int kvm_ipc__handle(int fd, u32 type, u32 len, u8 *data)
604b1addaeSSasha Levin {
614b1addaeSSasha Levin 	void (*cb)(int fd, u32 type, u32 len, u8 *msg);
624b1addaeSSasha Levin 
6344a56bfdSLai Jiangshan 	if (type >= KVM_IPC_MAX_MSGS)
644b1addaeSSasha Levin 		return -ENOSPC;
654b1addaeSSasha Levin 
664b1addaeSSasha Levin 	down_read(&msgs_rwlock);
6744a56bfdSLai Jiangshan 	cb = msgs[type];
684b1addaeSSasha Levin 	up_read(&msgs_rwlock);
694b1addaeSSasha Levin 
704b1addaeSSasha Levin 	if (cb == NULL) {
7144a56bfdSLai Jiangshan 		pr_warning("No device handles type %u\n", type);
724b1addaeSSasha Levin 		return -ENODEV;
734b1addaeSSasha Levin 	}
744b1addaeSSasha Levin 
7544a56bfdSLai Jiangshan 	cb(fd, type, len, data);
764b1addaeSSasha Levin 
774b1addaeSSasha Levin 	return 0;
784b1addaeSSasha Levin }
794b1addaeSSasha Levin 
804b1addaeSSasha Levin static int kvm_ipc__new_conn(int fd)
814b1addaeSSasha Levin {
824b1addaeSSasha Levin 	int client;
834b1addaeSSasha Levin 	struct epoll_event ev;
844b1addaeSSasha Levin 
854b1addaeSSasha Levin 	client = accept(fd, NULL, NULL);
864b1addaeSSasha Levin 	if (client < 0)
874b1addaeSSasha Levin 		return -1;
884b1addaeSSasha Levin 
894b1addaeSSasha Levin 	ev.events = EPOLLIN | EPOLLRDHUP;
904b1addaeSSasha Levin 	ev.data.fd = client;
914b1addaeSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client, &ev) < 0) {
924b1addaeSSasha Levin 		close(client);
934b1addaeSSasha Levin 		return -1;
944b1addaeSSasha Levin 	}
954b1addaeSSasha Levin 
964b1addaeSSasha Levin 	return client;
974b1addaeSSasha Levin }
984b1addaeSSasha Levin 
994b1addaeSSasha Levin static void kvm_ipc__close_conn(int fd)
1004b1addaeSSasha Levin {
1014b1addaeSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
1024b1addaeSSasha Levin 	close(fd);
1034b1addaeSSasha Levin }
1044b1addaeSSasha Levin 
1058e463c62SAsias He static int kvm_ipc__receive(int fd)
1064b1addaeSSasha Levin {
107a9aae6c5SLai Jiangshan 	struct kvm_ipc_head head;
108a9aae6c5SLai Jiangshan 	u8 *msg = NULL;
1094b1addaeSSasha Levin 	u32 n;
1104b1addaeSSasha Levin 
111a9aae6c5SLai Jiangshan 	n = read(fd, &head, sizeof(head));
112a9aae6c5SLai Jiangshan 	if (n != sizeof(head))
113a9aae6c5SLai Jiangshan 		goto done;
114a9aae6c5SLai Jiangshan 
115a9aae6c5SLai Jiangshan 	msg = malloc(head.len);
1164b1addaeSSasha Levin 	if (msg == NULL)
1174b1addaeSSasha Levin 		goto done;
1184b1addaeSSasha Levin 
119a9aae6c5SLai Jiangshan 	n = read_in_full(fd, msg, head.len);
120a9aae6c5SLai Jiangshan 	if (n != head.len)
1214b1addaeSSasha Levin 		goto done;
1224b1addaeSSasha Levin 
123a9aae6c5SLai Jiangshan 	kvm_ipc__handle(fd, head.type, head.len, msg);
1244b1addaeSSasha Levin 
1258e463c62SAsias He 	return 0;
1268e463c62SAsias He 
1274b1addaeSSasha Levin done:
1284b1addaeSSasha Levin 	free(msg);
1298e463c62SAsias He 	return -1;
1304b1addaeSSasha Levin }
1314b1addaeSSasha Levin 
1324b1addaeSSasha Levin static void *kvm_ipc__thread(void *param)
1334b1addaeSSasha Levin {
1344b1addaeSSasha Levin 	struct epoll_event event;
1354b1addaeSSasha Levin 
1364b1addaeSSasha Levin 	for (;;) {
1374b1addaeSSasha Levin 		int nfds;
1384b1addaeSSasha Levin 
1394b1addaeSSasha Levin 		nfds = epoll_wait(epoll_fd, &event, 1, -1);
1404b1addaeSSasha Levin 		if (nfds > 0) {
1414b1addaeSSasha Levin 			int fd = event.data.fd;
1424b1addaeSSasha Levin 
14347f72b90SSasha Levin 			if (fd == stop_fd && event.events & EPOLLIN) {
144c733c80bSSasha Levin 				break;
145c733c80bSSasha Levin 			} else if (fd == server_fd) {
1468e463c62SAsias He 				int client, r;
1474b1addaeSSasha Levin 
1484b1addaeSSasha Levin 				client = kvm_ipc__new_conn(fd);
1498e463c62SAsias He 				/*
1508e463c62SAsias He 				 * Handle multiple IPC cmd at a time
1518e463c62SAsias He 				 */
1528e463c62SAsias He 				do {
1538e463c62SAsias He 					r = kvm_ipc__receive(client);
1548e463c62SAsias He 				} while	(r == 0);
1558e463c62SAsias He 
1564b1addaeSSasha Levin 			} else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) {
1574b1addaeSSasha Levin 				kvm_ipc__close_conn(fd);
1584b1addaeSSasha Levin 			} else {
159a9aae6c5SLai Jiangshan 				kvm_ipc__receive(fd);
1604b1addaeSSasha Levin 			}
1614b1addaeSSasha Levin 		}
1624b1addaeSSasha Levin 	}
1634b1addaeSSasha Levin 
1644b1addaeSSasha Levin 	return NULL;
1654b1addaeSSasha Levin }
1664b1addaeSSasha Levin 
1674b1addaeSSasha Levin int kvm_ipc__start(int sock)
1684b1addaeSSasha Levin {
169*e21e8ff3SYang Bai 	int ret;
17021b3c2c0SSasha Levin 	struct epoll_event ev = {0};
1714b1addaeSSasha Levin 
1724b1addaeSSasha Levin 	server_fd = sock;
1734b1addaeSSasha Levin 
1744b1addaeSSasha Levin 	epoll_fd = epoll_create(KVM_IPC_MAX_MSGS);
175*e21e8ff3SYang Bai 	if (epoll_fd < 0) {
176*e21e8ff3SYang Bai 		ret = epoll_fd;
177*e21e8ff3SYang Bai 		goto err;
178*e21e8ff3SYang Bai 	}
1794b1addaeSSasha Levin 
18047f72b90SSasha Levin 	ev.events = EPOLLIN | EPOLLET;
1814b1addaeSSasha Levin 	ev.data.fd = sock;
182*e21e8ff3SYang Bai 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0) {
183*e21e8ff3SYang Bai 		pr_err("Failed starting IPC thread");
184*e21e8ff3SYang Bai 		ret = -EFAULT;
185*e21e8ff3SYang Bai 		goto err_epoll;
186*e21e8ff3SYang Bai 	}
1874b1addaeSSasha Levin 
188c733c80bSSasha Levin 	stop_fd = eventfd(0, 0);
189*e21e8ff3SYang Bai 	if (stop_fd < 0) {
190*e21e8ff3SYang Bai 		ret = stop_fd;
191*e21e8ff3SYang Bai 		goto err_epoll;
192*e21e8ff3SYang Bai 	}
193*e21e8ff3SYang Bai 
19447f72b90SSasha Levin 	ev.events = EPOLLIN | EPOLLET;
195c733c80bSSasha Levin 	ev.data.fd = stop_fd;
196*e21e8ff3SYang Bai 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stop_fd, &ev) < 0) {
197*e21e8ff3SYang Bai 		pr_err("Failed adding stop event to epoll");
198*e21e8ff3SYang Bai 		ret = -EFAULT;
199*e21e8ff3SYang Bai 		goto err_stop;
200*e21e8ff3SYang Bai 	}
201c733c80bSSasha Levin 
202*e21e8ff3SYang Bai 	if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0) {
203*e21e8ff3SYang Bai 		pr_err("Failed starting IPC thread");
204*e21e8ff3SYang Bai 		ret = -EFAULT;
205*e21e8ff3SYang Bai 		goto err_stop;
206*e21e8ff3SYang Bai 	}
2074b1addaeSSasha Levin 
2084b1addaeSSasha Levin 	return 0;
209*e21e8ff3SYang Bai 
210*e21e8ff3SYang Bai err_stop:
211*e21e8ff3SYang Bai 	close(stop_fd);
212*e21e8ff3SYang Bai err_epoll:
213*e21e8ff3SYang Bai 	close(epoll_fd);
214*e21e8ff3SYang Bai err:
215*e21e8ff3SYang Bai 	return ret;
2164b1addaeSSasha Levin }
217c733c80bSSasha Levin 
218c733c80bSSasha Levin int kvm_ipc__stop(void)
219c733c80bSSasha Levin {
220c733c80bSSasha Levin 	u64 val = 1;
221c733c80bSSasha Levin 	int ret;
222c733c80bSSasha Levin 
223c733c80bSSasha Levin 	ret = write(stop_fd, &val, sizeof(val));
224c733c80bSSasha Levin 	if (ret < 0)
225c733c80bSSasha Levin 		return ret;
226c733c80bSSasha Levin 
227c733c80bSSasha Levin 	close(server_fd);
228c733c80bSSasha Levin 	close(epoll_fd);
229c733c80bSSasha Levin 
230c733c80bSSasha Levin 	return ret;
231c733c80bSSasha Levin }
232