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);    //

                            |- ap->ops->qc_issue(qc) --> ahci_qc_issue //激活定时器去通知硬件发送fis,fis通过中断发送,还未找到

                    |- ata_scsi_simulate(dev, scmd);    //sata命令不转换成ata命令,通过加载时的错误处理获取的id赋值

                        |- cmd->scsi_done == scsi_done

//
//AHCI中断
|- ahci_interrupt
    |- ahci_port_intr(ap);
        |- ahci_handle_port_interrupt(ap, port_mmio, status)
            |- ata_qc_complete_multiple(ap, qc_active)
                |- ata_qc_complete(qc);
                    |- __ata_qc_complete(qc);
                        |- qc->complete_fn(qc) --> ata_scsi_qc_complete

//

//scsi第一次回调:底层调用scsi_done函数产生软中断,运行函数scsi_softirq_done,scsi中间层根据其判断是否成功

ata_scsi_qc_complete

qc->scsidone(cmd) --> scsi_done

|- raise_softirq_irqoff(BLOCK_SOFTIRQ) --> blk_done_softirq

    |- rq->q->softirq_done_fn(rq) --> scsi_softirq_done

        |- scsi_decide_disposition

        |- scsi_finish_command  //成功,结束命令

            |- scsi_io_completion(cmd, good_bytes);

                |- scsi_end_request

                    |- blk_end_request

                        |- blk_end_bidi_request

                            |- blk_finish_request

                                |- req->end_io(req, error) --> blk_end_sync_rq

        |- scsi_queue_insert    //如果错误,从此执行

        |- scsi_eh_scmd_add    

            |- scsi_host_set_state(shost, SHOST_RECOVERY)   //后该host将处于阻塞状态,任何发往该host的IO都将阻塞,直至error handler处理完成

                |- shost->shost_state = SHOST_RECOVERY

            |- scsi_eh_wakeup(shost);   //唤醒错误处理线程,调度运行

                |- wake_up_process(shost->ehandler) --> scsi_error_handler

                    |- return try_to_wake_up(p, TASK_NORMAL, 0)

        |- scsi_finish_command  //结束命令

//

//得到block_device块设备,这样scsi层和块设备层就联系了

|- add_disk

    |- disk_alloc_events(disk)

    |- register_disk(disk)

         |- bdev = bdget_disk(disk, 0);

             |- bdev = bdget(part_devt(part));

                 |- inode = iget5_locked(blockdev_superblock, hash(dev), bdev_test,bdev_set, &dev);

                      |- inode = alloc_inode(sb);

                            |- inode = sb->s_op->alloc_inode(sb); //inode通过超级块获得,超级块初始化见下面

                 |- bdev = &BDEV_I(inode)->bdev; //通过这个inode得到bdev

    |- blk_register_queue(disk)

    |- disk_add_events(disk)

//

//超级块初始化

|- vfs_caches_init

    |- bdev_cache_init

        |- struct vfsmount bd_mnt = kern_mount(&bd_type) --> kern_mount_data

            |- struct vfsmount mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);   //根据文件系统类型分配安装点并初始化

                  |- struct dentry root = mount_fs(type, flags, name, data)

                        |- root = mount_fs(type, flags, name, data);

                             |- root = type->mount(&bd_type, flags, name, data) --> bd_mount

                                 |- mount_pseudo(fs_type, "bdev:", &bdev_sops, NULL,BDEVFS_MAGIC);    //分配超级块和目录项并初始化

                                    |- s->s_op = ops ? ops : &simple_super_operations;(ops是&bdev_sops)       //超级块ops初始化为bdev_sops

        |- blockdev_superblock = bd_mnt->mnt_sb; //获得超级块


 

 

转载路径:https://blog.csdn.net/qq_37403371/article/details/84396099

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值