Linux AHCI驱动分析之块设备层
接上一篇博客Linux AHCI驱动分析之设备初始化参考ATA Disk在Linux中的驱动架构对比分析ata驱动框架及scsi请求处理流程ATA接口寄存器描述从ATA层向设备发送TRIM命令使用硬盘ATA命令读取磁盘scsi底层设备注册——如何一步步注册到block层Scsi命令队列转换为ata命令过程scsi设备的请求处理函数(request_fn)打开内核调试信息定义ATA_DEBUG和AT
作者
QQ群:852283276
微信:arm80x86
微信公众号:青儿创客基地
B站:主页 https://space.bilibili.com/208826118
接上一篇博客Linux AHCI驱动分析之设备初始化
参考
ATA Disk在Linux中的驱动架构对比分析
ata驱动框架及scsi请求处理流程
ATA接口寄存器描述
从ATA层向设备发送TRIM命令
使用硬盘ATA命令读取磁盘
scsi底层设备注册——如何一步步注册到block层
Scsi命令队列转换为ata命令过程
scsi设备的请求处理函数(request_fn)
libATA Developer’s Guide
块设备读写流程
块设备读写流程
Linux Block Layer块设备层基于MultiQueue的部分源码分析
为request的每一个bio创建DMA映射
Linux kernel scatterlist API介绍
打开内核调试信息
定义ATA_DEBUG
和ATA_VERBOSE_DEBUG
,
//include\linux\libata.h
/*
* compile-time options: to be removed as soon as all the drivers are
* converted to the new debugging mechanism
*/
#undef ATA_DEBUG /* debugging output */
#undef ATA_VERBOSE_DEBUG /* yet more debugging output */
#undef ATA_IRQ_TRAP /* define to ack screaming irqs */
#undef ATA_NDEBUG /* define to disable quick runtime checks */
/* note: prints function name for you */
#ifdef ATA_DEBUG
#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args)
#ifdef ATA_VERBOSE_DEBUG
#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args)
#else
#define VPRINTK(fmt, args...)
#endif /* ATA_VERBOSE_DEBUG */
#else
#define DPRINTK(fmt, args...)
#define VPRINTK(fmt, args...)
#endif /* ATA_DEBUG */
//drivers\ata\libata-core.c
struct ata_port *ata_port_alloc(struct ata_host *host)
{
...
#if defined(ATA_VERBOSE_DEBUG)
/* turn on all debugging levels */
ap->msg_enable = 0x00FF;
#elif defined(ATA_DEBUG)
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_INFO | ATA_MSG_CTL | ATA_MSG_WARN | ATA_MSG_ERR;
#else
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN;
#endif
...
}
驱动模型
驱动模型如下图,通过LibATA驱动作为SCSI Middle Level与ATA Host之间的转换层,从而可以很好的将ATA Host直接融入到SCSI的驱动体系中来,可以直接将ATA设备驱成SCSI Device。
注册块设备
下面进入ata_host_register
函数,
ata_host_register //drivers\ata\libata-core.c
host->n_tags = clamp(sht->can_queue, 1, ATA_MAX_QUEUE - 1); //can_queue = AHCI_MAX_CMDS - 1, n_tags=31, tag ATA_MAX_QUEUE - 1 is reserved for internal commands
ata_tport_add
ata_scsi_add_hosts //drivers\ata\libata-scsi.c
scsi_host_alloc
shost->hostt = sht; // struct scsi_host_template *sht
shost->can_queue = sht->can_queue; //struct scsi_host_template
shost->sg_tablesize = sht->sg_tablesize; //struct scsi_host_template
shost->use_blk_mq = scsi_use_blk_mq && !shost->hostt->disable_blk_mq; //scsi_use_blk_mq is from Kconfig
shost->max_cmd_len = 16;
scsi_add_host_with_dma //drivers\scsi\hosts.c
scsi_use_blk_mq: scsi_mq_setup_tags //mq tagset
scsi_setup_command_freelist
scsi_get_host_cmd_pool
scsi_host_alloc_command
scsi_host_set_state(shost, SHOST_RUNNING)
scsi_sysfs_add_host
scsi_proc_host_add
sata_link_init_spd
ata_pack_xfermask
async_schedule(async_port_probe, ap)
ata_port_probe
__ata_port_probe
ata_port_wait_eh
ata_scsi_scan_host //drivers\ata\libata-scsi.c
__scsi_add_device //drivers\scsi\scsi_scan.c
scsi_alloc_target
scsi_probe_and_add_lun
scsi_device_lookup_by_target
scsi_alloc_sdev
scsi_mq_alloc_queue/scsi_alloc_queue
scsi_change_queue_depth
scsi_sysfs_device_initialize
scsi_probe_lun //探测lun
scsi_execute_req: INQUIRY
scsi_execute_req_flags //drivers\scsi\scsi_lib.c
scsi_execute
blk_get_request
blk_rq_set_block_pc
blk_rq_map_kern
blk_execute_rq
scsi_add_lun //drivers\scsi\scsi_scan.c
scsi_sysfs_add_sdev //drivers\scsi\scsi_sysfs.c
device_add //触发上层probe,
bsg_register_queue //block\bsg.c
scsi_target_reap
块设备队列
队列创建,支持多队列和传统的单队列,Linux内核默认是单队列(3.19,4.14),请求分发函数为scsi_queue_rq/scsi_request_fn
__scsi_init_queue //drivers\scsi\scsi_lib.c
blk_queue_max_segments
blk_queue_max_hw_sectors
blk_queue_bounce_limit
blk_queue_segment_boundary
dma_set_seg_boundary
blk_queue_max_segment_size
blk_queue_dma_alignment
scsi_alloc_queue //传统单队列
__scsi_alloc_queue
blk_init_queue
__scsi_init_queue
blk_queue_prep_rq(q, scsi_prep_fn); //设置prep_rq函数
blk_queue_unprep_rq(q, scsi_unprep_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);
static struct blk_mq_ops scsi_mq_ops = {
.map_queue = blk_mq_map_queue,
.queue_rq = scsi_queue_rq,
.complete = scsi_softirq_done,
.timeout = scsi_timeout,
.init_request = scsi_init_request,
.exit_request = scsi_exit_request,
};
scsi_mq_alloc_queue //多队列mq
blk_mq_init_queue
__scsi_init_queue
以传统单队列为例,
scsi_request_fn //drivers\scsi\scsi_lib.c
blk_peek_request
ret = q->prep_rq_fn(q, rq); //scsi_prep_fn
scsi_prep_state_check
scsi_get_cmd_from_req //构造struct scsi_cmnd
scsi_setup_cmnd
scsi_setup_fs_cmnd/scsi_setup_blk_pc_cmnd
scsi_prep_return
scsi_dev_queue_ready
blk_start_request
struct scsi_cmnd *cmd = req->special;
scsi_target_queue_ready
scsi_host_queue_ready
scsi_init_cmd_errh
cmd->scsi_done = scsi_done; //中断函数中会用到
scsi_dispatch_cmd
scsi_log_send
host->hostt->queuecommand;
scsi_queue_insert
其中host->hostt->queuecommand
来自struct scsi_host_template
,对应ata_scsi_queuecmd
函数,函数ata_sg_setup
中调用dma_map_sg
,对于我们的异构系统,需要修改的就是这个函数,
ata_scsi_queuecmd //drivers\ata\libata-scsi.c
ata_shost_to_port
ata_scsi_dump_cdb
ata_scsi_find_dev
__ata_scsi_queuecmd
ata_get_xlat_func/atapi_xlat //check if SCSI to ATA translation is possible
ata_scsi_translate/ata_scsi_simulate
ata_scsi_translate
ata_scsi_qc_new
qc->scsicmd = cmd;
qc->scsidone = cmd->scsi_done;
qc->sg = scsi_sglist(cmd);
qc->n_elem = scsi_sg_count(cmd);
ata_sg_init
qc->complete_fn = ata_scsi_qc_complete;
ata_get_xlat_func[ata_scsi_rw_xlat/ata_scsi_pass_thru]/atapi_xlat //translate
struct ata_taskfile *tf //构建tf
ap->ops->qc_defer //ahci_pmp_qc_defer, deferred:推迟; 延缓; 展期 drivers\ata\libahci.c
ata_std_qc_defer/sata_pmp_qc_defer_cmd_switch
ata_qc_issue
ata_sg_setup
dma_map_sg
ap->ops->qc_prep //ahci_qc_prep drivers\ata\libahci.c
ata_tf_to_fis //tf转fis,协议Command Table的CFIS
memcpy(cmd_tbl + AHCI_CMD_TBL_CDB, qc->cdb, qc->dev->cdb_len) //协议Command Table的ACMD
ahci_fill_sg
ahci_fill_cmd_slot
ap->ops->qc_issue //ahci_qc_issue drivers\ata\libahci.c
writel(1 << qc->tag, port_mmio + PORT_SCR_ACT);
writel(fbs, port_mmio + PORT_FBS);
writel(1 << qc->tag, port_mmio + PORT_CMD_ISSUE);
scsi驱动
scsi驱动位于drivers\scsi\sd.c
,
init_sd //drivers\scsi\sd.c
register_blkdev
blk_register_region
class_register
scsi_register_driver
其中scsi_register_driver(&sd_template.gendrv)
,上面说的probe函数即sd_probe
static struct scsi_driver sd_template = {
.gendrv = {
.name = "sd",
.owner = THIS_MODULE,
.probe = sd_probe,
.remove = sd_remove,
.shutdown = sd_shutdown,
.pm = &sd_pm_ops,
},
.rescan = sd_rescan,
.init_command = sd_init_command,
.uninit_command = sd_uninit_command,
.done = sd_done,
.eh_action = sd_eh_action,
};
sd_probe
函数,磁盘的队列就是用的scsi_device
里的队列,不需要新创建了,
sd_probe //drivers\scsi\sd.c
alloc_disk
sd_format_disk_name
sd_probe_async
gd->fops = &sd_fops;
gd->queue = sdkp->device->request_queue; //scsi_disk->scsi_device->request_queue
sd_revalidate_disk
add_disk
sd_dif_config_host
sd_revalidate_disk
其中sd_fops
如下,
static const struct block_device_operations sd_fops = {
.owner = THIS_MODULE,
.open = sd_open,
.release = sd_release,
.ioctl = sd_ioctl,
.getgeo = sd_getgeo,
#ifdef CONFIG_COMPAT
.compat_ioctl = sd_compat_ioctl,
#endif
.check_events = sd_check_events,
.revalidate_disk = sd_revalidate_disk,
.unlock_native_capacity = sd_unlock_native_capacity,
};
sd_ioctl
scsi_verify_blk_ioctl
scsi_ioctl/scsi_cmd_blk_ioctl
其中,
scsi_ioctl //block\scsi_ioctl.c
sg_scsi_ioctl/sdev->host->hostt->ioctl //ata_scsi_ioctl
scsi_cmd_blk_ioctl
scsi_verify_blk_ioctl
scsi_cmd_ioctl
sg_io
ata_scsi_ioctl //drivers\ata\libata-scsi.c
ata_sas_scsi_ioctl
ATA_IOC_GET_IO32
ATA_IOC_SET_IO32
HDIO_GET_IDENTITY ata_get_identity
HDIO_DRIVE_CMD ata_cmd_ioctl
HDIO_DRIVE_TASK ata_task_ioctl
中断
以传统单队列为例,
ahci_single_irq_intr //drivers\ata\libahci.c
ahci_port_intr
ahci_handle_port_interrupt
ata_qc_complete_multiple //drivers\ata\libata-core.c
ata_qc_from_tag
ata_qc_complete
__ata_qc_complete
ata_sg_clean
dma_unmap_sg
qc->complete_fn //ata_scsi_qc_complete drivers\ata\libata-scsi.c
ata_gen_passthru_sense/ata_gen_ata_sense
ata_dump_status
qc->scsidone //scsi_done drivers\scsi\scsi_lib.c
ata_qc_free
更多推荐
所有评论(0)