Skip to content

Commit 15180ec

Browse files
ikhorndavem330
authored andcommitted
net: ethernet: ti: cpsw: fix vlan mcast
At this moment, mcast addresses are added for real device only (reserved vlans for dual-emac mode), even if a mcast address was added for some vlan only, thus ALE doesn't have corresponding vlan mcast entries after vlan socket joined multicast group. So ALE drops vlan frames with mcast addresses intended for vlans and potentially can receive mcast frames for base ndev. That's not correct. So, fix it by creating only vlan/mcast entries as requested. Patch doesn't use any additional lists and is based on device mc address list and cpsw ALE table entries. Signed-off-by: Ivan Khoronzhuk <ivan.khoronzhuk@linaro.org> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 960abf6 commit 15180ec

File tree

1 file changed

+138
-31
lines changed

1 file changed

+138
-31
lines changed

drivers/net/ethernet/ti/cpsw.c

Lines changed: 138 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -570,21 +570,6 @@ static inline int cpsw_get_slave_port(u32 slave_num)
570570
return slave_num + 1;
571571
}
572572

573-
static void cpsw_add_mcast(struct cpsw_priv *priv, const u8 *addr)
574-
{
575-
struct cpsw_common *cpsw = priv->cpsw;
576-
577-
if (cpsw->data.dual_emac) {
578-
struct cpsw_slave *slave = cpsw->slaves + priv->emac_port;
579-
580-
cpsw_ale_add_mcast(cpsw->ale, addr, ALE_PORT_HOST,
581-
ALE_VLAN, slave->port_vlan, 0);
582-
return;
583-
}
584-
585-
cpsw_ale_add_mcast(cpsw->ale, addr, ALE_ALL_PORTS, 0, 0, 0);
586-
}
587-
588573
static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
589574
{
590575
struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
@@ -640,7 +625,7 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
640625

641626
/* Clear all mcast from ALE */
642627
cpsw_ale_flush_multicast(ale, ALE_ALL_PORTS, -1);
643-
__dev_mc_unsync(ndev, NULL);
628+
__hw_addr_ref_unsync_dev(&ndev->mc, ndev, NULL);
644629

645630
/* Flood All Unicast Packets to Host port */
646631
cpsw_ale_control_set(ale, 0, ALE_P0_UNI_FLOOD, 1);
@@ -661,29 +646,148 @@ static void cpsw_set_promiscious(struct net_device *ndev, bool enable)
661646
}
662647
}
663648

664-
static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr)
649+
struct addr_sync_ctx {
650+
struct net_device *ndev;
651+
const u8 *addr; /* address to be synched */
652+
int consumed; /* number of address instances */
653+
int flush; /* flush flag */
654+
};
655+
656+
/**
657+
* cpsw_set_mc - adds multicast entry to the table if it's not added or deletes
658+
* if it's not deleted
659+
* @ndev: device to sync
660+
* @addr: address to be added or deleted
661+
* @vid: vlan id, if vid < 0 set/unset address for real device
662+
* @add: add address if the flag is set or remove otherwise
663+
*/
664+
static int cpsw_set_mc(struct net_device *ndev, const u8 *addr,
665+
int vid, int add)
665666
{
666667
struct cpsw_priv *priv = netdev_priv(ndev);
668+
struct cpsw_common *cpsw = priv->cpsw;
669+
int mask, flags, ret;
670+
671+
if (vid < 0) {
672+
if (cpsw->data.dual_emac)
673+
vid = cpsw->slaves[priv->emac_port].port_vlan;
674+
else
675+
vid = 0;
676+
}
677+
678+
mask = cpsw->data.dual_emac ? ALE_PORT_HOST : ALE_ALL_PORTS;
679+
flags = vid ? ALE_VLAN : 0;
680+
681+
if (add)
682+
ret = cpsw_ale_add_mcast(cpsw->ale, addr, mask, flags, vid, 0);
683+
else
684+
ret = cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
685+
686+
return ret;
687+
}
688+
689+
static int cpsw_update_vlan_mc(struct net_device *vdev, int vid, void *ctx)
690+
{
691+
struct addr_sync_ctx *sync_ctx = ctx;
692+
struct netdev_hw_addr *ha;
693+
int found = 0, ret = 0;
694+
695+
if (!vdev || !(vdev->flags & IFF_UP))
696+
return 0;
697+
698+
/* vlan address is relevant if its sync_cnt != 0 */
699+
netdev_for_each_mc_addr(ha, vdev) {
700+
if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
701+
found = ha->sync_cnt;
702+
break;
703+
}
704+
}
705+
706+
if (found)
707+
sync_ctx->consumed++;
708+
709+
if (sync_ctx->flush) {
710+
if (!found)
711+
cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
712+
return 0;
713+
}
714+
715+
if (found)
716+
ret = cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 1);
717+
718+
return ret;
719+
}
720+
721+
static int cpsw_add_mc_addr(struct net_device *ndev, const u8 *addr, int num)
722+
{
723+
struct addr_sync_ctx sync_ctx;
724+
int ret;
725+
726+
sync_ctx.consumed = 0;
727+
sync_ctx.addr = addr;
728+
sync_ctx.ndev = ndev;
729+
sync_ctx.flush = 0;
730+
731+
ret = vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
732+
if (sync_ctx.consumed < num && !ret)
733+
ret = cpsw_set_mc(ndev, addr, -1, 1);
734+
735+
return ret;
736+
}
737+
738+
static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr, int num)
739+
{
740+
struct addr_sync_ctx sync_ctx;
741+
742+
sync_ctx.consumed = 0;
743+
sync_ctx.addr = addr;
744+
sync_ctx.ndev = ndev;
745+
sync_ctx.flush = 1;
746+
747+
vlan_for_each(ndev, cpsw_update_vlan_mc, &sync_ctx);
748+
if (sync_ctx.consumed == num)
749+
cpsw_set_mc(ndev, addr, -1, 0);
667750

668-
cpsw_add_mcast(priv, addr);
669751
return 0;
670752
}
671753

672-
static int cpsw_del_mc_addr(struct net_device *ndev, const u8 *addr)
754+
static int cpsw_purge_vlan_mc(struct net_device *vdev, int vid, void *ctx)
673755
{
674-
struct cpsw_priv *priv = netdev_priv(ndev);
675-
struct cpsw_common *cpsw = priv->cpsw;
676-
int vid, flags;
756+
struct addr_sync_ctx *sync_ctx = ctx;
757+
struct netdev_hw_addr *ha;
758+
int found = 0;
677759

678-
if (cpsw->data.dual_emac) {
679-
vid = cpsw->slaves[priv->emac_port].port_vlan;
680-
flags = ALE_VLAN;
681-
} else {
682-
vid = 0;
683-
flags = 0;
760+
if (!vdev || !(vdev->flags & IFF_UP))
761+
return 0;
762+
763+
/* vlan address is relevant if its sync_cnt != 0 */
764+
netdev_for_each_mc_addr(ha, vdev) {
765+
if (ether_addr_equal(ha->addr, sync_ctx->addr)) {
766+
found = ha->sync_cnt;
767+
break;
768+
}
684769
}
685770

686-
cpsw_ale_del_mcast(cpsw->ale, addr, 0, flags, vid);
771+
if (!found)
772+
return 0;
773+
774+
sync_ctx->consumed++;
775+
cpsw_set_mc(sync_ctx->ndev, sync_ctx->addr, vid, 0);
776+
return 0;
777+
}
778+
779+
static int cpsw_purge_all_mc(struct net_device *ndev, const u8 *addr, int num)
780+
{
781+
struct addr_sync_ctx sync_ctx;
782+
783+
sync_ctx.addr = addr;
784+
sync_ctx.ndev = ndev;
785+
sync_ctx.consumed = 0;
786+
787+
vlan_for_each(ndev, cpsw_purge_vlan_mc, &sync_ctx);
788+
if (sync_ctx.consumed < num)
789+
cpsw_set_mc(ndev, addr, -1, 0);
790+
687791
return 0;
688792
}
689793

@@ -704,7 +808,9 @@ static void cpsw_ndo_set_rx_mode(struct net_device *ndev)
704808
/* Restore allmulti on vlans if necessary */
705809
cpsw_ale_set_allmulti(cpsw->ale, ndev->flags & IFF_ALLMULTI);
706810

707-
__dev_mc_sync(ndev, cpsw_add_mc_addr, cpsw_del_mc_addr);
811+
/* add/remove mcast address either for real netdev or for vlan */
812+
__hw_addr_ref_sync_dev(&ndev->mc, ndev, cpsw_add_mc_addr,
813+
cpsw_del_mc_addr);
708814
}
709815

710816
static void cpsw_intr_enable(struct cpsw_common *cpsw)
@@ -1964,7 +2070,7 @@ static int cpsw_ndo_stop(struct net_device *ndev)
19642070
struct cpsw_common *cpsw = priv->cpsw;
19652071

19662072
cpsw_info(priv, ifdown, "shutting down cpsw device\n");
1967-
__dev_mc_unsync(priv->ndev, cpsw_del_mc_addr);
2073+
__hw_addr_ref_unsync_dev(&ndev->mc, ndev, cpsw_purge_all_mc);
19682074
netif_tx_stop_all_queues(priv->ndev);
19692075
netif_carrier_off(priv->ndev);
19702076

@@ -2415,6 +2521,7 @@ static int cpsw_ndo_vlan_rx_kill_vid(struct net_device *ndev,
24152521
HOST_PORT_NUM, ALE_VLAN, vid);
24162522
ret |= cpsw_ale_del_mcast(cpsw->ale, priv->ndev->broadcast,
24172523
0, ALE_VLAN, vid);
2524+
ret |= cpsw_ale_flush_multicast(cpsw->ale, 0, vid);
24182525
err:
24192526
pm_runtime_put(cpsw->dev);
24202527
return ret;

0 commit comments

Comments
 (0)