All of lore.kernel.org
 help / color / mirror / Atom feed
From: Paolo Bonzini <pbonzini@redhat.com>
To: minyard@acm.org, qemu-devel@nongnu.org
Cc: mjg59@srcf.ucam.org, mst@redhat.com, hwd@huawei.com,
	bcketchum@gmail.com, Corey Minyard <cminyard@mvista.com>,
	afaerber@suse.de
Subject: Re: [Qemu-devel] [PATCH 5/6] qemu-char: Add reconnecting to client sockets
Date: Thu, 02 Oct 2014 14:25:34 +0200	[thread overview]
Message-ID: <542D443E.5040006@redhat.com> (raw)
In-Reply-To: <1412197761-18504-6-git-send-email-minyard@acm.org>

Il 01/10/2014 23:09, minyard@acm.org ha scritto:
> From: Corey Minyard <cminyard@mvista.com>
> 
> 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 <cminyard@mvista.com>
> ---
>  qapi-schema.json | 15 ++++++----
>  qemu-char.c      | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
>  qemu-options.hx  | 20 ++++++++-----
>  3 files changed, 105 insertions(+), 18 deletions(-)
> 
> diff --git a/qapi-schema.json b/qapi-schema.json
> index 4bfaf20..148097b 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 b118e88..f33173c 100644
> --- a/qemu-char.c
> +++ b/qemu-char.c
> @@ -2501,8 +2501,20 @@ 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;

Please assert that s->connected == 0 here.

> +    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 +2796,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 +2979,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,7 +3032,28 @@ static bool qemu_chr_finish_socket_connection(CharDriverState *chr, int fd,
>          tcp_chr_connect(chr);
>      }
>  
> -    return chr;
> +    return true;

As mentioned in patch 2, this should be done there.

> +}
> +
> +static void qemu_chr_socket_connected(int fd, void *opaque)
> +{
> +    CharDriverState *chr = opaque;
> +    TCPCharDriver *s = chr->opaque;
> +    Error *err = NULL;
> +
> +    if (fd >= 0) {
> +        if (qemu_chr_finish_socket_connection(chr, fd, &err)) {

If the errp argument is removed in qemu_chr_finish_socket_connection...

> +            return;
> +        }
> +        if (err) {
> +            error_report("%s", error_get_pretty(err));
> +            error_free(err);
> +        }
> +        closesocket(fd);
> +    }
> +
> +    s->connected = 0;

... and we are sure that s->connected is already 0, this function can be
simply

    CharDriverState *chr = opaque;

    if (fd < 0) {
        qemu_chr_socket_restart_timer(chr);
        return;
     }

    qemu_chr_finish_socket_connection(chr, fd);

I have no other comment.  Everything here is basically rippling from the
observation that errp is unused after patch 4; sorry for not noticing
that earlier.

Paolo

> +    qemu_chr_socket_restart_timer(chr);
>  }
>  
>  static bool qemu_chr_open_socket_fd(CharDriverState *chr, Error **errp)
> @@ -3023,7 +3063,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) {
> @@ -3450,6 +3493,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");
> @@ -3476,6 +3520,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) {
> @@ -3875,6 +3921,9 @@ QemuOptsList qemu_chardev_opts = {
>              .name = "delay",
>              .type = QEMU_OPT_BOOL,
>          },{
> +            .name = "reconnect",
> +            .type = QEMU_OPT_NUMBER,
> +        },{
>              .name = "telnet",
>              .type = QEMU_OPT_BOOL,
>          },{
> @@ -4018,6 +4067,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)
>  {
> @@ -4028,6 +4097,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));
> @@ -4060,13 +4130,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 365b56c..22cf3b9 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.
> 

  reply	other threads:[~2014-10-02 12:25 UTC|newest]

Thread overview: 26+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-10-01 21:09 [Qemu-devel] [PATCH v4 0/6] Add reconnect capability to sockets minyard
2014-10-01 21:09 ` [Qemu-devel] [PATCH 1/6] qemu-char: Make the filename size for a chardev a #define minyard
2014-10-01 21:09 ` [Qemu-devel] [PATCH 2/6] qemu-char: Rework qemu_chr_open_socket() for reconnect minyard
2014-10-02 12:19   ` Paolo Bonzini
2014-10-01 21:09 ` [Qemu-devel] [PATCH 3/6] qemu-char: Move some items into TCPCharDriver minyard
2014-10-01 21:09 ` [Qemu-devel] [PATCH 4/6] qemu-char: set socket filename to disconnected when not connected minyard
2014-10-02 12:20   ` Paolo Bonzini
2014-10-01 21:09 ` [Qemu-devel] [PATCH 5/6] qemu-char: Add reconnecting to client sockets minyard
2014-10-02 12:25   ` Paolo Bonzini [this message]
2014-10-01 21:09 ` [Qemu-devel] [PATCH 6/6] qemu-char: Print the remote and local addresses for a socket minyard
  -- strict thread matches above, loose matches on Subject: below --
2014-10-02 16:17 [Qemu-devel] [PATCH v5 0/6] Add reconnecting to client sockets minyard
2014-10-02 16:17 ` [Qemu-devel] [PATCH 5/6] qemu-char: " minyard
2014-10-03 22:22   ` Paolo Bonzini
2014-10-04  3:24     ` Corey Minyard
2014-10-04 15:54       ` Paolo Bonzini
2014-09-25 20:07 [Qemu-devel] [PATCH v3 0/6] chardev: " minyard
2014-09-25 20:07 ` [Qemu-devel] [PATCH 5/6] qemu-char: " minyard
2014-10-01 12:38   ` Corey Minyard
2014-10-01 18:15     ` Paolo Bonzini
2014-10-01 19:10   ` Eric Blake
2014-10-01 21:03     ` Corey Minyard
2014-09-22 16:59 [Qemu-devel] [PATCH v2 0/6] chardev: " minyard
2014-09-22 16:59 ` [Qemu-devel] [PATCH 5/6] qemu-char: " minyard
2014-09-22 20:26   ` Eric Blake
2014-09-21 23:04 [Qemu-devel] [PATCH 0/6] Add reconnect capability for " minyard
2014-09-21 23:04 ` [Qemu-devel] [PATCH 5/6] qemu-char: Add reconnecting to " minyard
2014-09-22  8:02   ` Paolo Bonzini
2014-09-22 20:24   ` Eric Blake
2014-09-22 20:36     ` Corey Minyard
2014-09-22 20:53       ` Eric Blake

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=542D443E.5040006@redhat.com \
    --to=pbonzini@redhat.com \
    --cc=afaerber@suse.de \
    --cc=bcketchum@gmail.com \
    --cc=cminyard@mvista.com \
    --cc=hwd@huawei.com \
    --cc=minyard@acm.org \
    --cc=mjg59@srcf.ucam.org \
    --cc=mst@redhat.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.