Dave's original prime patches had a per file lookup (currently a list, but should probably be a hash as mentioned in the comment). This is handy for when an importer tries to import the same BO multiple times as it will prevent object duplication in that case. The problem is it does not account for multiple importers. If an exporter passes a dma-buf fd to multiple importers, a new object would be created for each one, which is bad.
To handle multiple importers, we create a per device dma buf hash table where the key is the dma_buf pointer, and the value is the gem object. There is also a lock introduced to help with the various ref count dances (and subsequent hash list traversals).
Cc: Daniel Vetter daniel.vetter@ffwll.ch Cc: Dave Airlie airlied@redhat.com Signed-off-by: Ben Widawsky ben@bwidawsk.net --- drivers/gpu/drm/drm_gem.c | 3 ++ drivers/gpu/drm/drm_prime.c | 46 +++++++++++++++++++++++++++++++++++++++++++ drivers/gpu/drm/drm_stub.c | 8 +++++++ include/drm/drmP.h | 13 ++++++++++++ 4 files changed, 70 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e19a958..c9bf515 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -146,6 +146,7 @@ int drm_gem_object_init(struct drm_device *dev, atomic_set(&obj->handle_count, 0); obj->size = size; obj->prime_fd = -1; + INIT_HLIST_NODE(&obj->brown);
return 0; } @@ -168,6 +169,8 @@ int drm_gem_private_object_init(struct drm_device *dev, atomic_set(&obj->handle_count, 0); obj->size = size; obj->prime_fd = -1; + INIT_HLIST_NODE(&obj->brown); + return 0; } EXPORT_SYMBOL(drm_gem_private_object_init); diff --git a/drivers/gpu/drm/drm_prime.c b/drivers/gpu/drm/drm_prime.c index 11f142f..b148d5c 100644 --- a/drivers/gpu/drm/drm_prime.c +++ b/drivers/gpu/drm/drm_prime.c @@ -1,5 +1,6 @@ #include <linux/export.h> #include <linux/dma-buf.h> +#include <linux/hash.h> #include "drmP.h"
struct drm_prime_member { @@ -124,3 +125,48 @@ void drm_prime_remove_fd_handle_mapping(struct drm_prime_file_private *prime_fpr } } EXPORT_SYMBOL(drm_prime_remove_fd_handle_mapping); + +static struct drm_gem_object *get_obj_from_dma_buf(struct drm_device *dev, + struct dma_buf *buf) +{ + struct drm_gem_object *obj; + struct hlist_head *bucket = + &dev->dma_buf_hash[hash_ptr(buf, DRM_DMA_BUF_HASH_BITS)]; + struct hlist_node *tmp; + + hlist_for_each_entry(obj, tmp, bucket, brown) { + if (obj->export_dma_buf == buf) + return obj; + } + + return NULL; +} + +int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj) +{ + struct drm_gem_object *tmp; + unsigned long hash; + + if ((tmp = get_obj_from_dma_buf(dev, obj->export_dma_buf))) { + DRM_DEBUG_PRIME("%p found DRM hash\n", obj->export_dma_buf); + if (WARN_ON(tmp != obj)) + return -1; + return 0; + } + + hash = hash_ptr(obj->export_dma_buf, DRM_DMA_BUF_HASH_BITS); + hlist_add_head(&obj->brown, &dev->dma_buf_hash[hash]); + + DRM_DEBUG_PRIME("%p added to DRM hash\n", obj->export_dma_buf); + + return 0; +} +EXPORT_SYMBOL(drm_prime_add_dma_buf); + +int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, + struct drm_gem_object **obj) +{ + *obj = get_obj_from_dma_buf(dev, buf); + return 0; +} +EXPORT_SYMBOL(drm_prime_lookup_obj); diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c index 6d7b083..78a614c 100644 --- a/drivers/gpu/drm/drm_stub.c +++ b/drivers/gpu/drm/drm_stub.c @@ -313,6 +313,14 @@ int drm_fill_in_dev(struct drm_device *dev, } }
+ if (driver->driver_features & DRIVER_PRIME) { + int i; + for (i = 0; i < DRM_DMA_BUF_HASH_ENTRIES; i++) + INIT_HLIST_HEAD(&dev->dma_buf_hash[i]); + + mutex_init(&dev->prime_mutex); + } + return 0;
error_out_unreg: diff --git a/include/drm/drmP.h b/include/drm/drmP.h index 5ed9b41..84dde0d 100644 --- a/include/drm/drmP.h +++ b/include/drm/drmP.h @@ -668,6 +668,8 @@ struct drm_gem_object {
/* dma buf attachment backing this object */ struct dma_buf_attachment *import_attach; + + struct hlist_node brown; };
/* initial implementaton using a linked list - todo hashtab */ @@ -730,6 +732,9 @@ struct drm_bus {
};
+#define DRM_DMA_BUF_HASH_BITS 5 +#define DRM_DMA_BUF_HASH_ENTRIES (1<<DRM_DMA_BUF_HASH_BITS) + /** * DRM driver structure. This structure represent the common code for * a family of cards. There will one drm_device for each card present @@ -1198,6 +1203,9 @@ struct drm_device { struct idr object_name_idr; /*@} */ int switch_power_state; + + struct mutex prime_mutex; + struct hlist_head dma_buf_hash[DRM_DMA_BUF_HASH_ENTRIES]; };
#define DRM_SWITCH_POWER_ON 0 @@ -1538,12 +1546,17 @@ extern int drm_prime_fd_to_handle_ioctl(struct drm_device *dev, void *data, extern struct sg_table *drm_prime_pages_to_sg(struct page **pages, int nr_pages); extern void drm_prime_gem_destroy(struct drm_gem_object *obj, struct sg_table *sg);
+ void drm_prime_init_file_private(struct drm_prime_file_private *prime_fpriv); void drm_prime_destroy_file_private(struct drm_prime_file_private *prime_fpriv); int drm_prime_insert_fd_handle_mapping(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t handle); int drm_prime_lookup_fd_handle_mapping(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf, uint32_t *handle); void drm_prime_remove_fd_handle_mapping(struct drm_prime_file_private *prime_fpriv, struct dma_buf *dma_buf);
+int drm_prime_add_dma_buf(struct drm_device *dev, struct drm_gem_object *obj); +int drm_prime_lookup_obj(struct drm_device *dev, struct dma_buf *buf, + struct drm_gem_object **obj); + #if DRM_DEBUG_CODE extern int drm_vma_info(struct seq_file *m, void *data); #endif