Author: adrian
Date: Tue Mar 13 06:28:52 2012
New Revision: 232914
URL: http://svn.freebsd.org/changeset/base/232914

Log:
  Fix link status handling on if_arge upon system boot to allow bootp/NFS to
  function.
  
  From the submitter:
  
  This patch fixes an issue I encountered using an NFS root with an
  ar71xx-based MikroTik RouterBoard 450G on -current where the kernel fails
  to contact a DHCP/BOOTP server via if_arge when it otherwise should be able
  to.  This may be the same issue that Monthadar Al Jaberi reported against
  an RSPRO on 6 March, as the signature is the same:
  
  %%%
  
  DHCP/BOOTP timeout for server 255.255.255.255
  DHCP/BOOTP timeout for server 255.255.255.255
  DHCP/BOOTP timeout for server 255.255.255.255
  .
  .
  .
  DHCP/BOOTP timeout for server 255.255.255.255
  DHCP/BOOTP timeout for server 255.255.255.255
  arge0: initialization failed: no memory for rx buffers
  DHCP/BOOTP timeout for server 255.255.255.255
  arge0: initialization failed: no memory for rx buffers
  
  %%%
  
  The primary issue that I found is that the DHCP/BOOTP message that
  bootpc_call() is sending never makes it onto the wire, which I believe is
  due to the following:
  
  - Last December, a change was made to the ifioctl that bootpc_call() uses
  to adjust the netmask around the sosend().
  
  - The new ioctl (SIOCAIFADDR) performs an if_init when invoked, whereas the
  old one (SIOCSIFNETMASK) did not.
  
  - if_arge maintains its own sense of link state in sc->arge_link_status.
  
  - On a single-phy interface, sc->arge_link_status is initialized to 0 in
  arge_init_locked().
  
  - sc->arge_link_status remains 0 until a phy state change notification
  causes arge_link_task to run, notice the link is up, and set it to 1.
  
  - The inits caused by the ifioctls in bootpc_call are reinitializing the
  interface, but not the phy, so sc->arge_link_status goes to 0 and remains
  there.
  
  - arge_start_locked() always sees sc->arge_link_status == 0 and returns
  without queuing anything.
  
  The attached patch changes arge_init_locked() such that in the single-phy
  case, instead of initializing sc->arge_link_status to 0, it runs
  arge_link_task() to set it according to the current phy state.  This change
  has allowed my setup to mount an NFS root successfully.
  
  Submitted by: Patrick Kelsey <kel...@ieee.org>
  Reviewed by:  juli

Modified:
  head/sys/mips/atheros/if_arge.c

Modified: head/sys/mips/atheros/if_arge.c
==============================================================================
--- head/sys/mips/atheros/if_arge.c     Tue Mar 13 06:22:49 2012        
(r232913)
+++ head/sys/mips/atheros/if_arge.c     Tue Mar 13 06:28:52 2012        
(r232914)
@@ -110,6 +110,7 @@ static int arge_ioctl(struct ifnet *, u_
 static void arge_init(void *);
 static void arge_init_locked(struct arge_softc *);
 static void arge_link_task(void *, int);
+static void arge_update_link_locked(struct arge_softc *sc);
 static void arge_set_pll(struct arge_softc *, int, int);
 static int arge_miibus_readreg(device_t, int, int);
 static void arge_miibus_statchg(device_t);
@@ -684,13 +685,20 @@ static void
 arge_link_task(void *arg, int pending)
 {
        struct arge_softc       *sc;
+       sc = (struct arge_softc *)arg;
+
+       ARGE_LOCK(sc);
+       arge_update_link_locked(sc);
+       ARGE_UNLOCK(sc);
+}
+
+static void
+arge_update_link_locked(struct arge_softc *sc)
+{
        struct mii_data         *mii;
        struct ifnet            *ifp;
        uint32_t                media, duplex;
 
-       sc = (struct arge_softc *)arg;
-
-       ARGE_LOCK(sc);
        mii = device_get_softc(sc->arge_miibus);
        ifp = sc->arge_ifp;
        if (mii == NULL || ifp == NULL ||
@@ -708,10 +716,10 @@ arge_link_task(void *arg, int pending)
                        duplex = mii->mii_media_active & IFM_GMASK;
                        arge_set_pll(sc, media, duplex);
                }
-       } else
+       } else {
                sc->arge_link_status = 0;
+       }
 
-       ARGE_UNLOCK(sc);
 }
 
 static void
@@ -853,7 +861,6 @@ arge_init_locked(struct arge_softc *sc)
 
 
        if (sc->arge_miibus) {
-               sc->arge_link_status = 0;
                mii = device_get_softc(sc->arge_miibus);
                mii_mediachg(mii);
        }
@@ -867,8 +874,10 @@ arge_init_locked(struct arge_softc *sc)
        ifp->if_drv_flags |= IFF_DRV_RUNNING;
        ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
 
-       if (sc->arge_miibus)
+       if (sc->arge_miibus) {
                callout_reset(&sc->arge_stat_callout, hz, arge_tick, sc);
+               arge_update_link_locked(sc);
+       }
 
        ARGE_WRITE(sc, AR71XX_DMA_TX_DESC, ARGE_TX_RING_ADDR(sc, 0));
        ARGE_WRITE(sc, AR71XX_DMA_RX_DESC, ARGE_RX_RING_ADDR(sc, 0));
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to