|
7 | 7 | */ |
8 | 8 |
|
9 | 9 | #include <linux/if_bridge.h> |
| 10 | +#include <linux/netdevice.h> |
10 | 11 | #include <linux/notifier.h> |
11 | 12 | #include <linux/of_mdio.h> |
12 | 13 | #include <linux/of_net.h> |
@@ -1374,6 +1375,135 @@ int dsa_port_mrp_del_ring_role(const struct dsa_port *dp, |
1374 | 1375 | return ds->ops->port_mrp_del_ring_role(ds, dp->index, mrp); |
1375 | 1376 | } |
1376 | 1377 |
|
| 1378 | +static int dsa_port_assign_master(struct dsa_port *dp, |
| 1379 | + struct net_device *master, |
| 1380 | + struct netlink_ext_ack *extack, |
| 1381 | + bool fail_on_err) |
| 1382 | +{ |
| 1383 | + struct dsa_switch *ds = dp->ds; |
| 1384 | + int port = dp->index, err; |
| 1385 | + |
| 1386 | + err = ds->ops->port_change_master(ds, port, master, extack); |
| 1387 | + if (err && !fail_on_err) |
| 1388 | + dev_err(ds->dev, "port %d failed to assign master %s: %pe\n", |
| 1389 | + port, master->name, ERR_PTR(err)); |
| 1390 | + |
| 1391 | + if (err && fail_on_err) |
| 1392 | + return err; |
| 1393 | + |
| 1394 | + dp->cpu_dp = master->dsa_ptr; |
| 1395 | + |
| 1396 | + return 0; |
| 1397 | +} |
| 1398 | + |
| 1399 | +/* Change the dp->cpu_dp affinity for a user port. Note that both cross-chip |
| 1400 | + * notifiers and drivers have implicit assumptions about user-to-CPU-port |
| 1401 | + * mappings, so we unfortunately cannot delay the deletion of the objects |
| 1402 | + * (switchdev, standalone addresses, standalone VLANs) on the old CPU port |
| 1403 | + * until the new CPU port has been set up. So we need to completely tear down |
| 1404 | + * the old CPU port before changing it, and restore it on errors during the |
| 1405 | + * bringup of the new one. |
| 1406 | + */ |
| 1407 | +int dsa_port_change_master(struct dsa_port *dp, struct net_device *master, |
| 1408 | + struct netlink_ext_ack *extack) |
| 1409 | +{ |
| 1410 | + struct net_device *bridge_dev = dsa_port_bridge_dev_get(dp); |
| 1411 | + struct net_device *old_master = dsa_port_to_master(dp); |
| 1412 | + struct net_device *dev = dp->slave; |
| 1413 | + struct dsa_switch *ds = dp->ds; |
| 1414 | + bool vlan_filtering; |
| 1415 | + int err, tmp; |
| 1416 | + |
| 1417 | + /* Bridges may hold host FDB, MDB and VLAN objects. These need to be |
| 1418 | + * migrated, so dynamically unoffload and later reoffload the bridge |
| 1419 | + * port. |
| 1420 | + */ |
| 1421 | + if (bridge_dev) { |
| 1422 | + dsa_port_pre_bridge_leave(dp, bridge_dev); |
| 1423 | + dsa_port_bridge_leave(dp, bridge_dev); |
| 1424 | + } |
| 1425 | + |
| 1426 | + /* The port might still be VLAN filtering even if it's no longer |
| 1427 | + * under a bridge, either due to ds->vlan_filtering_is_global or |
| 1428 | + * ds->needs_standalone_vlan_filtering. In turn this means VLANs |
| 1429 | + * on the CPU port. |
| 1430 | + */ |
| 1431 | + vlan_filtering = dsa_port_is_vlan_filtering(dp); |
| 1432 | + if (vlan_filtering) { |
| 1433 | + err = dsa_slave_manage_vlan_filtering(dev, false); |
| 1434 | + if (err) { |
| 1435 | + NL_SET_ERR_MSG_MOD(extack, |
| 1436 | + "Failed to remove standalone VLANs"); |
| 1437 | + goto rewind_old_bridge; |
| 1438 | + } |
| 1439 | + } |
| 1440 | + |
| 1441 | + /* Standalone addresses, and addresses of upper interfaces like |
| 1442 | + * VLAN, LAG, HSR need to be migrated. |
| 1443 | + */ |
| 1444 | + dsa_slave_unsync_ha(dev); |
| 1445 | + |
| 1446 | + err = dsa_port_assign_master(dp, master, extack, true); |
| 1447 | + if (err) |
| 1448 | + goto rewind_old_addrs; |
| 1449 | + |
| 1450 | + dsa_slave_sync_ha(dev); |
| 1451 | + |
| 1452 | + if (vlan_filtering) { |
| 1453 | + err = dsa_slave_manage_vlan_filtering(dev, true); |
| 1454 | + if (err) { |
| 1455 | + NL_SET_ERR_MSG_MOD(extack, |
| 1456 | + "Failed to restore standalone VLANs"); |
| 1457 | + goto rewind_new_addrs; |
| 1458 | + } |
| 1459 | + } |
| 1460 | + |
| 1461 | + if (bridge_dev) { |
| 1462 | + err = dsa_port_bridge_join(dp, bridge_dev, extack); |
| 1463 | + if (err && err == -EOPNOTSUPP) { |
| 1464 | + NL_SET_ERR_MSG_MOD(extack, |
| 1465 | + "Failed to reoffload bridge"); |
| 1466 | + goto rewind_new_vlan; |
| 1467 | + } |
| 1468 | + } |
| 1469 | + |
| 1470 | + return 0; |
| 1471 | + |
| 1472 | +rewind_new_vlan: |
| 1473 | + if (vlan_filtering) |
| 1474 | + dsa_slave_manage_vlan_filtering(dev, false); |
| 1475 | + |
| 1476 | +rewind_new_addrs: |
| 1477 | + dsa_slave_unsync_ha(dev); |
| 1478 | + |
| 1479 | + dsa_port_assign_master(dp, old_master, NULL, false); |
| 1480 | + |
| 1481 | +/* Restore the objects on the old CPU port */ |
| 1482 | +rewind_old_addrs: |
| 1483 | + dsa_slave_sync_ha(dev); |
| 1484 | + |
| 1485 | + if (vlan_filtering) { |
| 1486 | + tmp = dsa_slave_manage_vlan_filtering(dev, true); |
| 1487 | + if (tmp) { |
| 1488 | + dev_err(ds->dev, |
| 1489 | + "port %d failed to restore standalone VLANs: %pe\n", |
| 1490 | + dp->index, ERR_PTR(tmp)); |
| 1491 | + } |
| 1492 | + } |
| 1493 | + |
| 1494 | +rewind_old_bridge: |
| 1495 | + if (bridge_dev) { |
| 1496 | + tmp = dsa_port_bridge_join(dp, bridge_dev, extack); |
| 1497 | + if (tmp) { |
| 1498 | + dev_err(ds->dev, |
| 1499 | + "port %d failed to rejoin bridge %s: %pe\n", |
| 1500 | + dp->index, bridge_dev->name, ERR_PTR(tmp)); |
| 1501 | + } |
| 1502 | + } |
| 1503 | + |
| 1504 | + return err; |
| 1505 | +} |
| 1506 | + |
1377 | 1507 | void dsa_port_set_tag_protocol(struct dsa_port *cpu_dp, |
1378 | 1508 | const struct dsa_device_ops *tag_ops) |
1379 | 1509 | { |
|
0 commit comments