aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRob Clark <rob@ti.com>2012-09-05 17:48:38 -0400
committerDave Airlie <airlied@redhat.com>2012-10-02 08:15:39 -0400
commitf7eff60ea0e4e35732604e3591e2ff7b3ef41981 (patch)
treea9dd6031479ce9585d7d0af061aa165ed9c5d49b
parent33cce6e9801f7d0184a636e9096a7cf7f8237ff9 (diff)
drm: refcnt drm_framebuffer (v4.1)
This simplifies drm fb lifetime, and if the crtc/plane needs to hold a ref to the fb when disabling a pipe until the next vblank, this avoids the need to make disabling an overlay synchronous. This is a problem that shows up when userspace is using a drm plane to implement a hw cursor.. making overlay disable synchronous causes a performance problem when x11 is rapidly enabling/disabling the hw cursor. But not making it synchronous opens up a race condition for crashing if userspace turns around and immediately deletes the fb. Refcnt'ing the fb makes it possible to solve this problem. v1: original v2: add drm_framebuffer_remove() which is called in all paths where fb->funcs->destroy() was directly called before. This cleans up the CRTCs/planes that the fb was attached to. You should only directly use drm_framebuffer_unreference() if you are also using drm_framebuffer_reference() to keep a ref to the fb. v3: add comment explaining the fb refcount v4: remove duplicate 'list_del(&fb->filp_head)' [airlied: v4.1: fix local rejection] Signed-off-by: Rob Clark <rob@ti.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r--drivers/gpu/drm/drm_crtc.c79
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c4
-rw-r--r--drivers/staging/omapdrm/omap_fbdev.c4
-rw-r--r--include/drm/drm_crtc.h14
4 files changed, 85 insertions, 16 deletions
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index c418c772a7e5..39afe13a5fa2 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -294,6 +294,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
294{ 294{
295 int ret; 295 int ret;
296 296
297 kref_init(&fb->refcount);
298
297 ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); 299 ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
298 if (ret) 300 if (ret)
299 return ret; 301 return ret;
@@ -307,6 +309,38 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
307} 309}
308EXPORT_SYMBOL(drm_framebuffer_init); 310EXPORT_SYMBOL(drm_framebuffer_init);
309 311
312static void drm_framebuffer_free(struct kref *kref)
313{
314 struct drm_framebuffer *fb =
315 container_of(kref, struct drm_framebuffer, refcount);
316 fb->funcs->destroy(fb);
317}
318
319/**
320 * drm_framebuffer_unreference - unref a framebuffer
321 *
322 * LOCKING:
323 * Caller must hold mode config lock.
324 */
325void drm_framebuffer_unreference(struct drm_framebuffer *fb)
326{
327 struct drm_device *dev = fb->dev;
328 DRM_DEBUG("FB ID: %d\n", fb->base.id);
329 WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
330 kref_put(&fb->refcount, drm_framebuffer_free);
331}
332EXPORT_SYMBOL(drm_framebuffer_unreference);
333
334/**
335 * drm_framebuffer_reference - incr the fb refcnt
336 */
337void drm_framebuffer_reference(struct drm_framebuffer *fb)
338{
339 DRM_DEBUG("FB ID: %d\n", fb->base.id);
340 kref_get(&fb->refcount);
341}
342EXPORT_SYMBOL(drm_framebuffer_reference);
343
310/** 344/**
311 * drm_framebuffer_cleanup - remove a framebuffer object 345 * drm_framebuffer_cleanup - remove a framebuffer object
312 * @fb: framebuffer to remove 346 * @fb: framebuffer to remove
@@ -320,6 +354,32 @@ EXPORT_SYMBOL(drm_framebuffer_init);
320void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 354void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
321{ 355{
322 struct drm_device *dev = fb->dev; 356 struct drm_device *dev = fb->dev;
357 /*
358 * This could be moved to drm_framebuffer_remove(), but for
359 * debugging is nice to keep around the list of fb's that are
360 * no longer associated w/ a drm_file but are not unreferenced
361 * yet. (i915 and omapdrm have debugfs files which will show
362 * this.)
363 */
364 drm_mode_object_put(dev, &fb->base);
365 list_del(&fb->head);
366 dev->mode_config.num_fb--;
367}
368EXPORT_SYMBOL(drm_framebuffer_cleanup);
369
370/**
371 * drm_framebuffer_remove - remove and unreference a framebuffer object
372 * @fb: framebuffer to remove
373 *
374 * LOCKING:
375 * Caller must hold mode config lock.
376 *
377 * Scans all the CRTCs and planes in @dev's mode_config. If they're
378 * using @fb, removes it, setting it to NULL.
379 */
380void drm_framebuffer_remove(struct drm_framebuffer *fb)
381{
382 struct drm_device *dev = fb->dev;
323 struct drm_crtc *crtc; 383 struct drm_crtc *crtc;
324 struct drm_plane *plane; 384 struct drm_plane *plane;
325 struct drm_mode_set set; 385 struct drm_mode_set set;
@@ -350,11 +410,11 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
350 } 410 }
351 } 411 }
352 412
353 drm_mode_object_put(dev, &fb->base); 413 list_del(&fb->filp_head);
354 list_del(&fb->head); 414
355 dev->mode_config.num_fb--; 415 drm_framebuffer_unreference(fb);
356} 416}
357EXPORT_SYMBOL(drm_framebuffer_cleanup); 417EXPORT_SYMBOL(drm_framebuffer_remove);
358 418
359/** 419/**
360 * drm_crtc_init - Initialise a new CRTC object 420 * drm_crtc_init - Initialise a new CRTC object
@@ -1031,7 +1091,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
1031 } 1091 }
1032 1092
1033 list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 1093 list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
1034 fb->funcs->destroy(fb); 1094 drm_framebuffer_remove(fb);
1035 } 1095 }
1036 1096
1037 list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, 1097 list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
@@ -2337,11 +2397,7 @@ int drm_mode_rmfb(struct drm_device *dev,
2337 goto out; 2397 goto out;
2338 } 2398 }
2339 2399
2340 /* TODO release all crtc connected to the framebuffer */ 2400 drm_framebuffer_remove(fb);
2341 /* TODO unhock the destructor from the buffer object */
2342
2343 list_del(&fb->filp_head);
2344 fb->funcs->destroy(fb);
2345 2401
2346out: 2402out:
2347 mutex_unlock(&dev->mode_config.mutex); 2403 mutex_unlock(&dev->mode_config.mutex);
@@ -2491,8 +2547,7 @@ void drm_fb_release(struct drm_file *priv)
2491 2547
2492 mutex_lock(&dev->mode_config.mutex); 2548 mutex_lock(&dev->mode_config.mutex);
2493 list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 2549 list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
2494 list_del(&fb->filp_head); 2550 drm_framebuffer_remove(fb);
2495 fb->funcs->destroy(fb);
2496 } 2551 }
2497 mutex_unlock(&dev->mode_config.mutex); 2552 mutex_unlock(&dev->mode_config.mutex);
2498} 2553}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index d5586cc75163..f4ac43356583 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -266,8 +266,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,
266 /* release drm framebuffer and real buffer */ 266 /* release drm framebuffer and real buffer */
267 if (fb_helper->fb && fb_helper->fb->funcs) { 267 if (fb_helper->fb && fb_helper->fb->funcs) {
268 fb = fb_helper->fb; 268 fb = fb_helper->fb;
269 if (fb && fb->funcs->destroy) 269 if (fb)
270 fb->funcs->destroy(fb); 270 drm_framebuffer_remove(fb);
271 } 271 }
272 272
273 /* release linux framebuffer */ 273 /* release linux framebuffer */
diff --git a/drivers/staging/omapdrm/omap_fbdev.c b/drivers/staging/omapdrm/omap_fbdev.c
index 8c6ed3b0c6f6..8a027bb77d97 100644
--- a/drivers/staging/omapdrm/omap_fbdev.c
+++ b/drivers/staging/omapdrm/omap_fbdev.c
@@ -276,7 +276,7 @@ fail:
276 if (fbi) 276 if (fbi)
277 framebuffer_release(fbi); 277 framebuffer_release(fbi);
278 if (fb) 278 if (fb)
279 fb->funcs->destroy(fb); 279 drm_framebuffer_remove(fb);
280 } 280 }
281 281
282 return ret; 282 return ret;
@@ -401,7 +401,7 @@ void omap_fbdev_free(struct drm_device *dev)
401 401
402 /* this will free the backing object */ 402 /* this will free the backing object */
403 if (fbdev->fb) 403 if (fbdev->fb)
404 fbdev->fb->funcs->destroy(fbdev->fb); 404 drm_framebuffer_remove(fbdev->fb);
405 405
406 kfree(fbdev); 406 kfree(fbdev);
407 407
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index eb91d520ce0b..68fdb299e39f 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -218,6 +218,7 @@ struct drm_display_info {
218}; 218};
219 219
220struct drm_framebuffer_funcs { 220struct drm_framebuffer_funcs {
221 /* note: use drm_framebuffer_remove() */
221 void (*destroy)(struct drm_framebuffer *framebuffer); 222 void (*destroy)(struct drm_framebuffer *framebuffer);
222 int (*create_handle)(struct drm_framebuffer *fb, 223 int (*create_handle)(struct drm_framebuffer *fb,
223 struct drm_file *file_priv, 224 struct drm_file *file_priv,
@@ -242,6 +243,16 @@ struct drm_framebuffer_funcs {
242 243
243struct drm_framebuffer { 244struct drm_framebuffer {
244 struct drm_device *dev; 245 struct drm_device *dev;
246 /*
247 * Note that the fb is refcounted for the benefit of driver internals,
248 * for example some hw, disabling a CRTC/plane is asynchronous, and
249 * scanout does not actually complete until the next vblank. So some
250 * cleanup (like releasing the reference(s) on the backing GEM bo(s))
251 * should be deferred. In cases like this, the driver would like to
252 * hold a ref to the fb even though it has already been removed from
253 * userspace perspective.
254 */
255 struct kref refcount;
245 struct list_head head; 256 struct list_head head;
246 struct drm_mode_object base; 257 struct drm_mode_object base;
247 const struct drm_framebuffer_funcs *funcs; 258 const struct drm_framebuffer_funcs *funcs;
@@ -919,6 +930,9 @@ extern void drm_framebuffer_set_object(struct drm_device *dev,
919extern int drm_framebuffer_init(struct drm_device *dev, 930extern int drm_framebuffer_init(struct drm_device *dev,
920 struct drm_framebuffer *fb, 931 struct drm_framebuffer *fb,
921 const struct drm_framebuffer_funcs *funcs); 932 const struct drm_framebuffer_funcs *funcs);
933extern void drm_framebuffer_unreference(struct drm_framebuffer *fb);
934extern void drm_framebuffer_reference(struct drm_framebuffer *fb);
935extern void drm_framebuffer_remove(struct drm_framebuffer *fb);
922extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb); 936extern void drm_framebuffer_cleanup(struct drm_framebuffer *fb);
923extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc); 937extern int drmfb_probe(struct drm_device *dev, struct drm_crtc *crtc);
924extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb); 938extern int drmfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);