X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fvideo%2Ffb_defio.c;h=0a7a6679ee6eb05e2be754ff816ba3547a0f7fdb;hb=8259cf4342029aad37660e524178c8858f48b0ab;hp=a0c5d9d90d741499e5cdb6087d138337c494b3e9;hpb=84902b7af642c86a518c17629c0dbe705a4b6d14;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c index a0c5d9d..0a7a667 100644 --- a/drivers/video/fb_defio.c +++ b/drivers/video/fb_defio.c @@ -4,7 +4,7 @@ * Copyright (C) 2006 Jaya Kumar * * This file is subject to the terms and conditions of the GNU General Public - * License. See the file COPYING in the main directory of this archive + * License. See the file COPYING in the main directory of this archive * for more details. */ @@ -24,34 +24,57 @@ #include #include +struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs) +{ + void *screen_base = (void __force *) info->screen_base; + struct page *page; + + if (is_vmalloc_addr(screen_base + offs)) + page = vmalloc_to_page(screen_base + offs); + else + page = pfn_to_page((info->fix.smem_start + offs) >> PAGE_SHIFT); + + return page; +} + /* this is to find and return the vmalloc-ed fb pages */ -static struct page* fb_deferred_io_nopage(struct vm_area_struct *vma, - unsigned long vaddr, int *type) +static int fb_deferred_io_fault(struct vm_area_struct *vma, + struct vm_fault *vmf) { unsigned long offset; struct page *page; struct fb_info *info = vma->vm_private_data; - /* info->screen_base is in System RAM */ - void *screen_base = (void __force *) info->screen_base; - offset = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); + offset = vmf->pgoff << PAGE_SHIFT; if (offset >= info->fix.smem_len) - return NOPAGE_SIGBUS; + return VM_FAULT_SIGBUS; - page = vmalloc_to_page(screen_base + offset); + page = fb_deferred_io_page(info, offset); if (!page) - return NOPAGE_OOM; + return VM_FAULT_SIGBUS; get_page(page); - if (type) - *type = VM_FAULT_MINOR; - return page; + + if (vma->vm_file) + page->mapping = vma->vm_file->f_mapping; + else + printk(KERN_ERR "no mapping available\n"); + + BUG_ON(!page->mapping); + page->index = vmf->pgoff; + + vmf->page = page; + return 0; } int fb_deferred_io_fsync(struct file *file, struct dentry *dentry, int datasync) { struct fb_info *info = file->private_data; + /* Skip if deferred io is complied-in but disabled on this fbdev */ + if (!info->fbdefio) + return 0; + /* Kill off the delayed work */ cancel_rearming_delayed_work(&info->deferred_work); @@ -62,10 +85,12 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_fsync); /* vm_ops->page_mkwrite handler */ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, - struct page *page) + struct vm_fault *vmf) { + struct page *page = vmf->page; struct fb_info *info = vma->vm_private_data; struct fb_deferred_io *fbdefio = info->fbdefio; + struct page *cur; /* this is a callback we get when userspace first tries to write to the page. we schedule a workqueue. that workqueue @@ -75,7 +100,24 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, /* protect against the workqueue changing the page list */ mutex_lock(&fbdefio->lock); - list_add(&page->lru, &fbdefio->pagelist); + + /* we loop through the pagelist before adding in order + to keep the pagelist sorted */ + list_for_each_entry(cur, &fbdefio->pagelist, lru) { + /* this check is to catch the case where a new + process could start writing to the same page + through a new pte. this new access can cause the + mkwrite even when the original ps's pte is marked + writable */ + if (unlikely(cur == page)) + goto page_already_added; + else if (cur->index > page->index) + break; + } + + list_add_tail(&page->lru, &cur->lru); + +page_already_added: mutex_unlock(&fbdefio->lock); /* come back after delay to process the deferred IO */ @@ -84,10 +126,21 @@ static int fb_deferred_io_mkwrite(struct vm_area_struct *vma, } static struct vm_operations_struct fb_deferred_io_vm_ops = { - .nopage = fb_deferred_io_nopage, + .fault = fb_deferred_io_fault, .page_mkwrite = fb_deferred_io_mkwrite, }; +static int fb_deferred_io_set_page_dirty(struct page *page) +{ + if (!PageDirty(page)) + SetPageDirty(page); + return 0; +} + +static const struct address_space_operations fb_deferred_io_aops = { + .set_page_dirty = fb_deferred_io_set_page_dirty, +}; + static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma) { vma->vm_ops = &fb_deferred_io_vm_ops; @@ -137,13 +190,32 @@ void fb_deferred_io_init(struct fb_info *info) } EXPORT_SYMBOL_GPL(fb_deferred_io_init); +void fb_deferred_io_open(struct fb_info *info, + struct inode *inode, + struct file *file) +{ + file->f_mapping->a_ops = &fb_deferred_io_aops; +} +EXPORT_SYMBOL_GPL(fb_deferred_io_open); + void fb_deferred_io_cleanup(struct fb_info *info) { struct fb_deferred_io *fbdefio = info->fbdefio; + struct page *page; + int i; BUG_ON(!fbdefio); cancel_delayed_work(&info->deferred_work); flush_scheduled_work(); + + /* clear out the mapping that we setup */ + for (i = 0 ; i < info->fix.smem_len; i += PAGE_SIZE) { + page = fb_deferred_io_page(info, i); + page->mapping = NULL; + } + + info->fbops->fb_mmap = NULL; + mutex_destroy(&fbdefio->lock); } EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);