Subject: Re: possible bug in NetBSD asynchronous I/O
To: Christos Zoulas <christos@zoulas.com>
From: Emmanuel Dreyfus <p99dreyf@criens.u-psud.fr>
List: tech-kern
Date: 04/29/2001 23:41:17
> Well, putting a cpu_Debugger() in uipc_socket.c in sowakeup()
> reveals that the call is generated from uipc_usrreq(), and it
> is an sorwakeup.. The call stack is from the read... I think
> this is wrong.

Are you sure? I've bloated my kernel with debug printf(), and it seems
to me that the SIGIO comes through a sowwakeup, from
sys/kern/uipc_usrreq.c:usrreq()

   case PRU_RCVD:
      switch (so->so_type) {

      case SOCK_DGRAM:
         panic("uipc 1");
         /*NOTREACHED*/

      case SOCK_STREAM:
#define  rcv (&so->so_rcv)
#define snd (&so2->so_snd)
         if (unp->unp_conn == 0)
            break;
         so2 = unp->unp_conn->unp_socket;
         /*
          * Adjust backpressure on sender
          * and wakeup any waiting to write.
          */
         snd->sb_mbmax += unp->unp_mbcnt - rcv->sb_mbcnt;
         unp->unp_mbcnt = rcv->sb_mbcnt;
         snd->sb_hiwat += unp->unp_cc - rcv->sb_cc;
         unp->unp_cc = rcv->sb_cc;
         sowwakeup(so2);

The sowwakeup brings the SIGIO regardless if the owner of the write end
of the pipe was really unable to write to the pipe or not. This is where
it is wrong.

I've spent most of my day looking at OpenBSD sources, and I can't find
where they are doing things differently than we do in NetBSD. And they
don't have the problem we have. This is getting very frustrating.

Here is a simplier test program that exhibits the problem.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <fcntl.h>

void io_sighandler (int sig) {
  printf ("pid=%d got sigio\n", getpid ());
  printf ("TEST FAILED\n");
  exit (-1);
}

int main(int argc, char** argv) {
    struct sigaction aio;
    int fdsync[2];
    int err;
    char c;
    sigset_t set;

    sigemptyset(&set);
    sigaddset(&set,SIGIO);

    aio.sa_flags = SA_RESTART;
    aio.sa_handler = io_sighandler;
    sigemptyset(&aio.sa_mask);
    if (sigaction(SIGIO, &aio, 0) == -1) {
        printf("Error: Bad return value from sigaction call\n");
        exit(1);
    }

    if (pipe(fdsync) < 0) { /* fd for synchronization */
      printf("Error: bad pipe call\n");
      exit(1);
    }

    /* now set the pipe write end to be non-blocking async */
    fcntl(fdsync[1],F_SETFL, O_NONBLOCK | FASYNC);
    fcntl(fdsync[1],F_SETOWN, getpid());

    err = write(fdsync[1], "AAAA", 4);
    if (err < 0) {
       printf("write() got err=%d\n", err);
    }
    printf ("written %d bytes\n", err);

    sleep (1);

    printf ("reading from the pipe\n");
    do 
       err = read(fdsync[0], &c, 1);
    while (!err);
    printf ("readen %d bytes\n", err);

    printf("TEST SUCCESSFUL\n");
    exit(0);
}


-- 
Emmanuel Dreyfus
p99dreyf@criens.u-psud.fr