aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_crtc.c
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2012-12-10 15:19:18 -0500
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-01-20 16:16:58 -0500
commit4b096ac10da0b63f09bd123b86fed8deb80646ce (patch)
tree3d252a1b2ed182beb769796fb99eb68693176bfa /drivers/gpu/drm/drm_crtc.c
parentdac35663cef4ca7f572d430bb54b14be8f03cb10 (diff)
drm: revamp locking around fb creation/destruction
Well, at least step 1. The goal here is that framebuffer objects can survive outside of the mode_config lock, with just a reference held as protection. The first step to get there is to introduce a special fb_lock which protects fb lookup, creation and destruction, to make them appear atomic. This new fb_lock can nest within the mode_config lock. But the idea is (once the reference counting part is completed) that we only quickly take that fb_lock to lookup a framebuffer and grab a reference, without any other locks involved. vmwgfx is the only driver which does framebuffer lookups itself, also wrap those calls to drm_mode_object_find with the new lock. Also protect the fb_list walking in i915 and omapdrm with the new lock. As a slight complication there's also the list of user-created fbs attached to the file private. The problem now is that at fclose() time we need to walk that list, eventually do a modeset call to remove the fb from active usage (and are required to be able to take the mode_config lock), but in the end we need to grab the new fb_lock to remove the fb from the list. The easiest solution is to add another mutex to protect this per-file list. Currently that new fbs_lock nests within the modeset locks and so appears redudant. But later patches will switch around this sequence so that taking the modeset locks in the fb destruction path is optional in the fastpath. Ultimately the goal is that addfb and rmfb do not require the mode_config lock, since otherwise they have the potential to introduce stalls in the pageflip sequence of a compositor (if the compositor e.g. switches to a fullscreen client or if it enables a plane). But that requires a few more steps and hoops to jump through. Note that framebuffer creation/destruction is now double-protected - once by the fb_lock and in parts by the idr_lock. The later would be unnecessariy if framebuffers would have their own idr allocator. But that's material for another patch (series). v2: Properly initialize the fb->filp_head list in _init, otherwise the newly added WARN to check whether the fb isn't on a fpriv list any more will fail for driver-private objects. v3: Fixup two error-case unlock bugs spotted by Richard Wilbur. Reviewed-by: Rob Clark <rob@ti.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Diffstat (limited to 'drivers/gpu/drm/drm_crtc.c')
-rw-r--r--drivers/gpu/drm/drm_crtc.c116
1 files changed, 81 insertions, 35 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 4af6a3d5c9a1..13a3d3426961 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -262,15 +262,21 @@ again:
262 262
263 mutex_lock(&dev->mode_config.idr_mutex); 263 mutex_lock(&dev->mode_config.idr_mutex);
264 ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id); 264 ret = idr_get_new_above(&dev->mode_config.crtc_idr, obj, 1, &new_id);
265
266 if (!ret) {
267 /*
268 * Set up the object linking under the protection of the idr
269 * lock so that other users can't see inconsistent state.
270 */
271 obj->id = new_id;
272 obj->type = obj_type;
273 }
265 mutex_unlock(&dev->mode_config.idr_mutex); 274 mutex_unlock(&dev->mode_config.idr_mutex);
275
266 if (ret == -EAGAIN) 276 if (ret == -EAGAIN)
267 goto again; 277 goto again;
268 else if (ret)
269 return ret;
270 278
271 obj->id = new_id; 279 return ret;
272 obj->type = obj_type;
273 return 0;
274} 280}
275 281
276/** 282/**
@@ -312,6 +318,12 @@ EXPORT_SYMBOL(drm_mode_object_find);
312 * Allocates an ID for the framebuffer's parent mode object, sets its mode 318 * Allocates an ID for the framebuffer's parent mode object, sets its mode
313 * functions & device file and adds it to the master fd list. 319 * functions & device file and adds it to the master fd list.
314 * 320 *
321 * IMPORTANT:
322 * This functions publishes the fb and makes it available for concurrent access
323 * by other users. Which means by this point the fb _must_ be fully set up -
324 * since all the fb attributes are invariant over its lifetime, no further
325 * locking but only correct reference counting is required.
326 *
315 * RETURNS: 327 * RETURNS:
316 * Zero on success, error code on failure. 328 * Zero on success, error code on failure.
317 */ 329 */
@@ -320,16 +332,20 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
320{ 332{
321 int ret; 333 int ret;
322 334
335 mutex_lock(&dev->mode_config.fb_lock);
323 kref_init(&fb->refcount); 336 kref_init(&fb->refcount);
337 INIT_LIST_HEAD(&fb->filp_head);
338 fb->dev = dev;
339 fb->funcs = funcs;
324 340
325 ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); 341 ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
326 if (ret) 342 if (ret)
327 return ret; 343 goto out;
328 344
329 fb->dev = dev;
330 fb->funcs = funcs;
331 dev->mode_config.num_fb++; 345 dev->mode_config.num_fb++;
332 list_add(&fb->head, &dev->mode_config.fb_list); 346 list_add(&fb->head, &dev->mode_config.fb_list);
347out:
348 mutex_unlock(&dev->mode_config.fb_lock);
333 349
334 return 0; 350 return 0;
335} 351}
@@ -385,8 +401,10 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
385 * this.) 401 * this.)
386 */ 402 */
387 drm_mode_object_put(dev, &fb->base); 403 drm_mode_object_put(dev, &fb->base);
404 mutex_lock(&dev->mode_config.fb_lock);
388 list_del(&fb->head); 405 list_del(&fb->head);
389 dev->mode_config.num_fb--; 406 dev->mode_config.num_fb--;
407 mutex_unlock(&dev->mode_config.fb_lock);
390} 408}
391EXPORT_SYMBOL(drm_framebuffer_cleanup); 409EXPORT_SYMBOL(drm_framebuffer_cleanup);
392 410
@@ -406,6 +424,7 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
406 int ret; 424 int ret;
407 425
408 WARN_ON(!drm_modeset_is_locked(dev)); 426 WARN_ON(!drm_modeset_is_locked(dev));
427 WARN_ON(!list_empty(&fb->filp_head));
409 428
410 /* remove from any CRTC */ 429 /* remove from any CRTC */
411 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 430 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -432,8 +451,6 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
432 } 451 }
433 } 452 }
434 453
435 list_del(&fb->filp_head);
436
437 drm_framebuffer_unreference(fb); 454 drm_framebuffer_unreference(fb);
438} 455}
439EXPORT_SYMBOL(drm_framebuffer_remove); 456EXPORT_SYMBOL(drm_framebuffer_remove);
@@ -989,6 +1006,7 @@ void drm_mode_config_init(struct drm_device *dev)
989{ 1006{
990 mutex_init(&dev->mode_config.mutex); 1007 mutex_init(&dev->mode_config.mutex);
991 mutex_init(&dev->mode_config.idr_mutex); 1008 mutex_init(&dev->mode_config.idr_mutex);
1009 mutex_init(&dev->mode_config.fb_lock);
992 INIT_LIST_HEAD(&dev->mode_config.fb_list); 1010 INIT_LIST_HEAD(&dev->mode_config.fb_list);
993 INIT_LIST_HEAD(&dev->mode_config.crtc_list); 1011 INIT_LIST_HEAD(&dev->mode_config.crtc_list);
994 INIT_LIST_HEAD(&dev->mode_config.connector_list); 1012 INIT_LIST_HEAD(&dev->mode_config.connector_list);
@@ -1091,6 +1109,9 @@ void drm_mode_config_cleanup(struct drm_device *dev)
1091 drm_property_destroy(dev, property); 1109 drm_property_destroy(dev, property);
1092 } 1110 }
1093 1111
1112 /* Single-threaded teardown context, so it's not requied to grab the
1113 * fb_lock to protect against concurrent fb_list access. Contrary, it
1114 * would actually deadlock with the drm_framebuffer_cleanup function. */
1094 list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 1115 list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
1095 drm_framebuffer_remove(fb); 1116 drm_framebuffer_remove(fb);
1096 } 1117 }
@@ -1220,8 +1241,8 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
1220 if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1241 if (!drm_core_check_feature(dev, DRIVER_MODESET))
1221 return -EINVAL; 1242 return -EINVAL;
1222 1243
1223 drm_modeset_lock_all(dev);
1224 1244
1245 mutex_lock(&file_priv->fbs_lock);
1225 /* 1246 /*
1226 * For the non-control nodes we need to limit the list of resources 1247 * For the non-control nodes we need to limit the list of resources
1227 * by IDs in the group list for this node 1248 * by IDs in the group list for this node
@@ -1229,6 +1250,23 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
1229 list_for_each(lh, &file_priv->fbs) 1250 list_for_each(lh, &file_priv->fbs)
1230 fb_count++; 1251 fb_count++;
1231 1252
1253 /* handle this in 4 parts */
1254 /* FBs */
1255 if (card_res->count_fbs >= fb_count) {
1256 copied = 0;
1257 fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
1258 list_for_each_entry(fb, &file_priv->fbs, filp_head) {
1259 if (put_user(fb->base.id, fb_id + copied)) {
1260 mutex_unlock(&file_priv->fbs_lock);
1261 return -EFAULT;
1262 }
1263 copied++;
1264 }
1265 }
1266 card_res->count_fbs = fb_count;
1267 mutex_unlock(&file_priv->fbs_lock);
1268
1269 drm_modeset_lock_all(dev);
1232 mode_group = &file_priv->master->minor->mode_group; 1270 mode_group = &file_priv->master->minor->mode_group;
1233 if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { 1271 if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
1234 1272
@@ -1252,21 +1290,6 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
1252 card_res->max_width = dev->mode_config.max_width; 1290 card_res->max_width = dev->mode_config.max_width;
1253 card_res->min_width = dev->mode_config.min_width; 1291 card_res->min_width = dev->mode_config.min_width;
1254 1292
1255 /* handle this in 4 parts */
1256 /* FBs */
1257 if (card_res->count_fbs >= fb_count) {
1258 copied = 0;
1259 fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr;
1260 list_for_each_entry(fb, &file_priv->fbs, filp_head) {
1261 if (put_user(fb->base.id, fb_id + copied)) {
1262 ret = -EFAULT;
1263 goto out;
1264 }
1265 copied++;
1266 }
1267 }
1268 card_res->count_fbs = fb_count;
1269
1270 /* CRTCs */ 1293 /* CRTCs */
1271 if (card_res->count_crtcs >= crtc_count) { 1294 if (card_res->count_crtcs >= crtc_count) {
1272 copied = 0; 1295 copied = 0;
@@ -1765,8 +1788,10 @@ int drm_mode_setplane(struct drm_device *dev, void *data,
1765 } 1788 }
1766 crtc = obj_to_crtc(obj); 1789 crtc = obj_to_crtc(obj);
1767 1790
1791 mutex_lock(&dev->mode_config.fb_lock);
1768 obj = drm_mode_object_find(dev, plane_req->fb_id, 1792 obj = drm_mode_object_find(dev, plane_req->fb_id,
1769 DRM_MODE_OBJECT_FB); 1793 DRM_MODE_OBJECT_FB);
1794 mutex_unlock(&dev->mode_config.fb_lock);
1770 if (!obj) { 1795 if (!obj) {
1771 DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 1796 DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
1772 plane_req->fb_id); 1797 plane_req->fb_id);
@@ -1908,8 +1933,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
1908 } 1933 }
1909 fb = crtc->fb; 1934 fb = crtc->fb;
1910 } else { 1935 } else {
1936 mutex_lock(&dev->mode_config.fb_lock);
1911 obj = drm_mode_object_find(dev, crtc_req->fb_id, 1937 obj = drm_mode_object_find(dev, crtc_req->fb_id,
1912 DRM_MODE_OBJECT_FB); 1938 DRM_MODE_OBJECT_FB);
1939 mutex_unlock(&dev->mode_config.fb_lock);
1913 if (!obj) { 1940 if (!obj) {
1914 DRM_DEBUG_KMS("Unknown FB ID%d\n", 1941 DRM_DEBUG_KMS("Unknown FB ID%d\n",
1915 crtc_req->fb_id); 1942 crtc_req->fb_id);
@@ -2151,16 +2178,17 @@ int drm_mode_addfb(struct drm_device *dev,
2151 fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); 2178 fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);
2152 if (IS_ERR(fb)) { 2179 if (IS_ERR(fb)) {
2153 DRM_DEBUG_KMS("could not create framebuffer\n"); 2180 DRM_DEBUG_KMS("could not create framebuffer\n");
2154 ret = PTR_ERR(fb); 2181 drm_modeset_unlock_all(dev);
2155 goto out; 2182 return PTR_ERR(fb);
2156 } 2183 }
2157 2184
2185 mutex_lock(&file_priv->fbs_lock);
2158 or->fb_id = fb->base.id; 2186 or->fb_id = fb->base.id;
2159 list_add(&fb->filp_head, &file_priv->fbs); 2187 list_add(&fb->filp_head, &file_priv->fbs);
2160 DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 2188 DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
2161 2189 mutex_unlock(&file_priv->fbs_lock);
2162out:
2163 drm_modeset_unlock_all(dev); 2190 drm_modeset_unlock_all(dev);
2191
2164 return ret; 2192 return ret;
2165} 2193}
2166 2194
@@ -2333,16 +2361,18 @@ int drm_mode_addfb2(struct drm_device *dev,
2333 fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); 2361 fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
2334 if (IS_ERR(fb)) { 2362 if (IS_ERR(fb)) {
2335 DRM_DEBUG_KMS("could not create framebuffer\n"); 2363 DRM_DEBUG_KMS("could not create framebuffer\n");
2336 ret = PTR_ERR(fb); 2364 drm_modeset_unlock_all(dev);
2337 goto out; 2365 return PTR_ERR(fb);
2338 } 2366 }
2339 2367
2368 mutex_lock(&file_priv->fbs_lock);
2340 r->fb_id = fb->base.id; 2369 r->fb_id = fb->base.id;
2341 list_add(&fb->filp_head, &file_priv->fbs); 2370 list_add(&fb->filp_head, &file_priv->fbs);
2342 DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 2371 DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
2372 mutex_unlock(&file_priv->fbs_lock);
2343 2373
2344out:
2345 drm_modeset_unlock_all(dev); 2374 drm_modeset_unlock_all(dev);
2375
2346 return ret; 2376 return ret;
2347} 2377}
2348 2378
@@ -2373,27 +2403,34 @@ int drm_mode_rmfb(struct drm_device *dev,
2373 return -EINVAL; 2403 return -EINVAL;
2374 2404
2375 drm_modeset_lock_all(dev); 2405 drm_modeset_lock_all(dev);
2406 mutex_lock(&dev->mode_config.fb_lock);
2376 obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB); 2407 obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
2377 /* TODO check that we really get a framebuffer back. */ 2408 /* TODO check that we really get a framebuffer back. */
2378 if (!obj) { 2409 if (!obj) {
2410 mutex_unlock(&dev->mode_config.fb_lock);
2379 ret = -EINVAL; 2411 ret = -EINVAL;
2380 goto out; 2412 goto out;
2381 } 2413 }
2382 fb = obj_to_fb(obj); 2414 fb = obj_to_fb(obj);
2415 mutex_unlock(&dev->mode_config.fb_lock);
2383 2416
2417 mutex_lock(&file_priv->fbs_lock);
2384 list_for_each_entry(fbl, &file_priv->fbs, filp_head) 2418 list_for_each_entry(fbl, &file_priv->fbs, filp_head)
2385 if (fb == fbl) 2419 if (fb == fbl)
2386 found = 1; 2420 found = 1;
2387
2388 if (!found) { 2421 if (!found) {
2389 ret = -EINVAL; 2422 ret = -EINVAL;
2423 mutex_unlock(&file_priv->fbs_lock);
2390 goto out; 2424 goto out;
2391 } 2425 }
2392 2426
2393 drm_framebuffer_remove(fb); 2427 list_del_init(&fb->filp_head);
2428 mutex_unlock(&file_priv->fbs_lock);
2394 2429
2430 drm_framebuffer_remove(fb);
2395out: 2431out:
2396 drm_modeset_unlock_all(dev); 2432 drm_modeset_unlock_all(dev);
2433
2397 return ret; 2434 return ret;
2398} 2435}
2399 2436
@@ -2422,7 +2459,9 @@ int drm_mode_getfb(struct drm_device *dev,
2422 return -EINVAL; 2459 return -EINVAL;
2423 2460
2424 drm_modeset_lock_all(dev); 2461 drm_modeset_lock_all(dev);
2462 mutex_lock(&dev->mode_config.fb_lock);
2425 obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); 2463 obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
2464 mutex_unlock(&dev->mode_config.fb_lock);
2426 if (!obj) { 2465 if (!obj) {
2427 ret = -EINVAL; 2466 ret = -EINVAL;
2428 goto out; 2467 goto out;
@@ -2460,7 +2499,9 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
2460 return -EINVAL; 2499 return -EINVAL;
2461 2500
2462 drm_modeset_lock_all(dev); 2501 drm_modeset_lock_all(dev);
2502 mutex_lock(&dev->mode_config.fb_lock);
2463 obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB); 2503 obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
2504 mutex_unlock(&dev->mode_config.fb_lock);
2464 if (!obj) { 2505 if (!obj) {
2465 ret = -EINVAL; 2506 ret = -EINVAL;
2466 goto out_err1; 2507 goto out_err1;
@@ -2535,9 +2576,12 @@ void drm_fb_release(struct drm_file *priv)
2535 struct drm_framebuffer *fb, *tfb; 2576 struct drm_framebuffer *fb, *tfb;
2536 2577
2537 drm_modeset_lock_all(dev); 2578 drm_modeset_lock_all(dev);
2579 mutex_lock(&priv->fbs_lock);
2538 list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 2580 list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
2581 list_del_init(&fb->filp_head);
2539 drm_framebuffer_remove(fb); 2582 drm_framebuffer_remove(fb);
2540 } 2583 }
2584 mutex_unlock(&priv->fbs_lock);
2541 drm_modeset_unlock_all(dev); 2585 drm_modeset_unlock_all(dev);
2542} 2586}
2543 2587
@@ -3542,7 +3586,9 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
3542 if (crtc->funcs->page_flip == NULL) 3586 if (crtc->funcs->page_flip == NULL)
3543 goto out; 3587 goto out;
3544 3588
3589 mutex_lock(&dev->mode_config.fb_lock);
3545 obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB); 3590 obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB);
3591 mutex_unlock(&dev->mode_config.fb_lock);
3546 if (!obj) 3592 if (!obj)
3547 goto out; 3593 goto out;
3548 fb = obj_to_fb(obj); 3594 fb = obj_to_fb(obj);