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

Reply via email to