diff options
Diffstat (limited to 'kernel/rcutree_trace.c')
| -rw-r--r-- | kernel/rcutree_trace.c | 208 | 
1 files changed, 186 insertions, 22 deletions
| diff --git a/kernel/rcutree_trace.c b/kernel/rcutree_trace.c index c8e97853b970..4e144876dc68 100644 --- a/kernel/rcutree_trace.c +++ b/kernel/rcutree_trace.c @@ -46,6 +46,22 @@  #define RCU_TREE_NONCORE  #include "rcutree.h" +#ifdef CONFIG_RCU_BOOST + +DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_status); +DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_cpu); +DECLARE_PER_CPU(unsigned int, rcu_cpu_kthread_loops); +DECLARE_PER_CPU(char, rcu_cpu_has_work); + +static char convert_kthread_status(unsigned int kthread_status) +{ +	if (kthread_status > RCU_KTHREAD_MAX) +		return '?'; +	return "SRWOY"[kthread_status]; +} + +#endif /* #ifdef CONFIG_RCU_BOOST */ +  static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)  {  	if (!rdp->beenonline) @@ -57,14 +73,31 @@ static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp)  		   rdp->passed_quiesc, rdp->passed_quiesc_completed,  		   rdp->qs_pending);  #ifdef CONFIG_NO_HZ -	seq_printf(m, " dt=%d/%d dn=%d df=%lu", -		   rdp->dynticks->dynticks, +	seq_printf(m, " dt=%d/%d/%d df=%lu", +		   atomic_read(&rdp->dynticks->dynticks),  		   rdp->dynticks->dynticks_nesting, -		   rdp->dynticks->dynticks_nmi, +		   rdp->dynticks->dynticks_nmi_nesting,  		   rdp->dynticks_fqs);  #endif /* #ifdef CONFIG_NO_HZ */  	seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); -	seq_printf(m, " ql=%ld b=%ld", rdp->qlen, rdp->blimit); +	seq_printf(m, " ql=%ld qs=%c%c%c%c", +		   rdp->qlen, +		   ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != +			rdp->nxttail[RCU_NEXT_TAIL]], +		   ".R"[rdp->nxttail[RCU_WAIT_TAIL] != +			rdp->nxttail[RCU_NEXT_READY_TAIL]], +		   ".W"[rdp->nxttail[RCU_DONE_TAIL] != +			rdp->nxttail[RCU_WAIT_TAIL]], +		   ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); +#ifdef CONFIG_RCU_BOOST +	seq_printf(m, " kt=%d/%c/%d ktl=%x", +		   per_cpu(rcu_cpu_has_work, rdp->cpu), +		   convert_kthread_status(per_cpu(rcu_cpu_kthread_status, +					  rdp->cpu)), +		   per_cpu(rcu_cpu_kthread_cpu, rdp->cpu), +		   per_cpu(rcu_cpu_kthread_loops, rdp->cpu) & 0xffff); +#endif /* #ifdef CONFIG_RCU_BOOST */ +	seq_printf(m, " b=%ld", rdp->blimit);  	seq_printf(m, " ci=%lu co=%lu ca=%lu\n",  		   rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted);  } @@ -115,13 +148,27 @@ static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp)  		   rdp->qs_pending);  #ifdef CONFIG_NO_HZ  	seq_printf(m, ",%d,%d,%d,%lu", -		   rdp->dynticks->dynticks, +		   atomic_read(&rdp->dynticks->dynticks),  		   rdp->dynticks->dynticks_nesting, -		   rdp->dynticks->dynticks_nmi, +		   rdp->dynticks->dynticks_nmi_nesting,  		   rdp->dynticks_fqs);  #endif /* #ifdef CONFIG_NO_HZ */  	seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); -	seq_printf(m, ",%ld,%ld", rdp->qlen, rdp->blimit); +	seq_printf(m, ",%ld,\"%c%c%c%c\"", rdp->qlen, +		   ".N"[rdp->nxttail[RCU_NEXT_READY_TAIL] != +			rdp->nxttail[RCU_NEXT_TAIL]], +		   ".R"[rdp->nxttail[RCU_WAIT_TAIL] != +			rdp->nxttail[RCU_NEXT_READY_TAIL]], +		   ".W"[rdp->nxttail[RCU_DONE_TAIL] != +			rdp->nxttail[RCU_WAIT_TAIL]], +		   ".D"[&rdp->nxtlist != rdp->nxttail[RCU_DONE_TAIL]]); +#ifdef CONFIG_RCU_BOOST +	seq_printf(m, ",%d,\"%c\"", +		   per_cpu(rcu_cpu_has_work, rdp->cpu), +		   convert_kthread_status(per_cpu(rcu_cpu_kthread_status, +					  rdp->cpu))); +#endif /* #ifdef CONFIG_RCU_BOOST */ +	seq_printf(m, ",%ld", rdp->blimit);  	seq_printf(m, ",%lu,%lu,%lu\n",  		   rdp->n_cbs_invoked, rdp->n_cbs_orphaned, rdp->n_cbs_adopted);  } @@ -130,9 +177,13 @@ static int show_rcudata_csv(struct seq_file *m, void *unused)  {  	seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pqc\",\"pq\",");  #ifdef CONFIG_NO_HZ -	seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\","); +	seq_puts(m, "\"dt\",\"dt nesting\",\"dt NMI nesting\",\"df\",");  #endif /* #ifdef CONFIG_NO_HZ */ -	seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\",\"ci\",\"co\",\"ca\"\n"); +	seq_puts(m, "\"of\",\"ri\",\"ql\",\"qs\""); +#ifdef CONFIG_RCU_BOOST +	seq_puts(m, "\"kt\",\"ktl\""); +#endif /* #ifdef CONFIG_RCU_BOOST */ +	seq_puts(m, ",\"b\",\"ci\",\"co\",\"ca\"\n");  #ifdef CONFIG_TREE_PREEMPT_RCU  	seq_puts(m, "\"rcu_preempt:\"\n");  	PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m); @@ -157,11 +208,76 @@ static const struct file_operations rcudata_csv_fops = {  	.release = single_release,  }; +#ifdef CONFIG_RCU_BOOST + +static void print_one_rcu_node_boost(struct seq_file *m, struct rcu_node *rnp) +{ +	seq_printf(m,  "%d:%d tasks=%c%c%c%c kt=%c ntb=%lu neb=%lu nnb=%lu " +		   "j=%04x bt=%04x\n", +		   rnp->grplo, rnp->grphi, +		   "T."[list_empty(&rnp->blkd_tasks)], +		   "N."[!rnp->gp_tasks], +		   "E."[!rnp->exp_tasks], +		   "B."[!rnp->boost_tasks], +		   convert_kthread_status(rnp->boost_kthread_status), +		   rnp->n_tasks_boosted, rnp->n_exp_boosts, +		   rnp->n_normal_boosts, +		   (int)(jiffies & 0xffff), +		   (int)(rnp->boost_time & 0xffff)); +	seq_printf(m, "%s: nt=%lu egt=%lu bt=%lu nb=%lu ny=%lu nos=%lu\n", +		   "     balk", +		   rnp->n_balk_blkd_tasks, +		   rnp->n_balk_exp_gp_tasks, +		   rnp->n_balk_boost_tasks, +		   rnp->n_balk_notblocked, +		   rnp->n_balk_notyet, +		   rnp->n_balk_nos); +} + +static int show_rcu_node_boost(struct seq_file *m, void *unused) +{ +	struct rcu_node *rnp; + +	rcu_for_each_leaf_node(&rcu_preempt_state, rnp) +		print_one_rcu_node_boost(m, rnp); +	return 0; +} + +static int rcu_node_boost_open(struct inode *inode, struct file *file) +{ +	return single_open(file, show_rcu_node_boost, NULL); +} + +static const struct file_operations rcu_node_boost_fops = { +	.owner = THIS_MODULE, +	.open = rcu_node_boost_open, +	.read = seq_read, +	.llseek = seq_lseek, +	.release = single_release, +}; + +/* + * Create the rcuboost debugfs entry.  Standard error return. + */ +static int rcu_boost_trace_create_file(struct dentry *rcudir) +{ +	return !debugfs_create_file("rcuboost", 0444, rcudir, NULL, +				    &rcu_node_boost_fops); +} + +#else /* #ifdef CONFIG_RCU_BOOST */ + +static int rcu_boost_trace_create_file(struct dentry *rcudir) +{ +	return 0;  /* There cannot be an error if we didn't create it! */ +} + +#endif /* #else #ifdef CONFIG_RCU_BOOST */ +  static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)  {  	unsigned long gpnum;  	int level = 0; -	int phase;  	struct rcu_node *rnp;  	gpnum = rsp->gpnum; @@ -178,13 +294,11 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp)  			seq_puts(m, "\n");  			level = rnp->level;  		} -		phase = gpnum & 0x1; -		seq_printf(m, "%lx/%lx %c%c>%c%c %d:%d ^%d    ", +		seq_printf(m, "%lx/%lx %c%c>%c %d:%d ^%d    ",  			   rnp->qsmask, rnp->qsmaskinit, -			   "T."[list_empty(&rnp->blocked_tasks[phase])], -			   "E."[list_empty(&rnp->blocked_tasks[phase + 2])], -			   "T."[list_empty(&rnp->blocked_tasks[!phase])], -			   "E."[list_empty(&rnp->blocked_tasks[!phase + 2])], +			   ".G"[rnp->gp_tasks != NULL], +			   ".E"[rnp->exp_tasks != NULL], +			   ".T"[!list_empty(&rnp->blkd_tasks)],  			   rnp->grplo, rnp->grphi, rnp->grpnum);  	}  	seq_puts(m, "\n"); @@ -216,16 +330,35 @@ static const struct file_operations rcuhier_fops = {  	.release = single_release,  }; +static void show_one_rcugp(struct seq_file *m, struct rcu_state *rsp) +{ +	unsigned long flags; +	unsigned long completed; +	unsigned long gpnum; +	unsigned long gpage; +	unsigned long gpmax; +	struct rcu_node *rnp = &rsp->node[0]; + +	raw_spin_lock_irqsave(&rnp->lock, flags); +	completed = rsp->completed; +	gpnum = rsp->gpnum; +	if (rsp->completed == rsp->gpnum) +		gpage = 0; +	else +		gpage = jiffies - rsp->gp_start; +	gpmax = rsp->gp_max; +	raw_spin_unlock_irqrestore(&rnp->lock, flags); +	seq_printf(m, "%s: completed=%ld  gpnum=%lu  age=%ld  max=%ld\n", +		   rsp->name, completed, gpnum, gpage, gpmax); +} +  static int show_rcugp(struct seq_file *m, void *unused)  {  #ifdef CONFIG_TREE_PREEMPT_RCU -	seq_printf(m, "rcu_preempt: completed=%ld  gpnum=%lu\n", -		   rcu_preempt_state.completed, rcu_preempt_state.gpnum); +	show_one_rcugp(m, &rcu_preempt_state);  #endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ -	seq_printf(m, "rcu_sched: completed=%ld  gpnum=%lu\n", -		   rcu_sched_state.completed, rcu_sched_state.gpnum); -	seq_printf(m, "rcu_bh: completed=%ld  gpnum=%lu\n", -		   rcu_bh_state.completed, rcu_bh_state.gpnum); +	show_one_rcugp(m, &rcu_sched_state); +	show_one_rcugp(m, &rcu_bh_state);  	return 0;  } @@ -298,6 +431,29 @@ static const struct file_operations rcu_pending_fops = {  	.release = single_release,  }; +static int show_rcutorture(struct seq_file *m, void *unused) +{ +	seq_printf(m, "rcutorture test sequence: %lu %s\n", +		   rcutorture_testseq >> 1, +		   (rcutorture_testseq & 0x1) ? "(test in progress)" : ""); +	seq_printf(m, "rcutorture update version number: %lu\n", +		   rcutorture_vernum); +	return 0; +} + +static int rcutorture_open(struct inode *inode, struct file *file) +{ +	return single_open(file, show_rcutorture, NULL); +} + +static const struct file_operations rcutorture_fops = { +	.owner = THIS_MODULE, +	.open = rcutorture_open, +	.read = seq_read, +	.llseek = seq_lseek, +	.release = single_release, +}; +  static struct dentry *rcudir;  static int __init rcutree_trace_init(void) @@ -318,6 +474,9 @@ static int __init rcutree_trace_init(void)  	if (!retval)  		goto free_out; +	if (rcu_boost_trace_create_file(rcudir)) +		goto free_out; +  	retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops);  	if (!retval)  		goto free_out; @@ -331,6 +490,11 @@ static int __init rcutree_trace_init(void)  						NULL, &rcu_pending_fops);  	if (!retval)  		goto free_out; + +	retval = debugfs_create_file("rcutorture", 0444, rcudir, +						NULL, &rcutorture_fops); +	if (!retval) +		goto free_out;  	return 0;  free_out:  	debugfs_remove_recursive(rcudir); | 
