aboutsummaryrefslogtreecommitdiff
path: root/chardev/char-socket.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2018-03-16 11:05:03 +0000
committerPeter Maydell <peter.maydell@linaro.org>2018-03-16 11:05:03 +0000
commit3788c7b6e56fa34ee2a73e41706eb2a2447ba75a (patch)
tree8f016e7c9175686b4d7c2d1847c8cc877102dc6b /chardev/char-socket.c
parenta57946ff2acb9c0d95c9f127914540586b0b8c21 (diff)
parent0790f86861079b1932679d0f011e431aaf4ee9e2 (diff)
Merge remote-tracking branch 'remotes/bonzini/tags/for-upstream' into staging
* Record-replay lockstep execution, log dumper and fixes (Alex, Pavel) * SCSI fix to pass maximum transfer size (Daniel Barboza) * chardev fixes and improved iothread support (Daniel Berrangé, Peter) * checkpatch tweak (Eric) * make help tweak (Marc-André) * make more PCI NICs available with -net or -nic (myself) * change default q35 NIC to e1000e (myself) * SCSI support for NDOB bit (myself) * membarrier system call support (myself) * SuperIO refactoring (Philippe) * miscellaneous cleanups and fixes (Thomas) # gpg: Signature made Mon 12 Mar 2018 16:10:52 GMT # gpg: using RSA key BFFBD25F78C7AE83 # gpg: Good signature from "Paolo Bonzini <bonzini@gnu.org>" # gpg: aka "Paolo Bonzini <pbonzini@redhat.com>" # Primary key fingerprint: 46F5 9FBD 57D6 12E7 BFD4 E2F7 7E15 100C CD36 69B1 # Subkey fingerprint: F133 3857 4B66 2389 866C 7682 BFFB D25F 78C7 AE83 * remotes/bonzini/tags/for-upstream: (69 commits) tcg: fix cpu_io_recompile replay: update documentation replay: save vmstate of the asynchronous events replay: don't process async events when warping the clock scripts/replay-dump.py: replay log dumper replay: avoid recursive call of checkpoints replay: check return values of fwrite replay: push replay_mutex_lock up the call tree replay: don't destroy mutex at exit replay: make locking visible outside replay code replay/replay-internal.c: track holding of replay_lock replay/replay.c: bump REPLAY_VERSION again replay: save prior value of the host clock replay: added replay log format description replay: fix save/load vm for non-empty queue replay: fixed replay_enable_events replay: fix processing async events cpu-exec: fix exception_index handling hw/i386/pc: Factor out the superio code hw/alpha/dp264: Use the TYPE_SMC37C669_SUPERIO ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org> # Conflicts: # default-configs/i386-softmmu.mak # default-configs/x86_64-softmmu.mak
Diffstat (limited to 'chardev/char-socket.c')
-rw-r--r--chardev/char-socket.c137
1 files changed, 100 insertions, 37 deletions
diff --git a/chardev/char-socket.c b/chardev/char-socket.c
index 0c8d6d430a..d92c5aee73 100644
--- a/chardev/char-socket.c
+++ b/chardev/char-socket.c
@@ -41,6 +41,11 @@
#define TCP_MAX_FDS 16
typedef struct {
+ char buf[21];
+ size_t buflen;
+} TCPChardevTelnetInit;
+
+typedef struct {
Chardev parent;
QIOChannel *ioc; /* Client I/O channel */
QIOChannelSocket *sioc; /* Client master channel */
@@ -60,6 +65,8 @@ typedef struct {
bool is_listen;
bool is_telnet;
bool is_tn3270;
+ GSource *telnet_source;
+ TCPChardevTelnetInit *telnet_init;
GSource *reconnect_timer;
int64_t reconnect_time;
@@ -70,6 +77,7 @@ typedef struct {
OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
static gboolean socket_reconnect_timeout(gpointer opaque);
+static void tcp_chr_telnet_init(Chardev *chr);
static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
{
@@ -423,8 +431,8 @@ static void tcp_chr_disconnect(Chardev *chr)
tcp_chr_free_connection(chr);
if (s->listener) {
- qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
- chr, NULL);
+ qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
+ chr, NULL, chr->gcontext);
}
update_disconnected_filename(s);
if (emit_close) {
@@ -450,7 +458,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque)
len = s->max_size;
}
size = tcp_chr_recv(chr, (void *)buf, len);
- if (size == 0 || size == -1) {
+ if (size == 0 || (size == -1 && errno != EAGAIN)) {
/* connection closed */
tcp_chr_disconnect(chr);
} else if (size > 0) {
@@ -556,10 +564,33 @@ static void tcp_chr_connect(void *opaque)
qemu_chr_be_event(chr, CHR_EVENT_OPENED);
}
+static void tcp_chr_telnet_destroy(SocketChardev *s)
+{
+ if (s->telnet_source) {
+ g_source_destroy(s->telnet_source);
+ g_source_unref(s->telnet_source);
+ s->telnet_source = NULL;
+ }
+}
+
static void tcp_chr_update_read_handler(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
+ if (s->listener) {
+ /*
+ * It's possible that chardev context is changed in
+ * qemu_chr_be_update_read_handlers(). Reset it for QIO net
+ * listener if there is.
+ */
+ qio_net_listener_set_client_func_full(s->listener, tcp_chr_accept,
+ chr, NULL, chr->gcontext);
+ }
+
+ if (s->telnet_source) {
+ tcp_chr_telnet_init(CHARDEV(s));
+ }
+
if (!s->connected) {
return;
}
@@ -573,32 +604,30 @@ static void tcp_chr_update_read_handler(Chardev *chr)
}
}
-typedef struct {
- Chardev *chr;
- char buf[21];
- size_t buflen;
-} TCPChardevTelnetInit;
-
static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
GIOCondition cond G_GNUC_UNUSED,
gpointer user_data)
{
- TCPChardevTelnetInit *init = user_data;
+ SocketChardev *s = user_data;
+ Chardev *chr = CHARDEV(s);
+ TCPChardevTelnetInit *init = s->telnet_init;
ssize_t ret;
+ assert(init);
+
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) {
ret = 0;
} else {
- tcp_chr_disconnect(init->chr);
+ tcp_chr_disconnect(chr);
goto end;
}
}
init->buflen -= ret;
if (init->buflen == 0) {
- tcp_chr_connect(init->chr);
+ tcp_chr_connect(chr);
goto end;
}
@@ -607,16 +636,30 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
return G_SOURCE_CONTINUE;
end:
- g_free(init);
+ g_free(s->telnet_init);
+ s->telnet_init = NULL;
+ g_source_unref(s->telnet_source);
+ s->telnet_source = NULL;
return G_SOURCE_REMOVE;
}
static void tcp_chr_telnet_init(Chardev *chr)
{
SocketChardev *s = SOCKET_CHARDEV(chr);
- TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1);
+ TCPChardevTelnetInit *init;
size_t n = 0;
+ /* Destroy existing task */
+ tcp_chr_telnet_destroy(s);
+
+ if (s->telnet_init) {
+ /* We are possibly during a handshake already */
+ goto cont;
+ }
+
+ s->telnet_init = g_new0(TCPChardevTelnetInit, 1);
+ init = s->telnet_init;
+
#define IACSET(x, a, b, c) \
do { \
x[n++] = a; \
@@ -624,7 +667,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
x[n++] = c; \
} while (0)
- init->chr = chr;
if (!s->is_tn3270) {
init->buflen = 12;
/* Prep the telnet negotion to put telnet in binary,
@@ -647,10 +689,11 @@ static void tcp_chr_telnet_init(Chardev *chr)
#undef IACSET
- qio_channel_add_watch(
- s->ioc, G_IO_OUT,
- tcp_chr_telnet_init_io,
- init, NULL);
+cont:
+ s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
+ tcp_chr_telnet_init_io,
+ s, NULL,
+ chr->gcontext);
}
@@ -707,7 +750,7 @@ static void tcp_chr_tls_init(Chardev *chr)
tcp_chr_tls_handshake,
chr,
NULL,
- NULL);
+ chr->gcontext);
}
@@ -743,7 +786,8 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc)
qio_channel_set_delay(s->ioc, false);
}
if (s->listener) {
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
+ qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
+ NULL, chr->gcontext);
}
if (s->tls_creds) {
@@ -823,8 +867,11 @@ static void char_socket_finalize(Object *obj)
tcp_chr_free_connection(chr);
tcp_chr_reconn_timer_cancel(s);
qapi_free_SocketAddress(s->addr);
+ tcp_chr_telnet_destroy(s);
+ g_free(s->telnet_init);
if (s->listener) {
- qio_net_listener_set_client_func(s->listener, NULL, NULL, NULL);
+ qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
+ NULL, chr->gcontext);
object_unref(OBJECT(s->listener));
}
if (s->tls_creds) {
@@ -854,11 +901,22 @@ cleanup:
object_unref(OBJECT(sioc));
}
+static void tcp_chr_connect_async(Chardev *chr)
+{
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+ QIOChannelSocket *sioc;
+
+ sioc = qio_channel_socket_new();
+ tcp_chr_set_client_ioc_name(chr, sioc);
+ qio_channel_socket_connect_async(sioc, s->addr,
+ qemu_chr_socket_connected,
+ chr, NULL, chr->gcontext);
+}
+
static gboolean socket_reconnect_timeout(gpointer opaque)
{
Chardev *chr = CHARDEV(opaque);
SocketChardev *s = SOCKET_CHARDEV(opaque);
- QIOChannelSocket *sioc;
g_source_unref(s->reconnect_timer);
s->reconnect_timer = NULL;
@@ -867,11 +925,7 @@ static gboolean socket_reconnect_timeout(gpointer opaque)
return false;
}
- sioc = qio_channel_socket_new();
- tcp_chr_set_client_ioc_name(chr, sioc);
- qio_channel_socket_connect_async(sioc, s->addr,
- qemu_chr_socket_connected,
- chr, NULL, NULL);
+ tcp_chr_connect_async(chr);
return false;
}
@@ -950,13 +1004,8 @@ static void qmp_chardev_open_socket(Chardev *chr,
s->reconnect_time = reconnect;
}
- if (s->reconnect_time) {
- sioc = qio_channel_socket_new();
- tcp_chr_set_client_ioc_name(chr, sioc);
- qio_channel_socket_connect_async(sioc, s->addr,
- qemu_chr_socket_connected,
- chr, NULL, NULL);
- } else {
+ /* If reconnect_time is set, will do that in chr_machine_done. */
+ if (!s->reconnect_time) {
if (s->is_listen) {
char *name;
s->listener = qio_net_listener_new();
@@ -980,8 +1029,10 @@ static void qmp_chardev_open_socket(Chardev *chr,
return;
}
if (!s->ioc) {
- qio_net_listener_set_client_func(s->listener, tcp_chr_accept,
- chr, NULL);
+ qio_net_listener_set_client_func_full(s->listener,
+ tcp_chr_accept,
+ chr, NULL,
+ chr->gcontext);
}
} else if (qemu_chr_wait_connected(chr, errp) < 0) {
goto error;
@@ -1103,6 +1154,17 @@ char_socket_get_connected(Object *obj, Error **errp)
return s->connected;
}
+static int tcp_chr_machine_done_hook(Chardev *chr)
+{
+ SocketChardev *s = SOCKET_CHARDEV(chr);
+
+ if (s->reconnect_time) {
+ tcp_chr_connect_async(chr);
+ }
+
+ return 0;
+}
+
static void char_socket_class_init(ObjectClass *oc, void *data)
{
ChardevClass *cc = CHARDEV_CLASS(oc);
@@ -1118,6 +1180,7 @@ static void char_socket_class_init(ObjectClass *oc, void *data)
cc->chr_add_client = tcp_chr_add_client;
cc->chr_add_watch = tcp_chr_add_watch;
cc->chr_update_read_handler = tcp_chr_update_read_handler;
+ cc->chr_machine_done = tcp_chr_machine_done_hook;
object_class_property_add(oc, "addr", "SocketAddress",
char_socket_get_addr, NULL,