--- zzzz-none-000/linux-4.4.271/kernel/time/timer.c 2021-06-03 06:22:09.000000000 +0000 +++ hawkeye-5590-750/linux-4.4.271/kernel/time/timer.c 2023-04-19 10:22:30.000000000 +0000 @@ -43,6 +43,12 @@ #include #include #include +#if defined(CONFIG_AVM_ENHANCED) +#include +#include +#include +#include +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ #include #include @@ -54,6 +60,9 @@ #define CREATE_TRACE_POINTS #include +#if defined(CONFIG_AVM_SIMPLE_PROFILING) +#include +#endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/ __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; @@ -84,6 +93,11 @@ unsigned long timer_jiffies; unsigned long next_timer; unsigned long active_timers; +#if defined(CONFIG_AVM_ENHANCED) + void *last_fn; + void *next_fn; + unsigned long last_expire; +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ unsigned long all_timers; int cpu; bool migration_enabled; @@ -1183,7 +1197,13 @@ lock_map_acquire(&lockdep_map); trace_timer_expire_entry(timer); +#if defined(CONFIG_AVM_SIMPLE_PROFILING) + avm_simple_profiling_log(avm_profile_data_type_timer_begin, (unsigned int)fn, (unsigned int)data); +#endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/ fn(data); +#if defined(CONFIG_AVM_SIMPLE_PROFILING) + avm_simple_profiling_log(avm_profile_data_type_timer_end, (unsigned int)fn, (unsigned int)data); +#endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/ trace_timer_expire_exit(timer); lock_map_release(&lockdep_map); @@ -1262,6 +1282,10 @@ call_timer_fn(timer, fn, data); spin_lock_irq(&base->lock); } +#if defined(CONFIG_AVM_ENHANCED) + base->last_fn = fn; + base->last_expire = jiffies; +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ } } base->running_timer = NULL; @@ -1294,6 +1318,9 @@ /* Look at the cascade bucket(s)? */ if (!index || slot < index) goto cascade; +#if defined(CONFIG_AVM_ENHANCED) + base->next_fn = nte->function; +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ return expires; } slot = (slot + 1) & TVR_MASK; @@ -1321,6 +1348,9 @@ continue; found = 1; +#if defined(CONFIG_AVM_ENHANCED) + base->next_fn = nte->function; +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ if (time_before(nte->expires, expires)) expires = nte->expires; } @@ -1341,6 +1371,9 @@ timer_jiffies += TVN_SIZE - index; timer_jiffies >>= TVN_BITS; } +#if defined(CONFIG_AVM_ENHANCED) + base->next_fn = NULL; +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ return expires; } @@ -1724,3 +1757,138 @@ do_usleep_range(min, max); } EXPORT_SYMBOL(usleep_range); + +#if defined(CONFIG_AVM_ENHANCED) +static int nmi_timer_notify(struct notifier_block *self, unsigned long dummy, void *param); +static int module_check_notifier(struct notifier_block *nb, unsigned long val, void *data); +/*--------------------------------------------------------------------------------*\ +\*--------------------------------------------------------------------------------*/ +#if defined(CONFIG_AVM_FASTIRQ) || defined(CONFIG_MIPS) +static struct notifier_block nmi_timer_nb = { + .notifier_call = nmi_timer_notify, + .priority = INT_MAX - 1, +}; +#endif /*--- #if defined(CONFIG_AVM_FASTIRQ) || defined(CONFIG_MIPS) ---*/ +static struct notifier_block module_check_nb = { + .notifier_call = module_check_notifier, + .priority = INT_MAX, +}; +/*--------------------------------------------------------------------------------*\ +\*--------------------------------------------------------------------------------*/ +static void small_timer_statistic(void) { + struct tvec_base *base; + unsigned long flags, ts; + int cpu; + + ts = jiffies; + for_each_online_cpu(cpu) { + base = per_cpu_ptr(&tvec_bases, cpu); + if(spin_trylock_irqsave(&base->lock, flags)) { + unsigned long next_timer = __next_timer_interrupt(base); + printk(KERN_ERR"CPU%u: active_timers: %lu\n" + " last: %pS before %d ms\n" + " running: %pS\n" + " next: %pS in %d ms\n", + cpu, base->active_timers, + base->last_fn, jiffies_to_msecs(ts - base->last_expire), + base->running_timer ? base->running_timer->function : NULL, + base->next_fn, jiffies_to_msecs(next_timer - ts)); + spin_unlock_irqrestore(&base->lock, flags); + } else { + printk(KERN_ERR"%s: can't get lock on cpu%u\n", __func__, cpu); + } + } +} +#define is_in_range(pos, start, size) ((pos) >= (start) && ((pos) < (start + size))) +#define set_varray(idx, type) varray[idx].vec = (struct tvec *)&base->type, \ + varray[idx].slot_size = ARRAY_SIZE(base->type.vec) + +/** + */ +static int check_pending_timer_on_module_exit_on_base(int cpu, struct tvec_base *base, unsigned long module_addr, + unsigned long module_size) { + int slot, array; + unsigned long flags; + struct timer_list *nte; + int timer_pending = 0; + struct _varray { + struct tvec *vec; + unsigned int slot_size; + } varray[5]; + + set_varray(0, tv1); + set_varray(1, tv2); + set_varray(2, tv3); + set_varray(3, tv4); + set_varray(4, tv5); + + if(!spin_trylock_irqsave(&base->lock, flags)) { + printk(KERN_ERR"%s: can't get lock on cpu%u\n", __func__, cpu); + return 0; + } + /* Look tv1-tv5. */ + for (array = 0; array < ARRAY_SIZE(varray); array++) { + struct tvec *varp = varray[array].vec; + + for(slot = 0; slot < varray[array].slot_size; slot++) { + hlist_for_each_entry(nte, varp->vec + slot, entry) { +#if 0 + pr_err("%s: %u: tv%u: slot=%u expires in %d ms %pS %s\n", __func__, __LINE__, + array+1, slot, jiffies_to_msecs(nte->expires - jiffies), nte->function, + (nte->flags & TIMER_DEFERRABLE) ? "deferrable" : ""); +#endif + if(is_in_range((unsigned long)nte->function, module_addr, module_size)) { + avm_DebugSignalLog(0, "great error: timer with caller %pS pending (expired in %d ms)." + "Can't unload module!\n", + nte->function, jiffies_to_msecs(nte->expires - jiffies)); + timer_pending |= 1; + } + } + } + } + spin_unlock_irqrestore(&base->lock, flags); + return timer_pending; +} +/** + */ +static int check_pending_timer_on_module_exit(unsigned long module_addr, unsigned long module_size) { + struct tvec_base *base; + int cpu, timer_pending = 0; + for_each_online_cpu(cpu) { + base = per_cpu_ptr(&tvec_bases, cpu); + timer_pending |= check_pending_timer_on_module_exit_on_base(cpu, base, module_addr, module_size); + } + return timer_pending; +} +/** + */ +static int module_check_notifier(struct notifier_block *nb, unsigned long val, void *data) { + struct module *mod = data; + + if (val != MODULE_STATE_GOING) + return NOTIFY_DONE; + +// pr_err("%s: %u: addr=%p size=%u\n", __func__, __LINE__, mod->module_core, mod->core_size); + return check_pending_timer_on_module_exit((unsigned long)mod->module_core, mod->core_size) ? + NOTIFY_BAD : NOTIFY_DONE; +} +/** + */ +__maybe_unused +static int nmi_timer_notify(struct notifier_block *self __maybe_unused, unsigned long dummy __maybe_unused, + void *param __maybe_unused) { + small_timer_statistic(); + return NOTIFY_OK; +} +/** + */ +static int __init small_timer_statistic_init(void) { + /*--- printk(KERN_ERR"%s: init\n", __func__); ---*/ +#if defined(CONFIG_AVM_FASTIRQ) || defined(CONFIG_MIPS) + register_nmi_notifier(&nmi_timer_nb); +#endif /*--- #if defined(CONFIG_AVM_FASTIRQ) || defined(CONFIG_MIPS) ---*/ + register_module_notifier(&module_check_nb); + return 0; +} +late_initcall(small_timer_statistic_init); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/