Linux 2.6.10內(nèi)核下PCI Express Native熱插拔框架的實(shí)現(xiàn)機(jī)制
PCI熱插拔技術(shù),可以有效避免由更換外設(shè)引起的服務(wù)器系統(tǒng)停機(jī),對(duì)于提高服務(wù)器系統(tǒng)可用性和可擴(kuò)展性意義重大。本文討論了PCI Express熱插拔所涉及的軟件因素,并基于此,剖析了Linux 2.6.10內(nèi)核下PCI Express 插槽熱插拔子系統(tǒng)的關(guān)鍵實(shí)現(xiàn)機(jī)制。
1997年,PCI SIG制定了第一個(gè)PCI熱插拔規(guī)范,其中定義了支持熱插拔所必需的平臺(tái)、板卡和軟件元素。PCI SIG推出了標(biāo)準(zhǔn)熱插拔控制器規(guī)范(SHPC SPEC),其中明確了熱插拔的標(biāo)準(zhǔn)使用模式和嚴(yán)格的寄存器組要求,并且允許操作系統(tǒng)提供商在平臺(tái)特定的軟件之外提供熱插拔支持,逐步完成了熱插拔標(biāo)準(zhǔn)制定工作,進(jìn)入技術(shù)的全面推廣階段。 2002年以后,Intel把熱插拔作為一種天然屬性賦予新推出的PCI Express規(guī)范,PCI Express熱插拔總結(jié)了五年來工業(yè)標(biāo)準(zhǔn)的經(jīng)驗(yàn),具有如下特點(diǎn):
- 基于SHPC模式并對(duì)其進(jìn)行了功能擴(kuò)展;
- PCI Express從設(shè)計(jì)上就把熱插拔寄存器集成進(jìn)入了其標(biāo)準(zhǔn)的性能寄存器組(而在SHPC 1.0中,熱插拔寄存器是附加的);
- 提供給操作系統(tǒng)一個(gè)統(tǒng)一的熱插拔硬件寄存器接口,賦予了操作系統(tǒng)進(jìn)行原生熱插拔(繞過了傳統(tǒng)基于BIOS的熱插拔方法)的能力;
- 通過在基本的體系機(jī)構(gòu)層次定義熱插拔必須的硬件要求,完善了一種標(biāo)準(zhǔn)的使用模式;
- 提供了規(guī)格參數(shù)來保證OEM產(chǎn)品的低成本而高平臺(tái)可靠性。
具有完整功能的PCI Express熱插拔系統(tǒng)在平臺(tái)硬件和固件支持之外,還必須有操作系統(tǒng)以及設(shè)備驅(qū)動(dòng)程序的支持。
在各大芯片廠商紛紛推出支持PCI熱插拔的產(chǎn)品的同時(shí),Microsoft,Novell,SCO等公司在他們相關(guān)的操作系統(tǒng)中也都包括了支持熱插拔的技術(shù), Novell NetWare 4.11&5和SCO UnixWare 7以及更新版本支持完整PCI熱插拔(熱替換和熱添加)。Microsoft Windows NT 4.0利用Compaq 服務(wù)器支持盤(SSD) for NT 4.0,提供了PCI熱替換支持,進(jìn)入2000年后,Microsoft Windows 2000也內(nèi)建了對(duì)PCI熱插拔技術(shù)的全面軟件支持。對(duì)于GNU/Linux,從2001年1月份的內(nèi)核2.4版本開始,PCI 熱插拔開始成為其標(biāo)準(zhǔn)特性之一,到2.6 版本,熱插拔功能已經(jīng)被整合進(jìn)入核心設(shè)備模型?,F(xiàn)在,幾乎所有的Linux發(fā)行版本,包括RedHat, Debian和 United Linux,都對(duì)PCI 熱插拔提供了良好支持,并分別有所擴(kuò)展。BSD分支在這方面起步較晚,OpenBSD在2004年三月份的3.6版本中才出現(xiàn)了不含設(shè)備驅(qū)動(dòng)的設(shè)備熱插拔參考框架,而FreeBSD 到5.3版本為止,尚未提供對(duì)PCI 熱插拔的支持。
作為2002年出現(xiàn)的規(guī)范,各個(gè)通用操作系統(tǒng)對(duì)PCI Express的支持才剛剛起步。在微軟公司的 Windows 2000, Windows XP, and Windows Server 2003中,PCI Express設(shè)備被當(dāng)作PCI設(shè)備進(jìn)行熱插拔,其他PCI Express的高級(jí)功能可以由固件激活。在Windows Longhorn中,才提供對(duì)PCI Express高級(jí)特性的原生支持。GNU/Linux在內(nèi)核版本2.6.9中開始專門提供對(duì)于PCI Express 熱插拔的支持,中間歷經(jīng)軟件架構(gòu)的改動(dòng),逐漸發(fā)展為以內(nèi)核版本2.6.10以及內(nèi)核版本2.6.12下兩種不同的PCI Express熱插拔驅(qū)動(dòng)架構(gòu)。本文主要討論2.6.10所采用的PCI Express熱插拔驅(qū)動(dòng)架構(gòu),并在最后給出2.6.12的新特性。
根據(jù)規(guī)范,一個(gè)完整的 Native PCI Express 熱插拔系統(tǒng)需要幾方面的相互配合,分別為硬件元素、固件元素和軟件元素。硬件元素是指主板總線系統(tǒng)的電氣特性方面的支持,包括熱插拔控制器(Hot-Plug Controller)、卡槽電源切換邏輯(Card Slot Power Switching Logic)、板卡重置邏輯(Card Reset Logic)、電源指示燈(Power Indicator)、提示按鈕(Attention Button)和板卡存在檢測(cè)引腳(Card Present Detect Pins)等等;固件元素是指主板BIOS必須對(duì)熱插拔提供的支持,要實(shí)現(xiàn)Native PCI Express熱插拔,固件必須提供OSHP方法或ACPI _OSC方法之一;軟件元素是指操作系統(tǒng)操綜合使用PCI Express 熱插拔所必須提供的功能組件。
為了操縱平臺(tái)的硬件元素,提供PCI Express熱插拔服務(wù),我們必須實(shí)現(xiàn)表1所示的主要熱插拔軟件元素。
PCIE熱插拔(PCIEHP)子系統(tǒng)中的標(biāo)準(zhǔn)熱插拔系統(tǒng)驅(qū)動(dòng)程序通過檢查PCI Express性能數(shù)據(jù)結(jié)構(gòu)中的插槽性能寄存器(Slot Capabilities register),來獲取硬件的熱插拔部件信息。這些寄存器所能反映的信息如下:
- 插槽是否支持熱插拔
- 設(shè)備是否支持不通知軟件的突然拔出操作
- 提示燈、控制器等硬件元素是否存在
一旦軟件完成了PCI Express設(shè)備熱插拔功能的開啟和配置工作,熱插拔行為(例如移出或插入請(qǐng)求,電源故障)就可以向系統(tǒng)熱插拔處理機(jī)制提交系統(tǒng)中斷和電源管理事件。這種熱插拔處理機(jī)制由操作系統(tǒng)獨(dú)立提供,圖1展示了PCI Express Native熱插拔的服務(wù)模型,并展示了它與傳統(tǒng)的基于ACPI的模型之間的區(qū)別。
四Linux 2.6.10 PCIE 熱插拔子系統(tǒng)代碼分析
為了提供PCI Express熱插拔服務(wù),Linux 2.6.10 PCIE熱插拔(PCIEHP)子系統(tǒng)必須實(shí)現(xiàn)用戶操作界面、熱插拔服務(wù)程序和標(biāo)準(zhǔn)熱插拔系統(tǒng)驅(qū)動(dòng)等熱插拔軟件元素,并使軟件的行為符合熱插拔標(biāo)準(zhǔn)使用模式[4]。另外,一些特定的熱插拔功能必須與支持熱插拔的設(shè)備驅(qū)動(dòng)結(jié)合在一起發(fā)揮作用。
PCIEHP核內(nèi)部分的主體是一個(gè)核心線程,其控制邏輯以模塊的方式加載入內(nèi)核,負(fù)責(zé)監(jiān)控PCI Express總線上的熱插拔事件,并做相關(guān)處理;核外部分是一個(gè)用戶空間的腳本,從內(nèi)核中調(diào)用,并根據(jù)內(nèi)核傳回的信息執(zhí)行后續(xù)處理過程。
1 熱插拔驅(qū)動(dòng)程序生命周期
從PCIEHP子系統(tǒng)啟動(dòng),到子系統(tǒng)被卸載期間,PCIEHP完全接管系統(tǒng)中PCI Express插槽熱插拔事件。
PCIEHP子系統(tǒng)啟動(dòng)時(shí),首先要開啟用戶態(tài)守護(hù)進(jìn)程。然后初始化通知機(jī)制,開啟PCI Express熱插拔事件處理核心線程,為操作系統(tǒng)中各總線數(shù)據(jù)結(jié)構(gòu)分別預(yù)初始化一個(gè)熱插拔插槽列表。接著,根據(jù)內(nèi)核編譯時(shí)是否指定ACPI式資源管理,初始化設(shè)備資源管理方式。最后,根據(jù)設(shè)備標(biāo)識(shí),在系統(tǒng)中注冊(cè)PCI Express熱插拔驅(qū)動(dòng)程序,并將其綁定到總線上所有可能掛載熱插拔插槽的PCIE橋接設(shè)備,然后初始化對(duì)應(yīng)的熱插拔控制器,分配資源,開啟定時(shí)的中斷輪詢機(jī)制。
卸載PCIEHP子系統(tǒng),首先釋放熱插拔控制器全局列表中每個(gè)控制器所占用的資源,釋放熱插拔插槽全局列表中每個(gè)插槽所占用的資源,終止通知機(jī)制的運(yùn)行,結(jié)束PCI Express熱插拔事件處理核心線程;接著,根據(jù)內(nèi)核編譯時(shí)是否指定ACPI式資源管理,釋放相關(guān)設(shè)備占用的系統(tǒng)資源;然后,注銷PCI Express熱插拔驅(qū)動(dòng)程序;在處理完當(dāng)前熱插拔事件后,終止核外守護(hù)進(jìn)程。
在PCIEHP加載后,為了在熱插拔執(zhí)行過程中追蹤其狀態(tài)變化,我們把熱插拔插槽的狀態(tài)抽象如下:
- 靜態(tài) (STATIC STATE ),
正常工作或者空閑的時(shí)段, - 開啟前閃爍提示態(tài)(BLINKINGON STATE)
發(fā)出開啟請(qǐng)求到確認(rèn)之前的時(shí)段 - 關(guān)閉前閃爍提示態(tài) (BLINKINGOFF STATE)
發(fā)出關(guān)閉請(qǐng)求到確認(rèn)之前的時(shí)段 - 開啟態(tài) (POWERON STATE)
執(zhí)行開啟操作的時(shí)段 - 關(guān)閉態(tài)(POWEROFF STATE)
執(zhí)行關(guān)閉操作的時(shí)段
各個(gè)插槽狀態(tài)之間的轉(zhuǎn)換關(guān)系如圖2所示:
2 用戶控制腳本/sbin/hotplug和核內(nèi)外通信機(jī)制
在內(nèi)核熱插拔處理完畢后,它會(huì)在核內(nèi)調(diào)用一個(gè)用戶態(tài)腳本hotplug,這個(gè)腳本將根據(jù)內(nèi)核提供的信息進(jìn)行后續(xù)處理工作。它是設(shè)備熱插拔通告的用戶空間部分,它接收內(nèi)核傳出的熱插拔操作類型和環(huán)境變量,處理設(shè)備掛載和卸載操作。通常,hotplug會(huì)根據(jù)設(shè)備類型調(diào)用一個(gè)策略腳本,針對(duì)設(shè)備和系統(tǒng)當(dāng)前參數(shù)進(jìn)行后續(xù)的配置工作。例如,對(duì)于網(wǎng)卡,可能會(huì)調(diào)用腳本指定IP地址,網(wǎng)關(guān)和域名服務(wù)器等等,對(duì)于存儲(chǔ)設(shè)備,可能會(huì)調(diào)用mount命令來加載它到文件系統(tǒng)內(nèi)。
內(nèi)核傳送給hotplug的信息包含熱插拔操作類型(例如PCI),并且在一個(gè)環(huán)境變量中提供本次操作相關(guān)信息:
- 行為種類:添加或刪除
- 對(duì)象設(shè)備所屬PCI 類、子類和編程接口
- 對(duì)象制造商標(biāo)志和設(shè)備標(biāo)志
- 對(duì)象總線地址、插槽地址和功能函數(shù)編號(hào)
核外的后續(xù)配置工作涉及如下文件:
/etc/hotplug/pci.rc /ect/hotplug/pci.Agent /etc/rc.d/init.d/hotplug
在內(nèi)核中,由kernel/kmod.c中的函數(shù)
int call_usermodehelper (char *path, char **argv, char **envp, int wait)來開啟用戶態(tài)腳本/sbin/hotplug。在參數(shù)表中,path表示了所啟動(dòng)的核外應(yīng)用程序的路徑,argv是應(yīng)用程序的參數(shù)表,envp是環(huán)境變量列表,wait則指出了是否同步等待應(yīng)用程序執(zhí)行完畢再返回執(zhí)行結(jié)果的狀態(tài)。
3 PCIE 熱插拔模塊構(gòu)成
為了使用PCI Express Native Hotplug,我們必須在編譯的時(shí)候開啟對(duì)應(yīng)的功能模塊。
在內(nèi)核配置中,PCIE Hotplug對(duì)應(yīng)的開關(guān)為HOTPLUG_PCI_PCIE,它依賴于HOTPLUG_PCI。如果你的主板支持PCI Express Native Hotplug,可以選擇Y;如果你只是想把這個(gè)驅(qū)動(dòng)作為模塊來編譯,那么選擇M,此模塊叫做pciehp,在源代碼dirverpcihotplugKconfig文件中,你可以看到:
config HOTPLUG_PCI_PCIE tristate "PCI Express Hotplug driver" depends on HOTPLUG_PCI |
完整的pciehp模塊功能涉及到如下幾個(gè)文件pci_hotplug_core.c, pciehp_core.c,pciehp_ctrl.c,pciehp_pci.c pciehp_hpc.c,另外根據(jù)是否開啟了ACPI,包含pciehprm_acpi.c 或者pciehprm_nonacpi.c,在源代碼dirverpcihotplugMakefile文件中,你可以看到:
pci_hotplug-objs := pci_hotplug_core.o pciehp-objs := pciehp_core.o pciehp_ctrl.o pciehp_pci.o pciehp_hpc.o ifdef CONFIG_ACPI_BUS pciehp-objs += pciehprm_acpi.o else pciehp-objs += pciehprm_nonacpi.o endif |
代碼的主要任務(wù)就是在所有支持熱插拔的PCIE橋上加載熱插拔驅(qū)動(dòng)程序,監(jiān)控?zé)岵灏问录⒏鶕?jù)類型,如是熱插入事件、熱拔出事件還是電源故障等分別予以處理。
熱插拔驅(qū)動(dòng)程序的加載所進(jìn)行的主要工作是開啟并初始化PCIE熱插拔的內(nèi)核線程,這部分代碼位于/driver/pci/hotplug/pciehp_core.c中。入口函數(shù)為:
static int __init pcied_init(void)
#ifdef CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE pciehp_poll_mode = 1; #endif retval = pcie_start_thread(); if (retval) goto error_hpc_init; retval = pciehprm_init(PCI); if (!retval) { retval = pci_register_driver(&pcie_driver); dbg("pci_register_driver = %d ", retval); info(DRIVER_DESC " version: " DRIVER_VERSION " "); } |
pcied_init中所涉及的關(guān)鍵函數(shù)分析如下:
retval = pcie_start_thread();
初始化并開啟通知機(jī)制:
pciehp_event_start_thread()啟動(dòng)事件監(jiān)控處理線程
然后初始化slot列表,系統(tǒng)中每個(gè)bus給一個(gè)slot列表。
struct pci_func *pciehp_slot_list[256]; 都設(shè)為NULL
retval = pciehprm_init(PCI);
原型:int pciehprm_init(enum php_ctlr_type ctlr_type)
初始化資源(區(qū)別兩種情況:acpi和非acpi的)在非acpi的初始化方式下,調(diào)用空函數(shù)legacy_pciehprm_init_pci();在pcihprm_nonacpi.h中,定義了irq_info,irq_routing_table兩個(gè)結(jié)構(gòu)。在acpi初始化方式下,通過pciehprm_acpi_scan_pci()在acpi樹下遍歷搜尋PCI設(shè)備。不論acpi和非acpi的,都要求php_ctlr_type為PCI。
retval = pci_register_driver(&pcie_driver);
注冊(cè)并初始化PCI橋熱插拔驅(qū)動(dòng)程序模塊。
把設(shè)備驅(qū)動(dòng)加入已注冊(cè)設(shè)備驅(qū)動(dòng)列表,即使在期間沒有相應(yīng)設(shè)備出現(xiàn)驅(qū)動(dòng)程序仍然保持有效。
int count = 0; /* initialize common driver fields */ |
用來泛化之,可以把drv->driver看作為drv的基類信息
drv->driver.name = drv->name; drv->driver.bus = &pci_bus_type; drv->driver.probe = pci_device_probe; drv->driver.remove = pci_device_remove; drv->driver.kobj.ktype = &pci_driver_kobj_type; pci_init_dynids(&drv->dynids); count = driver_register(&drv->driver); |
注意:這里是如何從基類drv->driver一般設(shè)備的驅(qū)動(dòng)向子類drv這個(gè)pci設(shè)備的驅(qū)動(dòng)逆向關(guān)聯(lián)的--使用CONTAINER宏
pcie_driver為PCIE驅(qū)動(dòng)程序?qū)ο?,定義在 pciehp_core.c中
static struct pci_driver pcie_driver = { //驅(qū)動(dòng)名稱定義為"pciehp" .name= PCIE_MODULE_NAME, // id_table指定探測(cè)函數(shù)probe所應(yīng)用的范圍,這里在表中指定為所有的PCI橋設(shè)備 .id_table = pcied_pci_tbl, //probe為指定的設(shè)備探測(cè)函數(shù) .probe = pcie_probe, }; static struct pci_device_id pcied_pci_tbl[] = { { //此處選擇所有PCI橋 .class = ((PCI_CLASS_BRIDGE_PCI << 8) | 0x00), .class_mask = ~0, .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, }, |
設(shè)備探測(cè)pcie_probe:
static int pcie_probe (struct pci_dev *pdev, const struct pci_device_id *ent) in pcie-core.c |
已經(jīng)確定了pdev就是pcie_driver 所匹配的PCI橋設(shè)備,而且它在pcie_driver中所對(duì)應(yīng)的設(shè)備特征號(hào)是*ent,就可以對(duì)其進(jìn)行進(jìn)一步的初始化和探測(cè)。
具體行為如下:
- 綁定熱插拔插槽
- 設(shè)置了控制器及其狀態(tài)
- 注冊(cè)中斷處理函數(shù)
- 讀寫配置頭,然后分配資源,最后對(duì)插槽檢測(cè),掛接
熱插拔驅(qū)動(dòng)程序?qū)τ跓岵灏问录妮喸兒屯ㄖ捎卯惒綑C(jī)制,對(duì)熱插拔功能部件的操縱是通過讀寫相關(guān)寄存器組進(jìn)行的。主要功能函數(shù)關(guān)系請(qǐng)參見圖3:
圖3中主要函數(shù)的功能介紹如下:
A插槽事件監(jiān)控線程
作為熱插拔活動(dòng)最直接的信息,插槽事件由硬件操作并置位相關(guān)寄存器組,系統(tǒng)軟件可以通過定時(shí)輪詢或者中斷方式獲取事件信息,執(zhí)行對(duì)應(yīng)的事件預(yù)處理函數(shù)。插槽事件如下:
- 熱插拔命令到達(dá)
- 插槽鎖狀態(tài)改變
- 適配卡存在狀態(tài)改變
- 電源出現(xiàn)故障
php_ctlr->int_poll_timer.function = &int_poll_timeout 其中php_ctlr是熱插拔控制器狀態(tài)php_ctlr_state_s類型,它定義于pciehp_hpc.h中,記錄當(dāng)前熱插拔控制器重要狀態(tài),被用作HPC(controller)的控制器句柄;熱插拔控制器controller位于文件pciehp.h中,描述了PCIE熱插拔控制器的特征;
定時(shí)輪詢函數(shù)原型如下:
static void int_poll_timeout(unsigned long lphp_ctlr)
其中調(diào)用pcie_isr( 0, (void *)php_ctlr, NULL );
當(dāng)最后一個(gè)參數(shù)是NULL時(shí),采用polling機(jī)制。
static irqreturn_t pcie_isr(int IRQ, void *dev_id, struct pt_regs *regs);
輪詢機(jī)制通過定期(2second,使用一個(gè)計(jì)時(shí)器)讀取ctrller對(duì)應(yīng)的狀態(tài)寄存器,來獲取事件,然后調(diào)用ctrl狀態(tài)參量對(duì)應(yīng)的事件處理函數(shù)。也就是按鈕、電源、MRL等等事件處理函數(shù),并分別調(diào)用event_semaphore來激活event_thread.
B事件預(yù)處理函數(shù)
在插槽事件監(jiān)控線程截獲插槽事件后,它根據(jù)事件類型調(diào)用這組處理函數(shù),執(zhí)行完畢后,填寫對(duì)應(yīng)控制器上所掛接的事件隊(duì)列,并激活睡眠等待的處理線程??梢约せ钏咧械奶幚砭€程的函數(shù)包括如下幾個(gè):
pciehp_handle_attention_button :處理按鈕事件
pciehp_handle_switch_change:處理開關(guān)狀態(tài)改變事件
pciehp_handle_presence_change:處理存在性狀態(tài)變化事件
pciehp_handle_power_fault:處理電源故障事件
pushbutton_helper_thread:按鈕動(dòng)作處理線程
C熱插拔事件處理核心線程event_thread
event_thread的處理過程如下:
在一個(gè)無限循環(huán)中,阻塞等待插槽事件發(fā)生
當(dāng)線程被某一事件喚醒后,
如果 熱插拔請(qǐng)求已通過延時(shí)確認(rèn)
進(jìn)入熱插拔請(qǐng)求處理函數(shù)
否則
輪詢熱插拔控制器隊(duì)列:
把控制器作為參數(shù)傳給插槽事件處理函數(shù)
其中
if (pushbutton_pending) pciehp_pushbutton_thread(pushbutton_pending); else if (surprise_rm_pending) pciehp_surprise_rm_thread(surprise_rm_pending); |
前一個(gè)處理按鈕事件,后一個(gè)處理突然拔出事件。
參數(shù)pushbutton_pending是由如下函數(shù)提供的:
static void pushbutton_helper_thread(unsigned long data) { pushbutton_pending = data; up(&event_semaphore); |
而這個(gè)函數(shù)又被作為定時(shí)task事件的行動(dòng)部分,賦值給
static void interrupt_event_handler(struct controller *ctrl)中的
p_slot->task_event.function:
函數(shù):
p_slot->task_event.function
= (void (*)(unsigned long)) pushbutton_helper_thread;
參數(shù):
p_slot->task_event.data
= (unsigned long) p_slot;
D插槽事件處理函數(shù)interrupt_event_handler
根據(jù)控制器信息,獲取控制器對(duì)應(yīng)插槽事件
尚有事件等待處理,則執(zhí)行以下內(nèi)容:
逐一查看控制器所掛接事件隊(duì)列的每個(gè)成員(根據(jù)事件類型):
1. 請(qǐng)求取消
取消前五秒內(nèi)觸發(fā)的一次熱插拔請(qǐng)求
2. 請(qǐng)求觸發(fā)
觸發(fā)一次熱插拔操作:導(dǎo)致一個(gè)五秒鐘的延時(shí),如果五秒內(nèi)請(qǐng)求沒有被拒絕,則確認(rèn)前一個(gè)熱插拔請(qǐng)求
3. 電源故障
彈出提示信息
4. 其他
讀取插槽的功能寄存器,更新狀態(tài)信息。
E熱插拔請(qǐng)求處理函數(shù)pciehp_pushbutton_thread
一個(gè)定期運(yùn)行的程序,處理當(dāng)前插槽上阻塞的請(qǐng)求,根據(jù)請(qǐng)求類型:對(duì)插槽中的設(shè)備進(jìn)行熱移出(F)或者熱添加操作(G)。
F插槽熱拔出設(shè)備pciehp_disable_slot
保證所除去的不是視頻控制器
卸載所除去的適配卡占用的系統(tǒng)資源,更新總線結(jié)構(gòu),關(guān)閉電源
通知用戶態(tài)守護(hù)進(jìn)程
更新插槽狀態(tài)
G插槽熱添加設(shè)備pciehp_enable_slot
1. 執(zhí)行適配卡添加的一系列相關(guān)操作: 失敗恢復(fù)預(yù)處理,存在性檢驗(yàn)、打開電源,檢查link training狀態(tài),獲取設(shè)備基本信息,配置設(shè)備,為設(shè)備建立相關(guān)數(shù)據(jù)結(jié)構(gòu),掛接到上級(jí)總線等。
2. 為新添加的設(shè)備查找并掛接驅(qū)動(dòng)程序。
3. 特別地,對(duì)于橋接設(shè)備,把它掛接到上級(jí)總線后,還要繼續(xù)對(duì)其下級(jí)總線進(jìn)行掃描和掛接。
4. 通知用戶態(tài)守護(hù)進(jìn)程。
5. 更新插槽狀態(tài)。
熱插拔PCI板卡可以使用提示按鈕或用戶界面來進(jìn)行,下面我們介紹使用用戶界面來啟動(dòng)熱插入和熱拔出的操作過程,以及Fedora C4T2下所采用的方式。
設(shè)備的熱插入
1. 操作員安裝卡,閉合插槽保護(hù)鎖,保護(hù)鎖感應(yīng)器通知熱插拔控制器把連接信號(hào)接通到插槽。
2. 然后,操作員通知熱插拔服務(wù)程序:卡已經(jīng)被安裝并可以激活。軟件提示用戶對(duì)此進(jìn)行確認(rèn)。
3. 在操作員請(qǐng)求連接后,熱插拔服務(wù)程序向控制著熱插拔控制器的熱插拔系統(tǒng)驅(qū)動(dòng)程序下達(dá)命令,閃爍插槽的電源指示燈,提示操作員此時(shí)不可以拔動(dòng)適配卡。
4. 在熱插拔軟件對(duì)此請(qǐng)求進(jìn)行確認(rèn)期間內(nèi),電源指示燈繼續(xù)閃爍。注意此時(shí)軟件可能會(huì)拒絕這個(gè)安裝請(qǐng)求(例如,安全策略此刻禁止插槽被激活)。另外,如果請(qǐng)求沒有生效,軟件拒絕請(qǐng)求并對(duì)熱插拔控制器發(fā)出命令關(guān)閉電源指示燈。規(guī)范建議軟件通過一條消息通知操作員請(qǐng)求被拒絕的原因。
5. 如果請(qǐng)求被確認(rèn),熱插拔服務(wù)程序?qū)岵灏蜗到y(tǒng)驅(qū)動(dòng)發(fā)出請(qǐng)求,為插槽加電。
6. 加電后,軟件發(fā)出命令完全打開電源指示燈。
7. 當(dāng)link training完成后,操作系統(tǒng)指示平臺(tái)配置程序賦予適配卡必需的資源,來配置適配卡的功能。
8. 操作系統(tǒng)為PCI Express設(shè)備中的功能尋找恰當(dāng)?shù)尿?qū)動(dòng)程序,并加載之。
9. 接著系統(tǒng)調(diào)用驅(qū)動(dòng)程序的初始化入口,并執(zhí)行驅(qū)動(dòng)的初始化代碼。這些代碼完成設(shè)備的設(shè)置,并填寫設(shè)備的PCI 配置命令寄存器的相關(guān)標(biāo)志位來激活設(shè)備。
1. 操作員通過指定適配卡所在物理插槽號(hào)碼來初始化移出請(qǐng)求。
2. 軟件彈出窗口要求操作員確認(rèn)請(qǐng)求。注意,此時(shí)電源指示燈保持開啟狀態(tài)。
3. 操作員確認(rèn)請(qǐng)求后,熱插拔服務(wù)程序向熱插拔系統(tǒng)驅(qū)動(dòng)發(fā)出請(qǐng)求,要求熱插拔控制器閃爍電源指示燈。注意此時(shí)軟件可能會(huì)拒絕這個(gè)移出請(qǐng)求(例如,適配卡目前正被關(guān)鍵系統(tǒng)功能所使用)。另外,如果請(qǐng)求沒有被確認(rèn),軟件將拒絕請(qǐng)求并對(duì)熱插拔控制器發(fā)出命令,重新開啟電源指示燈。規(guī)范建議軟件通過一條消息通知操作員請(qǐng)求被拒絕的原因。
4. 如果請(qǐng)求被確認(rèn),熱插拔服務(wù)程序?qū)⒚钸m配卡的設(shè)備驅(qū)動(dòng)保持靜默,也就是說驅(qū)動(dòng)一方面必須停止向適配卡發(fā)出請(qǐng)求,另一方面必須完成或者終止所有已經(jīng)發(fā)出的請(qǐng)求,并禁止適配卡產(chǎn)生新的事務(wù)(包括中斷)。
5. 軟件發(fā)出命令,通過在插槽所連接的根端口或交換端口中的鏈接控制寄存器禁掉適配卡的鏈接。這使得鏈接兩側(cè)的端口均被禁止。
6. 軟件指示熱插拔控制器禁掉插槽。
7. 成功切斷電源后,軟件發(fā)出關(guān)閉電源指示燈命令。指示燈熄滅后,操作員可以開始安全地從插槽移出適配卡。打開插槽安全鎖,熱插拔控制器從插槽上撤除所有的信號(hào)(例如SMBus 和Vaux),此時(shí)卡可以被移出。
8. 操作系統(tǒng)釋放內(nèi)存空間,I/O空間,中斷線等曾經(jīng)屬于該設(shè)備的系統(tǒng)資源。
如果在最近一次編譯中選擇了PCI Express 熱插拔功能,而且驅(qū)動(dòng)是以模塊方式存在,那么,可以在命令行下鍵入以下內(nèi)容:
modprobe pciehp
如果驅(qū)動(dòng)成功,則可以在/sys/bus/pci/slots/下面發(fā)現(xiàn)以可熱插拔插槽編號(hào)命名的目錄,進(jìn)入相關(guān)目錄,可以進(jìn)行下一步操作。
echo 1 >power開啟某個(gè)插槽上的電源 ,進(jìn)行熱插入
echo 0 >power關(guān)閉某個(gè)插槽上的電源,執(zhí)行熱拔出
若不能加載pciehp驅(qū)動(dòng),一般是由于硬件不支持或者固件缺少OSHP方法或ACPI _OSC方法之一。
六 linux2.6.12中PCIE驅(qū)動(dòng)模型的變化
在linux2.6.10中,Linux驅(qū)動(dòng)程序模型要求物理設(shè)備被單獨(dú)的驅(qū)動(dòng)程序獨(dú)占訪問。 PCI Express端口是一個(gè)擁有許多獨(dú)立功能的PCI-PCI橋設(shè)備,作為一個(gè)簡(jiǎn)潔的方案,每個(gè)功能要分別實(shí)現(xiàn)其自己的驅(qū)動(dòng)程序,但是這樣造成了多個(gè)驅(qū)動(dòng)程序在唯一的PCI-PCI橋設(shè)備中出現(xiàn)競(jìng)爭(zhēng)的狀況。也就是說,雖然PCI Express提供了如Power Management (PME)、 Advanced Error Reporting (AER)、 Hot-Plug (HP) 和Virtual Channel (VC) access等多種功能,但是如果某個(gè)PCI Express端口的native hotplug 驅(qū)動(dòng)程序加載后,它就會(huì)獨(dú)占這個(gè)PCI-PCI橋的端口,內(nèi)核就不能再于其上加載其他功能的驅(qū)動(dòng)程序了。為解決這個(gè)問題,在Linux內(nèi)核版本2.6.12中,PCI Express的熱插拔又有所改變,提出了PCI Express 端口總線驅(qū)動(dòng)程序(PCI Express Port Bus Driver)的概念。
在實(shí)現(xiàn)上,PCI Express 端口總線驅(qū)動(dòng)程序管理主板上的所有PCI Express 端口,并且把所有提供的服務(wù)請(qǐng)求發(fā)送到對(duì)應(yīng)的服務(wù)驅(qū)動(dòng)程序上。其優(yōu)點(diǎn)概括如下:
- 允許多個(gè)服務(wù)驅(qū)動(dòng)程序在一個(gè)PCI-PCI橋端口設(shè)備上同步運(yùn)行。
- 允許各個(gè)服務(wù)驅(qū)動(dòng)程序相互不受干擾地獨(dú)立實(shí)現(xiàn)。
- 允許一個(gè)服務(wù)驅(qū)動(dòng)程序在多個(gè)PCI-PCI橋端口設(shè)備上運(yùn)行。
- 管理PCI-PCI橋端口設(shè)備資源并分發(fā)它們到發(fā)出請(qǐng)求的服務(wù)驅(qū)動(dòng)程序。
例如,在注冊(cè)熱插拔驅(qū)動(dòng)程序時(shí),不再使用pci_register_driver直接向系統(tǒng)注冊(cè),而是使用int pcie_port_service_register(struct pcie_port_service_driver *new) 向端口總線驅(qū)動(dòng)程序注冊(cè);在注銷熱插拔驅(qū)動(dòng)程序時(shí),不再使用pci_unregister_driver直接向系統(tǒng)注銷,而是使用void pcie_port_service_unregister(struct pcie_port_service_driver *new)來向端口總線驅(qū)動(dòng)程序注銷;而端口總線驅(qū)動(dòng)程序是直接注冊(cè)注銷到系統(tǒng)的。
目前,PCI Express 端口總線驅(qū)動(dòng)程序模型還在發(fā)展演變中。
根據(jù)可用性理論,系統(tǒng)的可用度可以使用下列公式來計(jì)算:
在高負(fù)荷運(yùn)轉(zhuǎn)的服務(wù)器上,商用可靠性的元件的故障更換并非小概率事件,對(duì)于更換故障元件和升級(jí)配件這樣的事件,沒有熱插拔支持的系統(tǒng)必須停機(jī)斷電進(jìn)行處理,而具備熱插拔支持的系統(tǒng)則僅僅需要很少的軟件切換和拔插時(shí)間開銷。從上面的公式我們可以看出,MTTR值越小,系統(tǒng)的可用性就越高。我們定義了一個(gè)比例因子V,V= MTTRnoHP/MTTRHP,根據(jù)業(yè)界經(jīng)驗(yàn),V的取值一般在10-100之間。描述可用性的一種常用的方法是使用"9"。如三個(gè) 9 表示 99.9% 的可用性,它表示一年大約有 8.5 小時(shí)的服務(wù)中斷期。四個(gè) 9 (99.99%) 是更高一級(jí)的可用性,表示一年大約有 1 小時(shí)的服務(wù)中斷期。根據(jù)可用度公式計(jì)算,在單個(gè)元件可靠度不變的情況下,Linux操作系統(tǒng)對(duì)PCI Express熱插拔的支持,可以使服務(wù)器系統(tǒng)的外設(shè)相關(guān)可用性躍升一個(gè)等級(jí)(1個(gè)9),同時(shí),PCI Express熱插拔技術(shù)使得在線更換和升級(jí)PCI Express外設(shè)板卡成為可能,這使系統(tǒng)獲得了良好的可擴(kuò)展性。
綜上所述,本文討論了PCI Express熱插拔所涉及的軟件因素,分析了linux2.6.10的PCI Express插槽熱插拔功能PCIEHP子系統(tǒng),并對(duì)熱插拔支持在提高服務(wù)器系統(tǒng)外設(shè)相關(guān)可用性的作用進(jìn)行了定量的分析。為了繼續(xù)提高操作系統(tǒng)可用性和可擴(kuò)展性支持能力,Linux PCI Express hotplug以下方面還有待發(fā)展:繼續(xù)完善熱插拔架構(gòu)的開放性,以提供完整統(tǒng)一的接口供驅(qū)動(dòng)開發(fā)人員編寫其他設(shè)備的熱插拔支持模塊;在插槽熱插拔之外,提供對(duì)Server IO Module(SIOM)熱插拔的支持;完善熱替換和熱升級(jí)技術(shù)。這些問題都是非常具有挑戰(zhàn)性的。