Allow a client chardev to reconnect if disconnected
cover Allow a client chardev to reconnect if disconnected
This patch set was part of the IPMI patches, but people have asked it to be separated out because it's useful by itself and the IPMI patches are stuck in legal limbo.
I have left the patch to move allocating CharDriverState in one place (patch 1). I didn't get much opinion either way, but I think it is an improvement and a net reduction in code. I also added a patch (patch 6) that goes another step there, it makes the error handling in qmp_chardev_add() consistent and is also a net reduction in code.
The main patches to add the feature are patch 2 and patch 3.
The rest are little possible bug fixes that I noticed while working in the code.
I went through all the comments and I believe I fixed everything.
Thanks to everyone that reviewed this before.
PATCH 1/7 qemu-char: Allocate CharDriverState in qemu_chr_new_from_opts
From: Corey Minyard <[email protected]>
This allocates the CharDriverState structure and passes it in to the open routine. This allows a coming option to automatically attempt to reconnect a chardev if the connection fails. The chardev has to be kept around so a reconnect can be done on it.
--- backends/baum.c | 6 +- backends/msmouse.c | 5 +- hw/misc/ivshmem.c | 11 ++- include/sysemu/char.h | 9 ++- include/ui/console.h | 4 +- include/ui/qemu-spice.h | 9 ++- qemu-char.c | 189 ++++++++++++++++++++++-------------------------- spice-qemu-char.c | 15 ++-- ui/console.c | 10 +-- ui/gtk.c | 5 +- 10 files changed, 122 insertions(+), 141 deletions(-)
backends/baum.c +2 -4
@@ -561,10 +561,9 @@ static void baum_close(struct CharDriverState *chr)g_free (baum); } -CharDriverState *chr_baum_init (void ) +CharDriverState *chr_baum_init (CharDriverState *chr) {BaumDriverState *baum; -CharDriverState *chr;brlapi_handle_t *handle;#ifdef CONFIG_SDL SDL_SysWMinfo info;
@@ -572,7 +571,7 @@ CharDriverState *chr_baum_init(void)int tty; baum =g_malloc0 (sizeof (BaumDriverState)); - baum->chr = chr =g_malloc0 (sizeof (CharDriverState)); + baum->chr = chr; chr->opaque = baum; chr->chr_write = baum_write;
@@ -618,7 +617,6 @@ fail:brlapi__closeConnection (handle); fail_handle:g_free (handle); -g_free (chr);g_free (baum);return NULL ; }
backends/msmouse.c +1 -4
@@ -63,11 +63,8 @@ static void msmouse_chr_close (struct CharDriverState *chr)g_free (chr); } -CharDriverState *qemu_chr_open_msmouse (void ) +CharDriverState *qemu_chr_open_msmouse (CharDriverState *chr) { -CharDriverState *chr; - - chr =g_malloc0 (sizeof (CharDriverState)); chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; chr->explicit_be_open = true;
hw/misc/ivshmem.c +8 -3
@@ -291,12 +291,17 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier * {/* create a event character device based on the passed eventfd */ IVShmemState *s = opaque; -CharDriverState * chr; +CharDriverState *chr, *newchr;int eventfd =event_notifier_get_fd (n); - chr =qemu_chr_open_eventfd (eventfd); - + newchr =g_malloc0 (sizeof (*chr)); +if (newchr ==NULL ) { +fprintf (stderr,"creating eventfd for eventfd %d failed\n" , eventfd); +exit (-1 ); + } + chr =qemu_chr_open_eventfd (newchr, eventfd);if (chr ==NULL ) { +g_free (newchr);fprintf (stderr,"creating eventfd for eventfd %d failed\n" , eventfd);exit (-1 ); }
include/sysemu/char.h +5 -4
@@ -292,21 +292,22 @@ CharDriverState *qemu_chr_find(const char *name);QemuOpts *qemu_chr_parse_compat (const char *label,const char *filename); -void register_char_driver (const char *name,CharDriverState *(*open)(QemuOpts *)); +void register_char_driver (const char *name, +CharDriverState *(*open)(CharDriverState *,QemuOpts *));void register_char_driver_qapi (const char *name,ChardevBackendKind kind,void (*parse)(QemuOpts *opts,ChardevBackend *backend,Error **errp));/* add an eventfd to the qemu devices that are polled */ -CharDriverState *qemu_chr_open_eventfd (int eventfd); +CharDriverState *qemu_chr_open_eventfd (CharDriverState *chr,int eventfd);extern int term_escape_char;CharDriverState *qemu_char_get_next_serial (void );/* msmouse */ -CharDriverState *qemu_chr_open_msmouse (void ); +CharDriverState *qemu_chr_open_msmouse (CharDriverState *chr);/* baum.c */ -CharDriverState *chr_baum_init (void ); +CharDriverState *chr_baum_init (CharDriverState *chr);#endif
include/ui/console.h +2 -2
@@ -299,9 +299,9 @@ void qemu_console_copy(QemuConsole *con, int src_x, int src_y,DisplaySurface *qemu_console_surface (QemuConsole *con);DisplayState *qemu_console_displaystate (QemuConsole *console); -typedef CharDriverState *(VcHandler )(ChardevVC *vc); +typedef CharDriverState *(VcHandler )(CharDriverState *chr,ChardevVC *vc); -CharDriverState *vc_init (ChardevVC *vc); +CharDriverState *vc_init (CharDriverState *chr,ChardevVC *vc);void register_vc_handler (VcHandler *handler);/* sdl.c */
include/ui/qemu-spice.h +6 -3
@@ -48,12 +48,15 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,void do_info_spice_print (Monitor *mon,const QObject *data);void do_info_spice (Monitor *mon,QObject **ret_data); -CharDriverState *qemu_chr_open_spice_vmc (const char *type); +CharDriverState *qemu_chr_open_spice_vmc (CharDriverState *chr, +const char *type);#if SPICE_SERVER_VERSION >=0x000c02 -CharDriverState *qemu_chr_open_spice_port (const char *name); +CharDriverState *qemu_chr_open_spice_port (CharDriverState *chr, +const char *name);void qemu_spice_register_ports (void );#else -static inline CharDriverState *qemu_chr_open_spice_port (const char *name) +static inline CharDriverState *qemu_chr_open_spice_port (CharDriverState *chr, +const char *name) {return NULL ; }#endif
qemu-char.c +88 -101
576 lines not on this page · raw message
spice-qemu-char.c +6 -9
@@ -261,14 +261,11 @@ static void print_allowed_subtypes(void)fprintf (stderr,"\n" ); } -static CharDriverState *chr_open (const char *subtype, +static CharDriverState *chr_open (CharDriverState *chr,const char *subtype,void (*set_fe_open)(struct CharDriverState *,int )) - { -CharDriverState *chr;SpiceCharDriver *s; - chr =g_malloc0 (sizeof (CharDriverState)); s =g_malloc0 (sizeof (SpiceCharDriver)); s->chr = chr; s->active = false;
@@ -286,7 +283,7 @@ static CharDriverState *chr_open(const char *subtype,return chr; } -CharDriverState *qemu_chr_open_spice_vmc (const char *type) +CharDriverState *qemu_chr_open_spice_vmc (CharDriverState *chr,const char *type) {const char **psubtype =spice_server_char_device_recognized_subtypes ();
@@ -306,13 +303,13 @@ CharDriverState *qemu_chr_open_spice_vmc(const char *type)return NULL ; } -return chr_open (type, spice_vmc_set_fe_open); +return chr_open (chr, type, spice_vmc_set_fe_open); }#if SPICE_SERVER_VERSION >=0x000c02 -CharDriverState *qemu_chr_open_spice_port (const char *name) +CharDriverState *qemu_chr_open_spice_port (CharDriverState *chr, +const char *name) { -CharDriverState *chr;SpiceCharDriver *s;if (name ==NULL ) {
@@ -320,7 +317,7 @@ CharDriverState *qemu_chr_open_spice_port(const char *name)return NULL ; } - chr =chr_open ("port" , spice_port_set_fe_open); + chr =chr_open (chr,"port" , spice_port_set_fe_open); s = chr->opaque; s->sin.portname =g_strdup (name);
ui/console.c +3 -7
@@ -1724,15 +1724,12 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init (chr); } -static CharDriverState *text_console_init (ChardevVC *vc) +static CharDriverState *text_console_init (CharDriverState *chr,ChardevVC *vc) { -CharDriverState *chr;QemuConsole *s;unsigned width =0 ;unsigned height =0 ; - chr =g_malloc0 (sizeof (CharDriverState)); -if (vc->has_width) { width = vc->width; }else if (vc->has_cols) {
@@ -1754,7 +1751,6 @@ static CharDriverState *text_console_init(ChardevVC *vc) }if (!s) { -g_free (chr);return NULL ; }
@@ -1774,9 +1770,9 @@ static CharDriverState *text_console_init(ChardevVC *vc)static VcHandler *vc_handler = text_console_init; -CharDriverState *vc_init (ChardevVC *vc) +CharDriverState *vc_init (CharDriverState *chr,ChardevVC *vc) { -return vc_handler (vc); +return vc_handler (chr, vc); }void register_vc_handler (VcHandler *handler)
ui/gtk.c +1 -4
@@ -1125,11 +1125,8 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)static int nb_vcs;static CharDriverState *vcs[MAX_VCS ]; -static CharDriverState *gd_vc_handler (ChardevVC *unused) +static CharDriverState *gd_vc_handler (CharDriverState *chr,ChardevVC *unused) { -CharDriverState *chr; - - chr =g_malloc0 (sizeof (*chr)); chr->chr_write = gd_vc_chr_write;/* defer OPENED events until our vc is fully initialized */ chr->explicit_be_open = true;
Signature · 1 line
1.8.3.1
PATCH 2/7 qemu-char: Allow a chardev to reconnect if disconnected
In reply to [email protected]From: Corey Minyard <[email protected]>
Allow a socket that connects to reconnect on a periodic basis if it fails to connect at startup or if the connection drops while in use.
--- include/sysemu/char.h | 16 ++++- qemu-char.c | 165 +++++++++++++++++++++++++++++++++++++++++++++----- qemu-options.hx | 11 +++- 3 files changed, 173 insertions(+), 19 deletions(-)
include/sysemu/char.h +15 -1
@@ -53,6 +53,17 @@ typedef struct {typedef void IOEventHandler (void *opaque,int event); +struct ReconData { +void *opaque; +uint64_t recon_time; +struct ReconHandlers *handlers; +void (*state_change)(void *open_opaque,int is_open); +void *state_change_opaque; +void (*reconnected)(struct ReconData *r); +void (*connection_lost)(struct ReconData *r); +void (*shutdown)(struct ReconData *r); +}; +struct CharDriverState {void (*init)(struct CharDriverState *s);int (*chr_write)(struct CharDriverState *s,const uint8_t *buf,int len);
@@ -82,6 +93,8 @@ struct CharDriverState {guint fd_in_tag;QemuOpts *opts;QTAILQ_ENTRY (CharDriverState) next; +GSList *backend; +struct ReconData *recon; };/**
@@ -293,7 +306,8 @@ CharDriverState *qemu_chr_find(const char *name);QemuOpts *qemu_chr_parse_compat (const char *label,const char *filename);void register_char_driver (const char *name, -CharDriverState *(*open)(CharDriverState *,QemuOpts *)); +CharDriverState *(*open)(CharDriverState *,QemuOpts *), +int (*recon_setup)(CharDriverState *));void register_char_driver_qapi (const char *name,ChardevBackendKind kind,void (*parse)(QemuOpts *opts,ChardevBackend *backend,Error **errp));
qemu-char.c +150 -15
272 lines not on this page · raw message
qemu-options.hx +8 -3
@@ -1787,8 +1787,9 @@ ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" "-chardev socket,id=id[,host=host],port=host[,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" + " [,server][,nowait][,telnet][,mux=on|off][,reconnect=seconds] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,mux=on|off]\n" + " [,reconnect=seconds] (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"
@@ -1860,7 +1861,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
@@ -1874,6 +1875,10 @@ connect to a listening socket. @option{telnet} specifies that traffic on the socket should interpret telnet escape sequences. +@option{reconnect} specifies that if the client socket does not connect at +startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @option
Signature · 1 line
1.8.3.1
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected
Quoted 371 lines · On 2014/3/5 8:38, [email protected] wrote:
From: Corey Minyard <[email protected]>
Allow a socket that connects to reconnect on a periodic basis if it fails to connect at startup or if the connection drops while in use.
Signed-off-by Corey Minyard <[email protected]>--- include/sysemu/char.h | 16 ++++- qemu-char.c | 165 +++++++++++++++++++++++++++++++++++++++++++++----- qemu-options.hx | 11 +++- 3 files changed, 173 insertions(+), 19 deletions(-)include/sysemu/char.h +15
-1@@ -53,6 +53,17 @@ typedef struct {typedef void IOEventHandler (void *opaque,int event); +struct ReconData { +void *opaque; +uint64_t recon_time; +struct ReconHandlers *handlers; +void (*state_change)(void *open_opaque,int is_open); +void *state_change_opaque; +void (*reconnected)(struct ReconData *r); +void (*connection_lost)(struct ReconData *r); +void (*shutdown)(struct ReconData *r); +}; +struct CharDriverState {void (*init)(struct CharDriverState *s);int (*chr_write)(struct CharDriverState *s,const uint8_t *buf,int len);@@ -82,6 +93,8 @@ struct CharDriverState {guint fd_in_tag;QemuOpts *opts;QTAILQ_ENTRY (CharDriverState) next; +GSList *backend; +struct ReconData *recon; };/** @@ -293,7 +306,8 @@ CharDriverState *qemu_chr_find(const char *name);QemuOpts *qemu_chr_parse_compat (const char *label,const char *filename);void register_char_driver (const char *name, -CharDriverState *(*open)(CharDriverState *,QemuOpts *)); +CharDriverState *(*open)(CharDriverState *,QemuOpts *), +int (*recon_setup)(CharDriverState *));void register_char_driver_qapi (const char *name,ChardevBackendKind kind,void (*parse)(QemuOpts *opts,ChardevBackend *backend,Error **errp));qemu-char.c +150
-15@@ -88,6 +88,44 @@/***********************************************************/ /* character device */ +struct GenericReconData { +QEMUTimer *recon_timer; +}; + +static void generic_recon_timeout (void *opaque) +{ +struct ReconData *r = opaque; +struct GenericReconData *d = r->opaque; + +timer_mod (d->recon_timer, + (get_clock () + (r->recon_time *get_ticks_per_sec ()))); + r->state_change (r->state_change_opaque,0 ); +} + +static void generic_reconnected (struct ReconData *r) +{ +struct GenericReconData *d = r->opaque; + +timer_del (d->recon_timer); +} + +static void generic_connection_lost (struct ReconData *r) +{ +struct GenericReconData *d = r->opaque; + + r->state_change (r->state_change_opaque,1 ); +timer_mod (d->recon_timer, + (get_clock () + (r->recon_time *get_ticks_per_sec ()))); +} + +static void generic_recon_shutdown (struct ReconData *r) +{ +struct GenericReconData *d = r->opaque; +timer_free (d->recon_timer); +free (d); + r->opaque =NULL ; +} + static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =QTAILQ_HEAD_INITIALIZER (chardevs);@@ -96,9 +134,15 @@ void qemu_chr_be_event(CharDriverState *s, int event)/* Keep track if the char device is open */ switch (event) {case CHR_EVENT_OPENED : +if (s->recon) { + s->recon->reconnected (s->recon); + } s->be_open =1 ;break ;case CHR_EVENT_CLOSED : +if (s->recon) { + s->recon->connection_lost (s->recon); + } s->be_open =0 ;break ; }@@ -2582,6 +2626,7 @@ static void tcp_chr_close(CharDriverState *chr)closesocket (s->listen_fd); }g_free (s); + chr->opaque =NULL ;qemu_chr_be_event (chr,CHR_EVENT_CLOSED ); }@@ -2641,8 +2686,6 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr, chr->get_msgfd = tcp_get_msgfd; chr->chr_add_client = tcp_chr_add_client; chr->chr_add_watch = tcp_chr_add_watch; -/* be isn't opened until we get a connection */ - chr->explicit_be_open = true;if (is_listen) { s->listen_fd = fd;@@ -2680,6 +2723,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,bool do_nodelay = !qemu_opt_get_bool (opts,"delay" , true);bool is_unix =qemu_opt_get (opts,"path" ) !=NULL ; +/* be isn't opened until we get a connection */ + chr->explicit_be_open = true; +if (is_unix) {if (is_listen) { fd =unix_listen_opts (opts, &local_err);@@ -2716,8 +2762,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,if (fd >=0 ) {closesocket (fd); } -if (chr) { +if (chr && chr->opaque) {g_free (chr->opaque); + chr->opaque =NULL ; }return NULL ; }@@ -3128,6 +3175,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, typedef struct CharDriver { const char *name; + int (*recon_setup)(CharDriverState *); /* old, pre qapi */ CharDriverState *(*open)(CharDriverState *chr, QemuOpts *opts); /* new, qapi-based */@@ -3138,13 +3186,15 @@ typedef struct CharDriver {static GSList *backends;void register_char_driver (const char *name, -CharDriverState *(*open)(CharDriverState *,QemuOpts *)) +CharDriverState *(*open)(CharDriverState *,QemuOpts *), +int (*recon_setup)(CharDriverState *)) {CharDriver *s; s =g_malloc0 (sizeof (*s)); s->name =g_strdup (name); s->open = open; + s->recon_setup = recon_setup; backends =g_slist_append (backends, s); }@@ -3162,6 +3212,50 @@ void register_char_driver_qapi(const char *name, ChardevBackendKind kind, backends =g_slist_append (backends, s); } +static void generic_recon_state_change (void *opaque,int is_open) +{ +struct CharDriverState *chr = opaque; + +if (!is_open) { +struct CharDriver *cd = chr->backend->data; + +if (chr->be_open) { +return ; + } + + cd->open (chr, chr->opts); + }else { +void (*chr_close)(struct CharDriverState *chr) = chr->chr_close; + +if (chr_close) { + chr->chr_close =NULL ; +chr_close (chr); + } + } +} + +static int generic_recon_setup (struct CharDriverState *chr) +{ +struct GenericReconData *d; + + d =g_malloc (sizeof (*d)); + chr->recon->opaque = d; + + chr->recon->reconnected = generic_reconnected; + chr->recon->connection_lost = generic_connection_lost; + chr->recon->shutdown = generic_recon_shutdown; + chr->recon->state_change = generic_recon_state_change; + chr->recon->state_change_opaque = chr; + + d->recon_timer =timer_new (QEMU_CLOCK_REALTIME ,SCALE_NS , + generic_recon_timeout, chr->recon); + +/* Make sure it connects in time. */ +timer_mod (d->recon_timer, + (get_clock () + (chr->recon->recon_time *get_ticks_per_sec ()))); +return 1 ; +} +CharDriverState *qemu_chr_new_from_opts (QemuOpts *opts,void (*init)(struct CharDriverState *s),Error **errp)@@ -3169,6 +3263,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,CharDriver *cd;CharDriverState *chr;GSList *i; +uint64_t recon_time;if (qemu_opts_id (opts) ==NULL ) {error_setg (errp,"chardev: no id specified" );@@ -3244,11 +3339,41 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, } chr =g_malloc0 (sizeof (CharDriverState)); - chr = cd->open (chr, opts); -if (!chr) { -error_setg (errp,"chardev: opening backend \"%s\" failed" , -qemu_opt_get (opts,"backend" )); - goto err; + + chr->backend = i; + recon_time =qemu_opt_get_number (opts,"reconnect" ,0 ); +if (recon_time) { +CharDriver *d = chr->backend->data; +if (!d->recon_setup) { +g_free (chr); +fprintf (stderr,"chardev: reconnect not supported on %s\n" , +qemu_opt_get (opts,"backend" )); +return NULL ; + } +if (qemu_opt_get_bool (opts,"server" ,0 )) { +g_free (chr); +fprintf (stderr,"chardev: server device cannot reconnect\n" ); +return NULL ; + } + chr->opts = opts; + chr->recon =g_malloc (sizeof (*chr->recon)); + chr->recon->recon_time = recon_time; +if (!d->recon_setup (chr)) { +g_free (chr->recon); +g_free (chr); +fprintf (stderr,"chardev: Unable to set up reconnect\n" ); +return NULL ; + } + } + +if (!cd->open (chr, opts)) { +if (!chr->recon) { +/* Reconnect is not enabled, give up */ +fprintf (stderr,"chardev: opening backend \"%s\" failed\n" , +qemu_opt_get (opts,"backend" )); +g_free (chr); +return NULL ; + } }if (!chr->filename)@@ -3267,7 +3392,8 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,int len =strlen (qemu_opts_id (opts)) +6 ; base->label =g_malloc (len);snprintf (base->label, len,"%s-base" ,qemu_opts_id (opts)); - chr =qemu_chr_open_mux (chr, base); + chr =g_malloc0 (sizeof (CharDriverState)); +qemu_chr_open_mux (chr, base); chr->filename = base->filename; chr->avail_connections =MAX_MUX ;QTAILQ_INSERT_TAIL (&chardevs, chr, next);@@ -3275,7 +3401,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, chr->avail_connections =1 ; } chr->label =g_strdup (qemu_opts_id (opts)); - chr->opts = opts;return chr; err:@@ -3378,9 +3503,16 @@ void qemu_chr_fe_release(CharDriverState *s)void qemu_chr_delete (CharDriverState *chr) { +void (*chr_close)(struct CharDriverState *chr) = chr->chr_close; +QTAILQ_REMOVE (&chardevs, chr, next); -if (chr->chr_close) { - chr->chr_close (chr); +if (chr_close) { + chr->chr_close =NULL ; +chr_close (chr); + } +if (chr->recon) { + chr->recon->shutdown (chr->recon); +g_free (chr->recon); }g_free (chr->filename);g_free (chr->label);@@ -3515,6 +3647,9 @@ QemuOptsList qemu_chardev_opts = { .name = "mux", .type = QEMU_OPT_BOOL, },{ + .name = "reconnect", + .type = QEMU_OPT_NUMBER, + },{ .name = "signal", .type = QEMU_OPT_BOOL, },{@@ -3811,8 +3946,8 @@ void qmp_chardev_remove(const char *id, Error **errp)static void register_types (void ) {register_char_driver_qapi ("null" ,CHARDEV_BACKEND_KIND_NULL ,NULL ); -register_char_driver ("socket" , qemu_chr_open_socket); -register_char_driver ("udp" , qemu_chr_open_udp); +register_char_driver ("socket" , qemu_chr_open_socket, generic_recon_setup); +register_char_driver ("udp" , qemu_chr_open_udp,NULL );register_char_driver_qapi ("ringbuf" ,CHARDEV_BACKEND_KIND_RINGBUF , qemu_chr_parse_ringbuf); register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,qemu-options.hx +4
-3@@ -1787,8 +1787,9 @@ ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" "-chardev socket,id=id[,host=host],port=host[,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" + " [,server][,nowait][,telnet][,mux=on|off][,reconnect=seconds] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,mux=on|off]\n" + " [,reconnect=seconds] (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"@@ -1860,7 +1861,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
qemu-options.hx connect to a listening socket. · line 1884
Quoted 10 lines
qemu-options.hx +4
-0@@ -1874,6 +1875,10 @@ connect to a listening socket. @option{telnet} specifies that traffic on the socket should interpret telnet escape sequences. +@option{reconnect} specifies that if the client socket does not connect at +startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @option
The client will reconnect for ever when the server is dead. Is it better that try to reconnect several times? Or add a option which specifies times of reconnect?
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected
Quoted 9 lines · Weidong Huang
escape sequences.
+@option{reconnect} specifies that if the client socket does not connect at
qemu-options.hx +3
-0+startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @option
Quoted 3 lines
The client will reconnect for ever when the server is dead. Is it better that try to reconnect several times? Or add a option which specifies times of reconnect?
I'm not really sure about this. For a remote IPMI BMC, you would want it to reconnect for forever, there's no point it having it stop trying after a while, since you want it to come back even if the BMC is down for a day. I would think the same if you wanted a remote console or something of that nature.
What's the use case where you would want it to stop trying? I can't think of any.
Thanks,
-corey
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected
Quoted 19 lines · Corey Minyard
Quoted 9 lines · Weidong Huang
escape sequences.
+@option{reconnect} specifies that if the client socket does not connect at
qemu-options.hx +3
-0+startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @optionQuoted 3 lines
The client will reconnect for ever when the server is dead. Is it better that try to reconnect several times? Or add a option which specifies times of reconnect?
I'm not really sure about this. For a remote IPMI BMC, you would want it to reconnect for forever, there's no point it having it stop trying after a while, since you want it to come back even if the BMC is down for a day. I would think the same if you wanted a remote console or something of that nature.
Quoted 6 lines
What's the use case where you would want it to stop trying? I can't think of any.
Thanks,
-corey
Depends on what happens after it fails. Imagine whoever was using the socket is gone. It might be useful to stop VM or exit after a while rather than just hang about.
Signature · 1 line
MST
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected
In reply to [email protected]Quoted 371 lines · On Tue, Mar 04, 2014 at 06:38:52PM -0600, [email protected] wrote:
From: Corey Minyard <[email protected]>
Allow a socket that connects to reconnect on a periodic basis if it fails to connect at startup or if the connection drops while in use.
Signed-off-by Corey Minyard <[email protected]>--- include/sysemu/char.h | 16 ++++- qemu-char.c | 165 +++++++++++++++++++++++++++++++++++++++++++++----- qemu-options.hx | 11 +++- 3 files changed, 173 insertions(+), 19 deletions(-)include/sysemu/char.h +15
-1@@ -53,6 +53,17 @@ typedef struct {typedef void IOEventHandler (void *opaque,int event); +struct ReconData { +void *opaque; +uint64_t recon_time; +struct ReconHandlers *handlers; +void (*state_change)(void *open_opaque,int is_open); +void *state_change_opaque; +void (*reconnected)(struct ReconData *r); +void (*connection_lost)(struct ReconData *r); +void (*shutdown)(struct ReconData *r); +}; +struct CharDriverState {void (*init)(struct CharDriverState *s);int (*chr_write)(struct CharDriverState *s,const uint8_t *buf,int len);@@ -82,6 +93,8 @@ struct CharDriverState {guint fd_in_tag;QemuOpts *opts;QTAILQ_ENTRY (CharDriverState) next; +GSList *backend; +struct ReconData *recon; };/** @@ -293,7 +306,8 @@ CharDriverState *qemu_chr_find(const char *name);QemuOpts *qemu_chr_parse_compat (const char *label,const char *filename);void register_char_driver (const char *name, -CharDriverState *(*open)(CharDriverState *,QemuOpts *)); +CharDriverState *(*open)(CharDriverState *,QemuOpts *), +int (*recon_setup)(CharDriverState *));void register_char_driver_qapi (const char *name,ChardevBackendKind kind,void (*parse)(QemuOpts *opts,ChardevBackend *backend,Error **errp));qemu-char.c +150
-15@@ -88,6 +88,44 @@/***********************************************************/ /* character device */ +struct GenericReconData { +QEMUTimer *recon_timer; +}; + +static void generic_recon_timeout (void *opaque) +{ +struct ReconData *r = opaque; +struct GenericReconData *d = r->opaque; + +timer_mod (d->recon_timer, + (get_clock () + (r->recon_time *get_ticks_per_sec ()))); + r->state_change (r->state_change_opaque,0 ); +} + +static void generic_reconnected (struct ReconData *r) +{ +struct GenericReconData *d = r->opaque; + +timer_del (d->recon_timer); +} + +static void generic_connection_lost (struct ReconData *r) +{ +struct GenericReconData *d = r->opaque; + + r->state_change (r->state_change_opaque,1 ); +timer_mod (d->recon_timer, + (get_clock () + (r->recon_time *get_ticks_per_sec ()))); +} + +static void generic_recon_shutdown (struct ReconData *r) +{ +struct GenericReconData *d = r->opaque; +timer_free (d->recon_timer); +free (d); + r->opaque =NULL ; +} + static QTAILQ_HEAD(CharDriverStateHead, CharDriverState) chardevs =QTAILQ_HEAD_INITIALIZER (chardevs);@@ -96,9 +134,15 @@ void qemu_chr_be_event(CharDriverState *s, int event)/* Keep track if the char device is open */ switch (event) {case CHR_EVENT_OPENED : +if (s->recon) { + s->recon->reconnected (s->recon); + } s->be_open =1 ;break ;case CHR_EVENT_CLOSED : +if (s->recon) { + s->recon->connection_lost (s->recon); + } s->be_open =0 ;break ; }@@ -2582,6 +2626,7 @@ static void tcp_chr_close(CharDriverState *chr)closesocket (s->listen_fd); }g_free (s); + chr->opaque =NULL ;qemu_chr_be_event (chr,CHR_EVENT_CLOSED ); }@@ -2641,8 +2686,6 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr, chr->get_msgfd = tcp_get_msgfd; chr->chr_add_client = tcp_chr_add_client; chr->chr_add_watch = tcp_chr_add_watch; -/* be isn't opened until we get a connection */ - chr->explicit_be_open = true;if (is_listen) { s->listen_fd = fd;@@ -2680,6 +2723,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,bool do_nodelay = !qemu_opt_get_bool (opts,"delay" , true);bool is_unix =qemu_opt_get (opts,"path" ) !=NULL ; +/* be isn't opened until we get a connection */ + chr->explicit_be_open = true; +if (is_unix) {if (is_listen) { fd =unix_listen_opts (opts, &local_err);@@ -2716,8 +2762,9 @@ static CharDriverState *qemu_chr_open_socket(CharDriverState *chr,if (fd >=0 ) {closesocket (fd); } -if (chr) { +if (chr && chr->opaque) {g_free (chr->opaque); + chr->opaque =NULL ; }return NULL ; }@@ -3128,6 +3175,7 @@ static void qemu_chr_parse_mux(QemuOpts *opts, ChardevBackend *backend, typedef struct CharDriver { const char *name; + int (*recon_setup)(CharDriverState *); /* old, pre qapi */ CharDriverState *(*open)(CharDriverState *chr, QemuOpts *opts); /* new, qapi-based */@@ -3138,13 +3186,15 @@ typedef struct CharDriver {static GSList *backends;void register_char_driver (const char *name, -CharDriverState *(*open)(CharDriverState *,QemuOpts *)) +CharDriverState *(*open)(CharDriverState *,QemuOpts *), +int (*recon_setup)(CharDriverState *)) {CharDriver *s; s =g_malloc0 (sizeof (*s)); s->name =g_strdup (name); s->open = open; + s->recon_setup = recon_setup; backends =g_slist_append (backends, s); }@@ -3162,6 +3212,50 @@ void register_char_driver_qapi(const char *name, ChardevBackendKind kind, backends =g_slist_append (backends, s); } +static void generic_recon_state_change (void *opaque,int is_open) +{ +struct CharDriverState *chr = opaque; + +if (!is_open) { +struct CharDriver *cd = chr->backend->data; + +if (chr->be_open) { +return ; + } + + cd->open (chr, chr->opts); + }else { +void (*chr_close)(struct CharDriverState *chr) = chr->chr_close; + +if (chr_close) { + chr->chr_close =NULL ; +chr_close (chr); + } + } +} + +static int generic_recon_setup (struct CharDriverState *chr) +{ +struct GenericReconData *d; + + d =g_malloc (sizeof (*d)); + chr->recon->opaque = d; + + chr->recon->reconnected = generic_reconnected; + chr->recon->connection_lost = generic_connection_lost; + chr->recon->shutdown = generic_recon_shutdown; + chr->recon->state_change = generic_recon_state_change; + chr->recon->state_change_opaque = chr; + + d->recon_timer =timer_new (QEMU_CLOCK_REALTIME ,SCALE_NS , + generic_recon_timeout, chr->recon); + +/* Make sure it connects in time. */ +timer_mod (d->recon_timer, + (get_clock () + (chr->recon->recon_time *get_ticks_per_sec ()))); +return 1 ; +} +CharDriverState *qemu_chr_new_from_opts (QemuOpts *opts,void (*init)(struct CharDriverState *s),Error **errp)@@ -3169,6 +3263,7 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,CharDriver *cd;CharDriverState *chr;GSList *i; +uint64_t recon_time;if (qemu_opts_id (opts) ==NULL ) {error_setg (errp,"chardev: no id specified" );@@ -3244,11 +3339,41 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, } chr =g_malloc0 (sizeof (CharDriverState)); - chr = cd->open (chr, opts); -if (!chr) { -error_setg (errp,"chardev: opening backend \"%s\" failed" , -qemu_opt_get (opts,"backend" )); - goto err; + + chr->backend = i; + recon_time =qemu_opt_get_number (opts,"reconnect" ,0 ); +if (recon_time) { +CharDriver *d = chr->backend->data; +if (!d->recon_setup) { +g_free (chr); +fprintf (stderr,"chardev: reconnect not supported on %s\n" , +qemu_opt_get (opts,"backend" )); +return NULL ; + } +if (qemu_opt_get_bool (opts,"server" ,0 )) { +g_free (chr); +fprintf (stderr,"chardev: server device cannot reconnect\n" ); +return NULL ; + } + chr->opts = opts; + chr->recon =g_malloc (sizeof (*chr->recon)); + chr->recon->recon_time = recon_time; +if (!d->recon_setup (chr)) { +g_free (chr->recon); +g_free (chr); +fprintf (stderr,"chardev: Unable to set up reconnect\n" ); +return NULL ; + } + } + +if (!cd->open (chr, opts)) { +if (!chr->recon) { +/* Reconnect is not enabled, give up */ +fprintf (stderr,"chardev: opening backend \"%s\" failed\n" , +qemu_opt_get (opts,"backend" )); +g_free (chr); +return NULL ; + } }if (!chr->filename)@@ -3267,7 +3392,8 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts,int len =strlen (qemu_opts_id (opts)) +6 ; base->label =g_malloc (len);snprintf (base->label, len,"%s-base" ,qemu_opts_id (opts)); - chr =qemu_chr_open_mux (chr, base); + chr =g_malloc0 (sizeof (CharDriverState)); +qemu_chr_open_mux (chr, base); chr->filename = base->filename; chr->avail_connections =MAX_MUX ;QTAILQ_INSERT_TAIL (&chardevs, chr, next);@@ -3275,7 +3401,6 @@ CharDriverState *qemu_chr_new_from_opts(QemuOpts *opts, chr->avail_connections =1 ; } chr->label =g_strdup (qemu_opts_id (opts)); - chr->opts = opts;return chr; err:@@ -3378,9 +3503,16 @@ void qemu_chr_fe_release(CharDriverState *s)void qemu_chr_delete (CharDriverState *chr) { +void (*chr_close)(struct CharDriverState *chr) = chr->chr_close; +QTAILQ_REMOVE (&chardevs, chr, next); -if (chr->chr_close) { - chr->chr_close (chr); +if (chr_close) { + chr->chr_close =NULL ; +chr_close (chr); + } +if (chr->recon) { + chr->recon->shutdown (chr->recon); +g_free (chr->recon); }g_free (chr->filename);g_free (chr->label);@@ -3515,6 +3647,9 @@ QemuOptsList qemu_chardev_opts = { .name = "mux", .type = QEMU_OPT_BOOL, },{ + .name = "reconnect", + .type = QEMU_OPT_NUMBER, + },{ .name = "signal", .type = QEMU_OPT_BOOL, },{@@ -3811,8 +3946,8 @@ void qmp_chardev_remove(const char *id, Error **errp)static void register_types (void ) {register_char_driver_qapi ("null" ,CHARDEV_BACKEND_KIND_NULL ,NULL ); -register_char_driver ("socket" , qemu_chr_open_socket); -register_char_driver ("udp" , qemu_chr_open_udp); +register_char_driver ("socket" , qemu_chr_open_socket, generic_recon_setup); +register_char_driver ("udp" , qemu_chr_open_udp,NULL );register_char_driver_qapi ("ringbuf" ,CHARDEV_BACKEND_KIND_RINGBUF , qemu_chr_parse_ringbuf); register_char_driver_qapi("file", CHARDEV_BACKEND_KIND_FILE,qemu-options.hx +4
-3@@ -1787,8 +1787,9 @@ ETEXI DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev null,id=id[,mux=on|off]\n" "-chardev socket,id=id[,host=host],port=host[,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" + " [,server][,nowait][,telnet][,mux=on|off][,reconnect=seconds] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,mux=on|off]\n" + " [,reconnect=seconds] (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"@@ -1860,7 +1861,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
qemu-options.hx connect to a listening socket. · line 1884
Quoted 10 lines
qemu-options.hx +4
-0@@ -1874,6 +1875,10 @@ connect to a listening socket. @option{telnet} specifies that traffic on the socket should interpret telnet escape sequences. +@option{reconnect} specifies that if the client socket does not connect at +startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @option
This looks useful but it bothers me that this new option can't be used together with others:
server and nowait, which limits its usefulness and would make it harder to extend in the future.
- I think that specifying a time-out should be separate from the option to retry.
E.g. it might be quite reasonable to retry connecting immediately.
- it seems to me that if we don't specify nowait, this
should stop the VM until reconnect.
- I think this can co-exist with server option.
E.g. if we detect that the other side closed the socket,
start listening again. Or even listen all the time
and accept if disconnected.
I'd like to suggest we generalize this a bit:
When we detect a disconnect, three thinkable things to do would be:
- keep going as if nothing happened (default for all sockets except monitor)
- exit qemu
(default for monitor)
- retry
When waiting for retry it seems reasonable to either stop the VM or keep going (nowait option).
When retrying, it seems reasonable to specify a timeout to make it fail.
All of the above seem to apply to server and client sockets, with or without nowait option.
For clients only, it might be somewhat useful to limit the network traffic caused by reconnects.
Thanks,
MST
Quoted 2 lines
Signature · 1 line
1.8.3.1
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected
Quoted 13 lines · Michael S. Tsirkin
+@option{reconnect} specifies that if the client socket does not connect at
qemu-options.hx +3
-0+startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @optionThis looks useful but it bothers me that this new option can't be used together with others:
server and nowait, which limits its usefulness and would make it harder to extend in the future.
Quoted 3 lines
- I think that specifying a time-out should be separate from the option to retry.
E.g. it might be quite reasonable to retry connecting immediately.
Maybe, but that would basically disable qemu until the connection came back, because it would just be spinning trying to connect. That doesn't seem like a good idea to me.
Quoted 2 lines
- it seems to me that if we don't specify nowait, this should stop the VM until reconnect.
That's an interesting thought. Currently the option basically implies nowait. It's probably easy to add this, but I'm not sure of the use case. Anyone else have any opinions on this?
Quoted 4 lines
- I think this can co-exist with server option. E.g. if we detect that the other side closed the socket, start listening again. Or even listen all the time and accept if disconnected.
IIRC, that's what it does already, even without this patch. If in server mode, I believe you can disconnect and then reconnect a socket and it works fine. So I see no need for this function in server mode.
Quoted 10 lines
I'd like to suggest we generalize this a bit:
When we detect a disconnect, three thinkable things to do would be:
- keep going as if nothing happened (default for all sockets except monitor)
- exit qemu
(default for monitor)
- retry
There's also
- stop the VM until the socket reconnects.
I'm not sure why you would want to single out monitor as special. Especially if it is mux-ed into another connection.
Quoted 6 lines
When waiting for retry it seems reasonable to either stop the VM or keep going (nowait option).
When retrying, it seems reasonable to specify a timeout to make it fail.
I just don't see why you would want to do this. It means that the connection is gone until the VM starts back up again.
One thing I did think of was to add a monitor command to try to reconnect. Then I could understand giving up, since you can get into the monitor and kick it back off.
Thanks,
-corey
Quoted 11 lines
All of the above seem to apply to server and client sockets, with or without nowait option.
For clients only, it might be somewhat useful to limit the network traffic caused by reconnects.
Thanks,
MSTQuoted 2 lines
Signature · 1 line
1.8.3.1
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected
Quoted 14 lines · Corey Minyard
Quoted 13 lines · Michael S. Tsirkin
+@option{reconnect} specifies that if the client socket does not connect at
qemu-options.hx +3
-0+startup, or if the client socket is closed for some reason (like the other +end exited), wait the given number of seconds and attempt to reconnect. + TCP and unix socket options are given below: @table @optionThis looks useful but it bothers me that this new option can't be used together with others:
server and nowait, which limits its usefulness and would make it harder to extend in the future.
Quoted 6 lines
Quoted 3 lines
- I think that specifying a time-out should be separate from the option to retry.
E.g. it might be quite reasonable to retry connecting immediately.Maybe, but that would basically disable qemu until the connection came back, because it would just be spinning trying to connect.
Why spin? queue the request in bh.
Quoted 9 lines
That doesn't
seem like a good idea to me.Quoted 2 lines
- it seems to me that if we don't specify nowait, this should stop the VM until reconnect.
That's an interesting thought. Currently the option basically implies nowait. It's probably easy to add this, but I'm not sure of the use case.
To avoid losing information that would have been sent there.
Quoted 21 lines
Anyone else have any opinions on this?
Quoted 4 lines
- I think this can co-exist with server option. E.g. if we detect that the other side closed the socket, start listening again. Or even listen all the time and accept if disconnected.
IIRC, that's what it does already, even without this patch. If in server mode, I believe you can disconnect and then reconnect a socket and it works fine. So I see no need for this function in server mode.
Quoted 10 lines
I'd like to suggest we generalize this a bit:
When we detect a disconnect, three thinkable things to do would be:
- keep going as if nothing happened (default for all sockets except monitor)
- exit qemu
(default for monitor)
- retry
Quoted 5 lines
There's also
- stop the VM until the socket reconnects.I'm not sure why you would want to single out monitor as special. Especially if it is mux-ed into another connection.
it seems useful to make qemu exit when monitor closes. many programs behave this way, this makes sure if you lose connection,
the server does not hang around doing nothing. combine with keepalive socket option,
you get a way to make qemu exit if it loses networking, which just might help restore networking.
Quoted 9 lines
Quoted 6 lines
When waiting for retry it seems reasonable to either stop the VM or keep going (nowait option).
When retrying, it seems reasonable to specify a timeout to make it fail.
I just don't see why you would want to do this. It means that the connection is gone until the VM starts back up again.
e.g. if socket is used for serial, you might want to stop VM to avoid losing output.
Quoted 8 lines
One thing I did think of was to add a monitor command to try to reconnect. Then I could understand giving up, since you can get into the monitor and kick it back off.
Thanks,
-corey
if qemu exits on timeout, then that's also useful as a way of automatically cleaning up resources if you lose connection to qemu.
which might help host recover from e.g. oom situation, and become responsive again.
Quoted 11 lines
Quoted 11 lines
All of the above seem to apply to server and client sockets, with or without nowait option.
For clients only, it might be somewhat useful to limit the network traffic caused by reconnects.
Thanks,
MSTQuoted 2 lines
Signature · 1 line
1.8.3.1
PATCH 3/7 qemu-char: Wait until socket connect to report connected
In reply to [email protected]From: Corey Minyard <[email protected]>
The socket code was reporting that a socket connection was connected as soon as the connect call was issued. If the connection failed, it would then report it was not connected. With the reconnect code, it's better to wait until the connection is actually operational before reporting that the socket is connected.
--- qemu-char.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 40 insertions(+), 9 deletions(-)
qemu-char.c +40 -9
@@ -2310,9 +2310,10 @@ static CharDriverState *qemu_chr_open_udp(CharDriverState *chr, QemuOpts *opts) typedef struct { GIOChannel *chan, *listen_chan; + int waitsrc; guint listen_tag; int fd, listen_fd; - int connected; + enum { TCP_NOT_CONNECTED, TCP_WAITING_CONNECT, TCP_CONNECTED } state; int max_size; int do_telnetopt; int do_nodelay;
@@ -2325,7 +2326,7 @@ static gboolean tcp_chr_accept(GIOChannel *chan, GIOCondition cond, void *opaquestatic int tcp_chr_write (CharDriverState *chr,const uint8_t *buf,int len) {TCPCharDriver *s = chr->opaque; -if (s->connected) { +if (s->state ==TCP_CONNECTED ) {return io_channel_send (s->chan, buf, len); }else {/* XXX: indicate an error ? */
@@ -2337,7 +2338,7 @@ static int tcp_chr_read_poll(void *opaque) {CharDriverState *chr = opaque;TCPCharDriver *s = chr->opaque; -if (!s->connected) +if (s->state !=TCP_CONNECTED )return 0 ; s->max_size =qemu_chr_be_can_write (chr);return s->max_size;
@@ -2482,7 +2483,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)uint8_t buf[READ_BUF_LEN ];int len, size; -if (!s->connected || s->max_size <=0 ) { +if (s->state !=TCP_CONNECTED || s->max_size <=0 ) {return TRUE; } len =sizeof (buf);
@@ -2491,7 +2492,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) size =tcp_chr_recv (chr, (void *)buf, len);if (size ==0 ) {/* connection closed */ - s->connected =0 ; + s->state =TCP_NOT_CONNECTED ;if (s->listen_chan) { s->listen_tag =g_io_add_watch (s->listen_chan,G_IO_IN , tcp_chr_accept, chr); }
@@ -2518,17 +2519,48 @@ CharDriverState *qemu_chr_open_eventfd(CharDriverState *chr, int eventfd) }#endif -static void tcp_chr_connect (void *opaque) +static gboolean tcp_wait_connect (GIOChannel *source, +GIOCondition condition, +gpointer opaque) {CharDriverState *chr = opaque;TCPCharDriver *s = chr->opaque; - s->connected =1 ; +if (s->state !=TCP_WAITING_CONNECT ) { +return FALSE; + } +if (condition &G_IO_ERR || condition &G_IO_HUP ) { +/* The connected failed */ + s->state =TCP_NOT_CONNECTED ; +g_io_channel_unref (s->chan); + s->chan =NULL ; +closesocket (s->fd); + s->fd =-1 ; +return FALSE; + } + + s->state =TCP_CONNECTED ;if (s->chan) { chr->fd_in_tag =io_add_watch_poll (s->chan, tcp_chr_read_poll, tcp_chr_read, chr); }qemu_chr_be_generic_open (chr); +return FALSE; +} + +static void tcp_chr_connect (void *opaque) +{ +CharDriverState *chr = opaque; +TCPCharDriver *s = chr->opaque; + + s->state =TCP_WAITING_CONNECT ; +if (s->chan) { +/* Wait until write becomes ready before reporting connected. */ + s->waitsrc =g_io_add_watch (s->chan, +G_IO_OUT |G_IO_HUP |G_IO_ERR , + tcp_wait_connect, + chr); + } }#define IACSET (x,a,b,c) x[0] = a; x[1] = b; x[2] = c;
@@ -2650,7 +2682,7 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr, s =g_malloc0 (sizeof (TCPCharDriver)); - s->connected =0 ; + s->state =TCP_NOT_CONNECTED ; s->fd =-1 ; s->listen_fd =-1 ; s->msgfd =-1 ;
@@ -2695,7 +2727,6 @@ static CharDriverState *qemu_chr_open_socket_fd(CharDriverState *chr, s->do_telnetopt =1 ; } }else { - s->connected =1 ; s->fd = fd;socket_set_nodelay (fd); s->chan =io_channel_from_socket (s->fd);
Signature · 1 line
1.8.3.1
PATCH 4/7 qemu-char: remove free of chr from win_stdio_close
In reply to [email protected]From: Corey Minyard <[email protected]>
This will result in a double free on close, because it's freed in qemu_chr_delete() right after calling the close function.
--- qemu-char.c | 1 - 1 file changed, 1 deletion(-)
qemu-char.c +0 -1
@@ -2105,7 +2105,6 @@ static void win_stdio_close(CharDriverState *chr) }g_free (chr->opaque); -g_free (chr); } static CharDriverState *qemu_chr_open_stdio(CharDriverState *chr,
Signature · 1 line
1.8.3.1
PATCH 5/7 qemu-char: Close fd at end of file
In reply to [email protected]From: Corey Minyard <[email protected]>
The chardev backends that used qemu_chr_open_fd did not get their file descriptors closed at end of file or when the chardev was closed. This could result in a file descriptor leak.
--- qemu-char.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-)
qemu-char.c +29 -6
@@ -851,6 +851,8 @@ typedef struct FDCharDriver {GIOChannel *fd_in, *fd_out;int max_size;QTAILQ_ENTRY (FDCharDriver) node; +int close_fdin; +int close_fdout; } FDCharDriver;static int fd_chr_write (CharDriverState *chr,const uint8_t *buf,int len)
@@ -860,6 +862,18 @@ static int fd_chr_write(CharDriverState *chr, const uint8_t *buf, int len)return io_channel_send (s->fd_out, buf, len); } +static void fd_close_fds (FDCharDriver *s) +{ +if ((s->close_fdin != s->close_fdout) && (s->close_fdout !=-1 )) { +close (s->close_fdout); + } + s->close_fdout =-1 ; +if (s->close_fdin !=-1 ) { +close (s->close_fdin); + } + s->close_fdin =-1 ; +} +static gboolean fd_chr_read (GIOChannel *chan,GIOCondition cond,void *opaque) {CharDriverState *chr = opaque;
@@ -881,6 +895,7 @@ static gboolean fd_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque) len, &bytes_read, NULL);if (status ==G_IO_STATUS_EOF ) {remove_fd_in_watch (chr); +fd_close_fds (s);qemu_chr_be_event (chr,CHR_EVENT_CLOSED );return FALSE; }
@@ -929,19 +944,27 @@ static void fd_chr_close(struct CharDriverState *chr)g_io_channel_unref (s->fd_out); } +fd_close_fds (s);g_free (s);qemu_chr_be_event (chr,CHR_EVENT_CLOSED ); }/* open a character device to a unix fd */ static CharDriverState *qemu_chr_open_fd (CharDriverState *chr, -int fd_in,int fd_out) +int fd_in,int fd_out, +int close_fds_on_close) {FDCharDriver *s; s =g_malloc0 (sizeof (FDCharDriver)); s->fd_in =io_channel_from_fd (fd_in); s->fd_out =io_channel_from_fd (fd_out); +if (close_fds_on_close) { + s->close_fdin = fd_in; + s->close_fdout = fd_out; + }else { + s->close_fdin = s->close_fdout =-1 ; + }fcntl (fd_out,F_SETFL ,O_NONBLOCK ); s->chr = chr; chr->opaque = s;
@@ -979,7 +1002,7 @@ static CharDriverState *qemu_chr_open_pipe(CharDriverState *chr,return NULL ; } } -return qemu_chr_open_fd (chr, fd_in, fd_out); +return qemu_chr_open_fd (chr, fd_in, fd_out, TRUE); }/* init terminal so that we can grab keys */
@@ -1032,7 +1055,7 @@ static CharDriverState *qemu_chr_open_stdio(CharDriverState *chr,fcntl (0 ,F_SETFL ,O_NONBLOCK );atexit (term_exit); -qemu_chr_open_fd (chr,0 ,1 ); +qemu_chr_open_fd (chr,0 ,1 , FALSE); chr->chr_close = qemu_chr_close_stdio; chr->chr_set_echo = qemu_chr_set_echo_stdio;if (opts->has_signal) {
@@ -1438,7 +1461,7 @@ static void qemu_chr_close_tty(CharDriverState *chr)static CharDriverState *qemu_chr_open_tty_fd (CharDriverState *chr,int fd) {tty_serial_init (fd,115200 ,'N' ,8 ,1 ); -qemu_chr_open_fd (chr, fd, fd); +qemu_chr_open_fd (chr, fd, fd, TRUE); chr->chr_ioctl = tty_serial_ioctl; chr->chr_close = qemu_chr_close_tty;return chr;
@@ -2514,7 +2537,7 @@ static gboolean tcp_chr_read(GIOChannel *chan, GIOCondition cond, void *opaque)#ifndef _WIN32CharDriverState *qemu_chr_open_eventfd (CharDriverState *chr,int eventfd) { -return qemu_chr_open_fd (chr, eventfd, eventfd); +return qemu_chr_open_fd (chr, eventfd, eventfd, FALSE); }#endif
@@ -3769,7 +3792,7 @@ static CharDriverState *qmp_chardev_open_file(CharDriverState *chr, } } -return qemu_chr_open_fd (chr, in, out); +return qemu_chr_open_fd (chr, in, out, TRUE); } static CharDriverState *qmp_chardev_open_serial(CharDriverState *chr,
Signature · 1 line
1.8.3.1
PATCH 6/7 qemu-char: Clean up error handling in qmp_chardev_add
In reply to [email protected]From: Corey Minyard <[email protected]>
The open functions called by qmp_chardev_add could either return NULL or set errp to return an error. Modify to just return errp, as that can pass useful information where returning NULL cannot.
--- backends/baum.c | 6 +- backends/msmouse.c | 4 +- hw/misc/ivshmem.c | 11 +- include/sysemu/char.h | 10 +- include/ui/console.h | 4 +- include/ui/qemu-spice.h | 15 ++- qemu-char.c | 336 ++++++++++++++++++++++-------------------------- spice-qemu-char.c | 15 ++- ui/console.c | 10 +- ui/gtk.c | 4 +- 10 files changed, 190 insertions(+), 225 deletions(-)
backends/baum.c +3 -3
@@ -561,7 +561,7 @@ static void baum_close(struct CharDriverState *chr)g_free (baum); } -CharDriverState *chr_baum_init (CharDriverState *chr) +void chr_baum_init (CharDriverState *chr,Error **errp) {BaumDriverState *baum;brlapi_handle_t *handle;
@@ -610,15 +610,15 @@ CharDriverState *chr_baum_init(CharDriverState *chr)qemu_set_fd_handler (baum->brlapi_fd, baum_chr_read,NULL , baum); -return chr; +return ; fail:timer_free (baum->cellCount_timer);brlapi__closeConnection (handle); fail_handle: +error_setg (errp,"Failed to initialize baum" );g_free (handle);g_free (baum); -return NULL ; }static void register_types (void )
backends/msmouse.c +1 -3
@@ -63,15 +63,13 @@ static void msmouse_chr_close (struct CharDriverState *chr)g_free (chr); } -CharDriverState *qemu_chr_open_msmouse (CharDriverState *chr) +void qemu_chr_open_msmouse (CharDriverState *chr,Error **errp) { chr->chr_write = msmouse_chr_write; chr->chr_close = msmouse_chr_close; chr->explicit_be_open = true;qemu_add_mouse_event_handler (msmouse_event, chr,0 ,"QEMU Microsoft Mouse" ); - -return chr; }static void register_types (void )
hw/misc/ivshmem.c +3 -8
@@ -291,20 +291,15 @@ static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier * {/* create a event character device based on the passed eventfd */ IVShmemState *s = opaque; -CharDriverState *chr, *newchr; +CharDriverState *chr;int eventfd =event_notifier_get_fd (n); - newchr =g_malloc0 (sizeof (*chr)); -if (newchr ==NULL ) { -fprintf (stderr,"creating eventfd for eventfd %d failed\n" , eventfd); -exit (-1 ); - } - chr =qemu_chr_open_eventfd (newchr, eventfd); + chr =g_malloc0 (sizeof (*chr));if (chr ==NULL ) { -g_free (newchr);fprintf (stderr,"creating eventfd for eventfd %d failed\n" , eventfd);exit (-1 ); } +qemu_chr_open_eventfd (chr, eventfd);qemu_chr_fe_claim_no_fail (chr);/* if MSI is supported we need multiple interrupts */
include/sysemu/char.h +5 -5
@@ -306,22 +306,22 @@ CharDriverState *qemu_chr_find(const char *name);QemuOpts *qemu_chr_parse_compat (const char *label,const char *filename);void register_char_driver (const char *name, -CharDriverState *(*open)(CharDriverState *,QemuOpts *), -int (*recon_setup)(CharDriverState *)); +void (*open)(CharDriverState *,QemuOpts *,Error **), +int (*recon_setup)(CharDriverState *));void register_char_driver_qapi (const char *name,ChardevBackendKind kind,void (*parse)(QemuOpts *opts,ChardevBackend *backend,Error **errp));/* add an eventfd to the qemu devices that are polled */ -CharDriverState *qemu_chr_open_eventfd (CharDriverState *chr,int eventfd); +void qemu_chr_open_eventfd (CharDriverState *chr,int eventfd);extern int term_escape_char;CharDriverState *qemu_char_get_next_serial (void );/* msmouse */ -CharDriverState *qemu_chr_open_msmouse (CharDriverState *chr); +void qemu_chr_open_msmouse (CharDriverState *chr,Error **errp);/* baum.c */ -CharDriverState *chr_baum_init (CharDriverState *chr); +void chr_baum_init (CharDriverState *chr,Error **errp);#endif
include/ui/console.h +2 -2
@@ -299,9 +299,9 @@ void qemu_console_copy(QemuConsole *con, int src_x, int src_y,DisplaySurface *qemu_console_surface (QemuConsole *con);DisplayState *qemu_console_displaystate (QemuConsole *console); -typedef CharDriverState *(VcHandler )(CharDriverState *chr,ChardevVC *vc); +typedef void (VcHandler )(CharDriverState *chr,ChardevVC *vc,Error **errp); -CharDriverState *vc_init (CharDriverState *chr,ChardevVC *vc); +void vc_init (CharDriverState *chr,ChardevVC *vc,Error **errp);void register_vc_handler (VcHandler *handler);/* sdl.c */
include/ui/qemu-spice.h +8 -7
@@ -48,16 +48,17 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port,void do_info_spice_print (Monitor *mon,const QObject *data);void do_info_spice (Monitor *mon,QObject **ret_data); -CharDriverState *qemu_chr_open_spice_vmc (CharDriverState *chr, -const char *type); +void qemu_chr_open_spice_vmc (CharDriverState *chr,const char *type, +Error **errp);#if SPICE_SERVER_VERSION >=0x000c02 -CharDriverState *qemu_chr_open_spice_port (CharDriverState *chr, -const char *name); +void qemu_chr_open_spice_port (CharDriverState *chr,const char *name, +Error **errp);void qemu_spice_register_ports (void );#else -static inline CharDriverState *qemu_chr_open_spice_port (CharDriverState *chr, -const char *name) -{return NULL ; } +static inline void qemu_chr_open_spice_port (CharDriverState *chr, +const char *name, +Error **errp) +{error_setg (errp,"open spice port not supported" ); }#endif #else /* CONFIG_SPICE */
qemu-char.c +154 -182
849 lines not on this page · raw message
spice-qemu-char.c +8 -7
@@ -261,8 +261,8 @@ static void print_allowed_subtypes(void)fprintf (stderr,"\n" ); } -static CharDriverState *chr_open (CharDriverState *chr,const char *subtype, -void (*set_fe_open)(struct CharDriverState *,int )) +static void chr_open (CharDriverState *chr,const char *subtype, +void (*set_fe_open)(struct CharDriverState *,int )) {SpiceCharDriver *s;
@@ -279,18 +279,18 @@ static CharDriverState *chr_open(CharDriverState *chr, const char *subtype, chr->chr_fe_event = spice_chr_fe_event;QLIST_INSERT_HEAD (&spice_chars, s, next); - -return chr; } -CharDriverState *qemu_chr_open_spice_vmc (CharDriverState *chr,const char *type) +void qemu_chr_open_spice_vmc (CharDriverState *chr,const char *type, +Error *errp) {const char **psubtype =spice_server_char_device_recognized_subtypes ();if (type ==NULL ) {fprintf (stderr,"spice-qemu-char: missing name parameter\n" );print_allowed_subtypes (); -return NULL ; +error_setg (errp,"open spice vmc failed" ); +return ; }for (; *psubtype !=NULL ; ++psubtype) {if (strcmp (type, *psubtype) ==0 ) {
@@ -300,7 +300,8 @@ CharDriverState *qemu_chr_open_spice_vmc(CharDriverState *chr, const char *type)if (*psubtype ==NULL ) {fprintf (stderr,"spice-qemu-char: unsupported type: %s\n" , type);print_allowed_subtypes (); -return NULL ; +error_setg (errp,"open spice vmc failed" ); +return ; }return chr_open (chr, type, spice_vmc_set_fe_open);
ui/console.c +5 -5
@@ -1724,7 +1724,7 @@ static void text_console_do_init(CharDriverState *chr, DisplayState *ds) chr->init (chr); } -static CharDriverState *text_console_init (CharDriverState *chr,ChardevVC *vc) +static void text_console_init (CharDriverState *chr,ChardevVC *vc,Error **errp) {QemuConsole *s;unsigned width =0 ;
@@ -1751,7 +1751,8 @@ static CharDriverState *text_console_init(CharDriverState *chr, ChardevVC *vc) }if (!s) { -return NULL ; +error_setg (errp,"Unable to create a new vc console" ); +return ; } s->chr = chr;
@@ -1765,14 +1766,13 @@ static CharDriverState *text_console_init(CharDriverState *chr, ChardevVC *vc)if (display_state) {text_console_do_init (chr, display_state); } -return chr; }static VcHandler *vc_handler = text_console_init; -CharDriverState *vc_init (CharDriverState *chr,ChardevVC *vc) +void vc_init (CharDriverState *chr,ChardevVC *vc,Error **errp) { -return vc_handler (chr, vc); +vc_handler (chr, vc, errp); }void register_vc_handler (VcHandler *handler)
ui/gtk.c +1 -3
@@ -1125,15 +1125,13 @@ static int gd_vc_chr_write(CharDriverState *chr, const uint8_t *buf, int len)static int nb_vcs;static CharDriverState *vcs[MAX_VCS ]; -static CharDriverState *gd_vc_handler (CharDriverState *chr,ChardevVC *unused) +static void gd_vc_handler (CharDriverState *chr,ChardevVC *unused,Error **errp) { chr->chr_write = gd_vc_chr_write;/* defer OPENED events until our vc is fully initialized */ chr->explicit_be_open = true; vcs[nb_vcs++] = chr; - -return chr; }void early_gtk_display_init (void )
Signature · 1 line
1.8.3.1
PATCH 7/7 console: Don't use the console if NULL
In reply to [email protected]From: Corey Minyard <[email protected]>
Two spots used an allocated console, even though new_console could return NULL. Check the return value first.
--- ui/console.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-)
ui/console.c +6 -1
@@ -1582,6 +1582,9 @@ QemuConsole *graphic_console_init(DeviceState *dev, ds =get_alloc_displaystate ();trace_console_gfx_new (); s =new_console (ds,GRAPHIC_CONSOLE ); +if (!s) { +return NULL ; + } s->hw_ops = hw_ops; s->hw = opaque;if (dev) {
@@ -1747,7 +1750,9 @@ static void text_console_init(CharDriverState *chr, ChardevVC *vc, Error **errp) s =new_console (NULL ,TEXT_CONSOLE ); }else { s =new_console (NULL ,TEXT_CONSOLE_FIXED_SIZE ); - s->surface =qemu_create_displaysurface (width, height); +if (s) { + s->surface =qemu_create_displaysurface (width, height); + } }if (!s) {
Signature · 1 line
1.8.3.1