blob: a875e7dcf580964ea56fd2eaf8dc678026e94eb3 [file] [log] [blame]
Sasha Levin4b1adda2011-10-16 21:57:54 +02001#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>
Sasha Levinc733c802011-10-25 13:30:53 +020010#include <sys/eventfd.h>
Sasha Levin4b1adda2011-10-16 21:57:54 +020011
Lai Jiangshana9aae6c2011-12-20 17:08:50 +080012struct kvm_ipc_head {
13 u32 type;
14 u32 len;
15};
16
Sasha Levin4b1adda2011-10-16 21:57:54 +020017#define KVM_IPC_MAX_MSGS 16
18
19static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg);
20static DECLARE_RWSEM(msgs_rwlock);
Sasha Levinc733c802011-10-25 13:30:53 +020021static int epoll_fd, server_fd, stop_fd;
22static pthread_t thread;
Sasha Levin4b1adda2011-10-16 21:57:54 +020023
24int 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
Lai Jiangshan44a56bf2011-12-20 17:08:49 +080036static int kvm_ipc__handle(int fd, u32 type, u32 len, u8 *data)
Sasha Levin4b1adda2011-10-16 21:57:54 +020037{
38 void (*cb)(int fd, u32 type, u32 len, u8 *msg);
39
Lai Jiangshan44a56bf2011-12-20 17:08:49 +080040 if (type >= KVM_IPC_MAX_MSGS)
Sasha Levin4b1adda2011-10-16 21:57:54 +020041 return -ENOSPC;
42
43 down_read(&msgs_rwlock);
Lai Jiangshan44a56bf2011-12-20 17:08:49 +080044 cb = msgs[type];
Sasha Levin4b1adda2011-10-16 21:57:54 +020045 up_read(&msgs_rwlock);
46
47 if (cb == NULL) {
Lai Jiangshan44a56bf2011-12-20 17:08:49 +080048 pr_warning("No device handles type %u\n", type);
Sasha Levin4b1adda2011-10-16 21:57:54 +020049 return -ENODEV;
50 }
51
Lai Jiangshan44a56bf2011-12-20 17:08:49 +080052 cb(fd, type, len, data);
Sasha Levin4b1adda2011-10-16 21:57:54 +020053
54 return 0;
55}
56
57static 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
76static void kvm_ipc__close_conn(int fd)
77{
78 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
79 close(fd);
80}
81
Lai Jiangshana9aae6c2011-12-20 17:08:50 +080082static void kvm_ipc__receive(int fd)
Sasha Levin4b1adda2011-10-16 21:57:54 +020083{
Lai Jiangshana9aae6c2011-12-20 17:08:50 +080084 struct kvm_ipc_head head;
85 u8 *msg = NULL;
Sasha Levin4b1adda2011-10-16 21:57:54 +020086 u32 n;
87
Lai Jiangshana9aae6c2011-12-20 17:08:50 +080088 n = read(fd, &head, sizeof(head));
89 if (n != sizeof(head))
90 goto done;
91
92 msg = malloc(head.len);
Sasha Levin4b1adda2011-10-16 21:57:54 +020093 if (msg == NULL)
94 goto done;
95
Lai Jiangshana9aae6c2011-12-20 17:08:50 +080096 n = read_in_full(fd, msg, head.len);
97 if (n != head.len)
Sasha Levin4b1adda2011-10-16 21:57:54 +020098 goto done;
99
Lai Jiangshana9aae6c2011-12-20 17:08:50 +0800100 kvm_ipc__handle(fd, head.type, head.len, msg);
Sasha Levin4b1adda2011-10-16 21:57:54 +0200101
102done:
103 free(msg);
104}
105
106static 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
Sasha Levin47f72b92011-10-28 11:01:54 +0200117 if (fd == stop_fd && event.events & EPOLLIN) {
Sasha Levinc733c802011-10-25 13:30:53 +0200118 break;
119 } else if (fd == server_fd) {
Sasha Levin4b1adda2011-10-16 21:57:54 +0200120 int client;
121
122 client = kvm_ipc__new_conn(fd);
Lai Jiangshana9aae6c2011-12-20 17:08:50 +0800123 kvm_ipc__receive(client);
Sasha Levin4b1adda2011-10-16 21:57:54 +0200124 } else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) {
125 kvm_ipc__close_conn(fd);
126 } else {
Lai Jiangshana9aae6c2011-12-20 17:08:50 +0800127 kvm_ipc__receive(fd);
Sasha Levin4b1adda2011-10-16 21:57:54 +0200128 }
129 }
130 }
131
132 return NULL;
133}
134
135int kvm_ipc__start(int sock)
136{
Sasha Levin21b3c2c2011-12-06 14:15:30 +0200137 struct epoll_event ev = {0};
Sasha Levin4b1adda2011-10-16 21:57:54 +0200138
139 server_fd = sock;
140
141 epoll_fd = epoll_create(KVM_IPC_MAX_MSGS);
142
Sasha Levin47f72b92011-10-28 11:01:54 +0200143 ev.events = EPOLLIN | EPOLLET;
Sasha Levin4b1adda2011-10-16 21:57:54 +0200144 ev.data.fd = sock;
145 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0)
146 die("Failed starting IPC thread");
147
Sasha Levinc733c802011-10-25 13:30:53 +0200148 stop_fd = eventfd(0, 0);
Sasha Levin47f72b92011-10-28 11:01:54 +0200149 ev.events = EPOLLIN | EPOLLET;
Sasha Levinc733c802011-10-25 13:30:53 +0200150 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
Sasha Levin4b1adda2011-10-16 21:57:54 +0200154 if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0)
155 die("Failed starting IPC thread");
156
157 return 0;
158}
Sasha Levinc733c802011-10-25 13:30:53 +0200159
160int 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}