chardev: allow telnet gsource to switch gcontext

It was originally created by qio_channel_add_watch() so it's always
assigning the task to main context.  Now we use the new API called
qio_channel_add_watch_source() so that we get the GSource handle rather
than the tag ID.

Meanwhile, caching the gsource and TCPChardevTelnetInit (which holds the
handshake data) in SocketChardev.telnet_source so that we can also do
dynamic context switch when update read handlers.

Signed-off-by: Peter Xu <peterx@redhat.com>
Message-Id: <20180306053320.15401-5-peterx@redhat.com>
Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
This commit is contained in:
Peter Xu 2018-03-06 13:33:15 +08:00 committed by Paolo Bonzini
parent 3da9de5ce2
commit ce1230c054

View File

@ -40,6 +40,11 @@
#define TCP_MAX_FDS 16 #define TCP_MAX_FDS 16
typedef struct {
char buf[21];
size_t buflen;
} TCPChardevTelnetInit;
typedef struct { typedef struct {
Chardev parent; Chardev parent;
QIOChannel *ioc; /* Client I/O channel */ QIOChannel *ioc; /* Client I/O channel */
@ -60,6 +65,8 @@ typedef struct {
bool is_listen; bool is_listen;
bool is_telnet; bool is_telnet;
bool is_tn3270; bool is_tn3270;
GSource *telnet_source;
TCPChardevTelnetInit *telnet_init;
GSource *reconnect_timer; GSource *reconnect_timer;
int64_t reconnect_time; int64_t reconnect_time;
@ -70,6 +77,7 @@ typedef struct {
OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET) OBJECT_CHECK(SocketChardev, (obj), TYPE_CHARDEV_SOCKET)
static gboolean socket_reconnect_timeout(gpointer opaque); static gboolean socket_reconnect_timeout(gpointer opaque);
static void tcp_chr_telnet_init(Chardev *chr);
static void tcp_chr_reconn_timer_cancel(SocketChardev *s) static void tcp_chr_reconn_timer_cancel(SocketChardev *s)
{ {
@ -556,6 +564,15 @@ static void tcp_chr_connect(void *opaque)
qemu_chr_be_event(chr, CHR_EVENT_OPENED); 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) static void tcp_chr_update_read_handler(Chardev *chr)
{ {
SocketChardev *s = SOCKET_CHARDEV(chr); SocketChardev *s = SOCKET_CHARDEV(chr);
@ -570,6 +587,10 @@ static void tcp_chr_update_read_handler(Chardev *chr)
chr, NULL, chr->gcontext); chr, NULL, chr->gcontext);
} }
if (s->telnet_source) {
tcp_chr_telnet_init(CHARDEV(s));
}
if (!s->connected) { if (!s->connected) {
return; return;
} }
@ -583,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, static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
GIOCondition cond G_GNUC_UNUSED, GIOCondition cond G_GNUC_UNUSED,
gpointer user_data) gpointer user_data)
{ {
TCPChardevTelnetInit *init = user_data; SocketChardev *s = user_data;
Chardev *chr = CHARDEV(s);
TCPChardevTelnetInit *init = s->telnet_init;
ssize_t ret; ssize_t ret;
assert(init);
ret = qio_channel_write(ioc, init->buf, init->buflen, NULL); ret = qio_channel_write(ioc, init->buf, init->buflen, NULL);
if (ret < 0) { if (ret < 0) {
if (ret == QIO_CHANNEL_ERR_BLOCK) { if (ret == QIO_CHANNEL_ERR_BLOCK) {
ret = 0; ret = 0;
} else { } else {
tcp_chr_disconnect(init->chr); tcp_chr_disconnect(chr);
goto end; goto end;
} }
} }
init->buflen -= ret; init->buflen -= ret;
if (init->buflen == 0) { if (init->buflen == 0) {
tcp_chr_connect(init->chr); tcp_chr_connect(chr);
goto end; goto end;
} }
@ -617,16 +636,30 @@ static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc,
return G_SOURCE_CONTINUE; return G_SOURCE_CONTINUE;
end: 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; return G_SOURCE_REMOVE;
} }
static void tcp_chr_telnet_init(Chardev *chr) static void tcp_chr_telnet_init(Chardev *chr)
{ {
SocketChardev *s = SOCKET_CHARDEV(chr); SocketChardev *s = SOCKET_CHARDEV(chr);
TCPChardevTelnetInit *init = g_new0(TCPChardevTelnetInit, 1); TCPChardevTelnetInit *init;
size_t n = 0; 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) \ #define IACSET(x, a, b, c) \
do { \ do { \
x[n++] = a; \ x[n++] = a; \
@ -634,7 +667,6 @@ static void tcp_chr_telnet_init(Chardev *chr)
x[n++] = c; \ x[n++] = c; \
} while (0) } while (0)
init->chr = chr;
if (!s->is_tn3270) { if (!s->is_tn3270) {
init->buflen = 12; init->buflen = 12;
/* Prep the telnet negotion to put telnet in binary, /* Prep the telnet negotion to put telnet in binary,
@ -657,10 +689,11 @@ static void tcp_chr_telnet_init(Chardev *chr)
#undef IACSET #undef IACSET
qio_channel_add_watch( cont:
s->ioc, G_IO_OUT, s->telnet_source = qio_channel_add_watch_source(s->ioc, G_IO_OUT,
tcp_chr_telnet_init_io, tcp_chr_telnet_init_io,
init, NULL); s, NULL,
chr->gcontext);
} }
@ -835,6 +868,8 @@ static void char_socket_finalize(Object *obj)
tcp_chr_free_connection(chr); tcp_chr_free_connection(chr);
tcp_chr_reconn_timer_cancel(s); tcp_chr_reconn_timer_cancel(s);
qapi_free_SocketAddress(s->addr); qapi_free_SocketAddress(s->addr);
tcp_chr_telnet_destroy(s);
g_free(s->telnet_init);
if (s->listener) { if (s->listener) {
qio_net_listener_set_client_func_full(s->listener, NULL, NULL, qio_net_listener_set_client_func_full(s->listener, NULL, NULL,
NULL, chr->gcontext); NULL, chr->gcontext);