Commit | Line | Data |
---|---|---|
1c8284eb MD |
1 | /* |
2 | * ltt/probes/jbd2-trace.c | |
3 | * | |
4 | * JBD2 tracepoint probes. | |
5 | * | |
6 | * (C) Copyright 2009 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> | |
7 | * Dual LGPL v2.1/GPL v2 license. | |
8 | */ | |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/debugfs.h> | |
12 | #include <linux/mutex.h> | |
13 | #include <linux/rcupdate.h> | |
14 | #include <trace/events/jbd2.h> | |
15 | ||
16 | #include "../ltt-tracer.h" | |
17 | ||
18 | static struct dentry *jbd2_filter_dentry, *jbd2_filter_dev_dentry; | |
19 | static DEFINE_MUTEX(jbd2_filter_mutex); | |
20 | /* Make sure we don't race between module exit and file write */ | |
21 | static int module_exits; | |
22 | ||
23 | struct rcu_dev_filter { | |
24 | struct rcu_head rcu; | |
25 | char devname[NAME_MAX]; | |
26 | }; | |
27 | ||
28 | static struct rcu_dev_filter *dev_filter; | |
29 | ||
30 | /* | |
31 | * Probes are executed in rcu_sched read-side critical section. | |
32 | */ | |
33 | static int do_filter(const char *dev) | |
34 | { | |
35 | struct rcu_dev_filter *ldev_filter = rcu_dereference(dev_filter); | |
36 | ||
37 | if (unlikely(ldev_filter)) | |
38 | if (unlikely(strcmp(ldev_filter->devname, dev))) | |
39 | return 0; | |
40 | return 1; | |
41 | } | |
42 | ||
43 | void probe_jbd2_checkpoint(void *data, journal_t *journal, int result) | |
44 | { | |
45 | if (unlikely(!do_filter(journal->j_devname))) | |
46 | return; | |
47 | trace_mark_tp(jbd2, checkpoint, jbd2_checkpoint, | |
48 | probe_jbd2_checkpoint, "dev %s need_checkpoint %d", | |
49 | journal->j_devname, result); | |
50 | } | |
51 | ||
52 | void probe_jbd2_start_commit(void *data, journal_t *journal, | |
53 | transaction_t *commit_transaction) | |
54 | { | |
55 | if (unlikely(!do_filter(journal->j_devname))) | |
56 | return; | |
57 | trace_mark_tp(jbd2, start_commit, jbd2_start_commit, | |
58 | probe_jbd2_start_commit, "dev %s transaction %d", | |
59 | journal->j_devname, commit_transaction->t_tid); | |
60 | } | |
61 | ||
62 | void probe_jbd2_end_commit(void *data, journal_t *journal, | |
63 | transaction_t *commit_transaction) | |
64 | { | |
65 | if (unlikely(!do_filter(journal->j_devname))) | |
66 | return; | |
67 | trace_mark_tp(jbd2, end_commit, jbd2_end_commit, | |
68 | probe_jbd2_end_commit, "dev %s transaction %d head %d", | |
69 | journal->j_devname, commit_transaction->t_tid, | |
70 | journal->j_tail_sequence); | |
71 | } | |
72 | ||
73 | static void free_dev_filter(struct rcu_head *head) | |
74 | { | |
75 | kfree(container_of(head, struct rcu_dev_filter, rcu)); | |
76 | } | |
77 | ||
78 | static ssize_t filter_op_write(struct file *file, | |
79 | const char __user *user_buf, size_t count, loff_t *ppos) | |
80 | { | |
81 | int err = 0; | |
82 | char buf[NAME_MAX]; | |
83 | int buf_size; | |
84 | char name[NAME_MAX]; | |
85 | struct rcu_dev_filter *new, *old; | |
86 | ||
87 | mutex_lock(&jbd2_filter_mutex); | |
88 | if (module_exits) { | |
89 | err = -EPERM; | |
90 | goto error; | |
91 | } | |
92 | buf_size = min(count, sizeof(buf) - 1); | |
93 | err = copy_from_user(buf, user_buf, buf_size); | |
94 | if (err) | |
95 | goto error; | |
96 | buf[buf_size] = 0; | |
97 | ||
98 | if (sscanf(buf, "%s", name) != 1) { | |
99 | err = -EPERM; | |
100 | goto error; | |
101 | } | |
102 | ||
103 | old = dev_filter; | |
104 | ||
105 | /* Empty string or * means all active */ | |
106 | if (name[0] == '\0' || (name[0] == '*' && name[1] == '\0')) { | |
107 | new = NULL; | |
108 | } else { | |
109 | new = kmalloc(sizeof(*new), GFP_KERNEL); | |
110 | strcpy(new->devname, name); | |
111 | } | |
112 | ||
113 | rcu_assign_pointer(dev_filter, new); | |
114 | if (old) | |
115 | call_rcu_sched(&old->rcu, free_dev_filter); | |
116 | ||
117 | mutex_unlock(&jbd2_filter_mutex); | |
118 | return count; | |
119 | ||
120 | error: | |
121 | mutex_unlock(&jbd2_filter_mutex); | |
122 | return err; | |
123 | } | |
124 | ||
125 | static ssize_t filter_op_read(struct file *filp, char __user *buffer, | |
126 | size_t count, loff_t *ppos) | |
127 | { | |
128 | ssize_t bcount; | |
129 | const char *devname; | |
130 | ||
131 | mutex_lock(&jbd2_filter_mutex); | |
132 | if (!dev_filter) | |
133 | devname = "*"; | |
134 | else | |
135 | devname = dev_filter->devname; | |
136 | bcount = simple_read_from_buffer(buffer, count, ppos, | |
137 | devname, strlen(devname)); | |
138 | mutex_unlock(&jbd2_filter_mutex); | |
139 | return bcount; | |
140 | } | |
141 | ||
142 | static struct file_operations jbd2_file_operations = { | |
143 | .write = filter_op_write, | |
144 | .read = filter_op_read, | |
145 | }; | |
146 | ||
147 | static void release_filter_dev(void) | |
148 | { | |
149 | struct rcu_dev_filter *old; | |
150 | ||
151 | mutex_lock(&jbd2_filter_mutex); | |
152 | module_exits = 1; | |
153 | old = dev_filter; | |
154 | rcu_assign_pointer(dev_filter, NULL); | |
155 | if (old) | |
156 | call_rcu_sched(&old->rcu, free_dev_filter); | |
157 | mutex_unlock(&jbd2_filter_mutex); | |
158 | } | |
159 | ||
160 | static int __init filter_init(void) | |
161 | { | |
162 | struct dentry *filter_root_dentry; | |
163 | int err = 0; | |
164 | ||
165 | filter_root_dentry = get_filter_root(); | |
166 | if (!filter_root_dentry) { | |
167 | err = -ENOENT; | |
168 | goto end; | |
169 | } | |
170 | ||
171 | jbd2_filter_dentry = debugfs_create_dir("jbd2", filter_root_dentry); | |
172 | ||
173 | if (IS_ERR(jbd2_filter_dentry) || !jbd2_filter_dentry) { | |
174 | printk(KERN_ERR "Failed to create jbd2 filter file\n"); | |
175 | err = -ENOMEM; | |
176 | goto end; | |
177 | } | |
178 | ||
179 | jbd2_filter_dev_dentry = debugfs_create_file("dev", S_IWUSR, | |
180 | jbd2_filter_dentry, NULL, &jbd2_file_operations); | |
181 | if (IS_ERR(jbd2_filter_dentry) || !jbd2_filter_dentry) { | |
182 | printk(KERN_ERR "Failed to create jbd2 filter file\n"); | |
183 | err = -ENOMEM; | |
184 | goto release_filter_dentry; | |
185 | } | |
186 | ||
187 | goto end; | |
188 | ||
189 | release_filter_dentry: | |
190 | debugfs_remove(jbd2_filter_dentry); | |
191 | release_filter_dev(); | |
192 | end: | |
193 | return err; | |
194 | } | |
195 | ||
196 | static void __exit filter_exit(void) | |
197 | { | |
198 | debugfs_remove(jbd2_filter_dev_dentry); | |
199 | debugfs_remove(jbd2_filter_dentry); | |
200 | release_filter_dev(); | |
201 | } | |
202 | ||
203 | module_init(filter_init); | |
204 | module_exit(filter_exit); | |
205 | ||
206 | MODULE_LICENSE("GPL and additional rights"); | |
207 | MODULE_AUTHOR("Mathieu Desnoyers"); | |
208 | MODULE_DESCRIPTION("JBD2 Tracepoint Probes"); |