aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJaya Kumar <jayakumar.lkml@gmail.com>2007-05-08 03:37:37 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-05-08 14:15:26 -0400
commit60b59beafba875aef6d378078bce0baf2287ae14 (patch)
treebb599c0e2ad43ee8f515a9f9af009442931b6a37
parent3a2842480bbef42c3c90e14c1f378360d8c20a0c (diff)
fbdev: mm: Deferred IO support
This implements deferred IO support in fbdev. Deferred IO is a way to delay and repurpose IO. This implementation is done using mm's page_mkwrite and page_mkclean hooks in order to detect, delay and then rewrite IO. This functionality is used by hecubafb. [adaplas] This is useful for graphics hardware with no directly addressable/mappable framebuffer. Implementing this will allow the "framebuffer" to be accesible from user space via mmap(). Signed-off-by: Jaya Kumar <jayakumar.lkml@gmail.com> Signed-off-by: Antonino Daplas <adaplas@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--Documentation/fb/deferred_io.txt75
-rw-r--r--drivers/video/Kconfig5
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/fb_defio.c137
-rw-r--r--include/linux/fb.h20
-rw-r--r--mm/rmap.c1
6 files changed, 239 insertions, 0 deletions
diff --git a/Documentation/fb/deferred_io.txt b/Documentation/fb/deferred_io.txt
new file mode 100644
index 000000000000..73cf9fb7cf60
--- /dev/null
+++ b/Documentation/fb/deferred_io.txt
@@ -0,0 +1,75 @@
1Deferred IO
2-----------
3
4Deferred IO is a way to delay and repurpose IO. It uses host memory as a
5buffer and the MMU pagefault as a pretrigger for when to perform the device
6IO. The following example may be a useful explaination of how one such setup
7works:
8
9- userspace app like Xfbdev mmaps framebuffer
10- deferred IO and driver sets up nopage and page_mkwrite handlers
11- userspace app tries to write to mmaped vaddress
12- we get pagefault and reach nopage handler
13- nopage handler finds and returns physical page
14- we get page_mkwrite where we add this page to a list
15- schedule a workqueue task to be run after a delay
16- app continues writing to that page with no additional cost. this is
17 the key benefit.
18- the workqueue task comes in and mkcleans the pages on the list, then
19 completes the work associated with updating the framebuffer. this is
20 the real work talking to the device.
21- app tries to write to the address (that has now been mkcleaned)
22- get pagefault and the above sequence occurs again
23
24As can be seen from above, one benefit is roughly to allow bursty framebuffer
25writes to occur at minimum cost. Then after some time when hopefully things
26have gone quiet, we go and really update the framebuffer which would be
27a relatively more expensive operation.
28
29For some types of nonvolatile high latency displays, the desired image is
30the final image rather than the intermediate stages which is why it's okay
31to not update for each write that is occuring.
32
33It may be the case that this is useful in other scenarios as well. Paul Mundt
34has mentioned a case where it is beneficial to use the page count to decide
35whether to coalesce and issue SG DMA or to do memory bursts.
36
37Another one may be if one has a device framebuffer that is in an usual format,
38say diagonally shifting RGB, this may then be a mechanism for you to allow
39apps to pretend to have a normal framebuffer but reswizzle for the device
40framebuffer at vsync time based on the touched pagelist.
41
42How to use it: (for applications)
43---------------------------------
44No changes needed. mmap the framebuffer like normal and just use it.
45
46How to use it: (for fbdev drivers)
47----------------------------------
48The following example may be helpful.
49
501. Setup your structure. Eg:
51
52static struct fb_deferred_io hecubafb_defio = {
53 .delay = HZ,
54 .deferred_io = hecubafb_dpy_deferred_io,
55};
56
57The delay is the minimum delay between when the page_mkwrite trigger occurs
58and when the deferred_io callback is called. The deferred_io callback is
59explained below.
60
612. Setup your deferred IO callback. Eg:
62static void hecubafb_dpy_deferred_io(struct fb_info *info,
63 struct list_head *pagelist)
64
65The deferred_io callback is where you would perform all your IO to the display
66device. You receive the pagelist which is the list of pages that were written
67to during the delay. You must not modify this list. This callback is called
68from a workqueue.
69
703. Call init
71 info->fbdefio = &hecubafb_defio;
72 fb_deferred_io_init(info);
73
744. Call cleanup
75 fb_deferred_io_cleanup(info);
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 8dfa88a68ae9..61c0a03d33f2 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -91,6 +91,11 @@ config FB_CFB_IMAGEBLIT
91 blitting. This is used by drivers that don't provide their own 91 blitting. This is used by drivers that don't provide their own
92 (accelerated) version. 92 (accelerated) version.
93 93
94config FB_DEFERRED_IO
95 bool
96 depends on FB
97 default y
98
94config FB_SVGALIB 99config FB_SVGALIB
95 tristate 100 tristate
96 depends on FB 101 depends on FB
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 281462414930..deb5fa55f783 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
20obj-$(CONFIG_FB_SVGALIB) += svgalib.o 20obj-$(CONFIG_FB_SVGALIB) += svgalib.o
21obj-$(CONFIG_FB_MACMODES) += macmodes.o 21obj-$(CONFIG_FB_MACMODES) += macmodes.o
22obj-$(CONFIG_FB_DDC) += fb_ddc.o 22obj-$(CONFIG_FB_DDC) += fb_ddc.o
23obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o
23 24
24# Hardware specific drivers go first 25# Hardware specific drivers go first
25obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o 26obj-$(CONFIG_FB_AMIGA) += amifb.o c2p.o
diff --git a/drivers/video/fb_defio.c b/drivers/video/fb_defio.c
new file mode 100644
index 000000000000..cc780f4e183d
--- /dev/null
+++ b/drivers/video/fb_defio.c
@@ -0,0 +1,137 @@
1/*
2 * linux/drivers/video/fb_defio.c
3 *
4 * Copyright (C) 2006 Jaya Kumar
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file COPYING in the main directory of this archive
8 * for more details.
9 */
10
11#include <linux/module.h>
12#include <linux/kernel.h>
13#include <linux/errno.h>
14#include <linux/string.h>
15#include <linux/mm.h>
16#include <linux/slab.h>
17#include <linux/vmalloc.h>
18#include <linux/delay.h>
19#include <linux/interrupt.h>
20#include <linux/fb.h>
21#include <linux/list.h>
22#include <asm/uaccess.h>
23
24/* to support deferred IO */
25#include <linux/rmap.h>
26#include <linux/pagemap.h>
27
28/* this is to find and return the vmalloc-ed fb pages */
29static struct page* fb_deferred_io_nopage(struct vm_area_struct *vma,
30 unsigned long vaddr, int *type)
31{
32 unsigned long offset;
33 struct page *page;
34 struct fb_info *info = vma->vm_private_data;
35
36 offset = (vaddr - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
37 if (offset >= info->fix.smem_len)
38 return NOPAGE_SIGBUS;
39
40 page = vmalloc_to_page(info->screen_base + offset);
41 if (!page)
42 return NOPAGE_OOM;
43
44 get_page(page);
45 if (type)
46 *type = VM_FAULT_MINOR;
47 return page;
48}
49
50/* vm_ops->page_mkwrite handler */
51int fb_deferred_io_mkwrite(struct vm_area_struct *vma,
52 struct page *page)
53{
54 struct fb_info *info = vma->vm_private_data;
55 struct fb_deferred_io *fbdefio = info->fbdefio;
56
57 /* this is a callback we get when userspace first tries to
58 write to the page. we schedule a workqueue. that workqueue
59 will eventually mkclean the touched pages and execute the
60 deferred framebuffer IO. then if userspace touches a page
61 again, we repeat the same scheme */
62
63 /* protect against the workqueue changing the page list */
64 mutex_lock(&fbdefio->lock);
65 list_add(&page->lru, &fbdefio->pagelist);
66 mutex_unlock(&fbdefio->lock);
67
68 /* come back after delay to process the deferred IO */
69 schedule_delayed_work(&info->deferred_work, fbdefio->delay);
70 return 0;
71}
72
73static struct vm_operations_struct fb_deferred_io_vm_ops = {
74 .nopage = fb_deferred_io_nopage,
75 .page_mkwrite = fb_deferred_io_mkwrite,
76};
77
78static int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
79{
80 vma->vm_ops = &fb_deferred_io_vm_ops;
81 vma->vm_flags |= ( VM_IO | VM_RESERVED | VM_DONTEXPAND );
82 vma->vm_private_data = info;
83 return 0;
84}
85
86/* workqueue callback */
87static void fb_deferred_io_work(struct work_struct *work)
88{
89 struct fb_info *info = container_of(work, struct fb_info,
90 deferred_work.work);
91 struct list_head *node, *next;
92 struct page *cur;
93 struct fb_deferred_io *fbdefio = info->fbdefio;
94
95 /* here we mkclean the pages, then do all deferred IO */
96 mutex_lock(&fbdefio->lock);
97 list_for_each_entry(cur, &fbdefio->pagelist, lru) {
98 lock_page(cur);
99 page_mkclean(cur);
100 unlock_page(cur);
101 }
102
103 /* driver's callback with pagelist */
104 fbdefio->deferred_io(info, &fbdefio->pagelist);
105
106 /* clear the list */
107 list_for_each_safe(node, next, &fbdefio->pagelist) {
108 list_del(node);
109 }
110 mutex_unlock(&fbdefio->lock);
111}
112
113void fb_deferred_io_init(struct fb_info *info)
114{
115 struct fb_deferred_io *fbdefio = info->fbdefio;
116
117 BUG_ON(!fbdefio);
118 mutex_init(&fbdefio->lock);
119 info->fbops->fb_mmap = fb_deferred_io_mmap;
120 INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
121 INIT_LIST_HEAD(&fbdefio->pagelist);
122 if (fbdefio->delay == 0) /* set a default of 1 s */
123 fbdefio->delay = HZ;
124}
125EXPORT_SYMBOL_GPL(fb_deferred_io_init);
126
127void fb_deferred_io_cleanup(struct fb_info *info)
128{
129 struct fb_deferred_io *fbdefio = info->fbdefio;
130
131 BUG_ON(!fbdefio);
132 cancel_delayed_work(&info->deferred_work);
133 flush_scheduled_work();
134}
135EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
136
137MODULE_LICENSE("GPL");
diff --git a/include/linux/fb.h b/include/linux/fb.h
index be913ec87169..8a8255b94b62 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -561,6 +561,16 @@ struct fb_pixmap {
561 void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size); 561 void (*readio) (struct fb_info *info, void *dst, void __iomem *src, unsigned int size);
562}; 562};
563 563
564#ifdef CONFIG_FB_DEFERRED_IO
565struct fb_deferred_io {
566 /* delay between mkwrite and deferred handler */
567 unsigned long delay;
568 struct mutex lock; /* mutex that protects the page list */
569 struct list_head pagelist; /* list of touched pages */
570 /* callback */
571 void (*deferred_io)(struct fb_info *info, struct list_head *pagelist);
572};
573#endif
564 574
565/* 575/*
566 * Frame buffer operations 576 * Frame buffer operations
@@ -778,6 +788,10 @@ struct fb_info {
778 struct mutex bl_curve_mutex; 788 struct mutex bl_curve_mutex;
779 u8 bl_curve[FB_BACKLIGHT_LEVELS]; 789 u8 bl_curve[FB_BACKLIGHT_LEVELS];
780#endif 790#endif
791#ifdef CONFIG_FB_DEFERRED_IO
792 struct delayed_work deferred_work;
793 struct fb_deferred_io *fbdefio;
794#endif
781 795
782 struct fb_ops *fbops; 796 struct fb_ops *fbops;
783 struct device *device; /* This is the parent */ 797 struct device *device; /* This is the parent */
@@ -913,6 +927,12 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
913 } 927 }
914} 928}
915 929
930#ifdef CONFIG_FB_DEFERRED_IO
931/* drivers/video/fb_defio.c */
932extern void fb_deferred_io_init(struct fb_info *info);
933extern void fb_deferred_io_cleanup(struct fb_info *info);
934#endif
935
916/* drivers/video/fbsysfs.c */ 936/* drivers/video/fbsysfs.c */
917extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev); 937extern struct fb_info *framebuffer_alloc(size_t size, struct device *dev);
918extern void framebuffer_release(struct fb_info *info); 938extern void framebuffer_release(struct fb_info *info);
diff --git a/mm/rmap.c b/mm/rmap.c
index 75a32be64a21..304f51985c78 100644
--- a/mm/rmap.c
+++ b/mm/rmap.c
@@ -505,6 +505,7 @@ int page_mkclean(struct page *page)
505 505
506 return ret; 506 return ret;
507} 507}
508EXPORT_SYMBOL_GPL(page_mkclean);
508 509
509/** 510/**
510 * page_set_anon_rmap - setup new anonymous rmap 511 * page_set_anon_rmap - setup new anonymous rmap