I have noticed, for a while, that my O2 systems were horribly slow during installs or upgrades, when fetching sets from the network (28 *minutes* to fetch base64.tgz).
At first, I thought this was a bsd.rd specific bug, but couldn't find anything obvious. After gathering enough data, I found out that the problem only occurs on a cold boot. After a reboot, the network performance is as good as it can be. That would explain why I would only notice it during upgrades. I also noticed that, on a warm boot, the dmesg would show: mec0 at macebus0 base 0x00280000 irq 3: MAC-110 rev 1, address 08:00:69:0e:bf:a1 nsphy0 at mec0 phy 8: DP83840 10/100 PHY, rev. 1 but on cold boots, it would show: mec0 at macebus0 base 0x00280000 irq 3: MAC-110 rev 1, address 08:00:69:0e:bf:a1 nsphy0 at mec0 phy 10: DP83840 10/100 PHY, rev. 1 Note that, in these cases, the phy seems to attach to a different address. In these cases, after booting, "ifconfig mec0" would show: mec0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500 lladdr 08:00:69:0e:bf:a1 llprio 3 media: Ethernet autoselect status: active inet 10.0.1.193 netmask 0xff000000 broadcast 10.255.255.255 while one would expect the "media" line to be similar to: media: Ethernet autoselect (100baseTX full-duplex) Investigating further, it seems that, after a cold boot, the MII bus takes some time to initialize; the phy does not answer to address 8 but to a larger address (10 or 11), then, after being reset, to its correct address of 8. So the kernel would discover the phy at a wrong address, attach it, and after it gets reset, reading from the phy at the wrong address would return either all bits clear or all bits set, confusing the link speed logic without any way to recover. What I tried but did not work: - invoking mii_attach() twice in the mec driver. This would attach nsphy twice, once at the wrong address, then once at the correct address, but the first (wrong) attachment would be preferred. - adding a one second delay between the Ethernet interface reset and mii_attach(). This would work most of the time, but not always. What I tried and works: - the first time the interface is reset, the mii bus is walked and all phys found on it are reset. Thus, by the time mii_attach() runs and walks the bus again, the phy will answer at the right address. The diff below implements this (last chunk of if_mec.c), and also cleans the mii read/write routines a bit (all the other chunks). Tested on three different R5K family O2 systems, which have all been exposing that problem on cold boot. Index: dev/if_mec.c =================================================================== RCS file: /OpenBSD/src/sys/arch/sgi/dev/if_mec.c,v retrieving revision 1.37 diff -u -p -r1.37 if_mec.c --- dev/if_mec.c 22 Jan 2017 10:17:37 -0000 1.37 +++ dev/if_mec.c 8 Dec 2018 15:00:04 -0000 @@ -264,7 +264,6 @@ struct mec_softc { bus_dma_tag_t sc_dmat; /* bus_dma tag. */ struct mii_data sc_mii; /* MII/media information. */ - int sc_phyaddr; /* MII address. */ struct timeout sc_tick_ch; /* Tick timeout. */ bus_dmamap_t sc_cddmamap; /* bus_dma map for control data. */ @@ -329,7 +328,7 @@ void mec_start(struct ifnet *); void mec_watchdog(struct ifnet *); void mec_tick(void *); int mec_ioctl(struct ifnet *, u_long, caddr_t); -void mec_reset(struct mec_softc *); +void mec_reset(struct mec_softc *, int); void mec_iff(struct mec_softc *); int mec_intr(void *arg); void mec_stop(struct ifnet *); @@ -422,7 +421,7 @@ mec_attach(struct device *parent, struct enaddr_aton(bios_enaddr, sc->sc_ac.ac_enaddr); /* Reset device. */ - mec_reset(sc); + mec_reset(sc, 1); command = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_MAC_CONTROL); @@ -451,7 +450,6 @@ mec_attach(struct device *parent, struct ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_MANUAL); } else { ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER | IFM_AUTO); - sc->sc_phyaddr = child->mii_phy; } bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); @@ -499,21 +497,19 @@ mec_mii_readreg(struct device *self, int struct mec_softc *sc = (void *)self; bus_space_tag_t st = sc->sc_st; bus_space_handle_t sh = sc->sc_sh; - uint32_t val; + uint64_t val; int i; if (mec_mii_wait(sc) != 0) return 0; - bus_space_write_4(st, sh, MEC_PHY_ADDRESS, + bus_space_write_8(st, sh, MEC_PHY_ADDRESS, (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER)); bus_space_write_8(st, sh, MEC_PHY_READ_INITIATE, 1); - delay(25); for (i = 0; i < 20; i++) { - delay(30); - - val = bus_space_read_4(st, sh, MEC_PHY_DATA); + delay(25); + val = bus_space_read_8(st, sh, MEC_PHY_DATA); if ((val & MEC_PHY_DATA_BUSY) == 0) return val & MEC_PHY_DATA_VALUE; @@ -529,18 +525,13 @@ mec_mii_writereg(struct device *self, in bus_space_handle_t sh = sc->sc_sh; if (mec_mii_wait(sc) != 0) { - printf("timed out writing %x: %x\n", reg, val); + printf("MII timed out writing %x: %x\n", reg, val); return; } - bus_space_write_4(st, sh, MEC_PHY_ADDRESS, + bus_space_write_8(st, sh, MEC_PHY_ADDRESS, (phy << MEC_PHY_ADDR_DEVSHIFT) | (reg & MEC_PHY_ADDR_REGISTER)); - - delay(60); - - bus_space_write_4(st, sh, MEC_PHY_DATA, val & MEC_PHY_DATA_VALUE); - - delay(60); + bus_space_write_8(st, sh, MEC_PHY_DATA, val & MEC_PHY_DATA_VALUE); mec_mii_wait(sc); } @@ -548,20 +539,14 @@ mec_mii_writereg(struct device *self, in int mec_mii_wait(struct mec_softc *sc) { - uint32_t busy; - int i, s; + uint64_t busy; + int i; for (i = 0; i < 100; i++) { - delay(30); - - s = splhigh(); - busy = bus_space_read_4(sc->sc_st, sc->sc_sh, MEC_PHY_DATA); - splx(s); - + busy = bus_space_read_8(sc->sc_st, sc->sc_sh, MEC_PHY_DATA); if ((busy & MEC_PHY_DATA_BUSY) == 0) return 0; - if (busy == 0xffff) /* XXX ? */ - return 0; + delay(30); } printf("%s: MII timed out\n", sc->sc_dev.dv_xname); @@ -628,7 +613,7 @@ mec_init(struct ifnet *ifp) mec_stop(ifp); /* Reset device. */ - mec_reset(sc); + mec_reset(sc, 0); /* Setup filter for multicast or promisc mode. */ mec_iff(sc); @@ -676,7 +661,7 @@ mec_init(struct ifnet *ifp) } void -mec_reset(struct mec_softc *sc) +mec_reset(struct mec_softc *sc, int firsttime) { bus_space_tag_t st = sc->sc_st; bus_space_handle_t sh = sc->sc_sh; @@ -705,6 +690,41 @@ mec_reset(struct mec_softc *sc) DPRINTF(MEC_DEBUG_RESET, ("mec: control now %llx\n", bus_space_read_8(st, sh, MEC_MAC_CONTROL))); + + if (firsttime) { + /* + * After a cold boot, during the initial MII probe, the + * PHY would sometimes answer to addresses 11 or 10, only + * to settle to address 8 shortly after. + * + * Because of this, the PHY driver would attach to the wrong + * address and further link configuration would fail (with PHY + * register reads returning either 0 or FFFF), leading to + * horrible performance and no way to select a proper media. + */ + int i, reg, phyno; + for (phyno = 0; phyno < MII_NPHY; phyno++) { + reg = mec_mii_readreg(&sc->sc_dev, phyno, MII_BMSR); + /* same logic as in mii_attach() */ + if (reg == 0 || reg == 0xffff || + (reg & (BMSR_MEDIAMASK | BMSR_EXTSTAT)) == 0) + continue; + /* inline mii_phy_reset() */ + mec_mii_writereg(&sc->sc_dev, phyno, MII_BMCR, + BMCR_RESET | BMCR_ISO); + delay(500); + for (i = 0; i < 100; i++) { + reg = mec_mii_readreg(&sc->sc_dev, phyno, + MII_BMCR); + if ((reg & BMCR_RESET) == 0) { + mec_mii_writereg(&sc->sc_dev, phyno, + MII_BMCR, reg | BMCR_ISO); + break; + } + delay(1000); + } + } + } } void Index: dev/if_mecreg.h =================================================================== RCS file: /OpenBSD/src/sys/arch/sgi/dev/if_mecreg.h,v retrieving revision 1.2 diff -u -p -r1.2 if_mecreg.h --- dev/if_mecreg.h 31 Jul 2007 19:10:22 -0000 1.2 +++ dev/if_mecreg.h 8 Dec 2018 15:00:04 -0000 @@ -117,13 +117,11 @@ #define MEC_TX_VECTOR 0x58 #define MEC_IRQ_VECTOR 0x58 -#define MEC_PHY_DATA_PAD 0x60 /* XXX ? */ -#define MEC_PHY_DATA 0x64 +#define MEC_PHY_DATA 0x60 #define MEC_PHY_DATA_BUSY 0x00010000 #define MEC_PHY_DATA_VALUE 0x0000ffff -#define MEC_PHY_ADDRESS_PAD 0x68 /* XXX ? */ -#define MEC_PHY_ADDRESS 0x6c +#define MEC_PHY_ADDRESS 0x68 #define MEC_PHY_ADDR_REGISTER 0x0000001f #define MEC_PHY_ADDR_DEVICE 0x000003e0 #define MEC_PHY_ADDR_DEVSHIFT 5