SCSI 구동 프레임 분석

27278 단어 링크 ux 구동
1. 먼저 SCSI 와 관련 된 데이터 구 조 를 살 펴 보면 주로 scsihost_template (SCSI 호스트 어댑터 템 플 릿), scsihost (SCSI 호스트 어댑터), scsitarget (목표 노드), scsidevice (논리 단위), scsi disk 라면 또 하나의 scsidisk 구조.
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
"); 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
", -error); return error; }
scsi_host_alloc 와 분배 i2c어댑터, 분배 spimaster 는 호스트 어댑터 층 을 분배 하 는 구조 체 와 유사 합 니 다.
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
", shost->host_no, PTR_ERR(shost->ehandler)); goto fail_kfree; } scsi_proc_hostdir_add(shost->hostt); return shost; fail_kfree: kfree(shost); return NULL; }

일반적으로 scsi 호스트 어댑터 가 구동 하 는 prob 에서 먼저 scsialloc_host, 그리고 scsiadd_host, 이어서 scsi 호출scan_host 스 캔 scsi 버스.
scsi 버스 스 캔 의 목적 은 프로 토 콜 의 특정 또는 칩 의 특정한 방식 으로 호스트 어댑터 뒤에 걸 려 있 는 목표 노드 와 논리 부 를 탐지 하여 메모리 에 해당 하 는 데이터 구 조 를 구축 하여 시스템 에 추가 하 는 것 입 니 다.
scsi 중간 층 은 가능 한 ID 와 LUN 으로 INQUIRY 명령 을 구성 한 다음 에 이 INQUIRY 명령 을 블록 IO 시스템 에 제출 합 니 다. 후 자 는 최종 적 으로 중간 층 의 정책 루틴 을 호출 하여 SCSI 명령 으로 추출 한 후 scsi 바 텀 구동 의 quuecommand 리 셋 함 수 를 호출 합 니 다.
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
", shost->host_no, sht->info ? sht->info(shost) : sht->name); if (!shost->can_queue) { printk(KERN_ERR "%s: can_queue = 0 no longer supported
", 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; }
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: 
", __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);
}
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 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);
}

4. 567913. 사실은 말하자면 scsi device 와 일치 하고 이 scsi 장치 의 유형 도 메 인 이 scsi disk 일 때 sd probe 를 촉발 하여 scsi disk 구조 와 gendisk 구 조 를 분배 한 다음 에 위 (gendisk) 와 아래 (scsi device) 의 관 계 를 구축한다. 커 널 의 대부분 구동 원 리 는 이와 같 고 각 층 간 의 관 계 를 구축한다.

좋은 웹페이지 즐겨찾기