📄 smpboot_64.c
字号:
/* * x86 SMP booting functions * * (c) 1995 Alan Cox, Building #3 <alan@redhat.com> * (c) 1998, 1999, 2000 Ingo Molnar <mingo@redhat.com> * Copyright 2001 Andi Kleen, SuSE Labs. * * Much of the core SMP work is based on previous work by Thomas Radke, to * whom a great many thanks are extended. * * Thanks to Intel for making available several different Pentium, * Pentium Pro and Pentium-II/Xeon MP machines. * Original development of Linux SMP code supported by Caldera. * * This code is released under the GNU General Public License version 2 * * Fixes * Felix Koop : NR_CPUS used properly * Jose Renau : Handle single CPU case. * Alan Cox : By repeated request 8) - Total BogoMIP report. * Greg Wright : Fix for kernel stacks panic. * Erich Boleyn : MP v1.4 and additional changes. * Matthias Sattler : Changes for 2.1 kernel map. * Michel Lespinasse : Changes for 2.1 kernel map. * Michael Chastain : Change trampoline.S to gnu as. * Alan Cox : Dumb bug: 'B' step PPro's are fine * Ingo Molnar : Added APIC timers, based on code * from Jose Renau * Ingo Molnar : various cleanups and rewrites * Tigran Aivazian : fixed "0.00 in /proc/uptime on SMP" bug. * Maciej W. Rozycki : Bits for genuine 82489DX APICs * Andi Kleen : Changed for SMP boot into long mode. * Rusty Russell : Hacked into shape for new "hotplug" boot process. * Andi Kleen : Converted to new state machine. * Various cleanups. * Probably mostly hotplug CPU ready now. * Ashok Raj : CPU hotplug support */#include <linux/init.h>#include <linux/mm.h>#include <linux/kernel_stat.h>#include <linux/bootmem.h>#include <linux/thread_info.h>#include <linux/module.h>#include <linux/delay.h>#include <linux/mc146818rtc.h>#include <linux/smp.h>#include <linux/kdebug.h>#include <asm/mtrr.h>#include <asm/pgalloc.h>#include <asm/desc.h>#include <asm/tlbflush.h>#include <asm/proto.h>#include <asm/nmi.h>#include <asm/irq.h>#include <asm/hw_irq.h>#include <asm/numa.h>/* Number of siblings per CPU package */int smp_num_siblings = 1;EXPORT_SYMBOL(smp_num_siblings);/* Last level cache ID of each logical CPU */DEFINE_PER_CPU(u8, cpu_llc_id) = BAD_APICID;/* Bitmask of currently online CPUs */cpumask_t cpu_online_map __read_mostly;EXPORT_SYMBOL(cpu_online_map);/* * Private maps to synchronize booting between AP and BP. * Probably not needed anymore, but it makes for easier debugging. -AK */cpumask_t cpu_callin_map;cpumask_t cpu_callout_map;EXPORT_SYMBOL(cpu_callout_map);cpumask_t cpu_possible_map;EXPORT_SYMBOL(cpu_possible_map);/* Per CPU bogomips and other parameters */DEFINE_PER_CPU_SHARED_ALIGNED(struct cpuinfo_x86, cpu_info);EXPORT_PER_CPU_SYMBOL(cpu_info);/* Set when the idlers are all forked */int smp_threads_ready;/* representing HT siblings of each logical CPU */DEFINE_PER_CPU(cpumask_t, cpu_sibling_map);EXPORT_PER_CPU_SYMBOL(cpu_sibling_map);/* representing HT and core siblings of each logical CPU */DEFINE_PER_CPU(cpumask_t, cpu_core_map);EXPORT_PER_CPU_SYMBOL(cpu_core_map);/* * Trampoline 80x86 program as an array. */extern const unsigned char trampoline_data[];extern const unsigned char trampoline_end[];/* State of each CPU */DEFINE_PER_CPU(int, cpu_state) = { 0 };/* * Store all idle threads, this can be reused instead of creating * a new thread. Also avoids complicated thread destroy functionality * for idle threads. */struct task_struct *idle_thread_array[NR_CPUS] __cpuinitdata ;#define get_idle_for_cpu(x) (idle_thread_array[(x)])#define set_idle_for_cpu(x,p) (idle_thread_array[(x)] = (p))/* * Currently trivial. Write the real->protected mode * bootstrap into the page concerned. The caller * has made sure it's suitably aligned. */static unsigned long __cpuinit setup_trampoline(void){ void *tramp = __va(SMP_TRAMPOLINE_BASE); memcpy(tramp, trampoline_data, trampoline_end - trampoline_data); return virt_to_phys(tramp);}/* * The bootstrap kernel entry code has set these up. Save them for * a given CPU */static void __cpuinit smp_store_cpu_info(int id){ struct cpuinfo_x86 *c = &cpu_data(id); *c = boot_cpu_data; c->cpu_index = id; identify_cpu(c); print_cpu_info(c);}static atomic_t init_deasserted __cpuinitdata;/* * Report back to the Boot Processor. * Running on AP. */void __cpuinit smp_callin(void){ int cpuid, phys_id; unsigned long timeout; /* * If waken up by an INIT in an 82489DX configuration * we may get here before an INIT-deassert IPI reaches * our local APIC. We have to wait for the IPI or we'll * lock up on an APIC access. */ while (!atomic_read(&init_deasserted)) cpu_relax(); /* * (This works even if the APIC is not enabled.) */ phys_id = GET_APIC_ID(apic_read(APIC_ID)); cpuid = smp_processor_id(); if (cpu_isset(cpuid, cpu_callin_map)) { panic("smp_callin: phys CPU#%d, CPU#%d already present??\n", phys_id, cpuid); } Dprintk("CPU#%d (phys ID: %d) waiting for CALLOUT\n", cpuid, phys_id); /* * STARTUP IPIs are fragile beasts as they might sometimes * trigger some glue motherboard logic. Complete APIC bus * silence for 1 second, this overestimates the time the * boot CPU is spending to send the up to 2 STARTUP IPIs * by a factor of two. This should be enough. */ /* * Waiting 2s total for startup (udelay is not yet working) */ timeout = jiffies + 2*HZ; while (time_before(jiffies, timeout)) { /* * Has the boot CPU finished it's STARTUP sequence? */ if (cpu_isset(cpuid, cpu_callout_map)) break; cpu_relax(); } if (!time_before(jiffies, timeout)) { panic("smp_callin: CPU%d started up but did not get a callout!\n", cpuid); } /* * the boot CPU has finished the init stage and is spinning * on callin_map until we finish. We are free to set up this * CPU, first the APIC. (this is probably redundant on most * boards) */ Dprintk("CALLIN, before setup_local_APIC().\n"); setup_local_APIC(); /* * Get our bogomips. * * Need to enable IRQs because it can take longer and then * the NMI watchdog might kill us. */ local_irq_enable(); calibrate_delay(); local_irq_disable(); Dprintk("Stack at about %p\n",&cpuid); /* * Save our processor parameters */ smp_store_cpu_info(cpuid); /* * Allow the master to continue. */ cpu_set(cpuid, cpu_callin_map);}/* maps the cpu to the sched domain representing multi-core */cpumask_t cpu_coregroup_map(int cpu){ struct cpuinfo_x86 *c = &cpu_data(cpu); /* * For perf, we return last level cache shared map. * And for power savings, we return cpu_core_map */ if (sched_mc_power_savings || sched_smt_power_savings) return per_cpu(cpu_core_map, cpu); else return c->llc_shared_map;}/* representing cpus for which sibling maps can be computed */static cpumask_t cpu_sibling_setup_map;static inline void set_cpu_sibling_map(int cpu){ int i; struct cpuinfo_x86 *c = &cpu_data(cpu); cpu_set(cpu, cpu_sibling_setup_map); if (smp_num_siblings > 1) { for_each_cpu_mask(i, cpu_sibling_setup_map) { if (c->phys_proc_id == cpu_data(i).phys_proc_id && c->cpu_core_id == cpu_data(i).cpu_core_id) { cpu_set(i, per_cpu(cpu_sibling_map, cpu)); cpu_set(cpu, per_cpu(cpu_sibling_map, i)); cpu_set(i, per_cpu(cpu_core_map, cpu)); cpu_set(cpu, per_cpu(cpu_core_map, i)); cpu_set(i, c->llc_shared_map); cpu_set(cpu, cpu_data(i).llc_shared_map); } } } else { cpu_set(cpu, per_cpu(cpu_sibling_map, cpu)); } cpu_set(cpu, c->llc_shared_map); if (current_cpu_data.x86_max_cores == 1) { per_cpu(cpu_core_map, cpu) = per_cpu(cpu_sibling_map, cpu); c->booted_cores = 1; return; } for_each_cpu_mask(i, cpu_sibling_setup_map) { if (per_cpu(cpu_llc_id, cpu) != BAD_APICID && per_cpu(cpu_llc_id, cpu) == per_cpu(cpu_llc_id, i)) { cpu_set(i, c->llc_shared_map); cpu_set(cpu, cpu_data(i).llc_shared_map); } if (c->phys_proc_id == cpu_data(i).phys_proc_id) { cpu_set(i, per_cpu(cpu_core_map, cpu)); cpu_set(cpu, per_cpu(cpu_core_map, i)); /* * Does this new cpu bringup a new core? */ if (cpus_weight(per_cpu(cpu_sibling_map, cpu)) == 1) { /* * for each core in package, increment * the booted_cores for this new cpu */ if (first_cpu(per_cpu(cpu_sibling_map, i)) == i) c->booted_cores++; /* * increment the core count for all * the other cpus in this package */ if (i != cpu) cpu_data(i).booted_cores++; } else if (i != cpu && !c->booted_cores) c->booted_cores = cpu_data(i).booted_cores; } }}/* * Setup code on secondary processor (after comming out of the trampoline) */void __cpuinit start_secondary(void){ /* * Dont put anything before smp_callin(), SMP * booting is too fragile that we want to limit the * things done here to the most necessary things. */ cpu_init(); preempt_disable(); smp_callin(); /* otherwise gcc will move up the smp_processor_id before the cpu_init */ barrier(); /* * Check TSC sync first: */ check_tsc_sync_target(); if (nmi_watchdog == NMI_IO_APIC) { disable_8259A_irq(0); enable_NMI_through_LVT0(NULL); enable_8259A_irq(0); } /* * The sibling maps must be set before turing the online map on for * this cpu */ set_cpu_sibling_map(smp_processor_id()); /* * We need to hold call_lock, so there is no inconsistency * between the time smp_call_function() determines number of * IPI recipients, and the time when the determination is made * for which cpus receive the IPI in genapic_flat.c. Holding this * lock helps us to not include this cpu in a currently in progress * smp_call_function(). */ lock_ipi_call_lock(); spin_lock(&vector_lock); /* Setup the per cpu irq handling data structures */ __setup_vector_irq(smp_processor_id()); /* * Allow the master to continue. */ cpu_set(smp_processor_id(), cpu_online_map); per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; spin_unlock(&vector_lock); unlock_ipi_call_lock(); setup_secondary_APIC_clock(); cpu_idle();}extern volatile unsigned long init_rsp;extern void (*initial_code)(void);#ifdef APIC_DEBUGstatic void inquire_remote_apic(int apicid){ unsigned i, regs[] = { APIC_ID >> 4, APIC_LVR >> 4, APIC_SPIV >> 4 }; char *names[] = { "ID", "VERSION", "SPIV" }; int timeout; unsigned int status; printk(KERN_INFO "Inquiring remote APIC #%d...\n", apicid); for (i = 0; i < ARRAY_SIZE(regs); i++) { printk("... APIC #%d %s: ", apicid, names[i]); /* * Wait for idle. */ status = safe_apic_wait_icr_idle(); if (status) printk("a previous APIC delivery may have failed\n"); apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(apicid)); apic_write(APIC_ICR, APIC_DM_REMRD | regs[i]); timeout = 0; do { udelay(100); status = apic_read(APIC_ICR) & APIC_ICR_RR_MASK; } while (status == APIC_ICR_RR_INPROG && timeout++ < 1000); switch (status) { case APIC_ICR_RR_VALID: status = apic_read(APIC_RRR); printk("%08x\n", status); break; default: printk("failed\n"); } }}#endif/* * Kick the secondary to wake up. */static int __cpuinit wakeup_secondary_via_INIT(int phys_apicid, unsigned int start_rip){ unsigned long send_status, accept_status = 0; int maxlvt, num_starts, j; Dprintk("Asserting INIT.\n"); /* * Turn INIT on target chip */ apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid)); /* * Send IPI */ apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT); Dprintk("Waiting for send to finish...\n"); send_status = safe_apic_wait_icr_idle(); mdelay(10); Dprintk("Deasserting INIT.\n"); /* Target chip */ apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid)); /* Send IPI */ apic_write(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT); Dprintk("Waiting for send to finish...\n"); send_status = safe_apic_wait_icr_idle(); mb(); atomic_set(&init_deasserted, 1); num_starts = 2; /* * Run STARTUP IPI loop. */ Dprintk("#startup loops: %d.\n", num_starts); maxlvt = get_maxlvt(); for (j = 1; j <= num_starts; j++) { Dprintk("Sending STARTUP #%d.\n",j); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); Dprintk("After apic_write.\n"); /* * STARTUP IPI */ /* Target chip */ apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(phys_apicid)); /* Boot on the stack */ /* Kick the second */ apic_write(APIC_ICR, APIC_DM_STARTUP | (start_rip >> 12)); /* * Give the other CPU some time to accept the IPI. */ udelay(300); Dprintk("Startup point 1.\n"); Dprintk("Waiting for send to finish...\n"); send_status = safe_apic_wait_icr_idle(); /* * Give the other CPU some time to accept the IPI. */ udelay(200); /* * Due to the Pentium erratum 3AP. */ if (maxlvt > 3) { apic_write(APIC_ESR, 0); } accept_status = (apic_read(APIC_ESR) & 0xEF); if (send_status || accept_status) break; } Dprintk("After Startup.\n"); if (send_status) printk(KERN_ERR "APIC never delivered???\n"); if (accept_status) printk(KERN_ERR "APIC delivery error (%lx).\n", accept_status); return (send_status | accept_status);}struct create_idle { struct work_struct work; struct task_struct *idle; struct completion done; int cpu;};static void __cpuinit do_fork_idle(struct work_struct *work){ struct create_idle *c_idle = container_of(work, struct create_idle, work); c_idle->idle = fork_idle(c_idle->cpu); complete(&c_idle->done);}/* * Boot one CPU. */static int __cpuinit do_boot_cpu(int cpu, int apicid){ unsigned long boot_error; int timeout; unsigned long start_rip; struct create_idle c_idle = { .work = __WORK_INITIALIZER(c_idle.work, do_fork_idle), .cpu = cpu, .done = COMPLETION_INITIALIZER_ONSTACK(c_idle.done), };
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -