SCSI驱动框架分析

1. 先来看下SCSI相关的数据结构,主要有scsi_host_template(SCSI主机适配器模板),scsi_host(SCSI主机适配器),scsi_target(目标节点),scsi_device(逻辑单元),如果是scsi disk,则又有一个scsi_disk结构。

struct scsi_host_template {
	struct module *module;
	const char *name;	//scsi hba驱动的名字

	int (* detect)(struct scsi_host_template *);
	int (* release)(struct Scsi_Host *);
	const char *(* info)(struct Scsi_Host *);
	int (* ioctl)(struct scsi_device *dev, int cmd, void __user *arg);


#ifdef CONFIG_COMPAT
	int (* compat_ioctl)(struct scsi_device *dev, int cmd, void __user *arg);
#endif
	int (* queuecommand)(struct Scsi_Host *, struct scsi_cmnd *); 	//将SCSI命令排入底层驱动的队列

	int (* transfer_response)(struct scsi_cmnd *,
				  void (*done)(struct scsi_cmnd *));	
	int (* eh_abort_handler)(struct scsi_cmnd *);	//放弃给定的命令
	int (* eh_device_reset_handler)(struct scsi_cmnd *);	//scsi设备复位
	int (* eh_target_reset_handler)(struct scsi_cmnd *);	//目标节点复位
	int (* eh_bus_reset_handler)(struct scsi_cmnd *);	//scsi总线复位
	int (* eh_host_reset_handler)(struct scsi_cmnd *);	//主机适配器复位
	int (* slave_alloc)(struct scsi_device *);	//在扫描到一个新的SCSI设备后调用
	int (* slave_configure)(struct scsi_device *);	//在接收到SCSI设备的inquiry响应之后调用
	void (* slave_destroy)(struct scsi_device *);	//在销毁scsi设备之前调用
	int (* target_alloc)(struct scsi_target *);	//在发现一个新的scsi目标器后调用
	void (* target_destroy)(struct scsi_target *);	//在销毁scsi目标器后调用
	int (* scan_finished)(struct Scsi_Host *, unsigned long);	
	void (* scan_start)(struct Scsi_Host *);
	int (* change_queue_depth)(struct scsi_device *, int, int);	//改变主机适配器队列深度的回调函数
	int (* change_queue_type)(struct scsi_device *, int);	//改变主机适配器tag类型的回调函数
	int (* bios_param)(struct scsi_device *, struct block_device *,
			sector_t, int []);
	void (*unlock_native_capacity)(struct scsi_device *);
	int (*proc_info)(struct Scsi_Host *, char *, char **, off_t, int, int);
	enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *);

	const char *proc_name;
	struct proc_dir_entry *proc_dir;
	int can_queue;
	int this_id;

	unsigned short sg_tablesize;
	unsigned short sg_prot_tablesize;
	unsigned short max_sectors;
	unsigned long dma_boundary;
#define SCSI_DEFAULT_MAX_SECTORS	1024
	short cmd_per_lun;
	unsigned char present;
	unsigned supported_mode:2;
	unsigned unchecked_isa_dma:1;
	unsigned use_clustering:1;
	unsigned emulated:1;
	unsigned skip_settle_delay:1;
	unsigned ordered_tag:1;
	unsigned int max_host_blocked;
#define SCSI_DEFAULT_HOST_BLOCKED	7
	struct device_attribute **shost_attrs;
	struct device_attribute **sdev_attrs;
	struct list_head legacy_hosts;
	u64 vendor_id;
};
struct Scsi_Host {
	/*
	 * __devices is protected by the host_lock, but you should
	 * usually use scsi_device_lookup / shost_for_each_device
	 * to access it and don't care about locking yourself.
	 * In the rare case of beeing in irq context you can use
	 * their __ prefixed variants with the lock held. NEVER
	 * access this list directly from a driver.
	 */
	struct list_head	__devices;	//这个scsi适配器的scsi设备链表头
	struct list_head	__targets;	//这个scsi适配器的scsi目标节点链表头
	
	struct scsi_host_cmd_pool *cmd_pool;	//存储池
	spinlock_t		free_list_lock;
	struct list_head	free_list; /* backup store of cmd structs */
	struct list_head	starved_list;	//饥饿链表

	spinlock_t		default_lock;
	spinlock_t		*host_lock;

	struct mutex		scan_mutex;/* serialize scanning activity */

	struct list_head	eh_cmd_q;	//进入错误恢复的scsi命令链表的表头
	struct task_struct    * ehandler;  /* Error recovery thread. */	//错误恢复线程
	struct completion     * eh_action; /* Wait for specific actions on the		//完成变量
					      host. */
	wait_queue_head_t       host_wait;	//等待队列头
	struct scsi_host_template *hostt;	//指向用于创建此主机适配器的模板
	struct scsi_transport_template *transportt;	//指向传输模板

	/*
	 * Area to keep a shared tag map (if needed, will be
	 * NULL if not).
	 */
	struct blk_queue_tag	*bqt;

	/*
	 * The following two fields are protected with host_lock;
	 * however, eh routines can safely access during eh processing
	 * without acquiring the lock.
	 */
	unsigned int host_busy;		   /* commands actually active on low-level */	//已经派发给底层的命令数
	unsigned int host_failed;	   /* commands that failed. */	//失败的命令数
	unsigned int host_eh_scheduled;    /* EH scheduled without command */
    
	unsigned int host_no;  /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */	//主机编号
	int resetting; /* if set, it means that last_reset is a valid value */	//如果为1,last_reset有效
	unsigned long last_reset;		//上次复位的时间

	/*
	 * These three parameters can be used to allow for wide scsi,
	 * and for host adapters that support multiple busses
	 * The first two should be set to 1 more than the actual max id
	 * or lun (i.e. 8 for normal systems).
	 */
	unsigned int max_id;	//目标节点最大编号
	unsigned int max_lun;	//逻辑设备lun最大编号
	unsigned int max_channel;	//最大通道编号

	/*
	 * This is a unique identifier that must be assigned so that we
	 * have some way of identifying each detected host adapter properly
	 * and uniquely.  For hosts that do not support more than one card
	 * in the system at one time, this does not need to be set.  It is
	 * initialized to 0 in scsi_register.
	 */
	unsigned int unique_id;	//唯一标识符

	/*
	 * The maximum length of SCSI commands that this host can accept.
	 * Probably 12 for most host adapters, but could be 16 for others.
	 * or 260 if the driver supports variable length cdbs.
	 * For drivers that don't set this field, a value of 12 is
	 * assumed.
	 */
	unsigned short max_cmd_len;		//可以接受的SCSI命令的最大长度

	int this_id;	//这个主机适配器的scsi  id
	int can_queue;	//可以同时接受的命令数
	short cmd_per_lun;
	short unsigned int sg_tablesize;
	short unsigned int sg_prot_tablesize;
	short unsigned int max_sectors;
	unsigned long dma_boundary;
	/* 
	 * Used to assign serial numbers to the cmds.
	 * Protected by the host lock.
	 */
	unsigned long cmd_serial_number;
	
	unsigned active_mode:2;
	unsigned unchecked_isa_dma:1;
	unsigned use_clustering:1;
	unsigned use_blk_tcq:1;

	/*
	 * Host has requested that no further requests come through for the
	 * time being.
	 */
	unsigned host_self_blocked:1;
    
	/*
	 * Host uses correct SCSI ordering not PC ordering. The bit is
	 * set for the minority of drivers whose authors actually read
	 * the spec ;).
	 */
	unsigned reverse_ordering:1;

	/*
	 * Ordered write support
	 */
	unsigned ordered_tag:1;

	/* Task mgmt function in progress */
	unsigned tmf_in_progress:1;

	/* Asynchronous scan in progress */
	unsigned async_scan:1;

	/*
	 * Optional work queue to be utilized by the transport
	 */
	char work_q_name[20];
	struct workqueue_struct *work_q;

	/*
	 * Host has rejected a command because it was busy.
	 */
	unsigned int host_blocked;

	/*
	 * Value host_blocked counts down from
	 */
	unsigned int max_host_blocked;

	/* Protection Information */
	unsigned int prot_capabilities;
	unsigned char prot_guard_type;

	/*
	 * q used for scsi_tgt msgs, async events or any other requests that
	 * need to be processed in userspace
	 */
	struct request_queue *uspace_req_q;

	/* legacy crap */
	unsigned long base;
	unsigned long io_port;
	unsigned char n_io_port;
	unsigned char dma_channel;
	unsigned int  irq;
	

	enum scsi_host_state shost_state;

	/* ldm bits */
	struct device		shost_gendev, shost_dev;

	/*
	 * List of hosts per template.
	 *
	 * This is only for use by scsi_module.c for legacy templates.
	 * For these access to it is synchronized implicitly by
	 * module_init/module_exit.
	 */
	struct list_head sht_legacy_list;

	/*
	 * Points to the transport data (if any) which is allocated
	 * separately
	 */
	void *shost_data;

	/*
	 * Points to the physical bus device we'd use to do DMA
	 * Needed just in case we have virtual hosts.
	 */
	struct device *dma_dev;

	/*
	 * We should ensure that this is aligned, both for better performance
	 * and also because some compilers (m68k) don't automatically force
	 * alignment to a long boundary.
	 */
	unsigned long hostdata[0]  /* Used for storage of host specific stuff */
		__attribute__ ((aligned (sizeof(unsigned long))));
};
struct scsi_target {
	struct scsi_device	*starget_sdev_user;	//用于目标节点一次只允许对一个lun进行IO的场合
	struct list_head	siblings;	//链接到所属主机适配器链表的连接件
	struct list_head	devices;	//这个目标节点的scsi设备链表头
	struct device		dev;	//内嵌通用设备
	unsigned int		reap_ref; /* protected by the host lock */
	unsigned int		channel;	//通道号
	unsigned int		id; /* target id ... replace	
				     * scsi_device.id eventually */		//目标节点的ID
	unsigned int		create:1; /* signal that it needs to be added */	//是否需要添加
	unsigned int		single_lun:1;	/* Indicates we should only
						 * allow I/O to one of the luns
						 * for the device at a time. */
	unsigned int		pdt_1f_for_no_lun;	/* PDT = 0x1f */	
						/* means no lun present */
	/* commands actually active on LLD. protected by host lock. */
	unsigned int		target_busy;
	/*
	 * LLDs should set this in the slave_alloc host template callout.
	 * If set to zero then there is not limit.
	 */
	unsigned int		can_queue;	//可以同时处理的命令数
	unsigned int		target_blocked;	//目标节点阻塞计数器
	unsigned int		max_target_blocked;	
#define SCSI_DEFAULT_TARGET_BLOCKED	3

	char			scsi_level;
	struct execute_work	ew;		//工作结构
	enum scsi_target_state	state;	//状态
	void 			*hostdata; /* available to low-level driver */
	unsigned long		starget_data[0]; /* for the transport */
	/* starget_data must be the last element!!!! */
} __attribute__((aligned(sizeof(unsigned long))));
struct scsi_device {
	struct Scsi_Host *host;		//指向主机适配器
	struct request_queue *request_queue;	//指向请求队列

	/* the next two are protected by the host->host_lock */
	struct list_head    siblings;   /* list of all devices on this host */	//链入到主机适配器的设备链表
	struct list_head    same_target_siblings; /* just the devices sharing same target id */		//链入目标节点的设备链表

	/* this is now protected by the request_queue->queue_lock */
	unsigned int device_busy;	/* commands actually active on	
					 * low-level. protected by queue_lock. */	//已经派发的命令数
	spinlock_t list_lock;
	struct list_head cmd_list;	/* queue of in use SCSI Command structures */		//使用中的scsi命令队列
	struct list_head starved_entry;		//链入所属主机适配器的饥饿链表的连接件
	struct scsi_cmnd *current_cmnd;	/* currently active command */	//指向当前活动命令结构
	unsigned short queue_depth;	/* How deep of a queue we want */	//队列深度
	unsigned short max_queue_depth;	/* max queue depth */		//最大队列深度
	unsigned short last_queue_full_depth; /* These two are used by */	//记录上次队列满时设备的活动命令数
	unsigned short last_queue_full_count; /* scsi_track_queue_full() */	//记录在队列满时设备活动命令数未发生变化的次数
	unsigned long last_queue_full_time;	/* last queue full time */	//上次队列满的时间
	unsigned long queue_ramp_up_period;	/* ramp up period in jiffies */	//间隔
#define SCSI_DEFAULT_RAMP_UP_PERIOD	(120 * HZ)

	unsigned long last_queue_ramp_up;	/* last queue ramp up time */	//上次ramp up的时间

	unsigned int id, lun, channel;	//目标节点的ID,lun编号,通道编号

	unsigned int manufacturer;	/* Manufacturer of device, for using 
					 * vendor-specific cmd's */
	unsigned sector_size;	/* size in bytes */

	void *hostdata;		/* available to low-level driver */
	char type;	//scsi设备类型
	char scsi_level;	//scsi规范的版本号
	char inq_periph_qual;	/* PQ from INQUIRY data */	
	unsigned char inquiry_len;	/* valid bytes in 'inquiry' */
	unsigned char * inquiry;	/* INQUIRY response data */
	const char * vendor;		/* [back_compat] point into 'inquiry' ... */
	const char * model;		/* ... after scan; point to static string */
	const char * rev;		/* ... "nullnullnullnull" before scan */
	unsigned char current_tag;	/* current tag */
	struct scsi_target      *sdev_target;   /* used only for single_lun */	//只用于single_lun的目标器

	unsigned int	sdev_bflags; /* black/white flags as also found in
				 * scsi_devinfo.[hc]. For now used only to
				 * pass settings from slave_alloc to scsi
				 * core. */
	unsigned writeable:1;
	unsigned removable:1;
	unsigned changed:1;	/* Data invalid due to media change */
	unsigned busy:1;	/* Used to prevent races */
	unsigned lockable:1;	/* Able to prevent media removal */
	unsigned locked:1;      /* Media removal disabled */
	unsigned borken:1;	/* Tell the Seagate driver to be 
				 * painfully slow on this device */
	unsigned disconnect:1;	/* can disconnect */
	unsigned soft_reset:1;	/* Uses soft reset option */
	unsigned sdtr:1;	/* Device supports SDTR messages */
	unsigned wdtr:1;	/* Device supports WDTR messages */
	unsigned ppr:1;		/* Device supports PPR messages */
	unsigned tagged_supported:1;	/* Supports SCSI-II tagged queuing */
	unsigned simple_tags:1;	/* simple queue tag messages are enabled */
	unsigned ordered_tags:1;/* ordered queue tag messages are enabled */
	unsigned was_reset:1;	/* There was a bus reset on the bus for 
				 * this device */
	unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN
				     * because we did a bus reset. */
	unsigned use_10_for_rw:1; /* first try 10-byte read / write */
	unsigned use_10_for_ms:1; /* first try 10-byte mode sense/select */
	unsigned skip_ms_page_8:1;	/* do not use MODE SENSE page 0x08 */
	unsigned skip_ms_page_3f:1;	/* do not use MODE SENSE page 0x3f */
	unsigned use_192_bytes_for_3f:1; /* ask for 192 bytes from page 0x3f */
	unsigned no_start_on_add:1;	/* do not issue start on add */
	unsigned allow_restart:1; /* issue START_UNIT in error handler */
	unsigned manage_start_stop:1;	/* Let HLD (sd) manage start/stop */
	unsigned start_stop_pwr_cond:1;	/* Set power cond. in START_STOP_UNIT */
	unsigned no_uld_attach:1; /* disable connecting to upper level drivers */
	unsigned select_no_atn:1;
	unsigned fix_capacity:1;	/* READ_CAPACITY is too high by 1 */
	unsigned guess_capacity:1;	/* READ_CAPACITY might be too high by 1 */
	unsigned retry_hwerror:1;	/* Retry HARDWARE_ERROR */
	unsigned last_sector_bug:1;	/* do not use multisector accesses on
					   SD_LAST_BUGGY_SECTORS */
	unsigned no_read_disc_info:1;	/* Avoid READ_DISC_INFO cmds */
	unsigned no_read_capacity_16:1; /* Avoid READ_CAPACITY_16 cmds */
	unsigned is_visible:1;	/* is the device visible in sysfs */

	DECLARE_BITMAP(supported_events, SDEV_EVT_MAXBITS); /* supported events */	//支持事件的位图
	struct list_head event_list;	/* asserted events */	//要发送到用户空间的事件链表的表头
	struct work_struct event_work;	//发送时间的工作队列

	unsigned int device_blocked;	/* Device returned QUEUE_FULL. */	//阻塞计数器

	unsigned int max_device_blocked; /* what device_blocked counts down from  */	
#define SCSI_DEFAULT_DEVICE_BLOCKED	3

	atomic_t iorequest_cnt;
	atomic_t iodone_cnt;
	atomic_t ioerr_cnt;

	struct device		sdev_gendev,
				sdev_dev;	//sdev_gendev嵌入到总线的设备链表,sdev_dev嵌入到scsi设备类链表

	struct execute_work	ew; /* used to get process context on put */
	struct work_struct	requeue_work;

	struct scsi_dh_data	*scsi_dh_data;
	enum scsi_device_state sdev_state;
	unsigned long		sdev_data[0];
} __attribute__((aligned(sizeof(unsigned long))));


2. 接下来看比较重要的API函数:

static int __init init_scsi(void)
{
	int error;

	error = scsi_init_queue();	//初始化聚散列表等
	if (error)
		return error;
	error = scsi_init_procfs();	//初始化proc文件系统
	if (error)
		goto cleanup_queue;
	error = scsi_init_devinfo();	//设置scsi动态设备信息列表
	if (error)
		goto cleanup_procfs;
	error = scsi_init_hosts();	//注册shost_class类
	if (error)
		goto cleanup_devlist;
	error = scsi_init_sysctl();	//注册sysctl控制表
	if (error)
		goto cleanup_hosts;
	error = scsi_sysfs_register();	//注册scsi总线及sdev_class类
	if (error)
		goto cleanup_sysctl;

	scsi_netlink_init();	//初始化scsi传输netlingk接口

	printk(KERN_NOTICE "SCSI subsystem initialized\n");
	return 0;

cleanup_sysctl:
	scsi_exit_sysctl();
cleanup_hosts:
	scsi_exit_hosts();
cleanup_devlist:
	scsi_exit_devinfo();
cleanup_procfs:
	scsi_exit_procfs();
cleanup_queue:
	scsi_exit_queue();
	printk(KERN_ERR "SCSI subsystem failed to initialize, error = %d\n",
	       -error);
	return error;
}
scsi_host_alloc和分配i2c_adapter,分配spi_master相类似,都是分配主机适配器层的结构体。

scsi_add_host是向系统注册scsi_host。

struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
{
	struct Scsi_Host *shost;
	gfp_t gfp_mask = GFP_KERNEL;

	if (sht->unchecked_isa_dma && privsize)
		gfp_mask |= __GFP_DMA;

	shost = kzalloc(sizeof(struct Scsi_Host) + privsize, gfp_mask);
	if (!shost)
		return NULL;

	shost->host_lock = &shost->default_lock;
	spin_lock_init(shost->host_lock);
	shost->shost_state = SHOST_CREATED;
	INIT_LIST_HEAD(&shost->__devices);
	INIT_LIST_HEAD(&shost->__targets);
	INIT_LIST_HEAD(&shost->eh_cmd_q);
	INIT_LIST_HEAD(&shost->starved_list);
	init_waitqueue_head(&shost->host_wait);

	mutex_init(&shost->scan_mutex);

	/*
	 * subtract one because we increment first then return, but we need to
	 * know what the next host number was before increment
	 */
	shost->host_no = atomic_inc_return(&scsi_host_next_hn) - 1;
	shost->dma_channel = 0xff;

	/* These three are default values which can be overridden */
	shost->max_channel = 0;
	shost->max_id = 8;
	shost->max_lun = 8;

	/* Give each shost a default transportt */
	shost->transportt = &blank_transport_template;

	/*
	 * All drivers right now should be able to handle 12 byte
	 * commands.  Every so often there are requests for 16 byte
	 * commands, but individual low-level drivers need to certify that
	 * they actually do something sensible with such commands.
	 */
	shost->max_cmd_len = 12;
	shost->hostt = sht;
	shost->this_id = sht->this_id;
	shost->can_queue = sht->can_queue;
	shost->sg_tablesize = sht->sg_tablesize;
	shost->sg_prot_tablesize = sht->sg_prot_tablesize;
	shost->cmd_per_lun = sht->cmd_per_lun;
	shost->unchecked_isa_dma = sht->unchecked_isa_dma;
	shost->use_clustering = sht->use_clustering;
	shost->ordered_tag = sht->ordered_tag;

	if (sht->supported_mode == MODE_UNKNOWN)
		/* means we didn't set it ... default to INITIATOR */
		shost->active_mode = MODE_INITIATOR;
	else
		shost->active_mode = sht->supported_mode;

	if (sht->max_host_blocked)
		shost->max_host_blocked = sht->max_host_blocked;
	else
		shost->max_host_blocked = SCSI_DEFAULT_HOST_BLOCKED;

	/*
	 * If the driver imposes no hard sector transfer limit, start at
	 * machine infinity initially.
	 */
	if (sht->max_sectors)
		shost->max_sectors = sht->max_sectors;
	else
		shost->max_sectors = SCSI_DEFAULT_MAX_SECTORS;

	/*
	 * assume a 4GB boundary, if not set
	 */
	if (sht->dma_boundary)
		shost->dma_boundary = sht->dma_boundary;
	else
		shost->dma_boundary = 0xffffffff;

	//设置scsi_host的shost_gendev总线设备
	device_initialize(&shost->shost_gendev);
	dev_set_name(&shost->shost_gendev, "host%d", shost->host_no);
	shost->shost_gendev.bus = &scsi_bus_type;
	shost->shost_gendev.type = &scsi_host_type;
	//设置scsi_host的shsot_dev类设备
	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);
	if (IS_ERR(shost->ehandler)) {
		printk(KERN_WARNING "scsi%d: error handler thread failed to spawn, error = %ld\n",
			shost->host_no, PTR_ERR(shost->ehandler));
		goto fail_kfree;
	}

	scsi_proc_hostdir_add(shost->hostt);
	return shost;

 fail_kfree:
	kfree(shost);
	return NULL;
}

int scsi_add_host_with_dma(struct Scsi_Host *shost, struct device *dev,
			   struct device *dma_dev)
{
	struct scsi_host_template *sht = shost->hostt;
	int error = -EINVAL;

	printk(KERN_INFO "scsi%d : %s\n", shost->host_no,
			sht->info ? sht->info(shost) : sht->name);

	if (!shost->can_queue) {
		printk(KERN_ERR "%s: can_queue = 0 no longer supported\n",
				sht->name);
		goto fail;
	}

	error = scsi_setup_command_freelist(shost);	//准备scsi命令结构的存储池
	if (error)
		goto fail;

	if (!shost->shost_gendev.parent)
		shost->shost_gendev.parent = dev ? dev : &platform_bus;
	shost->dma_dev = dma_dev;

	error = device_add(&shost->shost_gendev);	//注册控制器设备到系统
	if (error)
		goto out;

	pm_runtime_set_active(&shost->shost_gendev);
	pm_runtime_enable(&shost->shost_gendev);
	device_enable_async_suspend(&shost->shost_gendev);

	scsi_host_set_state(shost, SHOST_RUNNING);
	get_device(shost->shost_gendev.parent);

	device_enable_async_suspend(&shost->shost_dev);

	error = device_add(&shost->shost_dev);	//注册控制器类设备到系统
	if (error)
		goto out_del_gendev;

	get_device(&shost->shost_gendev);

	if (shost->transportt->host_size) {
		shost->shost_data = kzalloc(shost->transportt->host_size,
					 GFP_KERNEL);
		if (shost->shost_data == NULL) {
			error = -ENOMEM;
			goto out_del_dev;
		}
	}

	if (shost->transportt->create_work_queue) {
		snprintf(shost->work_q_name, sizeof(shost->work_q_name),
			 "scsi_wq_%d", shost->host_no);
		shost->work_q = create_singlethread_workqueue(
					shost->work_q_name);
		if (!shost->work_q) {
			error = -EINVAL;
			goto out_free_shost_data;
		}
	}

	error = scsi_sysfs_add_host(shost);
	if (error)
		goto out_destroy_host;

	scsi_proc_host_add(shost);
	return error;

 out_destroy_host:
	if (shost->work_q)
		destroy_workqueue(shost->work_q);
 out_free_shost_data:
	kfree(shost->shost_data);
 out_del_dev:
	device_del(&shost->shost_dev);
 out_del_gendev:
	device_del(&shost->shost_gendev);
 out:
	scsi_destroy_command_freelist(shost);
 fail:
	return error;
}
一般在scsi主机适配器驱动的prob里面,先是scsi_alloc_host,然后scsi_add_host,紧接着就调用scsi_scan_host扫描scsi总线。

scsi总线扫描的目的是通过协议特定或芯片特定的方式探测出挂接在主机适配器后面的目标节点和逻辑单元,为它们在内存中构建相应的数据结构,将它们添加到系统中。

scsi中间层依次以可能的ID和LUN构造INQUIRY命令,之后将这些INQUIRY命令提交到块IO系统,后者最终将调用中间层的策略例程,再次提取到SCSI命令后,调用scsi底层驱动的queuecommand回调函数。

void scsi_scan_host(struct Scsi_Host *shost)
{
	struct task_struct *p;
	struct async_scan_data *data;

	if (strncmp(scsi_scan_type, "none", 4) == 0)
		return;
	if (scsi_autopm_get_host(shost) < 0)
		return;

	data = scsi_prep_async_scan(shost);
	if (!data) {
		do_scsi_scan_host(shost);	//同步扫描
		scsi_autopm_put_host(shost);
		return;
	}

	p = kthread_run(do_scan_async, data, "scsi_scan_%d", shost->host_no);
	if (IS_ERR(p))
		do_scan_async(data);
	/* scsi_autopm_put_host(shost) is called in do_scan_async() */
}
static void do_scsi_scan_host(struct Scsi_Host *shost)
{
	if (shost->hostt->scan_finished) {	//对于有自定义扫描逻辑的适配器
		unsigned long start = jiffies;
		if (shost->hostt->scan_start)
			shost->hostt->scan_start(shost);

		while (!shost->hostt->scan_finished(shost, jiffies - start))
			msleep(10);
	} else {
		scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
				SCAN_WILD_CARD, 0);	//标准化扫描
	}
}
int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
			    unsigned int id, unsigned int lun, int rescan)
{
	SCSI_LOG_SCAN_BUS(3, shost_printk (KERN_INFO, shost,
		"%s: <%u:%u:%u>\n",
		__func__, channel, id, lun));

	if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
	    ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
	    ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
		return -EINVAL;

	mutex_lock(&shost->scan_mutex);
	if (!shost->async_scan)
		scsi_complete_async_scans();

	if (scsi_host_scan_allowed(shost) && scsi_autopm_get_host(shost) == 0) {
		if (channel == SCAN_WILD_CARD)
			for (channel = 0; channel <= shost->max_channel;
			     channel++)	//扫描所有的通道
				scsi_scan_channel(shost, channel, id, lun,
						  rescan);
		else
			scsi_scan_channel(shost, channel, id, lun, rescan);
		scsi_autopm_put_host(shost);
	}
	mutex_unlock(&shost->scan_mutex);

	return 0;
}
static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
			      unsigned int id, unsigned int lun, int rescan)
{
	uint order_id;

	if (id == SCAN_WILD_CARD)	//扫描所有的目标节点
		for (id = 0; id < shost->max_id; ++id) {
			/*
			 * XXX adapter drivers when possible (FCP, iSCSI)
			 * could modify max_id to match the current max,
			 * not the absolute max.
			 *
			 * XXX add a shost id iterator, so for example,
			 * the FC ID can be the same as a target id
			 * without a huge overhead of sparse id's.
			 */
			if (shost->reverse_ordering)
				/*
				 * Scan from high to low id.
				 */
				order_id = shost->max_id - id - 1;
			else
				order_id = id;
			__scsi_scan_target(&shost->shost_gendev, channel,
					order_id, lun, rescan);
		}
	else
		__scsi_scan_target(&shost->shost_gendev, channel,
				id, lun, rescan);
}
static void __scsi_scan_target(struct device *parent, unsigned int channel,
		unsigned int id, unsigned int lun, int rescan)
{
	struct Scsi_Host *shost = dev_to_shost(parent);
	int bflags = 0;
	int res;
	struct scsi_target *starget;

	if (shost->this_id == id)
		/*
		 * Don't scan the host adapter
		 */
		return;

	starget = scsi_alloc_target(parent, channel, id);
	if (!starget)
		return;
	scsi_autopm_get_target(starget);

	if (lun != SCAN_WILD_CARD) {
		/*
		 * Scan for a specific host/chan/id/lun.
		 */
		scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);
		goto out_reap;
	}

	/*
	 * Scan LUN 0, if there is some response, scan further. Ideally, we
	 * would not configure LUN 0 until all LUNs are scanned.
	 */
	 //先扫描LUN0,对LUN0的INQUIRY响应是所有目标节点必须实现的
	res = scsi_probe_and_add_lun(starget, 0, &bflags, NULL, rescan, NULL);
	if (res == SCSI_SCAN_LUN_PRESENT || res == SCSI_SCAN_TARGET_PRESENT) {
		//如果对LUN0有响应,继续探测其它lun编号的逻辑单元
		if (scsi_report_lun_scan(starget, bflags, rescan) != 0)	//向lun0发送report lun命令
			/*
			 * The REPORT LUN did not scan the target,
			 * do a sequential scan.
			 */
			//如果上面方法失败,从1开始到最大编号,依次探测
			scsi_sequential_lun_scan(starget, bflags,
						 starget->scsi_level, rescan);
	}

 out_reap:
	scsi_autopm_put_target(starget);
	/* now determine if the target has any children at all
	 * and if not, nuke it */
	scsi_target_reap(starget);

	put_device(&starget->dev);
}
scsi_probe_and_add_lun函数分为两个过程:

发送SCSI INQUIRY命令探测逻辑单元(scsi_probe_lun)

如果响应表明逻辑单元存在并且有效,将它添加到系统(scsi_add_lun)

scsi_probe_lun和scsi_add_lun不再分析,具体可以看代码。

3. 以scsi  disk为例来说明scsi高层驱动

当module_init(init_sd);里面的函数初始化时,就会触发scsi总线上scsi_device和scsi_driver的匹配

static int __init init_sd(void)
{
	int majors = 0, i, err;

	SCSI_LOG_HLQUEUE(3, printk("init_sd: sd driver entry point\n"));

	for (i = 0; i < SD_MAJORS; i++)
		if (register_blkdev(sd_major(i), "sd") == 0)
			majors++;

	if (!majors)
		return -ENODEV;

	err = class_register(&sd_disk_class);
	if (err)
		goto err_out;

	err = scsi_register_driver(&sd_template.gendrv);
	if (err)
		goto err_out_class;

	sd_cdb_cache = kmem_cache_create("sd_ext_cdb", SD_EXT_CDB_SIZE,
					 0, 0, NULL);
	if (!sd_cdb_cache) {
		printk(KERN_ERR "sd: can't init extended cdb cache\n");
		goto err_out_class;
	}

	sd_cdb_pool = mempool_create_slab_pool(SD_MEMPOOL_SIZE, sd_cdb_cache);
	if (!sd_cdb_pool) {
		printk(KERN_ERR "sd: can't init extended cdb pool\n");
		goto err_out_cache;
	}

	return 0;

err_out_cache:
	kmem_cache_destroy(sd_cdb_cache);

err_out_class:
	class_unregister(&sd_disk_class);
err_out:
	for (i = 0; i < SD_MAJORS; i++)
		unregister_blkdev(sd_major(i), "sd");
	return err;
}
static int sd_probe(struct device *dev)
{
	struct scsi_device *sdp = to_scsi_device(dev);
	struct scsi_disk *sdkp;
	struct gendisk *gd;
	int index;
	int error;

	error = -ENODEV;
	if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD && sdp->type != TYPE_RBC)
		goto out;

	SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp,
					"sd_attach\n"));

	error = -ENOMEM;
	sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);	//分配scsi_disk结构
	if (!sdkp)
		goto out;

	gd = alloc_disk(SD_MINORS);	//分配gendisk,向更高层BLOCK IO层分配
	if (!gd)
		goto out_free;

	do {
		if (!ida_pre_get(&sd_index_ida, GFP_KERNEL))
			goto out_put;

		spin_lock(&sd_index_lock);
		error = ida_get_new(&sd_index_ida, &index);
		spin_unlock(&sd_index_lock);
	} while (error == -EAGAIN);

	if (error)
		goto out_put;

	if (index >= SD_MAX_DISKS) {
		error = -ENODEV;
		sdev_printk(KERN_WARNING, sdp, "SCSI disk (sd) name space exhausted.\n");
		goto out_free_index;
	}
	//终于找到U盘为神马叫sd的函数了
	error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN);
	if (error)
		goto out_free_index;

	//几个比较重要的指针赋值
	sdkp->device = sdp;
	sdkp->driver = &sd_template;
	sdkp->disk = gd;
	sdkp->index = index;
	atomic_set(&sdkp->openers, 0);

	if (!sdp->request_queue->rq_timeout) {
		if (sdp->type != TYPE_MOD)
			blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
		else
			blk_queue_rq_timeout(sdp->request_queue,
					     SD_MOD_TIMEOUT);
	}

	device_initialize(&sdkp->dev);
	sdkp->dev.parent = dev;
	sdkp->dev.class = &sd_disk_class;
	dev_set_name(&sdkp->dev, dev_name(dev));

	if (device_add(&sdkp->dev))	//添加到系统
		goto out_free_index;

	get_device(dev);
	dev_set_drvdata(dev, sdkp);

	get_device(&sdkp->dev);	/* prevent release before async_schedule */
	async_schedule(sd_probe_async, sdkp);	//剩下的初始化工作

	return 0;

 out_free_index:
	spin_lock(&sd_index_lock);
	ida_remove(&sd_index_ida, index);
	spin_unlock(&sd_index_lock);
 out_put:
	put_disk(gd);
 out_free:
	kfree(sdkp);
 out:
	return error;
}
其实说白了就是:当匹配到了一个scsi_device,而且此scsi设备的类型域是scsi disk时,就触发sd_probe分配scsi_disk结构和gendisk结构,然后建立起对上(gendisk)和对下(scsi_device)的关系。内核中大部分驱动的原理都是如此,建立每一层之间的关系。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值