SATA代码分析和学习心得

来源:互联网 发布:python curl post 编辑:程序博客网 时间:2024/06/02 02:09
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_probesata_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);                                =>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/sdgsd_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_fnscsi_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.htmllinux那些事ubuntu学习心得之SATA硬盘跟IDE硬盘http://www.myexception.cn/software/1648362.html SATA硬盘和IDE硬盘的区别 http://blog.csdn.net/ediwal/article/details/47831963
原创粉丝点击