2019-05-19 16:51:55 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2006-07-14 11:24:36 +04:00
/* delayacct.c - per-task delay accounting
*
* Copyright ( C ) Shailabh Nagar , IBM Corp . 2006
*/
# include <linux/sched.h>
2017-02-04 03:20:53 +03:00
# include <linux/sched/task.h>
2017-02-05 13:48:36 +03:00
# include <linux/sched/cputime.h>
2021-05-04 23:43:48 +03:00
# include <linux/sched/clock.h>
2006-07-14 11:24:36 +04:00
# include <linux/slab.h>
2009-09-18 23:55:55 +04:00
# include <linux/taskstats.h>
2006-07-14 11:24:36 +04:00
# include <linux/sysctl.h>
# include <linux/delayacct.h>
2011-07-11 23:28:14 +04:00
# include <linux/module.h>
2006-07-14 11:24:36 +04:00
2021-05-04 23:43:32 +03:00
DEFINE_STATIC_KEY_FALSE ( delayacct_key ) ;
int delayacct_on __read_mostly ; /* Delay accounting turned on/off */
2006-12-07 07:33:20 +03:00
struct kmem_cache * delayacct_cache ;
2006-07-14 11:24:36 +04:00
2021-05-10 15:01:00 +03:00
static void set_delayacct ( bool enabled )
{
if ( enabled ) {
static_branch_enable ( & delayacct_key ) ;
delayacct_on = 1 ;
} else {
delayacct_on = 0 ;
static_branch_disable ( & delayacct_key ) ;
}
}
2021-05-04 23:43:32 +03:00
static int __init delayacct_setup_enable ( char * str )
2006-07-14 11:24:36 +04:00
{
2021-05-04 23:43:32 +03:00
delayacct_on = 1 ;
2006-07-14 11:24:36 +04:00
return 1 ;
}
2021-05-04 23:43:32 +03:00
__setup ( " delayacct " , delayacct_setup_enable ) ;
2006-07-14 11:24:36 +04:00
void delayacct_init ( void )
{
2016-01-15 02:18:21 +03:00
delayacct_cache = KMEM_CACHE ( task_delay_info , SLAB_PANIC | SLAB_ACCOUNT ) ;
2006-07-14 11:24:36 +04:00
delayacct_tsk_init ( & init_task ) ;
2021-05-10 15:01:00 +03:00
set_delayacct ( delayacct_on ) ;
}
# ifdef CONFIG_PROC_SYSCTL
2022-02-18 13:59:36 +03:00
static int sysctl_delayacct ( struct ctl_table * table , int write , void * buffer ,
2021-05-10 15:01:00 +03:00
size_t * lenp , loff_t * ppos )
{
int state = delayacct_on ;
struct ctl_table t ;
int err ;
if ( write & & ! capable ( CAP_SYS_ADMIN ) )
return - EPERM ;
t = * table ;
t . data = & state ;
err = proc_dointvec_minmax ( & t , write , buffer , lenp , ppos ) ;
if ( err < 0 )
return err ;
if ( write )
set_delayacct ( state ) ;
return err ;
2006-07-14 11:24:36 +04:00
}
2022-02-18 13:59:36 +03:00
static struct ctl_table kern_delayacct_table [ ] = {
{
. procname = " task_delayacct " ,
. data = NULL ,
. maxlen = sizeof ( unsigned int ) ,
. mode = 0644 ,
. proc_handler = sysctl_delayacct ,
. extra1 = SYSCTL_ZERO ,
. extra2 = SYSCTL_ONE ,
} ,
{ }
} ;
static __init int kernel_delayacct_sysctls_init ( void )
{
register_sysctl_init ( " kernel " , kern_delayacct_table ) ;
return 0 ;
}
late_initcall ( kernel_delayacct_sysctls_init ) ;
2021-05-10 15:01:00 +03:00
# endif
2006-07-14 11:24:36 +04:00
void __delayacct_tsk_init ( struct task_struct * tsk )
{
2006-12-07 07:33:17 +03:00
tsk - > delays = kmem_cache_zalloc ( delayacct_cache , GFP_KERNEL ) ;
2006-07-14 11:24:36 +04:00
if ( tsk - > delays )
2018-04-23 19:10:23 +03:00
raw_spin_lock_init ( & tsk - > delays - > lock ) ;
2006-07-14 11:24:36 +04:00
}
/*
2014-07-17 01:04:35 +04:00
* Finish delay accounting for a statistic using its timestamps ( @ start ) ,
* accumalator ( @ total ) and @ count
2006-07-14 11:24:36 +04:00
*/
2021-05-04 23:43:48 +03:00
static void delayacct_end ( raw_spinlock_t * lock , u64 * start , u64 * total , u32 * count )
2006-07-14 11:24:36 +04:00
{
2021-05-04 23:43:48 +03:00
s64 ns = local_clock ( ) - * start ;
2006-11-06 10:52:10 +03:00
unsigned long flags ;
2006-07-14 11:24:36 +04:00
2014-07-17 01:04:35 +04:00
if ( ns > 0 ) {
2018-04-23 19:10:23 +03:00
raw_spin_lock_irqsave ( lock , flags ) ;
2014-07-17 01:04:35 +04:00
* total + = ns ;
( * count ) + + ;
2018-04-23 19:10:23 +03:00
raw_spin_unlock_irqrestore ( lock , flags ) ;
2014-07-17 01:04:35 +04:00
}
2006-07-14 11:24:36 +04:00
}
2006-07-14 11:24:37 +04:00
void __delayacct_blkio_start ( void )
{
2021-05-04 23:43:48 +03:00
current - > delays - > blkio_start = local_clock ( ) ;
2006-07-14 11:24:37 +04:00
}
2017-12-18 19:15:10 +03:00
/*
* We cannot rely on the ` current ` macro , as we haven ' t yet switched back to
* the process being woken .
*/
void __delayacct_blkio_end ( struct task_struct * p )
2006-07-14 11:24:37 +04:00
{
2022-01-20 05:10:02 +03:00
delayacct_end ( & p - > delays - > lock ,
& p - > delays - > blkio_start ,
& p - > delays - > blkio_delay ,
& p - > delays - > blkio_count ) ;
2006-07-14 11:24:37 +04:00
}
2006-07-14 11:24:41 +04:00
2021-05-04 23:43:32 +03:00
int delayacct_add_tsk ( struct taskstats * d , struct task_struct * tsk )
2006-07-14 11:24:41 +04:00
{
2017-01-31 06:09:29 +03:00
u64 utime , stime , stimescaled , utimescaled ;
2014-07-17 01:04:37 +04:00
unsigned long long t2 , t3 ;
unsigned long flags , t1 ;
s64 tmp ;
2006-07-14 11:24:41 +04:00
2017-01-31 06:09:29 +03:00
task_cputime ( tsk , & utime , & stime ) ;
2014-07-17 01:04:37 +04:00
tmp = ( s64 ) d - > cpu_run_real_total ;
2017-01-31 06:09:29 +03:00
tmp + = utime + stime ;
2006-07-14 11:24:41 +04:00
d - > cpu_run_real_total = ( tmp < ( s64 ) d - > cpu_run_real_total ) ? 0 : tmp ;
2017-01-31 06:09:29 +03:00
task_cputime_scaled ( tsk , & utimescaled , & stimescaled ) ;
2014-07-17 01:04:37 +04:00
tmp = ( s64 ) d - > cpu_scaled_run_real_total ;
2017-01-31 06:09:29 +03:00
tmp + = utimescaled + stimescaled ;
2007-10-18 14:06:34 +04:00
d - > cpu_scaled_run_real_total =
( tmp < ( s64 ) d - > cpu_scaled_run_real_total ) ? 0 : tmp ;
2006-07-14 11:24:41 +04:00
/*
* No locking available for sched_info ( and too expensive to add one )
* Mitigate by taking snapshot of values
*/
2007-10-15 19:00:12 +04:00
t1 = tsk - > sched_info . pcount ;
2006-07-14 11:24:41 +04:00
t2 = tsk - > sched_info . run_delay ;
2008-12-17 10:41:22 +03:00
t3 = tsk - > se . sum_exec_runtime ;
2006-07-14 11:24:41 +04:00
d - > cpu_count + = t1 ;
2007-07-09 20:52:00 +04:00
tmp = ( s64 ) d - > cpu_delay_total + t2 ;
2006-07-14 11:24:41 +04:00
d - > cpu_delay_total = ( tmp < ( s64 ) d - > cpu_delay_total ) ? 0 : tmp ;
2007-07-09 20:52:00 +04:00
tmp = ( s64 ) d - > cpu_run_virtual_total + t3 ;
2006-07-14 11:24:41 +04:00
d - > cpu_run_virtual_total =
( tmp < ( s64 ) d - > cpu_run_virtual_total ) ? 0 : tmp ;
2021-05-04 23:43:32 +03:00
if ( ! tsk - > delays )
return 0 ;
2006-07-14 11:24:41 +04:00
/* zero XXX_total, non-zero XXX_count implies XXX stat overflowed */
2018-04-23 19:10:23 +03:00
raw_spin_lock_irqsave ( & tsk - > delays - > lock , flags ) ;
2006-07-14 11:24:41 +04:00
tmp = d - > blkio_delay_total + tsk - > delays - > blkio_delay ;
d - > blkio_delay_total = ( tmp < d - > blkio_delay_total ) ? 0 : tmp ;
tmp = d - > swapin_delay_total + tsk - > delays - > swapin_delay ;
d - > swapin_delay_total = ( tmp < d - > swapin_delay_total ) ? 0 : tmp ;
2008-07-25 12:48:53 +04:00
tmp = d - > freepages_delay_total + tsk - > delays - > freepages_delay ;
d - > freepages_delay_total = ( tmp < d - > freepages_delay_total ) ? 0 : tmp ;
2018-10-27 01:06:08 +03:00
tmp = d - > thrashing_delay_total + tsk - > delays - > thrashing_delay ;
d - > thrashing_delay_total = ( tmp < d - > thrashing_delay_total ) ? 0 : tmp ;
2022-01-20 05:10:15 +03:00
tmp = d - > compact_delay_total + tsk - > delays - > compact_delay ;
d - > compact_delay_total = ( tmp < d - > compact_delay_total ) ? 0 : tmp ;
2022-06-02 01:55:25 +03:00
tmp = d - > wpcopy_delay_total + tsk - > delays - > wpcopy_delay ;
d - > wpcopy_delay_total = ( tmp < d - > wpcopy_delay_total ) ? 0 : tmp ;
2023-04-08 12:28:35 +03:00
tmp = d - > irq_delay_total + tsk - > delays - > irq_delay ;
d - > irq_delay_total = ( tmp < d - > irq_delay_total ) ? 0 : tmp ;
2006-07-14 11:24:41 +04:00
d - > blkio_count + = tsk - > delays - > blkio_count ;
d - > swapin_count + = tsk - > delays - > swapin_count ;
2008-07-25 12:48:53 +04:00
d - > freepages_count + = tsk - > delays - > freepages_count ;
2018-10-27 01:06:08 +03:00
d - > thrashing_count + = tsk - > delays - > thrashing_count ;
2022-01-20 05:10:15 +03:00
d - > compact_count + = tsk - > delays - > compact_count ;
2022-06-02 01:55:25 +03:00
d - > wpcopy_count + = tsk - > delays - > wpcopy_count ;
2023-04-08 12:28:35 +03:00
d - > irq_count + = tsk - > delays - > irq_count ;
2018-04-23 19:10:23 +03:00
raw_spin_unlock_irqrestore ( & tsk - > delays - > lock , flags ) ;
2006-07-14 11:24:41 +04:00
return 0 ;
}
2006-07-14 11:24:43 +04:00
__u64 __delayacct_blkio_ticks ( struct task_struct * tsk )
{
__u64 ret ;
2006-11-06 10:52:10 +03:00
unsigned long flags ;
2006-07-14 11:24:43 +04:00
2018-04-23 19:10:23 +03:00
raw_spin_lock_irqsave ( & tsk - > delays - > lock , flags ) ;
2022-01-20 05:10:02 +03:00
ret = nsec_to_clock_t ( tsk - > delays - > blkio_delay ) ;
2018-04-23 19:10:23 +03:00
raw_spin_unlock_irqrestore ( & tsk - > delays - > lock , flags ) ;
2006-07-14 11:24:43 +04:00
return ret ;
}
2008-07-25 12:48:52 +04:00
void __delayacct_freepages_start ( void )
{
2021-05-04 23:43:48 +03:00
current - > delays - > freepages_start = local_clock ( ) ;
2008-07-25 12:48:52 +04:00
}
void __delayacct_freepages_end ( void )
{
2021-05-04 23:43:48 +03:00
delayacct_end ( & current - > delays - > lock ,
& current - > delays - > freepages_start ,
& current - > delays - > freepages_delay ,
& current - > delays - > freepages_count ) ;
2008-07-25 12:48:52 +04:00
}
2022-08-15 10:11:35 +03:00
void __delayacct_thrashing_start ( bool * in_thrashing )
2018-10-27 01:06:08 +03:00
{
2022-08-15 10:11:35 +03:00
* in_thrashing = ! ! current - > in_thrashing ;
if ( * in_thrashing )
return ;
current - > in_thrashing = 1 ;
2021-05-04 23:43:48 +03:00
current - > delays - > thrashing_start = local_clock ( ) ;
2018-10-27 01:06:08 +03:00
}
2022-08-15 10:11:35 +03:00
void __delayacct_thrashing_end ( bool * in_thrashing )
2018-10-27 01:06:08 +03:00
{
2022-08-15 10:11:35 +03:00
if ( * in_thrashing )
return ;
current - > in_thrashing = 0 ;
2018-10-27 01:06:08 +03:00
delayacct_end ( & current - > delays - > lock ,
& current - > delays - > thrashing_start ,
& current - > delays - > thrashing_delay ,
& current - > delays - > thrashing_count ) ;
}
2022-01-20 05:10:02 +03:00
void __delayacct_swapin_start ( void )
{
current - > delays - > swapin_start = local_clock ( ) ;
}
void __delayacct_swapin_end ( void )
{
delayacct_end ( & current - > delays - > lock ,
& current - > delays - > swapin_start ,
& current - > delays - > swapin_delay ,
& current - > delays - > swapin_count ) ;
}
2022-01-20 05:10:15 +03:00
void __delayacct_compact_start ( void )
{
current - > delays - > compact_start = local_clock ( ) ;
}
void __delayacct_compact_end ( void )
{
delayacct_end ( & current - > delays - > lock ,
& current - > delays - > compact_start ,
& current - > delays - > compact_delay ,
& current - > delays - > compact_count ) ;
}
2022-06-02 01:55:25 +03:00
void __delayacct_wpcopy_start ( void )
{
current - > delays - > wpcopy_start = local_clock ( ) ;
}
void __delayacct_wpcopy_end ( void )
{
delayacct_end ( & current - > delays - > lock ,
& current - > delays - > wpcopy_start ,
& current - > delays - > wpcopy_delay ,
& current - > delays - > wpcopy_count ) ;
}
2023-04-08 12:28:35 +03:00
void __delayacct_irq ( struct task_struct * task , u32 delta )
{
unsigned long flags ;
raw_spin_lock_irqsave ( & task - > delays - > lock , flags ) ;
task - > delays - > irq_delay + = delta ;
task - > delays - > irq_count + + ;
raw_spin_unlock_irqrestore ( & task - > delays - > lock , flags ) ;
}