Trace softirqs and collect data about how often they get executed. kernel/rcupdate.c | 4 - kernel/sched.c | 10 +++ kernel/softirq.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+), 2 deletions(-) diff -puN kernel/softirq.c~25-softirq-debug kernel/softirq.c --- linux-2.6.0-rtcache/kernel/softirq.c~25-softirq-debug 2004-03-29 12:02:51.000000000 +0530 +++ linux-2.6.0-rtcache-dipankar/kernel/softirq.c 2004-03-29 13:27:42.212845760 +0530 @@ -14,6 +14,8 @@ #include #include #include +#include +#include /* - No shared variables, all the data are CPU local. @@ -42,6 +44,15 @@ static struct softirq_action softirq_vec static DEFINE_PER_CPU(struct task_struct *, ksoftirqd); +#define SOFTIRQ_TRACE_SIZE 30000 + +static int sirq_trace_buf[NR_CPUS][ALIGN((SOFTIRQ_TRACE_SIZE * sizeof(int)), SMP_CACHE_BYTES) / sizeof(int)] __cacheline_aligned; +extern int rcu_trace; +extern unsigned long rcu_trace_start; +DEFINE_PER_CPU(int, softirq_ticks) = 0; +DEFINE_PER_CPU(int, total_ticks) = 0; + + /* * we cannot loop indefinitely here to avoid userspace starvation, * but we also don't want to introduce a worst case 1/HZ latency @@ -84,8 +95,16 @@ asmlinkage void do_softirq(void) if (pending) { struct softirq_action *h; + local_bh_disable(); restart: + if (rcu_trace) { + int cpu = smp_processor_id(); + unsigned long bucket = (jiffies - rcu_trace_start) >> 2; + if (bucket < SOFTIRQ_TRACE_SIZE) + sirq_trace_buf[cpu][bucket]++; + } + /* Reset the pending bitmask before enabling irqs */ local_softirq_pending() = 0; @@ -393,3 +412,148 @@ __init int spawn_ksoftirqd(void) register_cpu_notifier(&cpu_nfb); return 0; } + +#ifdef CONFIG_PROC_FS +static void *sirqtrace_start(struct seq_file *m, loff_t *pos) +{ + static int cpu; + int *rt; + + cpu = (int)m->private; + rt = &sirq_trace_buf[cpu][0]; + return *pos < SOFTIRQ_TRACE_SIZE ? rt + *pos : NULL; +} + +static void *sirqtrace_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return sirqtrace_start(m, pos); +} + +static void sirqtrace_stop(struct seq_file *m, void *v) +{ +} + +static int show_sirqtrace(struct seq_file *m, void *v) +{ + int *rt = v; + int cpu; + + cpu = (int)m->private; + seq_printf(m, "%d %d\n", rt - &sirq_trace_buf[cpu][0], *rt); + return 0; +} + +struct seq_operations sirqtrace_op = { + .start = sirqtrace_start, + .next = sirqtrace_next, + .stop = sirqtrace_stop, + .show = show_sirqtrace, +}; + +static int sirqtrace_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *de; + int f; + + de = PROC_I(inode)->pde; + f = seq_open(file, &sirqtrace_op); + if (file->private_data) + ((struct seq_file *)(file->private_data))->private = de->data; + return f; +} +static struct file_operations proc_sirqtrace_operations = { + .open = sirqtrace_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +static void *sirqprof_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 : *pos : NULL; +} + +static void *sirqprof_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return sirqtrace_start(m, pos); +} + +static void sirqprof_stop(struct seq_file *m, void *v) +{ +} + +static int show_sirqprof(struct seq_file *m, void *v) +{ + int *rt = v; + int cpu; + + cpu = (int)m->private; + seq_printf(m, "%d %d\n", per_cpu(cpu, total_ticks), per_cpu(cpu, softirq_ticks)); + return 0; +} + +struct seq_operations sirqprof_op = { + .start = sirqprof_start, + .next = sirqprof_next, + .stop = sirqprof_stop, + .show = show_sirqprof, +}; + +static int sirqprof_open(struct inode *inode, struct file *file) +{ + struct proc_dir_entry *de; + int f; + + de = PROC_I(inode)->pde; + f = seq_open(file, &sirqprof_op); + if (file->private_data) + ((struct seq_file *)(file->private_data))->private = de->data; + return f; +} +static struct file_operations proc_sirqprof_operations = { + .open = sirqprof_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +extern struct proc_dir_entry proc_root; + +void sirq_proc_init(void) +{ + struct proc_dir_entry *entry; + char buf[16]; + int i; + + entry = proc_mkdir("softirq", &proc_root); + if (entry) { + struct proc_dir_entry *sirq_entry; + for (i = 0; i < NR_CPUS; i++) { + if (cpu_online(i)) { + sprintf(buf, "trace-cpu%d", i); + sirq_entry = create_proc_entry(buf, 0, entry); + if (!sirq_entry) + continue; + sirq_entry->proc_fops = &proc_sirqtrace_operations; + sirq_entry->data = (void *)i; + sprintf(buf, "profile%d", i); + sirq_entry = create_proc_entry(buf, 0, entry); + if (!sirq_entry) + continue; + sirq_entry->proc_fops = &proc_sirqprof_operations; + sirq_entry->data = (void *)i; + } + } + } +} + +static int __init sirq_late_init(void) +{ + sirq_proc_init(); + return 0; +} + +__initcall(sirq_late_init); + +#endif diff -puN kernel/rcupdate.c~25-softirq-debug kernel/rcupdate.c --- linux-2.6.0-rtcache/kernel/rcupdate.c~25-softirq-debug 2004-03-29 12:04:45.000000000 +0530 +++ linux-2.6.0-rtcache-dipankar/kernel/rcupdate.c 2004-03-29 12:04:58.000000000 +0530 @@ -63,8 +63,8 @@ static DEFINE_PER_CPU(struct tasklet_str static int rcu_trace_buf[NR_CPUS][ALIGN((RCU_TRACE_SIZE * sizeof(int)), SMP_CACHE_BYTES) / sizeof(int)] __cacheline_aligned; static int rcu_grace_buf[NR_CPUS][ALIGN((RCU_TRACE_SIZE * sizeof(int)), SMP_CACHE_BYTES) / sizeof(int)] __cacheline_aligned; -static int rcu_trace; -static unsigned long rcu_trace_start; +int rcu_trace; +unsigned long rcu_trace_start; /** * call_rcu - Queue an RCU update request. diff -puN kernel/sched.c~25-softirq-debug kernel/sched.c --- linux-2.6.0-rtcache/kernel/sched.c~25-softirq-debug 2004-03-29 12:14:07.000000000 +0530 +++ linux-2.6.0-rtcache-dipankar/kernel/sched.c 2004-03-29 12:17:01.000000000 +0530 @@ -1347,6 +1347,10 @@ EXPORT_PER_CPU_SYMBOL(kstat); (jiffies - (rq)->expired_timestamp >= \ STARVATION_LIMIT * ((rq)->nr_running) + 1))) +DECLARE_PER_CPU(int, total_ticks); +DECLARE_PER_CPU(int, softirq_ticks); +extern int rcu_trace; + /* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. @@ -1364,6 +1368,12 @@ void scheduler_tick(int user_ticks, int if (rcu_pending(cpu)) rcu_check_callbacks(cpu, user_ticks); + if (rcu_trace) { + per_cpu(cpu, total_ticks)++; + if (in_softirq()) + per_cpu(cpu, softirq_ticks)++; + } + /* note: this timer irq context must be accounted for as well */ if (hardirq_count() - HARDIRQ_OFFSET) { cpustat->irq += sys_ticks; _