On 10/07/2016 08:54 AM, Stefan Hajnoczi wrote:
> The socket(2) and accept(2) syscalls have been extended to take flags
> that affect the socket atomically at creation time.  This not only
> avoids the overhead of additional system calls but also closes the race
> condition with forking threads.
> 
> This patch adds support for the SOCK_NONBLOCK flag.  QEMU already
> implements the SOCK_CLOEXEC flag.

Atomic where supported by the OS, racy fallback on older systems, but
not the end of the world (and our already-existing fallback is already
racy, where the SOCK_CLOEXEC race is more likely to affect a
multithreaded forking app, while SOCK_NONBLOCK is just there for
convenience).

> 
> All qemu_socket() and qemu_accept() callers are updated to pass the new
> flags argument.  Callers that later use qemu_set_nonblock() are
> refactored as follows:
> 
>   fd = qemu_socket(...) or qemu_accept(...);
>   ...
>   qemu_set_nonblock(fd);
> 
> Becomes:
> 
>   fd = qemu_socket(..., QEMU_SOCK_NONBLOCK) or
>        qemu_accept(..., QEMU_SOCK_NONBLOCK);
> 
> For full details on SOCK_NONBLOCK in the POSIX spec see:
> http://austingroupbugs.net/view.php?id=411

/me that looks strangely familiar... :)

> 
> Note that slirp code violates the coding style with a mix of tabs and
> space indentation.  This patch passes checkpatch.pl but the diff may
> appear odd due to the mixed indentation in slirp code.
> 
> Suggested-by: Eric Blake <ebl...@redhat.com>
> Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com>
> ---

> +++ b/include/qemu/sockets.h
> @@ -11,9 +11,14 @@ int inet_aton(const char *cp, struct in_addr *ia);
>  
>  #include "qapi-types.h"
>  
> +typedef enum {
> +    QEMU_SOCK_NONBLOCK = 1,
> +} QemuSockFlags;

Good, since we can't rely on SOCK_NONBLOCK being defined everywhere yet.

> --- a/slirp/misc.c
> +++ b/slirp/misc.c

> @@ -184,13 +185,13 @@ fork_exec(struct socket *so, const char *ex, int do_pty)
>                   * of connect() fail in the child process
>                   */
>                  do {
> -                    so->s = accept(s, (struct sockaddr *)&addr, &addrlen);
> +                    so->s = qemu_accept(s, (struct sockaddr *)&addr, 
> &addrlen,
> +                                        QEMU_SOCK_NONBLOCK);

Silent bug fix here and elsewhere that we now set CLOEXEC where we
previously didn't.  Probably worth mentioning in the commit message that
you fixed a couple of places that used accept() instead of the proper
qemu_accept().

> +++ b/util/osdep.c
> @@ -267,12 +267,21 @@ ssize_t qemu_write_full(int fd, const void *buf, size_t 
> count)
>  /*
>   * Opens a socket with FD_CLOEXEC set
>   */
> -int qemu_socket(int domain, int type, int protocol)
> +int qemu_socket(int domain, int type, int protocol, QemuSockFlags flags)
>  {
>      int ret;
>  
> -#ifdef SOCK_CLOEXEC
> -    ret = socket(domain, type | SOCK_CLOEXEC, protocol);
> +    /* Require both SOCK_CLOEXEC and SOCK_NONBLOCK to avoid additional cases
> +     * with only one defined.  Both were added to POSIX in Austin Group #411 
> so
> +     * chances are good they come in a pair.

Indeed.

> @@ -288,12 +300,17 @@ int qemu_socket(int domain, int type, int protocol)
>  /*
>   * Accept a connection and set FD_CLOEXEC
>   */
> -int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
> +int qemu_accept(int s, struct sockaddr *addr, socklen_t *addrlen,
> +                QemuSockFlags flags)
>  {
>      int ret;
>  
>  #ifdef CONFIG_ACCEPT4
> -    ret = accept4(s, addr, addrlen, SOCK_CLOEXEC);
> +    int accept_flags = SOCK_CLOEXEC;
> +    if (flags & QEMU_SOCK_NONBLOCK) {
> +        accept_flags |= SOCK_NONBLOCK;
> +    }

You're also (implicitly) assuming that CONFIG_ACCEPT4 implies both
SOCK_CLOEXEC and SOCK_NONBLOCK; again likely to be true.

> @@ -317,18 +318,20 @@ static int inet_connect_addr(struct addrinfo *addr, 
> bool *in_progress,
>                               ConnectState *connect_state, Error **errp)
>  {
>      int sock, rc;
> +    QemuSockFlags flags = 0;
>  
>      *in_progress = false;
>  
> -    sock = qemu_socket(addr->ai_family, addr->ai_socktype, 
> addr->ai_protocol);
> -    if (sock < 0) {
> -        error_setg_errno(errp, errno, "Failed to create socket");
> -        return -1;
> -    }
> -    socket_set_fast_reuse(sock);
>      if (connect_state != NULL) {
> -        qemu_set_nonblock(sock);
> +        flags = QEMU_SOCK_NONBLOCK;

Use |= here? ...


> @@ -732,16 +737,16 @@ static int unix_connect_saddr(UnixSocketAddress *saddr, 
> Error **errp,
>          return -1;
>      }
>  
> -    sock = qemu_socket(PF_UNIX, SOCK_STREAM, 0);
> -    if (sock < 0) {
> -        error_setg_errno(errp, errno, "Failed to create socket");
> -        return -1;
> -    }
>      if (callback != NULL) {
>          connect_state = g_malloc0(sizeof(*connect_state));
>          connect_state->callback = callback;
>          connect_state->opaque = opaque;
> -        qemu_set_nonblock(sock);
> +        flags |= QEMU_SOCK_NONBLOCK;
> +    }

...to match how you did it here?  (No current semantic difference, but
might lead to future maintenance problems if further additions aren't
careful.)

I found a minor nit and some suggested commit message improvements, but
the patch itself is sane enough that I'm okay if you add:
Reviewed-by: Eric Blake <ebl...@redhat.com>

-- 
Eric Blake   eblake redhat com    +1-919-301-3266
Libvirt virtualization library http://libvirt.org

Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to