Re: [PATCH] 2.6, 2.4, Nforce2, Experimental idle halt workaround instead of apic ack delay.

From: Ross Dickson
Date: Fri Feb 13 2004 - 20:26:40 EST


On Saturday 14 February 2004 00:41, Ross Dickson wrote:
> On Friday 13 February 2004 21:17, Prakash K. Cheemplavam wrote:
> > Hi,
> >
> > I am just testing this patch with latest 2.6.3-rc2-mm1. It works in that
> > sense, that my machine doesn't lock up of APIC issue. (If it locks up -
> > hasn't done yet - then because of something else, I am currently
> > discssing it in another thread...)
> >
> > But it doesn't work in the sense of cooling my machine down. Though
> > athcool reports disconnect is activated it behaves like it is not, ie,
> > turning disconnect off makes no difference in temperatures. Your old
> > tack patch in conjunction with 2.6.2-rc1 (linus) works like a charm, ie
> > no lock-ups and less temp.
> >
>
> Thanks Prakash for testing it and spotting thermal problem.
>
> Here are some temperatures from my machine read from the bios on reboot.
> I gave it minimal activity for the minutes prior to reboot.
>
> Win98, 47C
> XPHome, 42C
> Patched Linux 2.4.24 (1000Hz), 40C
> Patched Linux 2.6.3-rc1-mm1, 53C OUCH!
>
> Sorry, I will have to go through my latest patch and see why the temp differs
> so much between 2.4 and 2.6. I currently use patched 2.4.24 with Suse 8.2 for
> convenience. When it stopped the lockups on 2.6 I thought the 2.6 was
> working the same way.
>

Found the problem for 2.6

After fixing it the 2.6 temperature is
Patched Linux 2.6.3-rc1-mm1, 38C
Ambient today is 1C cooler also.

The fix is to put the brackets back on "!need_resched()" so that we call
the function and test its return value - not just test the function pointer!

Should be as follows:

static void c1halt_idle(void)
{
if (!hlt_counter && current_cpu_data.hlt_works_ok) {
local_irq_disable();
/* only hlt disconnect if more than 1.6% of apic interval remains */
if( (!need_resched() ) &&
(apic_read(APIC_TMCCT) > (apic_read(APIC_TMICT)>>6))) {
ndelay(500); /* helps nforce2 but adds 0.5us hard int latency */
safe_halt(); /* nothing better to do until we wake up */
}
else
local_irq_enable();
}
}

Again attached in tarball, 2.4 patch was not changed.

Revision1 patch for 2.6.3-rc1-mm1:

---CUTHERE---
--- linux-2.6.3-rc1-mm1/arch/i386/kernel/process.c 2004-02-11 21:29:49.000000000 +1000
+++ linux-2.6.3-rc1-mm1-nf/arch/i386/kernel/process.c 2004-02-11 23:44:04.000000000 +1000
@@ -50,10 +50,11 @@
#include <asm/desc.h>
#include <asm/atomic_kmap.h>
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
+#include <asm/apic.h>

#include <linux/irq.h>
#include <linux/err.h>

#include <asm/tlbflush.h>
@@ -92,11 +93,11 @@ EXPORT_SYMBOL(enable_hlt);

/*
* We use this if we don't have any better
* idle routine..
*/
-void default_idle(void)
+static void default_idle(void)
{
if (!hlt_counter && current_cpu_data.hlt_works_ok) {
local_irq_disable();
if (!need_resched())
safe_halt();
@@ -104,10 +105,29 @@ void default_idle(void)
local_irq_enable();
}
}

/*
+ * We use this to avoid nforce2 lockups
+ * Reduces frequency of C1 disconnects
+ */
+static void c1halt_idle(void)
+{
+ if (!hlt_counter && current_cpu_data.hlt_works_ok) {
+ local_irq_disable();
+ /* only hlt disconnect if more than 1.6% of apic interval remains */
+ if( (!need_resched()) &&
+ (apic_read(APIC_TMCCT) > (apic_read(APIC_TMICT)>>6))) {
+ ndelay(500); /* helps nforce2 but adds 0.5us hard int latency */
+ safe_halt(); /* nothing better to do until we wake up */
+ }
+ else
+ local_irq_enable();
+ }
+}
+
+/*
* On SMP it's slightly faster (but much more power-consuming!)
* to poll the ->work.need_resched flag instead of waiting for the
* cross-CPU IPI to arrive. Use this option with caution.
*/
static void poll_idle (void)
@@ -195,20 +215,18 @@ static inline void check_cpu_quiescent(v
* The idle thread. There's no useful work to be
* done, so just try to conserve power and have a
* low exit latency (ie sit in a loop waiting for
* somebody to say that they'd like to reschedule)
*/
+static void (*idle)(void);
void cpu_idle (void)
{
/* endless idle loop with no priority at all */
while (1) {
while (!need_resched()) {
- void (*idle)(void) = pm_idle;
-
if (!idle)
- idle = default_idle;
-
+ idle = pm_idle ? pm_idle : default_idle;
check_cpu_quiescent();
irq_stat[smp_processor_id()].idle_timestamp = jiffies;
idle();
}
schedule();
@@ -260,16 +278,18 @@ void __init select_idle_routine(const st

static int __init idle_setup (char *str)
{
if (!strncmp(str, "poll", 4)) {
printk("using polling idle threads.\n");
- pm_idle = poll_idle;
+ idle = poll_idle;
} else if (!strncmp(str, "halt", 4)) {
printk("using halt in idle threads.\n");
- pm_idle = default_idle;
+ idle = default_idle;
+ } else if (!strncmp(str, "C1halt", 6)) {
+ printk("using C1 halt disconnect friendly idle threads.\n");
+ idle = c1halt_idle;
}
-
return 1;
}

__setup("idle=", idle_setup);
---CUTHERE---


--- linux-2.6.3-rc1-mm1/arch/i386/kernel/process.c 2004-02-11 21:29:49.000000000 +1000
+++ linux-2.6.3-rc1-mm1-nf/arch/i386/kernel/process.c 2004-02-11 23:44:04.000000000 +1000
@@ -50,10 +50,11 @@
#include <asm/desc.h>
#include <asm/atomic_kmap.h>
#ifdef CONFIG_MATH_EMULATION
#include <asm/math_emu.h>
#endif
+#include <asm/apic.h>

#include <linux/irq.h>
#include <linux/err.h>

#include <asm/tlbflush.h>
@@ -92,11 +93,11 @@ EXPORT_SYMBOL(enable_hlt);

/*
* We use this if we don't have any better
* idle routine..
*/
-void default_idle(void)
+static void default_idle(void)
{
if (!hlt_counter && current_cpu_data.hlt_works_ok) {
local_irq_disable();
if (!need_resched())
safe_halt();
@@ -104,10 +105,29 @@ void default_idle(void)
local_irq_enable();
}
}

/*
+ * We use this to avoid nforce2 lockups
+ * Reduces frequency of C1 disconnects
+ */
+static void c1halt_idle(void)
+{
+ if (!hlt_counter && current_cpu_data.hlt_works_ok) {
+ local_irq_disable();
+ /* only hlt disconnect if more than 1.6% of apic interval remains */
+ if( (!need_resched()) &&
+ (apic_read(APIC_TMCCT) > (apic_read(APIC_TMICT)>>6))) {
+ ndelay(500); /* helps nforce2 but adds 0.5us hard int latency */
+ safe_halt(); /* nothing better to do until we wake up */
+ }
+ else
+ local_irq_enable();
+ }
+}
+
+/*
* On SMP it's slightly faster (but much more power-consuming!)
* to poll the ->work.need_resched flag instead of waiting for the
* cross-CPU IPI to arrive. Use this option with caution.
*/
static void poll_idle (void)
@@ -195,20 +215,18 @@ static inline void check_cpu_quiescent(v
* The idle thread. There's no useful work to be
* done, so just try to conserve power and have a
* low exit latency (ie sit in a loop waiting for
* somebody to say that they'd like to reschedule)
*/
+static void (*idle)(void);
void cpu_idle (void)
{
/* endless idle loop with no priority at all */
while (1) {
while (!need_resched()) {
- void (*idle)(void) = pm_idle;
-
if (!idle)
- idle = default_idle;
-
+ idle = pm_idle ? pm_idle : default_idle;
check_cpu_quiescent();
irq_stat[smp_processor_id()].idle_timestamp = jiffies;
idle();
}
schedule();
@@ -260,16 +278,18 @@ void __init select_idle_routine(const st

static int __init idle_setup (char *str)
{
if (!strncmp(str, "poll", 4)) {
printk("using polling idle threads.\n");
- pm_idle = poll_idle;
+ idle = poll_idle;
} else if (!strncmp(str, "halt", 4)) {
printk("using halt in idle threads.\n");
- pm_idle = default_idle;
+ idle = default_idle;
+ } else if (!strncmp(str, "C1halt", 6)) {
+ printk("using C1 halt disconnect friendly idle threads.\n");
+ idle = c1halt_idle;
}
-
return 1;
}

__setup("idle=", idle_setup);