Thread (38 messages) 38 messages, 4 authors, 2026-01-13

Re: [PATCH bpf-next v3 10/16] bpf: Support lockless unlink when freeing map or local storage

From: Martin KaFai Lau <martin.lau@linux.dev>
Date: 2026-01-13 00:15:34
Also in: bpf


On 1/12/26 2:38 PM, Amery Hung wrote:
quoted
[ btw, a nit, I think it can use a better function name instead of
"lockless". This function still takes the lock if it can. ]
Does using _nofail suffix make it more clear?
sgtm.
quoted
quoted
sure how destroy() can hold b->lock in a way that causes map_free() to
fail acquiring b->lock.
I recall ETIMEDOUT was mentioned to be the likely (only?) case here.
Assume the b->lock did fail in map_free here, there are >1 selem(s)
using the same b->lock. Is it always true that the selem that failed at
the b->lock in map_free() here must race with the very same selem in
destroy()?
This is still an open issue/question.
quoted
quoted
quoted
quoted
+             }
+     }
+
+     /*
+      * Only let destroy() unlink from local_storage->list and do mem_uncharge
+      * as owner is guaranteed to be valid in destroy().
+      */
+     if (local_storage && caller == BPF_LOCAL_STORAGE_DESTROY) {
If I read here correctly, only bpf_local_storage_destroy() can do the
bpf_selem_free(). For example, if a bpf_sk_storage_map is going away,
the selem (which is memcg charged) will stay in the sk until the sk is
closed?
You read it correctly and Yes there will be stale elements in
local_storage->list.

I would hope the unlink from local_storage part is doable from
map_free() and destroy(), but it is hard to guarantee (1) mem_uncharge
is done only once (2) while the owner is still valid in a lockless
way.
This needs to be addressed. It cannot leave the selem lingering. At
least the selem should be freed for the common case (i.e., succeeds in
both locks). Lingering selem is ok in case of lock failure. Many sk can
be long-lived connections. The user space may want to recreate the map,
and it will be limited by the memcg.
I think this is doable by maintaining a local memory charge in local storage.

So, remove selem->size and have a copy of total selem memory charge in
a new local_storage->selem_size. Update will be protected by
local_storage->lock in common paths (not in bpf_selem_unlink_nofail).
More specifically, charge in bpf_selem_link_storage_nolock() when a
selem is going to be publicized. uncharge in
bpf_selem_unlink_storage_nolock() when a selem is being deleted. Then,
in destroy() we simply get the total amount to be uncharged from the
owner from local_storage->selem_size.
Right, I had a similar thought before. Because of the nofail/lockless 
consideration, I suspect the local_storage->selem_size will need to be 
atomic. I am not sure if it is enough though, e.g. there is a debug/warn 
on sk->sk_omem_alloc in __sk_destruct to ensure it is 0, so I stopped 
thinking on it for now, but it could be a direction to explore.

If it is the case, it is another atomic in the destruct/map_free code 
path. I am still open-minded on the nofail requirement for both locks, 
but complexity is building up. Kumar has also commented that b->lock in 
map_free should not fail. Regardless, I think lets get the nofail code 
path for map_free() sorted out first. Then we can move on to handle this 
case.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help