AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/06/30 21:20 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": [
    "BATMAN_ADV",
    "BATMAN_ADV_BATMAN_V",
    "BATMAN_ADV_BLA"
  ],
  "FocusSymbols": [
    "batadv_hardif_enable_interface",
    "batadv_hardif_disable_interface",
    "batadv_tp_handle_out_of_order",
    "batadv_tp_recv_msg",
    "batadv_tvlv_containers_process",
    "batadv_tvlv_containers_contain"
  ],
  "Reasoning": "The patch makes functional changes to the B.A.T.M.A.N. Advanced routing protocol. It modifies the handling of hard interfaces (adding/removing them from the mesh), the processing of out-of-order TP meter packets, and the parsing of TVLV containers. These are core logic changes reachable from user space via netlink/sysfs and network packets, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/06/30 21:20 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit aa2ef41dd33cad16598635d20e34d53f58de5c0b\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Tue Jun 30 21:20:52 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c\nindex bb2f012b454ea4..22622283f59b0c 100644\n--- a/net/batman-adv/bat_iv_ogm.c\n+++ b/net/batman-adv/bat_iv_ogm.c\n@@ -404,23 +404,14 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,\n /* send a batman ogm packet */\n static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)\n {\n-\tstruct net_device *mesh_iface;\n-\n \tif (!forw_packet-\u003eif_incoming) {\n \t\tpr_err(\"Error - can't forward packet: incoming iface not specified\\n\");\n \t\treturn;\n \t}\n \n-\tmesh_iface = forw_packet-\u003eif_incoming-\u003emesh_iface;\n-\n \tif (WARN_ON(!forw_packet-\u003eif_outgoing))\n \t\treturn;\n \n-\tif (forw_packet-\u003eif_outgoing-\u003emesh_iface != mesh_iface) {\n-\t\tpr_warn(\"%s: mesh interface switch for queued OGM\\n\", __func__);\n-\t\treturn;\n-\t}\n-\n \tif (forw_packet-\u003eif_incoming-\u003eif_status != BATADV_IF_ACTIVE)\n \t\treturn;\n \n@@ -910,8 +901,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)\n \n static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)\n {\n-\tif (hard_iface-\u003eif_status == BATADV_IF_NOT_IN_USE ||\n-\t    hard_iface-\u003eif_status == BATADV_IF_TO_BE_REMOVED)\n+\tif (hard_iface-\u003eif_status == BATADV_IF_TO_BE_REMOVED)\n \t\treturn;\n \n \tmutex_lock(\u0026hard_iface-\u003ebat_iv.ogm_buff_mutex);\ndiff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c\nindex 4841f0f1a9b139..262e40040007cc 100644\n--- a/net/batman-adv/bat_v_elp.c\n+++ b/net/batman-adv/bat_v_elp.c\n@@ -90,12 +90,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,\n \tu32 throughput;\n \tint ret;\n \n-\t/* don't query throughput when no longer associated with any\n-\t * batman-adv interface\n-\t */\n-\tif (!mesh_iface)\n-\t\treturn false;\n-\n \t/* if the user specified a customised value for this interface, then\n \t * return it directly\n \t */\n@@ -311,8 +305,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)\n \t\tgoto out;\n \n \t/* we are in the process of shutting this interface down */\n-\tif (hard_iface-\u003eif_status == BATADV_IF_NOT_IN_USE ||\n-\t    hard_iface-\u003eif_status == BATADV_IF_TO_BE_REMOVED)\n+\tif (hard_iface-\u003eif_status == BATADV_IF_TO_BE_REMOVED)\n \t\tgoto out;\n \n \t/* the interface was enabled but may not be ready yet */\ndiff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c\nindex 037921aad35d58..e921d49f7ecedd 100644\n--- a/net/batman-adv/bat_v_ogm.c\n+++ b/net/batman-adv/bat_v_ogm.c\n@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)\n \n /**\n  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface\n- * @bat_priv: the bat priv with all the mesh interface information\n  * @skb: the OGM to send\n  * @hard_iface: the interface to use to send the OGM\n  */\n-static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,\n-\t\t\t\t    struct sk_buff *skb,\n+static void batadv_v_ogm_send_to_if(struct sk_buff *skb,\n \t\t\t\t    struct batadv_hard_iface *hard_iface)\n {\n+\tstruct batadv_priv *bat_priv = netdev_priv(hard_iface-\u003emesh_iface);\n+\n \tif (hard_iface-\u003eif_status != BATADV_IF_ACTIVE) {\n \t\tkfree_skb(skb);\n \t\treturn;\n@@ -189,7 +189,6 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)\n \n /**\n  * batadv_v_ogm_aggr_send() - flush \u0026 send aggregation queue\n- * @bat_priv: the bat priv with all the mesh interface information\n  * @hard_iface: the interface with the aggregation queue to flush\n  *\n  * Aggregates all OGMv2 packets currently in the aggregation queue into a\n@@ -199,8 +198,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)\n  *\n  * Caller needs to hold the hard_iface-\u003ebat_v.aggr_list.lock.\n  */\n-static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,\n-\t\t\t\t   struct batadv_hard_iface *hard_iface)\n+static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)\n {\n \tunsigned int aggr_len = hard_iface-\u003ebat_v.aggr_len;\n \tstruct sk_buff *skb_aggr;\n@@ -230,26 +228,21 @@ static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,\n \t\tconsume_skb(skb);\n \t}\n \n-\tbatadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);\n+\tbatadv_v_ogm_send_to_if(skb_aggr, hard_iface);\n }\n \n /**\n  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface\n- * @bat_priv: the bat priv with all the mesh interface information\n  * @skb: the OGM to queue\n  * @hard_iface: the interface to queue the OGM on\n  */\n-static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,\n-\t\t\t\t     struct sk_buff *skb,\n+static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,\n \t\t\t\t     struct batadv_hard_iface *hard_iface)\n {\n-\tif (hard_iface-\u003emesh_iface != bat_priv-\u003emesh_iface) {\n-\t\tkfree_skb(skb);\n-\t\treturn;\n-\t}\n+\tstruct batadv_priv *bat_priv = netdev_priv(hard_iface-\u003emesh_iface);\n \n \tif (!READ_ONCE(bat_priv-\u003eaggregated_ogms)) {\n-\t\tbatadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);\n+\t\tbatadv_v_ogm_send_to_if(skb, hard_iface);\n \t\treturn;\n \t}\n \n@@ -260,7 +253,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,\n \t}\n \n \tif (!batadv_v_ogm_queue_left(skb, hard_iface))\n-\t\tbatadv_v_ogm_aggr_send(bat_priv, hard_iface);\n+\t\tbatadv_v_ogm_aggr_send(hard_iface);\n \n \thard_iface-\u003ebat_v.aggr_len += batadv_v_ogm_len(skb);\n \t__skb_queue_tail(\u0026hard_iface-\u003ebat_v.aggr_list, skb);\n@@ -357,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)\n \t\t\tbreak;\n \t\t}\n \n-\t\tbatadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);\n+\t\tbatadv_v_ogm_queue_on_if(skb_tmp, hard_iface);\n \t\tbatadv_hardif_put(hard_iface);\n \t}\n \trcu_read_unlock();\n@@ -397,14 +390,12 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)\n {\n \tstruct batadv_hard_iface_bat_v *batv;\n \tstruct batadv_hard_iface *hard_iface;\n-\tstruct batadv_priv *bat_priv;\n \n \tbatv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);\n \thard_iface = container_of(batv, struct batadv_hard_iface, bat_v);\n-\tbat_priv = netdev_priv(hard_iface-\u003emesh_iface);\n \n \tspin_lock_bh(\u0026hard_iface-\u003ebat_v.aggr_list.lock);\n-\tbatadv_v_ogm_aggr_send(bat_priv, hard_iface);\n+\tbatadv_v_ogm_aggr_send(hard_iface);\n \tspin_unlock_bh(\u0026hard_iface-\u003ebat_v.aggr_list.lock);\n \n \tbatadv_v_ogm_start_queue_timer(hard_iface);\n@@ -601,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,\n \t\t   if_outgoing-\u003enet_dev-\u003ename, ntohl(ogm_forward-\u003ethroughput),\n \t\t   ogm_forward-\u003ettl, if_incoming-\u003enet_dev-\u003ename);\n \n-\tbatadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);\n+\tbatadv_v_ogm_queue_on_if(skb, if_outgoing);\n \n out:\n \tbatadv_orig_ifinfo_put(orig_ifinfo);\ndiff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c\nindex 5c73f6ba16cff3..f9a1fadf8de9e4 100644\n--- a/net/batman-adv/bridge_loop_avoidance.c\n+++ b/net/batman-adv/bridge_loop_avoidance.c\n@@ -344,7 +344,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,\n \tstruct sk_buff *skb;\n \tstruct ethhdr *ethhdr;\n \tstruct batadv_hard_iface *primary_if;\n-\tstruct net_device *mesh_iface;\n \tu8 *hw_src;\n \tstruct batadv_bla_claim_dst local_claim_dest;\n \t__be32 zeroip = 0;\n@@ -357,14 +356,10 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,\n \t       sizeof(local_claim_dest));\n \tlocal_claim_dest.type = claimtype;\n \n-\tmesh_iface = READ_ONCE(primary_if-\u003emesh_iface);\n-\tif (!mesh_iface)\n-\t\tgoto out;\n-\n \tskb = arp_create(ARPOP_REPLY, ETH_P_ARP,\n \t\t\t /* IP DST: 0.0.0.0 */\n \t\t\t zeroip,\n-\t\t\t mesh_iface,\n+\t\t\t primary_if-\u003emesh_iface,\n \t\t\t /* IP SRC: 0.0.0.0 */\n \t\t\t zeroip,\n \t\t\t /* Ethernet DST: Broadcast */\n@@ -442,7 +437,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,\n \t}\n \n \tskb_reset_mac_header(skb);\n-\tskb-\u003eprotocol = eth_type_trans(skb, mesh_iface);\n+\tskb-\u003eprotocol = eth_type_trans(skb, primary_if-\u003emesh_iface);\n \tbatadv_inc_counter(bat_priv, BATADV_CNT_RX);\n \tbatadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,\n \t\t\t   skb-\u003elen + ETH_HLEN);\ndiff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c\nindex 03d01c20a9548b..b6867576bbafae 100644\n--- a/net/batman-adv/hard-interface.c\n+++ b/net/batman-adv/hard-interface.c\n@@ -63,6 +63,7 @@ void batadv_hardif_release(struct kref *ref)\n \tstruct batadv_hard_iface *hard_iface;\n \n \thard_iface = container_of(ref, struct batadv_hard_iface, refcount);\n+\tnetdev_put(hard_iface-\u003emesh_iface, \u0026hard_iface-\u003emeshif_dev_tracker);\n \tnetdev_put(hard_iface-\u003enet_dev, \u0026hard_iface-\u003edev_tracker);\n \n \tkfree_rcu(hard_iface, rcu);\n@@ -75,21 +76,21 @@ void batadv_hardif_release(struct kref *ref)\n  * Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors\n  */\n struct batadv_hard_iface *\n-batadv_hardif_get_by_netdev(const struct net_device *net_dev)\n+batadv_hardif_get_by_netdev(struct net_device *net_dev)\n {\n \tstruct batadv_hard_iface *hard_iface;\n+\tstruct net_device *mesh_iface;\n \n-\trcu_read_lock();\n-\tlist_for_each_entry_rcu(hard_iface, \u0026batadv_hardif_list, list) {\n-\t\tif (hard_iface-\u003enet_dev == net_dev \u0026\u0026\n-\t\t    kref_get_unless_zero(\u0026hard_iface-\u003erefcount))\n-\t\t\tgoto out;\n-\t}\n+\tASSERT_RTNL();\n \n-\thard_iface = NULL;\n+\tmesh_iface = netdev_master_upper_dev_get(net_dev);\n+\tif (!mesh_iface || !batadv_meshif_is_valid(mesh_iface))\n+\t\treturn NULL;\n+\n+\thard_iface = netdev_lower_dev_get_private(mesh_iface, net_dev);\n+\tif (!kref_get_unless_zero(\u0026hard_iface-\u003erefcount))\n+\t\treturn NULL;\n \n-out:\n-\trcu_read_unlock();\n \treturn hard_iface;\n }\n \n@@ -245,7 +246,7 @@ struct net_device *__batadv_get_real_netdev(struct net_device *netdev)\n \t}\n \n \thard_iface = batadv_hardif_get_by_netdev(netdev);\n-\tif (!hard_iface || !hard_iface-\u003emesh_iface)\n+\tif (!hard_iface)\n \t\tgoto out;\n \n \tnet = dev_net(hard_iface-\u003emesh_iface);\n@@ -539,16 +540,10 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa\n \tconst struct batadv_hard_iface *tmp_hard_iface;\n \tstruct list_head *iter;\n \n-\tif (!mesh_iface)\n-\t\treturn;\n-\n \tnetdev_for_each_lower_private(mesh_iface, tmp_hard_iface, iter) {\n \t\tif (tmp_hard_iface == hard_iface)\n \t\t\tcontinue;\n \n-\t\tif (tmp_hard_iface-\u003eif_status == BATADV_IF_NOT_IN_USE)\n-\t\t\tcontinue;\n-\n \t\tif (!batadv_compare_eth(tmp_hard_iface-\u003enet_dev-\u003edev_addr,\n \t\t\t\t\thard_iface-\u003enet_dev-\u003edev_addr))\n \t\t\tcontinue;\n@@ -574,9 +569,6 @@ static void batadv_hardif_recalc_extra_skbroom(struct net_device *mesh_iface)\n \n \trcu_read_lock();\n \tnetdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) {\n-\t\tif (hard_iface-\u003eif_status == BATADV_IF_NOT_IN_USE)\n-\t\t\tcontinue;\n-\n \t\tlower_header_len = max_t(unsigned short, lower_header_len,\n \t\t\t\t\t hard_iface-\u003enet_dev-\u003ehard_header_len);\n \n@@ -723,38 +715,63 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)\n }\n \n /**\n- * batadv_hardif_enable_interface() - Enslave hard interface to mesh interface\n- * @hard_iface: hard interface to add to mesh interface\n+ * batadv_hardif_enable_interface() - Enslave interface to mesh interface\n+ * @net_dev: netdev struct of the interface to add to mesh interface\n  * @mesh_iface: netdev struct of the mesh interface\n  *\n  * Return: 0 on success or negative error number in case of failure\n  */\n-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,\n+int batadv_hardif_enable_interface(struct net_device *net_dev,\n \t\t\t\t   struct net_device *mesh_iface)\n {\n \tstruct batadv_priv *bat_priv;\n \t__be16 ethertype = htons(ETH_P_BATMAN);\n \tint max_header_len = batadv_max_header_len();\n+\tstruct batadv_hard_iface *hard_iface;\n \tunsigned int required_mtu;\n \tunsigned int hardif_mtu;\n \tbool fragmentation;\n \tint ret;\n \n-\thardif_mtu = READ_ONCE(hard_iface-\u003enet_dev-\u003emtu);\n+\tASSERT_RTNL();\n+\n+\tif (!batadv_is_valid_iface(net_dev))\n+\t\treturn -EINVAL;\n+\n+\thardif_mtu = READ_ONCE(net_dev-\u003emtu);\n \trequired_mtu = READ_ONCE(mesh_iface-\u003emtu) + max_header_len;\n \n \tif (hardif_mtu \u003c ETH_MIN_MTU + max_header_len)\n \t\treturn -EINVAL;\n \n-\tif (hard_iface-\u003eif_status != BATADV_IF_NOT_IN_USE)\n-\t\tgoto out;\n+\thard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);\n+\tif (!hard_iface)\n+\t\treturn -ENOMEM;\n \n-\tkref_get(\u0026hard_iface-\u003erefcount);\n+\tnetdev_hold(net_dev, \u0026hard_iface-\u003edev_tracker, GFP_ATOMIC);\n+\thard_iface-\u003enet_dev = net_dev;\n+\n+\thard_iface-\u003eif_status = BATADV_IF_INACTIVE;\n+\n+\tINIT_HLIST_HEAD(\u0026hard_iface-\u003eneigh_list);\n+\n+\tmutex_init(\u0026hard_iface-\u003ebat_iv.ogm_buff_mutex);\n+\tspin_lock_init(\u0026hard_iface-\u003eneigh_list_lock);\n+\tkref_init(\u0026hard_iface-\u003erefcount);\n+\n+\thard_iface-\u003enum_bcasts = BATADV_NUM_BCASTS_DEFAULT;\n+\tif (batadv_is_wifi_hardif(hard_iface))\n+\t\thard_iface-\u003enum_bcasts = BATADV_NUM_BCASTS_WIRELESS;\n+\n+\tWRITE_ONCE(hard_iface-\u003ehop_penalty, 0);\n+\n+\tbatadv_v_hardif_init(hard_iface);\n \n \tnetdev_hold(mesh_iface, \u0026hard_iface-\u003emeshif_dev_tracker, GFP_ATOMIC);\n \thard_iface-\u003emesh_iface = mesh_iface;\n \tbat_priv = netdev_priv(hard_iface-\u003emesh_iface);\n \n+\tbat_priv-\u003ehardif_generation++;\n \tret = netdev_master_upper_dev_link(hard_iface-\u003enet_dev,\n \t\t\t\t\t   mesh_iface, hard_iface, NULL, NULL);\n \tif (ret)\n@@ -764,9 +781,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,\n \tif (ret \u003c 0)\n \t\tgoto err_upper;\n \n-\thard_iface-\u003eif_status = BATADV_IF_INACTIVE;\n-\n-\tkref_get(\u0026hard_iface-\u003erefcount);\n \thard_iface-\u003ebatman_adv_ptype.type = ethertype;\n \thard_iface-\u003ebatman_adv_ptype.func = batadv_batman_skb_recv;\n \thard_iface-\u003ebatman_adv_ptype.dev = hard_iface-\u003enet_dev;\n@@ -802,14 +816,11 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,\n \tif (bat_priv-\u003ealgo_ops-\u003eiface.enabled)\n \t\tbat_priv-\u003ealgo_ops-\u003eiface.enabled(hard_iface);\n \n-out:\n \treturn 0;\n \n err_upper:\n \tnetdev_upper_dev_unlink(hard_iface-\u003enet_dev, mesh_iface);\n err_dev:\n-\thard_iface-\u003emesh_iface = NULL;\n-\tnetdev_put(mesh_iface, \u0026hard_iface-\u003emeshif_dev_tracker);\n \tbatadv_hardif_put(hard_iface);\n \treturn ret;\n }\n@@ -821,17 +832,18 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,\n void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)\n {\n \tstruct batadv_priv *bat_priv = netdev_priv(hard_iface-\u003emesh_iface);\n-\tstruct batadv_hard_iface *primary_if = NULL;\n+\tstruct batadv_hard_iface *primary_if;\n+\n+\tASSERT_RTNL();\n \n \tbatadv_hardif_deactivate_interface(hard_iface);\n \n \tif (hard_iface-\u003eif_status != BATADV_IF_INACTIVE)\n-\t\tgoto out;\n+\t\treturn;\n \n \tbatadv_info(hard_iface-\u003emesh_iface, \"Removing interface: %s\\n\",\n \t\t    hard_iface-\u003enet_dev-\u003ename);\n \tdev_remove_pack(\u0026hard_iface-\u003ebatman_adv_ptype);\n-\tbatadv_hardif_put(hard_iface);\n \n \tprimary_if = batadv_primary_if_get_selected(bat_priv);\n \tif (hard_iface == primary_if) {\n@@ -842,15 +854,16 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)\n \n \t\tbatadv_hardif_put(new_if);\n \t}\n+\tbatadv_hardif_put(primary_if);\n \n \tbat_priv-\u003ealgo_ops-\u003eiface.disable(hard_iface);\n-\thard_iface-\u003eif_status = BATADV_IF_NOT_IN_USE;\n+\thard_iface-\u003eif_status = BATADV_IF_TO_BE_REMOVED;\n \n \t/* delete all references to this hard_iface */\n \tbatadv_purge_orig_ref(bat_priv);\n \tbatadv_purge_outstanding_packets(bat_priv, hard_iface);\n-\tnetdev_put(hard_iface-\u003emesh_iface, \u0026hard_iface-\u003emeshif_dev_tracker);\n \n+\tbat_priv-\u003ehardif_generation++;\n \tnetdev_upper_dev_unlink(hard_iface-\u003enet_dev, hard_iface-\u003emesh_iface);\n \tbatadv_hardif_recalc_extra_skbroom(hard_iface-\u003emesh_iface);\n \n@@ -858,67 +871,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)\n \tif (list_empty(\u0026hard_iface-\u003emesh_iface-\u003eadj_list.lower))\n \t\tbatadv_gw_check_client_stop(bat_priv);\n \n-\thard_iface-\u003emesh_iface = NULL;\n-\tbatadv_hardif_put(hard_iface);\n-\n-out:\n-\tbatadv_hardif_put(primary_if);\n-}\n-\n-static struct batadv_hard_iface *\n-batadv_hardif_add_interface(struct net_device *net_dev)\n-{\n-\tstruct batadv_hard_iface *hard_iface;\n-\n-\tASSERT_RTNL();\n-\n-\tif (!batadv_is_valid_iface(net_dev))\n-\t\treturn NULL;\n-\n-\thard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);\n-\tif (!hard_iface)\n-\t\treturn NULL;\n-\n-\tnetdev_hold(net_dev, \u0026hard_iface-\u003edev_tracker, GFP_ATOMIC);\n-\thard_iface-\u003enet_dev = net_dev;\n-\n-\thard_iface-\u003emesh_iface = NULL;\n-\thard_iface-\u003eif_status = BATADV_IF_NOT_IN_USE;\n-\n-\tINIT_LIST_HEAD(\u0026hard_iface-\u003elist);\n-\tINIT_HLIST_HEAD(\u0026hard_iface-\u003eneigh_list);\n-\n-\tmutex_init(\u0026hard_iface-\u003ebat_iv.ogm_buff_mutex);\n-\tspin_lock_init(\u0026hard_iface-\u003eneigh_list_lock);\n-\tkref_init(\u0026hard_iface-\u003erefcount);\n-\n-\thard_iface-\u003enum_bcasts = BATADV_NUM_BCASTS_DEFAULT;\n-\tif (batadv_is_wifi_hardif(hard_iface))\n-\t\thard_iface-\u003enum_bcasts = BATADV_NUM_BCASTS_WIRELESS;\n-\n-\tWRITE_ONCE(hard_iface-\u003ehop_penalty, 0);\n-\n-\tbatadv_v_hardif_init(hard_iface);\n-\n-\tkref_get(\u0026hard_iface-\u003erefcount);\n-\tlist_add_tail_rcu(\u0026hard_iface-\u003elist, \u0026batadv_hardif_list);\n-\tbatadv_hardif_generation++;\n-\n-\treturn hard_iface;\n-}\n-\n-static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)\n-{\n-\tASSERT_RTNL();\n-\n-\t/* first deactivate interface */\n-\tif (hard_iface-\u003eif_status != BATADV_IF_NOT_IN_USE)\n-\t\tbatadv_hardif_disable_interface(hard_iface);\n-\n-\tif (hard_iface-\u003eif_status != BATADV_IF_NOT_IN_USE)\n-\t\treturn;\n-\n-\thard_iface-\u003eif_status = BATADV_IF_TO_BE_REMOVED;\n \tbatadv_hardif_put(hard_iface);\n }\n \n@@ -1082,10 +1034,6 @@ static int batadv_hard_if_event(struct notifier_block *this,\n \tbatadv_wifi_net_device_event(event, net_dev);\n \n \thard_iface = batadv_hardif_get_by_netdev(net_dev);\n-\tif (!hard_iface \u0026\u0026 (event == NETDEV_REGISTER ||\n-\t\t\t    event == NETDEV_POST_TYPE_CHANGE))\n-\t\thard_iface = batadv_hardif_add_interface(net_dev);\n-\n \tif (!hard_iface)\n \t\tgoto out;\n \n@@ -1099,19 +1047,12 @@ static int batadv_hard_if_event(struct notifier_block *this,\n \t\tbreak;\n \tcase NETDEV_UNREGISTER:\n \tcase NETDEV_PRE_TYPE_CHANGE:\n-\t\tlist_del_rcu(\u0026hard_iface-\u003elist);\n-\t\tbatadv_hardif_generation++;\n-\n-\t\tbatadv_hardif_remove_interface(hard_iface);\n+\t\tbatadv_hardif_disable_interface(hard_iface);\n \t\tbreak;\n \tcase NETDEV_CHANGEMTU:\n-\t\tif (hard_iface-\u003emesh_iface)\n-\t\t\tbatadv_update_min_mtu(hard_iface-\u003emesh_iface);\n+\t\tbatadv_update_min_mtu(hard_iface-\u003emesh_iface);\n \t\tbreak;\n \tcase NETDEV_CHANGEADDR:\n-\t\tif (hard_iface-\u003eif_status == BATADV_IF_NOT_IN_USE)\n-\t\t\tgoto hardif_put;\n-\n \t\tbatadv_check_known_mac_addr(hard_iface);\n \n \t\tbat_priv = netdev_priv(hard_iface-\u003emesh_iface);\ndiff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h\nindex af31696c397808..935f47ca9a48fd 100644\n--- a/net/batman-adv/hard-interface.h\n+++ b/net/batman-adv/hard-interface.h\n@@ -21,12 +21,6 @@\n  * enum batadv_hard_if_state - State of a hard interface\n  */\n enum batadv_hard_if_state {\n-\t/**\n-\t * @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a\n-\t * batman-adv mesh interface\n-\t */\n-\tBATADV_IF_NOT_IN_USE,\n-\n \t/**\n \t * @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh\n \t * interface\n@@ -74,8 +68,8 @@ u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);\n u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);\n bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);\n struct batadv_hard_iface*\n-batadv_hardif_get_by_netdev(const struct net_device *net_dev);\n-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,\n+batadv_hardif_get_by_netdev(struct net_device *net_dev);\n+int batadv_hardif_enable_interface(struct net_device *net_dev,\n \t\t\t\t   struct net_device *mesh_iface);\n void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);\n int batadv_hardif_min_mtu(struct net_device *mesh_iface);\ndiff --git a/net/batman-adv/main.c b/net/batman-adv/main.c\nindex 3c4572284b532f..04bb030ef299ac 100644\n--- a/net/batman-adv/main.c\n+++ b/net/batman-adv/main.c\n@@ -59,11 +59,6 @@\n #include \"tp_meter.h\"\n #include \"translation-table.h\"\n \n-/* List manipulations on hardif_list have to be rtnl_lock()'ed,\n- * list traversals just rcu-locked\n- */\n-struct list_head batadv_hardif_list;\n-unsigned int batadv_hardif_generation;\n static int (*batadv_rx_handler[256])(struct sk_buff *skb,\n \t\t\t\t     struct batadv_hard_iface *recv_if);\n \n@@ -95,7 +90,6 @@ static int __init batadv_init(void)\n \tif (ret \u003c 0)\n \t\treturn ret;\n \n-\tINIT_LIST_HEAD(\u0026batadv_hardif_list);\n \tbatadv_algo_init();\n \n \tbatadv_recv_handler_init();\n@@ -450,9 +444,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,\n \tif (unlikely(skb-\u003emac_len != ETH_HLEN || !skb_mac_header(skb)))\n \t\tgoto err_free;\n \n-\tif (!hard_iface-\u003emesh_iface)\n-\t\tgoto err_free;\n-\n \tbat_priv = netdev_priv(hard_iface-\u003emesh_iface);\n \n \tif (READ_ONCE(bat_priv-\u003emesh_state) != BATADV_MESH_ACTIVE)\ndiff --git a/net/batman-adv/main.h b/net/batman-adv/main.h\nindex f68fc8b7239cd5..e738758ee4a7ae 100644\n--- a/net/batman-adv/main.h\n+++ b/net/batman-adv/main.h\n@@ -226,9 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)\n \t\treturn -1;\n }\n \n-extern struct list_head batadv_hardif_list;\n-extern unsigned int batadv_hardif_generation;\n-\n extern struct workqueue_struct *batadv_event_workqueue;\n \n int batadv_mesh_init(struct net_device *mesh_iface);\ndiff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c\nindex 44026810b99cea..a37368c1f5b55c 100644\n--- a/net/batman-adv/mesh-interface.c\n+++ b/net/batman-adv/mesh-interface.c\n@@ -836,18 +836,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,\n \t\t\t\t   struct net_device *slave_dev,\n \t\t\t\t   struct netlink_ext_ack *extack)\n {\n-\tstruct batadv_hard_iface *hard_iface;\n-\tint ret = -EINVAL;\n-\n-\thard_iface = batadv_hardif_get_by_netdev(slave_dev);\n-\tif (!hard_iface || hard_iface-\u003emesh_iface)\n-\t\tgoto out;\n-\n-\tret = batadv_hardif_enable_interface(hard_iface, dev);\n-\n-out:\n-\tbatadv_hardif_put(hard_iface);\n-\treturn ret;\n+\treturn batadv_hardif_enable_interface(slave_dev, dev);\n }\n \n /**\ndiff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c\nindex 4cf9e3c54ad3b2..d2bc48c7071434 100644\n--- a/net/batman-adv/netlink.c\n+++ b/net/batman-adv/netlink.c\n@@ -968,7 +968,7 @@ batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)\n \tbat_priv = netdev_priv(mesh_iface);\n \n \trtnl_lock();\n-\tcb-\u003eseq = batadv_hardif_generation \u003c\u003c 1 | 1;\n+\tcb-\u003eseq = bat_priv-\u003ehardif_generation \u003c\u003c 1 | 1;\n \n \tnetdev_for_each_lower_private(mesh_iface, hard_iface, iter) {\n \t\tif (i++ \u003c skip)\n@@ -1211,7 +1211,9 @@ batadv_netlink_get_hardif_from_ifindex(struct batadv_priv *bat_priv,\n \tif (!hard_dev)\n \t\treturn ERR_PTR(-ENODEV);\n \n+\trtnl_lock();\n \thard_iface = batadv_hardif_get_by_netdev(hard_dev);\n+\trtnl_unlock();\n \tif (!hard_iface)\n \t\tgoto err_put_harddev;\n \ndiff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c\nindex 9b38bd9e8da7ec..48f837cf665a2a 100644\n--- a/net/batman-adv/originator.c\n+++ b/net/batman-adv/originator.c\n@@ -1033,7 +1033,6 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,\n \n \t\t/* don't purge if the interface is not (going) down */\n \t\tif (if_outgoing-\u003eif_status != BATADV_IF_INACTIVE \u0026\u0026\n-\t\t    if_outgoing-\u003eif_status != BATADV_IF_NOT_IN_USE \u0026\u0026\n \t\t    if_outgoing-\u003eif_status != BATADV_IF_TO_BE_REMOVED)\n \t\t\tcontinue;\n \n@@ -1077,7 +1076,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,\n \n \t\t/* don't purge if the interface is not (going) down */\n \t\tif (if_outgoing-\u003eif_status != BATADV_IF_INACTIVE \u0026\u0026\n-\t\t    if_outgoing-\u003eif_status != BATADV_IF_NOT_IN_USE \u0026\u0026\n \t\t    if_outgoing-\u003eif_status != BATADV_IF_TO_BE_REMOVED)\n \t\t\tcontinue;\n \n@@ -1127,10 +1125,8 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,\n \n \t\tif (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) ||\n \t\t    if_incoming-\u003eif_status == BATADV_IF_INACTIVE ||\n-\t\t    if_incoming-\u003eif_status == BATADV_IF_NOT_IN_USE ||\n \t\t    if_incoming-\u003eif_status == BATADV_IF_TO_BE_REMOVED) {\n \t\t\tif (if_incoming-\u003eif_status == BATADV_IF_INACTIVE ||\n-\t\t\t    if_incoming-\u003eif_status == BATADV_IF_NOT_IN_USE ||\n \t\t\t    if_incoming-\u003eif_status == BATADV_IF_TO_BE_REMOVED)\n \t\t\t\tbatadv_dbg(BATADV_DBG_BATMAN, bat_priv,\n \t\t\t\t\t   \"neighbor purge: originator %pM, neighbor: %pM, iface: %s\\n\",\ndiff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c\nindex c2eea7dbc48833..00467aa79de9dc 100644\n--- a/net/batman-adv/tp_meter.c\n+++ b/net/batman-adv/tp_meter.c\n@@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,\n }\n \n /**\n- * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists\n+ * batadv_tp_sender_release() - release batadv_tp_sender\n  *  and queue for free after rcu grace period\n- * @ref: kref pointer of the batadv_tp_vars_common\n+ * @ref: kref pointer of the batadv_tp_sender\n  */\n-static void batadv_tp_vars_common_release(struct kref *ref)\n+static void batadv_tp_sender_release(struct kref *ref)\n {\n-\tstruct batadv_tp_vars_common *tp_vars;\n-\tstruct batadv_tp_unacked *un, *safe;\n-\n-\ttp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);\n-\n-\t/* lock should not be needed because this object is now out of any\n-\t * context!\n-\t */\n-\tspin_lock_bh(\u0026tp_vars-\u003eunacked_lock);\n-\tlist_for_each_entry_safe(un, safe, \u0026tp_vars-\u003eunacked_list, list) {\n-\t\tlist_del(\u0026un-\u003elist);\n-\t\tkfree(un);\n-\t}\n-\tspin_unlock_bh(\u0026tp_vars-\u003eunacked_lock);\n+\tstruct batadv_tp_sender *tp_vars;\n \n-\tkfree_rcu(tp_vars, rcu);\n+\ttp_vars = container_of(ref, struct batadv_tp_sender, common.refcount);\n+\tkfree_rcu(tp_vars, common.rcu);\n }\n \n /**\n@@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)\n \tif (!tp_vars)\n \t\treturn;\n \n-\tkref_put(\u0026tp_vars-\u003ecommon.refcount, batadv_tp_vars_common_release);\n+\tkref_put(\u0026tp_vars-\u003ecommon.refcount, batadv_tp_sender_release);\n }\n \n /**\n@@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,\n \tinit_waitqueue_head(\u0026tp_vars-\u003emore_bytes);\n \tinit_completion(\u0026tp_vars-\u003efinished);\n \n-\tspin_lock_init(\u0026tp_vars-\u003ecommon.unacked_lock);\n-\tINIT_LIST_HEAD(\u0026tp_vars-\u003ecommon.unacked_list);\n-\n \tspin_lock_init(\u0026tp_vars-\u003ecc_lock);\n \n \ttp_vars-\u003eprerandom_offset = 0;\n@@ -1251,6 +1236,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst\n \treturn tp_vars;\n }\n \n+/**\n+ * batadv_tp_receiver_release() - release batadv_tp_receiver\n+ *  and queue for free after rcu grace period\n+ * @ref: kref pointer of the batadv_tp_receiver\n+ */\n+static void batadv_tp_receiver_release(struct kref *ref)\n+{\n+\tstruct batadv_tp_receiver *tp_vars;\n+\tstruct batadv_tp_unacked *safe;\n+\tstruct batadv_tp_unacked *un;\n+\n+\ttp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount);\n+\n+\t/* lock should not be needed because this object is now out of any\n+\t * context!\n+\t */\n+\tspin_lock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n+\tlist_for_each_entry_safe(un, safe, \u0026tp_vars-\u003eunacked_list, list) {\n+\t\tlist_del(\u0026un-\u003elist);\n+\t\tkfree(un);\n+\t\ttp_vars-\u003eunacked_count--;\n+\t}\n+\tspin_unlock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n+\n+\tkfree_rcu(tp_vars, common.rcu);\n+}\n+\n /**\n  * batadv_tp_receiver_put() - decrement the batadv_tp_receiver\n  *  refcounter and possibly release it\n@@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)\n \tif (!tp_vars)\n \t\treturn;\n \n-\tkref_put(\u0026tp_vars-\u003ecommon.refcount, batadv_tp_vars_common_release);\n+\tkref_put(\u0026tp_vars-\u003ecommon.refcount, batadv_tp_receiver_release);\n }\n \n /**\n@@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)\n \tif (batadv_tp_list_detach(\u0026tp_vars-\u003ecommon))\n \t\tbatadv_tp_receiver_put(tp_vars);\n \n-\tspin_lock_bh(\u0026tp_vars-\u003ecommon.unacked_lock);\n-\tlist_for_each_entry_safe(un, safe, \u0026tp_vars-\u003ecommon.unacked_list, list) {\n+\tspin_lock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n+\tlist_for_each_entry_safe(un, safe, \u0026tp_vars-\u003eunacked_list, list) {\n \t\tlist_del(\u0026un-\u003elist);\n \t\tkfree(un);\n-\t\ttp_vars-\u003ecommon.unacked_count--;\n+\t\ttp_vars-\u003eunacked_count--;\n \t}\n-\tspin_unlock_bh(\u0026tp_vars-\u003ecommon.unacked_lock);\n+\tspin_unlock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n \n \t/* drop reference of timer */\n \tif (WARN_ON(atomic_xchg(\u0026tp_vars-\u003ereceiving, 0) != 1))\n@@ -1403,69 +1415,106 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,\n  */\n static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,\n \t\t\t\t\t  u32 seqno, u32 payload_len)\n-\t__must_hold(\u0026tp_vars-\u003ecommon.unacked_lock)\n+\t__must_hold(\u0026tp_vars-\u003eack_seqno_lock)\n {\n-\tstruct batadv_tp_unacked *un, *new;\n-\tbool added = false;\n-\n-\tnew = kmalloc_obj(*new, GFP_ATOMIC);\n-\tif (unlikely(!new))\n-\t\treturn false;\n-\n-\tnew-\u003eseqno = seqno;\n-\tnew-\u003elen = payload_len;\n-\n-\t/* if the list is empty immediately attach this new object */\n-\tif (list_empty(\u0026tp_vars-\u003ecommon.unacked_list)) {\n-\t\tlist_add(\u0026new-\u003elist, \u0026tp_vars-\u003ecommon.unacked_list);\n-\t\ttp_vars-\u003ecommon.unacked_count++;\n-\t\treturn true;\n-\t}\n-\n-\t/* otherwise loop over the list and either drop the packet because this\n-\t * is a duplicate or store it at the right position.\n+\tstruct list_head *pos = \u0026tp_vars-\u003eunacked_list;\n+\tstruct batadv_tp_unacked *new = NULL;\n+\tu32 end_seqno = seqno + payload_len;\n+\tstruct batadv_tp_unacked *safe;\n+\tstruct batadv_tp_unacked *un;\n+\n+\t/* loop over the list to find either an existing entry which the new\n+\t * seqno range can be merged with or the position at which a new entry\n+\t * has to be inserted.\n \t *\n \t * The iteration is done in the reverse way because it is likely that\n \t * the last received packet (the one being processed now) has a bigger\n \t * seqno than all the others already stored.\n \t */\n-\tlist_for_each_entry_reverse(un, \u0026tp_vars-\u003ecommon.unacked_list, list) {\n-\t\t/* check for duplicates */\n-\t\tif (new-\u003eseqno == un-\u003eseqno) {\n-\t\t\tif (new-\u003elen \u003e un-\u003elen)\n-\t\t\t\tun-\u003elen = new-\u003elen;\n-\t\t\tkfree(new);\n-\t\t\tadded = true;\n-\t\t\tbreak;\n-\t\t}\n-\n-\t\t/* look for the right position */\n-\t\tif (batadv_seq_before(new-\u003eseqno, un-\u003eseqno))\n+\tlist_for_each_entry_reverse(un, \u0026tp_vars-\u003eunacked_list, list) {\n+\t\t/* look for the right position - an un which is smaller */\n+\t\tif (batadv_seq_before(seqno, un-\u003eseqno))\n \t\t\tcontinue;\n \n-\t\t/* as soon as an entry having a bigger seqno is found, the new\n-\t\t * one is attached _after_ it. In this way the list is kept in\n-\t\t * ascending order\n+\t\t/* smaller/equal seqno was found but they might be directly\n+\t\t * after another or overlapping. keep only a single entry\n+\t\t *\n+\t\t * It is already known that:\n+\t\t *\n+\t\t *\tun-\u003eseqno \u003c= seqno\n+\t\t *\n+\t\t * When establishing that:\n+\t\t *\n+\t\t *\tseqno \u003c= un-\u003eseqno + un-\u003elen\n+\t\t *\n+\t\t * Then it is not necessary to add a new entry because the\n+\t\t * smaller/equal seqno of un might already contain the new\n+\t\t * received packet or we only add new data directly after\n+\t\t * the end of un. The latter can be identified using:\n+\t\t *\n+\t\t *\tun-\u003eseqno + un-\u003elen \u003c= end_seqno\n \t\t */\n-\t\tlist_add(\u0026new-\u003elist, \u0026un-\u003elist);\n-\t\tadded = true;\n-\t\ttp_vars-\u003ecommon.unacked_count++;\n+\t\tif (!batadv_seq_before(un-\u003eseqno + un-\u003elen, seqno)) {\n+\t\t\t/* new data directly after un? */\n+\t\t\tif (!batadv_seq_before(end_seqno, un-\u003eseqno + un-\u003elen))\n+\t\t\t\tun-\u003elen = end_seqno - un-\u003eseqno;\n+\n+\t\t\t/* un now represents both old un + new range and has to\n+\t\t\t * be used to check if the gap to the next seqno range\n+\t\t\t * was closed\n+\t\t\t */\n+\t\t\tnew = un;\n+\t\t} else {\n+\t\t\t/* as soon as an entry having a smaller seqno is found,\n+\t\t\t * the new one is attached _after_ it. In this way the\n+\t\t\t * list is kept in ascending order\n+\t\t\t */\n+\t\t\tpos = \u0026un-\u003elist;\n+\t\t}\n+\n \t\tbreak;\n \t}\n \n-\t/* received packet with smallest seqno out of order; add it to front */\n-\tif (!added) {\n-\t\tlist_add(\u0026new-\u003elist, \u0026tp_vars-\u003ecommon.unacked_list);\n-\t\ttp_vars-\u003ecommon.unacked_count++;\n+\t/* no entry to merge with was found; insert a new one after the entry\n+\t * with the next smaller seqno (or at the front of the list when the\n+\t * new seqno is the smallest or the list is empty)\n+\t */\n+\tif (!new) {\n+\t\tnew = kmalloc_obj(*new, GFP_ATOMIC);\n+\t\tif (unlikely(!new))\n+\t\t\treturn false;\n+\n+\t\tnew-\u003eseqno = seqno;\n+\t\tnew-\u003elen = payload_len;\n+\n+\t\tlist_add(\u0026new-\u003elist, pos);\n+\t\ttp_vars-\u003eunacked_count++;\n+\t}\n+\n+\t/* check if new filled the gap to the next list entries */\n+\tun = new;\n+\tlist_for_each_entry_safe_continue(un, safe, \u0026tp_vars-\u003eunacked_list, list) {\n+\t\tif (batadv_seq_before(end_seqno, un-\u003eseqno))\n+\t\t\tbreak;\n+\n+\t\t/* next entry is overlapping or adjacent - combine both */\n+\t\tif (batadv_seq_before(end_seqno, un-\u003eseqno + un-\u003elen)) {\n+\t\t\tend_seqno = un-\u003eseqno + un-\u003elen;\n+\t\t\tnew-\u003elen = end_seqno - new-\u003eseqno;\n+\t\t}\n+\n+\t\tlist_del(\u0026un-\u003elist);\n+\t\tkfree(un);\n+\t\ttp_vars-\u003eunacked_count--;\n \t}\n \n \t/* remove the last (biggest) unacked seqno when list is too large */\n-\tif (tp_vars-\u003ecommon.unacked_count \u003e BATADV_TP_MAX_UNACKED) {\n-\t\tun = list_last_entry(\u0026tp_vars-\u003ecommon.unacked_list,\n+\tif (tp_vars-\u003eunacked_count \u003e BATADV_TP_MAX_UNACKED) {\n+\t\tun = list_last_entry(\u0026tp_vars-\u003eunacked_list,\n \t\t\t\t     struct batadv_tp_unacked, list);\n \t\tlist_del(\u0026un-\u003elist);\n \t\tkfree(un);\n-\t\ttp_vars-\u003ecommon.unacked_count--;\n+\t\ttp_vars-\u003eunacked_count--;\n \t}\n \n \treturn true;\n@@ -1477,7 +1526,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,\n  * @tp_vars: the private data of the current TP meter session\n  */\n static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)\n-\t__must_hold(\u0026tp_vars-\u003ecommon.unacked_lock)\n+\t__must_hold(\u0026tp_vars-\u003eack_seqno_lock)\n {\n \tstruct batadv_tp_unacked *un, *safe;\n \tu32 to_ack;\n@@ -1485,7 +1534,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)\n \t/* go through the unacked packet list and possibly ACK them as\n \t * well\n \t */\n-\tlist_for_each_entry_safe(un, safe, \u0026tp_vars-\u003ecommon.unacked_list, list) {\n+\tlist_for_each_entry_safe(un, safe, \u0026tp_vars-\u003eunacked_list, list) {\n \t\t/* the list is ordered, therefore it is possible to stop as soon\n \t\t * there is a gap between the last acked seqno and the seqno of\n \t\t * the packet under inspection\n@@ -1493,14 +1542,14 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)\n \t\tif (batadv_seq_before(tp_vars-\u003elast_recv, un-\u003eseqno))\n \t\t\tbreak;\n \n-\t\tto_ack = un-\u003eseqno + un-\u003elen - tp_vars-\u003elast_recv;\n+\t\tto_ack = un-\u003eseqno + un-\u003elen;\n \n-\t\tif (batadv_seq_before(tp_vars-\u003elast_recv, un-\u003eseqno + un-\u003elen))\n-\t\t\ttp_vars-\u003elast_recv += to_ack;\n+\t\tif (batadv_seq_before(tp_vars-\u003elast_recv, to_ack))\n+\t\t\ttp_vars-\u003elast_recv = to_ack;\n \n \t\tlist_del(\u0026un-\u003elist);\n \t\tkfree(un);\n-\t\ttp_vars-\u003ecommon.unacked_count--;\n+\t\ttp_vars-\u003eunacked_count--;\n \t}\n }\n \n@@ -1547,9 +1596,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,\n \ttp_vars-\u003ecommon.bat_priv = bat_priv;\n \tkref_init(\u0026tp_vars-\u003ecommon.refcount);\n \n-\tspin_lock_init(\u0026tp_vars-\u003ecommon.unacked_lock);\n-\tINIT_LIST_HEAD(\u0026tp_vars-\u003ecommon.unacked_list);\n-\ttp_vars-\u003ecommon.unacked_count = 0;\n+\tspin_lock_init(\u0026tp_vars-\u003eack_seqno_lock);\n+\tINIT_LIST_HEAD(\u0026tp_vars-\u003eunacked_list);\n+\ttp_vars-\u003eunacked_count = 0;\n \n \tkref_get(\u0026tp_vars-\u003ecommon.refcount);\n \ttimer_setup(\u0026tp_vars-\u003ecommon.timer, batadv_tp_receiver_shutdown, 0);\n@@ -1609,7 +1658,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,\n \t\tWRITE_ONCE(tp_vars-\u003elast_recv_time, jiffies);\n \t}\n \n-\tspin_lock_bh(\u0026tp_vars-\u003ecommon.unacked_lock);\n+\tspin_lock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n \n \t/* if the packet is a duplicate, it may be the case that an ACK has been\n \t * lost. Resend the ACK\n@@ -1625,7 +1674,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,\n \t\t * not been enqueued correctly\n \t\t */\n \t\tif (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {\n-\t\t\tspin_unlock_bh(\u0026tp_vars-\u003ecommon.unacked_lock);\n+\t\t\tspin_unlock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n \t\t\tgoto out;\n \t\t}\n \n@@ -1641,7 +1690,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,\n \n send_ack:\n \tto_ack = tp_vars-\u003elast_recv;\n-\tspin_unlock_bh(\u0026tp_vars-\u003ecommon.unacked_lock);\n+\tspin_unlock_bh(\u0026tp_vars-\u003eack_seqno_lock);\n \n \t/* send the ACK. If the received packet was out of order, the ACK that\n \t * is going to be sent is a duplicate (the sender will count them and\ndiff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c\nindex 1c9fb21985f6a5..49bf2ed9ecdc39 100644\n--- a/net/batman-adv/tvlv.c\n+++ b/net/batman-adv/tvlv.c\n@@ -442,6 +442,54 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,\n \treturn NET_RX_SUCCESS;\n }\n \n+/**\n+ * batadv_tvlv_hdr_next() - move a tvlv buffer cursor to the next container\n+ * @tvlv_value: cursor into the tvlv buffer, advanced past the returned\n+ *  container's content on success\n+ * @tvlv_value_len: remaining length of the tvlv buffer, reduced by the returned\n+ *  container's size on success\n+ *\n+ * Parses a single container header at the current cursor position and, if a\n+ * complete container is available, advances the cursor and remaining length\n+ * past it. The returned header stays valid; its content is located at\n+ * (returned header + 1) and is ntohs(hdr-\u003elen) bytes long.\n+ *\n+ * Return: pointer to the next tvlv container header, or NULL if no further\n+ * complete container is present in the buffer.\n+ */\n+static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)\n+{\n+\tstruct batadv_tvlv_hdr *tvlv_hdr;\n+\tu16 tvlv_value_cont_len;\n+\tvoid *tvlv_value_cont;\n+\tu16 tvlv_len;\n+\n+\ttvlv_value_cont = *tvlv_value;\n+\ttvlv_len = *tvlv_value_len;\n+\n+\tif (tvlv_len \u003c sizeof(*tvlv_hdr))\n+\t\treturn NULL;\n+\n+\ttvlv_hdr = tvlv_value_cont;\n+\ttvlv_value_cont_len = ntohs(tvlv_hdr-\u003elen);\n+\ttvlv_value_cont = tvlv_hdr + 1;\n+\ttvlv_len -= sizeof(*tvlv_hdr);\n+\n+\tif (tvlv_value_cont_len \u003e tvlv_len)\n+\t\treturn NULL;\n+\n+\t/* the next tvlv header is accessed assuming (at least) 2-byte\n+\t * alignment, so it must start at an even offset.\n+\t */\n+\tif (tvlv_value_cont_len \u0026 1)\n+\t\treturn NULL;\n+\n+\t*tvlv_value = (u8 *)tvlv_value_cont + tvlv_value_cont_len;\n+\t*tvlv_value_len = tvlv_len - tvlv_value_cont_len;\n+\n+\treturn tvlv_hdr;\n+}\n+\n /**\n  * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container\n  * @tvlv_value: tvlv content\n@@ -457,28 +505,10 @@ static bool batadv_tvlv_containers_contain(void *tvlv_value,\n \t\t\t\t\t   u8 version)\n {\n \tstruct batadv_tvlv_hdr *tvlv_hdr;\n-\tu16 tvlv_value_cont_len;\n-\n-\twhile (tvlv_value_len \u003e= sizeof(*tvlv_hdr)) {\n-\t\ttvlv_hdr = tvlv_value;\n-\t\ttvlv_value_cont_len = ntohs(tvlv_hdr-\u003elen);\n-\t\ttvlv_value = tvlv_hdr + 1;\n-\t\ttvlv_value_len -= sizeof(*tvlv_hdr);\n-\n-\t\tif (tvlv_value_cont_len \u003e tvlv_value_len)\n-\t\t\tbreak;\n-\n-\t\t/* the next tvlv header is accessed assuming (at least) 2-byte\n-\t\t * alignment, so it must start at an even offset.\n-\t\t */\n-\t\tif (tvlv_value_cont_len \u0026 1)\n-\t\t\tbreak;\n \n+\twhile ((tvlv_hdr = batadv_tvlv_hdr_next(\u0026tvlv_value, \u0026tvlv_value_len))) {\n \t\tif (tvlv_hdr-\u003etype == type \u0026\u0026 tvlv_hdr-\u003eversion == version)\n \t\t\treturn true;\n-\n-\t\ttvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;\n-\t\ttvlv_value_len -= tvlv_value_cont_len;\n \t}\n \n \treturn false;\n@@ -511,20 +541,8 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,\n \tu8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;\n \tint ret = NET_RX_SUCCESS;\n \n-\twhile (tvlv_value_len \u003e= sizeof(*tvlv_hdr)) {\n-\t\ttvlv_hdr = tvlv_value;\n+\twhile ((tvlv_hdr = batadv_tvlv_hdr_next(\u0026tvlv_value, \u0026tvlv_value_len))) {\n \t\ttvlv_value_cont_len = ntohs(tvlv_hdr-\u003elen);\n-\t\ttvlv_value = tvlv_hdr + 1;\n-\t\ttvlv_value_len -= sizeof(*tvlv_hdr);\n-\n-\t\tif (tvlv_value_cont_len \u003e tvlv_value_len)\n-\t\t\tbreak;\n-\n-\t\t/* the next tvlv header is accessed assuming (at least) 2-byte\n-\t\t * alignment, so it must start at an even offset.\n-\t\t */\n-\t\tif (tvlv_value_cont_len \u0026 1)\n-\t\t\tbreak;\n \n \t\ttvlv_handler = batadv_tvlv_handler_get(bat_priv,\n \t\t\t\t\t\t       tvlv_hdr-\u003etype,\n@@ -532,11 +550,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,\n \n \t\tret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,\n \t\t\t\t\t\tpacket_type, orig_node, skb,\n-\t\t\t\t\t\ttvlv_value,\n+\t\t\t\t\t\ttvlv_hdr + 1,\n \t\t\t\t\t\ttvlv_value_cont_len);\n \t\tbatadv_tvlv_handler_put(tvlv_handler);\n-\t\ttvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;\n-\t\ttvlv_value_len -= tvlv_value_cont_len;\n \t}\n \n \tif (packet_type != BATADV_IV_OGM \u0026\u0026\ndiff --git a/net/batman-adv/types.h b/net/batman-adv/types.h\nindex b1f9f8964c3fd2..cd12755d21f356 100644\n--- a/net/batman-adv/types.h\n+++ b/net/batman-adv/types.h\n@@ -214,9 +214,6 @@ struct batadv_wifi_net_device_state {\n  * struct batadv_hard_iface - network device known to batman-adv\n  */\n struct batadv_hard_iface {\n-\t/** @list: list node for batadv_hardif_list */\n-\tstruct list_head list;\n-\n \t/** @if_status: status of the interface for batman-adv */\n \tchar if_status;\n \n@@ -1335,9 +1332,9 @@ struct batadv_tp_unacked {\n \tu32 seqno;\n \n \t/** @len: length of the packet */\n-\tu16 len;\n+\tu32 len;\n \n-\t/** @list: list node for \u0026batadv_tp_vars_common.unacked_list */\n+\t/** @list: list node for \u0026batadv_tp_receiver.unacked_list */\n \tstruct list_head list;\n };\n \n@@ -1360,15 +1357,6 @@ struct batadv_tp_vars_common {\n \t/** @session: TP session identifier */\n \tu8 session[2];\n \n-\t/** @unacked_list: list of unacked packets (meta-info only) */\n-\tstruct list_head unacked_list;\n-\n-\t/** @unacked_lock: protect unacked_list + \u0026batadv_tp_receiver.last_recv */\n-\tspinlock_t unacked_lock;\n-\n-\t/** @unacked_count: number of unacked entries */\n-\tsize_t unacked_count;\n-\n \t/** @refcount: number of context where the object is used */\n \tstruct kref refcount;\n \n@@ -1482,6 +1470,15 @@ struct batadv_tp_receiver {\n \n \t/** @last_recv_time: time (jiffies) a msg was received */\n \tunsigned long last_recv_time;\n+\n+\t/** @unacked_list: list of unacked packets (meta-info only) */\n+\tstruct list_head unacked_list;\n+\n+\t/** @ack_seqno_lock: protect unacked_list + \u0026batadv_tp_receiver.last_recv */\n+\tspinlock_t ack_seqno_lock;\n+\n+\t/** @unacked_count: number of unacked entries */\n+\tsize_t unacked_count;\n };\n \n /**\n@@ -1679,6 +1676,9 @@ struct batadv_priv {\n \t/** @tp_num: number of currently active tp sessions */\n \tatomic_t tp_num;\n \n+\t/** @hardif_generation: generation counter added to netlink hardif dumps */\n+\tunsigned int hardif_generation;\n+\n \t/** @orig_work: work queue callback item for orig node purging */\n \tstruct delayed_work orig_work;\n \n"
}

2/1 2026/06/30 21:20 agent patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "BATMAN_ADV",
    "BATMAN_ADV_BATMAN_V",
    "BATMAN_ADV_BLA"
  ],
  "FocusSymbols": [
    "batadv_hardif_enable_interface",
    "batadv_hardif_disable_interface",
    "batadv_tp_handle_out_of_order",
    "batadv_tp_recv_msg",
    "batadv_tvlv_containers_process",
    "batadv_tvlv_containers_contain"
  ],
  "Reasoning": "The patch makes functional changes to the B.A.T.M.A.N. Advanced routing protocol. It modifies the handling of hard interfaces (adding/removing them from the mesh), the processing of out-of-order TP meter packets, and the parsing of TVLV containers. These are core logic changes reachable from user space via netlink/sysfs and network packets, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

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 aa2ef41dd33cad16598635d20e34d53f58de5c0b
Author: syz-cluster <triage@syzkaller.com>
Date:   Tue Jun 30 21:20:52 2026 +0000

    syz-cluster: applied patch under review

diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c
index bb2f012b454ea4..22622283f59b0c 100644
--- a/net/batman-adv/bat_iv_ogm.c
+++ b/net/batman-adv/bat_iv_ogm.c
@@ -404,23 +404,14 @@ static void batadv_iv_ogm_send_to_if(struct batadv_forw_packet *forw_packet,
 /* send a batman ogm packet */
 static void batadv_iv_ogm_emit(struct batadv_forw_packet *forw_packet)
 {
-	struct net_device *mesh_iface;
-
 	if (!forw_packet->if_incoming) {
 		pr_err("Error - can't forward packet: incoming iface not specified\n");
 		return;
 	}
 
-	mesh_iface = forw_packet->if_incoming->mesh_iface;
-
 	if (WARN_ON(!forw_packet->if_outgoing))
 		return;
 
-	if (forw_packet->if_outgoing->mesh_iface != mesh_iface) {
-		pr_warn("%s: mesh interface switch for queued OGM\n", __func__);
-		return;
-	}
-
 	if (forw_packet->if_incoming->if_status != BATADV_IF_ACTIVE)
 		return;
 
@@ -910,8 +901,7 @@ static void batadv_iv_ogm_schedule_buff(struct batadv_hard_iface *hard_iface)
 
 static void batadv_iv_ogm_schedule(struct batadv_hard_iface *hard_iface)
 {
-	if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
-	    hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+	if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
 		return;
 
 	mutex_lock(&hard_iface->bat_iv.ogm_buff_mutex);
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index 4841f0f1a9b139..262e40040007cc 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -90,12 +90,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
 	u32 throughput;
 	int ret;
 
-	/* don't query throughput when no longer associated with any
-	 * batman-adv interface
-	 */
-	if (!mesh_iface)
-		return false;
-
 	/* if the user specified a customised value for this interface, then
 	 * return it directly
 	 */
@@ -311,8 +305,7 @@ static void batadv_v_elp_periodic_work(struct work_struct *work)
 		goto out;
 
 	/* we are in the process of shutting this interface down */
-	if (hard_iface->if_status == BATADV_IF_NOT_IN_USE ||
-	    hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
+	if (hard_iface->if_status == BATADV_IF_TO_BE_REMOVED)
 		goto out;
 
 	/* the interface was enabled but may not be ready yet */
diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c
index 037921aad35d58..e921d49f7ecedd 100644
--- a/net/batman-adv/bat_v_ogm.c
+++ b/net/batman-adv/bat_v_ogm.c
@@ -115,14 +115,14 @@ static void batadv_v_ogm_start_timer(struct batadv_priv *bat_priv)
 
 /**
  * batadv_v_ogm_send_to_if() - send a batman ogm using a given interface
- * @bat_priv: the bat priv with all the mesh interface information
  * @skb: the OGM to send
  * @hard_iface: the interface to use to send the OGM
  */
-static void batadv_v_ogm_send_to_if(struct batadv_priv *bat_priv,
-				    struct sk_buff *skb,
+static void batadv_v_ogm_send_to_if(struct sk_buff *skb,
 				    struct batadv_hard_iface *hard_iface)
 {
+	struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
+
 	if (hard_iface->if_status != BATADV_IF_ACTIVE) {
 		kfree_skb(skb);
 		return;
@@ -189,7 +189,6 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
 
 /**
  * batadv_v_ogm_aggr_send() - flush & send aggregation queue
- * @bat_priv: the bat priv with all the mesh interface information
  * @hard_iface: the interface with the aggregation queue to flush
  *
  * Aggregates all OGMv2 packets currently in the aggregation queue into a
@@ -199,8 +198,7 @@ static void batadv_v_ogm_aggr_list_free(struct batadv_hard_iface *hard_iface)
  *
  * Caller needs to hold the hard_iface->bat_v.aggr_list.lock.
  */
-static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
-				   struct batadv_hard_iface *hard_iface)
+static void batadv_v_ogm_aggr_send(struct batadv_hard_iface *hard_iface)
 {
 	unsigned int aggr_len = hard_iface->bat_v.aggr_len;
 	struct sk_buff *skb_aggr;
@@ -230,26 +228,21 @@ static void batadv_v_ogm_aggr_send(struct batadv_priv *bat_priv,
 		consume_skb(skb);
 	}
 
-	batadv_v_ogm_send_to_if(bat_priv, skb_aggr, hard_iface);
+	batadv_v_ogm_send_to_if(skb_aggr, hard_iface);
 }
 
 /**
  * batadv_v_ogm_queue_on_if() - queue a batman ogm on a given interface
- * @bat_priv: the bat priv with all the mesh interface information
  * @skb: the OGM to queue
  * @hard_iface: the interface to queue the OGM on
  */
-static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
-				     struct sk_buff *skb,
+static void batadv_v_ogm_queue_on_if(struct sk_buff *skb,
 				     struct batadv_hard_iface *hard_iface)
 {
-	if (hard_iface->mesh_iface != bat_priv->mesh_iface) {
-		kfree_skb(skb);
-		return;
-	}
+	struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
 
 	if (!READ_ONCE(bat_priv->aggregated_ogms)) {
-		batadv_v_ogm_send_to_if(bat_priv, skb, hard_iface);
+		batadv_v_ogm_send_to_if(skb, hard_iface);
 		return;
 	}
 
@@ -260,7 +253,7 @@ static void batadv_v_ogm_queue_on_if(struct batadv_priv *bat_priv,
 	}
 
 	if (!batadv_v_ogm_queue_left(skb, hard_iface))
-		batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+		batadv_v_ogm_aggr_send(hard_iface);
 
 	hard_iface->bat_v.aggr_len += batadv_v_ogm_len(skb);
 	__skb_queue_tail(&hard_iface->bat_v.aggr_list, skb);
@@ -357,7 +350,7 @@ static void batadv_v_ogm_send_meshif(struct batadv_priv *bat_priv)
 			break;
 		}
 
-		batadv_v_ogm_queue_on_if(bat_priv, skb_tmp, hard_iface);
+		batadv_v_ogm_queue_on_if(skb_tmp, hard_iface);
 		batadv_hardif_put(hard_iface);
 	}
 	rcu_read_unlock();
@@ -397,14 +390,12 @@ void batadv_v_ogm_aggr_work(struct work_struct *work)
 {
 	struct batadv_hard_iface_bat_v *batv;
 	struct batadv_hard_iface *hard_iface;
-	struct batadv_priv *bat_priv;
 
 	batv = container_of(work, struct batadv_hard_iface_bat_v, aggr_wq.work);
 	hard_iface = container_of(batv, struct batadv_hard_iface, bat_v);
-	bat_priv = netdev_priv(hard_iface->mesh_iface);
 
 	spin_lock_bh(&hard_iface->bat_v.aggr_list.lock);
-	batadv_v_ogm_aggr_send(bat_priv, hard_iface);
+	batadv_v_ogm_aggr_send(hard_iface);
 	spin_unlock_bh(&hard_iface->bat_v.aggr_list.lock);
 
 	batadv_v_ogm_start_queue_timer(hard_iface);
@@ -601,7 +592,7 @@ static void batadv_v_ogm_forward(struct batadv_priv *bat_priv,
 		   if_outgoing->net_dev->name, ntohl(ogm_forward->throughput),
 		   ogm_forward->ttl, if_incoming->net_dev->name);
 
-	batadv_v_ogm_queue_on_if(bat_priv, skb, if_outgoing);
+	batadv_v_ogm_queue_on_if(skb, if_outgoing);
 
 out:
 	batadv_orig_ifinfo_put(orig_ifinfo);
diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c
index 5c73f6ba16cff3..f9a1fadf8de9e4 100644
--- a/net/batman-adv/bridge_loop_avoidance.c
+++ b/net/batman-adv/bridge_loop_avoidance.c
@@ -344,7 +344,6 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
 	struct sk_buff *skb;
 	struct ethhdr *ethhdr;
 	struct batadv_hard_iface *primary_if;
-	struct net_device *mesh_iface;
 	u8 *hw_src;
 	struct batadv_bla_claim_dst local_claim_dest;
 	__be32 zeroip = 0;
@@ -357,14 +356,10 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
 	       sizeof(local_claim_dest));
 	local_claim_dest.type = claimtype;
 
-	mesh_iface = READ_ONCE(primary_if->mesh_iface);
-	if (!mesh_iface)
-		goto out;
-
 	skb = arp_create(ARPOP_REPLY, ETH_P_ARP,
 			 /* IP DST: 0.0.0.0 */
 			 zeroip,
-			 mesh_iface,
+			 primary_if->mesh_iface,
 			 /* IP SRC: 0.0.0.0 */
 			 zeroip,
 			 /* Ethernet DST: Broadcast */
@@ -442,7 +437,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, const u8 *mac,
 	}
 
 	skb_reset_mac_header(skb);
-	skb->protocol = eth_type_trans(skb, mesh_iface);
+	skb->protocol = eth_type_trans(skb, primary_if->mesh_iface);
 	batadv_inc_counter(bat_priv, BATADV_CNT_RX);
 	batadv_add_counter(bat_priv, BATADV_CNT_RX_BYTES,
 			   skb->len + ETH_HLEN);
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 03d01c20a9548b..b6867576bbafae 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -63,6 +63,7 @@ void batadv_hardif_release(struct kref *ref)
 	struct batadv_hard_iface *hard_iface;
 
 	hard_iface = container_of(ref, struct batadv_hard_iface, refcount);
+	netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
 	netdev_put(hard_iface->net_dev, &hard_iface->dev_tracker);
 
 	kfree_rcu(hard_iface, rcu);
@@ -75,21 +76,21 @@ void batadv_hardif_release(struct kref *ref)
  * Return: batadv_hard_iface of net_dev (with increased refcnt), NULL on errors
  */
 struct batadv_hard_iface *
-batadv_hardif_get_by_netdev(const struct net_device *net_dev)
+batadv_hardif_get_by_netdev(struct net_device *net_dev)
 {
 	struct batadv_hard_iface *hard_iface;
+	struct net_device *mesh_iface;
 
-	rcu_read_lock();
-	list_for_each_entry_rcu(hard_iface, &batadv_hardif_list, list) {
-		if (hard_iface->net_dev == net_dev &&
-		    kref_get_unless_zero(&hard_iface->refcount))
-			goto out;
-	}
+	ASSERT_RTNL();
 
-	hard_iface = NULL;
+	mesh_iface = netdev_master_upper_dev_get(net_dev);
+	if (!mesh_iface || !batadv_meshif_is_valid(mesh_iface))
+		return NULL;
+
+	hard_iface = netdev_lower_dev_get_private(mesh_iface, net_dev);
+	if (!kref_get_unless_zero(&hard_iface->refcount))
+		return NULL;
 
-out:
-	rcu_read_unlock();
 	return hard_iface;
 }
 
@@ -245,7 +246,7 @@ struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
 	}
 
 	hard_iface = batadv_hardif_get_by_netdev(netdev);
-	if (!hard_iface || !hard_iface->mesh_iface)
+	if (!hard_iface)
 		goto out;
 
 	net = dev_net(hard_iface->mesh_iface);
@@ -539,16 +540,10 @@ static void batadv_check_known_mac_addr(const struct batadv_hard_iface *hard_ifa
 	const struct batadv_hard_iface *tmp_hard_iface;
 	struct list_head *iter;
 
-	if (!mesh_iface)
-		return;
-
 	netdev_for_each_lower_private(mesh_iface, tmp_hard_iface, iter) {
 		if (tmp_hard_iface == hard_iface)
 			continue;
 
-		if (tmp_hard_iface->if_status == BATADV_IF_NOT_IN_USE)
-			continue;
-
 		if (!batadv_compare_eth(tmp_hard_iface->net_dev->dev_addr,
 					hard_iface->net_dev->dev_addr))
 			continue;
@@ -574,9 +569,6 @@ static void batadv_hardif_recalc_extra_skbroom(struct net_device *mesh_iface)
 
 	rcu_read_lock();
 	netdev_for_each_lower_private_rcu(mesh_iface, hard_iface, iter) {
-		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
-			continue;
-
 		lower_header_len = max_t(unsigned short, lower_header_len,
 					 hard_iface->net_dev->hard_header_len);
 
@@ -723,38 +715,63 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
 }
 
 /**
- * batadv_hardif_enable_interface() - Enslave hard interface to mesh interface
- * @hard_iface: hard interface to add to mesh interface
+ * batadv_hardif_enable_interface() - Enslave interface to mesh interface
+ * @net_dev: netdev struct of the interface to add to mesh interface
  * @mesh_iface: netdev struct of the mesh interface
  *
  * Return: 0 on success or negative error number in case of failure
  */
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+int batadv_hardif_enable_interface(struct net_device *net_dev,
 				   struct net_device *mesh_iface)
 {
 	struct batadv_priv *bat_priv;
 	__be16 ethertype = htons(ETH_P_BATMAN);
 	int max_header_len = batadv_max_header_len();
+	struct batadv_hard_iface *hard_iface;
 	unsigned int required_mtu;
 	unsigned int hardif_mtu;
 	bool fragmentation;
 	int ret;
 
-	hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
+	ASSERT_RTNL();
+
+	if (!batadv_is_valid_iface(net_dev))
+		return -EINVAL;
+
+	hardif_mtu = READ_ONCE(net_dev->mtu);
 	required_mtu = READ_ONCE(mesh_iface->mtu) + max_header_len;
 
 	if (hardif_mtu < ETH_MIN_MTU + max_header_len)
 		return -EINVAL;
 
-	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
-		goto out;
+	hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
+	if (!hard_iface)
+		return -ENOMEM;
 
-	kref_get(&hard_iface->refcount);
+	netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
+	hard_iface->net_dev = net_dev;
+
+	hard_iface->if_status = BATADV_IF_INACTIVE;
+
+	INIT_HLIST_HEAD(&hard_iface->neigh_list);
+
+	mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
+	spin_lock_init(&hard_iface->neigh_list_lock);
+	kref_init(&hard_iface->refcount);
+
+	hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
+	if (batadv_is_wifi_hardif(hard_iface))
+		hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
+
+	WRITE_ONCE(hard_iface->hop_penalty, 0);
+
+	batadv_v_hardif_init(hard_iface);
 
 	netdev_hold(mesh_iface, &hard_iface->meshif_dev_tracker, GFP_ATOMIC);
 	hard_iface->mesh_iface = mesh_iface;
 	bat_priv = netdev_priv(hard_iface->mesh_iface);
 
+	bat_priv->hardif_generation++;
 	ret = netdev_master_upper_dev_link(hard_iface->net_dev,
 					   mesh_iface, hard_iface, NULL, NULL);
 	if (ret)
@@ -764,9 +781,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 	if (ret < 0)
 		goto err_upper;
 
-	hard_iface->if_status = BATADV_IF_INACTIVE;
-
-	kref_get(&hard_iface->refcount);
 	hard_iface->batman_adv_ptype.type = ethertype;
 	hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
 	hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
@@ -802,14 +816,11 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 	if (bat_priv->algo_ops->iface.enabled)
 		bat_priv->algo_ops->iface.enabled(hard_iface);
 
-out:
 	return 0;
 
 err_upper:
 	netdev_upper_dev_unlink(hard_iface->net_dev, mesh_iface);
 err_dev:
-	hard_iface->mesh_iface = NULL;
-	netdev_put(mesh_iface, &hard_iface->meshif_dev_tracker);
 	batadv_hardif_put(hard_iface);
 	return ret;
 }
@@ -821,17 +832,18 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
 void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
 {
 	struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
-	struct batadv_hard_iface *primary_if = NULL;
+	struct batadv_hard_iface *primary_if;
+
+	ASSERT_RTNL();
 
 	batadv_hardif_deactivate_interface(hard_iface);
 
 	if (hard_iface->if_status != BATADV_IF_INACTIVE)
-		goto out;
+		return;
 
 	batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
 		    hard_iface->net_dev->name);
 	dev_remove_pack(&hard_iface->batman_adv_ptype);
-	batadv_hardif_put(hard_iface);
 
 	primary_if = batadv_primary_if_get_selected(bat_priv);
 	if (hard_iface == primary_if) {
@@ -842,15 +854,16 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
 
 		batadv_hardif_put(new_if);
 	}
+	batadv_hardif_put(primary_if);
 
 	bat_priv->algo_ops->iface.disable(hard_iface);
-	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
+	hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
 
 	/* delete all references to this hard_iface */
 	batadv_purge_orig_ref(bat_priv);
 	batadv_purge_outstanding_packets(bat_priv, hard_iface);
-	netdev_put(hard_iface->mesh_iface, &hard_iface->meshif_dev_tracker);
 
+	bat_priv->hardif_generation++;
 	netdev_upper_dev_unlink(hard_iface->net_dev, hard_iface->mesh_iface);
 	batadv_hardif_recalc_extra_skbroom(hard_iface->mesh_iface);
 
@@ -858,67 +871,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
 	if (list_empty(&hard_iface->mesh_iface->adj_list.lower))
 		batadv_gw_check_client_stop(bat_priv);
 
-	hard_iface->mesh_iface = NULL;
-	batadv_hardif_put(hard_iface);
-
-out:
-	batadv_hardif_put(primary_if);
-}
-
-static struct batadv_hard_iface *
-batadv_hardif_add_interface(struct net_device *net_dev)
-{
-	struct batadv_hard_iface *hard_iface;
-
-	ASSERT_RTNL();
-
-	if (!batadv_is_valid_iface(net_dev))
-		return NULL;
-
-	hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
-	if (!hard_iface)
-		return NULL;
-
-	netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
-	hard_iface->net_dev = net_dev;
-
-	hard_iface->mesh_iface = NULL;
-	hard_iface->if_status = BATADV_IF_NOT_IN_USE;
-
-	INIT_LIST_HEAD(&hard_iface->list);
-	INIT_HLIST_HEAD(&hard_iface->neigh_list);
-
-	mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
-	spin_lock_init(&hard_iface->neigh_list_lock);
-	kref_init(&hard_iface->refcount);
-
-	hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
-	if (batadv_is_wifi_hardif(hard_iface))
-		hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
-
-	WRITE_ONCE(hard_iface->hop_penalty, 0);
-
-	batadv_v_hardif_init(hard_iface);
-
-	kref_get(&hard_iface->refcount);
-	list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
-	batadv_hardif_generation++;
-
-	return hard_iface;
-}
-
-static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
-{
-	ASSERT_RTNL();
-
-	/* first deactivate interface */
-	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
-		batadv_hardif_disable_interface(hard_iface);
-
-	if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
-		return;
-
-	hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
 	batadv_hardif_put(hard_iface);
 }
 
@@ -1082,10 +1034,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
 	batadv_wifi_net_device_event(event, net_dev);
 
 	hard_iface = batadv_hardif_get_by_netdev(net_dev);
-	if (!hard_iface && (event == NETDEV_REGISTER ||
-			    event == NETDEV_POST_TYPE_CHANGE))
-		hard_iface = batadv_hardif_add_interface(net_dev);
-
 	if (!hard_iface)
 		goto out;
 
@@ -1099,19 +1047,12 @@ static int batadv_hard_if_event(struct notifier_block *this,
 		break;
 	case NETDEV_UNREGISTER:
 	case NETDEV_PRE_TYPE_CHANGE:
-		list_del_rcu(&hard_iface->list);
-		batadv_hardif_generation++;
-
-		batadv_hardif_remove_interface(hard_iface);
+		batadv_hardif_disable_interface(hard_iface);
 		break;
 	case NETDEV_CHANGEMTU:
-		if (hard_iface->mesh_iface)
-			batadv_update_min_mtu(hard_iface->mesh_iface);
+		batadv_update_min_mtu(hard_iface->mesh_iface);
 		break;
 	case NETDEV_CHANGEADDR:
-		if (hard_iface->if_status == BATADV_IF_NOT_IN_USE)
-			goto hardif_put;
-
 		batadv_check_known_mac_addr(hard_iface);
 
 		bat_priv = netdev_priv(hard_iface->mesh_iface);
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index af31696c397808..935f47ca9a48fd 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -21,12 +21,6 @@
  * enum batadv_hard_if_state - State of a hard interface
  */
 enum batadv_hard_if_state {
-	/**
-	 * @BATADV_IF_NOT_IN_USE: interface is not used as slave interface of a
-	 * batman-adv mesh interface
-	 */
-	BATADV_IF_NOT_IN_USE,
-
 	/**
 	 * @BATADV_IF_TO_BE_REMOVED: interface will be removed from mesh
 	 * interface
@@ -74,8 +68,8 @@ u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev);
 u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
 bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
 struct batadv_hard_iface*
-batadv_hardif_get_by_netdev(const struct net_device *net_dev);
-int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
+batadv_hardif_get_by_netdev(struct net_device *net_dev);
+int batadv_hardif_enable_interface(struct net_device *net_dev,
 				   struct net_device *mesh_iface);
 void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
 int batadv_hardif_min_mtu(struct net_device *mesh_iface);
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index 3c4572284b532f..04bb030ef299ac 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -59,11 +59,6 @@
 #include "tp_meter.h"
 #include "translation-table.h"
 
-/* List manipulations on hardif_list have to be rtnl_lock()'ed,
- * list traversals just rcu-locked
- */
-struct list_head batadv_hardif_list;
-unsigned int batadv_hardif_generation;
 static int (*batadv_rx_handler[256])(struct sk_buff *skb,
 				     struct batadv_hard_iface *recv_if);
 
@@ -95,7 +90,6 @@ static int __init batadv_init(void)
 	if (ret < 0)
 		return ret;
 
-	INIT_LIST_HEAD(&batadv_hardif_list);
 	batadv_algo_init();
 
 	batadv_recv_handler_init();
@@ -450,9 +444,6 @@ int batadv_batman_skb_recv(struct sk_buff *skb, struct net_device *dev,
 	if (unlikely(skb->mac_len != ETH_HLEN || !skb_mac_header(skb)))
 		goto err_free;
 
-	if (!hard_iface->mesh_iface)
-		goto err_free;
-
 	bat_priv = netdev_priv(hard_iface->mesh_iface);
 
 	if (READ_ONCE(bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h
index f68fc8b7239cd5..e738758ee4a7ae 100644
--- a/net/batman-adv/main.h
+++ b/net/batman-adv/main.h
@@ -226,9 +226,6 @@ static inline int batadv_print_vid(unsigned short vid)
 		return -1;
 }
 
-extern struct list_head batadv_hardif_list;
-extern unsigned int batadv_hardif_generation;
-
 extern struct workqueue_struct *batadv_event_workqueue;
 
 int batadv_mesh_init(struct net_device *mesh_iface);
diff --git a/net/batman-adv/mesh-interface.c b/net/batman-adv/mesh-interface.c
index 44026810b99cea..a37368c1f5b55c 100644
--- a/net/batman-adv/mesh-interface.c
+++ b/net/batman-adv/mesh-interface.c
@@ -836,18 +836,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,
 				   struct net_device *slave_dev,
 				   struct netlink_ext_ack *extack)
 {
-	struct batadv_hard_iface *hard_iface;
-	int ret = -EINVAL;
-
-	hard_iface = batadv_hardif_get_by_netdev(slave_dev);
-	if (!hard_iface || hard_iface->mesh_iface)
-		goto out;
-
-	ret = batadv_hardif_enable_interface(hard_iface, dev);
-
-out:
-	batadv_hardif_put(hard_iface);
-	return ret;
+	return batadv_hardif_enable_interface(slave_dev, dev);
 }
 
 /**
diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c
index 4cf9e3c54ad3b2..d2bc48c7071434 100644
--- a/net/batman-adv/netlink.c
+++ b/net/batman-adv/netlink.c
@@ -968,7 +968,7 @@ batadv_netlink_dump_hardif(struct sk_buff *msg, struct netlink_callback *cb)
 	bat_priv = netdev_priv(mesh_iface);
 
 	rtnl_lock();
-	cb->seq = batadv_hardif_generation << 1 | 1;
+	cb->seq = bat_priv->hardif_generation << 1 | 1;
 
 	netdev_for_each_lower_private(mesh_iface, hard_iface, iter) {
 		if (i++ < skip)
@@ -1211,7 +1211,9 @@ batadv_netlink_get_hardif_from_ifindex(struct batadv_priv *bat_priv,
 	if (!hard_dev)
 		return ERR_PTR(-ENODEV);
 
+	rtnl_lock();
 	hard_iface = batadv_hardif_get_by_netdev(hard_dev);
+	rtnl_unlock();
 	if (!hard_iface)
 		goto err_put_harddev;
 
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index 9b38bd9e8da7ec..48f837cf665a2a 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -1033,7 +1033,6 @@ batadv_purge_neigh_ifinfo(struct batadv_priv *bat_priv,
 
 		/* don't purge if the interface is not (going) down */
 		if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
-		    if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
 		    if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
 			continue;
 
@@ -1077,7 +1076,6 @@ batadv_purge_orig_ifinfo(struct batadv_priv *bat_priv,
 
 		/* don't purge if the interface is not (going) down */
 		if (if_outgoing->if_status != BATADV_IF_INACTIVE &&
-		    if_outgoing->if_status != BATADV_IF_NOT_IN_USE &&
 		    if_outgoing->if_status != BATADV_IF_TO_BE_REMOVED)
 			continue;
 
@@ -1127,10 +1125,8 @@ batadv_purge_orig_neighbors(struct batadv_priv *bat_priv,
 
 		if (batadv_has_timed_out(last_seen, BATADV_PURGE_TIMEOUT) ||
 		    if_incoming->if_status == BATADV_IF_INACTIVE ||
-		    if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
 		    if_incoming->if_status == BATADV_IF_TO_BE_REMOVED) {
 			if (if_incoming->if_status == BATADV_IF_INACTIVE ||
-			    if_incoming->if_status == BATADV_IF_NOT_IN_USE ||
 			    if_incoming->if_status == BATADV_IF_TO_BE_REMOVED)
 				batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
 					   "neighbor purge: originator %pM, neighbor: %pM, iface: %s\n",
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index c2eea7dbc48833..00467aa79de9dc 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -358,28 +358,16 @@ batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,
 }
 
 /**
- * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists
+ * batadv_tp_sender_release() - release batadv_tp_sender
  *  and queue for free after rcu grace period
- * @ref: kref pointer of the batadv_tp_vars_common
+ * @ref: kref pointer of the batadv_tp_sender
  */
-static void batadv_tp_vars_common_release(struct kref *ref)
+static void batadv_tp_sender_release(struct kref *ref)
 {
-	struct batadv_tp_vars_common *tp_vars;
-	struct batadv_tp_unacked *un, *safe;
-
-	tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);
-
-	/* lock should not be needed because this object is now out of any
-	 * context!
-	 */
-	spin_lock_bh(&tp_vars->unacked_lock);
-	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
-		list_del(&un->list);
-		kfree(un);
-	}
-	spin_unlock_bh(&tp_vars->unacked_lock);
+	struct batadv_tp_sender *tp_vars;
 
-	kfree_rcu(tp_vars, rcu);
+	tp_vars = container_of(ref, struct batadv_tp_sender, common.refcount);
+	kfree_rcu(tp_vars, common.rcu);
 }
 
 /**
@@ -392,7 +380,7 @@ static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)
 	if (!tp_vars)
 		return;
 
-	kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+	kref_put(&tp_vars->common.refcount, batadv_tp_sender_release);
 }
 
 /**
@@ -1145,9 +1133,6 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	init_waitqueue_head(&tp_vars->more_bytes);
 	init_completion(&tp_vars->finished);
 
-	spin_lock_init(&tp_vars->common.unacked_lock);
-	INIT_LIST_HEAD(&tp_vars->common.unacked_list);
-
 	spin_lock_init(&tp_vars->cc_lock);
 
 	tp_vars->prerandom_offset = 0;
@@ -1251,6 +1236,33 @@ batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst
 	return tp_vars;
 }
 
+/**
+ * batadv_tp_receiver_release() - release batadv_tp_receiver
+ *  and queue for free after rcu grace period
+ * @ref: kref pointer of the batadv_tp_receiver
+ */
+static void batadv_tp_receiver_release(struct kref *ref)
+{
+	struct batadv_tp_receiver *tp_vars;
+	struct batadv_tp_unacked *safe;
+	struct batadv_tp_unacked *un;
+
+	tp_vars = container_of(ref, struct batadv_tp_receiver, common.refcount);
+
+	/* lock should not be needed because this object is now out of any
+	 * context!
+	 */
+	spin_lock_bh(&tp_vars->ack_seqno_lock);
+	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+		list_del(&un->list);
+		kfree(un);
+		tp_vars->unacked_count--;
+	}
+	spin_unlock_bh(&tp_vars->ack_seqno_lock);
+
+	kfree_rcu(tp_vars, common.rcu);
+}
+
 /**
  * batadv_tp_receiver_put() - decrement the batadv_tp_receiver
  *  refcounter and possibly release it
@@ -1261,7 +1273,7 @@ static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)
 	if (!tp_vars)
 		return;
 
-	kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+	kref_put(&tp_vars->common.refcount, batadv_tp_receiver_release);
 }
 
 /**
@@ -1304,13 +1316,13 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
 	if (batadv_tp_list_detach(&tp_vars->common))
 		batadv_tp_receiver_put(tp_vars);
 
-	spin_lock_bh(&tp_vars->common.unacked_lock);
-	list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+	spin_lock_bh(&tp_vars->ack_seqno_lock);
+	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
 		list_del(&un->list);
 		kfree(un);
-		tp_vars->common.unacked_count--;
+		tp_vars->unacked_count--;
 	}
-	spin_unlock_bh(&tp_vars->common.unacked_lock);
+	spin_unlock_bh(&tp_vars->ack_seqno_lock);
 
 	/* drop reference of timer */
 	if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
@@ -1403,69 +1415,106 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
  */
 static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
 					  u32 seqno, u32 payload_len)
-	__must_hold(&tp_vars->common.unacked_lock)
+	__must_hold(&tp_vars->ack_seqno_lock)
 {
-	struct batadv_tp_unacked *un, *new;
-	bool added = false;
-
-	new = kmalloc_obj(*new, GFP_ATOMIC);
-	if (unlikely(!new))
-		return false;
-
-	new->seqno = seqno;
-	new->len = payload_len;
-
-	/* if the list is empty immediately attach this new object */
-	if (list_empty(&tp_vars->common.unacked_list)) {
-		list_add(&new->list, &tp_vars->common.unacked_list);
-		tp_vars->common.unacked_count++;
-		return true;
-	}
-
-	/* otherwise loop over the list and either drop the packet because this
-	 * is a duplicate or store it at the right position.
+	struct list_head *pos = &tp_vars->unacked_list;
+	struct batadv_tp_unacked *new = NULL;
+	u32 end_seqno = seqno + payload_len;
+	struct batadv_tp_unacked *safe;
+	struct batadv_tp_unacked *un;
+
+	/* loop over the list to find either an existing entry which the new
+	 * seqno range can be merged with or the position at which a new entry
+	 * has to be inserted.
 	 *
 	 * The iteration is done in the reverse way because it is likely that
 	 * the last received packet (the one being processed now) has a bigger
 	 * seqno than all the others already stored.
 	 */
-	list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
-		/* check for duplicates */
-		if (new->seqno == un->seqno) {
-			if (new->len > un->len)
-				un->len = new->len;
-			kfree(new);
-			added = true;
-			break;
-		}
-
-		/* look for the right position */
-		if (batadv_seq_before(new->seqno, un->seqno))
+	list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) {
+		/* look for the right position - an un which is smaller */
+		if (batadv_seq_before(seqno, un->seqno))
 			continue;
 
-		/* as soon as an entry having a bigger seqno is found, the new
-		 * one is attached _after_ it. In this way the list is kept in
-		 * ascending order
+		/* smaller/equal seqno was found but they might be directly
+		 * after another or overlapping. keep only a single entry
+		 *
+		 * It is already known that:
+		 *
+		 *	un->seqno <= seqno
+		 *
+		 * When establishing that:
+		 *
+		 *	seqno <= un->seqno + un->len
+		 *
+		 * Then it is not necessary to add a new entry because the
+		 * smaller/equal seqno of un might already contain the new
+		 * received packet or we only add new data directly after
+		 * the end of un. The latter can be identified using:
+		 *
+		 *	un->seqno + un->len <= end_seqno
 		 */
-		list_add(&new->list, &un->list);
-		added = true;
-		tp_vars->common.unacked_count++;
+		if (!batadv_seq_before(un->seqno + un->len, seqno)) {
+			/* new data directly after un? */
+			if (!batadv_seq_before(end_seqno, un->seqno + un->len))
+				un->len = end_seqno - un->seqno;
+
+			/* un now represents both old un + new range and has to
+			 * be used to check if the gap to the next seqno range
+			 * was closed
+			 */
+			new = un;
+		} else {
+			/* as soon as an entry having a smaller seqno is found,
+			 * the new one is attached _after_ it. In this way the
+			 * list is kept in ascending order
+			 */
+			pos = &un->list;
+		}
+
 		break;
 	}
 
-	/* received packet with smallest seqno out of order; add it to front */
-	if (!added) {
-		list_add(&new->list, &tp_vars->common.unacked_list);
-		tp_vars->common.unacked_count++;
+	/* no entry to merge with was found; insert a new one after the entry
+	 * with the next smaller seqno (or at the front of the list when the
+	 * new seqno is the smallest or the list is empty)
+	 */
+	if (!new) {
+		new = kmalloc_obj(*new, GFP_ATOMIC);
+		if (unlikely(!new))
+			return false;
+
+		new->seqno = seqno;
+		new->len = payload_len;
+
+		list_add(&new->list, pos);
+		tp_vars->unacked_count++;
+	}
+
+	/* check if new filled the gap to the next list entries */
+	un = new;
+	list_for_each_entry_safe_continue(un, safe, &tp_vars->unacked_list, list) {
+		if (batadv_seq_before(end_seqno, un->seqno))
+			break;
+
+		/* next entry is overlapping or adjacent - combine both */
+		if (batadv_seq_before(end_seqno, un->seqno + un->len)) {
+			end_seqno = un->seqno + un->len;
+			new->len = end_seqno - new->seqno;
+		}
+
+		list_del(&un->list);
+		kfree(un);
+		tp_vars->unacked_count--;
 	}
 
 	/* remove the last (biggest) unacked seqno when list is too large */
-	if (tp_vars->common.unacked_count > BATADV_TP_MAX_UNACKED) {
-		un = list_last_entry(&tp_vars->common.unacked_list,
+	if (tp_vars->unacked_count > BATADV_TP_MAX_UNACKED) {
+		un = list_last_entry(&tp_vars->unacked_list,
 				     struct batadv_tp_unacked, list);
 		list_del(&un->list);
 		kfree(un);
-		tp_vars->common.unacked_count--;
+		tp_vars->unacked_count--;
 	}
 
 	return true;
@@ -1477,7 +1526,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
  * @tp_vars: the private data of the current TP meter session
  */
 static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
-	__must_hold(&tp_vars->common.unacked_lock)
+	__must_hold(&tp_vars->ack_seqno_lock)
 {
 	struct batadv_tp_unacked *un, *safe;
 	u32 to_ack;
@@ -1485,7 +1534,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
 	/* go through the unacked packet list and possibly ACK them as
 	 * well
 	 */
-	list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
+	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
 		/* the list is ordered, therefore it is possible to stop as soon
 		 * there is a gap between the last acked seqno and the seqno of
 		 * the packet under inspection
@@ -1493,14 +1542,14 @@ static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
 		if (batadv_seq_before(tp_vars->last_recv, un->seqno))
 			break;
 
-		to_ack = un->seqno + un->len - tp_vars->last_recv;
+		to_ack = un->seqno + un->len;
 
-		if (batadv_seq_before(tp_vars->last_recv, un->seqno + un->len))
-			tp_vars->last_recv += to_ack;
+		if (batadv_seq_before(tp_vars->last_recv, to_ack))
+			tp_vars->last_recv = to_ack;
 
 		list_del(&un->list);
 		kfree(un);
-		tp_vars->common.unacked_count--;
+		tp_vars->unacked_count--;
 	}
 }
 
@@ -1547,9 +1596,9 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
 	tp_vars->common.bat_priv = bat_priv;
 	kref_init(&tp_vars->common.refcount);
 
-	spin_lock_init(&tp_vars->common.unacked_lock);
-	INIT_LIST_HEAD(&tp_vars->common.unacked_list);
-	tp_vars->common.unacked_count = 0;
+	spin_lock_init(&tp_vars->ack_seqno_lock);
+	INIT_LIST_HEAD(&tp_vars->unacked_list);
+	tp_vars->unacked_count = 0;
 
 	kref_get(&tp_vars->common.refcount);
 	timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
@@ -1609,7 +1658,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
 		WRITE_ONCE(tp_vars->last_recv_time, jiffies);
 	}
 
-	spin_lock_bh(&tp_vars->common.unacked_lock);
+	spin_lock_bh(&tp_vars->ack_seqno_lock);
 
 	/* if the packet is a duplicate, it may be the case that an ACK has been
 	 * lost. Resend the ACK
@@ -1625,7 +1674,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
 		 * not been enqueued correctly
 		 */
 		if (!batadv_tp_handle_out_of_order(tp_vars, seqno, payload_len)) {
-			spin_unlock_bh(&tp_vars->common.unacked_lock);
+			spin_unlock_bh(&tp_vars->ack_seqno_lock);
 			goto out;
 		}
 
@@ -1641,7 +1690,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
 
 send_ack:
 	to_ack = tp_vars->last_recv;
-	spin_unlock_bh(&tp_vars->common.unacked_lock);
+	spin_unlock_bh(&tp_vars->ack_seqno_lock);
 
 	/* send the ACK. If the received packet was out of order, the ACK that
 	 * is going to be sent is a duplicate (the sender will count them and
diff --git a/net/batman-adv/tvlv.c b/net/batman-adv/tvlv.c
index 1c9fb21985f6a5..49bf2ed9ecdc39 100644
--- a/net/batman-adv/tvlv.c
+++ b/net/batman-adv/tvlv.c
@@ -442,6 +442,54 @@ static int batadv_tvlv_call_handler(struct batadv_priv *bat_priv,
 	return NET_RX_SUCCESS;
 }
 
+/**
+ * batadv_tvlv_hdr_next() - move a tvlv buffer cursor to the next container
+ * @tvlv_value: cursor into the tvlv buffer, advanced past the returned
+ *  container's content on success
+ * @tvlv_value_len: remaining length of the tvlv buffer, reduced by the returned
+ *  container's size on success
+ *
+ * Parses a single container header at the current cursor position and, if a
+ * complete container is available, advances the cursor and remaining length
+ * past it. The returned header stays valid; its content is located at
+ * (returned header + 1) and is ntohs(hdr->len) bytes long.
+ *
+ * Return: pointer to the next tvlv container header, or NULL if no further
+ * complete container is present in the buffer.
+ */
+static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)
+{
+	struct batadv_tvlv_hdr *tvlv_hdr;
+	u16 tvlv_value_cont_len;
+	void *tvlv_value_cont;
+	u16 tvlv_len;
+
+	tvlv_value_cont = *tvlv_value;
+	tvlv_len = *tvlv_value_len;
+
+	if (tvlv_len < sizeof(*tvlv_hdr))
+		return NULL;
+
+	tvlv_hdr = tvlv_value_cont;
+	tvlv_value_cont_len = ntohs(tvlv_hdr->len);
+	tvlv_value_cont = tvlv_hdr + 1;
+	tvlv_len -= sizeof(*tvlv_hdr);
+
+	if (tvlv_value_cont_len > tvlv_len)
+		return NULL;
+
+	/* the next tvlv header is accessed assuming (at least) 2-byte
+	 * alignment, so it must start at an even offset.
+	 */
+	if (tvlv_value_cont_len & 1)
+		return NULL;
+
+	*tvlv_value = (u8 *)tvlv_value_cont + tvlv_value_cont_len;
+	*tvlv_value_len = tvlv_len - tvlv_value_cont_len;
+
+	return tvlv_hdr;
+}
+
 /**
  * batadv_tvlv_containers_contain() - check if a tvlv buffer holds a container
  * @tvlv_value: tvlv content
@@ -457,28 +505,10 @@ static bool batadv_tvlv_containers_contain(void *tvlv_value,
 					   u8 version)
 {
 	struct batadv_tvlv_hdr *tvlv_hdr;
-	u16 tvlv_value_cont_len;
-
-	while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
-		tvlv_hdr = tvlv_value;
-		tvlv_value_cont_len = ntohs(tvlv_hdr->len);
-		tvlv_value = tvlv_hdr + 1;
-		tvlv_value_len -= sizeof(*tvlv_hdr);
-
-		if (tvlv_value_cont_len > tvlv_value_len)
-			break;
-
-		/* the next tvlv header is accessed assuming (at least) 2-byte
-		 * alignment, so it must start at an even offset.
-		 */
-		if (tvlv_value_cont_len & 1)
-			break;
 
+	while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
 		if (tvlv_hdr->type == type && tvlv_hdr->version == version)
 			return true;
-
-		tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
-		tvlv_value_len -= tvlv_value_cont_len;
 	}
 
 	return false;
@@ -511,20 +541,8 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
 	u8 cifnotfound = BATADV_TVLV_HANDLER_OGM_CIFNOTFND;
 	int ret = NET_RX_SUCCESS;
 
-	while (tvlv_value_len >= sizeof(*tvlv_hdr)) {
-		tvlv_hdr = tvlv_value;
+	while ((tvlv_hdr = batadv_tvlv_hdr_next(&tvlv_value, &tvlv_value_len))) {
 		tvlv_value_cont_len = ntohs(tvlv_hdr->len);
-		tvlv_value = tvlv_hdr + 1;
-		tvlv_value_len -= sizeof(*tvlv_hdr);
-
-		if (tvlv_value_cont_len > tvlv_value_len)
-			break;
-
-		/* the next tvlv header is accessed assuming (at least) 2-byte
-		 * alignment, so it must start at an even offset.
-		 */
-		if (tvlv_value_cont_len & 1)
-			break;
 
 		tvlv_handler = batadv_tvlv_handler_get(bat_priv,
 						       tvlv_hdr->type,
@@ -532,11 +550,9 @@ int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,
 
 		ret |= batadv_tvlv_call_handler(bat_priv, tvlv_handler,
 						packet_type, orig_node, skb,
-						tvlv_value,
+						tvlv_hdr + 1,
 						tvlv_value_cont_len);
 		batadv_tvlv_handler_put(tvlv_handler);
-		tvlv_value = (u8 *)tvlv_value + tvlv_value_cont_len;
-		tvlv_value_len -= tvlv_value_cont_len;
 	}
 
 	if (packet_type != BATADV_IV_OGM &&
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index b1f9f8964c3fd2..cd12755d21f356 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -214,9 +214,6 @@ struct batadv_wifi_net_device_state {
  * struct batadv_hard_iface - network device known to batman-adv
  */
 struct batadv_hard_iface {
-	/** @list: list node for batadv_hardif_list */
-	struct list_head list;
-
 	/** @if_status: status of the interface for batman-adv */
 	char if_status;
 
@@ -1335,9 +1332,9 @@ struct batadv_tp_unacked {
 	u32 seqno;
 
 	/** @len: length of the packet */
-	u16 len;
+	u32 len;
 
-	/** @list: list node for &batadv_tp_vars_common.unacked_list */
+	/** @list: list node for &batadv_tp_receiver.unacked_list */
 	struct list_head list;
 };
 
@@ -1360,15 +1357,6 @@ struct batadv_tp_vars_common {
 	/** @session: TP session identifier */
 	u8 session[2];
 
-	/** @unacked_list: list of unacked packets (meta-info only) */
-	struct list_head unacked_list;
-
-	/** @unacked_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
-	spinlock_t unacked_lock;
-
-	/** @unacked_count: number of unacked entries */
-	size_t unacked_count;
-
 	/** @refcount: number of context where the object is used */
 	struct kref refcount;
 
@@ -1482,6 +1470,15 @@ struct batadv_tp_receiver {
 
 	/** @last_recv_time: time (jiffies) a msg was received */
 	unsigned long last_recv_time;
+
+	/** @unacked_list: list of unacked packets (meta-info only) */
+	struct list_head unacked_list;
+
+	/** @ack_seqno_lock: protect unacked_list + &batadv_tp_receiver.last_recv */
+	spinlock_t ack_seqno_lock;
+
+	/** @unacked_count: number of unacked entries */
+	size_t unacked_count;
 };
 
 /**
@@ -1679,6 +1676,9 @@ struct batadv_priv {
 	/** @tp_num: number of currently active tp sessions */
 	atomic_t tp_num;
 
+	/** @hardif_generation: generation counter added to netlink hardif dumps */
+	unsigned int hardif_generation;
+
 	/** @orig_work: work queue callback item for orig node purging */
 	struct delayed_work orig_work;
 

3/2 2026/06/30 21:20 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19185
							output: 34
							thoughts: 1017
						

Thoughts:
**Analyzing Kernel Patch for batman-adv**

Okay, so I've got this patch series targeting the `net/batman-adv/` directory in the kernel.  Right off the bat, it's pretty extensive, touching a significant portion of the code – a clear indication of some pretty substantial architectural changes.  We're looking at modifications across a wide range of files: `bat_iv_ogm.c`, `bat_v_elp.c`, `bat_v_ogm.c`, `bridge_loop_avoidance.c`, `hard-interface.c`, `main.c`, `mesh-interface.c`, `netlink.c`, `originator.c`, `tp_meter.c`, `tvlv.c`, and `types.h`.  Time to dig in and understand the implications.

First pass seems to be cleanup:

*   In the `bat_iv_ogm.c` and `bat_v_elp.c` files, looks like there's some dead code being removed, particularly checks related to interface states and outgoing packet conditions. Looks like `BATADV_IF_NOT_IN_USE` state is now gone.

*   In `bat_v_ogm.c` and `bridge_loop_avoidance.c`, it seems we're streamlining argument passing, specifically removing `bat_priv` and relying on the `hard_iface->mesh_iface` relationship for context. This suggests a shift in how the core data structures are being organized and accessed.

*   `hard-interface.c` is where things start to get interesting.  This is where the interface handling is undergoing a significant refactor.  We see changes to `batadv_hardif_release`, `batadv_hardif_get_by_netdev`, and a significant signature change for `batadv_hardif_enable_interface`.  It's clear the way hardware interfaces are managed in the batman-adv mesh is being reworked. The removal of the add/remove interface functions (with events being handled a different way) is a red flag - it means the lifecycle of the interfaces have been rewritten.

*   The modifications to `tp_meter.c` are notable.  Splitting `batadv_tp_vars_common_release`, moving the `unacked_list`, and the `batadv_tp_handle_out_of_order` rewrite are indicative of changes to how B.A.T.M.A.N. Advanced handles throughput and sequence handling.  This is a critical area, so I need to pay close attention to this one.

*   Finally, the addition of `batadv_tvlv_hdr_next` in `tvlv.c` suggests a more robust and safer method for iterating through Type-Length-Value (TLV) containers.

Overall, I see a clear direction here:

1.  **Refactoring and cleanup:** Removal of unnecessary checks, consolidation of data access.
2.  **Interface Management Rework:**  A significant shift in how hardware interfaces are handled, likely for better integration or more flexible configuration.
3.  **Throughput & Sequence Handling:** Improvements to throughput measurement and out-of-order packet processing, which impacts reliability and efficiency.

This is a functionally impactful change, clearly.  These modifications run deep into the core logic, which means it should absolutely be thoroughly fuzzed.  We need to make sure we've covered the interactions in this architecture.

Now, let's nail down the relevant configuration options.  `CONFIG_BATMAN_ADV` is the primary one, obviously.  `tp_meter` is likely enabled through a few options: I suspect `CONFIG_BATMAN_ADV_DAT`, `CONFIG_BATMAN_ADV_NC`, or perhaps just enabled by default with `CONFIG_BATMAN_ADV`.  Time to check the `net/batman-adv/Kconfig` file to be certain.



4/2 2026/06/30 21:21 tool grepper
0m Args:
{
  "Expression": "config BATMAN_ADV",
  "PathPrefix": "net/batman-adv/Kconfig"
}

Results:
{
  "Output": "net/batman-adv/Kconfig-9-\nnet/batman-adv/Kconfig:10:config BATMAN_ADV\nnet/batman-adv/Kconfig-11-\ttristate \"B.A.T.M.A.N. Advanced Meshing Protocol\"\n--\nnet/batman-adv/Kconfig-19-\nnet/batman-adv/Kconfig:20:config BATMAN_ADV_BATMAN_V\nnet/batman-adv/Kconfig-21-\tbool \"B.A.T.M.A.N. V protocol\"\n--\nnet/batman-adv/Kconfig-33-\nnet/batman-adv/Kconfig:34:config BATMAN_ADV_BLA\nnet/batman-adv/Kconfig-35-\tbool \"Bridge Loop Avoidance\"\n--\nnet/batman-adv/Kconfig-46-\nnet/batman-adv/Kconfig:47:config BATMAN_ADV_DAT\nnet/batman-adv/Kconfig-48-\tbool \"Distributed ARP Table\"\n--\nnet/batman-adv/Kconfig-56-\nnet/batman-adv/Kconfig:57:config BATMAN_ADV_MCAST\nnet/batman-adv/Kconfig-58-\tbool \"Multicast optimisation\"\n--\nnet/batman-adv/Kconfig-65-\nnet/batman-adv/Kconfig:66:config BATMAN_ADV_DEBUG\nnet/batman-adv/Kconfig-67-\tbool \"B.A.T.M.A.N. debugging\"\n--\nnet/batman-adv/Kconfig-74-\nnet/batman-adv/Kconfig:75:config BATMAN_ADV_TRACING\nnet/batman-adv/Kconfig-76-\tbool \"B.A.T.M.A.N. tracing support\"\n"
}

5/2 2026/06/30 21:21 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 21550
							output: 31
							thoughts: 91
						

Thoughts:
Here's my analysis of the patch's impact:

**Patch Impact Assessment**

Okay, so this patch is clearly going to affect our BATMAN code, no question. I see it touching `bat_v_elp.c` and `bat_v_ogm.c`. Those are definitely part of the BATMAN_V implementation, so we need to carefully review how these changes might impact the existing V protocol logic and performance.

Then, there's `bridge_loop_avoidance.c`, which, as I recall, is the heart of BATMAN_ADV's Bridge Loop Avoidance (BLA) functionality. This immediately flags a potential impact on our loop detection and prevention mechanisms. We'll need to scrutinize these changes thoroughly to ensure they don't introduce any regressions or unexpected behavior there. I'm especially interested in how this interacts with the various BLA configuration options.

Finally, we have `tp_meter.c`. Now, that one is less immediately obvious. I need to refresh my memory on where that module lives. Let me quickly check the `net/batman-adv/Makefile` to confirm if it's core to batman-adv or a module that can be configured. I suspect it's core, but best to be sure. That's probably related to throughput metering so we need to understand how the changes affect any related performance metrics.



6/2 2026/06/30 21:21 tool grepper
0m Args:
{
  "Expression": "tp_meter",
  "PathPrefix": "net/batman-adv/Makefile"
}

Results:
{
  "Output": "net/batman-adv/Makefile=29=batman-adv-$(CONFIG_BATMAN_ADV_TRACING) += trace.o\nnet/batman-adv/Makefile:30:batman-adv-y += tp_meter.o\nnet/batman-adv/Makefile-31-batman-adv-y += translation-table.o\n"
}

7/2 2026/06/30 21:21 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22059
							output: 31
							thoughts: 82
						

Thoughts:
Here's my analysis of the build process, focusing on `tp_meter.c` within the BATMAN-ADV context:

**Analyzing the BATMAN-ADV Build Process**

Okay, so I know that `tp_meter.c` is always included in the build if `BATMAN_ADV` is turned on. That's a given, nothing new there. Makes sense, it's a core component.

Now, let's make sure that `BATMAN_ADV_BATMAN_V` and `BATMAN_ADV_BLA` are also enabled alongside it. That configuration is crucial; it drives the functionality. Gotta verify those are set appropriately.

To confirm how the object files are managed, I need to dive into `net/batman-adv/Makefile`. Specifically, I'll search for `bat_v_elp.o` and `bat_v_ogm.o` within that Makefile. The build process details will illuminate the dependency chain. I need to understand how these modules are linked and built relative to `tp_meter.c` when the various BATMAN-ADV features are active.



8/2 2026/06/30 21:21 tool grepper
0m Args:
{
  "Expression": "bat_v",
  "PathPrefix": "net/batman-adv/Makefile"
}

Results:
{
  "Output": "net/batman-adv/Makefile=8=batman-adv-y += bat_iv_ogm.o\nnet/batman-adv/Makefile:9:batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v.o\nnet/batman-adv/Makefile:10:batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_elp.o\nnet/batman-adv/Makefile:11:batman-adv-$(CONFIG_BATMAN_ADV_BATMAN_V) += bat_v_ogm.o\nnet/batman-adv/Makefile-12-batman-adv-y += bitarray.o\n"
}

9/2 2026/06/30 21:21 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22590
							output: 34
							thoughts: 30
						

Thoughts:
Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person:

**Assessment of Requirements**

Alright, the initial affirmation of `BATMAN_ADV_BATMAN_V` is spot on. That's a fundamental necessity. Now, my mind immediately jumps to a potential related area that warrants closer scrutiny. What about `bridge_loop_avoidance.c`? Considering the need for Batman-adv functionality, I have to ensure the bridge loop avoidance logic is robust and correctly interacts with the new version. The interaction of the bridge loop avoidance code with this version is something that warrants thorough inspection.



10/2 2026/06/30 21:21 tool grepper
0m Args:
{
  "Expression": "bridge_loop_avoidance",
  "PathPrefix": "net/batman-adv/Makefile"
}

Results:
{
  "Output": "net/batman-adv/Makefile=12=batman-adv-y += bitarray.o\nnet/batman-adv/Makefile:13:batman-adv-$(CONFIG_BATMAN_ADV_BLA) += bridge_loop_avoidance.o\nnet/batman-adv/Makefile-14-batman-adv-$(CONFIG_BATMAN_ADV_DAT) += distributed-arp-table.o\n"
}

11/2 2026/06/30 21:21 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 22898
							output: 94
							thoughts: 770
						

Thoughts:
**Analyzing the Target Functions for Fuzzing**

Alright, the objective is clear: we're targeting `BATMAN_ADV_BLA` and need to identify the key functions to fuzz. The initial step is always to pinpoint the core modules that are touched by the patch. I've quickly scanned the modified functions and identified a substantial list.

Now comes the crucial part – distilling the candidates for fuzzing. I've narrowed down the list based on potential for external interaction, specifically looking for entry points that can be triggered from userspace, either directly or indirectly. After a quick assessment, the initial frontrunners are: `batadv_hardif_enable_interface`, `batadv_hardif_disable_interface`, `batadv_hard_if_event`, `batadv_meshif_slave_add`, `batadv_tp_handle_out_of_order`, `batadv_tp_recv_msg`, `batadv_tvlv_containers_process`, and `batadv_tvlv_containers_contain`.

I've taken a deeper dive on a few of these, to confirm reachability.  `batadv_tp_recv_msg` is reachable via incoming TP meter messages; `batadv_tvlv_containers_process` via incoming TVLV containers; `batadv_hardif_enable_interface` via adding a slave to a mesh interface, which can happen via netlink or sysfs; and finally, `batadv_meshif_slave_add` is the netdev_ops `ndo_add_slave` function for the mesh interface.

Ultimately, I'm confident about proceeding with these six functions for our focused fuzzing effort: `batadv_hardif_enable_interface`, `batadv_hardif_disable_interface`, `batadv_tp_handle_out_of_order`, `batadv_tp_recv_msg`, `batadv_tvlv_containers_process` and `batadv_tvlv_containers_contain`.

Next, I'll perform a quick sanity check to ensure these functions aren't static inline, ensuring we can properly instrument and target them with the fuzzer.



12/2 2026/06/30 21:21 tool grepper
0m Args:
{
  "Expression": "^(static )?(void|int|bool|struct batadv_tvlv_hdr \\*) batadv_(hardif_enable_interface|hardif_disable_interface|tp_handle_out_of_order|tp_recv_msg|tvlv_containers_process|tvlv_containers_contain)",
  "PathPrefix": "net/batman-adv/"
}

Results:
{
  "Output": "net/batman-adv/hard-interface.c=703=batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)\n--\nnet/batman-adv/hard-interface.c-723- */\nnet/batman-adv/hard-interface.c:724:int batadv_hardif_enable_interface(struct net_device *net_dev,\nnet/batman-adv/hard-interface.c-725-\t\t\t\t   struct net_device *mesh_iface)\n--\nnet/batman-adv/hard-interface.c-831- */\nnet/batman-adv/hard-interface.c:832:void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)\nnet/batman-adv/hard-interface.c-833-{\n--\nnet/batman-adv/hard-interface.h=71=batadv_hardif_get_by_netdev(struct net_device *net_dev);\nnet/batman-adv/hard-interface.h:72:int batadv_hardif_enable_interface(struct net_device *net_dev,\nnet/batman-adv/hard-interface.h-73-\t\t\t\t   struct net_device *mesh_iface);\nnet/batman-adv/hard-interface.h:74:void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);\nnet/batman-adv/hard-interface.h-75-int batadv_hardif_min_mtu(struct net_device *mesh_iface);\n--\nnet/batman-adv/tp_meter.c=1346=static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,\n--\nnet/batman-adv/tp_meter.c-1415- */\nnet/batman-adv/tp_meter.c:1416:static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,\nnet/batman-adv/tp_meter.c-1417-\t\t\t\t\t  u32 seqno, u32 payload_len)\n--\nnet/batman-adv/tp_meter.c=1564=batadv_tp_init_recv(struct batadv_priv *bat_priv,\n--\nnet/batman-adv/tp_meter.c-1625- */\nnet/batman-adv/tp_meter.c:1626:static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,\nnet/batman-adv/tp_meter.c-1627-\t\t\t       const struct sk_buff *skb)\n--\nnet/batman-adv/tvlv.c=460=static struct batadv_tvlv_hdr *batadv_tvlv_hdr_next(void **tvlv_value, u16 *tvlv_value_len)\n--\nnet/batman-adv/tvlv.c-502- */\nnet/batman-adv/tvlv.c:503:static bool batadv_tvlv_containers_contain(void *tvlv_value,\nnet/batman-adv/tvlv.c-504-\t\t\t\t\t   u16 tvlv_value_len, u8 type,\n--\nnet/batman-adv/tvlv.c-529- */\nnet/batman-adv/tvlv.c:530:int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,\nnet/batman-adv/tvlv.c-531-\t\t\t\t   u8 packet_type,\n--\nnet/batman-adv/tvlv.h=40=void batadv_tvlv_handler_unregister(struct batadv_priv *bat_priv,\nnet/batman-adv/tvlv.h-41-\t\t\t\t    u8 type, u8 version);\nnet/batman-adv/tvlv.h:42:int batadv_tvlv_containers_process(struct batadv_priv *bat_priv,\nnet/batman-adv/tvlv.h-43-\t\t\t\t   u8 packet_type,\n"
}

13/2 2026/06/30 21:21 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 25275
							output: 217
							thoughts: 698
						

Thoughts:
**My Analysis of the B.A.T.M.A.N. Advanced Patch for Fuzzing**

Okay, let's break this down for fuzzing purposes. First, I need to identify the key functions that are modified. Based on the changes, the relevant functions seem to be: `batadv_hardif_enable_interface`, `batadv_hardif_disable_interface`, `batadv_tp_handle_out_of_order`, `batadv_tp_recv_msg`, `batadv_tvlv_containers_process`, and `batadv_tvlv_containers_contain`. I'll confirm that those static functions are safe to include, and then I'll mark those as my `FocusSymbols`.

Next, I need to pinpoint the relevant kernel configuration options. The files touched by the patch are `bat_v_elp.c`, `bat_v_ogm.c`, and `bridge_loop_avoidance.c`. From this, I can derive `BATMAN_ADV`, `BATMAN_ADV_BATMAN_V`, and `BATMAN_ADV_BLA` as the necessary configs. It's a good practice to double-check their exact names, which I did against the `Kconfig` file. I'll make sure those are included in my `EnableConfigs`.

Now, does this patch warrant fuzzing? Absolutely. The patch deals with critical aspects of B.A.T.M.A.N. Advanced: interface handling, TP meter packet processing, and TVLV container parsing. These are all core logic paths, accessible via user space through netlink, sysfs, and network packets, making them high-value targets for fuzzing. I can definitively set `WorthFuzzing` to true.

Therefore, here's my summarized setup:

*   `WorthFuzzing`: `true`
*   `FocusSymbols`:
    *   `batadv_hardif_enable_interface`
    *   `batadv_hardif_disable_interface`
    *   `batadv_tp_handle_out_of_order`
    *   `batadv_tp_recv_msg`
    *   `batadv_tvlv_containers_process`
    *   `batadv_tvlv_containers_contain`
*   `EnableConfigs`:
    *   `BATMAN_ADV`
    *   `BATMAN_ADV_BATMAN_V`
    *   `BATMAN_ADV_BLA`

I'm confident in this setup. Let's call `set-results` and move forward.



14/2 2026/06/30 21:21 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BATMAN_ADV",
    "BATMAN_ADV_BATMAN_V",
    "BATMAN_ADV_BLA"
  ],
  "FocusSymbols": [
    "batadv_hardif_enable_interface",
    "batadv_hardif_disable_interface",
    "batadv_tp_handle_out_of_order",
    "batadv_tp_recv_msg",
    "batadv_tvlv_containers_process",
    "batadv_tvlv_containers_contain"
  ],
  "Reasoning": "The patch makes functional changes to the B.A.T.M.A.N. Advanced routing protocol. It modifies the handling of hard interfaces (adding/removing them from the mesh), the processing of out-of-order TP meter packets, and the parsing of TVLV containers. These are core logic changes reachable from user space via netlink/sysfs and network packets, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BATMAN_ADV",
    "BATMAN_ADV_BATMAN_V",
    "BATMAN_ADV_BLA"
  ],
  "FocusSymbols": [
    "batadv_hardif_enable_interface",
    "batadv_hardif_disable_interface",
    "batadv_tp_handle_out_of_order",
    "batadv_tp_recv_msg",
    "batadv_tvlv_containers_process",
    "batadv_tvlv_containers_contain"
  ],
  "Reasoning": "The patch makes functional changes to the B.A.T.M.A.N. Advanced routing protocol. It modifies the handling of hard interfaces (adding/removing them from the mesh), the processing of out-of-order TP meter packets, and the parsing of TVLV containers. These are core logic changes reachable from user space via netlink/sysfs and network packets, making them suitable for fuzzing.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)