Using non-Global mappings for kernel address space

Catalin Marinas catalin.marinas at arm.com
Mon Nov 8 12:13:38 EST 2010


Hi,

On Mon, 2010-11-01 at 14:23 +0000, Christoffer Dall wrote:
> For the purpose of performance evaluation of our KVM for ARM port, I
> am interested in avoiding having to flush the TLB every time I switch
> from the guest kernel to the host kernel and vice-versa. 

I'm not familiar with the KVM implementation for ARMv6(so it has been on
my list to look at for some time). Does the guest OS use a full 4GB
virtual address space? Does it only run in user mode or both
user/kernel?

Do you target SMP? That may get even trickier with the ASIDs because of
the roll-over event being broadcast via IPI.

> The only way
> to accomplish this on ARMv6 would be to have non-Global mappings of
> the host kernel address space using a separate ASID from that of the
> guest kernel.
> 
> My general approach is outlined as follows:
>  - Mark all pages and sections (except interrupt vector page) with
> addr > TASK_SIZE as non-global
>  - Set a special kernel reserved ASID at the same time of
> paging_init()

Why do you need a different ASID for the kernel? Can you not use the
same ASID for both user and kernel (while in the same context) and avoid
switching the ASID when entering/exiting the kernel? This way you could
even move the switch_mm() function to the vectors page (global), though
it may not be a problem.

As I said, this may not apply as I'm not familiar with the ARM KVM
implementation.

>  - Defer setting the CP15 context id register until return from kernel
> to userspace in ret_fast_syscall and ret_slow_syscall (ret_to_user)
>  - Set the kernel reserved ASID as part of the vector_stub (and add a
> little vector_stub code for the SWI handler for this purpose)
>  - Modify the tlbflush.h functions so they  invalidate the requested
> parts of the TLB matching the changes above. (Actually for testing
> purposes, make all functions invalidate the entire TLB).

There is local_flush_tlb_mm() that uses the ASID as well.

> The problem is that with the above changes, the kernel seems to boot
> just fine, but when it starts running user space processes, the system
> eventually crashes with the messages:

I think there is fundamental problem with your approach. Basically the
kernel always runs with ASID 0 no matter which thread is the active one.
If it gets a TLB entry for a user page (either because of explicit
get_user etc. or just speculative access), the TLB will have ASID 0
associated with a user space address. When you switch tasks, even if
user space has a new ASID, the kernel would still use ASID 0 when
accessing the new user space address. If it hits a previously loaded
TLB, that would have the wrong translation.

So the kernel must use the same ASID as the user space application, or
at least have a different one for each application rather than the
common 0 value.

In Linux we use ASID 0 but only for brief periods of time when switching
the context or at roll-over but there is no active user space access
with ASID 0.

Some more comments on the patch below:
 
> --- a/arch/arm/kernel/entry-armv.S
> +++ b/arch/arm/kernel/entry-armv.S
> @@ -998,6 +998,12 @@ vector_\name:
>         mrs     lr, spsr
>         str     lr, [sp, #8]            @ save spsr
>  
> +       @ 
> +       @ Set kernel reserved ASID
> +       @
> +       mov     r0, #0
> +       mcr     p15, 0, r0, c13, c0, 1

You probably need an ISB after this (at least on ARMv7, not sure whether
it would work without on ARMv6).

> @@ -1147,6 +1153,12 @@ vector_addrexcptn:
>   * for CPUs with separate I & D caches.
>   */
>         .align  5
> +__pre_branch_swi_vector:
> +       sub     sp, sp, #S_FRAME_SIZE
> +       stmia   sp, {r0 - r12}                  @ Calling r0 - r12
> +       mov     r8, #0
> +       mcr     p15, 0, r8, c13, c0, 1
> +       ldr     pc, __branch_swi_vector

ISB after the ASID setting?

> diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S
> index 159d041..2a5472b 100644
> --- a/arch/arm/kernel/entry-common.S
> +++ b/arch/arm/kernel/entry-common.S
> @@ -14,6 +14,12 @@
>  
>  #include "entry-header.S"
>  
> +.macro set_context_id reg
> +       ldr     \reg, [tsk, #TSK_ACTIVE_MM]
> +       cmp     \reg, #0
> +       ldrne   \reg, [\reg, #MM_CONTEXT_ID]
> +       mcrne   p15, 0, \reg, c13, c0, 1
> +.endm

ISB.

> --- a/arch/arm/mm/context.c
> +++ b/arch/arm/mm/context.c
> @@ -31,7 +31,6 @@ void __init_new_context(struct task_struct *tsk, struct mm_struct *mm)
>  static void flush_context(void)
>  {
>         /* set the reserved ASID before flushing the TLB */
> -       asm("mcr        p15, 0, %0, c13, c0, 1\n" : : "r" (0));
>         isb();
>         local_flush_tlb_all();
>         if (icache_is_vivt_asid_tagged()) {
> @@ -122,10 +121,6 @@ unsigned int __new_asid(void)
>          */
>         if (unlikely((asid & ~ASID_MASK) == 0)) {
>                 asid = ++cpu_last_asid;
> -               /* set the reserved ASID before flushing the TLB */
> -               asm("mcr        p15, 0, %0, c13, c0, 1  @ set reserved context ID\n"
> -                   :
> -                   : "r" (0));
>                 isb();
>                 flush_tlb_all();
>                 if (icache_is_vivt_asid_tagged()) {

How do you handle the ASID roll-over since you removed this?

> +void __init kernel_asid_init(void)
> +{
> +       /* Set the kernel reserved ASID (= 0) */
> +       asm("mcr        p15, 0, %0, c13, c0, 1  @ set reserved context ID\n"
> +           :
> +           : "r" (0));

isb();

BTW, Cortex-A15 has full hardware virtualisation available, so this way
you could avoid many of the above problems.

Catalin




More information about the linux-arm-kernel mailing list