mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 12:57:06 +09:00
dma-buf: dma-buf-cache: fix concurrency issue
The same dma-buf may attach/map on different cpu at the same time, this lead to concurrency issue. Add mutex lock to make dma-buf-cache thread-safe cpu1 cpu2 | | dma_buf_cache_map_attachment dma_buf_cache_map_attachment | | dma_buf_map_attachment dma_buf_map_attachment | | system_heap_map_dma_buf system_heap_map_dma_buf | | iommu_dma_map_sg iommu_dma_map_sg | | __finalise_sg __finalise_sg | | sg_dma_address(sg) = iova1 | | | cache->sg_table = sg_table sg_dma_address(sg) = iova2 + iova1 after add mutex lock cpu1 cpu2 | | dma_buf_cache_map_attachment dma_buf_cache_map_attachment | | mutex_lock mutex_lock | | dma_buf_map_attachment return cache->sg_table | | system_heap_map_dma_buf mutex_unlock | iommu_dma_map_sg | __finalise_sg | sg_dma_address(sg) = iova1 | cache->sg_table = sg_table | mutex_unlock Change-Id: I2e08dafe61668ce35467b2c3b074f132f08ed6ba Signed-off-by: Simon Xue <xxm@rock-chips.com>
This commit is contained in:
@@ -8,9 +8,10 @@
|
||||
#undef CONFIG_DMABUF_CACHE
|
||||
#include <linux/dma-buf-cache.h>
|
||||
|
||||
/* NOTE: dma-buf-cache APIs are not irq safe, please DO NOT run in irq context !! */
|
||||
|
||||
struct dma_buf_cache_list {
|
||||
struct list_head head;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct dma_buf_cache {
|
||||
@@ -25,9 +26,10 @@ static int dma_buf_cache_destructor(struct dma_buf *dmabuf, void *dtor_data)
|
||||
struct dma_buf_cache_list *data;
|
||||
struct dma_buf_cache *cache, *tmp;
|
||||
|
||||
mutex_lock(&dmabuf->cache_lock);
|
||||
|
||||
data = dmabuf->dtor_data;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
list_for_each_entry_safe(cache, tmp, &data->head, list) {
|
||||
if (!IS_ERR_OR_NULL(cache->sg_table))
|
||||
dma_buf_unmap_attachment(cache->attach,
|
||||
@@ -37,9 +39,9 @@ static int dma_buf_cache_destructor(struct dma_buf *dmabuf, void *dtor_data)
|
||||
dma_buf_detach(dmabuf, cache->attach);
|
||||
list_del(&cache->list);
|
||||
kfree(cache);
|
||||
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
mutex_unlock(&dmabuf->cache_lock);
|
||||
|
||||
kfree(data);
|
||||
return 0;
|
||||
@@ -57,14 +59,10 @@ dma_buf_cache_get_cache(struct dma_buf_attachment *attach)
|
||||
|
||||
data = dmabuf->dtor_data;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
list_for_each_entry(cache, &data->head, list) {
|
||||
if (cache->attach == attach) {
|
||||
mutex_unlock(&data->lock);
|
||||
if (cache->attach == attach)
|
||||
return cache;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
@@ -74,9 +72,13 @@ void dma_buf_cache_detach(struct dma_buf *dmabuf,
|
||||
{
|
||||
struct dma_buf_cache *cache;
|
||||
|
||||
mutex_lock(&dmabuf->cache_lock);
|
||||
|
||||
cache = dma_buf_cache_get_cache(attach);
|
||||
if (!cache)
|
||||
dma_buf_detach(dmabuf, attach);
|
||||
|
||||
mutex_unlock(&dmabuf->cache_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_buf_cache_detach);
|
||||
|
||||
@@ -87,49 +89,58 @@ struct dma_buf_attachment *dma_buf_cache_attach(struct dma_buf *dmabuf,
|
||||
struct dma_buf_cache_list *data;
|
||||
struct dma_buf_cache *cache;
|
||||
|
||||
mutex_lock(&dmabuf->cache_lock);
|
||||
|
||||
if (!dmabuf->dtor) {
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_init(&data->lock);
|
||||
if (!data) {
|
||||
attach = ERR_PTR(-ENOMEM);
|
||||
goto err_data;
|
||||
}
|
||||
INIT_LIST_HEAD(&data->head);
|
||||
|
||||
dma_buf_set_destructor(dmabuf, dma_buf_cache_destructor, data);
|
||||
}
|
||||
|
||||
if (dmabuf->dtor && dmabuf->dtor != dma_buf_cache_destructor)
|
||||
return dma_buf_attach(dmabuf, dev);
|
||||
if (dmabuf->dtor && dmabuf->dtor != dma_buf_cache_destructor) {
|
||||
attach = dma_buf_attach(dmabuf, dev);
|
||||
goto attach_done;
|
||||
}
|
||||
|
||||
data = dmabuf->dtor_data;
|
||||
|
||||
mutex_lock(&data->lock);
|
||||
list_for_each_entry(cache, &data->head, list) {
|
||||
if (cache->attach->dev == dev) {
|
||||
/* Already attached */
|
||||
mutex_unlock(&data->lock);
|
||||
return cache->attach;
|
||||
attach = cache->attach;
|
||||
goto attach_done;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
cache = kzalloc(sizeof(*cache), GFP_KERNEL);
|
||||
if (!cache)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (!cache) {
|
||||
attach = ERR_PTR(-ENOMEM);
|
||||
goto err_cache;
|
||||
}
|
||||
/* Cache attachment */
|
||||
attach = dma_buf_attach(dmabuf, dev);
|
||||
if (IS_ERR_OR_NULL(attach)) {
|
||||
kfree(cache);
|
||||
return attach;
|
||||
}
|
||||
if (IS_ERR_OR_NULL(attach))
|
||||
goto err_attach;
|
||||
|
||||
cache->attach = attach;
|
||||
mutex_lock(&data->lock);
|
||||
list_add(&cache->list, &data->head);
|
||||
mutex_unlock(&data->lock);
|
||||
|
||||
return cache->attach;
|
||||
attach_done:
|
||||
mutex_unlock(&dmabuf->cache_lock);
|
||||
return attach;
|
||||
|
||||
err_attach:
|
||||
kfree(cache);
|
||||
err_cache:
|
||||
kfree(data);
|
||||
dma_buf_set_destructor(dmabuf, NULL, NULL);
|
||||
err_data:
|
||||
mutex_unlock(&dmabuf->cache_lock);
|
||||
return attach;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_buf_cache_attach);
|
||||
|
||||
@@ -137,38 +148,51 @@ void dma_buf_cache_unmap_attachment(struct dma_buf_attachment *attach,
|
||||
struct sg_table *sg_table,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_buf *dmabuf = attach->dmabuf;
|
||||
struct dma_buf_cache *cache;
|
||||
|
||||
mutex_lock(&dmabuf->cache_lock);
|
||||
|
||||
cache = dma_buf_cache_get_cache(attach);
|
||||
if (!cache)
|
||||
dma_buf_unmap_attachment(attach, sg_table, direction);
|
||||
|
||||
mutex_unlock(&dmabuf->cache_lock);
|
||||
}
|
||||
EXPORT_SYMBOL(dma_buf_cache_unmap_attachment);
|
||||
|
||||
struct sg_table *dma_buf_cache_map_attachment(struct dma_buf_attachment *attach,
|
||||
enum dma_data_direction direction)
|
||||
{
|
||||
struct dma_buf *dmabuf = attach->dmabuf;
|
||||
struct dma_buf_cache *cache;
|
||||
struct sg_table *sg_table;
|
||||
|
||||
mutex_lock(&dmabuf->cache_lock);
|
||||
|
||||
cache = dma_buf_cache_get_cache(attach);
|
||||
if (!cache)
|
||||
return dma_buf_map_attachment(attach, direction);
|
||||
|
||||
if (!cache) {
|
||||
sg_table = dma_buf_map_attachment(attach, direction);
|
||||
goto map_done;
|
||||
}
|
||||
if (cache->sg_table) {
|
||||
/* Already mapped */
|
||||
if (cache->direction == direction)
|
||||
return cache->sg_table;
|
||||
|
||||
if (cache->direction == direction) {
|
||||
sg_table = cache->sg_table;
|
||||
goto map_done;
|
||||
}
|
||||
/* Different directions */
|
||||
dma_buf_unmap_attachment(attach, cache->sg_table,
|
||||
cache->direction);
|
||||
|
||||
}
|
||||
|
||||
/* Cache map */
|
||||
cache->sg_table = dma_buf_map_attachment(attach, direction);
|
||||
sg_table = dma_buf_map_attachment(attach, direction);
|
||||
cache->sg_table = sg_table;
|
||||
cache->direction = direction;
|
||||
|
||||
return cache->sg_table;
|
||||
map_done:
|
||||
mutex_unlock(&dmabuf->cache_lock);
|
||||
return sg_table;
|
||||
}
|
||||
EXPORT_SYMBOL(dma_buf_cache_map_attachment);
|
||||
|
||||
@@ -702,6 +702,9 @@ struct dma_buf *dma_buf_export(const struct dma_buf_export_info *exp_info)
|
||||
dmabuf->exp_name = exp_info->exp_name;
|
||||
dmabuf->owner = exp_info->owner;
|
||||
spin_lock_init(&dmabuf->name_lock);
|
||||
#ifdef CONFIG_DMABUF_CACHE
|
||||
mutex_init(&dmabuf->cache_lock);
|
||||
#endif
|
||||
init_waitqueue_head(&dmabuf->poll);
|
||||
dmabuf->cb_excl.poll = dmabuf->cb_shared.poll = &dmabuf->poll;
|
||||
dmabuf->cb_excl.active = dmabuf->cb_shared.active = 0;
|
||||
|
||||
@@ -442,6 +442,9 @@ struct dma_buf {
|
||||
#ifdef CONFIG_NO_GKI
|
||||
dma_buf_destructor dtor;
|
||||
void *dtor_data;
|
||||
#ifdef CONFIG_DMABUF_CACHE
|
||||
struct mutex cache_lock;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
ANDROID_KABI_RESERVE(1);
|
||||
|
||||
Reference in New Issue
Block a user