Provide the /proc/kstackwatch file to read or update the configuration. Only a single process can open this file at a time, enforced using atomic config_file_busy, to prevent concurrent access. ksw_get_config() exposes the configuration pointer as const. Signed-off-by: Jinchao Wang --- mm/kstackwatch/kernel.c | 75 +++++++++++++++++++++++++++++++++++- mm/kstackwatch/kstackwatch.h | 3 ++ 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/mm/kstackwatch/kernel.c b/mm/kstackwatch/kernel.c index 1502795e02af..8e1dca45003e 100644 --- a/mm/kstackwatch/kernel.c +++ b/mm/kstackwatch/kernel.c @@ -3,7 +3,10 @@ #include #include +#include +#include #include +#include #include "kstackwatch.h" @@ -12,6 +15,7 @@ MODULE_DESCRIPTION("Kernel Stack Watch"); MODULE_LICENSE("GPL"); static struct ksw_config *ksw_config; +static atomic_t config_file_busy = ATOMIC_INIT(0); /* * Format of the configuration string: @@ -23,7 +27,7 @@ static struct ksw_config *ksw_config; * - local_var_offset : offset from the stack pointer at function+ip_offset * - local_var_len : length of the local variable(1,2,4,8) */ -static int __maybe_unused ksw_parse_config(char *buf, struct ksw_config *config) +static int ksw_parse_config(char *buf, struct ksw_config *config) { char *func_part, *local_var_part = NULL; char *token; @@ -92,18 +96,87 @@ static int __maybe_unused ksw_parse_config(char *buf, struct ksw_config *config) return -EINVAL; } +static ssize_t kstackwatch_proc_write(struct file *file, + const char __user *buffer, size_t count, + loff_t *pos) +{ + char input[MAX_CONFIG_STR_LEN]; + int ret; + + if (count == 0 || count >= sizeof(input)) + return -EINVAL; + + if (copy_from_user(input, buffer, count)) + return -EFAULT; + + input[count] = '\0'; + strim(input); + + if (!strlen(input)) { + pr_info("config cleared\n"); + return count; + } + + ret = ksw_parse_config(input, ksw_config); + if (ret) { + pr_err("Failed to parse config %d\n", ret); + return ret; + } + + return count; +} + +static int kstackwatch_proc_show(struct seq_file *m, void *v) +{ + seq_printf(m, "%s\n", ksw_config->config_str); + return 0; +} + +static int kstackwatch_proc_open(struct inode *inode, struct file *file) +{ + if (atomic_cmpxchg(&config_file_busy, 0, 1)) + return -EBUSY; + + return single_open(file, kstackwatch_proc_show, NULL); +} + +static int kstackwatch_proc_release(struct inode *inode, struct file *file) +{ + atomic_set(&config_file_busy, 0); + return single_release(inode, file); +} + +static const struct proc_ops kstackwatch_proc_ops = { + .proc_open = kstackwatch_proc_open, + .proc_read = seq_read, + .proc_write = kstackwatch_proc_write, + .proc_lseek = seq_lseek, + .proc_release = kstackwatch_proc_release, +}; + +const struct ksw_config *ksw_get_config(void) +{ + return ksw_config; +} static int __init kstackwatch_init(void) { ksw_config = kzalloc(sizeof(*ksw_config), GFP_KERNEL); if (!ksw_config) return -ENOMEM; + if (!proc_create("kstackwatch", 0600, NULL, &kstackwatch_proc_ops)) { + pr_err("create proc kstackwatch fail"); + kfree(ksw_config); + return -ENOMEM; + } + pr_info("module loaded\n"); return 0; } static void __exit kstackwatch_exit(void) { + remove_proc_entry("kstackwatch", NULL); kfree(ksw_config); pr_info("module unloaded\n"); diff --git a/mm/kstackwatch/kstackwatch.h b/mm/kstackwatch/kstackwatch.h index 7c595c5c24d1..277b192f80fa 100644 --- a/mm/kstackwatch/kstackwatch.h +++ b/mm/kstackwatch/kstackwatch.h @@ -35,4 +35,7 @@ struct ksw_config { char config_str[MAX_CONFIG_STR_LEN]; }; +// singleton, only modified in kernel.c +const struct ksw_config *ksw_get_config(void); + #endif /* _KSTACKWATCH_H */ -- 2.43.0