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)) != sizeof(head)) 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)) != sizeof(head)) 51 return -1; 52 53 if (write_in_full(fd, msg, len) != len) 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 struct epoll_event ev = {0}; 170 171 server_fd = sock; 172 173 epoll_fd = epoll_create(KVM_IPC_MAX_MSGS); 174 175 ev.events = EPOLLIN | EPOLLET; 176 ev.data.fd = sock; 177 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0) 178 die("Failed starting IPC thread"); 179 180 stop_fd = eventfd(0, 0); 181 ev.events = EPOLLIN | EPOLLET; 182 ev.data.fd = stop_fd; 183 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stop_fd, &ev) < 0) 184 die("Failed adding stop event to epoll"); 185 186 if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0) 187 die("Failed starting IPC thread"); 188 189 return 0; 190 } 191 192 int kvm_ipc__stop(void) 193 { 194 u64 val = 1; 195 int ret; 196 197 ret = write(stop_fd, &val, sizeof(val)); 198 if (ret < 0) 199 return ret; 200 201 close(server_fd); 202 close(epoll_fd); 203 204 return ret; 205 } 206