9ns: Extend struct dir and the stat M bufs
[akaros.git] / kern / src / net / devip.c
index 0bc5093..6053e8a 100644 (file)
@@ -38,7 +38,7 @@
 #include <cpio.h>
 #include <pmap.h>
 #include <smp.h>
-#include <ip.h>
+#include <net/ip.h>
 
 struct dev ipdevtab;
 
@@ -103,6 +103,7 @@ static long ndbwrite(struct Fs *, char *unused_char_p_t, uint32_t, int);
 static void closeconv(struct conv *);
 static void setup_proto_qio_bypass(struct conv *cv);
 static void undo_proto_qio_bypass(struct conv *cv);
+static int connected(void *a);
 
 static struct conv *chan2conv(struct chan *chan)
 {
@@ -126,6 +127,36 @@ static int topdirgen(struct chan *c, struct dir *dp)
        return founddevdir(c, q, get_cur_genbuf(), 0, network, 0555, dp);
 }
 
+/* Computes the perm field for a stat for Qdata.  Since select() polls the
+ * 'actionability' of a socket via the qdata FD, we'll also report listenable
+ * and connected conversations.  It's a minor hack.  =( */
+static int qdata_stat_perm(struct conv *cv)
+{
+       int perm;
+
+       perm = cv->perm;
+       /* If there is ever a listener, then it's readable.  Ideally, we'd only
+        * report this on the Qlisten file (which we also do).  The socket crap
+        * should never use a listening socket for data, so there shouldn't be any
+        * confusion when a Qdata shows up as readable. */
+       perm |= cv->incall ? DMREADABLE : 0;
+       /* For connectable convs, they need to be both connected and qio
+        * readable/writable.  The way to think about this is that the convs are not
+        * truly writable/readable until they are connected.  Conveniently, this
+        * means that when select polls Qdata for non-blocking connect(), a
+        * connected conversation pops up as writable (the qio is writable too).
+        *
+        * Note that a conversation can be 'Connected' even if it failed to connect.
+        * At least that's what the 9ns TCP code does.  It's more like "the protocol
+        * did what it needed and the connectctlmsg call (or its non-blocking
+        * equivalent) is done".  For instance, TCP has a few reasons to call
+        * Fsconnected, such as when we send the SYN and get a RST. */
+       if (!cv->p->connect || connected(cv)) {
+               perm |= qreadable(cv->rq) ? DMREADABLE : 0;
+               perm |= qwritable(cv->wq) ? DMWRITABLE : 0;
+       }
+       return perm;
+}
 
 static int ip3gen(struct chan *c, int i, struct dir *dp)
 {
@@ -146,9 +177,7 @@ static int ip3gen(struct chan *c, int i, struct dir *dp)
                        return founddevdir(c, q, "ctl", 0,
                                                   cv->owner, cv->perm, dp);
                case Qdata:
-                       perm = cv->perm;
-                       perm |= qreadable(cv->rq) ? DMREADABLE : 0;
-                       perm |= qwritable(cv->wq) ? DMWRITABLE : 0;
+                       perm = qdata_stat_perm(cv);
                        return founddevdir(c, q, "data", qlen(cv->rq),
                                                           cv->owner, perm, dp);
                case Qerr:
@@ -157,7 +186,9 @@ static int ip3gen(struct chan *c, int i, struct dir *dp)
                        return founddevdir(c, q, "err", qlen(cv->eq),
                                                           cv->owner, perm, dp);
                case Qlisten:
-                       return founddevdir(c, q, "listen", 0, cv->owner, cv->perm, dp);
+                       perm = cv->perm;
+                       perm |= cv->incall ? DMREADABLE : 0;
+                       return founddevdir(c, q, "listen", 0, cv->owner, perm, dp);
                case Qlocal:
                        p = "local";
                        break;
@@ -231,7 +262,7 @@ static int ip1gen(struct chan *c, int i, struct dir *dp)
        }
        devdir(c, q, p, len, network, prot, dp);
        if (i == Qndb && f->ndbmtime > kerndate)
-               dp->mtime = f->ndbmtime;
+               dp->mtime.tv_sec = f->ndbmtime;
        return 1;
 }
 
@@ -616,7 +647,7 @@ static int ipwstat(struct chan *c, uint8_t * dp, int n)
                error(EPERM, ERROR_FIXME);
        if (!emptystr(d->uid))
                kstrdup(&cv->owner, d->uid);
-       if (d->mode != ~0UL)
+       if (d->mode != -1)
                cv->perm = d->mode & 0777;
        poperror();
        kfree(d);
@@ -640,9 +671,11 @@ static char *ipchaninfo(struct chan *ch, char *ret, size_t ret_l)
                case Qdata:
                        proto = f->p[PROTO(ch->qid)];
                        conv = proto->conv[CONV(ch->qid)];
-                       snprintf(ret, ret_l, "Qdata, %s, proto %s, conv idx %d, rq len %d, wq len %d",
+                       snprintf(ret, ret_l,
+                                "Qdata, %s, proto %s, conv idx %d, rq len %d, wq len %d, total read %llu",
                                 SLIST_EMPTY(&conv->data_taps) ? "untapped" : "tapped",
-                                proto->name, conv->x, qlen(conv->rq), qlen(conv->wq));
+                                proto->name, conv->x, qlen(conv->rq), qlen(conv->wq),
+                                        q_bytes_read(conv->rq));
                        break;
                case Qarp:
                        ret = "Qarp";
@@ -653,9 +686,10 @@ static char *ipchaninfo(struct chan *ch, char *ret, size_t ret_l)
                case Qlisten:
                        proto = f->p[PROTO(ch->qid)];
                        conv = proto->conv[CONV(ch->qid)];
-                       snprintf(ret, ret_l, "Qlisten, %s proto %s, conv idx %d",
+                       snprintf(ret, ret_l,
+                                "Qlisten, %s proto %s, conv idx %d, has %sincalls",
                                 SLIST_EMPTY(&conv->listen_taps) ? "untapped" : "tapped",
-                                proto->name, conv->x);
+                                proto->name, conv->x, conv->incall ? "" : "no ");
                        break;
                case Qlog:
                        ret = "Qlog";
@@ -1043,12 +1077,18 @@ void Fsstdconnect(struct conv *c, char *argv[], int argc)
                        break;
        }
 
+       /* TODO: why is an IPnoaddr (in v6 format, equivalent to v6Unspecified),
+        * a v4 format? */
        if ((memcmp(c->raddr, v4prefix, IPv4off) == 0 &&
                 memcmp(c->laddr, v4prefix, IPv4off) == 0)
                || ipcmp(c->raddr, IPnoaddr) == 0)
                c->ipversion = V4;
        else
                c->ipversion = V6;
+       /* Linux has taught people to use zeros for local interfaces.  TODO: We
+        * might need this for v6 in the future. */
+       if (!ipcmp(c->raddr, IPv4_zeroes))
+               ipmove(c->raddr, IPv4_loopback);
 }
 
 /*
@@ -1059,7 +1099,8 @@ static int connected(void *a)
        return ((struct conv *)a)->state == Connected;
 }
 
-static void connectctlmsg(struct Proto *x, struct conv *c, struct cmdbuf *cb)
+static void connectctlmsg(struct Proto *x, struct conv *c, struct cmdbuf *cb,
+                          struct chan *chan)
 {
        ERRSTACK(1);
        char *p;
@@ -1070,8 +1111,22 @@ static void connectctlmsg(struct Proto *x, struct conv *c, struct cmdbuf *cb)
        c->cerr[0] = '\0';
        if (x->connect == NULL)
                error(EFAIL, "connect not supported");
+       /* It's up to the proto connect method to not block the kthread.  This is
+        * currently the case for e.g. TCP. */
        x->connect(c, cb->f, cb->nf);
-
+       /* This is notionally right before the rendez_sleep: either we block or we
+        * kick back to userspace.  We do this before the unlock to avoid races with
+        * c->state (rendez's internal lock deals with its race with the waker) and
+        * to avoid the excessive unlock and relock.
+        *
+        * Also, it's important that we don't do anything important for the
+        * functionality of the conv after the rendez sleep.  The non-blocking style
+        * won't call back into the kernel - it just wants the event.  I considered
+        * allowing multiple connect calls, where we just return if it was already
+        * connected, but that would break UDP, which allows multiple different
+        * connect calls. */
+       if ((chan->flag & O_NONBLOCK) && !connected(c))
+               error(EINPROGRESS, "connection not ready yet");
        qunlock(&c->qlock);
        if (waserror()) {
                qlock(&c->qlock);
@@ -1396,7 +1451,7 @@ static long ipwrite(struct chan *ch, void *v, long n, int64_t off)
                        if (cb->nf < 1)
                                error(EFAIL, "short control request");
                        if (strcmp(cb->f[0], "connect") == 0)
-                               connectctlmsg(x, c, cb);
+                               connectctlmsg(x, c, cb, ch);
                        else if (strcmp(cb->f[0], "announce") == 0)
                                announcectlmsg(x, c, cb);
                        else if (strcmp(cb->f[0], "bind") == 0)
@@ -1465,19 +1520,10 @@ static long ipbwrite(struct chan *ch, struct block *bp, uint32_t offset)
        }
 }
 
-static void ip_wake_cb(struct queue *q, void *data, int filter)
+static void fire_data_taps(struct conv *conv, int filter)
 {
-       struct conv *conv = (struct conv*)data;
        struct fd_tap *tap_i;
-       /* For these two, we want to ignore events on the opposite end of the
-        * queues.  For instance, we want to know when the WQ is writable.  Our
-        * writes will actually make it readable - we don't want to trigger a tap
-        * for that.  However, qio doesn't know how/why we are using a queue, or
-        * even who the ends are (hence the callbacks) */
-       if ((filter & FDTAP_FILT_READABLE) && (q == conv->wq))
-               return;
-       if ((filter & FDTAP_FILT_WRITABLE) && (q == conv->rq))
-               return;
+
        /* At this point, we have an event we want to send to our taps (if any).
         * The lock protects list integrity and the existence of the tap.
         *
@@ -1501,6 +1547,22 @@ static void ip_wake_cb(struct queue *q, void *data, int filter)
        spin_unlock(&conv->tap_lock);
 }
 
+static void ip_wake_cb(struct queue *q, void *data, int filter)
+{
+       struct conv *conv = (struct conv*)data;
+
+       /* For these two, we want to ignore events on the opposite end of the
+        * queues.  For instance, we want to know when the WQ is writable.  Our
+        * writes will actually make it readable - we don't want to trigger a tap
+        * for that.  However, qio doesn't know how/why we are using a queue, or
+        * even who the ends are (hence the callbacks) */
+       if ((filter & FDTAP_FILT_READABLE) && (q == conv->wq))
+               return;
+       if ((filter & FDTAP_FILT_WRITABLE) && (q == conv->rq))
+               return;
+       fire_data_taps(conv, filter);
+}
+
 int iptapfd(struct chan *chan, struct fd_tap *tap, int cmd)
 {
        struct conv *conv = chan2conv(chan);
@@ -1736,6 +1798,8 @@ int Fsconnected(struct conv *c, char *msg)
        }
 
        rendez_wakeup(&c->cr);
+       /* The user can poll or tap the connection status via Qdata */
+       fire_data_taps(c, FDTAP_FILT_WRITABLE);
        return 0;
 }