aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDave Airlie <airlied@gmail.com>2013-04-21 19:54:36 -0400
committerDave Airlie <airlied@redhat.com>2013-04-30 19:30:15 -0400
commit219b47339ced80ca580bb6ce7d1636166984afa7 (patch)
tree1646e5e3b5998a5b13f71c8f06b7a28a0163fbe7 /drivers
parentc55b6b3da25aa3af36ec51a13a4ed15fef0d7a73 (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>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/gpu/drm/drm_gem.c4
-rw-r--r--drivers/gpu/drm/drm_prime.c76
2 files changed, 42 insertions, 38 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
205drm_gem_remove_prime_handles(struct drm_gem_object *obj, struct drm_file *filp) 205drm_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};
65static int drm_prime_add_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle);
65 66
66static struct sg_table *drm_gem_map_dma_buf(struct dma_buf_attachment *attach, 67static 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
245out_have_obj:
246 get_dma_buf(dmabuf);
247 *prime_fd = dma_buf_fd(dmabuf, flags);
248out:
249 drm_gem_object_unreference_unlocked(obj);
250 mutex_unlock(&file_priv->prime.lock);
251 return ret;
249} 252}
250EXPORT_SYMBOL(drm_gem_prime_handle_to_fd); 253EXPORT_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}
488EXPORT_SYMBOL(drm_prime_destroy_file_private); 491EXPORT_SYMBOL(drm_prime_destroy_file_private);
489 492
490int drm_prime_add_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle) 493static 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}
503EXPORT_SYMBOL(drm_prime_add_imported_buf_handle);
504 507
505int drm_prime_lookup_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle) 508int 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}
517EXPORT_SYMBOL(drm_prime_lookup_imported_buf_handle); 520EXPORT_SYMBOL(drm_prime_lookup_buf_handle);
518 521
519void drm_prime_remove_imported_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf) 522void 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}
532EXPORT_SYMBOL(drm_prime_remove_imported_buf_handle); 536EXPORT_SYMBOL(drm_prime_remove_buf_handle);