xref: /kvmtool/kvm-ipc.c (revision 4b1addaeed6f8c717b2b818e3f867829849d8e86)
1*4b1addaeSSasha Levin #include "kvm/kvm-ipc.h"
2*4b1addaeSSasha Levin #include "kvm/rwsem.h"
3*4b1addaeSSasha Levin #include "kvm/read-write.h"
4*4b1addaeSSasha Levin #include "kvm/util.h"
5*4b1addaeSSasha Levin 
6*4b1addaeSSasha Levin #include <sys/epoll.h>
7*4b1addaeSSasha Levin #include <sys/un.h>
8*4b1addaeSSasha Levin #include <sys/types.h>
9*4b1addaeSSasha Levin #include <sys/socket.h>
10*4b1addaeSSasha Levin 
11*4b1addaeSSasha Levin #define KVM_IPC_MAX_MSGS 16
12*4b1addaeSSasha Levin 
13*4b1addaeSSasha Levin static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg);
14*4b1addaeSSasha Levin static DECLARE_RWSEM(msgs_rwlock);
15*4b1addaeSSasha Levin static int epoll_fd, server_fd;
16*4b1addaeSSasha Levin 
17*4b1addaeSSasha Levin int kvm_ipc__register_handler(u32 type, void (*cb)(int fd, u32 type, u32 len, u8 *msg))
18*4b1addaeSSasha Levin {
19*4b1addaeSSasha Levin 	if (type >= KVM_IPC_MAX_MSGS)
20*4b1addaeSSasha Levin 		return -ENOSPC;
21*4b1addaeSSasha Levin 
22*4b1addaeSSasha Levin 	down_write(&msgs_rwlock);
23*4b1addaeSSasha Levin 	msgs[type] = cb;
24*4b1addaeSSasha Levin 	up_write(&msgs_rwlock);
25*4b1addaeSSasha Levin 
26*4b1addaeSSasha Levin 	return 0;
27*4b1addaeSSasha Levin }
28*4b1addaeSSasha Levin 
29*4b1addaeSSasha Levin int kvm_ipc__handle(int fd, struct kvm_ipc_msg *msg)
30*4b1addaeSSasha Levin {
31*4b1addaeSSasha Levin 	void (*cb)(int fd, u32 type, u32 len, u8 *msg);
32*4b1addaeSSasha Levin 
33*4b1addaeSSasha Levin 	if (msg->type >= KVM_IPC_MAX_MSGS)
34*4b1addaeSSasha Levin 		return -ENOSPC;
35*4b1addaeSSasha Levin 
36*4b1addaeSSasha Levin 	down_read(&msgs_rwlock);
37*4b1addaeSSasha Levin 	cb = msgs[msg->type];
38*4b1addaeSSasha Levin 	up_read(&msgs_rwlock);
39*4b1addaeSSasha Levin 
40*4b1addaeSSasha Levin 	if (cb == NULL) {
41*4b1addaeSSasha Levin 		pr_warning("No device handles type %u\n", msg->type);
42*4b1addaeSSasha Levin 		return -ENODEV;
43*4b1addaeSSasha Levin 	}
44*4b1addaeSSasha Levin 
45*4b1addaeSSasha Levin 	cb(fd, msg->type, msg->len, msg->data);
46*4b1addaeSSasha Levin 
47*4b1addaeSSasha Levin 	return 0;
48*4b1addaeSSasha Levin }
49*4b1addaeSSasha Levin 
50*4b1addaeSSasha Levin static int kvm_ipc__new_conn(int fd)
51*4b1addaeSSasha Levin {
52*4b1addaeSSasha Levin 	int client;
53*4b1addaeSSasha Levin 	struct epoll_event ev;
54*4b1addaeSSasha Levin 
55*4b1addaeSSasha Levin 	client = accept(fd, NULL, NULL);
56*4b1addaeSSasha Levin 	if (client < 0)
57*4b1addaeSSasha Levin 		return -1;
58*4b1addaeSSasha Levin 
59*4b1addaeSSasha Levin 	ev.events = EPOLLIN | EPOLLRDHUP;
60*4b1addaeSSasha Levin 	ev.data.fd = client;
61*4b1addaeSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client, &ev) < 0) {
62*4b1addaeSSasha Levin 		close(client);
63*4b1addaeSSasha Levin 		return -1;
64*4b1addaeSSasha Levin 	}
65*4b1addaeSSasha Levin 
66*4b1addaeSSasha Levin 	return client;
67*4b1addaeSSasha Levin }
68*4b1addaeSSasha Levin 
69*4b1addaeSSasha Levin static void kvm_ipc__close_conn(int fd)
70*4b1addaeSSasha Levin {
71*4b1addaeSSasha Levin 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
72*4b1addaeSSasha Levin 	close(fd);
73*4b1addaeSSasha Levin }
74*4b1addaeSSasha Levin 
75*4b1addaeSSasha Levin static void kvm_ipc__new_data(int fd)
76*4b1addaeSSasha Levin {
77*4b1addaeSSasha Levin 	struct kvm_ipc_msg *msg;
78*4b1addaeSSasha Levin 	u32 n;
79*4b1addaeSSasha Levin 
80*4b1addaeSSasha Levin 	msg = malloc(sizeof(*msg));
81*4b1addaeSSasha Levin 	if (msg == NULL)
82*4b1addaeSSasha Levin 		goto done;
83*4b1addaeSSasha Levin 
84*4b1addaeSSasha Levin 	n = read(fd, msg, sizeof(*msg));
85*4b1addaeSSasha Levin 	if (n != sizeof(*msg))
86*4b1addaeSSasha Levin 		goto done;
87*4b1addaeSSasha Levin 
88*4b1addaeSSasha Levin 	msg = realloc(msg, sizeof(*msg) + msg->len);
89*4b1addaeSSasha Levin 	if (msg == NULL)
90*4b1addaeSSasha Levin 		goto done;
91*4b1addaeSSasha Levin 
92*4b1addaeSSasha Levin 	n = read_in_full(fd, msg->data, msg->len);
93*4b1addaeSSasha Levin 	if (n != msg->len)
94*4b1addaeSSasha Levin 		goto done;
95*4b1addaeSSasha Levin 
96*4b1addaeSSasha Levin 	kvm_ipc__handle(fd, msg);
97*4b1addaeSSasha Levin 
98*4b1addaeSSasha Levin done:
99*4b1addaeSSasha Levin 	free(msg);
100*4b1addaeSSasha Levin }
101*4b1addaeSSasha Levin 
102*4b1addaeSSasha Levin static void *kvm_ipc__thread(void *param)
103*4b1addaeSSasha Levin {
104*4b1addaeSSasha Levin 	struct epoll_event event;
105*4b1addaeSSasha Levin 
106*4b1addaeSSasha Levin 	for (;;) {
107*4b1addaeSSasha Levin 		int nfds;
108*4b1addaeSSasha Levin 
109*4b1addaeSSasha Levin 		nfds = epoll_wait(epoll_fd, &event, 1, -1);
110*4b1addaeSSasha Levin 		if (nfds > 0) {
111*4b1addaeSSasha Levin 			int fd = event.data.fd;
112*4b1addaeSSasha Levin 
113*4b1addaeSSasha Levin 			if (fd == server_fd) {
114*4b1addaeSSasha Levin 				int client;
115*4b1addaeSSasha Levin 
116*4b1addaeSSasha Levin 				client = kvm_ipc__new_conn(fd);
117*4b1addaeSSasha Levin 				kvm_ipc__new_data(client);
118*4b1addaeSSasha Levin 			} else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) {
119*4b1addaeSSasha Levin 				kvm_ipc__close_conn(fd);
120*4b1addaeSSasha Levin 			} else {
121*4b1addaeSSasha Levin 				kvm_ipc__new_data(fd);
122*4b1addaeSSasha Levin 			}
123*4b1addaeSSasha Levin 		}
124*4b1addaeSSasha Levin 	}
125*4b1addaeSSasha Levin 
126*4b1addaeSSasha Levin 	return NULL;
127*4b1addaeSSasha Levin }
128*4b1addaeSSasha Levin 
129*4b1addaeSSasha Levin int kvm_ipc__start(int sock)
130*4b1addaeSSasha Levin {
131*4b1addaeSSasha Levin 	pthread_t thread;
132*4b1addaeSSasha Levin 	struct epoll_event ev;
133*4b1addaeSSasha Levin 
134*4b1addaeSSasha Levin 	server_fd = sock;
135*4b1addaeSSasha Levin 
136*4b1addaeSSasha Levin 	epoll_fd = epoll_create(KVM_IPC_MAX_MSGS);
137*4b1addaeSSasha Levin 
138*4b1addaeSSasha Levin 	ev.events = EPOLLIN | EPOLLOUT | EPOLLPRI;
139*4b1addaeSSasha Levin 	ev.data.fd = sock;
140*4b1addaeSSasha Levin 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0)
141*4b1addaeSSasha Levin 		die("Failed starting IPC thread");
142*4b1addaeSSasha Levin 
143*4b1addaeSSasha Levin 	if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0)
144*4b1addaeSSasha Levin 		die("Failed starting IPC thread");
145*4b1addaeSSasha Levin 
146*4b1addaeSSasha Levin 	return 0;
147*4b1addaeSSasha Levin }
148