Skip to content

Commit

Permalink
cputlb: Add functions for flushing TLB for a single MMU index
Browse files Browse the repository at this point in the history
Guest CPU TLB maintenance operations may be sufficiently
specialized to only need to flush TLB entries corresponding
to a particular MMU index. Implement cputlb functions for
this, to avoid the inefficiency of flushing TLB entries
which we don't need to.

Signed-off-by: Peter Maydell <[email protected]>
Reviewed-by: Edgar E. Iglesias <[email protected]>
Message-id: [email protected]
  • Loading branch information
pm215 committed Aug 25, 2015
1 parent 14db7fe commit d7a74a9
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
97 changes: 97 additions & 0 deletions cputlb.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,47 @@ void tlb_flush(CPUState *cpu, int flush_global)
tlb_flush_count++;
}

static inline void v_tlb_flush_by_mmuidx(CPUState *cpu, va_list argp)
{
CPUArchState *env = cpu->env_ptr;

#if defined(DEBUG_TLB)
printf("tlb_flush_by_mmuidx:");
#endif
/* must reset current TB so that interrupts cannot modify the
links while we are modifying them */
cpu->current_tb = NULL;

for (;;) {
int mmu_idx = va_arg(argp, int);

if (mmu_idx < 0) {
break;
}

#if defined(DEBUG_TLB)
printf(" %d", mmu_idx);
#endif

memset(env->tlb_table[mmu_idx], -1, sizeof(env->tlb_table[0]));
memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0]));
}

#if defined(DEBUG_TLB)
printf("\n");
#endif

memset(cpu->tb_jmp_cache, 0, sizeof(cpu->tb_jmp_cache));
}

void tlb_flush_by_mmuidx(CPUState *cpu, ...)
{
va_list argp;
va_start(argp, cpu);
v_tlb_flush_by_mmuidx(cpu, argp);
va_end(argp);
}

static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong addr)
{
if (addr == (tlb_entry->addr_read &
Expand Down Expand Up @@ -121,6 +162,62 @@ void tlb_flush_page(CPUState *cpu, target_ulong addr)
tb_flush_jmp_cache(cpu, addr);
}

void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...)
{
CPUArchState *env = cpu->env_ptr;
int i, k;
va_list argp;

va_start(argp, addr);

#if defined(DEBUG_TLB)
printf("tlb_flush_page_by_mmu_idx: " TARGET_FMT_lx, addr);
#endif
/* Check if we need to flush due to large pages. */
if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) {
#if defined(DEBUG_TLB)
printf(" forced full flush ("
TARGET_FMT_lx "/" TARGET_FMT_lx ")\n",
env->tlb_flush_addr, env->tlb_flush_mask);
#endif
v_tlb_flush_by_mmuidx(cpu, argp);
va_end(argp);
return;
}
/* must reset current TB so that interrupts cannot modify the
links while we are modifying them */
cpu->current_tb = NULL;

addr &= TARGET_PAGE_MASK;
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);

for (;;) {
int mmu_idx = va_arg(argp, int);

if (mmu_idx < 0) {
break;
}

#if defined(DEBUG_TLB)
printf(" %d", mmu_idx);
#endif

tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr);

/* check whether there are vltb entries that need to be flushed */
for (k = 0; k < CPU_VTLB_SIZE; k++) {
tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], addr);
}
}
va_end(argp);

#if defined(DEBUG_TLB)
printf("\n");
#endif

tb_flush_jmp_cache(cpu, addr);
}

/* update the TLBs so that writes to code in the virtual page 'addr'
can be detected */
void tlb_protect_code(ram_addr_t ram_addr)
Expand Down
47 changes: 47 additions & 0 deletions include/exec/exec-all.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,46 @@ bool qemu_in_vcpu_thread(void);
void cpu_reload_memory_map(CPUState *cpu);
void tcg_cpu_address_space_init(CPUState *cpu, AddressSpace *as);
/* cputlb.c */
/**
* tlb_flush_page:
* @cpu: CPU whose TLB should be flushed
* @addr: virtual address of page to be flushed
*
* Flush one page from the TLB of the specified CPU, for all
* MMU indexes.
*/
void tlb_flush_page(CPUState *cpu, target_ulong addr);
/**
* tlb_flush:
* @cpu: CPU whose TLB should be flushed
* @flush_global: ignored
*
* Flush the entire TLB for the specified CPU.
* The flush_global flag is in theory an indicator of whether the whole
* TLB should be flushed, or only those entries not marked global.
* In practice QEMU does not implement any global/not global flag for
* TLB entries, and the argument is ignored.
*/
void tlb_flush(CPUState *cpu, int flush_global);
/**
* tlb_flush_page_by_mmuidx:
* @cpu: CPU whose TLB should be flushed
* @addr: virtual address of page to be flushed
* @...: list of MMU indexes to flush, terminated by a negative value
*
* Flush one page from the TLB of the specified CPU, for the specified
* MMU indexes.
*/
void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, ...);
/**
* tlb_flush_by_mmuidx:
* @cpu: CPU whose TLB should be flushed
* @...: list of MMU indexes to flush, terminated by a negative value
*
* Flush all entries from the TLB of the specified CPU, for the specified
* MMU indexes.
*/
void tlb_flush_by_mmuidx(CPUState *cpu, ...);
void tlb_set_page(CPUState *cpu, target_ulong vaddr,
hwaddr paddr, int prot,
int mmu_idx, target_ulong size);
Expand All @@ -115,6 +153,15 @@ static inline void tlb_flush_page(CPUState *cpu, target_ulong addr)
static inline void tlb_flush(CPUState *cpu, int flush_global)
{
}

static inline void tlb_flush_page_by_mmuidx(CPUState *cpu,
target_ulong addr, ...)
{
}

static inline void tlb_flush_by_mmuidx(CPUState *cpu, ...)
{
}
#endif

#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
Expand Down

0 comments on commit d7a74a9

Please sign in to comment.