赞
踩
scsi_transport_fc.c
是一个实现SCSI传输层(SCSI Transport)的C语言源文件,其中实现了与FC(Fibre Channel)传输相关的功能。在这个文件中,将实现struct fc_function_template
中声明的函数,以及其他与FC传输层相关的功能。
这个文件通常包含了以下内容:
struct fc_function_template
: 用于描述传输层与驱动程序之间通信的函数指针结构,其中包含了一组用于通信的函数指针。
FC传输层的初始化函数和清理函数,用于连接和释放传输层与驱动程序之间的通信接口。
用于处理虚拟端口(vport)和远程端口(rport)的创建、删除和状态变更的函数。
用于处理FC事件的函数,包括向主机对象发布FC事件,处理性能影响通知(FPIN)等。
用于阻止远程端口I/O操作和SCSI异常处理的函数。
其他与FC传输层相关的功能函数。
scsi_transport_fc.c`是实现FC传输层与SCSI层之间交互的重要源文件,它提供了传输层与驱动程序之间的接口,并实现了管理虚拟端口和远程端口、处理FC事件以及其他与FC传输层相关的功能。
struct fc_internal { struct scsi_transport_template t; struct fc_function_template *f; /* * For attributes : each object has : * An array of the actual attributes structures * An array of null-terminated pointers to the attribute * structures - used for mid-layer interaction. * * The attribute containers for the starget and host are are * part of the midlayer. As the remote port is specific to the * fc transport, we must provide the attribute container. */ struct device_attribute private_starget_attrs[ FC_STARGET_NUM_ATTRS]; struct device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1]; struct device_attribute private_host_attrs[FC_HOST_NUM_ATTRS]; struct device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1]; struct transport_container rport_attr_cont; struct device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS]; struct device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1]; struct transport_container vport_attr_cont; struct device_attribute private_vport_attrs[FC_VPORT_NUM_ATTRS]; struct device_attribute *vport_attrs[FC_VPORT_NUM_ATTRS + 1]; }; #define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t)
struct fc_internal
是一个用于存储FC传输层(Fibre Channel Transport)内部数据的结构体。这个结构体包含了用于传输层的模板(struct scsi_transport_template t
)和函数(struct fc_function_template *f
),以及一些用于存储和管理传输层内部属性的数据。
主要成员包括:
struct scsi_transport_template t
: 这是一个scsi_transport_template
类型的成员,表示SCSI传输层的模板。在FC传输层中,使用SCSI传输层作为基础,然后扩展FC传输层的功能。
struct fc_function_template *f
: 这是一个指向struct fc_function_template
类型的指针,表示用于传输层和驱动程序之间通信的函数指针结构。
struct device_attribute private_starget_attrs[FC_STARGET_NUM_ATTRS]
和 struct device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1]
: 这两个数组用于存储虚拟SCSI目标(starget)的属性。private_starget_attrs
数组包含实际的属性结构,而 starget_attrs
数组包含指向这些属性结构的指针,用于与中间层(mid-layer)进行交互。
struct device_attribute private_host_attrs[FC_HOST_NUM_ATTRS]
和 struct device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1]
: 这两个数组用于存储主机(host)的属性。private_host_attrs
数组包含实际的属性结构,而 host_attrs
数组包含指向这些属性结构的指针,用于与中间层进行交互。
struct transport_container rport_attr_cont
和 struct device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS]
和 struct device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1]
: 这些用于存储远程端口(rport)的属性。private_rport_attrs
数组包含实际的属性结构,而 rport_attrs
数组包含指向这些属性结构的指针。
struct transport_container vport_attr_cont
和 struct device_attribute private_vport_attrs[FC_VPORT_NUM_ATTRS]
和 struct device_attribute *vport_attrs[FC_VPORT_NUM_ATTRS + 1]
: 这些用于存储虚拟端口(vport)的属性。private_vport_attrs
数组包含实际的属性结构,而 vport_attrs
数组包含指向这些属性结构的指针。
to_fc_internal(tmpl)
宏用于从传输层模板指针(tmpl
)转换为 struct fc_internal
结构体指针,方便访问和管理FC传输层的内部数据。
static int fc_target_setup(struct transport_container *tc, struct device *dev, struct device *cdev) { struct scsi_target *starget = to_scsi_target(dev); struct fc_rport *rport = starget_to_rport(starget); /* * if parent is remote port, use values from remote port. * Otherwise, this host uses the fc_transport, but not the * remote port interface. As such, initialize to known non-values. */ if (rport) { fc_starget_node_name(starget) = rport->node_name; fc_starget_port_name(starget) = rport->port_name; fc_starget_port_id(starget) = rport->port_id; } else { fc_starget_node_name(starget) = -1; fc_starget_port_name(starget) = -1; fc_starget_port_id(starget) = -1; } return 0; } static DECLARE_TRANSPORT_CLASS(fc_transport_class, "fc_transport", fc_target_setup, NULL, NULL);
这段代码定义了一个 FC(Fibre Channel)传输层的初始化函数 fc_target_setup
,并通过宏 DECLARE_TRANSPORT_CLASS
来创建一个名为 fc_transport_class
的传输层类。
fc_target_setup
: 这个函数用于设置 FC 目标设备的属性。在 FC 传输层中,一个 SCSI 目标设备可以对应一个远程 FC 端口(rport)。此函数首先通过传入的 dev
参数获取 SCSI 目标设备(struct scsi_target *starget = to_scsi_target(dev);
),然后从目标设备中获取对应的 FC 端口(struct fc_rport *rport = starget_to_rport(starget);
)。如果存在 FC 端口,则将目标设备的属性设置为与该 FC 端口相对应的值(即从 FC 端口的属性中获取对应的值),否则将属性初始化为特定的非值。
DECLARE_TRANSPORT_CLASS
: 这是一个宏,用于创建一个传输层类。宏的参数包括:
fc_transport_class
: 这是传输层类的名称。"fc_transport"
: 这是传输层类的标识符,通常是一个字符串,用于在内核中唯一标识传输层类。fc_target_setup
: 这是传输层类的初始化函数,用于设置传输层类的各个属性。NULL
: 这是传输层类的检测函数,用于检测传输层是否可用,这里为 NULL 表示没有检测函数。NULL
: 这是传输层类的卸载函数,用于在传输层卸载时执行清理操作,这里为 NULL 表示没有卸载函数。通过上述代码,成功创建了一个名为 fc_transport_class
的 FC 传输层类,其中定义了初始化函数 fc_target_setup
。在创建此类的实例时,将会调用该函数来设置 FC 目标设备的属性。
static int fc_host_setup(struct transport_container *tc, struct device *dev, struct device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); /* * Set default values easily detected by the midlayer as * failure cases. The scsi lldd is responsible for initializing * all transport attributes to valid values per host. */ fc_host->node_name = -1; fc_host->port_name = -1; fc_host->permanent_port_name = -1; fc_host->supported_classes = FC_COS_UNSPECIFIED; memset(fc_host->supported_fc4s, 0, sizeof(fc_host->supported_fc4s)); fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN; fc_host->maxframe_size = -1; fc_host->max_npiv_vports = 0; memset(fc_host->serial_number, 0, sizeof(fc_host->serial_number)); memset(fc_host->manufacturer, 0, sizeof(fc_host->manufacturer)); memset(fc_host->model, 0, sizeof(fc_host->model)); memset(fc_host->model_description, 0, sizeof(fc_host->model_description)); memset(fc_host->hardware_version, 0, sizeof(fc_host->hardware_version)); memset(fc_host->driver_version, 0, sizeof(fc_host->driver_version)); memset(fc_host->firmware_version, 0, sizeof(fc_host->firmware_version)); memset(fc_host->optionrom_version, 0, sizeof(fc_host->optionrom_version)); fc_host->port_id = -1; fc_host->port_type = FC_PORTTYPE_UNKNOWN; fc_host->port_state = FC_PORTSTATE_UNKNOWN; memset(fc_host->active_fc4s, 0, sizeof(fc_host->active_fc4s)); fc_host->speed = FC_PORTSPEED_UNKNOWN; fc_host->fabric_name = -1; memset(fc_host->symbolic_name, 0, sizeof(fc_host->symbolic_name)); memset(fc_host->system_hostname, 0, sizeof(fc_host->system_hostname)); memset(&fc_host->fpin_stats, 0, sizeof(fc_host->fpin_stats)); fc_host->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN; INIT_LIST_HEAD(&fc_host->rports); INIT_LIST_HEAD(&fc_host->rport_bindings); INIT_LIST_HEAD(&fc_host->vports); fc_host->next_rport_number = 0; fc_host->next_target_id = 0; fc_host->next_vport_number = 0; fc_host->npiv_vports_inuse = 0; snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name), "fc_wq_%d", shost->host_no); fc_host->work_q = alloc_workqueue("%s", 0, 0, fc_host->work_q_name); if (!fc_host->work_q) return -ENOMEM; fc_host->dev_loss_tmo = fc_dev_loss_tmo; snprintf(fc_host->devloss_work_q_name, sizeof(fc_host->devloss_work_q_name), "fc_dl_%d", shost->host_no); fc_host->devloss_work_q = alloc_workqueue("%s", 0, 0, fc_host->devloss_work_q_name); if (!fc_host->devloss_work_q) { destroy_workqueue(fc_host->work_q); fc_host->work_q = NULL; return -ENOMEM; } fc_bsg_hostadd(shost, fc_host); /* ignore any bsg add error - we just can't do sgio */ return 0; }
这段代码定义了一个 FC(Fibre Channel)主机的初始化函数 fc_host_setup
,用于设置 FC 主机的属性。在 FC 传输层中,一个 SCSI 主机(Scsi_Host)可以对应一个 FC 主机,即 Fibre Channel HBA。
函数中的主要步骤如下:
获取 FC 主机属性结构体 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
,其中 shost_to_fc_host
宏用于从 SCSI 主机 struct Scsi_Host *shost
中获取对应的 FC 主机属性结构体。
初始化 FC 主机的属性为默认值或特定的非值。这些属性包括 FC 主机的名称、支持的速率、支持的功能等。
为 FC 主机创建两个工作队列(workqueue)用于处理不同的工作任务。一个是 work_q
,用于处理主机相关的工作任务;另一个是 devloss_work_q
,用于处理设备丢失超时的工作任务。
调用 fc_bsg_hostadd(shost, fc_host);
将 FC 主机添加到 BSG(Block SCSI Generic)子系统中,以支持 BSG 相关的功能。
总体上,fc_host_setup
函数负责初始化 FC 主机的属性,并为其创建相应的工作队列,以便在主机运行时处理不同的事件和任务。
static int fc_host_remove(struct transport_container *tc, struct device *dev, struct device *cdev) { struct Scsi_Host *shost = dev_to_shost(dev); struct fc_host_attrs *fc_host = shost_to_fc_host(shost); fc_bsg_remove(fc_host->rqst_q); return 0; } static DECLARE_TRANSPORT_CLASS(fc_host_class, "fc_host", fc_host_setup, fc_host_remove, NULL); /* * Setup and Remove actions for remote ports are handled * in the service functions below. */ static DECLARE_TRANSPORT_CLASS(fc_rport_class, "fc_remote_ports", NULL, NULL, NULL); /* * Setup and Remove actions for virtual ports are handled * in the service functions below. */ static DECLARE_TRANSPORT_CLASS(fc_vport_class, "fc_vports", NULL, NULL, NULL);
这段代码定义了 FC 主机(FC_Host)以及相关的远程端口(Remote Port)和虚拟端口(Virtual Port)的传输类(Transport Class)。传输类是用于连接 Linux SCSI 中间层(SCSI midlayer)和 FC 驱动的接口。
fc_host_remove
函数是用于移除 FC 主机的操作。在移除主机时,它会调用 fc_bsg_remove
函数,将与该主机关联的 BSG 相关信息进行清理。
DECLARE_TRANSPORT_CLASS
宏用于定义传输类,它包含了传输类的名称、设置(Setup)函数、移除(Remove)函数以及其他相关信息。对于 FC 主机类,我们可以通过 fc_host_setup
初始化主机属性,通过 fc_host_remove
移除主机。而对于远程端口和虚拟端口,不需要进行特定的设置和移除操作,因此设置为 NULL。
定义了三个传输类:fc_host_class
用于 FC 主机,fc_rport_class
用于远程端口,fc_vport_class
用于虚拟端口。
这些传输类将在 FC 传输层中与 Linux SCSI 中间层进行交互,以实现 FC 主机、远程端口和虚拟端口的管理和操作。
/* * Netlink Infrastructure */ static atomic_t fc_event_seq; /** * fc_get_event_number - Obtain the next sequential FC event number * * Notes: * We could have inlined this, but it would have required fc_event_seq to * be exposed. For now, live with the subroutine call. * Atomic used to avoid lock/unlock... */ u32 fc_get_event_number(void) { return atomic_add_return(1, &fc_event_seq); } EXPORT_SYMBOL(fc_get_event_number);
这段代码实现了一个用于获取下一个顺序 FC 事件号的函数 fc_get_event_number
。它使用原子变量 fc_event_seq
来实现自增的功能,确保在多线程环境下获取唯一的事件号。
函数的执行流程如下:
atomic_add_return(1, &fc_event_seq)
对原子变量 fc_event_seq
进行自增操作,并返回自增后的结果,即下一个顺序 FC 事件号。该函数通常用于生成唯一的事件号,并在发送 FC 事件消息时使用,以确保事件号的唯一性。
/** * fc_host_post_fc_event - routine to do the work of posting an event * on an fc_host. * @shost: host the event occurred on * @event_number: fc event number obtained from get_fc_event_number() * @event_code: fc_host event being posted * @data_len: amount, in bytes, of event data * @data_buf: pointer to event data * @vendor_id: value for Vendor id * * Notes: * This routine assumes no locks are held on entry. */ void fc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number, enum fc_host_event_code event_code, u32 data_len, char *data_buf, u64 vendor_id) { struct sk_buff *skb; struct nlmsghdr *nlh; struct fc_nl_event *event; const char *name; size_t len, padding; int err; if (!data_buf || data_len < 4) data_len = 0; if (!scsi_nl_sock) { err = -ENOENT; goto send_fail; } len = FC_NL_MSGALIGN(sizeof(*event) - sizeof(event->event_data) + data_len); skb = nlmsg_new(len, GFP_KERNEL); if (!skb) { err = -ENOBUFS; goto send_fail; } nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, len, 0); if (!nlh) { err = -ENOBUFS; goto send_fail_skb; } event = nlmsg_data(nlh); INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC, FC_NL_ASYNC_EVENT, len); event->seconds = ktime_get_real_seconds(); event->vendor_id = vendor_id; event->host_no = shost->host_no; event->event_datalen = data_len; /* bytes */ event->event_num = event_number; event->event_code = event_code; if (data_len) memcpy(event->event_data_flex, data_buf, data_len); padding = len - offsetof(typeof(*event), event_data_flex) - data_len; memset(event->event_data_flex + data_len, 0, padding); nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS, GFP_KERNEL); return; send_fail_skb: kfree_skb(skb); send_fail: name = get_fc_host_event_code_name(event_code); printk(KERN_WARNING "%s: Dropped Event : host %d %s data 0x%08x - err %d\n", __func__, shost->host_no, (name) ? name : "<unknown>", (data_len) ? *((u32 *)data_buf) : 0xFFFFFFFF, err); return; } EXPORT_SYMBOL(fc_host_post_fc_event);
这段代码实现了一个用于在 fc_host 上发布事件的函数 fc_host_post_fc_event
。该函数用于发布 FC 主机事件,将事件信息通过 Netlink 协议传递给用户空间,以便用户空间可以监听和处理这些事件。
函数的执行流程如下:
data_buf
和数据长度 data_len
。scsi_nl_sock
) 用于发送事件,则返回错误。len
,并创建一个新的 skb (socket buffer) 来准备发送事件数据。nlmsg_multicast
函数将 skb 发送到指定的 Netlink 组 (SCSI_NL_GRP_FC_EVENTS
),以传递事件数据给用户空间监听。这个函数允许 FC 主机在发生特定事件时,通知用户空间,并将相关的数据一并传递给用户空间,以供用户空间进行处理和分析。
/** * fc_host_post_event - called to post an even on an fc_host. * @shost: host the event occurred on * @event_number: fc event number obtained from get_fc_event_number() * @event_code: fc_host event being posted * @event_data: 32bits of data for the event being posted * * Notes: * This routine assumes no locks are held on entry. */ void fc_host_post_event(struct Scsi_Host *shost, u32 event_number, enum fc_host_event_code event_code, u32 event_data) { fc_host_post_fc_event(shost, event_number, event_code, (u32)sizeof(u32), (char *)&event_data, 0); } EXPORT_SYMBOL(fc_host_post_event);
这段代码实现了一个函数 fc_host_post_event
,用于在 fc_host 上发布事件。这个函数是对之前提到的 fc_host_post_fc_event
函数的一个简化封装,用于方便地发布事件并将事件数据转换为 32 位整数的形式。
函数的执行流程如下:
fc_host_post_fc_event
函数,将参数传递给它。event_data
参数转换为一个指向 32 位整数的指针,并指定数据长度为 4 字节。fc_host_post_fc_event
来发布事件,并传递相应的参数。这个函数允许 FC 主机在发生特定事件时,通过一个简化的接口来通知用户空间,并将事件数据以 32 位整数的形式一并传递给用户空间。
/** * fc_host_post_vendor_event - called to post a vendor unique event * on an fc_host * @shost: host the event occurred on * @event_number: fc event number obtained from get_fc_event_number() * @data_len: amount, in bytes, of vendor unique data * @data_buf: pointer to vendor unique data * @vendor_id: Vendor id * * Notes: * This routine assumes no locks are held on entry. */ void fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number, u32 data_len, char * data_buf, u64 vendor_id) { fc_host_post_fc_event(shost, event_number, FCH_EVT_VENDOR_UNIQUE, data_len, data_buf, vendor_id); } EXPORT_SYMBOL(fc_host_post_vendor_event);
这段代码实现了一个函数 fc_host_post_vendor_event
,用于在 fc_host 上发布供应商特定的事件(Vendor Unique Event)。这个函数是对之前提到的 fc_host_post_fc_event
函数的另一个简化封装,用于方便地发布供应商特定的事件,并将供应商特定的数据传递给用户空间。
函数的执行流程如下:
fc_host_post_fc_event
函数,将参数传递给它。FCH_EVT_VENDOR_UNIQUE
,以指示这是一个供应商特定的事件。data_len
和 data_buf
参数,用于指定供应商特定的数据。vendor_id
参数,以标识供应商的唯一标识。这个函数允许 FC 主机在发生供应商特定的事件时,通过一个简化的接口来通知用户空间,并将供应商特定的数据一并传递给用户空间。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。