Skip to content

Commit 0d38547

Browse files
hcahcaSasha Levin
authored andcommitted
s390/idle: Fix cpu idle exit cpu time accounting
[ Upstream commit 0d785e2 ] With the conversion to generic entry [1] cpu idle exit cpu time accounting was converted from assembly to C. This introduced an reversed order of cpu time accounting. On cpu idle exit the current accounting happens with the following call chain: -> do_io_irq()/do_ext_irq() -> irq_enter_rcu() -> account_hardirq_enter() -> vtime_account_irq() -> vtime_account_kernel() vtime_account_kernel() accounts the passed cpu time since last_update_timer as system time, and updates last_update_timer to the current cpu timer value. However the subsequent call of -> account_idle_time_irq() will incorrectly subtract passed cpu time from timer_idle_enter to the updated last_update_timer value from system_timer. Then last_update_timer is updated to a sys_enter_timer, which means that last_update_timer goes back in time. Subsequently account_hardirq_exit() will account too much cpu time as hardirq time. The sum of all accounted cpu times is still correct, however some cpu time which was previously accounted as system time is now accounted as hardirq time, plus there is the oddity that last_update_timer goes back in time. Restore previous behavior by extracting cpu time accounting code from account_idle_time_irq() into a new update_timer_idle() function and call it before irq_enter_rcu(). Fixes: 56e62a7 ("s390: convert to generic entry") [1] Reviewed-by: Sven Schnelle <svens@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Vasily Gorbik <gor@linux.ibm.com> Signed-off-by: Sasha Levin <sashal@kernel.org>
1 parent bb19062 commit 0d38547

File tree

3 files changed

+18
-6
lines changed

3 files changed

+18
-6
lines changed

arch/s390/include/asm/idle.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ extern struct device_attribute dev_attr_idle_count;
2323
extern struct device_attribute dev_attr_idle_time_us;
2424

2525
void psw_idle(struct s390_idle_data *data, unsigned long psw_mask);
26+
void update_timer_idle(void);
2627

2728
#endif /* _S390_IDLE_H */

arch/s390/kernel/idle.c

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,10 @@
2121

2222
static DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
2323

24-
void account_idle_time_irq(void)
24+
void update_timer_idle(void)
2525
{
2626
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
2727
struct lowcore *lc = get_lowcore();
28-
unsigned long idle_time;
2928
u64 cycles_new[8];
3029
int i;
3130

@@ -35,13 +34,19 @@ void account_idle_time_irq(void)
3534
this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]);
3635
}
3736

38-
idle_time = lc->int_clock - idle->clock_idle_enter;
39-
4037
lc->steal_timer += idle->clock_idle_enter - lc->last_update_clock;
4138
lc->last_update_clock = lc->int_clock;
4239

4340
lc->system_timer += lc->last_update_timer - idle->timer_idle_enter;
4441
lc->last_update_timer = lc->sys_enter_timer;
42+
}
43+
44+
void account_idle_time_irq(void)
45+
{
46+
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
47+
unsigned long idle_time;
48+
49+
idle_time = get_lowcore()->int_clock - idle->clock_idle_enter;
4550

4651
/* Account time spent with enabled wait psw loaded as idle time. */
4752
WRITE_ONCE(idle->idle_time, READ_ONCE(idle->idle_time) + idle_time);

arch/s390/kernel/irq.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,10 @@ void noinstr do_io_irq(struct pt_regs *regs)
146146
struct pt_regs *old_regs = set_irq_regs(regs);
147147
bool from_idle;
148148

149+
from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
150+
if (from_idle)
151+
update_timer_idle();
152+
149153
irq_enter_rcu();
150154

151155
if (user_mode(regs)) {
@@ -154,7 +158,6 @@ void noinstr do_io_irq(struct pt_regs *regs)
154158
current->thread.last_break = regs->last_break;
155159
}
156160

157-
from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
158161
if (from_idle)
159162
account_idle_time_irq();
160163

@@ -182,6 +185,10 @@ void noinstr do_ext_irq(struct pt_regs *regs)
182185
struct pt_regs *old_regs = set_irq_regs(regs);
183186
bool from_idle;
184187

188+
from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
189+
if (from_idle)
190+
update_timer_idle();
191+
185192
irq_enter_rcu();
186193

187194
if (user_mode(regs)) {
@@ -194,7 +201,6 @@ void noinstr do_ext_irq(struct pt_regs *regs)
194201
regs->int_parm = get_lowcore()->ext_params;
195202
regs->int_parm_long = get_lowcore()->ext_params2;
196203

197-
from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
198204
if (from_idle)
199205
account_idle_time_irq();
200206

0 commit comments

Comments
 (0)