diff options
author | Paul E. McKenney <paul.mckenney@linaro.org> | 2011-03-29 20:48:28 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2011-05-06 02:16:56 -0400 |
commit | d71df90eadfc35aa549ff9a850842673febca71f (patch) | |
tree | dd275a3f4848784bc6625574341c11befc1b3823 /kernel | |
parent | 0ac3d136b2e3cdf1161178223bc5da14a06241d0 (diff) |
rcu: add tracing for RCU's kthread run states.
Add tracing to help debugging situations when RCU's kthreads are not
running but are supposed to be.
Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: Josh Triplett <josh@joshtriplett.org>
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/rcutree.c | 11 | ||||
-rw-r--r-- | kernel/rcutree.h | 11 | ||||
-rw-r--r-- | kernel/rcutree_plugin.h | 3 | ||||
-rw-r--r-- | kernel/rcutree_trace.c | 23 |
4 files changed, 44 insertions, 4 deletions
diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 198e4df7d83e..d8917401cbbc 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c | |||
@@ -91,8 +91,9 @@ EXPORT_SYMBOL_GPL(rcu_scheduler_active); | |||
91 | * handle all flavors of RCU. | 91 | * handle all flavors of RCU. |
92 | */ | 92 | */ |
93 | static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task); | 93 | static DEFINE_PER_CPU(struct task_struct *, rcu_cpu_kthread_task); |
94 | DEFINE_PER_CPU(unsigned int, rcu_cpu_kthread_status); | ||
94 | static DEFINE_PER_CPU(wait_queue_head_t, rcu_cpu_wq); | 95 | static DEFINE_PER_CPU(wait_queue_head_t, rcu_cpu_wq); |
95 | static DEFINE_PER_CPU(char, rcu_cpu_has_work); | 96 | DEFINE_PER_CPU(char, rcu_cpu_has_work); |
96 | static char rcu_kthreads_spawnable; | 97 | static char rcu_kthreads_spawnable; |
97 | 98 | ||
98 | static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); | 99 | static void rcu_node_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu); |
@@ -1563,11 +1564,13 @@ static int rcu_cpu_kthread(void *arg) | |||
1563 | int cpu = (int)(long)arg; | 1564 | int cpu = (int)(long)arg; |
1564 | unsigned long flags; | 1565 | unsigned long flags; |
1565 | int spincnt = 0; | 1566 | int spincnt = 0; |
1567 | unsigned int *statusp = &per_cpu(rcu_cpu_kthread_status, cpu); | ||
1566 | wait_queue_head_t *wqp = &per_cpu(rcu_cpu_wq, cpu); | 1568 | wait_queue_head_t *wqp = &per_cpu(rcu_cpu_wq, cpu); |
1567 | char work; | 1569 | char work; |
1568 | char *workp = &per_cpu(rcu_cpu_has_work, cpu); | 1570 | char *workp = &per_cpu(rcu_cpu_has_work, cpu); |
1569 | 1571 | ||
1570 | for (;;) { | 1572 | for (;;) { |
1573 | *statusp = RCU_KTHREAD_WAITING; | ||
1571 | wait_event_interruptible(*wqp, | 1574 | wait_event_interruptible(*wqp, |
1572 | *workp != 0 || kthread_should_stop()); | 1575 | *workp != 0 || kthread_should_stop()); |
1573 | local_bh_disable(); | 1576 | local_bh_disable(); |
@@ -1575,6 +1578,7 @@ static int rcu_cpu_kthread(void *arg) | |||
1575 | local_bh_enable(); | 1578 | local_bh_enable(); |
1576 | break; | 1579 | break; |
1577 | } | 1580 | } |
1581 | *statusp = RCU_KTHREAD_RUNNING; | ||
1578 | local_irq_save(flags); | 1582 | local_irq_save(flags); |
1579 | work = *workp; | 1583 | work = *workp; |
1580 | *workp = 0; | 1584 | *workp = 0; |
@@ -1587,10 +1591,12 @@ static int rcu_cpu_kthread(void *arg) | |||
1587 | else | 1591 | else |
1588 | spincnt = 0; | 1592 | spincnt = 0; |
1589 | if (spincnt > 10) { | 1593 | if (spincnt > 10) { |
1594 | *statusp = RCU_KTHREAD_YIELDING; | ||
1590 | rcu_yield(rcu_cpu_kthread_timer, (unsigned long)cpu); | 1595 | rcu_yield(rcu_cpu_kthread_timer, (unsigned long)cpu); |
1591 | spincnt = 0; | 1596 | spincnt = 0; |
1592 | } | 1597 | } |
1593 | } | 1598 | } |
1599 | *statusp = RCU_KTHREAD_STOPPED; | ||
1594 | return 0; | 1600 | return 0; |
1595 | } | 1601 | } |
1596 | 1602 | ||
@@ -1637,10 +1643,12 @@ static int rcu_node_kthread(void *arg) | |||
1637 | struct task_struct *t; | 1643 | struct task_struct *t; |
1638 | 1644 | ||
1639 | for (;;) { | 1645 | for (;;) { |
1646 | rnp->node_kthread_status = RCU_KTHREAD_WAITING; | ||
1640 | wait_event_interruptible(rnp->node_wq, rnp->wakemask != 0 || | 1647 | wait_event_interruptible(rnp->node_wq, rnp->wakemask != 0 || |
1641 | kthread_should_stop()); | 1648 | kthread_should_stop()); |
1642 | if (kthread_should_stop()) | 1649 | if (kthread_should_stop()) |
1643 | break; | 1650 | break; |
1651 | rnp->node_kthread_status = RCU_KTHREAD_RUNNING; | ||
1644 | raw_spin_lock_irqsave(&rnp->lock, flags); | 1652 | raw_spin_lock_irqsave(&rnp->lock, flags); |
1645 | mask = rnp->wakemask; | 1653 | mask = rnp->wakemask; |
1646 | rnp->wakemask = 0; | 1654 | rnp->wakemask = 0; |
@@ -1661,6 +1669,7 @@ static int rcu_node_kthread(void *arg) | |||
1661 | preempt_enable(); | 1669 | preempt_enable(); |
1662 | } | 1670 | } |
1663 | } | 1671 | } |
1672 | rnp->node_kthread_status = RCU_KTHREAD_STOPPED; | ||
1664 | return 0; | 1673 | return 0; |
1665 | } | 1674 | } |
1666 | 1675 | ||
diff --git a/kernel/rcutree.h b/kernel/rcutree.h index d49046c79c59..67341dbebd95 100644 --- a/kernel/rcutree.h +++ b/kernel/rcutree.h | |||
@@ -89,6 +89,13 @@ struct rcu_dynticks { | |||
89 | atomic_t dynticks; /* Even value for dynticks-idle, else odd. */ | 89 | atomic_t dynticks; /* Even value for dynticks-idle, else odd. */ |
90 | }; | 90 | }; |
91 | 91 | ||
92 | /* RCU's kthread states for tracing. */ | ||
93 | #define RCU_KTHREAD_STOPPED 0 | ||
94 | #define RCU_KTHREAD_RUNNING 1 | ||
95 | #define RCU_KTHREAD_WAITING 2 | ||
96 | #define RCU_KTHREAD_YIELDING 3 | ||
97 | #define RCU_KTHREAD_MAX 3 | ||
98 | |||
92 | /* | 99 | /* |
93 | * Definition for node within the RCU grace-period-detection hierarchy. | 100 | * Definition for node within the RCU grace-period-detection hierarchy. |
94 | */ | 101 | */ |
@@ -152,6 +159,8 @@ struct rcu_node { | |||
152 | wait_queue_head_t boost_wq; | 159 | wait_queue_head_t boost_wq; |
153 | /* Wait queue on which to park the boost */ | 160 | /* Wait queue on which to park the boost */ |
154 | /* kthread. */ | 161 | /* kthread. */ |
162 | unsigned int boost_kthread_status; | ||
163 | /* State of boost_kthread_task for tracing. */ | ||
155 | unsigned long n_tasks_boosted; | 164 | unsigned long n_tasks_boosted; |
156 | /* Total number of tasks boosted. */ | 165 | /* Total number of tasks boosted. */ |
157 | unsigned long n_exp_boosts; | 166 | unsigned long n_exp_boosts; |
@@ -179,6 +188,8 @@ struct rcu_node { | |||
179 | wait_queue_head_t node_wq; | 188 | wait_queue_head_t node_wq; |
180 | /* Wait queue on which to park the per-node */ | 189 | /* Wait queue on which to park the per-node */ |
181 | /* kthread. */ | 190 | /* kthread. */ |
191 | unsigned int node_kthread_status; | ||
192 | /* State of node_kthread_task for tracing. */ | ||
182 | } ____cacheline_internodealigned_in_smp; | 193 | } ____cacheline_internodealigned_in_smp; |
183 | 194 | ||
184 | /* | 195 | /* |
diff --git a/kernel/rcutree_plugin.h b/kernel/rcutree_plugin.h index 07d346445d12..22a6a46de7c6 100644 --- a/kernel/rcutree_plugin.h +++ b/kernel/rcutree_plugin.h | |||
@@ -1198,11 +1198,13 @@ static int rcu_boost_kthread(void *arg) | |||
1198 | int more2boost; | 1198 | int more2boost; |
1199 | 1199 | ||
1200 | for (;;) { | 1200 | for (;;) { |
1201 | rnp->boost_kthread_status = RCU_KTHREAD_WAITING; | ||
1201 | wait_event_interruptible(rnp->boost_wq, rnp->boost_tasks || | 1202 | wait_event_interruptible(rnp->boost_wq, rnp->boost_tasks || |
1202 | rnp->exp_tasks || | 1203 | rnp->exp_tasks || |
1203 | kthread_should_stop()); | 1204 | kthread_should_stop()); |
1204 | if (kthread_should_stop()) | 1205 | if (kthread_should_stop()) |
1205 | break; | 1206 | break; |
1207 | rnp->boost_kthread_status = RCU_KTHREAD_RUNNING; | ||
1206 | more2boost = rcu_boost(rnp); | 1208 | more2boost = rcu_boost(rnp); |
1207 | if (more2boost) | 1209 | if (more2boost) |
1208 | spincnt++; | 1210 | spincnt++; |
@@ -1213,6 +1215,7 @@ static int rcu_boost_kthread(void *arg) | |||
1213 | spincnt = 0; | 1215 | spincnt = 0; |
1214 | } | 1216 | } |
1215 | } | 1217 | } |
1218 | rnp->boost_kthread_status = RCU_KTHREAD_STOPPED; | ||
1216 | return 0; | 1219 | return 0; |
1217 | } | 1220 | } |
1218 | 1221 | ||
diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index afd262f91262..fc40e89a028c 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c | |||
@@ -46,6 +46,16 @@ | |||
46 | #define RCU_TREE_NONCORE | 46 | #define RCU_TREE_NONCORE |
47 | #include "rcutree.h" | 47 | #include "rcutree.h" |
48 | 48 | ||
49 | DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status); | ||
50 | DECLARE_PER_CPU(char, rcu_cpu_has_work); | ||
51 | |||
52 | static char convert_kthread_status(unsigned int kthread_status) | ||
53 | { | ||
54 | if (kthread_status > RCU_KTHREAD_MAX) | ||
55 | return '?'; | ||
56 | return "SRWY"[kthread_status]; | ||
57 | } | ||
58 | |||
49 | static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | 59 | static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) |
50 | { | 60 | { |
51 | if (!rdp->beenonline) | 61 | if (!rdp->beenonline) |
@@ -64,7 +74,7 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | |||
64 | rdp->dynticks_fqs); | 74 | rdp->dynticks_fqs); |
65 | #endif /* #ifdef CONFIG_NO_HZ */ | 75 | #endif /* #ifdef CONFIG_NO_HZ */ |
66 | seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); | 76 | seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); |
67 | seq_printf(m, " ql=%ld qs=%c%c%c%c b=%ld", | 77 | seq_printf(m, " ql=%ld qs=%c%c%c%c kt=%d/%c b=%ld", |
68 | rdp->qlen, | 78 | rdp->qlen, |
69 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != | 79 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != |
70 | rdp->nxttail[RCU_NEXT_TAIL]], | 80 | rdp->nxttail[RCU_NEXT_TAIL]], |
@@ -73,6 +83,9 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) | |||
73 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != | 83 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != |
74 | rdp->nxttail[RCU_WAIT_TAIL]], | 84 | rdp->nxttail[RCU_WAIT_TAIL]], |
75 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]], | 85 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]], |
86 | per_cpu(rcu_cpu_has_work, rdp->cpu), | ||
87 | convert_kthread_status(per_cpu(rcu_cpu_kthread_status, | ||
88 | rdp->cpu)), | ||
76 | rdp->blimit); | 89 | rdp->blimit); |
77 | seq_printf(m, " ci=%lu co=%lu ca=%lu\n", | 90 | seq_printf(m, " ci=%lu co=%lu ca=%lu\n", |
78 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); | 91 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); |
@@ -130,7 +143,7 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) | |||
130 | rdp->dynticks_fqs); | 143 | rdp->dynticks_fqs); |
131 | #endif /* #ifdef CONFIG_NO_HZ */ | 144 | #endif /* #ifdef CONFIG_NO_HZ */ |
132 | seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); | 145 | seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); |
133 | seq_printf(m, ",%ld,\"%c%c%c%c\",%ld", rdp->qlen, | 146 | seq_printf(m, ",%ld,\"%c%c%c%c\",%d,\"%c\",%ld", rdp->qlen, |
134 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != | 147 | ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != |
135 | rdp->nxttail[RCU_NEXT_TAIL]], | 148 | rdp->nxttail[RCU_NEXT_TAIL]], |
136 | ".R"[rdp->nxttail[RCU_WAIT_TAIL] != | 149 | ".R"[rdp->nxttail[RCU_WAIT_TAIL] != |
@@ -138,6 +151,9 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) | |||
138 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != | 151 | ".W"[rdp->nxttail[RCU_DONE_TAIL] != |
139 | rdp->nxttail[RCU_WAIT_TAIL]], | 152 | rdp->nxttail[RCU_WAIT_TAIL]], |
140 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]], | 153 | ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]], |
154 | per_cpu(rcu_cpu_has_work, rdp->cpu), | ||
155 | convert_kthread_status(per_cpu(rcu_cpu_kthread_status, | ||
156 | rdp->cpu)), | ||
141 | rdp->blimit); | 157 | rdp->blimit); |
142 | seq_printf(m, ",%lu,%lu,%lu\n", | 158 | seq_printf(m, ",%lu,%lu,%lu\n", |
143 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); | 159 | rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted); |
@@ -178,13 +194,14 @@ static const struct file_operations rcudata_csv_fops = { | |||
178 | 194 | ||
179 | static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) | 195 | static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) |
180 | { | 196 | { |
181 | seq_printf(m, "%d:%d tasks=%c%c%c%c ntb=%lu neb=%lu nnb=%lu " | 197 | seq_printf(m, "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu " |
182 | "j=%04x bt=%04x\n", | 198 | "j=%04x bt=%04x\n", |
183 | rnp->grplo, rnp->grphi, | 199 | rnp->grplo, rnp->grphi, |
184 | "T."[list_empty(&rnp->blkd_tasks)], | 200 | "T."[list_empty(&rnp->blkd_tasks)], |
185 | "N."[!rnp->gp_tasks], | 201 | "N."[!rnp->gp_tasks], |
186 | "E."[!rnp->exp_tasks], | 202 | "E."[!rnp->exp_tasks], |
187 | "B."[!rnp->boost_tasks], | 203 | "B."[!rnp->boost_tasks], |
204 | convert_kthread_status(rnp->boost_kthread_status), | ||
188 | rnp->n_tasks_boosted, rnp->n_exp_boosts, | 205 | rnp->n_tasks_boosted, rnp->n_exp_boosts, |
189 | rnp->n_normal_boosts, | 206 | rnp->n_normal_boosts, |
190 | (int)(jiffies & 0xffff), | 207 | (int)(jiffies & 0xffff), |