net: dsa: Allow providing PHY statistics from CPU port
Implement the same type of ethtool diversion that we have for ETH_SS_STATS and make it work with ETH_SS_PHY_STATS. This allows providing PHY level statistics for CPU ports that are directly connecting to a PHY device. Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Родитель
6207a78c09
Коммит
cf96357303
|
@ -361,6 +361,8 @@ struct dsa_switch_ops {
|
|||
void (*get_ethtool_stats)(struct dsa_switch *ds,
|
||||
int port, uint64_t *data);
|
||||
int (*get_sset_count)(struct dsa_switch *ds, int port, int sset);
|
||||
void (*get_ethtool_phy_stats)(struct dsa_switch *ds,
|
||||
int port, uint64_t *data);
|
||||
|
||||
/*
|
||||
* ethtool Wake-on-LAN
|
||||
|
@ -589,4 +591,9 @@ static inline int call_dsa_notifiers(unsigned long val, struct net_device *dev,
|
|||
#define BRCM_TAG_GET_PORT(v) ((v) >> 8)
|
||||
#define BRCM_TAG_GET_QUEUE(v) ((v) & 0xff)
|
||||
|
||||
|
||||
int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data);
|
||||
int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data);
|
||||
int dsa_port_get_phy_sset_count(struct dsa_port *dp);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,32 @@ static void dsa_master_get_ethtool_stats(struct net_device *dev,
|
|||
ds->ops->get_ethtool_stats(ds, port, data + count);
|
||||
}
|
||||
|
||||
static void dsa_master_get_ethtool_phy_stats(struct net_device *dev,
|
||||
struct ethtool_stats *stats,
|
||||
uint64_t *data)
|
||||
{
|
||||
struct dsa_port *cpu_dp = dev->dsa_ptr;
|
||||
const struct ethtool_ops *ops = cpu_dp->orig_ethtool_ops;
|
||||
struct dsa_switch *ds = cpu_dp->ds;
|
||||
int port = cpu_dp->index;
|
||||
int count = 0;
|
||||
|
||||
if (dev->phydev && !ops->get_ethtool_phy_stats) {
|
||||
count = phy_ethtool_get_sset_count(dev->phydev);
|
||||
if (count >= 0)
|
||||
phy_ethtool_get_stats(dev->phydev, stats, data);
|
||||
} else if (ops->get_sset_count && ops->get_ethtool_phy_stats) {
|
||||
count = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
|
||||
ops->get_ethtool_phy_stats(dev, stats, data);
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
count = 0;
|
||||
|
||||
if (ds->ops->get_ethtool_phy_stats)
|
||||
ds->ops->get_ethtool_phy_stats(ds, port, data + count);
|
||||
}
|
||||
|
||||
static int dsa_master_get_sset_count(struct net_device *dev, int sset)
|
||||
{
|
||||
struct dsa_port *cpu_dp = dev->dsa_ptr;
|
||||
|
@ -38,11 +64,14 @@ static int dsa_master_get_sset_count(struct net_device *dev, int sset)
|
|||
struct dsa_switch *ds = cpu_dp->ds;
|
||||
int count = 0;
|
||||
|
||||
if (ops->get_sset_count) {
|
||||
if (sset == ETH_SS_PHY_STATS && dev->phydev &&
|
||||
!ops->get_ethtool_phy_stats)
|
||||
count = phy_ethtool_get_sset_count(dev->phydev);
|
||||
else if (ops->get_sset_count)
|
||||
count = ops->get_sset_count(dev, sset);
|
||||
if (count < 0)
|
||||
count = 0;
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
count = 0;
|
||||
|
||||
if (ds->ops->get_sset_count)
|
||||
count += ds->ops->get_sset_count(ds, cpu_dp->index, sset);
|
||||
|
@ -67,7 +96,14 @@ static void dsa_master_get_strings(struct net_device *dev, uint32_t stringset,
|
|||
/* We do not want to be NULL-terminated, since this is a prefix */
|
||||
pfx[sizeof(pfx) - 1] = '_';
|
||||
|
||||
if (ops->get_sset_count && ops->get_strings) {
|
||||
if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
|
||||
!ops->get_ethtool_phy_stats) {
|
||||
mcount = phy_ethtool_get_sset_count(dev->phydev);
|
||||
if (mcount < 0)
|
||||
mcount = 0;
|
||||
else
|
||||
phy_ethtool_get_strings(dev->phydev, data);
|
||||
} else if (ops->get_sset_count && ops->get_strings) {
|
||||
mcount = ops->get_sset_count(dev, stringset);
|
||||
if (mcount < 0)
|
||||
mcount = 0;
|
||||
|
@ -107,6 +143,7 @@ static int dsa_master_ethtool_setup(struct net_device *dev)
|
|||
ops->get_sset_count = dsa_master_get_sset_count;
|
||||
ops->get_ethtool_stats = dsa_master_get_ethtool_stats;
|
||||
ops->get_strings = dsa_master_get_strings;
|
||||
ops->get_ethtool_phy_stats = dsa_master_get_ethtool_phy_stats;
|
||||
|
||||
dev->ethtool_ops = ops;
|
||||
|
||||
|
|
|
@ -383,3 +383,60 @@ void dsa_port_link_unregister_of(struct dsa_port *dp)
|
|||
else
|
||||
dsa_port_setup_phy_of(dp, false);
|
||||
}
|
||||
|
||||
int dsa_port_get_phy_strings(struct dsa_port *dp, uint8_t *data)
|
||||
{
|
||||
struct phy_device *phydev;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (of_phy_is_fixed_link(dp->dn))
|
||||
return ret;
|
||||
|
||||
phydev = dsa_port_get_phy_device(dp);
|
||||
if (IS_ERR_OR_NULL(phydev))
|
||||
return ret;
|
||||
|
||||
ret = phy_ethtool_get_strings(phydev, data);
|
||||
put_device(&phydev->mdio.dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dsa_port_get_phy_strings);
|
||||
|
||||
int dsa_port_get_ethtool_phy_stats(struct dsa_port *dp, uint64_t *data)
|
||||
{
|
||||
struct phy_device *phydev;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (of_phy_is_fixed_link(dp->dn))
|
||||
return ret;
|
||||
|
||||
phydev = dsa_port_get_phy_device(dp);
|
||||
if (IS_ERR_OR_NULL(phydev))
|
||||
return ret;
|
||||
|
||||
ret = phy_ethtool_get_stats(phydev, NULL, data);
|
||||
put_device(&phydev->mdio.dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dsa_port_get_ethtool_phy_stats);
|
||||
|
||||
int dsa_port_get_phy_sset_count(struct dsa_port *dp)
|
||||
{
|
||||
struct phy_device *phydev;
|
||||
int ret = -EOPNOTSUPP;
|
||||
|
||||
if (of_phy_is_fixed_link(dp->dn))
|
||||
return ret;
|
||||
|
||||
phydev = dsa_port_get_phy_device(dp);
|
||||
if (IS_ERR_OR_NULL(phydev))
|
||||
return ret;
|
||||
|
||||
ret = phy_ethtool_get_sset_count(phydev);
|
||||
put_device(&phydev->mdio.dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dsa_port_get_phy_sset_count);
|
||||
|
|
Загрузка…
Ссылка в новой задаче