net: Delay etherbind until the link is up
authorBarret Rhoden <brho@cs.berkeley.edu>
Tue, 14 Nov 2017 19:26:20 +0000 (14:26 -0500)
committerBarret Rhoden <brho@cs.berkeley.edu>
Thu, 16 Nov 2017 15:46:57 +0000 (10:46 -0500)
On some drivers, at least r8169, the link auto-negotiation takes a few
seconds.  It starts during attach(), but we can't wait there.  Lots of NICs
get attached, including ones that will never have a link.  Note that
attach() happens when you try to cat \#ether.0/ether0/addr.

We can't wait at attach.  If we try to error out during transmit, that
actually breaks (error at ipifc.c L444).  The IP stack will try to send
packets.

So we have two choices: on every transmit, do a netif_wait_for_carrier(),
or catch it after attach, but before Ipifc initialization.  It turns out
that during etherbind() we can wait.  This is when we try to prepare the
NIC to use IPv4, v6, and ARP.

Note this means all 9ns drivers need to tell the rest of the stack that
their links are up.

Signed-off-by: Barret Rhoden <brho@cs.berkeley.edu>
kern/drivers/dev/ether.c
kern/drivers/net/ether8139.c
kern/drivers/net/ether8169.c
kern/drivers/net/ether82563.c
kern/drivers/net/etherigbe.c
kern/include/linux/compat_todo.h
kern/include/linux_compat.h
kern/include/net/ip.h
kern/include/rendez.h
kern/src/net/netif.c

index 024ab94..9a73098 100644 (file)
@@ -695,6 +695,7 @@ static void etherreset(void)
                memset(ether, 0, sizeof(struct ether));
                rwinit(&ether->rwlock);
                qlock_init(&ether->vlq);
+               rendez_init(&ether->link_rz);
                ether->ctlrno = ctlrno;
                ether->mbps = 10;
                ether->mtu = ETHERMAXTU;
index 6580182..b492949 100644 (file)
@@ -414,6 +414,7 @@ static void rtl8139init(struct ether *edev)
        csr32w(ctlr, Rcr, ctlr->rcr);
 
        spin_unlock_irqsave(&ctlr->ilock);
+       netif_carrier_on(edev);
 }
 
 static void rtl8139attach(struct ether *edev)
index 4b50bdc..2f42e0a 100644 (file)
@@ -859,6 +859,7 @@ rtl8169attach(struct ether* edev)
        printd("%s: speed %d fd %d link %d rfc %d tfc %d\n",
                edev->name, phy->speed, phy->fd, phy->link, phy->rfc,
               phy->tfc);
+       netif_carrier_on(edev);
 }
 
 static void
index fe307a6..ba44a1c 100644 (file)
@@ -1543,6 +1543,7 @@ static void i82563lproc(void *v)
                                edev->mbps = speedtab[sp];
                        if (prevlink == 0 && ctlr->type == i218)
                                k1fix(ctlr);    /* link newly up: kludge away */
+                       netif_carrier_on(edev);
                } else
                        ctlr->didk1fix = 0;     /* force fix at next link up */
                prevlink = edev->link;
index 5f1179f..f92468f 100644 (file)
@@ -899,6 +899,7 @@ igbelproc(void* arg)
                csr32w(ctlr, Ctrl, ctrl);
 
 enable:
+               netif_carrier_on(edev);
                ctlr->lim = 0;
                igbeim(ctlr, Lsc);
 
index 6a625d9..13b76dc 100644 (file)
@@ -1008,11 +1008,6 @@ static inline void eth_broadcast_addr(uint8_t *addr)
        memset(addr, 0xff, Eaddrlen);
 }
 
-static inline bool netif_carrier_ok(struct ether *dev)
-{
-       return true; // XXX
-}
-
 #define BOND_LINK_FAIL 1
 #define BOND_LINK_UP 2
 #define BOND_MODE_8023AD 1
index 72eb44a..68e3655 100644 (file)
@@ -814,8 +814,6 @@ static bool pci_dev_run_wake(struct pci_device *dev)
  * using them, but we can leave the functions around to remind us what the code
  * is supposed to do, especially for things we don't support yet. */
 #define SET_NETDEV_DEV(...)
-#define netif_carrier_off(...)
-#define netif_carrier_on(...)
 /* May need to do something with edev's queues or flags. */
 #define netif_tx_wake_all_queues(...)
 #define netif_tx_wake_queue(...)
index fb7b9f3..1903e6c 100644 (file)
@@ -1076,7 +1076,9 @@ struct netif {
        int limit;                                      /* flow control */
        int alen;                                       /* address length */
        int mbps;                                       /* megabits per sec */
-       int link;                                       /* link status */
+       int link;                                       /* link status (seems to be driver specific) */
+       struct rendez link_rz;
+       bool link_is_up;
        unsigned int feat;                      /* dev features turned on */
        unsigned int hw_features;       /* dev features available */
        uint8_t addr[Nmaxaddr];
@@ -1180,6 +1182,27 @@ struct ether {
        struct netif;
 };
 
+static inline void netif_carrier_on(struct ether *edev)
+{
+       edev->link_is_up = TRUE;
+       rendez_wakeup(&edev->link_rz);
+}
+
+static inline bool netif_carrier_ok(struct ether *edev)
+{
+       return edev->link_is_up;
+}
+
+static inline void netif_carrier_off(struct ether *edev)
+{
+       edev->link_is_up = FALSE;
+}
+
+static void netif_wait_for_carrier(struct ether *edev)
+{
+       rendez_sleep(&edev->link_rz, (rendez_cond_t)netif_carrier_ok, edev);
+}
+
 extern struct block *etheriq(struct ether *, struct block *, int);
 extern void addethercard(char *unused_char_p_t, int (*)(struct ether *));
 extern int archether(int unused_int, struct ether *);
index 74975ca..f607105 100644 (file)
@@ -44,6 +44,8 @@ struct rendez {
        struct cond_var                         cv;
 };
 
+typedef int (*rendez_cond_t)(void *arg);
+
 void rendez_init(struct rendez *rv);
 void rendez_sleep(struct rendez *rv, int (*cond)(void*), void *arg);
 void rendez_sleep_timeout(struct rendez *rv, int (*cond)(void*), void *arg,
index b342889..3494269 100644 (file)
@@ -414,6 +414,10 @@ long netifwrite(struct ether *nif, struct chan *c, void *a, long n)
 
        f = nif->f[NETID(c->qid.path)];
        if ((p = matchtoken(buf, "connect")) != 0) {
+               /* We'd like to not use the NIC until it has come up fully -
+                * auto-negotiation is done and packets will get sent out.  This is
+                * about the best place to do it. */
+               netif_wait_for_carrier(nif);
                type = strtol(p, 0, 0); /* allows any base, though usually hex */
                if (typeinuse(nif, type))
                        error(EBUSY, ERROR_FIXME);