~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to drivers/watchdog/softdog.c

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
ImportĀ upstreamĀ versionĀ 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      SoftDog 0.07:   A Software Watchdog Device
 
3
 *
 
4
 *      (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
 
5
 *                                                      All Rights Reserved.
 
6
 *
 
7
 *      This program is free software; you can redistribute it and/or
 
8
 *      modify it under the terms of the GNU General Public License
 
9
 *      as published by the Free Software Foundation; either version
 
10
 *      2 of the License, or (at your option) any later version.
 
11
 *
 
12
 *      Neither Alan Cox nor CymruNet Ltd. admit liability nor provide
 
13
 *      warranty for any of this software. This material is provided
 
14
 *      "AS-IS" and at no charge.
 
15
 *
 
16
 *      (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
 
17
 *
 
18
 *      Software only watchdog driver. Unlike its big brother the WDT501P
 
19
 *      driver this won't always recover a failed machine.
 
20
 *
 
21
 *  03/96: Angelo Haritsis <ah@doc.ic.ac.uk> :
 
22
 *      Modularised.
 
23
 *      Added soft_margin; use upon insmod to change the timer delay.
 
24
 *      NB: uses same minor as wdt (WATCHDOG_MINOR); we could use separate
 
25
 *          minors.
 
26
 *
 
27
 *  19980911 Alan Cox
 
28
 *      Made SMP safe for 2.3.x
 
29
 *
 
30
 *  20011127 Joel Becker (jlbec@evilplan.org>
 
31
 *      Added soft_noboot; Allows testing the softdog trigger without
 
32
 *      requiring a recompile.
 
33
 *      Added WDIOC_GETTIMEOUT and WDIOC_SETTIMOUT.
 
34
 *
 
35
 *  20020530 Joel Becker <joel.becker@oracle.com>
 
36
 *      Added Matt Domsch's nowayout module option.
 
37
 */
 
38
 
 
39
#include <linux/module.h>
 
40
#include <linux/moduleparam.h>
 
41
#include <linux/types.h>
 
42
#include <linux/timer.h>
 
43
#include <linux/miscdevice.h>
 
44
#include <linux/watchdog.h>
 
45
#include <linux/fs.h>
 
46
#include <linux/notifier.h>
 
47
#include <linux/reboot.h>
 
48
#include <linux/init.h>
 
49
#include <linux/jiffies.h>
 
50
#include <linux/uaccess.h>
 
51
#include <linux/kernel.h>
 
52
 
 
53
#define PFX "SoftDog: "
 
54
 
 
55
#define TIMER_MARGIN    60              /* Default is 60 seconds */
 
56
static int soft_margin = TIMER_MARGIN;  /* in seconds */
 
57
module_param(soft_margin, int, 0);
 
58
MODULE_PARM_DESC(soft_margin,
 
59
        "Watchdog soft_margin in seconds. (0 < soft_margin < 65536, default="
 
60
                                        __MODULE_STRING(TIMER_MARGIN) ")");
 
61
 
 
62
static int nowayout = WATCHDOG_NOWAYOUT;
 
63
module_param(nowayout, int, 0);
 
64
MODULE_PARM_DESC(nowayout,
 
65
                "Watchdog cannot be stopped once started (default="
 
66
                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 
67
 
 
68
#ifdef ONLY_TESTING
 
69
static int soft_noboot = 1;
 
70
#else
 
71
static int soft_noboot = 0;
 
72
#endif  /* ONLY_TESTING */
 
73
 
 
74
module_param(soft_noboot, int, 0);
 
75
MODULE_PARM_DESC(soft_noboot,
 
76
        "Softdog action, set to 1 to ignore reboots, 0 to reboot "
 
77
                                        "(default depends on ONLY_TESTING)");
 
78
 
 
79
static int soft_panic;
 
80
module_param(soft_panic, int, 0);
 
81
MODULE_PARM_DESC(soft_panic,
 
82
        "Softdog action, set to 1 to panic, 0 to reboot (default=0)");
 
83
 
 
84
/*
 
85
 *      Our timer
 
86
 */
 
87
 
 
88
static void watchdog_fire(unsigned long);
 
89
 
 
90
static struct timer_list watchdog_ticktock =
 
91
                TIMER_INITIALIZER(watchdog_fire, 0, 0);
 
92
static unsigned long driver_open, orphan_timer;
 
93
static char expect_close;
 
94
 
 
95
 
 
96
/*
 
97
 *      If the timer expires..
 
98
 */
 
99
 
 
100
static void watchdog_fire(unsigned long data)
 
101
{
 
102
        if (test_and_clear_bit(0, &orphan_timer))
 
103
                module_put(THIS_MODULE);
 
104
 
 
105
        if (soft_noboot)
 
106
                printk(KERN_CRIT PFX "Triggered - Reboot ignored.\n");
 
107
        else if (soft_panic) {
 
108
                printk(KERN_CRIT PFX "Initiating panic.\n");
 
109
                panic("Software Watchdog Timer expired.");
 
110
        } else {
 
111
                printk(KERN_CRIT PFX "Initiating system reboot.\n");
 
112
                emergency_restart();
 
113
                printk(KERN_CRIT PFX "Reboot didn't ?????\n");
 
114
        }
 
115
}
 
116
 
 
117
/*
 
118
 *      Softdog operations
 
119
 */
 
120
 
 
121
static int softdog_keepalive(void)
 
122
{
 
123
        mod_timer(&watchdog_ticktock, jiffies+(soft_margin*HZ));
 
124
        return 0;
 
125
}
 
126
 
 
127
static int softdog_stop(void)
 
128
{
 
129
        del_timer(&watchdog_ticktock);
 
130
        return 0;
 
131
}
 
132
 
 
133
static int softdog_set_heartbeat(int t)
 
134
{
 
135
        if ((t < 0x0001) || (t > 0xFFFF))
 
136
                return -EINVAL;
 
137
 
 
138
        soft_margin = t;
 
139
        return 0;
 
140
}
 
141
 
 
142
/*
 
143
 *      /dev/watchdog handling
 
144
 */
 
145
 
 
146
static int softdog_open(struct inode *inode, struct file *file)
 
147
{
 
148
        if (test_and_set_bit(0, &driver_open))
 
149
                return -EBUSY;
 
150
        if (!test_and_clear_bit(0, &orphan_timer))
 
151
                __module_get(THIS_MODULE);
 
152
        /*
 
153
         *      Activate timer
 
154
         */
 
155
        softdog_keepalive();
 
156
        return nonseekable_open(inode, file);
 
157
}
 
158
 
 
159
static int softdog_release(struct inode *inode, struct file *file)
 
160
{
 
161
        /*
 
162
         *      Shut off the timer.
 
163
         *      Lock it in if it's a module and we set nowayout
 
164
         */
 
165
        if (expect_close == 42) {
 
166
                softdog_stop();
 
167
                module_put(THIS_MODULE);
 
168
        } else {
 
169
                printk(KERN_CRIT PFX
 
170
                        "Unexpected close, not stopping watchdog!\n");
 
171
                set_bit(0, &orphan_timer);
 
172
                softdog_keepalive();
 
173
        }
 
174
        clear_bit(0, &driver_open);
 
175
        expect_close = 0;
 
176
        return 0;
 
177
}
 
178
 
 
179
static ssize_t softdog_write(struct file *file, const char __user *data,
 
180
                                                size_t len, loff_t *ppos)
 
181
{
 
182
        /*
 
183
         *      Refresh the timer.
 
184
         */
 
185
        if (len) {
 
186
                if (!nowayout) {
 
187
                        size_t i;
 
188
 
 
189
                        /* In case it was set long ago */
 
190
                        expect_close = 0;
 
191
 
 
192
                        for (i = 0; i != len; i++) {
 
193
                                char c;
 
194
 
 
195
                                if (get_user(c, data + i))
 
196
                                        return -EFAULT;
 
197
                                if (c == 'V')
 
198
                                        expect_close = 42;
 
199
                        }
 
200
                }
 
201
                softdog_keepalive();
 
202
        }
 
203
        return len;
 
204
}
 
205
 
 
206
static long softdog_ioctl(struct file *file, unsigned int cmd,
 
207
                                                        unsigned long arg)
 
208
{
 
209
        void __user *argp = (void __user *)arg;
 
210
        int __user *p = argp;
 
211
        int new_margin;
 
212
        static const struct watchdog_info ident = {
 
213
                .options =              WDIOF_SETTIMEOUT |
 
214
                                        WDIOF_KEEPALIVEPING |
 
215
                                        WDIOF_MAGICCLOSE,
 
216
                .firmware_version =     0,
 
217
                .identity =             "Software Watchdog",
 
218
        };
 
219
        switch (cmd) {
 
220
        case WDIOC_GETSUPPORT:
 
221
                return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
 
222
        case WDIOC_GETSTATUS:
 
223
        case WDIOC_GETBOOTSTATUS:
 
224
                return put_user(0, p);
 
225
        case WDIOC_KEEPALIVE:
 
226
                softdog_keepalive();
 
227
                return 0;
 
228
        case WDIOC_SETTIMEOUT:
 
229
                if (get_user(new_margin, p))
 
230
                        return -EFAULT;
 
231
                if (softdog_set_heartbeat(new_margin))
 
232
                        return -EINVAL;
 
233
                softdog_keepalive();
 
234
                /* Fall */
 
235
        case WDIOC_GETTIMEOUT:
 
236
                return put_user(soft_margin, p);
 
237
        default:
 
238
                return -ENOTTY;
 
239
        }
 
240
}
 
241
 
 
242
/*
 
243
 *      Notifier for system down
 
244
 */
 
245
 
 
246
static int softdog_notify_sys(struct notifier_block *this, unsigned long code,
 
247
        void *unused)
 
248
{
 
249
        if (code == SYS_DOWN || code == SYS_HALT)
 
250
                /* Turn the WDT off */
 
251
                softdog_stop();
 
252
        return NOTIFY_DONE;
 
253
}
 
254
 
 
255
/*
 
256
 *      Kernel Interfaces
 
257
 */
 
258
 
 
259
static const struct file_operations softdog_fops = {
 
260
        .owner          = THIS_MODULE,
 
261
        .llseek         = no_llseek,
 
262
        .write          = softdog_write,
 
263
        .unlocked_ioctl = softdog_ioctl,
 
264
        .open           = softdog_open,
 
265
        .release        = softdog_release,
 
266
};
 
267
 
 
268
static struct miscdevice softdog_miscdev = {
 
269
        .minor          = WATCHDOG_MINOR,
 
270
        .name           = "watchdog",
 
271
        .fops           = &softdog_fops,
 
272
};
 
273
 
 
274
static struct notifier_block softdog_notifier = {
 
275
        .notifier_call  = softdog_notify_sys,
 
276
};
 
277
 
 
278
static char banner[] __initdata = KERN_INFO "Software Watchdog Timer: 0.07 "
 
279
        "initialized. soft_noboot=%d soft_margin=%d sec soft_panic=%d "
 
280
        "(nowayout= %d)\n";
 
281
 
 
282
static int __init watchdog_init(void)
 
283
{
 
284
        int ret;
 
285
 
 
286
        /* Check that the soft_margin value is within it's range;
 
287
           if not reset to the default */
 
288
        if (softdog_set_heartbeat(soft_margin)) {
 
289
                softdog_set_heartbeat(TIMER_MARGIN);
 
290
                printk(KERN_INFO PFX
 
291
                    "soft_margin must be 0 < soft_margin < 65536, using %d\n",
 
292
                        TIMER_MARGIN);
 
293
        }
 
294
 
 
295
        ret = register_reboot_notifier(&softdog_notifier);
 
296
        if (ret) {
 
297
                printk(KERN_ERR PFX
 
298
                        "cannot register reboot notifier (err=%d)\n", ret);
 
299
                return ret;
 
300
        }
 
301
 
 
302
        ret = misc_register(&softdog_miscdev);
 
303
        if (ret) {
 
304
                printk(KERN_ERR PFX
 
305
                        "cannot register miscdev on minor=%d (err=%d)\n",
 
306
                                                WATCHDOG_MINOR, ret);
 
307
                unregister_reboot_notifier(&softdog_notifier);
 
308
                return ret;
 
309
        }
 
310
 
 
311
        printk(banner, soft_noboot, soft_margin, soft_panic, nowayout);
 
312
 
 
313
        return 0;
 
314
}
 
315
 
 
316
static void __exit watchdog_exit(void)
 
317
{
 
318
        misc_deregister(&softdog_miscdev);
 
319
        unregister_reboot_notifier(&softdog_notifier);
 
320
}
 
321
 
 
322
module_init(watchdog_init);
 
323
module_exit(watchdog_exit);
 
324
 
 
325
MODULE_AUTHOR("Alan Cox");
 
326
MODULE_DESCRIPTION("Software Watchdog Device Driver");
 
327
MODULE_LICENSE("GPL");
 
328
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);