diff options
author | Vignesh Raghavendra | 2020-07-30 12:00:39 -0500 |
---|---|---|
committer | Vignesh Raghavendra | 2020-08-03 02:15:10 -0500 |
commit | 705dff231a44935978768e5f1919fcd2044977d2 (patch) | |
tree | 14b4988d8eee1c18560b10fdf159a29391d19f88 | |
parent | 8144016363757f82e6c3e8991e771a0b72af7d6b (diff) | |
download | kernel-705dff231a44935978768e5f1919fcd2044977d2.tar.gz kernel-705dff231a44935978768e5f1919fcd2044977d2.tar.xz kernel-705dff231a44935978768e5f1919fcd2044977d2.zip |
net: ti: prueth_switch: Offload multicast filtering for host port
Prueth switch firmware supports offloading of filtering of multicast
traffic just for CPU port. The programming model is similar to Dual EMAC
mode. Therefore reuse the code to implement offload support in switch
mode.
Note that in switch mode multicast filtering is always enabled.
Currently, therefore no way to disable filter completely because setting
ALLMULTI flag on bridge device is no-op. (See net/bridge/br_device.c
bridge device's ndo_set_rx_mode is set to br_dev_set_multicast_list()
which is an empty function).
Signed-off-by: Vignesh Raghavendra <vigneshr@ti.com>
-rw-r--r-- | drivers/net/ethernet/ti/prueth.h | 3 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/prueth_core.c | 34 | ||||
-rw-r--r-- | drivers/net/ethernet/ti/prueth_switch.c | 74 |
3 files changed, 99 insertions, 12 deletions
diff --git a/drivers/net/ethernet/ti/prueth.h b/drivers/net/ethernet/ti/prueth.h index ab6fcfe769cb..6720b25a4751 100644 --- a/drivers/net/ethernet/ti/prueth.h +++ b/drivers/net/ethernet/ti/prueth.h | |||
@@ -505,4 +505,7 @@ static inline void emac_finish_napi(struct prueth_emac *emac, | |||
505 | enable_irq(irq); | 505 | enable_irq(irq); |
506 | } | 506 | } |
507 | 507 | ||
508 | void emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash); | ||
509 | void emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash); | ||
510 | u8 emac_get_mc_hash(u8 *mac, u8 *mask); | ||
508 | #endif /* __NET_TI_PRUETH_H */ | 511 | #endif /* __NET_TI_PRUETH_H */ |
diff --git a/drivers/net/ethernet/ti/prueth_core.c b/drivers/net/ethernet/ti/prueth_core.c index 0d06ac433ad3..644014f8349d 100644 --- a/drivers/net/ethernet/ti/prueth_core.c +++ b/drivers/net/ethernet/ti/prueth_core.c | |||
@@ -2072,7 +2072,7 @@ static void emac_mc_filter_hashmask(struct prueth_emac *emac, | |||
2072 | ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES); | 2072 | ICSS_EMAC_FW_MULTICAST_FILTER_MASK_SIZE_BYTES); |
2073 | } | 2073 | } |
2074 | 2074 | ||
2075 | static void emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) | 2075 | static void emac_mc_filter_bin_update(struct prueth_emac *emac, u8 hash, u8 val) |
2076 | { | 2076 | { |
2077 | struct prueth *prueth = emac->prueth; | 2077 | struct prueth *prueth = emac->prueth; |
2078 | u32 mc_filter_tbl_base = prueth->fw_offsets->mc_filter_tbl; | 2078 | u32 mc_filter_tbl_base = prueth->fw_offsets->mc_filter_tbl; |
@@ -2083,11 +2083,20 @@ static void emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) | |||
2083 | ram = prueth->mem[PRUETH_MEM_DRAM1].va; | 2083 | ram = prueth->mem[PRUETH_MEM_DRAM1].va; |
2084 | 2084 | ||
2085 | mc_filter_tbl = ram + mc_filter_tbl_base; | 2085 | mc_filter_tbl = ram + mc_filter_tbl_base; |
2086 | writeb(ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED, | 2086 | writeb(val, mc_filter_tbl + hash); |
2087 | mc_filter_tbl + hash); | ||
2088 | } | 2087 | } |
2089 | 2088 | ||
2090 | static u8 emac_get_mc_hash(u8 *mac, u8 *mask) | 2089 | void emac_mc_filter_bin_allow(struct prueth_emac *emac, u8 hash) |
2090 | { | ||
2091 | emac_mc_filter_bin_update(emac, hash, ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_ALLOWED); | ||
2092 | } | ||
2093 | |||
2094 | void emac_mc_filter_bin_disallow(struct prueth_emac *emac, u8 hash) | ||
2095 | { | ||
2096 | emac_mc_filter_bin_update(emac, hash, ICSS_EMAC_FW_MULTICAST_FILTER_HOST_RCV_NOT_ALLOWED); | ||
2097 | } | ||
2098 | |||
2099 | u8 emac_get_mc_hash(u8 *mac, u8 *mask) | ||
2091 | { | 2100 | { |
2092 | int j; | 2101 | int j; |
2093 | u8 hash; | 2102 | u8 hash; |
@@ -2117,13 +2126,6 @@ static void emac_ndo_set_rx_mode(struct net_device *ndev) | |||
2117 | u32 mask; | 2126 | u32 mask; |
2118 | u8 hash; | 2127 | u8 hash; |
2119 | 2128 | ||
2120 | if (PRUETH_IS_SWITCH(prueth)) { | ||
2121 | netdev_dbg(ndev, | ||
2122 | "%s: promisc/mc filtering not supported for switch\n", | ||
2123 | __func__); | ||
2124 | return; | ||
2125 | } | ||
2126 | |||
2127 | if (promisc && PRUETH_IS_LRE(prueth)) { | 2129 | if (promisc && PRUETH_IS_LRE(prueth)) { |
2128 | netdev_dbg(ndev, | 2130 | netdev_dbg(ndev, |
2129 | "%s: promisc mode not supported for LRE\n", | 2131 | "%s: promisc mode not supported for LRE\n", |
@@ -2166,7 +2168,7 @@ static void emac_ndo_set_rx_mode(struct net_device *ndev) | |||
2166 | goto unlock; | 2168 | goto unlock; |
2167 | } | 2169 | } |
2168 | 2170 | ||
2169 | if (ndev->flags & IFF_ALLMULTI) | 2171 | if (ndev->flags & IFF_ALLMULTI && !PRUETH_IS_SWITCH(prueth)) |
2170 | goto unlock; | 2172 | goto unlock; |
2171 | 2173 | ||
2172 | emac_mc_filter_ctrl(emac, true); /* all multicast blocked */ | 2174 | emac_mc_filter_ctrl(emac, true); /* all multicast blocked */ |
@@ -2178,6 +2180,14 @@ static void emac_ndo_set_rx_mode(struct net_device *ndev) | |||
2178 | hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); | 2180 | hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); |
2179 | emac_mc_filter_bin_allow(emac, hash); | 2181 | emac_mc_filter_bin_allow(emac, hash); |
2180 | } | 2182 | } |
2183 | |||
2184 | /* Add bridge device's MC addresses as well */ | ||
2185 | if (prueth->hw_bridge_dev) { | ||
2186 | netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { | ||
2187 | hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); | ||
2188 | emac_mc_filter_bin_allow(emac, hash); | ||
2189 | } | ||
2190 | } | ||
2181 | unlock: | 2191 | unlock: |
2182 | spin_unlock_irqrestore(&emac->addr_lock, flags); | 2192 | spin_unlock_irqrestore(&emac->addr_lock, flags); |
2183 | } | 2193 | } |
diff --git a/drivers/net/ethernet/ti/prueth_switch.c b/drivers/net/ethernet/ti/prueth_switch.c index f5891306baad..edb9473928b7 100644 --- a/drivers/net/ethernet/ti/prueth_switch.c +++ b/drivers/net/ethernet/ti/prueth_switch.c | |||
@@ -1075,6 +1075,68 @@ static int prueth_switchdev_attr_set(struct net_device *ndev, | |||
1075 | return err; | 1075 | return err; |
1076 | } | 1076 | } |
1077 | 1077 | ||
1078 | static int prueth_switchdev_obj_add(struct net_device *ndev, | ||
1079 | const struct switchdev_obj *obj, | ||
1080 | struct switchdev_trans *trans, | ||
1081 | struct netlink_ext_ack *extack) | ||
1082 | { | ||
1083 | struct prueth_emac *emac = netdev_priv(ndev); | ||
1084 | struct prueth *prueth = emac->prueth; | ||
1085 | struct switchdev_obj_port_mdb *mdb; | ||
1086 | int ret = 0; | ||
1087 | u8 hash; | ||
1088 | |||
1089 | if (switchdev_trans_ph_prepare(trans)) | ||
1090 | return 0; | ||
1091 | |||
1092 | switch (obj->id) { | ||
1093 | case SWITCHDEV_OBJ_ID_HOST_MDB: | ||
1094 | mdb = SWITCHDEV_OBJ_PORT_MDB(obj); | ||
1095 | dev_dbg(prueth->dev, "MDB add: %s: vid %u:%pM port: %x\n", | ||
1096 | ndev->name, mdb->vid, mdb->addr, emac->port_id); | ||
1097 | hash = emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); | ||
1098 | emac_mc_filter_bin_allow(emac, hash); | ||
1099 | break; | ||
1100 | default: | ||
1101 | ret = -EOPNOTSUPP; | ||
1102 | break; | ||
1103 | } | ||
1104 | |||
1105 | return ret; | ||
1106 | } | ||
1107 | |||
1108 | static int prueth_switchdev_obj_del(struct net_device *ndev, | ||
1109 | const struct switchdev_obj *obj) | ||
1110 | { | ||
1111 | struct prueth_emac *emac = netdev_priv(ndev); | ||
1112 | struct prueth *prueth = emac->prueth; | ||
1113 | struct switchdev_obj_port_mdb *mdb; | ||
1114 | struct netdev_hw_addr *ha; | ||
1115 | u8 hash, tmp_hash; | ||
1116 | int ret = 0; | ||
1117 | |||
1118 | switch (obj->id) { | ||
1119 | case SWITCHDEV_OBJ_ID_HOST_MDB: | ||
1120 | mdb = SWITCHDEV_OBJ_PORT_MDB(obj); | ||
1121 | dev_dbg(prueth->dev, "MDB del: %s: vid %u:%pM port: %x\n", | ||
1122 | ndev->name, mdb->vid, mdb->addr, emac->port_id); | ||
1123 | hash = emac_get_mc_hash(mdb->addr, emac->mc_filter_mask); | ||
1124 | netdev_for_each_mc_addr(ha, prueth->hw_bridge_dev) { | ||
1125 | tmp_hash = emac_get_mc_hash(ha->addr, emac->mc_filter_mask); | ||
1126 | /* Another MC address is in the bin. Don't disable. */ | ||
1127 | if (tmp_hash == hash) | ||
1128 | return 0; | ||
1129 | } | ||
1130 | emac_mc_filter_bin_disallow(emac, hash); | ||
1131 | break; | ||
1132 | default: | ||
1133 | ret = -EOPNOTSUPP; | ||
1134 | break; | ||
1135 | } | ||
1136 | |||
1137 | return ret; | ||
1138 | } | ||
1139 | |||
1078 | /* switchdev notifiers */ | 1140 | /* switchdev notifiers */ |
1079 | static int prueth_sw_switchdev_blocking_event(struct notifier_block *unused, | 1141 | static int prueth_sw_switchdev_blocking_event(struct notifier_block *unused, |
1080 | unsigned long event, void *ptr) | 1142 | unsigned long event, void *ptr) |
@@ -1093,6 +1155,18 @@ static int prueth_sw_switchdev_blocking_event(struct notifier_block *unused, | |||
1093 | prueth_sw_port_dev_check, | 1155 | prueth_sw_port_dev_check, |
1094 | prueth_switchdev_attr_set); | 1156 | prueth_switchdev_attr_set); |
1095 | return notifier_from_errno(err); | 1157 | return notifier_from_errno(err); |
1158 | |||
1159 | case SWITCHDEV_PORT_OBJ_ADD: | ||
1160 | err = switchdev_handle_port_obj_add(ndev, ptr, | ||
1161 | prueth_sw_port_dev_check, | ||
1162 | prueth_switchdev_obj_add); | ||
1163 | return notifier_from_errno(err); | ||
1164 | |||
1165 | case SWITCHDEV_PORT_OBJ_DEL: | ||
1166 | err = switchdev_handle_port_obj_del(ndev, ptr, | ||
1167 | prueth_sw_port_dev_check, | ||
1168 | prueth_switchdev_obj_del); | ||
1169 | return notifier_from_errno(err); | ||
1096 | default: | 1170 | default: |
1097 | break; | 1171 | break; |
1098 | } | 1172 | } |