linux/drivers/watchdog/wdrtas.c
<<
>>
Prefs
   1/*
   2 * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as
   3 * RTAS calls are available
   4 */
   5
   6/*
   7 * RTAS watchdog driver
   8 *
   9 * (C) Copyright IBM Corp. 2005
  10 * device driver to exploit watchdog RTAS functions
  11 *
  12 * Authors : Utz Bacher <utz.bacher@de.ibm.com>
  13 *
  14 * This program is free software; you can redistribute it and/or modify
  15 * it under the terms of the GNU General Public License as published by
  16 * the Free Software Foundation; either version 2, or (at your option)
  17 * any later version.
  18 *
  19 * This program is distributed in the hope that it will be useful,
  20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  22 * GNU General Public License for more details.
  23 *
  24 * You should have received a copy of the GNU General Public License
  25 * along with this program; if not, write to the Free Software
  26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  27 */
  28
  29#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  30
  31#include <linux/fs.h>
  32#include <linux/init.h>
  33#include <linux/kernel.h>
  34#include <linux/miscdevice.h>
  35#include <linux/module.h>
  36#include <linux/notifier.h>
  37#include <linux/reboot.h>
  38#include <linux/types.h>
  39#include <linux/watchdog.h>
  40#include <linux/uaccess.h>
  41
  42#include <asm/rtas.h>
  43
  44#define WDRTAS_MAGIC_CHAR               42
  45#define WDRTAS_SUPPORTED_MASK           (WDIOF_SETTIMEOUT | \
  46                                         WDIOF_MAGICCLOSE)
  47
  48MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
  49MODULE_DESCRIPTION("RTAS watchdog driver");
  50MODULE_LICENSE("GPL");
  51MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
  52MODULE_ALIAS_MISCDEV(TEMP_MINOR);
  53
  54static bool wdrtas_nowayout = WATCHDOG_NOWAYOUT;
  55static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
  56static char wdrtas_expect_close;
  57
  58static int wdrtas_interval;
  59
  60#define WDRTAS_THERMAL_SENSOR           3
  61static int wdrtas_token_get_sensor_state;
  62#define WDRTAS_SURVEILLANCE_IND         9000
  63static int wdrtas_token_set_indicator;
  64#define WDRTAS_SP_SPI                   28
  65static int wdrtas_token_get_sp;
  66static int wdrtas_token_event_scan;
  67
  68#define WDRTAS_DEFAULT_INTERVAL         300
  69
  70#define WDRTAS_LOGBUFFER_LEN            128
  71static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN];
  72
  73
  74/*** watchdog access functions */
  75
  76/**
  77 * wdrtas_set_interval - sets the watchdog interval
  78 * @interval: new interval
  79 *
  80 * returns 0 on success, <0 on failures
  81 *
  82 * wdrtas_set_interval sets the watchdog keepalive interval by calling the
  83 * RTAS function set-indicator (surveillance). The unit of interval is
  84 * seconds.
  85 */
  86
  87static int wdrtas_set_interval(int interval)
  88{
  89        long result;
  90        static int print_msg = 10;
  91
  92        /* rtas uses minutes */
  93        interval = (interval + 59) / 60;
  94
  95        result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
  96                           WDRTAS_SURVEILLANCE_IND, 0, interval);
  97        if (result < 0 && print_msg) {
  98                pr_err("setting the watchdog to %i timeout failed: %li\n",
  99                       interval, result);
 100                print_msg--;
 101        }
 102
 103        return result;
 104}
 105
 106#define WDRTAS_SP_SPI_LEN 4
 107
 108/**
 109 * wdrtas_get_interval - returns the current watchdog interval
 110 * @fallback_value: value (in seconds) to use, if the RTAS call fails
 111 *
 112 * returns the interval
 113 *
 114 * wdrtas_get_interval returns the current watchdog keepalive interval
 115 * as reported by the RTAS function ibm,get-system-parameter. The unit
 116 * of the return value is seconds.
 117 */
 118static int wdrtas_get_interval(int fallback_value)
 119{
 120        long result;
 121        char value[WDRTAS_SP_SPI_LEN];
 122
 123        spin_lock(&rtas_data_buf_lock);
 124        memset(rtas_data_buf, 0, WDRTAS_SP_SPI_LEN);
 125        result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
 126                           WDRTAS_SP_SPI, __pa(rtas_data_buf),
 127                           WDRTAS_SP_SPI_LEN);
 128
 129        memcpy(value, rtas_data_buf, WDRTAS_SP_SPI_LEN);
 130        spin_unlock(&rtas_data_buf_lock);
 131
 132        if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
 133                pr_warn("could not get sp_spi watchdog timeout (%li). Continuing\n",
 134                        result);
 135                return fallback_value;
 136        }
 137
 138        /* rtas uses minutes */
 139        return ((int)value[2]) * 60;
 140}
 141
 142/**
 143 * wdrtas_timer_start - starts watchdog
 144 *
 145 * wdrtas_timer_start starts the watchdog by calling the RTAS function
 146 * set-interval (surveillance)
 147 */
 148static void wdrtas_timer_start(void)
 149{
 150        wdrtas_set_interval(wdrtas_interval);
 151}
 152
 153/**
 154 * wdrtas_timer_stop - stops watchdog
 155 *
 156 * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function
 157 * set-interval (surveillance)
 158 */
 159static void wdrtas_timer_stop(void)
 160{
 161        wdrtas_set_interval(0);
 162}
 163
 164/**
 165 * wdrtas_log_scanned_event - logs an event we received during keepalive
 166 *
 167 * wdrtas_log_scanned_event prints a message to the log buffer dumping
 168 * the results of the last event-scan call
 169 */
 170static void wdrtas_log_scanned_event(void)
 171{
 172        int i;
 173
 174        for (i = 0; i < WDRTAS_LOGBUFFER_LEN; i += 16)
 175                pr_info("dumping event (line %i/%i), data = "
 176                        "%02x %02x %02x %02x  %02x %02x %02x %02x   "
 177                        "%02x %02x %02x %02x  %02x %02x %02x %02x\n",
 178                        (i / 16) + 1, (WDRTAS_LOGBUFFER_LEN / 16),
 179                        wdrtas_logbuffer[i + 0], wdrtas_logbuffer[i + 1],
 180                        wdrtas_logbuffer[i + 2], wdrtas_logbuffer[i + 3],
 181                        wdrtas_logbuffer[i + 4], wdrtas_logbuffer[i + 5],
 182                        wdrtas_logbuffer[i + 6], wdrtas_logbuffer[i + 7],
 183                        wdrtas_logbuffer[i + 8], wdrtas_logbuffer[i + 9],
 184                        wdrtas_logbuffer[i + 10], wdrtas_logbuffer[i + 11],
 185                        wdrtas_logbuffer[i + 12], wdrtas_logbuffer[i + 13],
 186                        wdrtas_logbuffer[i + 14], wdrtas_logbuffer[i + 15]);
 187}
 188
 189/**
 190 * wdrtas_timer_keepalive - resets watchdog timer to keep system alive
 191 *
 192 * wdrtas_timer_keepalive restarts the watchdog timer by calling the
 193 * RTAS function event-scan and repeats these calls as long as there are
 194 * events available. All events will be dumped.
 195 */
 196static void wdrtas_timer_keepalive(void)
 197{
 198        long result;
 199
 200        do {
 201                result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
 202                                   RTAS_EVENT_SCAN_ALL_EVENTS, 0,
 203                                   (void *)__pa(wdrtas_logbuffer),
 204                                   WDRTAS_LOGBUFFER_LEN);
 205                if (result < 0)
 206                        pr_err("event-scan failed: %li\n", result);
 207                if (result == 0)
 208                        wdrtas_log_scanned_event();
 209        } while (result == 0);
 210}
 211
 212/**
 213 * wdrtas_get_temperature - returns current temperature
 214 *
 215 * returns temperature or <0 on failures
 216 *
 217 * wdrtas_get_temperature returns the current temperature in Fahrenheit. It
 218 * uses the RTAS call get-sensor-state, token 3 to do so
 219 */
 220static int wdrtas_get_temperature(void)
 221{
 222        int result;
 223        int temperature = 0;
 224
 225        result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
 226
 227        if (result < 0)
 228                pr_warn("reading the thermal sensor failed: %i\n", result);
 229        else
 230                temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
 231
 232        return temperature;
 233}
 234
 235/**
 236 * wdrtas_get_status - returns the status of the watchdog
 237 *
 238 * returns a bitmask of defines WDIOF_... as defined in
 239 * include/linux/watchdog.h
 240 */
 241static int wdrtas_get_status(void)
 242{
 243        return 0; /* TODO */
 244}
 245
 246/**
 247 * wdrtas_get_boot_status - returns the reason for the last boot
 248 *
 249 * returns a bitmask of defines WDIOF_... as defined in
 250 * include/linux/watchdog.h, indicating why the watchdog rebooted the system
 251 */
 252static int wdrtas_get_boot_status(void)
 253{
 254        return 0; /* TODO */
 255}
 256
 257/*** watchdog API and operations stuff */
 258
 259/* wdrtas_write - called when watchdog device is written to
 260 * @file: file structure
 261 * @buf: user buffer with data
 262 * @len: amount to data written
 263 * @ppos: position in file
 264 *
 265 * returns the number of successfully processed characters, which is always
 266 * the number of bytes passed to this function
 267 *
 268 * wdrtas_write processes all the data given to it and looks for the magic
 269 * character 'V'. This character allows the watchdog device to be closed
 270 * properly.
 271 */
 272static ssize_t wdrtas_write(struct file *file, const char __user *buf,
 273             size_t len, loff_t *ppos)
 274{
 275        int i;
 276        char c;
 277
 278        if (!len)
 279                goto out;
 280
 281        if (!wdrtas_nowayout) {
 282                wdrtas_expect_close = 0;
 283                /* look for 'V' */
 284                for (i = 0; i < len; i++) {
 285                        if (get_user(c, buf + i))
 286                                return -EFAULT;
 287                        /* allow to close device */
 288                        if (c == 'V')
 289                                wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
 290                }
 291        }
 292
 293        wdrtas_timer_keepalive();
 294
 295out:
 296        return len;
 297}
 298
 299/**
 300 * wdrtas_ioctl - ioctl function for the watchdog device
 301 * @file: file structure
 302 * @cmd: command for ioctl
 303 * @arg: argument pointer
 304 *
 305 * returns 0 on success, <0 on failure
 306 *
 307 * wdrtas_ioctl implements the watchdog API ioctls
 308 */
 309
 310static long wdrtas_ioctl(struct file *file, unsigned int cmd,
 311                                                        unsigned long arg)
 312{
 313        int __user *argp = (void __user *)arg;
 314        int i;
 315        static const struct watchdog_info wdinfo = {
 316                .options = WDRTAS_SUPPORTED_MASK,
 317                .firmware_version = 0,
 318                .identity = "wdrtas",
 319        };
 320
 321        switch (cmd) {
 322        case WDIOC_GETSUPPORT:
 323                if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
 324                        return -EFAULT;
 325                return 0;
 326
 327        case WDIOC_GETSTATUS:
 328                i = wdrtas_get_status();
 329                return put_user(i, argp);
 330
 331        case WDIOC_GETBOOTSTATUS:
 332                i = wdrtas_get_boot_status();
 333                return put_user(i, argp);
 334
 335        case WDIOC_GETTEMP:
 336                if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
 337                        return -EOPNOTSUPP;
 338
 339                i = wdrtas_get_temperature();
 340                return put_user(i, argp);
 341
 342        case WDIOC_SETOPTIONS:
 343                if (get_user(i, argp))
 344                        return -EFAULT;
 345                if (i & WDIOS_DISABLECARD)
 346                        wdrtas_timer_stop();
 347                if (i & WDIOS_ENABLECARD) {
 348                        wdrtas_timer_keepalive();
 349                        wdrtas_timer_start();
 350                }
 351                /* not implemented. Done by H8
 352                if (i & WDIOS_TEMPPANIC) {
 353                } */
 354                return 0;
 355
 356        case WDIOC_KEEPALIVE:
 357                wdrtas_timer_keepalive();
 358                return 0;
 359
 360        case WDIOC_SETTIMEOUT:
 361                if (get_user(i, argp))
 362                        return -EFAULT;
 363
 364                if (wdrtas_set_interval(i))
 365                        return -EINVAL;
 366
 367                wdrtas_timer_keepalive();
 368
 369                if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
 370                        wdrtas_interval = i;
 371                else
 372                        wdrtas_interval = wdrtas_get_interval(i);
 373                /* fallthrough */
 374
 375        case WDIOC_GETTIMEOUT:
 376                return put_user(wdrtas_interval, argp);
 377
 378        default:
 379                return -ENOTTY;
 380        }
 381}
 382
 383/**
 384 * wdrtas_open - open function of watchdog device
 385 * @inode: inode structure
 386 * @file: file structure
 387 *
 388 * returns 0 on success, -EBUSY if the file has been opened already, <0 on
 389 * other failures
 390 *
 391 * function called when watchdog device is opened
 392 */
 393static int wdrtas_open(struct inode *inode, struct file *file)
 394{
 395        /* only open once */
 396        if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
 397                atomic_dec(&wdrtas_miscdev_open);
 398                return -EBUSY;
 399        }
 400
 401        wdrtas_timer_start();
 402        wdrtas_timer_keepalive();
 403
 404        return nonseekable_open(inode, file);
 405}
 406
 407/**
 408 * wdrtas_close - close function of watchdog device
 409 * @inode: inode structure
 410 * @file: file structure
 411 *
 412 * returns 0 on success
 413 *
 414 * close function. Always succeeds
 415 */
 416static int wdrtas_close(struct inode *inode, struct file *file)
 417{
 418        /* only stop watchdog, if this was announced using 'V' before */
 419        if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
 420                wdrtas_timer_stop();
 421        else {
 422                pr_warn("got unexpected close. Watchdog not stopped.\n");
 423                wdrtas_timer_keepalive();
 424        }
 425
 426        wdrtas_expect_close = 0;
 427        atomic_dec(&wdrtas_miscdev_open);
 428        return 0;
 429}
 430
 431/**
 432 * wdrtas_temp_read - gives back the temperature in fahrenheit
 433 * @file: file structure
 434 * @buf: user buffer
 435 * @count: number of bytes to be read
 436 * @ppos: position in file
 437 *
 438 * returns always 1 or -EFAULT in case of user space copy failures, <0 on
 439 * other failures
 440 *
 441 * wdrtas_temp_read gives the temperature to the users by copying this
 442 * value as one byte into the user space buffer. The unit is Fahrenheit...
 443 */
 444static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
 445                 size_t count, loff_t *ppos)
 446{
 447        int temperature = 0;
 448
 449        temperature = wdrtas_get_temperature();
 450        if (temperature < 0)
 451                return temperature;
 452
 453        if (copy_to_user(buf, &temperature, 1))
 454                return -EFAULT;
 455
 456        return 1;
 457}
 458
 459/**
 460 * wdrtas_temp_open - open function of temperature device
 461 * @inode: inode structure
 462 * @file: file structure
 463 *
 464 * returns 0 on success, <0 on failure
 465 *
 466 * function called when temperature device is opened
 467 */
 468static int wdrtas_temp_open(struct inode *inode, struct file *file)
 469{
 470        return nonseekable_open(inode, file);
 471}
 472
 473/**
 474 * wdrtas_temp_close - close function of temperature device
 475 * @inode: inode structure
 476 * @file: file structure
 477 *
 478 * returns 0 on success
 479 *
 480 * close function. Always succeeds
 481 */
 482static int wdrtas_temp_close(struct inode *inode, struct file *file)
 483{
 484        return 0;
 485}
 486
 487/**
 488 * wdrtas_reboot - reboot notifier function
 489 * @nb: notifier block structure
 490 * @code: reboot code
 491 * @ptr: unused
 492 *
 493 * returns NOTIFY_DONE
 494 *
 495 * wdrtas_reboot stops the watchdog in case of a reboot
 496 */
 497static int wdrtas_reboot(struct notifier_block *this,
 498                                        unsigned long code, void *ptr)
 499{
 500        if (code == SYS_DOWN || code == SYS_HALT)
 501                wdrtas_timer_stop();
 502
 503        return NOTIFY_DONE;
 504}
 505
 506/*** initialization stuff */
 507
 508static const struct file_operations wdrtas_fops = {
 509        .owner          = THIS_MODULE,
 510        .llseek         = no_llseek,
 511        .write          = wdrtas_write,
 512        .unlocked_ioctl = wdrtas_ioctl,
 513        .open           = wdrtas_open,
 514        .release        = wdrtas_close,
 515};
 516
 517static struct miscdevice wdrtas_miscdev = {
 518        .minor =        WATCHDOG_MINOR,
 519        .name =         "watchdog",
 520        .fops =         &wdrtas_fops,
 521};
 522
 523static const struct file_operations wdrtas_temp_fops = {
 524        .owner          = THIS_MODULE,
 525        .llseek         = no_llseek,
 526        .read           = wdrtas_temp_read,
 527        .open           = wdrtas_temp_open,
 528        .release        = wdrtas_temp_close,
 529};
 530
 531static struct miscdevice wdrtas_tempdev = {
 532        .minor =        TEMP_MINOR,
 533        .name =         "temperature",
 534        .fops =         &wdrtas_temp_fops,
 535};
 536
 537static struct notifier_block wdrtas_notifier = {
 538        .notifier_call =        wdrtas_reboot,
 539};
 540
 541/**
 542 * wdrtas_get_tokens - reads in RTAS tokens
 543 *
 544 * returns 0 on success, <0 on failure
 545 *
 546 * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
 547 * this watchdog driver. It tolerates, if "get-sensor-state" and
 548 * "ibm,get-system-parameter" are not available.
 549 */
 550static int wdrtas_get_tokens(void)
 551{
 552        wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
 553        if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
 554                pr_warn("couldn't get token for get-sensor-state. Trying to continue without temperature support.\n");
 555        }
 556
 557        wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
 558        if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
 559                pr_warn("couldn't get token for ibm,get-system-parameter. Trying to continue with a default timeout value of %i seconds.\n",
 560                        WDRTAS_DEFAULT_INTERVAL);
 561        }
 562
 563        wdrtas_token_set_indicator = rtas_token("set-indicator");
 564        if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
 565                pr_err("couldn't get token for set-indicator. Terminating watchdog code.\n");
 566                return -EIO;
 567        }
 568
 569        wdrtas_token_event_scan = rtas_token("event-scan");
 570        if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
 571                pr_err("couldn't get token for event-scan. Terminating watchdog code.\n");
 572                return -EIO;
 573        }
 574
 575        return 0;
 576}
 577
 578/**
 579 * wdrtas_unregister_devs - unregisters the misc dev handlers
 580 *
 581 * wdrtas_register_devs unregisters the watchdog and temperature watchdog
 582 * misc devs
 583 */
 584static void wdrtas_unregister_devs(void)
 585{
 586        misc_deregister(&wdrtas_miscdev);
 587        if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
 588                misc_deregister(&wdrtas_tempdev);
 589}
 590
 591/**
 592 * wdrtas_register_devs - registers the misc dev handlers
 593 *
 594 * returns 0 on success, <0 on failure
 595 *
 596 * wdrtas_register_devs registers the watchdog and temperature watchdog
 597 * misc devs
 598 */
 599static int wdrtas_register_devs(void)
 600{
 601        int result;
 602
 603        result = misc_register(&wdrtas_miscdev);
 604        if (result) {
 605                pr_err("couldn't register watchdog misc device. Terminating watchdog code.\n");
 606                return result;
 607        }
 608
 609        if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
 610                result = misc_register(&wdrtas_tempdev);
 611                if (result) {
 612                        pr_warn("couldn't register watchdog temperature misc device. Continuing without temperature support.\n");
 613                        wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
 614                }
 615        }
 616
 617        return 0;
 618}
 619
 620/**
 621 * wdrtas_init - init function of the watchdog driver
 622 *
 623 * returns 0 on success, <0 on failure
 624 *
 625 * registers the file handlers and the reboot notifier
 626 */
 627static int __init wdrtas_init(void)
 628{
 629        if (wdrtas_get_tokens())
 630                return -ENODEV;
 631
 632        if (wdrtas_register_devs())
 633                return -ENODEV;
 634
 635        if (register_reboot_notifier(&wdrtas_notifier)) {
 636                pr_err("could not register reboot notifier. Terminating watchdog code.\n");
 637                wdrtas_unregister_devs();
 638                return -ENODEV;
 639        }
 640
 641        if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
 642                wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
 643        else
 644                wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
 645
 646        return 0;
 647}
 648
 649/**
 650 * wdrtas_exit - exit function of the watchdog driver
 651 *
 652 * unregisters the file handlers and the reboot notifier
 653 */
 654static void __exit wdrtas_exit(void)
 655{
 656        if (!wdrtas_nowayout)
 657                wdrtas_timer_stop();
 658
 659        wdrtas_unregister_devs();
 660
 661        unregister_reboot_notifier(&wdrtas_notifier);
 662}
 663
 664module_init(wdrtas_init);
 665module_exit(wdrtas_exit);
 666
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.