diff options
-rw-r--r-- | net/dsa/port.c | 12 | ||||
-rw-r--r-- | net/dsa/slave.c | 110 | ||||
-rw-r--r-- | net/dsa/switch.c | 42 |
3 files changed, 157 insertions, 7 deletions
diff --git a/net/dsa/port.c b/net/dsa/port.c index e9b5b50f8cf1..c011dff523d0 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c | |||
@@ -291,7 +291,10 @@ int dsa_port_vlan_add(struct dsa_port *dp, | |||
291 | .vlan = vlan, | 291 | .vlan = vlan, |
292 | }; | 292 | }; |
293 | 293 | ||
294 | if (br_vlan_enabled(dp->bridge_dev)) | 294 | /* Can be called from dsa_slave_port_obj_add() or |
295 | * dsa_slave_vlan_rx_add_vid() | ||
296 | */ | ||
297 | if (!dp->bridge_dev || br_vlan_enabled(dp->bridge_dev)) | ||
295 | return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); | 298 | return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_ADD, &info); |
296 | 299 | ||
297 | return 0; | 300 | return 0; |
@@ -306,10 +309,13 @@ int dsa_port_vlan_del(struct dsa_port *dp, | |||
306 | .vlan = vlan, | 309 | .vlan = vlan, |
307 | }; | 310 | }; |
308 | 311 | ||
309 | if (netif_is_bridge_master(vlan->obj.orig_dev)) | 312 | if (vlan->obj.orig_dev && netif_is_bridge_master(vlan->obj.orig_dev)) |
310 | return -EOPNOTSUPP; | 313 | return -EOPNOTSUPP; |
311 | 314 | ||
312 | if (br_vlan_enabled(dp->bridge_dev)) | 315 | /* Can be called from dsa_slave_port_obj_del() or |
316 | * dsa_slave_vlan_rx_kill_vid() | ||
317 | */ | ||
318 | if (!dp->bridge_dev || br_vlan_enabled(dp->bridge_dev)) | ||
313 | return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); | 319 | return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); |
314 | 320 | ||
315 | return 0; | 321 | return 0; |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index a78b2bba0332..90629b12beaf 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -983,6 +983,72 @@ static int dsa_slave_get_ts_info(struct net_device *dev, | |||
983 | return ds->ops->get_ts_info(ds, p->dp->index, ts); | 983 | return ds->ops->get_ts_info(ds, p->dp->index, ts); |
984 | } | 984 | } |
985 | 985 | ||
986 | static int dsa_slave_vlan_rx_add_vid(struct net_device *dev, __be16 proto, | ||
987 | u16 vid) | ||
988 | { | ||
989 | struct dsa_port *dp = dsa_slave_to_port(dev); | ||
990 | struct switchdev_obj_port_vlan vlan = { | ||
991 | .vid_begin = vid, | ||
992 | .vid_end = vid, | ||
993 | /* This API only allows programming tagged, non-PVID VIDs */ | ||
994 | .flags = 0, | ||
995 | }; | ||
996 | struct bridge_vlan_info info; | ||
997 | int ret; | ||
998 | |||
999 | /* Check for a possible bridge VLAN entry now since there is no | ||
1000 | * need to emulate the switchdev prepare + commit phase. | ||
1001 | */ | ||
1002 | if (dp->bridge_dev) { | ||
1003 | /* br_vlan_get_info() returns -EINVAL or -ENOENT if the | ||
1004 | * device, respectively the VID is not found, returning | ||
1005 | * 0 means success, which is a failure for us here. | ||
1006 | */ | ||
1007 | ret = br_vlan_get_info(dp->bridge_dev, vid, &info); | ||
1008 | if (ret == 0) | ||
1009 | return -EBUSY; | ||
1010 | } | ||
1011 | |||
1012 | ret = dsa_port_vlan_add(dp, &vlan, NULL); | ||
1013 | if (ret == -EOPNOTSUPP) | ||
1014 | ret = 0; | ||
1015 | |||
1016 | return ret; | ||
1017 | } | ||
1018 | |||
1019 | static int dsa_slave_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, | ||
1020 | u16 vid) | ||
1021 | { | ||
1022 | struct dsa_port *dp = dsa_slave_to_port(dev); | ||
1023 | struct switchdev_obj_port_vlan vlan = { | ||
1024 | .vid_begin = vid, | ||
1025 | .vid_end = vid, | ||
1026 | /* This API only allows programming tagged, non-PVID VIDs */ | ||
1027 | .flags = 0, | ||
1028 | }; | ||
1029 | struct bridge_vlan_info info; | ||
1030 | int ret; | ||
1031 | |||
1032 | /* Check for a possible bridge VLAN entry now since there is no | ||
1033 | * need to emulate the switchdev prepare + commit phase. | ||
1034 | */ | ||
1035 | if (dp->bridge_dev) { | ||
1036 | /* br_vlan_get_info() returns -EINVAL or -ENOENT if the | ||
1037 | * device, respectively the VID is not found, returning | ||
1038 | * 0 means success, which is a failure for us here. | ||
1039 | */ | ||
1040 | ret = br_vlan_get_info(dp->bridge_dev, vid, &info); | ||
1041 | if (ret == 0) | ||
1042 | return -EBUSY; | ||
1043 | } | ||
1044 | |||
1045 | ret = dsa_port_vlan_del(dp, &vlan); | ||
1046 | if (ret == -EOPNOTSUPP) | ||
1047 | ret = 0; | ||
1048 | |||
1049 | return ret; | ||
1050 | } | ||
1051 | |||
986 | static const struct ethtool_ops dsa_slave_ethtool_ops = { | 1052 | static const struct ethtool_ops dsa_slave_ethtool_ops = { |
987 | .get_drvinfo = dsa_slave_get_drvinfo, | 1053 | .get_drvinfo = dsa_slave_get_drvinfo, |
988 | .get_regs_len = dsa_slave_get_regs_len, | 1054 | .get_regs_len = dsa_slave_get_regs_len, |
@@ -1048,6 +1114,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = { | |||
1048 | .ndo_setup_tc = dsa_slave_setup_tc, | 1114 | .ndo_setup_tc = dsa_slave_setup_tc, |
1049 | .ndo_get_stats64 = dsa_slave_get_stats64, | 1115 | .ndo_get_stats64 = dsa_slave_get_stats64, |
1050 | .ndo_get_port_parent_id = dsa_slave_get_port_parent_id, | 1116 | .ndo_get_port_parent_id = dsa_slave_get_port_parent_id, |
1117 | .ndo_vlan_rx_add_vid = dsa_slave_vlan_rx_add_vid, | ||
1118 | .ndo_vlan_rx_kill_vid = dsa_slave_vlan_rx_kill_vid, | ||
1051 | }; | 1119 | }; |
1052 | 1120 | ||
1053 | static const struct switchdev_ops dsa_slave_switchdev_ops = { | 1121 | static const struct switchdev_ops dsa_slave_switchdev_ops = { |
@@ -1307,7 +1375,8 @@ int dsa_slave_create(struct dsa_port *port) | |||
1307 | if (slave_dev == NULL) | 1375 | if (slave_dev == NULL) |
1308 | return -ENOMEM; | 1376 | return -ENOMEM; |
1309 | 1377 | ||
1310 | slave_dev->features = master->vlan_features | NETIF_F_HW_TC; | 1378 | slave_dev->features = master->vlan_features | NETIF_F_HW_TC | |
1379 | NETIF_F_HW_VLAN_CTAG_FILTER; | ||
1311 | slave_dev->hw_features |= NETIF_F_HW_TC; | 1380 | slave_dev->hw_features |= NETIF_F_HW_TC; |
1312 | slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; | 1381 | slave_dev->ethtool_ops = &dsa_slave_ethtool_ops; |
1313 | eth_hw_addr_inherit(slave_dev, master); | 1382 | eth_hw_addr_inherit(slave_dev, master); |
@@ -1408,16 +1477,49 @@ static int dsa_slave_changeupper(struct net_device *dev, | |||
1408 | return err; | 1477 | return err; |
1409 | } | 1478 | } |
1410 | 1479 | ||
1480 | static int dsa_slave_upper_vlan_check(struct net_device *dev, | ||
1481 | struct netdev_notifier_changeupper_info * | ||
1482 | info) | ||
1483 | { | ||
1484 | struct netlink_ext_ack *ext_ack; | ||
1485 | struct net_device *slave; | ||
1486 | struct dsa_port *dp; | ||
1487 | |||
1488 | ext_ack = netdev_notifier_info_to_extack(&info->info); | ||
1489 | |||
1490 | if (!is_vlan_dev(dev)) | ||
1491 | return NOTIFY_DONE; | ||
1492 | |||
1493 | slave = vlan_dev_real_dev(dev); | ||
1494 | if (!dsa_slave_dev_check(slave)) | ||
1495 | return NOTIFY_DONE; | ||
1496 | |||
1497 | dp = dsa_slave_to_port(slave); | ||
1498 | if (!dp->bridge_dev) | ||
1499 | return NOTIFY_DONE; | ||
1500 | |||
1501 | /* Deny enslaving a VLAN device into a VLAN-aware bridge */ | ||
1502 | if (br_vlan_enabled(dp->bridge_dev) && | ||
1503 | netif_is_bridge_master(info->upper_dev) && info->linking) { | ||
1504 | NL_SET_ERR_MSG_MOD(ext_ack, | ||
1505 | "Cannot enslave VLAN device into VLAN aware bridge"); | ||
1506 | return notifier_from_errno(-EINVAL); | ||
1507 | } | ||
1508 | |||
1509 | return NOTIFY_DONE; | ||
1510 | } | ||
1511 | |||
1411 | static int dsa_slave_netdevice_event(struct notifier_block *nb, | 1512 | static int dsa_slave_netdevice_event(struct notifier_block *nb, |
1412 | unsigned long event, void *ptr) | 1513 | unsigned long event, void *ptr) |
1413 | { | 1514 | { |
1414 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); | 1515 | struct net_device *dev = netdev_notifier_info_to_dev(ptr); |
1415 | 1516 | ||
1416 | if (!dsa_slave_dev_check(dev)) | 1517 | if (event == NETDEV_CHANGEUPPER) { |
1417 | return NOTIFY_DONE; | 1518 | if (!dsa_slave_dev_check(dev)) |
1519 | return dsa_slave_upper_vlan_check(dev, ptr); | ||
1418 | 1520 | ||
1419 | if (event == NETDEV_CHANGEUPPER) | ||
1420 | return dsa_slave_changeupper(dev, ptr); | 1521 | return dsa_slave_changeupper(dev, ptr); |
1522 | } | ||
1421 | 1523 | ||
1422 | return NOTIFY_DONE; | 1524 | return NOTIFY_DONE; |
1423 | } | 1525 | } |
diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 142b294d3446..e1fae969aa73 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c | |||
@@ -12,6 +12,7 @@ | |||
12 | 12 | ||
13 | #include <linux/netdevice.h> | 13 | #include <linux/netdevice.h> |
14 | #include <linux/notifier.h> | 14 | #include <linux/notifier.h> |
15 | #include <linux/if_vlan.h> | ||
15 | #include <net/switchdev.h> | 16 | #include <net/switchdev.h> |
16 | 17 | ||
17 | #include "dsa_priv.h" | 18 | #include "dsa_priv.h" |
@@ -168,6 +169,43 @@ static int dsa_switch_mdb_del(struct dsa_switch *ds, | |||
168 | return 0; | 169 | return 0; |
169 | } | 170 | } |
170 | 171 | ||
172 | static int dsa_port_vlan_device_check(struct net_device *vlan_dev, | ||
173 | int vlan_dev_vid, | ||
174 | void *arg) | ||
175 | { | ||
176 | struct switchdev_obj_port_vlan *vlan = arg; | ||
177 | u16 vid; | ||
178 | |||
179 | for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) { | ||
180 | if (vid == vlan_dev_vid) | ||
181 | return -EBUSY; | ||
182 | } | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static int dsa_port_vlan_check(struct dsa_switch *ds, int port, | ||
188 | const struct switchdev_obj_port_vlan *vlan) | ||
189 | { | ||
190 | const struct dsa_port *dp = dsa_to_port(ds, port); | ||
191 | int err = 0; | ||
192 | |||
193 | /* Device is not bridged, let it proceed with the VLAN device | ||
194 | * creation. | ||
195 | */ | ||
196 | if (!dp->bridge_dev) | ||
197 | return err; | ||
198 | |||
199 | /* dsa_slave_vlan_rx_{add,kill}_vid() cannot use the prepare pharse and | ||
200 | * already checks whether there is an overlapping bridge VLAN entry | ||
201 | * with the same VID, so here we only need to check that if we are | ||
202 | * adding a bridge VLAN entry there is not an overlapping VLAN device | ||
203 | * claiming that VID. | ||
204 | */ | ||
205 | return vlan_for_each(dp->slave, dsa_port_vlan_device_check, | ||
206 | (void *)vlan); | ||
207 | } | ||
208 | |||
171 | static int | 209 | static int |
172 | dsa_switch_vlan_prepare_bitmap(struct dsa_switch *ds, | 210 | dsa_switch_vlan_prepare_bitmap(struct dsa_switch *ds, |
173 | const struct switchdev_obj_port_vlan *vlan, | 211 | const struct switchdev_obj_port_vlan *vlan, |
@@ -179,6 +217,10 @@ dsa_switch_vlan_prepare_bitmap(struct dsa_switch *ds, | |||
179 | return -EOPNOTSUPP; | 217 | return -EOPNOTSUPP; |
180 | 218 | ||
181 | for_each_set_bit(port, bitmap, ds->num_ports) { | 219 | for_each_set_bit(port, bitmap, ds->num_ports) { |
220 | err = dsa_port_vlan_check(ds, port, vlan); | ||
221 | if (err) | ||
222 | return err; | ||
223 | |||
182 | err = ds->ops->port_vlan_prepare(ds, port, vlan); | 224 | err = ds->ops->port_vlan_prepare(ds, port, vlan); |
183 | if (err) | 225 | if (err) |
184 | return err; | 226 | return err; |