patch-2.4.25 linux-2.4.25/arch/ppc64/kernel/idle.c
Next file: linux-2.4.25/arch/ppc64/kernel/ioctl32.c
Previous file: linux-2.4.25/arch/ppc64/kernel/iSeries_setup.c
Back to the patch index
Back to the overall index
- Lines: 323
- Date:
2004-02-18 05:36:30.000000000 -0800
- Orig file:
linux-2.4.24/arch/ppc64/kernel/idle.c
- Orig date:
2003-06-13 07:51:31.000000000 -0700
diff -urN linux-2.4.24/arch/ppc64/kernel/idle.c linux-2.4.25/arch/ppc64/kernel/idle.c
@@ -2,7 +2,12 @@
* Idle daemon for PowerPC. Idle daemon will handle any action
* that needs to be taken when the system becomes idle.
*
- * Written by Cort Dougan (cort@cs.nmt.edu)
+ * Originally Written by Cort Dougan (cort@cs.nmt.edu)
+ *
+ * iSeries supported added by Mike Corrigan <mikejc@us.ibm.com>
+ *
+ * Additional shared processor, SMT, and firmware support
+ * Copyright (c) 2003 Dave Engebretsen <engebret@us.ibm.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -10,6 +15,7 @@
* 2 of the License, or (at your option) any later version.
*/
#include <linux/config.h>
+#include <linux/init.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
@@ -28,17 +34,19 @@
#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/cache.h>
+#include <asm/cputable.h>
#include <asm/time.h>
#include <asm/iSeries/LparData.h>
#include <asm/iSeries/HvCall.h>
#include <asm/iSeries/ItLpQueue.h>
+int (*idle_loop)(void);
+
#ifdef CONFIG_PPC_ISERIES
static void yield_shared_processor(void)
{
struct paca_struct *lpaca = get_paca();
- unsigned long tb;
HvCall_setEnabledInterrupts( HvCall_MaskIPI |
HvCall_MaskLpEvent |
@@ -46,68 +54,180 @@
HvCall_MaskTimeout );
if ( ! ItLpQueue_isLpIntPending( paca->lpQueuePtr ) ) {
- tb = get_tb();
- /* Compute future tb value when yield should expire */
- HvCall_yieldProcessor( HvCall_YieldTimed, tb+tb_ticks_per_jiffy );
+ /*
+ * Compute future tb value when yield should expire.
+ * We want to be woken up when the next decrementer is
+ * to fire.
+ */
- /* The decrementer stops during the yield. Force a fake decrementer
- * here and let the timer_interrupt code sort out the actual time.
+ __cli();
+ lpaca->yielded = 1; /* Indicate a prod is desired */
+ lpaca->xLpPaca.xIdle = 1; /* Inform the HV we are idle */
+
+ HvCall_yieldProcessor(HvCall_YieldTimed,
+ lpaca->next_jiffy_update_tb);
+
+ lpaca->yielded = 0; /* Back to IPI's */
+ __sti();
+
+ /*
+ * The decrementer stops during the yield. Force a fake
+ * decrementer here and let the timer_interrupt code sort
+ * out the actual time.
*/
lpaca->xLpPaca.xIntDword.xFields.xDecrInt = 1;
}
process_iSeries_events();
}
-#endif /* CONFIG_PPC_ISERIES */
-int idled(void)
+int idle_iSeries(void)
{
struct paca_struct *lpaca;
long oldval;
-#ifdef CONFIG_PPC_ISERIES
unsigned long CTRL;
-#endif
/* endless loop with no priority at all */
current->nice = 20;
current->counter = -100;
-#ifdef CONFIG_PPC_ISERIES
+
/* ensure iSeries run light will be out when idle */
current->thread.flags &= ~PPC_FLAG_RUN_LIGHT;
CTRL = mfspr(CTRLF);
CTRL &= ~RUNLATCH;
mtspr(CTRLT, CTRL);
-#endif
init_idle();
lpaca = get_paca();
for (;;) {
-#ifdef CONFIG_PPC_ISERIES
if ( lpaca->xLpPaca.xSharedProc ) {
if ( ItLpQueue_isLpIntPending( lpaca->lpQueuePtr ) )
process_iSeries_events();
if ( !current->need_resched )
yield_shared_processor();
- }
- else
-#endif
- {
+ } else {
/* Avoid an IPI by setting need_resched */
oldval = xchg(¤t->need_resched, -1);
if (!oldval) {
while(current->need_resched == -1) {
-#ifdef CONFIG_PPC_ISERIES
HMT_medium();
if ( ItLpQueue_isLpIntPending( lpaca->lpQueuePtr ) )
process_iSeries_events();
+ HMT_low();
+ }
+ }
+ }
+ HMT_medium();
+ if (current->need_resched) {
+ lpaca->xLpPaca.xIdle = 0;
+ schedule();
+ check_pgt_cache();
+ }
+ }
+ return 0;
+}
#endif
+
+int idle_default(void)
+{
+ long oldval;
+
+ current->nice = 20;
+ current->counter = -100;
+ init_idle();
+
+ for (;;) {
+ /* Avoid an IPI by setting need_resched */
+ oldval = xchg(¤t->need_resched, -1);
+ if (!oldval) {
+ while(current->need_resched == -1) {
HMT_low();
+ }
+ }
+ HMT_medium();
+ if (current->need_resched) {
+ schedule();
+ check_pgt_cache();
+ }
+ }
+ return 0;
+}
+
+int idle_dedicated(void)
+{
+ long oldval;
+ struct paca_struct *lpaca = get_paca(), *ppaca;;
+ unsigned long start_snooze;
+
+ ppaca = &paca[(lpaca->xPacaIndex) ^ 1];
+ current->nice = 20;
+ current->counter = -100;
+ init_idle();
+
+ for (;;) {
+ /* Indicate to the HV that we are idle. Now would be
+ * a good time to find other work to dispatch. */
+ lpaca->xLpPaca.xIdle = 1;
+
+ /* Avoid an IPI by setting need_resched */
+ oldval = xchg(¤t->need_resched, -1);
+ if (!oldval) {
+ start_snooze = __get_tb();
+ while(current->need_resched == -1) {
+ if (__get_tb() <
+ (start_snooze +
+ naca->smt_snooze_delay*tb_ticks_per_usec)) {
+ HMT_low(); /* Low thread priority */
+ continue;
+ }
+
+ HMT_very_low(); /* Low power mode */
+
+ /* If the SMT mode is system controlled & the
+ * partner thread is doing work, switch into
+ * ST mode.
+ */
+ if((naca->smt_state == SMT_DYNAMIC) &&
+ (!(ppaca->xLpPaca.xIdle))) {
+ /* need_resched could be 1 or -1 at this
+ * point. If it is -1, set it to 0, so
+ * an IPI/Prod is sent. If it is 1, keep
+ * it that way & schedule work.
+ */
+ oldval = xchg(¤t->need_resched, 0);
+ if(oldval == 1) {
+ current->need_resched = oldval;
+ break;
+ }
+
+ /* DRENG: Go HMT_medium here ? */
+ __cli();
+ lpaca->yielded = 1;
+
+ /* SMT dynamic mode. Cede will result
+ * in this thread going dormant, if the
+ * partner thread is still doing work.
+ * Thread wakes up if partner goes idle,
+ * an interrupt is presented, or a prod
+ * occurs. Returning from the cede
+ * enables external interrupts.
+ */
+ cede_processor();
+
+ lpaca->yielded = 0;
+ } else {
+ /* Give the HV an opportunity at the
+ * processor, since we are not doing
+ * any work.
+ */
+ poll_pending();
}
}
}
HMT_medium();
if (current->need_resched) {
+ lpaca->xLpPaca.xIdle = 0;
schedule();
check_pgt_cache();
}
@@ -115,12 +235,78 @@
return 0;
}
+int idle_shared(void)
+{
+ struct paca_struct *lpaca = get_paca();
+
+ /* endless loop with no priority at all */
+ current->nice = 20;
+ current->counter = -100;
+
+ init_idle();
+
+ for (;;) {
+ /* Indicate to the HV that we are idle. Now would be
+ * a good time to find other work to dispatch. */
+ lpaca->xLpPaca.xIdle = 1;
+
+ if (!current->need_resched) {
+ __cli();
+ lpaca->yielded = 1;
+
/*
- * SMP entry into the idle task - calls the same thing as the
- * non-smp versions. -- Cort
+ * Yield the processor to the hypervisor. We return if
+ * an external interrupt occurs (which are driven prior
+ * to returning here) or if a prod occurs from another
+ * processor. When returning here, external interrupts
+ * are enabled.
*/
+ cede_processor();
+
+ lpaca->yielded = 0;
+ }
+
+ HMT_medium();
+ if (current->need_resched) {
+ lpaca->xLpPaca.xIdle = 0;
+ schedule();
+ check_pgt_cache();
+ }
+ }
+
+ return 0;
+}
+
int cpu_idle(void)
{
- idled();
+ idle_loop();
return 0;
}
+
+int idle_setup(void)
+{
+#ifdef CONFIG_PPC_ISERIES
+ idle_loop = idle_iSeries;
+#else
+ if (systemcfg->platform & PLATFORM_PSERIES) {
+ if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) {
+ if(get_paca()->xLpPaca.xSharedProc) {
+ printk("idle = idle_shared\n");
+ idle_loop = idle_shared;
+ } else {
+ printk("idle = idle_dedicated\n");
+ idle_loop = idle_dedicated;
+ }
+ } else {
+ printk("idle = idle_default\n");
+ idle_loop = idle_default;
+ }
+ } else {
+ printk("idle_setup: unknown platform, use idle_default\n");
+ idle_loop = idle_default;
+ }
+#endif
+
+ return 1;
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)