SCSI FC 传输¶
日期:2008 年 11 月 18 日
功能相关的内核修订
rports : <<TBS>>
vports : 2.6.22
bsg support : 2.6.30 (?TBD?)
引言¶
本文件记录了 SCSI FC 传输的功能和组件。它还提供了传输与 FC LLDD 之间的 API 文档。
FC 传输可在以下位置找到:
drivers/scsi/scsi_transport_fc.c
include/scsi/scsi_transport_fc.h
include/scsi/scsi_netlink_fc.h
include/scsi/scsi_bsg_fc.h
此文件位于 SCSI FC 传输
FC 远程端口 (rports)¶
<< 待补充 >>
FC 虚拟端口 (vports)¶
概述¶
新的 FC 标准定义了允许单个物理端口显示为多个通信端口的机制。使用 N_Port ID 虚拟化 (NPIV) 机制,可以为到 Fabric 的点对点连接分配多个 N_Port_ID。每个 N_Port_ID 在 Fabric 上显示为独立的端口,尽管它共享一个物理链路与交换机进行通信。每个 N_Port_ID 可以根据 Fabric 分区和阵列 LUN 掩码拥有独特的 Fabric 视图(就像一个普通的非 NPIV 适配器一样)。使用虚拟 Fabric (VF) 机制,为每个帧添加 Fabric 头部允许端口与 Fabric 端口交互以加入多个 Fabric。端口将在其加入的每个 Fabric 上获取一个 N_Port_ID。每个 Fabric 将拥有其自身独特的端点和配置参数视图。NPIV 可以与 VF 一起使用,以便端口可以在每个虚拟 Fabric 上获取多个 N_Port_ID。
FC 传输现在识别一个新的对象——vport(虚拟端口)。vport 是一个具有全球唯一端口名称 (WWPN) 和全球节点名称 (WWNN) 的实体。传输还允许为 vport 指定 FC4 角色,其中 FCP_Initiator 是预期的主要角色。一旦通过上述方法之一实例化,它将拥有一个独特的 N_Port_ID 和 Fabric 端点及存储实体的视图。与物理适配器关联的 fc_host 将导出创建 vports 的能力。传输将在 Linux 设备树中创建 vport 对象,并指示 fc_host 的驱动程序实例化虚拟端口。通常,驱动程序将在 vport 上创建一个新的 scsi_host 实例,从而为 vport 形成一个唯一的 <H,C,T,L> 命名空间。因此,无论 FC 端口是基于物理端口还是基于虚拟端口,每个都将作为唯一的 scsi_host 出现,拥有自己的目标和 LUN 空间。
注意
目前,传输层仅支持创建基于 NPIV 的 vport。然而,我们已考虑了基于 VF 的 vport,如果需要,添加支持应仅需少量修改。接下来的讨论将集中在 NPIV 上。
注意
全球名称的分配(和唯一性保证)由控制 vport 的管理实体负责。例如,如果 vports 要与虚拟机关联,XEN 管理工具将负责为 vport 创建 WWPN/WWNN,使用其自身的命名权限和 OUI。(注意:它已经为虚拟 MAC 地址做了这些。)
设备树和 Vport 对象:¶
如今,设备树通常包含 scsi_host 对象,其下方有 rport 和 SCSI 目标对象。目前,FC 传输层会创建 vport 对象并将其放置在与物理适配器对应的 scsi_host 对象之下。LLDD 将为 vport 分配一个新的 scsi_host,并将其对象链接到 vport 之下。vport 的 scsi_host 之下树的其余部分与非 NPIV 情况相同。当前编写的传输层易于允许 vport 的父级是 scsi_host 以外的其他对象。这在未来可用于将对象链接到虚拟机特定的设备树。如果 vport 的父级不是物理端口的 scsi_host,则指向 vport 对象的符号链接将放置在物理端口的 scsi_host 中。
以下是设备树中预期的结构:
典型的物理端口 Scsi_Host
/sys/devices/.../host17/并且它具有典型的子树结构
/sys/devices/.../host17/rport-17:0-0/target17:0:0/17:0:0:0:然后 vport 在物理端口上创建
/sys/devices/.../host17/vport-17:0-0然后 vport 的 Scsi_Host 被创建
/sys/devices/.../host17/vport-17:0-0/host18然后树的其余部分继续,例如
/sys/devices/.../host17/vport-17:0-0/host18/rport-18:0-0/target18:0:0/18:0:0:0:以下是 sysfs 树中预期的结构
scsi_hosts: /sys/class/scsi_host/host17 physical port's scsi_host /sys/class/scsi_host/host18 vport's scsi_host fc_hosts: /sys/class/fc_host/host17 physical port's fc_host /sys/class/fc_host/host18 vport's fc_host fc_vports: /sys/class/fc_vports/vport-17:0-0 the vport's fc_vport fc_rports: /sys/class/fc_remote_ports/rport-17:0-0 rport on the physical port /sys/class/fc_remote_ports/rport-18:0-0 rport on the vport
Vport 属性¶
新的 fc_vport 类对象具有以下属性:
- node_name: 只读
vport 的 WWNN
- port_name: 只读
vport 的 WWPN
- roles: 只读
指示 vport 上启用的 FC4 角色。
- symbolic_name: 读写
一个字符串,附加到驱动程序的符号端口名称字符串后,并在交换机上注册以识别 vport。例如,一个管理程序可以将此字符串设置为“Xen Domain 2 VM 5 Vport 2”,并且这组标识符可以在交换机管理屏幕上看到,用于识别该端口。
- vport_delete: 只写
当写入“1”时,将销毁 vport。
- vport_disable: 只写
当写入“1”时,将把 vport 转换到禁用状态。vport 仍将在 Linux 内核中实例化,但不会在 FC 链路上处于活动状态。当写入“0”时,将启用 vport。
- vport_last_state: 只读
指示 vport 的前一状态。请参阅下面关于“Vport 状态”的部分。
- vport_state: 只读
指示 vport 的当前状态。请参阅下面关于“Vport 状态”的部分。
- vport_type: 只读
反映用于创建虚拟端口的 FC 机制。目前仅支持 NPIV。
对于 fc_host 类对象,为 vports 添加了以下属性:
- max_npiv_vports: 只读
指示驱动程序/适配器在 fc_host 上支持的最大 NPIV vport 数量。
- npiv_vports_inuse: 只读
指示在 fc_host 上已实例化的 NPIV vport 数量。
- vport_create: 只写
一个“简单”的创建接口,用于在 fc_host 上实例化一个 vport。将一个“<WWPN>:<WWNN>”字符串写入属性。传输层随后实例化 vport 对象并调用 LLDD 以 FCP_Initiator 角色创建 vport。每个 WWN 都指定为 16 个十六进制字符,并且 *不能* 包含任何前缀(例如 0x, x 等)。
- vport_delete: 只写
一个“简单”的删除接口,用于销毁 vport。将一个“<WWPN>:<WWNN>”字符串写入属性。传输层将定位 fc_host 上具有相同 WWN 的 vport 并将其销毁。每个 WWN 都指定为 16 个十六进制字符,并且 *不能* 包含任何前缀(例如 0x, x 等)。
Vport 状态¶
Vport 实例化包括两部分:
在内核和 LLDD 中创建。这意味着所有传输和驱动程序数据结构都已构建,并且设备对象已创建。这等同于驱动程序在适配器上的“附加”,这与适配器的链路状态无关。
通过 ELS 流量等方式在 FC 链路上实例化 vport。这等同于“链路激活”和成功的链路初始化。
有关 Vport 创建的更多信息,请参阅下面的接口部分。
一旦 vport 在内核/LLDD 中实例化,其状态可以通过 sysfs 属性报告。存在以下状态:
- FC_VPORT_UNKNOWN - 未知
一个临时状态,通常只在 vport 正在内核和 LLDD 中实例化时设置。
- FC_VPORT_ACTIVE - 活动
vport 已在 FC 链路上成功创建。它功能齐全。
- FC_VPORT_DISABLED - 禁用
vport 已实例化,但处于“禁用”状态。vport 未在 FC 链路上实例化。这等同于物理端口的链路“已断开”。
- FC_VPORT_LINKDOWN - 链路断开
vport 无法运行,因为物理链路未运行。
- FC_VPORT_INITIALIZING - 初始化中
vport 正在 FC 链路上实例化。LLDD 将在开始 ELS 流量以创建 vport 之前设置此状态。此状态将持续到 vport 成功创建(状态变为 FC_VPORT_ACTIVE)或失败(状态为以下值之一)。由于此状态是暂时的,它不会保留在“vport_last_state”中。
- FC_VPORT_NO_FABRIC_SUPP - 无 Fabric 支持
vport 无法运行。遇到以下条件之一:
FC 拓扑不是点对点
FC 端口未连接到 F_Port
F_Port 已指示不支持 NPIV。
- FC_VPORT_NO_FABRIC_RSCS - 无 Fabric 资源
vport 无法运行。Fabric 的 FDISC 操作失败,状态指示其没有足够的资源完成该操作。
- FC_VPORT_FABRIC_LOGOUT - Fabric 注销
vport 无法运行。Fabric 已注销与 vport 关联的 N_Port_ID。
- FC_VPORT_FABRIC_REJ_WWN - Fabric 拒绝 WWN
vport 无法运行。Fabric 的 FDISC 操作失败,状态指示 WWN 无效。
- FC_VPORT_FAILED - VPort 失败
vport 无法运行。这是所有其他错误条件的通用捕获。
下表显示了不同的状态转换:
状态
事件
新状态
不适用
初始化
未知
未知
链路断开
链路断开
链路激活 & 环路
无 Fabric 支持
链路激活 & 无 Fabric
无 Fabric 支持
链路激活 & FLOGI 响应指示不支持 NPIV
无 Fabric 支持
链路激活 & 正在发送 FDISC
初始化中
禁用请求
禁用
链路断开
链路激活
未知
初始化中
FDISC ACC
活动
FDISC LS_RJT(无资源)
无 Fabric 资源
FDISC LS_RJT(pname 或 nport_id 无效)
Fabric 拒绝 WWN
FDISC LS_RJT 因其他原因失败
Vport 失败
链路断开
链路断开
禁用请求
禁用
禁用
启用请求
未知
活动
从 Fabric 收到 LOGO
Fabric 注销
链路断开
链路断开
禁用请求
禁用
Fabric 注销
链路仍激活
未知
以下 4 种错误状态都具有相同的转换:
No Fabric Support:
No Fabric Resources:
Fabric Rejected WWN:
Vport Failed:
Disable request Disable
Link goes down Linkdown
传输 <-> LLDD 接口¶
LLDD 对 Vport 的支持
LLDD 通过在传输模板中提供 `vport_create()` 函数来表明对 vport 的支持。此函数的存在将导致在 fc_host 上创建新的属性。作为物理端口相对于传输层完成初始化的一部分,它应该设置 `max_npiv_vports` 属性,以指示驱动程序和/或适配器支持的最大 vport 数量。
Vport 创建
LLDD `vport_create()` 语法为:
int vport_create(struct fc_vport *vport, bool disable)其中
vport
是新分配的 vport 对象
disable
如果为“true”,vport 将在禁用状态下创建。如果为“false”,vport 将在创建时启用。
当请求创建新的 vport 时(通过 sgio/netlink,或 vport_create fc_host 属性),传输层将验证 LLDD 是否可以支持另一个 vport(例如 `max_npiv_vports` > `npiv_vports_inuse`)。如果不支持,创建请求将失败。如果仍有空间,传输层将增加 vport 计数,创建 vport 对象,然后调用 LLDD 的 `vport_create()` 函数,传入新分配的 vport 对象。
如上所述,vport 创建分为两部分:
在内核和 LLDD 中创建。这意味着所有传输和驱动程序数据结构都已构建,并且设备对象已创建。这等同于驱动程序在适配器上的“附加”,这与适配器的链路状态无关。
通过 ELS 流量等方式在 FC 链路上实例化 vport。这等同于“链路激活”和成功的链路初始化。
LLDD 的 `vport_create()` 函数不会在返回之前同步等待两部分完全完成。它必须验证支持 NPIV 的基础设施是否存在,并在返回之前完成 vport 创建的第一部分(数据结构构建)。我们不将 `vport_create()` 依赖于链路侧操作,主要是因为:
链路可能已断开。如果链路断开,这不是故障。它仅表示 vport 处于不可操作状态,直到链路激活。这与 vport 创建后链路跳动的情况一致。
vport 可以在禁用状态下创建。
这与以下模型一致:vport 等同于一个 FC 适配器。`vport_create` 与驱动程序附加到适配器同义,而这与链路状态无关。
注意
已定义了特殊的错误代码,以区分基础设施故障情况,从而加快解决速度。
LLDD `vport_create()` 函数的预期行为是:
验证基础设施
- 如果驱动程序或适配器不能支持另一个 vport,无论是因为
固件不正确、(虚报)`max_npiv`,或缺少其他资源——则返回 `VPCERR_UNSUPPORTED`。
- 如果驱动程序针对已在适配器上活动的 WWN 验证新 WWN,并检测到重叠——则返回 `VPCERR_BAD_WWN`。
适配器并检测到重叠 - 返回 VPCERR_BAD_WWN。
- 如果驱动程序检测到拓扑是环路、非 Fabric,或者
FLOGI 不支持 NPIV——则返回 `VPCERR_NO_FABRIC_SUPP`。
- 分配数据结构。如果遇到错误,例如内存不足,则返回相应的负数 `Exxx` 错误代码。
内存状况,返回相应的负 Exxx 错误代码。
如果角色是 FCP Initiator,LLDD 应:
调用
scsi_host_alloc()
为 vport 分配一个 `scsi_host`。调用 `scsi_add_host(new_shost, &vport->dev)` 以启动 `scsi_host` 并将其绑定为 vport 设备的子级。
初始化 `fc_host` 属性值。
- 根据禁用标志和链路状态启动进一步的 vport 状态转换——并返回成功(零)。
链路状态 - 并返回成功(零)。
LLDD 实现者注意事项
建议物理端口和虚拟端口使用不同的 `fc_function_templates`。物理端口的模板将包含 `vport_create`、`vport_delete` 和 `vport_disable` 函数,而 vport 则不包含。
建议物理端口和虚拟端口使用不同的 `scsi_host_templates`。很可能有些驱动程序属性(如链路速度、拓扑设置等)嵌入在 `scsi_host_template` 中,仅适用于物理端口。这确保了属性适用于各自的 `scsi_host`。
Vport 禁用/启用
LLDD `vport_disable()` 语法为:
int vport_disable(struct fc_vport *vport, bool disable)其中
vport
vport 是启用还是禁用
disable
如果为“true”,vport 将被禁用。如果为“false”,vport 将被启用。
当请求更改 vport 的禁用状态时,传输层将根据现有 vport 状态验证该请求。如果请求是禁用,且 vport 已被禁用,则请求将失败。类似地,如果请求是启用,而 vport 不处于禁用状态,则请求也将失败。如果请求对于 vport 状态有效,传输层将调用 LLDD 更改 vport 的状态。
在 LLDD 内部,如果 vport 被禁用,它仍将在内核和 LLDD 中实例化,但不会以任何方式在 FC 链路上活动或可见。(参见 Vport 创建和两部分实例化讨论)。vport 将保持此状态,直到被删除或重新启用。启用 vport 时,LLDD 将在 FC 链路上重新实例化 vport——本质上是重启 LLDD 状态机(参见上面的 Vport 状态)。
Vport 删除
LLDD `vport_delete()` 语法为:
int vport_delete(struct fc_vport *vport)其中
vport: 要删除的 vport
当请求删除 vport 时(通过 sgio/netlink,或通过 `fc_host` 或 `fc_vport` 的 `vport_delete` 属性),传输层将调用 LLDD 以终止 FC 链路上的 vport,并销毁所有其他数据结构和引用。如果 LLDD 成功完成,传输层将销毁 vport 对象并完成 vport 删除。如果 LLDD 删除请求失败,vport 对象将保留,但会处于不确定状态。
在 LLDD 内部,应遵循 `scsi_host` 拆卸的常规代码路径。例如,如果 vport 具有 FCP Initiator 角色,LLDD 将为 vport 的 `scsi_host` 调用
fc_remove_host()
,然后调用scsi_remove_host()
和scsi_host_put()
。
- 其他
- fc_host 的 `port_type` 属性
新增了一个 `fc_host` `port_type` 值 - `FC_PORTTYPE_NPIV`。此值必须设置在所有基于 vport 的 `fc_host` 上。通常,在物理端口上,`port_type` 属性会根据拓扑类型和 Fabric 的存在设置为 NPORT、NLPORT 等。由于这不适用于 vport,因此报告用于创建 vport 的 FC 机制更有意义。
- 驱动程序卸载
FC 驱动程序必须在调用
scsi_remove_host()
之前调用fc_remove_host()
。这允许 `fc_host` 在 `scsi_host` 拆卸之前拆卸所有远程端口。`fc_remove_host()` 调用已更新,以同时删除 `fc_host` 的所有 vport。
传输层提供的函数¶
以下函数由 FC 传输层提供给 LLD 使用。
`fc_vport_create`
创建 vport
`fc_vport_terminate`
分离并移除 vport
详情
/**
* fc_vport_create - Admin App or LLDD requests creation of a vport
* @shost: scsi host the virtual port is connected to.
* @ids: The world wide names, FC4 port roles, etc for
* the virtual port.
*
* Notes:
* This routine assumes no locks are held on entry.
*/
struct fc_vport *
fc_vport_create(struct Scsi_Host *shost, struct fc_vport_identifiers *ids)
/**
* fc_vport_terminate - Admin App or LLDD requests termination of a vport
* @vport: fc_vport to be terminated
*
* Calls the LLDD vport_delete() function, then deallocates and removes
* the vport from the shost and object tree.
*
* Notes:
* This routine assumes no locks are held on entry.
*/
int
fc_vport_terminate(struct fc_vport *vport)
FC BSG 支持 (CT & ELS 透传等)¶
<< 待补充 >>
致谢¶
以下人员对本文档做出了贡献:
James Smart james.smart@broadcom.com