Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 100600
b: refs/heads/master
c: 1382953
h: refs/heads/master
v: v3
  • Loading branch information
Pekka Paalanen authored and Thomas Gleixner committed May 24, 2008
1 parent 308c097 commit 5ce37b6
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 119 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: bd8ac686c73c7e925fcfe0b02dc4e7b947127864
refs/heads/master: 138295373ccf7625fcb0218dfea114837983bc39
186 changes: 76 additions & 110 deletions trunk/arch/x86/kernel/mmiotrace/kmmio.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
* 2008 Pekka Paalanen <pq@iki.fi>
*/

#include <linux/version.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/hash.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/uaccess.h>
#include <linux/ptrace.h>
#include <linux/preempt.h>
Expand All @@ -22,10 +19,9 @@
#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/cacheflush.h>
#include <asm/errno.h>
#include <asm/tlbflush.h>
#include <asm/pgtable.h>

#include <asm/errno.h>
#include <asm/debugreg.h>
#include <linux/mmiotrace.h>

#define KMMIO_PAGE_HASH_BITS 4
Expand Down Expand Up @@ -57,14 +53,9 @@ struct kmmio_context {
int active;
};

static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val,
void *args);

static DEFINE_MUTEX(kmmio_init_mutex);
static DEFINE_SPINLOCK(kmmio_lock);

/* These are protected by kmmio_lock */
static int kmmio_initialized;
/* Protected by kmmio_lock */
unsigned int kmmio_count;

/* Read-protected by RCU, write-protected by kmmio_lock. */
Expand All @@ -79,60 +70,6 @@ static struct list_head *kmmio_page_list(unsigned long page)
/* Accessed per-cpu */
static DEFINE_PER_CPU(struct kmmio_context, kmmio_ctx);

/* protected by kmmio_init_mutex */
static struct notifier_block nb_die = {
.notifier_call = kmmio_die_notifier
};

/**
* Makes sure kmmio is initialized and usable.
* This must be called before any other kmmio function defined here.
* May sleep.
*/
void reference_kmmio(void)
{
mutex_lock(&kmmio_init_mutex);
spin_lock_irq(&kmmio_lock);
if (!kmmio_initialized) {
int i;
for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++)
INIT_LIST_HEAD(&kmmio_page_table[i]);
if (register_die_notifier(&nb_die))
BUG();
}
kmmio_initialized++;
spin_unlock_irq(&kmmio_lock);
mutex_unlock(&kmmio_init_mutex);
}
EXPORT_SYMBOL_GPL(reference_kmmio);

/**
* Clean up kmmio after use. This must be called for every call to
* reference_kmmio(). All probes registered after the corresponding
* reference_kmmio() must have been unregistered when calling this.
* May sleep.
*/
void unreference_kmmio(void)
{
bool unreg = false;

mutex_lock(&kmmio_init_mutex);
spin_lock_irq(&kmmio_lock);

if (kmmio_initialized == 1) {
BUG_ON(is_kmmio_active());
unreg = true;
}
kmmio_initialized--;
BUG_ON(kmmio_initialized < 0);
spin_unlock_irq(&kmmio_lock);

if (unreg)
unregister_die_notifier(&nb_die); /* calls sync_rcu() */
mutex_unlock(&kmmio_init_mutex);
}
EXPORT_SYMBOL(unreference_kmmio);

/*
* this is basically a dynamic stabbing problem:
* Could use the existing prio tree code or
Expand Down Expand Up @@ -167,58 +104,56 @@ static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page)
return NULL;
}

/** Mark the given page as not present. Access to it will trigger a fault. */
static void arm_kmmio_fault_page(unsigned long page, int *page_level)
static void set_page_present(unsigned long addr, bool present, int *pglevel)
{
unsigned long address = page & PAGE_MASK;
pteval_t pteval;
pmdval_t pmdval;
int level;
pte_t *pte = lookup_address(address, &level);
pmd_t *pmd;
pte_t *pte = lookup_address(addr, &level);

if (!pte) {
pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n",
__func__, page);
pr_err("kmmio: no pte for page 0x%08lx\n", addr);
return;
}

if (level == PG_LEVEL_2M) {
pmd_t *pmd = (pmd_t *)pte;
set_pmd(pmd, __pmd(pmd_val(*pmd) & ~_PAGE_PRESENT));
} else {
/* PG_LEVEL_4K */
set_pte(pte, __pte(pte_val(*pte) & ~_PAGE_PRESENT));
if (pglevel)
*pglevel = level;

switch (level) {
case PG_LEVEL_2M:
pmd = (pmd_t *)pte;
pmdval = pmd_val(*pmd) & ~_PAGE_PRESENT;
if (present)
pmdval |= _PAGE_PRESENT;
set_pmd(pmd, __pmd(pmdval));
break;

case PG_LEVEL_4K:
pteval = pte_val(*pte) & ~_PAGE_PRESENT;
if (present)
pteval |= _PAGE_PRESENT;
set_pte_atomic(pte, __pte(pteval));
break;

default:
pr_err("kmmio: unexpected page level 0x%x.\n", level);
return;
}

if (page_level)
*page_level = level;
__flush_tlb_one(addr);
}

__flush_tlb_one(page);
/** Mark the given page as not present. Access to it will trigger a fault. */
static void arm_kmmio_fault_page(unsigned long page, int *page_level)
{
set_page_present(page & PAGE_MASK, false, page_level);
}

/** Mark the given page as present. */
static void disarm_kmmio_fault_page(unsigned long page, int *page_level)
{
unsigned long address = page & PAGE_MASK;
int level;
pte_t *pte = lookup_address(address, &level);

if (!pte) {
pr_err("kmmio: Error in %s: no pte for page 0x%08lx\n",
__func__, page);
return;
}

if (level == PG_LEVEL_2M) {
pmd_t *pmd = (pmd_t *)pte;
set_pmd(pmd, __pmd(pmd_val(*pmd) | _PAGE_PRESENT));
} else {
/* PG_LEVEL_4K */
set_pte(pte, __pte(pte_val(*pte) | _PAGE_PRESENT));
}

if (page_level)
*page_level = level;

__flush_tlb_one(page);
set_page_present(page & PAGE_MASK, true, page_level);
}

/*
Expand All @@ -240,6 +175,7 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
{
struct kmmio_context *ctx;
struct kmmio_fault_page *faultpage;
int ret = 0; /* default to fault not handled */

/*
* Preemption is now disabled to prevent process switch during
Expand All @@ -257,21 +193,35 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
/*
* Either this page fault is not caused by kmmio, or
* another CPU just pulled the kmmio probe from under
* our feet. In the latter case all hell breaks loose.
* our feet. The latter case should not be possible.
*/
goto no_kmmio;
}

ctx = &get_cpu_var(kmmio_ctx);
if (ctx->active) {
disarm_kmmio_fault_page(faultpage->page, NULL);
if (addr == ctx->addr) {
/*
* On SMP we sometimes get recursive probe hits on the
* same address. Context is already saved, fall out.
*/
pr_debug("kmmio: duplicate probe hit on CPU %d, for "
"address 0x%08lx.\n",
smp_processor_id(), addr);
ret = 1;
goto no_kmmio_ctx;
}
/*
* Prevent overwriting already in-flight context.
* If this page fault really was due to kmmio trap,
* all hell breaks loose.
* This should not happen, let's hope disarming at least
* prevents a panic.
*/
pr_emerg("kmmio: recursive probe hit on CPU %d, "
"for address 0x%08lx. Ignoring.\n",
smp_processor_id(), addr);
pr_emerg("kmmio: previous hit was at 0x%08lx.\n",
ctx->addr);
goto no_kmmio_ctx;
}
ctx->active++;
Expand Down Expand Up @@ -302,14 +252,14 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
*/

put_cpu_var(kmmio_ctx);
return 1;
return 1; /* fault handled */

no_kmmio_ctx:
put_cpu_var(kmmio_ctx);
no_kmmio:
rcu_read_unlock();
preempt_enable_no_resched();
return 0; /* page fault not handled by kmmio */
return ret;
}

/*
Expand All @@ -322,8 +272,11 @@ static int post_kmmio_handler(unsigned long condition, struct pt_regs *regs)
int ret = 0;
struct kmmio_context *ctx = &get_cpu_var(kmmio_ctx);

if (!ctx->active)
if (!ctx->active) {
pr_debug("kmmio: spurious debug trap on CPU %d.\n",
smp_processor_id());
goto out;
}

if (ctx->probe && ctx->probe->post_handler)
ctx->probe->post_handler(ctx->probe, condition, regs);
Expand Down Expand Up @@ -525,9 +478,22 @@ static int kmmio_die_notifier(struct notifier_block *nb, unsigned long val,
{
struct die_args *arg = args;

if (val == DIE_DEBUG)
if (val == DIE_DEBUG && (arg->err & DR_STEP))
if (post_kmmio_handler(arg->err, arg->regs) == 1)
return NOTIFY_STOP;

return NOTIFY_DONE;
}

static struct notifier_block nb_die = {
.notifier_call = kmmio_die_notifier
};

static int __init init_kmmio(void)
{
int i;
for (i = 0; i < KMMIO_PAGE_TABLE_SIZE; i++)
INIT_LIST_HEAD(&kmmio_page_table[i]);
return register_die_notifier(&nb_die);
}
fs_initcall(init_kmmio); /* should be before device_initcall() */
3 changes: 0 additions & 3 deletions trunk/arch/x86/kernel/mmiotrace/mmio-mod.c
Original file line number Diff line number Diff line change
Expand Up @@ -415,8 +415,6 @@ void enable_mmiotrace(void)
if (is_enabled())
goto out;

reference_kmmio();

#if 0 /* XXX: tracing does not support text entries */
marker_file = debugfs_create_file("marker", 0660, dir, NULL,
&fops_marker);
Expand Down Expand Up @@ -448,7 +446,6 @@ void disable_mmiotrace(void)
spin_unlock_irq(&trace_lock);

clear_trace_list(); /* guarantees: no more kmmio callbacks */
unreference_kmmio();
if (marker_file) {
debugfs_remove(marker_file);
marker_file = NULL;
Expand Down
2 changes: 0 additions & 2 deletions trunk/include/linux/mmiotrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ static inline int is_kmmio_active(void)
return kmmio_count;
}

extern void reference_kmmio(void);
extern void unreference_kmmio(void);
extern int register_kmmio_probe(struct kmmio_probe *p);
extern void unregister_kmmio_probe(struct kmmio_probe *p);

Expand Down
47 changes: 44 additions & 3 deletions trunk/kernel/trace/trace_mmiotrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include <linux/kernel.h>
#include <linux/mmiotrace.h>
#include <linux/pci.h>

#include "trace.h"

Expand Down Expand Up @@ -53,12 +54,52 @@ static void mmio_trace_ctrl_update(struct trace_array *tr)
}
}

static int mmio_print_pcidev(struct trace_seq *s, const struct pci_dev *dev)
{
int ret = 0;
int i;
resource_size_t start, end;
const struct pci_driver *drv = pci_dev_driver(dev);

/* XXX: incomplete checks for trace_seq_printf() return value */
ret += trace_seq_printf(s, "PCIDEV %02x%02x %04x%04x %x",
dev->bus->number, dev->devfn,
dev->vendor, dev->device, dev->irq);
/*
* XXX: is pci_resource_to_user() appropriate, since we are
* supposed to interpret the __ioremap() phys_addr argument based on
* these printed values?
*/
for (i = 0; i < 7; i++) {
pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
ret += trace_seq_printf(s, " %llx",
(unsigned long long)(start |
(dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
}
for (i = 0; i < 7; i++) {
pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
ret += trace_seq_printf(s, " %llx",
dev->resource[i].start < dev->resource[i].end ?
(unsigned long long)(end - start) + 1 : 0);
}
if (drv)
ret += trace_seq_printf(s, " %s\n", drv->name);
else
ret += trace_seq_printf(s, " \n");
return ret;
}

/* XXX: This is not called for trace_pipe file! */
void mmio_print_header(struct trace_iterator *iter)
static void mmio_print_header(struct trace_iterator *iter)
{
struct trace_seq *s = &iter->seq;
trace_seq_printf(s, "VERSION broken 20070824\n");
/* TODO: print /proc/bus/pci/devices contents as PCIDEV lines */
struct pci_dev *dev = NULL;

trace_seq_printf(s, "VERSION 20070824\n");

for_each_pci_dev(dev)
mmio_print_pcidev(s, dev);
/* XXX: return value? What if header is very long? */
}

static int mmio_print_rw(struct trace_iterator *iter)
Expand Down

0 comments on commit 5ce37b6

Please sign in to comment.