On Wed, 16 Jul 2025 14:33:52 -0400 Steven Rostedt <rost...@goodmis.org> wrote:
> [Task enters kernel] > request -> add cookie > request -> add cookie > [..] > callback -> add trace + cookie > [ftrace clears bits] > callback -> add trace + cookie > [Task exits back to user space] Another solution could be to add another unwind mask of completed callbacks. Then we could use the fetch_or(). I guess that could look like this: diff --git a/include/linux/unwind_deferred.h b/include/linux/unwind_deferred.h index 15045999c5e2..0124865aaab4 100644 --- a/include/linux/unwind_deferred.h +++ b/include/linux/unwind_deferred.h @@ -61,8 +61,10 @@ static __always_inline void unwind_reset_info(void) } while (!try_cmpxchg(&info->unwind_mask, &bits, 0UL)); current->unwind_info.id.id = 0; - if (unlikely(info->cache)) + if (unlikely(info->cache)) { info->cache->nr_entries = 0; + info->cache->unwind_completed = 0; + } } } diff --git a/include/linux/unwind_deferred_types.h b/include/linux/unwind_deferred_types.h index 5dc9cda141ff..33b62ac25c86 100644 --- a/include/linux/unwind_deferred_types.h +++ b/include/linux/unwind_deferred_types.h @@ -3,6 +3,7 @@ #define _LINUX_UNWIND_USER_DEFERRED_TYPES_H struct unwind_cache { + unsigned long unwind_completed; unsigned int nr_entries; unsigned long entries[]; }; diff --git a/kernel/unwind/deferred.c b/kernel/unwind/deferred.c index 60cc71062f86..9a3e06ee9d63 100644 --- a/kernel/unwind/deferred.c +++ b/kernel/unwind/deferred.c @@ -170,13 +170,19 @@ static void process_unwind_deferred(struct task_struct *task) unwind_user_faultable(&trace); + if (info->cache) + bits &= ~(info->cache->unwind_completed); + cookie = info->id.id; guard(srcu_lite)(&unwind_srcu); list_for_each_entry_srcu(work, &callbacks, list, srcu_read_lock_held(&unwind_srcu)) { - if (test_bit(work->bit, &bits)) + if (test_bit(work->bit, &bits)) { work->func(work, &trace, cookie); + if (info->cache) + info->cache->unwind_completed |= BIT(work->bit); + } } } Instead of adding another long word in the tasks struct, I just use the unwind_cache that gets allocated on the first use. I think this can work. I'll switch it over to this and then I can use the fetch_or() and there should be no extra callbacks, even if an already called callback is requested again after another callback was requested which would trigger another task work. I'll update this patch (and fold it into the bitmask patch) with the fetch_or() and create this patch as a separate patch that just gets rid of spurious callbacks. -- Steve