[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if
From: |
Weidong Huang |
Subject: |
Re: [Qemu-devel] [PATCH 2/7] qemu-char: Allow a chardev to reconnect if disconnected |
Date: |
Thu, 6 Mar 2014 14:47:44 +0800 |
User-agent: |
Mozilla/5.0 (Windows NT 6.1; rv:11.0) Gecko/20120327 Thunderbird/11.0.1 |
On 2014/3/5 8:38, address@hidden wrote:
> From: Corey Minyard <address@hidden>
>
> 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 <address@hidden>
> ---
> include/sysemu/char.h | 16 ++++-
> qemu-char.c | 165
> +++++++++++++++++++++++++++++++++++++++++++++-----
> qemu-options.hx | 11 +++-
> 3 files changed, 173 insertions(+), 19 deletions(-)
>
> diff --git a/include/sysemu/char.h b/include/sysemu/char.h
> index f6844dd..1800c54 100644
> --- a/include/sysemu/char.h
> +++ b/include/sysemu/char.h
> @@ -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));
>
> diff --git a/qemu-char.c b/qemu-char.c
> index fc688cf..d9838aa 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -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,
> diff --git a/qemu-options.hx b/qemu-options.hx
> index 56e5fdf..1002a3d 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -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.
>
> address@hidden -chardev socket ,address@hidden address@hidden options} or
> @var{unix options}] [,server] [,nowait] [,telnet]
> address@hidden -chardev socket ,address@hidden address@hidden options} or
> @var{unix options}] [,server] [,nowait] [,telnet] [,address@hidden
>
> 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.
>
> address@hidden 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?
[Qemu-devel] [PATCH 3/7] qemu-char: Wait until socket connect to report connected, minyard, 2014/03/04
[Qemu-devel] [PATCH 4/7] qemu-char: remove free of chr from win_stdio_close, minyard, 2014/03/04
[Qemu-devel] [PATCH 5/7] qemu-char: Close fd at end of file, minyard, 2014/03/04
[Qemu-devel] [PATCH 6/7] qemu-char: Clean up error handling in qmp_chardev_add, minyard, 2014/03/04
[Qemu-devel] [PATCH 7/7] console: Don't use the console if NULL, minyard, 2014/03/04