]>
Commit | Line | Data |
---|---|---|
346e15be JB |
1 | /* |
2 | * lib/dynamic_printk.c | |
3 | * | |
4 | * make pr_debug()/dev_dbg() calls runtime configurable based upon their | |
5 | * their source module. | |
6 | * | |
7 | * Copyright (C) 2008 Red Hat, Inc., Jason Baron <jbaron@redhat.com> | |
8 | */ | |
9 | ||
10 | #include <linux/kernel.h> | |
11 | #include <linux/module.h> | |
12 | #include <linux/uaccess.h> | |
13 | #include <linux/seq_file.h> | |
14 | #include <linux/debugfs.h> | |
15 | #include <linux/fs.h> | |
16 | ||
17 | extern struct mod_debug __start___verbose[]; | |
18 | extern struct mod_debug __stop___verbose[]; | |
19 | ||
20 | struct debug_name { | |
21 | struct hlist_node hlist; | |
22 | struct hlist_node hlist2; | |
23 | int hash1; | |
24 | int hash2; | |
25 | char *name; | |
26 | int enable; | |
27 | int type; | |
28 | }; | |
29 | ||
30 | static int nr_entries; | |
31 | static int num_enabled; | |
32 | int dynamic_enabled = DYNAMIC_ENABLED_NONE; | |
33 | static struct hlist_head module_table[DEBUG_HASH_TABLE_SIZE] = | |
34 | { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT }; | |
35 | static struct hlist_head module_table2[DEBUG_HASH_TABLE_SIZE] = | |
36 | { [0 ... DEBUG_HASH_TABLE_SIZE-1] = HLIST_HEAD_INIT }; | |
37 | static DECLARE_MUTEX(debug_list_mutex); | |
38 | ||
39 | /* dynamic_printk_enabled, and dynamic_printk_enabled2 are bitmasks in which | |
40 | * bit n is set to 1 if any modname hashes into the bucket n, 0 otherwise. They | |
41 | * use independent hash functions, to reduce the chance of false positives. | |
42 | */ | |
43 | long long dynamic_printk_enabled; | |
44 | EXPORT_SYMBOL_GPL(dynamic_printk_enabled); | |
45 | long long dynamic_printk_enabled2; | |
46 | EXPORT_SYMBOL_GPL(dynamic_printk_enabled2); | |
47 | ||
48 | /* returns the debug module pointer. */ | |
49 | static struct debug_name *find_debug_module(char *module_name) | |
50 | { | |
51 | int i; | |
52 | struct hlist_head *head; | |
53 | struct hlist_node *node; | |
54 | struct debug_name *element; | |
55 | ||
56 | element = NULL; | |
57 | for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) { | |
58 | head = &module_table[i]; | |
59 | hlist_for_each_entry_rcu(element, node, head, hlist) | |
60 | if (!strcmp(element->name, module_name)) | |
61 | return element; | |
62 | } | |
63 | return NULL; | |
64 | } | |
65 | ||
66 | /* returns the debug module pointer. */ | |
67 | static struct debug_name *find_debug_module_hash(char *module_name, int hash) | |
68 | { | |
69 | struct hlist_head *head; | |
70 | struct hlist_node *node; | |
71 | struct debug_name *element; | |
72 | ||
73 | element = NULL; | |
74 | head = &module_table[hash]; | |
75 | hlist_for_each_entry_rcu(element, node, head, hlist) | |
76 | if (!strcmp(element->name, module_name)) | |
77 | return element; | |
78 | return NULL; | |
79 | } | |
80 | ||
81 | /* caller must hold mutex*/ | |
82 | static int __add_debug_module(char *mod_name, int hash, int hash2) | |
83 | { | |
84 | struct debug_name *new; | |
85 | char *module_name; | |
86 | int ret = 0; | |
87 | ||
88 | if (find_debug_module(mod_name)) { | |
89 | ret = -EINVAL; | |
90 | goto out; | |
91 | } | |
92 | module_name = kmalloc(strlen(mod_name) + 1, GFP_KERNEL); | |
93 | if (!module_name) { | |
94 | ret = -ENOMEM; | |
95 | goto out; | |
96 | } | |
97 | module_name = strcpy(module_name, mod_name); | |
98 | module_name[strlen(mod_name)] = '\0'; | |
99 | new = kzalloc(sizeof(struct debug_name), GFP_KERNEL); | |
100 | if (!new) { | |
101 | kfree(module_name); | |
102 | ret = -ENOMEM; | |
103 | goto out; | |
104 | } | |
105 | INIT_HLIST_NODE(&new->hlist); | |
106 | INIT_HLIST_NODE(&new->hlist2); | |
107 | new->name = module_name; | |
108 | new->hash1 = hash; | |
109 | new->hash2 = hash2; | |
110 | hlist_add_head_rcu(&new->hlist, &module_table[hash]); | |
111 | hlist_add_head_rcu(&new->hlist2, &module_table2[hash2]); | |
112 | nr_entries++; | |
113 | out: | |
114 | return ret; | |
115 | } | |
116 | ||
117 | int unregister_dynamic_debug_module(char *mod_name) | |
118 | { | |
119 | struct debug_name *element; | |
120 | int ret = 0; | |
121 | ||
122 | down(&debug_list_mutex); | |
123 | element = find_debug_module(mod_name); | |
124 | if (!element) { | |
125 | ret = -EINVAL; | |
126 | goto out; | |
127 | } | |
128 | hlist_del_rcu(&element->hlist); | |
129 | hlist_del_rcu(&element->hlist2); | |
130 | synchronize_rcu(); | |
131 | kfree(element->name); | |
132 | if (element->enable) | |
133 | num_enabled--; | |
134 | kfree(element); | |
135 | nr_entries--; | |
136 | out: | |
137 | up(&debug_list_mutex); | |
1c93ca09 | 138 | return ret; |
346e15be JB |
139 | } |
140 | EXPORT_SYMBOL_GPL(unregister_dynamic_debug_module); | |
141 | ||
142 | int register_dynamic_debug_module(char *mod_name, int type, char *share_name, | |
143 | char *flags, int hash, int hash2) | |
144 | { | |
145 | struct debug_name *elem; | |
146 | int ret = 0; | |
147 | ||
148 | down(&debug_list_mutex); | |
149 | elem = find_debug_module(mod_name); | |
150 | if (!elem) { | |
151 | if (__add_debug_module(mod_name, hash, hash2)) | |
152 | goto out; | |
153 | elem = find_debug_module(mod_name); | |
154 | if (dynamic_enabled == DYNAMIC_ENABLED_ALL && | |
155 | !strcmp(mod_name, share_name)) { | |
156 | elem->enable = true; | |
157 | num_enabled++; | |
158 | } | |
159 | } | |
160 | elem->type |= type; | |
161 | out: | |
162 | up(&debug_list_mutex); | |
163 | return ret; | |
164 | } | |
165 | EXPORT_SYMBOL_GPL(register_dynamic_debug_module); | |
166 | ||
167 | int __dynamic_dbg_enabled_helper(char *mod_name, int type, int value, int hash) | |
168 | { | |
169 | struct debug_name *elem; | |
170 | int ret = 0; | |
171 | ||
172 | if (dynamic_enabled == DYNAMIC_ENABLED_ALL) | |
173 | return 1; | |
174 | rcu_read_lock(); | |
175 | elem = find_debug_module_hash(mod_name, hash); | |
176 | if (elem && elem->enable) | |
177 | ret = 1; | |
178 | rcu_read_unlock(); | |
179 | return ret; | |
180 | } | |
181 | EXPORT_SYMBOL_GPL(__dynamic_dbg_enabled_helper); | |
182 | ||
183 | static void set_all(bool enable) | |
184 | { | |
185 | struct debug_name *e; | |
186 | struct hlist_node *node; | |
187 | int i; | |
188 | long long enable_mask; | |
189 | ||
190 | for (i = 0; i < DEBUG_HASH_TABLE_SIZE; i++) { | |
191 | if (module_table[i].first != NULL) { | |
192 | hlist_for_each_entry(e, node, &module_table[i], hlist) { | |
193 | e->enable = enable; | |
194 | } | |
195 | } | |
196 | } | |
197 | if (enable) | |
198 | enable_mask = ULLONG_MAX; | |
199 | else | |
200 | enable_mask = 0; | |
201 | dynamic_printk_enabled = enable_mask; | |
202 | dynamic_printk_enabled2 = enable_mask; | |
203 | } | |
204 | ||
205 | static int disabled_hash(int i, bool first_table) | |
206 | { | |
207 | struct debug_name *e; | |
208 | struct hlist_node *node; | |
209 | ||
210 | if (first_table) { | |
211 | hlist_for_each_entry(e, node, &module_table[i], hlist) { | |
212 | if (e->enable) | |
213 | return 0; | |
214 | } | |
215 | } else { | |
216 | hlist_for_each_entry(e, node, &module_table2[i], hlist2) { | |
217 | if (e->enable) | |
218 | return 0; | |
219 | } | |
220 | } | |
221 | return 1; | |
222 | } | |
223 | ||
224 | static ssize_t pr_debug_write(struct file *file, const char __user *buf, | |
225 | size_t length, loff_t *ppos) | |
226 | { | |
227 | char *buffer, *s, *value_str, *setting_str; | |
228 | int err, value; | |
229 | struct debug_name *elem = NULL; | |
230 | int all = 0; | |
231 | ||
232 | if (length > PAGE_SIZE || length < 0) | |
233 | return -EINVAL; | |
234 | ||
235 | buffer = (char *)__get_free_page(GFP_KERNEL); | |
236 | if (!buffer) | |
237 | return -ENOMEM; | |
238 | ||
239 | err = -EFAULT; | |
240 | if (copy_from_user(buffer, buf, length)) | |
241 | goto out; | |
242 | ||
243 | err = -EINVAL; | |
244 | if (length < PAGE_SIZE) | |
245 | buffer[length] = '\0'; | |
246 | else if (buffer[PAGE_SIZE-1]) | |
247 | goto out; | |
248 | ||
249 | err = -EINVAL; | |
250 | down(&debug_list_mutex); | |
251 | ||
252 | if (strncmp("set", buffer, 3)) | |
253 | goto out_up; | |
254 | s = buffer + 3; | |
255 | setting_str = strsep(&s, "="); | |
256 | if (s == NULL) | |
257 | goto out_up; | |
258 | setting_str = strstrip(setting_str); | |
259 | value_str = strsep(&s, " "); | |
260 | if (s == NULL) | |
261 | goto out_up; | |
262 | s = strstrip(s); | |
263 | if (!strncmp(s, "all", 3)) | |
264 | all = 1; | |
265 | else | |
266 | elem = find_debug_module(s); | |
267 | if (!strncmp(setting_str, "enable", 6)) { | |
268 | value = !!simple_strtol(value_str, NULL, 10); | |
269 | if (all) { | |
270 | if (value) { | |
271 | set_all(true); | |
272 | num_enabled = nr_entries; | |
273 | dynamic_enabled = DYNAMIC_ENABLED_ALL; | |
274 | } else { | |
275 | set_all(false); | |
276 | num_enabled = 0; | |
277 | dynamic_enabled = DYNAMIC_ENABLED_NONE; | |
278 | } | |
279 | err = 0; | |
280 | } else { | |
281 | if (elem) { | |
282 | if (value && (elem->enable == 0)) { | |
283 | dynamic_printk_enabled |= | |
284 | (1LL << elem->hash1); | |
285 | dynamic_printk_enabled2 |= | |
286 | (1LL << elem->hash2); | |
287 | elem->enable = 1; | |
288 | num_enabled++; | |
289 | dynamic_enabled = DYNAMIC_ENABLED_SOME; | |
290 | err = 0; | |
291 | printk(KERN_DEBUG | |
aa6f3c64 | 292 | "debugging enabled for module %s\n", |
346e15be JB |
293 | elem->name); |
294 | } else if (!value && (elem->enable == 1)) { | |
295 | elem->enable = 0; | |
296 | num_enabled--; | |
297 | if (disabled_hash(elem->hash1, true)) | |
298 | dynamic_printk_enabled &= | |
299 | ~(1LL << elem->hash1); | |
300 | if (disabled_hash(elem->hash2, false)) | |
301 | dynamic_printk_enabled2 &= | |
302 | ~(1LL << elem->hash2); | |
303 | if (num_enabled) | |
304 | dynamic_enabled = | |
305 | DYNAMIC_ENABLED_SOME; | |
306 | else | |
307 | dynamic_enabled = | |
308 | DYNAMIC_ENABLED_NONE; | |
309 | err = 0; | |
310 | printk(KERN_DEBUG | |
311 | "debugging disabled for module " | |
aa6f3c64 | 312 | "%s\n", elem->name); |
346e15be JB |
313 | } |
314 | } | |
315 | } | |
316 | } | |
317 | if (!err) | |
318 | err = length; | |
319 | out_up: | |
320 | up(&debug_list_mutex); | |
321 | out: | |
322 | free_page((unsigned long)buffer); | |
323 | return err; | |
324 | } | |
325 | ||
326 | static void *pr_debug_seq_start(struct seq_file *f, loff_t *pos) | |
327 | { | |
328 | return (*pos < DEBUG_HASH_TABLE_SIZE) ? pos : NULL; | |
329 | } | |
330 | ||
331 | static void *pr_debug_seq_next(struct seq_file *s, void *v, loff_t *pos) | |
332 | { | |
333 | (*pos)++; | |
334 | if (*pos >= DEBUG_HASH_TABLE_SIZE) | |
335 | return NULL; | |
336 | return pos; | |
337 | } | |
338 | ||
339 | static void pr_debug_seq_stop(struct seq_file *s, void *v) | |
340 | { | |
341 | /* Nothing to do */ | |
342 | } | |
343 | ||
344 | static int pr_debug_seq_show(struct seq_file *s, void *v) | |
345 | { | |
346 | struct hlist_head *head; | |
347 | struct hlist_node *node; | |
348 | struct debug_name *elem; | |
349 | unsigned int i = *(loff_t *) v; | |
350 | ||
351 | rcu_read_lock(); | |
352 | head = &module_table[i]; | |
353 | hlist_for_each_entry_rcu(elem, node, head, hlist) { | |
354 | seq_printf(s, "%s enabled=%d", elem->name, elem->enable); | |
355 | seq_printf(s, "\n"); | |
356 | } | |
357 | rcu_read_unlock(); | |
358 | return 0; | |
359 | } | |
360 | ||
361 | static struct seq_operations pr_debug_seq_ops = { | |
362 | .start = pr_debug_seq_start, | |
363 | .next = pr_debug_seq_next, | |
364 | .stop = pr_debug_seq_stop, | |
365 | .show = pr_debug_seq_show | |
366 | }; | |
367 | ||
368 | static int pr_debug_open(struct inode *inode, struct file *filp) | |
369 | { | |
370 | return seq_open(filp, &pr_debug_seq_ops); | |
371 | } | |
372 | ||
373 | static const struct file_operations pr_debug_operations = { | |
374 | .open = pr_debug_open, | |
375 | .read = seq_read, | |
376 | .write = pr_debug_write, | |
377 | .llseek = seq_lseek, | |
378 | .release = seq_release, | |
379 | }; | |
380 | ||
381 | static int __init dynamic_printk_init(void) | |
382 | { | |
383 | struct dentry *dir, *file; | |
384 | struct mod_debug *iter; | |
385 | unsigned long value; | |
386 | ||
387 | dir = debugfs_create_dir("dynamic_printk", NULL); | |
388 | if (!dir) | |
389 | return -ENOMEM; | |
390 | file = debugfs_create_file("modules", 0644, dir, NULL, | |
391 | &pr_debug_operations); | |
392 | if (!file) { | |
393 | debugfs_remove(dir); | |
394 | return -ENOMEM; | |
395 | } | |
396 | for (value = (unsigned long)__start___verbose; | |
397 | value < (unsigned long)__stop___verbose; | |
398 | value += sizeof(struct mod_debug)) { | |
399 | iter = (struct mod_debug *)value; | |
400 | register_dynamic_debug_module(iter->modname, | |
401 | iter->type, | |
402 | iter->logical_modname, | |
403 | iter->flag_names, iter->hash, iter->hash2); | |
404 | } | |
11332830 JB |
405 | if (dynamic_enabled == DYNAMIC_ENABLED_ALL) |
406 | set_all(true); | |
346e15be JB |
407 | return 0; |
408 | } | |
409 | module_init(dynamic_printk_init); | |
410 | /* may want to move this earlier so we can get traces as early as possible */ | |
411 | ||
412 | static int __init dynamic_printk_setup(char *str) | |
413 | { | |
414 | if (str) | |
415 | return -ENOENT; | |
11332830 | 416 | dynamic_enabled = DYNAMIC_ENABLED_ALL; |
346e15be JB |
417 | return 0; |
418 | } | |
419 | /* Use early_param(), so we can get debug output as early as possible */ | |
420 | early_param("dynamic_printk", dynamic_printk_setup); |