分类:
2010-08-25 17:20:37
前一段时间在Linux平台上做了一个virtual SCSI host driver,该virtual scsi host driver主要实现将scsi command转换成块设备的请求,即将块设备伪装成一个标准scsi设备。在此对scsi host driver的编写做一下总结。
SCSI device driver是SCSI设备驱动,也可以称之为功能驱动(Function driver)。SCSI middle level driver为SCSI中间层驱动,抽象了SCSI的总线逻辑。Scsi host driver控制SCSI总线控制器,实现SCSI数据的物理层传输。
Scsi host driver需要与scsi middle level进行数据交互,在scsi middle level定了标准的scsi host模版,所有接口函数和属性参数都定义在标准模版中。模版的类型为struct scsi_host_template。
Virtual scsi host driver的模版定义如下:
struct scsi_host_template scsi_host_template = {
.module = THIS_MODULE,
.name = SCSI_HOST_IDENT,
.info = scsi_info,
.slave_configure = scsi_slave_configure,
.queuecommand = scsi_queuecommand,
.can_queue = SCSI_HOST_CAN_QUEUE, /* host cmd queue depth */
.cmd_per_lun = SCSI_CMD_PER_LUN, /* lun cmd queue depth */
.sg_tablesize = SG_ALL,
.use_clustering = SCSI_CLUSTERING,
.this_id = SCSI_HOST_ID,
.emulated = 1,
};
.queuecommand:通过该接口函数,scsi middle level将请求提交给scsi host driver。所以该函数是上下层之间的数据通道。
.can_queue:描述了scsi host命令队列的长度。通常scsi host具有一个命令队列,scsi middle level通过queuecommand接口将命令直接挂入host的命令队列中,然后一步返回。
.cmd_per_lun:该参数描述了每个lun通路所能缓存命令的数量。
.sg_tablesize:scatter-gather表的长度。
在scsi host driver中首先需要定义一个描述host的结构。这个结构实际上是Scsi_Host的派生类,继承和扩展了Scsi_Host的功能。vscsi_host_s是virtual scsi host driver中定义的结构。
typedef struct vscsi_host_s {
__u32 host_no; /* host number, Major no */
struct Scsi_Host *scsi_host; /* scsi host object */
/* vscsi device array */
struct vscsi_device_s devices[MAX_CHANNEL][CHANNEL_DEV_NUM];
atomic_t dev_cnt; /* vscsi device counters */
struct semaphore host_lock; /* vscsi device access lock */
/* vscsi command queue */
struct bscsi_cmd_arr_s vscsi_cmd_queue;
}vscsi_host_t;
ü scsi_host为scsi middle level分配的scsi host对象指针。
ü devices为vscsi_host控制器管理的设备,其中MAX_CHANNEL为vscsi_host最大的通道数,CHANNEL_DEV_NUM为vscsi_host最大的lun数。
ü dev_cnt描述了当前vscsi_host管理的设备数量。
ü host_lock为设备访问锁。
ü Vscsi_cmd_queue为vscsi_host的命令队列。scsi middle level可以直接将scsi命令挂在该命令队列上。
Scsi host的初始化过程如下:
1、 理所当然,第一步需要初始化scsi host结构中的所有成员,例如vscsi_cmd_queue等。
2、 通过scsi_host_alloc函数让内核分配一个scsi host,调用方式为:shost = scsi_host_alloc(&scsi_host_template, sizeof(struct bscsi_host_s))。其中scsi_host_template为模版对象,即scsi host的属性以及接口方法。
3、 通过scsi_add_host函数将新分配的scsi host添加到系统中。调用如下:scsi_add_host(shost, NULL)。
4、 调用scsi_scan_host(shost)扫描scsi host。
在scsi host驱动中,Scsi host控制的每个scsi设备都有一个对象。Virtual scsi driver中采用struct vscsi_device_s结构来描述。
在scsi host scan的过程中,驱动程序会扫描scsi host的每个channel、lun,然后回自动probe每个scsi device。在virtual scsi host driver中,用户可以手动的将一个设备添加到scsi host中,并且为其分配channle,id,lun。
添加scsi device的接口函数为__scsi_add_device,实现如下:
sdev = __scsi_add_device(vscsi_host->scsi_host, channel, id, lun, &vscsi_host);
bscsi_host->scsi_host为已经存在的scsi host对象。
channel, id, lun为新添加的scsi device标识。
vscsi_host为vscsi host的对象,将该scsi host对象告诉新创建的scsi device。
通过__scsi_add_device函数,能够在scsi middle level创建一个scsi device,并且建立起scsi device与scsi host之间的关系。
scsi middle level通过queuecommand函数将请求提交给scsi host。Scsi middle level将获取的上层请求封装成scsi command,然后将scsi command递交给scsi host,也就是说scsi host层只能处理scsi command。
在scsi command中存在两种数据传输方式:
1、 buffer的方式(Block-PC),这应该是SCSI驱动中较早的一种方式,其将数据存储在一个buffer中提交给scsi host。
2、 scatter-gather方式。这种方式为聚散DMA方式,对于磁盘的读写基本都采用这种方式,scsi middle level可以很方便的将scsi disk driver提交的bio转换成scatter-gather table,而无须任何的数据拷贝。