Add support for FD taps on Qlisten chans in #I
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 31 Aug 2015 18:43:35 +0000 (14:43 -0400)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 28 Sep 2015 19:14:00 +0000 (15:14 -0400)
Applications can request an FD tap on a Qlisten file, e.g.
/net/tcp/0/listen.  The FDTAP_FILT_READABLE filter will fire when there
is a new connection.

Note that you must use open() with O_PATH to get a Qlisten FD.
Otherwise you'll block (or at least attempt) to get a new conversation.
After opening with O_PATH, you can either set up the Tap on your own, or
use epoll to find out about new conversations.

Right now, I have the Qlisten tap allow FDTAP_FILT_HANGUP requests,
mostly because epoll adds that in for every tap it uses.  Since there is
no distant end to the connection, hangup doesn't really make sense.
Maybe we'll find a use for it.

kern/include/ip.h
kern/src/net/devip.c

index c918015..dd79a6f 100644 (file)
@@ -82,6 +82,7 @@ struct conv {
        atomic_t snoopers;                      /* number of processes with snoop open */
 
        struct fdtap_slist data_taps;
+       struct fdtap_slist listen_taps;
        spinlock_t tap_lock;
 
        struct rendez cr;
index c1b7278..8d71a29 100644 (file)
@@ -1318,6 +1318,7 @@ int iptapfd(struct chan *chan, struct fd_tap *tap, int cmd)
 
        #define DEVIP_LEGAL_DATA_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_WRITABLE | \
                                       FDTAP_FILT_HANGUP)
+       #define DEVIP_LEGAL_LISTEN_TAPS (FDTAP_FILT_READABLE | FDTAP_FILT_HANGUP)
 
        /* That's a lot of pointers to get to the conv! */
        f = ipfs[chan->dev];
@@ -1357,6 +1358,30 @@ int iptapfd(struct chan *chan, struct fd_tap *tap, int cmd)
                        }
                        spin_unlock(&conv->tap_lock);
                        return ret;
+               case Qlisten:
+                       if (tap->filter & ~DEVIP_LEGAL_LISTEN_TAPS) {
+                               set_errno(ENOSYS);
+                               set_errstr("Unsupported #I listen tap, must be %p",
+                                          DEVIP_LEGAL_LISTEN_TAPS);
+                               return -1;
+                       }
+                       spin_lock(&conv->tap_lock);
+                       switch (cmd) {
+                               case (FDTAP_CMD_ADD):
+                                       SLIST_INSERT_HEAD(&conv->listen_taps, tap, link);
+                                       ret = 0;
+                                       break;
+                               case (FDTAP_CMD_REM):
+                                       SLIST_REMOVE(&conv->listen_taps, tap, fd_tap, link);
+                                       ret = 0;
+                                       break;
+                               default:
+                                       set_errno(ENOSYS);
+                                       set_errstr("Unsupported #I data tap command");
+                                       ret = -1;
+                       }
+                       spin_unlock(&conv->tap_lock);
+                       return ret;
                default:
                        set_errno(ENOSYS);
                        set_errstr("Can't tap #I file type %d", TYPE(chan->qid));
@@ -1446,6 +1471,7 @@ retry:
                        rendez_init(&c->cr);
                        rendez_init(&c->listenr);
                        SLIST_INIT(&c->data_taps);      /* already = 0; set to be futureproof */
+                       SLIST_INIT(&c->listen_taps);
                        spinlock_init(&c->tap_lock);
                        qlock(&c->qlock);
                        c->p = p;
@@ -1536,6 +1562,17 @@ struct Proto *Fsrcvpcolx(struct Fs *f, uint8_t proto)
        return f->t2p[proto];
 }
 
+static void fire_listener_taps(struct conv *conv)
+{
+       struct fd_tap *tap_i;
+       if (SLIST_EMPTY(&conv->listen_taps))
+               return;
+       spin_lock(&conv->tap_lock);
+       SLIST_FOREACH(tap_i, &conv->listen_taps, link)
+               fire_tap(tap_i, FDTAP_FILT_READABLE);
+       spin_unlock(&conv->tap_lock);
+}
+
 /*
  *  called with protocol locked
  */
@@ -1573,6 +1610,7 @@ struct conv *Fsnewcall(struct conv *c, uint8_t * raddr, uint16_t rport,
        qunlock(&c->qlock);
 
        rendez_wakeup(&c->listenr);
+       fire_listener_taps(c);
 
        return nc;
 }