aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2013-08-14 18:02:46 -0400
committerDave Airlie <airlied@redhat.com>2013-08-20 22:58:17 -0400
commit319c933c71f3dbdb2b3274d1634d3494c70efa06 (patch)
tree21573ae28fb9b9c38dd974eebc80a69b9a7d70e1 /drivers/gpu/drm
parent20228c447846da9399ead53fdbbc8ab69b47788a (diff)
drm/prime: proper locking+refcounting for obj->dma_buf link
The export dma-buf cache is semantically similar to an flink name. So semantically it makes sense to treat it the same and remove the name (i.e. the dma_buf pointer) and its references when the last gem handle disappears. Again we need to be careful, but double so: Not just could someone race and export with a gem close ioctl (so we need to recheck obj->handle_count again when assigning the new name), but multiple exports can also race against each another. This is prevented by holding the dev->object_name_lock across the entire section which touches obj->dma_buf. With the new scheme we also need to reinstate the obj->dma_buf link at import time (in case the only reference userspace has held in-between was through the dma-buf fd and not through any native gem handle). For simplicity we don't check whether it's a native object but unconditionally set up that link - with the new scheme of removing the obj->dma_buf reference when the last handle disappears we can do that. To make it clear that this is not just for exported buffers anymore als rename it from export_dma_buf to dma_buf. To make sure that now one can race a fd_to_handle or handle_to_fd with gem_close we use the same tricks as in flink of extending the dev->object_name_locking critical section. With this change we finally have a guaranteed 1:1 relationship (at least for native objects) between gem objects and dma-bufs, even accounting for races (which can happen since the dma-buf itself holds a reference while in-flight). This prevent igt/prime_self_import/export-vs-gem_close-race from Oopsing the kernel. There is still a leak though since the per-file priv dma-buf/handle cache handling is racy. That will be fixed in a later patch. v2: Remove the bogus dma_buf_put from the export_and_register_object failure path if we've raced with the handle count dropping to 0. Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm')
-rw-r--r--drivers/gpu/drm/drm_fops.c1
-rw-r--r--drivers/gpu/drm/drm_gem.c24
-rw-r--r--drivers/gpu/drm/drm_prime.c70
3 files changed, 77 insertions, 18 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 59f459291093..2d2401e9c5ae 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -486,6 +486,7 @@ int drm_release(struct inode *inode, struct file *filp)
486 if (dev->driver->postclose) 486 if (dev->driver->postclose)
487 dev->driver->postclose(dev, file_priv); 487 dev->driver->postclose(dev, file_priv);
488 488
489
489 if (drm_core_check_feature(dev, DRIVER_PRIME)) 490 if (drm_core_check_feature(dev, DRIVER_PRIME))
490 drm_prime_destroy_file_private(&file_priv->prime); 491 drm_prime_destroy_file_private(&file_priv->prime);
491 492
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index d47aa774d64b..4b3c533be859 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -195,9 +195,14 @@ drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp)
195 drm_prime_remove_buf_handle(&filp->prime, 195 drm_prime_remove_buf_handle(&filp->prime,
196 obj->import_attach->dmabuf); 196 obj->import_attach->dmabuf);
197 } 197 }
198 if (obj->export_dma_buf) { 198
199 /*
200 * Note: obj->dma_buf can't disappear as long as we still hold a
201 * handle reference in obj->handle_count.
202 */
203 if (obj->dma_buf) {
199 drm_prime_remove_buf_handle(&filp->prime, 204 drm_prime_remove_buf_handle(&filp->prime,
200 obj->export_dma_buf); 205 obj->dma_buf);
201 } 206 }
202} 207}
203 208
@@ -231,6 +236,15 @@ static void drm_gem_object_handle_free(struct drm_gem_object *obj)
231 } 236 }
232} 237}
233 238
239static void drm_gem_object_exported_dma_buf_free(struct drm_gem_object *obj)
240{
241 /* Unbreak the reference cycle if we have an exported dma_buf. */
242 if (obj->dma_buf) {
243 dma_buf_put(obj->dma_buf);
244 obj->dma_buf = NULL;
245 }
246}
247
234static void 248static void
235drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj) 249drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
236{ 250{
@@ -244,8 +258,10 @@ drm_gem_object_handle_unreference_unlocked(struct drm_gem_object *obj)
244 */ 258 */
245 259
246 mutex_lock(&obj->dev->object_name_lock); 260 mutex_lock(&obj->dev->object_name_lock);
247 if (--obj->handle_count == 0) 261 if (--obj->handle_count == 0) {
248 drm_gem_object_handle_free(obj); 262 drm_gem_object_handle_free(obj);
263 drm_gem_object_exported_dma_buf_free(obj);
264 }
249 mutex_unlock(&obj->dev->object_name_lock); 265 mutex_unlock(&obj->dev->object_name_lock);
250 266
251 drm_gem_object_unreference_unlocked(obj); 267 drm_gem_object_unreference_unlocked(obj);
@@ -712,6 +728,8 @@ drm_gem_release(struct drm_device *dev, struct drm_file *file_private)
712void 728void
713drm_gem_object_release(struct drm_gem_object *obj) 729drm_gem_object_release(struct drm_gem_object *obj)
714{ 730{
731 WARN_ON(obj->dma_buf);
732
715 if (obj->filp) 733 if (obj->filp)
716 fput(obj->filp); 734 fput(obj->filp);
717} 735}
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c
index 3d576018893a..5e543e9264d7 100644
--- a/drivers/gpu/drm/drm_prime.c
+++ b/drivers/gpu/drm/drm_prime.c
@@ -193,11 +193,8 @@ void drm_gem_dmabuf_release(struct dma_buf *dma_buf)
193{ 193{
194 struct drm_gem_object *obj = dma_buf->priv; 194 struct drm_gem_object *obj = dma_buf->priv;
195 195
196 if (obj->export_dma_buf == dma_buf) { 196 /* drop the reference on the export fd holds */
197 /* drop the reference on the export fd holds */ 197 drm_gem_object_unreference_unlocked(obj);
198 obj->export_dma_buf = NULL;
199 drm_gem_object_unreference_unlocked(obj);
200 }
201} 198}
202EXPORT_SYMBOL(drm_gem_dmabuf_release); 199EXPORT_SYMBOL(drm_gem_dmabuf_release);
203 200
@@ -298,6 +295,37 @@ struct dma_buf *drm_gem_prime_export(struct drm_device *dev,
298} 295}
299EXPORT_SYMBOL(drm_gem_prime_export); 296EXPORT_SYMBOL(drm_gem_prime_export);
300 297
298static struct dma_buf *export_and_register_object(struct drm_device *dev,
299 struct drm_gem_object *obj,
300 uint32_t flags)
301{
302 struct dma_buf *dmabuf;
303
304 /* prevent races with concurrent gem_close. */
305 if (obj->handle_count == 0) {
306 dmabuf = ERR_PTR(-ENOENT);
307 return dmabuf;
308 }
309
310 dmabuf = dev->driver->gem_prime_export(dev, obj, flags);
311 if (IS_ERR(dmabuf)) {
312 /* normally the created dma-buf takes ownership of the ref,
313 * but if that fails then drop the ref
314 */
315 return dmabuf;
316 }
317
318 /*
319 * Note that callers do not need to clean up the export cache
320 * since the check for obj->handle_count guarantees that someone
321 * will clean it up.
322 */
323 obj->dma_buf = dmabuf;
324 get_dma_buf(obj->dma_buf);
325
326 return dmabuf;
327}
328
301int drm_gem_prime_handle_to_fd(struct drm_device *dev, 329int drm_gem_prime_handle_to_fd(struct drm_device *dev,
302 struct drm_file *file_priv, uint32_t handle, uint32_t flags, 330 struct drm_file *file_priv, uint32_t handle, uint32_t flags,
303 int *prime_fd) 331 int *prime_fd)
@@ -313,15 +341,20 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
313 /* re-export the original imported object */ 341 /* re-export the original imported object */
314 if (obj->import_attach) { 342 if (obj->import_attach) {
315 dmabuf = obj->import_attach->dmabuf; 343 dmabuf = obj->import_attach->dmabuf;
344 get_dma_buf(dmabuf);
316 goto out_have_obj; 345 goto out_have_obj;
317 } 346 }
318 347
319 if (obj->export_dma_buf) { 348 mutex_lock(&dev->object_name_lock);
320 dmabuf = obj->export_dma_buf; 349 if (obj->dma_buf) {
350 get_dma_buf(obj->dma_buf);
351 dmabuf = obj->dma_buf;
352 mutex_unlock(&dev->object_name_lock);
321 goto out_have_obj; 353 goto out_have_obj;
322 } 354 }
323 355
324 dmabuf = dev->driver->gem_prime_export(dev, obj, flags); 356 dmabuf = export_and_register_object(dev, obj, flags);
357 mutex_unlock(&dev->object_name_lock);
325 if (IS_ERR(dmabuf)) { 358 if (IS_ERR(dmabuf)) {
326 /* normally the created dma-buf takes ownership of the ref, 359 /* normally the created dma-buf takes ownership of the ref,
327 * but if that fails then drop the ref 360 * but if that fails then drop the ref
@@ -329,14 +362,13 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
329 ret = PTR_ERR(dmabuf); 362 ret = PTR_ERR(dmabuf);
330 goto out; 363 goto out;
331 } 364 }
332 obj->export_dma_buf = dmabuf;
333 365
334 mutex_lock(&file_priv->prime.lock); 366 mutex_lock(&file_priv->prime.lock);
335 /* if we've exported this buffer the cheat and add it to the import list 367 /* if we've exported this buffer the cheat and add it to the import list
336 * so we get the correct handle back 368 * so we get the correct handle back
337 */ 369 */
338 ret = drm_prime_add_buf_handle(&file_priv->prime, 370 ret = drm_prime_add_buf_handle(&file_priv->prime,
339 obj->export_dma_buf, handle); 371 dmabuf, handle);
340 if (ret) 372 if (ret)
341 goto fail_put_dmabuf; 373 goto fail_put_dmabuf;
342 374
@@ -349,7 +381,6 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev,
349 return 0; 381 return 0;
350 382
351out_have_obj: 383out_have_obj:
352 get_dma_buf(dmabuf);
353 ret = dma_buf_fd(dmabuf, flags); 384 ret = dma_buf_fd(dmabuf, flags);
354 if (ret < 0) { 385 if (ret < 0) {
355 dma_buf_put(dmabuf); 386 dma_buf_put(dmabuf);
@@ -365,8 +396,6 @@ fail_rm_handle:
365 dmabuf); 396 dmabuf);
366 mutex_unlock(&file_priv->prime.lock); 397 mutex_unlock(&file_priv->prime.lock);
367fail_put_dmabuf: 398fail_put_dmabuf:
368 /* clear NOT to be checked when releasing dma_buf */
369 obj->export_dma_buf = NULL;
370 dma_buf_put(dmabuf); 399 dma_buf_put(dmabuf);
371out: 400out:
372 drm_gem_object_unreference_unlocked(obj); 401 drm_gem_object_unreference_unlocked(obj);
@@ -448,13 +477,22 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev,
448 goto out_put; 477 goto out_put;
449 478
450 /* never seen this one, need to import */ 479 /* never seen this one, need to import */
480 mutex_lock(&dev->object_name_lock);
451 obj = dev->driver->gem_prime_import(dev, dma_buf); 481 obj = dev->driver->gem_prime_import(dev, dma_buf);
452 if (IS_ERR(obj)) { 482 if (IS_ERR(obj)) {
453 ret = PTR_ERR(obj); 483 ret = PTR_ERR(obj);
454 goto out_put; 484 goto out_unlock;
485 }
486
487 if (obj->dma_buf) {
488 WARN_ON(obj->dma_buf != dma_buf);
489 } else {
490 obj->dma_buf = dma_buf;
491 get_dma_buf(dma_buf);
455 } 492 }
456 493
457 ret = drm_gem_handle_create(file_priv, obj, handle); 494 /* drm_gem_handle_create_tail unlocks dev->object_name_lock. */
495 ret = drm_gem_handle_create_tail(file_priv, obj, handle);
458 drm_gem_object_unreference_unlocked(obj); 496 drm_gem_object_unreference_unlocked(obj);
459 if (ret) 497 if (ret)
460 goto out_put; 498 goto out_put;
@@ -475,6 +513,8 @@ fail:
475 * to detach.. which seems ok.. 513 * to detach.. which seems ok..
476 */ 514 */
477 drm_gem_handle_delete(file_priv, *handle); 515 drm_gem_handle_delete(file_priv, *handle);
516out_unlock:
517 mutex_lock(&dev->object_name_lock);
478out_put: 518out_put:
479 dma_buf_put(dma_buf); 519 dma_buf_put(dma_buf);
480 mutex_unlock(&file_priv->prime.lock); 520 mutex_unlock(&file_priv->prime.lock);