日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

Linux IO系统分析(scsi篇)

發布時間:2024/4/24 综合教程 72 生活家
生活随笔 收集整理的這篇文章主要介紹了 Linux IO系统分析(scsi篇) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、概述

  Linux內核中SCSI子系統由SCSI上層,中間層,底層驅動模塊三部分組成,負責管理SCSI資源和處理其他子系統,如文件系統,提交到SCSI子系統中的IO請求。

因此,理解SCSI子系統的IO處理機制對理解整個SCSI子系統至關重要,同時也有助于理解整個Linux內核的IO處理機制。

二、SCSI

  SCSI設備訪問請求的提交分為兩個步驟:

用戶空間提交請求到通用塊層
通用塊層提交塊請求到SCSI子系統

  用戶空間提交請求到同樣塊層:

    在Linux用戶空間,有三種方式提交對SCSI設備請求到通用塊層:

通過文件系統(fs)提供的文件訪問接口進行訪問。  對建立在 SCSI 設備上的 Linux 文件系統中的文件讀寫操作,就屬于這種訪問方式; 
RAW設備訪問方式。 常見的應用就是 dd 命令, RAW設備訪問方式 和 通過文件系統提供的文件訪問接口進行訪問的最大區別在于前者對SCSI設備直接進行線性地址訪問, 不需要由文件系統進行地址映射;
SCSI PASSTHROUGH 方式。 通過 LINUX 提供的 SG 進行訪問,就屬于這種方式,用戶可以直接發 CDB[2] 命令給 SCSI 設備。所以,通過該接口,用戶可以做一些 SCSI 管理操作,如 SES 管理等。

 LINUX 內核處理三種訪問請求的方式

  經由文件系統或RAW設備方式提交的請求,會通過底層塊設備訪問層(ll_rw_block()),生成塊IO請求(bio),并提交給通用塊層 [3] ;而通過 SG 接口提交的訪問請求,會調用 SCSI 中間層提供的接口,將請求直接交由通用塊層進行處理

通用塊層提交塊訪問請求到 SCSI 子系統

  為什么要通過通用塊層呢?這是因為首先通用塊層會根據磁盤訪問的特性對請求進行優化操作;其次,通用塊層提供了調度功能,能夠對請求進行調度;再次,通用塊層可擴展的結構,使各種設備的塊驅動都能比較容易的和其集成。

當請求提交到通用塊層后,通用塊層需要完成準備,調度并交付塊訪問請求給 SCSI 中間層的操作。塊訪問請求可以理解為描述了塊訪問區域,訪問方式和關聯的 BIO 的請求,在內核中用 'struct request'結構表示。塊設備會有對應的塊訪問請求設備隊列,用于記錄需要該設備處理的訪問請求,新生成的塊訪問請求會被加入到對應設備的塊訪問請求隊列中。 SCSI 子系統對 IO 的處理,實際上是處理塊訪問請求隊列上的塊訪問請求。

  通用塊層提供了兩種方式調度處理塊訪問請求隊列:

直接調度
通過 LINUX 內核工作隊列機制調度執行。

  兩種方式,最后都會調用塊訪問請求隊列處理函數進行處理,而 SCSI 設備在初始化時會向通用塊層注冊 SCSI 子系統定義的塊訪問請求隊列處理函數。清單 1[4] 顯示了這個過程。這樣當通用塊層處理 SCSI 設備的塊訪問請求隊列時,調用的就是 SCSI 中間層定義的這些處理函數。通過這種方式,通用塊層就將塊訪問請求的處理交給了 SCSI 子系統。

 1 struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) 
 2  {   ……
 3     q = blk_init_queue(scsi_request_fn, NULL);
 4      //request generate block layer allocate a request queue 
 5     ……
 6     blk_queue_prep_rq(q, scsi_prep_fn); //Prepare a scsi request 
 7     blk_queue_max_hw_segments(q, shost->sg_tablesize); 
 8     //define sg table size 
 9     ……
10     blk_queue_softirq_done(q, scsi_softirq_done); 
11  }

SCSI 子系統處理塊訪問請求

   當 SCSI 子系統的請求隊列處理函數被通用塊層調用后,SCSI 中間層會根據塊訪問請求的內容,生成、初始并提交 SCSI 命令 (struct scsi_cmd) 到 SCSI TARGET 端

SCSI命令初始化和提交:

  SCSI 命令記錄了命令描述塊 (CDB),感測數據緩存 (SENSE BUFFER),IO 超時時間等 SCSI 相關的信息和 SCSI 子系統處理命令需要的一些其他信息,如回調函數等。清單 2 顯示了這個命令的主要結構。

 1 struct scsi_cmnd { 
 2     ……
 3     void (*done) (struct scsi_cmnd *);      /* Mid-level done function */ 
 4     ……
 5     int retries;                /*retried time*/ 
 6     int timeout_per_command;   /*timeout define*/ 
 7     ……
 8     enum dma_data_direction sc_data_direction;  /*data transfer direction*/ 
 9     ……
10     unsigned char cmnd[MAX_COMMAND_SIZE];   /*cdb*/ 
11     void *request_buffer;          /* Actual requested buffer */ 
12     struct request *request;      /* The command we are working on */ 
13     ……
14     unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; 
15                                /* obtained by REQUEST SENSE when 
16                                * CHECK CONDITION is received on original 
17                                * command (auto-sense) */ 
18     /* Low-level done function - can be used by */
19     /*low-level driver to point  to completion function. */ 
20     void (*scsi_done) (struct scsi_cmnd *); 
21     ……
22  };

  初始化的過程首先按照電梯調度算法,從塊設備的請求隊列上取出一個塊訪問請求,根據塊訪問請求的信息,定義 SCSI 命令中數據傳輸的方向,長度和地址。其次,定義 CDB,SCSI 中間層的回調函數等。

  在完成初始化后,SCSI 中間層通過調用scsi_host_template[5]結構中定義queuecommand函數將 SCSI 命令提交給 SCSI 底層驅動部分。queuecommand函數,是一個 SCSI 命令隊列處理函數,在 SCSI 底層驅動中,定義了queuecommand函數的具體實現。因此,SCSI

中間層,調用queuecommand函數實際上就是調用了底層驅動定義的queuecommand函數的處理實體,將 SCSI 命令提交給了各個廠家定義的 SCSI 底層驅動進行處理。這個過程和通用塊設備層調用 SCSI 中間層的處理函數進行塊請求處理的機制很相似,這也體現了 LINUX

內核代碼具有很好的擴展性。底層驅動接受到請求后,就要開始處理 SCSI 命令了,這一層和硬件關系緊密,所以這塊代碼一般都是由各個廠家自己實現。基本流程可概括為:從底層驅動維護的隊列中,取出一個 SCSI 命令,封裝成廠家自定義的請求格式,然后采用

DMA 或者其他方式,將請求提交給 SCSI TARGET 端,由 SCSI TARGET 端對請求處理,并返回執行結果給 SCSI 底層驅動層。

SCSI 命令執行結果的處理

  當 SCSI 底層驅動接受到 SCSI TARGET 端返回的命令執行結果后,SCSI 子系統主要通過兩次回調過程完成對命令執行結果的處理。 SCSI 底層驅動在接受到 SCSI TARGET 端返回的命令執行結果后,會調用 SCSI 中間層定義的回調函數,將處理結果交付給 SCSI 中間層進行處理,這是第一次回調過程。 SCSI 中間層處理完成后,將調用 SCSI 上層定義的回調函數,結束 IO 在整個 SCSI 子系統中的處理,這為第二次回調過程。

第一次回調:

  SCSI 中間層在調用queuecommand函數將 SCSI 命令提交給 SCSI 底層驅動的同時,也將回調函數指針傳給了 SCSI 底層驅動。底層驅動接受到 SCSI TARGET 端返回的命令執行結果后,會調用該回調函數,產生一個中斷號為 BLOCK_SOFTIRQ 的軟中斷進行第一次回調處理。在這次回調處理過程中,SCSI 中間層首先會根據 SCSI 底層驅動處理的結果判斷請求處理是否成功。處理成功,并不意味著處理沒有錯誤,而是返回的信息,能夠讓 SCSI 中間層很明確的知道,對于這個命令,中間層已經沒有必要繼續進行處理了。所以,對于處理成功的 SCSI 命令,SCSI 中間層會調用第二次回調函數進入到第二次回調過程。清單 3 顯示了 SCSI 中間層定義的該軟中斷的處理函數。

 1 static void scsi_softirq_done(struct request *rq) 
 2  { 
 3     ……
 4     disposition = scsi_decide_disposition(cmd); 
 5     ……
 6     switch (disposition) { 
 7       case SUCCESS: 
 8         scsi_finish_command(cmd);   
 9         //enter to second callback process 
10         break; 
11       case NEEDS_RETRY: 
12         scsi_retry_command(cmd); 
13         break; 
14       case ADD_TO_MLQUEUE: 
15         scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); 
16          break; 
17        default: 
18          if (!scsi_eh_scmd_add(cmd, 0)) 
19             scsi_finish_command(cmd); 
20     } 
21  }

第二次回調:

  不同的 SCSI 上層模塊會定義自己不同的第二次回調函數,如 SD 模塊,會在sd_init_command函數中,定義自己的第二次回調函數sd_rw_intr,這個回調函數會根據 SD 模塊的需要,對 SCSI 命令執行的結果做進一步的處理。清單 4 顯示了 SD 模塊注冊第二次回調的代碼。雖然各個 SCSI 上層模塊可以定義自己的第二次回調函數,但是這些回調函數最終都會結束 SCSI 子系統對這個塊訪問請求的處理。

1 static int sd_init_command(struct scsi_cmnd * SCpnt) 
2  { 
3     ……
4     SCpnt->done = sd_rw_intr; 
5     return 1; 
6  }

總結

以上是生活随笔為你收集整理的Linux IO系统分析(scsi篇)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。