static DEFINE_SPINLOCK(task_mortuary);
static void process_task_mortuary(void);
-
/* Take ownership of the task struct and place it on the
* list for processing. Only after two full buffer syncs
* does the task eventually get freed, because by then
* Add IBS fetch and op entries to event buffer
*/
static void add_ibs_begin(struct oprofile_cpu_buffer *cpu_buf, int code,
- int in_kernel, struct mm_struct *mm)
+ struct mm_struct *mm)
{
unsigned long rip;
int i, count;
struct task_struct *new;
unsigned long cookie = 0;
int in_kernel = 1;
- unsigned int i;
sync_buffer_state state = sb_buffer_start;
+ #ifndef CONFIG_OPROFILE_IBS
+ unsigned int i;
unsigned long available;
+ #endif
mutex_lock(&buffer_mutex);
/* Remember, only we can modify tail_pos */
+ #ifndef CONFIG_OPROFILE_IBS
available = get_slots(cpu_buf);
for (i = 0; i < available; ++i) {
+ #else
+ while (get_slots(cpu_buf)) {
+ #endif
struct op_sample *s = &cpu_buf->buffer[cpu_buf->tail_pos];
if (is_code(s->eip)) {
#ifdef CONFIG_OPROFILE_IBS
} else if (s->event == IBS_FETCH_BEGIN) {
state = sb_bt_start;
- add_ibs_begin(cpu_buf,
- IBS_FETCH_CODE, in_kernel, mm);
+ add_ibs_begin(cpu_buf, IBS_FETCH_CODE, mm);
} else if (s->event == IBS_OP_BEGIN) {
state = sb_bt_start;
- add_ibs_begin(cpu_buf,
- IBS_OP_CODE, in_kernel, mm);
+ add_ibs_begin(cpu_buf, IBS_OP_CODE, mm);
#endif
} else {
struct mm_struct *oldmm = mm;
mutex_unlock(&buffer_mutex);
}
+
+/* The function can be used to add a buffer worth of data directly to
+ * the kernel buffer. The buffer is assumed to be a circular buffer.
+ * Take the entries from index start and end at index end, wrapping
+ * at max_entries.
+ */
+void oprofile_put_buff(unsigned long *buf, unsigned int start,
+ unsigned int stop, unsigned int max)
+{
+ int i;
+
+ i = start;
+
+ mutex_lock(&buffer_mutex);
+ while (i != stop) {
+ add_event_entry(buf[i++]);
+
+ if (i >= max)
+ i = 0;
+ }
+
+ mutex_unlock(&buffer_mutex);
+}
+
#include <linux/oprofile.h>
#include <linux/vmalloc.h>
#include <linux/errno.h>
-
+
#include "event_buffer.h"
#include "cpu_buffer.h"
#include "buffer_sync.h"
{
int i;
- for_each_online_cpu(i) {
+ for_each_possible_cpu(i) {
vfree(per_cpu(cpu_buffer, i).buffer);
per_cpu(cpu_buffer, i).buffer = NULL;
}
}
+unsigned long oprofile_get_cpu_buffer_size(void)
+{
+ return fs_cpu_buffer_size;
+}
+
+void oprofile_cpu_buffer_inc_smpl_lost(void)
+{
+ struct oprofile_cpu_buffer *cpu_buf
+ = &__get_cpu_var(cpu_buffer);
+
+ cpu_buf->sample_lost_overflow++;
+}
+
int alloc_cpu_buffers(void)
{
int i;
-
+
unsigned long buffer_size = fs_cpu_buffer_size;
-
- for_each_online_cpu(i) {
+
+ for_each_possible_cpu(i) {
struct oprofile_cpu_buffer *b = &per_cpu(cpu_buffer, i);
-
+
b->buffer = vmalloc_node(sizeof(struct op_sample) * buffer_size,
cpu_to_node(i));
if (!b->buffer)
goto fail;
-
+
b->last_task = NULL;
b->last_is_kernel = -1;
b->tracing = 0;
}
/* Resets the cpu buffer to a sane state. */
- void cpu_buffer_reset(struct oprofile_cpu_buffer * cpu_buf)
+ void cpu_buffer_reset(struct oprofile_cpu_buffer *cpu_buf)
{
/* reset these to invalid values; the next sample
* collected will populate the buffer with proper
}
/* compute number of available slots in cpu_buffer queue */
- static unsigned long nr_available_slots(struct oprofile_cpu_buffer const * b)
+ static unsigned long nr_available_slots(struct oprofile_cpu_buffer const *b)
{
unsigned long head = b->head_pos;
unsigned long tail = b->tail_pos;
return tail + (b->buffer_size - head) - 1;
}
- static void increment_head(struct oprofile_cpu_buffer * b)
+ static void increment_head(struct oprofile_cpu_buffer *b)
{
unsigned long new_head = b->head_pos + 1;
}
static inline void
- add_sample(struct oprofile_cpu_buffer * cpu_buf,
- unsigned long pc, unsigned long event)
+ add_sample(struct oprofile_cpu_buffer *cpu_buf,
+ unsigned long pc, unsigned long event)
{
- struct op_sample * entry = &cpu_buf->buffer[cpu_buf->head_pos];
+ struct op_sample *entry = &cpu_buf->buffer[cpu_buf->head_pos];
entry->eip = pc;
entry->event = event;
increment_head(cpu_buf);
}
static inline void
- add_code(struct oprofile_cpu_buffer * buffer, unsigned long value)
+ add_code(struct oprofile_cpu_buffer *buffer, unsigned long value)
{
add_sample(buffer, ESCAPE_CODE, value);
}
* pc. We tag this in the buffer by generating kernel enter/exit
* events whenever is_kernel changes
*/
- static int log_sample(struct oprofile_cpu_buffer * cpu_buf, unsigned long pc,
+ static int log_sample(struct oprofile_cpu_buffer *cpu_buf, unsigned long pc,
int is_kernel, unsigned long event)
{
- struct task_struct * task;
+ struct task_struct *task;
cpu_buf->sample_received++;
cpu_buf->last_task = task;
add_code(cpu_buf, (unsigned long)task);
}
-
+
add_sample(cpu_buf, pc, event);
return 1;
}
return 1;
}
- static void oprofile_end_trace(struct oprofile_cpu_buffer * cpu_buf)
+ static void oprofile_end_trace(struct oprofile_cpu_buffer *cpu_buf)
{
cpu_buf->tracing = 0;
}
#ifdef CONFIG_OPROFILE_IBS
- #define MAX_IBS_SAMPLE_SIZE 14
- static int log_ibs_sample(struct oprofile_cpu_buffer *cpu_buf,
- unsigned long pc, int is_kernel, unsigned int *ibs, int ibs_code)
+ #define MAX_IBS_SAMPLE_SIZE 14
+
+ void oprofile_add_ibs_sample(struct pt_regs *const regs,
+ unsigned int *const ibs_sample, int ibs_code)
{
+ int is_kernel = !user_mode(regs);
+ struct oprofile_cpu_buffer *cpu_buf = &__get_cpu_var(cpu_buffer);
struct task_struct *task;
cpu_buf->sample_received++;
if (nr_available_slots(cpu_buf) < MAX_IBS_SAMPLE_SIZE) {
+ /* we can't backtrace since we lost the source of this event */
cpu_buf->sample_lost_overflow++;
- return 0;
+ return;
}
- is_kernel = !!is_kernel;
-
/* notice a switch from user->kernel or vice versa */
if (cpu_buf->last_is_kernel != is_kernel) {
cpu_buf->last_is_kernel = is_kernel;
/* notice a task switch */
if (!is_kernel) {
task = current;
-
if (cpu_buf->last_task != task) {
cpu_buf->last_task = task;
add_code(cpu_buf, (unsigned long)task);
}
add_code(cpu_buf, ibs_code);
- add_sample(cpu_buf, ibs[0], ibs[1]);
- add_sample(cpu_buf, ibs[2], ibs[3]);
- add_sample(cpu_buf, ibs[4], ibs[5]);
+ add_sample(cpu_buf, ibs_sample[0], ibs_sample[1]);
+ add_sample(cpu_buf, ibs_sample[2], ibs_sample[3]);
+ add_sample(cpu_buf, ibs_sample[4], ibs_sample[5]);
if (ibs_code == IBS_OP_BEGIN) {
- add_sample(cpu_buf, ibs[6], ibs[7]);
- add_sample(cpu_buf, ibs[8], ibs[9]);
- add_sample(cpu_buf, ibs[10], ibs[11]);
- }
-
- return 1;
- }
-
- void oprofile_add_ibs_sample(struct pt_regs *const regs,
- unsigned int * const ibs_sample, u8 code)
- {
- int is_kernel = !user_mode(regs);
- unsigned long pc = profile_pc(regs);
-
- struct oprofile_cpu_buffer *cpu_buf =
- &per_cpu(cpu_buffer, smp_processor_id());
-
- if (!backtrace_depth) {
- log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code);
- return;
+ add_sample(cpu_buf, ibs_sample[6], ibs_sample[7]);
+ add_sample(cpu_buf, ibs_sample[8], ibs_sample[9]);
+ add_sample(cpu_buf, ibs_sample[10], ibs_sample[11]);
}
- /* if log_sample() fails we can't backtrace since we lost the source
- * of this event */
- if (log_ibs_sample(cpu_buf, pc, is_kernel, ibs_sample, code))
+ if (backtrace_depth)
oprofile_ops.backtrace(regs, backtrace_depth);
}
*/
static void wq_sync_buffer(struct work_struct *work)
{
- struct oprofile_cpu_buffer * b =
+ struct oprofile_cpu_buffer *b =
container_of(work, struct oprofile_cpu_buffer, work.work);
if (b->cpu != smp_processor_id()) {
printk(KERN_DEBUG "WQ on CPU%d, prefer CPU%d\n",
smp_processor_id(), b->cpu);
+
+ if (!cpu_online(b->cpu)) {
+ cancel_delayed_work(&b->work);
+ return;
+ }
}
sync_buffer(b->cpu);