// SPDX-License-Identifier: GPL-2.0 /* * linux/arch/alpha/kernel/smp.c * * 2001-07-09 Phil Ezolt (Phillip.Ezolt@compaq.com) * Renamed modified smp_call_function to smp_call_function_on_cpu() * Created an function that conforms to the old calling convention * of smp_call_function(). * * This is helpful for DCPI. *
*/
/* Set to a secondary's cpuid when it comes online. */ staticint smp_secondary_alive = 0;
int smp_num_probed; /* Internal processor count */ int smp_num_cpus = 1; /* Number that came online. */
EXPORT_SYMBOL(smp_num_cpus);
/* * Called by both boot and secondaries to move global data into * per-processor storage.
*/ staticinlinevoid __init
smp_store_cpu_info(int cpuid)
{
cpu_data[cpuid].loops_per_jiffy = loops_per_jiffy;
cpu_data[cpuid].last_asn = ASN_FIRST_VERSION;
cpu_data[cpuid].need_new_asn = 0;
cpu_data[cpuid].asn_lock = 0;
}
/* * Ideally sets up per-cpu profiling hooks. Doesn't do much now...
*/ staticinlinevoid __init
smp_setup_percpu_timer(int cpuid)
{
cpu_data[cpuid].prof_counter = 1;
cpu_data[cpuid].prof_multiplier = 1;
}
while (time_before(jiffies, stop)) { if (!smp_secondary_alive) return;
barrier();
}
printk("wait_boot_cpu_to_stop: FAILED on CPU %d, hanging now\n", cpuid); for (;;)
barrier();
}
/* * Where secondaries begin a life of C.
*/ void __init
smp_callin(void)
{ int cpuid = hard_smp_processor_id();
if (cpu_online(cpuid)) {
printk("??, cpu 0x%x already present??\n", cpuid);
BUG();
}
set_cpu_online(cpuid, true);
/* Turn on machine checks. */
wrmces(7);
/* Set trap vectors. */
trap_init();
/* Set interrupt vector. */
wrent(entInt, 0);
/* Get our local ticker going. */
smp_setup_percpu_timer(cpuid);
init_clockevent();
/* Call platform-specific callin, if specified */ if (alpha_mv.smp_callin)
alpha_mv.smp_callin();
/* All kernel threads share the same mm context. */
mmgrab(&init_mm);
current->active_mm = &init_mm;
/* inform the notifiers about the new cpu */
notify_cpu_starting(cpuid);
/* Must have completely accurate bogos. */
local_irq_enable();
/* Wait boot CPU to stop with irq enabled before running
calibrate_delay. */
wait_boot_cpu_to_stop(cpuid);
mb();
calibrate_delay();
smp_store_cpu_info(cpuid); /* Allow master to continue only after we written loops_per_jiffy. */
wmb();
smp_secondary_alive = 1;
DBGS(("smp_callin: commencing CPU %d current %p active_mm %p\n",
cpuid, current, current->active_mm));
cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
}
/* Wait until hwrpb->txrdy is clear for cpu. Return -1 on timeout. */ staticint
wait_for_txrdy (unsignedlong cpumask)
{ unsignedlong timeout;
if (!(hwrpb->txrdy & cpumask)) return 0;
timeout = jiffies + 10*HZ; while (time_before(jiffies, timeout)) { if (!(hwrpb->txrdy & cpumask)) return 0;
udelay(10);
barrier();
}
return -1;
}
/* * Send a message to a secondary's console. "START" is one such * interesting message. ;-)
*/ staticvoid
send_secondary_console_msg(char *str, int cpuid)
{ struct percpu_struct *cpu; registerchar *cp1, *cp2; unsignedlong cpumask;
size_t len;
DBGS((KERN_INFO "recv_secondary_console_msg: on %d " "message is '%s'\n", mycpu, buf));
}
hwrpb->txrdy = 0;
}
/* * Convince the console to have a secondary cpu begin execution.
*/ staticint
secondary_cpu_start(int cpuid, struct task_struct *idle)
{ struct percpu_struct *cpu; struct pcb_struct *hwpcb, *ipcb; unsignedlong timeout;
/* Initialize the CPU's HWPCB to something just good enough for us to get started. Immediately after starting, we'll swpctx to the target idle task's pcb. Reuse the stack in the mean
time. Precalculate the target PCBB. */
hwpcb->ksp = (unsignedlong)ipcb + sizeof(union thread_union) - 16;
hwpcb->usp = 0;
hwpcb->ptbr = ipcb->ptbr;
hwpcb->pcc = 0;
hwpcb->asn = 0;
hwpcb->unique = virt_to_phys(ipcb);
hwpcb->flags = ipcb->flags;
hwpcb->res1 = hwpcb->res2 = 0;
/* Setup HWRPB fields that SRM uses to activate secondary CPU */
hwrpb->CPU_restart = __smp_callin;
hwrpb->CPU_restart_data = (unsignedlong) __smp_callin;
/* Recalculate and update the HWRPB checksum */
hwrpb_update_checksum(hwrpb);
/* * Send a "start" command to the specified processor.
*/
/* SRM III 3.4.1.3 */
cpu->flags |= 0x22; /* turn on Context Valid and Restart Capable */
cpu->flags &= ~1; /* turn off Bootstrap In Progress */
wmb();
send_secondary_console_msg("START\r\n", cpuid);
/* Wait 10 seconds for an ACK from the console. */
timeout = jiffies + 10*HZ; while (time_before(jiffies, timeout)) { if (cpu->flags & 1) goto started;
udelay(10);
barrier();
}
printk(KERN_ERR "SMP: Processor %d failed to start.\n", cpuid); return -1;
started:
DBGS(("secondary_cpu_start: SUCCESS for CPU %d!!!\n", cpuid)); return 0;
}
/* * Bring one cpu online.
*/ staticint
smp_boot_one_cpu(int cpuid, struct task_struct *idle)
{ unsignedlong timeout;
/* Signal the secondary to wait a moment. */
smp_secondary_alive = -1;
/* Whirrr, whirrr, whirrrrrrrrr... */ if (secondary_cpu_start(cpuid, idle)) return -1;
/* Notify the secondary CPU it can run calibrate_delay. */
mb();
smp_secondary_alive = 0;
/* We've been acked by the console; wait one second for
the task to start up for real. */
timeout = jiffies + 1*HZ; while (time_before(jiffies, timeout)) { if (smp_secondary_alive == 1) goto alive;
udelay(10);
barrier();
}
/* We failed to boot the CPU. */
printk(KERN_ERR "SMP: Processor %d is stuck.\n", cpuid); return -1;
alive: /* Another "Red Snapper". */ return 0;
}
/* * Called from setup_arch. Detect an SMP system and which processors * are present.
*/ void __init
setup_smp(void)
{ struct percpu_struct *cpubase, *cpu; unsignedlong i;
if (boot_cpuid != 0) {
printk(KERN_WARNING "SMP: Booting off cpu %d instead of 0?\n",
boot_cpuid);
}
if (hwrpb->nr_processors > 1) { int boot_cpu_palrev;
for (i = 0; i < hwrpb->nr_processors; i++) {
cpu = (struct percpu_struct *)
((char *)cpubase + i*hwrpb->processor_size); if ((cpu->flags & 0x1cc) == 0x1cc) {
smp_num_probed++;
set_cpu_possible(i, true);
set_cpu_present(i, true);
cpu->pal_revision = boot_cpu_palrev;
}
DBGS(("setup_smp: CPU %d: flags 0x%lx type 0x%lx\n",
i, cpu->flags, cpu->type));
DBGS(("setup_smp: CPU %d: PAL rev 0x%lx\n",
i, cpu->pal_revision));
}
} else {
smp_num_probed = 1;
}
/* * Called by smp_init prepare the secondaries
*/ void __init
smp_prepare_cpus(unsignedint max_cpus)
{ /* Take care of some initial bookkeeping. */
memset(ipi_data, 0, sizeof(ipi_data));
/* Nothing to do on a UP box, or when told not to. */ if (smp_num_probed == 1 || max_cpus == 0) {
init_cpu_possible(cpumask_of(boot_cpuid));
init_cpu_present(cpumask_of(boot_cpuid));
printk(KERN_INFO "SMP mode deactivated.\n"); return;
}
printk(KERN_INFO "SMP starting up secondaries.\n");
smp_num_cpus = smp_num_probed;
}
int
__cpu_up(unsignedint cpu, struct task_struct *tidle)
{
smp_boot_one_cpu(cpu, tidle);
#if 0
DBGS(("handle_ipi: on CPU %d ops 0x%lx PC 0x%lx\n",
this_cpu, *pending_ipis, regs->pc)); #endif
mb(); /* Order interrupt and bit testing. */ while ((ops = xchg(pending_ipis, 0)) != 0) {
mb(); /* Order bit clearing and data access. */ do { unsignedlong which;
which = ops & -ops;
ops &= ~which;
which = __ffs(which);
switch (which) { case IPI_RESCHEDULE:
scheduler_ipi(); break;
case IPI_CALL_FUNC:
generic_smp_call_function_interrupt(); break;
case IPI_CPU_STOP:
halt();
default:
printk(KERN_CRIT "Unknown IPI on CPU %d: %lu\n",
this_cpu, which); break;
}
} while (ops);
mb(); /* Order data access and bit testing. */
}
cpu_data[this_cpu].ipi_count++;
if (hwrpb->txrdy)
recv_secondary_console_msg();
}
void
arch_smp_send_reschedule(int cpu)
{ #ifdef DEBUG_IPI_MSG if (cpu == hard_smp_processor_id())
printk(KERN_WARNING "smp_send_reschedule: Sending IPI to self.\n"); #endif
send_ipi_message(cpumask_of(cpu), IPI_RESCHEDULE);
}
void
smp_send_stop(void)
{
cpumask_t to_whom;
cpumask_copy(&to_whom, cpu_online_mask);
cpumask_clear_cpu(smp_processor_id(), &to_whom); #ifdef DEBUG_IPI_MSG if (hard_smp_processor_id() != boot_cpu_id)
printk(KERN_WARNING "smp_send_stop: Not on boot cpu.\n"); #endif
send_ipi_message(&to_whom, IPI_CPU_STOP);
}
void
flush_tlb_all(void)
{ /* Although we don't have any data to pass, we do want to
synchronize with the other processors. */
on_each_cpu(ipi_flush_tlb_all, NULL, 1);
}
void
flush_tlb_range(struct vm_area_struct *vma, unsignedlong start, unsignedlong end)
{ /* On the Alpha we always flush the whole user tlb. */
flush_tlb_mm(vma->vm_mm);
}
EXPORT_SYMBOL(flush_tlb_range);
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.