gs_watchdogd: Use timeout defined by platform
Uses timeout value of watchdog device directly, without modifying it
with ioctl. On some platforms, especially these with multiple watchdog
devices, the timeout values can be strictly defined for the watchdogs to
timeout in a certain order. Leaves timeout value as it is and divides
minimal of them by 2 as watchdog kicking period.
Flag: EXEMPT normal/day-to-day bugfix
Bug: 348318712
Change-Id: Icdcce368f1803cd3b38a48f05e2788d881e3ad6f
diff --git a/gs_watchdogd/gs_watchdogd.cpp b/gs_watchdogd/gs_watchdogd.cpp
index 82e01d0..333e023 100644
--- a/gs_watchdogd/gs_watchdogd.cpp
+++ b/gs_watchdogd/gs_watchdogd.cpp
@@ -19,38 +19,31 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
+#include <log/log.h>
-#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <linux/watchdog.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/cdefs.h>
#include <unistd.h>
-#include <chrono>
+#include <cstdlib>
#include <vector>
+#define NSEC_PER_SEC (1000LL * 1000LL * 1000LL)
+
#define DEV_GLOB "/sys/devices/platform/*.watchdog_cl*/watchdog/watchdog*"
-#define DEFAULT_INTERVAL 10s
-#define DEFAULT_MARGIN 10s
-
using android::base::Basename;
using android::base::StringPrintf;
-using std::literals::chrono_literals::operator""s;
-int main(int argc, char** argv) {
+int main(int __unused argc, char** argv) {
+ auto min_timeout_nsecs = std::numeric_limits<typeof(NSEC_PER_SEC)>::max();
+
android::base::InitLogging(argv, &android::base::KernelLogger);
- std::chrono::seconds interval = argc >= 2
- ? std::chrono::seconds(atoi(argv[1])) : DEFAULT_INTERVAL;
- std::chrono::seconds margin = argc >= 3
- ? std::chrono::seconds(atoi(argv[2])) : DEFAULT_MARGIN;
-
- LOG(INFO) << "gs_watchdogd started (interval " << interval.count()
- << ", margin " << margin.count() << ")!";
-
glob_t globbuf;
int ret = glob(DEV_GLOB, GLOB_MARK, nullptr, &globbuf);
if (ret) {
@@ -61,8 +54,7 @@
std::vector<android::base::unique_fd> wdt_dev_fds;
for (size_t i = 0; i < globbuf.gl_pathc; i++) {
- std::chrono::seconds timeout = interval + margin;
- int timeout_secs = timeout.count();
+ int timeout_secs;
std::string dev_path = StringPrintf("/dev/%s", Basename(globbuf.gl_pathv[i]).c_str());
int fd = TEMP_FAILURE_RETRY(open(dev_path.c_str(), O_RDWR | O_CLOEXEC));
@@ -71,29 +63,39 @@
return 1;
}
- wdt_dev_fds.emplace_back(fd);
- ret = ioctl(fd, WDIOC_SETTIMEOUT, &timeout_secs);
+ ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout_secs);
if (ret) {
- PLOG(ERROR) << "Failed to set timeout to " << timeout_secs;
- ret = ioctl(fd, WDIOC_GETTIMEOUT, &timeout_secs);
- if (ret) {
- PLOG(ERROR) << "Failed to get timeout";
- } else {
- interval = timeout > margin ? timeout - margin : 1s;
- LOG(WARNING) << "Adjusted interval to timeout returned by driver: "
- << "timeout " << timeout_secs
- << ", interval " << interval.count()
- << ", margin " << margin.count();
- }
+ PLOG(ERROR) << "Failed to get timeout on " << dev_path;
+ continue;
+ } else {
+ min_timeout_nsecs = std::min(min_timeout_nsecs, NSEC_PER_SEC * timeout_secs);
}
+
+ wdt_dev_fds.emplace_back(fd);
}
globfree(&globbuf);
+ if (wdt_dev_fds.empty()) {
+ LOG(ERROR) << "no valid wdt dev found";
+ return 1;
+ }
+
+ timespec ts;
+ auto result = div(min_timeout_nsecs / 2, NSEC_PER_SEC);
+ ts.tv_sec = result.quot;
+ ts.tv_nsec = result.rem;
+
while (true) {
+ timespec rem = ts;
+
for (const auto& fd : wdt_dev_fds) {
TEMP_FAILURE_RETRY(write(fd, "", 1));
}
- sleep(interval.count());
+
+ if (TEMP_FAILURE_RETRY(nanosleep(&rem, &rem))) {
+ PLOG(ERROR) << "nanosleep failed";
+ return 1;
+ }
}
}
diff --git a/gs_watchdogd/init.gs_watchdogd.rc b/gs_watchdogd/init.gs_watchdogd.rc
index f58ce50..23d5fb2 100644
--- a/gs_watchdogd/init.gs_watchdogd.rc
+++ b/gs_watchdogd/init.gs_watchdogd.rc
@@ -1,5 +1,5 @@
-# Set watchdog timer to 30 seconds and pet it every 10 seconds to get a 20 second margin
-service gs_watchdogd /system_ext/bin/gs_watchdogd 10 20
+# Pet watchdog timer every half of its timeout period.
+service gs_watchdogd /system_ext/bin/gs_watchdogd
class core
oneshot
seclabel u:r:gs_watchdogd:s0