当前位置:   article > 正文

Linux AHCI驱动_linuxahci子系统

linuxahci子系统

目录

概念

port

link

device

tag

scsi host

scsi device

sgl

Port、Link、Device之间的关系

Port

Link

host link

pmp link

Device

初始化流程

异常流程

ops

ata_port_operations 

scsi_host_temp

Shadow Registers / Task File Registers

libata内部使用的ATA命令发送接口

ata_exec_internal、ata_exec_internal_sg

SCSI层发送ATA命令

设备与驱动匹配过程复习

ata_scsi_queuecmd 

ata_std_qc_defer

scsi_execute_req 同步命令访问

命令完成 ata_qc_complete 、 ata_qc_complete_multiple

tag active相关变量

核心ATA发送命令 ata_qc_issue

ahci_qc_prep

ahci_qc_issue

端口恢复 ahci_port_resume

ahci_stop_engine

ahci_start_engine

ahci_stop_fis_rx

ahci_error_handler

sata_pmp_error_handler

ahci_port_suspend


非AHCI模式下的SATA驱动不同芯片实现方式很不同,规范未规定软件如何组装FIS,如何组装PRD。

如fsl的sata控制器中,命令表为CFIS(32B)+SFIS(32B)+ACMD(16B)+RSRVD(16B)+PRD(63)*16;
有些控制器的CFIS和其他其他字段可能根本不连续。

PRD的定义不同芯片也不一样。AHCI规范就规定了所有的地址和定义,让驱动统一了起来;
ahci的命令表为CFIS(64B)+ACMD(16B)+RSRVD(48B)+PRD(64KB)*16;

概念

port

一个AHCI HBA可支持最多32个sata port。

一个sata port可通过Port Multiplier连接最多15个link。

device

从linux驱动源码来看,一个link可连接2个设备,但一个link时如何连接2个设备的呢?

tag

SATA设备NCQ模式下一次最多同时存在32个待处理的命令,使用tag来标记每个命令的序号,驱动代码中有些地方使用tag数值表示某个命令,有些地方使用bitmask来表示哪些命令正在处理。

scsi host

scsi主机,在ata-scsi中,一个port就是一个scsi host,在ata_scsi_add_hosts函数中创建。

ahci_init_one
    -> ata_host_activate
        -> ata_host_register
            -> ata_scsi_add_hosts

                -> for (i = 0; i < n_ports; i++) {

                        scsi_host_alloc();

                        scsi_add_host_with_dma();

                }

            -> async_port_probe

scsi device

 scsi设备。在ata-scsi中,对应一个port的一个link,也就是ata的一个pmp

ahci_init_one
    -> ata_host_activate
        -> ata_host_register
                -> ata_scsi_add_hosts

                -> async_port_probe //每个端口

                        -> ata_port_probe(ap);

                        -> ata_scsi_scan_host;
                                 -> __scsi_add_device;

ata_scsi_port_error_handler

        ->ata_scsi_hotplug //通过schedule_delayed_work唤醒

                -> ata_scsi_handle_link_detach

                -> ata_scsi_scan_host;

        

sgl

参考文章IOMMU/SMMUV3代码分析(8)函数dma_map_sg()_linux解码者的博客-CSDN博客

集散链表。sg类型为scatterlist,表示一小块连续物理内存。多个sg组成一个链表就叫做sgl,sgl表述一片分散的物理内存。

 结构体scatterlist包含从CPU和IO设备角度看到的一块连续的物理内存区域的地址和长度。其中dma_addrss为设备看到的虚拟地址即IOVA,page_link为CPU看到的虚拟地址即VA,length为数据长度,offset为数据在页中的偏移。(当前单个scattherlist表示最多为一页)

  1. struct scatterlist {
  2. unsigned long page_link;//CPU侧看到的物理地址(以page为单位,只能表述是第哪个page)
  3. unsigned int offset;//CPU侧看到的物理地址(page_link+offset,能具体表述那个字节)
  4. unsigned int length;//本断数据的长度
  5. dma_addr_t dma_address;//设备侧看到的DMA虚拟地址
  6. #ifdef CONFIG_NEED_SG_DMA_LENGTH
  7. unsigned int dma_length;
  8. #endif
  9. };

 dma_map_sg

将旧sgl中多个分散的物理地址映射为连续ivoa地址,iova被赋值到dma_address变量中,组成新的sgl,新sgl的sg数量比旧sgl的sg数量更少。

  1. 0-> ata_sg_setup
  2. 1-> dma_map_sg
  3. 2->ops->map_sg //如果CPU支持IOMMU则__iommu_map_sg_attrs,否则__swiotlb_map_sg_attrs
  4. 0-> __iommu_map_sg_attrs
  5. 1-> iommu_dma_map_sg

Port、Link、Device之间的关系

一个AHCI包含最多32个host port,每个host port可使用Port Multiplier芯片扩展出最多15个Link,每个host port也是scsi设备的host,每个Link上可接一个Device(如SSD或其他ata设备)。Linux驱动为何统一,即使没有使用Port Multiplier来扩展link,驱动也会视作端口有一个link(我称之为host link),然后通过link连接Device

AHCI HBA -> port0-31 -> link0-15 -> device

Port

由ata_port_alloc分配,结构体定义如下

  1. struct ata_port {
  2. struct Scsi_Host *scsi_host; /* our co-allocated scsi host */
  3. struct ata_port_operations *ops;
  4. spinlock_t *lock;
  5. /* Flags owned by the EH context. Only EH should touch these once the
  6. port is active */
  7. unsigned long flags; /* ATA_FLAG_xxx */
  8. /* Flags that change dynamically, protected by ap->lock */
  9. unsigned int pflags; /* ATA_PFLAG_xxx */
  10. unsigned int print_id; /* user visible unique port ID */
  11. unsigned int local_port_no; /* host local port num */
  12. unsigned int port_no; /* 0 based port no. inside the host */
  13. struct ata_ioports ioaddr; /* ATA cmd/ctl/dma register blocks */
  14. u8 ctl; /* cache of ATA control register */
  15. u8 last_ctl; /* Cache last written value */
  16. struct ata_link* sff_pio_task_link; /* link currently used */
  17. struct delayed_work sff_pio_task;
  18. struct ata_bmdma_prd *bmdma_prd; /* BMDMA SG list */
  19. dma_addr_t bmdma_prd_dma; /* and its DMA mapping */
  20. unsigned int pio_mask;
  21. unsigned int mwdma_mask;
  22. unsigned int udma_mask;
  23. unsigned int cbl; /* cable type; ATA_CBL_xxx */
  24. struct ata_queued_cmd qcmd[ATA_MAX_QUEUE + 1];
  25. unsigned long sas_tag_allocated; /* for sas tag allocation only */
  26. u64 qc_active;
  27. int nr_active_links; /* #links with active qcs */
  28. unsigned int sas_last_tag; /* track next tag hw expects */
  29. struct ata_link link; /* host default link ,host端口用的link*/
  30. struct ata_link *slave_link; /* see ata_slave_link_init() */
  31. int nr_pmp_links; /* nr of available PMP links */
  32. struct ata_link *pmp_link; /* array of PMP links */
  33. struct ata_link *excl_link; /* for PMP qc exclusion */
  34. struct ata_port_stats stats;
  35. struct ata_host *host;
  36. struct device *dev;
  37. struct device tdev;
  38. struct mutex scsi_scan_mutex;
  39. struct delayed_work hotplug_task;
  40. struct work_struct scsi_rescan_task;
  41. unsigned int hsm_task_state;
  42. u32 msg_enable;
  43. struct list_head eh_done_q;
  44. wait_queue_head_t eh_wait_q;
  45. int eh_tries;
  46. struct completion park_req_pending;
  47. pm_message_t pm_mesg;
  48. enum ata_lpm_policy target_lpm_policy;
  49. struct timer_list fastdrain_timer;
  50. unsigned long fastdrain_cnt;
  51. int em_message_type;
  52. void *private_data;
  53. struct ata_acpi_gtm __acpi_init_gtm; /* use ata_acpi_init_gtm() */
  54. /* owned by EH */
  55. u8 sector_buf[ATA_SECT_SIZE] ____cacheline_aligned;
  56. };

ata_port.link就是一个host port的host link,无论是否有pmp端口,该link一定存在,用于处理该host port的操作,很多操作只能host port来处理,不能使用pmp端口。.pmp=0,但在使用时,一般会linkno = 15。

在系统运行期间通过sata_pmp_attach->sata_pmp_init_links->ata_link_init接口来初始化15个link并使用port->pmp_link指向这15个link的数组,.pmp值为0~14。

结构体定义如下

  1. struct ata_link {
  2. struct ata_port *ap;
  3. int pmp; /* port multiplier port # */
  4. struct device tdev;
  5. unsigned int active_tag; /* active tag on this link */
  6. u32 sactive; /* active NCQ commands */
  7. unsigned int flags; /* ATA_LFLAG_xxx */
  8. u32 saved_scontrol; /* SControl on probe */
  9. unsigned int hw_sata_spd_limit;
  10. unsigned int sata_spd_limit;
  11. unsigned int sata_spd; /* current SATA PHY speed */
  12. enum ata_lpm_policy lpm_policy;
  13. /* record runtime error info, protected by host_set lock */
  14. struct ata_eh_info eh_info;
  15. /* EH context */
  16. struct ata_eh_context eh_context;
  17. struct ata_device device[ATA_MAX_DEVICES];//ATA_MAX_DEVICES=2
  18. unsigned long last_lpm_change; /* when last LPM change happened */
  19. };

Device

ata_link_init->ata_dev_init来初始化一个link的两个设备 

结构体定义如下

  1. struct ata_device {
  2. struct ata_link *link;
  3. unsigned int devno; /* 0 or 1 */
  4. unsigned int horkage; /* List of broken features */
  5. unsigned long flags; /* ATA_DFLAG_xxx */
  6. struct scsi_device *sdev; /* attached SCSI device */
  7. void *private_data;
  8. union acpi_object *gtf_cache;
  9. unsigned int gtf_filter;
  10. struct device tdev;
  11. /* n_sector is CLEAR_BEGIN, read comment above CLEAR_BEGIN */
  12. u64 n_sectors; /* size of device, if ATA */
  13. u64 n_native_sectors; /* native size, if ATA */
  14. unsigned int class; /* ATA_DEV_xxx */
  15. unsigned long unpark_deadline;
  16. u8 pio_mode;
  17. u8 dma_mode;
  18. u8 xfer_mode;
  19. unsigned int xfer_shift; /* ATA_SHIFT_xxx */
  20. unsigned int multi_count; /* sectors count for
  21. READ/WRITE MULTIPLE */
  22. unsigned int max_sectors; /* per-device max sectors */
  23. unsigned int cdb_len;
  24. /* per-dev xfer mask */
  25. unsigned long pio_mask;
  26. unsigned long mwdma_mask;
  27. unsigned long udma_mask;
  28. /* for CHS addressing */
  29. u16 cylinders; /* Number of cylinders */
  30. u16 heads; /* Number of heads */
  31. u16 sectors; /* Number of sectors per track */
  32. union {
  33. u16 id[ATA_ID_WORDS]; /* IDENTIFY xxx DEVICE data */
  34. u32 gscr[SATA_PMP_GSCR_DWORDS]; /* PMP GSCR block */
  35. } ____cacheline_aligned;
  36. /* DEVSLP Timing Variables from Identify Device Data Log */
  37. u8 devslp_timing[ATA_LOG_DEVSLP_SIZE];
  38. /* NCQ send and receive log subcommand support */
  39. u8 ncq_send_recv_cmds[ATA_LOG_NCQ_SEND_RECV_SIZE];
  40. u8 ncq_non_data_cmds[ATA_LOG_NCQ_NON_DATA_SIZE];
  41. /* ZAC zone configuration */
  42. u32 zac_zoned_cap;
  43. u32 zac_zones_optimal_open;
  44. u32 zac_zones_optimal_nonseq;
  45. u32 zac_zones_max_open;
  46. /* error history */
  47. int spdn_cnt;
  48. /* ering is CLEAR_END, read comment above CLEAR_END */
  49. struct ata_ering ering;
  50. };

初始化流程

待补充

异常流程

  1. 异常处理的初始化:
  2. sata_port_ops.sched_eh=ata_std_sched_eh(libata-core.c);
  3. scsi_host_alloc->.ehandler=scsi_error_handler(scsi/hosts.c);
  4. subsystem init->ata_init->ata_attach_transport(libata_core.c)->.eh_strategy_handler=ata_scsi_error(libata_eh.c);
  5. ata_qc_complete(ATA_CMD_SET_MULTI)(libata-core.c)->ata_port_schedule_eh(libata-eh.c);
  6. ata_port_probe(libata-core.c)->__ata_port_probe(libata-core.c)->ata_port_schedule_eh(libata-eh.c);
  7. ata_port_detach(libata-core.c)->ata_port_schedule_eh(libata-eh.c);
  8. ata_do_link_abort(libata-eh.c)->ata_port_schedule_eh(libata-eh.c);
  9. 异常处理流程:
  10. ahci_handle_port_interrupt(libahci.c)
  11. ->sata_async_notification(libata-eh.c)
  12. ->ata_port_schedule_eh(libata-eh.c)
  13. ->ata_std_sched_eh(libata-eh.c) //.sched_eh
  14. ->scsi_schedule_eh(scsi/scsi_error.c)
  15. ->scsi_error_handler //SCSI的主错误处理内核态线程, 通过scsi_eh_wakeup唤醒(scsi/hosts.c))
  16. ->ata_scsi_error(libata_eh.c) //.eh_strategy_handler
  17. ->ata_scsi_port_error_handler(libata_eh.c)
  18. ->ata_std_error_handler(libata_eh.c) //.error_handler
  19. ->ata_do_eh(libata_eh.c)
  20. ->ata_eh_recover(libata_eh.c)
  21. ->ata_eh_reset(libata_eh.c)
  22. ->ata_std_postreset(libata_core.c) //.postreset
  23. ->sata_print_link_status(libata_core.c)

ops

ata_port_operations 

  1. //libata-core.c
  2. const struct ata_port_operations sata_port_ops = {
  3. .inherits = &ata_base_port_ops,
  4. .qc_defer = ata_std_qc_defer,
  5. .hardreset = sata_std_hardreset,
  6. };
  7. //libata-pmp.c
  8. const struct ata_port_operations sata_pmp_port_ops = {
  9. .inherits = &sata_port_ops,
  10. .pmp_prereset = ata_std_prereset,
  11. .pmp_hardreset = sata_std_hardreset,
  12. .pmp_postreset = ata_std_postreset,
  13. .error_handler = sata_pmp_error_handler,
  14. };
  15. //libahci.c
  16. struct ata_port_operations ahci_ops = {
  17. .inherits = &sata_pmp_port_ops,
  18. .qc_defer = ahci_pmp_qc_defer,
  19. .qc_prep = ahci_qc_prep,
  20. .qc_issue = ahci_qc_issue,
  21. .qc_fill_rtf = ahci_qc_fill_rtf,
  22. .freeze = ahci_freeze,//关闭端口中断使能寄存器(0x14)
  23. .thaw = ahci_thaw,//清除端口中断状态(0x10),并打开端口中断使能寄存器(0x14)
  24. .softreset = ahci_softreset,
  25. .hardreset = ahci_hardreset,
  26. .postreset = ahci_postreset,
  27. .pmp_softreset = ahci_softreset,
  28. .error_handler = ahci_error_handler,
  29. .post_internal_cmd = ahci_post_internal_cmd,
  30. .dev_config = ahci_dev_config,
  31. .scr_read = ahci_scr_read,//sata 标准寄存器读
  32. .scr_write = ahci_scr_write,//sata 标准寄存器写
  33. .pmp_attach = ahci_pmp_attach,
  34. .pmp_detach = ahci_pmp_detach,
  35. .set_lpm = ahci_set_lpm,
  36. .em_show = ahci_led_show,
  37. .em_store = ahci_led_store,
  38. .sw_activity_show = ahci_activity_show,
  39. .sw_activity_store = ahci_activity_store,
  40. .transmit_led_message = ahci_transmit_led_message,
  41. #ifdef CONFIG_PM
  42. .port_suspend = ahci_port_suspend,
  43. .port_resume = ahci_port_resume,
  44. #endif
  45. .port_start = ahci_port_start,//在 ahci_host_activate->ata_host_activate->ata_host_start(libata_core.c) 中调用
  46. .port_stop = ahci_port_stop,//在 ata_host_stop (libata_core.c) 中调用
  47. };

scsi_host_temp

  1. //libata.h
  2. #define ATA_BASE_SHT(drv_name) \
  3. .module = THIS_MODULE, \
  4. .name = drv_name, \
  5. .ioctl = ata_scsi_ioctl, \
  6. .queuecommand = ata_scsi_queuecmd, \
  7. .can_queue = ATA_DEF_QUEUE, \
  8. .tag_alloc_policy = BLK_TAG_ALLOC_RR, \
  9. .this_id = ATA_SHT_THIS_ID, \
  10. .emulated = ATA_SHT_EMULATED, \
  11. .use_clustering = ATA_SHT_USE_CLUSTERING, \
  12. .proc_name = drv_name, \
  13. .slave_configure = ata_scsi_slave_config, \
  14. .slave_destroy = ata_scsi_slave_destroy, \
  15. .bios_param = ata_std_bios_param, \
  16. .unlock_native_capacity = ata_scsi_unlock_native_capacity, \
  17. .sdev_attrs = ata_common_sdev_attrs
  18. #define ATA_NCQ_SHT(drv_name) \
  19. ATA_BASE_SHT(drv_name), \
  20. .change_queue_depth = ata_scsi_change_queue_depth
  21. //ahci.h
  22. #define AHCI_SHT(drv_name) \
  23. ATA_NCQ_SHT(drv_name), \
  24. .can_queue = AHCI_MAX_CMDS, \
  25. .sg_tablesize = AHCI_MAX_SG, \
  26. .dma_boundary = AHCI_DMA_BOUNDARY, \
  27. .shost_attrs = ahci_shost_attrs, \
  28. .sdev_attrs = ahci_sdev_attrs
  29. //ahci.c
  30. static struct scsi_host_template ahci_sht = {
  31.     AHCI_SHT("ahci"),
  32. };

Shadow Registers / Task File Registers

设备中有一个Task File结构,用于与主机交互命令,源码定义于libata.h

  1. struct ata_taskfile {
  2. unsigned long flags; /* ATA_TFLAG_xxx */
  3. u8 protocol; /* ATA_PROT_xxx */
  4. u8 ctl; /* control reg */
  5. u8 hob_feature; /* additional data */
  6. u8 hob_nsect; /* to support LBA48 */
  7. u8 hob_lbal;
  8. u8 hob_lbam;
  9. u8 hob_lbah;
  10. u8 feature;
  11. u8 nsect;
  12. u8 lbal;
  13. u8 lbam;
  14. u8 lbah;
  15. u8 device;
  16. u8 command; /* IO operation */
  17. u32 auxiliary; /* auxiliary field */
  18. /* from SATA 3.1 and */
  19. /* ATA-8 ACS-3 */
  20. };

libata内部使用的ATA命令发送接口

libata中使用tf(taskfile)作为入参来描述一条命令。

ata_exec_internal、ata_exec_internal_sg

前者使用buf、后者使用sg来指向数据内存。发送tf中的命令给设备。

此接口会将tf结构转换为qc(struct ata_queued_cmd)队列命令,再交由ata_qc_issue来完成命令的发送。

这两个内部发送命令固定使用第一个队列命令,qc->tag = ATA_TAG_INTERNAL=32; qc->hw_tag = 0。

  1. //libata-core.c
  2. 0-> ata_exec_internal
  3. 1-> ata_exec_internal_sg //tf转换为qc后发送ata命令
  4. 2-> __ata_qc_from_tag //获取tag=ATA_TAG_INTERNAL专用qc
  5. 2-> qc->complete_fn = ata_qc_complete_internal;
  6. 2-> ata_qc_issue
  7. 2-> wait_for_completion_timeout //等待完成命令完成
  8. 2-> ata_qc_free //释放qc资源
  9. 0-> 完成中断
  10. 1-> ata_qc_complete_internal
  11. 2-> complete(waiting); //通过调用complete来唤醒等待线程

SCSI层发送ATA命令

scsi中使用scsi_cmnd来描述一条命令,scsi_cmnd中也有一个tag用于标记队列命令,功能与taskfile中的hw_tag类似。

ata core注册的scsi接口如下

//ahci.c

static struct scsi_host_template ahci_sht = {

    AHCI_SHT("ahci"),

};

其中

.queuecommand = ata_scsi_queuecmd

  1. /**********初始化阶段**********/
  2. //mq模式下 初始化scsi阶段就已经决定了发送接口
  3. ata_scsi_add_hosts
  4. -> scsi_add_host_with_dma //每个端口都会执行
  5. -> scsi_mq_setup_tags
  6. -> shost->tag_set.ops = &scsi_mq_ops; //{.queue_rq = scsi_queue_rq,...}
  7. init_sd (scsi/sd.c)
  8. -> register_blkdev //执行16次,注册16sd块设备
  9. -> scsi_register_driver(&sd_template.gendrv); //sd_template={.gendrv = {.probe = sd_probe,...},...}
  10. -> drv->bus = &scsi_bus_type; //scsi_bus_type={.match = scsi_bus_match,,...}
  11. -> driver_register//注册scsi总线驱动,总线类型为scsi_bus_type,此总线类型的match函数中会匹配dev->type == &scsi_dev_type
  12. /**********初始化阶段或运行阶段**********/
  13. ata_scsi_scan_host
  14. -> __scsi_add_device
  15. -> scsi_probe_and_add_lun //探测并添加逻辑单元号(Logic Unit Number),lun范围[1,512]
  16. -> scsi_alloc_sdev
  17. -> scsi_sysfs_device_initialize
  18. -> sdev->sdev_gendev.bus = &scsi_bus_type;
  19. -> sdev->sdev_gendev.type = &scsi_dev_type;//
  20. if(使用blk的mq模式)
  21. -> scsi_mq_alloc_queue
  22. else
  23. -> scsi_old_alloc_queue
  24. q->request_fn = scsi_request_fn
  25. -> scsi_probe_lun //通过scsi_execute_req获取设备inquiry
  26. -> scsi_add_lun
  27. -> scsi_sysfs_add_sdev
  28. -> device_add(&sdev->sdev_gendev); //之前已经把bus和type都配置了,此接口在match成功后,会调用驱动的probe函数
  29. ...-> sd_probe //完成创建磁盘和与设备关联的相关操作,完成后在用户层面就可以看到磁盘
  30. /**********运行阶段**********/
  31. sd_probe(scsi/sd.c)
  32. -> alloc_disk //分配genhd数据结构并设置相关属性
  33. -> sd_probe_async
  34. -> sd_revalidate_disk
  35. -> sd_spinup_disk //启动disk,TEST_UNIT_READY命令
  36. -> sd_read_capacity //获取盘信息,capacity容量、sector_size和physical_block_size,并配置给request_queue
  37. -> sd_print_capacity //打印磁盘信息
  38. -> device_add_disk //将磁盘添加到系统
  39. //mq模式
  40. scsi_queue_rq
  41. -> cmd->scsi_done = scsi_mq_done;
  42. -> scsi_dispatch_cmd
  43. -> host->hostt->queuecommand(host, cmd); //ata_scsi_queuecmd
  44. //非mq模式
  45. scsi_request_fn
  46. -> cmd->scsi_done = scsi_done;
  47. -> scsi_dispatch_cmd
  48. -> host->hostt->queuecommand(host, cmd); //ata_scsi_queuecmd

IO层面

        sector:盘扇区,一般512

        logical blocks:盘逻辑块,就是sector,有些地方叫logic sector,一般512

        physical blocks:盘物理块,一般hhd 512、ssd 4K,为逻辑块整数倍,操作一个物理块和操作一个逻辑块耗时相同。

文件系统层面

        块:windows中又叫簇,在创建文件系统时指定,为盘物理块整数倍,

设备与驱动匹配过程复习

设备注册device_add和驱动注册driver_register都会调用的bus_type中的match接口来匹配驱动与设备,匹配成功则调用bus_type中的probe或者驱动中的probe,一旦匹配成功,dev->driver就会设置为匹配驱动。

        如果是pci这样真实物理总线,pci_bus_match函数会根据drv的id_table来匹配pci总线上的设备;

        如果是平台总线,platform_match函数会先用drv.of_device_id来匹配,只要.name .type .compatible三个参数中的任何一个匹配成功则匹配成功,优先级 .compatible> .type>.name;如果匹配失败,则匹配acpi;如果匹配失败,则根据drv的id_table来匹配,此处的id_table与pci总线的id_table不同,包含一个字符串和一个指针,其实还是通过字符串来匹配的,并不是设备id


一、先注册驱动,再注册设备
        1、注册驱动需指定device_driver.bus_type,bus_type设置.name和.match,然后driver_register注册驱动

        2、注册设备需指定device.bus_type和device.device_type,然后调用device_add注册设备。

        device_add

            ->bus_add_device //将设备注册进指定的bus_type中

            ->bus_probe_device//匹配设备与驱动并probe

                ->device_initial_probe->__device_attach

                    ->bus_for_each_drv 为此bus_type的所有驱动调用__device_attach_driver(drv, data)

                        ->__device_attach_driver

                           ->driver_match_device //调用bus_type中的.match接口匹配设备

                           ->driver_probe_device //调用probe

                                -> if (dev->bus->probe) dev->bus->probe(dev);

                                    else if (drv->probe) drv->probe(dev);//dev->bus->probe和drv->probe同时不为空时属于异常情况,driver_register时会警告

  1. __device_attach()
  2. {
  3. //如果此设备有驱动,really_probe中会dev->driver = drv;所以一个设备最多只能匹配到一个驱动
  4. if (dev->driver)
  5. {
  6. ret = device_bind_driver(dev);
  7. }
  8. //没有驱动去寻找驱动匹配
  9. else
  10. {
  11. ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach_driver); //设备匹配驱动
  12. }
  13. }

二、先注册设备,再注册驱动

1、注册设备需指定device.bus_type和device.device_type,然后调用device_add注册设备

2、注册驱动需指定device_driver.bus_type,bus_type设置.name和.match,然后driver_register注册驱动

    driver_register

        ->bus_add_driver

            ->driver_attach

                ->bus_for_each_dev //为此bus_type的所有设备调用__driver_attach

                    ->__driver_attach

                        ->driver_match_device

                        ->driver_probe_device

ata_scsi_queuecmd 

  1. 0-> ata_scsi_queuecmd
  2. 1-> __ata_scsi_queuecmd
  3. 2-> ata_scsi_translate //scsi命令转化为ATA命令后发出SCSI命令到ATA设备。
  4. 3-> 根据scmd.cmnd获取转化函数xlat_func
  5. 3-> ata_scsi_qc_new //通过scsi命令获取qc,并初始化
  6. 4-> ata_qc_new_init
  7. 5-> __ata_qc_from_tag //通过tag号获取qc
  8. 4-> qc->scsidone = cmd->scsi_done; //将scsi命令的完成回调函数赋值给qc
  9. 3-> ata_sg_init //scsi命令中获取sg链表
  10. 3-> qc->complete_fn = ata_scsi_qc_complete;
  11. 3-> xlat_func(qc); // 将scsi命令转化为ata命令,
  12. 如果xlat_func==atapi_xlat
  13. 4-> qc->complete_fn = atapi_qc_complete;
  14. 3-> ap->ops->qc_defer(qc) //ata_std_qc_defer 检测qc是否需要被延迟(deferred),被延时的命令会被返回繁忙错误,等待再次被发送
  15. 3-> ata_qc_issue
  16. 0-> 完成中断
  17. 1-> ata_scsi_qc_complete 或 atapi_qc_complete
  18. 2-> ata_qc_done
  19. 3-> ata_qc_free(qc); //释放qc资源
  20. 3-> qc->scsidone(qc->scsicmd); //调用scsi层的完成函数

ata_std_qc_defer

判断当前link是否繁忙,是否需要延迟处理当前命令:

上一个命令是非NCQ模式且未完成,则需要延迟处理。

  1. int ata_std_qc_defer(struct ata_queued_cmd *qc)
  2. {
  3. struct ata_link *link = qc->dev->link;
  4. if (ata_is_ncq(qc->tf.protocol)) {
  5. //NCQ模式下,link->active_tag存放的是上一条非NCQ模式命令的活跃状态。
  6. //即NCQ模式下,上一条非NCQ未完成的
  7. if (!ata_tag_valid(link->active_tag))
  8. return 0;
  9. } else {
  10. //非NCQ模式下,link->active_tag表示正在执行的命令tag号,link->sactive固定=0
  11. if (!ata_tag_valid(link->active_tag) && !link->sactive)
  12. return 0;
  13. }
  14. return ATA_DEFER_LINK;
  15. }

scsi_execute_req 同步命令访问

  1. 通过blk层来调用的访问接口
  2. scsi_execute_req
  3. -> scsi_execute -> __scsi_execute
  4. -> blk_get_request //从request_queue中分配空闲的request
  5. -> scsi_req //request -> scsi_request
  6. -> blk_rq_map_kern // 映射内核数据到一个块设备驱动层请求。buffer -> bio,bio通过blk_rq_append_bio放入到request中。还有一个对应用户数据接口blk_rq_map_user,映射用户数据到一个块设备驱动层请求
  7. -> blk_execute_rq //将request插入到I/O调度器队列,at_head==1说明将request插入到队列头部
  8. blk_execute_rq //是块I/O子系统提供的公共函数,此接口会等待命令完成
  9. -> blk_execute_rq_nowait //此接口不会等待命令完成,通过done回调函数中complete来唤醒后面的wait_for_completion_io等待
  10. -> blk_mq_sched_insert_request //Linux block 层IO请求处理过程,流程略
  11. -> wait_for_completion_io

Linux block 层IO请求处理过程参考 

blk_rq_map_kern将内核数据映射到request。

• int blk_rq_map_kern(struct request_queue *, struct request *, void *,unsigned int, gfp_t)映射内核数据到一个块设备驱动层请求,用于REQ_TYPE_BLOCK_PC;

• int blk_rq_map_user(struct request_queue *, struct request *, structrq_map_data *, void __user *, unsigned long, gfp_t)映射用户数据到一个块设备驱动层请求,用户与REQ_TYPE_BLOCK_PC;

• int blk_rq_map_sg(struct request_queue *, struct request *, structscatterlist *)映射一个块设备驱动层请求到聚散列表;

• int blk_rq_map_integrity_sg(struct request *, struct scatterlist *)映射块设备驱动层请求中的完整性向量到聚散列表。

命令完成 ata_qc_complete 、 ata_qc_complete_multiple

  1. 命令发起过程出错后调用 ata_qc_complete
  2. 命令处理完成后的中断函数中调用 ata_qc_complete_multiple->ata_qc_complete
  3. 0-> ahci_handle_port_interrupt //读取SActive(SCR3)寄存器获取qc_active
  4. 1-> ata_qc_complete_multiple //将qc_active转换为完成命令的tag
  5. 2-> ata_qc_from_tag //通过tag获取命令qc
  6. 2-> ata_qc_complete
  7. 3-> __ata_qc_complete
  8. 4-> ata_sg_clean //清除qc中的sg指针
  9. 4-> link->sactive &= ~(1 << qc->hw_tag); //清除NCQ的对应tag
  10. 4-> qc->complete_fn 调用执行ata_qc_issue前注册的完成函数

tag active相关变量

link->active_tag; 用于非NCQ,0~32

        数值型

        表示非NCQ模式正在执行的命令tag, 等于ATA_TAG_POISON(0xfafbfcfdU)时表示空闲。

        非NCQ模式下link->active_tag=qc->tag;命令完成后置为空闲。

        NCQ模式link->active_tag不会被使用。但在libata内部命令中会置为空闲,然而,libata内部根本不会使用NCQ模式。所以NCQ模式下,link->active_tag代表了上一条非NCQ命令的状态

link->sactive; 用于NCQ,0~0xffffffff

        bitmask型

        NCQ模式表示该link下正活跃的命令,非NCQ模式下link->sactive=0;

qc->hw_tag; 0~31

        数值型

        表示当前命令使用的真实命令号tag,寄存器操作时都是使用的qc->hw_tag。一般情况qc->hw_tag==qc->tag。

qc->tag; 0~32, 32表示libata内部命令

        数值型

        libata库使用的tag,与真实的tag有区别,该tag可以=32(ATA_TAG_INTERNAL),表示是内部使用的tag,ATA最多支持32个命令,所以对硬件来说32是非法的,软件层面用32来表示libata内部专用命令

ap->qc_active; 0~0xffffffff

        bitmask型

        无论是否为NCQ模式,用于表示该ata port下正活跃的命令,

        link->sactive的区别,link->sactive只用于NCQ模式,一个port下可最多16个link,而一个link才代表了一个硬盘,所以判断一个命令是否繁忙是使用link->sactive而非ap->qc_active

        1个port的最多16个link的中断都会汇集到该port上,在中断处理函数中ap->qc_active用来对产生中断的tag进行筛选,以保证程序的正常运行。

ap->nr_active_links;

        有正在执行的qc的link数量

核心ATA发送命令 ata_qc_issue

所有发送ata的命令都会调用到此接口。

  1. //libata-core.c
  2. 0-> ata_qc_issue //分发taskfile到device,taskfile存放于qc中
  3. 1-> link->sactive |= 1 << qc->hw_tag; //NCQ模式下使用sactive的bit表示待处理的多个命令, 命令完成后清除sactive对应bit
  4. 1-> link->active_tag = qc->tag; //非NCQ模式使用link->active_tag表示待处理的单个命令, 命令完成后active_tag=ATA_TAG_POISON表示空闲。
  5. 1-> ata_sg_setup //如果为DMA模式,则设置sgl
  6. 2-> dma_map_sg //重新生成sgl表,以减少链表个数,并映射物理地址到io虚拟地址(dma_address)
  7. 1-> ap->ops->qc_prep(qc); //ahci_qc_prep 将qc命令拷贝到正确的位置,准备发送
  8. 1-> ap->ops->qc_issue(qc); //ahci_qc_issue 发送qc命令

ahci_qc_prep

命令准备,将qc中的命令移到AHCI规定的Command Header及Command Table中

  1. struct ahci_cmd_hdr {
  2. __le32 opts;
  3. __le32 status;
  4. __le32 tbl_addr;
  5. __le32 tbl_addr_hi;
  6. __le32 reserved[4];
  7. };
  8. struct ahci_sg {
  9. __le32 addr;
  10. __le32 addr_hi;
  11. __le32 reserved;
  12. __le32 flags_size;
  13. };
  14. 0-> ahci_qc_prep
  15. 1-> ata_tf_to_fis //将tf的各个值填入到cfis中对应字段
  16. 1-> memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len); //如果是scsi的命令,则拷贝cdb命令到ACMD中,即使用SATA中的ATAPI命令。
  17. 1-> ahci_fill_sg //将上层的sgl中的sg一个一个填入到PRD表中
  18. 1-> ahci_fill_cmd_slot //填充command header,command header的地址初始化阶段已被写到了PxCLB和PxCLBU寄存器中
  19. 2-> opts = cmd_fis_len | n_elem << 16 | (qc->dev->link->pmp << 12);
  20. opts |= AHCI_CMD_WRITE; //写命令
  21. opts |= AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH; //atapi命令
  22. DW0=opts
  23. 2-> 清零PRDBC,该字段用于当前已经完成的读写字节数
  24. 2-> 将本命令的内存的DMA地址赋值给CTBA
  25. Command Header初始化位置:
  26. ahci_port_resume
  27. ->ahci_start_port
  28. ->ahci_start_fis_rx
  29. ->writel(pp->cmd_slot_dma & 0xffffffff, port_mmio + PORT_LST_ADDR);

如图可知AHCI_CMD_TBL_SZ = 0x80 + 64K * 16,但Linux驱动中没有使用64K个PRD,只使用了168个,所以AHCI_CMD_TBL_SZ = 0x80 + 168 * 16;

ahci_qc_issue

让HBA发送命令

  1. 0-> ahci_qc_issue
  2. 1-> 往SActive(SCR3)寄存器写入tag //如果为NCQ模式则进行此步骤,用于表示该命令待处理,此寄存器通过软件只能由01,所以只需对对应bit1即可,无需读-改-写。
  3. 1-> FBS模式下配置FBS相关寄存器
  4. 1-> PxCI.CI对应tag位置1

端口恢复 ahci_port_resume

  1. //初始化时
  2. ahci_host_activate
  3. ->ata_host_activate
  4. ->ahci_port_start
  5. ->ahci_port_resume
  6. //运行时
  7. ata_port_schedule_eh
  8. ->ata_std_sched_eh
  9. ->scsi_schedule_eh
  10. ->scsi_error_handler//SCSI的主错误处理内核态线程, 通过scsi_eh_wakeup唤醒
  11. ->ata_scsi_error
  12. ->ata_scsi_port_error_handler
  13. ->ata_eh_handle_port_resume
  14. ->ahci_port_resume
  15. //端口恢复
  16. ahci_port_resume
  17. -> ahci_power_up
  18. ->配置PxCMD寄存器,开启Staggered Spin-up(交错启动模式),切换到Active模式
  19. -> ahci_start_port
  20. -> ahci_start_fis_rx
  21. -> 配置PxCLB、PxCLBU、PxFB、PxFBU寄存器
  22. -> 配置PxCMD寄存器,使能接收FIS
  23. -> ahci_start_engine
  24. -> 配置PxCMD寄存器,使能端口DMA引擎
  25. -> 配置LED闪烁定时器及回调函数

ahci_stop_engine

  1. int ahci_stop_engine(struct ata_port *ap)
  2. {
  3. void __iomem *port_mmio = ahci_port_base(ap);
  4. struct ahci_host_priv *hpriv = ap->host->private_data;
  5. u32 tmp;
  6. ......
  7. tmp = readl(port_mmio + PORT_CMD);
  8. ......
  9. /* setting HBA to idle */
  10. tmp &= ~PORT_CMD_START;
  11. writel(tmp, port_mmio + PORT_CMD);
  12. /* wait for engine to stop. This could be as long as 500 msec */
  13. tmp = ata_wait_register(ap, port_mmio + PORT_CMD,
  14. PORT_CMD_LIST_ON, PORT_CMD_LIST_ON, 1, 500);
  15. if (tmp & PORT_CMD_LIST_ON)
  16. return -EIO;
  17. return 0;
  18. }

ahci_start_engine

  1. void ahci_start_engine(struct ata_port *ap)
  2. {
  3. void __iomem *port_mmio = ahci_port_base(ap);
  4. u32 tmp;
  5. /* start DMA */
  6. tmp = readl(port_mmio + PORT_CMD);
  7. tmp |= PORT_CMD_START;
  8. writel(tmp, port_mmio + PORT_CMD);
  9. readl(port_mmio + PORT_CMD); /* flush */
  10. }

ahci_stop_fis_rx

  1. static int ahci_stop_fis_rx(struct ata_port *ap)
  2. {
  3. void __iomem *port_mmio = ahci_port_base(ap);
  4. u32 tmp;
  5. /* disable FIS reception */
  6. tmp = readl(port_mmio + PORT_CMD);
  7. tmp &= ~PORT_CMD_FIS_RX;
  8. writel(tmp, port_mmio + PORT_CMD);
  9. /* wait for completion, spec says 500ms, give it 1000 */
  10. tmp = ata_wait_register(ap, port_mmio + PORT_CMD, PORT_CMD_FIS_ON,
  11. PORT_CMD_FIS_ON, 10, 1000);
  12. if (tmp & PORT_CMD_FIS_ON)
  13. return -EBUSY;
  14. return 0;
  15. }

ahci_error_handler

  1. void ahci_error_handler(struct ata_port *ap)
  2. {
  3. struct ahci_host_priv *hpriv = ap->host->private_data;
  4. if (!(ap->pflags & ATA_PFLAG_FROZEN)) {
  5. /* restart engine */
  6. hpriv->stop_engine(ap); //ahci_stop_engine
  7. hpriv->start_engine(ap);//ahci_start_engine
  8. }
  9. sata_pmp_error_handler(ap);
  10. if (!ata_dev_enabled(ap->link.device))
  11. hpriv->stop_engine(ap);
  12. }

sata_pmp_error_handler

热插拔时

.error_handler = ahci_error_handler 

-> sata_pmp_error_handler(libata_pmp.c)

    ->sata_pmp_eh_recover(libata_pmp.c)

        ->ata_eh_recover(libata_eh.c)

            ->ata_eh_reset(libata_eh.c)

                ->.postreset

                    ->..-> ata_std_postreset(libata_core.c)

                             -> sata_print_link_status(libata_core.c)

  1. void sata_pmp_error_handler(struct ata_port *ap)
  2. {
  3.     ata_eh_autopsy(ap);
  4.     ata_eh_report(ap);
  5.     sata_pmp_eh_recover(ap);
  6.     ata_eh_finish(ap);
  7. }

ahci_port_suspend

  1. static int ahci_port_suspend(struct ata_port *ap, pm_message_t mesg)
  2. {
  3. const char *emsg = NULL;
  4. int rc;
  5. rc = ahci_deinit_port(ap, &emsg);
  6. if (rc == 0)
  7. ahci_power_down(ap);
  8. else {
  9. ata_port_err(ap, "%s (%d)\n", emsg, rc);
  10. ata_port_freeze(ap);
  11. }
  12. ahci_rpm_put_port(ap);
  13. return rc;
  14. }
  15. static int ahci_deinit_port(struct ata_port *ap, const char **emsg)
  16. {
  17. int rc;
  18. struct ahci_host_priv *hpriv = ap->host->private_data;
  19. /* disable DMA */
  20. rc = hpriv->stop_engine(ap);
  21. /* disable FIS reception */
  22. rc = ahci_stop_fis_rx(ap);
  23. return 0;
  24. }

更多AHCI的中断处理:Linux AHCI 驱动(中断部分)_chen_xing_hai的博客-CSDN博客

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/爱喝兽奶帝天荒/article/detail/1003478
推荐阅读
相关标签
  

闽ICP备14008679号