realtek: dsa: consolidate statistics handling in stats.c

Move hardware statistics helpers and packet counter allocation, read
and clear functions into stats.c.

Standardize function names on the otto_stats_ prefix, followed by the
chip identifier where applicable, and remove redundant stats suffixes.
Update declarations, includes, callers and operation tables accordingly.

No functional changes intended.

Assisted-by: ChatGPT (OpenAI GPT-6 Astra)
Link: https://github.com/openwrt/openwrt/pull/25802
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Markus Stockhausen committed 2026-10-11 13:41:18 +02:00
1 parent 74521f894e
commit c8fbe693c3
11 files changed
+468 -452

No files matched your search

@@ -6,7 +6,6 @@
#include <net/nexthop.h>
#include <net/neighbour.h>
#include <net/netevent.h>
#include <linux/cleanup.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/inetdevice.h>
@@ -221,95 +220,6 @@ static int rtldsa_ports_probe(struct rtl838x_switch_priv *priv)
// return idx;
// }
/*
* Packet counters share hardware memory with octet counters (2 packet counters
* per 1 octet block). Allocation relies on two complementary bitmaps:
*
* octet_cntr_use_bm: 0 = free block, 1 = used (or split into packet counters)
* packet_cntr_use_bm: 1 = free standalone counter, 0 = unavailable
*
* Allocation strategy:
* 1. Look for a free standalone counter from an already split block (bit = 1).
* 2. If none are free, claim a new octet block 'j', use counter index (2 * j),
* and mark counter (2 * j + 1) as available for future allocations.
*/
/**
* rtldsa_packet_cntr_alloc - Allocate a hardware packet counter.
* @priv: Switch driver private structure.
*
* Return: Counter index (>= 0) on success, or -1 if full.
*/
int rtldsa_packet_cntr_alloc(struct rtl838x_switch_priv *priv)
{
int idx, j, base = 0;
/* On SoCs where a PIE rule implicitly logs into the LOG table entry
* with its own rule ID (RTL930x), the tc cls_flower offload consumes
* one LOG counter per PIE rule and does not pass through this
* allocator. Keep that range reserved so a route counter handed out
* here cannot alias a flow's LOG entry.
*
* The offload is ingress-only, so PIE rule IDs currently populate
* only the lower half of that range, and a route counter's LOG entry
* (idx / 2) never dips below the upper half either. If egress PIE
* rules are ever wired up, this reservation needs to grow so their
* LOG entries stay clear of route counters too.
*/
if (priv->r->pie_rule_id_is_log_counter)
base = priv->r->n_pie_blocks * PIE_BLOCK_SIZE;
scoped_guard(mutex, &priv->reg_mutex) {
idx = find_next_bit(priv->packet_cntr_use_bm, priv->r->n_counters * 2, base);
if (idx >= priv->r->n_counters * 2) {
j = find_next_zero_bit(priv->octet_cntr_use_bm, priv->r->n_counters,
base / 2);
if (j >= priv->r->n_counters)
return -1;
__set_bit(j, priv->octet_cntr_use_bm);
idx = j * 2;
__set_bit(j * 2 + 1, priv->packet_cntr_use_bm);
} else {
__clear_bit(idx, priv->packet_cntr_use_bm);
}
}
return idx;
}
/**
* rtldsa_packet_cntr_free - Release a packet counter from rtldsa_packet_cntr_alloc().
* @priv: Switch driver private structure.
* @idx: Packet counter index to free; a negative id is ignored.
*
* Marks the counter free again and, once both halves of its octet block are
* free, returns the whole block to the octet counter pool.
*/
void rtldsa_packet_cntr_free(struct rtl838x_switch_priv *priv, int idx)
{
int j;
if (idx < 0 || idx >= priv->r->n_counters * 2)
return;
scoped_guard(mutex, &priv->reg_mutex) {
/* already free - guard against a double release */
if (test_bit(idx, priv->packet_cntr_use_bm))
return;
__set_bit(idx, priv->packet_cntr_use_bm);
j = idx / 2;
if (test_bit(j, priv->octet_cntr_use_bm) &&
test_bit(idx ^ 1, priv->packet_cntr_use_bm)) {
__clear_bit(idx, priv->packet_cntr_use_bm);
__clear_bit(idx ^ 1, priv->packet_cntr_use_bm);
__clear_bit(j, priv->octet_cntr_use_bm);
}
}
}
int rtl83xx_port_is_under(const struct net_device *dev, struct rtl838x_switch_priv *priv)
{
/* Is the lower network device a DSA user network device of our RTL930X-switch?
@@ -156,8 +156,8 @@ static int rtldsa_83xx_setup(struct dsa_switch *ds)
}
priv->r->print_matrix();
rtldsa_stats_init(priv);
rtldsa_stats_init_counters(priv);
otto_stats_init(priv);
otto_stats_init_counters(priv);
rtldsa_83xx_mc_pmasks_setup(priv);
otto_vlan_setup(priv);
@@ -206,8 +206,8 @@ static int rtldsa_93xx_setup(struct dsa_switch *ds)
priv->r->traffic_set(priv->r->cpu_port, BIT_ULL(priv->r->cpu_port));
priv->r->print_matrix();
rtldsa_stats_init(priv);
rtldsa_stats_init_counters(priv);
otto_stats_init(priv);
otto_stats_init_counters(priv);
otto_vlan_setup(priv);
@@ -1249,15 +1249,15 @@ const struct dsa_switch_ops rtldsa_83xx_switch_ops = {
.phylink_get_caps = rtldsa_phylink_get_caps,
.get_strings = rtldsa_get_strings,
.get_ethtool_stats = rtldsa_get_ethtool_stats,
.get_sset_count = rtldsa_get_sset_count,
.get_eth_phy_stats = rtldsa_get_eth_phy_stats,
.get_eth_mac_stats = rtldsa_get_eth_mac_stats,
.get_eth_ctrl_stats = rtldsa_get_eth_ctrl_stats,
.get_rmon_stats = rtldsa_get_rmon_stats,
.get_stats64 = rtldsa_get_stats64,
.get_pause_stats = rtldsa_get_pause_stats,
.get_strings = otto_stats_get_strings,
.get_ethtool_stats = otto_stats_get_ethtool,
.get_sset_count = otto_stats_get_sset_count,
.get_eth_phy_stats = otto_stats_get_eth_phy,
.get_eth_mac_stats = otto_stats_get_eth_mac,
.get_eth_ctrl_stats = otto_stats_get_eth_ctrl,
.get_rmon_stats = otto_stats_get_rmon,
.get_stats64 = otto_stats_get64,
.get_pause_stats = otto_stats_get_pause,
.port_enable = rtldsa_port_enable,
.port_disable = rtldsa_port_disable,
@@ -1311,15 +1311,15 @@ const struct dsa_switch_ops rtldsa_93xx_switch_ops = {
.phylink_get_caps = rtldsa_phylink_get_caps,
.get_strings = rtldsa_get_strings,
.get_ethtool_stats = rtldsa_get_ethtool_stats,
.get_sset_count = rtldsa_get_sset_count,
.get_eth_phy_stats = rtldsa_get_eth_phy_stats,
.get_eth_mac_stats = rtldsa_get_eth_mac_stats,
.get_eth_ctrl_stats = rtldsa_get_eth_ctrl_stats,
.get_rmon_stats = rtldsa_get_rmon_stats,
.get_stats64 = rtldsa_get_stats64,
.get_pause_stats = rtldsa_get_pause_stats,
.get_strings = otto_stats_get_strings,
.get_ethtool_stats = otto_stats_get_ethtool,
.get_sset_count = otto_stats_get_sset_count,
.get_eth_phy_stats = otto_stats_get_eth_phy,
.get_eth_mac_stats = otto_stats_get_eth_mac,
.get_eth_ctrl_stats = otto_stats_get_eth_ctrl,
.get_rmon_stats = otto_stats_get_rmon,
.get_stats64 = otto_stats_get64,
.get_pause_stats = otto_stats_get_pause,
.port_enable = rtldsa_port_enable,
.port_disable = rtldsa_port_disable,
@@ -80,6 +80,7 @@
#include "l3.h"
#include "pie.h"
#include "rtl-otto.h"
#include "stats.h"
/* Route entry types */
#define ROUTE_TYPE_IP4UC 0
@@ -1745,7 +1746,7 @@ static void otto_l3_route_update_hw(struct otto_l3_ctrl *ctrl, struct otto_l3_ro
return;
if (r->pr.id < 0) {
r->pr.packet_cntr = rtldsa_packet_cntr_alloc(priv);
r->pr.packet_cntr = otto_stats_packet_cntr_alloc(priv);
if (r->pr.packet_cntr >= 0) {
dev_info(ctrl->dev, "Using packet counter %d\n",
r->pr.packet_cntr);
@@ -2174,7 +2175,7 @@ static void otto_l3_route_teardown(struct otto_l3_ctrl *ctrl, struct otto_l3_rou
otto_l2_nexthop_del(priv, &r->nh);
dev_dbg(ctrl->dev, "releasing packet counter %d\n", r->pr.packet_cntr);
rtldsa_packet_cntr_free(priv, r->pr.packet_cntr);
otto_stats_packet_cntr_free(priv, r->pr.packet_cntr);
/* Once the rows are not where the list says, after a failed move or a
* lookup that timed out, the row of a prefix route may still be in
@@ -337,7 +337,7 @@ struct rtldsa_config {
* code.
*
* Any SoC family which requires stat_port_table_read must use the table
* rtldsa_counters_(un)lock_table helpers. They are using a mutex for locking. The counters
* otto_stats_counters_(un)lock_table helpers. They are using a mutex for locking. The counters
* update is therefore not atomic.
*/
void (*stat_update_counters_atomically)(struct rtl838x_switch_priv *priv, int port);
@@ -554,8 +554,6 @@ struct fdb_update_work {
};
void rtldsa_port_fast_age(struct dsa_switch *ds, int port);
int rtldsa_packet_cntr_alloc(struct rtl838x_switch_priv *priv);
void rtldsa_packet_cntr_free(struct rtl838x_switch_priv *priv, int idx);
int rtl83xx_port_is_under(const struct net_device *dev, struct rtl838x_switch_priv *priv);
/* Port register accessor functions for the RTL839x and RTL931X SoCs */
void rtl839x_mask_port_reg_be(u64 clear, u64 set, int reg);
@@ -157,46 +157,6 @@ static void rtl838x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
priv->eee_enabled = enable;
}
static u32 rtl838x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter)
{
u32 buf[2];
u32 v;
dev_dbg(priv->dev, "reading LOG packet counter %d\n", counter);
otto_table_read(RTL8380_TBL_LOG, counter / 2, &buf);
dev_dbg(priv->dev, "LOG entry: %08x %08x\n", buf[0], buf[1]);
if (counter % 2)
v = buf[0];
else
v = buf[1];
return v;
}
static void rtl838x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter)
{
int tbl = otto_table_acquire(RTL8380_TBL_LOG);
u32 buf[2];
dev_dbg(priv->dev, "clearing LOG packet counter %d\n", counter);
/*
* Two counters share one LOG table entry. Read the current entry
* first so clearing one half preserves the adjacent counter.
*/
__otto_table_read(tbl, counter / 2, &buf);
if (counter % 2)
buf[0] = 0;
else
buf[1] = 0;
__otto_table_write(tbl, counter / 2, &buf);
otto_table_release(tbl);
}
static void rtl838x_set_igr_filter(int port, enum igr_filter state)
{
sw_w32_mask(0x3 << ((port & 0xf) << 1), state << ((port & 0xf) << 1),
@@ -229,12 +189,6 @@ static void rtl838x_set_receive_management_action(int port, rma_ctrl_t type, act
}
}
static void rtldsa_838x_stat_init(struct rtl838x_switch_priv *priv)
{
/* Enable statistics module: all counters plus debug */
sw_w32_mask(0, 3, RTL838X_STAT_CTRL);
}
const struct rtldsa_config rtldsa_838x_cfg = {
.switch_ops = &rtldsa_83xx_switch_ops,
.phylink_mac_ops = &rtldsa_83xx_phylink_mac_ops,
@@ -254,12 +208,12 @@ const struct rtldsa_config rtldsa_838x_cfg = {
.get_port_reg_le = rtl838x_get_port_reg,
.stat_port_rst = RTL838X_STAT_PORT_RST,
.stat_rst = RTL838X_STAT_RST,
.stat_init = rtldsa_838x_stat_init,
.stat_init = otto_stats_838x_init,
.stat_port_std_mib = RTL838X_STAT_PORT_STD_MIB,
.mib_desc = &rtldsa_838x_mib_desc,
.stat_counters_lock = rtldsa_counters_lock_register,
.stat_counters_unlock = rtldsa_counters_unlock_register,
.stat_update_counters_atomically = rtldsa_update_counters_atomically,
.stat_counters_lock = otto_stats_counters_lock_register,
.stat_counters_unlock = otto_stats_counters_unlock_register,
.stat_update_counters_atomically = otto_stats_update_counters_atomically,
.stat_counter_poll_interval = RTLDSA_COUNTERS_POLL_INTERVAL,
.port_iso_ctrl = rtl838x_port_iso_ctrl,
.traffic_enable = rtl838x_traffic_enable,
@@ -330,8 +284,8 @@ const struct rtldsa_config rtldsa_838x_cfg = {
.pie_rule_add = rtl838x_pie_rule_add,
.pie_rule_rm = rtl838x_pie_rule_rm,
.l2_learning_setup = otto_l2_838x_learning_setup,
.packet_cntr_read = rtl838x_packet_cntr_read,
.packet_cntr_clear = rtl838x_packet_cntr_clear,
.packet_cntr_read = otto_stats_838x_packet_cntr_read,
.packet_cntr_clear = otto_stats_838x_packet_cntr_clear,
.set_receive_management_action = rtl838x_set_receive_management_action,
.get_egress_rate = rtldsa_838x_get_egress_rate,
.set_egress_rate = rtldsa_838x_set_egress_rate,
@@ -157,45 +157,6 @@ static void rtl839x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
priv->eee_enabled = enable;
}
static u32 rtl839x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter)
{
u32 buf[2];
u32 v;
dev_dbg(priv->dev, "reading LOG packet counter %d\n", counter);
otto_table_read(RTL8390_TBL_LOG, counter / 2, &buf);
if (counter % 2)
v = buf[0];
else
v = buf[1];
return v;
}
static void rtl839x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter)
{
int tbl = otto_table_acquire(RTL8390_TBL_LOG);
u32 buf[2];
dev_dbg(priv->dev, "clearing LOG packet counter %d\n", counter);
/*
* Two counters share one LOG table entry. Read the current entry
* first so clearing one half preserves the adjacent counter.
*/
__otto_table_read(tbl, counter / 2, &buf);
if (counter % 2)
buf[0] = 0;
else
buf[1] = 0;
__otto_table_write(tbl, counter / 2, &buf);
otto_table_release(tbl);
}
static void rtl839x_set_igr_filter(int port, enum igr_filter state)
{
sw_w32_mask(0x3 << ((port & 0xf) << 1), state << ((port & 0xf) << 1),
@@ -249,9 +210,9 @@ const struct rtldsa_config rtldsa_839x_cfg = {
.stat_rst = RTL839X_STAT_RST,
.stat_port_std_mib = RTL839X_STAT_PORT_STD_MIB,
.mib_desc = &rtldsa_839x_mib_desc,
.stat_counters_lock = rtldsa_counters_lock_register,
.stat_counters_unlock = rtldsa_counters_unlock_register,
.stat_update_counters_atomically = rtldsa_update_counters_atomically,
.stat_counters_lock = otto_stats_counters_lock_register,
.stat_counters_unlock = otto_stats_counters_unlock_register,
.stat_update_counters_atomically = otto_stats_update_counters_atomically,
.stat_counter_poll_interval = RTLDSA_COUNTERS_POLL_INTERVAL,
.traffic_enable = rtl839x_traffic_enable,
.traffic_disable = rtl839x_traffic_disable,
@@ -320,8 +281,8 @@ const struct rtldsa_config rtldsa_839x_cfg = {
.pie_rule_add = rtl839x_pie_rule_add,
.pie_rule_rm = rtl839x_pie_rule_rm,
.l2_learning_setup = otto_l2_839x_learning_setup,
.packet_cntr_read = rtl839x_packet_cntr_read,
.packet_cntr_clear = rtl839x_packet_cntr_clear,
.packet_cntr_read = otto_stats_839x_packet_cntr_read,
.packet_cntr_clear = otto_stats_839x_packet_cntr_clear,
.set_receive_management_action = rtl839x_set_receive_management_action,
.get_egress_rate = rtldsa_839x_get_egress_rate,
.set_egress_rate = rtldsa_839x_set_egress_rate,
@@ -383,61 +383,6 @@ static void rtl930x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
// return mtu_id;
// }
/* A PIE rule logs its matched packets into the LOG table entry that carries
* its own rule ID, in data word 1 - one entry per rule. Counters handed out
* by rtldsa_packet_cntr_alloc() for L3 route statistics start above the PIE
* rule ID range and pack two 32-bit counters into one 64-bit LOG entry.
*/
#define RTL930X_PIE_RULE_IDS (rtldsa_930x_cfg.n_pie_blocks * PIE_BLOCK_SIZE)
static u32 rtl930x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter)
{
u32 buf[2];
u32 v;
dev_dbg(priv->dev, "reading LOG packet counter %d\n", counter);
if (counter < RTL930X_PIE_RULE_IDS) {
otto_table_read(RTL9300_TBL_LOG, counter, &buf);
v = buf[1];
} else {
/* Two counters share one entry, so the parity of counter picks
* which half of it to return.
*/
otto_table_read(RTL9300_TBL_LOG, counter / 2, &buf);
v = counter % 2 ? buf[0] : buf[1];
}
dev_dbg(priv->dev, "LOG entry: %08x %08x\n", buf[0], buf[1]);
return v;
}
static void rtl930x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter)
{
int tbl = otto_table_acquire(RTL9300_TBL_LOG);
u32 v[2];
dev_dbg(priv->dev, "clearing LOG packet counter %d\n", counter);
/* Read-modify-write: for a route counter the other 32-bit counter in
* the same 64-bit entry must be preserved; for a PIE rule ID the entry
* is ours alone but the RMW is harmless.
*/
if (counter < RTL930X_PIE_RULE_IDS) {
__otto_table_read(tbl, counter, &v);
v[1] = 0;
__otto_table_write(tbl, counter, &v);
} else {
__otto_table_read(tbl, counter / 2, &v);
v[counter % 2 ? 0 : 1] = 0;
__otto_table_write(tbl, counter / 2, &v);
}
otto_table_release(tbl);
}
static void rtl930x_set_igr_filter(int port, enum igr_filter state)
{
sw_w32_mask(0x3 << ((port & 0xf) << 1), state << ((port & 0xf) << 1),
@@ -620,9 +565,9 @@ const struct rtldsa_config rtldsa_930x_cfg = {
.stat_port_std_mib = RTL930X_STAT_PORT_MIB_CNTR,
.stat_port_prv_mib = RTL930X_STAT_PORT_PRVTE_CNTR,
.mib_desc = &rtldsa_930x_mib_desc,
.stat_counters_lock = rtldsa_counters_lock_register,
.stat_counters_unlock = rtldsa_counters_unlock_register,
.stat_update_counters_atomically = rtldsa_update_counters_atomically,
.stat_counters_lock = otto_stats_counters_lock_register,
.stat_counters_unlock = otto_stats_counters_unlock_register,
.stat_update_counters_atomically = otto_stats_update_counters_atomically,
.stat_counter_poll_interval = RTLDSA_COUNTERS_POLL_INTERVAL,
.traffic_enable = rtl930x_traffic_enable,
.traffic_disable = rtl930x_traffic_disable,
@@ -686,8 +631,8 @@ const struct rtldsa_config rtldsa_930x_cfg = {
.pie_rule_add = rtl930x_pie_rule_add,
.pie_rule_rm = rtl930x_pie_rule_rm,
.l2_learning_setup = otto_l2_930x_learning_setup,
.packet_cntr_read = rtl930x_packet_cntr_read,
.packet_cntr_clear = rtl930x_packet_cntr_clear,
.packet_cntr_read = otto_stats_930x_packet_cntr_read,
.packet_cntr_clear = otto_stats_930x_packet_cntr_clear,
.led_init = rtl930x_led_init,
.enable_learning = otto_l2_930x_enable_learning,
.enable_l2_new_sa_fwd = otto_l2_930x_set_port_new_sa_fwd,
@@ -385,36 +385,6 @@ static void rtldsa_931x_led_init(struct rtl838x_switch_priv *priv)
dev_dbg(dev, "%08x: %08x\n", 0xbb000600 + i * 4, sw_r32(0x0600 + i * 4));
}
static u64 rtldsa_931x_stat_port_table_read(int port, unsigned int mib_size,
unsigned int mib_offset, bool is_pvt)
{
enum otto_table_id id;
int field_offset;
u32 val[2];
if (is_pvt) {
id = RTL9310_TBL_STAT_PORT_PRVTE_CNTR;
field_offset = 27;
} else {
id = RTL9310_TBL_STAT_PORT_MIB_CNTR;
field_offset = 52;
}
/* Counter fields are numbered down from the last data word of the
* entry, so the high half of a 64 bit counter sits at the lower word
* index.
*/
if (mib_size == 2) {
otto_table_offset_read(id, port, &val, field_offset - (mib_offset + 1));
return (u64)val[0] << 32 | val[1];
}
otto_table_offset_read(id, port, &val[0], field_offset - mib_offset);
return val[0];
}
const struct rtldsa_config rtldsa_931x_cfg = {
.switch_ops = &rtldsa_93xx_switch_ops,
.phylink_mac_ops = &rtldsa_93xx_phylink_mac_ops,
@@ -436,9 +406,9 @@ const struct rtldsa_config rtldsa_931x_cfg = {
.stat_rst = RTL931X_STAT_RST,
.stat_port_std_mib = 0, /* Not defined */
.mib_desc = &rtldsa_931x_mib_desc,
.stat_port_table_read = rtldsa_931x_stat_port_table_read,
.stat_counters_lock = rtldsa_counters_lock_table,
.stat_counters_unlock = rtldsa_counters_unlock_table,
.stat_port_table_read = otto_stats_931x_port_table_read,
.stat_counters_lock = otto_stats_counters_lock_table,
.stat_counters_unlock = otto_stats_counters_unlock_table,
.stat_counter_poll_interval = RTLDSA_COUNTERS_FAST_POLL_INTERVAL,
.traffic_enable = rtl931x_traffic_enable,
.traffic_disable = rtl931x_traffic_disable,
@@ -1,7 +1,13 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <linux/bitmap.h>
#include <linux/cleanup.h>
#include <linux/mutex.h>
#include <linux/soc/realtek/otto_table.h>
#include <linux/string.h>
#include <net/dsa.h>
#include "pie.h"
#include "rtl-otto.h"
#include "stats.h"
@@ -368,7 +374,43 @@ const struct rtldsa_mib_desc rtldsa_931x_mib_desc = {
.list = rtldsa_931x_mib_list
};
void rtldsa_stats_init(struct rtl838x_switch_priv *priv)
void otto_stats_838x_init(struct rtl838x_switch_priv *priv)
{
/* Enable statistics module: all counters plus debug */
sw_w32_mask(0, 3, RTL838X_STAT_CTRL);
}
u64 otto_stats_931x_port_table_read(int port, unsigned int mib_size,
unsigned int mib_offset, bool is_pvt)
{
enum otto_table_id id;
int field_offset;
u32 val[2];
if (is_pvt) {
id = RTL9310_TBL_STAT_PORT_PRVTE_CNTR;
field_offset = 27;
} else {
id = RTL9310_TBL_STAT_PORT_MIB_CNTR;
field_offset = 52;
}
/* Counter fields are numbered down from the last data word of the
* entry, so the high half of a 64 bit counter sits at the lower word
* index.
*/
if (mib_size == 2) {
otto_table_offset_read(id, port, &val, field_offset - (mib_offset + 1));
return (u64)val[0] << 32 | val[1];
}
otto_table_offset_read(id, port, &val[0], field_offset - mib_offset);
return val[0];
}
void otto_stats_init(struct rtl838x_switch_priv *priv)
{
mutex_lock(&priv->reg_mutex);
@@ -381,9 +423,9 @@ void rtldsa_stats_init(struct rtl838x_switch_priv *priv)
mutex_unlock(&priv->reg_mutex);
}
static bool rtldsa_read_mib_item(struct rtl838x_switch_priv *priv, int port,
const struct rtldsa_mib_item *mib_item,
u64 *data)
static bool otto_stats_read_mib_item(struct rtl838x_switch_priv *priv, int port,
const struct rtldsa_mib_item *mib_item,
u64 *data)
{
u32 high1, high2;
int reg, reg_offset, addr_low;
@@ -428,14 +470,14 @@ static bool rtldsa_read_mib_item(struct rtl838x_switch_priv *priv, int port,
return true;
}
static void rtldsa_update_counter(struct rtl838x_switch_priv *priv, int port,
struct rtldsa_counter *counter,
const struct rtldsa_mib_item *mib_item)
static void otto_stats_update_counter(struct rtl838x_switch_priv *priv, int port,
struct rtldsa_counter *counter,
const struct rtldsa_mib_item *mib_item)
{
u64 val;
u32 val32, diff;
if (!rtldsa_read_mib_item(priv, port, mib_item, &val))
if (!otto_stats_read_mib_item(priv, port, mib_item, &val))
return;
if (mib_item->size == 2) {
@@ -448,8 +490,8 @@ static void rtldsa_update_counter(struct rtl838x_switch_priv *priv, int port,
}
}
static void rtldsa_update_link_stat(struct rtnl_link_stats64 *s,
const struct rtldsa_counter_state *counters)
static void otto_stats_update_link(struct rtnl_link_stats64 *s,
const struct rtldsa_counter_state *counters)
{
s->rx_packets = counters->if_in_ucast_pkts.val +
counters->if_in_mcast_pkts.val +
@@ -483,7 +525,7 @@ static void rtldsa_update_link_stat(struct rtnl_link_stats64 *s,
s->tx_errors = s->tx_aborted_errors + s->tx_window_errors;
}
static void rtldsa_update_port_counters(struct rtl838x_switch_priv *priv, int port)
static void otto_stats_update_port_counters(struct rtl838x_switch_priv *priv, int port)
{
struct rtldsa_counter_state *counters = &priv->ports[port].counters;
const struct rtldsa_mib_desc *mib_desc;
@@ -500,120 +542,120 @@ static void rtldsa_update_port_counters(struct rtl838x_switch_priv *priv, int po
return;
counters->last_update = now;
rtldsa_update_counter(priv, port, &counters->symbol_errors,
&mib_desc->symbol_errors);
otto_stats_update_counter(priv, port, &counters->symbol_errors,
&mib_desc->symbol_errors);
rtldsa_update_counter(priv, port, &counters->if_in_octets,
&mib_desc->if_in_octets);
rtldsa_update_counter(priv, port, &counters->if_out_octets,
&mib_desc->if_out_octets);
rtldsa_update_counter(priv, port, &counters->if_in_ucast_pkts,
&mib_desc->if_in_ucast_pkts);
rtldsa_update_counter(priv, port, &counters->if_in_mcast_pkts,
&mib_desc->if_in_mcast_pkts);
rtldsa_update_counter(priv, port, &counters->if_in_bcast_pkts,
&mib_desc->if_in_bcast_pkts);
rtldsa_update_counter(priv, port, &counters->if_out_ucast_pkts,
&mib_desc->if_out_ucast_pkts);
rtldsa_update_counter(priv, port, &counters->if_out_mcast_pkts,
&mib_desc->if_out_mcast_pkts);
rtldsa_update_counter(priv, port, &counters->if_out_bcast_pkts,
&mib_desc->if_out_bcast_pkts);
rtldsa_update_counter(priv, port, &counters->if_out_discards,
&mib_desc->if_out_discards);
rtldsa_update_counter(priv, port, &counters->single_collisions,
&mib_desc->single_collisions);
rtldsa_update_counter(priv, port, &counters->multiple_collisions,
&mib_desc->multiple_collisions);
rtldsa_update_counter(priv, port, &counters->deferred_transmissions,
&mib_desc->deferred_transmissions);
rtldsa_update_counter(priv, port, &counters->late_collisions,
&mib_desc->late_collisions);
rtldsa_update_counter(priv, port, &counters->excessive_collisions,
&mib_desc->excessive_collisions);
rtldsa_update_counter(priv, port, &counters->crc_align_errors,
&mib_desc->crc_align_errors);
rtldsa_update_counter(priv, port, &counters->rx_pkts_over_max_octets,
&mib_desc->rx_pkts_over_max_octets);
otto_stats_update_counter(priv, port, &counters->if_in_octets,
&mib_desc->if_in_octets);
otto_stats_update_counter(priv, port, &counters->if_out_octets,
&mib_desc->if_out_octets);
otto_stats_update_counter(priv, port, &counters->if_in_ucast_pkts,
&mib_desc->if_in_ucast_pkts);
otto_stats_update_counter(priv, port, &counters->if_in_mcast_pkts,
&mib_desc->if_in_mcast_pkts);
otto_stats_update_counter(priv, port, &counters->if_in_bcast_pkts,
&mib_desc->if_in_bcast_pkts);
otto_stats_update_counter(priv, port, &counters->if_out_ucast_pkts,
&mib_desc->if_out_ucast_pkts);
otto_stats_update_counter(priv, port, &counters->if_out_mcast_pkts,
&mib_desc->if_out_mcast_pkts);
otto_stats_update_counter(priv, port, &counters->if_out_bcast_pkts,
&mib_desc->if_out_bcast_pkts);
otto_stats_update_counter(priv, port, &counters->if_out_discards,
&mib_desc->if_out_discards);
otto_stats_update_counter(priv, port, &counters->single_collisions,
&mib_desc->single_collisions);
otto_stats_update_counter(priv, port, &counters->multiple_collisions,
&mib_desc->multiple_collisions);
otto_stats_update_counter(priv, port, &counters->deferred_transmissions,
&mib_desc->deferred_transmissions);
otto_stats_update_counter(priv, port, &counters->late_collisions,
&mib_desc->late_collisions);
otto_stats_update_counter(priv, port, &counters->excessive_collisions,
&mib_desc->excessive_collisions);
otto_stats_update_counter(priv, port, &counters->crc_align_errors,
&mib_desc->crc_align_errors);
otto_stats_update_counter(priv, port, &counters->rx_pkts_over_max_octets,
&mib_desc->rx_pkts_over_max_octets);
rtldsa_update_counter(priv, port, &counters->unsupported_opcodes,
&mib_desc->unsupported_opcodes);
otto_stats_update_counter(priv, port, &counters->unsupported_opcodes,
&mib_desc->unsupported_opcodes);
rtldsa_update_counter(priv, port, &counters->rx_undersize_pkts,
&mib_desc->rx_undersize_pkts);
rtldsa_update_counter(priv, port, &counters->rx_oversize_pkts,
&mib_desc->rx_oversize_pkts);
rtldsa_update_counter(priv, port, &counters->rx_fragments,
&mib_desc->rx_fragments);
rtldsa_update_counter(priv, port, &counters->rx_jabbers,
&mib_desc->rx_jabbers);
otto_stats_update_counter(priv, port, &counters->rx_undersize_pkts,
&mib_desc->rx_undersize_pkts);
otto_stats_update_counter(priv, port, &counters->rx_oversize_pkts,
&mib_desc->rx_oversize_pkts);
otto_stats_update_counter(priv, port, &counters->rx_fragments,
&mib_desc->rx_fragments);
otto_stats_update_counter(priv, port, &counters->rx_jabbers,
&mib_desc->rx_jabbers);
for (int i = 0; i < ARRAY_SIZE(mib_desc->tx_pkts); i++) {
if (mib_desc->tx_pkts[i].reg == MIB_REG_INVALID)
break;
rtldsa_update_counter(priv, port, &counters->tx_pkts[i],
&mib_desc->tx_pkts[i]);
otto_stats_update_counter(priv, port, &counters->tx_pkts[i],
&mib_desc->tx_pkts[i]);
}
for (int i = 0; i < ARRAY_SIZE(mib_desc->rx_pkts); i++) {
if (mib_desc->rx_pkts[i].reg == MIB_REG_INVALID)
break;
rtldsa_update_counter(priv, port, &counters->rx_pkts[i],
&mib_desc->rx_pkts[i]);
otto_stats_update_counter(priv, port, &counters->rx_pkts[i],
&mib_desc->rx_pkts[i]);
}
rtldsa_update_counter(priv, port, &counters->drop_events,
&mib_desc->drop_events);
rtldsa_update_counter(priv, port, &counters->collisions,
&mib_desc->collisions);
otto_stats_update_counter(priv, port, &counters->drop_events,
&mib_desc->drop_events);
otto_stats_update_counter(priv, port, &counters->collisions,
&mib_desc->collisions);
rtldsa_update_counter(priv, port, &counters->rx_pause_frames,
&mib_desc->rx_pause_frames);
rtldsa_update_counter(priv, port, &counters->tx_pause_frames,
&mib_desc->tx_pause_frames);
otto_stats_update_counter(priv, port, &counters->rx_pause_frames,
&mib_desc->rx_pause_frames);
otto_stats_update_counter(priv, port, &counters->tx_pause_frames,
&mib_desc->tx_pause_frames);
/* prepare get_stats64 reply without requiring caller waiting for mutex */
spin_lock(&counters->link_stat_lock);
rtldsa_update_link_stat(&counters->link_stat, counters);
otto_stats_update_link(&counters->link_stat, counters);
spin_unlock(&counters->link_stat_lock);
}
void rtldsa_counters_lock_register(struct rtl838x_switch_priv *priv, int port)
void otto_stats_counters_lock_register(struct rtl838x_switch_priv *priv, int port)
__acquires(&priv->ports[port].counters.lock)
{
spin_lock(&priv->ports[port].counters.lock);
}
void rtldsa_counters_unlock_register(struct rtl838x_switch_priv *priv, int port)
void otto_stats_counters_unlock_register(struct rtl838x_switch_priv *priv, int port)
__releases(&priv->ports[port].counters.lock)
{
spin_unlock(&priv->ports[port].counters.lock);
}
void rtldsa_counters_lock_table(struct rtl838x_switch_priv *priv, int port __maybe_unused)
void otto_stats_counters_lock_table(struct rtl838x_switch_priv *priv, int port __maybe_unused)
__acquires(&priv->counters_lock)
{
mutex_lock(&priv->counters_lock);
}
void rtldsa_counters_unlock_table(struct rtl838x_switch_priv *priv, int port __maybe_unused)
void otto_stats_counters_unlock_table(struct rtl838x_switch_priv *priv, int port __maybe_unused)
__releases(&priv->ports[port].counters.lock)
{
mutex_unlock(&priv->counters_lock);
}
static void rtldsa_counters_lock(struct rtl838x_switch_priv *priv, int port)
static void otto_stats_counters_lock(struct rtl838x_switch_priv *priv, int port)
{
priv->r->stat_counters_lock(priv, port);
}
static void rtldsa_counters_unlock(struct rtl838x_switch_priv *priv, int port)
static void otto_stats_counters_unlock(struct rtl838x_switch_priv *priv, int port)
{
priv->r->stat_counters_unlock(priv, port);
}
static void rtldsa_poll_counters(struct work_struct *work)
static void otto_stats_poll_counters(struct work_struct *work)
{
struct rtl838x_switch_priv *priv = container_of(to_delayed_work(work),
struct rtl838x_switch_priv,
@@ -623,16 +665,16 @@ static void rtldsa_poll_counters(struct work_struct *work)
if (!priv->ports[port].phy && !priv->ports[port].has_pcs)
continue;
rtldsa_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
rtldsa_counters_unlock(priv, port);
otto_stats_counters_lock(priv, port);
otto_stats_update_port_counters(priv, port);
otto_stats_counters_unlock(priv, port);
}
queue_delayed_work(priv->wq, &priv->counters_work,
priv->r->stat_counter_poll_interval);
}
void rtldsa_stats_init_counters(struct rtl838x_switch_priv *priv)
void otto_stats_init_counters(struct rtl838x_switch_priv *priv)
{
struct rtldsa_counter_state *counters;
@@ -647,12 +689,12 @@ void rtldsa_stats_init_counters(struct rtl838x_switch_priv *priv)
spin_lock_init(&counters->link_stat_lock);
}
INIT_DELAYED_WORK(&priv->counters_work, rtldsa_poll_counters);
INIT_DELAYED_WORK(&priv->counters_work, otto_stats_poll_counters);
queue_delayed_work(priv->wq, &priv->counters_work,
priv->r->stat_counter_poll_interval);
}
void rtldsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data)
void otto_stats_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data)
{
struct rtl838x_switch_priv *priv = ds->priv;
const struct rtldsa_mib_desc *mib_desc;
@@ -669,8 +711,8 @@ void rtldsa_get_strings(struct dsa_switch *ds, int port, u32 stringset, u8 *data
ethtool_puts(&data, mib_desc->list[i].name);
}
void rtldsa_get_ethtool_stats(struct dsa_switch *ds, int port,
u64 *data)
void otto_stats_get_ethtool(struct dsa_switch *ds, int port,
u64 *data)
{
struct rtl838x_switch_priv *priv = ds->priv;
const struct rtldsa_mib_desc *mib_desc;
@@ -682,11 +724,11 @@ void rtldsa_get_ethtool_stats(struct dsa_switch *ds, int port,
mib_desc = priv->r->mib_desc;
for (int i = 0; i < mib_desc->list_count; i++) {
mib_item = &mib_desc->list[i].item;
rtldsa_read_mib_item(priv, port, mib_item, &data[i]);
otto_stats_read_mib_item(priv, port, mib_item, &data[i]);
}
}
int rtldsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
int otto_stats_get_sset_count(struct dsa_switch *ds, int port, int sset)
{
struct rtl838x_switch_priv *priv = ds->priv;
@@ -699,8 +741,8 @@ int rtldsa_get_sset_count(struct dsa_switch *ds, int port, int sset)
return priv->r->mib_desc->list_count;
}
void rtldsa_get_eth_phy_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_phy_stats *phy_stats)
void otto_stats_get_eth_phy(struct dsa_switch *ds, int port,
struct ethtool_eth_phy_stats *phy_stats)
{
struct rtl838x_switch_priv *priv = ds->priv;
struct rtldsa_counter_state *counters = &priv->ports[port].counters;
@@ -708,17 +750,17 @@ void rtldsa_get_eth_phy_stats(struct dsa_switch *ds, int port,
if (port < 0 || port >= priv->r->cpu_port)
return;
rtldsa_counters_lock(priv, port);
otto_stats_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
otto_stats_update_port_counters(priv, port);
phy_stats->SymbolErrorDuringCarrier = counters->symbol_errors.val;
rtldsa_counters_unlock(priv, port);
otto_stats_counters_unlock(priv, port);
}
void rtldsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats)
void otto_stats_get_eth_mac(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats)
{
struct rtl838x_switch_priv *priv = ds->priv;
struct rtldsa_counter_state *counters = &priv->ports[port].counters;
@@ -726,9 +768,9 @@ void rtldsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
if (port < 0 || port >= priv->r->cpu_port)
return;
rtldsa_counters_lock(priv, port);
otto_stats_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
otto_stats_update_port_counters(priv, port);
/* Frame and octet counters are calculated based on RFC3635, while also
* taking into account that the behaviour of the hardware counters differs
@@ -765,11 +807,11 @@ void rtldsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
mac_stats->FrameCheckSequenceErrors = counters->crc_align_errors.val;
rtldsa_counters_unlock(priv, port);
otto_stats_counters_unlock(priv, port);
}
void rtldsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
void otto_stats_get_eth_ctrl(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats)
{
struct rtl838x_switch_priv *priv = ds->priv;
struct rtldsa_counter_state *counters = &priv->ports[port].counters;
@@ -777,18 +819,18 @@ void rtldsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
if (port < 0 || port >= priv->r->cpu_port)
return;
rtldsa_counters_lock(priv, port);
otto_stats_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
otto_stats_update_port_counters(priv, port);
ctrl_stats->UnsupportedOpcodesReceived = counters->unsupported_opcodes.val;
rtldsa_counters_unlock(priv, port);
otto_stats_counters_unlock(priv, port);
}
void rtldsa_get_rmon_stats(struct dsa_switch *ds, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
void otto_stats_get_rmon(struct dsa_switch *ds, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges)
{
struct rtl838x_switch_priv *priv = ds->priv;
const struct rtldsa_mib_desc *mib_desc;
@@ -799,9 +841,9 @@ void rtldsa_get_rmon_stats(struct dsa_switch *ds, int port,
mib_desc = priv->r->mib_desc;
rtldsa_counters_lock(priv, port);
otto_stats_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
otto_stats_update_port_counters(priv, port);
rmon_stats->undersize_pkts = counters->rx_undersize_pkts.val;
rmon_stats->oversize_pkts = counters->rx_oversize_pkts.val;
@@ -824,18 +866,18 @@ void rtldsa_get_rmon_stats(struct dsa_switch *ds, int port,
*ranges = mib_desc->rmon_ranges;
rtldsa_counters_unlock(priv, port);
otto_stats_counters_unlock(priv, port);
}
void rtldsa_update_counters_atomically(struct rtl838x_switch_priv *priv, int port)
void otto_stats_update_counters_atomically(struct rtl838x_switch_priv *priv, int port)
{
rtldsa_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
rtldsa_counters_unlock(priv, port);
otto_stats_counters_lock(priv, port);
otto_stats_update_port_counters(priv, port);
otto_stats_counters_unlock(priv, port);
}
void rtldsa_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s)
void otto_stats_get64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s)
{
struct rtl838x_switch_priv *priv = ds->priv;
struct rtldsa_counter_state *counters = &priv->ports[port].counters;
@@ -852,8 +894,8 @@ void rtldsa_get_stats64(struct dsa_switch *ds, int port,
spin_unlock(&counters->link_stat_lock);
}
void rtldsa_get_pause_stats(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats)
void otto_stats_get_pause(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats)
{
struct rtl838x_switch_priv *priv = ds->priv;
struct rtldsa_counter_state *counters = &priv->ports[port].counters;
@@ -861,12 +903,235 @@ void rtldsa_get_pause_stats(struct dsa_switch *ds, int port,
if (port < 0 || port >= priv->r->cpu_port)
return;
rtldsa_counters_lock(priv, port);
otto_stats_counters_lock(priv, port);
rtldsa_update_port_counters(priv, port);
otto_stats_update_port_counters(priv, port);
pause_stats->tx_pause_frames = counters->tx_pause_frames.val;
pause_stats->rx_pause_frames = counters->rx_pause_frames.val;
rtldsa_counters_unlock(priv, port);
otto_stats_counters_unlock(priv, port);
}
/*
* Packet counters share hardware memory with octet counters (2 packet counters
* per 1 octet block). Allocation relies on two complementary bitmaps:
*
* octet_cntr_use_bm: 0 = free block, 1 = used (or split into packet counters)
* packet_cntr_use_bm: 1 = free standalone counter, 0 = unavailable
*
* Allocation strategy:
* 1. Look for a free standalone counter from an already split block (bit = 1).
* 2. If none are free, claim a new octet block 'j', use counter index (2 * j),
* and mark counter (2 * j + 1) as available for future allocations.
*/
/**
* otto_stats_packet_cntr_alloc - Allocate a hardware packet counter.
* @priv: Switch driver private structure.
*
* Return: Counter index (>= 0) on success, or -1 if full.
*/
int otto_stats_packet_cntr_alloc(struct rtl838x_switch_priv *priv)
{
int idx, j, base = 0;
/* On SoCs where a PIE rule implicitly logs into the LOG table entry
* with its own rule ID (RTL930x), the tc cls_flower offload consumes
* one LOG counter per PIE rule and does not pass through this
* allocator. Keep that range reserved so a route counter handed out
* here cannot alias a flow's LOG entry.
*
* The offload is ingress-only, so PIE rule IDs currently populate
* only the lower half of that range, and a route counter's LOG entry
* (idx / 2) never dips below the upper half either. If egress PIE
* rules are ever wired up, this reservation needs to grow so their
* LOG entries stay clear of route counters too.
*/
if (priv->r->pie_rule_id_is_log_counter)
base = priv->r->n_pie_blocks * PIE_BLOCK_SIZE;
scoped_guard(mutex, &priv->reg_mutex) {
idx = find_next_bit(priv->packet_cntr_use_bm, priv->r->n_counters * 2, base);
if (idx >= priv->r->n_counters * 2) {
j = find_next_zero_bit(priv->octet_cntr_use_bm, priv->r->n_counters,
base / 2);
if (j >= priv->r->n_counters)
return -1;
__set_bit(j, priv->octet_cntr_use_bm);
idx = j * 2;
__set_bit(j * 2 + 1, priv->packet_cntr_use_bm);
} else {
__clear_bit(idx, priv->packet_cntr_use_bm);
}
}
return idx;
}
/**
* otto_stats_packet_cntr_free - Release a packet counter from otto_stats_packet_cntr_alloc().
* @priv: Switch driver private structure.
* @idx: Packet counter index to free; a negative id is ignored.
*
* Marks the counter free again and, once both halves of its octet block are
* free, returns the whole block to the octet counter pool.
*/
void otto_stats_packet_cntr_free(struct rtl838x_switch_priv *priv, int idx)
{
int j;
if (idx < 0 || idx >= priv->r->n_counters * 2)
return;
scoped_guard(mutex, &priv->reg_mutex) {
/* already free - guard against a double release */
if (test_bit(idx, priv->packet_cntr_use_bm))
return;
__set_bit(idx, priv->packet_cntr_use_bm);
j = idx / 2;
if (test_bit(j, priv->octet_cntr_use_bm) &&
test_bit(idx ^ 1, priv->packet_cntr_use_bm)) {
__clear_bit(idx, priv->packet_cntr_use_bm);
__clear_bit(idx ^ 1, priv->packet_cntr_use_bm);
__clear_bit(j, priv->octet_cntr_use_bm);
}
}
}
u32 otto_stats_838x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter)
{
u32 buf[2];
u32 v;
dev_dbg(priv->dev, "reading LOG packet counter %d\n", counter);
otto_table_read(RTL8380_TBL_LOG, counter / 2, &buf);
dev_dbg(priv->dev, "LOG entry: %08x %08x\n", buf[0], buf[1]);
if (counter % 2)
v = buf[0];
else
v = buf[1];
return v;
}
void otto_stats_838x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter)
{
int tbl = otto_table_acquire(RTL8380_TBL_LOG);
u32 buf[2];
dev_dbg(priv->dev, "clearing LOG packet counter %d\n", counter);
/*
* Two counters share one LOG table entry. Read the current entry
* first so clearing one half preserves the adjacent counter.
*/
__otto_table_read(tbl, counter / 2, &buf);
if (counter % 2)
buf[0] = 0;
else
buf[1] = 0;
__otto_table_write(tbl, counter / 2, &buf);
otto_table_release(tbl);
}
u32 otto_stats_839x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter)
{
u32 buf[2];
u32 v;
dev_dbg(priv->dev, "reading LOG packet counter %d\n", counter);
otto_table_read(RTL8390_TBL_LOG, counter / 2, &buf);
if (counter % 2)
v = buf[0];
else
v = buf[1];
return v;
}
void otto_stats_839x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter)
{
int tbl = otto_table_acquire(RTL8390_TBL_LOG);
u32 buf[2];
dev_dbg(priv->dev, "clearing LOG packet counter %d\n", counter);
/*
* Two counters share one LOG table entry. Read the current entry
* first so clearing one half preserves the adjacent counter.
*/
__otto_table_read(tbl, counter / 2, &buf);
if (counter % 2)
buf[0] = 0;
else
buf[1] = 0;
__otto_table_write(tbl, counter / 2, &buf);
otto_table_release(tbl);
}
/* A PIE rule logs its matched packets into the LOG table entry that carries
* its own rule ID, in data word 1 - one entry per rule. Counters handed out
* by otto_stats_packet_cntr_alloc() for L3 route statistics start above the PIE
* rule ID range and pack two 32-bit counters into one 64-bit LOG entry.
*/
#define RTL930X_PIE_RULE_IDS (rtldsa_930x_cfg.n_pie_blocks * PIE_BLOCK_SIZE)
u32 otto_stats_930x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter)
{
u32 buf[2];
u32 v;
dev_dbg(priv->dev, "reading LOG packet counter %d\n", counter);
if (counter < RTL930X_PIE_RULE_IDS) {
otto_table_read(RTL9300_TBL_LOG, counter, &buf);
v = buf[1];
} else {
/* Two counters share one entry, so the parity of counter picks
* which half of it to return.
*/
otto_table_read(RTL9300_TBL_LOG, counter / 2, &buf);
v = counter % 2 ? buf[0] : buf[1];
}
dev_dbg(priv->dev, "LOG entry: %08x %08x\n", buf[0], buf[1]);
return v;
}
void otto_stats_930x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter)
{
int tbl = otto_table_acquire(RTL9300_TBL_LOG);
u32 v[2];
dev_dbg(priv->dev, "clearing LOG packet counter %d\n", counter);
/* Read-modify-write: for a route counter the other 32-bit counter in
* the same 64-bit entry must be preserved; for a PIE rule ID the entry
* is ours alone but the RMW is harmless.
*/
if (counter < RTL930X_PIE_RULE_IDS) {
__otto_table_read(tbl, counter, &v);
v[1] = 0;
__otto_table_write(tbl, counter, &v);
} else {
__otto_table_read(tbl, counter / 2, &v);
v[counter % 2 ? 0 : 1] = 0;
__otto_table_write(tbl, counter / 2, &v);
}
otto_table_release(tbl);
}
@@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __RTL_OTTO_STATS_H
#define __RTL_OTTO_STATS_H
#ifndef _OTTO_STATS_H
#define _OTTO_STATS_H
#include <linux/ethtool.h>
#include <linux/jiffies.h>
@@ -177,36 +177,48 @@ struct ethtool_rmon_stats;
struct rtl838x_switch_priv;
struct rtnl_link_stats64;
void rtldsa_stats_init(struct rtl838x_switch_priv *priv);
void rtldsa_stats_init_counters(struct rtl838x_switch_priv *priv);
void otto_stats_init(struct rtl838x_switch_priv *priv);
void otto_stats_init_counters(struct rtl838x_switch_priv *priv);
void otto_stats_838x_init(struct rtl838x_switch_priv *priv);
u64 otto_stats_931x_port_table_read(int port, unsigned int mib_size,
unsigned int mib_offset, bool is_pvt);
void rtldsa_get_strings(struct dsa_switch *ds, int port, u32 stringset,
u8 *data);
void rtldsa_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data);
int rtldsa_get_sset_count(struct dsa_switch *ds, int port, int sset);
void rtldsa_get_eth_phy_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_phy_stats *phy_stats);
void rtldsa_get_eth_mac_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats);
void rtldsa_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats);
void rtldsa_get_rmon_stats(struct dsa_switch *ds, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges);
void rtldsa_get_stats64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s);
void rtldsa_get_pause_stats(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats);
int otto_stats_packet_cntr_alloc(struct rtl838x_switch_priv *priv);
void otto_stats_packet_cntr_free(struct rtl838x_switch_priv *priv, int idx);
u32 otto_stats_838x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter);
void otto_stats_838x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter);
u32 otto_stats_839x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter);
void otto_stats_839x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter);
u32 otto_stats_930x_packet_cntr_read(struct rtl838x_switch_priv *priv, int counter);
void otto_stats_930x_packet_cntr_clear(struct rtl838x_switch_priv *priv, int counter);
void rtldsa_counters_lock_register(struct rtl838x_switch_priv *priv, int port)
void otto_stats_get_strings(struct dsa_switch *ds, int port, u32 stringset,
u8 *data);
void otto_stats_get_ethtool(struct dsa_switch *ds, int port, u64 *data);
int otto_stats_get_sset_count(struct dsa_switch *ds, int port, int sset);
void otto_stats_get_eth_phy(struct dsa_switch *ds, int port,
struct ethtool_eth_phy_stats *phy_stats);
void otto_stats_get_eth_mac(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats);
void otto_stats_get_eth_ctrl(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats);
void otto_stats_get_rmon(struct dsa_switch *ds, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges);
void otto_stats_get64(struct dsa_switch *ds, int port,
struct rtnl_link_stats64 *s);
void otto_stats_get_pause(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats);
void otto_stats_counters_lock_register(struct rtl838x_switch_priv *priv, int port)
__acquires(&priv->ports[port].counters.lock);
void rtldsa_counters_unlock_register(struct rtl838x_switch_priv *priv, int port)
void otto_stats_counters_unlock_register(struct rtl838x_switch_priv *priv, int port)
__releases(&priv->ports[port].counters.lock);
void rtldsa_counters_lock_table(struct rtl838x_switch_priv *priv, int port)
void otto_stats_counters_lock_table(struct rtl838x_switch_priv *priv, int port)
__acquires(&priv->counters_lock);
void rtldsa_counters_unlock_table(struct rtl838x_switch_priv *priv, int port)
void otto_stats_counters_unlock_table(struct rtl838x_switch_priv *priv, int port)
__releases(&priv->ports[port].counters.lock);
void rtldsa_update_counters_atomically(struct rtl838x_switch_priv *priv,
int port);
void otto_stats_update_counters_atomically(struct rtl838x_switch_priv *priv,
int port);
#endif /* __RTL_OTTO_STATS_H */
#endif /* _OTTO_STATS_H */
@@ -481,7 +481,7 @@ static int otto_tc_configure_flower(struct rtl838x_switch_priv *priv,
/* The only caller, otto_tc_pie_cls_flower_add(), is RTL930x-only, where
* the PIE rule ID is also the LOG table counter ID: the counter is
* implied by the rule and only known once pie_rule_add() has assigned
* the ID. That range is kept out of rtldsa_packet_cntr_alloc(), so the
* the ID. That range is kept out of otto_stats_packet_cntr_alloc(), so the
* implied LOG entry cannot alias an L3 route PIE rule's counter.
*/
flow->rule.log_sel = true;