summaryrefslogtreecommitdiffstats
path: root/sys/dev/re
diff options
context:
space:
mode:
authoryongari <yongari@FreeBSD.org>2010-11-05 19:28:00 +0000
committeryongari <yongari@FreeBSD.org>2010-11-05 19:28:00 +0000
commit3f7fdc49477b1af5af349aa052cb0711a96f8542 (patch)
treee60c0c509fe8161b56ab14cda21272f5ad5addbb /sys/dev/re
parent6a88b24ee2d7ca5016b91d9d019359d50e6924df (diff)
downloadFreeBSD-src-3f7fdc49477b1af5af349aa052cb0711a96f8542.zip
FreeBSD-src-3f7fdc49477b1af5af349aa052cb0711a96f8542.tar.gz
Add simple MAC statistics counter reading support. Unfortunately
useful counters like rl_missed_pkts is 16 bits quantity which is too small to hold meaningful information happened in a second. This means driver should frequently read these counters in order not to lose accuracy and that approach is too inefficient in driver's view. Moreover it seems there is no way to trigger an interrupt to detect counter near-full or wraparound event as well as lacking clearing the MAC counters. Another limitation of reading the counters from RealTek controllers is lack of interrupt firing at the end of DMA cycle of MAC counter read request such that driver have to poll the end of the DMA which is a time consuming process as well as inefficient. The more severe issue of the MAC counter read request is it takes too long to complete the DMA. All these limitation made maintaining MAC counters in driver impractical. For now, just provide simple sysctl interface to trigger reading the MAC counters. These counters could be used to track down driver issues. Users can read MAC counters maintained in controller with the following command. #sysctl dev.re.0.stats=1 While I'm here add check for validity of dma map and allocated memory before unloading/freeing them. Tested by: rmacklem
Diffstat (limited to 'sys/dev/re')
-rw-r--r--sys/dev/re/if_re.c154
1 files changed, 139 insertions, 15 deletions
diff --git a/sys/dev/re/if_re.c b/sys/dev/re/if_re.c
index 0f8baa9..95983af 100644
--- a/sys/dev/re/if_re.c
+++ b/sys/dev/re/if_re.c
@@ -123,6 +123,7 @@ __FBSDID("$FreeBSD$");
#include <sys/socket.h>
#include <sys/lock.h>
#include <sys/mutex.h>
+#include <sys/sysctl.h>
#include <sys/taskqueue.h>
#include <net/if.h>
@@ -281,6 +282,9 @@ static void re_clrwol (struct rl_softc *);
static int re_diag (struct rl_softc *);
#endif
+static void re_add_sysctls (struct rl_softc *);
+static int re_sysctl_stats (SYSCTL_HANDLER_ARGS);
+
static device_method_t re_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, re_probe),
@@ -1084,6 +1088,35 @@ re_allocmem(device_t dev, struct rl_softc *sc)
}
}
+ /* Create DMA map for statistics. */
+ error = bus_dma_tag_create(sc->rl_parent_tag, RL_DUMP_ALIGN, 0,
+ BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
+ sizeof(struct rl_stats), 1, sizeof(struct rl_stats), 0, NULL, NULL,
+ &sc->rl_ldata.rl_stag);
+ if (error) {
+ device_printf(dev, "could not create statistics DMA tag\n");
+ return (error);
+ }
+ /* Allocate DMA'able memory for statistics. */
+ error = bus_dmamem_alloc(sc->rl_ldata.rl_stag,
+ (void **)&sc->rl_ldata.rl_stats,
+ BUS_DMA_WAITOK | BUS_DMA_COHERENT | BUS_DMA_ZERO,
+ &sc->rl_ldata.rl_smap);
+ if (error) {
+ device_printf(dev,
+ "could not allocate statistics DMA memory\n");
+ return (error);
+ }
+ /* Load the map for statistics. */
+ sc->rl_ldata.rl_stats_addr = 0;
+ error = bus_dmamap_load(sc->rl_ldata.rl_stag, sc->rl_ldata.rl_smap,
+ sc->rl_ldata.rl_stats, sizeof(struct rl_stats), re_dma_map_addr,
+ &sc->rl_ldata.rl_stats_addr, BUS_DMA_NOWAIT);
+ if (error != 0 || sc->rl_ldata.rl_stats_addr == 0) {
+ device_printf(dev, "could not load statistics DMA memory\n");
+ return (ENOMEM);
+ }
+
return (0);
}
@@ -1374,6 +1407,7 @@ re_attach(device_t dev)
error = re_allocmem(dev, sc);
if (error)
goto fail;
+ re_add_sysctls(sc);
ifp = sc->rl_ifp = if_alloc(IFT_ETHER);
if (ifp == NULL) {
@@ -1603,22 +1637,26 @@ re_detach(device_t dev)
/* Unload and free the RX DMA ring memory and map */
if (sc->rl_ldata.rl_rx_list_tag) {
- bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag,
- sc->rl_ldata.rl_rx_list_map);
- bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag,
- sc->rl_ldata.rl_rx_list,
- sc->rl_ldata.rl_rx_list_map);
+ if (sc->rl_ldata.rl_rx_list_map)
+ bus_dmamap_unload(sc->rl_ldata.rl_rx_list_tag,
+ sc->rl_ldata.rl_rx_list_map);
+ if (sc->rl_ldata.rl_rx_list_map && sc->rl_ldata.rl_rx_list)
+ bus_dmamem_free(sc->rl_ldata.rl_rx_list_tag,
+ sc->rl_ldata.rl_rx_list,
+ sc->rl_ldata.rl_rx_list_map);
bus_dma_tag_destroy(sc->rl_ldata.rl_rx_list_tag);
}
/* Unload and free the TX DMA ring memory and map */
if (sc->rl_ldata.rl_tx_list_tag) {
- bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag,
- sc->rl_ldata.rl_tx_list_map);
- bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag,
- sc->rl_ldata.rl_tx_list,
- sc->rl_ldata.rl_tx_list_map);
+ if (sc->rl_ldata.rl_tx_list_map)
+ bus_dmamap_unload(sc->rl_ldata.rl_tx_list_tag,
+ sc->rl_ldata.rl_tx_list_map);
+ if (sc->rl_ldata.rl_tx_list_map && sc->rl_ldata.rl_tx_list)
+ bus_dmamem_free(sc->rl_ldata.rl_tx_list_tag,
+ sc->rl_ldata.rl_tx_list,
+ sc->rl_ldata.rl_tx_list_map);
bus_dma_tag_destroy(sc->rl_ldata.rl_tx_list_tag);
}
@@ -1643,11 +1681,12 @@ re_detach(device_t dev)
/* Unload and free the stats buffer and map */
if (sc->rl_ldata.rl_stag) {
- bus_dmamap_unload(sc->rl_ldata.rl_stag,
- sc->rl_ldata.rl_rx_list_map);
- bus_dmamem_free(sc->rl_ldata.rl_stag,
- sc->rl_ldata.rl_stats,
- sc->rl_ldata.rl_smap);
+ if (sc->rl_ldata.rl_smap)
+ bus_dmamap_unload(sc->rl_ldata.rl_stag,
+ sc->rl_ldata.rl_smap);
+ if (sc->rl_ldata.rl_smap && sc->rl_ldata.rl_stats)
+ bus_dmamem_free(sc->rl_ldata.rl_stag,
+ sc->rl_ldata.rl_stats, sc->rl_ldata.rl_smap);
bus_dma_tag_destroy(sc->rl_ldata.rl_stag);
}
@@ -3185,3 +3224,88 @@ re_clrwol(struct rl_softc *sc)
v &= ~RL_CFG5_WOL_LANWAKE;
CSR_WRITE_1(sc, RL_CFG5, v);
}
+
+static void
+re_add_sysctls(struct rl_softc *sc)
+{
+ struct sysctl_ctx_list *ctx;
+ struct sysctl_oid_list *children;
+
+ ctx = device_get_sysctl_ctx(sc->rl_dev);
+ children = SYSCTL_CHILDREN(device_get_sysctl_tree(sc->rl_dev));
+
+ SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "stats",
+ CTLTYPE_INT | CTLFLAG_RW, sc, 0, re_sysctl_stats, "I",
+ "Statistics Information");
+}
+
+static int
+re_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct rl_softc *sc;
+ struct rl_stats *stats;
+ int error, i, result;
+
+ result = -1;
+ error = sysctl_handle_int(oidp, &result, 0, req);
+ if (error || req->newptr == NULL)
+ return (error);
+
+ if (result == 1) {
+ sc = (struct rl_softc *)arg1;
+ RL_LOCK(sc);
+ bus_dmamap_sync(sc->rl_ldata.rl_stag,
+ sc->rl_ldata.rl_smap, BUS_DMASYNC_PREREAD);
+ CSR_WRITE_4(sc, RL_DUMPSTATS_HI,
+ RL_ADDR_HI(sc->rl_ldata.rl_stats_addr));
+ CSR_WRITE_4(sc, RL_DUMPSTATS_LO,
+ RL_ADDR_LO(sc->rl_ldata.rl_stats_addr));
+ CSR_WRITE_4(sc, RL_DUMPSTATS_LO,
+ RL_ADDR_LO(sc->rl_ldata.rl_stats_addr |
+ RL_DUMPSTATS_START));
+ for (i = RL_TIMEOUT; i > 0; i--) {
+ if ((CSR_READ_4(sc, RL_DUMPSTATS_LO) &
+ RL_DUMPSTATS_START) == 0)
+ break;
+ DELAY(1000);
+ }
+ bus_dmamap_sync(sc->rl_ldata.rl_stag,
+ sc->rl_ldata.rl_smap, BUS_DMASYNC_POSTREAD);
+ RL_UNLOCK(sc);
+ if (i == 0) {
+ device_printf(sc->rl_dev,
+ "DUMP statistics request timedout\n");
+ return (ETIMEDOUT);
+ }
+ stats = sc->rl_ldata.rl_stats;
+ printf("%s statistics:\n", device_get_nameunit(sc->rl_dev));
+ printf("Tx frames : %ju\n",
+ (uintmax_t)le64toh(stats->rl_tx_pkts));
+ printf("Rx frames : %ju\n",
+ (uintmax_t)le64toh(stats->rl_rx_pkts));
+ printf("Tx errors : %ju\n",
+ (uintmax_t)le64toh(stats->rl_tx_errs));
+ printf("Rx errors : %u\n",
+ le32toh(stats->rl_rx_errs));
+ printf("Rx missed frames : %u\n",
+ (uint32_t)le16toh(stats->rl_missed_pkts));
+ printf("Rx frame alignment errs : %u\n",
+ (uint32_t)le16toh(stats->rl_rx_framealign_errs));
+ printf("Tx single collisions : %u\n",
+ le32toh(stats->rl_tx_onecoll));
+ printf("Tx multiple collisions : %u\n",
+ le32toh(stats->rl_tx_multicolls));
+ printf("Rx unicast frames : %ju\n",
+ (uintmax_t)le64toh(stats->rl_rx_ucasts));
+ printf("Rx broadcast frames : %ju\n",
+ (uintmax_t)le64toh(stats->rl_rx_bcasts));
+ printf("Rx multicast frames : %u\n",
+ le32toh(stats->rl_rx_mcasts));
+ printf("Tx aborts : %u\n",
+ (uint32_t)le16toh(stats->rl_tx_aborts));
+ printf("Tx underruns : %u\n",
+ (uint32_t)le16toh(stats->rl_rx_underruns));
+ }
+
+ return (error);
+}
OpenPOWER on IntegriCloud