|
@@ -21,14 +21,17 @@ static int show_schedstat(struct seq_file *seq, void *v)
|
|
|
if (mask_str == NULL)
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- seq_printf(seq, "version %d\n", SCHEDSTAT_VERSION);
|
|
|
- seq_printf(seq, "timestamp %lu\n", jiffies);
|
|
|
- for_each_online_cpu(cpu) {
|
|
|
- struct rq *rq = cpu_rq(cpu);
|
|
|
+ if (v == (void *)1) {
|
|
|
+ seq_printf(seq, "version %d\n", SCHEDSTAT_VERSION);
|
|
|
+ seq_printf(seq, "timestamp %lu\n", jiffies);
|
|
|
+ } else {
|
|
|
+ struct rq *rq;
|
|
|
#ifdef CONFIG_SMP
|
|
|
struct sched_domain *sd;
|
|
|
int dcount = 0;
|
|
|
#endif
|
|
|
+ cpu = (unsigned long)(v - 2);
|
|
|
+ rq = cpu_rq(cpu);
|
|
|
|
|
|
/* runqueue-specific stats */
|
|
|
seq_printf(seq,
|
|
@@ -77,30 +80,66 @@ static int show_schedstat(struct seq_file *seq, void *v)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-static int schedstat_open(struct inode *inode, struct file *file)
|
|
|
+/*
|
|
|
+ * This itererator needs some explanation.
|
|
|
+ * It returns 1 for the header position.
|
|
|
+ * This means 2 is cpu 0.
|
|
|
+ * In a hotplugged system some cpus, including cpu 0, may be missing so we have
|
|
|
+ * to use cpumask_* to iterate over the cpus.
|
|
|
+ */
|
|
|
+static void *schedstat_start(struct seq_file *file, loff_t *offset)
|
|
|
{
|
|
|
- unsigned int size = PAGE_SIZE * (1 + num_online_cpus() / 32);
|
|
|
- char *buf = kmalloc(size, GFP_KERNEL);
|
|
|
- struct seq_file *m;
|
|
|
- int res;
|
|
|
+ unsigned long n = *offset;
|
|
|
|
|
|
- if (!buf)
|
|
|
- return -ENOMEM;
|
|
|
- res = single_open(file, show_schedstat, NULL);
|
|
|
- if (!res) {
|
|
|
- m = file->private_data;
|
|
|
- m->buf = buf;
|
|
|
- m->size = size;
|
|
|
- } else
|
|
|
- kfree(buf);
|
|
|
- return res;
|
|
|
+ if (n == 0)
|
|
|
+ return (void *) 1;
|
|
|
+
|
|
|
+ n--;
|
|
|
+
|
|
|
+ if (n > 0)
|
|
|
+ n = cpumask_next(n - 1, cpu_online_mask);
|
|
|
+ else
|
|
|
+ n = cpumask_first(cpu_online_mask);
|
|
|
+
|
|
|
+ *offset = n + 1;
|
|
|
+
|
|
|
+ if (n < nr_cpu_ids)
|
|
|
+ return (void *)(unsigned long)(n + 2);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void *schedstat_next(struct seq_file *file, void *data, loff_t *offset)
|
|
|
+{
|
|
|
+ (*offset)++;
|
|
|
+ return schedstat_start(file, offset);
|
|
|
+}
|
|
|
+
|
|
|
+static void schedstat_stop(struct seq_file *file, void *data)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+static const struct seq_operations schedstat_sops = {
|
|
|
+ .start = schedstat_start,
|
|
|
+ .next = schedstat_next,
|
|
|
+ .stop = schedstat_stop,
|
|
|
+ .show = show_schedstat,
|
|
|
+};
|
|
|
+
|
|
|
+static int schedstat_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return seq_open(file, &schedstat_sops);
|
|
|
}
|
|
|
|
|
|
+static int schedstat_release(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+};
|
|
|
+
|
|
|
static const struct file_operations proc_schedstat_operations = {
|
|
|
.open = schedstat_open,
|
|
|
.read = seq_read,
|
|
|
.llseek = seq_lseek,
|
|
|
- .release = single_release,
|
|
|
+ .release = schedstat_release,
|
|
|
};
|
|
|
|
|
|
static int __init proc_schedstat_init(void)
|