--- zzzz-none-000/linux-4.4.60/kernel/time/timer.c	2017-04-08 07:53:53.000000000 +0000
+++ jet-2400-727/linux-4.4.60/kernel/time/timer.c	2021-03-17 14:36:41.000000000 +0000
@@ -42,6 +42,12 @@
 #include <linux/sched/sysctl.h>
 #include <linux/slab.h>
 #include <linux/compat.h>
+#if defined(CONFIG_AVM_ENHANCED)
+#include <asm/traps.h>
+#include <avm/sammel/debug.h>
+#include <linux/module.h>
+#include <avm/watchdog/watchdog.h>
+#endif/*--- #if defined(CONFIG_AVM_ENHANCED) ---*/
 
 #include <asm/uaccess.h>
 #include <asm/unistd.h>
@@ -53,6 +59,9 @@
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/timer.h>
+#if defined(CONFIG_AVM_SIMPLE_PROFILING)
+#include <avm/profile/profile.h>
+#endif /*--- #if defined(CONFIG_AVM_SIMPLE_PROFILING) ---*/
 
 __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
 
@@ -83,6 +92,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;
@@ -1175,7 +1189,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);
@@ -1254,6 +1274,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;
@@ -1286,6 +1310,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;
@@ -1313,6 +1340,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;
 			}
@@ -1333,6 +1363,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;
 }
 
@@ -1716,3 +1749,137 @@
 	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;
+}
+/**
+ */
+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) ---*/