Re: [Nouveau] RFC: [PATCH] x86/kmmio: fix mmiotrace for hugepages

From: Pierre Moreau
Date: Thu Mar 03 2016 - 17:50:32 EST


The secondary hit exception thrown while MMIOtracing NVIDIA's driver is gone
with this patch.

Tested-by: Pierre Moreau <pierre.morrow@xxxxxxx>

On 02:03 AM - Mar 03 2016, Karol Herbst wrote:
> Because Linux might use bigger pages than the 4K pages to handle those mmio
> ioremaps, the kmmio code shouldn't rely on the pade id as it currently does.
>
> Using the memory address instead of the page id let's us lookup how big the
> page is and what it's base address is, so that we won't get a page fault
> within the same page twice anymore.
>
> I don't know if I got this right though, so please read this change with
> great care
>
> v2: use page_level macros
>
> Signed-off-by: Karol Herbst <nouveau@xxxxxxxxxxxxxx>
> ---
> arch/x86/mm/kmmio.c | 89 ++++++++++++++++++++++++++++++++++++-----------------
> 1 file changed, 60 insertions(+), 29 deletions(-)
>
> diff --git a/arch/x86/mm/kmmio.c b/arch/x86/mm/kmmio.c
> index 637ab34..d203beb 100644
> --- a/arch/x86/mm/kmmio.c
> +++ b/arch/x86/mm/kmmio.c
> @@ -33,7 +33,7 @@
> struct kmmio_fault_page {
> struct list_head list;
> struct kmmio_fault_page *release_next;
> - unsigned long page; /* location of the fault page */
> + unsigned long addr; /* the requested address */
> pteval_t old_presence; /* page presence prior to arming */
> bool armed;
>
> @@ -70,9 +70,16 @@ unsigned int kmmio_count;
> static struct list_head kmmio_page_table[KMMIO_PAGE_TABLE_SIZE];
> static LIST_HEAD(kmmio_probes);
>
> -static struct list_head *kmmio_page_list(unsigned long page)
> +static struct list_head *kmmio_page_list(unsigned long addr)
> {
> - return &kmmio_page_table[hash_long(page, KMMIO_PAGE_HASH_BITS)];
> + unsigned int l;
> + pte_t *pte = lookup_address(addr, &l);
> +
> + if (!pte)
> + return NULL;
> + addr &= page_level_mask(l);
> +
> + return &kmmio_page_table[hash_long(addr, KMMIO_PAGE_HASH_BITS)];
> }
>
> /* Accessed per-cpu */
> @@ -98,15 +105,19 @@ static struct kmmio_probe *get_kmmio_probe(unsigned long addr)
> }
>
> /* You must be holding RCU read lock. */
> -static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long page)
> +static struct kmmio_fault_page *get_kmmio_fault_page(unsigned long addr)
> {
> struct list_head *head;
> struct kmmio_fault_page *f;
> + unsigned int l;
> + pte_t *pte = lookup_address(addr, &l);
>
> - page &= PAGE_MASK;
> - head = kmmio_page_list(page);
> + if (!pte)
> + return NULL;
> + addr &= page_level_mask(l);
> + head = kmmio_page_list(addr);
> list_for_each_entry_rcu(f, head, list) {
> - if (f->page == page)
> + if (f->addr == addr)
> return f;
> }
> return NULL;
> @@ -137,10 +148,10 @@ static void clear_pte_presence(pte_t *pte, bool clear, pteval_t *old)
> static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
> {
> unsigned int level;
> - pte_t *pte = lookup_address(f->page, &level);
> + pte_t *pte = lookup_address(f->addr, &level);
>
> if (!pte) {
> - pr_err("no pte for page 0x%08lx\n", f->page);
> + pr_err("no pte for addr 0x%08lx\n", f->addr);
> return -1;
> }
>
> @@ -156,7 +167,7 @@ static int clear_page_presence(struct kmmio_fault_page *f, bool clear)
> return -1;
> }
>
> - __flush_tlb_one(f->page);
> + __flush_tlb_one(f->addr);
> return 0;
> }
>
> @@ -176,12 +187,12 @@ static int arm_kmmio_fault_page(struct kmmio_fault_page *f)
> int ret;
> WARN_ONCE(f->armed, KERN_ERR pr_fmt("kmmio page already armed.\n"));
> if (f->armed) {
> - pr_warning("double-arm: page 0x%08lx, ref %d, old %d\n",
> - f->page, f->count, !!f->old_presence);
> + pr_warning("double-arm: addr 0x%08lx, ref %d, old %d\n",
> + f->addr, f->count, !!f->old_presence);
> }
> ret = clear_page_presence(f, true);
> - WARN_ONCE(ret < 0, KERN_ERR pr_fmt("arming 0x%08lx failed.\n"),
> - f->page);
> + WARN_ONCE(ret < 0, KERN_ERR pr_fmt("arming at 0x%08lx failed.\n"),
> + f->addr);
> f->armed = true;
> return ret;
> }
> @@ -191,7 +202,7 @@ static void disarm_kmmio_fault_page(struct kmmio_fault_page *f)
> {
> int ret = clear_page_presence(f, false);
> WARN_ONCE(ret < 0,
> - KERN_ERR "kmmio disarming 0x%08lx failed.\n", f->page);
> + KERN_ERR "kmmio disarming at 0x%08lx failed.\n", f->addr);
> f->armed = false;
> }
>
> @@ -215,6 +226,12 @@ 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 */
> + unsigned long page_base = addr;
> + unsigned int l;
> + pte_t *pte = lookup_address(addr, &l);
> + if (!pte)
> + return -EINVAL;
> + page_base &= page_level_mask(l);
>
> /*
> * Preemption is now disabled to prevent process switch during
> @@ -227,7 +244,7 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
> preempt_disable();
> rcu_read_lock();
>
> - faultpage = get_kmmio_fault_page(addr);
> + faultpage = get_kmmio_fault_page(page_base);
> if (!faultpage) {
> /*
> * Either this page fault is not caused by kmmio, or
> @@ -239,7 +256,7 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
>
> ctx = &get_cpu_var(kmmio_ctx);
> if (ctx->active) {
> - if (addr == ctx->addr) {
> + if (page_base == ctx->addr) {
> /*
> * A second fault on the same page means some other
> * condition needs handling by do_page_fault(), the
> @@ -267,9 +284,9 @@ int kmmio_handler(struct pt_regs *regs, unsigned long addr)
> ctx->active++;
>
> ctx->fpage = faultpage;
> - ctx->probe = get_kmmio_probe(addr);
> + ctx->probe = get_kmmio_probe(page_base);
> ctx->saved_flags = (regs->flags & (X86_EFLAGS_TF | X86_EFLAGS_IF));
> - ctx->addr = addr;
> + ctx->addr = page_base;
>
> if (ctx->probe && ctx->probe->pre_handler)
> ctx->probe->pre_handler(ctx->probe, regs, addr);
> @@ -354,12 +371,11 @@ out:
> }
>
> /* You must be holding kmmio_lock. */
> -static int add_kmmio_fault_page(unsigned long page)
> +static int add_kmmio_fault_page(unsigned long addr)
> {
> struct kmmio_fault_page *f;
>
> - page &= PAGE_MASK;
> - f = get_kmmio_fault_page(page);
> + f = get_kmmio_fault_page(addr);
> if (f) {
> if (!f->count)
> arm_kmmio_fault_page(f);
> @@ -372,26 +388,25 @@ static int add_kmmio_fault_page(unsigned long page)
> return -1;
>
> f->count = 1;
> - f->page = page;
> + f->addr = addr;
>
> if (arm_kmmio_fault_page(f)) {
> kfree(f);
> return -1;
> }
>
> - list_add_rcu(&f->list, kmmio_page_list(f->page));
> + list_add_rcu(&f->list, kmmio_page_list(f->addr));
>
> return 0;
> }
>
> /* You must be holding kmmio_lock. */
> -static void release_kmmio_fault_page(unsigned long page,
> +static void release_kmmio_fault_page(unsigned long addr,
> struct kmmio_fault_page **release_list)
> {
> struct kmmio_fault_page *f;
>
> - page &= PAGE_MASK;
> - f = get_kmmio_fault_page(page);
> + f = get_kmmio_fault_page(addr);
> if (!f)
> return;
>
> @@ -420,18 +435,27 @@ int register_kmmio_probe(struct kmmio_probe *p)
> int ret = 0;
> unsigned long size = 0;
> const unsigned long size_lim = p->len + (p->addr & ~PAGE_MASK);
> + unsigned int l;
> + pte_t *pte;
>
> spin_lock_irqsave(&kmmio_lock, flags);
> if (get_kmmio_probe(p->addr)) {
> ret = -EEXIST;
> goto out;
> }
> +
> + pte = lookup_address(p->addr, &l);
> + if (!pte) {
> + ret = -EINVAL;
> + goto out;
> + }
> +
> kmmio_count++;
> list_add_rcu(&p->list, &kmmio_probes);
> while (size < size_lim) {
> if (add_kmmio_fault_page(p->addr + size))
> pr_err("Unable to set page fault.\n");
> - size += PAGE_SIZE;
> + size += page_level_size(l);
> }
> out:
> spin_unlock_irqrestore(&kmmio_lock, flags);
> @@ -506,11 +530,18 @@ void unregister_kmmio_probe(struct kmmio_probe *p)
> const unsigned long size_lim = p->len + (p->addr & ~PAGE_MASK);
> struct kmmio_fault_page *release_list = NULL;
> struct kmmio_delayed_release *drelease;
> + unsigned int l;
> + pte_t *pte;
> +
> + pte = lookup_address(p->addr, &l);
> + if (!pte) {
> + return;
> + }
>
> spin_lock_irqsave(&kmmio_lock, flags);
> while (size < size_lim) {
> release_kmmio_fault_page(p->addr + size, &release_list);
> - size += PAGE_SIZE;
> + size += page_level_size(l);
> }
> list_del_rcu(&p->list);
> kmmio_count--;
> --
> 2.7.2
>
> _______________________________________________
> Nouveau mailing list
> Nouveau@xxxxxxxxxxxxxxxxxxxxx
> https://lists.freedesktop.org/mailman/listinfo/nouveau

Attachment: signature.asc
Description: PGP signature