Actions
Bug #445
opensyzkaller: rcu detected stall in batadv_mcast_mla_update
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