Skip to content

Commit 10738ee

Browse files
Lennart Sorensendavem330
authored andcommitted
drivers: net: cpsw: Support ALLMULTI and fix IFF_PROMISC in switch mode
The cpsw driver did not support the IFF_ALLMULTI flag which makes dynamic multicast routing not work. Related to this, when enabling IFF_PROMISC in switch mode, all registered multicast addresses are flushed, resulting in only broadcast and unicast traffic being received. A new cpsw_ale_set_allmulti function now scans through the ALE entry table and adds/removes the host port from the unregistered multicast port mask of each vlan entry depending on the state of IFF_ALLMULTI. In promiscious mode, cpsw_ale_set_allmulti is used to force reception of all multicast traffic in addition to the unicast and broadcast traffic. With this change dynamic multicast and promiscious mode both work in switch mode. Signed-off-by: Len Sorensen <lsorense@csclub.uwaterloo.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 35532c2 commit 10738ee

File tree

3 files changed

+49
-2
lines changed

3 files changed

+49
-2
lines changed

drivers/net/ethernet/ti/cpsw.c

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,12 +638,16 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
638638
if (ndev->flags & IFF_PROMISC) {
639639
/* Enable promiscuous mode */
640640
cpsw_set_promiscious(ndev, true);
641+
cpsw_ale_set_allmulti(priv->ale, IFF_ALLMULTI);
641642
return;
642643
} else {
643644
/* Disable promiscuous mode */
644645
cpsw_set_promiscious(ndev, false);
645646
}
646647

648+
/* Restore allmulti on vlans if necessary */
649+
cpsw_ale_set_allmulti(priv->ale, priv->ndev->flags & IFF_ALLMULTI);
650+
647651
/* Clear all mcast from ALE */
648652
cpsw_ale_flush_multicast(priv->ale, ALE_ALL_PORTS << priv->host_port);
649653

@@ -1149,6 +1153,7 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
11491153
const int port = priv->host_port;
11501154
u32 reg;
11511155
int i;
1156+
int unreg_mcast_mask;
11521157

11531158
reg = (priv->version == CPSW_VERSION_1) ? CPSW1_PORT_VLAN :
11541159
CPSW2_PORT_VLAN;
@@ -1158,9 +1163,14 @@ static inline void cpsw_add_default_vlan(struct cpsw_priv *priv)
11581163
for (i = 0; i < priv->data.slaves; i++)
11591164
slave_write(priv->slaves + i, vlan, reg);
11601165

1166+
if (priv->ndev->flags & IFF_ALLMULTI)
1167+
unreg_mcast_mask = ALE_ALL_PORTS;
1168+
else
1169+
unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
1170+
11611171
cpsw_ale_add_vlan(priv->ale, vlan, ALE_ALL_PORTS << port,
11621172
ALE_ALL_PORTS << port, ALE_ALL_PORTS << port,
1163-
(ALE_PORT_1 | ALE_PORT_2) << port);
1173+
unreg_mcast_mask << port);
11641174
}
11651175

11661176
static void cpsw_init_host_port(struct cpsw_priv *priv)
@@ -1620,11 +1630,17 @@ static inline int cpsw_add_vlan_ale_entry(struct cpsw_priv *priv,
16201630
unsigned short vid)
16211631
{
16221632
int ret;
1633+
int unreg_mcast_mask;
1634+
1635+
if (priv->ndev->flags & IFF_ALLMULTI)
1636+
unreg_mcast_mask = ALE_ALL_PORTS;
1637+
else
1638+
unreg_mcast_mask = ALE_PORT_1 | ALE_PORT_2;
16231639

16241640
ret = cpsw_ale_add_vlan(priv->ale, vid,
16251641
ALE_ALL_PORTS << priv->host_port,
16261642
0, ALE_ALL_PORTS << priv->host_port,
1627-
(ALE_PORT_1 | ALE_PORT_2) << priv->host_port);
1643+
unreg_mcast_mask << priv->host_port);
16281644
if (ret != 0)
16291645
return ret;
16301646

drivers/net/ethernet/ti/cpsw_ale.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,35 @@ int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port_mask)
443443
return 0;
444444
}
445445

446+
void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti)
447+
{
448+
u32 ale_entry[ALE_ENTRY_WORDS];
449+
int type, idx;
450+
int unreg_mcast = 0;
451+
452+
/* Only bother doing the work if the setting is actually changing */
453+
if (ale->allmulti == allmulti)
454+
return;
455+
456+
/* Remember the new setting to check against next time */
457+
ale->allmulti = allmulti;
458+
459+
for (idx = 0; idx < ale->params.ale_entries; idx++) {
460+
cpsw_ale_read(ale, idx, ale_entry);
461+
type = cpsw_ale_get_entry_type(ale_entry);
462+
if (type != ALE_TYPE_VLAN)
463+
continue;
464+
465+
unreg_mcast = cpsw_ale_get_vlan_unreg_mcast(ale_entry);
466+
if (allmulti)
467+
unreg_mcast |= 1;
468+
else
469+
unreg_mcast &= ~1;
470+
cpsw_ale_set_vlan_unreg_mcast(ale_entry, unreg_mcast);
471+
cpsw_ale_write(ale, idx, ale_entry);
472+
}
473+
}
474+
446475
struct ale_control_info {
447476
const char *name;
448477
int offset, port_offset;

drivers/net/ethernet/ti/cpsw_ale.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct cpsw_ale {
2727
struct cpsw_ale_params params;
2828
struct timer_list timer;
2929
unsigned long ageout;
30+
int allmulti;
3031
};
3132

3233
enum cpsw_ale_control {
@@ -103,6 +104,7 @@ int cpsw_ale_del_mcast(struct cpsw_ale *ale, u8 *addr, int port_mask,
103104
int cpsw_ale_add_vlan(struct cpsw_ale *ale, u16 vid, int port, int untag,
104105
int reg_mcast, int unreg_mcast);
105106
int cpsw_ale_del_vlan(struct cpsw_ale *ale, u16 vid, int port);
107+
void cpsw_ale_set_allmulti(struct cpsw_ale *ale, int allmulti);
106108

107109
int cpsw_ale_control_get(struct cpsw_ale *ale, int port, int control);
108110
int cpsw_ale_control_set(struct cpsw_ale *ale, int port,

0 commit comments

Comments
 (0)