Linux 看門狗定時器驅動核心核心 API

最後審閱:2013年2月12日

Wim Van Sebroeck <wim@iguana.be>

介紹

本文件不描述看門狗定時器(WDT)驅動程式或裝置是什麼。它也不描述使用者空間可用於與看門狗定時器通訊的 API。如果您想了解這些,請閱讀以下檔案:Linux 看門狗驅動程式 API

那麼本文件描述了什麼?它描述了希望使用看門狗定時器驅動核心框架的看門狗定時器驅動程式可以使用的 API。該框架提供了所有與使用者空間的介面,這樣相同的程式碼就不必每次都重複。這也意味著看門狗定時器驅動程式只需要提供控制看門狗定時器(WDT)的不同例程(操作)。

API

每個希望使用看門狗定時器驅動核心的看門狗定時器驅動程式都必須 #include <linux/watchdog.h>(無論如何,在編寫看門狗裝置驅動程式時都必須這樣做)。此標頭檔案包含以下注冊/登出例程

extern int watchdog_register_device(struct watchdog_device *);
extern void watchdog_unregister_device(struct watchdog_device *);

watchdog_register_device 例程註冊一個看門狗定時器裝置。此例程的引數是指向 watchdog_device 結構的指標。此例程在成功時返回零,在失敗時返回負的 errno 程式碼。

watchdog_unregister_device 例程登出一個已註冊的看門狗定時器裝置。此例程的引數是指向已註冊的 watchdog_device 結構的指標。

看門狗子系統包含一個註冊延遲機制,允許您在引導過程中儘早註冊看門狗。

看門狗裝置結構如下所示

struct watchdog_device {
      int id;
      struct device *parent;
      const struct attribute_group **groups;
      const struct watchdog_info *info;
      const struct watchdog_ops *ops;
      const struct watchdog_governor *gov;
      unsigned int bootstatus;
      unsigned int timeout;
      unsigned int pretimeout;
      unsigned int min_timeout;
      unsigned int max_timeout;
      unsigned int min_hw_heartbeat_ms;
      unsigned int max_hw_heartbeat_ms;
      struct notifier_block reboot_nb;
      struct notifier_block restart_nb;
      void *driver_data;
      struct watchdog_core_data *wd_data;
      unsigned long status;
      struct list_head deferred;
};

它包含以下欄位

  • id: 由 watchdog_register_device 設定,id 0 是特殊的。它既有 /dev/watchdog0 字元裝置(動態主裝置號,次裝置號為 0),也有舊的 /dev/watchdog 雜項裝置。在呼叫 watchdog_register_device 時會自動設定 id

  • parent: 在呼叫 watchdog_register_device 之前,將其設定為父裝置(或 NULL)。

  • groups: 建立看門狗裝置時要建立的 sysfs 屬性組列表。

  • info: 指向 watchdog_info 結構的指標。此結構提供了關於看門狗定時器本身的一些額外資訊。(例如其唯一名稱)

  • ops: 指向看門狗支援的看門狗操作列表的指標。

  • gov: 指向分配的看門狗裝置預超時調節器(governor)的指標,或 NULL。

  • timeout: 看門狗定時器的超時值(以秒為單位)。如果設定了 WDOG_ACTIVE 且使用者空間未傳送心跳請求,系統將在該時間後重啟。

  • pretimeout: 看門狗定時器的預超時值(以秒為單位)。

  • min_timeout: 看門狗定時器的最小超時值(以秒為單位)。如果設定,則為 'timeout' 的最小可配置值。

  • max_timeout: 看門狗定時器的最大超時值(以秒為單位),從使用者空間角度看。如果設定,則為 'timeout' 的最大可配置值。如果 max_hw_heartbeat_ms 不為零,則不使用。

  • min_hw_heartbeat_ms: 心跳之間最短時間的硬體限制,以毫秒為單位。此值通常為 0;僅當硬體不能容忍更低的心跳間隔時才應提供此值。

  • max_hw_heartbeat_ms: 硬體心跳的最大值,以毫秒為單位。如果設定,當 'timeout' 大於 max_hw_heartbeat_ms 時,基礎設施將向看門狗驅動程式傳送心跳,除非 WDOG_ACTIVE 已設定且使用者空間在至少 'timeout' 秒內未能傳送心跳。如果驅動程式未實現停止功能,則必須設定 max_hw_heartbeat_ms

  • reboot_nb: 用於重啟通知的通知塊,僅供內部使用。如果驅動程式呼叫 watchdog_stop_on_reboot,看門狗核心將在收到此類通知時停止看門狗。

  • restart_nb: 用於機器重啟的通知塊,僅供內部使用。如果看門狗能夠重啟機器,它應該定義 ops->restart。優先順序可以透過 watchdog_set_restart_priority 更改。

  • bootstatus: 裝置啟動後的狀態(透過看門狗 WDIOF_* 狀態位報告)。

  • driver_data: 指向看門狗裝置的驅動程式私有資料的指標。此資料只能透過 watchdog_set_drvdatawatchdog_get_drvdata 例程訪問。

  • wd_data: 指向看門狗核心內部資料的指標。

  • status: 此欄位包含一些狀態位,提供有關裝置狀態的額外資訊(例如:看門狗定時器是否正在執行/活躍,或者 nowayout 位是否已設定)。

  • deferred: wtd_deferred_reg_list 中的條目,用於註冊早期初始化的看門狗。

看門狗操作列表定義為

struct watchdog_ops {
      struct module *owner;
      /* mandatory operations */
      int (*start)(struct watchdog_device *);
      /* optional operations */
      int (*stop)(struct watchdog_device *);
      int (*ping)(struct watchdog_device *);
      unsigned int (*status)(struct watchdog_device *);
      int (*set_timeout)(struct watchdog_device *, unsigned int);
      int (*set_pretimeout)(struct watchdog_device *, unsigned int);
      unsigned int (*get_timeleft)(struct watchdog_device *);
      int (*restart)(struct watchdog_device *);
      long (*ioctl)(struct watchdog_device *, unsigned int, unsigned long);
};

首先定義看門狗定時器驅動程式操作的模組所有者非常重要。當看門狗處於活動狀態時,此模組所有者將用於鎖定模組。(這可以避免在解除安裝模組而 /dev/watchdog 仍開啟時發生系統崩潰)。

有些操作是強制性的,有些是可選的。強制性操作包括

  • start: 指向啟動看門狗定時器裝置的例程的指標。該例程需要一個指向看門狗定時器裝置結構的指標作為引數。它在成功時返回零,在失敗時返回負的 errno 程式碼。

並非所有看門狗定時器硬體都支援相同的功能。這就是為什麼所有其他例程/操作都是可選的。它們只有在受支援時才需要提供。這些可選例程/操作包括

  • stop: 透過此例程停止看門狗定時器裝置。

    該例程需要一個指向看門狗定時器裝置結構的指標作為引數。它在成功時返回零,在失敗時返回負的 errno 程式碼。有些看門狗定時器硬體只能啟動而不能停止。支援此類硬體的驅動程式不必實現停止例程。

    如果驅動程式沒有停止功能,看門狗核心將在看門狗裝置關閉後設置 WDOG_HW_RUNNING 並開始呼叫驅動程式的保活(keepalive)ping 功能。

    如果看門狗驅動程式未實現停止功能,則必須設定 max_hw_heartbeat_ms

  • ping: 這是向看門狗定時器硬體傳送保活 ping 的例程。

    該例程需要一個指向看門狗定時器裝置結構的指標作為引數。它在成功時返回零,在失敗時返回負的 errno 程式碼。

    大多數不支援將其作為單獨功能的硬體都使用 start 功能重啟看門狗定時器硬體。這也是看門狗定時器驅動核心所做的:向看門狗定時器硬體傳送保活 ping 時,它將使用 ping 操作(如果可用)或 start 操作(如果 ping 操作不可用)。

    (注意:WDIOC_KEEPALIVE ioctl 呼叫僅在看門狗資訊結構的 option 欄位中設定了 WDIOF_KEEPALIVEPING 位時才有效。)

  • status: 此例程檢檢視門狗定時器裝置的狀態。裝置的狀態透過看門狗 WDIOF_* 狀態標誌/位報告。

    WDIOF_MAGICCLOSEWDIOF_KEEPALIVEPING 由看門狗核心報告;驅動程式無需報告這些位。此外,如果驅動程式未提供狀態函式,看門狗核心將報告 struct watchdog_devicebootstatus 變數中提供的狀態位。

  • set_timeout: 此例程檢查並更改看門狗定時器裝置的超時。成功時返回 0,引數超出範圍時返回 -EINVAL,無法寫入看門狗值時返回 -EIO。成功時,此例程應將 watchdog_device 的超時值設定為實際達到的超時值(可能與請求的值不同,因為看門狗不一定具有 1 秒的解析度)。

    實現 max_hw_heartbeat_ms 的驅動程式將硬體看門狗心跳設定為超時和 max_hw_heartbeat_ms 中的最小值。這些驅動程式將 watchdog_device 的超時值設定為請求的超時值(如果它大於 max_hw_heartbeat_ms),或者設定為實際達到的超時值。(注意:需要在看門狗資訊結構的 options 欄位中設定 WDIOF_SETTIMEOUT。)

    如果看門狗驅動程式除了設定 watchdog_device.timeout 之外不需要執行任何操作,則可以省略此回撥。

    如果未提供 set_timeout 但設定了 WDIOF_SETTIMEOUT,則看門狗基礎設施會在內部將 watchdog_device 的超時值更新為請求的值。

    如果使用預超時功能(WDIOF_PRETIMEOUT),則 set_timeout 還必須負責檢查預超時是否仍然有效並相應地設定定時器。這在核心中無法無競爭地完成,因此是驅動程式的職責。

  • set_pretimeout: 此例程檢查並更改看門狗的預超時值。它是可選的,因為並非所有看門狗都支援預超時通知。超時值不是絕對時間,而是實際超時發生前的時間(以秒為單位)。成功時返回 0,引數超出範圍時返回 -EINVAL,無法寫入看門狗值時返回 -EIO。值為 0 會停用預超時通知。

    (注意:需要在看門狗資訊結構的 options 欄位中設定 WDIOF_PRETIMEOUT。)

    如果看門狗驅動程式除了設定 watchdog_device.pretimeout 之外不需要執行任何操作,則可以省略此回撥。這意味著如果未提供 set_pretimeout 但設定了 WDIOF_PRETIMEOUT,看門狗基礎設施會在內部將 watchdog_device 的預超時值更新為請求的值。

  • get_timeleft: 此例程返回重置前剩餘的時間。

  • restart: 此例程重啟機器。它在成功時返回 0,在失敗時返回負的 errno 程式碼。

  • ioctl: 如果此例程存在,它將在我們進行內部 ioctl 呼叫處理之前首先被呼叫。如果不支援某個命令,此例程應返回 -ENOIOCTLCMD。傳遞給 ioctl 呼叫的引數是:watchdog_devicecmdarg

狀態位應(最好)使用 set_bitclear_bit 等位操作設定。定義的狀態位包括

  • WDOG_ACTIVE: 此狀態位指示看門狗定時器裝置是否從使用者角度來看處於活躍狀態。當此標誌設定時,使用者空間應向驅動程式傳送心跳請求。

  • WDOG_NO_WAY_OUT: 此位儲存看門狗的 nowayout 設定。如果設定了此位,看門狗定時器將無法停止。

  • WDOG_HW_RUNNING: 如果硬體看門狗正在執行,由看門狗驅動程式設定。如果看門狗定時器硬體無法停止,則必須設定此位。如果看門狗定時器在引導後、看門狗裝置開啟之前正在執行,也可以設定此位。如果設定了此位,看門狗基礎設施將在 WDOG_ACTIVE 未設定時向看門狗硬體傳送保活訊號。注意:當您註冊設定了此位的看門狗定時器裝置時,開啟 /dev/watchdog 將跳過 start 操作,而是傳送保活請求。

    要設定 WDOG_NO_WAY_OUT 狀態位(在註冊看門狗定時器裝置之前),您可以選擇

    • 在您的 watchdog_device 結構中靜態設定,使用

      .status = WATCHDOG_NOWAYOUT_INIT_STATUS,

      (這將設定與 CONFIG_WATCHDOG_NOWAYOUT 相同的值)或

    • 使用以下輔助函式

      static inline void watchdog_set_nowayout(struct watchdog_device *wdd,
                                               int nowayout)
      
注意

看門狗定時器驅動核心支援“魔術關閉”(magic close)功能和“無法退出”(nowayout)功能。要使用“魔術關閉”功能,您必須在看門狗資訊結構的 options 欄位中設定 WDIOF_MAGICCLOSE 位。

“無法退出”功能將覆蓋“魔術關閉”功能。

要獲取或設定驅動程式特定資料,應使用以下兩個輔助函式

static inline void watchdog_set_drvdata(struct watchdog_device *wdd,
                                        void *data)
static inline void *watchdog_get_drvdata(struct watchdog_device *wdd)

watchdog_set_drvdata 函式允許您新增驅動程式特定資料。此函式的引數是您要新增驅動程式特定資料的看門狗裝置,以及指向資料本身的指標。

watchdog_get_drvdata 函式允許您檢索驅動程式特定資料。此函式的引數是您要從中檢索資料的看門狗裝置。該函式返回指向驅動程式特定資料的指標。

要初始化 timeout 欄位,可以使用以下函式

extern int watchdog_init_timeout(struct watchdog_device *wdd,
                                 unsigned int timeout_parm,
                                 struct device *dev);

watchdog_init_timeout 函式允許您使用模組超時引數或從裝置樹中檢索 timeout-sec 屬性(如果模組超時引數無效)來初始化 timeout 欄位。最佳實踐是將預設超時值設定為 watchdog_device 中的超時值,然後使用此函式設定使用者“首選”的超時值。此例程在成功時返回零,在失敗時返回負的 errno 程式碼。

要在重啟時停用看門狗,使用者必須呼叫以下輔助函式

static inline void watchdog_stop_on_reboot(struct watchdog_device *wdd);

要在登出看門狗時停用看門狗,使用者必須呼叫以下輔助函式。請注意,這僅在未設定 nowayout 標誌時才會停止看門狗。

static inline void watchdog_stop_on_unregister(struct watchdog_device *wdd);

要更改重啟處理程式的優先順序,應使用以下輔助函式

void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority);

使用者應遵循以下優先順序設定指南

  • 0:應在最後手段時呼叫,重啟能力有限

  • 128:預設重啟處理程式,如果沒有其他處理程式可用,和/或如果重啟足以重啟整個系統,則使用此值

  • 255:最高優先順序,將搶佔所有其他重啟處理程式

要觸發預超時通知,應使用以下函式

void watchdog_notify_pretimeout(struct watchdog_device *wdd)

該函式可以在中斷上下文中呼叫。如果看門狗預超時調節器框架(kbuild CONFIG_WATCHDOG_PRETIMEOUT_GOV 符號)已啟用,則由預先配置並分配給看門狗裝置的預超時調節器執行操作。如果看門狗預超時調節器框架未啟用,watchdog_notify_pretimeout() 會向核心日誌緩衝區列印通知訊息。

要設定看門狗的最後已知硬體保活時間,應使用以下函式

int watchdog_set_last_hw_keepalive(struct watchdog_device *wdd,
                                   unsigned int last_ping_ms)

此函式必須在看門狗註冊後立即呼叫。它將最後已知的硬體心跳設定為在當前時間之前 last_ping_ms 毫秒發生。僅當在呼叫探測(probe)時看門狗已在執行,並且看門狗只能在上次 ping 後經過 min_hw_heartbeat_ms 時間後才能被 ping 時,才需要呼叫此函式。