--- zzzz-none-000/linux-5.4.213/kernel/time/timer.c 2022-09-15 10:04:56.000000000 +0000 +++ miami-7690-761/linux-5.4.213/kernel/time/timer.c 2024-05-29 11:20:02.000000000 +0000 @@ -50,11 +50,18 @@ #include #include #include +#if defined(CONFIG_AVM_ENHANCED) +#include +#include +#include +#include +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/ #include "tick-internal.h" #define CREATE_TRACE_POINTS #include +#include __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES; @@ -1415,7 +1422,9 @@ lock_map_acquire(&lockdep_map); trace_timer_expire_entry(timer, baseclk); + avm_simple_profiling_log(avm_profile_data_type_timer_begin, (unsigned long)fn, 0); fn(timer); + avm_simple_profiling_log(avm_profile_data_type_timer_end, (unsigned long)fn, 0); trace_timer_expire_exit(timer); lock_map_release(&lockdep_map); @@ -1783,8 +1792,9 @@ levels = collect_expired_timers(base, heads); base->clk++; - while (levels--) + while (levels--) { expire_timers(base, heads + levels); + } } raw_spin_unlock_irq(&base->lock); timer_base_unlock_expiry(base); @@ -2103,3 +2113,97 @@ } } EXPORT_SYMBOL(usleep_range); + +#if defined(CONFIG_AVM_ENHANCED) +static int module_check_notifier(struct notifier_block *nb, unsigned long val, void *data); +static struct notifier_block module_check_nb = { + .notifier_call = module_check_notifier, + .priority = INT_MAX, +}; +#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 timer_base *base, + unsigned long module_addr, + unsigned long module_size) +{ + unsigned long flags; + struct timer_list *nte; + int timer_pending = 0; + struct hlist_head *vec; + int idx; + + if (!raw_spin_trylock_irqsave(&base->lock, flags)) { + pr_err("%s: can't get lock on cpu%u\n", __func__, cpu); + return 0; + } + + for (idx = 0; idx < WHEEL_SIZE; idx++) { + if (test_bit(idx, base->pending_map)) { + vec = base->vectors + idx; + if (hlist_empty(vec)) + pr_err("%s vec is empty\n", __func__); + + hlist_for_each_entry(nte, vec, entry) { + pr_debug("%s: %u: lvl%d: slot=%u expires in %d ms %pS %s\n", + __func__, __LINE__, + idx, idx, + jiffies_to_msecs(nte->expires - jiffies), + nte->function, + (nte->flags & TIMER_DEFERRABLE) ? "deferrable" : ""); + + 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; + } + + } + } + } + + raw_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 timer_base *base; + int cpu, timer_pending = 0; + for_each_online_cpu(cpu) { + base = get_timer_cpu_base(0, cpu); + timer_pending |= check_pending_timer_on_module_exit_on_base(cpu, base, module_addr, + module_size); + if (IS_ENABLED(CONFIG_NO_HZ_COMMON)) { + base = get_timer_cpu_base(TIMER_DEFERRABLE, 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_debug("%s: %u: addr=%p size=%u\n", __func__, __LINE__, + mod->core_layout.base, mod->core_layout.size); + return check_pending_timer_on_module_exit((unsigned long)mod->core_layout.base, mod->core_layout.size) + ? NOTIFY_BAD : NOTIFY_DONE; +} + +static int __init small_timer_statistic_init(void) +{ + pr_debug("%s: init\n", __func__); + register_module_notifier(&module_check_nb); + return 0; +} +late_initcall(small_timer_statistic_init); +#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/