mirror of
https://github.com/openwrt/openwrt.git
synced 2026-10-14 02:19:06 +04:00
realtek: dsa: consolidate L2 helpers in l2.c
Move the hash index, hash/CAM lookup, unicast reference lookup and ageing-time helpers into l2.c. This removes the dependency of L2 code on lookup helpers implemented in dsa.c. Rename the moved functions with the otto_l2_ prefix, place their public declarations in l2.h and update includes and callers. Leave DSA callbacks and port/bridge handling in place. 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:
1 parent
174f1180a4
commit
74521f894e
5 files changed
+143
-136
No files matched your search
@@ -201,28 +201,6 @@ static int rtldsa_ports_probe(struct rtl838x_switch_priv *priv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl83xx_get_l2aging(struct rtl838x_switch_priv *priv)
|
||||
{
|
||||
int t = sw_r32(priv->r->l2_ctrl_1);
|
||||
|
||||
/* RTL838x uses a high-resolution 23-bit AGE_UNIT where one unit is
|
||||
* 204.8 ms. RTL839x and RTL93xx use a 21-bit AGE_UNIT where one unit
|
||||
* is 600 ms. An AGE_UNIT value of 0 disables dynamic address aging.
|
||||
*/
|
||||
if (priv->r->high_res_l2_age) {
|
||||
t &= GENMASK(22, 0);
|
||||
t = t * 128 / 625;
|
||||
} else {
|
||||
t &= GENMASK(20, 0);
|
||||
t = t * 3 / 5;
|
||||
}
|
||||
|
||||
pr_debug("L2 AGING time: %d sec\n", t);
|
||||
pr_debug("Dynamic aging for ports: %x\n", sw_r32(priv->r->l2_port_aging_out));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
// Currently Unused
|
||||
// /* Allocate a 64 bit octet counter located in the LOG HW table */
|
||||
// static int rtl83xx_octet_cntr_alloc(struct rtl838x_switch_priv *priv)
|
||||
@@ -519,7 +497,7 @@ static int rtl83xx_sw_probe(struct platform_device *pdev)
|
||||
if (soc_info.family != RTL9310_FAMILY_ID)
|
||||
sw_w32(0x1, priv->r->imr_glb);
|
||||
|
||||
rtl83xx_get_l2aging(priv);
|
||||
otto_l2_get_ageing_time(priv);
|
||||
|
||||
if (priv->r->qos_init)
|
||||
priv->r->qos_init(priv);
|
||||
|
||||
@@ -797,87 +797,6 @@ static void rtldsa_setup_l2_mc_entry(struct rtl838x_l2_entry *e, int vid, u64 ma
|
||||
u64_to_ether_addr(mac, e->mac);
|
||||
}
|
||||
|
||||
static int rtldsa_l2_hash_index(u32 key, int slot)
|
||||
{
|
||||
return slot > 3 ? ((key >> 14) & 0xffff) | (slot & 3) : ((key << 2) | slot) & 0xffff;
|
||||
}
|
||||
|
||||
/* Uses the seed to identify a hash bucket in the L2 using the derived hash key and then loops
|
||||
* over the entries in the bucket until either a matching entry is found or an empty slot
|
||||
* Returns the filled in rtl838x_l2_entry and the index in the bucket when an entry was found
|
||||
* when an empty slot was found and must exist is false, the index of the slot is returned
|
||||
* when no slots are available returns -1
|
||||
*/
|
||||
int rtldsa_find_l2_hash_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e)
|
||||
{
|
||||
u32 key = priv->r->l2_hash_key(priv, seed);
|
||||
int free_slot = -1;
|
||||
u64 entry;
|
||||
|
||||
pr_debug("%s: using key %x, for seed %016llx\n", __func__, key, seed);
|
||||
/* Loop over all entries in the hash-bucket and over the second block on 93xx SoCs */
|
||||
for (int i = 0; i < priv->r->l2_bucket_size; i++) {
|
||||
entry = priv->r->read_l2_entry_using_hash(key, i, e);
|
||||
pr_debug("valid %d, mac %016llx\n", e->valid, ether_addr_to_u64(&e->mac[0]));
|
||||
|
||||
/* Remember the first free slot, but keep looking: the address
|
||||
* may be sitting further down the bucket.
|
||||
*/
|
||||
if (!e->valid) {
|
||||
if (free_slot < 0)
|
||||
free_slot = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((entry & 0x0fffffffffffffffULL) == seed)
|
||||
return rtldsa_l2_hash_index(key, i);
|
||||
}
|
||||
|
||||
if (must_exist || free_slot < 0)
|
||||
return -1;
|
||||
|
||||
/* The loop leaves the last entry it read here, valid bit included.
|
||||
* Reading the free slot back does not undo that on RTL930x and
|
||||
* RTL931x, whose readers return as soon as the valid bit is clear.
|
||||
*/
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
||||
return rtldsa_l2_hash_index(key, free_slot);
|
||||
}
|
||||
|
||||
/* Uses the seed to identify an entry in the CAM by looping over all its entries
|
||||
* Returns the filled in rtl838x_l2_entry and the index in the CAM when an entry was found
|
||||
* when an empty slot was found the index of the slot is returned
|
||||
* when no slots are available returns -1
|
||||
*/
|
||||
static int rtldsa_find_l2_cam_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e)
|
||||
{
|
||||
int idx = -1;
|
||||
int cam_rows;
|
||||
u64 entry;
|
||||
|
||||
cam_rows = otto_table_rows(priv->r->l2_cam_tbl);
|
||||
if (cam_rows < 0)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < cam_rows; i++) {
|
||||
entry = priv->r->read_cam(i, e);
|
||||
if (!must_exist && !e->valid) {
|
||||
if (idx < 0) /* First empty entry? */
|
||||
idx = i;
|
||||
break;
|
||||
} else if ((entry & 0x0fffffffffffffffULL) == seed) {
|
||||
pr_debug("Found entry in CAM\n");
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
static int rtldsa_port_fdb_add(struct dsa_switch *ds, int port,
|
||||
const unsigned char *addr, u16 vid,
|
||||
const struct dsa_db db)
|
||||
@@ -901,11 +820,11 @@ static int rtldsa_port_fdb_add(struct dsa_switch *ds, int port,
|
||||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
idx = rtldsa_find_l2_hash_entry(priv, seed, false, &e);
|
||||
idx = otto_l2_find_hash_entry(priv, seed, false, &e);
|
||||
|
||||
/* Found an existing or empty entry */
|
||||
if (idx >= 0) {
|
||||
struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx);
|
||||
struct rtldsa_l2_uc *m = otto_l2_uc_lookup(priv, idx);
|
||||
|
||||
if (m) {
|
||||
/* A slot nobody had claimed carries whatever its last
|
||||
@@ -922,7 +841,7 @@ static int rtldsa_port_fdb_add(struct dsa_switch *ds, int port,
|
||||
}
|
||||
|
||||
/* Hash buckets full, try CAM */
|
||||
idx = rtldsa_find_l2_cam_entry(priv, seed, false, &e);
|
||||
idx = otto_l2_find_cam_entry(priv, seed, false, &e);
|
||||
|
||||
if (idx >= 0) {
|
||||
rtldsa_setup_l2_uc_entry(&e, port, vid, mac);
|
||||
@@ -956,10 +875,10 @@ static int rtldsa_port_fdb_del(struct dsa_switch *ds, int port,
|
||||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
idx = rtldsa_find_l2_hash_entry(priv, seed, true, &e);
|
||||
idx = otto_l2_find_hash_entry(priv, seed, true, &e);
|
||||
|
||||
if (idx >= 0) {
|
||||
struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx);
|
||||
struct rtldsa_l2_uc *m = otto_l2_uc_lookup(priv, idx);
|
||||
|
||||
pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3);
|
||||
|
||||
@@ -982,7 +901,7 @@ static int rtldsa_port_fdb_del(struct dsa_switch *ds, int port,
|
||||
}
|
||||
|
||||
/* Check CAM for spillover from hash buckets */
|
||||
idx = rtldsa_find_l2_cam_entry(priv, seed, true, &e);
|
||||
idx = otto_l2_find_cam_entry(priv, seed, true, &e);
|
||||
|
||||
if (idx >= 0) {
|
||||
e.valid = false;
|
||||
@@ -1091,7 +1010,7 @@ static int rtldsa_83xx_port_mdb_add(struct dsa_switch *ds, int port,
|
||||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
idx = rtldsa_find_l2_hash_entry(priv, seed, false, &e);
|
||||
idx = otto_l2_find_hash_entry(priv, seed, false, &e);
|
||||
|
||||
/* Found an existing or empty entry */
|
||||
if (idx >= 0) {
|
||||
@@ -1113,7 +1032,7 @@ static int rtldsa_83xx_port_mdb_add(struct dsa_switch *ds, int port,
|
||||
}
|
||||
|
||||
/* Hash buckets full, try CAM */
|
||||
idx = rtldsa_find_l2_cam_entry(priv, seed, false, &e);
|
||||
idx = otto_l2_find_cam_entry(priv, seed, false, &e);
|
||||
|
||||
if (idx >= 0) {
|
||||
if (e.valid) {
|
||||
@@ -1177,7 +1096,7 @@ static int rtldsa_port_mdb_del(struct dsa_switch *ds, int port,
|
||||
|
||||
mutex_lock(&priv->reg_mutex);
|
||||
|
||||
idx = rtldsa_find_l2_hash_entry(priv, seed, true, &e);
|
||||
idx = otto_l2_find_hash_entry(priv, seed, true, &e);
|
||||
|
||||
if (idx >= 0) {
|
||||
pr_debug("Found entry index %d, key %d and bucket %d\n", idx, idx >> 2, idx & 3);
|
||||
@@ -1190,7 +1109,7 @@ static int rtldsa_port_mdb_del(struct dsa_switch *ds, int port,
|
||||
}
|
||||
|
||||
/* Check CAM for spillover from hash buckets */
|
||||
idx = rtldsa_find_l2_cam_entry(priv, seed, true, &e);
|
||||
idx = otto_l2_find_cam_entry(priv, seed, true, &e);
|
||||
|
||||
if (idx >= 0) {
|
||||
portmask = rtldsa_mc_group_del_port(priv, e.mc_portmask_index, port);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/soc/realtek/otto_table.h>
|
||||
#include <linux/string.h>
|
||||
|
||||
#include "l2.h"
|
||||
#include "l3.h"
|
||||
@@ -56,13 +58,127 @@
|
||||
|
||||
#define RTL839X_SPCL_TRAP_ARP_CTRL (0x1060)
|
||||
|
||||
int otto_l2_get_ageing_time(struct rtl838x_switch_priv *priv)
|
||||
{
|
||||
int t = sw_r32(priv->r->l2_ctrl_1);
|
||||
|
||||
/* RTL838x uses a high-resolution 23-bit AGE_UNIT where one unit is
|
||||
* 204.8 ms. RTL839x and RTL93xx use a 21-bit AGE_UNIT where one unit
|
||||
* is 600 ms. An AGE_UNIT value of 0 disables dynamic address aging.
|
||||
*/
|
||||
if (priv->r->high_res_l2_age) {
|
||||
t &= GENMASK(22, 0);
|
||||
t = t * 128 / 625;
|
||||
} else {
|
||||
t &= GENMASK(20, 0);
|
||||
t = t * 3 / 5;
|
||||
}
|
||||
|
||||
pr_debug("L2 AGING time: %d sec\n", t);
|
||||
pr_debug("Dynamic aging for ports: %x\n", sw_r32(priv->r->l2_port_aging_out));
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static int otto_l2_hash_index(u32 key, int slot)
|
||||
{
|
||||
return slot > 3 ? ((key >> 14) & 0xffff) | (slot & 3) : ((key << 2) | slot) & 0xffff;
|
||||
}
|
||||
|
||||
/* Uses the seed to identify a hash bucket in the L2 using the derived hash key and then loops
|
||||
* over the entries in the bucket until either a matching entry is found or an empty slot
|
||||
* Returns the filled in rtl838x_l2_entry and the index in the bucket when an entry was found
|
||||
* when an empty slot was found and must exist is false, the index of the slot is returned
|
||||
* when no slots are available returns -1
|
||||
*/
|
||||
int otto_l2_find_hash_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e)
|
||||
{
|
||||
u32 key = priv->r->l2_hash_key(priv, seed);
|
||||
int free_slot = -1;
|
||||
u64 entry;
|
||||
|
||||
pr_debug("%s: using key %x, for seed %016llx\n", __func__, key, seed);
|
||||
/* Loop over all entries in the hash-bucket and over the second block on 93xx SoCs */
|
||||
for (int i = 0; i < priv->r->l2_bucket_size; i++) {
|
||||
entry = priv->r->read_l2_entry_using_hash(key, i, e);
|
||||
pr_debug("valid %d, mac %016llx\n", e->valid, ether_addr_to_u64(&e->mac[0]));
|
||||
|
||||
/* Remember the first free slot, but keep looking: the address
|
||||
* may be sitting further down the bucket.
|
||||
*/
|
||||
if (!e->valid) {
|
||||
if (free_slot < 0)
|
||||
free_slot = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((entry & 0x0fffffffffffffffULL) == seed)
|
||||
return otto_l2_hash_index(key, i);
|
||||
}
|
||||
|
||||
if (must_exist || free_slot < 0)
|
||||
return -1;
|
||||
|
||||
/* The loop leaves the last entry it read here, valid bit included.
|
||||
* Reading the free slot back does not undo that on RTL930x and
|
||||
* RTL931x, whose readers return as soon as the valid bit is clear.
|
||||
*/
|
||||
memset(e, 0, sizeof(*e));
|
||||
|
||||
return otto_l2_hash_index(key, free_slot);
|
||||
}
|
||||
|
||||
/* Uses the seed to identify an entry in the CAM by looping over all its entries
|
||||
* Returns the filled in rtl838x_l2_entry and the index in the CAM when an entry was found
|
||||
* when an empty slot was found the index of the slot is returned
|
||||
* when no slots are available returns -1
|
||||
*/
|
||||
int otto_l2_find_cam_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e)
|
||||
{
|
||||
int idx = -1;
|
||||
int cam_rows;
|
||||
u64 entry;
|
||||
|
||||
cam_rows = otto_table_rows(priv->r->l2_cam_tbl);
|
||||
if (cam_rows < 0)
|
||||
return -1;
|
||||
|
||||
for (int i = 0; i < cam_rows; i++) {
|
||||
entry = priv->r->read_cam(i, e);
|
||||
if (!must_exist && !e->valid) {
|
||||
if (idx < 0) /* First empty entry? */
|
||||
idx = i;
|
||||
break;
|
||||
} else if ((entry & 0x0fffffffffffffffULL) == seed) {
|
||||
pr_debug("Found entry in CAM\n");
|
||||
idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
/* RTL931x hashes its second block into rows the fib_entries count does not
|
||||
* reach, so an index can fall outside the map and is simply not tracked.
|
||||
*/
|
||||
struct rtldsa_l2_uc *otto_l2_uc_lookup(struct rtl838x_switch_priv *priv, int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= priv->r->fib_entries)
|
||||
return NULL;
|
||||
|
||||
return &priv->l2_uc_map[idx];
|
||||
}
|
||||
|
||||
/* The count pins at its ceiling rather than wrapping: an entry that reached it
|
||||
* is never freed by the driver again, which leaks a row but never hands a live
|
||||
* one to somebody else.
|
||||
*/
|
||||
static void otto_l2_uc_get(struct rtl838x_switch_priv *priv, int idx)
|
||||
{
|
||||
struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx);
|
||||
struct rtldsa_l2_uc *m = otto_l2_uc_lookup(priv, idx);
|
||||
|
||||
if (!m)
|
||||
return;
|
||||
@@ -77,7 +193,7 @@ static void otto_l2_uc_get(struct rtl838x_switch_priv *priv, int idx)
|
||||
|
||||
static void otto_l2_uc_put(struct rtl838x_switch_priv *priv, int idx)
|
||||
{
|
||||
struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx);
|
||||
struct rtldsa_l2_uc *m = otto_l2_uc_lookup(priv, idx);
|
||||
|
||||
if (!m || !m->l3_refcount || m->l3_refcount == RTLDSA_L2_L3_REFCOUNT_MAX)
|
||||
return;
|
||||
@@ -92,7 +208,7 @@ static void otto_l2_uc_release_row(struct rtl838x_switch_priv *priv,
|
||||
struct otto_l3_nexthop *nh,
|
||||
struct rtl838x_l2_entry *e)
|
||||
{
|
||||
struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, nh->l2_id);
|
||||
struct rtldsa_l2_uc *m = otto_l2_uc_lookup(priv, nh->l2_id);
|
||||
|
||||
/* Another route is still forwarding through this entry: it has to stay
|
||||
* exactly as it is, next hop and route id included.
|
||||
@@ -126,7 +242,7 @@ static void otto_l2_uc_put_row(struct rtl838x_switch_priv *priv,
|
||||
{
|
||||
struct rtl838x_l2_entry e = {};
|
||||
|
||||
if (rtldsa_find_l2_hash_entry(priv, nh->l2_seed, true, &e) != nh->l2_id)
|
||||
if (otto_l2_find_hash_entry(priv, nh->l2_seed, true, &e) != nh->l2_id)
|
||||
return;
|
||||
|
||||
if (!e.next_hop)
|
||||
@@ -157,7 +273,7 @@ int otto_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop
|
||||
*/
|
||||
guard(mutex)(&priv->reg_mutex);
|
||||
|
||||
idx = rtldsa_find_l2_hash_entry(priv, seed, false, &e);
|
||||
idx = otto_l2_find_hash_entry(priv, seed, false, &e);
|
||||
if (idx < 0) {
|
||||
pr_err("%s: No more L2 forwarding entries available\n", __func__);
|
||||
return -1;
|
||||
@@ -183,7 +299,7 @@ int otto_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop
|
||||
otto_l2_uc_put_row(priv, nh);
|
||||
|
||||
if (!nh->l2_installed || nh->l2_id != idx) {
|
||||
struct rtldsa_l2_uc *m = rtldsa_l2_uc_lookup(priv, idx);
|
||||
struct rtldsa_l2_uc *m = otto_l2_uc_lookup(priv, idx);
|
||||
|
||||
/* An entry nobody had claimed carries whatever its last owner
|
||||
* left behind, including a count for a route long gone.
|
||||
@@ -252,7 +368,7 @@ int otto_l2_nexthop_del(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop
|
||||
* the address that was installed, and it is still the same row. A
|
||||
* negative index means the address has left the bucket altogether.
|
||||
*/
|
||||
idx = rtldsa_find_l2_hash_entry(priv, nh->l2_seed, true, &e);
|
||||
idx = otto_l2_find_hash_entry(priv, nh->l2_seed, true, &e);
|
||||
if (idx != nh->l2_id) {
|
||||
dev_err(priv->dev, "next hop %d is at %d now, not removing it\n",
|
||||
nh->l2_id, idx);
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
struct rtl838x_switch_priv;
|
||||
struct rtl838x_l2_entry;
|
||||
struct rtldsa_l2_uc;
|
||||
struct otto_l3_nexthop;
|
||||
|
||||
enum rtldsa_flood_type {
|
||||
@@ -58,6 +59,14 @@ enum rtldsa_flood_type {
|
||||
|
||||
#define RTLDSA_L2_L3_REFCOUNT_MAX 0x7f
|
||||
|
||||
/* Returns the L2 ageing time in seconds. */
|
||||
int otto_l2_get_ageing_time(struct rtl838x_switch_priv *priv);
|
||||
int otto_l2_find_hash_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e);
|
||||
int otto_l2_find_cam_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e);
|
||||
struct rtldsa_l2_uc *otto_l2_uc_lookup(struct rtl838x_switch_priv *priv, int idx);
|
||||
|
||||
int otto_l2_nexthop_add(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh,
|
||||
bool require_existing);
|
||||
int otto_l2_nexthop_del(struct rtl838x_switch_priv *priv, struct otto_l3_nexthop *nh);
|
||||
|
||||
@@ -611,19 +611,4 @@ extern struct rtl83xx_soc_info soc_info;
|
||||
void rtl838x_dbgfs_init(struct rtl838x_switch_priv *priv);
|
||||
void rtl930x_dbgfs_init(struct rtl838x_switch_priv *priv);
|
||||
|
||||
int rtldsa_find_l2_hash_entry(struct rtl838x_switch_priv *priv, u64 seed,
|
||||
bool must_exist, struct rtl838x_l2_entry *e);
|
||||
|
||||
/* RTL931x hashes its second block into rows the fib_entries count does not
|
||||
* reach, so an index can fall outside the map and is simply not tracked.
|
||||
*/
|
||||
static inline struct rtldsa_l2_uc *rtldsa_l2_uc_lookup(struct rtl838x_switch_priv *priv,
|
||||
int idx)
|
||||
{
|
||||
if (idx < 0 || idx >= priv->r->fib_entries)
|
||||
return NULL;
|
||||
|
||||
return &priv->l2_uc_map[idx];
|
||||
}
|
||||
|
||||
#endif /* _RTL838X_H */
|
||||
Reference in new issue
Block a user