| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * ION Memory Allocator system contig heap exporter |
| * |
| * Copyright (C) 2019 Google, Inc. |
| */ |
| |
| #include <asm/page.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/err.h> |
| #include <linux/highmem.h> |
| #include <linux/ion.h> |
| #include <linux/mm.h> |
| #include <linux/module.h> |
| #include <linux/scatterlist.h> |
| #include <linux/slab.h> |
| |
| static int ion_system_contig_heap_allocate(struct ion_heap *heap, |
| struct ion_buffer *buffer, |
| unsigned long len, |
| unsigned long flags) |
| { |
| int order = get_order(len); |
| struct page *page; |
| struct sg_table *table; |
| unsigned long i; |
| int ret; |
| |
| page = alloc_pages(GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN, order); |
| if (!page) |
| return -ENOMEM; |
| |
| split_page(page, order); |
| |
| len = PAGE_ALIGN(len); |
| for (i = len >> PAGE_SHIFT; i < (1 << order); i++) |
| __free_page(page + i); |
| |
| table = kmalloc(sizeof(*table), GFP_KERNEL); |
| if (!table) { |
| ret = -ENOMEM; |
| goto free_pages; |
| } |
| |
| ret = sg_alloc_table(table, 1, GFP_KERNEL); |
| if (ret) |
| goto free_table; |
| |
| sg_set_page(table->sgl, page, len, 0); |
| |
| buffer->sg_table = table; |
| |
| ion_buffer_prep_noncached(buffer); |
| |
| return 0; |
| |
| free_table: |
| kfree(table); |
| free_pages: |
| for (i = 0; i < len >> PAGE_SHIFT; i++) |
| __free_page(page + i); |
| |
| return ret; |
| } |
| |
| static void ion_system_contig_heap_free(struct ion_buffer *buffer) |
| { |
| struct sg_table *table = buffer->sg_table; |
| struct page *page = sg_page(table->sgl); |
| unsigned long pages = PAGE_ALIGN(buffer->size) >> PAGE_SHIFT; |
| unsigned long i; |
| |
| for (i = 0; i < pages; i++) |
| __free_page(page + i); |
| sg_free_table(table); |
| kfree(table); |
| } |
| |
| static struct ion_heap_ops kmalloc_ops = { |
| .allocate = ion_system_contig_heap_allocate, |
| .free = ion_system_contig_heap_free, |
| }; |
| |
| static struct ion_heap contig_heap = { |
| .ops = &kmalloc_ops, |
| .type = ION_HEAP_TYPE_SYSTEM_CONTIG, |
| .name = "ion_system_contig_heap", |
| }; |
| |
| static int __init ion_system_contig_heap_init(void) |
| { |
| return ion_device_add_heap(&contig_heap); |
| } |
| |
| static void __exit ion_system_contig_heap_exit(void) |
| { |
| ion_device_remove_heap(&contig_heap); |
| } |
| |
| module_init(ion_system_contig_heap_init); |
| module_exit(ion_system_contig_heap_exit); |
| MODULE_LICENSE("GPL v2"); |