Skip to content

Commit

Permalink
MIPS: mm: Use the Hardware Page Table Walker if the core supports it
Browse files Browse the repository at this point in the history
The Hardware Page Table Walker aims to speed up TLB refill exceptions
by handling them in the hardware level instead of having a software
TLB refill handler. However, a TLB refill exception can still be
thrown in certain cases such as, synchronus exceptions, or address
translation or memory errors during the HTW operation. As a result of
which, HTW must not be considered a complete replacement for the TLB
refill software handler, but rather a fast-path for it.
For HTW to work, the PWBase register must contain the task's page
global directory address so the HTW will kick in on TLB refill
exceptions.

Due to HTW being a separate engine embedded deep in the CPU pipeline,
we need to restart the HTW everytime a PTE changes to avoid HTW
fetching a old entry from the page tables. It's also necessary to
restart the HTW on context switches to prevent it from fetching a
page from the previous process. Finally, since HTW is using the
entryhi register to write the translations to the TLB, it's necessary
to stop the HTW whenever the entryhi changes (eg for tlb probe
perations) and enable it back afterwards.

== Performance ==

The following trivial test was used to measure the performance of the
HTW. Using the same root filesystem, the following command was used
to measure the number of tlb refill handler executions with and
without (using 'nohtw' kernel parameter) HTW support.  The kernel was
modified to use a scratch register as a counter for the TLB refill
exceptions.

find /usr -type f -exec ls -lh {} \;

HTW Enabled:
TLB refill exceptions: 12306

HTW Disabled:
TLB refill exceptions: 17805

Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: Markos Chandras <markos.chandras@imgtec.com>
Patchwork: https://patchwork.linux-mips.org/patch/7336/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
  • Loading branch information
Markos Chandras authored and Ralf Baechle committed Aug 1, 2014
1 parent 3d528b3 commit f1014d1
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 0 deletions.
10 changes: 10 additions & 0 deletions arch/mips/include/asm/mmu_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,20 @@
#include <asm/tlbflush.h>
#include <asm-generic/mm_hooks.h>

#define htw_set_pwbase(pgd) \
do { \
if (cpu_has_htw) { \
write_c0_pwbase(pgd); \
back_to_back_c0_hazard(); \
htw_reset(); \
} \
} while (0)

#define TLBMISS_HANDLER_SETUP_PGD(pgd) \
do { \
extern void tlbmiss_handler_setup_pgd(unsigned long); \
tlbmiss_handler_setup_pgd((unsigned long)(pgd)); \
htw_set_pwbase((unsigned long)pgd); \
} while (0)

#ifdef CONFIG_MIPS_PGD_C0_CONTEXT
Expand Down
27 changes: 27 additions & 0 deletions arch/mips/include/asm/pgtable.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,31 @@ extern void paging_init(void);

#define pmd_page_vaddr(pmd) pmd_val(pmd)

#define htw_stop() \
do { \
if (cpu_has_htw) \
write_c0_pwctl(read_c0_pwctl() & \
~(1 << MIPS_PWCTL_PWEN_SHIFT)); \
} while(0)

#define htw_start() \
do { \
if (cpu_has_htw) \
write_c0_pwctl(read_c0_pwctl() | \
(1 << MIPS_PWCTL_PWEN_SHIFT)); \
} while(0)


#define htw_reset() \
do { \
if (cpu_has_htw) { \
htw_stop(); \
back_to_back_c0_hazard(); \
htw_start(); \
back_to_back_c0_hazard(); \
} \
} while(0)

#if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32)

#define pte_none(pte) (!(((pte).pte_low | (pte).pte_high) & ~_PAGE_GLOBAL))
Expand Down Expand Up @@ -131,6 +156,7 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt
null.pte_low = null.pte_high = _PAGE_GLOBAL;

set_pte_at(mm, addr, ptep, null);
htw_reset();
}
#else

Expand Down Expand Up @@ -168,6 +194,7 @@ static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *pt
else
#endif
set_pte_at(mm, addr, ptep, __pte(0));
htw_reset();
}
#endif

Expand Down
12 changes: 12 additions & 0 deletions arch/mips/mm/tlb-r4k.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ void local_flush_tlb_all(void)
local_irq_save(flags);
/* Save old context and create impossible VPN2 value */
old_ctx = read_c0_entryhi();
htw_stop();
write_c0_entrylo0(0);
write_c0_entrylo1(0);

Expand Down Expand Up @@ -90,6 +91,7 @@ void local_flush_tlb_all(void)
}
tlbw_use_hazard();
write_c0_entryhi(old_ctx);
htw_start();
flush_itlb();
local_irq_restore(flags);
}
Expand Down Expand Up @@ -131,6 +133,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
int oldpid = read_c0_entryhi();
int newpid = cpu_asid(cpu, mm);

htw_stop();
while (start < end) {
int idx;

Expand All @@ -151,6 +154,7 @@ void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
}
tlbw_use_hazard();
write_c0_entryhi(oldpid);
htw_start();
} else {
drop_mmu_context(mm, cpu);
}
Expand All @@ -174,6 +178,7 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
start &= (PAGE_MASK << 1);
end += ((PAGE_SIZE << 1) - 1);
end &= (PAGE_MASK << 1);
htw_stop();

while (start < end) {
int idx;
Expand All @@ -195,6 +200,7 @@ void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
}
tlbw_use_hazard();
write_c0_entryhi(pid);
htw_start();
} else {
local_flush_tlb_all();
}
Expand All @@ -214,6 +220,7 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
page &= (PAGE_MASK << 1);
local_irq_save(flags);
oldpid = read_c0_entryhi();
htw_stop();
write_c0_entryhi(page | newpid);
mtc0_tlbw_hazard();
tlb_probe();
Expand All @@ -231,6 +238,7 @@ void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)

finish:
write_c0_entryhi(oldpid);
htw_start();
flush_itlb_vm(vma);
local_irq_restore(flags);
}
Expand All @@ -247,6 +255,7 @@ void local_flush_tlb_one(unsigned long page)

local_irq_save(flags);
oldpid = read_c0_entryhi();
htw_stop();
page &= (PAGE_MASK << 1);
write_c0_entryhi(page);
mtc0_tlbw_hazard();
Expand All @@ -263,6 +272,7 @@ void local_flush_tlb_one(unsigned long page)
tlbw_use_hazard();
}
write_c0_entryhi(oldpid);
htw_start();
flush_itlb();
local_irq_restore(flags);
}
Expand Down Expand Up @@ -351,6 +361,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
local_irq_save(flags);
/* Save old context and create impossible VPN2 value */
old_ctx = read_c0_entryhi();
htw_stop();
old_pagemask = read_c0_pagemask();
wired = read_c0_wired();
write_c0_wired(wired + 1);
Expand All @@ -366,6 +377,7 @@ void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,

write_c0_entryhi(old_ctx);
tlbw_use_hazard(); /* What is the hazard here? */
htw_start();
write_c0_pagemask(old_pagemask);
local_flush_tlb_all();
local_irq_restore(flags);
Expand Down
91 changes: 91 additions & 0 deletions arch/mips/mm/tlbex.c
Original file line number Diff line number Diff line change
Expand Up @@ -2194,6 +2194,94 @@ static void flush_tlb_handlers(void)
(unsigned long)tlbmiss_handler_setup_pgd_end);
}

static void print_htw_config(void)
{
unsigned long config;
unsigned int pwctl;
const int field = 2 * sizeof(unsigned long);

config = read_c0_pwfield();
pr_debug("PWField (0x%0*lx): GDI: 0x%02lx UDI: 0x%02lx MDI: 0x%02lx PTI: 0x%02lx PTEI: 0x%02lx\n",
field, config,
(config & MIPS_PWFIELD_GDI_MASK) >> MIPS_PWFIELD_GDI_SHIFT,
(config & MIPS_PWFIELD_UDI_MASK) >> MIPS_PWFIELD_UDI_SHIFT,
(config & MIPS_PWFIELD_MDI_MASK) >> MIPS_PWFIELD_MDI_SHIFT,
(config & MIPS_PWFIELD_PTI_MASK) >> MIPS_PWFIELD_PTI_SHIFT,
(config & MIPS_PWFIELD_PTEI_MASK) >> MIPS_PWFIELD_PTEI_SHIFT);

config = read_c0_pwsize();
pr_debug("PWSize (0x%0*lx): GDW: 0x%02lx UDW: 0x%02lx MDW: 0x%02lx PTW: 0x%02lx PTEW: 0x%02lx\n",
field, config,
(config & MIPS_PWSIZE_GDW_MASK) >> MIPS_PWSIZE_GDW_SHIFT,
(config & MIPS_PWSIZE_UDW_MASK) >> MIPS_PWSIZE_UDW_SHIFT,
(config & MIPS_PWSIZE_MDW_MASK) >> MIPS_PWSIZE_MDW_SHIFT,
(config & MIPS_PWSIZE_PTW_MASK) >> MIPS_PWSIZE_PTW_SHIFT,
(config & MIPS_PWSIZE_PTEW_MASK) >> MIPS_PWSIZE_PTEW_SHIFT);

pwctl = read_c0_pwctl();
pr_debug("PWCtl (0x%x): PWEn: 0x%x DPH: 0x%x HugePg: 0x%x Psn: 0x%x\n",
pwctl,
(pwctl & MIPS_PWCTL_PWEN_MASK) >> MIPS_PWCTL_PWEN_SHIFT,
(pwctl & MIPS_PWCTL_DPH_MASK) >> MIPS_PWCTL_DPH_SHIFT,
(pwctl & MIPS_PWCTL_HUGEPG_MASK) >> MIPS_PWCTL_HUGEPG_SHIFT,
(pwctl & MIPS_PWCTL_PSN_MASK) >> MIPS_PWCTL_PSN_SHIFT);
}

static void config_htw_params(void)
{
unsigned long pwfield, pwsize, ptei;
unsigned int config;

/*
* We are using 2-level page tables, so we only need to
* setup GDW and PTW appropriately. UDW and MDW will remain 0.
* The default value of GDI/UDI/MDI/PTI is 0xc. It is illegal to
* write values less than 0xc in these fields because the entire
* write will be dropped. As a result of which, we must preserve
* the original reset values and overwrite only what we really want.
*/

pwfield = read_c0_pwfield();
/* re-initialize the GDI field */
pwfield &= ~MIPS_PWFIELD_GDI_MASK;
pwfield |= PGDIR_SHIFT << MIPS_PWFIELD_GDI_SHIFT;
/* re-initialize the PTI field including the even/odd bit */
pwfield &= ~MIPS_PWFIELD_PTI_MASK;
pwfield |= PAGE_SHIFT << MIPS_PWFIELD_PTI_SHIFT;
/* Set the PTEI right shift */
ptei = _PAGE_GLOBAL_SHIFT << MIPS_PWFIELD_PTEI_SHIFT;
pwfield |= ptei;
write_c0_pwfield(pwfield);
/* Check whether the PTEI value is supported */
back_to_back_c0_hazard();
pwfield = read_c0_pwfield();
if (((pwfield & MIPS_PWFIELD_PTEI_MASK) << MIPS_PWFIELD_PTEI_SHIFT)
!= ptei) {
pr_warn("Unsupported PTEI field value: 0x%lx. HTW will not be enabled",
ptei);
/*
* Drop option to avoid HTW being enabled via another path
* (eg htw_reset())
*/
current_cpu_data.options &= ~MIPS_CPU_HTW;
return;
}

pwsize = ilog2(PTRS_PER_PGD) << MIPS_PWSIZE_GDW_SHIFT;
pwsize |= ilog2(PTRS_PER_PTE) << MIPS_PWSIZE_PTW_SHIFT;
write_c0_pwsize(pwsize);

/* Make sure everything is set before we enable the HTW */
back_to_back_c0_hazard();

/* Enable HTW and disable the rest of the pwctl fields */
config = 1 << MIPS_PWCTL_PWEN_SHIFT;
write_c0_pwctl(config);
pr_info("Hardware Page Table Walker enabled\n");

print_htw_config();
}

void build_tlb_refill_handler(void)
{
/*
Expand Down Expand Up @@ -2258,5 +2346,8 @@ void build_tlb_refill_handler(void)
}
if (cpu_has_local_ebase)
build_r4000_tlb_refill_handler();
if (cpu_has_htw)
config_htw_params();

}
}

0 comments on commit f1014d1

Please sign in to comment.