当前位置:   article > 正文

SCSI系列三:linux SCSI 子系统二十-scsi_transport_fc(4)

SCSI系列三:linux SCSI 子系统二十-scsi_transport_fc(4)

scsi_transport_fc.c

scsi_transport_fc.c是一个实现SCSI传输层(SCSI Transport)的C语言源文件,其中实现了与FC(Fibre Channel)传输相关的功能。在这个文件中,将实现struct fc_function_template中声明的函数,以及其他与FC传输层相关的功能。

这个文件通常包含了以下内容:

  1. struct fc_function_template: 用于描述传输层与驱动程序之间通信的函数指针结构,其中包含了一组用于通信的函数指针。

  2. FC传输层的初始化函数和清理函数,用于连接和释放传输层与驱动程序之间的通信接口。

  3. 用于处理虚拟端口(vport)和远程端口(rport)的创建、删除和状态变更的函数。

  4. 用于处理FC事件的函数,包括向主机对象发布FC事件,处理性能影响通知(FPIN)等。

  5. 用于阻止远程端口I/O操作和SCSI异常处理的函数。

  6. 其他与FC传输层相关的功能函数。

scsi_transport_fc.c`是实现FC传输层与SCSI层之间交互的重要源文件,它提供了传输层与驱动程序之间的接口,并实现了管理虚拟端口和远程端口、处理FC事件以及其他与FC传输层相关的功能。

fc_internal


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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

struct fc_internal 是一个用于存储FC传输层(Fibre Channel Transport)内部数据的结构体。这个结构体包含了用于传输层的模板(struct scsi_transport_template t)和函数(struct fc_function_template *f),以及一些用于存储和管理传输层内部属性的数据。

主要成员包括:

  1. struct scsi_transport_template t: 这是一个scsi_transport_template类型的成员,表示SCSI传输层的模板。在FC传输层中,使用SCSI传输层作为基础,然后扩展FC传输层的功能。

  2. struct fc_function_template *f: 这是一个指向struct fc_function_template类型的指针,表示用于传输层和驱动程序之间通信的函数指针结构。

  3. 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)进行交互。

  4. 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 数组包含指向这些属性结构的指针,用于与中间层进行交互。

  5. struct transport_container rport_attr_contstruct 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 数组包含指向这些属性结构的指针。

  6. struct transport_container vport_attr_contstruct 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传输层的内部数据。

fc_target_setup

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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

这段代码定义了一个 FC(Fibre Channel)传输层的初始化函数 fc_target_setup,并通过宏 DECLARE_TRANSPORT_CLASS 来创建一个名为 fc_transport_class 的传输层类。

  1. 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 端口的属性中获取对应的值),否则将属性初始化为特定的非值。

  2. DECLARE_TRANSPORT_CLASS: 这是一个宏,用于创建一个传输层类。宏的参数包括:

    • fc_transport_class: 这是传输层类的名称。
    • "fc_transport": 这是传输层类的标识符,通常是一个字符串,用于在内核中唯一标识传输层类。
    • fc_target_setup: 这是传输层类的初始化函数,用于设置传输层类的各个属性。
    • NULL: 这是传输层类的检测函数,用于检测传输层是否可用,这里为 NULL 表示没有检测函数。
    • NULL: 这是传输层类的卸载函数,用于在传输层卸载时执行清理操作,这里为 NULL 表示没有卸载函数。

通过上述代码,成功创建了一个名为 fc_transport_class 的 FC 传输层类,其中定义了初始化函数 fc_target_setup。在创建此类的实例时,将会调用该函数来设置 FC 目标设备的属性。

fc_host_setup

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81

这段代码定义了一个 FC(Fibre Channel)主机的初始化函数 fc_host_setup,用于设置 FC 主机的属性。在 FC 传输层中,一个 SCSI 主机(Scsi_Host)可以对应一个 FC 主机,即 Fibre Channel HBA。

函数中的主要步骤如下:

  1. 获取 FC 主机属性结构体 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);,其中 shost_to_fc_host 宏用于从 SCSI 主机 struct Scsi_Host *shost 中获取对应的 FC 主机属性结构体。

  2. 初始化 FC 主机的属性为默认值或特定的非值。这些属性包括 FC 主机的名称、支持的速率、支持的功能等。

  3. 为 FC 主机创建两个工作队列(workqueue)用于处理不同的工作任务。一个是 work_q,用于处理主机相关的工作任务;另一个是 devloss_work_q,用于处理设备丢失超时的工作任务。

  4. 调用 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);

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

这段代码定义了 FC 主机(FC_Host)以及相关的远程端口(Remote Port)和虚拟端口(Virtual Port)的传输类(Transport Class)。传输类是用于连接 Linux SCSI 中间层(SCSI midlayer)和 FC 驱动的接口。

  1. fc_host_remove 函数是用于移除 FC 主机的操作。在移除主机时,它会调用 fc_bsg_remove 函数,将与该主机关联的 BSG 相关信息进行清理。

  2. DECLARE_TRANSPORT_CLASS 宏用于定义传输类,它包含了传输类的名称、设置(Setup)函数、移除(Remove)函数以及其他相关信息。对于 FC 主机类,我们可以通过 fc_host_setup 初始化主机属性,通过 fc_host_remove 移除主机。而对于远程端口和虚拟端口,不需要进行特定的设置和移除操作,因此设置为 NULL。

  3. 定义了三个传输类:fc_host_class 用于 FC 主机,fc_rport_class 用于远程端口,fc_vport_class 用于虚拟端口。

这些传输类将在 FC 传输层中与 Linux SCSI 中间层进行交互,以实现 FC 主机、远程端口和虚拟端口的管理和操作。

fc_get_event_number

/*
 * 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

这段代码实现了一个用于获取下一个顺序 FC 事件号的函数 fc_get_event_number。它使用原子变量 fc_event_seq 来实现自增的功能,确保在多线程环境下获取唯一的事件号。

函数的执行流程如下:

  1. atomic_add_return(1, &fc_event_seq) 对原子变量 fc_event_seq 进行自增操作,并返回自增后的结果,即下一个顺序 FC 事件号。
  2. 函数返回获取到的事件号。

该函数通常用于生成唯一的事件号,并在发送 FC 事件消息时使用,以确保事件号的唯一性。

fc_host_post_fc_event

/**
 * 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77

这段代码实现了一个用于在 fc_host 上发布事件的函数 fc_host_post_fc_event。该函数用于发布 FC 主机事件,将事件信息通过 Netlink 协议传递给用户空间,以便用户空间可以监听和处理这些事件。

函数的执行流程如下:

  1. 首先检查传入的参数,包括事件数据 data_buf 和数据长度 data_len
  2. 如果没有有效的套接字 (scsi_nl_sock) 用于发送事件,则返回错误。
  3. 计算事件数据的总长度 len,并创建一个新的 skb (socket buffer) 来准备发送事件数据。
  4. 构建 Netlink 消息头,并将事件数据填充到 skb 中的 nlh (Netlink 消息头) 中。
  5. 使用 nlmsg_multicast 函数将 skb 发送到指定的 Netlink 组 (SCSI_NL_GRP_FC_EVENTS),以传递事件数据给用户空间监听。
  6. 如果发送失败,打印警告信息,指示事件发送失败。

这个函数允许 FC 主机在发生特定事件时,通知用户空间,并将相关的数据一并传递给用户空间,以供用户空间进行处理和分析。

fc_host_post_event

/**
 * 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

这段代码实现了一个函数 fc_host_post_event,用于在 fc_host 上发布事件。这个函数是对之前提到的 fc_host_post_fc_event 函数的一个简化封装,用于方便地发布事件并将事件数据转换为 32 位整数的形式。

函数的执行流程如下:

  1. 调用 fc_host_post_fc_event 函数,将参数传递给它。
  2. event_data 参数转换为一个指向 32 位整数的指针,并指定数据长度为 4 字节。
  3. 调用 fc_host_post_fc_event 来发布事件,并传递相应的参数。

这个函数允许 FC 主机在发生特定事件时,通过一个简化的接口来通知用户空间,并将事件数据以 32 位整数的形式一并传递给用户空间。

fc_host_post_vendor_event


/**
 * 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);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

这段代码实现了一个函数 fc_host_post_vendor_event,用于在 fc_host 上发布供应商特定的事件(Vendor Unique Event)。这个函数是对之前提到的 fc_host_post_fc_event 函数的另一个简化封装,用于方便地发布供应商特定的事件,并将供应商特定的数据传递给用户空间。

函数的执行流程如下:

  1. 调用 fc_host_post_fc_event 函数,将参数传递给它。
  2. 设置事件代码为 FCH_EVT_VENDOR_UNIQUE,以指示这是一个供应商特定的事件。
  3. 传递 data_lendata_buf 参数,用于指定供应商特定的数据。
  4. 传递 vendor_id 参数,以标识供应商的唯一标识。

这个函数允许 FC 主机在发生供应商特定的事件时,通过一个简化的接口来通知用户空间,并将供应商特定的数据一并传递给用户空间。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/酷酷是懒虫/article/detail/1003480
推荐阅读
相关标签
  

闽ICP备14008679号