summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorharti <harti@FreeBSD.org>2003-07-14 15:06:53 +0000
committerharti <harti@FreeBSD.org>2003-07-14 15:06:53 +0000
commit0bfe551a79aad7e1b7edd92af15b87ee2b00fd13 (patch)
treecdfdfe42636b74a28047ca23e94050dceb8b456b /sys
parent4e3c77a852c18b9278f3f22b8c605dc4a530468d (diff)
downloadFreeBSD-src-0bfe551a79aad7e1b7edd92af15b87ee2b00fd13.zip
FreeBSD-src-0bfe551a79aad7e1b7edd92af15b87ee2b00fd13.tar.gz
Implement statistics for the PHY chips. Statistics are hold in
64-bit counters that wrap on overflow. They are collecte once per second from the chips. Currently they can be retrieved via a sysctl phy_stats. A write of an arbitrary value to the sysctl atomically retrieves the statistics and clears them.
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/utopia/utopia.c259
-rw-r--r--sys/dev/utopia/utopia.h25
2 files changed, 280 insertions, 4 deletions
diff --git a/sys/dev/utopia/utopia.c b/sys/dev/utopia/utopia.c
index d8d8677..9cf412a 100644
--- a/sys/dev/utopia/utopia.c
+++ b/sys/dev/utopia/utopia.c
@@ -77,6 +77,47 @@ static struct proc *utopia_kproc;
static void utopia_dump(struct utopia *) __unused;
/*
+ * Statistics update inlines
+ */
+static uint32_t
+utp_update(struct utopia *utp, u_int reg, u_int nreg, uint32_t mask)
+{
+ int err;
+ u_int n;
+ uint8_t regs[4];
+ uint32_t val;
+
+ n = nreg;
+ if ((err = READREGS(utp, reg, regs, &n)) != 0) {
+#ifdef DIAGNOSTIC
+ printf("%s: register read error %s(%u,%u): %d\n", __func__,
+ utp->chip->name, reg, nreg, err);
+#endif
+ return (0);
+ }
+ if (n < nreg) {
+#ifdef DIAGNOSTIC
+ printf("%s: got only %u regs %s(%u,%u): %d\n", __func__, n,
+ utp->chip->name, reg, nreg, err);
+#endif
+ return (0);
+ }
+ val = 0;
+ for (n = nreg; n > 0; n--) {
+ val <<= 8;
+ val |= regs[n - 1];
+ }
+ return (val & mask);
+}
+
+#define UPDATE8(UTP, REG) utp_update(UTP, REG, 1, 0xff)
+#define UPDATE12(UTP, REG) utp_update(UTP, REG, 2, 0xfff)
+#define UPDATE16(UTP, REG) utp_update(UTP, REG, 2, 0xffff)
+#define UPDATE19(UTP, REG) utp_update(UTP, REG, 3, 0x7ffff)
+#define UPDATE20(UTP, REG) utp_update(UTP, REG, 3, 0xfffff)
+#define UPDATE21(UTP, REG) utp_update(UTP, REG, 3, 0x1fffff)
+
+/*
* Debugging - dump all registers.
*/
static void
@@ -473,6 +514,85 @@ utopia_intr_default(struct utopia *utp)
& SUNI_REGM_RSOPSIS_LOSV));
}
+/*
+ * Update statistics from a SUNI/LITE or SUNI/ULTRA
+ */
+static void
+suni_lite_update_stats(struct utopia *utp)
+{
+ int err;
+
+ /* write to the master if we can */
+ if (!(utp->flags & UTP_FL_NORESET)) {
+ err = WRITEREG(utp, SUNI_REGO_MRESET, 0, 0);
+ } else {
+ err = WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0);
+
+ }
+ if (err) {
+#ifdef DIAGNOSTIC
+ printf("%s: register write error %s: %d\n", __func__,
+ utp->chip->name, err);
+#endif
+ return;
+ }
+
+ DELAY(8);
+
+ utp->stats.rx_sbip += UPDATE16(utp, SUNI_REGO_RSOP_BIP8);
+ utp->stats.rx_lbip += UPDATE20(utp, SUNI_REGO_RLOPBIP8_24);
+ utp->stats.rx_lfebe += UPDATE20(utp, SUNI_REGO_RLOPFEBE);
+ utp->stats.rx_pbip += UPDATE16(utp, SUNI_REGO_RPOPBIP8);
+ utp->stats.rx_pfebe += UPDATE16(utp, SUNI_REGO_RPOPFEBE);
+ utp->stats.rx_corr += UPDATE8(utp, SUNI_REGO_RACPCHCS);
+ utp->stats.rx_uncorr += UPDATE8(utp, SUNI_REGO_RACPUHCS);
+ utp->stats.rx_cells += UPDATE19(utp, SUNI_REGO_RACPCNT);
+ utp->stats.tx_cells += UPDATE19(utp, SUNI_REGO_TACPCNT);
+}
+
+/*
+ * Update statistics from a SUNI/622
+ */
+static void
+suni_622_update_stats(struct utopia *utp)
+{
+ int err;
+
+ /* write to the master if we can */
+ if (!(utp->flags & UTP_FL_NORESET)) {
+ err = WRITEREG(utp, SUNI_REGO_MRESET, 0, 0);
+ } else {
+ err = WRITEREG(utp, SUNI_REGO_RSOP_BIP8, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_RLOPBIP8_24, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_RPOPBIP8, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_RACPCHCS, 0, 0);
+ err |= WRITEREG(utp, SUNI_REGO_TACPCNT, 0, 0);
+ }
+ if (err) {
+#ifdef DIAGNOSTIC
+ printf("%s: register write error %s: %d\n", __func__,
+ utp->chip->name, err);
+#endif
+ return;
+ }
+
+ DELAY(8);
+
+ utp->stats.rx_sbip += UPDATE16(utp, SUNI_REGO_RSOP_BIP8);
+ utp->stats.rx_lbip += UPDATE20(utp, SUNI_REGO_RLOPBIP8_24);
+ utp->stats.rx_lfebe += UPDATE20(utp, SUNI_REGO_RLOPFEBE);
+ utp->stats.rx_pbip += UPDATE16(utp, SUNI_REGO_RPOPBIP8);
+ utp->stats.rx_pfebe += UPDATE16(utp, SUNI_REGO_RPOPFEBE);
+ utp->stats.rx_corr += UPDATE12(utp, SUNI_REGO_RACPCHCS_622);
+ utp->stats.rx_uncorr += UPDATE12(utp, SUNI_REGO_RACPUHCS_622);
+ utp->stats.rx_cells += UPDATE21(utp, SUNI_REGO_RACPCNT_622);
+ utp->stats.tx_cells += UPDATE21(utp, SUNI_REGO_TACPCNT);
+}
+
static const struct utopia_chip chip_622 = {
UTP_TYPE_SUNI_622,
"Suni/622 (PMC-5355)",
@@ -484,6 +604,7 @@ static const struct utopia_chip chip_622 = {
utopia_update_carrier_default,
utopia_set_loopback_622,
utopia_intr_default,
+ suni_622_update_stats,
};
static const struct utopia_chip chip_lite = {
UTP_TYPE_SUNI_LITE,
@@ -496,6 +617,7 @@ static const struct utopia_chip chip_lite = {
utopia_update_carrier_default,
utopia_set_loopback_lite,
utopia_intr_default,
+ suni_lite_update_stats,
};
static const struct utopia_chip chip_ultra = {
UTP_TYPE_SUNI_ULTRA,
@@ -508,6 +630,7 @@ static const struct utopia_chip chip_ultra = {
utopia_update_carrier_default,
utopia_set_loopback_ultra,
utopia_intr_default,
+ suni_lite_update_stats,
};
/*
@@ -628,6 +751,49 @@ idt77105_intr(struct utopia *utp)
utopia_check_carrier(utp, reg & IDTPHY_REGM_ISTAT_GOOD);
}
+static void
+idt77105_update_stats(struct utopia *utp)
+{
+ int err = 0;
+ uint8_t regs[2];
+ u_int n;
+
+#ifdef DIAGNOSTIC
+#define UDIAG(F,A,B) printf(F, A, B)
+#else
+#define UDIAG(F,A,B) do { } while (0)
+#endif
+
+#define UPD(FIELD, CODE, N, MASK) \
+ err = WRITEREG(utp, IDTPHY_REGO_CNTS, 0xff, CODE); \
+ if (err != 0) { \
+ UDIAG("%s: cannot write CNTS: %d\n", __func__, err); \
+ return; \
+ } \
+ n = N; \
+ err = READREGS(utp, IDTPHY_REGO_CNT, regs, &n); \
+ if (err != 0) { \
+ UDIAG("%s: cannot read CNT: %d\n", __func__, err); \
+ return; \
+ } \
+ if (n != N) { \
+ UDIAG("%s: got only %u registers\n", __func__, n); \
+ return; \
+ } \
+ if (N == 1) \
+ utp->stats.FIELD += (regs[0] & MASK); \
+ else \
+ utp->stats.FIELD += (regs[0] | (regs[1] << 8)) & MASK;
+
+ UPD(rx_symerr, IDTPHY_REGM_CNTS_SEC, 1, 0xff);
+ UPD(tx_cells, IDTPHY_REGM_CNTS_TX, 2, 0xffff);
+ UPD(rx_cells, IDTPHY_REGM_CNTS_RX, 2, 0xffff);
+ UPD(rx_uncorr, IDTPHY_REGM_CNTS_HECE, 1, 0x1f);
+
+#undef UDIAG
+#undef UPD
+}
+
static const struct utopia_chip chip_idt77105 = {
UTP_TYPE_IDT77105,
"IDT77105",
@@ -639,6 +805,7 @@ static const struct utopia_chip chip_idt77105 = {
idt77105_update_carrier,
idt77105_set_loopback,
idt77105_intr,
+ idt77105_update_stats,
};
/*
@@ -840,6 +1007,49 @@ idt77155_reset(struct utopia *utp)
return (err ? EIO : 0);
}
+/*
+ * Update statistics from a IDT77155
+ * This appears to be the same as for the Suni/Lite and Ultra. IDT however
+ * makes no assessment about the transfer time. Assume 7us.
+ */
+static void
+idt77155_update_stats(struct utopia *utp)
+{
+ int err;
+
+ /* write to the master if we can */
+ if (!(utp->flags & UTP_FL_NORESET)) {
+ err = WRITEREG(utp, IDTPHY_REGO_MRID, 0, 0);
+ } else {
+ err = WRITEREG(utp, IDTPHY_REGO_BIPC, 0, 0);
+ err |= WRITEREG(utp, IDTPHY_REGO_B2EC, 0, 0);
+ err |= WRITEREG(utp, IDTPHY_REGO_B3EC, 0, 0);
+ err |= WRITEREG(utp, IDTPHY_REGO_CEC, 0, 0);
+ err |= WRITEREG(utp, IDTPHY_REGO_TXCNT, 0, 0);
+
+ }
+ if (err) {
+#ifdef DIAGNOSTIC
+ printf("%s: register write error %s: %d\n", __func__,
+ utp->chip->name, err);
+#endif
+ return;
+ }
+
+ DELAY(8);
+
+ utp->stats.rx_sbip += UPDATE16(utp, IDTPHY_REGO_BIPC);
+ utp->stats.rx_lbip += UPDATE20(utp, IDTPHY_REGO_B2EC);
+ utp->stats.rx_lfebe += UPDATE20(utp, IDTPHY_REGO_FEBEC);
+ utp->stats.rx_pbip += UPDATE16(utp, IDTPHY_REGO_B3EC);
+ utp->stats.rx_pfebe += UPDATE16(utp, IDTPHY_REGO_PFEBEC);
+ utp->stats.rx_corr += UPDATE8(utp, IDTPHY_REGO_CEC);
+ utp->stats.rx_uncorr += UPDATE8(utp, IDTPHY_REGO_UEC);
+ utp->stats.rx_cells += UPDATE19(utp, IDTPHY_REGO_RCCNT);
+ utp->stats.tx_cells += UPDATE19(utp, IDTPHY_REGO_TXCNT);
+}
+
+
static const struct utopia_chip chip_idt77155 = {
UTP_TYPE_IDT77155,
"IDT77155",
@@ -851,6 +1061,7 @@ static const struct utopia_chip chip_idt77155 = {
idt77155_update_carrier,
idt77155_set_loopback,
idt77155_intr,
+ idt77155_update_stats,
};
static int
@@ -877,6 +1088,11 @@ unknown_intr(struct utopia *utp __unused)
{
}
+static void
+unknown_update_stats(struct utopia *utp __unused)
+{
+}
+
static const struct utopia_chip chip_unknown = {
UTP_TYPE_UNKNOWN,
"unknown",
@@ -888,6 +1104,7 @@ static const struct utopia_chip chip_unknown = {
unknown_update_carrier,
unknown_set_loopback,
unknown_intr,
+ unknown_update_stats,
};
/*
@@ -1097,6 +1314,33 @@ utopia_sysctl_regs(SYSCTL_HANDLER_ARGS)
return (error);
}
+static int
+utopia_sysctl_stats(SYSCTL_HANDLER_ARGS)
+{
+ struct utopia *utp = (struct utopia *)arg1;
+ void *val;
+ int error;
+
+ val = malloc(sizeof(utp->stats), M_TEMP, M_WAITOK);
+
+ UTP_LOCK(utp);
+ bcopy(&utp->stats, val, sizeof(utp->stats));
+ if (req->newptr != NULL)
+ bzero((char *)&utp->stats + sizeof(utp->stats.version),
+ sizeof(utp->stats) - sizeof(utp->stats.version));
+ UTP_UNLOCK(utp);
+
+ error = SYSCTL_OUT(req, val, sizeof(utp->stats));
+ free(val, M_TEMP);
+
+ if (error && req->newptr != NULL)
+ bcopy(val, &utp->stats, sizeof(utp->stats));
+
+ /* ignore actual new value */
+
+ return (error);
+}
+
/*
* Handle the loopback sysctl
*/
@@ -1160,6 +1404,7 @@ utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
utp->media = media;
utp->lock = lock;
utp->chip = &chip_unknown;
+ utp->stats.version = 1;
ifmedia_init(media,
IFM_ATM_SDH | IFM_ATM_UNASSIGNED | IFM_ATM_NOSCRAMB,
@@ -1185,6 +1430,11 @@ utopia_attach(struct utopia *utp, struct ifatm *ifatm, struct ifmedia *media,
"phy name") == NULL)
return (-1);
+ if (SYSCTL_ADD_PROC(ctx, children, OID_AUTO, "phy_stats",
+ CTLFLAG_RW | CTLTYPE_OPAQUE, utp, 0, utopia_sysctl_stats, "S",
+ "phy statistics") == NULL)
+ return (-1);
+
UTP_WLOCK_LIST();
LIST_INSERT_HEAD(&utopia_list, utp, link);
UTP_WUNLOCK_LIST();
@@ -1237,9 +1487,10 @@ utopia_daemon(void *arg __unused)
LIST_REMOVE(utp, link);
utp->state &= ~UTP_ST_DETACH;
wakeup_one(utp);
- } else if ((utp->state & UTP_ST_ACTIVE) &&
- (utp->flags & UTP_FL_POLL_CARRIER)) {
- utopia_update_carrier(utp);
+ } else if (utp->state & UTP_ST_ACTIVE) {
+ if (utp->flags & UTP_FL_POLL_CARRIER)
+ utopia_update_carrier(utp);
+ utopia_update_stats(utp);
}
UTP_UNLOCK(utp);
mtx_unlock(&Giant); /* XXX depend on MPSAFE */
@@ -1247,7 +1498,7 @@ utopia_daemon(void *arg __unused)
}
UTP_RLOCK_LIST();
- msleep(&utopia_list, &utopia_list_mtx, PZERO, "utopia", hz);
+ msleep(&utopia_list, &utopia_list_mtx, PZERO, "*idle*", hz);
}
wakeup_one(&utopia_list);
UTP_RUNLOCK_LIST();
diff --git a/sys/dev/utopia/utopia.h b/sys/dev/utopia/utopia.h
index 1abe99b..ae044f9 100644
--- a/sys/dev/utopia/utopia.h
+++ b/sys/dev/utopia/utopia.h
@@ -88,6 +88,26 @@ struct utopia_print {
#define UTP_TYPE_IDT77105 4
#define UTP_TYPE_IDT77155 5
+/*
+ * Statistics. These structures are versioned.
+ */
+struct utopia_stats1 {
+ uint32_t version; /* version of this statistics struct */
+ uint32_t fill;
+
+ uint64_t rx_sbip; /* rx section BIP errors */
+ uint64_t rx_lbip; /* rx line BIP errors */
+ uint64_t rx_lfebe; /* rx line far end block errors */
+ uint64_t rx_pbip; /* rx path BIP errors */
+ uint64_t rx_pfebe; /* rx path far end block errors */
+ uint64_t rx_cells; /* received cells */
+ uint64_t rx_corr; /* correctable cell errors */
+ uint64_t rx_uncorr; /* uncorrectable cell errors */
+ uint64_t rx_symerr; /* symbol errors */
+
+ uint64_t tx_cells; /* transmitted cells */
+};
+
#ifdef _KERNEL
#include <sys/queue.h>
@@ -117,6 +137,7 @@ struct utopia {
u_int carrier; /* carrier state */
u_int loopback; /* loopback mode */
const struct utopia_chip *chip; /* chip operations */
+ struct utopia_stats1 stats; /* statistics */
};
struct utopia_chip {
@@ -147,6 +168,9 @@ struct utopia_chip {
/* handle interrupt */
void (*intr)(struct utopia *);
+
+ /* update statistics */
+ void (*update_stats)(struct utopia *);
};
/*
@@ -168,6 +192,7 @@ void utopia_reset_media(struct utopia *);
#define utopia_set_unass(S, U) ((S)->chip->set_unass((S), (U)))
#define utopia_set_noscramb(S, N) ((S)->chip->set_noscramb((S), (N)))
#define utopia_update_carrier(S) ((S)->chip->update_carrier((S)))
+#define utopia_update_stats(S) ((S)->chip->update_stats((S)))
#define utopia_set_loopback(S, L) ((S)->chip->set_loopback((S), (L)))
#define utopia_intr(S) ((S)->chip->intr((S)))
OpenPOWER on IntegriCloud