18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  FiberChannel transport specific attributes exported to sysfs.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Copyright (c) 2003 Silicon Graphics, Inc.  All rights reserved.
68c2ecf20Sopenharmony_ci *  Copyright (C) 2004-2007   James Smart, Emulex Corporation
78c2ecf20Sopenharmony_ci *    Rewrite for host, target, device, and remote port attributes,
88c2ecf20Sopenharmony_ci *    statistics, and service functions...
98c2ecf20Sopenharmony_ci *    Add vports, etc
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/delay.h>
158c2ecf20Sopenharmony_ci#include <linux/kernel.h>
168c2ecf20Sopenharmony_ci#include <linux/bsg-lib.h>
178c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
188c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
198c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h>
208c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_fc.h>
218c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
228c2ecf20Sopenharmony_ci#include <net/netlink.h>
238c2ecf20Sopenharmony_ci#include <scsi/scsi_netlink_fc.h>
248c2ecf20Sopenharmony_ci#include <scsi/scsi_bsg_fc.h>
258c2ecf20Sopenharmony_ci#include "scsi_priv.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_cistatic int fc_queue_work(struct Scsi_Host *, struct work_struct *);
288c2ecf20Sopenharmony_cistatic void fc_vport_sched_delete(struct work_struct *work);
298c2ecf20Sopenharmony_cistatic int fc_vport_setup(struct Scsi_Host *shost, int channel,
308c2ecf20Sopenharmony_ci	struct device *pdev, struct fc_vport_identifiers  *ids,
318c2ecf20Sopenharmony_ci	struct fc_vport **vport);
328c2ecf20Sopenharmony_cistatic int fc_bsg_hostadd(struct Scsi_Host *, struct fc_host_attrs *);
338c2ecf20Sopenharmony_cistatic int fc_bsg_rportadd(struct Scsi_Host *, struct fc_rport *);
348c2ecf20Sopenharmony_cistatic void fc_bsg_remove(struct request_queue *);
358c2ecf20Sopenharmony_cistatic void fc_bsg_goose_queue(struct fc_rport *);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci/*
388c2ecf20Sopenharmony_ci * Module Parameters
398c2ecf20Sopenharmony_ci */
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/*
428c2ecf20Sopenharmony_ci * dev_loss_tmo: the default number of seconds that the FC transport
438c2ecf20Sopenharmony_ci *   should insulate the loss of a remote port.
448c2ecf20Sopenharmony_ci *   The maximum will be capped by the value of SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
458c2ecf20Sopenharmony_ci */
468c2ecf20Sopenharmony_cistatic unsigned int fc_dev_loss_tmo = 60;		/* seconds */
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_cimodule_param_named(dev_loss_tmo, fc_dev_loss_tmo, uint, S_IRUGO|S_IWUSR);
498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dev_loss_tmo,
508c2ecf20Sopenharmony_ci		 "Maximum number of seconds that the FC transport should"
518c2ecf20Sopenharmony_ci		 " insulate the loss of a remote port. Once this value is"
528c2ecf20Sopenharmony_ci		 " exceeded, the scsi target is removed. Value should be"
538c2ecf20Sopenharmony_ci		 " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if"
548c2ecf20Sopenharmony_ci		 " fast_io_fail_tmo is not set.");
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci/*
578c2ecf20Sopenharmony_ci * Redefine so that we can have same named attributes in the
588c2ecf20Sopenharmony_ci * sdev/starget/host objects.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_ci#define FC_DEVICE_ATTR(_prefix,_name,_mode,_show,_store)		\
618c2ecf20Sopenharmony_cistruct device_attribute device_attr_##_prefix##_##_name = 	\
628c2ecf20Sopenharmony_ci	__ATTR(_name,_mode,_show,_store)
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci#define fc_enum_name_search(title, table_type, table)			\
658c2ecf20Sopenharmony_cistatic const char *get_fc_##title##_name(enum table_type table_key)	\
668c2ecf20Sopenharmony_ci{									\
678c2ecf20Sopenharmony_ci	int i;								\
688c2ecf20Sopenharmony_ci	char *name = NULL;						\
698c2ecf20Sopenharmony_ci									\
708c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(table); i++) {			\
718c2ecf20Sopenharmony_ci		if (table[i].value == table_key) {			\
728c2ecf20Sopenharmony_ci			name = table[i].name;				\
738c2ecf20Sopenharmony_ci			break;						\
748c2ecf20Sopenharmony_ci		}							\
758c2ecf20Sopenharmony_ci	}								\
768c2ecf20Sopenharmony_ci	return name;							\
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define fc_enum_name_match(title, table_type, table)			\
808c2ecf20Sopenharmony_cistatic int get_fc_##title##_match(const char *table_key,		\
818c2ecf20Sopenharmony_ci		enum table_type *value)					\
828c2ecf20Sopenharmony_ci{									\
838c2ecf20Sopenharmony_ci	int i;								\
848c2ecf20Sopenharmony_ci									\
858c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(table); i++) {			\
868c2ecf20Sopenharmony_ci		if (strncmp(table_key, table[i].name,			\
878c2ecf20Sopenharmony_ci				table[i].matchlen) == 0) {		\
888c2ecf20Sopenharmony_ci			*value = table[i].value;			\
898c2ecf20Sopenharmony_ci			return 0; /* success */				\
908c2ecf20Sopenharmony_ci		}							\
918c2ecf20Sopenharmony_ci	}								\
928c2ecf20Sopenharmony_ci	return 1; /* failure */						\
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci/* Convert fc_port_type values to ascii string name */
978c2ecf20Sopenharmony_cistatic struct {
988c2ecf20Sopenharmony_ci	enum fc_port_type	value;
998c2ecf20Sopenharmony_ci	char			*name;
1008c2ecf20Sopenharmony_ci} fc_port_type_names[] = {
1018c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_UNKNOWN,		"Unknown" },
1028c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_OTHER,		"Other" },
1038c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_NOTPRESENT,	"Not Present" },
1048c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_NPORT,	"NPort (fabric via point-to-point)" },
1058c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_NLPORT,	"NLPort (fabric via loop)" },
1068c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_LPORT,	"LPort (private loop)" },
1078c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_PTP,	"Point-To-Point (direct nport connection)" },
1088c2ecf20Sopenharmony_ci	{ FC_PORTTYPE_NPIV,		"NPIV VPORT" },
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_cifc_enum_name_search(port_type, fc_port_type, fc_port_type_names)
1118c2ecf20Sopenharmony_ci#define FC_PORTTYPE_MAX_NAMELEN		50
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* Reuse fc_port_type enum function for vport_type */
1148c2ecf20Sopenharmony_ci#define get_fc_vport_type_name get_fc_port_type_name
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci/* Convert fc_host_event_code values to ascii string name */
1188c2ecf20Sopenharmony_cistatic const struct {
1198c2ecf20Sopenharmony_ci	enum fc_host_event_code		value;
1208c2ecf20Sopenharmony_ci	char				*name;
1218c2ecf20Sopenharmony_ci} fc_host_event_code_names[] = {
1228c2ecf20Sopenharmony_ci	{ FCH_EVT_LIP,			"lip" },
1238c2ecf20Sopenharmony_ci	{ FCH_EVT_LINKUP,		"link_up" },
1248c2ecf20Sopenharmony_ci	{ FCH_EVT_LINKDOWN,		"link_down" },
1258c2ecf20Sopenharmony_ci	{ FCH_EVT_LIPRESET,		"lip_reset" },
1268c2ecf20Sopenharmony_ci	{ FCH_EVT_RSCN,			"rscn" },
1278c2ecf20Sopenharmony_ci	{ FCH_EVT_ADAPTER_CHANGE,	"adapter_chg" },
1288c2ecf20Sopenharmony_ci	{ FCH_EVT_PORT_UNKNOWN,		"port_unknown" },
1298c2ecf20Sopenharmony_ci	{ FCH_EVT_PORT_ONLINE,		"port_online" },
1308c2ecf20Sopenharmony_ci	{ FCH_EVT_PORT_OFFLINE,		"port_offline" },
1318c2ecf20Sopenharmony_ci	{ FCH_EVT_PORT_FABRIC,		"port_fabric" },
1328c2ecf20Sopenharmony_ci	{ FCH_EVT_LINK_UNKNOWN,		"link_unknown" },
1338c2ecf20Sopenharmony_ci	{ FCH_EVT_LINK_FPIN,		"link_FPIN" },
1348c2ecf20Sopenharmony_ci	{ FCH_EVT_VENDOR_UNIQUE,	"vendor_unique" },
1358c2ecf20Sopenharmony_ci};
1368c2ecf20Sopenharmony_cifc_enum_name_search(host_event_code, fc_host_event_code,
1378c2ecf20Sopenharmony_ci		fc_host_event_code_names)
1388c2ecf20Sopenharmony_ci#define FC_HOST_EVENT_CODE_MAX_NAMELEN	30
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/* Convert fc_port_state values to ascii string name */
1428c2ecf20Sopenharmony_cistatic struct {
1438c2ecf20Sopenharmony_ci	enum fc_port_state	value;
1448c2ecf20Sopenharmony_ci	char			*name;
1458c2ecf20Sopenharmony_ci} fc_port_state_names[] = {
1468c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_UNKNOWN,		"Unknown" },
1478c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_NOTPRESENT,	"Not Present" },
1488c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_ONLINE,		"Online" },
1498c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_OFFLINE,		"Offline" },
1508c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_BLOCKED,		"Blocked" },
1518c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_BYPASSED,	"Bypassed" },
1528c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_DIAGNOSTICS,	"Diagnostics" },
1538c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_LINKDOWN,	"Linkdown" },
1548c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_ERROR,		"Error" },
1558c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_LOOPBACK,	"Loopback" },
1568c2ecf20Sopenharmony_ci	{ FC_PORTSTATE_DELETED,		"Deleted" },
1578c2ecf20Sopenharmony_ci};
1588c2ecf20Sopenharmony_cifc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
1598c2ecf20Sopenharmony_ci#define FC_PORTSTATE_MAX_NAMELEN	20
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* Convert fc_vport_state values to ascii string name */
1638c2ecf20Sopenharmony_cistatic struct {
1648c2ecf20Sopenharmony_ci	enum fc_vport_state	value;
1658c2ecf20Sopenharmony_ci	char			*name;
1668c2ecf20Sopenharmony_ci} fc_vport_state_names[] = {
1678c2ecf20Sopenharmony_ci	{ FC_VPORT_UNKNOWN,		"Unknown" },
1688c2ecf20Sopenharmony_ci	{ FC_VPORT_ACTIVE,		"Active" },
1698c2ecf20Sopenharmony_ci	{ FC_VPORT_DISABLED,		"Disabled" },
1708c2ecf20Sopenharmony_ci	{ FC_VPORT_LINKDOWN,		"Linkdown" },
1718c2ecf20Sopenharmony_ci	{ FC_VPORT_INITIALIZING,	"Initializing" },
1728c2ecf20Sopenharmony_ci	{ FC_VPORT_NO_FABRIC_SUPP,	"No Fabric Support" },
1738c2ecf20Sopenharmony_ci	{ FC_VPORT_NO_FABRIC_RSCS,	"No Fabric Resources" },
1748c2ecf20Sopenharmony_ci	{ FC_VPORT_FABRIC_LOGOUT,	"Fabric Logout" },
1758c2ecf20Sopenharmony_ci	{ FC_VPORT_FABRIC_REJ_WWN,	"Fabric Rejected WWN" },
1768c2ecf20Sopenharmony_ci	{ FC_VPORT_FAILED,		"VPort Failed" },
1778c2ecf20Sopenharmony_ci};
1788c2ecf20Sopenharmony_cifc_enum_name_search(vport_state, fc_vport_state, fc_vport_state_names)
1798c2ecf20Sopenharmony_ci#define FC_VPORTSTATE_MAX_NAMELEN	24
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci/* Reuse fc_vport_state enum function for vport_last_state */
1828c2ecf20Sopenharmony_ci#define get_fc_vport_last_state_name get_fc_vport_state_name
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci/* Convert fc_tgtid_binding_type values to ascii string name */
1868c2ecf20Sopenharmony_cistatic const struct {
1878c2ecf20Sopenharmony_ci	enum fc_tgtid_binding_type	value;
1888c2ecf20Sopenharmony_ci	char				*name;
1898c2ecf20Sopenharmony_ci	int				matchlen;
1908c2ecf20Sopenharmony_ci} fc_tgtid_binding_type_names[] = {
1918c2ecf20Sopenharmony_ci	{ FC_TGTID_BIND_NONE, "none", 4 },
1928c2ecf20Sopenharmony_ci	{ FC_TGTID_BIND_BY_WWPN, "wwpn (World Wide Port Name)", 4 },
1938c2ecf20Sopenharmony_ci	{ FC_TGTID_BIND_BY_WWNN, "wwnn (World Wide Node Name)", 4 },
1948c2ecf20Sopenharmony_ci	{ FC_TGTID_BIND_BY_ID, "port_id (FC Address)", 7 },
1958c2ecf20Sopenharmony_ci};
1968c2ecf20Sopenharmony_cifc_enum_name_search(tgtid_bind_type, fc_tgtid_binding_type,
1978c2ecf20Sopenharmony_ci		fc_tgtid_binding_type_names)
1988c2ecf20Sopenharmony_cifc_enum_name_match(tgtid_bind_type, fc_tgtid_binding_type,
1998c2ecf20Sopenharmony_ci		fc_tgtid_binding_type_names)
2008c2ecf20Sopenharmony_ci#define FC_BINDTYPE_MAX_NAMELEN	30
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci#define fc_bitfield_name_search(title, table)			\
2048c2ecf20Sopenharmony_cistatic ssize_t							\
2058c2ecf20Sopenharmony_ciget_fc_##title##_names(u32 table_key, char *buf)		\
2068c2ecf20Sopenharmony_ci{								\
2078c2ecf20Sopenharmony_ci	char *prefix = "";					\
2088c2ecf20Sopenharmony_ci	ssize_t len = 0;					\
2098c2ecf20Sopenharmony_ci	int i;							\
2108c2ecf20Sopenharmony_ci								\
2118c2ecf20Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(table); i++) {		\
2128c2ecf20Sopenharmony_ci		if (table[i].value & table_key) {		\
2138c2ecf20Sopenharmony_ci			len += sprintf(buf + len, "%s%s",	\
2148c2ecf20Sopenharmony_ci				prefix, table[i].name);		\
2158c2ecf20Sopenharmony_ci			prefix = ", ";				\
2168c2ecf20Sopenharmony_ci		}						\
2178c2ecf20Sopenharmony_ci	}							\
2188c2ecf20Sopenharmony_ci	len += sprintf(buf + len, "\n");			\
2198c2ecf20Sopenharmony_ci	return len;						\
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci/* Convert FC_COS bit values to ascii string name */
2248c2ecf20Sopenharmony_cistatic const struct {
2258c2ecf20Sopenharmony_ci	u32 			value;
2268c2ecf20Sopenharmony_ci	char			*name;
2278c2ecf20Sopenharmony_ci} fc_cos_names[] = {
2288c2ecf20Sopenharmony_ci	{ FC_COS_CLASS1,	"Class 1" },
2298c2ecf20Sopenharmony_ci	{ FC_COS_CLASS2,	"Class 2" },
2308c2ecf20Sopenharmony_ci	{ FC_COS_CLASS3,	"Class 3" },
2318c2ecf20Sopenharmony_ci	{ FC_COS_CLASS4,	"Class 4" },
2328c2ecf20Sopenharmony_ci	{ FC_COS_CLASS6,	"Class 6" },
2338c2ecf20Sopenharmony_ci};
2348c2ecf20Sopenharmony_cifc_bitfield_name_search(cos, fc_cos_names)
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci/* Convert FC_PORTSPEED bit values to ascii string name */
2388c2ecf20Sopenharmony_cistatic const struct {
2398c2ecf20Sopenharmony_ci	u32 			value;
2408c2ecf20Sopenharmony_ci	char			*name;
2418c2ecf20Sopenharmony_ci} fc_port_speed_names[] = {
2428c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_1GBIT,		"1 Gbit" },
2438c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_2GBIT,		"2 Gbit" },
2448c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_4GBIT,		"4 Gbit" },
2458c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_10GBIT,		"10 Gbit" },
2468c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_8GBIT,		"8 Gbit" },
2478c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_16GBIT,		"16 Gbit" },
2488c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_32GBIT,		"32 Gbit" },
2498c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_20GBIT,		"20 Gbit" },
2508c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_40GBIT,		"40 Gbit" },
2518c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_50GBIT,		"50 Gbit" },
2528c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_100GBIT,		"100 Gbit" },
2538c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_25GBIT,		"25 Gbit" },
2548c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_64GBIT,		"64 Gbit" },
2558c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_128GBIT,		"128 Gbit" },
2568c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_256GBIT,		"256 Gbit" },
2578c2ecf20Sopenharmony_ci	{ FC_PORTSPEED_NOT_NEGOTIATED,	"Not Negotiated" },
2588c2ecf20Sopenharmony_ci};
2598c2ecf20Sopenharmony_cifc_bitfield_name_search(port_speed, fc_port_speed_names)
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cistatic int
2638c2ecf20Sopenharmony_cishow_fc_fc4s (char *buf, u8 *fc4_list)
2648c2ecf20Sopenharmony_ci{
2658c2ecf20Sopenharmony_ci	int i, len=0;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	for (i = 0; i < FC_FC4_LIST_SIZE; i++, fc4_list++)
2688c2ecf20Sopenharmony_ci		len += sprintf(buf + len , "0x%02x ", *fc4_list);
2698c2ecf20Sopenharmony_ci	len += sprintf(buf + len, "\n");
2708c2ecf20Sopenharmony_ci	return len;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci/* Convert FC_PORT_ROLE bit values to ascii string name */
2758c2ecf20Sopenharmony_cistatic const struct {
2768c2ecf20Sopenharmony_ci	u32 			value;
2778c2ecf20Sopenharmony_ci	char			*name;
2788c2ecf20Sopenharmony_ci} fc_port_role_names[] = {
2798c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_FCP_TARGET,		"FCP Target" },
2808c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_FCP_INITIATOR,		"FCP Initiator" },
2818c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_IP_PORT,			"IP Port" },
2828c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_FCP_DUMMY_INITIATOR,	"FCP Dummy Initiator" },
2838c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_NVME_INITIATOR,		"NVMe Initiator" },
2848c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_NVME_TARGET,		"NVMe Target" },
2858c2ecf20Sopenharmony_ci	{ FC_PORT_ROLE_NVME_DISCOVERY,		"NVMe Discovery" },
2868c2ecf20Sopenharmony_ci};
2878c2ecf20Sopenharmony_cifc_bitfield_name_search(port_roles, fc_port_role_names)
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/*
2908c2ecf20Sopenharmony_ci * Define roles that are specific to port_id. Values are relative to ROLE_MASK.
2918c2ecf20Sopenharmony_ci */
2928c2ecf20Sopenharmony_ci#define FC_WELLKNOWN_PORTID_MASK	0xfffff0
2938c2ecf20Sopenharmony_ci#define FC_WELLKNOWN_ROLE_MASK  	0x00000f
2948c2ecf20Sopenharmony_ci#define FC_FPORT_PORTID			0x00000e
2958c2ecf20Sopenharmony_ci#define FC_FABCTLR_PORTID		0x00000d
2968c2ecf20Sopenharmony_ci#define FC_DIRSRVR_PORTID		0x00000c
2978c2ecf20Sopenharmony_ci#define FC_TIMESRVR_PORTID		0x00000b
2988c2ecf20Sopenharmony_ci#define FC_MGMTSRVR_PORTID		0x00000a
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_cistatic void fc_timeout_deleted_rport(struct work_struct *work);
3028c2ecf20Sopenharmony_cistatic void fc_timeout_fail_rport_io(struct work_struct *work);
3038c2ecf20Sopenharmony_cistatic void fc_scsi_scan_rport(struct work_struct *work);
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci/*
3068c2ecf20Sopenharmony_ci * Attribute counts pre object type...
3078c2ecf20Sopenharmony_ci * Increase these values if you add attributes
3088c2ecf20Sopenharmony_ci */
3098c2ecf20Sopenharmony_ci#define FC_STARGET_NUM_ATTRS 	3
3108c2ecf20Sopenharmony_ci#define FC_RPORT_NUM_ATTRS	10
3118c2ecf20Sopenharmony_ci#define FC_VPORT_NUM_ATTRS	9
3128c2ecf20Sopenharmony_ci#define FC_HOST_NUM_ATTRS	29
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistruct fc_internal {
3158c2ecf20Sopenharmony_ci	struct scsi_transport_template t;
3168c2ecf20Sopenharmony_ci	struct fc_function_template *f;
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	/*
3198c2ecf20Sopenharmony_ci	 * For attributes : each object has :
3208c2ecf20Sopenharmony_ci	 *   An array of the actual attributes structures
3218c2ecf20Sopenharmony_ci	 *   An array of null-terminated pointers to the attribute
3228c2ecf20Sopenharmony_ci	 *     structures - used for mid-layer interaction.
3238c2ecf20Sopenharmony_ci	 *
3248c2ecf20Sopenharmony_ci	 * The attribute containers for the starget and host are are
3258c2ecf20Sopenharmony_ci	 * part of the midlayer. As the remote port is specific to the
3268c2ecf20Sopenharmony_ci	 * fc transport, we must provide the attribute container.
3278c2ecf20Sopenharmony_ci	 */
3288c2ecf20Sopenharmony_ci	struct device_attribute private_starget_attrs[
3298c2ecf20Sopenharmony_ci							FC_STARGET_NUM_ATTRS];
3308c2ecf20Sopenharmony_ci	struct device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1];
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	struct device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];
3338c2ecf20Sopenharmony_ci	struct device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	struct transport_container rport_attr_cont;
3368c2ecf20Sopenharmony_ci	struct device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS];
3378c2ecf20Sopenharmony_ci	struct device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1];
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	struct transport_container vport_attr_cont;
3408c2ecf20Sopenharmony_ci	struct device_attribute private_vport_attrs[FC_VPORT_NUM_ATTRS];
3418c2ecf20Sopenharmony_ci	struct device_attribute *vport_attrs[FC_VPORT_NUM_ATTRS + 1];
3428c2ecf20Sopenharmony_ci};
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci#define to_fc_internal(tmpl)	container_of(tmpl, struct fc_internal, t)
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int fc_target_setup(struct transport_container *tc, struct device *dev,
3478c2ecf20Sopenharmony_ci			   struct device *cdev)
3488c2ecf20Sopenharmony_ci{
3498c2ecf20Sopenharmony_ci	struct scsi_target *starget = to_scsi_target(dev);
3508c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(starget);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci	/*
3538c2ecf20Sopenharmony_ci	 * if parent is remote port, use values from remote port.
3548c2ecf20Sopenharmony_ci	 * Otherwise, this host uses the fc_transport, but not the
3558c2ecf20Sopenharmony_ci	 * remote port interface. As such, initialize to known non-values.
3568c2ecf20Sopenharmony_ci	 */
3578c2ecf20Sopenharmony_ci	if (rport) {
3588c2ecf20Sopenharmony_ci		fc_starget_node_name(starget) = rport->node_name;
3598c2ecf20Sopenharmony_ci		fc_starget_port_name(starget) = rport->port_name;
3608c2ecf20Sopenharmony_ci		fc_starget_port_id(starget) = rport->port_id;
3618c2ecf20Sopenharmony_ci	} else {
3628c2ecf20Sopenharmony_ci		fc_starget_node_name(starget) = -1;
3638c2ecf20Sopenharmony_ci		fc_starget_port_name(starget) = -1;
3648c2ecf20Sopenharmony_ci		fc_starget_port_id(starget) = -1;
3658c2ecf20Sopenharmony_ci	}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	return 0;
3688c2ecf20Sopenharmony_ci}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(fc_transport_class,
3718c2ecf20Sopenharmony_ci			       "fc_transport",
3728c2ecf20Sopenharmony_ci			       fc_target_setup,
3738c2ecf20Sopenharmony_ci			       NULL,
3748c2ecf20Sopenharmony_ci			       NULL);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic int fc_host_setup(struct transport_container *tc, struct device *dev,
3778c2ecf20Sopenharmony_ci			 struct device *cdev)
3788c2ecf20Sopenharmony_ci{
3798c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(dev);
3808c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/*
3838c2ecf20Sopenharmony_ci	 * Set default values easily detected by the midlayer as
3848c2ecf20Sopenharmony_ci	 * failure cases.  The scsi lldd is responsible for initializing
3858c2ecf20Sopenharmony_ci	 * all transport attributes to valid values per host.
3868c2ecf20Sopenharmony_ci	 */
3878c2ecf20Sopenharmony_ci	fc_host->node_name = -1;
3888c2ecf20Sopenharmony_ci	fc_host->port_name = -1;
3898c2ecf20Sopenharmony_ci	fc_host->permanent_port_name = -1;
3908c2ecf20Sopenharmony_ci	fc_host->supported_classes = FC_COS_UNSPECIFIED;
3918c2ecf20Sopenharmony_ci	memset(fc_host->supported_fc4s, 0,
3928c2ecf20Sopenharmony_ci		sizeof(fc_host->supported_fc4s));
3938c2ecf20Sopenharmony_ci	fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN;
3948c2ecf20Sopenharmony_ci	fc_host->maxframe_size = -1;
3958c2ecf20Sopenharmony_ci	fc_host->max_npiv_vports = 0;
3968c2ecf20Sopenharmony_ci	memset(fc_host->serial_number, 0,
3978c2ecf20Sopenharmony_ci		sizeof(fc_host->serial_number));
3988c2ecf20Sopenharmony_ci	memset(fc_host->manufacturer, 0,
3998c2ecf20Sopenharmony_ci		sizeof(fc_host->manufacturer));
4008c2ecf20Sopenharmony_ci	memset(fc_host->model, 0,
4018c2ecf20Sopenharmony_ci		sizeof(fc_host->model));
4028c2ecf20Sopenharmony_ci	memset(fc_host->model_description, 0,
4038c2ecf20Sopenharmony_ci		sizeof(fc_host->model_description));
4048c2ecf20Sopenharmony_ci	memset(fc_host->hardware_version, 0,
4058c2ecf20Sopenharmony_ci		sizeof(fc_host->hardware_version));
4068c2ecf20Sopenharmony_ci	memset(fc_host->driver_version, 0,
4078c2ecf20Sopenharmony_ci		sizeof(fc_host->driver_version));
4088c2ecf20Sopenharmony_ci	memset(fc_host->firmware_version, 0,
4098c2ecf20Sopenharmony_ci		sizeof(fc_host->firmware_version));
4108c2ecf20Sopenharmony_ci	memset(fc_host->optionrom_version, 0,
4118c2ecf20Sopenharmony_ci		sizeof(fc_host->optionrom_version));
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	fc_host->port_id = -1;
4148c2ecf20Sopenharmony_ci	fc_host->port_type = FC_PORTTYPE_UNKNOWN;
4158c2ecf20Sopenharmony_ci	fc_host->port_state = FC_PORTSTATE_UNKNOWN;
4168c2ecf20Sopenharmony_ci	memset(fc_host->active_fc4s, 0,
4178c2ecf20Sopenharmony_ci		sizeof(fc_host->active_fc4s));
4188c2ecf20Sopenharmony_ci	fc_host->speed = FC_PORTSPEED_UNKNOWN;
4198c2ecf20Sopenharmony_ci	fc_host->fabric_name = -1;
4208c2ecf20Sopenharmony_ci	memset(fc_host->symbolic_name, 0, sizeof(fc_host->symbolic_name));
4218c2ecf20Sopenharmony_ci	memset(fc_host->system_hostname, 0, sizeof(fc_host->system_hostname));
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	fc_host->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN;
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fc_host->rports);
4268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fc_host->rport_bindings);
4278c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&fc_host->vports);
4288c2ecf20Sopenharmony_ci	fc_host->next_rport_number = 0;
4298c2ecf20Sopenharmony_ci	fc_host->next_target_id = 0;
4308c2ecf20Sopenharmony_ci	fc_host->next_vport_number = 0;
4318c2ecf20Sopenharmony_ci	fc_host->npiv_vports_inuse = 0;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	snprintf(fc_host->work_q_name, sizeof(fc_host->work_q_name),
4348c2ecf20Sopenharmony_ci		 "fc_wq_%d", shost->host_no);
4358c2ecf20Sopenharmony_ci	fc_host->work_q = alloc_workqueue("%s", 0, 0, fc_host->work_q_name);
4368c2ecf20Sopenharmony_ci	if (!fc_host->work_q)
4378c2ecf20Sopenharmony_ci		return -ENOMEM;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	fc_host->dev_loss_tmo = fc_dev_loss_tmo;
4408c2ecf20Sopenharmony_ci	snprintf(fc_host->devloss_work_q_name,
4418c2ecf20Sopenharmony_ci		 sizeof(fc_host->devloss_work_q_name),
4428c2ecf20Sopenharmony_ci		 "fc_dl_%d", shost->host_no);
4438c2ecf20Sopenharmony_ci	fc_host->devloss_work_q = alloc_workqueue("%s", 0, 0,
4448c2ecf20Sopenharmony_ci					fc_host->devloss_work_q_name);
4458c2ecf20Sopenharmony_ci	if (!fc_host->devloss_work_q) {
4468c2ecf20Sopenharmony_ci		destroy_workqueue(fc_host->work_q);
4478c2ecf20Sopenharmony_ci		fc_host->work_q = NULL;
4488c2ecf20Sopenharmony_ci		return -ENOMEM;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	fc_bsg_hostadd(shost, fc_host);
4528c2ecf20Sopenharmony_ci	/* ignore any bsg add error - we just can't do sgio */
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	return 0;
4558c2ecf20Sopenharmony_ci}
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic int fc_host_remove(struct transport_container *tc, struct device *dev,
4588c2ecf20Sopenharmony_ci			 struct device *cdev)
4598c2ecf20Sopenharmony_ci{
4608c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(dev);
4618c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	fc_bsg_remove(fc_host->rqst_q);
4648c2ecf20Sopenharmony_ci	return 0;
4658c2ecf20Sopenharmony_ci}
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(fc_host_class,
4688c2ecf20Sopenharmony_ci			       "fc_host",
4698c2ecf20Sopenharmony_ci			       fc_host_setup,
4708c2ecf20Sopenharmony_ci			       fc_host_remove,
4718c2ecf20Sopenharmony_ci			       NULL);
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci/*
4748c2ecf20Sopenharmony_ci * Setup and Remove actions for remote ports are handled
4758c2ecf20Sopenharmony_ci * in the service functions below.
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(fc_rport_class,
4788c2ecf20Sopenharmony_ci			       "fc_remote_ports",
4798c2ecf20Sopenharmony_ci			       NULL,
4808c2ecf20Sopenharmony_ci			       NULL,
4818c2ecf20Sopenharmony_ci			       NULL);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_ci/*
4848c2ecf20Sopenharmony_ci * Setup and Remove actions for virtual ports are handled
4858c2ecf20Sopenharmony_ci * in the service functions below.
4868c2ecf20Sopenharmony_ci */
4878c2ecf20Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(fc_vport_class,
4888c2ecf20Sopenharmony_ci			       "fc_vports",
4898c2ecf20Sopenharmony_ci			       NULL,
4908c2ecf20Sopenharmony_ci			       NULL,
4918c2ecf20Sopenharmony_ci			       NULL);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci/*
4948c2ecf20Sopenharmony_ci * Netlink Infrastructure
4958c2ecf20Sopenharmony_ci */
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_cistatic atomic_t fc_event_seq;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci/**
5008c2ecf20Sopenharmony_ci * fc_get_event_number - Obtain the next sequential FC event number
5018c2ecf20Sopenharmony_ci *
5028c2ecf20Sopenharmony_ci * Notes:
5038c2ecf20Sopenharmony_ci *   We could have inlined this, but it would have required fc_event_seq to
5048c2ecf20Sopenharmony_ci *   be exposed. For now, live with the subroutine call.
5058c2ecf20Sopenharmony_ci *   Atomic used to avoid lock/unlock...
5068c2ecf20Sopenharmony_ci */
5078c2ecf20Sopenharmony_ciu32
5088c2ecf20Sopenharmony_cifc_get_event_number(void)
5098c2ecf20Sopenharmony_ci{
5108c2ecf20Sopenharmony_ci	return atomic_add_return(1, &fc_event_seq);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_get_event_number);
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci/**
5158c2ecf20Sopenharmony_ci * fc_host_post_fc_event - routine to do the work of posting an event
5168c2ecf20Sopenharmony_ci *                      on an fc_host.
5178c2ecf20Sopenharmony_ci * @shost:		host the event occurred on
5188c2ecf20Sopenharmony_ci * @event_number:	fc event number obtained from get_fc_event_number()
5198c2ecf20Sopenharmony_ci * @event_code:		fc_host event being posted
5208c2ecf20Sopenharmony_ci * @data_len:		amount, in bytes, of event data
5218c2ecf20Sopenharmony_ci * @data_buf:		pointer to event data
5228c2ecf20Sopenharmony_ci * @vendor_id:          value for Vendor id
5238c2ecf20Sopenharmony_ci *
5248c2ecf20Sopenharmony_ci * Notes:
5258c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
5268c2ecf20Sopenharmony_ci */
5278c2ecf20Sopenharmony_civoid
5288c2ecf20Sopenharmony_cifc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number,
5298c2ecf20Sopenharmony_ci		enum fc_host_event_code event_code,
5308c2ecf20Sopenharmony_ci		u32 data_len, char *data_buf, u64 vendor_id)
5318c2ecf20Sopenharmony_ci{
5328c2ecf20Sopenharmony_ci	struct sk_buff *skb;
5338c2ecf20Sopenharmony_ci	struct nlmsghdr	*nlh;
5348c2ecf20Sopenharmony_ci	struct fc_nl_event *event;
5358c2ecf20Sopenharmony_ci	const char *name;
5368c2ecf20Sopenharmony_ci	u32 len;
5378c2ecf20Sopenharmony_ci	int err;
5388c2ecf20Sopenharmony_ci
5398c2ecf20Sopenharmony_ci	if (!data_buf || data_len < 4)
5408c2ecf20Sopenharmony_ci		data_len = 0;
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (!scsi_nl_sock) {
5438c2ecf20Sopenharmony_ci		err = -ENOENT;
5448c2ecf20Sopenharmony_ci		goto send_fail;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	len = FC_NL_MSGALIGN(sizeof(*event) + data_len);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	skb = nlmsg_new(len, GFP_KERNEL);
5508c2ecf20Sopenharmony_ci	if (!skb) {
5518c2ecf20Sopenharmony_ci		err = -ENOBUFS;
5528c2ecf20Sopenharmony_ci		goto send_fail;
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, len, 0);
5568c2ecf20Sopenharmony_ci	if (!nlh) {
5578c2ecf20Sopenharmony_ci		err = -ENOBUFS;
5588c2ecf20Sopenharmony_ci		goto send_fail_skb;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci	event = nlmsg_data(nlh);
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_ci	INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
5638c2ecf20Sopenharmony_ci				FC_NL_ASYNC_EVENT, len);
5648c2ecf20Sopenharmony_ci	event->seconds = ktime_get_real_seconds();
5658c2ecf20Sopenharmony_ci	event->vendor_id = vendor_id;
5668c2ecf20Sopenharmony_ci	event->host_no = shost->host_no;
5678c2ecf20Sopenharmony_ci	event->event_datalen = data_len;	/* bytes */
5688c2ecf20Sopenharmony_ci	event->event_num = event_number;
5698c2ecf20Sopenharmony_ci	event->event_code = event_code;
5708c2ecf20Sopenharmony_ci	if (data_len)
5718c2ecf20Sopenharmony_ci		memcpy(&event->event_data, data_buf, data_len);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
5748c2ecf20Sopenharmony_ci			GFP_KERNEL);
5758c2ecf20Sopenharmony_ci	return;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_cisend_fail_skb:
5788c2ecf20Sopenharmony_ci	kfree_skb(skb);
5798c2ecf20Sopenharmony_cisend_fail:
5808c2ecf20Sopenharmony_ci	name = get_fc_host_event_code_name(event_code);
5818c2ecf20Sopenharmony_ci	printk(KERN_WARNING
5828c2ecf20Sopenharmony_ci		"%s: Dropped Event : host %d %s data 0x%08x - err %d\n",
5838c2ecf20Sopenharmony_ci		__func__, shost->host_no,
5848c2ecf20Sopenharmony_ci		(name) ? name : "<unknown>",
5858c2ecf20Sopenharmony_ci		(data_len) ? *((u32 *)data_buf) : 0xFFFFFFFF, err);
5868c2ecf20Sopenharmony_ci	return;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_host_post_fc_event);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci/**
5918c2ecf20Sopenharmony_ci * fc_host_post_event - called to post an even on an fc_host.
5928c2ecf20Sopenharmony_ci * @shost:		host the event occurred on
5938c2ecf20Sopenharmony_ci * @event_number:	fc event number obtained from get_fc_event_number()
5948c2ecf20Sopenharmony_ci * @event_code:		fc_host event being posted
5958c2ecf20Sopenharmony_ci * @event_data:		32bits of data for the event being posted
5968c2ecf20Sopenharmony_ci *
5978c2ecf20Sopenharmony_ci * Notes:
5988c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
5998c2ecf20Sopenharmony_ci */
6008c2ecf20Sopenharmony_civoid
6018c2ecf20Sopenharmony_cifc_host_post_event(struct Scsi_Host *shost, u32 event_number,
6028c2ecf20Sopenharmony_ci		enum fc_host_event_code event_code, u32 event_data)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	fc_host_post_fc_event(shost, event_number, event_code,
6058c2ecf20Sopenharmony_ci		(u32)sizeof(u32), (char *)&event_data, 0);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_host_post_event);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci/**
6118c2ecf20Sopenharmony_ci * fc_host_post_vendor_event - called to post a vendor unique event
6128c2ecf20Sopenharmony_ci *                      on an fc_host
6138c2ecf20Sopenharmony_ci * @shost:		host the event occurred on
6148c2ecf20Sopenharmony_ci * @event_number:	fc event number obtained from get_fc_event_number()
6158c2ecf20Sopenharmony_ci * @data_len:		amount, in bytes, of vendor unique data
6168c2ecf20Sopenharmony_ci * @data_buf:		pointer to vendor unique data
6178c2ecf20Sopenharmony_ci * @vendor_id:          Vendor id
6188c2ecf20Sopenharmony_ci *
6198c2ecf20Sopenharmony_ci * Notes:
6208c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
6218c2ecf20Sopenharmony_ci */
6228c2ecf20Sopenharmony_civoid
6238c2ecf20Sopenharmony_cifc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
6248c2ecf20Sopenharmony_ci		u32 data_len, char * data_buf, u64 vendor_id)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	fc_host_post_fc_event(shost, event_number, FCH_EVT_VENDOR_UNIQUE,
6278c2ecf20Sopenharmony_ci		data_len, data_buf, vendor_id);
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_host_post_vendor_event);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci/**
6328c2ecf20Sopenharmony_ci * fc_host_rcv_fpin - routine to process a received FPIN.
6338c2ecf20Sopenharmony_ci * @shost:		host the FPIN was received on
6348c2ecf20Sopenharmony_ci * @fpin_len:		length of FPIN payload, in bytes
6358c2ecf20Sopenharmony_ci * @fpin_buf:		pointer to FPIN payload
6368c2ecf20Sopenharmony_ci *
6378c2ecf20Sopenharmony_ci * Notes:
6388c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
6398c2ecf20Sopenharmony_ci */
6408c2ecf20Sopenharmony_civoid
6418c2ecf20Sopenharmony_cifc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf)
6428c2ecf20Sopenharmony_ci{
6438c2ecf20Sopenharmony_ci	fc_host_post_fc_event(shost, fc_get_event_number(),
6448c2ecf20Sopenharmony_ci				FCH_EVT_LINK_FPIN, fpin_len, fpin_buf, 0);
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_host_fpin_rcv);
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci
6498c2ecf20Sopenharmony_cistatic __init int fc_transport_init(void)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	int error;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	atomic_set(&fc_event_seq, 0);
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	error = transport_class_register(&fc_host_class);
6568c2ecf20Sopenharmony_ci	if (error)
6578c2ecf20Sopenharmony_ci		return error;
6588c2ecf20Sopenharmony_ci	error = transport_class_register(&fc_vport_class);
6598c2ecf20Sopenharmony_ci	if (error)
6608c2ecf20Sopenharmony_ci		goto unreg_host_class;
6618c2ecf20Sopenharmony_ci	error = transport_class_register(&fc_rport_class);
6628c2ecf20Sopenharmony_ci	if (error)
6638c2ecf20Sopenharmony_ci		goto unreg_vport_class;
6648c2ecf20Sopenharmony_ci	error = transport_class_register(&fc_transport_class);
6658c2ecf20Sopenharmony_ci	if (error)
6668c2ecf20Sopenharmony_ci		goto unreg_rport_class;
6678c2ecf20Sopenharmony_ci	return 0;
6688c2ecf20Sopenharmony_ci
6698c2ecf20Sopenharmony_ciunreg_rport_class:
6708c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_rport_class);
6718c2ecf20Sopenharmony_ciunreg_vport_class:
6728c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_vport_class);
6738c2ecf20Sopenharmony_ciunreg_host_class:
6748c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_host_class);
6758c2ecf20Sopenharmony_ci	return error;
6768c2ecf20Sopenharmony_ci}
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_cistatic void __exit fc_transport_exit(void)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_transport_class);
6818c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_rport_class);
6828c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_host_class);
6838c2ecf20Sopenharmony_ci	transport_class_unregister(&fc_vport_class);
6848c2ecf20Sopenharmony_ci}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci/*
6878c2ecf20Sopenharmony_ci * FC Remote Port Attribute Management
6888c2ecf20Sopenharmony_ci */
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci#define fc_rport_show_function(field, format_string, sz, cast)		\
6918c2ecf20Sopenharmony_cistatic ssize_t								\
6928c2ecf20Sopenharmony_cishow_fc_rport_##field (struct device *dev, 				\
6938c2ecf20Sopenharmony_ci		       struct device_attribute *attr, char *buf)	\
6948c2ecf20Sopenharmony_ci{									\
6958c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);		\
6968c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);		\
6978c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
6988c2ecf20Sopenharmony_ci	if ((i->f->get_rport_##field) &&				\
6998c2ecf20Sopenharmony_ci	    !((rport->port_state == FC_PORTSTATE_BLOCKED) ||		\
7008c2ecf20Sopenharmony_ci	      (rport->port_state == FC_PORTSTATE_DELETED) ||		\
7018c2ecf20Sopenharmony_ci	      (rport->port_state == FC_PORTSTATE_NOTPRESENT)))		\
7028c2ecf20Sopenharmony_ci		i->f->get_rport_##field(rport);				\
7038c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, cast rport->field); 	\
7048c2ecf20Sopenharmony_ci}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci#define fc_rport_store_function(field)					\
7078c2ecf20Sopenharmony_cistatic ssize_t								\
7088c2ecf20Sopenharmony_cistore_fc_rport_##field(struct device *dev,				\
7098c2ecf20Sopenharmony_ci		       struct device_attribute *attr,			\
7108c2ecf20Sopenharmony_ci		       const char *buf,	size_t count)			\
7118c2ecf20Sopenharmony_ci{									\
7128c2ecf20Sopenharmony_ci	int val;							\
7138c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);		\
7148c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);		\
7158c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
7168c2ecf20Sopenharmony_ci	char *cp;							\
7178c2ecf20Sopenharmony_ci	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||		\
7188c2ecf20Sopenharmony_ci	    (rport->port_state == FC_PORTSTATE_DELETED) ||		\
7198c2ecf20Sopenharmony_ci	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))		\
7208c2ecf20Sopenharmony_ci		return -EBUSY;						\
7218c2ecf20Sopenharmony_ci	val = simple_strtoul(buf, &cp, 0);				\
7228c2ecf20Sopenharmony_ci	if (*cp && (*cp != '\n'))					\
7238c2ecf20Sopenharmony_ci		return -EINVAL;						\
7248c2ecf20Sopenharmony_ci	i->f->set_rport_##field(rport, val);				\
7258c2ecf20Sopenharmony_ci	return count;							\
7268c2ecf20Sopenharmony_ci}
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci#define fc_rport_rd_attr(field, format_string, sz)			\
7298c2ecf20Sopenharmony_ci	fc_rport_show_function(field, format_string, sz, )		\
7308c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, field, S_IRUGO,			\
7318c2ecf20Sopenharmony_ci			 show_fc_rport_##field, NULL)
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci#define fc_rport_rd_attr_cast(field, format_string, sz, cast)		\
7348c2ecf20Sopenharmony_ci	fc_rport_show_function(field, format_string, sz, (cast))	\
7358c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, field, S_IRUGO,			\
7368c2ecf20Sopenharmony_ci			  show_fc_rport_##field, NULL)
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci#define fc_rport_rw_attr(field, format_string, sz)			\
7398c2ecf20Sopenharmony_ci	fc_rport_show_function(field, format_string, sz, )		\
7408c2ecf20Sopenharmony_ci	fc_rport_store_function(field)					\
7418c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, field, S_IRUGO | S_IWUSR,		\
7428c2ecf20Sopenharmony_ci			show_fc_rport_##field,				\
7438c2ecf20Sopenharmony_ci			store_fc_rport_##field)
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci#define fc_private_rport_show_function(field, format_string, sz, cast)	\
7478c2ecf20Sopenharmony_cistatic ssize_t								\
7488c2ecf20Sopenharmony_cishow_fc_rport_##field (struct device *dev, 				\
7498c2ecf20Sopenharmony_ci		       struct device_attribute *attr, char *buf)	\
7508c2ecf20Sopenharmony_ci{									\
7518c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);		\
7528c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, cast rport->field); 	\
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci#define fc_private_rport_rd_attr(field, format_string, sz)		\
7568c2ecf20Sopenharmony_ci	fc_private_rport_show_function(field, format_string, sz, )	\
7578c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, field, S_IRUGO,			\
7588c2ecf20Sopenharmony_ci			 show_fc_rport_##field, NULL)
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci#define fc_private_rport_rd_attr_cast(field, format_string, sz, cast)	\
7618c2ecf20Sopenharmony_ci	fc_private_rport_show_function(field, format_string, sz, (cast)) \
7628c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, field, S_IRUGO,			\
7638c2ecf20Sopenharmony_ci			  show_fc_rport_##field, NULL)
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci#define fc_private_rport_rd_enum_attr(title, maxlen)			\
7678c2ecf20Sopenharmony_cistatic ssize_t								\
7688c2ecf20Sopenharmony_cishow_fc_rport_##title (struct device *dev,				\
7698c2ecf20Sopenharmony_ci		       struct device_attribute *attr, char *buf)	\
7708c2ecf20Sopenharmony_ci{									\
7718c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);		\
7728c2ecf20Sopenharmony_ci	const char *name;						\
7738c2ecf20Sopenharmony_ci	name = get_fc_##title##_name(rport->title);			\
7748c2ecf20Sopenharmony_ci	if (!name)							\
7758c2ecf20Sopenharmony_ci		return -EINVAL;						\
7768c2ecf20Sopenharmony_ci	return snprintf(buf, maxlen, "%s\n", name);			\
7778c2ecf20Sopenharmony_ci}									\
7788c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, title, S_IRUGO,			\
7798c2ecf20Sopenharmony_ci			show_fc_rport_##title, NULL)
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci#define SETUP_RPORT_ATTRIBUTE_RD(field)					\
7838c2ecf20Sopenharmony_ci	i->private_rport_attrs[count] = device_attr_rport_##field; \
7848c2ecf20Sopenharmony_ci	i->private_rport_attrs[count].attr.mode = S_IRUGO;		\
7858c2ecf20Sopenharmony_ci	i->private_rport_attrs[count].store = NULL;			\
7868c2ecf20Sopenharmony_ci	i->rport_attrs[count] = &i->private_rport_attrs[count];		\
7878c2ecf20Sopenharmony_ci	if (i->f->show_rport_##field)					\
7888c2ecf20Sopenharmony_ci		count++
7898c2ecf20Sopenharmony_ci
7908c2ecf20Sopenharmony_ci#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(field)				\
7918c2ecf20Sopenharmony_ci	i->private_rport_attrs[count] = device_attr_rport_##field; \
7928c2ecf20Sopenharmony_ci	i->private_rport_attrs[count].attr.mode = S_IRUGO;		\
7938c2ecf20Sopenharmony_ci	i->private_rport_attrs[count].store = NULL;			\
7948c2ecf20Sopenharmony_ci	i->rport_attrs[count] = &i->private_rport_attrs[count];		\
7958c2ecf20Sopenharmony_ci	count++
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci#define SETUP_RPORT_ATTRIBUTE_RW(field)					\
7988c2ecf20Sopenharmony_ci	i->private_rport_attrs[count] = device_attr_rport_##field; \
7998c2ecf20Sopenharmony_ci	if (!i->f->set_rport_##field) {					\
8008c2ecf20Sopenharmony_ci		i->private_rport_attrs[count].attr.mode = S_IRUGO;	\
8018c2ecf20Sopenharmony_ci		i->private_rport_attrs[count].store = NULL;		\
8028c2ecf20Sopenharmony_ci	}								\
8038c2ecf20Sopenharmony_ci	i->rport_attrs[count] = &i->private_rport_attrs[count];		\
8048c2ecf20Sopenharmony_ci	if (i->f->show_rport_##field)					\
8058c2ecf20Sopenharmony_ci		count++
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(field)				\
8088c2ecf20Sopenharmony_ci{									\
8098c2ecf20Sopenharmony_ci	i->private_rport_attrs[count] = device_attr_rport_##field; \
8108c2ecf20Sopenharmony_ci	i->rport_attrs[count] = &i->private_rport_attrs[count];		\
8118c2ecf20Sopenharmony_ci	count++;							\
8128c2ecf20Sopenharmony_ci}
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci/* The FC Transport Remote Port Attributes: */
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/* Fixed Remote Port Attributes */
8188c2ecf20Sopenharmony_ci
8198c2ecf20Sopenharmony_cifc_private_rport_rd_attr(maxframe_size, "%u bytes\n", 20);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic ssize_t
8228c2ecf20Sopenharmony_cishow_fc_rport_supported_classes (struct device *dev,
8238c2ecf20Sopenharmony_ci				 struct device_attribute *attr, char *buf)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);
8268c2ecf20Sopenharmony_ci	if (rport->supported_classes == FC_COS_UNSPECIFIED)
8278c2ecf20Sopenharmony_ci		return snprintf(buf, 20, "unspecified\n");
8288c2ecf20Sopenharmony_ci	return get_fc_cos_names(rport->supported_classes, buf);
8298c2ecf20Sopenharmony_ci}
8308c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, supported_classes, S_IRUGO,
8318c2ecf20Sopenharmony_ci		show_fc_rport_supported_classes, NULL);
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci/* Dynamic Remote Port Attributes */
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci/*
8368c2ecf20Sopenharmony_ci * dev_loss_tmo attribute
8378c2ecf20Sopenharmony_ci */
8388c2ecf20Sopenharmony_cistatic int fc_str_to_dev_loss(const char *buf, unsigned long *val)
8398c2ecf20Sopenharmony_ci{
8408c2ecf20Sopenharmony_ci	char *cp;
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_ci	*val = simple_strtoul(buf, &cp, 0);
8438c2ecf20Sopenharmony_ci	if (*cp && (*cp != '\n'))
8448c2ecf20Sopenharmony_ci		return -EINVAL;
8458c2ecf20Sopenharmony_ci	/*
8468c2ecf20Sopenharmony_ci	 * Check for overflow; dev_loss_tmo is u32
8478c2ecf20Sopenharmony_ci	 */
8488c2ecf20Sopenharmony_ci	if (*val > UINT_MAX)
8498c2ecf20Sopenharmony_ci		return -EINVAL;
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	return 0;
8528c2ecf20Sopenharmony_ci}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_cistatic int fc_rport_set_dev_loss_tmo(struct fc_rport *rport,
8558c2ecf20Sopenharmony_ci				     unsigned long val)
8568c2ecf20Sopenharmony_ci{
8578c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
8588c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
8618c2ecf20Sopenharmony_ci	    (rport->port_state == FC_PORTSTATE_DELETED) ||
8628c2ecf20Sopenharmony_ci	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))
8638c2ecf20Sopenharmony_ci		return -EBUSY;
8648c2ecf20Sopenharmony_ci	/*
8658c2ecf20Sopenharmony_ci	 * Check for overflow; dev_loss_tmo is u32
8668c2ecf20Sopenharmony_ci	 */
8678c2ecf20Sopenharmony_ci	if (val > UINT_MAX)
8688c2ecf20Sopenharmony_ci		return -EINVAL;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	/*
8718c2ecf20Sopenharmony_ci	 * If fast_io_fail is off we have to cap
8728c2ecf20Sopenharmony_ci	 * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT
8738c2ecf20Sopenharmony_ci	 */
8748c2ecf20Sopenharmony_ci	if (rport->fast_io_fail_tmo == -1 &&
8758c2ecf20Sopenharmony_ci	    val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
8768c2ecf20Sopenharmony_ci		return -EINVAL;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	i->f->set_rport_dev_loss_tmo(rport, val);
8798c2ecf20Sopenharmony_ci	return 0;
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_cifc_rport_show_function(dev_loss_tmo, "%d\n", 20, )
8838c2ecf20Sopenharmony_cistatic ssize_t
8848c2ecf20Sopenharmony_cistore_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
8858c2ecf20Sopenharmony_ci			    const char *buf, size_t count)
8868c2ecf20Sopenharmony_ci{
8878c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);
8888c2ecf20Sopenharmony_ci	unsigned long val;
8898c2ecf20Sopenharmony_ci	int rc;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	rc = fc_str_to_dev_loss(buf, &val);
8928c2ecf20Sopenharmony_ci	if (rc)
8938c2ecf20Sopenharmony_ci		return rc;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	rc = fc_rport_set_dev_loss_tmo(rport, val);
8968c2ecf20Sopenharmony_ci	if (rc)
8978c2ecf20Sopenharmony_ci		return rc;
8988c2ecf20Sopenharmony_ci	return count;
8998c2ecf20Sopenharmony_ci}
9008c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, dev_loss_tmo, S_IRUGO | S_IWUSR,
9018c2ecf20Sopenharmony_ci		show_fc_rport_dev_loss_tmo, store_fc_rport_dev_loss_tmo);
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci/* Private Remote Port Attributes */
9058c2ecf20Sopenharmony_ci
9068c2ecf20Sopenharmony_cifc_private_rport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
9078c2ecf20Sopenharmony_cifc_private_rport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
9088c2ecf20Sopenharmony_cifc_private_rport_rd_attr(port_id, "0x%06x\n", 20);
9098c2ecf20Sopenharmony_ci
9108c2ecf20Sopenharmony_cistatic ssize_t
9118c2ecf20Sopenharmony_cishow_fc_rport_roles (struct device *dev, struct device_attribute *attr,
9128c2ecf20Sopenharmony_ci		     char *buf)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	/* identify any roles that are port_id specific */
9178c2ecf20Sopenharmony_ci	if ((rport->port_id != -1) &&
9188c2ecf20Sopenharmony_ci	    (rport->port_id & FC_WELLKNOWN_PORTID_MASK) ==
9198c2ecf20Sopenharmony_ci					FC_WELLKNOWN_PORTID_MASK) {
9208c2ecf20Sopenharmony_ci		switch (rport->port_id & FC_WELLKNOWN_ROLE_MASK) {
9218c2ecf20Sopenharmony_ci		case FC_FPORT_PORTID:
9228c2ecf20Sopenharmony_ci			return snprintf(buf, 30, "Fabric Port\n");
9238c2ecf20Sopenharmony_ci		case FC_FABCTLR_PORTID:
9248c2ecf20Sopenharmony_ci			return snprintf(buf, 30, "Fabric Controller\n");
9258c2ecf20Sopenharmony_ci		case FC_DIRSRVR_PORTID:
9268c2ecf20Sopenharmony_ci			return snprintf(buf, 30, "Directory Server\n");
9278c2ecf20Sopenharmony_ci		case FC_TIMESRVR_PORTID:
9288c2ecf20Sopenharmony_ci			return snprintf(buf, 30, "Time Server\n");
9298c2ecf20Sopenharmony_ci		case FC_MGMTSRVR_PORTID:
9308c2ecf20Sopenharmony_ci			return snprintf(buf, 30, "Management Server\n");
9318c2ecf20Sopenharmony_ci		default:
9328c2ecf20Sopenharmony_ci			return snprintf(buf, 30, "Unknown Fabric Entity\n");
9338c2ecf20Sopenharmony_ci		}
9348c2ecf20Sopenharmony_ci	} else {
9358c2ecf20Sopenharmony_ci		if (rport->roles == FC_PORT_ROLE_UNKNOWN)
9368c2ecf20Sopenharmony_ci			return snprintf(buf, 20, "unknown\n");
9378c2ecf20Sopenharmony_ci		return get_fc_port_roles_names(rport->roles, buf);
9388c2ecf20Sopenharmony_ci	}
9398c2ecf20Sopenharmony_ci}
9408c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, roles, S_IRUGO,
9418c2ecf20Sopenharmony_ci		show_fc_rport_roles, NULL);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_cifc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
9448c2ecf20Sopenharmony_cifc_private_rport_rd_attr(scsi_target_id, "%d\n", 20);
9458c2ecf20Sopenharmony_ci
9468c2ecf20Sopenharmony_ci/*
9478c2ecf20Sopenharmony_ci * fast_io_fail_tmo attribute
9488c2ecf20Sopenharmony_ci */
9498c2ecf20Sopenharmony_cistatic ssize_t
9508c2ecf20Sopenharmony_cishow_fc_rport_fast_io_fail_tmo (struct device *dev,
9518c2ecf20Sopenharmony_ci				struct device_attribute *attr, char *buf)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);
9548c2ecf20Sopenharmony_ci
9558c2ecf20Sopenharmony_ci	if (rport->fast_io_fail_tmo == -1)
9568c2ecf20Sopenharmony_ci		return snprintf(buf, 5, "off\n");
9578c2ecf20Sopenharmony_ci	return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo);
9588c2ecf20Sopenharmony_ci}
9598c2ecf20Sopenharmony_ci
9608c2ecf20Sopenharmony_cistatic ssize_t
9618c2ecf20Sopenharmony_cistore_fc_rport_fast_io_fail_tmo(struct device *dev,
9628c2ecf20Sopenharmony_ci				struct device_attribute *attr, const char *buf,
9638c2ecf20Sopenharmony_ci				size_t count)
9648c2ecf20Sopenharmony_ci{
9658c2ecf20Sopenharmony_ci	int val;
9668c2ecf20Sopenharmony_ci	char *cp;
9678c2ecf20Sopenharmony_ci	struct fc_rport *rport = transport_class_to_rport(dev);
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
9708c2ecf20Sopenharmony_ci	    (rport->port_state == FC_PORTSTATE_DELETED) ||
9718c2ecf20Sopenharmony_ci	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))
9728c2ecf20Sopenharmony_ci		return -EBUSY;
9738c2ecf20Sopenharmony_ci	if (strncmp(buf, "off", 3) == 0)
9748c2ecf20Sopenharmony_ci		rport->fast_io_fail_tmo = -1;
9758c2ecf20Sopenharmony_ci	else {
9768c2ecf20Sopenharmony_ci		val = simple_strtoul(buf, &cp, 0);
9778c2ecf20Sopenharmony_ci		if ((*cp && (*cp != '\n')) || (val < 0))
9788c2ecf20Sopenharmony_ci			return -EINVAL;
9798c2ecf20Sopenharmony_ci		/*
9808c2ecf20Sopenharmony_ci		 * Cap fast_io_fail by dev_loss_tmo or
9818c2ecf20Sopenharmony_ci		 * SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
9828c2ecf20Sopenharmony_ci		 */
9838c2ecf20Sopenharmony_ci		if ((val >= rport->dev_loss_tmo) ||
9848c2ecf20Sopenharmony_ci		    (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
9858c2ecf20Sopenharmony_ci			return -EINVAL;
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci		rport->fast_io_fail_tmo = val;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci	return count;
9908c2ecf20Sopenharmony_ci}
9918c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR,
9928c2ecf20Sopenharmony_ci	show_fc_rport_fast_io_fail_tmo, store_fc_rport_fast_io_fail_tmo);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci
9958c2ecf20Sopenharmony_ci/*
9968c2ecf20Sopenharmony_ci * FC SCSI Target Attribute Management
9978c2ecf20Sopenharmony_ci */
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci/*
10008c2ecf20Sopenharmony_ci * Note: in the target show function we recognize when the remote
10018c2ecf20Sopenharmony_ci *  port is in the hierarchy and do not allow the driver to get
10028c2ecf20Sopenharmony_ci *  involved in sysfs functions. The driver only gets involved if
10038c2ecf20Sopenharmony_ci *  it's the "old" style that doesn't use rports.
10048c2ecf20Sopenharmony_ci */
10058c2ecf20Sopenharmony_ci#define fc_starget_show_function(field, format_string, sz, cast)	\
10068c2ecf20Sopenharmony_cistatic ssize_t								\
10078c2ecf20Sopenharmony_cishow_fc_starget_##field (struct device *dev, 				\
10088c2ecf20Sopenharmony_ci			 struct device_attribute *attr, char *buf)	\
10098c2ecf20Sopenharmony_ci{									\
10108c2ecf20Sopenharmony_ci	struct scsi_target *starget = transport_class_to_starget(dev);	\
10118c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);	\
10128c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
10138c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(starget);		\
10148c2ecf20Sopenharmony_ci	if (rport)							\
10158c2ecf20Sopenharmony_ci		fc_starget_##field(starget) = rport->field;		\
10168c2ecf20Sopenharmony_ci	else if (i->f->get_starget_##field)				\
10178c2ecf20Sopenharmony_ci		i->f->get_starget_##field(starget);			\
10188c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, 			\
10198c2ecf20Sopenharmony_ci		cast fc_starget_##field(starget)); 			\
10208c2ecf20Sopenharmony_ci}
10218c2ecf20Sopenharmony_ci
10228c2ecf20Sopenharmony_ci#define fc_starget_rd_attr(field, format_string, sz)			\
10238c2ecf20Sopenharmony_ci	fc_starget_show_function(field, format_string, sz, )		\
10248c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(starget, field, S_IRUGO,			\
10258c2ecf20Sopenharmony_ci			 show_fc_starget_##field, NULL)
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci#define fc_starget_rd_attr_cast(field, format_string, sz, cast)		\
10288c2ecf20Sopenharmony_ci	fc_starget_show_function(field, format_string, sz, (cast))	\
10298c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(starget, field, S_IRUGO,			\
10308c2ecf20Sopenharmony_ci			  show_fc_starget_##field, NULL)
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci#define SETUP_STARGET_ATTRIBUTE_RD(field)				\
10338c2ecf20Sopenharmony_ci	i->private_starget_attrs[count] = device_attr_starget_##field; \
10348c2ecf20Sopenharmony_ci	i->private_starget_attrs[count].attr.mode = S_IRUGO;		\
10358c2ecf20Sopenharmony_ci	i->private_starget_attrs[count].store = NULL;			\
10368c2ecf20Sopenharmony_ci	i->starget_attrs[count] = &i->private_starget_attrs[count];	\
10378c2ecf20Sopenharmony_ci	if (i->f->show_starget_##field)					\
10388c2ecf20Sopenharmony_ci		count++
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci#define SETUP_STARGET_ATTRIBUTE_RW(field)				\
10418c2ecf20Sopenharmony_ci	i->private_starget_attrs[count] = device_attr_starget_##field; \
10428c2ecf20Sopenharmony_ci	if (!i->f->set_starget_##field) {				\
10438c2ecf20Sopenharmony_ci		i->private_starget_attrs[count].attr.mode = S_IRUGO;	\
10448c2ecf20Sopenharmony_ci		i->private_starget_attrs[count].store = NULL;		\
10458c2ecf20Sopenharmony_ci	}								\
10468c2ecf20Sopenharmony_ci	i->starget_attrs[count] = &i->private_starget_attrs[count];	\
10478c2ecf20Sopenharmony_ci	if (i->f->show_starget_##field)					\
10488c2ecf20Sopenharmony_ci		count++
10498c2ecf20Sopenharmony_ci
10508c2ecf20Sopenharmony_ci/* The FC Transport SCSI Target Attributes: */
10518c2ecf20Sopenharmony_cifc_starget_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
10528c2ecf20Sopenharmony_cifc_starget_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
10538c2ecf20Sopenharmony_cifc_starget_rd_attr(port_id, "0x%06x\n", 20);
10548c2ecf20Sopenharmony_ci
10558c2ecf20Sopenharmony_ci
10568c2ecf20Sopenharmony_ci/*
10578c2ecf20Sopenharmony_ci * FC Virtual Port Attribute Management
10588c2ecf20Sopenharmony_ci */
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci#define fc_vport_show_function(field, format_string, sz, cast)		\
10618c2ecf20Sopenharmony_cistatic ssize_t								\
10628c2ecf20Sopenharmony_cishow_fc_vport_##field (struct device *dev, 				\
10638c2ecf20Sopenharmony_ci		       struct device_attribute *attr, char *buf)	\
10648c2ecf20Sopenharmony_ci{									\
10658c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);		\
10668c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);		\
10678c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
10688c2ecf20Sopenharmony_ci	if ((i->f->get_vport_##field) &&				\
10698c2ecf20Sopenharmony_ci	    !(vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)))	\
10708c2ecf20Sopenharmony_ci		i->f->get_vport_##field(vport);				\
10718c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, cast vport->field); 	\
10728c2ecf20Sopenharmony_ci}
10738c2ecf20Sopenharmony_ci
10748c2ecf20Sopenharmony_ci#define fc_vport_store_function(field)					\
10758c2ecf20Sopenharmony_cistatic ssize_t								\
10768c2ecf20Sopenharmony_cistore_fc_vport_##field(struct device *dev,				\
10778c2ecf20Sopenharmony_ci		       struct device_attribute *attr,			\
10788c2ecf20Sopenharmony_ci		       const char *buf,	size_t count)			\
10798c2ecf20Sopenharmony_ci{									\
10808c2ecf20Sopenharmony_ci	int val;							\
10818c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);		\
10828c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);		\
10838c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
10848c2ecf20Sopenharmony_ci	char *cp;							\
10858c2ecf20Sopenharmony_ci	if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))	\
10868c2ecf20Sopenharmony_ci		return -EBUSY;						\
10878c2ecf20Sopenharmony_ci	val = simple_strtoul(buf, &cp, 0);				\
10888c2ecf20Sopenharmony_ci	if (*cp && (*cp != '\n'))					\
10898c2ecf20Sopenharmony_ci		return -EINVAL;						\
10908c2ecf20Sopenharmony_ci	i->f->set_vport_##field(vport, val);				\
10918c2ecf20Sopenharmony_ci	return count;							\
10928c2ecf20Sopenharmony_ci}
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci#define fc_vport_store_str_function(field, slen)			\
10958c2ecf20Sopenharmony_cistatic ssize_t								\
10968c2ecf20Sopenharmony_cistore_fc_vport_##field(struct device *dev,				\
10978c2ecf20Sopenharmony_ci		       struct device_attribute *attr, 			\
10988c2ecf20Sopenharmony_ci		       const char *buf,	size_t count)			\
10998c2ecf20Sopenharmony_ci{									\
11008c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);		\
11018c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);		\
11028c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
11038c2ecf20Sopenharmony_ci	unsigned int cnt=count;						\
11048c2ecf20Sopenharmony_ci									\
11058c2ecf20Sopenharmony_ci	/* count may include a LF at end of string */			\
11068c2ecf20Sopenharmony_ci	if (buf[cnt-1] == '\n')						\
11078c2ecf20Sopenharmony_ci		cnt--;							\
11088c2ecf20Sopenharmony_ci	if (cnt > ((slen) - 1))						\
11098c2ecf20Sopenharmony_ci		return -EINVAL;						\
11108c2ecf20Sopenharmony_ci	memcpy(vport->field, buf, cnt);					\
11118c2ecf20Sopenharmony_ci	i->f->set_vport_##field(vport);					\
11128c2ecf20Sopenharmony_ci	return count;							\
11138c2ecf20Sopenharmony_ci}
11148c2ecf20Sopenharmony_ci
11158c2ecf20Sopenharmony_ci#define fc_vport_rd_attr(field, format_string, sz)			\
11168c2ecf20Sopenharmony_ci	fc_vport_show_function(field, format_string, sz, )		\
11178c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, field, S_IRUGO,			\
11188c2ecf20Sopenharmony_ci			 show_fc_vport_##field, NULL)
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci#define fc_vport_rd_attr_cast(field, format_string, sz, cast)		\
11218c2ecf20Sopenharmony_ci	fc_vport_show_function(field, format_string, sz, (cast))	\
11228c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, field, S_IRUGO,			\
11238c2ecf20Sopenharmony_ci			  show_fc_vport_##field, NULL)
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci#define fc_vport_rw_attr(field, format_string, sz)			\
11268c2ecf20Sopenharmony_ci	fc_vport_show_function(field, format_string, sz, )		\
11278c2ecf20Sopenharmony_ci	fc_vport_store_function(field)					\
11288c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, field, S_IRUGO | S_IWUSR,		\
11298c2ecf20Sopenharmony_ci			show_fc_vport_##field,				\
11308c2ecf20Sopenharmony_ci			store_fc_vport_##field)
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_ci#define fc_private_vport_show_function(field, format_string, sz, cast)	\
11338c2ecf20Sopenharmony_cistatic ssize_t								\
11348c2ecf20Sopenharmony_cishow_fc_vport_##field (struct device *dev,				\
11358c2ecf20Sopenharmony_ci		       struct device_attribute *attr, char *buf)	\
11368c2ecf20Sopenharmony_ci{									\
11378c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);		\
11388c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, cast vport->field); 	\
11398c2ecf20Sopenharmony_ci}
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_ci#define fc_private_vport_store_u32_function(field)			\
11428c2ecf20Sopenharmony_cistatic ssize_t								\
11438c2ecf20Sopenharmony_cistore_fc_vport_##field(struct device *dev,				\
11448c2ecf20Sopenharmony_ci		       struct device_attribute *attr,			\
11458c2ecf20Sopenharmony_ci		       const char *buf,	size_t count)			\
11468c2ecf20Sopenharmony_ci{									\
11478c2ecf20Sopenharmony_ci	u32 val;							\
11488c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);		\
11498c2ecf20Sopenharmony_ci	char *cp;							\
11508c2ecf20Sopenharmony_ci	if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))		\
11518c2ecf20Sopenharmony_ci		return -EBUSY;						\
11528c2ecf20Sopenharmony_ci	val = simple_strtoul(buf, &cp, 0);				\
11538c2ecf20Sopenharmony_ci	if (*cp && (*cp != '\n'))					\
11548c2ecf20Sopenharmony_ci		return -EINVAL;						\
11558c2ecf20Sopenharmony_ci	vport->field = val;						\
11568c2ecf20Sopenharmony_ci	return count;							\
11578c2ecf20Sopenharmony_ci}
11588c2ecf20Sopenharmony_ci
11598c2ecf20Sopenharmony_ci
11608c2ecf20Sopenharmony_ci#define fc_private_vport_rd_attr(field, format_string, sz)		\
11618c2ecf20Sopenharmony_ci	fc_private_vport_show_function(field, format_string, sz, )	\
11628c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, field, S_IRUGO,			\
11638c2ecf20Sopenharmony_ci			 show_fc_vport_##field, NULL)
11648c2ecf20Sopenharmony_ci
11658c2ecf20Sopenharmony_ci#define fc_private_vport_rd_attr_cast(field, format_string, sz, cast)	\
11668c2ecf20Sopenharmony_ci	fc_private_vport_show_function(field, format_string, sz, (cast)) \
11678c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, field, S_IRUGO,			\
11688c2ecf20Sopenharmony_ci			  show_fc_vport_##field, NULL)
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_ci#define fc_private_vport_rw_u32_attr(field, format_string, sz)		\
11718c2ecf20Sopenharmony_ci	fc_private_vport_show_function(field, format_string, sz, )	\
11728c2ecf20Sopenharmony_ci	fc_private_vport_store_u32_function(field)			\
11738c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, field, S_IRUGO | S_IWUSR,		\
11748c2ecf20Sopenharmony_ci			show_fc_vport_##field,				\
11758c2ecf20Sopenharmony_ci			store_fc_vport_##field)
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci
11788c2ecf20Sopenharmony_ci#define fc_private_vport_rd_enum_attr(title, maxlen)			\
11798c2ecf20Sopenharmony_cistatic ssize_t								\
11808c2ecf20Sopenharmony_cishow_fc_vport_##title (struct device *dev,				\
11818c2ecf20Sopenharmony_ci		       struct device_attribute *attr,			\
11828c2ecf20Sopenharmony_ci		       char *buf)					\
11838c2ecf20Sopenharmony_ci{									\
11848c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);		\
11858c2ecf20Sopenharmony_ci	const char *name;						\
11868c2ecf20Sopenharmony_ci	name = get_fc_##title##_name(vport->title);			\
11878c2ecf20Sopenharmony_ci	if (!name)							\
11888c2ecf20Sopenharmony_ci		return -EINVAL;						\
11898c2ecf20Sopenharmony_ci	return snprintf(buf, maxlen, "%s\n", name);			\
11908c2ecf20Sopenharmony_ci}									\
11918c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, title, S_IRUGO,			\
11928c2ecf20Sopenharmony_ci			show_fc_vport_##title, NULL)
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci
11958c2ecf20Sopenharmony_ci#define SETUP_VPORT_ATTRIBUTE_RD(field)					\
11968c2ecf20Sopenharmony_ci	i->private_vport_attrs[count] = device_attr_vport_##field; \
11978c2ecf20Sopenharmony_ci	i->private_vport_attrs[count].attr.mode = S_IRUGO;		\
11988c2ecf20Sopenharmony_ci	i->private_vport_attrs[count].store = NULL;			\
11998c2ecf20Sopenharmony_ci	i->vport_attrs[count] = &i->private_vport_attrs[count];		\
12008c2ecf20Sopenharmony_ci	if (i->f->get_##field)						\
12018c2ecf20Sopenharmony_ci		count++
12028c2ecf20Sopenharmony_ci	/* NOTE: Above MACRO differs: checks function not show bit */
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci#define SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(field)				\
12058c2ecf20Sopenharmony_ci	i->private_vport_attrs[count] = device_attr_vport_##field; \
12068c2ecf20Sopenharmony_ci	i->private_vport_attrs[count].attr.mode = S_IRUGO;		\
12078c2ecf20Sopenharmony_ci	i->private_vport_attrs[count].store = NULL;			\
12088c2ecf20Sopenharmony_ci	i->vport_attrs[count] = &i->private_vport_attrs[count];		\
12098c2ecf20Sopenharmony_ci	count++
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci#define SETUP_VPORT_ATTRIBUTE_WR(field)					\
12128c2ecf20Sopenharmony_ci	i->private_vport_attrs[count] = device_attr_vport_##field; \
12138c2ecf20Sopenharmony_ci	i->vport_attrs[count] = &i->private_vport_attrs[count];		\
12148c2ecf20Sopenharmony_ci	if (i->f->field)						\
12158c2ecf20Sopenharmony_ci		count++
12168c2ecf20Sopenharmony_ci	/* NOTE: Above MACRO differs: checks function */
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci#define SETUP_VPORT_ATTRIBUTE_RW(field)					\
12198c2ecf20Sopenharmony_ci	i->private_vport_attrs[count] = device_attr_vport_##field; \
12208c2ecf20Sopenharmony_ci	if (!i->f->set_vport_##field) {					\
12218c2ecf20Sopenharmony_ci		i->private_vport_attrs[count].attr.mode = S_IRUGO;	\
12228c2ecf20Sopenharmony_ci		i->private_vport_attrs[count].store = NULL;		\
12238c2ecf20Sopenharmony_ci	}								\
12248c2ecf20Sopenharmony_ci	i->vport_attrs[count] = &i->private_vport_attrs[count];		\
12258c2ecf20Sopenharmony_ci	count++
12268c2ecf20Sopenharmony_ci	/* NOTE: Above MACRO differs: does not check show bit */
12278c2ecf20Sopenharmony_ci
12288c2ecf20Sopenharmony_ci#define SETUP_PRIVATE_VPORT_ATTRIBUTE_RW(field)				\
12298c2ecf20Sopenharmony_ci{									\
12308c2ecf20Sopenharmony_ci	i->private_vport_attrs[count] = device_attr_vport_##field; \
12318c2ecf20Sopenharmony_ci	i->vport_attrs[count] = &i->private_vport_attrs[count];		\
12328c2ecf20Sopenharmony_ci	count++;							\
12338c2ecf20Sopenharmony_ci}
12348c2ecf20Sopenharmony_ci
12358c2ecf20Sopenharmony_ci
12368c2ecf20Sopenharmony_ci/* The FC Transport Virtual Port Attributes: */
12378c2ecf20Sopenharmony_ci
12388c2ecf20Sopenharmony_ci/* Fixed Virtual Port Attributes */
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci/* Dynamic Virtual Port Attributes */
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci/* Private Virtual Port Attributes */
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_cifc_private_vport_rd_enum_attr(vport_state, FC_VPORTSTATE_MAX_NAMELEN);
12458c2ecf20Sopenharmony_cifc_private_vport_rd_enum_attr(vport_last_state, FC_VPORTSTATE_MAX_NAMELEN);
12468c2ecf20Sopenharmony_cifc_private_vport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
12478c2ecf20Sopenharmony_cifc_private_vport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_cistatic ssize_t
12508c2ecf20Sopenharmony_cishow_fc_vport_roles (struct device *dev, struct device_attribute *attr,
12518c2ecf20Sopenharmony_ci		     char *buf)
12528c2ecf20Sopenharmony_ci{
12538c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);
12548c2ecf20Sopenharmony_ci
12558c2ecf20Sopenharmony_ci	if (vport->roles == FC_PORT_ROLE_UNKNOWN)
12568c2ecf20Sopenharmony_ci		return snprintf(buf, 20, "unknown\n");
12578c2ecf20Sopenharmony_ci	return get_fc_port_roles_names(vport->roles, buf);
12588c2ecf20Sopenharmony_ci}
12598c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, roles, S_IRUGO, show_fc_vport_roles, NULL);
12608c2ecf20Sopenharmony_ci
12618c2ecf20Sopenharmony_cifc_private_vport_rd_enum_attr(vport_type, FC_PORTTYPE_MAX_NAMELEN);
12628c2ecf20Sopenharmony_ci
12638c2ecf20Sopenharmony_cifc_private_vport_show_function(symbolic_name, "%s\n",
12648c2ecf20Sopenharmony_ci		FC_VPORT_SYMBOLIC_NAMELEN + 1, )
12658c2ecf20Sopenharmony_cifc_vport_store_str_function(symbolic_name, FC_VPORT_SYMBOLIC_NAMELEN)
12668c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, symbolic_name, S_IRUGO | S_IWUSR,
12678c2ecf20Sopenharmony_ci		show_fc_vport_symbolic_name, store_fc_vport_symbolic_name);
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_cistatic ssize_t
12708c2ecf20Sopenharmony_cistore_fc_vport_delete(struct device *dev, struct device_attribute *attr,
12718c2ecf20Sopenharmony_ci		      const char *buf, size_t count)
12728c2ecf20Sopenharmony_ci{
12738c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);
12748c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);
12758c2ecf20Sopenharmony_ci	unsigned long flags;
12768c2ecf20Sopenharmony_ci
12778c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
12788c2ecf20Sopenharmony_ci	if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING | FC_VPORT_DELETING)) {
12798c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
12808c2ecf20Sopenharmony_ci		return -EBUSY;
12818c2ecf20Sopenharmony_ci	}
12828c2ecf20Sopenharmony_ci	vport->flags |= FC_VPORT_DELETING;
12838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci	fc_queue_work(shost, &vport->vport_delete_work);
12868c2ecf20Sopenharmony_ci	return count;
12878c2ecf20Sopenharmony_ci}
12888c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, vport_delete, S_IWUSR,
12898c2ecf20Sopenharmony_ci			NULL, store_fc_vport_delete);
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci/*
12938c2ecf20Sopenharmony_ci * Enable/Disable vport
12948c2ecf20Sopenharmony_ci *  Write "1" to disable, write "0" to enable
12958c2ecf20Sopenharmony_ci */
12968c2ecf20Sopenharmony_cistatic ssize_t
12978c2ecf20Sopenharmony_cistore_fc_vport_disable(struct device *dev, struct device_attribute *attr,
12988c2ecf20Sopenharmony_ci		       const char *buf,
12998c2ecf20Sopenharmony_ci			   size_t count)
13008c2ecf20Sopenharmony_ci{
13018c2ecf20Sopenharmony_ci	struct fc_vport *vport = transport_class_to_vport(dev);
13028c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);
13038c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
13048c2ecf20Sopenharmony_ci	int stat;
13058c2ecf20Sopenharmony_ci
13068c2ecf20Sopenharmony_ci	if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
13078c2ecf20Sopenharmony_ci		return -EBUSY;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	if (*buf == '0') {
13108c2ecf20Sopenharmony_ci		if (vport->vport_state != FC_VPORT_DISABLED)
13118c2ecf20Sopenharmony_ci			return -EALREADY;
13128c2ecf20Sopenharmony_ci	} else if (*buf == '1') {
13138c2ecf20Sopenharmony_ci		if (vport->vport_state == FC_VPORT_DISABLED)
13148c2ecf20Sopenharmony_ci			return -EALREADY;
13158c2ecf20Sopenharmony_ci	} else
13168c2ecf20Sopenharmony_ci		return -EINVAL;
13178c2ecf20Sopenharmony_ci
13188c2ecf20Sopenharmony_ci	stat = i->f->vport_disable(vport, ((*buf == '0') ? false : true));
13198c2ecf20Sopenharmony_ci	return stat ? stat : count;
13208c2ecf20Sopenharmony_ci}
13218c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(vport, vport_disable, S_IWUSR,
13228c2ecf20Sopenharmony_ci			NULL, store_fc_vport_disable);
13238c2ecf20Sopenharmony_ci
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci/*
13268c2ecf20Sopenharmony_ci * Host Attribute Management
13278c2ecf20Sopenharmony_ci */
13288c2ecf20Sopenharmony_ci
13298c2ecf20Sopenharmony_ci#define fc_host_show_function(field, format_string, sz, cast)		\
13308c2ecf20Sopenharmony_cistatic ssize_t								\
13318c2ecf20Sopenharmony_cishow_fc_host_##field (struct device *dev,				\
13328c2ecf20Sopenharmony_ci		      struct device_attribute *attr, char *buf)		\
13338c2ecf20Sopenharmony_ci{									\
13348c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
13358c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
13368c2ecf20Sopenharmony_ci	if (i->f->get_host_##field)					\
13378c2ecf20Sopenharmony_ci		i->f->get_host_##field(shost);				\
13388c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, cast fc_host_##field(shost)); \
13398c2ecf20Sopenharmony_ci}
13408c2ecf20Sopenharmony_ci
13418c2ecf20Sopenharmony_ci#define fc_host_store_function(field)					\
13428c2ecf20Sopenharmony_cistatic ssize_t								\
13438c2ecf20Sopenharmony_cistore_fc_host_##field(struct device *dev, 				\
13448c2ecf20Sopenharmony_ci		      struct device_attribute *attr,			\
13458c2ecf20Sopenharmony_ci		      const char *buf,	size_t count)			\
13468c2ecf20Sopenharmony_ci{									\
13478c2ecf20Sopenharmony_ci	int val;							\
13488c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
13498c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
13508c2ecf20Sopenharmony_ci	char *cp;							\
13518c2ecf20Sopenharmony_ci									\
13528c2ecf20Sopenharmony_ci	val = simple_strtoul(buf, &cp, 0);				\
13538c2ecf20Sopenharmony_ci	if (*cp && (*cp != '\n'))					\
13548c2ecf20Sopenharmony_ci		return -EINVAL;						\
13558c2ecf20Sopenharmony_ci	i->f->set_host_##field(shost, val);				\
13568c2ecf20Sopenharmony_ci	return count;							\
13578c2ecf20Sopenharmony_ci}
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci#define fc_host_store_str_function(field, slen)				\
13608c2ecf20Sopenharmony_cistatic ssize_t								\
13618c2ecf20Sopenharmony_cistore_fc_host_##field(struct device *dev,				\
13628c2ecf20Sopenharmony_ci		      struct device_attribute *attr,			\
13638c2ecf20Sopenharmony_ci		      const char *buf, size_t count)			\
13648c2ecf20Sopenharmony_ci{									\
13658c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
13668c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
13678c2ecf20Sopenharmony_ci	unsigned int cnt=count;						\
13688c2ecf20Sopenharmony_ci									\
13698c2ecf20Sopenharmony_ci	/* count may include a LF at end of string */			\
13708c2ecf20Sopenharmony_ci	if (buf[cnt-1] == '\n')						\
13718c2ecf20Sopenharmony_ci		cnt--;							\
13728c2ecf20Sopenharmony_ci	if (cnt > ((slen) - 1))						\
13738c2ecf20Sopenharmony_ci		return -EINVAL;						\
13748c2ecf20Sopenharmony_ci	memcpy(fc_host_##field(shost), buf, cnt);			\
13758c2ecf20Sopenharmony_ci	i->f->set_host_##field(shost);					\
13768c2ecf20Sopenharmony_ci	return count;							\
13778c2ecf20Sopenharmony_ci}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci#define fc_host_rd_attr(field, format_string, sz)			\
13808c2ecf20Sopenharmony_ci	fc_host_show_function(field, format_string, sz, )		\
13818c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, field, S_IRUGO,			\
13828c2ecf20Sopenharmony_ci			 show_fc_host_##field, NULL)
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci#define fc_host_rd_attr_cast(field, format_string, sz, cast)		\
13858c2ecf20Sopenharmony_ci	fc_host_show_function(field, format_string, sz, (cast))		\
13868c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, field, S_IRUGO,			\
13878c2ecf20Sopenharmony_ci			  show_fc_host_##field, NULL)
13888c2ecf20Sopenharmony_ci
13898c2ecf20Sopenharmony_ci#define fc_host_rw_attr(field, format_string, sz)			\
13908c2ecf20Sopenharmony_ci	fc_host_show_function(field, format_string, sz, )		\
13918c2ecf20Sopenharmony_ci	fc_host_store_function(field)					\
13928c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, field, S_IRUGO | S_IWUSR,		\
13938c2ecf20Sopenharmony_ci			show_fc_host_##field,				\
13948c2ecf20Sopenharmony_ci			store_fc_host_##field)
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci#define fc_host_rd_enum_attr(title, maxlen)				\
13978c2ecf20Sopenharmony_cistatic ssize_t								\
13988c2ecf20Sopenharmony_cishow_fc_host_##title (struct device *dev,				\
13998c2ecf20Sopenharmony_ci		      struct device_attribute *attr, char *buf)		\
14008c2ecf20Sopenharmony_ci{									\
14018c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
14028c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);	\
14038c2ecf20Sopenharmony_ci	const char *name;						\
14048c2ecf20Sopenharmony_ci	if (i->f->get_host_##title)					\
14058c2ecf20Sopenharmony_ci		i->f->get_host_##title(shost);				\
14068c2ecf20Sopenharmony_ci	name = get_fc_##title##_name(fc_host_##title(shost));		\
14078c2ecf20Sopenharmony_ci	if (!name)							\
14088c2ecf20Sopenharmony_ci		return -EINVAL;						\
14098c2ecf20Sopenharmony_ci	return snprintf(buf, maxlen, "%s\n", name);			\
14108c2ecf20Sopenharmony_ci}									\
14118c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, title, S_IRUGO, show_fc_host_##title, NULL)
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci#define SETUP_HOST_ATTRIBUTE_RD(field)					\
14148c2ecf20Sopenharmony_ci	i->private_host_attrs[count] = device_attr_host_##field;	\
14158c2ecf20Sopenharmony_ci	i->private_host_attrs[count].attr.mode = S_IRUGO;		\
14168c2ecf20Sopenharmony_ci	i->private_host_attrs[count].store = NULL;			\
14178c2ecf20Sopenharmony_ci	i->host_attrs[count] = &i->private_host_attrs[count];		\
14188c2ecf20Sopenharmony_ci	if (i->f->show_host_##field)					\
14198c2ecf20Sopenharmony_ci		count++
14208c2ecf20Sopenharmony_ci
14218c2ecf20Sopenharmony_ci#define SETUP_HOST_ATTRIBUTE_RD_NS(field)				\
14228c2ecf20Sopenharmony_ci	i->private_host_attrs[count] = device_attr_host_##field;	\
14238c2ecf20Sopenharmony_ci	i->private_host_attrs[count].attr.mode = S_IRUGO;		\
14248c2ecf20Sopenharmony_ci	i->private_host_attrs[count].store = NULL;			\
14258c2ecf20Sopenharmony_ci	i->host_attrs[count] = &i->private_host_attrs[count];		\
14268c2ecf20Sopenharmony_ci	count++
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci#define SETUP_HOST_ATTRIBUTE_RW(field)					\
14298c2ecf20Sopenharmony_ci	i->private_host_attrs[count] = device_attr_host_##field;	\
14308c2ecf20Sopenharmony_ci	if (!i->f->set_host_##field) {					\
14318c2ecf20Sopenharmony_ci		i->private_host_attrs[count].attr.mode = S_IRUGO;	\
14328c2ecf20Sopenharmony_ci		i->private_host_attrs[count].store = NULL;		\
14338c2ecf20Sopenharmony_ci	}								\
14348c2ecf20Sopenharmony_ci	i->host_attrs[count] = &i->private_host_attrs[count];		\
14358c2ecf20Sopenharmony_ci	if (i->f->show_host_##field)					\
14368c2ecf20Sopenharmony_ci		count++
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci
14398c2ecf20Sopenharmony_ci#define fc_private_host_show_function(field, format_string, sz, cast)	\
14408c2ecf20Sopenharmony_cistatic ssize_t								\
14418c2ecf20Sopenharmony_cishow_fc_host_##field (struct device *dev,				\
14428c2ecf20Sopenharmony_ci		      struct device_attribute *attr, char *buf)		\
14438c2ecf20Sopenharmony_ci{									\
14448c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
14458c2ecf20Sopenharmony_ci	return snprintf(buf, sz, format_string, cast fc_host_##field(shost)); \
14468c2ecf20Sopenharmony_ci}
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci#define fc_private_host_rd_attr(field, format_string, sz)		\
14498c2ecf20Sopenharmony_ci	fc_private_host_show_function(field, format_string, sz, )	\
14508c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, field, S_IRUGO,			\
14518c2ecf20Sopenharmony_ci			 show_fc_host_##field, NULL)
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci#define fc_private_host_rd_attr_cast(field, format_string, sz, cast)	\
14548c2ecf20Sopenharmony_ci	fc_private_host_show_function(field, format_string, sz, (cast)) \
14558c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, field, S_IRUGO,			\
14568c2ecf20Sopenharmony_ci			  show_fc_host_##field, NULL)
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci#define SETUP_PRIVATE_HOST_ATTRIBUTE_RD(field)			\
14598c2ecf20Sopenharmony_ci	i->private_host_attrs[count] = device_attr_host_##field;	\
14608c2ecf20Sopenharmony_ci	i->private_host_attrs[count].attr.mode = S_IRUGO;		\
14618c2ecf20Sopenharmony_ci	i->private_host_attrs[count].store = NULL;			\
14628c2ecf20Sopenharmony_ci	i->host_attrs[count] = &i->private_host_attrs[count];		\
14638c2ecf20Sopenharmony_ci	count++
14648c2ecf20Sopenharmony_ci
14658c2ecf20Sopenharmony_ci#define SETUP_PRIVATE_HOST_ATTRIBUTE_RW(field)			\
14668c2ecf20Sopenharmony_ci{									\
14678c2ecf20Sopenharmony_ci	i->private_host_attrs[count] = device_attr_host_##field;	\
14688c2ecf20Sopenharmony_ci	i->host_attrs[count] = &i->private_host_attrs[count];		\
14698c2ecf20Sopenharmony_ci	count++;							\
14708c2ecf20Sopenharmony_ci}
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_ci
14738c2ecf20Sopenharmony_ci/* Fixed Host Attributes */
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_cistatic ssize_t
14768c2ecf20Sopenharmony_cishow_fc_host_supported_classes (struct device *dev,
14778c2ecf20Sopenharmony_ci			        struct device_attribute *attr, char *buf)
14788c2ecf20Sopenharmony_ci{
14798c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
14808c2ecf20Sopenharmony_ci
14818c2ecf20Sopenharmony_ci	if (fc_host_supported_classes(shost) == FC_COS_UNSPECIFIED)
14828c2ecf20Sopenharmony_ci		return snprintf(buf, 20, "unspecified\n");
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	return get_fc_cos_names(fc_host_supported_classes(shost), buf);
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, supported_classes, S_IRUGO,
14878c2ecf20Sopenharmony_ci		show_fc_host_supported_classes, NULL);
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_cistatic ssize_t
14908c2ecf20Sopenharmony_cishow_fc_host_supported_fc4s (struct device *dev,
14918c2ecf20Sopenharmony_ci			     struct device_attribute *attr, char *buf)
14928c2ecf20Sopenharmony_ci{
14938c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
14948c2ecf20Sopenharmony_ci	return (ssize_t)show_fc_fc4s(buf, fc_host_supported_fc4s(shost));
14958c2ecf20Sopenharmony_ci}
14968c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, supported_fc4s, S_IRUGO,
14978c2ecf20Sopenharmony_ci		show_fc_host_supported_fc4s, NULL);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_cistatic ssize_t
15008c2ecf20Sopenharmony_cishow_fc_host_supported_speeds (struct device *dev,
15018c2ecf20Sopenharmony_ci			       struct device_attribute *attr, char *buf)
15028c2ecf20Sopenharmony_ci{
15038c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci	if (fc_host_supported_speeds(shost) == FC_PORTSPEED_UNKNOWN)
15068c2ecf20Sopenharmony_ci		return snprintf(buf, 20, "unknown\n");
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci	return get_fc_port_speed_names(fc_host_supported_speeds(shost), buf);
15098c2ecf20Sopenharmony_ci}
15108c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, supported_speeds, S_IRUGO,
15118c2ecf20Sopenharmony_ci		show_fc_host_supported_speeds, NULL);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci
15148c2ecf20Sopenharmony_cifc_private_host_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
15158c2ecf20Sopenharmony_cifc_private_host_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
15168c2ecf20Sopenharmony_cifc_private_host_rd_attr_cast(permanent_port_name, "0x%llx\n", 20,
15178c2ecf20Sopenharmony_ci			     unsigned long long);
15188c2ecf20Sopenharmony_cifc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20);
15198c2ecf20Sopenharmony_cifc_private_host_rd_attr(max_npiv_vports, "%u\n", 20);
15208c2ecf20Sopenharmony_cifc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1));
15218c2ecf20Sopenharmony_cifc_private_host_rd_attr(manufacturer, "%s\n", FC_SERIAL_NUMBER_SIZE + 1);
15228c2ecf20Sopenharmony_cifc_private_host_rd_attr(model, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
15238c2ecf20Sopenharmony_cifc_private_host_rd_attr(model_description, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
15248c2ecf20Sopenharmony_cifc_private_host_rd_attr(hardware_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
15258c2ecf20Sopenharmony_cifc_private_host_rd_attr(driver_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
15268c2ecf20Sopenharmony_cifc_private_host_rd_attr(firmware_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
15278c2ecf20Sopenharmony_cifc_private_host_rd_attr(optionrom_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
15288c2ecf20Sopenharmony_ci
15298c2ecf20Sopenharmony_ci
15308c2ecf20Sopenharmony_ci/* Dynamic Host Attributes */
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_cistatic ssize_t
15338c2ecf20Sopenharmony_cishow_fc_host_active_fc4s (struct device *dev,
15348c2ecf20Sopenharmony_ci			  struct device_attribute *attr, char *buf)
15358c2ecf20Sopenharmony_ci{
15368c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
15378c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
15388c2ecf20Sopenharmony_ci
15398c2ecf20Sopenharmony_ci	if (i->f->get_host_active_fc4s)
15408c2ecf20Sopenharmony_ci		i->f->get_host_active_fc4s(shost);
15418c2ecf20Sopenharmony_ci
15428c2ecf20Sopenharmony_ci	return (ssize_t)show_fc_fc4s(buf, fc_host_active_fc4s(shost));
15438c2ecf20Sopenharmony_ci}
15448c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, active_fc4s, S_IRUGO,
15458c2ecf20Sopenharmony_ci		show_fc_host_active_fc4s, NULL);
15468c2ecf20Sopenharmony_ci
15478c2ecf20Sopenharmony_cistatic ssize_t
15488c2ecf20Sopenharmony_cishow_fc_host_speed (struct device *dev,
15498c2ecf20Sopenharmony_ci		    struct device_attribute *attr, char *buf)
15508c2ecf20Sopenharmony_ci{
15518c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
15528c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
15538c2ecf20Sopenharmony_ci
15548c2ecf20Sopenharmony_ci	if (i->f->get_host_speed)
15558c2ecf20Sopenharmony_ci		i->f->get_host_speed(shost);
15568c2ecf20Sopenharmony_ci
15578c2ecf20Sopenharmony_ci	if (fc_host_speed(shost) == FC_PORTSPEED_UNKNOWN)
15588c2ecf20Sopenharmony_ci		return snprintf(buf, 20, "unknown\n");
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	return get_fc_port_speed_names(fc_host_speed(shost), buf);
15618c2ecf20Sopenharmony_ci}
15628c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, speed, S_IRUGO,
15638c2ecf20Sopenharmony_ci		show_fc_host_speed, NULL);
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci
15668c2ecf20Sopenharmony_cifc_host_rd_attr(port_id, "0x%06x\n", 20);
15678c2ecf20Sopenharmony_cifc_host_rd_enum_attr(port_type, FC_PORTTYPE_MAX_NAMELEN);
15688c2ecf20Sopenharmony_cifc_host_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
15698c2ecf20Sopenharmony_cifc_host_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
15708c2ecf20Sopenharmony_cifc_host_rd_attr(symbolic_name, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_cifc_private_host_show_function(system_hostname, "%s\n",
15738c2ecf20Sopenharmony_ci		FC_SYMBOLIC_NAME_SIZE + 1, )
15748c2ecf20Sopenharmony_cifc_host_store_str_function(system_hostname, FC_SYMBOLIC_NAME_SIZE)
15758c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, system_hostname, S_IRUGO | S_IWUSR,
15768c2ecf20Sopenharmony_ci		show_fc_host_system_hostname, store_fc_host_system_hostname);
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci
15798c2ecf20Sopenharmony_ci/* Private Host Attributes */
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_cistatic ssize_t
15828c2ecf20Sopenharmony_cishow_fc_private_host_tgtid_bind_type(struct device *dev,
15838c2ecf20Sopenharmony_ci				     struct device_attribute *attr, char *buf)
15848c2ecf20Sopenharmony_ci{
15858c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
15868c2ecf20Sopenharmony_ci	const char *name;
15878c2ecf20Sopenharmony_ci
15888c2ecf20Sopenharmony_ci	name = get_fc_tgtid_bind_type_name(fc_host_tgtid_bind_type(shost));
15898c2ecf20Sopenharmony_ci	if (!name)
15908c2ecf20Sopenharmony_ci		return -EINVAL;
15918c2ecf20Sopenharmony_ci	return snprintf(buf, FC_BINDTYPE_MAX_NAMELEN, "%s\n", name);
15928c2ecf20Sopenharmony_ci}
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci#define get_list_head_entry(pos, head, member) 		\
15958c2ecf20Sopenharmony_ci	pos = list_entry((head)->next, typeof(*pos), member)
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_cistatic ssize_t
15988c2ecf20Sopenharmony_cistore_fc_private_host_tgtid_bind_type(struct device *dev,
15998c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count)
16008c2ecf20Sopenharmony_ci{
16018c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
16028c2ecf20Sopenharmony_ci	struct fc_rport *rport;
16038c2ecf20Sopenharmony_ci 	enum fc_tgtid_binding_type val;
16048c2ecf20Sopenharmony_ci	unsigned long flags;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	if (get_fc_tgtid_bind_type_match(buf, &val))
16078c2ecf20Sopenharmony_ci		return -EINVAL;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci	/* if changing bind type, purge all unused consistent bindings */
16108c2ecf20Sopenharmony_ci	if (val != fc_host_tgtid_bind_type(shost)) {
16118c2ecf20Sopenharmony_ci		spin_lock_irqsave(shost->host_lock, flags);
16128c2ecf20Sopenharmony_ci		while (!list_empty(&fc_host_rport_bindings(shost))) {
16138c2ecf20Sopenharmony_ci			get_list_head_entry(rport,
16148c2ecf20Sopenharmony_ci				&fc_host_rport_bindings(shost), peers);
16158c2ecf20Sopenharmony_ci			list_del(&rport->peers);
16168c2ecf20Sopenharmony_ci			rport->port_state = FC_PORTSTATE_DELETED;
16178c2ecf20Sopenharmony_ci			fc_queue_work(shost, &rport->rport_delete_work);
16188c2ecf20Sopenharmony_ci		}
16198c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
16208c2ecf20Sopenharmony_ci	}
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	fc_host_tgtid_bind_type(shost) = val;
16238c2ecf20Sopenharmony_ci	return count;
16248c2ecf20Sopenharmony_ci}
16258c2ecf20Sopenharmony_ci
16268c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, tgtid_bind_type, S_IRUGO | S_IWUSR,
16278c2ecf20Sopenharmony_ci			show_fc_private_host_tgtid_bind_type,
16288c2ecf20Sopenharmony_ci			store_fc_private_host_tgtid_bind_type);
16298c2ecf20Sopenharmony_ci
16308c2ecf20Sopenharmony_cistatic ssize_t
16318c2ecf20Sopenharmony_cistore_fc_private_host_issue_lip(struct device *dev,
16328c2ecf20Sopenharmony_ci	struct device_attribute *attr, const char *buf, size_t count)
16338c2ecf20Sopenharmony_ci{
16348c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
16358c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
16368c2ecf20Sopenharmony_ci	int ret;
16378c2ecf20Sopenharmony_ci
16388c2ecf20Sopenharmony_ci	/* ignore any data value written to the attribute */
16398c2ecf20Sopenharmony_ci	if (i->f->issue_fc_host_lip) {
16408c2ecf20Sopenharmony_ci		ret = i->f->issue_fc_host_lip(shost);
16418c2ecf20Sopenharmony_ci		return ret ? ret: count;
16428c2ecf20Sopenharmony_ci	}
16438c2ecf20Sopenharmony_ci
16448c2ecf20Sopenharmony_ci	return -ENOENT;
16458c2ecf20Sopenharmony_ci}
16468c2ecf20Sopenharmony_ci
16478c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, issue_lip, S_IWUSR, NULL,
16488c2ecf20Sopenharmony_ci			store_fc_private_host_issue_lip);
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_cistatic ssize_t
16518c2ecf20Sopenharmony_cistore_fc_private_host_dev_loss_tmo(struct device *dev,
16528c2ecf20Sopenharmony_ci				   struct device_attribute *attr,
16538c2ecf20Sopenharmony_ci				   const char *buf, size_t count)
16548c2ecf20Sopenharmony_ci{
16558c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
16568c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
16578c2ecf20Sopenharmony_ci	struct fc_rport *rport;
16588c2ecf20Sopenharmony_ci	unsigned long val, flags;
16598c2ecf20Sopenharmony_ci	int rc;
16608c2ecf20Sopenharmony_ci
16618c2ecf20Sopenharmony_ci	rc = fc_str_to_dev_loss(buf, &val);
16628c2ecf20Sopenharmony_ci	if (rc)
16638c2ecf20Sopenharmony_ci		return rc;
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	fc_host_dev_loss_tmo(shost) = val;
16668c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
16678c2ecf20Sopenharmony_ci	list_for_each_entry(rport, &fc_host->rports, peers)
16688c2ecf20Sopenharmony_ci		fc_rport_set_dev_loss_tmo(rport, val);
16698c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
16708c2ecf20Sopenharmony_ci	return count;
16718c2ecf20Sopenharmony_ci}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_cifc_private_host_show_function(dev_loss_tmo, "%d\n", 20, );
16748c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, dev_loss_tmo, S_IRUGO | S_IWUSR,
16758c2ecf20Sopenharmony_ci		      show_fc_host_dev_loss_tmo,
16768c2ecf20Sopenharmony_ci		      store_fc_private_host_dev_loss_tmo);
16778c2ecf20Sopenharmony_ci
16788c2ecf20Sopenharmony_cifc_private_host_rd_attr(npiv_vports_inuse, "%u\n", 20);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci/*
16818c2ecf20Sopenharmony_ci * Host Statistics Management
16828c2ecf20Sopenharmony_ci */
16838c2ecf20Sopenharmony_ci
16848c2ecf20Sopenharmony_ci/* Show a given attribute in the statistics group */
16858c2ecf20Sopenharmony_cistatic ssize_t
16868c2ecf20Sopenharmony_cifc_stat_show(const struct device *dev, char *buf, unsigned long offset)
16878c2ecf20Sopenharmony_ci{
16888c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
16898c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
16908c2ecf20Sopenharmony_ci	struct fc_host_statistics *stats;
16918c2ecf20Sopenharmony_ci	ssize_t ret = -ENOENT;
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci	if (offset > sizeof(struct fc_host_statistics) ||
16948c2ecf20Sopenharmony_ci	    offset % sizeof(u64) != 0)
16958c2ecf20Sopenharmony_ci		WARN_ON(1);
16968c2ecf20Sopenharmony_ci
16978c2ecf20Sopenharmony_ci	if (i->f->get_fc_host_stats) {
16988c2ecf20Sopenharmony_ci		stats = (i->f->get_fc_host_stats)(shost);
16998c2ecf20Sopenharmony_ci		if (stats)
17008c2ecf20Sopenharmony_ci			ret = snprintf(buf, 20, "0x%llx\n",
17018c2ecf20Sopenharmony_ci			      (unsigned long long)*(u64 *)(((u8 *) stats) + offset));
17028c2ecf20Sopenharmony_ci	}
17038c2ecf20Sopenharmony_ci	return ret;
17048c2ecf20Sopenharmony_ci}
17058c2ecf20Sopenharmony_ci
17068c2ecf20Sopenharmony_ci
17078c2ecf20Sopenharmony_ci/* generate a read-only statistics attribute */
17088c2ecf20Sopenharmony_ci#define fc_host_statistic(name)						\
17098c2ecf20Sopenharmony_cistatic ssize_t show_fcstat_##name(struct device *cd,			\
17108c2ecf20Sopenharmony_ci				  struct device_attribute *attr,	\
17118c2ecf20Sopenharmony_ci				  char *buf)				\
17128c2ecf20Sopenharmony_ci{									\
17138c2ecf20Sopenharmony_ci	return fc_stat_show(cd, buf, 					\
17148c2ecf20Sopenharmony_ci			    offsetof(struct fc_host_statistics, name));	\
17158c2ecf20Sopenharmony_ci}									\
17168c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, name, S_IRUGO, show_fcstat_##name, NULL)
17178c2ecf20Sopenharmony_ci
17188c2ecf20Sopenharmony_cifc_host_statistic(seconds_since_last_reset);
17198c2ecf20Sopenharmony_cifc_host_statistic(tx_frames);
17208c2ecf20Sopenharmony_cifc_host_statistic(tx_words);
17218c2ecf20Sopenharmony_cifc_host_statistic(rx_frames);
17228c2ecf20Sopenharmony_cifc_host_statistic(rx_words);
17238c2ecf20Sopenharmony_cifc_host_statistic(lip_count);
17248c2ecf20Sopenharmony_cifc_host_statistic(nos_count);
17258c2ecf20Sopenharmony_cifc_host_statistic(error_frames);
17268c2ecf20Sopenharmony_cifc_host_statistic(dumped_frames);
17278c2ecf20Sopenharmony_cifc_host_statistic(link_failure_count);
17288c2ecf20Sopenharmony_cifc_host_statistic(loss_of_sync_count);
17298c2ecf20Sopenharmony_cifc_host_statistic(loss_of_signal_count);
17308c2ecf20Sopenharmony_cifc_host_statistic(prim_seq_protocol_err_count);
17318c2ecf20Sopenharmony_cifc_host_statistic(invalid_tx_word_count);
17328c2ecf20Sopenharmony_cifc_host_statistic(invalid_crc_count);
17338c2ecf20Sopenharmony_cifc_host_statistic(fcp_input_requests);
17348c2ecf20Sopenharmony_cifc_host_statistic(fcp_output_requests);
17358c2ecf20Sopenharmony_cifc_host_statistic(fcp_control_requests);
17368c2ecf20Sopenharmony_cifc_host_statistic(fcp_input_megabytes);
17378c2ecf20Sopenharmony_cifc_host_statistic(fcp_output_megabytes);
17388c2ecf20Sopenharmony_cifc_host_statistic(fcp_packet_alloc_failures);
17398c2ecf20Sopenharmony_cifc_host_statistic(fcp_packet_aborts);
17408c2ecf20Sopenharmony_cifc_host_statistic(fcp_frame_alloc_failures);
17418c2ecf20Sopenharmony_cifc_host_statistic(fc_no_free_exch);
17428c2ecf20Sopenharmony_cifc_host_statistic(fc_no_free_exch_xid);
17438c2ecf20Sopenharmony_cifc_host_statistic(fc_xid_not_found);
17448c2ecf20Sopenharmony_cifc_host_statistic(fc_xid_busy);
17458c2ecf20Sopenharmony_cifc_host_statistic(fc_seq_not_found);
17468c2ecf20Sopenharmony_cifc_host_statistic(fc_non_bls_resp);
17478c2ecf20Sopenharmony_ci
17488c2ecf20Sopenharmony_cistatic ssize_t
17498c2ecf20Sopenharmony_cifc_reset_statistics(struct device *dev, struct device_attribute *attr,
17508c2ecf20Sopenharmony_ci		    const char *buf, size_t count)
17518c2ecf20Sopenharmony_ci{
17528c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
17538c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
17548c2ecf20Sopenharmony_ci
17558c2ecf20Sopenharmony_ci	/* ignore any data value written to the attribute */
17568c2ecf20Sopenharmony_ci	if (i->f->reset_fc_host_stats) {
17578c2ecf20Sopenharmony_ci		i->f->reset_fc_host_stats(shost);
17588c2ecf20Sopenharmony_ci		return count;
17598c2ecf20Sopenharmony_ci	}
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci	return -ENOENT;
17628c2ecf20Sopenharmony_ci}
17638c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, reset_statistics, S_IWUSR, NULL,
17648c2ecf20Sopenharmony_ci				fc_reset_statistics);
17658c2ecf20Sopenharmony_ci
17668c2ecf20Sopenharmony_cistatic struct attribute *fc_statistics_attrs[] = {
17678c2ecf20Sopenharmony_ci	&device_attr_host_seconds_since_last_reset.attr,
17688c2ecf20Sopenharmony_ci	&device_attr_host_tx_frames.attr,
17698c2ecf20Sopenharmony_ci	&device_attr_host_tx_words.attr,
17708c2ecf20Sopenharmony_ci	&device_attr_host_rx_frames.attr,
17718c2ecf20Sopenharmony_ci	&device_attr_host_rx_words.attr,
17728c2ecf20Sopenharmony_ci	&device_attr_host_lip_count.attr,
17738c2ecf20Sopenharmony_ci	&device_attr_host_nos_count.attr,
17748c2ecf20Sopenharmony_ci	&device_attr_host_error_frames.attr,
17758c2ecf20Sopenharmony_ci	&device_attr_host_dumped_frames.attr,
17768c2ecf20Sopenharmony_ci	&device_attr_host_link_failure_count.attr,
17778c2ecf20Sopenharmony_ci	&device_attr_host_loss_of_sync_count.attr,
17788c2ecf20Sopenharmony_ci	&device_attr_host_loss_of_signal_count.attr,
17798c2ecf20Sopenharmony_ci	&device_attr_host_prim_seq_protocol_err_count.attr,
17808c2ecf20Sopenharmony_ci	&device_attr_host_invalid_tx_word_count.attr,
17818c2ecf20Sopenharmony_ci	&device_attr_host_invalid_crc_count.attr,
17828c2ecf20Sopenharmony_ci	&device_attr_host_fcp_input_requests.attr,
17838c2ecf20Sopenharmony_ci	&device_attr_host_fcp_output_requests.attr,
17848c2ecf20Sopenharmony_ci	&device_attr_host_fcp_control_requests.attr,
17858c2ecf20Sopenharmony_ci	&device_attr_host_fcp_input_megabytes.attr,
17868c2ecf20Sopenharmony_ci	&device_attr_host_fcp_output_megabytes.attr,
17878c2ecf20Sopenharmony_ci	&device_attr_host_fcp_packet_alloc_failures.attr,
17888c2ecf20Sopenharmony_ci	&device_attr_host_fcp_packet_aborts.attr,
17898c2ecf20Sopenharmony_ci	&device_attr_host_fcp_frame_alloc_failures.attr,
17908c2ecf20Sopenharmony_ci	&device_attr_host_fc_no_free_exch.attr,
17918c2ecf20Sopenharmony_ci	&device_attr_host_fc_no_free_exch_xid.attr,
17928c2ecf20Sopenharmony_ci	&device_attr_host_fc_xid_not_found.attr,
17938c2ecf20Sopenharmony_ci	&device_attr_host_fc_xid_busy.attr,
17948c2ecf20Sopenharmony_ci	&device_attr_host_fc_seq_not_found.attr,
17958c2ecf20Sopenharmony_ci	&device_attr_host_fc_non_bls_resp.attr,
17968c2ecf20Sopenharmony_ci	&device_attr_host_reset_statistics.attr,
17978c2ecf20Sopenharmony_ci	NULL
17988c2ecf20Sopenharmony_ci};
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_cistatic struct attribute_group fc_statistics_group = {
18018c2ecf20Sopenharmony_ci	.name = "statistics",
18028c2ecf20Sopenharmony_ci	.attrs = fc_statistics_attrs,
18038c2ecf20Sopenharmony_ci};
18048c2ecf20Sopenharmony_ci
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci/* Host Vport Attributes */
18078c2ecf20Sopenharmony_ci
18088c2ecf20Sopenharmony_cistatic int
18098c2ecf20Sopenharmony_cifc_parse_wwn(const char *ns, u64 *nm)
18108c2ecf20Sopenharmony_ci{
18118c2ecf20Sopenharmony_ci	unsigned int i, j;
18128c2ecf20Sopenharmony_ci	u8 wwn[8];
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_ci	memset(wwn, 0, sizeof(wwn));
18158c2ecf20Sopenharmony_ci
18168c2ecf20Sopenharmony_ci	/* Validate and store the new name */
18178c2ecf20Sopenharmony_ci	for (i=0, j=0; i < 16; i++) {
18188c2ecf20Sopenharmony_ci		int value;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci		value = hex_to_bin(*ns++);
18218c2ecf20Sopenharmony_ci		if (value >= 0)
18228c2ecf20Sopenharmony_ci			j = (j << 4) | value;
18238c2ecf20Sopenharmony_ci		else
18248c2ecf20Sopenharmony_ci			return -EINVAL;
18258c2ecf20Sopenharmony_ci		if (i % 2) {
18268c2ecf20Sopenharmony_ci			wwn[i/2] = j & 0xff;
18278c2ecf20Sopenharmony_ci			j = 0;
18288c2ecf20Sopenharmony_ci		}
18298c2ecf20Sopenharmony_ci	}
18308c2ecf20Sopenharmony_ci
18318c2ecf20Sopenharmony_ci	*nm = wwn_to_u64(wwn);
18328c2ecf20Sopenharmony_ci
18338c2ecf20Sopenharmony_ci	return 0;
18348c2ecf20Sopenharmony_ci}
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci
18378c2ecf20Sopenharmony_ci/*
18388c2ecf20Sopenharmony_ci * "Short-cut" sysfs variable to create a new vport on a FC Host.
18398c2ecf20Sopenharmony_ci * Input is a string of the form "<WWPN>:<WWNN>". Other attributes
18408c2ecf20Sopenharmony_ci * will default to a NPIV-based FCP_Initiator; The WWNs are specified
18418c2ecf20Sopenharmony_ci * as hex characters, and may *not* contain any prefixes (e.g. 0x, x, etc)
18428c2ecf20Sopenharmony_ci */
18438c2ecf20Sopenharmony_cistatic ssize_t
18448c2ecf20Sopenharmony_cistore_fc_host_vport_create(struct device *dev, struct device_attribute *attr,
18458c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
18468c2ecf20Sopenharmony_ci{
18478c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
18488c2ecf20Sopenharmony_ci	struct fc_vport_identifiers vid;
18498c2ecf20Sopenharmony_ci	struct fc_vport *vport;
18508c2ecf20Sopenharmony_ci	unsigned int cnt=count;
18518c2ecf20Sopenharmony_ci	int stat;
18528c2ecf20Sopenharmony_ci
18538c2ecf20Sopenharmony_ci	memset(&vid, 0, sizeof(vid));
18548c2ecf20Sopenharmony_ci
18558c2ecf20Sopenharmony_ci	/* count may include a LF at end of string */
18568c2ecf20Sopenharmony_ci	if (buf[cnt-1] == '\n')
18578c2ecf20Sopenharmony_ci		cnt--;
18588c2ecf20Sopenharmony_ci
18598c2ecf20Sopenharmony_ci	/* validate we have enough characters for WWPN */
18608c2ecf20Sopenharmony_ci	if ((cnt != (16+1+16)) || (buf[16] != ':'))
18618c2ecf20Sopenharmony_ci		return -EINVAL;
18628c2ecf20Sopenharmony_ci
18638c2ecf20Sopenharmony_ci	stat = fc_parse_wwn(&buf[0], &vid.port_name);
18648c2ecf20Sopenharmony_ci	if (stat)
18658c2ecf20Sopenharmony_ci		return stat;
18668c2ecf20Sopenharmony_ci
18678c2ecf20Sopenharmony_ci	stat = fc_parse_wwn(&buf[17], &vid.node_name);
18688c2ecf20Sopenharmony_ci	if (stat)
18698c2ecf20Sopenharmony_ci		return stat;
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	vid.roles = FC_PORT_ROLE_FCP_INITIATOR;
18728c2ecf20Sopenharmony_ci	vid.vport_type = FC_PORTTYPE_NPIV;
18738c2ecf20Sopenharmony_ci	/* vid.symbolic_name is already zero/NULL's */
18748c2ecf20Sopenharmony_ci	vid.disable = false;		/* always enabled */
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	/* we only allow support on Channel 0 !!! */
18778c2ecf20Sopenharmony_ci	stat = fc_vport_setup(shost, 0, &shost->shost_gendev, &vid, &vport);
18788c2ecf20Sopenharmony_ci	return stat ? stat : count;
18798c2ecf20Sopenharmony_ci}
18808c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, vport_create, S_IWUSR, NULL,
18818c2ecf20Sopenharmony_ci			store_fc_host_vport_create);
18828c2ecf20Sopenharmony_ci
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci/*
18858c2ecf20Sopenharmony_ci * "Short-cut" sysfs variable to delete a vport on a FC Host.
18868c2ecf20Sopenharmony_ci * Vport is identified by a string containing "<WWPN>:<WWNN>".
18878c2ecf20Sopenharmony_ci * The WWNs are specified as hex characters, and may *not* contain
18888c2ecf20Sopenharmony_ci * any prefixes (e.g. 0x, x, etc)
18898c2ecf20Sopenharmony_ci */
18908c2ecf20Sopenharmony_cistatic ssize_t
18918c2ecf20Sopenharmony_cistore_fc_host_vport_delete(struct device *dev, struct device_attribute *attr,
18928c2ecf20Sopenharmony_ci			   const char *buf, size_t count)
18938c2ecf20Sopenharmony_ci{
18948c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);
18958c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
18968c2ecf20Sopenharmony_ci	struct fc_vport *vport;
18978c2ecf20Sopenharmony_ci	u64 wwpn, wwnn;
18988c2ecf20Sopenharmony_ci	unsigned long flags;
18998c2ecf20Sopenharmony_ci	unsigned int cnt=count;
19008c2ecf20Sopenharmony_ci	int stat, match;
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci	/* count may include a LF at end of string */
19038c2ecf20Sopenharmony_ci	if (buf[cnt-1] == '\n')
19048c2ecf20Sopenharmony_ci		cnt--;
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci	/* validate we have enough characters for WWPN */
19078c2ecf20Sopenharmony_ci	if ((cnt != (16+1+16)) || (buf[16] != ':'))
19088c2ecf20Sopenharmony_ci		return -EINVAL;
19098c2ecf20Sopenharmony_ci
19108c2ecf20Sopenharmony_ci	stat = fc_parse_wwn(&buf[0], &wwpn);
19118c2ecf20Sopenharmony_ci	if (stat)
19128c2ecf20Sopenharmony_ci		return stat;
19138c2ecf20Sopenharmony_ci
19148c2ecf20Sopenharmony_ci	stat = fc_parse_wwn(&buf[17], &wwnn);
19158c2ecf20Sopenharmony_ci	if (stat)
19168c2ecf20Sopenharmony_ci		return stat;
19178c2ecf20Sopenharmony_ci
19188c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
19198c2ecf20Sopenharmony_ci	match = 0;
19208c2ecf20Sopenharmony_ci	/* we only allow support on Channel 0 !!! */
19218c2ecf20Sopenharmony_ci	list_for_each_entry(vport, &fc_host->vports, peers) {
19228c2ecf20Sopenharmony_ci		if ((vport->channel == 0) &&
19238c2ecf20Sopenharmony_ci		    (vport->port_name == wwpn) && (vport->node_name == wwnn)) {
19248c2ecf20Sopenharmony_ci			if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
19258c2ecf20Sopenharmony_ci				break;
19268c2ecf20Sopenharmony_ci			vport->flags |= FC_VPORT_DELETING;
19278c2ecf20Sopenharmony_ci			match = 1;
19288c2ecf20Sopenharmony_ci			break;
19298c2ecf20Sopenharmony_ci		}
19308c2ecf20Sopenharmony_ci	}
19318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci	if (!match)
19348c2ecf20Sopenharmony_ci		return -ENODEV;
19358c2ecf20Sopenharmony_ci
19368c2ecf20Sopenharmony_ci	stat = fc_vport_terminate(vport);
19378c2ecf20Sopenharmony_ci	return stat ? stat : count;
19388c2ecf20Sopenharmony_ci}
19398c2ecf20Sopenharmony_cistatic FC_DEVICE_ATTR(host, vport_delete, S_IWUSR, NULL,
19408c2ecf20Sopenharmony_ci			store_fc_host_vport_delete);
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_cistatic int fc_host_match(struct attribute_container *cont,
19448c2ecf20Sopenharmony_ci			  struct device *dev)
19458c2ecf20Sopenharmony_ci{
19468c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
19478c2ecf20Sopenharmony_ci	struct fc_internal *i;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci	if (!scsi_is_host_device(dev))
19508c2ecf20Sopenharmony_ci		return 0;
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	shost = dev_to_shost(dev);
19538c2ecf20Sopenharmony_ci	if (!shost->transportt  || shost->transportt->host_attrs.ac.class
19548c2ecf20Sopenharmony_ci	    != &fc_host_class.class)
19558c2ecf20Sopenharmony_ci		return 0;
19568c2ecf20Sopenharmony_ci
19578c2ecf20Sopenharmony_ci	i = to_fc_internal(shost->transportt);
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci	return &i->t.host_attrs.ac == cont;
19608c2ecf20Sopenharmony_ci}
19618c2ecf20Sopenharmony_ci
19628c2ecf20Sopenharmony_cistatic int fc_target_match(struct attribute_container *cont,
19638c2ecf20Sopenharmony_ci			    struct device *dev)
19648c2ecf20Sopenharmony_ci{
19658c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
19668c2ecf20Sopenharmony_ci	struct fc_internal *i;
19678c2ecf20Sopenharmony_ci
19688c2ecf20Sopenharmony_ci	if (!scsi_is_target_device(dev))
19698c2ecf20Sopenharmony_ci		return 0;
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_ci	shost = dev_to_shost(dev->parent);
19728c2ecf20Sopenharmony_ci	if (!shost->transportt  || shost->transportt->host_attrs.ac.class
19738c2ecf20Sopenharmony_ci	    != &fc_host_class.class)
19748c2ecf20Sopenharmony_ci		return 0;
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci	i = to_fc_internal(shost->transportt);
19778c2ecf20Sopenharmony_ci
19788c2ecf20Sopenharmony_ci	return &i->t.target_attrs.ac == cont;
19798c2ecf20Sopenharmony_ci}
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_cistatic void fc_rport_dev_release(struct device *dev)
19828c2ecf20Sopenharmony_ci{
19838c2ecf20Sopenharmony_ci	struct fc_rport *rport = dev_to_rport(dev);
19848c2ecf20Sopenharmony_ci	put_device(dev->parent);
19858c2ecf20Sopenharmony_ci	kfree(rport);
19868c2ecf20Sopenharmony_ci}
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ciint scsi_is_fc_rport(const struct device *dev)
19898c2ecf20Sopenharmony_ci{
19908c2ecf20Sopenharmony_ci	return dev->release == fc_rport_dev_release;
19918c2ecf20Sopenharmony_ci}
19928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(scsi_is_fc_rport);
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_cistatic int fc_rport_match(struct attribute_container *cont,
19958c2ecf20Sopenharmony_ci			    struct device *dev)
19968c2ecf20Sopenharmony_ci{
19978c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
19988c2ecf20Sopenharmony_ci	struct fc_internal *i;
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci	if (!scsi_is_fc_rport(dev))
20018c2ecf20Sopenharmony_ci		return 0;
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci	shost = dev_to_shost(dev->parent);
20048c2ecf20Sopenharmony_ci	if (!shost->transportt  || shost->transportt->host_attrs.ac.class
20058c2ecf20Sopenharmony_ci	    != &fc_host_class.class)
20068c2ecf20Sopenharmony_ci		return 0;
20078c2ecf20Sopenharmony_ci
20088c2ecf20Sopenharmony_ci	i = to_fc_internal(shost->transportt);
20098c2ecf20Sopenharmony_ci
20108c2ecf20Sopenharmony_ci	return &i->rport_attr_cont.ac == cont;
20118c2ecf20Sopenharmony_ci}
20128c2ecf20Sopenharmony_ci
20138c2ecf20Sopenharmony_ci
20148c2ecf20Sopenharmony_cistatic void fc_vport_dev_release(struct device *dev)
20158c2ecf20Sopenharmony_ci{
20168c2ecf20Sopenharmony_ci	struct fc_vport *vport = dev_to_vport(dev);
20178c2ecf20Sopenharmony_ci	put_device(dev->parent);		/* release kobj parent */
20188c2ecf20Sopenharmony_ci	kfree(vport);
20198c2ecf20Sopenharmony_ci}
20208c2ecf20Sopenharmony_ci
20218c2ecf20Sopenharmony_cistatic int scsi_is_fc_vport(const struct device *dev)
20228c2ecf20Sopenharmony_ci{
20238c2ecf20Sopenharmony_ci	return dev->release == fc_vport_dev_release;
20248c2ecf20Sopenharmony_ci}
20258c2ecf20Sopenharmony_ci
20268c2ecf20Sopenharmony_cistatic int fc_vport_match(struct attribute_container *cont,
20278c2ecf20Sopenharmony_ci			    struct device *dev)
20288c2ecf20Sopenharmony_ci{
20298c2ecf20Sopenharmony_ci	struct fc_vport *vport;
20308c2ecf20Sopenharmony_ci	struct Scsi_Host *shost;
20318c2ecf20Sopenharmony_ci	struct fc_internal *i;
20328c2ecf20Sopenharmony_ci
20338c2ecf20Sopenharmony_ci	if (!scsi_is_fc_vport(dev))
20348c2ecf20Sopenharmony_ci		return 0;
20358c2ecf20Sopenharmony_ci	vport = dev_to_vport(dev);
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_ci	shost = vport_to_shost(vport);
20388c2ecf20Sopenharmony_ci	if (!shost->transportt  || shost->transportt->host_attrs.ac.class
20398c2ecf20Sopenharmony_ci	    != &fc_host_class.class)
20408c2ecf20Sopenharmony_ci		return 0;
20418c2ecf20Sopenharmony_ci
20428c2ecf20Sopenharmony_ci	i = to_fc_internal(shost->transportt);
20438c2ecf20Sopenharmony_ci	return &i->vport_attr_cont.ac == cont;
20448c2ecf20Sopenharmony_ci}
20458c2ecf20Sopenharmony_ci
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci/**
20488c2ecf20Sopenharmony_ci * fc_eh_timed_out - FC Transport I/O timeout intercept handler
20498c2ecf20Sopenharmony_ci * @scmd:	The SCSI command which timed out
20508c2ecf20Sopenharmony_ci *
20518c2ecf20Sopenharmony_ci * This routine protects against error handlers getting invoked while a
20528c2ecf20Sopenharmony_ci * rport is in a blocked state, typically due to a temporarily loss of
20538c2ecf20Sopenharmony_ci * connectivity. If the error handlers are allowed to proceed, requests
20548c2ecf20Sopenharmony_ci * to abort i/o, reset the target, etc will likely fail as there is no way
20558c2ecf20Sopenharmony_ci * to communicate with the device to perform the requested function. These
20568c2ecf20Sopenharmony_ci * failures may result in the midlayer taking the device offline, requiring
20578c2ecf20Sopenharmony_ci * manual intervention to restore operation.
20588c2ecf20Sopenharmony_ci *
20598c2ecf20Sopenharmony_ci * This routine, called whenever an i/o times out, validates the state of
20608c2ecf20Sopenharmony_ci * the underlying rport. If the rport is blocked, it returns
20618c2ecf20Sopenharmony_ci * EH_RESET_TIMER, which will continue to reschedule the timeout.
20628c2ecf20Sopenharmony_ci * Eventually, either the device will return, or devloss_tmo will fire,
20638c2ecf20Sopenharmony_ci * and when the timeout then fires, it will be handled normally.
20648c2ecf20Sopenharmony_ci * If the rport is not blocked, normal error handling continues.
20658c2ecf20Sopenharmony_ci *
20668c2ecf20Sopenharmony_ci * Notes:
20678c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
20688c2ecf20Sopenharmony_ci */
20698c2ecf20Sopenharmony_cienum blk_eh_timer_return
20708c2ecf20Sopenharmony_cifc_eh_timed_out(struct scsi_cmnd *scmd)
20718c2ecf20Sopenharmony_ci{
20728c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device));
20738c2ecf20Sopenharmony_ci
20748c2ecf20Sopenharmony_ci	if (rport->port_state == FC_PORTSTATE_BLOCKED)
20758c2ecf20Sopenharmony_ci		return BLK_EH_RESET_TIMER;
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	return BLK_EH_DONE;
20788c2ecf20Sopenharmony_ci}
20798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_eh_timed_out);
20808c2ecf20Sopenharmony_ci
20818c2ecf20Sopenharmony_ci/*
20828c2ecf20Sopenharmony_ci * Called by fc_user_scan to locate an rport on the shost that
20838c2ecf20Sopenharmony_ci * matches the channel and target id, and invoke scsi_scan_target()
20848c2ecf20Sopenharmony_ci * on the rport.
20858c2ecf20Sopenharmony_ci */
20868c2ecf20Sopenharmony_cistatic void
20878c2ecf20Sopenharmony_cifc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, u64 lun)
20888c2ecf20Sopenharmony_ci{
20898c2ecf20Sopenharmony_ci	struct fc_rport *rport;
20908c2ecf20Sopenharmony_ci	unsigned long flags;
20918c2ecf20Sopenharmony_ci
20928c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
20938c2ecf20Sopenharmony_ci
20948c2ecf20Sopenharmony_ci	list_for_each_entry(rport, &fc_host_rports(shost), peers) {
20958c2ecf20Sopenharmony_ci		if (rport->scsi_target_id == -1)
20968c2ecf20Sopenharmony_ci			continue;
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci		if (rport->port_state != FC_PORTSTATE_ONLINE)
20998c2ecf20Sopenharmony_ci			continue;
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci		if ((channel == rport->channel) &&
21028c2ecf20Sopenharmony_ci		    (id == rport->scsi_target_id)) {
21038c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(shost->host_lock, flags);
21048c2ecf20Sopenharmony_ci			scsi_scan_target(&rport->dev, channel, id, lun,
21058c2ecf20Sopenharmony_ci					 SCSI_SCAN_MANUAL);
21068c2ecf20Sopenharmony_ci			return;
21078c2ecf20Sopenharmony_ci		}
21088c2ecf20Sopenharmony_ci	}
21098c2ecf20Sopenharmony_ci
21108c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
21118c2ecf20Sopenharmony_ci}
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci/*
21148c2ecf20Sopenharmony_ci * Called via sysfs scan routines. Necessary, as the FC transport
21158c2ecf20Sopenharmony_ci * wants to place all target objects below the rport object. So this
21168c2ecf20Sopenharmony_ci * routine must invoke the scsi_scan_target() routine with the rport
21178c2ecf20Sopenharmony_ci * object as the parent.
21188c2ecf20Sopenharmony_ci */
21198c2ecf20Sopenharmony_cistatic int
21208c2ecf20Sopenharmony_cifc_user_scan(struct Scsi_Host *shost, uint channel, uint id, u64 lun)
21218c2ecf20Sopenharmony_ci{
21228c2ecf20Sopenharmony_ci	uint chlo, chhi;
21238c2ecf20Sopenharmony_ci	uint tgtlo, tgthi;
21248c2ecf20Sopenharmony_ci
21258c2ecf20Sopenharmony_ci	if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
21268c2ecf20Sopenharmony_ci	    ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
21278c2ecf20Sopenharmony_ci	    ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
21288c2ecf20Sopenharmony_ci		return -EINVAL;
21298c2ecf20Sopenharmony_ci
21308c2ecf20Sopenharmony_ci	if (channel == SCAN_WILD_CARD) {
21318c2ecf20Sopenharmony_ci		chlo = 0;
21328c2ecf20Sopenharmony_ci		chhi = shost->max_channel + 1;
21338c2ecf20Sopenharmony_ci	} else {
21348c2ecf20Sopenharmony_ci		chlo = channel;
21358c2ecf20Sopenharmony_ci		chhi = channel + 1;
21368c2ecf20Sopenharmony_ci	}
21378c2ecf20Sopenharmony_ci
21388c2ecf20Sopenharmony_ci	if (id == SCAN_WILD_CARD) {
21398c2ecf20Sopenharmony_ci		tgtlo = 0;
21408c2ecf20Sopenharmony_ci		tgthi = shost->max_id;
21418c2ecf20Sopenharmony_ci	} else {
21428c2ecf20Sopenharmony_ci		tgtlo = id;
21438c2ecf20Sopenharmony_ci		tgthi = id + 1;
21448c2ecf20Sopenharmony_ci	}
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci	for ( ; chlo < chhi; chlo++)
21478c2ecf20Sopenharmony_ci		for ( ; tgtlo < tgthi; tgtlo++)
21488c2ecf20Sopenharmony_ci			fc_user_scan_tgt(shost, chlo, tgtlo, lun);
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci	return 0;
21518c2ecf20Sopenharmony_ci}
21528c2ecf20Sopenharmony_ci
21538c2ecf20Sopenharmony_cistruct scsi_transport_template *
21548c2ecf20Sopenharmony_cifc_attach_transport(struct fc_function_template *ft)
21558c2ecf20Sopenharmony_ci{
21568c2ecf20Sopenharmony_ci	int count;
21578c2ecf20Sopenharmony_ci	struct fc_internal *i = kzalloc(sizeof(struct fc_internal),
21588c2ecf20Sopenharmony_ci					GFP_KERNEL);
21598c2ecf20Sopenharmony_ci
21608c2ecf20Sopenharmony_ci	if (unlikely(!i))
21618c2ecf20Sopenharmony_ci		return NULL;
21628c2ecf20Sopenharmony_ci
21638c2ecf20Sopenharmony_ci	i->t.target_attrs.ac.attrs = &i->starget_attrs[0];
21648c2ecf20Sopenharmony_ci	i->t.target_attrs.ac.class = &fc_transport_class.class;
21658c2ecf20Sopenharmony_ci	i->t.target_attrs.ac.match = fc_target_match;
21668c2ecf20Sopenharmony_ci	i->t.target_size = sizeof(struct fc_starget_attrs);
21678c2ecf20Sopenharmony_ci	transport_container_register(&i->t.target_attrs);
21688c2ecf20Sopenharmony_ci
21698c2ecf20Sopenharmony_ci	i->t.host_attrs.ac.attrs = &i->host_attrs[0];
21708c2ecf20Sopenharmony_ci	i->t.host_attrs.ac.class = &fc_host_class.class;
21718c2ecf20Sopenharmony_ci	i->t.host_attrs.ac.match = fc_host_match;
21728c2ecf20Sopenharmony_ci	i->t.host_size = sizeof(struct fc_host_attrs);
21738c2ecf20Sopenharmony_ci	if (ft->get_fc_host_stats)
21748c2ecf20Sopenharmony_ci		i->t.host_attrs.statistics = &fc_statistics_group;
21758c2ecf20Sopenharmony_ci	transport_container_register(&i->t.host_attrs);
21768c2ecf20Sopenharmony_ci
21778c2ecf20Sopenharmony_ci	i->rport_attr_cont.ac.attrs = &i->rport_attrs[0];
21788c2ecf20Sopenharmony_ci	i->rport_attr_cont.ac.class = &fc_rport_class.class;
21798c2ecf20Sopenharmony_ci	i->rport_attr_cont.ac.match = fc_rport_match;
21808c2ecf20Sopenharmony_ci	transport_container_register(&i->rport_attr_cont);
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci	i->vport_attr_cont.ac.attrs = &i->vport_attrs[0];
21838c2ecf20Sopenharmony_ci	i->vport_attr_cont.ac.class = &fc_vport_class.class;
21848c2ecf20Sopenharmony_ci	i->vport_attr_cont.ac.match = fc_vport_match;
21858c2ecf20Sopenharmony_ci	transport_container_register(&i->vport_attr_cont);
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci	i->f = ft;
21888c2ecf20Sopenharmony_ci
21898c2ecf20Sopenharmony_ci	/* Transport uses the shost workq for scsi scanning */
21908c2ecf20Sopenharmony_ci	i->t.create_work_queue = 1;
21918c2ecf20Sopenharmony_ci
21928c2ecf20Sopenharmony_ci	i->t.user_scan = fc_user_scan;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	/*
21958c2ecf20Sopenharmony_ci	 * Setup SCSI Target Attributes.
21968c2ecf20Sopenharmony_ci	 */
21978c2ecf20Sopenharmony_ci	count = 0;
21988c2ecf20Sopenharmony_ci	SETUP_STARGET_ATTRIBUTE_RD(node_name);
21998c2ecf20Sopenharmony_ci	SETUP_STARGET_ATTRIBUTE_RD(port_name);
22008c2ecf20Sopenharmony_ci	SETUP_STARGET_ATTRIBUTE_RD(port_id);
22018c2ecf20Sopenharmony_ci
22028c2ecf20Sopenharmony_ci	BUG_ON(count > FC_STARGET_NUM_ATTRS);
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci	i->starget_attrs[count] = NULL;
22058c2ecf20Sopenharmony_ci
22068c2ecf20Sopenharmony_ci
22078c2ecf20Sopenharmony_ci	/*
22088c2ecf20Sopenharmony_ci	 * Setup SCSI Host Attributes.
22098c2ecf20Sopenharmony_ci	 */
22108c2ecf20Sopenharmony_ci	count=0;
22118c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(node_name);
22128c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(port_name);
22138c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(permanent_port_name);
22148c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(supported_classes);
22158c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(supported_fc4s);
22168c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(supported_speeds);
22178c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(maxframe_size);
22188c2ecf20Sopenharmony_ci	if (ft->vport_create) {
22198c2ecf20Sopenharmony_ci		SETUP_HOST_ATTRIBUTE_RD_NS(max_npiv_vports);
22208c2ecf20Sopenharmony_ci		SETUP_HOST_ATTRIBUTE_RD_NS(npiv_vports_inuse);
22218c2ecf20Sopenharmony_ci	}
22228c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(serial_number);
22238c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(manufacturer);
22248c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(model);
22258c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(model_description);
22268c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(hardware_version);
22278c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(driver_version);
22288c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(firmware_version);
22298c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(optionrom_version);
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(port_id);
22328c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(port_type);
22338c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(port_state);
22348c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(active_fc4s);
22358c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(speed);
22368c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(fabric_name);
22378c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RD(symbolic_name);
22388c2ecf20Sopenharmony_ci	SETUP_HOST_ATTRIBUTE_RW(system_hostname);
22398c2ecf20Sopenharmony_ci
22408c2ecf20Sopenharmony_ci	/* Transport-managed attributes */
22418c2ecf20Sopenharmony_ci	SETUP_PRIVATE_HOST_ATTRIBUTE_RW(dev_loss_tmo);
22428c2ecf20Sopenharmony_ci	SETUP_PRIVATE_HOST_ATTRIBUTE_RW(tgtid_bind_type);
22438c2ecf20Sopenharmony_ci	if (ft->issue_fc_host_lip)
22448c2ecf20Sopenharmony_ci		SETUP_PRIVATE_HOST_ATTRIBUTE_RW(issue_lip);
22458c2ecf20Sopenharmony_ci	if (ft->vport_create)
22468c2ecf20Sopenharmony_ci		SETUP_PRIVATE_HOST_ATTRIBUTE_RW(vport_create);
22478c2ecf20Sopenharmony_ci	if (ft->vport_delete)
22488c2ecf20Sopenharmony_ci		SETUP_PRIVATE_HOST_ATTRIBUTE_RW(vport_delete);
22498c2ecf20Sopenharmony_ci
22508c2ecf20Sopenharmony_ci	BUG_ON(count > FC_HOST_NUM_ATTRS);
22518c2ecf20Sopenharmony_ci
22528c2ecf20Sopenharmony_ci	i->host_attrs[count] = NULL;
22538c2ecf20Sopenharmony_ci
22548c2ecf20Sopenharmony_ci	/*
22558c2ecf20Sopenharmony_ci	 * Setup Remote Port Attributes.
22568c2ecf20Sopenharmony_ci	 */
22578c2ecf20Sopenharmony_ci	count=0;
22588c2ecf20Sopenharmony_ci	SETUP_RPORT_ATTRIBUTE_RD(maxframe_size);
22598c2ecf20Sopenharmony_ci	SETUP_RPORT_ATTRIBUTE_RD(supported_classes);
22608c2ecf20Sopenharmony_ci	SETUP_RPORT_ATTRIBUTE_RW(dev_loss_tmo);
22618c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(node_name);
22628c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_name);
22638c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_id);
22648c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles);
22658c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state);
22668c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id);
22678c2ecf20Sopenharmony_ci	SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(fast_io_fail_tmo);
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci	BUG_ON(count > FC_RPORT_NUM_ATTRS);
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	i->rport_attrs[count] = NULL;
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	/*
22748c2ecf20Sopenharmony_ci	 * Setup Virtual Port Attributes.
22758c2ecf20Sopenharmony_ci	 */
22768c2ecf20Sopenharmony_ci	count=0;
22778c2ecf20Sopenharmony_ci	SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(vport_state);
22788c2ecf20Sopenharmony_ci	SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(vport_last_state);
22798c2ecf20Sopenharmony_ci	SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(node_name);
22808c2ecf20Sopenharmony_ci	SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(port_name);
22818c2ecf20Sopenharmony_ci	SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(roles);
22828c2ecf20Sopenharmony_ci	SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(vport_type);
22838c2ecf20Sopenharmony_ci	SETUP_VPORT_ATTRIBUTE_RW(symbolic_name);
22848c2ecf20Sopenharmony_ci	SETUP_VPORT_ATTRIBUTE_WR(vport_delete);
22858c2ecf20Sopenharmony_ci	SETUP_VPORT_ATTRIBUTE_WR(vport_disable);
22868c2ecf20Sopenharmony_ci
22878c2ecf20Sopenharmony_ci	BUG_ON(count > FC_VPORT_NUM_ATTRS);
22888c2ecf20Sopenharmony_ci
22898c2ecf20Sopenharmony_ci	i->vport_attrs[count] = NULL;
22908c2ecf20Sopenharmony_ci
22918c2ecf20Sopenharmony_ci	return &i->t;
22928c2ecf20Sopenharmony_ci}
22938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_attach_transport);
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_civoid fc_release_transport(struct scsi_transport_template *t)
22968c2ecf20Sopenharmony_ci{
22978c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(t);
22988c2ecf20Sopenharmony_ci
22998c2ecf20Sopenharmony_ci	transport_container_unregister(&i->t.target_attrs);
23008c2ecf20Sopenharmony_ci	transport_container_unregister(&i->t.host_attrs);
23018c2ecf20Sopenharmony_ci	transport_container_unregister(&i->rport_attr_cont);
23028c2ecf20Sopenharmony_ci	transport_container_unregister(&i->vport_attr_cont);
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	kfree(i);
23058c2ecf20Sopenharmony_ci}
23068c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_release_transport);
23078c2ecf20Sopenharmony_ci
23088c2ecf20Sopenharmony_ci/**
23098c2ecf20Sopenharmony_ci * fc_queue_work - Queue work to the fc_host workqueue.
23108c2ecf20Sopenharmony_ci * @shost:	Pointer to Scsi_Host bound to fc_host.
23118c2ecf20Sopenharmony_ci * @work:	Work to queue for execution.
23128c2ecf20Sopenharmony_ci *
23138c2ecf20Sopenharmony_ci * Return value:
23148c2ecf20Sopenharmony_ci * 	1 - work queued for execution
23158c2ecf20Sopenharmony_ci *	0 - work is already queued
23168c2ecf20Sopenharmony_ci *	-EINVAL - work queue doesn't exist
23178c2ecf20Sopenharmony_ci */
23188c2ecf20Sopenharmony_cistatic int
23198c2ecf20Sopenharmony_cifc_queue_work(struct Scsi_Host *shost, struct work_struct *work)
23208c2ecf20Sopenharmony_ci{
23218c2ecf20Sopenharmony_ci	if (unlikely(!fc_host_work_q(shost))) {
23228c2ecf20Sopenharmony_ci		printk(KERN_ERR
23238c2ecf20Sopenharmony_ci			"ERROR: FC host '%s' attempted to queue work, "
23248c2ecf20Sopenharmony_ci			"when no workqueue created.\n", shost->hostt->name);
23258c2ecf20Sopenharmony_ci		dump_stack();
23268c2ecf20Sopenharmony_ci
23278c2ecf20Sopenharmony_ci		return -EINVAL;
23288c2ecf20Sopenharmony_ci	}
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	return queue_work(fc_host_work_q(shost), work);
23318c2ecf20Sopenharmony_ci}
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_ci/**
23348c2ecf20Sopenharmony_ci * fc_flush_work - Flush a fc_host's workqueue.
23358c2ecf20Sopenharmony_ci * @shost:	Pointer to Scsi_Host bound to fc_host.
23368c2ecf20Sopenharmony_ci */
23378c2ecf20Sopenharmony_cistatic void
23388c2ecf20Sopenharmony_cifc_flush_work(struct Scsi_Host *shost)
23398c2ecf20Sopenharmony_ci{
23408c2ecf20Sopenharmony_ci	if (!fc_host_work_q(shost)) {
23418c2ecf20Sopenharmony_ci		printk(KERN_ERR
23428c2ecf20Sopenharmony_ci			"ERROR: FC host '%s' attempted to flush work, "
23438c2ecf20Sopenharmony_ci			"when no workqueue created.\n", shost->hostt->name);
23448c2ecf20Sopenharmony_ci		dump_stack();
23458c2ecf20Sopenharmony_ci		return;
23468c2ecf20Sopenharmony_ci	}
23478c2ecf20Sopenharmony_ci
23488c2ecf20Sopenharmony_ci	flush_workqueue(fc_host_work_q(shost));
23498c2ecf20Sopenharmony_ci}
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci/**
23528c2ecf20Sopenharmony_ci * fc_queue_devloss_work - Schedule work for the fc_host devloss workqueue.
23538c2ecf20Sopenharmony_ci * @shost:	Pointer to Scsi_Host bound to fc_host.
23548c2ecf20Sopenharmony_ci * @work:	Work to queue for execution.
23558c2ecf20Sopenharmony_ci * @delay:	jiffies to delay the work queuing
23568c2ecf20Sopenharmony_ci *
23578c2ecf20Sopenharmony_ci * Return value:
23588c2ecf20Sopenharmony_ci * 	1 on success / 0 already queued / < 0 for error
23598c2ecf20Sopenharmony_ci */
23608c2ecf20Sopenharmony_cistatic int
23618c2ecf20Sopenharmony_cifc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work,
23628c2ecf20Sopenharmony_ci				unsigned long delay)
23638c2ecf20Sopenharmony_ci{
23648c2ecf20Sopenharmony_ci	if (unlikely(!fc_host_devloss_work_q(shost))) {
23658c2ecf20Sopenharmony_ci		printk(KERN_ERR
23668c2ecf20Sopenharmony_ci			"ERROR: FC host '%s' attempted to queue work, "
23678c2ecf20Sopenharmony_ci			"when no workqueue created.\n", shost->hostt->name);
23688c2ecf20Sopenharmony_ci		dump_stack();
23698c2ecf20Sopenharmony_ci
23708c2ecf20Sopenharmony_ci		return -EINVAL;
23718c2ecf20Sopenharmony_ci	}
23728c2ecf20Sopenharmony_ci
23738c2ecf20Sopenharmony_ci	return queue_delayed_work(fc_host_devloss_work_q(shost), work, delay);
23748c2ecf20Sopenharmony_ci}
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_ci/**
23778c2ecf20Sopenharmony_ci * fc_flush_devloss - Flush a fc_host's devloss workqueue.
23788c2ecf20Sopenharmony_ci * @shost:	Pointer to Scsi_Host bound to fc_host.
23798c2ecf20Sopenharmony_ci */
23808c2ecf20Sopenharmony_cistatic void
23818c2ecf20Sopenharmony_cifc_flush_devloss(struct Scsi_Host *shost)
23828c2ecf20Sopenharmony_ci{
23838c2ecf20Sopenharmony_ci	if (!fc_host_devloss_work_q(shost)) {
23848c2ecf20Sopenharmony_ci		printk(KERN_ERR
23858c2ecf20Sopenharmony_ci			"ERROR: FC host '%s' attempted to flush work, "
23868c2ecf20Sopenharmony_ci			"when no workqueue created.\n", shost->hostt->name);
23878c2ecf20Sopenharmony_ci		dump_stack();
23888c2ecf20Sopenharmony_ci		return;
23898c2ecf20Sopenharmony_ci	}
23908c2ecf20Sopenharmony_ci
23918c2ecf20Sopenharmony_ci	flush_workqueue(fc_host_devloss_work_q(shost));
23928c2ecf20Sopenharmony_ci}
23938c2ecf20Sopenharmony_ci
23948c2ecf20Sopenharmony_ci
23958c2ecf20Sopenharmony_ci/**
23968c2ecf20Sopenharmony_ci * fc_remove_host - called to terminate any fc_transport-related elements for a scsi host.
23978c2ecf20Sopenharmony_ci * @shost:	Which &Scsi_Host
23988c2ecf20Sopenharmony_ci *
23998c2ecf20Sopenharmony_ci * This routine is expected to be called immediately preceding the
24008c2ecf20Sopenharmony_ci * a driver's call to scsi_remove_host().
24018c2ecf20Sopenharmony_ci *
24028c2ecf20Sopenharmony_ci * WARNING: A driver utilizing the fc_transport, which fails to call
24038c2ecf20Sopenharmony_ci *   this routine prior to scsi_remove_host(), will leave dangling
24048c2ecf20Sopenharmony_ci *   objects in /sys/class/fc_remote_ports. Access to any of these
24058c2ecf20Sopenharmony_ci *   objects can result in a system crash !!!
24068c2ecf20Sopenharmony_ci *
24078c2ecf20Sopenharmony_ci * Notes:
24088c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
24098c2ecf20Sopenharmony_ci */
24108c2ecf20Sopenharmony_civoid
24118c2ecf20Sopenharmony_cifc_remove_host(struct Scsi_Host *shost)
24128c2ecf20Sopenharmony_ci{
24138c2ecf20Sopenharmony_ci	struct fc_vport *vport = NULL, *next_vport = NULL;
24148c2ecf20Sopenharmony_ci	struct fc_rport *rport = NULL, *next_rport = NULL;
24158c2ecf20Sopenharmony_ci	struct workqueue_struct *work_q;
24168c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
24178c2ecf20Sopenharmony_ci	unsigned long flags;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	/* Remove any vports */
24228c2ecf20Sopenharmony_ci	list_for_each_entry_safe(vport, next_vport, &fc_host->vports, peers) {
24238c2ecf20Sopenharmony_ci		vport->flags |= FC_VPORT_DELETING;
24248c2ecf20Sopenharmony_ci		fc_queue_work(shost, &vport->vport_delete_work);
24258c2ecf20Sopenharmony_ci	}
24268c2ecf20Sopenharmony_ci
24278c2ecf20Sopenharmony_ci	/* Remove any remote ports */
24288c2ecf20Sopenharmony_ci	list_for_each_entry_safe(rport, next_rport,
24298c2ecf20Sopenharmony_ci			&fc_host->rports, peers) {
24308c2ecf20Sopenharmony_ci		list_del(&rport->peers);
24318c2ecf20Sopenharmony_ci		rport->port_state = FC_PORTSTATE_DELETED;
24328c2ecf20Sopenharmony_ci		fc_queue_work(shost, &rport->rport_delete_work);
24338c2ecf20Sopenharmony_ci	}
24348c2ecf20Sopenharmony_ci
24358c2ecf20Sopenharmony_ci	list_for_each_entry_safe(rport, next_rport,
24368c2ecf20Sopenharmony_ci			&fc_host->rport_bindings, peers) {
24378c2ecf20Sopenharmony_ci		list_del(&rport->peers);
24388c2ecf20Sopenharmony_ci		rport->port_state = FC_PORTSTATE_DELETED;
24398c2ecf20Sopenharmony_ci		fc_queue_work(shost, &rport->rport_delete_work);
24408c2ecf20Sopenharmony_ci	}
24418c2ecf20Sopenharmony_ci
24428c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
24438c2ecf20Sopenharmony_ci
24448c2ecf20Sopenharmony_ci	/* flush all scan work items */
24458c2ecf20Sopenharmony_ci	scsi_flush_work(shost);
24468c2ecf20Sopenharmony_ci
24478c2ecf20Sopenharmony_ci	/* flush all stgt delete, and rport delete work items, then kill it  */
24488c2ecf20Sopenharmony_ci	if (fc_host->work_q) {
24498c2ecf20Sopenharmony_ci		work_q = fc_host->work_q;
24508c2ecf20Sopenharmony_ci		fc_host->work_q = NULL;
24518c2ecf20Sopenharmony_ci		destroy_workqueue(work_q);
24528c2ecf20Sopenharmony_ci	}
24538c2ecf20Sopenharmony_ci
24548c2ecf20Sopenharmony_ci	/* flush all devloss work items, then kill it  */
24558c2ecf20Sopenharmony_ci	if (fc_host->devloss_work_q) {
24568c2ecf20Sopenharmony_ci		work_q = fc_host->devloss_work_q;
24578c2ecf20Sopenharmony_ci		fc_host->devloss_work_q = NULL;
24588c2ecf20Sopenharmony_ci		destroy_workqueue(work_q);
24598c2ecf20Sopenharmony_ci	}
24608c2ecf20Sopenharmony_ci}
24618c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_remove_host);
24628c2ecf20Sopenharmony_ci
24638c2ecf20Sopenharmony_cistatic void fc_terminate_rport_io(struct fc_rport *rport)
24648c2ecf20Sopenharmony_ci{
24658c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
24668c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
24678c2ecf20Sopenharmony_ci
24688c2ecf20Sopenharmony_ci	/* Involve the LLDD if possible to terminate all io on the rport. */
24698c2ecf20Sopenharmony_ci	if (i->f->terminate_rport_io)
24708c2ecf20Sopenharmony_ci		i->f->terminate_rport_io(rport);
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci	/*
24738c2ecf20Sopenharmony_ci	 * Must unblock to flush queued IO. scsi-ml will fail incoming reqs.
24748c2ecf20Sopenharmony_ci	 */
24758c2ecf20Sopenharmony_ci	scsi_target_unblock(&rport->dev, SDEV_TRANSPORT_OFFLINE);
24768c2ecf20Sopenharmony_ci}
24778c2ecf20Sopenharmony_ci
24788c2ecf20Sopenharmony_ci/**
24798c2ecf20Sopenharmony_ci * fc_starget_delete - called to delete the scsi descendants of an rport
24808c2ecf20Sopenharmony_ci * @work:	remote port to be operated on.
24818c2ecf20Sopenharmony_ci *
24828c2ecf20Sopenharmony_ci * Deletes target and all sdevs.
24838c2ecf20Sopenharmony_ci */
24848c2ecf20Sopenharmony_cistatic void
24858c2ecf20Sopenharmony_cifc_starget_delete(struct work_struct *work)
24868c2ecf20Sopenharmony_ci{
24878c2ecf20Sopenharmony_ci	struct fc_rport *rport =
24888c2ecf20Sopenharmony_ci		container_of(work, struct fc_rport, stgt_delete_work);
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	fc_terminate_rport_io(rport);
24918c2ecf20Sopenharmony_ci	scsi_remove_target(&rport->dev);
24928c2ecf20Sopenharmony_ci}
24938c2ecf20Sopenharmony_ci
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_ci/**
24968c2ecf20Sopenharmony_ci * fc_rport_final_delete - finish rport termination and delete it.
24978c2ecf20Sopenharmony_ci * @work:	remote port to be deleted.
24988c2ecf20Sopenharmony_ci */
24998c2ecf20Sopenharmony_cistatic void
25008c2ecf20Sopenharmony_cifc_rport_final_delete(struct work_struct *work)
25018c2ecf20Sopenharmony_ci{
25028c2ecf20Sopenharmony_ci	struct fc_rport *rport =
25038c2ecf20Sopenharmony_ci		container_of(work, struct fc_rport, rport_delete_work);
25048c2ecf20Sopenharmony_ci	struct device *dev = &rport->dev;
25058c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
25068c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
25078c2ecf20Sopenharmony_ci	unsigned long flags;
25088c2ecf20Sopenharmony_ci	int do_callback = 0;
25098c2ecf20Sopenharmony_ci
25108c2ecf20Sopenharmony_ci	fc_terminate_rport_io(rport);
25118c2ecf20Sopenharmony_ci
25128c2ecf20Sopenharmony_ci	/*
25138c2ecf20Sopenharmony_ci	 * if a scan is pending, flush the SCSI Host work_q so that
25148c2ecf20Sopenharmony_ci	 * that we can reclaim the rport scan work element.
25158c2ecf20Sopenharmony_ci	 */
25168c2ecf20Sopenharmony_ci	if (rport->flags & FC_RPORT_SCAN_PENDING)
25178c2ecf20Sopenharmony_ci		scsi_flush_work(shost);
25188c2ecf20Sopenharmony_ci
25198c2ecf20Sopenharmony_ci	/*
25208c2ecf20Sopenharmony_ci	 * Cancel any outstanding timers. These should really exist
25218c2ecf20Sopenharmony_ci	 * only when rmmod'ing the LLDD and we're asking for
25228c2ecf20Sopenharmony_ci	 * immediate termination of the rports
25238c2ecf20Sopenharmony_ci	 */
25248c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
25258c2ecf20Sopenharmony_ci	if (rport->flags & FC_RPORT_DEVLOSS_PENDING) {
25268c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
25278c2ecf20Sopenharmony_ci		if (!cancel_delayed_work(&rport->fail_io_work))
25288c2ecf20Sopenharmony_ci			fc_flush_devloss(shost);
25298c2ecf20Sopenharmony_ci		if (!cancel_delayed_work(&rport->dev_loss_work))
25308c2ecf20Sopenharmony_ci			fc_flush_devloss(shost);
25318c2ecf20Sopenharmony_ci		cancel_work_sync(&rport->scan_work);
25328c2ecf20Sopenharmony_ci		spin_lock_irqsave(shost->host_lock, flags);
25338c2ecf20Sopenharmony_ci		rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
25348c2ecf20Sopenharmony_ci	}
25358c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	/* Delete SCSI target and sdevs */
25388c2ecf20Sopenharmony_ci	if (rport->scsi_target_id != -1)
25398c2ecf20Sopenharmony_ci		fc_starget_delete(&rport->stgt_delete_work);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	/*
25428c2ecf20Sopenharmony_ci	 * Notify the driver that the rport is now dead. The LLDD will
25438c2ecf20Sopenharmony_ci	 * also guarantee that any communication to the rport is terminated
25448c2ecf20Sopenharmony_ci	 *
25458c2ecf20Sopenharmony_ci	 * Avoid this call if we already called it when we preserved the
25468c2ecf20Sopenharmony_ci	 * rport for the binding.
25478c2ecf20Sopenharmony_ci	 */
25488c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
25498c2ecf20Sopenharmony_ci	if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
25508c2ecf20Sopenharmony_ci	    (i->f->dev_loss_tmo_callbk)) {
25518c2ecf20Sopenharmony_ci		rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
25528c2ecf20Sopenharmony_ci		do_callback = 1;
25538c2ecf20Sopenharmony_ci	}
25548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
25558c2ecf20Sopenharmony_ci
25568c2ecf20Sopenharmony_ci	if (do_callback)
25578c2ecf20Sopenharmony_ci		i->f->dev_loss_tmo_callbk(rport);
25588c2ecf20Sopenharmony_ci
25598c2ecf20Sopenharmony_ci	fc_bsg_remove(rport->rqst_q);
25608c2ecf20Sopenharmony_ci
25618c2ecf20Sopenharmony_ci	transport_remove_device(dev);
25628c2ecf20Sopenharmony_ci	device_del(dev);
25638c2ecf20Sopenharmony_ci	transport_destroy_device(dev);
25648c2ecf20Sopenharmony_ci	scsi_host_put(shost);			/* for fc_host->rport list */
25658c2ecf20Sopenharmony_ci	put_device(dev);			/* for self-reference */
25668c2ecf20Sopenharmony_ci}
25678c2ecf20Sopenharmony_ci
25688c2ecf20Sopenharmony_ci
25698c2ecf20Sopenharmony_ci/**
25708c2ecf20Sopenharmony_ci * fc_remote_port_create - allocates and creates a remote FC port.
25718c2ecf20Sopenharmony_ci * @shost:	scsi host the remote port is connected to.
25728c2ecf20Sopenharmony_ci * @channel:	Channel on shost port connected to.
25738c2ecf20Sopenharmony_ci * @ids:	The world wide names, fc address, and FC4 port
25748c2ecf20Sopenharmony_ci *		roles for the remote port.
25758c2ecf20Sopenharmony_ci *
25768c2ecf20Sopenharmony_ci * Allocates and creates the remoter port structure, including the
25778c2ecf20Sopenharmony_ci * class and sysfs creation.
25788c2ecf20Sopenharmony_ci *
25798c2ecf20Sopenharmony_ci * Notes:
25808c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
25818c2ecf20Sopenharmony_ci */
25828c2ecf20Sopenharmony_cistatic struct fc_rport *
25838c2ecf20Sopenharmony_cifc_remote_port_create(struct Scsi_Host *shost, int channel,
25848c2ecf20Sopenharmony_ci		      struct fc_rport_identifiers  *ids)
25858c2ecf20Sopenharmony_ci{
25868c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
25878c2ecf20Sopenharmony_ci	struct fc_internal *fci = to_fc_internal(shost->transportt);
25888c2ecf20Sopenharmony_ci	struct fc_rport *rport;
25898c2ecf20Sopenharmony_ci	struct device *dev;
25908c2ecf20Sopenharmony_ci	unsigned long flags;
25918c2ecf20Sopenharmony_ci	int error;
25928c2ecf20Sopenharmony_ci	size_t size;
25938c2ecf20Sopenharmony_ci
25948c2ecf20Sopenharmony_ci	size = (sizeof(struct fc_rport) + fci->f->dd_fcrport_size);
25958c2ecf20Sopenharmony_ci	rport = kzalloc(size, GFP_KERNEL);
25968c2ecf20Sopenharmony_ci	if (unlikely(!rport)) {
25978c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: allocation failure\n", __func__);
25988c2ecf20Sopenharmony_ci		return NULL;
25998c2ecf20Sopenharmony_ci	}
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	rport->maxframe_size = -1;
26028c2ecf20Sopenharmony_ci	rport->supported_classes = FC_COS_UNSPECIFIED;
26038c2ecf20Sopenharmony_ci	rport->dev_loss_tmo = fc_host->dev_loss_tmo;
26048c2ecf20Sopenharmony_ci	memcpy(&rport->node_name, &ids->node_name, sizeof(rport->node_name));
26058c2ecf20Sopenharmony_ci	memcpy(&rport->port_name, &ids->port_name, sizeof(rport->port_name));
26068c2ecf20Sopenharmony_ci	rport->port_id = ids->port_id;
26078c2ecf20Sopenharmony_ci	rport->roles = ids->roles;
26088c2ecf20Sopenharmony_ci	rport->port_state = FC_PORTSTATE_ONLINE;
26098c2ecf20Sopenharmony_ci	if (fci->f->dd_fcrport_size)
26108c2ecf20Sopenharmony_ci		rport->dd_data = &rport[1];
26118c2ecf20Sopenharmony_ci	rport->channel = channel;
26128c2ecf20Sopenharmony_ci	rport->fast_io_fail_tmo = -1;
26138c2ecf20Sopenharmony_ci
26148c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport);
26158c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&rport->fail_io_work, fc_timeout_fail_rport_io);
26168c2ecf20Sopenharmony_ci	INIT_WORK(&rport->scan_work, fc_scsi_scan_rport);
26178c2ecf20Sopenharmony_ci	INIT_WORK(&rport->stgt_delete_work, fc_starget_delete);
26188c2ecf20Sopenharmony_ci	INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete);
26198c2ecf20Sopenharmony_ci
26208c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
26218c2ecf20Sopenharmony_ci
26228c2ecf20Sopenharmony_ci	rport->number = fc_host->next_rport_number++;
26238c2ecf20Sopenharmony_ci	if ((rport->roles & FC_PORT_ROLE_FCP_TARGET) ||
26248c2ecf20Sopenharmony_ci	    (rport->roles & FC_PORT_ROLE_FCP_DUMMY_INITIATOR))
26258c2ecf20Sopenharmony_ci		rport->scsi_target_id = fc_host->next_target_id++;
26268c2ecf20Sopenharmony_ci	else
26278c2ecf20Sopenharmony_ci		rport->scsi_target_id = -1;
26288c2ecf20Sopenharmony_ci	list_add_tail(&rport->peers, &fc_host->rports);
26298c2ecf20Sopenharmony_ci	scsi_host_get(shost);			/* for fc_host->rport list */
26308c2ecf20Sopenharmony_ci
26318c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
26328c2ecf20Sopenharmony_ci
26338c2ecf20Sopenharmony_ci	dev = &rport->dev;
26348c2ecf20Sopenharmony_ci	device_initialize(dev);			/* takes self reference */
26358c2ecf20Sopenharmony_ci	dev->parent = get_device(&shost->shost_gendev); /* parent reference */
26368c2ecf20Sopenharmony_ci	dev->release = fc_rport_dev_release;
26378c2ecf20Sopenharmony_ci	dev_set_name(dev, "rport-%d:%d-%d",
26388c2ecf20Sopenharmony_ci		     shost->host_no, channel, rport->number);
26398c2ecf20Sopenharmony_ci	transport_setup_device(dev);
26408c2ecf20Sopenharmony_ci
26418c2ecf20Sopenharmony_ci	error = device_add(dev);
26428c2ecf20Sopenharmony_ci	if (error) {
26438c2ecf20Sopenharmony_ci		printk(KERN_ERR "FC Remote Port device_add failed\n");
26448c2ecf20Sopenharmony_ci		goto delete_rport;
26458c2ecf20Sopenharmony_ci	}
26468c2ecf20Sopenharmony_ci	transport_add_device(dev);
26478c2ecf20Sopenharmony_ci	transport_configure_device(dev);
26488c2ecf20Sopenharmony_ci
26498c2ecf20Sopenharmony_ci	fc_bsg_rportadd(shost, rport);
26508c2ecf20Sopenharmony_ci	/* ignore any bsg add error - we just can't do sgio */
26518c2ecf20Sopenharmony_ci
26528c2ecf20Sopenharmony_ci	if (rport->roles & FC_PORT_ROLE_FCP_TARGET) {
26538c2ecf20Sopenharmony_ci		/* initiate a scan of the target */
26548c2ecf20Sopenharmony_ci		rport->flags |= FC_RPORT_SCAN_PENDING;
26558c2ecf20Sopenharmony_ci		scsi_queue_work(shost, &rport->scan_work);
26568c2ecf20Sopenharmony_ci	}
26578c2ecf20Sopenharmony_ci
26588c2ecf20Sopenharmony_ci	return rport;
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_cidelete_rport:
26618c2ecf20Sopenharmony_ci	transport_destroy_device(dev);
26628c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
26638c2ecf20Sopenharmony_ci	list_del(&rport->peers);
26648c2ecf20Sopenharmony_ci	scsi_host_put(shost);			/* for fc_host->rport list */
26658c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
26668c2ecf20Sopenharmony_ci	put_device(dev->parent);
26678c2ecf20Sopenharmony_ci	kfree(rport);
26688c2ecf20Sopenharmony_ci	return NULL;
26698c2ecf20Sopenharmony_ci}
26708c2ecf20Sopenharmony_ci
26718c2ecf20Sopenharmony_ci/**
26728c2ecf20Sopenharmony_ci * fc_remote_port_add - notify fc transport of the existence of a remote FC port.
26738c2ecf20Sopenharmony_ci * @shost:	scsi host the remote port is connected to.
26748c2ecf20Sopenharmony_ci * @channel:	Channel on shost port connected to.
26758c2ecf20Sopenharmony_ci * @ids:	The world wide names, fc address, and FC4 port
26768c2ecf20Sopenharmony_ci *		roles for the remote port.
26778c2ecf20Sopenharmony_ci *
26788c2ecf20Sopenharmony_ci * The LLDD calls this routine to notify the transport of the existence
26798c2ecf20Sopenharmony_ci * of a remote port. The LLDD provides the unique identifiers (wwpn,wwn)
26808c2ecf20Sopenharmony_ci * of the port, it's FC address (port_id), and the FC4 roles that are
26818c2ecf20Sopenharmony_ci * active for the port.
26828c2ecf20Sopenharmony_ci *
26838c2ecf20Sopenharmony_ci * For ports that are FCP targets (aka scsi targets), the FC transport
26848c2ecf20Sopenharmony_ci * maintains consistent target id bindings on behalf of the LLDD.
26858c2ecf20Sopenharmony_ci * A consistent target id binding is an assignment of a target id to
26868c2ecf20Sopenharmony_ci * a remote port identifier, which persists while the scsi host is
26878c2ecf20Sopenharmony_ci * attached. The remote port can disappear, then later reappear, and
26888c2ecf20Sopenharmony_ci * it's target id assignment remains the same. This allows for shifts
26898c2ecf20Sopenharmony_ci * in FC addressing (if binding by wwpn or wwnn) with no apparent
26908c2ecf20Sopenharmony_ci * changes to the scsi subsystem which is based on scsi host number and
26918c2ecf20Sopenharmony_ci * target id values.  Bindings are only valid during the attachment of
26928c2ecf20Sopenharmony_ci * the scsi host. If the host detaches, then later re-attaches, target
26938c2ecf20Sopenharmony_ci * id bindings may change.
26948c2ecf20Sopenharmony_ci *
26958c2ecf20Sopenharmony_ci * This routine is responsible for returning a remote port structure.
26968c2ecf20Sopenharmony_ci * The routine will search the list of remote ports it maintains
26978c2ecf20Sopenharmony_ci * internally on behalf of consistent target id mappings. If found, the
26988c2ecf20Sopenharmony_ci * remote port structure will be reused. Otherwise, a new remote port
26998c2ecf20Sopenharmony_ci * structure will be allocated.
27008c2ecf20Sopenharmony_ci *
27018c2ecf20Sopenharmony_ci * Whenever a remote port is allocated, a new fc_remote_port class
27028c2ecf20Sopenharmony_ci * device is created.
27038c2ecf20Sopenharmony_ci *
27048c2ecf20Sopenharmony_ci * Should not be called from interrupt context.
27058c2ecf20Sopenharmony_ci *
27068c2ecf20Sopenharmony_ci * Notes:
27078c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
27088c2ecf20Sopenharmony_ci */
27098c2ecf20Sopenharmony_cistruct fc_rport *
27108c2ecf20Sopenharmony_cifc_remote_port_add(struct Scsi_Host *shost, int channel,
27118c2ecf20Sopenharmony_ci	struct fc_rport_identifiers  *ids)
27128c2ecf20Sopenharmony_ci{
27138c2ecf20Sopenharmony_ci	struct fc_internal *fci = to_fc_internal(shost->transportt);
27148c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
27158c2ecf20Sopenharmony_ci	struct fc_rport *rport;
27168c2ecf20Sopenharmony_ci	unsigned long flags;
27178c2ecf20Sopenharmony_ci	int match = 0;
27188c2ecf20Sopenharmony_ci
27198c2ecf20Sopenharmony_ci	/* ensure any stgt delete functions are done */
27208c2ecf20Sopenharmony_ci	fc_flush_work(shost);
27218c2ecf20Sopenharmony_ci
27228c2ecf20Sopenharmony_ci	/*
27238c2ecf20Sopenharmony_ci	 * Search the list of "active" rports, for an rport that has been
27248c2ecf20Sopenharmony_ci	 * deleted, but we've held off the real delete while the target
27258c2ecf20Sopenharmony_ci	 * is in a "blocked" state.
27268c2ecf20Sopenharmony_ci	 */
27278c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci	list_for_each_entry(rport, &fc_host->rports, peers) {
27308c2ecf20Sopenharmony_ci
27318c2ecf20Sopenharmony_ci		if ((rport->port_state == FC_PORTSTATE_BLOCKED ||
27328c2ecf20Sopenharmony_ci		     rport->port_state == FC_PORTSTATE_NOTPRESENT) &&
27338c2ecf20Sopenharmony_ci			(rport->channel == channel)) {
27348c2ecf20Sopenharmony_ci
27358c2ecf20Sopenharmony_ci			switch (fc_host->tgtid_bind_type) {
27368c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_BY_WWPN:
27378c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_NONE:
27388c2ecf20Sopenharmony_ci				if (rport->port_name == ids->port_name)
27398c2ecf20Sopenharmony_ci					match = 1;
27408c2ecf20Sopenharmony_ci				break;
27418c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_BY_WWNN:
27428c2ecf20Sopenharmony_ci				if (rport->node_name == ids->node_name)
27438c2ecf20Sopenharmony_ci					match = 1;
27448c2ecf20Sopenharmony_ci				break;
27458c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_BY_ID:
27468c2ecf20Sopenharmony_ci				if (rport->port_id == ids->port_id)
27478c2ecf20Sopenharmony_ci					match = 1;
27488c2ecf20Sopenharmony_ci				break;
27498c2ecf20Sopenharmony_ci			}
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci			if (match) {
27528c2ecf20Sopenharmony_ci
27538c2ecf20Sopenharmony_ci				memcpy(&rport->node_name, &ids->node_name,
27548c2ecf20Sopenharmony_ci					sizeof(rport->node_name));
27558c2ecf20Sopenharmony_ci				memcpy(&rport->port_name, &ids->port_name,
27568c2ecf20Sopenharmony_ci					sizeof(rport->port_name));
27578c2ecf20Sopenharmony_ci				rport->port_id = ids->port_id;
27588c2ecf20Sopenharmony_ci
27598c2ecf20Sopenharmony_ci				rport->port_state = FC_PORTSTATE_ONLINE;
27608c2ecf20Sopenharmony_ci				rport->roles = ids->roles;
27618c2ecf20Sopenharmony_ci
27628c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(shost->host_lock, flags);
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci				if (fci->f->dd_fcrport_size)
27658c2ecf20Sopenharmony_ci					memset(rport->dd_data, 0,
27668c2ecf20Sopenharmony_ci						fci->f->dd_fcrport_size);
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci				/*
27698c2ecf20Sopenharmony_ci				 * If we were not a target, cancel the
27708c2ecf20Sopenharmony_ci				 * io terminate and rport timers, and
27718c2ecf20Sopenharmony_ci				 * we're done.
27728c2ecf20Sopenharmony_ci				 *
27738c2ecf20Sopenharmony_ci				 * If we were a target, but our new role
27748c2ecf20Sopenharmony_ci				 * doesn't indicate a target, leave the
27758c2ecf20Sopenharmony_ci				 * timers running expecting the role to
27768c2ecf20Sopenharmony_ci				 * change as the target fully logs in. If
27778c2ecf20Sopenharmony_ci				 * it doesn't, the target will be torn down.
27788c2ecf20Sopenharmony_ci				 *
27798c2ecf20Sopenharmony_ci				 * If we were a target, and our role shows
27808c2ecf20Sopenharmony_ci				 * we're still a target, cancel the timers
27818c2ecf20Sopenharmony_ci				 * and kick off a scan.
27828c2ecf20Sopenharmony_ci				 */
27838c2ecf20Sopenharmony_ci
27848c2ecf20Sopenharmony_ci				/* was a target, not in roles */
27858c2ecf20Sopenharmony_ci				if ((rport->scsi_target_id != -1) &&
27868c2ecf20Sopenharmony_ci				    (!(ids->roles & FC_PORT_ROLE_FCP_TARGET)))
27878c2ecf20Sopenharmony_ci					return rport;
27888c2ecf20Sopenharmony_ci
27898c2ecf20Sopenharmony_ci				/*
27908c2ecf20Sopenharmony_ci				 * Stop the fail io and dev_loss timers.
27918c2ecf20Sopenharmony_ci				 * If they flush, the port_state will
27928c2ecf20Sopenharmony_ci				 * be checked and will NOOP the function.
27938c2ecf20Sopenharmony_ci				 */
27948c2ecf20Sopenharmony_ci				if (!cancel_delayed_work(&rport->fail_io_work))
27958c2ecf20Sopenharmony_ci					fc_flush_devloss(shost);
27968c2ecf20Sopenharmony_ci				if (!cancel_delayed_work(&rport->dev_loss_work))
27978c2ecf20Sopenharmony_ci					fc_flush_devloss(shost);
27988c2ecf20Sopenharmony_ci
27998c2ecf20Sopenharmony_ci				spin_lock_irqsave(shost->host_lock, flags);
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_ci				rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT |
28028c2ecf20Sopenharmony_ci						  FC_RPORT_DEVLOSS_PENDING |
28038c2ecf20Sopenharmony_ci						  FC_RPORT_DEVLOSS_CALLBK_DONE);
28048c2ecf20Sopenharmony_ci
28058c2ecf20Sopenharmony_ci				spin_unlock_irqrestore(shost->host_lock, flags);
28068c2ecf20Sopenharmony_ci
28078c2ecf20Sopenharmony_ci				/* if target, initiate a scan */
28088c2ecf20Sopenharmony_ci				if (rport->scsi_target_id != -1) {
28098c2ecf20Sopenharmony_ci					scsi_target_unblock(&rport->dev,
28108c2ecf20Sopenharmony_ci							    SDEV_RUNNING);
28118c2ecf20Sopenharmony_ci					spin_lock_irqsave(shost->host_lock,
28128c2ecf20Sopenharmony_ci							  flags);
28138c2ecf20Sopenharmony_ci					rport->flags |= FC_RPORT_SCAN_PENDING;
28148c2ecf20Sopenharmony_ci					scsi_queue_work(shost,
28158c2ecf20Sopenharmony_ci							&rport->scan_work);
28168c2ecf20Sopenharmony_ci					spin_unlock_irqrestore(shost->host_lock,
28178c2ecf20Sopenharmony_ci							flags);
28188c2ecf20Sopenharmony_ci				}
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci				fc_bsg_goose_queue(rport);
28218c2ecf20Sopenharmony_ci
28228c2ecf20Sopenharmony_ci				return rport;
28238c2ecf20Sopenharmony_ci			}
28248c2ecf20Sopenharmony_ci		}
28258c2ecf20Sopenharmony_ci	}
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_ci	/*
28288c2ecf20Sopenharmony_ci	 * Search the bindings array
28298c2ecf20Sopenharmony_ci	 * Note: if never a FCP target, you won't be on this list
28308c2ecf20Sopenharmony_ci	 */
28318c2ecf20Sopenharmony_ci	if (fc_host->tgtid_bind_type != FC_TGTID_BIND_NONE) {
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci		/* search for a matching consistent binding */
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci		list_for_each_entry(rport, &fc_host->rport_bindings,
28368c2ecf20Sopenharmony_ci					peers) {
28378c2ecf20Sopenharmony_ci			if (rport->channel != channel)
28388c2ecf20Sopenharmony_ci				continue;
28398c2ecf20Sopenharmony_ci
28408c2ecf20Sopenharmony_ci			switch (fc_host->tgtid_bind_type) {
28418c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_BY_WWPN:
28428c2ecf20Sopenharmony_ci				if (rport->port_name == ids->port_name)
28438c2ecf20Sopenharmony_ci					match = 1;
28448c2ecf20Sopenharmony_ci				break;
28458c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_BY_WWNN:
28468c2ecf20Sopenharmony_ci				if (rport->node_name == ids->node_name)
28478c2ecf20Sopenharmony_ci					match = 1;
28488c2ecf20Sopenharmony_ci				break;
28498c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_BY_ID:
28508c2ecf20Sopenharmony_ci				if (rport->port_id == ids->port_id)
28518c2ecf20Sopenharmony_ci					match = 1;
28528c2ecf20Sopenharmony_ci				break;
28538c2ecf20Sopenharmony_ci			case FC_TGTID_BIND_NONE: /* to keep compiler happy */
28548c2ecf20Sopenharmony_ci				break;
28558c2ecf20Sopenharmony_ci			}
28568c2ecf20Sopenharmony_ci
28578c2ecf20Sopenharmony_ci			if (match) {
28588c2ecf20Sopenharmony_ci				list_move_tail(&rport->peers, &fc_host->rports);
28598c2ecf20Sopenharmony_ci				break;
28608c2ecf20Sopenharmony_ci			}
28618c2ecf20Sopenharmony_ci		}
28628c2ecf20Sopenharmony_ci
28638c2ecf20Sopenharmony_ci		if (match) {
28648c2ecf20Sopenharmony_ci			memcpy(&rport->node_name, &ids->node_name,
28658c2ecf20Sopenharmony_ci				sizeof(rport->node_name));
28668c2ecf20Sopenharmony_ci			memcpy(&rport->port_name, &ids->port_name,
28678c2ecf20Sopenharmony_ci				sizeof(rport->port_name));
28688c2ecf20Sopenharmony_ci			rport->port_id = ids->port_id;
28698c2ecf20Sopenharmony_ci			rport->port_state = FC_PORTSTATE_ONLINE;
28708c2ecf20Sopenharmony_ci			rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_ci			if (fci->f->dd_fcrport_size)
28738c2ecf20Sopenharmony_ci				memset(rport->dd_data, 0,
28748c2ecf20Sopenharmony_ci						fci->f->dd_fcrport_size);
28758c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(shost->host_lock, flags);
28768c2ecf20Sopenharmony_ci
28778c2ecf20Sopenharmony_ci			fc_remote_port_rolechg(rport, ids->roles);
28788c2ecf20Sopenharmony_ci			return rport;
28798c2ecf20Sopenharmony_ci		}
28808c2ecf20Sopenharmony_ci	}
28818c2ecf20Sopenharmony_ci
28828c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_ci	/* No consistent binding found - create new remote port entry */
28858c2ecf20Sopenharmony_ci	rport = fc_remote_port_create(shost, channel, ids);
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ci	return rport;
28888c2ecf20Sopenharmony_ci}
28898c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_remote_port_add);
28908c2ecf20Sopenharmony_ci
28918c2ecf20Sopenharmony_ci
28928c2ecf20Sopenharmony_ci/**
28938c2ecf20Sopenharmony_ci * fc_remote_port_delete - notifies the fc transport that a remote port is no longer in existence.
28948c2ecf20Sopenharmony_ci * @rport:	The remote port that no longer exists
28958c2ecf20Sopenharmony_ci *
28968c2ecf20Sopenharmony_ci * The LLDD calls this routine to notify the transport that a remote
28978c2ecf20Sopenharmony_ci * port is no longer part of the topology. Note: Although a port
28988c2ecf20Sopenharmony_ci * may no longer be part of the topology, it may persist in the remote
28998c2ecf20Sopenharmony_ci * ports displayed by the fc_host. We do this under 2 conditions:
29008c2ecf20Sopenharmony_ci *
29018c2ecf20Sopenharmony_ci * 1) If the port was a scsi target, we delay its deletion by "blocking" it.
29028c2ecf20Sopenharmony_ci *    This allows the port to temporarily disappear, then reappear without
29038c2ecf20Sopenharmony_ci *    disrupting the SCSI device tree attached to it. During the "blocked"
29048c2ecf20Sopenharmony_ci *    period the port will still exist.
29058c2ecf20Sopenharmony_ci *
29068c2ecf20Sopenharmony_ci * 2) If the port was a scsi target and disappears for longer than we
29078c2ecf20Sopenharmony_ci *    expect, we'll delete the port and the tear down the SCSI device tree
29088c2ecf20Sopenharmony_ci *    attached to it. However, we want to semi-persist the target id assigned
29098c2ecf20Sopenharmony_ci *    to that port if it eventually does exist. The port structure will
29108c2ecf20Sopenharmony_ci *    remain (although with minimal information) so that the target id
29118c2ecf20Sopenharmony_ci *    bindings also remain.
29128c2ecf20Sopenharmony_ci *
29138c2ecf20Sopenharmony_ci * If the remote port is not an FCP Target, it will be fully torn down
29148c2ecf20Sopenharmony_ci * and deallocated, including the fc_remote_port class device.
29158c2ecf20Sopenharmony_ci *
29168c2ecf20Sopenharmony_ci * If the remote port is an FCP Target, the port will be placed in a
29178c2ecf20Sopenharmony_ci * temporary blocked state. From the LLDD's perspective, the rport no
29188c2ecf20Sopenharmony_ci * longer exists. From the SCSI midlayer's perspective, the SCSI target
29198c2ecf20Sopenharmony_ci * exists, but all sdevs on it are blocked from further I/O. The following
29208c2ecf20Sopenharmony_ci * is then expected.
29218c2ecf20Sopenharmony_ci *
29228c2ecf20Sopenharmony_ci *   If the remote port does not return (signaled by a LLDD call to
29238c2ecf20Sopenharmony_ci *   fc_remote_port_add()) within the dev_loss_tmo timeout, then the
29248c2ecf20Sopenharmony_ci *   scsi target is removed - killing all outstanding i/o and removing the
29258c2ecf20Sopenharmony_ci *   scsi devices attached to it. The port structure will be marked Not
29268c2ecf20Sopenharmony_ci *   Present and be partially cleared, leaving only enough information to
29278c2ecf20Sopenharmony_ci *   recognize the remote port relative to the scsi target id binding if
29288c2ecf20Sopenharmony_ci *   it later appears.  The port will remain as long as there is a valid
29298c2ecf20Sopenharmony_ci *   binding (e.g. until the user changes the binding type or unloads the
29308c2ecf20Sopenharmony_ci *   scsi host with the binding).
29318c2ecf20Sopenharmony_ci *
29328c2ecf20Sopenharmony_ci *   If the remote port returns within the dev_loss_tmo value (and matches
29338c2ecf20Sopenharmony_ci *   according to the target id binding type), the port structure will be
29348c2ecf20Sopenharmony_ci *   reused. If it is no longer a SCSI target, the target will be torn
29358c2ecf20Sopenharmony_ci *   down. If it continues to be a SCSI target, then the target will be
29368c2ecf20Sopenharmony_ci *   unblocked (allowing i/o to be resumed), and a scan will be activated
29378c2ecf20Sopenharmony_ci *   to ensure that all luns are detected.
29388c2ecf20Sopenharmony_ci *
29398c2ecf20Sopenharmony_ci * Called from normal process context only - cannot be called from interrupt.
29408c2ecf20Sopenharmony_ci *
29418c2ecf20Sopenharmony_ci * Notes:
29428c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
29438c2ecf20Sopenharmony_ci */
29448c2ecf20Sopenharmony_civoid
29458c2ecf20Sopenharmony_cifc_remote_port_delete(struct fc_rport  *rport)
29468c2ecf20Sopenharmony_ci{
29478c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
29488c2ecf20Sopenharmony_ci	unsigned long timeout = rport->dev_loss_tmo;
29498c2ecf20Sopenharmony_ci	unsigned long flags;
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci	/*
29528c2ecf20Sopenharmony_ci	 * No need to flush the fc_host work_q's, as all adds are synchronous.
29538c2ecf20Sopenharmony_ci	 *
29548c2ecf20Sopenharmony_ci	 * We do need to reclaim the rport scan work element, so eventually
29558c2ecf20Sopenharmony_ci	 * (in fc_rport_final_delete()) we'll flush the scsi host work_q if
29568c2ecf20Sopenharmony_ci	 * there's still a scan pending.
29578c2ecf20Sopenharmony_ci	 */
29588c2ecf20Sopenharmony_ci
29598c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
29608c2ecf20Sopenharmony_ci
29618c2ecf20Sopenharmony_ci	if (rport->port_state != FC_PORTSTATE_ONLINE) {
29628c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
29638c2ecf20Sopenharmony_ci		return;
29648c2ecf20Sopenharmony_ci	}
29658c2ecf20Sopenharmony_ci
29668c2ecf20Sopenharmony_ci	/*
29678c2ecf20Sopenharmony_ci	 * In the past, we if this was not an FCP-Target, we would
29688c2ecf20Sopenharmony_ci	 * unconditionally just jump to deleting the rport.
29698c2ecf20Sopenharmony_ci	 * However, rports can be used as node containers by the LLDD,
29708c2ecf20Sopenharmony_ci	 * and its not appropriate to just terminate the rport at the
29718c2ecf20Sopenharmony_ci	 * first sign of a loss in connectivity. The LLDD may want to
29728c2ecf20Sopenharmony_ci	 * send ELS traffic to re-validate the login. If the rport is
29738c2ecf20Sopenharmony_ci	 * immediately deleted, it makes it inappropriate for a node
29748c2ecf20Sopenharmony_ci	 * container.
29758c2ecf20Sopenharmony_ci	 * So... we now unconditionally wait dev_loss_tmo before
29768c2ecf20Sopenharmony_ci	 * destroying an rport.
29778c2ecf20Sopenharmony_ci	 */
29788c2ecf20Sopenharmony_ci
29798c2ecf20Sopenharmony_ci	rport->port_state = FC_PORTSTATE_BLOCKED;
29808c2ecf20Sopenharmony_ci
29818c2ecf20Sopenharmony_ci	rport->flags |= FC_RPORT_DEVLOSS_PENDING;
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
29848c2ecf20Sopenharmony_ci
29858c2ecf20Sopenharmony_ci	scsi_target_block(&rport->dev);
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci	/* see if we need to kill io faster than waiting for device loss */
29888c2ecf20Sopenharmony_ci	if ((rport->fast_io_fail_tmo != -1) &&
29898c2ecf20Sopenharmony_ci	    (rport->fast_io_fail_tmo < timeout))
29908c2ecf20Sopenharmony_ci		fc_queue_devloss_work(shost, &rport->fail_io_work,
29918c2ecf20Sopenharmony_ci					rport->fast_io_fail_tmo * HZ);
29928c2ecf20Sopenharmony_ci
29938c2ecf20Sopenharmony_ci	/* cap the length the devices can be blocked until they are deleted */
29948c2ecf20Sopenharmony_ci	fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ);
29958c2ecf20Sopenharmony_ci}
29968c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_remote_port_delete);
29978c2ecf20Sopenharmony_ci
29988c2ecf20Sopenharmony_ci/**
29998c2ecf20Sopenharmony_ci * fc_remote_port_rolechg - notifies the fc transport that the roles on a remote may have changed.
30008c2ecf20Sopenharmony_ci * @rport:	The remote port that changed.
30018c2ecf20Sopenharmony_ci * @roles:      New roles for this port.
30028c2ecf20Sopenharmony_ci *
30038c2ecf20Sopenharmony_ci * Description: The LLDD calls this routine to notify the transport that the
30048c2ecf20Sopenharmony_ci * roles on a remote port may have changed. The largest effect of this is
30058c2ecf20Sopenharmony_ci * if a port now becomes a FCP Target, it must be allocated a
30068c2ecf20Sopenharmony_ci * scsi target id.  If the port is no longer a FCP target, any
30078c2ecf20Sopenharmony_ci * scsi target id value assigned to it will persist in case the
30088c2ecf20Sopenharmony_ci * role changes back to include FCP Target. No changes in the scsi
30098c2ecf20Sopenharmony_ci * midlayer will be invoked if the role changes (in the expectation
30108c2ecf20Sopenharmony_ci * that the role will be resumed. If it doesn't normal error processing
30118c2ecf20Sopenharmony_ci * will take place).
30128c2ecf20Sopenharmony_ci *
30138c2ecf20Sopenharmony_ci * Should not be called from interrupt context.
30148c2ecf20Sopenharmony_ci *
30158c2ecf20Sopenharmony_ci * Notes:
30168c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
30178c2ecf20Sopenharmony_ci */
30188c2ecf20Sopenharmony_civoid
30198c2ecf20Sopenharmony_cifc_remote_port_rolechg(struct fc_rport  *rport, u32 roles)
30208c2ecf20Sopenharmony_ci{
30218c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
30228c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
30238c2ecf20Sopenharmony_ci	unsigned long flags;
30248c2ecf20Sopenharmony_ci	int create = 0;
30258c2ecf20Sopenharmony_ci
30268c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
30278c2ecf20Sopenharmony_ci	if (roles & FC_PORT_ROLE_FCP_TARGET) {
30288c2ecf20Sopenharmony_ci		if (rport->scsi_target_id == -1) {
30298c2ecf20Sopenharmony_ci			rport->scsi_target_id = fc_host->next_target_id++;
30308c2ecf20Sopenharmony_ci			create = 1;
30318c2ecf20Sopenharmony_ci		} else if (!(rport->roles & FC_PORT_ROLE_FCP_TARGET))
30328c2ecf20Sopenharmony_ci			create = 1;
30338c2ecf20Sopenharmony_ci	}
30348c2ecf20Sopenharmony_ci
30358c2ecf20Sopenharmony_ci	rport->roles = roles;
30368c2ecf20Sopenharmony_ci
30378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	if (create) {
30408c2ecf20Sopenharmony_ci		/*
30418c2ecf20Sopenharmony_ci		 * There may have been a delete timer running on the
30428c2ecf20Sopenharmony_ci		 * port. Ensure that it is cancelled as we now know
30438c2ecf20Sopenharmony_ci		 * the port is an FCP Target.
30448c2ecf20Sopenharmony_ci		 * Note: we know the rport exists and is in an online
30458c2ecf20Sopenharmony_ci		 *  state as the LLDD would not have had an rport
30468c2ecf20Sopenharmony_ci		 *  reference to pass us.
30478c2ecf20Sopenharmony_ci		 *
30488c2ecf20Sopenharmony_ci		 * Take no action on the del_timer failure as the state
30498c2ecf20Sopenharmony_ci		 * machine state change will validate the
30508c2ecf20Sopenharmony_ci		 * transaction.
30518c2ecf20Sopenharmony_ci		 */
30528c2ecf20Sopenharmony_ci		if (!cancel_delayed_work(&rport->fail_io_work))
30538c2ecf20Sopenharmony_ci			fc_flush_devloss(shost);
30548c2ecf20Sopenharmony_ci		if (!cancel_delayed_work(&rport->dev_loss_work))
30558c2ecf20Sopenharmony_ci			fc_flush_devloss(shost);
30568c2ecf20Sopenharmony_ci
30578c2ecf20Sopenharmony_ci		spin_lock_irqsave(shost->host_lock, flags);
30588c2ecf20Sopenharmony_ci		rport->flags &= ~(FC_RPORT_FAST_FAIL_TIMEDOUT |
30598c2ecf20Sopenharmony_ci				  FC_RPORT_DEVLOSS_PENDING |
30608c2ecf20Sopenharmony_ci				  FC_RPORT_DEVLOSS_CALLBK_DONE);
30618c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
30628c2ecf20Sopenharmony_ci
30638c2ecf20Sopenharmony_ci		/* ensure any stgt delete functions are done */
30648c2ecf20Sopenharmony_ci		fc_flush_work(shost);
30658c2ecf20Sopenharmony_ci
30668c2ecf20Sopenharmony_ci		scsi_target_unblock(&rport->dev, SDEV_RUNNING);
30678c2ecf20Sopenharmony_ci		/* initiate a scan of the target */
30688c2ecf20Sopenharmony_ci		spin_lock_irqsave(shost->host_lock, flags);
30698c2ecf20Sopenharmony_ci		rport->flags |= FC_RPORT_SCAN_PENDING;
30708c2ecf20Sopenharmony_ci		scsi_queue_work(shost, &rport->scan_work);
30718c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
30728c2ecf20Sopenharmony_ci	}
30738c2ecf20Sopenharmony_ci}
30748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_remote_port_rolechg);
30758c2ecf20Sopenharmony_ci
30768c2ecf20Sopenharmony_ci/**
30778c2ecf20Sopenharmony_ci * fc_timeout_deleted_rport - Timeout handler for a deleted remote port.
30788c2ecf20Sopenharmony_ci * @work:	rport target that failed to reappear in the allotted time.
30798c2ecf20Sopenharmony_ci *
30808c2ecf20Sopenharmony_ci * Description: An attempt to delete a remote port blocks, and if it fails
30818c2ecf20Sopenharmony_ci *              to return in the allotted time this gets called.
30828c2ecf20Sopenharmony_ci */
30838c2ecf20Sopenharmony_cistatic void
30848c2ecf20Sopenharmony_cifc_timeout_deleted_rport(struct work_struct *work)
30858c2ecf20Sopenharmony_ci{
30868c2ecf20Sopenharmony_ci	struct fc_rport *rport =
30878c2ecf20Sopenharmony_ci		container_of(work, struct fc_rport, dev_loss_work.work);
30888c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
30898c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
30908c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
30918c2ecf20Sopenharmony_ci	unsigned long flags;
30928c2ecf20Sopenharmony_ci	int do_callback = 0;
30938c2ecf20Sopenharmony_ci
30948c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
30958c2ecf20Sopenharmony_ci
30968c2ecf20Sopenharmony_ci	rport->flags &= ~FC_RPORT_DEVLOSS_PENDING;
30978c2ecf20Sopenharmony_ci
30988c2ecf20Sopenharmony_ci	/*
30998c2ecf20Sopenharmony_ci	 * If the port is ONLINE, then it came back. If it was a SCSI
31008c2ecf20Sopenharmony_ci	 * target, validate it still is. If not, tear down the
31018c2ecf20Sopenharmony_ci	 * scsi_target on it.
31028c2ecf20Sopenharmony_ci	 */
31038c2ecf20Sopenharmony_ci	if ((rport->port_state == FC_PORTSTATE_ONLINE) &&
31048c2ecf20Sopenharmony_ci	    (rport->scsi_target_id != -1) &&
31058c2ecf20Sopenharmony_ci	    !(rport->roles & FC_PORT_ROLE_FCP_TARGET)) {
31068c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &rport->dev,
31078c2ecf20Sopenharmony_ci			"blocked FC remote port time out: no longer"
31088c2ecf20Sopenharmony_ci			" a FCP target, removing starget\n");
31098c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
31108c2ecf20Sopenharmony_ci		scsi_target_unblock(&rport->dev, SDEV_TRANSPORT_OFFLINE);
31118c2ecf20Sopenharmony_ci		fc_queue_work(shost, &rport->stgt_delete_work);
31128c2ecf20Sopenharmony_ci		return;
31138c2ecf20Sopenharmony_ci	}
31148c2ecf20Sopenharmony_ci
31158c2ecf20Sopenharmony_ci	/* NOOP state - we're flushing workq's */
31168c2ecf20Sopenharmony_ci	if (rport->port_state != FC_PORTSTATE_BLOCKED) {
31178c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
31188c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &rport->dev,
31198c2ecf20Sopenharmony_ci			"blocked FC remote port time out: leaving"
31208c2ecf20Sopenharmony_ci			" rport%s alone\n",
31218c2ecf20Sopenharmony_ci			(rport->scsi_target_id != -1) ?  " and starget" : "");
31228c2ecf20Sopenharmony_ci		return;
31238c2ecf20Sopenharmony_ci	}
31248c2ecf20Sopenharmony_ci
31258c2ecf20Sopenharmony_ci	if ((fc_host->tgtid_bind_type == FC_TGTID_BIND_NONE) ||
31268c2ecf20Sopenharmony_ci	    (rport->scsi_target_id == -1)) {
31278c2ecf20Sopenharmony_ci		list_del(&rport->peers);
31288c2ecf20Sopenharmony_ci		rport->port_state = FC_PORTSTATE_DELETED;
31298c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, &rport->dev,
31308c2ecf20Sopenharmony_ci			"blocked FC remote port time out: removing"
31318c2ecf20Sopenharmony_ci			" rport%s\n",
31328c2ecf20Sopenharmony_ci			(rport->scsi_target_id != -1) ?  " and starget" : "");
31338c2ecf20Sopenharmony_ci		fc_queue_work(shost, &rport->rport_delete_work);
31348c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
31358c2ecf20Sopenharmony_ci		return;
31368c2ecf20Sopenharmony_ci	}
31378c2ecf20Sopenharmony_ci
31388c2ecf20Sopenharmony_ci	dev_printk(KERN_ERR, &rport->dev,
31398c2ecf20Sopenharmony_ci		"blocked FC remote port time out: removing target and "
31408c2ecf20Sopenharmony_ci		"saving binding\n");
31418c2ecf20Sopenharmony_ci
31428c2ecf20Sopenharmony_ci	list_move_tail(&rport->peers, &fc_host->rport_bindings);
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci	/*
31458c2ecf20Sopenharmony_ci	 * Note: We do not remove or clear the hostdata area. This allows
31468c2ecf20Sopenharmony_ci	 *   host-specific target data to persist along with the
31478c2ecf20Sopenharmony_ci	 *   scsi_target_id. It's up to the host to manage it's hostdata area.
31488c2ecf20Sopenharmony_ci	 */
31498c2ecf20Sopenharmony_ci
31508c2ecf20Sopenharmony_ci	/*
31518c2ecf20Sopenharmony_ci	 * Reinitialize port attributes that may change if the port comes back.
31528c2ecf20Sopenharmony_ci	 */
31538c2ecf20Sopenharmony_ci	rport->maxframe_size = -1;
31548c2ecf20Sopenharmony_ci	rport->supported_classes = FC_COS_UNSPECIFIED;
31558c2ecf20Sopenharmony_ci	rport->roles = FC_PORT_ROLE_UNKNOWN;
31568c2ecf20Sopenharmony_ci	rport->port_state = FC_PORTSTATE_NOTPRESENT;
31578c2ecf20Sopenharmony_ci	rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
31588c2ecf20Sopenharmony_ci
31598c2ecf20Sopenharmony_ci	/*
31608c2ecf20Sopenharmony_ci	 * Pre-emptively kill I/O rather than waiting for the work queue
31618c2ecf20Sopenharmony_ci	 * item to teardown the starget. (FCOE libFC folks prefer this
31628c2ecf20Sopenharmony_ci	 * and to have the rport_port_id still set when it's done).
31638c2ecf20Sopenharmony_ci	 */
31648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
31658c2ecf20Sopenharmony_ci	fc_terminate_rport_io(rport);
31668c2ecf20Sopenharmony_ci
31678c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci	if (rport->port_state == FC_PORTSTATE_NOTPRESENT) {	/* still missing */
31708c2ecf20Sopenharmony_ci
31718c2ecf20Sopenharmony_ci		/* remove the identifiers that aren't used in the consisting binding */
31728c2ecf20Sopenharmony_ci		switch (fc_host->tgtid_bind_type) {
31738c2ecf20Sopenharmony_ci		case FC_TGTID_BIND_BY_WWPN:
31748c2ecf20Sopenharmony_ci			rport->node_name = -1;
31758c2ecf20Sopenharmony_ci			rport->port_id = -1;
31768c2ecf20Sopenharmony_ci			break;
31778c2ecf20Sopenharmony_ci		case FC_TGTID_BIND_BY_WWNN:
31788c2ecf20Sopenharmony_ci			rport->port_name = -1;
31798c2ecf20Sopenharmony_ci			rport->port_id = -1;
31808c2ecf20Sopenharmony_ci			break;
31818c2ecf20Sopenharmony_ci		case FC_TGTID_BIND_BY_ID:
31828c2ecf20Sopenharmony_ci			rport->node_name = -1;
31838c2ecf20Sopenharmony_ci			rport->port_name = -1;
31848c2ecf20Sopenharmony_ci			break;
31858c2ecf20Sopenharmony_ci		case FC_TGTID_BIND_NONE:	/* to keep compiler happy */
31868c2ecf20Sopenharmony_ci			break;
31878c2ecf20Sopenharmony_ci		}
31888c2ecf20Sopenharmony_ci
31898c2ecf20Sopenharmony_ci		/*
31908c2ecf20Sopenharmony_ci		 * As this only occurs if the remote port (scsi target)
31918c2ecf20Sopenharmony_ci		 * went away and didn't come back - we'll remove
31928c2ecf20Sopenharmony_ci		 * all attached scsi devices.
31938c2ecf20Sopenharmony_ci		 */
31948c2ecf20Sopenharmony_ci		rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
31958c2ecf20Sopenharmony_ci		fc_queue_work(shost, &rport->stgt_delete_work);
31968c2ecf20Sopenharmony_ci
31978c2ecf20Sopenharmony_ci		do_callback = 1;
31988c2ecf20Sopenharmony_ci	}
31998c2ecf20Sopenharmony_ci
32008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
32018c2ecf20Sopenharmony_ci
32028c2ecf20Sopenharmony_ci	/*
32038c2ecf20Sopenharmony_ci	 * Notify the driver that the rport is now dead. The LLDD will
32048c2ecf20Sopenharmony_ci	 * also guarantee that any communication to the rport is terminated
32058c2ecf20Sopenharmony_ci	 *
32068c2ecf20Sopenharmony_ci	 * Note: we set the CALLBK_DONE flag above to correspond
32078c2ecf20Sopenharmony_ci	 */
32088c2ecf20Sopenharmony_ci	if (do_callback && i->f->dev_loss_tmo_callbk)
32098c2ecf20Sopenharmony_ci		i->f->dev_loss_tmo_callbk(rport);
32108c2ecf20Sopenharmony_ci}
32118c2ecf20Sopenharmony_ci
32128c2ecf20Sopenharmony_ci
32138c2ecf20Sopenharmony_ci/**
32148c2ecf20Sopenharmony_ci * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a disconnected SCSI target.
32158c2ecf20Sopenharmony_ci * @work:	rport to terminate io on.
32168c2ecf20Sopenharmony_ci *
32178c2ecf20Sopenharmony_ci * Notes: Only requests the failure of the io, not that all are flushed
32188c2ecf20Sopenharmony_ci *    prior to returning.
32198c2ecf20Sopenharmony_ci */
32208c2ecf20Sopenharmony_cistatic void
32218c2ecf20Sopenharmony_cifc_timeout_fail_rport_io(struct work_struct *work)
32228c2ecf20Sopenharmony_ci{
32238c2ecf20Sopenharmony_ci	struct fc_rport *rport =
32248c2ecf20Sopenharmony_ci		container_of(work, struct fc_rport, fail_io_work.work);
32258c2ecf20Sopenharmony_ci
32268c2ecf20Sopenharmony_ci	if (rport->port_state != FC_PORTSTATE_BLOCKED)
32278c2ecf20Sopenharmony_ci		return;
32288c2ecf20Sopenharmony_ci
32298c2ecf20Sopenharmony_ci	rport->flags |= FC_RPORT_FAST_FAIL_TIMEDOUT;
32308c2ecf20Sopenharmony_ci	fc_terminate_rport_io(rport);
32318c2ecf20Sopenharmony_ci}
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci/**
32348c2ecf20Sopenharmony_ci * fc_scsi_scan_rport - called to perform a scsi scan on a remote port.
32358c2ecf20Sopenharmony_ci * @work:	remote port to be scanned.
32368c2ecf20Sopenharmony_ci */
32378c2ecf20Sopenharmony_cistatic void
32388c2ecf20Sopenharmony_cifc_scsi_scan_rport(struct work_struct *work)
32398c2ecf20Sopenharmony_ci{
32408c2ecf20Sopenharmony_ci	struct fc_rport *rport =
32418c2ecf20Sopenharmony_ci		container_of(work, struct fc_rport, scan_work);
32428c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
32438c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
32448c2ecf20Sopenharmony_ci	unsigned long flags;
32458c2ecf20Sopenharmony_ci
32468c2ecf20Sopenharmony_ci	if ((rport->port_state == FC_PORTSTATE_ONLINE) &&
32478c2ecf20Sopenharmony_ci	    (rport->roles & FC_PORT_ROLE_FCP_TARGET) &&
32488c2ecf20Sopenharmony_ci	    !(i->f->disable_target_scan)) {
32498c2ecf20Sopenharmony_ci		scsi_scan_target(&rport->dev, rport->channel,
32508c2ecf20Sopenharmony_ci				 rport->scsi_target_id, SCAN_WILD_CARD,
32518c2ecf20Sopenharmony_ci				 SCSI_SCAN_RESCAN);
32528c2ecf20Sopenharmony_ci	}
32538c2ecf20Sopenharmony_ci
32548c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
32558c2ecf20Sopenharmony_ci	rport->flags &= ~FC_RPORT_SCAN_PENDING;
32568c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
32578c2ecf20Sopenharmony_ci}
32588c2ecf20Sopenharmony_ci
32598c2ecf20Sopenharmony_ci/**
32608c2ecf20Sopenharmony_ci * fc_block_rport() - Block SCSI eh thread for blocked fc_rport.
32618c2ecf20Sopenharmony_ci * @rport: Remote port that scsi_eh is trying to recover.
32628c2ecf20Sopenharmony_ci *
32638c2ecf20Sopenharmony_ci * This routine can be called from a FC LLD scsi_eh callback. It
32648c2ecf20Sopenharmony_ci * blocks the scsi_eh thread until the fc_rport leaves the
32658c2ecf20Sopenharmony_ci * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is
32668c2ecf20Sopenharmony_ci * necessary to avoid the scsi_eh failing recovery actions for blocked
32678c2ecf20Sopenharmony_ci * rports which would lead to offlined SCSI devices.
32688c2ecf20Sopenharmony_ci *
32698c2ecf20Sopenharmony_ci * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED.
32708c2ecf20Sopenharmony_ci *	    FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be
32718c2ecf20Sopenharmony_ci *	    passed back to scsi_eh.
32728c2ecf20Sopenharmony_ci */
32738c2ecf20Sopenharmony_ciint fc_block_rport(struct fc_rport *rport)
32748c2ecf20Sopenharmony_ci{
32758c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = rport_to_shost(rport);
32768c2ecf20Sopenharmony_ci	unsigned long flags;
32778c2ecf20Sopenharmony_ci
32788c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
32798c2ecf20Sopenharmony_ci	while (rport->port_state == FC_PORTSTATE_BLOCKED &&
32808c2ecf20Sopenharmony_ci	       !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) {
32818c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
32828c2ecf20Sopenharmony_ci		msleep(1000);
32838c2ecf20Sopenharmony_ci		spin_lock_irqsave(shost->host_lock, flags);
32848c2ecf20Sopenharmony_ci	}
32858c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
32868c2ecf20Sopenharmony_ci
32878c2ecf20Sopenharmony_ci	if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)
32888c2ecf20Sopenharmony_ci		return FAST_IO_FAIL;
32898c2ecf20Sopenharmony_ci
32908c2ecf20Sopenharmony_ci	return 0;
32918c2ecf20Sopenharmony_ci}
32928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_block_rport);
32938c2ecf20Sopenharmony_ci
32948c2ecf20Sopenharmony_ci/**
32958c2ecf20Sopenharmony_ci * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
32968c2ecf20Sopenharmony_ci * @cmnd: SCSI command that scsi_eh is trying to recover
32978c2ecf20Sopenharmony_ci *
32988c2ecf20Sopenharmony_ci * This routine can be called from a FC LLD scsi_eh callback. It
32998c2ecf20Sopenharmony_ci * blocks the scsi_eh thread until the fc_rport leaves the
33008c2ecf20Sopenharmony_ci * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is
33018c2ecf20Sopenharmony_ci * necessary to avoid the scsi_eh failing recovery actions for blocked
33028c2ecf20Sopenharmony_ci * rports which would lead to offlined SCSI devices.
33038c2ecf20Sopenharmony_ci *
33048c2ecf20Sopenharmony_ci * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED.
33058c2ecf20Sopenharmony_ci *	    FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be
33068c2ecf20Sopenharmony_ci *	    passed back to scsi_eh.
33078c2ecf20Sopenharmony_ci */
33088c2ecf20Sopenharmony_ciint fc_block_scsi_eh(struct scsi_cmnd *cmnd)
33098c2ecf20Sopenharmony_ci{
33108c2ecf20Sopenharmony_ci	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
33118c2ecf20Sopenharmony_ci
33128c2ecf20Sopenharmony_ci	if (WARN_ON_ONCE(!rport))
33138c2ecf20Sopenharmony_ci		return FAST_IO_FAIL;
33148c2ecf20Sopenharmony_ci
33158c2ecf20Sopenharmony_ci	return fc_block_rport(rport);
33168c2ecf20Sopenharmony_ci}
33178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_block_scsi_eh);
33188c2ecf20Sopenharmony_ci
33198c2ecf20Sopenharmony_ci/**
33208c2ecf20Sopenharmony_ci * fc_vport_setup - allocates and creates a FC virtual port.
33218c2ecf20Sopenharmony_ci * @shost:	scsi host the virtual port is connected to.
33228c2ecf20Sopenharmony_ci * @channel:	Channel on shost port connected to.
33238c2ecf20Sopenharmony_ci * @pdev:	parent device for vport
33248c2ecf20Sopenharmony_ci * @ids:	The world wide names, FC4 port roles, etc for
33258c2ecf20Sopenharmony_ci *              the virtual port.
33268c2ecf20Sopenharmony_ci * @ret_vport:	The pointer to the created vport.
33278c2ecf20Sopenharmony_ci *
33288c2ecf20Sopenharmony_ci * Allocates and creates the vport structure, calls the parent host
33298c2ecf20Sopenharmony_ci * to instantiate the vport, this completes w/ class and sysfs creation.
33308c2ecf20Sopenharmony_ci *
33318c2ecf20Sopenharmony_ci * Notes:
33328c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
33338c2ecf20Sopenharmony_ci */
33348c2ecf20Sopenharmony_cistatic int
33358c2ecf20Sopenharmony_cifc_vport_setup(struct Scsi_Host *shost, int channel, struct device *pdev,
33368c2ecf20Sopenharmony_ci	struct fc_vport_identifiers  *ids, struct fc_vport **ret_vport)
33378c2ecf20Sopenharmony_ci{
33388c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
33398c2ecf20Sopenharmony_ci	struct fc_internal *fci = to_fc_internal(shost->transportt);
33408c2ecf20Sopenharmony_ci	struct fc_vport *vport;
33418c2ecf20Sopenharmony_ci	struct device *dev;
33428c2ecf20Sopenharmony_ci	unsigned long flags;
33438c2ecf20Sopenharmony_ci	size_t size;
33448c2ecf20Sopenharmony_ci	int error;
33458c2ecf20Sopenharmony_ci
33468c2ecf20Sopenharmony_ci	*ret_vport = NULL;
33478c2ecf20Sopenharmony_ci
33488c2ecf20Sopenharmony_ci	if ( ! fci->f->vport_create)
33498c2ecf20Sopenharmony_ci		return -ENOENT;
33508c2ecf20Sopenharmony_ci
33518c2ecf20Sopenharmony_ci	size = (sizeof(struct fc_vport) + fci->f->dd_fcvport_size);
33528c2ecf20Sopenharmony_ci	vport = kzalloc(size, GFP_KERNEL);
33538c2ecf20Sopenharmony_ci	if (unlikely(!vport)) {
33548c2ecf20Sopenharmony_ci		printk(KERN_ERR "%s: allocation failure\n", __func__);
33558c2ecf20Sopenharmony_ci		return -ENOMEM;
33568c2ecf20Sopenharmony_ci	}
33578c2ecf20Sopenharmony_ci
33588c2ecf20Sopenharmony_ci	vport->vport_state = FC_VPORT_UNKNOWN;
33598c2ecf20Sopenharmony_ci	vport->vport_last_state = FC_VPORT_UNKNOWN;
33608c2ecf20Sopenharmony_ci	vport->node_name = ids->node_name;
33618c2ecf20Sopenharmony_ci	vport->port_name = ids->port_name;
33628c2ecf20Sopenharmony_ci	vport->roles = ids->roles;
33638c2ecf20Sopenharmony_ci	vport->vport_type = ids->vport_type;
33648c2ecf20Sopenharmony_ci	if (fci->f->dd_fcvport_size)
33658c2ecf20Sopenharmony_ci		vport->dd_data = &vport[1];
33668c2ecf20Sopenharmony_ci	vport->shost = shost;
33678c2ecf20Sopenharmony_ci	vport->channel = channel;
33688c2ecf20Sopenharmony_ci	vport->flags = FC_VPORT_CREATING;
33698c2ecf20Sopenharmony_ci	INIT_WORK(&vport->vport_delete_work, fc_vport_sched_delete);
33708c2ecf20Sopenharmony_ci
33718c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
33728c2ecf20Sopenharmony_ci
33738c2ecf20Sopenharmony_ci	if (fc_host->npiv_vports_inuse >= fc_host->max_npiv_vports) {
33748c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(shost->host_lock, flags);
33758c2ecf20Sopenharmony_ci		kfree(vport);
33768c2ecf20Sopenharmony_ci		return -ENOSPC;
33778c2ecf20Sopenharmony_ci	}
33788c2ecf20Sopenharmony_ci	fc_host->npiv_vports_inuse++;
33798c2ecf20Sopenharmony_ci	vport->number = fc_host->next_vport_number++;
33808c2ecf20Sopenharmony_ci	list_add_tail(&vport->peers, &fc_host->vports);
33818c2ecf20Sopenharmony_ci	scsi_host_get(shost);			/* for fc_host->vport list */
33828c2ecf20Sopenharmony_ci
33838c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci	dev = &vport->dev;
33868c2ecf20Sopenharmony_ci	device_initialize(dev);			/* takes self reference */
33878c2ecf20Sopenharmony_ci	dev->parent = get_device(pdev);		/* takes parent reference */
33888c2ecf20Sopenharmony_ci	dev->release = fc_vport_dev_release;
33898c2ecf20Sopenharmony_ci	dev_set_name(dev, "vport-%d:%d-%d",
33908c2ecf20Sopenharmony_ci		     shost->host_no, channel, vport->number);
33918c2ecf20Sopenharmony_ci	transport_setup_device(dev);
33928c2ecf20Sopenharmony_ci
33938c2ecf20Sopenharmony_ci	error = device_add(dev);
33948c2ecf20Sopenharmony_ci	if (error) {
33958c2ecf20Sopenharmony_ci		printk(KERN_ERR "FC Virtual Port device_add failed\n");
33968c2ecf20Sopenharmony_ci		goto delete_vport;
33978c2ecf20Sopenharmony_ci	}
33988c2ecf20Sopenharmony_ci	transport_add_device(dev);
33998c2ecf20Sopenharmony_ci	transport_configure_device(dev);
34008c2ecf20Sopenharmony_ci
34018c2ecf20Sopenharmony_ci	error = fci->f->vport_create(vport, ids->disable);
34028c2ecf20Sopenharmony_ci	if (error) {
34038c2ecf20Sopenharmony_ci		printk(KERN_ERR "FC Virtual Port LLDD Create failed\n");
34048c2ecf20Sopenharmony_ci		goto delete_vport_all;
34058c2ecf20Sopenharmony_ci	}
34068c2ecf20Sopenharmony_ci
34078c2ecf20Sopenharmony_ci	/*
34088c2ecf20Sopenharmony_ci	 * if the parent isn't the physical adapter's Scsi_Host, ensure
34098c2ecf20Sopenharmony_ci	 * the Scsi_Host at least contains a symlink to the vport.
34108c2ecf20Sopenharmony_ci	 */
34118c2ecf20Sopenharmony_ci	if (pdev != &shost->shost_gendev) {
34128c2ecf20Sopenharmony_ci		error = sysfs_create_link(&shost->shost_gendev.kobj,
34138c2ecf20Sopenharmony_ci				 &dev->kobj, dev_name(dev));
34148c2ecf20Sopenharmony_ci		if (error)
34158c2ecf20Sopenharmony_ci			printk(KERN_ERR
34168c2ecf20Sopenharmony_ci				"%s: Cannot create vport symlinks for "
34178c2ecf20Sopenharmony_ci				"%s, err=%d\n",
34188c2ecf20Sopenharmony_ci				__func__, dev_name(dev), error);
34198c2ecf20Sopenharmony_ci	}
34208c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
34218c2ecf20Sopenharmony_ci	vport->flags &= ~FC_VPORT_CREATING;
34228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
34238c2ecf20Sopenharmony_ci
34248c2ecf20Sopenharmony_ci	dev_printk(KERN_NOTICE, pdev,
34258c2ecf20Sopenharmony_ci			"%s created via shost%d channel %d\n", dev_name(dev),
34268c2ecf20Sopenharmony_ci			shost->host_no, channel);
34278c2ecf20Sopenharmony_ci
34288c2ecf20Sopenharmony_ci	*ret_vport = vport;
34298c2ecf20Sopenharmony_ci
34308c2ecf20Sopenharmony_ci	return 0;
34318c2ecf20Sopenharmony_ci
34328c2ecf20Sopenharmony_cidelete_vport_all:
34338c2ecf20Sopenharmony_ci	transport_remove_device(dev);
34348c2ecf20Sopenharmony_ci	device_del(dev);
34358c2ecf20Sopenharmony_cidelete_vport:
34368c2ecf20Sopenharmony_ci	transport_destroy_device(dev);
34378c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
34388c2ecf20Sopenharmony_ci	list_del(&vport->peers);
34398c2ecf20Sopenharmony_ci	scsi_host_put(shost);			/* for fc_host->vport list */
34408c2ecf20Sopenharmony_ci	fc_host->npiv_vports_inuse--;
34418c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
34428c2ecf20Sopenharmony_ci	put_device(dev->parent);
34438c2ecf20Sopenharmony_ci	kfree(vport);
34448c2ecf20Sopenharmony_ci
34458c2ecf20Sopenharmony_ci	return error;
34468c2ecf20Sopenharmony_ci}
34478c2ecf20Sopenharmony_ci
34488c2ecf20Sopenharmony_ci/**
34498c2ecf20Sopenharmony_ci * fc_vport_create - Admin App or LLDD requests creation of a vport
34508c2ecf20Sopenharmony_ci * @shost:	scsi host the virtual port is connected to.
34518c2ecf20Sopenharmony_ci * @channel:	channel on shost port connected to.
34528c2ecf20Sopenharmony_ci * @ids:	The world wide names, FC4 port roles, etc for
34538c2ecf20Sopenharmony_ci *              the virtual port.
34548c2ecf20Sopenharmony_ci *
34558c2ecf20Sopenharmony_ci * Notes:
34568c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
34578c2ecf20Sopenharmony_ci */
34588c2ecf20Sopenharmony_cistruct fc_vport *
34598c2ecf20Sopenharmony_cifc_vport_create(struct Scsi_Host *shost, int channel,
34608c2ecf20Sopenharmony_ci	struct fc_vport_identifiers *ids)
34618c2ecf20Sopenharmony_ci{
34628c2ecf20Sopenharmony_ci	int stat;
34638c2ecf20Sopenharmony_ci	struct fc_vport *vport;
34648c2ecf20Sopenharmony_ci
34658c2ecf20Sopenharmony_ci	stat = fc_vport_setup(shost, channel, &shost->shost_gendev,
34668c2ecf20Sopenharmony_ci		 ids, &vport);
34678c2ecf20Sopenharmony_ci	return stat ? NULL : vport;
34688c2ecf20Sopenharmony_ci}
34698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_vport_create);
34708c2ecf20Sopenharmony_ci
34718c2ecf20Sopenharmony_ci/**
34728c2ecf20Sopenharmony_ci * fc_vport_terminate - Admin App or LLDD requests termination of a vport
34738c2ecf20Sopenharmony_ci * @vport:	fc_vport to be terminated
34748c2ecf20Sopenharmony_ci *
34758c2ecf20Sopenharmony_ci * Calls the LLDD vport_delete() function, then deallocates and removes
34768c2ecf20Sopenharmony_ci * the vport from the shost and object tree.
34778c2ecf20Sopenharmony_ci *
34788c2ecf20Sopenharmony_ci * Notes:
34798c2ecf20Sopenharmony_ci *	This routine assumes no locks are held on entry.
34808c2ecf20Sopenharmony_ci */
34818c2ecf20Sopenharmony_ciint
34828c2ecf20Sopenharmony_cifc_vport_terminate(struct fc_vport *vport)
34838c2ecf20Sopenharmony_ci{
34848c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = vport_to_shost(vport);
34858c2ecf20Sopenharmony_ci	struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
34868c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
34878c2ecf20Sopenharmony_ci	struct device *dev = &vport->dev;
34888c2ecf20Sopenharmony_ci	unsigned long flags;
34898c2ecf20Sopenharmony_ci	int stat;
34908c2ecf20Sopenharmony_ci
34918c2ecf20Sopenharmony_ci	if (i->f->vport_delete)
34928c2ecf20Sopenharmony_ci		stat = i->f->vport_delete(vport);
34938c2ecf20Sopenharmony_ci	else
34948c2ecf20Sopenharmony_ci		stat = -ENOENT;
34958c2ecf20Sopenharmony_ci
34968c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
34978c2ecf20Sopenharmony_ci	vport->flags &= ~FC_VPORT_DELETING;
34988c2ecf20Sopenharmony_ci	if (!stat) {
34998c2ecf20Sopenharmony_ci		vport->flags |= FC_VPORT_DELETED;
35008c2ecf20Sopenharmony_ci		list_del(&vport->peers);
35018c2ecf20Sopenharmony_ci		fc_host->npiv_vports_inuse--;
35028c2ecf20Sopenharmony_ci		scsi_host_put(shost);		/* for fc_host->vport list */
35038c2ecf20Sopenharmony_ci	}
35048c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
35058c2ecf20Sopenharmony_ci
35068c2ecf20Sopenharmony_ci	if (stat)
35078c2ecf20Sopenharmony_ci		return stat;
35088c2ecf20Sopenharmony_ci
35098c2ecf20Sopenharmony_ci	if (dev->parent != &shost->shost_gendev)
35108c2ecf20Sopenharmony_ci		sysfs_remove_link(&shost->shost_gendev.kobj, dev_name(dev));
35118c2ecf20Sopenharmony_ci	transport_remove_device(dev);
35128c2ecf20Sopenharmony_ci	device_del(dev);
35138c2ecf20Sopenharmony_ci	transport_destroy_device(dev);
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	/*
35168c2ecf20Sopenharmony_ci	 * Removing our self-reference should mean our
35178c2ecf20Sopenharmony_ci	 * release function gets called, which will drop the remaining
35188c2ecf20Sopenharmony_ci	 * parent reference and free the data structure.
35198c2ecf20Sopenharmony_ci	 */
35208c2ecf20Sopenharmony_ci	put_device(dev);			/* for self-reference */
35218c2ecf20Sopenharmony_ci
35228c2ecf20Sopenharmony_ci	return 0; /* SUCCESS */
35238c2ecf20Sopenharmony_ci}
35248c2ecf20Sopenharmony_ciEXPORT_SYMBOL(fc_vport_terminate);
35258c2ecf20Sopenharmony_ci
35268c2ecf20Sopenharmony_ci/**
35278c2ecf20Sopenharmony_ci * fc_vport_sched_delete - workq-based delete request for a vport
35288c2ecf20Sopenharmony_ci * @work:	vport to be deleted.
35298c2ecf20Sopenharmony_ci */
35308c2ecf20Sopenharmony_cistatic void
35318c2ecf20Sopenharmony_cifc_vport_sched_delete(struct work_struct *work)
35328c2ecf20Sopenharmony_ci{
35338c2ecf20Sopenharmony_ci	struct fc_vport *vport =
35348c2ecf20Sopenharmony_ci		container_of(work, struct fc_vport, vport_delete_work);
35358c2ecf20Sopenharmony_ci	int stat;
35368c2ecf20Sopenharmony_ci
35378c2ecf20Sopenharmony_ci	stat = fc_vport_terminate(vport);
35388c2ecf20Sopenharmony_ci	if (stat)
35398c2ecf20Sopenharmony_ci		dev_printk(KERN_ERR, vport->dev.parent,
35408c2ecf20Sopenharmony_ci			"%s: %s could not be deleted created via "
35418c2ecf20Sopenharmony_ci			"shost%d channel %d - error %d\n", __func__,
35428c2ecf20Sopenharmony_ci			dev_name(&vport->dev), vport->shost->host_no,
35438c2ecf20Sopenharmony_ci			vport->channel, stat);
35448c2ecf20Sopenharmony_ci}
35458c2ecf20Sopenharmony_ci
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci/*
35488c2ecf20Sopenharmony_ci * BSG support
35498c2ecf20Sopenharmony_ci */
35508c2ecf20Sopenharmony_ci
35518c2ecf20Sopenharmony_ci/**
35528c2ecf20Sopenharmony_ci * fc_bsg_job_timeout - handler for when a bsg request timesout
35538c2ecf20Sopenharmony_ci * @req:	request that timed out
35548c2ecf20Sopenharmony_ci */
35558c2ecf20Sopenharmony_cistatic enum blk_eh_timer_return
35568c2ecf20Sopenharmony_cifc_bsg_job_timeout(struct request *req)
35578c2ecf20Sopenharmony_ci{
35588c2ecf20Sopenharmony_ci	struct bsg_job *job = blk_mq_rq_to_pdu(req);
35598c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = fc_bsg_to_shost(job);
35608c2ecf20Sopenharmony_ci	struct fc_rport *rport = fc_bsg_to_rport(job);
35618c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
35628c2ecf20Sopenharmony_ci	int err = 0, inflight = 0;
35638c2ecf20Sopenharmony_ci
35648c2ecf20Sopenharmony_ci	if (rport && rport->port_state == FC_PORTSTATE_BLOCKED)
35658c2ecf20Sopenharmony_ci		return BLK_EH_RESET_TIMER;
35668c2ecf20Sopenharmony_ci
35678c2ecf20Sopenharmony_ci	inflight = bsg_job_get(job);
35688c2ecf20Sopenharmony_ci
35698c2ecf20Sopenharmony_ci	if (inflight && i->f->bsg_timeout) {
35708c2ecf20Sopenharmony_ci		/* call LLDD to abort the i/o as it has timed out */
35718c2ecf20Sopenharmony_ci		err = i->f->bsg_timeout(job);
35728c2ecf20Sopenharmony_ci		if (err == -EAGAIN) {
35738c2ecf20Sopenharmony_ci			bsg_job_put(job);
35748c2ecf20Sopenharmony_ci			return BLK_EH_RESET_TIMER;
35758c2ecf20Sopenharmony_ci		} else if (err)
35768c2ecf20Sopenharmony_ci			printk(KERN_ERR "ERROR: FC BSG request timeout - LLD "
35778c2ecf20Sopenharmony_ci				"abort failed with status %d\n", err);
35788c2ecf20Sopenharmony_ci	}
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci	/* the blk_end_sync_io() doesn't check the error */
35818c2ecf20Sopenharmony_ci	if (inflight)
35828c2ecf20Sopenharmony_ci		blk_mq_end_request(req, BLK_STS_IOERR);
35838c2ecf20Sopenharmony_ci	return BLK_EH_DONE;
35848c2ecf20Sopenharmony_ci}
35858c2ecf20Sopenharmony_ci
35868c2ecf20Sopenharmony_ci/**
35878c2ecf20Sopenharmony_ci * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD
35888c2ecf20Sopenharmony_ci * @shost:	scsi host rport attached to
35898c2ecf20Sopenharmony_ci * @job:	bsg job to be processed
35908c2ecf20Sopenharmony_ci */
35918c2ecf20Sopenharmony_cistatic int fc_bsg_host_dispatch(struct Scsi_Host *shost, struct bsg_job *job)
35928c2ecf20Sopenharmony_ci{
35938c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
35948c2ecf20Sopenharmony_ci	struct fc_bsg_request *bsg_request = job->request;
35958c2ecf20Sopenharmony_ci	struct fc_bsg_reply *bsg_reply = job->reply;
35968c2ecf20Sopenharmony_ci	int cmdlen = sizeof(uint32_t);	/* start with length of msgcode */
35978c2ecf20Sopenharmony_ci	int ret;
35988c2ecf20Sopenharmony_ci
35998c2ecf20Sopenharmony_ci	/* check if we really have all the request data needed */
36008c2ecf20Sopenharmony_ci	if (job->request_len < cmdlen) {
36018c2ecf20Sopenharmony_ci		ret = -ENOMSG;
36028c2ecf20Sopenharmony_ci		goto fail_host_msg;
36038c2ecf20Sopenharmony_ci	}
36048c2ecf20Sopenharmony_ci
36058c2ecf20Sopenharmony_ci	/* Validate the host command */
36068c2ecf20Sopenharmony_ci	switch (bsg_request->msgcode) {
36078c2ecf20Sopenharmony_ci	case FC_BSG_HST_ADD_RPORT:
36088c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_host_add_rport);
36098c2ecf20Sopenharmony_ci		break;
36108c2ecf20Sopenharmony_ci
36118c2ecf20Sopenharmony_ci	case FC_BSG_HST_DEL_RPORT:
36128c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_host_del_rport);
36138c2ecf20Sopenharmony_ci		break;
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_ci	case FC_BSG_HST_ELS_NOLOGIN:
36168c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_host_els);
36178c2ecf20Sopenharmony_ci		/* there better be a xmt and rcv payloads */
36188c2ecf20Sopenharmony_ci		if ((!job->request_payload.payload_len) ||
36198c2ecf20Sopenharmony_ci		    (!job->reply_payload.payload_len)) {
36208c2ecf20Sopenharmony_ci			ret = -EINVAL;
36218c2ecf20Sopenharmony_ci			goto fail_host_msg;
36228c2ecf20Sopenharmony_ci		}
36238c2ecf20Sopenharmony_ci		break;
36248c2ecf20Sopenharmony_ci
36258c2ecf20Sopenharmony_ci	case FC_BSG_HST_CT:
36268c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_host_ct);
36278c2ecf20Sopenharmony_ci		/* there better be xmt and rcv payloads */
36288c2ecf20Sopenharmony_ci		if ((!job->request_payload.payload_len) ||
36298c2ecf20Sopenharmony_ci		    (!job->reply_payload.payload_len)) {
36308c2ecf20Sopenharmony_ci			ret = -EINVAL;
36318c2ecf20Sopenharmony_ci			goto fail_host_msg;
36328c2ecf20Sopenharmony_ci		}
36338c2ecf20Sopenharmony_ci		break;
36348c2ecf20Sopenharmony_ci
36358c2ecf20Sopenharmony_ci	case FC_BSG_HST_VENDOR:
36368c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_host_vendor);
36378c2ecf20Sopenharmony_ci		if ((shost->hostt->vendor_id == 0L) ||
36388c2ecf20Sopenharmony_ci		    (bsg_request->rqst_data.h_vendor.vendor_id !=
36398c2ecf20Sopenharmony_ci			shost->hostt->vendor_id)) {
36408c2ecf20Sopenharmony_ci			ret = -ESRCH;
36418c2ecf20Sopenharmony_ci			goto fail_host_msg;
36428c2ecf20Sopenharmony_ci		}
36438c2ecf20Sopenharmony_ci		break;
36448c2ecf20Sopenharmony_ci
36458c2ecf20Sopenharmony_ci	default:
36468c2ecf20Sopenharmony_ci		ret = -EBADR;
36478c2ecf20Sopenharmony_ci		goto fail_host_msg;
36488c2ecf20Sopenharmony_ci	}
36498c2ecf20Sopenharmony_ci
36508c2ecf20Sopenharmony_ci	ret = i->f->bsg_request(job);
36518c2ecf20Sopenharmony_ci	if (!ret)
36528c2ecf20Sopenharmony_ci		return 0;
36538c2ecf20Sopenharmony_ci
36548c2ecf20Sopenharmony_cifail_host_msg:
36558c2ecf20Sopenharmony_ci	/* return the errno failure code as the only status */
36568c2ecf20Sopenharmony_ci	BUG_ON(job->reply_len < sizeof(uint32_t));
36578c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
36588c2ecf20Sopenharmony_ci	bsg_reply->result = ret;
36598c2ecf20Sopenharmony_ci	job->reply_len = sizeof(uint32_t);
36608c2ecf20Sopenharmony_ci	bsg_job_done(job, bsg_reply->result,
36618c2ecf20Sopenharmony_ci		       bsg_reply->reply_payload_rcv_len);
36628c2ecf20Sopenharmony_ci	return 0;
36638c2ecf20Sopenharmony_ci}
36648c2ecf20Sopenharmony_ci
36658c2ecf20Sopenharmony_ci
36668c2ecf20Sopenharmony_ci/*
36678c2ecf20Sopenharmony_ci * fc_bsg_goose_queue - restart rport queue in case it was stopped
36688c2ecf20Sopenharmony_ci * @rport:	rport to be restarted
36698c2ecf20Sopenharmony_ci */
36708c2ecf20Sopenharmony_cistatic void
36718c2ecf20Sopenharmony_cifc_bsg_goose_queue(struct fc_rport *rport)
36728c2ecf20Sopenharmony_ci{
36738c2ecf20Sopenharmony_ci	struct request_queue *q = rport->rqst_q;
36748c2ecf20Sopenharmony_ci
36758c2ecf20Sopenharmony_ci	if (q)
36768c2ecf20Sopenharmony_ci		blk_mq_run_hw_queues(q, true);
36778c2ecf20Sopenharmony_ci}
36788c2ecf20Sopenharmony_ci
36798c2ecf20Sopenharmony_ci/**
36808c2ecf20Sopenharmony_ci * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD
36818c2ecf20Sopenharmony_ci * @shost:	scsi host rport attached to
36828c2ecf20Sopenharmony_ci * @job:	bsg job to be processed
36838c2ecf20Sopenharmony_ci */
36848c2ecf20Sopenharmony_cistatic int fc_bsg_rport_dispatch(struct Scsi_Host *shost, struct bsg_job *job)
36858c2ecf20Sopenharmony_ci{
36868c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
36878c2ecf20Sopenharmony_ci	struct fc_bsg_request *bsg_request = job->request;
36888c2ecf20Sopenharmony_ci	struct fc_bsg_reply *bsg_reply = job->reply;
36898c2ecf20Sopenharmony_ci	int cmdlen = sizeof(uint32_t);	/* start with length of msgcode */
36908c2ecf20Sopenharmony_ci	int ret;
36918c2ecf20Sopenharmony_ci
36928c2ecf20Sopenharmony_ci	/* check if we really have all the request data needed */
36938c2ecf20Sopenharmony_ci	if (job->request_len < cmdlen) {
36948c2ecf20Sopenharmony_ci		ret = -ENOMSG;
36958c2ecf20Sopenharmony_ci		goto fail_rport_msg;
36968c2ecf20Sopenharmony_ci	}
36978c2ecf20Sopenharmony_ci
36988c2ecf20Sopenharmony_ci	/* Validate the rport command */
36998c2ecf20Sopenharmony_ci	switch (bsg_request->msgcode) {
37008c2ecf20Sopenharmony_ci	case FC_BSG_RPT_ELS:
37018c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_rport_els);
37028c2ecf20Sopenharmony_ci		goto check_bidi;
37038c2ecf20Sopenharmony_ci
37048c2ecf20Sopenharmony_ci	case FC_BSG_RPT_CT:
37058c2ecf20Sopenharmony_ci		cmdlen += sizeof(struct fc_bsg_rport_ct);
37068c2ecf20Sopenharmony_cicheck_bidi:
37078c2ecf20Sopenharmony_ci		/* there better be xmt and rcv payloads */
37088c2ecf20Sopenharmony_ci		if ((!job->request_payload.payload_len) ||
37098c2ecf20Sopenharmony_ci		    (!job->reply_payload.payload_len)) {
37108c2ecf20Sopenharmony_ci			ret = -EINVAL;
37118c2ecf20Sopenharmony_ci			goto fail_rport_msg;
37128c2ecf20Sopenharmony_ci		}
37138c2ecf20Sopenharmony_ci		break;
37148c2ecf20Sopenharmony_ci	default:
37158c2ecf20Sopenharmony_ci		ret = -EBADR;
37168c2ecf20Sopenharmony_ci		goto fail_rport_msg;
37178c2ecf20Sopenharmony_ci	}
37188c2ecf20Sopenharmony_ci
37198c2ecf20Sopenharmony_ci	ret = i->f->bsg_request(job);
37208c2ecf20Sopenharmony_ci	if (!ret)
37218c2ecf20Sopenharmony_ci		return 0;
37228c2ecf20Sopenharmony_ci
37238c2ecf20Sopenharmony_cifail_rport_msg:
37248c2ecf20Sopenharmony_ci	/* return the errno failure code as the only status */
37258c2ecf20Sopenharmony_ci	BUG_ON(job->reply_len < sizeof(uint32_t));
37268c2ecf20Sopenharmony_ci	bsg_reply->reply_payload_rcv_len = 0;
37278c2ecf20Sopenharmony_ci	bsg_reply->result = ret;
37288c2ecf20Sopenharmony_ci	job->reply_len = sizeof(uint32_t);
37298c2ecf20Sopenharmony_ci	bsg_job_done(job, bsg_reply->result,
37308c2ecf20Sopenharmony_ci		       bsg_reply->reply_payload_rcv_len);
37318c2ecf20Sopenharmony_ci	return 0;
37328c2ecf20Sopenharmony_ci}
37338c2ecf20Sopenharmony_ci
37348c2ecf20Sopenharmony_cistatic int fc_bsg_dispatch(struct bsg_job *job)
37358c2ecf20Sopenharmony_ci{
37368c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = fc_bsg_to_shost(job);
37378c2ecf20Sopenharmony_ci
37388c2ecf20Sopenharmony_ci	if (scsi_is_fc_rport(job->dev))
37398c2ecf20Sopenharmony_ci		return fc_bsg_rport_dispatch(shost, job);
37408c2ecf20Sopenharmony_ci	else
37418c2ecf20Sopenharmony_ci		return fc_bsg_host_dispatch(shost, job);
37428c2ecf20Sopenharmony_ci}
37438c2ecf20Sopenharmony_ci
37448c2ecf20Sopenharmony_cistatic blk_status_t fc_bsg_rport_prep(struct fc_rport *rport)
37458c2ecf20Sopenharmony_ci{
37468c2ecf20Sopenharmony_ci	if (rport->port_state == FC_PORTSTATE_BLOCKED &&
37478c2ecf20Sopenharmony_ci	    !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
37488c2ecf20Sopenharmony_ci		return BLK_STS_RESOURCE;
37498c2ecf20Sopenharmony_ci
37508c2ecf20Sopenharmony_ci	if (rport->port_state != FC_PORTSTATE_ONLINE)
37518c2ecf20Sopenharmony_ci		return BLK_STS_IOERR;
37528c2ecf20Sopenharmony_ci
37538c2ecf20Sopenharmony_ci	return BLK_STS_OK;
37548c2ecf20Sopenharmony_ci}
37558c2ecf20Sopenharmony_ci
37568c2ecf20Sopenharmony_ci
37578c2ecf20Sopenharmony_cistatic int fc_bsg_dispatch_prep(struct bsg_job *job)
37588c2ecf20Sopenharmony_ci{
37598c2ecf20Sopenharmony_ci	struct fc_rport *rport = fc_bsg_to_rport(job);
37608c2ecf20Sopenharmony_ci	blk_status_t ret;
37618c2ecf20Sopenharmony_ci
37628c2ecf20Sopenharmony_ci	ret = fc_bsg_rport_prep(rport);
37638c2ecf20Sopenharmony_ci	switch (ret) {
37648c2ecf20Sopenharmony_ci	case BLK_STS_OK:
37658c2ecf20Sopenharmony_ci		break;
37668c2ecf20Sopenharmony_ci	case BLK_STS_RESOURCE:
37678c2ecf20Sopenharmony_ci		return -EAGAIN;
37688c2ecf20Sopenharmony_ci	default:
37698c2ecf20Sopenharmony_ci		return -EIO;
37708c2ecf20Sopenharmony_ci	}
37718c2ecf20Sopenharmony_ci
37728c2ecf20Sopenharmony_ci	return fc_bsg_dispatch(job);
37738c2ecf20Sopenharmony_ci}
37748c2ecf20Sopenharmony_ci
37758c2ecf20Sopenharmony_ci/**
37768c2ecf20Sopenharmony_ci * fc_bsg_hostadd - Create and add the bsg hooks so we can receive requests
37778c2ecf20Sopenharmony_ci * @shost:	shost for fc_host
37788c2ecf20Sopenharmony_ci * @fc_host:	fc_host adding the structures to
37798c2ecf20Sopenharmony_ci */
37808c2ecf20Sopenharmony_cistatic int
37818c2ecf20Sopenharmony_cifc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host)
37828c2ecf20Sopenharmony_ci{
37838c2ecf20Sopenharmony_ci	struct device *dev = &shost->shost_gendev;
37848c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
37858c2ecf20Sopenharmony_ci	struct request_queue *q;
37868c2ecf20Sopenharmony_ci	char bsg_name[20];
37878c2ecf20Sopenharmony_ci
37888c2ecf20Sopenharmony_ci	fc_host->rqst_q = NULL;
37898c2ecf20Sopenharmony_ci
37908c2ecf20Sopenharmony_ci	if (!i->f->bsg_request)
37918c2ecf20Sopenharmony_ci		return -ENOTSUPP;
37928c2ecf20Sopenharmony_ci
37938c2ecf20Sopenharmony_ci	snprintf(bsg_name, sizeof(bsg_name),
37948c2ecf20Sopenharmony_ci		 "fc_host%d", shost->host_no);
37958c2ecf20Sopenharmony_ci
37968c2ecf20Sopenharmony_ci	q = bsg_setup_queue(dev, bsg_name, fc_bsg_dispatch, fc_bsg_job_timeout,
37978c2ecf20Sopenharmony_ci				i->f->dd_bsg_size);
37988c2ecf20Sopenharmony_ci	if (IS_ERR(q)) {
37998c2ecf20Sopenharmony_ci		dev_err(dev,
38008c2ecf20Sopenharmony_ci			"fc_host%d: bsg interface failed to initialize - setup queue\n",
38018c2ecf20Sopenharmony_ci			shost->host_no);
38028c2ecf20Sopenharmony_ci		return PTR_ERR(q);
38038c2ecf20Sopenharmony_ci	}
38048c2ecf20Sopenharmony_ci	__scsi_init_queue(shost, q);
38058c2ecf20Sopenharmony_ci	blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT);
38068c2ecf20Sopenharmony_ci	fc_host->rqst_q = q;
38078c2ecf20Sopenharmony_ci	return 0;
38088c2ecf20Sopenharmony_ci}
38098c2ecf20Sopenharmony_ci
38108c2ecf20Sopenharmony_ci/**
38118c2ecf20Sopenharmony_ci * fc_bsg_rportadd - Create and add the bsg hooks so we can receive requests
38128c2ecf20Sopenharmony_ci * @shost:	shost that rport is attached to
38138c2ecf20Sopenharmony_ci * @rport:	rport that the bsg hooks are being attached to
38148c2ecf20Sopenharmony_ci */
38158c2ecf20Sopenharmony_cistatic int
38168c2ecf20Sopenharmony_cifc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport)
38178c2ecf20Sopenharmony_ci{
38188c2ecf20Sopenharmony_ci	struct device *dev = &rport->dev;
38198c2ecf20Sopenharmony_ci	struct fc_internal *i = to_fc_internal(shost->transportt);
38208c2ecf20Sopenharmony_ci	struct request_queue *q;
38218c2ecf20Sopenharmony_ci
38228c2ecf20Sopenharmony_ci	rport->rqst_q = NULL;
38238c2ecf20Sopenharmony_ci
38248c2ecf20Sopenharmony_ci	if (!i->f->bsg_request)
38258c2ecf20Sopenharmony_ci		return -ENOTSUPP;
38268c2ecf20Sopenharmony_ci
38278c2ecf20Sopenharmony_ci	q = bsg_setup_queue(dev, dev_name(dev), fc_bsg_dispatch_prep,
38288c2ecf20Sopenharmony_ci				fc_bsg_job_timeout, i->f->dd_bsg_size);
38298c2ecf20Sopenharmony_ci	if (IS_ERR(q)) {
38308c2ecf20Sopenharmony_ci		dev_err(dev, "failed to setup bsg queue\n");
38318c2ecf20Sopenharmony_ci		return PTR_ERR(q);
38328c2ecf20Sopenharmony_ci	}
38338c2ecf20Sopenharmony_ci	__scsi_init_queue(shost, q);
38348c2ecf20Sopenharmony_ci	blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT);
38358c2ecf20Sopenharmony_ci	rport->rqst_q = q;
38368c2ecf20Sopenharmony_ci	return 0;
38378c2ecf20Sopenharmony_ci}
38388c2ecf20Sopenharmony_ci
38398c2ecf20Sopenharmony_ci
38408c2ecf20Sopenharmony_ci/**
38418c2ecf20Sopenharmony_ci * fc_bsg_remove - Deletes the bsg hooks on fchosts/rports
38428c2ecf20Sopenharmony_ci * @q:	the request_queue that is to be torn down.
38438c2ecf20Sopenharmony_ci *
38448c2ecf20Sopenharmony_ci * Notes:
38458c2ecf20Sopenharmony_ci *   Before unregistering the queue empty any requests that are blocked
38468c2ecf20Sopenharmony_ci *
38478c2ecf20Sopenharmony_ci *
38488c2ecf20Sopenharmony_ci */
38498c2ecf20Sopenharmony_cistatic void
38508c2ecf20Sopenharmony_cifc_bsg_remove(struct request_queue *q)
38518c2ecf20Sopenharmony_ci{
38528c2ecf20Sopenharmony_ci	bsg_remove_queue(q);
38538c2ecf20Sopenharmony_ci}
38548c2ecf20Sopenharmony_ci
38558c2ecf20Sopenharmony_ci
38568c2ecf20Sopenharmony_ci/* Original Author:  Martin Hicks */
38578c2ecf20Sopenharmony_ciMODULE_AUTHOR("James Smart");
38588c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("FC Transport Attributes");
38598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
38608c2ecf20Sopenharmony_ci
38618c2ecf20Sopenharmony_cimodule_init(fc_transport_init);
38628c2ecf20Sopenharmony_cimodule_exit(fc_transport_exit);
3863