Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 501a3cba authored by Kamal Agrawal's avatar Kamal Agrawal Committed by Pranav Patel
Browse files

msm: kgsl: Fix memory leak for anonymous buffers



Currently, clean up is not done properly for anonymous buffer
(KGSL_MEM_ENTRY_USER). Fix it by freeing up resources allocated
during memdesc_sg_virt.

Change-Id: I75bff2e718b494c102a8075a5f27323e1823b212
Signed-off-by: default avatarKamal Agrawal <kamaagra@codeaurora.org>
Signed-off-by: default avatarPranav Patel <pranavp@codeaurora.org>
parent d1c52736
Loading
Loading
Loading
Loading
+45 −50
Original line number Diff line number Diff line
@@ -269,8 +269,12 @@ kgsl_mem_entry_create(void)
	return entry;
}
#ifdef CONFIG_DMA_SHARED_BUFFER
static void kgsl_destroy_ion(struct kgsl_dma_buf_meta *meta)
static void kgsl_destroy_ion(struct kgsl_memdesc *memdesc)
{
	struct kgsl_mem_entry *entry = container_of(memdesc,
				struct kgsl_mem_entry, memdesc);
	struct kgsl_dma_buf_meta *meta = entry->priv_data;

	if (meta != NULL) {
		dma_buf_unmap_attachment(meta->attach, meta->table,
			DMA_FROM_DEVICE);
@@ -278,13 +282,44 @@ static void kgsl_destroy_ion(struct kgsl_dma_buf_meta *meta)
		dma_buf_put(meta->dmabuf);
		kfree(meta);
	}

	/*
	 * Ion takes care of freeing the sg_table for us so
	 * clear the sg table to ensure kgsl_sharedmem_free
	 * doesn't try to free it again
	 */
	memdesc->sgt = NULL;
}
#else
static void kgsl_destroy_ion(struct kgsl_dma_buf_meta *meta)

static struct kgsl_memdesc_ops kgsl_dmabuf_ops = {
	.free = kgsl_destroy_ion,
};
#endif

static void kgsl_destroy_anon(struct kgsl_memdesc *memdesc)
{
	int i = 0, j;
	struct scatterlist *sg;
	struct page *page;

	for_each_sg(memdesc->sgt->sgl, sg, memdesc->sgt->nents, i) {
		page = sg_page(sg);
		for (j = 0; j < (sg->length >> PAGE_SHIFT); j++) {
			/*
			 * Mark the page in the scatterlist as dirty if they
			 * were writable by the GPU.
			 */
			if (!(memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY))
				set_page_dirty_lock(nth_page(page, j));

			/*
			 * Put the page reference taken using get_user_pages
			 * during memdesc_sg_virt.
			 */
			put_page(nth_page(page, j));
		}
	}
}
#endif

void
kgsl_mem_entry_destroy(struct kref *kref)
@@ -307,41 +342,8 @@ kgsl_mem_entry_destroy(struct kref *kref)
		atomic_long_sub(entry->memdesc.size,
			&kgsl_driver.stats.mapped);

	/*
	 * Ion takes care of freeing the sg_table for us so
	 * clear the sg table before freeing the sharedmem
	 * so kgsl_sharedmem_free doesn't try to free it again
	 */
	if (memtype == KGSL_MEM_ENTRY_ION)
		entry->memdesc.sgt = NULL;

	if ((memtype == KGSL_MEM_ENTRY_USER)
		&& !(entry->memdesc.flags & KGSL_MEMFLAGS_GPUREADONLY)) {
		int i = 0, j;
		struct scatterlist *sg;
		struct page *page;
		/*
		 * Mark all of pages in the scatterlist as dirty since they
		 * were writable by the GPU.
		 */
		for_each_sg(entry->memdesc.sgt->sgl, sg,
			    entry->memdesc.sgt->nents, i) {
			page = sg_page(sg);
			for (j = 0; j < (sg->length >> PAGE_SHIFT); j++)
				set_page_dirty_lock(nth_page(page, j));
		}
	}

	kgsl_sharedmem_free(&entry->memdesc);

	switch (memtype) {
	case KGSL_MEM_ENTRY_ION:
		kgsl_destroy_ion(entry->priv_data);
		break;
	default:
		break;
	}

	kfree(entry);
}
EXPORT_SYMBOL(kgsl_mem_entry_destroy);
@@ -2189,6 +2191,10 @@ static int memdesc_sg_virt(struct kgsl_memdesc *memdesc, unsigned long useraddr)
	return ret;
}

static struct kgsl_memdesc_ops kgsl_usermem_ops = {
	.free = kgsl_destroy_anon,
};

static int kgsl_setup_anon_useraddr(struct kgsl_pagetable *pagetable,
	struct kgsl_mem_entry *entry, unsigned long hostptr,
	size_t offset, size_t size)
@@ -2204,6 +2210,7 @@ static int kgsl_setup_anon_useraddr(struct kgsl_pagetable *pagetable,
	entry->memdesc.pagetable = pagetable;
	entry->memdesc.size = (uint64_t) size;
	entry->memdesc.flags |= (uint64_t)KGSL_MEMFLAGS_USERMEM_ADDR;
	entry->memdesc.ops = &kgsl_usermem_ops;

	if (kgsl_memdesc_use_cpu_map(&entry->memdesc)) {

@@ -2495,11 +2502,6 @@ long kgsl_ioctl_gpuobj_import(struct kgsl_device_private *dev_priv,
	return 0;

unmap:
	if (kgsl_memdesc_usermem_type(&entry->memdesc) == KGSL_MEM_ENTRY_ION) {
		kgsl_destroy_ion(entry->priv_data);
		entry->memdesc.sgt = NULL;
	}

	kgsl_sharedmem_free(&entry->memdesc);

out:
@@ -2596,6 +2598,7 @@ static int kgsl_setup_dma_buf(struct kgsl_device *device,
	entry->priv_data = meta;
	entry->memdesc.pagetable = pagetable;
	entry->memdesc.size = 0;
	entry->memdesc.ops = &kgsl_dmabuf_ops;
	/* USE_CPU_MAP is not impemented for ION. */
	entry->memdesc.flags &= ~((uint64_t) KGSL_MEMFLAGS_USE_CPU_MAP);
	entry->memdesc.flags |= (uint64_t)KGSL_MEMFLAGS_USERMEM_ION;
@@ -2801,14 +2804,6 @@ long kgsl_ioctl_map_user_mem(struct kgsl_device_private *dev_priv,
	return result;

error_attach:
	switch (kgsl_memdesc_usermem_type(&entry->memdesc)) {
	case KGSL_MEM_ENTRY_ION:
		kgsl_destroy_ion(entry->priv_data);
		entry->memdesc.sgt = NULL;
		break;
	default:
		break;
	}
	kgsl_sharedmem_free(&entry->memdesc);
error:
	/* Clear gpuaddr here so userspace doesn't get any wrong ideas */
+1 −0
Original line number Diff line number Diff line
@@ -996,6 +996,7 @@ void kgsl_sharedmem_free(struct kgsl_memdesc *memdesc)
	if (memdesc->sgt) {
		sg_free_table(memdesc->sgt);
		kfree(memdesc->sgt);
		memdesc->sgt = NULL;
	}

	if (memdesc->pages)