SAN存储—存储协议技术

存储领域的技术无疑在日新月异的发展。从应用的角度来说,用户最早因为数据量的增多而产生存储的需求,从而产生最早最简单的存储架构直连附加存储DAS;在满足海量存储的需求后,用户需要对日益增长的数据进行统一有效的管理,并希望有效利用存储空间及存储资源,此时用户对存储产生了集中化和网络化的需求,从而有了存储区域网络SAN的诞生。

最早的SAN采用的是FC协议,随着技术的发展成熟,之后相继出现了以iSCSI协议为基础的iSCSI-SAN和以Infiniband协议为基础的IB-SAN来满足不同环境的网络存储需要。

多协议统一支持

iSCSI是一种在TCP/IP上进行数据块传输的标准,可以实现在IP网络上运行SCSI协议,使其能够在诸如高速千兆以太网上进行快速的数据存取操作;FCP协议是光纤通道和上层应用之间的接口,用于连接工作站、大型机、巨型机、存储设备以及显示设备的高速数据传输机制,它是在光纤信道上的SCSI接口协议。IB-SAN说的主要是SRP协议和ISER协议。SRP协议其主要作用是把SCSI协议的命令和数据通过RDMA的方式在Infiniband网络上传输;ISER协议类似于SRP协议,其主要作用是把iSCSI协议的命令和数据通过RDMA的方式在如Infiniband网络上传输。随着互联技术的发展,以后还会出现其它的协议,每出现一种新的协议,都要开发一种新的Target支持。

在现有的Target设计中,每种Target都是单独实现的,如iSCSI-TARGET,FC-TARGET,这些Target虽然有通用的数据读写处理过程,但没有结合点。

在通常的行业应用中,不仅需要多个Initiator同时访问Target,并且需要让有不同带宽和IO速率要求的应用同时进行连接,如有的应用需要使用光纤通道来访问磁盘阵列,有的客户应用需要使用IB协议来访问磁盘阵列,有的应用需要使用ISCSI协议来访问磁盘阵列,以满足不同的应用需求。

开发了一种多协议通用目标器软件,能够将SCSI命令通过不同的传输协议传输给储存后端设备来处理,如FCP,iSCSI,SRP协议等。在多协议通用目标器软件中,有效地将各种IO的通用处理结合在一起,对其进行了抽象,提取了中间层的概念,并将具体的协议实现与通用的命令处理相分离。

通用目标器的总体结构

多协议通用目标器软件的总体结构如图1所示,主要由三部分组成:协议解释驱动、通用的中间层模块,以及后端设备处理模块。下面对这三部分进行详细说明。

  1. HBA卡接收或者发送消息,如接收来自Initiator的消息或将消息发送到Initiator端。对于每种传输协议,都有相应的协议解释驱动,它的实现完全是协议相关的,在我们的系统中,实现了FC,SRP,iSCSI协议解释驱动,当有新的传输协议出现时,我们只要编写相应的协议相关的部分就可。每个协议解释驱动包含一个消息队列。如果协议解释驱动接收到与协议相关的消息并且并不涉及数据处理,则协议解释驱动将会自己处理,否则消息将被传送给通用的中间层模块来处理。
  2. SCSI命令,并将这些命令放入协议无关的SCSI命令队列中,进行通用的命令处理,Session以及命令的创建,完成LUN到设备的转换,并将这些命令根据不同的设备类型分发到不同的设备上去。并最终调用后端设备句柄来完成命令的处理。它会监控队列中命令的状态,错误,超时,如果命令并不需要后端设备的处理,则中间层完成命令处理并返回给Initiator。

中间层提供了权限控制功能,提供了强大的安全机制来进行权限控制,对每个Initiator会有相应的访问权限。有的Initiator可以访问一组设备,而另外的Initiator可以访问另一组设备。它会创建一个LUN的数据结构,每个LUN创建一个SCSI命令的消息队列,为了控制哪些Initiator能够访问LUN,有一个访问控制链表来控制哪些initiator能够访问哪些LUN。一旦一个SCSI命令被接收,则访问控制链表会检查是否有权限来访问此LUN。对不同的协议,有不同的标识符,如FC卡根据WWN号来区分,ISCSI根据Initiator名字来区分,SCSI根据SCSI ID来区分。

后端处理模块。后段处理模块由SCSI命令处理器来组成,它负责SCSI命令执行。当LUN的SCSI命令队列接收到一个新命令时,SCSI命令处理器调用相应的后端设备句柄来完成这个命令。它通过mupc_register_virdev_driver向中间层注册,这个函数它提供一种方式来注册虚拟设备,这个设备可对远程的initiator可见。如FILEIO模式使用文件将其虚拟成远端可用的scsi disk,也可以使用BLOCKIO模式。

san-target

图1 多协议通用目标器软件的总体结构

为了说明上述三个部分的交互过程,本节介绍了协议解释驱动的注册、session注册及数据读写命令处理。Initiator在需要建立与目标器的连接时,首先要加载协议解释模块模块,完成协议解释驱动的注册过程,当协议解释驱动接收到Initiator的连接请求时,会调用mupc_register_session()函数来完成session的创建,此后initiator就可以开始与目标器的命令交互过程,Initiator端就能看到目标器提供的后端磁盘,并可以对它进行格式化读写。

协议解释驱动调用mupc_register_proto_template()向中间层注册,中间层就知道了协议解释驱动的入口点。mupc_register_proto_template函数输入参数定义如下:

structmupc_trans_template
{
      /* 协议解释驱动的名字 */
const char name[15];
/* 表示异步事件报告是否可以在原子上下文中执行 */
unsigned report_aen_atom:1;
int (* detect) (structmupc_trans_template *trans_template);
int (* release)(structmupc_trans *trans);
/* 传递response数据和状态在结构中 */
int (* xmit_response)(structmupc_cmd *cmd);
/* 通知initiator对应命令数据缓存已经分配好,可以接收数据了 */
int (* rdy_to_xfer)(structmupc_cmd *cmd);
/* 通知驱动命令即将被释放 */
void (*on_free_cmd) (structmup_cmd *cmd);
void (* task_mgmt_fn_done)(structmup_mgmt_cmd *mgmt_cmd);
/* 用于异步事件报告 */
void (* report_aen)(intmgmt_fn, const uint8_t *lun, intlun_len);
}

不同的协议解释驱动的模版mupc_trans_template不同,这样在中间层驱动中就不必关注各种传输协议的具体实现细节,调用统一的接口就可以完成,在协议解释驱动中,则专注于与协议相关的传输处理,减少了开发量,当新增一个协议驱动时,调用此函数向中间层注册,中间层就会调用协议解释驱动的相应函数。

  1. Session 注册

当协议解释驱动确定需要创建新的session时,例如接受新的TCP连接或者接收FC连接,则调用中间层提供的mupc_register_session函数,其输入参数定义如下:
 

Struct mupc_session *mupc_register_session(
      Struct mupc_trans  *trans,
      int atomic,
      const char *initiator_name,
      void *data,
      void (*result_fn) (

             structmupc_session *sess,

             void *data,

             int result));

注:result_fn – session初始化完成后将被异步调用。

此过程从以下几步来完成session的注册:

  1. mupc_session数据结构,将其状态设置成正在初始化状态,并增加session的引用计数;
  2. initiator名查询其可以访问的设备列表,并将此session加入到访问控制组的session列表中;
  3. session中的设备,设置协议解释驱动期待接收的命令序号,分配缓存池,创建数据处理线程;

如果result_fn不为空,则调用协议解释驱动提供的result_fn,并将session初始化过程中的延迟命令取出(包括数据读写命令和任务管理命令),将延迟命令进行初始化,至此整个的初始化过程完成。session初始化过程如图2所示。

san-session

例如,当srp协议解释驱动接收到IB_CM_REQ_RECEIVED消息时,就会调用mupc_register_session来完成session的创建。

  1. 当协议解释驱动调用中间层mupc_rx_cmd函数时,它会分配mupc_cmd命令并初始化相应的域,提取出相应的LUN号并返回。其过程如下:
  2. mupc_cmd命令结构,设置命令的状态,命令的缺省任务属性,命令的超时间隔,命令的引用计数;调用cmd_init_done(),设置cmd时间戳。将命令的session指向当前的session,初始化其协议解释驱动指针以及协议解释驱动操作函数表;增加session的命令计数,将其加入到session的命令队列中。初始化命令时,使用了命令状态机,命令在几个状态之间进行转换。在初始化阶段,设置状态为CMD_STATE_INIT并进init_cmd函数中进行处理。根据命令的LUN号来查找相应的trans_dev,确定命令的数据传输方向,初始化命令的后端设备并将命令关联到后端设备线程;接着需要解析命令,设置命令状态为CMD_STATE_PARSE。在初始化命令过程中从CDB结构中解释出操作码,传输的数据长度。
  3. proc_active_cmd函数进入状态机的下一个处理流程中, 如果命令并不需要数据传输,则由多协议中间层来处理;否则判断状态为CMD_STATE_PARSE,则调用后端设备的parse函数处理scsi命令。在parse函数中,会根据后端设备提供的解析函数,对命令缓冲区长度及超时进行解析并设置。
  4. 函数解析完成以后,设置命令状态为CMD_STATE_PARSE_SPACE,然后分配相应的内存空间。
  5. CMD_STATE_TGT_RPE_EXEC并将命令传递给后端设备执行exec函数来处理;
  6. CMD_STATE_RDY_TO_XFER调用协议解释驱动的rdy_to_xfer函数,告诉协议解释驱动空间已经准备好了并且可以启动数据传输。当协议解释驱动获得所有的数据后,然后调用mupc_rx_data函数交给中间层驱动处理。
  7. CMD_STATE_TGT_PRE_EXEC,如果后端设备提供预处理函数则会先执行预处理工作。接着设置命令状态CMD_STATE_SEND_FOR_EXEC,调用后端设备的exec函数具体处理此写入命令,后端设备会根据scsi的操作码执行相应的scsi命令。

之后设置命令状态为CMD_STATE_PRE_XMIT_RESP,调用协议解释驱动的xmit_response函数来发送响应,当发送完响应后,设置命令状态为CMD_STATE_FINISHED,协议解释驱动调用mupc_trans_cmd_done函数来告诉中间层可以释放命令以及相应的数据缓存。命令处理流程如图3所述:

san-cmd-flow

在协议解释驱动中,如SRP协议,在接收到SRP_CMD消息时,就会调用mupc_rx_cmd函数来生成一个待处理的mupc_cmd命令。

下面分别介绍各个具体协议的实现。

如果引用本站的原创文章,请注明原文链接:,本站保留追究责任的权利!

发表评论