summaryrefslogtreecommitdiffstats
path: root/arch/s390/kernel/smp.c
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2012-03-11 11:59:27 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2012-03-11 11:59:28 -0400
commit4c1051e37a0e2a941115c6fb7ba08c318f25a0f9 (patch)
treef228f1a90c0d7abb8308f275d0906dd7d1588ba3 /arch/s390/kernel/smp.c
parent8b646bd759086f6090fe27acf414c0b5faa737f4 (diff)
[S390] rework idle code
Whenever the cpu loads an enabled wait PSW it will appear as idle to the underlying host system. The code in default_idle calls vtime_stop_cpu which does the necessary voodoo to get the cpu time accounting right. The udelay code just loads an enabled wait PSW. To correct this rework the vtime_stop_cpu/vtime_start_cpu logic and move the difficult parts to entry[64].S, vtime_stop_cpu can now be called from anywhere and vtime_start_cpu is gone. The correction of the cpu time during wakeup from an enabled wait PSW is done with a critical section in entry[64].S. As vtime_start_cpu is gone, s390_idle_check can be removed as well. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'arch/s390/kernel/smp.c')
-rw-r--r--arch/s390/kernel/smp.c46
1 files changed, 17 insertions, 29 deletions
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 6db8526a602..afd6e5113a9 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -972,22 +972,16 @@ static DEVICE_ATTR(capability, 0444, show_capability, NULL);
static ssize_t show_idle_count(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct s390_idle_data *idle;
+ struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
unsigned long long idle_count;
unsigned int sequence;
- idle = &per_cpu(s390_idle, dev->id);
-repeat:
- sequence = idle->sequence;
- smp_rmb();
- if (sequence & 1)
- goto repeat;
- idle_count = idle->idle_count;
- if (idle->idle_enter)
- idle_count++;
- smp_rmb();
- if (idle->sequence != sequence)
- goto repeat;
+ do {
+ sequence = ACCESS_ONCE(idle->sequence);
+ idle_count = ACCESS_ONCE(idle->idle_count);
+ if (ACCESS_ONCE(idle->idle_enter))
+ idle_count++;
+ } while ((sequence & 1) || (idle->sequence != sequence));
return sprintf(buf, "%llu\n", idle_count);
}
static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
@@ -995,24 +989,18 @@ static DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL);
static ssize_t show_idle_time(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct s390_idle_data *idle;
- unsigned long long now, idle_time, idle_enter;
+ struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id);
+ unsigned long long now, idle_time, idle_enter, idle_exit;
unsigned int sequence;
- idle = &per_cpu(s390_idle, dev->id);
- now = get_clock();
-repeat:
- sequence = idle->sequence;
- smp_rmb();
- if (sequence & 1)
- goto repeat;
- idle_time = idle->idle_time;
- idle_enter = idle->idle_enter;
- if (idle_enter != 0ULL && idle_enter < now)
- idle_time += now - idle_enter;
- smp_rmb();
- if (idle->sequence != sequence)
- goto repeat;
+ do {
+ now = get_clock();
+ sequence = ACCESS_ONCE(idle->sequence);
+ idle_time = ACCESS_ONCE(idle->idle_time);
+ idle_enter = ACCESS_ONCE(idle->idle_enter);
+ idle_exit = ACCESS_ONCE(idle->idle_exit);
+ } while ((sequence & 1) || (idle->sequence != sequence));
+ idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0;
return sprintf(buf, "%llu\n", idle_time >> 12);
}
static DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL);