Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/framebuffer: Acquire internal references on GEM handles

Acquire GEM handles in drm_framebuffer_init() and release them in
the corresponding drm_framebuffer_cleanup(). Ties the handle's
lifetime to the framebuffer. Not all GEM buffer objects have GEM
handles. If not set, no refcounting takes place. This is the case
for some fbdev emulation. This is not a problem as these GEM objects
do not use dma-bufs and drivers will not release them while fbdev
emulation is running. Framebuffer flags keep a bit per color plane
of which the framebuffer holds a GEM handle reference.

As all drivers use drm_framebuffer_init(), they will now all hold
dma-buf references as fixed in commit 5307dce878d4 ("drm/gem: Acquire
references on GEM handles for framebuffers").

In the GEM framebuffer helpers, restore the original ref counting
on buffer objects. As the helpers for handle refcounting are now
no longer called from outside the DRM core, unexport the symbols.

v3:
- don't mix internal flags with mode flags (Christian)
v2:
- track framebuffer handle refs by flag
- drop gma500 cleanup (Christian)

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Fixes: 5307dce878d4 ("drm/gem: Acquire references on GEM handles for framebuffers")
Reported-by: Bert Karwatzki <spasswolf@web.de>
Closes: https://lore.kernel.org/dri-devel/20250703115915.3096-1-spasswolf@web.de/
Tested-by: Bert Karwatzki <spasswolf@web.de>
Tested-by: Mario Limonciello <superm1@kernel.org>
Tested-by: Borislav Petkov (AMD) <bp@alien8.de>
Cc: Thomas Zimmermann <tzimmermann@suse.de>
Cc: Anusha Srivatsa <asrivats@redhat.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Cc: Maxime Ripard <mripard@kernel.org>
Cc: Sumit Semwal <sumit.semwal@linaro.org>
Cc: "Christian König" <christian.koenig@amd.com>
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linaro-mm-sig@lists.linaro.org
Cc: <stable@vger.kernel.org>
Reviewed-by: Christian König <christian.koenig@amd.com>
Link: https://lore.kernel.org/r/20250707131224.249496-1-tzimmermann@suse.de

+68 -26
+29 -2
drivers/gpu/drm/drm_framebuffer.c
··· 862 862 int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 863 863 const struct drm_framebuffer_funcs *funcs) 864 864 { 865 + unsigned int i; 865 866 int ret; 867 + bool exists; 866 868 867 869 if (WARN_ON_ONCE(fb->dev != dev || !fb->format)) 868 870 return -EINVAL; 871 + 872 + for (i = 0; i < fb->format->num_planes; i++) { 873 + if (drm_WARN_ON_ONCE(dev, fb->internal_flags & DRM_FRAMEBUFFER_HAS_HANDLE_REF(i))) 874 + fb->internal_flags &= ~DRM_FRAMEBUFFER_HAS_HANDLE_REF(i); 875 + if (fb->obj[i]) { 876 + exists = drm_gem_object_handle_get_if_exists_unlocked(fb->obj[i]); 877 + if (exists) 878 + fb->internal_flags |= DRM_FRAMEBUFFER_HAS_HANDLE_REF(i); 879 + } 880 + } 869 881 870 882 INIT_LIST_HEAD(&fb->filp_head); 871 883 ··· 887 875 ret = __drm_mode_object_add(dev, &fb->base, DRM_MODE_OBJECT_FB, 888 876 false, drm_framebuffer_free); 889 877 if (ret) 890 - goto out; 878 + goto err; 891 879 892 880 mutex_lock(&dev->mode_config.fb_lock); 893 881 dev->mode_config.num_fb++; ··· 895 883 mutex_unlock(&dev->mode_config.fb_lock); 896 884 897 885 drm_mode_object_register(dev, &fb->base); 898 - out: 886 + 887 + return 0; 888 + 889 + err: 890 + for (i = 0; i < fb->format->num_planes; i++) { 891 + if (fb->internal_flags & DRM_FRAMEBUFFER_HAS_HANDLE_REF(i)) { 892 + drm_gem_object_handle_put_unlocked(fb->obj[i]); 893 + fb->internal_flags &= ~DRM_FRAMEBUFFER_HAS_HANDLE_REF(i); 894 + } 895 + } 899 896 return ret; 900 897 } 901 898 EXPORT_SYMBOL(drm_framebuffer_init); ··· 981 960 void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 982 961 { 983 962 struct drm_device *dev = fb->dev; 963 + unsigned int i; 964 + 965 + for (i = 0; i < fb->format->num_planes; i++) { 966 + if (fb->internal_flags & DRM_FRAMEBUFFER_HAS_HANDLE_REF(i)) 967 + drm_gem_object_handle_put_unlocked(fb->obj[i]); 968 + } 984 969 985 970 mutex_lock(&dev->mode_config.fb_lock); 986 971 list_del(&fb->head);
+24 -14
drivers/gpu/drm/drm_gem.c
··· 223 223 } 224 224 225 225 /** 226 - * drm_gem_object_handle_get_unlocked - acquire reference on user-space handles 226 + * drm_gem_object_handle_get_if_exists_unlocked - acquire reference on user-space handle, if any 227 227 * @obj: GEM object 228 228 * 229 - * Acquires a reference on the GEM buffer object's handle. Required 230 - * to keep the GEM object alive. Call drm_gem_object_handle_put_unlocked() 231 - * to release the reference. 229 + * Acquires a reference on the GEM buffer object's handle. Required to keep 230 + * the GEM object alive. Call drm_gem_object_handle_put_if_exists_unlocked() 231 + * to release the reference. Does nothing if the buffer object has no handle. 232 + * 233 + * Returns: 234 + * True if a handle exists, or false otherwise 232 235 */ 233 - void drm_gem_object_handle_get_unlocked(struct drm_gem_object *obj) 236 + bool drm_gem_object_handle_get_if_exists_unlocked(struct drm_gem_object *obj) 234 237 { 235 238 struct drm_device *dev = obj->dev; 236 239 237 240 guard(mutex)(&dev->object_name_lock); 238 241 239 - drm_WARN_ON(dev, !obj->handle_count); /* first ref taken in create-tail helper */ 242 + /* 243 + * First ref taken during GEM object creation, if any. Some 244 + * drivers set up internal framebuffers with GEM objects that 245 + * do not have a GEM handle. Hence, this counter can be zero. 246 + */ 247 + if (!obj->handle_count) 248 + return false; 249 + 240 250 drm_gem_object_handle_get(obj); 251 + 252 + return true; 241 253 } 242 - EXPORT_SYMBOL(drm_gem_object_handle_get_unlocked); 243 254 244 255 /** 245 256 * drm_gem_object_handle_free - release resources bound to userspace handles ··· 283 272 } 284 273 285 274 /** 286 - * drm_gem_object_handle_put_unlocked - releases reference on user-space handles 275 + * drm_gem_object_handle_put_unlocked - releases reference on user-space handle 287 276 * @obj: GEM object 288 277 * 289 278 * Releases a reference on the GEM buffer object's handle. Possibly releases ··· 294 283 struct drm_device *dev = obj->dev; 295 284 bool final = false; 296 285 297 - if (WARN_ON(READ_ONCE(obj->handle_count) == 0)) 286 + if (drm_WARN_ON(dev, READ_ONCE(obj->handle_count) == 0)) 298 287 return; 299 288 300 289 /* 301 - * Must bump handle count first as this may be the last 302 - * ref, in which case the object would disappear before we 303 - * checked for a name 304 - */ 290 + * Must bump handle count first as this may be the last 291 + * ref, in which case the object would disappear before 292 + * we checked for a name. 293 + */ 305 294 306 295 mutex_lock(&dev->object_name_lock); 307 296 if (--obj->handle_count == 0) { ··· 314 303 if (final) 315 304 drm_gem_object_put(obj); 316 305 } 317 - EXPORT_SYMBOL(drm_gem_object_handle_put_unlocked); 318 306 319 307 /* 320 308 * Called at device or object close to release the file's
+7 -9
drivers/gpu/drm/drm_gem_framebuffer_helper.c
··· 99 99 unsigned int i; 100 100 101 101 for (i = 0; i < fb->format->num_planes; i++) 102 - drm_gem_object_handle_put_unlocked(fb->obj[i]); 102 + drm_gem_object_put(fb->obj[i]); 103 103 104 104 drm_framebuffer_cleanup(fb); 105 105 kfree(fb); ··· 182 182 if (!objs[i]) { 183 183 drm_dbg_kms(dev, "Failed to lookup GEM object\n"); 184 184 ret = -ENOENT; 185 - goto err_gem_object_handle_put_unlocked; 185 + goto err_gem_object_put; 186 186 } 187 - drm_gem_object_handle_get_unlocked(objs[i]); 188 - drm_gem_object_put(objs[i]); 189 187 190 188 min_size = (height - 1) * mode_cmd->pitches[i] 191 189 + drm_format_info_min_pitch(info, i, width) ··· 193 195 drm_dbg_kms(dev, 194 196 "GEM object size (%zu) smaller than minimum size (%u) for plane %d\n", 195 197 objs[i]->size, min_size, i); 196 - drm_gem_object_handle_put_unlocked(objs[i]); 198 + drm_gem_object_put(objs[i]); 197 199 ret = -EINVAL; 198 - goto err_gem_object_handle_put_unlocked; 200 + goto err_gem_object_put; 199 201 } 200 202 } 201 203 202 204 ret = drm_gem_fb_init(dev, fb, mode_cmd, objs, i, funcs); 203 205 if (ret) 204 - goto err_gem_object_handle_put_unlocked; 206 + goto err_gem_object_put; 205 207 206 208 return 0; 207 209 208 - err_gem_object_handle_put_unlocked: 210 + err_gem_object_put: 209 211 while (i > 0) { 210 212 --i; 211 - drm_gem_object_handle_put_unlocked(objs[i]); 213 + drm_gem_object_put(objs[i]); 212 214 } 213 215 return ret; 214 216 }
+1 -1
drivers/gpu/drm/drm_internal.h
··· 161 161 162 162 /* drm_gem.c */ 163 163 int drm_gem_init(struct drm_device *dev); 164 - void drm_gem_object_handle_get_unlocked(struct drm_gem_object *obj); 164 + bool drm_gem_object_handle_get_if_exists_unlocked(struct drm_gem_object *obj); 165 165 void drm_gem_object_handle_put_unlocked(struct drm_gem_object *obj); 166 166 int drm_gem_handle_create_tail(struct drm_file *file_priv, 167 167 struct drm_gem_object *obj,
+7
include/drm/drm_framebuffer.h
··· 23 23 #ifndef __DRM_FRAMEBUFFER_H__ 24 24 #define __DRM_FRAMEBUFFER_H__ 25 25 26 + #include <linux/bits.h> 26 27 #include <linux/ctype.h> 27 28 #include <linux/list.h> 28 29 #include <linux/sched.h> ··· 100 99 unsigned color, struct drm_clip_rect *clips, 101 100 unsigned num_clips); 102 101 }; 102 + 103 + #define DRM_FRAMEBUFFER_HAS_HANDLE_REF(_i) BIT(0u + (_i)) 103 104 104 105 /** 105 106 * struct drm_framebuffer - frame buffer object ··· 191 188 * DRM_MODE_FB_MODIFIERS. 192 189 */ 193 190 int flags; 191 + /** 192 + * @internal_flags: Framebuffer flags like DRM_FRAMEBUFFER_HAS_HANDLE_REF. 193 + */ 194 + unsigned int internal_flags; 194 195 /** 195 196 * @filp_head: Placed on &drm_file.fbs, protected by &drm_file.fbs_lock. 196 197 */