xref: /kvmtool/kvm-ipc.c (revision e21e8ff36e54eac082d510eb18c4b655a6c3261b)
1 #include "kvm/kvm-ipc.h"
2 #include "kvm/rwsem.h"
3 #include "kvm/read-write.h"
4 #include "kvm/util.h"
5 
6 #include <sys/epoll.h>
7 #include <sys/un.h>
8 #include <sys/types.h>
9 #include <sys/socket.h>
10 #include <sys/eventfd.h>
11 
12 struct kvm_ipc_head {
13 	u32 type;
14 	u32 len;
15 };
16 
17 #define KVM_IPC_MAX_MSGS 16
18 
19 static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg);
20 static DECLARE_RWSEM(msgs_rwlock);
21 static int epoll_fd, server_fd, stop_fd;
22 static pthread_t thread;
23 
24 int kvm_ipc__register_handler(u32 type, void (*cb)(int fd, u32 type, u32 len, u8 *msg))
25 {
26 	if (type >= KVM_IPC_MAX_MSGS)
27 		return -ENOSPC;
28 
29 	down_write(&msgs_rwlock);
30 	msgs[type] = cb;
31 	up_write(&msgs_rwlock);
32 
33 	return 0;
34 }
35 
36 int kvm_ipc__send(int fd, u32 type)
37 {
38 	struct kvm_ipc_head head = {.type = type, .len = 0,};
39 
40 	if (write_in_full(fd, &head, sizeof(head)) < 0)
41 		return -1;
42 
43 	return 0;
44 }
45 
46 int kvm_ipc__send_msg(int fd, u32 type, u32 len, u8 *msg)
47 {
48 	struct kvm_ipc_head head = {.type = type, .len = len,};
49 
50 	if (write_in_full(fd, &head, sizeof(head)) < 0)
51 		return -1;
52 
53 	if (write_in_full(fd, msg, len) < 0)
54 		return -1;
55 
56 	return 0;
57 }
58 
59 static int kvm_ipc__handle(int fd, u32 type, u32 len, u8 *data)
60 {
61 	void (*cb)(int fd, u32 type, u32 len, u8 *msg);
62 
63 	if (type >= KVM_IPC_MAX_MSGS)
64 		return -ENOSPC;
65 
66 	down_read(&msgs_rwlock);
67 	cb = msgs[type];
68 	up_read(&msgs_rwlock);
69 
70 	if (cb == NULL) {
71 		pr_warning("No device handles type %u\n", type);
72 		return -ENODEV;
73 	}
74 
75 	cb(fd, type, len, data);
76 
77 	return 0;
78 }
79 
80 static int kvm_ipc__new_conn(int fd)
81 {
82 	int client;
83 	struct epoll_event ev;
84 
85 	client = accept(fd, NULL, NULL);
86 	if (client < 0)
87 		return -1;
88 
89 	ev.events = EPOLLIN | EPOLLRDHUP;
90 	ev.data.fd = client;
91 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client, &ev) < 0) {
92 		close(client);
93 		return -1;
94 	}
95 
96 	return client;
97 }
98 
99 static void kvm_ipc__close_conn(int fd)
100 {
101 	epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
102 	close(fd);
103 }
104 
105 static int kvm_ipc__receive(int fd)
106 {
107 	struct kvm_ipc_head head;
108 	u8 *msg = NULL;
109 	u32 n;
110 
111 	n = read(fd, &head, sizeof(head));
112 	if (n != sizeof(head))
113 		goto done;
114 
115 	msg = malloc(head.len);
116 	if (msg == NULL)
117 		goto done;
118 
119 	n = read_in_full(fd, msg, head.len);
120 	if (n != head.len)
121 		goto done;
122 
123 	kvm_ipc__handle(fd, head.type, head.len, msg);
124 
125 	return 0;
126 
127 done:
128 	free(msg);
129 	return -1;
130 }
131 
132 static void *kvm_ipc__thread(void *param)
133 {
134 	struct epoll_event event;
135 
136 	for (;;) {
137 		int nfds;
138 
139 		nfds = epoll_wait(epoll_fd, &event, 1, -1);
140 		if (nfds > 0) {
141 			int fd = event.data.fd;
142 
143 			if (fd == stop_fd && event.events & EPOLLIN) {
144 				break;
145 			} else if (fd == server_fd) {
146 				int client, r;
147 
148 				client = kvm_ipc__new_conn(fd);
149 				/*
150 				 * Handle multiple IPC cmd at a time
151 				 */
152 				do {
153 					r = kvm_ipc__receive(client);
154 				} while	(r == 0);
155 
156 			} else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) {
157 				kvm_ipc__close_conn(fd);
158 			} else {
159 				kvm_ipc__receive(fd);
160 			}
161 		}
162 	}
163 
164 	return NULL;
165 }
166 
167 int kvm_ipc__start(int sock)
168 {
169 	int ret;
170 	struct epoll_event ev = {0};
171 
172 	server_fd = sock;
173 
174 	epoll_fd = epoll_create(KVM_IPC_MAX_MSGS);
175 	if (epoll_fd < 0) {
176 		ret = epoll_fd;
177 		goto err;
178 	}
179 
180 	ev.events = EPOLLIN | EPOLLET;
181 	ev.data.fd = sock;
182 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0) {
183 		pr_err("Failed starting IPC thread");
184 		ret = -EFAULT;
185 		goto err_epoll;
186 	}
187 
188 	stop_fd = eventfd(0, 0);
189 	if (stop_fd < 0) {
190 		ret = stop_fd;
191 		goto err_epoll;
192 	}
193 
194 	ev.events = EPOLLIN | EPOLLET;
195 	ev.data.fd = stop_fd;
196 	if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stop_fd, &ev) < 0) {
197 		pr_err("Failed adding stop event to epoll");
198 		ret = -EFAULT;
199 		goto err_stop;
200 	}
201 
202 	if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0) {
203 		pr_err("Failed starting IPC thread");
204 		ret = -EFAULT;
205 		goto err_stop;
206 	}
207 
208 	return 0;
209 
210 err_stop:
211 	close(stop_fd);
212 err_epoll:
213 	close(epoll_fd);
214 err:
215 	return ret;
216 }
217 
218 int kvm_ipc__stop(void)
219 {
220 	u64 val = 1;
221 	int ret;
222 
223 	ret = write(stop_fd, &val, sizeof(val));
224 	if (ret < 0)
225 		return ret;
226 
227 	close(server_fd);
228 	close(epoll_fd);
229 
230 	return ret;
231 }
232