mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 22:51:42 +00:00
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 <g.liakhovetski@gmx.de> Signed-off-by: Paul Mundt <lethal@linux-sh.org>
This commit is contained in:
parent
9016332014
commit
5c1a56b5f6
@ -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);
|
||||
/*
|
||||
* 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 */
|
||||
lcdc_write_chan(ch, LDSM2R, 1);
|
||||
|
||||
dma_unmap_sg(info->dev, ch->sglist, nr_pages, DMA_TO_DEVICE);
|
||||
/* 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)
|
||||
|
Loading…
Reference in New Issue
Block a user