From 9f781168c5d4d2640be89b53a41443d4f368f489 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 2 Oct 2014 11:17:33 -0500 Subject: qemu-char: Make the filename size for a chardev a #define Signed-off-by: Corey Minyard Reviewed-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- qemu-char.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index 8623c70964..f9d2a02109 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -84,6 +84,7 @@ #define READ_BUF_LEN 4096 #define READ_RETRIES 10 +#define CHR_MAX_FILENAME_SIZE 256 /***********************************************************/ /* character device */ @@ -989,7 +990,8 @@ static CharDriverState *qemu_chr_open_fd(int fd_in, int fd_out) static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) { int fd_in, fd_out; - char filename_in[256], filename_out[256]; + char filename_in[CHR_MAX_FILENAME_SIZE]; + char filename_out[CHR_MAX_FILENAME_SIZE]; const char *filename = opts->device; if (filename == NULL) { @@ -997,8 +999,8 @@ static CharDriverState *qemu_chr_open_pipe(ChardevHostdev *opts) return NULL; } - snprintf(filename_in, 256, "%s.in", filename); - snprintf(filename_out, 256, "%s.out", filename); + snprintf(filename_in, CHR_MAX_FILENAME_SIZE, "%s.in", filename); + snprintf(filename_out, CHR_MAX_FILENAME_SIZE, "%s.out", filename); TFR(fd_in = qemu_open(filename_in, O_RDWR | O_BINARY)); TFR(fd_out = qemu_open(filename_out, O_RDWR | O_BINARY)); if (fd_in < 0 || fd_out < 0) { @@ -1976,7 +1978,7 @@ static int win_chr_pipe_init(CharDriverState *chr, const char *filename) OVERLAPPED ov; int ret; DWORD size; - char openname[256]; + char openname[CHR_MAX_FILENAME_SIZE]; s->fpipe = TRUE; @@ -2918,12 +2920,12 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, s->write_msgfds = 0; s->write_msgfds_num = 0; - chr->filename = g_malloc(256); + chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE); switch (ss.ss_family) { #ifndef _WIN32 case AF_UNIX: s->is_unix = 1; - snprintf(chr->filename, 256, "unix:%s%s", + snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "unix:%s%s", ((struct sockaddr_un *)(&ss))->sun_path, is_listen ? ",server" : ""); break; @@ -2936,7 +2938,7 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, s->do_nodelay = do_nodelay; getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); - snprintf(chr->filename, 256, "%s:%s%s%s:%s%s", + snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "%s:%s%s%s:%s%s", is_telnet ? "telnet" : "tcp", left, host, right, serv, is_listen ? ",server" : ""); -- cgit v1.2.3 From 43ded1a0d2381e65f0fff28269b7d02d4ead6aac Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 2 Oct 2014 11:17:34 -0500 Subject: qemu-char: Rework qemu_chr_open_socket() for reconnect Move all socket configuration to qmp_chardev_open_socket(). qemu_chr_open_socket_fd() just opens the socket. This is getting ready for the reconnect code, which will call open_sock_fd() on a reconnect attempt. Signed-off-by: Corey Minyard Reviewed-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- qemu-char.c | 118 ++++++++++++++++++++++++++++++++++-------------------------- 1 file changed, 68 insertions(+), 50 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index f9d2a02109..dcfeb73a22 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2891,13 +2891,11 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, - bool is_listen, bool is_telnet, - bool is_waitconnect, - Error **errp) +static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, + bool is_listen, bool is_telnet, + Error **errp) { - CharDriverState *chr = NULL; - TCPCharDriver *s = NULL; + TCPCharDriver *s = chr->opaque; char host[NI_MAXHOST], serv[NI_MAXSERV]; const char *left = "", *right = ""; struct sockaddr_storage ss; @@ -2905,26 +2903,14 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, memset(&ss, 0, ss_len); if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) { + closesocket(fd); error_setg_errno(errp, errno, "getsockname"); - return NULL; + return false; } - chr = qemu_chr_alloc(); - s = g_malloc0(sizeof(TCPCharDriver)); - - s->connected = 0; - s->fd = -1; - s->listen_fd = -1; - s->read_msgfds = 0; - s->read_msgfds_num = 0; - s->write_msgfds = 0; - s->write_msgfds_num = 0; - - chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE); switch (ss.ss_family) { #ifndef _WIN32 case AF_UNIX: - s->is_unix = 1; snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "unix:%s%s", ((struct sockaddr_un *)(&ss))->sun_path, is_listen ? ",server" : ""); @@ -2935,7 +2921,6 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, right = "]"; /* fall through */ case AF_INET: - s->do_nodelay = do_nodelay; getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "%s:%s%s%s:%s%s", @@ -2945,25 +2930,11 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, break; } - chr->opaque = s; - chr->chr_write = tcp_chr_write; - chr->chr_sync_read = tcp_chr_sync_read; - chr->chr_close = tcp_chr_close; - chr->get_msgfds = tcp_get_msgfds; - chr->set_msgfds = tcp_set_msgfds; - chr->chr_add_client = tcp_chr_add_client; - chr->chr_add_watch = tcp_chr_add_watch; - chr->chr_update_read_handler = tcp_chr_update_read_handler; - /* be isn't opened until we get a connection */ - chr->explicit_be_open = true; - if (is_listen) { s->listen_fd = fd; s->listen_chan = io_channel_from_socket(s->listen_fd); - s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_accept, chr); - if (is_telnet) { - s->do_telnetopt = 1; - } + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, + tcp_chr_accept, chr); } else { s->connected = 1; s->fd = fd; @@ -2972,13 +2943,26 @@ static CharDriverState *qemu_chr_open_socket_fd(int fd, bool do_nodelay, tcp_chr_connect(chr); } - if (is_listen && is_waitconnect) { - fprintf(stderr, "QEMU waiting for connection on: %s\n", - chr->filename); - tcp_chr_accept(s->listen_chan, G_IO_IN, chr); - qemu_set_nonblock(s->listen_fd); + return true; +} + +static bool qemu_chr_open_socket_fd(CharDriverState *chr, SocketAddress *addr, + bool is_listen, bool is_telnet, + Error **errp) +{ + int fd; + + if (is_listen) { + fd = socket_listen(addr, errp); + } else { + fd = socket_connect(addr, errp, NULL, NULL); } - return chr; + if (fd < 0) { + return false; + } + + return qemu_chr_finish_socket_connection(chr, fd, is_listen, is_telnet, + errp); } /*********************************************************/ @@ -3969,23 +3953,57 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, Error **errp) { + CharDriverState *chr; + TCPCharDriver *s; SocketAddress *addr = sock->addr; bool do_nodelay = sock->has_nodelay ? sock->nodelay : false; bool is_listen = sock->has_server ? sock->server : true; bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; - int fd; + + chr = qemu_chr_alloc(); + s = g_malloc0(sizeof(TCPCharDriver)); + + s->fd = -1; + s->listen_fd = -1; + s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX; + s->do_nodelay = do_nodelay; + + chr->opaque = s; + chr->chr_write = tcp_chr_write; + chr->chr_sync_read = tcp_chr_sync_read; + chr->chr_close = tcp_chr_close; + chr->get_msgfds = tcp_get_msgfds; + chr->set_msgfds = tcp_set_msgfds; + chr->chr_add_client = tcp_chr_add_client; + chr->chr_add_watch = tcp_chr_add_watch; + chr->chr_update_read_handler = tcp_chr_update_read_handler; + /* be isn't opened until we get a connection */ + chr->explicit_be_open = true; + + chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE); if (is_listen) { - fd = socket_listen(addr, errp); - } else { - fd = socket_connect(addr, errp, NULL, NULL); + if (is_telnet) { + s->do_telnetopt = 1; + } } - if (fd < 0) { + + if (!qemu_chr_open_socket_fd(chr, addr, is_listen, is_telnet, errp)) { + g_free(s); + g_free(chr->filename); + g_free(chr); return NULL; } - return qemu_chr_open_socket_fd(fd, do_nodelay, is_listen, - is_telnet, is_waitconnect, errp); + + if (is_listen && is_waitconnect) { + fprintf(stderr, "QEMU waiting for connection on: %s\n", + chr->filename); + tcp_chr_accept(s->listen_chan, G_IO_IN, chr); + qemu_set_nonblock(s->listen_fd); + } + + return chr; } static CharDriverState *qmp_chardev_open_udp(ChardevUdp *udp, -- cgit v1.2.3 From cfb429cb1acef4925812e3361ce56884043e84f7 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 2 Oct 2014 11:17:35 -0500 Subject: qemu-char: Move some items into TCPCharDriver This keeps them from having to be passed around and makes them available for later functions, like printing and reconnecting. Signed-off-by: Corey Minyard Reviewed-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- qemu-char.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 51 insertions(+), 14 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index dcfeb73a22..a671d6bbcb 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -28,6 +28,9 @@ #include "sysemu/char.h" #include "hw/usb.h" #include "qmp-commands.h" +#include "qapi/qmp-input-visitor.h" +#include "qapi/qmp-output-visitor.h" +#include "qapi-visit.h" #include #include @@ -86,6 +89,34 @@ #define READ_RETRIES 10 #define CHR_MAX_FILENAME_SIZE 256 +/***********************************************************/ +/* Socket address helpers */ +static void qapi_copy_SocketAddress(SocketAddress **p_dest, + SocketAddress *src) +{ + QmpOutputVisitor *qov; + QmpInputVisitor *qiv; + Visitor *ov, *iv; + QObject *obj; + + *p_dest = NULL; + + qov = qmp_output_visitor_new(); + ov = qmp_output_get_visitor(qov); + visit_type_SocketAddress(ov, &src, NULL, &error_abort); + obj = qmp_output_get_qobject(qov); + qmp_output_visitor_cleanup(qov); + if (!obj) { + return; + } + + qiv = qmp_input_visitor_new(obj); + iv = qmp_input_get_visitor(qiv); + visit_type_SocketAddress(iv, p_dest, NULL, &error_abort); + qmp_input_visitor_cleanup(qiv); + qobject_decref(obj); +} + /***********************************************************/ /* character device */ @@ -2412,6 +2443,10 @@ typedef struct { int read_msgfds_num; int *write_msgfds; int write_msgfds_num; + + SocketAddress *addr; + bool is_listen; + bool is_telnet; } TCPCharDriver; static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque); @@ -2861,6 +2896,8 @@ static void tcp_chr_close(CharDriverState *chr) { TCPCharDriver *s = chr->opaque; int i; + + qapi_free_SocketAddress(s->addr); if (s->fd >= 0) { remove_fd_in_watch(chr); if (s->chan) { @@ -2892,7 +2929,6 @@ static void tcp_chr_close(CharDriverState *chr) } static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, - bool is_listen, bool is_telnet, Error **errp) { TCPCharDriver *s = chr->opaque; @@ -2913,7 +2949,7 @@ static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, case AF_UNIX: snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "unix:%s%s", ((struct sockaddr_un *)(&ss))->sun_path, - is_listen ? ",server" : ""); + s->is_listen ? ",server" : ""); break; #endif case AF_INET6: @@ -2924,13 +2960,13 @@ static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "%s:%s%s%s:%s%s", - is_telnet ? "telnet" : "tcp", + s->is_telnet ? "telnet" : "tcp", left, host, right, serv, - is_listen ? ",server" : ""); + s->is_listen ? ",server" : ""); break; } - if (is_listen) { + if (s->is_listen) { s->listen_fd = fd; s->listen_chan = io_channel_from_socket(s->listen_fd); s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, @@ -2946,23 +2982,21 @@ static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, return true; } -static bool qemu_chr_open_socket_fd(CharDriverState *chr, SocketAddress *addr, - bool is_listen, bool is_telnet, - Error **errp) +static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp) { + TCPCharDriver *s = chr->opaque; int fd; - if (is_listen) { - fd = socket_listen(addr, errp); + if (s->is_listen) { + fd = socket_listen(s->addr, errp); } else { - fd = socket_connect(addr, errp, NULL, NULL); + fd = socket_connect(s->addr, errp, NULL, NULL); } if (fd < 0) { return false; } - return qemu_chr_finish_socket_connection(chr, fd, is_listen, is_telnet, - errp); + return qemu_chr_finish_socket_connection(chr, fd, errp); } /*********************************************************/ @@ -3967,7 +4001,10 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, s->fd = -1; s->listen_fd = -1; s->is_unix = addr->kind == SOCKET_ADDRESS_KIND_UNIX; + s->is_listen = is_listen; + s->is_telnet = is_telnet; s->do_nodelay = do_nodelay; + qapi_copy_SocketAddress(&s->addr, sock->addr); chr->opaque = s; chr->chr_write = tcp_chr_write; @@ -3989,7 +4026,7 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, } } - if (!qemu_chr_open_socket_fd(chr, addr, is_listen, is_telnet, errp)) { + if (!qemu_chr_open_socket_fd(chr, errp)) { g_free(s); g_free(chr->filename); g_free(chr); -- cgit v1.2.3 From 16cc4ffe34f3fd6d0626ed459fb241304786a04b Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 2 Oct 2014 11:17:36 -0500 Subject: qemu-char: set socket filename to disconnected when not connected This way we can tell if the socket is connected or not. It also splits the string conversions out into separate functions to make this more convenient. Signed-off-by: Corey Minyard Reviewed-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- qemu-char.c | 110 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 72 insertions(+), 38 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index a671d6bbcb..549ebd84e9 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -117,6 +117,60 @@ static void qapi_copy_SocketAddress(SocketAddress **p_dest, qobject_decref(obj); } +static int SocketAddress_to_str(char *dest, int max_len, + const char *prefix, SocketAddress *addr, + bool is_listen, bool is_telnet) +{ + switch (addr->kind) { + case SOCKET_ADDRESS_KIND_INET: + return snprintf(dest, max_len, "%s%s:%s:%s%s", prefix, + is_telnet ? "telnet" : "tcp", addr->inet->host, + addr->inet->port, is_listen ? ",server" : ""); + break; + case SOCKET_ADDRESS_KIND_UNIX: + return snprintf(dest, max_len, "%sunix:%s%s", prefix, + addr->q_unix->path, is_listen ? ",server" : ""); + break; + case SOCKET_ADDRESS_KIND_FD: + return snprintf(dest, max_len, "%sfd:%s%s", prefix, addr->fd->str, + is_listen ? ",server" : ""); + break; + default: + abort(); + } +} + +static int sockaddr_to_str(char *dest, int max_len, + struct sockaddr_storage *ss, socklen_t ss_len, + bool is_listen, bool is_telnet) +{ + char host[NI_MAXHOST], serv[NI_MAXSERV]; + const char *left = "", *right = ""; + + switch (ss->ss_family) { +#ifndef _WIN32 + case AF_UNIX: + return snprintf(dest, max_len, "unix:%s%s", + ((struct sockaddr_un *)(ss))->sun_path, + is_listen ? ",server" : ""); +#endif + case AF_INET6: + left = "["; + right = "]"; + /* fall through */ + case AF_INET: + getnameinfo((struct sockaddr *) ss, ss_len, host, sizeof(host), + serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); + return snprintf(dest, max_len, "%s:%s%s%s:%s%s", + is_telnet ? "telnet" : "tcp", + left, host, right, serv, + is_listen ? ",server" : ""); + + default: + return snprintf(dest, max_len, "unknown"); + } +} + /***********************************************************/ /* character device */ @@ -2727,6 +2781,8 @@ static void tcp_chr_disconnect(CharDriverState *chr) s->chan = NULL; closesocket(s->fd); s->fd = -1; + SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, + "disconnected:", s->addr, s->is_listen, s->is_telnet); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } @@ -2798,6 +2854,17 @@ static void tcp_chr_connect(void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; + struct sockaddr_storage ss; + socklen_t ss_len = sizeof(ss); + + memset(&ss, 0, ss_len); + if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) { + snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, + "Error in getsockname: %s\n", strerror(errno)); + } else { + sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, &ss, ss_len, + s->is_listen, s->is_telnet); + } s->connected = 1; if (s->chan) { @@ -2928,43 +2995,9 @@ static void tcp_chr_close(CharDriverState *chr) qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } -static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, - Error **errp) +static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd) { TCPCharDriver *s = chr->opaque; - char host[NI_MAXHOST], serv[NI_MAXSERV]; - const char *left = "", *right = ""; - struct sockaddr_storage ss; - socklen_t ss_len = sizeof(ss); - - memset(&ss, 0, ss_len); - if (getsockname(fd, (struct sockaddr *) &ss, &ss_len) != 0) { - closesocket(fd); - error_setg_errno(errp, errno, "getsockname"); - return false; - } - - switch (ss.ss_family) { -#ifndef _WIN32 - case AF_UNIX: - snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "unix:%s%s", - ((struct sockaddr_un *)(&ss))->sun_path, - s->is_listen ? ",server" : ""); - break; -#endif - case AF_INET6: - left = "["; - right = "]"; - /* fall through */ - case AF_INET: - getnameinfo((struct sockaddr *) &ss, ss_len, host, sizeof(host), - serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); - snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "%s:%s%s%s:%s%s", - s->is_telnet ? "telnet" : "tcp", - left, host, right, serv, - s->is_listen ? ",server" : ""); - break; - } if (s->is_listen) { s->listen_fd = fd; @@ -2978,8 +3011,6 @@ static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd, s->chan = io_channel_from_socket(s->fd); tcp_chr_connect(chr); } - - return true; } static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp) @@ -2996,7 +3027,8 @@ static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp) return false; } - return qemu_chr_finish_socket_connection(chr, fd, errp); + qemu_chr_finish_socket_connection(chr, fd); + return true; } /*********************************************************/ @@ -4019,6 +4051,8 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, chr->explicit_be_open = true; chr->filename = g_malloc(CHR_MAX_FILENAME_SIZE); + SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:", + addr, is_listen, is_telnet); if (is_listen) { if (is_telnet) { -- cgit v1.2.3 From 5dd1f02b4bc2f2c2ef3a2adfd8a412c8c8769085 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 2 Oct 2014 11:17:37 -0500 Subject: qemu-char: Add reconnecting to client sockets Adds a "reconnect" option to socket backends that gives a reconnect timeout. This only applies to client sockets. If the other end of a socket closes the connection, qemu will attempt to reconnect after the given number of seconds. Signed-off-by: Corey Minyard Signed-off-by: Paolo Bonzini --- qapi-schema.json | 15 +++++++---- qemu-char.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- qemu-options.hx | 20 ++++++++++----- 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/qapi-schema.json b/qapi-schema.json index 4bfaf20ebe..148097b7d8 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -2651,14 +2651,19 @@ # @nodelay: #optional set TCP_NODELAY socket option (default: false) # @telnet: #optional enable telnet protocol on server # sockets (default: false) +# @reconnect: #optional For a client socket, if a socket is disconnected, +# then attempt a reconnect after the given number of seconds. +# Setting this to zero disables this function. (default: 0) +# (Since: 2.2) # # Since: 1.4 ## -{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', - '*server' : 'bool', - '*wait' : 'bool', - '*nodelay' : 'bool', - '*telnet' : 'bool' } } +{ 'type': 'ChardevSocket', 'data': { 'addr' : 'SocketAddress', + '*server' : 'bool', + '*wait' : 'bool', + '*nodelay' : 'bool', + '*telnet' : 'bool', + '*reconnect' : 'int' } } ## # @ChardevUdp: diff --git a/qemu-char.c b/qemu-char.c index 549ebd84e9..aa15bd3da3 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2501,8 +2501,21 @@ typedef struct { SocketAddress *addr; bool is_listen; bool is_telnet; + + guint reconnect_timer; + int64_t reconnect_time; } TCPCharDriver; +static gboolean socket_reconnect_timeout(gpointer opaque); + +static void qemu_chr_socket_restart_timer(CharDriverState *chr) +{ + TCPCharDriver *s = chr->opaque; + assert(s->connected == 0); + s->reconnect_timer = g_timeout_add_seconds(s->reconnect_time, + socket_reconnect_timeout, chr); +} + static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque); #ifndef _WIN32 @@ -2784,6 +2797,9 @@ static void tcp_chr_disconnect(CharDriverState *chr) SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, "disconnected:", s->addr, s->is_listen, s->is_telnet); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); + if (s->reconnect_time) { + qemu_chr_socket_restart_timer(chr); + } } static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) @@ -2964,6 +2980,10 @@ static void tcp_chr_close(CharDriverState *chr) TCPCharDriver *s = chr->opaque; int i; + if (s->reconnect_timer) { + g_source_remove(s->reconnect_timer); + s->reconnect_timer = 0; + } qapi_free_SocketAddress(s->addr); if (s->fd >= 0) { remove_fd_in_watch(chr); @@ -3013,6 +3033,18 @@ static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd) } } +static void qemu_chr_socket_connected(int fd, void *opaque) +{ + CharDriverState *chr = opaque; + + if (fd < 0) { + qemu_chr_socket_restart_timer(chr); + return; + } + + qemu_chr_finish_socket_connection(chr, fd); +} + static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp) { TCPCharDriver *s = chr->opaque; @@ -3020,7 +3052,10 @@ static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp) if (s->is_listen) { fd = socket_listen(s->addr, errp); - } else { + } else if (s->reconnect_time) { + fd = socket_connect(s->addr, errp, qemu_chr_socket_connected, chr); + return fd >= 0; + } else { fd = socket_connect(s->addr, errp, NULL, NULL); } if (fd < 0) { @@ -3448,6 +3483,7 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true); bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); + int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0); const char *path = qemu_opt_get(opts, "path"); const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); @@ -3474,6 +3510,8 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, backend->socket->telnet = is_telnet; backend->socket->has_wait = true; backend->socket->wait = is_waitconnect; + backend->socket->has_reconnect = true; + backend->socket->reconnect = reconnect; addr = g_new0(SocketAddress, 1); if (path) { @@ -3872,6 +3910,9 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "delay", .type = QEMU_OPT_BOOL, + },{ + .name = "reconnect", + .type = QEMU_OPT_NUMBER, },{ .name = "telnet", .type = QEMU_OPT_BOOL, @@ -4016,6 +4057,26 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, #endif /* WIN32 */ +static gboolean socket_reconnect_timeout(gpointer opaque) +{ + CharDriverState *chr = opaque; + TCPCharDriver *s = chr->opaque; + Error *err; + + s->reconnect_timer = 0; + + if (chr->be_open) { + return false; + } + + if (!qemu_chr_open_socket_fd(chr, &err)) { + error_report("Unable to connect to char device %s\n", chr->label); + qemu_chr_socket_restart_timer(chr); + } + + return false; +} + static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, Error **errp) { @@ -4026,6 +4087,7 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, bool is_listen = sock->has_server ? sock->server : true; bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; + int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; chr = qemu_chr_alloc(); s = g_malloc0(sizeof(TCPCharDriver)); @@ -4058,13 +4120,19 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, if (is_telnet) { s->do_telnetopt = 1; } + } else if (reconnect > 0) { + s->reconnect_time = reconnect; } if (!qemu_chr_open_socket_fd(chr, errp)) { - g_free(s); - g_free(chr->filename); - g_free(chr); - return NULL; + if (s->reconnect_time) { + qemu_chr_socket_restart_timer(chr); + } else { + g_free(s); + g_free(chr->filename); + g_free(chr); + return NULL; + } } if (is_listen && is_waitconnect) { diff --git a/qemu-options.hx b/qemu-options.hx index 365b56c424..22cf3b94ce 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -1930,9 +1930,9 @@ ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" - "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay]\n" - " [,server][,nowait][,telnet][,mux=on|off] (tcp)\n" - "-chardev socket,id=id,path=path[,server][,nowait][,telnet],[mux=on|off] (unix)\n" + "-chardev socket,id=id[,host=host],port=port[,to=to][,ipv4][,ipv6][,nodelay][,reconnect=seconds]\n" + " [,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds][,mux=on|off] (unix)\n" "-chardev udp,id=id[,host=host],port=port[,localaddr=localaddr]\n" " [,localport=localport][,ipv4][,ipv6][,mux=on|off]\n" "-chardev msmouse,id=id[,mux=on|off]\n" @@ -2004,7 +2004,7 @@ Options to each backend are described below. A void device. This device will not emit any data, and will drop any data it receives. The null backend does not take any options. -@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] +@item -chardev socket ,id=@var{id} [@var{TCP options} or @var{unix options}] [,server] [,nowait] [,telnet] [,reconnect=@var{seconds}] Create a two-way stream socket, which can be either a TCP or a unix socket. A unix socket will be created if @option{path} is specified. Behaviour is @@ -2018,6 +2018,10 @@ connect to a listening socket. @option{telnet} specifies that traffic on the socket should interpret telnet escape sequences. +@option{reconnect} sets the timeout for reconnecting on non-server sockets when +the remote end goes away. qemu will delay this many seconds and then attempt +to reconnect. Zero disables reconnecting, and is the default. + TCP and unix socket options are given below: @table @option @@ -2687,14 +2691,16 @@ telnet on port 5555 to access the QEMU port. localhost 5555 @end table -@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay] +@item tcp:[@var{host}]:@var{port}[,@var{server}][,nowait][,nodelay][,reconnect=@var{seconds}] The TCP Net Console has two modes of operation. It can send the serial I/O to a location or wait for a connection from a location. By default the TCP Net Console is sent to @var{host} at the @var{port}. If you use the @var{server} option QEMU will wait for a client socket application to connect to the port before continuing, unless the @code{nowait} option was specified. The @code{nodelay} option disables the Nagle buffering -algorithm. If @var{host} is omitted, 0.0.0.0 is assumed. Only +algorithm. The @code{reconnect} option only applies if @var{noserver} is +set, if the connection goes down it will attempt to reconnect at the +given interval. If @var{host} is omitted, 0.0.0.0 is assumed. Only one TCP connection at a time is accepted. You can use @code{telnet} to connect to the corresponding character device. @table @code @@ -2715,7 +2721,7 @@ MAGIC_SYSRQ sequence if you use a telnet that supports sending the break sequence. Typically in unix telnet you do it with Control-] and then type "send break" followed by pressing the enter key. -@item unix:@var{path}[,server][,nowait] +@item unix:@var{path}[,server][,nowait][,reconnect=@var{seconds}] A unix domain socket is used instead of a tcp socket. The option works the same as if you had specified @code{-serial tcp} except the unix domain socket @var{path} is used for connections. -- cgit v1.2.3 From 01ca519f24d6eb8afdfaa971c930d1b67235dfca Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Thu, 2 Oct 2014 11:17:38 -0500 Subject: qemu-char: Print the remote and local addresses for a socket It seems that it might be a good idea to know what is at the remote end of a socket for tracking down issues. So add that to the socket filename. Signed-off-by: Corey Minyard Reviewed-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- qemu-char.c | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index aa15bd3da3..62af0efa18 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -142,9 +142,11 @@ static int SocketAddress_to_str(char *dest, int max_len, static int sockaddr_to_str(char *dest, int max_len, struct sockaddr_storage *ss, socklen_t ss_len, + struct sockaddr_storage *ps, socklen_t ps_len, bool is_listen, bool is_telnet) { - char host[NI_MAXHOST], serv[NI_MAXSERV]; + char shost[NI_MAXHOST], sserv[NI_MAXSERV]; + char phost[NI_MAXHOST], pserv[NI_MAXSERV]; const char *left = "", *right = ""; switch (ss->ss_family) { @@ -159,12 +161,15 @@ static int sockaddr_to_str(char *dest, int max_len, right = "]"; /* fall through */ case AF_INET: - getnameinfo((struct sockaddr *) ss, ss_len, host, sizeof(host), - serv, sizeof(serv), NI_NUMERICHOST | NI_NUMERICSERV); - return snprintf(dest, max_len, "%s:%s%s%s:%s%s", + getnameinfo((struct sockaddr *) ss, ss_len, shost, sizeof(shost), + sserv, sizeof(sserv), NI_NUMERICHOST | NI_NUMERICSERV); + getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost), + pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV); + return snprintf(dest, max_len, "%s:%s%s%s:%s%s <-> %s%s%s:%s", is_telnet ? "telnet" : "tcp", - left, host, right, serv, - is_listen ? ",server" : ""); + left, shost, right, sserv, + is_listen ? ",server" : "", + left, phost, right, pserv); default: return snprintf(dest, max_len, "unknown"); @@ -2870,15 +2875,19 @@ static void tcp_chr_connect(void *opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; - struct sockaddr_storage ss; - socklen_t ss_len = sizeof(ss); + struct sockaddr_storage ss, ps; + socklen_t ss_len = sizeof(ss), ps_len = sizeof(ps); memset(&ss, 0, ss_len); if (getsockname(s->fd, (struct sockaddr *) &ss, &ss_len) != 0) { snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, "Error in getsockname: %s\n", strerror(errno)); + } else if (getpeername(s->fd, (struct sockaddr *) &ps, &ps_len) != 0) { + snprintf(chr->filename, CHR_MAX_FILENAME_SIZE, + "Error in getpeername: %s\n", strerror(errno)); } else { - sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, &ss, ss_len, + sockaddr_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, + &ss, ss_len, &ps, ps_len, s->is_listen, s->is_telnet); } -- cgit v1.2.3 From 20674449453924ac808a82619456f68bb14df007 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:16 -0300 Subject: vl.c: Small coding style fix Just to make checkpatch.pl happy when moving the code. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- vl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vl.c b/vl.c index 9d2aaaf1dc..72436cd62f 100644 --- a/vl.c +++ b/vl.c @@ -2712,7 +2712,7 @@ static int configure_accelerator(MachineClass *mc) if (*p == ':') { p++; } - p = get_opt_name(buf, sizeof (buf), p, ':'); + p = get_opt_name(buf, sizeof(buf), p, ':'); for (i = 0; i < ARRAY_SIZE(accel_list); i++) { if (strcmp(accel_list[i].opt_name, buf) == 0) { if (!accel_list[i].available()) { -- cgit v1.2.3 From a1a9cb0ccd9aa6f0afcf151006fd981ec39ffa15 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:17 -0300 Subject: accel: Move accel code to accel.c Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- Makefile.objs | 1 + accel.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ include/sysemu/accel.h | 32 ++++++++++++++ vl.c | 81 +---------------------------------- 4 files changed, 147 insertions(+), 80 deletions(-) create mode 100644 accel.c create mode 100644 include/sysemu/accel.h diff --git a/Makefile.objs b/Makefile.objs index 97db978d16..add83755be 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -62,6 +62,7 @@ common-obj-$(CONFIG_SPICE) += spice-qemu-char.o common-obj-y += audio/ common-obj-y += hw/ +common-obj-y += accel.o common-obj-y += ui/ common-obj-y += bt-host.o bt-vhci.o diff --git a/accel.c b/accel.c new file mode 100644 index 0000000000..9424796b8b --- /dev/null +++ b/accel.c @@ -0,0 +1,113 @@ +/* + * QEMU System Emulator, accelerator interfaces + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysemu/accel.h" +#include "qemu-common.h" +#include "sysemu/arch_init.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "sysemu/qtest.h" +#include "hw/xen/xen.h" + +int tcg_tb_size; +static bool tcg_allowed = true; + +static int tcg_init(MachineClass *mc) +{ + tcg_exec_init(tcg_tb_size * 1024 * 1024); + return 0; +} + +static struct { + const char *opt_name; + const char *name; + int (*available)(void); + int (*init)(MachineClass *mc); + bool *allowed; +} accel_list[] = { + { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, + { "xen", "Xen", xen_available, xen_init, &xen_allowed }, + { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed }, + { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed }, +}; + +int configure_accelerator(MachineClass *mc) +{ + const char *p; + char buf[10]; + int i, ret; + bool accel_initialised = false; + bool init_failed = false; + + p = qemu_opt_get(qemu_get_machine_opts(), "accel"); + if (p == NULL) { + /* Use the default "accelerator", tcg */ + p = "tcg"; + } + + while (!accel_initialised && *p != '\0') { + if (*p == ':') { + p++; + } + p = get_opt_name(buf, sizeof(buf), p, ':'); + for (i = 0; i < ARRAY_SIZE(accel_list); i++) { + if (strcmp(accel_list[i].opt_name, buf) == 0) { + if (!accel_list[i].available()) { + printf("%s not supported for this target\n", + accel_list[i].name); + break; + } + *(accel_list[i].allowed) = true; + ret = accel_list[i].init(mc); + if (ret < 0) { + init_failed = true; + fprintf(stderr, "failed to initialize %s: %s\n", + accel_list[i].name, + strerror(-ret)); + *(accel_list[i].allowed) = false; + } else { + accel_initialised = true; + } + break; + } + } + if (i == ARRAY_SIZE(accel_list)) { + fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf); + } + } + + if (!accel_initialised) { + if (!init_failed) { + fprintf(stderr, "No accelerator found!\n"); + } + exit(1); + } + + if (init_failed) { + fprintf(stderr, "Back to %s accelerator.\n", accel_list[i].name); + } + + return !accel_initialised; +} diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h new file mode 100644 index 0000000000..5537d74406 --- /dev/null +++ b/include/sysemu/accel.h @@ -0,0 +1,32 @@ +/* QEMU accelerator interfaces + * + * Copyright (c) 2014 Red Hat Inc + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef HW_ACCEL_H +#define HW_ACCEL_H + +#include "qemu/typedefs.h" + +extern int tcg_tb_size; + +int configure_accelerator(MachineClass *mc); + +#endif diff --git a/vl.c b/vl.c index 72436cd62f..c3def21666 100644 --- a/vl.c +++ b/vl.c @@ -61,6 +61,7 @@ int main(int argc, char **argv) #include "qemu/sockets.h" #include "hw/hw.h" #include "hw/boards.h" +#include "sysemu/accel.h" #include "hw/usb.h" #include "hw/pcmcia.h" #include "hw/i386/pc.h" @@ -213,11 +214,9 @@ static NotifierList exit_notifiers = static NotifierList machine_init_done_notifiers = NOTIFIER_LIST_INITIALIZER(machine_init_done_notifiers); -static bool tcg_allowed = true; bool xen_allowed; uint32_t xen_domid; enum xen_mode xen_mode = XEN_EMULATE; -static int tcg_tb_size; static int has_defaults = 1; static int default_serial = 1; @@ -2675,84 +2674,6 @@ static MachineClass *machine_parse(const char *name) exit(!name || !is_help_option(name)); } -static int tcg_init(MachineClass *mc) -{ - tcg_exec_init(tcg_tb_size * 1024 * 1024); - return 0; -} - -static struct { - const char *opt_name; - const char *name; - int (*available)(void); - int (*init)(MachineClass *mc); - bool *allowed; -} accel_list[] = { - { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, - { "xen", "Xen", xen_available, xen_init, &xen_allowed }, - { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed }, - { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed }, -}; - -static int configure_accelerator(MachineClass *mc) -{ - const char *p; - char buf[10]; - int i, ret; - bool accel_initialised = false; - bool init_failed = false; - - p = qemu_opt_get(qemu_get_machine_opts(), "accel"); - if (p == NULL) { - /* Use the default "accelerator", tcg */ - p = "tcg"; - } - - while (!accel_initialised && *p != '\0') { - if (*p == ':') { - p++; - } - p = get_opt_name(buf, sizeof(buf), p, ':'); - for (i = 0; i < ARRAY_SIZE(accel_list); i++) { - if (strcmp(accel_list[i].opt_name, buf) == 0) { - if (!accel_list[i].available()) { - printf("%s not supported for this target\n", - accel_list[i].name); - break; - } - *(accel_list[i].allowed) = true; - ret = accel_list[i].init(mc); - if (ret < 0) { - init_failed = true; - fprintf(stderr, "failed to initialize %s: %s\n", - accel_list[i].name, - strerror(-ret)); - *(accel_list[i].allowed) = false; - } else { - accel_initialised = true; - } - break; - } - } - if (i == ARRAY_SIZE(accel_list)) { - fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf); - } - } - - if (!accel_initialised) { - if (!init_failed) { - fprintf(stderr, "No accelerator found!\n"); - } - exit(1); - } - - if (init_failed) { - fprintf(stderr, "Back to %s accelerator.\n", accel_list[i].name); - } - - return !accel_initialised; -} - void qemu_add_exit_notifier(Notifier *notify) { notifier_list_add(&exit_notifiers, notify); -- cgit v1.2.3 From e54adde6154fc86e52abb911e7c0604c9dc7a58a Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:18 -0300 Subject: accel: Create AccelType typedef Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/accel.c b/accel.c index 9424796b8b..3cefd7451c 100644 --- a/accel.c +++ b/accel.c @@ -40,13 +40,15 @@ static int tcg_init(MachineClass *mc) return 0; } -static struct { +typedef struct AccelType { const char *opt_name; const char *name; int (*available)(void); int (*init)(MachineClass *mc); bool *allowed; -} accel_list[] = { +} AccelType; + +static AccelType accel_list[] = { { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, { "xen", "Xen", xen_available, xen_init, &xen_allowed }, { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed }, -- cgit v1.2.3 From e8b466ef95637e083b8c962476e38dd640f360f1 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:19 -0300 Subject: accel: Simplify configure_accelerator() using AccelType *acc variable Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/accel.c b/accel.c index 3cefd7451c..fc8c5518df 100644 --- a/accel.c +++ b/accel.c @@ -62,6 +62,7 @@ int configure_accelerator(MachineClass *mc) int i, ret; bool accel_initialised = false; bool init_failed = false; + AccelType *acc = NULL; p = qemu_opt_get(qemu_get_machine_opts(), "accel"); if (p == NULL) { @@ -75,20 +76,21 @@ int configure_accelerator(MachineClass *mc) } p = get_opt_name(buf, sizeof(buf), p, ':'); for (i = 0; i < ARRAY_SIZE(accel_list); i++) { - if (strcmp(accel_list[i].opt_name, buf) == 0) { - if (!accel_list[i].available()) { + acc = &accel_list[i]; + if (strcmp(acc->opt_name, buf) == 0) { + if (!acc->available()) { printf("%s not supported for this target\n", - accel_list[i].name); + acc->name); break; } - *(accel_list[i].allowed) = true; - ret = accel_list[i].init(mc); + *(acc->allowed) = true; + ret = acc->init(mc); if (ret < 0) { init_failed = true; fprintf(stderr, "failed to initialize %s: %s\n", - accel_list[i].name, + acc->name, strerror(-ret)); - *(accel_list[i].allowed) = false; + *(acc->allowed) = false; } else { accel_initialised = true; } @@ -108,7 +110,7 @@ int configure_accelerator(MachineClass *mc) } if (init_failed) { - fprintf(stderr, "Back to %s accelerator.\n", accel_list[i].name); + fprintf(stderr, "Back to %s accelerator.\n", acc->name); } return !accel_initialised; -- cgit v1.2.3 From a22465520060951e70124729521318a559a3fe88 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:20 -0300 Subject: accel: Move accel name lookup to separate function Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 57 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/accel.c b/accel.c index fc8c5518df..c752fcc52d 100644 --- a/accel.c +++ b/accel.c @@ -55,11 +55,24 @@ static AccelType accel_list[] = { { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed }, }; +/* Lookup AccelType from opt_name. Returns NULL if not found */ +static AccelType *accel_find(const char *opt_name) +{ + int i; + for (i = 0; i < ARRAY_SIZE(accel_list); i++) { + AccelType *acc = &accel_list[i]; + if (acc->opt_name && strcmp(acc->opt_name, opt_name) == 0) { + return acc; + } + } + return NULL; +} + int configure_accelerator(MachineClass *mc) { const char *p; char buf[10]; - int i, ret; + int ret; bool accel_initialised = false; bool init_failed = false; AccelType *acc = NULL; @@ -75,30 +88,26 @@ int configure_accelerator(MachineClass *mc) p++; } p = get_opt_name(buf, sizeof(buf), p, ':'); - for (i = 0; i < ARRAY_SIZE(accel_list); i++) { - acc = &accel_list[i]; - if (strcmp(acc->opt_name, buf) == 0) { - if (!acc->available()) { - printf("%s not supported for this target\n", - acc->name); - break; - } - *(acc->allowed) = true; - ret = acc->init(mc); - if (ret < 0) { - init_failed = true; - fprintf(stderr, "failed to initialize %s: %s\n", - acc->name, - strerror(-ret)); - *(acc->allowed) = false; - } else { - accel_initialised = true; - } - break; - } - } - if (i == ARRAY_SIZE(accel_list)) { + acc = accel_find(buf); + if (!acc) { fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf); + continue; + } + if (!acc->available()) { + printf("%s not supported for this target\n", + acc->name); + continue; + } + *(acc->allowed) = true; + ret = acc->init(mc); + if (ret < 0) { + init_failed = true; + fprintf(stderr, "failed to initialize %s: %s\n", + acc->name, + strerror(-ret)); + *(acc->allowed) = false; + } else { + accel_initialised = true; } } -- cgit v1.2.3 From b14a0b7469fa3e6c26d18438531d26138c55b67e Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:21 -0300 Subject: accel: Use QOM classes for accel types Instead of having a static AccelType array, register a class for each accelerator type, and use class name lookup to find accelerator information. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 117 +++++++++++++++++++++++++++++++++++++++---------- include/sysemu/accel.h | 30 +++++++++++++ 2 files changed, 123 insertions(+), 24 deletions(-) diff --git a/accel.c b/accel.c index c752fcc52d..a3e2fd9149 100644 --- a/accel.c +++ b/accel.c @@ -30,6 +30,7 @@ #include "sysemu/kvm.h" #include "sysemu/qtest.h" #include "hw/xen/xen.h" +#include "qom/object.h" int tcg_tb_size; static bool tcg_allowed = true; @@ -40,32 +41,20 @@ static int tcg_init(MachineClass *mc) return 0; } -typedef struct AccelType { - const char *opt_name; - const char *name; - int (*available)(void); - int (*init)(MachineClass *mc); - bool *allowed; -} AccelType; - -static AccelType accel_list[] = { - { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed }, - { "xen", "Xen", xen_available, xen_init, &xen_allowed }, - { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed }, - { "qtest", "QTest", qtest_available, qtest_init_accel, &qtest_allowed }, +static const TypeInfo accel_type = { + .name = TYPE_ACCEL, + .parent = TYPE_OBJECT, + .class_size = sizeof(AccelClass), + .instance_size = sizeof(AccelState), }; -/* Lookup AccelType from opt_name. Returns NULL if not found */ -static AccelType *accel_find(const char *opt_name) +/* Lookup AccelClass from opt_name. Returns NULL if not found */ +static AccelClass *accel_find(const char *opt_name) { - int i; - for (i = 0; i < ARRAY_SIZE(accel_list); i++) { - AccelType *acc = &accel_list[i]; - if (acc->opt_name && strcmp(acc->opt_name, opt_name) == 0) { - return acc; - } - } - return NULL; + char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); + AccelClass *ac = ACCEL_CLASS(object_class_by_name(class_name)); + g_free(class_name); + return ac; } int configure_accelerator(MachineClass *mc) @@ -75,7 +64,7 @@ int configure_accelerator(MachineClass *mc) int ret; bool accel_initialised = false; bool init_failed = false; - AccelType *acc = NULL; + AccelClass *acc = NULL; p = qemu_opt_get(qemu_get_machine_opts(), "accel"); if (p == NULL) { @@ -124,3 +113,83 @@ int configure_accelerator(MachineClass *mc) return !accel_initialised; } + + +static void tcg_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "tcg"; + ac->available = tcg_available; + ac->init = tcg_init; + ac->allowed = &tcg_allowed; +} + +#define TYPE_TCG_ACCEL ACCEL_CLASS_NAME("tcg") + +static const TypeInfo tcg_accel_type = { + .name = TYPE_TCG_ACCEL, + .parent = TYPE_ACCEL, + .class_init = tcg_accel_class_init, +}; + +static void xen_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "Xen"; + ac->available = xen_available; + ac->init = xen_init; + ac->allowed = &xen_allowed; +} + +#define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen") + +static const TypeInfo xen_accel_type = { + .name = TYPE_XEN_ACCEL, + .parent = TYPE_ACCEL, + .class_init = xen_accel_class_init, +}; + +static void kvm_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "KVM"; + ac->available = kvm_available; + ac->init = kvm_init; + ac->allowed = &kvm_allowed; +} + +#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm") + +static const TypeInfo kvm_accel_type = { + .name = TYPE_KVM_ACCEL, + .parent = TYPE_ACCEL, + .class_init = kvm_accel_class_init, +}; + +static void qtest_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "QTest"; + ac->available = qtest_available; + ac->init = qtest_init_accel; + ac->allowed = &qtest_allowed; +} + +#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest") + +static const TypeInfo qtest_accel_type = { + .name = TYPE_QTEST_ACCEL, + .parent = TYPE_ACCEL, + .class_init = qtest_accel_class_init, +}; + +static void register_accel_types(void) +{ + type_register_static(&accel_type); + type_register_static(&tcg_accel_type); + type_register_static(&xen_accel_type); + type_register_static(&kvm_accel_type); + type_register_static(&qtest_accel_type); +} + +type_init(register_accel_types); diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 5537d74406..120ca0e5c0 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -24,6 +24,36 @@ #define HW_ACCEL_H #include "qemu/typedefs.h" +#include "qom/object.h" + +typedef struct AccelState { + /*< private >*/ + Object parent_obj; +} AccelState; + +typedef struct AccelClass { + /*< private >*/ + ObjectClass parent_class; + /*< public >*/ + + const char *opt_name; + const char *name; + int (*available)(void); + int (*init)(MachineClass *mc); + bool *allowed; +} AccelClass; + +#define TYPE_ACCEL "accel" + +#define ACCEL_CLASS_SUFFIX "-" TYPE_ACCEL +#define ACCEL_CLASS_NAME(a) (a ACCEL_CLASS_SUFFIX) + +#define ACCEL_CLASS(klass) \ + OBJECT_CLASS_CHECK(AccelClass, (klass), TYPE_ACCEL) +#define ACCEL(obj) \ + OBJECT_CHECK(AccelState, (obj), TYPE_ACCEL) +#define ACCEL_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AccelClass, (obj), TYPE_ACCEL) extern int tcg_tb_size; -- cgit v1.2.3 From f6dfb83547d0cf5aa938cce391ba33c32a97bed9 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:22 -0300 Subject: accel: Make AccelClass.available() optional When we move accel classes outside accel.c, the available() function won't be necessary anymore, because the classes will be registered only if the accelerator code is really enabled at build time. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel.c b/accel.c index a3e2fd9149..85177f1b52 100644 --- a/accel.c +++ b/accel.c @@ -82,7 +82,7 @@ int configure_accelerator(MachineClass *mc) fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf); continue; } - if (!acc->available()) { + if (acc->available && !acc->available()) { printf("%s not supported for this target\n", acc->name); continue; -- cgit v1.2.3 From b31f9acaaa9ddc5d0725efc2994b0d802d67ea38 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:23 -0300 Subject: accel: Report unknown accelerator as "not found" instead of "does not exist" As the accelerator classes won't be registered anymore if they are not enabled at compile time, saying "does not exist" may be misleading, as the accelerator may be simply disabled. Change the wording to just say "not found". Suggested-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel.c b/accel.c index 85177f1b52..fce6eabbf5 100644 --- a/accel.c +++ b/accel.c @@ -79,7 +79,7 @@ int configure_accelerator(MachineClass *mc) p = get_opt_name(buf, sizeof(buf), p, ':'); acc = accel_find(buf); if (!acc) { - fprintf(stderr, "\"%s\" accelerator does not exist.\n", buf); + fprintf(stderr, "\"%s\" accelerator not found.\n", buf); continue; } if (acc->available && !acc->available()) { -- cgit v1.2.3 From 782c3f2939a8faefa4c5a324dfb472a534048510 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:24 -0300 Subject: accel: Move KVM accel registration to kvm-all.c Note that this has an user-visible side-effect: instead of reporting "KVM is not supported for this target", QEMU binaries not supporting KVM will report "kvm accelerator does not exist". As kvm_availble() always return 1 when CONFIG_KVM is enabled, we don't need to set AccelClass.available anymore. kvm_enabled() is not being completely removed yet only because qmp_query_kvm() still uses it. This also allows us to make kvm_init() static. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 18 ------------------ include/sysemu/kvm.h | 2 -- kvm-all.c | 26 +++++++++++++++++++++++++- kvm-stub.c | 5 ----- 4 files changed, 25 insertions(+), 26 deletions(-) diff --git a/accel.c b/accel.c index fce6eabbf5..a20e42714c 100644 --- a/accel.c +++ b/accel.c @@ -149,23 +149,6 @@ static const TypeInfo xen_accel_type = { .class_init = xen_accel_class_init, }; -static void kvm_accel_class_init(ObjectClass *oc, void *data) -{ - AccelClass *ac = ACCEL_CLASS(oc); - ac->name = "KVM"; - ac->available = kvm_available; - ac->init = kvm_init; - ac->allowed = &kvm_allowed; -} - -#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm") - -static const TypeInfo kvm_accel_type = { - .name = TYPE_KVM_ACCEL, - .parent = TYPE_ACCEL, - .class_init = kvm_accel_class_init, -}; - static void qtest_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); @@ -188,7 +171,6 @@ static void register_accel_types(void) type_register_static(&accel_type); type_register_static(&tcg_accel_type); type_register_static(&xen_accel_type); - type_register_static(&kvm_accel_type); type_register_static(&qtest_accel_type); } diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 77ee240875..b0cd657f77 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -163,8 +163,6 @@ extern KVMState *kvm_state; /* external API */ -int kvm_init(MachineClass *mc); - int kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); int kvm_has_robust_singlestep(void); diff --git a/kvm-all.c b/kvm-all.c index 4afcd0551b..18dcd45496 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -25,6 +25,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "sysemu/sysemu.h" +#include "sysemu/accel.h" #include "hw/hw.h" #include "hw/pci/msi.h" #include "hw/s390x/adapter.h" @@ -106,6 +107,8 @@ struct KVMState #endif }; +#define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm") + KVMState *kvm_state; bool kvm_kernel_irqchip; bool kvm_async_interrupts_allowed; @@ -1377,7 +1380,7 @@ static int kvm_max_vcpus(KVMState *s) return (ret) ? ret : kvm_recommended_vcpus(s); } -int kvm_init(MachineClass *mc) +static int kvm_init(MachineClass *mc) { static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" @@ -2225,3 +2228,24 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target) } return r; } + +static void kvm_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "KVM"; + ac->init = kvm_init; + ac->allowed = &kvm_allowed; +} + +static const TypeInfo kvm_accel_type = { + .name = TYPE_KVM_ACCEL, + .parent = TYPE_ACCEL, + .class_init = kvm_accel_class_init, +}; + +static void kvm_type_init(void) +{ + type_register_static(&kvm_accel_type); +} + +type_init(kvm_type_init); diff --git a/kvm-stub.c b/kvm-stub.c index 8e7737caa9..43fc0dd01e 100644 --- a/kvm-stub.c +++ b/kvm-stub.c @@ -35,11 +35,6 @@ int kvm_init_vcpu(CPUState *cpu) return -ENOSYS; } -int kvm_init(MachineClass *mc) -{ - return -ENOSYS; -} - void kvm_flush_coalesced_mmio_buffer(void) { } -- cgit v1.2.3 From b152b05a35acc0ff3da5648fd5cb97136853838c Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:25 -0300 Subject: accel: Move Xen registration code to xen-common.c Note that this has an user-visible side-effect: instead of reporting "Xen is not supported for this target", QEMU binaries not supporting Xen will report "xen accelerator does not exist". As xen_available() always return 1 when CONFIG_XEN is enabled, we don't need to set AccelClass.available anymore. xen_enabled() is not being removed yet, but only because vl.c is still using it. This also allows us to make xen_init() static. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 18 ------------------ include/hw/xen/xen.h | 1 - xen-common-stub.c | 6 ------ xen-common.c | 25 ++++++++++++++++++++++++- 4 files changed, 24 insertions(+), 26 deletions(-) diff --git a/accel.c b/accel.c index a20e42714c..2cefbb0aa7 100644 --- a/accel.c +++ b/accel.c @@ -132,23 +132,6 @@ static const TypeInfo tcg_accel_type = { .class_init = tcg_accel_class_init, }; -static void xen_accel_class_init(ObjectClass *oc, void *data) -{ - AccelClass *ac = ACCEL_CLASS(oc); - ac->name = "Xen"; - ac->available = xen_available; - ac->init = xen_init; - ac->allowed = &xen_allowed; -} - -#define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen") - -static const TypeInfo xen_accel_type = { - .name = TYPE_XEN_ACCEL, - .parent = TYPE_ACCEL, - .class_init = xen_accel_class_init, -}; - static void qtest_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); @@ -170,7 +153,6 @@ static void register_accel_types(void) { type_register_static(&accel_type); type_register_static(&tcg_accel_type); - type_register_static(&xen_accel_type); type_register_static(&qtest_accel_type); } diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index f71f2d8963..b0ed04caa9 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -36,7 +36,6 @@ void xen_cmos_set_s3_resume(void *opaque, int irq, int level); qemu_irq *xen_interrupt_controller_init(void); -int xen_init(MachineClass *mc); void xenstore_store_pv_console_info(int i, struct CharDriverState *chr); #if defined(NEED_CPU_H) && !defined(CONFIG_USER_ONLY) diff --git a/xen-common-stub.c b/xen-common-stub.c index bd56ca2ce5..906f991f1e 100644 --- a/xen-common-stub.c +++ b/xen-common-stub.c @@ -11,9 +11,3 @@ void xenstore_store_pv_console_info(int i, CharDriverState *chr) { } - -int xen_init(MachineClass *mc) -{ - return -ENOSYS; -} - diff --git a/xen-common.c b/xen-common.c index f07b35e471..acc64d5cc8 100644 --- a/xen-common.c +++ b/xen-common.c @@ -11,6 +11,7 @@ #include "hw/xen/xen_backend.h" #include "qmp-commands.h" #include "sysemu/char.h" +#include "sysemu/accel.h" //#define DEBUG_XEN @@ -109,7 +110,7 @@ static void xen_change_state_handler(void *opaque, int running, } } -int xen_init(MachineClass *mc) +static int xen_init(MachineClass *mc) { xen_xc = xen_xc_interface_open(0, 0, 0); if (xen_xc == XC_HANDLER_INITIAL_VALUE) { @@ -121,3 +122,25 @@ int xen_init(MachineClass *mc) return 0; } +static void xen_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "Xen"; + ac->init = xen_init; + ac->allowed = &xen_allowed; +} + +#define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen") + +static const TypeInfo xen_accel_type = { + .name = TYPE_XEN_ACCEL, + .parent = TYPE_ACCEL, + .class_init = xen_accel_class_init, +}; + +static void xen_type_init(void) +{ + type_register_static(&xen_accel_type); +} + +type_init(xen_type_init); -- cgit v1.2.3 From 3a6ce5147f1273a5eec6a82fac9c8e6d085c1247 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:26 -0300 Subject: accel: Move qtest accel registration to qtest.c As qtest_availble() returns 1 only when CONFIG_POSIX is set, keep setting AccelClass.available to keep current behavior (this is different from what we did for KVM and Xen). This also allows us to make qtest_init_accel() static. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 18 ------------------ include/sysemu/qtest.h | 1 - qtest.c | 27 ++++++++++++++++++++++++++- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/accel.c b/accel.c index 2cefbb0aa7..2cf47337af 100644 --- a/accel.c +++ b/accel.c @@ -132,28 +132,10 @@ static const TypeInfo tcg_accel_type = { .class_init = tcg_accel_class_init, }; -static void qtest_accel_class_init(ObjectClass *oc, void *data) -{ - AccelClass *ac = ACCEL_CLASS(oc); - ac->name = "QTest"; - ac->available = qtest_available; - ac->init = qtest_init_accel; - ac->allowed = &qtest_allowed; -} - -#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest") - -static const TypeInfo qtest_accel_type = { - .name = TYPE_QTEST_ACCEL, - .parent = TYPE_ACCEL, - .class_init = qtest_accel_class_init, -}; - static void register_accel_types(void) { type_register_static(&accel_type); type_register_static(&tcg_accel_type); - type_register_static(&qtest_accel_type); } type_init(register_accel_types); diff --git a/include/sysemu/qtest.h b/include/sysemu/qtest.h index 95c9ade778..05473b75a5 100644 --- a/include/sysemu/qtest.h +++ b/include/sysemu/qtest.h @@ -26,7 +26,6 @@ static inline bool qtest_enabled(void) bool qtest_driver(void); -int qtest_init_accel(MachineClass *mc); void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp); static inline int qtest_available(void) diff --git a/qtest.c b/qtest.c index ef0d99191c..0af8b74752 100644 --- a/qtest.c +++ b/qtest.c @@ -17,6 +17,7 @@ #include "exec/ioport.h" #include "exec/memory.h" #include "hw/irq.h" +#include "sysemu/accel.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" #include "qemu/config-file.h" @@ -519,7 +520,7 @@ static void configure_qtest_icount(const char *options) qemu_opts_del(opts); } -int qtest_init_accel(MachineClass *mc) +static int qtest_init_accel(MachineClass *mc) { configure_qtest_icount("0"); return 0; @@ -557,3 +558,27 @@ bool qtest_driver(void) { return qtest_chr; } + +static void qtest_accel_class_init(ObjectClass *oc, void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "QTest"; + ac->available = qtest_available; + ac->init = qtest_init_accel; + ac->allowed = &qtest_allowed; +} + +#define TYPE_QTEST_ACCEL ACCEL_CLASS_NAME("qtest") + +static const TypeInfo qtest_accel_type = { + .name = TYPE_QTEST_ACCEL, + .parent = TYPE_ACCEL, + .class_init = qtest_accel_class_init, +}; + +static void qtest_type_init(void) +{ + type_register_static(&qtest_accel_type); +} + +type_init(qtest_type_init); -- cgit v1.2.3 From 32592e112fc9da1e4f6b48896e6473c49043d5dd Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:27 -0300 Subject: accel: Remove tcg_available() function As the function always return 1, it is not needed anymore. Signed-off-by: Eduardo Habkost Reviewed-by: Paolo Bonzini Signed-off-by: Paolo Bonzini --- accel.c | 1 - arch_init.c | 5 ----- include/sysemu/arch_init.h | 1 - 3 files changed, 7 deletions(-) diff --git a/accel.c b/accel.c index 2cf47337af..0f3fceeb7c 100644 --- a/accel.c +++ b/accel.c @@ -119,7 +119,6 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "tcg"; - ac->available = tcg_available; ac->init = tcg_init; ac->allowed = &tcg_allowed; } diff --git a/arch_init.c b/arch_init.c index c974f3fea9..9b3e25d805 100644 --- a/arch_init.c +++ b/arch_init.c @@ -1337,11 +1337,6 @@ void cpudef_init(void) #endif } -int tcg_available(void) -{ - return 1; -} - int kvm_available(void) { #ifdef CONFIG_KVM diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 769ec069b7..54b36c16c4 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -33,7 +33,6 @@ void do_smbios_option(QemuOpts *opts); void ram_mig_init(void); void cpudef_init(void); void audio_init(void); -int tcg_available(void); int kvm_available(void); int xen_available(void); -- cgit v1.2.3 From d95c8527e9662a54a8f98c2ced35736604b1b253 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:28 -0300 Subject: accel: Move accel init/allowed code to separate function Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/accel.c b/accel.c index 0f3fceeb7c..9241967cad 100644 --- a/accel.c +++ b/accel.c @@ -57,6 +57,17 @@ static AccelClass *accel_find(const char *opt_name) return ac; } +static int accel_init(AccelClass *acc, MachineClass *mc) +{ + int ret; + *(acc->allowed) = true; + ret = acc->init(mc); + if (ret < 0) { + *(acc->allowed) = false; + } + return ret; +} + int configure_accelerator(MachineClass *mc) { const char *p; @@ -87,14 +98,12 @@ int configure_accelerator(MachineClass *mc) acc->name); continue; } - *(acc->allowed) = true; - ret = acc->init(mc); + ret = accel_init(acc, mc); if (ret < 0) { init_failed = true; fprintf(stderr, "failed to initialize %s: %s\n", acc->name, strerror(-ret)); - *(acc->allowed) = false; } else { accel_initialised = true; } -- cgit v1.2.3 From 0d15da8e6fc3cbcd088476c561dcc88b9aae493a Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:29 -0300 Subject: accel: Rename 'init' method to 'init_machine' Today, all accelerator init functions affect some global state: * tcg_init() calls tcg_exec_init() and affects globals such as tcg_tcx, page size globals, and possibly others; * kvm_init() changes the kvm_state global, cpu_interrupt_handler, and possibly others; * xen_init() changes the xen_xc global, and registers a change state handler. With the new accelerator QOM classes, initialization may now be split in two steps: * instance_init() will do basic initialization that doesn't affect any global state and don't need MachineState or MachineClass data. This will allow probing code to safely create multiple accelerator objects on the fly just for reporting host/accelerator capabilities, for example. * accel_init_machine()/init_machine() will save the accelerator object in MachineState, and do initialization steps which still affect global state, machine state, or that need data from MachineClass or MachineState. To clarify the difference between those two steps, rename init() to init_machine(). Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 8 ++++---- include/sysemu/accel.h | 2 +- kvm-all.c | 2 +- qtest.c | 2 +- xen-common.c | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/accel.c b/accel.c index 9241967cad..b151d558a5 100644 --- a/accel.c +++ b/accel.c @@ -57,11 +57,11 @@ static AccelClass *accel_find(const char *opt_name) return ac; } -static int accel_init(AccelClass *acc, MachineClass *mc) +static int accel_init_machine(AccelClass *acc, MachineClass *mc) { int ret; *(acc->allowed) = true; - ret = acc->init(mc); + ret = acc->init_machine(mc); if (ret < 0) { *(acc->allowed) = false; } @@ -98,7 +98,7 @@ int configure_accelerator(MachineClass *mc) acc->name); continue; } - ret = accel_init(acc, mc); + ret = accel_init_machine(acc, mc); if (ret < 0) { init_failed = true; fprintf(stderr, "failed to initialize %s: %s\n", @@ -128,7 +128,7 @@ static void tcg_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "tcg"; - ac->init = tcg_init; + ac->init_machine = tcg_init; ac->allowed = &tcg_allowed; } diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 120ca0e5c0..8812cdae21 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -39,7 +39,7 @@ typedef struct AccelClass { const char *opt_name; const char *name; int (*available)(void); - int (*init)(MachineClass *mc); + int (*init_machine)(MachineClass *mc); bool *allowed; } AccelClass; diff --git a/kvm-all.c b/kvm-all.c index 18dcd45496..ce0e4c7e40 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -2233,7 +2233,7 @@ static void kvm_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "KVM"; - ac->init = kvm_init; + ac->init_machine = kvm_init; ac->allowed = &kvm_allowed; } diff --git a/qtest.c b/qtest.c index 0af8b74752..18e26fc41f 100644 --- a/qtest.c +++ b/qtest.c @@ -564,7 +564,7 @@ static void qtest_accel_class_init(ObjectClass *oc, void *data) AccelClass *ac = ACCEL_CLASS(oc); ac->name = "QTest"; ac->available = qtest_available; - ac->init = qtest_init_accel; + ac->init_machine = qtest_init_accel; ac->allowed = &qtest_allowed; } diff --git a/xen-common.c b/xen-common.c index acc64d5cc8..acb738f2ff 100644 --- a/xen-common.c +++ b/xen-common.c @@ -126,7 +126,7 @@ static void xen_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "Xen"; - ac->init = xen_init; + ac->init_machine = xen_init; ac->allowed = &xen_allowed; } -- cgit v1.2.3 From f6a1ef64408a5f7f52601589fef2a850b93d817e Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:30 -0300 Subject: accel: Pass MachineState object to accel init functions Most of the machine options and machine state information is in the MachineState object, not on the MachineClass. This will allow init functions to use the MachineState object directly instead of qemu_get_machine_opts() or the current_machine global. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 11 ++++++----- include/hw/boards.h | 2 -- include/qemu/typedefs.h | 1 + include/sysemu/accel.h | 4 ++-- kvm-all.c | 3 ++- qtest.c | 2 +- vl.c | 2 +- xen-common.c | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/accel.c b/accel.c index b151d558a5..6087ab32b7 100644 --- a/accel.c +++ b/accel.c @@ -24,6 +24,7 @@ */ #include "sysemu/accel.h" +#include "hw/boards.h" #include "qemu-common.h" #include "sysemu/arch_init.h" #include "sysemu/sysemu.h" @@ -35,7 +36,7 @@ int tcg_tb_size; static bool tcg_allowed = true; -static int tcg_init(MachineClass *mc) +static int tcg_init(MachineState *ms) { tcg_exec_init(tcg_tb_size * 1024 * 1024); return 0; @@ -57,18 +58,18 @@ static AccelClass *accel_find(const char *opt_name) return ac; } -static int accel_init_machine(AccelClass *acc, MachineClass *mc) +static int accel_init_machine(AccelClass *acc, MachineState *ms) { int ret; *(acc->allowed) = true; - ret = acc->init_machine(mc); + ret = acc->init_machine(ms); if (ret < 0) { *(acc->allowed) = false; } return ret; } -int configure_accelerator(MachineClass *mc) +int configure_accelerator(MachineState *ms) { const char *p; char buf[10]; @@ -98,7 +99,7 @@ int configure_accelerator(MachineClass *mc) acc->name); continue; } - ret = accel_init_machine(acc, mc); + ret = accel_init_machine(acc, ms); if (ret < 0) { init_failed = true; fprintf(stderr, "failed to initialize %s: %s\n", diff --git a/include/hw/boards.h b/include/hw/boards.h index dfb6718dc1..0058c49d8d 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -9,8 +9,6 @@ #include "qom/object.h" -typedef struct MachineState MachineState; - typedef void QEMUMachineInitFunc(MachineState *ms); typedef void QEMUMachineResetFunc(void); diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 5f20b0e263..04df51b6fc 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -32,6 +32,7 @@ typedef struct MemoryMappingList MemoryMappingList; typedef struct QEMUMachine QEMUMachine; typedef struct MachineClass MachineClass; +typedef struct MachineState MachineState; typedef struct NICInfo NICInfo; typedef struct HCIInfo HCIInfo; typedef struct AudioState AudioState; diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 8812cdae21..997720f36c 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -39,7 +39,7 @@ typedef struct AccelClass { const char *opt_name; const char *name; int (*available)(void); - int (*init_machine)(MachineClass *mc); + int (*init_machine)(MachineState *ms); bool *allowed; } AccelClass; @@ -57,6 +57,6 @@ typedef struct AccelClass { extern int tcg_tb_size; -int configure_accelerator(MachineClass *mc); +int configure_accelerator(MachineState *ms); #endif diff --git a/kvm-all.c b/kvm-all.c index ce0e4c7e40..0a9de929e0 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -1380,8 +1380,9 @@ static int kvm_max_vcpus(KVMState *s) return (ret) ? ret : kvm_recommended_vcpus(s); } -static int kvm_init(MachineClass *mc) +static int kvm_init(MachineState *ms) { + MachineClass *mc = MACHINE_GET_CLASS(ms); static const char upgrade_note[] = "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" "(see http://sourceforge.net/projects/kvm).\n"; diff --git a/qtest.c b/qtest.c index 18e26fc41f..4b85995de0 100644 --- a/qtest.c +++ b/qtest.c @@ -520,7 +520,7 @@ static void configure_qtest_icount(const char *options) qemu_opts_del(opts); } -static int qtest_init_accel(MachineClass *mc) +static int qtest_init_accel(MachineState *ms) { configure_qtest_icount("0"); return 0; diff --git a/vl.c b/vl.c index c3def21666..020b7c3d76 100644 --- a/vl.c +++ b/vl.c @@ -4179,7 +4179,7 @@ int main(int argc, char **argv, char **envp) exit(1); } - configure_accelerator(machine_class); + configure_accelerator(current_machine); if (qtest_chrdev) { Error *local_err = NULL; diff --git a/xen-common.c b/xen-common.c index acb738f2ff..56359ca725 100644 --- a/xen-common.c +++ b/xen-common.c @@ -110,7 +110,7 @@ static void xen_change_state_handler(void *opaque, int running, } } -static int xen_init(MachineClass *mc) +static int xen_init(MachineState *ms) { xen_xc = xen_xc_interface_open(0, 0, 0); if (xen_xc == XC_HANDLER_INITIAL_VALUE) { -- cgit v1.2.3 From ac2da55e01b1a84e6bba32768211201dec230232 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:31 -0300 Subject: accel: Create accel object when initializing machine Create an actual TYPE_ACCEL object when initializing a machine. This will allow accelerator classes to implement some initialization on instance_init, and to save state on the TYPE_ACCEL object. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- accel.c | 7 +++++++ include/hw/boards.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/accel.c b/accel.c index 6087ab32b7..74e41daaa5 100644 --- a/accel.c +++ b/accel.c @@ -32,6 +32,7 @@ #include "sysemu/qtest.h" #include "hw/xen/xen.h" #include "qom/object.h" +#include "hw/boards.h" int tcg_tb_size; static bool tcg_allowed = true; @@ -60,11 +61,17 @@ static AccelClass *accel_find(const char *opt_name) static int accel_init_machine(AccelClass *acc, MachineState *ms) { + ObjectClass *oc = OBJECT_CLASS(acc); + const char *cname = object_class_get_name(oc); + AccelState *accel = ACCEL(object_new(cname)); int ret; + ms->accelerator = accel; *(acc->allowed) = true; ret = acc->init_machine(ms); if (ret < 0) { + ms->accelerator = NULL; *(acc->allowed) = false; + object_unref(OBJECT(accel)); } return ret; } diff --git a/include/hw/boards.h b/include/hw/boards.h index 0058c49d8d..6a60c3cf97 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -5,6 +5,7 @@ #include "qemu/typedefs.h" #include "sysemu/blockdev.h" +#include "sysemu/accel.h" #include "hw/qdev.h" #include "qom/object.h" @@ -131,6 +132,7 @@ struct MachineState { char *kernel_cmdline; char *initrd_filename; const char *cpu_model; + AccelState *accelerator; }; #endif -- cgit v1.2.3 From fc02086b5ab8de50ce8234cf8f42b254de9e5d91 Mon Sep 17 00:00:00 2001 From: Eduardo Habkost Date: Fri, 26 Sep 2014 17:45:32 -0300 Subject: kvm: Make KVMState be the TYPE_KVM_ACCEL instance struct Now that we create an accel object before calling machine_init, we can simply use the accel object to save all KVMState data, instead of allocationg KVMState manually. Reviewed-by: Paolo Bonzini Signed-off-by: Eduardo Habkost Signed-off-by: Paolo Bonzini --- kvm-all.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/kvm-all.c b/kvm-all.c index 0a9de929e0..e98a7c74a7 100644 --- a/kvm-all.c +++ b/kvm-all.c @@ -71,8 +71,10 @@ typedef struct KVMSlot typedef struct kvm_dirty_log KVMDirtyLog; -struct KVMState +typedef struct KVMState { + AccelState parent_obj; + KVMSlot *slots; int nr_slots; int fd; @@ -105,10 +107,13 @@ struct KVMState QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; bool direct_msi; #endif -}; +} KVMState; #define TYPE_KVM_ACCEL ACCEL_CLASS_NAME("kvm") +#define KVM_STATE(obj) \ + OBJECT_CHECK(KVMState, (obj), TYPE_KVM_ACCEL) + KVMState *kvm_state; bool kvm_kernel_irqchip; bool kvm_async_interrupts_allowed; @@ -1401,7 +1406,7 @@ static int kvm_init(MachineState *ms) int i, type = 0; const char *kvm_type; - s = g_malloc0(sizeof(KVMState)); + s = KVM_STATE(ms->accelerator); /* * On systems where the kernel can support different base page @@ -1590,7 +1595,6 @@ err: close(s->fd); } g_free(s->slots); - g_free(s); return ret; } @@ -2242,6 +2246,7 @@ static const TypeInfo kvm_accel_type = { .name = TYPE_KVM_ACCEL, .parent = TYPE_ACCEL, .class_init = kvm_accel_class_init, + .instance_size = sizeof(KVMState), }; static void kvm_type_init(void) -- cgit v1.2.3 From cdebec5e40bd0af82da0659f37af85ee2aa2c9d1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 6 Oct 2014 16:49:57 +0200 Subject: linuxboot: compute initrd loading address Even though hw/i386/pc.c tries to compute a valid loading address for the initrd, close to the top of RAM, this does not take into account other data that is malloced into that memory by SeaBIOS. Luckily we can easily look at the memory map to find out how much memory is used up there. This patch places the initrd in the first four gigabytes, below the first hole (as returned by INT 15h, AX=e801h). Without this patch: [ 0.000000] init_memory_mapping: [mem 0x07000000-0x07fdffff] [ 0.000000] RAMDISK: [mem 0x0710a000-0x07fd7fff] With this patch: [ 0.000000] init_memory_mapping: [mem 0x07000000-0x07fdffff] [ 0.000000] RAMDISK: [mem 0x07112000-0x07fdffff] So linuxboot is able to use the 64k that were added as padding for QEMU <= 2.1. Acked-by: Michael S. Tsirkin Signed-off-by: Paolo Bonzini --- pc-bios/linuxboot.bin | Bin 1024 -> 1024 bytes pc-bios/optionrom/linuxboot.S | 47 ++++++++++++++++++++++++++++++++++++++---- pc-bios/optionrom/optionrom.h | 21 ++++++++++++++++--- 3 files changed, 61 insertions(+), 7 deletions(-) diff --git a/pc-bios/linuxboot.bin b/pc-bios/linuxboot.bin index e7c36694f9..130103fb73 100644 Binary files a/pc-bios/linuxboot.bin and b/pc-bios/linuxboot.bin differ diff --git a/pc-bios/optionrom/linuxboot.S b/pc-bios/optionrom/linuxboot.S index 748c831160..5bc0af08e0 100644 --- a/pc-bios/optionrom/linuxboot.S +++ b/pc-bios/optionrom/linuxboot.S @@ -76,14 +76,45 @@ boot_kernel: copy_kernel: + /* Compute initrd address */ + mov $0xe801, %ax + xor %cx, %cx + xor %dx, %dx + int $0x15 + + /* Output could be in AX/BX or CX/DX */ + or %cx, %cx + jnz 1f + or %dx, %dx + jnz 1f + mov %ax, %cx + mov %bx, %dx +1: + + or %dx, %dx + jnz 2f + addw $1024, %cx /* add 1 MB */ + movzwl %cx, %edi + shll $10, %edi /* convert to bytes */ + jmp 3f + +2: + addw $16777216 >> 16, %dx /* add 16 MB */ + movzwl %dx, %edi + shll $16, %edi /* convert to bytes */ + +3: + read_fw FW_CFG_INITRD_SIZE + subl %eax, %edi + andl $-4096, %edi /* EDI = start of initrd */ /* We need to load the kernel into memory we can't access in 16 bit mode, so let's get into 32 bit mode, write the kernel and jump back again. */ /* Reserve space on the stack for our GDT descriptor. */ - mov %esp, %ebp - sub $16, %esp + mov %esp, %ebp + sub $16, %esp /* Now create the GDT descriptor */ movw $((3 * 8) - 1), -16(%bp) @@ -108,10 +139,18 @@ copy_kernel: /* We're now running in 16-bit CS, but 32-bit ES! */ /* Load kernel and initrd */ + pushl %edi + read_fw_blob_addr32_edi(FW_CFG_INITRD) read_fw_blob_addr32(FW_CFG_KERNEL) - read_fw_blob_addr32(FW_CFG_INITRD) read_fw_blob_addr32(FW_CFG_CMDLINE) - read_fw_blob_addr32(FW_CFG_SETUP) + + read_fw FW_CFG_SETUP_ADDR + mov %eax, %edi + mov %eax, %ebx + read_fw_blob_addr32_edi(FW_CFG_SETUP) + + /* Update the header with the initrd address we chose above */ + popl %es:0x218(%ebx) /* And now jump into Linux! */ mov $0, %eax diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h index ce436085d9..f1a9021ec1 100644 --- a/pc-bios/optionrom/optionrom.h +++ b/pc-bios/optionrom/optionrom.h @@ -51,8 +51,6 @@ .endm #define read_fw_blob_pre(var) \ - read_fw var ## _ADDR; \ - mov %eax, %edi; \ read_fw var ## _SIZE; \ mov %eax, %ecx; \ mov $var ## _DATA, %ax; \ @@ -68,6 +66,8 @@ * Clobbers: %eax, %edx, %es, %ecx, %edi */ #define read_fw_blob(var) \ + read_fw var ## _ADDR; \ + mov %eax, %edi; \ read_fw_blob_pre(var); \ /* old as(1) doesn't like this insn so emit the bytes instead: \ rep insb (%dx), %es:(%edi); \ @@ -80,7 +80,22 @@ * * Clobbers: %eax, %edx, %es, %ecx, %edi */ -#define read_fw_blob_addr32(var) \ +#define read_fw_blob_addr32(var) \ + read_fw var ## _ADDR; \ + mov %eax, %edi; \ + read_fw_blob_pre(var); \ + /* old as(1) doesn't like this insn so emit the bytes instead: \ + addr32 rep insb (%dx), %es:(%edi); \ + */ \ + .dc.b 0x67,0xf3,0x6c + +/* + * Read a blob from the fw_cfg device in forced addr32 mode, address is in %edi. + * Requires _SIZE and _DATA values for the parameter. + * + * Clobbers: %eax, %edx, %edi, %es, %ecx + */ +#define read_fw_blob_addr32_edi(var) \ read_fw_blob_pre(var); \ /* old as(1) doesn't like this insn so emit the bytes instead: \ addr32 rep insb (%dx), %es:(%edi); \ -- cgit v1.2.3 From 35e4e96c4d5bfcf8a22930d8e99f7c8c44420062 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 8 Oct 2014 01:19:00 +0200 Subject: virtio-scsi: fix use-after-free of VirtIOSCSIReq scsi_req_continue can complete the request and cause the VirtIOSCSIReq to be freed. Fetch req->sreq just once to avoid the bug. Reported-by: Richard Jones Tested-by: Richard Jones Reviewed-by: Fam Zheng Signed-off-by: Paolo Bonzini --- hw/scsi/virtio-scsi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 203e62449a..6c02fe2b9a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -545,11 +545,12 @@ bool virtio_scsi_handle_cmd_req_prepare(VirtIOSCSI *s, VirtIOSCSIReq *req) void virtio_scsi_handle_cmd_req_submit(VirtIOSCSI *s, VirtIOSCSIReq *req) { - if (scsi_req_enqueue(req->sreq)) { - scsi_req_continue(req->sreq); + SCSIRequest *sreq = req->sreq; + if (scsi_req_enqueue(sreq)) { + scsi_req_continue(sreq); } - bdrv_io_unplug(req->sreq->dev->conf.bs); - scsi_req_unref(req->sreq); + bdrv_io_unplug(sreq->dev->conf.bs); + scsi_req_unref(sreq); } static void virtio_scsi_handle_cmd(VirtIODevice *vdev, VirtQueue *vq) -- cgit v1.2.3 From 5748e4c2be4f5c24c691f91328be02a9c4cb3063 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 8 Oct 2014 07:11:54 -0500 Subject: qemu-error: Add error_vreport() Needed to nicely print socket error reports. Signed-off-by: Corey Minyard Signed-off-by: Paolo Bonzini --- include/qemu/error-report.h | 1 + util/qemu-error.c | 23 ++++++++++++++++++----- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h index 000eae3957..7ab235590e 100644 --- a/include/qemu/error-report.h +++ b/include/qemu/error-report.h @@ -38,6 +38,7 @@ void error_vprintf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); void error_printf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void error_printf_unless_qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void error_set_progname(const char *argv0); +void error_vreport(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); const char *error_get_progname(void); extern bool enable_timestamp_msg; diff --git a/util/qemu-error.c b/util/qemu-error.c index 7b167fd06b..9bba5f53d8 100644 --- a/util/qemu-error.c +++ b/util/qemu-error.c @@ -199,14 +199,13 @@ static void error_print_loc(void) bool enable_timestamp_msg; /* * Print an error message to current monitor if we have one, else to stderr. - * Format arguments like sprintf(). The result should not contain + * Format arguments like vsprintf(). The result should not contain * newlines. * Prepend the current location and append a newline. * It's wrong to call this in a QMP monitor. Use qerror_report() there. */ -void error_report(const char *fmt, ...) +void error_vreport(const char *fmt, va_list ap) { - va_list ap; GTimeVal tv; gchar *timestr; @@ -218,8 +217,22 @@ void error_report(const char *fmt, ...) } error_print_loc(); - va_start(ap, fmt); error_vprintf(fmt, ap); - va_end(ap); error_printf("\n"); } + +/* + * Print an error message to current monitor if we have one, else to stderr. + * Format arguments like sprintf(). The result should not contain + * newlines. + * Prepend the current location and append a newline. + * It's wrong to call this in a QMP monitor. Use qerror_report() there. + */ +void error_report(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + error_vreport(fmt, ap); + va_end(ap); +} -- cgit v1.2.3 From 5179502918d0c230afb50d8b95247de440fccdd5 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 8 Oct 2014 07:11:56 -0500 Subject: qemu-sockets: Add error to non-blocking connect handler An error value here would be quite handy and more consistent with the rest of the code. Signed-off-by: Corey Minyard [Make sure SO_ERROR value is passed to error_setg_errno. - Paolo] Signed-off-by: Paolo Bonzini --- include/qemu/sockets.h | 2 +- migration-tcp.c | 4 ++-- migration-unix.c | 4 ++-- qemu-char.c | 7 +++++-- util/qemu-sockets.c | 20 +++++++++++++++----- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/include/qemu/sockets.h b/include/qemu/sockets.h index fdbb196787..f47dae614a 100644 --- a/include/qemu/sockets.h +++ b/include/qemu/sockets.h @@ -47,7 +47,7 @@ int recv_all(int fd, void *buf, int len1, bool single_read); /* callback function for nonblocking connect * valid fd on success, negative error code on failure */ -typedef void NonBlockingConnectHandler(int fd, void *opaque); +typedef void NonBlockingConnectHandler(int fd, Error *errp, void *opaque); InetSocketAddress *inet_parse(const char *str, Error **errp); int inet_listen_opts(QemuOpts *opts, int port_offset, Error **errp); diff --git a/migration-tcp.c b/migration-tcp.c index 2e34517bb9..91c9cf381e 100644 --- a/migration-tcp.c +++ b/migration-tcp.c @@ -33,12 +33,12 @@ do { } while (0) #endif -static void tcp_wait_for_connect(int fd, void *opaque) +static void tcp_wait_for_connect(int fd, Error *err, void *opaque) { MigrationState *s = opaque; if (fd < 0) { - DPRINTF("migrate connect error\n"); + DPRINTF("migrate connect error: %s\n", error_get_pretty(err)); s->file = NULL; migrate_fd_error(s); } else { diff --git a/migration-unix.c b/migration-unix.c index 0a5f8a1332..1cdadfbc83 100644 --- a/migration-unix.c +++ b/migration-unix.c @@ -33,12 +33,12 @@ do { } while (0) #endif -static void unix_wait_for_connect(int fd, void *opaque) +static void unix_wait_for_connect(int fd, Error *err, void *opaque) { MigrationState *s = opaque; if (fd < 0) { - DPRINTF("migrate connect error\n"); + DPRINTF("migrate connect error: %s\n", error_get_pretty(err)); s->file = NULL; migrate_fd_error(s); } else { diff --git a/qemu-char.c b/qemu-char.c index 62af0efa18..c71805ad8d 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -3042,11 +3042,13 @@ static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd) } } -static void qemu_chr_socket_connected(int fd, void *opaque) +static void qemu_chr_socket_connected(int fd, Error *err, void *opaque) { CharDriverState *chr = opaque; if (fd < 0) { + error_report("Unable to connect to char device %s: %s", + chr->label, error_get_pretty(err)); qemu_chr_socket_restart_timer(chr); return; } @@ -4079,7 +4081,8 @@ static gboolean socket_reconnect_timeout(gpointer opaque) } if (!qemu_chr_open_socket_fd(chr, &err)) { - error_report("Unable to connect to char device %s\n", chr->label); + error_report("Unable to connect to char device %s: %s\n", + chr->label, error_get_pretty(err)); qemu_chr_socket_restart_timer(chr); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 1eef590af5..a76bb3c913 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -234,6 +234,7 @@ static void wait_for_connect(void *opaque) int val = 0, rc = 0; socklen_t valsize = sizeof(val); bool in_progress; + Error *err = NULL; qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); @@ -244,10 +245,12 @@ static void wait_for_connect(void *opaque) /* update rc to contain error */ if (!rc && val) { rc = -1; + errno = val; } /* connect error */ if (rc < 0) { + error_setg_errno(&err, errno, "Error connecting to socket"); closesocket(s->fd); s->fd = rc; } @@ -257,9 +260,14 @@ static void wait_for_connect(void *opaque) while (s->current_addr->ai_next != NULL && s->fd < 0) { s->current_addr = s->current_addr->ai_next; s->fd = inet_connect_addr(s->current_addr, &in_progress, s, NULL); + if (s->fd < 0) { + error_free(err); + err = NULL; + error_setg_errno(&err, errno, "Unable to start socket connect"); + } /* connect in progress */ if (in_progress) { - return; + goto out; } } @@ -267,9 +275,11 @@ static void wait_for_connect(void *opaque) } if (s->callback) { - s->callback(s->fd, s->opaque); + s->callback(s->fd, err, s->opaque); } g_free(s); +out: + error_free(err); } static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, @@ -401,7 +411,7 @@ int inet_connect_opts(QemuOpts *opts, Error **errp, return sock; } else { if (callback) { - callback(sock, opaque); + callback(sock, NULL, opaque); } } g_free(connect_state); @@ -769,7 +779,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, } else if (rc >= 0) { /* non blocking socket immediate success, call callback */ if (callback != NULL) { - callback(sock, opaque); + callback(sock, NULL, opaque); } } @@ -919,7 +929,7 @@ int socket_connect(SocketAddress *addr, Error **errp, fd = monitor_get_fd(cur_mon, addr->fd->str, errp); if (fd >= 0 && callback) { qemu_set_nonblock(fd); - callback(fd, opaque); + callback(fd, NULL, opaque); } break; -- cgit v1.2.3 From 5008e5b7b817b5ea2b788203122cd50e7c16e599 Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Wed, 8 Oct 2014 07:11:55 -0500 Subject: qemu-char: Fix reconnect socket error reporting If reconnect was set, errors wouldn't always be reported. Fix that and also only report a connect error once until a connection has been made. The primary purpose of this is to tell the user that a connection failed so they can know they need to figure out what went wrong. So we don't want to spew too much out here, just enough so they know. Signed-off-by: Corey Minyard Signed-off-by: Paolo Bonzini --- qemu-char.c | 52 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/qemu-char.c b/qemu-char.c index c71805ad8d..bd0709bab5 100644 --- a/qemu-char.c +++ b/qemu-char.c @@ -2509,6 +2509,7 @@ typedef struct { guint reconnect_timer; int64_t reconnect_time; + bool connect_err_reported; } TCPCharDriver; static gboolean socket_reconnect_timeout(gpointer opaque); @@ -2521,6 +2522,19 @@ static void qemu_chr_socket_restart_timer(CharDriverState *chr) socket_reconnect_timeout, chr); } +static void check_report_connect_error(CharDriverState *chr, + Error *err) +{ + TCPCharDriver *s = chr->opaque; + + if (!s->connect_err_reported) { + error_report("Unable to connect character device %s: %s", + chr->label, error_get_pretty(err)); + s->connect_err_reported = true; + } + qemu_chr_socket_restart_timer(chr); +} + static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaque); #ifndef _WIN32 @@ -3045,14 +3059,14 @@ static void qemu_chr_finish_socket_connection(CharDriverState *chr, int fd) static void qemu_chr_socket_connected(int fd, Error *err, void *opaque) { CharDriverState *chr = opaque; + TCPCharDriver *s = chr->opaque; if (fd < 0) { - error_report("Unable to connect to char device %s: %s", - chr->label, error_get_pretty(err)); - qemu_chr_socket_restart_timer(chr); + check_report_connect_error(chr, err); return; } + s->connect_err_reported = false; qemu_chr_finish_socket_connection(chr, fd); } @@ -4068,11 +4082,19 @@ static CharDriverState *qmp_chardev_open_parallel(ChardevHostdev *parallel, #endif /* WIN32 */ +static void socket_try_connect(CharDriverState *chr) +{ + Error *err = NULL; + + if (!qemu_chr_open_socket_fd(chr, &err)) { + check_report_connect_error(chr, err); + } +} + static gboolean socket_reconnect_timeout(gpointer opaque) { CharDriverState *chr = opaque; TCPCharDriver *s = chr->opaque; - Error *err; s->reconnect_timer = 0; @@ -4080,11 +4102,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque) return false; } - if (!qemu_chr_open_socket_fd(chr, &err)) { - error_report("Unable to connect to char device %s: %s\n", - chr->label, error_get_pretty(err)); - qemu_chr_socket_restart_timer(chr); - } + socket_try_connect(chr); return false; } @@ -4136,15 +4154,13 @@ static CharDriverState *qmp_chardev_open_socket(ChardevSocket *sock, s->reconnect_time = reconnect; } - if (!qemu_chr_open_socket_fd(chr, errp)) { - if (s->reconnect_time) { - qemu_chr_socket_restart_timer(chr); - } else { - g_free(s); - g_free(chr->filename); - g_free(chr); - return NULL; - } + if (s->reconnect_time) { + socket_try_connect(chr); + } else if (!qemu_chr_open_socket_fd(chr, errp)) { + g_free(s); + g_free(chr->filename); + g_free(chr); + return NULL; } if (is_listen && is_waitconnect) { -- cgit v1.2.3