net: dsa: microchip: add MIB counter reading support
Add background MIB counter reading support. Port MIB counters should only be read when there is link. Otherwise it is a waste of time as hardware never increases those counters. There are exceptions as some switches keep track of dropped counts no matter what. Signed-off-by: Tristram Ha <Tristram.Ha@microchip.com> Reviewed-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
parent
42fc6a4c61
commit
7c6ff470aa
|
@ -10,6 +10,7 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/platform_data/microchip-ksz.h>
|
||||
#include <linux/phy.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
@ -18,8 +19,8 @@
|
|||
#include <net/switchdev.h>
|
||||
|
||||
#include "ksz_priv.h"
|
||||
#include "ksz_common.h"
|
||||
#include "ksz9477_reg.h"
|
||||
#include "ksz_common.h"
|
||||
|
||||
static const struct {
|
||||
int index;
|
||||
|
@ -259,6 +260,75 @@ static int ksz9477_reset_switch(struct ksz_device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ksz9477_r_mib_cnt(struct ksz_device *dev, int port, u16 addr,
|
||||
u64 *cnt)
|
||||
{
|
||||
struct ksz_poll_ctx ctx = {
|
||||
.dev = dev,
|
||||
.port = port,
|
||||
.offset = REG_PORT_MIB_CTRL_STAT__4,
|
||||
};
|
||||
struct ksz_port *p = &dev->ports[port];
|
||||
u32 data;
|
||||
int ret;
|
||||
|
||||
/* retain the flush/freeze bit */
|
||||
data = p->freeze ? MIB_COUNTER_FLUSH_FREEZE : 0;
|
||||
data |= MIB_COUNTER_READ;
|
||||
data |= (addr << MIB_COUNTER_INDEX_S);
|
||||
ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
|
||||
|
||||
ret = readx_poll_timeout(ksz_pread32_poll, &ctx, data,
|
||||
!(data & MIB_COUNTER_READ), 10, 1000);
|
||||
|
||||
/* failed to read MIB. get out of loop */
|
||||
if (ret < 0) {
|
||||
dev_dbg(dev->dev, "Failed to get MIB\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* count resets upon read */
|
||||
ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
|
||||
*cnt += data;
|
||||
}
|
||||
|
||||
static void ksz9477_r_mib_pkt(struct ksz_device *dev, int port, u16 addr,
|
||||
u64 *dropped, u64 *cnt)
|
||||
{
|
||||
addr = ksz9477_mib_names[addr].index;
|
||||
ksz9477_r_mib_cnt(dev, port, addr, cnt);
|
||||
}
|
||||
|
||||
static void ksz9477_freeze_mib(struct ksz_device *dev, int port, bool freeze)
|
||||
{
|
||||
u32 val = freeze ? MIB_COUNTER_FLUSH_FREEZE : 0;
|
||||
struct ksz_port *p = &dev->ports[port];
|
||||
|
||||
/* enable/disable the port for flush/freeze function */
|
||||
mutex_lock(&p->mib.cnt_mutex);
|
||||
ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, val);
|
||||
|
||||
/* used by MIB counter reading code to know freeze is enabled */
|
||||
p->freeze = freeze;
|
||||
mutex_unlock(&p->mib.cnt_mutex);
|
||||
}
|
||||
|
||||
static void ksz9477_port_init_cnt(struct ksz_device *dev, int port)
|
||||
{
|
||||
struct ksz_port_mib *mib = &dev->ports[port].mib;
|
||||
|
||||
/* flush all enabled port MIB counters */
|
||||
mutex_lock(&mib->cnt_mutex);
|
||||
ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
|
||||
MIB_COUNTER_FLUSH_FREEZE);
|
||||
ksz_write8(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FLUSH);
|
||||
ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, 0);
|
||||
mutex_unlock(&mib->cnt_mutex);
|
||||
|
||||
mib->cnt_ptr = 0;
|
||||
memset(mib->counters, 0, dev->mib_cnt * sizeof(u64));
|
||||
}
|
||||
|
||||
static enum dsa_tag_protocol ksz9477_get_tag_protocol(struct dsa_switch *ds,
|
||||
int port)
|
||||
{
|
||||
|
@ -342,47 +412,6 @@ static void ksz9477_get_strings(struct dsa_switch *ds, int port,
|
|||
}
|
||||
}
|
||||
|
||||
static void ksz_get_ethtool_stats(struct dsa_switch *ds, int port,
|
||||
uint64_t *buf)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
int i;
|
||||
u32 data;
|
||||
int timeout;
|
||||
|
||||
mutex_lock(&dev->stats_mutex);
|
||||
|
||||
for (i = 0; i < TOTAL_SWITCH_COUNTER_NUM; i++) {
|
||||
data = MIB_COUNTER_READ;
|
||||
data |= ((ksz9477_mib_names[i].index & 0xFF) <<
|
||||
MIB_COUNTER_INDEX_S);
|
||||
ksz_pwrite32(dev, port, REG_PORT_MIB_CTRL_STAT__4, data);
|
||||
|
||||
timeout = 1000;
|
||||
do {
|
||||
ksz_pread32(dev, port, REG_PORT_MIB_CTRL_STAT__4,
|
||||
&data);
|
||||
usleep_range(1, 10);
|
||||
if (!(data & MIB_COUNTER_READ))
|
||||
break;
|
||||
} while (timeout-- > 0);
|
||||
|
||||
/* failed to read MIB. get out of loop */
|
||||
if (!timeout) {
|
||||
dev_dbg(dev->dev, "Failed to get MIB\n");
|
||||
break;
|
||||
}
|
||||
|
||||
/* count resets upon read */
|
||||
ksz_pread32(dev, port, REG_PORT_MIB_DATA, &data);
|
||||
|
||||
dev->mib_value[i] += (uint64_t)data;
|
||||
buf[i] = dev->mib_value[i];
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->stats_mutex);
|
||||
}
|
||||
|
||||
static void ksz9477_cfg_port_member(struct ksz_device *dev, int port,
|
||||
u8 member)
|
||||
{
|
||||
|
@ -1151,9 +1180,14 @@ static int ksz9477_setup(struct dsa_switch *ds)
|
|||
/* queue based egress rate limit */
|
||||
ksz_cfg(dev, REG_SW_MAC_CTRL_5, SW_OUT_RATE_LIMIT_QUEUE_BASED, true);
|
||||
|
||||
/* enable global MIB counter freeze function */
|
||||
ksz_cfg(dev, REG_SW_MAC_CTRL_6, SW_MIB_COUNTER_FREEZE, true);
|
||||
|
||||
/* start switch */
|
||||
ksz_cfg(dev, REG_SW_OPERATION, SW_START, true);
|
||||
|
||||
ksz_init_mib_timer(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1287,6 +1321,7 @@ static int ksz9477_switch_init(struct ksz_device *dev)
|
|||
if (!dev->ports)
|
||||
return -ENOMEM;
|
||||
for (i = 0; i < dev->mib_port_cnt; i++) {
|
||||
mutex_init(&dev->ports[i].mib.cnt_mutex);
|
||||
dev->ports[i].mib.counters =
|
||||
devm_kzalloc(dev->dev,
|
||||
sizeof(u64) *
|
||||
|
@ -1311,6 +1346,10 @@ static const struct ksz_dev_ops ksz9477_dev_ops = {
|
|||
.flush_dyn_mac_table = ksz9477_flush_dyn_mac_table,
|
||||
.phy_setup = ksz9477_phy_setup,
|
||||
.port_setup = ksz9477_port_setup,
|
||||
.r_mib_cnt = ksz9477_r_mib_cnt,
|
||||
.r_mib_pkt = ksz9477_r_mib_pkt,
|
||||
.freeze_mib = ksz9477_freeze_mib,
|
||||
.port_init_cnt = ksz9477_port_init_cnt,
|
||||
.shutdown = ksz9477_reset_switch,
|
||||
.detect = ksz9477_switch_detect,
|
||||
.init = ksz9477_switch_init,
|
||||
|
|
|
@ -40,6 +40,85 @@ void ksz_update_port_member(struct ksz_device *dev, int port)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ksz_update_port_member);
|
||||
|
||||
static void port_r_cnt(struct ksz_device *dev, int port)
|
||||
{
|
||||
struct ksz_port_mib *mib = &dev->ports[port].mib;
|
||||
u64 *dropped;
|
||||
|
||||
/* Some ports may not have MIB counters before SWITCH_COUNTER_NUM. */
|
||||
while (mib->cnt_ptr < dev->reg_mib_cnt) {
|
||||
dev->dev_ops->r_mib_cnt(dev, port, mib->cnt_ptr,
|
||||
&mib->counters[mib->cnt_ptr]);
|
||||
++mib->cnt_ptr;
|
||||
}
|
||||
|
||||
/* last one in storage */
|
||||
dropped = &mib->counters[dev->mib_cnt];
|
||||
|
||||
/* Some ports may not have MIB counters after SWITCH_COUNTER_NUM. */
|
||||
while (mib->cnt_ptr < dev->mib_cnt) {
|
||||
dev->dev_ops->r_mib_pkt(dev, port, mib->cnt_ptr,
|
||||
dropped, &mib->counters[mib->cnt_ptr]);
|
||||
++mib->cnt_ptr;
|
||||
}
|
||||
mib->cnt_ptr = 0;
|
||||
}
|
||||
|
||||
static void ksz_mib_read_work(struct work_struct *work)
|
||||
{
|
||||
struct ksz_device *dev = container_of(work, struct ksz_device,
|
||||
mib_read);
|
||||
struct ksz_port_mib *mib;
|
||||
struct ksz_port *p;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < dev->mib_port_cnt; i++) {
|
||||
p = &dev->ports[i];
|
||||
mib = &p->mib;
|
||||
mutex_lock(&mib->cnt_mutex);
|
||||
|
||||
/* Only read MIB counters when the port is told to do.
|
||||
* If not, read only dropped counters when link is not up.
|
||||
*/
|
||||
if (!p->read) {
|
||||
const struct dsa_port *dp = dsa_to_port(dev->ds, i);
|
||||
|
||||
if (!netif_carrier_ok(dp->slave))
|
||||
mib->cnt_ptr = dev->reg_mib_cnt;
|
||||
}
|
||||
port_r_cnt(dev, i);
|
||||
p->read = false;
|
||||
mutex_unlock(&mib->cnt_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
static void mib_monitor(struct timer_list *t)
|
||||
{
|
||||
struct ksz_device *dev = from_timer(dev, t, mib_read_timer);
|
||||
|
||||
mod_timer(&dev->mib_read_timer, jiffies + dev->mib_read_interval);
|
||||
schedule_work(&dev->mib_read);
|
||||
}
|
||||
|
||||
void ksz_init_mib_timer(struct ksz_device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* Read MIB counters every 30 seconds to avoid overflow. */
|
||||
dev->mib_read_interval = msecs_to_jiffies(30000);
|
||||
|
||||
INIT_WORK(&dev->mib_read, ksz_mib_read_work);
|
||||
timer_setup(&dev->mib_read_timer, mib_monitor, 0);
|
||||
|
||||
for (i = 0; i < dev->mib_port_cnt; i++)
|
||||
dev->dev_ops->port_init_cnt(dev, i);
|
||||
|
||||
/* Start the timer 2 seconds later. */
|
||||
dev->mib_read_timer.expires = jiffies + msecs_to_jiffies(2000);
|
||||
add_timer(&dev->mib_read_timer);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ksz_init_mib_timer);
|
||||
|
||||
int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg)
|
||||
{
|
||||
struct ksz_device *dev = ds->priv;
|
||||
|
@ -72,6 +151,24 @@ int ksz_sset_count(struct dsa_switch *ds, int port, int sset)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(ksz_sset_count);
|
||||
|
||||
void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf)
|
||||
{
|
||||
const struct dsa_port *dp = dsa_to_port(ds, port);
|
||||
struct ksz_device *dev = ds->priv;
|
||||
struct ksz_port_mib *mib;
|
||||
|
||||
mib = &dev->ports[port].mib;
|
||||
mutex_lock(&mib->cnt_mutex);
|
||||
|
||||
/* Only read dropped counters if no link. */
|
||||
if (!netif_carrier_ok(dp->slave))
|
||||
mib->cnt_ptr = dev->reg_mib_cnt;
|
||||
port_r_cnt(dev, port);
|
||||
memcpy(buf, mib->counters, dev->mib_cnt * sizeof(u64));
|
||||
mutex_unlock(&mib->cnt_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ksz_get_ethtool_stats);
|
||||
|
||||
int ksz_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
struct net_device *br)
|
||||
{
|
||||
|
@ -339,6 +436,12 @@ EXPORT_SYMBOL(ksz_switch_register);
|
|||
|
||||
void ksz_switch_remove(struct ksz_device *dev)
|
||||
{
|
||||
/* timer started */
|
||||
if (dev->mib_read_timer.expires) {
|
||||
del_timer_sync(&dev->mib_read_timer);
|
||||
flush_work(&dev->mib_read);
|
||||
}
|
||||
|
||||
dev->dev_ops->exit(dev);
|
||||
dsa_unregister_switch(dev->ds);
|
||||
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0
|
||||
* Microchip switch driver common header
|
||||
*
|
||||
* Copyright (C) 2017-2018 Microchip Technology Inc.
|
||||
* Copyright (C) 2017-2019 Microchip Technology Inc.
|
||||
*/
|
||||
|
||||
#ifndef __KSZ_COMMON_H
|
||||
#define __KSZ_COMMON_H
|
||||
|
||||
void ksz_update_port_member(struct ksz_device *dev, int port);
|
||||
void ksz_init_mib_timer(struct ksz_device *dev);
|
||||
|
||||
/* Common DSA access functions */
|
||||
|
||||
int ksz_phy_read16(struct dsa_switch *ds, int addr, int reg);
|
||||
int ksz_phy_write16(struct dsa_switch *ds, int addr, int reg, u16 val);
|
||||
int ksz_sset_count(struct dsa_switch *ds, int port, int sset);
|
||||
void ksz_get_ethtool_stats(struct dsa_switch *ds, int port, uint64_t *buf);
|
||||
int ksz_port_bridge_join(struct dsa_switch *ds, int port,
|
||||
struct net_device *br);
|
||||
void ksz_port_bridge_leave(struct dsa_switch *ds, int port,
|
||||
|
@ -211,4 +213,18 @@ static void ksz_port_cfg(struct ksz_device *dev, int port, int offset, u8 bits,
|
|||
ksz_write8(dev, addr, data);
|
||||
}
|
||||
|
||||
struct ksz_poll_ctx {
|
||||
struct ksz_device *dev;
|
||||
int port;
|
||||
int offset;
|
||||
};
|
||||
|
||||
static inline u32 ksz_pread32_poll(struct ksz_poll_ctx *ctx)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
ksz_pread32(ctx->dev, ctx->port, ctx->offset, &data);
|
||||
return data;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
#include <linux/etherdevice.h>
|
||||
#include <net/dsa.h>
|
||||
|
||||
#include "ksz9477_reg.h"
|
||||
|
||||
struct ksz_io_ops;
|
||||
|
||||
struct vlan_table {
|
||||
|
@ -23,6 +21,7 @@ struct vlan_table {
|
|||
};
|
||||
|
||||
struct ksz_port_mib {
|
||||
struct mutex cnt_mutex; /* structure access */
|
||||
u8 cnt_ptr;
|
||||
u64 *counters;
|
||||
};
|
||||
|
@ -38,7 +37,8 @@ struct ksz_port {
|
|||
u32 fiber:1; /* port is fiber */
|
||||
u32 sgmii:1; /* port is SGMII */
|
||||
u32 force:1;
|
||||
u32 link_just_down:1; /* link just goes down */
|
||||
u32 read:1; /* read MIB counters in background */
|
||||
u32 freeze:1; /* MIB counter freeze is enabled */
|
||||
|
||||
struct ksz_port_mib mib;
|
||||
};
|
||||
|
@ -79,8 +79,6 @@ struct ksz_device {
|
|||
|
||||
struct vlan_table *vlan_cache;
|
||||
|
||||
u64 mib_value[TOTAL_SWITCH_COUNTER_NUM];
|
||||
|
||||
u8 *txbuf;
|
||||
|
||||
struct ksz_port *ports;
|
||||
|
@ -153,6 +151,7 @@ struct ksz_dev_ops {
|
|||
u64 *cnt);
|
||||
void (*r_mib_pkt)(struct ksz_device *dev, int port, u16 addr,
|
||||
u64 *dropped, u64 *cnt);
|
||||
void (*freeze_mib)(struct ksz_device *dev, int port, bool freeze);
|
||||
void (*port_init_cnt)(struct ksz_device *dev, int port);
|
||||
int (*shutdown)(struct ksz_device *dev);
|
||||
int (*detect)(struct ksz_device *dev);
|
||||
|
|
Loading…
Reference in New Issue