diff options
author | Dave Airlie <airlied@gmail.com> | 2013-04-21 19:54:36 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2013-04-30 19:30:15 -0400 |
commit | 219b47339ced80ca580bb6ce7d1636166984afa7 (patch) | |
tree | 1646e5e3b5998a5b13f71c8f06b7a28a0163fbe7 | |
parent | c55b6b3da25aa3af36ec51a13a4ed15fef0d7a73 (diff) |
drm/prime: keep a reference from the handle to exported dma-buf (v6)
Currently we have a problem with this:
1. i915: create gem object
2. i915: export gem object to prime
3. radeon: import gem object
4. close prime fd
5. radeon: unref object
6. i915: unref object
i915 has an imported object reference in its file priv, that isn't
cleaned up properly until fd close. The reference gets added at step 2,
but at step 6 we don't have enough info to clean it up.
The solution is to take a reference on the dma-buf when we export it,
and drop the reference when the gem handle goes away.
So when we export a dma_buf from a gem object, we keep track of it
with the handle, we take a reference to the dma_buf. When we close
the handle (i.e. userspace is finished with the buffer), we drop
the reference to the dma_buf, and it gets collected.
This patch isn't meant to fix any other problem or bikesheds, and it doesn't
fix any races with other scenarios.
v1.1: move export symbol line back up.
v2: okay I had to do a bit more, as the first patch showed a leak
on one of my tests, that I found using the dma-buf debugfs support,
the problem case is exporting a buffer twice with the same handle,
we'd add another export handle for it unnecessarily, however
we now fail if we try to export the same object with a different gem handle,
however I'm not sure if that is a case I want to support, and I've
gotten the code to WARN_ON if we hit something like that.
v2.1: rebase this patch, write better commit msg.
v3: cleanup error handling, track import vs export in linked list,
these two patches were separate previously, but seem to work better
like this.
v4: danvet is correct, this code is no longer useful, since the buffer
better exist, so remove it.
v5: always take a reference to the dma buf object, import or export.
(Imre Deak contributed this originally)
v6: square the circle, remove import vs export tracking now
that there is no difference
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Cc: stable@vger.kernel.org
Signed-off-by: Dave Airlie <airlied@redhat.com>
-rw-r--r-- | drivers/gpu/drm/drm_gem.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_prime.c | 76 | ||||
-rw-r--r-- | include/drm/drmP.h | 5 |
3 files changed, 44 insertions, 41 deletions
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index af779ae19ebf..cf919e36e8ae 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c | |||
@@ -205,11 +205,11 @@ static void | |||
205 | drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) | 205 | drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) |
206 | { | 206 | { |
207 | if (obj->import_attach) { | 207 | if (obj->import_attach) { |
208 | drm_prime_remove_imported_buf_handle(&filp->prime, | 208 | drm_prime_remove_buf_handle(&filp->prime, |
209 | obj->import_attach->dmabuf); | 209 | obj->import_attach->dmabuf); |
210 | } | 210 | } |
211 | if (obj->export_dma_buf) { | 211 | if (obj->export_dma_buf) { |
212 | drm_prime_remove_imported_buf_handle(&filp->prime, | 212 | drm_prime_remove_buf_handle(&filp->prime, |
213 | obj->export_dma_buf); | 213 | obj->export_dma_buf); |
214 | } | 214 | } |
215 | } | 215 | } |
diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 25d02187067e..7830d8e1f212 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c | |||
@@ -62,6 +62,7 @@ struct drm_prime_member { | |||
62 | struct dma_buf *dma_buf; | 62 | struct dma_buf *dma_buf; |
63 | uint32_t handle; | 63 | uint32_t handle; |
64 | }; | 64 | }; |
65 | static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); | ||
65 | 66 | ||
66 | static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, | 67 | static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, |
67 | enum dma_data_direction dir) | 68 | enum dma_data_direction dir) |
@@ -200,7 +201,8 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, | |||
200 | { | 201 | { |
201 | struct drm_gem_object *obj; | 202 | struct drm_gem_object *obj; |
202 | void *buf; | 203 | void *buf; |
203 | int ret; | 204 | int ret = 0; |
205 | struct dma_buf *dmabuf; | ||
204 | 206 | ||
205 | obj = drm_gem_object_lookup(dev, file_priv, handle); | 207 | obj = drm_gem_object_lookup(dev, file_priv, handle); |
206 | if (!obj) | 208 | if (!obj) |
@@ -209,43 +211,44 @@ int drm_gem_prime_handle_to_fd(struct drm_device *dev, | |||
209 | mutex_lock(&file_priv->prime.lock); | 211 | mutex_lock(&file_priv->prime.lock); |
210 | /* re-export the original imported object */ | 212 | /* re-export the original imported object */ |
211 | if (obj->import_attach) { | 213 | if (obj->import_attach) { |
212 | get_dma_buf(obj->import_attach->dmabuf); | 214 | dmabuf = obj->import_attach->dmabuf; |
213 | *prime_fd = dma_buf_fd(obj->import_attach->dmabuf, flags); | 215 | goto out_have_obj; |
214 | drm_gem_object_unreference_unlocked(obj); | ||
215 | mutex_unlock(&file_priv->prime.lock); | ||
216 | return 0; | ||
217 | } | 216 | } |
218 | 217 | ||
219 | if (obj->export_dma_buf) { | 218 | if (obj->export_dma_buf) { |
220 | get_dma_buf(obj->export_dma_buf); | 219 | dmabuf = obj->export_dma_buf; |
221 | *prime_fd = dma_buf_fd(obj->export_dma_buf, flags); | 220 | goto out_have_obj; |
222 | drm_gem_object_unreference_unlocked(obj); | ||
223 | } else { | ||
224 | buf = dev->driver->gem_prime_export(dev, obj, flags); | ||
225 | if (IS_ERR(buf)) { | ||
226 | /* normally the created dma-buf takes ownership of the ref, | ||
227 | * but if that fails then drop the ref | ||
228 | */ | ||
229 | drm_gem_object_unreference_unlocked(obj); | ||
230 | mutex_unlock(&file_priv->prime.lock); | ||
231 | return PTR_ERR(buf); | ||
232 | } | ||
233 | obj->export_dma_buf = buf; | ||
234 | *prime_fd = dma_buf_fd(buf, flags); | ||
235 | } | 221 | } |
222 | |||
223 | buf = dev->driver->gem_prime_export(dev, obj, flags); | ||
224 | if (IS_ERR(buf)) { | ||
225 | /* normally the created dma-buf takes ownership of the ref, | ||
226 | * but if that fails then drop the ref | ||
227 | */ | ||
228 | ret = PTR_ERR(buf); | ||
229 | goto out; | ||
230 | } | ||
231 | obj->export_dma_buf = buf; | ||
232 | |||
236 | /* if we've exported this buffer the cheat and add it to the import list | 233 | /* if we've exported this buffer the cheat and add it to the import list |
237 | * so we get the correct handle back | 234 | * so we get the correct handle back |
238 | */ | 235 | */ |
239 | ret = drm_prime_add_imported_buf_handle(&file_priv->prime, | 236 | ret = drm_prime_add_buf_handle(&file_priv->prime, |
240 | obj->export_dma_buf, handle); | 237 | obj->export_dma_buf, handle); |
241 | if (ret) { | 238 | if (ret) |
242 | drm_gem_object_unreference_unlocked(obj); | 239 | goto out; |
243 | mutex_unlock(&file_priv->prime.lock); | ||
244 | return ret; | ||
245 | } | ||
246 | 240 | ||
241 | *prime_fd = dma_buf_fd(buf, flags); | ||
247 | mutex_unlock(&file_priv->prime.lock); | 242 | mutex_unlock(&file_priv->prime.lock); |
248 | return 0; | 243 | return 0; |
244 | |||
245 | out_have_obj: | ||
246 | get_dma_buf(dmabuf); | ||
247 | *prime_fd = dma_buf_fd(dmabuf, flags); | ||
248 | out: | ||
249 | drm_gem_object_unreference_unlocked(obj); | ||
250 | mutex_unlock(&file_priv->prime.lock); | ||
251 | return ret; | ||
249 | } | 252 | } |
250 | EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); | 253 | EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); |
251 | 254 | ||
@@ -314,7 +317,7 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, | |||
314 | 317 | ||
315 | mutex_lock(&file_priv->prime.lock); | 318 | mutex_lock(&file_priv->prime.lock); |
316 | 319 | ||
317 | ret = drm_prime_lookup_imported_buf_handle(&file_priv->prime, | 320 | ret = drm_prime_lookup_buf_handle(&file_priv->prime, |
318 | dma_buf, handle); | 321 | dma_buf, handle); |
319 | if (!ret) { | 322 | if (!ret) { |
320 | ret = 0; | 323 | ret = 0; |
@@ -333,7 +336,7 @@ int drm_gem_prime_fd_to_handle(struct drm_device *dev, | |||
333 | if (ret) | 336 | if (ret) |
334 | goto out_put; | 337 | goto out_put; |
335 | 338 | ||
336 | ret = drm_prime_add_imported_buf_handle(&file_priv->prime, | 339 | ret = drm_prime_add_buf_handle(&file_priv->prime, |
337 | dma_buf, *handle); | 340 | dma_buf, *handle); |
338 | if (ret) | 341 | if (ret) |
339 | goto fail; | 342 | goto fail; |
@@ -487,7 +490,7 @@ void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv) | |||
487 | } | 490 | } |
488 | EXPORT_SYMBOL(drm_prime_destroy_file_private); | 491 | EXPORT_SYMBOL(drm_prime_destroy_file_private); |
489 | 492 | ||
490 | int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle) | 493 | static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle) |
491 | { | 494 | { |
492 | struct drm_prime_member *member; | 495 | struct drm_prime_member *member; |
493 | 496 | ||
@@ -495,14 +498,14 @@ int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv | |||
495 | if (!member) | 498 | if (!member) |
496 | return -ENOMEM; | 499 | return -ENOMEM; |
497 | 500 | ||
501 | get_dma_buf(dma_buf); | ||
498 | member->dma_buf = dma_buf; | 502 | member->dma_buf = dma_buf; |
499 | member->handle = handle; | 503 | member->handle = handle; |
500 | list_add(&member->entry, &prime_fpriv->head); | 504 | list_add(&member->entry, &prime_fpriv->head); |
501 | return 0; | 505 | return 0; |
502 | } | 506 | } |
503 | EXPORT_SYMBOL(drm_prime_add_imported_buf_handle); | ||
504 | 507 | ||
505 | int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) | 508 | int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) |
506 | { | 509 | { |
507 | struct drm_prime_member *member; | 510 | struct drm_prime_member *member; |
508 | 511 | ||
@@ -514,19 +517,20 @@ int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fp | |||
514 | } | 517 | } |
515 | return -ENOENT; | 518 | return -ENOENT; |
516 | } | 519 | } |
517 | EXPORT_SYMBOL(drm_prime_lookup_imported_buf_handle); | 520 | EXPORT_SYMBOL(drm_prime_lookup_buf_handle); |
518 | 521 | ||
519 | void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) | 522 | void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) |
520 | { | 523 | { |
521 | struct drm_prime_member *member, *safe; | 524 | struct drm_prime_member *member, *safe; |
522 | 525 | ||
523 | mutex_lock(&prime_fpriv->lock); | 526 | mutex_lock(&prime_fpriv->lock); |
524 | list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) { | 527 | list_for_each_entry_safe(member, safe, &prime_fpriv->head, entry) { |
525 | if (member->dma_buf == dma_buf) { | 528 | if (member->dma_buf == dma_buf) { |
529 | dma_buf_put(dma_buf); | ||
526 | list_del(&member->entry); | 530 | list_del(&member->entry); |
527 | kfree(member); | 531 | kfree(member); |
528 | } | 532 | } |
529 | } | 533 | } |
530 | mutex_unlock(&prime_fpriv->lock); | 534 | mutex_unlock(&prime_fpriv->lock); |
531 | } | 535 | } |
532 | EXPORT_SYMBOL(drm_prime_remove_imported_buf_handle); | 536 | EXPORT_SYMBOL(drm_prime_remove_buf_handle); |
diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 2d94d7413d71..f1ce786736e4 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h | |||
@@ -1593,9 +1593,8 @@ extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *s | |||
1593 | 1593 | ||
1594 | void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); | 1594 | void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); |
1595 | void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); | 1595 | void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); |
1596 | int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); | 1596 | int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); |
1597 | int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); | 1597 | void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); |
1598 | void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf); | ||
1599 | 1598 | ||
1600 | int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); | 1599 | int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); |
1601 | int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, | 1600 | int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, |