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.
This commit is contained in:
harti 2003-07-14 15:06:53 +00:00
parent 4e3c77a852
commit 0bfe551a79
2 changed files with 280 additions and 4 deletions

View File

@ -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();

View File

@ -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)))