/* * This function is specific to ASIDs, and should not be called when * MMIDs are in use.
*/ if (WARN_ON(IS_ENABLED(CONFIG_DEBUG_VM) && cpu_has_mmid)) return;
cpu = smp_processor_id();
asid = asid_cache(cpu);
if (!((asid += cpu_asid_inc()) & cpu_asid_mask(&cpu_data[cpu]))) { if (cpu_has_vtag_icache)
flush_icache_all();
local_flush_tlb_all(); /* start new asid cycle */
}
set_cpu_context(cpu, mm, asid);
asid_cache(cpu) = asid;
}
EXPORT_SYMBOL_GPL(get_new_mmu_context);
void check_mmu_context(struct mm_struct *mm)
{ unsignedint cpu = smp_processor_id();
/* * This function is specific to ASIDs, and should not be called when * MMIDs are in use.
*/ if (WARN_ON(IS_ENABLED(CONFIG_DEBUG_VM) && cpu_has_mmid)) return;
/* Check if our ASID is of an older version and thus invalid */ if (!asid_versions_eq(cpu, cpu_context(cpu, mm), asid_cache(cpu)))
get_new_mmu_context(mm);
}
EXPORT_SYMBOL_GPL(check_mmu_context);
staticvoid flush_context(void)
{
u64 mmid; int cpu;
/* Update the list of reserved MMIDs and the MMID bitmap */
bitmap_zero(mmid_map, num_mmids);
/* Reserve an MMID for kmap/wired entries */
__set_bit(MMID_KERNEL_WIRED, mmid_map);
/* * If this CPU has already been through a * rollover, but hasn't run another task in * the meantime, we must preserve its reserved * MMID, as this is the only trace we have of * the process it is still running.
*/ if (mmid == 0)
mmid = per_cpu(reserved_mmids, cpu);
/* * Queue a TLB invalidation for each CPU to perform on next * context-switch
*/
cpumask_setall(&tlb_flush_pending);
}
staticbool check_update_reserved_mmid(u64 mmid, u64 newmmid)
{ bool hit; int cpu;
/* * Iterate over the set of reserved MMIDs looking for a match. * If we find one, then we can update our mm to use newmmid * (i.e. the same MMID in the current generation) but we can't * exit the loop early, since we need to ensure that all copies * of the old MMID are updated to reflect the mm. Failure to do * so could result in us missing the reserved MMID in a future * generation.
*/
hit = false;
for_each_possible_cpu(cpu) { if (per_cpu(reserved_mmids, cpu) == mmid) {
hit = true;
per_cpu(reserved_mmids, cpu) = newmmid;
}
}
mmid = cpu_context(0, mm);
version = atomic64_read(&mmid_version);
mmid_mask = cpu_asid_mask(&boot_cpu_data);
if (!asid_versions_eq(0, mmid, 0)) {
u64 newmmid = version | (mmid & mmid_mask);
/* * If our current MMID was active during a rollover, we * can continue to use it and this was just a false alarm.
*/ if (check_update_reserved_mmid(mmid, newmmid)) {
mmid = newmmid; goto set_context;
}
/* * We had a valid MMID in a previous life, so try to re-use * it if possible.
*/ if (!__test_and_set_bit(mmid & mmid_mask, mmid_map)) {
mmid = newmmid; goto set_context;
}
}
/* Allocate a free MMID */
mmid = find_next_zero_bit(mmid_map, num_mmids, cur_idx); if (mmid != num_mmids) goto reserve_mmid;
/* We're out of MMIDs, so increment the global version */
version = atomic64_add_return_relaxed(asid_first_version(0),
&mmid_version);
/* Note currently active MMIDs & mark TLBs as requiring flushes */
flush_context();
/* We have more MMIDs than CPUs, so this will always succeed */
mmid = find_first_zero_bit(mmid_map, num_mmids);
if (!cpu_has_mmid) {
check_mmu_context(mm);
write_c0_entryhi(cpu_asid(cpu, mm)); goto setup_pgd;
}
/* * MMID switch fast-path, to avoid acquiring cpu_mmid_lock when it's * unnecessary. * * The memory ordering here is subtle. If our active_mmids is non-zero * and the MMID matches the current version, then we update the CPU's * asid_cache with a relaxed cmpxchg. Racing with a concurrent rollover * means that either: * * - We get a zero back from the cmpxchg and end up waiting on * cpu_mmid_lock in check_mmu_context(). Taking the lock synchronises * with the rollover and so we are forced to see the updated * generation. * * - We get a valid MMID back from the cmpxchg, which means the * relaxed xchg in flush_context will treat us as reserved * because atomic RmWs are totally ordered for a given location.
*/
ctx = cpu_context(cpu, mm);
old_active_mmid = READ_ONCE(cpu_data[cpu].asid_cache); if (!old_active_mmid ||
!asid_versions_eq(cpu, ctx, atomic64_read(&mmid_version)) ||
!cmpxchg_relaxed(&cpu_data[cpu].asid_cache, old_active_mmid, ctx)) {
raw_spin_lock_irqsave(&cpu_mmid_lock, flags);
/* * Invalidate the local TLB if needed. Note that we must only clear our * bit in tlb_flush_pending after this is complete, so that the * cpu_has_shared_ftlb_entries case below isn't misled.
*/ if (cpumask_test_cpu(cpu, &tlb_flush_pending)) { if (cpu_has_vtag_icache)
flush_icache_all();
local_flush_tlb_all();
cpumask_clear_cpu(cpu, &tlb_flush_pending);
}
/* * If this CPU shares FTLB entries with its siblings and one or more of * those siblings hasn't yet invalidated its TLB following a version * increase then we need to invalidate any TLB entries for our MMID * that we might otherwise pick up from a sibling. * * We ifdef on CONFIG_SMP because cpu_sibling_map isn't defined in * CONFIG_SMP=n kernels.
*/ #ifdef CONFIG_SMP if (cpu_has_shared_ftlb_entries &&
cpumask_intersects(&tlb_flush_pending, &cpu_sibling_map[cpu])) { /* Ensure we operate on the new MMID */
mtc0_tlbw_hazard();
/* * Invalidate all TLB entries associated with the new * MMID, and wait for the invalidation to complete.
*/
ginvt_mmid();
sync_ginv();
} #endif
staticint mmid_init(void)
{ if (!cpu_has_mmid) return 0;
/* * Expect allocation after rollover to fail if we don't have at least * one more MMID than CPUs.
*/
num_mmids = asid_first_version(0);
WARN_ON(num_mmids <= num_possible_cpus());
atomic64_set(&mmid_version, asid_first_version(0));
mmid_map = bitmap_zalloc(num_mmids, GFP_KERNEL); if (!mmid_map)
panic("Failed to allocate bitmap for %u MMIDs\n", num_mmids);
/* Reserve an MMID for kmap/wired entries */
__set_bit(MMID_KERNEL_WIRED, mmid_map);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.