| /* SPDX-License-Identifier: GPL-2.0 */ |
| #ifndef _LINUX_UNWIND_USER_DEFERRED_H |
| #define _LINUX_UNWIND_USER_DEFERRED_H |
| |
| #include <linux/task_work.h> |
| #include <linux/unwind_user.h> |
| #include <linux/unwind_deferred_types.h> |
| |
| struct unwind_work; |
| |
| typedef void (*unwind_callback_t)(struct unwind_work *work, struct unwind_stacktrace *trace, u64 cookie); |
| |
| struct unwind_work { |
| struct list_head list; |
| unwind_callback_t func; |
| int bit; |
| }; |
| |
| #ifdef CONFIG_UNWIND_USER |
| |
| enum { |
| UNWIND_PENDING_BIT = 0, |
| UNWIND_USED_BIT, |
| }; |
| |
| enum { |
| UNWIND_PENDING = BIT(UNWIND_PENDING_BIT), |
| |
| /* Set if the unwinding was used (directly or deferred) */ |
| UNWIND_USED = BIT(UNWIND_USED_BIT) |
| }; |
| |
| void unwind_task_init(struct task_struct *task); |
| void unwind_task_free(struct task_struct *task); |
| |
| int unwind_user_faultable(struct unwind_stacktrace *trace); |
| |
| int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func); |
| int unwind_deferred_request(struct unwind_work *work, u64 *cookie); |
| void unwind_deferred_cancel(struct unwind_work *work); |
| |
| void unwind_deferred_task_exit(struct task_struct *task); |
| |
| static __always_inline void unwind_reset_info(void) |
| { |
| struct unwind_task_info *info = ¤t->unwind_info; |
| unsigned long bits; |
| |
| /* Was there any unwinding? */ |
| if (unlikely(info->unwind_mask)) { |
| bits = info->unwind_mask; |
| do { |
| /* Is a task_work going to run again before going back */ |
| if (bits & UNWIND_PENDING) |
| return; |
| } while (!try_cmpxchg(&info->unwind_mask, &bits, 0UL)); |
| current->unwind_info.id.id = 0; |
| |
| if (unlikely(info->cache)) { |
| info->cache->nr_entries = 0; |
| info->cache->unwind_completed = 0; |
| } |
| } |
| } |
| |
| #else /* !CONFIG_UNWIND_USER */ |
| |
| static inline void unwind_task_init(struct task_struct *task) {} |
| static inline void unwind_task_free(struct task_struct *task) {} |
| |
| static inline int unwind_user_faultable(struct unwind_stacktrace *trace) { return -ENOSYS; } |
| static inline int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func) { return -ENOSYS; } |
| static inline int unwind_deferred_request(struct unwind_work *work, u64 *timestamp) { return -ENOSYS; } |
| static inline void unwind_deferred_cancel(struct unwind_work *work) {} |
| |
| static inline void unwind_deferred_task_exit(struct task_struct *task) {} |
| static inline void unwind_reset_info(void) {} |
| |
| #endif /* !CONFIG_UNWIND_USER */ |
| |
| #endif /* _LINUX_UNWIND_USER_DEFERRED_H */ |