realtek: l3: relocate find_slot()

Move find_slot() and its helpers into the new layer 3 ecosystem.

Link: https://github.com/openwrt/openwrt/pull/23979
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
This commit is contained in:
Markus Stockhausen
2026-06-29 18:17:08 +02:00
parent 4d82173451
commit 339db36d97
4 changed files with 118 additions and 119 deletions
@@ -95,6 +95,33 @@ static void otto_l3_839x_route_write(struct otto_l3_ctrl *ctrl, int idx, struct
rtl_table_release(r);
}
__maybe_unused
static u32 otto_l3_930x_hash4(u32 ip, int algorithm, bool move_dip)
{
u32 s0, s1, pH;
u32 rows[4];
u32 hash;
memset(rows, 0, sizeof(rows));
rows[0] = HASH_PICK(ip, 27, 5);
rows[1] = HASH_PICK(ip, 18, 9);
rows[2] = HASH_PICK(ip, 9, 9);
if (!move_dip)
rows[3] = HASH_PICK(ip, 0, 9);
if (!algorithm) {
hash = rows[0] ^ rows[1] ^ rows[2] ^ rows[3];
} else {
s0 = rows[0] + rows[1] + rows[2];
s1 = (s0 & 0x1ff) + ((s0 & (0x1ff << 9)) >> 9);
pH = (s1 & 0x1ff) + ((s1 & (0x1ff << 9)) >> 9);
hash = pH ^ rows[3];
}
return hash;
}
/*
* Get the Destination-MAC of an L3 egress interface or the Source MAC for routed packets
* from the SoC's L3_EGR_INTF_MAC table. Indexes 0-2047 are DMACs, 2048+ are SMACs
@@ -115,6 +142,55 @@ static u64 otto_l3_930x_get_egress_mac(struct otto_l3_ctrl *ctrl, u32 idx)
return mac;
}
/* Read a host route entry from the table using its index. Only IPv4 and IPv6 unicast supported */
__maybe_unused
static void otto_l3_930x_host_route_read(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt)
{
struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 1);
u32 v;
idx = ((idx / 6) * 8) + (idx % 6);
rtl_table_read(r, idx);
/* The table has a size of 5 (for UC, 11 for MC) registers */
v = sw_r32(rtl_table_data(r, 0));
rt->attr.valid = !!(v & BIT(31));
if (!rt->attr.valid)
goto out;
rt->attr.type = (v >> 29) & 0x3;
switch (rt->attr.type) {
case 0: /* IPv4 Unicast route */
rt->dst_ip = sw_r32(rtl_table_data(r, 4));
break;
case 2: /* IPv6 Unicast route */
ipv6_addr_set(&rt->dst_ip6,
sw_r32(rtl_table_data(r, 3)), sw_r32(rtl_table_data(r, 2)),
sw_r32(rtl_table_data(r, 1)), sw_r32(rtl_table_data(r, 0)));
break;
case 1: /* IPv4 Multicast route */
case 3: /* IPv6 Multicast route */
dev_warn(ctrl->dev, "route type not supported\n");
goto out;
}
rt->attr.hit = !!(v & BIT(20));
rt->attr.dst_null = !!(v & BIT(19));
rt->attr.action = (v >> 17) & 3;
rt->nh.id = (v >> 6) & 0x7ff;
rt->attr.ttl_dec = !!(v & BIT(5));
rt->attr.ttl_check = !!(v & BIT(4));
rt->attr.qos_as = !!(v & BIT(3));
rt->attr.qos_prio = v & 0x7;
dev_dbg(ctrl->dev, "index %d is valid: %d\n", idx, rt->attr.valid);
dev_dbg(ctrl->dev, "next_hop: %d, hit: %d, action :%d, ttl_dec %d, ttl_check %d, dst_null %d\n",
rt->nh.id, rt->attr.hit, rt->attr.action, rt->attr.ttl_dec, rt->attr.ttl_check,
rt->attr.dst_null);
dev_dbg(ctrl->dev, "Destination: %pI4\n", &rt->dst_ip);
out:
rtl_table_release(r);
}
/* Write a host route entry from the table using its index. Only unicast routes supported */
__maybe_unused
static void otto_l3_930x_host_route_write(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt)
@@ -168,6 +244,41 @@ out:
rtl_table_release(r);
}
__maybe_unused
static int otto_l3_930x_find_slot(struct otto_l3_ctrl *ctrl, struct otto_l3_route *rt, bool must_exist)
{
int slot_width, algorithm, addr, idx;
struct otto_l3_route route_entry;
u32 hash;
/* IPv6 entries take up 3 slots */
slot_width = (rt->attr.type == 0) || (rt->attr.type == 2) ? 1 : 3;
for (int t = 0; t < 2; t++) {
algorithm = (sw_r32(RTL930X_L3_HOST_TBL_CTRL) >> (2 + t)) & 0x1;
hash = otto_l3_930x_hash4(rt->dst_ip, algorithm, false);
dev_dbg(ctrl->dev, "table %d, algorithm %d, hash %04x\n", t, algorithm, hash);
for (int s = 0; s < 6; s += slot_width) {
addr = (t << 12) | ((hash & 0x1ff) << 3) | s;
dev_dbg(ctrl->dev, "physical address %d\n", addr);
idx = ((addr / 8) * 6) + (addr % 8);
dev_dbg(ctrl->dev, "logical address %d\n", idx);
otto_l3_930x_host_route_read(ctrl, idx, &route_entry);
dev_dbg(ctrl->dev, "route valid %d, route dest: %pI4, hit %d\n",
rt->attr.valid, &rt->dst_ip, rt->attr.hit);
if (!must_exist && rt->attr.valid)
return idx;
if (must_exist && route_entry.dst_ip == rt->dst_ip)
return idx;
}
}
return -1;
}
/*
* Reads a MAC entry for L3 termination as entry point for routing from the hardware table.
* idx is the index into the L3_ROUTER_MAC table
@@ -693,7 +804,7 @@ static int otto_l3_nexthop_update(struct otto_l3_ctrl *ctrl, __be32 ip_addr, u64
r->pr.dip_m = inet_make_mask(r->prefix_len);
if (r->is_host_route) {
int slot = priv->r->find_l3_slot(r, false);
int slot = ctrl->cfg->find_slot(ctrl, r, false);
dev_info(ctrl->dev, "Got slot for route: %d\n", slot);
ctrl->cfg->host_route_write(ctrl, slot, r);
@@ -769,7 +880,7 @@ static void otto_l3_route_remove(struct otto_l3_ctrl *ctrl, struct otto_l3_route
dev_warn(ctrl->dev, "Could not remove route\n");
if (r->is_host_route) {
id = priv->r->find_l3_slot(r, false);
id = ctrl->cfg->find_slot(ctrl, r, false);
dev_dbg(ctrl->dev, "Got id for host route: %d\n", id);
r->attr.valid = false;
ctrl->cfg->host_route_write(ctrl, id, r);
@@ -955,7 +1066,7 @@ static int otto_l3_fib_add_v4(struct otto_l3_ctrl *ctrl, struct fib_entry_notifi
route->attr.action = ROUTE_ACT_TRAP2CPU;
route->attr.type = 0;
slot = priv->r->find_l3_slot(route, false);
slot = ctrl->cfg->find_slot(ctrl, route, false);
dev_dbg(ctrl->dev, "Got slot for route: %d\n", slot);
ctrl->cfg->host_route_write(ctrl, slot, route);
}
@@ -1201,6 +1312,7 @@ const struct otto_l3_config otto_l3_839x_cfg = {
const struct otto_l3_config otto_l3_930x_cfg = {
#ifdef CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD
.find_slot = otto_l3_930x_find_slot,
.get_egress_mac = otto_l3_930x_get_egress_mac,
.set_egress_intf = otto_l3_930x_set_egress_intf,
.host_route_write = otto_l3_930x_host_route_write,
@@ -8,6 +8,8 @@
#define MAX_HOST_ROUTES 1536
#define MAX_INTERFACES 100
#define HASH_PICK(val, lsb, len) ((val & (((1 << len) - 1) << lsb)) >> lsb)
/* An entry in the RTL93XX SoC's ROUTER_MAC tables setting up a termination point
* for the L3 routing system. Packets arriving and matching an entry in this table
* will be considered for routing.
@@ -43,6 +45,7 @@ struct otto_l3_intf {
};
struct otto_l3_config {
int (*find_slot)(struct otto_l3_ctrl *ctrl, struct otto_l3_route *rt, bool must_exist);
void (*set_egress_intf)(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_intf *intf);
u64 (*get_egress_mac)(struct otto_l3_ctrl *ctrl, u32 idx);
void (*host_route_write)(struct otto_l3_ctrl *ctrl, int idx, struct otto_l3_route *rt);
@@ -1403,7 +1403,6 @@ struct rtldsa_config {
void (*packet_cntr_clear)(int counter);
int (*l3_setup)(struct rtl838x_switch_priv *priv);
void (*set_l3_egress_mac)(u32 idx, u64 mac);
int (*find_l3_slot)(struct otto_l3_route *rt, bool must_exist);
void (*set_receive_management_action)(int port, rma_ctrl_t type, action_type_t action);
void (*led_init)(struct rtl838x_switch_priv *priv);
void (*qos_init)(struct rtl838x_switch_priv *priv);
@@ -1184,34 +1184,6 @@ static void rtl930x_init_eee(struct rtl838x_switch_priv *priv, bool enable)
#ifdef CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD
#define HASH_PICK(val, lsb, len) ((val & (((1 << len) - 1) << lsb)) >> lsb)
static u32 rtl930x_l3_hash4(u32 ip, int algorithm, bool move_dip)
{
u32 rows[4];
u32 hash;
u32 s0, s1, pH;
memset(rows, 0, sizeof(rows));
rows[0] = HASH_PICK(ip, 27, 5);
rows[1] = HASH_PICK(ip, 18, 9);
rows[2] = HASH_PICK(ip, 9, 9);
if (!move_dip)
rows[3] = HASH_PICK(ip, 0, 9);
if (!algorithm) {
hash = rows[0] ^ rows[1] ^ rows[2] ^ rows[3];
} else {
s0 = rows[0] + rows[1] + rows[2];
s1 = (s0 & 0x1ff) + ((s0 & (0x1ff << 9)) >> 9);
pH = (s1 & 0x1ff) + ((s1 & (0x1ff << 9)) >> 9);
hash = pH ^ rows[3];
}
return hash;
}
// Currently not used
// static u32 rtl930x_l3_hash6(struct in6_addr *ip6, int algorithm, bool move_dip)
// {
@@ -1265,92 +1237,6 @@ static u32 rtl930x_l3_hash4(u32 ip, int algorithm, bool move_dip)
// return hash;
// }
/* Read a host route entry from the table using its index
* We currently only support IPv4 and IPv6 unicast route
*/
static void rtl930x_host_route_read(int idx, struct otto_l3_route *rt)
{
u32 v;
/* Read L3_HOST_ROUTE_IPUC table (1) via register RTL9300_TBL_1 */
struct table_reg *r = rtl_table_get(RTL9300_TBL_1, 1);
idx = ((idx / 6) * 8) + (idx % 6);
pr_debug("In %s, physical index %d\n", __func__, idx);
rtl_table_read(r, idx);
/* The table has a size of 5 (for UC, 11 for MC) registers */
v = sw_r32(rtl_table_data(r, 0));
rt->attr.valid = !!(v & BIT(31));
if (!rt->attr.valid)
goto out;
rt->attr.type = (v >> 29) & 0x3;
switch (rt->attr.type) {
case 0: /* IPv4 Unicast route */
rt->dst_ip = sw_r32(rtl_table_data(r, 4));
break;
case 2: /* IPv6 Unicast route */
ipv6_addr_set(&rt->dst_ip6,
sw_r32(rtl_table_data(r, 3)), sw_r32(rtl_table_data(r, 2)),
sw_r32(rtl_table_data(r, 1)), sw_r32(rtl_table_data(r, 0)));
break;
case 1: /* IPv4 Multicast route */
case 3: /* IPv6 Multicast route */
pr_warn("%s: route type not supported\n", __func__);
goto out;
}
rt->attr.hit = !!(v & BIT(20));
rt->attr.dst_null = !!(v & BIT(19));
rt->attr.action = (v >> 17) & 3;
rt->nh.id = (v >> 6) & 0x7ff;
rt->attr.ttl_dec = !!(v & BIT(5));
rt->attr.ttl_check = !!(v & BIT(4));
rt->attr.qos_as = !!(v & BIT(3));
rt->attr.qos_prio = v & 0x7;
pr_debug("%s: index %d is valid: %d\n", __func__, idx, rt->attr.valid);
pr_debug("%s: next_hop: %d, hit: %d, action :%d, ttl_dec %d, ttl_check %d, dst_null %d\n",
__func__, rt->nh.id, rt->attr.hit, rt->attr.action, rt->attr.ttl_dec, rt->attr.ttl_check,
rt->attr.dst_null);
pr_debug("%s: Destination: %pI4\n", __func__, &rt->dst_ip);
out:
rtl_table_release(r);
}
static int rtl930x_find_l3_slot(struct otto_l3_route *rt, bool must_exist)
{
int slot_width, algorithm, addr, idx;
u32 hash;
struct otto_l3_route route_entry;
/* IPv6 entries take up 3 slots */
slot_width = (rt->attr.type == 0) || (rt->attr.type == 2) ? 1 : 3;
for (int t = 0; t < 2; t++) {
algorithm = (sw_r32(RTL930X_L3_HOST_TBL_CTRL) >> (2 + t)) & 0x1;
hash = rtl930x_l3_hash4(rt->dst_ip, algorithm, false);
pr_debug("%s: table %d, algorithm %d, hash %04x\n", __func__, t, algorithm, hash);
for (int s = 0; s < 6; s += slot_width) {
addr = (t << 12) | ((hash & 0x1ff) << 3) | s;
pr_debug("%s physical address %d\n", __func__, addr);
idx = ((addr / 8) * 6) + (addr % 8);
pr_debug("%s logical address %d\n", __func__, idx);
rtl930x_host_route_read(idx, &route_entry);
pr_debug("%s route valid %d, route dest: %pI4, hit %d\n", __func__,
rt->attr.valid, &rt->dst_ip, rt->attr.hit);
if (!must_exist && rt->attr.valid)
return idx;
if (must_exist && route_entry.dst_ip == rt->dst_ip)
return idx;
}
}
return -1;
}
// Currently not used
// static int rtl930x_l3_mtu_del(struct rtl838x_switch_priv *priv, int mtu)
// {
@@ -2468,7 +2354,6 @@ const struct rtldsa_config rtldsa_930x_cfg = {
#ifdef CONFIG_NET_DSA_RTL83XX_RTL930X_L3_OFFLOAD
.l3_setup = rtl930x_l3_setup,
.set_l3_egress_mac = rtl930x_set_l3_egress_mac,
.find_l3_slot = rtl930x_find_l3_slot,
#endif
.led_init = rtl930x_led_init,
.enable_learning = rtldsa_930x_enable_learning,