scsi_transport_fc.c

来自「linux 内核源代码」· C语言 代码 · 共 2,128 行 · 第 1/5 页

C
2,128
字号
/* *  FiberChannel transport specific attributes exported to sysfs. * *  Copyright (c) 2003 Silicon Graphics, Inc.  All rights reserved. * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * *  ======== * *  Copyright (C) 2004-2007   James Smart, Emulex Corporation *    Rewrite for host, target, device, and remote port attributes, *    statistics, and service functions... *    Add vports, etc * */#include <linux/module.h>#include <linux/init.h>#include <scsi/scsi_device.h>#include <scsi/scsi_host.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_fc.h>#include <scsi/scsi_cmnd.h>#include <linux/netlink.h>#include <net/netlink.h>#include <scsi/scsi_netlink_fc.h>#include "scsi_priv.h"#include "scsi_transport_fc_internal.h"static int fc_queue_work(struct Scsi_Host *, struct work_struct *);static void fc_vport_sched_delete(struct work_struct *work);/* * This is a temporary carrier for creating a vport. It will eventually * be replaced  by a real message definition for sgio or netlink. * * fc_vport_identifiers: This set of data contains all elements * to uniquely identify and instantiate a FC virtual port. * * Notes: *   symbolic_name: The driver is to append the symbolic_name string data *      to the symbolic_node_name data that it generates by default. *      the resulting combination should then be registered with the switch. *      It is expected that things like Xen may stuff a VM title into *      this field. */struct fc_vport_identifiers {	u64 node_name;	u64 port_name;	u32 roles;	bool disable;	enum fc_port_type vport_type;	/* only FC_PORTTYPE_NPIV allowed */	char symbolic_name[FC_VPORT_SYMBOLIC_NAMELEN];};static int fc_vport_create(struct Scsi_Host *shost, int channel,	struct device *pdev, struct fc_vport_identifiers  *ids,	struct fc_vport **vport);/* * Redefine so that we can have same named attributes in the * sdev/starget/host objects. */#define FC_CLASS_DEVICE_ATTR(_prefix,_name,_mode,_show,_store)		\struct class_device_attribute class_device_attr_##_prefix##_##_name = 	\	__ATTR(_name,_mode,_show,_store)#define fc_enum_name_search(title, table_type, table)			\static const char *get_fc_##title##_name(enum table_type table_key)	\{									\	int i;								\	char *name = NULL;						\									\	for (i = 0; i < ARRAY_SIZE(table); i++) {			\		if (table[i].value == table_key) {			\			name = table[i].name;				\			break;						\		}							\	}								\	return name;							\}#define fc_enum_name_match(title, table_type, table)			\static int get_fc_##title##_match(const char *table_key,		\		enum table_type *value)					\{									\	int i;								\									\	for (i = 0; i < ARRAY_SIZE(table); i++) {			\		if (strncmp(table_key, table[i].name,			\				table[i].matchlen) == 0) {		\			*value = table[i].value;			\			return 0; /* success */				\		}							\	}								\	return 1; /* failure */						\}/* Convert fc_port_type values to ascii string name */static struct {	enum fc_port_type	value;	char			*name;} fc_port_type_names[] = {	{ FC_PORTTYPE_UNKNOWN,		"Unknown" },	{ FC_PORTTYPE_OTHER,		"Other" },	{ FC_PORTTYPE_NOTPRESENT,	"Not Present" },	{ FC_PORTTYPE_NPORT,	"NPort (fabric via point-to-point)" },	{ FC_PORTTYPE_NLPORT,	"NLPort (fabric via loop)" },	{ FC_PORTTYPE_LPORT,	"LPort (private loop)" },	{ FC_PORTTYPE_PTP,	"Point-To-Point (direct nport connection" },	{ FC_PORTTYPE_NPIV,		"NPIV VPORT" },};fc_enum_name_search(port_type, fc_port_type, fc_port_type_names)#define FC_PORTTYPE_MAX_NAMELEN		50/* Reuse fc_port_type enum function for vport_type */#define get_fc_vport_type_name get_fc_port_type_name/* Convert fc_host_event_code values to ascii string name */static const struct {	enum fc_host_event_code		value;	char				*name;} fc_host_event_code_names[] = {	{ FCH_EVT_LIP,			"lip" },	{ FCH_EVT_LINKUP,		"link_up" },	{ FCH_EVT_LINKDOWN,		"link_down" },	{ FCH_EVT_LIPRESET,		"lip_reset" },	{ FCH_EVT_RSCN,			"rscn" },	{ FCH_EVT_ADAPTER_CHANGE,	"adapter_chg" },	{ FCH_EVT_PORT_UNKNOWN,		"port_unknown" },	{ FCH_EVT_PORT_ONLINE,		"port_online" },	{ FCH_EVT_PORT_OFFLINE,		"port_offline" },	{ FCH_EVT_PORT_FABRIC,		"port_fabric" },	{ FCH_EVT_LINK_UNKNOWN,		"link_unknown" },	{ FCH_EVT_VENDOR_UNIQUE,	"vendor_unique" },};fc_enum_name_search(host_event_code, fc_host_event_code,		fc_host_event_code_names)#define FC_HOST_EVENT_CODE_MAX_NAMELEN	30/* Convert fc_port_state values to ascii string name */static struct {	enum fc_port_state	value;	char			*name;} fc_port_state_names[] = {	{ FC_PORTSTATE_UNKNOWN,		"Unknown" },	{ FC_PORTSTATE_NOTPRESENT,	"Not Present" },	{ FC_PORTSTATE_ONLINE,		"Online" },	{ FC_PORTSTATE_OFFLINE,		"Offline" },	{ FC_PORTSTATE_BLOCKED,		"Blocked" },	{ FC_PORTSTATE_BYPASSED,	"Bypassed" },	{ FC_PORTSTATE_DIAGNOSTICS,	"Diagnostics" },	{ FC_PORTSTATE_LINKDOWN,	"Linkdown" },	{ FC_PORTSTATE_ERROR,		"Error" },	{ FC_PORTSTATE_LOOPBACK,	"Loopback" },	{ FC_PORTSTATE_DELETED,		"Deleted" },};fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)#define FC_PORTSTATE_MAX_NAMELEN	20/* Convert fc_vport_state values to ascii string name */static struct {	enum fc_vport_state	value;	char			*name;} fc_vport_state_names[] = {	{ FC_VPORT_UNKNOWN,		"Unknown" },	{ FC_VPORT_ACTIVE,		"Active" },	{ FC_VPORT_DISABLED,		"Disabled" },	{ FC_VPORT_LINKDOWN,		"Linkdown" },	{ FC_VPORT_INITIALIZING,	"Initializing" },	{ FC_VPORT_NO_FABRIC_SUPP,	"No Fabric Support" },	{ FC_VPORT_NO_FABRIC_RSCS,	"No Fabric Resources" },	{ FC_VPORT_FABRIC_LOGOUT,	"Fabric Logout" },	{ FC_VPORT_FABRIC_REJ_WWN,	"Fabric Rejected WWN" },	{ FC_VPORT_FAILED,		"VPort Failed" },};fc_enum_name_search(vport_state, fc_vport_state, fc_vport_state_names)#define FC_VPORTSTATE_MAX_NAMELEN	24/* Reuse fc_vport_state enum function for vport_last_state */#define get_fc_vport_last_state_name get_fc_vport_state_name/* Convert fc_tgtid_binding_type values to ascii string name */static const struct {	enum fc_tgtid_binding_type	value;	char				*name;	int				matchlen;} fc_tgtid_binding_type_names[] = {	{ FC_TGTID_BIND_NONE, "none", 4 },	{ FC_TGTID_BIND_BY_WWPN, "wwpn (World Wide Port Name)", 4 },	{ FC_TGTID_BIND_BY_WWNN, "wwnn (World Wide Node Name)", 4 },	{ FC_TGTID_BIND_BY_ID, "port_id (FC Address)", 7 },};fc_enum_name_search(tgtid_bind_type, fc_tgtid_binding_type,		fc_tgtid_binding_type_names)fc_enum_name_match(tgtid_bind_type, fc_tgtid_binding_type,		fc_tgtid_binding_type_names)#define FC_BINDTYPE_MAX_NAMELEN	30#define fc_bitfield_name_search(title, table)			\static ssize_t							\get_fc_##title##_names(u32 table_key, char *buf)		\{								\	char *prefix = "";					\	ssize_t len = 0;					\	int i;							\								\	for (i = 0; i < ARRAY_SIZE(table); i++) {		\		if (table[i].value & table_key) {		\			len += sprintf(buf + len, "%s%s",	\				prefix, table[i].name);		\			prefix = ", ";				\		}						\	}							\	len += sprintf(buf + len, "\n");			\	return len;						\}/* Convert FC_COS bit values to ascii string name */static const struct {	u32 			value;	char			*name;} fc_cos_names[] = {	{ FC_COS_CLASS1,	"Class 1" },	{ FC_COS_CLASS2,	"Class 2" },	{ FC_COS_CLASS3,	"Class 3" },	{ FC_COS_CLASS4,	"Class 4" },	{ FC_COS_CLASS6,	"Class 6" },};fc_bitfield_name_search(cos, fc_cos_names)/* Convert FC_PORTSPEED bit values to ascii string name */static const struct {	u32 			value;	char			*name;} fc_port_speed_names[] = {	{ FC_PORTSPEED_1GBIT,		"1 Gbit" },	{ FC_PORTSPEED_2GBIT,		"2 Gbit" },	{ FC_PORTSPEED_4GBIT,		"4 Gbit" },	{ FC_PORTSPEED_10GBIT,		"10 Gbit" },	{ FC_PORTSPEED_8GBIT,		"8 Gbit" },	{ FC_PORTSPEED_16GBIT,		"16 Gbit" },	{ FC_PORTSPEED_NOT_NEGOTIATED,	"Not Negotiated" },};fc_bitfield_name_search(port_speed, fc_port_speed_names)static intshow_fc_fc4s (char *buf, u8 *fc4_list){	int i, len=0;	for (i = 0; i < FC_FC4_LIST_SIZE; i++, fc4_list++)		len += sprintf(buf + len , "0x%02x ", *fc4_list);	len += sprintf(buf + len, "\n");	return len;}/* Convert FC_PORT_ROLE bit values to ascii string name */static const struct {	u32 			value;	char			*name;} fc_port_role_names[] = {	{ FC_PORT_ROLE_FCP_TARGET,	"FCP Target" },	{ FC_PORT_ROLE_FCP_INITIATOR,	"FCP Initiator" },	{ FC_PORT_ROLE_IP_PORT,		"IP Port" },};fc_bitfield_name_search(port_roles, fc_port_role_names)/* * Define roles that are specific to port_id. Values are relative to ROLE_MASK. */#define FC_WELLKNOWN_PORTID_MASK	0xfffff0#define FC_WELLKNOWN_ROLE_MASK  	0x00000f#define FC_FPORT_PORTID			0x00000e#define FC_FABCTLR_PORTID		0x00000d#define FC_DIRSRVR_PORTID		0x00000c#define FC_TIMESRVR_PORTID		0x00000b#define FC_MGMTSRVR_PORTID		0x00000astatic void fc_timeout_deleted_rport(struct work_struct *work);static void fc_timeout_fail_rport_io(struct work_struct *work);static void fc_scsi_scan_rport(struct work_struct *work);/* * Attribute counts pre object type... * Increase these values if you add attributes */#define FC_STARGET_NUM_ATTRS 	3#define FC_RPORT_NUM_ATTRS	10#define FC_VPORT_NUM_ATTRS	9#define FC_HOST_NUM_ATTRS	21struct 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 class_device_attribute private_starget_attrs[							FC_STARGET_NUM_ATTRS];	struct class_device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1];	struct class_device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];	struct class_device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];	struct transport_container rport_attr_cont;	struct class_device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS];	struct class_device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1];	struct transport_container vport_attr_cont;	struct class_device_attribute private_vport_attrs[FC_VPORT_NUM_ATTRS];	struct class_device_attribute *vport_attrs[FC_VPORT_NUM_ATTRS + 1];};#define to_fc_internal(tmpl)	container_of(tmpl, struct fc_internal, t)static int fc_target_setup(struct transport_container *tc, struct device *dev,			   struct class_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);static int fc_host_setup(struct transport_container *tc, struct device *dev,			 struct class_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));	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));	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, KOBJ_NAME_LEN, "fc_wq_%d",		shost->host_no);	fc_host->work_q = create_singlethread_workqueue(					fc_host->work_q_name);	if (!fc_host->work_q)		return -ENOMEM;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?