sas控制器驅動結構粗探--基於3.10.0-693.25.4
部門測試環境最近出了個核心core,是宕機在了mpt3sas這個模組,以前沒見過這個模組,怎麼查這個core呢?以前沒見過,現在見見就好了;)。這個模組是sas控制器的驅動,在之前的IO棧研究中,只瞭解到過通用塊層,scsi往下的就沒接觸了,也正好趁此解這個bug的機會了解下IO棧scsi以下是什麼。
從config描述可以看到,mpt3sas是SAS控制器驅動,特殊點在於它是基於Fusion-MPT 架構的。
config SCSI_MPT3SAS tristate "LSI MPT Fusion SAS 3.0 Device Driver" depends on PCI && SCSI select SCSI_SAS_ATTRS select RAID_ATTRS ---help--- This driver supports PCI-Express SAS 12Gb/s Host Adapters.
Fusion-MPT是什麼呢?先看網路上的一段描述
Fusion-MPT 技術由 LSI Logic 開發,旨在為客戶提供更為容易的實現 SCSI 和 Fibre Channel 的解決方案。
Fusion-MPT 技術主要包括 Fusion-MPT 韌體,SAS、U320 SCSI、Fibre Channel硬核,和作業系統級的驅動程式等部分。
Fusion-MPT 架構中使用統一的韌體及驅動來支援所有基於 Fusion-MPT 技術的 I/O 控制器。
Fusion-MPT 架構可分為作業系統層和硬體層兩部分,而從驅動程式設計的角度,又可進一步將其分為驅動、韌體和硬體三個功能層次。
Fusion-MPT 在硬體層之上構建獨有的韌體層,不同的韌體為上層驅動程式提供對 SCSI 或 FC 的支援,以及高階的整合 RAID 等功能。韌體層有效地將驅動程式同硬體隔離,對上層驅動程式提供統一的 MPI ( Message Passing Interface )介面,使同一驅動程式可以應用於不同的底層硬體系統,有助於加速應用開發。驅動層對上層作業系統提供功能函式介面,通過 MPI 訪問韌體層,實現作業系統對硬體的訪問,並且按照通訊協議實現相關的幀封裝和拆解。訊息傳遞介面 MPI 提供了一個訊息傳遞傳輸架構,它定義了主機與 LSI Fusion-MPT 晶片組通訊的介面。
上述描述的很明白了,概括來說,Fusion-MPT是個好東西,它在硬體上提供了一個韌體,上層驅動程式可以用MPI介面訪問韌體,而韌體可以接不同的硬體,這樣簡化了作業系統對不通硬體的訪問。而mpt3sas這個驅動,是用了MPT韌體,下面接的是sas介面,我們看下這個裝置在系統中的位置,LSISAS 1068E就是用mpt3sas的一個裝置
依據資料簡單分析下mpt3sas驅動的結構,
static int __init
_mpt3sas_init(void)
{
int error;
#ifdef MPT2SAS_SCSI
pr_info("%s version %s loaded\n" , MPT2SAS_DRIVER_NAME,
MPT2SAS_DRIVER_VERSION);
#else
pr_info("%s version %s loaded\n", MPT3SAS_DRIVER_NAME,
MPT3SAS_DRIVER_VERSION);
#endif /* MPT2SAS_SCSI */
mpt3sas_transport_template = //設定template
sas_attach_transport(&mpt3sas_transport_functions);
if (!mpt3sas_transport_template)
return -ENODEV;
#ifdef MPT2SAS_SCSI
mpt2sas_raid_template = raid_class_attach(&mpt2sas_raid_functions);
if (!mpt2sas_raid_template) {
sas_release_transport(mpt3sas_transport_template);
return -ENODEV;
}
#else
mpt3sas_raid_template = raid_class_attach(&mpt3sas_raid_functions);
if (!mpt3sas_raid_template) { //設定template
sas_release_transport(mpt3sas_transport_template);
return -ENODEV;
}
#endif /* MPT2SAS_SCSI */
error = scsih_init(); //註冊一系列回撥函式
if (error) {
scsih_exit();
return error;
}
#ifdef MPT2SAS_SCSI
mpt3sas_ctl_init(1);
#else
mpt3sas_ctl_init(2); //註冊/dev/mpt3clt這個裝置
#endif /* MPT2SAS_SCSI */
error = pci_register_driver(&mpt3sas_driver); //註冊一個pci裝置
if (error)
scsih_exit();
return error;
}
static int
scsih_init(void)
{
mpt2_ids = 0;
mpt3_ids = 0;
mpt3sas_base_initialize_callback_handler();
/* queuecommand callback hander */
scsi_io_cb_idx = mpt3sas_base_register_callback_handler(_scsih_io_done);
/* task management callback handler */
tm_cb_idx = mpt3sas_base_register_callback_handler(_scsih_tm_done);
/* base internal commands callback handler */
base_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_base_done);
port_enable_cb_idx = mpt3sas_base_register_callback_handler(
mpt3sas_port_enable_done);
/* transport internal commands callback handler */
transport_cb_idx = mpt3sas_base_register_callback_handler(
mpt3sas_transport_done);
/* scsih internal commands callback handler */
scsih_cb_idx = mpt3sas_base_register_callback_handler(_scsih_done);
/* configuration page API internal commands callback handler */
config_cb_idx = mpt3sas_base_register_callback_handler(
mpt3sas_config_done);
/* ctl module callback handler */
ctl_cb_idx = mpt3sas_base_register_callback_handler(mpt3sas_ctl_done);
tm_tr_cb_idx = mpt3sas_base_register_callback_handler(
_scsih_tm_tr_complete);
tm_tr_volume_cb_idx = mpt3sas_base_register_callback_handler(
_scsih_tm_volume_tr_complete);
tm_sas_control_cb_idx = mpt3sas_base_register_callback_handler(
_scsih_sas_control_complete);
return 0;
}
我們不詳細去分析mpt3sas_transport_template、mpt3sas_raid_template和scsih_init到底幹了啥,因為我們的目的是瞭解mpt3sas這個驅動的結構,有個大概的框架,以後需要深入接觸的時候再仔細分析。我們重點關注mpt3sas註冊的pci裝置。
PCI 的註冊就是將 PCI 驅動程式掛載到其所在的匯流排的 drivers 鏈,同時掃描 PCI 裝置,將它能夠進行驅動的裝置掛載到 driver 上的 devices 連結串列上並對其呼叫probe函式進行。mpt3sas的pci probe函式是_scsih_probe
static int
_scsih_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct MPT3SAS_ADAPTER *ioc; //建立並初始化MPT3SAS_ADAPTER
...
/* Use mpt3sas driver host template for SAS 3.0 HBA's */
//申請一個scsi host
shost = scsi_host_alloc(&mpt3sas_driver_template,
sizeof(struct MPT3SAS_ADAPTER));
if (!shost)
return -ENODEV;
ioc = shost_priv(shost);
...
ioc->is_driver_loading = 1;
if ((mpt3sas_base_attach(ioc))) { // 初始化控制器,分配資源
pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
rv = -ENODEV;
goto out_attach_fail;
}
...
rv = scsi_add_host(shost, &pdev->dev); //新增到scsi middle level
if (rv) {
pr_err(MPT3SAS_FMT "failure at %s:%d/%s()!\n",
ioc->name, __FILE__, __LINE__, __func__);
goto out_add_shost_fail;
}
...
scsi_scan_host(shost); //掃描所有scsi匯流排
return 0;
...
}
控制器驅動是這樣的套路,先分配資源初始化控制器,然後建立個scsi host新增到系統,然後掃描scsi 匯流排的裝置建立一個個target連線到系統中,此時IO棧看到的就是scsi裝置,再往上就是通用塊層。IO到了scsi層,怎麼再和下面的硬體進行通訊的就在這個驅動裡實現,但我們不需要關心那麼多。
補充來自網上的資料
在 SCSI middle level 定義了 Scsi host、Scsi target、Scsi device 等資料結構。Scsi
host 描述了一個 scsi 匯流排控制器,在很多實際的系統中,scsi host 為一塊基於 PCI 匯流排的 HBA 或一個 SCSI
控制器晶片。每個 Scsi host 中可以存在多個 channel,一個 channel 實際擴充套件了一條 scsi 匯流排。每個
channel 可以 連線多個 scsi 節點,具體連線的數量與 scsi 匯流排帶載能力有關。Scsi target 對 scsi 總線上的
scsi node 進行了抽象。每個 Scsi target可能擁有多個 lun,即多個 Scsi device。Scsi device
用於描述一個 scsi 的具體功能單元,其在 scsi host 中通過channel、id、lun 進行定址。需要注意的是 Scsi
device 的作用是擴充了 Scsi target 的描述裝置能力,不代表任何實體屬性。在實際環境裡, Scsi target
對應一個物理磁碟或 Raid卷,Scsi device 對應一個邏輯單元 lun。
以上就是mpt3sas這個控制器驅動的大體結構。
我遇到的宕機問題是/dev/mpt3sas ioctl的處理過程中控制器資源已經被釋放了,導致記憶體非法訪問,這個一般是併發導致的,和https://patchwork.kernel.org/patch/10196101/這個patch描述的問題很像,但不是同一個問題。具體原因還在分析中。
參考: