Watchdog 서브시스템
Watchdog 서브시스템을 시스템 생존성 보장과 장애 자동 복구 관점에서 심층 정리합니다. watchdog_device/watchdog_ops 생명주기, timeout·pretimeout·nowayout 정책의 운영 의미, 하드웨어 워치독(iTCO 등)과 softdog 차이, userspace daemon과 커널 keepalive 협력 구조, pretimeout governor를 이용한 덤프 수집, suspend/resume 중 watchdog 유지 전략, 오동작 리셋 루프 방지 설정, 현장 장애 재현과 검증 절차까지 고신뢰 시스템 구축에 필요한 핵심을 다룹니다.
핵심 요약
- watchdog_device 구조체 — id, timeout, pretimeout, min/max_timeout, status 비트를 포함하는 핵심 객체입니다.
- watchdog_ops 콜백 — start/stop/ping/set_timeout/get_timeleft 다섯 가지가 핵심이며, ping이 없으면 기본적으로 stop+start로 대체됩니다.
- nowayout 플래그 — 일단 활성화되면 Magic Close('V')조차 무시하고 watchdog을 절대 멈추지 않습니다. 임베디드 프로덕션에서 필수 설정입니다.
- pretimeout governor — 리셋 전에 IRQ/NMI를 발생시켜 kdump로 크래시 덤프를 확보합니다. noop/panic 두 가지 governor가 있습니다.
- keep_alive 커널 워커 — 유저 프로세스가 파일을 닫아도 커널 내부 hrtimer가 ping을 계속 유지합니다.
- WDOG_HW_RUNNING 비트 — 부트로더가 WDT를 활성화한 채로 커널에 진입할 때 이 비트를 설정하면 커널이 즉시 ping을 시작합니다.
- devm_watchdog_register_device() — devres 기반 등록으로 remove() 시 자동 해제됩니다.
- systemd 통합 — RuntimeWatchdogSec으로 PID 1이 HW watchdog을 관리하고, WatchdogSec으로 서비스별 liveness를 체크합니다.
단계별 이해
- 하드웨어 watchdog 확인
dmesg에서 "watchdog" 메시지와 /sys/class/watchdog/ 디렉터리로 HW watchdog 존재를 확인합니다. - watchdog_device 초기화
min/max_timeout, 기본 timeout, info, ops를 설정하고 devm_watchdog_register_device()로 등록합니다. - nowayout 전략 결정
프로덕션 임베디드는 nowayout=1, 개발/테스트 환경은 nowayout=0으로 시작합니다. - pretimeout 설정
kdump 연동을 원하면 pretimeout governor를 panic으로 설정하고 IRQ 핸들러에서 watchdog_notify_pretimeout()을 호출합니다. - 사용자 공간 kick 전략
watchdog 데몬, systemd RuntimeWatchdogSec, 또는 직접 /dev/watchdog 중 환경에 맞는 방식을 선택합니다. - WDOG_HW_RUNNING 처리
부트로더가 WDT를 시작했으면 probe()에서 HW 레지스터를 확인하여 set_bit(WDOG_HW_RUNNING, ...) 합니다. - PM 통합 검증
suspend/resume 이후 watchdog이 정상 동작하는지 확인합니다. PM notifier 또는 드라이버 pm_ops에서 재초기화합니다. - 부팅 루프 방지
bootstatus로 이전 부팅이 WDT 리셋이었는지 확인하고, 반복 루프 시 안전 모드 진입 로직을 추가합니다.
Watchdog 서브시스템 개요
Watchdog 서브시스템(drivers/watchdog/)은 시스템이 비정상 상태(hang, deadlock, 커널 패닉 미검출 등)에 빠졌을 때 하드웨어 또는 소프트웨어 타이머를 이용해 강제 리셋을 수행하는 프레임워크입니다. 사용자 공간 데몬이나 커널 스레드가 주기적으로 watchdog을 "kick"(ping)하고, 일정 시간 안에 kick이 없으면 시스템을 리부팅합니다.
핵심 데이터 구조
Watchdog 프레임워크의 핵심은 세 가지 구조체입니다: watchdog_device(드라이버가 등록하는 watchdog 인스턴스), watchdog_ops(하드웨어 제어 콜백), watchdog_info(유저 공간에 노출되는 정보).
/* include/linux/watchdog.h */
struct watchdog_device {
int id; /* watchdog 인스턴스 번호 (0 → /dev/watchdog0) */
struct device *parent; /* 부모 디바이스 (platform_device.dev 등) */
const struct watchdog_info *info; /* 유저 공간 노출 정보 (identity, options) */
const struct watchdog_ops *ops; /* 하드웨어 제어 콜백 */
const struct watchdog_governor *gov; /* pretimeout governor */
unsigned int bootstatus; /* 부팅 시 watchdog 리셋 발생 여부 */
unsigned int timeout; /* 현재 타임아웃 (초) */
unsigned int pretimeout; /* pretimeout 값 (초, 0이면 비활성) */
unsigned int min_timeout; /* 하드웨어 허용 최소 타임아웃 */
unsigned int max_timeout; /* 하드웨어 허용 최대 타임아웃 */
unsigned int min_hw_heartbeat_ms; /* HW ping 최소 간격(ms) */
unsigned int max_hw_heartbeat_ms; /* HW 최대 허트비트(ms) */
struct notifier_block reboot_nb; /* 리부트 notifier */
struct notifier_block restart_nb; /* restart handler */
struct notifier_block pm_nb; /* PM notifier */
void *driver_data; /* 드라이버 private 데이터 */
struct watchdog_core_data *wd_data; /* 코어 내부 데이터 (커널 전용) */
unsigned long status; /* WDOG_* 상태 비트 */
/* status 비트 플래그 */
#define WDOG_ACTIVE 0 /* watchdog 동작 중 */
#define WDOG_NO_WAY_OUT 1 /* nowayout: close해도 멈추지 않음 */
#define WDOG_STOP_ON_REBOOT 2 /* 리부트 시 자동 정지 */
#define WDOG_HW_RUNNING 3 /* HW watchdog이 자체 동작 중 */
#define WDOG_STOP_ON_UNREGISTER 4 /* unregister 시 정지 */
#define WDOG_NO_PING_ON_SUSPEND 5 /* suspend 중 ping 금지 */
};
/* Watchdog 하드웨어 제어 콜백 */
struct watchdog_ops {
struct module *owner;
int (*start)(struct watchdog_device *); /* HW 타이머 시작 (필수) */
int (*stop)(struct watchdog_device *); /* HW 타이머 정지 */
int (*ping)(struct watchdog_device *); /* 카운터 리셋 (kick) */
unsigned int (*status)(struct watchdog_device *); /* 상태 읽기 */
int (*set_timeout)(struct watchdog_device *, unsigned int); /* 타임아웃 변경 */
int (*set_pretimeout)(struct watchdog_device *, unsigned int); /* pretimeout 설정 */
unsigned int (*get_timeleft)(struct watchdog_device *); /* 남은 시간(초) */
int (*restart)(struct watchdog_device *, unsigned long, void *); /* 시스템 재시작 */
long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long); /* 커스텀 ioctl */
};
/* 유저 공간 노출 정보 — WDIOC_GETSUPPORT로 조회 */
struct watchdog_info {
__u32 options; /* WDIOF_* 기능 플래그 */
__u32 firmware_version; /* 펌웨어 버전 */
__u8 identity[32]; /* 드라이버 식별 문자열 */
};
| WDIOF_* 플래그 | 값 | 의미 |
|---|---|---|
WDIOF_OVERHEAT | 0x0001 | 과열로 인한 리셋 감지 |
WDIOF_FANFAULT | 0x0002 | 팬 장애 감지 |
WDIOF_EXTERN1/2 | 0x0004/0x0008 | 외부 릴레이 1/2 |
WDIOF_POWERUNDER | 0x0010 | 전압 저하 감지 |
WDIOF_CARDRESET | 0x0020 | 마지막 리부트가 watchdog에 의한 것 |
WDIOF_POWEROVER | 0x0040 | 전압 초과 감지 |
WDIOF_SETTIMEOUT | 0x0080 | 타임아웃 런타임 설정 가능 |
WDIOF_MAGICCLOSE | 0x0100 | Magic Close 기능 지원 |
WDIOF_PRETIMEOUT | 0x0200 | pretimeout 기능 지원 |
WDIOF_ALARMONLY | 0x0400 | 알람만 (리셋 없음) |
WDIOF_KEEPALIVEPING | 0x8000 | Keep Alive ping 지원 |
Watchdog ioctl 인터페이스
사용자 공간에서 /dev/watchdog (또는 /dev/watchdogN)을 통해 watchdog을 제어합니다. 디바이스를 open()하면 watchdog이 시작되고, 파일에 아무 데이터든 write()하면 kick이 됩니다.
| ioctl 명령 | 방향 | 인자 타입 | 설명 |
|---|---|---|---|
WDIOC_GETSUPPORT | R | watchdog_info | 드라이버 정보 및 지원 기능 조회 |
WDIOC_GETSTATUS | R | int | 현재 상태 플래그 조회 |
WDIOC_GETBOOTSTATUS | R | int | 부팅 시 watchdog 리셋 여부 |
WDIOC_GETTEMP | R | int | 온도 읽기 (지원 시) |
WDIOC_SETOPTIONS | W | int | WDIOS_DISABLECARD / WDIOS_ENABLECARD |
WDIOC_KEEPALIVE | - | - | watchdog ping (kick) |
WDIOC_SETTIMEOUT | RW | int | 타임아웃 설정 (초), 실제 적용 값 반환 |
WDIOC_GETTIMEOUT | R | int | 현재 타임아웃 조회 |
WDIOC_SETPRETIMEOUT | RW | int | pretimeout 설정 (초) |
WDIOC_GETPRETIMEOUT | R | int | 현재 pretimeout 조회 |
WDIOC_GETTIMELEFT | R | int | 만료까지 남은 시간 (초) |
/* 사용자 공간에서 watchdog 제어 예제 */
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/watchdog.h>
int main(void)
{
struct watchdog_info ident;
int fd, timeout, timeleft;
fd = open("/dev/watchdog", O_WRONLY);
if (fd < 0) {
perror("open /dev/watchdog");
return 1;
}
/* 드라이버 정보 조회 */
ioctl(fd, WDIOC_GETSUPPORT, &ident);
printf("Watchdog: %s, options=0x%x\\n",
ident.identity, ident.options);
/* 타임아웃을 30초로 설정 */
timeout = 30;
ioctl(fd, WDIOC_SETTIMEOUT, &timeout);
printf("Timeout set to %d seconds\\n", timeout);
/* 주기적으로 kick */
for (;;) {
ioctl(fd, WDIOC_KEEPALIVE, 0);
ioctl(fd, WDIOC_GETTIMELEFT, &timeleft);
printf("Time left: %d sec\\n", timeleft);
sleep(10);
}
/* Magic Close로 안전하게 중지 */
write(fd, "V", 1);
close(fd);
return 0;
}
Watchdog 드라이버 구현
최소한의 SoC watchdog 드라이버 구현 예제입니다. devm_watchdog_register_device()를 사용하면 자동으로 리소스가 해제됩니다.
/* drivers/watchdog/example_wdt.c — SoC Watchdog 드라이버 예제 */
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/watchdog.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/of.h>
/* 하드웨어 레지스터 오프셋 */
#define WDT_CR 0x00 /* Control Register */
#define WDT_TORR 0x04 /* Timeout Range Register */
#define WDT_CRR 0x0C /* Counter Restart (kick) */
#define WDT_STAT 0x10 /* Interrupt Status */
#define WDT_EOI 0x14 /* Interrupt Clear */
#define WDT_CR_EN BIT(0) /* Watchdog Enable */
#define WDT_CR_RMOD BIT(1) /* Response Mode: 0=reset, 1=irq+reset */
#define WDT_CRR_KICK 0x76 /* Magic kick value */
#define WDT_MIN_TIMEOUT 1
#define WDT_MAX_TIMEOUT 256
#define WDT_DEFAULT_TIMEOUT 30
static bool nowayout = WATCHDOG_NOWAYOUT;
module_param(nowayout, bool, 0444);
MODULE_PARM_DESC(nowayout, "Disable watchdog shutdown on close");
struct example_wdt {
struct watchdog_device wdd;
void __iomem *base;
struct clk *clk;
unsigned long rate;
};
static inline struct example_wdt *to_example_wdt(struct watchdog_device *wdd)
{
return container_of(wdd, struct example_wdt, wdd);
}
static int example_wdt_start(struct watchdog_device *wdd)
{
struct example_wdt *wdt = to_example_wdt(wdd);
u32 val;
val = readl(wdt->base + WDT_CR);
val |= WDT_CR_EN;
writel(val, wdt->base + WDT_CR);
return 0;
}
static int example_wdt_stop(struct watchdog_device *wdd)
{
struct example_wdt *wdt = to_example_wdt(wdd);
u32 val;
val = readl(wdt->base + WDT_CR);
val &= ~WDT_CR_EN;
writel(val, wdt->base + WDT_CR);
return 0;
}
static int example_wdt_ping(struct watchdog_device *wdd)
{
struct example_wdt *wdt = to_example_wdt(wdd);
writel(WDT_CRR_KICK, wdt->base + WDT_CRR);
return 0;
}
static int example_wdt_set_timeout(struct watchdog_device *wdd,
unsigned int timeout)
{
struct example_wdt *wdt = to_example_wdt(wdd);
u32 count;
/* 클럭 주파수 기반으로 카운트 값 계산 */
count = timeout * wdt->rate;
writel(count, wdt->base + WDT_TORR);
wdd->timeout = timeout;
return 0;
}
static unsigned int example_wdt_get_timeleft(struct watchdog_device *wdd)
{
struct example_wdt *wdt = to_example_wdt(wdd);
u32 count;
count = readl(wdt->base + WDT_TORR);
return count / wdt->rate;
}
static const struct watchdog_info example_wdt_info = {
.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING |
WDIOF_MAGICCLOSE,
.identity = "Example SoC Watchdog",
};
static const struct watchdog_ops example_wdt_ops = {
.owner = THIS_MODULE,
.start = example_wdt_start,
.stop = example_wdt_stop,
.ping = example_wdt_ping,
.set_timeout = example_wdt_set_timeout,
.get_timeleft = example_wdt_get_timeleft,
};
static int example_wdt_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct example_wdt *wdt;
int ret;
wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
if (!wdt)
return -ENOMEM;
/* MMIO 레지스터 매핑 */
wdt->base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(wdt->base))
return PTR_ERR(wdt->base);
/* 클럭 획득 및 활성화 */
wdt->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(wdt->clk))
return PTR_ERR(wdt->clk);
wdt->rate = clk_get_rate(wdt->clk);
if (!wdt->rate)
return -EINVAL;
/* watchdog_device 초기화 */
wdt->wdd.info = &example_wdt_info;
wdt->wdd.ops = &example_wdt_ops;
wdt->wdd.min_timeout = WDT_MIN_TIMEOUT;
wdt->wdd.max_timeout = WDT_MAX_TIMEOUT;
wdt->wdd.timeout = WDT_DEFAULT_TIMEOUT;
wdt->wdd.parent = dev;
/* nowayout 설정 */
watchdog_set_nowayout(&wdt->wdd, nowayout);
/* 부팅 시 이미 동작 중인 HW watchdog 처리 */
if (readl(wdt->base + WDT_CR) & WDT_CR_EN)
set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
/* devm 기반 등록 — remove 시 자동 해제 */
ret = devm_watchdog_register_device(dev, &wdt->wdd);
if (ret)
return ret;
platform_set_drvdata(pdev, wdt);
dev_info(dev, "watchdog registered (timeout=%ds, nowayout=%d)\\n",
wdt->wdd.timeout, nowayout);
return 0;
}
static const struct of_device_id example_wdt_of_match[] = {
{ .compatible = "vendor,example-wdt" },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, example_wdt_of_match);
static struct platform_driver example_wdt_driver = {
.probe = example_wdt_probe,
.driver = {
.name = "example-wdt",
.of_match_table = example_wdt_of_match,
},
};
module_platform_driver(example_wdt_driver);
MODULE_AUTHOR("Kernel Developer");
MODULE_DESCRIPTION("Example SoC Watchdog Driver");
MODULE_LICENSE("GPL");
Device Tree 바인딩 예제:
/* arch/arm64/boot/dts/vendor/example.dtsi */
watchdog@40010000 {
compatible = "vendor,example-wdt";
reg = <0x40010000 0x1000>;
clocks = <&clk_wdt>;
timeout-sec = <30>;
};
platform_driver.remove()에서 별도로 watchdog_unregister_device()를 호출할 필요가 없습니다. 디바이스 모델의 devres 메커니즘이 자동으로 해제합니다.
Magic Close와 Nowayout
Watchdog은 한 번 활성화되면 주기적으로 kick하지 않으면 시스템을 리셋합니다. Magic Close는 파일을 닫기 전에 'V' 문자를 write하면 watchdog을 안전하게 중지할 수 있는 메커니즘입니다. Nowayout이 설정되면 어떤 방법으로도 watchdog을 중지할 수 없습니다.
/* drivers/watchdog/watchdog_dev.c — Magic Close 처리 */
static ssize_t watchdog_write(struct file *file,
const char __user *data, size_t len, loff_t *ppos)
{
struct watchdog_core_data *wd_data = file->private_data;
size_t i;
char c;
if (len == 0)
return 0;
/* 'V' 문자가 있는지 스캔 */
wd_data->expect_close = 0;
for (i = 0; i < len; i++) {
if (get_user(c, data + i))
return -EFAULT;
if (c == 'V')
wd_data->expect_close = 42; /* magic value */
}
/* watchdog kick 수행 */
watchdog_ping(wd_data->wdd);
return len;
}
static int watchdog_release(struct inode *inode, struct file *file)
{
struct watchdog_core_data *wd_data = file->private_data;
struct watchdog_device *wdd = wd_data->wdd;
if (!test_bit(WDOG_NO_WAY_OUT, &wdd->status) &&
wd_data->expect_close == 42) {
/* Magic Close: watchdog 중지 허용 */
watchdog_stop(wdd);
} else {
/* 강제 유지: 커널 워커가 대신 ping 계속 */
pr_crit("watchdog%d: not stopping!\\n", wdd->id);
watchdog_ping(wdd);
}
return 0;
}
| 조건 | nowayout=0 | nowayout=1 |
|---|---|---|
close() 전에 'V' write | watchdog 중지 | watchdog 계속 동작 |
close() 전에 'V' 없음 | 커널 워커가 ping 유지 | 커널 워커가 ping 유지 |
| 유저 프로세스 crash | 파일 자동 close → ping 유지 | 파일 자동 close → ping 유지 |
WDIOC_SETOPTIONS(DISABLECARD) | watchdog 중지 | EBUSY 에러 |
=y이면 모든 watchdog 드라이버의 기본 nowayout이 1이 됩니다. 프로덕션 임베디드 시스템에서는 강력히 권장되는 설정입니다. 개별 드라이버의 nowayout 모듈 파라미터로 오버라이드할 수 있습니다.
Pretimeout과 Governor
Pretimeout은 watchdog이 실제로 시스템을 리셋하기 전에 미리 알림(인터럽트/NMI)을 발생시키는 기능입니다. 이 시점에서 로그를 남기거나 패닉을 유발하여 크래시 덤프를 확보할 수 있습니다.
/* pretimeout 콜백 구현 예제 */
static irqreturn_t example_wdt_pretimeout_irq(int irq, void *data)
{
struct watchdog_device *wdd = data;
/* watchdog core에 pretimeout 이벤트 전달
* → 등록된 governor가 처리 */
watchdog_notify_pretimeout(wdd);
return IRQ_HANDLED;
}
/* probe()에서 pretimeout 설정 */
static int example_wdt_probe(struct platform_device *pdev)
{
/* ... 기존 초기화 ... */
int irq;
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
ret = devm_request_irq(dev, irq,
example_wdt_pretimeout_irq, 0,
"example-wdt-pretimeout", &wdt->wdd);
if (!ret) {
/* pretimeout 지원 활성화 */
wdt->wdd.info->options |= WDIOF_PRETIMEOUT;
/* Response Mode를 IRQ+Reset으로 설정 */
val = readl(wdt->base + WDT_CR);
val |= WDT_CR_RMOD;
writel(val, wdt->base + WDT_CR);
}
}
/* ... 등록 ... */
}
| Pretimeout Governor | 모듈 | 동작 |
|---|---|---|
| noop | pretimeout_noop.ko | pr_alert()로 로그만 출력 |
| panic | pretimeout_panic.ko | panic() 호출 → kdump로 크래시 덤프 확보 |
# Governor 설정 방법
# 현재 governor 확인
cat /sys/class/watchdog/watchdog0/pretimeout_governor
# 사용 가능한 governor 목록
cat /sys/class/watchdog/watchdog0/pretimeout_available_governors
# governor 변경
echo panic > /sys/class/watchdog/watchdog0/pretimeout_governor
# pretimeout 설정 (예: 10초 전 알림)
# timeout=30, pretimeout=10 → 20초 후 IRQ, 30초 후 리셋
panic으로 설정하고 kdump를 구성하면, watchdog 만료 직전에 자동으로 크래시 덤프를 생성할 수 있습니다. 이는 hang 상태 디버깅에 매우 유용합니다.
사용자 공간 활용
리눅스에서 watchdog을 활용하는 대표적인 방법은 전용 데몬(watchdog)을 사용하거나, systemd의 내장 watchdog 기능을 이용하는 것입니다.
# /etc/watchdog.conf — watchdog 데몬 설정
watchdog-device = /dev/watchdog
watchdog-timeout = 30
interval = 10
# 시스템 모니터링 옵션
max-load-1 = 24 # 1분 로드 평균 임계값
max-load-5 = 18 # 5분 로드 평균 임계값
min-memory = 1 # 최소 가용 메모리 (페이지)
max-temperature = 90 # 최대 온도 (°C, HWMON)
# 파일 존재 확인 (NFS 마운트 등)
file = /var/run/important_service.pid
# 핑 테스트 (네트워크 연결 확인)
ping = 192.168.1.1
ping = 10.0.0.1
# 인터페이스 확인
interface = eth0
# 사용자 정의 테스트 스크립트
test-binary = /usr/local/bin/health_check.sh
test-timeout = 10
# systemd watchdog 통합
# /etc/systemd/system.conf — 하드웨어 watchdog
[Manager]
RuntimeWatchdogSec=30 # HW watchdog 타임아웃 (PID 1이 /dev/watchdog ping)
RebootWatchdogSec=10min # 재부팅 중 watchdog 타임아웃
KExecWatchdogSec=0 # kexec 중 watchdog (0=비활성)
WatchdogDevice=/dev/watchdog0 # 사용할 watchdog 디바이스
# 서비스 단위 watchdog — /etc/systemd/system/myapp.service
[Service]
Type=notify
WatchdogSec=60 # 60초마다 sd_notify 기대
WatchdogSignal=SIGABRT # 실패 시 보낼 시그널
Restart=on-watchdog # watchdog 실패 시 재시작
ExecStart=/usr/bin/myapp
/* 애플리케이션에서 systemd watchdog 통합 */
#include <systemd/sd-daemon.h>
int main(void)
{
uint64_t usec;
int wd_enabled;
/* systemd watchdog 활성화 여부 및 간격 확인 */
wd_enabled = sd_watchdog_enabled(0, &usec);
if (wd_enabled > 0) {
printf("Watchdog enabled, interval=%llu us\\n", usec);
}
/* 서비스 준비 완료 알림 */
sd_notify(0, "READY=1");
for (;;) {
/* 애플리케이션 메인 작업 수행 */
do_work();
/* 정상 동작 중이면 watchdog ping */
if (health_check_ok())
sd_notify(0, "WATCHDOG=1");
/* WatchdogSec / 2 간격으로 호출 권장 */
usleep(usec / 2);
}
sd_notify(0, "STOPPING=1");
return 0;
}
| 방법 | 장점 | 단점 | 적합한 환경 |
|---|---|---|---|
| watchdog 데몬 | 시스템 전체 모니터링 (CPU, 메모리, 온도, 네트워크) | 별도 패키지 설치 필요, 세밀한 앱 모니터링 어려움 | 임베디드 시스템, 서버 |
| systemd HW watchdog | PID 1이 직접 관리, 추가 설치 불필요 | PID 1 hang 시에만 동작, 서비스 수준 모니터링 별도 필요 | systemd 기반 범용 시스템 |
| systemd 서비스 watchdog | 서비스별 세밀한 liveness 체크 | 애플리케이션 코드 수정 필요 (sd_notify) |
미션 크리티컬 서비스 |
| 직접 /dev/watchdog | 완전한 제어, 의존성 없음 | 데몬 구현 필요, 실수 시 시스템 리셋 위험 | 커스텀 임베디드 |
Watchdog 디버깅
Watchdog 관련 문제 진단은 sysfs 인터페이스, dmesg 로그, 하드웨어 상태 레지스터를 통해 수행합니다.
# sysfs 인터페이스 — /sys/class/watchdog/watchdog0/
cat /sys/class/watchdog/watchdog0/identity # 드라이버 이름
cat /sys/class/watchdog/watchdog0/timeout # 현재 타임아웃 (초)
cat /sys/class/watchdog/watchdog0/timeleft # 만료까지 남은 시간
cat /sys/class/watchdog/watchdog0/bootstatus # 부팅 시 리셋 원인
cat /sys/class/watchdog/watchdog0/status # 현재 상태 (active 등)
cat /sys/class/watchdog/watchdog0/nowayout # nowayout 설정 여부
cat /sys/class/watchdog/watchdog0/state # active / inactive
cat /sys/class/watchdog/watchdog0/pretimeout # pretimeout 값
# dmesg에서 watchdog 관련 로그 확인
dmesg | grep -i watchdog
dmesg | grep -i "iTCO_wdt\|sp5100_tco\|softdog"
# 로드된 watchdog 모듈 확인
lsmod | grep wdt
lsmod | grep watchdog
# watchdog 디바이스 목록
ls -la /dev/watchdog*
| 드라이버 | 플랫폼 | 소스 위치 | 비고 |
|---|---|---|---|
iTCO_wdt | Intel 칩셋 (ICH/PCH) | drivers/watchdog/iTCO_wdt.c | 가장 보편적인 서버/데스크톱 WDT |
sp5100_tco | AMD 칩셋 | drivers/watchdog/sp5100_tco.c | AMD 서버/데스크톱 WDT |
imx2_wdt | NXP i.MX SoC | drivers/watchdog/imx2_wdt.c | 임베디드 ARM SoC 대표 |
bcm2835_wdt | Broadcom (Raspberry Pi) | drivers/watchdog/bcm2835_wdt.c | RPi 보드 WDT |
softdog | 소프트웨어 (범용) | drivers/watchdog/softdog.c | HW 없이 테스트용, hrtimer 기반 |
mei_wdt | Intel ME | drivers/watchdog/mei_wdt.c | Intel Management Engine WDT |
omap_wdt | TI OMAP/AM335x | drivers/watchdog/omap_wdt.c | BeagleBone 등 TI SoC |
| 문제 | 증상 | 원인 | 해결 방법 |
|---|---|---|---|
| 부팅 후 즉시 리셋 반복 | 부팅 루프, 콘솔에 watchdog timeout 로그 | 부트로더가 WDT 활성화 후 커널이 제때 ping 못함 | 부트로더에서 WDT 비활성화하거나 WDOG_HW_RUNNING 설정 확인 |
| watchdog close 후에도 리셋 | 데몬 종료 수초 후 시스템 리셋 | nowayout=1 또는 Magic Close 미수행 |
nowayout 파라미터 확인, close 전 'V' write |
| /dev/watchdog 열기 실패 | EBUSY 또는 ENODEV |
다른 프로세스가 이미 점유, 또는 드라이버 미로드 | fuser /dev/watchdog으로 확인, 모듈 로드 확인 |
| 타임아웃 설정이 원하는 값과 다름 | WDIOC_SETTIMEOUT 반환값이 요청값과 불일치 |
HW가 특정 단계만 지원 (예: 1,2,4,8,...초) | 반환된 실제 값 사용, 드라이버 소스에서 지원 범위 확인 |
| suspend/resume 후 watchdog 동작 안함 | resume 후 ping 무응답 | PM 콜백에서 HW 재초기화 누락 | 드라이버의 suspend/resume 콜백 구현 확인 |
Watchdog Core 내부 동작
커널 watchdog 프레임워크(drivers/watchdog/watchdog_core.c, watchdog_dev.c)는 char device, 내부 타이머, 동기화 메커니즘을 통해 사용자 공간과 하드웨어 사이를 중개합니다.
watchdog_core.c 핵심 흐름
/* drivers/watchdog/watchdog_core.c — 등록 시 내부 초기화 */
int watchdog_register_device(struct watchdog_device *wdd)
{
int ret;
/* ① 유효성 검사 */
ret = watchdog_check_min_max_timeout(wdd);
if (ret)
return ret;
/* ② timeout 범위 클램핑 */
if (wdd->timeout < wdd->min_timeout)
wdd->timeout = wdd->min_timeout;
if (wdd->max_timeout && wdd->timeout > wdd->max_timeout)
wdd->timeout = wdd->max_timeout;
/* ③ watchdog_core_data 할당 및 hrtimer 초기화 */
ret = watchdog_dev_register(wdd);
if (ret)
return ret;
/* ④ 이미 HW가 동작 중이면 즉시 keep_alive 시작 */
if (test_bit(WDOG_HW_RUNNING, &wdd->status))
watchdog_ping(wdd); /* hrtimer 시동 */
/* ⑤ restart handler 등록 */
if (wdd->ops->restart) {
wdd->restart_nb.notifier_call = watchdog_restart_notifier;
register_restart_handler(&wdd->restart_nb);
}
return 0;
}
/* hrtimer 콜백 — 주기적 ping 유지 */
static enum hrtimer_restart watchdog_timer_expired(struct hrtimer *timer)
{
struct watchdog_core_data *wd_data;
wd_data = container_of(timer, struct watchdog_core_data, timer);
kthread_queue_work(wd_data->kworker, &wd_data->ping_work);
return HRTIMER_NORESTART;
}
/* ping_work 워커 — 실제 HW ping 수행 */
static void watchdog_ping_work(struct kthread_work *work)
{
struct watchdog_core_data *wd_data;
struct watchdog_device *wdd;
wd_data = container_of(work, struct watchdog_core_data, ping_work);
wdd = wd_data->wdd;
if (!wdd)
return;
mutex_lock(&wd_data->lock);
if (watchdog_worker_should_ping(wd_data))
watchdog_ping(wdd);
mutex_unlock(&wd_data->lock);
}
| 내부 함수 | 소스 위치 | 역할 |
|---|---|---|
watchdog_dev_register() | watchdog_dev.c | char device 생성, wd_data 할당, hrtimer 초기화 |
watchdog_ping() | watchdog_dev.c | ops→ping() 호출, 없으면 stop+start 시퀀스 |
watchdog_timer_expired() | watchdog_dev.c | hrtimer 콜백 → kthread 워크 큐에 ping_work 제출 |
watchdog_ping_work() | watchdog_dev.c | 실제 HW ping 수행 (mutex 보호) |
watchdog_start_hrtimer() | watchdog_dev.c | timeout/2 주기로 hrtimer 설정 |
watchdog_worker_should_ping() | watchdog_dev.c | ACTIVE 상태 & 유저 open 여부로 ping 필요성 판단 |
watchdog_restart_notifier() | watchdog_core.c | reboot notifier로 ops→restart() 호출 |
Char Device 등록 메커니즘
watchdog 프레임워크는 두 가지 char device 경로를 제공합니다. id=0인 첫 번째 watchdog은 /dev/watchdog(misc device, major 10)으로도 접근할 수 있고, 모든 watchdog은 /dev/watchdogN(major 248)으로 접근합니다.
/* watchdog char device 등록 순서 */
/* 1. misc device (레거시 호환 — /dev/watchdog) */
static struct miscdevice watchdog_miscdev = {
.minor = WATCHDOG_MINOR, /* 130 */
.name = "watchdog",
.fops = &watchdog_fops,
};
/* 2. 새로운 방식 — /dev/watchdog0, /dev/watchdog1, ... */
/* watchdog_dev_register()에서 cdev_add()로 등록 */
static dev_t watchdog_devt; /* alloc_chrdev_region()으로 동적 할당 */
/* 파일 오퍼레이션 */
static const struct file_operations watchdog_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.write = watchdog_write, /* ping + 'V' 감지 */
.unlocked_ioctl = watchdog_ioctl,
.compat_ioctl = compat_ptr_ioctl,
.open = watchdog_open, /* watchdog 시작 */
.release = watchdog_release, /* Magic Close 처리 */
};
iTCO_wdt 심층 분석
iTCO_wdt(drivers/watchdog/iTCO_wdt.c)는 Intel 칩셋의 TCO(Total Cost of Ownership) 타이머를 사용하는 가장 보편적인 x86 watchdog 드라이버입니다. ICH(I/O Controller Hub) 또는 PCH(Platform Controller Hub)에 내장되어 있으며, 다단계 리셋 메커니즘을 제공합니다.
/* drivers/watchdog/iTCO_wdt.c — 핵심 레지스터 접근 */
/* ping (카운터 리로드) */
static int iTCO_wdt_ping(struct watchdog_device *wd_dev)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
spin_lock(&p->io_lock);
iTCO_vendor_pre_keepalive(p->smi_res, wd_dev->timeout);
/* TCO Timer Reload: 특정 값 0x01을 RLD에 쓰면 카운터 리셋 */
outw(0x01, TCO_RLD(p));
spin_unlock(&p->io_lock);
return 0;
}
/* 타임아웃 설정 */
static int iTCO_wdt_set_timeout(struct watchdog_device *wd_dev,
unsigned int t)
{
struct iTCO_wdt_private *p = watchdog_get_drvdata(wd_dev);
unsigned int val;
unsigned long tmrval;
/* 틱 단위로 변환: 1틱 = 0.6초 (TCO v1) 또는 1초 (TCO v2+) */
tmrval = seconds_to_ticks(p, t);
spin_lock(&p->io_lock);
if (p->iTCO_version == 3) {
outl(tmrval, TCO_TMR(p));
val = inl(TCO_TMR(p));
} else if (p->iTCO_version >= 2) {
outw(tmrval, TCO_TMR(p));
val = inw(TCO_TMR(p));
} else {
outb(tmrval, TCO_TMR(p));
val = inb(TCO_TMR(p));
}
spin_unlock(&p->io_lock);
wd_dev->timeout = ticks_to_seconds(p, val);
return 0;
}
| TCO 버전 | 칩셋 세대 | 틱 단위 | 최대 타임아웃 | 비고 |
|---|---|---|---|---|
| TCO v1 | ICH0~ICH5 | 0.6초 | 약 39초 | 구형 8비트 레지스터 |
| TCO v2 | ICH6~ICH9 | 1.0초 | 614초 | 16비트 레지스터 |
| TCO v3 | ICH10~PCH | 1.0초 | 614초 | 32비트, PMBASE 방식 변경 |
# iTCO_wdt 동작 확인
# 드라이버 로드 확인
lsmod | grep iTCO
modinfo iTCO_wdt
# TCO 레지스터 직접 읽기 (루트 필요)
# ACPIBASE는 보통 0x400 또는 0x500에 위치 (lspci로 확인)
lspci -v | grep -i "SMBus\|LPC"
# dmesg에서 iTCO 초기화 로그 확인
dmesg | grep -i "iTCO\|TCO"
# 예: [ 2.384] iTCO_wdt: Intel TCO WatchDog Timer Driver v1.11
# 예: [ 2.385] iTCO_wdt: Found a Intel PCH TCO device (Version=3, TCOBASE=0x0460)
# bootstatus: 이전 리셋이 TCO watchdog에 의한 것인지 확인
cat /sys/class/watchdog/watchdog0/bootstatus
# 0: 정상 부팅 ≠0: WDT 리셋
# 가능하면 SMI 비활성화 (테스트 전용 — 프로덕션 사용 금지)
# iTCO 드라이버는 부팅 시 SMI_TCO_EN을 비활성화하여 SMI→직접 리셋으로 동작
GBL_SMI_EN=1) 커널 드라이버가 제어하지 못하게 합니다. 이 경우 iTCO_wdt는 로드는 되지만 /dev/watchdog이 생성되지 않을 수 있습니다. CONFIG_ITCO_WDT_LIST_NOGPIO로 우회 가능합니다.
softdog 내부 구현
softdog(drivers/watchdog/softdog.c)은 별도 하드웨어 없이 커널 hrtimer만으로 동작하는 소프트웨어 watchdog입니다. 하드웨어 WDT가 없는 가상 머신이나 개발 환경에서 주로 사용합니다.
/* drivers/watchdog/softdog.c — 핵심 구현 */
static struct hrtimer softdog_fire_timer;
static unsigned long softdog_fire_time; /* 만료 절대 시간 (jiffies) */
static int softdog_ping(struct watchdog_device *w)
{
if (!hrtimer_active(&softdog_fire_timer))
return 0;
/* hrtimer를 현재 시각 + timeout으로 재설정 */
hrtimer_forward_now(&softdog_fire_timer,
ktime_set(w->timeout, 0));
return 0;
}
static int softdog_start(struct watchdog_device *w)
{
hrtimer_start(&softdog_fire_timer,
ktime_set(w->timeout, 0),
HRTIMER_MODE_REL);
return 0;
}
static int softdog_stop(struct watchdog_device *w)
{
hrtimer_cancel(&softdog_fire_timer);
return 0;
}
/* hrtimer 콜백 — 시스템 리셋 또는 패닉 */
static enum hrtimer_restart softdog_fire(struct hrtimer *timer)
{
if (softdog_panic) {
panic("Software Watchdog Timeout - %s\\n", current->comm);
} else {
pr_crit("Initiating system reboot\\n");
emergency_restart();
}
return HRTIMER_NORESTART;
}
static int __init watchdog_init(void)
{
hrtimer_init(&softdog_fire_timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL);
softdog_fire_timer.function = softdog_fire;
return platform_driver_register(&softdog_driver);
}
| 파라미터 | 기본값 | 설명 |
|---|---|---|
soft_margin | 60초 | watchdog 타임아웃 (1~65535초) |
nowayout | CONFIG 기본값 | Magic Close 무시 여부 |
soft_panic | 0 | 만료 시 panic (1) 또는 emergency_restart (0) |
# softdog 사용법
# 모듈 로드 (기본 60초 타임아웃)
modprobe softdog
# panic 모드로 로드 (만료 시 kdump 트리거)
modprobe softdog soft_panic=1
# 타임아웃 120초, nowayout 활성화
modprobe softdog soft_margin=120 nowayout=1
# 확인
cat /sys/class/watchdog/watchdog0/identity
# Software Watchdog
Lockup Detector와 Watchdog
리눅스 커널의 Lockup Detector(kernel/watchdog.c)는 watchdog 드라이버 프레임워크(drivers/watchdog/)와 이름은 같지만 별도 메커니즘입니다. CPU가 커널 공간에서 너무 오래 선점 불가 상태로 있으면(softlockup) 또는 인터럽트조차 처리 못 하면(hardlockup) 감지합니다.
/* kernel/watchdog.c — Softlockup 감지 로직 */
/* hrtimer 콜백: 각 CPU마다 주기적으로 타임스탬프 업데이트 */
static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
{
unsigned long touch_ts = watchdog_touch_ts();
int duration;
/* watchdog kthread가 최근에 실행되었는지 확인 */
if (period_ts(touch_ts)) {
/* SCHED_FIFO kthread가 실행되지 않음 → softlockup 후보 */
duration = is_softlockup(touch_ts);
if (duration) {
pr_emerg("BUG: soft lockup - CPU#%d stuck for %us!\\n",
smp_processor_id(), duration);
print_modules();
dump_stack();
if (softlockup_panic)
panic("softlockup: hung tasks");
}
}
/* hrtimer 재시작: 4초 주기 */
hrtimer_forward_now(hrtimer, ns_to_ktime(sample_period));
return HRTIMER_RESTART;
}
/* Hardlockup: NMI 핸들러 (arch/x86/kernel/nmi.c) */
static int hardlockup_detector_nmi_handler(unsigned int type,
struct pt_regs *regs)
{
if (is_hardlockup()) {
pr_emerg("Watchdog detected hard LOCKUP on cpu %d\\n",
this_cpu());
panic("Hard LOCKUP");
}
return NMI_DONE;
}
# Lockup Detector 제어
# 임계 시간 변경 (기본 10초)
echo 20 > /proc/sys/kernel/watchdog_thresh
# softlockup 감지 시 panic 활성화
echo 1 > /proc/sys/kernel/softlockup_panic
# hardlockup 감지 시 panic (기본: 1)
echo 1 > /proc/sys/kernel/hardlockup_panic
# watchdog 완전 비활성화 (디버깅 중 유용)
echo 0 > /proc/sys/kernel/watchdog
# CPU별 lockup detector 활성/비활성
echo 0 > /proc/sys/kernel/watchdog_cpumask # 모든 CPU 비활성
echo f > /proc/sys/kernel/watchdog_cpumask # CPU 0-3만 활성
# perf NMI 카운터 기반 hardlockup 확인
dmesg | grep "perf event create"
cat /sys/devices/system/cpu/cpu0/perf_event_mux_interval_ms
| 구분 | kernel/watchdog.c | drivers/watchdog/ |
|---|---|---|
| 목적 | CPU lockup 감지 (디버깅) | 시스템 리셋 (신뢰성) |
| 메커니즘 | hrtimer + SCHED_FIFO kthread + perf NMI | HW 타이머 카운트다운 |
| 트리거 | 커널 선점불가 또는 인터럽트 불가 | ping 타임아웃 |
| 결과 | BUG/panic/dump (복구 시도) | 하드웨어 리셋 (강제) |
| CPU hang 시 | hardlockup NMI로 감지 | 독립 HW로 여전히 동작 |
| 설정 | /proc/sys/kernel/watchdog* | /sys/class/watchdog/ |
| 상호작용 | pretimeout panic → kdump 후 HW WDT 리셋 | lockup_detector가 panic 트리거 |
IPMI/BMC Out-of-band Watchdog
IPMI Watchdog(drivers/char/ipmi/ipmi_watchdog.c)는 서버의 BMC(Baseboard Management Controller)에 내장된 watchdog을 제어합니다. OS와 독립된 별도 프로세서가 관리하므로 OS 커널 자체가 hang되어도 시스템을 리셋할 수 있습니다.
# IPMI watchdog 설정 (ipmitool)
# 현재 WDT 설정 확인
ipmitool mc watchdog get
# WDT 설정: 300초, 만료 시 리셋
ipmitool mc watchdog set action=reset interval=300
# 수동 kick
ipmitool mc watchdog reset
# WDT 정지
ipmitool mc watchdog off
# 커널 드라이버 활성화 확인
modprobe ipmi_watchdog
modprobe ipmi_si # IPMI 시스템 인터페이스
# 모듈 파라미터 — 타임아웃 60초, 만료 시 리셋
modprobe ipmi_watchdog timeout=60 action=reset pretimeout=10
/* IPMI Set Watchdog Timer 명령 구조
* IPMI 2.0 Spec Section 27.7 */
struct ipmi_set_wdt_cmd {
u8 timer_use; /* 0x44 = SMS/OS WDT, Don't Stop */
u8 timer_actions; /* 0x01 = timeout → reset */
/* 0x02 = timeout → power down */
/* 0x03 = timeout → power cycle */
u8 pretimeout; /* pretimeout 인터벌 (초) */
u8 timer_use_flags; /* 만료 플래그 클리어 비트 */
u16 initial_countdown; /* 타임아웃 (100ms 단위) */
};
| 특성 | OS HW Watchdog | IPMI/BMC Watchdog |
|---|---|---|
| 독립성 | SoC/칩셋 내장 (OS 의존적) | 완전 독립 BMC 프로세서 |
| OS hang 감지 | OS 커널이 ping 안 하면 감지 | BMC 자체가 판단 (OS 무관) |
| 전원 제어 | 리셋만 가능 | 리셋 / 파워오프 / 파워사이클 |
| 원격 관리 | 불가 | IPMI over LAN으로 원격 제어 |
| 인터페이스 | /dev/watchdog | IPMI KCS/BT/SSIF + /dev/watchdog |
| 주요 용도 | 임베디드, 데스크톱 | 서버, 데이터센터 |
Multi-stage Stacked Watchdog
Stacked watchdog 패턴은 하드웨어 WDT와 소프트웨어 WDT를 계층적으로 쌓아 더 세밀한 감지와 복구를 구현합니다. 커널은 gpio_wdt 드라이버로 GPIO 출력을 통해 외부 watchdog 칩(MAX6370, DS1232 등)을 제어합니다.
/* drivers/watchdog/gpio_wdt.c — GPIO 기반 외부 WDT */
struct gpio_wdt_priv {
struct gpio_desc *gpiod; /* WDI (Watchdog Input) GPIO */
bool state; /* 현재 GPIO 출력 상태 */
enum gpio_wdt_hw_algo hw_algo; /* toggle / level 방식 */
struct watchdog_device wdd;
struct timer_list timer; /* 커널 내부 ping 타이머 */
bool running;
};
static int gpio_wdt_ping(struct watchdog_device *wdd)
{
struct gpio_wdt_priv *priv = watchdog_get_drvdata(wdd);
if (priv->hw_algo == HW_ALGO_TOGGLE) {
/* WDI 핀을 토글하여 외부 WDT 칩에 kick 신호 */
priv->state = !priv->state;
gpiod_set_value_cansleep(priv->gpiod, priv->state);
} else {
/* Level 방식: 짧은 펄스 */
gpiod_set_value_cansleep(priv->gpiod, 0);
udelay(1);
gpiod_set_value_cansleep(priv->gpiod, 1);
}
/* 내부 타이머 재설정 */
mod_timer(&priv->timer,
jiffies + wdd->timeout * HZ / 2);
return 0;
}
/* Device Tree — Multi-stage Watchdog 설정 예 */
/* 외부 MAX6370 WDT 칩 (GPIO 기반) */
watchdog-gpio {
compatible = "linux,wdt-gpio";
gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;
hw_algo = "toggle";
hw_margin_ms = <1000>; /* 외부 WDT 타임아웃: 1초 */
timeout-sec = <60>; /* 소프트웨어 관점 타임아웃 */
always-running; /* probe 후 항상 동작 */
};
/* 내부 SoC WDT */
watchdog@40010000 {
compatible = "vendor,soc-wdt";
reg = <0x40010000 0x1000>;
timeout-sec = <10>; /* 짧은 타임아웃으로 커널 hang 빠르게 감지 */
};
| 계층 | 타임아웃 | 감지 대상 | 액션 |
|---|---|---|---|
| L1: systemd 서비스 WDT | WatchdogSec (예: 30초) | 특정 서비스 응답 없음 | 서비스 재시작 |
| L2: systemd RuntimeWatchdog | RuntimeWatchdogSec (예: 60초) | PID 1 hang (systemd) | 커널 panic → HW 리셋 |
| L3: SoC HW WDT | 짧음 (예: 10초) | 커널 자체 hang | HW 리셋 |
| L4: 외부 WDT 칩 (GPIO) | 매우 짧음 (예: 1초) | GPIO 신호 없음 | 전원 차단/리셋 |
| L5: IPMI/BMC WDT | 긴 폴백 (예: 5분) | 완전 전원 장애 | 파워사이클 + 알림 |
전원 관리 통합
Watchdog과 시스템 전원 관리(PM)는 복잡하게 상호작용합니다. suspend 중에는 ping이 불가능하고, resume 후에는 HW 타이머가 재초기화되어야 합니다.
/* drivers/watchdog/watchdog_dev.c — PM notifier */
static int watchdog_pm_notifier(struct notifier_block *nb,
unsigned long action, void *data)
{
struct watchdog_core_data *wd_data;
struct watchdog_device *wdd;
int ret = 0;
wd_data = container_of(nb, struct watchdog_core_data, pm_nb);
wdd = wd_data->wdd;
switch (action) {
case PM_HIBERNATION_PREPARE:
case PM_SUSPEND_PREPARE:
/* suspend 직전: hrtimer 정지 */
if (!test_bit(WDOG_NO_PING_ON_SUSPEND, &wdd->status)) {
watchdog_stop_hrtimer(wd_data);
watchdog_update_worker(wdd);
}
break;
case PM_POST_HIBERNATION:
case PM_POST_SUSPEND:
/* resume 후: HW 재초기화 후 ping 재개 */
if (watchdog_active(wdd) || watchdog_hw_running(wdd)) {
watchdog_ping(wdd);
}
break;
}
return notifier_from_errno(ret);
}
/* 드라이버가 직접 PM ops를 구현하는 경우 */
static int example_wdt_suspend(struct device *dev)
{
struct example_wdt *wdt = dev_get_drvdata(dev);
if (watchdog_active(&wdt->wdd)) {
/* WDOG_NO_PING_ON_SUSPEND가 없으면 타임아웃을 최대로 늘려
* suspend 동안 리셋이 발생하지 않도록 함 */
example_wdt_set_timeout(&wdt->wdd, wdt->wdd.max_timeout);
example_wdt_ping(&wdt->wdd);
}
return 0;
}
static int example_wdt_resume(struct device *dev)
{
struct example_wdt *wdt = dev_get_drvdata(dev);
if (watchdog_active(&wdt->wdd)) {
/* 원래 타임아웃 복원 후 ping */
example_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
example_wdt_ping(&wdt->wdd);
}
return 0;
}
static const struct dev_pm_ops example_wdt_pm_ops = {
.suspend = example_wdt_suspend,
.resume = example_wdt_resume,
SET_SYSTEM_SLEEP_PM_OPS(example_wdt_suspend, example_wdt_resume)
};
| 플래그 / 설정 | 의미 | 언제 사용 |
|---|---|---|
WDOG_NO_PING_ON_SUSPEND | suspend 중 ping 완전 차단 | HW가 자동 정지되는 SoC |
WDOG_STOP_ON_REBOOT | reboot notifier에서 자동 stop | 리부트 중 리셋 방지 |
WDOG_STOP_ON_UNREGISTER | 모듈 언로드 시 자동 stop | 개발/테스트 드라이버 |
| PM notifier 기반 | 프레임워크가 suspend/resume 처리 | 간단한 드라이버 (권장) |
| 드라이버 pm_ops 직접 구현 | HW 재초기화 등 세밀한 제어 | 복잡한 HW 시퀀스 필요 시 |
WDOG_NO_PING_ON_SUSPEND와 함께 최대 타임아웃 설정이 필수입니다.
관련 문서
이 주제와 관련된 다른 문서를 더 깊이 이해하고 싶다면 다음을 참고하세요.