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 124b1addaeSSasha Levin #define KVM_IPC_MAX_MSGS 16 134b1addaeSSasha Levin 144b1addaeSSasha Levin static void (*msgs[KVM_IPC_MAX_MSGS])(int fd, u32 type, u32 len, u8 *msg); 154b1addaeSSasha Levin static DECLARE_RWSEM(msgs_rwlock); 16c733c80bSSasha Levin static int epoll_fd, server_fd, stop_fd; 17c733c80bSSasha Levin static pthread_t thread; 184b1addaeSSasha Levin 194b1addaeSSasha Levin int kvm_ipc__register_handler(u32 type, void (*cb)(int fd, u32 type, u32 len, u8 *msg)) 204b1addaeSSasha Levin { 214b1addaeSSasha Levin if (type >= KVM_IPC_MAX_MSGS) 224b1addaeSSasha Levin return -ENOSPC; 234b1addaeSSasha Levin 244b1addaeSSasha Levin down_write(&msgs_rwlock); 254b1addaeSSasha Levin msgs[type] = cb; 264b1addaeSSasha Levin up_write(&msgs_rwlock); 274b1addaeSSasha Levin 284b1addaeSSasha Levin return 0; 294b1addaeSSasha Levin } 304b1addaeSSasha Levin 31*44a56bfdSLai Jiangshan static int kvm_ipc__handle(int fd, u32 type, u32 len, u8 *data) 324b1addaeSSasha Levin { 334b1addaeSSasha Levin void (*cb)(int fd, u32 type, u32 len, u8 *msg); 344b1addaeSSasha Levin 35*44a56bfdSLai Jiangshan if (type >= KVM_IPC_MAX_MSGS) 364b1addaeSSasha Levin return -ENOSPC; 374b1addaeSSasha Levin 384b1addaeSSasha Levin down_read(&msgs_rwlock); 39*44a56bfdSLai Jiangshan cb = msgs[type]; 404b1addaeSSasha Levin up_read(&msgs_rwlock); 414b1addaeSSasha Levin 424b1addaeSSasha Levin if (cb == NULL) { 43*44a56bfdSLai Jiangshan pr_warning("No device handles type %u\n", type); 444b1addaeSSasha Levin return -ENODEV; 454b1addaeSSasha Levin } 464b1addaeSSasha Levin 47*44a56bfdSLai Jiangshan cb(fd, type, len, data); 484b1addaeSSasha Levin 494b1addaeSSasha Levin return 0; 504b1addaeSSasha Levin } 514b1addaeSSasha Levin 524b1addaeSSasha Levin static int kvm_ipc__new_conn(int fd) 534b1addaeSSasha Levin { 544b1addaeSSasha Levin int client; 554b1addaeSSasha Levin struct epoll_event ev; 564b1addaeSSasha Levin 574b1addaeSSasha Levin client = accept(fd, NULL, NULL); 584b1addaeSSasha Levin if (client < 0) 594b1addaeSSasha Levin return -1; 604b1addaeSSasha Levin 614b1addaeSSasha Levin ev.events = EPOLLIN | EPOLLRDHUP; 624b1addaeSSasha Levin ev.data.fd = client; 634b1addaeSSasha Levin if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client, &ev) < 0) { 644b1addaeSSasha Levin close(client); 654b1addaeSSasha Levin return -1; 664b1addaeSSasha Levin } 674b1addaeSSasha Levin 684b1addaeSSasha Levin return client; 694b1addaeSSasha Levin } 704b1addaeSSasha Levin 714b1addaeSSasha Levin static void kvm_ipc__close_conn(int fd) 724b1addaeSSasha Levin { 734b1addaeSSasha Levin epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); 744b1addaeSSasha Levin close(fd); 754b1addaeSSasha Levin } 764b1addaeSSasha Levin 774b1addaeSSasha Levin static void kvm_ipc__new_data(int fd) 784b1addaeSSasha Levin { 794b1addaeSSasha Levin struct kvm_ipc_msg *msg; 804b1addaeSSasha Levin u32 n; 814b1addaeSSasha Levin 824b1addaeSSasha Levin msg = malloc(sizeof(*msg)); 834b1addaeSSasha Levin if (msg == NULL) 844b1addaeSSasha Levin goto done; 854b1addaeSSasha Levin 864b1addaeSSasha Levin n = read(fd, msg, sizeof(*msg)); 874b1addaeSSasha Levin if (n != sizeof(*msg)) 884b1addaeSSasha Levin goto done; 894b1addaeSSasha Levin 904b1addaeSSasha Levin msg = realloc(msg, sizeof(*msg) + msg->len); 914b1addaeSSasha Levin if (msg == NULL) 924b1addaeSSasha Levin goto done; 934b1addaeSSasha Levin 944b1addaeSSasha Levin n = read_in_full(fd, msg->data, msg->len); 954b1addaeSSasha Levin if (n != msg->len) 964b1addaeSSasha Levin goto done; 974b1addaeSSasha Levin 98*44a56bfdSLai Jiangshan kvm_ipc__handle(fd, msg->type, msg->len, msg->data); 994b1addaeSSasha Levin 1004b1addaeSSasha Levin done: 1014b1addaeSSasha Levin free(msg); 1024b1addaeSSasha Levin } 1034b1addaeSSasha Levin 1044b1addaeSSasha Levin static void *kvm_ipc__thread(void *param) 1054b1addaeSSasha Levin { 1064b1addaeSSasha Levin struct epoll_event event; 1074b1addaeSSasha Levin 1084b1addaeSSasha Levin for (;;) { 1094b1addaeSSasha Levin int nfds; 1104b1addaeSSasha Levin 1114b1addaeSSasha Levin nfds = epoll_wait(epoll_fd, &event, 1, -1); 1124b1addaeSSasha Levin if (nfds > 0) { 1134b1addaeSSasha Levin int fd = event.data.fd; 1144b1addaeSSasha Levin 11547f72b90SSasha Levin if (fd == stop_fd && event.events & EPOLLIN) { 116c733c80bSSasha Levin break; 117c733c80bSSasha Levin } else if (fd == server_fd) { 1184b1addaeSSasha Levin int client; 1194b1addaeSSasha Levin 1204b1addaeSSasha Levin client = kvm_ipc__new_conn(fd); 1214b1addaeSSasha Levin kvm_ipc__new_data(client); 1224b1addaeSSasha Levin } else if (event.events && (EPOLLERR | EPOLLRDHUP | EPOLLHUP)) { 1234b1addaeSSasha Levin kvm_ipc__close_conn(fd); 1244b1addaeSSasha Levin } else { 1254b1addaeSSasha Levin kvm_ipc__new_data(fd); 1264b1addaeSSasha Levin } 1274b1addaeSSasha Levin } 1284b1addaeSSasha Levin } 1294b1addaeSSasha Levin 1304b1addaeSSasha Levin return NULL; 1314b1addaeSSasha Levin } 1324b1addaeSSasha Levin 1334b1addaeSSasha Levin int kvm_ipc__start(int sock) 1344b1addaeSSasha Levin { 13521b3c2c0SSasha Levin struct epoll_event ev = {0}; 1364b1addaeSSasha Levin 1374b1addaeSSasha Levin server_fd = sock; 1384b1addaeSSasha Levin 1394b1addaeSSasha Levin epoll_fd = epoll_create(KVM_IPC_MAX_MSGS); 1404b1addaeSSasha Levin 14147f72b90SSasha Levin ev.events = EPOLLIN | EPOLLET; 1424b1addaeSSasha Levin ev.data.fd = sock; 1434b1addaeSSasha Levin if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sock, &ev) < 0) 1444b1addaeSSasha Levin die("Failed starting IPC thread"); 1454b1addaeSSasha Levin 146c733c80bSSasha Levin stop_fd = eventfd(0, 0); 14747f72b90SSasha Levin ev.events = EPOLLIN | EPOLLET; 148c733c80bSSasha Levin ev.data.fd = stop_fd; 149c733c80bSSasha Levin if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, stop_fd, &ev) < 0) 150c733c80bSSasha Levin die("Failed adding stop event to epoll"); 151c733c80bSSasha Levin 1524b1addaeSSasha Levin if (pthread_create(&thread, NULL, kvm_ipc__thread, NULL) != 0) 1534b1addaeSSasha Levin die("Failed starting IPC thread"); 1544b1addaeSSasha Levin 1554b1addaeSSasha Levin return 0; 1564b1addaeSSasha Levin } 157c733c80bSSasha Levin 158c733c80bSSasha Levin int kvm_ipc__stop(void) 159c733c80bSSasha Levin { 160c733c80bSSasha Levin u64 val = 1; 161c733c80bSSasha Levin int ret; 162c733c80bSSasha Levin 163c733c80bSSasha Levin ret = write(stop_fd, &val, sizeof(val)); 164c733c80bSSasha Levin if (ret < 0) 165c733c80bSSasha Levin return ret; 166c733c80bSSasha Levin 167c733c80bSSasha Levin close(server_fd); 168c733c80bSSasha Levin close(epoll_fd); 169c733c80bSSasha Levin 170c733c80bSSasha Levin return ret; 171c733c80bSSasha Levin } 172