diff --git a/include/linux/profile.h b/include/linux/profile.h index 11db1ec516e2..75258860a5e1 100644 --- a/include/linux/profile.h +++ b/include/linux/profile.h @@ -31,6 +31,11 @@ static inline int create_proc_profile(void) } #endif +enum profile_type { + PROFILE_TASK_EXIT, + PROFILE_MUNMAP +}; + #ifdef CONFIG_PROFILING extern int prof_on __read_mostly; @@ -61,6 +66,15 @@ static inline void profile_hit(int type, void *ip) struct task_struct; struct mm_struct; +/* task is in do_exit() */ +void profile_task_exit(struct task_struct * task); + +/* sys_munmap */ +void profile_munmap(unsigned long addr); + +int profile_event_register(enum profile_type, struct notifier_block * n); +int profile_event_unregister(enum profile_type, struct notifier_block * n); + #else #define prof_on 0 @@ -85,6 +99,18 @@ static inline void profile_hit(int type, void *ip) return; } +static inline int profile_event_register(enum profile_type t, struct notifier_block * n) +{ + return -ENOSYS; +} + +static inline int profile_event_unregister(enum profile_type t, struct notifier_block * n) +{ + return -ENOSYS; +} + +#define profile_task_exit(a) do { } while (0) +#define profile_munmap(a) do { } while (0) #endif /* CONFIG_PROFILING */ diff --git a/kernel/exit.c b/kernel/exit.c index 15dc2ec80c46..3c9d90fa6d48 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -811,6 +811,7 @@ void __noreturn do_exit(long code) WARN_ON(tsk->plug); + profile_task_exit(tsk); kcov_task_exit(tsk); kmsan_task_exit(tsk); diff --git a/kernel/profile.c b/kernel/profile.c index 8a77769bc4b4..f568049eb1fe 100644 --- a/kernel/profile.c +++ b/kernel/profile.c @@ -136,6 +136,59 @@ int __ref profile_init(void) return -ENOMEM; } +/* Profile event notifications */ + +static BLOCKING_NOTIFIER_HEAD(task_exit_notifier); +static BLOCKING_NOTIFIER_HEAD(munmap_notifier); + +void profile_task_exit(struct task_struct *task) +{ + blocking_notifier_call_chain(&task_exit_notifier, 0, task); +} + +void profile_munmap(unsigned long addr) +{ + blocking_notifier_call_chain(&munmap_notifier, 0, (void *)addr); +} + +int profile_event_register(enum profile_type type, struct notifier_block *n) +{ + int err = -EINVAL; + + switch (type) { + case PROFILE_TASK_EXIT: + err = blocking_notifier_chain_register( + &task_exit_notifier, n); + break; + case PROFILE_MUNMAP: + err = blocking_notifier_chain_register( + &munmap_notifier, n); + break; + } + + return err; +} +EXPORT_SYMBOL_GPL(profile_event_register); + +int profile_event_unregister(enum profile_type type, struct notifier_block *n) +{ + int err = -EINVAL; + + switch (type) { + case PROFILE_TASK_EXIT: + err = blocking_notifier_chain_unregister( + &task_exit_notifier, n); + break; + case PROFILE_MUNMAP: + err = blocking_notifier_chain_unregister( + &munmap_notifier, n); + break; + } + + return err; +} +EXPORT_SYMBOL_GPL(profile_event_unregister); + #if defined(CONFIG_SMP) && defined(CONFIG_PROC_FS) /* * Each cpu has a pair of open-addressed hashtables for pending diff --git a/mm/mmap.c b/mm/mmap.c index 47c95ec2df33..2704c0004c75 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -2802,6 +2802,7 @@ EXPORT_SYMBOL(vm_munmap); SYSCALL_DEFINE2(munmap, unsigned long, addr, size_t, len) { addr = untagged_addr(addr); + profile_munmap(addr); return __vm_munmap(addr, len, true); }