[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20151123182608.GA19888@pengutronix.de>
Date: Mon, 23 Nov 2015 19:26:08 +0100
From: Uwe Kleine-König
<u.kleine-koenig@...gutronix.de>
To: Guenter Roeck <linux@...ck-us.net>
Cc: linux-watchdog@...r.kernel.org, Wim Van Sebroeck <wim@...ana.be>,
linux-kernel@...r.kernel.org,
Timo Kokkonen <timo.kokkonen@...code.fi>,
linux-doc@...r.kernel.org, Jonathan Corbet <corbet@....net>
Subject: Re: [PATCH v5 1/8] watchdog: Introduce hardware maximum timeout in
watchdog core
Hello Guenter,
On Mon, Nov 23, 2015 at 08:14:56AM -0800, Guenter Roeck wrote:
> On 11/22/2015 11:53 PM, Uwe Kleine-König wrote:
> >On Sun, Nov 22, 2015 at 07:20:58PM -0800, Guenter Roeck wrote:
> >>@@ -160,7 +176,11 @@ they are supported. These optional routines/operations are:
> >> and -EIO for "could not write value to the watchdog". On success this
> >> routine should set the timeout value of the watchdog_device to the
> >> achieved timeout value (which may be different from the requested one
> >>- because the watchdog does not necessarily has a 1 second resolution).
> >>+ because the watchdog does not necessarily have a 1 second resolution).
> >>+ Drivers implementing hw_max_timeout_ms set the hardware watchdog timeout
> >>+ to the minimum of timeout and hw_max_timeout_ms. Those drivers set the
> >
> >Actually this is something that the wdg core could abstract for drivers.
> >Oh well, apart from hw_max_timeout_ms having ms accuracy.
> >
> Not that sure. about the abstraction. The actual timeout to set depends on
> the hardware, and may have an unknown granularity. The watchdog core can
What I wanted to say is that the driver core can do (ignoring scaling
due to different units):
if (timeout > wdd->hw_max_timeout_ms)
wdd->set_timeout(wdd->hw_max_timeout_ms);
else
wdd->set_timeout(timeout)
So that the device specific driver can assume that timeout passed to its
set_timeout callback is always less than or equal to hw_max_timeout_ms.
And so it doesn't need to compare against hw_max_timeout_ms again.
> not predict what that granularity would be. We can play with it, and try
> if it can be done, but I really would like this to be a separate patch.
>
> hw_max_timeout is in ms because some watchdogs have a very low maximum
> HW timeout.
>
> >>+ timeout value of the watchdog_device either to the requested timeout value
> >>+ (if it is larger than hw_max_timeout_ms), or to the achieved timeout value.
> >> (Note: the WDIOF_SETTIMEOUT needs to be set in the options field of the
> >> watchdog's info structure).
> >> * get_timeleft: this routines returns the time that's left before a reset.
> >>diff --git a/drivers/watchdog/watchdog_dev.c b/drivers/watchdog/watchdog_dev.c
> >>index 56a649e66eb2..1dba3f57dba3 100644
> >>--- a/drivers/watchdog/watchdog_dev.c
> >>+++ b/drivers/watchdog/watchdog_dev.c
> >>[...]
> >>+static long watchdog_next_keepalive(struct watchdog_device *wdd)
> >>+{
> >>+ unsigned int timeout_ms = wdd->timeout * 1000;
> >>+ unsigned long keepalive_interval;
> >>+ unsigned long last_heartbeat;
> >>+ unsigned long virt_timeout;
> >>+ unsigned int hw_timeout_ms;
> >>+
> >>+ virt_timeout = wdd->last_keepalive + msecs_to_jiffies(timeout_ms);
> >
> >I think it's sensible to store
> >
> > last_keepalive + timeout
> >
> >(i.e. the expected expiration time) in struct watchdog_device instead of
> >last_keepalive. This moves the (maybe expensive) calculation to a
> >context that has userspace interaction anyhow. On the other hand this
> >complicates the set_timeout call. Hmm.
> >
>
> I would rather keep the code simple. It is not as if this is called
> all the time. Also, I need last_keepalive later in the series
> to determine if the minimum timeout between hardware pings has elapsed,
> so we would need both.
I'm not sure my suggestion improves the situation, but I will keep my
idea in mind and check once the dust settled.
> >>+ hw_timeout_ms = min(timeout_ms, wdd->max_hw_timeout_ms);
> >>+ keepalive_interval = msecs_to_jiffies(hw_timeout_ms / 2);
> >>+
> >>+ /*
> >>+ * To ensure that the watchdog times out wdd->timeout seconds
> >>+ * after the most recent ping from userspace, the last
> >>+ * worker ping has to come in hw_timeout_ms before this timeout.
> >>+ */
> >>+ last_heartbeat = virt_timeout - msecs_to_jiffies(hw_timeout_ms);
> >>+ return min_t(long, last_heartbeat - jiffies, keepalive_interval);
> >>+}
> >>[...]
> >>@@ -61,26 +137,25 @@ static struct watchdog_device *old_wdd;
> >>
> >> static int watchdog_ping(struct watchdog_device *wdd)
> >> {
> >>- int err = 0;
> >>+ int err;
> >>
> >> mutex_lock(&wdd->lock);
> >>+ wdd->last_keepalive = jiffies;
> >>+ err = _watchdog_ping(wdd);
> >>+ mutex_unlock(&wdd->lock);
> >>
> >>- if (test_bit(WDOG_UNREGISTERED, &wdd->status)) {
> >>- err = -ENODEV;
> >>- goto out_ping;
> >>- }
> >>+ return err;
> >>+}
> >>
> >>- if (!watchdog_active(wdd))
> >>- goto out_ping;
> >>+static void watchdog_ping_work(struct work_struct *work)
> >>+{
> >>+ struct watchdog_device *wdd;
> >>
> >>- if (wdd->ops->ping)
> >>- err = wdd->ops->ping(wdd); /* ping the watchdog */
> >>- else
> >>- err = wdd->ops->start(wdd); /* restart watchdog */
> >>+ wdd = container_of(to_delayed_work(work), struct watchdog_device, work);
> >>
> >>-out_ping:
> >>+ mutex_lock(&wdd->lock);
> >>+ _watchdog_ping(wdd);
> >> mutex_unlock(&wdd->lock);
> >>- return err;
> >
> >Calling this function might come after last_keepalive + timeout in which
> >case the watchdog shouldn't be pinged.
> >
> Unless the code is wrong, the last time this function is called should be
> at (timeout - max_hw_timeout), ie well before the timeout elapsed.
> Given that, I don't think this is something to be concerned about.
Depends on max_hw_timeout and the load of the machine if that can
happen. And thinking again, if the ping didn't come in until
last_keepalive + timeout, the machine is reset anyhow ...
> >> }
> >>
> >> /*
> >>@@ -107,8 +182,11 @@ static int watchdog_start(struct watchdog_device *wdd)
> >> goto out_start;
> >>
> >> err = wdd->ops->start(wdd);
> >>- if (err == 0)
> >>+ if (err == 0) {
> >> set_bit(WDOG_ACTIVE, &wdd->status);
> >>+ wdd->last_keepalive = jiffies;
> >>+ watchdog_update_worker(wdd, true);
> >>+ }
> >
> >I think it's more correct to sample jiffies before calling .start.
> >Something like:
> >
> > unsigned long started_at = jiffies;
> >
> > err = wdd->ops->start(wdd);
> > if (err == 0)
> > wdd->last_keepalive = jiffies;
> >
> I assume you mean
> wdd->last_keepalive = started_at;
right.
Best regards
Uwe
--
Pengutronix e.K. | Uwe Kleine-König |
Industrial Linux Solutions | http://www.pengutronix.de/ |
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists