1. 程式人生 > >linux硬碟識別過程

linux硬碟識別過程

目錄

1. 硬碟啟動協議

2.SCSI匯流排掃描的方法

方法:

3. 核心列印資訊

硬碟開機.核心函式跟蹤列印資訊

資訊解讀

硬碟熱插拔.核心函式跟蹤列印資訊

資訊解讀

4. 硬碟識別過程

5. 硬碟識別過程程式碼呼叫


1. 硬碟啟動協議

2.SCSI匯流排掃描的方法

  • SCSI匯流排掃描是通過協議特定或者晶片特定的方法探測出掛接在主機介面卡後面的目標節點和邏輯單元,為它們在記憶體中構建相應的資料結構並把它們新增到系統中。

方法:

  • scsi中間層以可能的ID和LUN構造INQUIRY命令,之後將這些命令提交給I/O子系統後,通過SCSI上層磁碟驅動處理生成請求,後通過SCSI中間層將請求轉換成CDB,最後呼叫SCSI底層驅動的queuecommand回撥函式實現命令傳送。

 

3. 核心列印資訊

  • 硬碟開機.核心函式跟蹤列印資訊

    ata1.02: SATA link down (SStatus 0 SControl 310)

    ata1.03: hard resetting link

    ata1.03: SATA link down (SStatus 0 SControl 310)

    ata1.04: hard resetting link

    ata1.04: SATA link down (SStatus 0 SControl 

    310)

    ata1.00: ATA-9: WDC WD5000LUCT-63C26Y0, 01.01A01, max UDMA/133

    ata1.00976773168 sectors, multi 0: LBA48 NCQ (depth 31/32)

    ata1.00: configured for UDMA/133

    ata1: EH complete

    scsi 0:0:0:0: Direct-Access     ATA      WDC WD5000LUCT-6 01.0

     PQ: 0 ANSI: 5

    ------------[ cut here ]------------

    WARNING: at drivers/scsi/sd.c:2936 sd_probe+0x1c/0x378()

    Modules linked in:

    CPU: 2 PID: 6 Comm: kworker/u8:0 Not tainted 3.10.0_hi3536 #6

    Workqueue: events_unbound async_run_entry_fn

    [<80019e10>] (unwind_backtrace+0x0/0xf4) from [<80016ea4>] (show_stack+0x10/0x14)

    [<80016ea4>] (show_stack+0x10/0x14) from [<8002c278>] (warn_slowpath_common+0x54/0x6c)

    [<8002c278>] (warn_slowpath_common+0x54/0x6c) from [<8002c32c>] (warn_slowpath_null+0x1c/0x24)

    [<8002c32c>] (warn_slowpath_null+0x1c/0x24) from [<80338860>] (sd_probe+0x1c/0x378)

    [<80338860>] (sd_probe+0x1c/0x378) from [<8031a224>] (driver_probe_device+0x78/0x214)

    [<8031a224>] (driver_probe_device+0x78/0x214) from [<80318898>] (bus_for_each_drv+0x58/0x8c)

    [<80318898>] (bus_for_each_drv+0x58/0x8c) from [<8031a17c>] (device_attach+0x74/0x88)

    [<8031a17c>] (device_attach+0x74/0x88) from [<803197a0>] (bus_probe_device+0x84/0xa8)

    [<803197a0>] (bus_probe_device+0x84/0xa8) from [<80317efc>] (device_add+0x4ec/0x59c)

    [<80317efc>] (device_add+0x4ec/0x59c) from [<80331f34>] (scsi_sysfs_add_sdev+0x84/0x294)

    [<80331f34>] (scsi_sysfs_add_sdev+0x84/0x294) from [<803300d0>] (scsi_probe_and_add_lun+0x8bc/0x98c)

    [<803300d0>] (scsi_probe_and_add_lun+0x8bc/0x98c) from [<803304a0>] (__scsi_add_device+0xf4/0x104)

    [<803304a0>] (__scsi_add_device+0xf4/0x104) from [<8034ac5c>] (ata_scsi_scan_host+0xb0/0x234)

    [<8034ac5c>] (ata_scsi_scan_host+0xb0/0x234) from [<80050df4>] (async_run_entry_fn+0x48/0x184)

    [<80050df4>] (async_run_entry_fn+0x48/0x184) from [<800452c8>] (process_one_work+0x10c/0x370)

    [<800452c8>] (process_one_work+0x10c/0x370) from [<80045fd0>] (worker_thread+0x138/0x3fc)

    [<80045fd0>] (worker_thread+0x138/0x3fc) from [<8004b24c>] (kthread+0xb4/0xb8)

    [<8004b24c>] (kthread+0xb4/0xb8) from [<800130d8>] (ret_from_fork+0x14/0x3c)

    ---[ end trace 63dc1c18fe366fe2 ]---

    sd 0:0:0:0: Fix disk[0:0:0:0] to sde

    sd 0:0:0:0: [sde] 976773168 512-byte logical blocks: (500 GB/465 GiB)

    sd 0:0:0:0: Attached scsi generic sg0 type 0

    <ata_scsi_scan_host>:hd [0:0:0:0] change hd status to HD_STATUS_OK

    sd 0:0:0:0: [sde] 4096-byte physical blocks

    sd 0:0:0:0: [sde] Write Protect is off

    sd 0:0:0:0: [sde] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA

     sde: sde1 sde2 sde3 sde4

    sd 0:0:0:0: [sde] Attached SCSI disk

    ata2: SATA link down (SStatus 0 SControl 300)

    ata3: SATA link down (SStatus 0 SControl 300)

    ata4: SATA link down (SStatus 0 SControl 300)

  • 資訊解讀

    • 基本引數:

      • ata1.02表示的是ata的prot1,複用埠號2

      • max UDMA/133:最大讀取速度133MB/s

      • LBA48:指以48位邏輯定址的方式使用硬碟

      • NCQ:原生命令佇列技術是一種使硬碟內部優化工作負荷執行順序,通過對內部佇列中的命令進行重新排序實現智慧資料管理,改善硬碟因機械部件而受到的各種效能制約。

      • EH complete:error handler complete

    • 流程處理
      • 首先是ata卡硬體初始化與故障處理,並列印了硬碟的相關資訊
      • 之後開始掃描硬碟資訊,上面為掃描過程中的呼叫關係
      • 列印識別資訊
      • 依次掃描其他ata埠
    • dump_stack資訊
      • 通過工作佇列async_run_entry_fn掃描scsi的host,chanle,target,device
      • 最後通過sd_probe關聯到block_device,到通用塊層。

 

  • 硬碟熱插拔.核心函式跟蹤列印資訊

    硬碟斷電:

    ata1.00: exception Emask 0x10 SAct 0x0 SErr 0x10002 action 0xf

    ata1.00: SError: { RecovComm PHYRdyChg }

    ata1.00: hard resetting link

    ata1.00: SATA link down (SStatus 0 SControl 310)

    ata1.00: hard resetting link

    ata1.00: SATA link down (SStatus 0 SControl 310)

    ata1.00: limiting SATA link speed to 1.5 Gbps

    ata1.00: hard resetting link

    ata1.00: SATA link down (SStatus 0 SControl 310)

    ata1.00: disabled

    ata1: EH complete

    <ata_scsi_remove_dev>:hd [0:0:0:0] change hd status to HD_STATUS_NOEXIST

    ata1.00: detaching (SCSI 0:0:0:0)

    sd 0:0:0:0: [sde] Synchronizing SCSI cache

    sd 0:0:0:0: [sde] 

    Result: hostbyte=0x04 driverbyte=0x00

    sd 0:0:0:0: [sde] Stopping disk

    sd 0:0:0:0: [sde] START_STOP FAILED

    sd 0:0:0:0: [sde] 

    Result: hostbyte=0x04 driverbyte=0x00

    #

    硬碟上電:

    ata1.00: exception Emask 0x10 SAct 0x0 SErr 0x4050000 action 0xf    //ata_eh_link_report

    ata1.00: SError: { PHYRdyChg CommWake DevExch }

    ata1.00: hard resetting link                                        //ata_eh_reset --> postreset(slave, classes)(.postreset = ata_std_postreset)

    ata1.00: SATA link up 1.5 Gbps (SStatus 113 SControl 310)           //ata_std_postreset--> sata_print_link_status

    ata1.00: ATA-9: WDC WD5000LUCT-63C26Y0, 01.01A01, max UDMA/133      //ata_dev_configure

    ata1.00976773168 sectors, multi 0: LBA48 NCQ (depth 31/32)        //ata_dev_configure

    ata1.00: configured for UDMA/133                                    //generic_set_mode

    ata1: EH complete                                                   //ata_scsi_port_error_handler

    scsi 0:0:0:0: Direct-Access     ATA      WDC WD5000LUCT-6 01.0 PQ: 0 ANSI: 5    //scsi_add_lun

    ------------[ cut here ]------------

    WARNING: at drivers/scsi/sd.c:2936 sd_probe+0x1c/0x378()

    Modules linked in:

    CPU: 0 PID: 4 Comm: kworker/0:0 Tainted: G        W    3.10.0_hi3536 #6

    Workqueue: events ata_scsi_hotplug

    [<80019e10>] (unwind_backtrace+0x0/0xf4) from [<80016ea4>] (show_stack+0x10/0x14)

    [<80016ea4>] (show_stack+0x10/0x14) from [<8002c278>] (warn_slowpath_common+0x54/0x6c)

    [<8002c278>] (warn_slowpath_common+0x54/0x6c) from [<8002c32c>] (warn_slowpath_null+0x1c/0x24)

    [<8002c32c>] (warn_slowpath_null+0x1c/0x24) from [<80338860>] (sd_probe+0x1c/0x378)

    [<80338860>] (sd_probe+0x1c/0x378) from [<8031a224>] (driver_probe_device+0x78/0x214)

    [<8031a224>] (driver_probe_device+0x78/0x214) from [<80318898>] (bus_for_each_drv+0x58/0x8c)

    [<80318898>] (bus_for_each_drv+0x58/0x8c) from [<8031a17c>] (device_attach+0x74/0x88)

    [<8031a17c>] (device_attach+0x74/0x88) from [<803197a0>] (bus_probe_device+0x84/0xa8)

    [<803197a0>] (bus_probe_device+0x84/0xa8) from [<80317efc>] (device_add+0x4ec/0x59c)

    [<80317efc>] (device_add+0x4ec/0x59c) from [<80331f34>] (scsi_sysfs_add_sdev+0x84/0x294)

    [<80331f34>] (scsi_sysfs_add_sdev+0x84/0x294) from [<803300d0>] (scsi_probe_and_add_lun+0x8bc/0x98c)

    [<803300d0>] (scsi_probe_and_add_lun+0x8bc/0x98c) from [<803304a0>] (__scsi_add_device+0xf4/0x104)

    [<803304a0>] (__scsi_add_device+0xf4/0x104) from [<8034ac5c>] (ata_scsi_scan_host+0xb0/0x234)

    [<8034ac5c>] (ata_scsi_scan_host+0xb0/0x234) from [<8034ae88>] (ata_scsi_hotplug+0x70/0x7c)

    [<8034ae88>] (ata_scsi_hotplug+0x70/0x7c) from [<800452c8>] (process_one_work+0x10c/0x370)

    [<800452c8>] (process_one_work+0x10c/0x370) from [<80045fd0>] (worker_thread+0x138/0x3fc)

    [<80045fd0>] (worker_thread+0x138/0x3fc) from [<8004b24c>] (kthread+0xb4/0xb8)

    [<8004b24c>] (kthread+0xb4/0xb8) from [<800130d8>] (ret_from_fork+0x14/0x3c)

    ---[ end trace 63dc1c18fe366fe3 ]---

    sd 0:0:0:0: Fix disk[0:0:0:0] to sde

    sd 0:0:0:0: Attached scsi generic sg0 type 0

    sd 0:0:0:0: [sde] 976773168 512-byte logical blocks: (500 GB/465 GiB)

    sd 0:0:0:0: [sde] 4096-byte physical blocks

    sd 0:0:0:0: [sde] Write Protect is off

    sd 0:0:0:0: [sde] Write cache: enabled, read cache: enabled, doesn't support DPO or FUA

    <ata_scsi_scan_host>:hd [0:0:0:0] change hd status to HD_STATUS_OK

     sde: sde1 sde2 sde3 sde4

    sd 0:0:0:0: [sde] Attached SCSI disk

  • 資訊解讀

    • 流程處理:
      • 斷電
        • 斷電後會重連幾次,進行錯誤處理,直到完成
        • 移除磁碟,同步scsi快取
        • hostbyte=0x04表示DID_BAD_TARGET
      • 上電
        • 和開機啟動基本相同
        • dump_stack資訊
          • 磁碟的掃描是通過ata_scsi_hotplug工作佇列實現的

4. 硬碟識別過程

  1. 系統啟動時初始化各檔案系統的超級塊,分配超級塊操作函式
  2. AHCI平臺裝置初始化
    1. 通過驅動和裝置匹配,初始化AHCI平臺裝置
    2. 新增ATA的SCSI主機介面卡,包括兩部分,為主機介面卡分配資料結構,然後將主機介面卡新增到系統
    3. 初始化並執行錯誤處理執行緒scsi_error_handler
    4. 初始化上電識別硬碟的工作佇列async_run_entry_fn和熱插拔識別工作佇列ata_scsi_hotplug
  3. ata檢測及錯誤處理
    1. 執行ahci錯誤處理函式
    2. 連線報告
    3. 傳送ata id查詢命令獲取硬碟基本資訊,共512位元組
      1. 將ata命令轉換成FIS,設定host暫存器,
      2. 傳輸層傳送FIS給鏈路層,鏈路層加上SOF,EOF,CRC,加擾,8b/10b轉換
      3. 傳送到物理層,物理層發出
      4. 相反過程接收
    4. 列印硬碟型號、扇區數、工作模式、定址方式等
  4. 硬碟檢測
    1. 探測lun裝置,為裝置分配了請求佇列,設定回撥處理函式,設定超時處理函式,設定請求處理函式
    2. 通過傳送SCSI INQUIRY命令探測lun單元
      1. 該命令先轉換成通用塊層的request,再把請求進行I/O後加入請求佇列。把請求佇列傳送到請求處理函式scsi_requeset_fn。
      2. 初始化完成量,定義回撥函式,並等待完成量,阻塞
      3. 在SCSI中間層把請求轉換成SCSI CDB。再把命令傳送給ata host處理。
      4. 根據CDB選擇合適的處理函式,INQUIRY命令不需要轉換成ata命令,通過載入硬碟時獲取的ata id 處理。處理完成後呼叫SCSI中間層回撥函式scsi_done。
      5. 再呼叫回撥函式結束request,傳送完成量給阻塞的lun探測。結果返回前其一直阻塞(完成量),根據返回引數註冊scsi_device到scsi總線上。
    3. 總線上的每個driver來匹配device。
    4. 匹配成功則呼叫函式sd_probe初始化scsi_device,對映其對應的<host,channel,target,lun>和裝置檔名稱,同時分配主次裝置號。
    5. 然後通過add_disk註冊到通用塊層,在通用塊層根據初始化的超級塊操作函式關聯到inode指向的block_device。
  5. 識別完成。

5. 硬碟識別過程程式碼呼叫

//AHCI初始化

kernel_init

kernel_init_freeable

do_one_initcall

driver_register

bus_add_driver

bus_for_each_dev

__driver_attach

driver_probe_device

(ahci_platform.c, struct platform_driver ahci_driver, ahci_platform_ops)

|- ahci_probe

    |- ata_host_alloc_pinfo     //熱插拔

        |- ata_host_alloc      

            |- ata_port_alloc   //增加WARN_ON(1)

                |- INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);   //初始化熱插拔檢測工作佇列

    |- ata_host_activate(host, irq, ahci_interrupt, IRQF_SHARED, &ahci_platform_sht);   //申請了中斷,並呼叫ata_host_register函式註冊host

        |- ata_host_register(host, sht);    //增加WARN_ON(1) 

            |- ata_scsi_add_hosts(host, sht);   //新增ATA的SCSI主機介面卡包括兩部分,為主機介面卡分配資料結構,然後將主機介面卡新增到系統

                |- scsi_host_alloc(sht, sizeof(struct ata_port *))  //繼續初始化scsi_host結構體

                    |- shost->ehandler = kthread_run(scsi_error_handler, shost, "scsi_eh_%d", shost->host_no);  //建立並執行scsi錯誤處理執行緒

                |- scsi_add_host_with_dma(ap->scsi_host, &ap->tdev, ap->host->dev);

            |- async_schedule(async_port_probe, ap) //async_port_probe呼叫ata_scsi_scan_host掃描scsi_devcie,並向上層註冊scsi_device

                |- __async_schedule

                    |- INIT_WORK(&entry->work, async_run_entry_fn);  //初始化硬碟掃描工作佇列

//

//硬碟識別前錯誤處理識別硬碟資訊

kthread

|- scsi_error_handler

    |- shost->transportt->eh_strategy_handler(shost) --> ata_scsi_error    // ata_init 中初始化

        |- ata_scsi_cmd_error_handler(host, ap, &eh_work_q);

        |- ata_scsi_port_error_handler(host, ap);

            |- ap->ops->error_handler(ap);--> ahci_error_handler

                |- sata_pmp_error_handler

                    |- ata_eh_report

                        |- ata_eh_link_report   //列印除錯資訊

                    |- sata_pmp_eh_recover

                        |- ata_eh_recover(ap, ops->prereset, ops->softreset, ops->hardreset, ops->postreset, NULL);

                            |- ata_eh_reset(link, ata_link_nr_vacant(link), prereset, softreset, hardreset, postreset);//列印hard resetting link

                                |- postreset(slave, classes) --> ata_std_postreset

                                    |- sata_print_link_status   //列印sata link狀態,SATA link up 1.5 Gbps (SStatus 113 SControl 310)

                            |- ata_set_mode(link, &dev) //Set ATA device disk transfer mode

                                |- ap->ops->set_mode(link, r_failed_dev) --> generic_set_mode

                                    |- ata_dev_info(dev, "configured for %s\n", name);

                            |- ata_eh_revalidate_and_attach(link, &dev);

                                |- ata_dev_read_id(dev, &dev->class, readid_flags, dev->id);  //初始化ata_taskfile的引數,其命令ATA_CMD_ID_ATA,返回值儲存在dev_id中,共512位元組

                                    |- ata_do_dev_read_id(dev, &tf, id);

                                        |- ata_exec_internal(dev, tf, NULL, DMA_FROM_DEVICE, id, sizeof(id[0]) * ATA_ID_WORDS, 0);

                                            |- ata_exec_internal_sg(dev, tf, cdb, dma_dir, psg, n_elem, timeout);   //通過完成變數阻塞執行命令

                                                |- ata_qc_issue(qc);   

                                |- ata_dev_configure(dev);

//

//執行硬碟掃描工作佇列                                                   

|- worker_thread

    |- process_one_work                                                        

        |- worker->current_func(work) --> ata_scsi_hotplug / async_run_entry_fn -- async_port_probe

         

|- ata_scsi_scan_host   //掃描scsi_devcie,並向上層註冊scsi_device

    |- sdev = __scsi_add_device //新增並註冊一個scsi_device

        |- scsi_alloc_target

        |- scsi_probe_and_add_lun   //如果sdev裝置已經存在,就返回SCSI_SCAN_LUN_PRESENT,不進行下面操作

            |- scsi_alloc_sdev 

                |- sdev->request_queue = scsi_alloc_queue(sdev)  //為SCSI裝置分配了請求佇列,設定回撥處理函式,設定超時處理函式

                    |- blk_queue_softirq_done(q, scsi_softirq_done);

                    |- blk_queue_rq_timed_out(q, scsi_times_out);

            |- scsi_probe_lun   //傳送SCSI INQUIRY命令探測lun單元

                |- scsi_execute_req     //scsi命令轉換成request

            |- scsi_add_lun     //通過SCSI INQUIRY 返回的引數初始化sdev的一些成員,初始化裝置型別sdev->type,其在sd_probe中會使用

                |- scsi_sysfs_add_sdev  //向上層註冊sdev

                    |- scsi_target_add

                        |- device_add(&starget->dev) //把裝置新增到所屬匯流排的裝置列表

                            |- bus_add_device

                            |- bus_probe_device(dev)    //為新找到的裝置匹配一個driver

                                |- device_attach(dev)

                                    |- bus_for_each_drv(dev->bus, NULL, dev, __device_attach);   //用bus上的每個drv來匹配dev

                                        |- __device_attach

                                            |- driver_match_device  //如果匹配成功,則執行driver_probe_device函式

                                                |- return drv->bus->match ? drv->bus->match(dev, drv) : 1 --> scsi_bus_type.match=scsi_bus_match

                                            |- driver_probe_device

                                                |- really_probe     //根據bus->probe是否為真來判斷是否呼叫drv->probe;無論呼叫哪個probe,都會執行到sd_probe函式。

                                                    |- drv->probe(dev) --> sd_probe   //匹配到裝置時呼叫,在給定的ID和裝置名之間建立對映,同時確定了塊裝置的主次裝置號。並關聯到了通用塊層 //增加WARN_ON(1)

                                                        |- gd = alloc_disk(SD_MINORS);

                                                        |- async_schedule_domain(sd_probe_async, sdkp, &scsi_sd_probe_domain)

                                                            |- __async_schedule

                                                                |- sd_probe_async   //正常是通過工作佇列執行,當工作佇列滿了或者記憶體不足時直接執行

                                                                    |- gendisk引數初始化

                                                                    |- scsi_disk引數初始化

                                                                    |- sd_revalidate_disk   //列印資訊

                                                                        |- sd_read_capacity    

                                                                        |- sd_read_write_protect_flag

                                                                        |- sd_read_cache_type

                                                                    |- blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);

                                                                    |- add_disk(gd);    //註冊到通用塊層

//

//硬碟識別過程中scsi_alloc_sdev呼叫,初始化request_fn和make_request_fn

scsi_alloc_queue

    |- q = __scsi_alloc_queue(sdev->host, scsi_request_fn);

        |- q = blk_init_queue(request_fn, NULL);

            |- blk_init_queue_node

                |- blk_init_allocated_queue

                    |- q->request_fn = rfn   //request_fn初始化為scsi_request_fn

                    |- blk_queue_make_request(q, blk_queue_bio)

                        |- q->make_request_fn = mfn  //make_request_fn初始化為blk_queue_bio

                    |- elevator_init(q, NULL)

    |- blk_queue_prep_rq(q, scsi_prep_fn);  //在sd_probe中修改為sd_prep_fn

    |- blk_queue_softirq_done(q, scsi_softirq_done);

    |- blk_queue_rq_timed_out(q, scsi_times_out);

    |- blk_queue_lld_busy(q, scsi_lld_busy);

//

//scsi命令轉換成request再轉換成scsi命令傳送

scsi_probe_lun

    |- result = scsi_execute_req(sdev,  scsi_cmd, DMA_FROM_DEVICE,inq_result, try_inquiry_len, &sshdr, HZ / 2 + HZ * scsi_inq_timeout, 3,&resid);

        |- scsi_execute_req_flags

            |- result = scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense, timeout, retries, flags, resid);

                |- blk_get_request  //申請request並初始化

                    |- rq = get_request(q, rw, NULL, gfp_mask);

                        |- __get_request

                            |- rq = mempool_alloc(rl->rq_pool, gfp_mask);   

                            |- blk_rq_init(q, rq); 

                |- blk_execute_rq(req->q, NULL, req, 1); //利用完成變數來進行同步操作,探測lun

                    |- rq->end_io_data = &wait; 

                    |- blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq); //把請求插入I/O排程佇列,之後處理請求

                        |- rq->end_io = blk_end_sync_rq; //scsi第一次回撥過程中會呼叫該函式,其會呼叫complete(waiting),scsi_probe_lun繼續執行

                        |- __elv_add_request(q, rq, where);

                        |- __blk_run_queue(q);

                            |- __blk_run_queue_uncond(q);

                                |- q->request_fn(q) --> scsi_request_fn

                    |- wait_for_completion_io(&wait);   //scsi_probe_lun一直會阻塞在這裡直到接收到完成訊號,預設超時時間為LONG_MAX,可忽略

            |- scsi_execute_req_flags   //將感測資料格式化為公共格式

//

//SCSI中間層(SCSI協議層)

request_queue->request_fn---> scsi_request_fn //scsi裝置請求佇列處理函式

        |- req = blk_peek_request(q).

            |- rq = __elv_next_request(q)   //從請求佇列中獲取一個請求

            |- q->end_sector = rq_end_sector(rq);

            |- ret = q->prep_rq_fn(q, rq)--> sd_prep_fn   //根據request初始化SCSI命令,構建SCSI CDB

        |- blk_queue_start_tag

            |- blk_start_request(rq);

                |- blk_dequeue_request(req);

                    |- list_del_init(&rq->queuelist);    //把request從request queue佇列裡刪除掉

        |- rtn = scsi_dispatch_cmd(cmd)     //分發請求,把SCSI命令提交給SCSI控制器 

            |- cmd->scsi_done = scsi_done;

            |- host->hostt->queuecommand(host, cmd)   --> ata_scsi_queuecmd   

//SCSI底層(SCSI傳輸層/SCSI控制器驅動層)//向ata管理器發出scsi cdb

                |- __ata_scsi_queuecmd(cmd, dev)

                    |- xlat_func = ata_get_xlat_func(dev, scsi_op);

                    |- ata_scsi_translate(dev, scmd, xlat_func);    //Scis命令真實轉換為ata命令的流程

                        |- qc = ata_scsi_qc_new(dev, cmd)

                            |- qc->scsidone = cmd->scsi_done == scsi_done //設定ata—scsi回撥函式

                        |- qc->complete_fn = ata_scsi_qc_complete;   //設定ata處理完成回撥函式

                        |- xlat_func(qc) ---> 假設是READ_6呼叫函式 ata_scsi_rw_xlat //前面返回的xlat_func函式

                            |- ata_build_rw_tf(&qc->tf, qc->dev, block, n_block, tf_flags, qc->tag);//打包ata命令,主要是scsi_cmd初始化qc->tf,其代表ata—cmd

                        |- ata_qc_issue(qc);    //傳送命令給指定裝置

                            |- ap->ops->qc_prep(qc) --> ahci_qc_prep   //把ata cmd 轉換成fis

                                |- ata_tf_to_fis(&qc->tf, qc->dev->link->pmp, 1, cmd_tbl); 

                                |- ahci_fill_sg(qc, cmd_tbl);   //

                                |- ahci_fill_cmd_slot(pp, qc->tag, opts);    //

  &nbs