mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-07 14:19:35 +00:00
net: airoha: Add FLOW_CLS_STATS callback support
Introduce per-flow stats accounting to the flowtable hw offload in the airoha_eth driver. Flow stats are split in the PPE and NPU modules: - PPE: accounts for high 32bit of per-flow stats - NPU: accounts for low 32bit of per-flow stats FLOW_CLS_STATS can be enabled or disabled at compile time. Signed-off-by: Lorenzo Bianconi <lorenzo@kernel.org> Reviewed-by: Simon Horman <horms@kernel.org> Link: https://patch.msgid.link/20250516-airoha-en7581-flowstats-v2-2-06d5fbf28984@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
c52918744e
commit
b81e0f2b58
@ -24,4 +24,11 @@ config NET_AIROHA
|
|||||||
This driver supports the gigabit ethernet MACs in the
|
This driver supports the gigabit ethernet MACs in the
|
||||||
Airoha SoC family.
|
Airoha SoC family.
|
||||||
|
|
||||||
|
config NET_AIROHA_FLOW_STATS
|
||||||
|
default y
|
||||||
|
bool "Airoha flow stats"
|
||||||
|
depends on NET_AIROHA && NET_AIROHA_NPU
|
||||||
|
help
|
||||||
|
Enable Aiorha flowtable statistic counters.
|
||||||
|
|
||||||
endif #NET_VENDOR_AIROHA
|
endif #NET_VENDOR_AIROHA
|
||||||
|
@ -50,6 +50,14 @@
|
|||||||
#define PPE_NUM 2
|
#define PPE_NUM 2
|
||||||
#define PPE1_SRAM_NUM_ENTRIES (8 * 1024)
|
#define PPE1_SRAM_NUM_ENTRIES (8 * 1024)
|
||||||
#define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES)
|
#define PPE_SRAM_NUM_ENTRIES (2 * PPE1_SRAM_NUM_ENTRIES)
|
||||||
|
#ifdef CONFIG_NET_AIROHA_FLOW_STATS
|
||||||
|
#define PPE1_STATS_NUM_ENTRIES (4 * 1024)
|
||||||
|
#else
|
||||||
|
#define PPE1_STATS_NUM_ENTRIES 0
|
||||||
|
#endif /* CONFIG_NET_AIROHA_FLOW_STATS */
|
||||||
|
#define PPE_STATS_NUM_ENTRIES (2 * PPE1_STATS_NUM_ENTRIES)
|
||||||
|
#define PPE1_SRAM_NUM_DATA_ENTRIES (PPE1_SRAM_NUM_ENTRIES - PPE1_STATS_NUM_ENTRIES)
|
||||||
|
#define PPE_SRAM_NUM_DATA_ENTRIES (2 * PPE1_SRAM_NUM_DATA_ENTRIES)
|
||||||
#define PPE_DRAM_NUM_ENTRIES (16 * 1024)
|
#define PPE_DRAM_NUM_ENTRIES (16 * 1024)
|
||||||
#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES)
|
#define PPE_NUM_ENTRIES (PPE_SRAM_NUM_ENTRIES + PPE_DRAM_NUM_ENTRIES)
|
||||||
#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1)
|
#define PPE_HASH_MASK (PPE_NUM_ENTRIES - 1)
|
||||||
@ -261,6 +269,8 @@ struct airoha_foe_mac_info {
|
|||||||
|
|
||||||
u16 pppoe_id;
|
u16 pppoe_id;
|
||||||
u16 src_mac_lo;
|
u16 src_mac_lo;
|
||||||
|
|
||||||
|
u32 meter;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define AIROHA_FOE_IB1_UNBIND_PREBIND BIT(24)
|
#define AIROHA_FOE_IB1_UNBIND_PREBIND BIT(24)
|
||||||
@ -296,6 +306,11 @@ struct airoha_foe_mac_info {
|
|||||||
#define AIROHA_FOE_TUNNEL BIT(6)
|
#define AIROHA_FOE_TUNNEL BIT(6)
|
||||||
#define AIROHA_FOE_TUNNEL_ID GENMASK(5, 0)
|
#define AIROHA_FOE_TUNNEL_ID GENMASK(5, 0)
|
||||||
|
|
||||||
|
#define AIROHA_FOE_TUNNEL_MTU GENMASK(31, 16)
|
||||||
|
#define AIROHA_FOE_ACNT_GRP3 GENMASK(15, 9)
|
||||||
|
#define AIROHA_FOE_METER_GRP3 GENMASK(8, 5)
|
||||||
|
#define AIROHA_FOE_METER_GRP2 GENMASK(4, 0)
|
||||||
|
|
||||||
struct airoha_foe_bridge {
|
struct airoha_foe_bridge {
|
||||||
u32 dest_mac_hi;
|
u32 dest_mac_hi;
|
||||||
|
|
||||||
@ -379,6 +394,8 @@ struct airoha_foe_ipv6 {
|
|||||||
u32 ib2;
|
u32 ib2;
|
||||||
|
|
||||||
struct airoha_foe_mac_info_common l2;
|
struct airoha_foe_mac_info_common l2;
|
||||||
|
|
||||||
|
u32 meter;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct airoha_foe_entry {
|
struct airoha_foe_entry {
|
||||||
@ -397,6 +414,16 @@ struct airoha_foe_entry {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct airoha_foe_stats {
|
||||||
|
u32 bytes;
|
||||||
|
u32 packets;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct airoha_foe_stats64 {
|
||||||
|
u64 bytes;
|
||||||
|
u64 packets;
|
||||||
|
};
|
||||||
|
|
||||||
struct airoha_flow_data {
|
struct airoha_flow_data {
|
||||||
struct ethhdr eth;
|
struct ethhdr eth;
|
||||||
|
|
||||||
@ -447,6 +474,7 @@ struct airoha_flow_table_entry {
|
|||||||
struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */
|
struct hlist_node l2_subflow_node; /* PPE L2 subflow entry */
|
||||||
u32 hash;
|
u32 hash;
|
||||||
|
|
||||||
|
struct airoha_foe_stats64 stats;
|
||||||
enum airoha_flow_entry_type type;
|
enum airoha_flow_entry_type type;
|
||||||
|
|
||||||
struct rhash_head node;
|
struct rhash_head node;
|
||||||
@ -523,6 +551,9 @@ struct airoha_ppe {
|
|||||||
struct hlist_head *foe_flow;
|
struct hlist_head *foe_flow;
|
||||||
u16 foe_check_time[PPE_NUM_ENTRIES];
|
u16 foe_check_time[PPE_NUM_ENTRIES];
|
||||||
|
|
||||||
|
struct airoha_foe_stats *foe_stats;
|
||||||
|
dma_addr_t foe_stats_dma;
|
||||||
|
|
||||||
struct dentry *debugfs_dir;
|
struct dentry *debugfs_dir;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -582,6 +613,8 @@ int airoha_ppe_init(struct airoha_eth *eth);
|
|||||||
void airoha_ppe_deinit(struct airoha_eth *eth);
|
void airoha_ppe_deinit(struct airoha_eth *eth);
|
||||||
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
||||||
u32 hash);
|
u32 hash);
|
||||||
|
void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,
|
||||||
|
struct airoha_foe_stats64 *stats);
|
||||||
|
|
||||||
#ifdef CONFIG_DEBUG_FS
|
#ifdef CONFIG_DEBUG_FS
|
||||||
int airoha_ppe_debugfs_init(struct airoha_ppe *ppe);
|
int airoha_ppe_debugfs_init(struct airoha_ppe *ppe);
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <linux/of_reserved_mem.h>
|
#include <linux/of_reserved_mem.h>
|
||||||
#include <linux/regmap.h>
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
|
#include "airoha_eth.h"
|
||||||
#include "airoha_npu.h"
|
#include "airoha_npu.h"
|
||||||
|
|
||||||
#define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin"
|
#define NPU_EN7581_FIRMWARE_DATA "airoha/en7581_npu_data.bin"
|
||||||
@ -72,6 +73,7 @@ enum {
|
|||||||
PPE_FUNC_SET_WAIT_HWNAT_INIT,
|
PPE_FUNC_SET_WAIT_HWNAT_INIT,
|
||||||
PPE_FUNC_SET_WAIT_HWNAT_DEINIT,
|
PPE_FUNC_SET_WAIT_HWNAT_DEINIT,
|
||||||
PPE_FUNC_SET_WAIT_API,
|
PPE_FUNC_SET_WAIT_API,
|
||||||
|
PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
@ -115,6 +117,10 @@ struct ppe_mbox_data {
|
|||||||
u32 size;
|
u32 size;
|
||||||
u32 data;
|
u32 data;
|
||||||
} set_info;
|
} set_info;
|
||||||
|
struct {
|
||||||
|
u32 npu_stats_addr;
|
||||||
|
u32 foe_stats_addr;
|
||||||
|
} stats_info;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -351,7 +357,40 @@ static int airoha_npu_foe_commit_entry(struct airoha_npu *npu,
|
|||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct airoha_npu *airoha_npu_get(struct device *dev)
|
static int airoha_npu_stats_setup(struct airoha_npu *npu,
|
||||||
|
dma_addr_t foe_stats_addr)
|
||||||
|
{
|
||||||
|
int err, size = PPE_STATS_NUM_ENTRIES * sizeof(*npu->stats);
|
||||||
|
struct ppe_mbox_data *ppe_data;
|
||||||
|
|
||||||
|
if (!size) /* flow stats are disabled */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ppe_data = kzalloc(sizeof(*ppe_data), GFP_ATOMIC);
|
||||||
|
if (!ppe_data)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
ppe_data->func_type = NPU_OP_SET;
|
||||||
|
ppe_data->func_id = PPE_FUNC_SET_WAIT_FLOW_STATS_SETUP;
|
||||||
|
ppe_data->stats_info.foe_stats_addr = foe_stats_addr;
|
||||||
|
|
||||||
|
err = airoha_npu_send_msg(npu, NPU_FUNC_PPE, ppe_data,
|
||||||
|
sizeof(*ppe_data));
|
||||||
|
if (err)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
npu->stats = devm_ioremap(npu->dev,
|
||||||
|
ppe_data->stats_info.npu_stats_addr,
|
||||||
|
size);
|
||||||
|
if (!npu->stats)
|
||||||
|
err = -ENOMEM;
|
||||||
|
out:
|
||||||
|
kfree(ppe_data);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
@ -389,6 +428,17 @@ struct airoha_npu *airoha_npu_get(struct device *dev)
|
|||||||
goto error_module_put;
|
goto error_module_put;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stats_addr) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = airoha_npu_stats_setup(npu, *stats_addr);
|
||||||
|
if (err) {
|
||||||
|
dev_err(dev, "failed to allocate npu stats buffer\n");
|
||||||
|
npu = ERR_PTR(err);
|
||||||
|
goto error_module_put;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return npu;
|
return npu;
|
||||||
|
|
||||||
error_module_put:
|
error_module_put:
|
||||||
|
@ -17,6 +17,8 @@ struct airoha_npu {
|
|||||||
struct work_struct wdt_work;
|
struct work_struct wdt_work;
|
||||||
} cores[NPU_NUM_CORES];
|
} cores[NPU_NUM_CORES];
|
||||||
|
|
||||||
|
struct airoha_foe_stats __iomem *stats;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int (*ppe_init)(struct airoha_npu *npu);
|
int (*ppe_init)(struct airoha_npu *npu);
|
||||||
int (*ppe_deinit)(struct airoha_npu *npu);
|
int (*ppe_deinit)(struct airoha_npu *npu);
|
||||||
@ -30,5 +32,5 @@ struct airoha_npu {
|
|||||||
} ops;
|
} ops;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct airoha_npu *airoha_npu_get(struct device *dev);
|
struct airoha_npu *airoha_npu_get(struct device *dev, dma_addr_t *stats_addr);
|
||||||
void airoha_npu_put(struct airoha_npu *npu);
|
void airoha_npu_put(struct airoha_npu *npu);
|
||||||
|
@ -102,7 +102,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
|
|||||||
|
|
||||||
if (airoha_ppe2_is_enabled(eth)) {
|
if (airoha_ppe2_is_enabled(eth)) {
|
||||||
sram_num_entries =
|
sram_num_entries =
|
||||||
PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_ENTRIES);
|
PPE_RAM_NUM_ENTRIES_SHIFT(PPE1_SRAM_NUM_DATA_ENTRIES);
|
||||||
airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
|
airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
|
||||||
PPE_SRAM_TB_NUM_ENTRY_MASK |
|
PPE_SRAM_TB_NUM_ENTRY_MASK |
|
||||||
PPE_DRAM_TB_NUM_ENTRY_MASK,
|
PPE_DRAM_TB_NUM_ENTRY_MASK,
|
||||||
@ -119,7 +119,7 @@ static void airoha_ppe_hw_init(struct airoha_ppe *ppe)
|
|||||||
dram_num_entries));
|
dram_num_entries));
|
||||||
} else {
|
} else {
|
||||||
sram_num_entries =
|
sram_num_entries =
|
||||||
PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_ENTRIES);
|
PPE_RAM_NUM_ENTRIES_SHIFT(PPE_SRAM_NUM_DATA_ENTRIES);
|
||||||
airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
|
airoha_fe_rmw(eth, REG_PPE_TB_CFG(0),
|
||||||
PPE_SRAM_TB_NUM_ENTRY_MASK |
|
PPE_SRAM_TB_NUM_ENTRY_MASK |
|
||||||
PPE_DRAM_TB_NUM_ENTRY_MASK,
|
PPE_DRAM_TB_NUM_ENTRY_MASK,
|
||||||
@ -417,6 +417,77 @@ static u32 airoha_ppe_foe_get_entry_hash(struct airoha_foe_entry *hwe)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static u32 airoha_ppe_foe_get_flow_stats_index(struct airoha_ppe *ppe, u32 hash)
|
||||||
|
{
|
||||||
|
if (!airoha_ppe2_is_enabled(ppe->eth))
|
||||||
|
return hash;
|
||||||
|
|
||||||
|
return hash >= PPE_STATS_NUM_ENTRIES ? hash - PPE1_STATS_NUM_ENTRIES
|
||||||
|
: hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void airoha_ppe_foe_flow_stat_entry_reset(struct airoha_ppe *ppe,
|
||||||
|
struct airoha_npu *npu,
|
||||||
|
int index)
|
||||||
|
{
|
||||||
|
memset_io(&npu->stats[index], 0, sizeof(*npu->stats));
|
||||||
|
memset(&ppe->foe_stats[index], 0, sizeof(*ppe->foe_stats));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void airoha_ppe_foe_flow_stats_reset(struct airoha_ppe *ppe,
|
||||||
|
struct airoha_npu *npu)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < PPE_STATS_NUM_ENTRIES; i++)
|
||||||
|
airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void airoha_ppe_foe_flow_stats_update(struct airoha_ppe *ppe,
|
||||||
|
struct airoha_npu *npu,
|
||||||
|
struct airoha_foe_entry *hwe,
|
||||||
|
u32 hash)
|
||||||
|
{
|
||||||
|
int type = FIELD_GET(AIROHA_FOE_IB1_BIND_PACKET_TYPE, hwe->ib1);
|
||||||
|
u32 index, pse_port, val, *data, *ib2, *meter;
|
||||||
|
u8 nbq;
|
||||||
|
|
||||||
|
index = airoha_ppe_foe_get_flow_stats_index(ppe, hash);
|
||||||
|
if (index >= PPE_STATS_NUM_ENTRIES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (type == PPE_PKT_TYPE_BRIDGE) {
|
||||||
|
data = &hwe->bridge.data;
|
||||||
|
ib2 = &hwe->bridge.ib2;
|
||||||
|
meter = &hwe->bridge.l2.meter;
|
||||||
|
} else if (type >= PPE_PKT_TYPE_IPV6_ROUTE_3T) {
|
||||||
|
data = &hwe->ipv6.data;
|
||||||
|
ib2 = &hwe->ipv6.ib2;
|
||||||
|
meter = &hwe->ipv6.meter;
|
||||||
|
} else {
|
||||||
|
data = &hwe->ipv4.data;
|
||||||
|
ib2 = &hwe->ipv4.ib2;
|
||||||
|
meter = &hwe->ipv4.l2.meter;
|
||||||
|
}
|
||||||
|
|
||||||
|
airoha_ppe_foe_flow_stat_entry_reset(ppe, npu, index);
|
||||||
|
|
||||||
|
val = FIELD_GET(AIROHA_FOE_CHANNEL | AIROHA_FOE_QID, *data);
|
||||||
|
*data = (*data & ~AIROHA_FOE_ACTDP) |
|
||||||
|
FIELD_PREP(AIROHA_FOE_ACTDP, val);
|
||||||
|
|
||||||
|
val = *ib2 & (AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT |
|
||||||
|
AIROHA_FOE_IB2_PSE_QOS | AIROHA_FOE_IB2_FAST_PATH);
|
||||||
|
*meter |= FIELD_PREP(AIROHA_FOE_TUNNEL_MTU, val);
|
||||||
|
|
||||||
|
pse_port = FIELD_GET(AIROHA_FOE_IB2_PSE_PORT, *ib2);
|
||||||
|
nbq = pse_port == 1 ? 6 : 5;
|
||||||
|
*ib2 &= ~(AIROHA_FOE_IB2_NBQ | AIROHA_FOE_IB2_PSE_PORT |
|
||||||
|
AIROHA_FOE_IB2_PSE_QOS);
|
||||||
|
*ib2 |= FIELD_PREP(AIROHA_FOE_IB2_PSE_PORT, 6) |
|
||||||
|
FIELD_PREP(AIROHA_FOE_IB2_NBQ, nbq);
|
||||||
|
}
|
||||||
|
|
||||||
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
struct airoha_foe_entry *airoha_ppe_foe_get_entry(struct airoha_ppe *ppe,
|
||||||
u32 hash)
|
u32 hash)
|
||||||
{
|
{
|
||||||
@ -470,6 +541,8 @@ static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
|
|||||||
struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
|
struct airoha_foe_entry *hwe = ppe->foe + hash * sizeof(*hwe);
|
||||||
u32 ts = airoha_ppe_get_timestamp(ppe);
|
u32 ts = airoha_ppe_get_timestamp(ppe);
|
||||||
struct airoha_eth *eth = ppe->eth;
|
struct airoha_eth *eth = ppe->eth;
|
||||||
|
struct airoha_npu *npu;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1));
|
memcpy(&hwe->d, &e->d, sizeof(*hwe) - sizeof(hwe->ib1));
|
||||||
wmb();
|
wmb();
|
||||||
@ -478,25 +551,28 @@ static int airoha_ppe_foe_commit_entry(struct airoha_ppe *ppe,
|
|||||||
e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts);
|
e->ib1 |= FIELD_PREP(AIROHA_FOE_IB1_BIND_TIMESTAMP, ts);
|
||||||
hwe->ib1 = e->ib1;
|
hwe->ib1 = e->ib1;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
npu = rcu_dereference(eth->npu);
|
||||||
|
if (!npu) {
|
||||||
|
err = -ENODEV;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
airoha_ppe_foe_flow_stats_update(ppe, npu, hwe, hash);
|
||||||
|
|
||||||
if (hash < PPE_SRAM_NUM_ENTRIES) {
|
if (hash < PPE_SRAM_NUM_ENTRIES) {
|
||||||
dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe);
|
dma_addr_t addr = ppe->foe_dma + hash * sizeof(*hwe);
|
||||||
bool ppe2 = airoha_ppe2_is_enabled(eth) &&
|
bool ppe2 = airoha_ppe2_is_enabled(eth) &&
|
||||||
hash >= PPE1_SRAM_NUM_ENTRIES;
|
hash >= PPE1_SRAM_NUM_ENTRIES;
|
||||||
struct airoha_npu *npu;
|
|
||||||
int err = -ENODEV;
|
|
||||||
|
|
||||||
rcu_read_lock();
|
err = npu->ops.ppe_foe_commit_entry(npu, addr, sizeof(*hwe),
|
||||||
npu = rcu_dereference(eth->npu);
|
hash, ppe2);
|
||||||
if (npu)
|
|
||||||
err = npu->ops.ppe_foe_commit_entry(npu, addr,
|
|
||||||
sizeof(*hwe), hash,
|
|
||||||
ppe2);
|
|
||||||
rcu_read_unlock();
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
unlock:
|
||||||
|
rcu_read_unlock();
|
||||||
|
|
||||||
return 0;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe,
|
static void airoha_ppe_foe_remove_flow(struct airoha_ppe *ppe,
|
||||||
@ -582,6 +658,7 @@ airoha_ppe_foe_commit_subflow_entry(struct airoha_ppe *ppe,
|
|||||||
l2->common.etype = ETH_P_IPV6;
|
l2->common.etype = ETH_P_IPV6;
|
||||||
|
|
||||||
hwe.bridge.ib2 = e->data.bridge.ib2;
|
hwe.bridge.ib2 = e->data.bridge.ib2;
|
||||||
|
hwe.bridge.data = e->data.bridge.data;
|
||||||
airoha_ppe_foe_commit_entry(ppe, &hwe, hash);
|
airoha_ppe_foe_commit_entry(ppe, &hwe, hash);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -681,6 +758,98 @@ static int airoha_ppe_foe_flow_commit_entry(struct airoha_ppe *ppe,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int airoha_ppe_get_entry_idle_time(struct airoha_ppe *ppe, u32 ib1)
|
||||||
|
{
|
||||||
|
u32 state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1);
|
||||||
|
u32 ts, ts_mask, now = airoha_ppe_get_timestamp(ppe);
|
||||||
|
int idle;
|
||||||
|
|
||||||
|
if (state == AIROHA_FOE_STATE_BIND) {
|
||||||
|
ts = FIELD_GET(AIROHA_FOE_IB1_BIND_TIMESTAMP, ib1);
|
||||||
|
ts_mask = AIROHA_FOE_IB1_BIND_TIMESTAMP;
|
||||||
|
} else {
|
||||||
|
ts = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, ib1);
|
||||||
|
now = FIELD_GET(AIROHA_FOE_IB1_UNBIND_TIMESTAMP, now);
|
||||||
|
ts_mask = AIROHA_FOE_IB1_UNBIND_TIMESTAMP;
|
||||||
|
}
|
||||||
|
idle = now - ts;
|
||||||
|
|
||||||
|
return idle < 0 ? idle + ts_mask + 1 : idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
airoha_ppe_foe_flow_l2_entry_update(struct airoha_ppe *ppe,
|
||||||
|
struct airoha_flow_table_entry *e)
|
||||||
|
{
|
||||||
|
int min_idle = airoha_ppe_get_entry_idle_time(ppe, e->data.ib1);
|
||||||
|
struct airoha_flow_table_entry *iter;
|
||||||
|
struct hlist_node *n;
|
||||||
|
|
||||||
|
lockdep_assert_held(&ppe_lock);
|
||||||
|
|
||||||
|
hlist_for_each_entry_safe(iter, n, &e->l2_flows, l2_subflow_node) {
|
||||||
|
struct airoha_foe_entry *hwe;
|
||||||
|
u32 ib1, state;
|
||||||
|
int idle;
|
||||||
|
|
||||||
|
hwe = airoha_ppe_foe_get_entry(ppe, iter->hash);
|
||||||
|
ib1 = READ_ONCE(hwe->ib1);
|
||||||
|
|
||||||
|
state = FIELD_GET(AIROHA_FOE_IB1_BIND_STATE, ib1);
|
||||||
|
if (state != AIROHA_FOE_STATE_BIND) {
|
||||||
|
iter->hash = 0xffff;
|
||||||
|
airoha_ppe_foe_remove_flow(ppe, iter);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
idle = airoha_ppe_get_entry_idle_time(ppe, ib1);
|
||||||
|
if (idle >= min_idle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
min_idle = idle;
|
||||||
|
e->data.ib1 &= ~AIROHA_FOE_IB1_BIND_TIMESTAMP;
|
||||||
|
e->data.ib1 |= ib1 & AIROHA_FOE_IB1_BIND_TIMESTAMP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void airoha_ppe_foe_flow_entry_update(struct airoha_ppe *ppe,
|
||||||
|
struct airoha_flow_table_entry *e)
|
||||||
|
{
|
||||||
|
struct airoha_foe_entry *hwe_p, hwe = {};
|
||||||
|
|
||||||
|
spin_lock_bh(&ppe_lock);
|
||||||
|
|
||||||
|
if (e->type == FLOW_TYPE_L2) {
|
||||||
|
airoha_ppe_foe_flow_l2_entry_update(ppe, e);
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e->hash == 0xffff)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
hwe_p = airoha_ppe_foe_get_entry(ppe, e->hash);
|
||||||
|
if (!hwe_p)
|
||||||
|
goto unlock;
|
||||||
|
|
||||||
|
memcpy(&hwe, hwe_p, sizeof(*hwe_p));
|
||||||
|
if (!airoha_ppe_foe_compare_entry(e, &hwe)) {
|
||||||
|
e->hash = 0xffff;
|
||||||
|
goto unlock;
|
||||||
|
}
|
||||||
|
|
||||||
|
e->data.ib1 = hwe.ib1;
|
||||||
|
unlock:
|
||||||
|
spin_unlock_bh(&ppe_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_ppe_entry_idle_time(struct airoha_ppe *ppe,
|
||||||
|
struct airoha_flow_table_entry *e)
|
||||||
|
{
|
||||||
|
airoha_ppe_foe_flow_entry_update(ppe, e);
|
||||||
|
|
||||||
|
return airoha_ppe_get_entry_idle_time(ppe, e->data.ib1);
|
||||||
|
}
|
||||||
|
|
||||||
static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port,
|
static int airoha_ppe_flow_offload_replace(struct airoha_gdm_port *port,
|
||||||
struct flow_cls_offload *f)
|
struct flow_cls_offload *f)
|
||||||
{
|
{
|
||||||
@ -896,6 +1065,60 @@ static int airoha_ppe_flow_offload_destroy(struct airoha_gdm_port *port,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void airoha_ppe_foe_entry_get_stats(struct airoha_ppe *ppe, u32 hash,
|
||||||
|
struct airoha_foe_stats64 *stats)
|
||||||
|
{
|
||||||
|
u32 index = airoha_ppe_foe_get_flow_stats_index(ppe, hash);
|
||||||
|
struct airoha_eth *eth = ppe->eth;
|
||||||
|
struct airoha_npu *npu;
|
||||||
|
|
||||||
|
if (index >= PPE_STATS_NUM_ENTRIES)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
|
||||||
|
npu = rcu_dereference(eth->npu);
|
||||||
|
if (npu) {
|
||||||
|
u64 packets = ppe->foe_stats[index].packets;
|
||||||
|
u64 bytes = ppe->foe_stats[index].bytes;
|
||||||
|
struct airoha_foe_stats npu_stats;
|
||||||
|
|
||||||
|
memcpy_fromio(&npu_stats, &npu->stats[index],
|
||||||
|
sizeof(*npu->stats));
|
||||||
|
stats->packets = packets << 32 | npu_stats.packets;
|
||||||
|
stats->bytes = bytes << 32 | npu_stats.bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int airoha_ppe_flow_offload_stats(struct airoha_gdm_port *port,
|
||||||
|
struct flow_cls_offload *f)
|
||||||
|
{
|
||||||
|
struct airoha_eth *eth = port->qdma->eth;
|
||||||
|
struct airoha_flow_table_entry *e;
|
||||||
|
u32 idle;
|
||||||
|
|
||||||
|
e = rhashtable_lookup(ð->flow_table, &f->cookie,
|
||||||
|
airoha_flow_table_params);
|
||||||
|
if (!e)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
|
idle = airoha_ppe_entry_idle_time(eth->ppe, e);
|
||||||
|
f->stats.lastused = jiffies - idle * HZ;
|
||||||
|
|
||||||
|
if (e->hash != 0xffff) {
|
||||||
|
struct airoha_foe_stats64 stats = {};
|
||||||
|
|
||||||
|
airoha_ppe_foe_entry_get_stats(eth->ppe, e->hash, &stats);
|
||||||
|
f->stats.pkts += (stats.packets - e->stats.packets);
|
||||||
|
f->stats.bytes += (stats.bytes - e->stats.bytes);
|
||||||
|
e->stats = stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port,
|
static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port,
|
||||||
struct flow_cls_offload *f)
|
struct flow_cls_offload *f)
|
||||||
{
|
{
|
||||||
@ -904,6 +1127,8 @@ static int airoha_ppe_flow_offload_cmd(struct airoha_gdm_port *port,
|
|||||||
return airoha_ppe_flow_offload_replace(port, f);
|
return airoha_ppe_flow_offload_replace(port, f);
|
||||||
case FLOW_CLS_DESTROY:
|
case FLOW_CLS_DESTROY:
|
||||||
return airoha_ppe_flow_offload_destroy(port, f);
|
return airoha_ppe_flow_offload_destroy(port, f);
|
||||||
|
case FLOW_CLS_STATS:
|
||||||
|
return airoha_ppe_flow_offload_stats(port, f);
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -929,11 +1154,12 @@ static int airoha_ppe_flush_sram_entries(struct airoha_ppe *ppe,
|
|||||||
|
|
||||||
static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth)
|
static struct airoha_npu *airoha_ppe_npu_get(struct airoha_eth *eth)
|
||||||
{
|
{
|
||||||
struct airoha_npu *npu = airoha_npu_get(eth->dev);
|
struct airoha_npu *npu = airoha_npu_get(eth->dev,
|
||||||
|
ð->ppe->foe_stats_dma);
|
||||||
|
|
||||||
if (IS_ERR(npu)) {
|
if (IS_ERR(npu)) {
|
||||||
request_module("airoha-npu");
|
request_module("airoha-npu");
|
||||||
npu = airoha_npu_get(eth->dev);
|
npu = airoha_npu_get(eth->dev, ð->ppe->foe_stats_dma);
|
||||||
}
|
}
|
||||||
|
|
||||||
return npu;
|
return npu;
|
||||||
@ -956,6 +1182,8 @@ static int airoha_ppe_offload_setup(struct airoha_eth *eth)
|
|||||||
if (err)
|
if (err)
|
||||||
goto error_npu_put;
|
goto error_npu_put;
|
||||||
|
|
||||||
|
airoha_ppe_foe_flow_stats_reset(eth->ppe, npu);
|
||||||
|
|
||||||
rcu_assign_pointer(eth->npu, npu);
|
rcu_assign_pointer(eth->npu, npu);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
|
|
||||||
@ -1027,6 +1255,15 @@ int airoha_ppe_init(struct airoha_eth *eth)
|
|||||||
if (!ppe->foe_flow)
|
if (!ppe->foe_flow)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
foe_size = PPE_STATS_NUM_ENTRIES * sizeof(*ppe->foe_stats);
|
||||||
|
if (foe_size) {
|
||||||
|
ppe->foe_stats = dmam_alloc_coherent(eth->dev, foe_size,
|
||||||
|
&ppe->foe_stats_dma,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!ppe->foe_stats)
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
err = rhashtable_init(ð->flow_table, &airoha_flow_table_params);
|
err = rhashtable_init(ð->flow_table, &airoha_flow_table_params);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
@ -61,6 +61,7 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
|
|||||||
u16 *src_port = NULL, *dest_port = NULL;
|
u16 *src_port = NULL, *dest_port = NULL;
|
||||||
struct airoha_foe_mac_info_common *l2;
|
struct airoha_foe_mac_info_common *l2;
|
||||||
unsigned char h_source[ETH_ALEN] = {};
|
unsigned char h_source[ETH_ALEN] = {};
|
||||||
|
struct airoha_foe_stats64 stats = {};
|
||||||
unsigned char h_dest[ETH_ALEN];
|
unsigned char h_dest[ETH_ALEN];
|
||||||
struct airoha_foe_entry *hwe;
|
struct airoha_foe_entry *hwe;
|
||||||
u32 type, state, ib2, data;
|
u32 type, state, ib2, data;
|
||||||
@ -144,14 +145,18 @@ static int airoha_ppe_debugfs_foe_show(struct seq_file *m, void *private,
|
|||||||
cpu_to_be16(hwe->ipv4.l2.src_mac_lo);
|
cpu_to_be16(hwe->ipv4.l2.src_mac_lo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
airoha_ppe_foe_entry_get_stats(ppe, i, &stats);
|
||||||
|
|
||||||
*((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi);
|
*((__be32 *)h_dest) = cpu_to_be32(l2->dest_mac_hi);
|
||||||
*((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo);
|
*((__be16 *)&h_dest[4]) = cpu_to_be16(l2->dest_mac_lo);
|
||||||
*((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi);
|
*((__be32 *)h_source) = cpu_to_be32(l2->src_mac_hi);
|
||||||
|
|
||||||
seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x"
|
seq_printf(m, " eth=%pM->%pM etype=%04x data=%08x"
|
||||||
" vlan=%d,%d ib1=%08x ib2=%08x\n",
|
" vlan=%d,%d ib1=%08x ib2=%08x"
|
||||||
|
" packets=%llu bytes=%llu\n",
|
||||||
h_source, h_dest, l2->etype, data,
|
h_source, h_dest, l2->etype, data,
|
||||||
l2->vlan1, l2->vlan2, hwe->ib1, ib2);
|
l2->vlan1, l2->vlan2, hwe->ib1, ib2,
|
||||||
|
stats.packets, stats.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user