Skip to content

Commit c6439bf

Browse files
committed
Merge tag 'trace-deferred-unwind-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace
Pull initial deferred unwind infrastructure from Steven Rostedt: "This is the core infrastructure for the deferred unwinder that is required for sframes[1]. Several other patch series are based on this work although those patch series are not dependent on each other. In order to simplify the development, having this core series upstream will allow the other series to be worked on in parallel. The other series are: - The two patches to implement x86 support [2] [3] - The s390 work [4] - The perf work [5] - The ftrace work [6] - The sframe work [7] And more is on the way. The core infrastructure adds the following in kernel APIs: - int unwind_user_faultable(struct unwind_stacktrace *trace); Performs a user space stack trace that may fault user pages in. - int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func); Allows a tracer to register with the unwind deferred infrastructure. - int unwind_deferred_request(struct unwind_work *work, u64 *cookie); Used when a tracer request a deferred trace. Can be called from interrupt or NMI context. - void unwind_deferred_cancel(struct unwind_work *work); Called by a tracer to unregister from the deferred unwind infrastructure. - void unwind_deferred_task_exit(struct task_struct *task); Called by task exit code to flush any pending unwind requests. - void unwind_task_init(struct task_struct *task); Called by do_fork() to initialize the task struct for the deferred unwinder. - void unwind_task_free(struct task_struct *task); Called by do_exit() to free up any resources used by the deferred unwinder. None of the above is actually compiled unless an architecture enables it, which none currently do" Link: https://sourceware.org/binutils/wiki/sframe [1] Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ [2] Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ [3] Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ [4] Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ [5] Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ [6] Link: https://lore.kernel.org/linux-trace-kernel/[email protected]/ [7] * tag 'trace-deferred-unwind-v6.17' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: unwind: Finish up unwind when a task exits unwind deferred: Use SRCU unwind_deferred_task_work() unwind: Add USED bit to only have one conditional on way back to user space unwind deferred: Add unwind_completed mask to stop spurious callbacks unwind deferred: Use bitmask to determine which callbacks to call unwind_user/deferred: Make unwind deferral requests NMI-safe unwind_user/deferred: Add deferred unwinding interface unwind_user/deferred: Add unwind cache unwind_user/deferred: Add unwind_user_faultable() unwind_user: Add user space unwinding API with frame pointer support
2 parents 89748ac + b3b9cb1 commit c6439bf

File tree

16 files changed

+703
-0
lines changed

16 files changed

+703
-0
lines changed

MAINTAINERS

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26253,6 +26253,13 @@ F: Documentation/driver-api/uio-howto.rst
2625326253
F: drivers/uio/
2625426254
F: include/linux/uio_driver.h
2625526255

26256+
USERSPACE STACK UNWINDING
26257+
M: Josh Poimboeuf <[email protected]>
26258+
M: Steven Rostedt <[email protected]>
26259+
S: Maintained
26260+
F: include/linux/unwind*.h
26261+
F: kernel/unwind/
26262+
2625626263
UTIL-LINUX PACKAGE
2625726264
M: Karel Zak <[email protected]>
2625826265

arch/Kconfig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,6 +444,13 @@ config HAVE_HARDLOCKUP_DETECTOR_ARCH
444444
It uses the same command line parameters, and sysctl interface,
445445
as the generic hardlockup detectors.
446446

447+
config UNWIND_USER
448+
bool
449+
450+
config HAVE_UNWIND_USER_FP
451+
bool
452+
select UNWIND_USER
453+
447454
config HAVE_PERF_REGS
448455
bool
449456
help

include/asm-generic/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ mandatory-y += tlbflush.h
5959
mandatory-y += topology.h
6060
mandatory-y += trace_clock.h
6161
mandatory-y += uaccess.h
62+
mandatory-y += unwind_user.h
6263
mandatory-y += vermagic.h
6364
mandatory-y += vga.h
6465
mandatory-y += video.h

include/asm-generic/unwind_user.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_GENERIC_UNWIND_USER_H
3+
#define _ASM_GENERIC_UNWIND_USER_H
4+
5+
#endif /* _ASM_GENERIC_UNWIND_USER_H */

include/linux/irq-entry-common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <linux/context_tracking.h>
88
#include <linux/tick.h>
99
#include <linux/kmsan.h>
10+
#include <linux/unwind_deferred.h>
1011

1112
#include <asm/entry-common.h>
1213

@@ -256,6 +257,7 @@ static __always_inline void exit_to_user_mode(void)
256257
lockdep_hardirqs_on_prepare();
257258
instrumentation_end();
258259

260+
unwind_reset_info();
259261
user_enter_irqoff();
260262
arch_exit_to_user_mode();
261263
lockdep_hardirqs_on(CALLER_ADDR0);

include/linux/sched.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <linux/rv.h>
4848
#include <linux/uidgid_types.h>
4949
#include <linux/tracepoint-defs.h>
50+
#include <linux/unwind_deferred_types.h>
5051
#include <asm/kmap_size.h>
5152

5253
/* task_struct member predeclarations (sorted alphabetically): */
@@ -1646,6 +1647,10 @@ struct task_struct {
16461647
struct user_event_mm *user_event_mm;
16471648
#endif
16481649

1650+
#ifdef CONFIG_UNWIND_USER
1651+
struct unwind_task_info unwind_info;
1652+
#endif
1653+
16491654
/* CPU-specific state of this task: */
16501655
struct thread_struct thread;
16511656

include/linux/unwind_deferred.h

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_UNWIND_USER_DEFERRED_H
3+
#define _LINUX_UNWIND_USER_DEFERRED_H
4+
5+
#include <linux/task_work.h>
6+
#include <linux/unwind_user.h>
7+
#include <linux/unwind_deferred_types.h>
8+
9+
struct unwind_work;
10+
11+
typedef void (*unwind_callback_t)(struct unwind_work *work, struct unwind_stacktrace *trace, u64 cookie);
12+
13+
struct unwind_work {
14+
struct list_head list;
15+
unwind_callback_t func;
16+
int bit;
17+
};
18+
19+
#ifdef CONFIG_UNWIND_USER
20+
21+
enum {
22+
UNWIND_PENDING_BIT = 0,
23+
UNWIND_USED_BIT,
24+
};
25+
26+
enum {
27+
UNWIND_PENDING = BIT(UNWIND_PENDING_BIT),
28+
29+
/* Set if the unwinding was used (directly or deferred) */
30+
UNWIND_USED = BIT(UNWIND_USED_BIT)
31+
};
32+
33+
void unwind_task_init(struct task_struct *task);
34+
void unwind_task_free(struct task_struct *task);
35+
36+
int unwind_user_faultable(struct unwind_stacktrace *trace);
37+
38+
int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func);
39+
int unwind_deferred_request(struct unwind_work *work, u64 *cookie);
40+
void unwind_deferred_cancel(struct unwind_work *work);
41+
42+
void unwind_deferred_task_exit(struct task_struct *task);
43+
44+
static __always_inline void unwind_reset_info(void)
45+
{
46+
struct unwind_task_info *info = &current->unwind_info;
47+
unsigned long bits;
48+
49+
/* Was there any unwinding? */
50+
if (unlikely(info->unwind_mask)) {
51+
bits = info->unwind_mask;
52+
do {
53+
/* Is a task_work going to run again before going back */
54+
if (bits & UNWIND_PENDING)
55+
return;
56+
} while (!try_cmpxchg(&info->unwind_mask, &bits, 0UL));
57+
current->unwind_info.id.id = 0;
58+
59+
if (unlikely(info->cache)) {
60+
info->cache->nr_entries = 0;
61+
info->cache->unwind_completed = 0;
62+
}
63+
}
64+
}
65+
66+
#else /* !CONFIG_UNWIND_USER */
67+
68+
static inline void unwind_task_init(struct task_struct *task) {}
69+
static inline void unwind_task_free(struct task_struct *task) {}
70+
71+
static inline int unwind_user_faultable(struct unwind_stacktrace *trace) { return -ENOSYS; }
72+
static inline int unwind_deferred_init(struct unwind_work *work, unwind_callback_t func) { return -ENOSYS; }
73+
static inline int unwind_deferred_request(struct unwind_work *work, u64 *timestamp) { return -ENOSYS; }
74+
static inline void unwind_deferred_cancel(struct unwind_work *work) {}
75+
76+
static inline void unwind_deferred_task_exit(struct task_struct *task) {}
77+
static inline void unwind_reset_info(void) {}
78+
79+
#endif /* !CONFIG_UNWIND_USER */
80+
81+
#endif /* _LINUX_UNWIND_USER_DEFERRED_H */

include/linux/unwind_deferred_types.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_UNWIND_USER_DEFERRED_TYPES_H
3+
#define _LINUX_UNWIND_USER_DEFERRED_TYPES_H
4+
5+
struct unwind_cache {
6+
unsigned long unwind_completed;
7+
unsigned int nr_entries;
8+
unsigned long entries[];
9+
};
10+
11+
/*
12+
* The unwind_task_id is a unique identifier that maps to a user space
13+
* stacktrace. It is generated the first time a deferred user space
14+
* stacktrace is requested after a task has entered the kerenl and
15+
* is cleared to zero when it exits. The mapped id will be a non-zero
16+
* number.
17+
*
18+
* To simplify the generation of the 64 bit number, 32 bits will be
19+
* the CPU it was generated on, and the other 32 bits will be a per
20+
* cpu counter that gets incremented by two every time a new identifier
21+
* is generated. The LSB will always be set to keep the value
22+
* from being zero.
23+
*/
24+
union unwind_task_id {
25+
struct {
26+
u32 cpu;
27+
u32 cnt;
28+
};
29+
u64 id;
30+
};
31+
32+
struct unwind_task_info {
33+
unsigned long unwind_mask;
34+
struct unwind_cache *cache;
35+
struct callback_head work;
36+
union unwind_task_id id;
37+
};
38+
39+
#endif /* _LINUX_UNWIND_USER_DEFERRED_TYPES_H */

include/linux/unwind_user.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_UNWIND_USER_H
3+
#define _LINUX_UNWIND_USER_H
4+
5+
#include <linux/unwind_user_types.h>
6+
#include <asm/unwind_user.h>
7+
8+
#ifndef ARCH_INIT_USER_FP_FRAME
9+
#define ARCH_INIT_USER_FP_FRAME
10+
#endif
11+
12+
int unwind_user(struct unwind_stacktrace *trace, unsigned int max_entries);
13+
14+
#endif /* _LINUX_UNWIND_USER_H */

include/linux/unwind_user_types.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_UNWIND_USER_TYPES_H
3+
#define _LINUX_UNWIND_USER_TYPES_H
4+
5+
#include <linux/types.h>
6+
7+
/*
8+
* Unwind types, listed in priority order: lower numbers are attempted first if
9+
* available.
10+
*/
11+
enum unwind_user_type_bits {
12+
UNWIND_USER_TYPE_FP_BIT = 0,
13+
14+
NR_UNWIND_USER_TYPE_BITS,
15+
};
16+
17+
enum unwind_user_type {
18+
/* Type "none" for the start of stack walk iteration. */
19+
UNWIND_USER_TYPE_NONE = 0,
20+
UNWIND_USER_TYPE_FP = BIT(UNWIND_USER_TYPE_FP_BIT),
21+
};
22+
23+
struct unwind_stacktrace {
24+
unsigned int nr;
25+
unsigned long *entries;
26+
};
27+
28+
struct unwind_user_frame {
29+
s32 cfa_off;
30+
s32 ra_off;
31+
s32 fp_off;
32+
bool use_fp;
33+
};
34+
35+
struct unwind_user_state {
36+
unsigned long ip;
37+
unsigned long sp;
38+
unsigned long fp;
39+
enum unwind_user_type current_type;
40+
unsigned int available_types;
41+
bool done;
42+
};
43+
44+
#endif /* _LINUX_UNWIND_USER_TYPES_H */

0 commit comments

Comments
 (0)