-
Notifications
You must be signed in to change notification settings - Fork 6
fprobes and fprobe events implementation from Codasip #19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: codasip-cheri-riscv-6.18
Are you sure you want to change the base?
Changes from all commits
b881363
5f3b9ef
993d6ec
d323280
0662f32
5ae0037
694bbab
c81be47
4ef73bd
c26bc6c
a3da753
1932c85
bb80a16
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| /* SPDX-License-Identifier: GPL-2.0 */ | ||
| #ifndef _ASM_RISCV_FPROBE_H | ||
| #define _ASM_RISCV_FPROBE_H | ||
|
|
||
| #include <asm-generic/fprobe.h> | ||
|
|
||
| #ifdef CONFIG_CHERI_KERNEL | ||
| #undef ARCH_DEFINE_ENCODE_FPROBE_HEADER | ||
| #endif | ||
|
|
||
| #endif /* _ASM_RISCV_FPROBE_H */ | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -147,9 +147,9 @@ static inline void enable_fprobe(struct fprobe *fp) | |
| fp->flags &= ~FPROBE_FL_DISABLED; | ||
| } | ||
|
|
||
| /* The entry data size is 4 bits (=16) * sizeof(long) in maximum */ | ||
| /* The entry data size is 4 bits (=16) * <return stack's word size> in maximum */ | ||
| #define FPROBE_DATA_SIZE_BITS 4 | ||
| #define MAX_FPROBE_DATA_SIZE_WORD ((1L << FPROBE_DATA_SIZE_BITS) - 1) | ||
| #define MAX_FPROBE_DATA_SIZE (MAX_FPROBE_DATA_SIZE_WORD * sizeof(long)) | ||
| #define MAX_FPROBE_DATA_SIZE (MAX_FPROBE_DATA_SIZE_WORD * RET_STACK_WORD_SIZE) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think all replacements of
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to keep the shadow stack changes separate from the fprobe changes. The shadow stack is also used by the function graph tracer. |
||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -259,14 +259,14 @@ static __always_inline bool ftrace_regs_has_args(struct ftrace_regs *fregs) | |
| } | ||
|
|
||
| #ifdef CONFIG_HAVE_REGS_AND_STACK_ACCESS_API | ||
| static __always_inline unsigned long | ||
| static __always_inline uintptr_t | ||
| ftrace_regs_get_kernel_stack_nth(struct ftrace_regs *fregs, unsigned int nth) | ||
| { | ||
| unsigned long *stackp; | ||
| uintptr_t *stackp; | ||
|
|
||
| stackp = (unsigned long *)ftrace_regs_get_stack_pointer(fregs); | ||
| if (((unsigned long)(stackp + nth) & ~(THREAD_SIZE - 1)) == | ||
| ((unsigned long)stackp & ~(THREAD_SIZE - 1))) | ||
| stackp = (uintptr_t *)ftrace_regs_get_stack_pointer(fregs); | ||
| if (((uintptr_t)(stackp + nth) & ~(THREAD_SIZE - 1)) == | ||
| ((uintptr_t)stackp & ~(THREAD_SIZE - 1))) | ||
| return *(stackp + nth); | ||
|
|
||
| return 0; | ||
|
|
@@ -1212,6 +1212,8 @@ struct ftrace_ret_stack { | |
| uintptr_t *retp; | ||
| }; | ||
|
|
||
| #define RET_STACK_WORD_SIZE sizeof(typeof(*current->ret_stack)) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you reduce this to
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, typeof() doesn't make things clearer. |
||
|
|
||
| /* | ||
| * Primary handler of a function return. | ||
| * It relays on ftrace_return_to_handler. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,8 +22,7 @@ | |
|
|
||
| #define FPROBE_HASH_BITS 6 | ||
| #define FPROBE_TABLE_SIZE (1 << FPROBE_HASH_BITS) | ||
|
|
||
| #define SIZE_IN_LONG(x) ((x + sizeof(long) - 1) >> (sizeof(long) == 8 ? 3 : 2)) | ||
| #define SIZE_IN_LONG(x) DIV_ROUND_UP(x, RET_STACK_WORD_SIZE) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The name
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought it's good to not diverge from upstream. But this is misleading, I'll rename it. |
||
|
|
||
| /* | ||
| * fprobe_table: hold 'fprobe_hlist::hlist' for checking the fprobe still | ||
|
|
@@ -56,7 +55,7 @@ static struct fprobe_hlist_node *find_first_fprobe_node(unsigned long ip) | |
| struct fprobe_hlist_node *node; | ||
| struct hlist_head *head; | ||
|
|
||
| head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)]; | ||
| head = &fprobe_ip_table[hash_ptr(__c_fakep(ip), FPROBE_IP_HASH_BITS)]; | ||
| hlist_for_each_entry_rcu(node, head, hlist, | ||
| lockdep_is_held(&fprobe_mutex)) { | ||
| if (node->addr == ip) | ||
|
|
@@ -80,7 +79,7 @@ static void insert_fprobe_node(struct fprobe_hlist_node *node) | |
| hlist_add_before_rcu(&node->hlist, &next->hlist); | ||
| return; | ||
| } | ||
| head = &fprobe_ip_table[hash_ptr((void *)ip, FPROBE_IP_HASH_BITS)]; | ||
| head = &fprobe_ip_table[hash_ptr(__c_fakep(ip), FPROBE_IP_HASH_BITS)]; | ||
| hlist_add_head_rcu(&node->hlist, head); | ||
| } | ||
|
|
||
|
|
@@ -153,7 +152,7 @@ static int del_fprobe_hash(struct fprobe *fp) | |
| /* The arch should encode fprobe_header info into one unsigned long */ | ||
| #define FPROBE_HEADER_SIZE_IN_LONG 1 | ||
|
|
||
| static inline bool write_fprobe_header(unsigned long *stack, | ||
| static inline bool write_fprobe_header(uintptr_t *stack, | ||
| struct fprobe *fp, unsigned int size_words) | ||
| { | ||
| if (WARN_ON_ONCE(size_words > MAX_FPROBE_DATA_SIZE_WORD || | ||
|
|
@@ -164,7 +163,7 @@ static inline bool write_fprobe_header(unsigned long *stack, | |
| return true; | ||
| } | ||
|
|
||
| static inline void read_fprobe_header(unsigned long *stack, | ||
| static inline void read_fprobe_header(uintptr_t *stack, | ||
| struct fprobe **fp, unsigned int *size_words) | ||
| { | ||
| *fp = arch_decode_fprobe_header_fp(*stack); | ||
|
|
@@ -177,11 +176,11 @@ static inline void read_fprobe_header(unsigned long *stack, | |
| struct __fprobe_header { | ||
| struct fprobe *fp; | ||
| unsigned long size_words; | ||
| } __packed; | ||
| } __packed_if_not_cheri; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if this __packed is actually needed or if it could be removed in general. Did you look into this?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree that the __packed isn't really needed for non-cheri. Looking at the upstream code, the __packed was added when the second member was changed from int bits to unsigned long size_words. No idea why they did that. __packed on cheri makes the compilation fail The idea of __packed_if_not_cheri was to keep the non-cheri code unchanged. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had this patch for removal in my list already, I will probably just propose the patch upstream and see if there are any arguments against removal. |
||
|
|
||
| #define FPROBE_HEADER_SIZE_IN_LONG SIZE_IN_LONG(sizeof(struct __fprobe_header)) | ||
|
|
||
| static inline bool write_fprobe_header(unsigned long *stack, | ||
| static inline bool write_fprobe_header(uintptr_t *stack, | ||
| struct fprobe *fp, unsigned int size_words) | ||
| { | ||
| struct __fprobe_header *fph = (struct __fprobe_header *)stack; | ||
|
|
@@ -194,7 +193,7 @@ static inline bool write_fprobe_header(unsigned long *stack, | |
| return true; | ||
| } | ||
|
|
||
| static inline void read_fprobe_header(unsigned long *stack, | ||
| static inline void read_fprobe_header(uintptr_t *stack, | ||
| struct fprobe **fp, unsigned int *size_words) | ||
| { | ||
| struct __fprobe_header *fph = (struct __fprobe_header *)stack; | ||
|
|
@@ -214,7 +213,7 @@ static inline void read_fprobe_header(unsigned long *stack, | |
| * the shadow stack with its entry data size. | ||
| * | ||
| */ | ||
| static inline int __fprobe_handler(unsigned long ip, unsigned long parent_ip, | ||
| static inline int __fprobe_handler(unsigned long ip, uintptr_t parent_ip, | ||
| struct fprobe *fp, struct ftrace_regs *fregs, | ||
| void *data) | ||
| { | ||
|
|
@@ -224,7 +223,7 @@ static inline int __fprobe_handler(unsigned long ip, unsigned long parent_ip, | |
| return fp->entry_handler(fp, ip, parent_ip, fregs, data); | ||
| } | ||
|
|
||
| static inline int __fprobe_kprobe_handler(unsigned long ip, unsigned long parent_ip, | ||
| static inline int __fprobe_kprobe_handler(unsigned long ip, uintptr_t parent_ip, | ||
| struct fprobe *fp, struct ftrace_regs *fregs, | ||
| void *data) | ||
| { | ||
|
|
@@ -250,9 +249,9 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops, | |
| struct ftrace_regs *fregs) | ||
| { | ||
| struct fprobe_hlist_node *node, *first; | ||
| unsigned long *fgraph_data = NULL; | ||
| uintptr_t *fgraph_data = NULL; | ||
| unsigned long func = trace->func; | ||
| unsigned long ret_ip; | ||
| uintptr_t ret_ip; | ||
| int reserved_words; | ||
| struct fprobe *fp; | ||
| int used, ret; | ||
|
|
@@ -280,7 +279,7 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops, | |
| } | ||
| node = first; | ||
| if (reserved_words) { | ||
| fgraph_data = fgraph_reserve_data(gops->idx, reserved_words * sizeof(long)); | ||
| fgraph_data = fgraph_reserve_data(gops->idx, reserved_words * RET_STACK_WORD_SIZE); | ||
| if (unlikely(!fgraph_data)) { | ||
| hlist_for_each_entry_from_rcu(node, hlist) { | ||
| if (node->addr != func) | ||
|
|
@@ -328,8 +327,6 @@ static int fprobe_entry(struct ftrace_graph_ent *trace, struct fgraph_ops *gops, | |
| used += FPROBE_HEADER_SIZE_IN_LONG + size_words; | ||
| } | ||
| } | ||
| if (used < reserved_words) | ||
| memset(fgraph_data + used, 0, reserved_words - used); | ||
|
|
||
| /* If any exit_handler is set, data must be used. */ | ||
| return used != 0; | ||
|
|
@@ -340,13 +337,13 @@ static void fprobe_return(struct ftrace_graph_ret *trace, | |
| struct fgraph_ops *gops, | ||
| struct ftrace_regs *fregs) | ||
| { | ||
| unsigned long *fgraph_data = NULL; | ||
| unsigned long ret_ip; | ||
| uintptr_t *fgraph_data = NULL; | ||
| uintptr_t ret_ip; | ||
| struct fprobe *fp; | ||
| int size, curr; | ||
| int size_words; | ||
|
|
||
| fgraph_data = (unsigned long *)fgraph_retrieve_data(gops->idx, &size); | ||
| fgraph_data = (uintptr_t *)fgraph_retrieve_data(gops->idx, &size); | ||
| if (WARN_ON_ONCE(!fgraph_data)) | ||
| return; | ||
| size_words = SIZE_IN_LONG(size); | ||
|
|
@@ -621,7 +618,7 @@ static int fprobe_init(struct fprobe *fp, unsigned long *addrs, int num) | |
| if (!fp || !addrs || num <= 0) | ||
| return -EINVAL; | ||
|
|
||
| size = ALIGN(fp->entry_data_size, sizeof(long)); | ||
| size = ALIGN(fp->entry_data_size, RET_STACK_WORD_SIZE); | ||
| if (size > MAX_FPROBE_DATA_SIZE) | ||
| return -E2BIG; | ||
| fp->entry_data_size = size; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would be better to only set
ARCH_DEFINE_ENCODE_FPROBE_HEADERfor!CONFIG_CHERI_KERNELindependent of architecture as it shouldn't work for any architecture, right? I don't think this is specific to risc-v, it's just the first architecture with support for it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is the other way around. Almost all archs can encode the fprobe header info into one unsigned long. Only risc-v cheri and loongarch can't do this.