adreno源码系列私有内存申请

Posted bubbleben

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了adreno源码系列私有内存申请相关的知识,希望对你有一定的参考价值。

static const struct kgsl_ioctl kgsl_ioctl_funcs[] = 
    ...
    // ioctl命令:IOCTL_KGSL_GPUMEM_ALLOC
    // ioctl函数:kgsl_ioctl_gpumem_alloc
	KGSL_IOCTL_FUNC(IOCTL_KGSL_GPUMEM_ALLOC,
			kgsl_ioctl_gpumem_alloc),
    ...

1. kgsl_gpumem_alloc

struct kgsl_gpumem_alloc 
    // 返回值:GPU虚拟地址
	unsigned long gpuaddr; /* output param */
    // 申请的物理内存大小
	__kernel_size_t size;
    // 标志位
	unsigned int flags;
;

// ioctl参数:kgsl_gpumem_alloc
#define IOCTL_KGSL_GPUMEM_ALLOC \\
	_IOWR(KGSL_IOC_TYPE, 0x2f, struct kgsl_gpumem_alloc)

2. kgsl_ioctl_gpumem_alloc

long kgsl_ioctl_gpumem_alloc(struct kgsl_device_private *dev_priv,
		unsigned int cmd, void *data)

    // ioctl参数
	struct kgsl_gpumem_alloc *param = data;
    
    // kgsl_mem_entry用于描述用户空间的内存分配[见2.1节]
	struct kgsl_mem_entry *entry;
    // 用户空间指定的标志位
	uint64_t flags = param->flags;

	/*
	 * On 64 bit kernel, secure memory region is expanded and
	 * moved to 64 bit address, 32 bit apps can not access it from
	 * this IOCTL.
	 */
	if ((param->flags & KGSL_MEMFLAGS_SECURE) && is_compat_task()
			&& test_bit(KGSL_MMU_64BIT, &device->mmu.features))
		return -EOPNOTSUPP;

	/* Legacy functions doesn't support these advanced features */
	flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);

	if (is_compat_task())
		flags |= KGSL_MEMFLAGS_FORCE_32BIT;

    // 创建kgsl_mem_entry[见2.2节]
	entry = gpumem_alloc_entry(dev_priv, (uint64_t) param->size, flags);

	if (IS_ERR(entry))
		return PTR_ERR(entry);

    // 更新参数
	param->gpuaddr = (unsigned long) entry->memdesc.gpuaddr;
	param->size = (size_t) entry->memdesc.size;
	param->flags = (unsigned int) entry->memdesc.flags;

	/* Put the extra ref from kgsl_mem_entry_create() */
    // 减少引用计数, 如果引用计数减为0则通过kgsl_mem_entry_destroy释放kgsl_mem_entry
	kgsl_mem_entry_put(entry);

	return 0;

2.1 kgsl_mem_entry

/*
 * struct kgsl_mem_entry - a userspace memory allocation
 */
struct kgsl_mem_entry 
    // Currently userspace can only hold a single reference count but the kernel may hold more
	struct kref refcount;
    // description of the memory[见2.1.1节]
	struct kgsl_memdesc memdesc;
    // type-specific data, such as the dma-buf attachment pointer
	void *priv_data;
    // rb_node for the gpu address lookup rb tree
	struct rb_node node;
    // idr index for this entry, can be used to find memory that does not have a valid GPU address
	unsigned int id;
    // 持有该内存的进程
	struct kgsl_process_private *priv;
    // if !0, userspace requested that his memory be freed, but there are still references to it
	int pending_free;
    // String containing user specified metadata for the entry
	char metadata[KGSL_GPUOBJ_ALLOC_METADATA_MAX + 1];
    // used to schedule a kgsl_mem_entry_put in atomic contexts
	struct work_struct work;
	/**
	 * @map_count: Count how many vmas this object is mapped in - used for
	 * debugfs accounting
	 */
    // 映射的VMA数量
	atomic_t map_count;
;

2.1.1 kgsl_memdesc

/**
 * struct kgsl_memdesc - GPU memory object descriptor
 */
struct kgsl_memdesc 
    // 此块对象映射的页表
	struct kgsl_pagetable *pagetable;
    // CPU(进程)虚拟地址
	void *hostptr;
    // 使用CPU虚拟地址的线程个数
	unsigned int hostptr_count;
    // GPU虚拟地址
	uint64_t gpuaddr;
    // 该内存对象的物理地址
	phys_addr_t physaddr;
    // 该内存对象的物理内存大小
	uint64_t size;
    // Internal flags and settings
	unsigned int priv;
	struct sg_table *sgt;
    // 操作这块内存的函数[见2.1.2节]
	const struct kgsl_memdesc_ops *ops;
    // 用户空间申请内存时设置的标志位(Flags set from userspace)
	uint64_t flags;
	struct device *dev;
    // dma attributes for this memory
	unsigned long attrs;
    // An array of pointers to allocated pages
    // 申请的物理页面数组
	struct page **pages;
    // Total number of pages allocated
    // 申请的物理页面数量
	unsigned int page_count;
	/*
	 * @lock: Spinlock to protect the gpuaddr from being accessed by
	 * multiple entities trying to map the same SVM region at once
	 */
	spinlock_t lock;
;

2.1.2 kgsl_memdesc_ops

// 具体实现见2.2.5节kgsl_page_ops
struct kgsl_memdesc_ops 
	unsigned int vmflags;
	vm_fault_t (*vmfault)(struct kgsl_memdesc *memdesc,
		struct vm_area_struct *vma, struct vm_fault *vmf);
    // 释放内存
	void (*free)(struct kgsl_memdesc *memdesc);
    // 映射到内核虚拟地址空间
	int (*map_kernel)(struct kgsl_memdesc *memdesc);
    // 解映射
	void (*unmap_kernel)(struct kgsl_memdesc *memdesc);
	/**
	 * @put_gpuaddr: Put away the GPU address and unmap the memory
	 * descriptor
	 */
	void (*put_gpuaddr)(struct kgsl_memdesc *memdesc);
;

2.2 gpumem_alloc_entry

struct kgsl_mem_entry *gpumem_alloc_entry(
		struct kgsl_device_private *dev_priv,
		uint64_t size, uint64_t flags)

	int ret;
	struct kgsl_process_private *private = dev_priv->process_priv;
	struct kgsl_mem_entry *entry;
	struct kgsl_mmu *mmu = &dev_priv->device->mmu;
	unsigned int align;

	flags &= KGSL_MEMFLAGS_GPUREADONLY
		| KGSL_CACHEMODE_MASK
		| KGSL_MEMTYPE_MASK
		| KGSL_MEMALIGN_MASK
		| KGSL_MEMFLAGS_USE_CPU_MAP
		| KGSL_MEMFLAGS_SECURE
		| KGSL_MEMFLAGS_FORCE_32BIT
		| KGSL_MEMFLAGS_IOCOHERENT
		| KGSL_MEMFLAGS_GUARD_PAGE;

	/* Return not supported error if secure memory isn't enabled */
	if (!kgsl_mmu_is_secured(mmu) &&
			(flags & KGSL_MEMFLAGS_SECURE)) 
		dev_WARN_ONCE(dev_priv->device->dev, 1,
				"Secure memory not supported");
		return ERR_PTR(-EOPNOTSUPP);
	

	/* Cap the alignment bits to the highest number we can handle */
	align = MEMFLAGS(flags, KGSL_MEMALIGN_MASK, KGSL_MEMALIGN_SHIFT);
	if (align >= ilog2(KGSL_MAX_ALIGN)) 
		dev_err(dev_priv->device->dev,
			"Alignment too large; restricting to %dK\\n",
			KGSL_MAX_ALIGN >> 10);

		flags &= ~((uint64_t) KGSL_MEMALIGN_MASK);
		flags |= (uint64_t)((ilog2(KGSL_MAX_ALIGN) <<
						KGSL_MEMALIGN_SHIFT) &
					KGSL_MEMALIGN_MASK);
	

	/* For now only allow allocations up to 4G */
	if (size == 0 || size > UINT_MAX)
		return ERR_PTR(-EINVAL);

    // 更新缓存策略
	flags = kgsl_filter_cachemode(flags);

    // 前面主要完成标志位的校验和更新
    // 这里开始创建kgsl_mem_entry[见2.2.1节]
	entry = kgsl_mem_entry_create();
	if (entry == NULL)
		return ERR_PTR(-ENOMEM);

    // 根据标志位判断是否是cached buffer
	if (IS_ENABLED(CONFIG_QCOM_KGSL_IOCOHERENCY_DEFAULT) &&
		kgsl_cachemode_is_cached(flags))
		flags |= KGSL_MEMFLAGS_IOCOHERENT;

    // 私有内存分配[2.2.2节]
	ret = kgsl_allocate_user(dev_priv->device, &entry->memdesc,
		size, flags, 0);
	if (ret != 0)
		goto err;

    // 将该内存绑定到kgsl进程[2.2.7节]
	ret = kgsl_mem_entry_attach_process(dev_priv->device, private, entry);
	if (ret != 0) 
		kgsl_sharedmem_free(&entry->memdesc);
		goto err;
	

	kgsl_process_add_stats(private,
			kgsl_memdesc_usermem_type(&entry->memdesc),
			entry->memdesc.size);
	trace_kgsl_mem_alloc(entry);

    // 将kgsl_mem_entry提交到kgsl_process_private, 以便其他操作也能够访问
	kgsl_mem_entry_commit_process(entry);
	return entry;
err:
	kfree(entry);
	return ERR_PTR(ret);

2.2.1 kgsl_mem_entry_create

static struct kgsl_mem_entry *kgsl_mem_entry_create(void)

    // 创建kgsl_mem_entry
	struct kgsl_mem_entry *entry = kzalloc(sizeof(*entry), GFP_KERNEL);

	if (entry != NULL) 
        // 初始化kgsl_mem_entry引用计数为1
		kref_init(&entry->refcount);
		/* put this ref in userspace memory alloc and map ioctls */
        // 引用计数加1
		kref_get(&entry->refcount);
        // 初始化映射的VMA数量为0
		atomic_set(&entry->map_count, 0);
	

	return entry;

2.2.2 kgsl_allocate_user

enum kgsl_mmutype 
    // 支持IOMMU
	KGSL_MMU_TYPE_IOMMU = 0,
	KGSL_MMU_TYPE_NONE
;

int kgsl_allocate_user(struct kgsl_device *device, struct kgsl_memdesc *memdesc,
		u64 size, u64 flags, u32 priv)

    // 如果不支持IOMMU, 则需要分配连续内存
	if (device->mmu.type == KGSL_MMU_TYPE_NONE)
		return kgsl_alloc_contiguous(device, memdesc, size, flags,
			priv);
	else if (flags & KGSL_MEMFLAGS_SECURE)
		return kgsl_allocate_secure(device, memdesc, size, flags, priv);

    // 页面分配[见2.2.3节]
	return kgsl_alloc_pages(device, memdesc, size, flags, priv);

2.2.3 kgsl_alloc_pages

static int kgsl_alloc_pages(struct kgsl_device *device,
		struct kgsl_memdesc *memdesc, u64 size, u64 flags, u32 priv)

	struct page **pages;
	int count;

    // size大小对齐
	size = PAGE_ALIGN(size);

    // 判断size大小有效性
	if (!size || size > UINT_MAX)
		return -EINVAL;

    // 根据标志位初始化kgsl_memdesc[见2.2.4节]
	kgsl_memdesc_init(device, memdesc, flags);
    // 传入的priv为0
	memdesc->priv |= priv;

    // #define KGSL_MEMDESC_SYSMEM BIT(9)
	if (priv & KGSL_MEMDESC_SYSMEM) 
		memdesc->ops = &kgsl_system_ops;
		count = kgsl_system_alloc_pages(size, &pages, device->dev);
	 else 
        // 设置kgsl_memdesc的kgsl_memdesc_ops实现[2.2.5节]
		memdesc->ops = &kgsl_page_ops;
        // 分配页面并返回分配的page[2.2.6节]
		count = _kgsl_alloc_pages(size, &pages, device->dev);
	

	if (count < 0)
		return count;

    // 页面数组指针
	memdesc->pages = pages;
    // 内存大小
	memdesc->size = size;
    // 页面数量
	memdesc->page_count = count;

    // 更新全局的kgsl的内存统计: 将申请的内存大小统计进kgsl_driver的stats结构体page_alloc成员
	KGSL_STATS_ADD(size, &kgsl_driver.stats.page_alloc,
		&kgsl_driver.stats.page_alloc_max);

	return 0;

2.2.4 kgsl_memdesc_init

void kgsl_memdesc_init(struct kgsl_device *device,
			struct kgsl_memdesc *memdesc, uint64_t flags)

	struct kgsl_mmu *mmu = &device->mmu;
	unsigned int align;

    // 初始化kgsl_memdesc
	memset(memdesc, 0, sizeof(*memdesc));
	/* Turn off SVM if the system doesn't support it */
    // 判断是否支持KGSL_MMU_IOPGTABLE
	if (!kgsl_mmu_is_perprocess(mmu))
		flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);

	/* Secure memory disables advanced addressing modes */
	if (flags & KGSL_MEMFLAGS_SECURE)
		flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);

	/* Disable IO coherence if it is not supported on the chip */
    // 判断是否支持I/O coherency
	if (!kgsl_mmu_has_feature(device, KGSL_MMU_IO_COHERENT)) 
		flags &= ~((uint64_t) KGSL_MEMFLAGS_IOCOHERENT);

		WARN_ONCE(IS_ENABLED(CONFIG_QCOM_KGSL_IOCOHERENCY_DEFAULT),
			"I/O coherency is not supported on this target\\n");
	 else if (IS_ENABLED(CONFIG_QCOM_KGSL_IOCOHERENCY_DEFAULT))
		flags |= KGSL_MEMFLAGS_IOCOHERENT;

	/*
	 * We can't enable I/O coherency on uncached surfaces because of
	 * situations where hardware might snoop the cpu caches which can
	 * have stale data. This happens primarily due to the limitations
	 * of dma caching APIs available on arm64
	 */
	if (!kgsl_cachemode_is_cached(flags))
		flags &= ~((u64) KGSL_MEMFLAGS_IOCOHERENT);

	if (kgsl_mmu_has_feature(device, KGSL_MMU_NEED_GUARD_PAGE) ||
		(flags & KGSL_MEMFLAGS_GUARD_PAGE))
		memdesc->priv |= KGSL_MEMDESC_GUARD_PAGE;

	if (flags & KGSL_MEMFLAGS_SECURE)
		memdesc->priv |= KGSL_MEMDESC_SECURE;

    // 设置标志位
	memdesc->flags = flags;
    // 设置持有该内存的device
	memdesc->dev = &device->pdev->dev;

    // 对齐
	align = max_t(unsigned int,
		kgsl_memdesc_get_align(memdesc), ilog2(PAGE_SIZE));
    // 设置kgsl_memdesc的对齐标志位
	kgsl_memdesc_set_align(memdesc, align);

	spin_lock_init(&memdesc->lock);

2.2.5 kgsl_page_ops

static const struct kgsl_memdesc_ops kgsl_page_ops = 
	.free = kgsl_free_pages,
	.vmflags = VM_DONTDUMP | VM_DONTEXPAND | VM_DONTCOPY | VM_MIXEDMAP,
	.vmfault = kgsl_paged_vmfault,
	.map_kernel = kgsl_paged_map_kernel,
	.unmap_kernel = kgsl_paged_unmap_kernel,
	.put_gpuaddr = kgsl_unmap_and_put_gpuaddr,
;

2.2.6 _kgsl_alloc_pages

static int _kgsl_alloc_pages(struct kgsl_memdesc *memdesc,
		u64 size, struct page ***pages, struct device *dev)

	int count = 0;
    // 将内存大小转换为页面数量
	int npages = size >> PAGE_SHIFT;
    // attempt to allocate physically contiguous memory by kmalloc
    // but upon failure, fall back to non-contiguous (vmalloc) allocation
	struct page **local = kvcalloc(npages, sizeof(*local), GFP_KERNEL);
	u32 page_size, align;
	u64 len = size;

	if (!local)
		return -ENOMEM;

    // 共享内存设置成功或者未配置CONFIG_QCOM_KGSL_USE_SHMEM则返回0[见2.2.6.1节]
	count = kgsl_memdesc_file_setup(memdesc, size);
	if (count) 
		kvfree(local);
		return count;
	

	/* Start with 1MB alignment to get the biggest page we can */
	align = ilog2(SZ_1M);

    // 根据内存大小计算页面大小
	page_size = kgsl_get_page_size(len, align);

	while (len) 
        // 调用kgsl_pool_alloc_page分配, 并将获取的page通过local数组返回
		int ret = kgsl_alloc_page(&page_size, &local[count],
			npages, &align, count, memdesc->shmem_filp, dev);

		if (ret == -EAGAIN)
			continue;
		else if (ret <= 0) 
			int i;

			for (i = 0; i < count; ) 
				int n = 1 << compound_order(local[i]);

				kgsl_free_page(local[i]);
				i += n;
			
			kvfree(local);

			if (!kgsl_sharedmem_noretry_flag)
				pr_err_ratelimited("kgsl: out of memory: only allocated %lldKb of %lldKb requested\\n",
					(size - len) >> 10, size >> 10);

			if (memdesc->shmem_filp)
				fput(memdesc->shmem_filp);

			return -ENOMEM;
		

		count += ret;
		npages -= ret;
		len -= page_size;

		page_size = kgsl_get_page_size(len, align);
	

    // pages作为返回值
	*pages = local;

	return count;

2.2.6.1 kgsl_memdesc_file_setup

// 配置kgsl使用共享内存
#ifdef CONFIG_QCOM_KGSL_USE_SHMEM
static int kgsl_memdesc_file_setup(struct kgsl_memdesc *memdesc, uint64_t size)

	int ret;

    // 在共享内存目录下挂载一个kgsl-3d0的目录, 共享size大小的内存
	memdesc->shmem_filp = shm

以上是关于adreno源码系列私有内存申请的主要内容,如果未能解决你的问题,请参考以下文章

adreno源码系列全局内存申请

adreno源码系列全局内存申请

adreno源码系列adreno_ringbuffer

adreno源码系列adreno_ringbuffer

adreno源码系列adreno_dispatcher

adreno源码系列adreno_dispatcher