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:
parent
9e3544077c
commit
c69e698fff
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user