Skip to content

Commit

Permalink
net: dsa: install secondary unicast and multicast addresses as host F…
Browse files Browse the repository at this point in the history
…DB/MDB

In preparation of disabling flooding towards the CPU in standalone ports
mode, identify the addresses requested by upper interfaces and use the
new API for DSA FDB isolation to request the hardware driver to offload
these as FDB or MDB objects. The objects belong to the user port's
database, and are installed pointing towards the CPU port.

Because dev_uc_add()/dev_mc_add() is VLAN-unaware, we offload to the
port standalone database addresses with VID 0 (also VLAN-unaware).
So this excludes switches with global VLAN filtering from supporting
unicast filtering, because there, it is possible for a port of a switch
to join a VLAN-aware bridge, and this changes the VLAN awareness of
standalone ports, requiring VLAN-aware standalone host FDB entries.
For the same reason, hellcreek, which requires VLAN awareness in
standalone mode, is also exempted from unicast filtering.

We create "standalone" variants of dsa_port_host_fdb_add() and
dsa_port_host_mdb_add() (and the _del coresponding functions).

We also create a separate work item type for handling deferred
standalone host FDB/MDB entries compared to the switchdev one.
This is done for the purpose of clarity - the procedure for offloading a
bridge FDB entry is different than offloading a standalone one, and
the switchdev event work handles only FDBs anyway, not MDBs.
Deferral is needed for standalone entries because ndo_set_rx_mode runs
in atomic context. We could probably optimize things a little by first
queuing up all entries that need to be offloaded, and scheduling the
work item just once, but the data structures that we can pass through
__dev_uc_sync() and __dev_mc_sync() are limiting (there is nothing like
a void *priv), so we'd have to keep the list of queued events somewhere
in struct dsa_switch, and possibly a lock for it. Too complicated for
now.

Adding the address to the master is handled by dev_uc_sync(), adding it
to the hardware is handled by __dev_uc_sync(). So this is the reason why
dsa_port_standalone_host_fdb_add() does not call dev_uc_add(). Not that
it had the rtnl_mutex anyway - ndo_set_rx_mode has it, but is atomic.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed Mar 3, 2022
1 parent 68d6d71 commit 5e8a1e0
Show file tree
Hide file tree
Showing 3 changed files with 273 additions and 40 deletions.
37 changes: 37 additions & 0 deletions net/dsa/dsa_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,21 @@ struct dsa_switchdev_event_work {
bool host_addr;
};

enum dsa_standalone_event {
DSA_UC_ADD,
DSA_UC_DEL,
DSA_MC_ADD,
DSA_MC_DEL,
};

struct dsa_standalone_event_work {
struct work_struct work;
struct net_device *dev;
enum dsa_standalone_event event;
unsigned char addr[ETH_ALEN];
u16 vid;
};

struct dsa_slave_priv {
/* Copy of CPU port xmit for faster access in slave transmit hot path */
struct sk_buff * (*xmit)(struct sk_buff *skb,
Expand Down Expand Up @@ -223,6 +238,10 @@ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr,
u16 vid);
int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
u16 vid);
int dsa_port_standalone_host_fdb_add(struct dsa_port *dp,
const unsigned char *addr, u16 vid);
int dsa_port_standalone_host_fdb_del(struct dsa_port *dp,
const unsigned char *addr, u16 vid);
int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, const unsigned char *addr,
u16 vid);
int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, const unsigned char *addr,
Expand All @@ -236,6 +255,10 @@ int dsa_port_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_standalone_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_standalone_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb);
int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp,
Expand Down Expand Up @@ -502,6 +525,20 @@ static inline void *dsa_etype_header_pos_tx(struct sk_buff *skb)
int dsa_switch_register_notifier(struct dsa_switch *ds);
void dsa_switch_unregister_notifier(struct dsa_switch *ds);

static inline bool dsa_switch_supports_uc_filtering(struct dsa_switch *ds)
{
return ds->ops->port_fdb_add && ds->ops->port_fdb_del &&
ds->fdb_isolation && !ds->vlan_filtering_is_global &&
!ds->needs_standalone_vlan_filtering;
}

static inline bool dsa_switch_supports_mc_filtering(struct dsa_switch *ds)
{
return ds->ops->port_mdb_add && ds->ops->port_mdb_del &&
ds->fdb_isolation && !ds->vlan_filtering_is_global &&
!ds->needs_standalone_vlan_filtering;
}

/* dsa2.c */
void dsa_lag_map(struct dsa_switch_tree *dst, struct dsa_lag *lag);
void dsa_lag_unmap(struct dsa_switch_tree *dst, struct dsa_lag *lag);
Expand Down
160 changes: 120 additions & 40 deletions net/dsa/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -835,20 +835,43 @@ int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr,
return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info);
}

int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, const unsigned char *addr,
u16 vid)
static int dsa_port_host_fdb_add(struct dsa_port *dp,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
struct dsa_notifier_fdb_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
.addr = addr,
.vid = vid,
.db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
},
.db = db,
};

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info);
}

int dsa_port_standalone_host_fdb_add(struct dsa_port *dp,
const unsigned char *addr, u16 vid)
{
struct dsa_db db = {
.type = DSA_DB_PORT,
.dp = dp,
};

return dsa_port_host_fdb_add(dp, addr, vid, db);
}

int dsa_port_bridge_host_fdb_add(struct dsa_port *dp,
const unsigned char *addr, u16 vid)
{
struct dsa_port *cpu_dp = dp->cpu_dp;
struct dsa_db db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
};
int err;

/* Avoid a call to __dev_set_promiscuity() on the master, which
Expand All @@ -861,26 +884,46 @@ int dsa_port_bridge_host_fdb_add(struct dsa_port *dp, const unsigned char *addr,
return err;
}

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_ADD, &info);
return dsa_port_host_fdb_add(dp, addr, vid, db);
}

int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, const unsigned char *addr,
u16 vid)
static int dsa_port_host_fdb_del(struct dsa_port *dp,
const unsigned char *addr, u16 vid,
struct dsa_db db)
{
struct dsa_notifier_fdb_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
.addr = addr,
.vid = vid,
.db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
},
.db = db,
};

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info);
}

int dsa_port_standalone_host_fdb_del(struct dsa_port *dp,
const unsigned char *addr, u16 vid)
{
struct dsa_db db = {
.type = DSA_DB_PORT,
.dp = dp,
};

return dsa_port_host_fdb_del(dp, addr, vid, db);
}

int dsa_port_bridge_host_fdb_del(struct dsa_port *dp,
const unsigned char *addr, u16 vid)
{
struct dsa_port *cpu_dp = dp->cpu_dp;
struct dsa_db db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
};
int err;

if (cpu_dp->master->priv_flags & IFF_UNICAST_FLT) {
Expand All @@ -889,10 +932,7 @@ int dsa_port_bridge_host_fdb_del(struct dsa_port *dp, const unsigned char *addr,
return err;
}

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_FDB_DEL, &info);
return dsa_port_host_fdb_del(dp, addr, vid, db);
}

int dsa_port_lag_fdb_add(struct dsa_port *dp, const unsigned char *addr,
Expand Down Expand Up @@ -982,54 +1022,94 @@ int dsa_port_mdb_del(const struct dsa_port *dp,
return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info);
}

int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
static int dsa_port_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db)
{
struct dsa_notifier_mdb_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
.mdb = mdb,
.db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
},
.db = db,
};

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info);
}

int dsa_port_standalone_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
{
struct dsa_db db = {
.type = DSA_DB_PORT,
.dp = dp,
};

return dsa_port_host_mdb_add(dp, mdb, db);
}

int dsa_port_bridge_host_mdb_add(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
{
struct dsa_port *cpu_dp = dp->cpu_dp;
struct dsa_db db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
};
int err;

err = dev_mc_add(cpu_dp->master, mdb->addr);
if (err)
return err;

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_ADD, &info);
return dsa_port_host_mdb_add(dp, mdb, db);
}

int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
static int dsa_port_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb,
struct dsa_db db)
{
struct dsa_notifier_mdb_info info = {
.sw_index = dp->ds->index,
.port = dp->index,
.mdb = mdb,
.db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
},
.db = db,
};

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info);
}

int dsa_port_standalone_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
{
struct dsa_db db = {
.type = DSA_DB_PORT,
.dp = dp,
};

return dsa_port_host_mdb_del(dp, mdb, db);
}

int dsa_port_bridge_host_mdb_del(const struct dsa_port *dp,
const struct switchdev_obj_port_mdb *mdb)
{
struct dsa_port *cpu_dp = dp->cpu_dp;
struct dsa_db db = {
.type = DSA_DB_BRIDGE,
.bridge = *dp->bridge,
};
int err;

err = dev_mc_del(cpu_dp->master, mdb->addr);
if (err)
return err;

if (!dp->ds->fdb_isolation)
info.db.bridge.num = 0;

return dsa_port_notify(dp, DSA_NOTIFIER_HOST_MDB_DEL, &info);
return dsa_port_host_mdb_del(dp, mdb, db);
}

int dsa_port_vlan_add(struct dsa_port *dp,
Expand Down
Loading

0 comments on commit 5e8a1e0

Please sign in to comment.