diff options
author | Marc Zyngier <Marc.Zyngier@arm.com> | 2013-06-21 07:06:19 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-07-21 21:21:33 -0400 |
commit | b7dc4032cd44843ea93119adb00a5f15b7b05943 (patch) | |
tree | 52faa078648b8fa6418ee78ae4abca59265bca66 /arch | |
parent | c9f3f7f79f3ee852f253bdb1dbbb43d79bcaa6c2 (diff) |
ARM: 7767/1: let the ASID allocator handle suspended animation
commit ae120d9edfe96628f03d87634acda0bfa7110632 upstream.
When a CPU is running a process, the ASID for that process is
held in a per-CPU variable (the "active ASIDs" array). When
the ASID allocator handles a rollover, it copies the active
ASIDs into a "reserved ASIDs" array to ensure that a process
currently running on another CPU will continue to run unaffected.
The active array is zero-ed to indicate that a rollover occurred.
Because of this mechanism, a reserved ASID is only remembered for
a single rollover. A subsequent rollover will completely refill
the reserved ASIDs array.
In a severely oversubscribed environment where a CPU can be
prevented from running for extended periods of time (think virtual
machines), the above has a horrible side effect:
[P{a} denotes process P running with ASID a]
CPU-0 CPU-1
A{x} [active = <x 0>]
[suspended] runs B{y} [active = <x y>]
[rollover:
active = <0 0>
reserved = <x y>]
runs B{y} [active = <0 y>
reserved = <x y>]
[rollover:
active = <0 0>
reserved = <0 y>]
runs C{x} [active = <0 x>]
[resumes]
runs A{x}
At that stage, both A and C have the same ASID, with deadly
consequences.
The fix is to preserve reserved ASIDs across rollovers if
the CPU doesn't have an active ASID when the rollover occurs.
Acked-by: Will Deacon <will.deacon@arm.com>
Acked-by: Catalin Carinas <catalin.marinas@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/arm/mm/context.c | 9 |
1 files changed, 9 insertions, 0 deletions
diff --git a/arch/arm/mm/context.c b/arch/arm/mm/context.c index 2ac37372ef52..8e12fcbb2c63 100644 --- a/arch/arm/mm/context.c +++ b/arch/arm/mm/context.c | |||
@@ -128,6 +128,15 @@ static void flush_context(unsigned int cpu) | |||
128 | asid = 0; | 128 | asid = 0; |
129 | } else { | 129 | } else { |
130 | asid = atomic64_xchg(&per_cpu(active_asids, i), 0); | 130 | asid = atomic64_xchg(&per_cpu(active_asids, i), 0); |
131 | /* | ||
132 | * If this CPU has already been through a | ||
133 | * rollover, but hasn't run another task in | ||
134 | * the meantime, we must preserve its reserved | ||
135 | * ASID, as this is the only trace we have of | ||
136 | * the process it is still running. | ||
137 | */ | ||
138 | if (asid == 0) | ||
139 | asid = per_cpu(reserved_asids, i); | ||
131 | __set_bit(ASID_TO_IDX(asid), asid_map); | 140 | __set_bit(ASID_TO_IDX(asid), asid_map); |
132 | } | 141 | } |
133 | per_cpu(reserved_asids, i) = asid; | 142 | per_cpu(reserved_asids, i) = asid; |