diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c index fec56e68559..5d5e282dc2a 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/common.c @@ -656,6 +656,16 @@ int rtldsa_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexth } /* Found an existing (e->valid is true) or empty entry, make it a nexthop entry */ + if (!nh->l2_installed || nh->l2_id != idx) { + struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx); + + /* An entry nobody had claimed carries whatever its last + * owner left behind. + */ + if (m && !e.valid) + *m = (struct rtldsa_l2_uc){}; + } + nh->l2_id = idx; nh->l2_seed = seed; if (e.valid) { @@ -690,6 +700,7 @@ int rtldsa_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexth int rtldsa_l2_nexthop_del(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh) { struct rtl838x_l2_entry e = {}; + struct rtldsa_l2_uc *m; u32 key = nh->l2_id >> 2; int i = nh->l2_id & 0x3; int idx; @@ -717,7 +728,12 @@ int rtldsa_l2_nexthop_del(struct rtl838x_switch_priv *priv, struct otto_l3_nexth return -ESTALE; } - if (e.is_static) + m = rtldsa_l2_uc_lookup(priv, nh->l2_id); + + /* The bridge put this address here as well, so the entry stays; it + * just stops being a next hop. + */ + if (e.is_static && (!m || !m->fdb_ref)) e.valid = false; e.next_hop = false; /* A route id takes that field on the families that keep one, so what diff --git a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/dsa.c b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/dsa.c index c6f9d668eea..8020bbed768 100644 --- a/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/dsa.c +++ b/target/linux/realtek/files-6.18/drivers/net/dsa/rtl83xx/dsa.c @@ -1666,6 +1666,17 @@ static int rtldsa_port_fdb_add(struct dsa_switch *ds, int port, /* Found an existing or empty entry */ if (idx >= 0) { + struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx); + + if (m) { + /* A slot nobody had claimed carries whatever its last + * owner left behind. + */ + if (!e.valid) + *m = (struct rtldsa_l2_uc){}; + m->fdb_ref = true; + } + rtldsa_setup_l2_uc_entry(&e, port, vid, mac); priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); goto out; @@ -1709,8 +1720,24 @@ static int rtldsa_port_fdb_del(struct dsa_switch *ds, int port, idx = rtldsa_find_l2_hash_entry(priv, seed, true, &e); if (idx >= 0) { + struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx); + pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3); - e.valid = false; + + if (m) + m->fdb_ref = false; + + /* A route still forwarding through this address keeps it, as + * the next hop it already is, reachable through no port of its + * own. + */ + if (m && m->l3_refcount && e.next_hop) { + e.port = priv->r->port_ignore; + e.age = 0; + } else { + e.valid = false; + } + priv->r->write_l2_entry_using_hash(idx >> 2, idx & 0x3, &e); goto out; }