14b1addaeSSasha Levin #include "kvm/kvm-ipc.h" 24b1addaeSSasha Levin #include "kvm/rwsem.h" 34b1addaeSSasha Levin #include "kvm/read-write.h" 44b1addaeSSasha Levin #include "kvm/util.h" 54b1addaeSSasha Levin 64b1addaeSSasha Levin #include <sys/epoll.h> 74b1addaeSSasha Levin #include <sys/un.h> 84b1addaeSSasha Levin #include <sys/types.h> 94b1addaeSSasha Levin #include <sys/socket.h> 10c733c80bSSasha Levin #include <sys/eventfd.h> 114b1addaeSSasha Levin 12a9aae6c5SLai Jiangshan struct kvm_ipc_head { 13a9aae6c5SLai Jiangshan u32 type; 14a9aae6c5SLai Jiangshan u32 len; 15a9aae6c5SLai Jiangshan }; 16a9aae6c5SLai Jiangshan 174b1addaeSSasha Levin #define KVM_IPC_MAX_MSGS 16 184b1addaeSSasha Levin 194b1addaeSSasha Levin static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg); 204b1addaeSSasha Levin static DECLARE_RWSEM(msgs_rwlock); 21c733c80bSSasha Levin static int epoll_fd, server_fd, stop_fd; 22c733c80bSSasha Levin static pthread_t thread; 234b1addaeSSasha Levin 244b1addaeSSasha Levin int kvm_ipc__register_handler(u32 type, void (*cb)(int fd, u32 type, u32 len, u8 *msg)) 254b1addaeSSasha Levin { 264b1addaeSSasha Levin if (type >= KVM_IPC_MAX_MSGS) 274b1addaeSSasha Levin return -ENOSPC; 284b1addaeSSasha Levin 294b1addaeSSasha Levin down_write(&msgs_rwlock); 304b1addaeSSasha Levin msgs[type] = cb; 314b1addaeSSasha Levin up_write(&msgs_rwlock); 324b1addaeSSasha Levin 334b1addaeSSasha Levin return 0; 344b1addaeSSasha Levin } 354b1addaeSSasha Levin 3650dc18aeSLai Jiangshan int kvm_ipc__send(int fd, u32 type) 3750dc18aeSLai Jiangshan { 3850dc18aeSLai Jiangshan struct kvm_ipc_head head = {.type = type, .len = 0,}; 3950dc18aeSLai Jiangshan 40ca088268SAsias He if (write_in_full(fd, &head, sizeof(head)) < 0) 4150dc18aeSLai Jiangshan return -1; 4250dc18aeSLai Jiangshan 4350dc18aeSLai Jiangshan return 0; 4450dc18aeSLai Jiangshan } 4550dc18aeSLai Jiangshan 4650dc18aeSLai Jiangshan int kvm_ipc__send_msg(int fd, u32 type, u32 len, u8 *msg) 4750dc18aeSLai Jiangshan { 4850dc18aeSLai Jiangshan struct kvm_ipc_head head = {.type = type, .len = len,}; 4950dc18aeSLai Jiangshan 50ca088268SAsias He if (write_in_full(fd, &head, sizeof(head)) < 0) 5150dc18aeSLai Jiangshan return -1; 5250dc18aeSLai Jiangshan 53fd5b45a1SAsias He if (write_in_full(fd, msg, len) < 0) 5450dc18aeSLai Jiangshan return -1; 5550dc18aeSLai Jiangshan 5650dc18aeSLai Jiangshan return 0; 5750dc18aeSLai Jiangshan } 5850dc18aeSLai Jiangshan 5944a56bfdSLai Jiangshan static int kvm_ipc__handle(int fd, u32 type, u32 len, u8 *data) 604b1addaeSSasha Levin { 614b1addaeSSasha Levin void (*cb)(int fd, u32 type, u32 len, u8 *msg); 624b1addaeSSasha Levin 6344a56bfdSLai Jiangshan if (type >= KVM_IPC_MAX_MSGS) 644b1addaeSSasha Levin return -ENOSPC; 654b1addaeSSasha Levin 664b1addaeSSasha Levin down_read(&msgs_rwlock); 6744a56bfdSLai Jiangshan cb = msgs[type]; 684b1addaeSSasha Levin up_read(&msgs_rwlock); 694b1addaeSSasha Levin 704b1addaeSSasha Levin if (cb == NULL) { 7144a56bfdSLai Jiangshan pr_warning("No device handles type %u\n", type); 724b1addaeSSasha Levin return -ENODEV; 734b1addaeSSasha Levin } 744b1addaeSSasha Levin 7544a56bfdSLai Jiangshan cb(fd, type, len, data); 764b1addaeSSasha Levin 774b1addaeSSasha Levin return 0; 784b1addaeSSasha Levin } 794b1addaeSSasha Levin 804b1addaeSSasha Levin static int kvm_ipc__new_conn(int fd) 814b1addaeSSasha Levin { 824b1addaeSSasha Levin int client; 834b1addaeSSasha Levin struct epoll_event ev; 844b1addaeSSasha Levin 854b1addaeSSasha Levin client = accept(fd, NULL, NULL); 864b1addaeSSasha Levin if (client < 0) 874b1addaeSSasha Levin return -1; 884b1addaeSSasha Levin 894b1addaeSSasha Levin ev.events = EPOLLIN | EPOLLRDHUP; 904b1addaeSSasha Levin ev.data.fd = client; 914b1addaeSSasha Levin if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client, &ev) < 0) { 924b1addaeSSasha Levin close(client); 934b1addaeSSasha Levin return -1; 944b1addaeSSasha Levin } 954b1addaeSSasha Levin 964b1addaeSSasha Levin return client; 974b1addaeSSasha Levin } 984b1addaeSSasha Levin 994b1addaeSSasha Levin static void kvm_ipc__close_conn(int fd) 1004b1addaeSSasha Levin { 1014b1addaeSSasha Levin epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); 1024b1addaeSSasha Levin close(fd); 1034b1addaeSSasha Levin } 1044b1addaeSSasha Levin 1058e463c62SAsias He static int kvm_ipc__receive(int fd) 1064b1addaeSSasha Levin { 107a9aae6c5SLai Jiangshan struct kvm_ipc_head head; 108a9aae6c5SLai Jiangshan u8 *msg = NULL; 1094b1addaeSSasha Levin u32 n; 1104b1addaeSSasha Levin 111a9aae6c5SLai Jiangshan n = read(fd, &head, sizeof(head)); 112a9aae6c5SLai Jiangshan if (n != sizeof(head)) 113a9aae6c5SLai Jiangshan goto done; 114a9aae6c5SLai Jiangshan 115a9aae6c5SLai Jiangshan msg = malloc(head.len); 1164b1addaeSSasha Levin if (msg == NULL) 1174b1addaeSSasha Levin goto done; 1184b1addaeSSasha Levin 119a9aae6c5SLai Jiangshan n = read_in_full(fd, msg, head.len); 120a9aae6c5SLai Jiangshan if (n != head.len) 1214b1addaeSSasha Levin goto done; 1224b1addaeSSasha Levin 123a9aae6c5SLai Jiangshan kvm_ipc__handle(fd, head.type, head.len, msg); 1244b1addaeSSasha Levin 1258e463c62SAsias He return 0; 1268e463c62SAsias He 1274b1addaeSSasha Levin done: 1284b1addaeSSasha Levin free(msg); 1298e463c62SAsias He return -1; 1304b1addaeSSasha Levin } 1314b1addaeSSasha Levin 1324b1addaeSSasha Levin static void *kvm_ipc__thread(void *param) 1334b1addaeSSasha Levin { 1344b1addaeSSasha Levin struct epoll_event event; 1354b1addaeSSasha Levin 1364b1addaeSSasha Levin for (;;) { 1374b1addaeSSasha Levin int nfds; 1384b1addaeSSasha Levin 1394b1addaeSSasha Levin nfds = epoll_wait(epoll_fd, &event, 1, -1); 1404b1addaeSSasha Levin if (nfds > 0) { 1414b1addaeSSasha Levin int fd = event.data.fd; 1424b1addaeSSasha Levin 14347f72b90SSasha Levin if (fd == stop_fd && event.events & EPOLLIN) { 144c733c80bSSasha Levin break; 145c733c80bSSasha Levin } else if (fd == server_fd) { 1468e463c62SAsias He int client, r; 1474b1addaeSSasha Levin 1484b1addaeSSasha Levin client = kvm_ipc__new_conn(fd); 1498e463c62SAsias He /* 1508e463c62SAsias He * Handle multiple IPC cmd at a time 1518e463c62SAsias He */ 1528e463c62SAsias He do { 1538e463c62SAsias He r = kvm_ipc__receive(client); 1548e463c62SAsias He } while (r == 0); 1558e463c62SAsias He 1564b1addaeSSasha Levin } else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) { 1574b1addaeSSasha Levin kvm_ipc__close_conn(fd); 1584b1addaeSSasha Levin } else { 159a9aae6c5SLai Jiangshan kvm_ipc__receive(fd); 1604b1addaeSSasha Levin } 1614b1addaeSSasha Levin } 1624b1addaeSSasha Levin } 1634b1addaeSSasha Levin 1644b1addaeSSasha Levin return NULL; 1654b1addaeSSasha Levin } 1664b1addaeSSasha Levin 1674b1addaeSSasha Levin int kvm_ipc__start(int sock) 1684b1addaeSSasha Levin { 169*e21e8ff3SYang Bai int ret; 17021b3c2c0SSasha Levin struct epoll_event ev = {0}; 1714b1addaeSSasha Levin 1724b1addaeSSasha Levin server_fd = sock; 1734b1addaeSSasha Levin 1744b1addaeSSasha Levin epoll_fd = epoll_create(KVM_IPC_MAX_MSGS); 175*e21e8ff3SYang Bai if (epoll_fd < 0) { 176*e21e8ff3SYang Bai ret = epoll_fd; 177*e21e8ff3SYang Bai goto err; 178*e21e8ff3SYang Bai } 1794b1addaeSSasha Levin 18047f72b90SSasha Levin ev.events = EPOLLIN | EPOLLET; 1814b1addaeSSasha Levin ev.data.fd = sock; 182*e21e8ff3SYang Bai if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0) { 183*e21e8ff3SYang Bai pr_err("Failed starting IPC thread"); 184*e21e8ff3SYang Bai ret = -EFAULT; 185*e21e8ff3SYang Bai goto err_epoll; 186*e21e8ff3SYang Bai } 1874b1addaeSSasha Levin 188c733c80bSSasha Levin stop_fd = eventfd(0, 0); 189*e21e8ff3SYang Bai if (stop_fd < 0) { 190*e21e8ff3SYang Bai ret = stop_fd; 191*e21e8ff3SYang Bai goto err_epoll; 192*e21e8ff3SYang Bai } 193*e21e8ff3SYang Bai 19447f72b90SSasha Levin ev.events = EPOLLIN | EPOLLET; 195c733c80bSSasha Levin ev.data.fd = stop_fd; 196*e21e8ff3SYang Bai if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stop_fd, &ev) < 0) { 197*e21e8ff3SYang Bai pr_err("Failed adding stop event to epoll"); 198*e21e8ff3SYang Bai ret = -EFAULT; 199*e21e8ff3SYang Bai goto err_stop; 200*e21e8ff3SYang Bai } 201c733c80bSSasha Levin 202*e21e8ff3SYang Bai if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0) { 203*e21e8ff3SYang Bai pr_err("Failed starting IPC thread"); 204*e21e8ff3SYang Bai ret = -EFAULT; 205*e21e8ff3SYang Bai goto err_stop; 206*e21e8ff3SYang Bai } 2074b1addaeSSasha Levin 2084b1addaeSSasha Levin return 0; 209*e21e8ff3SYang Bai 210*e21e8ff3SYang Bai err_stop: 211*e21e8ff3SYang Bai close(stop_fd); 212*e21e8ff3SYang Bai err_epoll: 213*e21e8ff3SYang Bai close(epoll_fd); 214*e21e8ff3SYang Bai err: 215*e21e8ff3SYang Bai return ret; 2164b1addaeSSasha Levin } 217c733c80bSSasha Levin 218c733c80bSSasha Levin int kvm_ipc__stop(void) 219c733c80bSSasha Levin { 220c733c80bSSasha Levin u64 val = 1; 221c733c80bSSasha Levin int ret; 222c733c80bSSasha Levin 223c733c80bSSasha Levin ret = write(stop_fd, &val, sizeof(val)); 224c733c80bSSasha Levin if (ret < 0) 225c733c80bSSasha Levin return ret; 226c733c80bSSasha Levin 227c733c80bSSasha Levin close(server_fd); 228c733c80bSSasha Levin close(epoll_fd); 229c733c80bSSasha Levin 230c733c80bSSasha Levin return ret; 231c733c80bSSasha Levin } 232