Skip to contentSkip to conversation

Timer list init is done AFTER use

PATCH In review george anzinger 3 participants 9 messages Dec 20, 2002 – Dec 22, 2002

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Timer list init is done AFTER use

On SMP systems the timer list init is done by way of a cpu_notifier call. This has two problems:

1.) Timers are started WAY before the cpu_notifier call chain is executed. In particular the console blanking timer is deleted and inserted every time printk() is called. That this does not fail is only because the kernel has yet to protect location zero.

2.) This notifier is called when a cpu comes up. I suspect that initializing the timer list when a hot swap of a cpu is done is NOT the right thing to do. In any case, if this is a desired action, the list still needs to be initialized prior to its use.

The attached patch initializes all the timer lists at init_timers time and does not put code in the notify list. --
George Anzinger [email protected]
High-res-timers:
http://sourceforge.net/projects/high-res-timers/
Preemption patch:
http://www.kernel.org/pub/linux/kernel/people/rml

kernel/timer.c renamed from kernel/timer.c~ +5 -22
@@ -1150,7 +1150,7 @@
 	return 0;
 }
 
-static void __devinit init_timers_cpu(int cpu)
+static void __init init_timers_cpu(int cpu)
 {
 	int j;
 	tvec_base_t *base;
@@ -1167,29 +1167,12 @@
 		INIT_LIST_HEAD(base->tv1.vec + j);
 }
 	
-static int __devinit timer_cpu_notify(struct notifier_block *self, 
-				unsigned long action, void *hcpu)
-{
-	long cpu = (long)hcpu;
-	switch(action) {
-	case CPU_UP_PREPARE:
-		init_timers_cpu(cpu);
-		break;
-	default:
-		break;
-	}
-	return NOTIFY_OK;
-}
-
-static struct notifier_block __devinitdata timers_nb = {
-	.notifier_call	= timer_cpu_notify,
-};
-
 
 void __init init_timers(void)
 {
-	timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,
-				(void *)(long)smp_processor_id());
-	register_cpu_notifier(&timers_nb);
+	int cpu;
+	for (cpu = 0; cpu < NR_CPUS; cpu++){
+		init_timers_cpu(cpu);
+	}
 	open_softirq(TIMER_SOFTIRQ, run_timer_softirq, NULL);
 }
#1 lore raw
Quoted 9 lines · george anzinger

On SMP systems the timer list init is done by way of a cpu_notifier call. This has two problems:

1.) Timers are started WAY before the cpu_notifier call chain is executed. In particular the console blanking timer is deleted and inserted every time printk() is called. That this does not fail is only because the kernel has yet to protect location zero.

But init_timers() directly calls timer_cpu_notify(), which directly calls init_timers_cpu().

So your patch appears to be a no-op for the boot CPU.

Quoted 5 lines

2.) This notifier is called when a cpu comes up. I suspect that initializing the timer list when a hot swap of a cpu is done is NOT the right thing to do. In any case, if this is a desired action, the list still needs to be initialized prior to its use.

It should be OK as-is? The CPU_UP_PREPARE callout is performed before the secondary starts doing things. Its timers are initialised.

Quoted 2 lines

The attached patch initializes all the timer lists at init_timers time and does not put code in the notify list.

But the patch assumes that the per-cpu data exists for all CPUs - even the !cpu_possible() ones.

This is true at present. But the intent here is that the per-cpu storage be allocated as the CPUs come up, and in their node-local memory. That saves memory and presumably having the cpu-local timers in the cpu-local memory is a good thing.

I have working code which did all that, but it sort-of got lost because there was a lot going on at the time.

Have you actually observed any problem?

#2 lore raw
Quoted 12 lines · Andrew Morton
Quoted 9 lines · george anzinger

On SMP systems the timer list init is done by way of a cpu_notifier call. This has two problems:

1.) Timers are started WAY before the cpu_notifier call chain is executed. In particular the console blanking timer is deleted and inserted every time printk() is called. That this does not fail is only because the kernel has yet to protect location zero.

Quoted 4 lines

But init_timers() directly calls timer_cpu_notify(), which directly calls init_timers_cpu().

So your patch appears to be a no-op for the boot CPU.

That is correct. The problem is when cpu_init is called for the secondary cpus. It almost immediately calls printk. You can put a break point here, wait for the second entry and then in mod_timer. You will see that mod_timer grabs the "base" for the second cpu which is not initialized.

Quoted 9 lines
Quoted 6 lines

2.) This notifier is called when a cpu comes up. I suspect that initializing the timer list when a hot swap of a cpu is done is NOT the right thing to do. In any case, if this is a desired action, the list still needs to be initialized prior to its use.

It should be OK as-is? The CPU_UP_PREPARE callout is performed before the secondary starts doing things. Its timers are initialised.

Yes, but not really. As noted, the timers are called as a result of printk. (I use a standard pc for this so the console is on the standard pc tube. I don't know about other consoles...) My comments here are a wonderment if this is the right thing to do when doing a hot swap of the cpu. I sort of doubt that this is correct.

Quoted 6 lines
Quoted 3 lines

The attached patch initializes all the timer lists at init_timers time and does not put code in the notify list.

But the patch assumes that the per-cpu data exists for all CPUs - even the !cpu_possible() ones.

True.

Quoted 6 lines

This is true at present. But the intent here is that the per-cpu storage be allocated as the CPUs come up, and in their node-local memory. That saves memory and presumably having the cpu-local timers in the cpu-local memory is a good thing.

I agree. It is possible that we may want to hold off printks until the cpu is up. There is a stub to do this, but it is a nop on the i386.

Quoted 4 lines

I have working code which did all that, but it sort-of got lost because there was a lot going on at the time.

Have you actually observed any problem?

Yes, with my High-res-timers patch (all 4 of them) with CONFIG_HIGH_RES turned off fails to boot. I traced the timer insert as I suggested above and it somehow gets away with writing into *NULL. I am not sure how this happens, but when I ask KGDB to read it back, it also is able to read it. The inserted timer (this is on an unpatched system, i.e. no high-res or anything but the kgdb patch) looks like this:
(gdb) p *timer
$13 = {entry = {next = 0xc11455b8, prev = 0x0}, expires =

605082, lock = {
    lock = 1}, magic = 1267182958, function = 0xc01d8510

<blank_screen>,
data = 0, base = 0x0}

The base of 0 is ok as that is updated after the insert by mod_timer. The prev =0, however is not cool.

Signature · 5 lines
George Anzinger   [email protected]
High-res-timers: 
http://sourceforge.net/projects/high-res-timers/
Preemption patch:
http://www.kernel.org/pub/linux/kernel/people/rml
#3 lore raw
Quoted 14 lines · george anzinger
Quoted 12 lines · Andrew Morton
Quoted 9 lines · george anzinger

On SMP systems the timer list init is done by way of a cpu_notifier call. This has two problems:

1.) Timers are started WAY before the cpu_notifier call chain is executed. In particular the console blanking timer is deleted and inserted every time printk() is called. That this does not fail is only because the kernel has yet to protect location zero.

Quoted 7 lines
Quoted 4 lines

But init_timers() directly calls timer_cpu_notify(), which directly calls init_timers_cpu().

So your patch appears to be a no-op for the boot CPU.

That is correct. The problem is when cpu_init is called for the secondary cpus. It almost immediately calls printk.

OK. So until that CPU sees it bit come on in smp_commenced_mask() it's not allowed to assume that it is running yet.

Quoted 4 lines

...
My comments here are a wonderment if
this is the right thing to do when doing a hot swap of the cpu. I sort of doubt that this is correct.

I agree. And from a quick read it does seem that ia32 is doing the right thing apart from calling printk.

I don't think we should make changes to the timer code because who knows what assumptions other console drivers could be making?

I don't think we should carefully remove all printk() calls because printk() is supposed to be robust, and always callable.

The logical thing is to implement arch_consoles_callable(). Does this look workable?

kernel/printk.c renamed from kernel/printk.c~ga +5 -1
@@ -43,7 +43,11 @@
 #define LOG_BUF_MASK	(LOG_BUF_LEN-1)
 
 #ifndef arch_consoles_callable
-#define arch_consoles_callable() (1)
+/*
+ * Some console drivers may assume that per-cpu resources have been allocated.
+ * So don't allow them to be called by this CPU until it is officially up.
+ */
+#define arch_consoles_callable() cpu_online(smp_processor_id())
 #endif
 
 /* printk's without a loglevel use this.. */

_

#4 lore raw
Quoted 35 lines · Andrew Morton
Quoted 14 lines · george anzinger
Quoted 12 lines · Andrew Morton
Quoted 9 lines · george anzinger

On SMP systems the timer list init is done by way of a cpu_notifier call. This has two problems:

1.) Timers are started WAY before the cpu_notifier call chain is executed. In particular the console blanking timer is deleted and inserted every time printk() is called. That this does not fail is only because the kernel has yet to protect location zero.

Quoted 7 lines
Quoted 4 lines

But init_timers() directly calls timer_cpu_notify(), which directly calls init_timers_cpu().

So your patch appears to be a no-op for the boot CPU.

That is correct. The problem is when cpu_init is called for the secondary cpus. It almost immediately calls printk.

OK. So until that CPU sees it bit come on in smp_commenced_mask() it's not allowed to assume that it is running yet.

Quoted 4 lines

...
My comments here are a wonderment if
this is the right thing to do when doing a hot swap of the cpu. I sort of doubt that this is correct.

I agree. And from a quick read it does seem that ia32 is doing the right thing apart from calling printk.

Quoted 8 lines

I don't think we should make changes to the timer code because who knows what assumptions other console drivers could be making?

I don't think we should carefully remove all printk() calls because printk() is supposed to be robust, and always callable.

The logical thing is to implement arch_consoles_callable(). Does this look workable?

I am not sure. The first question is when does the online bit get set for cpu 0. The next is that it does inhibit a rather large block of printks. Is this ok?

Mind you, I have not tried it yet...

-g

Quoted 17 lines
kernel/printk.c Fri Dec 20 11:33:14 2002 renamed from kernel/printk.c~ga Fri Dec 20 11:32:05 2002 +5 -1
@@ -43,7 +43,11 @@
 #define LOG_BUF_MASK   (LOG_BUF_LEN-1)
 
 #ifndef arch_consoles_callable
-#define arch_consoles_callable() (1)
+/*
+ * Some console drivers may assume that per-cpu resources have been allocated.
+ * So don't allow them to be called by this CPU until it is officially up.
+ */
+#define arch_consoles_callable() cpu_online(smp_processor_id())
 #endif
 
 /* printk's without a loglevel use this.. */
Signature · 5 lines
George Anzinger   [email protected]
High-res-timers: 
http://sourceforge.net/projects/high-res-timers/
Preemption patch:
http://www.kernel.org/pub/linux/kernel/people/rml
#5 lore raw
Quoted 7 lines · george anzinger

...

Quoted 2 lines

The logical thing is to implement arch_consoles_callable(). Does this look workable?

I am not sure. The first question is when does the online bit get set for cpu 0.

Too late, probably. We might need an escape clause for the boot CPU there.

Quoted 2 lines

The next is that it does inhibit a
rather large block of printks. Is this ok?

They get buffered, so the info will come out eventually. But we do want it to come out in a timely manner.

Quoted 1 line

Mind you, I have not tried it yet...

I think it's the right approach. I can take poke at it if you like.

#6 lore raw
In reply to george anzinger
Quoted 6 lines · george anzinger

...
I am not sure. The first question is when does the online bit get set for cpu 0. The next is that it does inhibit a rather large block of printks. Is this ok?

The boot cpu is set online extremely late. Strangely late. Why is this?

How about something like the below? We mark the boot cpu online in generic code as soon as it has initialised its per-cpu storage (seems appropriate?)

This will then allow that cpu to actually start calling into console drivers, if they have been registered. If those drivers do a mod_timer() (as the vga console does) then that will work OK.

Secondary CPUs also are not marked online until their per-cpu storage is initialised, and their notifiers have been called.

If a non-online cpu calls printk, its output will be buffered. It will be displayed by the next call to printk by an online CPU.

(The patch needs set_cpu_online()/set_cpu_possible() implementations done for the other architectures)

 arch/i386/kernel/smpboot.c |    5 -----
 include/asm-i386/smp.h     |   29 +++++++++++++++++++++++------
 include/linux/smp.h        |    7 ++++++-
 init/main.c                |    8 ++++++++
 kernel/printk.c            |    6 +++++-
 5 files changed, 42 insertions(+), 13 deletions(-)
kernel/printk.c renamed from kernel/printk.c~ga +5 -1
@@ -43,7 +43,11 @@
 #define LOG_BUF_MASK	(LOG_BUF_LEN-1)
 
 #ifndef arch_consoles_callable
-#define arch_consoles_callable() (1)
+/*
+ * Some console drivers may assume that per-cpu resources have been allocated.
+ * So don't allow them to be called by this CPU until it is officially up.
+ */
+#define arch_consoles_callable() cpu_online(smp_processor_id())
 #endif
 
 /* printk's without a loglevel use this.. */
init/main.c renamed from init/main.c~ga +8 -0
@@ -361,6 +361,14 @@ asmlinkage void __init start_kernel(void
 	printk(linux_banner);
 	setup_arch(&command_line);
 	setup_per_cpu_areas();
+
+	/*
+	 * Once the boot CPU's per-cpu memory is set up it may be considered
+	 * online.  This is mainly to turn on printk output.
+	 */
+	set_cpu_online(smp_processor_id());
+	set_cpu_possible(smp_processor_id());
+
 	build_all_zonelists();
 	page_alloc_init();
 	printk("Kernel command line: %s\n", saved_command_line);
include/asm-i386/smp.h renamed from include/asm-i386/smp.h~ga +23 -6
@@ -80,15 +80,32 @@ extern volatile int logical_apicid_to_cp
 
 extern volatile unsigned long cpu_callout_map;
 
-#define cpu_possible(cpu) (cpu_callout_map & (1<<(cpu)))
-#define cpu_online(cpu) (cpu_online_map & (1<<(cpu)))
+static inline int cpu_possible(int cpu)
+{
+	return cpu_callout_map & (1 << cpu);
+}
+
+static inline void set_cpu_possible(int cpu)
+{
+	cpu_callout_map |= (1 << cpu);
+}
+
+static inline int cpu_online(int cpu)
+{
+	return cpu_online_map & (1 << cpu);
+}
+
+static inline void set_cpu_online(int cpu)
+{
+	cpu_online_map |= (1 << cpu);
+}
 
-extern inline unsigned int num_online_cpus(void)
+static inline unsigned int num_online_cpus(void)
 {
 	return hweight32(cpu_online_map);
 }
 
-extern inline int any_online_cpu(unsigned int mask)
+static inline int any_online_cpu(unsigned int mask)
 {
 	if (mask & cpu_online_map)
 		return __ffs(mask & cpu_online_map);
@@ -96,13 +113,13 @@ extern inline int any_online_cpu(unsigne
 	return -1;
 }
 
-static __inline int hard_smp_processor_id(void)
+static inline int hard_smp_processor_id(void)
 {
 	/* we don't want to mark this access volatile - bad code generation */
 	return GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID));
 }
 
-static __inline int logical_smp_processor_id(void)
+static inline int logical_smp_processor_id(void)
 {
 	/* we don't want to mark this access volatile - bad code generation */
 	return GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR));
arch/i386/kernel/smpboot.c renamed from arch/i386/kernel/smpboot.c~ga +0 -5
@@ -992,11 +992,6 @@ static void __init smp_boot_cpus(unsigne
 	printk("CPU%d: ", 0);
 	print_cpu_info(&cpu_data[0]);
 
-	/*
-	 * We have the boot CPU online for sure.
-	 */
-	set_bit(0, &cpu_online_map);
-	set_bit(0, &cpu_callout_map);
 	boot_cpu_logical_apicid = logical_smp_processor_id();
 	map_cpu_to_boot_apicid(0, boot_cpu_apicid);
 
include/linux/smp.h renamed from include/linux/smp.h~ga +6 -1
@@ -94,7 +94,12 @@ static inline void smp_send_reschedule_a
 #define cpu_online(cpu)				({ BUG_ON((cpu) != 0); 1; })
 #define num_online_cpus()			1
 #define num_booting_cpus()			1
-#define cpu_possible(cpu)				({ BUG_ON((cpu) != 0); 1; })
+#define cpu_possible(cpu)			({ BUG_ON((cpu) != 0); 1; })
+static inline void set_cpu_online(int cpu)
+{}
+static inline void set_cpu_possible(int cpu)
+{}
+
 
 struct notifier_block;
 

_

#7 lore raw

Looks ok. I will give it a spin on Monday.

-g

Quoted 160 lines · Andrew Morton
Quoted 6 lines · george anzinger

...
I am not sure. The first question is when does the online bit get set for cpu 0. The next is that it does inhibit a rather large block of printks. Is this ok?

The boot cpu is set online extremely late. Strangely late. Why is this?

How about something like the below? We mark the boot cpu online in generic code as soon as it has initialised its per-cpu storage (seems appropriate?)

This will then allow that cpu to actually start calling into console drivers, if they have been registered. If those drivers do a mod_timer() (as the vga console does) then that will work OK.

Secondary CPUs also are not marked online until their per-cpu storage is initialised, and their notifiers have been called.

If a non-online cpu calls printk, its output will be buffered. It will be displayed by the next call to printk by an online CPU.

(The patch needs set_cpu_online()/set_cpu_possible() implementations done for the other architectures)

 arch/i386/kernel/smpboot.c |    5 -----
 include/asm-i386/smp.h     |   29 +++++++++++++++++++++++------
 include/linux/smp.h        |    7 ++++++-
 init/main.c                |    8 ++++++++
 kernel/printk.c            |    6 +++++-
 5 files changed, 42 insertions(+), 13 deletions(-)
kernel/printk.c Sat Dec 21 23:27:08 2002 renamed from kernel/printk.c~ga Sat Dec 21 23:27:08 2002 +5 -1
@@ -43,7 +43,11 @@
 #define LOG_BUF_MASK   (LOG_BUF_LEN-1)
 
 #ifndef arch_consoles_callable
-#define arch_consoles_callable() (1)
+/*
+ * Some console drivers may assume that per-cpu resources have been allocated.
+ * So don't allow them to be called by this CPU until it is officially up.
+ */
+#define arch_consoles_callable() cpu_online(smp_processor_id())
 #endif
 
 /* printk's without a loglevel use this.. */
init/main.c Sat Dec 21 23:55:00 2002 renamed from init/main.c~ga Sat Dec 21 23:48:03 2002 +8 -0
@@ -361,6 +361,14 @@ asmlinkage void __init start_kernel(void
        printk(linux_banner);
        setup_arch(&command_line);
        setup_per_cpu_areas();
+
+       /*
+        * Once the boot CPU's per-cpu memory is set up it may be considered
+        * online.  This is mainly to turn on printk output.
+        */
+       set_cpu_online(smp_processor_id());
+       set_cpu_possible(smp_processor_id());
+
        build_all_zonelists();
        page_alloc_init();
        printk("Kernel command line: %s\n", saved_command_line);
include/asm-i386/smp.h Sat Dec 21 23:54:16 2002 renamed from include/asm-i386/smp.h~ga Sat Dec 21 23:49:43 2002 +23 -6
@@ -80,15 +80,32 @@ extern volatile int logical_apicid_to_cp
 
 extern volatile unsigned long cpu_callout_map;
 
-#define cpu_possible(cpu) (cpu_callout_map & (1<<(cpu)))
-#define cpu_online(cpu) (cpu_online_map & (1<<(cpu)))
+static inline int cpu_possible(int cpu)
+{
+       return cpu_callout_map & (1 << cpu);
+}
+
+static inline void set_cpu_possible(int cpu)
+{
+       cpu_callout_map |= (1 << cpu);
+}
+
+static inline int cpu_online(int cpu)
+{
+       return cpu_online_map & (1 << cpu);
+}
+
+static inline void set_cpu_online(int cpu)
+{
+       cpu_online_map |= (1 << cpu);
+}
 
-extern inline unsigned int num_online_cpus(void)
+static inline unsigned int num_online_cpus(void)
 {
        return hweight32(cpu_online_map);
 }
 
-extern inline int any_online_cpu(unsigned int mask)
+static inline int any_online_cpu(unsigned int mask)
 {
        if (mask & cpu_online_map)
                return __ffs(mask & cpu_online_map);
@@ -96,13 +113,13 @@ extern inline int any_online_cpu(unsigne
        return -1;
 }
 
-static __inline int hard_smp_processor_id(void)
+static inline int hard_smp_processor_id(void)
 {
        /* we don't want to mark this access volatile - bad code generation */
        return GET_APIC_ID(*(unsigned long *)(APIC_BASE+APIC_ID));
 }
 
-static __inline int logical_smp_processor_id(void)
+static inline int logical_smp_processor_id(void)
 {
        /* we don't want to mark this access volatile - bad code generation */
        return GET_APIC_LOGICAL_ID(*(unsigned long *)(APIC_BASE+APIC_LDR));
arch/i386/kernel/smpboot.c Sat Dec 21 23:54:43 2002 renamed from arch/i386/kernel/smpboot.c~ga Sat Dec 21 23:52:51 2002 +0 -5
@@ -992,11 +992,6 @@ static void __init smp_boot_cpus(unsigne
        printk("CPU%d: ", 0);
        print_cpu_info(&cpu_data[0]);
 
-       /*
-        * We have the boot CPU online for sure.
-        */
-       set_bit(0, &cpu_online_map);
-       set_bit(0, &cpu_callout_map);
        boot_cpu_logical_apicid = logical_smp_processor_id();
        map_cpu_to_boot_apicid(0, boot_cpu_apicid);
 
include/linux/smp.h Sun Dec 22 00:30:15 2002 renamed from include/linux/smp.h~ga Sun Dec 22 00:29:05 2002 +6 -1
@@ -94,7 +94,12 @@ static inline void smp_send_reschedule_a
 #define cpu_online(cpu)                                ({ BUG_ON((cpu) != 0); 1; })
 #define num_online_cpus()                      1
 #define num_booting_cpus()                     1
-#define cpu_possible(cpu)                              ({ BUG_ON((cpu) != 0); 1; })
+#define cpu_possible(cpu)                      ({ BUG_ON((cpu) != 0); 1; })
+static inline void set_cpu_online(int cpu)
+{}
+static inline void set_cpu_possible(int cpu)
+{}
+
 
 struct notifier_block;
 

_
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 5 lines
George Anzinger   [email protected]
High-res-timers: 
http://sourceforge.net/projects/high-res-timers/
Preemption patch:
http://www.kernel.org/pub/linux/kernel/people/rml
#8 lore raw
In reply to Andrew Morton
Quoted 6 lines

The boot cpu is set online extremely late. Strangely late. Why is this?

How about something like the below? We mark the boot cpu online in generic code as soon as it has initialised its per-cpu storage (seems appropriate?)

I seem to recall some related problem with the topology stuff ... anyway, I tested your patch on a 16-way NUMA-Q and it works just fine. Thanks for fixing that up ....

Quoted 3 lines

This will then allow that cpu to actually start calling into console drivers, if they have been registered. If those drivers do a mod_timer() (as the vga console does) then that will work OK.

Would be nice to shift generic console_init earlier, if that's what you're implying, but we still need early_printk for setup_arch.

M.