3

In Linux (running on ARM) there is one process that has an open fd to /dev/watchdog/ and is sending an ioctl() each few seconds as a keep alive:

while (1) { 
    ioctl(fd, WDIOC_KEEPALIVE, 0);
    sleep(10);
}

I want to send the keep alive from another process too, but I can't open another fd to /dev/watchdog/: when I tried to echo to /dev/watchdog/ I get the error "Device or resource busy".

  1. Where I can see that the watchdog is defined to work only with 1 process at a time? (I saw in another Linux that some processes can open fd to /dev/watchdog/).

  2. What can I do to feed the watchdog from 2 processes?

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128
yfr24493AzzrggAcom
  • 159
  • 1
  • 2
  • 13
  • Can't you open in non-exclusive mode in all processes? – 0andriy Jan 02 '20 at 21:13
  • A better way is an ipc to kicker. You circumvent the wdog if you kick from two places. – artless noise Jan 03 '20 at 14:09
  • 1
    Most hardware only supports one watchdog timer. If you want two processes that feed the watchdog, you can fork after opening it -- but you still only have one watchdog. There's no way to set it up so that it will trigger if either process dies; only if both die. – Chris Dodd Jan 30 '20 at 23:17
  • @Chris Dodd that not the question. The post talking about 1 watchdog (/dev/watchdog) that ping from 2 processes – yfr24493AzzrggAcom Jan 31 '20 at 09:30
  • 1
    @yfr24493AzzrggAcom: Not clear what you are asking -- having two processes sending pings to one watchdog is pretty much pointless and defeats the whole point of a watchdog (which is to detect if a process died). What are you trying to do? – Chris Dodd Jan 31 '20 at 17:23
  • How are you opening the file handle? You may also have to set up the configuration for it, but let's start with the first question. – JonBelanger Feb 01 '20 at 21:31

1 Answers1

5

Due to the implementation of /dev/watchdog in the kernel, only one process can use it at the same time, so opening /dev/watchdog from two different processes is not possible.

You can see this right in the source code of the Linux kernel, specifically in drivers/watchdog/watchdog_dev.c. Here's the relevant snippet of code:

/*
 *  watchdog_open: open the /dev/watchdog* devices.
 *  @inode: inode of device
 *  @file: file handle to device
 *
 *  When the /dev/watchdog* device gets opened, we start the watchdog.
 *  Watch out: the /dev/watchdog device is single open, so we make sure
 *  it can only be opened once.
 */

static int watchdog_open(struct inode *inode, struct file *file)
{
    /* ... */

    /* the watchdog is single open! */
    if (test_and_set_bit(_WDOG_DEV_OPEN, &wd_data->status))
        return -EBUSY;

    /* ... */

If you want to feed the watchdog from two different processes, you can work around this issue by creating a simple "master" program that talks to the watchdog while orchestrating the two subprocesses as you wish. This can be done in different ways (pipes, sockets, threads, etc). A single popen() per child process seems like a simple solution.

Here's a working example, master.c:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>

int main(int argc, char **argv) {
    int watchdog_fd;
    FILE *child1_fp, *child2_fp;

    if (argc != 3 || !argv[1] || !*argv[1] || !argv[2] || !*argv[2]) {
        fprintf(stderr, "Usage: %s 'CHILD_1_COMMAND' 'CHILD_2_COMMAND'\n", argv[0]);
        return 1;
    }

    // Open a fd to talk to the watchdog.
    watchdog_fd = open("/dev/watchdog", O_RDWR);
    if (watchdog_fd == -1) {
        perror("open failed");
        return 1;
    }

    // Start the first process.
    child1_fp = popen(argv[1], "r");
    if (child1_fp == NULL) {
        perror("popen (1) failed");
        return 1;
    }

    // Start the second process.
    child2_fp = popen(argv[2], "r");
    if (child2_fp == NULL) {
        perror("popen (2) failed");
        return 1;
    }

    while (1) {
        char tmp;
        size_t count;

        // Get one byte of data from each of the two processes.
        count = fread(&tmp, 1, 1, child1_fp);
        count += fread(&tmp, 1, 1, child2_fp);

        // If both processes provided the data, ping the watchdog.
        if (count == 2) {
            if (ioctl(watchdog_fd, WDIOC_KEEPALIVE, 0) < 0)
                perror("ioctl failed");
        }
    }

    return 0;
}

And two identical programs a.c and b.c just for testing purposes:

#include <stdio.h>
#include <unistd.h>

int main(void) {
    setvbuf(stdout, NULL, _IONBF, 0);

    while (1) {
        putchar('x');
        sleep(10);
    }
}

Compile and run:

$ gcc -o master master.c
$ gcc -o a a.c
$ gcc -o b b.c

$ ./master ./a ./b

In the above example code, master pings the watchdog if and only if the two children are alive and running: if one of the two hangs or dies, the master will stop pinging the watchdog. However, it's simple to rework the logic to work differently, and it's also simple to make it work with more than two child processes.

Marco Bonelli
  • 63,369
  • 21
  • 118
  • 128