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

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);//初始化请求队列,需要复杂的IO调度
										=>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结构体并初始化. 当内核发现系统中的一个新的磁盘时(启动时或者运行时热插入),就调用该函数.该函数负责初始化一个新的gendisk对象并为分区初始化hd_struct数组
		=>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;		
	=>device_add(&sdkp->dev)
	=>async_schedule(sd_probe_async, sdkp);	
	sd_probe_async
		=>gd->major = sd_major((index & 0xf0) >> 4);
		=>gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
		=>gd->minors = 16;
		=>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核心层函数,本质里面很抽象,谈到具体实现,就是指针
					=>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_get(bdev, mode, flags, 0);
					=>return do_open(bdev, &fake_file, for_part);
						=>file->f_mapping = bdev->bd_inode->i_mapping;
						=>disk = get_gendisk(bdev->bd_dev, &part);
						=>rescan_partitions(disk, bdev);
							=>state = check_partition(disk, bdev)
								=>res = check_part[i++](state, bdev);
									=>msdos_partition
										=>data = read_dev_sector(bdev, 0, &sect);//读主引导扇区
										=>根据主引导扇区提取分区信息
							=>for (p = 1; p < state->limit; p++) 
								==>add_partition(disk, p, from, size, state->parts[p].flags);//将各个分区初始化并与gendisk关联
									=>struct hd_struct *p;
									=>p = kzalloc(sizeof(*p), GFP_KERNEL);
									=>p->start_sect = start;
									p->nr_sects = len;
									p->partno = part;
									p->policy = disk->policy;
									snprintf(p->kobj.name,KOBJ_NAME_LEN,"%s%d",disk->kobj.name,part);
									=>p->kobj.parent = &disk->kobj;
									p->kobj.ktype = &ktype_part;
									kobject_init(&p->kobj);
									kobject_add(&p->kobj);
									if (!disk->part_uevent_suppress)
										kobject_uevent(&p->kobj, KOBJ_ADD);
									sysfs_create_link(&p->kobj, &block_subsys.kobj, "subsystem");
									if (flags & ADDPART_FLAG_WHOLEDISK) {
										static struct attribute addpartattr = {
											.name = "whole_disk",
											.mode = S_IRUSR | S_IRGRP | S_IROTH,
										};
								
										sysfs_create_file(&p->kobj, &addpartattr);
									}
									partition_sysfs_add_subdir(p);
									disk->part[part-1] = p;
			=>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//scsi device对应的request_queue,scsi_request_fn属于scsi middle
	=>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;
		=>shost = sdev->host;//找到了scsi host
		=>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);//在lib_ata层,通过SCSI HOST找到ATA AP
			=>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);
							      	 =>list = &__get_cpu_var(blk_cpu_done);
									list_add_tail(&req->csd.list, list);//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
	=>local_irq_disable();
	cpu_list = &__get_cpu_var(blk_cpu_done);
	list_replace_init(cpu_list, &local_list);
	local_irq_enable();
	=>while (!list_empty(&local_list)) 
		rq->q->softirq_done_fn(rq);
			struct request *rq;
			rq = list_entry(local_list.next, struct request, csd.list);
			list_del_init(&rq->csd.list);//req出队列链表,消费者
			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

libata 开发手册
http://blog.csdn.net/chengm8/article/details/19246869
存储之ATA Disk (libata模块)
http://blog.csdn.net/chengm8/article/details/7876811

linux libata初始化分析
http://blog.csdn.net/leino11121/article/details/6585116

libATA Developer’s Guide
https://www.kernel.org/doc/htmldocs/libata/

Documentation/ata/ata_exceptions.txt
http://www.archivum.info/linux-ide@vger.kernel.org/2005-09/00043/(RFC-PATCH)-Documentation-ata-ata_exceptions.txt.html

Linux3.10.0块IO子系统流程(4)-- 为请求构造SCSI命令
https://www.cnblogs.com/luxiaodai/p/9266311.html

scsi设备的请求处理函数(request_fn) //博客很不错
https://blog.csdn.net/weixin_36145588/article/details/72878602

为request的每一个bio创建DMA映射
https://blog.csdn.net/weixin_36145588/article/details/72897864

linux硬盘识别过程
https://blog.csdn.net/qq_37403371/article/details/84396099
Linux scsi 异步扫描分析笔记
http://blog.chinaunix.net/uid-709830-id-2069884.html
Linux Scsi子系统框架介绍
https://blog.csdn.net/feelabclihu/article/details/108675669

  • 5
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
SATA (Serial ATA) 是一种高速传输数据的接口标准,它已经成为现代计算机中硬盘驱动器的主要接口标准之一。Linux 内核中的 SATA 驱动程序主要用于支持 SATA 硬盘控制器。 SATA 驱动程序的源代码主要包括以下几个部分: 1. 驱动程序注册和初始化 在 Linux 内核启动时,SATA 驱动程序会自动注册到内核中,以便能够识别并支持 SATA 硬盘控制器。驱动程序的初始化主要包括以下几个步骤: - 检查硬件设备是否存在并可用 - 分配内存空间和初始化数据结构 - 注册中断处理函数 - 设置硬件设备的工作模式和参数 2. 块设备驱动 SATA 硬盘控制器对应的块设备驱动程序主要实现以下几个功能: - 探测硬盘的容量和参数 - 支持读写操作 - 支持块设备的缓存和缓存管理 块设备驱动程序的核心代码包括请求队列管理、命令和数据传输、块设备缓存管理等模块。 3. 中断处理程序 当 SATA 硬盘控制器发生中断时,SATA 驱动程序会调用相应的中断处理程序来处理中断事件。中断处理程序主要实现以下几个功能: - 读取和处理硬件设备的状态信息 - 处理传输命令和数据的完成情况 - 唤醒等待中的进程 4. 控制器驱动程序 SATA 控制器驱动程序主要用于管理和控制 SATA 控制器的工作,包括以下几个功能: - 初始化和配置控制器 - 管理控制器的中断和 DMA 控制 - 处理控制器的错误和异常情况 控制器驱动程序的核心代码主要包括控制器的初始化、中断处理、DMA 控制等模块。 总的来说,SATA 驱动程序的源代码主要涉及硬件设备的初始化和配置、块设备驱动、中断处理程序和控制器驱动程序等几个方面。在理解和分析 SATA 驱动程序的源代码时,需要掌握相关的硬件知识和 Linux 内核编程技巧。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值