summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/unwind_deferred.h4
-rw-r--r--include/linux/unwind_deferred_types.h1
-rw-r--r--kernel/unwind/deferred.c19
3 files changed, 19 insertions, 5 deletions
diff --git a/include/linux/unwind_deferred.h b/include/linux/unwind_deferred.h
index 337ead927d4d..b9ec4c8515c7 100644
--- a/include/linux/unwind_deferred.h
+++ b/include/linux/unwind_deferred.h
@@ -55,8 +55,10 @@ static __always_inline void unwind_reset_info(void)
* depends on nr_entries being cleared on exit to user,
* this needs to be a separate conditional.
*/
- if (unlikely(info->cache))
+ if (unlikely(info->cache)) {
info->cache->nr_entries = 0;
+ info->cache->unwind_completed = 0;
+ }
}
#else /* !CONFIG_UNWIND_USER */
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 e19f02ef416d..a3d26014a2e6 100644
--- a/kernel/unwind/deferred.c
+++ b/kernel/unwind/deferred.c
@@ -166,12 +166,18 @@ static void unwind_deferred_task_work(struct callback_head *head)
unwind_user_faultable(&trace);
+ if (info->cache)
+ bits &= ~(info->cache->unwind_completed);
+
cookie = info->id.id;
guard(mutex)(&callback_mutex);
list_for_each_entry(work, &callbacks, list) {
- 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);
+ }
}
}
@@ -260,23 +266,28 @@ int unwind_deferred_request(struct unwind_work *work, u64 *cookie)
void unwind_deferred_cancel(struct unwind_work *work)
{
struct task_struct *g, *t;
+ int bit;
if (!work)
return;
+ bit = work->bit;
+
/* No work should be using a reserved bit */
- if (WARN_ON_ONCE(BIT(work->bit) & RESERVED_BITS))
+ if (WARN_ON_ONCE(BIT(bit) & RESERVED_BITS))
return;
guard(mutex)(&callback_mutex);
list_del(&work->list);
- __clear_bit(work->bit, &unwind_mask);
+ __clear_bit(bit, &unwind_mask);
guard(rcu)();
/* Clear this bit from all threads */
for_each_process_thread(g, t) {
- clear_bit(work->bit, &t->unwind_info.unwind_mask);
+ clear_bit(bit, &t->unwind_info.unwind_mask);
+ if (t->unwind_info.cache)
+ clear_bit(bit, &t->unwind_info.cache->unwind_completed);
}
}