| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/06/29 11:52 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Freescale DPAA2 switch driver (drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c) to add LAG offload support. This driver depends on the fsl-mc bus (FSL_MC_BUS), which is specific to NXP QorIQ DPAA2 hardware (ARCH_LAYERSCAPE). There is no software emulation for this hardware in standard virtualized environments like QEMU or GCE, making the modified code unreachable during fuzzing.",
"WorthFuzzing": false
} |
| 1/1 | 2026/06/29 11:52 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 5d8979c7a5fba3ee4df2f15fbbbf10c748d14e97\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Mon Jun 29 11:52:09 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c\nindex 858ba844ac5164..8320b26c3f726e 100644\n--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c\n+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c\n@@ -25,6 +25,9 @@\n \n #define DEFAULT_VLAN_ID\t\t\t1\n \n+static struct notifier_block dpaa2_switch_port_switchdev_nb;\n+static struct notifier_block dpaa2_switch_port_switchdev_blocking_nb;\n+\n static u16 dpaa2_switch_port_get_fdb_id(struct ethsw_port_priv *port_priv)\n {\n \treturn port_priv-\u003efdb-\u003efdb_id;\n@@ -51,6 +54,17 @@ dpaa2_switch_filter_block_get_unused(struct ethsw_core *ethsw)\n \treturn NULL;\n }\n \n+static struct dpaa2_switch_lag *\n+dpaa2_switch_lag_get_unused(struct ethsw_core *ethsw)\n+{\n+\tint i;\n+\n+\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++)\n+\t\tif (!ethsw-\u003elags[i].in_use)\n+\t\t\treturn \u0026ethsw-\u003elags[i];\n+\treturn NULL;\n+}\n+\n static bool dpaa2_switch_fdb_in_use_by_others(struct ethsw_core *ethsw,\n \t\t\t\t\t struct dpaa2_switch_fdb *fdb,\n \t\t\t\t\t struct ethsw_port_priv *except)\n@@ -71,9 +85,9 @@ static struct dpaa2_switch_fdb *\n dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv,\n \t\t\t struct net_device *upper_dev)\n {\n-\tstruct ethsw_port_priv *other_port_priv;\n-\tstruct net_device *other_dev;\n-\tstruct list_head *iter;\n+\tstruct ethsw_port_priv *other_port_priv = NULL;\n+\tstruct net_device *other_dev, *other_dev2;\n+\tstruct list_head *iter, *iter2;\n \n \t/* The below call to netdev_for_each_lower_dev() demands the RTNL lock\n \t * being held. Assert on it so that it's easier to catch new code\n@@ -82,17 +96,32 @@ dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv,\n \tASSERT_RTNL();\n \n \t/* If part of a bridge, use the FDB of the first dpaa2 switch interface\n-\t * to be present in that bridge\n+\t * to be present in that bridge. The search descends one level through\n+\t * a bridged bond's lowers as well.\n \t */\n \tnetdev_for_each_lower_dev(upper_dev, other_dev, iter) {\n-\t\tif (!dpaa2_switch_port_dev_check(other_dev))\n-\t\t\tcontinue;\n+\t\tif (netif_is_lag_master(other_dev)) {\n+\t\t\tnetdev_for_each_lower_dev(other_dev, other_dev2, iter2) {\n+\t\t\t\tif (!dpaa2_switch_port_dev_check(other_dev2))\n+\t\t\t\t\tcontinue;\n \n-\t\tif (other_dev == port_priv-\u003enetdev)\n-\t\t\tcontinue;\n+\t\t\t\tif (other_dev2 == port_priv-\u003enetdev)\n+\t\t\t\t\tcontinue;\n \n-\t\tother_port_priv = netdev_priv(other_dev);\n-\t\treturn other_port_priv-\u003efdb;\n+\t\t\t\tother_port_priv = netdev_priv(other_dev2);\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (!dpaa2_switch_port_dev_check(other_dev))\n+\t\t\t\tcontinue;\n+\n+\t\t\tif (other_dev == port_priv-\u003enetdev)\n+\t\t\t\tcontinue;\n+\n+\t\t\tother_port_priv = netdev_priv(other_dev);\n+\t\t}\n+\t\tif (other_port_priv)\n+\t\t\treturn other_port_priv-\u003efdb;\n \t}\n \n \treturn port_priv-\u003efdb;\n@@ -537,6 +566,103 @@ static int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv,\n \treturn err;\n }\n \n+static int dpaa2_switch_port_fdb_add(struct ethsw_port_priv *port_priv,\n+\t\t\t\t const unsigned char *addr)\n+{\n+\tint err;\n+\n+\tif (is_unicast_ether_addr(addr))\n+\t\terr = dpaa2_switch_port_fdb_add_uc(port_priv, addr);\n+\telse\n+\t\terr = dpaa2_switch_port_fdb_add_mc(port_priv, addr);\n+\n+\treturn err;\n+}\n+\n+static int dpaa2_switch_port_fdb_del(struct ethsw_port_priv *port_priv,\n+\t\t\t\t const unsigned char *addr)\n+{\n+\tif (is_unicast_ether_addr(addr))\n+\t\treturn dpaa2_switch_port_fdb_del_uc(port_priv, addr);\n+\telse\n+\t\treturn dpaa2_switch_port_fdb_del_mc(port_priv, addr);\n+}\n+\n+static struct dpaa2_mac_addr *\n+dpaa2_switch_mac_addr_find(struct list_head *addr_list,\n+\t\t\t const unsigned char *addr, u16 vid)\n+{\n+\tstruct dpaa2_mac_addr *a;\n+\n+\tlist_for_each_entry(a, addr_list, list)\n+\t\tif (ether_addr_equal(a-\u003eaddr, addr) \u0026\u0026 a-\u003evid == vid)\n+\t\t\treturn a;\n+\n+\treturn NULL;\n+}\n+\n+static int dpaa2_switch_lag_fdb_add(struct dpaa2_switch_lag *lag,\n+\t\t\t\t const unsigned char *addr, u16 vid)\n+{\n+\tstruct ethsw_port_priv *port_priv = lag-\u003eprimary;\n+\tstruct dpaa2_mac_addr *a;\n+\tint err = 0;\n+\n+\tmutex_lock(\u0026lag-\u003efdb_lock);\n+\n+\ta = dpaa2_switch_mac_addr_find(\u0026lag-\u003efdbs, addr, vid);\n+\tif (a) {\n+\t\trefcount_inc(\u0026a-\u003erefcount);\n+\t\tgoto out;\n+\t}\n+\n+\ta = kzalloc(sizeof(*a), GFP_KERNEL);\n+\tif (!a) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out;\n+\t}\n+\n+\terr = dpaa2_switch_port_fdb_add(port_priv, addr);\n+\tif (err) {\n+\t\tkfree(a);\n+\t\tgoto out;\n+\t}\n+\n+\tether_addr_copy(a-\u003eaddr, addr);\n+\ta-\u003evid = vid;\n+\trefcount_set(\u0026a-\u003erefcount, 1);\n+\tlist_add_tail(\u0026a-\u003elist, \u0026lag-\u003efdbs);\n+\n+out:\n+\tmutex_unlock(\u0026lag-\u003efdb_lock);\n+\n+\treturn err;\n+}\n+\n+static void dpaa2_switch_lag_fdb_del(struct dpaa2_switch_lag *lag,\n+\t\t\t\t const unsigned char *addr, u16 vid)\n+{\n+\tstruct ethsw_port_priv *port_priv = lag-\u003eprimary;\n+\tstruct dpaa2_mac_addr *a;\n+\n+\tmutex_lock(\u0026lag-\u003efdb_lock);\n+\n+\ta = dpaa2_switch_mac_addr_find(\u0026lag-\u003efdbs, addr, vid);\n+\tif (!a)\n+\t\tgoto out;\n+\n+\tif (!refcount_dec_and_test(\u0026a-\u003erefcount))\n+\t\tgoto out;\n+\n+\tlist_del(\u0026a-\u003elist);\n+\tkfree(a);\n+\n+\tdpaa2_switch_port_fdb_del(port_priv, addr);\n+\n+out:\n+\tmutex_unlock(\u0026lag-\u003efdb_lock);\n+}\n+\n static void dpaa2_switch_port_get_stats(struct net_device *netdev,\n \t\t\t\t\tstruct rtnl_link_stats64 *stats)\n {\n@@ -1485,6 +1611,33 @@ bool dpaa2_switch_port_dev_check(const struct net_device *netdev)\n \treturn netdev-\u003enetdev_ops == \u0026dpaa2_switch_port_ops;\n }\n \n+static bool dpaa2_switch_foreign_dev_check(const struct net_device *dev,\n+\t\t\t\t\t const struct net_device *foreign_dev)\n+{\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(dev);\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct ethsw_port_priv *other_port;\n+\tint i;\n+\n+\tif (netif_is_bridge_master(foreign_dev))\n+\t\tif (port_priv-\u003efdb-\u003ebridge_dev == foreign_dev)\n+\t\t\treturn false;\n+\n+\tif (netif_is_bridge_port(foreign_dev)) {\n+\t\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n+\t\t\tother_port = ethsw-\u003eports[i];\n+\n+\t\t\tif (!other_port)\n+\t\t\t\tcontinue;\n+\t\t\tif (dpaa2_switch_port_offloads_bridge_port(other_port,\n+\t\t\t\t\t\t\t\t foreign_dev))\n+\t\t\t\treturn false;\n+\t\t}\n+\t}\n+\n+\treturn true;\n+}\n+\n static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv)\n {\n \tstruct fsl_mc_device *dpsw_port_dev, *dpmac_dev;\n@@ -1860,51 +2013,32 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev,\n \t\t\t\t\t vlan-\u003echanged);\n }\n \n-static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc,\n-\t\t\t\t\t const unsigned char *addr)\n-{\n-\tstruct netdev_hw_addr_list *list = (is_uc) ? \u0026netdev-\u003euc : \u0026netdev-\u003emc;\n-\tstruct netdev_hw_addr *ha;\n-\n-\tnetif_addr_lock_bh(netdev);\n-\tlist_for_each_entry(ha, \u0026list-\u003elist, list) {\n-\t\tif (ether_addr_equal(ha-\u003eaddr, addr)) {\n-\t\t\tnetif_addr_unlock_bh(netdev);\n-\t\t\treturn 1;\n-\t\t}\n-\t}\n-\tnetif_addr_unlock_bh(netdev);\n-\treturn 0;\n-}\n-\n static int dpaa2_switch_port_mdb_add(struct net_device *netdev,\n \t\t\t\t const struct switchdev_obj_port_mdb *mdb)\n {\n \tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n-\tint err;\n+\tstruct dpaa2_switch_lag *lag;\n \n-\t/* Check if address is already set on this port */\n-\tif (dpaa2_switch_port_lookup_address(netdev, 0, mdb-\u003eaddr))\n-\t\treturn -EEXIST;\n-\n-\terr = dpaa2_switch_port_fdb_add_mc(port_priv, mdb-\u003eaddr);\n-\tif (err)\n-\t\treturn err;\n-\n-\terr = dev_mc_add(netdev, mdb-\u003eaddr);\n-\tif (err) {\n-\t\tnetdev_err(netdev, \"dev_mc_add err %d\\n\", err);\n-\t\tdpaa2_switch_port_fdb_del_mc(port_priv, mdb-\u003eaddr);\n-\t}\n-\n-\treturn err;\n+\tlag = rtnl_dereference(port_priv-\u003elag);\n+\tif (lag)\n+\t\treturn dpaa2_switch_lag_fdb_add(lag, mdb-\u003eaddr, mdb-\u003evid);\n+\telse\n+\t\treturn dpaa2_switch_port_fdb_add(port_priv, mdb-\u003eaddr);\n }\n \n-static int dpaa2_switch_port_obj_add(struct net_device *netdev,\n-\t\t\t\t const struct switchdev_obj *obj)\n+static int dpaa2_switch_port_obj_add(struct net_device *netdev, const void *ctx,\n+\t\t\t\t const struct switchdev_obj *obj,\n+\t\t\t\t struct netlink_ext_ack *extack)\n {\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n \tint err;\n \n+\tif (ctx \u0026\u0026 ctx != port_priv)\n+\t\treturn 0;\n+\n+\tif (!dpaa2_switch_port_offloads_bridge_port(port_priv, obj-\u003eorig_dev))\n+\t\treturn -EOPNOTSUPP;\n+\n \tswitch (obj-\u003eid) {\n \tcase SWITCHDEV_OBJ_ID_PORT_VLAN:\n \t\terr = dpaa2_switch_port_vlans_add(netdev,\n@@ -2000,29 +2134,29 @@ static int dpaa2_switch_port_mdb_del(struct net_device *netdev,\n \t\t\t\t const struct switchdev_obj_port_mdb *mdb)\n {\n \tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n-\tint err;\n-\n-\tif (!dpaa2_switch_port_lookup_address(netdev, 0, mdb-\u003eaddr))\n-\t\treturn -ENOENT;\n-\n-\terr = dpaa2_switch_port_fdb_del_mc(port_priv, mdb-\u003eaddr);\n-\tif (err)\n-\t\treturn err;\n+\tstruct dpaa2_switch_lag *lag;\n \n-\terr = dev_mc_del(netdev, mdb-\u003eaddr);\n-\tif (err) {\n-\t\tnetdev_err(netdev, \"dev_mc_del err %d\\n\", err);\n-\t\treturn err;\n-\t}\n+\tlag = rtnl_dereference(port_priv-\u003elag);\n+\tif (lag)\n+\t\tdpaa2_switch_lag_fdb_del(lag, mdb-\u003eaddr, mdb-\u003evid);\n+\telse\n+\t\tdpaa2_switch_port_fdb_del(port_priv, mdb-\u003eaddr);\n \n-\treturn err;\n+\treturn 0;\n }\n \n-static int dpaa2_switch_port_obj_del(struct net_device *netdev,\n+static int dpaa2_switch_port_obj_del(struct net_device *netdev, const void *ctx,\n \t\t\t\t const struct switchdev_obj *obj)\n {\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n \tint err;\n \n+\tif (ctx \u0026\u0026 ctx != port_priv)\n+\t\treturn 0;\n+\n+\tif (!dpaa2_switch_port_offloads_bridge_port(port_priv, obj-\u003eorig_dev))\n+\t\treturn -EOPNOTSUPP;\n+\n \tswitch (obj-\u003eid) {\n \tcase SWITCHDEV_OBJ_ID_PORT_VLAN:\n \t\terr = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));\n@@ -2048,6 +2182,21 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,\n \treturn notifier_from_errno(err);\n }\n \n+static struct net_device *\n+dpaa2_switch_port_to_bridge_port(struct ethsw_port_priv *port_priv)\n+{\n+\tstruct dpaa2_switch_lag *lag;\n+\n+\tif (!port_priv-\u003efdb-\u003ebridge_dev)\n+\t\treturn NULL;\n+\n+\tlag = rtnl_dereference(port_priv-\u003elag);\n+\tif (lag)\n+\t\treturn lag-\u003ebond_dev;\n+\n+\treturn port_priv-\u003enetdev;\n+}\n+\n static int dpaa2_switch_port_bridge_join(struct net_device *netdev,\n \t\t\t\t\t struct net_device *upper_dev,\n \t\t\t\t\t struct netlink_ext_ack *extack)\n@@ -2055,6 +2204,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,\n \tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n \tstruct dpaa2_switch_fdb *old_fdb = port_priv-\u003efdb;\n \tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct net_device *brport_dev;\n \tbool learn_ena;\n \tint err;\n \n@@ -2066,7 +2216,8 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,\n \tdpaa2_switch_port_set_fdb(port_priv, upper_dev, true);\n \n \t/* Inherit the initial bridge port learning state */\n-\tlearn_ena = br_port_flag_is_set(netdev, BR_LEARNING);\n+\tbrport_dev = dpaa2_switch_port_to_bridge_port(port_priv);\n+\tlearn_ena = br_port_flag_is_set(brport_dev, BR_LEARNING);\n \terr = dpaa2_switch_port_set_learning(port_priv, learn_ena);\n \tport_priv-\u003elearn_ena = learn_ena;\n \n@@ -2080,8 +2231,11 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,\n \tif (err)\n \t\tgoto err_egress_flood;\n \n-\terr = switchdev_bridge_port_offload(netdev, netdev, NULL,\n-\t\t\t\t\t NULL, NULL, false, extack);\n+\tbrport_dev = dpaa2_switch_port_to_bridge_port(port_priv);\n+\terr = switchdev_bridge_port_offload(brport_dev, netdev, port_priv,\n+\t\t\t\t\t \u0026dpaa2_switch_port_switchdev_nb,\n+\t\t\t\t\t \u0026dpaa2_switch_port_switchdev_blocking_nb,\n+\t\t\t\t\t false, extack);\n \tif (err)\n \t\tgoto err_switchdev_offload;\n \n@@ -2115,7 +2269,22 @@ static int dpaa2_switch_port_restore_rxvlan(struct net_device *vdev, int vid, vo\n \n static void dpaa2_switch_port_pre_bridge_leave(struct net_device *netdev)\n {\n-\tswitchdev_bridge_port_unoffload(netdev, NULL, NULL, NULL);\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct net_device *brport_dev;\n+\n+\tbrport_dev = dpaa2_switch_port_to_bridge_port(port_priv);\n+\tif (!brport_dev)\n+\t\treturn;\n+\n+\tswitchdev_bridge_port_unoffload(brport_dev, port_priv,\n+\t\t\t\t\t\u0026dpaa2_switch_port_switchdev_nb,\n+\t\t\t\t\t\u0026dpaa2_switch_port_switchdev_blocking_nb);\n+\n+\t/* Make sure that any FDB add/del operations are completed before the\n+\t * bridge layout changes\n+\t */\n+\tflush_workqueue(ethsw-\u003eworkqueue);\n }\n \n static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)\n@@ -2177,30 +2346,53 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)\n \t\t\t\t\t false);\n }\n \n+static int\n+dpaa2_switch_have_vlan_upper(struct net_device *upper_dev,\n+\t\t\t __always_unused struct netdev_nested_priv *priv)\n+{\n+\treturn is_vlan_dev(upper_dev);\n+}\n+\n static int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev)\n {\n-\tstruct net_device *upper_dev;\n-\tstruct list_head *iter;\n+\tstruct netdev_nested_priv priv = {};\n \n \t/* RCU read lock not necessary because we have write-side protection\n-\t * (rtnl_mutex), however a non-rcu iterator does not exist.\n+\t * (rtnl_mutex), however a non-rcu iterator does not exist. Walk the\n+\t * entire upper chain so that a VLAN device stacked on a intermediate\n+\t * bond is caught too.\n \t */\n-\tnetdev_for_each_upper_dev_rcu(netdev, upper_dev, iter)\n-\t\tif (is_vlan_dev(upper_dev))\n-\t\t\treturn -EOPNOTSUPP;\n+\tif (netdev_walk_all_upper_dev_rcu(netdev, dpaa2_switch_have_vlan_upper,\n+\t\t\t\t\t \u0026priv))\n+\t\treturn -EOPNOTSUPP;\n \n \treturn 0;\n }\n \n+static int dpaa2_switch_check_dpsw_instance(struct net_device *dev,\n+\t\t\t\t\t struct netdev_nested_priv *priv)\n+{\n+\tstruct ethsw_port_priv *port_priv = (struct ethsw_port_priv *)priv-\u003edata;\n+\tstruct ethsw_port_priv *other_priv = netdev_priv(dev);\n+\n+\tif (!dpaa2_switch_port_dev_check(dev))\n+\t\treturn 0;\n+\n+\tif (other_priv-\u003eethsw_data == port_priv-\u003eethsw_data)\n+\t\treturn 0;\n+\n+\treturn 1;\n+}\n+\n static int\n dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,\n \t\t\t\t\t struct net_device *upper_dev,\n \t\t\t\t\t struct netlink_ext_ack *extack)\n {\n \tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n-\tstruct ethsw_port_priv *other_port_priv;\n-\tstruct net_device *other_dev;\n-\tstruct list_head *iter;\n+\tstruct netdev_nested_priv data = {\n+\t\t.data = (void *)port_priv,\n+\t};\n \tint err;\n \n \tif (!br_vlan_enabled(upper_dev)) {\n@@ -2215,6 +2407,70 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,\n \t\treturn err;\n \t}\n \n+\terr = netdev_walk_all_lower_dev(upper_dev,\n+\t\t\t\t\tdpaa2_switch_check_dpsw_instance,\n+\t\t\t\t\t\u0026data);\n+\tif (err) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"Interface from a different DPSW is in the bridge already\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int dpaa2_switch_pre_lag_join(struct net_device *netdev,\n+\t\t\t\t struct net_device *upper_dev,\n+\t\t\t\t struct netdev_lag_upper_info *info,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct ethsw_port_priv *other_port_priv;\n+\tstruct dpaa2_switch_lag *lag = NULL;\n+\tstruct dpsw_lag_cfg cfg = {0};\n+\tstruct net_device *other_dev;\n+\tint i, num_ifs = 0, err;\n+\tstruct list_head *iter;\n+\n+\tif (!(ethsw-\u003efeatures \u0026 ETHSW_FEATURE_LAG_OFFLOAD)) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"LAG offload is supported only for DPSW \u003e= v8.13\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (info-\u003etx_type != NETDEV_LAG_TX_TYPE_HASH) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"Can only offload LAG using hash TX type\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (info-\u003ehash_type != NETDEV_LAG_HASH_L23) {\n+\t\tNL_SET_ERR_MSG_MOD(extack, \"Can only offload L2+L3 Tx hash\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tif (!dpaa2_switch_port_has_mac(port_priv)) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"Only switch interfaces connected to MACs can be under a LAG\");\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tif (vlan_uses_dev(upper_dev)) {\n+\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t \"Cannot join a LAG upper that has a VLAN\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n+\t\tif (!ethsw-\u003elags[i].in_use)\n+\t\t\tcontinue;\n+\t\tif (ethsw-\u003elags[i].bond_dev != upper_dev)\n+\t\t\tcontinue;\n+\t\tlag = \u0026ethsw-\u003elags[i];\n+\t\tbreak;\n+\t}\n+\n \tnetdev_for_each_lower_dev(upper_dev, other_dev, iter) {\n \t\tif (!dpaa2_switch_port_dev_check(other_dev))\n \t\t\tcontinue;\n@@ -2222,9 +2478,248 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,\n \t\tother_port_priv = netdev_priv(other_dev);\n \t\tif (other_port_priv-\u003eethsw_data != port_priv-\u003eethsw_data) {\n \t\t\tNL_SET_ERR_MSG_MOD(extack,\n-\t\t\t\t\t \"Interface from a different DPSW is in the bridge already\");\n+\t\t\t\t\t \"Interface from a different DPSW is in the bond already\");\n \t\t\treturn -EINVAL;\n \t\t}\n+\n+\t\tcfg.if_id[num_ifs++] = other_port_priv-\u003eidx;\n+\n+\t\tif (num_ifs \u003e= DPSW_MAX_LAG_IFS) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t\t \"Cannot add more than 8 DPAA2 switch ports under the same bond\");\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t}\n+\n+\tif (lag) {\n+\t\tcfg.group_id = lag-\u003eid;\n+\t\tcfg.if_id[num_ifs++] = port_priv-\u003eidx;\n+\t\tcfg.num_ifs = num_ifs;\n+\t\tcfg.phase = DPSW_LAG_SET_PHASE_CHECK;\n+\n+\t\terr = dpsw_lag_set(ethsw-\u003emc_io, 0, ethsw-\u003edpsw_handle, \u0026cfg);\n+\t\tif (err) {\n+\t\t\tNL_SET_ERR_MSG_MOD(extack,\n+\t\t\t\t\t \"Cannot offload LAG configuration\");\n+\t\t\treturn -EOPNOTSUPP;\n+\t\t}\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static void dpaa2_switch_port_set_lag_group(struct ethsw_port_priv *port_priv,\n+\t\t\t\t\t struct net_device *bond_dev)\n+{\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct ethsw_port_priv *other_port_priv = NULL;\n+\tstruct dpaa2_switch_lag *lag = NULL;\n+\tstruct dpaa2_switch_lag *other_lag;\n+\tstruct net_device *other_dev;\n+\tstruct list_head *iter;\n+\n+\tnetdev_for_each_lower_dev(bond_dev, other_dev, iter) {\n+\t\tif (!dpaa2_switch_port_dev_check(other_dev))\n+\t\t\tcontinue;\n+\n+\t\tother_port_priv = netdev_priv(other_dev);\n+\t\tother_lag = rtnl_dereference(other_port_priv-\u003elag);\n+\t\tif (!other_lag)\n+\t\t\tcontinue;\n+\n+\t\tif (other_lag-\u003ebond_dev == bond_dev) {\n+\t\t\trcu_assign_pointer(port_priv-\u003elag, other_lag);\n+\t\t\treturn;\n+\t\t}\n+\t}\n+\n+\t/* This is the first interface to be added under a bond device. Find an\n+\t * unused LAG group. No need to check for NULL since there are the same\n+\t * amount of DPSW ports as LAG groups, meaning that each port can have\n+\t * its own LAG group.\n+\t */\n+\tlag = dpaa2_switch_lag_get_unused(ethsw);\n+\tlag-\u003ein_use = true;\n+\tlag-\u003ebond_dev = bond_dev;\n+\tlag-\u003eprimary = port_priv;\n+\trcu_assign_pointer(port_priv-\u003elag, lag);\n+}\n+\n+static bool dpaa2_switch_port_in_lag(struct ethsw_port_priv *port_priv,\n+\t\t\t\t struct net_device *bond_dev)\n+{\n+\tstruct dpaa2_switch_lag *lag;\n+\n+\tif (!port_priv)\n+\t\treturn false;\n+\n+\tlag = rtnl_dereference(port_priv-\u003elag);\n+\treturn lag \u0026\u0026 lag-\u003ebond_dev == bond_dev;\n+}\n+\n+static int dpaa2_switch_set_lag_cfg(struct net_device *bond_dev, u8 lag_id,\n+\t\t\t\t struct ethsw_core *ethsw)\n+{\n+\tstruct dpaa2_switch_lag *lag = \u0026ethsw-\u003elags[lag_id - 1];\n+\tstruct ethsw_port_priv *primary, *port_priv;\n+\tstruct ethsw_port_priv *new_primary = NULL;\n+\tstruct dpsw_lag_cfg cfg = {0};\n+\tstruct dpaa2_mac_addr *a;\n+\tu8 num_ifs = 0;\n+\tint err, i;\n+\n+\tcfg.group_id = lag_id;\n+\n+\t/* Determine the primary port. The caller clears -\u003elag on the port that\n+\t * is leaving, so a NULL -\u003elag on the current primary means it is the\n+\t * one leaving: elect the first remaining member as the new primary.\n+\t * Otherwise keep the current primary.\n+\t */\n+\tif (rtnl_dereference(lag-\u003eprimary-\u003elag)) {\n+\t\tprimary = lag-\u003eprimary;\n+\t} else {\n+\t\tprimary = NULL;\n+\t\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n+\t\t\tif (dpaa2_switch_port_in_lag(ethsw-\u003eports[i], bond_dev)) {\n+\t\t\t\tnew_primary = ethsw-\u003eports[i];\n+\t\t\t\tprimary = new_primary;\n+\t\t\t\tbreak;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/* Build the interface list, always placing the primary first */\n+\tif (primary)\n+\t\tcfg.if_id[num_ifs++] = primary-\u003eidx;\n+\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n+\t\tport_priv = ethsw-\u003eports[i];\n+\t\tif (port_priv == primary)\n+\t\t\tcontinue;\n+\t\tif (!dpaa2_switch_port_in_lag(port_priv, bond_dev))\n+\t\t\tcontinue;\n+\n+\t\tcfg.if_id[num_ifs++] = port_priv-\u003eidx;\n+\t}\n+\tcfg.num_ifs = num_ifs;\n+\n+\t/* No more interfaces under this LAG group, mark it as not in use. Wait\n+\t * for a grace period so that any readers of the lag structure finished.\n+\t */\n+\tif (!num_ifs) {\n+\t\tsynchronize_net();\n+\n+\t\tlag-\u003ebond_dev = NULL;\n+\t\tlag-\u003eprimary = NULL;\n+\t\tlag-\u003ein_use = false;\n+\t}\n+\n+\t/* When the primary changes, migrate the FDB entries from the old\n+\t * primary to the new one: remove them before reconfiguring the LAG in\n+\t * hardware and re-add them on the new primary afterwards. We do not\n+\t * touch any refcounting since the intention is to change the HW entry,\n+\t * not the parallel software tracking.\n+\t */\n+\tif (new_primary) {\n+\t\tmutex_lock(\u0026lag-\u003efdb_lock);\n+\t\tlist_for_each_entry(a, \u0026lag-\u003efdbs, list)\n+\t\t\tdpaa2_switch_port_fdb_del(lag-\u003eprimary, a-\u003eaddr);\n+\t\tmutex_unlock(\u0026lag-\u003efdb_lock);\n+\t}\n+\n+\terr = dpsw_lag_set(ethsw-\u003emc_io, 0, ethsw-\u003edpsw_handle, \u0026cfg);\n+\tif (err)\n+\t\treturn err;\n+\n+\tif (new_primary) {\n+\t\tmutex_lock(\u0026lag-\u003efdb_lock);\n+\t\tlist_for_each_entry(a, \u0026lag-\u003efdbs, list) {\n+\t\t\terr = dpaa2_switch_port_fdb_add(new_primary, a-\u003eaddr);\n+\t\t\tif (err)\n+\t\t\t\tnetdev_err(new_primary-\u003enetdev, \"Unable to migrate FDB\\n\");\n+\t\t}\n+\t\tmutex_unlock(\u0026lag-\u003efdb_lock);\n+\n+\t\tsynchronize_net();\n+\t\tlag-\u003eprimary = new_primary;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int dpaa2_switch_port_bond_join(struct net_device *netdev,\n+\t\t\t\t struct net_device *bond_dev,\n+\t\t\t\t struct netdev_lag_upper_info *info,\n+\t\t\t\t struct netlink_ext_ack *extack)\n+{\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct net_device *bridge_dev;\n+\tstruct dpaa2_switch_lag *lag;\n+\tint err = 0;\n+\tu8 lag_id;\n+\n+\t/* Setup the port_priv-\u003elag pointer for this switch port */\n+\tdpaa2_switch_port_set_lag_group(port_priv, bond_dev);\n+\n+\t/* Create the LAG configuration and apply it in MC */\n+\tlag = rtnl_dereference(port_priv-\u003elag);\n+\tlag_id = lag-\u003eid;\n+\terr = dpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);\n+\tif (err)\n+\t\tgoto err_lag_cfg;\n+\n+\t/* If the bond device is a switch port, join the bridge as well */\n+\tbridge_dev = netdev_master_upper_dev_get(bond_dev);\n+\tif (!bridge_dev || !netif_is_bridge_master(bridge_dev))\n+\t\treturn 0;\n+\n+\terr = dpaa2_switch_port_bridge_join(netdev, bridge_dev, extack);\n+\tif (err)\n+\t\tgoto err_lag_cfg;\n+\n+\treturn err;\n+\n+err_lag_cfg:\n+\trcu_assign_pointer(port_priv-\u003elag, NULL);\n+\tdpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);\n+\n+\treturn err;\n+}\n+\n+static int dpaa2_switch_port_bond_leave(struct net_device *netdev,\n+\t\t\t\t\tstruct net_device *bond_dev)\n+{\n+\tstruct net_device *bridge_dev = netdev_master_upper_dev_get(bond_dev);\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n+\tstruct dpaa2_switch_lag *lag = rtnl_dereference(port_priv-\u003elag);\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tstruct net_device *brpdev;\n+\tbool learn_ena;\n+\tint err;\n+\n+\tif (!lag)\n+\t\treturn 0;\n+\n+\t/* Recreate the LAG configuration for the LAG group that we left. */\n+\trcu_assign_pointer(port_priv-\u003elag, NULL);\n+\tdpaa2_switch_set_lag_cfg(bond_dev, lag-\u003eid, ethsw);\n+\n+\tif (bridge_dev \u0026\u0026 netif_is_bridge_master(bridge_dev)) {\n+\t\t/* Make sure that the new primary inherits the learning state */\n+\t\tif (lag-\u003eprimary) {\n+\t\t\tbrpdev = dpaa2_switch_port_to_bridge_port(lag-\u003eprimary);\n+\t\t\tlearn_ena = br_port_flag_is_set(brpdev, BR_LEARNING);\n+\t\t\terr = dpaa2_switch_port_set_learning(lag-\u003eprimary,\n+\t\t\t\t\t\t\t learn_ena);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t\tlag-\u003eprimary-\u003elearn_ena = learn_ena;\n+\t\t}\n+\n+\t\t/* In case the bond is a bridge port, leave the upper bridge as\n+\t\t * well.\n+\t\t */\n+\t\treturn dpaa2_switch_port_bridge_leave(netdev);\n \t}\n \n \treturn 0;\n@@ -2234,8 +2729,8 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,\n \t\t\t\t\t struct netdev_notifier_changeupper_info *info)\n {\n \tstruct ethsw_port_priv *port_priv;\n+\tstruct net_device *upper_dev, *br;\n \tstruct netlink_ext_ack *extack;\n-\tstruct net_device *upper_dev;\n \tint err;\n \n \tif (!dpaa2_switch_port_dev_check(netdev))\n@@ -2252,6 +2747,24 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,\n \n \t\tif (!info-\u003elinking)\n \t\t\tdpaa2_switch_port_pre_bridge_leave(netdev);\n+\t} else if (netif_is_lag_master(upper_dev)) {\n+\t\tif (!info-\u003elinking) {\n+\t\t\tif (netif_is_bridge_port(upper_dev))\n+\t\t\t\tdpaa2_switch_port_pre_bridge_leave(netdev);\n+\t\t\treturn 0;\n+\t\t}\n+\n+\t\tif (netif_is_bridge_port(upper_dev)) {\n+\t\t\tbr = netdev_master_upper_dev_get(upper_dev);\n+\t\t\terr = dpaa2_switch_prechangeupper_sanity_checks(netdev,\n+\t\t\t\t\t\t\t\t\tbr,\n+\t\t\t\t\t\t\t\t\textack);\n+\t\t\tif (err)\n+\t\t\t\treturn err;\n+\t\t}\n+\n+\t\treturn dpaa2_switch_pre_lag_join(netdev, upper_dev,\n+\t\t\t\t\t\t info-\u003eupper_info, extack);\n \t} else if (is_vlan_dev(upper_dev)) {\n \t\tport_priv = netdev_priv(netdev);\n \t\tif (port_priv-\u003efdb-\u003ebridge_dev) {\n@@ -2283,6 +2796,80 @@ static int dpaa2_switch_port_changeupper(struct net_device *netdev,\n \t\t\t\t\t\t\t extack);\n \t\telse\n \t\t\treturn dpaa2_switch_port_bridge_leave(netdev);\n+\t} else if (netif_is_lag_master(upper_dev)) {\n+\t\tif (info-\u003elinking)\n+\t\t\treturn dpaa2_switch_port_bond_join(netdev, upper_dev,\n+\t\t\t\t\t\t\t info-\u003eupper_info,\n+\t\t\t\t\t\t\t extack);\n+\t\telse\n+\t\t\treturn dpaa2_switch_port_bond_leave(netdev, upper_dev);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+dpaa2_switch_lag_prechangeupper(struct net_device *netdev,\n+\t\t\t\tstruct netdev_notifier_changeupper_info *info)\n+{\n+\tstruct net_device *lower;\n+\tstruct list_head *iter;\n+\tint err = 0;\n+\n+\tif (!netif_is_lag_master(netdev))\n+\t\treturn 0;\n+\n+\tnetdev_for_each_lower_dev(netdev, lower, iter) {\n+\t\tif (!dpaa2_switch_port_dev_check(lower))\n+\t\t\tcontinue;\n+\n+\t\terr = dpaa2_switch_port_prechangeupper(lower, info);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn err;\n+}\n+\n+static int\n+dpaa2_switch_lag_changeupper(struct net_device *netdev,\n+\t\t\t struct netdev_notifier_changeupper_info *info)\n+{\n+\tstruct net_device *lower;\n+\tstruct list_head *iter;\n+\tint err = 0;\n+\n+\tif (!netif_is_lag_master(netdev))\n+\t\treturn 0;\n+\n+\tnetdev_for_each_lower_dev(netdev, lower, iter) {\n+\t\tif (!dpaa2_switch_port_dev_check(lower))\n+\t\t\tcontinue;\n+\n+\t\terr = dpaa2_switch_port_changeupper(lower, info);\n+\t\tif (err)\n+\t\t\treturn err;\n+\t}\n+\n+\treturn 0;\n+}\n+\n+static int\n+dpaa2_switch_port_changelowerstate(struct net_device *netdev,\n+\t\t\t\t struct netdev_lag_lower_state_info *linfo)\n+{\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(netdev);\n+\tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n+\tint err;\n+\n+\tif (!rtnl_dereference(port_priv-\u003elag))\n+\t\treturn 0;\n+\n+\terr = dpsw_if_set_lag_state(ethsw-\u003emc_io, 0, ethsw-\u003edpsw_handle,\n+\t\t\t\t port_priv-\u003eidx, linfo-\u003etx_enabled ? 1 : 0);\n+\tif (err) {\n+\t\tnetdev_err(netdev, \"dpsw_if_set_lag_state() = %d\\n\", err);\n+\t\treturn err;\n \t}\n \n \treturn 0;\n@@ -2292,6 +2879,7 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,\n \t\t\t\t\t unsigned long event, void *ptr)\n {\n \tstruct net_device *netdev = netdev_notifier_info_to_dev(ptr);\n+\tstruct netdev_notifier_changelowerstate_info *info;\n \tint err = 0;\n \n \tswitch (event) {\n@@ -2300,13 +2888,29 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,\n \t\tif (err)\n \t\t\treturn notifier_from_errno(err);\n \n+\t\terr = dpaa2_switch_lag_prechangeupper(netdev, ptr);\n+\t\tif (err)\n+\t\t\treturn notifier_from_errno(err);\n+\n \t\tbreak;\n \tcase NETDEV_CHANGEUPPER:\n \t\terr = dpaa2_switch_port_changeupper(netdev, ptr);\n \t\tif (err)\n \t\t\treturn notifier_from_errno(err);\n \n+\t\terr = dpaa2_switch_lag_changeupper(netdev, ptr);\n+\t\tif (err)\n+\t\t\treturn notifier_from_errno(err);\n+\n \t\tbreak;\n+\tcase NETDEV_CHANGELOWERSTATE:\n+\t\tinfo = ptr;\n+\t\tif (!dpaa2_switch_port_dev_check(netdev))\n+\t\t\tbreak;\n+\n+\t\terr = dpaa2_switch_port_changelowerstate(netdev,\n+\t\t\t\t\t\t\t info-\u003elower_state_info);\n+\t\treturn notifier_from_errno(err);\n \t}\n \n \treturn NOTIFY_DONE;\n@@ -2316,80 +2920,97 @@ struct ethsw_switchdev_event_work {\n \tstruct work_struct work;\n \tstruct switchdev_notifier_fdb_info fdb_info;\n \tstruct net_device *dev;\n+\tstruct net_device *orig_dev;\n \tunsigned long event;\n+\tu16 vid;\n };\n \n static void dpaa2_switch_event_work(struct work_struct *work)\n {\n \tstruct ethsw_switchdev_event_work *switchdev_work =\n \t\tcontainer_of(work, struct ethsw_switchdev_event_work, work);\n+\tstruct net_device *orig_dev = switchdev_work-\u003eorig_dev;\n \tstruct net_device *dev = switchdev_work-\u003edev;\n+\tstruct ethsw_port_priv *port_priv = netdev_priv(dev);\n \tstruct switchdev_notifier_fdb_info *fdb_info;\n+\tstruct dpaa2_switch_lag *lag;\n \tint err;\n \n-\trtnl_lock();\n \tfdb_info = \u0026switchdev_work-\u003efdb_info;\n \n+\t/* The lag structures are freed only from dpaa2_switch_remove(), which\n+\t * first flushes this workqueue, so the pointer stays valid for the\n+\t * lifetime of the work item. Only the dereference needs the RCU\n+\t * read-side lock; the FDB helpers below can sleep and must run outside\n+\t * of it.\n+\t */\n+\trcu_read_lock();\n+\tlag = rcu_dereference(port_priv-\u003elag);\n+\trcu_read_unlock();\n+\n \tswitch (switchdev_work-\u003eevent) {\n \tcase SWITCHDEV_FDB_ADD_TO_DEVICE:\n-\t\tif (!fdb_info-\u003eadded_by_user || fdb_info-\u003eis_local)\n-\t\t\tbreak;\n-\t\tif (is_unicast_ether_addr(fdb_info-\u003eaddr))\n-\t\t\terr = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev),\n-\t\t\t\t\t\t\t fdb_info-\u003eaddr);\n+\t\tif (lag)\n+\t\t\terr = dpaa2_switch_lag_fdb_add(lag, fdb_info-\u003eaddr,\n+\t\t\t\t\t\t switchdev_work-\u003evid);\n \t\telse\n-\t\t\terr = dpaa2_switch_port_fdb_add_mc(netdev_priv(dev),\n-\t\t\t\t\t\t\t fdb_info-\u003eaddr);\n+\t\t\terr = dpaa2_switch_port_fdb_add(port_priv,\n+\t\t\t\t\t\t\tfdb_info-\u003eaddr);\n \t\tif (err)\n \t\t\tbreak;\n \t\tfdb_info-\u003eoffloaded = true;\n-\t\tcall_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,\n+\t\tcall_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, orig_dev,\n \t\t\t\t\t \u0026fdb_info-\u003einfo, NULL);\n \t\tbreak;\n \tcase SWITCHDEV_FDB_DEL_TO_DEVICE:\n-\t\tif (!fdb_info-\u003eadded_by_user || fdb_info-\u003eis_local)\n-\t\t\tbreak;\n-\t\tif (is_unicast_ether_addr(fdb_info-\u003eaddr))\n-\t\t\tdpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info-\u003eaddr);\n+\t\tif (lag)\n+\t\t\tdpaa2_switch_lag_fdb_del(lag, fdb_info-\u003eaddr,\n+\t\t\t\t\t\t switchdev_work-\u003evid);\n \t\telse\n-\t\t\tdpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info-\u003eaddr);\n+\t\t\tdpaa2_switch_port_fdb_del(port_priv, fdb_info-\u003eaddr);\n \t\tbreak;\n \t}\n \n-\trtnl_unlock();\n \tkfree(switchdev_work-\u003efdb_info.addr);\n \tkfree(switchdev_work);\n \tdev_put(dev);\n+\tdev_put(orig_dev);\n }\n \n-/* Called under rcu_read_lock() */\n-static int dpaa2_switch_port_event(struct notifier_block *nb,\n-\t\t\t\t unsigned long event, void *ptr)\n+static int\n+dpaa2_switch_port_fdb_event(struct net_device *dev,\n+\t\t\t struct net_device *orig_dev,\n+\t\t\t unsigned long event, const void *ctx,\n+\t\t\t const struct switchdev_notifier_fdb_info *fdb_info)\n {\n-\tstruct net_device *dev = switchdev_notifier_info_to_dev(ptr);\n \tstruct ethsw_port_priv *port_priv = netdev_priv(dev);\n \tstruct ethsw_switchdev_event_work *switchdev_work;\n-\tstruct switchdev_notifier_fdb_info *fdb_info = ptr;\n \tstruct ethsw_core *ethsw = port_priv-\u003eethsw_data;\n \n-\tif (event == SWITCHDEV_PORT_ATTR_SET)\n-\t\treturn dpaa2_switch_port_attr_set_event(dev, ptr);\n+\tif (ctx \u0026\u0026 ctx != port_priv)\n+\t\treturn 0;\n \n-\tif (!dpaa2_switch_port_dev_check(dev))\n-\t\treturn NOTIFY_DONE;\n+\t/* For the moment, do nothing with entries towards foreign devices */\n+\tif (dpaa2_switch_foreign_dev_check(dev, orig_dev))\n+\t\treturn 0;\n+\n+\tif (!fdb_info-\u003eadded_by_user || fdb_info-\u003eis_local)\n+\t\treturn 0;\n \n \tswitchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);\n \tif (!switchdev_work)\n-\t\treturn NOTIFY_BAD;\n+\t\treturn -ENOMEM;\n \n \tINIT_WORK(\u0026switchdev_work-\u003ework, dpaa2_switch_event_work);\n \tswitchdev_work-\u003edev = dev;\n \tswitchdev_work-\u003eevent = event;\n+\tswitchdev_work-\u003eorig_dev = orig_dev;\n+\tswitchdev_work-\u003evid = fdb_info-\u003evid;\n \n \tswitch (event) {\n \tcase SWITCHDEV_FDB_ADD_TO_DEVICE:\n \tcase SWITCHDEV_FDB_DEL_TO_DEVICE:\n-\t\tmemcpy(\u0026switchdev_work-\u003efdb_info, ptr,\n+\t\tmemcpy(\u0026switchdev_work-\u003efdb_info, fdb_info,\n \t\t sizeof(switchdev_work-\u003efdb_info));\n \t\tswitchdev_work-\u003efdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);\n \t\tif (!switchdev_work-\u003efdb_info.addr)\n@@ -2400,52 +3021,61 @@ static int dpaa2_switch_port_event(struct notifier_block *nb,\n \n \t\t/* Take a reference on the device to avoid being freed. */\n \t\tdev_hold(dev);\n+\t\tdev_hold(orig_dev);\n \t\tbreak;\n \tdefault:\n \t\tkfree(switchdev_work);\n-\t\treturn NOTIFY_DONE;\n+\t\treturn 0;\n \t}\n \n \tqueue_work(ethsw-\u003eworkqueue, \u0026switchdev_work-\u003ework);\n \n-\treturn NOTIFY_DONE;\n+\treturn 0;\n \n err_addr_alloc:\n \tkfree(switchdev_work);\n-\treturn NOTIFY_BAD;\n+\treturn -ENOMEM;\n }\n \n-static int dpaa2_switch_port_obj_event(unsigned long event,\n-\t\t\t\t struct net_device *netdev,\n-\t\t\t\t struct switchdev_notifier_port_obj_info *port_obj_info)\n+/* Called under rcu_read_lock() */\n+static int dpaa2_switch_port_event(struct notifier_block *nb,\n+\t\t\t\t unsigned long event, void *ptr)\n {\n-\tint err = -EOPNOTSUPP;\n-\n-\tif (!dpaa2_switch_port_dev_check(netdev))\n-\t\treturn NOTIFY_DONE;\n+\tstruct net_device *dev = switchdev_notifier_info_to_dev(ptr);\n+\tint err;\n \n \tswitch (event) {\n-\tcase SWITCHDEV_PORT_OBJ_ADD:\n-\t\terr = dpaa2_switch_port_obj_add(netdev, port_obj_info-\u003eobj);\n-\t\tbreak;\n-\tcase SWITCHDEV_PORT_OBJ_DEL:\n-\t\terr = dpaa2_switch_port_obj_del(netdev, port_obj_info-\u003eobj);\n-\t\tbreak;\n+\tcase SWITCHDEV_PORT_ATTR_SET:\n+\t\treturn dpaa2_switch_port_attr_set_event(dev, ptr);\n+\tcase SWITCHDEV_FDB_ADD_TO_DEVICE:\n+\tcase SWITCHDEV_FDB_DEL_TO_DEVICE:\n+\t\terr = switchdev_handle_fdb_event_to_device(dev, event, ptr,\n+\t\t\t\t\t\t\t dpaa2_switch_port_dev_check,\n+\t\t\t\t\t\t\t dpaa2_switch_foreign_dev_check,\n+\t\t\t\t\t\t\t dpaa2_switch_port_fdb_event);\n+\t\treturn notifier_from_errno(err);\n+\tdefault:\n+\t\treturn NOTIFY_DONE;\n \t}\n-\n-\tport_obj_info-\u003ehandled = true;\n-\treturn notifier_from_errno(err);\n }\n \n static int dpaa2_switch_port_blocking_event(struct notifier_block *nb,\n \t\t\t\t\t unsigned long event, void *ptr)\n {\n \tstruct net_device *dev = switchdev_notifier_info_to_dev(ptr);\n+\tint err;\n \n \tswitch (event) {\n \tcase SWITCHDEV_PORT_OBJ_ADD:\n+\t\terr = switchdev_handle_port_obj_add(dev, ptr,\n+\t\t\t\t\t\t dpaa2_switch_port_dev_check,\n+\t\t\t\t\t\t dpaa2_switch_port_obj_add);\n+\t\treturn notifier_from_errno(err);\n \tcase SWITCHDEV_PORT_OBJ_DEL:\n-\t\treturn dpaa2_switch_port_obj_event(event, dev, ptr);\n+\t\terr = switchdev_handle_port_obj_del(dev, ptr,\n+\t\t\t\t\t\t dpaa2_switch_port_dev_check,\n+\t\t\t\t\t\t dpaa2_switch_port_obj_del);\n+\t\treturn notifier_from_errno(err);\n \tcase SWITCHDEV_PORT_ATTR_SET:\n \t\treturn dpaa2_switch_port_attr_set_event(dev, ptr);\n \t}\n@@ -2490,19 +3120,22 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,\n \tdma_addr_t addr = dpaa2_fd_get_addr(fd);\n \tstruct ethsw_core *ethsw = fq-\u003eethsw;\n \tstruct ethsw_port_priv *port_priv;\n+\tstruct dpaa2_switch_lag *lag;\n \tstruct net_device *netdev;\n \tstruct vlan_ethhdr *hdr;\n \tstruct sk_buff *skb;\n \tu16 vlan_tci, vid;\n \tint if_id, err;\n \tvoid *vaddr;\n+\tu64 flc;\n \n \tvaddr = dpaa2_iova_to_virt(ethsw-\u003eiommu_domain, addr);\n \tdma_unmap_page(ethsw-\u003edev, addr, DPAA2_SWITCH_RX_BUF_SIZE,\n \t\t DMA_FROM_DEVICE);\n \n \t/* get switch ingress interface ID */\n-\tif_id = upper_32_bits(dpaa2_fd_get_flc(fd)) \u0026 0x0000FFFF;\n+\tflc = dpaa2_fd_get_flc(fd);\n+\tif_id = DPAA2_ETHSW_FLC_IF_ID(flc);\n \tif (if_id \u003e= ethsw-\u003esw_attr.num_ifs) {\n \t\tdev_err(ethsw-\u003edev, \"Frame received from unknown interface!\\n\");\n \t\tgoto err_free_fd;\n@@ -2541,12 +3174,20 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,\n \t\t}\n \t}\n \n-\tskb-\u003edev = netdev;\n+\trcu_read_lock();\n+\n+\tlag = rcu_dereference(port_priv-\u003elag);\n+\tif (DPAA2_ETHSW_FLC_IMPRECISE_IF_ID(flc) \u0026\u0026 lag)\n+\t\tskb-\u003edev = lag-\u003ebond_dev;\n+\telse\n+\t\tskb-\u003edev = netdev;\n \tskb-\u003eprotocol = eth_type_trans(skb, skb-\u003edev);\n \n \t/* Setup the offload_fwd_mark only if the port is under a bridge */\n \tskb-\u003eoffload_fwd_mark = !!(port_priv-\u003efdb-\u003ebridge_dev);\n \n+\trcu_read_unlock();\n+\n \tnetif_receive_skb(skb);\n \n \treturn;\n@@ -2561,6 +3202,9 @@ static void dpaa2_switch_detect_features(struct ethsw_core *ethsw)\n \n \tif (ethsw-\u003emajor \u003e 8 || (ethsw-\u003emajor == 8 \u0026\u0026 ethsw-\u003eminor \u003e= 6))\n \t\tethsw-\u003efeatures |= ETHSW_FEATURE_MAC_ADDR;\n+\n+\tif (ethsw-\u003emajor \u003e 8 || (ethsw-\u003emajor == 8 \u0026\u0026 ethsw-\u003eminor \u003e= 13))\n+\t\tethsw-\u003efeatures |= ETHSW_FEATURE_LAG_OFFLOAD;\n }\n \n static int dpaa2_switch_setup_fqs(struct ethsw_core *ethsw)\n@@ -3195,17 +3839,15 @@ static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)\n \treturn err;\n }\n \n-/* Add an ACL to redirect frames with specific destination MAC address to\n- * control interface\n- */\n+/* Add an ACL to redirect frames to control interface based on the dst MAC */\n static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,\n-\t\t\t\t\t const char *mac)\n+\t\t\t\t\t const u8 *mac, const u8 *mask)\n {\n \tstruct dpaa2_switch_acl_entry acl_entry = {0};\n \n \t/* Match on the destination MAC address */\n \tether_addr_copy(acl_entry.key.match.l2_dest_mac, mac);\n-\teth_broadcast_addr(acl_entry.key.mask.l2_dest_mac);\n+\tether_addr_copy(acl_entry.key.mask.l2_dest_mac, mask);\n \n \t/* Trap to CPU */\n \tacl_entry.cfg.precedence = 0;\n@@ -3216,7 +3858,8 @@ static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,\n \n static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)\n {\n-\tconst char stpa[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};\n+\tconst u8 ll_mac[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};\n+\tconst u8 ll_mask[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xf0};\n \tstruct switchdev_obj_port_vlan vlan = {\n \t\t.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,\n \t\t.vid = DEFAULT_VLAN_ID,\n@@ -3291,7 +3934,7 @@ static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)\n \tif (err)\n \t\treturn err;\n \n-\terr = dpaa2_switch_port_trap_mac_addr(port_priv, stpa);\n+\terr = dpaa2_switch_port_trap_mac_addr(port_priv, ll_mac, ll_mask);\n \tif (err)\n \t\treturn err;\n \n@@ -3331,6 +3974,9 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)\n \tdev = \u0026sw_dev-\u003edev;\n \tethsw = dev_get_drvdata(dev);\n \n+\t/* Make sure that all events were handled before we kfree anything */\n+\tflush_workqueue(ethsw-\u003eworkqueue);\n+\n \tdpaa2_switch_teardown_irqs(sw_dev);\n \n \tdpsw_disable(ethsw-\u003emc_io, 0, ethsw-\u003edpsw_handle);\n@@ -3344,12 +3990,15 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)\n \tfor (i = 0; i \u003c DPAA2_SWITCH_RX_NUM_FQS; i++)\n \t\tnetif_napi_del(\u0026ethsw-\u003efq[i].napi);\n \n-\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++)\n+\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n \t\tdpaa2_switch_remove_port(ethsw, i);\n+\t\tmutex_destroy(\u0026ethsw-\u003elags[i].fdb_lock);\n+\t}\n \n \tkfree(ethsw-\u003efdbs);\n \tkfree(ethsw-\u003efilter_blocks);\n \tkfree(ethsw-\u003eports);\n+\tkfree(ethsw-\u003elags);\n \n \tdpaa2_switch_teardown(sw_dev);\n \n@@ -3377,6 +4026,7 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,\n \tport_priv = netdev_priv(port_netdev);\n \tport_priv-\u003enetdev = port_netdev;\n \tport_priv-\u003eethsw_data = ethsw;\n+\trcu_assign_pointer(port_priv-\u003elag, NULL);\n \n \tmutex_init(\u0026port_priv-\u003emac_lock);\n \n@@ -3484,6 +4134,21 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)\n \t\tgoto err_free_fdbs;\n \t}\n \n+\tethsw-\u003elags = kcalloc(ethsw-\u003esw_attr.num_ifs, sizeof(*ethsw-\u003elags),\n+\t\t\t GFP_KERNEL);\n+\tif (!ethsw-\u003elags) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err_free_filter;\n+\t}\n+\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n+\t\tethsw-\u003elags[i].bond_dev = NULL;\n+\t\tethsw-\u003elags[i].ethsw = ethsw;\n+\t\tethsw-\u003elags[i].id = i + 1;\n+\t\tethsw-\u003elags[i].in_use = 0;\n+\t\tmutex_init(\u0026ethsw-\u003elags[i].fdb_lock);\n+\t\tINIT_LIST_HEAD(\u0026ethsw-\u003elags[i].fdbs);\n+\t}\n+\n \tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++) {\n \t\terr = dpaa2_switch_probe_port(ethsw, i);\n \t\tif (err)\n@@ -3530,6 +4195,10 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)\n err_free_netdev:\n \tfor (i--; i \u003e= 0; i--)\n \t\tdpaa2_switch_remove_port(ethsw, i);\n+\tfor (i = 0; i \u003c ethsw-\u003esw_attr.num_ifs; i++)\n+\t\tmutex_destroy(\u0026ethsw-\u003elags[i].fdb_lock);\n+\tkfree(ethsw-\u003elags);\n+err_free_filter:\n \tkfree(ethsw-\u003efilter_blocks);\n err_free_fdbs:\n \tkfree(ethsw-\u003efdbs);\ndiff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h\nindex 42b3ca73f55d54..63b702b0000c2e 100644\n--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h\n+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h\n@@ -41,7 +41,8 @@\n #define ETHSW_MAX_FRAME_LENGTH\t(DPAA2_MFL - VLAN_ETH_HLEN - ETH_FCS_LEN)\n #define ETHSW_L2_MAX_FRM(mtu)\t((mtu) + VLAN_ETH_HLEN + ETH_FCS_LEN)\n \n-#define ETHSW_FEATURE_MAC_ADDR\tBIT(0)\n+#define ETHSW_FEATURE_MAC_ADDR\t\tBIT(0)\n+#define ETHSW_FEATURE_LAG_OFFLOAD\tBIT(1)\n \n /* Number of receive queues (one RX and one TX_CONF) */\n #define DPAA2_SWITCH_RX_NUM_FQS\t2\n@@ -86,6 +87,9 @@\n \n #define DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE\t256\n \n+#define DPAA2_ETHSW_FLC_IF_ID(flc)\t\t(((flc) \u003e\u003e 32) \u0026 GENMASK(15, 0))\n+#define DPAA2_ETHSW_FLC_IMPRECISE_IF_ID(flc)\t((flc) \u0026 BIT_ULL(63))\n+\n extern const struct ethtool_ops dpaa2_switch_port_ethtool_ops;\n \n struct ethsw_core;\n@@ -99,12 +103,30 @@ struct dpaa2_switch_fq {\n \tu32 fqid;\n };\n \n+struct dpaa2_mac_addr {\n+\tunsigned char addr[ETH_ALEN];\n+\tu16 vid;\n+\trefcount_t refcount;\n+\tstruct list_head list;\n+};\n+\n struct dpaa2_switch_fdb {\n \tstruct net_device\t*bridge_dev;\n \tu16\t\t\tfdb_id;\n \tbool\t\t\tin_use;\n };\n \n+struct dpaa2_switch_lag {\n+\tstruct ethsw_core\t*ethsw;\n+\tstruct net_device\t*bond_dev;\n+\tbool\t\t\tin_use;\n+\tu8\t\t\tid;\n+\tstruct ethsw_port_priv\t*primary;\n+\t/* Protects the list of fdbs installed on this LAG */\n+\tstruct mutex\t\tfdb_lock;\n+\tstruct list_head\tfdbs;\n+};\n+\n struct dpaa2_switch_acl_entry {\n \tstruct list_head\tlist;\n \tu16\t\t\tprio;\n@@ -163,6 +185,8 @@ struct ethsw_port_priv {\n \tstruct dpaa2_mac\t*mac;\n \t/* Protects against changes to port_priv-\u003emac */\n \tstruct mutex\t\tmac_lock;\n+\n+\tstruct dpaa2_switch_lag __rcu *lag;\n };\n \n /* Switch data */\n@@ -190,6 +214,8 @@ struct ethsw_core {\n \tstruct dpaa2_switch_fdb\t\t*fdbs;\n \tstruct dpaa2_switch_filter_block *filter_blocks;\n \tu16\t\t\t\tmirror_port;\n+\n+\tstruct dpaa2_switch_lag\t\t*lags;\n };\n \n static inline int dpaa2_switch_get_index(struct ethsw_core *ethsw,\n@@ -274,4 +300,18 @@ int dpaa2_switch_block_offload_mirror(struct dpaa2_switch_filter_block *block,\n \n int dpaa2_switch_block_unoffload_mirror(struct dpaa2_switch_filter_block *block,\n \t\t\t\t\tstruct ethsw_port_priv *port_priv);\n+\n+static inline bool\n+dpaa2_switch_port_offloads_bridge_port(struct ethsw_port_priv *port_priv,\n+\t\t\t\t const struct net_device *dev)\n+{\n+\tstruct dpaa2_switch_lag *lag = rcu_dereference_rtnl(port_priv-\u003elag);\n+\n+\tif (lag \u0026\u0026 lag-\u003ebond_dev == dev)\n+\t\treturn true;\n+\tif (port_priv-\u003enetdev == dev)\n+\t\treturn true;\n+\treturn false;\n+}\n+\n #endif\t/* __ETHSW_H */\ndiff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h\nindex 397d55f2bd9950..9a2055c64983ce 100644\n--- a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h\n+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h\n@@ -12,7 +12,7 @@\n \n /* DPSW Version */\n #define DPSW_VER_MAJOR\t\t8\n-#define DPSW_VER_MINOR\t\t9\n+#define DPSW_VER_MINOR\t\t13\n \n #define DPSW_CMD_BASE_VERSION\t1\n #define DPSW_CMD_VERSION_2\t2\n@@ -92,11 +92,14 @@\n #define DPSW_CMDID_CTRL_IF_SET_POOLS DPSW_CMD_ID(0x0A1)\n #define DPSW_CMDID_CTRL_IF_ENABLE DPSW_CMD_ID(0x0A2)\n #define DPSW_CMDID_CTRL_IF_DISABLE DPSW_CMD_ID(0x0A3)\n+#define DPSW_CMDID_SET_LAG DPSW_CMD_V2(0x0A4)\n #define DPSW_CMDID_CTRL_IF_SET_QUEUE DPSW_CMD_ID(0x0A6)\n \n #define DPSW_CMDID_SET_EGRESS_FLOOD DPSW_CMD_ID(0x0AC)\n #define DPSW_CMDID_IF_SET_LEARNING_MODE DPSW_CMD_ID(0x0AD)\n \n+#define DPSW_CMDID_IF_SET_LAG_STATE DPSW_CMD_ID(0x0B0)\n+\n /* Macros for accessing command fields smaller than 1byte */\n #define DPSW_MASK(field) \\\n \tGENMASK(DPSW_##field##_SHIFT + DPSW_##field##_SIZE - 1, \\\n@@ -552,5 +555,18 @@ struct dpsw_cmd_if_reflection {\n \t/* only 2 bits from the LSB */\n \tu8 filter;\n };\n+\n+struct dpsw_cmd_lag {\n+\tu8 group_id;\n+\tu8 num_ifs;\n+\tu8 pad[6];\n+\tu8 if_id[DPSW_MAX_LAG_IFS];\n+\tu8 phase;\n+};\n+\n+struct dpsw_cmd_if_set_lag_state {\n+\t__le16 if_id;\n+\tu8 tx_enabled;\n+};\n #pragma pack(pop)\n #endif /* __FSL_DPSW_CMD_H */\ndiff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.c b/drivers/net/ethernet/freescale/dpaa2/dpsw.c\nindex ab921d75deb2e8..f75cbdce42ba2f 100644\n--- a/drivers/net/ethernet/freescale/dpaa2/dpsw.c\n+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.c\n@@ -1659,3 +1659,63 @@ int dpsw_if_remove_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n \n \treturn mc_send_command(mc_io, \u0026cmd);\n }\n+\n+/**\n+ * dpsw_lag_set() - Set LAG configuration\n+ * @mc_io:\tPointer to MC portal's I/O object\n+ * @cmd_flags:\tCommand flags; one or more of 'MC_CMD_FLAG_'\n+ * @token:\tToken of DPSW object\n+ * @cfg:\tpointer to LAG configuration\n+ *\n+ * Return: '0' on Success; Error code otherwise.\n+ */\n+int dpsw_lag_set(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n+\t\t const struct dpsw_lag_cfg *cfg)\n+{\n+\tstruct fsl_mc_command cmd = { 0 };\n+\tstruct dpsw_cmd_lag *cmd_params;\n+\tint i = 0;\n+\n+\tcmd.header = mc_encode_cmd_header(DPSW_CMDID_SET_LAG, cmd_flags, token);\n+\n+\tif (cfg-\u003enum_ifs \u003e DPSW_MAX_LAG_IFS)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tcmd_params = (struct dpsw_cmd_lag *)cmd.params;\n+\tcmd_params-\u003egroup_id = cfg-\u003egroup_id;\n+\tcmd_params-\u003enum_ifs = cfg-\u003enum_ifs;\n+\tcmd_params-\u003ephase = cfg-\u003ephase;\n+\n+\tfor (i = 0; i \u003c cfg-\u003enum_ifs; i++)\n+\t\tcmd_params-\u003eif_id[i] = cfg-\u003eif_id[i];\n+\n+\treturn mc_send_command(mc_io, \u0026cmd);\n+}\n+\n+/**\n+ * dpsw_if_set_lag_state() - Change per port LAG state\n+ * @mc_io: Pointer to MC portal's I/O object\n+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'\n+ * @token: Token of DPSW object\n+ * @if_id: ID of the switch interface\n+ * @tx_enabled: Value of the per port LAG state\n+ * - 0 if the interface will not be active as part of the LAG group\n+ * - 1 if the interface will be active in the LAG group\n+ *\n+ * Return: '0' on Success; Error code otherwise.\n+ */\n+int dpsw_if_set_lag_state(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n+\t\t\t u16 if_id, u8 tx_enabled)\n+{\n+\tstruct dpsw_cmd_if_set_lag_state *cmd_params;\n+\tstruct fsl_mc_command cmd = { 0 };\n+\n+\tcmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_LAG_STATE,\n+\t\t\t\t\t cmd_flags, token);\n+\n+\tcmd_params = (struct dpsw_cmd_if_set_lag_state *)cmd.params;\n+\tcmd_params-\u003eif_id = cpu_to_le16(if_id);\n+\tcmd_params-\u003etx_enabled = tx_enabled;\n+\n+\treturn mc_send_command(mc_io, \u0026cmd);\n+}\ndiff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.h b/drivers/net/ethernet/freescale/dpaa2/dpsw.h\nindex b90bd363f47a19..89f0267de8e9bd 100644\n--- a/drivers/net/ethernet/freescale/dpaa2/dpsw.h\n+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.h\n@@ -20,6 +20,8 @@ struct fsl_mc_io;\n \n #define DPSW_MAX_IF\t\t64\n \n+#define DPSW_MAX_LAG_IFS\t8\n+\n int dpsw_open(struct fsl_mc_io *mc_io, u32 cmd_flags, int dpsw_id, u16 *token);\n \n int dpsw_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);\n@@ -788,4 +790,32 @@ int dpsw_if_add_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n \n int dpsw_if_remove_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n \t\t\t u16 if_id, const struct dpsw_reflection_cfg *cfg);\n+\n+/* Link Aggregation Group configuration */\n+\n+#define DPSW_LAG_SET_PHASE_APPLY 0\n+#define DPSW_LAG_SET_PHASE_CHECK 1\n+\n+/**\n+ * struct dpsw_lag_cfg - Configuration structure for a LAG group\n+ * @group_id: Link aggregation group ID. Valid values are in the\n+ * [1, DPSW_MAX_LAG_IFS] range.\n+ * @num_ifs: Number of interfaces in this LAG group, valid range is\n+ * [0, DPSW_MAX_LAG_IFS].\n+ * @if_id: Array containing the interface IDs of the ports part of a LAG group\n+ * @phase: Use DPSW_LAG_SET_PHASE_APPLY for LAG configuration processing or\n+ * DPSW_LAG_SET_PHASE_CHECK for LAG configuration validation.\n+ */\n+struct dpsw_lag_cfg {\n+\tu8 group_id;\n+\tu8 num_ifs;\n+\tu8 if_id[DPSW_MAX_LAG_IFS];\n+\tu8 phase;\n+};\n+\n+int dpsw_lag_set(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n+\t\t const struct dpsw_lag_cfg *cfg);\n+\n+int dpsw_if_set_lag_state(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,\n+\t\t\t u16 if_id, u8 tx_enabled);\n #endif /* __FSL_DPSW_H */\n"
} |
| 2/1 | 2026/06/29 11:52 | agent | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Freescale DPAA2 switch driver (drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c) to add LAG offload support. This driver depends on the fsl-mc bus (FSL_MC_BUS), which is specific to NXP QorIQ DPAA2 hardware (ARCH_LAYERSCAPE). There is no software emulation for this hardware in standard virtualized environments like QEMU or GCE, making the modified code unreachable during fuzzing.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer. Your job is to review a provided patch series and determine if it makes functional changes to the kernel that should be fuzzed. IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively use your code access tools to examine the actual source code and confirm any assumptions. Return WorthFuzzing=false if the patch only contains: - Modifications to Documentation/, Kconfig files, or code comments. - Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints. - Changes to numeric constants or macros that do not functionally alter execution flow. - Code paths that are impossible to reach in virtualized environments like GCE or QEMU, even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim). If it modifies reachable core kernel logic, drivers, or architectures, use your code search tools to verify the code can be executed, then return WorthFuzzing=true. When returning WorthFuzzing=true, you MUST ALSO: 1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols. Avoid listing generic hot-path functions to prevent skewed test distributions. 2. Identify any specific CONFIG_ options required to properly test this new/modified feature. Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions. Do not list too generic configs (we already have them enabled). Only list those that specifically cover the modified code. List them in the EnableConfigs output array, and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4"). Prefer calling several tools at the same time to save round-trips. Use set-results tool to provide results of the analysis. It must be called exactly once before the final reply. Ignore results of this tool. Prompt: For your convenience, here is the diff of the changes:
commit 5d8979c7a5fba3ee4df2f15fbbbf10c748d14e97
Author: syz-cluster <triage@syzkaller.com>
Date: Mon Jun 29 11:52:09 2026 +0000
syz-cluster: applied patch under review
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
index 858ba844ac5164..8320b26c3f726e 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
@@ -25,6 +25,9 @@
#define DEFAULT_VLAN_ID 1
+static struct notifier_block dpaa2_switch_port_switchdev_nb;
+static struct notifier_block dpaa2_switch_port_switchdev_blocking_nb;
+
static u16 dpaa2_switch_port_get_fdb_id(struct ethsw_port_priv *port_priv)
{
return port_priv->fdb->fdb_id;
@@ -51,6 +54,17 @@ dpaa2_switch_filter_block_get_unused(struct ethsw_core *ethsw)
return NULL;
}
+static struct dpaa2_switch_lag *
+dpaa2_switch_lag_get_unused(struct ethsw_core *ethsw)
+{
+ int i;
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ if (!ethsw->lags[i].in_use)
+ return ðsw->lags[i];
+ return NULL;
+}
+
static bool dpaa2_switch_fdb_in_use_by_others(struct ethsw_core *ethsw,
struct dpaa2_switch_fdb *fdb,
struct ethsw_port_priv *except)
@@ -71,9 +85,9 @@ static struct dpaa2_switch_fdb *
dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv,
struct net_device *upper_dev)
{
- struct ethsw_port_priv *other_port_priv;
- struct net_device *other_dev;
- struct list_head *iter;
+ struct ethsw_port_priv *other_port_priv = NULL;
+ struct net_device *other_dev, *other_dev2;
+ struct list_head *iter, *iter2;
/* The below call to netdev_for_each_lower_dev() demands the RTNL lock
* being held. Assert on it so that it's easier to catch new code
@@ -82,17 +96,32 @@ dpaa2_switch_fdb_for_join(struct ethsw_port_priv *port_priv,
ASSERT_RTNL();
/* If part of a bridge, use the FDB of the first dpaa2 switch interface
- * to be present in that bridge
+ * to be present in that bridge. The search descends one level through
+ * a bridged bond's lowers as well.
*/
netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
- if (!dpaa2_switch_port_dev_check(other_dev))
- continue;
+ if (netif_is_lag_master(other_dev)) {
+ netdev_for_each_lower_dev(other_dev, other_dev2, iter2) {
+ if (!dpaa2_switch_port_dev_check(other_dev2))
+ continue;
- if (other_dev == port_priv->netdev)
- continue;
+ if (other_dev2 == port_priv->netdev)
+ continue;
- other_port_priv = netdev_priv(other_dev);
- return other_port_priv->fdb;
+ other_port_priv = netdev_priv(other_dev2);
+ break;
+ }
+ } else {
+ if (!dpaa2_switch_port_dev_check(other_dev))
+ continue;
+
+ if (other_dev == port_priv->netdev)
+ continue;
+
+ other_port_priv = netdev_priv(other_dev);
+ }
+ if (other_port_priv)
+ return other_port_priv->fdb;
}
return port_priv->fdb;
@@ -537,6 +566,103 @@ static int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
return err;
}
+static int dpaa2_switch_port_fdb_add(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ int err;
+
+ if (is_unicast_ether_addr(addr))
+ err = dpaa2_switch_port_fdb_add_uc(port_priv, addr);
+ else
+ err = dpaa2_switch_port_fdb_add_mc(port_priv, addr);
+
+ return err;
+}
+
+static int dpaa2_switch_port_fdb_del(struct ethsw_port_priv *port_priv,
+ const unsigned char *addr)
+{
+ if (is_unicast_ether_addr(addr))
+ return dpaa2_switch_port_fdb_del_uc(port_priv, addr);
+ else
+ return dpaa2_switch_port_fdb_del_mc(port_priv, addr);
+}
+
+static struct dpaa2_mac_addr *
+dpaa2_switch_mac_addr_find(struct list_head *addr_list,
+ const unsigned char *addr, u16 vid)
+{
+ struct dpaa2_mac_addr *a;
+
+ list_for_each_entry(a, addr_list, list)
+ if (ether_addr_equal(a->addr, addr) && a->vid == vid)
+ return a;
+
+ return NULL;
+}
+
+static int dpaa2_switch_lag_fdb_add(struct dpaa2_switch_lag *lag,
+ const unsigned char *addr, u16 vid)
+{
+ struct ethsw_port_priv *port_priv = lag->primary;
+ struct dpaa2_mac_addr *a;
+ int err = 0;
+
+ mutex_lock(&lag->fdb_lock);
+
+ a = dpaa2_switch_mac_addr_find(&lag->fdbs, addr, vid);
+ if (a) {
+ refcount_inc(&a->refcount);
+ goto out;
+ }
+
+ a = kzalloc(sizeof(*a), GFP_KERNEL);
+ if (!a) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ err = dpaa2_switch_port_fdb_add(port_priv, addr);
+ if (err) {
+ kfree(a);
+ goto out;
+ }
+
+ ether_addr_copy(a->addr, addr);
+ a->vid = vid;
+ refcount_set(&a->refcount, 1);
+ list_add_tail(&a->list, &lag->fdbs);
+
+out:
+ mutex_unlock(&lag->fdb_lock);
+
+ return err;
+}
+
+static void dpaa2_switch_lag_fdb_del(struct dpaa2_switch_lag *lag,
+ const unsigned char *addr, u16 vid)
+{
+ struct ethsw_port_priv *port_priv = lag->primary;
+ struct dpaa2_mac_addr *a;
+
+ mutex_lock(&lag->fdb_lock);
+
+ a = dpaa2_switch_mac_addr_find(&lag->fdbs, addr, vid);
+ if (!a)
+ goto out;
+
+ if (!refcount_dec_and_test(&a->refcount))
+ goto out;
+
+ list_del(&a->list);
+ kfree(a);
+
+ dpaa2_switch_port_fdb_del(port_priv, addr);
+
+out:
+ mutex_unlock(&lag->fdb_lock);
+}
+
static void dpaa2_switch_port_get_stats(struct net_device *netdev,
struct rtnl_link_stats64 *stats)
{
@@ -1485,6 +1611,33 @@ bool dpaa2_switch_port_dev_check(const struct net_device *netdev)
return netdev->netdev_ops == &dpaa2_switch_port_ops;
}
+static bool dpaa2_switch_foreign_dev_check(const struct net_device *dev,
+ const struct net_device *foreign_dev)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(dev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct ethsw_port_priv *other_port;
+ int i;
+
+ if (netif_is_bridge_master(foreign_dev))
+ if (port_priv->fdb->bridge_dev == foreign_dev)
+ return false;
+
+ if (netif_is_bridge_port(foreign_dev)) {
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ other_port = ethsw->ports[i];
+
+ if (!other_port)
+ continue;
+ if (dpaa2_switch_port_offloads_bridge_port(other_port,
+ foreign_dev))
+ return false;
+ }
+ }
+
+ return true;
+}
+
static int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv)
{
struct fsl_mc_device *dpsw_port_dev, *dpmac_dev;
@@ -1860,51 +2013,32 @@ int dpaa2_switch_port_vlans_add(struct net_device *netdev,
vlan->changed);
}
-static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc,
- const unsigned char *addr)
-{
- struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
- struct netdev_hw_addr *ha;
-
- netif_addr_lock_bh(netdev);
- list_for_each_entry(ha, &list->list, list) {
- if (ether_addr_equal(ha->addr, addr)) {
- netif_addr_unlock_bh(netdev);
- return 1;
- }
- }
- netif_addr_unlock_bh(netdev);
- return 0;
-}
-
static int dpaa2_switch_port_mdb_add(struct net_device *netdev,
const struct switchdev_obj_port_mdb *mdb)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- int err;
+ struct dpaa2_switch_lag *lag;
- /* Check if address is already set on this port */
- if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
- return -EEXIST;
-
- err = dpaa2_switch_port_fdb_add_mc(port_priv, mdb->addr);
- if (err)
- return err;
-
- err = dev_mc_add(netdev, mdb->addr);
- if (err) {
- netdev_err(netdev, "dev_mc_add err %d\n", err);
- dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
- }
-
- return err;
+ lag = rtnl_dereference(port_priv->lag);
+ if (lag)
+ return dpaa2_switch_lag_fdb_add(lag, mdb->addr, mdb->vid);
+ else
+ return dpaa2_switch_port_fdb_add(port_priv, mdb->addr);
}
-static int dpaa2_switch_port_obj_add(struct net_device *netdev,
- const struct switchdev_obj *obj)
+static int dpaa2_switch_port_obj_add(struct net_device *netdev, const void *ctx,
+ const struct switchdev_obj *obj,
+ struct netlink_ext_ack *extack)
{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int err;
+ if (ctx && ctx != port_priv)
+ return 0;
+
+ if (!dpaa2_switch_port_offloads_bridge_port(port_priv, obj->orig_dev))
+ return -EOPNOTSUPP;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = dpaa2_switch_port_vlans_add(netdev,
@@ -2000,29 +2134,29 @@ static int dpaa2_switch_port_mdb_del(struct net_device *netdev,
const struct switchdev_obj_port_mdb *mdb)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- int err;
-
- if (!dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
- return -ENOENT;
-
- err = dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
- if (err)
- return err;
+ struct dpaa2_switch_lag *lag;
- err = dev_mc_del(netdev, mdb->addr);
- if (err) {
- netdev_err(netdev, "dev_mc_del err %d\n", err);
- return err;
- }
+ lag = rtnl_dereference(port_priv->lag);
+ if (lag)
+ dpaa2_switch_lag_fdb_del(lag, mdb->addr, mdb->vid);
+ else
+ dpaa2_switch_port_fdb_del(port_priv, mdb->addr);
- return err;
+ return 0;
}
-static int dpaa2_switch_port_obj_del(struct net_device *netdev,
+static int dpaa2_switch_port_obj_del(struct net_device *netdev, const void *ctx,
const struct switchdev_obj *obj)
{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
int err;
+ if (ctx && ctx != port_priv)
+ return 0;
+
+ if (!dpaa2_switch_port_offloads_bridge_port(port_priv, obj->orig_dev))
+ return -EOPNOTSUPP;
+
switch (obj->id) {
case SWITCHDEV_OBJ_ID_PORT_VLAN:
err = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
@@ -2048,6 +2182,21 @@ static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
return notifier_from_errno(err);
}
+static struct net_device *
+dpaa2_switch_port_to_bridge_port(struct ethsw_port_priv *port_priv)
+{
+ struct dpaa2_switch_lag *lag;
+
+ if (!port_priv->fdb->bridge_dev)
+ return NULL;
+
+ lag = rtnl_dereference(port_priv->lag);
+ if (lag)
+ return lag->bond_dev;
+
+ return port_priv->netdev;
+}
+
static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
struct net_device *upper_dev,
struct netlink_ext_ack *extack)
@@ -2055,6 +2204,7 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
struct dpaa2_switch_fdb *old_fdb = port_priv->fdb;
struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *brport_dev;
bool learn_ena;
int err;
@@ -2066,7 +2216,8 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
dpaa2_switch_port_set_fdb(port_priv, upper_dev, true);
/* Inherit the initial bridge port learning state */
- learn_ena = br_port_flag_is_set(netdev, BR_LEARNING);
+ brport_dev = dpaa2_switch_port_to_bridge_port(port_priv);
+ learn_ena = br_port_flag_is_set(brport_dev, BR_LEARNING);
err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
port_priv->learn_ena = learn_ena;
@@ -2080,8 +2231,11 @@ static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
if (err)
goto err_egress_flood;
- err = switchdev_bridge_port_offload(netdev, netdev, NULL,
- NULL, NULL, false, extack);
+ brport_dev = dpaa2_switch_port_to_bridge_port(port_priv);
+ err = switchdev_bridge_port_offload(brport_dev, netdev, port_priv,
+ &dpaa2_switch_port_switchdev_nb,
+ &dpaa2_switch_port_switchdev_blocking_nb,
+ false, extack);
if (err)
goto err_switchdev_offload;
@@ -2115,7 +2269,22 @@ static int dpaa2_switch_port_restore_rxvlan(struct net_device *vdev, int vid, vo
static void dpaa2_switch_port_pre_bridge_leave(struct net_device *netdev)
{
- switchdev_bridge_port_unoffload(netdev, NULL, NULL, NULL);
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *brport_dev;
+
+ brport_dev = dpaa2_switch_port_to_bridge_port(port_priv);
+ if (!brport_dev)
+ return;
+
+ switchdev_bridge_port_unoffload(brport_dev, port_priv,
+ &dpaa2_switch_port_switchdev_nb,
+ &dpaa2_switch_port_switchdev_blocking_nb);
+
+ /* Make sure that any FDB add/del operations are completed before the
+ * bridge layout changes
+ */
+ flush_workqueue(ethsw->workqueue);
}
static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
@@ -2177,30 +2346,53 @@ static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
false);
}
+static int
+dpaa2_switch_have_vlan_upper(struct net_device *upper_dev,
+ __always_unused struct netdev_nested_priv *priv)
+{
+ return is_vlan_dev(upper_dev);
+}
+
static int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev)
{
- struct net_device *upper_dev;
- struct list_head *iter;
+ struct netdev_nested_priv priv = {};
/* RCU read lock not necessary because we have write-side protection
- * (rtnl_mutex), however a non-rcu iterator does not exist.
+ * (rtnl_mutex), however a non-rcu iterator does not exist. Walk the
+ * entire upper chain so that a VLAN device stacked on a intermediate
+ * bond is caught too.
*/
- netdev_for_each_upper_dev_rcu(netdev, upper_dev, iter)
- if (is_vlan_dev(upper_dev))
- return -EOPNOTSUPP;
+ if (netdev_walk_all_upper_dev_rcu(netdev, dpaa2_switch_have_vlan_upper,
+ &priv))
+ return -EOPNOTSUPP;
return 0;
}
+static int dpaa2_switch_check_dpsw_instance(struct net_device *dev,
+ struct netdev_nested_priv *priv)
+{
+ struct ethsw_port_priv *port_priv = (struct ethsw_port_priv *)priv->data;
+ struct ethsw_port_priv *other_priv = netdev_priv(dev);
+
+ if (!dpaa2_switch_port_dev_check(dev))
+ return 0;
+
+ if (other_priv->ethsw_data == port_priv->ethsw_data)
+ return 0;
+
+ return 1;
+}
+
static int
dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
struct net_device *upper_dev,
struct netlink_ext_ack *extack)
{
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
- struct ethsw_port_priv *other_port_priv;
- struct net_device *other_dev;
- struct list_head *iter;
+ struct netdev_nested_priv data = {
+ .data = (void *)port_priv,
+ };
int err;
if (!br_vlan_enabled(upper_dev)) {
@@ -2215,6 +2407,70 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
return err;
}
+ err = netdev_walk_all_lower_dev(upper_dev,
+ dpaa2_switch_check_dpsw_instance,
+ &data);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Interface from a different DPSW is in the bridge already");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int dpaa2_switch_pre_lag_join(struct net_device *netdev,
+ struct net_device *upper_dev,
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct ethsw_port_priv *other_port_priv;
+ struct dpaa2_switch_lag *lag = NULL;
+ struct dpsw_lag_cfg cfg = {0};
+ struct net_device *other_dev;
+ int i, num_ifs = 0, err;
+ struct list_head *iter;
+
+ if (!(ethsw->features & ETHSW_FEATURE_LAG_OFFLOAD)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "LAG offload is supported only for DPSW >= v8.13");
+ return -EOPNOTSUPP;
+ }
+
+ if (info->tx_type != NETDEV_LAG_TX_TYPE_HASH) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Can only offload LAG using hash TX type");
+ return -EOPNOTSUPP;
+ }
+
+ if (info->hash_type != NETDEV_LAG_HASH_L23) {
+ NL_SET_ERR_MSG_MOD(extack, "Can only offload L2+L3 Tx hash");
+ return -EOPNOTSUPP;
+ }
+
+ if (!dpaa2_switch_port_has_mac(port_priv)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Only switch interfaces connected to MACs can be under a LAG");
+ return -EINVAL;
+ }
+
+ if (vlan_uses_dev(upper_dev)) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot join a LAG upper that has a VLAN");
+ return -EOPNOTSUPP;
+ }
+
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ if (!ethsw->lags[i].in_use)
+ continue;
+ if (ethsw->lags[i].bond_dev != upper_dev)
+ continue;
+ lag = ðsw->lags[i];
+ break;
+ }
+
netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
if (!dpaa2_switch_port_dev_check(other_dev))
continue;
@@ -2222,9 +2478,248 @@ dpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
other_port_priv = netdev_priv(other_dev);
if (other_port_priv->ethsw_data != port_priv->ethsw_data) {
NL_SET_ERR_MSG_MOD(extack,
- "Interface from a different DPSW is in the bridge already");
+ "Interface from a different DPSW is in the bond already");
return -EINVAL;
}
+
+ cfg.if_id[num_ifs++] = other_port_priv->idx;
+
+ if (num_ifs >= DPSW_MAX_LAG_IFS) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot add more than 8 DPAA2 switch ports under the same bond");
+ return -EINVAL;
+ }
+ }
+
+ if (lag) {
+ cfg.group_id = lag->id;
+ cfg.if_id[num_ifs++] = port_priv->idx;
+ cfg.num_ifs = num_ifs;
+ cfg.phase = DPSW_LAG_SET_PHASE_CHECK;
+
+ err = dpsw_lag_set(ethsw->mc_io, 0, ethsw->dpsw_handle, &cfg);
+ if (err) {
+ NL_SET_ERR_MSG_MOD(extack,
+ "Cannot offload LAG configuration");
+ return -EOPNOTSUPP;
+ }
+ }
+
+ return 0;
+}
+
+static void dpaa2_switch_port_set_lag_group(struct ethsw_port_priv *port_priv,
+ struct net_device *bond_dev)
+{
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct ethsw_port_priv *other_port_priv = NULL;
+ struct dpaa2_switch_lag *lag = NULL;
+ struct dpaa2_switch_lag *other_lag;
+ struct net_device *other_dev;
+ struct list_head *iter;
+
+ netdev_for_each_lower_dev(bond_dev, other_dev, iter) {
+ if (!dpaa2_switch_port_dev_check(other_dev))
+ continue;
+
+ other_port_priv = netdev_priv(other_dev);
+ other_lag = rtnl_dereference(other_port_priv->lag);
+ if (!other_lag)
+ continue;
+
+ if (other_lag->bond_dev == bond_dev) {
+ rcu_assign_pointer(port_priv->lag, other_lag);
+ return;
+ }
+ }
+
+ /* This is the first interface to be added under a bond device. Find an
+ * unused LAG group. No need to check for NULL since there are the same
+ * amount of DPSW ports as LAG groups, meaning that each port can have
+ * its own LAG group.
+ */
+ lag = dpaa2_switch_lag_get_unused(ethsw);
+ lag->in_use = true;
+ lag->bond_dev = bond_dev;
+ lag->primary = port_priv;
+ rcu_assign_pointer(port_priv->lag, lag);
+}
+
+static bool dpaa2_switch_port_in_lag(struct ethsw_port_priv *port_priv,
+ struct net_device *bond_dev)
+{
+ struct dpaa2_switch_lag *lag;
+
+ if (!port_priv)
+ return false;
+
+ lag = rtnl_dereference(port_priv->lag);
+ return lag && lag->bond_dev == bond_dev;
+}
+
+static int dpaa2_switch_set_lag_cfg(struct net_device *bond_dev, u8 lag_id,
+ struct ethsw_core *ethsw)
+{
+ struct dpaa2_switch_lag *lag = ðsw->lags[lag_id - 1];
+ struct ethsw_port_priv *primary, *port_priv;
+ struct ethsw_port_priv *new_primary = NULL;
+ struct dpsw_lag_cfg cfg = {0};
+ struct dpaa2_mac_addr *a;
+ u8 num_ifs = 0;
+ int err, i;
+
+ cfg.group_id = lag_id;
+
+ /* Determine the primary port. The caller clears ->lag on the port that
+ * is leaving, so a NULL ->lag on the current primary means it is the
+ * one leaving: elect the first remaining member as the new primary.
+ * Otherwise keep the current primary.
+ */
+ if (rtnl_dereference(lag->primary->lag)) {
+ primary = lag->primary;
+ } else {
+ primary = NULL;
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ if (dpaa2_switch_port_in_lag(ethsw->ports[i], bond_dev)) {
+ new_primary = ethsw->ports[i];
+ primary = new_primary;
+ break;
+ }
+ }
+ }
+
+ /* Build the interface list, always placing the primary first */
+ if (primary)
+ cfg.if_id[num_ifs++] = primary->idx;
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ port_priv = ethsw->ports[i];
+ if (port_priv == primary)
+ continue;
+ if (!dpaa2_switch_port_in_lag(port_priv, bond_dev))
+ continue;
+
+ cfg.if_id[num_ifs++] = port_priv->idx;
+ }
+ cfg.num_ifs = num_ifs;
+
+ /* No more interfaces under this LAG group, mark it as not in use. Wait
+ * for a grace period so that any readers of the lag structure finished.
+ */
+ if (!num_ifs) {
+ synchronize_net();
+
+ lag->bond_dev = NULL;
+ lag->primary = NULL;
+ lag->in_use = false;
+ }
+
+ /* When the primary changes, migrate the FDB entries from the old
+ * primary to the new one: remove them before reconfiguring the LAG in
+ * hardware and re-add them on the new primary afterwards. We do not
+ * touch any refcounting since the intention is to change the HW entry,
+ * not the parallel software tracking.
+ */
+ if (new_primary) {
+ mutex_lock(&lag->fdb_lock);
+ list_for_each_entry(a, &lag->fdbs, list)
+ dpaa2_switch_port_fdb_del(lag->primary, a->addr);
+ mutex_unlock(&lag->fdb_lock);
+ }
+
+ err = dpsw_lag_set(ethsw->mc_io, 0, ethsw->dpsw_handle, &cfg);
+ if (err)
+ return err;
+
+ if (new_primary) {
+ mutex_lock(&lag->fdb_lock);
+ list_for_each_entry(a, &lag->fdbs, list) {
+ err = dpaa2_switch_port_fdb_add(new_primary, a->addr);
+ if (err)
+ netdev_err(new_primary->netdev, "Unable to migrate FDB\n");
+ }
+ mutex_unlock(&lag->fdb_lock);
+
+ synchronize_net();
+ lag->primary = new_primary;
+ }
+
+ return 0;
+}
+
+static int dpaa2_switch_port_bond_join(struct net_device *netdev,
+ struct net_device *bond_dev,
+ struct netdev_lag_upper_info *info,
+ struct netlink_ext_ack *extack)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *bridge_dev;
+ struct dpaa2_switch_lag *lag;
+ int err = 0;
+ u8 lag_id;
+
+ /* Setup the port_priv->lag pointer for this switch port */
+ dpaa2_switch_port_set_lag_group(port_priv, bond_dev);
+
+ /* Create the LAG configuration and apply it in MC */
+ lag = rtnl_dereference(port_priv->lag);
+ lag_id = lag->id;
+ err = dpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);
+ if (err)
+ goto err_lag_cfg;
+
+ /* If the bond device is a switch port, join the bridge as well */
+ bridge_dev = netdev_master_upper_dev_get(bond_dev);
+ if (!bridge_dev || !netif_is_bridge_master(bridge_dev))
+ return 0;
+
+ err = dpaa2_switch_port_bridge_join(netdev, bridge_dev, extack);
+ if (err)
+ goto err_lag_cfg;
+
+ return err;
+
+err_lag_cfg:
+ rcu_assign_pointer(port_priv->lag, NULL);
+ dpaa2_switch_set_lag_cfg(bond_dev, lag_id, ethsw);
+
+ return err;
+}
+
+static int dpaa2_switch_port_bond_leave(struct net_device *netdev,
+ struct net_device *bond_dev)
+{
+ struct net_device *bridge_dev = netdev_master_upper_dev_get(bond_dev);
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct dpaa2_switch_lag *lag = rtnl_dereference(port_priv->lag);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ struct net_device *brpdev;
+ bool learn_ena;
+ int err;
+
+ if (!lag)
+ return 0;
+
+ /* Recreate the LAG configuration for the LAG group that we left. */
+ rcu_assign_pointer(port_priv->lag, NULL);
+ dpaa2_switch_set_lag_cfg(bond_dev, lag->id, ethsw);
+
+ if (bridge_dev && netif_is_bridge_master(bridge_dev)) {
+ /* Make sure that the new primary inherits the learning state */
+ if (lag->primary) {
+ brpdev = dpaa2_switch_port_to_bridge_port(lag->primary);
+ learn_ena = br_port_flag_is_set(brpdev, BR_LEARNING);
+ err = dpaa2_switch_port_set_learning(lag->primary,
+ learn_ena);
+ if (err)
+ return err;
+ lag->primary->learn_ena = learn_ena;
+ }
+
+ /* In case the bond is a bridge port, leave the upper bridge as
+ * well.
+ */
+ return dpaa2_switch_port_bridge_leave(netdev);
}
return 0;
@@ -2234,8 +2729,8 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,
struct netdev_notifier_changeupper_info *info)
{
struct ethsw_port_priv *port_priv;
+ struct net_device *upper_dev, *br;
struct netlink_ext_ack *extack;
- struct net_device *upper_dev;
int err;
if (!dpaa2_switch_port_dev_check(netdev))
@@ -2252,6 +2747,24 @@ static int dpaa2_switch_port_prechangeupper(struct net_device *netdev,
if (!info->linking)
dpaa2_switch_port_pre_bridge_leave(netdev);
+ } else if (netif_is_lag_master(upper_dev)) {
+ if (!info->linking) {
+ if (netif_is_bridge_port(upper_dev))
+ dpaa2_switch_port_pre_bridge_leave(netdev);
+ return 0;
+ }
+
+ if (netif_is_bridge_port(upper_dev)) {
+ br = netdev_master_upper_dev_get(upper_dev);
+ err = dpaa2_switch_prechangeupper_sanity_checks(netdev,
+ br,
+ extack);
+ if (err)
+ return err;
+ }
+
+ return dpaa2_switch_pre_lag_join(netdev, upper_dev,
+ info->upper_info, extack);
} else if (is_vlan_dev(upper_dev)) {
port_priv = netdev_priv(netdev);
if (port_priv->fdb->bridge_dev) {
@@ -2283,6 +2796,80 @@ static int dpaa2_switch_port_changeupper(struct net_device *netdev,
extack);
else
return dpaa2_switch_port_bridge_leave(netdev);
+ } else if (netif_is_lag_master(upper_dev)) {
+ if (info->linking)
+ return dpaa2_switch_port_bond_join(netdev, upper_dev,
+ info->upper_info,
+ extack);
+ else
+ return dpaa2_switch_port_bond_leave(netdev, upper_dev);
+ }
+
+ return 0;
+}
+
+static int
+dpaa2_switch_lag_prechangeupper(struct net_device *netdev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct net_device *lower;
+ struct list_head *iter;
+ int err = 0;
+
+ if (!netif_is_lag_master(netdev))
+ return 0;
+
+ netdev_for_each_lower_dev(netdev, lower, iter) {
+ if (!dpaa2_switch_port_dev_check(lower))
+ continue;
+
+ err = dpaa2_switch_port_prechangeupper(lower, info);
+ if (err)
+ return err;
+ }
+
+ return err;
+}
+
+static int
+dpaa2_switch_lag_changeupper(struct net_device *netdev,
+ struct netdev_notifier_changeupper_info *info)
+{
+ struct net_device *lower;
+ struct list_head *iter;
+ int err = 0;
+
+ if (!netif_is_lag_master(netdev))
+ return 0;
+
+ netdev_for_each_lower_dev(netdev, lower, iter) {
+ if (!dpaa2_switch_port_dev_check(lower))
+ continue;
+
+ err = dpaa2_switch_port_changeupper(lower, info);
+ if (err)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+dpaa2_switch_port_changelowerstate(struct net_device *netdev,
+ struct netdev_lag_lower_state_info *linfo)
+{
+ struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+ struct ethsw_core *ethsw = port_priv->ethsw_data;
+ int err;
+
+ if (!rtnl_dereference(port_priv->lag))
+ return 0;
+
+ err = dpsw_if_set_lag_state(ethsw->mc_io, 0, ethsw->dpsw_handle,
+ port_priv->idx, linfo->tx_enabled ? 1 : 0);
+ if (err) {
+ netdev_err(netdev, "dpsw_if_set_lag_state() = %d\n", err);
+ return err;
}
return 0;
@@ -2292,6 +2879,7 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
+ struct netdev_notifier_changelowerstate_info *info;
int err = 0;
switch (event) {
@@ -2300,13 +2888,29 @@ static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
if (err)
return notifier_from_errno(err);
+ err = dpaa2_switch_lag_prechangeupper(netdev, ptr);
+ if (err)
+ return notifier_from_errno(err);
+
break;
case NETDEV_CHANGEUPPER:
err = dpaa2_switch_port_changeupper(netdev, ptr);
if (err)
return notifier_from_errno(err);
+ err = dpaa2_switch_lag_changeupper(netdev, ptr);
+ if (err)
+ return notifier_from_errno(err);
+
break;
+ case NETDEV_CHANGELOWERSTATE:
+ info = ptr;
+ if (!dpaa2_switch_port_dev_check(netdev))
+ break;
+
+ err = dpaa2_switch_port_changelowerstate(netdev,
+ info->lower_state_info);
+ return notifier_from_errno(err);
}
return NOTIFY_DONE;
@@ -2316,80 +2920,97 @@ struct ethsw_switchdev_event_work {
struct work_struct work;
struct switchdev_notifier_fdb_info fdb_info;
struct net_device *dev;
+ struct net_device *orig_dev;
unsigned long event;
+ u16 vid;
};
static void dpaa2_switch_event_work(struct work_struct *work)
{
struct ethsw_switchdev_event_work *switchdev_work =
container_of(work, struct ethsw_switchdev_event_work, work);
+ struct net_device *orig_dev = switchdev_work->orig_dev;
struct net_device *dev = switchdev_work->dev;
+ struct ethsw_port_priv *port_priv = netdev_priv(dev);
struct switchdev_notifier_fdb_info *fdb_info;
+ struct dpaa2_switch_lag *lag;
int err;
- rtnl_lock();
fdb_info = &switchdev_work->fdb_info;
+ /* The lag structures are freed only from dpaa2_switch_remove(), which
+ * first flushes this workqueue, so the pointer stays valid for the
+ * lifetime of the work item. Only the dereference needs the RCU
+ * read-side lock; the FDB helpers below can sleep and must run outside
+ * of it.
+ */
+ rcu_read_lock();
+ lag = rcu_dereference(port_priv->lag);
+ rcu_read_unlock();
+
switch (switchdev_work->event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
- if (!fdb_info->added_by_user || fdb_info->is_local)
- break;
- if (is_unicast_ether_addr(fdb_info->addr))
- err = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev),
- fdb_info->addr);
+ if (lag)
+ err = dpaa2_switch_lag_fdb_add(lag, fdb_info->addr,
+ switchdev_work->vid);
else
- err = dpaa2_switch_port_fdb_add_mc(netdev_priv(dev),
- fdb_info->addr);
+ err = dpaa2_switch_port_fdb_add(port_priv,
+ fdb_info->addr);
if (err)
break;
fdb_info->offloaded = true;
- call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
+ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, orig_dev,
&fdb_info->info, NULL);
break;
case SWITCHDEV_FDB_DEL_TO_DEVICE:
- if (!fdb_info->added_by_user || fdb_info->is_local)
- break;
- if (is_unicast_ether_addr(fdb_info->addr))
- dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
+ if (lag)
+ dpaa2_switch_lag_fdb_del(lag, fdb_info->addr,
+ switchdev_work->vid);
else
- dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr);
+ dpaa2_switch_port_fdb_del(port_priv, fdb_info->addr);
break;
}
- rtnl_unlock();
kfree(switchdev_work->fdb_info.addr);
kfree(switchdev_work);
dev_put(dev);
+ dev_put(orig_dev);
}
-/* Called under rcu_read_lock() */
-static int dpaa2_switch_port_event(struct notifier_block *nb,
- unsigned long event, void *ptr)
+static int
+dpaa2_switch_port_fdb_event(struct net_device *dev,
+ struct net_device *orig_dev,
+ unsigned long event, const void *ctx,
+ const struct switchdev_notifier_fdb_info *fdb_info)
{
- struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
struct ethsw_port_priv *port_priv = netdev_priv(dev);
struct ethsw_switchdev_event_work *switchdev_work;
- struct switchdev_notifier_fdb_info *fdb_info = ptr;
struct ethsw_core *ethsw = port_priv->ethsw_data;
- if (event == SWITCHDEV_PORT_ATTR_SET)
- return dpaa2_switch_port_attr_set_event(dev, ptr);
+ if (ctx && ctx != port_priv)
+ return 0;
- if (!dpaa2_switch_port_dev_check(dev))
- return NOTIFY_DONE;
+ /* For the moment, do nothing with entries towards foreign devices */
+ if (dpaa2_switch_foreign_dev_check(dev, orig_dev))
+ return 0;
+
+ if (!fdb_info->added_by_user || fdb_info->is_local)
+ return 0;
switchdev_work = kzalloc_obj(*switchdev_work, GFP_ATOMIC);
if (!switchdev_work)
- return NOTIFY_BAD;
+ return -ENOMEM;
INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work);
switchdev_work->dev = dev;
switchdev_work->event = event;
+ switchdev_work->orig_dev = orig_dev;
+ switchdev_work->vid = fdb_info->vid;
switch (event) {
case SWITCHDEV_FDB_ADD_TO_DEVICE:
case SWITCHDEV_FDB_DEL_TO_DEVICE:
- memcpy(&switchdev_work->fdb_info, ptr,
+ memcpy(&switchdev_work->fdb_info, fdb_info,
sizeof(switchdev_work->fdb_info));
switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
if (!switchdev_work->fdb_info.addr)
@@ -2400,52 +3021,61 @@ static int dpaa2_switch_port_event(struct notifier_block *nb,
/* Take a reference on the device to avoid being freed. */
dev_hold(dev);
+ dev_hold(orig_dev);
break;
default:
kfree(switchdev_work);
- return NOTIFY_DONE;
+ return 0;
}
queue_work(ethsw->workqueue, &switchdev_work->work);
- return NOTIFY_DONE;
+ return 0;
err_addr_alloc:
kfree(switchdev_work);
- return NOTIFY_BAD;
+ return -ENOMEM;
}
-static int dpaa2_switch_port_obj_event(unsigned long event,
- struct net_device *netdev,
- struct switchdev_notifier_port_obj_info *port_obj_info)
+/* Called under rcu_read_lock() */
+static int dpaa2_switch_port_event(struct notifier_block *nb,
+ unsigned long event, void *ptr)
{
- int err = -EOPNOTSUPP;
-
- if (!dpaa2_switch_port_dev_check(netdev))
- return NOTIFY_DONE;
+ struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ int err;
switch (event) {
- case SWITCHDEV_PORT_OBJ_ADD:
- err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj);
- break;
- case SWITCHDEV_PORT_OBJ_DEL:
- err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj);
- break;
+ case SWITCHDEV_PORT_ATTR_SET:
+ return dpaa2_switch_port_attr_set_event(dev, ptr);
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ err = switchdev_handle_fdb_event_to_device(dev, event, ptr,
+ dpaa2_switch_port_dev_check,
+ dpaa2_switch_foreign_dev_check,
+ dpaa2_switch_port_fdb_event);
+ return notifier_from_errno(err);
+ default:
+ return NOTIFY_DONE;
}
-
- port_obj_info->handled = true;
- return notifier_from_errno(err);
}
static int dpaa2_switch_port_blocking_event(struct notifier_block *nb,
unsigned long event, void *ptr)
{
struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+ int err;
switch (event) {
case SWITCHDEV_PORT_OBJ_ADD:
+ err = switchdev_handle_port_obj_add(dev, ptr,
+ dpaa2_switch_port_dev_check,
+ dpaa2_switch_port_obj_add);
+ return notifier_from_errno(err);
case SWITCHDEV_PORT_OBJ_DEL:
- return dpaa2_switch_port_obj_event(event, dev, ptr);
+ err = switchdev_handle_port_obj_del(dev, ptr,
+ dpaa2_switch_port_dev_check,
+ dpaa2_switch_port_obj_del);
+ return notifier_from_errno(err);
case SWITCHDEV_PORT_ATTR_SET:
return dpaa2_switch_port_attr_set_event(dev, ptr);
}
@@ -2490,19 +3120,22 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,
dma_addr_t addr = dpaa2_fd_get_addr(fd);
struct ethsw_core *ethsw = fq->ethsw;
struct ethsw_port_priv *port_priv;
+ struct dpaa2_switch_lag *lag;
struct net_device *netdev;
struct vlan_ethhdr *hdr;
struct sk_buff *skb;
u16 vlan_tci, vid;
int if_id, err;
void *vaddr;
+ u64 flc;
vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, addr);
dma_unmap_page(ethsw->dev, addr, DPAA2_SWITCH_RX_BUF_SIZE,
DMA_FROM_DEVICE);
/* get switch ingress interface ID */
- if_id = upper_32_bits(dpaa2_fd_get_flc(fd)) & 0x0000FFFF;
+ flc = dpaa2_fd_get_flc(fd);
+ if_id = DPAA2_ETHSW_FLC_IF_ID(flc);
if (if_id >= ethsw->sw_attr.num_ifs) {
dev_err(ethsw->dev, "Frame received from unknown interface!\n");
goto err_free_fd;
@@ -2541,12 +3174,20 @@ static void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,
}
}
- skb->dev = netdev;
+ rcu_read_lock();
+
+ lag = rcu_dereference(port_priv->lag);
+ if (DPAA2_ETHSW_FLC_IMPRECISE_IF_ID(flc) && lag)
+ skb->dev = lag->bond_dev;
+ else
+ skb->dev = netdev;
skb->protocol = eth_type_trans(skb, skb->dev);
/* Setup the offload_fwd_mark only if the port is under a bridge */
skb->offload_fwd_mark = !!(port_priv->fdb->bridge_dev);
+ rcu_read_unlock();
+
netif_receive_skb(skb);
return;
@@ -2561,6 +3202,9 @@ static void dpaa2_switch_detect_features(struct ethsw_core *ethsw)
if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 6))
ethsw->features |= ETHSW_FEATURE_MAC_ADDR;
+
+ if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 13))
+ ethsw->features |= ETHSW_FEATURE_LAG_OFFLOAD;
}
static int dpaa2_switch_setup_fqs(struct ethsw_core *ethsw)
@@ -3195,17 +3839,15 @@ static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
return err;
}
-/* Add an ACL to redirect frames with specific destination MAC address to
- * control interface
- */
+/* Add an ACL to redirect frames to control interface based on the dst MAC */
static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,
- const char *mac)
+ const u8 *mac, const u8 *mask)
{
struct dpaa2_switch_acl_entry acl_entry = {0};
/* Match on the destination MAC address */
ether_addr_copy(acl_entry.key.match.l2_dest_mac, mac);
- eth_broadcast_addr(acl_entry.key.mask.l2_dest_mac);
+ ether_addr_copy(acl_entry.key.mask.l2_dest_mac, mask);
/* Trap to CPU */
acl_entry.cfg.precedence = 0;
@@ -3216,7 +3858,8 @@ static int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,
static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
{
- const char stpa[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
+ const u8 ll_mac[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
+ const u8 ll_mask[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xf0};
struct switchdev_obj_port_vlan vlan = {
.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
.vid = DEFAULT_VLAN_ID,
@@ -3291,7 +3934,7 @@ static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
if (err)
return err;
- err = dpaa2_switch_port_trap_mac_addr(port_priv, stpa);
+ err = dpaa2_switch_port_trap_mac_addr(port_priv, ll_mac, ll_mask);
if (err)
return err;
@@ -3331,6 +3974,9 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)
dev = &sw_dev->dev;
ethsw = dev_get_drvdata(dev);
+ /* Make sure that all events were handled before we kfree anything */
+ flush_workqueue(ethsw->workqueue);
+
dpaa2_switch_teardown_irqs(sw_dev);
dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
@@ -3344,12 +3990,15 @@ static void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)
for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
netif_napi_del(ðsw->fq[i].napi);
- for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
dpaa2_switch_remove_port(ethsw, i);
+ mutex_destroy(ðsw->lags[i].fdb_lock);
+ }
kfree(ethsw->fdbs);
kfree(ethsw->filter_blocks);
kfree(ethsw->ports);
+ kfree(ethsw->lags);
dpaa2_switch_teardown(sw_dev);
@@ -3377,6 +4026,7 @@ static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
port_priv = netdev_priv(port_netdev);
port_priv->netdev = port_netdev;
port_priv->ethsw_data = ethsw;
+ rcu_assign_pointer(port_priv->lag, NULL);
mutex_init(&port_priv->mac_lock);
@@ -3484,6 +4134,21 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)
goto err_free_fdbs;
}
+ ethsw->lags = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->lags),
+ GFP_KERNEL);
+ if (!ethsw->lags) {
+ err = -ENOMEM;
+ goto err_free_filter;
+ }
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+ ethsw->lags[i].bond_dev = NULL;
+ ethsw->lags[i].ethsw = ethsw;
+ ethsw->lags[i].id = i + 1;
+ ethsw->lags[i].in_use = 0;
+ mutex_init(ðsw->lags[i].fdb_lock);
+ INIT_LIST_HEAD(ðsw->lags[i].fdbs);
+ }
+
for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
err = dpaa2_switch_probe_port(ethsw, i);
if (err)
@@ -3530,6 +4195,10 @@ static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)
err_free_netdev:
for (i--; i >= 0; i--)
dpaa2_switch_remove_port(ethsw, i);
+ for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+ mutex_destroy(ðsw->lags[i].fdb_lock);
+ kfree(ethsw->lags);
+err_free_filter:
kfree(ethsw->filter_blocks);
err_free_fdbs:
kfree(ethsw->fdbs);
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
index 42b3ca73f55d54..63b702b0000c2e 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
@@ -41,7 +41,8 @@
#define ETHSW_MAX_FRAME_LENGTH (DPAA2_MFL - VLAN_ETH_HLEN - ETH_FCS_LEN)
#define ETHSW_L2_MAX_FRM(mtu) ((mtu) + VLAN_ETH_HLEN + ETH_FCS_LEN)
-#define ETHSW_FEATURE_MAC_ADDR BIT(0)
+#define ETHSW_FEATURE_MAC_ADDR BIT(0)
+#define ETHSW_FEATURE_LAG_OFFLOAD BIT(1)
/* Number of receive queues (one RX and one TX_CONF) */
#define DPAA2_SWITCH_RX_NUM_FQS 2
@@ -86,6 +87,9 @@
#define DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE 256
+#define DPAA2_ETHSW_FLC_IF_ID(flc) (((flc) >> 32) & GENMASK(15, 0))
+#define DPAA2_ETHSW_FLC_IMPRECISE_IF_ID(flc) ((flc) & BIT_ULL(63))
+
extern const struct ethtool_ops dpaa2_switch_port_ethtool_ops;
struct ethsw_core;
@@ -99,12 +103,30 @@ struct dpaa2_switch_fq {
u32 fqid;
};
+struct dpaa2_mac_addr {
+ unsigned char addr[ETH_ALEN];
+ u16 vid;
+ refcount_t refcount;
+ struct list_head list;
+};
+
struct dpaa2_switch_fdb {
struct net_device *bridge_dev;
u16 fdb_id;
bool in_use;
};
+struct dpaa2_switch_lag {
+ struct ethsw_core *ethsw;
+ struct net_device *bond_dev;
+ bool in_use;
+ u8 id;
+ struct ethsw_port_priv *primary;
+ /* Protects the list of fdbs installed on this LAG */
+ struct mutex fdb_lock;
+ struct list_head fdbs;
+};
+
struct dpaa2_switch_acl_entry {
struct list_head list;
u16 prio;
@@ -163,6 +185,8 @@ struct ethsw_port_priv {
struct dpaa2_mac *mac;
/* Protects against changes to port_priv->mac */
struct mutex mac_lock;
+
+ struct dpaa2_switch_lag __rcu *lag;
};
/* Switch data */
@@ -190,6 +214,8 @@ struct ethsw_core {
struct dpaa2_switch_fdb *fdbs;
struct dpaa2_switch_filter_block *filter_blocks;
u16 mirror_port;
+
+ struct dpaa2_switch_lag *lags;
};
static inline int dpaa2_switch_get_index(struct ethsw_core *ethsw,
@@ -274,4 +300,18 @@ int dpaa2_switch_block_offload_mirror(struct dpaa2_switch_filter_block *block,
int dpaa2_switch_block_unoffload_mirror(struct dpaa2_switch_filter_block *block,
struct ethsw_port_priv *port_priv);
+
+static inline bool
+dpaa2_switch_port_offloads_bridge_port(struct ethsw_port_priv *port_priv,
+ const struct net_device *dev)
+{
+ struct dpaa2_switch_lag *lag = rcu_dereference_rtnl(port_priv->lag);
+
+ if (lag && lag->bond_dev == dev)
+ return true;
+ if (port_priv->netdev == dev)
+ return true;
+ return false;
+}
+
#endif /* __ETHSW_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
index 397d55f2bd9950..9a2055c64983ce 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw-cmd.h
@@ -12,7 +12,7 @@
/* DPSW Version */
#define DPSW_VER_MAJOR 8
-#define DPSW_VER_MINOR 9
+#define DPSW_VER_MINOR 13
#define DPSW_CMD_BASE_VERSION 1
#define DPSW_CMD_VERSION_2 2
@@ -92,11 +92,14 @@
#define DPSW_CMDID_CTRL_IF_SET_POOLS DPSW_CMD_ID(0x0A1)
#define DPSW_CMDID_CTRL_IF_ENABLE DPSW_CMD_ID(0x0A2)
#define DPSW_CMDID_CTRL_IF_DISABLE DPSW_CMD_ID(0x0A3)
+#define DPSW_CMDID_SET_LAG DPSW_CMD_V2(0x0A4)
#define DPSW_CMDID_CTRL_IF_SET_QUEUE DPSW_CMD_ID(0x0A6)
#define DPSW_CMDID_SET_EGRESS_FLOOD DPSW_CMD_ID(0x0AC)
#define DPSW_CMDID_IF_SET_LEARNING_MODE DPSW_CMD_ID(0x0AD)
+#define DPSW_CMDID_IF_SET_LAG_STATE DPSW_CMD_ID(0x0B0)
+
/* Macros for accessing command fields smaller than 1byte */
#define DPSW_MASK(field) \
GENMASK(DPSW_##field##_SHIFT + DPSW_##field##_SIZE - 1, \
@@ -552,5 +555,18 @@ struct dpsw_cmd_if_reflection {
/* only 2 bits from the LSB */
u8 filter;
};
+
+struct dpsw_cmd_lag {
+ u8 group_id;
+ u8 num_ifs;
+ u8 pad[6];
+ u8 if_id[DPSW_MAX_LAG_IFS];
+ u8 phase;
+};
+
+struct dpsw_cmd_if_set_lag_state {
+ __le16 if_id;
+ u8 tx_enabled;
+};
#pragma pack(pop)
#endif /* __FSL_DPSW_CMD_H */
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.c b/drivers/net/ethernet/freescale/dpaa2/dpsw.c
index ab921d75deb2e8..f75cbdce42ba2f 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpsw.c
+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.c
@@ -1659,3 +1659,63 @@ int dpsw_if_remove_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
return mc_send_command(mc_io, &cmd);
}
+
+/**
+ * dpsw_lag_set() - Set LAG configuration
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @cfg: pointer to LAG configuration
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_lag_set(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ const struct dpsw_lag_cfg *cfg)
+{
+ struct fsl_mc_command cmd = { 0 };
+ struct dpsw_cmd_lag *cmd_params;
+ int i = 0;
+
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_SET_LAG, cmd_flags, token);
+
+ if (cfg->num_ifs > DPSW_MAX_LAG_IFS)
+ return -EOPNOTSUPP;
+
+ cmd_params = (struct dpsw_cmd_lag *)cmd.params;
+ cmd_params->group_id = cfg->group_id;
+ cmd_params->num_ifs = cfg->num_ifs;
+ cmd_params->phase = cfg->phase;
+
+ for (i = 0; i < cfg->num_ifs; i++)
+ cmd_params->if_id[i] = cfg->if_id[i];
+
+ return mc_send_command(mc_io, &cmd);
+}
+
+/**
+ * dpsw_if_set_lag_state() - Change per port LAG state
+ * @mc_io: Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token: Token of DPSW object
+ * @if_id: ID of the switch interface
+ * @tx_enabled: Value of the per port LAG state
+ * - 0 if the interface will not be active as part of the LAG group
+ * - 1 if the interface will be active in the LAG group
+ *
+ * Return: '0' on Success; Error code otherwise.
+ */
+int dpsw_if_set_lag_state(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ u16 if_id, u8 tx_enabled)
+{
+ struct dpsw_cmd_if_set_lag_state *cmd_params;
+ struct fsl_mc_command cmd = { 0 };
+
+ cmd.header = mc_encode_cmd_header(DPSW_CMDID_IF_SET_LAG_STATE,
+ cmd_flags, token);
+
+ cmd_params = (struct dpsw_cmd_if_set_lag_state *)cmd.params;
+ cmd_params->if_id = cpu_to_le16(if_id);
+ cmd_params->tx_enabled = tx_enabled;
+
+ return mc_send_command(mc_io, &cmd);
+}
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpsw.h b/drivers/net/ethernet/freescale/dpaa2/dpsw.h
index b90bd363f47a19..89f0267de8e9bd 100644
--- a/drivers/net/ethernet/freescale/dpaa2/dpsw.h
+++ b/drivers/net/ethernet/freescale/dpaa2/dpsw.h
@@ -20,6 +20,8 @@ struct fsl_mc_io;
#define DPSW_MAX_IF 64
+#define DPSW_MAX_LAG_IFS 8
+
int dpsw_open(struct fsl_mc_io *mc_io, u32 cmd_flags, int dpsw_id, u16 *token);
int dpsw_close(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token);
@@ -788,4 +790,32 @@ int dpsw_if_add_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
int dpsw_if_remove_reflection(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
u16 if_id, const struct dpsw_reflection_cfg *cfg);
+
+/* Link Aggregation Group configuration */
+
+#define DPSW_LAG_SET_PHASE_APPLY 0
+#define DPSW_LAG_SET_PHASE_CHECK 1
+
+/**
+ * struct dpsw_lag_cfg - Configuration structure for a LAG group
+ * @group_id: Link aggregation group ID. Valid values are in the
+ * [1, DPSW_MAX_LAG_IFS] range.
+ * @num_ifs: Number of interfaces in this LAG group, valid range is
+ * [0, DPSW_MAX_LAG_IFS].
+ * @if_id: Array containing the interface IDs of the ports part of a LAG group
+ * @phase: Use DPSW_LAG_SET_PHASE_APPLY for LAG configuration processing or
+ * DPSW_LAG_SET_PHASE_CHECK for LAG configuration validation.
+ */
+struct dpsw_lag_cfg {
+ u8 group_id;
+ u8 num_ifs;
+ u8 if_id[DPSW_MAX_LAG_IFS];
+ u8 phase;
+};
+
+int dpsw_lag_set(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ const struct dpsw_lag_cfg *cfg);
+
+int dpsw_if_set_lag_state(struct fsl_mc_io *mc_io, u32 cmd_flags, u16 token,
+ u16 if_id, u8 tx_enabled);
#endif /* __FSL_DPSW_H */
|
| 3/2 | 2026/06/29 11:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 22166 output: 26 thoughts: 734 Thoughts: **Analyzing the Reachability of DPAA2 LAG Offload Code** Okay, so this patch introduces Link Aggregation Group (LAG) offload support to the Freescale DPAA2 switch driver, which is located in `drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c`. It's implementing new functions like `dpaa2_switch_lag_get_unused`, `dpaa2_switch_lag_fdb_add`, and modifications to existing ones like `dpaa2_switch_port_netdevice_event` to handle `NETDEV_CHANGEUPPER` and `NETDEV_CHANGELOWERSTATE`. The core of the driver seems to be interacting directly with the hardware, a Freescale DPAA2 switch. My first thought is, can I even *test* this in a virtualized environment? This driver is specific to the DPAA2 architecture, typically found on ARM64-based NXP Layerscape SoCs. I need to figure out if this code is reachable in QEMU or Google Compute Engine (GCE), which are my usual testing grounds. The `dpaa2_switch_probe` function is called when a device matching the fsl-mc bus is detected. Now, the fsl-mc bus is a NXP-specific bus. I have to see if there's any software emulation of that bus within QEMU. Usually, I'd check a standard syzkaller setup, but I doubt it has any NXP-specific emulation. I'll check the kernel configuration. Are `CONFIG_FSL_MC_BUS` and `CONFIG_FSL_DPAA2_SWITCH` even enabling anything meaningful without the actual hardware? Since syzkaller isn't set up for NXP hardware, the question is, how can I even reach this code? I need to confirm the dependencies in `dpaa2-switch.c`. Ah, yes, it *depends on* `FSL_MC_BUS` and `FSL_DPAA2_SWITCH`. So, it looks like `dpaa2_switch_probe` *requires* the fsl-mc bus to be present to run. The fsl-mc bus devices are typically populated by the firmware or the device tree on an NXP SoC. Okay, I think I have it. It's essentially impossible to reach this code in a virtualized environment like QEMU or GCE *without* specific hardware emulation of the fsl-mc bus and DPAA2 hardware. This would mean I would have to create custom emulation specific to the DPAA2 hardware to reach this new code, which is beyond the scope of a standard fuzzing effort. Let's double-check. I'll use `grepper` to search through `drivers/` and `tools/` for mentions of `fsl-mc` or `dpaa2` to see if there's *any* mock implementation or any software emulation for the device, but I am not very hopeful. This patch should therefore have `WorthFuzzing=false`. |
| 4/2 | 2026/06/29 11:52 | tool | grepper |
0mArgs:{
"Expression": "fsl-mc",
"PathPrefix": "drivers/"
}Results: {
"Output": "drivers/bus/Kconfig=242=config DA8XX_MSTPRI\n--\ndrivers/bus/Kconfig-249-\ndrivers/bus/Kconfig:250:source \"drivers/bus/fsl-mc/Kconfig\"\ndrivers/bus/Kconfig-251-source \"drivers/bus/mhi/Kconfig\"\n--\ndrivers/bus/Makefile=11=obj-$(CONFIG_MOXTET)\t\t+= moxtet.o\ndrivers/bus/Makefile-12-\ndrivers/bus/Makefile:13:# DPAA2 fsl-mc bus\ndrivers/bus/Makefile:14:obj-$(CONFIG_FSL_MC_BUS)\t+= fsl-mc/\ndrivers/bus/Makefile-15-\n--\ndrivers/bus/fsl-mc/Kconfig-2-#\ndrivers/bus/fsl-mc/Kconfig:3:# DPAA2 fsl-mc bus\ndrivers/bus/fsl-mc/Kconfig-4-#\n--\ndrivers/bus/fsl-mc/Kconfig=8=config FSL_MC_BUS\ndrivers/bus/fsl-mc/Kconfig:9:\tbool \"QorIQ DPAA2 fsl-mc bus driver\"\ndrivers/bus/fsl-mc/Kconfig-10-\tdepends on OF \u0026\u0026 (ARCH_LAYERSCAPE || (COMPILE_TEST \u0026\u0026 (ARM || ARM64 || X86_LOCAL_APIC || PPC)))\n--\ndrivers/bus/fsl-mc/Kconfig-13-\t Driver to enable the bus infrastructure for the QorIQ DPAA2\ndrivers/bus/fsl-mc/Kconfig:14:\t architecture. The fsl-mc bus driver handles discovery of\ndrivers/bus/fsl-mc/Kconfig-15-\t DPAA2 objects (which are represented as Linux devices) and\n--\ndrivers/bus/fsl-mc/Makefile=7=obj-$(CONFIG_FSL_MC_BUS) += mc-bus-driver.o\ndrivers/bus/fsl-mc/Makefile-8-\ndrivers/bus/fsl-mc/Makefile:9:mc-bus-driver-objs := fsl-mc-bus.o \\\ndrivers/bus/fsl-mc/Makefile-10-\t\t mc-sys.o \\\n--\ndrivers/bus/fsl-mc/Makefile-15-\t\t dprc-driver.o \\\ndrivers/bus/fsl-mc/Makefile:16:\t\t fsl-mc-allocator.o \\\ndrivers/bus/fsl-mc/Makefile:17:\t\t fsl-mc-msi.o \\\ndrivers/bus/fsl-mc/Makefile-18-\t\t dpmcp.o \\\n--\ndrivers/bus/fsl-mc/Makefile-21-# MC userspace support\ndrivers/bus/fsl-mc/Makefile:22:obj-$(CONFIG_FSL_MC_UAPI_SUPPORT) += fsl-mc-uapi.o\n--\ndrivers/bus/fsl-mc/dpbp.c-8-\ndrivers/bus/fsl-mc/dpbp.c:9:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/dpbp.c-10-\n--\ndrivers/bus/fsl-mc/dpcon.c-8-\ndrivers/bus/fsl-mc/dpcon.c:9:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/dpcon.c-10-\n--\ndrivers/bus/fsl-mc/dpmcp.c-8-\ndrivers/bus/fsl-mc/dpmcp.c:9:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/dpmcp.c-10-\n--\ndrivers/bus/fsl-mc/dprc-driver.c-15-\ndrivers/bus/fsl-mc/dprc-driver.c:16:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/dprc-driver.c-17-\n--\ndrivers/bus/fsl-mc/dprc-driver.c=68=static int __fsl_mc_device_remove(struct device *dev, void *data)\n--\ndrivers/bus/fsl-mc/dprc-driver.c-79- *\ndrivers/bus/fsl-mc/dprc-driver.c:80: * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object\ndrivers/bus/fsl-mc/dprc-driver.c-81- * @obj_desc_array: array of object descriptors for child objects currently\n--\ndrivers/bus/fsl-mc/dprc-driver.c=123=struct fsl_mc_device *fsl_mc_device_lookup(struct fsl_mc_obj_desc *obj_desc,\n--\ndrivers/bus/fsl-mc/dprc-driver.c-136- *\ndrivers/bus/fsl-mc/dprc-driver.c:137: * @mc_dev: pointer to the fsl-mc device for a given MC object\ndrivers/bus/fsl-mc/dprc-driver.c-138- * @obj_desc: pointer to the MC object's descriptor in the MC\ndrivers/bus/fsl-mc/dprc-driver.c-139- *\ndrivers/bus/fsl-mc/dprc-driver.c:140: * If the plugged state has changed from unplugged to plugged, the fsl-mc\ndrivers/bus/fsl-mc/dprc-driver.c-141- * device is bound to the corresponding device driver.\ndrivers/bus/fsl-mc/dprc-driver.c:142: * If the plugged state has changed from plugged to unplugged, the fsl-mc\ndrivers/bus/fsl-mc/dprc-driver.c-143- * device is unbound from the corresponding device driver.\n--\ndrivers/bus/fsl-mc/dprc-driver.c=169=static void fsl_mc_obj_device_add(struct fsl_mc_device *mc_bus_dev,\n--\ndrivers/bus/fsl-mc/dprc-driver.c-192- *\ndrivers/bus/fsl-mc/dprc-driver.c:193: * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object\ndrivers/bus/fsl-mc/dprc-driver.c-194- * @obj_desc_array: array of device descriptors for child devices currently\n--\ndrivers/bus/fsl-mc/dprc-driver.c=202=static void dprc_add_new_devices(struct fsl_mc_device *mc_bus_dev,\n--\ndrivers/bus/fsl-mc/dprc-driver.c-228- *\ndrivers/bus/fsl-mc/dprc-driver.c:229: * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object\ndrivers/bus/fsl-mc/dprc-driver.c-230- * @alloc_interrupts: if true the function allocates the interrupt pool,\n--\ndrivers/bus/fsl-mc/dprc-driver.c=245=int dprc_scan_objects(struct fsl_mc_device *mc_bus_dev,\n--\ndrivers/bus/fsl-mc/dprc-driver.c-356- *\ndrivers/bus/fsl-mc/dprc-driver.c:357: * @mc_bus_dev: pointer to the fsl-mc device that represents a DPRC object\ndrivers/bus/fsl-mc/dprc-driver.c-358- * @alloc_interrupts: if true the function allocates the interrupt pool,\n--\ndrivers/bus/fsl-mc/dprc-driver.c=572=static int dprc_setup_irq(struct fsl_mc_device *mc_dev)\n--\ndrivers/bus/fsl-mc/dprc-driver.c-601- *\ndrivers/bus/fsl-mc/dprc-driver.c:602: * @mc_dev: Pointer to fsl-mc device representing a DPRC\ndrivers/bus/fsl-mc/dprc-driver.c-603- *\n--\ndrivers/bus/fsl-mc/dprc-driver.c=711=EXPORT_SYMBOL_GPL(dprc_setup);\n--\ndrivers/bus/fsl-mc/dprc-driver.c-715- *\ndrivers/bus/fsl-mc/dprc-driver.c:716: * @mc_dev: Pointer to fsl-mc device representing a DPRC\ndrivers/bus/fsl-mc/dprc-driver.c-717- *\n--\ndrivers/bus/fsl-mc/dprc-driver.c=758=static void dprc_teardown_irq(struct fsl_mc_device *mc_dev)\n--\ndrivers/bus/fsl-mc/dprc-driver.c-771- *\ndrivers/bus/fsl-mc/dprc-driver.c:772: * @mc_dev: Pointer to fsl-mc device representing the DPRC\ndrivers/bus/fsl-mc/dprc-driver.c-773- *\n--\ndrivers/bus/fsl-mc/dprc-driver.c=815=EXPORT_SYMBOL_GPL(dprc_cleanup);\n--\ndrivers/bus/fsl-mc/dprc-driver.c-819- *\ndrivers/bus/fsl-mc/dprc-driver.c:820: * @mc_dev: Pointer to fsl-mc device representing the DPRC\ndrivers/bus/fsl-mc/dprc-driver.c-821- *\n--\ndrivers/bus/fsl-mc/dprc.c-9-\ndrivers/bus/fsl-mc/dprc.c:10:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/dprc.c-11-\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-2-/*\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:3: * fsl-mc object allocator driver\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-4- *\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-12-\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:13:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-14-\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=15=static bool __must_check fsl_mc_is_allocatable(struct fsl_mc_device *mc_dev)\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-23- * fsl_mc_resource_pool_add_device - add allocatable object to a resource\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:24: * pool of a given fsl-mc bus\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-25- *\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:26: * @mc_bus: pointer to the fsl-mc bus\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-27- * @pool_type: pool type\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:28: * @mc_dev: pointer to allocatable fsl-mc device\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-29- */\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=30=static int __must_check fsl_mc_resource_pool_add_device(struct fsl_mc_bus\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-91- *\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:92: * @mc_dev: pointer to allocatable fsl-mc device\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-93- *\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:94: * It permanently removes an allocatable fsl-mc device from the resource\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-95- * pool. It's an error if the device is in use.\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=254=EXPORT_SYMBOL_GPL(fsl_mc_resource_free);\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-256-/**\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:257: * fsl_mc_object_allocate - Allocates an fsl-mc object of the given\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:258: * pool type from a given fsl-mc bus instance\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-259- *\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:260: * @mc_dev: fsl-mc device which is used in conjunction with the\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-261- * allocated object\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=322=EXPORT_SYMBOL_GPL(fsl_mc_object_allocate);\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-324-/**\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:325: * fsl_mc_object_free - Returns an fsl-mc object to the resource\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-326- * pool where it came from.\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:327: * @mc_adev: Pointer to the fsl-mc device\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-328- */\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=343=EXPORT_SYMBOL_GPL(fsl_mc_object_free);\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-351-/*\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:352: * Initialize the interrupt pool associated with an fsl-mc bus.\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-353- * It allocates a block of IRQs from the GIC-ITS.\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=411=EXPORT_SYMBOL_GPL(fsl_mc_populate_irq_pool);\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-413-/*\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:414: * Teardown the interrupt pool associated with an fsl-mc bus.\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-415- * It frees the IRQs that were allocated to the pool, back to the GIC-ITS.\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=438=EXPORT_SYMBOL_GPL(fsl_mc_cleanup_irq_pool);\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-440-/*\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:441: * Allocate the IRQs required by a given fsl-mc device.\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-442- */\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=506=EXPORT_SYMBOL_GPL(fsl_mc_allocate_irqs);\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-508-/*\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:509: * Frees the IRQs that were allocated for an fsl-mc device.\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-510- */\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=562=static int fsl_mc_allocator_probe(struct fsl_mc_device *mc_dev)\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-585-\tdev_dbg(\u0026mc_dev-\u003edev,\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:586:\t\t\"Allocatable fsl-mc device bound to fsl_mc_allocator driver\");\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-587-\treturn 0;\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c=594=static void fsl_mc_allocator_remove(struct fsl_mc_device *mc_dev)\n--\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-604-\tdev_dbg(\u0026mc_dev-\u003edev,\ndrivers/bus/fsl-mc/fsl-mc-allocator.c:605:\t\t\"Allocatable fsl-mc device unbound from fsl_mc_allocator driver\");\ndrivers/bus/fsl-mc/fsl-mc-allocator.c-606-}\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-10-\ndrivers/bus/fsl-mc/fsl-mc-bus.c:11:#define pr_fmt(fmt) \"fsl-mc: \" fmt\ndrivers/bus/fsl-mc/fsl-mc-bus.c-12-\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-25-\ndrivers/bus/fsl-mc/fsl-mc-bus.c:26:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/fsl-mc-bus.c-27-\ndrivers/bus/fsl-mc/fsl-mc-bus.c-28-/*\ndrivers/bus/fsl-mc/fsl-mc-bus.c:29: * Default DMA mask for devices on a fsl-mc bus\ndrivers/bus/fsl-mc/fsl-mc-bus.c-30- */\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=33=static struct fsl_mc_version mc_version;\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-36- * struct fsl_mc - Private data of a \"fsl,qoriq-mc\" platform device\ndrivers/bus/fsl-mc/fsl-mc-bus.c:37: * @root_mc_bus_dev: fsl-mc device representing the root DPRC\ndrivers/bus/fsl-mc/fsl-mc-bus.c-38- * @num_translation_ranges: number of entries in addr_translation_ranges\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=80=static phys_addr_t mc_portal_base_phys_addr;\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-83- * fsl_mc_bus_match - device to driver matching callback\ndrivers/bus/fsl-mc/fsl-mc-bus.c:84: * @dev: the fsl-mc device to match against\ndrivers/bus/fsl-mc/fsl-mc-bus.c:85: * @drv: the device driver to search for matching fsl-mc object type\ndrivers/bus/fsl-mc/fsl-mc-bus.c-86- * structures\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=139=static int fsl_mc_bus_uevent(const struct device *dev, struct kobj_uevent_env *env)\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-142-\ndrivers/bus/fsl-mc/fsl-mc-bus.c:143:\tif (add_uevent_var(env, \"MODALIAS=fsl-mc:v%08Xd%s\",\ndrivers/bus/fsl-mc/fsl-mc-bus.c-144-\t\t\t mc_dev-\u003eobj_desc.vendor,\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=214=static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-218-\ndrivers/bus/fsl-mc/fsl-mc-bus.c:219:\treturn sysfs_emit(buf, \"fsl-mc:v%08Xd%s\\n\", mc_dev-\u003eobj_desc.vendor,\ndrivers/bus/fsl-mc/fsl-mc-bus.c-220-\t\t\tmc_dev-\u003eobj_desc.type);\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=326=const struct bus_type fsl_mc_bus_type = {\ndrivers/bus/fsl-mc/fsl-mc-bus.c:327:\t.name = \"fsl-mc\",\ndrivers/bus/fsl-mc/fsl-mc-bus.c-328-\t.driver_override = true,\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=499=static int mc_get_version(struct fsl_mc_io *mc_io,\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-528- *\ndrivers/bus/fsl-mc/fsl-mc-bus.c:529: * Return:\tmc version when called after fsl-mc-bus probe; NULL otherwise.\ndrivers/bus/fsl-mc/fsl-mc-bus.c-530- */\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=632=static int fsl_mc_device_get_mmio_regions(struct fsl_mc_device *mc_dev,\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-710-\t\tregions[i].end = regions[i].start + region_desc.size - 1;\ndrivers/bus/fsl-mc/fsl-mc-bus.c:711:\t\tregions[i].name = \"fsl-mc object MMIO region\";\ndrivers/bus/fsl-mc/fsl-mc-bus.c-712-\t\tregions[i].flags = region_desc.flags \u0026 IORESOURCE_BITS;\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=737=static void fsl_mc_device_release(struct device *dev)\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-749-/*\ndrivers/bus/fsl-mc/fsl-mc-bus.c:750: * Add a newly discovered fsl-mc device to be visible in Linux\ndrivers/bus/fsl-mc/fsl-mc-bus.c-751- */\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=882=static struct notifier_block fsl_mc_nb;\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-884-/**\ndrivers/bus/fsl-mc/fsl-mc-bus.c:885: * fsl_mc_device_remove - Remove an fsl-mc device from being visible to\ndrivers/bus/fsl-mc/fsl-mc-bus.c-886- * Linux\ndrivers/bus/fsl-mc/fsl-mc-bus.c-887- *\ndrivers/bus/fsl-mc/fsl-mc-bus.c:888: * @mc_dev: Pointer to an fsl-mc device\ndrivers/bus/fsl-mc/fsl-mc-bus.c-889- */\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=900=struct fsl_mc_device *fsl_mc_get_endpoint(struct fsl_mc_device *mc_dev,\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-936-\t * interrogating the firmware. This is the case when the device was not\ndrivers/bus/fsl-mc/fsl-mc-bus.c:937:\t * yet discovered by the fsl-mc bus, thus the lookup returned NULL.\ndrivers/bus/fsl-mc/fsl-mc-bus.c-938-\t * Force a rescan of the devices in this container and retry the lookup.\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c=1005=static int fsl_mc_firmware_check(struct platform_device *pdev)\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-1013-\t\tdev_err(\u0026pdev-\u003edev,\ndrivers/bus/fsl-mc/fsl-mc-bus.c:1014:\t\t\t\"fsl-mc: DPL processing was not started, DPAA2 will not work!\\n\");\ndrivers/bus/fsl-mc/fsl-mc-bus.c-1015-\t\treturn -EOPNOTSUPP;\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-1020-\t\tdev_dbg(\u0026pdev-\u003edev,\ndrivers/bus/fsl-mc/fsl-mc-bus.c:1021:\t\t\t\"fsl-mc: DPL processing in progress, defer probe\\n\");\ndrivers/bus/fsl-mc/fsl-mc-bus.c-1022-\t\treturn -EPROBE_DEFER;\n--\ndrivers/bus/fsl-mc/fsl-mc-bus.c-1027-\t\tdev_err(\u0026pdev-\u003edev,\ndrivers/bus/fsl-mc/fsl-mc-bus.c:1028:\t\t\t\"fsl-mc: MC boot completed with error 0x%x\\n\", mcs);\ndrivers/bus/fsl-mc/fsl-mc-bus.c-1029-\t\treturn -EINVAL;\n--\ndrivers/bus/fsl-mc/fsl-mc-msi.c-15-\ndrivers/bus/fsl-mc/fsl-mc-msi.c:16:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/fsl-mc-msi.c-17-\n--\ndrivers/bus/fsl-mc/fsl-mc-private.h=582=struct fsl_mc_uapi {\n--\ndrivers/bus/fsl-mc/fsl-mc-private.h-591- * struct fsl_mc_bus - logical bus that corresponds to a physical DPRC\ndrivers/bus/fsl-mc/fsl-mc-private.h:592: * @mc_dev: fsl-mc device for the bus device itself.\ndrivers/bus/fsl-mc/fsl-mc-private.h-593- * @resource_pools: array of resource pools (one pool per resource type)\n--\ndrivers/bus/fsl-mc/fsl-mc-uapi.c-13-\ndrivers/bus/fsl-mc/fsl-mc-uapi.c:14:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/fsl-mc-uapi.c-15-\n--\ndrivers/bus/fsl-mc/mc-io.c-9-\ndrivers/bus/fsl-mc/mc-io.c:10:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/mc-io.c-11-\n--\ndrivers/bus/fsl-mc/mc-sys.c-16-\ndrivers/bus/fsl-mc/mc-sys.c:17:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/mc-sys.c-18-\n--\ndrivers/bus/fsl-mc/obj-api.c-8-\ndrivers/bus/fsl-mc/obj-api.c:9:#include \"fsl-mc-private.h\"\ndrivers/bus/fsl-mc/obj-api.c-10-\n--\ndrivers/crypto/caam/Kconfig=178=config CRYPTO_DEV_FSL_DPAA2_CAAM\n--\ndrivers/crypto/caam/Kconfig-193-\t It handles DPSECI DPAA2 objects that sit on the Management Complex\ndrivers/crypto/caam/Kconfig:194:\t (MC) fsl-mc bus.\ndrivers/crypto/caam/Kconfig-195-\n--\ndrivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c=152=dpaa2_qdma_populate_fd(u32 format, struct dpaa2_qdma_comp *dpaa2_comp)\n--\ndrivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c-163-\t * Bypass memory translation, Frame list format, short length disable\ndrivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c:164:\t * we need to disable BMT if fsl-mc use iova addr\ndrivers/dma/fsl-dpaa2-qdma/dpaa2-qdma.c-165-\t */\n--\ndrivers/iommu/iommu.c=1685=EXPORT_SYMBOL_GPL(pci_device_group);\ndrivers/iommu/iommu.c-1686-\ndrivers/iommu/iommu.c:1687:/* Get the IOMMU group for device on fsl-mc bus */\ndrivers/iommu/iommu.c-1688-struct iommu_group *fsl_mc_device_group(struct device *dev)\n--\ndrivers/soc/fsl/dpio/Makefile-5-\ndrivers/soc/fsl/dpio/Makefile:6:obj-$(CONFIG_FSL_MC_DPIO) += fsl-mc-dpio.o\ndrivers/soc/fsl/dpio/Makefile-7-\ndrivers/soc/fsl/dpio/Makefile:8:fsl-mc-dpio-objs := dpio.o qbman-portal.o dpio-service.o dpio-driver.o\n--\ndrivers/soc/fsl/dpio/dpio-service.c=226=irqreturn_t dpaa2_io_irq(struct dpaa2_io *obj)\n--\ndrivers/soc/fsl/dpio/dpio-service.c-249-\t\t} else {\ndrivers/soc/fsl/dpio/dpio-service.c:250:\t\t\tpr_crit(\"fsl-mc-dpio: Unrecognised/ignored DQRR entry\\n\");\ndrivers/soc/fsl/dpio/dpio-service.c-251-\t\t}\n--\ndrivers/vfio/Kconfig=95=source \"drivers/vfio/mdev/Kconfig\"\ndrivers/vfio/Kconfig:96:source \"drivers/vfio/fsl-mc/Kconfig\"\ndrivers/vfio/Kconfig-97-source \"drivers/vfio/cdx/Kconfig\"\n--\ndrivers/vfio/Makefile=16=obj-$(CONFIG_VFIO_MDEV) += mdev/\ndrivers/vfio/Makefile:17:obj-$(CONFIG_VFIO_FSL_MC) += fsl-mc/\ndrivers/vfio/Makefile-18-obj-$(CONFIG_VFIO_CDX) += cdx/\n--\ndrivers/vfio/fsl-mc/Kconfig=4=config VFIO_FSL_MC\ndrivers/vfio/fsl-mc/Kconfig:5:\ttristate \"VFIO support for QorIQ DPAA2 fsl-mc bus devices\"\ndrivers/vfio/fsl-mc/Kconfig-6-\tselect EVENTFD\ndrivers/vfio/fsl-mc/Kconfig-7-\thelp\ndrivers/vfio/fsl-mc/Kconfig:8:\t Driver to enable support for the VFIO QorIQ DPAA2 fsl-mc\ndrivers/vfio/fsl-mc/Kconfig-9-\t (Management Complex) devices. This is required to passthrough\ndrivers/vfio/fsl-mc/Kconfig:10:\t fsl-mc bus devices using the VFIO framework.\ndrivers/vfio/fsl-mc/Kconfig-11-\n--\ndrivers/vfio/fsl-mc/Makefile-2-\ndrivers/vfio/fsl-mc/Makefile:3:vfio-fsl-mc-y := vfio_fsl_mc.o vfio_fsl_mc_intr.o\ndrivers/vfio/fsl-mc/Makefile:4:obj-$(CONFIG_VFIO_FSL_MC) += vfio-fsl-mc.o\n--\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c=416=static int vfio_fsl_mc_bus_notifier(struct notifier_block *nb,\n--\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c-437-\t\tif (mc_drv \u0026\u0026 mc_drv != \u0026vfio_fsl_mc_driver)\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c:438:\t\t\tdev_warn(dev, \"VFIO_FSL_MC: Object %s bound to driver %s while DPRC bound to vfio-fsl-mc\\n\",\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c-439-\t\t\t\t dev_name(dev), mc_drv-\u003edriver.name);\n--\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c=575=static const struct vfio_device_ops vfio_fsl_mc_ops = {\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c:576:\t.name\t\t= \"vfio-fsl-mc\",\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c-577-\t.init\t\t= vfio_fsl_mc_init_dev,\n--\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c=592=static struct fsl_mc_driver vfio_fsl_mc_driver = {\n--\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c-595-\t.driver\t= {\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c:596:\t\t.name\t= \"vfio-fsl-mc\",\ndrivers/vfio/fsl-mc/vfio_fsl_mc.c-597-\t},\n"
} |
| 5/2 | 2026/06/29 11:52 | llm | patch-evaluator |
0mModel:gemini-3.1-pro-preview Args: null Results: null Tokens: input: 32549 output: 144 thoughts: 166 Thoughts: **My Assessment of the Proposed Patch** Okay, after reviewing this patch, here's my take. The code modification touches `drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c`. Right away, that raises a flag – it's a driver for the DPAA2 switch, specifically targeting the NXP QorIQ DPAA2 architecture, which we know is `ARCH_LAYERSCAPE`. The core issue is that the `fsl-mc` bus, which this driver clearly relies on, is hardware-specific. We're not talking about something that's easily emulated in standard environments like QEMU or even something we see in syzkaller. Therefore, because this driver requires that specific hardware and its associated bus (`fsl-mc`), it's effectively unreachable in the kind of virtualized environments we typically use for fuzzing. The likelihood of generating any useful test coverage in such a setup is incredibly low, bordering on zero. So, in good conscience, I'm going to set `WorthFuzzing=false`. My rationale is clear: this patch modifies a hardware-specific driver for the Freescale DPAA2 switch, and that code is inaccessible in our standard virtualized fuzzing setups without very specific hardware emulation, which isn't available or feasible for this. |
| 6/2 | 2026/06/29 11:52 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch modifies the Freescale DPAA2 switch driver (drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c) to add LAG offload support. This driver depends on the fsl-mc bus (FSL_MC_BUS), which is specific to NXP QorIQ DPAA2 hardware (ARCH_LAYERSCAPE). There is no software emulation for this hardware in standard virtualized environments like QEMU or GCE, making the modified code unreachable during fuzzing.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies the Freescale DPAA2 switch driver (drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c) to add LAG offload support. This driver depends on the fsl-mc bus (FSL_MC_BUS), which is specific to NXP QorIQ DPAA2 hardware (ARCH_LAYERSCAPE). There is no software emulation for this hardware in standard virtualized environments like QEMU or GCE, making the modified code unreachable during fuzzing.",
"WorthFuzzing": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|