0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

基于PCIe-Native機(jī)制的熱插拔

openEuler ? 來源:openEuler ? 作者:openEuler ? 2022-09-06 10:32 ? 次閱讀

熱插拔即帶電插拔,在虛擬化場(chǎng)景下,熱插拔就是在虛擬機(jī)運(yùn)行過程中對(duì)磁盤網(wǎng)卡等設(shè)備進(jìn)行動(dòng)態(tài)調(diào)整。

常見的熱插拔機(jī)制有 ACPI 機(jī)制的熱插拔,PCIe-Native 機(jī)制的熱插拔。ACPI 機(jī)制的熱插拔依賴 ACPI 表,在 ACPI 表中會(huì)存放設(shè)備熱插拔相關(guān)的信息。PCIe-Native 機(jī)制的熱插拔是 PCI 規(guī)范中定義的,設(shè)備一般是熱插到 Root Port 設(shè)備上,Root Port 設(shè)備可以認(rèn)為是一個(gè)虛擬的橋設(shè)備,對(duì)應(yīng)一個(gè)插槽。Root Port 設(shè)備本身不支持熱插拔,因此需要在啟動(dòng)虛擬機(jī)前提前配置。

目前,StratoVirt 標(biāo)準(zhǔn)機(jī)型中實(shí)現(xiàn)了基于 PCIe-Native 機(jī)制的熱插拔。支持熱插拔的設(shè)備包括磁盤、網(wǎng)卡、PCI 直通設(shè)備。

熱插拔的整體流程如下:

3699a45a-2d1a-11ed-ba43-dac502259ad0.png

對(duì)于熱插主要分為兩步:

  1. 用戶通過 QMP 下發(fā) device_add 命令,StratoVirt 收到命令后會(huì)進(jìn)行設(shè)備的實(shí)例化,然后插入到對(duì)應(yīng)的 Root Port 設(shè)備上。
  2. Root Port 設(shè)備更新相關(guān)的寄存器配置,然后發(fā)送中斷通知虛擬機(jī)內(nèi)驅(qū)動(dòng)處理。

對(duì)于熱拔也可以分為兩步:

  1. 用戶通過 QMP 下發(fā) device_del 命令,StratoVirt 收到命令后,更新 Root Port 中的寄存器,然后發(fā)送中斷通知虛擬機(jī)內(nèi)驅(qū)動(dòng)處理。
  2. 虛擬機(jī)內(nèi)驅(qū)動(dòng)處理后會(huì)回寫寄存器,觸發(fā) StratoVirt 側(cè)銷毀相應(yīng)設(shè)備。

具體實(shí)現(xiàn)

在 StratoVirt 的 pci/src/hotplug.rs 文件中定義了熱插拔特性,其中 plug 函數(shù)對(duì)應(yīng)熱插操作,用于熱插設(shè)備。unplug_request 函數(shù)對(duì)應(yīng)熱拔操作,用于發(fā)起熱拔設(shè)備請(qǐng)求,這里只是通知虛擬機(jī)內(nèi)驅(qū)動(dòng)去處理熱拔請(qǐng)求,還未移除設(shè)備,可以理解為是一個(gè)異步請(qǐng)求。當(dāng)虛擬機(jī)內(nèi)驅(qū)動(dòng)處理完成后,寫寄存器觸發(fā)設(shè)備下線后,會(huì)回調(diào) unplug 函數(shù)用于銷毀設(shè)備。

pub trait HotplugOps: Send {    /// Plug device, usually called when hot plug device in device_add.    fn plug(&mut self, dev: &Arc>) -> Result<()>;
    /// Unplug device request, usually called when hot unplug device in device_del.    /// Only send unplug request to the guest OS, without actually removing the device.    fn unplug_request(&mut self, dev: &Arc>) -> Result<()>;
    /// Remove the device.    fn unplug(&mut self, dev: &Arc>) -> Result<()>;}

熱插實(shí)現(xiàn)

StratoVirt 里通過給 RootPort 實(shí)現(xiàn)了 HotplugOps 特性,使得 PCI 設(shè)備能夠熱插到 Root Port 設(shè)備上。

設(shè)備熱插的主要實(shí)現(xiàn)邏輯在 plug 函數(shù)里。首先獲取了設(shè)備的 devfn 號(hào),也就是 Device 號(hào)和 Function 號(hào),目前熱插只支持 Device 號(hào)和 Function 號(hào)都為 0 的設(shè)備。因此這里做了判斷。

然后會(huì)在 RootPort 設(shè)備的 PCI 配置空間中的 PCI Express Capability(PCI 配置空間和 PCI Express Capability 寄存器定義可以參考 PCI 規(guī)范)中設(shè)置 Slot 狀態(tài)寄存器和 Link 狀態(tài)寄存器,然后通過 hotplug_event_notify 函數(shù)發(fā)送中斷通知虛擬機(jī)。這里熱插設(shè)備主要是通過 Attention Button Pressed(對(duì)應(yīng) PCI_EXP_HP_EV_ABP)事件觸發(fā)的。

這里簡(jiǎn)單介紹下不同標(biāo)記位的含義。

符號(hào) 描述
PCI_EXP_SLTSTA Slot Status Register 表示 Slot 狀態(tài)寄存器,不同的位表示 Slot 不同的狀態(tài)
PCI_EXP_SLTSTA_PDS Presence Detect State 表示 Slot 上設(shè)備的在位狀態(tài),置 1 表示在位
PCI_EXP_HP_EV_PDC Presence Detect Changed 表示 Slot 上設(shè)備在位狀態(tài)是否發(fā)生變化
PCI_EXP_HP_EV_ABP Attention Button Pressed 表示 Attention 按鈕被按下,該按鈕用于觸發(fā)熱插拔操作
PCI_EXP_LNKSTA Link Status Register 表示 Link 狀態(tài)的寄存器
PCI_EXP_LNKSTA_DLLLA Data Link Layer Link Active 表示數(shù)據(jù)鏈路控制和管理狀態(tài),置 1 表示處于 Active 狀態(tài)
impl HotplugOps for RootPort {    fn plug(&mut self, dev: &Arc>) -> Result<()> {        let devfn = dev            .lock()            .unwrap()            .devfn()            .chain_err(|| "Failed to get devfn")?;        // Only if devfn is equal to 0, hot plugging is supported.        if devfn == 0 {            let offset = self.config.ext_cap_offset;            le_write_set_value_u16(                &mut self.config.config,                (offset + PCI_EXP_SLTSTA) as usize,                PCI_EXP_SLTSTA_PDS | PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP,            )?;            le_write_set_value_u16(                &mut self.config.config,                (offset + PCI_EXP_LNKSTA) as usize,                PCI_EXP_LNKSTA_NLW | PCI_EXP_LNKSTA_DLLLA,            )?;            self.hotplug_event_notify();        }        Ok(())    }}

在 hotplug_event_notify 函數(shù)中會(huì)調(diào)用 MSIX 中斷的 notify 函數(shù)發(fā)送中斷到虛擬機(jī)內(nèi),虛擬機(jī)內(nèi) pciehp 驅(qū)動(dòng)收到中斷后會(huì)處理相關(guān)的熱插請(qǐng)求。

fn hotplug_event_notify(&mut self) {    if let Some(msix) = self.config.msix.as_mut() {        msix.lock()            .unwrap()            .notify(0, self.dev_id.load(Ordering::Acquire));    } else {        error!("Failed to send interrupt: msix does not exist");    }}

熱拔實(shí)現(xiàn)

對(duì)于設(shè)備熱拔請(qǐng)求的邏輯主要在 unplug_request 函數(shù),該函數(shù)負(fù)責(zé)更新寄存器,并且通過調(diào)用 hotplug_event_notify 函數(shù)發(fā)送中斷通知虛擬機(jī)內(nèi)驅(qū)動(dòng)處理設(shè)備熱拔請(qǐng)求。

unplug_request 函數(shù)里主要是清零了 Link 狀態(tài)寄存器中的 PCI_EXP_LNKSTA_DLLLA 標(biāo)記位,并且在 Slot 狀態(tài)寄存器中的設(shè)置了 PCI_EXP_HP_EV_ABP 標(biāo)記位。從這里也可以發(fā)現(xiàn),其實(shí)無論是熱插請(qǐng)求還是熱拔請(qǐng)求,都是通過 Attention Button Pressed(對(duì)應(yīng) PCI_EXP_HP_EV_ABP)事件觸發(fā)的,虛擬機(jī)內(nèi)驅(qū)動(dòng)會(huì)根據(jù)設(shè)備的在位狀態(tài)來判斷是熱插請(qǐng)求還是熱拔請(qǐng)求。

impl HotplugOps for RootPort {    fn unplug_request(&mut self, dev: &Arc>) -> Result<()> {        let devfn = dev            .lock()            .unwrap()            .devfn()            .chain_err(|| "Failed to get devfn")?;        if devfn != 0 {            return self.unplug(dev);        }
        let offset = self.config.ext_cap_offset;        le_write_clear_value_u16(            &mut self.config.config,            (offset + PCI_EXP_LNKSTA) as usize,            PCI_EXP_LNKSTA_DLLLA,        )?;
        let mut slot_status = PCI_EXP_HP_EV_ABP;        if let Some(&true) = FAST_UNPLUG_FEATURE.get() {            slot_status |= PCI_EXP_HP_EV_PDC;        }        le_write_set_value_u16(            &mut self.config.config,            (offset + PCI_EXP_SLTSTA) as usize,            slot_status,        )?;        self.hotplug_event_notify();        Ok(())    }}

對(duì)于熱拔設(shè)備,StratoVirt 側(cè)在更新寄存器發(fā)送中斷通知虛擬機(jī)內(nèi)驅(qū)動(dòng)后,實(shí)際上還沒有真正的移除設(shè)備,而是等到虛擬機(jī)內(nèi)驅(qū)動(dòng)處理后回寫寄存器通知 StratoVirt 側(cè)下線設(shè)備后,才會(huì)真正銷毀設(shè)備。

虛擬機(jī)內(nèi)驅(qū)動(dòng)寫 Root Port 寄存器會(huì)調(diào)用到 write_config 函數(shù),在 write_config 函數(shù)里會(huì)調(diào)用 do_unplug 函數(shù)來處理熱拔設(shè)備相關(guān)的邏輯。

    fn write_config(&mut self, offset: usize, data: &[u8]) {        ...
        self.do_unplug(offset, end, old_ctl);    }

do_unplug 函數(shù)里首先保證了寫入的寄存器是 Slot Control 寄存器,否則直接返回,不做處理。然后判斷在設(shè)備當(dāng)前在位的情況下,寫入的寄存器標(biāo)記位為 PCI_EXP_SLTCTL_PWR_IND_OFF 和 PCI_EXP_SLTCTL_PCC 時(shí),并且這兩個(gè)標(biāo)記位發(fā)生了變化,也就是寫入之前的沒有這兩個(gè)標(biāo)記位,上述條件都滿足時(shí),會(huì)調(diào)用 remove_devices 函數(shù)開始真正銷毀設(shè)備。

符號(hào) 描述
PCI_EXP_SLTCTL_PCC Power Controller Control 表示電源管理狀態(tài),置 1 表示上電狀態(tài)
PCI_EXP_SLTCTL_PWR_IND_OFF Power Indicator off 表示是否允許移除設(shè)備,置 1 表示設(shè)備允許被移除
fn do_unplug(&mut self, offset: usize, end: usize, old_ctl: u16) {    let cap_offset = self.config.ext_cap_offset;    // Only care the write config about slot control    if !ranges_overlap(        offset,        end,        (cap_offset + PCI_EXP_SLTCTL) as usize,        (cap_offset + PCI_EXP_SLTCTL + 2) as usize,    ) {        return;    }
    let status =        le_read_u16(&self.config.config, (cap_offset + PCI_EXP_SLTSTA) as usize).unwrap();    let val = le_read_u16(&self.config.config, offset).unwrap();    // Only unplug device when the slot is on    // Don't unplug when slot is off for guest OS overwrite the off status before slot on.    if (status & PCI_EXP_SLTSTA_PDS != 0)        && (val as u16 & PCI_EXP_SLTCTL_PCC == PCI_EXP_SLTCTL_PCC)        && (val as u16 & PCI_EXP_SLTCTL_PWR_IND_OFF == PCI_EXP_SLTCTL_PWR_IND_OFF)        && (old_ctl & PCI_EXP_SLTCTL_PCC != PCI_EXP_SLTCTL_PCC            || old_ctl & PCI_EXP_SLTCTL_PWR_IND_OFF != PCI_EXP_SLTCTL_PWR_IND_OFF)    {        self.remove_devices();
        if let Err(e) = self.update_register_status() {            error!("{}", e.display_chain());            error!("Failed to update register status");        }    }
    self.hotplug_command_completed();    self.hotplug_event_notify();}

在調(diào)用 remove_devices 函數(shù)移除設(shè)備之后,調(diào)用 update_register_status 函數(shù)更新寄存器的狀態(tài),主要是清理了 Link 狀態(tài)和設(shè)備在位狀態(tài),并且設(shè)置了 Presence Detect Changed(對(duì)應(yīng) PCI_EXP_HP_EV_PDC)標(biāo)記位表示設(shè)備在位狀態(tài)發(fā)生了變化。

/// Update register when the guest OS trigger the removal of the device.fn update_register_status(&mut self) -> Result<()> {    let cap_offset = self.config.ext_cap_offset;    le_write_clear_value_u16(        &mut self.config.config,        (cap_offset + PCI_EXP_SLTSTA) as usize,        PCI_EXP_SLTSTA_PDS,    )?;    le_write_clear_value_u16(        &mut self.config.config,        (cap_offset + PCI_EXP_LNKSTA) as usize,        PCI_EXP_LNKSTA_DLLLA,    )?;    le_write_set_value_u16(        &mut self.config.config,        (cap_offset + PCI_EXP_SLTSTA) as usize,        PCI_EXP_SLTSTA_PDC,    )?;    Ok(())}

在更新完寄存器后,在 hotplug_command_completed 還會(huì)設(shè)置 Command Completed(對(duì)應(yīng) PCI_EXP_HP_EV_CCI)表示命令處理完成,最后再發(fā)送中斷通知虛擬機(jī)內(nèi)驅(qū)動(dòng)。至此,整個(gè)設(shè)備熱拔流程就結(jié)束了。

fn hotplug_command_completed(&mut self) {    if let Err(e) = le_write_set_value_u16(        &mut self.config.config,        (self.config.ext_cap_offset + PCI_EXP_SLTSTA) as usize,        PCI_EXP_HP_EV_CCI,    ) {        error!("{}", e.display_chain());        error!("Failed to write command completed");    }}
符號(hào) 描述
PCI_EXP_HP_EV_CCI Command Completed 表示命令處理完成,可以處理下一條命令

總結(jié)

PCIe Native 機(jī)制的熱插拔主要是通過 Root Port 設(shè)備上的寄存器來表示不同狀態(tài),通過中斷來通知虛擬機(jī),從而實(shí)現(xiàn)了設(shè)備的熱插拔。

審核編輯:湯梓紅


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 熱插拔
    +關(guān)注

    關(guān)注

    2

    文章

    208

    瀏覽量

    36279
  • PCIe
    +關(guān)注

    關(guān)注

    15

    文章

    1165

    瀏覽量

    81968
  • 虛擬機(jī)
    +關(guān)注

    關(guān)注

    1

    文章

    888

    瀏覽量

    27811

原文標(biāo)題:StratoVirt 中的 PCI 設(shè)備熱插拔實(shí)現(xiàn)

文章出處:【微信號(hào):openEulercommunity,微信公眾號(hào):openEuler】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    高精度熱插拔和電源監(jiān)控

    本內(nèi)容提供了高精度熱插拔和電源監(jiān)控,介紹什么是熱插拔和利用熱插拔進(jìn)行的設(shè)計(jì)方案及電源監(jiān)控知識(shí)。
    發(fā)表于 11-04 10:00 ?1619次閱讀
    高精度<b class='flag-5'>熱插拔</b>和電源監(jiān)控

    熱插拔是什么?熱插拔有哪些特點(diǎn)?

    什么是熱插拔?熱插拔(hot-plugging或Hot Swap)即帶電插拔,熱插拔功能就是允許用戶在不關(guān)閉系統(tǒng),不切斷電源的情況下取出和更換損壞的硬盤、電源或板卡等部件,從而提高了系
    發(fā)表于 12-13 10:53

    即插即用和熱插拔的區(qū)別

    本帖最后由 eehome 于 2013-1-5 10:01 編輯 “即插即用”是指安裝了設(shè)備之后系統(tǒng)可以自動(dòng)配置和管理設(shè)備,不需要人工處理即可使用?!?b class='flag-5'>熱插拔”是指可以在開機(jī)狀態(tài)下將設(shè)備與主機(jī)
    發(fā)表于 10-23 10:26

    如何對(duì)BMS單元連接進(jìn)行熱插拔

    過渡到熱插拔測(cè)試,一些故障實(shí)際上是針對(duì)熱插拔失效機(jī)制的。觀察表明,2、3、4故障與熱插拔故障檢測(cè)有關(guān)。當(dāng)然,由于不同的熱插拔連接模式的出現(xiàn),
    發(fā)表于 09-07 18:20

    熱插拔PCI總線

    DN155- 熱插拔PCI總線
    發(fā)表于 05-28 12:21

    熱插拔CompactPCI總線

    DN200- 熱插拔CompactPCI總線
    發(fā)表于 07-17 06:32

    熱插拔的基本原理是什么?熱插拔有哪些功能?

    熱插拔的基本原理是什么?熱插拔有哪些功能?
    發(fā)表于 05-24 06:01

    使熱插拔與電子熔絲的優(yōu)勢(shì)

    使用熱插拔控制器的優(yōu)勢(shì)電子熔絲與熱插拔控制器之間的主要區(qū)別是熱插拔是一種能夠驅(qū)動(dòng)外部FET的控制器(如圖1所示)。FET通過熱插拔控制器中的控制邏輯進(jìn)行開啟和關(guān)閉,以調(diào)節(jié)負(fù)載處的電源供
    發(fā)表于 11-17 07:12

    熱插拔裝置軟件

    熱插拔裝置軟件USB Safely Remove是一款支持熱插拔裝置和迅速切斷一個(gè)公用的熱插拔裝置的軟件。
    發(fā)表于 04-23 09:32 ?151次下載

    熱插拔

    熱插拔              熱插拔(hot-plugging或Hot Swap)功能就是允許用戶在不關(guān)閉系統(tǒng),不切斷電源的情況下取出和更換
    發(fā)表于 12-17 11:41 ?609次閱讀

    PCIe總線的熱插拔機(jī)制

    當(dāng)然,熱插拔不僅僅是硬件的事,其需要軟硬件協(xié)同實(shí)現(xiàn)。要想實(shí)現(xiàn)熱插拔功能,操作系統(tǒng)、主板熱插拔驅(qū)動(dòng)器、PCIe卡設(shè)備驅(qū)動(dòng)以及PCIe卡硬件功能
    的頭像 發(fā)表于 09-06 09:20 ?2w次閱讀

    PCIe引腳PRSNT與熱插拔

    熱插拔的基本目的是要讓PCIe設(shè)備按照規(guī)定的順序、原則,從系統(tǒng)中移除或插入到系統(tǒng)中來,并能正常的工作,且不影響系統(tǒng)的正常運(yùn)行。事實(shí)上,PCIe熱插拔”的關(guān)鍵目的就是為前面面所提到的系
    的頭像 發(fā)表于 12-14 10:59 ?4511次閱讀

    熱插拔和非熱插拔的區(qū)別

    熱插拔和非熱插拔的區(qū)別? 熱插拔和非熱插拔是指電子設(shè)備或組件在工作狀態(tài)下是否可以進(jìn)行插拔操作的一種分類。
    的頭像 發(fā)表于 12-28 10:01 ?2348次閱讀

    熱插拔是什么原理

    熱插拔(Hot Swap)是一種允許在系統(tǒng)運(yùn)行過程中,動(dòng)態(tài)地插入或移除硬件設(shè)備的技術(shù)。這種技術(shù)在計(jì)算機(jī)硬件、通信設(shè)備和存儲(chǔ)設(shè)備等領(lǐng)域得到了廣泛應(yīng)用。熱插拔技術(shù)的目的是為了提高系統(tǒng)的可擴(kuò)展性、可靠性
    的頭像 發(fā)表于 01-16 11:03 ?3364次閱讀
    <b class='flag-5'>熱插拔</b>是什么原理

    鍵盤熱插拔和非熱插拔的區(qū)別

    鍵盤熱插拔和非熱插拔的區(qū)別 鍵盤是計(jì)算機(jī)外設(shè)設(shè)備之一,熱插拔是指在計(jì)算機(jī)運(yùn)行中插入或拔出設(shè)備而無需重啟計(jì)算機(jī),非熱插拔則需要重啟計(jì)算機(jī)才能生效。鍵盤
    的頭像 發(fā)表于 02-02 17:34 ?7564次閱讀