tracing: make trace_seq_reset global and rename to trace_seq_init
[deliverable/linux.git] / kernel / trace / trace.c
index 11fde0ad9cb66e31fb29bb7f1d470df7b2be32b2..ea055aa21cd977e78cc6bbbce03479a18ad8cfaf 100644 (file)
@@ -80,7 +80,7 @@ static int dummy_set_flag(u32 old_flags, u32 bit, int set)
  * of the tracer is successful. But that is the only place that sets
  * this back to zero.
  */
-int tracing_disabled = 1;
+static int tracing_disabled = 1;
 
 static DEFINE_PER_CPU(local_t, ftrace_cpu_disabled);
 
@@ -98,6 +98,9 @@ static inline void ftrace_enable_cpu(void)
 
 static cpumask_var_t __read_mostly     tracing_buffer_mask;
 
+/* Define which cpu buffers are currently read in trace_pipe */
+static cpumask_var_t                   tracing_reader_cpumask;
+
 #define for_each_tracing_cpu(cpu)      \
        for_each_cpu(cpu, tracing_buffer_mask)
 
@@ -336,14 +339,7 @@ __update_max_tr(struct trace_array *tr, struct task_struct *tsk, int cpu)
        data->rt_priority = tsk->rt_priority;
 
        /* record this tasks comm */
-       tracing_record_cmdline(current);
-}
-
-static void
-trace_seq_reset(struct trace_seq *s)
-{
-       s->len = 0;
-       s->readpos = 0;
+       tracing_record_cmdline(tsk);
 }
 
 ssize_t trace_seq_to_user(struct trace_seq *s, char __user *ubuf, size_t cnt)
@@ -392,7 +388,7 @@ trace_print_seq(struct seq_file *m, struct trace_seq *s)
        s->buffer[len] = 0;
        seq_puts(m, s->buffer);
 
-       trace_seq_reset(s);
+       trace_seq_init(s);
 }
 
 /**
@@ -459,6 +455,8 @@ update_max_tr_single(struct trace_array *tr, struct task_struct *tsk, int cpu)
  * Register a new plugin tracer.
  */
 int register_tracer(struct tracer *type)
+__releases(kernel_lock)
+__acquires(kernel_lock)
 {
        struct tracer *t;
        int len;
@@ -497,6 +495,9 @@ int register_tracer(struct tracer *type)
        else
                if (!type->flags->opts)
                        type->flags->opts = dummy_tracer_opt;
+       if (!type->wait_pipe)
+               type->wait_pipe = default_wait_pipe;
+
 
 #ifdef CONFIG_FTRACE_STARTUP_TEST
        if (type->selftest && !tracing_selftest_disabled) {
@@ -626,7 +627,7 @@ static int cmdline_idx;
 static DEFINE_SPINLOCK(trace_cmdline_lock);
 
 /* temporary disable recording */
-atomic_t trace_record_cmdline_disabled __read_mostly;
+static atomic_t trace_record_cmdline_disabled __read_mostly;
 
 static void trace_init_cmdlines(void)
 {
@@ -838,6 +839,20 @@ void trace_buffer_unlock_commit(struct trace_array *tr,
        trace_wake_up();
 }
 
+struct ring_buffer_event *
+trace_current_buffer_lock_reserve(unsigned char type, unsigned long len,
+                                 unsigned long flags, int pc)
+{
+       return trace_buffer_lock_reserve(&global_trace,
+                                        type, len, flags, pc);
+}
+
+void trace_current_buffer_unlock_commit(struct ring_buffer_event *event,
+                                       unsigned long flags, int pc)
+{
+       return trace_buffer_unlock_commit(&global_trace, event, flags, pc);
+}
+
 void
 trace_function(struct trace_array *tr,
               unsigned long ip, unsigned long parent_ip, unsigned long flags,
@@ -983,10 +998,12 @@ static void ftrace_trace_userstack(struct trace_array *tr,
 #endif
 }
 
-void __trace_userstack(struct trace_array *tr, unsigned long flags)
+#ifdef UNUSED
+static void __trace_userstack(struct trace_array *tr, unsigned long flags)
 {
        ftrace_trace_userstack(tr, flags, preempt_count());
 }
+#endif /* UNUSED */
 
 static void
 ftrace_trace_special(void *__tr,
@@ -1060,7 +1077,10 @@ tracing_sched_wakeup_trace(struct trace_array *tr,
        entry->next_prio                = wakee->prio;
        entry->next_state               = wakee->state;
        entry->next_cpu                 = task_cpu(wakee);
-       trace_buffer_unlock_commit(tr, event, flags, pc);
+
+       ring_buffer_unlock_commit(tr->buffer, event);
+       ftrace_trace_stack(tr, flags, 6, pc);
+       ftrace_trace_userstack(tr, flags, pc);
 }
 
 void
@@ -1185,10 +1205,25 @@ __find_next_entry(struct trace_iterator *iter, int *ent_cpu, u64 *ent_ts)
 {
        struct ring_buffer *buffer = iter->tr->buffer;
        struct trace_entry *ent, *next = NULL;
+       int cpu_file = iter->cpu_file;
        u64 next_ts = 0, ts;
        int next_cpu = -1;
        int cpu;
 
+       /*
+        * If we are in a per_cpu trace file, don't bother by iterating over
+        * all cpu and peek directly.
+        */
+       if (cpu_file > TRACE_PIPE_ALL_CPU) {
+               if (ring_buffer_empty_cpu(buffer, cpu_file))
+                       return NULL;
+               ent = peek_next_entry(iter, cpu_file, ent_ts);
+               if (ent_cpu)
+                       *ent_cpu = cpu_file;
+
+               return ent;
+       }
+
        for_each_tracing_cpu(cpu) {
 
                if (ring_buffer_empty_cpu(buffer, cpu))
@@ -1266,19 +1301,32 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos)
        return ent;
 }
 
+/*
+ * No necessary locking here. The worst thing which can
+ * happen is loosing events consumed at the same time
+ * by a trace_pipe reader.
+ * Other than that, we don't risk to crash the ring buffer
+ * because it serializes the readers.
+ *
+ * The current tracer is copied to avoid a global locking
+ * all around.
+ */
 static void *s_start(struct seq_file *m, loff_t *pos)
 {
        struct trace_iterator *iter = m->private;
+       static struct tracer *old_tracer;
+       int cpu_file = iter->cpu_file;
        void *p = NULL;
        loff_t l = 0;
        int cpu;
 
+       /* copy the tracer to avoid using a global lock all around */
        mutex_lock(&trace_types_lock);
-
-       if (!current_trace || current_trace != iter->trace) {
-               mutex_unlock(&trace_types_lock);
-               return NULL;
+       if (unlikely(old_tracer != current_trace && current_trace)) {
+               old_tracer = current_trace;
+               *iter->trace = *current_trace;
        }
+       mutex_unlock(&trace_types_lock);
 
        atomic_inc(&trace_record_cmdline_disabled);
 
@@ -1289,9 +1337,12 @@ static void *s_start(struct seq_file *m, loff_t *pos)
 
                ftrace_disable_cpu();
 
-               for_each_tracing_cpu(cpu) {
-                       ring_buffer_iter_reset(iter->buffer_iter[cpu]);
-               }
+               if (cpu_file == TRACE_PIPE_ALL_CPU) {
+                       for_each_tracing_cpu(cpu)
+                               ring_buffer_iter_reset(iter->buffer_iter[cpu]);
+               } else
+                       ring_buffer_iter_reset(iter->buffer_iter[cpu_file]);
+
 
                ftrace_enable_cpu();
 
@@ -1309,7 +1360,6 @@ static void *s_start(struct seq_file *m, loff_t *pos)
 static void s_stop(struct seq_file *m, void *p)
 {
        atomic_dec(&trace_record_cmdline_disabled);
-       mutex_unlock(&trace_types_lock);
 }
 
 static void print_lat_help_header(struct seq_file *m)
@@ -1641,30 +1691,40 @@ static struct seq_operations tracer_seq_ops = {
 };
 
 static struct trace_iterator *
-__tracing_open(struct inode *inode, struct file *file, int *ret)
+__tracing_open(struct inode *inode, struct file *file)
 {
+       long cpu_file = (long) inode->i_private;
+       void *fail_ret = ERR_PTR(-ENOMEM);
        struct trace_iterator *iter;
        struct seq_file *m;
-       int cpu;
+       int cpu, ret;
 
-       if (tracing_disabled) {
-               *ret = -ENODEV;
-               return NULL;
-       }
+       if (tracing_disabled)
+               return ERR_PTR(-ENODEV);
 
        iter = kzalloc(sizeof(*iter), GFP_KERNEL);
-       if (!iter) {
-               *ret = -ENOMEM;
-               goto out;
-       }
+       if (!iter)
+               return ERR_PTR(-ENOMEM);
 
+       /*
+        * We make a copy of the current tracer to avoid concurrent
+        * changes on it while we are reading.
+        */
        mutex_lock(&trace_types_lock);
+       iter->trace = kzalloc(sizeof(*iter->trace), GFP_KERNEL);
+       if (!iter->trace)
+               goto fail;
+
+       if (current_trace)
+               *iter->trace = *current_trace;
+
        if (current_trace && current_trace->print_max)
                iter->tr = &max_tr;
        else
-               iter->tr = inode->i_private;
-       iter->trace = current_trace;
+               iter->tr = &global_trace;
        iter->pos = -1;
+       mutex_init(&iter->mutex);
+       iter->cpu_file = cpu_file;
 
        /* Notify the tracer early; before we stop tracing. */
        if (iter->trace && iter->trace->open)
@@ -1674,20 +1734,30 @@ __tracing_open(struct inode *inode, struct file *file, int *ret)
        if (ring_buffer_overruns(iter->tr->buffer))
                iter->iter_flags |= TRACE_FILE_ANNOTATE;
 
+       if (iter->cpu_file == TRACE_PIPE_ALL_CPU) {
+               for_each_tracing_cpu(cpu) {
 
-       for_each_tracing_cpu(cpu) {
+                       iter->buffer_iter[cpu] =
+                               ring_buffer_read_start(iter->tr->buffer, cpu);
 
+                       if (!iter->buffer_iter[cpu])
+                               goto fail_buffer;
+               }
+       } else {
+               cpu = iter->cpu_file;
                iter->buffer_iter[cpu] =
-                       ring_buffer_read_start(iter->tr->buffer, cpu);
+                               ring_buffer_read_start(iter->tr->buffer, cpu);
 
                if (!iter->buffer_iter[cpu])
-                       goto fail_buffer;
+                       goto fail;
        }
 
        /* TODO stop tracer */
-       *ret = seq_open(file, &tracer_seq_ops);
-       if (*ret)
+       ret = seq_open(file, &tracer_seq_ops);
+       if (ret < 0) {
+               fail_ret = ERR_PTR(ret);
                goto fail_buffer;
+       }
 
        m = file->private_data;
        m->private = iter;
@@ -1697,7 +1767,6 @@ __tracing_open(struct inode *inode, struct file *file, int *ret)
 
        mutex_unlock(&trace_types_lock);
 
- out:
        return iter;
 
  fail_buffer:
@@ -1705,10 +1774,12 @@ __tracing_open(struct inode *inode, struct file *file, int *ret)
                if (iter->buffer_iter[cpu])
                        ring_buffer_read_finish(iter->buffer_iter[cpu]);
        }
+ fail:
        mutex_unlock(&trace_types_lock);
+       kfree(iter->trace);
        kfree(iter);
 
-       return ERR_PTR(-ENOMEM);
+       return fail_ret;
 }
 
 int tracing_open_generic(struct inode *inode, struct file *filp)
@@ -1720,7 +1791,7 @@ int tracing_open_generic(struct inode *inode, struct file *filp)
        return 0;
 }
 
-int tracing_release(struct inode *inode, struct file *file)
+static int tracing_release(struct inode *inode, struct file *file)
 {
        struct seq_file *m = (struct seq_file *)file->private_data;
        struct trace_iterator *iter = m->private;
@@ -1740,15 +1811,20 @@ int tracing_release(struct inode *inode, struct file *file)
        mutex_unlock(&trace_types_lock);
 
        seq_release(inode, file);
+       mutex_destroy(&iter->mutex);
+       kfree(iter->trace);
        kfree(iter);
        return 0;
 }
 
 static int tracing_open(struct inode *inode, struct file *file)
 {
-       int ret;
+       struct trace_iterator *iter;
+       int ret = 0;
 
-       __tracing_open(inode, file, &ret);
+       iter = __tracing_open(inode, file);
+       if (IS_ERR(iter))
+               ret = PTR_ERR(iter);
 
        return ret;
 }
@@ -1756,11 +1832,13 @@ static int tracing_open(struct inode *inode, struct file *file)
 static int tracing_lt_open(struct inode *inode, struct file *file)
 {
        struct trace_iterator *iter;
-       int ret;
+       int ret = 0;
 
-       iter = __tracing_open(inode, file, &ret);
+       iter = __tracing_open(inode, file);
 
-       if (!ret)
+       if (IS_ERR(iter))
+               ret = PTR_ERR(iter);
+       else
                iter->iter_flags |= TRACE_FILE_LAT_FMT;
 
        return ret;
@@ -1955,57 +2033,62 @@ static ssize_t
 tracing_trace_options_read(struct file *filp, char __user *ubuf,
                       size_t cnt, loff_t *ppos)
 {
-       int i;
+       struct tracer_opt *trace_opts;
+       u32 tracer_flags;
+       int len = 0;
        char *buf;
        int r = 0;
-       int len = 0;
-       u32 tracer_flags = current_trace->flags->val;
-       struct tracer_opt *trace_opts = current_trace->flags->opts;
+       int i;
 
 
-       /* calulate max size */
+       /* calculate max size */
        for (i = 0; trace_options[i]; i++) {
                len += strlen(trace_options[i]);
-               len += 3; /* "no" and space */
+               len += 3; /* "no" and newline */
        }
 
+       mutex_lock(&trace_types_lock);
+       tracer_flags = current_trace->flags->val;
+       trace_opts = current_trace->flags->opts;
+
        /*
         * Increase the size with names of options specific
         * of the current tracer.
         */
        for (i = 0; trace_opts[i].name; i++) {
                len += strlen(trace_opts[i].name);
-               len += 3; /* "no" and space */
+               len += 3; /* "no" and newline */
        }
 
        /* +2 for \n and \0 */
        buf = kmalloc(len + 2, GFP_KERNEL);
-       if (!buf)
+       if (!buf) {
+               mutex_unlock(&trace_types_lock);
                return -ENOMEM;
+       }
 
        for (i = 0; trace_options[i]; i++) {
                if (trace_flags & (1 << i))
-                       r += sprintf(buf + r, "%s ", trace_options[i]);
+                       r += sprintf(buf + r, "%s\n", trace_options[i]);
                else
-                       r += sprintf(buf + r, "no%s ", trace_options[i]);
+                       r += sprintf(buf + r, "no%s\n", trace_options[i]);
        }
 
        for (i = 0; trace_opts[i].name; i++) {
                if (tracer_flags & trace_opts[i].bit)
-                       r += sprintf(buf + r, "%s ",
+                       r += sprintf(buf + r, "%s\n",
                                trace_opts[i].name);
                else
-                       r += sprintf(buf + r, "no%s ",
+                       r += sprintf(buf + r, "no%s\n",
                                trace_opts[i].name);
        }
+       mutex_unlock(&trace_types_lock);
 
-       r += sprintf(buf + r, "\n");
        WARN_ON(r >= len + 2);
 
        r = simple_read_from_buffer(ubuf, cnt, ppos, buf, r);
 
        kfree(buf);
-
        return r;
 }
 
@@ -2080,7 +2163,9 @@ tracing_trace_options_write(struct file *filp, const char __user *ubuf,
 
        /* If no option could be set, test the specific tracer options */
        if (!trace_options[i]) {
+               mutex_lock(&trace_types_lock);
                ret = set_tracer_option(current_trace, cmp, neg);
+               mutex_unlock(&trace_types_lock);
                if (ret)
                        return ret;
        }
@@ -2145,7 +2230,7 @@ tracing_ctrl_write(struct file *filp, const char __user *ubuf,
 {
        struct trace_array *tr = filp->private_data;
        char buf[64];
-       long val;
+       unsigned long val;
        int ret;
 
        if (cnt >= sizeof(buf))
@@ -2206,8 +2291,17 @@ int tracer_init(struct tracer *t, struct trace_array *tr)
        return t->init(tr);
 }
 
+struct trace_option_dentry;
+
+static struct trace_option_dentry *
+create_trace_option_files(struct tracer *tracer);
+
+static void
+destroy_trace_option_files(struct trace_option_dentry *topts);
+
 static int tracing_set_tracer(const char *buf)
 {
+       static struct trace_option_dentry *topts;
        struct trace_array *tr = &global_trace;
        struct tracer *t;
        int ret = 0;
@@ -2228,7 +2322,12 @@ static int tracing_set_tracer(const char *buf)
        if (current_trace && current_trace->reset)
                current_trace->reset(tr);
 
+       destroy_trace_option_files(topts);
+
        current_trace = t;
+
+       topts = create_trace_option_files(current_trace);
+
        if (t->init) {
                ret = tracer_init(t, tr);
                if (ret)
@@ -2293,9 +2392,9 @@ static ssize_t
 tracing_max_lat_write(struct file *filp, const char __user *ubuf,
                      size_t cnt, loff_t *ppos)
 {
-       long *ptr = filp->private_data;
+       unsigned long *ptr = filp->private_data;
        char buf[64];
-       long val;
+       unsigned long val;
        int ret;
 
        if (cnt >= sizeof(buf))
@@ -2315,54 +2414,96 @@ tracing_max_lat_write(struct file *filp, const char __user *ubuf,
        return cnt;
 }
 
-static atomic_t tracing_reader;
-
 static int tracing_open_pipe(struct inode *inode, struct file *filp)
 {
+       long cpu_file = (long) inode->i_private;
        struct trace_iterator *iter;
+       int ret = 0;
 
        if (tracing_disabled)
                return -ENODEV;
 
-       /* We only allow for reader of the pipe */
-       if (atomic_inc_return(&tracing_reader) != 1) {
-               atomic_dec(&tracing_reader);
-               return -EBUSY;
+       mutex_lock(&trace_types_lock);
+
+       /* We only allow one reader per cpu */
+       if (cpu_file == TRACE_PIPE_ALL_CPU) {
+               if (!cpumask_empty(tracing_reader_cpumask)) {
+                       ret = -EBUSY;
+                       goto out;
+               }
+               cpumask_setall(tracing_reader_cpumask);
+       } else {
+               if (!cpumask_test_cpu(cpu_file, tracing_reader_cpumask))
+                       cpumask_set_cpu(cpu_file, tracing_reader_cpumask);
+               else {
+                       ret = -EBUSY;
+                       goto out;
+               }
        }
 
        /* create a buffer to store the information to pass to userspace */
        iter = kzalloc(sizeof(*iter), GFP_KERNEL);
-       if (!iter)
-               return -ENOMEM;
+       if (!iter) {
+               ret = -ENOMEM;
+               goto out;
+       }
 
-       if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) {
-               kfree(iter);
-               return -ENOMEM;
+       /*
+        * We make a copy of the current tracer to avoid concurrent
+        * changes on it while we are reading.
+        */
+       iter->trace = kmalloc(sizeof(*iter->trace), GFP_KERNEL);
+       if (!iter->trace) {
+               ret = -ENOMEM;
+               goto fail;
        }
+       if (current_trace)
+               *iter->trace = *current_trace;
 
-       mutex_lock(&trace_types_lock);
+       if (!alloc_cpumask_var(&iter->started, GFP_KERNEL)) {
+               ret = -ENOMEM;
+               goto fail;
+       }
 
        /* trace pipe does not show start of buffer */
        cpumask_setall(iter->started);
 
+       iter->cpu_file = cpu_file;
        iter->tr = &global_trace;
-       iter->trace = current_trace;
+       mutex_init(&iter->mutex);
        filp->private_data = iter;
 
        if (iter->trace->pipe_open)
                iter->trace->pipe_open(iter);
+
+out:
        mutex_unlock(&trace_types_lock);
+       return ret;
 
-       return 0;
+fail:
+       kfree(iter->trace);
+       kfree(iter);
+       mutex_unlock(&trace_types_lock);
+       return ret;
 }
 
 static int tracing_release_pipe(struct inode *inode, struct file *file)
 {
        struct trace_iterator *iter = file->private_data;
 
+       mutex_lock(&trace_types_lock);
+
+       if (iter->cpu_file == TRACE_PIPE_ALL_CPU)
+               cpumask_clear(tracing_reader_cpumask);
+       else
+               cpumask_clear_cpu(iter->cpu_file, tracing_reader_cpumask);
+
+       mutex_unlock(&trace_types_lock);
+
        free_cpumask_var(iter->started);
+       mutex_destroy(&iter->mutex);
+       kfree(iter->trace);
        kfree(iter);
-       atomic_dec(&tracing_reader);
 
        return 0;
 }
@@ -2388,6 +2529,38 @@ tracing_poll_pipe(struct file *filp, poll_table *poll_table)
        }
 }
 
+
+void default_wait_pipe(struct trace_iterator *iter)
+{
+       DEFINE_WAIT(wait);
+
+       prepare_to_wait(&trace_wait, &wait, TASK_INTERRUPTIBLE);
+
+       if (trace_empty(iter))
+               schedule();
+
+       finish_wait(&trace_wait, &wait);
+}
+
+/*
+ * This is a make-shift waitqueue.
+ * A tracer might use this callback on some rare cases:
+ *
+ *  1) the current tracer might hold the runqueue lock when it wakes up
+ *     a reader, hence a deadlock (sched, function, and function graph tracers)
+ *  2) the function tracers, trace all functions, we don't want
+ *     the overhead of calling wake_up and friends
+ *     (and tracing them too)
+ *
+ *     Anyway, this is really very primitive wakeup.
+ */
+void poll_wait_pipe(struct trace_iterator *iter)
+{
+       set_current_state(TASK_INTERRUPTIBLE);
+       /* sleep for 100 msecs, and try again. */
+       schedule_timeout(HZ / 10);
+}
+
 /* Must be called with trace_types_lock mutex held. */
 static int tracing_wait_pipe(struct file *filp)
 {
@@ -2399,33 +2572,14 @@ static int tracing_wait_pipe(struct file *filp)
                        return -EAGAIN;
                }
 
-               /*
-                * This is a make-shift waitqueue. The reason we don't use
-                * an actual wait queue is because:
-                *  1) we only ever have one waiter
-                *  2) the tracing, traces all functions, we don't want
-                *     the overhead of calling wake_up and friends
-                *     (and tracing them too)
-                *     Anyway, this is really very primitive wakeup.
-                */
-               set_current_state(TASK_INTERRUPTIBLE);
-               iter->tr->waiter = current;
-
-               mutex_unlock(&trace_types_lock);
-
-               /* sleep for 100 msecs, and try again. */
-               schedule_timeout(HZ/10);
+               mutex_unlock(&iter->mutex);
 
-               mutex_lock(&trace_types_lock);
+               iter->trace->wait_pipe(iter);
 
-               iter->tr->waiter = NULL;
+               mutex_lock(&iter->mutex);
 
-               if (signal_pending(current)) {
+               if (signal_pending(current))
                        return -EINTR;
-               }
-
-               if (iter->trace != current_trace)
-                       return 0;
 
                /*
                 * We block until we read something and tracing is disabled.
@@ -2438,8 +2592,6 @@ static int tracing_wait_pipe(struct file *filp)
                 */
                if (!tracer_enabled && iter->pos)
                        break;
-
-               continue;
        }
 
        return 1;
@@ -2453,6 +2605,7 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
                  size_t cnt, loff_t *ppos)
 {
        struct trace_iterator *iter = filp->private_data;
+       static struct tracer *old_tracer;
        ssize_t sret;
 
        /* return any leftover data */
@@ -2460,9 +2613,22 @@ tracing_read_pipe(struct file *filp, char __user *ubuf,
        if (sret != -EBUSY)
                return sret;
 
-       trace_seq_reset(&iter->seq);
+       trace_seq_init(&iter->seq);
 
+       /* copy the tracer to avoid using a global lock all around */
        mutex_lock(&trace_types_lock);
+       if (unlikely(old_tracer != current_trace && current_trace)) {
+               old_tracer = current_trace;
+               *iter->trace = *current_trace;
+       }
+       mutex_unlock(&trace_types_lock);
+
+       /*
+        * Avoid more than one consumer on a single file descriptor
+        * This is just a matter of traces coherency, the ring buffer itself
+        * is protected.
+        */
+       mutex_lock(&iter->mutex);
        if (iter->trace->read) {
                sret = iter->trace->read(iter, filp, ubuf, cnt, ppos);
                if (sret)
@@ -2509,7 +2675,7 @@ waitagain:
        /* Now copy what we have to the user */
        sret = trace_seq_to_user(&iter->seq, ubuf, cnt);
        if (iter->seq.readpos >= iter->seq.len)
-               trace_seq_reset(&iter->seq);
+               trace_seq_init(&iter->seq);
 
        /*
         * If there was nothing to send to user, inspite of consuming trace
@@ -2519,7 +2685,7 @@ waitagain:
                goto waitagain;
 
 out:
-       mutex_unlock(&trace_types_lock);
+       mutex_unlock(&iter->mutex);
 
        return sret;
 }
@@ -2537,15 +2703,48 @@ static void tracing_spd_release_pipe(struct splice_pipe_desc *spd,
 }
 
 static struct pipe_buf_operations tracing_pipe_buf_ops = {
-       .can_merge = 0,
-       .map = generic_pipe_buf_map,
-       .unmap = generic_pipe_buf_unmap,
-       .confirm = generic_pipe_buf_confirm,
-       .release = tracing_pipe_buf_release,
-       .steal = generic_pipe_buf_steal,
-       .get = generic_pipe_buf_get,
+       .can_merge              = 0,
+       .map                    = generic_pipe_buf_map,
+       .unmap                  = generic_pipe_buf_unmap,
+       .confirm                = generic_pipe_buf_confirm,
+       .release                = tracing_pipe_buf_release,
+       .steal                  = generic_pipe_buf_steal,
+       .get                    = generic_pipe_buf_get,
 };
 
+static size_t
+tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter)
+{
+       size_t count;
+       int ret;
+
+       /* Seq buffer is page-sized, exactly what we need. */
+       for (;;) {
+               count = iter->seq.len;
+               ret = print_trace_line(iter);
+               count = iter->seq.len - count;
+               if (rem < count) {
+                       rem = 0;
+                       iter->seq.len -= count;
+                       break;
+               }
+               if (ret == TRACE_TYPE_PARTIAL_LINE) {
+                       iter->seq.len -= count;
+                       break;
+               }
+
+               trace_consume(iter);
+               rem -= count;
+               if (!find_next_entry_inc(iter)) {
+                       rem = 0;
+                       iter->ent = NULL;
+                       break;
+               }
+       }
+
+       return rem;
+}
+
 static ssize_t tracing_splice_read_pipe(struct file *filp,
                                        loff_t *ppos,
                                        struct pipe_inode_info *pipe,
@@ -2556,62 +2755,51 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
        struct partial_page partial[PIPE_BUFFERS];
        struct trace_iterator *iter = filp->private_data;
        struct splice_pipe_desc spd = {
-               .pages = pages,
-               .partial = partial,
-               .nr_pages = 0, /* This gets updated below. */
-               .flags = flags,
-               .ops = &tracing_pipe_buf_ops,
-               .spd_release = tracing_spd_release_pipe,
+               .pages          = pages,
+               .partial        = partial,
+               .nr_pages       = 0, /* This gets updated below. */
+               .flags          = flags,
+               .ops            = &tracing_pipe_buf_ops,
+               .spd_release    = tracing_spd_release_pipe,
        };
+       static struct tracer *old_tracer;
        ssize_t ret;
-       size_t count, rem;
+       size_t rem;
        unsigned int i;
 
+       /* copy the tracer to avoid using a global lock all around */
        mutex_lock(&trace_types_lock);
+       if (unlikely(old_tracer != current_trace && current_trace)) {
+               old_tracer = current_trace;
+               *iter->trace = *current_trace;
+       }
+       mutex_unlock(&trace_types_lock);
+
+       mutex_lock(&iter->mutex);
 
        if (iter->trace->splice_read) {
                ret = iter->trace->splice_read(iter, filp,
                                               ppos, pipe, len, flags);
                if (ret)
-                       goto out;
+                       goto out_err;
        }
 
        ret = tracing_wait_pipe(filp);
        if (ret <= 0)
-               goto out;
+               goto out_err;
 
        if (!iter->ent && !find_next_entry_inc(iter)) {
                ret = -EFAULT;
-               goto out;
+               goto out_err;
        }
 
        /* Fill as many pages as possible. */
        for (i = 0, rem = len; i < PIPE_BUFFERS && rem; i++) {
                pages[i] = alloc_page(GFP_KERNEL);
+               if (!pages[i])
+                       break;
 
-               /* Seq buffer is page-sized, exactly what we need. */
-               for (;;) {
-                       count = iter->seq.len;
-                       ret = print_trace_line(iter);
-                       count = iter->seq.len - count;
-                       if (rem < count) {
-                               rem = 0;
-                               iter->seq.len -= count;
-                               break;
-                       }
-                       if (ret == TRACE_TYPE_PARTIAL_LINE) {
-                               iter->seq.len -= count;
-                               break;
-                       }
-
-                       trace_consume(iter);
-                       rem -= count;
-                       if (!find_next_entry_inc(iter)) {
-                               rem = 0;
-                               iter->ent = NULL;
-                               break;
-                       }
-               }
+               rem = tracing_fill_pipe_page(rem, iter);
 
                /* Copy the data into the page, so we can start over. */
                ret = trace_seq_to_buffer(&iter->seq,
@@ -2624,17 +2812,17 @@ static ssize_t tracing_splice_read_pipe(struct file *filp,
                partial[i].offset = 0;
                partial[i].len = iter->seq.len;
 
-               trace_seq_reset(&iter->seq);
+               trace_seq_init(&iter->seq);
        }
 
-       mutex_unlock(&trace_types_lock);
+       mutex_unlock(&iter->mutex);
 
        spd.nr_pages = i;
 
        return splice_to_pipe(pipe, &spd);
 
-out:
-       mutex_unlock(&trace_types_lock);
+out_err:
+       mutex_unlock(&iter->mutex);
 
        return ret;
 }
@@ -2874,15 +3062,345 @@ struct dentry *tracing_init_dentry(void)
        return d_tracer;
 }
 
+static struct dentry *d_percpu;
+
+struct dentry *tracing_dentry_percpu(void)
+{
+       static int once;
+       struct dentry *d_tracer;
+
+       if (d_percpu)
+               return d_percpu;
+
+       d_tracer = tracing_init_dentry();
+
+       if (!d_tracer)
+               return NULL;
+
+       d_percpu = debugfs_create_dir("per_cpu", d_tracer);
+
+       if (!d_percpu && !once) {
+               once = 1;
+               pr_warning("Could not create debugfs directory 'per_cpu'\n");
+               return NULL;
+       }
+
+       return d_percpu;
+}
+
+static void tracing_init_debugfs_percpu(long cpu)
+{
+       struct dentry *d_percpu = tracing_dentry_percpu();
+       struct dentry *entry, *d_cpu;
+       /* strlen(cpu) + MAX(log10(cpu)) + '\0' */
+       char cpu_dir[7];
+
+       if (cpu > 999 || cpu < 0)
+               return;
+
+       sprintf(cpu_dir, "cpu%ld", cpu);
+       d_cpu = debugfs_create_dir(cpu_dir, d_percpu);
+       if (!d_cpu) {
+               pr_warning("Could not create debugfs '%s' entry\n", cpu_dir);
+               return;
+       }
+
+       /* per cpu trace_pipe */
+       entry = debugfs_create_file("trace_pipe", 0444, d_cpu,
+                               (void *) cpu, &tracing_pipe_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs 'trace_pipe' entry\n");
+
+       /* per cpu trace */
+       entry = debugfs_create_file("trace", 0444, d_cpu,
+                               (void *) cpu, &tracing_fops);
+       if (!entry)
+               pr_warning("Could not create debugfs 'trace' entry\n");
+}
+
 #ifdef CONFIG_FTRACE_SELFTEST
 /* Let selftest have access to static functions in this file */
 #include "trace_selftest.c"
 #endif
 
+struct trace_option_dentry {
+       struct tracer_opt               *opt;
+       struct tracer_flags             *flags;
+       struct dentry                   *entry;
+};
+
+static ssize_t
+trace_options_read(struct file *filp, char __user *ubuf, size_t cnt,
+                       loff_t *ppos)
+{
+       struct trace_option_dentry *topt = filp->private_data;
+       char *buf;
+
+       if (topt->flags->val & topt->opt->bit)
+               buf = "1\n";
+       else
+               buf = "0\n";
+
+       return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2);
+}
+
+static ssize_t
+trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt,
+                        loff_t *ppos)
+{
+       struct trace_option_dentry *topt = filp->private_data;
+       unsigned long val;
+       char buf[64];
+       int ret;
+
+       if (cnt >= sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, cnt))
+               return -EFAULT;
+
+       buf[cnt] = 0;
+
+       ret = strict_strtoul(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       ret = 0;
+       switch (val) {
+       case 0:
+               /* do nothing if already cleared */
+               if (!(topt->flags->val & topt->opt->bit))
+                       break;
+
+               mutex_lock(&trace_types_lock);
+               if (current_trace->set_flag)
+                       ret = current_trace->set_flag(topt->flags->val,
+                                                     topt->opt->bit, 0);
+               mutex_unlock(&trace_types_lock);
+               if (ret)
+                       return ret;
+               topt->flags->val &= ~topt->opt->bit;
+               break;
+       case 1:
+               /* do nothing if already set */
+               if (topt->flags->val & topt->opt->bit)
+                       break;
+
+               mutex_lock(&trace_types_lock);
+               if (current_trace->set_flag)
+                       ret = current_trace->set_flag(topt->flags->val,
+                                                     topt->opt->bit, 1);
+               mutex_unlock(&trace_types_lock);
+               if (ret)
+                       return ret;
+               topt->flags->val |= topt->opt->bit;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       *ppos += cnt;
+
+       return cnt;
+}
+
+
+static const struct file_operations trace_options_fops = {
+       .open = tracing_open_generic,
+       .read = trace_options_read,
+       .write = trace_options_write,
+};
+
+static ssize_t
+trace_options_core_read(struct file *filp, char __user *ubuf, size_t cnt,
+                       loff_t *ppos)
+{
+       long index = (long)filp->private_data;
+       char *buf;
+
+       if (trace_flags & (1 << index))
+               buf = "1\n";
+       else
+               buf = "0\n";
+
+       return simple_read_from_buffer(ubuf, cnt, ppos, buf, 2);
+}
+
+static ssize_t
+trace_options_core_write(struct file *filp, const char __user *ubuf, size_t cnt,
+                        loff_t *ppos)
+{
+       long index = (long)filp->private_data;
+       char buf[64];
+       unsigned long val;
+       int ret;
+
+       if (cnt >= sizeof(buf))
+               return -EINVAL;
+
+       if (copy_from_user(&buf, ubuf, cnt))
+               return -EFAULT;
+
+       buf[cnt] = 0;
+
+       ret = strict_strtoul(buf, 10, &val);
+       if (ret < 0)
+               return ret;
+
+       switch (val) {
+       case 0:
+               trace_flags &= ~(1 << index);
+               break;
+       case 1:
+               trace_flags |= 1 << index;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       *ppos += cnt;
+
+       return cnt;
+}
+
+static const struct file_operations trace_options_core_fops = {
+       .open = tracing_open_generic,
+       .read = trace_options_core_read,
+       .write = trace_options_core_write,
+};
+
+static struct dentry *trace_options_init_dentry(void)
+{
+       struct dentry *d_tracer;
+       static struct dentry *t_options;
+
+       if (t_options)
+               return t_options;
+
+       d_tracer = tracing_init_dentry();
+       if (!d_tracer)
+               return NULL;
+
+       t_options = debugfs_create_dir("options", d_tracer);
+       if (!t_options) {
+               pr_warning("Could not create debugfs directory 'options'\n");
+               return NULL;
+       }
+
+       return t_options;
+}
+
+static void
+create_trace_option_file(struct trace_option_dentry *topt,
+                        struct tracer_flags *flags,
+                        struct tracer_opt *opt)
+{
+       struct dentry *t_options;
+       struct dentry *entry;
+
+       t_options = trace_options_init_dentry();
+       if (!t_options)
+               return;
+
+       topt->flags = flags;
+       topt->opt = opt;
+
+       entry = debugfs_create_file(opt->name, 0644, t_options, topt,
+                                   &trace_options_fops);
+
+       topt->entry = entry;
+
+}
+
+static struct trace_option_dentry *
+create_trace_option_files(struct tracer *tracer)
+{
+       struct trace_option_dentry *topts;
+       struct tracer_flags *flags;
+       struct tracer_opt *opts;
+       int cnt;
+
+       if (!tracer)
+               return NULL;
+
+       flags = tracer->flags;
+
+       if (!flags || !flags->opts)
+               return NULL;
+
+       opts = flags->opts;
+
+       for (cnt = 0; opts[cnt].name; cnt++)
+               ;
+
+       topts = kcalloc(cnt + 1, sizeof(*topts), GFP_KERNEL);
+       if (!topts)
+               return NULL;
+
+       for (cnt = 0; opts[cnt].name; cnt++)
+               create_trace_option_file(&topts[cnt], flags,
+                                        &opts[cnt]);
+
+       return topts;
+}
+
+static void
+destroy_trace_option_files(struct trace_option_dentry *topts)
+{
+       int cnt;
+
+       if (!topts)
+               return;
+
+       for (cnt = 0; topts[cnt].opt; cnt++) {
+               if (topts[cnt].entry)
+                       debugfs_remove(topts[cnt].entry);
+       }
+
+       kfree(topts);
+}
+
+static struct dentry *
+create_trace_option_core_file(const char *option, long index)
+{
+       struct dentry *t_options;
+       struct dentry *entry;
+
+       t_options = trace_options_init_dentry();
+       if (!t_options)
+               return NULL;
+
+       entry = debugfs_create_file(option, 0644, t_options, (void *)index,
+                                   &trace_options_core_fops);
+
+       return entry;
+}
+
+static __init void create_trace_options_dir(void)
+{
+       struct dentry *t_options;
+       struct dentry *entry;
+       int i;
+
+       t_options = trace_options_init_dentry();
+       if (!t_options)
+               return;
+
+       for (i = 0; trace_options[i]; i++) {
+               entry = create_trace_option_core_file(trace_options[i], i);
+               if (!entry)
+                       pr_warning("Could not create debugfs %s entry\n",
+                                  trace_options[i]);
+       }
+}
+
 static __init int tracer_init_debugfs(void)
 {
        struct dentry *d_tracer;
        struct dentry *entry;
+       int cpu;
 
        d_tracer = tracing_init_dentry();
 
@@ -2896,18 +3414,15 @@ static __init int tracer_init_debugfs(void)
        if (!entry)
                pr_warning("Could not create debugfs 'trace_options' entry\n");
 
+       create_trace_options_dir();
+
        entry = debugfs_create_file("tracing_cpumask", 0644, d_tracer,
                                    NULL, &tracing_cpumask_fops);
        if (!entry)
                pr_warning("Could not create debugfs 'tracing_cpumask' entry\n");
 
-       entry = debugfs_create_file("latency_trace", 0444, d_tracer,
-                                   &global_trace, &tracing_lt_fops);
-       if (!entry)
-               pr_warning("Could not create debugfs 'latency_trace' entry\n");
-
        entry = debugfs_create_file("trace", 0444, d_tracer,
-                                   &global_trace, &tracing_fops);
+                                (void *) TRACE_PIPE_ALL_CPU, &tracing_fops);
        if (!entry)
                pr_warning("Could not create debugfs 'trace' entry\n");
 
@@ -2938,8 +3453,8 @@ static __init int tracer_init_debugfs(void)
        if (!entry)
                pr_warning("Could not create debugfs 'README' entry\n");
 
-       entry = debugfs_create_file("trace_pipe", 0644, d_tracer,
-                                   NULL, &tracing_pipe_fops);
+       entry = debugfs_create_file("trace_pipe", 0444, d_tracer,
+                       (void *) TRACE_PIPE_ALL_CPU, &tracing_pipe_fops);
        if (!entry)
                pr_warning("Could not create debugfs "
                           "'trace_pipe' entry\n");
@@ -2967,6 +3482,10 @@ static __init int tracer_init_debugfs(void)
 #ifdef CONFIG_SYSPROF_TRACER
        init_tracer_sysprof_debugfs(d_tracer);
 #endif
+
+       for_each_tracing_cpu(cpu)
+               tracing_init_debugfs_percpu(cpu);
+
        return 0;
 }
 
@@ -3105,7 +3624,7 @@ trace_printk_seq(struct trace_seq *s)
 
        printk(KERN_TRACE "%s", s->buffer);
 
-       trace_seq_reset(s);
+       trace_seq_init(s);
 }
 
 void ftrace_dump(void)
@@ -3190,8 +3709,12 @@ __init static int tracer_alloc_buffers(void)
        if (!alloc_cpumask_var(&tracing_cpumask, GFP_KERNEL))
                goto out_free_buffer_mask;
 
+       if (!alloc_cpumask_var(&tracing_reader_cpumask, GFP_KERNEL))
+               goto out_free_tracing_cpumask;
+
        cpumask_copy(tracing_buffer_mask, cpu_possible_mask);
        cpumask_copy(tracing_cpumask, cpu_all_mask);
+       cpumask_clear(tracing_reader_cpumask);
 
        /* TODO: make the number of buffers hot pluggable with CPUS */
        global_trace.buffer = ring_buffer_alloc(trace_buf_size,
@@ -3240,6 +3763,8 @@ __init static int tracer_alloc_buffers(void)
        ret = 0;
 
 out_free_cpumask:
+       free_cpumask_var(tracing_reader_cpumask);
+out_free_tracing_cpumask:
        free_cpumask_var(tracing_cpumask);
 out_free_buffer_mask:
        free_cpumask_var(tracing_buffer_mask);
This page took 0.042717 seconds and 5 git commands to generate.