From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from eggs.gnu.org ([2001:4830:134:3::10]:34594) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZEfVl-0001wa-SY for qemu-devel@nongnu.org; Mon, 13 Jul 2015 11:19:20 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ZEfVi-0003R2-B3 for qemu-devel@nongnu.org; Mon, 13 Jul 2015 11:19:17 -0400 Received: from mx1.redhat.com ([209.132.183.28]:34940) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ZEfVh-0003Qb-OK for qemu-devel@nongnu.org; Mon, 13 Jul 2015 11:19:14 -0400 Date: Mon, 13 Jul 2015 18:18:51 +0300 From: "Michael S. Tsirkin" Message-ID: <20150713181721-mutt-send-email-mst@redhat.com> References: <235736C59B081941AD5490A89823819B1E69857D@xmb-rcd-x11.cisco.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <235736C59B081941AD5490A89823819B1E69857D@xmb-rcd-x11.cisco.com> Subject: Re: [Qemu-devel] [PATCH] net: vhost-user reconnect List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: "Matus Fabian -X (matfabia - Pantheon Technologies SRO at Cisco)" Cc: "pbonzini@redhat.com" , "jasowang@redhat.com" , "qemu-devel@nongnu.org" , "stefanha@redhat.com" , "Damjan Marion (damarion)" On Mon, Jul 13, 2015 at 02:44:37PM +0000, Matus Fabian -X (matfabia - Pantheon Technologies SRO at Cisco) wrote: > If userspace switch restarts it will reconnect to unix socket but QEMU will not > send any vhost-user information and that basically means that userspace switch > restart requires restart of VM. > Fix detects that userspace switch is disconnected and notify VM that link status > is down. After user space switch is reconnected QEMU send vhost-user information > to the userspace switch and notify VM that link is up. > > Signed-off-by: Matus Fabian This only works most of the time. Reconnect can't be implemented without guest changes. See http://mid.gmane.org/1434945048-27958-1-git-send-email-mukawa@igel.co.jp > --- > hw/net/vhost_net.c | 126 +++++++++++++++++++++++++++++++++++++++++ > hw/net/virtio-net.c | 62 ++++++++++++-------- > hw/virtio/vhost.c | 137 +++++++++++++++++++++++++++++++++++++++++++++ > hw/virtio/virtio.c | 24 ++++++++ > include/hw/virtio/vhost.h | 8 +++ > include/hw/virtio/virtio.h | 1 + > include/net/vhost_net.h | 7 +++ > include/sysemu/char.h | 3 + > net/vhost-user.c | 24 ++++++++ > qemu-char.c | 101 +++++++++++++++++++++++++++++++++ > 10 files changed, 470 insertions(+), 23 deletions(-) > > diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c > index 9bd360b..16a31d7 100644 > --- a/hw/net/vhost_net.c > +++ b/hw/net/vhost_net.c > @@ -167,6 +167,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) > if (r < 0) { > goto fail; > } > + net->dev.features |= 1 << VIRTIO_NET_F_STATUS; > if (backend_kernel) { > if (!qemu_has_vnet_hdr_len(options->net_backend, > sizeof(struct virtio_net_hdr_mrg_rxbuf))) { > @@ -380,6 +381,23 @@ void vhost_net_cleanup(struct vhost_net *net) > g_free(net); > } > > +void vhost_net_reconnect_init(VHostNetState *net) > +{ > + int r; > + > + r = vhost_dev_reconnect_init(&net->dev); > + if (r < 0) { > + fprintf(stderr, "vhost reconnect init failed: %d\n", r); > + fflush(stderr); > + } > + net->dev.features |= 1 << VIRTIO_NET_F_STATUS; > +} > + > +void vhost_net_hup(VHostNetState *net) > +{ > + vhost_dev_hup(&net->dev); > +} > + > bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) > { > return vhost_virtqueue_pending(&net->dev, idx); > @@ -412,6 +430,89 @@ VHostNetState *get_vhost_net(NetClientState *nc) > > return vhost_net; > } > + > +bool vhost_net_in_reconnect(NetClientState *nc) > +{ > + VHostNetState *vhost_net = 0; > + > + if (!nc) { > + return false; > + } > + switch (nc->info->type) { > + case NET_CLIENT_OPTIONS_KIND_TAP: > + vhost_net = tap_get_vhost_net(nc); > + break; > + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: > + vhost_net = vhost_user_get_vhost_net(nc); > + break; > + default: > + break; > + } > + > + if (!vhost_net) { > + return false; > + } > + > + return vhost_dev_in_reconnect(&vhost_net->dev); > +} > + > +bool vhost_net_is_hup(NetClientState *nc) > +{ > + VHostNetState *vhost_net = 0; > + > + if (!nc) { > + return false; > + } > + switch (nc->info->type) { > + case NET_CLIENT_OPTIONS_KIND_TAP: > + vhost_net = tap_get_vhost_net(nc); > + break; > + case NET_CLIENT_OPTIONS_KIND_VHOST_USER: > + vhost_net = vhost_user_get_vhost_net(nc); > + break; > + default: > + break; > + } > + > + if (!vhost_net) { > + return false; > + } > + > + return vhost_dev_is_hup(&vhost_net->dev); > +} > + > +int vhost_net_reconnect(VirtIODevice *dev, NetClientState *ncs, > + int total_queues) > +{ > + int r, i; > + > + BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(dev))); > + VirtioBusState *vbus = VIRTIO_BUS(qbus); > + VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(vbus); > + > + if (!k->set_guest_notifiers) { > + error_report("binding does not support guest notifiers"); > + r = -ENOSYS; > + return r; > + } > + > + r = k->set_guest_notifiers(qbus->parent, total_queues * 2, true); > + if (r < 0) { > + error_report("Error binding guest notifier: %d", -r); > + return r; > + } > + > + for (i = 0; i < total_queues; i++) { > + VHostNetState *net = get_vhost_net(ncs[i].peer); > + r = vhost_dev_reconnect(&net->dev, dev); > + vhost_dev_reconnect_done(&net->dev); > + if (r < 0) { > + return r; > + } > + } > + return 0; > +} > + > #else > struct vhost_net *vhost_net_init(VhostNetOptions *options) > { > @@ -457,4 +558,29 @@ VHostNetState *get_vhost_net(NetClientState *nc) > { > return 0; > } > + > +void vhost_net_reconnect_init(VHostNetState *net) > +{ > +} > + > +bool vhost_net_in_reconnect(NetClientState *nc) > +{ > + return false; > +} > + > +int vhost_net_reconnect(VirtIODevice *dev, NetClientState *ncs, > + int total_queues) > +{ > + return -ENOSYS; > +} > + > +void vhost_net_hup(VHostNetState *net) > +{ > +} > + > +bool vhost_net_is_hup(NetClientState *nc) > +{ > + return false; > +} > + > #endif > diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c > index d728233..490a187 100644 > --- a/hw/net/virtio-net.c > +++ b/hw/net/virtio-net.c > @@ -121,6 +121,11 @@ static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) > return; > } > > + if (vhost_net_in_reconnect(nc->peer)) { > + vhost_net_reconnect(vdev, n->nic->ncs, queues); > + return; > + } > + > if ((virtio_net_started(n, status) && !nc->peer->link_down) == > !!n->vhost_started) { > return; > @@ -156,36 +161,44 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) > { > VirtIONet *n = VIRTIO_NET(vdev); > VirtIONetQueue *q; > + NetClientState *nc = qemu_get_queue(n->nic); > int i; > uint8_t queue_status; > > - virtio_net_vhost_status(n, status); > - > - for (i = 0; i < n->max_queues; i++) { > - q = &n->vqs[i]; > + if (vhost_net_is_hup(nc->peer) && !(status & VIRTIO_NET_S_LINK_UP)) { > + return; > + } > > - if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { > - queue_status = 0; > - } else { > - queue_status = status; > - } > + virtio_net_vhost_status(n, status); > > - if (!q->tx_waiting) { > - continue; > - } > + if (!vhost_net_in_reconnect(nc->peer)) { > + for (i = 0; i < n->max_queues; i++) { > + q = &n->vqs[i]; > > - if (virtio_net_started(n, queue_status) && !n->vhost_started) { > - if (q->tx_timer) { > - timer_mod(q->tx_timer, > - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + n->tx_timeout); > + if ((!n->multiqueue && i != 0) || i >= n->curr_queues) { > + queue_status = 0; > } else { > - qemu_bh_schedule(q->tx_bh); > + queue_status = status; > } > - } else { > - if (q->tx_timer) { > - timer_del(q->tx_timer); > + > + if (!q->tx_waiting) { > + continue; > + } > + > + if (virtio_net_started(n, queue_status) && !n->vhost_started) { > + if (q->tx_timer) { > + timer_mod(q->tx_timer, > + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + > + n->tx_timeout); > + } else { > + qemu_bh_schedule(q->tx_bh); > + } > } else { > - qemu_bh_cancel(q->tx_bh); > + if (q->tx_timer) { > + timer_del(q->tx_timer); > + } else { > + qemu_bh_cancel(q->tx_bh); > + } > } > } > } > @@ -197,10 +210,13 @@ static void virtio_net_set_link_status(NetClientState *nc) > VirtIODevice *vdev = VIRTIO_DEVICE(n); > uint16_t old_status = n->status; > > - if (nc->link_down) > + if (nc->link_down) { > n->status &= ~VIRTIO_NET_S_LINK_UP; > - else > + vdev->status &= ~VIRTIO_NET_S_LINK_UP; > + } else { > n->status |= VIRTIO_NET_S_LINK_UP; > + vdev->status |= VIRTIO_NET_S_LINK_UP; > + } > > if (n->status != old_status) > virtio_notify_config(vdev); > diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c > index 2712c6f..5edaa9a 100644 > --- a/hw/virtio/vhost.c > +++ b/hw/virtio/vhost.c > @@ -817,6 +817,51 @@ fail_alloc_desc: > return r; > } > > +static int vhost_virtqueue_resend(struct vhost_dev *dev, > + struct VirtIODevice *vdev, > + struct vhost_virtqueue *vq, > + unsigned idx) > +{ > + int r; > + int vhost_vq_index = idx - dev->vq_index; > + struct vhost_vring_file file = { > + .index = vhost_vq_index > + }; > + struct vhost_vring_state state = { > + .index = vhost_vq_index > + }; > + struct VirtQueue *vvq = virtio_get_queue(vdev, idx); > + > + assert(idx >= dev->vq_index && idx < dev->vq_index + dev->nvqs); > + > + vq->num = state.num = virtio_queue_get_num(vdev, idx); > + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_NUM, &state); > + if (r) { > + return -errno; > + } > + > + state.num = virtio_queue_reset_and_get_last_avail_idx(vdev, idx); > + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_BASE, &state); > + if (r) { > + return -errno; > + } > + r = vhost_virtqueue_set_addr(dev, vq, vhost_vq_index, dev->log_enabled); > + if (r < 0) { > + return -errno; > + } > + > + file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq)); > + r = dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_KICK, &file); > + if (r) { > + return -errno; > + } > + > + /* Clear and discard previous events if any. */ > + event_notifier_test_and_clear(&vq->masked_notifier); > + > + return 0; > +} > + > static void vhost_virtqueue_stop(struct vhost_dev *dev, > struct VirtIODevice *vdev, > struct vhost_virtqueue *vq, > @@ -872,6 +917,17 @@ static void vhost_eventfd_del(MemoryListener *listener, > { > } > > +static int vhost_virtqueue_recon(struct vhost_dev *dev, > + struct vhost_virtqueue *vq, int n) > +{ > + struct vhost_vring_file file = { > + .index = n, > + }; > + > + file.fd = event_notifier_get_fd(&vq->masked_notifier); > + return dev->vhost_ops->vhost_call(dev, VHOST_SET_VRING_CALL, &file); > +} > + > static int vhost_virtqueue_init(struct vhost_dev *dev, > struct vhost_virtqueue *vq, int n) > { > @@ -962,6 +1018,8 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, > hdev->log_size = 0; > hdev->log_enabled = false; > hdev->started = false; > + hdev->in_reconnect = false; > + hdev->hup = false; > hdev->memory_changed = false; > memory_listener_register(&hdev->memory_listener, &address_space_memory); > return 0; > @@ -1184,3 +1242,82 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev) > hdev->log_size = 0; > } > > +int vhost_dev_reconnect_init(struct vhost_dev *hdev) > +{ > + uint64_t features; > + int i, r; > + > + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_OWNER, NULL); > + if (r < 0) { > + return r; > + } > + r = hdev->vhost_ops->vhost_call(hdev, VHOST_GET_FEATURES, &features); > + if (r < 0) { > + return r; > + } > + hdev->features = features; > + hdev->in_reconnect = true; > + > + for (i = 0; i < hdev->nvqs; ++i) { > + r = vhost_virtqueue_recon(hdev, hdev->vqs + i, i); > + if (r < 0) { > + return r; > + } > + } > + > + return r; > +} > + > +void vhost_dev_hup(struct vhost_dev *hdev) > +{ > + hdev->hup = true; > +} > + > +bool vhost_dev_in_reconnect(struct vhost_dev *hdev) > +{ > + return hdev->in_reconnect; > +} > + > +bool vhost_dev_is_hup(struct vhost_dev *hdev) > +{ > + return hdev->hup; > +} > + > +void vhost_dev_reconnect_done(struct vhost_dev *hdev) > +{ > + hdev->in_reconnect = false; > + hdev->hup = false; > +} > + > +int vhost_dev_reconnect(struct vhost_dev *hdev, VirtIODevice *vdev) > +{ > + int i, r; > + > + r = vhost_dev_set_features(hdev, hdev->log_enabled); > + if (r < 0) { > + return r; > + } > + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_MEM_TABLE, hdev->mem); > + if (r < 0) { > + return r; > + } > + for (i = 0; i < hdev->nvqs; ++i) { > + r = vhost_virtqueue_resend(hdev, vdev, hdev->vqs + i, > + hdev->vq_index + i); > + if (r < 0) { > + return r; > + } > + } > + > + if (hdev->log_enabled) { > + hdev->log_size = vhost_get_log_size(hdev); > + hdev->log = hdev->log_size ? > + g_malloc0(hdev->log_size * sizeof *hdev->log) : NULL; > + r = hdev->vhost_ops->vhost_call(hdev, VHOST_SET_LOG_BASE, hdev->log); > + if (r < 0) { > + return r; > + } > + } > + > + return r; > +} > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c > index ee4e07c..ea7e1c7 100644 > --- a/hw/virtio/virtio.c > +++ b/hw/virtio/virtio.c > @@ -151,6 +151,13 @@ static inline uint16_t vring_avail_idx(VirtQueue *vq) > return virtio_lduw_phys(vq->vdev, pa); > } > > +static inline void vring_set_avail_idx(VirtQueue *vq, uint16_t val) > +{ > + hwaddr pa; > + pa = vq->vring.avail + offsetof(VRingAvail, idx); > + virtio_stw_phys(vq->vdev, pa, val); > +} > + > static inline uint16_t vring_avail_ring(VirtQueue *vq, int i) > { > hwaddr pa; > @@ -1424,6 +1431,23 @@ uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n) > return vdev->vq[n].last_avail_idx; > } > > +uint16_t virtio_queue_reset_and_get_last_avail_idx(VirtIODevice *vdev, int n) > +{ > + uint16_t r; > + VirtQueue *q; > + > + q = virtio_get_queue(vdev, n); > + > + if (n % 2) { > + r = vring_avail_idx(q); > + vring_set_avail_idx(q, r); > + } else { > + r = vring_used_idx(q); > + } > + > + return r; > +} > + > void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx) > { > vdev->vq[n].last_avail_idx = idx; > diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h > index dd51050..cae4fe9 100644 > --- a/include/hw/virtio/vhost.h > +++ b/include/hw/virtio/vhost.h > @@ -48,6 +48,8 @@ struct vhost_dev { > unsigned long long acked_features; > unsigned long long backend_features; > bool started; > + bool in_reconnect; > + bool hup; > bool log_enabled; > unsigned long long log_size; > Error *migration_blocker; > @@ -67,6 +69,12 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev); > void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev); > int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); > void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev); > +int vhost_dev_reconnect_init(struct vhost_dev *hdev); > +int vhost_dev_reconnect(struct vhost_dev *hdev, VirtIODevice *vdev); > +bool vhost_dev_in_reconnect(struct vhost_dev *hdev); > +void vhost_dev_reconnect_done(struct vhost_dev *hdev); > +void vhost_dev_hup(struct vhost_dev *hdev); > +bool vhost_dev_is_hup(struct vhost_dev *hdev); > > /* Test and clear masked event pending status. > * Should be called after unmask to avoid losing events. > diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h > index 473fb75..c1564f1 100644 > --- a/include/hw/virtio/virtio.h > +++ b/include/hw/virtio/virtio.h > @@ -225,6 +225,7 @@ hwaddr virtio_queue_get_avail_size(VirtIODevice *vdev, int n); > hwaddr virtio_queue_get_used_size(VirtIODevice *vdev, int n); > hwaddr virtio_queue_get_ring_size(VirtIODevice *vdev, int n); > uint16_t virtio_queue_get_last_avail_idx(VirtIODevice *vdev, int n); > +uint16_t virtio_queue_reset_and_get_last_avail_idx(VirtIODevice *vdev, int n); > void virtio_queue_set_last_avail_idx(VirtIODevice *vdev, int n, uint16_t idx); > void virtio_queue_invalidate_signalled_used(VirtIODevice *vdev, int n); > VirtQueue *virtio_get_queue(VirtIODevice *vdev, int n); > diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h > index 840d4b1..5ef6fdd 100644 > --- a/include/net/vhost_net.h > +++ b/include/net/vhost_net.h > @@ -20,6 +20,13 @@ void vhost_net_stop(VirtIODevice *dev, NetClientState *ncs, int total_queues); > > void vhost_net_cleanup(VHostNetState *net); > > +void vhost_net_reconnect_init(VHostNetState *net); > +bool vhost_net_in_reconnect(NetClientState *nc); > +int vhost_net_reconnect(VirtIODevice *dev, NetClientState *ncs, > + int total_queues); > +void vhost_net_hup(VHostNetState *net); > +bool vhost_net_is_hup(NetClientState *nc); > + > uint64_t vhost_net_get_features(VHostNetState *net, uint64_t features); > void vhost_net_ack_features(VHostNetState *net, uint64_t features); > > diff --git a/include/sysemu/char.h b/include/sysemu/char.h > index 832b7fe..9eb9b8c 100644 > --- a/include/sysemu/char.h > +++ b/include/sysemu/char.h > @@ -18,6 +18,8 @@ > #define CHR_EVENT_MUX_IN 3 /* mux-focus was set to this terminal */ > #define CHR_EVENT_MUX_OUT 4 /* mux-focus will move on */ > #define CHR_EVENT_CLOSED 5 /* connection closed */ > +#define CHR_EVENT_RECON 6 /* connection reconnected */ > +#define CHR_EVENT_HUP 7 /* connection hang up */ > > > #define CHR_IOCTL_SERIAL_SET_PARAMS 1 > @@ -84,6 +86,7 @@ struct CharDriverState { > int avail_connections; > int is_mux; > guint fd_in_tag; > + guint fd_hup_tag; > QemuOpts *opts; > QTAILQ_ENTRY(CharDriverState) next; > }; > diff --git a/net/vhost-user.c b/net/vhost-user.c > index b51bc04..33511a4 100644 > --- a/net/vhost-user.c > +++ b/net/vhost-user.c > @@ -112,6 +112,20 @@ static void net_vhost_link_down(VhostUserState *s, bool link_down) > } > } > > +static void vhost_user_reconnect(VhostUserState *s) > +{ > + if (vhost_user_running(s)) { > + vhost_net_reconnect_init(s->vhost_net); > + } > +} > + > +static void vhost_user_hup(VhostUserState *s) > +{ > + if (vhost_user_running(s)) { > + vhost_net_hup(s->vhost_net); > + } > +} > + > static void net_vhost_user_event(void *opaque, int event) > { > VhostUserState *s = opaque; > @@ -127,6 +141,16 @@ static void net_vhost_user_event(void *opaque, int event) > vhost_user_stop(s); > error_report("chardev \"%s\" went down", s->nc.info_str); > break; > + case CHR_EVENT_RECON: > + vhost_user_reconnect(s); > + net_vhost_link_down(s, false); > + error_report("chardev \"%s\" reconnected", s->nc.info_str); > + break; > + case CHR_EVENT_HUP: > + vhost_user_hup(s); > + net_vhost_link_down(s, true); > + error_report("chardev \"%s\" hang up", s->nc.info_str); > + break; > } > } > > diff --git a/qemu-char.c b/qemu-char.c > index 617e034..9f42d04 100644 > --- a/qemu-char.c > +++ b/qemu-char.c > @@ -2904,6 +2904,105 @@ CharDriverState *qemu_chr_open_eventfd(int eventfd) > } > #endif > > +static gboolean tcp_chr_chan_hup(GIOChannel *chanel, GIOCondition cond, > + void *opaque); > + > +static void tcp_chr_telnet_init(int fd); > + > +static gboolean tcp_chr_recon(GIOChannel *chanel, GIOCondition cond, > + void *opaque) > +{ > + CharDriverState *chr = opaque; > + TCPCharDriver *s = chr->opaque; > + struct sockaddr_in saddr; > +#ifndef _WIN32 > + struct sockaddr_un uaddr; > +#endif > + struct sockaddr *addr; > + socklen_t len; > + int fd; > + > + for (;;) { > +#ifndef _WIN32 > + if (s->is_unix) { > + len = sizeof(uaddr); > + addr = (struct sockaddr *)&uaddr; > + } else > +#endif > + { > + len = sizeof(saddr); > + addr = (struct sockaddr *)&saddr; > + } > + fd = qemu_accept(s->listen_fd, addr, &len); > + if (fd < 0 && errno != EINTR) { > + s->listen_tag = 0; > + return FALSE; > + } else if (fd >= 0) { > + if (s->do_telnetopt) { > + tcp_chr_telnet_init(fd); > + } > + break; > + } > + } > + qemu_set_nonblock(fd); > + if (s->do_nodelay) { > + socket_set_nodelay(fd); > + } > + s->fd = fd; > + s->chan = io_channel_from_socket(fd); > + SocketAddress_to_str(chr->filename, CHR_MAX_FILENAME_SIZE, > + "", s->addr, s->is_listen, s->is_telnet); > + s->connected = 1; > + if (s->chan) { > + chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, > + tcp_chr_read, chr); > + } > + if (chr->chr_event) { > + chr->chr_event(chr->handler_opaque, CHR_EVENT_RECON); > + } > + if (s->listen_tag) { > + g_source_remove(s->listen_tag); > + s->listen_tag = 0; > + } > + if (s->chan) { > + chr->fd_hup_tag = g_io_add_watch(s->chan, G_IO_HUP, tcp_chr_chan_hup, > + chr); > + } > + > + return TRUE; > +} > + > +static gboolean tcp_chr_chan_hup(GIOChannel *chanel, GIOCondition cond, > + void *opaque) > +{ > + CharDriverState *chr = opaque; > + TCPCharDriver *s = chr->opaque; > + > + if (!(cond & G_IO_HUP)) { > + return FALSE; > + } > + if (chr->fd_hup_tag) { > + g_source_remove(chr->fd_hup_tag); > + chr->fd_hup_tag = 0; > + } > + if (s->is_listen) { > + s->listen_tag = g_io_add_watch(s->listen_chan, G_IO_IN, tcp_chr_recon, > + chr); > + } > + remove_fd_in_watch(chr); > + g_io_channel_unref(s->chan); > + 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); > + if (chr->chr_event) { > + chr->chr_event(chr->handler_opaque, CHR_EVENT_HUP); > + } > + > + return TRUE; > +} > + > static void tcp_chr_connect(void *opaque) > { > CharDriverState *chr = opaque; > @@ -2928,6 +3027,8 @@ static void tcp_chr_connect(void *opaque) > if (s->chan) { > chr->fd_in_tag = io_add_watch_poll(s->chan, tcp_chr_read_poll, > tcp_chr_read, chr); > + chr->fd_hup_tag = g_io_add_watch(s->chan, G_IO_HUP, tcp_chr_chan_hup, > + chr); > } > qemu_chr_be_generic_open(chr); > } > -- > 1.9.1