Merge branch 'upstream/android-3.10' into linaro-fixes/android-3.10

Pull in updates from AOSP, mostly focused on ION

Signed-off-by: John Stultz <john.stultz@linaro.org>
This commit is contained in:
John Stultz 2013-12-12 16:31:19 -08:00
commit 5ef3c884d2
16 changed files with 760 additions and 334 deletions

View File

@ -22,13 +22,14 @@
/* fiq stack: r0-r15,cpsr,spsr of interrupted mode */
ENTRY(fiq_glue)
/* store pc, cpsr from previous mode */
/* store pc, cpsr from previous mode, reserve space for spsr */
mrs r12, spsr
sub r11, lr, #4
sub lr, lr, #4
subs r10, #1
bne nested_fiq
stmfd sp!, {r11-r12, lr}
str r12, [sp, #-8]!
str lr, [sp, #-4]!
/* store r8-r14 from previous mode */
sub sp, sp, #(7 * 4)
@ -85,12 +86,15 @@ fiq_from_usr_mode_exit:
msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
ldmfd sp!, {r0-r7}
add sp, sp, #(7 * 4)
ldmfd sp!, {r11-r12, lr}
ldr lr, [sp, #(4 * 7)]
ldr r12, [sp, #(4 * 8)]
add sp, sp, #(10 * 4)
exit_fiq:
msr spsr_cxsf, r12
add r10, #1
movs pc, r11
cmp r11, #0
moveqs pc, lr
bx r11 /* jump to custom fiq return function */
nested_fiq:
orr r12, r12, #(PSR_F_BIT)
@ -98,14 +102,17 @@ nested_fiq:
fiq_glue_end:
ENTRY(fiq_glue_setup) /* func, data, sp */
mrs r3, cpsr
ENTRY(fiq_glue_setup) /* func, data, sp, smc call number */
stmfd sp!, {r4}
mrs r4, cpsr
msr cpsr_c, #(FIQ_MODE | PSR_I_BIT | PSR_F_BIT)
movs r8, r0
mov r9, r1
mov sp, r2
mov r11, r3
moveq r10, #0
movne r10, #1
msr cpsr_c, r3
msr cpsr_c, r4
ldmfd sp!, {r4}
bx lr

View File

@ -18,20 +18,23 @@
#include <asm/fiq_glue.h>
extern unsigned char fiq_glue, fiq_glue_end;
extern void fiq_glue_setup(void *func, void *data, void *sp);
extern void fiq_glue_setup(void *func, void *data, void *sp,
fiq_return_handler_t fiq_return_handler);
static struct fiq_handler fiq_debbuger_fiq_handler = {
.name = "fiq_glue",
};
DEFINE_PER_CPU(void *, fiq_stack);
static struct fiq_glue_handler *current_handler;
static fiq_return_handler_t fiq_return_handler;
static DEFINE_MUTEX(fiq_glue_lock);
static void fiq_glue_setup_helper(void *info)
{
struct fiq_glue_handler *handler = info;
fiq_glue_setup(handler->fiq, handler,
__get_cpu_var(fiq_stack) + THREAD_START_SP);
__get_cpu_var(fiq_stack) + THREAD_START_SP,
fiq_return_handler);
}
int fiq_glue_register_handler(struct fiq_glue_handler *handler)
@ -80,6 +83,49 @@ int fiq_glue_register_handler(struct fiq_glue_handler *handler)
return ret;
}
static void fiq_glue_update_return_handler(void (*fiq_return)(void))
{
fiq_return_handler = fiq_return;
if (current_handler)
on_each_cpu(fiq_glue_setup_helper, current_handler, true);
}
int fiq_glue_set_return_handler(void (*fiq_return)(void))
{
int ret;
mutex_lock(&fiq_glue_lock);
if (fiq_return_handler) {
ret = -EBUSY;
goto err_busy;
}
fiq_glue_update_return_handler(fiq_return);
ret = 0;
err_busy:
mutex_unlock(&fiq_glue_lock);
return ret;
}
EXPORT_SYMBOL(fiq_glue_set_return_handler);
int fiq_glue_clear_return_handler(void (*fiq_return)(void))
{
int ret;
mutex_lock(&fiq_glue_lock);
if (WARN_ON(fiq_return_handler != fiq_return)) {
ret = -EINVAL;
goto err_inval;
}
fiq_glue_update_return_handler(NULL);
ret = 0;
err_inval:
mutex_unlock(&fiq_glue_lock);
return ret;
}
EXPORT_SYMBOL(fiq_glue_clear_return_handler);
/**
* fiq_glue_resume - Restore fiqs after suspend or low power idle states
*
@ -93,7 +139,8 @@ void fiq_glue_resume(void)
if (!current_handler)
return;
fiq_glue_setup(current_handler->fiq, current_handler,
__get_cpu_var(fiq_stack) + THREAD_START_SP);
__get_cpu_var(fiq_stack) + THREAD_START_SP,
fiq_return_handler);
if (current_handler->resume)
current_handler->resume(current_handler);
}

View File

@ -18,8 +18,11 @@ struct fiq_glue_handler {
void (*fiq)(struct fiq_glue_handler *h, void *regs, void *svc_sp);
void (*resume)(struct fiq_glue_handler *h);
};
typedef void (*fiq_return_handler_t)(void);
int fiq_glue_register_handler(struct fiq_glue_handler *handler);
int fiq_glue_set_return_handler(fiq_return_handler_t fiq_return);
int fiq_glue_clear_return_handler(fiq_return_handler_t fiq_return);
#ifdef CONFIG_FIQ_GLUE
void fiq_glue_resume(void);

View File

@ -1,11 +1,17 @@
menuconfig ION
tristate "Ion Memory Manager"
depends on ARM
select GENERIC_ALLOCATOR
select DMA_SHARED_BUFFER
help
Chose this option to enable the ION Memory Manager.
config ION_TEST
tristate "Ion Test Device"
depends on ION
help
Choose this option to create a device that can be used to test the
kernel and device side ION functions.
config ION_TEGRA
tristate "Ion for Tegra"
depends on ARCH_TEGRA && ION

View File

@ -1,5 +1,6 @@
obj-$(CONFIG_ION) += ion.o ion_heap.o ion_page_pool.o ion_system_heap.o \
ion_carveout_heap.o ion_chunk_heap.o ion_cma_heap.o
obj-$(CONFIG_ION_TEST) += ion_test.o
ifdef CONFIG_COMPAT
obj-$(CONFIG_ION) += compat_ion.o
endif

View File

@ -432,22 +432,16 @@ static bool ion_handle_validate(struct ion_client *client, struct ion_handle *ha
static int ion_handle_add(struct ion_client *client, struct ion_handle *handle)
{
int rc;
int id;
struct rb_node **p = &client->handles.rb_node;
struct rb_node *parent = NULL;
struct ion_handle *entry;
do {
int id;
rc = idr_pre_get(&client->idr, GFP_KERNEL);
if (!rc)
return -ENOMEM;
rc = idr_get_new_above(&client->idr, handle, 1, &id);
handle->id = id;
} while (rc == -EAGAIN);
id = idr_alloc(&client->idr, handle, 1, 0, GFP_KERNEL);
if (id < 0)
return id;
if (rc < 0)
return rc;
handle->id = id;
while (*p) {
parent = *p;
@ -477,7 +471,7 @@ struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
struct ion_heap *heap;
int ret;
pr_debug("%s: len %d align %d heap_id_mask %u flags %x\n", __func__,
pr_debug("%s: len %zu align %zu heap_id_mask %u flags %x\n", __func__,
len, align, heap_id_mask, flags);
/*
* traverse the list of heaps available in this system in priority
@ -675,7 +669,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused)
struct ion_client *client = s->private;
struct rb_node *n;
size_t sizes[ION_NUM_HEAP_IDS] = {0};
const char *names[ION_NUM_HEAP_IDS] = {0};
const char *names[ION_NUM_HEAP_IDS] = {NULL};
int i;
mutex_lock(&client->lock);
@ -694,7 +688,7 @@ static int ion_debug_client_show(struct seq_file *s, void *unused)
for (i = 0; i < ION_NUM_HEAP_IDS; i++) {
if (!names[i])
continue;
seq_printf(s, "%16.16s: %16u\n", names[i], sizes[i]);
seq_printf(s, "%16.16s: %16zu\n", names[i], sizes[i]);
}
return 0;
}
@ -786,7 +780,6 @@ void ion_client_destroy(struct ion_client *client)
ion_handle_destroy(&handle->ref);
}
idr_remove_all(&client->idr);
idr_destroy(&client->idr);
down_write(&dev->lock);
@ -894,17 +887,18 @@ static void ion_buffer_sync_for_device(struct ion_buffer *buffer,
mutex_unlock(&buffer->lock);
}
int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
static int ion_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct ion_buffer *buffer = vma->vm_private_data;
unsigned long pfn;
int ret;
mutex_lock(&buffer->lock);
ion_buffer_page_dirty(buffer->pages + vmf->pgoff);
BUG_ON(!buffer->pages || !buffer->pages[vmf->pgoff]);
ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address,
ion_buffer_page(buffer->pages[vmf->pgoff]));
pfn = page_to_pfn(ion_buffer_page(buffer->pages[vmf->pgoff]));
ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
mutex_unlock(&buffer->lock);
if (ret)
return VM_FAULT_ERROR;
@ -945,7 +939,7 @@ static void ion_vm_close(struct vm_area_struct *vma)
mutex_unlock(&buffer->lock);
}
struct vm_operations_struct ion_vma_ops = {
static struct vm_operations_struct ion_vma_ops = {
.open = ion_vm_open,
.close = ion_vm_close,
.fault = ion_vm_fault,
@ -963,6 +957,8 @@ static int ion_mmap(struct dma_buf *dmabuf, struct vm_area_struct *vma)
}
if (ion_buffer_fault_user_mappings(buffer)) {
vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND |
VM_DONTDUMP;
vma->vm_private_data = buffer;
vma->vm_ops = &ion_vma_ops;
ion_vm_open(vma);
@ -1034,7 +1030,7 @@ static void ion_dma_buf_end_cpu_access(struct dma_buf *dmabuf, size_t start,
mutex_unlock(&buffer->lock);
}
struct dma_buf_ops dma_buf_ops = {
static struct dma_buf_ops dma_buf_ops = {
.map_dma_buf = ion_map_dma_buf,
.unmap_dma_buf = ion_unmap_dma_buf,
.mmap = ion_mmap,
@ -1164,41 +1160,65 @@ static int ion_sync_for_device(struct ion_client *client, int fd)
return 0;
}
/* fix up the cases where the ioctl direction bits are incorrect */
static unsigned int ion_ioctl_dir(unsigned int cmd)
{
switch (cmd) {
case ION_IOC_SYNC:
case ION_IOC_FREE:
case ION_IOC_CUSTOM:
return _IOC_WRITE;
default:
return _IOC_DIR(cmd);
}
}
static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct ion_client *client = filp->private_data;
struct ion_device *dev = client->dev;
struct ion_handle *cleanup_handle = NULL;
int ret = 0;
unsigned int dir;
union {
struct ion_fd_data fd;
struct ion_allocation_data allocation;
struct ion_handle_data handle;
struct ion_custom_data custom;
} data;
dir = ion_ioctl_dir(cmd);
if (_IOC_SIZE(cmd) > sizeof(data))
return -EINVAL;
if (dir & _IOC_WRITE)
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
switch (cmd) {
case ION_IOC_ALLOC:
{
struct ion_allocation_data data;
struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
return -EFAULT;
handle = ion_alloc(client, data.len, data.align,
data.heap_id_mask, data.flags);
handle = ion_alloc(client, data.allocation.len,
data.allocation.align,
data.allocation.heap_id_mask,
data.allocation.flags);
if (IS_ERR(handle))
return PTR_ERR(handle);
data.handle = handle->id;
data.allocation.handle = handle->id;
if (copy_to_user((void __user *)arg, &data, sizeof(data))) {
ion_free(client, handle);
return -EFAULT;
}
cleanup_handle = handle;
break;
}
case ION_IOC_FREE:
{
struct ion_handle_data data;
struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_handle_data)))
return -EFAULT;
handle = ion_handle_get_by_id(client, data.handle);
handle = ion_handle_get_by_id(client, data.handle.handle);
if (IS_ERR(handle))
return PTR_ERR(handle);
ion_free(client, handle);
@ -1208,68 +1228,52 @@ static long ion_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
case ION_IOC_SHARE:
case ION_IOC_MAP:
{
struct ion_fd_data data;
struct ion_handle *handle;
if (copy_from_user(&data, (void __user *)arg, sizeof(data)))
return -EFAULT;
handle = ion_handle_get_by_id(client, data.handle);
handle = ion_handle_get_by_id(client, data.handle.handle);
if (IS_ERR(handle))
return PTR_ERR(handle);
data.fd = ion_share_dma_buf_fd(client, handle);
data.fd.fd = ion_share_dma_buf_fd(client, handle);
ion_handle_put(handle);
if (copy_to_user((void __user *)arg, &data, sizeof(data)))
return -EFAULT;
if (data.fd < 0)
return data.fd;
if (data.fd.fd < 0)
ret = data.fd.fd;
break;
}
case ION_IOC_IMPORT:
{
struct ion_fd_data data;
struct ion_handle *handle;
int ret = 0;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_fd_data)))
return -EFAULT;
handle = ion_import_dma_buf(client, data.fd);
handle = ion_import_dma_buf(client, data.fd.fd);
if (IS_ERR(handle))
ret = PTR_ERR(handle);
else
data.handle = handle->id;
if (copy_to_user((void __user *)arg, &data,
sizeof(struct ion_fd_data)))
return -EFAULT;
if (ret < 0)
return ret;
data.handle.handle = handle->id;
break;
}
case ION_IOC_SYNC:
{
struct ion_fd_data data;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_fd_data)))
return -EFAULT;
ion_sync_for_device(client, data.fd);
ret = ion_sync_for_device(client, data.fd.fd);
break;
}
case ION_IOC_CUSTOM:
{
struct ion_device *dev = client->dev;
struct ion_custom_data data;
if (!dev->custom_ioctl)
return -ENOTTY;
if (copy_from_user(&data, (void __user *)arg,
sizeof(struct ion_custom_data)))
return -EFAULT;
return dev->custom_ioctl(client, data.cmd, data.arg);
ret = dev->custom_ioctl(client, data.custom.cmd,
data.custom.arg);
break;
}
default:
return -ENOTTY;
}
return 0;
if (dir & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, _IOC_SIZE(cmd))) {
if (cleanup_handle)
ion_free(client, cleanup_handle);
return -EFAULT;
}
}
return ret;
}
static int ion_release(struct inode *inode, struct file *file)
@ -1343,10 +1347,10 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
char task_comm[TASK_COMM_LEN];
get_task_comm(task_comm, client->task);
seq_printf(s, "%16.s %16u %16u\n", task_comm,
seq_printf(s, "%16.s %16u %16zu\n", task_comm,
client->pid, size);
} else {
seq_printf(s, "%16.s %16u %16u\n", client->name,
seq_printf(s, "%16.s %16u %16zu\n", client->name,
client->pid, size);
}
}
@ -1361,19 +1365,20 @@ static int ion_debug_heap_show(struct seq_file *s, void *unused)
continue;
total_size += buffer->size;
if (!buffer->handle_count) {
seq_printf(s, "%16.s %16u %16u %d %d\n", buffer->task_comm,
buffer->pid, buffer->size, buffer->kmap_cnt,
seq_printf(s, "%16.s %16u %16zu %d %d\n",
buffer->task_comm, buffer->pid,
buffer->size, buffer->kmap_cnt,
atomic_read(&buffer->ref.refcount));
total_orphaned_size += buffer->size;
}
}
mutex_unlock(&dev->buffer_lock);
seq_printf(s, "----------------------------------------------------\n");
seq_printf(s, "%16.s %16u\n", "total orphaned",
seq_printf(s, "%16.s %16zu\n", "total orphaned",
total_orphaned_size);
seq_printf(s, "%16.s %16u\n", "total ", total_size);
seq_printf(s, "%16.s %16zu\n", "total ", total_size);
if (heap->flags & ION_HEAP_FLAG_DEFER_FREE)
seq_printf(s, "%16.s %16u\n", "deferred free",
seq_printf(s, "%16.s %16zu\n", "deferred free",
heap->free_list_size);
seq_printf(s, "----------------------------------------------------\n");
@ -1529,11 +1534,11 @@ void __init ion_reserve(struct ion_platform_data *data)
int ret = memblock_reserve(data->heaps[i].base,
data->heaps[i].size);
if (ret)
pr_err("memblock reserve of %x@%lx failed\n",
pr_err("memblock reserve of %zx@%lx failed\n",
data->heaps[i].size,
data->heaps[i].base);
}
pr_info("%s: %s reserved base %lx size %d\n", __func__,
pr_info("%s: %s reserved base %lx size %zu\n", __func__,
data->heaps[i].name,
data->heaps[i].base,
data->heaps[i].size);

View File

@ -14,7 +14,7 @@
*
*/
#include <linux/spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/genalloc.h>
#include <linux/io.h>
@ -25,8 +25,6 @@
#include "ion.h"
#include "ion_priv.h"
#include <asm/mach/map.h>
struct ion_carveout_heap {
struct ion_heap heap;
struct gen_pool *pool;
@ -62,7 +60,11 @@ static int ion_carveout_heap_phys(struct ion_heap *heap,
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
*addr = buffer->priv_phys;
struct sg_table *table = buffer->priv_virt;
struct page *page = sg_page(table->sgl);
ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
*addr = paddr;
*len = buffer->size;
return 0;
}
@ -72,91 +74,95 @@ static int ion_carveout_heap_allocate(struct ion_heap *heap,
unsigned long size, unsigned long align,
unsigned long flags)
{
buffer->priv_phys = ion_carveout_allocate(heap, size, align);
return buffer->priv_phys == ION_CARVEOUT_ALLOCATE_FAIL ? -ENOMEM : 0;
struct sg_table *table;
ion_phys_addr_t paddr;
int ret;
if (align > PAGE_SIZE)
return -EINVAL;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return -ENOMEM;
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret)
goto err_free;
paddr = ion_carveout_allocate(heap, size, align);
if (paddr == ION_CARVEOUT_ALLOCATE_FAIL) {
ret = -ENOMEM;
goto err_free_table;
}
sg_set_page(table->sgl, pfn_to_page(PFN_DOWN(paddr)), size, 0);
buffer->priv_virt = table;
return 0;
err_free_table:
sg_free_table(table);
err_free:
kfree(table);
return ret;
}
static void ion_carveout_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
struct sg_table *table = buffer->priv_virt;
struct page *page = sg_page(table->sgl);
ion_phys_addr_t paddr = PFN_PHYS(page_to_pfn(page));
ion_carveout_free(heap, buffer->priv_phys, buffer->size);
buffer->priv_phys = ION_CARVEOUT_ALLOCATE_FAIL;
ion_heap_buffer_zero(buffer);
if (ion_buffer_cached(buffer))
dma_sync_sg_for_device(NULL, table->sgl, table->nents,
DMA_BIDIRECTIONAL);
ion_carveout_free(heap, paddr, buffer->size);
sg_free_table(table);
kfree(table);
}
struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static struct sg_table *ion_carveout_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret) {
kfree(table);
return ERR_PTR(ret);
}
sg_set_page(table->sgl, phys_to_page(buffer->priv_phys), buffer->size,
0);
return table;
return buffer->priv_virt;
}
void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static void ion_carveout_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
sg_free_table(buffer->sg_table);
}
void *ion_carveout_heap_map_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
void *ret;
int mtype = MT_MEMORY_NONCACHED;
if (buffer->flags & ION_FLAG_CACHED)
mtype = MT_MEMORY;
ret = __arm_ioremap(buffer->priv_phys, buffer->size,
mtype);
if (ret == NULL)
return ERR_PTR(-ENOMEM);
return ret;
}
void ion_carveout_heap_unmap_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
__arm_iounmap(buffer->vaddr);
buffer->vaddr = NULL;
return;
}
int ion_carveout_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
struct vm_area_struct *vma)
{
return remap_pfn_range(vma, vma->vm_start,
__phys_to_pfn(buffer->priv_phys) + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
pgprot_noncached(vma->vm_page_prot));
}
static struct ion_heap_ops carveout_heap_ops = {
.allocate = ion_carveout_heap_allocate,
.free = ion_carveout_heap_free,
.phys = ion_carveout_heap_phys,
.map_dma = ion_carveout_heap_map_dma,
.unmap_dma = ion_carveout_heap_unmap_dma,
.map_user = ion_carveout_heap_map_user,
.map_kernel = ion_carveout_heap_map_kernel,
.unmap_kernel = ion_carveout_heap_unmap_kernel,
.map_user = ion_heap_map_user,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
};
struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_carveout_heap *carveout_heap;
int ret;
struct page *page;
size_t size;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
ion_pages_sync_for_device(NULL, page, size, DMA_BIDIRECTIONAL);
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
return ERR_PTR(ret);
carveout_heap = kzalloc(sizeof(struct ion_carveout_heap), GFP_KERNEL);
if (!carveout_heap)
@ -172,6 +178,7 @@ struct ion_heap *ion_carveout_heap_create(struct ion_platform_heap *heap_data)
-1);
carveout_heap->heap.ops = &carveout_heap_ops;
carveout_heap->heap.type = ION_HEAP_TYPE_CARVEOUT;
carveout_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
return &carveout_heap->heap;
}

View File

@ -25,8 +25,6 @@
#include "ion.h"
#include "ion_priv.h"
#include <asm/mach/map.h>
struct ion_chunk_heap {
struct ion_heap heap;
struct gen_pool *pool;
@ -49,8 +47,8 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
unsigned long num_chunks;
unsigned long allocated_size;
if (ion_buffer_fault_user_mappings(buffer))
return -ENOMEM;
if (align > chunk_heap->chunk_size)
return -EINVAL;
allocated_size = ALIGN(size, chunk_heap->chunk_size);
num_chunks = allocated_size / chunk_heap->chunk_size;
@ -73,7 +71,8 @@ static int ion_chunk_heap_allocate(struct ion_heap *heap,
chunk_heap->chunk_size);
if (!paddr)
goto err;
sg_set_page(sg, phys_to_page(paddr), chunk_heap->chunk_size, 0);
sg_set_page(sg, pfn_to_page(PFN_DOWN(paddr)),
chunk_heap->chunk_size, 0);
sg = sg_next(sg);
}
@ -119,14 +118,14 @@ static void ion_chunk_heap_free(struct ion_buffer *buffer)
kfree(table);
}
struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static struct sg_table *ion_chunk_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return buffer->priv_virt;
}
void ion_chunk_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static void ion_chunk_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return;
}
@ -144,9 +143,18 @@ static struct ion_heap_ops chunk_heap_ops = {
struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
{
struct ion_chunk_heap *chunk_heap;
struct vm_struct *vm_struct;
pgprot_t pgprot = pgprot_writecombine(PAGE_KERNEL);
int i, ret;
int ret;
struct page *page;
size_t size;
page = pfn_to_page(PFN_DOWN(heap_data->base));
size = heap_data->size;
ion_pages_sync_for_device(NULL, page, size, DMA_BIDIRECTIONAL);
ret = ion_heap_pages_zero(page, size, pgprot_writecombine(PAGE_KERNEL));
if (ret)
return ERR_PTR(ret);
chunk_heap = kzalloc(sizeof(struct ion_chunk_heap), GFP_KERNEL);
if (!chunk_heap)
@ -163,39 +171,15 @@ struct ion_heap *ion_chunk_heap_create(struct ion_platform_heap *heap_data)
chunk_heap->size = heap_data->size;
chunk_heap->allocated = 0;
vm_struct = get_vm_area(PAGE_SIZE, VM_ALLOC);
if (!vm_struct) {
ret = -ENOMEM;
goto error;
}
for (i = 0; i < chunk_heap->size; i += PAGE_SIZE) {
struct page *page = phys_to_page(chunk_heap->base + i);
struct page **pages = &page;
ret = map_vm_area(vm_struct, pgprot, &pages);
if (ret)
goto error_map_vm_area;
memset(vm_struct->addr, 0, PAGE_SIZE);
unmap_kernel_range((unsigned long)vm_struct->addr, PAGE_SIZE);
}
free_vm_area(vm_struct);
ion_pages_sync_for_device(NULL, pfn_to_page(PFN_DOWN(heap_data->base)),
heap_data->size, DMA_BIDIRECTIONAL);
gen_pool_add(chunk_heap->pool, chunk_heap->base, heap_data->size, -1);
chunk_heap->heap.ops = &chunk_heap_ops;
chunk_heap->heap.type = ION_HEAP_TYPE_CHUNK;
chunk_heap->heap.flags = ION_HEAP_FLAG_DEFER_FREE;
pr_info("%s: base %lu size %zu align %ld\n", __func__,
chunk_heap->base, heap_data->size, heap_data->align);
pr_info("%s: base %lu size %zu align %ld\n", __func__, chunk_heap->base,
heap_data->size, heap_data->align);
return &chunk_heap->heap;
error_map_vm_area:
free_vm_area(vm_struct);
error:
gen_pool_destroy(chunk_heap->pool);
error_gen_pool_create:
kfree(chunk_heap);
return ERR_PTR(ret);

View File

@ -44,8 +44,8 @@ struct ion_cma_buffer_info {
* This function could be replaced by dma_common_get_sgtable
* as soon as it will avalaible.
*/
int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t handle, size_t size)
static int ion_cma_get_sgtable(struct device *dev, struct sg_table *sgt,
void *cpu_addr, dma_addr_t handle, size_t size)
{
struct page *page = virt_to_page(cpu_addr);
int ret;
@ -69,13 +69,20 @@ static int ion_cma_allocate(struct ion_heap *heap, struct ion_buffer *buffer,
dev_dbg(dev, "Request buffer allocation len %ld\n", len);
if (buffer->flags & ION_FLAG_CACHED)
return -EINVAL;
if (align > PAGE_SIZE)
return -EINVAL;
info = kzalloc(sizeof(struct ion_cma_buffer_info), GFP_KERNEL);
if (!info) {
dev_err(dev, "Can't allocate buffer info\n");
return ION_CMA_ALLOCATE_FAILED;
}
info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle), 0);
info->cpu_addr = dma_alloc_coherent(dev, len, &(info->handle),
GFP_HIGHUSER | __GFP_ZERO);
if (!info->cpu_addr) {
dev_err(dev, "Fail to allocate buffer\n");
@ -128,8 +135,8 @@ static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
struct device *dev = cma_heap->dev;
struct ion_cma_buffer_info *info = buffer->priv_virt;
dev_dbg(dev, "Return buffer %p physical address 0x%x\n", buffer,
info->handle);
dev_dbg(dev, "Return buffer %p physical address 0x%pa\n", buffer,
&info->handle);
*addr = info->handle;
*len = buffer->size;
@ -137,16 +144,16 @@ static int ion_cma_phys(struct ion_heap *heap, struct ion_buffer *buffer,
return 0;
}
struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static struct sg_table *ion_cma_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct ion_cma_buffer_info *info = buffer->priv_virt;
return info->table;
}
void ion_cma_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static void ion_cma_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return;
}
@ -162,13 +169,19 @@ static int ion_cma_mmap(struct ion_heap *mapper, struct ion_buffer *buffer,
buffer->size);
}
void *ion_cma_map_kernel(struct ion_heap *heap, struct ion_buffer *buffer)
static void *ion_cma_map_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct ion_cma_buffer_info *info = buffer->priv_virt;
/* kernel memory mapping has been done at allocation time */
return info->cpu_addr;
}
static void ion_cma_unmap_kernel(struct ion_heap *heap,
struct ion_buffer *buffer)
{
}
static struct ion_heap_ops ion_cma_ops = {
.allocate = ion_cma_allocate,
.free = ion_cma_free,
@ -177,6 +190,7 @@ static struct ion_heap_ops ion_cma_ops = {
.phys = ion_cma_phys,
.map_user = ion_cma_mmap,
.map_kernel = ion_cma_map_kernel,
.unmap_kernel = ion_cma_unmap_kernel,
};
struct ion_heap *ion_cma_heap_create(struct ion_platform_heap *data)

View File

@ -38,7 +38,7 @@ void *ion_heap_map_kernel(struct ion_heap *heap,
struct page **tmp = pages;
if (!pages)
return 0;
return NULL;
if (buffer->flags & ION_FLAG_CACHED)
pgprot = PAGE_KERNEL;
@ -76,6 +76,7 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
unsigned long offset = vma->vm_pgoff * PAGE_SIZE;
struct scatterlist *sg;
int i;
int ret;
for_each_sg(table->sgl, sg, table->nents, i) {
struct page *page = sg_page(sg);
@ -91,8 +92,10 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
offset = 0;
}
len = min(len, remainder);
remap_pfn_range(vma, addr, page_to_pfn(page), len,
ret = remap_pfn_range(vma, addr, page_to_pfn(page), len,
vma->vm_page_prot);
if (ret)
return ret;
addr += len;
if (addr >= vma->vm_end)
return 0;
@ -100,68 +103,60 @@ int ion_heap_map_user(struct ion_heap *heap, struct ion_buffer *buffer,
return 0;
}
static int ion_heap_clear_pages(struct page **pages, int num, pgprot_t pgprot)
{
void *addr = vm_map_ram(pages, num, -1, pgprot);
if (!addr)
return -ENOMEM;
memset(addr, 0, PAGE_SIZE * num);
vm_unmap_ram(addr, num);
return 0;
}
static int ion_heap_sglist_zero(struct scatterlist *sgl, unsigned int nents,
pgprot_t pgprot)
{
int p = 0;
int ret = 0;
struct sg_page_iter piter;
struct page *pages[32];
for_each_sg_page(sgl, &piter, nents, 0) {
pages[p++] = sg_page_iter_page(&piter);
if (p == ARRAY_SIZE(pages)) {
ret = ion_heap_clear_pages(pages, p, pgprot);
if (ret)
return ret;
p = 0;
}
}
if (p)
ret = ion_heap_clear_pages(pages, p, pgprot);
return ret;
}
int ion_heap_buffer_zero(struct ion_buffer *buffer)
{
struct sg_table *table = buffer->sg_table;
pgprot_t pgprot;
struct scatterlist *sg;
struct vm_struct *vm_struct;
int i, j, ret = 0;
if (buffer->flags & ION_FLAG_CACHED)
pgprot = PAGE_KERNEL;
else
pgprot = pgprot_writecombine(PAGE_KERNEL);
vm_struct = get_vm_area(PAGE_SIZE, VM_ALLOC);
if (!vm_struct)
return -ENOMEM;
for_each_sg(table->sgl, sg, table->nents, i) {
struct page *page = sg_page(sg);
unsigned long len = sg->length;
for (j = 0; j < len / PAGE_SIZE; j++) {
struct page *sub_page = page + j;
struct page **pages = &sub_page;
ret = map_vm_area(vm_struct, pgprot, &pages);
if (ret)
goto end;
memset(vm_struct->addr, 0, PAGE_SIZE);
unmap_kernel_range((unsigned long)vm_struct->addr,
PAGE_SIZE);
}
}
end:
free_vm_area(vm_struct);
return ret;
return ion_heap_sglist_zero(table->sgl, table->nents, pgprot);
}
struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags,
unsigned int order)
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot)
{
struct page *page = alloc_pages(gfp_flags, order);
struct scatterlist sg;
if (!page)
return page;
if (ion_buffer_fault_user_mappings(buffer))
split_page(page, order);
return page;
}
void ion_heap_free_pages(struct ion_buffer *buffer, struct page *page,
unsigned int order)
{
int i;
if (!ion_buffer_fault_user_mappings(buffer)) {
__free_pages(page, order);
return;
}
for (i = 0; i < (1 << order); i++)
__free_page(page + i);
sg_init_table(&sg, 1);
sg_set_page(&sg, page, size, 0);
return ion_heap_sglist_zero(&sg, 1, pgprot);
}
void ion_heap_freelist_add(struct ion_heap *heap, struct ion_buffer * buffer)
@ -200,16 +195,16 @@ size_t ion_heap_freelist_drain(struct ion_heap *heap, size_t size)
if (total_drained >= size)
break;
list_del(&buffer->list);
ion_buffer_destroy(buffer);
heap->free_list_size -= buffer->size;
total_drained += buffer->size;
ion_buffer_destroy(buffer);
}
rt_mutex_unlock(&heap->lock);
return total_drained;
}
int ion_heap_deferred_free(void *data)
static int ion_heap_deferred_free(void *data)
{
struct ion_heap *heap = data;
@ -281,7 +276,7 @@ struct ion_heap *ion_heap_create(struct ion_platform_heap *heap_data)
}
if (IS_ERR_OR_NULL(heap)) {
pr_err("%s: error creating heap %s type %d base %lu size %u\n",
pr_err("%s: error creating heap %s type %d base %lu size %zu\n",
__func__, heap_data->name, heap_data->type,
heap_data->base, heap_data->size);
return ERR_PTR(-EINVAL);

View File

@ -134,7 +134,7 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
int i;
bool high;
high = gfp_mask & __GFP_HIGHMEM;
high = !!(gfp_mask & __GFP_HIGHMEM);
if (nr_to_scan == 0)
return ion_page_pool_total(pool, high);
@ -143,10 +143,10 @@ int ion_page_pool_shrink(struct ion_page_pool *pool, gfp_t gfp_mask,
struct page *page;
mutex_lock(&pool->mutex);
if (high && pool->high_count) {
page = ion_page_pool_remove(pool, true);
} else if (pool->low_count) {
if (pool->low_count) {
page = ion_page_pool_remove(pool, false);
} else if (high && pool->high_count) {
page = ion_page_pool_remove(pool, true);
} else {
mutex_unlock(&pool->mutex);
break;

View File

@ -215,19 +215,7 @@ void ion_heap_unmap_kernel(struct ion_heap *, struct ion_buffer *);
int ion_heap_map_user(struct ion_heap *, struct ion_buffer *,
struct vm_area_struct *);
int ion_heap_buffer_zero(struct ion_buffer *buffer);
/**
* ion_heap_alloc_pages - allocate pages from alloc_pages
* @buffer: the buffer to allocate for, used to extract the flags
* @gfp_flags: the gfp_t for the allocation
* @order: the order of the allocatoin
*
* This funciton allocations from alloc pages and also does any other
* necessary operations based on the buffer->flags. For buffers which
* will be faulted in the pages are split using split_page
*/
struct page *ion_heap_alloc_pages(struct ion_buffer *buffer, gfp_t gfp_flags,
unsigned int order);
int ion_heap_pages_zero(struct page *page, size_t size, pgprot_t pgprot);
/**
* ion_heap_init_deferred_free -- initialize deferred free functionality

View File

@ -26,11 +26,9 @@
#include "ion.h"
#include "ion_priv.h"
static unsigned int high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
__GFP_NOWARN | __GFP_NORETRY) &
~__GFP_WAIT;
static unsigned int low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO |
__GFP_NOWARN);
static gfp_t high_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN |
__GFP_NORETRY) & ~__GFP_WAIT;
static gfp_t low_order_gfp_flags = (GFP_HIGHUSER | __GFP_ZERO | __GFP_NOWARN);
static const unsigned int orders[] = {8, 4, 0};
static const int num_orders = ARRAY_SIZE(orders);
static int order_to_index(unsigned int order)
@ -74,14 +72,14 @@ static struct page *alloc_buffer_page(struct ion_system_heap *heap,
if (order > 4)
gfp_flags = high_order_gfp_flags;
page = ion_heap_alloc_pages(buffer, gfp_flags, order);
page = alloc_pages(gfp_flags, order);
if (!page)
return 0;
return NULL;
ion_pages_sync_for_device(NULL, page, PAGE_SIZE << order,
DMA_BIDIRECTIONAL);
}
if (!page)
return 0;
return NULL;
return page;
}
@ -91,15 +89,10 @@ static void free_buffer_page(struct ion_system_heap *heap,
unsigned int order)
{
bool cached = ion_buffer_cached(buffer);
bool split_pages = ion_buffer_fault_user_mappings(buffer);
int i;
if (!cached) {
struct ion_page_pool *pool = heap->pools[order_to_index(order)];
ion_page_pool_free(pool, page);
} else if (split_pages) {
for (i = 0; i < (1 << order); i++)
__free_page(page + i);
} else {
__free_pages(page, order);
}
@ -150,6 +143,9 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
long size_remaining = PAGE_ALIGN(size);
unsigned int max_order = orders[0];
if (align > PAGE_SIZE)
return -EINVAL;
INIT_LIST_HEAD(&pages);
while (size_remaining > 0) {
info = alloc_largest_available(sys_heap, buffer, size_remaining, max_order);
@ -160,8 +156,7 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
max_order = info->order;
i++;
}
table = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
goto err;
@ -183,14 +178,14 @@ static int ion_system_heap_allocate(struct ion_heap *heap,
err1:
kfree(table);
err:
list_for_each_entry(info, &pages, list) {
list_for_each_entry_safe(info, tmp_info, &pages, list) {
free_buffer_page(sys_heap, buffer, info->page, info->order);
kfree(info);
}
return -ENOMEM;
}
void ion_system_heap_free(struct ion_buffer *buffer)
static void ion_system_heap_free(struct ion_buffer *buffer)
{
struct ion_heap *heap = buffer->heap;
struct ion_system_heap *sys_heap = container_of(heap,
@ -214,14 +209,14 @@ void ion_system_heap_free(struct ion_buffer *buffer)
kfree(table);
}
struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static struct sg_table *ion_system_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return buffer->priv_virt;
}
void ion_system_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static void ion_system_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
return;
}
@ -362,61 +357,83 @@ static int ion_system_contig_heap_allocate(struct ion_heap *heap,
unsigned long align,
unsigned long flags)
{
buffer->priv_virt = kzalloc(len, GFP_KERNEL);
if (!buffer->priv_virt)
int order = get_order(len);
struct page *page;
struct sg_table *table;
unsigned long i;
int ret;
if (align > (PAGE_SIZE << order))
return -EINVAL;
page = alloc_pages(low_order_gfp_flags, 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 = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table) {
ret = -ENOMEM;
goto out;
}
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret)
goto out;
sg_set_page(table->sgl, page, len, 0);
buffer->priv_virt = table;
ion_pages_sync_for_device(NULL, page, len, DMA_BIDIRECTIONAL);
return 0;
out:
for (i = 0; i < len >> PAGE_SHIFT; i++)
__free_page(page + i);
kfree(table);
return ret;
}
void ion_system_contig_heap_free(struct ion_buffer *buffer)
static void ion_system_contig_heap_free(struct ion_buffer *buffer)
{
kfree(buffer->priv_virt);
struct sg_table *table = buffer->priv_virt;
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 int ion_system_contig_heap_phys(struct ion_heap *heap,
struct ion_buffer *buffer,
ion_phys_addr_t *addr, size_t *len)
{
*addr = virt_to_phys(buffer->priv_virt);
struct sg_table *table = buffer->priv_virt;
struct page *page = sg_page(table->sgl);
*addr = page_to_phys(page);
*len = buffer->size;
return 0;
}
struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap,
static struct sg_table *ion_system_contig_heap_map_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
struct sg_table *table;
int ret;
table = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
if (!table)
return ERR_PTR(-ENOMEM);
ret = sg_alloc_table(table, 1, GFP_KERNEL);
if (ret) {
kfree(table);
return ERR_PTR(ret);
}
sg_set_page(table->sgl, virt_to_page(buffer->priv_virt), buffer->size,
0);
return table;
return buffer->priv_virt;
}
void ion_system_contig_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
static void ion_system_contig_heap_unmap_dma(struct ion_heap *heap,
struct ion_buffer *buffer)
{
sg_free_table(buffer->sg_table);
kfree(buffer->sg_table);
}
int ion_system_contig_heap_map_user(struct ion_heap *heap,
struct ion_buffer *buffer,
struct vm_area_struct *vma)
{
unsigned long pfn = __phys_to_pfn(virt_to_phys(buffer->priv_virt));
return remap_pfn_range(vma, vma->vm_start, pfn + vma->vm_pgoff,
vma->vm_end - vma->vm_start,
vma->vm_page_prot);
}
static struct ion_heap_ops kmalloc_ops = {
@ -427,7 +444,7 @@ static struct ion_heap_ops kmalloc_ops = {
.unmap_dma = ion_system_contig_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_system_contig_heap_map_user,
.map_user = ion_heap_map_user,
};
struct ion_heap *ion_system_contig_heap_create(struct ion_platform_heap *unused)

View File

@ -0,0 +1,281 @@
/*
*
* Copyright (C) 2013 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "ion-test: " fmt
#include <linux/dma-buf.h>
#include <linux/dma-direction.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/vmalloc.h>
#include "ion.h"
#include "../uapi/ion_test.h"
#define u64_to_uptr(x) ((void __user *)(unsigned long)(x))
struct ion_test_device {
struct miscdevice misc;
};
struct ion_test_data {
struct dma_buf *dma_buf;
struct device *dev;
};
static int ion_handle_test_dma(struct device *dev, struct dma_buf *dma_buf,
void __user *ptr, size_t offset, size_t size, bool write)
{
int ret = 0;
struct dma_buf_attachment *attach;
struct sg_table *table;
pgprot_t pgprot = pgprot_writecombine(PAGE_KERNEL);
enum dma_data_direction dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
struct sg_page_iter sg_iter;
unsigned long offset_page;
attach = dma_buf_attach(dma_buf, dev);
if (IS_ERR(attach))
return PTR_ERR(attach);
table = dma_buf_map_attachment(attach, dir);
if (IS_ERR(table))
return PTR_ERR(table);
offset_page = offset >> PAGE_SHIFT;
offset %= PAGE_SIZE;
for_each_sg_page(table->sgl, &sg_iter, table->nents, offset_page) {
struct page *page = sg_page_iter_page(&sg_iter);
void *vaddr = vmap(&page, 1, VM_MAP, pgprot);
size_t to_copy = PAGE_SIZE - offset;
to_copy = min(to_copy, size);
if (!vaddr) {
ret = -ENOMEM;
goto err;
}
if (write)
ret = copy_from_user(vaddr + offset, ptr, to_copy);
else
ret = copy_to_user(ptr, vaddr + offset, to_copy);
vunmap(vaddr);
if (ret) {
ret = -EFAULT;
goto err;
}
size -= to_copy;
if (!size)
break;
ptr += to_copy;
offset = 0;
}
err:
dma_buf_unmap_attachment(attach, table, dir);
dma_buf_detach(dma_buf, attach);
return ret;
}
static int ion_handle_test_kernel(struct dma_buf *dma_buf, void __user *ptr,
size_t offset, size_t size, bool write)
{
int ret;
unsigned long page_offset = offset >> PAGE_SHIFT;
size_t copy_offset = offset % PAGE_SIZE;
size_t copy_size = size;
enum dma_data_direction dir = write ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
if (offset > dma_buf->size || size > dma_buf->size - offset)
return -EINVAL;
ret = dma_buf_begin_cpu_access(dma_buf, offset, size, dir);
if (ret)
return ret;
while (copy_size > 0) {
size_t to_copy;
void *vaddr = dma_buf_kmap(dma_buf, page_offset);
if (!vaddr)
goto err;
to_copy = min_t(size_t, PAGE_SIZE - copy_offset, copy_size);
if (write)
ret = copy_from_user(vaddr + copy_offset, ptr, to_copy);
else
ret = copy_to_user(ptr, vaddr + copy_offset, to_copy);
dma_buf_kunmap(dma_buf, page_offset, vaddr);
if (ret) {
ret = -EFAULT;
goto err;
}
copy_size -= to_copy;
ptr += to_copy;
page_offset++;
copy_offset = 0;
}
err:
dma_buf_end_cpu_access(dma_buf, offset, size, dir);
return ret;
}
static long ion_test_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct ion_test_data *test_data = filp->private_data;
int ret = 0;
union {
struct ion_test_rw_data test_rw;
} data;
if (_IOC_SIZE(cmd) > sizeof(data))
return -EINVAL;
if (_IOC_DIR(cmd) & _IOC_WRITE)
if (copy_from_user(&data, (void __user *)arg, _IOC_SIZE(cmd)))
return -EFAULT;
switch (cmd) {
case ION_IOC_TEST_SET_FD:
{
struct dma_buf *dma_buf = NULL;
int fd = arg;
if (fd >= 0) {
dma_buf = dma_buf_get((int)arg);
if (IS_ERR(dma_buf))
return PTR_ERR(dma_buf);
}
if (test_data->dma_buf)
dma_buf_put(test_data->dma_buf);
test_data->dma_buf = dma_buf;
break;
}
case ION_IOC_TEST_DMA_MAPPING:
{
ret = ion_handle_test_dma(test_data->dev, test_data->dma_buf,
u64_to_uptr(data.test_rw.ptr),
data.test_rw.offset, data.test_rw.size,
data.test_rw.write);
break;
}
case ION_IOC_TEST_KERNEL_MAPPING:
{
ret = ion_handle_test_kernel(test_data->dma_buf,
u64_to_uptr(data.test_rw.ptr),
data.test_rw.offset, data.test_rw.size,
data.test_rw.write);
break;
}
default:
return -ENOTTY;
}
if (_IOC_DIR(cmd) & _IOC_READ) {
if (copy_to_user((void __user *)arg, &data, sizeof(data)))
return -EFAULT;
}
return ret;
}
static int ion_test_open(struct inode *inode, struct file *file)
{
struct ion_test_data *data;
struct miscdevice *miscdev = file->private_data;
data = kzalloc(sizeof(struct ion_test_data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->dev = miscdev->parent;
file->private_data = data;
return 0;
}
static int ion_test_release(struct inode *inode, struct file *file)
{
struct ion_test_data *data = file->private_data;
kfree(data);
return 0;
}
static const struct file_operations ion_test_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = ion_test_ioctl,
.open = ion_test_open,
.release = ion_test_release,
};
static int __init ion_test_probe(struct platform_device *pdev)
{
int ret;
struct ion_test_device *testdev;
testdev = devm_kzalloc(&pdev->dev, sizeof(struct ion_test_device),
GFP_KERNEL);
if (!testdev)
return -ENOMEM;
testdev->misc.minor = MISC_DYNAMIC_MINOR;
testdev->misc.name = "ion-test";
testdev->misc.fops = &ion_test_fops;
testdev->misc.parent = &pdev->dev;
ret = misc_register(&testdev->misc);
if (ret) {
pr_err("failed to register misc device.\n");
return ret;
}
platform_set_drvdata(pdev, testdev);
return 0;
}
static struct platform_driver ion_test_platform_driver = {
.driver = {
.name = "ion-test",
},
};
static int __init ion_test_init(void)
{
platform_device_register_simple("ion-test", -1, NULL, 0);
return platform_driver_probe(&ion_test_platform_driver, ion_test_probe);
}
static void __exit ion_test_exit(void)
{
platform_driver_unregister(&ion_test_platform_driver);
}
module_init(ion_test_init);
module_exit(ion_test_exit);

View File

@ -0,0 +1,71 @@
/*
* drivers/staging/android/uapi/ion.h
*
* Copyright (C) 2011 Google, Inc.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
* may be copied, distributed, and modified under those terms.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#ifndef _UAPI_LINUX_ION_TEST_H
#define _UAPI_LINUX_ION_TEST_H
#include <linux/ioctl.h>
#include <linux/types.h>
typedef int ion_user_handle_t;
/**
* struct ion_test_rw_data - metadata passed to the kernel to read handle
* @ptr: a pointer to an area at least as large as size
* @offset: offset into the ion buffer to start reading
* @size: size to read or write
* @write: 1 to write, 0 to read
*/
struct ion_test_rw_data {
__u64 ptr;
__u64 offset;
__u64 size;
int write;
};
#define ION_IOC_MAGIC 'I'
/**
* DOC: ION_IOC_TEST_SET_DMA_BUF - attach a dma buf to the test driver
*
* Attaches a dma buf fd to the test driver. Passing a second fd or -1 will
* release the first fd.
*/
#define ION_IOC_TEST_SET_FD \
_IO(ION_IOC_MAGIC, 0xf0)
/**
* DOC: ION_IOC_TEST_DMA_MAPPING - read or write memory from a handle as DMA
*
* Reads or writes the memory from a handle using an uncached mapping. Can be
* used by unit tests to emulate a DMA engine as close as possible. Only
* expected to be used for debugging and testing, may not always be available.
*/
#define ION_IOC_TEST_DMA_MAPPING \
_IOW(ION_IOC_MAGIC, 0xf1, struct ion_test_rw_data)
/**
* DOC: ION_IOC_TEST_KERNEL_MAPPING - read or write memory from a handle
*
* Reads or writes the memory from a handle using a kernel mapping. Can be
* used by unit tests to test heap map_kernel functions. Only expected to be
* used for debugging and testing, may not always be available.
*/
#define ION_IOC_TEST_KERNEL_MAPPING \
_IOW(ION_IOC_MAGIC, 0xf2, struct ion_test_rw_data)
#endif /* _UAPI_LINUX_ION_H */

View File

@ -1496,7 +1496,7 @@ static const struct file_operations proc_iface_stat_fmt_fops = {
.open = proc_iface_stat_fmt_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
.release = seq_release_private,
};
static int __init iface_stat_init(struct proc_dir_entry *parent_procdir)
@ -2904,7 +2904,7 @@ static const struct file_operations proc_qtaguid_ctrl_fops = {
.read = seq_read,
.write = qtaguid_ctrl_proc_write,
.llseek = seq_lseek,
.release = seq_release,
.release = seq_release_private,
};
static const struct seq_operations proc_qtaguid_stats_seqops = {