2017-04-19 09:05:38

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 00/12] Unify interrupt mode and setup it as soon as possible

According to Ingo's and Eric's advice[1,2], Try my best to optimize the
init of interrupt delivery mode for x86.

MP specification defines three different interrupt modes as follows:

1. PIC Mode
2. Virtual Wire Mode
3. Symmetric I/O Mode

Currently, in kernel,

1. Setup Virtual Wire Mode during IRQ initialization( step 1
in the following figure).

2. Enable and Setup Symmetric I/O Mode either during the
SMP-capable system prepares CPUs(step 2) or during the UP system
initializes itself(step 3).

start_kernel
+---------------+
|
+--> .......
|
| setup_arch
+--> +-------+
|
| init_IRQ
+-> +--+-----+
| | init_ISA_irqs
| +------> +-+--------+
| | +----------------+
+---> +------> | 1.init_bsp_APIC|
| ....... +----------------+
+--->
| rest_init
+--->---+-----+
| | kernel_init
| +> ----+-----+
| | kernel_init_freeable
| +-> ----+-------------+
| | smp_prepare_cpus
| +---> +----+---------+
| | | +-------------------+
| | +-> |2. apic_bsp_setup |
| | +-------------------+
| |
v | smp_init
+---> +---+----+
| +-------------------+
+--> |3. apic_bsp_setup |
+-------------------+

The purpose of this patchset is unifying these setup steps and executing
as soon as possible as follows:

start_kernel
---------------+
|
|
|
| x86_late_time_init
+---->---+------------+
| |
| | +------------------------+
| +----> | 4. init_interrupt_mode |
| +------------------------+
v

By the way, Also fix a bug about kexec[3].

[1]. https://lkml.org/lkml/2016/8/2/929
[2]. https://lkml.org/lkml/2016/8/1/506
[3]. https://lkml.org/lkml/2016/7/25/1118

Changes since V1:

- Move the initialization from init_IRQ() to x86_late_time_init()
- Use a threshold to refactor the check logic in timer_irq_works()
- Rename the framework to a selector
- Split two patches
- Consistently start sentences with upper case letters
- Fix some typos
- Rewrite the changelog

Dou Liyang (12):
x86/apic: Replace init_bsp_APIC() with apic_virtual_wire_mode_setup()
x86/apic: Construct a selector for the interrupt delivery mode
x86/apic: Prepare for unifying the interrupt delivery modes setup
x86/time: Initialize interrupt mode behind timer init
x86/ioapic: Refactor the delay logic in timer_irq_works()
x86/apic: Split local APIC timer setup from the APIC setup
x86/apic: Move the logical APIC ID setup from apic_bsp_setup()
x86/apic: Make the interrupt mode setup earlier for SMP-capable system
x86/apic: Setup interrupt mode earlier in case of no SMP motherboard
x86/apic: Make the interrupt mode setup earlier for UP system
x86/apic: Mark the apic_interrupt_mode extern for refining code
x86/apic: Remove the apic_virtual_wire_mode_setup()

arch/x86/include/asm/apic.h | 14 +++-
arch/x86/kernel/apic/apic.c | 183 +++++++++++++++++++++--------------------
arch/x86/kernel/apic/io_apic.c | 47 +++++++++--
arch/x86/kernel/irqinit.c | 3 -
arch/x86/kernel/smpboot.c | 64 +++-----------
arch/x86/kernel/time.c | 8 ++
6 files changed, 168 insertions(+), 151 deletions(-)

--
2.5.5




2017-04-19 09:05:43

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 01/12] x86/apic: Replace init_bsp_APIC() with apic_virtual_wire_mode_setup()

The init_bsp_APIC() sets up virtual wire mode through the local APIC.

The function name implies that the BSP's APIC will be initialized here,
which is unsuitable, while its initialization actually locates almost
at the end of start_kernel(). And the CONFIG X86_64 is also imply the
X86_LOCAL_APIC is y.

Rename it with a straightforward name, remove the redundant macros to
increase readability by adding an empty stub for the X86_LOCAL_APIC = n.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/include/asm/apic.h | 3 ++-
arch/x86/kernel/apic/apic.c | 4 ++--
arch/x86/kernel/irqinit.c | 5 ++---
3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 730ef65..86597f3 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -127,7 +127,7 @@ extern void disconnect_bsp_APIC(int virt_wire_setup);
extern void disable_local_APIC(void);
extern void lapic_shutdown(void);
extern void sync_Arb_IDs(void);
-extern void init_bsp_APIC(void);
+extern void apic_virtual_wire_mode_setup(void);
extern void setup_local_APIC(void);
extern void init_apic_mappings(void);
void register_lapic_address(unsigned long address);
@@ -170,6 +170,7 @@ static inline void disable_local_APIC(void) { }
# define setup_boot_APIC_clock x86_init_noop
# define setup_secondary_APIC_clock x86_init_noop
static inline void lapic_update_tsc_freq(void) { }
+static inline void apic_virtual_wire_mode_setup(void) {}
#endif /* !CONFIG_X86_LOCAL_APIC */

#ifdef CONFIG_X86_X2APIC
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 8ccb7ef..221056a 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1154,9 +1154,9 @@ void __init sync_Arb_IDs(void)
}

/*
- * An initial setup of the virtual wire mode.
+ * Setup the through-local-APIC virtual wire mode.
*/
-void __init init_bsp_APIC(void)
+void __init apic_virtual_wire_mode_setup(void)
{
unsigned int value;

diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index 1423ab1..25ddd7c 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -72,9 +72,8 @@ void __init init_ISA_irqs(void)
struct irq_chip *chip = legacy_pic->chip;
int i;

-#if defined(CONFIG_X86_64) || defined(CONFIG_X86_LOCAL_APIC)
- init_bsp_APIC();
-#endif
+ apic_virtual_wire_mode_setup();
+
legacy_pic->init(0);

for (i = 0; i < nr_legacy_irqs(); i++)
--
2.5.5



2017-04-19 09:05:49

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 02/12] x86/apic: Construct a selector for the interrupt delivery mode

Now, there are many switches in kernel which are used to determine the
final interrupt delivery mode, as shown below:

1) kconfig:
CONFIG_X86_64; CONFIG_X86_LOCAL_APIC; CONFIG_x86_IO_APIC
2) kernel option: disable_apic; skip_ioapic_setup
3) CPU Capability: boot_cpu_has(X86_FEATURE_APIC)
4) MP table: smp_found_config
5) ACPI: acpi_lapic; acpi_ioapic; nr_ioapic

These switches are disordered and scattered. Hard to wrap them.

Unify them into a single function, select an interrupt delivery
mode for BSP through these switches.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/apic/apic.c | 57 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 57 insertions(+)

diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 221056a..c754e48 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1153,6 +1153,63 @@ void __init sync_Arb_IDs(void)
APIC_INT_LEVELTRIG | APIC_DM_INIT);
}

+enum apic_interrupt_mode {
+ APIC_PIC = 0,
+ APIC_VIRTUAL_WIRE,
+ APIC_SYMMETRIC_IO,
+ APIC_MODE_COUNT
+};
+
+static int __init apic_bsp_mode_check(void)
+{
+
+ /* Check kernel option */
+ if (disable_apic) {
+ pr_info("APIC disabled via kernel command line\n");
+ return APIC_PIC;
+ }
+ /* Check BIOS */
+#ifdef CONFIG_X86_64
+ /* On 64-bit, The APIC is integrated, So, must have APIC feature */
+ if (!boot_cpu_has(X86_FEATURE_APIC)) {
+ disable_apic = 1;
+ pr_info("APIC disabled by BIOS\n");
+ return APIC_PIC;
+ }
+#else
+ if (!boot_cpu_has(X86_FEATURE_APIC) &&
+ APIC_INTEGRATED(boot_cpu_apic_version)) {
+ disable_apic = 1;
+ pr_err(FW_BUG "Local APIC %d not detected, force emulation\n",
+ boot_cpu_physical_apicid);
+ return APIC_PIC;
+ }
+#endif
+ /*
+ * Check MP table, if neither an integrated nor a separate chip
+ * doesn't exist.
+ */
+ if (!boot_cpu_has(X86_FEATURE_APIC) && !smp_found_config) {
+ pr_info("BIOS don't support APIC, and no SMP configuration\n");
+ return APIC_PIC;
+ }
+
+ /* Check MP table, if it is the virtual wire */
+ if (!smp_found_config) {
+ disable_ioapic_support();
+
+ /* Check local APIC, if there is no SMP motherboard */
+ if (!acpi_lapic)
+ pr_info("SMP motherboard not detected\n");
+
+ return APIC_VIRTUAL_WIRE;
+ }
+
+ /* Other checks of ACPI options will be done in each setup function */
+
+ return APIC_SYMMETRIC_IO;
+}
+
/*
* Setup the through-local-APIC virtual wire mode.
*/
--
2.5.5



2017-04-19 09:05:58

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 03/12] x86/apic: Prepare for unifying the interrupt delivery modes setup

There are three positions for initializing the interrupt delivery
modes:

1) In IRQ initial function, may setup the through-local-APIC
virtual wire mode.

2) In an SMP-capable system, will try to switch to symmetric I/O
model when preparing the cpus in native_smp_prepare_cpus().

3) In UP system with UP_LATE_INIT=y, will set up local APIC and
I/O APIC in smp_init().

Switching to symmetric I/O model is so late, which causes the
dump-capture kernel hangs with 'notsc' option inherited from 1st kernel
option.

Preparatory patch to initialize an interrupt mode directly and provide
a new function init_interrupt_mode() to unify that three positions and
do that setup as soon as possible.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/include/asm/apic.h | 2 ++
arch/x86/kernel/apic/apic.c | 16 ++++++++++++++++
2 files changed, 18 insertions(+)

diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 86597f3..64f30ee3 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -128,6 +128,7 @@ extern void disable_local_APIC(void);
extern void lapic_shutdown(void);
extern void sync_Arb_IDs(void);
extern void apic_virtual_wire_mode_setup(void);
+extern void init_interrupt_mode(void);
extern void setup_local_APIC(void);
extern void init_apic_mappings(void);
void register_lapic_address(unsigned long address);
@@ -171,6 +172,7 @@ static inline void disable_local_APIC(void) { }
# define setup_secondary_APIC_clock x86_init_noop
static inline void lapic_update_tsc_freq(void) { }
static inline void apic_virtual_wire_mode_setup(void) {}
+static inline void init_interrupt_mode(void) {}
#endif /* !CONFIG_X86_LOCAL_APIC */

#ifdef CONFIG_X86_X2APIC
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index c754e48..8ac4f58 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1259,6 +1259,22 @@ void __init apic_virtual_wire_mode_setup(void)
apic_write(APIC_LVT1, value);
}

+/* Init the interrupt delivery mode for the BSP */
+void __init init_interrupt_mode(void)
+{
+ switch (apic_bsp_mode_check()) {
+ case APIC_PIC:
+ pr_info("Keep in PIC mode(8259)\n");
+ return;
+ case APIC_VIRTUAL_WIRE:
+ pr_info("Switch to virtual wire mode\n");
+ return;
+ case APIC_SYMMETRIC_IO:
+ pr_info("Switch to symmectic I/O mode\n");
+ return;
+ }
+}
+
static void lapic_setup_esr(void)
{
unsigned int oldvalue, value, maxlvt;
--
2.5.5



2017-04-19 09:06:00

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 04/12] x86/time: Initialize interrupt mode behind timer init

In start_kernel(), firstly, it works on the default interrupy mode, then
switch to the final mode. default mode may not be compatible with the
actual hardware, which cause the delivery interrupt to fail.

Try to set up the final mode as soon as possible. according to the parts
which split from that initialization:

1) Set up the APIC/IOAPIC (including testing whether the timer
interrupt works)

2) Calibrate TSC

3) Set up the local APIC timer

-- From Thomas Gleixner

Initializing the mode should be earlier than calibrating TSC and needs
testing whether the timer interrupt works at the same time.

Add init_interrupt_mode() to the right location in x86_late_time_init().

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/time.c | 8 ++++++++
1 file changed, 8 insertions(+)

diff --git a/arch/x86/kernel/time.c b/arch/x86/kernel/time.c
index d39c091..04139c8 100644
--- a/arch/x86/kernel/time.c
+++ b/arch/x86/kernel/time.c
@@ -21,6 +21,7 @@
#include <asm/timer.h>
#include <asm/hpet.h>
#include <asm/time.h>
+#include <asm/apic.h>

#ifdef CONFIG_X86_64
__visible volatile unsigned long jiffies __cacheline_aligned = INITIAL_JIFFIES;
@@ -84,6 +85,13 @@ void __init hpet_time_init(void)
static __init void x86_late_time_init(void)
{
x86_init.timers.timer_init();
+
+ /*
+ * After PIT/HPET timers init, select and setup
+ * the final interrupt mode for delivering IRQs.
+ */
+ init_interrupt_mode();
+
tsc_init();
}

--
2.5.5



2017-04-19 09:06:10

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 07/12] x86/apic: Move the logical APIC ID setup from apic_bsp_setup()

The apic_bsp_setup() sets and returns logical APIC ID for setting up
cpu0_logical_apicid in SMP-capable system.

The id has nothing to do with the initialization of local APIC and
I/O APIC. apic_bsp_setup() should be specific for the initialization.

Move the id setup to native_smp_prepare_cpus().

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/include/asm/apic.h | 2 +-
arch/x86/kernel/apic/apic.c | 10 +---------
arch/x86/kernel/smpboot.c | 4 ++++
3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 64f30ee3..9f2bc9c 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -146,7 +146,7 @@ static inline int apic_force_enable(unsigned long addr)
extern int apic_force_enable(unsigned long addr);
#endif

-extern int apic_bsp_setup(bool upmode);
+extern void apic_bsp_setup(bool upmode);
extern void apic_ap_setup(void);

/*
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 903fd8f..f726c4f 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2331,25 +2331,17 @@ static void __init apic_bsp_up_setup(void)
* Returns:
* apic_id of BSP APIC
*/
-int __init apic_bsp_setup(bool upmode)
+void __init apic_bsp_setup(bool upmode)
{
- int id;
-
connect_bsp_APIC();
if (upmode)
apic_bsp_up_setup();
setup_local_APIC();

- if (x2apic_mode)
- id = apic_read(APIC_LDR);
- else
- id = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR));
-
enable_IO_APIC();
end_local_APIC_setup();
irq_remap_enable_fault_handling();
setup_IO_APIC();
- return id;
}

/*
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index db91a40..830b01b 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1347,6 +1347,10 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
default_setup_apic_routing();
cpu0_logical_apicid = apic_bsp_setup(false);

+ if (x2apic_mode)
+ cpu0_logical_apicid = apic_read(APIC_LDR);
+ else
+ cpu0_logical_apicid = GET_APIC_LOGICAL_ID(apic_read(APIC_LDR));

/* Setup local timer */
x86_init.timers.setup_percpu_clockev();
--
2.5.5



2017-04-19 09:06:16

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 06/12] x86/apic: Split local APIC timer setup from the APIC setup

The apic_bsp_setup() set up the local APIC, I/O APIC and APIC timer.
The local APIC and I/O APIC setup belongs to interrupt delivery mode
setup. Setting up the local APIC timer for booting CPU is another job.

Unifying the interrupt delivery mode need setting the apic_bsp_setup()
in advance of calibrating TSC. but the APIC timer setup cannot be run
there.

Split local APIC timer setup from the APIC setup, keep it in the
original position for SMP and UP kernel.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/apic/apic.c | 4 ++--
arch/x86/kernel/smpboot.c | 4 ++++
2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 8ac4f58..903fd8f 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2349,8 +2349,6 @@ int __init apic_bsp_setup(bool upmode)
end_local_APIC_setup();
irq_remap_enable_fault_handling();
setup_IO_APIC();
- /* Setup local timer */
- x86_init.timers.setup_percpu_clockev();
return id;
}

@@ -2390,6 +2388,8 @@ int __init APIC_init_uniprocessor(void)

default_setup_apic_routing();
apic_bsp_setup(true);
+ /* Setup local timer */
+ x86_init.timers.setup_percpu_clockev();
return 0;
}

diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index bd1f1ad..db91a40 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1347,6 +1347,10 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
default_setup_apic_routing();
cpu0_logical_apicid = apic_bsp_setup(false);

+
+ /* Setup local timer */
+ x86_init.timers.setup_percpu_clockev();
+
pr_info("CPU0: ");
print_cpu_info(&cpu_data(0));

--
2.5.5



2017-04-19 09:06:12

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 05/12] x86/ioapic: Refactor the delay logic in timer_irq_works()

Timer_irq_works() calls mdelay(10) to delay ten ticks and checks whether
the timer IRQ works or not. The mdelay() depends on the loops_per_jiffy
which is set up in calibrate_delay().

Timer_irq_works() has been in advance of calibrate_delay(). the mdelay()
doesn't work well in timer_irq_works().

Refactor the delay logic by replace the mdelay() with __delay() and
regard 2**MAX_BAND as the maximum threshold of CPU frequency.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/apic/io_apic.c | 47 ++++++++++++++++++++++++++++++++++++------
1 file changed, 41 insertions(+), 6 deletions(-)

diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 347bb9f..d3b0268 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1608,6 +1608,18 @@ static int __init notimercheck(char *s)
__setup("no_timer_check", notimercheck);

/*
+ * The loops_per_jiffy is not be set, udelay() or mdelay() cannot
+ * be called here.
+ * Set 16GHZ(loops_per_jiffy almost is 2**24) as the maximum threshold
+ * of CPU frequency. Exceed the threshold indicates the timer interrupt
+ * doesn't work.
+ *
+ * FIXME: Explore a new way to check the timer interrupt work.
+ */
+
+#define MAX_BAND 24
+
+/*
* There is a nasty bug in some older SMP boards, their mptable lies
* about the timer IRQ. We do the following to work around the situation:
*
@@ -1617,28 +1629,51 @@ __setup("no_timer_check", notimercheck);
*/
static int __init timer_irq_works(void)
{
- unsigned long t1 = jiffies;
+ unsigned long ticks;
unsigned long flags;
+ /* Set up approx 2 Bo*oMips to start */
+ int band = 11;
+ int loop_times;

if (no_timer_check)
return 1;

local_save_flags(flags);
local_irq_enable();
- /* Let ten ticks pass... */
- mdelay((10 * 1000) / HZ);
- local_irq_restore(flags);
+ /*
+ * Estimate the loops in (1,2] jiffies.
+ *
+ * After the loops (2**12+2**13+...+2**MAX_BAND) times,
+ * if time_before(jiffies, ticks + 2) is also n, That the
+ * timer interrupt did not work, return 0.
+ */
+ ticks = jiffies;
+ do {
+ if (band++ == MAX_BAND) {
+ local_irq_restore(flags);
+ return 0;
+ }
+
+ __delay(1 << band);
+ } while (time_before(jiffies, ticks + 2));

/*
* Expect a few ticks at least, to be sure some possible
* glue logic does not lock up after one or two first
* ticks in a non-ExtINT mode. Also the local APIC
- * might have cached one ExtINT interrupt. Finally, at
+ * might have cached one ExtINT interrupt. Finally, at
* least one tick may be lost due to delays.
+ *
+ * As 2**12+2**13+...+2**band < 2**(1 + band),
+ * it certainly delayed for more than 4 ticks.
*/
+ for (loop_times = 0; loop_times < 4; loop_times++)
+ __delay(1 << (1 + band));
+
+ local_irq_restore(flags);

/* jiffies wrap? */
- if (time_after(jiffies, t1 + 4))
+ if (time_after(jiffies, ticks + 4))
return 1;
return 0;
}
--
2.5.5



2017-04-19 09:06:25

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 08/12] x86/apic: Make the interrupt mode setup earlier for SMP-capable system

In the SMP-capable system, enable and setup the interrupt delivery
mode in native_smp_prepare_cpus() which almost be called at the end of
start_kernel().

But, calibrate delay needs the timer interrupt which may be disabled
in dump-capture kernel.

Due to the MP table or ACPI has been read earlier, setup the interrupt
mode as soon as possible to cleanup the disabled situation for
SMP-capable system.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/apic/apic.c | 21 +++++++++++++++++++++
arch/x86/kernel/smpboot.c | 10 ----------
2 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index f726c4f..170cd1a 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1157,6 +1157,7 @@ enum apic_interrupt_mode {
APIC_PIC = 0,
APIC_VIRTUAL_WIRE,
APIC_SYMMETRIC_IO,
+ APIC_SYMMETRIC_IO_NO_ROUTING,
APIC_MODE_COUNT
};

@@ -1207,6 +1208,20 @@ static int __init apic_bsp_mode_check(void)

/* Other checks of ACPI options will be done in each setup function */

+#ifdef CONFIG_SMP
+ if (read_apic_id() != boot_cpu_physical_apicid) {
+ panic("Boot APIC ID in local APIC unexpected (%d vs %d)",
+ read_apic_id(), boot_cpu_physical_apicid);
+ /* Or can we switch back to PIC here? */
+ }
+
+ /* If SMP should be disabled, then really disable it! */
+ if (!setup_max_cpus) {
+ pr_info("SMP mode deactivated\n");
+ return APIC_SYMMETRIC_IO_NO_ROUTING;
+ }
+#endif
+
return APIC_SYMMETRIC_IO;
}

@@ -1271,6 +1286,12 @@ void __init init_interrupt_mode(void)
return;
case APIC_SYMMETRIC_IO:
pr_info("Switch to symmectic I/O mode\n");
+ default_setup_apic_routing();
+ apic_bsp_setup(false);
+ return;
+ case APIC_SYMMETRIC_IO_NO_ROUTING:
+ pr_info("Switch to symmectic I/O mode with no APIC routing\n");
+ apic_bsp_setup(false);
return;
}
}
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 830b01b..345ad20 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1332,21 +1332,11 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
return;
case SMP_FORCE_UP:
disable_smp();
- apic_bsp_setup(false);
return;
case SMP_OK:
break;
}

- if (read_apic_id() != boot_cpu_physical_apicid) {
- panic("Boot APIC ID in local APIC unexpected (%d vs %d)",
- read_apic_id(), boot_cpu_physical_apicid);
- /* Or can we switch back to PIC here? */
- }
-
- default_setup_apic_routing();
- cpu0_logical_apicid = apic_bsp_setup(false);
-
if (x2apic_mode)
cpu0_logical_apicid = apic_read(APIC_LDR);
else
--
2.5.5



2017-04-19 09:06:34

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 11/12] x86/apic: Mark the apic_interrupt_mode extern for refining code

Native_smp_prepare_cpus() prepares for SMP bootup, does some sanity
checking and enables APIC mode.

The APIC mode setup has been unified to init_interrupt_mode(), Here
is redundant and need to be cleaned up.

Mark the apic_interrupt_mode extern to refine the switch and remove
the redundant sanity check.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/include/asm/apic.h | 9 +++++++++
arch/x86/kernel/apic/apic.c | 12 +++---------
arch/x86/kernel/smpboot.c | 48 +++++++--------------------------------------
3 files changed, 19 insertions(+), 50 deletions(-)

diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index 9f2bc9c..b0b2cf0 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -53,6 +53,15 @@ extern int local_apic_timer_c2_ok;
extern int disable_apic;
extern unsigned int lapic_timer_frequency;

+extern enum apic_interrupt_mode_id apic_interrupt_mode;
+enum apic_interrupt_mode_id {
+ APIC_PIC = 0,
+ APIC_VIRTUAL_WIRE,
+ APIC_SYMMETRIC_IO,
+ APIC_SYMMETRIC_IO_NO_CONFIG,
+ APIC_SYMMETRIC_IO_NO_ROUTING
+};
+
#ifdef CONFIG_SMP
extern void __inquire_remote_apic(int apicid);
#else /* CONFIG_SMP */
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 5d27a24..f259daa 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1153,14 +1153,7 @@ void __init sync_Arb_IDs(void)
APIC_INT_LEVELTRIG | APIC_DM_INIT);
}

-enum apic_interrupt_mode {
- APIC_PIC = 0,
- APIC_VIRTUAL_WIRE,
- APIC_SYMMETRIC_IO,
- APIC_SYMMETRIC_IO_NO_CONFIG,
- APIC_SYMMETRIC_IO_NO_ROUTING,
- APIC_MODE_COUNT
-};
+enum apic_interrupt_mode_id apic_interrupt_mode;

static int __init apic_bsp_mode_check(int *upmode)
{
@@ -1286,7 +1279,8 @@ void __init init_interrupt_mode(void)
{
int upmode = false;

- switch (apic_bsp_mode_check(&upmode)) {
+ apic_interrupt_mode = apic_bsp_mode_check(&upmode);
+ switch (apic_interrupt_mode) {
case APIC_PIC:
pr_info("Keep in PIC mode(8259)\n");
return;
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 19d36ca..e2de8e8 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1193,7 +1193,7 @@ enum {
/*
* Various sanity checks.
*/
-static int __init smp_sanity_check(unsigned max_cpus)
+static void __init smp_sanity_check(void)
{
preempt_disable();

@@ -1231,16 +1231,6 @@ static int __init smp_sanity_check(unsigned max_cpus)
}

/*
- * If we couldn't find an SMP configuration at boot time,
- * get out of here now!
- */
- if (!smp_found_config && !acpi_lapic) {
- preempt_enable();
- pr_notice("SMP motherboard not detected\n");
- return SMP_NO_CONFIG;
- }
-
- /*
* Should not be necessary because the MP table should list the boot
* CPU too, but we do it for the sake of robustness anyway.
*/
@@ -1250,29 +1240,6 @@ static int __init smp_sanity_check(unsigned max_cpus)
physid_set(hard_smp_processor_id(), phys_cpu_present_map);
}
preempt_enable();
-
- /*
- * If we couldn't find a local APIC, then get out of here now!
- */
- if (APIC_INTEGRATED(boot_cpu_apic_version) &&
- !boot_cpu_has(X86_FEATURE_APIC)) {
- if (!disable_apic) {
- pr_err("BIOS bug, local APIC #%d not detected!...\n",
- boot_cpu_physical_apicid);
- pr_err("... forcing use of dummy APIC emulation (tell your hw vendor)\n");
- }
- return SMP_NO_APIC;
- }
-
- /*
- * If SMP should be disabled, then really disable it!
- */
- if (!max_cpus) {
- pr_info("SMP mode deactivated\n");
- return SMP_FORCE_UP;
- }
-
- return SMP_OK;
}

static void __init smp_cpu_index_default(void)
@@ -1320,18 +1287,17 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
set_sched_topology(x86_topology);

set_cpu_sibling_map(0);
+ smp_sanity_check();

- switch (smp_sanity_check(max_cpus)) {
- case SMP_NO_CONFIG:
- disable_smp();
- return;
- case SMP_NO_APIC:
+ switch (apic_interrupt_mode) {
+ case APIC_PIC:
disable_smp();
return;
- case SMP_FORCE_UP:
+ case APIC_SYMMETRIC_IO_NO_CONFIG:
disable_smp();
+ x86_init.timers.setup_percpu_clockev();
return;
- case SMP_OK:
+ default:
break;
}

--
2.5.5



2017-04-19 09:06:51

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 12/12] x86/apic: Remove the apic_virtual_wire_mode_setup()

Currently, enable and setup the interrupt mode has been advanced
and it has already included the virtual wire mode setup.

The apic_virtual_wire_mode_setup() which works for the virtual wire
mode is redundant.

Remove the apic_virtual_wire_mode_setup() function and add the apic
routing setup for the virtual wire mode.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/include/asm/apic.h | 2 --
arch/x86/kernel/apic/apic.c | 50 +--------------------------------------------
arch/x86/kernel/irqinit.c | 2 --
3 files changed, 1 insertion(+), 53 deletions(-)

diff --git a/arch/x86/include/asm/apic.h b/arch/x86/include/asm/apic.h
index b0b2cf0..6d643de 100644
--- a/arch/x86/include/asm/apic.h
+++ b/arch/x86/include/asm/apic.h
@@ -136,7 +136,6 @@ extern void disconnect_bsp_APIC(int virt_wire_setup);
extern void disable_local_APIC(void);
extern void lapic_shutdown(void);
extern void sync_Arb_IDs(void);
-extern void apic_virtual_wire_mode_setup(void);
extern void init_interrupt_mode(void);
extern void setup_local_APIC(void);
extern void init_apic_mappings(void);
@@ -180,7 +179,6 @@ static inline void disable_local_APIC(void) { }
# define setup_boot_APIC_clock x86_init_noop
# define setup_secondary_APIC_clock x86_init_noop
static inline void lapic_update_tsc_freq(void) { }
-static inline void apic_virtual_wire_mode_setup(void) {}
static inline void init_interrupt_mode(void) {}
#endif /* !CONFIG_X86_LOCAL_APIC */

diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index f259daa..8454226 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1225,55 +1225,6 @@ static int __init apic_bsp_mode_check(int *upmode)

}

-/*
- * Setup the through-local-APIC virtual wire mode.
- */
-void __init apic_virtual_wire_mode_setup(void)
-{
- unsigned int value;
-
- /*
- * Don't do the setup now if we have a SMP BIOS as the
- * through-I/O-APIC virtual wire mode might be active.
- */
- if (smp_found_config || !boot_cpu_has(X86_FEATURE_APIC))
- return;
-
- /*
- * Do not trust the local APIC being empty at bootup.
- */
- clear_local_APIC();
-
- /*
- * Enable APIC.
- */
- value = apic_read(APIC_SPIV);
- value &= ~APIC_VECTOR_MASK;
- value |= APIC_SPIV_APIC_ENABLED;
-
-#ifdef CONFIG_X86_32
- /* This bit is reserved on P4/Xeon and should be cleared */
- if ((boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) &&
- (boot_cpu_data.x86 == 15))
- value &= ~APIC_SPIV_FOCUS_DISABLED;
- else
-#endif
- value |= APIC_SPIV_FOCUS_DISABLED;
- value |= SPURIOUS_APIC_VECTOR;
- apic_write(APIC_SPIV, value);
-
- /*
- * Set up the virtual wire mode.
- */
- apic_write(APIC_LVT0, APIC_DM_EXTINT);
- value = APIC_DM_NMI;
- if (!lapic_is_integrated()) /* 82489DX */
- value |= APIC_LVT_LEVEL_TRIGGER;
- if (apic_extnmi == APIC_EXTNMI_NONE)
- value |= APIC_LVT_MASKED;
- apic_write(APIC_LVT1, value);
-}
-
/* Init the interrupt delivery mode for the BSP */
void __init init_interrupt_mode(void)
{
@@ -1286,6 +1237,7 @@ void __init init_interrupt_mode(void)
return;
case APIC_VIRTUAL_WIRE:
pr_info("Switch to virtual wire mode\n");
+ default_setup_apic_routing();
break;
case APIC_SYMMETRIC_IO:
pr_info("Switch to symmectic I/O mode\n");
diff --git a/arch/x86/kernel/irqinit.c b/arch/x86/kernel/irqinit.c
index 25ddd7c..a8c0de6 100644
--- a/arch/x86/kernel/irqinit.c
+++ b/arch/x86/kernel/irqinit.c
@@ -72,8 +72,6 @@ void __init init_ISA_irqs(void)
struct irq_chip *chip = legacy_pic->chip;
int i;

- apic_virtual_wire_mode_setup();
-
legacy_pic->init(0);

for (i = 0; i < nr_legacy_irqs(); i++)
--
2.5.5



2017-04-19 09:07:31

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 09/12] x86/apic: Setup interrupt mode earlier in case of no SMP motherboard

Kernel falls back to non SMP mode and sets up interrupt delivery mode
in APIC_init_uniprocessor() in case of no SMP motherboard.

Setting up interrupt delivery mode as soon as possible should wraps
this case too.

Wrap this case, make it consistent with SMP-capable systems.
Incidentally,
-Extract apic_bsp_setup() and Refine init_interrupt_mode().

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/apic/apic.c | 26 ++++++++++++++++++--------
arch/x86/kernel/smpboot.c | 2 --
2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 170cd1a..a915f09 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1157,11 +1157,12 @@ enum apic_interrupt_mode {
APIC_PIC = 0,
APIC_VIRTUAL_WIRE,
APIC_SYMMETRIC_IO,
+ APIC_SYMMETRIC_IO_NO_CONFIG,
APIC_SYMMETRIC_IO_NO_ROUTING,
APIC_MODE_COUNT
};

-static int __init apic_bsp_mode_check(void)
+static int __init apic_bsp_mode_check(int *upmode)
{

/* Check kernel option */
@@ -1200,8 +1201,11 @@ static int __init apic_bsp_mode_check(void)
disable_ioapic_support();

/* Check local APIC, if there is no SMP motherboard */
- if (!acpi_lapic)
+ if (!acpi_lapic) {
+ *upmode = true;
pr_info("SMP motherboard not detected\n");
+ return APIC_SYMMETRIC_IO_NO_CONFIG;
+ }

return APIC_VIRTUAL_WIRE;
}
@@ -1277,23 +1281,29 @@ void __init apic_virtual_wire_mode_setup(void)
/* Init the interrupt delivery mode for the BSP */
void __init init_interrupt_mode(void)
{
- switch (apic_bsp_mode_check()) {
+ int upmode = false;
+
+ switch (apic_bsp_mode_check(&upmode)) {
case APIC_PIC:
pr_info("Keep in PIC mode(8259)\n");
return;
case APIC_VIRTUAL_WIRE:
pr_info("Switch to virtual wire mode\n");
- return;
+ break;
case APIC_SYMMETRIC_IO:
pr_info("Switch to symmectic I/O mode\n");
default_setup_apic_routing();
- apic_bsp_setup(false);
- return;
+ break;
+ case APIC_SYMMETRIC_IO_NO_CONFIG:
+ pr_info("Switch to symmectic I/O mode with no SMP config\n");
+ default_setup_apic_routing();
+ break;
case APIC_SYMMETRIC_IO_NO_ROUTING:
pr_info("Switch to symmectic I/O mode with no APIC routing\n");
- apic_bsp_setup(false);
- return;
+ break;
}
+
+ apic_bsp_setup(upmode);
}

static void lapic_setup_esr(void)
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c
index 345ad20..19d36ca 100644
--- a/arch/x86/kernel/smpboot.c
+++ b/arch/x86/kernel/smpboot.c
@@ -1324,8 +1324,6 @@ void __init native_smp_prepare_cpus(unsigned int max_cpus)
switch (smp_sanity_check(max_cpus)) {
case SMP_NO_CONFIG:
disable_smp();
- if (APIC_init_uniprocessor())
- pr_notice("Local APIC not detected. Using dummy APIC emulation.\n");
return;
case SMP_NO_APIC:
disable_smp();
--
2.5.5



2017-04-19 09:07:33

by Dou Liyang

[permalink] [raw]
Subject: [RFC PATCH v2 10/12] x86/apic: Make the interrupt mode setup earlier for UP system

In UniProcessor kernel with UP_LATE_INIT=y, enable and setup the
interrupt delivery mode in smp_init() which almost be called at the
end of start_kernel().

But, calibrate delay needs the timer interrupt which may be disabled
in dump-capture kernel.

Due to the MP table or ACPI has been read earlier, setup the interrupt
mode as soon as possible to cleanup the disabled situation for UP
kernel and remove the original setup code.

Signed-off-by: Dou Liyang <[email protected]>
---
arch/x86/kernel/apic/apic.c | 49 ++++++---------------------------------------
1 file changed, 6 insertions(+), 43 deletions(-)

diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index a915f09..5d27a24 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -1224,9 +1224,12 @@ static int __init apic_bsp_mode_check(int *upmode)
pr_info("SMP mode deactivated\n");
return APIC_SYMMETRIC_IO_NO_ROUTING;
}
+#else
+ /* UP_LATE_INIT is true */
+ *upmode = true;
#endif
-
return APIC_SYMMETRIC_IO;
+
}

/*
@@ -2375,51 +2378,11 @@ void __init apic_bsp_setup(bool upmode)
setup_IO_APIC();
}

-/*
- * This initializes the IO-APIC and APIC hardware if this is
- * a UP kernel.
- */
-int __init APIC_init_uniprocessor(void)
-{
- if (disable_apic) {
- pr_info("Apic disabled\n");
- return -1;
- }
-#ifdef CONFIG_X86_64
- if (!boot_cpu_has(X86_FEATURE_APIC)) {
- disable_apic = 1;
- pr_info("Apic disabled by BIOS\n");
- return -1;
- }
-#else
- if (!smp_found_config && !boot_cpu_has(X86_FEATURE_APIC))
- return -1;
-
- /*
- * Complain if the BIOS pretends there is one.
- */
- if (!boot_cpu_has(X86_FEATURE_APIC) &&
- APIC_INTEGRATED(boot_cpu_apic_version)) {
- pr_err("BIOS bug, local APIC 0x%x not detected!...\n",
- boot_cpu_physical_apicid);
- return -1;
- }
-#endif
-
- if (!smp_found_config)
- disable_ioapic_support();
-
- default_setup_apic_routing();
- apic_bsp_setup(true);
- /* Setup local timer */
- x86_init.timers.setup_percpu_clockev();
- return 0;
-}
-
#ifdef CONFIG_UP_LATE_INIT
void __init up_late_init(void)
{
- APIC_init_uniprocessor();
+ /* Setup local APIC timer */
+ x86_init.timers.setup_percpu_clockev();
}
#endif

--
2.5.5