/* Steal a context from a task that has one at the moment. * *ThisisusedwhenwearerunningoutofavailablePIDnumbers *ontheprocessors. * *Thisisn'tanLRUsystem,itjustfreesupeachcontextin *turn(sort-ofpseudo-randomreplacement:).Thiswouldbethe *placetoimplementanLRUschemeifanyonewasmotivatedtodoit. *--paulus * *Forcontextstealing,weuseaslightlydifferentapproachfor *SMPandUP.Basically,theUPoneissimpleranddoesn'tuse *thestalemapaswecanjustflushthelocalCPU *--benh
*/ staticunsignedint steal_context_smp(unsignedint id)
{ struct mm_struct *mm; unsignedint cpu, max, i;
max = LAST_CONTEXT - FIRST_CONTEXT;
/* Attempt to free next_context first and then loop until we manage */ while (max--) { /* Pick up the victim mm */
mm = context_mm[id];
/* We have a candidate victim, check if it's active, on SMP *wecannotstealactivecontexts
*/ if (mm->context.active) {
id++; if (id > LAST_CONTEXT)
id = FIRST_CONTEXT; continue;
}
/* Mark this mm has having no context anymore */
mm->context.id = MMU_NO_CONTEXT;
/* Mark it stale on all CPUs that used this mm. For threaded *implementations,wesetitonallthreadsoneachcore *representedinthemask.Afutureimplementationwilluse *acoremapinsteadbutthiswilldofornow.
*/
for_each_cpu(cpu, mm_cpumask(mm)) { for (i = cpu_first_thread_sibling(cpu);
i <= cpu_last_thread_sibling(cpu); i++) { if (stale_map[i])
__set_bit(id, stale_map[i]);
}
cpu = i - 1;
} return id;
}
/* This will happen if you have more CPUs than available contexts, *allwecandohereiswaitabitandtryagain
*/
raw_spin_unlock(&context_lock);
cpu_relax();
raw_spin_lock(&context_lock);
/* This will cause the caller to try again */ return MMU_NO_CONTEXT;
}
staticunsignedint steal_all_contexts(void)
{ struct mm_struct *mm; int cpu = smp_processor_id(); unsignedint id;
for (id = FIRST_CONTEXT; id <= LAST_CONTEXT; id++) { /* Pick up the victim mm */
mm = context_mm[id];
/* Mark this mm as having no context anymore */
mm->context.id = MMU_NO_CONTEXT; if (id != FIRST_CONTEXT) {
context_mm[id] = NULL;
__clear_bit(id, context_map);
} if (IS_ENABLED(CONFIG_SMP))
__clear_bit(id, stale_map[cpu]);
}
/* Flush the TLB for all contexts (not to be used on SMP) */
_tlbil_all();
nr_free_contexts = LAST_CONTEXT - FIRST_CONTEXT;
return FIRST_CONTEXT;
}
/* Note that this will also be called on SMP if all other CPUs are *offlined,whichmeansthatitmaybecalledforcpu!=0.For *thistowork,wesomewhatassumethatCPUsthatareonlined *comeupwithafullycleanTLB(orarecleanedwhenofflined)
*/ staticunsignedint steal_context_up(unsignedint id)
{ struct mm_struct *mm; int cpu = smp_processor_id();
/* Pick up the victim mm */
mm = context_mm[id];
/* Flush the TLB for that context */
local_flush_tlb_mm(mm);
/* Mark this mm has having no context anymore */
mm->context.id = MMU_NO_CONTEXT;
/* XXX This clear should ultimately be part of local_flush_tlb_mm */ if (IS_ENABLED(CONFIG_SMP))
__clear_bit(id, stale_map[cpu]);
void switch_mmu_context(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk)
{ unsignedint id; unsignedint i, cpu = smp_processor_id(); unsignedlong *map;
/* No lockless fast path .. yet */
raw_spin_lock(&context_lock);
if (IS_ENABLED(CONFIG_SMP)) { /* Mark us active and the previous one not anymore */
next->context.active++; if (prev) {
WARN_ON(prev->context.active < 1);
prev->context.active--;
}
}
again:
/* If we already have a valid assigned context, skip all that */
id = next->context.id; if (likely(id != MMU_NO_CONTEXT)) goto ctxt_ok;
/* We really don't have a context, let's try to acquire one */
id = next_context; if (id > LAST_CONTEXT)
id = FIRST_CONTEXT;
map = context_map;
/* No more free contexts, let's try to steal one */ if (nr_free_contexts == 0) { if (num_online_cpus() > 1) {
id = steal_context_smp(id); if (id == MMU_NO_CONTEXT) goto again; goto stolen;
} if (IS_ENABLED(CONFIG_PPC_8xx))
id = steal_all_contexts(); else
id = steal_context_up(id); goto stolen;
}
nr_free_contexts--;
/* We know there's at least one free context, try to find it */ while (__test_and_set_bit(id, map)) {
id = find_next_zero_bit(map, LAST_CONTEXT+1, id); if (id > LAST_CONTEXT)
id = FIRST_CONTEXT;
}
stolen:
next_context = id + 1;
context_mm[id] = next;
next->context.id = id;
ctxt_ok:
/* If that context got marked stale on this CPU, then flush the *localTLBforitandunmarkitbeforeweuseit
*/ if (IS_ENABLED(CONFIG_SMP) && test_bit(id, stale_map[cpu])) {
local_flush_tlb_mm(next);
/* XXX This clear should ultimately be part of local_flush_tlb_mm */ for (i = cpu_first_thread_sibling(cpu);
i <= cpu_last_thread_sibling(cpu); i++) { if (stale_map[i])
__clear_bit(id, stale_map[i]);
}
}
/* Flick the MMU and release lock */ if (IS_ENABLED(CONFIG_BDI_SWITCH))
abatron_pteptrs[1] = next->pgd;
set_context(id, next->pgd); #ifdefined(CONFIG_BOOKE) && defined(CONFIG_PPC_KUAP)
tsk->thread.pid = id; #endif
raw_spin_unlock(&context_lock);
}
raw_spin_lock_irqsave(&context_lock, flags);
id = mm->context.id; if (id != MMU_NO_CONTEXT) {
__clear_bit(id, context_map);
mm->context.id = MMU_NO_CONTEXT;
context_mm[id] = NULL;
nr_free_contexts++;
}
raw_spin_unlock_irqrestore(&context_lock, flags);
}
staticint mmu_ctx_cpu_prepare(unsignedint cpu)
{ /* We don't touch CPU 0 map, it's allocated at aboot and kept *aroundforever
*/ if (cpu == boot_cpuid) return0;
/* We also clear the cpu_vm_mask bits of CPUs going away */
clear_tasks_mm_cpumask(cpu); #endif return0;
}
/* *Initializethecontextmanagementstuff.
*/ void __init mmu_context_init(void)
{ /* Mark init_mm as being active on all possible CPUs since *we'llgetcalledwithprev==init_mmthefirsttime *wescheduleonagivenCPU
*/
init_mm.context.active = NR_CPUS;
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.