* [PATCH RFC] drm/sysfb: Copy damage outside the DIRTYFB ioctl
@ 2026-11-11 1:37 Joseph Telaak
2026-11-11 8:48 ` Thomas Zimmermann
0 siblings, 1 reply; 3+ messages in thread
From: Joseph Telaak @ 2026-11-11 1:37 UTC (permalink / raw)
To: Thomas Zimmermann, Javier Martinez Canillas
Cc: Maarten Lankhorst, Maxime Ripard, David Airlie, Simona Vetter,
dri-devel, linux-kernel, Joseph Telaak
The sysfb drivers implement DIRTYFB with drm_atomic_helper_dirtyfb(),
which runs a blocking commit. The commit waits for the framebuffer's
fences, so the ioctl doesn't return until the GPU has finished
rendering into the framebuffer.
Xorg's modesetting driver calls DIRTYFB from its main thread after
every frame. When a separate GPU renders for a sysfb display, X then
blocks for as long as the GPU takes to render the client's frame. With
a game running, X took a median of 1.85 s to answer a pointer query.
Queue the damage instead and copy it from a worker. The worker still
waits for the fences, so nothing unfinished reaches the screen, and
damage that arrives while a copy is pending is merged into it.
With this change, X answers in a median of 23 ms and the game's frame
rate is unchanged.
Signed-off-by: Joseph Telaak <joseph@telaak.dev>
---
This is an RFC because I'm not sure DIRTYFB may return before the copy
is done. The fbdev emulation already defers its damage to a worker,
but userspace might rely on DIRTYFB being synchronous. If the approach
is fine, would you rather have it as a nonblocking mode of
drm_atomic_helper_dirtyfb() for all drivers instead of in sysfb?
I hit this on an Intel Atom CE5300 board, where simpledrm drives the
display and the SoC's PowerVR SGX545 GPU renders with a driver I'm
writing (not upstream yet). I haven't reproduced it with an upstream
GPU driver, but nothing in the stall is specific to the GPU.
---
drivers/gpu/drm/sysfb/corebootdrm.c | 3 +
drivers/gpu/drm/sysfb/drm_sysfb_helper.h | 15 ++-
drivers/gpu/drm/sysfb/drm_sysfb_modeset.c | 154 ++++++++++++++++++++++++++++++
drivers/gpu/drm/sysfb/efidrm.c | 3 +
drivers/gpu/drm/sysfb/ofdrm.c | 3 +
drivers/gpu/drm/sysfb/simpledrm.c | 3 +
drivers/gpu/drm/sysfb/vesadrm.c | 3 +
7 files changed, 183 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/sysfb/corebootdrm.c b/drivers/gpu/drm/sysfb/corebootdrm.c
index 853da9a..4ca359b 100644
--- a/drivers/gpu/drm/sysfb/corebootdrm.c
+++ b/drivers/gpu/drm/sysfb/corebootdrm.c
@@ -206,6 +206,9 @@ static int corebootdrm_mode_config_init(struct corebootdrm_device *cdev,
int ret;
ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ret;
+ ret = drm_sysfb_damage_init(sysfb);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
index 6dab27b..59c4064 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
@@ -5,6 +5,8 @@
#include <linux/container_of.h>
#include <linux/iosys-map.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
#include <video/pixel_format.h>
@@ -81,6 +83,12 @@ struct drm_sysfb_device {
/* hardware-framebuffer kernel address */
struct iosys_map fb_addr;
+
+ /* damage from DIRTYFB, copied to the hardware framebuffer by damage_work */
+ spinlock_t damage_lock;
+ struct drm_framebuffer *damage_fb;
+ struct drm_rect damage;
+ struct work_struct damage_work;
};
static inline struct drm_sysfb_device *to_drm_sysfb_device(struct drm_device *dev)
@@ -203,8 +211,13 @@ int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector);
* Mode config
*/
+int drm_sysfb_damage_init(struct drm_sysfb_device *sysfb);
+struct drm_framebuffer *drm_sysfb_fb_create(struct drm_device *dev, struct drm_file *file,
+ const struct drm_format_info *info,
+ const struct drm_mode_fb_cmd2 *mode_cmd);
+
#define DRM_SYSFB_MODE_CONFIG_FUNCS \
- .fb_create = drm_gem_fb_create_with_dirty, \
+ .fb_create = drm_sysfb_fb_create, \
.atomic_check = drm_atomic_helper_check, \
.atomic_commit = drm_atomic_helper_commit
diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
index 13df092..e77f0c0 100644
--- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
+++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
@@ -12,6 +12,7 @@
#include <drm/drm_fourcc.h>
#include <drm/drm_framebuffer.h>
#include <drm/drm_gem_framebuffer_helper.h>
+#include <drm/drm_managed.h>
#include <drm/drm_panic.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
@@ -612,3 +613,156 @@ int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
}
EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
+
+/*
+ * Damage
+ *
+ * DIRTYFB returns at once: the damage is merged into one pending rectangle,
+ * and a worker hands it to drm_atomic_helper_dirtyfb(). That commit waits for
+ * the framebuffer's fences, the GPU work that last rendered into it, before
+ * the CPU copies it to the hardware framebuffer. Done in the ioctl, the wait
+ * blocks the compositor for as long as that rendering takes. Damage that
+ * arrives while the worker runs is merged and copied by the next run, so none
+ * is dropped.
+ */
+
+static void drm_sysfb_damage_work(struct work_struct *work)
+{
+ struct drm_sysfb_device *sysfb = container_of(work, struct drm_sysfb_device,
+ damage_work);
+ struct drm_framebuffer *fb;
+ struct drm_clip_rect clip;
+ int idx, ret;
+
+ spin_lock(&sysfb->damage_lock);
+ fb = sysfb->damage_fb;
+ sysfb->damage_fb = NULL;
+ clip.x1 = sysfb->damage.x1;
+ clip.y1 = sysfb->damage.y1;
+ clip.x2 = sysfb->damage.x2;
+ clip.y2 = sysfb->damage.y2;
+ spin_unlock(&sysfb->damage_lock);
+ if (!fb)
+ return;
+
+ if (drm_dev_enter(&sysfb->dev, &idx)) {
+ ret = drm_atomic_helper_dirtyfb(fb, NULL, 0, 0, &clip, 1);
+ if (ret)
+ drm_err_ratelimited(&sysfb->dev, "damage update failed: %d\n", ret);
+ drm_dev_exit(idx);
+ }
+ drm_framebuffer_put(fb);
+}
+
+static void drm_sysfb_damage_fini(struct drm_device *dev, void *arg)
+{
+ struct drm_sysfb_device *sysfb = arg;
+
+ cancel_work_sync(&sysfb->damage_work);
+ if (sysfb->damage_fb)
+ drm_framebuffer_put(sysfb->damage_fb);
+ sysfb->damage_fb = NULL;
+}
+
+/**
+ * drm_sysfb_damage_init - Set up deferred damage for a sysfb device
+ * @sysfb: the device
+ *
+ * Call after drmm_mode_config_init(). Framebuffers from drm_sysfb_fb_create()
+ * then copy their damage outside the DIRTYFB ioctl.
+ *
+ * Returns: 0 on success, or a negative errno.
+ */
+int drm_sysfb_damage_init(struct drm_sysfb_device *sysfb)
+{
+ spin_lock_init(&sysfb->damage_lock);
+ INIT_WORK(&sysfb->damage_work, drm_sysfb_damage_work);
+
+ return drmm_add_action_or_reset(&sysfb->dev, drm_sysfb_damage_fini, sysfb);
+}
+EXPORT_SYMBOL(drm_sysfb_damage_init);
+
+static void drm_sysfb_damage_bounds(struct drm_framebuffer *fb, unsigned int flags,
+ const struct drm_clip_rect *clips, unsigned int num_clips,
+ struct drm_rect *r)
+{
+ unsigned int inc = 1;
+
+ if (!clips || !num_clips) {
+ drm_rect_init(r, 0, 0, fb->width, fb->height);
+ return;
+ }
+ if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
+ inc = 2;
+ num_clips /= 2;
+ }
+ drm_rect_init(r, clips->x1, clips->y1, clips->x2 - clips->x1, clips->y2 - clips->y1);
+ for (unsigned int i = 1; i < num_clips; i++) {
+ clips += inc;
+ r->x1 = min_t(int, r->x1, clips->x1);
+ r->y1 = min_t(int, r->y1, clips->y1);
+ r->x2 = max_t(int, r->x2, clips->x2);
+ r->y2 = max_t(int, r->y2, clips->y2);
+ }
+}
+
+static int drm_sysfb_fb_dirty(struct drm_framebuffer *fb, struct drm_file *file,
+ unsigned int flags, unsigned int color,
+ struct drm_clip_rect *clips, unsigned int num_clips)
+{
+ struct drm_sysfb_device *sysfb = to_drm_sysfb_device(fb->dev);
+ struct drm_rect r;
+ bool queue = false;
+
+ drm_sysfb_damage_bounds(fb, flags, clips, num_clips, &r);
+
+ spin_lock(&sysfb->damage_lock);
+ /* Pending damage of another framebuffer is copied first. */
+ while (sysfb->damage_fb && sysfb->damage_fb != fb) {
+ spin_unlock(&sysfb->damage_lock);
+ flush_work(&sysfb->damage_work);
+ spin_lock(&sysfb->damage_lock);
+ }
+ if (!sysfb->damage_fb) {
+ drm_framebuffer_get(fb);
+ sysfb->damage_fb = fb;
+ sysfb->damage = r;
+ queue = true;
+ } else {
+ sysfb->damage.x1 = min(sysfb->damage.x1, r.x1);
+ sysfb->damage.y1 = min(sysfb->damage.y1, r.y1);
+ sysfb->damage.x2 = max(sysfb->damage.x2, r.x2);
+ sysfb->damage.y2 = max(sysfb->damage.y2, r.y2);
+ }
+ spin_unlock(&sysfb->damage_lock);
+
+ if (queue)
+ queue_work(system_dfl_wq, &sysfb->damage_work);
+ return 0;
+}
+
+static const struct drm_framebuffer_funcs drm_sysfb_fb_funcs = {
+ .destroy = drm_gem_fb_destroy,
+ .create_handle = drm_gem_fb_create_handle,
+ .dirty = drm_sysfb_fb_dirty,
+};
+
+/**
+ * drm_sysfb_fb_create - &drm_mode_config_funcs.fb_create for sysfb devices
+ * @dev: the device
+ * @file: the DRM file of the caller
+ * @info: the pixel format
+ * @mode_cmd: the framebuffer from userspace
+ *
+ * Like drm_gem_fb_create_with_dirty(), but the framebuffer's damage is copied
+ * outside the DIRTYFB ioctl (drm_sysfb_damage_init()).
+ *
+ * Returns: the framebuffer, or an ERR_PTR() on failure.
+ */
+struct drm_framebuffer *drm_sysfb_fb_create(struct drm_device *dev, struct drm_file *file,
+ const struct drm_format_info *info,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ return drm_gem_fb_create_with_funcs(dev, file, info, mode_cmd, &drm_sysfb_fb_funcs);
+}
+EXPORT_SYMBOL(drm_sysfb_fb_create);
diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c
index 8b19f43..0ef093a 100644
--- a/drivers/gpu/drm/sysfb/efidrm.c
+++ b/drivers/gpu/drm/sysfb/efidrm.c
@@ -299,6 +299,9 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
*/
ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = drm_sysfb_damage_init(sysfb);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c
index c412555..4d31bb6 100644
--- a/drivers/gpu/drm/sysfb/ofdrm.c
+++ b/drivers/gpu/drm/sysfb/ofdrm.c
@@ -1021,6 +1021,9 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
*/
ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = drm_sysfb_damage_init(sysfb);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c
index deaeb7d..c40a6c5 100644
--- a/drivers/gpu/drm/sysfb/simpledrm.c
+++ b/drivers/gpu/drm/sysfb/simpledrm.c
@@ -813,6 +813,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
*/
ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = drm_sysfb_damage_init(sysfb);
if (ret)
return ERR_PTR(ret);
diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c
index 1668aab..812db4a 100644
--- a/drivers/gpu/drm/sysfb/vesadrm.c
+++ b/drivers/gpu/drm/sysfb/vesadrm.c
@@ -539,6 +539,9 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv,
*/
ret = drmm_mode_config_init(dev);
+ if (ret)
+ return ERR_PTR(ret);
+ ret = drm_sysfb_damage_init(sysfb);
if (ret)
return ERR_PTR(ret);
---
base-commit: 41b5458595c60143d0ebaa1e032c9cda5d764dff
change-id: 20261110-sysfb-dirtyfb-beda10fab602
Best regards,
--
Joseph Telaak <joseph@telaak.dev>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH RFC] drm/sysfb: Copy damage outside the DIRTYFB ioctl
2026-11-11 1:37 [PATCH RFC] drm/sysfb: Copy damage outside the DIRTYFB ioctl Joseph Telaak
@ 2026-11-11 8:48 ` Thomas Zimmermann
2026-11-11 8:55 ` Joseph Telaak
0 siblings, 1 reply; 3+ messages in thread
From: Thomas Zimmermann @ 2026-11-11 8:48 UTC (permalink / raw)
To: Joseph Telaak, Javier Martinez Canillas
Cc: Maarten Lankhorst, Maxime Ripard, David Airlie, Simona Vetter,
dri-devel, linux-kernel
Hi
Am 11.11.26 um 02:37 schrieb Joseph Telaak:
> The sysfb drivers implement DIRTYFB with drm_atomic_helper_dirtyfb(),
> which runs a blocking commit. The commit waits for the framebuffer's
> fences, so the ioctl doesn't return until the GPU has finished
> rendering into the framebuffer.
Yes, that's a requirement.
>
> Xorg's modesetting driver calls DIRTYFB from its main thread after
> every frame. When a separate GPU renders for a sysfb display, X then
> blocks for as long as the GPU takes to render the client's frame. With
> a game running, X took a median of 1.85 s to answer a pointer query.
>
> Queue the damage instead and copy it from a worker. The worker still
> waits for the fences, so nothing unfinished reaches the screen, and
> damage that arrives while a copy is pending is merged into it.
Pushing out work from the display update into a worker breaks feedback
from driver back to compositors. Your compositor/Xorg/whatever needs to
adapt to and deal with slow framerates.
>
> With this change, X answers in a median of 23 ms and the game's frame
> rate is unchanged.
Fix X if you really care.
Best regards
Thomas
>
> Signed-off-by: Joseph Telaak <joseph@telaak.dev>
> ---
> This is an RFC because I'm not sure DIRTYFB may return before the copy
> is done. The fbdev emulation already defers its damage to a worker,
> but userspace might rely on DIRTYFB being synchronous. If the approach
> is fine, would you rather have it as a nonblocking mode of
> drm_atomic_helper_dirtyfb() for all drivers instead of in sysfb?
>
> I hit this on an Intel Atom CE5300 board, where simpledrm drives the
> display and the SoC's PowerVR SGX545 GPU renders with a driver I'm
> writing (not upstream yet). I haven't reproduced it with an upstream
> GPU driver, but nothing in the stall is specific to the GPU.
> ---
> drivers/gpu/drm/sysfb/corebootdrm.c | 3 +
> drivers/gpu/drm/sysfb/drm_sysfb_helper.h | 15 ++-
> drivers/gpu/drm/sysfb/drm_sysfb_modeset.c | 154 ++++++++++++++++++++++++++++++
> drivers/gpu/drm/sysfb/efidrm.c | 3 +
> drivers/gpu/drm/sysfb/ofdrm.c | 3 +
> drivers/gpu/drm/sysfb/simpledrm.c | 3 +
> drivers/gpu/drm/sysfb/vesadrm.c | 3 +
> 7 files changed, 183 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/sysfb/corebootdrm.c b/drivers/gpu/drm/sysfb/corebootdrm.c
> index 853da9a..4ca359b 100644
> --- a/drivers/gpu/drm/sysfb/corebootdrm.c
> +++ b/drivers/gpu/drm/sysfb/corebootdrm.c
> @@ -206,6 +206,9 @@ static int corebootdrm_mode_config_init(struct corebootdrm_device *cdev,
> int ret;
>
> ret = drmm_mode_config_init(dev);
> + if (ret)
> + return ret;
> + ret = drm_sysfb_damage_init(sysfb);
> if (ret)
> return ret;
>
> diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
> index 6dab27b..59c4064 100644
> --- a/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
> +++ b/drivers/gpu/drm/sysfb/drm_sysfb_helper.h
> @@ -5,6 +5,8 @@
>
> #include <linux/container_of.h>
> #include <linux/iosys-map.h>
> +#include <linux/spinlock.h>
> +#include <linux/workqueue.h>
>
> #include <video/pixel_format.h>
>
> @@ -81,6 +83,12 @@ struct drm_sysfb_device {
>
> /* hardware-framebuffer kernel address */
> struct iosys_map fb_addr;
> +
> + /* damage from DIRTYFB, copied to the hardware framebuffer by damage_work */
> + spinlock_t damage_lock;
> + struct drm_framebuffer *damage_fb;
> + struct drm_rect damage;
> + struct work_struct damage_work;
> };
>
> static inline struct drm_sysfb_device *to_drm_sysfb_device(struct drm_device *dev)
> @@ -203,8 +211,13 @@ int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector);
> * Mode config
> */
>
> +int drm_sysfb_damage_init(struct drm_sysfb_device *sysfb);
> +struct drm_framebuffer *drm_sysfb_fb_create(struct drm_device *dev, struct drm_file *file,
> + const struct drm_format_info *info,
> + const struct drm_mode_fb_cmd2 *mode_cmd);
> +
> #define DRM_SYSFB_MODE_CONFIG_FUNCS \
> - .fb_create = drm_gem_fb_create_with_dirty, \
> + .fb_create = drm_sysfb_fb_create, \
> .atomic_check = drm_atomic_helper_check, \
> .atomic_commit = drm_atomic_helper_commit
>
> diff --git a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
> index 13df092..e77f0c0 100644
> --- a/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
> +++ b/drivers/gpu/drm/sysfb/drm_sysfb_modeset.c
> @@ -12,6 +12,7 @@
> #include <drm/drm_fourcc.h>
> #include <drm/drm_framebuffer.h>
> #include <drm/drm_gem_framebuffer_helper.h>
> +#include <drm/drm_managed.h>
> #include <drm/drm_panic.h>
> #include <drm/drm_print.h>
> #include <drm/drm_probe_helper.h>
> @@ -612,3 +613,156 @@ int drm_sysfb_connector_helper_get_modes(struct drm_connector *connector)
> return drm_connector_helper_get_modes_fixed(connector, &sysfb->fb_mode);
> }
> EXPORT_SYMBOL(drm_sysfb_connector_helper_get_modes);
> +
> +/*
> + * Damage
> + *
> + * DIRTYFB returns at once: the damage is merged into one pending rectangle,
> + * and a worker hands it to drm_atomic_helper_dirtyfb(). That commit waits for
> + * the framebuffer's fences, the GPU work that last rendered into it, before
> + * the CPU copies it to the hardware framebuffer. Done in the ioctl, the wait
> + * blocks the compositor for as long as that rendering takes. Damage that
> + * arrives while the worker runs is merged and copied by the next run, so none
> + * is dropped.
> + */
> +
> +static void drm_sysfb_damage_work(struct work_struct *work)
> +{
> + struct drm_sysfb_device *sysfb = container_of(work, struct drm_sysfb_device,
> + damage_work);
> + struct drm_framebuffer *fb;
> + struct drm_clip_rect clip;
> + int idx, ret;
> +
> + spin_lock(&sysfb->damage_lock);
> + fb = sysfb->damage_fb;
> + sysfb->damage_fb = NULL;
> + clip.x1 = sysfb->damage.x1;
> + clip.y1 = sysfb->damage.y1;
> + clip.x2 = sysfb->damage.x2;
> + clip.y2 = sysfb->damage.y2;
> + spin_unlock(&sysfb->damage_lock);
> + if (!fb)
> + return;
> +
> + if (drm_dev_enter(&sysfb->dev, &idx)) {
> + ret = drm_atomic_helper_dirtyfb(fb, NULL, 0, 0, &clip, 1);
> + if (ret)
> + drm_err_ratelimited(&sysfb->dev, "damage update failed: %d\n", ret);
> + drm_dev_exit(idx);
> + }
> + drm_framebuffer_put(fb);
> +}
> +
> +static void drm_sysfb_damage_fini(struct drm_device *dev, void *arg)
> +{
> + struct drm_sysfb_device *sysfb = arg;
> +
> + cancel_work_sync(&sysfb->damage_work);
> + if (sysfb->damage_fb)
> + drm_framebuffer_put(sysfb->damage_fb);
> + sysfb->damage_fb = NULL;
> +}
> +
> +/**
> + * drm_sysfb_damage_init - Set up deferred damage for a sysfb device
> + * @sysfb: the device
> + *
> + * Call after drmm_mode_config_init(). Framebuffers from drm_sysfb_fb_create()
> + * then copy their damage outside the DIRTYFB ioctl.
> + *
> + * Returns: 0 on success, or a negative errno.
> + */
> +int drm_sysfb_damage_init(struct drm_sysfb_device *sysfb)
> +{
> + spin_lock_init(&sysfb->damage_lock);
> + INIT_WORK(&sysfb->damage_work, drm_sysfb_damage_work);
> +
> + return drmm_add_action_or_reset(&sysfb->dev, drm_sysfb_damage_fini, sysfb);
> +}
> +EXPORT_SYMBOL(drm_sysfb_damage_init);
> +
> +static void drm_sysfb_damage_bounds(struct drm_framebuffer *fb, unsigned int flags,
> + const struct drm_clip_rect *clips, unsigned int num_clips,
> + struct drm_rect *r)
> +{
> + unsigned int inc = 1;
> +
> + if (!clips || !num_clips) {
> + drm_rect_init(r, 0, 0, fb->width, fb->height);
> + return;
> + }
> + if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
> + inc = 2;
> + num_clips /= 2;
> + }
> + drm_rect_init(r, clips->x1, clips->y1, clips->x2 - clips->x1, clips->y2 - clips->y1);
> + for (unsigned int i = 1; i < num_clips; i++) {
> + clips += inc;
> + r->x1 = min_t(int, r->x1, clips->x1);
> + r->y1 = min_t(int, r->y1, clips->y1);
> + r->x2 = max_t(int, r->x2, clips->x2);
> + r->y2 = max_t(int, r->y2, clips->y2);
> + }
> +}
> +
> +static int drm_sysfb_fb_dirty(struct drm_framebuffer *fb, struct drm_file *file,
> + unsigned int flags, unsigned int color,
> + struct drm_clip_rect *clips, unsigned int num_clips)
> +{
> + struct drm_sysfb_device *sysfb = to_drm_sysfb_device(fb->dev);
> + struct drm_rect r;
> + bool queue = false;
> +
> + drm_sysfb_damage_bounds(fb, flags, clips, num_clips, &r);
> +
> + spin_lock(&sysfb->damage_lock);
> + /* Pending damage of another framebuffer is copied first. */
> + while (sysfb->damage_fb && sysfb->damage_fb != fb) {
> + spin_unlock(&sysfb->damage_lock);
> + flush_work(&sysfb->damage_work);
> + spin_lock(&sysfb->damage_lock);
> + }
> + if (!sysfb->damage_fb) {
> + drm_framebuffer_get(fb);
> + sysfb->damage_fb = fb;
> + sysfb->damage = r;
> + queue = true;
> + } else {
> + sysfb->damage.x1 = min(sysfb->damage.x1, r.x1);
> + sysfb->damage.y1 = min(sysfb->damage.y1, r.y1);
> + sysfb->damage.x2 = max(sysfb->damage.x2, r.x2);
> + sysfb->damage.y2 = max(sysfb->damage.y2, r.y2);
> + }
> + spin_unlock(&sysfb->damage_lock);
> +
> + if (queue)
> + queue_work(system_dfl_wq, &sysfb->damage_work);
> + return 0;
> +}
> +
> +static const struct drm_framebuffer_funcs drm_sysfb_fb_funcs = {
> + .destroy = drm_gem_fb_destroy,
> + .create_handle = drm_gem_fb_create_handle,
> + .dirty = drm_sysfb_fb_dirty,
> +};
> +
> +/**
> + * drm_sysfb_fb_create - &drm_mode_config_funcs.fb_create for sysfb devices
> + * @dev: the device
> + * @file: the DRM file of the caller
> + * @info: the pixel format
> + * @mode_cmd: the framebuffer from userspace
> + *
> + * Like drm_gem_fb_create_with_dirty(), but the framebuffer's damage is copied
> + * outside the DIRTYFB ioctl (drm_sysfb_damage_init()).
> + *
> + * Returns: the framebuffer, or an ERR_PTR() on failure.
> + */
> +struct drm_framebuffer *drm_sysfb_fb_create(struct drm_device *dev, struct drm_file *file,
> + const struct drm_format_info *info,
> + const struct drm_mode_fb_cmd2 *mode_cmd)
> +{
> + return drm_gem_fb_create_with_funcs(dev, file, info, mode_cmd, &drm_sysfb_fb_funcs);
> +}
> +EXPORT_SYMBOL(drm_sysfb_fb_create);
> diff --git a/drivers/gpu/drm/sysfb/efidrm.c b/drivers/gpu/drm/sysfb/efidrm.c
> index 8b19f43..0ef093a 100644
> --- a/drivers/gpu/drm/sysfb/efidrm.c
> +++ b/drivers/gpu/drm/sysfb/efidrm.c
> @@ -299,6 +299,9 @@ static struct efidrm_device *efidrm_device_create(struct drm_driver *drv,
> */
>
> ret = drmm_mode_config_init(dev);
> + if (ret)
> + return ERR_PTR(ret);
> + ret = drm_sysfb_damage_init(sysfb);
> if (ret)
> return ERR_PTR(ret);
>
> diff --git a/drivers/gpu/drm/sysfb/ofdrm.c b/drivers/gpu/drm/sysfb/ofdrm.c
> index c412555..4d31bb6 100644
> --- a/drivers/gpu/drm/sysfb/ofdrm.c
> +++ b/drivers/gpu/drm/sysfb/ofdrm.c
> @@ -1021,6 +1021,9 @@ static struct ofdrm_device *ofdrm_device_create(struct drm_driver *drv,
> */
>
> ret = drmm_mode_config_init(dev);
> + if (ret)
> + return ERR_PTR(ret);
> + ret = drm_sysfb_damage_init(sysfb);
> if (ret)
> return ERR_PTR(ret);
>
> diff --git a/drivers/gpu/drm/sysfb/simpledrm.c b/drivers/gpu/drm/sysfb/simpledrm.c
> index deaeb7d..c40a6c5 100644
> --- a/drivers/gpu/drm/sysfb/simpledrm.c
> +++ b/drivers/gpu/drm/sysfb/simpledrm.c
> @@ -813,6 +813,9 @@ static struct simpledrm_device *simpledrm_device_create(struct drm_driver *drv,
> */
>
> ret = drmm_mode_config_init(dev);
> + if (ret)
> + return ERR_PTR(ret);
> + ret = drm_sysfb_damage_init(sysfb);
> if (ret)
> return ERR_PTR(ret);
>
> diff --git a/drivers/gpu/drm/sysfb/vesadrm.c b/drivers/gpu/drm/sysfb/vesadrm.c
> index 1668aab..812db4a 100644
> --- a/drivers/gpu/drm/sysfb/vesadrm.c
> +++ b/drivers/gpu/drm/sysfb/vesadrm.c
> @@ -539,6 +539,9 @@ static struct vesadrm_device *vesadrm_device_create(struct drm_driver *drv,
> */
>
> ret = drmm_mode_config_init(dev);
> + if (ret)
> + return ERR_PTR(ret);
> + ret = drm_sysfb_damage_init(sysfb);
> if (ret)
> return ERR_PTR(ret);
>
>
> ---
> base-commit: 41b5458595c60143d0ebaa1e032c9cda5d764dff
> change-id: 20261110-sysfb-dirtyfb-beda10fab602
>
> Best regards,
> --
> Joseph Telaak <joseph@telaak.dev>
>
--
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Stefan Gaiser, Jochen Jaser, Abhinav Puri, (HRB 36809, AG Nürnberg)
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH RFC] drm/sysfb: Copy damage outside the DIRTYFB ioctl
2026-11-11 8:48 ` Thomas Zimmermann
@ 2026-11-11 8:55 ` Joseph Telaak
0 siblings, 0 replies; 3+ messages in thread
From: Joseph Telaak @ 2026-11-11 8:55 UTC (permalink / raw)
To: Thomas Zimmermann
Cc: Javier Martinez Canillas, Maarten Lankhorst, Maxime Ripard,
David Airlie, Simona Vetter, dri-devel, linux-kernel
Hi Thomas,
Thanks for the quick review, and for explaining why the blocking
commit is required. That makes sense, so I’ll drop this patch and look
at handling it on the X side if it becomes a real problem.
Best regards,
Joseph
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-11-11 8:55 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-11 1:37 [PATCH RFC] drm/sysfb: Copy damage outside the DIRTYFB ioctl Joseph Telaak
2026-11-11 8:48 ` Thomas Zimmermann
2026-11-11 8:55 ` Joseph Telaak
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®