1 /* delayacct.c - per-task delay accounting 2 * 3 * Copyright (C) Shailabh Nagar, IBM Corp. 2006 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it would be useful, but 11 * WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 13 * the GNU General Public License for more details. 14 */ 15 16 #include <linux/sched.h> 17 #include <linux/slab.h> 18 #include <linux/time.h> 19 #include <linux/sysctl.h> 20 #include <linux/delayacct.h> 21 22 int delayacct_on __read_mostly = 1; /* Delay accounting turned on/off */ 23 kmem_cache_t *delayacct_cache; 24 25 static int __init delayacct_setup_disable(char *str) 26 { 27 delayacct_on = 0; 28 return 1; 29 } 30 __setup("nodelayacct", delayacct_setup_disable); 31 32 void delayacct_init(void) 33 { 34 delayacct_cache = kmem_cache_create("delayacct_cache", 35 sizeof(struct task_delay_info), 36 0, 37 SLAB_PANIC, 38 NULL, NULL); 39 delayacct_tsk_init(&init_task); 40 } 41 42 void __delayacct_tsk_init(struct task_struct *tsk) 43 { 44 tsk->delays = kmem_cache_zalloc(delayacct_cache, SLAB_KERNEL); 45 if (tsk->delays) 46 spin_lock_init(&tsk->delays->lock); 47 } 48 49 /* 50 * Start accounting for a delay statistic using 51 * its starting timestamp (@start) 52 */ 53 54 static inline void delayacct_start(struct timespec *start) 55 { 56 do_posix_clock_monotonic_gettime(start); 57 } 58 59 /* 60 * Finish delay accounting for a statistic using 61 * its timestamps (@start, @end), accumalator (@total) and @count 62 */ 63 64 static void delayacct_end(struct timespec *start, struct timespec *end, 65 u64 *total, u32 *count) 66 { 67 struct timespec ts; 68 s64 ns; 69 70 do_posix_clock_monotonic_gettime(end); 71 ts = timespec_sub(*end, *start); 72 ns = timespec_to_ns(&ts); 73 if (ns < 0) 74 return; 75 76 spin_lock(¤t->delays->lock); 77 *total += ns; 78 (*count)++; 79 spin_unlock(¤t->delays->lock); 80 } 81 82 void __delayacct_blkio_start(void) 83 { 84 delayacct_start(¤t->delays->blkio_start); 85 } 86 87 void __delayacct_blkio_end(void) 88 { 89 if (current->delays->flags & DELAYACCT_PF_SWAPIN) 90 /* Swapin block I/O */ 91 delayacct_end(¤t->delays->blkio_start, 92 ¤t->delays->blkio_end, 93 ¤t->delays->swapin_delay, 94 ¤t->delays->swapin_count); 95 else /* Other block I/O */ 96 delayacct_end(¤t->delays->blkio_start, 97 ¤t->delays->blkio_end, 98 ¤t->delays->blkio_delay, 99 ¤t->delays->blkio_count); 100 } 101 102 int __delayacct_add_tsk(struct taskstats *d, struct task_struct *tsk) 103 { 104 s64 tmp; 105 struct timespec ts; 106 unsigned long t1,t2,t3; 107 108 /* Though tsk->delays accessed later, early exit avoids 109 * unnecessary returning of other data 110 */ 111 if (!tsk->delays) 112 goto done; 113 114 tmp = (s64)d->cpu_run_real_total; 115 cputime_to_timespec(tsk->utime + tsk->stime, &ts); 116 tmp += timespec_to_ns(&ts); 117 d->cpu_run_real_total = (tmp < (s64)d->cpu_run_real_total) ? 0 : tmp; 118 119 /* 120 * No locking available for sched_info (and too expensive to add one) 121 * Mitigate by taking snapshot of values 122 */ 123 t1 = tsk->sched_info.pcnt; 124 t2 = tsk->sched_info.run_delay; 125 t3 = tsk->sched_info.cpu_time; 126 127 d->cpu_count += t1; 128 129 jiffies_to_timespec(t2, &ts); 130 tmp = (s64)d->cpu_delay_total + timespec_to_ns(&ts); 131 d->cpu_delay_total = (tmp < (s64)d->cpu_delay_total) ? 0 : tmp; 132 133 tmp = (s64)d->cpu_run_virtual_total + (s64)jiffies_to_usecs(t3) * 1000; 134 d->cpu_run_virtual_total = 135 (tmp < (s64)d->cpu_run_virtual_total) ? 0 : tmp; 136 137 /* zero XXX_total, non-zero XXX_count implies XXX stat overflowed */ 138 139 spin_lock(&tsk->delays->lock); 140 tmp = d->blkio_delay_total + tsk->delays->blkio_delay; 141 d->blkio_delay_total = (tmp < d->blkio_delay_total) ? 0 : tmp; 142 tmp = d->swapin_delay_total + tsk->delays->swapin_delay; 143 d->swapin_delay_total = (tmp < d->swapin_delay_total) ? 0 : tmp; 144 d->blkio_count += tsk->delays->blkio_count; 145 d->swapin_count += tsk->delays->swapin_count; 146 spin_unlock(&tsk->delays->lock); 147 148 done: 149 return 0; 150 } 151 152 __u64 __delayacct_blkio_ticks(struct task_struct *tsk) 153 { 154 __u64 ret; 155 156 spin_lock(&tsk->delays->lock); 157 ret = nsec_to_clock_t(tsk->delays->blkio_delay + 158 tsk->delays->swapin_delay); 159 spin_unlock(&tsk->delays->lock); 160 return ret; 161 } 162 163