iscsi是什么,iscsi:IO操作流程(二)

 2023-10-05 阅读 16 评论 0

摘要:上次我們討論了iscsi initiator IO操作需要經過的各個層次,以及每層所涉及的IO數據結構的變化。今天主要討論IO如何形成SCSI指令并下發的。 我們知道在通用塊層,IO最終放在request_queue中暫時保存。為了減少尋道時間,通用塊層采用“蓄流”的方式,

上次我們討論了iscsi initiator IO操作需要經過的各個層次,以及每層所涉及的IO數據結構的變化。今天主要討論IO如何形成SCSI指令并下發的。
我們知道在通用塊層,IO最終放在request_queue中暫時保存。為了減少尋道時間,通用塊層采用“蓄流”的方式,將IO蓄到隊列里一段時間,使接下來的IO有機會與已經存在的IO進行合并,即所謂的電梯算法。
當在一定合適的時機,“蓄流”打開時,IO繼續進行向下層傳遞,進而調用塊設備驅動為其請求隊列提供的request_fn進行處理。SCSI磁盤驅動在掃描發現并為磁盤分配請求隊列時將期實例化。具體指向為scsi_request_fn函數。
下圖展現了scsi_request_fn的調用情況。


這里寫圖片描述

iscsi是什么?

該函數調用blk_peek_request中逐個取出請求,并進行處理。每次調用它就從可處理的請求隊列中獲到一個請求。請求有可能是新的,有可能是由于SCSI子系統的底層相關設備不能處理請求再次加入到請求隊列的。如果已經處理過意味著scsi cmd已經申請;如果是新的,調用prep_rq_fn進行一些請求處理前的準備工作。scsi磁盤驅動(以下簡稱sd)在這個時間進行SCSI命令的構建(SCSI CDB)。這個預期可能有三種返回值:


- BLKPREP_OK:表示命令初期準備成功,則繼續往下執行
- BLKPREP_DEFER:暫時不能夠進行處理,則將請求再次加入到隊列
- BLKPREP_KILL:請求沒辦法繼續進行下去。這時從隊列中請下請求,向上層報告IO失敗。

iscsi發起程序,
接下來做三件事:


首先,進行設備的檢查工作。
當前的計算機體系結構中,設備的操作明顯慢于CPU的計算能力,是現代計算機的關鍵瓶頸之一。因而,在真正需要設備進行處理之前,盡可能的完善檢查IO處理的條件是否已經滿足。總之,針對IO層面的錯誤處理,要么盡可能多的恢復現場使業務能夠正常的運行下去,要么盡可能的早的失敗,給予應用進行錯誤處理的機會。此次的檢查主要針對后者,主要是硬件層面的檢查,具體點是對設備狀態、目標端、主機適配器等。
設備不能接收IO的情況分為三種分別為設備離線、設備正忙于處理其他事務、設備被阻塞住:
- 設備離線時,當設備再次上線時,需要重新初始化、重新建立請求隊列,當前的狀態和數據不需要處理,因而IO請求不需要進行處理了,直接放棄掉并向上層返回錯誤信息。
- 設備忙和設備阻塞住意味著設備臨時不能處理IO請求,一旦狀態移除,IO將會被繼續處理,此時系統將IO請求放置到延遲隊列中,擇機進行處理。
- 如果設備的狀態沒有問題,而設備所屬的目標端(target)、適配器(host)存在問題時,可以確保是臨時不能處理IO,此時將IO再次加入到設備的請求隊列中,下次再次進行重試。


其次,初始化錯誤處理。
scsi_init_cmd_errh用于命令執行出錯處理的初始化,設置超時定時器(新內核中的出理只進行了一些初始化的工作,超時沒有進行設置)。

iscsi發起程序連接不上。
最后,將scsi命令轉交到底層設備(LLD Low Level Device)進行處理。
scsi_dispatch_cmd用于將命令傳遞到底層設備驅動進行處理,一旦這個操作完成,意味著IO請求已經穿過SCSI中間層,傳遞到SCSI transport layer層。如果該返回0表示分發成功,否則表示出現錯誤,不再繼續處理請求隊列中的其它請求。
這個函數的主要工作如下:
- 檢查設備是否已經被刪除。如果被刪除,不需要下發了,直接返回錯即可。
- 檢查設備是否被阻塞住。
- 檢查命令的長度是否已經超過設備可以接受的長度。
- 調用主要適配器的queuecommand命令將數據進行分發

如果SCSI指令沒有被分發,最終可能因為:
- SCSI_MLQUEUE_DEVICE_BUSY:設備臨時被阻塞。
- SCSI_MLQUEUE_TARGET_BUSY:目標被阻塞
- SCSI_MLQUEUE_HOST_BUSY:主機適配器被阻塞
在上述三種情況下,將IO請求再次放入隊列,以便下次重試。

到此為止,SCSI層面的處理完成。
總結一下,IO請求將視系統當前的情況進行處理,最終都會執行結果分為三種情況:
當設備因故障移除時,直接向上層報告IO錯誤;當SCSI子系統的相關設備(適配器、目標節點、設備)暫時不能處理IO,加入到隊列后期處理;當設備正常時,回調適配器的queuecommand進一步進行處理。

iscsi配置,
返回來,我們再討論一下SCSI命令初始化的問題。
上面說明,系統根據請求信息準備scsi指令在prep_rq_fn中進行。sd初始化時,將其初始化為scsi_prep_fnscsi_prep_fn調用scsi_setup_cmnd進行scsi命令的初始化工作。scsi子系統的請求兩個層次(1)上層用請求(內核中稱做來源于文件系統的請求,這種說法不是特別的準確);(2)來源于scsi中間層的請求。前者需要構建SCSI命令,后者SCSI命令已經構建完成。

來自上層應用的請求需要構建成哪些指令,如何構建指令與設備類型有很大關系:來源于磁盤的IO請求需要構建的命令與塊操作相關,遵循SCSI SPC標準,而來源于磁帶的IO請求需要遵循相關的標準。下圖表達了SCSI協議族各協議之間的關系。一般地,設備類型特定的命令集由具體設備驅動來實現。此時,將調用scsi上層驅動的init_cmd實現。對于磁盤設備,設備加載時,將其指向函數:sd_init_command
綜上sd設備來源于上層應用請求的scsi命令構建調用關系為:
prep_rq_fn->scsi_prep_fn->scsi_setup_cmnd->scsi_setup_fs_cmnd->sd_init_command

個人感覺上層應用請求命令構建的時機找的有點別扭。道理上,由sd來構建磁盤讀寫相關的命令是符合常理設計的。sd作為SCSI服務的上層驅動,主要通過設備類型與其他相同層次的上層驅動在功能進行區分。必然的,磁盤IO相關的SCSI指令的構建由sd來完成;磁盤帶IO相關的SCSI指令由st來完成…………但是時機選擇回調機制實現,而不是在請求下發之前就準備好,不確定是什么原因。

這里寫圖片描述

iscsi有什么用?sd_init_command函數實現如下,

static int sd_init_command(struct scsi_cmnd *cmd)
{struct request *rq = cmd->request;switch (req_op(rq)) {case REQ_OP_DISCARD:switch (scsi_disk(rq->rq_disk)->provisioning_mode) {case SD_LBP_UNMAP:return sd_setup_unmap_cmnd(cmd);case SD_LBP_WS16:return sd_setup_write_same16_cmnd(cmd, true);case SD_LBP_WS10:return sd_setup_write_same10_cmnd(cmd, true);case SD_LBP_ZERO:return sd_setup_write_same10_cmnd(cmd, false);default:return BLKPREP_INVALID;}case REQ_OP_WRITE_ZEROES:return sd_setup_write_zeroes_cmnd(cmd);case REQ_OP_WRITE_SAME:return sd_setup_write_same_cmnd(cmd);case REQ_OP_FLUSH:return sd_setup_flush_cmnd(cmd);case REQ_OP_READ:case REQ_OP_WRITE:return sd_setup_read_write_cmnd(cmd);case REQ_OP_ZONE_REPORT:return sd_zbc_setup_report_cmnd(cmd);case REQ_OP_ZONE_RESET:return sd_zbc_setup_reset_cmnd(cmd);default:BUG();}
}

它根據請求的操作的類型構建不同的scsi指令。
- REQ_OP_DISCARD:告訴塊設備放棄使用某指定的塊。這個請求是配合自動精簡配置使用。這個功能對應SCSI指令的UNMAP命令。當此操作沒調用時,target不再為相應 LBA映射存儲空間;
- REQ_OP_WRITE_ZEROES:對指定的1個或者多個扇區寫0
- REQ_OP_WRITE_SAME: 將1個或者多個扇區寫成相同的數據。
- REQ_OP_FLUSH:通知target同步緩存數據
- REQ_OP_READ:讀操作
- REQ_OP_WRITE:寫操作
- REQ_OP_ZONE_REPORT REQ_OP_ZONE_RESET:應用于瓦式存儲的特殊操作
請寫請求將sd_setup_read_write_cmnd函數構建相應的scsi命令結構。由于協議不斷的發展,SCSI標準中有4種不同規格的寫讀操作稱作:read(10)、read(12)、read(16)、read(32)和write(10)、write(12)、write(16)、write(32)。括號中的數字與命令描述塊的大小,如read(10)的命令描述符為10個字節。10、12、16這三種的命令的區別是表求LBA所占用的空間不同,分別為4、6、8個字節。read(32)與write(32)主要是用于端到端T10 type2數據驗證時使用。
理解了上述區別就比較好理解操作的構建過程了。
- 如果需要支持T10 type2數據驗證,使用read(32)、write(32)
- 否則根據硬盤的最大扇區數分析,盡可能使用最小的命令描述塊(CDB)

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/114749.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息