1/* 2 * Read-Copy Update tracing for classic implementation 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 * Copyright IBM Corporation, 2008 19 * 20 * Papers: http://www.rdrop.com/users/paulmck/RCU 21 * 22 * For detailed explanation of Read-Copy Update mechanism see - 23 * Documentation/RCU 24 * 25 */ 26#include <linux/types.h> 27#include <linux/kernel.h> 28#include <linux/init.h> 29#include <linux/spinlock.h> 30#include <linux/smp.h> 31#include <linux/rcupdate.h> 32#include <linux/interrupt.h> 33#include <linux/sched.h> 34#include <asm/atomic.h> 35#include <linux/bitops.h> 36#include <linux/module.h> 37#include <linux/completion.h> 38#include <linux/moduleparam.h> 39#include <linux/percpu.h> 40#include <linux/notifier.h> 41#include <linux/cpu.h> 42#include <linux/mutex.h> 43#include <linux/debugfs.h> 44#include <linux/seq_file.h> 45 46#define RCU_TREE_NONCORE 47#include "rcutree.h" 48 49static void print_one_rcu_data(struct seq_file *m, struct rcu_data *rdp) 50{ 51 if (!rdp->beenonline) 52 return; 53 seq_printf(m, "%3d%cc=%lu g=%lu pq=%d pqc=%lu qp=%d", 54 rdp->cpu, 55 cpu_is_offline(rdp->cpu) ? '!' : ' ', 56 rdp->completed, rdp->gpnum, 57 rdp->passed_quiesc, rdp->passed_quiesc_completed, 58 rdp->qs_pending); 59#ifdef CONFIG_NO_HZ 60 seq_printf(m, " dt=%d/%d dn=%d df=%lu", 61 rdp->dynticks->dynticks, 62 rdp->dynticks->dynticks_nesting, 63 rdp->dynticks->dynticks_nmi, 64 rdp->dynticks_fqs); 65#endif /* #ifdef CONFIG_NO_HZ */ 66 seq_printf(m, " of=%lu ri=%lu", rdp->offline_fqs, rdp->resched_ipi); 67 seq_printf(m, " ql=%ld b=%ld\n", rdp->qlen, rdp->blimit); 68} 69 70#define PRINT_RCU_DATA(name, func, m) \ 71 do { \ 72 int _p_r_d_i; \ 73 \ 74 for_each_possible_cpu(_p_r_d_i) \ 75 func(m, &per_cpu(name, _p_r_d_i)); \ 76 } while (0) 77 78static int show_rcudata(struct seq_file *m, void *unused) 79{ 80#ifdef CONFIG_TREE_PREEMPT_RCU 81 seq_puts(m, "rcu_preempt:\n"); 82 PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data, m); 83#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 84 seq_puts(m, "rcu_sched:\n"); 85 PRINT_RCU_DATA(rcu_sched_data, print_one_rcu_data, m); 86 seq_puts(m, "rcu_bh:\n"); 87 PRINT_RCU_DATA(rcu_bh_data, print_one_rcu_data, m); 88 return 0; 89} 90 91static int rcudata_open(struct inode *inode, struct file *file) 92{ 93 return single_open(file, show_rcudata, NULL); 94} 95 96static const struct file_operations rcudata_fops = { 97 .owner = THIS_MODULE, 98 .open = rcudata_open, 99 .read = seq_read, 100 .llseek = seq_lseek, 101 .release = single_release, 102}; 103 104static void print_one_rcu_data_csv(struct seq_file *m, struct rcu_data *rdp) 105{ 106 if (!rdp->beenonline) 107 return; 108 seq_printf(m, "%d,%s,%lu,%lu,%d,%lu,%d", 109 rdp->cpu, 110 cpu_is_offline(rdp->cpu) ? "\"N\"" : "\"Y\"", 111 rdp->completed, rdp->gpnum, 112 rdp->passed_quiesc, rdp->passed_quiesc_completed, 113 rdp->qs_pending); 114#ifdef CONFIG_NO_HZ 115 seq_printf(m, ",%d,%d,%d,%lu", 116 rdp->dynticks->dynticks, 117 rdp->dynticks->dynticks_nesting, 118 rdp->dynticks->dynticks_nmi, 119 rdp->dynticks_fqs); 120#endif /* #ifdef CONFIG_NO_HZ */ 121 seq_printf(m, ",%lu,%lu", rdp->offline_fqs, rdp->resched_ipi); 122 seq_printf(m, ",%ld,%ld\n", rdp->qlen, rdp->blimit); 123} 124 125static int show_rcudata_csv(struct seq_file *m, void *unused) 126{ 127 seq_puts(m, "\"CPU\",\"Online?\",\"c\",\"g\",\"pq\",\"pqc\",\"pq\","); 128#ifdef CONFIG_NO_HZ 129 seq_puts(m, "\"dt\",\"dt nesting\",\"dn\",\"df\","); 130#endif /* #ifdef CONFIG_NO_HZ */ 131 seq_puts(m, "\"of\",\"ri\",\"ql\",\"b\"\n"); 132#ifdef CONFIG_TREE_PREEMPT_RCU 133 seq_puts(m, "\"rcu_preempt:\"\n"); 134 PRINT_RCU_DATA(rcu_preempt_data, print_one_rcu_data_csv, m); 135#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 136 seq_puts(m, "\"rcu_sched:\"\n"); 137 PRINT_RCU_DATA(rcu_sched_data, print_one_rcu_data_csv, m); 138 seq_puts(m, "\"rcu_bh:\"\n"); 139 PRINT_RCU_DATA(rcu_bh_data, print_one_rcu_data_csv, m); 140 return 0; 141} 142 143static int rcudata_csv_open(struct inode *inode, struct file *file) 144{ 145 return single_open(file, show_rcudata_csv, NULL); 146} 147 148static const struct file_operations rcudata_csv_fops = { 149 .owner = THIS_MODULE, 150 .open = rcudata_csv_open, 151 .read = seq_read, 152 .llseek = seq_lseek, 153 .release = single_release, 154}; 155 156static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) 157{ 158 unsigned long gpnum; 159 int level = 0; 160 int phase; 161 struct rcu_node *rnp; 162 163 gpnum = rsp->gpnum; 164 seq_printf(m, "c=%lu g=%lu s=%d jfq=%ld j=%x " 165 "nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld\n", 166 rsp->completed, gpnum, rsp->signaled, 167 (long)(rsp->jiffies_force_qs - jiffies), 168 (int)(jiffies & 0xffff), 169 rsp->n_force_qs, rsp->n_force_qs_ngp, 170 rsp->n_force_qs - rsp->n_force_qs_ngp, 171 rsp->n_force_qs_lh, rsp->orphan_qlen); 172 for (rnp = &rsp->node[0]; rnp - &rsp->node[0] < NUM_RCU_NODES; rnp++) { 173 if (rnp->level != level) { 174 seq_puts(m, "\n"); 175 level = rnp->level; 176 } 177 phase = gpnum & 0x1; 178 seq_printf(m, "%lx/%lx %c%c>%c%c %d:%d ^%d ", 179 rnp->qsmask, rnp->qsmaskinit, 180 "T."[list_empty(&rnp->blocked_tasks[phase])], 181 "E."[list_empty(&rnp->blocked_tasks[phase + 2])], 182 "T."[list_empty(&rnp->blocked_tasks[!phase])], 183 "E."[list_empty(&rnp->blocked_tasks[!phase + 2])], 184 rnp->grplo, rnp->grphi, rnp->grpnum); 185 } 186 seq_puts(m, "\n"); 187} 188 189static int show_rcuhier(struct seq_file *m, void *unused) 190{ 191#ifdef CONFIG_TREE_PREEMPT_RCU 192 seq_puts(m, "rcu_preempt:\n"); 193 print_one_rcu_state(m, &rcu_preempt_state); 194#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 195 seq_puts(m, "rcu_sched:\n"); 196 print_one_rcu_state(m, &rcu_sched_state); 197 seq_puts(m, "rcu_bh:\n"); 198 print_one_rcu_state(m, &rcu_bh_state); 199 return 0; 200} 201 202static int rcuhier_open(struct inode *inode, struct file *file) 203{ 204 return single_open(file, show_rcuhier, NULL); 205} 206 207static const struct file_operations rcuhier_fops = { 208 .owner = THIS_MODULE, 209 .open = rcuhier_open, 210 .read = seq_read, 211 .llseek = seq_lseek, 212 .release = single_release, 213}; 214 215static int show_rcugp(struct seq_file *m, void *unused) 216{ 217#ifdef CONFIG_TREE_PREEMPT_RCU 218 seq_printf(m, "rcu_preempt: completed=%ld gpnum=%lu\n", 219 rcu_preempt_state.completed, rcu_preempt_state.gpnum); 220#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 221 seq_printf(m, "rcu_sched: completed=%ld gpnum=%lu\n", 222 rcu_sched_state.completed, rcu_sched_state.gpnum); 223 seq_printf(m, "rcu_bh: completed=%ld gpnum=%lu\n", 224 rcu_bh_state.completed, rcu_bh_state.gpnum); 225 return 0; 226} 227 228static int rcugp_open(struct inode *inode, struct file *file) 229{ 230 return single_open(file, show_rcugp, NULL); 231} 232 233static const struct file_operations rcugp_fops = { 234 .owner = THIS_MODULE, 235 .open = rcugp_open, 236 .read = seq_read, 237 .llseek = seq_lseek, 238 .release = single_release, 239}; 240 241static void print_one_rcu_pending(struct seq_file *m, struct rcu_data *rdp) 242{ 243 seq_printf(m, "%3d%cnp=%ld " 244 "qsp=%ld rpq=%ld cbr=%ld cng=%ld " 245 "gpc=%ld gps=%ld nf=%ld nn=%ld\n", 246 rdp->cpu, 247 cpu_is_offline(rdp->cpu) ? '!' : ' ', 248 rdp->n_rcu_pending, 249 rdp->n_rp_qs_pending, 250 rdp->n_rp_report_qs, 251 rdp->n_rp_cb_ready, 252 rdp->n_rp_cpu_needs_gp, 253 rdp->n_rp_gp_completed, 254 rdp->n_rp_gp_started, 255 rdp->n_rp_need_fqs, 256 rdp->n_rp_need_nothing); 257} 258 259static void print_rcu_pendings(struct seq_file *m, struct rcu_state *rsp) 260{ 261 int cpu; 262 struct rcu_data *rdp; 263 264 for_each_possible_cpu(cpu) { 265 rdp = rsp->rda[cpu]; 266 if (rdp->beenonline) 267 print_one_rcu_pending(m, rdp); 268 } 269} 270 271static int show_rcu_pending(struct seq_file *m, void *unused) 272{ 273#ifdef CONFIG_TREE_PREEMPT_RCU 274 seq_puts(m, "rcu_preempt:\n"); 275 print_rcu_pendings(m, &rcu_preempt_state); 276#endif /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 277 seq_puts(m, "rcu_sched:\n"); 278 print_rcu_pendings(m, &rcu_sched_state); 279 seq_puts(m, "rcu_bh:\n"); 280 print_rcu_pendings(m, &rcu_bh_state); 281 return 0; 282} 283 284static int rcu_pending_open(struct inode *inode, struct file *file) 285{ 286 return single_open(file, show_rcu_pending, NULL); 287} 288 289static const struct file_operations rcu_pending_fops = { 290 .owner = THIS_MODULE, 291 .open = rcu_pending_open, 292 .read = seq_read, 293 .llseek = seq_lseek, 294 .release = single_release, 295}; 296 297static struct dentry *rcudir; 298 299static int __init rcuclassic_trace_init(void) 300{ 301 struct dentry *retval; 302 303 rcudir = debugfs_create_dir("rcu", NULL); 304 if (!rcudir) 305 goto free_out; 306 307 retval = debugfs_create_file("rcudata", 0444, rcudir, 308 NULL, &rcudata_fops); 309 if (!retval) 310 goto free_out; 311 312 retval = debugfs_create_file("rcudata.csv", 0444, rcudir, 313 NULL, &rcudata_csv_fops); 314 if (!retval) 315 goto free_out; 316 317 retval = debugfs_create_file("rcugp", 0444, rcudir, NULL, &rcugp_fops); 318 if (!retval) 319 goto free_out; 320 321 retval = debugfs_create_file("rcuhier", 0444, rcudir, 322 NULL, &rcuhier_fops); 323 if (!retval) 324 goto free_out; 325 326 retval = debugfs_create_file("rcu_pending", 0444, rcudir, 327 NULL, &rcu_pending_fops); 328 if (!retval) 329 goto free_out; 330 return 0; 331free_out: 332 debugfs_remove_recursive(rcudir); 333 return 1; 334} 335 336static void __exit rcuclassic_trace_cleanup(void) 337{ 338 debugfs_remove_recursive(rcudir); 339} 340 341 342module_init(rcuclassic_trace_init); 343module_exit(rcuclassic_trace_cleanup); 344 345MODULE_AUTHOR("Paul E. McKenney"); 346MODULE_DESCRIPTION("Read-Copy Update tracing for hierarchical implementation"); 347MODULE_LICENSE("GPL"); 348