__reserved_mem_init_node

kenrel: 5.10
arch: arm64

__reserved_mem_init_node通过将RESERVEDMEM_OF_DECLARE声明的of_device_id与reserved_mem 的compatible属性进行匹配,匹配成功则执行声明的回调,对于cma这个回调函数就是rmem_cma_setup,他会通过reserved_mem来初始化一个cma

/**
 * __reserved_mem_init_node() - call region specific reserved memory init code
 */
static int __init __reserved_mem_init_node(struct reserved_mem *rmem)
{
        extern const struct of_device_id __reservedmem_of_table[];
        const struct of_device_id *i; 
        int ret = -ENOENT;

        for (i = __reservedmem_of_table; i < &__rmem_of_table_sentinel; i++) {
          		reservedmem_of_init_fn initfn = i->data;
          		const char *compat = i->compatible;

                if (!of_flat_dt_is_compatible(rmem->fdt_node, compat))
                        continue;

                ret = initfn(rmem);
                if (ret == 0) {
                       pr_info("initialized node %s, compatible id %s\n",
                              rmem->name, compat);
                        break;
                }   
        }   
        return ret;
}

RESERVEDMEM_OF_DECLARE可以声明一个struct of_device_id结构体变量,struct of_device_id结构体定义如下,它代表reserved memory节点下的一个子节点:

struct of_device_id {                                                                                                                                           
        char    name[32];                                                                                                                                       
        char    type[32];                                                                                                                                       
        char    compatible[128];                                                                                                                                
        const void *data;                                                                                                                                       
};

所有声明的struct of_device_id结构体变量都会被放到__table_of_table段,比如对于cma则声明如下:

RESERVEDMEM_OF_DECLARE(cma, "shared-dma-pool", rmem_cma_setup);

__reservedmem_of_table是这个section的起始地址,__reserved_mem_init_node会遍历__table_of_table段的每个struct of_device_id,通过of_flat_dt_is_compatible比较它的compatible与当前参数传进的reserved_mem 的compable是否相等,如果相等则会调用声明的回调函数,对于cma,这个回调函数就是rmem_cma_setup

static int __init rmem_cma_setup(struct reserved_mem *rmem)
{
        phys_addr_t align = PAGE_SIZE << max(MAX_ORDER - 1, pageblock_order);
        phys_addr_t mask = align - 1;
        unsigned long node = rmem->fdt_node;
        bool default_cma = of_get_flat_dt_prop(node, "linux,cma-default", NULL);
        struct cma *cma;
        int err;
        /* 命令行的cma定义优先级大于dts? */
        if (size_cmdline != -1 && default_cma) {
                pr_info("Reserved memory: bypass %s node, using cmdline CMA params instead\n",
                        rmem->name);
                return -EBUSY;
        }   

        if (!of_get_flat_dt_prop(node, "reusable", NULL) ||
            of_get_flat_dt_prop(node, "no-map", NULL))
                return -EINVAL;

        if ((rmem->base & mask) || (rmem->size & mask)) {
                pr_err("Reserved memory: incorrect alignment of CMA region\n");
                return -EINVAL;
        }   

        err = cma_init_reserved_mem(rmem->base, rmem->size, 0, rmem->name, &cma);
        if (err) {
                pr_err("Reserved memory: unable to setup CMA region\n");
                return err;
        }
        /* Architecture specific contiguous memory fixup. */
        dma_contiguous_early_fixup(rmem->base, rmem->size);

        if (default_cma)
                dma_contiguous_default_area = cma;//设置默认的cma,保存到全局变量

        rmem->ops = &rmem_cma_ops;
        rmem->priv = cma;//私有数据设置为cma

        pr_info("Reserved memory: created CMA memory pool at %pa, size %ld MiB\n",
                &rmem->base, (unsigned long)rmem->size / SZ_1M);

        return 0;
}

cma_init_reserved_mem会从cma_areas全局数组中获取一个成员变量,用reserved_mem 对其进行初始化,并将初始化的cma通过参数cma返回,cma的定义参考如下:

struct cma {                                                                                                                                                    
        unsigned long   base_pfn;                                                                                                                               
        unsigned long   count;   //cma区域大小为多少个页                                                                                                                               
        unsigned long   *bitmap;                                                                                                                                
        unsigned int order_per_bit; /* Order of pages represented by one bit */                                                                                 
        struct mutex    lock;                                                                                                                                   
#ifdef CONFIG_CMA_DEBUGFS                                                                                                                                       
        struct hlist_head mem_head;                                                                                                                             
        spinlock_t mem_head_lock;                                                                                                                               
        struct debugfs_u32_array dfs_bitmap;                                                                                                                    
#endif                                                                                                                                                          
        char name[CMA_MAX_NAME];  //cma%d                                                                                                                              
};

值得注意的是,对于dma也有一个类似的RESERVEDMEM_OF_DECLARE声明:

RESERVEDMEM_OF_DECLARE(dma, "shared-dma-pool", rmem_dma_setup);

我们可以看到rmem_dma_setup实际就是通过初始化rmem->ops = &rmem_dma_ops;,这样保证dma_init_reserved_memory能调用到rmem_dma_ops .device_init 回调,device_init回调中初始化了compatable为"shared-dma-pool"的保留内存,它将来会用作dma pool来使用,可以有多个dma pool,通过链表进行管理,dma pool主要是为了应对小的dma内存分配,关于为何要出现dma pool可以参考下面的文章:
Generic DMA pools

static int __init rmem_dma_setup(struct reserved_mem *rmem)
{
        unsigned long node = rmem->fdt_node;

        if (of_get_flat_dt_prop(node, "reusable", NULL))
                return -EINVAL;

#ifdef CONFIG_ARM
        if (!of_get_flat_dt_prop(node, "no-map", NULL)) {
                pr_err("Reserved memory: regions without no-map are not yet supported\n");
                return -EINVAL;
        }   

        if (of_get_flat_dt_prop(node, "linux,dma-default", NULL)) {
                WARN(dma_reserved_default_memory,
                     "Reserved memory: region for default DMA coherent area is redefined\n");
                dma_reserved_default_memory = rmem;
        }   
#endif

        rmem->ops = &rmem_dma_ops;
        pr_info("Reserved memory: created DMA memory pool at %pa, size %ld MiB\n",
                &rmem->base, (unsigned long)rmem->size / SZ_1M);
        return 0;
}

最关键的就是初始化rmem->ops为rmem_dma_ops,rmem_dma_ops 定义如下:

static const struct reserved_mem_ops rmem_dma_ops = {
        .device_init    = rmem_dma_device_init,
        .device_release = rmem_dma_device_release,
};

而在dma_init_reserved_memory中会执行device_init 回调:

static int __init dma_init_reserved_memory(void)
{
        const struct reserved_mem_ops *ops;
        int ret;

        if (!dma_reserved_default_memory)
                return -ENOMEM;

        ops = dma_reserved_default_memory->ops;

        /*
         * We rely on rmem_dma_device_init() does not propagate error of
         * dma_assign_coherent_memory() for "NULL" device.
         */
        ret = ops->device_init(dma_reserved_default_memory, NULL);

        if (!ret) {
                dma_coherent_default_memory = dma_reserved_default_memory->priv;
                pr_info("DMA: default coherent area is set\n");
        }

        return ret;
}

core_initcall(dma_init_reserved_memory);

我们看下device_init 的内容,它初始化了dma_coherent_mem结构体,此处dev为null,是为了将保留区域用作dma pool来使用

static int rmem_dma_device_init(struct reserved_mem *rmem, struct device *dev)
{
        struct dma_coherent_mem *mem = rmem->priv;
        int ret;

        if (!mem) {
                ret = dma_init_coherent_memory(rmem->base, rmem->base,
                                               rmem->size, &mem);
                if (ret) {
                        pr_err("Reserved memory: failed to init DMA memory pool at %pa, size %ld MiB\n",
                                &rmem->base, (unsigned long)rmem->size / SZ_1M);
                        return ret;
                }
        }
        mem->use_dev_dma_pfn_offset = true;
        rmem->priv = mem;
        dma_assign_coherent_memory(dev, mem);
        return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值