diff options
-rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 118 |
1 files changed, 80 insertions, 38 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 3eddfabeba96..a50f7553b31d 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c | |||
@@ -356,6 +356,9 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, | |||
356 | if (ret) | 356 | if (ret) |
357 | goto out; | 357 | goto out; |
358 | 358 | ||
359 | /* Grab the idr reference. */ | ||
360 | drm_framebuffer_reference(fb); | ||
361 | |||
359 | dev->mode_config.num_fb++; | 362 | dev->mode_config.num_fb++; |
360 | list_add(&fb->head, &dev->mode_config.fb_list); | 363 | list_add(&fb->head, &dev->mode_config.fb_list); |
361 | out: | 364 | out: |
@@ -372,6 +375,23 @@ static void drm_framebuffer_free(struct kref *kref) | |||
372 | fb->funcs->destroy(fb); | 375 | fb->funcs->destroy(fb); |
373 | } | 376 | } |
374 | 377 | ||
378 | static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, | ||
379 | uint32_t id) | ||
380 | { | ||
381 | struct drm_mode_object *obj = NULL; | ||
382 | struct drm_framebuffer *fb; | ||
383 | |||
384 | mutex_lock(&dev->mode_config.idr_mutex); | ||
385 | obj = idr_find(&dev->mode_config.crtc_idr, id); | ||
386 | if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) | ||
387 | fb = NULL; | ||
388 | else | ||
389 | fb = obj_to_fb(obj); | ||
390 | mutex_unlock(&dev->mode_config.idr_mutex); | ||
391 | |||
392 | return fb; | ||
393 | } | ||
394 | |||
375 | /** | 395 | /** |
376 | * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference | 396 | * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference |
377 | * @dev: drm device | 397 | * @dev: drm device |
@@ -384,22 +404,12 @@ static void drm_framebuffer_free(struct kref *kref) | |||
384 | struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, | 404 | struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, |
385 | uint32_t id) | 405 | uint32_t id) |
386 | { | 406 | { |
387 | struct drm_mode_object *obj = NULL; | ||
388 | struct drm_framebuffer *fb; | 407 | struct drm_framebuffer *fb; |
389 | 408 | ||
390 | mutex_lock(&dev->mode_config.fb_lock); | 409 | mutex_lock(&dev->mode_config.fb_lock); |
391 | 410 | fb = __drm_framebuffer_lookup(dev, id); | |
392 | mutex_lock(&dev->mode_config.idr_mutex); | ||
393 | obj = idr_find(&dev->mode_config.crtc_idr, id); | ||
394 | if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) | ||
395 | fb = NULL; | ||
396 | else | ||
397 | fb = obj_to_fb(obj); | ||
398 | mutex_unlock(&dev->mode_config.idr_mutex); | ||
399 | |||
400 | if (fb) | 411 | if (fb) |
401 | kref_get(&fb->refcount); | 412 | kref_get(&fb->refcount); |
402 | |||
403 | mutex_unlock(&dev->mode_config.fb_lock); | 413 | mutex_unlock(&dev->mode_config.fb_lock); |
404 | 414 | ||
405 | return fb; | 415 | return fb; |
@@ -430,6 +440,24 @@ void drm_framebuffer_reference(struct drm_framebuffer *fb) | |||
430 | } | 440 | } |
431 | EXPORT_SYMBOL(drm_framebuffer_reference); | 441 | EXPORT_SYMBOL(drm_framebuffer_reference); |
432 | 442 | ||
443 | static void drm_framebuffer_free_bug(struct kref *kref) | ||
444 | { | ||
445 | BUG(); | ||
446 | } | ||
447 | |||
448 | /* dev->mode_config.fb_lock must be held! */ | ||
449 | static void __drm_framebuffer_unregister(struct drm_device *dev, | ||
450 | struct drm_framebuffer *fb) | ||
451 | { | ||
452 | mutex_lock(&dev->mode_config.idr_mutex); | ||
453 | idr_remove(&dev->mode_config.crtc_idr, fb->base.id); | ||
454 | mutex_unlock(&dev->mode_config.idr_mutex); | ||
455 | |||
456 | fb->base.id = 0; | ||
457 | |||
458 | kref_put(&fb->refcount, drm_framebuffer_free_bug); | ||
459 | } | ||
460 | |||
433 | /** | 461 | /** |
434 | * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr | 462 | * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr |
435 | * @fb: fb to unregister | 463 | * @fb: fb to unregister |
@@ -441,6 +469,12 @@ EXPORT_SYMBOL(drm_framebuffer_reference); | |||
441 | */ | 469 | */ |
442 | void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) | 470 | void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) |
443 | { | 471 | { |
472 | struct drm_device *dev = fb->dev; | ||
473 | |||
474 | mutex_lock(&dev->mode_config.fb_lock); | ||
475 | /* Mark fb as reaped and drop idr ref. */ | ||
476 | __drm_framebuffer_unregister(dev, fb); | ||
477 | mutex_unlock(&dev->mode_config.fb_lock); | ||
444 | } | 478 | } |
445 | EXPORT_SYMBOL(drm_framebuffer_unregister_private); | 479 | EXPORT_SYMBOL(drm_framebuffer_unregister_private); |
446 | 480 | ||
@@ -464,14 +498,6 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) | |||
464 | { | 498 | { |
465 | struct drm_device *dev = fb->dev; | 499 | struct drm_device *dev = fb->dev; |
466 | 500 | ||
467 | /* | ||
468 | * This could be moved to drm_framebuffer_remove(), but for | ||
469 | * debugging is nice to keep around the list of fb's that are | ||
470 | * no longer associated w/ a drm_file but are not unreferenced | ||
471 | * yet. (i915 and omapdrm have debugfs files which will show | ||
472 | * this.) | ||
473 | */ | ||
474 | drm_mode_object_put(dev, &fb->base); | ||
475 | mutex_lock(&dev->mode_config.fb_lock); | 501 | mutex_lock(&dev->mode_config.fb_lock); |
476 | list_del(&fb->head); | 502 | list_del(&fb->head); |
477 | dev->mode_config.num_fb--; | 503 | dev->mode_config.num_fb--; |
@@ -1181,9 +1207,15 @@ void drm_mode_config_cleanup(struct drm_device *dev) | |||
1181 | drm_property_destroy(dev, property); | 1207 | drm_property_destroy(dev, property); |
1182 | } | 1208 | } |
1183 | 1209 | ||
1184 | /* Single-threaded teardown context, so it's not requied to grab the | 1210 | /* |
1211 | * Single-threaded teardown context, so it's not required to grab the | ||
1185 | * fb_lock to protect against concurrent fb_list access. Contrary, it | 1212 | * fb_lock to protect against concurrent fb_list access. Contrary, it |
1186 | * would actually deadlock with the drm_framebuffer_cleanup function. */ | 1213 | * would actually deadlock with the drm_framebuffer_cleanup function. |
1214 | * | ||
1215 | * Also, if there are any framebuffers left, that's a driver leak now, | ||
1216 | * so politely WARN about this. | ||
1217 | */ | ||
1218 | WARN_ON(!list_empty(&dev->mode_config.fb_list)); | ||
1187 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { | 1219 | list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { |
1188 | drm_framebuffer_remove(fb); | 1220 | drm_framebuffer_remove(fb); |
1189 | } | 1221 | } |
@@ -2464,39 +2496,41 @@ int drm_mode_rmfb(struct drm_device *dev, | |||
2464 | struct drm_framebuffer *fb = NULL; | 2496 | struct drm_framebuffer *fb = NULL; |
2465 | struct drm_framebuffer *fbl = NULL; | 2497 | struct drm_framebuffer *fbl = NULL; |
2466 | uint32_t *id = data; | 2498 | uint32_t *id = data; |
2467 | int ret = 0; | ||
2468 | int found = 0; | 2499 | int found = 0; |
2469 | 2500 | ||
2470 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) | 2501 | if (!drm_core_check_feature(dev, DRIVER_MODESET)) |
2471 | return -EINVAL; | 2502 | return -EINVAL; |
2472 | 2503 | ||
2473 | drm_modeset_lock_all(dev); | ||
2474 | fb = drm_framebuffer_lookup(dev, *id); | ||
2475 | if (!fb) { | ||
2476 | ret = -EINVAL; | ||
2477 | goto out; | ||
2478 | } | ||
2479 | /* fb is protect by the mode_config lock, so drop the ref immediately */ | ||
2480 | drm_framebuffer_unreference(fb); | ||
2481 | |||
2482 | mutex_lock(&file_priv->fbs_lock); | 2504 | mutex_lock(&file_priv->fbs_lock); |
2505 | mutex_lock(&dev->mode_config.fb_lock); | ||
2506 | fb = __drm_framebuffer_lookup(dev, *id); | ||
2507 | if (!fb) | ||
2508 | goto fail_lookup; | ||
2509 | |||
2483 | list_for_each_entry(fbl, &file_priv->fbs, filp_head) | 2510 | list_for_each_entry(fbl, &file_priv->fbs, filp_head) |
2484 | if (fb == fbl) | 2511 | if (fb == fbl) |
2485 | found = 1; | 2512 | found = 1; |
2486 | if (!found) { | 2513 | if (!found) |
2487 | ret = -EINVAL; | 2514 | goto fail_lookup; |
2488 | mutex_unlock(&file_priv->fbs_lock); | 2515 | |
2489 | goto out; | 2516 | /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ |
2490 | } | 2517 | __drm_framebuffer_unregister(dev, fb); |
2491 | 2518 | ||
2492 | list_del_init(&fb->filp_head); | 2519 | list_del_init(&fb->filp_head); |
2520 | mutex_unlock(&dev->mode_config.fb_lock); | ||
2493 | mutex_unlock(&file_priv->fbs_lock); | 2521 | mutex_unlock(&file_priv->fbs_lock); |
2494 | 2522 | ||
2523 | drm_modeset_lock_all(dev); | ||
2495 | drm_framebuffer_remove(fb); | 2524 | drm_framebuffer_remove(fb); |
2496 | out: | ||
2497 | drm_modeset_unlock_all(dev); | 2525 | drm_modeset_unlock_all(dev); |
2498 | 2526 | ||
2499 | return ret; | 2527 | return 0; |
2528 | |||
2529 | fail_lookup: | ||
2530 | mutex_unlock(&dev->mode_config.fb_lock); | ||
2531 | mutex_unlock(&file_priv->fbs_lock); | ||
2532 | |||
2533 | return -EINVAL; | ||
2500 | } | 2534 | } |
2501 | 2535 | ||
2502 | /** | 2536 | /** |
@@ -2639,7 +2673,15 @@ void drm_fb_release(struct drm_file *priv) | |||
2639 | drm_modeset_lock_all(dev); | 2673 | drm_modeset_lock_all(dev); |
2640 | mutex_lock(&priv->fbs_lock); | 2674 | mutex_lock(&priv->fbs_lock); |
2641 | list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { | 2675 | list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { |
2676 | |||
2677 | mutex_lock(&dev->mode_config.fb_lock); | ||
2678 | /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ | ||
2679 | __drm_framebuffer_unregister(dev, fb); | ||
2680 | mutex_unlock(&dev->mode_config.fb_lock); | ||
2681 | |||
2642 | list_del_init(&fb->filp_head); | 2682 | list_del_init(&fb->filp_head); |
2683 | |||
2684 | /* This will also drop the fpriv->fbs reference. */ | ||
2643 | drm_framebuffer_remove(fb); | 2685 | drm_framebuffer_remove(fb); |
2644 | } | 2686 | } |
2645 | mutex_unlock(&priv->fbs_lock); | 2687 | mutex_unlock(&priv->fbs_lock); |