diff options
author | Pekka Paalanen <pq@iki.fi> | 2008-05-12 15:20:57 -0400 |
---|---|---|
committer | Thomas Gleixner <tglx@linutronix.de> | 2008-05-24 05:22:12 -0400 |
commit | 0fd0e3da4557c479b820b9a4a7afa25b4637ddf2 (patch) | |
tree | 5f34b3673202303f394c6dd180a15751f50014e9 /arch/x86/mm/fault.c | |
parent | f513638030ca384b0bace4df64f0b82f6ae1e4c6 (diff) |
x86: mmiotrace full patch, preview 1
kmmio.c handles the list of mmio probes with callbacks, list of traced
pages, and attaching into the page fault handler and die notifier. It
arms, traps and disarms the given pages, this is the core of mmiotrace.
mmio-mod.c is a user interface, hooking into ioremap functions and
registering the mmio probes. It also decodes the required information
from trapped mmio accesses via the pre and post callbacks in each probe.
Currently, hooking into ioremap functions works by redefining the symbols
of the target (binary) kernel module, so that it calls the traced
versions of the functions.
The most notable changes done since the last discussion are:
- kmmio.c is a built-in, not part of the module
- direct call from fault.c to kmmio.c, removing all dynamic hooks
- prepare for unregistering probes at any time
- make kmmio re-initializable and accessible to more than one user
- rewrite kmmio locking to remove all spinlocks from page fault path
Can I abuse call_rcu() like I do in kmmio.c:unregister_kmmio_probe()
or is there a better way?
The function called via call_rcu() itself calls call_rcu() again,
will this work or break? There I need a second grace period for RCU
after the first grace period for page faults.
Mmiotrace itself (mmio-mod.c) is still a module, I am going to attack
that next. At some point I will start looking into how to make mmiotrace
a tracer component of ftrace (thanks for the hint, Ingo). Ftrace should
make the user space part of mmiotracing as simple as
'cat /debug/trace/mmio > dump.txt'.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch/x86/mm/fault.c')
-rw-r--r-- | arch/x86/mm/fault.c | 59 |
1 files changed, 7 insertions, 52 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index e9a086a1a9ff..8c828a68d3b6 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/string.h> | 10 | #include <linux/string.h> |
11 | #include <linux/types.h> | 11 | #include <linux/types.h> |
12 | #include <linux/ptrace.h> | 12 | #include <linux/ptrace.h> |
13 | #include <linux/mmiotrace.h> | ||
13 | #include <linux/mman.h> | 14 | #include <linux/mman.h> |
14 | #include <linux/mm.h> | 15 | #include <linux/mm.h> |
15 | #include <linux/smp.h> | 16 | #include <linux/smp.h> |
@@ -49,60 +50,14 @@ | |||
49 | #define PF_RSVD (1<<3) | 50 | #define PF_RSVD (1<<3) |
50 | #define PF_INSTR (1<<4) | 51 | #define PF_INSTR (1<<4) |
51 | 52 | ||
52 | #ifdef CONFIG_MMIOTRACE_HOOKS | 53 | static inline int kmmio_fault(struct pt_regs *regs, unsigned long addr) |
53 | static pf_handler_func mmiotrace_pf_handler; /* protected by RCU */ | ||
54 | static DEFINE_SPINLOCK(mmiotrace_handler_lock); | ||
55 | |||
56 | int mmiotrace_register_pf(pf_handler_func new_pfh) | ||
57 | { | ||
58 | int ret = 0; | ||
59 | unsigned long flags; | ||
60 | spin_lock_irqsave(&mmiotrace_handler_lock, flags); | ||
61 | if (mmiotrace_pf_handler) | ||
62 | ret = -EBUSY; | ||
63 | else | ||
64 | mmiotrace_pf_handler = new_pfh; | ||
65 | spin_unlock_irqrestore(&mmiotrace_handler_lock, flags); | ||
66 | return ret; | ||
67 | } | ||
68 | EXPORT_SYMBOL_GPL(mmiotrace_register_pf); | ||
69 | |||
70 | /** | ||
71 | * mmiotrace_unregister_pf: | ||
72 | * The caller must ensure @old_pfh is not in use anymore before freeing it. | ||
73 | * This function does not guarantee it. The handler function pointer is | ||
74 | * protected by RCU, so you can do this by e.g. calling synchronize_rcu(). | ||
75 | */ | ||
76 | int mmiotrace_unregister_pf(pf_handler_func old_pfh) | ||
77 | { | ||
78 | int ret = 0; | ||
79 | unsigned long flags; | ||
80 | spin_lock_irqsave(&mmiotrace_handler_lock, flags); | ||
81 | if (mmiotrace_pf_handler != old_pfh) | ||
82 | ret = -EPERM; | ||
83 | else | ||
84 | mmiotrace_pf_handler = NULL; | ||
85 | spin_unlock_irqrestore(&mmiotrace_handler_lock, flags); | ||
86 | return ret; | ||
87 | } | ||
88 | EXPORT_SYMBOL_GPL(mmiotrace_unregister_pf); | ||
89 | #endif /* CONFIG_MMIOTRACE_HOOKS */ | ||
90 | |||
91 | /* returns non-zero if do_page_fault() should return */ | ||
92 | static inline int call_mmiotrace(struct pt_regs *regs, | ||
93 | unsigned long error_code, | ||
94 | unsigned long address) | ||
95 | { | 54 | { |
96 | #ifdef CONFIG_MMIOTRACE_HOOKS | 55 | #ifdef CONFIG_MMIOTRACE_HOOKS |
97 | int ret = 0; | 56 | if (unlikely(is_kmmio_active())) |
98 | rcu_read_lock(); | 57 | if (kmmio_handler(regs, addr) == 1) |
99 | if (mmiotrace_pf_handler) | 58 | return -1; |
100 | ret = mmiotrace_pf_handler(regs, error_code, address); | ||
101 | rcu_read_unlock(); | ||
102 | return ret; | ||
103 | #else | ||
104 | return 0; | ||
105 | #endif | 59 | #endif |
60 | return 0; | ||
106 | } | 61 | } |
107 | 62 | ||
108 | static inline int notify_page_fault(struct pt_regs *regs) | 63 | static inline int notify_page_fault(struct pt_regs *regs) |
@@ -657,7 +612,7 @@ void __kprobes do_page_fault(struct pt_regs *regs, unsigned long error_code) | |||
657 | 612 | ||
658 | if (notify_page_fault(regs)) | 613 | if (notify_page_fault(regs)) |
659 | return; | 614 | return; |
660 | if (call_mmiotrace(regs, error_code, address)) | 615 | if (unlikely(kmmio_fault(regs, address))) |
661 | return; | 616 | return; |
662 | 617 | ||
663 | /* | 618 | /* |