Re: [bpf-next v3 1/2] bpf: Offload kptr destructors that run from NMI
Kumar Kartikeya Dwivedi <[email protected]> Mon, 11 May 2026 22:10:07 +0200
| Newsgroups | dev.linux.lists.sashiko,org.kernel.vger.bpf |
|---|---|
| Message-ID | <CAP01T75942V+wKJD0yhs8yb7U5vj+y9s6efJ+UdpTMfzP31CWg@mail.gmail.com> |
On Mon, 11 May 2026 at 19:29, Alexei Starovoitov <[email protected]> wrote: > > On Mon May 11, 2026 at 9:38 AM PDT, Justin Suess wrote: > > [ 21.604660] Call Trace: > > [ 21.604662] <TASK> > > [ 21.604663] dump_stack_lvl+0x5d/0x80 > > [ 21.604666] print_usage_bug.part.0+0x22b/0x2c0 > > [ 21.604669] lock_acquire+0x295/0x2e0 > > [ 21.604671] ? terminate_walk+0x33/0x160 > > [ 21.604674] ? __call_rcu_common.constprop.0+0x309/0x730 > > [ 21.604679] _raw_spin_lock+0x30/0x40 > > [ 21.604680] ? __call_rcu_common.constprop.0+0x309/0x730 > > [ 21.604682] __call_rcu_common.constprop.0+0x309/0x730 > > [ 21.604686] bpf_obj_free_fields+0x118/0x250 > > [ 21.604691] free_htab_elem+0x85/0xd0 > > [ 21.604694] htab_map_delete_elem+0x168/0x230 > > [ 21.604698] bpf_prog_f6a7136050cb5431_clear_task_kptrs_from_nmi+0xeb/0x144 > > [ 21.604700] bpf_trace_run3+0x126/0x430 > > that's better. > Looks like we moved bpf_obj_free_fields() into htab_mem_dtor(), > but left check_and_free_fields() in free_htab_elem(). > > I think the fix is to remove check_and_free_fields() from ma path in free_htab_elem() > and fallback to bpf_mem_alloc at map create time when map has kptrs > with dtors. Even when BPF_F_NO_PREALLOC is not specified. > > Kumar, > > thoughts? > > Yeah, removing it from the path that helpers can invoke seems simpler. Remember though, this splat is just for hashtab, we have similar bpf_obj_free_fields() in array map on update. I think fundamentally the main issue here is that we logically free special fields when a map value is freed or deleted. When updating array maps we logically 'free' and then 'update' the same map value together. For hashtab, it happens on update/delete. We could relax this behavior to avoid eagerly freeing these special fields on update or deletion. The only worry is how this would impact programs that have come to rely on the existing behavior. There are patterns where people expect kptr to be NULL on some new map value, which causes programs to return errors when that expectation is not met. Just doing the skip when irqs_disabled() doesn't save us from the surprise side-effect. We need to decide upon this first before discussing the shape of the solution. This is the theoretical concern; In practice, I think most people who depend on such behavior use kptr in local storage maps (in schedulers). So it probably won't be a problem in practice, even though we can't judge this ahead of time. Also, we eagerly reuse map values when using memalloc, so the guarantees are already pretty weak I guess. So, if we are not going to go through a grace period (like local storage) and free back to kernel allocator before reuse, we should relax field freeing behavior. At best, we should cancel work for timer, wq, task_work, and task_work, leaving other items as-is. E.g. BPF_UPTR is used in task storage which I think is accessible to tracing programs, I am not sure how safe unpin_user_page() is when called from random reentrant contexts. We might have more cases in the future, we cannot guarantee we can handle everything in NMIs universally. So the best course of action seems to be relaxing bpf_obj_free_fields() to bpf_obj_cancel_fields() that just does cancel on async work (timer, wq, task_work) for delete / update and let other fields be as-is. We likely need to do bpf_obj_free_fields() additionally before prealloc_destroy() now, but that should be simple. Whether or not to use bpf_ma when kptrs are used in prealloc map is a separate change. This should hopefully resolve the issue, unless I missed other cases.