#include #include #include #include #include #include #ifdef CONFIG_MIPS #include #endif /*--- #ifdef CONFIG_MIPS ---*/ /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #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; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) struct _simple_profiling simple_profiling; #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ # define PROFILE_BUFFER_LEN (CONFIG_AVM_PROFILING_TRACE_MODE * 1024) struct _simple_profiling { /*--- struct _avm_profile_data data[PROFILE_BUFFER_LEN]; ---*/ atomic_t pos; spinlock_t lock; unsigned int len; unsigned int enabled; unsigned int mode; unsigned int mask; unsigned long start_time; unsigned long end_time; unsigned int wraparround; } simple_profiling; #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ /*--------------------------------------------------------------------------------*\ * time und pos-Reservierung muessen atomar passieren! \*--------------------------------------------------------------------------------*/ static unsigned int lavm_simple_profiling_incpos(unsigned int *time) { unsigned int pos = 0; unsigned long flags; if(simple_profiling.enabled == 1) { spin_lock_irqsave(&simple_profiling.lock, flags); *time = avm_profile_counter(); pos = atomic_inc_return(&(simple_profiling.pos)) - 1; spin_unlock_irqrestore(&simple_profiling.lock, flags); if(pos >= simple_profiling.len - 1) { simple_profiling.enabled = 0; simple_profiling.end_time = jiffies | 1; printk("[simple_profiling] buffer full: %u entries recorded\n", simple_profiling.len); return (unsigned int)-1; } } else if(simple_profiling.enabled == 2) { spin_lock_irqsave(&simple_profiling.lock, flags); *time = avm_profile_counter(); pos = atomic_inc_return(&(simple_profiling.pos)) - 1; if(pos >= simple_profiling.len) { printk("[simple_profiling] wraparround: %u entries recorded\n", simple_profiling.len); if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies | 1; simple_profiling.wraparround = 1; simple_profiling.pos.counter = pos = 0; } spin_unlock_irqrestore(&simple_profiling.lock, flags); } return pos; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int avm_simple_profiling(struct pt_regs *regs, unsigned int irq_num) { unsigned int pos, epc, time; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) struct _avm_profile_data *data; #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ if(simple_profiling.enabled == 0) return 0; if(simple_profiling.mode != 1) return 0; if(!(simple_profiling.mask & (1 << avm_profile_data_type_code_address_info))) return 0; #if defined(CONFIG_ARM) epc = regs->ARM_pc; #endif /*--- #if defined(CONFIG_ARM) ---*/ #if defined(CONFIG_MIPS) epc = regs->cp0_epc; #endif /*--- #if defined(CONFIG_MIPS) ---*/ pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return 0; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data = &((struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]))[pos % profile_DataSetsPerBlock]; data->type = avm_profile_data_type_code_address_info; data->curr = current; data->id = irq_num; data->addr = epc; data->time = time; data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.data[pos].type = avm_profile_data_type_code_address_info; simple_profiling.data[pos].curr = current; simple_profiling.data[pos].id = irq_num; simple_profiling.data[pos].addr = epc; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ return time; } EXPORT_SYMBOL(avm_simple_profiling); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_simple_profiling_skb(unsigned int addr, unsigned int where, volatile struct sk_buff *skb) { unsigned int pos, time; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) struct _avm_profile_data *data; #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ if(simple_profiling.enabled == 0) return; // if(simple_profiling.mode != 2) return; if(!(simple_profiling.mask & (1 << avm_profile_data_type_trace_skb))) return; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); data->curr = current; #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data->type = avm_profile_data_type_trace_skb; data->id = skb_cloned(skb); data->addr = (unsigned long)__builtin_return_address(0); data->time = time; data->total_access = skb->uniq_id & 0xFFFFFF; data->total_activate = where; #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.data[pos].type = avm_profile_data_type_trace_skb; simple_profiling.data[pos].id = skb_cloned(skb); simple_profiling.data[pos].addr = addr; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = skb->uniq_id & 0xFFFFFF; simple_profiling.data[pos].total_activate = where; #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ } EXPORT_SYMBOL(avm_simple_profiling_skb); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ unsigned int avm_simple_profiling_current_pos; void avm_simple_profiling_log(enum _avm_profile_data_type type, unsigned int addr, unsigned int id) { unsigned int pos, time; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) struct _avm_profile_data *data; #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ if(simple_profiling.enabled == 0) return; if(simple_profiling.mode != 1) return; if(!(simple_profiling.mask & (1 << type))) return; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return; avm_simple_profiling_current_pos = pos; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); data->curr = current; #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ switch(type) { default: case avm_profile_data_type_free: case avm_profile_data_type_unknown: break; case avm_profile_data_type_text: case avm_profile_data_type_code_address_info: case avm_profile_data_type_data_address_info: #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data->type = type; data->addr = addr; data->time = time; data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.data[pos].type = type; simple_profiling.data[pos].addr = addr; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ 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_cpphytx_begin: case avm_profile_data_type_cpphytx_end: case avm_profile_data_type_cpphyrx_begin: case avm_profile_data_type_cpphyrx_end: case avm_profile_data_type_func_begin: case avm_profile_data_type_func_end: #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data->type = type; data->id = id; data->addr = addr; data->time = time; data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.data[pos].type = type; simple_profiling.data[pos].id = id; simple_profiling.data[pos].addr = addr; simple_profiling.data[pos].time = time; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ break; } } EXPORT_SYMBOL(avm_simple_profiling_log); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void avm_simple_profiling_text(const char *text) { unsigned int pos, time; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) struct _avm_profile_data *data; #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ if(simple_profiling.enabled == 0) return; if(simple_profiling.mode != 1) return; if(!(simple_profiling.mask & (1 << avm_profile_data_type_text))) return; pos = lavm_simple_profiling_incpos(&time); if(pos == (unsigned int)-1) return; #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data = (struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock); data->type = avm_profile_data_type_text; data->time = time; data->addr = (unsigned int)text; data->total_access = avm_profile_sdramacess(); data->total_activate = avm_profile_sdramactivate(); #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.data[pos].type = avm_profile_data_type_text; simple_profiling.data[pos].time = time; simple_profiling.data[pos].addr = (unsigned int)text; simple_profiling.data[pos].total_access = avm_profile_sdramacess(); simple_profiling.data[pos].total_activate = avm_profile_sdramactivate(); #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ return; } EXPORT_SYMBOL(avm_simple_profiling_text); /*------------------------------------------------------------------------------------------*\ * on = 2: wraparround-mode \*------------------------------------------------------------------------------------------*/ void avm_simple_profiling_enable(unsigned int on, unsigned int mask, unsigned int *count, unsigned long *timediff) { if(on) { if(mask) { profile_current_mask = mask; profile_current_start_handle = on; } #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) if(simple_profiling.data == NULL) { int i; simple_profiling.data = (void **)kmalloc(sizeof(void *) * profile_BlockNeeded, GFP_ATOMIC); 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, 1); if(ptr == NULL) { simple_profiling.len = profile_DataSetsPerBlock * i; printk("[avm_simple_profiling_enable] resize %d pages instead %d pages\n", i, profile_BlockNeeded); break; } simple_profiling.data[i] = (void *)lowmem_page_address(ptr); /*--- printk("\t%d: %x ==> %x\n", i, ptr, simple_profiling.data[i]); ---*/ } printk("[avm_simple_profiling_enable] need %d pages for %d bytes Buffer %d samples per block, trace-mask 0x%x\n", i, simple_profiling.len, profile_DataSetsPerBlock, mask); } #endif /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ atomic_set(&(simple_profiling.pos), 0); simple_profiling.start_time = jiffies; simple_profiling.end_time = 0; simple_profiling.wraparround = 0; simple_profiling.mask = mask; switch (on) { default: printk(KERN_ERR "Unknown profiling mode. Assuming normal profiling without waparound.\n"); case 2: simple_profiling.enabled = 1; simple_profiling.mode = 1; break; case 6: simple_profiling.enabled = 2; simple_profiling.mode = 1; break; case 7: simple_profiling.enabled = 1; simple_profiling.mode = 2; break; case 8: simple_profiling.enabled = 2; simple_profiling.mode = 2; break; } if(count) *count = 0; if(timediff)*timediff = 0; avm_profile_counter_init(); return; } avm_profile_counter_exit(); simple_profiling.enabled = 0; if(simple_profiling.end_time == 0) simple_profiling.end_time = jiffies; if(count) { if(simple_profiling.wraparround == 0) { *count = simple_profiling.pos.counter; } else { *count = simple_profiling.len; } } printk("[simple_profiling] off: %u entries\n", simple_profiling.wraparround ? simple_profiling.len : simple_profiling.pos.counter); if(timediff) { if(time_after(simple_profiling.start_time, simple_profiling.end_time)) { *timediff = simple_profiling.end_time + 0xFFFFFFFF - simple_profiling.start_time; } else { *timediff = simple_profiling.end_time - simple_profiling.start_time; } } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ 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); } else { printk("[%s] Push-Button for profiling ignored (not initialized)\n"); } 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.counter) { #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data = ((struct _avm_profile_data *)(simple_profiling.data[idx / profile_DataSetsPerBlock]) + (idx % profile_DataSetsPerBlock)); #else/*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ data = &simple_profiling.data[idx]; #endif/*--- #else ---*//*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ return data; } return NULL; } pos = simple_profiling.pos.counter + idx; if(pos >= simple_profiling.len) { pos -= simple_profiling.len; if(pos >= simple_profiling.pos.counter) { return NULL; } } #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) data = ((struct _avm_profile_data *)(simple_profiling.data[pos / profile_DataSetsPerBlock]) + (pos % profile_DataSetsPerBlock)); #else/*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ data = &simple_profiling.data[pos]; #endif/*--- #else ---*//*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ return data; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int avm_simple_profiling_init(void) { spin_lock_init(&simple_profiling.lock); #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) simple_profiling.len = 0; #else /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.len = PROFILE_BUFFER_LEN; #endif /*--- #else ---*/ /*--- #if defined(AVM_PROFILING_VERSION) && (AVM_PROFILING_VERSION >= 2) ---*/ simple_profiling.enabled = 0; atomic_set(&(simple_profiling.pos), 0); printk("[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) ---*/