Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 9 Mar 2012 21:53:13 -0500
From:      Patrick Kelsey <kelsey@ieee.org>
To:        freebsd-mips@freebsd.org
Subject:   [PATCH] Fix for using NFS root with if_arge
Message-ID:  <CAD44qMVKyL-Fdrp-Tf3o9HXMH_UudCSfEOKnHz3nK%2BaqMwW6Pw@mail.gmail.com>

index | next in thread | raw e-mail

[-- Attachment #1 --]
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.

I think there is a secondary issue here regarding the "arge0:
initialization failed: no memory for rx buffers".  I have not dug into it
completely yet, but at first blush it seems that arge_stop() leaks mbufs
from the rx ring, so after some number of arge_init() calls (in this case
triggered by the DHCP/BOOTP message timeouts), all mbufs are exhausted.

-Patrick

[-- Attachment #2 --]
Index: mips/atheros/if_arge.c
===================================================================
--- sys/mips/atheros/if_arge.c	(revision 232725)
+++ sys/mips/atheros/if_arge.c	(working copy)
@@ -110,6 +110,7 @@
 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);
@@ -683,13 +694,20 @@
 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 ||
@@ -707,10 +725,10 @@
 			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
@@ -846,7 +864,6 @@
 
 
 	if (sc->arge_miibus) {
-		sc->arge_link_status = 0;
 		mii = device_get_softc(sc->arge_miibus);
 		mii_mediachg(mii);
 	}
@@ -860,8 +877,10 @@
 	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));
help

Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAD44qMVKyL-Fdrp-Tf3o9HXMH_UudCSfEOKnHz3nK%2BaqMwW6Pw>