Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android的ion(二)

Android的ion(二)

編輯:關於Android編程

Android的ion(二):今天來看一下kernel裡面的ion的實現,首先知道的是system/libion下的實現都是通過ion_ioctl系統調用進入kernel裡面的,下面來看一下系統調用進入kernel裡面的處理流程。

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_handle *handle;

        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.allocation.handle = handle->id;

        cleanup_handle = handle;
        break;
    }
    case ION_IOC_FREE:
    {
        struct ion_handle *handle;

        handle = ion_handle_get_by_id(client, data.handle.handle);
        if (IS_ERR(handle))
            return PTR_ERR(handle);
        ion_free(client, handle);
        ion_handle_put(handle);
        break;
    }
    case ION_IOC_SHARE:
    case ION_IOC_MAP:
    {
        struct ion_handle *handle;

        handle = ion_handle_get_by_id(client, data.handle.handle);
        if (IS_ERR(handle))
            return PTR_ERR(handle);
        data.fd.fd = ion_share_dma_buf_fd(client, handle);
        ion_handle_put(handle);
        if (data.fd.fd < 0)
            ret = data.fd.fd;
        break;
    }
    case ION_IOC_IMPORT:
    {
        struct ion_handle *handle;
        handle = ion_import_dma_buf(client, data.fd.fd);
        if (IS_ERR(handle))
            ret = PTR_ERR(handle);
        else
            data.handle.handle = handle->id;
        break;
    }
    case ION_IOC_SYNC:
    {
        ret = ion_sync_for_device(client, data.fd.fd);
        break;
    }
    case ION_IOC_CUSTOM:
    {
        if (!dev->custom_ioctl)
            return -ENOTTY;
        ret = dev->custom_ioctl(client, data.custom.cmd,
                        data.custom.arg);
        break;
    }
    case ION_IOC_CLEAN_CACHES:
        return client->dev->custom_ioctl(client,
                        ION_IOC_CLEAN_CACHES, arg);
    case ION_IOC_INV_CACHES:
        return client->dev->custom_ioctl(client,
                        ION_IOC_INV_CACHES, arg);
    case ION_IOC_CLEAN_INV_CACHES:
        return client->dev->custom_ioctl(client,
                        ION_IOC_CLEAN_INV_CACHES, arg);
    default:
        return -ENOTTY;
    }

    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;
}

首先通過copy_from_user將用戶空間的數據轉遞到kernel裡面,然後根據cmd分別進行處理。
首先來看cmd為ION_IOC_ALLOC的,此cmd為分配內存的,詳見源碼:

    case ION_IOC_ALLOC:
    {
        struct ion_handle *handle;

        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.allocation.handle = handle->id;

        cleanup_handle = handle;
        break;
    }

可以看到的是通過ion_alloc來分配內存,

struct ion_handle *ion_alloc(struct ion_client *client, size_t len,
                 size_t align, unsigned int heap_id_mask,
                 unsigned int flags)
{
    struct ion_handle *handle;
    struct ion_device *dev = client->dev;
    struct ion_buffer *buffer = NULL;
    struct ion_heap *heap;
    int ret;
    unsigned long secure_allocation = flags & ION_FLAG_SECURE;
    const unsigned int MAX_DBG_STR_LEN = 64;
    char dbg_str[MAX_DBG_STR_LEN];
    unsigned int dbg_str_idx = 0;

    dbg_str[0] = '\0';

    /*
     * For now, we don't want to fault in pages individually since
     * clients are already doing manual cache maintenance. In
     * other words, the implicit caching infrastructure is in
     * place (in code) but should not be used.
     */
    flags |= ION_FLAG_CACHED_NEEDS_SYNC;

    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
     * order.  If the heap type is supported by the client, and matches the
     * request of the caller allocate from it.  Repeat until allocate has
     * succeeded or all heaps have been tried
     */
    len = PAGE_ALIGN(len);

    if (!len)
        return ERR_PTR(-EINVAL);

    down_read(&dev->lock);
    plist_for_each_entry(heap, &dev->heaps, node) {
        /* if the caller didn't specify this heap id */
        if (!((1 << heap->id) & heap_id_mask))
            continue;
        /* Do not allow un-secure heap if secure is specified */
        if (secure_allocation &&
            !ion_heap_allow_secure_allocation(heap->type))
            continue;
        trace_ion_alloc_buffer_start(client->name, heap->name, len,
                         heap_id_mask, flags);
        buffer = ion_buffer_create(heap, dev, len, align, flags);
        trace_ion_alloc_buffer_end(client->name, heap->name, len,
                       heap_id_mask, flags);
        if (!IS_ERR(buffer))
            break;

        trace_ion_alloc_buffer_fallback(client->name, heap->name, len,
                        heap_id_mask, flags,
                        PTR_ERR(buffer));
        if (dbg_str_idx < MAX_DBG_STR_LEN) {
            unsigned int len_left = MAX_DBG_STR_LEN-dbg_str_idx-1;
            int ret_value = snprintf(&dbg_str[dbg_str_idx],
                        len_left, "%s ", heap->name);
            if (ret_value >= len_left) {
                /* overflow */
                dbg_str[MAX_DBG_STR_LEN-1] = '\0';
                dbg_str_idx = MAX_DBG_STR_LEN;
            } else if (ret_value >= 0) {
                dbg_str_idx += ret_value;
            } else {
                /* error */
                dbg_str[MAX_DBG_STR_LEN-1] = '\0';
            }
        }
    }
    up_read(&dev->lock);

    if (buffer == NULL) {
        trace_ion_alloc_buffer_fail(client->name, dbg_str, len,
                        heap_id_mask, flags, -ENODEV);
        return ERR_PTR(-ENODEV);
    }

    if (IS_ERR(buffer)) {
        trace_ion_alloc_buffer_fail(client->name, dbg_str, len,
                        heap_id_mask, flags,
                        PTR_ERR(buffer));
        pr_debug("ION is unable to allocate 0x%zx bytes (alignment: 0x%zx) from heap(s) %sfor client %s\n",
            len, align, dbg_str, client->name);
        return ERR_PTR(PTR_ERR(buffer));
    }

    handle = ion_handle_create(client, buffer);

    /*
     * ion_buffer_create will create a buffer with a ref_cnt of 1,
     * and ion_handle_create will take a second reference, drop one here
     */
    ion_buffer_put(buffer);

    if (IS_ERR(handle))
        return handle;

    mutex_lock(&client->lock);
    ret = ion_handle_add(client, handle);
    mutex_unlock(&client->lock);
    if (ret) {
        ion_handle_put(handle);
        handle = ERR_PTR(ret);
    }

    return handle;
}

主要的工作是先是遍歷heap鏈表,然後符合條件的才會去通過ion_buffer_create創建buffer,然後通過ion_handle_create來創建對應的handle來管理buffer。
首先可以看到是通過ion_buffer_create來創建buffer的:

/* this function should only be called while dev->lock is held */
static struct ion_buffer *ion_buffer_create(struct ion_heap *heap,
                     struct ion_device *dev,
                     unsigned long len,
                     unsigned long align,
                     unsigned long flags)
{
    struct ion_buffer *buffer;
    struct sg_table *table;
    struct scatterlist *sg;
    int i, ret;

    buffer = kzalloc(sizeof(struct ion_buffer), GFP_KERNEL);
    if (!buffer)
        return ERR_PTR(-ENOMEM);

    buffer->heap = heap;
    buffer->flags = flags;
    kref_init(&buffer->ref);

    ret = heap->ops->allocate(heap, buffer, len, align, flags);

    if (ret) {
        if (!(heap->flags & ION_HEAP_FLAG_DEFER_FREE))
            goto err2;

        ion_heap_freelist_drain(heap, 0);
        ret = heap->ops->allocate(heap, buffer, len, align,
                      flags);
        if (ret)
            goto err2;
    }

    buffer->dev = dev;
    buffer->size = len;
    buffer->flags = flags;
    INIT_LIST_HEAD(&buffer->vmas);

    table = heap->ops->map_dma(heap, buffer);
    if (WARN_ONCE(table == NULL,
            "heap->ops->map_dma should return ERR_PTR on error"))
        table = ERR_PTR(-EINVAL);
    if (IS_ERR(table)) {
        heap->ops->free(buffer);
        kfree(buffer);
        return ERR_PTR(PTR_ERR(table));
    }
    buffer->sg_table = table;
    if (ion_buffer_fault_user_mappings(buffer)) {
        int num_pages = PAGE_ALIGN(buffer->size) / PAGE_SIZE;
        struct scatterlist *sg;
        int i, j, k = 0;

        buffer->pages = vmalloc(sizeof(struct page *) * num_pages);
        if (!buffer->pages) {
            ret = -ENOMEM;
            goto err1;
        }

        for_each_sg(table->sgl, sg, table->nents, i) {
            struct page *page = sg_page(sg);

            for (j = 0; j < sg->length / PAGE_SIZE; j++)
                buffer->pages[k++] = page++;
        }

        if (ret)
            goto err;
    }

    mutex_init(&buffer->lock);
    /* this will set up dma addresses for the sglist -- it is not
       technically correct as per the dma api -- a specific
       device isn't really taking ownership here.  However, in practice on
       our systems the only dma_address space is physical addresses.
       Additionally, we can't afford the overhead of invalidating every
       allocation via dma_map_sg. The implicit contract here is that
       memory comming from the heaps is ready for dma, ie if it has a
       cached mapping that mapping has been invalidated */
    for_each_sg(buffer->sg_table->sgl, sg, buffer->sg_table->nents, i) {
        if (sg_dma_address(sg) == 0)
            sg_dma_address(sg) = sg_phys(sg);
    }
    mutex_lock(&dev->buffer_lock);
    ion_buffer_add(dev, buffer);
    mutex_unlock(&dev->buffer_lock);
    atomic_add(len, &heap->total_allocated);
    return buffer;

err:
    heap->ops->unmap_dma(heap, buffer);
    heap->ops->free(buffer);
err1:
    if (buffer->pages)
        vfree(buffer->pages);
err2:
    kfree(buffer);
    return ERR_PTR(ret);
}

在ion_buffer_create中可以看到的是首先調用kzalloc來申請一個內存,然後會發現其所申請的內存都被初始化過,在接下來根據不同的heap來調用其allocate方法,最後將申請到的buffer都放到設備數下進行管理。
接著使用ion_handle_create來創建一個handle,然後將client和buffer都和handle綁定起來。

static struct ion_handle *ion_handle_create(struct ion_client *client,
                     struct ion_buffer *buffer)
{
    struct ion_handle *handle;

    handle = kzalloc(sizeof(struct ion_handle), GFP_KERNEL);
    if (!handle)
        return ERR_PTR(-ENOMEM);
    kref_init(&handle->ref);
    RB_CLEAR_NODE(&handle->node);
    handle->client = client;
    ion_buffer_get(buffer);
    ion_buffer_add_to_handle(buffer);
    handle->buffer = buffer;

    return handle;
}

具體分配內存的工作是放在對應的heap對應的allocate中的:

static int ion_carveout_heap_allocate(struct ion_heap *heap,
                      struct ion_buffer *buffer,
                      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;
}

ion_carveout_heap_allocate還是通過調用ion_carveout_allocate來實現的:

ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap,
                      unsigned long size,
                      unsigned long align)
{
    struct ion_carveout_heap *carveout_heap =
        container_of(heap, struct ion_carveout_heap, heap);
    unsigned long offset = gen_pool_alloc_aligned(carveout_heap->pool,
                            size, ilog2(align));

    if (!offset) {
        if ((carveout_heap->total_size -
              carveout_heap->allocated_bytes) >= size)
            pr_debug("%s: heap %s has enough memory (%lx) but"
                " the allocation of size %lx still failed."
                " Memory is probably fragmented.",
                __func__, heap->name,
                carveout_heap->total_size -
                carveout_heap->allocated_bytes, size);
        return ION_CARVEOUT_ALLOCATE_FAIL;
    }

    carveout_heap->allocated_bytes += size;
    return offset;
}

這裡可以看出,其實buffer之前已經預留了一塊,只要上去取出buffer來就行,然後還要將取出的buffer記錄一下。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved