From: Sasha Levin <sashal@kernel.org>
To: linux-kernel@vger.kernel.org, stable@vger.kernel.org
Cc: Hou Tao <houtao1@huawei.com>, Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Sasha Levin <sashal@kernel.org>,
daniel@iogearbox.net, bpf@vger.kernel.org
Subject: [PATCH AUTOSEL 6.6 51/61] bpf: Call free_htab_elem() after htab_unlock_bucket()
Date: Sun, 24 Nov 2024 08:45:26 -0500 [thread overview]
Message-ID: <20241124134637.3346391-51-sashal@kernel.org> (raw)
In-Reply-To: <20241124134637.3346391-1-sashal@kernel.org>
From: Hou Tao <houtao1@huawei.com>
[ Upstream commit b9e9ed90b10c82a4e9d4d70a2890f06bfcdd3b78 ]
For htab of maps, when the map is removed from the htab, it may hold the
last reference of the map. bpf_map_fd_put_ptr() will invoke
bpf_map_free_id() to free the id of the removed map element. However,
bpf_map_fd_put_ptr() is invoked while holding a bucket lock
(raw_spin_lock_t), and bpf_map_free_id() attempts to acquire map_idr_lock
(spinlock_t), triggering the following lockdep warning:
=============================
[ BUG: Invalid wait context ]
6.11.0-rc4+ #49 Not tainted
-----------------------------
test_maps/4881 is trying to lock:
ffffffff84884578 (map_idr_lock){+...}-{3:3}, at: bpf_map_free_id.part.0+0x21/0x70
other info that might help us debug this:
context-{5:5}
2 locks held by test_maps/4881:
#0: ffffffff846caf60 (rcu_read_lock){....}-{1:3}, at: bpf_fd_htab_map_update_elem+0xf9/0x270
#1: ffff888149ced148 (&htab->lockdep_key#2){....}-{2:2}, at: htab_map_update_elem+0x178/0xa80
stack backtrace:
CPU: 0 UID: 0 PID: 4881 Comm: test_maps Not tainted 6.11.0-rc4+ #49
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), ...
Call Trace:
<TASK>
dump_stack_lvl+0x6e/0xb0
dump_stack+0x10/0x20
__lock_acquire+0x73e/0x36c0
lock_acquire+0x182/0x450
_raw_spin_lock_irqsave+0x43/0x70
bpf_map_free_id.part.0+0x21/0x70
bpf_map_put+0xcf/0x110
bpf_map_fd_put_ptr+0x9a/0xb0
free_htab_elem+0x69/0xe0
htab_map_update_elem+0x50f/0xa80
bpf_fd_htab_map_update_elem+0x131/0x270
htab_map_update_elem+0x50f/0xa80
bpf_fd_htab_map_update_elem+0x131/0x270
bpf_map_update_value+0x266/0x380
__sys_bpf+0x21bb/0x36b0
__x64_sys_bpf+0x45/0x60
x64_sys_call+0x1b2a/0x20d0
do_syscall_64+0x5d/0x100
entry_SYSCALL_64_after_hwframe+0x76/0x7e
One way to fix the lockdep warning is using raw_spinlock_t for
map_idr_lock as well. However, bpf_map_alloc_id() invokes
idr_alloc_cyclic() after acquiring map_idr_lock, it will trigger a
similar lockdep warning because the slab's lock (s->cpu_slab->lock) is
still a spinlock.
Instead of changing map_idr_lock's type, fix the issue by invoking
htab_put_fd_value() after htab_unlock_bucket(). However, only deferring
the invocation of htab_put_fd_value() is not enough, because the old map
pointers in htab of maps can not be saved during batched deletion.
Therefore, also defer the invocation of free_htab_elem(), so these
to-be-freed elements could be linked together similar to lru map.
There are four callers for ->map_fd_put_ptr:
(1) alloc_htab_elem() (through htab_put_fd_value())
It invokes ->map_fd_put_ptr() under a raw_spinlock_t. The invocation of
htab_put_fd_value() can not simply move after htab_unlock_bucket(),
because the old element has already been stashed in htab->extra_elems.
It may be reused immediately after htab_unlock_bucket() and the
invocation of htab_put_fd_value() after htab_unlock_bucket() may release
the newly-added element incorrectly. Therefore, saving the map pointer
of the old element for htab of maps before unlocking the bucket and
releasing the map_ptr after unlock. Beside the map pointer in the old
element, should do the same thing for the special fields in the old
element as well.
(2) free_htab_elem() (through htab_put_fd_value())
Its caller includes __htab_map_lookup_and_delete_elem(),
htab_map_delete_elem() and __htab_map_lookup_and_delete_batch().
For htab_map_delete_elem(), simply invoke free_htab_elem() after
htab_unlock_bucket(). For __htab_map_lookup_and_delete_batch(), just
like lru map, linking the to-be-freed element into node_to_free list
and invoking free_htab_elem() for these element after unlock. It is safe
to reuse batch_flink as the link for node_to_free, because these
elements have been removed from the hash llist.
Because htab of maps doesn't support lookup_and_delete operation,
__htab_map_lookup_and_delete_elem() doesn't have the problem, so kept
it as is.
(3) fd_htab_map_free()
It invokes ->map_fd_put_ptr without raw_spinlock_t.
(4) bpf_fd_htab_map_update_elem()
It invokes ->map_fd_put_ptr without raw_spinlock_t.
After moving free_htab_elem() outside htab bucket lock scope, using
pcpu_freelist_push() instead of __pcpu_freelist_push() to disable
the irq before freeing elements, and protecting the invocations of
bpf_mem_cache_free() with migrate_{disable|enable} pair.
Signed-off-by: Hou Tao <houtao1@huawei.com>
Link: https://lore.kernel.org/r/20241106063542.357743-2-houtao@huaweicloud.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
---
kernel/bpf/hashtab.c | 56 ++++++++++++++++++++++++++++++--------------
1 file changed, 39 insertions(+), 17 deletions(-)
diff --git a/kernel/bpf/hashtab.c b/kernel/bpf/hashtab.c
index 7c64ad4f3732b..fc34f72702cc4 100644
--- a/kernel/bpf/hashtab.c
+++ b/kernel/bpf/hashtab.c
@@ -892,9 +892,12 @@ static int htab_map_get_next_key(struct bpf_map *map, void *key, void *next_key)
static void htab_elem_free(struct bpf_htab *htab, struct htab_elem *l)
{
check_and_free_fields(htab, l);
+
+ migrate_disable();
if (htab->map.map_type == BPF_MAP_TYPE_PERCPU_HASH)
bpf_mem_cache_free(&htab->pcpu_ma, l->ptr_to_pptr);
bpf_mem_cache_free(&htab->ma, l);
+ migrate_enable();
}
static void htab_put_fd_value(struct bpf_htab *htab, struct htab_elem *l)
@@ -944,7 +947,7 @@ static void free_htab_elem(struct bpf_htab *htab, struct htab_elem *l)
if (htab_is_prealloc(htab)) {
bpf_map_dec_elem_count(&htab->map);
check_and_free_fields(htab, l);
- __pcpu_freelist_push(&htab->freelist, &l->fnode);
+ pcpu_freelist_push(&htab->freelist, &l->fnode);
} else {
dec_elem_count(htab);
htab_elem_free(htab, l);
@@ -1014,7 +1017,6 @@ static struct htab_elem *alloc_htab_elem(struct bpf_htab *htab, void *key,
*/
pl_new = this_cpu_ptr(htab->extra_elems);
l_new = *pl_new;
- htab_put_fd_value(htab, old_elem);
*pl_new = old_elem;
} else {
struct pcpu_freelist_node *l;
@@ -1100,6 +1102,7 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value,
struct htab_elem *l_new = NULL, *l_old;
struct hlist_nulls_head *head;
unsigned long flags;
+ void *old_map_ptr;
struct bucket *b;
u32 key_size, hash;
int ret;
@@ -1178,12 +1181,27 @@ static long htab_map_update_elem(struct bpf_map *map, void *key, void *value,
hlist_nulls_add_head_rcu(&l_new->hash_node, head);
if (l_old) {
hlist_nulls_del_rcu(&l_old->hash_node);
+
+ /* l_old has already been stashed in htab->extra_elems, free
+ * its special fields before it is available for reuse. Also
+ * save the old map pointer in htab of maps before unlock
+ * and release it after unlock.
+ */
+ old_map_ptr = NULL;
+ if (htab_is_prealloc(htab)) {
+ if (map->ops->map_fd_put_ptr)
+ old_map_ptr = fd_htab_map_get_ptr(map, l_old);
+ check_and_free_fields(htab, l_old);
+ }
+ }
+ htab_unlock_bucket(htab, b, hash, flags);
+ if (l_old) {
+ if (old_map_ptr)
+ map->ops->map_fd_put_ptr(map, old_map_ptr, true);
if (!htab_is_prealloc(htab))
free_htab_elem(htab, l_old);
- else
- check_and_free_fields(htab, l_old);
}
- ret = 0;
+ return 0;
err:
htab_unlock_bucket(htab, b, hash, flags);
return ret;
@@ -1427,15 +1445,15 @@ static long htab_map_delete_elem(struct bpf_map *map, void *key)
return ret;
l = lookup_elem_raw(head, hash, key, key_size);
-
- if (l) {
+ if (l)
hlist_nulls_del_rcu(&l->hash_node);
- free_htab_elem(htab, l);
- } else {
+ else
ret = -ENOENT;
- }
htab_unlock_bucket(htab, b, hash, flags);
+
+ if (l)
+ free_htab_elem(htab, l);
return ret;
}
@@ -1842,13 +1860,14 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map,
* may cause deadlock. See comments in function
* prealloc_lru_pop(). Let us do bpf_lru_push_free()
* after releasing the bucket lock.
+ *
+ * For htab of maps, htab_put_fd_value() in
+ * free_htab_elem() may acquire a spinlock with bucket
+ * lock being held and it violates the lock rule, so
+ * invoke free_htab_elem() after unlock as well.
*/
- if (is_lru_map) {
- l->batch_flink = node_to_free;
- node_to_free = l;
- } else {
- free_htab_elem(htab, l);
- }
+ l->batch_flink = node_to_free;
+ node_to_free = l;
}
dst_key += key_size;
dst_val += value_size;
@@ -1860,7 +1879,10 @@ __htab_map_lookup_and_delete_batch(struct bpf_map *map,
while (node_to_free) {
l = node_to_free;
node_to_free = node_to_free->batch_flink;
- htab_lru_push_free(htab, l);
+ if (is_lru_map)
+ htab_lru_push_free(htab, l);
+ else
+ free_htab_elem(htab, l);
}
next_batch:
--
2.43.0
next prev parent reply other threads:[~2024-11-24 13:48 UTC|newest]
Thread overview: 61+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-11-24 13:44 [PATCH AUTOSEL 6.6 01/61] drm/vc4: hdmi: Avoid log spam for audio start failure Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 02/61] drm/vc4: hvs: Set AXI panic modes for the HVS Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 03/61] drm: panel-orientation-quirks: Add quirk for AYA NEO 2 model Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 04/61] drm: panel-orientation-quirks: Add quirk for AYA NEO Founder edition Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 05/61] drm: panel-orientation-quirks: Add quirk for AYA NEO GEEK Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 06/61] drm/bridge: it6505: Enable module autoloading Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 07/61] drm/mcde: " Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 08/61] wifi: rtw89: check return value of ieee80211_probereq_get() for RNR Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 09/61] drm/radeon/r600_cs: Fix possible int overflow in r600_packet3_check() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 10/61] dlm: fix possible lkb_resource null dereference Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 11/61] drm/display: Fix building with GCC 15 Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 12/61] ALSA: hda: Use own quirk lookup helper Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 13/61] ALSA: hda/conexant: Use the new codec SSID matching Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 14/61] r8169: don't apply UDP padding quirk on RTL8126A Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 15/61] samples/bpf: Fix a resource leak Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 16/61] net: fec_mpc52xx_phy: Use %pa to format resource_size_t Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 17/61] net: ethernet: fs_enet: " Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 18/61] net/sched: cbs: Fix integer overflow in cbs_set_port_rate() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 19/61] af_packet: avoid erroring out after sock_init_data() in packet_create() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 20/61] Bluetooth: L2CAP: do not leave dangling sk pointer on error in l2cap_sock_create() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 21/61] Bluetooth: RFCOMM: avoid leaving dangling sk pointer in rfcomm_sock_alloc() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 22/61] net: af_can: do not leave a dangling sk pointer in can_create() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 23/61] net: ieee802154: do not leave a dangling sk pointer in ieee802154_create() Sasha Levin
2024-11-24 13:44 ` [PATCH AUTOSEL 6.6 24/61] net: inet: do not leave a dangling sk pointer in inet_create() Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 25/61] net: inet6: do not leave a dangling sk pointer in inet6_create() Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 26/61] wifi: ath5k: add PCI ID for SX76X Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 27/61] wifi: ath5k: add PCI ID for Arcadyan devices Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 28/61] fanotify: allow reporting errors on failure to open fd Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 29/61] drm/panel: simple: Add Microchip AC69T88A LVDS Display panel Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 30/61] net: sfp: change quirks for Alcatel Lucent G-010S-P Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 31/61] net: stmmac: Programming sequence for VLAN packets with split header Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 32/61] drm/sched: memset() 'job' in drm_sched_job_init() Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 33/61] drm/amdgpu: clear RB_OVERFLOW bit when enabling interrupts for vega20_ih Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 34/61] drm/amdgpu: Dereference the ATCS ACPI buffer Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 35/61] netlink: specs: Add missing bitset attrs to ethtool spec Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 36/61] drm/amdgpu: refine error handling in amdgpu_ttm_tt_pin_userptr Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 37/61] dma-debug: fix a possible deadlock on radix_lock Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 38/61] jfs: array-index-out-of-bounds fix in dtReadFirst Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 39/61] jfs: fix shift-out-of-bounds in dbSplit Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 40/61] jfs: fix array-index-out-of-bounds in jfs_readdir Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 41/61] jfs: add a check to prevent array-index-out-of-bounds in dbAdjTree Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 42/61] fsl/fman: Validate cell-index value obtained from Device Tree Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 43/61] net: enetc: remove ERR050089 workaround for i.MX95 Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 44/61] net: enetc: add i.MX95 EMDIO support Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 45/61] drm/amdgpu: skip amdgpu_device_cache_pci_state under sriov Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 46/61] virtio-net: fix overflow inside virtnet_rq_alloc Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 47/61] ALSA: usb-audio: Make mic volume workarounds globally applicable Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 48/61] drm/amdgpu: set the right AMDGPU sg segment limitation Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 49/61] wifi: ipw2x00: libipw_rx_any(): fix bad alignment Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 50/61] wifi: brcmfmac: Fix oops due to NULL pointer dereference in brcmf_sdiod_sglist_rw() Sasha Levin
2024-11-24 13:45 ` Sasha Levin [this message]
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 52/61] dsa: qca8k: Use nested lock to avoid splat Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 53/61] Bluetooth: btusb: Add RTL8852BE device 0489:e123 to device tables Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 54/61] Bluetooth: hci_core: Fix not checking skb length on hci_acldata_packet Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 55/61] Bluetooth: Add new quirks for ATS2851 Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 56/61] Bluetooth: Support " Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 57/61] Bluetooth: Set " Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 58/61] ASoC: hdmi-codec: reorder channel allocation list Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 59/61] rocker: fix link status detection in rocker_carrier_init() Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 60/61] net/neighbor: clear error in case strict check is not set Sasha Levin
2024-11-24 13:45 ` [PATCH AUTOSEL 6.6 61/61] netpoll: Use rcu_access_pointer() in __netpoll_setup Sasha Levin
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241124134637.3346391-51-sashal@kernel.org \
--to=sashal@kernel.org \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=houtao1@huawei.com \
--cc=linux-kernel@vger.kernel.org \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®