#include #include #include #include #include #include #include #include #include #ifdef CONFIG_MIPS #include #include #include #endif /*--- #ifdef CONFIG_MIPS ---*/ #include /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_AVM_SIMPLE_PROFILING) #include #include #include "avm_profile.h" #include "arch_profile.h" unsigned int profile_BlockNeeded = (CONFIG_AVM_PROFILING_TRACE_MODE * 10); static unsigned int profile_current_mask = 0; static enum _simple_profile_enable_mode profile_current_start_handle = 0; struct _simple_profiling simple_profiling; EXPORT_SYMBOL(simple_profiling); struct _cpu_nr_to_tc_core cpu_nr_to_tc_and_core[NR_CPUS]; /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline void profile_lock(spinlock_t *lock, unsigned long *flags) { #ifdef PROFILING_IN_YIELD if(is_yield_context()) { *flags = 0; __raw_spin_lock(&lock->raw_lock); return; } #endif/*--- #ifdef PROFILING_IN_YIELD ---*/ spin_lock_irqsave(lock, *flags); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline void profile_unlock(spinlock_t *lock, unsigned long *flags) { #ifdef PROFILING_IN_YIELD if(is_yield_context()) { __raw_spin_unlock(&lock->raw_lock); return; } #endif/*--- #ifdef PROFILING_IN_YIELD ---*/ spin_unlock_irqrestore(lock, *flags); } /*--------------------------------------------------------------------------------*\ * current, cpu_id, time und pos-Reservierung muessen atomar passieren! \*--------------------------------------------------------------------------------*/ static unsigned int lavm_simple_profiling_incpos(unsigned int *time, struct task_struct **cur_tsk, int *cpu_id, unsigned int *perfcnt1 __maybe_unused, unsigned int *perfcnt2 __maybe_unused, unsigned int *cpu_status __maybe_unused) { unsigned int pos = (unsigned int)-1; unsigned long flags; if(simple_profiling.enabled) { profile_lock(&simple_profiling.lock, &flags); if(cpu_status) *cpu_status = avm_profile_cpu_status(flags); *time = avm_get_cycles(); if(cur_tsk) *cur_tsk = current; if(cpu_id) *cpu_id = raw_smp_processor_id(); #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) if(perfcnt1) *perfcnt1 = avm_profile_sdramacess(); if(perfcnt2) *perfcnt2 = avm_profile_sdramactivate(); #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ pos = atomic_read(&simple_profiling.pos); atomic_inc(&simple_profiling.pos); if(simple_profiling.enabled == 1) { if(pos >= simple_profiling.len) { simple_profiling.enabled = 0; atomic_set(&simple_profiling.pos, simple_profiling.len - 1); simple_profiling.end_time = jiffies | 1; profile_unlock(&simple_profiling.lock, &flags); printk(KERN_INFO"[simple-profiling]buffer full: %u entries recorded\n", simple_profiling.len); return (unsigned int)-1; } } else if(simple_profiling.enabled == 2) { if(pos >= simple_profiling.len - 1) { if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies | 1; simple_profiling.wraparround = 1; atomic_set(&simple_profiling.pos, 0); profile_unlock(&simple_profiling.lock, &flags); printk(KERN_INFO"[simple-profiling]wraparround: %u entries recorded\n", simple_profiling.len); return pos; } } profile_unlock(&simple_profiling.lock, &flags); } return pos; } /*------------------------------------------------------------------------------------------*\ * addr = interrupted epc \*------------------------------------------------------------------------------------------*/ void __avm_simple_profiling_irqcontext(unsigned int epc __maybe_unused) { #if !defined(PROFILING_IN_YIELD) struct task_struct *cur_tsk; int cpu_id; unsigned int pos, time, total_access, total_activate, cpu_status; struct _avm_profile_data *data; if(!(simple_profiling.mask & (1 << avm_profile_data_type_code_address_info))) return; pos = lavm_simple_profiling_incpos(&time, &cur_tsk, &cpu_id, &total_access, &total_activate, &cpu_status); if(pos == (unsigned int)-1) { return; } data = &((struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]))[pos % profile_DataSetsPerBlock]; data->id = cur_tsk ? task_pid_nr(cur_tsk) & 0xFFFF : 0; #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) strncpy(data->comm, cur_tsk ? cur_tsk->comm : "", sizeof(data->comm)); #endif/*--- #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) ---*/ data->type = avm_profile_data_type_code_address_info; data->cpu_id = cpu_id; data->core_id = cpu_nr_to_tc_and_core[cpu_id].core; data->tc_id = cpu_nr_to_tc_and_core[cpu_id].tc; data->lr = (unsigned int) (cur_tsk ? KSTK_RA(cur_tsk) : 0); data->addr = epc; data->time = time; data->cpu_status = cpu_status; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access = total_access; data->total_activate = total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ data->cpu_status = cpu_status; #endif/*--- #if !defined(PROFILING_IN_YIELD) ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __avm_simple_profiling_skb(unsigned int addr, unsigned int where __maybe_unused, struct sk_buff *skb) { struct task_struct *cur_tsk; unsigned int pos, time; int cpu_id; struct _avm_profile_data *data; if(!(simple_profiling.mask & (1 << avm_profile_data_type_trace_skb))) return; pos = lavm_simple_profiling_incpos(&time, &cur_tsk, &cpu_id, NULL, NULL, NULL); if(pos == (unsigned int)-1) { return; } data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); data->id = skb_cloned(skb) & 0xFFFF; data->cpu_id = cpu_id; data->core_id = cpu_nr_to_tc_and_core[cpu_id].core; data->tc_id = cpu_nr_to_tc_and_core[cpu_id].tc; data->type = avm_profile_data_type_trace_skb; data->addr = addr; data->lr = 0; data->time = time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access = skb->uniq_id & 0xFFFFFF; data->total_activate = where; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ } EXPORT_SYMBOL(__avm_simple_profiling_skb); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void __avm_simple_profiling_enter_irq(unsigned int irq){ struct irq_desc *desc; desc = irq_to_desc(irq); avm_simple_profiling_log(avm_profile_data_type_hw_irq_begin, (unsigned int)desc, irq); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void __avm_simple_profiling_leave_irq(unsigned int irq){ struct irq_desc *desc; desc = irq_to_desc(irq); avm_simple_profiling_log(avm_profile_data_type_hw_irq_end, (unsigned int)desc, irq); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __avm_simple_profiling_log(enum _avm_profile_data_type type, unsigned int addr, unsigned int id) { struct task_struct *tsk; unsigned int pos, time, total_access, total_activate, cpu_status, lr = 0; int cpu_id; struct _avm_profile_data *data; if(!(simple_profiling.mask & (1 << type))) { return; } pos = lavm_simple_profiling_incpos(&time, &tsk, &cpu_id, &total_access, &total_activate, &cpu_status); if(pos == (unsigned int)-1) { return; } data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); switch(type) { default: case avm_profile_data_type_unknown: data->type = type; break; case avm_profile_data_type_code_begin: case avm_profile_data_type_code_end: tsk = (struct task_struct *)addr; addr = (unsigned int)KSTK_EIP(tsk);/*--- ... aus dem task den EPC ermitteln ---*/ /*--- kein break; ---*/ case avm_profile_data_type_code_address_info: if(id != AVM_PROFILE_IDLE_ID) { /*--- if no reserved id used - remark with pid ---*/ id = tsk ? task_pid_nr(tsk) : 0; #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) strncpy(data->comm, tsk ? tsk->comm : "", sizeof(data->comm)); } else { data->comm[0] = 0; #endif/*--- #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) ---*/ } lr = (unsigned int)(tsk ? KSTK_RA(tsk) : 0); /*--- kein break; ---*/ case avm_profile_data_type_hw_irq_begin: case avm_profile_data_type_hw_irq_end: case avm_profile_data_type_sw_irq_begin: case avm_profile_data_type_sw_irq_end: case avm_profile_data_type_timer_begin: case avm_profile_data_type_timer_end: case avm_profile_data_type_tasklet_begin: case avm_profile_data_type_tasklet_end: case avm_profile_data_type_hi_tasklet_begin: case avm_profile_data_type_hi_tasklet_end: case avm_profile_data_type_workitem_begin: case avm_profile_data_type_workitem_end: case avm_profile_data_type_func_begin: case avm_profile_data_type_func_end: case avm_profile_data_type_trigger_tasklet_begin: case avm_profile_data_type_trigger_tasklet_end: case avm_profile_data_type_trigger_user_begin: case avm_profile_data_type_trigger_user_end: data->id = id & 0xFFFF; data->cpu_id = cpu_id; data->core_id = cpu_nr_to_tc_and_core[cpu_id].core; data->tc_id = cpu_nr_to_tc_and_core[cpu_id].tc; data->type = type; data->addr = addr; data->lr = lr; data->time = time; data->cpu_status = cpu_status; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access = total_access; data->total_activate = total_activate; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ break; } } EXPORT_SYMBOL(__avm_simple_profiling_log); #if defined(PROFILING_IN_YIELD) /*------------------------------------------------------------------------------------------*\ * Code-Watch aus YIELD \*------------------------------------------------------------------------------------------*/ void __avm_simple_profiling_code_from_other_context(unsigned int pc, unsigned int lr, struct task_struct *curr_tsk, int cpu_id, int core, int tc_id, unsigned int perfcnt1, unsigned int perfcnt2) { struct _avm_profile_data *data; unsigned int pos, time, cpu_status; int id = 0; if(!(simple_profiling.mask & (1 << avm_profile_data_type_code_address_info))) { return; } pos = lavm_simple_profiling_incpos(&time, NULL, NULL, NULL, NULL, &cpu_status); if(pos == (unsigned int)-1) { return; } data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); if(curr_tsk) { id = task_pid_nr(curr_tsk); #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) strncpy(data->comm, curr_tsk->comm, sizeof(data->comm)); } else { data->comm[0] = 0; #endif/*--- #if defined(AVM_PROFILE_CURRENT_COMM_INCLUDED) ---*/ } data->id = id & 0xFFFF; data->cpu_id = cpu_id; data->core_id = core; data->tc_id = tc_id; data->type = avm_profile_data_type_code_address_info; data->addr = pc; data->lr = lr; data->time = time; data->cpu_status = cpu_status; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access = perfcnt1; data->total_activate = perfcnt2; #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ /*--- printk(KERN_ERR"cpu_id=%x core_id=%x tc_id=%x id=%x addr=%08x current=%p perfcnt1=%x perfcnt2=%x\n", cpu_id, core, tc_id, id, pc, curr_tsk, perfcnt1, perfcnt2); ---*/ } #endif/*--- #if defined(PROFILING_IN_YIELD) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int alloc_profiler_memory(void) { unsigned int i; simple_profiling.data = (void **)kmalloc(sizeof(void *) * profile_BlockNeeded, GFP_ATOMIC); if(simple_profiling.data == NULL) { return -ENOMEM; } memset(simple_profiling.data, 0, sizeof(void *) * profile_BlockNeeded); simple_profiling.len = profile_DataSetsPerBlock * profile_BlockNeeded; for(i = 0 ; i < profile_BlockNeeded ; i++) { struct page *ptr = alloc_pages(__GFP_NORETRY, 0); if(ptr == NULL) { simple_profiling.len = profile_DataSetsPerBlock * i; printk(KERN_INFO"[simple-profiling]resize %d pages instead %d pages\n", i, profile_BlockNeeded); profile_BlockNeeded = i; break; } simple_profiling.data[i] = (void *)lowmem_page_address(ptr); /*--- printk("\t%d: %x ==> %x\n", i, ptr, simple_profiling.data[i]); ---*/ } printk(KERN_INFO"[simple-profiling]need %d pages for %d entries (%d samples per page)\n", i, simple_profiling.len, profile_DataSetsPerBlock); return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void free_profiler_memory(void) { unsigned int i; if(simple_profiling.data == NULL) { return; } for(i = 0 ; i < profile_BlockNeeded ; i++) { free_pages((unsigned int)simple_profiling.data[i], 0); } kfree(simple_profiling.data); simple_profiling.data = NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void avm_simple_profiling_memresize(unsigned int BlockNeeded) { if(profile_BlockNeeded != BlockNeeded) { avm_simple_profiling_enable(sp_enable_off, 0, 0, NULL, NULL, 1); free_profiler_memory(); atomic_set(&simple_profiling.busy, 0); profile_BlockNeeded = BlockNeeded; } } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static inline __u32 CLK_TO_MSEC(__u64 cycles) { do_div(cycles, gCycle_per_usec * 1000UL); return (__u32)cycles; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ unsigned int timediff_msec(unsigned int count) { unsigned long i, msec = 0, last_time = 0; unsigned long long cycle_time = 0; for(i = 0 ; i < count ; i += 100 /*--- not every entry ---*/ ) { struct _avm_profile_data *pdata = avm_simple_profiling_by_idx(i); if(pdata) { if(likely(last_time)) { unsigned long diff_time = (pdata->time - last_time); cycle_time += (__u64)diff_time; } last_time = pdata->time; } } if(gCycle_per_usec) { msec = CLK_TO_MSEC(cycle_time); } return msec; } /*------------------------------------------------------------------------------------------*\ * on = 1: normal mode * on = 2: wraparround * on = 3: Uart-Trace * enable_perfcnt: Performance-Counter nutzen * setbusy: busy-Flag setzen * timediff in msec \*------------------------------------------------------------------------------------------*/ int avm_simple_profiling_enable(enum _simple_profile_enable_mode on, unsigned int enable_perfcnt, unsigned int mask, unsigned int *count, unsigned long *timediff, unsigned int set_busy) { unsigned int _count, disabled=0; /*--- printk(KERN_ERR"[simple-profiling]%s on=%u\n", __func__, on); ---*/ if(on) { if(atomic_add_return(1, &simple_profiling.busy) > 1) { printk(KERN_ERR"[simple-profiling]analyzing busy - can't enable profiler, try it later\n"); return -1; } if(mask) { profile_current_mask = mask; profile_current_start_handle = on; } if(simple_profiling.data == NULL) { if(alloc_profiler_memory()) { printk(KERN_ERR"[simple-profiling]can't enable profiler (memory-error)\n"); return -1; } } atomic_set(&simple_profiling.pos, 0); simple_profiling.start_time = jiffies; simple_profiling.end_time = 0; simple_profiling.wraparround = 0; simple_profiling.mask = mask; atomic_set(&simple_profiling.busy, 0); switch (on) { default: printk(KERN_ERR "[simple-profiling]%s Unknown profiling mode. Assuming normal profiling without wraparround.\n", __func__); case sp_enable_on: simple_profiling.enabled = 1; break; case sp_enable_wrap: case sp_enable_uart: simple_profiling.enabled = 2; /*--- wrap-arround mode ---*/ break; } if(count) *count = 0; if(timediff)*timediff = 0; if(arch_profile_ctrl.profiling_special_enable) { arch_profile_ctrl.profiling_special_enable(on, enable_perfcnt); } printk(KERN_INFO"[simple-profiling]profiler on %u free entries %s\n", simple_profiling.len, simple_profiling.enabled == 2 ? "(wrap-mode)" : ""); return 0; } if(set_busy) { atomic_set(&simple_profiling.busy, 1); } disabled = simple_profiling.enabled; if(simple_profiling.enabled) { simple_profiling.enabled = 0; if(arch_profile_ctrl.profiling_special_enable) { arch_profile_ctrl.profiling_special_enable(0, enable_perfcnt); } if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies; } if(simple_profiling.wraparround == 0) { _count = atomic_read(&simple_profiling.pos); } else { _count = simple_profiling.len; } if(count) { *count = _count; } if(timediff) { *timediff = timediff_msec(_count); } if(disabled) { printk(KERN_INFO"[simple-profiling]profiler off: %u entries\n", _count); } return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_simple_profiling_restart(void) { if(profile_current_start_handle || profile_current_mask) { /*--- printk("[%s] Push-Button triggering profile %d mask=0x%x\nUse:\n" ---*/ /*--- " 'echo profiler 0 >/dev/debug' to stop profiling\n" ---*/ /*--- " 'echo profiler 2 >/dev/debug' to write results to /var/profile.csv\n", ---*/ /*--- __FUNCTION__, profile_current_start_handle, profile_current_mask); ---*/ avm_simple_profiling_enable(profile_current_start_handle, 1, profile_current_mask, NULL, NULL, 0); } else { printk(KERN_INFO"[%s] Push-Button for profiling ignored (not initialized)\n", __func__); } return; } EXPORT_SYMBOL(avm_simple_profiling_restart); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _avm_profile_data *avm_simple_profiling_by_idx(unsigned int idx) { unsigned int pos; struct _avm_profile_data *data; if(simple_profiling.wraparround == 0) { if(idx < (unsigned int)atomic_read(&simple_profiling.pos)) { data = ((struct _avm_profile_data *)(simple_profiling.data[idx / profile_DataSetsPerBlock]) + (idx % profile_DataSetsPerBlock)); return data; } return NULL; } pos = atomic_read(&simple_profiling.pos) + idx; if(pos >= simple_profiling.len) { pos -= simple_profiling.len; if(pos >= (unsigned int)atomic_read(&simple_profiling.pos)) { return NULL; } } data = ((struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock)); return data; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_simple_profiling_init(void) { spin_lock_init(&simple_profiling.lock); simple_profiling.len = 0; simple_profiling.enabled = 0; atomic_set(&simple_profiling.pos, 0); atomic_set(&simple_profiling.busy, 0); gCycle_per_usec = avm_get_cyclefreq() / (1000 * 1000); /*--- printk(KERN_INFO "[simple-profiling] %u entries %u min\n", simple_profiling.len, CONFIG_AVM_PROFILING_TRACE_MODE); ---*/ return 0; } module_init(avm_simple_profiling_init); #endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/