return 0;
 }
 
+static struct dma_buf *drm_prime_lookup_buf_by_handle(struct drm_prime_file_private *prime_fpriv,
+                                                     uint32_t handle)
+{
+       struct drm_prime_member *member;
+
+       list_for_each_entry(member, &prime_fpriv->head, entry) {
+               if (member->handle == handle)
+                       return member->dma_buf;
+       }
+
+       return NULL;
+}
+
 static int drm_prime_lookup_buf_handle(struct drm_prime_file_private *prime_fpriv,
                                       struct dma_buf *dma_buf,
                                       uint32_t *handle)
        attach->priv = NULL;
 }
 
-static void drm_prime_remove_buf_handle_locked(
-               struct drm_prime_file_private *prime_fpriv,
-               struct dma_buf *dma_buf)
+void drm_prime_remove_buf_handle_locked(struct drm_prime_file_private *prime_fpriv,
+                                       struct dma_buf *dma_buf)
 {
        struct drm_prime_member *member, *safe;
 
         */
        obj->dma_buf = dmabuf;
        get_dma_buf(obj->dma_buf);
+       /* Grab a new ref since the callers is now used by the dma-buf */
+       drm_gem_object_reference(obj);
 
        return dmabuf;
 }
        int ret = 0;
        struct dma_buf *dmabuf;
 
+       mutex_lock(&file_priv->prime.lock);
        obj = drm_gem_object_lookup(dev, file_priv, handle);
-       if (!obj)
-               return -ENOENT;
+       if (!obj)  {
+               ret = -ENOENT;
+               goto out_unlock;
+       }
+
+       dmabuf = drm_prime_lookup_buf_by_handle(&file_priv->prime, handle);
+       if (dmabuf) {
+               get_dma_buf(dmabuf);
+               goto out_have_handle;
+       }
 
+       mutex_lock(&dev->object_name_lock);
        /* re-export the original imported object */
        if (obj->import_attach) {
                dmabuf = obj->import_attach->dmabuf;
                goto out_have_obj;
        }
 
-       mutex_lock(&dev->object_name_lock);
        if (obj->dma_buf) {
                get_dma_buf(obj->dma_buf);
                dmabuf = obj->dma_buf;
-               mutex_unlock(&dev->object_name_lock);
                goto out_have_obj;
        }
 
        dmabuf = export_and_register_object(dev, obj, flags);
-       mutex_unlock(&dev->object_name_lock);
        if (IS_ERR(dmabuf)) {
                /* normally the created dma-buf takes ownership of the ref,
                 * but if that fails then drop the ref
                 */
                ret = PTR_ERR(dmabuf);
+               mutex_unlock(&dev->object_name_lock);
                goto out;
        }
 
-       mutex_lock(&file_priv->prime.lock);
-       /* if we've exported this buffer the cheat and add it to the import list
-        * so we get the correct handle back
+out_have_obj:
+       /*
+        * If we've exported this buffer then cheat and add it to the import list
+        * so we get the correct handle back. We must do this under the
+        * protection of dev->object_name_lock to ensure that a racing gem close
+        * ioctl doesn't miss to remove this buffer handle from the cache.
         */
        ret = drm_prime_add_buf_handle(&file_priv->prime,
                                       dmabuf, handle);
+       mutex_unlock(&dev->object_name_lock);
        if (ret)
                goto fail_put_dmabuf;
 
+out_have_handle:
        ret = dma_buf_fd(dmabuf, flags);
-       if (ret < 0)
-               goto fail_rm_handle;
-
-       *prime_fd = ret;
-       mutex_unlock(&file_priv->prime.lock);
-       return 0;
-
-out_have_obj:
-       ret = dma_buf_fd(dmabuf, flags);
+       /*
+        * We must _not_ remove the buffer from the handle cache since the newly
+        * created dma buf is already linked in the global obj->dma_buf pointer,
+        * and that is invariant as long as a userspace gem handle exists.
+        * Closing the handle will clean out the cache anyway, so we don't leak.
+        */
        if (ret < 0) {
-               dma_buf_put(dmabuf);
+               goto fail_put_dmabuf;
        } else {
                *prime_fd = ret;
                ret = 0;
 
        goto out;
 
-fail_rm_handle:
-       drm_prime_remove_buf_handle_locked(&file_priv->prime,
-                                          dmabuf);
-       mutex_unlock(&file_priv->prime.lock);
 fail_put_dmabuf:
        dma_buf_put(dmabuf);
 out:
        drm_gem_object_unreference_unlocked(obj);
+out_unlock:
+       mutex_unlock(&file_priv->prime.lock);
+
        return ret;
 }
 EXPORT_SYMBOL(drm_gem_prime_handle_to_fd);
        WARN_ON(!list_empty(&prime_fpriv->head));
 }
 EXPORT_SYMBOL(drm_prime_destroy_file_private);
-
-void drm_prime_remove_buf_handle(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf)
-{
-       mutex_lock(&prime_fpriv->lock);
-       drm_prime_remove_buf_handle_locked(prime_fpriv, dma_buf);
-       mutex_unlock(&prime_fpriv->lock);
-}
-EXPORT_SYMBOL(drm_prime_remove_buf_handle);