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

drm: fix deadlock of syncobj v6

v2:
add a mutex between sync_cb execution and free.
v3:
clearly separating the roles for pt_lock and cb_mutex (Chris)
v4:
the cb_mutex should be taken outside of the pt_lock around
this if() block. (Chris)
v5:
fix a corner case
v6:
tidy drm_syncobj_fence_get_or_add_callback up. (Chris)

Tested by syncobj_basic and syncobj_wait of igt.

Signed-off-by: Chunming Zhou <david1.zhou@amd.com>
Cc: Daniel Vetter <daniel@ffwll.ch>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Christian König <christian.koenig@amd.com>
Cc: intel-gfx@lists.freedesktop.org
Reviewed-by: Chris Wilson <chris@chris-wilson.co.uk>
Signed-off-by: Christian König <christian.koenig@amd.com>
Link: https://patchwork.kernel.org/patch/10652893/

authored by

Chunming Zhou and committed by
Christian König
43cf1fc0 3d42f1dd

+95 -97
+89 -95
drivers/gpu/drm/drm_syncobj.c
··· 106 106 } 107 107 EXPORT_SYMBOL(drm_syncobj_find); 108 108 109 - static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, 110 - struct drm_syncobj_cb *cb, 111 - drm_syncobj_func_t func) 112 - { 113 - cb->func = func; 114 - list_add_tail(&cb->node, &syncobj->cb_list); 115 - } 116 - 117 - static int drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, 118 - struct dma_fence **fence, 119 - struct drm_syncobj_cb *cb, 120 - drm_syncobj_func_t func) 121 - { 122 - int ret; 123 - 124 - ret = drm_syncobj_search_fence(syncobj, 0, 0, fence); 125 - if (!ret) 126 - return 1; 127 - 128 - spin_lock(&syncobj->lock); 129 - /* We've already tried once to get a fence and failed. Now that we 130 - * have the lock, try one more time just to be sure we don't add a 131 - * callback when a fence has already been set. 132 - */ 133 - if (!list_empty(&syncobj->signal_pt_list)) { 134 - spin_unlock(&syncobj->lock); 135 - drm_syncobj_search_fence(syncobj, 0, 0, fence); 136 - if (*fence) 137 - return 1; 138 - spin_lock(&syncobj->lock); 139 - } else { 140 - *fence = NULL; 141 - drm_syncobj_add_callback_locked(syncobj, cb, func); 142 - ret = 0; 143 - } 144 - spin_unlock(&syncobj->lock); 145 - 146 - return ret; 147 - } 148 - 149 - void drm_syncobj_add_callback(struct drm_syncobj *syncobj, 150 - struct drm_syncobj_cb *cb, 151 - drm_syncobj_func_t func) 152 - { 153 - spin_lock(&syncobj->lock); 154 - drm_syncobj_add_callback_locked(syncobj, cb, func); 155 - spin_unlock(&syncobj->lock); 156 - } 157 - 158 - void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, 159 - struct drm_syncobj_cb *cb) 160 - { 161 - spin_lock(&syncobj->lock); 162 - list_del_init(&cb->node); 163 - spin_unlock(&syncobj->lock); 164 - } 165 - 166 - static void drm_syncobj_init(struct drm_syncobj *syncobj) 167 - { 168 - spin_lock(&syncobj->lock); 169 - syncobj->timeline_context = dma_fence_context_alloc(1); 170 - syncobj->timeline = 0; 171 - syncobj->signal_point = 0; 172 - init_waitqueue_head(&syncobj->wq); 173 - 174 - INIT_LIST_HEAD(&syncobj->signal_pt_list); 175 - spin_unlock(&syncobj->lock); 176 - } 177 - 178 - static void drm_syncobj_fini(struct drm_syncobj *syncobj) 179 - { 180 - struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp; 181 - 182 - spin_lock(&syncobj->lock); 183 - list_for_each_entry_safe(signal_pt, tmp, 184 - &syncobj->signal_pt_list, list) { 185 - list_del(&signal_pt->list); 186 - dma_fence_put(&signal_pt->fence_array->base); 187 - kfree(signal_pt); 188 - } 189 - spin_unlock(&syncobj->lock); 190 - } 191 - 192 109 static struct dma_fence 193 110 *drm_syncobj_find_signal_pt_for_point(struct drm_syncobj *syncobj, 194 111 uint64_t point) ··· 142 225 return NULL; 143 226 } 144 227 228 + static void drm_syncobj_add_callback_locked(struct drm_syncobj *syncobj, 229 + struct drm_syncobj_cb *cb, 230 + drm_syncobj_func_t func) 231 + { 232 + cb->func = func; 233 + list_add_tail(&cb->node, &syncobj->cb_list); 234 + } 235 + 236 + static void drm_syncobj_fence_get_or_add_callback(struct drm_syncobj *syncobj, 237 + struct dma_fence **fence, 238 + struct drm_syncobj_cb *cb, 239 + drm_syncobj_func_t func) 240 + { 241 + u64 pt_value = 0; 242 + 243 + if (syncobj->type == DRM_SYNCOBJ_TYPE_BINARY) { 244 + /*BINARY syncobj always wait on last pt */ 245 + pt_value = syncobj->signal_point; 246 + 247 + if (pt_value == 0) 248 + pt_value += DRM_SYNCOBJ_BINARY_POINT; 249 + } 250 + 251 + mutex_lock(&syncobj->cb_mutex); 252 + spin_lock(&syncobj->pt_lock); 253 + *fence = drm_syncobj_find_signal_pt_for_point(syncobj, pt_value); 254 + spin_unlock(&syncobj->pt_lock); 255 + if (!*fence) 256 + drm_syncobj_add_callback_locked(syncobj, cb, func); 257 + mutex_unlock(&syncobj->cb_mutex); 258 + } 259 + 260 + void drm_syncobj_add_callback(struct drm_syncobj *syncobj, 261 + struct drm_syncobj_cb *cb, 262 + drm_syncobj_func_t func) 263 + { 264 + mutex_lock(&syncobj->cb_mutex); 265 + drm_syncobj_add_callback_locked(syncobj, cb, func); 266 + mutex_unlock(&syncobj->cb_mutex); 267 + } 268 + 269 + void drm_syncobj_remove_callback(struct drm_syncobj *syncobj, 270 + struct drm_syncobj_cb *cb) 271 + { 272 + mutex_lock(&syncobj->cb_mutex); 273 + list_del_init(&cb->node); 274 + mutex_unlock(&syncobj->cb_mutex); 275 + } 276 + 277 + static void drm_syncobj_init(struct drm_syncobj *syncobj) 278 + { 279 + spin_lock(&syncobj->pt_lock); 280 + syncobj->timeline_context = dma_fence_context_alloc(1); 281 + syncobj->timeline = 0; 282 + syncobj->signal_point = 0; 283 + init_waitqueue_head(&syncobj->wq); 284 + 285 + INIT_LIST_HEAD(&syncobj->signal_pt_list); 286 + spin_unlock(&syncobj->pt_lock); 287 + } 288 + 289 + static void drm_syncobj_fini(struct drm_syncobj *syncobj) 290 + { 291 + struct drm_syncobj_signal_pt *signal_pt = NULL, *tmp; 292 + 293 + spin_lock(&syncobj->pt_lock); 294 + list_for_each_entry_safe(signal_pt, tmp, 295 + &syncobj->signal_pt_list, list) { 296 + list_del(&signal_pt->list); 297 + dma_fence_put(&signal_pt->fence_array->base); 298 + kfree(signal_pt); 299 + } 300 + spin_unlock(&syncobj->pt_lock); 301 + } 302 + 145 303 static int drm_syncobj_create_signal_pt(struct drm_syncobj *syncobj, 146 304 struct dma_fence *fence, 147 305 u64 point) ··· 241 249 fences[num_fences++] = dma_fence_get(fence); 242 250 /* timeline syncobj must take this dependency */ 243 251 if (syncobj->type == DRM_SYNCOBJ_TYPE_TIMELINE) { 244 - spin_lock(&syncobj->lock); 252 + spin_lock(&syncobj->pt_lock); 245 253 if (!list_empty(&syncobj->signal_pt_list)) { 246 254 tail_pt = list_last_entry(&syncobj->signal_pt_list, 247 255 struct drm_syncobj_signal_pt, list); 248 256 fences[num_fences++] = 249 257 dma_fence_get(&tail_pt->fence_array->base); 250 258 } 251 - spin_unlock(&syncobj->lock); 259 + spin_unlock(&syncobj->pt_lock); 252 260 } 253 261 signal_pt->fence_array = dma_fence_array_create(num_fences, fences, 254 262 syncobj->timeline_context, ··· 258 266 goto fail; 259 267 } 260 268 261 - spin_lock(&syncobj->lock); 269 + spin_lock(&syncobj->pt_lock); 262 270 if (syncobj->signal_point >= point) { 263 271 DRM_WARN("A later signal is ready!"); 264 - spin_unlock(&syncobj->lock); 272 + spin_unlock(&syncobj->pt_lock); 265 273 goto exist; 266 274 } 267 275 signal_pt->value = point; 268 276 list_add_tail(&signal_pt->list, &syncobj->signal_pt_list); 269 277 syncobj->signal_point = point; 270 - spin_unlock(&syncobj->lock); 278 + spin_unlock(&syncobj->pt_lock); 271 279 wake_up_all(&syncobj->wq); 272 280 273 281 return 0; ··· 286 294 { 287 295 struct drm_syncobj_signal_pt *signal_pt, *tmp, *tail_pt; 288 296 289 - spin_lock(&syncobj->lock); 297 + spin_lock(&syncobj->pt_lock); 290 298 tail_pt = list_last_entry(&syncobj->signal_pt_list, 291 299 struct drm_syncobj_signal_pt, 292 300 list); ··· 307 315 } 308 316 } 309 317 310 - spin_unlock(&syncobj->lock); 318 + spin_unlock(&syncobj->pt_lock); 311 319 } 312 320 /** 313 321 * drm_syncobj_replace_fence - replace fence in a sync object. ··· 336 344 drm_syncobj_create_signal_pt(syncobj, fence, pt_value); 337 345 if (fence) { 338 346 struct drm_syncobj_cb *cur, *tmp; 347 + LIST_HEAD(cb_list); 339 348 340 - spin_lock(&syncobj->lock); 349 + mutex_lock(&syncobj->cb_mutex); 341 350 list_for_each_entry_safe(cur, tmp, &syncobj->cb_list, node) { 342 351 list_del_init(&cur->node); 343 352 cur->func(syncobj, cur); 344 353 } 345 - spin_unlock(&syncobj->lock); 354 + mutex_unlock(&syncobj->cb_mutex); 346 355 } 347 356 } 348 357 EXPORT_SYMBOL(drm_syncobj_replace_fence); ··· 379 386 if (ret < 0) 380 387 return ret; 381 388 } 382 - spin_lock(&syncobj->lock); 389 + spin_lock(&syncobj->pt_lock); 383 390 *fence = drm_syncobj_find_signal_pt_for_point(syncobj, point); 384 391 if (!*fence) 385 392 ret = -EINVAL; 386 - spin_unlock(&syncobj->lock); 393 + spin_unlock(&syncobj->pt_lock); 387 394 return ret; 388 395 } 389 396 ··· 493 500 494 501 kref_init(&syncobj->refcount); 495 502 INIT_LIST_HEAD(&syncobj->cb_list); 496 - spin_lock_init(&syncobj->lock); 503 + spin_lock_init(&syncobj->pt_lock); 504 + mutex_init(&syncobj->cb_mutex); 497 505 if (flags & DRM_SYNCOBJ_CREATE_TYPE_TIMELINE) 498 506 syncobj->type = DRM_SYNCOBJ_TYPE_TIMELINE; 499 507 else
+6 -2
include/drm/drm_syncobj.h
··· 75 75 */ 76 76 struct list_head cb_list; 77 77 /** 78 - * @lock: Protects syncobj list and write-locks &fence. 78 + * @pt_lock: Protects pt list. 79 79 */ 80 - spinlock_t lock; 80 + spinlock_t pt_lock; 81 + /** 82 + * @cb_mutex: Protects syncobj cb list. 83 + */ 84 + struct mutex cb_mutex; 81 85 /** 82 86 * @file: A file backing for this syncobj. 83 87 */