xref: /kvmtool/kvm-ipc.c (revision 44a56bfd327f8c6a110c5cd5ddf254a7def8f9fb)
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 
124b1addaeSSasha Levin #define KVM_IPC_MAX_MSGS 16
134b1addaeSSasha Levin 
144b1addaeSSasha Levin static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg);
154b1addaeSSasha Levin static DECLARE_RWSEM(msgs_rwlock);
16c733c80bSSasha Levin static int epoll_fd, server_fd, stop_fd;
17c733c80bSSasha Levin static pthread_t thread;
184b1addaeSSasha Levin 
194b1addaeSSasha Levin int kvm_ipc__register_handler(u32 type, void (*cb)(int fd, u32 type, u32 len, u8 *msg))
204b1addaeSSasha Levin {
214b1addaeSSasha Levin 	if (type >= KVM_IPC_MAX_MSGS)
224b1addaeSSasha Levin 		return -ENOSPC;
234b1addaeSSasha Levin 
244b1addaeSSasha Levin 	down_write(&msgs_rwlock);
254b1addaeSSasha Levin 	msgs[type] = cb;
264b1addaeSSasha Levin 	up_write(&msgs_rwlock);
274b1addaeSSasha Levin 
284b1addaeSSasha Levin 	return 0;
294b1addaeSSasha Levin }
304b1addaeSSasha Levin 
31*44a56bfdSLai Jiangshan static int kvm_ipc__handle(int fd, u32 type, u32 len, u8 *data)
324b1addaeSSasha Levin {
334b1addaeSSasha Levin 	void (*cb)(int fd, u32 type, u32 len, u8 *msg);
344b1addaeSSasha Levin 
35*44a56bfdSLai Jiangshan 	if (type >= KVM_IPC_MAX_MSGS)
364b1addaeSSasha Levin 		return -ENOSPC;
374b1addaeSSasha Levin 
384b1addaeSSasha Levin 	down_read(&msgs_rwlock);
39*44a56bfdSLai Jiangshan 	cb = msgs[type];
404b1addaeSSasha Levin 	up_read(&msgs_rwlock);
414b1addaeSSasha Levin 
424b1addaeSSasha Levin 	if (cb == NULL) {
43*44a56bfdSLai Jiangshan 		pr_warning("No device handles type %u\n", type);
444b1addaeSSasha Levin 		return -ENODEV;
454b1addaeSSasha Levin 	}
464b1addaeSSasha Levin 
47*44a56bfdSLai Jiangshan 	cb(fd, type, len, data);
484b1addaeSSasha Levin 
494b1addaeSSasha Levin 	return 0;
504b1addaeSSasha Levin }
514b1addaeSSasha Levin 
524b1addaeSSasha Levin static int kvm_ipc__new_conn(int fd)
534b1addaeSSasha Levin {
544b1addaeSSasha Levin 	int client;
554b1addaeSSasha Levin 	struct epoll_event ev;
564b1addaeSSasha Levin 
574b1addaeSSasha Levin 	client = accept(fd, NULL, NULL);
584b1addaeSSasha Levin 	if (client < 0)
594b1addaeSSasha Levin 		return -1;
604b1addaeSSasha Levin 
614b1addaeSSasha Levin 	ev.events = EPOLLIN | EPOLLRDHUP;
624b1addaeSSasha Levin 	ev.data.fd = client;
634b1addaeSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client, &ev) < 0) {
644b1addaeSSasha Levin 		close(client);
654b1addaeSSasha Levin 		return -1;
664b1addaeSSasha Levin 	}
674b1addaeSSasha Levin 
684b1addaeSSasha Levin 	return client;
694b1addaeSSasha Levin }
704b1addaeSSasha Levin 
714b1addaeSSasha Levin static void kvm_ipc__close_conn(int fd)
724b1addaeSSasha Levin {
734b1addaeSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
744b1addaeSSasha Levin 	close(fd);
754b1addaeSSasha Levin }
764b1addaeSSasha Levin 
774b1addaeSSasha Levin static void kvm_ipc__new_data(int fd)
784b1addaeSSasha Levin {
794b1addaeSSasha Levin 	struct kvm_ipc_msg *msg;
804b1addaeSSasha Levin 	u32 n;
814b1addaeSSasha Levin 
824b1addaeSSasha Levin 	msg = malloc(sizeof(*msg));
834b1addaeSSasha Levin 	if (msg == NULL)
844b1addaeSSasha Levin 		goto done;
854b1addaeSSasha Levin 
864b1addaeSSasha Levin 	n = read(fd, msg, sizeof(*msg));
874b1addaeSSasha Levin 	if (n != sizeof(*msg))
884b1addaeSSasha Levin 		goto done;
894b1addaeSSasha Levin 
904b1addaeSSasha Levin 	msg = realloc(msg, sizeof(*msg) + msg->len);
914b1addaeSSasha Levin 	if (msg == NULL)
924b1addaeSSasha Levin 		goto done;
934b1addaeSSasha Levin 
944b1addaeSSasha Levin 	n = read_in_full(fd, msg->data, msg->len);
954b1addaeSSasha Levin 	if (n != msg->len)
964b1addaeSSasha Levin 		goto done;
974b1addaeSSasha Levin 
98*44a56bfdSLai Jiangshan 	kvm_ipc__handle(fd, msg->type, msg->len, msg->data);
994b1addaeSSasha Levin 
1004b1addaeSSasha Levin done:
1014b1addaeSSasha Levin 	free(msg);
1024b1addaeSSasha Levin }
1034b1addaeSSasha Levin 
1044b1addaeSSasha Levin static void *kvm_ipc__thread(void *param)
1054b1addaeSSasha Levin {
1064b1addaeSSasha Levin 	struct epoll_event event;
1074b1addaeSSasha Levin 
1084b1addaeSSasha Levin 	for (;;) {
1094b1addaeSSasha Levin 		int nfds;
1104b1addaeSSasha Levin 
1114b1addaeSSasha Levin 		nfds = epoll_wait(epoll_fd, &event, 1, -1);
1124b1addaeSSasha Levin 		if (nfds > 0) {
1134b1addaeSSasha Levin 			int fd = event.data.fd;
1144b1addaeSSasha Levin 
11547f72b90SSasha Levin 			if (fd == stop_fd && event.events & EPOLLIN) {
116c733c80bSSasha Levin 				break;
117c733c80bSSasha Levin 			} else if (fd == server_fd) {
1184b1addaeSSasha Levin 				int client;
1194b1addaeSSasha Levin 
1204b1addaeSSasha Levin 				client = kvm_ipc__new_conn(fd);
1214b1addaeSSasha Levin 				kvm_ipc__new_data(client);
1224b1addaeSSasha Levin 			} else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) {
1234b1addaeSSasha Levin 				kvm_ipc__close_conn(fd);
1244b1addaeSSasha Levin 			} else {
1254b1addaeSSasha Levin 				kvm_ipc__new_data(fd);
1264b1addaeSSasha Levin 			}
1274b1addaeSSasha Levin 		}
1284b1addaeSSasha Levin 	}
1294b1addaeSSasha Levin 
1304b1addaeSSasha Levin 	return NULL;
1314b1addaeSSasha Levin }
1324b1addaeSSasha Levin 
1334b1addaeSSasha Levin int kvm_ipc__start(int sock)
1344b1addaeSSasha Levin {
13521b3c2c0SSasha Levin 	struct epoll_event ev = {0};
1364b1addaeSSasha Levin 
1374b1addaeSSasha Levin 	server_fd = sock;
1384b1addaeSSasha Levin 
1394b1addaeSSasha Levin 	epoll_fd = epoll_create(KVM_IPC_MAX_MSGS);
1404b1addaeSSasha Levin 
14147f72b90SSasha Levin 	ev.events = EPOLLIN | EPOLLET;
1424b1addaeSSasha Levin 	ev.data.fd = sock;
1434b1addaeSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0)
1444b1addaeSSasha Levin 		die("Failed starting IPC thread");
1454b1addaeSSasha Levin 
146c733c80bSSasha Levin 	stop_fd = eventfd(0, 0);
14747f72b90SSasha Levin 	ev.events = EPOLLIN | EPOLLET;
148c733c80bSSasha Levin 	ev.data.fd = stop_fd;
149c733c80bSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stop_fd, &ev) < 0)
150c733c80bSSasha Levin 		die("Failed adding stop event to epoll");
151c733c80bSSasha Levin 
1524b1addaeSSasha Levin 	if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0)
1534b1addaeSSasha Levin 		die("Failed starting IPC thread");
1544b1addaeSSasha Levin 
1554b1addaeSSasha Levin 	return 0;
1564b1addaeSSasha Levin }
157c733c80bSSasha Levin 
158c733c80bSSasha Levin int kvm_ipc__stop(void)
159c733c80bSSasha Levin {
160c733c80bSSasha Levin 	u64 val = 1;
161c733c80bSSasha Levin 	int ret;
162c733c80bSSasha Levin 
163c733c80bSSasha Levin 	ret = write(stop_fd, &val, sizeof(val));
164c733c80bSSasha Levin 	if (ret < 0)
165c733c80bSSasha Levin 		return ret;
166c733c80bSSasha Levin 
167c733c80bSSasha Levin 	close(server_fd);
168c733c80bSSasha Levin 	close(epoll_fd);
169c733c80bSSasha Levin 
170c733c80bSSasha Levin 	return ret;
171c733c80bSSasha Levin }
172