video: sh_mobile_lcdcfb: Don't attempt to map zero-length scatterlists.
authorPaul Mundt <lethal@linux-sh.org>
Wed, 4 Nov 2009 06:59:04 +0000 (15:59 +0900)
committerPaul Mundt <lethal@linux-sh.org>
Wed, 4 Nov 2009 06:59:04 +0000 (15:59 +0900)
More aggressive DMA mapping debugging has uncovered a long-standing
buglet in the way that the sh_mobile_lcdcfb driver implements its
deferred I/O callback. When used as a console driver the acceleration
routines are called by the kernel which subsequently cause the deferred
I/O work to be scheduled, resulting in the deferred I/O callback being
entered without any dirty pages on the pagelist (the normal case for
userspace accesses). It's also possible to get in to this situation via
explicit calling of fsync() when nothing has dirtied the region.

Unfortunately it's not sufficient to skip over the callback when the
pagelist is empty given the console driver use case, so instead the
callback has to conditionalize the work for panel updates and DMA
mapping depending on whether anything is resident on the pagelist or
not.

Reported-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
drivers/video/sh_mobile_lcdcfb.c

index 3ad5157..b4b5de9 100644 (file)
@@ -281,18 +281,34 @@ static void sh_mobile_lcdc_deferred_io(struct fb_info *info,
                                       struct list_head *pagelist)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
-       unsigned int nr_pages;
 
        /* enable clocks before accessing hardware */
        sh_mobile_lcdc_clk_on(ch->lcdc);
 
-       nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
-       dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
-
-       /* trigger panel update */
-       lcdc_write_chan(ch, LDSM2R, 1);
-
-       dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+       /*
+        * It's possible to get here without anything on the pagelist via
+        * sh_mobile_lcdc_deferred_io_touch() or via a userspace fsync()
+        * invocation. In the former case, the acceleration routines are
+        * stepped in to when using the framebuffer console causing the
+        * workqueue to be scheduled without any dirty pages on the list.
+        *
+        * Despite this, a panel update is still needed given that the
+        * acceleration routines have their own methods for writing in
+        * that still need to be updated.
+        *
+        * The fsync() and empty pagelist case could be optimized for,
+        * but we don't bother, as any application exhibiting such
+        * behaviour is fundamentally broken anyways.
+        */
+       if (!list_empty(pagelist)) {
+               unsigned int nr_pages = sh_mobile_lcdc_sginit(info, pagelist);
+
+               /* trigger panel update */
+               dma_map_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+               lcdc_write_chan(ch, LDSM2R, 1);
+               dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
+       } else
+               lcdc_write_chan(ch, LDSM2R, 1);
 }
 
 static void sh_mobile_lcdc_deferred_io_touch(struct fb_info *info)