Commit 080bb352 authored by Florian Fainelli's avatar Florian Fainelli Committed by David S. Miller

net: phy: Maintain MDIO device and bus statistics

We maintain global statistics for an entire MDIO bus, as well as broken
down, per MDIO bus address statistics. Given that it is possible for
MDIO devices such as switches to access MDIO bus addresses for which
there is not a mdio_device instance created (therefore not a a
corresponding device directory in sysfs either), we also maintain
per-address statistics under the statistics folder. The layout looks
like this:

/sys/class/mdio_bus/../statistics/
	transfers
	errrors
	writes
	reads
	transfers_<addr>
	errors_<addr>
	writes_<addr>
	reads_<addr>

When a mdio_device instance is registered, a statistics/ folder is
created with the tranfers, errors, writes and reads attributes which
point to the appropriate MDIO bus statistics structure.

Statistics are 64-bit unsigned quantities and maintained through the
u64_stats_sync.h helper functions.
Signed-off-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Tested-by: default avatarAndrew Lunn <andrew@lunn.ch>
Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 41cdc741
What: /sys/bus/mdio_bus/devices/.../statistics/
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
This folder contains statistics about global and per
MDIO bus address statistics.
What: /sys/bus/mdio_bus/devices/.../statistics/transfers
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of transfers for this MDIO bus.
What: /sys/bus/mdio_bus/devices/.../statistics/errors
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of transfer errors for this MDIO bus.
What: /sys/bus/mdio_bus/devices/.../statistics/writes
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of write transactions for this MDIO bus.
What: /sys/bus/mdio_bus/devices/.../statistics/reads
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of read transactions for this MDIO bus.
What: /sys/bus/mdio_bus/devices/.../statistics/transfers_<addr>
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of transfers for this MDIO bus address.
What: /sys/bus/mdio_bus/devices/.../statistics/errors_<addr>
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of transfer errors for this MDIO bus address.
What: /sys/bus/mdio_bus/devices/.../statistics/writes_<addr>
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of write transactions for this MDIO bus address.
What: /sys/bus/mdio_bus/devices/.../statistics/reads_<addr>
Date: January 2020
KernelVersion: 5.6
Contact: netdev@vger.kernel.org
Description:
Total number of read transactions for this MDIO bus address.
...@@ -158,9 +158,11 @@ struct mii_bus *mdiobus_alloc_size(size_t size) ...@@ -158,9 +158,11 @@ struct mii_bus *mdiobus_alloc_size(size_t size)
if (size) if (size)
bus->priv = (void *)bus + aligned_size; bus->priv = (void *)bus + aligned_size;
/* Initialise the interrupts to polling */ /* Initialise the interrupts to polling and 64-bit seqcounts */
for (i = 0; i < PHY_MAX_ADDR; i++) for (i = 0; i < PHY_MAX_ADDR; i++) {
bus->irq[i] = PHY_POLL; bus->irq[i] = PHY_POLL;
u64_stats_init(&bus->stats[i].syncp);
}
return bus; return bus;
} }
...@@ -249,9 +251,215 @@ static void mdiobus_release(struct device *d) ...@@ -249,9 +251,215 @@ static void mdiobus_release(struct device *d)
kfree(bus); kfree(bus);
} }
struct mdio_bus_stat_attr {
int addr;
unsigned int field_offset;
};
static u64 mdio_bus_get_stat(struct mdio_bus_stats *s, unsigned int offset)
{
const char *p = (const char *)s + offset;
unsigned int start;
u64 val = 0;
do {
start = u64_stats_fetch_begin(&s->syncp);
val = u64_stats_read((const u64_stats_t *)p);
} while (u64_stats_fetch_retry(&s->syncp, start));
return val;
}
static u64 mdio_bus_get_global_stat(struct mii_bus *bus, unsigned int offset)
{
unsigned int i;
u64 val = 0;
for (i = 0; i < PHY_MAX_ADDR; i++)
val += mdio_bus_get_stat(&bus->stats[i], offset);
return val;
}
static ssize_t mdio_bus_stat_field_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mii_bus *bus = to_mii_bus(dev);
struct mdio_bus_stat_attr *sattr;
struct dev_ext_attribute *eattr;
u64 val;
eattr = container_of(attr, struct dev_ext_attribute, attr);
sattr = eattr->var;
if (sattr->addr < 0)
val = mdio_bus_get_global_stat(bus, sattr->field_offset);
else
val = mdio_bus_get_stat(&bus->stats[sattr->addr],
sattr->field_offset);
return sprintf(buf, "%llu\n", val);
}
static ssize_t mdio_bus_device_stat_field_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct mdio_device *mdiodev = to_mdio_device(dev);
struct mii_bus *bus = mdiodev->bus;
struct mdio_bus_stat_attr *sattr;
struct dev_ext_attribute *eattr;
int addr = mdiodev->addr;
u64 val;
eattr = container_of(attr, struct dev_ext_attribute, attr);
sattr = eattr->var;
val = mdio_bus_get_stat(&bus->stats[addr], sattr->field_offset);
return sprintf(buf, "%llu\n", val);
}
#define MDIO_BUS_STATS_ATTR_DECL(field, file) \
static struct dev_ext_attribute dev_attr_mdio_bus_##field = { \
.attr = { .attr = { .name = file, .mode = 0444 }, \
.show = mdio_bus_stat_field_show, \
}, \
.var = &((struct mdio_bus_stat_attr) { \
-1, offsetof(struct mdio_bus_stats, field) \
}), \
}; \
static struct dev_ext_attribute dev_attr_mdio_bus_device_##field = { \
.attr = { .attr = { .name = file, .mode = 0444 }, \
.show = mdio_bus_device_stat_field_show, \
}, \
.var = &((struct mdio_bus_stat_attr) { \
-1, offsetof(struct mdio_bus_stats, field) \
}), \
};
#define MDIO_BUS_STATS_ATTR(field) \
MDIO_BUS_STATS_ATTR_DECL(field, __stringify(field))
MDIO_BUS_STATS_ATTR(transfers);
MDIO_BUS_STATS_ATTR(errors);
MDIO_BUS_STATS_ATTR(writes);
MDIO_BUS_STATS_ATTR(reads);
#define MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, file) \
static struct dev_ext_attribute dev_attr_mdio_bus_addr_##field##_##addr = { \
.attr = { .attr = { .name = file, .mode = 0444 }, \
.show = mdio_bus_stat_field_show, \
}, \
.var = &((struct mdio_bus_stat_attr) { \
addr, offsetof(struct mdio_bus_stats, field) \
}), \
}
#define MDIO_BUS_STATS_ADDR_ATTR(field, addr) \
MDIO_BUS_STATS_ADDR_ATTR_DECL(field, addr, \
__stringify(field) "_" __stringify(addr))
#define MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(addr) \
MDIO_BUS_STATS_ADDR_ATTR(transfers, addr); \
MDIO_BUS_STATS_ADDR_ATTR(errors, addr); \
MDIO_BUS_STATS_ADDR_ATTR(writes, addr); \
MDIO_BUS_STATS_ADDR_ATTR(reads, addr) \
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(0);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(1);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(2);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(3);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(4);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(5);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(6);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(7);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(8);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(9);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(10);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(11);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(12);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(13);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(14);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(15);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(16);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(17);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(18);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(19);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(20);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(21);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(22);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(23);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(24);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(25);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(26);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(27);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(28);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(29);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(30);
MDIO_BUS_STATS_ADDR_ATTR_GROUP_DECL(31);
#define MDIO_BUS_STATS_ADDR_ATTR_GROUP(addr) \
&dev_attr_mdio_bus_addr_transfers_##addr.attr.attr, \
&dev_attr_mdio_bus_addr_errors_##addr.attr.attr, \
&dev_attr_mdio_bus_addr_writes_##addr.attr.attr, \
&dev_attr_mdio_bus_addr_reads_##addr.attr.attr \
static struct attribute *mdio_bus_statistics_attrs[] = {
&dev_attr_mdio_bus_transfers.attr.attr,
&dev_attr_mdio_bus_errors.attr.attr,
&dev_attr_mdio_bus_writes.attr.attr,
&dev_attr_mdio_bus_reads.attr.attr,
MDIO_BUS_STATS_ADDR_ATTR_GROUP(0),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(1),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(2),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(3),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(4),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(5),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(6),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(7),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(8),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(9),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(10),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(11),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(12),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(13),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(14),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(15),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(16),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(17),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(18),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(19),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(20),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(21),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(22),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(23),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(24),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(25),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(26),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(27),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(28),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(29),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(30),
MDIO_BUS_STATS_ADDR_ATTR_GROUP(31),
NULL,
};
static const struct attribute_group mdio_bus_statistics_group = {
.name = "statistics",
.attrs = mdio_bus_statistics_attrs,
};
static const struct attribute_group *mdio_bus_groups[] = {
&mdio_bus_statistics_group,
NULL,
};
static struct class mdio_bus_class = { static struct class mdio_bus_class = {
.name = "mdio_bus", .name = "mdio_bus",
.dev_release = mdiobus_release, .dev_release = mdiobus_release,
.dev_groups = mdio_bus_groups,
}; };
#if IS_ENABLED(CONFIG_OF_MDIO) #if IS_ENABLED(CONFIG_OF_MDIO)
...@@ -530,6 +738,24 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) ...@@ -530,6 +738,24 @@ struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr)
} }
EXPORT_SYMBOL(mdiobus_scan); EXPORT_SYMBOL(mdiobus_scan);
static void mdiobus_stats_acct(struct mdio_bus_stats *stats, bool op, int ret)
{
u64_stats_update_begin(&stats->syncp);
u64_stats_inc(&stats->transfers);
if (ret < 0) {
u64_stats_inc(&stats->errors);
goto out;
}
if (op)
u64_stats_inc(&stats->reads);
else
u64_stats_inc(&stats->writes);
out:
u64_stats_update_end(&stats->syncp);
}
/** /**
* __mdiobus_read - Unlocked version of the mdiobus_read function * __mdiobus_read - Unlocked version of the mdiobus_read function
* @bus: the mii_bus struct * @bus: the mii_bus struct
...@@ -549,6 +775,7 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum) ...@@ -549,6 +775,7 @@ int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum)
retval = bus->read(bus, addr, regnum); retval = bus->read(bus, addr, regnum);
trace_mdio_access(bus, 1, addr, regnum, retval, retval); trace_mdio_access(bus, 1, addr, regnum, retval, retval);
mdiobus_stats_acct(&bus->stats[addr], true, retval);
return retval; return retval;
} }
...@@ -574,6 +801,7 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val) ...@@ -574,6 +801,7 @@ int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val)
err = bus->write(bus, addr, regnum, val); err = bus->write(bus, addr, regnum, val);
trace_mdio_access(bus, 0, addr, regnum, val, err); trace_mdio_access(bus, 0, addr, regnum, val, err);
mdiobus_stats_acct(&bus->stats[addr], false, err);
return err; return err;
} }
...@@ -719,8 +947,27 @@ static int mdio_uevent(struct device *dev, struct kobj_uevent_env *env) ...@@ -719,8 +947,27 @@ static int mdio_uevent(struct device *dev, struct kobj_uevent_env *env)
return 0; return 0;
} }
static struct attribute *mdio_bus_device_statistics_attrs[] = {
&dev_attr_mdio_bus_device_transfers.attr.attr,
&dev_attr_mdio_bus_device_errors.attr.attr,
&dev_attr_mdio_bus_device_writes.attr.attr,
&dev_attr_mdio_bus_device_reads.attr.attr,
NULL,
};
static const struct attribute_group mdio_bus_device_statistics_group = {
.name = "statistics",
.attrs = mdio_bus_device_statistics_attrs,
};
static const struct attribute_group *mdio_bus_dev_groups[] = {
&mdio_bus_device_statistics_group,
NULL,
};
struct bus_type mdio_bus_type = { struct bus_type mdio_bus_type = {
.name = "mdio_bus", .name = "mdio_bus",
.dev_groups = mdio_bus_dev_groups,
.match = mdio_bus_match, .match = mdio_bus_match,
.uevent = mdio_uevent, .uevent = mdio_uevent,
}; };
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <linux/mod_devicetable.h> #include <linux/mod_devicetable.h>
#include <linux/u64_stats_sync.h>
#include <linux/atomic.h> #include <linux/atomic.h>
...@@ -212,6 +213,15 @@ struct sfp_bus; ...@@ -212,6 +213,15 @@ struct sfp_bus;
struct sfp_upstream_ops; struct sfp_upstream_ops;
struct sk_buff; struct sk_buff;
struct mdio_bus_stats {
u64_stats_t transfers;
u64_stats_t errors;
u64_stats_t writes;
u64_stats_t reads;
/* Must be last, add new statistics above */
struct u64_stats_sync syncp;
};
/* /*
* The Bus class for PHYs. Devices which provide access to * The Bus class for PHYs. Devices which provide access to
* PHYs should register using this structure * PHYs should register using this structure
...@@ -224,6 +234,7 @@ struct mii_bus { ...@@ -224,6 +234,7 @@ struct mii_bus {
int (*read)(struct mii_bus *bus, int addr, int regnum); int (*read)(struct mii_bus *bus, int addr, int regnum);
int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val); int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
int (*reset)(struct mii_bus *bus); int (*reset)(struct mii_bus *bus);
struct mdio_bus_stats stats[PHY_MAX_ADDR];
/* /*
* A lock to ensure that only one thing can read/write * A lock to ensure that only one thing can read/write
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment