aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/x86/kernel/mmiotrace/kmmio.c186
-rw-r--r--arch/x86/kernel/mmiotrace/mmio-mod.c3
-rw-r--r--include/linux/mmiotrace.h2
-rw-r--r--kernel/trace/trace_mmiotrace.c47
4 files changed, 120 insertions, 118 deletions
diff --git a/arch/x86/kernel/mmiotrace/kmmio.c b/arch/x86/kernel/mmiotrace/kmmio.c
index efb467933087..cd0d95fe4fe6 100644
--- a/arch/x86/kernel/mmiotrace/kmmio.c
+++ b/arch/x86/kernel/mmiotrace/kmmio.c
@@ -5,15 +5,12 @@
5 * 2008 Pekka Paalanen <pq@iki.fi> 5 * 2008 Pekka Paalanen <pq@iki.fi>
6 */ 6 */
7 7
8#include <linux/version.h>
9#include <linux/list.h> 8#include <linux/list.h>
10#include <linux/spinlock.h> 9#include <linux/spinlock.h>
11#include <linux/hash.h> 10#include <linux/hash.h>
12#include <linux/init.h> 11#include <linux/init.h>
13#include <linux/module.h> 12#include <linux/module.h>
14#include <linux/slab.h>
15#include <linux/kernel.h> 13#include <linux/kernel.h>
16#include <linux/mm.h>
17#include <linux/uaccess.h> 14#include <linux/uaccess.h>
18#include <linux/ptrace.h> 15#include <linux/ptrace.h>
19#include <linux/preempt.h> 16#include <linux/preempt.h>
@@ -22,10 +19,9 @@
22#include <linux/mutex.h> 19#include <linux/mutex.h>
23#include <asm/io.h> 20#include <asm/io.h>
24#include <asm/cacheflush.h> 21#include <asm/cacheflush.h>
25#include <asm/errno.h>
26#include <asm/tlbflush.h> 22#include <asm/tlbflush.h>
27#include <asm/pgtable.h> 23#include <asm/errno.h>
28 24#include <asm/debugreg.h>
29#include <linux/mmiotrace.h> 25#include <linux/mmiotrace.h>
30 26
31#define KMMIO_PAGE_HASH_BITS 4 27#define KMMIO_PAGE_HASH_BITS 4
@@ -57,14 +53,9 @@ struct kmmio_context {
57 int active; 53 int active;
58}; 54};
59 55
60static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val,
61 void *args);
62
63static DEFINE_MUTEX(kmmio_init_mutex);
64static DEFINE_SPINLOCK(kmmio_lock); 56static DEFINE_SPINLOCK(kmmio_lock);
65 57
66/* These are protected by kmmio_lock */ 58/* Protected by kmmio_lock */
67static int kmmio_initialized;
68unsigned int kmmio_count; 59unsigned int kmmio_count;
69 60
70/* Read-protected by RCU, write-protected by kmmio_lock. */ 61/* Read-protected by RCU, write-protected by kmmio_lock. */
@@ -79,60 +70,6 @@ static struct list_head *kmmio_page_list(unsigned long page)
79/* Accessed per-cpu */ 70/* Accessed per-cpu */
80static DEFINE_PER_CPU(struct kmmio_context, kmmio_ctx); 71static DEFINE_PER_CPU(struct kmmio_context, kmmio_ctx);
81 72
82/* protected by kmmio_init_mutex */
83static struct notifier_block nb_die = {
84 .notifier_call = kmmio_die_notifier
85};
86
87/**
88 * Makes sure kmmio is initialized and usable.
89 * This must be called before any other kmmio function defined here.
90 * May sleep.
91 */
92void reference_kmmio(void)
93{
94 mutex_lock(&kmmio_init_mutex);
95 spin_lock_irq(&kmmio_lock);
96 if (!kmmio_initialized) {
97 int i;
98 for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++)
99 INIT_LIST_HEAD(&kmmio_page_table[i]);
100 if (register_die_notifier(&nb_die))
101 BUG();
102 }
103 kmmio_initialized++;
104 spin_unlock_irq(&kmmio_lock);
105 mutex_unlock(&kmmio_init_mutex);
106}
107EXPORT_SYMBOL_GPL(reference_kmmio);
108
109/**
110 * Clean up kmmio after use. This must be called for every call to
111 * reference_kmmio(). All probes registered after the corresponding
112 * reference_kmmio() must have been unregistered when calling this.
113 * May sleep.
114 */
115void unreference_kmmio(void)
116{
117 bool unreg = false;
118
119 mutex_lock(&kmmio_init_mutex);
120 spin_lock_irq(&kmmio_lock);
121
122 if (kmmio_initialized == 1) {
123 BUG_ON(is_kmmio_active());
124 unreg = true;
125 }
126 kmmio_initialized--;
127 BUG_ON(kmmio_initialized < 0);
128 spin_unlock_irq(&kmmio_lock);
129
130 if (unreg)
131 unregister_die_notifier(&nb_die); /* calls sync_rcu() */
132 mutex_unlock(&kmmio_init_mutex);
133}
134EXPORT_SYMBOL(unreference_kmmio);
135
136/* 73/*
137 * this is basically a dynamic stabbing problem: 74 * this is basically a dynamic stabbing problem:
138 * Could use the existing prio tree code or 75 * Could use the existing prio tree code or
@@ -167,58 +104,56 @@ static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page)
167 return NULL; 104 return NULL;
168} 105}
169 106
170/** Mark the given page as not present. Access to it will trigger a fault. */ 107static void set_page_present(unsigned long addr, bool present, int *pglevel)
171static void arm_kmmio_fault_page(unsigned long page, int *page_level)
172{ 108{
173 unsigned long address = page & PAGE_MASK; 109 pteval_t pteval;
110 pmdval_t pmdval;
174 int level; 111 int level;
175 pte_t *pte = lookup_address(address, &level); 112 pmd_t *pmd;
113 pte_t *pte = lookup_address(addr, &level);
176 114
177 if (!pte) { 115 if (!pte) {
178 pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n", 116 pr_err("kmmio: no pte for page 0x%08lx\n", addr);
179 __func__, page);
180 return; 117 return;
181 } 118 }
182 119
183 if (level == PG_LEVEL_2M) { 120 if (pglevel)
184 pmd_t *pmd = (pmd_t *)pte; 121 *pglevel = level;
185 set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_PRESENT)); 122
186 } else { 123 switch (level) {
187 /* PG_LEVEL_4K */ 124 case PG_LEVEL_2M:
188 set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT)); 125 pmd = (pmd_t *)pte;
126 pmdval = pmd_val(*pmd) & ~_PAGE_PRESENT;
127 if (present)
128 pmdval |= _PAGE_PRESENT;
129 set_pmd(pmd, __pmd(pmdval));
130 break;
131
132 case PG_LEVEL_4K:
133 pteval = pte_val(*pte) & ~_PAGE_PRESENT;
134 if (present)
135 pteval |= _PAGE_PRESENT;
136 set_pte_atomic(pte, __pte(pteval));
137 break;
138
139 default:
140 pr_err("kmmio: unexpected page level 0x%x.\n", level);
141 return;
189 } 142 }
190 143
191 if (page_level) 144 __flush_tlb_one(addr);
192 *page_level = level; 145}
193 146
194 __flush_tlb_one(page); 147/** Mark the given page as not present. Access to it will trigger a fault. */
148static void arm_kmmio_fault_page(unsigned long page, int *page_level)
149{
150 set_page_present(page & PAGE_MASK, false, page_level);
195} 151}
196 152
197/** Mark the given page as present. */ 153/** Mark the given page as present. */
198static void disarm_kmmio_fault_page(unsigned long page, int *page_level) 154static void disarm_kmmio_fault_page(unsigned long page, int *page_level)
199{ 155{
200 unsigned long address = page & PAGE_MASK; 156 set_page_present(page & PAGE_MASK, true, page_level);
201 int level;
202 pte_t *pte = lookup_address(address, &level);
203
204 if (!pte) {
205 pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n",
206 __func__, page);
207 return;
208 }
209
210 if (level == PG_LEVEL_2M) {
211 pmd_t *pmd = (pmd_t *)pte;
212 set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_PRESENT));
213 } else {
214 /* PG_LEVEL_4K */
215 set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT));
216 }
217
218 if (page_level)
219 *page_level = level;
220
221 __flush_tlb_one(page);
222} 157}
223 158
224/* 159/*
@@ -240,6 +175,7 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
240{ 175{
241 struct kmmio_context *ctx; 176 struct kmmio_context *ctx;
242 struct kmmio_fault_page *faultpage; 177 struct kmmio_fault_page *faultpage;
178 int ret = 0; /* default to fault not handled */
243 179
244 /* 180 /*
245 * Preemption is now disabled to prevent process switch during 181 * Preemption is now disabled to prevent process switch during
@@ -257,21 +193,35 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
257 /* 193 /*
258 * Either this page fault is not caused by kmmio, or 194 * Either this page fault is not caused by kmmio, or
259 * another CPU just pulled the kmmio probe from under 195 * another CPU just pulled the kmmio probe from under
260 * our feet. In the latter case all hell breaks loose. 196 * our feet. The latter case should not be possible.
261 */ 197 */
262 goto no_kmmio; 198 goto no_kmmio;
263 } 199 }
264 200
265 ctx = &get_cpu_var(kmmio_ctx); 201 ctx = &get_cpu_var(kmmio_ctx);
266 if (ctx->active) { 202 if (ctx->active) {
203 disarm_kmmio_fault_page(faultpage->page, NULL);
204 if (addr == ctx->addr) {
205 /*
206 * On SMP we sometimes get recursive probe hits on the
207 * same address. Context is already saved, fall out.
208 */
209 pr_debug("kmmio: duplicate probe hit on CPU %d, for "
210 "address 0x%08lx.\n",
211 smp_processor_id(), addr);
212 ret = 1;
213 goto no_kmmio_ctx;
214 }
267 /* 215 /*
268 * Prevent overwriting already in-flight context. 216 * Prevent overwriting already in-flight context.
269 * If this page fault really was due to kmmio trap, 217 * This should not happen, let's hope disarming at least
270 * all hell breaks loose. 218 * prevents a panic.
271 */ 219 */
272 pr_emerg("kmmio: recursive probe hit on CPU %d, " 220 pr_emerg("kmmio: recursive probe hit on CPU %d, "
273 "for address 0x%08lx. Ignoring.\n", 221 "for address 0x%08lx. Ignoring.\n",
274 smp_processor_id(), addr); 222 smp_processor_id(), addr);
223 pr_emerg("kmmio: previous hit was at 0x%08lx.\n",
224 ctx->addr);
275 goto no_kmmio_ctx; 225 goto no_kmmio_ctx;
276 } 226 }
277 ctx->active++; 227 ctx->active++;
@@ -302,14 +252,14 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
302 */ 252 */
303 253
304 put_cpu_var(kmmio_ctx); 254 put_cpu_var(kmmio_ctx);
305 return 1; 255 return 1; /* fault handled */
306 256
307no_kmmio_ctx: 257no_kmmio_ctx:
308 put_cpu_var(kmmio_ctx); 258 put_cpu_var(kmmio_ctx);
309no_kmmio: 259no_kmmio:
310 rcu_read_unlock(); 260 rcu_read_unlock();
311 preempt_enable_no_resched(); 261 preempt_enable_no_resched();
312 return 0; /* page fault not handled by kmmio */ 262 return ret;
313} 263}
314 264
315/* 265/*
@@ -322,8 +272,11 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs)
322 int ret = 0; 272 int ret = 0;
323 struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx); 273 struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx);
324 274
325 if (!ctx->active) 275 if (!ctx->active) {
276 pr_debug("kmmio: spurious debug trap on CPU %d.\n",
277 smp_processor_id());
326 goto out; 278 goto out;
279 }
327 280
328 if (ctx->probe && ctx->probe->post_handler) 281 if (ctx->probe && ctx->probe->post_handler)
329 ctx->probe->post_handler(ctx->probe, condition, regs); 282 ctx->probe->post_handler(ctx->probe, condition, regs);
@@ -525,9 +478,22 @@ static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val,
525{ 478{
526 struct die_args *arg = args; 479 struct die_args *arg = args;
527 480
528 if (val == DIE_DEBUG) 481 if (val == DIE_DEBUG && (arg->err & DR_STEP))
529 if (post_kmmio_handler(arg->err, arg->regs) == 1) 482 if (post_kmmio_handler(arg->err, arg->regs) == 1)
530 return NOTIFY_STOP; 483 return NOTIFY_STOP;
531 484
532 return NOTIFY_DONE; 485 return NOTIFY_DONE;
533} 486}
487
488static struct notifier_block nb_die = {
489 .notifier_call = kmmio_die_notifier
490};
491
492static int __init init_kmmio(void)
493{
494 int i;
495 for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++)
496 INIT_LIST_HEAD(&kmmio_page_table[i]);
497 return register_die_notifier(&nb_die);
498}
499fs_initcall(init_kmmio); /* should be before device_initcall() */
diff --git a/arch/x86/kernel/mmiotrace/mmio-mod.c b/arch/x86/kernel/mmiotrace/mmio-mod.c
index 62abc281a512..8256546d49bf 100644
--- a/arch/x86/kernel/mmiotrace/mmio-mod.c
+++ b/arch/x86/kernel/mmiotrace/mmio-mod.c
@@ -415,8 +415,6 @@ void enable_mmiotrace(void)
415 if (is_enabled()) 415 if (is_enabled())
416 goto out; 416 goto out;
417 417
418 reference_kmmio();
419
420#if 0 /* XXX: tracing does not support text entries */ 418#if 0 /* XXX: tracing does not support text entries */
421 marker_file = debugfs_create_file("marker", 0660, dir, NULL, 419 marker_file = debugfs_create_file("marker", 0660, dir, NULL,
422 &fops_marker); 420 &fops_marker);
@@ -448,7 +446,6 @@ void disable_mmiotrace(void)
448 spin_unlock_irq(&trace_lock); 446 spin_unlock_irq(&trace_lock);
449 447
450 clear_trace_list(); /* guarantees: no more kmmio callbacks */ 448 clear_trace_list(); /* guarantees: no more kmmio callbacks */
451 unreference_kmmio();
452 if (marker_file) { 449 if (marker_file) {
453 debugfs_remove(marker_file); 450 debugfs_remove(marker_file);
454 marker_file = NULL; 451 marker_file = NULL;
diff --git a/include/linux/mmiotrace.h b/include/linux/mmiotrace.h
index c88a9c197d22..dd6b64b160fc 100644
--- a/include/linux/mmiotrace.h
+++ b/include/linux/mmiotrace.h
@@ -31,8 +31,6 @@ static inline int is_kmmio_active(void)
31 return kmmio_count; 31 return kmmio_count;
32} 32}
33 33
34extern void reference_kmmio(void);
35extern void unreference_kmmio(void);
36extern int register_kmmio_probe(struct kmmio_probe *p); 34extern int register_kmmio_probe(struct kmmio_probe *p);
37extern void unregister_kmmio_probe(struct kmmio_probe *p); 35extern void unregister_kmmio_probe(struct kmmio_probe *p);
38 36
diff --git a/kernel/trace/trace_mmiotrace.c b/kernel/trace/trace_mmiotrace.c
index 3a12b1ad0c63..361472b5788c 100644
--- a/kernel/trace/trace_mmiotrace.c
+++ b/kernel/trace/trace_mmiotrace.c
@@ -8,6 +8,7 @@
8 8
9#include <linux/kernel.h> 9#include <linux/kernel.h>
10#include <linux/mmiotrace.h> 10#include <linux/mmiotrace.h>
11#include <linux/pci.h>
11 12
12#include "trace.h" 13#include "trace.h"
13 14
@@ -53,12 +54,52 @@ static void mmio_trace_ctrl_update(struct trace_array *tr)
53 } 54 }
54} 55}
55 56
57static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev)
58{
59 int ret = 0;
60 int i;
61 resource_size_t start, end;
62 const struct pci_driver *drv = pci_dev_driver(dev);
63
64 /* XXX: incomplete checks for trace_seq_printf() return value */
65 ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x",
66 dev->bus->number, dev->devfn,
67 dev->vendor, dev->device, dev->irq);
68 /*
69 * XXX: is pci_resource_to_user() appropriate, since we are
70 * supposed to interpret the __ioremap() phys_addr argument based on
71 * these printed values?
72 */
73 for (i = 0; i < 7; i++) {
74 pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
75 ret += trace_seq_printf(s, " %llx",
76 (unsigned long long)(start |
77 (dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
78 }
79 for (i = 0; i < 7; i++) {
80 pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
81 ret += trace_seq_printf(s, " %llx",
82 dev->resource[i].start < dev->resource[i].end ?
83 (unsigned long long)(end - start) + 1 : 0);
84 }
85 if (drv)
86 ret += trace_seq_printf(s, " %s\n", drv->name);
87 else
88 ret += trace_seq_printf(s, " \n");
89 return ret;
90}
91
56/* XXX: This is not called for trace_pipe file! */ 92/* XXX: This is not called for trace_pipe file! */
57void mmio_print_header(struct trace_iterator *iter) 93static void mmio_print_header(struct trace_iterator *iter)
58{ 94{
59 struct trace_seq *s = &iter->seq; 95 struct trace_seq *s = &iter->seq;
60 trace_seq_printf(s, "VERSION broken 20070824\n"); 96 struct pci_dev *dev = NULL;
61 /* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */ 97
98 trace_seq_printf(s, "VERSION 20070824\n");
99
100 for_each_pci_dev(dev)
101 mmio_print_pcidev(s, dev);
102 /* XXX: return value? What if header is very long? */
62} 103}
63 104
64static int mmio_print_rw(struct trace_iterator *iter) 105static int mmio_print_rw(struct trace_iterator *iter)