From 6a6e53f7280facc0cf47f2a0f43d12562c1ae5d7 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Thu, 28 Dec 2017 15:30:03 +1100 Subject: [PATCH] [client] removed the dependency on the ivshmem-server Since we do not use IRQs anymore we can use the ivshmem-plain device which doesn't need the ivshmem-server. The QEMU arguments now should be as follows: -device ivshmem-plain,memdev=ivshmem -object memory-backend-file,id=ivshmem,share=on,mem-path=/dev/shm/looking-glass,size=32M Obviously adjusting the memory size as required. It is suggested that the shared memory file be created before the guest is started with the appropriate permissions, for example: touch /dev/shm/looking-glass chown user:kvm /dev/shm/looking-glass chmod 660 /dev/shm/looking-glass --- client/Makefile | 1 - client/ivshmem/ivshmem.c | 548 --------------------------------------- client/ivshmem/ivshmem.h | 40 --- client/main.c | 89 +++---- 4 files changed, 43 insertions(+), 635 deletions(-) delete mode 100644 client/ivshmem/ivshmem.c delete mode 100644 client/ivshmem/ivshmem.h diff --git a/client/Makefile b/client/Makefile index 9218f57b..f8bd485e 100644 --- a/client/Makefile +++ b/client/Makefile @@ -17,7 +17,6 @@ CFLAGS += -DBUILD_VERSION='"$(shell git describe --always --long --dirty --abbr OBJS = main.o \ lg-renderer.o \ spice/spice.o \ - ivshmem/ivshmem.o \ renderers/opengl.o # renderers/opengl-basic.o diff --git a/client/ivshmem/ivshmem.c b/client/ivshmem/ivshmem.c deleted file mode 100644 index 53337c1c..00000000 --- a/client/ivshmem/ivshmem.c +++ /dev/null @@ -1,548 +0,0 @@ -/* -Looking Glass - KVM FrameRelay (KVMFR) Client -Copyright (C) 2017 Geoffrey McRae -https://looking-glass.hostfission.com - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "ivshmem.h" -#include "debug.h" - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include - -#define MAX_IRQS 32 - -struct IVSHMEMServer -{ - int64_t version; - int64_t clientID; - int sharedFD; - - int irqs[MAX_IRQS]; - int irqCount; -}; - -struct IVSHMEMClient -{ - uint16_t clientID; - - int irqs[MAX_IRQS]; - int irqCount; - - struct IVSHMEMClient * last; - struct IVSHMEMClient * next; -}; - -struct IVSHMEM -{ - bool connected; - bool shutdown; - int socket; - struct IVSHMEMServer server; - struct IVSHMEMClient * clients; - - off_t mapSize; - void * map; -}; - -struct IVSHMEM ivshmem = -{ - .connected = false, - .shutdown = false, - .socket = -1 -}; - -// ============================================================================ -// internal functions - -void ivshmem_cleanup(); -bool ivshmem_read(void * buffer, const ssize_t size); -bool ivshmem_read_msg(int64_t * index, int *fd); -struct IVSHMEMClient * ivshmem_get_client(uint16_t clientID); -void ivshmem_remove_client(struct IVSHMEMClient * client); - -// ============================================================================ - -bool ivshmem_connect(const char * unix_socket) -{ - ivshmem.shutdown = false; - ivshmem.socket = socket(AF_UNIX, SOCK_STREAM, 0); - if (ivshmem.socket < 0) - { - DEBUG_ERROR("socket creation failed"); - return false; - } - - struct sockaddr_un addr; - addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, unix_socket, sizeof(addr.sun_path)); - - if (connect(ivshmem.socket, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0) - { - DEBUG_ERROR("socket connect failed"); - ivshmem_cleanup(); - return false; - } - - ivshmem.connected = true; - - if (!ivshmem_read(&ivshmem.server.version, sizeof(ivshmem.server.version))) - { - DEBUG_ERROR("read protocol version failed"); - ivshmem_cleanup(); - return false; - } - - if (ivshmem.server.version != 0) - { - DEBUG_ERROR("unsupported protocol version %ld", ivshmem.server.version); - ivshmem_cleanup(); - return false; - } - - if (!ivshmem_read(&ivshmem.server.clientID, sizeof(ivshmem.server.clientID))) - { - DEBUG_ERROR("read client id failed"); - ivshmem_cleanup(); - return false; - } - - DEBUG_PROTO("Protocol : %ld", ivshmem.server.version ); - DEBUG_PROTO("Client ID: %ld", ivshmem.server.clientID); - - if (!ivshmem_read_msg(NULL, &ivshmem.server.sharedFD)) - { - DEBUG_ERROR("failed to read shared memory file descriptor"); - ivshmem_cleanup(); - return false; - } - - struct stat stat; - if (fstat(ivshmem.server.sharedFD, &stat) != 0) - { - DEBUG_ERROR("failed to stat shared FD"); - ivshmem_cleanup(); - return false; - } - - ivshmem.mapSize = stat.st_size; - - DEBUG_INFO("RAM Size : %ld", ivshmem.mapSize); - ivshmem.map = mmap( - NULL, - stat.st_size, - PROT_READ | PROT_WRITE, - MAP_SHARED, - ivshmem.server.sharedFD, - 0); - - if (!ivshmem.map) - { - DEBUG_ERROR("failed to map memory"); - ivshmem_cleanup(); - return false; - } - - return true; -} - -// ============================================================================ - -void ivshmem_cleanup() -{ - struct IVSHMEMClient * client, * next; - client = ivshmem.clients; - while(client) - { - for(int i = 0; i < client->irqCount; ++i) - close(client->irqs[i]); - - next = client->next; - free(client); - client = next; - } - ivshmem.clients = NULL; - - for(int i = 0; i < ivshmem.server.irqCount; ++i) - close(ivshmem.server.irqs[i]); - ivshmem.server.irqCount = 0; - - if (ivshmem.map) - munmap(ivshmem.map, ivshmem.mapSize); - ivshmem.map = NULL; - ivshmem.mapSize = 0; - - if (ivshmem.socket >= 0) - { - ivshmem.shutdown = true; - shutdown(ivshmem.socket, SHUT_RDWR); - close(ivshmem.socket); - ivshmem.socket = -1; - } - - ivshmem.connected = false; -} - -// ============================================================================ - -void ivshmem_disconnect() -{ - if (!ivshmem.connected) - { - DEBUG_WARN("socket not connected"); - return; - } - - ivshmem_cleanup(); -} - -// ============================================================================ - -bool ivshmem_read(void * buffer, const ssize_t size) -{ - if (!ivshmem.connected) - { - DEBUG_ERROR("not connected"); - return false; - } - - ssize_t len = read(ivshmem.socket, buffer, size); - if (len != size) - { - DEBUG_ERROR("incomplete read"); - return false; - } - - return true; -} - -// ============================================================================ - -bool ivshmem_read_msg(int64_t * index, int * fd) -{ - if (!ivshmem.connected) - { - DEBUG_ERROR("not connected"); - return false; - } - - struct msghdr msg; - struct iovec iov[1]; - union { - struct cmsghdr cmsg; - char control[CMSG_SPACE(sizeof(int))]; - } msg_control; - - int64_t tmp; - if (!index) - index = &tmp; - - iov[0].iov_base = index; - iov[0].iov_len = sizeof(*index); - - memset(&msg, 0, sizeof(msg)); - msg.msg_iov = iov; - msg.msg_iovlen = 1; - msg.msg_control = &msg_control; - msg.msg_controllen = sizeof(msg_control); - - int ret = recvmsg(ivshmem.socket, &msg, 0); - if (ret < sizeof(*index)) - { - if (!ivshmem.shutdown) - DEBUG_ERROR("failed to read message\n"); - return false; - } - - if (ret == 0) - { - if (!ivshmem.shutdown) - DEBUG_ERROR("lost connetion to server\n"); - return false; - } - - if (!fd) - return true; - - *fd = -1; - struct cmsghdr *cmsg; - for(cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) - { - if (cmsg->cmsg_len != CMSG_LEN(sizeof(int)) || - cmsg->cmsg_level != SOL_SOCKET || - cmsg->cmsg_type != SCM_RIGHTS) - { - continue; - } - - memcpy(fd, CMSG_DATA(cmsg), sizeof(int)); - } - - return true; -} - -// ============================================================================ - -uint16_t ivshmem_get_id() -{ - if (!ivshmem.connected) - { - DEBUG_ERROR("not connected"); - return -1; - } - - return ivshmem.server.clientID; -} - -// ============================================================================ - -void * ivshmem_get_map() -{ - if (!ivshmem.connected) - { - DEBUG_ERROR("not connected"); - return NULL; - } - - if (!ivshmem.map) - { - DEBUG_ERROR("not mapped"); - return NULL; - } - - return ivshmem.map; -} - -// ============================================================================ - -size_t ivshmem_get_map_size() -{ - if (!ivshmem.connected) - { - DEBUG_ERROR("not connected"); - return 0; - } - - if (!ivshmem.map) - { - DEBUG_ERROR("not mapped"); - return 0; - } - - return ivshmem.mapSize; -} - -// ============================================================================ - -struct IVSHMEMClient * ivshmem_get_client(uint16_t clientID) -{ - struct IVSHMEMClient * client = NULL; - - if (ivshmem.clients == NULL) - { - client = (struct IVSHMEMClient *)malloc(sizeof(struct IVSHMEMClient)); - client->clientID = clientID; - client->last = NULL; - client->next = NULL; - client->irqCount = 0; - ivshmem.clients = client; - return client; - } - - client = ivshmem.clients; - while(client) - { - if (client->clientID == clientID) - return client; - client = client->next; - } - - client = (struct IVSHMEMClient *)malloc(sizeof(struct IVSHMEMClient)); - client->clientID = clientID; - client->last = NULL; - client->next = ivshmem.clients; - client->irqCount = 0; - client->next->last = client; - ivshmem.clients = client; - - return client; -} - -// ============================================================================ - -void ivshmem_remove_client(struct IVSHMEMClient * client) -{ - if (client->last) - client->last->next = client->next; - - if (client->next) - client->next->last = client->last; - - if (ivshmem.clients == client) - ivshmem.clients = client->next; - - free(client); -} - -// ============================================================================ - -bool ivshmem_process() -{ - int64_t index; - int fd; - - if (!ivshmem_read_msg(&index, &fd)) - { - if (!ivshmem.shutdown) - DEBUG_ERROR("failed to read message"); - return false; - } - - if (index == -1) - { - DEBUG_ERROR("invalid index -1"); - return false; - } - - if (index > 0xFFFF) - { - DEBUG_ERROR("invalid index > 0xFFFF"); - return false; - } - - if (index == ivshmem.server.clientID) - { - if (fd == -1) - { - DEBUG_ERROR("server sent disconnect"); - return false; - } - - if (ivshmem.server.irqCount == MAX_IRQS) - { - DEBUG_WARN("maximum IRQs reached, closing extra"); - close(fd); - return true; - } - - ivshmem.server.irqs[ivshmem.server.irqCount++] = fd; - return true; - } - - struct IVSHMEMClient * client = ivshmem_get_client(index); - if (!client) - { - DEBUG_ERROR("failed to get/create client record"); - return false; - } - - if (fd == -1) - { - DEBUG_PROTO("remove client %u", client->clientID); - ivshmem_remove_client(client); - return true; - } - - if (client->irqCount == MAX_IRQS) - { - DEBUG_WARN("maximum client IRQs reached, closing extra"); - close(fd); - return true; - } - - client->irqs[client->irqCount++] = fd; - return true; -} - -// ============================================================================ - -enum IVSHMEMWaitResult ivshmem_wait_irq(uint16_t vector, unsigned int timeout) -{ - if (vector > ivshmem.server.irqCount - 1) - return false; - - int fd = ivshmem.server.irqs[vector]; - fd_set fds; - FD_ZERO(&fds); - FD_SET(fd, &fds); - - struct timeval tv; - tv.tv_sec = timeout / 1000000L; - tv.tv_usec = timeout % 1000000L; - - while(true) - { - int ret = select(fd+1, &fds, NULL, NULL, &tv); - if (ret < 0) - { - if (errno == EINTR) - continue; - - DEBUG_ERROR("select error"); - break; - } - - if (ret == 0) - return IVSHMEM_WAIT_RESULT_TIMEOUT; - - if (FD_ISSET(fd, &fds)) - { - uint64_t kick; - int unused = read(fd, &kick, sizeof(kick)); - (void)unused; - return IVSHMEM_WAIT_RESULT_OK; - } - } - - return IVSHMEM_WAIT_RESULT_ERROR; -} - -// ============================================================================ - -bool ivshmem_kick_irq(uint16_t clientID, uint16_t vector) -{ - struct IVSHMEMClient * client = ivshmem_get_client(clientID); - if (!client) - { - DEBUG_ERROR("invalid client"); - return false; - } - - if (vector > client->irqCount - 1) - { - DEBUG_ERROR("invalid vector for client"); - return false; - } - - int fd = client->irqs[vector]; - uint64_t kick = ivshmem.server.clientID; - if (write(fd, &kick, sizeof(kick)) == sizeof(kick)) - return true; - - DEBUG_ERROR("failed to send kick"); - return false; -} diff --git a/client/ivshmem/ivshmem.h b/client/ivshmem/ivshmem.h deleted file mode 100644 index 9dddb9fd..00000000 --- a/client/ivshmem/ivshmem.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -Looking Glass - KVM FrameRelay (KVMFR) Client -Copyright (C) 2017 Geoffrey McRae -https://looking-glass.hostfission.com - -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU General Public License as published by the Free Software -Foundation; either version 2 of the License, or (at your option) any later -version. - -This program is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A -PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with -this program; if not, write to the Free Software Foundation, Inc., 59 Temple -Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include - -bool ivshmem_connect(const char * unix_socket); -void ivshmem_disconnect(); -bool ivshmem_process(); - -uint16_t ivshmem_get_id(); -void * ivshmem_get_map(); -size_t ivshmem_get_map_size(); - -enum IVSHMEMWaitResult -{ - IVSHMEM_WAIT_RESULT_OK, - IVSHMEM_WAIT_RESULT_TIMEOUT, - IVSHMEM_WAIT_RESULT_ERROR -}; - -enum IVSHMEMWaitResult ivshmem_wait_irq(uint16_t vector, unsigned int timeout); -bool ivshmem_kick_irq(uint16_t clientID, uint16_t vector); \ No newline at end of file diff --git a/client/main.c b/client/main.c index 670b7ba3..072468ec 100644 --- a/client/main.c +++ b/client/main.c @@ -25,6 +25,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include #include #include +#include +#include #include #include #include @@ -36,7 +38,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "debug.h" #include "utils.h" #include "KVMFR.h" -#include "ivshmem/ivshmem.h" #include "spice/spice.h" #include "kb.h" @@ -59,6 +60,7 @@ struct AppState void * lgrData; SDL_Window * window; + int shmFD; struct KVMFRHeader * shm; unsigned int shmSize; }; @@ -81,7 +83,7 @@ struct AppParams bool center; int x, y; unsigned int w, h; - const char * ivshmemSocket; + const char * shmFile; bool showFPS; bool useSpice; const char * spiceHost; @@ -108,7 +110,7 @@ struct AppParams params = .y = 0, .w = 1024, .h = 768, - .ivshmemSocket = "/tmp/ivshmem_socket", + .shmFile = "/dev/shm/looking-glass", .showFPS = false, .useSpice = true, .spiceHost = "127.0.0.1", @@ -373,22 +375,6 @@ int frameThread(void * unused) return 0; } -int ivshmemThread(void * arg) -{ - while(state.running) - if (!ivshmem_process()) - { - if (state.running) - { - state.running = false; - DEBUG_ERROR("failed to process ivshmem messages"); - } - break; - } - - return 0; -} - int spiceThread(void * arg) { while(state.running) @@ -625,6 +611,35 @@ void intHandler(int signal) } } +static void * map_memory() +{ + struct stat st; + if (stat(params.shmFile, &st) < 0) + { + DEBUG_ERROR("Failed to stat the shared memory file: %s", params.shmFile); + return NULL; + } + + state.shmSize = st.st_size; + state.shmFD = open(params.shmFile, O_RDWR, (mode_t)0600); + if (state.shmFD < 0) + { + DEBUG_ERROR("Failed to open the shared memory file: %s", params.shmFile); + return NULL; + } + + void * map = mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, state.shmFD, 0); + if (map == MAP_FAILED) + { + DEBUG_ERROR("Failed to map the shared memory file: %s", params.shmFile); + close(state.shmFD); + state.shmFD = 0; + return NULL; + } + + return map; +} + static bool try_renderer(const int index, const LG_RendererParams lgrParams, Uint32 * sdlFlags) { const LG_Renderer *r = LG_Renderers[index]; @@ -814,32 +829,17 @@ int run() SDL_ShowCursor(SDL_DISABLE); } - int shm_fd = 0; - SDL_Thread *t_ivshmem = NULL; SDL_Thread *t_spice = NULL; SDL_Thread *t_event = NULL; while(1) { - if (!ivshmem_connect(params.ivshmemSocket)) - { - DEBUG_ERROR("failed to connect to the ivshmem server"); - break; - } - - if (!(t_ivshmem = SDL_CreateThread(ivshmemThread, "ivshmemThread", NULL))) - { - DEBUG_ERROR("ivshmem create thread failed"); - break; - } - - state.shm = (struct KVMFRHeader *)ivshmem_get_map(); + state.shm = (struct KVMFRHeader *)map_memory(); if (!state.shm) { DEBUG_ERROR("Failed to map memory"); break; } - state.shmSize = ivshmem_get_map_size(); if (params.useSpice) { @@ -914,12 +914,6 @@ int run() if (t_event) SDL_WaitThread(t_event, NULL); - // this needs to happen here to abort any waiting reads - // as ivshmem uses recvmsg which has no timeout - ivshmem_disconnect(); - if (t_ivshmem) - SDL_WaitThread(t_ivshmem, NULL); - if (t_spice) SDL_WaitThread(t_spice, NULL); @@ -932,8 +926,11 @@ int run() if (cursor) SDL_FreeCursor(cursor); - if (shm_fd) - close(shm_fd); + if (state.shm) + { + munmap(state.shm, state.shmSize); + close(state.shmFD); + } TTF_Quit(); SDL_Quit(); @@ -953,7 +950,7 @@ void doHelp(char * app) "\n" " -h Print out this help\n" "\n" - " -f PATH Specify the path to the ivshmem socket [current: %s]\n" + " -f PATH Specify the path to the shared memory file [current: %s]\n" "\n" " -s Disable spice client\n" " -c HOST Specify the spice host [current: %s]\n" @@ -981,7 +978,7 @@ void doHelp(char * app) "\n", app, app, - params.ivshmemSocket, + params.shmFile, params.spiceHost, params.spicePort, params.center ? "center" : x, @@ -1031,7 +1028,7 @@ int main(int argc, char * argv[]) break; case 'f': - params.ivshmemSocket = optarg; + params.shmFile = optarg; continue; case 's':