WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] Re: mm.c:777:d2 Non-privileged (2) attempt to map I/O space

To: Jeremy Fitzhardinge <jeremy@xxxxxxxx>, keir.fraser@xxxxxxxxxxxxx, Ian.Campbell@xxxxxxxxxxxxx, JBeulich@xxxxxxxxxx
Subject: [Xen-devel] Re: mm.c:777:d2 Non-privileged (2) attempt to map I/O space 000f995a + (XEN) mm.c:845:d20 Error getting mfn jd (pfn 84fd) from L1 entry 800000000246d467 for l1e_owner=20, pg_owner=32753
From: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>
Date: Mon, 30 Nov 2009 22:11:20 -0500
Cc: Olivier NOEL <ONOEL@xxxxxxxxxxxxxx>, xen-devel@xxxxxxxxxxxxxxxxxxx
Delivery-date: Mon, 30 Nov 2009 19:13:45 -0800
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
In-reply-to: <20091109235051.GA20408@xxxxxxxxxxxxxxxxxxx>
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
References: <481249.38422.qm@xxxxxxxxxxxxxxxxxxxxxxxxxxx> <4A78CA69.3090105@xxxxxxxx> <0E87C0E865217944860BB378D2898000E1467F@xxxxxxxxxxxxxxxxxx> <0E87C0E865217944860BB378D2898000E146B1@xxxxxxxxxxxxxxxxxx> <4A7B306D.5080108@xxxxxxxx> <20091109235051.GA20408@xxxxxxxxxxxxxxxxxxx>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.5.19 (2009-01-05)
> (XEN) mm.c:841:d5 Error getting mfn 7c1b3 (pfn 784e1) from L1 entry 
> 800000007c1b3467 for l1e_owner=5, pg_owner=32753
> which also loops around forever.
> 

Jeremy, Keir, Ian, Jan, et. al.:

If you set vfb=['type=vnc,vnclisten=0.0.0.0,vncunused=1']
for your pv-ops domU you get:
(XEN) mm.c:845:d20 Error getting mfn jd (pfn 84fd) from L1 entry 
800000000246d467 for l1e_owner=20, pg_owner=32753
this is the first attempt at the fix.

What essentially happens is the Plymoth (a framebuffer daemon) starts
writing to the frame buffer, causes a page fault and the domU kernel doesn't 
handle
it too well.

Specifically these are the steps:
user space:
 fd = opens("/dev/fb0");
 handle =mmap(0, len, PROT_WRITE, MAP_SHARED, fd, 0)

and in the kernel the fb_deferred_io_mmap is called which
sets:
 vma->vm_flags = (VM_DONTEXPAND | VM_RESERVED | VM_IO)

 [VM_IO means that subsequent pages will have PAGE_IOMAP set]

next in the user space we do:
 handle[i] = 'a';

which causes a page_fault and we jump to the kernel:
page_fault ->
        handle_mm_fault ->
                __do_fault()
                    |-----vm_ops->fault (fb_deferred_io_fault):
                    |           fb_deferred_io_page:
                    |                   vmalloc_to_page [We now have a page]
                    |           vmf->page = page [page attached to the user 
address, good]
                    |----mk_pte( .. ), sets PAGE_IOMAP
                    |
                    |----xen_set_pte_at ():
                        [ This is the fun part ]
                          |----if (xen_iomap_pte(pteval)) [ checks if 
_PAGE_IOMAP is set]
                                  |----xen_set_domain_pte():
                                        [which makes the PTE belong to DOMID_IO]

And at that point the Xen Hypervisor is called, and spits out:
(XEN) mm.c:845:d20 Error getting mfn jd (pfn 84fd) from L1 entry 
800000000246d467 for l1e_owner=20, pg_owner=32753

as it interprets the PFN as the MFN.

This is incorrect as the page is vmalloc-ed and has no IO backing.

Poking around I've come up with three ideas to solve this:

1). Inhibit xen_fb_deferred_io_map from setting VM_IO. That fixes the issue, but
    there are drivers (sh_mobile_lcdcfb.c) that have the fb be backed up by a 
physical
    page, in which case VM_IO is correct. So this is a no go.

2). Inhibit xen_set_pte_at to call xen_set_domain_pte if the page has 
_PAGE_USER and _PAGE_IOMAP.
    That did not work at all. The Hypervisor seemed to have lost any clue to 
whom the page belongs.

3). Make xen-fbfront implement its own mmap functionality. This is what 
linux-2.6.18.hg has.
    I made an attempt at back-porting it (thought it is quite interrupt heavy 
as each
    page_fault causes it to trigger a HYPERVISOR event channel up call). I will 
need to redo
    this if this seems to be the right way.

Thoughts? Maybe there is a better way at doing this?

Attached is the test-case, and the patch for 3). Patch inlined for easier view:


>From 6938ff544e7483d7eedb8eac9ccad489cced27e7 Mon Sep 17 00:00:00 2001
From: Konrad Rzeszutek Wilk <konrad.wilk@xxxxxxxxxx>
Date: Mon, 30 Nov 2009 21:24:59 -0500
Subject: [PATCH 1/2] [xen-fb] Provide a fb_mmap function.

Provide a fb_mmap function instead of using fb_deferred_io_mmap
functionality. The reason behind this is that fb_deferred_io_mmap
sets the VM_IO flag which in a Xen environement is reserved for
pages which have a physical device mapped. For Xen FB our "physical
device" is a 2MB vmalloc area. The end result is that Xen MMU sets
PTE's for this 2MB with the wrong MFN b/c it sees the _PAGE_IOMAP set
(which is set when VM_IO is set).
---
 drivers/video/xen-fbfront.c |  125 ++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 123 insertions(+), 2 deletions(-)

diff --git a/drivers/video/xen-fbfront.c b/drivers/video/xen-fbfront.c
index 0c6b1c6..ae83653 100644
--- a/drivers/video/xen-fbfront.c
+++ b/drivers/video/xen-fbfront.c
@@ -32,6 +32,13 @@
 #include <xen/interface/io/protocols.h>
 #include <xen/xenbus.h>
 
+struct xenfb_mapping {
+       struct list_head        link;
+       struct vm_area_struct   *vma;
+       atomic_t                map_refs;
+       struct xenfb_info       *info;
+};
+
 struct xenfb_info {
        unsigned char           *fb;
        struct fb_info          *fb_info;
@@ -48,6 +55,9 @@ struct xenfb_info {
        int                     resize_dpy;     /* ditto */
        spinlock_t              resize_lock;
 
+       struct list_head        mappings;
+       spinlock_t              mm_lock;
+
        struct xenbus_device    *xbdev;
 };
 
@@ -321,12 +331,118 @@ static int xenfb_set_par(struct fb_info *info)
        return 0;
 }
 
+
+static void xenfb_vm_close(struct vm_area_struct *vma)
+{
+       struct xenfb_mapping *map = vma->vm_private_data;
+       struct xenfb_info *info = map->info;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+       if (atomic_dec_and_test(&map->map_refs)) {
+               list_del(&map->link);
+               kfree(map);
+       }
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+}
+
+static void xenfb_vm_open(struct vm_area_struct *vma)
+{
+       struct xenfb_mapping *map = vma->vm_private_data;
+       atomic_inc(&map->map_refs);
+}
+
+
+/* this is to find and return the vmalloc-ed fb pages */
+static int xenfb_vm_fault(struct vm_area_struct *vma,
+                       struct vm_fault *vmf)
+{
+       struct xenfb_mapping *map = vma->vm_private_data;
+       struct xenfb_info *info = map->info;
+       unsigned long offset;
+       struct page *page;
+       int y1, y2;
+
+       offset = vmf->pgoff << PAGE_SHIFT;
+       if (offset >= info->fb_info->fix.smem_len)
+               return VM_FAULT_SIGBUS;
+
+       page = vmalloc_to_page(info->fb_info->screen_base + offset);
+       if (!page)
+               return VM_FAULT_SIGBUS;
+
+       get_page(page);
+
+       page->index = vmf->pgoff;
+
+       y1 = vmf->pgoff * PAGE_SIZE / info->fb_info->fix.line_length;
+       y2 = (vmf->pgoff * PAGE_SIZE + PAGE_SIZE - 1) /
+               info->fb_info->fix.line_length;
+       if (y2 > info->fb_info->var.yres)
+               y2 = info->fb_info->var.yres;
+
+       xenfb_refresh(info, 0, y1, info->fb_info->var.xres, y2 - y1);
+
+       vmf->page = page;
+       return 0;
+}
+
+static struct vm_operations_struct xenfb_vm_ops = {
+       .open   = xenfb_vm_open,
+       .close  = xenfb_vm_close,
+       .fault = xenfb_vm_fault,
+};
+
+static int xenfb_mmap(struct fb_info *fb_info, struct vm_area_struct *vma)
+{
+       struct xenfb_info *info = fb_info->par;
+       struct xenfb_mapping *map;
+       int map_pages;
+       unsigned long flags;
+
+       if (!(vma->vm_flags & VM_WRITE))
+               return -EINVAL;
+       if (!(vma->vm_flags & VM_SHARED))
+               return -EINVAL;
+       if (vma->vm_pgoff != 0)
+               return -EINVAL;
+
+       map_pages = (vma->vm_end - vma->vm_start + PAGE_SIZE-1) >> PAGE_SHIFT;
+       if (map_pages > info->nr_pages)
+               return -EINVAL;
+
+       map = kzalloc(sizeof(*map), GFP_KERNEL);
+       if (map == NULL)
+               return -ENOMEM;
+
+       map->vma = vma;
+       map->info = info;
+       atomic_set(&map->map_refs, 1);
+
+       spin_lock_irqsave(&info->mm_lock, flags);
+       list_add(&map->link, &info->mappings);
+       spin_unlock_irqrestore(&info->mm_lock, flags);
+
+       vma->vm_ops = &xenfb_vm_ops;
+       /* It is _extremely_ important that VM_IO is not set here. If you do
+       * set it, the xen_set_pte (called later by __do_fault) will assign
+       * the pte to the DOMID_IO which is reserved for IO pages (ioremap,
+       * and its friends), not vmalloc-ed ones. The result is an ugly
+       * infinite page fault recursion. */
+       vma->vm_flags |= (VM_DONTEXPAND | VM_RESERVED);
+       vma->vm_private_data = map;
+
+       return 0;
+}
+
+
 static struct fb_ops xenfb_fb_ops = {
        .owner          = THIS_MODULE,
        .fb_read        = fb_sys_read,
        .fb_write       = xenfb_write,
        .fb_setcolreg   = xenfb_setcolreg,
        .fb_fillrect    = xenfb_fillrect,
+       .fb_mmap        = xenfb_mmap,
        .fb_copyarea    = xenfb_copyarea,
        .fb_imageblit   = xenfb_imageblit,
        .fb_check_var   = xenfb_check_var,
@@ -391,6 +507,9 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
        spin_lock_init(&info->dirty_lock);
        spin_lock_init(&info->resize_lock);
 
+       spin_lock_init(&info->mm_lock);
+       INIT_LIST_HEAD(&info->mappings);
+
        info->fb = vmalloc(fb_size);
        if (info->fb == NULL)
                goto error_nomem;
@@ -449,8 +568,10 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
                goto error;
        }
 
+       /* The xen_fb_mmap replaces this.
        fb_info->fbdefio = &xenfb_defio;
        fb_deferred_io_init(fb_info);
+       */
 
        xenfb_init_shared_page(info, fb_info);
 
@@ -460,7 +581,7 @@ static int __devinit xenfb_probe(struct xenbus_device *dev,
 
        ret = register_framebuffer(fb_info);
        if (ret) {
-               fb_deferred_io_cleanup(fb_info);
+               /* fb_deferred_io_cleanup(fb_info); */
                fb_dealloc_cmap(&fb_info->cmap);
                framebuffer_release(fb_info);
                xenbus_dev_fatal(dev, ret, "register_framebuffer");
@@ -516,7 +637,7 @@ static int xenfb_remove(struct xenbus_device *dev)
 
        xenfb_disconnect_backend(info);
        if (info->fb_info) {
-               fb_deferred_io_cleanup(info->fb_info);
+               /* fb_deferred_io_cleanup(info->fb_info); */
                unregister_framebuffer(info->fb_info);
                fb_dealloc_cmap(&info->fb_info->cmap);
                framebuffer_release(info->fb_info);
-- 
1.6.2.5

Attachment: mmap_test.c
Description: Text document

Attachment: 0001--xen-fb-Provide-a-fb_mmap-function.patch
Description: Text document

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
<Prev in Thread] Current Thread [Next in Thread>