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

drm/fbdev-generic: Fix locking with drm_client_buffer_vmap_local()

Temporarily lock the fbdev buffer object during updates to prevent
memory managers from evicting/moving the buffer. Moving a buffer
object while update its content results in undefined behaviour.

Fbdev-generic updates its buffer object from a shadow buffer. Gem-shmem
and gem-dma helpers do not move buffer objects, so they are safe to be
used with fbdev-generic. Gem-vram and qxl are based on TTM, but pin
buffer objects are part of the vmap operation. So both are also safe
to be used with fbdev-generic.

Amdgpu and nouveau do not pin or lock the buffer object during an
update. Their TTM-based memory management could move the buffer object
while the update is ongoing.

The new vmap_local and vunmap_local helpers hold the buffer object's
reservation lock during the buffer update. This prevents moving the
buffer object on all memory managers.

Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
Reviewed-by: Christian König <christian.koenig@amd.com>
Reviewed-by: Dmitry Osipenko <dmitry.osipenko@collabora.com>
Tested-by: Dmitry Osipenko <dmitry.osipenko@collabora.com> # virtio-gpu
Acked-by: Zack Rusin <zack.rusin@broadcom.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20240227113853.8464-11-tzimmermann@suse.de

+87 -10
+60 -8
drivers/gpu/drm/drm_client.c
··· 305 305 } 306 306 307 307 /** 308 + * drm_client_buffer_vmap_local - Map DRM client buffer into address space 309 + * @buffer: DRM client buffer 310 + * @map_copy: Returns the mapped memory's address 311 + * 312 + * This function maps a client buffer into kernel address space. If the 313 + * buffer is already mapped, it returns the existing mapping's address. 314 + * 315 + * Client buffer mappings are not ref'counted. Each call to 316 + * drm_client_buffer_vmap_local() should be closely followed by a call to 317 + * drm_client_buffer_vunmap_local(). See drm_client_buffer_vmap() for 318 + * long-term mappings. 319 + * 320 + * The returned address is a copy of the internal value. In contrast to 321 + * other vmap interfaces, you don't need it for the client's vunmap 322 + * function. So you can modify it at will during blit and draw operations. 323 + * 324 + * Returns: 325 + * 0 on success, or a negative errno code otherwise. 326 + */ 327 + int drm_client_buffer_vmap_local(struct drm_client_buffer *buffer, 328 + struct iosys_map *map_copy) 329 + { 330 + struct drm_gem_object *gem = buffer->gem; 331 + struct iosys_map *map = &buffer->map; 332 + int ret; 333 + 334 + drm_gem_lock(gem); 335 + 336 + ret = drm_gem_vmap(gem, map); 337 + if (ret) 338 + goto err_drm_gem_vmap_unlocked; 339 + *map_copy = *map; 340 + 341 + return 0; 342 + 343 + err_drm_gem_vmap_unlocked: 344 + drm_gem_unlock(gem); 345 + return 0; 346 + } 347 + EXPORT_SYMBOL(drm_client_buffer_vmap_local); 348 + 349 + /** 350 + * drm_client_buffer_vunmap_local - Unmap DRM client buffer 351 + * @buffer: DRM client buffer 352 + * 353 + * This function removes a client buffer's memory mapping established 354 + * with drm_client_buffer_vunmap_local(). Calling this function is only 355 + * required by clients that manage their buffer mappings by themselves. 356 + */ 357 + void drm_client_buffer_vunmap_local(struct drm_client_buffer *buffer) 358 + { 359 + struct drm_gem_object *gem = buffer->gem; 360 + struct iosys_map *map = &buffer->map; 361 + 362 + drm_gem_vunmap(gem, map); 363 + drm_gem_unlock(gem); 364 + } 365 + EXPORT_SYMBOL(drm_client_buffer_vunmap_local); 366 + 367 + /** 308 368 * drm_client_buffer_vmap - Map DRM client buffer into address space 309 369 * @buffer: DRM client buffer 310 370 * @map_copy: Returns the mapped memory's address ··· 391 331 struct iosys_map *map = &buffer->map; 392 332 int ret; 393 333 394 - /* 395 - * FIXME: The dependency on GEM here isn't required, we could 396 - * convert the driver handle to a dma-buf instead and use the 397 - * backend-agnostic dma-buf vmap support instead. This would 398 - * require that the handle2fd prime ioctl is reworked to pull the 399 - * fd_install step out of the driver backend hooks, to make that 400 - * final step optional for internal users. 401 - */ 402 334 ret = drm_gem_vmap_unlocked(buffer->gem, map); 403 335 if (ret) 404 336 return ret;
+2 -2
drivers/gpu/drm/drm_fbdev_generic.c
··· 197 197 */ 198 198 mutex_lock(&fb_helper->lock); 199 199 200 - ret = drm_client_buffer_vmap(buffer, &map); 200 + ret = drm_client_buffer_vmap_local(buffer, &map); 201 201 if (ret) 202 202 goto out; 203 203 204 204 dst = map; 205 205 drm_fbdev_generic_damage_blit_real(fb_helper, clip, &dst); 206 206 207 - drm_client_buffer_vunmap(buffer); 207 + drm_client_buffer_vunmap_local(buffer); 208 208 209 209 out: 210 210 mutex_unlock(&fb_helper->lock);
+12
drivers/gpu/drm/drm_gem.c
··· 1227 1227 } 1228 1228 EXPORT_SYMBOL(drm_gem_vunmap); 1229 1229 1230 + void drm_gem_lock(struct drm_gem_object *obj) 1231 + { 1232 + dma_resv_lock(obj->resv, NULL); 1233 + } 1234 + EXPORT_SYMBOL(drm_gem_lock); 1235 + 1236 + void drm_gem_unlock(struct drm_gem_object *obj) 1237 + { 1238 + dma_resv_unlock(obj->resv); 1239 + } 1240 + EXPORT_SYMBOL(drm_gem_unlock); 1241 + 1230 1242 int drm_gem_vmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map) 1231 1243 { 1232 1244 int ret;
+10
include/drm/drm_client.h
··· 141 141 142 142 /** 143 143 * @gem: GEM object backing this buffer 144 + * 145 + * FIXME: The dependency on GEM here isn't required, we could 146 + * convert the driver handle to a dma-buf instead and use the 147 + * backend-agnostic dma-buf vmap support instead. This would 148 + * require that the handle2fd prime ioctl is reworked to pull the 149 + * fd_install step out of the driver backend hooks, to make that 150 + * final step optional for internal users. 144 151 */ 145 152 struct drm_gem_object *gem; 146 153 ··· 166 159 drm_client_framebuffer_create(struct drm_client_dev *client, u32 width, u32 height, u32 format); 167 160 void drm_client_framebuffer_delete(struct drm_client_buffer *buffer); 168 161 int drm_client_framebuffer_flush(struct drm_client_buffer *buffer, struct drm_rect *rect); 162 + int drm_client_buffer_vmap_local(struct drm_client_buffer *buffer, 163 + struct iosys_map *map_copy); 164 + void drm_client_buffer_vunmap_local(struct drm_client_buffer *buffer); 169 165 int drm_client_buffer_vmap(struct drm_client_buffer *buffer, 170 166 struct iosys_map *map); 171 167 void drm_client_buffer_vunmap(struct drm_client_buffer *buffer);
+3
include/drm/drm_gem.h
··· 527 527 void drm_gem_put_pages(struct drm_gem_object *obj, struct page **pages, 528 528 bool dirty, bool accessed); 529 529 530 + void drm_gem_lock(struct drm_gem_object *obj); 531 + void drm_gem_unlock(struct drm_gem_object *obj); 532 + 530 533 int drm_gem_vmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map); 531 534 void drm_gem_vunmap_unlocked(struct drm_gem_object *obj, struct iosys_map *map); 532 535