fbdev: Put mmap for deferred I/O into drivers
authorThomas Zimmermann <tzimmermann@suse.de>
Fri, 29 Apr 2022 10:08:30 +0000 (12:08 +0200)
committerThomas Zimmermann <tzimmermann@suse.de>
Tue, 3 May 2022 14:04:21 +0000 (16:04 +0200)
The fbdev mmap function fb_mmap() unconditionally overrides the
driver's implementation if deferred I/O has been activated. This
makes it hard to implement mmap with anything but a vmalloc()'ed
software buffer. That is specifically a problem for DRM, where
video memory is maintained by a memory manager.

Leave the mmap handling to drivers and expect them to call the
helper for deferred I/O by thmeselves.

v4:
* unlock mm_lock in fb_mmap() error path (Dan)
v3:
* fix warning if fb_mmap is missing (kernel test robot)
v2:
* print a helpful error message if the defio setup is
  incorrect (Javier)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20220429100834.18898-2-tzimmermann@suse.de
15 files changed:
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
drivers/hid/hid-picolcd_fb.c
drivers/staging/fbtft/fbtft-core.c
drivers/video/fbdev/broadsheetfb.c
drivers/video/fbdev/core/fb_defio.c
drivers/video/fbdev/core/fbmem.c
drivers/video/fbdev/hecubafb.c
drivers/video/fbdev/hyperv_fb.c
drivers/video/fbdev/metronomefb.c
drivers/video/fbdev/sh_mobile_lcdcfb.c
drivers/video/fbdev/smscufx.c
drivers/video/fbdev/ssd1307fb.c
drivers/video/fbdev/udlfb.c
drivers/video/fbdev/xen-fbfront.c

index d265a73..d06ce0e 100644 (file)
@@ -2118,7 +2118,9 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
        struct drm_fb_helper *fb_helper = info->par;
 
-       if (fb_helper->dev->driver->gem_prime_mmap)
+       if (drm_fbdev_use_shadow_fb(fb_helper))
+               return fb_deferred_io_mmap(info, vma);
+       else if (fb_helper->dev->driver->gem_prime_mmap)
                return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
        else
                return -ENODEV;
index adf17c7..02a4a4f 100644 (file)
@@ -619,6 +619,7 @@ static const struct fb_ops vmw_fb_ops = {
        .fb_imageblit = vmw_fb_imageblit,
        .fb_pan_display = vmw_fb_pan_display,
        .fb_blank = vmw_fb_blank,
+       .fb_mmap = fb_deferred_io_mmap,
 };
 
 int vmw_fb_init(struct vmw_private *vmw_priv)
index 33c102a..78515e6 100644 (file)
@@ -428,6 +428,7 @@ static const struct fb_ops picolcdfb_ops = {
        .fb_imageblit = picolcd_fb_imageblit,
        .fb_check_var = picolcd_fb_check_var,
        .fb_set_par   = picolcd_set_par,
+       .fb_mmap      = fb_deferred_io_mmap,
 };
 
 
index 9c4d797..d4e14f7 100644 (file)
@@ -652,6 +652,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display,
        fbops->fb_imageblit =      fbtft_fb_imageblit;
        fbops->fb_setcolreg =      fbtft_fb_setcolreg;
        fbops->fb_blank     =      fbtft_fb_blank;
+       fbops->fb_mmap      =      fb_deferred_io_mmap;
 
        fbdefio->delay =           HZ / fps;
        fbdefio->sort_pagelist =   true;
index b9054f6..528bc09 100644 (file)
@@ -1055,6 +1055,7 @@ static const struct fb_ops broadsheetfb_ops = {
        .fb_fillrect    = broadsheetfb_fillrect,
        .fb_copyarea    = broadsheetfb_copyarea,
        .fb_imageblit   = broadsheetfb_imageblit,
+       .fb_mmap        = fb_deferred_io_mmap,
 };
 
 static struct fb_deferred_io broadsheetfb_defio = {
index 6aaf6d0..6924d48 100644 (file)
@@ -181,6 +181,7 @@ int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma)
        vma->vm_private_data = info;
        return 0;
 }
+EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
 
 /* workqueue callback */
 static void fb_deferred_io_work(struct work_struct *work)
index 8442747..b445a7a 100644 (file)
@@ -1334,7 +1334,6 @@ static int
 fb_mmap(struct file *file, struct vm_area_struct * vma)
 {
        struct fb_info *info = file_fb_info(file);
-       int (*fb_mmap_fn)(struct fb_info *info, struct vm_area_struct *vma);
        unsigned long mmio_pgoff;
        unsigned long start;
        u32 len;
@@ -1343,14 +1342,7 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
                return -ENODEV;
        mutex_lock(&info->mm_lock);
 
-       fb_mmap_fn = info->fbops->fb_mmap;
-
-#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
-       if (info->fbdefio)
-               fb_mmap_fn = fb_deferred_io_mmap;
-#endif
-
-       if (fb_mmap_fn) {
+       if (info->fbops->fb_mmap) {
                int res;
 
                /*
@@ -1358,9 +1350,19 @@ fb_mmap(struct file *file, struct vm_area_struct * vma)
                 * SME protection is removed ahead of the call
                 */
                vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
-               res = fb_mmap_fn(info, vma);
+               res = info->fbops->fb_mmap(info, vma);
                mutex_unlock(&info->mm_lock);
                return res;
+#if IS_ENABLED(CONFIG_FB_DEFERRED_IO)
+       } else if (info->fbdefio) {
+               /*
+                * FB deferred I/O wants you to handle mmap in your drivers. At a
+                * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
+                */
+               dev_warn_once(info->dev, "fbdev mmap not set up for deferred I/O.\n");
+               mutex_unlock(&info->mm_lock);
+               return -ENODEV;
+#endif
        }
 
        /*
index 00d7710..2c483e2 100644 (file)
@@ -204,6 +204,7 @@ static const struct fb_ops hecubafb_ops = {
        .fb_fillrect    = hecubafb_fillrect,
        .fb_copyarea    = hecubafb_copyarea,
        .fb_imageblit   = hecubafb_imageblit,
+       .fb_mmap        = fb_deferred_io_mmap,
 };
 
 static struct fb_deferred_io hecubafb_defio = {
index c8e0ea2..d7f6abf 100644 (file)
@@ -909,6 +909,7 @@ static const struct fb_ops hvfb_ops = {
        .fb_copyarea = hvfb_cfb_copyarea,
        .fb_imageblit = hvfb_cfb_imageblit,
        .fb_blank = hvfb_blank,
+       .fb_mmap = fb_deferred_io_mmap,
 };
 
 
index af858dd..2541f2f 100644 (file)
@@ -564,6 +564,7 @@ static const struct fb_ops metronomefb_ops = {
        .fb_fillrect    = metronomefb_fillrect,
        .fb_copyarea    = metronomefb_copyarea,
        .fb_imageblit   = metronomefb_imageblit,
+       .fb_mmap        = fb_deferred_io_mmap,
 };
 
 static struct fb_deferred_io metronomefb_defio = {
index 9a44174..ad034eb 100644 (file)
@@ -1480,6 +1480,9 @@ sh_mobile_lcdc_overlay_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
        struct sh_mobile_lcdc_overlay *ovl = info->par;
 
+       if (info->fbdefio)
+               return fb_deferred_io_mmap(info, vma);
+
        return dma_mmap_coherent(ovl->channel->lcdc->dev, vma, ovl->fb_mem,
                                 ovl->dma_handle, ovl->fb_size);
 }
@@ -1954,6 +1957,9 @@ sh_mobile_lcdc_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
        struct sh_mobile_lcdc_chan *ch = info->par;
 
+       if (info->fbdefio)
+               return fb_deferred_io_mmap(info, vma);
+
        return dma_mmap_coherent(ch->lcdc->dev, vma, ch->fb_mem,
                                 ch->dma_handle, ch->fb_size);
 }
index 28768c2..9383f8d 100644 (file)
@@ -779,6 +779,9 @@ static int ufx_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
        unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
        unsigned long page, pos;
 
+       if (info->fbdefio)
+               return fb_deferred_io_mmap(info, vma);
+
        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
                return -EINVAL;
        if (size > info->fix.smem_len)
index c6d5df3..7547b46 100644 (file)
@@ -368,6 +368,7 @@ static const struct fb_ops ssd1307fb_ops = {
        .fb_fillrect    = ssd1307fb_fillrect,
        .fb_copyarea    = ssd1307fb_copyarea,
        .fb_imageblit   = ssd1307fb_imageblit,
+       .fb_mmap        = fb_deferred_io_mmap,
 };
 
 static void ssd1307fb_deferred_io(struct fb_info *info,
index d280733..179b30b 100644 (file)
@@ -326,6 +326,9 @@ static int dlfb_ops_mmap(struct fb_info *info, struct vm_area_struct *vma)
        unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
        unsigned long page, pos;
 
+       if (info->fbdefio)
+               return fb_deferred_io_mmap(info, vma);
+
        if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
                return -EINVAL;
        if (size > info->fix.smem_len)
index 6826f98..6c8846e 100644 (file)
@@ -338,6 +338,7 @@ static const struct fb_ops xenfb_fb_ops = {
        .fb_imageblit   = xenfb_imageblit,
        .fb_check_var   = xenfb_check_var,
        .fb_set_par     = xenfb_set_par,
+       .fb_mmap        = fb_deferred_io_mmap,
 };
 
 static irqreturn_t xenfb_event_handler(int rq, void *dev_id)