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

drm/vmwgfx: Remove rcu locks from user resources

User resource lookups used rcu to avoid two extra atomics. Unfortunately
the rcu paths were buggy and it was easy to make the driver crash by
submitting command buffers from two different threads. Because the
lookups never show up in performance profiles replace them with a
regular spin lock which fixes the races in accesses to those shared
resources.

Fixes kernel oops'es in IGT's vmwgfx execution_buffer stress test and
seen crashes with apps using shared resources.

Fixes: e14c02e6b699 ("drm/vmwgfx: Look up objects without taking a reference")
Signed-off-by: Zack Rusin <zackr@vmware.com>
Reviewed-by: Martin Krastev <krastevm@vmware.com>
Reviewed-by: Maaz Mombasawala <mombasawalam@vmware.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20221207172907.959037-1-zack@kde.org

+90 -236
+4 -37
drivers/gpu/drm/vmwgfx/ttm_object.c
··· 254 254 kref_put(&base->refcount, ttm_release_base); 255 255 } 256 256 257 - /** 258 - * ttm_base_object_noref_lookup - look up a base object without reference 259 - * @tfile: The struct ttm_object_file the object is registered with. 260 - * @key: The object handle. 261 - * 262 - * This function looks up a ttm base object and returns a pointer to it 263 - * without refcounting the pointer. The returned pointer is only valid 264 - * until ttm_base_object_noref_release() is called, and the object 265 - * pointed to by the returned pointer may be doomed. Any persistent usage 266 - * of the object requires a refcount to be taken using kref_get_unless_zero(). 267 - * Iff this function returns successfully it needs to be paired with 268 - * ttm_base_object_noref_release() and no sleeping- or scheduling functions 269 - * may be called inbetween these function callse. 270 - * 271 - * Return: A pointer to the object if successful or NULL otherwise. 272 - */ 273 - struct ttm_base_object * 274 - ttm_base_object_noref_lookup(struct ttm_object_file *tfile, uint64_t key) 275 - { 276 - struct vmwgfx_hash_item *hash; 277 - int ret; 278 - 279 - rcu_read_lock(); 280 - ret = ttm_tfile_find_ref_rcu(tfile, key, &hash); 281 - if (ret) { 282 - rcu_read_unlock(); 283 - return NULL; 284 - } 285 - 286 - __release(RCU); 287 - return hlist_entry(hash, struct ttm_ref_object, hash)->obj; 288 - } 289 - EXPORT_SYMBOL(ttm_base_object_noref_lookup); 290 - 291 257 struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, 292 258 uint64_t key) 293 259 { ··· 261 295 struct vmwgfx_hash_item *hash; 262 296 int ret; 263 297 264 - rcu_read_lock(); 265 - ret = ttm_tfile_find_ref_rcu(tfile, key, &hash); 298 + spin_lock(&tfile->lock); 299 + ret = ttm_tfile_find_ref(tfile, key, &hash); 266 300 267 301 if (likely(ret == 0)) { 268 302 base = hlist_entry(hash, struct ttm_ref_object, hash)->obj; 269 303 if (!kref_get_unless_zero(&base->refcount)) 270 304 base = NULL; 271 305 } 272 - rcu_read_unlock(); 306 + spin_unlock(&tfile->lock); 307 + 273 308 274 309 return base; 275 310 }
-14
drivers/gpu/drm/vmwgfx/ttm_object.h
··· 307 307 #define ttm_prime_object_kfree(__obj, __prime) \ 308 308 kfree_rcu(__obj, __prime.base.rhead) 309 309 310 - struct ttm_base_object * 311 - ttm_base_object_noref_lookup(struct ttm_object_file *tfile, uint64_t key); 312 - 313 - /** 314 - * ttm_base_object_noref_release - release a base object pointer looked up 315 - * without reference 316 - * 317 - * Releases a base object pointer looked up with ttm_base_object_noref_lookup(). 318 - */ 319 - static inline void ttm_base_object_noref_release(void) 320 - { 321 - __acquire(RCU); 322 - rcu_read_unlock(); 323 - } 324 310 #endif
-38
drivers/gpu/drm/vmwgfx/vmwgfx_bo.c
··· 716 716 } 717 717 718 718 /** 719 - * vmw_user_bo_noref_lookup - Look up a vmw user buffer object without reference 720 - * @filp: The TTM object file the handle is registered with. 721 - * @handle: The user buffer object handle. 722 - * 723 - * This function looks up a struct vmw_bo and returns a pointer to the 724 - * struct vmw_buffer_object it derives from without refcounting the pointer. 725 - * The returned pointer is only valid until vmw_user_bo_noref_release() is 726 - * called, and the object pointed to by the returned pointer may be doomed. 727 - * Any persistent usage of the object requires a refcount to be taken using 728 - * ttm_bo_reference_unless_doomed(). Iff this function returns successfully it 729 - * needs to be paired with vmw_user_bo_noref_release() and no sleeping- 730 - * or scheduling functions may be called in between these function calls. 731 - * 732 - * Return: A struct vmw_buffer_object pointer if successful or negative 733 - * error pointer on failure. 734 - */ 735 - struct vmw_buffer_object * 736 - vmw_user_bo_noref_lookup(struct drm_file *filp, u32 handle) 737 - { 738 - struct vmw_buffer_object *vmw_bo; 739 - struct ttm_buffer_object *bo; 740 - struct drm_gem_object *gobj = drm_gem_object_lookup(filp, handle); 741 - 742 - if (!gobj) { 743 - DRM_ERROR("Invalid buffer object handle 0x%08lx.\n", 744 - (unsigned long)handle); 745 - return ERR_PTR(-ESRCH); 746 - } 747 - vmw_bo = gem_to_vmw_bo(gobj); 748 - bo = ttm_bo_get_unless_zero(&vmw_bo->base); 749 - vmw_bo = vmw_buffer_object(bo); 750 - drm_gem_object_put(gobj); 751 - 752 - return vmw_bo; 753 - } 754 - 755 - 756 - /** 757 719 * vmw_bo_fence_single - Utility function to fence a single TTM buffer 758 720 * object without unreserving it. 759 721 *
+1 -17
drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
··· 830 830 uint32_t handle, 831 831 const struct vmw_user_resource_conv *converter, 832 832 struct vmw_resource **p_res); 833 - extern struct vmw_resource * 834 - vmw_user_resource_noref_lookup_handle(struct vmw_private *dev_priv, 835 - struct ttm_object_file *tfile, 836 - uint32_t handle, 837 - const struct vmw_user_resource_conv * 838 - converter); 833 + 839 834 extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data, 840 835 struct drm_file *file_priv); 841 836 extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, ··· 867 872 static inline bool vmw_resource_mob_attached(const struct vmw_resource *res) 868 873 { 869 874 return !RB_EMPTY_NODE(&res->mob_node); 870 - } 871 - 872 - /** 873 - * vmw_user_resource_noref_release - release a user resource pointer looked up 874 - * without reference 875 - */ 876 - static inline void vmw_user_resource_noref_release(void) 877 - { 878 - ttm_base_object_noref_release(); 879 875 } 880 876 881 877 /** ··· 920 934 extern void vmw_bo_move_notify(struct ttm_buffer_object *bo, 921 935 struct ttm_resource *mem); 922 936 extern void vmw_bo_swap_notify(struct ttm_buffer_object *bo); 923 - extern struct vmw_buffer_object * 924 - vmw_user_bo_noref_lookup(struct drm_file *filp, u32 handle); 925 937 926 938 /** 927 939 * vmw_bo_adjust_prio - Adjust the buffer object eviction priority
+85 -97
drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
··· 290 290 rcache->valid_handle = 0; 291 291 } 292 292 293 + enum vmw_val_add_flags { 294 + vmw_val_add_flag_none = 0, 295 + vmw_val_add_flag_noctx = 1 << 0, 296 + }; 297 + 293 298 /** 294 - * vmw_execbuf_res_noref_val_add - Add a resource described by an unreferenced 295 - * rcu-protected pointer to the validation list. 299 + * vmw_execbuf_res_val_add - Add a resource to the validation list. 296 300 * 297 301 * @sw_context: Pointer to the software context. 298 302 * @res: Unreferenced rcu-protected pointer to the resource. 299 303 * @dirty: Whether to change dirty status. 304 + * @flags: specifies whether to use the context or not 300 305 * 301 306 * Returns: 0 on success. Negative error code on failure. Typical error codes 302 307 * are %-EINVAL on inconsistency and %-ESRCH if the resource was doomed. 303 308 */ 304 - static int vmw_execbuf_res_noref_val_add(struct vmw_sw_context *sw_context, 305 - struct vmw_resource *res, 306 - u32 dirty) 309 + static int vmw_execbuf_res_val_add(struct vmw_sw_context *sw_context, 310 + struct vmw_resource *res, 311 + u32 dirty, 312 + u32 flags) 307 313 { 308 314 struct vmw_private *dev_priv = res->dev_priv; 309 315 int ret; ··· 324 318 if (dirty) 325 319 vmw_validation_res_set_dirty(sw_context->ctx, 326 320 rcache->private, dirty); 327 - vmw_user_resource_noref_release(); 328 321 return 0; 329 322 } 330 323 331 - priv_size = vmw_execbuf_res_size(dev_priv, res_type); 332 - ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size, 333 - dirty, (void **)&ctx_info, 334 - &first_usage); 335 - vmw_user_resource_noref_release(); 336 - if (ret) 337 - return ret; 338 - 339 - if (priv_size && first_usage) { 340 - ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res, 341 - ctx_info); 342 - if (ret) { 343 - VMW_DEBUG_USER("Failed first usage context setup.\n"); 324 + if ((flags & vmw_val_add_flag_noctx) != 0) { 325 + ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty, 326 + (void **)&ctx_info, NULL); 327 + if (ret) 344 328 return ret; 329 + 330 + } else { 331 + priv_size = vmw_execbuf_res_size(dev_priv, res_type); 332 + ret = vmw_validation_add_resource(sw_context->ctx, res, priv_size, 333 + dirty, (void **)&ctx_info, 334 + &first_usage); 335 + if (ret) 336 + return ret; 337 + 338 + if (priv_size && first_usage) { 339 + ret = vmw_cmd_ctx_first_setup(dev_priv, sw_context, res, 340 + ctx_info); 341 + if (ret) { 342 + VMW_DEBUG_USER("Failed first usage context setup.\n"); 343 + return ret; 344 + } 345 345 } 346 346 } 347 347 348 348 vmw_execbuf_rcache_update(rcache, res, ctx_info); 349 - return 0; 350 - } 351 - 352 - /** 353 - * vmw_execbuf_res_noctx_val_add - Add a non-context resource to the resource 354 - * validation list if it's not already on it 355 - * 356 - * @sw_context: Pointer to the software context. 357 - * @res: Pointer to the resource. 358 - * @dirty: Whether to change dirty status. 359 - * 360 - * Returns: Zero on success. Negative error code on failure. 361 - */ 362 - static int vmw_execbuf_res_noctx_val_add(struct vmw_sw_context *sw_context, 363 - struct vmw_resource *res, 364 - u32 dirty) 365 - { 366 - struct vmw_res_cache_entry *rcache; 367 - enum vmw_res_type res_type = vmw_res_type(res); 368 - void *ptr; 369 - int ret; 370 - 371 - rcache = &sw_context->res_cache[res_type]; 372 - if (likely(rcache->valid && rcache->res == res)) { 373 - if (dirty) 374 - vmw_validation_res_set_dirty(sw_context->ctx, 375 - rcache->private, dirty); 376 - return 0; 377 - } 378 - 379 - ret = vmw_validation_add_resource(sw_context->ctx, res, 0, dirty, 380 - &ptr, NULL); 381 - if (ret) 382 - return ret; 383 - 384 - vmw_execbuf_rcache_update(rcache, res, ptr); 385 - 386 349 return 0; 387 350 } 388 351 ··· 373 398 * First add the resource the view is pointing to, otherwise it may be 374 399 * swapped out when the view is validated. 375 400 */ 376 - ret = vmw_execbuf_res_noctx_val_add(sw_context, vmw_view_srf(view), 377 - vmw_view_dirtying(view)); 401 + ret = vmw_execbuf_res_val_add(sw_context, vmw_view_srf(view), 402 + vmw_view_dirtying(view), vmw_val_add_flag_noctx); 378 403 if (ret) 379 404 return ret; 380 405 381 - return vmw_execbuf_res_noctx_val_add(sw_context, view, 382 - VMW_RES_DIRTY_NONE); 406 + return vmw_execbuf_res_val_add(sw_context, view, VMW_RES_DIRTY_NONE, 407 + vmw_val_add_flag_noctx); 383 408 } 384 409 385 410 /** ··· 450 475 if (IS_ERR(res)) 451 476 continue; 452 477 453 - ret = vmw_execbuf_res_noctx_val_add(sw_context, res, 454 - VMW_RES_DIRTY_SET); 478 + ret = vmw_execbuf_res_val_add(sw_context, res, 479 + VMW_RES_DIRTY_SET, 480 + vmw_val_add_flag_noctx); 455 481 if (unlikely(ret != 0)) 456 482 return ret; 457 483 } ··· 466 490 if (vmw_res_type(entry->res) == vmw_res_view) 467 491 ret = vmw_view_res_val_add(sw_context, entry->res); 468 492 else 469 - ret = vmw_execbuf_res_noctx_val_add 470 - (sw_context, entry->res, 471 - vmw_binding_dirtying(entry->bt)); 493 + ret = vmw_execbuf_res_val_add(sw_context, entry->res, 494 + vmw_binding_dirtying(entry->bt), 495 + vmw_val_add_flag_noctx); 472 496 if (unlikely(ret != 0)) 473 497 break; 474 498 } ··· 634 658 { 635 659 struct vmw_res_cache_entry *rcache = &sw_context->res_cache[res_type]; 636 660 struct vmw_resource *res; 637 - int ret; 661 + int ret = 0; 662 + bool needs_unref = false; 638 663 639 664 if (p_res) 640 665 *p_res = NULL; ··· 660 683 if (ret) 661 684 return ret; 662 685 663 - res = vmw_user_resource_noref_lookup_handle 664 - (dev_priv, sw_context->fp->tfile, *id_loc, converter); 665 - if (IS_ERR(res)) { 686 + ret = vmw_user_resource_lookup_handle 687 + (dev_priv, sw_context->fp->tfile, *id_loc, converter, &res); 688 + if (ret != 0) { 666 689 VMW_DEBUG_USER("Could not find/use resource 0x%08x.\n", 667 690 (unsigned int) *id_loc); 668 - return PTR_ERR(res); 669 - } 670 - 671 - ret = vmw_execbuf_res_noref_val_add(sw_context, res, dirty); 672 - if (unlikely(ret != 0)) 673 691 return ret; 692 + } 693 + needs_unref = true; 694 + 695 + ret = vmw_execbuf_res_val_add(sw_context, res, dirty, vmw_val_add_flag_none); 696 + if (unlikely(ret != 0)) 697 + goto res_check_done; 674 698 675 699 if (rcache->valid && rcache->res == res) { 676 700 rcache->valid_handle = true; ··· 686 708 if (p_res) 687 709 *p_res = res; 688 710 689 - return 0; 711 + res_check_done: 712 + if (needs_unref) 713 + vmw_resource_unreference(&res); 714 + 715 + return ret; 690 716 } 691 717 692 718 /** ··· 1153 1171 int ret; 1154 1172 1155 1173 vmw_validation_preload_bo(sw_context->ctx); 1156 - vmw_bo = vmw_user_bo_noref_lookup(sw_context->filp, handle); 1157 - if (IS_ERR(vmw_bo)) { 1158 - VMW_DEBUG_USER("Could not find or use MOB buffer.\n"); 1174 + ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo); 1175 + if (ret != 0) { 1176 + drm_dbg(&dev_priv->drm, "Could not find or use MOB buffer.\n"); 1159 1177 return PTR_ERR(vmw_bo); 1160 1178 } 1161 1179 ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, true, false); ··· 1207 1225 int ret; 1208 1226 1209 1227 vmw_validation_preload_bo(sw_context->ctx); 1210 - vmw_bo = vmw_user_bo_noref_lookup(sw_context->filp, handle); 1211 - if (IS_ERR(vmw_bo)) { 1212 - VMW_DEBUG_USER("Could not find or use GMR region.\n"); 1228 + ret = vmw_user_bo_lookup(sw_context->filp, handle, &vmw_bo); 1229 + if (ret != 0) { 1230 + drm_dbg(&dev_priv->drm, "Could not find or use GMR region.\n"); 1213 1231 return PTR_ERR(vmw_bo); 1214 1232 } 1215 1233 ret = vmw_validation_add_bo(sw_context->ctx, vmw_bo, false, false); ··· 2007 2025 res = vmw_shader_lookup(vmw_context_res_man(ctx), 2008 2026 cmd->body.shid, cmd->body.type); 2009 2027 if (!IS_ERR(res)) { 2010 - ret = vmw_execbuf_res_noctx_val_add(sw_context, res, 2011 - VMW_RES_DIRTY_NONE); 2028 + ret = vmw_execbuf_res_val_add(sw_context, res, 2029 + VMW_RES_DIRTY_NONE, 2030 + vmw_val_add_flag_noctx); 2012 2031 if (unlikely(ret != 0)) 2013 2032 return ret; 2014 2033 ··· 2256 2273 return PTR_ERR(res); 2257 2274 } 2258 2275 2259 - ret = vmw_execbuf_res_noctx_val_add(sw_context, res, 2260 - VMW_RES_DIRTY_NONE); 2276 + ret = vmw_execbuf_res_val_add(sw_context, res, 2277 + VMW_RES_DIRTY_NONE, 2278 + vmw_val_add_flag_noctx); 2261 2279 if (ret) 2262 2280 return ret; 2263 2281 } ··· 2761 2777 return PTR_ERR(res); 2762 2778 } 2763 2779 2764 - ret = vmw_execbuf_res_noctx_val_add(sw_context, res, 2765 - VMW_RES_DIRTY_NONE); 2780 + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE, 2781 + vmw_val_add_flag_noctx); 2766 2782 if (ret) { 2767 2783 VMW_DEBUG_USER("Error creating resource validation node.\n"); 2768 2784 return ret; ··· 3082 3098 3083 3099 vmw_dx_streamoutput_set_size(res, cmd->body.sizeInBytes); 3084 3100 3085 - ret = vmw_execbuf_res_noctx_val_add(sw_context, res, 3086 - VMW_RES_DIRTY_NONE); 3101 + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE, 3102 + vmw_val_add_flag_noctx); 3087 3103 if (ret) { 3088 3104 DRM_ERROR("Error creating resource validation node.\n"); 3089 3105 return ret; ··· 3132 3148 return 0; 3133 3149 } 3134 3150 3135 - ret = vmw_execbuf_res_noctx_val_add(sw_context, res, 3136 - VMW_RES_DIRTY_NONE); 3151 + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_NONE, 3152 + vmw_val_add_flag_noctx); 3137 3153 if (ret) { 3138 3154 DRM_ERROR("Error creating resource validation node.\n"); 3139 3155 return ret; ··· 4050 4066 if (ret) 4051 4067 return ret; 4052 4068 4053 - res = vmw_user_resource_noref_lookup_handle 4069 + ret = vmw_user_resource_lookup_handle 4054 4070 (dev_priv, sw_context->fp->tfile, handle, 4055 - user_context_converter); 4056 - if (IS_ERR(res)) { 4071 + user_context_converter, &res); 4072 + if (ret != 0) { 4057 4073 VMW_DEBUG_USER("Could not find or user DX context 0x%08x.\n", 4058 4074 (unsigned int) handle); 4059 - return PTR_ERR(res); 4075 + return ret; 4060 4076 } 4061 4077 4062 - ret = vmw_execbuf_res_noref_val_add(sw_context, res, VMW_RES_DIRTY_SET); 4063 - if (unlikely(ret != 0)) 4078 + ret = vmw_execbuf_res_val_add(sw_context, res, VMW_RES_DIRTY_SET, 4079 + vmw_val_add_flag_none); 4080 + if (unlikely(ret != 0)) { 4081 + vmw_resource_unreference(&res); 4064 4082 return ret; 4083 + } 4065 4084 4066 4085 sw_context->dx_ctx_node = vmw_execbuf_info_from_res(sw_context, res); 4067 4086 sw_context->man = vmw_context_res_man(res); 4068 4087 4088 + vmw_resource_unreference(&res); 4069 4089 return 0; 4070 4090 } 4071 4091
-33
drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
··· 281 281 return ret; 282 282 } 283 283 284 - /** 285 - * vmw_user_resource_noref_lookup_handle - lookup a struct resource from a 286 - * TTM user-space handle and perform basic type checks 287 - * 288 - * @dev_priv: Pointer to a device private struct 289 - * @tfile: Pointer to a struct ttm_object_file identifying the caller 290 - * @handle: The TTM user-space handle 291 - * @converter: Pointer to an object describing the resource type 292 - * 293 - * If the handle can't be found or is associated with an incorrect resource 294 - * type, -EINVAL will be returned. 295 - */ 296 - struct vmw_resource * 297 - vmw_user_resource_noref_lookup_handle(struct vmw_private *dev_priv, 298 - struct ttm_object_file *tfile, 299 - uint32_t handle, 300 - const struct vmw_user_resource_conv 301 - *converter) 302 - { 303 - struct ttm_base_object *base; 304 - 305 - base = ttm_base_object_noref_lookup(tfile, handle); 306 - if (!base) 307 - return ERR_PTR(-ESRCH); 308 - 309 - if (unlikely(ttm_base_object_type(base) != converter->object_type)) { 310 - ttm_base_object_noref_release(); 311 - return ERR_PTR(-EINVAL); 312 - } 313 - 314 - return converter->base_obj_to_res(base); 315 - } 316 - 317 284 /* 318 285 * Helper function that looks either a surface or bo. 319 286 *