diff --git a/drivers/gpu/msm/kgsl.h b/drivers/gpu/msm/kgsl.h index ad6973ba..3f9ff843 100755 --- a/drivers/gpu/msm/kgsl.h +++ b/drivers/gpu/msm/kgsl.h @@ -118,6 +118,8 @@ struct kgsl_memdesc_ops { int (*map_kernel_mem)(struct kgsl_memdesc *); }; +#define KGSL_MEMDESC_GUARD_PAGE BIT(0) + /* shared memory allocation */ struct kgsl_memdesc { struct kgsl_pagetable *pagetable; @@ -129,6 +131,7 @@ struct kgsl_memdesc { struct scatterlist *sg; unsigned int sglen; struct kgsl_memdesc_ops *ops; + int flags; }; /* List of different memory entry types */ diff --git a/drivers/gpu/msm/kgsl_sharedmem.c b/drivers/gpu/msm/kgsl_sharedmem.c index 7d63949e..7962cae3 100755 --- a/drivers/gpu/msm/kgsl_sharedmem.c +++ b/drivers/gpu/msm/kgsl_sharedmem.c @@ -64,6 +64,13 @@ struct mem_entry_stats { } +/* + * One page allocation for a guard region to protect against over-zealous + * GPU pre-fetch + */ + +static struct page *kgsl_guard_page; + /** * Given a kobj, find the process structure attached to it */ @@ -334,13 +341,20 @@ static void kgsl_page_alloc_free(struct kgsl_memdesc *memdesc) { int i = 0; struct scatterlist *sg; + int sglen = memdesc->sglen; + + /* Don't free the guard page if it was used */ + if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE) + sglen--; + kgsl_driver.stats.page_alloc -= memdesc->size; + if (memdesc->hostptr) { vunmap(memdesc->hostptr); kgsl_driver.stats.vmalloc -= memdesc->size; } if (memdesc->sg) - for_each_sg(memdesc->sg, sg, memdesc->sglen, i) + for_each_sg(memdesc->sg, sg, sglen, i) __free_page(sg_page(sg)); } @@ -363,17 +377,23 @@ static int kgsl_page_alloc_map_kernel(struct kgsl_memdesc *memdesc) pgprot_t page_prot = pgprot_writecombine(PAGE_KERNEL); struct page **pages = NULL; struct scatterlist *sg; + int sglen = memdesc->sglen; int i; + + /* Don't map the guard page if it exists */ + if (memdesc->flags & KGSL_MEMDESC_GUARD_PAGE) + sglen--; + /* create a list of pages to call vmap */ - pages = vmalloc(memdesc->sglen * sizeof(struct page *)); + pages = vmalloc(sglen * sizeof(struct page *)); if (!pages) { KGSL_CORE_ERR("vmalloc(%d) failed\n", - memdesc->sglen * sizeof(struct page *)); + sglen * sizeof(struct page *)); return -ENOMEM; } - for_each_sg(memdesc->sg, sg, memdesc->sglen, i) + for_each_sg(memdesc->sg, sg, sglen, i) pages[i] = sg_page(sg); - memdesc->hostptr = vmap(pages, memdesc->sglen, + memdesc->hostptr = vmap(pages, sglen, VM_IOREMAP, page_prot); KGSL_STATS_ADD(memdesc->size, kgsl_driver.stats.vmalloc, kgsl_driver.stats.vmalloc_max); @@ -472,6 +492,14 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, int sglen = PAGE_ALIGN(size) / PAGE_SIZE; int i; + /* + * Add guard page to the end of the allocation when the + * IOMMU is in use. + */ + + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) + sglen++; + memdesc->size = size; memdesc->pagetable = pagetable; memdesc->priv = KGSL_MEMFLAGS_CACHED; @@ -491,7 +519,7 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, memdesc->sglen = sglen; sg_init_table(memdesc->sg, sglen); - for (i = 0; i < memdesc->sglen; i++) { + for (i = 0; i < PAGE_ALIGN(size) / PAGE_SIZE; i++) { struct page *page = alloc_page(GFP_KERNEL | __GFP_ZERO | __GFP_HIGHMEM); if (!page) { @@ -502,6 +530,22 @@ _kgsl_sharedmem_page_alloc(struct kgsl_memdesc *memdesc, flush_dcache_page(page); sg_set_page(&memdesc->sg[i], page, PAGE_SIZE, 0); } + + /* ADd the guard page to the end of the sglist */ + + if (kgsl_mmu_get_mmutype() == KGSL_MMU_TYPE_IOMMU) { + if (kgsl_guard_page == NULL) + kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO | + __GFP_HIGHMEM); + + if (kgsl_guard_page != NULL) { + sg_set_page(&memdesc->sg[sglen - 1], kgsl_guard_page, + PAGE_SIZE, 0); + memdesc->flags |= KGSL_MEMDESC_GUARD_PAGE; + } else + memdesc->sglen--; + } + outer_cache_range_op_sg(memdesc->sg, memdesc->sglen, KGSL_CACHE_OP_FLUSH);