msm_kgsl: Optimize TLB flushing

Only flush TLB when it is absolutely nessasary as opposed to
flushing it on every map and unmap
This commit is contained in:
Shubhraprakash Das 2010-11-01 03:38:55 -04:00 committed by Jon Benson
parent 9e3544077c
commit c69e698fff
2 changed files with 61 additions and 31 deletions

View File

@ -395,6 +395,16 @@ int kgsl_mmu_init(struct kgsl_device *device)
return -ENOMEM;
}
mmu->hwpagetable = mmu->defaultpagetable;
mmu->tlbflushfilter.size = (mmu->va_range /
(PAGE_SIZE * GSL_PT_SUPER_PTE * 8)) + 1;
mmu->tlbflushfilter.base = (unsigned int *)
kzalloc(mmu->tlbflushfilter.size, GFP_KERNEL);
if (!mmu->tlbflushfilter.base) {
KGSL_MEM_ERR("Failed to create tlbflushfilter\n");
kgsl_mmu_close(device);
return -ENOMEM;
}
GSL_TLBFLUSH_FILTER_RESET();
kgsl_yamato_regwrite(device, REG_MH_MMU_PT_BASE,
mmu->hwpagetable->base.gpuaddr);
kgsl_yamato_regwrite(device, REG_MH_MMU_VA_RANGE,
@ -455,7 +465,7 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
unsigned int flags)
{
int numpages;
unsigned int pte, superpte, ptefirst, ptelast, physaddr;
unsigned int pte, ptefirst, ptelast, physaddr;
int flushtlb, alloc_size;
struct kgsl_mmu *mmu = NULL;
int phys_contiguous = flags & KGSL_MEMFLAGS_CONPHYS;
@ -516,15 +526,11 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
pte = ptefirst;
flushtlb = 0;
superpte = ptefirst & (GSL_PT_SUPER_PTE - 1);
for (pte = superpte; pte < ptefirst; pte++) {
/* tlb needs to be flushed only when a dirty superPTE
gets backed */
if (kgsl_pt_map_isdirty(pagetable, pte)) {
flushtlb = 1;
break;
}
}
/* tlb needs to be flushed when the first and last pte are not at
* superpte boundaries */
if ((ptefirst & (GSL_PT_SUPER_PTE - 1)) != 0 ||
((ptelast + 1) & (GSL_PT_SUPER_PTE-1)) != 0)
flushtlb = 1;
for (pte = ptefirst; pte < ptelast; pte++) {
#ifdef VERBOSE_DEBUG
@ -532,8 +538,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
uint32_t val = kgsl_pt_map_getaddr(pagetable, pte);
BUG_ON(val != 0 && val != GSL_PT_PAGE_DIRTY);
#endif
if (kgsl_pt_map_isdirty(pagetable, pte))
flushtlb = 1;
if ((pte & (GSL_PT_SUPER_PTE-1)) == 0)
if (GSL_TLBFLUSH_FILTER_ISDIRTY(pte / GSL_PT_SUPER_PTE))
flushtlb = 1;
/* mark pte as in use */
if (phys_contiguous)
physaddr = address;
@ -554,17 +562,6 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
address += KGSL_PAGESIZE;
}
/* set superpte to end of next superpte */
superpte = (ptelast + (GSL_PT_SUPER_PTE - 1))
& (GSL_PT_SUPER_PTE - 1);
for (pte = ptelast; pte < superpte; pte++) {
/* tlb needs to be flushed only when a dirty superPTE
gets backed */
if (kgsl_pt_map_isdirty(pagetable, pte)) {
flushtlb = 1;
break;
}
}
KGSL_MEM_INFO("pt %p p %08x g %08x pte f %d l %d n %d f %d\n",
pagetable, address, *gpuaddr, ptefirst, ptelast,
numpages, flushtlb);
@ -573,8 +570,10 @@ int kgsl_mmu_map(struct kgsl_pagetable *pagetable,
/* Invalidate tlb only if current page table used by GPU is the
* pagetable that we used to allocate */
if (pagetable == mmu->hwpagetable)
if (flushtlb && (pagetable == mmu->hwpagetable)) {
kgsl_yamato_setstate(mmu->device, KGSL_MMUFLAGS_TLBFLUSH);
GSL_TLBFLUSH_FILTER_RESET();
}
KGSL_MEM_VDBG("return %d\n", 0);
@ -587,7 +586,8 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr,
int range)
{
unsigned int numpages;
unsigned int pte, ptefirst, ptelast;
unsigned int pte, ptefirst, ptelast, superpte;
struct kgsl_mmu *mmu = NULL;
KGSL_MEM_VDBG("enter (pt=%p, gpuaddr=0x%08x, range=%d)\n",
pagetable, gpuaddr, range);
@ -604,22 +604,24 @@ kgsl_mmu_unmap(struct kgsl_pagetable *pagetable, unsigned int gpuaddr,
KGSL_MEM_INFO("pt %p gpu %08x pte first %d last %d numpages %d\n",
pagetable, gpuaddr, ptefirst, ptelast, numpages);
mmu = pagetable->mmu;
superpte = ptefirst - (ptefirst & (GSL_PT_SUPER_PTE-1));
GSL_TLBFLUSH_FILTER_SETDIRTY(superpte / GSL_PT_SUPER_PTE);
for (pte = ptefirst; pte < ptelast; pte++) {
#ifdef VERBOSE_DEBUG
/* check if PTE exists */
BUG_ON(!kgsl_pt_map_getaddr(pagetable, pte));
#endif
kgsl_pt_map_set(pagetable, pte, GSL_PT_PAGE_DIRTY);
superpte = pte - (pte & (GSL_PT_SUPER_PTE - 1));
if (pte == superpte)
GSL_TLBFLUSH_FILTER_SETDIRTY(superpte /
GSL_PT_SUPER_PTE);
}
dmb();
/* Invalidate tlb only if current page table used by GPU is the
* pagetable that we used to allocate */
if (pagetable == pagetable->mmu->hwpagetable)
kgsl_yamato_setstate(pagetable->mmu->device,
KGSL_MMUFLAGS_TLBFLUSH);
gen_pool_free(pagetable->pool, gpuaddr, range);
KGSL_MEM_VDBG("return %d\n", 0);
@ -653,6 +655,12 @@ int kgsl_mmu_close(struct kgsl_device *device)
if (mmu->dummyspace.gpuaddr)
kgsl_sharedmem_free(&mmu->dummyspace);
if (mmu->tlbflushfilter.base) {
mmu->tlbflushfilter.size = 0;
kfree(mmu->tlbflushfilter.base);
mmu->tlbflushfilter.base = NULL;
}
mmu->flags &= ~KGSL_FLAGS_STARTED;
mmu->flags &= ~KGSL_FLAGS_INITIALIZED;
mmu->flags &= ~KGSL_FLAGS_INITIALIZED0;

View File

@ -31,6 +31,21 @@
#define KGSL_MMUFLAGS_TLBFLUSH 0x10000000
#define KGSL_MMUFLAGS_PTUPDATE 0x20000000
/* Macros to manage TLB flushing */
#define GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS (sizeof(unsigned char) * 8)
#define GSL_TLBFLUSH_FILTER_GET(superpte) \
(*((unsigned char *) \
(((unsigned int)mmu->tlbflushfilter.base) \
+ (superpte / GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))))
#define GSL_TLBFLUSH_FILTER_SETDIRTY(superpte) \
(GSL_TLBFLUSH_FILTER_GET((superpte)) |= 1 << \
(superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS))
#define GSL_TLBFLUSH_FILTER_ISDIRTY(superpte) \
(GSL_TLBFLUSH_FILTER_GET((superpte)) & \
(1 << (superpte % GSL_TLBFLUSH_FILTER_ENTRY_NUMBITS)))
#define GSL_TLBFLUSH_FILTER_RESET() memset(mmu->tlbflushfilter.base,\
0, mmu->tlbflushfilter.size)
extern unsigned int kgsl_cache_enable;
struct kgsl_device;
@ -68,6 +83,11 @@ struct kgsl_pagetable {
struct gen_pool *pool;
};
struct kgsl_tlbflushfilter {
unsigned int *base;
unsigned int size;
};
struct kgsl_mmu {
unsigned int refcnt;
uint32_t flags;
@ -81,6 +101,8 @@ struct kgsl_mmu {
/* current page table object being used by device mmu */
struct kgsl_pagetable *defaultpagetable;
struct kgsl_pagetable *hwpagetable;
/* Maintain filter to manage tlb flushing */
struct kgsl_tlbflushfilter tlbflushfilter;
};