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
|
#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);
|
||||||
|
Loading…
Reference in New Issue
Block a user