[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [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
> (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 Attachment:
0001--xen-fb-Provide-a-fb_mmap-function.patch _______________________________________________ Xen-devel mailing list Xen-devel@xxxxxxxxxxxxxxxxxxx http://lists.xensource.com/xen-devel
|
Lists.xenproject.org is hosted with RackSpace, monitoring our |