/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_UNWIND_USER_DEFERRED_H #define _LINUX_UNWIND_USER_DEFERRED_H #include #include #include 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 */