diff options
| author | Frederic Weisbecker <fweisbec@gmail.com> | 2012-12-16 14:00:34 -0500 |
|---|---|---|
| committer | Frederic Weisbecker <fweisbec@gmail.com> | 2013-01-27 14:35:47 -0500 |
| commit | 6a61671bb2f3a1bd12cd17b8fca811a624782632 (patch) | |
| tree | 0afc2915fb7e517472710a49a524510322dd5baa /include/linux | |
| parent | c11f11fcbdb5be790c565aed46411486a7586afc (diff) | |
cputime: Safely read cputime of full dynticks CPUs
While remotely reading the cputime of a task running in a
full dynticks CPU, the values stored in utime/stime fields
of struct task_struct may be stale. Its values may be those
of the last kernel <-> user transition time snapshot and
we need to add the tickless time spent since this snapshot.
To fix this, flush the cputime of the dynticks CPUs on
kernel <-> user transition and record the time / context
where we did this. Then on top of this snapshot and the current
time, perform the fixup on the reader side from task_times()
accessors.
Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Li Zhong <zhong@linux.vnet.ibm.com>
Cc: Namhyung Kim <namhyung.kim@lge.com>
Cc: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Steven Rostedt <rostedt@goodmis.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
[fixed kvm module related build errors]
Signed-off-by: Sedat Dilek <sedat.dilek@gmail.com>
Diffstat (limited to 'include/linux')
| -rw-r--r-- | include/linux/hardirq.h | 4 | ||||
| -rw-r--r-- | include/linux/init_task.h | 11 | ||||
| -rw-r--r-- | include/linux/kvm_host.h | 20 | ||||
| -rw-r--r-- | include/linux/sched.h | 27 | ||||
| -rw-r--r-- | include/linux/vtime.h | 47 |
5 files changed, 76 insertions, 33 deletions
diff --git a/include/linux/hardirq.h b/include/linux/hardirq.h index 624ef3f45c8e..7105d5cbb762 100644 --- a/include/linux/hardirq.h +++ b/include/linux/hardirq.h | |||
| @@ -153,7 +153,7 @@ extern void rcu_nmi_exit(void); | |||
| 153 | */ | 153 | */ |
| 154 | #define __irq_enter() \ | 154 | #define __irq_enter() \ |
| 155 | do { \ | 155 | do { \ |
| 156 | vtime_account_irq_enter(current); \ | 156 | account_irq_enter_time(current); \ |
| 157 | add_preempt_count(HARDIRQ_OFFSET); \ | 157 | add_preempt_count(HARDIRQ_OFFSET); \ |
| 158 | trace_hardirq_enter(); \ | 158 | trace_hardirq_enter(); \ |
| 159 | } while (0) | 159 | } while (0) |
| @@ -169,7 +169,7 @@ extern void irq_enter(void); | |||
| 169 | #define __irq_exit() \ | 169 | #define __irq_exit() \ |
| 170 | do { \ | 170 | do { \ |
| 171 | trace_hardirq_exit(); \ | 171 | trace_hardirq_exit(); \ |
| 172 | vtime_account_irq_exit(current); \ | 172 | account_irq_exit_time(current); \ |
| 173 | sub_preempt_count(HARDIRQ_OFFSET); \ | 173 | sub_preempt_count(HARDIRQ_OFFSET); \ |
| 174 | } while (0) | 174 | } while (0) |
| 175 | 175 | ||
diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 6d087c5f57f7..cc898b871cef 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include <linux/pid_namespace.h> | 10 | #include <linux/pid_namespace.h> |
| 11 | #include <linux/user_namespace.h> | 11 | #include <linux/user_namespace.h> |
| 12 | #include <linux/securebits.h> | 12 | #include <linux/securebits.h> |
| 13 | #include <linux/seqlock.h> | ||
| 13 | #include <net/net_namespace.h> | 14 | #include <net/net_namespace.h> |
| 14 | 15 | ||
| 15 | #ifdef CONFIG_SMP | 16 | #ifdef CONFIG_SMP |
| @@ -141,6 +142,15 @@ extern struct task_group root_task_group; | |||
| 141 | # define INIT_PERF_EVENTS(tsk) | 142 | # define INIT_PERF_EVENTS(tsk) |
| 142 | #endif | 143 | #endif |
| 143 | 144 | ||
| 145 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN | ||
| 146 | # define INIT_VTIME(tsk) \ | ||
| 147 | .vtime_seqlock = __SEQLOCK_UNLOCKED(tsk.vtime_seqlock), \ | ||
| 148 | .vtime_snap = 0, \ | ||
| 149 | .vtime_snap_whence = VTIME_SYS, | ||
| 150 | #else | ||
| 151 | # define INIT_VTIME(tsk) | ||
| 152 | #endif | ||
| 153 | |||
| 144 | #define INIT_TASK_COMM "swapper" | 154 | #define INIT_TASK_COMM "swapper" |
| 145 | 155 | ||
| 146 | /* | 156 | /* |
| @@ -210,6 +220,7 @@ extern struct task_group root_task_group; | |||
| 210 | INIT_TRACE_RECURSION \ | 220 | INIT_TRACE_RECURSION \ |
| 211 | INIT_TASK_RCU_PREEMPT(tsk) \ | 221 | INIT_TASK_RCU_PREEMPT(tsk) \ |
| 212 | INIT_CPUSET_SEQ \ | 222 | INIT_CPUSET_SEQ \ |
| 223 | INIT_VTIME(tsk) \ | ||
| 213 | } | 224 | } |
| 214 | 225 | ||
| 215 | 226 | ||
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 4fe2396401da..b7996a768eb2 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h | |||
| @@ -741,7 +741,7 @@ static inline int kvm_deassign_device(struct kvm *kvm, | |||
| 741 | } | 741 | } |
| 742 | #endif /* CONFIG_IOMMU_API */ | 742 | #endif /* CONFIG_IOMMU_API */ |
| 743 | 743 | ||
| 744 | static inline void guest_enter(void) | 744 | static inline void __guest_enter(void) |
| 745 | { | 745 | { |
| 746 | /* | 746 | /* |
| 747 | * This is running in ioctl context so we can avoid | 747 | * This is running in ioctl context so we can avoid |
| @@ -751,7 +751,7 @@ static inline void guest_enter(void) | |||
| 751 | current->flags |= PF_VCPU; | 751 | current->flags |= PF_VCPU; |
| 752 | } | 752 | } |
| 753 | 753 | ||
| 754 | static inline void guest_exit(void) | 754 | static inline void __guest_exit(void) |
| 755 | { | 755 | { |
| 756 | /* | 756 | /* |
| 757 | * This is running in ioctl context so we can avoid | 757 | * This is running in ioctl context so we can avoid |
| @@ -761,6 +761,22 @@ static inline void guest_exit(void) | |||
| 761 | current->flags &= ~PF_VCPU; | 761 | current->flags &= ~PF_VCPU; |
| 762 | } | 762 | } |
| 763 | 763 | ||
| 764 | #ifdef CONFIG_CONTEXT_TRACKING | ||
| 765 | extern void guest_enter(void); | ||
| 766 | extern void guest_exit(void); | ||
| 767 | |||
| 768 | #else /* !CONFIG_CONTEXT_TRACKING */ | ||
| 769 | static inline void guest_enter(void) | ||
| 770 | { | ||
| 771 | __guest_enter(); | ||
| 772 | } | ||
| 773 | |||
| 774 | static inline void guest_exit(void) | ||
| 775 | { | ||
| 776 | __guest_exit(); | ||
| 777 | } | ||
| 778 | #endif /* !CONFIG_CONTEXT_TRACKING */ | ||
| 779 | |||
| 764 | static inline void kvm_guest_enter(void) | 780 | static inline void kvm_guest_enter(void) |
| 765 | { | 781 | { |
| 766 | unsigned long flags; | 782 | unsigned long flags; |
diff --git a/include/linux/sched.h b/include/linux/sched.h index a9c608b6154e..a9fa5145e1a7 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h | |||
| @@ -1368,6 +1368,15 @@ struct task_struct { | |||
| 1368 | #ifndef CONFIG_VIRT_CPU_ACCOUNTING | 1368 | #ifndef CONFIG_VIRT_CPU_ACCOUNTING |
| 1369 | struct cputime prev_cputime; | 1369 | struct cputime prev_cputime; |
| 1370 | #endif | 1370 | #endif |
| 1371 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN | ||
| 1372 | seqlock_t vtime_seqlock; | ||
| 1373 | unsigned long long vtime_snap; | ||
| 1374 | enum { | ||
| 1375 | VTIME_SLEEPING = 0, | ||
| 1376 | VTIME_USER, | ||
| 1377 | VTIME_SYS, | ||
| 1378 | } vtime_snap_whence; | ||
| 1379 | #endif | ||
| 1371 | unsigned long nvcsw, nivcsw; /* context switch counts */ | 1380 | unsigned long nvcsw, nivcsw; /* context switch counts */ |
| 1372 | struct timespec start_time; /* monotonic time */ | 1381 | struct timespec start_time; /* monotonic time */ |
| 1373 | struct timespec real_start_time; /* boot based time */ | 1382 | struct timespec real_start_time; /* boot based time */ |
| @@ -1792,11 +1801,13 @@ static inline void put_task_struct(struct task_struct *t) | |||
| 1792 | __put_task_struct(t); | 1801 | __put_task_struct(t); |
| 1793 | } | 1802 | } |
| 1794 | 1803 | ||
| 1795 | static inline cputime_t task_gtime(struct task_struct *t) | 1804 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN |
| 1796 | { | 1805 | extern void task_cputime(struct task_struct *t, |
| 1797 | return t->gtime; | 1806 | cputime_t *utime, cputime_t *stime); |
| 1798 | } | 1807 | extern void task_cputime_scaled(struct task_struct *t, |
| 1799 | 1808 | cputime_t *utimescaled, cputime_t *stimescaled); | |
| 1809 | extern cputime_t task_gtime(struct task_struct *t); | ||
| 1810 | #else | ||
| 1800 | static inline void task_cputime(struct task_struct *t, | 1811 | static inline void task_cputime(struct task_struct *t, |
| 1801 | cputime_t *utime, cputime_t *stime) | 1812 | cputime_t *utime, cputime_t *stime) |
| 1802 | { | 1813 | { |
| @@ -1815,6 +1826,12 @@ static inline void task_cputime_scaled(struct task_struct *t, | |||
| 1815 | if (stimescaled) | 1826 | if (stimescaled) |
| 1816 | *stimescaled = t->stimescaled; | 1827 | *stimescaled = t->stimescaled; |
| 1817 | } | 1828 | } |
| 1829 | |||
| 1830 | static inline cputime_t task_gtime(struct task_struct *t) | ||
| 1831 | { | ||
| 1832 | return t->gtime; | ||
| 1833 | } | ||
| 1834 | #endif | ||
| 1818 | extern void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); | 1835 | extern void task_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); |
| 1819 | extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); | 1836 | extern void thread_group_cputime_adjusted(struct task_struct *p, cputime_t *ut, cputime_t *st); |
| 1820 | 1837 | ||
diff --git a/include/linux/vtime.h b/include/linux/vtime.h index bb50c3ca0d79..71a5782d8c59 100644 --- a/include/linux/vtime.h +++ b/include/linux/vtime.h | |||
| @@ -8,35 +8,44 @@ extern void vtime_task_switch(struct task_struct *prev); | |||
| 8 | extern void vtime_account_system(struct task_struct *tsk); | 8 | extern void vtime_account_system(struct task_struct *tsk); |
| 9 | extern void vtime_account_idle(struct task_struct *tsk); | 9 | extern void vtime_account_idle(struct task_struct *tsk); |
| 10 | extern void vtime_account_user(struct task_struct *tsk); | 10 | extern void vtime_account_user(struct task_struct *tsk); |
| 11 | extern void vtime_account(struct task_struct *tsk); | 11 | extern void vtime_account_irq_enter(struct task_struct *tsk); |
| 12 | 12 | ||
| 13 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN | 13 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_NATIVE |
| 14 | extern bool vtime_accounting_enabled(void); | ||
| 15 | #else | ||
| 16 | static inline bool vtime_accounting_enabled(void) { return true; } | 14 | static inline bool vtime_accounting_enabled(void) { return true; } |
| 17 | #endif | 15 | #endif |
| 18 | 16 | ||
| 19 | #else /* !CONFIG_VIRT_CPU_ACCOUNTING */ | 17 | #else /* !CONFIG_VIRT_CPU_ACCOUNTING */ |
| 18 | |||
| 20 | static inline void vtime_task_switch(struct task_struct *prev) { } | 19 | static inline void vtime_task_switch(struct task_struct *prev) { } |
| 21 | static inline void vtime_account_system(struct task_struct *tsk) { } | 20 | static inline void vtime_account_system(struct task_struct *tsk) { } |
| 22 | static inline void vtime_account_user(struct task_struct *tsk) { } | 21 | static inline void vtime_account_user(struct task_struct *tsk) { } |
| 23 | static inline void vtime_account(struct task_struct *tsk) { } | 22 | static inline void vtime_account_irq_enter(struct task_struct *tsk) { } |
| 24 | static inline bool vtime_accounting_enabled(void) { return false; } | 23 | static inline bool vtime_accounting_enabled(void) { return false; } |
| 25 | #endif | 24 | #endif |
| 26 | 25 | ||
| 27 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN | 26 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING_GEN |
| 28 | static inline void arch_vtime_task_switch(struct task_struct *tsk) { } | 27 | extern void arch_vtime_task_switch(struct task_struct *tsk); |
| 29 | static inline void vtime_user_enter(struct task_struct *tsk) | 28 | extern void vtime_account_irq_exit(struct task_struct *tsk); |
| 30 | { | 29 | extern bool vtime_accounting_enabled(void); |
| 31 | vtime_account_system(tsk); | 30 | extern void vtime_user_enter(struct task_struct *tsk); |
| 32 | } | ||
| 33 | static inline void vtime_user_exit(struct task_struct *tsk) | 31 | static inline void vtime_user_exit(struct task_struct *tsk) |
| 34 | { | 32 | { |
| 35 | vtime_account_user(tsk); | 33 | vtime_account_user(tsk); |
| 36 | } | 34 | } |
| 35 | extern void vtime_guest_enter(struct task_struct *tsk); | ||
| 36 | extern void vtime_guest_exit(struct task_struct *tsk); | ||
| 37 | extern void vtime_init_idle(struct task_struct *tsk); | ||
| 37 | #else | 38 | #else |
| 39 | static inline void vtime_account_irq_exit(struct task_struct *tsk) | ||
| 40 | { | ||
| 41 | /* On hard|softirq exit we always account to hard|softirq cputime */ | ||
| 42 | vtime_account_system(tsk); | ||
| 43 | } | ||
| 38 | static inline void vtime_user_enter(struct task_struct *tsk) { } | 44 | static inline void vtime_user_enter(struct task_struct *tsk) { } |
| 39 | static inline void vtime_user_exit(struct task_struct *tsk) { } | 45 | static inline void vtime_user_exit(struct task_struct *tsk) { } |
| 46 | static inline void vtime_guest_enter(struct task_struct *tsk) { } | ||
| 47 | static inline void vtime_guest_exit(struct task_struct *tsk) { } | ||
| 48 | static inline void vtime_init_idle(struct task_struct *tsk) { } | ||
| 40 | #endif | 49 | #endif |
| 41 | 50 | ||
| 42 | #ifdef CONFIG_IRQ_TIME_ACCOUNTING | 51 | #ifdef CONFIG_IRQ_TIME_ACCOUNTING |
| @@ -45,25 +54,15 @@ extern void irqtime_account_irq(struct task_struct *tsk); | |||
| 45 | static inline void irqtime_account_irq(struct task_struct *tsk) { } | 54 | static inline void irqtime_account_irq(struct task_struct *tsk) { } |
| 46 | #endif | 55 | #endif |
| 47 | 56 | ||
| 48 | static inline void vtime_account_irq_enter(struct task_struct *tsk) | 57 | static inline void account_irq_enter_time(struct task_struct *tsk) |
| 49 | { | 58 | { |
| 50 | /* | 59 | vtime_account_irq_enter(tsk); |
| 51 | * Hardirq can interrupt idle task anytime. So we need vtime_account() | ||
| 52 | * that performs the idle check in CONFIG_VIRT_CPU_ACCOUNTING. | ||
| 53 | * Softirq can also interrupt idle task directly if it calls | ||
| 54 | * local_bh_enable(). Such case probably don't exist but we never know. | ||
| 55 | * Ksoftirqd is not concerned because idle time is flushed on context | ||
| 56 | * switch. Softirqs in the end of hardirqs are also not a problem because | ||
| 57 | * the idle time is flushed on hardirq time already. | ||
| 58 | */ | ||
| 59 | vtime_account(tsk); | ||
| 60 | irqtime_account_irq(tsk); | 60 | irqtime_account_irq(tsk); |
| 61 | } | 61 | } |
| 62 | 62 | ||
| 63 | static inline void vtime_account_irq_exit(struct task_struct *tsk) | 63 | static inline void account_irq_exit_time(struct task_struct *tsk) |
| 64 | { | 64 | { |
| 65 | /* On hard|softirq exit we always account to hard|softirq cputime */ | 65 | vtime_account_irq_exit(tsk); |
| 66 | vtime_account_system(tsk); | ||
| 67 | irqtime_account_irq(tsk); | 66 | irqtime_account_irq(tsk); |
| 68 | } | 67 | } |
| 69 | 68 | ||
