All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB
@ 2022-03-03 20:58 ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Rework the fbdev deferred-I/O to not interfere with fields of struct
page. Make the code more flexible and implement GEM SHMEM mmap on top
of it.

This patchset removes the need for a special shadow framebuffer for
fbdev mmap when using GEM SHMEM. SHMEM pages are now mmap'ed from
/dev/fb directly.

Patches 2 and 3 rework the fbdev deferred I/O code. It now allows
drivers to have better control of the mmap operations. All references
to fields in struct page are gone. The rsp state is help in a 
separate pageref structure.

Patches 4 to 7 provide callbacks an helpers to implement deferred I/O
with DRM drivers. Specifically, patch 6 introduces a callback to create
a dumb buffer for fbdev. This will be useful for many drivers that
currently cannot use generic fbdev emulation because of special placement
needs of the BO, such as amdgpu or radeon. The drivers can handle the
differences to regular dumb buffers in their new callback implementation.

Patch 8 extends the GEM SHMEM memory manager with a new helper for fbdev
dumb-buffer creation. The returned BO has it's mmap set up to implement
deferred I/O with SHMEM pages. No additional shadow buffer is requires
any longer. Many drivers can immediatelly benefit from this change.

Patch 9 extends virtgpu to support fbdev dumb buffers. It's build on
top of GEM SHMEM, but has some modifications that need to be implemented
for fbdev as well.

There's no immediate fbdev performance improvement from this patchset.
Most of all, it removes unnecessary shadow framebuffers and rsp memcpys.
A shadow fb for a FullHD display is ~8 MiB, which we now save. The patches
do reduce latency between drawing to the fbdev buffer to displaying
on the screen. Watching a video on the fbdev console felt smoother and
had less flickering.

Thomas Zimmermann (9):
  drm/simpledrm: Use fbdev defaults for shadow buffering
  fbdev: Put mmap for deferred I/O into drivers
  fbdev: Track deferred-I/O pages in pageref struct
  fbdev: Export helper for implementing page_mkwrite
  drm/fb-helper: Separate deferred I/O from shadow buffers
  drm/fb-helper: Provide callback to create fbdev dumb buffers
  drm/fb-helper: Provide fbdev deferred-I/O helpers
  drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
  drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers

 drivers/gpu/drm/drm_client.c            |  14 +-
 drivers/gpu/drm/drm_crtc_internal.h     |   3 +
 drivers/gpu/drm/drm_dumb_buffers.c      |  36 +++-
 drivers/gpu/drm/drm_fb_helper.c         |  79 +++++++--
 drivers/gpu/drm/drm_gem_shmem_helper.c  | 204 ++++++++++++++++++++---
 drivers/gpu/drm/gud/gud_drv.c           |   2 +-
 drivers/gpu/drm/hyperv/hyperv_drm_drv.c |   2 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c   |   2 +-
 drivers/gpu/drm/tiny/cirrus.c           |   2 +-
 drivers/gpu/drm/tiny/gm12u320.c         |   2 +-
 drivers/gpu/drm/tiny/simpledrm.c        |   3 +-
 drivers/gpu/drm/udl/udl_drv.c           |   2 +-
 drivers/gpu/drm/virtio/virtgpu_drv.c    |   2 +
 drivers/gpu/drm/virtio/virtgpu_drv.h    |   2 +
 drivers/gpu/drm/virtio/virtgpu_object.c |  54 +++++-
 drivers/gpu/drm/vkms/vkms_drv.c         |   2 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c      |   6 +-
 drivers/hid/hid-picolcd_fb.c            |   1 +
 drivers/staging/fbtft/fbtft-core.c      |   6 +-
 drivers/video/fbdev/broadsheetfb.c      |   6 +-
 drivers/video/fbdev/core/fb_defio.c     | 211 +++++++++++++++++-------
 drivers/video/fbdev/core/fbmem.c        |  19 +--
 drivers/video/fbdev/hecubafb.c          |   1 +
 drivers/video/fbdev/hyperv_fb.c         |   6 +-
 drivers/video/fbdev/metronomefb.c       |   6 +-
 drivers/video/fbdev/sh_mobile_lcdcfb.c  |  12 +-
 drivers/video/fbdev/smscufx.c           |   8 +-
 drivers/video/fbdev/ssd1307fb.c         |   1 +
 drivers/video/fbdev/udlfb.c             |   8 +-
 drivers/video/fbdev/xen-fbfront.c       |   6 +-
 include/drm/drm_client.h                |   3 +-
 include/drm/drm_drv.h                   |  27 +++
 include/drm/drm_fb_helper.h             |   9 +
 include/drm/drm_gem_shmem_helper.h      |  63 ++++++-
 include/linux/fb.h                      |  12 +-
 35 files changed, 668 insertions(+), 154 deletions(-)


base-commit: 710a019ad85e96e66f7d75ee7f4733cdd8a2b0d0
prerequisite-patch-id: c2b2f08f0eccc9f5df0c0da49fa1d36267deb11d
prerequisite-patch-id: c67e5d886a47b7d0266d81100837557fda34cb24
prerequisite-patch-id: 6e1032c6302461624f33194c8b8f37103a3fa6ef
-- 
2.35.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB
@ 2022-03-03 20:58 ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Rework the fbdev deferred-I/O to not interfere with fields of struct
page. Make the code more flexible and implement GEM SHMEM mmap on top
of it.

This patchset removes the need for a special shadow framebuffer for
fbdev mmap when using GEM SHMEM. SHMEM pages are now mmap'ed from
/dev/fb directly.

Patches 2 and 3 rework the fbdev deferred I/O code. It now allows
drivers to have better control of the mmap operations. All references
to fields in struct page are gone. The rsp state is help in a 
separate pageref structure.

Patches 4 to 7 provide callbacks an helpers to implement deferred I/O
with DRM drivers. Specifically, patch 6 introduces a callback to create
a dumb buffer for fbdev. This will be useful for many drivers that
currently cannot use generic fbdev emulation because of special placement
needs of the BO, such as amdgpu or radeon. The drivers can handle the
differences to regular dumb buffers in their new callback implementation.

Patch 8 extends the GEM SHMEM memory manager with a new helper for fbdev
dumb-buffer creation. The returned BO has it's mmap set up to implement
deferred I/O with SHMEM pages. No additional shadow buffer is requires
any longer. Many drivers can immediatelly benefit from this change.

Patch 9 extends virtgpu to support fbdev dumb buffers. It's build on
top of GEM SHMEM, but has some modifications that need to be implemented
for fbdev as well.

There's no immediate fbdev performance improvement from this patchset.
Most of all, it removes unnecessary shadow framebuffers and rsp memcpys.
A shadow fb for a FullHD display is ~8 MiB, which we now save. The patches
do reduce latency between drawing to the fbdev buffer to displaying
on the screen. Watching a video on the fbdev console felt smoother and
had less flickering.

Thomas Zimmermann (9):
  drm/simpledrm: Use fbdev defaults for shadow buffering
  fbdev: Put mmap for deferred I/O into drivers
  fbdev: Track deferred-I/O pages in pageref struct
  fbdev: Export helper for implementing page_mkwrite
  drm/fb-helper: Separate deferred I/O from shadow buffers
  drm/fb-helper: Provide callback to create fbdev dumb buffers
  drm/fb-helper: Provide fbdev deferred-I/O helpers
  drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
  drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers

 drivers/gpu/drm/drm_client.c            |  14 +-
 drivers/gpu/drm/drm_crtc_internal.h     |   3 +
 drivers/gpu/drm/drm_dumb_buffers.c      |  36 +++-
 drivers/gpu/drm/drm_fb_helper.c         |  79 +++++++--
 drivers/gpu/drm/drm_gem_shmem_helper.c  | 204 ++++++++++++++++++++---
 drivers/gpu/drm/gud/gud_drv.c           |   2 +-
 drivers/gpu/drm/hyperv/hyperv_drm_drv.c |   2 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c   |   2 +-
 drivers/gpu/drm/tiny/cirrus.c           |   2 +-
 drivers/gpu/drm/tiny/gm12u320.c         |   2 +-
 drivers/gpu/drm/tiny/simpledrm.c        |   3 +-
 drivers/gpu/drm/udl/udl_drv.c           |   2 +-
 drivers/gpu/drm/virtio/virtgpu_drv.c    |   2 +
 drivers/gpu/drm/virtio/virtgpu_drv.h    |   2 +
 drivers/gpu/drm/virtio/virtgpu_object.c |  54 +++++-
 drivers/gpu/drm/vkms/vkms_drv.c         |   2 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c      |   6 +-
 drivers/hid/hid-picolcd_fb.c            |   1 +
 drivers/staging/fbtft/fbtft-core.c      |   6 +-
 drivers/video/fbdev/broadsheetfb.c      |   6 +-
 drivers/video/fbdev/core/fb_defio.c     | 211 +++++++++++++++++-------
 drivers/video/fbdev/core/fbmem.c        |  19 +--
 drivers/video/fbdev/hecubafb.c          |   1 +
 drivers/video/fbdev/hyperv_fb.c         |   6 +-
 drivers/video/fbdev/metronomefb.c       |   6 +-
 drivers/video/fbdev/sh_mobile_lcdcfb.c  |  12 +-
 drivers/video/fbdev/smscufx.c           |   8 +-
 drivers/video/fbdev/ssd1307fb.c         |   1 +
 drivers/video/fbdev/udlfb.c             |   8 +-
 drivers/video/fbdev/xen-fbfront.c       |   6 +-
 include/drm/drm_client.h                |   3 +-
 include/drm/drm_drv.h                   |  27 +++
 include/drm/drm_fb_helper.h             |   9 +
 include/drm/drm_gem_shmem_helper.h      |  63 ++++++-
 include/linux/fb.h                      |  12 +-
 35 files changed, 668 insertions(+), 154 deletions(-)


base-commit: 710a019ad85e96e66f7d75ee7f4733cdd8a2b0d0
prerequisite-patch-id: c2b2f08f0eccc9f5df0c0da49fa1d36267deb11d
prerequisite-patch-id: c67e5d886a47b7d0266d81100837557fda34cb24
prerequisite-patch-id: 6e1032c6302461624f33194c8b8f37103a3fa6ef
-- 
2.35.1


^ permalink raw reply	[flat|nested] 62+ messages in thread

* [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Don't select shadow buffering for the fbdev console explicitly. The
fbdev emulation's heuristic will enable it for any framebuffer with
.dirty callback.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/tiny/simpledrm.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c
index f5b8e864a5cd..768242a78e2b 100644
--- a/drivers/gpu/drm/tiny/simpledrm.c
+++ b/drivers/gpu/drm/tiny/simpledrm.c
@@ -801,7 +801,6 @@ static int simpledrm_device_init_modeset(struct simpledrm_device *sdev)
 	dev->mode_config.max_width = max_width;
 	dev->mode_config.min_height = mode->vdisplay;
 	dev->mode_config.max_height = max_height;
-	dev->mode_config.prefer_shadow_fbdev = true;
 	dev->mode_config.preferred_depth = sdev->format->cpp[0] * 8;
 	dev->mode_config.funcs = &simpledrm_mode_config_funcs;
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Don't select shadow buffering for the fbdev console explicitly. The
fbdev emulation's heuristic will enable it for any framebuffer with
.dirty callback.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/tiny/simpledrm.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c
index f5b8e864a5cd..768242a78e2b 100644
--- a/drivers/gpu/drm/tiny/simpledrm.c
+++ b/drivers/gpu/drm/tiny/simpledrm.c
@@ -801,7 +801,6 @@ static int simpledrm_device_init_modeset(struct simpledrm_device *sdev)
 	dev->mode_config.max_width = max_width;
 	dev->mode_config.min_height = mode->vdisplay;
 	dev->mode_config.max_height = max_height;
-	dev->mode_config.prefer_shadow_fbdev = true;
 	dev->mode_config.preferred_depth = sdev->format->cpp[0] * 8;
 	dev->mode_config.funcs = &simpledrm_mode_config_funcs;
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 2/9] fbdev: Put mmap for deferred I/O into drivers
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

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.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c        |  4 +++-
 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c     |  1 +
 drivers/hid/hid-picolcd_fb.c           |  1 +
 drivers/staging/fbtft/fbtft-core.c     |  1 +
 drivers/video/fbdev/broadsheetfb.c     |  1 +
 drivers/video/fbdev/core/fb_defio.c    |  1 +
 drivers/video/fbdev/core/fbmem.c       | 19 +++++++++----------
 drivers/video/fbdev/hecubafb.c         |  1 +
 drivers/video/fbdev/hyperv_fb.c        |  1 +
 drivers/video/fbdev/metronomefb.c      |  1 +
 drivers/video/fbdev/sh_mobile_lcdcfb.c |  6 ++++++
 drivers/video/fbdev/smscufx.c          |  3 +++
 drivers/video/fbdev/ssd1307fb.c        |  1 +
 drivers/video/fbdev/udlfb.c            |  3 +++
 drivers/video/fbdev/xen-fbfront.c      |  1 +
 15 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d265a73313c9..d06ce0e92d66 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -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;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 8ee34576c7d0..33f355907fbb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -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)
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
index 33c102a60992..78515e6bacf0 100644
--- a/drivers/hid/hid-picolcd_fb.c
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -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,
 };
 
 
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index 4a35347b3020..60c55451e414 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -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;
diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
index b9054f658838..528bc0902338 100644
--- a/drivers/video/fbdev/broadsheetfb.c
+++ b/drivers/video/fbdev/broadsheetfb.c
@@ -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 = {
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 98b0f23bf5e2..662bb5d2b872 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -189,6 +189,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)
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index ad9aac06427a..6d89e65592a8 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1338,7 +1338,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;
@@ -1347,14 +1346,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;
 
 		/*
@@ -1362,11 +1354,18 @@ 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;
 	}
 
+	/*
+	 * FB deferred I/O want you to handle mmap in your drivers. At a
+	 * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
+	 */
+	if (WARN_ON_ONCE(info->fbdefio))
+		return -ENODEV;
+
 	/*
 	 * Ugh. This can be either the frame buffer mapping, or
 	 * if pgoff points past it, the mmio mapping.
diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c
index 00d77105161a..2c483e2cf9ec 100644
--- a/drivers/video/fbdev/hecubafb.c
+++ b/drivers/video/fbdev/hecubafb.c
@@ -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 = {
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index c8e0ea27caf1..d7f6abf827b9 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -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,
 };
 
 
diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
index af858dd23ea6..2541f2fe065b 100644
--- a/drivers/video/fbdev/metronomefb.c
+++ b/drivers/video/fbdev/metronomefb.c
@@ -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 = {
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index aa4ebe3192ec..1dc5079e4518 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -1483,6 +1483,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);
 }
@@ -1957,6 +1960,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);
 }
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
index bfac3ee4a642..10eb56feff0a 100644
--- a/drivers/video/fbdev/smscufx.c
+++ b/drivers/video/fbdev/smscufx.c
@@ -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)
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 1e2f71c2f8a8..5eed08bb165b 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -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,
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index 184bb8433b78..d7f02b935eb2 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -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)
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 6826f986da43..6c8846eba2fb 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -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)
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 2/9] fbdev: Put mmap for deferred I/O into drivers
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

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.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c        |  4 +++-
 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c     |  1 +
 drivers/hid/hid-picolcd_fb.c           |  1 +
 drivers/staging/fbtft/fbtft-core.c     |  1 +
 drivers/video/fbdev/broadsheetfb.c     |  1 +
 drivers/video/fbdev/core/fb_defio.c    |  1 +
 drivers/video/fbdev/core/fbmem.c       | 19 +++++++++----------
 drivers/video/fbdev/hecubafb.c         |  1 +
 drivers/video/fbdev/hyperv_fb.c        |  1 +
 drivers/video/fbdev/metronomefb.c      |  1 +
 drivers/video/fbdev/sh_mobile_lcdcfb.c |  6 ++++++
 drivers/video/fbdev/smscufx.c          |  3 +++
 drivers/video/fbdev/ssd1307fb.c        |  1 +
 drivers/video/fbdev/udlfb.c            |  3 +++
 drivers/video/fbdev/xen-fbfront.c      |  1 +
 15 files changed, 34 insertions(+), 11 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d265a73313c9..d06ce0e92d66 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -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;
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 8ee34576c7d0..33f355907fbb 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -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)
diff --git a/drivers/hid/hid-picolcd_fb.c b/drivers/hid/hid-picolcd_fb.c
index 33c102a60992..78515e6bacf0 100644
--- a/drivers/hid/hid-picolcd_fb.c
+++ b/drivers/hid/hid-picolcd_fb.c
@@ -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,
 };
 
 
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index 4a35347b3020..60c55451e414 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -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;
diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
index b9054f658838..528bc0902338 100644
--- a/drivers/video/fbdev/broadsheetfb.c
+++ b/drivers/video/fbdev/broadsheetfb.c
@@ -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 = {
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 98b0f23bf5e2..662bb5d2b872 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -189,6 +189,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)
diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c
index ad9aac06427a..6d89e65592a8 100644
--- a/drivers/video/fbdev/core/fbmem.c
+++ b/drivers/video/fbdev/core/fbmem.c
@@ -1338,7 +1338,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;
@@ -1347,14 +1346,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;
 
 		/*
@@ -1362,11 +1354,18 @@ 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;
 	}
 
+	/*
+	 * FB deferred I/O want you to handle mmap in your drivers. At a
+	 * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
+	 */
+	if (WARN_ON_ONCE(info->fbdefio))
+		return -ENODEV;
+
 	/*
 	 * Ugh. This can be either the frame buffer mapping, or
 	 * if pgoff points past it, the mmio mapping.
diff --git a/drivers/video/fbdev/hecubafb.c b/drivers/video/fbdev/hecubafb.c
index 00d77105161a..2c483e2cf9ec 100644
--- a/drivers/video/fbdev/hecubafb.c
+++ b/drivers/video/fbdev/hecubafb.c
@@ -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 = {
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index c8e0ea27caf1..d7f6abf827b9 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -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,
 };
 
 
diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
index af858dd23ea6..2541f2fe065b 100644
--- a/drivers/video/fbdev/metronomefb.c
+++ b/drivers/video/fbdev/metronomefb.c
@@ -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 = {
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index aa4ebe3192ec..1dc5079e4518 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -1483,6 +1483,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);
 }
@@ -1957,6 +1960,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);
 }
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
index bfac3ee4a642..10eb56feff0a 100644
--- a/drivers/video/fbdev/smscufx.c
+++ b/drivers/video/fbdev/smscufx.c
@@ -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)
diff --git a/drivers/video/fbdev/ssd1307fb.c b/drivers/video/fbdev/ssd1307fb.c
index 1e2f71c2f8a8..5eed08bb165b 100644
--- a/drivers/video/fbdev/ssd1307fb.c
+++ b/drivers/video/fbdev/ssd1307fb.c
@@ -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,
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index 184bb8433b78..d7f02b935eb2 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -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)
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 6826f986da43..6c8846eba2fb 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -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)
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Store the per-page state for fbdev's deferred I/O in struct
fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
that have to be flushed out to video memory. Update all affected
drivers.

Like with pages before, fbdev acquires a pageref when an mmaped page
of the framebuffer is being written to. It holds the pageref in a
list of all currently written pagerefs until it flushes the written
pages to video memory. Writeback occurs periodically. After writeback
fbdev releases all pagerefs and builds up a new dirty list until the
next writeback occurs.

Using pagerefs has a number of benefits.

For pages of the framebuffer, the deferred I/O code used struct
page.lru as an entry into the list of dirty pages. The lru field is
owned by the page cache, which makes deferred I/O incompatible with
some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
struct fb_deferred_io_pageref now provides an entry into a list of
dirty framebuffer pages, free'ing lru for use with the page cache.

Drivers also assumed that struct page.index is the page offset into
the framebuffer. This is not true for DRM buffers, which are located
at various offset within a mapped area. struct fb_deferred_io_pageref
explicitly stores an offset into the framebuffer. struct page.index
is now only the page offset into the mapped area.

These changes will allow DRM to use fbdev deferred I/O without an
intermediate shadow buffer.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c        |   6 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c     |   5 +-
 drivers/staging/fbtft/fbtft-core.c     |   5 +-
 drivers/video/fbdev/broadsheetfb.c     |   5 +-
 drivers/video/fbdev/core/fb_defio.c    | 153 +++++++++++++++++--------
 drivers/video/fbdev/hyperv_fb.c        |   5 +-
 drivers/video/fbdev/metronomefb.c      |   5 +-
 drivers/video/fbdev/sh_mobile_lcdcfb.c |   6 +-
 drivers/video/fbdev/smscufx.c          |   5 +-
 drivers/video/fbdev/udlfb.c            |   5 +-
 drivers/video/fbdev/xen-fbfront.c      |   5 +-
 include/linux/fb.h                     |  11 +-
 12 files changed, 147 insertions(+), 69 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d06ce0e92d66..dd1d72d58b35 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -717,13 +717,13 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
 			       struct list_head *pagelist)
 {
 	unsigned long start, end, min, max;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	struct drm_rect damage_area;
 
 	min = ULONG_MAX;
 	max = 0;
-	list_for_each_entry(page, pagelist, lru) {
-		start = page->index << PAGE_SHIFT;
+	list_for_each_entry(pageref, pagelist, list) {
+		start = pageref->offset;
 		end = start + PAGE_SIZE;
 		min = min(min, start);
 		max = max(max, end);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 33f355907fbb..a35f695727c9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
 	struct vmw_fb_par *par = info->par;
 	unsigned long start, end, min, max;
 	unsigned long flags;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	int y1, y2;
 
 	min = ULONG_MAX;
 	max = 0;
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		start = page->index << PAGE_SHIFT;
 		end = start + PAGE_SIZE - 1;
 		min = min(min, start);
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index 60c55451e414..8c596c7948f5 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -326,7 +326,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
 {
 	struct fbtft_par *par = info->par;
 	unsigned int dirty_lines_start, dirty_lines_end;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	unsigned long index;
 	unsigned int y_low = 0, y_high = 0;
 	int count = 0;
@@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
 	spin_unlock(&par->dirty_lock);
 
 	/* Mark display lines as dirty */
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		count++;
 		index = page->index << PAGE_SHIFT;
 		y_low = index / info->fix.line_length;
diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
index 528bc0902338..6afc6ef4cb5e 100644
--- a/drivers/video/fbdev/broadsheetfb.c
+++ b/drivers/video/fbdev/broadsheetfb.c
@@ -934,7 +934,7 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
 {
 	u16 y1 = 0, h = 0;
 	int prev_index = -1;
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	int h_inc;
 	u16 yres = info->var.yres;
@@ -944,7 +944,8 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
 	h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
 
 	/* walk the written page list and swizzle the data */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 		if (prev_index < 0) {
 			/* just starting so assign first page */
 			y1 = (cur->index << PAGE_SHIFT) / xres;
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 662bb5d2b872..20c6eb94a685 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -36,6 +36,60 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs
 	return page;
 }
 
+static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info,
+								 unsigned long offset,
+								 struct page *page)
+{
+	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct list_head *pos = &fbdefio->pagelist;
+	unsigned long pgoff = offset >> PAGE_SHIFT;
+	struct fb_deferred_io_pageref *pageref, *cur;
+
+	if (WARN_ON_ONCE(pgoff >= info->npagerefs))
+		return NULL; /* incorrect allocation size */
+
+	/* 1:1 mapping between pageref and page offset */
+	pageref = &info->pagerefs[pgoff];
+
+	/*
+	 * This check is to catch the case where a new process could start
+	 * writing to the same page through a new PTE. This new access
+	 * can cause a call to .page_mkwrite even if the original process'
+	 * PTE is marked writable.
+	 */
+	if (!list_empty(&pageref->list))
+		goto pageref_already_added;
+
+	pageref->page = page;
+	pageref->offset = pgoff << PAGE_SHIFT;
+
+	if (unlikely(fbdefio->sort_pagelist)) {
+		/*
+		 * We loop through the list of pagerefs before adding, in
+		 * order to keep the pagerefs sorted. This has significant
+		 * overhead of O(n^2) with n being the number of written
+		 * pages. If possible, drivers should try to work with
+		 * unsorted page lists instead.
+		 */
+		list_for_each_entry(cur, &info->fbdefio->pagelist, list) {
+			if (cur > pageref)
+				break;
+		}
+		pos = &cur->list;
+	}
+
+	list_add_tail(&pageref->list, pos);
+
+pageref_already_added:
+	return pageref;
+}
+
+static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
+				       struct fb_info *info)
+{
+	list_del_init(&pageref->list);
+}
+
 /* this is to find and return the vmalloc-ed fb pages */
 static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
 {
@@ -59,8 +113,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
 		printk(KERN_ERR "no mapping available\n");
 
 	BUG_ON(!page->mapping);
-	INIT_LIST_HEAD(&page->lru);
-	page->index = vmf->pgoff;
+	page->index = vmf->pgoff; /* for page_mkclean() */
 
 	vmf->page = page;
 	return 0;
@@ -96,7 +149,11 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	struct page *page = vmf->page;
 	struct fb_info *info = vmf->vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
-	struct list_head *pos = &fbdefio->pagelist;
+	struct fb_deferred_io_pageref *pageref;
+	unsigned long offset;
+	vm_fault_t ret;
+
+	offset = (vmf->address - vmf->vma->vm_start);
 
 	/* this is a callback we get when userspace first tries to
 	write to the page. we schedule a workqueue. that workqueue
@@ -113,6 +170,12 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
 		fbdefio->first_io(info);
 
+	pageref = fb_deferred_io_pageref_get(info, offset, page);
+	if (WARN_ON_ONCE(!pageref)) {
+		ret = VM_FAULT_OOM;
+		goto err_mutex_unlock;
+	}
+
 	/*
 	 * We want the page to remain locked from ->page_mkwrite until
 	 * the PTE is marked dirty to avoid page_mkclean() being called
@@ -121,47 +184,17 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	 * Do this by locking the page here and informing the caller
 	 * about it with VM_FAULT_LOCKED.
 	 */
-	lock_page(page);
-
-	/*
-	 * This check is to catch the case where a new process could start
-	 * writing to the same page through a new PTE. This new access
-	 * can cause a call to .page_mkwrite even if the original process'
-	 * PTE is marked writable.
-	 *
-	 * TODO: The lru field is owned by the page cache; hence the name.
-	 *       We dequeue in fb_deferred_io_work() after flushing the
-	 *       page's content into video memory. Instead of lru, fbdefio
-	 *       should have it's own field.
-	 */
-	if (!list_empty(&page->lru))
-		goto page_already_added;
-
-	if (unlikely(fbdefio->sort_pagelist)) {
-		/*
-		 * We loop through the pagelist before adding in order to
-		 * keep the pagelist sorted. This has significant overhead
-		 * of O(n^2) with n being the number of written pages. If
-		 * possible, drivers should try to work with unsorted page
-		 * lists instead.
-		 */
-		struct page *cur;
-
-		list_for_each_entry(cur, &fbdefio->pagelist, lru) {
-			if (cur->index > page->index)
-				break;
-		}
-		pos = &cur->lru;
-	}
-
-	list_add_tail(&page->lru, pos);
+	lock_page(pageref->page);
 
-page_already_added:
 	mutex_unlock(&fbdefio->lock);
 
 	/* come back after delay to process the deferred IO */
 	schedule_delayed_work(&info->deferred_work, fbdefio->delay);
 	return VM_FAULT_LOCKED;
+
+err_mutex_unlock:
+	mutex_unlock(&fbdefio->lock);
+	return ret;
 }
 
 static const struct vm_operations_struct fb_deferred_io_vm_ops = {
@@ -194,15 +227,14 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
 /* workqueue callback */
 static void fb_deferred_io_work(struct work_struct *work)
 {
-	struct fb_info *info = container_of(work, struct fb_info,
-						deferred_work.work);
-	struct list_head *node, *next;
-	struct page *cur;
+	struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
+	struct fb_deferred_io_pageref *pageref, *next;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 
 	/* here we mkclean the pages, then do all deferred IO */
 	mutex_lock(&fbdefio->lock);
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 		lock_page(cur);
 		page_mkclean(cur);
 		unlock_page(cur);
@@ -212,22 +244,48 @@ static void fb_deferred_io_work(struct work_struct *work)
 	fbdefio->deferred_io(info, &fbdefio->pagelist);
 
 	/* clear the list */
-	list_for_each_safe(node, next, &fbdefio->pagelist) {
-		list_del_init(node);
-	}
+	list_for_each_entry_safe(pageref, next, &fbdefio->pagelist, list)
+		fb_deferred_io_pageref_put(pageref, info);
+
 	mutex_unlock(&fbdefio->lock);
 }
 
-void fb_deferred_io_init(struct fb_info *info)
+int fb_deferred_io_init(struct fb_info *info)
 {
 	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct fb_deferred_io_pageref *pagerefs;
+	unsigned long npagerefs, i;
+	int ret;
 
 	BUG_ON(!fbdefio);
+
+	if (WARN_ON(!info->fix.smem_len))
+		return -EINVAL;
+
 	mutex_init(&fbdefio->lock);
 	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
 	INIT_LIST_HEAD(&fbdefio->pagelist);
 	if (fbdefio->delay == 0) /* set a default of 1 s */
 		fbdefio->delay = HZ;
+
+	npagerefs = DIV_ROUND_UP(info->fix.smem_len, PAGE_SIZE);
+
+	/* alloc a page ref for each page of the display memory */
+	pagerefs = kvcalloc(npagerefs, sizeof(*pagerefs), GFP_KERNEL);
+	if (!pagerefs) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	for (i = 0; i < npagerefs; ++i)
+		INIT_LIST_HEAD(&pagerefs[i].list);
+	info->npagerefs = npagerefs;
+	info->pagerefs = pagerefs;
+
+	return 0;
+
+err:
+	mutex_destroy(&fbdefio->lock);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_init);
 
@@ -254,6 +312,7 @@ void fb_deferred_io_cleanup(struct fb_info *info)
 		page->mapping = NULL;
 	}
 
+	kvfree(info->pagerefs);
 	mutex_destroy(&fbdefio->lock);
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index d7f6abf827b9..51eb57ea68f7 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -424,7 +424,7 @@ static void synthvid_deferred_io(struct fb_info *p,
 				 struct list_head *pagelist)
 {
 	struct hvfb_par *par = p->par;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	unsigned long start, end;
 	int y1, y2, miny, maxy;
 
@@ -437,7 +437,8 @@ static void synthvid_deferred_io(struct fb_info *p,
 	 * in synthvid_update function by clamping the y2
 	 * value to yres.
 	 */
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		start = page->index << PAGE_SHIFT;
 		end = start + PAGE_SIZE - 1;
 		y1 = start / p->fix.line_length;
diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
index 2541f2fe065b..8352fa3f4cef 100644
--- a/drivers/video/fbdev/metronomefb.c
+++ b/drivers/video/fbdev/metronomefb.c
@@ -469,12 +469,13 @@ static void metronomefb_dpy_deferred_io(struct fb_info *info,
 				struct list_head *pagelist)
 {
 	u16 cksum;
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct metronomefb_par *par = info->par;
 
 	/* walk the written page list and swizzle the data */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 		cksum = metronomefb_dpy_update_page(par,
 					(cur->index << PAGE_SHIFT));
 		par->metromem_img_csum -= par->csum_table[cur->index];
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index 1dc5079e4518..7fcc85352033 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -440,13 +440,15 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 	unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	int nr_pages = 0;
 
 	sg_init_table(ch->sglist, nr_pages_max);
 
-	list_for_each_entry(page, pagelist, lru)
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
+	}
 
 	return nr_pages;
 }
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
index 10eb56feff0a..cabc3d03897c 100644
--- a/drivers/video/fbdev/smscufx.c
+++ b/drivers/video/fbdev/smscufx.c
@@ -958,7 +958,7 @@ static void ufx_ops_fillrect(struct fb_info *info,
 static void ufx_dpy_deferred_io(struct fb_info *info,
 				struct list_head *pagelist)
 {
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct ufx_data *dev = info->par;
 
@@ -969,9 +969,10 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
 		return;
 
 	/* walk the written page list and render each to device */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
 		/* create a rectangle of full screen width that encloses the
 		 * entire dirty framebuffer page */
+		struct page *cur = pageref->page;
 		const int x = 0;
 		const int width = dev->info->var.xres;
 		const int y = (cur->index << PAGE_SHIFT) / (width * 2);
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index d7f02b935eb2..2d971c45bd93 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -784,7 +784,7 @@ static void dlfb_ops_fillrect(struct fb_info *info,
 static void dlfb_dpy_deferred_io(struct fb_info *info,
 				struct list_head *pagelist)
 {
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct dlfb_data *dlfb = info->par;
 	struct urb *urb;
@@ -811,7 +811,8 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
 	cmd = urb->transfer_buffer;
 
 	/* walk the written page list and render each to device */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 
 		if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
 				  &cmd, cur->index << PAGE_SHIFT,
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 6c8846eba2fb..608fcde767d3 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -185,13 +185,14 @@ static void xenfb_deferred_io(struct fb_info *fb_info,
 			      struct list_head *pagelist)
 {
 	struct xenfb_info *info = fb_info->par;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	unsigned long beg, end;
 	int y1, y2, miny, maxy;
 
 	miny = INT_MAX;
 	maxy = 0;
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		beg = page->index << PAGE_SHIFT;
 		end = beg + PAGE_SIZE - 1;
 		y1 = beg / fb_info->fix.line_length;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 9a77ab615c36..768de6534a82 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -201,6 +201,13 @@ struct fb_pixmap {
 };
 
 #ifdef CONFIG_FB_DEFERRED_IO
+struct fb_deferred_io_pageref {
+	struct page *page;
+	unsigned long offset;
+	/* private */
+	struct list_head list;
+};
+
 struct fb_deferred_io {
 	/* delay between mkwrite and deferred handler */
 	unsigned long delay;
@@ -469,6 +476,8 @@ struct fb_info {
 #endif
 #ifdef CONFIG_FB_DEFERRED_IO
 	struct delayed_work deferred_work;
+	unsigned long npagerefs;
+	struct fb_deferred_io_pageref *pagerefs;
 	struct fb_deferred_io *fbdefio;
 #endif
 
@@ -662,7 +671,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
 
 /* drivers/video/fb_defio.c */
 int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
-extern void fb_deferred_io_init(struct fb_info *info);
+extern int  fb_deferred_io_init(struct fb_info *info);
 extern void fb_deferred_io_open(struct fb_info *info,
 				struct inode *inode,
 				struct file *file);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Store the per-page state for fbdev's deferred I/O in struct
fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
that have to be flushed out to video memory. Update all affected
drivers.

Like with pages before, fbdev acquires a pageref when an mmaped page
of the framebuffer is being written to. It holds the pageref in a
list of all currently written pagerefs until it flushes the written
pages to video memory. Writeback occurs periodically. After writeback
fbdev releases all pagerefs and builds up a new dirty list until the
next writeback occurs.

Using pagerefs has a number of benefits.

For pages of the framebuffer, the deferred I/O code used struct
page.lru as an entry into the list of dirty pages. The lru field is
owned by the page cache, which makes deferred I/O incompatible with
some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
struct fb_deferred_io_pageref now provides an entry into a list of
dirty framebuffer pages, free'ing lru for use with the page cache.

Drivers also assumed that struct page.index is the page offset into
the framebuffer. This is not true for DRM buffers, which are located
at various offset within a mapped area. struct fb_deferred_io_pageref
explicitly stores an offset into the framebuffer. struct page.index
is now only the page offset into the mapped area.

These changes will allow DRM to use fbdev deferred I/O without an
intermediate shadow buffer.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c        |   6 +-
 drivers/gpu/drm/vmwgfx/vmwgfx_fb.c     |   5 +-
 drivers/staging/fbtft/fbtft-core.c     |   5 +-
 drivers/video/fbdev/broadsheetfb.c     |   5 +-
 drivers/video/fbdev/core/fb_defio.c    | 153 +++++++++++++++++--------
 drivers/video/fbdev/hyperv_fb.c        |   5 +-
 drivers/video/fbdev/metronomefb.c      |   5 +-
 drivers/video/fbdev/sh_mobile_lcdcfb.c |   6 +-
 drivers/video/fbdev/smscufx.c          |   5 +-
 drivers/video/fbdev/udlfb.c            |   5 +-
 drivers/video/fbdev/xen-fbfront.c      |   5 +-
 include/linux/fb.h                     |  11 +-
 12 files changed, 147 insertions(+), 69 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d06ce0e92d66..dd1d72d58b35 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -717,13 +717,13 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
 			       struct list_head *pagelist)
 {
 	unsigned long start, end, min, max;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	struct drm_rect damage_area;
 
 	min = ULONG_MAX;
 	max = 0;
-	list_for_each_entry(page, pagelist, lru) {
-		start = page->index << PAGE_SHIFT;
+	list_for_each_entry(pageref, pagelist, list) {
+		start = pageref->offset;
 		end = start + PAGE_SIZE;
 		min = min(min, start);
 		max = max(max, end);
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
index 33f355907fbb..a35f695727c9 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
@@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
 	struct vmw_fb_par *par = info->par;
 	unsigned long start, end, min, max;
 	unsigned long flags;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	int y1, y2;
 
 	min = ULONG_MAX;
 	max = 0;
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		start = page->index << PAGE_SHIFT;
 		end = start + PAGE_SIZE - 1;
 		min = min(min, start);
diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c
index 60c55451e414..8c596c7948f5 100644
--- a/drivers/staging/fbtft/fbtft-core.c
+++ b/drivers/staging/fbtft/fbtft-core.c
@@ -326,7 +326,7 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
 {
 	struct fbtft_par *par = info->par;
 	unsigned int dirty_lines_start, dirty_lines_end;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	unsigned long index;
 	unsigned int y_low = 0, y_high = 0;
 	int count = 0;
@@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
 	spin_unlock(&par->dirty_lock);
 
 	/* Mark display lines as dirty */
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		count++;
 		index = page->index << PAGE_SHIFT;
 		y_low = index / info->fix.line_length;
diff --git a/drivers/video/fbdev/broadsheetfb.c b/drivers/video/fbdev/broadsheetfb.c
index 528bc0902338..6afc6ef4cb5e 100644
--- a/drivers/video/fbdev/broadsheetfb.c
+++ b/drivers/video/fbdev/broadsheetfb.c
@@ -934,7 +934,7 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
 {
 	u16 y1 = 0, h = 0;
 	int prev_index = -1;
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	int h_inc;
 	u16 yres = info->var.yres;
@@ -944,7 +944,8 @@ static void broadsheetfb_dpy_deferred_io(struct fb_info *info,
 	h_inc = DIV_ROUND_UP(PAGE_SIZE , xres);
 
 	/* walk the written page list and swizzle the data */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 		if (prev_index < 0) {
 			/* just starting so assign first page */
 			y1 = (cur->index << PAGE_SHIFT) / xres;
diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 662bb5d2b872..20c6eb94a685 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -36,6 +36,60 @@ static struct page *fb_deferred_io_page(struct fb_info *info, unsigned long offs
 	return page;
 }
 
+static struct fb_deferred_io_pageref *fb_deferred_io_pageref_get(struct fb_info *info,
+								 unsigned long offset,
+								 struct page *page)
+{
+	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct list_head *pos = &fbdefio->pagelist;
+	unsigned long pgoff = offset >> PAGE_SHIFT;
+	struct fb_deferred_io_pageref *pageref, *cur;
+
+	if (WARN_ON_ONCE(pgoff >= info->npagerefs))
+		return NULL; /* incorrect allocation size */
+
+	/* 1:1 mapping between pageref and page offset */
+	pageref = &info->pagerefs[pgoff];
+
+	/*
+	 * This check is to catch the case where a new process could start
+	 * writing to the same page through a new PTE. This new access
+	 * can cause a call to .page_mkwrite even if the original process'
+	 * PTE is marked writable.
+	 */
+	if (!list_empty(&pageref->list))
+		goto pageref_already_added;
+
+	pageref->page = page;
+	pageref->offset = pgoff << PAGE_SHIFT;
+
+	if (unlikely(fbdefio->sort_pagelist)) {
+		/*
+		 * We loop through the list of pagerefs before adding, in
+		 * order to keep the pagerefs sorted. This has significant
+		 * overhead of O(n^2) with n being the number of written
+		 * pages. If possible, drivers should try to work with
+		 * unsorted page lists instead.
+		 */
+		list_for_each_entry(cur, &info->fbdefio->pagelist, list) {
+			if (cur > pageref)
+				break;
+		}
+		pos = &cur->list;
+	}
+
+	list_add_tail(&pageref->list, pos);
+
+pageref_already_added:
+	return pageref;
+}
+
+static void fb_deferred_io_pageref_put(struct fb_deferred_io_pageref *pageref,
+				       struct fb_info *info)
+{
+	list_del_init(&pageref->list);
+}
+
 /* this is to find and return the vmalloc-ed fb pages */
 static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
 {
@@ -59,8 +113,7 @@ static vm_fault_t fb_deferred_io_fault(struct vm_fault *vmf)
 		printk(KERN_ERR "no mapping available\n");
 
 	BUG_ON(!page->mapping);
-	INIT_LIST_HEAD(&page->lru);
-	page->index = vmf->pgoff;
+	page->index = vmf->pgoff; /* for page_mkclean() */
 
 	vmf->page = page;
 	return 0;
@@ -96,7 +149,11 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	struct page *page = vmf->page;
 	struct fb_info *info = vmf->vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
-	struct list_head *pos = &fbdefio->pagelist;
+	struct fb_deferred_io_pageref *pageref;
+	unsigned long offset;
+	vm_fault_t ret;
+
+	offset = (vmf->address - vmf->vma->vm_start);
 
 	/* this is a callback we get when userspace first tries to
 	write to the page. we schedule a workqueue. that workqueue
@@ -113,6 +170,12 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
 		fbdefio->first_io(info);
 
+	pageref = fb_deferred_io_pageref_get(info, offset, page);
+	if (WARN_ON_ONCE(!pageref)) {
+		ret = VM_FAULT_OOM;
+		goto err_mutex_unlock;
+	}
+
 	/*
 	 * We want the page to remain locked from ->page_mkwrite until
 	 * the PTE is marked dirty to avoid page_mkclean() being called
@@ -121,47 +184,17 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	 * Do this by locking the page here and informing the caller
 	 * about it with VM_FAULT_LOCKED.
 	 */
-	lock_page(page);
-
-	/*
-	 * This check is to catch the case where a new process could start
-	 * writing to the same page through a new PTE. This new access
-	 * can cause a call to .page_mkwrite even if the original process'
-	 * PTE is marked writable.
-	 *
-	 * TODO: The lru field is owned by the page cache; hence the name.
-	 *       We dequeue in fb_deferred_io_work() after flushing the
-	 *       page's content into video memory. Instead of lru, fbdefio
-	 *       should have it's own field.
-	 */
-	if (!list_empty(&page->lru))
-		goto page_already_added;
-
-	if (unlikely(fbdefio->sort_pagelist)) {
-		/*
-		 * We loop through the pagelist before adding in order to
-		 * keep the pagelist sorted. This has significant overhead
-		 * of O(n^2) with n being the number of written pages. If
-		 * possible, drivers should try to work with unsorted page
-		 * lists instead.
-		 */
-		struct page *cur;
-
-		list_for_each_entry(cur, &fbdefio->pagelist, lru) {
-			if (cur->index > page->index)
-				break;
-		}
-		pos = &cur->lru;
-	}
-
-	list_add_tail(&page->lru, pos);
+	lock_page(pageref->page);
 
-page_already_added:
 	mutex_unlock(&fbdefio->lock);
 
 	/* come back after delay to process the deferred IO */
 	schedule_delayed_work(&info->deferred_work, fbdefio->delay);
 	return VM_FAULT_LOCKED;
+
+err_mutex_unlock:
+	mutex_unlock(&fbdefio->lock);
+	return ret;
 }
 
 static const struct vm_operations_struct fb_deferred_io_vm_ops = {
@@ -194,15 +227,14 @@ EXPORT_SYMBOL_GPL(fb_deferred_io_mmap);
 /* workqueue callback */
 static void fb_deferred_io_work(struct work_struct *work)
 {
-	struct fb_info *info = container_of(work, struct fb_info,
-						deferred_work.work);
-	struct list_head *node, *next;
-	struct page *cur;
+	struct fb_info *info = container_of(work, struct fb_info, deferred_work.work);
+	struct fb_deferred_io_pageref *pageref, *next;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 
 	/* here we mkclean the pages, then do all deferred IO */
 	mutex_lock(&fbdefio->lock);
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 		lock_page(cur);
 		page_mkclean(cur);
 		unlock_page(cur);
@@ -212,22 +244,48 @@ static void fb_deferred_io_work(struct work_struct *work)
 	fbdefio->deferred_io(info, &fbdefio->pagelist);
 
 	/* clear the list */
-	list_for_each_safe(node, next, &fbdefio->pagelist) {
-		list_del_init(node);
-	}
+	list_for_each_entry_safe(pageref, next, &fbdefio->pagelist, list)
+		fb_deferred_io_pageref_put(pageref, info);
+
 	mutex_unlock(&fbdefio->lock);
 }
 
-void fb_deferred_io_init(struct fb_info *info)
+int fb_deferred_io_init(struct fb_info *info)
 {
 	struct fb_deferred_io *fbdefio = info->fbdefio;
+	struct fb_deferred_io_pageref *pagerefs;
+	unsigned long npagerefs, i;
+	int ret;
 
 	BUG_ON(!fbdefio);
+
+	if (WARN_ON(!info->fix.smem_len))
+		return -EINVAL;
+
 	mutex_init(&fbdefio->lock);
 	INIT_DELAYED_WORK(&info->deferred_work, fb_deferred_io_work);
 	INIT_LIST_HEAD(&fbdefio->pagelist);
 	if (fbdefio->delay == 0) /* set a default of 1 s */
 		fbdefio->delay = HZ;
+
+	npagerefs = DIV_ROUND_UP(info->fix.smem_len, PAGE_SIZE);
+
+	/* alloc a page ref for each page of the display memory */
+	pagerefs = kvcalloc(npagerefs, sizeof(*pagerefs), GFP_KERNEL);
+	if (!pagerefs) {
+		ret = -ENOMEM;
+		goto err;
+	}
+	for (i = 0; i < npagerefs; ++i)
+		INIT_LIST_HEAD(&pagerefs[i].list);
+	info->npagerefs = npagerefs;
+	info->pagerefs = pagerefs;
+
+	return 0;
+
+err:
+	mutex_destroy(&fbdefio->lock);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_init);
 
@@ -254,6 +312,7 @@ void fb_deferred_io_cleanup(struct fb_info *info)
 		page->mapping = NULL;
 	}
 
+	kvfree(info->pagerefs);
 	mutex_destroy(&fbdefio->lock);
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_cleanup);
diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c
index d7f6abf827b9..51eb57ea68f7 100644
--- a/drivers/video/fbdev/hyperv_fb.c
+++ b/drivers/video/fbdev/hyperv_fb.c
@@ -424,7 +424,7 @@ static void synthvid_deferred_io(struct fb_info *p,
 				 struct list_head *pagelist)
 {
 	struct hvfb_par *par = p->par;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	unsigned long start, end;
 	int y1, y2, miny, maxy;
 
@@ -437,7 +437,8 @@ static void synthvid_deferred_io(struct fb_info *p,
 	 * in synthvid_update function by clamping the y2
 	 * value to yres.
 	 */
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		start = page->index << PAGE_SHIFT;
 		end = start + PAGE_SIZE - 1;
 		y1 = start / p->fix.line_length;
diff --git a/drivers/video/fbdev/metronomefb.c b/drivers/video/fbdev/metronomefb.c
index 2541f2fe065b..8352fa3f4cef 100644
--- a/drivers/video/fbdev/metronomefb.c
+++ b/drivers/video/fbdev/metronomefb.c
@@ -469,12 +469,13 @@ static void metronomefb_dpy_deferred_io(struct fb_info *info,
 				struct list_head *pagelist)
 {
 	u16 cksum;
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct metronomefb_par *par = info->par;
 
 	/* walk the written page list and swizzle the data */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 		cksum = metronomefb_dpy_update_page(par,
 					(cur->index << PAGE_SHIFT));
 		par->metromem_img_csum -= par->csum_table[cur->index];
diff --git a/drivers/video/fbdev/sh_mobile_lcdcfb.c b/drivers/video/fbdev/sh_mobile_lcdcfb.c
index 1dc5079e4518..7fcc85352033 100644
--- a/drivers/video/fbdev/sh_mobile_lcdcfb.c
+++ b/drivers/video/fbdev/sh_mobile_lcdcfb.c
@@ -440,13 +440,15 @@ static int sh_mobile_lcdc_sginit(struct fb_info *info,
 {
 	struct sh_mobile_lcdc_chan *ch = info->par;
 	unsigned int nr_pages_max = ch->fb_size >> PAGE_SHIFT;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	int nr_pages = 0;
 
 	sg_init_table(ch->sglist, nr_pages_max);
 
-	list_for_each_entry(page, pagelist, lru)
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		sg_set_page(&ch->sglist[nr_pages++], page, PAGE_SIZE, 0);
+	}
 
 	return nr_pages;
 }
diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
index 10eb56feff0a..cabc3d03897c 100644
--- a/drivers/video/fbdev/smscufx.c
+++ b/drivers/video/fbdev/smscufx.c
@@ -958,7 +958,7 @@ static void ufx_ops_fillrect(struct fb_info *info,
 static void ufx_dpy_deferred_io(struct fb_info *info,
 				struct list_head *pagelist)
 {
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct ufx_data *dev = info->par;
 
@@ -969,9 +969,10 @@ static void ufx_dpy_deferred_io(struct fb_info *info,
 		return;
 
 	/* walk the written page list and render each to device */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
 		/* create a rectangle of full screen width that encloses the
 		 * entire dirty framebuffer page */
+		struct page *cur = pageref->page;
 		const int x = 0;
 		const int width = dev->info->var.xres;
 		const int y = (cur->index << PAGE_SHIFT) / (width * 2);
diff --git a/drivers/video/fbdev/udlfb.c b/drivers/video/fbdev/udlfb.c
index d7f02b935eb2..2d971c45bd93 100644
--- a/drivers/video/fbdev/udlfb.c
+++ b/drivers/video/fbdev/udlfb.c
@@ -784,7 +784,7 @@ static void dlfb_ops_fillrect(struct fb_info *info,
 static void dlfb_dpy_deferred_io(struct fb_info *info,
 				struct list_head *pagelist)
 {
-	struct page *cur;
+	struct fb_deferred_io_pageref *pageref;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct dlfb_data *dlfb = info->par;
 	struct urb *urb;
@@ -811,7 +811,8 @@ static void dlfb_dpy_deferred_io(struct fb_info *info,
 	cmd = urb->transfer_buffer;
 
 	/* walk the written page list and render each to device */
-	list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+	list_for_each_entry(pageref, &fbdefio->pagelist, list) {
+		struct page *cur = pageref->page;
 
 		if (dlfb_render_hline(dlfb, &urb, (char *) info->fix.smem_start,
 				  &cmd, cur->index << PAGE_SHIFT,
diff --git a/drivers/video/fbdev/xen-fbfront.c b/drivers/video/fbdev/xen-fbfront.c
index 6c8846eba2fb..608fcde767d3 100644
--- a/drivers/video/fbdev/xen-fbfront.c
+++ b/drivers/video/fbdev/xen-fbfront.c
@@ -185,13 +185,14 @@ static void xenfb_deferred_io(struct fb_info *fb_info,
 			      struct list_head *pagelist)
 {
 	struct xenfb_info *info = fb_info->par;
-	struct page *page;
+	struct fb_deferred_io_pageref *pageref;
 	unsigned long beg, end;
 	int y1, y2, miny, maxy;
 
 	miny = INT_MAX;
 	maxy = 0;
-	list_for_each_entry(page, pagelist, lru) {
+	list_for_each_entry(pageref, pagelist, list) {
+		struct page *page = pageref->page;
 		beg = page->index << PAGE_SHIFT;
 		end = beg + PAGE_SIZE - 1;
 		y1 = beg / fb_info->fix.line_length;
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 9a77ab615c36..768de6534a82 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -201,6 +201,13 @@ struct fb_pixmap {
 };
 
 #ifdef CONFIG_FB_DEFERRED_IO
+struct fb_deferred_io_pageref {
+	struct page *page;
+	unsigned long offset;
+	/* private */
+	struct list_head list;
+};
+
 struct fb_deferred_io {
 	/* delay between mkwrite and deferred handler */
 	unsigned long delay;
@@ -469,6 +476,8 @@ struct fb_info {
 #endif
 #ifdef CONFIG_FB_DEFERRED_IO
 	struct delayed_work deferred_work;
+	unsigned long npagerefs;
+	struct fb_deferred_io_pageref *pagerefs;
 	struct fb_deferred_io *fbdefio;
 #endif
 
@@ -662,7 +671,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
 
 /* drivers/video/fb_defio.c */
 int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
-extern void fb_deferred_io_init(struct fb_info *info);
+extern int  fb_deferred_io_init(struct fb_info *info);
 extern void fb_deferred_io_open(struct fb_info *info,
 				struct inode *inode,
 				struct file *file);
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 4/9] fbdev: Export helper for implementing page_mkwrite
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Refactor the page-write handler and export it as helper function
fb_deferred_io_page_mkwrite(). Drivers that implement struct
vm_operations_struct.page_mkwrite for deferred I/O should use the
function to let fbdev track written pages of mmap'ed framebuffer
memory.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/video/fbdev/core/fb_defio.c | 69 ++++++++++++++++++++---------
 include/linux/fb.h                  |  1 +
 2 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 20c6eb94a685..8cd40b1714d4 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -143,29 +143,18 @@ int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasy
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
 
-/* vm_ops->page_mkwrite handler */
-static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+/*
+ * Adds a page to the dirty list. Requires caller to hold
+ * struct fb_deferred_io.lock. Call this from struct
+ * vm_operations_struct.page_mkwrite.
+ */
+static vm_fault_t __fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
+					      struct page *page)
 {
-	struct page *page = vmf->page;
-	struct fb_info *info = vmf->vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct fb_deferred_io_pageref *pageref;
-	unsigned long offset;
 	vm_fault_t ret;
 
-	offset = (vmf->address - vmf->vma->vm_start);
-
-	/* this is a callback we get when userspace first tries to
-	write to the page. we schedule a workqueue. that workqueue
-	will eventually mkclean the touched pages and execute the
-	deferred framebuffer IO. then if userspace touches a page
-	again, we repeat the same scheme */
-
-	file_update_time(vmf->vma->vm_file);
-
-	/* protect against the workqueue changing the page list */
-	mutex_lock(&fbdefio->lock);
-
 	/* first write in this cycle, notify the driver */
 	if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
 		fbdefio->first_io(info);
@@ -186,8 +175,6 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	 */
 	lock_page(pageref->page);
 
-	mutex_unlock(&fbdefio->lock);
-
 	/* come back after delay to process the deferred IO */
 	schedule_delayed_work(&info->deferred_work, fbdefio->delay);
 	return VM_FAULT_LOCKED;
@@ -197,6 +184,48 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	return ret;
 }
 
+/**
+ * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
+ * @fb_info: The fbdev info structure
+ * @vmf: The VM fault
+ *
+ * This is a callback we get when userspace first tries to
+ * write to the page. We schedule a workqueue. That workqueue
+ * will eventually mkclean the touched pages and execute the
+ * deferred framebuffer IO. Then if userspace touches a page
+ * again, we repeat the same scheme.
+ *
+ * Returns:
+ * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
+ */
+vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+{
+	struct page *page = vmf->page;
+	struct fb_deferred_io *fbdefio = info->fbdefio;
+	unsigned long offset;
+	vm_fault_t ret;
+
+	offset = (vmf->address - vmf->vma->vm_start);
+
+	file_update_time(vmf->vma->vm_file);
+
+	/* protect against the workqueue changing the page list */
+	mutex_lock(&fbdefio->lock);
+	ret = __fb_deferred_io_track_page(info, offset, page);
+	mutex_unlock(&fbdefio->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_page_mkwrite);
+
+/* vm_ops->page_mkwrite handler */
+static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+{
+	struct fb_info *info = vmf->vma->vm_private_data;
+
+	return fb_deferred_io_page_mkwrite(info, vmf);
+}
+
 static const struct vm_operations_struct fb_deferred_io_vm_ops = {
 	.fault		= fb_deferred_io_fault,
 	.page_mkwrite	= fb_deferred_io_mkwrite,
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 768de6534a82..631a1d0cb193 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -670,6 +670,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
 }
 
 /* drivers/video/fb_defio.c */
+vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf);
 int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
 extern int  fb_deferred_io_init(struct fb_info *info);
 extern void fb_deferred_io_open(struct fb_info *info,
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 4/9] fbdev: Export helper for implementing page_mkwrite
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Refactor the page-write handler and export it as helper function
fb_deferred_io_page_mkwrite(). Drivers that implement struct
vm_operations_struct.page_mkwrite for deferred I/O should use the
function to let fbdev track written pages of mmap'ed framebuffer
memory.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/video/fbdev/core/fb_defio.c | 69 ++++++++++++++++++++---------
 include/linux/fb.h                  |  1 +
 2 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/drivers/video/fbdev/core/fb_defio.c b/drivers/video/fbdev/core/fb_defio.c
index 20c6eb94a685..8cd40b1714d4 100644
--- a/drivers/video/fbdev/core/fb_defio.c
+++ b/drivers/video/fbdev/core/fb_defio.c
@@ -143,29 +143,18 @@ int fb_deferred_io_fsync(struct file *file, loff_t start, loff_t end, int datasy
 }
 EXPORT_SYMBOL_GPL(fb_deferred_io_fsync);
 
-/* vm_ops->page_mkwrite handler */
-static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+/*
+ * Adds a page to the dirty list. Requires caller to hold
+ * struct fb_deferred_io.lock. Call this from struct
+ * vm_operations_struct.page_mkwrite.
+ */
+static vm_fault_t __fb_deferred_io_track_page(struct fb_info *info, unsigned long offset,
+					      struct page *page)
 {
-	struct page *page = vmf->page;
-	struct fb_info *info = vmf->vma->vm_private_data;
 	struct fb_deferred_io *fbdefio = info->fbdefio;
 	struct fb_deferred_io_pageref *pageref;
-	unsigned long offset;
 	vm_fault_t ret;
 
-	offset = (vmf->address - vmf->vma->vm_start);
-
-	/* this is a callback we get when userspace first tries to
-	write to the page. we schedule a workqueue. that workqueue
-	will eventually mkclean the touched pages and execute the
-	deferred framebuffer IO. then if userspace touches a page
-	again, we repeat the same scheme */
-
-	file_update_time(vmf->vma->vm_file);
-
-	/* protect against the workqueue changing the page list */
-	mutex_lock(&fbdefio->lock);
-
 	/* first write in this cycle, notify the driver */
 	if (fbdefio->first_io && list_empty(&fbdefio->pagelist))
 		fbdefio->first_io(info);
@@ -186,8 +175,6 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	 */
 	lock_page(pageref->page);
 
-	mutex_unlock(&fbdefio->lock);
-
 	/* come back after delay to process the deferred IO */
 	schedule_delayed_work(&info->deferred_work, fbdefio->delay);
 	return VM_FAULT_LOCKED;
@@ -197,6 +184,48 @@ static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
 	return ret;
 }
 
+/**
+ * fb_deferred_io_page_mkwrite - Mark a page as written for deferred I/O
+ * @fb_info: The fbdev info structure
+ * @vmf: The VM fault
+ *
+ * This is a callback we get when userspace first tries to
+ * write to the page. We schedule a workqueue. That workqueue
+ * will eventually mkclean the touched pages and execute the
+ * deferred framebuffer IO. Then if userspace touches a page
+ * again, we repeat the same scheme.
+ *
+ * Returns:
+ * VM_FAULT_LOCKED on success, or a VM_FAULT error otherwise.
+ */
+vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf)
+{
+	struct page *page = vmf->page;
+	struct fb_deferred_io *fbdefio = info->fbdefio;
+	unsigned long offset;
+	vm_fault_t ret;
+
+	offset = (vmf->address - vmf->vma->vm_start);
+
+	file_update_time(vmf->vma->vm_file);
+
+	/* protect against the workqueue changing the page list */
+	mutex_lock(&fbdefio->lock);
+	ret = __fb_deferred_io_track_page(info, offset, page);
+	mutex_unlock(&fbdefio->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(fb_deferred_io_page_mkwrite);
+
+/* vm_ops->page_mkwrite handler */
+static vm_fault_t fb_deferred_io_mkwrite(struct vm_fault *vmf)
+{
+	struct fb_info *info = vmf->vma->vm_private_data;
+
+	return fb_deferred_io_page_mkwrite(info, vmf);
+}
+
 static const struct vm_operations_struct fb_deferred_io_vm_ops = {
 	.fault		= fb_deferred_io_fault,
 	.page_mkwrite	= fb_deferred_io_mkwrite,
diff --git a/include/linux/fb.h b/include/linux/fb.h
index 768de6534a82..631a1d0cb193 100644
--- a/include/linux/fb.h
+++ b/include/linux/fb.h
@@ -670,6 +670,7 @@ static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch,
 }
 
 /* drivers/video/fb_defio.c */
+vm_fault_t fb_deferred_io_page_mkwrite(struct fb_info *info, struct vm_fault *vmf);
 int fb_deferred_io_mmap(struct fb_info *info, struct vm_area_struct *vma);
 extern int  fb_deferred_io_init(struct fb_info *info);
 extern void fb_deferred_io_open(struct fb_info *info,
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 5/9] drm/fb-helper: Separate deferred I/O from shadow buffers
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

DRM drivers will be able to handle deferred I/O by themselves. So
a driver will be able to use deferred I/O without an intermediate
shadow buffer.

Prepare fbdev emulation by separating shadow buffers and deferred
I/O from each other.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c | 35 ++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index dd1d72d58b35..660ec5038c4e 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -660,6 +660,19 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
 	       fb->funcs->dirty;
 }
 
+static bool drm_fbdev_use_deferred_io(struct drm_fb_helper *fb_helper)
+{
+	struct drm_framebuffer *fb = fb_helper->fb;
+
+	/*
+	 * Any driver with damage handling requires deferred I/O to
+	 * keep track of the updated screen areas. Drivers with shadow
+	 * buffers need deferred I/O to forward screen updates to the
+	 * buffer object.
+	 */
+	return fb->funcs->dirty || drm_fbdev_use_shadow_fb(fb_helper);
+}
+
 static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
 				 u32 width, u32 height)
 {
@@ -667,7 +680,7 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
 	struct drm_clip_rect *clip = &helper->damage_clip;
 	unsigned long flags;
 
-	if (!drm_fbdev_use_shadow_fb(helper))
+	if (!drm_fbdev_use_deferred_io(helper))
 		return;
 
 	spin_lock_irqsave(&helper->damage_lock, flags);
@@ -2119,8 +2132,16 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 	struct drm_fb_helper *fb_helper = info->par;
 
 	if (drm_fbdev_use_shadow_fb(fb_helper))
+		/*
+		 * Drivers with shadow buffer use fbdev's implementation of
+		 * deferred I/O.
+		 */
 		return fb_deferred_io_mmap(info, vma);
 	else if (fb_helper->dev->driver->gem_prime_mmap)
+		/*
+		 * Either directly mmap'ed or with deferred I/O; drivers
+		 * without shadow buffer handle mmap themselves.
+		 */
 		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
 	else
 		return -ENODEV;
@@ -2131,7 +2152,9 @@ static bool drm_fbdev_use_iomem(struct fb_info *info)
 	struct drm_fb_helper *fb_helper = info->par;
 	struct drm_client_buffer *buffer = fb_helper->buffer;
 
-	return !drm_fbdev_use_shadow_fb(fb_helper) && buffer->map.is_iomem;
+	if (drm_fbdev_use_shadow_fb(fb_helper))
+		return false;
+	return buffer->map.is_iomem;
 }
 
 static ssize_t fb_read_screen_base(struct fb_info *info, char __user *buf, size_t count,
@@ -2396,9 +2419,6 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 		if (!fbi->screen_buffer)
 			return -ENOMEM;
 		fbi->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
-
-		fbi->fbdefio = &drm_fbdev_defio;
-		fb_deferred_io_init(fbi);
 	} else {
 		/* buffer is mapped for HW framebuffer */
 		ret = drm_client_buffer_vmap(fb_helper->buffer, &map);
@@ -2424,6 +2444,11 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 #endif
 	}
 
+	if (drm_fbdev_use_deferred_io(fb_helper)) {
+		fbi->fbdefio = &drm_fbdev_defio;
+		fb_deferred_io_init(fbi);
+	}
+
 	return 0;
 }
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 5/9] drm/fb-helper: Separate deferred I/O from shadow buffers
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

DRM drivers will be able to handle deferred I/O by themselves. So
a driver will be able to use deferred I/O without an intermediate
shadow buffer.

Prepare fbdev emulation by separating shadow buffers and deferred
I/O from each other.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c | 35 ++++++++++++++++++++++++++++-----
 1 file changed, 30 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index dd1d72d58b35..660ec5038c4e 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -660,6 +660,19 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
 	       fb->funcs->dirty;
 }
 
+static bool drm_fbdev_use_deferred_io(struct drm_fb_helper *fb_helper)
+{
+	struct drm_framebuffer *fb = fb_helper->fb;
+
+	/*
+	 * Any driver with damage handling requires deferred I/O to
+	 * keep track of the updated screen areas. Drivers with shadow
+	 * buffers need deferred I/O to forward screen updates to the
+	 * buffer object.
+	 */
+	return fb->funcs->dirty || drm_fbdev_use_shadow_fb(fb_helper);
+}
+
 static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
 				 u32 width, u32 height)
 {
@@ -667,7 +680,7 @@ static void drm_fb_helper_damage(struct fb_info *info, u32 x, u32 y,
 	struct drm_clip_rect *clip = &helper->damage_clip;
 	unsigned long flags;
 
-	if (!drm_fbdev_use_shadow_fb(helper))
+	if (!drm_fbdev_use_deferred_io(helper))
 		return;
 
 	spin_lock_irqsave(&helper->damage_lock, flags);
@@ -2119,8 +2132,16 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 	struct drm_fb_helper *fb_helper = info->par;
 
 	if (drm_fbdev_use_shadow_fb(fb_helper))
+		/*
+		 * Drivers with shadow buffer use fbdev's implementation of
+		 * deferred I/O.
+		 */
 		return fb_deferred_io_mmap(info, vma);
 	else if (fb_helper->dev->driver->gem_prime_mmap)
+		/*
+		 * Either directly mmap'ed or with deferred I/O; drivers
+		 * without shadow buffer handle mmap themselves.
+		 */
 		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
 	else
 		return -ENODEV;
@@ -2131,7 +2152,9 @@ static bool drm_fbdev_use_iomem(struct fb_info *info)
 	struct drm_fb_helper *fb_helper = info->par;
 	struct drm_client_buffer *buffer = fb_helper->buffer;
 
-	return !drm_fbdev_use_shadow_fb(fb_helper) && buffer->map.is_iomem;
+	if (drm_fbdev_use_shadow_fb(fb_helper))
+		return false;
+	return buffer->map.is_iomem;
 }
 
 static ssize_t fb_read_screen_base(struct fb_info *info, char __user *buf, size_t count,
@@ -2396,9 +2419,6 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 		if (!fbi->screen_buffer)
 			return -ENOMEM;
 		fbi->flags |= FBINFO_VIRTFB | FBINFO_READS_FAST;
-
-		fbi->fbdefio = &drm_fbdev_defio;
-		fb_deferred_io_init(fbi);
 	} else {
 		/* buffer is mapped for HW framebuffer */
 		ret = drm_client_buffer_vmap(fb_helper->buffer, &map);
@@ -2424,6 +2444,11 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 #endif
 	}
 
+	if (drm_fbdev_use_deferred_io(fb_helper)) {
+		fbi->fbdefio = &drm_fbdev_defio;
+		fb_deferred_io_init(fbi);
+	}
+
 	return 0;
 }
 
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Provide struct drm_driver.dumb_create_fbdev, a callback hook for
fbdev dumb buffers. Wire up fbdev and client helpers to use the new
interface, if present.

This acknowledges the fact that fbdev buffers are different. The most
significant difference to regular GEM BOs is in mmap semantics. Fbdev
userspace treats the pages as video memory, which makes it impossible
to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
the BO permanently or install an intermediate shadow buffer for mmap.

So far, fbdev memory came from dumb buffers and DRM drivers had no
means of detecting this without reimplementing a good part of the fbdev
code. With the new callback, drivers can perma-pin fbdev buffer objects
if needed.

Several drivers also require damage handling, which fbdev implements
with its deferred I/O helpers. The new callback allows a driver's memory
manager to set up a suitable mmap.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_client.c        | 14 +++++++----
 drivers/gpu/drm/drm_crtc_internal.h |  3 +++
 drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
 drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
 include/drm/drm_client.h            |  3 ++-
 include/drm/drm_drv.h               | 17 ++++++++++++++
 6 files changed, 84 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index af3b7395bf69..c964064651d5 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
 }
 
 static struct drm_client_buffer *
-drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			 bool fbdev)
 {
 	const struct drm_format_info *info = drm_format_info(format);
 	struct drm_mode_create_dumb dumb_args = { };
@@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
 	dumb_args.width = width;
 	dumb_args.height = height;
 	dumb_args.bpp = info->cpp[0] * 8;
-	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
+	if (fbdev)
+		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
+	else
+		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
 	if (ret)
 		goto err_delete;
 
@@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * @width: Framebuffer width
  * @height: Framebuffer height
  * @format: Buffer format
+ * @fbdev: True if the client provides an fbdev device, or false otherwise
  *
  * This function creates a &drm_client_buffer which consists of a
  * &drm_framebuffer backed by a dumb buffer.
@@ -411,12 +416,13 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * Pointer to a client buffer or an error pointer on failure.
  */
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev)
 {
 	struct drm_client_buffer *buffer;
 	int ret;
 
-	buffer = drm_client_buffer_create(client, width, height, format);
+	buffer = drm_client_buffer_create(client, width, height, format, fbdev);
 	if (IS_ERR(buffer))
 		return buffer;
 
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 63279e984342..34e532953b5a 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -101,6 +101,9 @@ int drm_mode_getresources(struct drm_device *dev,
 int drm_mode_create_dumb(struct drm_device *dev,
 			 struct drm_mode_create_dumb *args,
 			 struct drm_file *file_priv);
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv);
 int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
 			  struct drm_file *file_priv);
 
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index ad17fa21cebb..a605b4c4e4a3 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -57,14 +57,10 @@
  * a hardware-specific ioctl to allocate suitable buffer objects.
  */
 
-int drm_mode_create_dumb(struct drm_device *dev,
-			 struct drm_mode_create_dumb *args,
-			 struct drm_file *file_priv)
+static int drm_mode_sanitize_args(struct drm_mode_create_dumb *args)
 {
 	u32 cpp, stride, size;
 
-	if (!dev->driver->dumb_create)
-		return -ENOSYS;
 	if (!args->width || !args->height || !args->bpp)
 		return -EINVAL;
 
@@ -93,6 +89,21 @@ int drm_mode_create_dumb(struct drm_device *dev,
 	args->pitch = 0;
 	args->size = 0;
 
+	return 0;
+}
+
+int drm_mode_create_dumb(struct drm_device *dev,
+			 struct drm_mode_create_dumb *args,
+			 struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
 	return dev->driver->dumb_create(file_priv, dev, args);
 }
 
@@ -102,6 +113,21 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
 	return drm_mode_create_dumb(dev, data, file_priv);
 }
 
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create_fbdev)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
+	return dev->driver->dumb_create_fbdev(file_priv, dev, args);
+}
+
 /**
  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 660ec5038c4e..9d1140a789f4 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -655,6 +655,15 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_framebuffer *fb = fb_helper->fb;
 
+	/*
+	 * If the driver provides an fbdev create function, it is expected
+	 * to provide its own implementation of deferred I/O. Shadow
+	 * buffering can still be enabled with the prefer_shadow_fbdev
+	 * flag.
+	 */
+	if (dev->driver->dumb_create_fbdev)
+		return dev->mode_config.prefer_shadow_fbdev;
+
 	return dev->mode_config.prefer_shadow_fbdev ||
 	       dev->mode_config.prefer_shadow ||
 	       fb->funcs->dirty;
@@ -2130,6 +2139,7 @@ static void drm_fbdev_fb_destroy(struct fb_info *info)
 static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
 	struct drm_fb_helper *fb_helper = info->par;
+	struct drm_device *dev = fb_helper->dev;
 
 	if (drm_fbdev_use_shadow_fb(fb_helper))
 		/*
@@ -2137,14 +2147,20 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 		 * deferred I/O.
 		 */
 		return fb_deferred_io_mmap(info, vma);
-	else if (fb_helper->dev->driver->gem_prime_mmap)
+	else if (!dev->driver->dumb_create_fbdev && drm_fbdev_use_deferred_io(fb_helper))
+		/*
+		 * Drivers without fbdev dumb-create helper rely on fbdev's
+		 * deferred I/O.
+		 */
+		return fb_deferred_io_mmap(info, vma);
+	else if (dev->driver->gem_prime_mmap)
 		/*
 		 * Either directly mmap'ed or with deferred I/O; drivers
 		 * without shadow buffer handle mmap themselves.
 		 */
-		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
-	else
-		return -ENODEV;
+		return dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
+
+	return -ENODEV;
 }
 
 static bool drm_fbdev_use_iomem(struct fb_info *info)
@@ -2395,7 +2411,7 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 
 	format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
 	buffer = drm_client_framebuffer_create(client, sizes->surface_width,
-					       sizes->surface_height, format);
+					       sizes->surface_height, format, true);
 	if (IS_ERR(buffer))
 		return PTR_ERR(buffer);
 
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 4fc8018eddda..8d80d5594bea 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -153,7 +153,8 @@ struct drm_client_buffer {
 };
 
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format);
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev);
 void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
 int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect);
 int drm_client_buffer_vmap(struct drm_client_buffer *buffer,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index f6159acb8856..da4a095de1e7 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -379,6 +379,23 @@ struct drm_driver {
 	int (*dumb_create)(struct drm_file *file_priv,
 			   struct drm_device *dev,
 			   struct drm_mode_create_dumb *args);
+	/**
+	 * @dumb_create_fbdev:
+	 *
+	 * This creates a new dumb buffer for use with DRM fbdev clients and
+	 * returns the resulting buffer handle. This handle can then be wrapped
+	 * up into a framebuffer modeset object.
+	 *
+	 * This is an interface for kernel clients and never called via ioctl.
+	 *
+	 * See @dumb_create for more information.
+	 *
+	 * Returns:
+	 * Zero on success, negative errno on failure.
+	 */
+	int (*dumb_create_fbdev)(struct drm_file *file_priv,
+				 struct drm_device *dev,
+				 struct drm_mode_create_dumb *args);
 	/**
 	 * @dumb_map_offset:
 	 *
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Provide struct drm_driver.dumb_create_fbdev, a callback hook for
fbdev dumb buffers. Wire up fbdev and client helpers to use the new
interface, if present.

This acknowledges the fact that fbdev buffers are different. The most
significant difference to regular GEM BOs is in mmap semantics. Fbdev
userspace treats the pages as video memory, which makes it impossible
to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
the BO permanently or install an intermediate shadow buffer for mmap.

So far, fbdev memory came from dumb buffers and DRM drivers had no
means of detecting this without reimplementing a good part of the fbdev
code. With the new callback, drivers can perma-pin fbdev buffer objects
if needed.

Several drivers also require damage handling, which fbdev implements
with its deferred I/O helpers. The new callback allows a driver's memory
manager to set up a suitable mmap.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_client.c        | 14 +++++++----
 drivers/gpu/drm/drm_crtc_internal.h |  3 +++
 drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
 drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
 include/drm/drm_client.h            |  3 ++-
 include/drm/drm_drv.h               | 17 ++++++++++++++
 6 files changed, 84 insertions(+), 15 deletions(-)

diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
index af3b7395bf69..c964064651d5 100644
--- a/drivers/gpu/drm/drm_client.c
+++ b/drivers/gpu/drm/drm_client.c
@@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
 }
 
 static struct drm_client_buffer *
-drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			 bool fbdev)
 {
 	const struct drm_format_info *info = drm_format_info(format);
 	struct drm_mode_create_dumb dumb_args = { };
@@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
 	dumb_args.width = width;
 	dumb_args.height = height;
 	dumb_args.bpp = info->cpp[0] * 8;
-	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
+	if (fbdev)
+		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
+	else
+		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
 	if (ret)
 		goto err_delete;
 
@@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * @width: Framebuffer width
  * @height: Framebuffer height
  * @format: Buffer format
+ * @fbdev: True if the client provides an fbdev device, or false otherwise
  *
  * This function creates a &drm_client_buffer which consists of a
  * &drm_framebuffer backed by a dumb buffer.
@@ -411,12 +416,13 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
  * Pointer to a client buffer or an error pointer on failure.
  */
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev)
 {
 	struct drm_client_buffer *buffer;
 	int ret;
 
-	buffer = drm_client_buffer_create(client, width, height, format);
+	buffer = drm_client_buffer_create(client, width, height, format, fbdev);
 	if (IS_ERR(buffer))
 		return buffer;
 
diff --git a/drivers/gpu/drm/drm_crtc_internal.h b/drivers/gpu/drm/drm_crtc_internal.h
index 63279e984342..34e532953b5a 100644
--- a/drivers/gpu/drm/drm_crtc_internal.h
+++ b/drivers/gpu/drm/drm_crtc_internal.h
@@ -101,6 +101,9 @@ int drm_mode_getresources(struct drm_device *dev,
 int drm_mode_create_dumb(struct drm_device *dev,
 			 struct drm_mode_create_dumb *args,
 			 struct drm_file *file_priv);
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv);
 int drm_mode_destroy_dumb(struct drm_device *dev, u32 handle,
 			  struct drm_file *file_priv);
 
diff --git a/drivers/gpu/drm/drm_dumb_buffers.c b/drivers/gpu/drm/drm_dumb_buffers.c
index ad17fa21cebb..a605b4c4e4a3 100644
--- a/drivers/gpu/drm/drm_dumb_buffers.c
+++ b/drivers/gpu/drm/drm_dumb_buffers.c
@@ -57,14 +57,10 @@
  * a hardware-specific ioctl to allocate suitable buffer objects.
  */
 
-int drm_mode_create_dumb(struct drm_device *dev,
-			 struct drm_mode_create_dumb *args,
-			 struct drm_file *file_priv)
+static int drm_mode_sanitize_args(struct drm_mode_create_dumb *args)
 {
 	u32 cpp, stride, size;
 
-	if (!dev->driver->dumb_create)
-		return -ENOSYS;
 	if (!args->width || !args->height || !args->bpp)
 		return -EINVAL;
 
@@ -93,6 +89,21 @@ int drm_mode_create_dumb(struct drm_device *dev,
 	args->pitch = 0;
 	args->size = 0;
 
+	return 0;
+}
+
+int drm_mode_create_dumb(struct drm_device *dev,
+			 struct drm_mode_create_dumb *args,
+			 struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
 	return dev->driver->dumb_create(file_priv, dev, args);
 }
 
@@ -102,6 +113,21 @@ int drm_mode_create_dumb_ioctl(struct drm_device *dev,
 	return drm_mode_create_dumb(dev, data, file_priv);
 }
 
+int drm_mode_create_dumb_fbdev(struct drm_device *dev,
+			       struct drm_mode_create_dumb *args,
+			       struct drm_file *file_priv)
+{
+	int ret;
+
+	if (!dev->driver->dumb_create_fbdev)
+		return -ENOSYS;
+	ret = drm_mode_sanitize_args(args);
+	if (ret)
+		return ret;
+
+	return dev->driver->dumb_create_fbdev(file_priv, dev, args);
+}
+
 /**
  * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer
  * @dev: DRM device
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 660ec5038c4e..9d1140a789f4 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -655,6 +655,15 @@ static bool drm_fbdev_use_shadow_fb(struct drm_fb_helper *fb_helper)
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_framebuffer *fb = fb_helper->fb;
 
+	/*
+	 * If the driver provides an fbdev create function, it is expected
+	 * to provide its own implementation of deferred I/O. Shadow
+	 * buffering can still be enabled with the prefer_shadow_fbdev
+	 * flag.
+	 */
+	if (dev->driver->dumb_create_fbdev)
+		return dev->mode_config.prefer_shadow_fbdev;
+
 	return dev->mode_config.prefer_shadow_fbdev ||
 	       dev->mode_config.prefer_shadow ||
 	       fb->funcs->dirty;
@@ -2130,6 +2139,7 @@ static void drm_fbdev_fb_destroy(struct fb_info *info)
 static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 {
 	struct drm_fb_helper *fb_helper = info->par;
+	struct drm_device *dev = fb_helper->dev;
 
 	if (drm_fbdev_use_shadow_fb(fb_helper))
 		/*
@@ -2137,14 +2147,20 @@ static int drm_fbdev_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
 		 * deferred I/O.
 		 */
 		return fb_deferred_io_mmap(info, vma);
-	else if (fb_helper->dev->driver->gem_prime_mmap)
+	else if (!dev->driver->dumb_create_fbdev && drm_fbdev_use_deferred_io(fb_helper))
+		/*
+		 * Drivers without fbdev dumb-create helper rely on fbdev's
+		 * deferred I/O.
+		 */
+		return fb_deferred_io_mmap(info, vma);
+	else if (dev->driver->gem_prime_mmap)
 		/*
 		 * Either directly mmap'ed or with deferred I/O; drivers
 		 * without shadow buffer handle mmap themselves.
 		 */
-		return fb_helper->dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
-	else
-		return -ENODEV;
+		return dev->driver->gem_prime_mmap(fb_helper->buffer->gem, vma);
+
+	return -ENODEV;
 }
 
 static bool drm_fbdev_use_iomem(struct fb_info *info)
@@ -2395,7 +2411,7 @@ static int drm_fb_helper_generic_probe(struct drm_fb_helper *fb_helper,
 
 	format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth);
 	buffer = drm_client_framebuffer_create(client, sizes->surface_width,
-					       sizes->surface_height, format);
+					       sizes->surface_height, format, true);
 	if (IS_ERR(buffer))
 		return PTR_ERR(buffer);
 
diff --git a/include/drm/drm_client.h b/include/drm/drm_client.h
index 4fc8018eddda..8d80d5594bea 100644
--- a/include/drm/drm_client.h
+++ b/include/drm/drm_client.h
@@ -153,7 +153,8 @@ struct drm_client_buffer {
 };
 
 struct drm_client_buffer *
-drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format);
+drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
+			      bool fbdev);
 void drm_client_framebuffer_delete(struct drm_client_buffer *buffer);
 int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect);
 int drm_client_buffer_vmap(struct drm_client_buffer *buffer,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index f6159acb8856..da4a095de1e7 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -379,6 +379,23 @@ struct drm_driver {
 	int (*dumb_create)(struct drm_file *file_priv,
 			   struct drm_device *dev,
 			   struct drm_mode_create_dumb *args);
+	/**
+	 * @dumb_create_fbdev:
+	 *
+	 * This creates a new dumb buffer for use with DRM fbdev clients and
+	 * returns the resulting buffer handle. This handle can then be wrapped
+	 * up into a framebuffer modeset object.
+	 *
+	 * This is an interface for kernel clients and never called via ioctl.
+	 *
+	 * See @dumb_create for more information.
+	 *
+	 * Returns:
+	 * Zero on success, negative errno on failure.
+	 */
+	int (*dumb_create_fbdev)(struct drm_file *file_priv,
+				 struct drm_device *dev,
+				 struct drm_mode_create_dumb *args);
 	/**
 	 * @dumb_map_offset:
 	 *
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 7/9] drm/fb-helper: Provide fbdev deferred-I/O helpers
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Add drm_fb_helper_vm_page_mkwrite(), a helper to track pages written
by fbdev userspace. DRM drivers should use this function to implement
fbdev deferred I/O.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c | 10 ++++++++++
 include/drm/drm_fb_helper.h     |  9 +++++++++
 2 files changed, 19 insertions(+)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 9d1140a789f4..6203f5ab33af 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -760,6 +760,16 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
 }
 EXPORT_SYMBOL(drm_fb_helper_deferred_io);
 
+vm_fault_t drm_fb_helper_vm_page_mkwrite(struct drm_fb_helper *fb_helper, struct vm_fault *vmf)
+{
+	struct fb_info *info = fb_helper->fbdev;
+
+	if (!info->fbdefio)
+		return 0;
+	return fb_deferred_io_page_mkwrite(info, vmf);
+}
+EXPORT_SYMBOL(drm_fb_helper_vm_page_mkwrite);
+
 /**
  * drm_fb_helper_sys_read - wrapper around fb_sys_read
  * @info: fb_info struct pointer
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 3af4624368d8..8994c65f0b5a 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -232,6 +232,9 @@ void drm_fb_helper_fill_info(struct fb_info *info,
 void drm_fb_helper_deferred_io(struct fb_info *info,
 			       struct list_head *pagelist);
 
+vm_fault_t drm_fb_helper_vm_page_mkwrite(struct drm_fb_helper *fb_helper,
+					 struct vm_fault *vmf);
+
 ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
 			       size_t count, loff_t *ppos);
 ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
@@ -355,6 +358,12 @@ static inline void drm_fb_helper_deferred_io(struct fb_info *info,
 {
 }
 
+static inline vm_fault_t drm_fb_helper_vm_page_mkwrite(struct drm_fb_helper *fb_helper,
+						       struct vm_fault *vmf)
+{
+	return 0;
+}
+
 static inline int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
 {
 	return -ENODEV;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 7/9] drm/fb-helper: Provide fbdev deferred-I/O helpers
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Add drm_fb_helper_vm_page_mkwrite(), a helper to track pages written
by fbdev userspace. DRM drivers should use this function to implement
fbdev deferred I/O.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_fb_helper.c | 10 ++++++++++
 include/drm/drm_fb_helper.h     |  9 +++++++++
 2 files changed, 19 insertions(+)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 9d1140a789f4..6203f5ab33af 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -760,6 +760,16 @@ void drm_fb_helper_deferred_io(struct fb_info *info,
 }
 EXPORT_SYMBOL(drm_fb_helper_deferred_io);
 
+vm_fault_t drm_fb_helper_vm_page_mkwrite(struct drm_fb_helper *fb_helper, struct vm_fault *vmf)
+{
+	struct fb_info *info = fb_helper->fbdev;
+
+	if (!info->fbdefio)
+		return 0;
+	return fb_deferred_io_page_mkwrite(info, vmf);
+}
+EXPORT_SYMBOL(drm_fb_helper_vm_page_mkwrite);
+
 /**
  * drm_fb_helper_sys_read - wrapper around fb_sys_read
  * @info: fb_info struct pointer
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 3af4624368d8..8994c65f0b5a 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -232,6 +232,9 @@ void drm_fb_helper_fill_info(struct fb_info *info,
 void drm_fb_helper_deferred_io(struct fb_info *info,
 			       struct list_head *pagelist);
 
+vm_fault_t drm_fb_helper_vm_page_mkwrite(struct drm_fb_helper *fb_helper,
+					 struct vm_fault *vmf);
+
 ssize_t drm_fb_helper_sys_read(struct fb_info *info, char __user *buf,
 			       size_t count, loff_t *ppos);
 ssize_t drm_fb_helper_sys_write(struct fb_info *info, const char __user *buf,
@@ -355,6 +358,12 @@ static inline void drm_fb_helper_deferred_io(struct fb_info *info,
 {
 }
 
+static inline vm_fault_t drm_fb_helper_vm_page_mkwrite(struct drm_fb_helper *fb_helper,
+						       struct vm_fault *vmf)
+{
+	return 0;
+}
+
 static inline int drm_fb_helper_defio_init(struct drm_fb_helper *fb_helper)
 {
 	return -ENODEV;
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The
created buffer object returned by this function implements deferred
I/O for its mmap operation.

Add this feature to a number of drivers that use GEM SHMEM helpers
as shadow planes over their regular video memory. The new macro
DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM
functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation
on these drivers will now mmap the GEM SHMEM pages directly with
deferred I/O without an intermediate shadow buffer.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_gem_shmem_helper.c  | 197 +++++++++++++++++++++---
 drivers/gpu/drm/gud/gud_drv.c           |   2 +-
 drivers/gpu/drm/hyperv/hyperv_drm_drv.c |   2 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c   |   2 +-
 drivers/gpu/drm/tiny/cirrus.c           |   2 +-
 drivers/gpu/drm/tiny/gm12u320.c         |   2 +-
 drivers/gpu/drm/tiny/simpledrm.c        |   2 +-
 drivers/gpu/drm/udl/udl_drv.c           |   2 +-
 drivers/gpu/drm/vkms/vkms_drv.c         |   2 +-
 include/drm/drm_gem_shmem_helper.h      |  63 +++++++-
 10 files changed, 240 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 8ad0e02991ca..ab7cb7d896c3 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -19,6 +19,7 @@
 #include <drm/drm.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_prime.h>
 #include <drm/drm_print.h>
@@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
 	.vm_ops = &drm_gem_shmem_vm_ops,
 };
 
+static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
+	.free = drm_gem_shmem_object_free,
+	.print_info = drm_gem_shmem_object_print_info,
+	.pin = drm_gem_shmem_object_pin,
+	.unpin = drm_gem_shmem_object_unpin,
+	.get_sg_table = drm_gem_shmem_object_get_sg_table,
+	.vmap = drm_gem_shmem_object_vmap,
+	.vunmap = drm_gem_shmem_object_vunmap,
+	.mmap = drm_gem_shmem_object_mmap_fbdev,
+	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
+};
+
 static struct drm_gem_shmem_object *
-__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
+__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
 {
 	struct drm_gem_shmem_object *shmem;
 	struct drm_gem_object *obj;
@@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
 		obj = &shmem->base;
 	}
 
-	if (!obj->funcs)
-		obj->funcs = &drm_gem_shmem_funcs;
+	if (!obj->funcs) {
+		if (fbdev)
+			obj->funcs = &drm_gem_shmem_funcs_fbdev;
+		else
+			obj->funcs = &drm_gem_shmem_funcs;
+	}
 
 	if (private) {
 		drm_gem_private_object_init(dev, obj, size);
@@ -124,7 +141,7 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
  */
 struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)
 {
-	return __drm_gem_shmem_create(dev, size, false);
+	return __drm_gem_shmem_create(dev, size, false, false);
 }
 EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
 
@@ -415,12 +432,12 @@ EXPORT_SYMBOL(drm_gem_shmem_vunmap);
 static struct drm_gem_shmem_object *
 drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
 				 struct drm_device *dev, size_t size,
-				 uint32_t *handle)
+				 bool fbdev, uint32_t *handle)
 {
 	struct drm_gem_shmem_object *shmem;
 	int ret;
 
-	shmem = drm_gem_shmem_create(dev, size);
+	shmem = __drm_gem_shmem_create(dev, size, false, fbdev);
 	if (IS_ERR(shmem))
 		return shmem;
 
@@ -496,6 +513,29 @@ bool drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
 }
 EXPORT_SYMBOL(drm_gem_shmem_purge);
 
+static int __drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
+				       bool fbdev, struct drm_mode_create_dumb *args)
+{
+	u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	struct drm_gem_shmem_object *shmem;
+
+	if (!args->pitch || !args->size) {
+		args->pitch = min_pitch;
+		args->size = PAGE_ALIGN(args->pitch * args->height);
+	} else {
+		/* ensure sane minimum values */
+		if (args->pitch < min_pitch)
+			args->pitch = min_pitch;
+		if (args->size < args->pitch * args->height)
+			args->size = PAGE_ALIGN(args->pitch * args->height);
+	}
+
+	shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, fbdev,
+						 &args->handle);
+
+	return PTR_ERR_OR_ZERO(shmem);
+}
+
 /**
  * drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
  * @file: DRM file structure to create the dumb buffer for
@@ -516,26 +556,38 @@ EXPORT_SYMBOL(drm_gem_shmem_purge);
 int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
 			      struct drm_mode_create_dumb *args)
 {
-	u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
-	struct drm_gem_shmem_object *shmem;
-
-	if (!args->pitch || !args->size) {
-		args->pitch = min_pitch;
-		args->size = PAGE_ALIGN(args->pitch * args->height);
-	} else {
-		/* ensure sane minimum values */
-		if (args->pitch < min_pitch)
-			args->pitch = min_pitch;
-		if (args->size < args->pitch * args->height)
-			args->size = PAGE_ALIGN(args->pitch * args->height);
-	}
-
-	shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
-
-	return PTR_ERR_OR_ZERO(shmem);
+	return __drm_gem_shmem_dumb_create(file, dev, false, args);
 }
 EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create);
 
+/**
+ * drm_gem_shmem_dumb_create_fbdev - Create a dumb shmem buffer object for fbdev
+ * @file: DRM file structure to create the dumb buffer for
+ * @dev: DRM device
+ * @args: IOCTL data
+ *
+ * This function computes the pitch of the dumb buffer and rounds it up to an
+ * integer number of bytes per pixel. Drivers for hardware that doesn't have
+ * any additional restrictions on the pitch can directly use this function as
+ * their &drm_driver.dumb_create_fbdev callback.
+ *
+ * For hardware with additional restrictions, drivers can adjust the fields
+ * set up by userspace before calling into this function.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
+				    struct drm_mode_create_dumb *args)
+{
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+	return __drm_gem_shmem_dumb_create(file, dev, true, args);
+#else
+	return -ENOSYS;
+#endif
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create_fbdev);
+
 static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
 {
 	struct vm_area_struct *vma = vmf->vma;
@@ -635,6 +687,103 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
 }
 EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap);
 
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+static vm_fault_t drm_gem_shmem_fault_fbdev(struct vm_fault *vmf)
+{
+	struct vm_area_struct *vma = vmf->vma;
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+	loff_t num_pages = obj->size >> PAGE_SHIFT;
+	struct drm_device *dev = obj->dev;
+	vm_fault_t ret;
+	struct page *page;
+	pgoff_t page_offset;
+
+	/* We don't use vmf->pgoff since that has the fake offset */
+	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
+
+	mutex_lock(&shmem->pages_lock);
+
+	if (page_offset >= num_pages || WARN_ON_ONCE(!shmem->pages) || shmem->madv < 0) {
+		ret = VM_FAULT_SIGBUS;
+		goto err_mutex_unlock;
+	}
+
+	page = shmem->pages[page_offset];
+
+	get_page(page);
+
+	if (vmf->vma->vm_file)
+		page->mapping = vmf->vma->vm_file->f_mapping;
+	else
+		drm_err(dev, "no mapping available\n");
+
+	if (drm_WARN_ON_ONCE(dev, !page->mapping)) {
+		ret = VM_FAULT_SIGBUS;
+		goto err_put_page;
+	}
+
+	/* for page_mkclean(); include the fake offset in the page index */
+	page->index = vmf->pgoff;
+
+	vmf->page = page;
+
+	mutex_unlock(&shmem->pages_lock);
+
+	return 0;
+
+err_put_page:
+	put_page(page);
+err_mutex_unlock:
+	mutex_unlock(&shmem->pages_lock);
+	return ret;
+}
+
+static vm_fault_t drm_gem_shmem_vm_page_mkwrite_fbdev(struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vmf->vma->vm_private_data;
+
+	return drm_fb_helper_vm_page_mkwrite(obj->dev->fb_helper, vmf);
+}
+
+const struct vm_operations_struct drm_gem_shmem_vm_ops_fbdev = {
+	.open = drm_gem_shmem_vm_open,
+	.close = drm_gem_shmem_vm_close,
+	.fault = drm_gem_shmem_fault_fbdev,
+	.page_mkwrite = drm_gem_shmem_vm_page_mkwrite_fbdev,
+};
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_ops_fbdev);
+
+int drm_gem_shmem_mmap_fbdev(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = &shmem->base;
+	int ret;
+
+	if (obj->import_attach) {
+		/* Drop the reference drm_gem_mmap_obj() acquired.*/
+		drm_gem_object_put(obj);
+		vma->vm_private_data = NULL;
+
+		return dma_buf_mmap(obj->dma_buf, vma, 0);
+	}
+
+	ret = drm_gem_shmem_get_pages(shmem);
+	if (ret) {
+		drm_gem_vm_close(vma);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+	if (shmem->map_wc)
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap_fbdev);
+#endif
+
 /**
  * drm_gem_shmem_print_info() - Print &drm_gem_shmem_object info for debugfs
  * @shmem: shmem GEM object
@@ -751,7 +900,7 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
 	size_t size = PAGE_ALIGN(attach->dmabuf->size);
 	struct drm_gem_shmem_object *shmem;
 
-	shmem = __drm_gem_shmem_create(dev, size, true);
+	shmem = __drm_gem_shmem_create(dev, size, true, false);
 	if (IS_ERR(shmem))
 		return ERR_CAST(shmem);
 
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
index 3f9d4b9a1e3d..1ac1ff1b2f81 100644
--- a/drivers/gpu/drm/gud/gud_drv.c
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -382,7 +382,7 @@ DEFINE_DRM_GEM_FOPS(gud_fops);
 static const struct drm_driver gud_drm_driver = {
 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
 	.fops			= &gud_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.gem_prime_import	= gud_gem_prime_import,
 	.debugfs_init		= gud_debugfs_init,
 
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index 4a8941fa0815..2701664c127b 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -38,7 +38,7 @@ static struct drm_driver hyperv_driver = {
 	.minor		 = DRIVER_MINOR,
 
 	.fops		 = &hv_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 };
 
 static int hyperv_pci_probe(struct pci_dev *pdev,
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 217844d71ab5..57dd5511e118 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -38,7 +38,7 @@ static const struct drm_driver mgag200_driver = {
 	.major = DRIVER_MAJOR,
 	.minor = DRIVER_MINOR,
 	.patchlevel = DRIVER_PATCHLEVEL,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 };
 
 /*
diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c
index c8e791840862..17d8ca07af94 100644
--- a/drivers/gpu/drm/tiny/cirrus.c
+++ b/drivers/gpu/drm/tiny/cirrus.c
@@ -542,7 +542,7 @@ static const struct drm_driver cirrus_driver = {
 	.minor		 = DRIVER_MINOR,
 
 	.fops		 = &cirrus_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 };
 
 static int cirrus_pci_probe(struct pci_dev *pdev,
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index 648e585d40a8..97946f0637f3 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -620,7 +620,7 @@ static const struct drm_driver gm12u320_drm_driver = {
 	.minor		 = DRIVER_MINOR,
 
 	.fops		 = &gm12u320_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.gem_prime_import = gm12u320_gem_prime_import,
 };
 
diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c
index 768242a78e2b..562d09627330 100644
--- a/drivers/gpu/drm/tiny/simpledrm.c
+++ b/drivers/gpu/drm/tiny/simpledrm.c
@@ -871,7 +871,7 @@ simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev)
 DEFINE_DRM_GEM_FOPS(simpledrm_fops);
 
 static struct drm_driver simpledrm_driver = {
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.name			= DRIVER_NAME,
 	.desc			= DRIVER_DESC,
 	.date			= DRIVER_DATE,
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 5703277c6f52..87f7648e73a5 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -55,7 +55,7 @@ static const struct drm_driver driver = {
 
 	/* GEM hooks */
 	.fops = &udl_driver_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.gem_prime_import = udl_driver_gem_prime_import,
 
 	.name = DRIVER_NAME,
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 0ffe5f0e33f7..645b92149b8b 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -116,7 +116,7 @@ static const struct drm_driver vkms_driver = {
 	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
 	.release		= vkms_release,
 	.fops			= &vkms_driver_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 
 	.debugfs_init           = vkms_config_debugfs_init,
 
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index d0a57853c188..16b0f4b60d33 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -139,6 +139,11 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
 
 extern const struct vm_operations_struct drm_gem_shmem_vm_ops;
 
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+extern const struct vm_operations_struct drm_gem_shmem_vm_ops_fbdev;
+int drm_gem_shmem_mmap_fbdev(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma);
+#endif
+
 /*
  * GEM object functions
  */
@@ -272,6 +277,27 @@ static inline int drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct v
 	return drm_gem_shmem_mmap(shmem, vma);
 }
 
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+/**
+ * drm_gem_shmem_object_mmap_fbdev - GEM object function for drm_gem_shmem_mmap_fbdev()
+ * @obj: GEM object
+ * @vma: VMA for the area to be mapped
+ *
+ * This function wraps drm_gem_shmem_mmap(). Drivers that employ the shmem helpers should
+ * use it as their &drm_gem_object_funcs.mmap handler.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+static inline int drm_gem_shmem_object_mmap_fbdev(struct drm_gem_object *obj,
+						  struct vm_area_struct *vma)
+{
+	struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+	return drm_gem_shmem_mmap_fbdev(shmem, vma);
+}
+#endif
+
 /*
  * Driver ops
  */
@@ -282,18 +308,47 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
 				    struct sg_table *sgt);
 int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
 			      struct drm_mode_create_dumb *args);
+int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
+				    struct drm_mode_create_dumb *args);
 
 /**
- * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ * DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE - Default shmem GEM operations
+ * @dumb_create_func: callback function for .dumb_create
+ * @dumb_create_fbdev_func: callback function for .dumb_create_fbdev
  *
  * This macro provides a shortcut for setting the shmem GEM operations in
- * the &drm_driver structure.
+ * the &drm_driver structure. The callbacks for creating dumb buffers are
+ * given as parameters. Use DRM_GEM_SHMEM_DRIVER_OPS or
+ * DRM_GEM_SHMEM_OPS_WITH_SHADOW_PLANES if possible.
  */
-#define DRM_GEM_SHMEM_DRIVER_OPS \
+#define DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(dumb_create_func, dumb_create_fbdev_func) \
 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd, \
 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle, \
 	.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \
 	.gem_prime_mmap		= drm_gem_prime_mmap, \
-	.dumb_create		= drm_gem_shmem_dumb_create
+	.dumb_create		= dumb_create_func, \
+	.dumb_create_fbdev	= dumb_create_fbdev_func
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS \
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_shmem_dumb_create, NULL)
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES - Default shmem GEM operations
+ *                                               for drivers that use shadow
+ *                                               planes
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure. Drivers that employ shmem GEM for shadow
+ * buffering should use this macro.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES \
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_shmem_dumb_create, \
+						  drm_gem_shmem_dumb_create_fbdev)
 
 #endif /* __DRM_GEM_SHMEM_HELPER_H__ */
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The
created buffer object returned by this function implements deferred
I/O for its mmap operation.

Add this feature to a number of drivers that use GEM SHMEM helpers
as shadow planes over their regular video memory. The new macro
DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM
functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation
on these drivers will now mmap the GEM SHMEM pages directly with
deferred I/O without an intermediate shadow buffer.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_gem_shmem_helper.c  | 197 +++++++++++++++++++++---
 drivers/gpu/drm/gud/gud_drv.c           |   2 +-
 drivers/gpu/drm/hyperv/hyperv_drm_drv.c |   2 +-
 drivers/gpu/drm/mgag200/mgag200_drv.c   |   2 +-
 drivers/gpu/drm/tiny/cirrus.c           |   2 +-
 drivers/gpu/drm/tiny/gm12u320.c         |   2 +-
 drivers/gpu/drm/tiny/simpledrm.c        |   2 +-
 drivers/gpu/drm/udl/udl_drv.c           |   2 +-
 drivers/gpu/drm/vkms/vkms_drv.c         |   2 +-
 include/drm/drm_gem_shmem_helper.h      |  63 +++++++-
 10 files changed, 240 insertions(+), 36 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index 8ad0e02991ca..ab7cb7d896c3 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -19,6 +19,7 @@
 #include <drm/drm.h>
 #include <drm/drm_device.h>
 #include <drm/drm_drv.h>
+#include <drm/drm_fb_helper.h>
 #include <drm/drm_gem_shmem_helper.h>
 #include <drm/drm_prime.h>
 #include <drm/drm_print.h>
@@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
 	.vm_ops = &drm_gem_shmem_vm_ops,
 };
 
+static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
+	.free = drm_gem_shmem_object_free,
+	.print_info = drm_gem_shmem_object_print_info,
+	.pin = drm_gem_shmem_object_pin,
+	.unpin = drm_gem_shmem_object_unpin,
+	.get_sg_table = drm_gem_shmem_object_get_sg_table,
+	.vmap = drm_gem_shmem_object_vmap,
+	.vunmap = drm_gem_shmem_object_vunmap,
+	.mmap = drm_gem_shmem_object_mmap_fbdev,
+	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
+};
+
 static struct drm_gem_shmem_object *
-__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
+__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
 {
 	struct drm_gem_shmem_object *shmem;
 	struct drm_gem_object *obj;
@@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
 		obj = &shmem->base;
 	}
 
-	if (!obj->funcs)
-		obj->funcs = &drm_gem_shmem_funcs;
+	if (!obj->funcs) {
+		if (fbdev)
+			obj->funcs = &drm_gem_shmem_funcs_fbdev;
+		else
+			obj->funcs = &drm_gem_shmem_funcs;
+	}
 
 	if (private) {
 		drm_gem_private_object_init(dev, obj, size);
@@ -124,7 +141,7 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
  */
 struct drm_gem_shmem_object *drm_gem_shmem_create(struct drm_device *dev, size_t size)
 {
-	return __drm_gem_shmem_create(dev, size, false);
+	return __drm_gem_shmem_create(dev, size, false, false);
 }
 EXPORT_SYMBOL_GPL(drm_gem_shmem_create);
 
@@ -415,12 +432,12 @@ EXPORT_SYMBOL(drm_gem_shmem_vunmap);
 static struct drm_gem_shmem_object *
 drm_gem_shmem_create_with_handle(struct drm_file *file_priv,
 				 struct drm_device *dev, size_t size,
-				 uint32_t *handle)
+				 bool fbdev, uint32_t *handle)
 {
 	struct drm_gem_shmem_object *shmem;
 	int ret;
 
-	shmem = drm_gem_shmem_create(dev, size);
+	shmem = __drm_gem_shmem_create(dev, size, false, fbdev);
 	if (IS_ERR(shmem))
 		return shmem;
 
@@ -496,6 +513,29 @@ bool drm_gem_shmem_purge(struct drm_gem_shmem_object *shmem)
 }
 EXPORT_SYMBOL(drm_gem_shmem_purge);
 
+static int __drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
+				       bool fbdev, struct drm_mode_create_dumb *args)
+{
+	u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
+	struct drm_gem_shmem_object *shmem;
+
+	if (!args->pitch || !args->size) {
+		args->pitch = min_pitch;
+		args->size = PAGE_ALIGN(args->pitch * args->height);
+	} else {
+		/* ensure sane minimum values */
+		if (args->pitch < min_pitch)
+			args->pitch = min_pitch;
+		if (args->size < args->pitch * args->height)
+			args->size = PAGE_ALIGN(args->pitch * args->height);
+	}
+
+	shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, fbdev,
+						 &args->handle);
+
+	return PTR_ERR_OR_ZERO(shmem);
+}
+
 /**
  * drm_gem_shmem_dumb_create - Create a dumb shmem buffer object
  * @file: DRM file structure to create the dumb buffer for
@@ -516,26 +556,38 @@ EXPORT_SYMBOL(drm_gem_shmem_purge);
 int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
 			      struct drm_mode_create_dumb *args)
 {
-	u32 min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
-	struct drm_gem_shmem_object *shmem;
-
-	if (!args->pitch || !args->size) {
-		args->pitch = min_pitch;
-		args->size = PAGE_ALIGN(args->pitch * args->height);
-	} else {
-		/* ensure sane minimum values */
-		if (args->pitch < min_pitch)
-			args->pitch = min_pitch;
-		if (args->size < args->pitch * args->height)
-			args->size = PAGE_ALIGN(args->pitch * args->height);
-	}
-
-	shmem = drm_gem_shmem_create_with_handle(file, dev, args->size, &args->handle);
-
-	return PTR_ERR_OR_ZERO(shmem);
+	return __drm_gem_shmem_dumb_create(file, dev, false, args);
 }
 EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create);
 
+/**
+ * drm_gem_shmem_dumb_create_fbdev - Create a dumb shmem buffer object for fbdev
+ * @file: DRM file structure to create the dumb buffer for
+ * @dev: DRM device
+ * @args: IOCTL data
+ *
+ * This function computes the pitch of the dumb buffer and rounds it up to an
+ * integer number of bytes per pixel. Drivers for hardware that doesn't have
+ * any additional restrictions on the pitch can directly use this function as
+ * their &drm_driver.dumb_create_fbdev callback.
+ *
+ * For hardware with additional restrictions, drivers can adjust the fields
+ * set up by userspace before calling into this function.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
+				    struct drm_mode_create_dumb *args)
+{
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+	return __drm_gem_shmem_dumb_create(file, dev, true, args);
+#else
+	return -ENOSYS;
+#endif
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_dumb_create_fbdev);
+
 static vm_fault_t drm_gem_shmem_fault(struct vm_fault *vmf)
 {
 	struct vm_area_struct *vma = vmf->vma;
@@ -635,6 +687,103 @@ int drm_gem_shmem_mmap(struct drm_gem_shmem_object *shmem, struct vm_area_struct
 }
 EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap);
 
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+static vm_fault_t drm_gem_shmem_fault_fbdev(struct vm_fault *vmf)
+{
+	struct vm_area_struct *vma = vmf->vma;
+	struct drm_gem_object *obj = vma->vm_private_data;
+	struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+	loff_t num_pages = obj->size >> PAGE_SHIFT;
+	struct drm_device *dev = obj->dev;
+	vm_fault_t ret;
+	struct page *page;
+	pgoff_t page_offset;
+
+	/* We don't use vmf->pgoff since that has the fake offset */
+	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
+
+	mutex_lock(&shmem->pages_lock);
+
+	if (page_offset >= num_pages || WARN_ON_ONCE(!shmem->pages) || shmem->madv < 0) {
+		ret = VM_FAULT_SIGBUS;
+		goto err_mutex_unlock;
+	}
+
+	page = shmem->pages[page_offset];
+
+	get_page(page);
+
+	if (vmf->vma->vm_file)
+		page->mapping = vmf->vma->vm_file->f_mapping;
+	else
+		drm_err(dev, "no mapping available\n");
+
+	if (drm_WARN_ON_ONCE(dev, !page->mapping)) {
+		ret = VM_FAULT_SIGBUS;
+		goto err_put_page;
+	}
+
+	/* for page_mkclean(); include the fake offset in the page index */
+	page->index = vmf->pgoff;
+
+	vmf->page = page;
+
+	mutex_unlock(&shmem->pages_lock);
+
+	return 0;
+
+err_put_page:
+	put_page(page);
+err_mutex_unlock:
+	mutex_unlock(&shmem->pages_lock);
+	return ret;
+}
+
+static vm_fault_t drm_gem_shmem_vm_page_mkwrite_fbdev(struct vm_fault *vmf)
+{
+	struct drm_gem_object *obj = vmf->vma->vm_private_data;
+
+	return drm_fb_helper_vm_page_mkwrite(obj->dev->fb_helper, vmf);
+}
+
+const struct vm_operations_struct drm_gem_shmem_vm_ops_fbdev = {
+	.open = drm_gem_shmem_vm_open,
+	.close = drm_gem_shmem_vm_close,
+	.fault = drm_gem_shmem_fault_fbdev,
+	.page_mkwrite = drm_gem_shmem_vm_page_mkwrite_fbdev,
+};
+EXPORT_SYMBOL_GPL(drm_gem_shmem_vm_ops_fbdev);
+
+int drm_gem_shmem_mmap_fbdev(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma)
+{
+	struct drm_gem_object *obj = &shmem->base;
+	int ret;
+
+	if (obj->import_attach) {
+		/* Drop the reference drm_gem_mmap_obj() acquired.*/
+		drm_gem_object_put(obj);
+		vma->vm_private_data = NULL;
+
+		return dma_buf_mmap(obj->dma_buf, vma, 0);
+	}
+
+	ret = drm_gem_shmem_get_pages(shmem);
+	if (ret) {
+		drm_gem_vm_close(vma);
+		return ret;
+	}
+
+	vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP;
+	vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
+	vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
+	if (shmem->map_wc)
+		vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(drm_gem_shmem_mmap_fbdev);
+#endif
+
 /**
  * drm_gem_shmem_print_info() - Print &drm_gem_shmem_object info for debugfs
  * @shmem: shmem GEM object
@@ -751,7 +900,7 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
 	size_t size = PAGE_ALIGN(attach->dmabuf->size);
 	struct drm_gem_shmem_object *shmem;
 
-	shmem = __drm_gem_shmem_create(dev, size, true);
+	shmem = __drm_gem_shmem_create(dev, size, true, false);
 	if (IS_ERR(shmem))
 		return ERR_CAST(shmem);
 
diff --git a/drivers/gpu/drm/gud/gud_drv.c b/drivers/gpu/drm/gud/gud_drv.c
index 3f9d4b9a1e3d..1ac1ff1b2f81 100644
--- a/drivers/gpu/drm/gud/gud_drv.c
+++ b/drivers/gpu/drm/gud/gud_drv.c
@@ -382,7 +382,7 @@ DEFINE_DRM_GEM_FOPS(gud_fops);
 static const struct drm_driver gud_drm_driver = {
 	.driver_features	= DRIVER_MODESET | DRIVER_GEM | DRIVER_ATOMIC,
 	.fops			= &gud_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.gem_prime_import	= gud_gem_prime_import,
 	.debugfs_init		= gud_debugfs_init,
 
diff --git a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
index 4a8941fa0815..2701664c127b 100644
--- a/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
+++ b/drivers/gpu/drm/hyperv/hyperv_drm_drv.c
@@ -38,7 +38,7 @@ static struct drm_driver hyperv_driver = {
 	.minor		 = DRIVER_MINOR,
 
 	.fops		 = &hv_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 };
 
 static int hyperv_pci_probe(struct pci_dev *pdev,
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.c b/drivers/gpu/drm/mgag200/mgag200_drv.c
index 217844d71ab5..57dd5511e118 100644
--- a/drivers/gpu/drm/mgag200/mgag200_drv.c
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.c
@@ -38,7 +38,7 @@ static const struct drm_driver mgag200_driver = {
 	.major = DRIVER_MAJOR,
 	.minor = DRIVER_MINOR,
 	.patchlevel = DRIVER_PATCHLEVEL,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 };
 
 /*
diff --git a/drivers/gpu/drm/tiny/cirrus.c b/drivers/gpu/drm/tiny/cirrus.c
index c8e791840862..17d8ca07af94 100644
--- a/drivers/gpu/drm/tiny/cirrus.c
+++ b/drivers/gpu/drm/tiny/cirrus.c
@@ -542,7 +542,7 @@ static const struct drm_driver cirrus_driver = {
 	.minor		 = DRIVER_MINOR,
 
 	.fops		 = &cirrus_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 };
 
 static int cirrus_pci_probe(struct pci_dev *pdev,
diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c
index 648e585d40a8..97946f0637f3 100644
--- a/drivers/gpu/drm/tiny/gm12u320.c
+++ b/drivers/gpu/drm/tiny/gm12u320.c
@@ -620,7 +620,7 @@ static const struct drm_driver gm12u320_drm_driver = {
 	.minor		 = DRIVER_MINOR,
 
 	.fops		 = &gm12u320_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.gem_prime_import = gm12u320_gem_prime_import,
 };
 
diff --git a/drivers/gpu/drm/tiny/simpledrm.c b/drivers/gpu/drm/tiny/simpledrm.c
index 768242a78e2b..562d09627330 100644
--- a/drivers/gpu/drm/tiny/simpledrm.c
+++ b/drivers/gpu/drm/tiny/simpledrm.c
@@ -871,7 +871,7 @@ simpledrm_device_create(struct drm_driver *drv, struct platform_device *pdev)
 DEFINE_DRM_GEM_FOPS(simpledrm_fops);
 
 static struct drm_driver simpledrm_driver = {
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.name			= DRIVER_NAME,
 	.desc			= DRIVER_DESC,
 	.date			= DRIVER_DATE,
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
index 5703277c6f52..87f7648e73a5 100644
--- a/drivers/gpu/drm/udl/udl_drv.c
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -55,7 +55,7 @@ static const struct drm_driver driver = {
 
 	/* GEM hooks */
 	.fops = &udl_driver_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 	.gem_prime_import = udl_driver_gem_prime_import,
 
 	.name = DRIVER_NAME,
diff --git a/drivers/gpu/drm/vkms/vkms_drv.c b/drivers/gpu/drm/vkms/vkms_drv.c
index 0ffe5f0e33f7..645b92149b8b 100644
--- a/drivers/gpu/drm/vkms/vkms_drv.c
+++ b/drivers/gpu/drm/vkms/vkms_drv.c
@@ -116,7 +116,7 @@ static const struct drm_driver vkms_driver = {
 	.driver_features	= DRIVER_MODESET | DRIVER_ATOMIC | DRIVER_GEM,
 	.release		= vkms_release,
 	.fops			= &vkms_driver_fops,
-	DRM_GEM_SHMEM_DRIVER_OPS,
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES,
 
 	.debugfs_init           = vkms_config_debugfs_init,
 
diff --git a/include/drm/drm_gem_shmem_helper.h b/include/drm/drm_gem_shmem_helper.h
index d0a57853c188..16b0f4b60d33 100644
--- a/include/drm/drm_gem_shmem_helper.h
+++ b/include/drm/drm_gem_shmem_helper.h
@@ -139,6 +139,11 @@ void drm_gem_shmem_print_info(const struct drm_gem_shmem_object *shmem,
 
 extern const struct vm_operations_struct drm_gem_shmem_vm_ops;
 
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+extern const struct vm_operations_struct drm_gem_shmem_vm_ops_fbdev;
+int drm_gem_shmem_mmap_fbdev(struct drm_gem_shmem_object *shmem, struct vm_area_struct *vma);
+#endif
+
 /*
  * GEM object functions
  */
@@ -272,6 +277,27 @@ static inline int drm_gem_shmem_object_mmap(struct drm_gem_object *obj, struct v
 	return drm_gem_shmem_mmap(shmem, vma);
 }
 
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+/**
+ * drm_gem_shmem_object_mmap_fbdev - GEM object function for drm_gem_shmem_mmap_fbdev()
+ * @obj: GEM object
+ * @vma: VMA for the area to be mapped
+ *
+ * This function wraps drm_gem_shmem_mmap(). Drivers that employ the shmem helpers should
+ * use it as their &drm_gem_object_funcs.mmap handler.
+ *
+ * Returns:
+ * 0 on success or a negative error code on failure.
+ */
+static inline int drm_gem_shmem_object_mmap_fbdev(struct drm_gem_object *obj,
+						  struct vm_area_struct *vma)
+{
+	struct drm_gem_shmem_object *shmem = to_drm_gem_shmem_obj(obj);
+
+	return drm_gem_shmem_mmap_fbdev(shmem, vma);
+}
+#endif
+
 /*
  * Driver ops
  */
@@ -282,18 +308,47 @@ drm_gem_shmem_prime_import_sg_table(struct drm_device *dev,
 				    struct sg_table *sgt);
 int drm_gem_shmem_dumb_create(struct drm_file *file, struct drm_device *dev,
 			      struct drm_mode_create_dumb *args);
+int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
+				    struct drm_mode_create_dumb *args);
 
 /**
- * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ * DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE - Default shmem GEM operations
+ * @dumb_create_func: callback function for .dumb_create
+ * @dumb_create_fbdev_func: callback function for .dumb_create_fbdev
  *
  * This macro provides a shortcut for setting the shmem GEM operations in
- * the &drm_driver structure.
+ * the &drm_driver structure. The callbacks for creating dumb buffers are
+ * given as parameters. Use DRM_GEM_SHMEM_DRIVER_OPS or
+ * DRM_GEM_SHMEM_OPS_WITH_SHADOW_PLANES if possible.
  */
-#define DRM_GEM_SHMEM_DRIVER_OPS \
+#define DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(dumb_create_func, dumb_create_fbdev_func) \
 	.prime_handle_to_fd	= drm_gem_prime_handle_to_fd, \
 	.prime_fd_to_handle	= drm_gem_prime_fd_to_handle, \
 	.gem_prime_import_sg_table = drm_gem_shmem_prime_import_sg_table, \
 	.gem_prime_mmap		= drm_gem_prime_mmap, \
-	.dumb_create		= drm_gem_shmem_dumb_create
+	.dumb_create		= dumb_create_func, \
+	.dumb_create_fbdev	= dumb_create_fbdev_func
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS - Default shmem GEM operations
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS \
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_shmem_dumb_create, NULL)
+
+/**
+ * DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES - Default shmem GEM operations
+ *                                               for drivers that use shadow
+ *                                               planes
+ *
+ * This macro provides a shortcut for setting the shmem GEM operations in
+ * the &drm_driver structure. Drivers that employ shmem GEM for shadow
+ * buffering should use this macro.
+ */
+#define DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES \
+	DRM_GEM_SHMEM_DRIVER_OPS_WITH_DUMB_CREATE(drm_gem_shmem_dumb_create, \
+						  drm_gem_shmem_dumb_create_fbdev)
 
 #endif /* __DRM_GEM_SHMEM_HELPER_H__ */
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-03 20:58   ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: dri-devel, linux-fbdev, Thomas Zimmermann

Implement struct drm_driver.dumb_create_fbdev with the helpers
provided by GEM SHMEM. Fbdev deferred I/O will now work without
an intermediate shadow buffer for mmap.

As the virtio driver replaces several of the regular GEM SHMEM
functions with its own implementation, some additional code is
necessary make fbdev optimization work. Especially, the driver
has to provide buffer-object functions for fbdev. Add the hook
struct drm_driver.gem_create_object_fbdev, which is similar to
struct drm_driver.gem_create_object and allows for the creation
of dedicated fbdev buffer objects. Wire things up within GEM
SHMEM.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_gem_shmem_helper.c  |  7 +++-
 drivers/gpu/drm/virtio/virtgpu_drv.c    |  2 +
 drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +
 drivers/gpu/drm/virtio/virtgpu_object.c | 54 +++++++++++++++++++++++--
 include/drm/drm_drv.h                   | 10 +++++
 5 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index ab7cb7d896c3..225aa17895bd 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -71,7 +71,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool f
 
 	size = PAGE_ALIGN(size);
 
-	if (dev->driver->gem_create_object) {
+	if (dev->driver->gem_create_object_fbdev && fbdev) {
+		obj = dev->driver->gem_create_object_fbdev(dev, size);
+		if (IS_ERR(obj))
+			return ERR_CAST(obj);
+		shmem = to_drm_gem_shmem_obj(obj);
+	} else if (dev->driver->gem_create_object) {
 		obj = dev->driver->gem_create_object(dev, size);
 		if (IS_ERR(obj))
 			return ERR_CAST(obj);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 5f25a8d15464..ee414f6e785a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -203,6 +203,7 @@ static const struct drm_driver driver = {
 	.postclose = virtio_gpu_driver_postclose,
 
 	.dumb_create = virtio_gpu_mode_dumb_create,
+	.dumb_create_fbdev = virtio_gpu_mode_dumb_create, // same as dumb_create
 	.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
 
 #if defined(CONFIG_DEBUG_FS)
@@ -215,6 +216,7 @@ static const struct drm_driver driver = {
 	.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
 
 	.gem_create_object = virtio_gpu_create_object,
+	.gem_create_object_fbdev = virtio_gpu_create_object_fbdev,
 	.fops = &virtio_gpu_driver_fops,
 
 	.ioctls = virtio_gpu_ioctls,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 0a194aaad419..71556c21c029 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -449,6 +449,8 @@ void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
 void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo);
 struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
 						size_t size);
+struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
+						      size_t size);
 int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 			     struct virtio_gpu_object_params *params,
 			     struct virtio_gpu_object **bo_ptr,
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index f293e6ad52da..255f2d650451 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -132,8 +132,8 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo)
 	return bo->base.base.funcs == &virtio_gpu_shmem_funcs;
 }
 
-struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
-						size_t size)
+static struct drm_gem_object *__virtio_gpu_create_object(struct drm_device *dev,
+							 size_t size)
 {
 	struct virtio_gpu_object_shmem *shmem;
 	struct drm_gem_shmem_object *dshmem;
@@ -143,10 +143,58 @@ struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 
 	dshmem = &shmem->base.base;
-	dshmem->base.funcs = &virtio_gpu_shmem_funcs;
 	return &dshmem->base;
 }
 
+struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
+						size_t size)
+{
+	struct drm_gem_object *obj;
+
+	obj = __virtio_gpu_create_object(dev, size);
+	if (IS_ERR(obj))
+		return obj;
+	obj->funcs = &virtio_gpu_shmem_funcs;
+
+	return obj;
+}
+
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs_fbdev = {
+	.free = virtio_gpu_free_object,
+	.open = virtio_gpu_gem_object_open,
+	.close = virtio_gpu_gem_object_close,
+	.print_info = drm_gem_shmem_object_print_info,
+	.export = virtgpu_gem_prime_export,
+	.pin = drm_gem_shmem_object_pin,
+	.unpin = drm_gem_shmem_object_unpin,
+	.get_sg_table = drm_gem_shmem_object_get_sg_table,
+	.vmap = drm_gem_shmem_object_vmap,
+	.vunmap = drm_gem_shmem_object_vunmap,
+	.mmap = drm_gem_shmem_object_mmap_fbdev,
+	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
+};
+
+struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
+						      size_t size)
+{
+	struct drm_gem_object *obj;
+
+	obj = __virtio_gpu_create_object(dev, size);
+	if (IS_ERR(obj))
+		return obj;
+	obj->funcs = &virtio_gpu_shmem_funcs_fbdev;
+
+	return obj;
+}
+#else
+struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
+						      size_t size)
+{
+	return ERR_PTR(-ENOSYS);
+}
+#endif
+
 static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
 					struct virtio_gpu_object *bo,
 					struct virtio_gpu_mem_entry **ents,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index da4a095de1e7..9081281c1f39 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -298,6 +298,16 @@ struct drm_driver {
 	struct drm_gem_object *(*gem_create_object)(struct drm_device *dev,
 						    size_t size);
 
+	/**
+	 * @gem_create_object_fbdev: constructor for gem objects that back fbdev
+	 *
+	 * Hook for allocating the GEM object struct, for use by the CMA
+	 * and SHMEM GEM helpers. Returns a GEM object on success, or an
+	 * ERR_PTR()-encoded error code otherwise.
+	 */
+	struct drm_gem_object *(*gem_create_object_fbdev)(struct drm_device *dev,
+							  size_t size);
+
 	/**
 	 * @prime_handle_to_fd:
 	 *
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
@ 2022-03-03 20:58   ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-03 20:58 UTC (permalink / raw)
  To: daniel, airlied, mripard, maarten.lankhorst, deller, javierm
  Cc: linux-fbdev, Thomas Zimmermann, dri-devel

Implement struct drm_driver.dumb_create_fbdev with the helpers
provided by GEM SHMEM. Fbdev deferred I/O will now work without
an intermediate shadow buffer for mmap.

As the virtio driver replaces several of the regular GEM SHMEM
functions with its own implementation, some additional code is
necessary make fbdev optimization work. Especially, the driver
has to provide buffer-object functions for fbdev. Add the hook
struct drm_driver.gem_create_object_fbdev, which is similar to
struct drm_driver.gem_create_object and allows for the creation
of dedicated fbdev buffer objects. Wire things up within GEM
SHMEM.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
---
 drivers/gpu/drm/drm_gem_shmem_helper.c  |  7 +++-
 drivers/gpu/drm/virtio/virtgpu_drv.c    |  2 +
 drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +
 drivers/gpu/drm/virtio/virtgpu_object.c | 54 +++++++++++++++++++++++--
 include/drm/drm_drv.h                   | 10 +++++
 5 files changed, 71 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
index ab7cb7d896c3..225aa17895bd 100644
--- a/drivers/gpu/drm/drm_gem_shmem_helper.c
+++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
@@ -71,7 +71,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool f
 
 	size = PAGE_ALIGN(size);
 
-	if (dev->driver->gem_create_object) {
+	if (dev->driver->gem_create_object_fbdev && fbdev) {
+		obj = dev->driver->gem_create_object_fbdev(dev, size);
+		if (IS_ERR(obj))
+			return ERR_CAST(obj);
+		shmem = to_drm_gem_shmem_obj(obj);
+	} else if (dev->driver->gem_create_object) {
 		obj = dev->driver->gem_create_object(dev, size);
 		if (IS_ERR(obj))
 			return ERR_CAST(obj);
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.c b/drivers/gpu/drm/virtio/virtgpu_drv.c
index 5f25a8d15464..ee414f6e785a 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.c
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.c
@@ -203,6 +203,7 @@ static const struct drm_driver driver = {
 	.postclose = virtio_gpu_driver_postclose,
 
 	.dumb_create = virtio_gpu_mode_dumb_create,
+	.dumb_create_fbdev = virtio_gpu_mode_dumb_create, // same as dumb_create
 	.dumb_map_offset = virtio_gpu_mode_dumb_mmap,
 
 #if defined(CONFIG_DEBUG_FS)
@@ -215,6 +216,7 @@ static const struct drm_driver driver = {
 	.gem_prime_import_sg_table = virtgpu_gem_prime_import_sg_table,
 
 	.gem_create_object = virtio_gpu_create_object,
+	.gem_create_object_fbdev = virtio_gpu_create_object_fbdev,
 	.fops = &virtio_gpu_driver_fops,
 
 	.ioctls = virtio_gpu_ioctls,
diff --git a/drivers/gpu/drm/virtio/virtgpu_drv.h b/drivers/gpu/drm/virtio/virtgpu_drv.h
index 0a194aaad419..71556c21c029 100644
--- a/drivers/gpu/drm/virtio/virtgpu_drv.h
+++ b/drivers/gpu/drm/virtio/virtgpu_drv.h
@@ -449,6 +449,8 @@ void virtio_gpu_fence_event_process(struct virtio_gpu_device *vdev,
 void virtio_gpu_cleanup_object(struct virtio_gpu_object *bo);
 struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
 						size_t size);
+struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
+						      size_t size);
 int virtio_gpu_object_create(struct virtio_gpu_device *vgdev,
 			     struct virtio_gpu_object_params *params,
 			     struct virtio_gpu_object **bo_ptr,
diff --git a/drivers/gpu/drm/virtio/virtgpu_object.c b/drivers/gpu/drm/virtio/virtgpu_object.c
index f293e6ad52da..255f2d650451 100644
--- a/drivers/gpu/drm/virtio/virtgpu_object.c
+++ b/drivers/gpu/drm/virtio/virtgpu_object.c
@@ -132,8 +132,8 @@ bool virtio_gpu_is_shmem(struct virtio_gpu_object *bo)
 	return bo->base.base.funcs == &virtio_gpu_shmem_funcs;
 }
 
-struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
-						size_t size)
+static struct drm_gem_object *__virtio_gpu_create_object(struct drm_device *dev,
+							 size_t size)
 {
 	struct virtio_gpu_object_shmem *shmem;
 	struct drm_gem_shmem_object *dshmem;
@@ -143,10 +143,58 @@ struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
 		return ERR_PTR(-ENOMEM);
 
 	dshmem = &shmem->base.base;
-	dshmem->base.funcs = &virtio_gpu_shmem_funcs;
 	return &dshmem->base;
 }
 
+struct drm_gem_object *virtio_gpu_create_object(struct drm_device *dev,
+						size_t size)
+{
+	struct drm_gem_object *obj;
+
+	obj = __virtio_gpu_create_object(dev, size);
+	if (IS_ERR(obj))
+		return obj;
+	obj->funcs = &virtio_gpu_shmem_funcs;
+
+	return obj;
+}
+
+#if defined(CONFIG_DRM_FBDEV_EMULATION)
+static const struct drm_gem_object_funcs virtio_gpu_shmem_funcs_fbdev = {
+	.free = virtio_gpu_free_object,
+	.open = virtio_gpu_gem_object_open,
+	.close = virtio_gpu_gem_object_close,
+	.print_info = drm_gem_shmem_object_print_info,
+	.export = virtgpu_gem_prime_export,
+	.pin = drm_gem_shmem_object_pin,
+	.unpin = drm_gem_shmem_object_unpin,
+	.get_sg_table = drm_gem_shmem_object_get_sg_table,
+	.vmap = drm_gem_shmem_object_vmap,
+	.vunmap = drm_gem_shmem_object_vunmap,
+	.mmap = drm_gem_shmem_object_mmap_fbdev,
+	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
+};
+
+struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
+						      size_t size)
+{
+	struct drm_gem_object *obj;
+
+	obj = __virtio_gpu_create_object(dev, size);
+	if (IS_ERR(obj))
+		return obj;
+	obj->funcs = &virtio_gpu_shmem_funcs_fbdev;
+
+	return obj;
+}
+#else
+struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
+						      size_t size)
+{
+	return ERR_PTR(-ENOSYS);
+}
+#endif
+
 static int virtio_gpu_object_shmem_init(struct virtio_gpu_device *vgdev,
 					struct virtio_gpu_object *bo,
 					struct virtio_gpu_mem_entry **ents,
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index da4a095de1e7..9081281c1f39 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -298,6 +298,16 @@ struct drm_driver {
 	struct drm_gem_object *(*gem_create_object)(struct drm_device *dev,
 						    size_t size);
 
+	/**
+	 * @gem_create_object_fbdev: constructor for gem objects that back fbdev
+	 *
+	 * Hook for allocating the GEM object struct, for use by the CMA
+	 * and SHMEM GEM helpers. Returns a GEM object on success, or an
+	 * ERR_PTR()-encoded error code otherwise.
+	 */
+	struct drm_gem_object *(*gem_create_object_fbdev)(struct drm_device *dev,
+							  size_t size);
+
 	/**
 	 * @prime_handle_to_fd:
 	 *
-- 
2.35.1


^ permalink raw reply related	[flat|nested] 62+ messages in thread

* Re: [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB
  2022-03-03 20:58 ` Thomas Zimmermann
@ 2022-03-08  9:13   ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08  9:13 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

Hello Thomas,

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Rework the fbdev deferred-I/O to not interfere with fields of struct
> page. Make the code more flexible and implement GEM SHMEM mmap on top
> of it.
> 
> This patchset removes the need for a special shadow framebuffer for
> fbdev mmap when using GEM SHMEM. SHMEM pages are now mmap'ed from
> /dev/fb directly.
>

Interesting. I wonder if you have any performance improvements after
dropping the shadow buffer.

> Patches 2 and 3 rework the fbdev deferred I/O code. It now allows
> drivers to have better control of the mmap operations. All references
> to fields in struct page are gone. The rsp state is help in a 
> separate pageref structure.
>

That's a very nice cleanup. This really was a huge layering violation.
 
> Patches 4 to 7 provide callbacks an helpers to implement deferred I/O
> with DRM drivers. Specifically, patch 6 introduces a callback to create
> a dumb buffer for fbdev. This will be useful for many drivers that
> currently cannot use generic fbdev emulation because of special placement
> needs of the BO, such as amdgpu or radeon. The drivers can handle the
> differences to regular dumb buffers in their new callback implementation.
> 
> Patch 8 extends the GEM SHMEM memory manager with a new helper for fbdev
> dumb-buffer creation. The returned BO has it's mmap set up to implement
> deferred I/O with SHMEM pages. No additional shadow buffer is requires
> any longer. Many drivers can immediatelly benefit from this change.
> 
> Patch 9 extends virtgpu to support fbdev dumb buffers. It's build on
> top of GEM SHMEM, but has some modifications that need to be implemented
> for fbdev as well.
> 
> There's no immediate fbdev performance improvement from this patchset.
> Most of all, it removes unnecessary shadow framebuffers and rsp memcpys.
> A shadow fb for a FullHD display is ~8 MiB, which we now save. The patches
> do reduce latency between drawing to the fbdev buffer to displaying
> on the screen. Watching a video on the fbdev console felt smoother and
> had less flickering.
>

Awesome. And you also answered here the question I had above.

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB
@ 2022-03-08  9:13   ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08  9:13 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

Hello Thomas,

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Rework the fbdev deferred-I/O to not interfere with fields of struct
> page. Make the code more flexible and implement GEM SHMEM mmap on top
> of it.
> 
> This patchset removes the need for a special shadow framebuffer for
> fbdev mmap when using GEM SHMEM. SHMEM pages are now mmap'ed from
> /dev/fb directly.
>

Interesting. I wonder if you have any performance improvements after
dropping the shadow buffer.

> Patches 2 and 3 rework the fbdev deferred I/O code. It now allows
> drivers to have better control of the mmap operations. All references
> to fields in struct page are gone. The rsp state is help in a 
> separate pageref structure.
>

That's a very nice cleanup. This really was a huge layering violation.
 
> Patches 4 to 7 provide callbacks an helpers to implement deferred I/O
> with DRM drivers. Specifically, patch 6 introduces a callback to create
> a dumb buffer for fbdev. This will be useful for many drivers that
> currently cannot use generic fbdev emulation because of special placement
> needs of the BO, such as amdgpu or radeon. The drivers can handle the
> differences to regular dumb buffers in their new callback implementation.
> 
> Patch 8 extends the GEM SHMEM memory manager with a new helper for fbdev
> dumb-buffer creation. The returned BO has it's mmap set up to implement
> deferred I/O with SHMEM pages. No additional shadow buffer is requires
> any longer. Many drivers can immediatelly benefit from this change.
> 
> Patch 9 extends virtgpu to support fbdev dumb buffers. It's build on
> top of GEM SHMEM, but has some modifications that need to be implemented
> for fbdev as well.
> 
> There's no immediate fbdev performance improvement from this patchset.
> Most of all, it removes unnecessary shadow framebuffers and rsp memcpys.
> A shadow fb for a FullHD display is ~8 MiB, which we now save. The patches
> do reduce latency between drawing to the fbdev buffer to displaying
> on the screen. Watching a video on the fbdev console felt smoother and
> had less flickering.
>

Awesome. And you also answered here the question I had above.

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08  9:31     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08  9:31 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Don't select shadow buffering for the fbdev console explicitly. The
> fbdev emulation's heuristic will enable it for any framebuffer with
> .dirty callback.
>

Indeed it does. Not related to your series but looking at this
patch I noticed that drivers/gpu/drm/tiny/bochs.c will be the
only driver that sets .prefer_shadow_fbdev after this lands.

The driver is using GEM so I wonder if after your series this
DRM driver could have a .dirty callback and the field just be
dropped? Or there would still be a case where it is needed ?

Anyway, just wanted to mention in case I forget later.

Your patch looks good to me and I think it could be pushed
without waiting for the other patches in the series.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
@ 2022-03-08  9:31     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08  9:31 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Don't select shadow buffering for the fbdev console explicitly. The
> fbdev emulation's heuristic will enable it for any framebuffer with
> .dirty callback.
>

Indeed it does. Not related to your series but looking at this
patch I noticed that drivers/gpu/drm/tiny/bochs.c will be the
only driver that sets .prefer_shadow_fbdev after this lands.

The driver is using GEM so I wonder if after your series this
DRM driver could have a .dirty callback and the field just be
dropped? Or there would still be a case where it is needed ?

Anyway, just wanted to mention in case I forget later.

Your patch looks good to me and I think it could be pushed
without waiting for the other patches in the series.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB
  2022-03-08  9:13   ` Javier Martinez Canillas
@ 2022-03-08  9:44     ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-08  9:44 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 2972 bytes --]

Hi Javier

Am 08.03.22 um 10:13 schrieb Javier Martinez Canillas:
> Hello Thomas,
> 
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Rework the fbdev deferred-I/O to not interfere with fields of struct
>> page. Make the code more flexible and implement GEM SHMEM mmap on top
>> of it.
>>
>> This patchset removes the need for a special shadow framebuffer for
>> fbdev mmap when using GEM SHMEM. SHMEM pages are now mmap'ed from
>> /dev/fb directly.
>>
> 
> Interesting. I wonder if you have any performance improvements after
> dropping the shadow buffer.
> 
>> Patches 2 and 3 rework the fbdev deferred I/O code. It now allows
>> drivers to have better control of the mmap operations. All references
>> to fields in struct page are gone. The rsp state is help in a
>> separate pageref structure.
>>
> 
> That's a very nice cleanup. This really was a huge layering violation.
>   
>> Patches 4 to 7 provide callbacks an helpers to implement deferred I/O
>> with DRM drivers. Specifically, patch 6 introduces a callback to create
>> a dumb buffer for fbdev. This will be useful for many drivers that
>> currently cannot use generic fbdev emulation because of special placement
>> needs of the BO, such as amdgpu or radeon. The drivers can handle the
>> differences to regular dumb buffers in their new callback implementation.
>>
>> Patch 8 extends the GEM SHMEM memory manager with a new helper for fbdev
>> dumb-buffer creation. The returned BO has it's mmap set up to implement
>> deferred I/O with SHMEM pages. No additional shadow buffer is requires
>> any longer. Many drivers can immediatelly benefit from this change.
>>
>> Patch 9 extends virtgpu to support fbdev dumb buffers. It's build on
>> top of GEM SHMEM, but has some modifications that need to be implemented
>> for fbdev as well.
>>
>> There's no immediate fbdev performance improvement from this patchset.
>> Most of all, it removes unnecessary shadow framebuffers and rsp memcpys.
>> A shadow fb for a FullHD display is ~8 MiB, which we now save. The patches
>> do reduce latency between drawing to the fbdev buffer to displaying
>> on the screen. Watching a video on the fbdev console felt smoother and
>> had less flickering.
>>
> 
> Awesome. And you also answered here the question I had above.

There's no significant improvement in performance.

But when I play Bug Buck Bunny in mplayer's benchmark mode, the 
displayed animation looks smoother than before. The test finishes in 73 
seconds instead of 74. I guess that one-second improvement comes from 
the memcpy that we now save. I use a somewhat recent Core i7 for 
testing. On old hardware, we might get more performance out of it. I can 
really only guess here.

Best regards
Thomas

> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB
@ 2022-03-08  9:44     ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-08  9:44 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2972 bytes --]

Hi Javier

Am 08.03.22 um 10:13 schrieb Javier Martinez Canillas:
> Hello Thomas,
> 
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Rework the fbdev deferred-I/O to not interfere with fields of struct
>> page. Make the code more flexible and implement GEM SHMEM mmap on top
>> of it.
>>
>> This patchset removes the need for a special shadow framebuffer for
>> fbdev mmap when using GEM SHMEM. SHMEM pages are now mmap'ed from
>> /dev/fb directly.
>>
> 
> Interesting. I wonder if you have any performance improvements after
> dropping the shadow buffer.
> 
>> Patches 2 and 3 rework the fbdev deferred I/O code. It now allows
>> drivers to have better control of the mmap operations. All references
>> to fields in struct page are gone. The rsp state is help in a
>> separate pageref structure.
>>
> 
> That's a very nice cleanup. This really was a huge layering violation.
>   
>> Patches 4 to 7 provide callbacks an helpers to implement deferred I/O
>> with DRM drivers. Specifically, patch 6 introduces a callback to create
>> a dumb buffer for fbdev. This will be useful for many drivers that
>> currently cannot use generic fbdev emulation because of special placement
>> needs of the BO, such as amdgpu or radeon. The drivers can handle the
>> differences to regular dumb buffers in their new callback implementation.
>>
>> Patch 8 extends the GEM SHMEM memory manager with a new helper for fbdev
>> dumb-buffer creation. The returned BO has it's mmap set up to implement
>> deferred I/O with SHMEM pages. No additional shadow buffer is requires
>> any longer. Many drivers can immediatelly benefit from this change.
>>
>> Patch 9 extends virtgpu to support fbdev dumb buffers. It's build on
>> top of GEM SHMEM, but has some modifications that need to be implemented
>> for fbdev as well.
>>
>> There's no immediate fbdev performance improvement from this patchset.
>> Most of all, it removes unnecessary shadow framebuffers and rsp memcpys.
>> A shadow fb for a FullHD display is ~8 MiB, which we now save. The patches
>> do reduce latency between drawing to the fbdev buffer to displaying
>> on the screen. Watching a video on the fbdev console felt smoother and
>> had less flickering.
>>
> 
> Awesome. And you also answered here the question I had above.

There's no significant improvement in performance.

But when I play Bug Buck Bunny in mplayer's benchmark mode, the 
displayed animation looks smoother than before. The test finishes in 73 
seconds instead of 74. I guess that one-second improvement comes from 
the memcpy that we now save. I use a somewhat recent Core i7 for 
testing. On old hardware, we might get more performance out of it. I can 
really only guess here.

Best regards
Thomas

> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
  2022-03-08  9:31     ` Javier Martinez Canillas
@ 2022-03-08  9:56       ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-08  9:56 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 1961 bytes --]

Hi

Am 08.03.22 um 10:31 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Don't select shadow buffering for the fbdev console explicitly. The
>> fbdev emulation's heuristic will enable it for any framebuffer with
>> .dirty callback.
>>
> 
> Indeed it does. Not related to your series but looking at this
> patch I noticed that drivers/gpu/drm/tiny/bochs.c will be the
> only driver that sets .prefer_shadow_fbdev after this lands.
> 
> The driver is using GEM so I wonder if after your series this
> DRM driver could have a .dirty callback and the field just be
> dropped? Or there would still be a case where it is needed ?
Bochs uses VRAM helpers (i.e., TTM). Fbdev and userspace would directly 
write into that buffer memory without a copy. So the dirty function 
would be empty.

Other drivers with VRAM helpers (e.g., hibmc, ast) operate on uncached 
I/O memory AFAICT. So they set .prefer_shadow, which also affects 
userspace. Bochs uses cached memory and shouldn't need prefer_shadow. 
Setting prefer_shadow_fbdev is only there for making the fbdev buffer 
object evictable from video memory.

As it stands, using prefer_shadow_fbdev is the cleanest solution, even 
if bochs is the only user of that field.

Alternatively, we could make it a requirement that qemu provides enough 
video memory for bochs to unconditionally pin the fbdev BO there without 
ever evicting. I guess, that would mean 32 MiB of VRAM at least.

Best regards
Thomas

> 
> Anyway, just wanted to mention in case I forget later.
> 
> Your patch looks good to me and I think it could be pushed
> without waiting for the other patches in the series.
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
@ 2022-03-08  9:56       ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-08  9:56 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 1961 bytes --]

Hi

Am 08.03.22 um 10:31 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Don't select shadow buffering for the fbdev console explicitly. The
>> fbdev emulation's heuristic will enable it for any framebuffer with
>> .dirty callback.
>>
> 
> Indeed it does. Not related to your series but looking at this
> patch I noticed that drivers/gpu/drm/tiny/bochs.c will be the
> only driver that sets .prefer_shadow_fbdev after this lands.
> 
> The driver is using GEM so I wonder if after your series this
> DRM driver could have a .dirty callback and the field just be
> dropped? Or there would still be a case where it is needed ?
Bochs uses VRAM helpers (i.e., TTM). Fbdev and userspace would directly 
write into that buffer memory without a copy. So the dirty function 
would be empty.

Other drivers with VRAM helpers (e.g., hibmc, ast) operate on uncached 
I/O memory AFAICT. So they set .prefer_shadow, which also affects 
userspace. Bochs uses cached memory and shouldn't need prefer_shadow. 
Setting prefer_shadow_fbdev is only there for making the fbdev buffer 
object evictable from video memory.

As it stands, using prefer_shadow_fbdev is the cleanest solution, even 
if bochs is the only user of that field.

Alternatively, we could make it a requirement that qemu provides enough 
video memory for bochs to unconditionally pin the fbdev BO there without 
ever evicting. I guess, that would mean 32 MiB of VRAM at least.

Best regards
Thomas

> 
> Anyway, just wanted to mention in case I forget later.
> 
> Your patch looks good to me and I think it could be pushed
> without waiting for the other patches in the series.
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
  2022-03-08  9:56       ` Thomas Zimmermann
@ 2022-03-08  9:58         ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08  9:58 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/8/22 10:56, Thomas Zimmermann wrote:
> Hi
> 
> Am 08.03.22 um 10:31 schrieb Javier Martinez Canillas:
>> On 3/3/22 21:58, Thomas Zimmermann wrote:
>>> Don't select shadow buffering for the fbdev console explicitly. The
>>> fbdev emulation's heuristic will enable it for any framebuffer with
>>> .dirty callback.
>>>
>>
>> Indeed it does. Not related to your series but looking at this
>> patch I noticed that drivers/gpu/drm/tiny/bochs.c will be the
>> only driver that sets .prefer_shadow_fbdev after this lands.
>>
>> The driver is using GEM so I wonder if after your series this
>> DRM driver could have a .dirty callback and the field just be
>> dropped? Or there would still be a case where it is needed ?
> Bochs uses VRAM helpers (i.e., TTM). Fbdev and userspace would directly 
> write into that buffer memory without a copy. So the dirty function 
> would be empty.
> 
> Other drivers with VRAM helpers (e.g., hibmc, ast) operate on uncached 
> I/O memory AFAICT. So they set .prefer_shadow, which also affects 
> userspace. Bochs uses cached memory and shouldn't need prefer_shadow. 
> Setting prefer_shadow_fbdev is only there for making the fbdev buffer 
> object evictable from video memory.
> 
> As it stands, using prefer_shadow_fbdev is the cleanest solution, even 
> if bochs is the only user of that field.
> 
> Alternatively, we could make it a requirement that qemu provides enough 
> video memory for bochs to unconditionally pin the fbdev BO there without 
> ever evicting. I guess, that would mean 32 MiB of VRAM at least.
>

I see. Thanks a lot for this explanation.
 
> Best regards
> Thomas
> 

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering
@ 2022-03-08  9:58         ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08  9:58 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/8/22 10:56, Thomas Zimmermann wrote:
> Hi
> 
> Am 08.03.22 um 10:31 schrieb Javier Martinez Canillas:
>> On 3/3/22 21:58, Thomas Zimmermann wrote:
>>> Don't select shadow buffering for the fbdev console explicitly. The
>>> fbdev emulation's heuristic will enable it for any framebuffer with
>>> .dirty callback.
>>>
>>
>> Indeed it does. Not related to your series but looking at this
>> patch I noticed that drivers/gpu/drm/tiny/bochs.c will be the
>> only driver that sets .prefer_shadow_fbdev after this lands.
>>
>> The driver is using GEM so I wonder if after your series this
>> DRM driver could have a .dirty callback and the field just be
>> dropped? Or there would still be a case where it is needed ?
> Bochs uses VRAM helpers (i.e., TTM). Fbdev and userspace would directly 
> write into that buffer memory without a copy. So the dirty function 
> would be empty.
> 
> Other drivers with VRAM helpers (e.g., hibmc, ast) operate on uncached 
> I/O memory AFAICT. So they set .prefer_shadow, which also affects 
> userspace. Bochs uses cached memory and shouldn't need prefer_shadow. 
> Setting prefer_shadow_fbdev is only there for making the fbdev buffer 
> object evictable from video memory.
> 
> As it stands, using prefer_shadow_fbdev is the cleanest solution, even 
> if bochs is the only user of that field.
> 
> Alternatively, we could make it a requirement that qemu provides enough 
> video memory for bochs to unconditionally pin the fbdev BO there without 
> ever evicting. I guess, that would mean 32 MiB of VRAM at least.
>

I see. Thanks a lot for this explanation.
 
> Best regards
> Thomas
> 

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/9] fbdev: Put mmap for deferred I/O into drivers
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 14:03     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 14:03 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> 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.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

[snip]

>  
> +	/*
> +	 * FB deferred I/O want you to handle mmap in your drivers. At a
> +	 * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
> +	 */
> +	if (WARN_ON_ONCE(info->fbdefio))
> +		return -ENODEV;
> +

Maybe part of that comment could be printed as a WARN_ON_ONCE() message ?

Regardless, the patch looks good to me:

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 2/9] fbdev: Put mmap for deferred I/O into drivers
@ 2022-03-08 14:03     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 14:03 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> 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.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

[snip]

>  
> +	/*
> +	 * FB deferred I/O want you to handle mmap in your drivers. At a
> +	 * minimum, point struct fb_ops.fb_mmap to fb_deferred_io_mmap().
> +	 */
> +	if (WARN_ON_ONCE(info->fbdefio))
> +		return -ENODEV;
> +

Maybe part of that comment could be printed as a WARN_ON_ONCE() message ?

Regardless, the patch looks good to me:

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 14:42     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 14:42 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Store the per-page state for fbdev's deferred I/O in struct
> fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
> that have to be flushed out to video memory. Update all affected
> drivers.
> 
> Like with pages before, fbdev acquires a pageref when an mmaped page
> of the framebuffer is being written to. It holds the pageref in a
> list of all currently written pagerefs until it flushes the written
> pages to video memory. Writeback occurs periodically. After writeback
> fbdev releases all pagerefs and builds up a new dirty list until the
> next writeback occurs.
> 
> Using pagerefs has a number of benefits.
> 
> For pages of the framebuffer, the deferred I/O code used struct
> page.lru as an entry into the list of dirty pages. The lru field is
> owned by the page cache, which makes deferred I/O incompatible with
> some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
> struct fb_deferred_io_pageref now provides an entry into a list of
> dirty framebuffer pages, free'ing lru for use with the page cache.
> 
> Drivers also assumed that struct page.index is the page offset into
> the framebuffer. This is not true for DRM buffers, which are located
> at various offset within a mapped area. struct fb_deferred_io_pageref
> explicitly stores an offset into the framebuffer. struct page.index
> is now only the page offset into the mapped area.
> 
> These changes will allow DRM to use fbdev deferred I/O without an
> intermediate shadow buffer.
> 

As mentioned, this is a very nice cleanup.

> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

[snip]

> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
> index 33f355907fbb..a35f695727c9 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
> @@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
>  	struct vmw_fb_par *par = info->par;
>  	unsigned long start, end, min, max;
>  	unsigned long flags;
> -	struct page *page;
> +	struct fb_deferred_io_pageref *pageref;
>  	int y1, y2;
>  
>  	min = ULONG_MAX;
>  	max = 0;
> -	list_for_each_entry(page, pagelist, lru) {
> +	list_for_each_entry(pageref, pagelist, list) {
> +		struct page *page = pageref->page;
>  		start = page->index << PAGE_SHIFT;

Do you think that may be worth it to also decouple the struct page.index and 
store the index as a struct fb_deferred_io_pageref.index field ? 

It's unlikely that this would change but it feels as if the layers are more
separated that way, since no driver will access struct page fields directly.

The mapping would be 1:1 anyways just like it's the case for the page offset.

In fact, that could allow to even avoid declaring a struct page *page here.

[snip]

> @@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
>  	spin_unlock(&par->dirty_lock);
>  
>  	/* Mark display lines as dirty */
> -	list_for_each_entry(page, pagelist, lru) {
> +	list_for_each_entry(pageref, pagelist, list) {
> +		struct page *page = pageref->page;

Same here and the other drivers. You only need the page for the index AFAICT.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
@ 2022-03-08 14:42     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 14:42 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Store the per-page state for fbdev's deferred I/O in struct
> fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
> that have to be flushed out to video memory. Update all affected
> drivers.
> 
> Like with pages before, fbdev acquires a pageref when an mmaped page
> of the framebuffer is being written to. It holds the pageref in a
> list of all currently written pagerefs until it flushes the written
> pages to video memory. Writeback occurs periodically. After writeback
> fbdev releases all pagerefs and builds up a new dirty list until the
> next writeback occurs.
> 
> Using pagerefs has a number of benefits.
> 
> For pages of the framebuffer, the deferred I/O code used struct
> page.lru as an entry into the list of dirty pages. The lru field is
> owned by the page cache, which makes deferred I/O incompatible with
> some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
> struct fb_deferred_io_pageref now provides an entry into a list of
> dirty framebuffer pages, free'ing lru for use with the page cache.
> 
> Drivers also assumed that struct page.index is the page offset into
> the framebuffer. This is not true for DRM buffers, which are located
> at various offset within a mapped area. struct fb_deferred_io_pageref
> explicitly stores an offset into the framebuffer. struct page.index
> is now only the page offset into the mapped area.
> 
> These changes will allow DRM to use fbdev deferred I/O without an
> intermediate shadow buffer.
> 

As mentioned, this is a very nice cleanup.

> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

[snip]

> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
> index 33f355907fbb..a35f695727c9 100644
> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
> @@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
>  	struct vmw_fb_par *par = info->par;
>  	unsigned long start, end, min, max;
>  	unsigned long flags;
> -	struct page *page;
> +	struct fb_deferred_io_pageref *pageref;
>  	int y1, y2;
>  
>  	min = ULONG_MAX;
>  	max = 0;
> -	list_for_each_entry(page, pagelist, lru) {
> +	list_for_each_entry(pageref, pagelist, list) {
> +		struct page *page = pageref->page;
>  		start = page->index << PAGE_SHIFT;

Do you think that may be worth it to also decouple the struct page.index and 
store the index as a struct fb_deferred_io_pageref.index field ? 

It's unlikely that this would change but it feels as if the layers are more
separated that way, since no driver will access struct page fields directly.

The mapping would be 1:1 anyways just like it's the case for the page offset.

In fact, that could allow to even avoid declaring a struct page *page here.

[snip]

> @@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
>  	spin_unlock(&par->dirty_lock);
>  
>  	/* Mark display lines as dirty */
> -	list_for_each_entry(page, pagelist, lru) {
> +	list_for_each_entry(pageref, pagelist, list) {
> +		struct page *page = pageref->page;

Same here and the other drivers. You only need the page for the index AFAICT.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 4/9] fbdev: Export helper for implementing page_mkwrite
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 17:21     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 17:21 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Refactor the page-write handler and export it as helper function
> fb_deferred_io_page_mkwrite(). Drivers that implement struct
> vm_operations_struct.page_mkwrite for deferred I/O should use the
> function to let fbdev track written pages of mmap'ed framebuffer
> memory.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 4/9] fbdev: Export helper for implementing page_mkwrite
@ 2022-03-08 17:21     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 17:21 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Refactor the page-write handler and export it as helper function
> fb_deferred_io_page_mkwrite(). Drivers that implement struct
> vm_operations_struct.page_mkwrite for deferred I/O should use the
> function to let fbdev track written pages of mmap'ed framebuffer
> memory.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 5/9] drm/fb-helper: Separate deferred I/O from shadow buffers
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 17:24     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 17:24 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> DRM drivers will be able to handle deferred I/O by themselves. So
> a driver will be able to use deferred I/O without an intermediate
> shadow buffer.
> 
> Prepare fbdev emulation by separating shadow buffers and deferred
> I/O from each other.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 5/9] drm/fb-helper: Separate deferred I/O from shadow buffers
@ 2022-03-08 17:24     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 17:24 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> DRM drivers will be able to handle deferred I/O by themselves. So
> a driver will be able to use deferred I/O without an intermediate
> shadow buffer.
> 
> Prepare fbdev emulation by separating shadow buffers and deferred
> I/O from each other.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 17:51     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 17:51 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Provide struct drm_driver.dumb_create_fbdev, a callback hook for
> fbdev dumb buffers. Wire up fbdev and client helpers to use the new
> interface, if present.
> 
> This acknowledges the fact that fbdev buffers are different. The most
> significant difference to regular GEM BOs is in mmap semantics. Fbdev
> userspace treats the pages as video memory, which makes it impossible
> to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
> the BO permanently or install an intermediate shadow buffer for mmap.
> 
> So far, fbdev memory came from dumb buffers and DRM drivers had no
> means of detecting this without reimplementing a good part of the fbdev
> code. With the new callback, drivers can perma-pin fbdev buffer objects
> if needed.
> 
> Several drivers also require damage handling, which fbdev implements
> with its deferred I/O helpers. The new callback allows a driver's memory
> manager to set up a suitable mmap.
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  drivers/gpu/drm/drm_client.c        | 14 +++++++----
>  drivers/gpu/drm/drm_crtc_internal.h |  3 +++
>  drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
>  drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
>  include/drm/drm_client.h            |  3 ++-
>  include/drm/drm_drv.h               | 17 ++++++++++++++
>  6 files changed, 84 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
> index af3b7395bf69..c964064651d5 100644
> --- a/drivers/gpu/drm/drm_client.c
> +++ b/drivers/gpu/drm/drm_client.c
> @@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
>  }
>  
>  static struct drm_client_buffer *
> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
> +			 bool fbdev)
>  {
>  	const struct drm_format_info *info = drm_format_info(format);
>  	struct drm_mode_create_dumb dumb_args = { };
> @@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
>  	dumb_args.width = width;
>  	dumb_args.height = height;
>  	dumb_args.bpp = info->cpp[0] * 8;
> -	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
> +	if (fbdev)

Maybe if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?

> +		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);

And drm_mode_create_dumb_fbdev() could just be made a stub if
CONFIG_DRM_FBDEV_EMULATION isn't enabled.

I believe the only usage of the DRM client API currently is the fbdev
emulation layer anyways? But still makes sense I think to condtionally
compile that since drm_client.o is built in the drm.ko module and the
drm_fb_helper.o only included if fbdev emulation has been enabled.

> +	else
> +		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>  	if (ret)
>  		goto err_delete;
>  
> @@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
>   * @width: Framebuffer width
>   * @height: Framebuffer height
>   * @format: Buffer format
> + * @fbdev: True if the client provides an fbdev device, or false otherwise
>   *

An emulated fbdev device ?

Other than those small nit,

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
@ 2022-03-08 17:51     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 17:51 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Provide struct drm_driver.dumb_create_fbdev, a callback hook for
> fbdev dumb buffers. Wire up fbdev and client helpers to use the new
> interface, if present.
> 
> This acknowledges the fact that fbdev buffers are different. The most
> significant difference to regular GEM BOs is in mmap semantics. Fbdev
> userspace treats the pages as video memory, which makes it impossible
> to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
> the BO permanently or install an intermediate shadow buffer for mmap.
> 
> So far, fbdev memory came from dumb buffers and DRM drivers had no
> means of detecting this without reimplementing a good part of the fbdev
> code. With the new callback, drivers can perma-pin fbdev buffer objects
> if needed.
> 
> Several drivers also require damage handling, which fbdev implements
> with its deferred I/O helpers. The new callback allows a driver's memory
> manager to set up a suitable mmap.
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  drivers/gpu/drm/drm_client.c        | 14 +++++++----
>  drivers/gpu/drm/drm_crtc_internal.h |  3 +++
>  drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
>  drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
>  include/drm/drm_client.h            |  3 ++-
>  include/drm/drm_drv.h               | 17 ++++++++++++++
>  6 files changed, 84 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
> index af3b7395bf69..c964064651d5 100644
> --- a/drivers/gpu/drm/drm_client.c
> +++ b/drivers/gpu/drm/drm_client.c
> @@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
>  }
>  
>  static struct drm_client_buffer *
> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
> +			 bool fbdev)
>  {
>  	const struct drm_format_info *info = drm_format_info(format);
>  	struct drm_mode_create_dumb dumb_args = { };
> @@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
>  	dumb_args.width = width;
>  	dumb_args.height = height;
>  	dumb_args.bpp = info->cpp[0] * 8;
> -	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
> +	if (fbdev)

Maybe if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?

> +		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);

And drm_mode_create_dumb_fbdev() could just be made a stub if
CONFIG_DRM_FBDEV_EMULATION isn't enabled.

I believe the only usage of the DRM client API currently is the fbdev
emulation layer anyways? But still makes sense I think to condtionally
compile that since drm_client.o is built in the drm.ko module and the
drm_fb_helper.o only included if fbdev emulation has been enabled.

> +	else
> +		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>  	if (ret)
>  		goto err_delete;
>  
> @@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
>   * @width: Framebuffer width
>   * @height: Framebuffer height
>   * @format: Buffer format
> + * @fbdev: True if the client provides an fbdev device, or false otherwise
>   *

An emulated fbdev device ?

Other than those small nit,

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 7/9] drm/fb-helper: Provide fbdev deferred-I/O helpers
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 18:56     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 18:56 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Add drm_fb_helper_vm_page_mkwrite(), a helper to track pages written
> by fbdev userspace. DRM drivers should use this function to implement
> fbdev deferred I/O.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 7/9] drm/fb-helper: Provide fbdev deferred-I/O helpers
@ 2022-03-08 18:56     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 18:56 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Add drm_fb_helper_vm_page_mkwrite(), a helper to track pages written
> by fbdev userspace. DRM drivers should use this function to implement
> fbdev deferred I/O.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 19:29     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 19:29 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The
> created buffer object returned by this function implements deferred
> I/O for its mmap operation.
> 
> Add this feature to a number of drivers that use GEM SHMEM helpers
> as shadow planes over their regular video memory. The new macro
> DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM
> functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation
> on these drivers will now mmap the GEM SHMEM pages directly with
> deferred I/O without an intermediate shadow buffer.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

[snip]

> @@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
>  	.vm_ops = &drm_gem_shmem_vm_ops,
>  };
>  
> +static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
> +	.free = drm_gem_shmem_object_free,
> +	.print_info = drm_gem_shmem_object_print_info,
> +	.pin = drm_gem_shmem_object_pin,
> +	.unpin = drm_gem_shmem_object_unpin,
> +	.get_sg_table = drm_gem_shmem_object_get_sg_table,
> +	.vmap = drm_gem_shmem_object_vmap,
> +	.vunmap = drm_gem_shmem_object_vunmap,
> +	.mmap = drm_gem_shmem_object_mmap_fbdev,
> +	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,

The drm_gem_shmem_funcs_fbdev is the same than drm_gem_shmem_funcs but
.mmap and .vm_ops callbacks. Maybe adding a macro to declare these two
struct drm_gem_object_funcs could make easier to maintain it long term ?

> +};
> +
>  static struct drm_gem_shmem_object *
> -__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
> +__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
>  {
>  	struct drm_gem_shmem_object *shmem;
>  	struct drm_gem_object *obj;
> @@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
>  		obj = &shmem->base;
>  	}
>  
> -	if (!obj->funcs)
> -		obj->funcs = &drm_gem_shmem_funcs;
> +	if (!obj->funcs) {
> +		if (fbdev)

Same question than in patch #6, maybe

                if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?

[snip]

> + */
> +int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
> +				    struct drm_mode_create_dumb *args)
> +{
> +#if defined(CONFIG_DRM_FBDEV_EMULATION)
> +	return __drm_gem_shmem_dumb_create(file, dev, true, args);
> +#else
> +	return -ENOSYS;
> +#endif
> +}

I believe the correct errno code here should be -ENOTSUPP.

[snip]

> +	/* We don't use vmf->pgoff since that has the fake offset */
> +	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;

I see this (vmf->address - vma->vm_start) calculation in many places of
this series. Maybe we could add a macro to calculate the offset instead ?

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
@ 2022-03-08 19:29     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 19:29 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The
> created buffer object returned by this function implements deferred
> I/O for its mmap operation.
> 
> Add this feature to a number of drivers that use GEM SHMEM helpers
> as shadow planes over their regular video memory. The new macro
> DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM
> functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation
> on these drivers will now mmap the GEM SHMEM pages directly with
> deferred I/O without an intermediate shadow buffer.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---

[snip]

> @@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
>  	.vm_ops = &drm_gem_shmem_vm_ops,
>  };
>  
> +static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
> +	.free = drm_gem_shmem_object_free,
> +	.print_info = drm_gem_shmem_object_print_info,
> +	.pin = drm_gem_shmem_object_pin,
> +	.unpin = drm_gem_shmem_object_unpin,
> +	.get_sg_table = drm_gem_shmem_object_get_sg_table,
> +	.vmap = drm_gem_shmem_object_vmap,
> +	.vunmap = drm_gem_shmem_object_vunmap,
> +	.mmap = drm_gem_shmem_object_mmap_fbdev,
> +	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,

The drm_gem_shmem_funcs_fbdev is the same than drm_gem_shmem_funcs but
.mmap and .vm_ops callbacks. Maybe adding a macro to declare these two
struct drm_gem_object_funcs could make easier to maintain it long term ?

> +};
> +
>  static struct drm_gem_shmem_object *
> -__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
> +__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
>  {
>  	struct drm_gem_shmem_object *shmem;
>  	struct drm_gem_object *obj;
> @@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
>  		obj = &shmem->base;
>  	}
>  
> -	if (!obj->funcs)
> -		obj->funcs = &drm_gem_shmem_funcs;
> +	if (!obj->funcs) {
> +		if (fbdev)

Same question than in patch #6, maybe

                if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?

[snip]

> + */
> +int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
> +				    struct drm_mode_create_dumb *args)
> +{
> +#if defined(CONFIG_DRM_FBDEV_EMULATION)
> +	return __drm_gem_shmem_dumb_create(file, dev, true, args);
> +#else
> +	return -ENOSYS;
> +#endif
> +}

I believe the correct errno code here should be -ENOTSUPP.

[snip]

> +	/* We don't use vmf->pgoff since that has the fake offset */
> +	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;

I see this (vmf->address - vma->vm_start) calculation in many places of
this series. Maybe we could add a macro to calculate the offset instead ?

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
  2022-03-03 20:58   ` Thomas Zimmermann
@ 2022-03-08 19:37     ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 19:37 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Implement struct drm_driver.dumb_create_fbdev with the helpers
> provided by GEM SHMEM. Fbdev deferred I/O will now work without
> an intermediate shadow buffer for mmap.
> 
> As the virtio driver replaces several of the regular GEM SHMEM
> functions with its own implementation, some additional code is
> necessary make fbdev optimization work. Especially, the driver
> has to provide buffer-object functions for fbdev. Add the hook
> struct drm_driver.gem_create_object_fbdev, which is similar to
> struct drm_driver.gem_create_object and allows for the creation
> of dedicated fbdev buffer objects. Wire things up within GEM
> SHMEM.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  drivers/gpu/drm/drm_gem_shmem_helper.c  |  7 +++-
>  drivers/gpu/drm/virtio/virtgpu_drv.c    |  2 +
>  drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +
>  drivers/gpu/drm/virtio/virtgpu_object.c | 54 +++++++++++++++++++++++--
>  include/drm/drm_drv.h                   | 10 +++++
>  5 files changed, 71 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> index ab7cb7d896c3..225aa17895bd 100644
> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> @@ -71,7 +71,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool f
>  
>  	size = PAGE_ALIGN(size);
>  
> -	if (dev->driver->gem_create_object) {
> +	if (dev->driver->gem_create_object_fbdev && fbdev) {

Same comment here to check if fbdev emulation is enabled or maybe is not
worht it ? But I think this hints the compiler to optimize the if branch.

[snip]

> +}
> +#else
> +struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
> +						      size_t size)
> +{
> +	return ERR_PTR(-ENOSYS);
> +}

As mentioned, I believe this should be ERR_PTR(-ENOTSUPP) instead.

Feel free to ignore all this nits if you consider that are not applicable.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
@ 2022-03-08 19:37     ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-08 19:37 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/3/22 21:58, Thomas Zimmermann wrote:
> Implement struct drm_driver.dumb_create_fbdev with the helpers
> provided by GEM SHMEM. Fbdev deferred I/O will now work without
> an intermediate shadow buffer for mmap.
> 
> As the virtio driver replaces several of the regular GEM SHMEM
> functions with its own implementation, some additional code is
> necessary make fbdev optimization work. Especially, the driver
> has to provide buffer-object functions for fbdev. Add the hook
> struct drm_driver.gem_create_object_fbdev, which is similar to
> struct drm_driver.gem_create_object and allows for the creation
> of dedicated fbdev buffer objects. Wire things up within GEM
> SHMEM.
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> ---
>  drivers/gpu/drm/drm_gem_shmem_helper.c  |  7 +++-
>  drivers/gpu/drm/virtio/virtgpu_drv.c    |  2 +
>  drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +
>  drivers/gpu/drm/virtio/virtgpu_object.c | 54 +++++++++++++++++++++++--
>  include/drm/drm_drv.h                   | 10 +++++
>  5 files changed, 71 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
> index ab7cb7d896c3..225aa17895bd 100644
> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
> @@ -71,7 +71,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool f
>  
>  	size = PAGE_ALIGN(size);
>  
> -	if (dev->driver->gem_create_object) {
> +	if (dev->driver->gem_create_object_fbdev && fbdev) {

Same comment here to check if fbdev emulation is enabled or maybe is not
worht it ? But I think this hints the compiler to optimize the if branch.

[snip]

> +}
> +#else
> +struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
> +						      size_t size)
> +{
> +	return ERR_PTR(-ENOSYS);
> +}

As mentioned, I believe this should be ERR_PTR(-ENOTSUPP) instead.

Feel free to ignore all this nits if you consider that are not applicable.

Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
  2022-03-08 14:42     ` Javier Martinez Canillas
@ 2022-03-09  8:36       ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:36 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 4680 bytes --]

Hi

Am 08.03.22 um 15:42 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Store the per-page state for fbdev's deferred I/O in struct
>> fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
>> that have to be flushed out to video memory. Update all affected
>> drivers.
>>
>> Like with pages before, fbdev acquires a pageref when an mmaped page
>> of the framebuffer is being written to. It holds the pageref in a
>> list of all currently written pagerefs until it flushes the written
>> pages to video memory. Writeback occurs periodically. After writeback
>> fbdev releases all pagerefs and builds up a new dirty list until the
>> next writeback occurs.
>>
>> Using pagerefs has a number of benefits.
>>
>> For pages of the framebuffer, the deferred I/O code used struct
>> page.lru as an entry into the list of dirty pages. The lru field is
>> owned by the page cache, which makes deferred I/O incompatible with
>> some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
>> struct fb_deferred_io_pageref now provides an entry into a list of
>> dirty framebuffer pages, free'ing lru for use with the page cache.
>>
>> Drivers also assumed that struct page.index is the page offset into
>> the framebuffer. This is not true for DRM buffers, which are located
>> at various offset within a mapped area. struct fb_deferred_io_pageref
>> explicitly stores an offset into the framebuffer. struct page.index
>> is now only the page offset into the mapped area.
>>
>> These changes will allow DRM to use fbdev deferred I/O without an
>> intermediate shadow buffer.
>>
> 
> As mentioned, this is a very nice cleanup.
> 
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
> 
> [snip]
> 
>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
>> index 33f355907fbb..a35f695727c9 100644
>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
>> @@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
>>   	struct vmw_fb_par *par = info->par;
>>   	unsigned long start, end, min, max;
>>   	unsigned long flags;
>> -	struct page *page;
>> +	struct fb_deferred_io_pageref *pageref;
>>   	int y1, y2;
>>   
>>   	min = ULONG_MAX;
>>   	max = 0;
>> -	list_for_each_entry(page, pagelist, lru) {
>> +	list_for_each_entry(pageref, pagelist, list) {
>> +		struct page *page = pageref->page;
>>   		start = page->index << PAGE_SHIFT;
> 
> Do you think that may be worth it to also decouple the struct page.index and
> store the index as a struct fb_deferred_io_pageref.index field ?
> 
> It's unlikely that this would change but it feels as if the layers are more
> separated that way, since no driver will access struct page fields directly.
> 
> The mapping would be 1:1 anyways just like it's the case for the page offset.
> 
> In fact, that could allow to even avoid declaring a struct page *page here.

There are two related fields page->index and pageref->offset. The former 
is the offset into the mapped file, the later is the offset in the 
mapped video memory (or fbdev buffer).  It's the same value for fbdev 
drivers. But for DRM, it's different: because BOs are mapped at an 
offset in the DRM device file, page->index has this offset added to it 
as well.

The value of page->index is required by page_mkclean(), [1] which we 
call to reset the mappings during the writeback phase of the deferred 
I/O. pageref->offset is for conveniently getting the video-memory offset 
in fb helpers.

I thought about using pageref->offset in fbdev drivers as well. It would 
be more correct, but didn't want to add unnecessary churn. Especially 
since I cannot test most of the fbdev drivers. If you think it's worth 
it, I'd change the drivers as well.

Best regards
Thomas

[1] https://elixir.bootlin.com/linux/latest/source/include/linux/rmap.h#L304


> 
> [snip]
> 
>> @@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
>>   	spin_unlock(&par->dirty_lock);
>>   
>>   	/* Mark display lines as dirty */
>> -	list_for_each_entry(page, pagelist, lru) {
>> +	list_for_each_entry(pageref, pagelist, list) {
>> +		struct page *page = pageref->page;
> 
> Same here and the other drivers. You only need the page for the index AFAICT.
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
@ 2022-03-09  8:36       ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:36 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 4680 bytes --]

Hi

Am 08.03.22 um 15:42 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Store the per-page state for fbdev's deferred I/O in struct
>> fb_deferred_io_pageref. Maintain a list of pagerefs for the pages
>> that have to be flushed out to video memory. Update all affected
>> drivers.
>>
>> Like with pages before, fbdev acquires a pageref when an mmaped page
>> of the framebuffer is being written to. It holds the pageref in a
>> list of all currently written pagerefs until it flushes the written
>> pages to video memory. Writeback occurs periodically. After writeback
>> fbdev releases all pagerefs and builds up a new dirty list until the
>> next writeback occurs.
>>
>> Using pagerefs has a number of benefits.
>>
>> For pages of the framebuffer, the deferred I/O code used struct
>> page.lru as an entry into the list of dirty pages. The lru field is
>> owned by the page cache, which makes deferred I/O incompatible with
>> some memory pages (e.g., most notably DRM's GEM SHMEM allocator).
>> struct fb_deferred_io_pageref now provides an entry into a list of
>> dirty framebuffer pages, free'ing lru for use with the page cache.
>>
>> Drivers also assumed that struct page.index is the page offset into
>> the framebuffer. This is not true for DRM buffers, which are located
>> at various offset within a mapped area. struct fb_deferred_io_pageref
>> explicitly stores an offset into the framebuffer. struct page.index
>> is now only the page offset into the mapped area.
>>
>> These changes will allow DRM to use fbdev deferred I/O without an
>> intermediate shadow buffer.
>>
> 
> As mentioned, this is a very nice cleanup.
> 
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
> 
> [snip]
> 
>> diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
>> index 33f355907fbb..a35f695727c9 100644
>> --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
>> +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
>> @@ -322,12 +322,13 @@ static void vmw_deferred_io(struct fb_info *info,
>>   	struct vmw_fb_par *par = info->par;
>>   	unsigned long start, end, min, max;
>>   	unsigned long flags;
>> -	struct page *page;
>> +	struct fb_deferred_io_pageref *pageref;
>>   	int y1, y2;
>>   
>>   	min = ULONG_MAX;
>>   	max = 0;
>> -	list_for_each_entry(page, pagelist, lru) {
>> +	list_for_each_entry(pageref, pagelist, list) {
>> +		struct page *page = pageref->page;
>>   		start = page->index << PAGE_SHIFT;
> 
> Do you think that may be worth it to also decouple the struct page.index and
> store the index as a struct fb_deferred_io_pageref.index field ?
> 
> It's unlikely that this would change but it feels as if the layers are more
> separated that way, since no driver will access struct page fields directly.
> 
> The mapping would be 1:1 anyways just like it's the case for the page offset.
> 
> In fact, that could allow to even avoid declaring a struct page *page here.

There are two related fields page->index and pageref->offset. The former 
is the offset into the mapped file, the later is the offset in the 
mapped video memory (or fbdev buffer).  It's the same value for fbdev 
drivers. But for DRM, it's different: because BOs are mapped at an 
offset in the DRM device file, page->index has this offset added to it 
as well.

The value of page->index is required by page_mkclean(), [1] which we 
call to reset the mappings during the writeback phase of the deferred 
I/O. pageref->offset is for conveniently getting the video-memory offset 
in fb helpers.

I thought about using pageref->offset in fbdev drivers as well. It would 
be more correct, but didn't want to add unnecessary churn. Especially 
since I cannot test most of the fbdev drivers. If you think it's worth 
it, I'd change the drivers as well.

Best regards
Thomas

[1] https://elixir.bootlin.com/linux/latest/source/include/linux/rmap.h#L304


> 
> [snip]
> 
>> @@ -340,7 +340,8 @@ static void fbtft_deferred_io(struct fb_info *info, struct list_head *pagelist)
>>   	spin_unlock(&par->dirty_lock);
>>   
>>   	/* Mark display lines as dirty */
>> -	list_for_each_entry(page, pagelist, lru) {
>> +	list_for_each_entry(pageref, pagelist, list) {
>> +		struct page *page = pageref->page;
> 
> Same here and the other drivers. You only need the page for the index AFAICT.
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
  2022-03-08 17:51     ` Javier Martinez Canillas
@ 2022-03-09  8:42       ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:42 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 4100 bytes --]

Hi

Am 08.03.22 um 18:51 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Provide struct drm_driver.dumb_create_fbdev, a callback hook for
>> fbdev dumb buffers. Wire up fbdev and client helpers to use the new
>> interface, if present.
>>
>> This acknowledges the fact that fbdev buffers are different. The most
>> significant difference to regular GEM BOs is in mmap semantics. Fbdev
>> userspace treats the pages as video memory, which makes it impossible
>> to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
>> the BO permanently or install an intermediate shadow buffer for mmap.
>>
>> So far, fbdev memory came from dumb buffers and DRM drivers had no
>> means of detecting this without reimplementing a good part of the fbdev
>> code. With the new callback, drivers can perma-pin fbdev buffer objects
>> if needed.
>>
>> Several drivers also require damage handling, which fbdev implements
>> with its deferred I/O helpers. The new callback allows a driver's memory
>> manager to set up a suitable mmap.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
>>   drivers/gpu/drm/drm_client.c        | 14 +++++++----
>>   drivers/gpu/drm/drm_crtc_internal.h |  3 +++
>>   drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
>>   drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
>>   include/drm/drm_client.h            |  3 ++-
>>   include/drm/drm_drv.h               | 17 ++++++++++++++
>>   6 files changed, 84 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
>> index af3b7395bf69..c964064651d5 100644
>> --- a/drivers/gpu/drm/drm_client.c
>> +++ b/drivers/gpu/drm/drm_client.c
>> @@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
>>   }
>>   
>>   static struct drm_client_buffer *
>> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
>> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
>> +			 bool fbdev)
>>   {
>>   	const struct drm_format_info *info = drm_format_info(format);
>>   	struct drm_mode_create_dumb dumb_args = { };
>> @@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
>>   	dumb_args.width = width;
>>   	dumb_args.height = height;
>>   	dumb_args.bpp = info->cpp[0] * 8;
>> -	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>> +	if (fbdev)
> 
> Maybe if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?
> 
>> +		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
> 
> And drm_mode_create_dumb_fbdev() could just be made a stub if
> CONFIG_DRM_FBDEV_EMULATION isn't enabled.

Makes sense.

> 
> I believe the only usage of the DRM client API currently is the fbdev
> emulation layer anyways? But still makes sense I think to condtionally
> compile that since drm_client.o is built in the drm.ko module and the
> drm_fb_helper.o only included if fbdev emulation has been enabled.

Makes sense, I think.

Fbdev emulation is the only user of this code. And there won't be other 
helpers like that. Any other client would use the regular dumb-buffer 
functions.

> 
>> +	else
>> +		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>>   	if (ret)
>>   		goto err_delete;
>>   
>> @@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
>>    * @width: Framebuffer width
>>    * @height: Framebuffer height
>>    * @format: Buffer format
>> + * @fbdev: True if the client provides an fbdev device, or false otherwise
>>    *
> 
> An emulated fbdev device ?

Ok.

Best regards
Thomas

> 
> Other than those small nit,
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
@ 2022-03-09  8:42       ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:42 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 4100 bytes --]

Hi

Am 08.03.22 um 18:51 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Provide struct drm_driver.dumb_create_fbdev, a callback hook for
>> fbdev dumb buffers. Wire up fbdev and client helpers to use the new
>> interface, if present.
>>
>> This acknowledges the fact that fbdev buffers are different. The most
>> significant difference to regular GEM BOs is in mmap semantics. Fbdev
>> userspace treats the pages as video memory, which makes it impossible
>> to ever move the mmap'ed buffer. Hence, drivers ussually have to pin
>> the BO permanently or install an intermediate shadow buffer for mmap.
>>
>> So far, fbdev memory came from dumb buffers and DRM drivers had no
>> means of detecting this without reimplementing a good part of the fbdev
>> code. With the new callback, drivers can perma-pin fbdev buffer objects
>> if needed.
>>
>> Several drivers also require damage handling, which fbdev implements
>> with its deferred I/O helpers. The new callback allows a driver's memory
>> manager to set up a suitable mmap.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
>>   drivers/gpu/drm/drm_client.c        | 14 +++++++----
>>   drivers/gpu/drm/drm_crtc_internal.h |  3 +++
>>   drivers/gpu/drm/drm_dumb_buffers.c  | 36 +++++++++++++++++++++++++----
>>   drivers/gpu/drm/drm_fb_helper.c     | 26 +++++++++++++++++----
>>   include/drm/drm_client.h            |  3 ++-
>>   include/drm/drm_drv.h               | 17 ++++++++++++++
>>   6 files changed, 84 insertions(+), 15 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_client.c b/drivers/gpu/drm/drm_client.c
>> index af3b7395bf69..c964064651d5 100644
>> --- a/drivers/gpu/drm/drm_client.c
>> +++ b/drivers/gpu/drm/drm_client.c
>> @@ -247,7 +247,8 @@ static void drm_client_buffer_delete(struct drm_client_buffer *buffer)
>>   }
>>   
>>   static struct drm_client_buffer *
>> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
>> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
>> +			 bool fbdev)
>>   {
>>   	const struct drm_format_info *info = drm_format_info(format);
>>   	struct drm_mode_create_dumb dumb_args = { };
>> @@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
>>   	dumb_args.width = width;
>>   	dumb_args.height = height;
>>   	dumb_args.bpp = info->cpp[0] * 8;
>> -	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>> +	if (fbdev)
> 
> Maybe if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?
> 
>> +		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
> 
> And drm_mode_create_dumb_fbdev() could just be made a stub if
> CONFIG_DRM_FBDEV_EMULATION isn't enabled.

Makes sense.

> 
> I believe the only usage of the DRM client API currently is the fbdev
> emulation layer anyways? But still makes sense I think to condtionally
> compile that since drm_client.o is built in the drm.ko module and the
> drm_fb_helper.o only included if fbdev emulation has been enabled.

Makes sense, I think.

Fbdev emulation is the only user of this code. And there won't be other 
helpers like that. Any other client would use the regular dumb-buffer 
functions.

> 
>> +	else
>> +		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>>   	if (ret)
>>   		goto err_delete;
>>   
>> @@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
>>    * @width: Framebuffer width
>>    * @height: Framebuffer height
>>    * @format: Buffer format
>> + * @fbdev: True if the client provides an fbdev device, or false otherwise
>>    *
> 
> An emulated fbdev device ?

Ok.

Best regards
Thomas

> 
> Other than those small nit,
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
  2022-03-08 19:29     ` Javier Martinez Canillas
@ 2022-03-09  8:47       ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:47 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 3611 bytes --]

Hi

Am 08.03.22 um 20:29 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The
>> created buffer object returned by this function implements deferred
>> I/O for its mmap operation.
>>
>> Add this feature to a number of drivers that use GEM SHMEM helpers
>> as shadow planes over their regular video memory. The new macro
>> DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM
>> functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation
>> on these drivers will now mmap the GEM SHMEM pages directly with
>> deferred I/O without an intermediate shadow buffer.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
> 
> [snip]
> 
>> @@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
>>   	.vm_ops = &drm_gem_shmem_vm_ops,
>>   };
>>   
>> +static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
>> +	.free = drm_gem_shmem_object_free,
>> +	.print_info = drm_gem_shmem_object_print_info,
>> +	.pin = drm_gem_shmem_object_pin,
>> +	.unpin = drm_gem_shmem_object_unpin,
>> +	.get_sg_table = drm_gem_shmem_object_get_sg_table,
>> +	.vmap = drm_gem_shmem_object_vmap,
>> +	.vunmap = drm_gem_shmem_object_vunmap,
>> +	.mmap = drm_gem_shmem_object_mmap_fbdev,
>> +	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
> 
> The drm_gem_shmem_funcs_fbdev is the same than drm_gem_shmem_funcs but
> .mmap and .vm_ops callbacks. Maybe adding a macro to declare these two
> struct drm_gem_object_funcs could make easier to maintain it long term ?

I see you point, but I'm undecided about this. Such macros also add some 
amount of obfuscation. I'm not sure if it's worth it for an internal 
constant. And since the fbdev buffer is never exported, we could remove 
some of the callbacks. AFAICT we never call pin, unpin or get_sg_table.

Best regards
Thomas

> 
>> +};
>> +
>>   static struct drm_gem_shmem_object *
>> -__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
>> +__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
>>   {
>>   	struct drm_gem_shmem_object *shmem;
>>   	struct drm_gem_object *obj;
>> @@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
>>   		obj = &shmem->base;
>>   	}
>>   
>> -	if (!obj->funcs)
>> -		obj->funcs = &drm_gem_shmem_funcs;
>> +	if (!obj->funcs) {
>> +		if (fbdev)
> 
> Same question than in patch #6, maybe
> 
>                  if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?
> 
> [snip]
> 
>> + */
>> +int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
>> +				    struct drm_mode_create_dumb *args)
>> +{
>> +#if defined(CONFIG_DRM_FBDEV_EMULATION)
>> +	return __drm_gem_shmem_dumb_create(file, dev, true, args);
>> +#else
>> +	return -ENOSYS;
>> +#endif
>> +}
> 
> I believe the correct errno code here should be -ENOTSUPP.
> 
> [snip]
> 
>> +	/* We don't use vmf->pgoff since that has the fake offset */
>> +	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
> 
> I see this (vmf->address - vma->vm_start) calculation in many places of
> this series. Maybe we could add a macro to calculate the offset instead ?
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
@ 2022-03-09  8:47       ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:47 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 3611 bytes --]

Hi

Am 08.03.22 um 20:29 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Implement struct drm_driver.dumb_create_fbdev for GEM SHMEM. The
>> created buffer object returned by this function implements deferred
>> I/O for its mmap operation.
>>
>> Add this feature to a number of drivers that use GEM SHMEM helpers
>> as shadow planes over their regular video memory. The new macro
>> DRM_GEM_SHMEM_DRIVER_OPS_WITH_SHADOW_PLANES sets the regular GEM
>> functions and dumb_create_fbdev in struct drm_driver. Fbdev emulation
>> on these drivers will now mmap the GEM SHMEM pages directly with
>> deferred I/O without an intermediate shadow buffer.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
> 
> [snip]
> 
>> @@ -49,8 +50,20 @@ static const struct drm_gem_object_funcs drm_gem_shmem_funcs = {
>>   	.vm_ops = &drm_gem_shmem_vm_ops,
>>   };
>>   
>> +static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
>> +	.free = drm_gem_shmem_object_free,
>> +	.print_info = drm_gem_shmem_object_print_info,
>> +	.pin = drm_gem_shmem_object_pin,
>> +	.unpin = drm_gem_shmem_object_unpin,
>> +	.get_sg_table = drm_gem_shmem_object_get_sg_table,
>> +	.vmap = drm_gem_shmem_object_vmap,
>> +	.vunmap = drm_gem_shmem_object_vunmap,
>> +	.mmap = drm_gem_shmem_object_mmap_fbdev,
>> +	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
> 
> The drm_gem_shmem_funcs_fbdev is the same than drm_gem_shmem_funcs but
> .mmap and .vm_ops callbacks. Maybe adding a macro to declare these two
> struct drm_gem_object_funcs could make easier to maintain it long term ?

I see you point, but I'm undecided about this. Such macros also add some 
amount of obfuscation. I'm not sure if it's worth it for an internal 
constant. And since the fbdev buffer is never exported, we could remove 
some of the callbacks. AFAICT we never call pin, unpin or get_sg_table.

Best regards
Thomas

> 
>> +};
>> +
>>   static struct drm_gem_shmem_object *
>> -__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
>> +__drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool fbdev)
>>   {
>>   	struct drm_gem_shmem_object *shmem;
>>   	struct drm_gem_object *obj;
>> @@ -70,8 +83,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private)
>>   		obj = &shmem->base;
>>   	}
>>   
>> -	if (!obj->funcs)
>> -		obj->funcs = &drm_gem_shmem_funcs;
>> +	if (!obj->funcs) {
>> +		if (fbdev)
> 
> Same question than in patch #6, maybe
> 
>                  if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?
> 
> [snip]
> 
>> + */
>> +int drm_gem_shmem_dumb_create_fbdev(struct drm_file *file, struct drm_device *dev,
>> +				    struct drm_mode_create_dumb *args)
>> +{
>> +#if defined(CONFIG_DRM_FBDEV_EMULATION)
>> +	return __drm_gem_shmem_dumb_create(file, dev, true, args);
>> +#else
>> +	return -ENOSYS;
>> +#endif
>> +}
> 
> I believe the correct errno code here should be -ENOTSUPP.
> 
> [snip]
> 
>> +	/* We don't use vmf->pgoff since that has the fake offset */
>> +	page_offset = (vmf->address - vma->vm_start) >> PAGE_SHIFT;
> 
> I see this (vmf->address - vma->vm_start) calculation in many places of
> this series. Maybe we could add a macro to calculate the offset instead ?
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
  2022-03-08 19:37     ` Javier Martinez Canillas
@ 2022-03-09  8:52       ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:52 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 2966 bytes --]

Hi

Am 08.03.22 um 20:37 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Implement struct drm_driver.dumb_create_fbdev with the helpers
>> provided by GEM SHMEM. Fbdev deferred I/O will now work without
>> an intermediate shadow buffer for mmap.
>>
>> As the virtio driver replaces several of the regular GEM SHMEM
>> functions with its own implementation, some additional code is
>> necessary make fbdev optimization work. Especially, the driver
>> has to provide buffer-object functions for fbdev. Add the hook
>> struct drm_driver.gem_create_object_fbdev, which is similar to
>> struct drm_driver.gem_create_object and allows for the creation
>> of dedicated fbdev buffer objects. Wire things up within GEM
>> SHMEM.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
>>   drivers/gpu/drm/drm_gem_shmem_helper.c  |  7 +++-
>>   drivers/gpu/drm/virtio/virtgpu_drv.c    |  2 +
>>   drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +
>>   drivers/gpu/drm/virtio/virtgpu_object.c | 54 +++++++++++++++++++++++--
>>   include/drm/drm_drv.h                   | 10 +++++
>>   5 files changed, 71 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> index ab7cb7d896c3..225aa17895bd 100644
>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> @@ -71,7 +71,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool f
>>   
>>   	size = PAGE_ALIGN(size);
>>   
>> -	if (dev->driver->gem_create_object) {
>> +	if (dev->driver->gem_create_object_fbdev && fbdev) {
> 
> Same comment here to check if fbdev emulation is enabled or maybe is not
> worht it ? But I think this hints the compiler to optimize the if branch.
> 
> [snip]
> 
>> +}
>> +#else
>> +struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
>> +						      size_t size)
>> +{
>> +	return ERR_PTR(-ENOSYS);
>> +}
> 
> As mentioned, I believe this should be ERR_PTR(-ENOTSUPP) instead.

I've been wondering about this as well. I finally went with the rules at 
[1].  All the variants of ENOTOP/ENOTSUPP seem to be for specific use 
cases, such as a certain feature is not implemented be a specific 
interface (e.g., sockets for EOPNOTSUPP).  ENOSYS is the only general 
error that indicates that an entire interface is missing. Even though 
checkpatch.pl warns that it's only for system calls.

Best regards
Thomas

[1] https://www.cs.helsinki.fi/linux/linux-kernel/2002-30/1135.html

> 
> Feel free to ignore all this nits if you consider that are not applicable.
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
@ 2022-03-09  8:52       ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-09  8:52 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2966 bytes --]

Hi

Am 08.03.22 um 20:37 schrieb Javier Martinez Canillas:
> On 3/3/22 21:58, Thomas Zimmermann wrote:
>> Implement struct drm_driver.dumb_create_fbdev with the helpers
>> provided by GEM SHMEM. Fbdev deferred I/O will now work without
>> an intermediate shadow buffer for mmap.
>>
>> As the virtio driver replaces several of the regular GEM SHMEM
>> functions with its own implementation, some additional code is
>> necessary make fbdev optimization work. Especially, the driver
>> has to provide buffer-object functions for fbdev. Add the hook
>> struct drm_driver.gem_create_object_fbdev, which is similar to
>> struct drm_driver.gem_create_object and allows for the creation
>> of dedicated fbdev buffer objects. Wire things up within GEM
>> SHMEM.
>>
>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> ---
>>   drivers/gpu/drm/drm_gem_shmem_helper.c  |  7 +++-
>>   drivers/gpu/drm/virtio/virtgpu_drv.c    |  2 +
>>   drivers/gpu/drm/virtio/virtgpu_drv.h    |  2 +
>>   drivers/gpu/drm/virtio/virtgpu_object.c | 54 +++++++++++++++++++++++--
>>   include/drm/drm_drv.h                   | 10 +++++
>>   5 files changed, 71 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/drm_gem_shmem_helper.c b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> index ab7cb7d896c3..225aa17895bd 100644
>> --- a/drivers/gpu/drm/drm_gem_shmem_helper.c
>> +++ b/drivers/gpu/drm/drm_gem_shmem_helper.c
>> @@ -71,7 +71,12 @@ __drm_gem_shmem_create(struct drm_device *dev, size_t size, bool private, bool f
>>   
>>   	size = PAGE_ALIGN(size);
>>   
>> -	if (dev->driver->gem_create_object) {
>> +	if (dev->driver->gem_create_object_fbdev && fbdev) {
> 
> Same comment here to check if fbdev emulation is enabled or maybe is not
> worht it ? But I think this hints the compiler to optimize the if branch.
> 
> [snip]
> 
>> +}
>> +#else
>> +struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
>> +						      size_t size)
>> +{
>> +	return ERR_PTR(-ENOSYS);
>> +}
> 
> As mentioned, I believe this should be ERR_PTR(-ENOTSUPP) instead.

I've been wondering about this as well. I finally went with the rules at 
[1].  All the variants of ENOTOP/ENOTSUPP seem to be for specific use 
cases, such as a certain feature is not implemented be a specific 
interface (e.g., sockets for EOPNOTSUPP).  ENOSYS is the only general 
error that indicates that an entire interface is missing. Even though 
checkpatch.pl warns that it's only for system calls.

Best regards
Thomas

[1] https://www.cs.helsinki.fi/linux/linux-kernel/2002-30/1135.html

> 
> Feel free to ignore all this nits if you consider that are not applicable.
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
  2022-03-09  8:36       ` Thomas Zimmermann
@ 2022-03-09 11:21         ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-09 11:21 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

Hello Thomas,

On 3/9/22 09:36, Thomas Zimmermann wrote:

[snip]

> 
> I thought about using pageref->offset in fbdev drivers as well. It would 
> be more correct, but didn't want to add unnecessary churn. Especially 
> since I cannot test most of the fbdev drivers. If you think it's worth 
> it, I'd change the drivers as well.
>

Thanks for the explanation. No, I don't think is worth it or at least as
as part of this series.
 
> Best regards
> Thomas
> 

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct
@ 2022-03-09 11:21         ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-09 11:21 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

Hello Thomas,

On 3/9/22 09:36, Thomas Zimmermann wrote:

[snip]

> 
> I thought about using pageref->offset in fbdev drivers as well. It would 
> be more correct, but didn't want to add unnecessary churn. Especially 
> since I cannot test most of the fbdev drivers. If you think it's worth 
> it, I'd change the drivers as well.
>

Thanks for the explanation. No, I don't think is worth it or at least as
as part of this series.
 
> Best regards
> Thomas
> 

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
  2022-03-09  8:47       ` Thomas Zimmermann
@ 2022-03-09 11:25         ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-09 11:25 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/9/22 09:47, Thomas Zimmermann wrote:

[snip]

>>>   
>>> +static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
>>> +	.free = drm_gem_shmem_object_free,
>>> +	.print_info = drm_gem_shmem_object_print_info,
>>> +	.pin = drm_gem_shmem_object_pin,
>>> +	.unpin = drm_gem_shmem_object_unpin,
>>> +	.get_sg_table = drm_gem_shmem_object_get_sg_table,
>>> +	.vmap = drm_gem_shmem_object_vmap,
>>> +	.vunmap = drm_gem_shmem_object_vunmap,
>>> +	.mmap = drm_gem_shmem_object_mmap_fbdev,
>>> +	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
>>
>> The drm_gem_shmem_funcs_fbdev is the same than drm_gem_shmem_funcs but
>> .mmap and .vm_ops callbacks. Maybe adding a macro to declare these two
>> struct drm_gem_object_funcs could make easier to maintain it long term ?
> 
> I see you point, but I'm undecided about this. Such macros also add some 
> amount of obfuscation. I'm not sure if it's worth it for an internal 
> constant. And since the fbdev buffer is never exported, we could remove 
> some of the callbacks. AFAICT we never call pin, unpin or get_sg_table.
>

Yeah, that's true too. It was just a suggestion, I'm OK with patch as is.
 
> Best regards
> Thomas
> 
>>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers
@ 2022-03-09 11:25         ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-09 11:25 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/9/22 09:47, Thomas Zimmermann wrote:

[snip]

>>>   
>>> +static const struct drm_gem_object_funcs drm_gem_shmem_funcs_fbdev = {
>>> +	.free = drm_gem_shmem_object_free,
>>> +	.print_info = drm_gem_shmem_object_print_info,
>>> +	.pin = drm_gem_shmem_object_pin,
>>> +	.unpin = drm_gem_shmem_object_unpin,
>>> +	.get_sg_table = drm_gem_shmem_object_get_sg_table,
>>> +	.vmap = drm_gem_shmem_object_vmap,
>>> +	.vunmap = drm_gem_shmem_object_vunmap,
>>> +	.mmap = drm_gem_shmem_object_mmap_fbdev,
>>> +	.vm_ops = &drm_gem_shmem_vm_ops_fbdev,
>>
>> The drm_gem_shmem_funcs_fbdev is the same than drm_gem_shmem_funcs but
>> .mmap and .vm_ops callbacks. Maybe adding a macro to declare these two
>> struct drm_gem_object_funcs could make easier to maintain it long term ?
> 
> I see you point, but I'm undecided about this. Such macros also add some 
> amount of obfuscation. I'm not sure if it's worth it for an internal 
> constant. And since the fbdev buffer is never exported, we could remove 
> some of the callbacks. AFAICT we never call pin, unpin or get_sg_table.
>

Yeah, that's true too. It was just a suggestion, I'm OK with patch as is.
 
> Best regards
> Thomas
> 
>>

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
  2022-03-09  8:52       ` Thomas Zimmermann
@ 2022-03-09 11:29         ` Javier Martinez Canillas
  -1 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-09 11:29 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev

On 3/9/22 09:52, Thomas Zimmermann wrote:

[snip]

>>> +struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
>>> +						      size_t size)
>>> +{
>>> +	return ERR_PTR(-ENOSYS);
>>> +}
>>
>> As mentioned, I believe this should be ERR_PTR(-ENOTSUPP) instead.
> 
> I've been wondering about this as well. I finally went with the rules at 
> [1].  All the variants of ENOTOP/ENOTSUPP seem to be for specific use 
> cases, such as a certain feature is not implemented be a specific 
> interface (e.g., sockets for EOPNOTSUPP).  ENOSYS is the only general 
> error that indicates that an entire interface is missing. Even though 
> checkpatch.pl warns that it's only for system calls.
> 
> Best regards
> Thomas
> 
> [1] https://www.cs.helsinki.fi/linux/linux-kernel/2002-30/1135.html
>

Thanks for the link. It would be good to have an authoritative guideline
about this in the kernel documentation (and make checkpatch.pl aware).

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers
@ 2022-03-09 11:29         ` Javier Martinez Canillas
  0 siblings, 0 replies; 62+ messages in thread
From: Javier Martinez Canillas @ 2022-03-09 11:29 UTC (permalink / raw)
  To: Thomas Zimmermann, daniel, airlied, mripard, maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel

On 3/9/22 09:52, Thomas Zimmermann wrote:

[snip]

>>> +struct drm_gem_object *virtio_gpu_create_object_fbdev(struct drm_device *dev,
>>> +						      size_t size)
>>> +{
>>> +	return ERR_PTR(-ENOSYS);
>>> +}
>>
>> As mentioned, I believe this should be ERR_PTR(-ENOTSUPP) instead.
> 
> I've been wondering about this as well. I finally went with the rules at 
> [1].  All the variants of ENOTOP/ENOTSUPP seem to be for specific use 
> cases, such as a certain feature is not implemented be a specific 
> interface (e.g., sockets for EOPNOTSUPP).  ENOSYS is the only general 
> error that indicates that an entire interface is missing. Even though 
> checkpatch.pl warns that it's only for system calls.
> 
> Best regards
> Thomas
> 
> [1] https://www.cs.helsinki.fi/linux/linux-kernel/2002-30/1135.html
>

Thanks for the link. It would be good to have an authoritative guideline
about this in the kernel documentation (and make checkpatch.pl aware).

-- 
Best regards,

Javier Martinez Canillas
Linux Engineering
Red Hat


^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
  2022-03-08 17:51     ` Javier Martinez Canillas
@ 2022-03-15 19:11       ` Thomas Zimmermann
  -1 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-15 19:11 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: dri-devel, linux-fbdev


[-- Attachment #1.1: Type: text/plain, Size: 2618 bytes --]

Hi Javier

Am 08.03.22 um 18:51 schrieb Javier Martinez Canillas:
[...]
>>   
>>   static struct drm_client_buffer *
>> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
>> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
>> +			 bool fbdev)
>>   {
>>   	const struct drm_format_info *info = drm_format_info(format);
>>   	struct drm_mode_create_dumb dumb_args = { };
>> @@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
>>   	dumb_args.width = width;
>>   	dumb_args.height = height;
>>   	dumb_args.bpp = info->cpp[0] * 8;
>> -	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>> +	if (fbdev)
> 
> Maybe if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?
> 
>> +		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
> 
> And drm_mode_create_dumb_fbdev() could just be made a stub if
> CONFIG_DRM_FBDEV_EMULATION isn't enabled.

While going through these commits again, I decided to change the logic 
to use a function pointer for the allocation. The fbdev emulation will 
provide an implementation that allocates the buffer object. The logic 
for picking the correct create-dumb function will be placed there.

Although it probably won't ever be needed, other clients could allocate 
buffers with other constraints, or even support acceleration instead of 
dumb buffers.

You may want to take another look at this patch in the patchset's next 
iteration.

Best regards
Thomas

> 
> I believe the only usage of the DRM client API currently is the fbdev
> emulation layer anyways? But still makes sense I think to condtionally
> compile that since drm_client.o is built in the drm.ko module and the
> drm_fb_helper.o only included if fbdev emulation has been enabled.
> 
>> +	else
>> +		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>>   	if (ret)
>>   		goto err_delete;
>>   
>> @@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
>>    * @width: Framebuffer width
>>    * @height: Framebuffer height
>>    * @format: Buffer format
>> + * @fbdev: True if the client provides an fbdev device, or false otherwise
>>    *
> 
> An emulated fbdev device ?
> 
> Other than those small nit,
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

* Re: [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers
@ 2022-03-15 19:11       ` Thomas Zimmermann
  0 siblings, 0 replies; 62+ messages in thread
From: Thomas Zimmermann @ 2022-03-15 19:11 UTC (permalink / raw)
  To: Javier Martinez Canillas, daniel, airlied, mripard,
	maarten.lankhorst, deller
  Cc: linux-fbdev, dri-devel


[-- Attachment #1.1: Type: text/plain, Size: 2618 bytes --]

Hi Javier

Am 08.03.22 um 18:51 schrieb Javier Martinez Canillas:
[...]
>>   
>>   static struct drm_client_buffer *
>> -drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format)
>> +drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format,
>> +			 bool fbdev)
>>   {
>>   	const struct drm_format_info *info = drm_format_info(format);
>>   	struct drm_mode_create_dumb dumb_args = { };
>> @@ -265,7 +266,10 @@ drm_client_buffer_create(struct drm_client_dev *client, u32 width, u32 height, u
>>   	dumb_args.width = width;
>>   	dumb_args.height = height;
>>   	dumb_args.bpp = info->cpp[0] * 8;
>> -	ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>> +	if (fbdev)
> 
> Maybe if (defined(CONFIG_DRM_FBDEV_EMULATION) && fbdev) ?
> 
>> +		ret = drm_mode_create_dumb_fbdev(dev, &dumb_args, client->file);
> 
> And drm_mode_create_dumb_fbdev() could just be made a stub if
> CONFIG_DRM_FBDEV_EMULATION isn't enabled.

While going through these commits again, I decided to change the logic 
to use a function pointer for the allocation. The fbdev emulation will 
provide an implementation that allocates the buffer object. The logic 
for picking the correct create-dumb function will be placed there.

Although it probably won't ever be needed, other clients could allocate 
buffers with other constraints, or even support acceleration instead of 
dumb buffers.

You may want to take another look at this patch in the patchset's next 
iteration.

Best regards
Thomas

> 
> I believe the only usage of the DRM client API currently is the fbdev
> emulation layer anyways? But still makes sense I think to condtionally
> compile that since drm_client.o is built in the drm.ko module and the
> drm_fb_helper.o only included if fbdev emulation has been enabled.
> 
>> +	else
>> +		ret = drm_mode_create_dumb(dev, &dumb_args, client->file);
>>   	if (ret)
>>   		goto err_delete;
>>   
>> @@ -402,6 +406,7 @@ static int drm_client_buffer_addfb(struct drm_client_buffer *buffer,
>>    * @width: Framebuffer width
>>    * @height: Framebuffer height
>>    * @format: Buffer format
>> + * @fbdev: True if the client provides an fbdev device, or false otherwise
>>    *
> 
> An emulated fbdev device ?
> 
> Other than those small nit,
> 
> Reviewed-by: Javier Martinez Canillas <javierm@redhat.com>
> 

-- 
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Maxfeldstr. 5, 90409 Nürnberg, Germany
(HRB 36809, AG Nürnberg)
Geschäftsführer: Ivo Totev

[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 840 bytes --]

^ permalink raw reply	[flat|nested] 62+ messages in thread

end of thread, other threads:[~2022-03-15 19:11 UTC | newest]

Thread overview: 62+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-03 20:58 [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB Thomas Zimmermann
2022-03-03 20:58 ` Thomas Zimmermann
2022-03-03 20:58 ` [PATCH 1/9] drm/simpledrm: Use fbdev defaults for shadow buffering Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08  9:31   ` Javier Martinez Canillas
2022-03-08  9:31     ` Javier Martinez Canillas
2022-03-08  9:56     ` Thomas Zimmermann
2022-03-08  9:56       ` Thomas Zimmermann
2022-03-08  9:58       ` Javier Martinez Canillas
2022-03-08  9:58         ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 2/9] fbdev: Put mmap for deferred I/O into drivers Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 14:03   ` Javier Martinez Canillas
2022-03-08 14:03     ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 3/9] fbdev: Track deferred-I/O pages in pageref struct Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 14:42   ` Javier Martinez Canillas
2022-03-08 14:42     ` Javier Martinez Canillas
2022-03-09  8:36     ` Thomas Zimmermann
2022-03-09  8:36       ` Thomas Zimmermann
2022-03-09 11:21       ` Javier Martinez Canillas
2022-03-09 11:21         ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 4/9] fbdev: Export helper for implementing page_mkwrite Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 17:21   ` Javier Martinez Canillas
2022-03-08 17:21     ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 5/9] drm/fb-helper: Separate deferred I/O from shadow buffers Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 17:24   ` Javier Martinez Canillas
2022-03-08 17:24     ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 6/9] drm/fb-helper: Provide callback to create fbdev dumb buffers Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 17:51   ` Javier Martinez Canillas
2022-03-08 17:51     ` Javier Martinez Canillas
2022-03-09  8:42     ` Thomas Zimmermann
2022-03-09  8:42       ` Thomas Zimmermann
2022-03-15 19:11     ` Thomas Zimmermann
2022-03-15 19:11       ` Thomas Zimmermann
2022-03-03 20:58 ` [PATCH 7/9] drm/fb-helper: Provide fbdev deferred-I/O helpers Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 18:56   ` Javier Martinez Canillas
2022-03-08 18:56     ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 8/9] drm/gem-shmem: Implement fbdev dumb buffer and mmap helpers Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 19:29   ` Javier Martinez Canillas
2022-03-08 19:29     ` Javier Martinez Canillas
2022-03-09  8:47     ` Thomas Zimmermann
2022-03-09  8:47       ` Thomas Zimmermann
2022-03-09 11:25       ` Javier Martinez Canillas
2022-03-09 11:25         ` Javier Martinez Canillas
2022-03-03 20:58 ` [PATCH 9/9] drm/virtio: Implement dumb_create_fbdev with GEM SHMEM helpers Thomas Zimmermann
2022-03-03 20:58   ` Thomas Zimmermann
2022-03-08 19:37   ` Javier Martinez Canillas
2022-03-08 19:37     ` Javier Martinez Canillas
2022-03-09  8:52     ` Thomas Zimmermann
2022-03-09  8:52       ` Thomas Zimmermann
2022-03-09 11:29       ` Javier Martinez Canillas
2022-03-09 11:29         ` Javier Martinez Canillas
2022-03-08  9:13 ` [PATCH 0/9] drm: Support GEM SHMEM fbdev without shadow FB Javier Martinez Canillas
2022-03-08  9:13   ` Javier Martinez Canillas
2022-03-08  9:44   ` Thomas Zimmermann
2022-03-08  9:44     ` Thomas Zimmermann

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.