From 91274487a979b693c264be35b2ffcb703f0a104e Mon Sep 17 00:00:00 2001 From: "Denis V. Lunev" Date: Mon, 3 Oct 2016 17:01:25 +0300 Subject: [PATCH 1/5] qga: minimal support for fstrim for Windows guests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately, there is no public Windows API to start trimming the filesystem. The only viable way here is to call 'defrag.exe /L' for each volume. This is working since Win8 and Win2k12. Signed-off-by: Denis V. Lunev Signed-off-by: Denis Plotnikov CC: Michael Roth CC: Stefan Weil CC: Marc-André Lureau * check g_utf16_to_utf8() return value for GError handling instead of GError directly (Marc-André) Signed-off-by: Michael Roth --- qga/commands-win32.c | 97 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 94 insertions(+), 3 deletions(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 9c9be12116..19d72b2411 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -840,8 +840,99 @@ static void guest_fsfreeze_cleanup(void) GuestFilesystemTrimResponse * qmp_guest_fstrim(bool has_minimum, int64_t minimum, Error **errp) { - error_setg(errp, QERR_UNSUPPORTED); - return NULL; + GuestFilesystemTrimResponse *resp; + HANDLE handle; + WCHAR guid[MAX_PATH] = L""; + + handle = FindFirstVolumeW(guid, ARRAYSIZE(guid)); + if (handle == INVALID_HANDLE_VALUE) { + error_setg_win32(errp, GetLastError(), "failed to find any volume"); + return NULL; + } + + resp = g_new0(GuestFilesystemTrimResponse, 1); + + do { + GuestFilesystemTrimResult *res; + GuestFilesystemTrimResultList *list; + PWCHAR uc_path; + DWORD char_count = 0; + char *path, *out; + GError *gerr = NULL; + gchar * argv[4]; + + GetVolumePathNamesForVolumeNameW(guid, NULL, 0, &char_count); + + if (GetLastError() != ERROR_MORE_DATA) { + continue; + } + if (GetDriveTypeW(guid) != DRIVE_FIXED) { + continue; + } + + uc_path = g_malloc(sizeof(WCHAR) * char_count); + if (!GetVolumePathNamesForVolumeNameW(guid, uc_path, char_count, + &char_count) || !*uc_path) { + /* strange, but this condition could be faced even with size == 2 */ + g_free(uc_path); + continue; + } + + res = g_new0(GuestFilesystemTrimResult, 1); + + path = g_utf16_to_utf8(uc_path, char_count, NULL, NULL, &gerr); + + g_free(uc_path); + + if (!path) { + res->has_error = true; + res->error = g_strdup(gerr->message); + g_error_free(gerr); + break; + } + + res->path = path; + + list = g_new0(GuestFilesystemTrimResultList, 1); + list->value = res; + list->next = resp->paths; + + resp->paths = list; + + memset(argv, 0, sizeof(argv)); + argv[0] = (gchar *)"defrag.exe"; + argv[1] = (gchar *)"/L"; + argv[2] = path; + + if (!g_spawn_sync(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, + &out /* stdout */, NULL /* stdin */, + NULL, &gerr)) { + res->has_error = true; + res->error = g_strdup(gerr->message); + g_error_free(gerr); + } else { + /* defrag.exe is UGLY. Exit code is ALWAYS zero. + Error is reported in the output with something like + (x89000020) etc code in the stdout */ + + int i; + gchar **lines = g_strsplit(out, "\r\n", 0); + g_free(out); + + for (i = 0; lines[i] != NULL; i++) { + if (g_strstr_len(lines[i], -1, "(0x") == NULL) { + continue; + } + res->has_error = true; + res->error = g_strdup(lines[i]); + break; + } + g_strfreev(lines); + } + } while (FindNextVolumeW(handle, guid, ARRAYSIZE(guid))); + + FindVolumeClose(handle); + return resp; } typedef enum { @@ -1416,7 +1507,7 @@ GList *ga_command_blacklist_init(GList *blacklist) "guest-get-memory-blocks", "guest-set-memory-blocks", "guest-get-memory-block-size", "guest-fsfreeze-freeze-list", - "guest-fstrim", NULL}; + NULL}; char **p = (char **)list_unsupported; while (*p) { From b8093d38e8dce0413fe8999fe2dee48a96ab1104 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 14 Oct 2016 10:00:53 +0100 Subject: [PATCH 2/5] qga: drop unused sockaddr in accept(2) call ga_channel_listen_accept() is currently hard-coded to support only AF_UNIX because the struct sockaddr_un type is used. This function should work with any address family. Drop the sockaddr since the client address is unused and is an optional argument to accept(2). Signed-off-by: Stefan Hajnoczi Reviewed-by: Michael Roth Signed-off-by: Michael Roth --- qga/channel-posix.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/qga/channel-posix.c b/qga/channel-posix.c index bb65d8ba17..bf321584ce 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -26,13 +26,10 @@ static gboolean ga_channel_listen_accept(GIOChannel *channel, GAChannel *c = data; int ret, client_fd; bool accepted = false; - struct sockaddr_un addr; - socklen_t addrlen = sizeof(addr); g_assert(channel != NULL); - client_fd = qemu_accept(g_io_channel_unix_get_fd(channel), - (struct sockaddr *)&addr, &addrlen); + client_fd = qemu_accept(g_io_channel_unix_get_fd(channel), NULL, NULL); if (client_fd == -1) { g_warning("error converting fd to gsocket: %s", strerror(errno)); goto out; From f06b2031a31cdd3acf6f61a977e505b8c6b58f73 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 14 Oct 2016 10:00:54 +0100 Subject: [PATCH 3/5] qga: drop unnecessary GA_CHANNEL_UNIX_LISTEN checks Throughout the code there are c->listen_channel checks which manage the listen socket file descriptor (waiting for accept(2), closing the file descriptor, etc). These checks are currently preceded by explicit c->method == GA_CHANNEL_UNIX_LISTEN checks. Explicit GA_CHANNEL_UNIX_LISTEN checks are not necessary since serial channel types do not create the listen channel (c->listen_channel). As more listen channel types are added, explicitly checking all of them becomes messy. Rely on c->listen_channel to determine whether or not a listen socket file descriptor is used. Signed-off-by: Stefan Hajnoczi Reviewed-by: Michael Roth Signed-off-by: Michael Roth --- qga/channel-posix.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/qga/channel-posix.c b/qga/channel-posix.c index bf321584ce..579891d940 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -61,7 +61,6 @@ static void ga_channel_listen_add(GAChannel *c, int listen_fd, bool create) static void ga_channel_listen_close(GAChannel *c) { - g_assert(c->method == GA_CHANNEL_UNIX_LISTEN); g_assert(c->listen_channel); g_io_channel_shutdown(c->listen_channel, true, NULL); g_io_channel_unref(c->listen_channel); @@ -77,7 +76,7 @@ static void ga_channel_client_close(GAChannel *c) g_io_channel_shutdown(c->client_channel, true, NULL); g_io_channel_unref(c->client_channel); c->client_channel = NULL; - if (c->method == GA_CHANNEL_UNIX_LISTEN && c->listen_channel) { + if (c->listen_channel) { ga_channel_listen_add(c, 0, false); } } @@ -255,8 +254,7 @@ GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, void ga_channel_free(GAChannel *c) { - if (c->method == GA_CHANNEL_UNIX_LISTEN - && c->listen_channel) { + if (c->listen_channel) { ga_channel_listen_close(c); } if (c->client_channel) { From 6a02c8069f6c28ed1251e3fbbdf16e49d2c27ccc Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 14 Oct 2016 10:00:55 +0100 Subject: [PATCH 4/5] sockets: add AF_VSOCK support Add the AF_VSOCK address family so that qemu-ga will be able to use virtio-vsock. The AF_VSOCK address family uses address tuples. The cid is the unique identifier comparable to an IP address. AF_VSOCK does not use name resolution so it's easy to convert between struct sockaddr_vm and strings. This patch defines a VsockSocketAddress instead of trying to piggy-back on InetSocketAddress. This is cleaner in the long run since it avoids lots of IPv4 vs IPv6 vs vsock special casing. Signed-off-by: Stefan Hajnoczi * treat trailing commas as garbage when parsing (Eric Blake) * add configure check instead of checking AF_VSOCK directly Signed-off-by: Michael Roth --- configure | 31 ++++++ qapi-schema.json | 23 ++++- util/qemu-sockets.c | 227 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 280 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 6b7acb1ccb..fd6f898cbc 100755 --- a/configure +++ b/configure @@ -4674,6 +4674,33 @@ if compile_prog "" "" ; then have_rtnetlink=yes fi +########################################## +# check for usable AF_VSOCK environment +have_af_vsock=no +cat > $TMPC << EOF +#include +#include +#include +#if !defined(AF_VSOCK) +# error missing AF_VSOCK flag +#endif +#include +int main(void) { + int sock, ret; + struct sockaddr_vm svm; + socklen_t len = sizeof(svm); + sock = socket(AF_VSOCK, SOCK_STREAM, 0); + ret = getpeername(sock, (struct sockaddr *)&svm, &len); + if ((ret == -1) && (errno == ENOTCONN)) { + return 0; + } + return -1; +} +EOF +if compile_prog "" "" ; then + have_af_vsock=yes +fi + ################################################# # Sparc implicitly links with --relax, which is # incompatible with -r, so --no-relax should be @@ -5662,6 +5689,10 @@ if test "$replication" = "yes" ; then echo "CONFIG_REPLICATION=y" >> $config_host_mak fi +if test "$have_af_vsock" = "yes" ; then + echo "CONFIG_AF_VSOCK=y" >> $config_host_mak +fi + # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on # a thread we have a handle to diff --git a/qapi-schema.json b/qapi-schema.json index 8a7b527091..5dc96af469 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -1063,12 +1063,14 @@ # # @unix: unix socket # +# @vsock: vsock family (since 2.8) +# # @unknown: otherwise # # Since: 2.1 ## { 'enum': 'NetworkAddressFamily', - 'data': [ 'ipv4', 'ipv6', 'unix', 'unknown' ] } + 'data': [ 'ipv4', 'ipv6', 'unix', 'vsock', 'unknown' ] } ## # @VncBasicInfo @@ -3094,6 +3096,24 @@ 'data': { 'path': 'str' } } +## +# @VsockSocketAddress +# +# Captures a socket address in the vsock namespace. +# +# @cid: unique host identifier +# @port: port +# +# Note that string types are used to allow for possible future hostname or +# service resolution support. +# +# Since 2.8 +## +{ 'struct': 'VsockSocketAddress', + 'data': { + 'cid': 'str', + 'port': 'str' } } + ## # @SocketAddress # @@ -3105,6 +3125,7 @@ 'data': { 'inet': 'InetSocketAddress', 'unix': 'UnixSocketAddress', + 'vsock': 'VsockSocketAddress', 'fd': 'String' } } ## diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 31f7fc69c1..fe1d07aaef 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -17,6 +17,10 @@ */ #include "qemu/osdep.h" +#ifdef CONFIG_AF_VSOCK +#include +#endif /* CONFIG_AF_VSOCK */ + #include "monitor/monitor.h" #include "qapi/error.h" #include "qemu/sockets.h" @@ -75,6 +79,9 @@ NetworkAddressFamily inet_netfamily(int family) case PF_INET6: return NETWORK_ADDRESS_FAMILY_IPV6; case PF_INET: return NETWORK_ADDRESS_FAMILY_IPV4; case PF_UNIX: return NETWORK_ADDRESS_FAMILY_UNIX; +#ifdef CONFIG_AF_VSOCK + case PF_VSOCK: return NETWORK_ADDRESS_FAMILY_VSOCK; +#endif /* CONFIG_AF_VSOCK */ } return NETWORK_ADDRESS_FAMILY_UNKNOWN; } @@ -650,6 +657,181 @@ int inet_connect(const char *str, Error **errp) return sock; } +#ifdef CONFIG_AF_VSOCK +static bool vsock_parse_vaddr_to_sockaddr(const VsockSocketAddress *vaddr, + struct sockaddr_vm *svm, + Error **errp) +{ + unsigned long long val; + + memset(svm, 0, sizeof(*svm)); + svm->svm_family = AF_VSOCK; + + if (parse_uint_full(vaddr->cid, &val, 10) < 0 || + val > UINT32_MAX) { + error_setg(errp, "Failed to parse cid '%s'", vaddr->cid); + return false; + } + svm->svm_cid = val; + + if (parse_uint_full(vaddr->port, &val, 10) < 0 || + val > UINT32_MAX) { + error_setg(errp, "Failed to parse port '%s'", vaddr->port); + return false; + } + svm->svm_port = val; + + return true; +} + +static int vsock_connect_addr(const struct sockaddr_vm *svm, bool *in_progress, + ConnectState *connect_state, Error **errp) +{ + int sock, rc; + + *in_progress = false; + + sock = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); + if (sock < 0) { + error_setg_errno(errp, errno, "Failed to create socket"); + return -1; + } + if (connect_state != NULL) { + qemu_set_nonblock(sock); + } + /* connect to peer */ + do { + rc = 0; + if (connect(sock, (const struct sockaddr *)svm, sizeof(*svm)) < 0) { + rc = -errno; + } + } while (rc == -EINTR); + + if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { + connect_state->fd = sock; + qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); + *in_progress = true; + } else if (rc < 0) { + error_setg_errno(errp, errno, "Failed to connect socket"); + closesocket(sock); + return -1; + } + return sock; +} + +static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp, + NonBlockingConnectHandler *callback, + void *opaque) +{ + struct sockaddr_vm svm; + int sock = -1; + bool in_progress; + ConnectState *connect_state = NULL; + + if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { + return -1; + } + + if (callback != NULL) { + connect_state = g_malloc0(sizeof(*connect_state)); + connect_state->callback = callback; + connect_state->opaque = opaque; + } + + sock = vsock_connect_addr(&svm, &in_progress, connect_state, errp); + if (sock < 0) { + /* do nothing */ + } else if (in_progress) { + /* wait_for_connect() will do the rest */ + return sock; + } else { + if (callback) { + callback(sock, NULL, opaque); + } + } + g_free(connect_state); + return sock; +} + +static int vsock_listen_saddr(VsockSocketAddress *vaddr, + Error **errp) +{ + struct sockaddr_vm svm; + int slisten; + + if (!vsock_parse_vaddr_to_sockaddr(vaddr, &svm, errp)) { + return -1; + } + + slisten = qemu_socket(AF_VSOCK, SOCK_STREAM, 0); + if (slisten < 0) { + error_setg_errno(errp, errno, "Failed to create socket"); + return -1; + } + + if (bind(slisten, (const struct sockaddr *)&svm, sizeof(svm)) != 0) { + error_setg_errno(errp, errno, "Failed to bind socket"); + closesocket(slisten); + return -1; + } + + if (listen(slisten, 1) != 0) { + error_setg_errno(errp, errno, "Failed to listen on socket"); + closesocket(slisten); + return -1; + } + return slisten; +} + +static VsockSocketAddress *vsock_parse(const char *str, Error **errp) +{ + VsockSocketAddress *addr = NULL; + char cid[33]; + char port[33]; + int n; + + if (sscanf(str, "%32[^:]:%32[^,]%n", cid, port, &n) != 2) { + error_setg(errp, "error parsing address '%s'", str); + return NULL; + } + if (str[n] != '\0') { + error_setg(errp, "trailing characters in address '%s'", str); + return NULL; + } + + addr = g_new0(VsockSocketAddress, 1); + addr->cid = g_strdup(cid); + addr->port = g_strdup(port); + return addr; +} +#else +static void vsock_unsupported(Error **errp) +{ + error_setg(errp, "socket family AF_VSOCK unsupported"); +} + +static int vsock_connect_saddr(VsockSocketAddress *vaddr, Error **errp, + NonBlockingConnectHandler *callback, + void *opaque) +{ + vsock_unsupported(errp); + return -1; +} + +static int vsock_listen_saddr(VsockSocketAddress *vaddr, + Error **errp) +{ + vsock_unsupported(errp); + return -1; +} + +static VsockSocketAddress *vsock_parse(const char *str, Error **errp) +{ + vsock_unsupported(errp); + return NULL; +} +#endif /* CONFIG_AF_VSOCK */ + #ifndef _WIN32 static int unix_listen_saddr(UnixSocketAddress *saddr, @@ -864,6 +1046,12 @@ SocketAddress *socket_parse(const char *str, Error **errp) addr->u.fd.data = g_new(String, 1); addr->u.fd.data->str = g_strdup(str + 3); } + } else if (strstart(str, "vsock:", NULL)) { + addr->type = SOCKET_ADDRESS_KIND_VSOCK; + addr->u.vsock.data = vsock_parse(str + strlen("vsock:"), errp); + if (addr->u.vsock.data == NULL) { + goto fail; + } } else { addr->type = SOCKET_ADDRESS_KIND_INET; addr->u.inet.data = inet_parse(str, errp); @@ -900,6 +1088,10 @@ int socket_connect(SocketAddress *addr, Error **errp, } break; + case SOCKET_ADDRESS_KIND_VSOCK: + fd = vsock_connect_saddr(addr->u.vsock.data, errp, callback, opaque); + break; + default: abort(); } @@ -923,6 +1115,10 @@ int socket_listen(SocketAddress *addr, Error **errp) fd = monitor_get_fd(cur_mon, addr->u.fd.data->str, errp); break; + case SOCKET_ADDRESS_KIND_VSOCK: + fd = vsock_listen_saddr(addr->u.vsock.data, errp); + break; + default: abort(); } @@ -1022,6 +1218,26 @@ socket_sockaddr_to_address_unix(struct sockaddr_storage *sa, } #endif /* WIN32 */ +#ifdef CONFIG_AF_VSOCK +static SocketAddress * +socket_sockaddr_to_address_vsock(struct sockaddr_storage *sa, + socklen_t salen, + Error **errp) +{ + SocketAddress *addr; + VsockSocketAddress *vaddr; + struct sockaddr_vm *svm = (struct sockaddr_vm *)sa; + + addr = g_new0(SocketAddress, 1); + addr->type = SOCKET_ADDRESS_KIND_VSOCK; + addr->u.vsock.data = vaddr = g_new0(VsockSocketAddress, 1); + vaddr->cid = g_strdup_printf("%u", svm->svm_cid); + vaddr->port = g_strdup_printf("%u", svm->svm_port); + + return addr; +} +#endif /* CONFIG_AF_VSOCK */ + SocketAddress * socket_sockaddr_to_address(struct sockaddr_storage *sa, socklen_t salen, @@ -1037,6 +1253,11 @@ socket_sockaddr_to_address(struct sockaddr_storage *sa, return socket_sockaddr_to_address_unix(sa, salen, errp); #endif /* WIN32 */ +#ifdef CONFIG_AF_VSOCK + case AF_VSOCK: + return socket_sockaddr_to_address_vsock(sa, salen, errp); +#endif + default: error_setg(errp, "socket family %d unsupported", sa->ss_family); @@ -1103,6 +1324,12 @@ char *socket_address_to_string(struct SocketAddress *addr, Error **errp) buf = g_strdup(addr->u.fd.data->str); break; + case SOCKET_ADDRESS_KIND_VSOCK: + buf = g_strdup_printf("%s:%s", + addr->u.vsock.data->cid, + addr->u.vsock.data->port); + break; + default: error_setg(errp, "socket family %d unsupported", addr->type); From 586ef5dee77180fc32e33bc08051600030630239 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Fri, 14 Oct 2016 10:00:56 +0100 Subject: [PATCH 5/5] qga: add vsock-listen method Add AF_VSOCK (virtio-vsock) support as an alternative to virtio-serial. $ qemu-system-x86_64 -device vhost-vsock-pci,guest-cid=3 ... (guest)# qemu-ga -m vsock-listen -p 3:1234 Signed-off-by: Stefan Hajnoczi Reviewed-by: Michael Roth Signed-off-by: Michael Roth --- qga/channel-posix.c | 25 +++++++++++++++++++++++++ qga/channel.h | 1 + qga/main.c | 6 ++++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/qga/channel-posix.c b/qga/channel-posix.c index 579891d940..71582e0c38 100644 --- a/qga/channel-posix.c +++ b/qga/channel-posix.c @@ -193,6 +193,31 @@ static gboolean ga_channel_open(GAChannel *c, const gchar *path, GAChannelMethod ga_channel_listen_add(c, fd, true); break; } + case GA_CHANNEL_VSOCK_LISTEN: { + Error *local_err = NULL; + SocketAddress *addr; + char *addr_str; + int fd; + + addr_str = g_strdup_printf("vsock:%s", path); + addr = socket_parse(addr_str, &local_err); + g_free(addr_str); + if (local_err != NULL) { + g_critical("%s", error_get_pretty(local_err)); + error_free(local_err); + return false; + } + + fd = socket_listen(addr, &local_err); + qapi_free_SocketAddress(addr); + if (local_err != NULL) { + g_critical("%s", error_get_pretty(local_err)); + error_free(local_err); + return false; + } + ga_channel_listen_add(c, fd, true); + break; + } default: g_critical("error binding/listening to specified socket"); return false; diff --git a/qga/channel.h b/qga/channel.h index ae8cf0f7e0..8fd0c8f72c 100644 --- a/qga/channel.h +++ b/qga/channel.h @@ -19,6 +19,7 @@ typedef enum { GA_CHANNEL_VIRTIO_SERIAL, GA_CHANNEL_ISA_SERIAL, GA_CHANNEL_UNIX_LISTEN, + GA_CHANNEL_VSOCK_LISTEN, } GAChannelMethod; typedef gboolean (*GAChannelCallback)(GIOCondition condition, gpointer opaque); diff --git a/qga/main.c b/qga/main.c index 0b9d04ea04..6caf215575 100644 --- a/qga/main.c +++ b/qga/main.c @@ -190,8 +190,8 @@ static void usage(const char *cmd) "Usage: %s [-m -p ] []\n" "QEMU Guest Agent %s\n" "\n" -" -m, --method transport method: one of unix-listen, virtio-serial, or\n" -" isa-serial (virtio-serial is the default)\n" +" -m, --method transport method: one of unix-listen, virtio-serial,\n" +" isa-serial, or vsock-listen (virtio-serial is the default)\n" " -p, --path device/socket path (the default for virtio-serial is:\n" " %s,\n" " the default for isa-serial is:\n" @@ -659,6 +659,8 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path) channel_method = GA_CHANNEL_ISA_SERIAL; } else if (strcmp(method, "unix-listen") == 0) { channel_method = GA_CHANNEL_UNIX_LISTEN; + } else if (strcmp(method, "vsock-listen") == 0) { + channel_method = GA_CHANNEL_VSOCK_LISTEN; } else { g_critical("unsupported channel method/type: %s", method); return false;