#include #include #include #include #include #include #include #include #ifdef CONFIG_MIPS #include #include #include #endif /*--- #ifdef CONFIG_MIPS ---*/ #include #ifdef CONFIG_LANTIQ #include #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD #include #ifndef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT int yield_thread_freq = 20 * 1000; /* --- freq in Hz -> this is 20 kHz --- */ int yield_freq_randomizer_percent = 25; module_param(yield_thread_freq, int, S_IRUSR | S_IWUSR); module_param(yield_freq_randomizer_percent , int, S_IRUSR | S_IWUSR); #endif #endif #if defined(CONFIG_AVM_SIMPLE_PROFILING) #include #include unsigned int profile_BlockNeeded = (CONFIG_AVM_PROFILING_TRACE_MODE * 10); unsigned int profile_current_mask = 0; unsigned int profile_current_start_handle = 0; extern unsigned long gCycle_per_usec; struct _simple_profiling simple_profiling; EXPORT_SYMBOL(simple_profiling); /*--------------------------------------------------------------------------------*\ * time und pos-Reservierung muessen atomar passieren! \*--------------------------------------------------------------------------------*/ static unsigned int lavm_simple_profiling_incpos(unsigned int *time) { unsigned int pos = (unsigned int)-1; unsigned long flags; if(simple_profiling.enabled == 1) { spin_lock_irqsave(&simple_profiling.lock, flags); *time = avm_profile_counter(); pos = simple_profiling.pos++; if(pos >= simple_profiling.len - 1) { simple_profiling.enabled = 0; simple_profiling.pos = simple_profiling.len - 1; spin_unlock_irqrestore(&simple_profiling.lock, flags); simple_profiling.end_time = jiffies | 1; printk(KERN_INFO"[simple_profiling] buffer full: %u entries recorded\n", simple_profiling.len); return (unsigned int)-1; } else { spin_unlock_irqrestore(&simple_profiling.lock, flags); } } else if(simple_profiling.enabled == 2) { spin_lock_irqsave(&simple_profiling.lock, flags); *time = avm_profile_counter(); pos = simple_profiling.pos++; if(pos >= simple_profiling.len) { if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies | 1; simple_profiling.wraparround = 1; pos = 0; simple_profiling.pos = 0; spin_unlock_irqrestore(&simple_profiling.lock, flags); printk(KERN_INFO"[simple_profiling] wraparround: %u entries recorded\n", simple_profiling.len); } else { spin_unlock_irqrestore(&simple_profiling.lock, flags); } } return pos; } /*------------------------------------------------------------------------------------------*\ * addr = interrupted epc \*------------------------------------------------------------------------------------------*/ unsigned int __avm_simple_profiling_irqcontext(unsigned int epc) { unsigned int pos, time; struct _avm_profile_data *data; if(!(simple_profiling.mask & (1 << avm_profile_data_type_code_address_info))) return 0; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return 0; data = &((struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]))[pos % profile_DataSetsPerBlock]; data->id = task_pid_nr(current) & 0xFFFF; data->type = avm_profile_data_type_code_address_info; data->cpu_id = smp_processor_id(); data->addr = epc; data->time = time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ return time; } /*--- EXPORT_SYMBOL(__avm_simple_profiling_irqcontext); ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD unsigned int avm_simple_profiling_yield(unsigned int epc, unsigned int cpu_id, unsigned int irq_num) { unsigned int pos, time; struct _avm_profile_data *data; if(simple_profiling.enabled == 0) return 0; if(!(simple_profiling.mask & (1 << avm_profile_data_type_code_address_yield))) return 0; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return 0; data = &((struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]))[pos % profile_DataSetsPerBlock]; /*--- data->curr_pid = task_pid_nr(current); ---*/ data->id = task_pid_nr(current) & 0XFFFF; data->cpu_id = cpu_id; data->type = avm_profile_data_type_code_address_info; data->addr = epc; data->time = time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT data->total_access = read_c0_perfcnt(0,2); data->total_activate = read_c0_perfcnt(1,2); #else data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #endif /*--- #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT ---*/ #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ return time; } #endif /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __avm_simple_profiling_skb(unsigned int addr, unsigned int where, struct sk_buff *skb) { unsigned int pos, time; struct _avm_profile_data *data; if(!(simple_profiling.mask & (1 << avm_profile_data_type_trace_skb))) return; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return; data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); /*--- data->curr_pid = task_pid_nr(current); ---*/ data->id = skb_cloned(skb) & 0xFFFF; data->cpu_id = smp_processor_id(); data->type = avm_profile_data_type_trace_skb; data->addr = addr; 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_irq_onoff(unsigned int switch_on __attribute__((unused)), unsigned int addr __attribute__((unused)), unsigned int id __attribute__((unused))) { #if defined(CONFIG_AVM_SIMPLE_PROFILING_IRQ_ON_OFF) if(!(simple_profiling.mask & ((1 << avm_profile_data_type_irq_on) | (1 << avm_profile_data_type_irq_off)))) return; avm_simple_profiling_log(switch_on ? avm_profile_data_type_irq_on : avm_profile_data_type_irq_off, addr, id); #endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING_IRQ_ON_OFF) ---*/ } EXPORT_SYMBOL(avm_simple_profiling_irq_onoff); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void __avm_simple_profiling_log(enum _avm_profile_data_type type, unsigned int addr, unsigned int id) { unsigned int pos, time; struct _avm_profile_data *data; /*--- if(simple_profiling.enabled == 0) return; ---*/ if(!(simple_profiling.mask & (1 << type))) return; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return; data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); /*--- data->curr_pid = task_pid_nr(current); ---*/ switch(type) { default: case avm_profile_data_type_unknown: data->type = type; break; case avm_profile_data_type_code_address_info: if(id != AVM_PROFILE_IDLE_ID) { /*--- if no reserved id used - remark with pid ---*/ id = task_pid_nr(current); } /*--- 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 = smp_processor_id(); data->type = type; data->addr = addr; data->time = time; #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #endif/*--- #if defined(AVM_PROFILE_ACTIVITY_INCLUDED) ---*/ break; } } EXPORT_SYMBOL(__avm_simple_profiling_log); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD static int avm_simple_profiling_yield_handler( int signal, void *ref ){ unsigned int irq = 0; unsigned long vpflags; unsigned int ntc = 2; //currently we are just interested in our SMP VPE contexts unsigned int tcs_to_profile_mask = ( 1 << 1 ) | ( 1 << 0 ); //default - will be replaced in YIELD_PCNT-Mode int tc; #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT tcs_to_profile_mask = ack_irqs_reload_counter(); #else ifx_gptu_timer_yield_ack( simple_profiling.yield_timer_no, yield_freq_randomizer_percent ); #endif vpflags = dvpe(); for (tc = 0; tc < ntc; tc++) { if ( tcs_to_profile_mask & (1 << tc) ){ unsigned int tcrestart_val; unsigned int cpu_id = tc; settc(tc); tcrestart_val = read_tc_c0_tcrestart(); avm_simple_profiling_yield( tcrestart_val, cpu_id, irq ); } } evpe(vpflags); return YIELD_HANDLED ; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void avm_simple_profiling_yield_enable(unsigned int on) { if(on) { simple_profiling.yield_handler = NO_YIELD_HANDLER; simple_profiling.yield_ref = &simple_profiling; #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT simple_profiling.yield_signal = YIELD_SIGNAL_PERFORMANE_COUNTER; #else simple_profiling.yield_signal = YIELD_SIGNAL_TIMER1; #endif printk(KERN_ERR "[%s] start yield profiling callback=%p ref_addr=%#x \n", __FUNCTION__, avm_simple_profiling_yield_handler, simple_profiling.yield_ref ); simple_profiling.yield_handler = request_yield_handler( simple_profiling.yield_signal, avm_simple_profiling_yield_handler, simple_profiling.yield_ref ); #ifndef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT if ( simple_profiling.yield_handler >= 0 ) { printk(KERN_ERR "[%s] request_yield_handler successful req_res =%d\n", __FUNCTION__, simple_profiling.yield_handler); /* --- freq in 1/1000 Hz --- */ simple_profiling.yield_timer_no = ifx_gptu_timer_set( 0, yield_thread_freq * 1000, 1, 0, TIMER_FLAG_YIELD_MODE , 0, 0 ); switch( simple_profiling.yield_timer_no ) { case TIMER1A: YIELDEN_GPCT_1A(1); break; case TIMER1B: YIELDEN_GPCT_1B(1); break; case TIMER2A: YIELDEN_GPCT_2A(1); break; case TIMER2B: YIELDEN_GPCT_2B(1); break; case TIMER3A: YIELDEN_GPCT_3A(1); break; case TIMER3B: YIELDEN_GPCT_3B(1); break; default: printk(KERN_ERR "[%s] could not reserve valid HW Timer: %d\n", __FUNCTION__, simple_profiling.yield_timer_no); ifx_gptu_timer_free( simple_profiling.yield_timer_no ); simple_profiling.yield_handler = NO_YIELD_HANDLER; return; } ifx_gptu_timer_start( simple_profiling.yield_timer_no, 0 ); printk(KERN_ERR "[%s] request_yield_handler successful timer_no= %d\n", __FUNCTION__, simple_profiling.yield_timer_no); } #endif/*--- #ifndef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT ---*/ return; } #ifndef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT if ( simple_profiling.yield_handler >= 0 ){ ifx_gptu_timer_stop( simple_profiling.yield_timer_no ); ifx_gptu_timer_free( simple_profiling.yield_timer_no ); free_yield_handler( simple_profiling.yield_signal, simple_profiling.yield_ref ); switch( simple_profiling.yield_timer_no ) { case TIMER1A: YIELDEN_GPCT_1A(0); break; case TIMER1B: YIELDEN_GPCT_1B(0); break; case TIMER2A: YIELDEN_GPCT_2A(0); break; case TIMER2B: YIELDEN_GPCT_2B(0); break; case TIMER3A: YIELDEN_GPCT_3A(0); break; case TIMER3B: YIELDEN_GPCT_3B(0); break; } #endif /*--- #ifndef CONFIG_AVM_SIMPLE_PROFILING_YIELD_PCNT ---*/ simple_profiling.yield_handler = NO_YIELD_HANDLER; } } #endif/*--- #ifdef CONFIG_AVM_SIMPLE_PROFILING_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"[%s] resize %d pages instead %d pages\n", __func__, 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"[%s] need %d pages for %d entries Buffer %d samples per block\n", __func__, 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(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 * setbusy: busy-Flag setzen * timediff in msec \*------------------------------------------------------------------------------------------*/ int avm_simple_profiling_enable(unsigned int on, unsigned int mask, unsigned int *count, unsigned long *timediff, unsigned int set_busy) { unsigned int _count; if(on) { if(atomic_add_return(1, &simple_profiling.busy) > 1) { printk(KERN_ERR"simple_profiling-analysing 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"can't enable profiler (memory-error)\n"); return -1; } } 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 "[%s]Unknown profiling mode. Assuming normal profiling without waparound.\n", __func__); case 1: simple_profiling.enabled = 1; break; case 2: simple_profiling.enabled = 2; /*--- wrap-arround mode ---*/ break; } if(count) *count = 0; if(timediff)*timediff = 0; avm_profile_counter_init(); #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD if(mask == ( 1 << avm_profile_data_type_code_address_yield )){ avm_simple_profiling_yield_enable(on); } #endif return 0; } if(set_busy) { atomic_set(&simple_profiling.busy, 1); } simple_profiling.enabled = 0; avm_profile_counter_exit(); #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD avm_simple_profiling_yield_enable(on); #endif/*--- #ifdef CONFIG_AVM_SIMPLE_PROFILING_YIELD ---*/ if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies; if(simple_profiling.wraparround == 0) { _count = simple_profiling.pos; } else { _count = simple_profiling.len; } printk(KERN_INFO"[%s] off: %u entries\n", __func__, _count); if(count) { *count = _count; } if(timediff) { *timediff = timediff_msec(_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, 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 < simple_profiling.pos) { data = ((struct _avm_profile_data *)(simple_profiling.data[idx / profile_DataSetsPerBlock]) + (idx % profile_DataSetsPerBlock)); return data; } return NULL; } pos = simple_profiling.pos + idx; if(pos >= simple_profiling.len) { pos -= simple_profiling.len; if(pos >= 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; simple_profiling.pos = 0; atomic_set(&simple_profiling.busy, 0); gCycle_per_usec = avm_get_clock(avm_clock_id_cpu) / 1000000; #if !defined(CONFIG_X86) gCycle_per_usec /= 2; #endif/*--- #if !defined(CONFIG_X86) ---*/ /*--- 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) ---*/