diff options
| -rw-r--r-- | helloworld.c | 3 | ||||
| -rw-r--r-- | watch.c | 2 | ||||
| -rw-r--r-- | watch.h | 6 | ||||
| -rw-r--r-- | watch_module.c | 34 | ||||
| -rw-r--r-- | watch_module.h | 68 | ||||
| -rw-r--r-- | watch_module_lib.c | 182 |
6 files changed, 236 insertions, 59 deletions
diff --git a/helloworld.c b/helloworld.c index aef2ac4..3996a0f 100644 --- a/helloworld.c +++ b/helloworld.c @@ -11,6 +11,7 @@ int main() watch_arg watch_arg = { .task_id = getpid(), .ptr = &temp, + .name = "temp", .length_byte = sizeof(int), .threshold = 105, .unsigned_flag = 1, @@ -26,6 +27,6 @@ int main() sleep(1); } - cancel_watch(); + cancel_all_watch(); return 0; }
\ No newline at end of file @@ -30,7 +30,7 @@ int start_watch(watch_arg w_arg) /// @brief cancel watch /// @return 0 means success, other means fail -int cancel_watch() +int cancel_all_watch() { if (file_desc < 0) { @@ -1,9 +1,11 @@ // watch.h #include <sys/types.h> +#define MAX_NAME_LEN (15) // max name length typedef struct { pid_t task_id; // current process id + char name[MAX_NAME_LEN + 1]; // name void *ptr; // virtual address int length_byte; // byte long long threshold; // threshold value @@ -15,5 +17,5 @@ typedef struct // start watch int start_watch(watch_arg w_arg); -// cancel watch -int cancel_watch();
\ No newline at end of file +// cancel all watch +int cancel_all_watch();
\ No newline at end of file diff --git a/watch_module.c b/watch_module.c index 04aff1d..a6b900e 100644 --- a/watch_module.c +++ b/watch_module.c @@ -23,12 +23,9 @@ static int device_release(struct inode *inode, struct file *file) { printk(KERN_INFO "%s\n", __FUNCTION__); // unmap and release the page - if (kaddr) - kunmap(kaddr); - if (page) - put_page(page); + free_page_list(); // cancel timer - cancel_hrTimer(); + cancel_all_hrTimer(); return 0; } @@ -36,17 +33,18 @@ static long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned lon { watch_arg warg; void *ptr; + kernel_watch_timer *timer = NULL; + kernel_watch_arg k_watch_arg; // copy watch_arg if (copy_from_user(&warg, (watch_arg *)ioctl_param, sizeof(warg))) { return -EACCES; } - printk(KERN_INFO "Received: task_id=%d, ptr=%p, length_byte=%d, time_ns=%ld, threshold=%lld\n", warg.task_id, warg.ptr, - warg.length_byte, warg.time_ns, warg.threshold); + printk(KERN_INFO "Received: task_id=%d, name=%s, ptr=%p, length_byte=%d, time_ns=%ld, threshold=%lld\n", + warg.task_id, warg.name, warg.ptr, warg.length_byte, warg.time_ns, warg.threshold); // user space address to kernel space address ptr = access_user_space_ptr(warg.task_id, (unsigned long)warg.ptr); - if (ptr == NULL) { printk(KERN_ERR "Cannot access user space\n"); @@ -58,17 +56,19 @@ static long device_ioctl(struct file *file, unsigned int ioctl_num, unsigned lon printk(KERN_ERR "Invalid length %d\n", warg.length_byte); return -EINVAL; } - // k_watch_arg init - k_watch_arg.kptr = ptr; - k_watch_arg.length_byte = warg.length_byte; - k_watch_arg.threshold = warg.threshold; - k_watch_arg.unsigned_flag = warg.unsigned_flag; - k_watch_arg.greater_flag = warg.greater_flag; - // start timer - start_hrTimer(warg.time_ns); - printk(KERN_INFO "Start watching\n"); + w_arg2k_w_arg(ptr, warg, &k_watch_arg); + timer = get_timer(warg.time_ns); // get a valuable timer + printk(KERN_INFO "timer: %p\n", timer); + printk(KERN_INFO "timer->sentinel: %d, timer->time_ns: %lld\n", timer->sentinel, timer->time_ns); + printk(KERN_INFO "timer->hr_timer: %p\n", &timer->hr_timer); + + TIMER_CANCEL(timer); // just in case + timer_add_watch(timer, k_watch_arg); + TIMER_START(timer); + + printk(KERN_INFO "Start watching\n"); return 0; } diff --git a/watch_module.h b/watch_module.h index d4f48c5..41a49ff 100644 --- a/watch_module.h +++ b/watch_module.h @@ -1,6 +1,9 @@ #include <linux/hrtimer.h> #include <linux/kprobes.h> #include <linux/ktime.h> +#include <linux/list.h> +#include <linux/slab.h> /* for kmalloc */ +#include <linux/string.h> #include <asm/uaccess.h> #include <linux/cdev.h> @@ -11,44 +14,85 @@ #include <linux/sched/signal.h> #include <linux/stacktrace.h> /* for stack_trace_print */ +#define MAX_TIMER_NUM (2048) // max timer number +#define TIMER_MAX_WATCH_NUM (32) // A timer max watch number at once time +#define MAX_NAME_LEN (15) // max name length typedef struct { pid_t task_id; // current process id + char name[MAX_NAME_LEN + 1]; // name void *ptr; // virtual address - int length_byte; // byte + int length_byte; // byte long long threshold; // threshold value unsigned char unsigned_flag; // unsigned flag (true: unsigned, false: signed) unsigned char greater_flag; // reverse flag (true: >, false: <) - unsigned long time_ns; // timer interval (ns) + unsigned long time_ns; // timer interval (ns) } watch_arg; typedef struct { + char name[MAX_NAME_LEN + 2]; // name, last char automatically add '\0' void *kptr; // kernel address + offset - int length_byte; // byte + int length_byte; // byte long long threshold; // threshold value unsigned char unsigned_flag; // unsigned flag (true: unsigned, false: signed) unsigned char greater_flag; // reverse flag (true: >, false: <) } kernel_watch_arg; -kernel_watch_arg k_watch_arg; -EXPORT_SYMBOL(k_watch_arg); // export k_watch_arg +typedef struct +{ + unsigned long long time_ns; // hrTimer time interval (ns) + struct hrtimer hr_timer; // hrTimer + ktime_t kt; // hrTimer time + unsigned sentinel; // sentinel + kernel_watch_arg k_watch_args[TIMER_MAX_WATCH_NUM]; // all watched kernel_watch_arg +} kernel_watch_timer; + +#define TIMER_FILLED(timer) ((timer)->sentinel >= TIMER_MAX_WATCH_NUM) +#define TIMER_EMPTY(timer) (!((timer)->time_ns | (timer)->sentinel)) + +#define TIMER_START(timer) (hrtimer_start(&timer->hr_timer, timer->kt, HRTIMER_MODE_REL)) +#define TIMER_CANCEL(timer) (hrtimer_cancel(&timer->hr_timer)) + +kernel_watch_timer kernel_wtimer_list[MAX_TIMER_NUM] = {0}; // all kernel_watch_timer +int kernel_wtimer_num = 0; // current kernel_watch_timer number + +EXPORT_SYMBOL(kernel_wtimer_list); // export kernel_watch_timer_list +EXPORT_SYMBOL(kernel_wtimer_num); // export kernel_watch_timer_num + +// Helper function +unsigned char w_arg2k_w_arg(void *ptr, watch_arg warg, kernel_watch_arg *k_watch_arg); + +// for timer +kernel_watch_timer *get_timer(unsigned long long time_ns); +unsigned char timer_add_watch(kernel_watch_timer *timer, kernel_watch_arg k_watch_arg); // for memory access -static struct page *page = NULL; -static void *kaddr = NULL; +typedef struct +{ + struct page *page; + void *kaddr; + struct list_head entry; +} watch_local_memory; + +static LIST_HEAD(watch_local_memory_list); + +void free_page_list(void); + +// static struct page *page = NULL; +// static void *kaddr = NULL; void *access_user_space_ptr(pid_t pid, unsigned long kaddr); // for timer #define US2NS (1000) // Interval in microseconds -static struct hrtimer hr_timer; -static ktime_t kt; +// static struct hrtimer hr_timer; +// static ktime_t kt; // hrTimer enum hrtimer_restart hrtimer_hander(struct hrtimer *timer); -int start_hrTimer(unsigned long timeout); -void cancel_hrTimer(void); +// int start_hrTimer(unsigned long timeout); +void cancel_all_hrTimer(void); unsigned char read_and_compare(kernel_watch_arg *k_arg); @@ -95,8 +139,6 @@ static void print_all_task_stack(void) unsigned long backtrace[BACKTRACE_DEPTH]; // save stack unsigned int nr_bt; // stack depth unsigned long long current_time; // last time - printk("-------------------------------------\n"); - printk("-------------watch monitor-----------\n"); current_time = ktime_get_real(); printk("Timestamp (ns): %lld\n", current_time); printk("Recent Load: %lu.%02lu, %lu.%02lu, %lu.%02lu\n", // recent load diff --git a/watch_module_lib.c b/watch_module_lib.c index 28bd3a0..43eb08b 100644 --- a/watch_module_lib.c +++ b/watch_module_lib.c @@ -1,5 +1,80 @@ #include "watch_module.h" +unsigned char w_arg2k_w_arg(void *ptr, watch_arg warg, kernel_watch_arg *k_watch_arg) +{ + // k_watch_arg init + strncpy(k_watch_arg->name, warg.name, MAX_NAME_LEN + 1); // name + k_watch_arg->name[MAX_NAME_LEN + 1] = '\0'; // just in case + k_watch_arg->kptr = ptr; + k_watch_arg->length_byte = warg.length_byte; + k_watch_arg->threshold = warg.threshold; + k_watch_arg->unsigned_flag = warg.unsigned_flag; + k_watch_arg->greater_flag = warg.greater_flag; + return 0; +} + +/// @brief get a valuable timer +/// @param time_ns +/// @return kernel_watch_timer *, NULL means fail +kernel_watch_timer *get_timer(unsigned long long time_ns) +{ + int i = 0; + kernel_watch_timer *timer = NULL; + // chose a timer + for (i = 0; i < kernel_wtimer_num; i++) + { + timer = &kernel_wtimer_list[i]; + + if (TIMER_EMPTY(timer)){ + break; + } + if ((timer->time_ns == time_ns) && (!TIMER_FILLED(timer))) + { + break; + } + } + // if all timer is full + if (i >= MAX_TIMER_NUM) + { + printk(KERN_ERR "No timer available\n"); + return NULL; + } + // if a new timer, init it + if (i > kernel_wtimer_num - 1) + { + printk(KERN_INFO "New timer\n"); + + kernel_wtimer_list[i].time_ns = time_ns; + kernel_wtimer_list[i].sentinel = 0; + + kernel_wtimer_list[i].kt = ktime_set(0, (unsigned long)time_ns); // ns + // CLOCK_MONOTONIC: time since boot | HRTIMER_MODE_REL : relative time + hrtimer_init(&(kernel_wtimer_list[i].hr_timer), CLOCK_MONOTONIC, HRTIMER_MODE_REL); + kernel_wtimer_list[i].hr_timer.function = hrtimer_hander; // callback function + + kernel_wtimer_num = i + 1; + } + + return &kernel_wtimer_list[i]; +} + +/// @brief hrTimer add watch +/// @param timer +/// @param k_watch_arg +/// @return 0 is success +unsigned char timer_add_watch(kernel_watch_timer *timer, kernel_watch_arg k_watch_arg) +{ + if (TIMER_FILLED(timer)) + { + printk(KERN_ERR "Timer is full\n"); + return -1; + } + memcpy(&timer->k_watch_args[timer->sentinel], &k_watch_arg, sizeof(k_watch_arg)); + // timer->k_watch_args[timer->sentinel] = k_watch_arg; + timer->sentinel++; + return 0; +} + /// @brief transfer user space address to kernel space address /// change static global "kaddr" and "page" value /// @param pid: process id @@ -11,12 +86,25 @@ void *access_user_space_ptr(pid_t pid, unsigned long addr) struct mm_struct *mm; int ret; + // unsigned long aligned_addr = 0; + // unsigned long offset = 0; + + watch_local_memory *node; + + // if (addr < TASK_SIZE || addr > -PAGE_SIZE) + // { + // printk(KERN_ERR "Invalid address\n"); + // return NULL; + // } + // for get_user_pages_remote unsigned long aligned_addr = addr & PAGE_MASK; unsigned long offset = addr & ~PAGE_MASK; printk(KERN_INFO "%s\n", __FUNCTION__); + node = kmalloc(sizeof(watch_local_memory), GFP_KERNEL); + // Find the task with pid rcu_read_lock(); task = pid_task(find_vpid(pid), PIDTYPE_PID); @@ -25,73 +113,117 @@ void *access_user_space_ptr(pid_t pid, unsigned long addr) if (!task) { printk(KERN_ERR "Cannot find task for PID %d\n", pid); + kfree(node); // careful there is kfree return NULL; } - // Get memory descriptor mm = get_task_mm(task); if (!mm) { printk(KERN_ERR "Cannot get memory descriptor\n"); + kfree(node); // careful there is kfree return NULL; } - down_read(&task->mm->mmap_lock); - ret = get_user_pages_remote(task->mm, aligned_addr, 1, FOLL_FORCE, &page, NULL, NULL); + ret = get_user_pages_remote(task->mm, aligned_addr, 1, FOLL_FORCE, &(node->page), NULL, NULL); up_read(&task->mm->mmap_lock); if (ret != 1) { printk(KERN_ERR "Cannot get user page\n"); + kfree(node); // careful there is kfree return NULL; } - // Map the page to kernel space - kaddr = kmap(page); - return kaddr + offset; + node->kaddr = kmap(node->page); + list_add_tail(&node->entry, &watch_local_memory_list); // add to list + printk(KERN_INFO "node->kaddr: %p, aligned_addr: %ld, offset: %ld\n", node->kaddr, aligned_addr, offset); + return (node->kaddr) + offset; } +/// @brief free all page in watch_local_memory_list +/// @param +void free_page_list(void) +{ + watch_local_memory *node, *next; + list_for_each_entry_safe(node, next, &watch_local_memory_list, entry) + { + if (node == NULL) + break; + // unmap and release the page + if (node->kaddr) + kunmap(node->kaddr); + if (node->page) + put_page(node->page); + list_del(&node->entry); + kfree(node); // careful there is kfree + } +} /// @brief hrTimer handler enum hrtimer_restart hrtimer_hander(struct hrtimer *timer) { - if (read_and_compare(&k_watch_arg)) + kernel_watch_timer *k_watch_timer = container_of(timer, kernel_watch_timer, hr_timer); + int i = 0, j = 0; + char buffer[1024] = {0}; // Buffer to store the messages + + // check all watched kernel_watch_arg + for (i = 0; i < k_watch_timer->sentinel; i++) { - // printk(KERN_INFO "Threshold reached\n"); + if (read_and_compare(&k_watch_timer->k_watch_args[i])) + { + snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), "%s ,", + k_watch_timer->k_watch_args[i].name); + j++; + } + } + if (j > 0) // if any threshold reached + { + printk("-------------------------------------\n"); + printk("-------------watch monitor-----------\n"); + printk("Threshold reached var name: %s\n", buffer); print_all_task_stack(); - // after 1s restart timer + // restart timer after 1s hrtimer_forward(timer, timer->base->get_time(), ktime_set(1, 0)); + printk("-------------------------------------\n"); } else { // keep frequency - hrtimer_forward(timer, timer->base->get_time(), kt); + hrtimer_forward(timer, timer->base->get_time(), k_watch_timer->kt); } - // restart timer - return HRTIMER_RESTART; + return HRTIMER_RESTART; // restart timer } /// @brief start hrTimer /// @param timeout: timeout in us /// @return 0 is success -int start_hrTimer(unsigned long timeout) -{ - printk("HrTimer Start\n"); +// int start_hrTimer(unsigned long timeout) +// { +// printk("HrTimer Start\n"); - kt = ktime_set(0, (unsigned long)timeout); // us -> ns - // CLOCK_MONOTONIC: time since boot | HRTIMER_MODE_REL : relative time - hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - hr_timer.function = hrtimer_hander; - // mode the same as hrtimer_init - hrtimer_start(&hr_timer, kt, HRTIMER_MODE_REL); - return 0; -} +// kt = ktime_set(0, (unsigned long)timeout); // us -> ns +// // CLOCK_MONOTONIC: time since boot | HRTIMER_MODE_REL : relative time +// hrtimer_init(&hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); +// hr_timer.function = hrtimer_hander; +// // mode the same as hrtimer_init +// hrtimer_start(&hr_timer, kt, HRTIMER_MODE_REL); +// return 0; +// } /// @brief cancel hrTimer /// @param -void cancel_hrTimer(void) +void cancel_all_hrTimer(void) { - hrtimer_cancel(&hr_timer); + int i = 0; + kernel_watch_timer *timer = NULL; + for (i = 0; i < kernel_wtimer_num; i++) + { + timer = &(kernel_wtimer_list[i]); + TIMER_CANCEL(timer); + } + + // hrtimer_cancel(&hr_timer); printk("HrTimer End\n"); } |
