oprofile: adding cpu_buffer_entries()
[linux-2.6-block.git] / drivers / oprofile / cpu_buffer.h
index 9c44d004da69c6a3747d5b9a4976be7f63c0d099..6055b5678394bc51fa0ddab7235c15df92682b01 100644 (file)
@@ -15,9 +15,9 @@
 #include <linux/workqueue.h>
 #include <linux/cache.h>
 #include <linux/sched.h>
+
 struct task_struct;
+
 int alloc_cpu_buffers(void);
 void free_cpu_buffers(void);
 
@@ -31,15 +31,15 @@ struct op_sample {
        unsigned long eip;
        unsigned long event;
 };
+
 struct oprofile_cpu_buffer {
        volatile unsigned long head_pos;
        volatile unsigned long tail_pos;
        unsigned long buffer_size;
-       struct task_struct * last_task;
+       struct task_struct *last_task;
        int last_is_kernel;
        int tracing;
-       struct op_sample * buffer;
+       struct op_sample *buffer;
        unsigned long sample_received;
        unsigned long sample_lost_overflow;
        unsigned long backtrace_aborted;
@@ -50,7 +50,63 @@ struct oprofile_cpu_buffer {
 
 DECLARE_PER_CPU(struct oprofile_cpu_buffer, cpu_buffer);
 
-void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf);
+void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf);
+
+static inline
+struct op_sample *cpu_buffer_write_entry(struct oprofile_cpu_buffer *cpu_buf)
+{
+       return &cpu_buf->buffer[cpu_buf->head_pos];
+}
+
+static inline
+void cpu_buffer_write_commit(struct oprofile_cpu_buffer *b)
+{
+       unsigned long new_head = b->head_pos + 1;
+
+       /*
+        * Ensure anything written to the slot before we increment is
+        * visible
+        */
+       wmb();
+
+       if (new_head < b->buffer_size)
+               b->head_pos = new_head;
+       else
+               b->head_pos = 0;
+}
+
+static inline
+struct op_sample *cpu_buffer_read_entry(struct oprofile_cpu_buffer *cpu_buf)
+{
+       return &cpu_buf->buffer[cpu_buf->tail_pos];
+}
+
+/* "acquire" as many cpu buffer slots as we can */
+static inline
+unsigned long cpu_buffer_entries(struct oprofile_cpu_buffer *b)
+{
+       unsigned long head = b->head_pos;
+       unsigned long tail = b->tail_pos;
+
+       /*
+        * Subtle. This resets the persistent last_task
+        * and in_kernel values used for switching notes.
+        * BUT, there is a small window between reading
+        * head_pos, and this call, that means samples
+        * can appear at the new head position, but not
+        * be prefixed with the notes for switching
+        * kernel mode or a task switch. This small hole
+        * can lead to mis-attribution or samples where
+        * we don't know if it's in the kernel or not,
+        * at the start of an event buffer.
+        */
+       cpu_buffer_reset(b);
+
+       if (head >= tail)
+               return head - tail;
+
+       return head + (b->buffer_size - tail);
+}
 
 /* transient events for the CPU buffer -> event buffer */
 #define CPU_IS_KERNEL 1