Project

General

Profile

Actions

Bug #445

open

syzkaller: rcu detected stall in batadv_mcast_mla_update

Added by Sven Eckelmann 3 months ago.

Status:
New
Priority:
Normal
Target version:
-
Start date:
06/20/2026
Due date:
% Done:

0%

Estimated time:

Description

See https://syzkaller.appspot.com/bug?extid=0a80c6499b110dbf88b7

Syzkaller detects a RCU side which takes too long:

rcu: INFO: rcu_preempt detected stalls on CPUs/tasks:
rcu:     Tasks blocked on level-0 rcu_node (CPUs 0-1): P4912/1:b..l
rcu:     (detected by 0, t=10503 jiffies, g=9721, q=383 ncpus=2)
task:kworker/u8:16   state:R  running task     stack:25480 pid:4912  tgid:4912  ppid:2      task_flags:0x4208060 flags:0x00080000
Workqueue: bat_events batadv_mcast_mla_update
Call Trace:
 <TASK>
 context_switch kernel/sched/core.c:5256 [inline]
 __schedule+0x1139/0x6150 kernel/sched/core.c:6863
 preempt_schedule_irq+0x51/0x90 kernel/sched/core.c:7190
 irqentry_exit+0x1d8/0x8c0 kernel/entry/common.c:216
 asm_sysvec_apic_timer_interrupt+0x1a/0x20 arch/x86/include/asm/idtentry.h:697
RIP: 0010:__update_cpu_freelist_fast mm/slub.c:4385 [inline]
RIP: 0010:__slab_alloc_node mm/slub.c:4873 [inline]
RIP: 0010:slab_alloc_node mm/slub.c:5251 [inline]
RIP: 0010:__kmalloc_cache_noprof+0x4de/0x800 mm/slub.c:5771
Code: c9 74 b9 48 85 db 75 a9 eb b2 4d 85 c9 74 ad 48 85 db 74 a8 bf ff ff ff ff 41 8b 44 24 40 49 8b 34 24 48 8d 4a 08 49 8b 1c 01 <4c> 89 c8 65 48 0f c7 0e 0f 85 51 ff ff ff 41 8b 44 24 40 0f 0d 0c
RSP: 0018:ffffc9000ebe79a0 EFLAGS: 00000246
RAX: 0000000000000010 RBX: ffff8880792c5a00 RCX: 000000000010c861
RDX: 000000000010c859 RSI: ffffffff93b4a250 RDI: 00000000ffffffff
RBP: ffffc9000ebe7a00 R08: 0000000000000018 R09: ffff8880792c5840
R10: 0000000000000000 R11: ffff888036e5aff0 R12: ffff88813ff26780
R13: 0000000000000820 R14: 00000000aaff3300 R15: 0000000000000018
 kmalloc_noprof include/linux/slab.h:957 [inline]
 batadv_mcast_mla_meshif_get_ipv6 net/batman-adv/multicast.c:475 [inline]
 batadv_mcast_mla_meshif_get net/batman-adv/multicast.c:533 [inline]
 __batadv_mcast_mla_update net/batman-adv/multicast.c:909 [inline]
 batadv_mcast_mla_update+0x1937/0x31b0 net/batman-adv/multicast.c:946
 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257
 process_scheduled_works kernel/workqueue.c:3340 [inline]
 worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421
 kthread+0x3c5/0x780 kernel/kthread.c:463
 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246
 </TASK>
rcu: rcu_preempt kthread starved for 9521 jiffies! g9721 f0x0 RCU_GP_WAIT_FQS(5) ->state=0x0 ->cpu=1
rcu:     Unless rcu_preempt kthread gets sufficient CPU time, OOM is now expected behavior.
rcu: RCU grace-period kthread stack dump:
task:rcu_preempt     state:R  running task     stack:28440 pid:16    tgid:16    ppid:2      task_flags:0x208040 flags:0x00080000
Call Trace:
 <TASK>
 context_switch kernel/sched/core.c:5256 [inline]
 __schedule+0x1139/0x6150 kernel/sched/core.c:6863
 preempt_schedule_common+0x44/0xc0 kernel/sched/core.c:7047
 preempt_schedule_thunk+0x16/0x30 arch/x86/entry/thunk.S:12
 __raw_spin_unlock_irqrestore include/linux/spinlock_api_smp.h:152 [inline]
 _raw_spin_unlock_irqrestore+0x61/0x80 kernel/locking/spinlock.c:194
 rcu_gp_fqs_loop+0x216/0xaf0 kernel/rcu/tree.c:2083
 rcu_gp_kthread+0x26d/0x380 kernel/rcu/tree.c:2285
 kthread+0x3c5/0x780 kernel/kthread.c:463
 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246
 </TASK>
rcu: Stack dump where RCU GP kthread last ran:
Sending NMI from CPU 0 to CPUs 1:
NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 5905 Comm: kworker/u9:3 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025
Workqueue: hci4 hci_conn_timeout
RIP: 0010:restore_regs_and_return_to_kernel+0x10/0x2e
Code: ef 3f 48 81 cf 00 08 00 00 48 81 cf 00 10 00 00 0f 22 df 58 5f e9 6e ff ff ff f6 84 24 88 00 00 00 03 74 02 0f 0b 41 5f 41 5e <41> 5d 41 5c 5d 5b 41 5b 41 5a 41 59 41 58 58 59 5a 5e 5f 48 83 c4
RSP: 0018:ffffc9000469f4c8 EFLAGS: 00000046
RAX: 00000000000f1699 RBX: 0000000000000000 RCX: 0000000000000007
RDX: 0000000000000000 RSI: ffffffff8dacde18 RDI: ffffffff8bf2b380
RBP: 0000000000000000 R08: 0000000000000001 R09: 0000000000000001
R10: ffffffff9088b3d7 R11: ffff8880265f8b30 R12: 0000000000000000
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffff8881249f5000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000200000000058 CR3: 000000005b778000 CR4: 00000000003526f0
Call Trace:
 <TASK>
RIP: 0010:rcu_is_watching+0x80/0xc0 kernel/rcu/tree.c:752
Code: 89 da 48 c1 ea 03 0f b6 14 02 48 89 d8 83 e0 07 83 c0 03 38 d0 7c 04 84 d2 75 24 8b 03 c1 e8 02 83 e0 01 65 ff 0d 30 9e 0f 12 <74> 07 5b 5d e9 97 21 d8 09 e8 e2 f4 88 ff 5b 5d e9 8b 21 d8 09 48
RSP: 0018:ffffc9000469f560 EFLAGS: 00000286
RAX: 0000000000000001 RBX: ffff8880b85339e8 RCX: 0000000000000002
RDX: 0000000000000000 RSI: ffffffff8bf2b300 RDI: ffffffff8dd7bf28
RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000002 R11: 00000000000128d3 R12: 0000000000000000
R13: 0000000000000000 R14: 0000000000000000 R15: 0000000000000000
 trace_lock_acquire include/trace/events/lock.h:24 [inline]
 lock_acquire+0x2cd/0x330 kernel/locking/lockdep.c:5831
 rcu_lock_acquire include/linux/rcupdate.h:331 [inline]
 rcu_read_lock include/linux/rcupdate.h:867 [inline]
 class_rcu_constructor include/linux/rcupdate.h:1195 [inline]
 unwind_next_frame+0xd1/0x20b0 arch/x86/kernel/unwind_orc.c:495
 arch_stack_walk+0x94/0x100 arch/x86/kernel/stacktrace.c:25
 stack_trace_save+0x8e/0xc0 kernel/stacktrace.c:122
 save_stack+0x160/0x1f0 mm/page_owner.c:165
 __reset_page_owner+0x84/0x1a0 mm/page_owner.c:320
 reset_page_owner include/linux/page_owner.h:25 [inline]
 free_pages_prepare mm/page_alloc.c:1395 [inline]
 __free_frozen_pages+0x7df/0x1170 mm/page_alloc.c:2943
 discard_slab mm/slub.c:3346 [inline]
 __put_partials+0x130/0x170 mm/slub.c:3886
 qlink_free mm/kasan/quarantine.c:163 [inline]
 qlist_free_all+0x4c/0xf0 mm/kasan/quarantine.c:179
 kasan_quarantine_reduce+0x195/0x1e0 mm/kasan/quarantine.c:286
 __kasan_slab_alloc+0x69/0x90 mm/kasan/common.c:349
 kasan_slab_alloc include/linux/kasan.h:252 [inline]
 slab_post_alloc_hook mm/slub.c:4953 [inline]
 slab_alloc_node mm/slub.c:5263 [inline]
 __kmalloc_cache_noprof+0x282/0x800 mm/slub.c:5771
 kmalloc_noprof include/linux/slab.h:957 [inline]
 hci_cmd_sync_submit+0xbc/0x330 net/bluetooth/hci_sync.c:714
 hci_cmd_sync_run+0x93/0xf0 net/bluetooth/hci_sync.c:807
 hci_cmd_sync_run_once+0x1b5/0x200 net/bluetooth/hci_sync.c:823
 hci_abort_conn+0x182/0x360 net/bluetooth/hci_conn.c:3004
 hci_conn_timeout+0x1f3/0x230 net/bluetooth/hci_conn.c:579
 process_one_work+0x9ba/0x1b20 kernel/workqueue.c:3257
 process_scheduled_works kernel/workqueue.c:3340 [inline]
 worker_thread+0x6c8/0xf10 kernel/workqueue.c:3421
 kthread+0x3c5/0x780 kernel/kthread.c:463
 ret_from_fork+0x983/0xb10 arch/x86/kernel/process.c:158
 ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:246
 </TASK>
NMI backtrace for cpu 1
CPU: 1 UID: 0 PID: 0 Comm: swapper/1 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 10/25/2025
RIP: 0010:pv_native_safe_halt+0xf/0x20 arch/x86/kernel/paravirt.c:82
Code: c6 5f 02 c3 cc cc cc cc 0f 1f 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 f3 0f 1e fa 66 90 0f 00 2d 13 49 12 00 fb f4 <e9> cc 35 03 00 66 2e 0f 1f 84 00 00 00 00 00 66 90 90 90 90 90 90
RSP: 0018:ffffc90000197de8 EFLAGS: 000002c6
RAX: 00000000000725eb RBX: 0000000000000001 RCX: ffffffff8b7816d9
RDX: 0000000000000000 RSI: ffffffff8dacde18 RDI: ffffffff8bf2b380
RBP: ffffed1003b58498 R08: 0000000000000001 R09: ffffed10170a673d
R10: ffff8880b85339eb R11: ffff88801dac2ff0 R12: 0000000000000001
R13: ffff88801dac24c0 R14: ffffffff9088b3d0 R15: 0000000000000000
FS:  0000000000000000(0000) GS:ffff8881249f5000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fa8c8f2a6b0 CR3: 00000000650ec000 CR4: 00000000003526f0
Call Trace:
 <TASK>
 arch_safe_halt arch/x86/include/asm/paravirt.h:107 [inline]
 default_idle+0x13/0x20 arch/x86/kernel/process.c:767
 default_idle_call+0x6c/0xb0 kernel/sched/idle.c:122
 cpuidle_idle_call kernel/sched/idle.c:191 [inline]
 do_idle+0x38d/0x510 kernel/sched/idle.c:332
 cpu_startup_entry+0x4f/0x60 kernel/sched/idle.c:430
 start_secondary+0x21d/0x2d0 arch/x86/kernel/smpboot.c:312
 common_startup_64+0x13e/0x148
 </TASK>

The function itself looks a little bit like O(n^2).

Is it possible to move the heavy lifting outside the RCU region?

diff --git i/net/batman-adv/multicast.c w/net/batman-adv/multicast.c
index 49d9a5ce..efa9b755 100644
--- i/net/batman-adv/multicast.c
+++ w/net/batman-adv/multicast.c
@@ -33,6 +33,7 @@
 #include <linux/printk.h>
 #include <linux/rculist.h>
 #include <linux/rcupdate.h>
+#include <linux/sched.h>
 #include <linux/skbuff.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
@@ -355,6 +356,63 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
     return false;
 }

+/**
+ * batadv_mcast_mla_list_free() - free a list of multicast addresses
+ * @mcast_list: the list to free
+ *
+ * Removes and frees all items in the given mcast_list.
+ */
+static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
+{
+    struct batadv_hw_addr *mcast_entry;
+    struct hlist_node *tmp;
+
+    hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
+        hlist_del(&mcast_entry->list);
+        kfree(mcast_entry);
+    }
+}
+
+/**
+ * batadv_mcast_mla_merge() - merge collected multicast addresses into a list
+ * @collected: a list of collected, not yet deduplicated multicast addresses
+ * @mcast_list: the list to merge the collected addresses into
+ *
+ * Moves all multicast addresses from @collected into @mcast_list, skipping and
+ * freeing any address which is already present in @mcast_list. The @collected
+ * list is empty afterwards.
+ *
+ * The duplicate elimination is quadratic in the number of addresses. Running it
+ * here, outside of any RCU read-side critical section, ensures that it neither
+ * stalls RCU grace periods nor blocks rescheduling when a large number of
+ * multicast listeners has to be processed.
+ *
+ * Return: the number of addresses added to @mcast_list.
+ */
+static int batadv_mcast_mla_merge(struct hlist_head *collected,
+                  struct hlist_head *mcast_list)
+{
+    struct batadv_hw_addr *new;
+    struct hlist_node *tmp;
+    int ret = 0;
+
+    hlist_for_each_entry_safe(new, tmp, collected, list) {
+        hlist_del(&new->list);
+
+        if (batadv_mcast_mla_is_duplicate(new->addr, mcast_list)) {
+            kfree(new);
+            continue;
+        }
+
+        hlist_add_head(&new->list, mcast_list);
+        ret++;
+
+        cond_resched();
+    }
+
+    return ret;
+}
+
 /**
  * batadv_mcast_mla_meshif_get_ipv4() - get meshif IPv4 multicast listeners
  * @dev: the device to collect multicast addresses from
@@ -374,9 +432,9 @@ batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev,
                  struct hlist_head *mcast_list,
                  struct batadv_mcast_mla_flags *flags)
 {
+    struct hlist_head collected = HLIST_HEAD_INIT;
     struct batadv_hw_addr *new;
     struct in_device *in_dev;
-    u8 mcast_addr[ETH_ALEN];
     struct ip_mc_list *pmc;
     int ret = 0;

@@ -401,24 +459,23 @@ batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev,
             !ipv4_is_local_multicast(pmc->multiaddr))
             continue;

-        ip_eth_mc_map(pmc->multiaddr, mcast_addr);
-
-        if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
-            continue;
-
         new = kmalloc_obj(*new, GFP_ATOMIC);
         if (!new) {
             ret = -ENOMEM;
             break;
         }

-        ether_addr_copy(new->addr, mcast_addr);
-        hlist_add_head(&new->list, mcast_list);
-        ret++;
+        ip_eth_mc_map(pmc->multiaddr, new->addr);
+        hlist_add_head(&new->list, &collected);
     }
     rcu_read_unlock();

-    return ret;
+    if (ret < 0) {
+        batadv_mcast_mla_list_free(&collected);
+        return ret;
+    }
+
+    return batadv_mcast_mla_merge(&collected, mcast_list);
 }

 /**
@@ -441,9 +498,9 @@ batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
                  struct hlist_head *mcast_list,
                  struct batadv_mcast_mla_flags *flags)
 {
+    struct hlist_head collected = HLIST_HEAD_INIT;
     struct batadv_hw_addr *new;
     struct inet6_dev *in6_dev;
-    u8 mcast_addr[ETH_ALEN];
     struct ifmcaddr6 *pmc6;
     int ret = 0;

@@ -474,24 +531,23 @@ batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
             IPV6_ADDR_SCOPE_LINKLOCAL)
             continue;

-        ipv6_eth_mc_map(&pmc6->mca_addr, mcast_addr);
-
-        if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
-            continue;
-
         new = kmalloc_obj(*new, GFP_ATOMIC);
         if (!new) {
             ret = -ENOMEM;
             break;
         }

-        ether_addr_copy(new->addr, mcast_addr);
-        hlist_add_head(&new->list, mcast_list);
-        ret++;
+        ipv6_eth_mc_map(&pmc6->mca_addr, new->addr);
+        hlist_add_head(&new->list, &collected);
     }
     rcu_read_unlock();

-    return ret;
+    if (ret < 0) {
+        batadv_mcast_mla_list_free(&collected);
+        return ret;
+    }
+
+    return batadv_mcast_mla_merge(&collected, mcast_list);
 }
 #else
 static inline int
@@ -658,23 +714,6 @@ static int batadv_mcast_mla_bridge_get(struct net_device *dev,
     return ret;
 }

-/**
- * batadv_mcast_mla_list_free() - free a list of multicast addresses
- * @mcast_list: the list to free
- *
- * Removes and frees all items in the given mcast_list.
- */
-static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
-{
-    struct batadv_hw_addr *mcast_entry;
-    struct hlist_node *tmp;
-
-    hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
-        hlist_del(&mcast_entry->list);
-        kfree(mcast_entry);
-    }
-}
-
 /**
  * batadv_mcast_mla_tt_retract() - clean up multicast listener announcements
  * @bat_priv: the bat priv with all the mesh interface information

It would even be better when the two lists are sorted - avoiding the O(n^2) and just needing O(n log n):

diff --git i/net/batman-adv/Kconfig w/net/batman-adv/Kconfig
index 58c408b7..86e83480 100644
--- i/net/batman-adv/Kconfig
+++ w/net/batman-adv/Kconfig
@@ -57,6 +57,7 @@ config BATMAN_ADV_DAT
 config BATMAN_ADV_MCAST
     bool "Multicast optimisation" 
     depends on BATMAN_ADV && INET && !(BRIDGE=m && BATMAN_ADV=y)
+    select LIST_SORT
     default y
     help
       This option enables the multicast optimisation which aims to
diff --git i/net/batman-adv/main.c w/net/batman-adv/main.c
index 3c457228..50e58995 100644
--- i/net/batman-adv/main.c
+++ w/net/batman-adv/main.c
@@ -189,7 +189,7 @@ int batadv_mesh_init(struct net_device *mesh_iface)
     INIT_HLIST_HEAD(&bat_priv->tt.req_list);
     INIT_LIST_HEAD(&bat_priv->tt.roam_list);
 #ifdef CONFIG_BATMAN_ADV_MCAST
-    INIT_HLIST_HEAD(&bat_priv->mcast.mla_list);
+    INIT_LIST_HEAD(&bat_priv->mcast.mla_list);
 #endif
     INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
     INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
diff --git i/net/batman-adv/multicast.c w/net/batman-adv/multicast.c
index 49d9a5ce..5774ca23 100644
--- i/net/batman-adv/multicast.c
+++ w/net/batman-adv/multicast.c
@@ -27,6 +27,7 @@
 #include <linux/ipv6.h>
 #include <linux/jiffies.h>
 #include <linux/list.h>
+#include <linux/list_sort.h>
 #include <linux/lockdep.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
@@ -344,17 +345,67 @@ batadv_mcast_mla_flags_get(struct batadv_priv *bat_priv)
  * Otherwise returns false.
  */
 static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
-                      struct hlist_head *mcast_list)
+                      struct list_head *mcast_list)
 {
     struct batadv_hw_addr *mcast_entry;

-    hlist_for_each_entry(mcast_entry, mcast_list, list)
+    list_for_each_entry(mcast_entry, mcast_list, list)
         if (batadv_compare_eth(mcast_entry->addr, mcast_addr))
             return true;

     return false;
 }

+/**
+ * batadv_mcast_mla_addr_cmp() - compare two multicast addresses
+ * @priv: unused
+ * @a: list node of the first multicast address
+ * @b: list node of the second multicast address
+ *
+ * Return: a negative, zero or positive value if the address behind @a sorts
+ * before, equal to or after the address behind @b.
+ */
+static int batadv_mcast_mla_addr_cmp(void *priv, const struct list_head *a,
+                     const struct list_head *b)
+{
+    struct batadv_hw_addr *aa = list_entry(a, struct batadv_hw_addr, list);
+    struct batadv_hw_addr *bb = list_entry(b, struct batadv_hw_addr, list);
+
+    return memcmp(aa->addr, bb->addr, ETH_ALEN);
+}
+
+/**
+ * batadv_mcast_mla_dedup() - remove duplicates from a list of multicast
+ *  addresses
+ * @mcast_list: the list of collected multicast addresses to deduplicate
+ *
+ * Sorts the given mcast_list and removes and frees any duplicate address from
+ * it.
+ *
+ * Sorting first turns the duplicate elimination into a single linear pass over
+ * the list. Together with the O(n log n) sort this keeps the overall work low
+ * enough to run outside of any RCU read-side critical section without stalling
+ * RCU grace periods, even when a large number of multicast listeners has to be
+ * processed.
+ */
+static void batadv_mcast_mla_dedup(struct list_head *mcast_list)
+{
+    struct batadv_hw_addr *mcast_entry, *prev = NULL, *tmp;
+
+    list_sort(NULL, mcast_list, batadv_mcast_mla_addr_cmp);
+
+    list_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
+        if (prev &&
+            batadv_compare_eth(prev->addr, mcast_entry->addr)) {
+            list_del(&mcast_entry->list);
+            kfree(mcast_entry);
+            continue;
+        }
+
+        prev = mcast_entry;
+    }
+}
+
 /**
  * batadv_mcast_mla_meshif_get_ipv4() - get meshif IPv4 multicast listeners
  * @dev: the device to collect multicast addresses from
@@ -371,12 +422,11 @@ static bool batadv_mcast_mla_is_duplicate(u8 *mcast_addr,
  */
 static int
 batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev,
-                 struct hlist_head *mcast_list,
+                 struct list_head *mcast_list,
                  struct batadv_mcast_mla_flags *flags)
 {
     struct batadv_hw_addr *new;
     struct in_device *in_dev;
-    u8 mcast_addr[ETH_ALEN];
     struct ip_mc_list *pmc;
     int ret = 0;

@@ -401,19 +451,14 @@ batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev,
             !ipv4_is_local_multicast(pmc->multiaddr))
             continue;

-        ip_eth_mc_map(pmc->multiaddr, mcast_addr);
-
-        if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
-            continue;
-
         new = kmalloc_obj(*new, GFP_ATOMIC);
         if (!new) {
             ret = -ENOMEM;
             break;
         }

-        ether_addr_copy(new->addr, mcast_addr);
-        hlist_add_head(&new->list, mcast_list);
+        ip_eth_mc_map(pmc->multiaddr, new->addr);
+        list_add(&new->list, mcast_list);
         ret++;
     }
     rcu_read_unlock();
@@ -438,12 +483,11 @@ batadv_mcast_mla_meshif_get_ipv4(struct net_device *dev,
 #if IS_ENABLED(CONFIG_IPV6)
 static int
 batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
-                 struct hlist_head *mcast_list,
+                 struct list_head *mcast_list,
                  struct batadv_mcast_mla_flags *flags)
 {
     struct batadv_hw_addr *new;
     struct inet6_dev *in6_dev;
-    u8 mcast_addr[ETH_ALEN];
     struct ifmcaddr6 *pmc6;
     int ret = 0;

@@ -474,19 +518,14 @@ batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
             IPV6_ADDR_SCOPE_LINKLOCAL)
             continue;

-        ipv6_eth_mc_map(&pmc6->mca_addr, mcast_addr);
-
-        if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
-            continue;
-
         new = kmalloc_obj(*new, GFP_ATOMIC);
         if (!new) {
             ret = -ENOMEM;
             break;
         }

-        ether_addr_copy(new->addr, mcast_addr);
-        hlist_add_head(&new->list, mcast_list);
+        ipv6_eth_mc_map(&pmc6->mca_addr, new->addr);
+        list_add(&new->list, mcast_list);
         ret++;
     }
     rcu_read_unlock();
@@ -496,7 +535,7 @@ batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
 #else
 static inline int
 batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
-                 struct hlist_head *mcast_list,
+                 struct list_head *mcast_list,
                  struct batadv_mcast_mla_flags *flags)
 {
     return 0;
@@ -524,7 +563,7 @@ batadv_mcast_mla_meshif_get_ipv6(struct net_device *dev,
  */
 static int
 batadv_mcast_mla_meshif_get(struct net_device *dev,
-                struct hlist_head *mcast_list,
+                struct list_head *mcast_list,
                 struct batadv_mcast_mla_flags *flags)
 {
     struct net_device *bridge = batadv_mcast_get_bridge(dev);
@@ -588,14 +627,13 @@ static void batadv_mcast_mla_br_addr_cpy(char *dst, const struct br_ip *src)
  * items added to the mcast_list otherwise.
  */
 static int batadv_mcast_mla_bridge_get(struct net_device *dev,
-                       struct hlist_head *mcast_list,
+                       struct list_head *mcast_list,
                        struct batadv_mcast_mla_flags *flags)
 {
     struct list_head bridge_mcast_list = LIST_HEAD_INIT(bridge_mcast_list);
     struct br_ip_list *br_ip_entry, *tmp;
     u8 tvlv_flags = flags->tvlv_flags;
     struct batadv_hw_addr *new;
-    u8 mcast_addr[ETH_ALEN];
     int ret;

     /* we don't need to detect these devices/listeners, the IGMP/MLD
@@ -635,18 +673,14 @@ static int batadv_mcast_mla_bridge_get(struct net_device *dev,
         }
 #endif

-        batadv_mcast_mla_br_addr_cpy(mcast_addr, &br_ip_entry->addr);
-        if (batadv_mcast_mla_is_duplicate(mcast_addr, mcast_list))
-            continue;
-
         new = kmalloc_obj(*new, GFP_ATOMIC);
         if (!new) {
             ret = -ENOMEM;
             break;
         }

-        ether_addr_copy(new->addr, mcast_addr);
-        hlist_add_head(&new->list, mcast_list);
+        batadv_mcast_mla_br_addr_cpy(new->addr, &br_ip_entry->addr);
+        list_add(&new->list, mcast_list);
     }

 out:
@@ -664,13 +698,12 @@ static int batadv_mcast_mla_bridge_get(struct net_device *dev,
  *
  * Removes and frees all items in the given mcast_list.
  */
-static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
+static void batadv_mcast_mla_list_free(struct list_head *mcast_list)
 {
-    struct batadv_hw_addr *mcast_entry;
-    struct hlist_node *tmp;
+    struct batadv_hw_addr *mcast_entry, *tmp;

-    hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
-        hlist_del(&mcast_entry->list);
+    list_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
+        list_del(&mcast_entry->list);
         kfree(mcast_entry);
     }
 }
@@ -686,13 +719,12 @@ static void batadv_mcast_mla_list_free(struct hlist_head *mcast_list)
  * If mcast_list is NULL then all are retracted.
  */
 static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,
-                    struct hlist_head *mcast_list)
+                    struct list_head *mcast_list)
 {
-    struct batadv_hw_addr *mcast_entry;
-    struct hlist_node *tmp;
+    struct batadv_hw_addr *mcast_entry, *tmp;

-    hlist_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list,
-                  list) {
+    list_for_each_entry_safe(mcast_entry, tmp, &bat_priv->mcast.mla_list,
+                 list) {
         if (mcast_list &&
             batadv_mcast_mla_is_duplicate(mcast_entry->addr,
                           mcast_list))
@@ -702,7 +734,7 @@ static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,
                        BATADV_NO_FLAGS,
                        "mcast TT outdated", false);

-        hlist_del(&mcast_entry->list);
+        list_del(&mcast_entry->list);
         kfree(mcast_entry);
     }
 }
@@ -716,15 +748,14 @@ static void batadv_mcast_mla_tt_retract(struct batadv_priv *bat_priv,
  * translation table if they have not been added yet.
  */
 static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv,
-                    struct hlist_head *mcast_list)
+                    struct list_head *mcast_list)
 {
-    struct batadv_hw_addr *mcast_entry;
-    struct hlist_node *tmp;
+    struct batadv_hw_addr *mcast_entry, *tmp;

     if (!mcast_list)
         return;

-    hlist_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
+    list_for_each_entry_safe(mcast_entry, tmp, mcast_list, list) {
         if (batadv_mcast_mla_is_duplicate(mcast_entry->addr,
                           &bat_priv->mcast.mla_list))
             continue;
@@ -734,8 +765,7 @@ static void batadv_mcast_mla_tt_add(struct batadv_priv *bat_priv,
                      BATADV_NULL_IFINDEX, BATADV_NO_MARK))
             continue;

-        hlist_del(&mcast_entry->list);
-        hlist_add_head(&mcast_entry->list, &bat_priv->mcast.mla_list);
+        list_move(&mcast_entry->list, &bat_priv->mcast.mla_list);
     }
 }

@@ -907,8 +937,8 @@ batadv_mcast_mla_flags_update(struct batadv_priv *bat_priv,
 static void __batadv_mcast_mla_update(struct batadv_priv *bat_priv)
 {
     struct net_device *mesh_iface = bat_priv->mesh_iface;
-    struct hlist_head mcast_list = HLIST_HEAD_INIT;
     struct batadv_mcast_mla_flags flags;
+    LIST_HEAD(mcast_list);
     int ret;

     flags = batadv_mcast_mla_flags_get(bat_priv);
@@ -921,6 +951,8 @@ static void __batadv_mcast_mla_update(struct batadv_priv *bat_priv)
     if (ret < 0)
         goto out;

+    batadv_mcast_mla_dedup(&mcast_list);
+
     spin_lock(&bat_priv->mcast.mla_lock);
     batadv_mcast_mla_tt_retract(bat_priv, &mcast_list);
     batadv_mcast_mla_tt_add(bat_priv, &mcast_list);
diff --git i/net/batman-adv/types.h w/net/batman-adv/types.h
index b1f9f896..5c397e6b 100644
--- i/net/batman-adv/types.h
+++ w/net/batman-adv/types.h
@@ -1246,7 +1246,7 @@ struct batadv_priv_mcast {
      * @mla_list: list of multicast addresses we are currently announcing
      *  via TT
      */
-    struct hlist_head mla_list; /* see __batadv_mcast_mla_update() */
+    struct list_head mla_list; /* see __batadv_mcast_mla_update() */

     /**
      * @want_all_unsnoopables_list: a list of orig_nodes wanting all
@@ -2201,7 +2201,7 @@ struct batadv_dat_entry {
  */
 struct batadv_hw_addr {
     /** @list: list node for the linking of entries */
-    struct hlist_node list;
+    struct list_head list;

     /** @addr: the MAC address of this list entry */
     unsigned char addr[ETH_ALEN];

Of course, the big problem could also be the kmalloc - which would now be even worse without the batadv_mcast_mla_is_duplicate precheck.

(all of these diffs are just symbolic and might not even work - or be accepted after an actual review process)

No data to display

Actions

Also available in: Atom PDF