From 5c1a56b5f616f7063f91eb85f0ea209658f387dc Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 4 Nov 2009 15:59:04 +0900 Subject: video: sh_mobile_lcdcfb: Don't attempt to map zero-length scatterlists. 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 Signed-off-by: Paul Mundt --- drivers/video/sh_mobile_lcdcfb.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) (limited to 'drivers/video') diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index 3ad5157f9899..b4b5de930cf5 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -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) -- cgit v1.2.3