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