tracing: Add internal tracing_snapshot() functions
[firefly-linux-kernel-4.4.55.git] / kernel / trace / trace.c
index c547ebbe36ffe6414c27ec4cc02eb77940629230..307524d784ecbeda1a7a0ca05178dbb44f8faed1 100644 (file)
@@ -339,6 +339,90 @@ void tracing_on(void)
 }
 EXPORT_SYMBOL_GPL(tracing_on);
 
+#ifdef CONFIG_TRACER_SNAPSHOT
+/**
+ * trace_snapshot - take a snapshot of the current buffer.
+ *
+ * This causes a swap between the snapshot buffer and the current live
+ * tracing buffer. You can use this to take snapshots of the live
+ * trace when some condition is triggered, but continue to trace.
+ *
+ * Note, make sure to allocate the snapshot with either
+ * a tracing_snapshot_alloc(), or by doing it manually
+ * with: echo 1 > /sys/kernel/debug/tracing/snapshot
+ *
+ * If the snapshot buffer is not allocated, it will stop tracing.
+ * Basically making a permanent snapshot.
+ */
+void tracing_snapshot(void)
+{
+       struct trace_array *tr = &global_trace;
+       struct tracer *tracer = tr->current_trace;
+       unsigned long flags;
+
+       if (!tr->allocated_snapshot) {
+               trace_printk("*** SNAPSHOT NOT ALLOCATED ***\n");
+               trace_printk("*** stopping trace here!   ***\n");
+               tracing_off();
+               return;
+       }
+
+       /* Note, snapshot can not be used when the tracer uses it */
+       if (tracer->use_max_tr) {
+               trace_printk("*** LATENCY TRACER ACTIVE ***\n");
+               trace_printk("*** Can not use snapshot (sorry) ***\n");
+               return;
+       }
+
+       local_irq_save(flags);
+       update_max_tr(tr, current, smp_processor_id());
+       local_irq_restore(flags);
+}
+
+static int resize_buffer_duplicate_size(struct trace_buffer *trace_buf,
+                                       struct trace_buffer *size_buf, int cpu_id);
+
+/**
+ * trace_snapshot_alloc - allocate and take a snapshot of the current buffer.
+ *
+ * This is similar to trace_snapshot(), but it will allocate the
+ * snapshot buffer if it isn't already allocated. Use this only
+ * where it is safe to sleep, as the allocation may sleep.
+ *
+ * This causes a swap between the snapshot buffer and the current live
+ * tracing buffer. You can use this to take snapshots of the live
+ * trace when some condition is triggered, but continue to trace.
+ */
+void tracing_snapshot_alloc(void)
+{
+       struct trace_array *tr = &global_trace;
+       int ret;
+
+       if (!tr->allocated_snapshot) {
+
+               /* allocate spare buffer */
+               ret = resize_buffer_duplicate_size(&tr->max_buffer,
+                                  &tr->trace_buffer, RING_BUFFER_ALL_CPUS);
+               if (WARN_ON(ret < 0))
+                       return;
+
+               tr->allocated_snapshot = true;
+       }
+
+       tracing_snapshot();
+}
+#else
+void tracing_snapshot(void)
+{
+       WARN_ONCE(1, "Snapshot feature not enabled, but internal snapshot used");
+}
+void tracing_snapshot_alloc(void)
+{
+       /* Give warning */
+       tracing_snapshot();
+}
+#endif /* CONFIG_TRACER_SNAPSHOT */
+
 /**
  * tracing_off - turn off tracing buffers
  *
@@ -2613,6 +2697,8 @@ __tracing_open(struct inode *inode, struct file *file, bool snapshot)
                tracing_iter_reset(iter, cpu);
        }
 
+       tr->ref++;
+
        mutex_unlock(&trace_types_lock);
 
        return iter;
@@ -2649,6 +2735,10 @@ static int tracing_release(struct inode *inode, struct file *file)
        tr = iter->tr;
 
        mutex_lock(&trace_types_lock);
+
+       WARN_ON(!tr->ref);
+       tr->ref--;
+
        for_each_tracing_cpu(cpu) {
                if (iter->buffer_iter[cpu])
                        ring_buffer_read_finish(iter->buffer_iter[cpu]);
@@ -4460,6 +4550,10 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp)
        if (!info)
                return -ENOMEM;
 
+       mutex_lock(&trace_types_lock);
+
+       tr->ref++;
+
        info->iter.tr           = tr;
        info->iter.cpu_file     = tc->cpu;
        info->iter.trace        = tr->current_trace;
@@ -4470,6 +4564,8 @@ static int tracing_buffers_open(struct inode *inode, struct file *filp)
 
        filp->private_data = info;
 
+       mutex_unlock(&trace_types_lock);
+
        return nonseekable_open(inode, filp);
 }
 
@@ -4568,10 +4664,17 @@ static int tracing_buffers_release(struct inode *inode, struct file *file)
        struct ftrace_buffer_info *info = file->private_data;
        struct trace_iterator *iter = &info->iter;
 
+       mutex_lock(&trace_types_lock);
+
+       WARN_ON(!iter->tr->ref);
+       iter->tr->ref--;
+
        if (info->spare)
                ring_buffer_free_read_page(iter->trace_buffer->buffer, info->spare);
        kfree(info);
 
+       mutex_unlock(&trace_types_lock);
+
        return 0;
 }
 
@@ -5411,6 +5514,10 @@ static int instance_delete(const char *name)
        if (!found)
                goto out_unlock;
 
+       ret = -EBUSY;
+       if (tr->ref)
+               goto out_unlock;
+
        list_del(&tr->list);
 
        event_trace_del_tracer(tr);