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) 机制,可以为到光纤的端到端连接分配多个 N_Port_ID。即使每个 N_Port_ID 共享一个到交换机的物理链路进行通信,它也会以独立端口的形式显示在光纤上的其他端点上。每个 N_Port_ID 可以基于光纤区域和阵列 lun 掩码对光纤具有唯一视图(就像普通的非 NPIV 适配器一样)。通过使用虚拟光纤 (VF) 机制,在每个帧中添加光纤头允许端口与光纤端口交互以加入多个光纤。端口将在其加入的每个光纤上获取 N_Port_ID。每个光纤都会有其自己对端点和配置参数的唯一视图。NPIV 可以与 VF 一起使用,以便端口可以在每个虚拟光纤上获取多个 N_Port_ID。

FC 传输现在正在识别一个新对象 - vport。vport 是一个具有全球唯一的世界范围端口名称 (wwpn) 和世界范围节点名称 (wwnn) 的实体。传输还允许为 vport 指定 FC4,其中 FCP_Initiator 是预期的主要角色。一旦通过上述方法之一实例化,它将具有不同的 N_Port_ID 以及光纤端点和存储实体的视图。与物理适配器关联的 fc_host 将导出创建 vport 的功能。传输将在 Linux 设备树中创建 vport 对象,并指示 fc_host 的驱动程序实例化虚拟端口。通常,驱动程序将在 vport 上创建一个新的 scsi_host 实例,从而为 vport 产生一个唯一的 <H,C,T,L> 命名空间。因此,无论 FC 端口是基于物理端口还是虚拟端口,每个端口都将显示为具有自己目标和 lun 空间的唯一 scsi_host。

注意

目前,传输的编写仅用于创建基于 NPIV 的 vport。但是,考虑了基于 VF 的 vport,如果需要添加支持,则应该是一个小的更改。其余讨论将集中在 NPIV 上。

注意

世界范围名称分配(和唯一性保证)由控制 vport 的管理实体决定。例如,如果要将 vport 与虚拟机关联,则 XEN 管理实用程序将负责使用其自己的命名机构和 OUI 为 vport 创建 wwpn/wwnn。(注意:它已经为虚拟 MAC 地址执行此操作)。

设备树和 Vport 对象:

如今,设备树通常包含 scsi_host 对象,其下是 rports 和 scsi 目标对象。当前,FC 传输创建 vport 对象并将其放置在对应于物理适配器的 scsi_host 对象下。LLDD 将为 vport 分配一个新的 scsi_host 并将其对象链接到 vport 下。vport 的 scsi_host 下的树的其余部分与非 NPIV 的情况相同。当前编写的传输可以轻松地允许 vport 的父级不是 scsi_host。将来可以使用它将对象链接到特定于 vm 的设备树。如果 vport 的父级不是物理端口的 scsi_host,则将在物理端口的 scsi_host 中放置一个指向 vport 对象的符号链接。

以下是设备树中的预期内容

典型的物理端口的 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 类对象,为 vport 添加了以下属性

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>”字符串写入该属性。传输将使用相同的 WWN 在 fc_host 上找到 vport 并将其拆除。每个 WWN 都被指定为 16 个十六进制字符,并且可能包含任何前缀(例如 0x、x 等)。

Vport 状态

Vport 实例化包含两个部分

  • 使用内核和 LLDD 创建。这意味着将建立所有传输和驱动程序数据结构,并创建设备对象。这等效于适配器上的驱动程序“附加”,与适配器的链路状态无关。

  • 通过 ELS 流量等在 FC 链路上实例化 vport。这等效于“链路启动”和链路初始化成功。

有关更多信息,请参阅下面有关 Vport 创建的接口部分。

一旦 vport 使用内核/LLDD 实例化,就可以通过 sysfs 属性报告 vport 状态。存在以下状态

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 - 没有光纤支持

vport 无法运行。遇到以下条件之一

  • FC 拓扑不是点对点

  • FC 端口未连接到 F_Port

  • F_Port 指示不支持 NPIV。

FC_VPORT_NO_FABRIC_RSCS - 没有光纤资源

vport 无法运行。光纤 FDISC 失败,状态指示它没有足够的资源来完成操作。

FC_VPORT_FABRIC_LOGOUT - 光纤注销

vport 无法运行。光纤已 LOGO’d 与 vport 关联的 N_Port_ID。

FC_VPORT_FABRIC_REJ_WWN - 光纤拒绝的 WWN

vport 无法运行。光纤 FDISC 失败,状态指示 WWN 无效。

FC_VPORT_FAILED - VPort 失败

vport 无法运行。这是所有其他错误条件的统称。

以下状态表指示不同的状态转换

状态

事件

新状态

不适用

初始化

未知

未知

链路断开

链路断开

链路启动 & 循环

没有光纤支持

链路启动 & 没有光纤

没有光纤支持

链路启动 & FLOGI 响应指示不支持 NPIV

没有光纤支持

链路启动 & 正在发送 FDISC

正在初始化

禁用请求

禁用

链路断开

链路启动

未知

正在初始化

FDISC ACC

活动

FDISC LS_RJT w/ 没有资源

没有光纤资源

FDISC LS_RJT w/ 无效的 pname 或无效的 nport_id

光纤拒绝的 WWN

FDISC LS_RJT 由于其他原因失败

VPort 失败

链路断开

链路断开

禁用请求

禁用

禁用

启用请求

未知

活动

从光纤接收到 LOGO

光纤注销

链路断开

链路断开

禁用请求

禁用

光纤注销

链路仍然启动

未知

以下 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 对象

禁用

如果为“true”,则创建的虚拟端口将处于禁用状态。如果为“false”,则创建虚拟端口后立即启用。

当请求创建新的虚拟端口(通过 sgio/netlink 或 vport_create fc_host 属性)时,传输层将验证 LLDD 是否支持另一个虚拟端口(例如,max_npiv_vports > npiv_vports_inuse)。如果不支持,则创建请求将失败。如果空间足够,传输层将增加虚拟端口计数,创建虚拟端口对象,然后使用新分配的虚拟端口对象调用 LLDD 的 vport_create() 函数。

如上所述,虚拟端口的创建分为两个部分

  • 使用内核和 LLDD 创建。这意味着将建立所有传输和驱动程序数据结构,并创建设备对象。这等效于适配器上的驱动程序“附加”,与适配器的链路状态无关。

  • 通过 ELS 流量等在 FC 链路上实例化 vport。这等效于“链路启动”和链路初始化成功。

LLDD 的 vport_create() 函数不会同步等待两个部分都完全完成后才返回。它必须验证是否存在支持 NPIV 的基础设施,并在返回之前完成虚拟端口创建的第一部分(数据结构构建)。我们不将 vport_create() 绑定到链路侧操作,主要是因为

  • 链路可能已断开。这并非故障。它只是意味着虚拟端口处于不可操作状态,直到链路恢复。这与虚拟端口创建后链路弹跳的情况一致。

  • 虚拟端口可能会在禁用状态下创建。

  • 这与以下模型一致:虚拟端口等同于 FC 适配器。vport_create 与驱动程序附加到适配器同义,它独立于链路状态。

注意

已定义特殊的错误代码来区分基础设施故障情况,以便更快地解决问题。

LLDD 的 vport_create() 函数的预期行为是

  • 验证基础设施

    • 如果驱动程序或适配器无法支持另一个虚拟端口,无论是

      由于固件不正确,(关于)max_npiv 的虚报,或者缺少其他资源 - 返回 VPCERR_UNSUPPORTED。

    • 如果驱动程序根据适配器上已激活的 WWN 验证 WWN,

      并检测到重叠 - 返回 VPCERR_BAD_WWN。

    • 如果驱动程序检测到拓扑是环路、非光纤网络,或者

      FLOGI 不支持 NPIV - 返回 VPCERR_NO_FABRIC_SUPP。

  • 分配数据结构。如果遇到错误,例如内存不足,

    返回相应的负 Exxx 错误代码。

  • 如果角色是 FCP 发起方,则 LLDD 将执行以下操作:

    • 调用 scsi_host_alloc() 为虚拟端口分配 scsi_host。

    • 调用 scsi_add_host(new_shost, &vport->dev) 以启动 scsi_host 并将其绑定为虚拟端口设备的子设备。

    • 初始化 fc_host 属性值。

  • 根据禁用标志和

    链路状态启动进一步的虚拟端口状态转换 - 并返回成功(零)。

LLDD 实现者注意事项

  • 建议物理端口和虚拟端口使用不同的 fc_function_templates。物理端口的模板将具有 vport_create、vport_delete 和 vport_disable 函数,而虚拟端口则不具有。

  • 建议物理端口和虚拟端口使用不同的 scsi_host_templates。可能存在嵌入在 scsi_host_template 中的驱动程序属性,仅适用于物理端口(链路速度、拓扑设置等)。这确保了这些属性适用于各自的 scsi_host。

虚拟端口禁用/启用

LLDD vport_disable() 的语法是

int vport_disable(struct fc_vport *vport, bool disable)

其中

vport

是要启用还是禁用虚拟端口

禁用

如果为“true”,则禁用虚拟端口。如果为“false”,则启用虚拟端口。

当请求更改虚拟端口的禁用状态时,传输层将根据现有虚拟端口状态验证请求。如果请求禁用且虚拟端口已禁用,则请求将失败。同样,如果请求启用且虚拟端口未处于禁用状态,则请求将失败。如果请求对虚拟端口状态有效,则传输层将调用 LLDD 来更改虚拟端口的状态。

在 LLDD 中,如果禁用虚拟端口,它仍会通过内核和 LLDD 实例化,但它不会以任何方式在 FC 链路上激活或可见。(请参阅虚拟端口创建和两部分实例化讨论)。虚拟端口将保持此状态,直到被删除或重新启用。启用虚拟端口时,LLDD 会在 FC 链路上重新实例化虚拟端口 - 本质上是重新启动 LLDD 状态机(请参阅上面的虚拟端口状态)。

虚拟端口删除

LLDD vport_delete() 的语法是

int vport_delete(struct fc_vport *vport)

其中

vport:是要删除的虚拟端口

当请求删除虚拟端口(通过 sgio/netlink 或通过 fc_host 或 fc_vport vport_delete 属性)时,传输层将调用 LLDD 以终止 FC 链路上的虚拟端口,并拆除所有其他数据结构和引用。如果 LLDD 成功完成,则传输层将拆除虚拟端口对象并完成虚拟端口的删除。如果 LLDD 删除请求失败,则虚拟端口对象将保留,但将处于不确定状态。

在 LLDD 中,应遵循 scsi_host 拆除的正常代码路径。例如,如果虚拟端口具有 FCP 发起方角色,则 LLDD 将为虚拟端口的 scsi_host 调用 fc_remove_host(),然后为虚拟端口的 scsi_host 调用 scsi_remove_host()scsi_host_put()

其他
fc_host port_type 属性

有一个新的 fc_host port_type 值 - FC_PORTTYPE_NPIV。必须在所有基于虚拟端口的 fc_hosts 上设置此值。通常,在物理端口上,port_type 属性将根据拓扑类型和光纤网络的存在设置为 NPORT、NLPORT 等。由于这不适用于虚拟端口,因此报告用于创建虚拟端口的 FC 机制更有意义。

驱动程序卸载

FC 驱动程序需要在调用 scsi_remove_host() 之前调用 fc_remove_host()。这允许 fc_host 在 scsi_host 被拆除之前拆除所有远程端口。已更新 fc_remove_host() 调用,以便删除 fc_host 的所有虚拟端口。

传输层提供的函数

以下函数由 FC 传输层提供,供 LLD 使用。

fc_vport_create

创建虚拟端口

fc_vport_terminate

分离并移除虚拟端口

详细信息

/**
* 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