-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathpcrate.c
151 lines (123 loc) · 4.28 KB
/
pcrate.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/*************************************************************************
> File Name: pcrate.c
> Author: sprookie
> Created Time: 2024年12月02日 星期一 11时55分03秒
************************************************************************/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kprobes.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/atomic.h>
#define RESET_PERIOD 3000 /* 1000ms -> 1s */
static struct timer_list reseter;
static atomic64_t mark_page_accessed_count = ATOMIC64_INIT(0);
static atomic64_t mark_buffer_dirty_count = ATOMIC64_INIT(0);
static atomic64_t add_to_page_cache_lru_count = ATOMIC64_INIT(0);
static atomic64_t account_page_dirtied_count = ATOMIC64_INIT(0);
static struct kprobe kp_mpa, kp_mbd, kp_apcl, kp_apd;
static void reset_counters(struct timer_list *unused)
{
atomic64_set(&mark_page_accessed_count, 0);
atomic64_set(&mark_buffer_dirty_count, 0);
atomic64_set(&add_to_page_cache_lru_count, 0);
atomic64_set(&account_page_dirtied_count, 0);
mod_timer(&reseter,
jiffies + msecs_to_jiffies(RESET_PERIOD));
}
static int cache_stat_show(struct seq_file *m, void *v) {
unsigned long long total, hits, misses, ratio;
unsigned long long mpa, mbd, apcl, apd;
mpa = atomic64_read(&mark_page_accessed_count);
mbd = atomic64_read(&mark_buffer_dirty_count);
apcl = atomic64_read(&add_to_page_cache_lru_count);
apd = atomic64_read(&account_page_dirtied_count);
total = mpa >= mbd ? mpa - mbd : 0;
misses = apcl >= apd ? apcl - apd : 0; // Use a safe comparison
hits = total >= misses ? total - misses : 0;
if (misses < 0) misses = 0;
hits = total > misses ? total - misses : 0;
if (total > 0) {
ratio = (1000 * hits) / total;
} else {
ratio = 0;
}
seq_printf(m, "Hits: %llu\nMisses: %llu\nDirties: %llu\nRatio: %llu.%llu%%\n",
hits, misses, atomic64_read(&mark_buffer_dirty_count),
ratio / 10, ratio % 10);
return 0;
}
static int cache_stat_open(struct inode *inode, struct file *file) {
return single_open(file, cache_stat_show, NULL);
}
static const struct proc_ops cache_stat_fops = {
.proc_open = cache_stat_open,
.proc_read = seq_read,
.proc_release = single_release,
};
static int mpa_handler_pre(struct kprobe *p, struct pt_regs *regs) {
atomic64_inc(&mark_page_accessed_count);
return 0;
}
static int mbd_handler_pre(struct kprobe *p, struct pt_regs *regs) {
atomic64_inc(&mark_buffer_dirty_count);
return 0;
}
static int apcl_handler_pre(struct kprobe *p, struct pt_regs *regs) {
atomic64_inc(&add_to_page_cache_lru_count);
return 0;
}
static int apd_handler_pre(struct kprobe *p, struct pt_regs *regs) {
atomic64_inc(&account_page_dirtied_count);
return 0;
}
static int __init cache_stat_init(void) {
int ret;
timer_setup(&reseter, reset_counters, 0);
ret = mod_timer(&reseter,
jiffies + msecs_to_jiffies(RESET_PERIOD));
kp_mpa.pre_handler = mpa_handler_pre;
kp_mpa.symbol_name = "mark_page_accessed";
ret = register_kprobe(&kp_mpa);
if (ret < 0) goto fail;
kp_mbd.pre_handler = mbd_handler_pre;
kp_mbd.symbol_name = "mark_buffer_dirty";
ret = register_kprobe(&kp_mbd);
if (ret < 0) goto unregister_mpa;
kp_apcl.pre_handler = apcl_handler_pre;
kp_apcl.symbol_name = "add_to_page_cache_lru";
ret = register_kprobe(&kp_apcl);
if (ret < 0) goto unregister_mbd;
kp_apd.pre_handler = apd_handler_pre;
kp_apd.symbol_name = "account_page_dirtied";
ret = register_kprobe(&kp_apd);
if (ret < 0) goto unregister_apcl;
proc_create("cache_stat", 0, NULL, &cache_stat_fops);
printk(KERN_INFO "Cache stat module loaded.\n");
return 0;
unregister_apcl:
unregister_kprobe(&kp_apcl);
unregister_mbd:
unregister_kprobe(&kp_mbd);
unregister_mpa:
unregister_kprobe(&kp_mpa);
fail:
printk(KERN_ERR "Failed to register kprobe\n");
return ret;
}
static void __exit cache_stat_exit(void) {
del_timer(&reseter);
remove_proc_entry("cache_stat", NULL);
unregister_kprobe(&kp_mpa);
unregister_kprobe(&kp_mbd);
unregister_kprobe(&kp_apcl);
unregister_kprobe(&kp_apd);
printk(KERN_INFO "Cache stat module unloaded.\n");
}
module_init(cache_stat_init);
module_exit(cache_stat_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("sprookie");
MODULE_DESCRIPTION("Cache stat module using atomic variables and kprobe");