smmu学习笔记之alloc_iova

在smmu初始化的时候会调用
struct iova *
alloc_iova(struct iova_domain *iovad, unsigned long size,
    unsigned long limit_pfn,
    bool size_aligned)
{
    struct iova *new_iova;
    int ret;

    new_iova = alloc_iova_mem();
    if (!new_iova)
        return NULL;

    ret = __alloc_and_insert_iova_range(iovad, size, limit_pfn,
            new_iova, size_aligned);

    if (ret) {
        free_iova_mem(new_iova);
        return NULL;
    }。

    return new_iova;
}

这个函数会按照limit_pfn,将会按照limit_pfn将nova插入到rb tree中
在alloc_iova 中首先用alloc_iova_mem 申请一块内存.alloc_iova_mem的实现比较简单,是通过kmem_cache_alloc来申请的。
struct iova *alloc_iova_mem(void)
{
    return kmem_cache_alloc(iova_cache, GFP_ATOMIC);
}
在__alloc_and_insert_iova_range 中首先根据limit_pfn 找到要插入rb tree的前一个节点。这是通过调用__get_cached_rbnode 来实现。
static struct rb_node *
__get_cached_rbnode(struct iova_domain *iovad, unsigned long *limit_pfn)
{
    if ((*limit_pfn != iovad->dma_32bit_pfn) ||
        (iovad->cached32_node == NULL))
        return rb_last(&iovad->rbroot);
    else {
        struct rb_node *prev_node = rb_prev(iovad->cached32_node);
        struct iova *curr_iova =
            container_of(iovad->cached32_node, struct iova, node);
        *limit_pfn = curr_iova->pfn_lo - 1;
        return prev_node;
    }
}
在__get_cached_rbnode 中一般iovad->dma_32bit_pfn == 4G,一般也没人申请这么大的size,所以不会从rb tree的最后一个node开始找,因此一般走else的case,如果rb_prev 找到前一个node,然后根据这个node的pfn_lo 重新调整limit_pfn
下面这一段有根据调整过后的limit_pfn来重新查找rb tree。感觉这段code只要是获得limit_pfn的size和pad_size
    while (curr) {
        struct iova *curr_iova = container_of(curr, struct iova, node);

        if (limit_pfn < curr_iova->pfn_lo)
            goto move_left;
        else if (limit_pfn < curr_iova->pfn_hi)
            goto adjust_limit_pfn;
        else {
            if (size_aligned)
                pad_size = iova_get_pad_size(size, limit_pfn);
            if ((curr_iova->pfn_hi + size + pad_size) <= limit_pfn)
                break;    /* found a free slot */
        }
adjust_limit_pfn:
        limit_pfn = curr_iova->pfn_lo - 1;
move_left:
        prev = curr;
        curr = rb_prev(curr);
    }
然后根据找到的limit_pfn和size,建立一个新的节点,这个节点的memory就是在alloc_iova_mem 中申请
    /* pfn_lo will point to size aligned address if size_aligned is set */
    new->pfn_lo = limit_pfn - (size + pad_size) + 1;
    new->pfn_hi = new->pfn_lo + size - 1;

    /* Insert the new_iova into domain rbtree by holding writer lock */
    /* Add new node and rebalance tree. */
    {
        struct rb_node **entry, *parent = NULL;

        /* If we have 'prev', it's a valid place to start the
           insertion. Otherwise, start from the root. */
        if (prev)
            entry = &prev;
        else
            entry = &iovad->rbroot.rb_node;

        /* Figure out where to put new node */
        while (*entry) {
            struct iova *this = container_of(*entry,
                            struct iova, node);
            parent = *entry;

            if (new->pfn_lo < this->pfn_lo)
                entry = &((*entry)->rb_left);
            else if (new->pfn_lo > this->pfn_lo)
                entry = &((*entry)->rb_right);
            else
                BUG(); /* this should not happen */
        }

        /* Add new node and rebalance tree. */
        rb_link_node(&new->node, parent, entry);
        rb_insert_color(&new->node, &iovad->rbroot);
    }
    __cached_rbnode_insert_update(iovad, saved_pfn, new);

最后就将新的iova插入到rb tree中.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值