diff options
author | David S. Miller <davem@davemloft.net> | 2016-03-01 16:24:54 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-03-01 16:24:54 -0500 |
commit | ec1606c0906de35dbc6f21aea75c2b950104cae5 (patch) | |
tree | 923ad677a36f4dae005e1f52c1973456fe2b23a9 | |
parent | 7f66ee41566d00f80ed15c0cec0b237f7af8ac0f (diff) | |
parent | 214cdb998739428b09d80b4b152faa7d1e6ad156 (diff) |
Merge branch 'mv88e6xxx-vlan-filtering'
Vivien Didelot says:
====================
net: dsa: mv88e6xxx: implement VLAN filtering
This patchset fixes hardware bridging for non 802.1Q aware systems.
The mv88e6xxx DSA driver currently depends on CONFIG_VLAN_8021Q and
CONFIG_BRIDGE_VLAN_FILTERING enabled for correct bridging between switch ports.
Patch 1/9 adds support for the VLAN filtering switchdev attribute in DSA.
Patchs 2/9 and 3/9 add helper functions for the following patches.
Patchs 4/9 to 6/9 assign dynamic address databases to VLANs, ports, and
bridge groups (the lowest available FID is cleared and assigned), and thus
restore support for per-port FDB operations.
Patchs 7/9 to 9/9 refine ports isolation and setup 802.1Q on user demand.
With this patchset, ports get correctly bridged and the driver behaves as
expected, with or without 802.1Q support.
With CONFIG_VLAN_8021Q enabled, setting a default PVID to the bridge correctly
propagates the corresponding VLAN, in addition to the hardware bridging:
# echo 42 > /sys/class/net/<bridge>/bridge/default_pvid
But considering CONFIG_BRIDGE_VLAN_FILTERING enabled, the hardware VLAN
filtering is enabled on all bridge members only when the user requests it:
# echo 1 > /sys/class/net/<bridge>/bridge/vlan_filtering
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/dsa/mv88e6171.c | 1 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6352.c | 1 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.c | 441 | ||||
-rw-r--r-- | drivers/net/dsa/mv88e6xxx.h | 6 | ||||
-rw-r--r-- | include/net/dsa.h | 2 | ||||
-rw-r--r-- | net/dsa/slave.c | 21 |
6 files changed, 370 insertions, 102 deletions
diff --git a/drivers/net/dsa/mv88e6171.c b/drivers/net/dsa/mv88e6171.c index dd1ebaf48077..d72ccbdf53ec 100644 --- a/drivers/net/dsa/mv88e6171.c +++ b/drivers/net/dsa/mv88e6171.c | |||
@@ -106,6 +106,7 @@ struct dsa_switch_driver mv88e6171_switch_driver = { | |||
106 | .port_join_bridge = mv88e6xxx_port_bridge_join, | 106 | .port_join_bridge = mv88e6xxx_port_bridge_join, |
107 | .port_leave_bridge = mv88e6xxx_port_bridge_leave, | 107 | .port_leave_bridge = mv88e6xxx_port_bridge_leave, |
108 | .port_stp_update = mv88e6xxx_port_stp_update, | 108 | .port_stp_update = mv88e6xxx_port_stp_update, |
109 | .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, | ||
109 | .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, | 110 | .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, |
110 | .port_vlan_add = mv88e6xxx_port_vlan_add, | 111 | .port_vlan_add = mv88e6xxx_port_vlan_add, |
111 | .port_vlan_del = mv88e6xxx_port_vlan_del, | 112 | .port_vlan_del = mv88e6xxx_port_vlan_del, |
diff --git a/drivers/net/dsa/mv88e6352.c b/drivers/net/dsa/mv88e6352.c index bbca36ac4f77..a41fa5043d77 100644 --- a/drivers/net/dsa/mv88e6352.c +++ b/drivers/net/dsa/mv88e6352.c | |||
@@ -327,6 +327,7 @@ struct dsa_switch_driver mv88e6352_switch_driver = { | |||
327 | .port_join_bridge = mv88e6xxx_port_bridge_join, | 327 | .port_join_bridge = mv88e6xxx_port_bridge_join, |
328 | .port_leave_bridge = mv88e6xxx_port_bridge_leave, | 328 | .port_leave_bridge = mv88e6xxx_port_bridge_leave, |
329 | .port_stp_update = mv88e6xxx_port_stp_update, | 329 | .port_stp_update = mv88e6xxx_port_stp_update, |
330 | .port_vlan_filtering = mv88e6xxx_port_vlan_filtering, | ||
330 | .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, | 331 | .port_vlan_prepare = mv88e6xxx_port_vlan_prepare, |
331 | .port_vlan_add = mv88e6xxx_port_vlan_add, | 332 | .port_vlan_add = mv88e6xxx_port_vlan_add, |
332 | .port_vlan_del = mv88e6xxx_port_vlan_del, | 333 | .port_vlan_del = mv88e6xxx_port_vlan_del, |
diff --git a/drivers/net/dsa/mv88e6xxx.c b/drivers/net/dsa/mv88e6xxx.c index d98dc635b00b..d11c9d58cf10 100644 --- a/drivers/net/dsa/mv88e6xxx.c +++ b/drivers/net/dsa/mv88e6xxx.c | |||
@@ -1087,12 +1087,32 @@ abort: | |||
1087 | return ret; | 1087 | return ret; |
1088 | } | 1088 | } |
1089 | 1089 | ||
1090 | static int _mv88e6xxx_port_vlan_map_set(struct dsa_switch *ds, int port, | 1090 | static int _mv88e6xxx_port_based_vlan_map(struct dsa_switch *ds, int port) |
1091 | u16 output_ports) | ||
1092 | { | 1091 | { |
1093 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | 1092 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
1093 | struct net_device *bridge = ps->ports[port].bridge_dev; | ||
1094 | const u16 mask = (1 << ps->num_ports) - 1; | 1094 | const u16 mask = (1 << ps->num_ports) - 1; |
1095 | u16 output_ports = 0; | ||
1095 | int reg; | 1096 | int reg; |
1097 | int i; | ||
1098 | |||
1099 | /* allow CPU port or DSA link(s) to send frames to every port */ | ||
1100 | if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port)) { | ||
1101 | output_ports = mask; | ||
1102 | } else { | ||
1103 | for (i = 0; i < ps->num_ports; ++i) { | ||
1104 | /* allow sending frames to every group member */ | ||
1105 | if (bridge && ps->ports[i].bridge_dev == bridge) | ||
1106 | output_ports |= BIT(i); | ||
1107 | |||
1108 | /* allow sending frames to CPU port and DSA link(s) */ | ||
1109 | if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) | ||
1110 | output_ports |= BIT(i); | ||
1111 | } | ||
1112 | } | ||
1113 | |||
1114 | /* prevent frames from going back out of the port they came in on */ | ||
1115 | output_ports &= ~BIT(port); | ||
1096 | 1116 | ||
1097 | reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); | 1117 | reg = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); |
1098 | if (reg < 0) | 1118 | if (reg < 0) |
@@ -1458,16 +1478,122 @@ loadpurge: | |||
1458 | return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE); | 1478 | return _mv88e6xxx_vtu_cmd(ds, GLOBAL_VTU_OP_STU_LOAD_PURGE); |
1459 | } | 1479 | } |
1460 | 1480 | ||
1461 | static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid, | 1481 | static int _mv88e6xxx_port_fid(struct dsa_switch *ds, int port, u16 *new, |
1462 | struct mv88e6xxx_vtu_stu_entry *entry) | 1482 | u16 *old) |
1483 | { | ||
1484 | u16 fid; | ||
1485 | int ret; | ||
1486 | |||
1487 | /* Port's default FID bits 3:0 are located in reg 0x06, offset 12 */ | ||
1488 | ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_BASE_VLAN); | ||
1489 | if (ret < 0) | ||
1490 | return ret; | ||
1491 | |||
1492 | fid = (ret & PORT_BASE_VLAN_FID_3_0_MASK) >> 12; | ||
1493 | |||
1494 | if (new) { | ||
1495 | ret &= ~PORT_BASE_VLAN_FID_3_0_MASK; | ||
1496 | ret |= (*new << 12) & PORT_BASE_VLAN_FID_3_0_MASK; | ||
1497 | |||
1498 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_BASE_VLAN, | ||
1499 | ret); | ||
1500 | if (ret < 0) | ||
1501 | return ret; | ||
1502 | } | ||
1503 | |||
1504 | /* Port's default FID bits 11:4 are located in reg 0x05, offset 0 */ | ||
1505 | ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_1); | ||
1506 | if (ret < 0) | ||
1507 | return ret; | ||
1508 | |||
1509 | fid |= (ret & PORT_CONTROL_1_FID_11_4_MASK) << 4; | ||
1510 | |||
1511 | if (new) { | ||
1512 | ret &= ~PORT_CONTROL_1_FID_11_4_MASK; | ||
1513 | ret |= (*new >> 4) & PORT_CONTROL_1_FID_11_4_MASK; | ||
1514 | |||
1515 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_1, | ||
1516 | ret); | ||
1517 | if (ret < 0) | ||
1518 | return ret; | ||
1519 | |||
1520 | netdev_dbg(ds->ports[port], "FID %d (was %d)\n", *new, fid); | ||
1521 | } | ||
1522 | |||
1523 | if (old) | ||
1524 | *old = fid; | ||
1525 | |||
1526 | return 0; | ||
1527 | } | ||
1528 | |||
1529 | static int _mv88e6xxx_port_fid_get(struct dsa_switch *ds, int port, u16 *fid) | ||
1530 | { | ||
1531 | return _mv88e6xxx_port_fid(ds, port, NULL, fid); | ||
1532 | } | ||
1533 | |||
1534 | static int _mv88e6xxx_port_fid_set(struct dsa_switch *ds, int port, u16 fid) | ||
1535 | { | ||
1536 | return _mv88e6xxx_port_fid(ds, port, &fid, NULL); | ||
1537 | } | ||
1538 | |||
1539 | static int _mv88e6xxx_fid_new(struct dsa_switch *ds, u16 *fid) | ||
1540 | { | ||
1541 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1542 | DECLARE_BITMAP(fid_bitmap, MV88E6XXX_N_FID); | ||
1543 | struct mv88e6xxx_vtu_stu_entry vlan; | ||
1544 | int i, err; | ||
1545 | |||
1546 | bitmap_zero(fid_bitmap, MV88E6XXX_N_FID); | ||
1547 | |||
1548 | /* Set every FID bit used by the (un)bridged ports */ | ||
1549 | for (i = 0; i < ps->num_ports; ++i) { | ||
1550 | err = _mv88e6xxx_port_fid_get(ds, i, fid); | ||
1551 | if (err) | ||
1552 | return err; | ||
1553 | |||
1554 | set_bit(*fid, fid_bitmap); | ||
1555 | } | ||
1556 | |||
1557 | /* Set every FID bit used by the VLAN entries */ | ||
1558 | err = _mv88e6xxx_vtu_vid_write(ds, GLOBAL_VTU_VID_MASK); | ||
1559 | if (err) | ||
1560 | return err; | ||
1561 | |||
1562 | do { | ||
1563 | err = _mv88e6xxx_vtu_getnext(ds, &vlan); | ||
1564 | if (err) | ||
1565 | return err; | ||
1566 | |||
1567 | if (!vlan.valid) | ||
1568 | break; | ||
1569 | |||
1570 | set_bit(vlan.fid, fid_bitmap); | ||
1571 | } while (vlan.vid < GLOBAL_VTU_VID_MASK); | ||
1572 | |||
1573 | /* The reset value 0x000 is used to indicate that multiple address | ||
1574 | * databases are not needed. Return the next positive available. | ||
1575 | */ | ||
1576 | *fid = find_next_zero_bit(fid_bitmap, MV88E6XXX_N_FID, 1); | ||
1577 | if (unlikely(*fid == MV88E6XXX_N_FID)) | ||
1578 | return -ENOSPC; | ||
1579 | |||
1580 | /* Clear the database */ | ||
1581 | return _mv88e6xxx_atu_flush(ds, *fid, true); | ||
1582 | } | ||
1583 | |||
1584 | static int _mv88e6xxx_vtu_new(struct dsa_switch *ds, u16 vid, | ||
1585 | struct mv88e6xxx_vtu_stu_entry *entry) | ||
1463 | { | 1586 | { |
1464 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | 1587 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
1465 | struct mv88e6xxx_vtu_stu_entry vlan = { | 1588 | struct mv88e6xxx_vtu_stu_entry vlan = { |
1466 | .valid = true, | 1589 | .valid = true, |
1467 | .vid = vid, | 1590 | .vid = vid, |
1468 | .fid = vid, /* We use one FID per VLAN */ | ||
1469 | }; | 1591 | }; |
1470 | int i; | 1592 | int i, err; |
1593 | |||
1594 | err = _mv88e6xxx_fid_new(ds, &vlan.fid); | ||
1595 | if (err) | ||
1596 | return err; | ||
1471 | 1597 | ||
1472 | /* exclude all ports except the CPU and DSA ports */ | 1598 | /* exclude all ports except the CPU and DSA ports */ |
1473 | for (i = 0; i < ps->num_ports; ++i) | 1599 | for (i = 0; i < ps->num_ports; ++i) |
@@ -1478,7 +1604,6 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid, | |||
1478 | if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || | 1604 | if (mv88e6xxx_6097_family(ds) || mv88e6xxx_6165_family(ds) || |
1479 | mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { | 1605 | mv88e6xxx_6351_family(ds) || mv88e6xxx_6352_family(ds)) { |
1480 | struct mv88e6xxx_vtu_stu_entry vstp; | 1606 | struct mv88e6xxx_vtu_stu_entry vstp; |
1481 | int err; | ||
1482 | 1607 | ||
1483 | /* Adding a VTU entry requires a valid STU entry. As VSTP is not | 1608 | /* Adding a VTU entry requires a valid STU entry. As VSTP is not |
1484 | * implemented, only one STU entry is needed to cover all VTU | 1609 | * implemented, only one STU entry is needed to cover all VTU |
@@ -1498,17 +1623,41 @@ static int _mv88e6xxx_vlan_init(struct dsa_switch *ds, u16 vid, | |||
1498 | if (err) | 1623 | if (err) |
1499 | return err; | 1624 | return err; |
1500 | } | 1625 | } |
1501 | |||
1502 | /* Clear all MAC addresses from the new database */ | ||
1503 | err = _mv88e6xxx_atu_flush(ds, vlan.fid, true); | ||
1504 | if (err) | ||
1505 | return err; | ||
1506 | } | 1626 | } |
1507 | 1627 | ||
1508 | *entry = vlan; | 1628 | *entry = vlan; |
1509 | return 0; | 1629 | return 0; |
1510 | } | 1630 | } |
1511 | 1631 | ||
1632 | static int _mv88e6xxx_vtu_get(struct dsa_switch *ds, u16 vid, | ||
1633 | struct mv88e6xxx_vtu_stu_entry *entry, bool creat) | ||
1634 | { | ||
1635 | int err; | ||
1636 | |||
1637 | if (!vid) | ||
1638 | return -EINVAL; | ||
1639 | |||
1640 | err = _mv88e6xxx_vtu_vid_write(ds, vid - 1); | ||
1641 | if (err) | ||
1642 | return err; | ||
1643 | |||
1644 | err = _mv88e6xxx_vtu_getnext(ds, entry); | ||
1645 | if (err) | ||
1646 | return err; | ||
1647 | |||
1648 | if (entry->vid != vid || !entry->valid) { | ||
1649 | if (!creat) | ||
1650 | return -EOPNOTSUPP; | ||
1651 | /* -ENOENT would've been more appropriate, but switchdev expects | ||
1652 | * -EOPNOTSUPP to inform bridge about an eventual software VLAN. | ||
1653 | */ | ||
1654 | |||
1655 | err = _mv88e6xxx_vtu_new(ds, vid, entry); | ||
1656 | } | ||
1657 | |||
1658 | return err; | ||
1659 | } | ||
1660 | |||
1512 | static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, | 1661 | static int mv88e6xxx_port_check_hw_vlan(struct dsa_switch *ds, int port, |
1513 | u16 vid_begin, u16 vid_end) | 1662 | u16 vid_begin, u16 vid_end) |
1514 | { | 1663 | { |
@@ -1563,16 +1712,51 @@ unlock: | |||
1563 | return err; | 1712 | return err; |
1564 | } | 1713 | } |
1565 | 1714 | ||
1715 | static const char * const mv88e6xxx_port_8021q_mode_names[] = { | ||
1716 | [PORT_CONTROL_2_8021Q_DISABLED] = "Disabled", | ||
1717 | [PORT_CONTROL_2_8021Q_FALLBACK] = "Fallback", | ||
1718 | [PORT_CONTROL_2_8021Q_CHECK] = "Check", | ||
1719 | [PORT_CONTROL_2_8021Q_SECURE] = "Secure", | ||
1720 | }; | ||
1721 | |||
1722 | int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, | ||
1723 | bool vlan_filtering) | ||
1724 | { | ||
1725 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | ||
1726 | u16 old, new = vlan_filtering ? PORT_CONTROL_2_8021Q_SECURE : | ||
1727 | PORT_CONTROL_2_8021Q_DISABLED; | ||
1728 | int ret; | ||
1729 | |||
1730 | mutex_lock(&ps->smi_mutex); | ||
1731 | |||
1732 | ret = _mv88e6xxx_reg_read(ds, REG_PORT(port), PORT_CONTROL_2); | ||
1733 | if (ret < 0) | ||
1734 | goto unlock; | ||
1735 | |||
1736 | old = ret & PORT_CONTROL_2_8021Q_MASK; | ||
1737 | |||
1738 | ret &= ~PORT_CONTROL_2_8021Q_MASK; | ||
1739 | ret |= new & PORT_CONTROL_2_8021Q_MASK; | ||
1740 | |||
1741 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), PORT_CONTROL_2, ret); | ||
1742 | if (ret < 0) | ||
1743 | goto unlock; | ||
1744 | |||
1745 | netdev_dbg(ds->ports[port], "802.1Q Mode: %s (was %s)\n", | ||
1746 | mv88e6xxx_port_8021q_mode_names[new], | ||
1747 | mv88e6xxx_port_8021q_mode_names[old]); | ||
1748 | unlock: | ||
1749 | mutex_unlock(&ps->smi_mutex); | ||
1750 | |||
1751 | return ret; | ||
1752 | } | ||
1753 | |||
1566 | int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, | 1754 | int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, |
1567 | const struct switchdev_obj_port_vlan *vlan, | 1755 | const struct switchdev_obj_port_vlan *vlan, |
1568 | struct switchdev_trans *trans) | 1756 | struct switchdev_trans *trans) |
1569 | { | 1757 | { |
1570 | int err; | 1758 | int err; |
1571 | 1759 | ||
1572 | /* We reserve a few VLANs to isolate unbridged ports */ | ||
1573 | if (vlan->vid_end >= 4000) | ||
1574 | return -EOPNOTSUPP; | ||
1575 | |||
1576 | /* If the requested port doesn't belong to the same bridge as the VLAN | 1760 | /* If the requested port doesn't belong to the same bridge as the VLAN |
1577 | * members, do not support it (yet) and fallback to software VLAN. | 1761 | * members, do not support it (yet) and fallback to software VLAN. |
1578 | */ | 1762 | */ |
@@ -1593,20 +1777,10 @@ static int _mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port, u16 vid, | |||
1593 | struct mv88e6xxx_vtu_stu_entry vlan; | 1777 | struct mv88e6xxx_vtu_stu_entry vlan; |
1594 | int err; | 1778 | int err; |
1595 | 1779 | ||
1596 | err = _mv88e6xxx_vtu_vid_write(ds, vid - 1); | 1780 | err = _mv88e6xxx_vtu_get(ds, vid, &vlan, true); |
1597 | if (err) | ||
1598 | return err; | ||
1599 | |||
1600 | err = _mv88e6xxx_vtu_getnext(ds, &vlan); | ||
1601 | if (err) | 1781 | if (err) |
1602 | return err; | 1782 | return err; |
1603 | 1783 | ||
1604 | if (vlan.vid != vid || !vlan.valid) { | ||
1605 | err = _mv88e6xxx_vlan_init(ds, vid, &vlan); | ||
1606 | if (err) | ||
1607 | return err; | ||
1608 | } | ||
1609 | |||
1610 | vlan.data[port] = untagged ? | 1784 | vlan.data[port] = untagged ? |
1611 | GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : | 1785 | GLOBAL_VTU_DATA_MEMBER_TAG_UNTAGGED : |
1612 | GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; | 1786 | GLOBAL_VTU_DATA_MEMBER_TAG_TAGGED; |
@@ -1647,16 +1821,12 @@ static int _mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, u16 vid) | |||
1647 | struct mv88e6xxx_vtu_stu_entry vlan; | 1821 | struct mv88e6xxx_vtu_stu_entry vlan; |
1648 | int i, err; | 1822 | int i, err; |
1649 | 1823 | ||
1650 | err = _mv88e6xxx_vtu_vid_write(ds, vid - 1); | 1824 | err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false); |
1651 | if (err) | 1825 | if (err) |
1652 | return err; | 1826 | return err; |
1653 | 1827 | ||
1654 | err = _mv88e6xxx_vtu_getnext(ds, &vlan); | 1828 | /* Tell switchdev if this VLAN is handled in software */ |
1655 | if (err) | 1829 | if (vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) |
1656 | return err; | ||
1657 | |||
1658 | if (vlan.vid != vid || !vlan.valid || | ||
1659 | vlan.data[port] == GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER) | ||
1660 | return -EOPNOTSUPP; | 1830 | return -EOPNOTSUPP; |
1661 | 1831 | ||
1662 | vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; | 1832 | vlan.data[port] = GLOBAL_VTU_DATA_MEMBER_TAG_NON_MEMBER; |
@@ -1684,7 +1854,6 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, | |||
1684 | const struct switchdev_obj_port_vlan *vlan) | 1854 | const struct switchdev_obj_port_vlan *vlan) |
1685 | { | 1855 | { |
1686 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | 1856 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
1687 | const u16 defpvid = 4000 + ds->index * DSA_MAX_PORTS + port; | ||
1688 | u16 pvid, vid; | 1857 | u16 pvid, vid; |
1689 | int err = 0; | 1858 | int err = 0; |
1690 | 1859 | ||
@@ -1700,8 +1869,7 @@ int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port, | |||
1700 | goto unlock; | 1869 | goto unlock; |
1701 | 1870 | ||
1702 | if (vid == pvid) { | 1871 | if (vid == pvid) { |
1703 | /* restore reserved VLAN ID */ | 1872 | err = _mv88e6xxx_port_pvid_set(ds, port, 0); |
1704 | err = _mv88e6xxx_port_pvid_set(ds, port, defpvid); | ||
1705 | if (err) | 1873 | if (err) |
1706 | goto unlock; | 1874 | goto unlock; |
1707 | } | 1875 | } |
@@ -1774,8 +1942,18 @@ static int _mv88e6xxx_port_fdb_load(struct dsa_switch *ds, int port, | |||
1774 | u8 state) | 1942 | u8 state) |
1775 | { | 1943 | { |
1776 | struct mv88e6xxx_atu_entry entry = { 0 }; | 1944 | struct mv88e6xxx_atu_entry entry = { 0 }; |
1945 | struct mv88e6xxx_vtu_stu_entry vlan; | ||
1946 | int err; | ||
1777 | 1947 | ||
1778 | entry.fid = vid; /* We use one FID per VLAN */ | 1948 | /* Null VLAN ID corresponds to the port private database */ |
1949 | if (vid == 0) | ||
1950 | err = _mv88e6xxx_port_fid_get(ds, port, &vlan.fid); | ||
1951 | else | ||
1952 | err = _mv88e6xxx_vtu_get(ds, vid, &vlan, false); | ||
1953 | if (err) | ||
1954 | return err; | ||
1955 | |||
1956 | entry.fid = vlan.fid; | ||
1779 | entry.state = state; | 1957 | entry.state = state; |
1780 | ether_addr_copy(entry.mac, addr); | 1958 | ether_addr_copy(entry.mac, addr); |
1781 | if (state != GLOBAL_ATU_DATA_STATE_UNUSED) { | 1959 | if (state != GLOBAL_ATU_DATA_STATE_UNUSED) { |
@@ -1790,10 +1968,6 @@ int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, | |||
1790 | const struct switchdev_obj_port_fdb *fdb, | 1968 | const struct switchdev_obj_port_fdb *fdb, |
1791 | struct switchdev_trans *trans) | 1969 | struct switchdev_trans *trans) |
1792 | { | 1970 | { |
1793 | /* We don't use per-port FDB */ | ||
1794 | if (fdb->vid == 0) | ||
1795 | return -EOPNOTSUPP; | ||
1796 | |||
1797 | /* We don't need any dynamic resource from the kernel (yet), | 1971 | /* We don't need any dynamic resource from the kernel (yet), |
1798 | * so skip the prepare phase. | 1972 | * so skip the prepare phase. |
1799 | */ | 1973 | */ |
@@ -1880,6 +2054,47 @@ static int _mv88e6xxx_atu_getnext(struct dsa_switch *ds, u16 fid, | |||
1880 | return 0; | 2054 | return 0; |
1881 | } | 2055 | } |
1882 | 2056 | ||
2057 | static int _mv88e6xxx_port_fdb_dump_one(struct dsa_switch *ds, u16 fid, u16 vid, | ||
2058 | int port, | ||
2059 | struct switchdev_obj_port_fdb *fdb, | ||
2060 | int (*cb)(struct switchdev_obj *obj)) | ||
2061 | { | ||
2062 | struct mv88e6xxx_atu_entry addr = { | ||
2063 | .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, | ||
2064 | }; | ||
2065 | int err; | ||
2066 | |||
2067 | err = _mv88e6xxx_atu_mac_write(ds, addr.mac); | ||
2068 | if (err) | ||
2069 | return err; | ||
2070 | |||
2071 | do { | ||
2072 | err = _mv88e6xxx_atu_getnext(ds, fid, &addr); | ||
2073 | if (err) | ||
2074 | break; | ||
2075 | |||
2076 | if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED) | ||
2077 | break; | ||
2078 | |||
2079 | if (!addr.trunk && addr.portv_trunkid & BIT(port)) { | ||
2080 | bool is_static = addr.state == | ||
2081 | (is_multicast_ether_addr(addr.mac) ? | ||
2082 | GLOBAL_ATU_DATA_STATE_MC_STATIC : | ||
2083 | GLOBAL_ATU_DATA_STATE_UC_STATIC); | ||
2084 | |||
2085 | fdb->vid = vid; | ||
2086 | ether_addr_copy(fdb->addr, addr.mac); | ||
2087 | fdb->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; | ||
2088 | |||
2089 | err = cb(&fdb->obj); | ||
2090 | if (err) | ||
2091 | break; | ||
2092 | } | ||
2093 | } while (!is_broadcast_ether_addr(addr.mac)); | ||
2094 | |||
2095 | return err; | ||
2096 | } | ||
2097 | |||
1883 | int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, | 2098 | int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, |
1884 | struct switchdev_obj_port_fdb *fdb, | 2099 | struct switchdev_obj_port_fdb *fdb, |
1885 | int (*cb)(struct switchdev_obj *obj)) | 2100 | int (*cb)(struct switchdev_obj *obj)) |
@@ -1888,55 +2103,37 @@ int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, | |||
1888 | struct mv88e6xxx_vtu_stu_entry vlan = { | 2103 | struct mv88e6xxx_vtu_stu_entry vlan = { |
1889 | .vid = GLOBAL_VTU_VID_MASK, /* all ones */ | 2104 | .vid = GLOBAL_VTU_VID_MASK, /* all ones */ |
1890 | }; | 2105 | }; |
2106 | u16 fid; | ||
1891 | int err; | 2107 | int err; |
1892 | 2108 | ||
1893 | mutex_lock(&ps->smi_mutex); | 2109 | mutex_lock(&ps->smi_mutex); |
1894 | 2110 | ||
2111 | /* Dump port's default Filtering Information Database (VLAN ID 0) */ | ||
2112 | err = _mv88e6xxx_port_fid_get(ds, port, &fid); | ||
2113 | if (err) | ||
2114 | goto unlock; | ||
2115 | |||
2116 | err = _mv88e6xxx_port_fdb_dump_one(ds, fid, 0, port, fdb, cb); | ||
2117 | if (err) | ||
2118 | goto unlock; | ||
2119 | |||
2120 | /* Dump VLANs' Filtering Information Databases */ | ||
1895 | err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid); | 2121 | err = _mv88e6xxx_vtu_vid_write(ds, vlan.vid); |
1896 | if (err) | 2122 | if (err) |
1897 | goto unlock; | 2123 | goto unlock; |
1898 | 2124 | ||
1899 | do { | 2125 | do { |
1900 | struct mv88e6xxx_atu_entry addr = { | ||
1901 | .mac = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }, | ||
1902 | }; | ||
1903 | |||
1904 | err = _mv88e6xxx_vtu_getnext(ds, &vlan); | 2126 | err = _mv88e6xxx_vtu_getnext(ds, &vlan); |
1905 | if (err) | 2127 | if (err) |
1906 | goto unlock; | 2128 | break; |
1907 | 2129 | ||
1908 | if (!vlan.valid) | 2130 | if (!vlan.valid) |
1909 | break; | 2131 | break; |
1910 | 2132 | ||
1911 | err = _mv88e6xxx_atu_mac_write(ds, addr.mac); | 2133 | err = _mv88e6xxx_port_fdb_dump_one(ds, vlan.fid, vlan.vid, port, |
2134 | fdb, cb); | ||
1912 | if (err) | 2135 | if (err) |
1913 | goto unlock; | 2136 | break; |
1914 | |||
1915 | do { | ||
1916 | err = _mv88e6xxx_atu_getnext(ds, vlan.fid, &addr); | ||
1917 | if (err) | ||
1918 | goto unlock; | ||
1919 | |||
1920 | if (addr.state == GLOBAL_ATU_DATA_STATE_UNUSED) | ||
1921 | break; | ||
1922 | |||
1923 | if (!addr.trunk && addr.portv_trunkid & BIT(port)) { | ||
1924 | bool is_static = addr.state == | ||
1925 | (is_multicast_ether_addr(addr.mac) ? | ||
1926 | GLOBAL_ATU_DATA_STATE_MC_STATIC : | ||
1927 | GLOBAL_ATU_DATA_STATE_UC_STATIC); | ||
1928 | |||
1929 | fdb->vid = vlan.vid; | ||
1930 | ether_addr_copy(fdb->addr, addr.mac); | ||
1931 | fdb->ndm_state = is_static ? NUD_NOARP : | ||
1932 | NUD_REACHABLE; | ||
1933 | |||
1934 | err = cb(&fdb->obj); | ||
1935 | if (err) | ||
1936 | goto unlock; | ||
1937 | } | ||
1938 | } while (!is_broadcast_ether_addr(addr.mac)); | ||
1939 | |||
1940 | } while (vlan.vid < GLOBAL_VTU_VID_MASK); | 2137 | } while (vlan.vid < GLOBAL_VTU_VID_MASK); |
1941 | 2138 | ||
1942 | unlock: | 2139 | unlock: |
@@ -1949,32 +2146,76 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, | |||
1949 | struct net_device *bridge) | 2146 | struct net_device *bridge) |
1950 | { | 2147 | { |
1951 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | 2148 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
2149 | u16 fid; | ||
2150 | int i, err; | ||
2151 | |||
2152 | mutex_lock(&ps->smi_mutex); | ||
2153 | |||
2154 | /* Get or create the bridge FID and assign it to the port */ | ||
2155 | for (i = 0; i < ps->num_ports; ++i) | ||
2156 | if (ps->ports[i].bridge_dev == bridge) | ||
2157 | break; | ||
2158 | |||
2159 | if (i < ps->num_ports) | ||
2160 | err = _mv88e6xxx_port_fid_get(ds, i, &fid); | ||
2161 | else | ||
2162 | err = _mv88e6xxx_fid_new(ds, &fid); | ||
2163 | if (err) | ||
2164 | goto unlock; | ||
2165 | |||
2166 | err = _mv88e6xxx_port_fid_set(ds, port, fid); | ||
2167 | if (err) | ||
2168 | goto unlock; | ||
1952 | 2169 | ||
2170 | /* Assign the bridge and remap each port's VLANTable */ | ||
1953 | ps->ports[port].bridge_dev = bridge; | 2171 | ps->ports[port].bridge_dev = bridge; |
1954 | 2172 | ||
1955 | return 0; | 2173 | for (i = 0; i < ps->num_ports; ++i) { |
2174 | if (ps->ports[i].bridge_dev == bridge) { | ||
2175 | err = _mv88e6xxx_port_based_vlan_map(ds, i); | ||
2176 | if (err) | ||
2177 | break; | ||
2178 | } | ||
2179 | } | ||
2180 | |||
2181 | unlock: | ||
2182 | mutex_unlock(&ps->smi_mutex); | ||
2183 | |||
2184 | return err; | ||
1956 | } | 2185 | } |
1957 | 2186 | ||
1958 | int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) | 2187 | int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port) |
1959 | { | 2188 | { |
1960 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | 2189 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); |
2190 | struct net_device *bridge = ps->ports[port].bridge_dev; | ||
2191 | u16 fid; | ||
2192 | int i, err; | ||
1961 | 2193 | ||
1962 | ps->ports[port].bridge_dev = NULL; | 2194 | mutex_lock(&ps->smi_mutex); |
1963 | 2195 | ||
1964 | return 0; | 2196 | /* Give the port a fresh Filtering Information Database */ |
1965 | } | 2197 | err = _mv88e6xxx_fid_new(ds, &fid); |
2198 | if (err) | ||
2199 | goto unlock; | ||
1966 | 2200 | ||
1967 | static int mv88e6xxx_setup_port_default_vlan(struct dsa_switch *ds, int port) | 2201 | err = _mv88e6xxx_port_fid_set(ds, port, fid); |
1968 | { | 2202 | if (err) |
1969 | struct mv88e6xxx_priv_state *ps = ds_to_priv(ds); | 2203 | goto unlock; |
1970 | const u16 pvid = 4000 + ds->index * DSA_MAX_PORTS + port; | ||
1971 | int err; | ||
1972 | 2204 | ||
1973 | mutex_lock(&ps->smi_mutex); | 2205 | /* Unassign the bridge and remap each port's VLANTable */ |
1974 | err = _mv88e6xxx_port_vlan_add(ds, port, pvid, true); | 2206 | ps->ports[port].bridge_dev = NULL; |
1975 | if (!err) | 2207 | |
1976 | err = _mv88e6xxx_port_pvid_set(ds, port, pvid); | 2208 | for (i = 0; i < ps->num_ports; ++i) { |
2209 | if (i == port || ps->ports[i].bridge_dev == bridge) { | ||
2210 | err = _mv88e6xxx_port_based_vlan_map(ds, i); | ||
2211 | if (err) | ||
2212 | break; | ||
2213 | } | ||
2214 | } | ||
2215 | |||
2216 | unlock: | ||
1977 | mutex_unlock(&ps->smi_mutex); | 2217 | mutex_unlock(&ps->smi_mutex); |
2218 | |||
1978 | return err; | 2219 | return err; |
1979 | } | 2220 | } |
1980 | 2221 | ||
@@ -2098,7 +2339,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) | |||
2098 | } | 2339 | } |
2099 | 2340 | ||
2100 | /* Port Control 2: don't force a good FCS, set the maximum frame size to | 2341 | /* Port Control 2: don't force a good FCS, set the maximum frame size to |
2101 | * 10240 bytes, enable secure 802.1q tags, don't discard tagged or | 2342 | * 10240 bytes, disable 802.1q tags checking, don't discard tagged or |
2102 | * untagged frames on this port, do a destination address lookup on all | 2343 | * untagged frames on this port, do a destination address lookup on all |
2103 | * received packets as usual, disable ARP mirroring and don't send a | 2344 | * received packets as usual, disable ARP mirroring and don't send a |
2104 | * copy of all transmitted/received frames on this port to the CPU. | 2345 | * copy of all transmitted/received frames on this port to the CPU. |
@@ -2123,7 +2364,7 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) | |||
2123 | reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; | 2364 | reg |= PORT_CONTROL_2_FORWARD_UNKNOWN; |
2124 | } | 2365 | } |
2125 | 2366 | ||
2126 | reg |= PORT_CONTROL_2_8021Q_SECURE; | 2367 | reg |= PORT_CONTROL_2_8021Q_DISABLED; |
2127 | 2368 | ||
2128 | if (reg) { | 2369 | if (reg) { |
2129 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), | 2370 | ret = _mv88e6xxx_reg_write(ds, REG_PORT(port), |
@@ -2220,12 +2461,15 @@ static int mv88e6xxx_setup_port(struct dsa_switch *ds, int port) | |||
2220 | if (ret) | 2461 | if (ret) |
2221 | goto abort; | 2462 | goto abort; |
2222 | 2463 | ||
2223 | /* Port based VLAN map: do not give each port its own address | 2464 | /* Port based VLAN map: give each port its own address |
2224 | * database, and allow every port to egress frames on all other ports. | 2465 | * database, and allow bidirectional communication between the |
2466 | * CPU and DSA port(s), and the other ports. | ||
2225 | */ | 2467 | */ |
2226 | reg = BIT(ps->num_ports) - 1; /* all ports */ | 2468 | ret = _mv88e6xxx_port_fid_set(ds, port, port + 1); |
2227 | reg &= ~BIT(port); /* except itself */ | 2469 | if (ret) |
2228 | ret = _mv88e6xxx_port_vlan_map_set(ds, port, reg); | 2470 | goto abort; |
2471 | |||
2472 | ret = _mv88e6xxx_port_based_vlan_map(ds, port); | ||
2229 | if (ret) | 2473 | if (ret) |
2230 | goto abort; | 2474 | goto abort; |
2231 | 2475 | ||
@@ -2249,13 +2493,6 @@ int mv88e6xxx_setup_ports(struct dsa_switch *ds) | |||
2249 | ret = mv88e6xxx_setup_port(ds, i); | 2493 | ret = mv88e6xxx_setup_port(ds, i); |
2250 | if (ret < 0) | 2494 | if (ret < 0) |
2251 | return ret; | 2495 | return ret; |
2252 | |||
2253 | if (dsa_is_cpu_port(ds, i) || dsa_is_dsa_port(ds, i)) | ||
2254 | continue; | ||
2255 | |||
2256 | ret = mv88e6xxx_setup_port_default_vlan(ds, i); | ||
2257 | if (ret < 0) | ||
2258 | return ret; | ||
2259 | } | 2496 | } |
2260 | return 0; | 2497 | return 0; |
2261 | } | 2498 | } |
diff --git a/drivers/net/dsa/mv88e6xxx.h b/drivers/net/dsa/mv88e6xxx.h index 6a30bda63a2f..d7b088dd8e16 100644 --- a/drivers/net/dsa/mv88e6xxx.h +++ b/drivers/net/dsa/mv88e6xxx.h | |||
@@ -133,7 +133,9 @@ | |||
133 | #define PORT_CONTROL_STATE_LEARNING 0x02 | 133 | #define PORT_CONTROL_STATE_LEARNING 0x02 |
134 | #define PORT_CONTROL_STATE_FORWARDING 0x03 | 134 | #define PORT_CONTROL_STATE_FORWARDING 0x03 |
135 | #define PORT_CONTROL_1 0x05 | 135 | #define PORT_CONTROL_1 0x05 |
136 | #define PORT_CONTROL_1_FID_11_4_MASK (0xff << 0) | ||
136 | #define PORT_BASE_VLAN 0x06 | 137 | #define PORT_BASE_VLAN 0x06 |
138 | #define PORT_BASE_VLAN_FID_3_0_MASK (0xf << 12) | ||
137 | #define PORT_DEFAULT_VLAN 0x07 | 139 | #define PORT_DEFAULT_VLAN 0x07 |
138 | #define PORT_DEFAULT_VLAN_MASK 0xfff | 140 | #define PORT_DEFAULT_VLAN_MASK 0xfff |
139 | #define PORT_CONTROL_2 0x08 | 141 | #define PORT_CONTROL_2 0x08 |
@@ -355,6 +357,8 @@ | |||
355 | #define GLOBAL2_QOS_WEIGHT 0x1c | 357 | #define GLOBAL2_QOS_WEIGHT 0x1c |
356 | #define GLOBAL2_MISC 0x1d | 358 | #define GLOBAL2_MISC 0x1d |
357 | 359 | ||
360 | #define MV88E6XXX_N_FID 4096 | ||
361 | |||
358 | struct mv88e6xxx_switch_id { | 362 | struct mv88e6xxx_switch_id { |
359 | u16 id; | 363 | u16 id; |
360 | char *name; | 364 | char *name; |
@@ -486,6 +490,8 @@ int mv88e6xxx_port_bridge_join(struct dsa_switch *ds, int port, | |||
486 | struct net_device *bridge); | 490 | struct net_device *bridge); |
487 | int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port); | 491 | int mv88e6xxx_port_bridge_leave(struct dsa_switch *ds, int port); |
488 | int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); | 492 | int mv88e6xxx_port_stp_update(struct dsa_switch *ds, int port, u8 state); |
493 | int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port, | ||
494 | bool vlan_filtering); | ||
489 | int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, | 495 | int mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port, |
490 | const struct switchdev_obj_port_vlan *vlan, | 496 | const struct switchdev_obj_port_vlan *vlan, |
491 | struct switchdev_trans *trans); | 497 | struct switchdev_trans *trans); |
diff --git a/include/net/dsa.h b/include/net/dsa.h index 3dd54867174a..26c0a3fa009a 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h | |||
@@ -305,6 +305,8 @@ struct dsa_switch_driver { | |||
305 | /* | 305 | /* |
306 | * VLAN support | 306 | * VLAN support |
307 | */ | 307 | */ |
308 | int (*port_vlan_filtering)(struct dsa_switch *ds, int port, | ||
309 | bool vlan_filtering); | ||
308 | int (*port_vlan_prepare)(struct dsa_switch *ds, int port, | 310 | int (*port_vlan_prepare)(struct dsa_switch *ds, int port, |
309 | const struct switchdev_obj_port_vlan *vlan, | 311 | const struct switchdev_obj_port_vlan *vlan, |
310 | struct switchdev_trans *trans); | 312 | struct switchdev_trans *trans); |
diff --git a/net/dsa/slave.c b/net/dsa/slave.c index cde29239b60d..27bf03d11670 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c | |||
@@ -317,6 +317,24 @@ static int dsa_slave_stp_update(struct net_device *dev, u8 state) | |||
317 | return ret; | 317 | return ret; |
318 | } | 318 | } |
319 | 319 | ||
320 | static int dsa_slave_vlan_filtering(struct net_device *dev, | ||
321 | const struct switchdev_attr *attr, | ||
322 | struct switchdev_trans *trans) | ||
323 | { | ||
324 | struct dsa_slave_priv *p = netdev_priv(dev); | ||
325 | struct dsa_switch *ds = p->parent; | ||
326 | |||
327 | /* bridge skips -EOPNOTSUPP, so skip the prepare phase */ | ||
328 | if (switchdev_trans_ph_prepare(trans)) | ||
329 | return 0; | ||
330 | |||
331 | if (ds->drv->port_vlan_filtering) | ||
332 | return ds->drv->port_vlan_filtering(ds, p->port, | ||
333 | attr->u.vlan_filtering); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
320 | static int dsa_slave_port_attr_set(struct net_device *dev, | 338 | static int dsa_slave_port_attr_set(struct net_device *dev, |
321 | const struct switchdev_attr *attr, | 339 | const struct switchdev_attr *attr, |
322 | struct switchdev_trans *trans) | 340 | struct switchdev_trans *trans) |
@@ -333,6 +351,9 @@ static int dsa_slave_port_attr_set(struct net_device *dev, | |||
333 | ret = ds->drv->port_stp_update(ds, p->port, | 351 | ret = ds->drv->port_stp_update(ds, p->port, |
334 | attr->u.stp_state); | 352 | attr->u.stp_state); |
335 | break; | 353 | break; |
354 | case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING: | ||
355 | ret = dsa_slave_vlan_filtering(dev, attr, trans); | ||
356 | break; | ||
336 | default: | 357 | default: |
337 | ret = -EOPNOTSUPP; | 358 | ret = -EOPNOTSUPP; |
338 | break; | 359 | break; |