Message ID | 7c4ddf95a57f7dfa546c11f8a08a9091b8bd1ceb.1501362828.git-series.knut.omang@oracle.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Sat, Jul 29, 2017 at 11:18:15PM +0200, Knut Omang wrote: > There's a potential race condition between multiple bind()'s > attempting to bind to the same port, which occasionally > allows more than one bind to succeed against the same port. > > When a subsequent listen() call is made with the same socket > only one will succeed. > > The current QEMU code does however not take this situation into account > and the listen will cause the code to break out and fail even > when there are actually available ports to use. > > This test exposes two subtests: > > /socket/listen-serial > /socket/listen-compete > > The "compete" subtest creates a number of threads and have them all trying to bind > to the same port with a large enough offset input to > allow all threads to get it's own port. > The "serial" subtest just does the same, except in series in a > single thread. > > The serial version passes, probably in most versions of QEMU. > > The parallel version exposes the problem in a relatively reliable way, > eg. it fails a majority of times, but not with a 100% rate, occasional > passes can be seen. Nevertheless this is quite good given that > the bug was tricky to reproduce and has been left undetected for > a while. > > The problem seems to be present in all versions of QEMU. > > The original failure scenario occurred with VNC port allocation > in a traditional Xen based build, in different code > but with similar functionality. > > Reported-by: Bhavesh Davda <bhavesh.davda@oracle.com> > Signed-off-by: Knut Omang <knut.omang@oracle.com> > Reviewed-by: Yuval Shaia <yuval.shaia@oracle.com> > Reviewed-by: Bhavesh Davda <bhavesh.davda@oracle.com> > Reviewed-by: Girish Moodalbail <girish.moodalbail@oracle.com> > --- > tests/Makefile.include | 2 +- > tests/test-listen.c | 253 ++++++++++++++++++++++++++++++++++++++++++- > 2 files changed, 255 insertions(+) > create mode 100644 tests/test-listen.c > > diff --git a/tests/Makefile.include b/tests/Makefile.include > index 7af278d..b37c0c8 100644 > --- a/tests/Makefile.include > +++ b/tests/Makefile.include > @@ -128,6 +128,7 @@ check-unit-y += tests/test-bufferiszero$(EXESUF) > gcov-files-check-bufferiszero-y = util/bufferiszero.c > check-unit-y += tests/test-uuid$(EXESUF) > check-unit-y += tests/ptimer-test$(EXESUF) > +#check-unit-y += tests/test-listen$(EXESUF) > gcov-files-ptimer-test-y = hw/core/ptimer.c > check-unit-y += tests/test-qapi-util$(EXESUF) > gcov-files-test-qapi-util-y = qapi/qapi-util.c > @@ -769,6 +770,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o > tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) > tests/numa-test$(EXESUF): tests/numa-test.o > tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o > +tests/test-listen$(EXESUF): tests/test-listen.o $(test-util-obj-y) > > tests/migration/stress$(EXESUF): tests/migration/stress.o > $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") > diff --git a/tests/test-listen.c b/tests/test-listen.c > new file mode 100644 > index 0000000..5c07537 > --- /dev/null > +++ b/tests/test-listen.c > @@ -0,0 +1,253 @@ > +/* > + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. > + * Author: Knut Omang <knut.omang@oracle.com> > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 > + * as published by the Free Software Foundation. Can you change that to "version 2 or later" - per the LICENSE file, we don't accept contributions under "version 2 only" except for 4 specific subdirs: "As of July 2013, contributions under version 2 of the GNU General Public License (and no later version) are only accepted for the following files or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*." > + * > + * Test parallel port listen configuration with > + * dynamic port allocation > + */ Regards, Daniel
On Mon, 2017-08-07 at 09:45 +0100, Daniel P. Berrange wrote: > On Sat, Jul 29, 2017 at 11:18:15PM +0200, Knut Omang wrote: > > There's a potential race condition between multiple bind()'s > > attempting to bind to the same port, which occasionally > > allows more than one bind to succeed against the same port. > > > > When a subsequent listen() call is made with the same socket > > only one will succeed. > > > > The current QEMU code does however not take this situation into account > > and the listen will cause the code to break out and fail even > > when there are actually available ports to use. > > > > This test exposes two subtests: > > > > /socket/listen-serial > > /socket/listen-compete > > > > The "compete" subtest creates a number of threads and have them all trying to bind > > to the same port with a large enough offset input to > > allow all threads to get it's own port. > > The "serial" subtest just does the same, except in series in a > > single thread. > > > > The serial version passes, probably in most versions of QEMU. > > > > The parallel version exposes the problem in a relatively reliable way, > > eg. it fails a majority of times, but not with a 100% rate, occasional > > passes can be seen. Nevertheless this is quite good given that > > the bug was tricky to reproduce and has been left undetected for > > a while. > > > > The problem seems to be present in all versions of QEMU. > > > > The original failure scenario occurred with VNC port allocation > > in a traditional Xen based build, in different code > > but with similar functionality. > > > > Reported-by: Bhavesh Davda <bhavesh.davda@oracle.com> > > Signed-off-by: Knut Omang <knut.omang@oracle.com> > > Reviewed-by: Yuval Shaia <yuval.shaia@oracle.com> > > Reviewed-by: Bhavesh Davda <bhavesh.davda@oracle.com> > > Reviewed-by: Girish Moodalbail <girish.moodalbail@oracle.com> > > --- > > tests/Makefile.include | 2 +- > > tests/test-listen.c | 253 ++++++++++++++++++++++++++++++++++++++++++- > > 2 files changed, 255 insertions(+) > > create mode 100644 tests/test-listen.c > > > > diff --git a/tests/Makefile.include b/tests/Makefile.include > > index 7af278d..b37c0c8 100644 > > --- a/tests/Makefile.include > > +++ b/tests/Makefile.include > > @@ -128,6 +128,7 @@ check-unit-y += tests/test-bufferiszero$(EXESUF) > > gcov-files-check-bufferiszero-y = util/bufferiszero.c > > check-unit-y += tests/test-uuid$(EXESUF) > > check-unit-y += tests/ptimer-test$(EXESUF) > > +#check-unit-y += tests/test-listen$(EXESUF) > > gcov-files-ptimer-test-y = hw/core/ptimer.c > > check-unit-y += tests/test-qapi-util$(EXESUF) > > gcov-files-test-qapi-util-y = qapi/qapi-util.c > > @@ -769,6 +770,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o > > tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) > > tests/numa-test$(EXESUF): tests/numa-test.o > > tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o > > +tests/test-listen$(EXESUF): tests/test-listen.o $(test-util-obj-y) > > > > tests/migration/stress$(EXESUF): tests/migration/stress.o > > $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") > > diff --git a/tests/test-listen.c b/tests/test-listen.c > > new file mode 100644 > > index 0000000..5c07537 > > --- /dev/null > > +++ b/tests/test-listen.c > > @@ -0,0 +1,253 @@ > > +/* > > + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. > > + * Author: Knut Omang <knut.omang@oracle.com> > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License version 2 > > + * as published by the Free Software Foundation. > > Can you change that to "version 2 or later" - per the LICENSE file, we don't > accept contributions under "version 2 only" except for 4 specific subdirs: > > > "As of July 2013, contributions under version 2 of the GNU General Public > License (and no later version) are only accepted for the following files > or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*." Oh, sorry - I wasn't aware of this, +"...or later" is fine with me. Would you like me to send a v7 of the set with only that change, or can you amend it as part of the merge? Thanks, Knut > > > + * > > + * Test parallel port listen configuration with > > + * dynamic port allocation > > + */ > > > Regards, > Daniel
On Mon, Aug 07, 2017 at 11:21:25AM +0200, Knut Omang wrote: > On Mon, 2017-08-07 at 09:45 +0100, Daniel P. Berrange wrote: > > On Sat, Jul 29, 2017 at 11:18:15PM +0200, Knut Omang wrote: > > > There's a potential race condition between multiple bind()'s > > > attempting to bind to the same port, which occasionally > > > allows more than one bind to succeed against the same port. > > > > > > When a subsequent listen() call is made with the same socket > > > only one will succeed. > > > > > > The current QEMU code does however not take this situation into account > > > and the listen will cause the code to break out and fail even > > > when there are actually available ports to use. > > > > > > This test exposes two subtests: > > > > > > /socket/listen-serial > > > /socket/listen-compete > > > > > > The "compete" subtest creates a number of threads and have them all trying to bind > > > to the same port with a large enough offset input to > > > allow all threads to get it's own port. > > > The "serial" subtest just does the same, except in series in a > > > single thread. > > > > > > The serial version passes, probably in most versions of QEMU. > > > > > > The parallel version exposes the problem in a relatively reliable way, > > > eg. it fails a majority of times, but not with a 100% rate, occasional > > > passes can be seen. Nevertheless this is quite good given that > > > the bug was tricky to reproduce and has been left undetected for > > > a while. > > > > > > The problem seems to be present in all versions of QEMU. > > > > > > The original failure scenario occurred with VNC port allocation > > > in a traditional Xen based build, in different code > > > but with similar functionality. > > > > > > Reported-by: Bhavesh Davda <bhavesh.davda@oracle.com> > > > Signed-off-by: Knut Omang <knut.omang@oracle.com> > > > Reviewed-by: Yuval Shaia <yuval.shaia@oracle.com> > > > Reviewed-by: Bhavesh Davda <bhavesh.davda@oracle.com> > > > Reviewed-by: Girish Moodalbail <girish.moodalbail@oracle.com> > > > --- > > > tests/Makefile.include | 2 +- > > > tests/test-listen.c | 253 ++++++++++++++++++++++++++++++++++++++++++- > > > 2 files changed, 255 insertions(+) > > > create mode 100644 tests/test-listen.c > > > > > > diff --git a/tests/Makefile.include b/tests/Makefile.include > > > index 7af278d..b37c0c8 100644 > > > --- a/tests/Makefile.include > > > +++ b/tests/Makefile.include > > > @@ -128,6 +128,7 @@ check-unit-y += tests/test-bufferiszero$(EXESUF) > > > gcov-files-check-bufferiszero-y = util/bufferiszero.c > > > check-unit-y += tests/test-uuid$(EXESUF) > > > check-unit-y += tests/ptimer-test$(EXESUF) > > > +#check-unit-y += tests/test-listen$(EXESUF) > > > gcov-files-ptimer-test-y = hw/core/ptimer.c > > > check-unit-y += tests/test-qapi-util$(EXESUF) > > > gcov-files-test-qapi-util-y = qapi/qapi-util.c > > > @@ -769,6 +770,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o > > > tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) > > > tests/numa-test$(EXESUF): tests/numa-test.o > > > tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o > > > +tests/test-listen$(EXESUF): tests/test-listen.o $(test-util-obj-y) > > > > > > tests/migration/stress$(EXESUF): tests/migration/stress.o > > > $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") > > > diff --git a/tests/test-listen.c b/tests/test-listen.c > > > new file mode 100644 > > > index 0000000..5c07537 > > > --- /dev/null > > > +++ b/tests/test-listen.c > > > @@ -0,0 +1,253 @@ > > > +/* > > > + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. > > > + * Author: Knut Omang <knut.omang@oracle.com> > > > + * > > > + * This program is free software; you can redistribute it and/or modify > > > + * it under the terms of the GNU General Public License version 2 > > > + * as published by the Free Software Foundation. > > > > Can you change that to "version 2 or later" - per the LICENSE file, we don't > > accept contributions under "version 2 only" except for 4 specific subdirs: > > > > > > "As of July 2013, contributions under version 2 of the GNU General Public > > License (and no later version) are only accepted for the following files > > or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*." > > Oh, sorry - I wasn't aware of this, +"...or later" is fine with me. > Would you like me to send a v7 of the set with only that change, or can you amend > it as part of the merge? Since its a copyright statement, I'd prefer if you just sent a v7. I've no other comments, so will queue it for merge once you resend, and send a pull request once 2.11 opens up. Regards, Daniel
On Mon, 2017-08-07 at 11:27 +0100, Daniel P. Berrange wrote: > On Mon, Aug 07, 2017 at 11:21:25AM +0200, Knut Omang wrote: > > On Mon, 2017-08-07 at 09:45 +0100, Daniel P. Berrange wrote: > > > On Sat, Jul 29, 2017 at 11:18:15PM +0200, Knut Omang wrote: > > > > There's a potential race condition between multiple bind()'s > > > > attempting to bind to the same port, which occasionally > > > > allows more than one bind to succeed against the same port. > > > > > > > > When a subsequent listen() call is made with the same socket > > > > only one will succeed. > > > > > > > > The current QEMU code does however not take this situation into account > > > > and the listen will cause the code to break out and fail even > > > > when there are actually available ports to use. > > > > > > > > This test exposes two subtests: > > > > > > > > /socket/listen-serial > > > > /socket/listen-compete > > > > > > > > The "compete" subtest creates a number of threads and have them all trying to bind > > > > to the same port with a large enough offset input to > > > > allow all threads to get it's own port. > > > > The "serial" subtest just does the same, except in series in a > > > > single thread. > > > > > > > > The serial version passes, probably in most versions of QEMU. > > > > > > > > The parallel version exposes the problem in a relatively reliable way, > > > > eg. it fails a majority of times, but not with a 100% rate, occasional > > > > passes can be seen. Nevertheless this is quite good given that > > > > the bug was tricky to reproduce and has been left undetected for > > > > a while. > > > > > > > > The problem seems to be present in all versions of QEMU. > > > > > > > > The original failure scenario occurred with VNC port allocation > > > > in a traditional Xen based build, in different code > > > > but with similar functionality. > > > > > > > > Reported-by: Bhavesh Davda <bhavesh.davda@oracle.com> > > > > Signed-off-by: Knut Omang <knut.omang@oracle.com> > > > > Reviewed-by: Yuval Shaia <yuval.shaia@oracle.com> > > > > Reviewed-by: Bhavesh Davda <bhavesh.davda@oracle.com> > > > > Reviewed-by: Girish Moodalbail <girish.moodalbail@oracle.com> > > > > --- > > > > tests/Makefile.include | 2 +- > > > > tests/test-listen.c | 253 ++++++++++++++++++++++++++++++++++++++++++- > > > > 2 files changed, 255 insertions(+) > > > > create mode 100644 tests/test-listen.c > > > > > > > > diff --git a/tests/Makefile.include b/tests/Makefile.include > > > > index 7af278d..b37c0c8 100644 > > > > --- a/tests/Makefile.include > > > > +++ b/tests/Makefile.include > > > > @@ -128,6 +128,7 @@ check-unit-y += tests/test-bufferiszero$(EXESUF) > > > > gcov-files-check-bufferiszero-y = util/bufferiszero.c > > > > check-unit-y += tests/test-uuid$(EXESUF) > > > > check-unit-y += tests/ptimer-test$(EXESUF) > > > > +#check-unit-y += tests/test-listen$(EXESUF) > > > > gcov-files-ptimer-test-y = hw/core/ptimer.c > > > > check-unit-y += tests/test-qapi-util$(EXESUF) > > > > gcov-files-test-qapi-util-y = qapi/qapi-util.c > > > > @@ -769,6 +770,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o > > > > tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) > > > > tests/numa-test$(EXESUF): tests/numa-test.o > > > > tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o > > > > +tests/test-listen$(EXESUF): tests/test-listen.o $(test-util-obj-y) > > > > > > > > tests/migration/stress$(EXESUF): tests/migration/stress.o > > > > $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") > > > > diff --git a/tests/test-listen.c b/tests/test-listen.c > > > > new file mode 100644 > > > > index 0000000..5c07537 > > > > --- /dev/null > > > > +++ b/tests/test-listen.c > > > > @@ -0,0 +1,253 @@ > > > > +/* > > > > + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. > > > > + * Author: Knut Omang <knut.omang@oracle.com> > > > > + * > > > > + * This program is free software; you can redistribute it and/or modify > > > > + * it under the terms of the GNU General Public License version 2 > > > > + * as published by the Free Software Foundation. > > > > > > Can you change that to "version 2 or later" - per the LICENSE file, we don't > > > accept contributions under "version 2 only" except for 4 specific subdirs: > > > > > > > > > "As of July 2013, contributions under version 2 of the GNU General Public > > > License (and no later version) are only accepted for the following files > > > or directories: bsd-user/, linux-user/, hw/vfio/, hw/xen/xen_pt*." > > > > Oh, sorry - I wasn't aware of this, +"...or later" is fine with me. > > Would you like me to send a v7 of the set with only that change, or can you amend > > it as part of the merge? > > Since its a copyright statement, I'd prefer if you just sent a v7. I've no > other comments, so will queue it for merge once you resend, and send a pull > request once 2.11 opens up. Ok, will do, Thanks, Knut > Regards, > Daniel
diff --git a/tests/Makefile.include b/tests/Makefile.include index 7af278d..b37c0c8 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -128,6 +128,7 @@ check-unit-y += tests/test-bufferiszero$(EXESUF) gcov-files-check-bufferiszero-y = util/bufferiszero.c check-unit-y += tests/test-uuid$(EXESUF) check-unit-y += tests/ptimer-test$(EXESUF) +#check-unit-y += tests/test-listen$(EXESUF) gcov-files-ptimer-test-y = hw/core/ptimer.c check-unit-y += tests/test-qapi-util$(EXESUF) gcov-files-test-qapi-util-y = qapi/qapi-util.c @@ -769,6 +770,7 @@ tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) tests/numa-test$(EXESUF): tests/numa-test.o tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o +tests/test-listen$(EXESUF): tests/test-listen.o $(test-util-obj-y) tests/migration/stress$(EXESUF): tests/migration/stress.o $(call quiet-command, $(LINKPROG) -static -O3 $(PTHREAD_LIB) -o $@ $< ,"LINK","$(TARGET_DIR)$@") diff --git a/tests/test-listen.c b/tests/test-listen.c new file mode 100644 index 0000000..5c07537 --- /dev/null +++ b/tests/test-listen.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * Author: Knut Omang <knut.omang@oracle.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 + * as published by the Free Software Foundation. + * + * Test parallel port listen configuration with + * dynamic port allocation + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu-common.h" +#include "qemu/thread.h" +#include "qemu/sockets.h" +#include "qapi/error.h" + +#define NAME_LEN 1024 +#define PORT_LEN 16 + +struct thr_info { + QemuThread thread; + int to_port; + bool ipv4; + bool ipv6; + int got_port; + int eno; + int fd; + const char *errstr; + char hostname[NAME_LEN + 1]; + char port[PORT_LEN + 1]; +}; + + +/* These two functions taken from test-io-channel-socket.c */ +static int check_bind(const char *hostname, bool *has_proto) +{ + int fd = -1; + struct addrinfo ai, *res = NULL; + int rc; + int ret = -1; + + memset(&ai, 0, sizeof(ai)); + ai.ai_flags = AI_CANONNAME | AI_ADDRCONFIG; + ai.ai_family = AF_UNSPEC; + ai.ai_socktype = SOCK_STREAM; + + /* lookup */ + rc = getaddrinfo(hostname, NULL, &ai, &res); + if (rc != 0) { + if (rc == EAI_ADDRFAMILY || + rc == EAI_FAMILY) { + *has_proto = false; + goto done; + } + goto cleanup; + } + + fd = qemu_socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd < 0) { + goto cleanup; + } + + if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) { + if (errno == EADDRNOTAVAIL) { + *has_proto = false; + goto done; + } + goto cleanup; + } + + *has_proto = true; + done: + ret = 0; + + cleanup: + if (fd != -1) { + close(fd); + } + if (res) { + freeaddrinfo(res); + } + return ret; +} + +static int check_protocol_support(bool *has_ipv4, bool *has_ipv6) +{ + if (check_bind("127.0.0.1", has_ipv4) < 0) { + return -1; + } + if (check_bind("::1", has_ipv6) < 0) { + return -1; + } + + return 0; +} + +static void *listener_thread(void *arg) +{ + struct thr_info *thr = (struct thr_info *)arg; + SocketAddress addr = { + .type = SOCKET_ADDRESS_TYPE_INET, + .u = { + .inet = { + .host = thr->hostname, + .port = thr->port, + .has_ipv4 = thr->ipv4, + .ipv4 = thr->ipv4, + .has_ipv6 = thr->ipv6, + .ipv6 = thr->ipv6, + .has_to = true, + .to = thr->to_port, + }, + }, + }; + Error *err = NULL; + int fd; + + fd = socket_listen(&addr, &err); + if (fd < 0) { + thr->eno = errno; + thr->errstr = error_get_pretty(err); + } else { + struct sockaddr_in a; + socklen_t a_len = sizeof(a); + g_assert_cmpint(getsockname(fd, (struct sockaddr *)&a, &a_len), ==, 0); + thr->got_port = ntohs(a.sin_port); + thr->fd = fd; + } + return arg; +} + + +static void listen_compete_nthr(bool threaded, int nthreads, + int start_port, int max_offset, + bool ipv4, bool ipv6) +{ + int i; + int failed_listens = 0; + struct thr_info *thr = g_new0(struct thr_info, nthreads); + int used[max_offset + 1]; + + memset(used, 0, sizeof(used)); + for (i = 0; i < nthreads; i++) { + snprintf(thr[i].port, PORT_LEN, "%d", start_port); + strcpy(thr[i].hostname, "localhost"); + thr[i].to_port = start_port + max_offset; + thr[i].ipv4 = ipv4; + thr[i].ipv6 = ipv6; + } + + for (i = 0; i < nthreads; i++) { + if (threaded) { + qemu_thread_create(&thr[i].thread, "listener", + listener_thread, &thr[i], + QEMU_THREAD_JOINABLE); + } else { + listener_thread(&thr[i]); + } + } + + if (threaded) { + for (i = 0; i < nthreads; i++) { + qemu_thread_join(&thr[i].thread); + } + } + for (i = 0; i < nthreads; i++) { + if (thr[i].got_port) { + closesocket(thr[i].fd); + } + } + + for (i = 0; i < nthreads; i++) { + if (thr[i].eno != 0) { + const char *m; + g_printerr("** Failed to assign a port to thread %d (errno = %d)\n", + i, thr[i].eno); + /* This is what we are interested in capturing - + * catch and report details if something unexpected happens: + */ + m = strstr(thr[i].errstr, "Failed to listen on socket"); + if (m != NULL) { + g_assert_cmpstr(thr[i].errstr, ==, + "Failed to listen on socket: Address already in use"); + } + failed_listens++; + } else { + int assigned_port = thr[i].got_port; + g_assert_cmpint(assigned_port, <= , thr[i].to_port); + g_assert_cmpint(used[assigned_port - start_port], == , 0); + } + } + g_assert_cmpint(failed_listens, ==, 0); + g_free(thr); +} + + +static void listen_compete_ipv4(void) +{ + listen_compete_nthr(true, 200, 5920, 300, true, false); +} + +static void listen_serial_ipv4(void) +{ + listen_compete_nthr(false, 200, 6300, 300, true, false); +} + +static void listen_compete_ipv6(void) +{ + listen_compete_nthr(true, 200, 5920, 300, true, false); +} + +static void listen_serial_ipv6(void) +{ + listen_compete_nthr(false, 200, 6300, 300, false, true); +} + +static void listen_compete_gen(void) +{ + listen_compete_nthr(true, 200, 5920, 300, true, true); +} + +static void listen_serial_gen(void) +{ + listen_compete_nthr(false, 200, 6300, 300, true, true); +} + + +int main(int argc, char **argv) +{ + bool has_ipv4, has_ipv6; + g_test_init(&argc, &argv, NULL); + + if (check_protocol_support(&has_ipv4, &has_ipv6) < 0) { + return 1; + } + + if (has_ipv4) { + g_test_add_func("/socket/listen-serial/ipv4", listen_serial_ipv4); + g_test_add_func("/socket/listen-compete/ipv4", listen_compete_ipv4); + } + if (has_ipv6) { + g_test_add_func("/socket/listen-serial/ipv6", listen_serial_ipv6); + g_test_add_func("/socket/listen-compete/ipv6", listen_compete_ipv6); + } + if (has_ipv4 && has_ipv6) { + g_test_add_func("/socket/listen-serial/generic", listen_serial_gen); + g_test_add_func("/socket/listen-compete/generic", listen_compete_gen); + } + return g_test_run(); +}