aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/gpu/drm/drm_crtc.c116
-rw-r--r--drivers/gpu/drm/drm_fops.c1
-rw-r--r--drivers/gpu/drm/i915/i915_debugfs.c5
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c4
-rw-r--r--drivers/staging/omapdrm/omap_debugfs.c2
-rw-r--r--include/drm/drmP.h8
-rw-r--r--include/drm/drm_crtc.h14
7 files changed, 113 insertions, 37 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) {