1*b5f87f15SMartin Schwidefsky /* 2*b5f87f15SMartin Schwidefsky * Idle functions for s390. 3*b5f87f15SMartin Schwidefsky * 4*b5f87f15SMartin Schwidefsky * Copyright IBM Corp. 2014 5*b5f87f15SMartin Schwidefsky * 6*b5f87f15SMartin Schwidefsky * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com> 7*b5f87f15SMartin Schwidefsky */ 8*b5f87f15SMartin Schwidefsky 9*b5f87f15SMartin Schwidefsky #include <linux/kernel.h> 10*b5f87f15SMartin Schwidefsky #include <linux/kernel_stat.h> 11*b5f87f15SMartin Schwidefsky #include <linux/kprobes.h> 12*b5f87f15SMartin Schwidefsky #include <linux/notifier.h> 13*b5f87f15SMartin Schwidefsky #include <linux/init.h> 14*b5f87f15SMartin Schwidefsky #include <linux/cpu.h> 15*b5f87f15SMartin Schwidefsky #include <asm/cputime.h> 16*b5f87f15SMartin Schwidefsky #include <asm/nmi.h> 17*b5f87f15SMartin Schwidefsky #include <asm/smp.h> 18*b5f87f15SMartin Schwidefsky #include "entry.h" 19*b5f87f15SMartin Schwidefsky 20*b5f87f15SMartin Schwidefsky static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); 21*b5f87f15SMartin Schwidefsky 22*b5f87f15SMartin Schwidefsky void __kprobes enabled_wait(void) 23*b5f87f15SMartin Schwidefsky { 24*b5f87f15SMartin Schwidefsky struct s390_idle_data *idle = &__get_cpu_var(s390_idle); 25*b5f87f15SMartin Schwidefsky unsigned long long idle_time; 26*b5f87f15SMartin Schwidefsky unsigned long psw_mask; 27*b5f87f15SMartin Schwidefsky 28*b5f87f15SMartin Schwidefsky trace_hardirqs_on(); 29*b5f87f15SMartin Schwidefsky 30*b5f87f15SMartin Schwidefsky /* Wait for external, I/O or machine check interrupt. */ 31*b5f87f15SMartin Schwidefsky psw_mask = PSW_KERNEL_BITS | PSW_MASK_WAIT | PSW_MASK_DAT | 32*b5f87f15SMartin Schwidefsky PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; 33*b5f87f15SMartin Schwidefsky clear_cpu_flag(CIF_NOHZ_DELAY); 34*b5f87f15SMartin Schwidefsky 35*b5f87f15SMartin Schwidefsky /* Call the assembler magic in entry.S */ 36*b5f87f15SMartin Schwidefsky psw_idle(idle, psw_mask); 37*b5f87f15SMartin Schwidefsky 38*b5f87f15SMartin Schwidefsky /* Account time spent with enabled wait psw loaded as idle time. */ 39*b5f87f15SMartin Schwidefsky idle->sequence++; 40*b5f87f15SMartin Schwidefsky smp_wmb(); 41*b5f87f15SMartin Schwidefsky idle_time = idle->clock_idle_exit - idle->clock_idle_enter; 42*b5f87f15SMartin Schwidefsky idle->clock_idle_enter = idle->clock_idle_exit = 0ULL; 43*b5f87f15SMartin Schwidefsky idle->idle_time += idle_time; 44*b5f87f15SMartin Schwidefsky idle->idle_count++; 45*b5f87f15SMartin Schwidefsky account_idle_time(idle_time); 46*b5f87f15SMartin Schwidefsky smp_wmb(); 47*b5f87f15SMartin Schwidefsky idle->sequence++; 48*b5f87f15SMartin Schwidefsky } 49*b5f87f15SMartin Schwidefsky 50*b5f87f15SMartin Schwidefsky static ssize_t show_idle_count(struct device *dev, 51*b5f87f15SMartin Schwidefsky struct device_attribute *attr, char *buf) 52*b5f87f15SMartin Schwidefsky { 53*b5f87f15SMartin Schwidefsky struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); 54*b5f87f15SMartin Schwidefsky unsigned long long idle_count; 55*b5f87f15SMartin Schwidefsky unsigned int sequence; 56*b5f87f15SMartin Schwidefsky 57*b5f87f15SMartin Schwidefsky do { 58*b5f87f15SMartin Schwidefsky sequence = ACCESS_ONCE(idle->sequence); 59*b5f87f15SMartin Schwidefsky idle_count = ACCESS_ONCE(idle->idle_count); 60*b5f87f15SMartin Schwidefsky if (ACCESS_ONCE(idle->clock_idle_enter)) 61*b5f87f15SMartin Schwidefsky idle_count++; 62*b5f87f15SMartin Schwidefsky } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); 63*b5f87f15SMartin Schwidefsky return sprintf(buf, "%llu\n", idle_count); 64*b5f87f15SMartin Schwidefsky } 65*b5f87f15SMartin Schwidefsky DEVICE_ATTR(idle_count, 0444, show_idle_count, NULL); 66*b5f87f15SMartin Schwidefsky 67*b5f87f15SMartin Schwidefsky static ssize_t show_idle_time(struct device *dev, 68*b5f87f15SMartin Schwidefsky struct device_attribute *attr, char *buf) 69*b5f87f15SMartin Schwidefsky { 70*b5f87f15SMartin Schwidefsky struct s390_idle_data *idle = &per_cpu(s390_idle, dev->id); 71*b5f87f15SMartin Schwidefsky unsigned long long now, idle_time, idle_enter, idle_exit; 72*b5f87f15SMartin Schwidefsky unsigned int sequence; 73*b5f87f15SMartin Schwidefsky 74*b5f87f15SMartin Schwidefsky do { 75*b5f87f15SMartin Schwidefsky now = get_tod_clock(); 76*b5f87f15SMartin Schwidefsky sequence = ACCESS_ONCE(idle->sequence); 77*b5f87f15SMartin Schwidefsky idle_time = ACCESS_ONCE(idle->idle_time); 78*b5f87f15SMartin Schwidefsky idle_enter = ACCESS_ONCE(idle->clock_idle_enter); 79*b5f87f15SMartin Schwidefsky idle_exit = ACCESS_ONCE(idle->clock_idle_exit); 80*b5f87f15SMartin Schwidefsky } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); 81*b5f87f15SMartin Schwidefsky idle_time += idle_enter ? ((idle_exit ? : now) - idle_enter) : 0; 82*b5f87f15SMartin Schwidefsky return sprintf(buf, "%llu\n", idle_time >> 12); 83*b5f87f15SMartin Schwidefsky } 84*b5f87f15SMartin Schwidefsky DEVICE_ATTR(idle_time_us, 0444, show_idle_time, NULL); 85*b5f87f15SMartin Schwidefsky 86*b5f87f15SMartin Schwidefsky cputime64_t arch_cpu_idle_time(int cpu) 87*b5f87f15SMartin Schwidefsky { 88*b5f87f15SMartin Schwidefsky struct s390_idle_data *idle = &per_cpu(s390_idle, cpu); 89*b5f87f15SMartin Schwidefsky unsigned long long now, idle_enter, idle_exit; 90*b5f87f15SMartin Schwidefsky unsigned int sequence; 91*b5f87f15SMartin Schwidefsky 92*b5f87f15SMartin Schwidefsky do { 93*b5f87f15SMartin Schwidefsky now = get_tod_clock(); 94*b5f87f15SMartin Schwidefsky sequence = ACCESS_ONCE(idle->sequence); 95*b5f87f15SMartin Schwidefsky idle_enter = ACCESS_ONCE(idle->clock_idle_enter); 96*b5f87f15SMartin Schwidefsky idle_exit = ACCESS_ONCE(idle->clock_idle_exit); 97*b5f87f15SMartin Schwidefsky } while ((sequence & 1) || (ACCESS_ONCE(idle->sequence) != sequence)); 98*b5f87f15SMartin Schwidefsky return idle_enter ? ((idle_exit ?: now) - idle_enter) : 0; 99*b5f87f15SMartin Schwidefsky } 100*b5f87f15SMartin Schwidefsky 101*b5f87f15SMartin Schwidefsky void arch_cpu_idle_enter(void) 102*b5f87f15SMartin Schwidefsky { 103*b5f87f15SMartin Schwidefsky local_mcck_disable(); 104*b5f87f15SMartin Schwidefsky } 105*b5f87f15SMartin Schwidefsky 106*b5f87f15SMartin Schwidefsky void arch_cpu_idle(void) 107*b5f87f15SMartin Schwidefsky { 108*b5f87f15SMartin Schwidefsky if (!test_cpu_flag(CIF_MCCK_PENDING)) 109*b5f87f15SMartin Schwidefsky /* Halt the cpu and keep track of cpu time accounting. */ 110*b5f87f15SMartin Schwidefsky enabled_wait(); 111*b5f87f15SMartin Schwidefsky local_irq_enable(); 112*b5f87f15SMartin Schwidefsky } 113*b5f87f15SMartin Schwidefsky 114*b5f87f15SMartin Schwidefsky void arch_cpu_idle_exit(void) 115*b5f87f15SMartin Schwidefsky { 116*b5f87f15SMartin Schwidefsky local_mcck_enable(); 117*b5f87f15SMartin Schwidefsky if (test_cpu_flag(CIF_MCCK_PENDING)) 118*b5f87f15SMartin Schwidefsky s390_handle_mcck(); 119*b5f87f15SMartin Schwidefsky } 120*b5f87f15SMartin Schwidefsky 121*b5f87f15SMartin Schwidefsky void arch_cpu_idle_dead(void) 122*b5f87f15SMartin Schwidefsky { 123*b5f87f15SMartin Schwidefsky cpu_die(); 124*b5f87f15SMartin Schwidefsky } 125