Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae120d9e authored by Marc Zyngier's avatar Marc Zyngier Committed by Russell King
Browse files

ARM: 7767/1: let the ASID allocator handle suspended animation



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.

Cc: <stable@vger.kernel.org> # 3.9
Acked-by: default avatarWill Deacon <will.deacon@arm.com>
Acked-by: default avatarCatalin Carinas <catalin.marinas@arm.com>
Signed-off-by: default avatarMarc Zyngier <marc.zyngier@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 8121cf31
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
@@ -128,6 +128,15 @@ static void flush_context(unsigned int cpu)
			asid = 0;
		} else {
			asid = atomic64_xchg(&per_cpu(active_asids, i), 0);
			/*
			 * If this CPU has already been through a
			 * rollover, but hasn't run another task in
			 * the meantime, we must preserve its reserved
			 * ASID, as this is the only trace we have of
			 * the process it is still running.
			 */
			if (asid == 0)
				asid = per_cpu(reserved_asids, i);
			__set_bit(ASID_TO_IDX(asid), asid_map);
		}
		per_cpu(reserved_asids, i) = asid;