1. 程式人生 > >樹莓派啟用看門狗watchdog

樹莓派啟用看門狗watchdog

樹莓派核心預設沒有啟用看門狗功能,當核心掛死時將進入“宕機”狀態或kgdb除錯狀態,並不會自動重啟系統。本文為樹莓派開啟看門狗功能並通過核心執行緒週期性喂狗,當出現系統崩潰時會自動重啟Linux系統。

環境說明:(1)單板:樹莓派b

                    (2)Linux核心:Linux-4.1.15

                    (3)Bootloader:u-boot-2015.10

原始碼檔案:linux-rpi-4.1.y/drivers/watchdog/bcm2835_wdt.c

1、看門狗驅動原始碼分析

樹莓派的看門狗驅動程式為核心drivers/watchdog/bcm2835_wdt.c檔案,該驅動程式實現了開關看門狗和喂狗的功能(不提供喂狗策略),它向核心看門狗子系統註冊驅動裝置,將喂狗策略移交應用程式,由應用程式開啟/dev/watchdogX標準介面並完成周期性喂狗的操作。簡單分析一下該驅動程式的原始碼:

static const struct of_device_id bcm2835_wdt_of_match[] = {
	{ .compatible = "brcm,bcm2835-pm-wdt", },
	{},
};
MODULE_DEVICE_TABLE(of, bcm2835_wdt_of_match);

static struct platform_driver bcm2835_wdt_driver = {
	.probe		= bcm2835_wdt_probe,
	.remove		= bcm2835_wdt_remove,
	.shutdown	= bcm2835_wdt_shutdown,
	.driver = {
		.name =		"bcm2835-wdt",
		.of_match_table = bcm2835_wdt_of_match,
	},
};
module_platform_driver(bcm2835_wdt_driver);
驅動程式通過platform driver實現,同時支援裝置數dtb新增platform device,匹配名稱為"brcm,bcm2835-pm-wdt"。
module_param(heartbeat, uint, 0);
MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");

module_param(nowayout, bool, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
驅動程式提供了兩個可調引數,其中heartbeat表示看門狗設定的超時時間,預設為15s;nowayout是一個bool型變數;如若設定了就表示該看門狗一旦開啟將不再向應用提供關閉的功能,只能通過不斷的喂狗操作來保證系統不會重啟。下面分析一下其中的probe初始化函式:
static int bcm2835_wdt_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *np = dev->of_node;
	struct bcm2835_wdt *wdt;
	int err;

	wdt = devm_kzalloc(dev, sizeof(struct bcm2835_wdt), GFP_KERNEL);
	if (!wdt)
		return -ENOMEM;
	platform_set_drvdata(pdev, wdt);

	spin_lock_init(&wdt->lock);

	wdt->base = of_iomap(np, 0);
	if (!wdt->base) {
		dev_err(dev, "Failed to remap watchdog regs");
		return -ENODEV;
	}

	watchdog_set_drvdata(&bcm2835_wdt_wdd, wdt);
	watchdog_init_timeout(&bcm2835_wdt_wdd, heartbeat, dev);
	watchdog_set_nowayout(&bcm2835_wdt_wdd, nowayout);
	err = watchdog_register_device(&bcm2835_wdt_wdd);
	if (err) {
		dev_err(dev, "Failed to register watchdog device");
		iounmap(wdt->base);
		return err;
	}

	dev_info(dev, "Broadcom BCM2835 watchdog timer");
	return 0;
}
該初始化函式完成以下功能:(1)分配bcm2835_wdt結構體記憶體空間,動態對映暫存器的虛擬記憶體空間到wdt->base中,後續通過向該地址空間寫入資料即可完成暫存器的操作。(2)初始化看門狗子系統的watchdog_device結構,根據輸入引數調整看門狗超時時間和設定status狀態標誌位。(3)向看門狗子系統註冊看門口裝置。

其中的watchdog_device結構例項bcm2835_wdt_wdd定義如下:

static struct watchdog_device bcm2835_wdt_wdd = {
	.info =		&bcm2835_wdt_info,
	.ops =		&bcm2835_wdt_ops,
	.min_timeout =	1,
	.max_timeout =	WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
	.timeout =	WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET),
};
這裡的ops結構為驅動程式向子系統註冊的驅動控制函式結構,子系統在在接收到使用者的Ioctl和write控制指令後會呼叫該註冊函式。由於子系統向用戶開放了調整看門狗超時時間的設定介面,這裡定義了最大和最小的超時時間限制(min_timeout和max_timeout),在調整timeout值時會進行保護判斷。
static struct watchdog_ops bcm2835_wdt_ops = {
	.owner =	THIS_MODULE,
	.start =	bcm2835_wdt_start,
	.stop =		bcm2835_wdt_stop,
	.set_timeout =	bcm2835_wdt_set_timeout,
	.get_timeleft =	bcm2835_wdt_get_timeleft,
};
樹莓派的驅動程式向子系統提供了以上4種介面(其他介面未實現),其中start介面表示啟動看門狗和喂狗操作,stop介面表示關閉看門狗,set_timeout表示調整看門狗超時時間,get_timeleft表示查詢距離看門狗超時還剩多少時間,各個函式的具體實現基本就是設定晶片的暫存器,就不仔細分析了。

bcm2835_wdt_probe函式在完成看門狗的註冊之後,在檔案系統的/dev目錄下就會生成watchdog0裝置檔案,後面應用程式就可以通過操作它來實現看門狗的控制了。Linux核心的看門狗子系統實現在drivers/watchdog/目錄下的watchdog_core.c和watchdog_dev.c檔案中,其中watchdog_core.c實現了子系統的初始化以及提供了面向驅動的註冊介面函式watchdog_register_device等;watchdog_dev.c實現了字元裝置的初始化和註冊,同時提供了裝置檔案控制介面watchdog_fops:

static const struct file_operations watchdog_fops = {
	.owner		= THIS_MODULE,
	.write		= watchdog_write,
	.unlocked_ioctl	= watchdog_ioctl,
	.open		= watchdog_open,
	.release	= watchdog_release,
};
可以看到這裡實現了open、close、write和ioctl的控制介面,另外在watchdog.h中定義了Ioctl的標準控制定義:
#define	WDIOC_GETSUPPORT	_IOR(WATCHDOG_IOCTL_BASE, 0, struct watchdog_info)	//獲取看門狗info資訊
#define	WDIOC_GETSTATUS		_IOR(WATCHDOG_IOCTL_BASE, 1, int)					//查詢看門狗status狀態資訊
#define	WDIOC_GETBOOTSTATUS	_IOR(WATCHDOG_IOCTL_BASE, 2, int)					//查詢看門狗bootstatus資訊
......			
#define	WDIOC_SETOPTIONS	_IOR(WATCHDOG_IOCTL_BASE, 4, int)					//設定開關看門狗
#define	WDIOC_KEEPALIVE		_IOR(WATCHDOG_IOCTL_BASE, 5, int)					//喂狗
#define	WDIOC_SETTIMEOUT        _IOWR(WATCHDOG_IOCTL_BASE, 6, int)				//設定看門狗超時時間
#define	WDIOC_GETTIMEOUT        _IOR(WATCHDOG_IOCTL_BASE, 7, int)				//查詢看門狗超時時間
......	
#define	WDIOC_GETTIMELEFT	_IOR(WATCHDOG_IOCTL_BASE, 10, int)					//查詢看門狗距離超時的剩餘時間
這些ioctl控制由watchdog_ioctl函式負責處理,它會轉接呼叫驅動程式中註冊的ops函式介面。關於看門狗子系統中驅動的註冊以及使用者控制呼叫流程為典型的Linux驅動子系統架構,同前博文《構建Linux核心驅動demo子系統示例》中分析總結的驅動子系統實現如出一轍(不同之處在於沒有實現sys和proc介面),本文不再詳細分析,具體看以參見watchdog_core.c和watchdog_dev.c中的原始碼。

2、看門狗驅動原始碼修改

由於看門狗驅動程式並沒有實現喂狗策略,因此要啟用看門狗可以通過編寫應用程式,在應用層建立一個守護程序實現週期喂狗操作。但本文並不通過該方式實現,而是修改該驅動程式,在核心中建立一個核心執行緒來實現喂狗的動作。在驅動原始碼中新增以下3個函式:

static void bcm2835_wdt_set_prio(unsigned int policy, unsigned int prio)
{
	struct sched_param param = { .sched_priority = prio };

	sched_setscheduler(current, policy, ¶m);
}

static int bcm2835_wdt_kthread(void *data)
{
	struct watchdog_device *wdog = (struct watchdog_device *)data;

	/* 調整核心執行緒的排程策略和優先順序 */
	bcm2835_wdt_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1);
	
	while(1) {
		if (kthread_should_stop()) {
			bcm2835_wdt_stop(wdog);
			break;
		}
	
		if (bcm2835_wdt_wdd.timeout != 0) {
			(void)bcm2835_wdt_start(&bcm2835_wdt_wdd);
			msleep((bcm2835_wdt_wdd.timeout >> 2)*1000);			
			dev_dbg(wdog->dev, "BCM2835 ping watchdog");
		} else {
			msleep(1000);
		}
	}
	
	return 0;
}

static void bcm2835_wdt_start_kthread(struct watchdog_device *wdog)
{
	/* 啟動看門狗並建立核心執行緒執行喂狗操作 */
	mutex_lock(&bcm2835_wdt_lock);
	if (kwdt_task == NULL) {
		kwdt_task = kthread_run(bcm2835_wdt_kthread, &bcm2835_wdt_wdd, "bcm2835_kwdt");
		if (IS_ERR(kwdt_task)) {
			dev_err(wdog->dev, "Failed to Create BCM2835 kernel watchdog thread");
			kwdt_task = NULL;
		}

		dev_info(wdog->dev, "Create BCM2835 kernel watchdog thread OK!"
				" TimeOut = %d sec", bcm2835_wdt_wdd.timeout);
	}
	mutex_unlock(&bcm2835_wdt_lock);
}
其中bcm2835_wdt_start_kthread函式用於建立核心喂狗守護程序bcm2835_kwdt,該執行緒執行函式bcm2835_wdt_kthread,它首先調整自身的排程策略為FIFO策略(軟實時),並提高程序的優先順序,這樣可以使該程序在系統較為繁忙時也能確保其排程性,然後在該函數週期的實現喂狗動作,喂狗的時間間隔為超時時間的1/4。如果執行緒需要銷燬會先執行關閉看門狗的動作,防止系統重啟。

在看門狗啟動函式bcm2835_wdt_start中新增如下:

static int bcm2835_wdt_start(struct watchdog_device *wdog)
{
	/* 喂狗操作 */
	......
	
	/* added by zhangyi 2016.5.7 */
	bcm2835_wdt_start_kthread(wdog);
	/* * */
	
	return 0;
}
這裡新增啟動核心執行緒的函式,如此可以通過"echo xx > /dev/watchdog0"啟動該核心執行緒,最後在驅動的release函式中增加釋放程式:
static int bcm2835_wdt_remove(struct platform_device *pdev)
{
	struct bcm2835_wdt *wdt = platform_get_drvdata(pdev);

	/* added by zhangyi 2016.5.7 */
	mutex_lock(&bcm2835_wdt_lock);
	if (kwdt_task) {
		kthread_stop(kwdt_task);
		kwdt_task = NULL;
	}
	mutex_unlock(&bcm2835_wdt_lock);
	/* * */
	
	watchdog_unregister_device(&bcm2835_wdt_wdd);
	iounmap(wdt->base);

	return 0;
}
這樣在核心解除安裝該驅動程式時會銷燬喂狗核心執行緒並關閉看門狗。

3、看門狗實驗效果

(1)編譯以上修改後的看門狗驅動程式,將生成的模組.ko檔案拷貝到樹莓派的根檔案系統的/lib/modules/4.1.15/kernel/drivers/watchdog/目錄下(若不需要udev自動載入則任意目錄皆可)。

(2)修改dts檔案arch/arm/boot/dts/bcm2708_common.dtsi,將其中的watchdog部分修改如下:

                watchdog: [email protected] {
                        compatible = "brcm,bcm2835-pm-wdt";
                        reg = <0x7e100000 0x28>;
                        timeout-sec = <15>;    //added by zhangyi 2016.5.7
                        //status = "disabled";
                };
預設dts是不使能watchdog的,這裡將他啟用,同時設定超時時間為15s(在驅動程式的bcm2835_wdt_probe->watchdog_init_timeout()函式中可能會用到)。修改完成後重新編譯dtb檔案並拷貝到U盤中。

(3)啟動看門狗

啟動樹莓派進入Linux系統後,可以發現在看門狗驅動模組已經順利載入了:

[email protected]:~# lsmod
Module                  Size  Used by
...
bcm2835_wdt             4142  0 
...

核心啟動日誌輸出如下:

[   12.868529] bcm2835-wdt 20100000.watchdog: Broadcom BCM2835 watchdog timer

下面啟動看門狗核心執行緒,超時時間為15s

[email protected]:~# echo 0 > /dev/watchdog0
[email protected]:~# dmesg -c

[  958.846139] watchdog watchdog0: Create BCM2835 kernel watchdog thread OK! TimeOut = 15 sec

(4)手動觸發看門狗復位

接下來為了驗證看門狗的功能,這裡手動觸發核心kernel panic,使得核心程序無法在排程,從而無法執行喂狗,最終系統重啟。

[email protected]:~# echo c > /proc/sysrq-trigger 

[ 1299.165014] sysrq: SysRq : Trigger a crash
[ 1299.169352] Unable to handle kernel NULL pointer dereference at virtual address 00000000
[ 1299.177544] pgd = c5ee8000
[ 1299.180279] [00000000] *pgd=05e50831, *pte=00000000, *ppte=00000000
[ 1299.186665] Internal error: Oops: 817 [#1] ARM


Entering kdb (current=0xc5838da0, pid 507) Oops: (null)
due to oops @ 0xc0318624
CPU: 0 PID: 507 Comm: bash Tainted: G           O    4.1.15 #5
Hardware name: BCM2708
task: c5838da0 ti: c5ed0000 task.ti: c5ed0000
PC is at sysrq_handle_crash+0x28/0x34
LR is at __handle_sysrq+0x9c/0x16c
pc : [<c0318624>]    lr : [<c0318edc>]    psr: 60000013
sp : c5ed1e60  ip : c5ed1e70  fp : c5ed1e6c
r10: 00000002  r9 : c5ed0000  r8 : 00000000
r7 : 00000005  r6 : 00000063  r5 : c0bc6bc4  r4 : c0c0cb48
r3 : 00000000  r2 : 00000001  r1 : c0c32590  r0 : 00000063
Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user
Control: 00c5387d  Table: 05ee8008  DAC: 00000015
CPU: 0 PID: 507 Comm: bash Tainted: G           O    4.1.15 #5
Hardware name: BCM2708
[<c0016660>] (unwind_backtrace) from [<c0013524>] (show_stack+0x20/0x24)
[<c0013524>] (show_stack) from [<c05263c4>] (dump_stack+0x20/0x28)
[<c05263c4>] (dump_stack) from [<c0010ae4>] (show_regs+0x1c/0x20)
[<c0010ae4>] (show_regs) from [<c00939f0>] (kdb_main_loop+0x33c/0x740)
[<c00939f0>] (kdb_main_loop) from [<c00963e8>] (kdb_stub+0x18c/0x3cc)
[<c00963e8>] (kdb_stub) from [<c008ccfc>] (kgdb_handle_exception+0x27c/0x7c0)
more> 


U-Boot 2015.10 (Jan 02 2016 - 10:49:06 +0800)


DRAM:  128 MiB
RPI Model B rev2
MMC:   bcm2835_sdhci: 0
reading uboot.env
In:    serial
Out:   lcd
Err:   lcd
Net:   Net Initialization Skipped
No ethernet found.
Hit any key to stop autoboot:  0 


可以看到在觸發了panic以後的15s後,系統自動重啟,重新載入u-boot並引導Linux系統啟動,至此樹莓派的看門狗功能新增完成。

最後,本文中僅作為一種簡單的實現方式,亦可根據需求實現看門狗在使用者主程序中或硬體中斷處理程式中執行喂狗策略(中斷中可監測業務程序的執行狀態並判斷是否需要喂狗),實現方法和種類很多,原理一樣。

相關推薦

樹莓啟用看門watchdog

樹莓派核心預設沒有啟用看門狗功能,當核心掛死時將進入“宕機”狀態或kgdb除錯狀態,並不會自動重啟系統。本文為樹莓派開啟看門狗功能並通過核心執行緒週期性喂狗,當出現系統崩潰時會自動重啟Linux系統。 環境說明:(1)單板:樹莓派b                    

Linux 軟件看門 watchdog

tro 特定 nis 正常 lose sign signed fcntl 定時器 Linux 自帶了一個 watchdog 的實現,用於監視系統的運行,包括一個內核 watchdog module 和一個用戶空間的 watchdog程序。內核 watchdog 模塊通過 /

【分享】iTOP-iMX6UL開發板驅動看門 watchdog 以及 Linux-c 測試例程

eabi abi 例如 ext ora dev 格式 oar href iTOP-iMX6UL開發板看門狗測試例程,iTOP-iMX6UL 開發板的看門狗驅動默認已經配置,可以直接使用測試例程。 版本 V1.1:1、格式修改;2、例程修改完善,其中增加餵狗代碼。1 看

【轉】Linux 軟體看門 watchdog

Linux 自帶了一個 watchdog 的實現,用於監視系統的執行,包括一個核心 watchdog module 和一個使用者空間的 watchdog程式。核心 watchdog 模組通過 /dev/watchdog 這個字元裝置與使用者空間通訊。使用者空間程式一旦開啟 /dev/watchdog

什麼是看門(watchdog)?看門有什麼作用?

什麼是看門狗(watchdog) 看門狗,又叫 watchdog timer,是一個定時器電路, 一般有一個輸入,叫喂狗,一個輸出到MCU的RST端,MCU正常工作的時候,每隔一端時間輸出一個訊號到喂狗端,給 WDT 清零,如果超過規定的時間不喂狗,(一般在程式跑飛時),WDT 定時超過,就回給出一個復位訊

Linux 軟體看門 watchdog

Linux 自帶了一個 watchdog 的實現,用於監視系統的執行,包括一個核心 watchdog module 和一個使用者空間的 watchdog 程式。核心 watchdog 模組通過 /dev/watchdog 這個字元裝置與使用者空間通訊。使用者空間程式一

Linux 軟體看門 watchdog

Linux 自帶了一個 watchdog 的實現,用於監視系統的執行,包括一個核心 watchdog module 和一個使用者空間的 watchdog程式。核心 watchdog 模組通過 /de

Android系統服務之看門(WatchDog)

現在的CPU基本上都帶有WatchDog功能,這種硬體的WatchDog可以在系統死掉(死鎖或者程式跑飛)後重啟系統,讓系統回到可以工作的狀態。WatchDog不能防止系統死掉,但是它能夠起死回生,從而提高系統的可用性。 硬體級的WatchDog也有它的侷限性,它只能在系統

樹莓 Raspbian 軟件源更改 看門啟用

hust XP emp 一行 啟用 pan 這一 targe ssh連接 1.替換腳本 下面腳本請直接復制到終端執行!! 適用於raspbian-stretch(基於Debian9) sudo -s echo -e "deb http://mirrors.us

Rasipbian(樹莓啟用root權限的方法

mod width config new 修改用戶 unlock sip 啟用 登陸 啟用root raspbian默認用戶名為pi、密碼為raspberry(若使用raspi-config修改過密碼,則為修改後的密碼)默認情況下,root用戶並未啟用,且沒有密碼

樹莓 Raspberry Pi 啟用 root 登陸賬戶

樹莓派使用指南樹莓派 Raspberry Pi 啟用 root 登陸賬戶樹莓派系統使用的linux是debian系統,所以樹莓派啟用root和debian是相同的。debian裏root賬戶默認沒有密碼,但賬戶鎖定。當需要root權限時,由默認賬戶經由sudo執行,Raspberry pi 系統中的Raspb

基於S3C2440的嵌入式Linux驅動——看門watchdog)驅動解讀

csdn note 動作 strong 申請 信號 error started 讀者 本文將介紹看門狗驅動的實現。 目標平臺:TQ2440 CPU:s3c2440 內核版本:2.6.30 1. 看門狗概述 看門狗其實就是一個定時器,當該定時器溢出前必須對看門狗進

樹莓 3b 串口啟用

p s 不知道 config linux 系統 emd 目錄 fig onf 網上搜到的方法都沒用,不知道是不是系統版本的原因。以下方法是試出來的。。。 uname -aLinux raspberrypi 4.14.79-v7+ #1159 SMP Sun Nov 4

樹莓 3b 串列埠啟用

網上搜到的方法都沒用,不知道是不是系統版本的原因。以下方法是試出來的。。。   uname -aLinux raspberrypi 4.14.79-v7+ #1159 SMP Sun Nov 4 17:50:20 GMT 2018 armv7l GNU/Linux   關於串列埠設

樹莓配置watchdog

0x00 安裝watchdog apt install watchdog 編輯/etc/modules,新增bcm2708_wdog 編輯/etc/watchdog.conf watchdog-device = /dev/watchdog # 最

嵌入式系統之WATCHDOG看門)概述

1。概述: WATCHDOG對於沒有底層開發經驗的開發人員來說,可能比較陌生,但是它在系統起到非常重要的作用,相當於系統警察,當系統發生嚴重錯誤(如程式進入死迴圈等)不能 恢復的時候,WATCHDOG能夠讓系統重啟。WATCHDOG的應用主要是在嵌入式作業系統中,避免了系統在

Resin 的 watchdog(看門)介紹

為了穩定和安全,Resin使用一個獨立的watchdog程序來啟動和監視Resin伺服器。watchdog連續你檢測Resin伺服器的狀 態,如果其沒有反應或者遲鈍,將會重啟Resin伺服器程序。大多數情況下,watchdog從resin.xml中讀取配置資訊,而不需要其它的

初次使用樹莓啟用root管理員(登入root管理員)

初次使用樹莓派系統時,預設使用者是pi ,密碼為raspberry。 要想使用root帳號,或者說開啟root使用者,可使用pi使用者登入,執行下面命令(此命令是給root賬戶設定密碼的,當切換到root管理員後,此命令無效) sudo passwd root 說

s3c6410硬體WATCHDOG TIMER(看門定時器)

  先簡述看門狗的工作過程,看門狗實際是一個定時器,內部有個計數器,每當時鐘訊號到來時,計數器暫存器減一。如果減到0,則重新啟動系統;如果在減到0之前,系統又設定計數器為一個較大的值,則系統不會重啟。系統正常時,就不會重啟;當系統發生故障時,不能設定計數暫存器,系統重新啟

Resin 的watchdog(看門)介紹和resin負載均衡實現

為了穩定和安全,Resin使用一個獨立的watchdog程序來啟動和監視Resin伺服器。watchdog連續你檢測Resin伺服器的狀態,如果其沒有反應或者遲鈍,將會重啟Resin伺服器程序。大多數情況下,watchdog從resin.xml中讀取配置資訊,而不需要其它