接上一篇博客Linux AHCI驅動分析之設備初始化
參考
ATA Disk在Linux中的驅動架構對比分析
ata驅動框架及scsi請求處理流程
ATA接口寄存器描述
從ATA層向設備發送TRIM命令
使用硬盤ATA命令讀取磁盤
scsi底層設備註冊——如何一步步註冊到block層
Scsi命令隊列轉換爲ata命令過程
scsi設備的請求處理函數(request_fn)
libATA Developer’s Guide
塊設備讀寫流程
塊設備讀寫流程
打開內核調試信息
定義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