dma-buf: system_heap: add support system-uncached type

Change-Id: Ie9f81536ef710b244e79c8d32abd71c2601df4c5
Signed-off-by: Simon Xue <xxm@rock-chips.com>
This commit is contained in:
Simon Xue
2024-12-06 17:12:19 +08:00
parent 43fb28d9a8
commit 1a44a28107
2 changed files with 107 additions and 16 deletions

View File

@@ -22,6 +22,7 @@
#include <linux/vmalloc.h>
static struct dma_heap *sys_heap;
static struct dma_heap *sys_uncached_heap;
struct system_heap_buffer {
struct dma_heap *heap;
@@ -31,6 +32,8 @@ struct system_heap_buffer {
struct sg_table sg_table;
int vmap_cnt;
void *vaddr;
bool uncached;
};
struct dma_heap_attachment {
@@ -38,6 +41,8 @@ struct dma_heap_attachment {
struct sg_table *table;
struct list_head list;
bool mapped;
bool uncached;
};
#define LOW_ORDER_GFP (GFP_HIGHUSER | __GFP_ZERO)
@@ -100,7 +105,7 @@ static int system_heap_attach(struct dma_buf *dmabuf,
a->dev = attachment->dev;
INIT_LIST_HEAD(&a->list);
a->mapped = false;
a->uncached = buffer->uncached;
attachment->priv = a;
mutex_lock(&buffer->lock);
@@ -130,9 +135,13 @@ static struct sg_table *system_heap_map_dma_buf(struct dma_buf_attachment *attac
{
struct dma_heap_attachment *a = attachment->priv;
struct sg_table *table = a->table;
int attr = attachment->dma_map_attrs;
int ret;
ret = dma_map_sgtable(attachment->dev, table, direction, 0);
if (a->uncached)
attr |= DMA_ATTR_SKIP_CPU_SYNC;
ret = dma_map_sgtable(attachment->dev, table, direction, attr);
if (ret)
return ERR_PTR(ret);
@@ -145,9 +154,12 @@ static void system_heap_unmap_dma_buf(struct dma_buf_attachment *attachment,
enum dma_data_direction direction)
{
struct dma_heap_attachment *a = attachment->priv;
int attr = attachment->dma_map_attrs;
if (a->uncached)
attr |= DMA_ATTR_SKIP_CPU_SYNC;
a->mapped = false;
dma_unmap_sgtable(attachment->dev, table, direction, 0);
dma_unmap_sgtable(attachment->dev, table, direction, attr);
}
static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
@@ -161,10 +173,12 @@ static int system_heap_dma_buf_begin_cpu_access(struct dma_buf *dmabuf,
if (buffer->vmap_cnt)
invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
list_for_each_entry(a, &buffer->attachments, list) {
if (!a->mapped)
continue;
dma_sync_sgtable_for_cpu(a->dev, a->table, direction);
if (!buffer->uncached) {
list_for_each_entry(a, &buffer->attachments, list) {
if (!a->mapped)
continue;
dma_sync_sgtable_for_cpu(a->dev, a->table, direction);
}
}
mutex_unlock(&buffer->lock);
@@ -182,10 +196,12 @@ static int system_heap_dma_buf_end_cpu_access(struct dma_buf *dmabuf,
if (buffer->vmap_cnt)
flush_kernel_vmap_range(buffer->vaddr, buffer->len);
list_for_each_entry(a, &buffer->attachments, list) {
if (!a->mapped)
continue;
dma_sync_sgtable_for_device(a->dev, a->table, direction);
if (!buffer->uncached) {
list_for_each_entry(a, &buffer->attachments, list) {
if (!a->mapped)
continue;
dma_sync_sgtable_for_device(a->dev, a->table, direction);
}
}
mutex_unlock(&buffer->lock);
@@ -252,6 +268,11 @@ system_heap_dma_buf_begin_cpu_access_partial(struct dma_buf *dmabuf,
if (buffer->vmap_cnt)
invalidate_kernel_vmap_range(buffer->vaddr, buffer->len);
if (buffer->uncached) {
mutex_unlock(&buffer->lock);
return 0;
}
ret = system_heap_sgl_sync_range(dma_heap_get_dev(heap), table,
offset, len, direction, true);
mutex_unlock(&buffer->lock);
@@ -274,6 +295,11 @@ system_heap_dma_buf_end_cpu_access_partial(struct dma_buf *dmabuf,
if (buffer->vmap_cnt)
flush_kernel_vmap_range(buffer->vaddr, buffer->len);
if (buffer->uncached) {
mutex_unlock(&buffer->lock);
return 0;
}
ret = system_heap_sgl_sync_range(dma_heap_get_dev(heap), table,
offset, len, direction, false);
mutex_unlock(&buffer->lock);
@@ -289,6 +315,9 @@ static int system_heap_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
struct sg_page_iter piter;
int ret;
if (buffer->uncached)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
for_each_sgtable_page(table, &piter, vma->vm_pgoff) {
struct page *page = sg_page_iter_page(&piter);
@@ -310,17 +339,21 @@ static void *system_heap_do_vmap(struct system_heap_buffer *buffer)
struct page **pages = vmalloc(sizeof(struct page *) * npages);
struct page **tmp = pages;
struct sg_page_iter piter;
pgprot_t pgprot = PAGE_KERNEL;
void *vaddr;
if (!pages)
return ERR_PTR(-ENOMEM);
if (buffer->uncached)
pgprot = pgprot_writecombine(PAGE_KERNEL);
for_each_sgtable_page(table, &piter, 0) {
WARN_ON(tmp - pages >= npages);
*tmp++ = sg_page_iter_page(&piter);
}
vaddr = vmap(pages, npages, VM_MAP, PAGE_KERNEL);
vaddr = vmap(pages, npages, VM_MAP, pgprot);
vfree(pages);
if (!vaddr)
@@ -424,10 +457,11 @@ static struct page *alloc_largest_available(unsigned long size,
return NULL;
}
static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
unsigned long len,
unsigned long fd_flags,
unsigned long heap_flags)
static struct dma_buf *system_heap_do_allocate(struct dma_heap *heap,
unsigned long len,
unsigned long fd_flags,
unsigned long heap_flags,
bool uncached)
{
struct system_heap_buffer *buffer;
DEFINE_DMA_BUF_EXPORT_INFO(exp_info);
@@ -448,6 +482,7 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
mutex_init(&buffer->lock);
buffer->heap = heap;
buffer->len = len;
buffer->uncached = uncached;
INIT_LIST_HEAD(&pages);
i = 0;
@@ -493,6 +528,18 @@ static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
ret = PTR_ERR(dmabuf);
goto free_pages;
}
/*
* For uncached buffers, we need to initially flush cpu cache, since
* the __GFP_ZERO on the allocation means the zeroing was done by the
* cpu and thus it is likely cached. Map (and implicitly flush) and
* unmap it now so we don't get corruption later on.
*/
if (buffer->uncached) {
dma_map_sgtable(dma_heap_get_dev(heap), table, DMA_BIDIRECTIONAL, 0);
dma_unmap_sgtable(dma_heap_get_dev(heap), table, DMA_BIDIRECTIONAL, 0);
}
return dmabuf;
free_pages:
@@ -510,10 +557,39 @@ free_buffer:
return ERR_PTR(ret);
}
static struct dma_buf *system_heap_allocate(struct dma_heap *heap,
unsigned long len,
unsigned long fd_flags,
unsigned long heap_flags)
{
return system_heap_do_allocate(heap, len, fd_flags, heap_flags, false);
}
static const struct dma_heap_ops system_heap_ops = {
.allocate = system_heap_allocate,
};
static struct dma_buf *system_uncached_heap_allocate(struct dma_heap *heap,
unsigned long len,
unsigned long fd_flags,
unsigned long heap_flags)
{
return system_heap_do_allocate(heap, len, fd_flags, heap_flags, true);
}
/* Dummy function to be used until we can call coerce_mask_and_coherent */
static struct dma_buf *system_uncached_heap_not_initialized(struct dma_heap *heap,
unsigned long len,
unsigned long fd_flags,
unsigned long heap_flags)
{
return ERR_PTR(-EBUSY);
}
static struct dma_heap_ops system_uncached_heap_ops = {
/* After system_heap_create is complete, we will swap this */
.allocate = system_uncached_heap_not_initialized,
};
static int system_heap_create(void)
{
struct dma_heap_export_info exp_info;
@@ -526,6 +602,18 @@ static int system_heap_create(void)
if (IS_ERR(sys_heap))
return PTR_ERR(sys_heap);
exp_info.name = "system-uncached";
exp_info.ops = &system_uncached_heap_ops;
exp_info.priv = NULL;
sys_uncached_heap = dma_heap_add(&exp_info);
if (IS_ERR(sys_uncached_heap))
return PTR_ERR(sys_uncached_heap);
dma_coerce_mask_and_coherent(dma_heap_get_dev(sys_uncached_heap), DMA_BIT_MASK(64));
mb(); /* make sure we only set allocate after dma_mask is set */
system_uncached_heap_ops.allocate = system_uncached_heap_allocate;
return 0;
}
module_init(system_heap_create);

View File

@@ -580,6 +580,8 @@ struct dma_buf_attach_ops {
* @importer_ops: importer operations for this attachment, if provided
* dma_buf_map/unmap_attachment() must be called with the dma_resv lock held.
* @importer_priv: importer specific attachment data.
* @dma_map_attrs: DMA attributes to be used when the exporter maps the buffer
* through dma_buf_map_attachment.
*
* This structure holds the attachment information between the dma_buf buffer
* and its user device(s). The list contains one attachment struct per device
@@ -600,6 +602,7 @@ struct dma_buf_attachment {
const struct dma_buf_attach_ops *importer_ops;
void *importer_priv;
void *priv;
unsigned long dma_map_attrs;
};
/**