// SPDX-License-Identifier: GPL-2.01/*2* Generic ASID allocator.3*4* Based on arch/arm/mm/context.c5*6* Copyright (C) 2002-2003 Deep Blue Solutions Ltd, all rights reserved.7* Copyright (C) 2012 ARM Ltd.8*/910#include <linux/slab.h>11#include <linux/mm_types.h>1213#include <asm/asid.h>1415#define reserved_asid(info, cpu) *per_cpu_ptr((info)->reserved, cpu)1617#define ASID_MASK(info) (~GENMASK((info)->bits - 1, 0))18#define ASID_FIRST_VERSION(info) (1UL << ((info)->bits))1920#define asid2idx(info, asid) (((asid) & ~ASID_MASK(info)) >> (info)->ctxt_shift)21#define idx2asid(info, idx) (((idx) << (info)->ctxt_shift) & ~ASID_MASK(info))2223static void flush_context(struct asid_info *info)24{25int i;26u64 asid;2728/* Update the list of reserved ASIDs and the ASID bitmap. */29bitmap_zero(info->map, NUM_CTXT_ASIDS(info));3031for_each_possible_cpu(i) {32asid = atomic64_xchg_relaxed(&active_asid(info, i), 0);33/*34* If this CPU has already been through a35* rollover, but hasn't run another task in36* the meantime, we must preserve its reserved37* ASID, as this is the only trace we have of38* the process it is still running.39*/40if (asid == 0)41asid = reserved_asid(info, i);42__set_bit(asid2idx(info, asid), info->map);43reserved_asid(info, i) = asid;44}4546/*47* Queue a TLB invalidation for each CPU to perform on next48* context-switch49*/50cpumask_setall(&info->flush_pending);51}5253static bool check_update_reserved_asid(struct asid_info *info, u64 asid,54u64 newasid)55{56int cpu;57bool hit = false;5859/*60* Iterate over the set of reserved ASIDs looking for a match.61* If we find one, then we can update our mm to use newasid62* (i.e. the same ASID in the current generation) but we can't63* exit the loop early, since we need to ensure that all copies64* of the old ASID are updated to reflect the mm. Failure to do65* so could result in us missing the reserved ASID in a future66* generation.67*/68for_each_possible_cpu(cpu) {69if (reserved_asid(info, cpu) == asid) {70hit = true;71reserved_asid(info, cpu) = newasid;72}73}7475return hit;76}7778static u64 new_context(struct asid_info *info, atomic64_t *pasid,79struct mm_struct *mm)80{81static u32 cur_idx = 1;82u64 asid = atomic64_read(pasid);83u64 generation = atomic64_read(&info->generation);8485if (asid != 0) {86u64 newasid = generation | (asid & ~ASID_MASK(info));8788/*89* If our current ASID was active during a rollover, we90* can continue to use it and this was just a false alarm.91*/92if (check_update_reserved_asid(info, asid, newasid))93return newasid;9495/*96* We had a valid ASID in a previous life, so try to re-use97* it if possible.98*/99if (!__test_and_set_bit(asid2idx(info, asid), info->map))100return newasid;101}102103/*104* Allocate a free ASID. If we can't find one, take a note of the105* currently active ASIDs and mark the TLBs as requiring flushes. We106* always count from ASID #2 (index 1), as we use ASID #0 when setting107* a reserved TTBR0 for the init_mm and we allocate ASIDs in even/odd108* pairs.109*/110asid = find_next_zero_bit(info->map, NUM_CTXT_ASIDS(info), cur_idx);111if (asid != NUM_CTXT_ASIDS(info))112goto set_asid;113114/* We're out of ASIDs, so increment the global generation count */115generation = atomic64_add_return_relaxed(ASID_FIRST_VERSION(info),116&info->generation);117flush_context(info);118119/* We have more ASIDs than CPUs, so this will always succeed */120asid = find_next_zero_bit(info->map, NUM_CTXT_ASIDS(info), 1);121122set_asid:123__set_bit(asid, info->map);124cur_idx = asid;125cpumask_clear(mm_cpumask(mm));126return idx2asid(info, asid) | generation;127}128129/*130* Generate a new ASID for the context.131*132* @pasid: Pointer to the current ASID batch allocated. It will be updated133* with the new ASID batch.134* @cpu: current CPU ID. Must have been acquired through get_cpu()135*/136void asid_new_context(struct asid_info *info, atomic64_t *pasid,137unsigned int cpu, struct mm_struct *mm)138{139unsigned long flags;140u64 asid;141142raw_spin_lock_irqsave(&info->lock, flags);143/* Check that our ASID belongs to the current generation. */144asid = atomic64_read(pasid);145if ((asid ^ atomic64_read(&info->generation)) >> info->bits) {146asid = new_context(info, pasid, mm);147atomic64_set(pasid, asid);148}149150if (cpumask_test_and_clear_cpu(cpu, &info->flush_pending))151info->flush_cpu_ctxt_cb();152153atomic64_set(&active_asid(info, cpu), asid);154cpumask_set_cpu(cpu, mm_cpumask(mm));155raw_spin_unlock_irqrestore(&info->lock, flags);156}157158/*159* Initialize the ASID allocator160*161* @info: Pointer to the asid allocator structure162* @bits: Number of ASIDs available163* @asid_per_ctxt: Number of ASIDs to allocate per-context. ASIDs are164* allocated contiguously for a given context. This value should be a power of165* 2.166*/167int asid_allocator_init(struct asid_info *info,168u32 bits, unsigned int asid_per_ctxt,169void (*flush_cpu_ctxt_cb)(void))170{171info->bits = bits;172info->ctxt_shift = ilog2(asid_per_ctxt);173info->flush_cpu_ctxt_cb = flush_cpu_ctxt_cb;174/*175* Expect allocation after rollover to fail if we don't have at least176* one more ASID than CPUs. ASID #0 is always reserved.177*/178WARN_ON(NUM_CTXT_ASIDS(info) - 1 <= num_possible_cpus());179atomic64_set(&info->generation, ASID_FIRST_VERSION(info));180info->map = bitmap_zalloc(NUM_CTXT_ASIDS(info), GFP_KERNEL);181if (!info->map)182return -ENOMEM;183184raw_spin_lock_init(&info->lock);185186return 0;187}188189190