#include <linux/utsname.h>
#include <linux/tick.h>
#include <linux/elfcore.h>
+#include <linux/kernel_stat.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
.lock = __SPIN_LOCK_UNLOCKED(s390_idle.lock)
};
-static int s390_idle_enter(void)
+void s390_idle_leave(void)
{
struct s390_idle_data *idle;
+ unsigned long long idle_time;
idle = &__get_cpu_var(s390_idle);
+ idle_time = S390_lowcore.int_clock - idle->idle_enter;
spin_lock(&idle->lock);
+ idle->idle_time += idle_time;
+ idle->idle_enter = 0ULL;
idle->idle_count++;
- idle->in_idle = 1;
- idle->idle_enter = get_clock();
spin_unlock(&idle->lock);
- vtime_stop_cpu_timer();
- return NOTIFY_OK;
-}
-
-void s390_idle_leave(void)
-{
- struct s390_idle_data *idle;
-
vtime_start_cpu_timer();
- idle = &__get_cpu_var(s390_idle);
- spin_lock(&idle->lock);
- idle->idle_time += get_clock() - idle->idle_enter;
- idle->in_idle = 0;
- spin_unlock(&idle->lock);
}
extern void s390_handle_mcck(void);
*/
static void default_idle(void)
{
+ struct s390_idle_data *idle = &__get_cpu_var(s390_idle);
+ unsigned long addr;
+ psw_t psw;
+
/* CPU is going idle. */
local_irq_disable();
if (need_resched()) {
local_irq_enable();
return;
}
- if (s390_idle_enter() == NOTIFY_BAD) {
- local_irq_enable();
- return;
- }
#ifdef CONFIG_HOTPLUG_CPU
if (cpu_is_offline(smp_processor_id())) {
preempt_enable_no_resched();
trace_hardirqs_on();
/* Don't trace preempt off for idle. */
stop_critical_timings();
+ vtime_stop_cpu_timer();
+
+ /*
+ * The inline assembly is equivalent to
+ * idle->idle_enter = get_clock();
+ * __load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT |
+ * PSW_MASK_IO | PSW_MASK_EXT);
+ * The difference is that the inline assembly makes sure that
+ * the stck instruction is right before the lpsw instruction.
+ * This is done to increase the precision.
+ */
+
/* Wait for external, I/O or machine check interrupt. */
- __load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT |
- PSW_MASK_IO | PSW_MASK_EXT);
+ psw.mask = psw_kernel_bits|PSW_MASK_WAIT|PSW_MASK_IO|PSW_MASK_EXT;
+#ifndef __s390x__
+ asm volatile(
+ " basr %0,0\n"
+ "0: ahi %0,1f-0b\n"
+ " st %0,4(%2)\n"
+ " stck 0(%3)\n"
+ " lpsw 0(%2)\n"
+ "1:"
+ : "=&d" (addr), "=m" (idle->idle_enter)
+ : "a" (&psw), "a" (&idle->idle_enter), "m" (psw)
+ : "memory", "cc");
+#else /* __s390x__ */
+ asm volatile(
+ " larl %0,1f\n"
+ " stg %0,8(%2)\n"
+ " stck 0(%3)\n"
+ " lpswe 0(%2)\n"
+ "1:"
+ : "=&d" (addr), "=m" (idle->idle_enter)
+ : "a" (&psw), "a" (&idle->idle_enter), "m" (psw)
+ : "memory", "cc");
+#endif /* __s390x__ */
start_critical_timings();
}
unsigned long long idle_count;
idle = &per_cpu(s390_idle, dev->id);
- spin_lock_irq(&idle->lock);
+ spin_lock(&idle->lock);
idle_count = idle->idle_count;
- spin_unlock_irq(&idle->lock);
+ if (idle->idle_enter)
+ idle_count++;
+ spin_unlock(&idle->lock);
return sprintf(buf, "%llu\n", idle_count);
}
static SYSDEV_ATTR(idle_count, 0444, show_idle_count, NULL);
struct sysdev_attribute *attr, char *buf)
{
struct s390_idle_data *idle;
- unsigned long long new_time;
+ unsigned long long now, idle_time, idle_enter;
idle = &per_cpu(s390_idle, dev->id);
- spin_lock_irq(&idle->lock);
- if (idle->in_idle) {
- new_time = get_clock();
- idle->idle_time += new_time - idle->idle_enter;
- idle->idle_enter = new_time;
- }
- new_time = idle->idle_time;
- spin_unlock_irq(&idle->lock);
- return sprintf(buf, "%llu\n", new_time >> 12);
+ spin_lock(&idle->lock);
+ now = get_clock();
+ idle_time = idle->idle_time;
+ idle_enter = idle->idle_enter;
+ if (idle_enter != 0ULL && idle_enter < now)
+ idle_time += now - idle_enter;
+ spin_unlock(&idle->lock);
+ return sprintf(buf, "%llu\n", idle_time >> 12);
}
static SYSDEV_ATTR(idle_time_us, 0444, show_idle_time, NULL);