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