From nobody Mon Oct 6 10:16:34 2025 Received: from m16.mail.163.com (m16.mail.163.com [117.135.210.4]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 7344728C2A2 for ; Tue, 22 Jul 2025 10:29:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=117.135.210.4 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753180194; cv=none; b=NcNcP5y6iNgYxaAU9UUFRx+DII0Hb/JDyTSKbwXvt3GLviwPJwSJJ29bMbvVRf4ttANE4xkBomGCcPvGJHqOkBf1lKADV4DZ9ppKDTl880wggLIIokJuWK3wzvJzaYM9sq6cTcBkgoAFFd0QCN0r/IVxPj8aMNZS+y3MHYZ+aWg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1753180194; c=relaxed/simple; bh=y6K+BmWl/8HOSj/sW4XrLYao2aq9MJ1APtsuseqlaIk=; h=From:To:Cc:Subject:Date:Message-Id:MIME-Version; b=LoTAPPzLG/gcHzmrO78q6oyFA+IIGYJoB9ElcFE7A8y0uWRTQv2P1gNCqTTggcxnGnnWwdBCnkOy5GzJjfxEttDH6azGRcEm7KtJ9Pq+oMpff0ndYMQ3AVP+mA+JYnCUuBoMXlCp9LreHL+6xW8fTyYid/DtCp09nVzZcKUgcB0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com; spf=pass smtp.mailfrom=163.com; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b=mIZarsTk; arc=none smtp.client-ip=117.135.210.4 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=163.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=163.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key) header.d=163.com header.i=@163.com header.b="mIZarsTk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=163.com; s=s110527; h=From:To:Subject:Date:Message-Id:MIME-Version; bh=uq UFJbVekLDgeDQMYnUJjp+Rm3wS/RISllxNOdae4fI=; b=mIZarsTk1luWEGAP3v qIz/ZI3HKhbnvOAoLrZMbRfsxxqO7lYse8P+gQQJieIwLOZSDRPC3dqqAXp8KbU3 sDNWIjw21MkZhC+eQ6UsHH0mYPiTI7NbTUu7wW13as31FMEyeXMDsxi75Na8u7eE O5A/oYJ36Hnczj519mKV1sxl0= Received: from localhost.localdomain (unknown []) by gzsmtp1 (Coremail) with SMTP id PCgvCgCH93_5Z39o5HhiAA--.9828S2; Tue, 22 Jul 2025 18:29:13 +0800 (CST) From: oushixiong1025@163.com To: Dave Airlie Cc: Sean Paul , Thomas Zimmermann , Maarten Lankhorst , Maxime Ripard , David Airlie , Simona Vetter , dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org, Shixiong Ou Subject: [PATCH] drm/udl: add noblocking dirtyfb support Date: Tue, 22 Jul 2025 18:29:12 +0800 Message-Id: <20250722102912.2256895-1-oushixiong1025@163.com> X-Mailer: git-send-email 2.25.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: PCgvCgCH93_5Z39o5HhiAA--.9828S2 X-Coremail-Antispam: 1Uf129KBjvJXoWxKrW3Gw43ZrWkCF48Zr4Utwb_yoWfGF4DpF s8XasIyrWjqF4Fgrn7Gr48AFy3Gw1Ik3ykG3yxCanakF15KryUXFyrAFyv9F15Jr43GFnx XF9rKFyqkFWUJrJanT9S1TB71UUUUU7qnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDUYxBIdaVFxhVjvjDU0xZFpf9x07j05l8UUUUU= X-CM-SenderInfo: xrxvxxx0lr0wirqskqqrwthudrp/1tbiXQWSD2h-YTh75QABsU Content-Type: text/plain; charset="utf-8" From: Shixiong Ou [WHY] The DIRTYFB IOCTL is blocking by default. In multi-GPU setups, this may rate-limit the Primary GPU if the UDL handles damage too slowly. For example, in a cloud virtual desktop environment, when a USB DisplayLink device is connected to the client, the primary screen's refresh rate may significantly degrade. This occurs because the DIRTYFB operations must first be transmitted over the network (to the remote host) before the actual USB display commands can be executed. [HOW] Add non-blocking DIRTYFB support for UDL as an optional feature. Move udl_handle_damage() to a dedicated kthread, and try to merge damage re= gions before processing to prevent display content from lagging behind the latest data too much. In my cloud desktop system environment, the udl_handle_damage() takes up to dozens of milliseconds. After using this optional feature, the desktop disp= lay becomes smoother and more responsive. Signed-off-by: Shixiong Ou --- drivers/gpu/drm/udl/udl_drv.c | 4 + drivers/gpu/drm/udl/udl_drv.h | 16 ++++ drivers/gpu/drm/udl/udl_main.c | 41 ++++++++++ drivers/gpu/drm/udl/udl_modeset.c | 132 ++++++++++++++++++++++++++++++ 4 files changed, 193 insertions(+) diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c index 1506094..c2fc4b3 100644 --- a/drivers/gpu/drm/udl/udl_drv.c +++ b/drivers/gpu/drm/udl/udl_drv.c @@ -17,6 +17,10 @@ =20 #include "udl_drv.h" =20 +int udl_noblocking_damage; +MODULE_PARM_DESC(noblocking_damage, "Noblocking damage (1 =3D enabled, 0 = =3D disabled(default))"); +module_param_named(noblocking_damage, udl_noblocking_damage, int, 0444); + static int udl_usb_suspend(struct usb_interface *interface, pm_message_t message) { diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h index 282ebd6..6ed4346 100644 --- a/drivers/gpu/drm/udl/udl_drv.h +++ b/drivers/gpu/drm/udl/udl_drv.h @@ -21,6 +21,8 @@ #include #include #include +#include +#include =20 struct drm_mode_create_dumb; =20 @@ -34,6 +36,13 @@ struct drm_mode_create_dumb; =20 struct udl_device; =20 +struct damage_work_node { + struct drm_framebuffer *fb; + struct drm_rect *clip; + + struct list_head list; +}; + struct urb_node { struct list_head entry; struct udl_device *dev; @@ -74,10 +83,17 @@ struct udl_device { int sku_pixel_limit; =20 struct urb_list urbs; + + + struct list_head damage_queue; + spinlock_t damage_lock; + struct work_struct damage_work; }; =20 #define to_udl(x) container_of(x, struct udl_device, drm) =20 +extern int udl_noblocking_damage; + static inline struct usb_device *udl_to_usb_device(struct udl_device *udl) { return interface_to_usbdev(to_usb_interface(udl->drm.dev)); diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c index 3ebe2ce..3de1a06 100644 --- a/drivers/gpu/drm/udl/udl_main.c +++ b/drivers/gpu/drm/udl/udl_main.c @@ -9,6 +9,7 @@ */ =20 #include +#include #include #include =20 @@ -348,10 +349,50 @@ err: return ret; } =20 +static void udl_free_damage_queue(struct drm_device *dev) +{ + struct udl_device *udl =3D to_udl(dev); + struct list_head *entry, *tmp; + struct drm_gem_object *obj; + unsigned long flags; + int i; + + if (!udl_noblocking_damage) + return; + + udl_noblocking_damage =3D false; + + spin_lock_irqsave(&udl->damage_lock, flags); + + list_for_each_safe(entry, tmp, &udl->damage_queue) { + struct damage_work_node *damage; + + damage =3D list_entry(entry, struct damage_work_node, list); + if (damage =3D=3D NULL) + continue; + list_del(&damage->list); + + for (i =3D 0; i < damage->fb->format->num_planes; ++i) { + obj =3D drm_gem_fb_get_obj(damage->fb, i); + if (obj) + drm_gem_object_put(obj); + } + + drm_framebuffer_put(damage->fb); + + kfree(damage->clip); + kfree(damage); + } + + spin_unlock_irqrestore(&udl->damage_lock, flags); +} + + int udl_drop_usb(struct drm_device *dev) { struct udl_device *udl =3D to_udl(dev); =20 + udl_free_damage_queue(dev); udl_free_urb_list(dev); put_device(udl->dmadev); udl->dmadev =3D NULL; diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_mo= deset.c index 5a15399..a4a4c4b 100644 --- a/drivers/gpu/drm/udl/udl_modeset.c +++ b/drivers/gpu/drm/udl/udl_modeset.c @@ -261,6 +261,124 @@ static const uint64_t udl_primary_plane_fmtmods[] =3D= { DRM_FORMAT_MOD_INVALID }; =20 +static void udl_damage_work_func(struct work_struct *work) +{ + struct udl_device *udl =3D container_of(work, struct udl_device, damage_w= ork); + struct drm_gem_object *obj; + unsigned long flags; + unsigned int i; + int ret; + + if (!list_empty(&udl->damage_queue)) { + struct damage_work_node *damage; + + spin_lock_irqsave(&udl->damage_lock, flags); + + damage =3D list_first_entry(&udl->damage_queue, + struct damage_work_node, list); + list_del(&damage->list); + spin_unlock_irqrestore(&udl->damage_lock, flags); + + if (damage->clip && damage->fb) { + struct iosys_map map[DRM_FORMAT_MAX_PLANES]; + + ret =3D drm_gem_fb_vmap(damage->fb, map, NULL); + if (ret) { + DRM_ERROR("vmap damage fb error %d\n", ret); + goto free; + } + + udl_handle_damage(damage->fb, &map[0], damage->clip); + drm_gem_fb_vunmap(damage->fb, map); + +free: + for (i =3D 0; i < damage->fb->format->num_planes; ++i) { + obj =3D drm_gem_fb_get_obj(damage->fb, i); + if (obj) + drm_gem_object_put(obj); + } + + drm_framebuffer_put(damage->fb); + + kfree(damage->clip); + kfree(damage); + } + } +} + +void udl_damage_merged(const struct drm_rect *rect, struct drm_rect *dst_r= ect) +{ + dst_rect->x1 =3D min(dst_rect->x1, rect->x1); + dst_rect->y1 =3D min(dst_rect->y1, rect->y1); + dst_rect->x2 =3D max(dst_rect->x2, rect->x2); + dst_rect->y2 =3D max(dst_rect->y2, rect->y2); +} + +static void udl_queue_damage_work(struct udl_device *udl, struct drm_frame= buffer *fb, + const struct drm_rect *clip) +{ + struct list_head *entry, *tmp; + unsigned long flags; + + spin_lock_irqsave(&udl->damage_lock, flags); + + /* Just merge the damage if the same framebuffer damage is already queued= */ + list_for_each_safe(entry, tmp, &udl->damage_queue) { + struct damage_work_node *dirty_work; + + dirty_work =3D list_entry(entry, struct damage_work_node, list); + if (dirty_work =3D=3D NULL) + continue; + + if (dirty_work->fb =3D=3D fb) { + /* Merged clips */ + udl_damage_merged(clip, dirty_work->clip); + spin_unlock_irqrestore(&udl->damage_lock, flags); + + return; + } + } + + struct damage_work_node *new_work; + struct drm_rect *new_damage; + + new_work =3D kzalloc(sizeof(*new_work), GFP_KERNEL); + if (!new_work) + goto err; + + new_damage =3D kzalloc(sizeof(*new_damage), GFP_KERNEL); + if (!new_damage) + goto free_work; + + memcpy(new_damage, clip, sizeof(*clip)); + + new_work->fb =3D fb; + new_work->clip =3D new_damage; + drm_framebuffer_get(fb); + + struct drm_gem_object *obj; + unsigned int i; + + for (i =3D 0; i < fb->format->num_planes; ++i) { + obj =3D drm_gem_fb_get_obj(fb, i); + if (obj) + drm_gem_object_get(obj); + } + + /* Queue a new damage request */ + list_add_tail(&new_work->list, &udl->damage_queue); + spin_unlock_irqrestore(&udl->damage_lock, flags); + + schedule_work(&udl->damage_work); + + return; + +free_work: + kfree(new_work); +err: + spin_unlock_irqrestore(&udl->damage_lock, flags); +} + static void udl_primary_plane_helper_atomic_update(struct drm_plane *plane, struct drm_atomic_state *state) { @@ -276,6 +394,14 @@ static void udl_primary_plane_helper_atomic_update(str= uct drm_plane *plane, if (!fb) return; /* no framebuffer; plane is disabled */ =20 + if (udl_noblocking_damage) { + struct udl_device *udl =3D to_udl(dev); + + drm_atomic_helper_damage_merged(old_plane_state, plane_state, &damage); + udl_queue_damage_work(udl, fb, &damage); + return; + } + ret =3D drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE); if (ret) return; @@ -600,5 +726,11 @@ int udl_modeset_init(struct drm_device *dev) =20 drm_mode_config_reset(dev); =20 + if (udl_noblocking_damage) { + INIT_LIST_HEAD(&udl->damage_queue); + spin_lock_init(&udl->damage_lock); + INIT_WORK(&udl->damage_work, udl_damage_work_func); + } + return 0; } --=20 2.43.0