drivers: net: ti: am65-cpsw-nuss: implement ndo_fdb_dump

When the cpsw is operated in switch-mode allow dumping of the ale entries.
This way userspace can read the fdb without special means (ti switch-config)

Signed-off-by: Jan Sondhauss <jan.sondhauss@wago.com>
This commit is contained in:
Jan Sondhauss
2024-03-05 13:51:48 +01:00
committed by Oleg Karfich
parent 742023c99f
commit 6727c3d86f
3 changed files with 125 additions and 0 deletions
+77
View File
@@ -1467,6 +1467,82 @@ static void am65_cpsw_nuss_ndo_get_stats(struct net_device *dev,
stats->tx_dropped = dev->stats.tx_dropped;
}
struct am65_cpsw_dump_ctx {
struct net_device *dev;
struct sk_buff *skb;
struct netlink_callback *cb;
int idx;
};
static int am65_cpsw_fdb_do_dump(const unsigned char *addr, u16 vid,
bool is_static, void *data)
{
struct am65_cpsw_dump_ctx *dump = data;
u32 portid = NETLINK_CB(dump->cb->skb).portid;
u32 seq = dump->cb->nlh->nlmsg_seq;
struct nlmsghdr *nlh;
struct ndmsg *ndm;
if (dump->idx < dump->cb->args[2])
goto skip;
nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, sizeof(*ndm),
NLM_F_MULTI);
if (!nlh)
return -EMSGSIZE;
ndm = nlmsg_data(nlh);
ndm->ndm_family = AF_BRIDGE;
ndm->ndm_pad1 = 0;
ndm->ndm_pad2 = 0;
ndm->ndm_flags = NTF_SELF;
ndm->ndm_type = 0;
ndm->ndm_ifindex = dump->dev->ifindex;
ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE;
if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr))
goto nla_put_failure;
if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid))
goto nla_put_failure;
nlmsg_end(dump->skb, nlh);
skip:
dump->idx++;
return 0;
nla_put_failure:
nlmsg_cancel(dump->skb, nlh);
return -EMSGSIZE;
}
static int am65_cpsw_ndo_fdb_dump(struct sk_buff *skb,
struct netlink_callback *cb,
struct net_device *ndev,
struct net_device *filter_dev, int *idx)
{
struct am65_cpsw_common *common = am65_ndev_to_common(ndev);
struct am65_cpsw_port *port = am65_ndev_to_port(ndev);
int ret;
struct am65_cpsw_dump_ctx dump = {
.dev = ndev,
.skb = skb,
.cb = cb,
.idx = *idx,
};
if (common->is_emac_mode)
return -EOPNOTSUPP;
ret = cpsw_ale_fdb_dump(common->ale, port->port_id,
am65_cpsw_fdb_do_dump, &dump);
*idx = dump.idx;
return ret;
}
static const struct net_device_ops am65_cpsw_nuss_netdev_ops = {
.ndo_open = am65_cpsw_nuss_ndo_slave_open,
.ndo_stop = am65_cpsw_nuss_ndo_slave_stop,
@@ -1481,6 +1557,7 @@ static const struct net_device_ops am65_cpsw_nuss_netdev_ops = {
.ndo_eth_ioctl = am65_cpsw_nuss_ndo_slave_ioctl,
.ndo_setup_tc = am65_cpsw_qos_ndo_setup_tc,
.ndo_set_tx_maxrate = am65_cpsw_qos_ndo_tx_p0_set_maxrate,
.ndo_fdb_dump = am65_cpsw_ndo_fdb_dump,
};
static void am65_cpsw_disable_phy(struct phy *phy)
+45
View File
@@ -1478,6 +1478,51 @@ struct cpsw_ale *cpsw_ale_create(struct cpsw_ale_params *params)
return ale;
}
int cpsw_ale_fdb_dump(struct cpsw_ale *ale, int port,
cpsw_ale_fdb_dump_cb_t *cb, void *data)
{
u32 entry[ALE_ENTRY_WORDS];
u8 addr[ETH_ALEN];
int entry_type;
bool is_static;
int vid;
int i;
int ret = 0;
int ucast_type;
for (i = 0; i < ale->params.ale_entries; i++) {
cpsw_ale_read(ale, i, entry);
if (port != cpsw_ale_get_port_num(entry, ale->port_num_bits))
continue;
entry_type = cpsw_ale_get_entry_type(entry);
if (entry_type == ALE_TYPE_FREE)
continue;
if (entry_type == ALE_TYPE_VLAN)
continue;
if (cpsw_ale_get_mcast(entry))
continue;
ucast_type = cpsw_ale_get_ucast_type(entry);
if (ucast_type == ALE_UCAST_UNTOUCHED)
continue;
vid = 0;
if (entry_type == ALE_TYPE_VLAN_ADDR)
vid = cpsw_ale_get_vlan_id(entry);
cpsw_ale_get_addr(entry, addr);
is_static = ucast_type == ALE_UCAST_PERSISTANT;
ret = cb(addr, vid, is_static , data);
if (ret)
return ret;
}
return 0;
}
void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data)
{
int i;
+3
View File
@@ -84,6 +84,8 @@ enum cpsw_ale_port_state {
ALE_PORT_STATE_FORWARD = 0x03,
};
typedef int cpsw_ale_fdb_dump_cb_t(const unsigned char *addr, u16 vid,
bool is_static, void *data);
/* ALE unicast entry flags - passed into cpsw_ale_add_ucast() */
#define ALE_SECURE BIT(0)
#define ALE_BLOCKED BIT(1)
@@ -127,6 +129,7 @@ int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
int cpsw_ale_control_set(struct cpsw_ale *ale, int port,
int control, int value);
void cpsw_ale_dump(struct cpsw_ale *ale, u32 *data);
int cpsw_ale_fdb_dump(struct cpsw_ale *ale, int port, cpsw_ale_fdb_dump_cb_t *cb, void *data);
void cpsw_ale_restore(struct cpsw_ale *ale, u32 *data);
u32 cpsw_ale_get_num_entries(struct cpsw_ale *ale);