watchdog: refuse to unload softdog module when its timer is running
authorLi RongQing <roy.qing.li@gmail.com>
Thu, 17 Dec 2015 13:30:02 +0000 (21:30 +0800)
committerWim Van Sebroeck <wim@iguana.be>
Sun, 27 Dec 2015 20:02:21 +0000 (21:02 +0100)
the softdog has static variables which are accessed if its timer is
still running after the driver is unloaded. and lead to crash:

   $modprobe softdog
   $echo 1 >/dev/watchdog
   $modprobe -r softdog

   CPU 20 Unable to handle kernel paging request at virtual address
   Oops[#1]:
   CPU: 20 PID: 0 Comm: swapper/20 Not tainted 4.1.13-WR8.0.0.0_standard
   ...
   Modules linked in: [last unloaded: softdog]
    ....
   Call Trace:
   [<ffffffff801e142c>] cascade+0x34/0xb0
   [<ffffffff801e1964>] run_timer_softirq+0x30c/0x368
   [<ffffffff80181044>] __do_softirq+0x1ec/0x418
   [<ffffffff801815d0>] irq_exit+0x90/0x98
   [<ffffffff8010749c>] plat_irq_dispatch+0xa4/0x140
   [<ffffffff80152740>] ret_from_irq+0x0/0x4
   [<ffffffff801529e0>] __r4k_wait+0x20/0x40
   [<ffffffff801c2278>] cpu_startup_entry+0x2a0/0x368
   [<ffffffff8015fa64>] start_secondary+0x444/0x4d8

add the module ref when timer is running to avoid to unload the softdog
module

Signed-off-by: Li RongQing <roy.qing.li@gmail.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
drivers/watchdog/softdog.c

index fe1e151..99a06f9 100644 (file)
@@ -86,6 +86,7 @@ static struct timer_list watchdog_ticktock =
 
 static void watchdog_fire(unsigned long data)
 {
+       module_put(THIS_MODULE);
        if (soft_noboot)
                pr_crit("Triggered - Reboot ignored\n");
        else if (soft_panic) {
@@ -104,13 +105,16 @@ static void watchdog_fire(unsigned long data)
 
 static int softdog_ping(struct watchdog_device *w)
 {
-       mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ));
+       if (!mod_timer(&watchdog_ticktock, jiffies+(w->timeout*HZ)))
+               __module_get(THIS_MODULE);
        return 0;
 }
 
 static int softdog_stop(struct watchdog_device *w)
 {
-       del_timer(&watchdog_ticktock);
+       if (del_timer(&watchdog_ticktock))
+               module_put(THIS_MODULE);
+
        return 0;
 }