Last active
August 29, 2015 14:21
-
-
Save pwq1989/fb1e89835ecafaa50568 to your computer and use it in GitHub Desktop.
a kernel module sample process's wall-time
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <linux/module.h> | |
#include <linux/sched.h> | |
#include <linux/kernel.h> | |
#include <linux/wait.h> | |
#include <linux/delay.h> | |
#include <linux/fs.h> | |
#include <asm/uaccess.h> | |
#include <linux/mm.h> | |
#include <linux/pid.h> | |
#include <linux/list.h> | |
#include <linux/spinlock.h> | |
#include <linux/smp.h> | |
#include <linux/cpumask.h> | |
#include <linux/kthread.h> | |
#include <linux/string.h> | |
#include <asm/processor.h> | |
#include <linux/perf_event.h> | |
#include <linux/hw_breakpoint.h> | |
#define NOT_USED( x ) (void*)(x) | |
#define THREAD_NAME "walltime_thread" | |
#define TEST_PID_FILE "/home/gin/code/kernel-sample/pid.file" | |
#define SLEEP_TIME 4000 | |
#define BUF_SIZE 32 | |
// status | |
enum { | |
IS_IN_SAMPLING = 0, | |
IS_NOT_IN_SAMPLING = 1 | |
}; | |
struct walltime_struct { | |
struct perf_event **events; | |
unsigned int *flags; | |
unsigned int cpu_number; | |
atomic_t count_down; | |
unsigned int sample_count; | |
atomic_t is_sampling; | |
}; | |
static struct walltime_struct _wts; | |
void wts_init(struct walltime_struct *wts) { | |
wts->events = NULL; | |
wts->flags = __alloc_percpu(sizeof(unsigned int), 8); | |
wts->cpu_number = num_online_cpus(); | |
atomic_set(&wts->count_down, wts->cpu_number); | |
atomic_set(&wts->is_sampling, IS_NOT_IN_SAMPLING); | |
wts->sample_count = 0; | |
} | |
void wts_reset(struct walltime_struct *wts) { | |
int cpu; | |
wts->events = NULL; | |
wts->cpu_number = num_online_cpus(); | |
atomic_set(&wts->count_down, wts->cpu_number); | |
atomic_set(&wts->is_sampling, IS_NOT_IN_SAMPLING); | |
wts->sample_count++; | |
for_each_possible_cpu(cpu) { | |
unsigned int *flag = per_cpu_ptr(wts->flags, cpu); | |
if (*flag) { | |
*flag = 0; | |
} | |
} | |
} | |
void wts_set_start(struct walltime_struct *wts, | |
struct perf_event** events) { | |
wts->events = events; | |
atomic_set(&wts->is_sampling, IS_IN_SAMPLING); | |
} | |
static struct task_struct *ts; | |
static volatile bool stop; // tag for task finish | |
// file read buf, just for example | |
static char buf[BUF_SIZE]; | |
// just an example | |
// read pid information from a file for test | |
static int read_update_pid(void) { | |
char filename[] = TEST_PID_FILE; | |
struct file *filp; | |
struct inode *inode; | |
off_t fsize; | |
mm_segment_t fs; | |
long pid; | |
printk(KERN_INFO "read file start.. \n"); | |
filp = filp_open(filename, O_RDONLY, 0); | |
inode=filp->f_dentry->d_inode; | |
fsize = inode->i_size; | |
BUG_ON(fsize > BUF_SIZE); // overflow | |
fs = get_fs(); | |
set_fs(KERNEL_DS); | |
filp->f_op->read(filp, buf, fsize, &(filp->f_pos)); | |
set_fs(fs); | |
buf[fsize] = '\0'; | |
filp_close(filp, NULL); | |
if (strict_strtoul(buf, 0, &pid) == 0) { // success | |
printk(KERN_INFO "target pid is %d\n ", (int)pid); | |
return (int)pid; | |
} else { | |
return -1; | |
} | |
} | |
static struct perf_event_attr attr; | |
static atomic_t count; | |
static void del_perf_event(struct walltime_struct*); | |
void walltime_perf_callback(struct perf_event *event, | |
struct perf_sample_data *data, | |
struct pt_regs *regs) { | |
int cpu; | |
int *flag; | |
cpu = get_cpu(); | |
flag = per_cpu_ptr(_wts.flags, cpu); | |
if (*flag == 0) { | |
*flag = 1; | |
atomic_dec(&_wts.count_down); | |
} | |
put_cpu(); | |
atomic_inc(&count); | |
} | |
// perf event example | |
static int register_perf_event(struct walltime_struct *wts) { | |
int cpu; | |
struct perf_event **events; | |
printk(KERN_INFO "before create event .\n"); | |
memset(&attr, 0, sizeof(attr)); | |
attr.size = sizeof(struct perf_event_attr); | |
attr.config = 0ULL; | |
attr.type = 1ULL; | |
attr.sample_period = 10000ULL; | |
events = __alloc_percpu(sizeof(struct walltime_struct*), 8); | |
if (events == NULL) return -1; | |
for_each_possible_cpu(cpu) { | |
struct perf_event **event = per_cpu_ptr(events, cpu); | |
if (cpu_is_offline(cpu)) { | |
*event = NULL; | |
continue; | |
} | |
*event = perf_event_create_kernel_counter(&attr, cpu, NULL, walltime_perf_callback, NULL); | |
if (IS_ERR(*event)) { | |
printk (KERN_INFO "create perf event failure\n"); | |
return -1; | |
} | |
} | |
wts_set_start(wts, events); | |
return 0; | |
} | |
static void del_perf_event(struct walltime_struct *wts) { | |
int cpu; | |
for_each_possible_cpu(cpu) { | |
struct perf_event **event = per_cpu_ptr(wts->events, cpu); | |
if (*event) { | |
perf_event_release_kernel(*event); | |
} | |
} | |
free_percpu(wts->events); | |
wts_reset(wts); | |
} | |
static int sample_task_func(void* param) { | |
struct pid *pid_c; | |
struct task_struct *ts_c; | |
struct task_struct *p; | |
int pid_num; | |
NOT_USED(param); | |
printk(KERN_INFO "sample start.. \n"); | |
while (!stop || !kthread_should_stop()) { | |
msleep(SLEEP_TIME); | |
//printk(KERN_INFO "schedule is called. \n"); | |
pid_num = read_update_pid(); | |
if (pid_num == -1) { | |
continue; | |
} | |
rcu_read_lock(); | |
pid_c = find_vpid(pid_num); | |
if (pid_c == NULL) { | |
continue; | |
} | |
ts_c = pid_task(pid_c, PIDTYPE_PID); | |
rcu_read_unlock(); | |
register_perf_event(&_wts); | |
printk(KERN_INFO "finish register!!! \n"); | |
while (atomic_read(&_wts.count_down) > 0) { | |
msleep(1); // wait for finish | |
} | |
del_perf_event(&_wts); | |
rcu_read_lock(); | |
// print main thread | |
printk(KERN_INFO "target pid is %d, rip is %#lx, rbp is %#lx \n ", | |
ts_c->pid, task_pt_regs(ts_c)->ip, task_pt_regs(ts_c)->bp); | |
// print other thread | |
for (p = next_thread(ts_c); | |
p != ts_c; p = next_thread(p)) { | |
printk(KERN_INFO "target pid is %d, rip is %#lx, rbp is %#lx \n ", | |
p->pid, task_pt_regs(p)->ip, task_pt_regs(p)->bp); | |
} | |
rcu_read_unlock(); | |
printk(KERN_INFO "end!!! \n"); | |
} | |
return 0; | |
} | |
/* Init function called on module entry */ | |
int walltime_module_init(void) { | |
char thread_name[] = THREAD_NAME; | |
stop = false; | |
wts_init(&_wts); | |
ts = kthread_create(sample_task_func, NULL, thread_name); | |
wake_up_process(ts); | |
printk(KERN_INFO "walltime_module_init called. Module is now loaded.\n"); | |
return 0; | |
} | |
/* Cleanup function called on module exit */ | |
void walltime_module_cleanup(void) { | |
int ret; | |
// set stop | |
stop = true; | |
if (ts != NULL) { | |
ret = kthread_stop(ts); // for test | |
} | |
//printk(KERN_INFO "count is %d\n", atomic_read(&count)); | |
printk(KERN_INFO "walltime_module_cleanup called. Module is now unloaded.\n"); | |
return; | |
} | |
/* Declare entry and exit functions */ | |
module_init(walltime_module_init); | |
module_exit(walltime_module_cleanup); | |
/* Defines the license for this linux kernel module */ | |
MODULE_LICENSE("GPL"); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment