-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathtimerhook.c
74 lines (59 loc) · 2.53 KB
/
timerhook.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
#include "timerhook.h"
#include <linux/atomic.h>
#include <linux/netdevice.h>
#include <trace/events/timer.h>
#include "tracewrapper.h"
/*
This file provides the functionality allows putting a spinlock around timer functions in a given module.
It is used to ensure that no timer registered by the network driver is running on another CPU when kgdb
is stopping all CPUs except the one with an exception.
Some network drivers (like pcnet32) define timers that can take internal spinlocks required to access the device.
If a core hits an exception while another core is executing such a timer (and owns the spinlock), kgdb will deadlock
as it won't be able to access the network card. Putting a spinlock around those timers and taking it before
disabling other cores solves this problem.
Note that this code relies on assumption that the timer function is defined in the same module that registered
the network device. If this assumption is broken, this code won't be able to catch the timer!
*/
static atomic_t timer_hook_installed;
static notrace void hook_timer_entry(void *v, struct timer_list *timer)
{
if (within_module_core((unsigned long)timer->function, ((struct timer_hook *)v)->module))
spin_lock(&((struct timer_hook *)v)->lock);
}
static notrace void hook_timer_exit(void *v, struct timer_list *timer)
{
if (within_module_core((unsigned long)timer->function, ((struct timer_hook *)v)->module))
spin_unlock(&((struct timer_hook *)v)->lock);
}
struct timer_hook *timerhook_create(struct module *moduleToHook)
{
struct timer_hook *hook;
BUG_ON(!moduleToHook);
if (!tracepoint_available(timer_expire_entry) || !tracepoint_available(timer_expire_exit))
{
printk(KERN_ERR "kgdboe: Missing tracepoints for timer_expire_entry/timer_expire_exit. Aborting.\n");
return NULL;
}
hook = (struct timer_hook *)kmalloc(sizeof(struct timer_hook), GFP_KERNEL);
if (!hook)
return NULL;
spin_lock_init(&hook->lock);
hook->module = moduleToHook;
if (atomic_inc_return(&timer_hook_installed) == 1)
{
register_tracepoint_wrapper(timer_expire_entry, hook_timer_entry, hook);
register_tracepoint_wrapper(timer_expire_exit, hook_timer_exit, hook);
}
return hook;
}
void timerhook_free(struct timer_hook *hook)
{
if (!hook)
return;
if (!atomic_dec_return(&timer_hook_installed))
{
unregister_tracepoint_wrapper(timer_expire_entry, hook_timer_entry, hook);
unregister_tracepoint_wrapper(timer_expire_exit, hook_timer_exit, hook);
}
kfree(hook);
}