2014-10-01 10:57:57 +02:00
/*
* Idle functions for s390 .
*
* Copyright IBM Corp . 2014
*
* Author ( s ) : Martin Schwidefsky < schwidefsky @ de . ibm . com >
*/
# include <linux/kernel.h>
# include <linux/kernel_stat.h>
# include <linux/kprobes.h>
# include <linux/notifier.h>
# include <linux/init.h>
# include <linux/cpu.h>
# include <asm/cputime.h>
# include <asm/nmi.h>
# include <asm/smp.h>
# include "entry.h"
static DEFINE_PER_CPU ( struct s390_idle_data , s390_idle ) ;
2014-10-22 12:42:38 +02:00
void enabled_wait ( void )
2014-10-01 10:57:57 +02:00
{
2014-10-15 07:48:18 +02:00
struct s390_idle_data * idle = this_cpu_ptr ( & s390_idle ) ;
2014-10-01 10:57:57 +02:00
unsigned long long idle_time ;
unsigned long psw_mask ;
trace_hardirqs_on ( ) ;
/* Wait for external, I/O or machine check interrupt. */
psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK ;
clear_cpu_flag ( CIF_NOHZ_DELAY ) ;
/* Call the assembler magic in entry.S */
psw_idle ( idle , psw_mask ) ;
2014-12-01 14:24:41 +01:00
trace_hardirqs_off ( ) ;
2014-10-01 10:57:57 +02:00
/* Account time spent with enabled wait psw loaded as idle time. */
2014-11-28 19:23:34 +01:00
write_seqcount_begin ( & idle - > seqcount ) ;
2014-10-01 10:57:57 +02:00
idle_time = idle - > clock_idle_exit - idle - > clock_idle_enter ;
idle - > clock_idle_enter = idle - > clock_idle_exit = 0ULL ;
idle - > idle_time + = idle_time ;
idle - > idle_count + + ;
account_idle_time ( idle_time ) ;
2014-11-28 19:23:34 +01:00
write_seqcount_end ( & idle - > seqcount ) ;
2014-10-01 10:57:57 +02:00
}
2014-10-22 12:42:38 +02:00
NOKPROBE_SYMBOL ( enabled_wait ) ;
2014-10-01 10:57:57 +02:00
static ssize_t show_idle_count ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct s390_idle_data * idle = & per_cpu ( s390_idle , dev - > id ) ;
unsigned long long idle_count ;
2014-11-28 19:23:34 +01:00
unsigned int seq ;
2014-10-01 10:57:57 +02:00
do {
2014-11-28 19:23:34 +01:00
seq = read_seqcount_begin ( & idle - > seqcount ) ;
2017-02-10 12:34:49 +01:00
idle_count = READ_ONCE ( idle - > idle_count ) ;
if ( READ_ONCE ( idle - > clock_idle_enter ) )
2014-10-01 10:57:57 +02:00
idle_count + + ;
2014-11-28 19:23:34 +01:00
} while ( read_seqcount_retry ( & idle - > seqcount , seq ) ) ;
2014-10-01 10:57:57 +02:00
return sprintf ( buf , " %llu \n " , idle_count ) ;
}
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 = & per_cpu ( s390_idle , dev - > id ) ;
unsigned long long now , idle_time , idle_enter , idle_exit ;
2014-11-28 19:23:34 +01:00
unsigned int seq ;
2014-10-01 10:57:57 +02:00
do {
now = get_tod_clock ( ) ;
2014-11-28 19:23:34 +01:00
seq = read_seqcount_begin ( & idle - > seqcount ) ;
2017-02-10 12:34:49 +01:00
idle_time = READ_ONCE ( idle - > idle_time ) ;
idle_enter = READ_ONCE ( idle - > clock_idle_enter ) ;
idle_exit = READ_ONCE ( idle - > clock_idle_exit ) ;
2014-11-28 19:23:34 +01:00
} while ( read_seqcount_retry ( & idle - > seqcount , seq ) ) ;
2014-10-01 10:57:57 +02:00
idle_time + = idle_enter ? ( ( idle_exit ? : now ) - idle_enter ) : 0 ;
return sprintf ( buf , " %llu \n " , idle_time > > 12 ) ;
}
DEVICE_ATTR ( idle_time_us , 0444 , show_idle_time , NULL ) ;
cputime64_t arch_cpu_idle_time ( int cpu )
{
struct s390_idle_data * idle = & per_cpu ( s390_idle , cpu ) ;
unsigned long long now , idle_enter , idle_exit ;
2014-11-28 19:23:34 +01:00
unsigned int seq ;
2014-10-01 10:57:57 +02:00
do {
now = get_tod_clock ( ) ;
2014-11-28 19:23:34 +01:00
seq = read_seqcount_begin ( & idle - > seqcount ) ;
2017-02-10 12:34:49 +01:00
idle_enter = READ_ONCE ( idle - > clock_idle_enter ) ;
idle_exit = READ_ONCE ( idle - > clock_idle_exit ) ;
2014-11-28 19:23:34 +01:00
} while ( read_seqcount_retry ( & idle - > seqcount , seq ) ) ;
2014-10-01 10:57:57 +02:00
return idle_enter ? ( ( idle_exit ? : now ) - idle_enter ) : 0 ;
}
void arch_cpu_idle_enter ( void )
{
local_mcck_disable ( ) ;
}
void arch_cpu_idle ( void )
{
if ( ! test_cpu_flag ( CIF_MCCK_PENDING ) )
/* Halt the cpu and keep track of cpu time accounting. */
enabled_wait ( ) ;
local_irq_enable ( ) ;
}
void arch_cpu_idle_exit ( void )
{
local_mcck_enable ( ) ;
if ( test_cpu_flag ( CIF_MCCK_PENDING ) )
s390_handle_mcck ( ) ;
}
void arch_cpu_idle_dead ( void )
{
cpu_die ( ) ;
}