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
阅读全文
0 0
- SATA代码分析和学习心得
- USB代码分析和学习心得之初始化流程以及读写流程
- SATA 和 SATA2
- PCIE 和SATA 接口
- DLL注入和代码注入学习心得
- SATA
- SATA
- SATA
- sata
- SATA
- SATA和SCSI linux区别
- SATA、IDE和SCSI介绍
- 网易公开课《Linux内核分析》学习心得-使用库函数API和C代码中嵌入汇编代码两种方式使用同一个系统调用
- 兼收并蓄 SATA与SAS技术分析
- 兼收并蓄 SATA与SAS技术分析
- android代码学习心得
- 泛函分析学习心得
- SuppressMessage和代码分析
- java中方法的重写和方法的重载
- Could not resolve URL for hyperlinked relationship using view name "xxx:api:xxx-detail". Y
- 【Python】urllib的基本用法01
- Android全局捕获崩溃异常记录日志保存至本地并定时删除
- Android崩溃日志获取与解析
- SATA代码分析和学习心得
- zeppelin实战安装配置,经过检验的呀!
- 获取本地cookies出现的错误 sqlite3.DatabaseError: malformed database schema (is_transient)
- 理解JavaScript作用域
- python第23篇之-最简单的类使用(面向对象编程)
- 卷积神经网络中图像池化操作全解析
- HTML+CSS编写静态网站-06 创建基础结构
- python第24篇之--类的初始化(面向对象编程)
- 开发者,只有被裁,没有退休