| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * DMABUF System heap exporter |
| * |
| * Copyright (C) 2011 Google, Inc. |
| * Copyright (C) 2019 Linaro Ltd. |
| */ |
| |
| #include <linux/dma-buf.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/dma-heap.h> |
| #include <linux/err.h> |
| #include <linux/highmem.h> |
| #include <linux/mm.h> |
| #include <linux/module.h> |
| #include <linux/scatterlist.h> |
| #include <linux/slab.h> |
| #include <linux/sched/signal.h> |
| #include <asm/page.h> |
| |
| #include "heap-helpers.h" |
| |
| struct dma_heap *sys_heap; |
| |
| static void system_heap_free(struct heap_helper_buffer *buffer) |
| { |
| pgoff_t pg; |
| |
| for (pg = 0; pg < buffer->pagecount; pg++) |
| __free_page(buffer->pages[pg]); |
| kfree(buffer->pages); |
| kfree(buffer); |
| } |
| |
| static int system_heap_allocate(struct dma_heap *heap, |
| unsigned long len, |
| unsigned long fd_flags, |
| unsigned long heap_flags) |
| { |
| struct heap_helper_buffer *helper_buffer; |
| struct dma_buf *dmabuf; |
| int ret = -ENOMEM; |
| pgoff_t pg; |
| |
| helper_buffer = kzalloc(sizeof(*helper_buffer), GFP_KERNEL); |
| if (!helper_buffer) |
| return -ENOMEM; |
| |
| init_heap_helper_buffer(helper_buffer, system_heap_free); |
| helper_buffer->heap = heap; |
| helper_buffer->size = len; |
| |
| helper_buffer->pagecount = len / PAGE_SIZE; |
| helper_buffer->pages = kmalloc_array(helper_buffer->pagecount, |
| sizeof(*helper_buffer->pages), |
| GFP_KERNEL); |
| if (!helper_buffer->pages) { |
| ret = -ENOMEM; |
| goto err0; |
| } |
| |
| for (pg = 0; pg < helper_buffer->pagecount; pg++) { |
| /* |
| * Avoid trying to allocate memory if the process |
| * has been killed by by SIGKILL |
| */ |
| if (fatal_signal_pending(current)) |
| goto err1; |
| |
| helper_buffer->pages[pg] = alloc_page(GFP_KERNEL | __GFP_ZERO); |
| if (!helper_buffer->pages[pg]) |
| goto err1; |
| } |
| |
| /* create the dmabuf */ |
| dmabuf = heap_helper_export_dmabuf(helper_buffer, fd_flags); |
| if (IS_ERR(dmabuf)) { |
| ret = PTR_ERR(dmabuf); |
| goto err1; |
| } |
| |
| helper_buffer->dmabuf = dmabuf; |
| |
| ret = dma_buf_fd(dmabuf, fd_flags); |
| if (ret < 0) { |
| dma_buf_put(dmabuf); |
| /* just return, as put will call release and that will free */ |
| return ret; |
| } |
| |
| return ret; |
| |
| err1: |
| while (pg > 0) |
| __free_page(helper_buffer->pages[--pg]); |
| kfree(helper_buffer->pages); |
| err0: |
| kfree(helper_buffer); |
| |
| return ret; |
| } |
| |
| static const struct dma_heap_ops system_heap_ops = { |
| .allocate = system_heap_allocate, |
| }; |
| |
| static int system_heap_create(void) |
| { |
| struct dma_heap_export_info exp_info; |
| int ret = 0; |
| |
| exp_info.name = "system"; |
| exp_info.ops = &system_heap_ops; |
| exp_info.priv = NULL; |
| |
| sys_heap = dma_heap_add(&exp_info); |
| if (IS_ERR(sys_heap)) |
| ret = PTR_ERR(sys_heap); |
| |
| return ret; |
| } |
| module_init(system_heap_create); |
| MODULE_LICENSE("GPL v2"); |