SATA学习之一 代码分析和学习心得

时间:2021-08-25 16:22:20
sata_fsl_init
=>platform_driver_register(&fsl_sata_driver);//注册platform驱动
static struct platform_driver fsl_sata_driver = {
.driver = {
.name = "fsl-sata",
.owner = THIS_MODULE,
.of_match_table = fsl_sata_match,
},
.probe = sata_fsl_probe,
.remove = sata_fsl_remove,
};
static struct of_device_id fsl_sata_match[] = {//匹配设备树
{
.compatible = "fsl,pq-sata",
},
{
.compatible = "fsl,pq-sata-v2",
},
{},
};

内核初始化解析设备树并注册sata的platform设备,触发fsl_sata_match后调用sata_fsl_probe



sata_fsl_probe
=>struct ata_port_info pi = sata_fsl_port_info[0];//指针函数初始化,都是套路
static const struct ata_port_info sata_fsl_port_info[] = {
{
.flags = SATA_FSL_HOST_FLAGS,
.pio_mask = ATA_PIO4,
.udma_mask = ATA_UDMA6,
.port_ops = &sata_fsl_ops,
},
};
static struct ata_port_operations sata_fsl_ops = {
.inherits = &sata_pmp_port_ops,

.qc_defer = ata_std_qc_defer,
.qc_prep = sata_fsl_qc_prep,
.qc_issue = sata_fsl_qc_issue,
.qc_fill_rtf = sata_fsl_qc_fill_rtf,

.scr_read = sata_fsl_scr_read,
.scr_write = sata_fsl_scr_write,

.freeze = sata_fsl_freeze,
.thaw = sata_fsl_thaw,
.softreset = sata_fsl_softreset,
.hardreset = sata_fsl_hardreset,
.pmp_softreset = sata_fsl_softreset,
.error_handler = sata_fsl_error_handler,
.post_internal_cmd = sata_fsl_post_internal_cmd,

.port_start = sata_fsl_port_start,
.port_stop = sata_fsl_port_stop,

.pmp_attach = sata_fsl_pmp_attach,
.pmp_detach = sata_fsl_pmp_detach,
};
=>hcr_base = of_iomap(ofdev->dev.of_node, 0);//解析设备树,获取基地址和中断号并存放在私有数据里面
ssr_base = hcr_base + 0x100;
csr_base = hcr_base + 0x140;
host_priv = kzalloc(sizeof(struct sata_fsl_host_priv), GFP_KERNEL);
host_priv->hcr_base = hcr_base;
host_priv->ssr_base = ssr_base;
host_priv->csr_base = csr_base;
irq = irq_of_parse_and_map(ofdev->dev.of_node, 0);
host_priv->irq = irq;
=>host = ata_host_alloc_pinfo(&ofdev->dev, ppi, SATA_FSL_MAX_PORTS);//第一个LIBATA API,初始化host structure
=>host = ata_host_alloc(dev, n_ports);//申请控制器
=>for (i = 0; i < max_ports; i++)
ap = ata_port_alloc(host);//申请port
=>ap = kzalloc(sizeof(*ap), GFP_KERNEL);//初始化ap
INIT_DELAYED_WORK(&ap->hotplug_task, ata_scsi_hotplug);//下面这一堆线程后面有用
INIT_WORK(&ap->scsi_rescan_task, ata_scsi_dev_rescan);
INIT_LIST_HEAD(&ap->eh_done_q);
init_waitqueue_head(&ap->eh_wait_q);
ata_link_init(ap, &ap->link, 0);//初始化端口下的设备,1个端口下最多2个
ata_sff_port_init(ap);
host->ports[i] = ap;

host->private_data = host_priv;
sata_fsl_init_controller(host);//里面一坨寄存器初始化
=>ata_host_activate(host, irq, sata_fsl_interrupt, SATA_FSL_IRQ_FLAG, &sata_fsl_sht);//第二个LIBATA API,里面有如下动作
register with libata core
initiate the device discovery process
invoke our port_start hander & error_hander to execute a dummy softreset EH session
=>ata_host_start(host);
=>ata_finalize_port_ops(host->ops);//固化指针
=>for (i = 0; i < host->n_ports; i++)
ata_finalize_port_ops(ap->ops);//固化指针
=>ap->ops->port_start(ap);
=>sata_fsl_port_start//申请并初始化CHBA相关的结构体,并赋值给CHBA寄存器,建立FIS区域,用于控制DMA数据传输
=>ata_eh_freeze_port(ap);
=>__ata_port_freeze(ap);
=>ap->ops->freeze(ap);
=>sata_fsl_freeze/* disable interrupts on the controller/port */
=>ap->pflags |= ATA_PFLAG_FROZEN//没有初始化完,所以freeze
=>host->flag |= ATA_HOST_STARTED//标记host位已经启动标记
=>rc = devm_request_irq(host->dev, irq, irq_handler, irq_flags, dev_driver_string(host->dev), host);//注册中断
=>ata_host_register(host, sht);//register initialized ATA host
=>ata_scsi_add_hosts(host, sht);//从这里链接到scsi代码,注册ata_host,二注册ata_host本质是通过libata转换注册scsi_host
=>shost = scsi_host_alloc(sht, sizeof(struct ata_port *));//分配资源
=>ap->scsi_host = shost;//scsi host初始化
shost->transportt = ata_scsi_transport_template;
shost->unique_id = ap->print_id;
shost->max_id = 16;
shost->max_lun = 1;
shost->max_channel = 1;
shost->max_cmd_len = 16;
=>*(struct ata_port **)&shost->hostdata[0] = ap;//ata host与scsi host生死相依
ap->scsi_host = shost;
=>scsi_add_host(ap->scsi_host, ap->host->dev);
=>struct Scsi_Host *shost;
=>shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask);
shost->shost_gendev.bus = &scsi_bus_type;
shost->shost_gendev.type = &scsi_host_type;
device_initialize(&shost->shost_dev);
shost->shost_dev.parent = &shost->shost_gendev;
shost->shost_dev.class = &shost_class;
dev_set_name(&shost->shost_dev, "host%d", shost->host_no);
shost->shost_dev.groups = scsi_sysfs_shost_attr_groups;
shost->ehandler = kthread_run(scsi_error_handler, shost, "scsi_eh_%d", shost->host_no);
=>for (i = 0; i < host->n_ports; i++)
async_schedule(async_port_probe, ap);

scsi_error_handler和async_port_probe这两个函数很重要也很复杂,单独拎出来分析说明

scsi_error_handler//ata卡硬件初始化与故障恢复流程应该处理很类似,所以归一了
=>schedule();//进入休眠阻塞状态,async_port_probe主流程会走到一定阶段会将其唤醒
set_current_state(TASK_INTERRUPTIBLE);
=>shost->transportt->eh_strategy_handler(shost);
i->t.eh_strategy_handler = ata_scsi_error;
i->t.eh_timed_out = ata_scsi_timed_out;
=>ata_scsi_error
=>ata_scsi_cmd_error_handler(host, ap, &eh_work_q);
=>ata_scsi_port_error_handler(host, ap);
=>ap->ops->error_handler(ap);
=>sata_fsl_error_handler
=>sata_pmp_error_handler(ap);
=>ata_eh_autopsy(ap);
=>ata_eh_link_autopsy(link);//读取错误寄存器,根据不同的错误类型作标记放在flag里,然后标记action,例如ehc->i.action |= ATA_EH_REVALIDATE
=>rc = sata_scr_read(link, SCR_ERROR, &serror);
/* obtain and analyze SError */
=>ata_eh_analyze_serror(link);
=>ata_eh_speed_down(dev, eflags, all_err_mask);
降速和将DMA->PIO,只是修改配置,设备重新初始化生效
=>ata_eh_report(ap);
=>ata_for_each_link(link, ap, HOST_FIRST)
ata_eh_link_report(link);//打印调试信息
=>sata_pmp_eh_recover(ap);
=>ata_eh_recover(ap, ops->prereset, ops->softreset, ops->hardreset, ops->postreset, NULL);
=>ata_for_each_link(link, ap, EDGE)
ata_eh_reset(link, ata_link_nr_vacant(link), prereset, softreset, hardreset, postreset);
=>if (hardreset) {
reset = hardreset;
ehc->i.action |= ATA_EH_HARDRESET;
} else if (softreset) {
reset = softreset;
ehc->i.action |= ATA_EH_SOFTRESET;
}
=>rc = prereset(link, deadline);
=>rc = ata_do_reset(link, reset, classes, deadline, true);
=>reset
=>hardreset
=>sata_fsl_hardreset//复位解复位SATA控制器
=>postreset(link, classes);//打印一堆sata link状态
=>ata_dev_read_id(dev, &dev->class, readid_flags, dev->id);
=>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);
=>qc一堆赋值
=>ata_qc_issue(qc);
=>ap->ops->qc_prep(qc);
sata_fsl_qc_prep//根据命令填充CHBA相关命令结构体
=>qc->err_mask |= ap->ops->qc_issue(qc);
sata_fsl_qc_issue
=>iowrite32(qc->dev->link->pmp, CQPMP + hcr_base);
=>iowrite32(1 << tag, CQ + hcr_base);
/* Simply queue command to the controller/device */
=>wait_for_completion_timeout(&wait, msecs_to_jiffies(timeout));//申请QC队列,command queue register, 触发命令,将控制权交给sata控制器,控制器处理完毕后触发中断,中断完成后才能返回
=>ata_sff_flush_pio_task(ap);
=>ata_eh_finish(ap);
=>schedule_delayed_work(&ap->hotplug_task, 0);//唤醒hostplug_task
=>ata_scsi_hotplug//线程动态探测sata卡是否热插拔
=>ata_scsi_handle_link_detach(&ap->link);
=>ata_scsi_scan_host(ap, 0); /* scan for new ones */
=>ata_for_each_link(link, ap, EDGE)
ata_for_each_dev(dev, link, ENABLED)
sdev = __scsi_add_device(ap->scsi_host, channel, id, 0, NULL);//注册scsi host控制器
=>queue_delayed_work(system_long_wq, &ap->hotplug_task, round_jiffies_relative(HZ));//1s后重新唤醒
=>wake_up_all(&ap->eh_wait_q);//唤醒之前强制等待的初始化主线程,在async_port_probe流程里面

async_port_probe//这是主流程,但是会被堵塞,待scsi_error_handler初始化注册完毕scsi host之后通过wake_up_all(&ap->eh_wait_q)会唤醒继续ata_scsi_add_host探测scsi卡
=>ata_port_probe
=>ehi->probe_mask |= ATA_ALL_DEVICES;
ehi->action |= ATA_EH_RESET;
ehi->flags |= ATA_EHI_NO_AUTOPSY | ATA_EHI_QUIET;
ap->pflags &= ~ATA_PFLAG_INITIALIZING;
ap->pflags |= ATA_PFLAG_LOADING;
ata_port_schedule_eh(ap);
=>scsi_schedule_eh(ap->scsi_host);
=>scsi_eh_wakeup(shost);
=>wake_up_process(shost->ehandler);//唤醒scsi_error_handler
=>ata_port_wait_eh(ap);
=>prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE);//休眠在这里,等待被scsi_error_handler唤醒
=>finish_wait(&ap->eh_wait_q, &wait);
=>ata_scsi_scan_host(ap, 1);
=>ata_for_each_link(link, ap, EDGE)
ata_for_each_dev(dev, link, ENABLED)
__scsi_add_device(ap->scsi_host, channel, id, 0, NULL);
=>scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
=>sdev = scsi_alloc_sdev(starget, lun, hostdata);//scsi总线扫描时,每当探测到1个设备,就会调用该函数
=>sdev->vendor = scsi_null_device_strs;
sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs;
sdev->host = shost;
sdev->queue_ramp_up_period = SCSI_DEFAULT_RAMP_UP_PERIOD;
sdev->id = starget->id;
sdev->lun = lun;
sdev->channel = starget->channel;
sdev->sdev_state = SDEV_CREATED;
sdev->sdev_gendev.parent = get_device(&starget->dev);
sdev->sdev_target = starget;
/* usually NULL and set by ->slave_alloc instead */
sdev->hostdata = hostdata;
sdev->request_queue = scsi_alloc_queue(sdev);
=>q = __scsi_alloc_queue(sdev->host, scsi_request_fn);
=>q = blk_init_queue(request_fn, NULL);
=>blk_init_queue_node(rfn, lock, -1);
=>uninit_q = blk_alloc_queue_node(GFP_KERNEL, node_id);
=>q = blk_init_allocated_queue_node(uninit_q, rfn, lock, node_id);
=>q = kmem_cache_alloc_node(blk_requestq_cachep, gfp_mask | __GFP_ZERO, node_id);
err = bdi_init(&q->backing_dev_info);
setup_timer(&q->backing_dev_info.laptop_mode_wb_timer, laptop_mode_timer_fn, (unsigned long) q);
setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);
INIT_LIST_HEAD(&q->timeout_list);
INIT_LIST_HEAD(&q->flush_queue[0]);
INIT_LIST_HEAD(&q->flush_queue[1]);
INIT_LIST_HEAD(&q->flush_data_in_flight);
INIT_DELAYED_WORK(&q->delay_work, blk_delay_work);

kobject_init(&q->kobj, &blk_queue_ktype);
=>elevator_init(q, NULL)//IO调度读初始化
=>e = elevator_get(chosen_elevator)或者e = elevator_get(CONFIG_DEFAULT_IOSCHED)
=>eq = elevator_alloc(q, e);
=>data = elevator_init_queue(q, eq);
=>elevator_attach(q, eq, data);
=>blk_queue_prep_rq(q, scsi_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_sysfs_device_initialize(sdev);
=>device_initialize(&sdev->sdev_gendev);//linux设备驱动模型,设备总线名称以及设备类型
=>sdev->sdev_gendev.bus = &scsi_bus_type;
struct bus_type scsi_bus_type = {
.name = "scsi",
.match = scsi_bus_match,
.uevent = scsi_bus_uevent,
};
static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
{
struct scsi_device *sdp;

if (dev->type != &scsi_dev_type)
return 0;

sdp = to_scsi_device(dev);
if (sdp->no_uld_attach)
return 0;
return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
//define SCSI_INQ_PQ_CON 0x00//在scsi_add_lun赋值
}
=>sdev->sdev_gendev.type = &scsi_dev_type;
=>scsi_probe_lun(sdev, result, result_len, &bflags)
=>打印scsi硬盘信息,例如scsi 0:0:0:0 Direct Access ATA
=>sdev->scsi_level = inq_result[2] & 0x07;//scsi_bus_type match函数需要该参数
=>scsi_sysfs_add_sdev(sdev) //device add, CF卡注册设备,可以触发sd模块的probe,即sd_probe
=>error = device_add(&sdev->sdev_gendev);
=>error = device_add(&sdev->sdev_dev);

SD卡驱动注册流程:
init_sd
=>for (i = 0; i < SD_MAJORS; i++)
register_blkdev(sd_major(i), "sd")
=>scsi_register_driver(&sd_template.gendrv);//scsi驱动注册,触发match,即scsi_bus_match,match后调用sd_probe
static struct scsi_driver sd_template = {
.owner = THIS_MODULE,
.gendrv = {
.name = "sd",
.probe = sd_probe,
.remove = sd_remove,
.suspend = sd_suspend,
.resume = sd_resume,
.shutdown = sd_shutdown,
},
.rescan = sd_rescan,
.done = sd_done,
};

sd_probe
=>sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);
=>gd = alloc_disk(SD_MINORS);//申请gendisk结构体并初始化
=>alloc_disk_node(minors, -1);
=>disk = kmalloc_node(sizeof(struct gendisk), GFP_KERNEL | __GFP_ZERO, node_id);
=>int size = (minors - 1) * sizeof(struct hd_struct *);//子分区的结构体申请
disk->part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, node_id);
disk->minors = minors;
kobj_set_kset_s(disk,block_subsys);
kobject_init(&disk->kobj);
rand_initialize_disk(disk);
INIT_WORK(&disk->async_notify, media_change_notify_thread);
=>sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN);//生成一堆sda sdb
=>sdkp->device = sdp;
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
sdkp->device = sdp;
sdkp->driver = &sd_template;
sdkp->disk = gd;
sdkp->index = index;
=>device_add(&sdkp->dev)
=>async_schedule(sd_probe_async, sdkp);
sd_probe_async
=>gd->fops = &sd_fops;
gd->private_data = &sdkp->driver;
gd->queue = sdkp->device->request_queue;
=>sd_revalidate_disk(gd);
=>scsi_device_online(sdp)
=>sd_spinup_disk(sdkp);//发scsi命令,SCSI设备OK了吗
=>cmd[0] = TEST_UNIT_READY;
memset((void *) &cmd[1], 0, 9);
=>scsi_execute_req(sdkp->device, cmd, DMA_NONE, NULL, 0, &sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL);
=>scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense, timeout, retries, 0, resid);
=>req = blk_get_request(sdev->request_queue, write, __GFP_WAIT);
=>req->cmd_len = COMMAND_SIZE(cmd[0]);//req的域成员赋值
memcpy(req->cmd, cmd, req->cmd_len);
req->sense = sense;
req->sense_len = 0;
req->retries = retries;
req->timeout = timeout;
req->cmd_type = REQ_TYPE_BLOCK_PC;
req->cmd_flags |= flags | REQ_QUIET | REQ_PREEMPT;
=>blk_execute_rq(req->q, NULL, req, 1);
=>blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq);
=>rq->rq_disk = bd_disk;
rq->end_io = done;
=>__elv_add_request(q, rq, where);
=>__blk_run_queue(q);
=>q->request_fn(q);
=>if (hang_check)
while (!wait_for_completion_timeout(&wait, hang_check * (HZ/2)));
else
wait_for_completion(&wait);
=>blk_put_request(req)
=>sd_read_capacity(sdkp, buffer);//读空间大小
=>blk_queue_flush(sdkp->disk->queue, flush);
=>set_capacity(disk, sdkp->capacity);
=>blk_queue_prep_rq(sdp->request_queue, sd_prep_fn);
=>blk_queue_unprep_rq(sdp->request_queue, sd_unprep_fn);
=>add_disk(gd);//add partitioning information to kernel list
=>blk_alloc_devt(&disk->part0, &devt);
=>bdi_register_dev(bdi, disk_devt(disk));
=>blk_register_region(disk_devt(disk), disk->minors, NULL, exact_match, exact_lock, disk);
=>register_disk(disk);
=>device_add(ddev)
=>disk_partitionable(disk)
=>bdget_disk(disk, 0);
=>blkdev_get(bdev, FMODE_READ, NULL);
=>blkdev_put(bdev, FMODE_READ);
=>/* announce disk after possible partitions are created */通过内核kobject_uevent向用户态发消息,udev或者mdev监听之后再用户态增加/dev/sdx节点
dev_set_uevent_suppress(ddev, 0);
kobject_uevent(&ddev->kobj, KOBJ_ADD);
=>disk_part_iter_init(&piter, disk, 0);
=>while ((part = disk_part_iter_next(&piter)))
while ((part = disk_part_iter_next(&piter)))
=>blk_register_queue(disk);

初始化完毕了,用户态的命令指令下发流程如下
例如sg_inq /dev/sdg
sd_ioctl
=>scsi_nonblockable_ioctl(sdp, cmd, p, (mode & FMODE_NDELAY) != 0)//故障处理,处理reset命令
=>scsi_cmd_ioctl(disk->queue, disk, mode, cmd, p);//处理正常的命令,send scsi addressing ioctls directly to mid level
=>case SG_IO:
sg_io(q, bd_disk, &hdr, mode);
=>blk_get_request(q, writing ? WRITE : READ, GFP_KERNEL);
=>blk_fill_sghdr_rq(q, rq, hdr, mode)
=>blk_execute_rq(q, bd_disk, rq, 0);
=>hdr->duration = jiffies_to_msecs(jiffies - start_time);
=>blk_complete_sghdr_rq(rq, hdr, bio);
=>scsi_ioctl//上面没有的命令走这个函数里面处理

读写流程:下发读写命令
块设备层域SCSI总线驱动联系的接口方法为request_fn,具体函数为scsi_request_fn
scsi_request_fn
=>req = elv_next_request(q)
=>ret = q->prep_rq_fn(q, rq);//scsi_prep_fn
=>switch (req->cmd_type) {
case REQ_TYPE_BLOCK_PC:
ret = scsi_setup_blk_pc_cmnd(sdev, req);
break;
case REQ_TYPE_FS:
ret = scsi_setup_fs_cmnd(sdev, req);
=>drv->init_command(cmd) //sd_init_command
=>SCpnt->allowed = SD_MAX_RETRIES; //最多重传5
break;
=>scsi_dispatch_cmd(cmd);
=>cmd->scsi_done = scsi_done;
=>host->hostt->queuecommand(host, cmd);
ata_scsi_queuecmd
static struct scsi_host_template sata_fsl_sht = {
ATA_NCQ_SHT("sata_fsl"),
.can_queue = SATA_FSL_QUEUE_DEPTH,
.sg_tablesize = SATA_FSL_MAX_PRD_USABLE,
.dma_boundary = ATA_DMA_BOUNDARY,
};
#define ATA_NCQ_SHT(drv_name) \
ATA_BASE_SHT(drv_name), \
.change_queue_depth = ata_scsi_change_queue_depth

#define ATA_BASE_SHT(drv_name) \
.module = THIS_MODULE, \
.name = drv_name, \
.ioctl = ata_scsi_ioctl, \
.queuecommand = ata_scsi_queuecmd,
=>ap = ata_shost_to_port(shost);
=>dev = ata_scsi_find_dev(ap, scsidev);
=>rc = __ata_scsi_queuecmd(cmd, dev);
=>xlat_func = ata_get_xlat_func(dev, scsi_op);//获取命令字翻译函数指针,包括ata_scsi_rw_xlate, ata_scsi_verify_xlate和SCSI->ATA命令转换
=>rc = ata_scsi_translate(dev, scmd, xlat_func);
=>qc = ata_scsi_qc_new(dev, cmd);
=>qc->complete_fn = ata_scsi_qc_complete;
=>ata_qc_issue(qc);/* select device, send command to hardware */
=>qc->flags |= ATA_QCFLAG_ACTIVE;
ap->qc_active |= 1 << qc->tag;
=>ap->ops->qc_prep(qc);
=>qc->err_mask |= ap->ops->qc_issue(qc);//命令发送到ata设备,启动控制器,把控制权交给控制器
sata_fsl_qc_issue//当控制器是FSL SATA控制器时
=>iowrite32(qc->dev->link->pmp, CQPMP + hcr_base);
=>iowrite32(1 << tag, CQ + hcr_base);

ata_sff_qc_issue//当控制器是PATA CF卡控制器
=>ata_dev_select(ap, qc->dev->devno, 1, 0);/* select the device */
=>case ATA_PROT_PIO:
ata_tf_to_host(ap, &qc->tf);
=>ap->ops->sff_tf_load(ap, tf);
ata_sff_tf_load
=>iowrite8(tf->feature, ioaddr->feature_addr);//写一堆参数
iowrite8(tf->nsect, ioaddr->nsect_addr);
iowrite8(tf->lbal, ioaddr->lbal_addr);
iowrite8(tf->lbam, ioaddr->lbam_addr);
iowrite8(tf->lbah, ioaddr->lbah_addr);
=>ap->ops->sff_exec_command(ap, tf);
iowrite8(tf->command, ap->ioaddr.command_addr);//启动控制器
if (qc->tf.flags & ATA_TFLAG_WRITE) {
/* PIO data out protocol */
ap->hsm_task_state = HSM_ST_FIRST;
ata_sff_queue_pio_task(link, 0);
=>ata_sff_queue_delayed_work(&ap->sff_pio_task, msecs_to_jiffies(delay));//启动sff_pio_task
/* always send first data block using the
* ata_sff_pio_task() codepath.
*/
} else {
/* PIO data in protocol */
ap->hsm_task_state = HSM_ST;

if (qc->tf.flags & ATA_TFLAG_POLLING)
ata_sff_queue_pio_task(link, 0);

/* if polling, ata_sff_pio_task() handles the

* rest. otherwise, interrupt handler takes
* over from here.
*/
}

对于FSL SATA控制器,控制器完成数据传输后,触发中断
sata_fsl_interrupt
=>sata_fsl_host_intr(ap);
=>sata_fsl_error_intr(ap);//如果存在故障的话,走故障处理流程
=>if (hstatus & FATAL_ERROR_DECODE) {//不同的故障,处理流程有差异
ehi->err_mask |= AC_ERR_ATA_BUS;
ehi->action |= ATA_EH_SOFTRESET;
freeze = 1;
}
if (ap->nr_pmp_links) {
if
else {
err_mask |= AC_ERR_HSM;
action |= ATA_EH_HARDRESET;//下面的故障处理流程,以“需要硬复位”为例
freeze = 1;
}
}
=>ata_port_freeze(ap);//故障处理
=>__ata_port_freeze(ap);
=>__ata_port_freeze(ap);
=>ap->ops->freeze(ap);
sata_fsl_freeze
=>temp = ioread32(hcr_base + HCONTROL);
iowrite32((temp & ~0x3F), hcr_base + HCONTROL);/* disable interrupts on the controller/port */
=>ap->pflags |= ATA_PFLAG_FROZEN;
=>nr_aborted = ata_port_abort(ap);
=>ata_do_link_abort(ap, NULL);
=>ata_port_schedule_eh(ap);
=>ata_eh_set_pending(ap, 1);
=>scsi_schedule_eh(ap->scsi_host);
=>shost->host_eh_scheduled++;
=>scsi_eh_wakeup(shost);
=>wake_up_process(shost->ehandler);//唤醒scsi_error_handler异常处理线程
=>qc = ata_qc_from_tag(ap, ATA_TAG_INTERNAL);
=>ata_qc_complete(qc);
=>__ata_qc_complete(qc);
=>qc->complete_fn(qc);
=>ata_scsi_qc_complete
=>qc->scsidone(cmd);
scsi_done
=>blk_complete_request(cmd->request);
=>__blk_complete_request(req);
=>raise_softirq_irqoff(BLOCK_SOFTIRQ);//触发软中断
=>ata_qc_free(qc);
=>iowrite32(interrupt_enables, hcr_base + HSTATUS);//重新使能中断

软中断的执行函数是blk_done_softirq,由于是scsi command引发的中断事件,因此会调用事先注册到请求队列上的scsi_softirq_done函数,完成具体的scsi软中断下半部事件处理。
blk_done_softirq
=>rq->q->softirq_done_fn(rq);
scsi_softirq_done
=>switch (disposition) {
case SUCCESS:
scsi_finish_command(cmd);
break;
case NEEDS_RETRY:
scsi_queue_insert(cmd, SCSI_MLQUEUE_EH_RETRY);
break;
case ADD_TO_MLQUEUE:
scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
break;
default:
if (!scsi_eh_scmd_add(cmd, 0))
scsi_finish_command(cmd);
}

参考博客:
scsi调用过程分析(ZT) http://blog.chinaunix.net/uid-26293227-id-3973595.html
linux那些事

ubuntu学习心得之SATA硬盘跟IDE硬盘
http://www.myexception.cn/software/1648362.html

SATA硬盘和IDE硬盘的区别
http://blog.csdn.net/ediwal/article/details/47831963

块设备剖析之块设备注册 - add_disk()函数解析 //这位同学的博客好好学习一下
http://blog.chinaunix.net/uid-30282771-id-5113192.html