ioctls and trailers
[unix-history] / usr / src / sys / vax / if / if_vv.c
index fcbc873..c3afc2a 100644 (file)
@@ -1,12 +1,13 @@
-/*     if_vv.c 4.11    82/12/17        */
+/*     if_vv.c 4.21    83/06/12        */
 
 #include "vv.h"
 
 #include "vv.h"
-#if NVV > 0
+
 /*
  * Proteon 10 Meg Ring Driver.
  * This device is called "vv" because its "real name",
  * V2LNI won't work if shortened to the obvious "v2".
  * Hence the subterfuge.
 /*
  * Proteon 10 Meg Ring Driver.
  * This device is called "vv" because its "real name",
  * V2LNI won't work if shortened to the obvious "v2".
  * Hence the subterfuge.
+ *
  */
 #include "../machine/pte.h"
 
  */
 #include "../machine/pte.h"
 
 #include "../h/protosw.h"
 #include "../h/socket.h"
 #include "../h/vmmac.h"
 #include "../h/protosw.h"
 #include "../h/socket.h"
 #include "../h/vmmac.h"
-#include <errno.h>
+#include "../h/errno.h"
+#include "../h/time.h"
+#include "../h/kernel.h"
+#include "../h/ioctl.h"
 
 #include "../net/if.h"
 
 #include "../net/if.h"
+#include "../net/netisr.h"
 #include "../net/route.h"
 #include "../net/route.h"
+
 #include "../netinet/in.h"
 #include "../netinet/in_systm.h"
 #include "../netinet/ip.h"
 #include "../netinet/ip_var.h"
 
 #include "../netinet/in.h"
 #include "../netinet/in_systm.h"
 #include "../netinet/ip.h"
 #include "../netinet/ip_var.h"
 
-#include "../vax/cpu.h"
 #include "../vax/mtpr.h"
 #include "../vax/mtpr.h"
-#include "../vaxif/if_vv.h"
-#include "../vaxif/if_uba.h"
+#include "../vax/cpu.h"
+
 #include "../vaxuba/ubareg.h"
 #include "../vaxuba/ubavar.h"
 
 #include "../vaxuba/ubareg.h"
 #include "../vaxuba/ubavar.h"
 
-#include "vv.h"
+#include "../vaxif/if_vv.h"
+#include "../vaxif/if_uba.h"
 
 /*
  * N.B. - if WIRECENTER is defined wrong, it can well break
  * the hardware!!
  */
 
 /*
  * N.B. - if WIRECENTER is defined wrong, it can well break
  * the hardware!!
  */
-
 #define        WIRECENTER
 
 #ifdef WIRECENTER
 #define        WIRECENTER
 
 #ifdef WIRECENTER
 #define        VVMTU   (1024+512)
 #define VVMRU  (1024+512+16)   /* space for trailer */
 
 #define        VVMTU   (1024+512)
 #define VVMRU  (1024+512+16)   /* space for trailer */
 
-int vv_dotrailer = 1,          /* so can do trailers selectively */
-    vv_trace = 0;
+int vv_tracehdr = 0,           /* 1 => trace headers (slowly!!) */
+    vv_tracetimeout = 1;       /* 1 => trace input error-rate limiting */
+    vv_logreaderrors = 0;      /* 1 => log all read errors */
+
+#define vvtracehdr     if (vv_tracehdr) vvprt_hdr
+#define        vvtrprintf      if (vv_tracetimeout) printf
+
+int vv_ticking = 0;            /* error flywheel is running */
+
+/*
+ * Interval in HZ - 50 msec.
+ * N.B. all times below are in units of flywheel ticks
+ */
+#define VV_FLYWHEEL            3
+#define        VV_ERRORTHRESHOLD       100     /* errors/flywheel-interval */
+#define        VV_MODE1ATTEMPTS        10      /* number mode 1 retries */
+#define        VV_MODE1DELAY           2       /* period interface is PAUSEd - 100ms */
+#define VV_MODE2DELAY          4       /* base interval host relay is off - 200ms */
+#define        VV_MAXDELAY             6400    /* max interval host relay is off - 2 minutes */
 
 int    vvprobe(), vvattach(), vvrint(), vvxint();
 struct uba_device *vvinfo[NVV];
 
 int    vvprobe(), vvattach(), vvrint(), vvxint();
 struct uba_device *vvinfo[NVV];
@@ -60,7 +82,7 @@ u_short vvstd[] = { 0 };
 struct uba_driver vvdriver =
        { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
 #define        VVUNIT(x)       minor(x)
 struct uba_driver vvdriver =
        { vvprobe, 0, vvattach, 0, vvstd, "vv", vvinfo };
 #define        VVUNIT(x)       minor(x)
-int    vvinit(),vvoutput(),vvreset();
+int    vvinit(),vvioctl(),vvoutput(),vvreset();
 
 /*
  * Software status of each interface.
 
 /*
  * Software status of each interface.
@@ -77,15 +99,36 @@ int vvinit(),vvoutput(),vvreset();
 struct vv_softc {
        struct  ifnet vs_if;            /* network-visible interface */
        struct  ifuba vs_ifuba;         /* UNIBUS resources */
 struct vv_softc {
        struct  ifnet vs_if;            /* network-visible interface */
        struct  ifuba vs_ifuba;         /* UNIBUS resources */
-       short   vs_oactive;             /* is output active? */
+       short   vs_oactive;             /* is output active */
+       short   vs_iactive;             /* is input active */
        short   vs_olen;                /* length of last output */
        u_short vs_lastx;               /* last destination address */
        short   vs_olen;                /* length of last output */
        u_short vs_lastx;               /* last destination address */
-       short   vs_tries;               /* current retry count */
+       short   vs_tries;               /* transmit current retry count */
        short   vs_init;                /* number of ring inits */
        short   vs_init;                /* number of ring inits */
-       short   vs_flush;               /* number of flushed packets */
        short   vs_nottaken;            /* number of packets refused */
        short   vs_nottaken;            /* number of packets refused */
+       /* input error rate limiting state */
+       short   vs_major;               /* recovery major state */
+       short   vs_minor;               /* recovery minor state */
+       short   vs_retry;               /* recovery retry count */
+       short   vs_delayclock;          /* recovery delay clock */
+       short   vs_delayrange;          /* increasing delay interval */
+       short   vs_dropped;             /* number of packes tossed in last dt */
 } vv_softc[NVV];
 
 } vv_softc[NVV];
 
+/*
+ * States of vs_iactive.
+ */
+#define        ACTIVE  1               /* interface should post new receives */
+#define        PAUSE   0               /* interface should NOT post new receives */
+#define        OPEN    -1              /* PAUSE and open host relay */
+
+/*
+ * Recovery major states.
+ */
+#define        MODE0   0               /* everything is wonderful */
+#define        MODE1   1               /* hopefully whatever will go away */
+#define        MODE2   2               /* drastic measures - open host relay for increasing intervals */
+
 vvprobe(reg)
        caddr_t reg;
 {
 vvprobe(reg)
        caddr_t reg;
 {
@@ -93,12 +136,12 @@ vvprobe(reg)
        register struct vvreg *addr = (struct vvreg *)reg;
 
 #ifdef lint
        register struct vvreg *addr = (struct vvreg *)reg;
 
 #ifdef lint
-       br = 0; cvec = br; br = cvec;
+       br = 0; cvec = br; br = cvec; vvrint(0);
 #endif
        /* reset interface, enable, and wait till dust settles */
        addr->vvicsr = VV_RST;
        addr->vvocsr = VV_RST;
 #endif
        /* reset interface, enable, and wait till dust settles */
        addr->vvicsr = VV_RST;
        addr->vvocsr = VV_RST;
-       DELAY(100000);
+       DELAY(10000);
        /* generate interrupt by doing 1 word DMA from 0 in uba space!! */
        addr->vvocsr = VV_IEN;          /* enable interrupt */
        addr->vvoba = 0;                /* low 16 bits */
        /* generate interrupt by doing 1 word DMA from 0 in uba space!! */
        addr->vvocsr = VV_IEN;          /* enable interrupt */
        addr->vvoba = 0;                /* low 16 bits */
@@ -121,27 +164,20 @@ vvattach(ui)
        struct uba_device *ui;
 {
        register struct vv_softc *vs = &vv_softc[ui->ui_unit];
        struct uba_device *ui;
 {
        register struct vv_softc *vs = &vv_softc[ui->ui_unit];
-       register struct sockaddr_in *sin;
 
        vs->vs_if.if_unit = ui->ui_unit;
        vs->vs_if.if_name = "vv";
        vs->vs_if.if_mtu = VVMTU;
 
        vs->vs_if.if_unit = ui->ui_unit;
        vs->vs_if.if_name = "vv";
        vs->vs_if.if_mtu = VVMTU;
-       vs->vs_if.if_net = ui->ui_flags;
-       vs->vs_if.if_host[0] = 0;       /* this will be reset in vvinit() */
-
-       sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
-       sin->sin_family = AF_INET;
-       sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
-
-       sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr;
-       sin->sin_family = AF_INET;
-       sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST);
-       vs->vs_if.if_flags = IFF_BROADCAST;
-
        vs->vs_if.if_init = vvinit;
        vs->vs_if.if_init = vvinit;
+       vs->vs_if.if_ioctl = vvioctl;
        vs->vs_if.if_output = vvoutput;
        vs->vs_if.if_reset = vvreset;
        vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16;
        vs->vs_if.if_output = vvoutput;
        vs->vs_if.if_reset = vvreset;
        vs->vs_ifuba.ifu_flags = UBA_CANTWAIT | UBA_NEEDBDP | UBA_NEED16;
+#if defined(VAX750)
+       /* don't chew up 750 bdp's */
+       if (cpu == VAX_750 && ui->ui_unit > 0)
+               vs->vs_ifuba.ifu_flags &= ~UBA_NEEDBDP;
+#endif
        if_attach(&vs->vs_if);
 }
 
        if_attach(&vs->vs_if);
 }
 
@@ -173,37 +209,39 @@ vvinit(unit)
        register struct vvreg *addr;
        struct sockaddr_in *sin;
        int ubainfo, s;
        register struct vvreg *addr;
        struct sockaddr_in *sin;
        int ubainfo, s;
+       int vvtimeout();
 
 
+       if (vs->vs_if.if_net == 0)
+               return;
        addr = (struct vvreg *)ui->ui_addr;
        if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
            sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 
        addr = (struct vvreg *)ui->ui_addr;
        if (if_ubainit(&vs->vs_ifuba, ui->ui_ubanum,
            sizeof (struct vv_header), (int)btoc(VVMTU)) == 0) { 
-nogo:
                printf("vv%d: can't initialize\n", unit);
                vs->vs_if.if_flags &= ~IFF_UP;
                return;
        }
                printf("vv%d: can't initialize\n", unit);
                vs->vs_if.if_flags &= ~IFF_UP;
                return;
        }
-
+       if (vv_ticking++ == 0)
+               timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
        /*
        /*
-        * discover our host address and post it
+        * Discover our host address and post it
         */
         */
-
        vs->vs_if.if_host[0] = vvidentify(unit);
        vs->vs_if.if_host[0] = vvidentify(unit);
-       if (vs->vs_if.if_host[0] == 0)
-               goto nogo;
        printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]);
        sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
        sin->sin_family = AF_INET;
        printf("vv%d: host %d\n", unit, vs->vs_if.if_host[0]);
        sin = (struct sockaddr_in *)&vs->vs_if.if_addr;
        sin->sin_family = AF_INET;
-       sin->sin_addr =
-           if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
+       sin->sin_addr = if_makeaddr(vs->vs_if.if_net, vs->vs_if.if_host[0]);
+       sin = (struct sockaddr_in *)&vs->vs_if.if_broadaddr;
+       sin->sin_family = AF_INET;
+       sin->sin_addr = if_makeaddr(vs->vs_if.if_net, VV_BROADCAST);
 
        /*
         * Reset the interface, and join the ring
         */
        addr->vvocsr = VV_RST | VV_CPB;         /* clear packet buffer */
        addr->vvicsr = VV_RST | VV_CONF;        /* close logical relay */
 
        /*
         * Reset the interface, and join the ring
         */
        addr->vvocsr = VV_RST | VV_CPB;         /* clear packet buffer */
        addr->vvicsr = VV_RST | VV_CONF;        /* close logical relay */
-       sleep((caddr_t)&lbolt, PZERO);          /* let contacts settle */
+       DELAY(500000);                          /* let contacts settle */
        vs->vs_init = 0;
        vs->vs_init = 0;
-       vs->vs_flush = 0;
+       vs->vs_dropped = 0;
        vs->vs_nottaken = 0;
 
        /*
        vs->vs_nottaken = 0;
 
        /*
@@ -212,12 +250,13 @@ nogo:
         */
        s = splimp();
        ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
         */
        s = splimp();
        ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
-       addr->vviba = (u_short) ubainfo;
-       addr->vviea = (u_short) (ubainfo >> 16);
+       addr->vviba = (u_short)ubainfo;
+       addr->vviea = (u_short)(ubainfo >> 16);
        addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
        addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB;
        addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
        addr->vvicsr = VV_IEN | VV_CONF | VV_DEN | VV_ENB;
+       vs->vs_iactive = ACTIVE;
        vs->vs_oactive = 1;
        vs->vs_oactive = 1;
-       vs->vs_if.if_flags |= IFF_UP;
+       vs->vs_if.if_flags |= IFF_UP | IFF_RUNNING;
        vvxint(unit);
        splx(s);
        if_rtinit(&vs->vs_if, RTF_UP);
        vvxint(unit);
        splx(s);
        if_rtinit(&vs->vs_if, RTF_UP);
@@ -227,38 +266,36 @@ nogo:
  * vvidentify() - return our host address
  */
 vvidentify(unit)
  * vvidentify() - return our host address
  */
 vvidentify(unit)
+       int unit;
 {
        register struct vv_softc *vs = &vv_softc[unit];
        register struct uba_device *ui = vvinfo[unit];
        register struct vvreg *addr;
        struct mbuf *m;
        struct vv_header *v;
 {
        register struct vv_softc *vs = &vv_softc[unit];
        register struct uba_device *ui = vvinfo[unit];
        register struct vvreg *addr;
        struct mbuf *m;
        struct vv_header *v;
-       int ubainfo, retrying, attempts, waitcount, s;
+       int ubainfo, attempts, waitcount;
 
        /*
         * Build a multicast message to identify our address
         */
        addr = (struct vvreg *)ui->ui_addr;
        attempts = 0;           /* total attempts, including bad msg type */
 
        /*
         * Build a multicast message to identify our address
         */
        addr = (struct vvreg *)ui->ui_addr;
        attempts = 0;           /* total attempts, including bad msg type */
-       retrying = 0;           /* first time through */
        m = m_get(M_DONTWAIT, MT_HEADER);
        m = m_get(M_DONTWAIT, MT_HEADER);
-       if (m == 0) {
-               printf("vvinit: can't get mbuf");
+       if (m == NULL)
                return (0);
                return (0);
-       }
+       m->m_next = 0;
        m->m_off = MMINOFF;
        m->m_len = sizeof(struct vv_header);
        m->m_off = MMINOFF;
        m->m_len = sizeof(struct vv_header);
-
        v = mtod(m, struct vv_header *);
        v = mtod(m, struct vv_header *);
-       v->vh_dhost = 0;                /* multicast destination address */
+       v->vh_dhost = VV_BROADCAST;     /* multicast destination address */
        v->vh_shost = 0;                /* will be overwritten with ours */
        v->vh_version = RING_VERSION;
        v->vh_type = RING_WHOAMI;
        v->vh_info = 0;
        v->vh_shost = 0;                /* will be overwritten with ours */
        v->vh_version = RING_VERSION;
        v->vh_type = RING_WHOAMI;
        v->vh_info = 0;
+       /* map xmit message into uba */
        vs->vs_olen =  if_wubaput(&vs->vs_ifuba, m);
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        vs->vs_olen =  if_wubaput(&vs->vs_ifuba, m);
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
-
        /*
         * Reset interface, establish Digital Loopback Mode, and
         * send the multicast (to myself) with Input Copy enabled.
        /*
         * Reset interface, establish Digital Loopback Mode, and
         * send the multicast (to myself) with Input Copy enabled.
@@ -272,8 +309,7 @@ retry:
        addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB;
 
        /* let flag timers fire so ring will initialize */
        addr->vvicsr = VV_STE | VV_DEN | VV_ENB | VV_LPB;
 
        /* let flag timers fire so ring will initialize */
-       sleep((caddr_t) &lbolt, PZERO);
-       sleep((caddr_t) &lbolt, PZERO);
+       DELAY(2000000);
 
        addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */
        ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
 
        addr->vvocsr = VV_RST | VV_CPB; /* clear packet buffer */
        ubainfo = vs->vs_ifuba.ifu_w.ifrw_info;
@@ -281,27 +317,26 @@ retry:
        addr->vvoea = (u_short) (ubainfo >> 16);
        addr->vvowc = -((vs->vs_olen + 1) >> 1);
        addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
        addr->vvoea = (u_short) (ubainfo >> 16);
        addr->vvowc = -((vs->vs_olen + 1) >> 1);
        addr->vvocsr = VV_CPB | VV_DEN | VV_INR | VV_ENB;
-
        /*
         * Wait for receive side to finish.
        /*
         * Wait for receive side to finish.
-        * Extract source address (which will be our own),
+        * Extract source address (which will our own),
         * and post to interface structure.
         */
        DELAY(1000);
         * and post to interface structure.
         */
        DELAY(1000);
-       for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++)
-               if (waitcount < 10)
+       for (waitcount = 0; (addr->vvicsr & VV_RDY) == 0; waitcount++) {
+               if (waitcount < 10) {
                        DELAY(1000);
                        DELAY(1000);
-               else {
-                       if (attempts++ < 10)
-                               goto retry;
-                       else {
-                               printf("vv%d: can't initialize\n", unit);
-                               printf("vvinit loopwait: icsr = %b\n",
-                                       0xffff&(addr->vvicsr),VV_IBITS);
-                               vs->vs_if.if_flags &= ~IFF_UP;
-                               return (0);
-                       }
+                       continue;
                }
                }
+               if (attempts++ >= 10) {
+                       printf("vv%d: can't initialize\n", unit);
+                       printf("vvinit loopwait: icsr = %b\n",
+                               0xffff&(addr->vvicsr), VV_IBITS);
+                       vs->vs_if.if_flags &= ~IFF_UP;
+                       return (0);
+               }
+               goto retry;
+       }
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        if (vs->vs_ifuba.ifu_xtofree)
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        if (vs->vs_ifuba.ifu_xtofree)
@@ -309,19 +344,163 @@ retry:
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
        m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0);
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
        m = if_rubaget(&vs->vs_ifuba, sizeof(struct vv_header), 0);
-       if (m)
+       if (m != NULL)
                m_freem(m);
        /*
                m_freem(m);
        /*
-        * check message type before we believe the source host address
+        * Check message type before we believe the source host address
         */
        v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
        if (v->vh_type != RING_WHOAMI)
                goto retry;
         */
        v = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
        if (v->vh_type != RING_WHOAMI)
                goto retry;
-       return (v->vh_shost);
+       return(v->vh_shost);
+}
+
+/*
+ * vvtimeout() - called by timer flywheel to monitor input packet
+ * discard rate.  Interfaces getting too many errors are shut
+ * down for a while.  If the condition persists, the interface
+ * is marked down.
+ */
+/*ARGSUSED*/
+vvtimeout(junk)
+       int junk;
+{
+       register struct vv_softc *vs;
+       register int i;
+       register struct vvreg *addr;
+       int ubainfo;
+
+       timeout(vvtimeout, (caddr_t) 0, VV_FLYWHEEL);
+       for (i = 0; i < NVV; i++) {
+               vs = &vv_softc[i];
+               addr = (struct vvreg *)vvinfo[i]->ui_addr;
+               if ((vs->vs_if.if_flags & IFF_UP) == 0)
+                       continue;
+               switch (vs->vs_major) {
+
+               /*
+                * MODE0: generally OK, just check error rate 
+                */
+               case MODE0:
+                       if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
+                               vs->vs_dropped = 0;
+                               continue;
+                       }
+                       /* suspend reads for a while */
+                       vvtrprintf("vv%d going MODE1 in vvtimeout\n",i);
+                       vs->vs_major = MODE1;
+                       vs->vs_iactive = PAUSE; /* no new reads */
+                       vs->vs_retry = VV_MODE1ATTEMPTS;
+                       vs->vs_delayclock = VV_MODE1DELAY;
+                       vs->vs_minor = 0;
+                       continue;
+
+               /*
+                * MODE1: excessive error rate observed
+                * Scheme: try simply suspending reads for a
+                * short while a small number of times
+                */
+               case MODE1:
+                       if (vs->vs_delayclock > 0) {
+                               vs->vs_delayclock--;
+                               continue;
+                       }
+                       switch (vs->vs_minor) {
+
+                       case 0:                         /* reenable reads */
+                               vvtrprintf("vv%d M1m0\n",i);
+                               vs->vs_dropped = 0;
+                               vs->vs_iactive = ACTIVE;
+                               vs->vs_minor = 1;       /* next state */
+                               ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
+                               addr->vviba = (u_short) ubainfo;
+                               addr->vviea = (u_short) (ubainfo >> 16);
+                               addr->vviwc =
+                                 -(sizeof (struct vv_header) + VVMTU) >> 1;
+                               addr->vvicsr = VV_RST | VV_CONF;
+                               addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
+                               continue;
+
+                       case 1:                         /* see if it worked */
+                               vvtrprintf("vv%d M1m1\n",i);
+                               if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
+                                       vs->vs_dropped = 0;
+                                       vs->vs_major = MODE0;   /* yeah!! */
+                                       continue;
+                               }
+                               if (vs->vs_retry -- > 0) {
+                                       vs->vs_dropped = 0;
+                                       vs->vs_iactive = PAUSE;
+                                       vs->vs_delayclock = VV_MODE1DELAY;
+                                       vs->vs_minor = 0; /* recheck */
+                                       continue;
+                               }
+                               vs->vs_major = MODE2;
+                               vs->vs_minor = 0;
+                               vs->vs_dropped = 0;
+                               vs->vs_iactive = OPEN;
+                               vs->vs_delayrange = VV_MODE2DELAY;
+                               vs->vs_delayclock = VV_MODE2DELAY;
+                               /* fall thru ... */
+                       }
+
+               /*
+                * MODE2: simply ignoring traffic didn't relieve condition
+                * Scheme: open host relay for intervals linearly
+                * increasing up to some maximum of a several minutes.
+                * This allows broken networks to return to operation
+                * without rebooting.
+                */
+               case MODE2:
+                       if (vs->vs_delayclock > 0) {
+                               vs->vs_delayclock--;
+                               continue;
+                       }
+                       switch (vs->vs_minor) {
+
+                       case 0:         /* close relay and reenable reads */
+                               vvtrprintf("vv%d M2m0\n",i);
+                               vs->vs_dropped = 0;
+                               vs->vs_iactive = ACTIVE;
+                               vs->vs_minor = 1;       /* next state */
+                               ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
+                               addr->vviba = (u_short) ubainfo;
+                               addr->vviea = (u_short) (ubainfo >> 16);
+                               addr->vviwc =
+                                 -(sizeof (struct vv_header) + VVMTU) >> 1;
+                               addr->vvicsr = VV_RST | VV_CONF;
+                               addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
+                               continue;
+
+                       case 1:                         /* see if it worked */
+                               vvtrprintf("vv%d M2m1\n",i);
+                               if (vs->vs_dropped < VV_ERRORTHRESHOLD) {
+                                       vs->vs_dropped = 0;
+                                       vs->vs_major = MODE0;   /* yeah!! */
+                                       continue;
+                               }
+                               vvtrprintf("vv%d M2m1 ++ delay\n",i);
+                               vs->vs_dropped = 0;
+                               vs->vs_iactive = OPEN;
+                               vs->vs_minor = 0;
+                               if (vs->vs_delayrange < VV_MAXDELAY)
+                                       vs->vs_delayrange +=
+                                         (vs->vs_delayrange/2);
+                               vs->vs_delayclock = vs->vs_delayrange;
+                               continue;
+                       }
+
+               default:
+                       printf("vv%d: major state screwed\n", i);
+                       vs->vs_if.if_flags &= ~IFF_UP;
+               }
+       }
 }
 
 /*
  * Start or restart output on interface.
 }
 
 /*
  * Start or restart output on interface.
+ * If interface is active, this is a retransmit, so just
+ * restuff registers and go.
  * If interface is not already active, get another datagram
  * to send off of the interface queue, and map it to the interface
  * before starting the output.
  * If interface is not already active, get another datagram
  * to send off of the interface queue, and map it to the interface
  * before starting the output.
@@ -334,7 +513,8 @@ vvstart(dev)
        register struct vv_softc *vs = &vv_softc[unit];
        register struct vvreg *addr;
        struct mbuf *m;
        register struct vv_softc *vs = &vv_softc[unit];
        register struct vvreg *addr;
        struct mbuf *m;
-       int ubainfo, dest;
+       int ubainfo;
+       int dest;
 
        if (vs->vs_oactive)
                goto restart;
 
        if (vs->vs_oactive)
                goto restart;
@@ -344,19 +524,22 @@ vvstart(dev)
         * just return.
         */
        IF_DEQUEUE(&vs->vs_if.if_snd, m);
         * just return.
         */
        IF_DEQUEUE(&vs->vs_if.if_snd, m);
-       if (m == 0) {
+       if (m == NULL) {
                vs->vs_oactive = 0;
                return;
        }
        dest = mtod(m, struct vv_header *)->vh_dhost;
        vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
        vs->vs_lastx = dest;
                vs->vs_oactive = 0;
                return;
        }
        dest = mtod(m, struct vv_header *)->vh_dhost;
        vs->vs_olen = if_wubaput(&vs->vs_ifuba, m);
        vs->vs_lastx = dest;
-
 restart:
        /*
         * Have request mapped to UNIBUS for transmission.
         * Purge any stale data from this BDP, and start the otput.
         */
 restart:
        /*
         * Have request mapped to UNIBUS for transmission.
         * Purge any stale data from this BDP, and start the otput.
         */
+       if (vs->vs_olen > VVMTU + sizeof (struct vv_header)) {
+               printf("vv%d vs_olen: %d > VVMTU\n", unit, vs->vs_olen);
+               panic("vvdriver vs_olen botch");
+       }
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        addr = (struct vvreg *)ui->ui_addr;
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_w.ifrw_bdp);
        addr = (struct vvreg *)ui->ui_addr;
@@ -383,18 +566,18 @@ vvxint(unit)
        addr = (struct vvreg *)ui->ui_addr;
        oc = 0xffff & (addr->vvocsr);
        if (vs->vs_oactive == 0) {
        addr = (struct vvreg *)ui->ui_addr;
        oc = 0xffff & (addr->vvocsr);
        if (vs->vs_oactive == 0) {
-               printf("vv%d: stray interrupt, vvocsr=%b\n", unit,
+               printf("vv%d: stray interrupt vvocsr = %b\n", unit,
                        oc, VV_OBITS);
                return;
        }
        if (oc &  (VV_OPT | VV_RFS)) {
                vs->vs_if.if_collisions++;
                        oc, VV_OBITS);
                return;
        }
        if (oc &  (VV_OPT | VV_RFS)) {
                vs->vs_if.if_collisions++;
-               if (++(vs->vs_tries) < VVRETRY) {
+               if (vs->vs_tries++ < VVRETRY) {
                        if (oc & VV_OPT)
                                vs->vs_init++;
                        if (oc & VV_RFS)
                                vs->vs_nottaken++;
                        if (oc & VV_OPT)
                                vs->vs_init++;
                        if (oc & VV_RFS)
                                vs->vs_nottaken++;
-                       addr->vvocsr = VV_IEN | VV_ENB | VV_INR;
+                       vvstart(unit);          /* restart this message */
                        return;
                }
                if (oc & VV_OPT)
                        return;
                }
                if (oc & VV_OPT)
@@ -405,7 +588,7 @@ vvxint(unit)
        vs->vs_tries = 0;
        if (oc & VVXERR) {
                vs->vs_if.if_oerrors++;
        vs->vs_tries = 0;
        if (oc & VVXERR) {
                vs->vs_if.if_oerrors++;
-               printf("vv%d: error, vvocsr=%b\n", unit, 0xffff & oc,
+               printf("vv%d: error vvocsr = %b\n", unit, 0xffff & oc,
                        VV_OBITS);
        }
        if (vs->vs_ifuba.ifu_xtofree) {
                        VV_OBITS);
        }
        if (vs->vs_ifuba.ifu_xtofree) {
@@ -413,7 +596,7 @@ vvxint(unit)
                vs->vs_ifuba.ifu_xtofree = 0;
        }
        if (vs->vs_if.if_snd.ifq_head == 0) {
                vs->vs_ifuba.ifu_xtofree = 0;
        }
        if (vs->vs_if.if_snd.ifq_head == 0) {
-               vs->vs_lastx = 256;
+               vs->vs_lastx = 256;             /* an invalid address */
                return;
        }
        vvstart(unit);
                return;
        }
        vvstart(unit);
@@ -446,10 +629,9 @@ vvrint(unit)
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
        if (addr->vvicsr & VVRERR) {
        if (vs->vs_ifuba.ifu_flags & UBA_NEEDBDP)
                UBAPURGE(vs->vs_ifuba.ifu_uba, vs->vs_ifuba.ifu_r.ifrw_bdp);
        if (addr->vvicsr & VVRERR) {
-               /*
-               printf("vv%d: error vvicsr = %b\n", unit,
-                       0xffff&(addr->vvicsr), VV_IBITS);
-               */
+               if (vv_logreaderrors)
+                       printf("vv%d: error vvicsr = %b\n", unit,
+                               0xffff&(addr->vvicsr), VV_IBITS);
                goto dropit;
        }
 
                goto dropit;
        }
 
@@ -465,18 +647,18 @@ vvrint(unit)
         * format packets.
         */
        vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
         * format packets.
         */
        vv = (struct vv_header *)(vs->vs_ifuba.ifu_r.ifrw_addr);
-       if (vv_trace)
-               vvprt_hdr("vi", vv);
+       vvtracehdr("vi", vv);
        resid = addr->vviwc;
        if (resid)
                resid |= 0176000;               /* ugly!!!! */
        len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1;
        len -= sizeof(struct vv_header);
        resid = addr->vviwc;
        if (resid)
                resid |= 0176000;               /* ugly!!!! */
        len = (((sizeof (struct vv_header) + VVMRU) >> 1) + resid) << 1;
        len -= sizeof(struct vv_header);
-       if (len > VVMRU)
+       if (len > VVMRU || len <= 0)
                goto dropit;
 #define        vvdataaddr(vv, off, type)       ((type)(((caddr_t)((vv)+1)+(off))))
                goto dropit;
 #define        vvdataaddr(vv, off, type)       ((type)(((caddr_t)((vv)+1)+(off))))
-       if (vv_dotrailer && vv->vh_type >= RING_IPTrailer &&
-            vv->vh_type < RING_IPTrailer+RING_IPNTrailer){
+       if ((ifp->if_flags & IFF_NOTRAILERS) == 0)
+       if (vv->vh_type >= RING_IPTrailer &&
+            vv->vh_type < RING_IPTrailer+RING_IPNTrailer) {
                off = (vv->vh_type - RING_IPTrailer) * 512;
                if (off > VVMTU)
                        goto dropit;
                off = (vv->vh_type - RING_IPTrailer) * 512;
                if (off > VVMTU)
                        goto dropit;
@@ -490,13 +672,18 @@ vvrint(unit)
        if (len == 0)
                goto dropit;
        m = if_rubaget(&vs->vs_ifuba, len, off);
        if (len == 0)
                goto dropit;
        m = if_rubaget(&vs->vs_ifuba, len, off);
-       if (m == 0)
+       if (m == NULL)
                goto dropit;
        if (off) {
                m->m_off += 2 * sizeof(u_short);
                m->m_len -= 2 * sizeof(u_short);
        }
                goto dropit;
        if (off) {
                m->m_off += 2 * sizeof(u_short);
                m->m_len -= 2 * sizeof(u_short);
        }
+
+       /*
+        * Demultiplex on packet type 
+        */
        switch (vv->vh_type) {
        switch (vv->vh_type) {
+
 #ifdef INET
        case RING_IP:
                schednetisr(NETISR_IP);
 #ifdef INET
        case RING_IP:
                schednetisr(NETISR_IP);
@@ -513,21 +700,51 @@ vvrint(unit)
                m_freem(m);
        } else
                IF_ENQUEUE(inq, m);
                m_freem(m);
        } else
                IF_ENQUEUE(inq, m);
-
 setup:
        /*
 setup:
        /*
-        * Restart the read for next packet.
+        * Check the error rate and start recovery if needed
+        * this has to go here since the timer flywheel runs at
+        * a lower ipl and never gets a chance to change the mode
         */
         */
-       ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
-       addr->vviba = (u_short) ubainfo;
-       addr->vviea = (u_short) (ubainfo >> 16);
-       addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
-       addr->vvicsr = VV_RST | VV_CONF;
-       addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
-       return;
+       if (vs->vs_major == MODE0 && vs->vs_dropped > VV_ERRORTHRESHOLD) {
+               vvtrprintf("vv%d going MODE1 in vvrint\n",unit);
+               vs->vs_major = MODE1;
+               vs->vs_iactive = PAUSE;         /* no new reads */
+               vs->vs_retry = VV_MODE1ATTEMPTS;
+               vs->vs_delayclock = VV_MODE1DELAY;
+               vs->vs_minor = 0;
+               vs->vs_dropped = 0;
+       }
+       switch (vs->vs_iactive) {
+
+       case ACTIVE:            /* Restart the read for next packet */
+               ubainfo = vs->vs_ifuba.ifu_r.ifrw_info;
+               addr->vviba = (u_short) ubainfo;
+               addr->vviea = (u_short) (ubainfo >> 16);
+               addr->vviwc = -(sizeof (struct vv_header) + VVMTU) >> 1;
+               addr->vvicsr = VV_RST | VV_CONF;
+               addr->vvicsr |= VV_IEN | VV_DEN | VV_ENB;
+               return;
+
+       case PAUSE:             /* requested to not start any new reads */
+               vs->vs_dropped = 0;
+               return;
 
 
+       case OPEN:              /* request to open host relay */
+               vs->vs_dropped = 0;
+               addr->vvicsr = 0;
+               return;
+
+       default:
+               printf("vv%d: vs_iactive = %d\n", unit, vs->vs_iactive);
+               return;
+       }
+       /*
+        * Drop packet on floor -- count them!!
+        */
 dropit:
        vs->vs_if.if_ierrors++;
 dropit:
        vs->vs_if.if_ierrors++;
+       vs->vs_dropped++;
        /*
        printf("vv%d: error vvicsr = %b\n", unit,
                0xffff&(addr->vvicsr), VV_IBITS);
        /*
        printf("vv%d: error vvicsr = %b\n", unit,
                0xffff&(addr->vvicsr), VV_IBITS);
@@ -552,14 +769,14 @@ vvoutput(ifp, m0, dst)
        int type, dest, s, error;
 
        switch (dst->sa_family) {
        int type, dest, s, error;
 
        switch (dst->sa_family) {
+
 #ifdef INET
        case AF_INET: {
                dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
 #ifdef INET
        case AF_INET: {
                dest = ((struct sockaddr_in *)dst)->sin_addr.s_addr;
-               if (dest & 0x00ffff00) {
+               if ((dest = in_lnaof(*((struct in_addr *)&dest))) >= 0x100) {
                        error = EPERM;
                        goto bad;
                }
                        error = EPERM;
                        goto bad;
                }
-               dest = (dest >> 24) & 0xff;
                off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
                if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 &&
                    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
                off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
                if (vv_dotrailer && off > 0 && (off & 0x1ff) == 0 &&
                    m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
@@ -593,7 +810,6 @@ gottrailertype:
        m = m0->m_next;
        m0->m_next = 0;
        m0 = m;
        m = m0->m_next;
        m0->m_next = 0;
        m0 = m;
-
 gottype:
        /*
         * Add local net header.  If no space in first mbuf,
 gottype:
        /*
         * Add local net header.  If no space in first mbuf,
@@ -602,7 +818,7 @@ gottype:
        if (m->m_off > MMAXOFF ||
            MMINOFF + sizeof (struct vv_header) > m->m_off) {
                m = m_get(M_DONTWAIT, MT_HEADER);
        if (m->m_off > MMAXOFF ||
            MMINOFF + sizeof (struct vv_header) > m->m_off) {
                m = m_get(M_DONTWAIT, MT_HEADER);
-               if (m == 0) {
+               if (m == NULL) {
                        error = ENOBUFS;
                        goto bad;
                }
                        error = ENOBUFS;
                        goto bad;
                }
@@ -619,8 +835,7 @@ gottype:
        vv->vh_version = RING_VERSION;
        vv->vh_type = type;
        vv->vh_info = off;
        vv->vh_version = RING_VERSION;
        vv->vh_type = type;
        vv->vh_info = off;
-       if (vv_trace)
-               vvprt_hdr("vo", vv);
+       vvtracehdr("vo", vv);
 
        /*
         * Queue message on interface, and start output if interface
 
        /*
         * Queue message on interface, and start output if interface
@@ -637,7 +852,6 @@ gottype:
                vvstart(ifp->if_unit);
        splx(s);
        return (0);
                vvstart(ifp->if_unit);
        splx(s);
        return (0);
-
 qfull:
        m0 = m;
        splx(s);
 qfull:
        m0 = m;
        splx(s);
@@ -646,6 +860,35 @@ bad:
        return(error);
 }
 
        return(error);
 }
 
+/*
+ * Process an ioctl request.
+ */
+vvioctl(ifp, cmd, data)
+       register struct ifnet *ifp;
+       int cmd;
+       caddr_t data;
+{
+       struct ifreq *ifr = (struct ifreq *)data;
+       int s = splimp(), error = 0;
+
+       switch (cmd) {
+
+       case SIOCSIFADDR:
+               /* too difficult to change addr while running */
+               if ((ifp->if_flags & IFF_RUNNING) == 0) {
+                       ifp->if_net = in_netof(ifr->ifr_addr.sin_addr);
+                       vvinit(ifp->if_unit);
+               } else
+                       error = EINVAL;
+               break;
+
+       default:
+               error = EINVAL;
+       }
+       splx(s);
+       return (error);
+}
+
 /*
  * vvprt_hdr(s, v) print the local net header in "v"
  *     with title is "s"
 /*
  * vvprt_hdr(s, v) print the local net header in "v"
  *     with title is "s"
@@ -661,6 +904,7 @@ vvprt_hdr(s, v)
                0xffff & (int)(v->vh_info));
 }
 
                0xffff & (int)(v->vh_info));
 }
 
+#ifdef notdef
 /*
  * print "l" hex bytes starting at "s"
  */
 /*
  * print "l" hex bytes starting at "s"
  */