netc_free_ntmp_bitmaps(priv);
}
+static void netc_clean_fdbt_ageing_entries(struct work_struct *work)
+{
+ struct delayed_work *dwork = to_delayed_work(work);
+ struct netc_switch *priv;
+
+ priv = container_of(dwork, struct netc_switch, fdbt_ageing_work);
+
+ /* Update the activity element in FDB table */
+ mutex_lock(&priv->fdbt_lock);
+ ntmp_fdbt_update_activity_element(&priv->ntmp);
+ /* Delete the ageing entries after the activity element is updated */
+ ntmp_fdbt_delete_ageing_entries(&priv->ntmp, NETC_FDBT_AGEING_THRESH);
+ mutex_unlock(&priv->fdbt_lock);
+
+ if (atomic_read(&priv->br_cnt))
+ schedule_delayed_work(&priv->fdbt_ageing_work,
+ READ_ONCE(priv->fdbt_ageing_delay));
+}
+
static void netc_switch_dos_default_config(struct netc_switch *priv)
{
struct netc_switch_regs *regs = &priv->regs;
INIT_HLIST_HEAD(&priv->fdb_list);
mutex_init(&priv->fdbt_lock);
+ priv->fdbt_ageing_delay = NETC_FDBT_AGEING_DELAY;
+ atomic_set(&priv->br_cnt, 0);
+ INIT_DELAYED_WORK(&priv->fdbt_ageing_work,
+ netc_clean_fdbt_ageing_entries);
INIT_HLIST_HEAD(&priv->vlan_list);
mutex_init(&priv->vft_lock);
{
struct netc_switch *priv = ds->priv;
+ disable_delayed_work_sync(&priv->fdbt_ageing_work);
netc_destroy_all_lists(priv);
netc_free_host_flood_rules(priv);
netc_free_ntmp_user(priv);
struct netlink_ext_ack *extack)
{
struct netc_port *np = NETC_PORT(ds, port);
+ struct netc_switch *priv = ds->priv;
u16 vlan_unaware_pvid;
int err;
out:
netc_port_remove_host_flood(np, np->host_flood);
+ if (atomic_inc_return(&priv->br_cnt) == 1)
+ schedule_delayed_work(&priv->fdbt_ageing_work,
+ READ_ONCE(priv->fdbt_ageing_delay));
+
return 0;
disable_mlo:
{
struct netc_port *np = NETC_PORT(ds, port);
struct net_device *ndev = np->dp->user;
+ struct netc_switch *priv = ds->priv;
u16 vlan_unaware_pvid;
bool mc, uc;
netc_port_set_pvid(np, NETC_STANDALONE_PVID);
np->pvid = NETC_STANDALONE_PVID;
+ if (atomic_dec_and_test(&priv->br_cnt))
+ cancel_delayed_work_sync(&priv->fdbt_ageing_work);
+
netc_port_remove_dynamic_entries(np);
uc = ndev->flags & IFF_PROMISC;
mc = ndev->flags & (IFF_PROMISC | IFF_ALLMULTI);
netc_port_del_vlan_entry(np, vlan_unaware_pvid);
}
+static int netc_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+ struct netc_switch *priv = ds->priv;
+ unsigned long delay_jiffies;
+
+ /* The dynamic FDB entry is deleted when its activity counter reaches
+ * NETC_FDBT_AGEING_THRESH (100). Each delayed_work tick increments
+ * the counter by 1 if the entry is inactive.
+ *
+ * Therefore:
+ * msecs (ms) = NETC_FDBT_AGEING_THRESH * delay_ms (ms)
+ * delay_ms = msecs / NETC_FDBT_AGEING_THRESH
+ * delay_jiffies = (delay_ms / 1000) * HZ
+ * = (msecs * HZ) / (1000 * NETC_FDBT_AGEING_THRESH)
+ *
+ * Use DIV_ROUND_CLOSEST_ULL to perform a single nearest-jiffy
+ * rounding, avoiding the two-step rounding error of the intermediate
+ * delay_ms approach.
+ * Maximum error = +/-0.5 jiffy * 100 = +/-50000/HZ ms.
+ */
+ delay_jiffies = DIV_ROUND_CLOSEST_ULL((u64)msecs * HZ,
+ 1000 * NETC_FDBT_AGEING_THRESH);
+ WRITE_ONCE(priv->fdbt_ageing_delay, delay_jiffies);
+
+ if (atomic_read(&priv->br_cnt))
+ mod_delayed_work(system_percpu_wq, &priv->fdbt_ageing_work,
+ READ_ONCE(priv->fdbt_ageing_delay));
+
+ return 0;
+}
+
static void netc_port_fast_age(struct dsa_switch *ds, int port)
{
struct netc_port *np = NETC_PORT(ds, port);
.port_vlan_del = netc_port_vlan_del,
.port_bridge_join = netc_port_bridge_join,
.port_bridge_leave = netc_port_bridge_leave,
+ .set_ageing_time = netc_set_ageing_time,
.port_fast_age = netc_port_fast_age,
.get_pause_stats = netc_port_get_pause_stats,
.get_rmon_stats = netc_port_get_rmon_stats,
ds->phylink_mac_ops = &netc_phylink_mac_ops;
ds->fdb_isolation = true;
ds->max_num_bridges = priv->info->num_ports - 1;
+ ds->ageing_time_min = 1000;
+ ds->ageing_time_max = U32_MAX;
ds->priv = priv;
priv->ds = ds;