📄 smpboot.c
字号:
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); disable_APIC_timer(); /* * Save our processor parameters */ smp_store_cpu_info(cpuid); /* * Allow the master to continue. */ cpu_set(cpuid, cpu_callin_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_set(cpu, cpu_sibling_setup_map); if (smp_num_siblings > 1) { for_each_cpu_mask(i, cpu_sibling_setup_map) { if (phys_proc_id[cpu] == phys_proc_id[i] && cpu_core_id[cpu] == cpu_core_id[i]) { cpu_set(i, cpu_sibling_map[cpu]); cpu_set(cpu, cpu_sibling_map[i]); cpu_set(i, cpu_core_map[cpu]); cpu_set(cpu, cpu_core_map[i]); } } } else { cpu_set(cpu, cpu_sibling_map[cpu]); } if (current_cpu_data.x86_max_cores == 1) { cpu_core_map[cpu] = cpu_sibling_map[cpu]; c[cpu].booted_cores = 1; return; } for_each_cpu_mask(i, cpu_sibling_setup_map) { if (phys_proc_id[cpu] == phys_proc_id[i]) { cpu_set(i, cpu_core_map[cpu]); cpu_set(cpu, cpu_core_map[i]); /* * Does this new cpu bringup a new core? */ if (cpus_weight(cpu_sibling_map[cpu]) == 1) { /* * for each core in package, increment * the booted_cores for this new cpu */ if (first_cpu(cpu_sibling_map[i]) == i) c[cpu].booted_cores++; /* * increment the core count for all * the other cpus in this package */ if (i != cpu) c[i].booted_cores++; } else if (i != cpu && !c[cpu].booted_cores) c[cpu].booted_cores = c[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(); Dprintk("cpu %d: setting up apic clock\n", smp_processor_id()); setup_secondary_APIC_clock(); Dprintk("cpu %d: enabling apic timer\n", smp_processor_id()); if (nmi_watchdog == NMI_IO_APIC) { disable_8259A_irq(0); enable_NMI_through_LVT0(NULL); enable_8259A_irq(0); } enable_APIC_timer(); /* * The sibling maps must be set before turing the online map on for * this cpu */ set_cpu_sibling_map(smp_processor_id()); /* * Wait for TSC sync to not schedule things before. * We still process interrupts, which could see an inconsistent * time in that window unfortunately. * Do this here because TSC sync has global unprotected state. */ tsc_sync_wait(); /* * We need to hold call_lock, so there is no inconsistency * between the time smp_call_function() determines number of * IPI receipients, 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(); /* * Allow the master to continue. */ cpu_set(smp_processor_id(), cpu_online_map); per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE; unlock_ipi_call_lock(); 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, status; printk(KERN_INFO "Inquiring remote APIC #%d...\n", apicid); for (i = 0; i < sizeof(regs) / sizeof(*regs); i++) { printk("... APIC #%d %s: ", apicid, names[i]); /* * Wait for idle. */ apic_wait_icr_idle(); 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 = 0, accept_status = 0; int maxlvt, timeout, 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"); timeout = 0; do { Dprintk("+"); udelay(100); send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); 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"); timeout = 0; do { Dprintk("+"); udelay(100); send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); 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_read_around(APIC_SPIV); 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"); timeout = 0; do { Dprintk("+"); udelay(100); send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY; } while (send_status && (timeout++ < 1000)); /* * Give the other CPU some time to accept the IPI. */ udelay(200); /* * Due to the Pentium erratum 3AP. */ if (maxlvt > 3) { apic_read_around(APIC_SPIV); 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 task_struct *idle; struct completion done; int cpu;};void do_fork_idle(void *_c_idle){ struct create_idle *c_idle = _c_idle; 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 = { .cpu = cpu, .done = COMPLETION_INITIALIZER(c_idle.done), }; DECLARE_WORK(work, do_fork_idle, &c_idle); c_idle.idle = get_idle_for_cpu(cpu); if (c_idle.idle) { c_idle.idle->thread.rsp = (unsigned long) (((struct pt_regs *) (THREAD_SIZE + (unsigned long) c_idle.idle->thread_info)) - 1); init_idle(c_idle.idle, cpu); goto do_rest; } /* * During cold boot process, keventd thread is not spun up yet. * When we do cpu hot-add, we create idle threads on the fly, we should * not acquire any attributes from the calling context. Hence the clean * way to create kernel_threads() is to do that from keventd(). * We do the current_is_keventd() due to the fact that ACPI notifier * was also queuing to keventd() and when the caller is already running * in context of keventd(), we would end up with locking up the keventd * thread. */ if (!keventd_up() || current_is_keventd()) work.func(work.data); else { schedule_work(&work); wait_for_completion(&c_idle.done); } if (IS_ERR(c_idle.idle)) { printk("failed fork for CPU %d\n", cpu); return PTR_ERR(c_idle.idle); } set_idle_for_cpu(cpu, c_idle.idle);do_rest: cpu_pda[cpu].pcurrent = c_idle.idle; start_rip = setup_trampoline(); init_rsp = c_idle.idle->thread.rsp; per_cpu(init_tss,cpu).rsp0 = init_rsp; initial_code = start_secondary; clear_ti_thread_flag(c_idle.idle->thread_info, TIF_FORK); printk(KERN_INFO "Booting processor %d/%d APIC 0x%x\n", cpu, cpus_weight(cpu_present_map), apicid); /* * This grunge runs the startup process for * the targeted processor. */ atomic_set(&init_deasserted, 0); Dprintk("Setting warm reset code and vector.\n"); CMOS_WRITE(0xa, 0xf); local_flush_tlb(); Dprintk("1.\n"); *((volatile unsigned short *) phys_to_virt(0x469)) = start_rip >> 4; Dprintk("2.\n"); *((volatile unsigned short *) phys_to_virt(0x467)) = start_rip & 0xf; Dprintk("3.\n"); /* * Be paranoid about clearing APIC errors. */ if (APIC_INTEGRATED(apic_version[apicid])) { apic_read_around(APIC_SPIV); apic_write(APIC_ESR, 0); apic_read(APIC_ESR); } /* * Status is now clean */ boot_error = 0; /* * Starting actual IPI sequence... */ boot_error = wakeup_secondary_via_INIT(apicid, start_rip);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -