.. | .. |
---|
15 | 15 | #include <linux/rtnetlink.h> |
---|
16 | 16 | #include <linux/netlink.h> |
---|
17 | 17 | #include <net/switchdev.h> |
---|
| 18 | +#include <net/vxlan.h> |
---|
18 | 19 | |
---|
19 | 20 | #include "spectrum_span.h" |
---|
20 | | -#include "spectrum_router.h" |
---|
21 | 21 | #include "spectrum_switchdev.h" |
---|
22 | 22 | #include "spectrum.h" |
---|
23 | 23 | #include "core.h" |
---|
.. | .. |
---|
84 | 84 | void (*port_leave)(struct mlxsw_sp_bridge_device *bridge_device, |
---|
85 | 85 | struct mlxsw_sp_bridge_port *bridge_port, |
---|
86 | 86 | struct mlxsw_sp_port *mlxsw_sp_port); |
---|
| 87 | + int (*vxlan_join)(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 88 | + const struct net_device *vxlan_dev, u16 vid, |
---|
| 89 | + struct netlink_ext_ack *extack); |
---|
87 | 90 | struct mlxsw_sp_fid * |
---|
88 | 91 | (*fid_get)(struct mlxsw_sp_bridge_device *bridge_device, |
---|
89 | | - u16 vid); |
---|
| 92 | + u16 vid, struct netlink_ext_ack *extack); |
---|
| 93 | + struct mlxsw_sp_fid * |
---|
| 94 | + (*fid_lookup)(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 95 | + u16 vid); |
---|
| 96 | + u16 (*fid_vid)(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 97 | + const struct mlxsw_sp_fid *fid); |
---|
90 | 98 | }; |
---|
91 | 99 | |
---|
92 | 100 | static int |
---|
.. | .. |
---|
128 | 136 | } |
---|
129 | 137 | |
---|
130 | 138 | static int mlxsw_sp_bridge_device_upper_rif_destroy(struct net_device *dev, |
---|
131 | | - void *data) |
---|
| 139 | + struct netdev_nested_priv *priv) |
---|
132 | 140 | { |
---|
133 | | - struct mlxsw_sp *mlxsw_sp = data; |
---|
| 141 | + struct mlxsw_sp *mlxsw_sp = priv->data; |
---|
134 | 142 | |
---|
135 | 143 | mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev); |
---|
136 | 144 | return 0; |
---|
.. | .. |
---|
139 | 147 | static void mlxsw_sp_bridge_device_rifs_destroy(struct mlxsw_sp *mlxsw_sp, |
---|
140 | 148 | struct net_device *dev) |
---|
141 | 149 | { |
---|
| 150 | + struct netdev_nested_priv priv = { |
---|
| 151 | + .data = (void *)mlxsw_sp, |
---|
| 152 | + }; |
---|
| 153 | + |
---|
142 | 154 | mlxsw_sp_rif_destroy_by_dev(mlxsw_sp, dev); |
---|
143 | 155 | netdev_walk_all_upper_dev_rcu(dev, |
---|
144 | 156 | mlxsw_sp_bridge_device_upper_rif_destroy, |
---|
145 | | - mlxsw_sp); |
---|
| 157 | + &priv); |
---|
| 158 | +} |
---|
| 159 | + |
---|
| 160 | +static int mlxsw_sp_bridge_device_vxlan_init(struct mlxsw_sp_bridge *bridge, |
---|
| 161 | + struct net_device *br_dev, |
---|
| 162 | + struct netlink_ext_ack *extack) |
---|
| 163 | +{ |
---|
| 164 | + struct net_device *dev, *stop_dev; |
---|
| 165 | + struct list_head *iter; |
---|
| 166 | + int err; |
---|
| 167 | + |
---|
| 168 | + netdev_for_each_lower_dev(br_dev, dev, iter) { |
---|
| 169 | + if (netif_is_vxlan(dev) && netif_running(dev)) { |
---|
| 170 | + err = mlxsw_sp_bridge_vxlan_join(bridge->mlxsw_sp, |
---|
| 171 | + br_dev, dev, 0, |
---|
| 172 | + extack); |
---|
| 173 | + if (err) { |
---|
| 174 | + stop_dev = dev; |
---|
| 175 | + goto err_vxlan_join; |
---|
| 176 | + } |
---|
| 177 | + } |
---|
| 178 | + } |
---|
| 179 | + |
---|
| 180 | + return 0; |
---|
| 181 | + |
---|
| 182 | +err_vxlan_join: |
---|
| 183 | + netdev_for_each_lower_dev(br_dev, dev, iter) { |
---|
| 184 | + if (netif_is_vxlan(dev) && netif_running(dev)) { |
---|
| 185 | + if (stop_dev == dev) |
---|
| 186 | + break; |
---|
| 187 | + mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev); |
---|
| 188 | + } |
---|
| 189 | + } |
---|
| 190 | + return err; |
---|
| 191 | +} |
---|
| 192 | + |
---|
| 193 | +static void mlxsw_sp_bridge_device_vxlan_fini(struct mlxsw_sp_bridge *bridge, |
---|
| 194 | + struct net_device *br_dev) |
---|
| 195 | +{ |
---|
| 196 | + struct net_device *dev; |
---|
| 197 | + struct list_head *iter; |
---|
| 198 | + |
---|
| 199 | + netdev_for_each_lower_dev(br_dev, dev, iter) { |
---|
| 200 | + if (netif_is_vxlan(dev) && netif_running(dev)) |
---|
| 201 | + mlxsw_sp_bridge_vxlan_leave(bridge->mlxsw_sp, dev); |
---|
| 202 | + } |
---|
146 | 203 | } |
---|
147 | 204 | |
---|
148 | 205 | static struct mlxsw_sp_bridge_device * |
---|
149 | 206 | mlxsw_sp_bridge_device_create(struct mlxsw_sp_bridge *bridge, |
---|
150 | | - struct net_device *br_dev) |
---|
| 207 | + struct net_device *br_dev, |
---|
| 208 | + struct netlink_ext_ack *extack) |
---|
151 | 209 | { |
---|
152 | 210 | struct device *dev = bridge->mlxsw_sp->bus_info->dev; |
---|
153 | 211 | struct mlxsw_sp_bridge_device *bridge_device; |
---|
154 | 212 | bool vlan_enabled = br_vlan_enabled(br_dev); |
---|
| 213 | + int err; |
---|
155 | 214 | |
---|
156 | 215 | if (vlan_enabled && bridge->vlan_enabled_exists) { |
---|
157 | 216 | dev_err(dev, "Only one VLAN-aware bridge is supported\n"); |
---|
| 217 | + NL_SET_ERR_MSG_MOD(extack, "Only one VLAN-aware bridge is supported"); |
---|
158 | 218 | return ERR_PTR(-EINVAL); |
---|
159 | 219 | } |
---|
160 | 220 | |
---|
.. | .. |
---|
176 | 236 | INIT_LIST_HEAD(&bridge_device->mids_list); |
---|
177 | 237 | list_add(&bridge_device->list, &bridge->bridges_list); |
---|
178 | 238 | |
---|
| 239 | + /* It is possible we already have VXLAN devices enslaved to the bridge. |
---|
| 240 | + * In which case, we need to replay their configuration as if they were |
---|
| 241 | + * just now enslaved to the bridge. |
---|
| 242 | + */ |
---|
| 243 | + err = mlxsw_sp_bridge_device_vxlan_init(bridge, br_dev, extack); |
---|
| 244 | + if (err) |
---|
| 245 | + goto err_vxlan_init; |
---|
| 246 | + |
---|
179 | 247 | return bridge_device; |
---|
| 248 | + |
---|
| 249 | +err_vxlan_init: |
---|
| 250 | + list_del(&bridge_device->list); |
---|
| 251 | + if (bridge_device->vlan_enabled) |
---|
| 252 | + bridge->vlan_enabled_exists = false; |
---|
| 253 | + kfree(bridge_device); |
---|
| 254 | + return ERR_PTR(err); |
---|
180 | 255 | } |
---|
181 | 256 | |
---|
182 | 257 | static void |
---|
183 | 258 | mlxsw_sp_bridge_device_destroy(struct mlxsw_sp_bridge *bridge, |
---|
184 | 259 | struct mlxsw_sp_bridge_device *bridge_device) |
---|
185 | 260 | { |
---|
| 261 | + mlxsw_sp_bridge_device_vxlan_fini(bridge, bridge_device->dev); |
---|
186 | 262 | mlxsw_sp_bridge_device_rifs_destroy(bridge->mlxsw_sp, |
---|
187 | 263 | bridge_device->dev); |
---|
188 | 264 | list_del(&bridge_device->list); |
---|
.. | .. |
---|
195 | 271 | |
---|
196 | 272 | static struct mlxsw_sp_bridge_device * |
---|
197 | 273 | mlxsw_sp_bridge_device_get(struct mlxsw_sp_bridge *bridge, |
---|
198 | | - struct net_device *br_dev) |
---|
| 274 | + struct net_device *br_dev, |
---|
| 275 | + struct netlink_ext_ack *extack) |
---|
199 | 276 | { |
---|
200 | 277 | struct mlxsw_sp_bridge_device *bridge_device; |
---|
201 | 278 | |
---|
.. | .. |
---|
203 | 280 | if (bridge_device) |
---|
204 | 281 | return bridge_device; |
---|
205 | 282 | |
---|
206 | | - return mlxsw_sp_bridge_device_create(bridge, br_dev); |
---|
| 283 | + return mlxsw_sp_bridge_device_create(bridge, br_dev, extack); |
---|
207 | 284 | } |
---|
208 | 285 | |
---|
209 | 286 | static void |
---|
.. | .. |
---|
284 | 361 | |
---|
285 | 362 | static struct mlxsw_sp_bridge_port * |
---|
286 | 363 | mlxsw_sp_bridge_port_get(struct mlxsw_sp_bridge *bridge, |
---|
287 | | - struct net_device *brport_dev) |
---|
| 364 | + struct net_device *brport_dev, |
---|
| 365 | + struct netlink_ext_ack *extack) |
---|
288 | 366 | { |
---|
289 | 367 | struct net_device *br_dev = netdev_master_upper_dev_get(brport_dev); |
---|
290 | 368 | struct mlxsw_sp_bridge_device *bridge_device; |
---|
.. | .. |
---|
297 | 375 | return bridge_port; |
---|
298 | 376 | } |
---|
299 | 377 | |
---|
300 | | - bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev); |
---|
| 378 | + bridge_device = mlxsw_sp_bridge_device_get(bridge, br_dev, extack); |
---|
301 | 379 | if (IS_ERR(bridge_device)) |
---|
302 | 380 | return ERR_CAST(bridge_device); |
---|
303 | 381 | |
---|
.. | .. |
---|
421 | 499 | { |
---|
422 | 500 | if (list_empty(&bridge_vlan->port_vlan_list)) |
---|
423 | 501 | mlxsw_sp_bridge_vlan_destroy(bridge_vlan); |
---|
424 | | -} |
---|
425 | | - |
---|
426 | | -static void mlxsw_sp_port_bridge_flags_get(struct mlxsw_sp_bridge *bridge, |
---|
427 | | - struct net_device *dev, |
---|
428 | | - unsigned long *brport_flags) |
---|
429 | | -{ |
---|
430 | | - struct mlxsw_sp_bridge_port *bridge_port; |
---|
431 | | - |
---|
432 | | - bridge_port = mlxsw_sp_bridge_port_find(bridge, dev); |
---|
433 | | - if (WARN_ON(!bridge_port)) |
---|
434 | | - return; |
---|
435 | | - |
---|
436 | | - memcpy(brport_flags, &bridge_port->flags, sizeof(*brport_flags)); |
---|
437 | | -} |
---|
438 | | - |
---|
439 | | -static int mlxsw_sp_port_attr_get(struct net_device *dev, |
---|
440 | | - struct switchdev_attr *attr) |
---|
441 | | -{ |
---|
442 | | - struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
---|
443 | | - struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; |
---|
444 | | - |
---|
445 | | - switch (attr->id) { |
---|
446 | | - case SWITCHDEV_ATTR_ID_PORT_PARENT_ID: |
---|
447 | | - attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac); |
---|
448 | | - memcpy(&attr->u.ppid.id, &mlxsw_sp->base_mac, |
---|
449 | | - attr->u.ppid.id_len); |
---|
450 | | - break; |
---|
451 | | - case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: |
---|
452 | | - mlxsw_sp_port_bridge_flags_get(mlxsw_sp->bridge, attr->orig_dev, |
---|
453 | | - &attr->u.brport_flags); |
---|
454 | | - break; |
---|
455 | | - case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: |
---|
456 | | - attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD | |
---|
457 | | - BR_MCAST_FLOOD; |
---|
458 | | - break; |
---|
459 | | - default: |
---|
460 | | - return -EOPNOTSUPP; |
---|
461 | | - } |
---|
462 | | - |
---|
463 | | - return 0; |
---|
464 | 502 | } |
---|
465 | 503 | |
---|
466 | 504 | static int |
---|
.. | .. |
---|
610 | 648 | mlxsw_sp_port_bridge_vlan_learning_set(mlxsw_sp_port, |
---|
611 | 649 | bridge_vlan, !set); |
---|
612 | 650 | return err; |
---|
| 651 | +} |
---|
| 652 | + |
---|
| 653 | +static int mlxsw_sp_port_attr_br_pre_flags_set(struct mlxsw_sp_port |
---|
| 654 | + *mlxsw_sp_port, |
---|
| 655 | + struct switchdev_trans *trans, |
---|
| 656 | + unsigned long brport_flags) |
---|
| 657 | +{ |
---|
| 658 | + if (brport_flags & ~(BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD)) |
---|
| 659 | + return -EINVAL; |
---|
| 660 | + |
---|
| 661 | + return 0; |
---|
613 | 662 | } |
---|
614 | 663 | |
---|
615 | 664 | static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port, |
---|
.. | .. |
---|
858 | 907 | attr->orig_dev, |
---|
859 | 908 | attr->u.stp_state); |
---|
860 | 909 | break; |
---|
| 910 | + case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS: |
---|
| 911 | + err = mlxsw_sp_port_attr_br_pre_flags_set(mlxsw_sp_port, |
---|
| 912 | + trans, |
---|
| 913 | + attr->u.brport_flags); |
---|
| 914 | + break; |
---|
861 | 915 | case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS: |
---|
862 | 916 | err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans, |
---|
863 | 917 | attr->orig_dev, |
---|
.. | .. |
---|
900 | 954 | |
---|
901 | 955 | static int |
---|
902 | 956 | mlxsw_sp_port_vlan_fid_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, |
---|
903 | | - struct mlxsw_sp_bridge_port *bridge_port) |
---|
| 957 | + struct mlxsw_sp_bridge_port *bridge_port, |
---|
| 958 | + struct netlink_ext_ack *extack) |
---|
904 | 959 | { |
---|
905 | 960 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; |
---|
906 | 961 | struct mlxsw_sp_bridge_device *bridge_device; |
---|
.. | .. |
---|
910 | 965 | int err; |
---|
911 | 966 | |
---|
912 | 967 | bridge_device = bridge_port->bridge_device; |
---|
913 | | - fid = bridge_device->ops->fid_get(bridge_device, vid); |
---|
| 968 | + fid = bridge_device->ops->fid_get(bridge_device, vid, extack); |
---|
914 | 969 | if (IS_ERR(fid)) |
---|
915 | 970 | return PTR_ERR(fid); |
---|
916 | 971 | |
---|
.. | .. |
---|
978 | 1033 | |
---|
979 | 1034 | static int |
---|
980 | 1035 | mlxsw_sp_port_vlan_bridge_join(struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan, |
---|
981 | | - struct mlxsw_sp_bridge_port *bridge_port) |
---|
| 1036 | + struct mlxsw_sp_bridge_port *bridge_port, |
---|
| 1037 | + struct netlink_ext_ack *extack) |
---|
982 | 1038 | { |
---|
983 | 1039 | struct mlxsw_sp_port *mlxsw_sp_port = mlxsw_sp_port_vlan->mlxsw_sp_port; |
---|
984 | 1040 | struct mlxsw_sp_bridge_vlan *bridge_vlan; |
---|
.. | .. |
---|
986 | 1042 | int err; |
---|
987 | 1043 | |
---|
988 | 1044 | /* No need to continue if only VLAN flags were changed */ |
---|
989 | | - if (mlxsw_sp_port_vlan->bridge_port) { |
---|
990 | | - mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); |
---|
| 1045 | + if (mlxsw_sp_port_vlan->bridge_port) |
---|
991 | 1046 | return 0; |
---|
992 | | - } |
---|
993 | 1047 | |
---|
994 | | - err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port); |
---|
| 1048 | + err = mlxsw_sp_port_vlan_fid_join(mlxsw_sp_port_vlan, bridge_port, |
---|
| 1049 | + extack); |
---|
995 | 1050 | if (err) |
---|
996 | 1051 | return err; |
---|
997 | 1052 | |
---|
.. | .. |
---|
1015 | 1070 | &bridge_vlan->port_vlan_list); |
---|
1016 | 1071 | |
---|
1017 | 1072 | mlxsw_sp_bridge_port_get(mlxsw_sp_port->mlxsw_sp->bridge, |
---|
1018 | | - bridge_port->dev); |
---|
| 1073 | + bridge_port->dev, extack); |
---|
1019 | 1074 | mlxsw_sp_port_vlan->bridge_port = bridge_port; |
---|
1020 | 1075 | |
---|
1021 | 1076 | return 0; |
---|
.. | .. |
---|
1068 | 1123 | static int |
---|
1069 | 1124 | mlxsw_sp_bridge_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, |
---|
1070 | 1125 | struct mlxsw_sp_bridge_port *bridge_port, |
---|
1071 | | - u16 vid, bool is_untagged, bool is_pvid) |
---|
| 1126 | + u16 vid, bool is_untagged, bool is_pvid, |
---|
| 1127 | + struct netlink_ext_ack *extack) |
---|
1072 | 1128 | { |
---|
1073 | 1129 | u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid); |
---|
1074 | 1130 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; |
---|
1075 | 1131 | u16 old_pvid = mlxsw_sp_port->pvid; |
---|
1076 | 1132 | int err; |
---|
1077 | 1133 | |
---|
1078 | | - mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_get(mlxsw_sp_port, vid); |
---|
1079 | | - if (IS_ERR(mlxsw_sp_port_vlan)) |
---|
1080 | | - return PTR_ERR(mlxsw_sp_port_vlan); |
---|
| 1134 | + /* The only valid scenario in which a port-vlan already exists, is if |
---|
| 1135 | + * the VLAN flags were changed and the port-vlan is associated with the |
---|
| 1136 | + * correct bridge port |
---|
| 1137 | + */ |
---|
| 1138 | + mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); |
---|
| 1139 | + if (mlxsw_sp_port_vlan && |
---|
| 1140 | + mlxsw_sp_port_vlan->bridge_port != bridge_port) |
---|
| 1141 | + return -EEXIST; |
---|
| 1142 | + |
---|
| 1143 | + if (!mlxsw_sp_port_vlan) { |
---|
| 1144 | + mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_create(mlxsw_sp_port, |
---|
| 1145 | + vid); |
---|
| 1146 | + if (IS_ERR(mlxsw_sp_port_vlan)) |
---|
| 1147 | + return PTR_ERR(mlxsw_sp_port_vlan); |
---|
| 1148 | + } |
---|
1081 | 1149 | |
---|
1082 | 1150 | err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true, |
---|
1083 | 1151 | is_untagged); |
---|
.. | .. |
---|
1088 | 1156 | if (err) |
---|
1089 | 1157 | goto err_port_pvid_set; |
---|
1090 | 1158 | |
---|
1091 | | - err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port); |
---|
| 1159 | + err = mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port, |
---|
| 1160 | + extack); |
---|
1092 | 1161 | if (err) |
---|
1093 | 1162 | goto err_port_vlan_bridge_join; |
---|
1094 | 1163 | |
---|
.. | .. |
---|
1099 | 1168 | err_port_pvid_set: |
---|
1100 | 1169 | mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); |
---|
1101 | 1170 | err_port_vlan_set: |
---|
1102 | | - mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); |
---|
| 1171 | + mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); |
---|
1103 | 1172 | return err; |
---|
1104 | 1173 | } |
---|
1105 | 1174 | |
---|
.. | .. |
---|
1108 | 1177 | const struct net_device *br_dev, |
---|
1109 | 1178 | const struct switchdev_obj_port_vlan *vlan) |
---|
1110 | 1179 | { |
---|
1111 | | - struct mlxsw_sp_rif *rif; |
---|
1112 | | - struct mlxsw_sp_fid *fid; |
---|
1113 | 1180 | u16 pvid; |
---|
1114 | 1181 | u16 vid; |
---|
1115 | 1182 | |
---|
1116 | | - rif = mlxsw_sp_rif_find_by_dev(mlxsw_sp, br_dev); |
---|
1117 | | - if (!rif) |
---|
| 1183 | + pvid = mlxsw_sp_rif_vid(mlxsw_sp, br_dev); |
---|
| 1184 | + if (!pvid) |
---|
1118 | 1185 | return 0; |
---|
1119 | | - fid = mlxsw_sp_rif_fid(rif); |
---|
1120 | | - pvid = mlxsw_sp_fid_8021q_vid(fid); |
---|
1121 | 1186 | |
---|
1122 | 1187 | for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { |
---|
1123 | 1188 | if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { |
---|
.. | .. |
---|
1138 | 1203 | |
---|
1139 | 1204 | static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port, |
---|
1140 | 1205 | const struct switchdev_obj_port_vlan *vlan, |
---|
1141 | | - struct switchdev_trans *trans) |
---|
| 1206 | + struct switchdev_trans *trans, |
---|
| 1207 | + struct netlink_ext_ack *extack) |
---|
1142 | 1208 | { |
---|
1143 | 1209 | bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; |
---|
1144 | 1210 | bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; |
---|
.. | .. |
---|
1160 | 1226 | return err; |
---|
1161 | 1227 | } |
---|
1162 | 1228 | |
---|
1163 | | - if (switchdev_trans_ph_prepare(trans)) |
---|
| 1229 | + if (switchdev_trans_ph_commit(trans)) |
---|
1164 | 1230 | return 0; |
---|
1165 | 1231 | |
---|
1166 | 1232 | bridge_port = mlxsw_sp_bridge_port_find(mlxsw_sp->bridge, orig_dev); |
---|
.. | .. |
---|
1175 | 1241 | |
---|
1176 | 1242 | err = mlxsw_sp_bridge_port_vlan_add(mlxsw_sp_port, bridge_port, |
---|
1177 | 1243 | vid, flag_untagged, |
---|
1178 | | - flag_pvid); |
---|
| 1244 | + flag_pvid, extack); |
---|
1179 | 1245 | if (err) |
---|
1180 | 1246 | return err; |
---|
1181 | 1247 | } |
---|
.. | .. |
---|
1216 | 1282 | { |
---|
1217 | 1283 | return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT : |
---|
1218 | 1284 | MLXSW_REG_SFD_OP_WRITE_REMOVE; |
---|
| 1285 | +} |
---|
| 1286 | + |
---|
| 1287 | +static int mlxsw_sp_port_fdb_tunnel_uc_op(struct mlxsw_sp *mlxsw_sp, |
---|
| 1288 | + const char *mac, u16 fid, |
---|
| 1289 | + enum mlxsw_sp_l3proto proto, |
---|
| 1290 | + const union mlxsw_sp_l3addr *addr, |
---|
| 1291 | + bool adding, bool dynamic) |
---|
| 1292 | +{ |
---|
| 1293 | + enum mlxsw_reg_sfd_uc_tunnel_protocol sfd_proto; |
---|
| 1294 | + char *sfd_pl; |
---|
| 1295 | + u8 num_rec; |
---|
| 1296 | + u32 uip; |
---|
| 1297 | + int err; |
---|
| 1298 | + |
---|
| 1299 | + switch (proto) { |
---|
| 1300 | + case MLXSW_SP_L3_PROTO_IPV4: |
---|
| 1301 | + uip = be32_to_cpu(addr->addr4); |
---|
| 1302 | + sfd_proto = MLXSW_REG_SFD_UC_TUNNEL_PROTOCOL_IPV4; |
---|
| 1303 | + break; |
---|
| 1304 | + case MLXSW_SP_L3_PROTO_IPV6: |
---|
| 1305 | + default: |
---|
| 1306 | + WARN_ON(1); |
---|
| 1307 | + return -EOPNOTSUPP; |
---|
| 1308 | + } |
---|
| 1309 | + |
---|
| 1310 | + sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL); |
---|
| 1311 | + if (!sfd_pl) |
---|
| 1312 | + return -ENOMEM; |
---|
| 1313 | + |
---|
| 1314 | + mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0); |
---|
| 1315 | + mlxsw_reg_sfd_uc_tunnel_pack(sfd_pl, 0, |
---|
| 1316 | + mlxsw_sp_sfd_rec_policy(dynamic), mac, fid, |
---|
| 1317 | + MLXSW_REG_SFD_REC_ACTION_NOP, uip, |
---|
| 1318 | + sfd_proto); |
---|
| 1319 | + num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl); |
---|
| 1320 | + err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl); |
---|
| 1321 | + if (err) |
---|
| 1322 | + goto out; |
---|
| 1323 | + |
---|
| 1324 | + if (num_rec != mlxsw_reg_sfd_num_rec_get(sfd_pl)) |
---|
| 1325 | + err = -EBUSY; |
---|
| 1326 | + |
---|
| 1327 | +out: |
---|
| 1328 | + kfree(sfd_pl); |
---|
| 1329 | + return err; |
---|
1219 | 1330 | } |
---|
1220 | 1331 | |
---|
1221 | 1332 | static int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port, |
---|
.. | .. |
---|
1667 | 1778 | } |
---|
1668 | 1779 | } |
---|
1669 | 1780 | |
---|
1670 | | -struct mlxsw_sp_span_respin_work { |
---|
1671 | | - struct work_struct work; |
---|
1672 | | - struct mlxsw_sp *mlxsw_sp; |
---|
1673 | | -}; |
---|
1674 | | - |
---|
1675 | | -static void mlxsw_sp_span_respin_work(struct work_struct *work) |
---|
1676 | | -{ |
---|
1677 | | - struct mlxsw_sp_span_respin_work *respin_work = |
---|
1678 | | - container_of(work, struct mlxsw_sp_span_respin_work, work); |
---|
1679 | | - |
---|
1680 | | - rtnl_lock(); |
---|
1681 | | - mlxsw_sp_span_respin(respin_work->mlxsw_sp); |
---|
1682 | | - rtnl_unlock(); |
---|
1683 | | - kfree(respin_work); |
---|
1684 | | -} |
---|
1685 | | - |
---|
1686 | | -static void mlxsw_sp_span_respin_schedule(struct mlxsw_sp *mlxsw_sp) |
---|
1687 | | -{ |
---|
1688 | | - struct mlxsw_sp_span_respin_work *respin_work; |
---|
1689 | | - |
---|
1690 | | - respin_work = kzalloc(sizeof(*respin_work), GFP_ATOMIC); |
---|
1691 | | - if (!respin_work) |
---|
1692 | | - return; |
---|
1693 | | - |
---|
1694 | | - INIT_WORK(&respin_work->work, mlxsw_sp_span_respin_work); |
---|
1695 | | - respin_work->mlxsw_sp = mlxsw_sp; |
---|
1696 | | - |
---|
1697 | | - mlxsw_core_schedule_work(&respin_work->work); |
---|
1698 | | -} |
---|
1699 | | - |
---|
1700 | 1781 | static int mlxsw_sp_port_obj_add(struct net_device *dev, |
---|
1701 | 1782 | const struct switchdev_obj *obj, |
---|
1702 | | - struct switchdev_trans *trans) |
---|
| 1783 | + struct switchdev_trans *trans, |
---|
| 1784 | + struct netlink_ext_ack *extack) |
---|
1703 | 1785 | { |
---|
1704 | 1786 | struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); |
---|
1705 | 1787 | const struct switchdev_obj_port_vlan *vlan; |
---|
.. | .. |
---|
1708 | 1790 | switch (obj->id) { |
---|
1709 | 1791 | case SWITCHDEV_OBJ_ID_PORT_VLAN: |
---|
1710 | 1792 | vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); |
---|
1711 | | - err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans); |
---|
| 1793 | + err = mlxsw_sp_port_vlans_add(mlxsw_sp_port, vlan, trans, |
---|
| 1794 | + extack); |
---|
1712 | 1795 | |
---|
1713 | 1796 | if (switchdev_trans_ph_prepare(trans)) { |
---|
1714 | 1797 | /* The event is emitted before the changes are actually |
---|
.. | .. |
---|
1716 | 1799 | * call for later, so that the respin logic sees the |
---|
1717 | 1800 | * updated bridge state. |
---|
1718 | 1801 | */ |
---|
1719 | | - mlxsw_sp_span_respin_schedule(mlxsw_sp_port->mlxsw_sp); |
---|
| 1802 | + mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); |
---|
1720 | 1803 | } |
---|
1721 | 1804 | break; |
---|
1722 | 1805 | case SWITCHDEV_OBJ_ID_PORT_MDB: |
---|
.. | .. |
---|
1746 | 1829 | mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); |
---|
1747 | 1830 | mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid); |
---|
1748 | 1831 | mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false); |
---|
1749 | | - mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); |
---|
| 1832 | + mlxsw_sp_port_vlan_destroy(mlxsw_sp_port_vlan); |
---|
1750 | 1833 | } |
---|
1751 | 1834 | |
---|
1752 | 1835 | static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port, |
---|
.. | .. |
---|
1869 | 1952 | break; |
---|
1870 | 1953 | } |
---|
1871 | 1954 | |
---|
1872 | | - mlxsw_sp_span_respin_schedule(mlxsw_sp_port->mlxsw_sp); |
---|
| 1955 | + mlxsw_sp_span_respin(mlxsw_sp_port->mlxsw_sp); |
---|
1873 | 1956 | |
---|
1874 | 1957 | return err; |
---|
1875 | 1958 | } |
---|
.. | .. |
---|
1891 | 1974 | return NULL; |
---|
1892 | 1975 | } |
---|
1893 | 1976 | |
---|
1894 | | -static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = { |
---|
1895 | | - .switchdev_port_attr_get = mlxsw_sp_port_attr_get, |
---|
1896 | | - .switchdev_port_attr_set = mlxsw_sp_port_attr_set, |
---|
1897 | | - .switchdev_port_obj_add = mlxsw_sp_port_obj_add, |
---|
1898 | | - .switchdev_port_obj_del = mlxsw_sp_port_obj_del, |
---|
1899 | | -}; |
---|
1900 | | - |
---|
1901 | 1977 | static int |
---|
1902 | 1978 | mlxsw_sp_bridge_8021q_port_join(struct mlxsw_sp_bridge_device *bridge_device, |
---|
1903 | 1979 | struct mlxsw_sp_bridge_port *bridge_port, |
---|
1904 | 1980 | struct mlxsw_sp_port *mlxsw_sp_port, |
---|
1905 | 1981 | struct netlink_ext_ack *extack) |
---|
1906 | 1982 | { |
---|
1907 | | - struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; |
---|
1908 | | - |
---|
1909 | 1983 | if (is_vlan_dev(bridge_port->dev)) { |
---|
1910 | 1984 | NL_SET_ERR_MSG_MOD(extack, "Can not enslave a VLAN device to a VLAN-aware bridge"); |
---|
1911 | 1985 | return -EINVAL; |
---|
1912 | 1986 | } |
---|
1913 | 1987 | |
---|
1914 | | - mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, 1); |
---|
1915 | | - if (WARN_ON(!mlxsw_sp_port_vlan)) |
---|
1916 | | - return -EINVAL; |
---|
1917 | | - |
---|
1918 | | - /* Let VLAN-aware bridge take care of its own VLANs */ |
---|
1919 | | - mlxsw_sp_port_vlan_put(mlxsw_sp_port_vlan); |
---|
| 1988 | + /* Port is no longer usable as a router interface */ |
---|
| 1989 | + if (mlxsw_sp_port->default_vlan->fid) |
---|
| 1990 | + mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port->default_vlan); |
---|
1920 | 1991 | |
---|
1921 | 1992 | return 0; |
---|
1922 | 1993 | } |
---|
.. | .. |
---|
1926 | 1997 | struct mlxsw_sp_bridge_port *bridge_port, |
---|
1927 | 1998 | struct mlxsw_sp_port *mlxsw_sp_port) |
---|
1928 | 1999 | { |
---|
1929 | | - mlxsw_sp_port_vlan_get(mlxsw_sp_port, 1); |
---|
1930 | 2000 | /* Make sure untagged frames are allowed to ingress */ |
---|
1931 | | - mlxsw_sp_port_pvid_set(mlxsw_sp_port, 1); |
---|
| 2001 | + mlxsw_sp_port_pvid_set(mlxsw_sp_port, MLXSW_SP_DEFAULT_VID); |
---|
| 2002 | +} |
---|
| 2003 | + |
---|
| 2004 | +static int |
---|
| 2005 | +mlxsw_sp_bridge_8021q_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 2006 | + const struct net_device *vxlan_dev, u16 vid, |
---|
| 2007 | + struct netlink_ext_ack *extack) |
---|
| 2008 | +{ |
---|
| 2009 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); |
---|
| 2010 | + struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); |
---|
| 2011 | + struct mlxsw_sp_nve_params params = { |
---|
| 2012 | + .type = MLXSW_SP_NVE_TYPE_VXLAN, |
---|
| 2013 | + .vni = vxlan->cfg.vni, |
---|
| 2014 | + .dev = vxlan_dev, |
---|
| 2015 | + }; |
---|
| 2016 | + struct mlxsw_sp_fid *fid; |
---|
| 2017 | + int err; |
---|
| 2018 | + |
---|
| 2019 | + /* If the VLAN is 0, we need to find the VLAN that is configured as |
---|
| 2020 | + * PVID and egress untagged on the bridge port of the VxLAN device. |
---|
| 2021 | + * It is possible no such VLAN exists |
---|
| 2022 | + */ |
---|
| 2023 | + if (!vid) { |
---|
| 2024 | + err = mlxsw_sp_vxlan_mapped_vid(vxlan_dev, &vid); |
---|
| 2025 | + if (err || !vid) |
---|
| 2026 | + return err; |
---|
| 2027 | + } |
---|
| 2028 | + |
---|
| 2029 | + fid = mlxsw_sp_fid_8021q_get(mlxsw_sp, vid); |
---|
| 2030 | + if (IS_ERR(fid)) { |
---|
| 2031 | + NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1Q FID"); |
---|
| 2032 | + return PTR_ERR(fid); |
---|
| 2033 | + } |
---|
| 2034 | + |
---|
| 2035 | + if (mlxsw_sp_fid_vni_is_set(fid)) { |
---|
| 2036 | + NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID"); |
---|
| 2037 | + err = -EINVAL; |
---|
| 2038 | + goto err_vni_exists; |
---|
| 2039 | + } |
---|
| 2040 | + |
---|
| 2041 | + err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, ¶ms, extack); |
---|
| 2042 | + if (err) |
---|
| 2043 | + goto err_nve_fid_enable; |
---|
| 2044 | + |
---|
| 2045 | + return 0; |
---|
| 2046 | + |
---|
| 2047 | +err_nve_fid_enable: |
---|
| 2048 | +err_vni_exists: |
---|
| 2049 | + mlxsw_sp_fid_put(fid); |
---|
| 2050 | + return err; |
---|
| 2051 | +} |
---|
| 2052 | + |
---|
| 2053 | +static struct net_device * |
---|
| 2054 | +mlxsw_sp_bridge_8021q_vxlan_dev_find(struct net_device *br_dev, u16 vid) |
---|
| 2055 | +{ |
---|
| 2056 | + struct net_device *dev; |
---|
| 2057 | + struct list_head *iter; |
---|
| 2058 | + |
---|
| 2059 | + netdev_for_each_lower_dev(br_dev, dev, iter) { |
---|
| 2060 | + u16 pvid; |
---|
| 2061 | + int err; |
---|
| 2062 | + |
---|
| 2063 | + if (!netif_is_vxlan(dev)) |
---|
| 2064 | + continue; |
---|
| 2065 | + |
---|
| 2066 | + err = mlxsw_sp_vxlan_mapped_vid(dev, &pvid); |
---|
| 2067 | + if (err || pvid != vid) |
---|
| 2068 | + continue; |
---|
| 2069 | + |
---|
| 2070 | + return dev; |
---|
| 2071 | + } |
---|
| 2072 | + |
---|
| 2073 | + return NULL; |
---|
1932 | 2074 | } |
---|
1933 | 2075 | |
---|
1934 | 2076 | static struct mlxsw_sp_fid * |
---|
1935 | 2077 | mlxsw_sp_bridge_8021q_fid_get(struct mlxsw_sp_bridge_device *bridge_device, |
---|
1936 | | - u16 vid) |
---|
| 2078 | + u16 vid, struct netlink_ext_ack *extack) |
---|
1937 | 2079 | { |
---|
1938 | 2080 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); |
---|
1939 | 2081 | |
---|
1940 | 2082 | return mlxsw_sp_fid_8021q_get(mlxsw_sp, vid); |
---|
1941 | 2083 | } |
---|
1942 | 2084 | |
---|
| 2085 | +static struct mlxsw_sp_fid * |
---|
| 2086 | +mlxsw_sp_bridge_8021q_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 2087 | + u16 vid) |
---|
| 2088 | +{ |
---|
| 2089 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); |
---|
| 2090 | + |
---|
| 2091 | + return mlxsw_sp_fid_8021q_lookup(mlxsw_sp, vid); |
---|
| 2092 | +} |
---|
| 2093 | + |
---|
| 2094 | +static u16 |
---|
| 2095 | +mlxsw_sp_bridge_8021q_fid_vid(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 2096 | + const struct mlxsw_sp_fid *fid) |
---|
| 2097 | +{ |
---|
| 2098 | + return mlxsw_sp_fid_8021q_vid(fid); |
---|
| 2099 | +} |
---|
| 2100 | + |
---|
1943 | 2101 | static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021q_ops = { |
---|
1944 | 2102 | .port_join = mlxsw_sp_bridge_8021q_port_join, |
---|
1945 | 2103 | .port_leave = mlxsw_sp_bridge_8021q_port_leave, |
---|
| 2104 | + .vxlan_join = mlxsw_sp_bridge_8021q_vxlan_join, |
---|
1946 | 2105 | .fid_get = mlxsw_sp_bridge_8021q_fid_get, |
---|
| 2106 | + .fid_lookup = mlxsw_sp_bridge_8021q_fid_lookup, |
---|
| 2107 | + .fid_vid = mlxsw_sp_bridge_8021q_fid_vid, |
---|
1947 | 2108 | }; |
---|
1948 | 2109 | |
---|
1949 | 2110 | static bool |
---|
.. | .. |
---|
1973 | 2134 | struct net_device *dev = bridge_port->dev; |
---|
1974 | 2135 | u16 vid; |
---|
1975 | 2136 | |
---|
1976 | | - vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 1; |
---|
| 2137 | + vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID; |
---|
1977 | 2138 | mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); |
---|
1978 | 2139 | if (WARN_ON(!mlxsw_sp_port_vlan)) |
---|
1979 | 2140 | return -EINVAL; |
---|
.. | .. |
---|
1987 | 2148 | if (mlxsw_sp_port_vlan->fid) |
---|
1988 | 2149 | mlxsw_sp_port_vlan_router_leave(mlxsw_sp_port_vlan); |
---|
1989 | 2150 | |
---|
1990 | | - return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port); |
---|
| 2151 | + return mlxsw_sp_port_vlan_bridge_join(mlxsw_sp_port_vlan, bridge_port, |
---|
| 2152 | + extack); |
---|
1991 | 2153 | } |
---|
1992 | 2154 | |
---|
1993 | 2155 | static void |
---|
.. | .. |
---|
1999 | 2161 | struct net_device *dev = bridge_port->dev; |
---|
2000 | 2162 | u16 vid; |
---|
2001 | 2163 | |
---|
2002 | | - vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : 1; |
---|
| 2164 | + vid = is_vlan_dev(dev) ? vlan_dev_vlan_id(dev) : MLXSW_SP_DEFAULT_VID; |
---|
2003 | 2165 | mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_vid(mlxsw_sp_port, vid); |
---|
2004 | | - if (!mlxsw_sp_port_vlan) |
---|
| 2166 | + if (!mlxsw_sp_port_vlan || !mlxsw_sp_port_vlan->bridge_port) |
---|
2005 | 2167 | return; |
---|
2006 | 2168 | |
---|
2007 | 2169 | mlxsw_sp_port_vlan_bridge_leave(mlxsw_sp_port_vlan); |
---|
2008 | 2170 | } |
---|
2009 | 2171 | |
---|
| 2172 | +static int |
---|
| 2173 | +mlxsw_sp_bridge_8021d_vxlan_join(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 2174 | + const struct net_device *vxlan_dev, u16 vid, |
---|
| 2175 | + struct netlink_ext_ack *extack) |
---|
| 2176 | +{ |
---|
| 2177 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); |
---|
| 2178 | + struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); |
---|
| 2179 | + struct mlxsw_sp_nve_params params = { |
---|
| 2180 | + .type = MLXSW_SP_NVE_TYPE_VXLAN, |
---|
| 2181 | + .vni = vxlan->cfg.vni, |
---|
| 2182 | + .dev = vxlan_dev, |
---|
| 2183 | + }; |
---|
| 2184 | + struct mlxsw_sp_fid *fid; |
---|
| 2185 | + int err; |
---|
| 2186 | + |
---|
| 2187 | + fid = mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); |
---|
| 2188 | + if (IS_ERR(fid)) { |
---|
| 2189 | + NL_SET_ERR_MSG_MOD(extack, "Failed to create 802.1D FID"); |
---|
| 2190 | + return -EINVAL; |
---|
| 2191 | + } |
---|
| 2192 | + |
---|
| 2193 | + if (mlxsw_sp_fid_vni_is_set(fid)) { |
---|
| 2194 | + NL_SET_ERR_MSG_MOD(extack, "VNI is already set on FID"); |
---|
| 2195 | + err = -EINVAL; |
---|
| 2196 | + goto err_vni_exists; |
---|
| 2197 | + } |
---|
| 2198 | + |
---|
| 2199 | + err = mlxsw_sp_nve_fid_enable(mlxsw_sp, fid, ¶ms, extack); |
---|
| 2200 | + if (err) |
---|
| 2201 | + goto err_nve_fid_enable; |
---|
| 2202 | + |
---|
| 2203 | + return 0; |
---|
| 2204 | + |
---|
| 2205 | +err_nve_fid_enable: |
---|
| 2206 | +err_vni_exists: |
---|
| 2207 | + mlxsw_sp_fid_put(fid); |
---|
| 2208 | + return err; |
---|
| 2209 | +} |
---|
| 2210 | + |
---|
2010 | 2211 | static struct mlxsw_sp_fid * |
---|
2011 | 2212 | mlxsw_sp_bridge_8021d_fid_get(struct mlxsw_sp_bridge_device *bridge_device, |
---|
2012 | | - u16 vid) |
---|
| 2213 | + u16 vid, struct netlink_ext_ack *extack) |
---|
2013 | 2214 | { |
---|
2014 | 2215 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); |
---|
2015 | 2216 | |
---|
2016 | 2217 | return mlxsw_sp_fid_8021d_get(mlxsw_sp, bridge_device->dev->ifindex); |
---|
2017 | 2218 | } |
---|
2018 | 2219 | |
---|
| 2220 | +static struct mlxsw_sp_fid * |
---|
| 2221 | +mlxsw_sp_bridge_8021d_fid_lookup(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 2222 | + u16 vid) |
---|
| 2223 | +{ |
---|
| 2224 | + struct mlxsw_sp *mlxsw_sp = mlxsw_sp_lower_get(bridge_device->dev); |
---|
| 2225 | + |
---|
| 2226 | + /* The only valid VLAN for a VLAN-unaware bridge is 0 */ |
---|
| 2227 | + if (vid) |
---|
| 2228 | + return NULL; |
---|
| 2229 | + |
---|
| 2230 | + return mlxsw_sp_fid_8021d_lookup(mlxsw_sp, bridge_device->dev->ifindex); |
---|
| 2231 | +} |
---|
| 2232 | + |
---|
| 2233 | +static u16 |
---|
| 2234 | +mlxsw_sp_bridge_8021d_fid_vid(struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 2235 | + const struct mlxsw_sp_fid *fid) |
---|
| 2236 | +{ |
---|
| 2237 | + return 0; |
---|
| 2238 | +} |
---|
| 2239 | + |
---|
2019 | 2240 | static const struct mlxsw_sp_bridge_ops mlxsw_sp_bridge_8021d_ops = { |
---|
2020 | 2241 | .port_join = mlxsw_sp_bridge_8021d_port_join, |
---|
2021 | 2242 | .port_leave = mlxsw_sp_bridge_8021d_port_leave, |
---|
| 2243 | + .vxlan_join = mlxsw_sp_bridge_8021d_vxlan_join, |
---|
2022 | 2244 | .fid_get = mlxsw_sp_bridge_8021d_fid_get, |
---|
| 2245 | + .fid_lookup = mlxsw_sp_bridge_8021d_fid_lookup, |
---|
| 2246 | + .fid_vid = mlxsw_sp_bridge_8021d_fid_vid, |
---|
2023 | 2247 | }; |
---|
2024 | 2248 | |
---|
2025 | 2249 | int mlxsw_sp_port_bridge_join(struct mlxsw_sp_port *mlxsw_sp_port, |
---|
.. | .. |
---|
2032 | 2256 | struct mlxsw_sp_bridge_port *bridge_port; |
---|
2033 | 2257 | int err; |
---|
2034 | 2258 | |
---|
2035 | | - bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev); |
---|
| 2259 | + bridge_port = mlxsw_sp_bridge_port_get(mlxsw_sp->bridge, brport_dev, |
---|
| 2260 | + extack); |
---|
2036 | 2261 | if (IS_ERR(bridge_port)) |
---|
2037 | 2262 | return PTR_ERR(bridge_port); |
---|
2038 | 2263 | bridge_device = bridge_port->bridge_device; |
---|
.. | .. |
---|
2069 | 2294 | mlxsw_sp_bridge_port_put(mlxsw_sp->bridge, bridge_port); |
---|
2070 | 2295 | } |
---|
2071 | 2296 | |
---|
| 2297 | +int mlxsw_sp_bridge_vxlan_join(struct mlxsw_sp *mlxsw_sp, |
---|
| 2298 | + const struct net_device *br_dev, |
---|
| 2299 | + const struct net_device *vxlan_dev, u16 vid, |
---|
| 2300 | + struct netlink_ext_ack *extack) |
---|
| 2301 | +{ |
---|
| 2302 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 2303 | + |
---|
| 2304 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 2305 | + if (WARN_ON(!bridge_device)) |
---|
| 2306 | + return -EINVAL; |
---|
| 2307 | + |
---|
| 2308 | + return bridge_device->ops->vxlan_join(bridge_device, vxlan_dev, vid, |
---|
| 2309 | + extack); |
---|
| 2310 | +} |
---|
| 2311 | + |
---|
| 2312 | +void mlxsw_sp_bridge_vxlan_leave(struct mlxsw_sp *mlxsw_sp, |
---|
| 2313 | + const struct net_device *vxlan_dev) |
---|
| 2314 | +{ |
---|
| 2315 | + struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); |
---|
| 2316 | + struct mlxsw_sp_fid *fid; |
---|
| 2317 | + |
---|
| 2318 | + /* If the VxLAN device is down, then the FID does not have a VNI */ |
---|
| 2319 | + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan->cfg.vni); |
---|
| 2320 | + if (!fid) |
---|
| 2321 | + return; |
---|
| 2322 | + |
---|
| 2323 | + mlxsw_sp_nve_fid_disable(mlxsw_sp, fid); |
---|
| 2324 | + /* Drop both the reference we just took during lookup and the reference |
---|
| 2325 | + * the VXLAN device took. |
---|
| 2326 | + */ |
---|
| 2327 | + mlxsw_sp_fid_put(fid); |
---|
| 2328 | + mlxsw_sp_fid_put(fid); |
---|
| 2329 | +} |
---|
| 2330 | + |
---|
| 2331 | +static void |
---|
| 2332 | +mlxsw_sp_switchdev_vxlan_addr_convert(const union vxlan_addr *vxlan_addr, |
---|
| 2333 | + enum mlxsw_sp_l3proto *proto, |
---|
| 2334 | + union mlxsw_sp_l3addr *addr) |
---|
| 2335 | +{ |
---|
| 2336 | + if (vxlan_addr->sa.sa_family == AF_INET) { |
---|
| 2337 | + addr->addr4 = vxlan_addr->sin.sin_addr.s_addr; |
---|
| 2338 | + *proto = MLXSW_SP_L3_PROTO_IPV4; |
---|
| 2339 | + } else { |
---|
| 2340 | + addr->addr6 = vxlan_addr->sin6.sin6_addr; |
---|
| 2341 | + *proto = MLXSW_SP_L3_PROTO_IPV6; |
---|
| 2342 | + } |
---|
| 2343 | +} |
---|
| 2344 | + |
---|
| 2345 | +static void |
---|
| 2346 | +mlxsw_sp_switchdev_addr_vxlan_convert(enum mlxsw_sp_l3proto proto, |
---|
| 2347 | + const union mlxsw_sp_l3addr *addr, |
---|
| 2348 | + union vxlan_addr *vxlan_addr) |
---|
| 2349 | +{ |
---|
| 2350 | + switch (proto) { |
---|
| 2351 | + case MLXSW_SP_L3_PROTO_IPV4: |
---|
| 2352 | + vxlan_addr->sa.sa_family = AF_INET; |
---|
| 2353 | + vxlan_addr->sin.sin_addr.s_addr = addr->addr4; |
---|
| 2354 | + break; |
---|
| 2355 | + case MLXSW_SP_L3_PROTO_IPV6: |
---|
| 2356 | + vxlan_addr->sa.sa_family = AF_INET6; |
---|
| 2357 | + vxlan_addr->sin6.sin6_addr = addr->addr6; |
---|
| 2358 | + break; |
---|
| 2359 | + } |
---|
| 2360 | +} |
---|
| 2361 | + |
---|
| 2362 | +static void mlxsw_sp_fdb_vxlan_call_notifiers(struct net_device *dev, |
---|
| 2363 | + const char *mac, |
---|
| 2364 | + enum mlxsw_sp_l3proto proto, |
---|
| 2365 | + union mlxsw_sp_l3addr *addr, |
---|
| 2366 | + __be32 vni, bool adding) |
---|
| 2367 | +{ |
---|
| 2368 | + struct switchdev_notifier_vxlan_fdb_info info; |
---|
| 2369 | + struct vxlan_dev *vxlan = netdev_priv(dev); |
---|
| 2370 | + enum switchdev_notifier_type type; |
---|
| 2371 | + |
---|
| 2372 | + type = adding ? SWITCHDEV_VXLAN_FDB_ADD_TO_BRIDGE : |
---|
| 2373 | + SWITCHDEV_VXLAN_FDB_DEL_TO_BRIDGE; |
---|
| 2374 | + mlxsw_sp_switchdev_addr_vxlan_convert(proto, addr, &info.remote_ip); |
---|
| 2375 | + info.remote_port = vxlan->cfg.dst_port; |
---|
| 2376 | + info.remote_vni = vni; |
---|
| 2377 | + info.remote_ifindex = 0; |
---|
| 2378 | + ether_addr_copy(info.eth_addr, mac); |
---|
| 2379 | + info.vni = vni; |
---|
| 2380 | + info.offloaded = adding; |
---|
| 2381 | + call_switchdev_notifiers(type, dev, &info.info, NULL); |
---|
| 2382 | +} |
---|
| 2383 | + |
---|
| 2384 | +static void mlxsw_sp_fdb_nve_call_notifiers(struct net_device *dev, |
---|
| 2385 | + const char *mac, |
---|
| 2386 | + enum mlxsw_sp_l3proto proto, |
---|
| 2387 | + union mlxsw_sp_l3addr *addr, |
---|
| 2388 | + __be32 vni, |
---|
| 2389 | + bool adding) |
---|
| 2390 | +{ |
---|
| 2391 | + if (netif_is_vxlan(dev)) |
---|
| 2392 | + mlxsw_sp_fdb_vxlan_call_notifiers(dev, mac, proto, addr, vni, |
---|
| 2393 | + adding); |
---|
| 2394 | +} |
---|
| 2395 | + |
---|
2072 | 2396 | static void |
---|
2073 | 2397 | mlxsw_sp_fdb_call_notifiers(enum switchdev_notifier_type type, |
---|
2074 | 2398 | const char *mac, u16 vid, |
---|
2075 | | - struct net_device *dev) |
---|
| 2399 | + struct net_device *dev, bool offloaded) |
---|
2076 | 2400 | { |
---|
2077 | 2401 | struct switchdev_notifier_fdb_info info; |
---|
2078 | 2402 | |
---|
2079 | 2403 | info.addr = mac; |
---|
2080 | 2404 | info.vid = vid; |
---|
2081 | | - call_switchdev_notifiers(type, dev, &info.info); |
---|
| 2405 | + info.offloaded = offloaded; |
---|
| 2406 | + call_switchdev_notifiers(type, dev, &info.info, NULL); |
---|
2082 | 2407 | } |
---|
2083 | 2408 | |
---|
2084 | 2409 | static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp, |
---|
2085 | 2410 | char *sfn_pl, int rec_index, |
---|
2086 | 2411 | bool adding) |
---|
2087 | 2412 | { |
---|
| 2413 | + unsigned int max_ports = mlxsw_core_max_ports(mlxsw_sp->core); |
---|
2088 | 2414 | struct mlxsw_sp_port_vlan *mlxsw_sp_port_vlan; |
---|
2089 | 2415 | struct mlxsw_sp_bridge_device *bridge_device; |
---|
2090 | 2416 | struct mlxsw_sp_bridge_port *bridge_port; |
---|
.. | .. |
---|
2097 | 2423 | int err; |
---|
2098 | 2424 | |
---|
2099 | 2425 | mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port); |
---|
| 2426 | + |
---|
| 2427 | + if (WARN_ON_ONCE(local_port >= max_ports)) |
---|
| 2428 | + return; |
---|
2100 | 2429 | mlxsw_sp_port = mlxsw_sp->ports[local_port]; |
---|
2101 | 2430 | if (!mlxsw_sp_port) { |
---|
2102 | 2431 | dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n"); |
---|
2103 | 2432 | goto just_remove; |
---|
2104 | 2433 | } |
---|
| 2434 | + |
---|
| 2435 | + if (mlxsw_sp_fid_is_dummy(mlxsw_sp, fid)) |
---|
| 2436 | + goto just_remove; |
---|
2105 | 2437 | |
---|
2106 | 2438 | mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid); |
---|
2107 | 2439 | if (!mlxsw_sp_port_vlan) { |
---|
.. | .. |
---|
2129 | 2461 | if (!do_notification) |
---|
2130 | 2462 | return; |
---|
2131 | 2463 | type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; |
---|
2132 | | - mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev); |
---|
| 2464 | + mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding); |
---|
2133 | 2465 | |
---|
2134 | 2466 | return; |
---|
2135 | 2467 | |
---|
.. | .. |
---|
2162 | 2494 | goto just_remove; |
---|
2163 | 2495 | } |
---|
2164 | 2496 | |
---|
| 2497 | + if (mlxsw_sp_fid_is_dummy(mlxsw_sp, fid)) |
---|
| 2498 | + goto just_remove; |
---|
| 2499 | + |
---|
2165 | 2500 | mlxsw_sp_port_vlan = mlxsw_sp_port_vlan_find_by_fid(mlxsw_sp_port, fid); |
---|
2166 | 2501 | if (!mlxsw_sp_port_vlan) { |
---|
2167 | 2502 | netdev_err(mlxsw_sp_port->dev, "Failed to find a matching {Port, VID} following FDB notification\n"); |
---|
.. | .. |
---|
2176 | 2511 | |
---|
2177 | 2512 | bridge_device = bridge_port->bridge_device; |
---|
2178 | 2513 | vid = bridge_device->vlan_enabled ? mlxsw_sp_port_vlan->vid : 0; |
---|
2179 | | - lag_vid = mlxsw_sp_port_vlan->vid; |
---|
| 2514 | + lag_vid = mlxsw_sp_fid_lag_vid_valid(mlxsw_sp_port_vlan->fid) ? |
---|
| 2515 | + mlxsw_sp_port_vlan->vid : 0; |
---|
2180 | 2516 | |
---|
2181 | 2517 | do_fdb_op: |
---|
2182 | 2518 | err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid, |
---|
.. | .. |
---|
2189 | 2525 | if (!do_notification) |
---|
2190 | 2526 | return; |
---|
2191 | 2527 | type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : SWITCHDEV_FDB_DEL_TO_BRIDGE; |
---|
2192 | | - mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev); |
---|
| 2528 | + mlxsw_sp_fdb_call_notifiers(type, mac, vid, bridge_port->dev, adding); |
---|
2193 | 2529 | |
---|
2194 | 2530 | return; |
---|
2195 | 2531 | |
---|
.. | .. |
---|
2197 | 2533 | adding = false; |
---|
2198 | 2534 | do_notification = false; |
---|
2199 | 2535 | goto do_fdb_op; |
---|
| 2536 | +} |
---|
| 2537 | + |
---|
| 2538 | +static int |
---|
| 2539 | +__mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, |
---|
| 2540 | + const struct mlxsw_sp_fid *fid, |
---|
| 2541 | + bool adding, |
---|
| 2542 | + struct net_device **nve_dev, |
---|
| 2543 | + u16 *p_vid, __be32 *p_vni) |
---|
| 2544 | +{ |
---|
| 2545 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 2546 | + struct net_device *br_dev, *dev; |
---|
| 2547 | + int nve_ifindex; |
---|
| 2548 | + int err; |
---|
| 2549 | + |
---|
| 2550 | + err = mlxsw_sp_fid_nve_ifindex(fid, &nve_ifindex); |
---|
| 2551 | + if (err) |
---|
| 2552 | + return err; |
---|
| 2553 | + |
---|
| 2554 | + err = mlxsw_sp_fid_vni(fid, p_vni); |
---|
| 2555 | + if (err) |
---|
| 2556 | + return err; |
---|
| 2557 | + |
---|
| 2558 | + dev = __dev_get_by_index(mlxsw_sp_net(mlxsw_sp), nve_ifindex); |
---|
| 2559 | + if (!dev) |
---|
| 2560 | + return -EINVAL; |
---|
| 2561 | + *nve_dev = dev; |
---|
| 2562 | + |
---|
| 2563 | + if (!netif_running(dev)) |
---|
| 2564 | + return -EINVAL; |
---|
| 2565 | + |
---|
| 2566 | + if (adding && !br_port_flag_is_set(dev, BR_LEARNING)) |
---|
| 2567 | + return -EINVAL; |
---|
| 2568 | + |
---|
| 2569 | + if (adding && netif_is_vxlan(dev)) { |
---|
| 2570 | + struct vxlan_dev *vxlan = netdev_priv(dev); |
---|
| 2571 | + |
---|
| 2572 | + if (!(vxlan->cfg.flags & VXLAN_F_LEARN)) |
---|
| 2573 | + return -EINVAL; |
---|
| 2574 | + } |
---|
| 2575 | + |
---|
| 2576 | + br_dev = netdev_master_upper_dev_get(dev); |
---|
| 2577 | + if (!br_dev) |
---|
| 2578 | + return -EINVAL; |
---|
| 2579 | + |
---|
| 2580 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 2581 | + if (!bridge_device) |
---|
| 2582 | + return -EINVAL; |
---|
| 2583 | + |
---|
| 2584 | + *p_vid = bridge_device->ops->fid_vid(bridge_device, fid); |
---|
| 2585 | + |
---|
| 2586 | + return 0; |
---|
| 2587 | +} |
---|
| 2588 | + |
---|
| 2589 | +static void mlxsw_sp_fdb_notify_mac_uc_tunnel_process(struct mlxsw_sp *mlxsw_sp, |
---|
| 2590 | + char *sfn_pl, |
---|
| 2591 | + int rec_index, |
---|
| 2592 | + bool adding) |
---|
| 2593 | +{ |
---|
| 2594 | + enum mlxsw_reg_sfn_uc_tunnel_protocol sfn_proto; |
---|
| 2595 | + enum switchdev_notifier_type type; |
---|
| 2596 | + struct net_device *nve_dev; |
---|
| 2597 | + union mlxsw_sp_l3addr addr; |
---|
| 2598 | + struct mlxsw_sp_fid *fid; |
---|
| 2599 | + char mac[ETH_ALEN]; |
---|
| 2600 | + u16 fid_index, vid; |
---|
| 2601 | + __be32 vni; |
---|
| 2602 | + u32 uip; |
---|
| 2603 | + int err; |
---|
| 2604 | + |
---|
| 2605 | + mlxsw_reg_sfn_uc_tunnel_unpack(sfn_pl, rec_index, mac, &fid_index, |
---|
| 2606 | + &uip, &sfn_proto); |
---|
| 2607 | + |
---|
| 2608 | + fid = mlxsw_sp_fid_lookup_by_index(mlxsw_sp, fid_index); |
---|
| 2609 | + if (!fid) |
---|
| 2610 | + goto err_fid_lookup; |
---|
| 2611 | + |
---|
| 2612 | + err = mlxsw_sp_nve_learned_ip_resolve(mlxsw_sp, uip, |
---|
| 2613 | + (enum mlxsw_sp_l3proto) sfn_proto, |
---|
| 2614 | + &addr); |
---|
| 2615 | + if (err) |
---|
| 2616 | + goto err_ip_resolve; |
---|
| 2617 | + |
---|
| 2618 | + err = __mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, fid, adding, |
---|
| 2619 | + &nve_dev, &vid, &vni); |
---|
| 2620 | + if (err) |
---|
| 2621 | + goto err_fdb_process; |
---|
| 2622 | + |
---|
| 2623 | + err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index, |
---|
| 2624 | + (enum mlxsw_sp_l3proto) sfn_proto, |
---|
| 2625 | + &addr, adding, true); |
---|
| 2626 | + if (err) |
---|
| 2627 | + goto err_fdb_op; |
---|
| 2628 | + |
---|
| 2629 | + mlxsw_sp_fdb_nve_call_notifiers(nve_dev, mac, |
---|
| 2630 | + (enum mlxsw_sp_l3proto) sfn_proto, |
---|
| 2631 | + &addr, vni, adding); |
---|
| 2632 | + |
---|
| 2633 | + type = adding ? SWITCHDEV_FDB_ADD_TO_BRIDGE : |
---|
| 2634 | + SWITCHDEV_FDB_DEL_TO_BRIDGE; |
---|
| 2635 | + mlxsw_sp_fdb_call_notifiers(type, mac, vid, nve_dev, adding); |
---|
| 2636 | + |
---|
| 2637 | + mlxsw_sp_fid_put(fid); |
---|
| 2638 | + |
---|
| 2639 | + return; |
---|
| 2640 | + |
---|
| 2641 | +err_fdb_op: |
---|
| 2642 | +err_fdb_process: |
---|
| 2643 | +err_ip_resolve: |
---|
| 2644 | + mlxsw_sp_fid_put(fid); |
---|
| 2645 | +err_fid_lookup: |
---|
| 2646 | + /* Remove an FDB entry in case we cannot process it. Otherwise the |
---|
| 2647 | + * device will keep sending the same notification over and over again. |
---|
| 2648 | + */ |
---|
| 2649 | + mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, mac, fid_index, |
---|
| 2650 | + (enum mlxsw_sp_l3proto) sfn_proto, &addr, |
---|
| 2651 | + false, true); |
---|
2200 | 2652 | } |
---|
2201 | 2653 | |
---|
2202 | 2654 | static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp, |
---|
.. | .. |
---|
2219 | 2671 | mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl, |
---|
2220 | 2672 | rec_index, false); |
---|
2221 | 2673 | break; |
---|
| 2674 | + case MLXSW_REG_SFN_REC_TYPE_LEARNED_UNICAST_TUNNEL: |
---|
| 2675 | + mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl, |
---|
| 2676 | + rec_index, true); |
---|
| 2677 | + break; |
---|
| 2678 | + case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_UNICAST_TUNNEL: |
---|
| 2679 | + mlxsw_sp_fdb_notify_mac_uc_tunnel_process(mlxsw_sp, sfn_pl, |
---|
| 2680 | + rec_index, false); |
---|
| 2681 | + break; |
---|
2222 | 2682 | } |
---|
2223 | 2683 | } |
---|
2224 | 2684 | |
---|
2225 | | -static void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp) |
---|
| 2685 | +static void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp, |
---|
| 2686 | + bool no_delay) |
---|
2226 | 2687 | { |
---|
2227 | 2688 | struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; |
---|
| 2689 | + unsigned int interval = no_delay ? 0 : bridge->fdb_notify.interval; |
---|
2228 | 2690 | |
---|
2229 | 2691 | mlxsw_core_schedule_dw(&bridge->fdb_notify.dw, |
---|
2230 | | - msecs_to_jiffies(bridge->fdb_notify.interval)); |
---|
| 2692 | + msecs_to_jiffies(interval)); |
---|
2231 | 2693 | } |
---|
| 2694 | + |
---|
| 2695 | +#define MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION 10 |
---|
2232 | 2696 | |
---|
2233 | 2697 | static void mlxsw_sp_fdb_notify_work(struct work_struct *work) |
---|
2234 | 2698 | { |
---|
2235 | 2699 | struct mlxsw_sp_bridge *bridge; |
---|
2236 | 2700 | struct mlxsw_sp *mlxsw_sp; |
---|
2237 | 2701 | char *sfn_pl; |
---|
| 2702 | + int queries; |
---|
2238 | 2703 | u8 num_rec; |
---|
2239 | 2704 | int i; |
---|
2240 | 2705 | int err; |
---|
.. | .. |
---|
2247 | 2712 | mlxsw_sp = bridge->mlxsw_sp; |
---|
2248 | 2713 | |
---|
2249 | 2714 | rtnl_lock(); |
---|
2250 | | - mlxsw_reg_sfn_pack(sfn_pl); |
---|
2251 | | - err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); |
---|
2252 | | - if (err) { |
---|
2253 | | - dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n"); |
---|
2254 | | - goto out; |
---|
| 2715 | + queries = MLXSW_SP_FDB_SFN_QUERIES_PER_SESSION; |
---|
| 2716 | + while (queries > 0) { |
---|
| 2717 | + mlxsw_reg_sfn_pack(sfn_pl); |
---|
| 2718 | + err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl); |
---|
| 2719 | + if (err) { |
---|
| 2720 | + dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n"); |
---|
| 2721 | + goto out; |
---|
| 2722 | + } |
---|
| 2723 | + num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl); |
---|
| 2724 | + for (i = 0; i < num_rec; i++) |
---|
| 2725 | + mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); |
---|
| 2726 | + if (num_rec != MLXSW_REG_SFN_REC_MAX_COUNT) |
---|
| 2727 | + goto out; |
---|
| 2728 | + queries--; |
---|
2255 | 2729 | } |
---|
2256 | | - num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl); |
---|
2257 | | - for (i = 0; i < num_rec; i++) |
---|
2258 | | - mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i); |
---|
2259 | 2730 | |
---|
2260 | 2731 | out: |
---|
2261 | 2732 | rtnl_unlock(); |
---|
2262 | 2733 | kfree(sfn_pl); |
---|
2263 | | - mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); |
---|
| 2734 | + mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, !queries); |
---|
2264 | 2735 | } |
---|
2265 | 2736 | |
---|
2266 | 2737 | struct mlxsw_sp_switchdev_event_work { |
---|
2267 | 2738 | struct work_struct work; |
---|
2268 | | - struct switchdev_notifier_fdb_info fdb_info; |
---|
| 2739 | + union { |
---|
| 2740 | + struct switchdev_notifier_fdb_info fdb_info; |
---|
| 2741 | + struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info; |
---|
| 2742 | + }; |
---|
2269 | 2743 | struct net_device *dev; |
---|
2270 | 2744 | unsigned long event; |
---|
2271 | 2745 | }; |
---|
2272 | 2746 | |
---|
2273 | | -static void mlxsw_sp_switchdev_event_work(struct work_struct *work) |
---|
| 2747 | +static void |
---|
| 2748 | +mlxsw_sp_switchdev_bridge_vxlan_fdb_event(struct mlxsw_sp *mlxsw_sp, |
---|
| 2749 | + struct mlxsw_sp_switchdev_event_work * |
---|
| 2750 | + switchdev_work, |
---|
| 2751 | + struct mlxsw_sp_fid *fid, __be32 vni) |
---|
| 2752 | +{ |
---|
| 2753 | + struct switchdev_notifier_vxlan_fdb_info vxlan_fdb_info; |
---|
| 2754 | + struct switchdev_notifier_fdb_info *fdb_info; |
---|
| 2755 | + struct net_device *dev = switchdev_work->dev; |
---|
| 2756 | + enum mlxsw_sp_l3proto proto; |
---|
| 2757 | + union mlxsw_sp_l3addr addr; |
---|
| 2758 | + int err; |
---|
| 2759 | + |
---|
| 2760 | + fdb_info = &switchdev_work->fdb_info; |
---|
| 2761 | + err = vxlan_fdb_find_uc(dev, fdb_info->addr, vni, &vxlan_fdb_info); |
---|
| 2762 | + if (err) |
---|
| 2763 | + return; |
---|
| 2764 | + |
---|
| 2765 | + mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info.remote_ip, |
---|
| 2766 | + &proto, &addr); |
---|
| 2767 | + |
---|
| 2768 | + switch (switchdev_work->event) { |
---|
| 2769 | + case SWITCHDEV_FDB_ADD_TO_DEVICE: |
---|
| 2770 | + err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, |
---|
| 2771 | + vxlan_fdb_info.eth_addr, |
---|
| 2772 | + mlxsw_sp_fid_index(fid), |
---|
| 2773 | + proto, &addr, true, false); |
---|
| 2774 | + if (err) |
---|
| 2775 | + return; |
---|
| 2776 | + vxlan_fdb_info.offloaded = true; |
---|
| 2777 | + call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, |
---|
| 2778 | + &vxlan_fdb_info.info, NULL); |
---|
| 2779 | + mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, |
---|
| 2780 | + vxlan_fdb_info.eth_addr, |
---|
| 2781 | + fdb_info->vid, dev, true); |
---|
| 2782 | + break; |
---|
| 2783 | + case SWITCHDEV_FDB_DEL_TO_DEVICE: |
---|
| 2784 | + err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, |
---|
| 2785 | + vxlan_fdb_info.eth_addr, |
---|
| 2786 | + mlxsw_sp_fid_index(fid), |
---|
| 2787 | + proto, &addr, false, |
---|
| 2788 | + false); |
---|
| 2789 | + vxlan_fdb_info.offloaded = false; |
---|
| 2790 | + call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, |
---|
| 2791 | + &vxlan_fdb_info.info, NULL); |
---|
| 2792 | + break; |
---|
| 2793 | + } |
---|
| 2794 | +} |
---|
| 2795 | + |
---|
| 2796 | +static void |
---|
| 2797 | +mlxsw_sp_switchdev_bridge_nve_fdb_event(struct mlxsw_sp_switchdev_event_work * |
---|
| 2798 | + switchdev_work) |
---|
| 2799 | +{ |
---|
| 2800 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 2801 | + struct net_device *dev = switchdev_work->dev; |
---|
| 2802 | + struct net_device *br_dev; |
---|
| 2803 | + struct mlxsw_sp *mlxsw_sp; |
---|
| 2804 | + struct mlxsw_sp_fid *fid; |
---|
| 2805 | + __be32 vni; |
---|
| 2806 | + int err; |
---|
| 2807 | + |
---|
| 2808 | + if (switchdev_work->event != SWITCHDEV_FDB_ADD_TO_DEVICE && |
---|
| 2809 | + switchdev_work->event != SWITCHDEV_FDB_DEL_TO_DEVICE) |
---|
| 2810 | + return; |
---|
| 2811 | + |
---|
| 2812 | + if (switchdev_work->event == SWITCHDEV_FDB_ADD_TO_DEVICE && |
---|
| 2813 | + !switchdev_work->fdb_info.added_by_user) |
---|
| 2814 | + return; |
---|
| 2815 | + |
---|
| 2816 | + if (!netif_running(dev)) |
---|
| 2817 | + return; |
---|
| 2818 | + br_dev = netdev_master_upper_dev_get(dev); |
---|
| 2819 | + if (!br_dev) |
---|
| 2820 | + return; |
---|
| 2821 | + if (!netif_is_bridge_master(br_dev)) |
---|
| 2822 | + return; |
---|
| 2823 | + mlxsw_sp = mlxsw_sp_lower_get(br_dev); |
---|
| 2824 | + if (!mlxsw_sp) |
---|
| 2825 | + return; |
---|
| 2826 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 2827 | + if (!bridge_device) |
---|
| 2828 | + return; |
---|
| 2829 | + |
---|
| 2830 | + fid = bridge_device->ops->fid_lookup(bridge_device, |
---|
| 2831 | + switchdev_work->fdb_info.vid); |
---|
| 2832 | + if (!fid) |
---|
| 2833 | + return; |
---|
| 2834 | + |
---|
| 2835 | + err = mlxsw_sp_fid_vni(fid, &vni); |
---|
| 2836 | + if (err) |
---|
| 2837 | + goto out; |
---|
| 2838 | + |
---|
| 2839 | + mlxsw_sp_switchdev_bridge_vxlan_fdb_event(mlxsw_sp, switchdev_work, fid, |
---|
| 2840 | + vni); |
---|
| 2841 | + |
---|
| 2842 | +out: |
---|
| 2843 | + mlxsw_sp_fid_put(fid); |
---|
| 2844 | +} |
---|
| 2845 | + |
---|
| 2846 | +static void mlxsw_sp_switchdev_bridge_fdb_event_work(struct work_struct *work) |
---|
2274 | 2847 | { |
---|
2275 | 2848 | struct mlxsw_sp_switchdev_event_work *switchdev_work = |
---|
2276 | 2849 | container_of(work, struct mlxsw_sp_switchdev_event_work, work); |
---|
.. | .. |
---|
2280 | 2853 | int err; |
---|
2281 | 2854 | |
---|
2282 | 2855 | rtnl_lock(); |
---|
| 2856 | + if (netif_is_vxlan(dev)) { |
---|
| 2857 | + mlxsw_sp_switchdev_bridge_nve_fdb_event(switchdev_work); |
---|
| 2858 | + goto out; |
---|
| 2859 | + } |
---|
| 2860 | + |
---|
2283 | 2861 | mlxsw_sp_port = mlxsw_sp_port_dev_lower_find(dev); |
---|
2284 | 2862 | if (!mlxsw_sp_port) |
---|
2285 | 2863 | goto out; |
---|
.. | .. |
---|
2294 | 2872 | break; |
---|
2295 | 2873 | mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, |
---|
2296 | 2874 | fdb_info->addr, |
---|
2297 | | - fdb_info->vid, dev); |
---|
| 2875 | + fdb_info->vid, dev, true); |
---|
2298 | 2876 | break; |
---|
2299 | 2877 | case SWITCHDEV_FDB_DEL_TO_DEVICE: |
---|
2300 | 2878 | fdb_info = &switchdev_work->fdb_info; |
---|
2301 | 2879 | mlxsw_sp_port_fdb_set(mlxsw_sp_port, fdb_info, false); |
---|
2302 | 2880 | break; |
---|
2303 | | - case SWITCHDEV_FDB_ADD_TO_BRIDGE: /* fall through */ |
---|
| 2881 | + case SWITCHDEV_FDB_ADD_TO_BRIDGE: |
---|
2304 | 2882 | case SWITCHDEV_FDB_DEL_TO_BRIDGE: |
---|
2305 | 2883 | /* These events are only used to potentially update an existing |
---|
2306 | 2884 | * SPAN mirror. |
---|
.. | .. |
---|
2317 | 2895 | dev_put(dev); |
---|
2318 | 2896 | } |
---|
2319 | 2897 | |
---|
| 2898 | +static void |
---|
| 2899 | +mlxsw_sp_switchdev_vxlan_fdb_add(struct mlxsw_sp *mlxsw_sp, |
---|
| 2900 | + struct mlxsw_sp_switchdev_event_work * |
---|
| 2901 | + switchdev_work) |
---|
| 2902 | +{ |
---|
| 2903 | + struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; |
---|
| 2904 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 2905 | + struct net_device *dev = switchdev_work->dev; |
---|
| 2906 | + u8 all_zeros_mac[ETH_ALEN] = { 0 }; |
---|
| 2907 | + enum mlxsw_sp_l3proto proto; |
---|
| 2908 | + union mlxsw_sp_l3addr addr; |
---|
| 2909 | + struct net_device *br_dev; |
---|
| 2910 | + struct mlxsw_sp_fid *fid; |
---|
| 2911 | + u16 vid; |
---|
| 2912 | + int err; |
---|
| 2913 | + |
---|
| 2914 | + vxlan_fdb_info = &switchdev_work->vxlan_fdb_info; |
---|
| 2915 | + br_dev = netdev_master_upper_dev_get(dev); |
---|
| 2916 | + |
---|
| 2917 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 2918 | + if (!bridge_device) |
---|
| 2919 | + return; |
---|
| 2920 | + |
---|
| 2921 | + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni); |
---|
| 2922 | + if (!fid) |
---|
| 2923 | + return; |
---|
| 2924 | + |
---|
| 2925 | + mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip, |
---|
| 2926 | + &proto, &addr); |
---|
| 2927 | + |
---|
| 2928 | + if (ether_addr_equal(vxlan_fdb_info->eth_addr, all_zeros_mac)) { |
---|
| 2929 | + err = mlxsw_sp_nve_flood_ip_add(mlxsw_sp, fid, proto, &addr); |
---|
| 2930 | + if (err) { |
---|
| 2931 | + mlxsw_sp_fid_put(fid); |
---|
| 2932 | + return; |
---|
| 2933 | + } |
---|
| 2934 | + vxlan_fdb_info->offloaded = true; |
---|
| 2935 | + call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, |
---|
| 2936 | + &vxlan_fdb_info->info, NULL); |
---|
| 2937 | + mlxsw_sp_fid_put(fid); |
---|
| 2938 | + return; |
---|
| 2939 | + } |
---|
| 2940 | + |
---|
| 2941 | + /* The device has a single FDB table, whereas Linux has two - one |
---|
| 2942 | + * in the bridge driver and another in the VxLAN driver. We only |
---|
| 2943 | + * program an entry to the device if the MAC points to the VxLAN |
---|
| 2944 | + * device in the bridge's FDB table |
---|
| 2945 | + */ |
---|
| 2946 | + vid = bridge_device->ops->fid_vid(bridge_device, fid); |
---|
| 2947 | + if (br_fdb_find_port(br_dev, vxlan_fdb_info->eth_addr, vid) != dev) |
---|
| 2948 | + goto err_br_fdb_find; |
---|
| 2949 | + |
---|
| 2950 | + err = mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr, |
---|
| 2951 | + mlxsw_sp_fid_index(fid), proto, |
---|
| 2952 | + &addr, true, false); |
---|
| 2953 | + if (err) |
---|
| 2954 | + goto err_fdb_tunnel_uc_op; |
---|
| 2955 | + vxlan_fdb_info->offloaded = true; |
---|
| 2956 | + call_switchdev_notifiers(SWITCHDEV_VXLAN_FDB_OFFLOADED, dev, |
---|
| 2957 | + &vxlan_fdb_info->info, NULL); |
---|
| 2958 | + mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, |
---|
| 2959 | + vxlan_fdb_info->eth_addr, vid, dev, true); |
---|
| 2960 | + |
---|
| 2961 | + mlxsw_sp_fid_put(fid); |
---|
| 2962 | + |
---|
| 2963 | + return; |
---|
| 2964 | + |
---|
| 2965 | +err_fdb_tunnel_uc_op: |
---|
| 2966 | +err_br_fdb_find: |
---|
| 2967 | + mlxsw_sp_fid_put(fid); |
---|
| 2968 | +} |
---|
| 2969 | + |
---|
| 2970 | +static void |
---|
| 2971 | +mlxsw_sp_switchdev_vxlan_fdb_del(struct mlxsw_sp *mlxsw_sp, |
---|
| 2972 | + struct mlxsw_sp_switchdev_event_work * |
---|
| 2973 | + switchdev_work) |
---|
| 2974 | +{ |
---|
| 2975 | + struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; |
---|
| 2976 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 2977 | + struct net_device *dev = switchdev_work->dev; |
---|
| 2978 | + struct net_device *br_dev = netdev_master_upper_dev_get(dev); |
---|
| 2979 | + u8 all_zeros_mac[ETH_ALEN] = { 0 }; |
---|
| 2980 | + enum mlxsw_sp_l3proto proto; |
---|
| 2981 | + union mlxsw_sp_l3addr addr; |
---|
| 2982 | + struct mlxsw_sp_fid *fid; |
---|
| 2983 | + u16 vid; |
---|
| 2984 | + |
---|
| 2985 | + vxlan_fdb_info = &switchdev_work->vxlan_fdb_info; |
---|
| 2986 | + |
---|
| 2987 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 2988 | + if (!bridge_device) |
---|
| 2989 | + return; |
---|
| 2990 | + |
---|
| 2991 | + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vxlan_fdb_info->vni); |
---|
| 2992 | + if (!fid) |
---|
| 2993 | + return; |
---|
| 2994 | + |
---|
| 2995 | + mlxsw_sp_switchdev_vxlan_addr_convert(&vxlan_fdb_info->remote_ip, |
---|
| 2996 | + &proto, &addr); |
---|
| 2997 | + |
---|
| 2998 | + if (ether_addr_equal(vxlan_fdb_info->eth_addr, all_zeros_mac)) { |
---|
| 2999 | + mlxsw_sp_nve_flood_ip_del(mlxsw_sp, fid, proto, &addr); |
---|
| 3000 | + mlxsw_sp_fid_put(fid); |
---|
| 3001 | + return; |
---|
| 3002 | + } |
---|
| 3003 | + |
---|
| 3004 | + mlxsw_sp_port_fdb_tunnel_uc_op(mlxsw_sp, vxlan_fdb_info->eth_addr, |
---|
| 3005 | + mlxsw_sp_fid_index(fid), proto, &addr, |
---|
| 3006 | + false, false); |
---|
| 3007 | + vid = bridge_device->ops->fid_vid(bridge_device, fid); |
---|
| 3008 | + mlxsw_sp_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, |
---|
| 3009 | + vxlan_fdb_info->eth_addr, vid, dev, false); |
---|
| 3010 | + |
---|
| 3011 | + mlxsw_sp_fid_put(fid); |
---|
| 3012 | +} |
---|
| 3013 | + |
---|
| 3014 | +static void mlxsw_sp_switchdev_vxlan_fdb_event_work(struct work_struct *work) |
---|
| 3015 | +{ |
---|
| 3016 | + struct mlxsw_sp_switchdev_event_work *switchdev_work = |
---|
| 3017 | + container_of(work, struct mlxsw_sp_switchdev_event_work, work); |
---|
| 3018 | + struct net_device *dev = switchdev_work->dev; |
---|
| 3019 | + struct mlxsw_sp *mlxsw_sp; |
---|
| 3020 | + struct net_device *br_dev; |
---|
| 3021 | + |
---|
| 3022 | + rtnl_lock(); |
---|
| 3023 | + |
---|
| 3024 | + if (!netif_running(dev)) |
---|
| 3025 | + goto out; |
---|
| 3026 | + br_dev = netdev_master_upper_dev_get(dev); |
---|
| 3027 | + if (!br_dev) |
---|
| 3028 | + goto out; |
---|
| 3029 | + if (!netif_is_bridge_master(br_dev)) |
---|
| 3030 | + goto out; |
---|
| 3031 | + mlxsw_sp = mlxsw_sp_lower_get(br_dev); |
---|
| 3032 | + if (!mlxsw_sp) |
---|
| 3033 | + goto out; |
---|
| 3034 | + |
---|
| 3035 | + switch (switchdev_work->event) { |
---|
| 3036 | + case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE: |
---|
| 3037 | + mlxsw_sp_switchdev_vxlan_fdb_add(mlxsw_sp, switchdev_work); |
---|
| 3038 | + break; |
---|
| 3039 | + case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE: |
---|
| 3040 | + mlxsw_sp_switchdev_vxlan_fdb_del(mlxsw_sp, switchdev_work); |
---|
| 3041 | + break; |
---|
| 3042 | + } |
---|
| 3043 | + |
---|
| 3044 | +out: |
---|
| 3045 | + rtnl_unlock(); |
---|
| 3046 | + kfree(switchdev_work); |
---|
| 3047 | + dev_put(dev); |
---|
| 3048 | +} |
---|
| 3049 | + |
---|
| 3050 | +static int |
---|
| 3051 | +mlxsw_sp_switchdev_vxlan_work_prepare(struct mlxsw_sp_switchdev_event_work * |
---|
| 3052 | + switchdev_work, |
---|
| 3053 | + struct switchdev_notifier_info *info) |
---|
| 3054 | +{ |
---|
| 3055 | + struct vxlan_dev *vxlan = netdev_priv(switchdev_work->dev); |
---|
| 3056 | + struct switchdev_notifier_vxlan_fdb_info *vxlan_fdb_info; |
---|
| 3057 | + struct vxlan_config *cfg = &vxlan->cfg; |
---|
| 3058 | + struct netlink_ext_ack *extack; |
---|
| 3059 | + |
---|
| 3060 | + extack = switchdev_notifier_info_to_extack(info); |
---|
| 3061 | + vxlan_fdb_info = container_of(info, |
---|
| 3062 | + struct switchdev_notifier_vxlan_fdb_info, |
---|
| 3063 | + info); |
---|
| 3064 | + |
---|
| 3065 | + if (vxlan_fdb_info->remote_port != cfg->dst_port) { |
---|
| 3066 | + NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default remote port is not supported"); |
---|
| 3067 | + return -EOPNOTSUPP; |
---|
| 3068 | + } |
---|
| 3069 | + if (vxlan_fdb_info->remote_vni != cfg->vni || |
---|
| 3070 | + vxlan_fdb_info->vni != cfg->vni) { |
---|
| 3071 | + NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Non-default VNI is not supported"); |
---|
| 3072 | + return -EOPNOTSUPP; |
---|
| 3073 | + } |
---|
| 3074 | + if (vxlan_fdb_info->remote_ifindex) { |
---|
| 3075 | + NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Local interface is not supported"); |
---|
| 3076 | + return -EOPNOTSUPP; |
---|
| 3077 | + } |
---|
| 3078 | + if (is_multicast_ether_addr(vxlan_fdb_info->eth_addr)) { |
---|
| 3079 | + NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast MAC addresses not supported"); |
---|
| 3080 | + return -EOPNOTSUPP; |
---|
| 3081 | + } |
---|
| 3082 | + if (vxlan_addr_multicast(&vxlan_fdb_info->remote_ip)) { |
---|
| 3083 | + NL_SET_ERR_MSG_MOD(extack, "VxLAN: FDB: Multicast destination IP is not supported"); |
---|
| 3084 | + return -EOPNOTSUPP; |
---|
| 3085 | + } |
---|
| 3086 | + |
---|
| 3087 | + switchdev_work->vxlan_fdb_info = *vxlan_fdb_info; |
---|
| 3088 | + |
---|
| 3089 | + return 0; |
---|
| 3090 | +} |
---|
| 3091 | + |
---|
2320 | 3092 | /* Called under rcu_read_lock() */ |
---|
2321 | 3093 | static int mlxsw_sp_switchdev_event(struct notifier_block *unused, |
---|
2322 | 3094 | unsigned long event, void *ptr) |
---|
2323 | 3095 | { |
---|
2324 | 3096 | struct net_device *dev = switchdev_notifier_info_to_dev(ptr); |
---|
2325 | 3097 | struct mlxsw_sp_switchdev_event_work *switchdev_work; |
---|
2326 | | - struct switchdev_notifier_fdb_info *fdb_info = ptr; |
---|
| 3098 | + struct switchdev_notifier_fdb_info *fdb_info; |
---|
| 3099 | + struct switchdev_notifier_info *info = ptr; |
---|
2327 | 3100 | struct net_device *br_dev; |
---|
| 3101 | + int err; |
---|
| 3102 | + |
---|
| 3103 | + if (event == SWITCHDEV_PORT_ATTR_SET) { |
---|
| 3104 | + err = switchdev_handle_port_attr_set(dev, ptr, |
---|
| 3105 | + mlxsw_sp_port_dev_check, |
---|
| 3106 | + mlxsw_sp_port_attr_set); |
---|
| 3107 | + return notifier_from_errno(err); |
---|
| 3108 | + } |
---|
2328 | 3109 | |
---|
2329 | 3110 | /* Tunnel devices are not our uppers, so check their master instead */ |
---|
2330 | 3111 | br_dev = netdev_master_upper_dev_get_rcu(dev); |
---|
.. | .. |
---|
2339 | 3120 | if (!switchdev_work) |
---|
2340 | 3121 | return NOTIFY_BAD; |
---|
2341 | 3122 | |
---|
2342 | | - INIT_WORK(&switchdev_work->work, mlxsw_sp_switchdev_event_work); |
---|
2343 | 3123 | switchdev_work->dev = dev; |
---|
2344 | 3124 | switchdev_work->event = event; |
---|
2345 | 3125 | |
---|
2346 | 3126 | switch (event) { |
---|
2347 | | - case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ |
---|
2348 | | - case SWITCHDEV_FDB_DEL_TO_DEVICE: /* fall through */ |
---|
2349 | | - case SWITCHDEV_FDB_ADD_TO_BRIDGE: /* fall through */ |
---|
| 3127 | + case SWITCHDEV_FDB_ADD_TO_DEVICE: |
---|
| 3128 | + case SWITCHDEV_FDB_DEL_TO_DEVICE: |
---|
| 3129 | + case SWITCHDEV_FDB_ADD_TO_BRIDGE: |
---|
2350 | 3130 | case SWITCHDEV_FDB_DEL_TO_BRIDGE: |
---|
| 3131 | + fdb_info = container_of(info, |
---|
| 3132 | + struct switchdev_notifier_fdb_info, |
---|
| 3133 | + info); |
---|
| 3134 | + INIT_WORK(&switchdev_work->work, |
---|
| 3135 | + mlxsw_sp_switchdev_bridge_fdb_event_work); |
---|
2351 | 3136 | memcpy(&switchdev_work->fdb_info, ptr, |
---|
2352 | 3137 | sizeof(switchdev_work->fdb_info)); |
---|
2353 | 3138 | switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); |
---|
.. | .. |
---|
2361 | 3146 | */ |
---|
2362 | 3147 | dev_hold(dev); |
---|
2363 | 3148 | break; |
---|
| 3149 | + case SWITCHDEV_VXLAN_FDB_ADD_TO_DEVICE: |
---|
| 3150 | + case SWITCHDEV_VXLAN_FDB_DEL_TO_DEVICE: |
---|
| 3151 | + INIT_WORK(&switchdev_work->work, |
---|
| 3152 | + mlxsw_sp_switchdev_vxlan_fdb_event_work); |
---|
| 3153 | + err = mlxsw_sp_switchdev_vxlan_work_prepare(switchdev_work, |
---|
| 3154 | + info); |
---|
| 3155 | + if (err) |
---|
| 3156 | + goto err_vxlan_work_prepare; |
---|
| 3157 | + dev_hold(dev); |
---|
| 3158 | + break; |
---|
2364 | 3159 | default: |
---|
2365 | 3160 | kfree(switchdev_work); |
---|
2366 | 3161 | return NOTIFY_DONE; |
---|
.. | .. |
---|
2370 | 3165 | |
---|
2371 | 3166 | return NOTIFY_DONE; |
---|
2372 | 3167 | |
---|
| 3168 | +err_vxlan_work_prepare: |
---|
2373 | 3169 | err_addr_alloc: |
---|
2374 | 3170 | kfree(switchdev_work); |
---|
2375 | 3171 | return NOTIFY_BAD; |
---|
2376 | 3172 | } |
---|
2377 | 3173 | |
---|
2378 | | -static struct notifier_block mlxsw_sp_switchdev_notifier = { |
---|
| 3174 | +struct notifier_block mlxsw_sp_switchdev_notifier = { |
---|
2379 | 3175 | .notifier_call = mlxsw_sp_switchdev_event, |
---|
| 3176 | +}; |
---|
| 3177 | + |
---|
| 3178 | +static int |
---|
| 3179 | +mlxsw_sp_switchdev_vxlan_vlan_add(struct mlxsw_sp *mlxsw_sp, |
---|
| 3180 | + struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 3181 | + const struct net_device *vxlan_dev, u16 vid, |
---|
| 3182 | + bool flag_untagged, bool flag_pvid, |
---|
| 3183 | + struct netlink_ext_ack *extack) |
---|
| 3184 | +{ |
---|
| 3185 | + struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); |
---|
| 3186 | + __be32 vni = vxlan->cfg.vni; |
---|
| 3187 | + struct mlxsw_sp_fid *fid; |
---|
| 3188 | + u16 old_vid; |
---|
| 3189 | + int err; |
---|
| 3190 | + |
---|
| 3191 | + /* We cannot have the same VLAN as PVID and egress untagged on multiple |
---|
| 3192 | + * VxLAN devices. Note that we get this notification before the VLAN is |
---|
| 3193 | + * actually added to the bridge's database, so it is not possible for |
---|
| 3194 | + * the lookup function to return 'vxlan_dev' |
---|
| 3195 | + */ |
---|
| 3196 | + if (flag_untagged && flag_pvid && |
---|
| 3197 | + mlxsw_sp_bridge_8021q_vxlan_dev_find(bridge_device->dev, vid)) { |
---|
| 3198 | + NL_SET_ERR_MSG_MOD(extack, "VLAN already mapped to a different VNI"); |
---|
| 3199 | + return -EINVAL; |
---|
| 3200 | + } |
---|
| 3201 | + |
---|
| 3202 | + if (!netif_running(vxlan_dev)) |
---|
| 3203 | + return 0; |
---|
| 3204 | + |
---|
| 3205 | + /* First case: FID is not associated with this VNI, but the new VLAN |
---|
| 3206 | + * is both PVID and egress untagged. Need to enable NVE on the FID, if |
---|
| 3207 | + * it exists |
---|
| 3208 | + */ |
---|
| 3209 | + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); |
---|
| 3210 | + if (!fid) { |
---|
| 3211 | + if (!flag_untagged || !flag_pvid) |
---|
| 3212 | + return 0; |
---|
| 3213 | + return mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, |
---|
| 3214 | + vxlan_dev, vid, extack); |
---|
| 3215 | + } |
---|
| 3216 | + |
---|
| 3217 | + /* Second case: FID is associated with the VNI and the VLAN associated |
---|
| 3218 | + * with the FID is the same as the notified VLAN. This means the flags |
---|
| 3219 | + * (PVID / egress untagged) were toggled and that NVE should be |
---|
| 3220 | + * disabled on the FID |
---|
| 3221 | + */ |
---|
| 3222 | + old_vid = mlxsw_sp_fid_8021q_vid(fid); |
---|
| 3223 | + if (vid == old_vid) { |
---|
| 3224 | + if (WARN_ON(flag_untagged && flag_pvid)) { |
---|
| 3225 | + mlxsw_sp_fid_put(fid); |
---|
| 3226 | + return -EINVAL; |
---|
| 3227 | + } |
---|
| 3228 | + mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); |
---|
| 3229 | + mlxsw_sp_fid_put(fid); |
---|
| 3230 | + return 0; |
---|
| 3231 | + } |
---|
| 3232 | + |
---|
| 3233 | + /* Third case: A new VLAN was configured on the VxLAN device, but this |
---|
| 3234 | + * VLAN is not PVID, so there is nothing to do. |
---|
| 3235 | + */ |
---|
| 3236 | + if (!flag_pvid) { |
---|
| 3237 | + mlxsw_sp_fid_put(fid); |
---|
| 3238 | + return 0; |
---|
| 3239 | + } |
---|
| 3240 | + |
---|
| 3241 | + /* Fourth case: Thew new VLAN is PVID, which means the VLAN currently |
---|
| 3242 | + * mapped to the VNI should be unmapped |
---|
| 3243 | + */ |
---|
| 3244 | + mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); |
---|
| 3245 | + mlxsw_sp_fid_put(fid); |
---|
| 3246 | + |
---|
| 3247 | + /* Fifth case: The new VLAN is also egress untagged, which means the |
---|
| 3248 | + * VLAN needs to be mapped to the VNI |
---|
| 3249 | + */ |
---|
| 3250 | + if (!flag_untagged) |
---|
| 3251 | + return 0; |
---|
| 3252 | + |
---|
| 3253 | + err = mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, vid, |
---|
| 3254 | + extack); |
---|
| 3255 | + if (err) |
---|
| 3256 | + goto err_vxlan_join; |
---|
| 3257 | + |
---|
| 3258 | + return 0; |
---|
| 3259 | + |
---|
| 3260 | +err_vxlan_join: |
---|
| 3261 | + mlxsw_sp_bridge_8021q_vxlan_join(bridge_device, vxlan_dev, old_vid, |
---|
| 3262 | + NULL); |
---|
| 3263 | + return err; |
---|
| 3264 | +} |
---|
| 3265 | + |
---|
| 3266 | +static void |
---|
| 3267 | +mlxsw_sp_switchdev_vxlan_vlan_del(struct mlxsw_sp *mlxsw_sp, |
---|
| 3268 | + struct mlxsw_sp_bridge_device *bridge_device, |
---|
| 3269 | + const struct net_device *vxlan_dev, u16 vid) |
---|
| 3270 | +{ |
---|
| 3271 | + struct vxlan_dev *vxlan = netdev_priv(vxlan_dev); |
---|
| 3272 | + __be32 vni = vxlan->cfg.vni; |
---|
| 3273 | + struct mlxsw_sp_fid *fid; |
---|
| 3274 | + |
---|
| 3275 | + if (!netif_running(vxlan_dev)) |
---|
| 3276 | + return; |
---|
| 3277 | + |
---|
| 3278 | + fid = mlxsw_sp_fid_lookup_by_vni(mlxsw_sp, vni); |
---|
| 3279 | + if (!fid) |
---|
| 3280 | + return; |
---|
| 3281 | + |
---|
| 3282 | + /* A different VLAN than the one mapped to the VNI is deleted */ |
---|
| 3283 | + if (mlxsw_sp_fid_8021q_vid(fid) != vid) |
---|
| 3284 | + goto out; |
---|
| 3285 | + |
---|
| 3286 | + mlxsw_sp_bridge_vxlan_leave(mlxsw_sp, vxlan_dev); |
---|
| 3287 | + |
---|
| 3288 | +out: |
---|
| 3289 | + mlxsw_sp_fid_put(fid); |
---|
| 3290 | +} |
---|
| 3291 | + |
---|
| 3292 | +static int |
---|
| 3293 | +mlxsw_sp_switchdev_vxlan_vlans_add(struct net_device *vxlan_dev, |
---|
| 3294 | + struct switchdev_notifier_port_obj_info * |
---|
| 3295 | + port_obj_info) |
---|
| 3296 | +{ |
---|
| 3297 | + struct switchdev_obj_port_vlan *vlan = |
---|
| 3298 | + SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); |
---|
| 3299 | + bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; |
---|
| 3300 | + bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; |
---|
| 3301 | + struct switchdev_trans *trans = port_obj_info->trans; |
---|
| 3302 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 3303 | + struct netlink_ext_ack *extack; |
---|
| 3304 | + struct mlxsw_sp *mlxsw_sp; |
---|
| 3305 | + struct net_device *br_dev; |
---|
| 3306 | + u16 vid; |
---|
| 3307 | + |
---|
| 3308 | + extack = switchdev_notifier_info_to_extack(&port_obj_info->info); |
---|
| 3309 | + br_dev = netdev_master_upper_dev_get(vxlan_dev); |
---|
| 3310 | + if (!br_dev) |
---|
| 3311 | + return 0; |
---|
| 3312 | + |
---|
| 3313 | + mlxsw_sp = mlxsw_sp_lower_get(br_dev); |
---|
| 3314 | + if (!mlxsw_sp) |
---|
| 3315 | + return 0; |
---|
| 3316 | + |
---|
| 3317 | + port_obj_info->handled = true; |
---|
| 3318 | + |
---|
| 3319 | + if (switchdev_trans_ph_commit(trans)) |
---|
| 3320 | + return 0; |
---|
| 3321 | + |
---|
| 3322 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 3323 | + if (!bridge_device) |
---|
| 3324 | + return -EINVAL; |
---|
| 3325 | + |
---|
| 3326 | + if (!bridge_device->vlan_enabled) |
---|
| 3327 | + return 0; |
---|
| 3328 | + |
---|
| 3329 | + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { |
---|
| 3330 | + int err; |
---|
| 3331 | + |
---|
| 3332 | + err = mlxsw_sp_switchdev_vxlan_vlan_add(mlxsw_sp, bridge_device, |
---|
| 3333 | + vxlan_dev, vid, |
---|
| 3334 | + flag_untagged, |
---|
| 3335 | + flag_pvid, extack); |
---|
| 3336 | + if (err) |
---|
| 3337 | + return err; |
---|
| 3338 | + } |
---|
| 3339 | + |
---|
| 3340 | + return 0; |
---|
| 3341 | +} |
---|
| 3342 | + |
---|
| 3343 | +static void |
---|
| 3344 | +mlxsw_sp_switchdev_vxlan_vlans_del(struct net_device *vxlan_dev, |
---|
| 3345 | + struct switchdev_notifier_port_obj_info * |
---|
| 3346 | + port_obj_info) |
---|
| 3347 | +{ |
---|
| 3348 | + struct switchdev_obj_port_vlan *vlan = |
---|
| 3349 | + SWITCHDEV_OBJ_PORT_VLAN(port_obj_info->obj); |
---|
| 3350 | + struct mlxsw_sp_bridge_device *bridge_device; |
---|
| 3351 | + struct mlxsw_sp *mlxsw_sp; |
---|
| 3352 | + struct net_device *br_dev; |
---|
| 3353 | + u16 vid; |
---|
| 3354 | + |
---|
| 3355 | + br_dev = netdev_master_upper_dev_get(vxlan_dev); |
---|
| 3356 | + if (!br_dev) |
---|
| 3357 | + return; |
---|
| 3358 | + |
---|
| 3359 | + mlxsw_sp = mlxsw_sp_lower_get(br_dev); |
---|
| 3360 | + if (!mlxsw_sp) |
---|
| 3361 | + return; |
---|
| 3362 | + |
---|
| 3363 | + port_obj_info->handled = true; |
---|
| 3364 | + |
---|
| 3365 | + bridge_device = mlxsw_sp_bridge_device_find(mlxsw_sp->bridge, br_dev); |
---|
| 3366 | + if (!bridge_device) |
---|
| 3367 | + return; |
---|
| 3368 | + |
---|
| 3369 | + if (!bridge_device->vlan_enabled) |
---|
| 3370 | + return; |
---|
| 3371 | + |
---|
| 3372 | + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) |
---|
| 3373 | + mlxsw_sp_switchdev_vxlan_vlan_del(mlxsw_sp, bridge_device, |
---|
| 3374 | + vxlan_dev, vid); |
---|
| 3375 | +} |
---|
| 3376 | + |
---|
| 3377 | +static int |
---|
| 3378 | +mlxsw_sp_switchdev_handle_vxlan_obj_add(struct net_device *vxlan_dev, |
---|
| 3379 | + struct switchdev_notifier_port_obj_info * |
---|
| 3380 | + port_obj_info) |
---|
| 3381 | +{ |
---|
| 3382 | + int err = 0; |
---|
| 3383 | + |
---|
| 3384 | + switch (port_obj_info->obj->id) { |
---|
| 3385 | + case SWITCHDEV_OBJ_ID_PORT_VLAN: |
---|
| 3386 | + err = mlxsw_sp_switchdev_vxlan_vlans_add(vxlan_dev, |
---|
| 3387 | + port_obj_info); |
---|
| 3388 | + break; |
---|
| 3389 | + default: |
---|
| 3390 | + break; |
---|
| 3391 | + } |
---|
| 3392 | + |
---|
| 3393 | + return err; |
---|
| 3394 | +} |
---|
| 3395 | + |
---|
| 3396 | +static void |
---|
| 3397 | +mlxsw_sp_switchdev_handle_vxlan_obj_del(struct net_device *vxlan_dev, |
---|
| 3398 | + struct switchdev_notifier_port_obj_info * |
---|
| 3399 | + port_obj_info) |
---|
| 3400 | +{ |
---|
| 3401 | + switch (port_obj_info->obj->id) { |
---|
| 3402 | + case SWITCHDEV_OBJ_ID_PORT_VLAN: |
---|
| 3403 | + mlxsw_sp_switchdev_vxlan_vlans_del(vxlan_dev, port_obj_info); |
---|
| 3404 | + break; |
---|
| 3405 | + default: |
---|
| 3406 | + break; |
---|
| 3407 | + } |
---|
| 3408 | +} |
---|
| 3409 | + |
---|
| 3410 | +static int mlxsw_sp_switchdev_blocking_event(struct notifier_block *unused, |
---|
| 3411 | + unsigned long event, void *ptr) |
---|
| 3412 | +{ |
---|
| 3413 | + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); |
---|
| 3414 | + int err = 0; |
---|
| 3415 | + |
---|
| 3416 | + switch (event) { |
---|
| 3417 | + case SWITCHDEV_PORT_OBJ_ADD: |
---|
| 3418 | + if (netif_is_vxlan(dev)) |
---|
| 3419 | + err = mlxsw_sp_switchdev_handle_vxlan_obj_add(dev, ptr); |
---|
| 3420 | + else |
---|
| 3421 | + err = switchdev_handle_port_obj_add(dev, ptr, |
---|
| 3422 | + mlxsw_sp_port_dev_check, |
---|
| 3423 | + mlxsw_sp_port_obj_add); |
---|
| 3424 | + return notifier_from_errno(err); |
---|
| 3425 | + case SWITCHDEV_PORT_OBJ_DEL: |
---|
| 3426 | + if (netif_is_vxlan(dev)) |
---|
| 3427 | + mlxsw_sp_switchdev_handle_vxlan_obj_del(dev, ptr); |
---|
| 3428 | + else |
---|
| 3429 | + err = switchdev_handle_port_obj_del(dev, ptr, |
---|
| 3430 | + mlxsw_sp_port_dev_check, |
---|
| 3431 | + mlxsw_sp_port_obj_del); |
---|
| 3432 | + return notifier_from_errno(err); |
---|
| 3433 | + case SWITCHDEV_PORT_ATTR_SET: |
---|
| 3434 | + err = switchdev_handle_port_attr_set(dev, ptr, |
---|
| 3435 | + mlxsw_sp_port_dev_check, |
---|
| 3436 | + mlxsw_sp_port_attr_set); |
---|
| 3437 | + return notifier_from_errno(err); |
---|
| 3438 | + } |
---|
| 3439 | + |
---|
| 3440 | + return NOTIFY_DONE; |
---|
| 3441 | +} |
---|
| 3442 | + |
---|
| 3443 | +static struct notifier_block mlxsw_sp_switchdev_blocking_notifier = { |
---|
| 3444 | + .notifier_call = mlxsw_sp_switchdev_blocking_event, |
---|
2380 | 3445 | }; |
---|
2381 | 3446 | |
---|
2382 | 3447 | u8 |
---|
.. | .. |
---|
2388 | 3453 | static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp) |
---|
2389 | 3454 | { |
---|
2390 | 3455 | struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge; |
---|
| 3456 | + struct notifier_block *nb; |
---|
2391 | 3457 | int err; |
---|
2392 | 3458 | |
---|
2393 | 3459 | err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME); |
---|
.. | .. |
---|
2402 | 3468 | return err; |
---|
2403 | 3469 | } |
---|
2404 | 3470 | |
---|
| 3471 | + nb = &mlxsw_sp_switchdev_blocking_notifier; |
---|
| 3472 | + err = register_switchdev_blocking_notifier(nb); |
---|
| 3473 | + if (err) { |
---|
| 3474 | + dev_err(mlxsw_sp->bus_info->dev, "Failed to register switchdev blocking notifier\n"); |
---|
| 3475 | + goto err_register_switchdev_blocking_notifier; |
---|
| 3476 | + } |
---|
| 3477 | + |
---|
2405 | 3478 | INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work); |
---|
2406 | 3479 | bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL; |
---|
2407 | | - mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp); |
---|
| 3480 | + mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp, false); |
---|
2408 | 3481 | return 0; |
---|
| 3482 | + |
---|
| 3483 | +err_register_switchdev_blocking_notifier: |
---|
| 3484 | + unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); |
---|
| 3485 | + return err; |
---|
2409 | 3486 | } |
---|
2410 | 3487 | |
---|
2411 | 3488 | static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp) |
---|
2412 | 3489 | { |
---|
2413 | | - cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw); |
---|
2414 | | - unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); |
---|
| 3490 | + struct notifier_block *nb; |
---|
2415 | 3491 | |
---|
| 3492 | + cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw); |
---|
| 3493 | + |
---|
| 3494 | + nb = &mlxsw_sp_switchdev_blocking_notifier; |
---|
| 3495 | + unregister_switchdev_blocking_notifier(nb); |
---|
| 3496 | + |
---|
| 3497 | + unregister_switchdev_notifier(&mlxsw_sp_switchdev_notifier); |
---|
2416 | 3498 | } |
---|
2417 | 3499 | |
---|
2418 | 3500 | int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp) |
---|
.. | .. |
---|
2440 | 3522 | kfree(mlxsw_sp->bridge); |
---|
2441 | 3523 | } |
---|
2442 | 3524 | |
---|
2443 | | -void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port) |
---|
2444 | | -{ |
---|
2445 | | - mlxsw_sp_port->dev->switchdev_ops = &mlxsw_sp_port_switchdev_ops; |
---|
2446 | | -} |
---|
2447 | | - |
---|
2448 | | -void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port) |
---|
2449 | | -{ |
---|
2450 | | -} |
---|