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