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 | |
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')
-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 | ||