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:
parent
3da9de5ce2
commit
ce1230c054
@ -40,6 +40,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 */
|
||||
@ -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)
|
||||
{
|
||||
@ -556,6 +564,15 @@ 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);
|
||||
@ -570,6 +587,10 @@ static void tcp_chr_update_read_handler(Chardev *chr)
|
||||
chr, NULL, chr->gcontext);
|
||||
}
|
||||
|
||||
if (s->telnet_source) {
|
||||
tcp_chr_telnet_init(CHARDEV(s));
|
||||
}
|
||||
|
||||
if (!s->connected) {
|
||||
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,
|
||||
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;
|
||||
}
|
||||
|
||||
@ -617,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; \
|
||||
@ -634,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,
|
||||
@ -657,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);
|
||||
}
|
||||
|
||||
|
||||
@ -835,6 +868,8 @@ 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_full(s->listener, NULL, NULL,
|
||||
NULL, chr->gcontext);
|
||||
|
Loading…
Reference in New Issue
Block a user