/*------------------------------------------------------------------------------------------*\ * * Copyright (C) 2006 AVM GmbH * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA \*------------------------------------------------------------------------------------------*/ #if defined(CONFIG_AVM_WATCHDOG) /*-----------------------------------------------------------------------------------------------*\ \*-----------------------------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /*--- #include ---*/ #include #include /*--- #include ---*/ #include #include #include #include #include /*--- #define AVM_WATCHDOG_DEBUG ---*/ #if defined(AVM_WATCHDOG_DEBUG) #define DBG(...) printk(KERN_INFO __VA_ARGS__) #else /*--- #if defined(AVM_WATCHDOG_DEBUG) ---*/ #define DBG(...) #endif /*--- #else ---*/ /*--- #if defined(AVM_WATCHDOG_DEBUG) ---*/ #include "avm_sammel.h" #include "ar7wdt.h" #include "avm_profile.h" /*--- #define DBG_TRIGGER(args...) printk(args) ---*/ #define DBG_TRIGGER(args...) static int oom_notify(struct notifier_block *self, unsigned long dummy, void *param); static struct notifier_block oom_nb = { .notifier_call = oom_notify }; extern void show_slab(void) __attribute__((weak)); extern unsigned long badness(struct task_struct *p, unsigned long uptime); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static struct _ar7wdt_data ar7wdt_data; static struct timer_list ar7wdt_timer; static struct timer_list PanicTimer; /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void AVM_WATCHDOG_timer_handler(unsigned long); static struct semaphore ar7wdt_sema; extern int ar7wdt_no_reboot; static unsigned int hw_wdt_is_running = 0; /*--- #define WATCHDOG_LIST_TASK_STATISTIC ---*/ #if defined(WATCHDOG_LIST_TASK_STATISTIC) static void show_task_statistic(void); #endif/*--- #if defined(WATCHDOG_LIST_TASK_STATISTIC) ---*/ static unsigned long get_act_pagefaults(void); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void AVM_WATCHDOG_timer_handler_action(unsigned int handle); volatile unsigned int AVM_WATCHDOG_locked = 0; static DEFINE_SPINLOCK(ar7_wdt_lock); static DEFINE_SPINLOCK(ar7_wdt_timer_lock); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static int _AVM_WATCHDOG_atoi(char *p) { int value = 0; while(p && *p && ((*p < '0') || (*p > '9'))) p++; while(p && *p) { if(*p >= '0' && *p <= '9') { value *= 10; value += *p - '0'; } else { break; } p++; } return value; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ #define AVM_WATCHDOG_DEL_TIMER 0x01 #define AVM_WATCHDOG_SET_TIMER 0x02 static void _AVM_WATCHDOG_ctrl_timer(int flags, int i) { unsigned long lockflags; struct timer_list *timer; unsigned long default_time; DBG(KERN_INFO "%s(flags=0x%x, handle=%d)\n", __func__, flags, i + 1); if(i == -1) { timer = &ar7wdt_timer; default_time = WDT_DEFAULT_TIME; if(jiffies - ar7wdt_data.last_hw_wd_trigger > (default_time + default_time / 2) * HZ) { printk(KERN_ERR"[%x][%s]Warning! last hw-trigger before %lu s (WDT_DEFAULT_TIME %lu s)\n", smp_processor_id(), __func__, (jiffies - ar7wdt_data.last_hw_wd_trigger) / HZ, default_time); } ar7wdt_data.last_hw_wd_trigger = jiffies; } else { timer = &(ar7wdt_data.appl[i].Timer); default_time = ar7wdt_data.appl[i].default_time; } spin_lock_irqsave(&ar7_wdt_timer_lock, lockflags); if(flags & AVM_WATCHDOG_DEL_TIMER) { del_timer(timer); } if(flags & AVM_WATCHDOG_SET_TIMER) { mod_timer(timer, jiffies + HZ * default_time); } spin_unlock_irqrestore(&ar7_wdt_timer_lock, lockflags); } static void lproc_wdt(struct seq_file *seq, void *priv); static int lproc_wd_simulate(char *buffer, void *priv); /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void AVM_WATCHDOG_init(void) { DBG(KERN_INFO "AVM_WATCHDOG_init:\n"); memset(&ar7wdt_data, 0x00, sizeof(ar7wdt_data)); ar7wdt_data.last_hw_wd_trigger = jiffies; sema_init(&ar7wdt_sema, 1); init_timer(&ar7wdt_timer); ar7wdt_timer.function = AVM_WATCHDOG_timer_handler; ar7wdt_timer.data = -1; _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_SET_TIMER, -1); add_simple_proc_file( "avm/wdt", lproc_wd_simulate, lproc_wdt, NULL); ar7wdt_hw_init(); ar7wdt_hw_trigger(); hw_wdt_is_running = 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void AVM_WATCHDOG_OOM_init(void) { register_oom_notifier(&oom_nb); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void AVM_WATCHDOG_deinit(void) { DBG(KERN_INFO "AVM_WATCHDOG_deinit:\n"); _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER, -1); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_find_handle(char *name, int len __attribute__((unused))) { int i; DBG(KERN_ERR "AVM_WATCHDOG_find_handle('%s', len=%u):\n", name, len); for( i = 0 ; i < MAX_WDT_APPLS ; i++) { if(ar7wdt_data.mask & (1 << i)) { if(!strcmp(ar7wdt_data.appl[i].Name, name)) { DBG(" handle=%u\n", i + 1); return i + 1; } } } DBG(" handle= not found\n"); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_strip_name(char **name, int len) { DBG(KERN_ERR "AVM_WATCHDOG_strip_name('%s', len=%u): ", *name, len); while((*name)[len - 1] == '\n' || (*name)[len - 1] == '\r') (*name)[len - 1] = '\0', len = strlen(*name); len = min(len, MAX_WDT_NAME_LEN - 1); (*name)[len] = '\0'; len = strlen(*name); DBG("-->('%s', len=%u)", *name, len); while(len && (**name == ' ' || **name == '\t' || **name == '\n' || **name == '\r')) (*name)++, len--; DBG("-->('%s', len=%u):\n", *name, len); return len; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void AVM_WATCHDOG_timer_init_ctrl_handler(unsigned long _handle __attribute__((unused))) { if(ar7wdt_no_reboot == 0) { panic("ar7wdt_hw_reboot: init sequence hangs !\n"); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_init_start(int handle __attribute__((unused)), char *name, int len __attribute__((unused))) { /*--- ignore handle ---*/ unsigned int Sekunden = _AVM_WATCHDOG_atoi(name); if(Sekunden == 0) Sekunden = 120; /*--- 2 Minuten ---*/ { #ifdef CONFIG_MIPS_UR8 hw_wdt_is_running = ar7wdt_hw_is_wdt_running(); #elif defined(CONFIG_ARCH_PUMA5) || defined(CONFIG_MACH_PUMA6) hw_wdt_is_running = ar7wdt_hw_is_wdt_running(); #elif defined(CONFIG_MIPS_FUSIV) #warning WATCHDOG: Ein wiederholtes initialisieren des Watchdogs auf dieser HW nicht implementiert! #elif defined(CONFIG_LANTIQ) #warning WATCHDOG: Ein wiederholtes initialisieren des Watchdogs auf dieser HW nicht implementiert! #elif defined(CONFIG_MACH_ATHEROS) || defined(CONFIG_ATH79) #warning WATCHDOG: Ein wiederholtes initialisieren des Watchdogs auf dieser HW nicht implementiert! #elif defined(CONFIG_ARCH_DAVINCI) #warning WATCHDOG: Ein wiederholtes initialisieren des Watchdogs auf dieser HW nicht implementiert! #else #warning WATCHDOG: Ein wiederholtes initialisieren des Watchdogs auf dieser HW nicht implementiert! #endif if(!hw_wdt_is_running) { ar7wdt_hw_init(); ar7wdt_hw_trigger(); hw_wdt_is_running = 1; } } init_waitqueue_head(&(ar7wdt_data.appl[MAX_WDT_APPLS].wait_queue)); ar7wdt_data.appl[MAX_WDT_APPLS].fasync = NULL; ar7wdt_data.appl[MAX_WDT_APPLS].default_time = Sekunden; ar7wdt_data.appl[MAX_WDT_APPLS].Timer.function = AVM_WATCHDOG_timer_init_ctrl_handler; ar7wdt_data.appl[MAX_WDT_APPLS].Timer.data = MAX_WDT_APPLS + 1; /*--- handle ---*/ init_timer(&(ar7wdt_data.appl[MAX_WDT_APPLS].Timer)); strcpy(ar7wdt_data.appl[MAX_WDT_APPLS].Name, "init-ctrl"); #ifdef CONFIG_PRINTK printk(KERN_CRIT"AVM_WATCHDOG: System Init UEberwachung %u Sekunden\n", Sekunden); #endif /*--- #ifdef CONFIG_PRINTK ---*/ _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_SET_TIMER, MAX_WDT_APPLS); return MAX_WDT_APPLS + 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_init_done(int handle, char *name __attribute__((unused)), int len __attribute__((unused))) { #ifdef CONFIG_PRINTK printk("AVM_WATCHDOG: System Init UEberwachung abgeschlossen (%lu ms noch verfuegbar)\n", 10 * (ar7wdt_data.appl[MAX_WDT_APPLS].Timer.expires - jiffies)); #endif /*--- #ifdef CONFIG_PRINTK ---*/ _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER, MAX_WDT_APPLS); return handle; } /*-------------------------------------------------------------------------------------*\ \*-------------------------------------------------------------------------------------*/ void AVM_WATCHDOG_ungraceful_release(int handle) { DBG(KERN_INFO "AVM_WATCHDOG_ungraceful_release(%u)\n", handle); if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "AVM_WATCHDOG_ungraceful_release(hdl=%u): invalid handle\n", handle); return; } handle--; if (ar7wdt_data.mask & (1 << handle)) { ar7wdt_data.appl[handle].crashflag = 1; #ifdef CONFIG_PRINTK printk(KERN_EMERG "AVM_WATCHDOG_ungraceful_release: handle %u (%s) still registered!\n", handle + 1, ar7wdt_data.appl[handle].Name); #endif /*--- #ifdef CONFIG_PRINTK ---*/ } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_register(int handle __attribute__((unused)), char *name, int len) { unsigned long flags; int i; DBG(KERN_INFO "%s(%s): start\n", __func__, name); if(ar7wdt_no_reboot >= 2) { return -EINVAL; /*--- inval argument ---*/ } AVM_WATCHDOG_strip_name(&name, len); for( i = 1 ; i < MAX_WDT_APPLS ; i++) { spin_lock_irqsave(&ar7_wdt_lock, flags); if(ar7wdt_data.mask & (1 << i)) { if(strcmp(ar7wdt_data.appl[i].Name, name)) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); continue; } ar7wdt_data.appl[handle].crashflag = 0; spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_INFO "%s(%s): already registered (accept) handle=%u\n", __func__, name, i + 1); return i + 1; } spin_unlock_irqrestore(&ar7_wdt_lock, flags); } DBG(KERN_INFO "%s(%s): not yet registered\n", __func__, name); for( i = 1 ; i < MAX_WDT_APPLS ; i++) { spin_lock_irqsave(&ar7_wdt_lock, flags); if(ar7wdt_data.mask & (1 << i)) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); continue; } ar7wdt_data.mask |= (1 << i); ar7wdt_data.triggered &= ~(1 << i); ar7wdt_data.requested |= (1 << i); spin_unlock_irqrestore(&ar7_wdt_lock, flags); init_timer(&(ar7wdt_data.appl[i].Timer)); ar7wdt_data.appl[i].req_jiffies = jiffies; ar7wdt_data.appl[i].avg_trigger = 0; ar7wdt_data.appl[i].pagefaults = get_act_pagefaults(); ar7wdt_data.appl[i].crashflag = 0; init_waitqueue_head(&(ar7wdt_data.appl[i].wait_queue)); ar7wdt_data.appl[i].fasync = NULL; ar7wdt_data.appl[i].default_time = WDT_DEFAULT_TIME; ar7wdt_data.appl[i].Timer.function = AVM_WATCHDOG_timer_handler; ar7wdt_data.appl[i].Timer.data = i + 1; /*--- handle ---*/ strncpy(ar7wdt_data.appl[i].Name, name, MAX_WDT_NAME_LEN); ar7wdt_data.appl[i].Name[MAX_WDT_NAME_LEN] = '\0'; _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_SET_TIMER, i); DBG(KERN_INFO "%s(%s): handle=%u mask=0x%08x\n", __func__, name, i + 1, ar7wdt_data.mask); return i + 1; } DBG(KERN_ERR "%s(%s): not registered, too many appls\n", __func__, name); return -EUSERS; /*--- to many users ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ struct fasync_struct **AVM_WATCHDOG_get_fasync_ptr(int handle) { if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "AVM_WATCHDOG_get_fasync_ptr(hdl=%u): invalid handle\n", handle); return NULL; /*--- io error ---*/ } handle--; if((ar7wdt_data.mask & (1 << handle)) == 0) { DBG(KERN_ERR "AVM_WATCHDOG_get_fasync_ptr(hdl=%u): invalid handle (not set in mask)\n", handle + 1); return NULL; /*--- io error ---*/ } return &(ar7wdt_data.appl[handle].fasync); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ wait_queue_head_t *AVM_WATCHDOG_get_wait_queue(int handle) { if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "AVM_WATCHDOG_get_wait_queue(hdl=%u): invalid handle\n", handle); return NULL; /*--- io error ---*/ } handle--; if((ar7wdt_data.mask & (1 << handle)) == 0) { DBG(KERN_ERR "AVM_WATCHDOG_get_wait_queue(hdl=%u): invalid handle (not set in mask)\n", handle + 1); return NULL; /*--- io error ---*/ } return &(ar7wdt_data.appl[handle].wait_queue); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_poll(int handle) { if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "AVM_WATCHDOG_poll(hdl=%u): invalid handle\n", handle); return -EINVAL; /*--- inval argument ---*/ } handle--; if((ar7wdt_data.mask & (1 << handle)) == 0) { DBG(KERN_ERR "AVM_WATCHDOG_poll(hdl=%u): invalid handle (not set in mask)\n", handle + 1); return -EINVAL; /*--- inval argument ---*/ } return (ar7wdt_data.requested & (1 << handle)) ? 1 : 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_release(int handle, char *name, int len) { unsigned long flags; DBG(KERN_INFO "%s(hdl=%u): name='%s', len=%u\n", __func__, handle, name, len); len = AVM_WATCHDOG_strip_name(&name, len); if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle); return -EINVAL; /*--- inval argument ---*/ } handle--; if(len) { if(strcmp(name, ar7wdt_data.appl[handle].Name)) { DBG(KERN_INFO "AVM_WATCHDOG_release(hdl=%u): name='%s', len=%u (name and handle to not correspond)\n", handle, name, len); handle = AVM_WATCHDOG_find_handle(name, len); if(handle == 0) { DBG(KERN_ERR "%s(hdl=%u): invalid name\n", __func__, handle + 1); return -EINVAL; /*--- inval argument ---*/ } handle--; } } spin_lock_irqsave(&ar7_wdt_lock, flags); if((ar7wdt_data.mask & (1 << handle)) == 0) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_ERR "%s(hdl=%u): invalid handle (not set in mask)\n", __func__, handle + 1); return -EINVAL; /*--- inval argument ---*/ } ar7wdt_data.mask &= ~(1 << handle); ar7wdt_data.requested &= ~(1 << handle); ar7wdt_data.states &= ~(1 << handle); ar7wdt_data.triggered &= ~(1 << handle); spin_unlock_irqrestore(&ar7_wdt_lock, flags); /*--- timer stoppen ---*/ _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER, handle); DBG(KERN_INFO "%s(hdl=%u): success\n", __func__, handle + 1); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_set_timeout(int handle, char *time, int len __attribute__((unused))) { unsigned long flags; if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle); return -EINVAL; /*--- inval argument ---*/ } handle--; spin_lock_irqsave(&ar7_wdt_lock, flags); if((ar7wdt_data.mask & (1 << handle)) == 0) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle + 1); return -EINVAL; /*--- inval argument ---*/ } ar7wdt_data.appl[handle].default_time = _AVM_WATCHDOG_atoi(time) / 2; spin_unlock_irqrestore(&ar7_wdt_lock, flags); _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_SET_TIMER | AVM_WATCHDOG_DEL_TIMER, handle); DBG(KERN_INFO "%s(hdl=%u): success (new timeout=%u)\n", __func__, handle + 1, ar7wdt_data.appl[handle].default_time); return handle + 1; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned long get_act_pagefaults(void) { unsigned long page_faults = 0; #ifdef CONFIG_VM_EVENT_COUNTERS struct vm_event_state new; all_vm_events((unsigned long *)&new); page_faults = new.event[PGFAULT]; #endif/*--- #ifdef CONFIG_VM_EVENT_COUNTERS ---*/ return page_faults; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_trigger(int handle, char *time __attribute__((unused)), int len __attribute__((unused))) { unsigned long flags; if(ar7wdt_no_reboot >= 2) { return -EINVAL; /*--- inval argument ---*/ } if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle); return -EINVAL; /*--- inval argument ---*/ } handle--; spin_lock_irqsave(&ar7_wdt_lock, flags); if((ar7wdt_data.mask & (1 << handle)) == 0) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_ERR "%s(hdl=%u): invalid handle (not set in mask)\n", __func__, handle + 1); return -EINVAL; /*--- inval argument ---*/ } ar7wdt_data.triggered |= (1 << handle); ar7wdt_data.states &= ~(1 << handle); spin_unlock_irqrestore(&ar7_wdt_lock, flags); if(likely(ar7wdt_data.appl[handle].avg_trigger)) { ar7wdt_data.appl[handle].avg_trigger += ((signed long)((jiffies - ar7wdt_data.appl[handle].req_jiffies) - ar7wdt_data.appl[handle].avg_trigger)) >> 3; } else { ar7wdt_data.appl[handle].avg_trigger = (jiffies - ar7wdt_data.appl[handle].req_jiffies); } ar7wdt_data.appl[handle].req_jiffies = jiffies; ar7wdt_data.appl[handle].pagefaults = get_act_pagefaults(); /*--- timer neu aufsetzen ---*/ _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER | AVM_WATCHDOG_SET_TIMER, handle); #if defined(WATCHDOG_LIST_TASK_STATISTIC) show_task_statistic(); #endif/*--- #if defined(WATCHDOG_LIST_TASK_STATISTIC) ---*/ DBG_TRIGGER(KERN_ERR "%s(hdl=%u): %s avg_trigger = %lu success\n", __func__, handle + 1, ar7wdt_data.appl[handle].Name, ar7wdt_data.appl[handle].avg_trigger); return handle + 1; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_disable(int handle __attribute__((unused)), char *time __attribute__((unused)), int len __attribute__((unused))) { unsigned long flags; int i; printk(KERN_CRIT "%s()\n", __func__); printk(KERN_INFO "registered appls:\n"); for (i = 0; i < MAX_WDT_APPLS; i++) { spin_lock_irqsave(&ar7_wdt_lock, flags); if((ar7wdt_data.mask & (1 << i))) { ar7wdt_data.mask &= ~(1 << i); ar7wdt_data.requested &= ~(1 << i); ar7wdt_data.states &= ~(1 << i); ar7wdt_data.triggered &= ~(1 << i); spin_unlock_irqrestore(&ar7_wdt_lock, flags); _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER, i); printk(KERN_INFO " hdl=%u, %s, disabled.\n", i + 1, ar7wdt_data.appl[i].Name); } else { spin_unlock_irqrestore(&ar7_wdt_lock, flags); } } _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER, -1); hw_wdt_is_running = 0; ar7wdt_hw_deinit(); ar7wdt_no_reboot = 3; return 1; /*--- sonst Endlosschleife, weil private_data == 0 ---*/ } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_read(int handle, char *Buffer, int max_len) { unsigned long flags; if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle); return -EINVAL; /*--- inval argument ---*/ } handle--; spin_lock_irqsave(&ar7_wdt_lock, flags); if((ar7wdt_data.mask & (1 << handle)) == 0) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_ERR "%s(hdl=%u): invalid handle (not set in mask)\n", __func__, handle + 1); return -EINVAL; /*--- inval argument ---*/ } if((ar7wdt_data.requested & (1 << handle)) == 0) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); Buffer[0] = '\0'; DBG(KERN_INFO "%s(hdl=%u): no request\n", __func__, handle + 1); return handle + 1; } ar7wdt_data.requested &= ~(1 << handle); spin_unlock_irqrestore(&ar7_wdt_lock, flags); max_len = min(max_len - 1, (int)sizeof("alive ?")); strncpy(Buffer, "alive ?", max_len); Buffer[max_len] = '\0'; DBG(KERN_INFO "%s(hdl=%u): request='%s'\n", __func__, handle + 1, Buffer); return handle + 1; } /*--------------------------------------------------------------------------------*\ * mode = 0: in jiffies * sonst in usec (aber liefere msec) \*--------------------------------------------------------------------------------*/ static char *human_timediff(char *buf, long timediff, int mode) { if(mode == 0) { if(timediff >= 0) { long msec = (timediff * 1000) / HZ; sprintf(buf, "%3lu.%03lu s", msec / 1000, msec % 1000); } else { strcpy(buf, "never"); } } else if(mode == 1) { sprintf(buf, "%10lu.%03lu ms", timediff / 1000, (timediff % 1000)); } else { sprintf(buf, "%10lu.%03lu us", timediff / 1000, (timediff % 1000)); } return buf; } #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) #define TASK_STATE_TO_CHAR_STR "RSDTtZX" #endif/*--- #if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 19) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static unsigned char task_state(unsigned int state) { static const char stat_nam[] = TASK_STATE_TO_CHAR_STR; return state < sizeof(stat_nam) - 1 ? stat_nam[state] : '?'; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int cmp_wdt_name(char *name) { int i; for (i = 0; i < MAX_WDT_APPLS; i++) { if((ar7wdt_data.mask & (1 << i))) { if(strcmp(name, ar7wdt_data.appl[i].Name) == 0) { return i + 1; } } } return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ void show_page_statistic(int now __attribute__((unused))) { #ifdef CONFIG_VM_EVENT_COUNTERS static int lastjiffies; int tdiff = (jiffies - lastjiffies); if(now || (tdiff > 10 * HZ)) { int i; static struct vm_event_state old; struct vm_event_state new; lastjiffies = jiffies; all_vm_events((unsigned long *)&new); for(i = 0; i < NR_VM_EVENT_ITEMS; i++){ old.event[i] = new.event[i] - old.event[i]; } tdiff /= HZ; if(tdiff == 0) tdiff = 1; printk(KERN_ERR"PGIN %lu(%lu) PGOUT %lu(%lu) PGFAULT %lu(%lu) SWPIN %lu(%lu) SWPOUT %lu(%lu) PGREFILL %lu(%lu)\n", new.event[PGPGIN], old.event[PGPGIN] / tdiff, new.event[PGPGOUT], old.event[PGPGOUT] / tdiff, new.event[PGFAULT], old.event[PGFAULT] / tdiff, new.event[PSWPIN], old.event[PSWPIN] / tdiff, new.event[PSWPOUT], old.event[PSWPOUT] / tdiff, new.event[PGREFILL_NORMAL], old.event[PGREFILL_NORMAL] / tdiff ); memcpy(&old, &new, sizeof(old)); } #endif/*--- #ifdef CONFIG_VM_EVENT_COUNTERS ---*/ } #if defined(WATCHDOG_LIST_TASK_STATISTIC) static struct _hist_task_times { unsigned long stime_act; unsigned long utime_act; unsigned long stime_last; unsigned long utime_last; unsigned int pid; char name[13]; unsigned int mark; } hist_task_times[120]; #define ARRAY_EL(a) (sizeof(a) / sizeof(a[0])) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ struct _hist_task_times *get_histtask_by_pid(unsigned int pid, char *name) { static unsigned int last; unsigned int i; for(i = last; i < ARRAY_EL(hist_task_times); i++) { if(hist_task_times[i].pid == pid) { last = i; hist_task_times[i].mark = 2; return &hist_task_times[i]; } } for(i = 0; i < last; i++) { if(hist_task_times[i].pid == pid) { last = i; hist_task_times[i].mark = 2; return &hist_task_times[i]; } } /*--- Create ---*/ for(i = 0; i < ARRAY_EL(hist_task_times); i++) { if(hist_task_times[i].mark == 0) { last = i; hist_task_times[i].utime_last = 0; hist_task_times[i].stime_last = 0; hist_task_times[i].mark = 2; hist_task_times[i].pid = pid; strncpy(hist_task_times[i].name, name, sizeof(hist_task_times[i].name)); return &hist_task_times[i]; } } printk("", pid); return NULL; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void show_task_statistic(void) { struct task_struct *g, *p; /*--- Summiere alle utime/stime ermittle maxrun ---*/ static unsigned long lastjiffies; unsigned long flags; unsigned long utime_sum = 0, stime_sum = 0; unsigned int i; unsigned long tdiff = (jiffies - lastjiffies); if(tdiff < 20 * HZ) { return; } lastjiffies = jiffies; read_lock_irqsave(&tasklist_lock, flags); do_each_thread(g, p) { struct _hist_task_times *pht = get_histtask_by_pid(p->pid, p->comm); if(pht) { pht->stime_act = task_stime(p); pht->utime_act = task_utime(p); utime_sum += pht->utime_act - pht->utime_last; stime_sum += pht->stime_act - pht->stime_last; } } while_each_thread(g, p); read_unlock_irqrestore(&tasklist_lock, flags); printk(KERN_EMERG "[%lu]%s: sum utime=%lu stime=%lu %lu (%lu %% from system - measure time %lu s)\n", jiffies, __func__, utime_sum, stime_sum, (utime_sum + stime_sum), ((utime_sum + stime_sum) * 100 ) / tdiff, tdiff / HZ); utime_sum |= 1; stime_sum |= 1; for(i = 0; i < ARRAY_EL(hist_task_times); i++) { struct _hist_task_times *pht = &hist_task_times[i]; if(pht->mark == 2) { if(((pht->stime_act - pht->stime_last) * 100 > (unsigned long)stime_sum) || ((pht->utime_act - pht->utime_last) * 100 > (unsigned long)utime_sum)) { printk(KERN_EMERG "name: %-13s pid %4d s/u %3lu %%%% %3lu %%%%)\n", pht->name, pht->pid, ((pht->stime_act - pht->stime_last) * 1000) / (unsigned long)stime_sum, ((pht->utime_act - pht->utime_last) * 1000) / (unsigned long)utime_sum); } pht->stime_last = pht->stime_act; pht->utime_last = pht->utime_act; } if(pht->mark) pht->mark--; } } #endif/*--- #if defined(WATCHDOG_LIST_TASK_STATISTIC) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void dump_task_memory(void) { struct timespec uptime; struct task_struct *g, *task; unsigned long flags, no_mm = 0, child = 0; read_lock_irqsave(&tasklist_lock, flags); do_posix_clock_monotonic_gettime(&uptime); printk(KERN_EMERG "\n%6s %5s %6s %6s %6s %6s " "%6s %6s " "%s", "pid", "score", "VMZ", "RSS", "Stack", "Data", "Code", "Lib", "Name\n"); do_each_thread(g, task) { struct mm_struct *mm = get_task_mm(task); if(mm == NULL) { no_mm++; } if(!thread_group_leader(task)) { child++; } if(mm && thread_group_leader(task)) { char *process_name, *thread_name; char txtbuf[128]; unsigned int process_namelen; unsigned long oom_score = badness(task, uptime.tv_sec); unsigned long total_rss = get_mm_rss(mm) << (PAGE_SHIFT -10); unsigned long code = (PAGE_ALIGN(mm->end_code) - (mm->start_code & PAGE_MASK)) >> 10; unsigned long lib = (mm->exec_vm << (PAGE_SHIFT-10)) - code; unsigned long stack = mm->stack_vm << (PAGE_SHIFT-10); unsigned long vm_size = (mm->total_vm - mm->reserved_vm) << (PAGE_SHIFT-10); unsigned long data = (mm->total_vm - mm->shared_vm - mm->stack_vm) << (PAGE_SHIFT-10); memset(txtbuf, 0, sizeof(txtbuf)); #if 0 /* auskommentiert da hier Zugriff auf vm-Speicher, der dann blockierend im OOM-Killer sein kann -> stattdessen Softwatchdogreboot ohne diese wertvolle Info */ if(!in_interrupt()) { read_unlock_irqrestore(&tasklist_lock, flags); /*--- maybe sleep ---*/ access_process_vm(task, mm->arg_start, txtbuf, sizeof(txtbuf)-1, 0); read_lock_irqsave(&tasklist_lock, flags); } #endif process_name = txtbuf; if(strstr(process_name, task->comm)) { snprintf(txtbuf, sizeof(txtbuf), "%s", task->comm); process_name = txtbuf; thread_name = NULL; } else { char *p = process_name; while((p = strstr(p, "/"))) p++, process_name = p; thread_name = task->comm; } if((process_namelen = strlen(process_name))) { if(process_name[process_namelen-1] == '\n') { process_name[process_namelen-1] = 0; } } mmput(mm); printk(KERN_EMERG "%6d %5lu %6lu %6lu %6lu %6lu " "%6lu %6lu " "%s%s%s%s\n", task->pid, oom_score, vm_size, total_rss, stack, data, code, lib, thread_name ? "{" : "", thread_name ? thread_name : "", thread_name ? "}" : "", process_name ); } else if(mm) { mmput(mm); } } while_each_thread(g, task); printk(KERN_EMERG"\ntasks without mm %lu childs %lu\n", no_mm, child); read_unlock_irqrestore(&tasklist_lock, flags); } /*--------------------------------------------------------------------------------*\ * OOM-Retry macht zumindest Sinn auf Boxen mit wenig User-Applikationen * * - OOM-Retry zaehlt hoch, wenn seit 100 Sekunden kein OOM aufgetreten * - wenn OOM-Retry = 5: Reboot \*--------------------------------------------------------------------------------*/ static unsigned int oom_retry(void) { static unsigned int count_oom, oom_jiffies; unsigned int dt; dt = (jiffies - oom_jiffies); if(dt > (100 * CONFIG_HZ)) { oom_jiffies = jiffies; count_oom = 1; printk(KERN_EMERG"\nERROR: OOM [#%u] retry\n", count_oom); show_mem(); if(!IS_ERR(&show_slab)) {/*--- Die Adresse der Variable muss auf Fehler und NULL geprüft werden ---*/ show_slab(); } return 1; } if(count_oom++ >= 5) { /*--- forget it ---*/ return 0; } printk(KERN_EMERG"\nERROR: OOM [#%u] retry\n", count_oom); return 1; } union saved_printk { int (*printk)(const char *format, ...); int (*vprintk)(const char *format, va_list list); }; static inline void _printk_save(union saved_printk *saved) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) saved->vprintk = vprintk; #else saved->printk = printk; #endif } static inline void _printk_restore(union saved_printk *saved) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 10, 0) vprintk_set(saved->vprintk); #else set_printk(saved->printk); #endif } extern int sysctl_panic_on_oom; static unsigned long oom_show_jiffies; /*--------------------------------------------------------------------------------*\ * only for analyze task-memory usage \*--------------------------------------------------------------------------------*/ static int oom_notify(struct notifier_block *self __attribute__((unused)), unsigned long dummy __attribute__((unused)), void *param) { union saved_printk saved_printk; unsigned long *freed = param; if(oom_retry()) { *freed = 1; schedule(); return 1; } *freed = 0; if(sysctl_panic_on_oom || ((jiffies - oom_show_jiffies) >= 10UL * HZ)) { oom_show_jiffies = jiffies | 1; mb(); _printk_save(&saved_printk); if(sysctl_panic_on_oom) { bust_spinlocks(1); avm_debug_disable_avm_printk(); /* folgende Ausgaben sofort raus */ console_verbose(); dump_stack(); show_mem(); if(!IS_ERR(&show_slab)) {/*--- Die Adresse der Variable muss auf Fehler und NULL geprüft werden ---*/ show_slab(); } } dump_task_memory(); _printk_restore(&saved_printk); } return NOTIFY_OK; } volatile struct task_struct *hungingtask = NULL; #if defined(CONFIG_MIPS) & 0 #define IS_MIPS16_EXTEND_OR_JAL(a) ((((a) >> (27 - 16)) == 30) | (((a) >> (27 - 16)) == 3)) /*--- Opcode EXTEND oder JAL ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void print_code_range(unsigned long pc, int left_offset, int right_offset) { unsigned int start = 1; int mips16 = pc & 0x1; if(!mips16) { signed int i; unsigned long access_addr = pc + sizeof(unsigned int) * left_offset; for(i = left_offset; i < right_offset; i++) { unsigned int pc_value; access_addr = pc + sizeof(unsigned int) * i; if(__get_user(pc_value, (unsigned int __user *)access_addr)) { return; } if(start) { printk(KERN_CONT"Code(0x%08lx):", access_addr); start = 0; } printk(KERN_CONT" %s0x%08x%s", i == 0 ? "<" : "", pc_value, i == 0 ? ">" : ""); } } else { /*--- wegen EXT-Code nur step by step vorhangeln: ---*/ unsigned short code0, code1; unsigned long pc_addr = pc & ~0x1; unsigned long access_addr = pc & ~0x1; unsigned long end_addr = (pc & ~0x1) + sizeof(unsigned short) * right_offset; while(left_offset < 0) { if(__get_user(code1, (unsigned short __user *)(access_addr - sizeof(short)))) { /*--- printk(KERN_ERR "[%s] load from 16 bit address 0x%lx failed (sigbus)\n", prefix, access_addr); ---*/ return; /*--- sigbus; ---*/ } if(__get_user(code0, (unsigned short __user *)(access_addr - 2 * sizeof(short)))) { /*--- printk(KERN_ERR "[%s] load from 16 bit address 0x%lx failed (sigbus)\n", prefix, access_addr); ---*/ return; /*--- sigbus; ---*/ } if(IS_MIPS16_EXTEND_OR_JAL(code0)) { access_addr -= 2 * sizeof(short); } else { access_addr -= sizeof(short); } left_offset++; } printk(KERN_CONT"Code(0x%08lx):", access_addr); while(access_addr < end_addr) { if(__get_user(code0, (unsigned short __user *)(access_addr))) { /*--- printk(KERN_ERR "[%s] load from 16 bit address 0x%lx failed (sigbus)\n", prefix, access_addr); ---*/ return; /*--- sigbus; ---*/ } if(access_addr == pc_addr) { if(IS_MIPS16_EXTEND_OR_JAL(code0)) { access_addr += sizeof(short); if(__get_user(code1, (unsigned short __user *)(access_addr))) { /*--- printk(KERN_ERR "[%s] load from 16 bit address 0x%lx failed (sigbus)\n", prefix, access_addr); ---*/ return; /*--- sigbus; ---*/ } printk(KERN_CONT" <0x%04x %04x>", code0, code1); } else { printk(KERN_CONT" <0x%04x>", code0); } } else { printk(KERN_CONT" 0x%04x", code0); } access_addr += sizeof(short); } } printk(KERN_CONT"\n"); } #endif/*--- #if defined(CONFIG_MIPS) ---*/ #if 0 /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void print_stack(struct mm_struct *mm, unsigned long stackaddr) { int start = 1; char txtbuf[64]; unsigned int i, value; for(i = 0; i < 10; i++) { if(__get_user(value, (unsigned int __user *)stackaddr)) { return; } if(__get_userinfo(txtbuf, sizeof(txtbuf), mm, value)) { txtbuf[0] = 0; } if(start) { printk(KERN_ERR"Userstack:\n"); start = 0; } printk(KERN_ERR"%lx %x %s\n", stackaddr, value, txtbuf); stackaddr += 4; } } #endif /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void __sched_show_user_task(struct task_struct *task){ char txtbuf[2][64]; struct pt_regs *preg __maybe_unused; struct mm_struct *mm; mm = get_task_mm(task); if(mm == NULL) { printk(KERN_CONT"no usercontext\n"); return; } #ifdef CONFIG_AVM_SIMPLE_PROFILING if(__get_userinfo(txtbuf[0], sizeof(txtbuf[0]), mm, KSTK_EIP(task))) { txtbuf[0][0] = 0; } #if defined(CONFIG_MIPS) preg = ((struct pt_regs *)__KSTK_TOS(task)); if(__get_userinfo(txtbuf[1], sizeof(txtbuf[1]), mm, preg->regs[31])) { txtbuf[1][0] = 0; } #endif/*--- #if defined(CONFIG_MIPS) ---*/ #else /*--- #ifdef CONFIG_AVM_SIMPLE_PROFILING ---*/ txtbuf[0][0] = txtbuf[1][0] = '\0'; #endif /*--- CONFIG_AVM_SIMPLE_PROFILING ---*/ printk(KERN_CONT "\n" #if defined(CONFIG_MIPS) "\tat: %08lx v0: %08lx v1: %08lx\n" "\ta0: %08lx a1: %08lx a2: %08lx a3: %08lx\n" "\tt0: %08lx t1: %08lx t2: %08lx t3: %08lx\n" "\tt4: %08lx t5: %08lx t6: %08lx t7: %08lx\n" "\ts0: %08lx s1: %08lx s2: %08lx s3: %08lx\n" "\ts4: %08lx s5: %08lx s6: %08lx s7: %08lx\n" "\tt8: %08lx t9: %08lx\n" "\tgp: %08lx fp: %08lx\n" #endif/*--- #if defined(CONFIG_MIPS) ---*/ "\tsp: %p (start_stack %p)\n" "\tepc: %p %s\n" #if defined(CONFIG_MIPS) "\tra: %p %s\n", preg->regs[ 1], preg->regs[ 2], preg->regs[ 3], preg->regs[ 4], preg->regs[ 5], preg->regs[ 6], preg->regs[ 7], preg->regs[ 8], preg->regs[ 9], preg->regs[10], preg->regs[11], preg->regs[12], preg->regs[13], preg->regs[14], preg->regs[15], preg->regs[16], preg->regs[17], preg->regs[18], preg->regs[19], preg->regs[20], preg->regs[21], preg->regs[22], preg->regs[23], preg->regs[24], preg->regs[25], preg->regs[28], preg->regs[30] #endif/*--- #if defined(CONFIG_MIPS) ---*/ ,(void *)KSTK_ESP(task), (void *)mm->start_stack, (void *)KSTK_EIP(task), txtbuf[0] #if defined(CONFIG_MIPS) ,(void *)preg->regs[31], txtbuf[1] #endif/*--- #if defined(CONFIG_MIPS) ---*/ ); #if defined(CONFIG_MIPS) & 0 print_code_range(KSTK_EIP(task), -2, 3); #endif/*--- #if defined(CONFIG_MIPS) ---*/ /*--- print_stack(mm, KSTK_ESP(task)); ---*/ mmput(mm); } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int watermark_under_low(void) { struct zone *zone; #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) for_each_populated_zone(zone) { if(zone_nr_free_pages(zone) < low_wmark_pages(zone)){ return 1; } } #else /*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ---*/ for_each_zone(zone) { if(zone_page_state(zone, NR_FREE_PAGES) < zone->pages_low) { return 1; } } #endif return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static struct task_struct *watchdog_task_list(int notrigger_handle) { char txtbuf[2][64]; struct task_struct *g, *p, *hungtask = NULL; unsigned long flags, page_faults; int handle, delayed = 0; unsigned int handle_mask = 0; union saved_printk saved_printk; struct timespec uptime; read_lock_irqsave(&tasklist_lock, flags); _printk_save(&saved_printk); avm_debug_disable_avm_printk(); /* folgende Ausgaben sofort ausgeben */ console_verbose(); do_posix_clock_monotonic_gettime(&uptime); /*--- printk("utime_sum: %lld unorm %d\n", utime_sum, unorm); ---*/ printk(KERN_EMERG "[%lu][%x]AVM_WATCHDOG_reboot(hdl=%u, %s): reboot (current: %s pgfault %lu oom_score %lu)\n", jiffies, smp_processor_id(), notrigger_handle + 1, ar7wdt_data.appl[notrigger_handle].Name, current->comm, current->signal->cmaj_flt, pid_alive(current) ? badness(current, uptime.tv_sec) : 0 ); page_faults = get_act_pagefaults(); printk(KERN_EMERG "pagefaults absolut %lu since last %s-trigger %lu\n", page_faults, ar7wdt_data.appl[notrigger_handle].Name, page_faults - ar7wdt_data.appl[notrigger_handle].pagefaults); do_each_thread(g, p) { if((handle = cmp_wdt_name(p->comm)) == 0) { continue; } handle--; handle_mask |= (1 << handle); if(thread_group_leader(p)) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) printk(KERN_EMERG " hdl=%2u %-13s pid %4d triggered before: %s(avg %s) state %c cpu%x pgfault %lu oom_score %lu\n", handle + 1, ar7wdt_data.appl[handle].Name, p->pid, human_timediff(txtbuf[0], jiffies - ar7wdt_data.appl[handle].req_jiffies, 0), human_timediff(txtbuf[1], ar7wdt_data.appl[handle].avg_trigger, 0), task_state(p->state), task_cpu(p), p->signal->cmaj_flt, pid_alive(p) ? badness(p, uptime.tv_sec) : 0 ); #else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ---*/ printk(KERN_EMERG " hdl=%2u %-13s pid %4d triggered before: %s(avg %s) state %c oom_score %lu\n", handle + 1, ar7wdt_data.appl[handle].Name, p->pid, human_timediff(txtbuf[0], jiffies - ar7wdt_data.appl[handle].req_jiffies, 0), human_timediff(txtbuf[1], ar7wdt_data.appl[handle].avg_trigger, 0), task_state(p->state), pid_alive(p) ? badness(p, uptime.tv_sec) : 0 ); #endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ---*/ if((notrigger_handle == handle) && (delayed == 0)) { delayed = 1; printk(KERN_EMERG " force SIGBUS for %s (pid= %d)\n", p->comm, p->pid); hungtask = p; } } } while_each_thread(g, p); for(handle = 0; handle < MAX_WDT_APPLS; handle++) { if((ar7wdt_data.mask & (1 << handle))) { if((handle_mask & (1 << handle)) == 0) { printk(KERN_EMERG " hdl=%2u %-13s triggered before: %s (avg %s) %s\n", handle + 1, ar7wdt_data.appl[handle].Name, human_timediff(txtbuf[0], jiffies - ar7wdt_data.appl[handle].req_jiffies, 0), human_timediff(txtbuf[1], ar7wdt_data.appl[handle].avg_trigger, 0), ar7wdt_data.appl[handle].crashflag ? "maybe crashed" : "" ); } } } if(hungtask) { printk(KERN_EMERG "ar7wdt_hw_reboot: kernel context for %s (pid=%d):\n", hungtask->comm, hungtask->pid); sched_show_task(hungtask); printk(KERN_EMERG "ar7wdt_hw_reboot: user context for %s:", hungtask->comm); __sched_show_user_task(hungtask); } read_unlock_irqrestore(&tasklist_lock, flags); /*--- printk(KERN_EMERG "ar7wdt_hw_reboot: kernel context for all blocked tasks:\n"); ---*/ /*--- show_state_filter(TASK_UNINTERRUPTIBLE); ---*/ if(oom_show_jiffies == 0) { show_mem(); if(!IS_ERR(&show_slab)) {/*--- Die Adresse der Variable muss auf Fehler und NULL geprüft werden ---*/ if(watermark_under_low()) { show_slab(); } } dump_task_memory(); } _printk_restore(&saved_printk); if(hw_wdt_is_running) ar7wdt_hw_trigger(); { extern void *current_rtnl_owner __attribute__ ((weak)); if(¤t_rtnl_owner) { struct task_struct *cur = (struct task_struct *)current_rtnl_owner ; extern atomic_t current_rtnl_count __attribute__ ((weak)); if(cur) printk(KERN_EMERG "ar7wdt_hw_reboot: RTNL_LOCK: locked by '%s' '%d' waiting\n", cur->comm, atomic_read(¤t_rtnl_count)); } } return hungtask; } /*--------------------------------------------------------------------------------*\ * 2 stufig: * context != 0: hartes wakeup des haengenden Tasks (auch wenn TASK_UNINTERRUPTIBLE) * context == 0: panic \*--------------------------------------------------------------------------------*/ static void panic_function(unsigned long context) { if(hw_wdt_is_running) ar7wdt_hw_trigger(); avm_debug_disable_avm_printk(); if(hw_wdt_is_running) ar7wdt_hw_trigger(); if(context) { struct task_struct *hungtask = (struct task_struct *)context; siginfo_t info; info.si_signo = SIGBUS; info.si_errno = ETIME; info.si_code = BUS_OBJERR; info.si_addr = (void *)0xEBADEBAD; force_sig_info(SIGBUS, &info, hungtask); if(hungtask->state == TASK_UNINTERRUPTIBLE) { printk(KERN_EMERG "ar7wdt_hw_reboot: violent wake up task %s (pid= %d):\n", hungtask->comm, hungtask->pid); hungingtask = hungtask; /*--- in sched.h bekanntmachen ---*/ wake_up_process(hungtask); } /*--- und nun noch panic verzoegert triggern ---*/ PanicTimer.data = 0; PanicTimer.function = panic_function; PanicTimer.expires = jiffies + HZ * 5; add_timer(&PanicTimer); } else { panic("ar7wdt_hw_reboot: delayed watchdog expired\n"); } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ int AVM_WATCHDOG_reboot(int handle) { if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "AVM_WATCHDOG_reboot(hdl=%u): invalid handle\n", handle); return -EINVAL; /*--- inval argument ---*/ } handle--; if((ar7wdt_data.mask & (1 << handle)) == 0) { DBG(KERN_ERR "AVM_WATCHDOG_reboot(hdl=%u): invalid handle (not set in mask)\n", handle + 1); return -EINVAL; /*--- inval argument ---*/ } if(ar7wdt_data.states & (1 << handle)) { struct task_struct *hungtask; if(hw_wdt_is_running) ar7wdt_hw_trigger(); hungtask = watchdog_task_list(handle); #ifdef CONFIG_SCHEDSTATS if (ar7wdt_no_reboot == 1) { #if KERNEL_VERSION(2, 6, 19) >= LINUX_VERSION_CODE show_sched_stats(); #endif show_state(); } #endif #if defined(CONFIG_PROC_FS) #if defined(CONFIG_MIPS_OHIO) && defined(CONFIG_SCHEDSTATS) show_process_eip(ar7wdt_data.appl[handle].Name); #endif #endif /*--- #if defined(CONFIG_PROC_FS) ---*/ if(ar7wdt_no_reboot == 0) { #if defined(CONFIG_ARCH_DAVINCI) /*--- erstmal den Handle ausblinken und nicht rebooten ---*/ #include #include #include { extern unsigned int davinci_revision[5]; int tmp, led_handle, appl_handle = 0, led_on, led_off; unsigned long next_jiffies; if(davinci_revision[1] == 1) { led_handle = avm_led_alloc_handle("error", 1, NULL); led_on = 0; led_off = 1; } else { led_handle = avm_led_alloc_handle("error", 0, NULL); led_on = 1; led_off = 0; } if (!strcmp(ar7wdt_data.appl[handle].Name, "galio")) appl_handle = 1; if (!strcmp(ar7wdt_data.appl[handle].Name, "mediasrv")) appl_handle = 2; if (!appl_handle) appl_handle = 3; if (led_handle) { avm_led_action_with_handle(led_handle, led_off); while (1) { tmp = appl_handle; next_jiffies = jiffies + msecs_to_jiffies(1000); while (time_after(next_jiffies, jiffies)) ; while(tmp--) { avm_led_action_with_handle(led_handle, led_on); next_jiffies = jiffies + msecs_to_jiffies(1000); while (time_after(next_jiffies, jiffies)) ; avm_led_action_with_handle(led_handle, led_off); next_jiffies = jiffies + msecs_to_jiffies(1000); while (time_after(next_jiffies, jiffies)) ; } } } } #endif AVM_WATCHDOG_deinit(); init_timer(&PanicTimer); PanicTimer.data = (unsigned long)hungtask; PanicTimer.function = panic_function; PanicTimer.expires = hungtask ? (jiffies + HZ * 1) : (jiffies + HZ * 5); add_timer(&PanicTimer); return 0; } } printk(KERN_ERR "AVM_WATCHDOG_reboot(hdl=%u): timer not triggered\n", handle + 1); return 0; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void AVM_WATCHDOG_timer_handler_action(unsigned int handle) { unsigned long flags; spin_lock_irqsave(&ar7_wdt_lock, flags); ar7wdt_data.states |= 1 << handle; ar7wdt_data.requested |= 1 << handle; spin_unlock_irqrestore(&ar7_wdt_lock, flags); if(ar7wdt_data.appl[handle].fasync) { kill_fasync(&(ar7wdt_data.appl[handle].fasync), SIGIO, POLL_IN); } else { wake_up_interruptible(&(ar7wdt_data.appl[handle].wait_queue)); } _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_SET_TIMER, handle); DBG_TRIGGER(KERN_INFO "%s(hdl=%u): timer triggered once\n", __func__, handle + 1); } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ static void AVM_WATCHDOG_timer_handler(unsigned long _handle) { unsigned long flags; int handle = (int)_handle; DBG(KERN_INFO "%s(hdl=%d)\n", __func__, handle); if(handle == -1) { _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_SET_TIMER, handle); ar7wdt_hw_trigger(); return; } if(handle < 1 || handle > MAX_WDT_APPLS) { DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle); return; } handle--; spin_lock_irqsave(&ar7_wdt_lock, flags); if((ar7wdt_data.mask & (1 << handle)) == 0) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_ERR "%s(hdl=%u): invalid handle\n", __func__, handle + 1); return; } if(ar7wdt_data.states & (1 << handle)) { spin_unlock_irqrestore(&ar7_wdt_lock, flags); DBG(KERN_INFO "%s(hdl=%u): timer triggered twice (reboot)\n", __func__, handle + 1); AVM_WATCHDOG_reboot(handle + 1); return; } spin_unlock_irqrestore(&ar7_wdt_lock, flags); _AVM_WATCHDOG_ctrl_timer(AVM_WATCHDOG_DEL_TIMER, handle); AVM_WATCHDOG_timer_handler_action(handle); return; } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ void AVM_WATCHDOG_emergency_retrigger(void) { if(hw_wdt_is_running) ar7wdt_hw_trigger(); } EXPORT_SYMBOL(AVM_WATCHDOG_emergency_retrigger); /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void simulate_kernel_crash(void) { int *inval_pointer = NULL; *inval_pointer = 0x43524153; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void simulate_oom(unsigned int slab) { unsigned int i, len = 4012; for(;;) { unsigned int *p; if(slab) { p = kmalloc(len * sizeof(int), GFP_KERNEL); } else { p = vmalloc(len * sizeof(int)); } for(i = 0; i < len; i++) { p[i] = i ^ (unsigned int)p; } } } #define SIMULATE_WD "simulate_wdr" /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void simulate_wdr(void) { AVM_WATCHDOG_register(0, SIMULATE_WD, sizeof(SIMULATE_WD)); } #if defined(CONFIG_SMP) /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void knockout_cpu(void *dummy __attribute__((unused))) { local_irq_disable(); for(;;) ; } #endif/*--- #if defined(CONFIG_SMP) ---*/ /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static int lproc_wd_simulate(char *buffer, void *priv __attribute__((unused))) { char parsbuf[256], *p; strncpy(parsbuf, buffer, sizeof(parsbuf) - 1); parsbuf[sizeof(parsbuf) -1] = '\0'; /*--- printk(KERN_INFO"%s parsbuf='%s'\n", __func__, parsbuf); ---*/ if(strstr(parsbuf, "simulate_wdr")) { printk(KERN_ERR"\nSimulate Watchdog-Reboot with '%s'\n", SIMULATE_WD); simulate_wdr(); } else if((p = strstr(parsbuf, "simulate_waddr"))) { unsigned int addr = 0, val = 0; p += sizeof("simulate_waddr") - 1; while(*p == ' ' || *p == '\t') p++; sscanf(p,"%x %x", &addr, &val); printk(KERN_ERR"simulate_waddr: *(%x) = %x\n", addr, val); *((unsigned int *)addr) = val; wmb(); } else if((p = strstr(parsbuf, "simulate_raddr"))) { unsigned int i, s = 3, addr = 0, count = 0, val; p += sizeof("simulate_raddr") - 1; while(*p == ' ' || *p == '\t') p++; sscanf(p,"%x %u", &addr, &count); if(count == 0) count = 1; printk(KERN_ERR"simulate_raddr: addr=%x count=%x\n", addr, count); for(i = 0; i < count; i++) { val = *((unsigned int *)addr); mb(); if(++s == 4) { s = 0; printk("\n%08x: %08x", addr, val); } else { printk(" %08x", val); } addr += 4; } printk("\n"); } else if(strstr(parsbuf, "simulate_oomslab")) { printk(KERN_ERR"\nSimulate OOM per kmalloc\n"); simulate_oom(1); } else if(strstr(parsbuf, "simulate_oom")) { printk(KERN_ERR"\nSimulate OOM per vmalloc\n"); simulate_oom(0); } else if(strstr(parsbuf, "simulate_kcrash")) { printk(KERN_ERR"\nSimulate Kernel-Crash\n"); simulate_kernel_crash(); } else if(strstr(parsbuf, "simulate_hw_wdog")) { local_irq_disable(); #if defined(CONFIG_SMP) on_each_cpu(knockout_cpu, NULL, 0); #endif/*--- #if defined(CONFIG_SMP) ---*/ for(;;) ; } else if((p = strstr(parsbuf, "simulate_jump"))) { unsigned int addr = 0; p += sizeof("simulate_jump") - 1; while(*p == ' ' || *p == '\t') p++; sscanf(p,"%x", &addr); if(addr) { ((void (*)(void))addr)(); } } else { printk(KERN_INFO"unknown option use: simulate_wdr, simulate_oom, simulate_oomslab, simulate_kcrash, simulate_hw_wdog, simulate_waddr , simulate_raddr , simulate_jump addr\n"); } return 0; } /*--------------------------------------------------------------------------------*\ \*--------------------------------------------------------------------------------*/ static void lproc_wdt(struct seq_file *seq, void *priv __attribute__((unused))){ char txtbuf[2][64]; struct task_struct *g, *p; unsigned long flags; unsigned int handle ,handle_mask = 0; struct timespec uptime; do_posix_clock_monotonic_gettime(&uptime); read_lock_irqsave(&tasklist_lock, flags); do_each_thread(g, p) { if((handle = cmp_wdt_name(p->comm)) == 0) { continue; } handle--; handle_mask |= (1 << handle); if(thread_group_leader(p)) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) seq_printf(seq, "hdl=%2u %-13s pid %4d triggered before: %s(avg %s) state %c cpu%x pgfault %lu oom_score %lu\n", handle + 1, ar7wdt_data.appl[handle].Name, p->pid, human_timediff(txtbuf[0], jiffies - ar7wdt_data.appl[handle].req_jiffies, 0), human_timediff(txtbuf[1], ar7wdt_data.appl[handle].avg_trigger, 0), task_state(p->state), task_cpu(p), p->signal->cmaj_flt, pid_alive(p) ? badness(p, uptime.tv_sec) : 0); #else/*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ---*/ seq_printf(seq, "hdl=%2u %-13s pid %4d triggered before: %s(avg %s) state %c oom_score %lu\n", handle + 1, ar7wdt_data.appl[handle].Name, p->pid, human_timediff(txtbuf[0], jiffies - ar7wdt_data.appl[handle].req_jiffies, 0), human_timediff(txtbuf[1], ar7wdt_data.appl[handle].avg_trigger, 0), task_state(p->state), pid_alive(p) ? badness(p, uptime.tv_sec) : 0); #endif/*--- #else ---*//*--- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) ---*/ } } while_each_thread(g, p); read_unlock_irqrestore(&tasklist_lock, flags); for(handle = 0; handle < MAX_WDT_APPLS; handle++) { if((ar7wdt_data.mask & (1 << handle))) { if((handle_mask & (1 << handle)) == 0) { seq_printf(seq, "hdl=%2u %-13s triggered before: %s (avg %s) %s\n", handle + 1, ar7wdt_data.appl[handle].Name, human_timediff(txtbuf[0], jiffies - ar7wdt_data.appl[handle].req_jiffies, 0), human_timediff(txtbuf[1], ar7wdt_data.appl[handle].avg_trigger, 0), ar7wdt_data.appl[handle].crashflag ? "maybe crashed" : "" ); } } } } /*------------------------------------------------------------------------------------------*\ \*------------------------------------------------------------------------------------------*/ EXPORT_SYMBOL(AVM_WATCHDOG_register); EXPORT_SYMBOL(AVM_WATCHDOG_release); EXPORT_SYMBOL(AVM_WATCHDOG_set_timeout); EXPORT_SYMBOL(AVM_WATCHDOG_trigger); EXPORT_SYMBOL(AVM_WATCHDOG_read); EXPORT_SYMBOL(AVM_WATCHDOG_reboot); EXPORT_SYMBOL(AVM_WATCHDOG_poll); EXPORT_SYMBOL(ar7wdt_no_reboot); #endif /*--- #if defined(CONFIG_AVM_WATCHDOG) ---*/