diff options
Diffstat (limited to 'arch/x86/kernel/apic/apic.c')
| -rw-r--r-- | arch/x86/kernel/apic/apic.c | 90 | 
1 files changed, 74 insertions, 16 deletions
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index a7eb82d9b01..ad28db7e6bd 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -62,6 +62,7 @@ unsigned disabled_cpus;  /* Processor that is doing the boot up */  unsigned int boot_cpu_physical_apicid = -1U; +EXPORT_SYMBOL_GPL(boot_cpu_physical_apicid);  /*   * The highest APIC ID seen during enumeration. @@ -74,6 +75,13 @@ unsigned int max_physical_apicid;  physid_mask_t phys_cpu_present_map;  /* + * Processor to be disabled specified by kernel parameter + * disable_cpu_apicid=<int>, mostly used for the kdump 2nd kernel to + * avoid undefined behaviour caused by sending INIT from AP to BSP. + */ +static unsigned int disabled_cpu_apicid __read_mostly = BAD_APICID; + +/*   * Map cpu index to physical APIC ID   */  DEFINE_EARLY_PER_CPU_READ_MOSTLY(u16, x86_cpu_to_apicid, BAD_APICID); @@ -125,6 +133,10 @@ static inline void imcr_apic_to_pic(void)   * +1=force-enable   */  static int force_enable_local_apic __initdata; + +/* Control whether x2APIC mode is enabled or not */ +static bool nox2apic __initdata; +  /*   * APIC command line parameters   */ @@ -154,8 +166,7 @@ int x2apic_mode;  /* x2apic enabled before OS handover */  int x2apic_preenabled;  static int x2apic_disabled; -static int nox2apic; -static __init int setup_nox2apic(char *str) +static int __init setup_nox2apic(char *str)  {  	if (x2apic_enabled()) {  		int apicid = native_apic_msr_read(APIC_ID); @@ -170,7 +181,7 @@ static __init int setup_nox2apic(char *str)  	} else  		setup_clear_cpu_cap(X86_FEATURE_X2APIC); -	nox2apic = 1; +	nox2apic = true;  	return 0;  } @@ -275,8 +286,12 @@ u32 native_safe_apic_wait_icr_idle(void)  void native_apic_icr_write(u32 low, u32 id)  { +	unsigned long flags; + +	local_irq_save(flags);  	apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(id));  	apic_write(APIC_ICR, low); +	local_irq_restore(flags);  }  u64 native_apic_icr_read(void) @@ -1967,7 +1982,7 @@ __visible void smp_trace_spurious_interrupt(struct pt_regs *regs)   */  static inline void __smp_error_interrupt(struct pt_regs *regs)  { -	u32 v0, v1; +	u32 v;  	u32 i = 0;  	static const char * const error_interrupt_reason[] = {  		"Send CS error",		/* APIC Error Bit 0 */ @@ -1981,21 +1996,21 @@ static inline void __smp_error_interrupt(struct pt_regs *regs)  	};  	/* First tickle the hardware, only then report what went on. -- REW */ -	v0 = apic_read(APIC_ESR); -	apic_write(APIC_ESR, 0); -	v1 = apic_read(APIC_ESR); +	if (lapic_get_maxlvt() > 3)	/* Due to the Pentium erratum 3AP. */ +		apic_write(APIC_ESR, 0); +	v = apic_read(APIC_ESR);  	ack_APIC_irq();  	atomic_inc(&irq_err_count); -	apic_printk(APIC_DEBUG, KERN_DEBUG "APIC error on CPU%d: %02x(%02x)", -		    smp_processor_id(), v0 , v1); +	apic_printk(APIC_DEBUG, KERN_DEBUG "APIC error on CPU%d: %02x", +		    smp_processor_id(), v); -	v1 = v1 & 0xff; -	while (v1) { -		if (v1 & 0x1) +	v &= 0xff; +	while (v) { +		if (v & 0x1)  			apic_printk(APIC_DEBUG, KERN_CONT " : %s", error_interrupt_reason[i]);  		i++; -		v1 >>= 1; +		v >>= 1;  	}  	apic_printk(APIC_DEBUG, KERN_CONT "\n"); @@ -2107,13 +2122,45 @@ void disconnect_bsp_APIC(int virt_wire_setup)  	apic_write(APIC_LVT1, value);  } -void generic_processor_info(int apicid, int version) +int generic_processor_info(int apicid, int version)  {  	int cpu, max = nr_cpu_ids;  	bool boot_cpu_detected = physid_isset(boot_cpu_physical_apicid,  				phys_cpu_present_map);  	/* +	 * boot_cpu_physical_apicid is designed to have the apicid +	 * returned by read_apic_id(), i.e, the apicid of the +	 * currently booting-up processor. However, on some platforms, +	 * it is temporarily modified by the apicid reported as BSP +	 * through MP table. Concretely: +	 * +	 * - arch/x86/kernel/mpparse.c: MP_processor_info() +	 * - arch/x86/mm/amdtopology.c: amd_numa_init() +	 * +	 * This function is executed with the modified +	 * boot_cpu_physical_apicid. So, disabled_cpu_apicid kernel +	 * parameter doesn't work to disable APs on kdump 2nd kernel. +	 * +	 * Since fixing handling of boot_cpu_physical_apicid requires +	 * another discussion and tests on each platform, we leave it +	 * for now and here we use read_apic_id() directly in this +	 * function, generic_processor_info(). +	 */ +	if (disabled_cpu_apicid != BAD_APICID && +	    disabled_cpu_apicid != read_apic_id() && +	    disabled_cpu_apicid == apicid) { +		int thiscpu = num_processors + disabled_cpus; + +		pr_warning("APIC: Disabling requested cpu." +			   " Processor %d/0x%x ignored.\n", +			   thiscpu, apicid); + +		disabled_cpus++; +		return -ENODEV; +	} + +	/*  	 * If boot cpu has not been detected yet, then only allow upto  	 * nr_cpu_ids - 1 processors and keep one slot free for boot cpu  	 */ @@ -2127,7 +2174,7 @@ void generic_processor_info(int apicid, int version)  			"  Processor %d/0x%x ignored.\n", max, thiscpu, apicid);  		disabled_cpus++; -		return; +		return -ENODEV;  	}  	if (num_processors >= nr_cpu_ids) { @@ -2138,7 +2185,7 @@ void generic_processor_info(int apicid, int version)  			"  Processor %d/0x%x ignored.\n", max, thiscpu, apicid);  		disabled_cpus++; -		return; +		return -EINVAL;  	}  	num_processors++; @@ -2183,6 +2230,8 @@ void generic_processor_info(int apicid, int version)  #endif  	set_cpu_possible(cpu, true);  	set_cpu_present(cpu, true); + +	return cpu;  }  int hard_smp_processor_id(void) @@ -2589,3 +2638,12 @@ static int __init lapic_insert_resource(void)   * that is using request_resource   */  late_initcall(lapic_insert_resource); + +static int __init apic_set_disabled_cpu_apicid(char *arg) +{ +	if (!arg || !get_option(&arg, &disabled_cpu_apicid)) +		return -EINVAL; + +	return 0; +} +early_param("disable_cpu_apicid", apic_set_disabled_cpu_apicid);  | 
