Message ID | 20240325204344.2298241-8-paweldembicki@gmail.com (mailing list archive) |
---|---|
State | Superseded |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | net: dsa: vsc73xx: Make vsc73xx usable | expand |
On Mon, Mar 25, 2024 at 09:43:32PM +0100, Pawel Dembicki wrote: > This patch implements VLAN filtering for the vsc73xx driver. > > After starting VLAN filtering, the switch is reconfigured from QinQ to > a simple VLAN aware mode. This is required because VSC73XX chips do not > support inner VLAN tag filtering. > > Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> ... > +static int vsc73xx_port_vlan_add(struct dsa_switch *ds, int port, > + const struct switchdev_obj_port_vlan *vlan, > + struct netlink_ext_ack *extack) > +{ > + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; > + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; > + struct dsa_port *dp = dsa_to_port(ds, port); > + enum vsc73xx_port_vlan_conf port_vlan_conf; > + struct vsc73xx_bridge_vlan *vsc73xx_vlan; > + struct vsc73xx_vlan_summary summary; > + struct vsc73xx *vsc = ds->priv; > + bool operate_on_storage; > + int ret; > + u16 vid; > + > + /* Be sure to deny alterations to the configuration done by tag_8021q. > + */ > + if (vid_is_dsa_8021q(vlan->vid)) { > + NL_SET_ERR_MSG_MOD(extack, > + "Range 3072-4095 reserved for dsa_8021q operation"); > + return -EBUSY; > + } > + > + /* The processed vlan->vid is excluded from the search because the VLAN > + * can be re-added with a different set of flags, so it's easiest to > + * ignore its old flags from the VLAN database software copy. > + */ > + vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid); > + > + /* VSC73XX allow only three untagged states: none, one or all */ > + if ((untagged && summary.num_tagged > 0 && summary.num_untagged > 0) || > + (!untagged && summary.num_untagged > 1)) { > + NL_SET_ERR_MSG_MOD(extack, > + "Port can have only none, one or all untagged vlan"); > + return -EBUSY; > + } > + > + vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); > + > + if (!vsc73xx_vlan) { > + vsc73xx_vlan = kzalloc(sizeof(*vsc73xx_vlan), GFP_KERNEL); > + if (!vsc73xx_vlan) > + return -ENOMEM; > + > + vsc73xx_vlan->vid = vlan->vid; > + vsc73xx_vlan->portmask = 0; > + vsc73xx_vlan->untagged = 0; > + > + INIT_LIST_HEAD(&vsc73xx_vlan->list); > + list_add_tail(&vsc73xx_vlan->list, &vsc->vlans); > + } > + > + vsc73xx_vlan->portmask |= BIT(port); > + > + if (untagged) > + vsc73xx_vlan->untagged |= BIT(port); > + else > + vsc73xx_vlan->untagged &= ~BIT(port); > + > + /* CPU port must be always tagged because port separation is based on > + * tag_8021q. > + */ > + if (port == CPU_PORT) > + goto update_vlan_table; > + > + operate_on_storage = vsc73xx_tag_8021q_active(dp); > + > + if (pvid) > + ret = vsc73xx_vlan_set_pvid(vsc, port, vlan->vid, > + operate_on_storage, false); > + else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && > + vid == vlan->vid) > + ret = vsc73xx_vlan_clear_pvid(vsc, port, operate_on_storage, > + false); Hi Pawel, some minor feedback from my side. If neither of the above conditions immediately above is met, then ret will be used ininitialised on the line below. Flagged by clang-17 W=1 build, and Smatch. > + if (ret) > + goto err; ...
Hi Pawel, kernel test robot noticed the following build warnings: [auto build test WARNING on net-next/main] url: https://github.com/intel-lab-lkp/linux/commits/Pawel-Dembicki/net-dsa-vsc73xx-use-read_poll_timeout-instead-delay-loop/20240326-053458 base: net-next/main patch link: https://lore.kernel.org/r/20240325204344.2298241-8-paweldembicki%40gmail.com patch subject: [PATCH net-next v7 07/16] net: dsa: vsc73xx: Add vlan filtering config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20240328/202403280937.NcxkkP5s-lkp@intel.com/config) compiler: clang version 19.0.0git (https://github.com/llvm/llvm-project 23de3862dce582ce91c1aa914467d982cb1a73b4) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240328/202403280937.NcxkkP5s-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202403280937.NcxkkP5s-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from drivers/net/dsa/vitesse-vsc73xx-core.c:20: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:547:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 547 | val = __raw_readb(PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:560:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 560 | val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu' 37 | #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) | ^ In file included from drivers/net/dsa/vitesse-vsc73xx-core.c:20: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:573:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 573 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) | ^ In file included from drivers/net/dsa/vitesse-vsc73xx-core.c:20: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:13: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:584:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 584 | __raw_writeb(value, PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:594:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 594 | __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:604:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 604 | __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ In file included from drivers/net/dsa/vitesse-vsc73xx-core.c:22: In file included from include/linux/of_mdio.h:12: In file included from include/linux/phy.h:16: In file included from include/linux/ethtool.h:18: In file included from include/linux/if_ether.h:19: In file included from include/linux/skbuff.h:17: In file included from include/linux/bvec.h:10: In file included from include/linux/highmem.h:10: In file included from include/linux/mm.h:2208: include/linux/vmstat.h:522:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion] 522 | return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_" | ~~~~~~~~~~~ ^ ~~~ >> drivers/net/dsa/vitesse-vsc73xx-core.c:1451:11: warning: variable 'ret' is used uninitialized whenever 'if' condition is false [-Wsometimes-uninitialized] 1451 | else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1452 | vid == vlan->vid) | ~~~~~~~~~~~~~~~~ drivers/net/dsa/vitesse-vsc73xx-core.c:1455:6: note: uninitialized use occurs here 1455 | if (ret) | ^~~ drivers/net/dsa/vitesse-vsc73xx-core.c:1451:7: note: remove the 'if' if its condition is always true 1451 | else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1452 | vid == vlan->vid) | ~~~~~~~~~~~~~~~~~ 1453 | ret = vsc73xx_vlan_clear_pvid(vsc, port, operate_on_storage, >> drivers/net/dsa/vitesse-vsc73xx-core.c:1451:11: warning: variable 'ret' is used uninitialized whenever '&&' condition is false [-Wsometimes-uninitialized] 1451 | else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/vitesse-vsc73xx-core.c:1455:6: note: uninitialized use occurs here 1455 | if (ret) | ^~~ drivers/net/dsa/vitesse-vsc73xx-core.c:1451:11: note: remove the '&&' if its condition is always true 1451 | else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/net/dsa/vitesse-vsc73xx-core.c:1393:9: note: initialize the variable 'ret' to silence this warning 1393 | int ret; | ^ | = 0 9 warnings generated. vim +1451 drivers/net/dsa/vitesse-vsc73xx-core.c 1380 1381 static int vsc73xx_port_vlan_add(struct dsa_switch *ds, int port, 1382 const struct switchdev_obj_port_vlan *vlan, 1383 struct netlink_ext_ack *extack) 1384 { 1385 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; 1386 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; 1387 struct dsa_port *dp = dsa_to_port(ds, port); 1388 enum vsc73xx_port_vlan_conf port_vlan_conf; 1389 struct vsc73xx_bridge_vlan *vsc73xx_vlan; 1390 struct vsc73xx_vlan_summary summary; 1391 struct vsc73xx *vsc = ds->priv; 1392 bool operate_on_storage; 1393 int ret; 1394 u16 vid; 1395 1396 /* Be sure to deny alterations to the configuration done by tag_8021q. 1397 */ 1398 if (vid_is_dsa_8021q(vlan->vid)) { 1399 NL_SET_ERR_MSG_MOD(extack, 1400 "Range 3072-4095 reserved for dsa_8021q operation"); 1401 return -EBUSY; 1402 } 1403 1404 /* The processed vlan->vid is excluded from the search because the VLAN 1405 * can be re-added with a different set of flags, so it's easiest to 1406 * ignore its old flags from the VLAN database software copy. 1407 */ 1408 vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid); 1409 1410 /* VSC73XX allow only three untagged states: none, one or all */ 1411 if ((untagged && summary.num_tagged > 0 && summary.num_untagged > 0) || 1412 (!untagged && summary.num_untagged > 1)) { 1413 NL_SET_ERR_MSG_MOD(extack, 1414 "Port can have only none, one or all untagged vlan"); 1415 return -EBUSY; 1416 } 1417 1418 vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); 1419 1420 if (!vsc73xx_vlan) { 1421 vsc73xx_vlan = kzalloc(sizeof(*vsc73xx_vlan), GFP_KERNEL); 1422 if (!vsc73xx_vlan) 1423 return -ENOMEM; 1424 1425 vsc73xx_vlan->vid = vlan->vid; 1426 vsc73xx_vlan->portmask = 0; 1427 vsc73xx_vlan->untagged = 0; 1428 1429 INIT_LIST_HEAD(&vsc73xx_vlan->list); 1430 list_add_tail(&vsc73xx_vlan->list, &vsc->vlans); 1431 } 1432 1433 vsc73xx_vlan->portmask |= BIT(port); 1434 1435 if (untagged) 1436 vsc73xx_vlan->untagged |= BIT(port); 1437 else 1438 vsc73xx_vlan->untagged &= ~BIT(port); 1439 1440 /* CPU port must be always tagged because port separation is based on 1441 * tag_8021q. 1442 */ 1443 if (port == CPU_PORT) 1444 goto update_vlan_table; 1445 1446 operate_on_storage = vsc73xx_tag_8021q_active(dp); 1447 1448 if (pvid) 1449 ret = vsc73xx_vlan_set_pvid(vsc, port, vlan->vid, 1450 operate_on_storage, false); > 1451 else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && 1452 vid == vlan->vid) 1453 ret = vsc73xx_vlan_clear_pvid(vsc, port, operate_on_storage, 1454 false); 1455 if (ret) 1456 goto err; 1457 1458 if (operate_on_storage) 1459 goto update_vlan_table; 1460 1461 port_vlan_conf = VSC73XX_VLAN_FILTER; 1462 1463 if (summary.num_tagged == 0 && untagged) 1464 port_vlan_conf = VSC73XX_VLAN_FILTER_UNTAG_ALL; 1465 vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf); 1466 1467 if (port_vlan_conf == VSC73XX_VLAN_FILTER_UNTAG_ALL) 1468 goto update_vlan_table; 1469 1470 if (untagged) { 1471 ret = vsc73xx_vlan_set_untagged_hw(vsc, port, vlan->vid); 1472 } else if (summary.num_untagged == 1) { 1473 vid = vsc73xx_find_first_vlan_untagged(vsc, port); 1474 ret = vsc73xx_vlan_set_untagged_hw(vsc, port, vid); 1475 } 1476 if (ret) 1477 goto err; 1478 1479 update_vlan_table: 1480 ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, true); 1481 if (!ret) 1482 return 0; 1483 err: 1484 list_del(&vsc73xx_vlan->list); 1485 kfree(vsc73xx_vlan); 1486 return ret; 1487 } 1488
Hi Pawel, kernel test robot noticed the following build warnings: url: https://github.com/intel-lab-lkp/linux/commits/Pawel-Dembicki/net-dsa-vsc73xx-use-read_poll_timeout-instead-delay-loop/20240326-053458 base: net-next/main patch link: https://lore.kernel.org/r/20240325204344.2298241-8-paweldembicki%40gmail.com patch subject: [PATCH net-next v7 07/16] net: dsa: vsc73xx: Add vlan filtering config: i386-randconfig-141-20240402 (https://download.01.org/0day-ci/archive/20240403/202404030102.Tv5bGJdv-lkp@intel.com/config) compiler: gcc-13 (Ubuntu 13.2.0-4ubuntu3) 13.2.0 If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org> | Closes: https://lore.kernel.org/r/202404030102.Tv5bGJdv-lkp@intel.com/ smatch warnings: drivers/net/dsa/vitesse-vsc73xx-core.c:1455 vsc73xx_port_vlan_add() error: uninitialized symbol 'ret'. vim +/ret +1455 drivers/net/dsa/vitesse-vsc73xx-core.c cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1381 static int vsc73xx_port_vlan_add(struct dsa_switch *ds, int port, cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1382 const struct switchdev_obj_port_vlan *vlan, cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1383 struct netlink_ext_ack *extack) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1384 { cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1385 bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1386 bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1387 struct dsa_port *dp = dsa_to_port(ds, port); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1388 enum vsc73xx_port_vlan_conf port_vlan_conf; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1389 struct vsc73xx_bridge_vlan *vsc73xx_vlan; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1390 struct vsc73xx_vlan_summary summary; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1391 struct vsc73xx *vsc = ds->priv; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1392 bool operate_on_storage; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1393 int ret; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1394 u16 vid; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1395 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1396 /* Be sure to deny alterations to the configuration done by tag_8021q. cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1397 */ cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1398 if (vid_is_dsa_8021q(vlan->vid)) { cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1399 NL_SET_ERR_MSG_MOD(extack, cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1400 "Range 3072-4095 reserved for dsa_8021q operation"); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1401 return -EBUSY; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1402 } cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1403 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1404 /* The processed vlan->vid is excluded from the search because the VLAN cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1405 * can be re-added with a different set of flags, so it's easiest to cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1406 * ignore its old flags from the VLAN database software copy. cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1407 */ cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1408 vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1409 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1410 /* VSC73XX allow only three untagged states: none, one or all */ cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1411 if ((untagged && summary.num_tagged > 0 && summary.num_untagged > 0) || cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1412 (!untagged && summary.num_untagged > 1)) { cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1413 NL_SET_ERR_MSG_MOD(extack, cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1414 "Port can have only none, one or all untagged vlan"); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1415 return -EBUSY; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1416 } cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1417 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1418 vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1419 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1420 if (!vsc73xx_vlan) { cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1421 vsc73xx_vlan = kzalloc(sizeof(*vsc73xx_vlan), GFP_KERNEL); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1422 if (!vsc73xx_vlan) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1423 return -ENOMEM; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1424 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1425 vsc73xx_vlan->vid = vlan->vid; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1426 vsc73xx_vlan->portmask = 0; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1427 vsc73xx_vlan->untagged = 0; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1428 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1429 INIT_LIST_HEAD(&vsc73xx_vlan->list); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1430 list_add_tail(&vsc73xx_vlan->list, &vsc->vlans); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1431 } cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1432 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1433 vsc73xx_vlan->portmask |= BIT(port); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1434 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1435 if (untagged) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1436 vsc73xx_vlan->untagged |= BIT(port); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1437 else cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1438 vsc73xx_vlan->untagged &= ~BIT(port); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1439 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1440 /* CPU port must be always tagged because port separation is based on cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1441 * tag_8021q. cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1442 */ cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1443 if (port == CPU_PORT) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1444 goto update_vlan_table; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1445 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1446 operate_on_storage = vsc73xx_tag_8021q_active(dp); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1447 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1448 if (pvid) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1449 ret = vsc73xx_vlan_set_pvid(vsc, port, vlan->vid, cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1450 operate_on_storage, false); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1451 else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1452 vid == vlan->vid) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1453 ret = vsc73xx_vlan_clear_pvid(vsc, port, operate_on_storage, cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1454 false); "ret" not initialized on else path cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 @1455 if (ret) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1456 goto err; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1457 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1458 if (operate_on_storage) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1459 goto update_vlan_table; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1460 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1461 port_vlan_conf = VSC73XX_VLAN_FILTER; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1462 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1463 if (summary.num_tagged == 0 && untagged) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1464 port_vlan_conf = VSC73XX_VLAN_FILTER_UNTAG_ALL; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1465 vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1466 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1467 if (port_vlan_conf == VSC73XX_VLAN_FILTER_UNTAG_ALL) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1468 goto update_vlan_table; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1469 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1470 if (untagged) { cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1471 ret = vsc73xx_vlan_set_untagged_hw(vsc, port, vlan->vid); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1472 } else if (summary.num_untagged == 1) { cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1473 vid = vsc73xx_find_first_vlan_untagged(vsc, port); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1474 ret = vsc73xx_vlan_set_untagged_hw(vsc, port, vid); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1475 } cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1476 if (ret) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1477 goto err; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1478 cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1479 update_vlan_table: cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1480 ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, true); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1481 if (!ret) cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1482 return 0; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1483 err: cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1484 list_del(&vsc73xx_vlan->list); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1485 kfree(vsc73xx_vlan); cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1486 return ret; cb0eaf3f8563b0 Pawel Dembicki 2024-03-25 1487 }
diff --git a/drivers/net/dsa/vitesse-vsc73xx-core.c b/drivers/net/dsa/vitesse-vsc73xx-core.c index 07f982cf62dd..833e4e50e737 100644 --- a/drivers/net/dsa/vitesse-vsc73xx-core.c +++ b/drivers/net/dsa/vitesse-vsc73xx-core.c @@ -22,9 +22,11 @@ #include <linux/of_mdio.h> #include <linux/bitops.h> #include <linux/if_bridge.h> +#include <linux/if_vlan.h> #include <linux/etherdevice.h> #include <linux/gpio/consumer.h> #include <linux/gpio/driver.h> +#include <linux/dsa/8021q.h> #include <linux/random.h> #include <net/dsa.h> @@ -62,6 +64,8 @@ #define VSC73XX_CAT_DROP 0x6e #define VSC73XX_CAT_PR_MISC_L2 0x6f #define VSC73XX_CAT_PR_USR_PRIO 0x75 +#define VSC73XX_CAT_VLAN_MISC 0x79 +#define VSC73XX_CAT_PORT_VLAN 0x7a #define VSC73XX_Q_MISC_CONF 0xdf /* MAC_CFG register bits */ @@ -122,6 +126,17 @@ #define VSC73XX_ADVPORTM_IO_LOOPBACK BIT(1) #define VSC73XX_ADVPORTM_HOST_LOOPBACK BIT(0) +/* TXUPDCFG transmit modify setup bits */ +#define VSC73XX_TXUPDCFG_DSCP_REWR_MODE GENMASK(20, 19) +#define VSC73XX_TXUPDCFG_DSCP_REWR_ENA BIT(18) +#define VSC73XX_TXUPDCFG_TX_INT_TO_USRPRIO_ENA BIT(17) +#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID GENMASK(15, 4) +#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA BIT(3) +#define VSC73XX_TXUPDCFG_TX_UPDATE_CRC_CPU_ENA BIT(1) +#define VSC73XX_TXUPDCFG_TX_INSERT_TAG BIT(0) + +#define VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_SHIFT 4 + /* CAT_DROP categorizer frame dropping register bits */ #define VSC73XX_CAT_DROP_DROP_MC_SMAC_ENA BIT(6) #define VSC73XX_CAT_DROP_FWD_CTRL_ENA BIT(4) @@ -135,6 +150,15 @@ #define VSC73XX_Q_MISC_CONF_EARLY_TX_512 (1 << 1) #define VSC73XX_Q_MISC_CONF_MAC_PAUSE_MODE BIT(0) +/* CAT_VLAN_MISC categorizer VLAN miscellaneous bits */ +#define VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA BIT(8) +#define VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA BIT(7) + +/* CAT_PORT_VLAN categorizer port VLAN */ +#define VSC73XX_CAT_PORT_VLAN_VLAN_CFI BIT(15) +#define VSC73XX_CAT_PORT_VLAN_VLAN_USR_PRIO GENMASK(14, 12) +#define VSC73XX_CAT_PORT_VLAN_VLAN_VID GENMASK(11, 0) + /* Frame analyzer block 2 registers */ #define VSC73XX_STORMLIMIT 0x02 #define VSC73XX_ADVLEARN 0x03 @@ -189,7 +213,8 @@ #define VSC73XX_VLANACCESS_VLAN_MIRROR BIT(29) #define VSC73XX_VLANACCESS_VLAN_SRC_CHECK BIT(28) #define VSC73XX_VLANACCESS_VLAN_PORT_MASK GENMASK(9, 2) -#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK GENMASK(2, 0) +#define VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT 2 +#define VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK GENMASK(1, 0) #define VSC73XX_VLANACCESS_VLAN_TBL_CMD_IDLE 0 #define VSC73XX_VLANACCESS_VLAN_TBL_CMD_READ_ENTRY 1 #define VSC73XX_VLANACCESS_VLAN_TBL_CMD_WRITE_ENTRY 2 @@ -347,6 +372,17 @@ static const struct vsc73xx_counter vsc73xx_tx_counters[] = { { 29, "TxQoSClass3" }, /* non-standard counter */ }; +struct vsc73xx_vlan_summary { + size_t num_tagged; + size_t num_untagged; +}; + +enum vsc73xx_port_vlan_conf { + VSC73XX_VLAN_FILTER, + VSC73XX_VLAN_FILTER_UNTAG_ALL, + VSC73XX_VLAN_IGNORE, +}; + int vsc73xx_is_addr_valid(u8 block, u8 subblock) { switch (block) { @@ -564,6 +600,90 @@ static enum dsa_tag_protocol vsc73xx_get_tag_protocol(struct dsa_switch *ds, return DSA_TAG_PROTO_NONE; } +static int vsc73xx_wait_for_vlan_table_cmd(struct vsc73xx *vsc) +{ + int ret, err; + u32 val; + + ret = read_poll_timeout(vsc73xx_read, err, + err < 0 || + ((val & VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK) == + VSC73XX_VLANACCESS_VLAN_TBL_CMD_IDLE), + VSC73XX_POLL_SLEEP_US, VSC73XX_POLL_TIMEOUT_US, + false, vsc, VSC73XX_BLOCK_ANALYZER, + 0, VSC73XX_VLANACCESS, &val); + if (ret) + return ret; + return err; +} + +static int +vsc73xx_read_vlan_table_entry(struct vsc73xx *vsc, u16 vid, u8 *portmap) +{ + u32 val; + int ret; + + vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANTIDX, vid); + + ret = vsc73xx_wait_for_vlan_table_cmd(vsc); + if (ret) + return ret; + + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, + VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK, + VSC73XX_VLANACCESS_VLAN_TBL_CMD_READ_ENTRY); + + ret = vsc73xx_wait_for_vlan_table_cmd(vsc); + if (ret) + return ret; + + vsc73xx_read(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, &val); + *portmap = (val & VSC73XX_VLANACCESS_VLAN_PORT_MASK) >> + VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT; + + return 0; +} + +static int +vsc73xx_write_vlan_table_entry(struct vsc73xx *vsc, u16 vid, u8 portmap) +{ + int ret; + + vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANTIDX, vid); + + ret = vsc73xx_wait_for_vlan_table_cmd(vsc); + if (ret) + return ret; + + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, + VSC73XX_VLANACCESS_VLAN_TBL_CMD_MASK | + VSC73XX_VLANACCESS_VLAN_SRC_CHECK | + VSC73XX_VLANACCESS_VLAN_PORT_MASK, + VSC73XX_VLANACCESS_VLAN_TBL_CMD_WRITE_ENTRY | + VSC73XX_VLANACCESS_VLAN_SRC_CHECK | + (portmap << VSC73XX_VLANACCESS_VLAN_PORT_MASK_SHIFT)); + + return vsc73xx_wait_for_vlan_table_cmd(vsc); +} + +static int +vsc73xx_update_vlan_table(struct vsc73xx *vsc, int port, u16 vid, bool set) +{ + u8 portmap; + int ret; + + ret = vsc73xx_read_vlan_table_entry(vsc, vid, &portmap); + if (ret) + return ret; + + if (set) + portmap |= BIT(port); + else + portmap &= ~BIT(port); + + return vsc73xx_write_vlan_table_entry(vsc, vid, portmap); +} + static int vsc73xx_setup(struct dsa_switch *ds) { struct vsc73xx *vsc = ds->priv; @@ -598,7 +718,7 @@ static int vsc73xx_setup(struct dsa_switch *ds) VSC73XX_MACACCESS, VSC73XX_MACACCESS_CMD_CLEAR_TABLE); - /* Clear VLAN table */ + /* Set VLAN table to default values */ vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANACCESS, VSC73XX_VLANACCESS_VLAN_TBL_CMD_CLEAR_TABLE); @@ -627,6 +747,9 @@ static int vsc73xx_setup(struct dsa_switch *ds) vsc73xx_write(vsc, VSC73XX_BLOCK_SYSTEM, 0, VSC73XX_GMIIDELAY, VSC73XX_GMIIDELAY_GMII0_GTXDELAY_2_0_NS | VSC73XX_GMIIDELAY_GMII0_RXDELAY_2_0_NS); + /* Ingess VLAN reception mask (table 145) */ + vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_VLANMASK, + 0xff); /* IP multicast flood mask (table 144) */ vsc73xx_write(vsc, VSC73XX_BLOCK_ANALYZER, 0, VSC73XX_IFLODMSK, 0xff); @@ -639,6 +762,12 @@ static int vsc73xx_setup(struct dsa_switch *ds) udelay(4); + /* Clear VLAN table */ + for (i = 0; i < VLAN_N_VID; i++) + vsc73xx_write_vlan_table_entry(vsc, i, 0); + + INIT_LIST_HEAD(&vsc->vlans); + return 0; } @@ -822,6 +951,12 @@ static void vsc73xx_phylink_mac_link_up(struct dsa_switch *ds, int port, val |= seed << VSC73XX_MAC_CFG_SEED_OFFSET; val |= VSC73XX_MAC_CFG_SEED_LOAD; val |= VSC73XX_MAC_CFG_WEXC_DIS; + + /* Those bits are responsible for MTU only. Kernel takes care about MTU, + * let's enable +8 bytes frame length unconditionally. + */ + val |= VSC73XX_MAC_CFG_VLAN_AWR | VSC73XX_MAC_CFG_VLAN_DBLAWR; + vsc73xx_write(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_MAC_CFG, val); /* Flow control for the PHY facing ports: @@ -1029,6 +1164,408 @@ static void vsc73xx_phylink_get_caps(struct dsa_switch *dsa, int port, config->mac_capabilities = MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000; } +static bool vsc73xx_tag_8021q_active(struct dsa_port *dp) +{ + return !dsa_port_is_vlan_filtering(dp); +} + +static void +vsc73xx_set_vlan_conf(struct vsc73xx *vsc, int port, + enum vsc73xx_port_vlan_conf port_vlan_conf) +{ + u32 val = 0; + + if (port_vlan_conf == VSC73XX_VLAN_IGNORE) + val = VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA | + VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA; + + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_CAT_VLAN_MISC, + VSC73XX_CAT_VLAN_MISC_VLAN_TCI_IGNORE_ENA | + VSC73XX_CAT_VLAN_MISC_VLAN_KEEP_TAG_ENA, val); + + val = (port_vlan_conf == VSC73XX_VLAN_FILTER) ? + VSC73XX_TXUPDCFG_TX_INSERT_TAG : 0; + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_TXUPDCFG, + VSC73XX_TXUPDCFG_TX_INSERT_TAG, val); +} + +static int vsc73xx_vlan_change_untagged_hw(struct vsc73xx *vsc, int port, + u16 vid, bool set) +{ + u32 val = 0; + + if (set) + val = VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA | + ((vid << VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_SHIFT) & + VSC73XX_TXUPDCFG_TX_UNTAGGED_VID); + + return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, + VSC73XX_TXUPDCFG, + VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA | + VSC73XX_TXUPDCFG_TX_UNTAGGED_VID, val); +} + +static int vsc73xx_vlan_set_untagged_hw(struct vsc73xx *vsc, int port, u16 vid) +{ + return vsc73xx_vlan_change_untagged_hw(vsc, port, vid, true); +} + +static int +vsc73xx_vlan_change_pvid_hw(struct vsc73xx *vsc, int port, u16 vid, bool set) +{ + u32 val = 0; + int ret; + + val = set ? 0 : VSC73XX_CAT_DROP_UNTAGGED_ENA; + + ret = vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, + VSC73XX_CAT_DROP, + VSC73XX_CAT_DROP_UNTAGGED_ENA, val); + if (!set || ret) + return ret; + + return vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, + VSC73XX_CAT_PORT_VLAN, + VSC73XX_CAT_PORT_VLAN_VLAN_VID, + vid & VSC73XX_CAT_PORT_VLAN_VLAN_VID); +} + +static int vsc73xx_vlan_change_pvid(struct vsc73xx *vsc, int port, u16 vid, + bool valid, bool operate_on_storage, + bool tag_8021q_mode) +{ + struct vsc73xx_portinfo *portinfo = &vsc->portinfo[port]; + + if (tag_8021q_mode) { + portinfo->pvid_tag_8021q_configured = valid; + portinfo->pvid_tag_8021q = vid; + } else { + portinfo->pvid_vlan_filtering_configured = valid; + portinfo->pvid_vlan_filtering = vid; + } + + if (operate_on_storage) + return 0; + + return vsc73xx_vlan_change_pvid_hw(vsc, port, vid, valid); +} + +static int vsc73xx_vlan_clear_pvid(struct vsc73xx *vsc, int port, + bool operate_on_storage, bool tag_8021q_mode) +{ + return vsc73xx_vlan_change_pvid(vsc, port, 0, false, + operate_on_storage, tag_8021q_mode); +} + +static int vsc73xx_vlan_set_pvid(struct vsc73xx *vsc, int port, u16 vid, + bool operate_on_storage, bool tag_8021q_mode) +{ + return vsc73xx_vlan_change_pvid(vsc, port, vid, true, + operate_on_storage, tag_8021q_mode); +} + +static bool vsc73xx_port_get_pvid(struct vsc73xx *vsc, int port, u16 *vid, + bool tag_8021q_mode) +{ + struct vsc73xx_portinfo *portinfo = &vsc->portinfo[port]; + + if (tag_8021q_mode) { + if (portinfo->pvid_tag_8021q_configured) + *vid = portinfo->pvid_tag_8021q; + return portinfo->pvid_tag_8021q_configured; + } + + if (portinfo->pvid_vlan_filtering_configured) + *vid = portinfo->pvid_vlan_filtering; + return portinfo->pvid_vlan_filtering_configured; +} + +static struct vsc73xx_bridge_vlan * +vsc73xx_bridge_vlan_find(struct vsc73xx *vsc, u16 vid) +{ + struct vsc73xx_bridge_vlan *vlan; + + list_for_each_entry(vlan, &vsc->vlans, list) + if (vlan->vid == vid) + return vlan; + + return NULL; +} + +static void vsc73xx_bridge_vlan_summary(struct vsc73xx *vsc, int port, + struct vsc73xx_vlan_summary *summary, + u16 ignored_vid) +{ + size_t num_tagged = 0, num_untagged = 0; + struct vsc73xx_bridge_vlan *vlan; + + list_for_each_entry(vlan, &vsc->vlans, list) { + if (!(vlan->portmask & BIT(port)) || vlan->vid == ignored_vid) + continue; + + if (vlan->untagged & BIT(port)) + num_untagged++; + else + num_tagged++; + } + + summary->num_untagged = num_untagged; + summary->num_tagged = num_tagged; +} + +static u16 vsc73xx_find_first_vlan_untagged(struct vsc73xx *vsc, int port) +{ + struct vsc73xx_bridge_vlan *vlan; + + list_for_each_entry(vlan, &vsc->vlans, list) + if ((vlan->portmask & BIT(port)) && + (vlan->untagged & BIT(port))) + return vlan->vid; + + return VLAN_N_VID; +} + +static int +vsc73xx_port_vlan_filtering(struct dsa_switch *ds, int port, + bool vlan_filtering, struct netlink_ext_ack *extack) +{ + enum vsc73xx_port_vlan_conf port_vlan_conf = VSC73XX_VLAN_IGNORE; + u16 vid_pvid = 0, vid_untagged = 0; + struct vsc73xx_portinfo *portinfo; + struct vsc73xx *vsc = ds->priv; + bool set_untagged = false; + bool set_pvid = false; + + portinfo = &vsc->portinfo[port]; + + /* The swap processed below is required because vsc73xx is using + * tag_8021q. When vlan_filtering is disabled, tag_8021q uses + * pvid/untagged vlans for port recognition. The values configured for + * vlans and pvid/untagged states are stored in portinfo structure. + * When vlan_filtering is enabled, we need to restore pvid/untagged from + * portinfo structure. Analogic routine is processed when vlan_filtering + * is disabled, but values used for tag_8021q are restored. + */ + if (vlan_filtering) { + struct vsc73xx_vlan_summary summary; + + /* Use VLAN_N_VID to count all vlans */ + vsc73xx_bridge_vlan_summary(vsc, port, &summary, VLAN_N_VID); + + port_vlan_conf = (summary.num_untagged > 1) ? + VSC73XX_VLAN_FILTER_UNTAG_ALL : + VSC73XX_VLAN_FILTER; + + if (summary.num_untagged == 1) { + vid_untagged = vsc73xx_find_first_vlan_untagged(vsc, + port); + set_untagged = true; + } + + vid_pvid = portinfo->pvid_vlan_filtering; + set_pvid = portinfo->pvid_vlan_filtering_configured; + } else { + vid_untagged = portinfo->untagged_tag_8021q; + set_untagged = portinfo->untagged_tag_8021q_configured; + vid_pvid = portinfo->pvid_tag_8021q; + set_pvid = portinfo->pvid_tag_8021q_configured; + } + + vsc73xx_vlan_change_untagged_hw(vsc, port, vid_untagged, set_untagged); + vsc73xx_vlan_change_pvid_hw(vsc, port, vid_pvid, set_pvid); + vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf); + + return 0; +} + +static int vsc73xx_port_vlan_add(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan, + struct netlink_ext_ack *extack) +{ + bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED; + bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID; + struct dsa_port *dp = dsa_to_port(ds, port); + enum vsc73xx_port_vlan_conf port_vlan_conf; + struct vsc73xx_bridge_vlan *vsc73xx_vlan; + struct vsc73xx_vlan_summary summary; + struct vsc73xx *vsc = ds->priv; + bool operate_on_storage; + int ret; + u16 vid; + + /* Be sure to deny alterations to the configuration done by tag_8021q. + */ + if (vid_is_dsa_8021q(vlan->vid)) { + NL_SET_ERR_MSG_MOD(extack, + "Range 3072-4095 reserved for dsa_8021q operation"); + return -EBUSY; + } + + /* The processed vlan->vid is excluded from the search because the VLAN + * can be re-added with a different set of flags, so it's easiest to + * ignore its old flags from the VLAN database software copy. + */ + vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid); + + /* VSC73XX allow only three untagged states: none, one or all */ + if ((untagged && summary.num_tagged > 0 && summary.num_untagged > 0) || + (!untagged && summary.num_untagged > 1)) { + NL_SET_ERR_MSG_MOD(extack, + "Port can have only none, one or all untagged vlan"); + return -EBUSY; + } + + vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); + + if (!vsc73xx_vlan) { + vsc73xx_vlan = kzalloc(sizeof(*vsc73xx_vlan), GFP_KERNEL); + if (!vsc73xx_vlan) + return -ENOMEM; + + vsc73xx_vlan->vid = vlan->vid; + vsc73xx_vlan->portmask = 0; + vsc73xx_vlan->untagged = 0; + + INIT_LIST_HEAD(&vsc73xx_vlan->list); + list_add_tail(&vsc73xx_vlan->list, &vsc->vlans); + } + + vsc73xx_vlan->portmask |= BIT(port); + + if (untagged) + vsc73xx_vlan->untagged |= BIT(port); + else + vsc73xx_vlan->untagged &= ~BIT(port); + + /* CPU port must be always tagged because port separation is based on + * tag_8021q. + */ + if (port == CPU_PORT) + goto update_vlan_table; + + operate_on_storage = vsc73xx_tag_8021q_active(dp); + + if (pvid) + ret = vsc73xx_vlan_set_pvid(vsc, port, vlan->vid, + operate_on_storage, false); + else if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && + vid == vlan->vid) + ret = vsc73xx_vlan_clear_pvid(vsc, port, operate_on_storage, + false); + if (ret) + goto err; + + if (operate_on_storage) + goto update_vlan_table; + + port_vlan_conf = VSC73XX_VLAN_FILTER; + + if (summary.num_tagged == 0 && untagged) + port_vlan_conf = VSC73XX_VLAN_FILTER_UNTAG_ALL; + vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf); + + if (port_vlan_conf == VSC73XX_VLAN_FILTER_UNTAG_ALL) + goto update_vlan_table; + + if (untagged) { + ret = vsc73xx_vlan_set_untagged_hw(vsc, port, vlan->vid); + } else if (summary.num_untagged == 1) { + vid = vsc73xx_find_first_vlan_untagged(vsc, port); + ret = vsc73xx_vlan_set_untagged_hw(vsc, port, vid); + } + if (ret) + goto err; + +update_vlan_table: + ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, true); + if (!ret) + return 0; +err: + list_del(&vsc73xx_vlan->list); + kfree(vsc73xx_vlan); + return ret; +} + +static int vsc73xx_port_vlan_del(struct dsa_switch *ds, int port, + const struct switchdev_obj_port_vlan *vlan) +{ + struct vsc73xx_bridge_vlan *vsc73xx_vlan; + struct vsc73xx_vlan_summary summary; + struct vsc73xx *vsc = ds->priv; + bool operate_on_storage; + int ret; + u16 vid; + + vsc73xx_bridge_vlan_summary(vsc, port, &summary, vlan->vid); + + ret = vsc73xx_update_vlan_table(vsc, port, vlan->vid, false); + if (ret) + return ret; + + operate_on_storage = vsc73xx_tag_8021q_active(dsa_to_port(ds, port)); + + if (!operate_on_storage) { + enum vsc73xx_port_vlan_conf port_vlan_conf = + VSC73XX_VLAN_FILTER; + + if (summary.num_tagged == 0) + port_vlan_conf = VSC73XX_VLAN_FILTER_UNTAG_ALL; + vsc73xx_set_vlan_conf(vsc, port, port_vlan_conf); + + if (summary.num_untagged <= 1) { + vid = vsc73xx_find_first_vlan_untagged(vsc, port); + vsc73xx_vlan_change_untagged_hw(vsc, port, vid, + summary.num_untagged); + } + } + + if (vsc73xx_port_get_pvid(vsc, port, &vid, false) && vid == vlan->vid) + vsc73xx_vlan_clear_pvid(vsc, port, operate_on_storage, false); + + vsc73xx_vlan = vsc73xx_bridge_vlan_find(vsc, vlan->vid); + + if (vsc73xx_vlan) { + vsc73xx_vlan->portmask &= ~BIT(port); + + if (vsc73xx_vlan->portmask) + return 0; + + list_del(&vsc73xx_vlan->list); + kfree(vsc73xx_vlan); + } + + return 0; +} + +static int vsc73xx_port_setup(struct dsa_switch *ds, int port) +{ + struct vsc73xx_portinfo *portinfo; + struct vsc73xx *vsc = ds->priv; + + portinfo = &vsc->portinfo[port]; + + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_CAT_DROP, + VSC73XX_CAT_DROP_TAGGED_ENA | + VSC73XX_CAT_DROP_UNTAGGED_ENA, + VSC73XX_CAT_DROP_UNTAGGED_ENA); + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_TXUPDCFG, + VSC73XX_TXUPDCFG_TX_UNTAGGED_VID_ENA | + VSC73XX_TXUPDCFG_TX_UNTAGGED_VID, 0); + vsc73xx_update_bits(vsc, VSC73XX_BLOCK_MAC, port, VSC73XX_CAT_PORT_VLAN, + VSC73XX_CAT_PORT_VLAN_VLAN_VID, 0); + + if (port == CPU_PORT) + vsc73xx_set_vlan_conf(vsc, port, VSC73XX_VLAN_FILTER); + else + vsc73xx_set_vlan_conf(vsc, port, VSC73XX_VLAN_IGNORE); + + portinfo->pvid_vlan_filtering_configured = false; + portinfo->pvid_tag_8021q_configured = false; + portinfo->untagged_tag_8021q_configured = false; + + return 0; +} + static void vsc73xx_refresh_fwd_map(struct dsa_switch *ds, int port, u8 state) { struct dsa_port *other_dp, *dp = dsa_to_port(ds, port); @@ -1120,11 +1657,15 @@ static const struct dsa_switch_ops vsc73xx_ds_ops = { .get_strings = vsc73xx_get_strings, .get_ethtool_stats = vsc73xx_get_ethtool_stats, .get_sset_count = vsc73xx_get_sset_count, + .port_setup = vsc73xx_port_setup, .port_enable = vsc73xx_port_enable, .port_disable = vsc73xx_port_disable, .port_change_mtu = vsc73xx_change_mtu, .port_max_mtu = vsc73xx_get_max_mtu, .port_stp_state_set = vsc73xx_port_stp_state_set, + .port_vlan_filtering = vsc73xx_port_vlan_filtering, + .port_vlan_add = vsc73xx_port_vlan_add, + .port_vlan_del = vsc73xx_port_vlan_del, .phylink_get_caps = vsc73xx_phylink_get_caps, }; diff --git a/drivers/net/dsa/vitesse-vsc73xx.h b/drivers/net/dsa/vitesse-vsc73xx.h index e7b08599a625..ce007273eafe 100644 --- a/drivers/net/dsa/vitesse-vsc73xx.h +++ b/drivers/net/dsa/vitesse-vsc73xx.h @@ -14,6 +14,27 @@ */ #define VSC73XX_MAX_NUM_PORTS 8 +/** + * struct vsc73xx_portinfo - port data structure: contains storage data + * @pvid_vlan_filtering_configured: imforms if port have configured pvid in vlan + * fitering mode + * @pvid_vlan_filtering: pvid vlan number used in vlan fitering mode + * @pvid_tag_8021q_configured: imforms if port have configured pvid in tag_8021q + * mode + * @pvid_tag_8021q: pvid vlan number used in tag_8021q mode + * @untagged_tag_8021q_configured: imforms if port have configured untagged vlan + * in tag_8021q mode + * @untagged_tag_8021q: untagged vlan number used in tag_8021q mode + */ +struct vsc73xx_portinfo { + bool pvid_vlan_filtering_configured; + u16 pvid_vlan_filtering; + bool pvid_tag_8021q_configured; + u16 pvid_tag_8021q; + bool untagged_tag_8021q_configured; + u16 untagged_tag_8021q; +}; + /** * struct vsc73xx - VSC73xx state container: main data structure * @dev: The device pointer @@ -25,6 +46,10 @@ * @addr: MAC address used in flow control frames * @ops: Structure with hardware-dependent operations * @priv: Pointer to the configuration interface structure + * @portinfo: Storage table portinfo structructures + * @vlans: List of configured vlans. Contains port mask and untagged status of + * every vlan configured in port vlan operation. It doesn't cover tag_8021q + * vlans. */ struct vsc73xx { struct device *dev; @@ -35,6 +60,8 @@ struct vsc73xx { u8 addr[ETH_ALEN]; const struct vsc73xx_ops *ops; void *priv; + struct vsc73xx_portinfo portinfo[VSC73XX_MAX_NUM_PORTS]; + struct list_head vlans; }; /** @@ -49,6 +76,21 @@ struct vsc73xx_ops { u32 val); }; +/** + * struct vsc73xx_bridge_vlan - VSC73xx driver structure which keeps vlan + * database copy + * @vid: VLAN number + * @portmask: each bit represents one port + * @untagged: each bit represents one port configured with @vid untagged + * @list: list structure + */ +struct vsc73xx_bridge_vlan { + u16 vid; + u8 portmask; + u8 untagged; + struct list_head list; +}; + int vsc73xx_is_addr_valid(u8 block, u8 subblock); int vsc73xx_probe(struct vsc73xx *vsc); void vsc73xx_remove(struct vsc73xx *vsc);
This patch implements VLAN filtering for the vsc73xx driver. After starting VLAN filtering, the switch is reconfigured from QinQ to a simple VLAN aware mode. This is required because VSC73XX chips do not support inner VLAN tag filtering. Signed-off-by: Pawel Dembicki <paweldembicki@gmail.com> --- v7: - rework pvid and untagged configuration routines - introduce portinfo structure which should make pvid/untagged procedures simpler - introduce 'vsc73xx_vlan_summary' structure - replace tagged/untagged count functions with 'vsc73xx_bridge_vlan_summary' - fix VSC73XX_VLANMASK configuration. It was copy from existing code. - stop configuring pvid/untagged registers whed pvid/untagged is disabled v6: - resend only v5: - fix possible leak in 'vsc73xx_port_vlan_add' - use proper variable in statement from 'vsc73xx_port_vlan_filtering' - change 'vlan_no' name to 'vid' - codding style improvements - comment improvements - handle return of 'vsc73xx_update_bits' - reduce I/O operations - use 'size_t' for counting variables v4: - reworked most of conditional register configs - simplified port_vlan function - move vlan table clearing from port_setup to setup - pvid configuration simplified (now kernel take care about no of pvids per port) - port vlans are stored in list now - introduce implementation of all untagged vlans state - many minor changes v3: - reworked all vlan commits - added storage variables for pvid and untagged vlans - move length extender settings to port setup - remove vlan table cleaning in wrong places v2: - no changes done drivers/net/dsa/vitesse-vsc73xx-core.c | 545 ++++++++++++++++++++++++- drivers/net/dsa/vitesse-vsc73xx.h | 42 ++ 2 files changed, 585 insertions(+), 2 deletions(-)