diff --git a/sys/dev/utopia/utopia.c b/sys/dev/utopia/utopia.c index d8d86773f48f..9cf412a253a7 100644 --- a/sys/dev/utopia/utopia.c +++ b/sys/dev/utopia/utopia.c @@ -76,6 +76,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. */ @@ -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 1abe99bb4590..ae044f98f08d 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 @@ -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)))