Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  scsi_transport_fc.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 *  FiberChannel transport specific attributes exported to sysfs.
 *
 *  Copyright (c) 2003 Silicon Graphics, Inc.  All rights reserved.
 *  Copyright (C) 2004-2007   James Smart, Emulex Corporation
 *    Rewrite for host, target, device, and remote port attributes,
 *    statistics, and service functions...
 *    Add vports, etc
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/bsg-lib.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
#include <scsi/scsi_cmnd.h>
#include <net/netlink.h>
#include <scsi/scsi_netlink_fc.h>
#include <scsi/scsi_bsg_fc.h>
#include <uapi/scsi/fc/fc_els.h>
#include "scsi_priv.h"

static int fc_queue_work(struct Scsi_Host *, struct work_struct *);
static void fc_vport_sched_delete(struct work_struct *work);
static int fc_vport_setup(struct Scsi_Host *shost, int channel,
 struct device *pdev, struct fc_vport_identifiers  *ids,
 struct fc_vport **vport);
static int fc_bsg_hostadd(struct Scsi_Host *, struct fc_host_attrs *);
static int fc_bsg_rportadd(struct Scsi_Host *, struct fc_rport *);
static void fc_bsg_remove(struct request_queue *);
static void fc_bsg_goose_queue(struct fc_rport *);
static void fc_li_stats_update(u16 event_type,
          struct fc_fpin_stats *stats);
static void fc_delivery_stats_update(u32 reason_code,
         struct fc_fpin_stats *stats);
static void fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats);

/*
 * Module Parameters
 */


/*
 * dev_loss_tmo: the default number of seconds that the FC transport
 *   should insulate the loss of a remote port.
 *   The maximum will be capped by the value of SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
 */

static unsigned int fc_dev_loss_tmo = 60;  /* seconds */

module_param_named(dev_loss_tmo, fc_dev_loss_tmo, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(dev_loss_tmo,
   "Maximum number of seconds that the FC transport should"
   " insulate the loss of a remote port. Once this value is"
   " exceeded, the scsi target is removed. Value should be"
   " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if"
   " fast_io_fail_tmo is not set.");

/*
 * Redefine so that we can have same named attributes in the
 * sdev/starget/host objects.
 */

#define FC_DEVICE_ATTR(_prefix,_name,_mode,_show,_store)  \
struct device_attribute device_attr_##_prefix##_##_name =  \
 __ATTR(_name,_mode,_show,_store)

#define fc_enum_name_search(title, table_type, table)   \
static const char *get_fc_##title##_name(enum table_type table_key) \
{         \
 int i;        \
 char *name = NULL;      \
         \
 for (i = 0; i < ARRAY_SIZE(table); i++) {   \
  if (table[i].value == table_key) {   \
   name = table[i].name;    \
   break;      \
  }       \
 }        \
 return name;       \
}

#define fc_enum_name_match(title, table_type, table)   \
static int get_fc_##title##_match(const char *table_key,  \
  enum table_type *value)     \
{         \
 int i;        \
         \
 for (i = 0; i < ARRAY_SIZE(table); i++) {   \
  if (strncmp(table_key, table[i].name,   \
    table[i].matchlen) == 0) {  \
   *value = table[i].value;   \
   return 0; /* success */ \
  }       \
 }        \
 return 1; /* failure */ \
}


/* Convert fc_port_type values to ascii string name */
static struct {
 enum fc_port_type value;
 char   *name;
} fc_port_type_names[] = {
 { FC_PORTTYPE_UNKNOWN,  "Unknown" },
 { FC_PORTTYPE_OTHER,  "Other" },
 { FC_PORTTYPE_NOTPRESENT, "Not Present" },
 { FC_PORTTYPE_NPORT, "NPort (fabric via point-to-point)" },
 { FC_PORTTYPE_NLPORT, "NLPort (fabric via loop)" },
 { FC_PORTTYPE_LPORT, "LPort (private loop)" },
 { FC_PORTTYPE_PTP, "Point-To-Point (direct nport connection)" },
 { FC_PORTTYPE_NPIV,  "NPIV VPORT" },
};
fc_enum_name_search(port_type, fc_port_type, fc_port_type_names)
#define FC_PORTTYPE_MAX_NAMELEN  50

/* Reuse fc_port_type enum function for vport_type */
#define get_fc_vport_type_name get_fc_port_type_name


/* Convert fc_host_event_code values to ascii string name */
static const struct {
 enum fc_host_event_code  value;
 char    *name;
} fc_host_event_code_names[] = {
 { FCH_EVT_LIP,   "lip" },
 { FCH_EVT_LINKUP,  "link_up" },
 { FCH_EVT_LINKDOWN,  "link_down" },
 { FCH_EVT_LIPRESET,  "lip_reset" },
 { FCH_EVT_RSCN,   "rscn" },
 { FCH_EVT_ADAPTER_CHANGE, "adapter_chg" },
 { FCH_EVT_PORT_UNKNOWN,  "port_unknown" },
 { FCH_EVT_PORT_ONLINE,  "port_online" },
 { FCH_EVT_PORT_OFFLINE,  "port_offline" },
 { FCH_EVT_PORT_FABRIC,  "port_fabric" },
 { FCH_EVT_LINK_UNKNOWN,  "link_unknown" },
 { FCH_EVT_LINK_FPIN,  "link_FPIN" },
 { FCH_EVT_LINK_FPIN_ACK, "link_FPIN_ACK" },
 { FCH_EVT_VENDOR_UNIQUE, "vendor_unique" },
};
fc_enum_name_search(host_event_code, fc_host_event_code,
  fc_host_event_code_names)
#define FC_HOST_EVENT_CODE_MAX_NAMELEN 30


/* Convert fc_port_state values to ascii string name */
static struct {
 enum fc_port_state value;
 char   *name;
 int   matchlen;
} fc_port_state_names[] = {
 { FC_PORTSTATE_UNKNOWN,  "Unknown", 7},
 { FC_PORTSTATE_NOTPRESENT, "Not Present", 11 },
 { FC_PORTSTATE_ONLINE,  "Online", 6 },
 { FC_PORTSTATE_OFFLINE,  "Offline", 7 },
 { FC_PORTSTATE_BLOCKED,  "Blocked", 7 },
 { FC_PORTSTATE_BYPASSED, "Bypassed", 8 },
 { FC_PORTSTATE_DIAGNOSTICS, "Diagnostics", 11 },
 { FC_PORTSTATE_LINKDOWN, "Linkdown", 8 },
 { FC_PORTSTATE_ERROR,  "Error", 5 },
 { FC_PORTSTATE_LOOPBACK, "Loopback", 8 },
 { FC_PORTSTATE_DELETED,  "Deleted", 7 },
 { FC_PORTSTATE_MARGINAL, "Marginal", 8 },
};
fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
fc_enum_name_match(port_state, fc_port_state, fc_port_state_names)
#define FC_PORTSTATE_MAX_NAMELEN 20


/* Convert fc_vport_state values to ascii string name */
static struct {
 enum fc_vport_state value;
 char   *name;
} fc_vport_state_names[] = {
 { FC_VPORT_UNKNOWN,  "Unknown" },
 { FC_VPORT_ACTIVE,  "Active" },
 { FC_VPORT_DISABLED,  "Disabled" },
 { FC_VPORT_LINKDOWN,  "Linkdown" },
 { FC_VPORT_INITIALIZING, "Initializing" },
 { FC_VPORT_NO_FABRIC_SUPP, "No Fabric Support" },
 { FC_VPORT_NO_FABRIC_RSCS, "No Fabric Resources" },
 { FC_VPORT_FABRIC_LOGOUT, "Fabric Logout" },
 { FC_VPORT_FABRIC_REJ_WWN, "Fabric Rejected WWN" },
 { FC_VPORT_FAILED,  "VPort Failed" },
};
fc_enum_name_search(vport_state, fc_vport_state, fc_vport_state_names)
#define FC_VPORTSTATE_MAX_NAMELEN 24

/* Reuse fc_vport_state enum function for vport_last_state */
#define get_fc_vport_last_state_name get_fc_vport_state_name


/* Convert fc_tgtid_binding_type values to ascii string name */
static const struct {
 enum fc_tgtid_binding_type value;
 char    *name;
 int    matchlen;
} fc_tgtid_binding_type_names[] = {
 { FC_TGTID_BIND_NONE, "none", 4 },
 { FC_TGTID_BIND_BY_WWPN, "wwpn (World Wide Port Name)", 4 },
 { FC_TGTID_BIND_BY_WWNN, "wwnn (World Wide Node Name)", 4 },
 { FC_TGTID_BIND_BY_ID, "port_id (FC Address)", 7 },
};
fc_enum_name_search(tgtid_bind_type, fc_tgtid_binding_type,
  fc_tgtid_binding_type_names)
fc_enum_name_match(tgtid_bind_type, fc_tgtid_binding_type,
  fc_tgtid_binding_type_names)
#define FC_BINDTYPE_MAX_NAMELEN 30


#define fc_bitfield_name_search(title, table)   \
static ssize_t       \
get_fc_##title##_names(u32 table_key, char *buf)  \
{        \
 char *prefix = "";     \
 ssize_t len = 0;     \
 int i;       \
        \
 for (i = 0; i < ARRAY_SIZE(table); i++) {  \
  if (table[i].value & table_key) {  \
   len += sprintf(buf + len, "%s%s", \
    prefix, table[i].name);  \
   prefix = ", ";    \
  }      \
 }       \
 len += sprintf(buf + len, "\n");   \
 return len;      \
}


/* Convert FC_COS bit values to ascii string name */
static const struct {
 u32    value;
 char   *name;
} fc_cos_names[] = {
 { FC_COS_CLASS1, "Class 1" },
 { FC_COS_CLASS2, "Class 2" },
 { FC_COS_CLASS3, "Class 3" },
 { FC_COS_CLASS4, "Class 4" },
 { FC_COS_CLASS6, "Class 6" },
};
fc_bitfield_name_search(cos, fc_cos_names)


/* Convert FC_PORTSPEED bit values to ascii string name */
static const struct {
 u32    value;
 char   *name;
} fc_port_speed_names[] = {
 { FC_PORTSPEED_1GBIT,  "1 Gbit" },
 { FC_PORTSPEED_2GBIT,  "2 Gbit" },
 { FC_PORTSPEED_4GBIT,  "4 Gbit" },
 { FC_PORTSPEED_10GBIT,  "10 Gbit" },
 { FC_PORTSPEED_8GBIT,  "8 Gbit" },
 { FC_PORTSPEED_16GBIT,  "16 Gbit" },
 { FC_PORTSPEED_32GBIT,  "32 Gbit" },
 { FC_PORTSPEED_20GBIT,  "20 Gbit" },
 { FC_PORTSPEED_40GBIT,  "40 Gbit" },
 { FC_PORTSPEED_50GBIT,  "50 Gbit" },
 { FC_PORTSPEED_100GBIT,  "100 Gbit" },
 { FC_PORTSPEED_25GBIT,  "25 Gbit" },
 { FC_PORTSPEED_64GBIT,  "64 Gbit" },
 { FC_PORTSPEED_128GBIT,  "128 Gbit" },
 { FC_PORTSPEED_256GBIT,  "256 Gbit" },
 { FC_PORTSPEED_NOT_NEGOTIATED, "Not Negotiated" },
};
fc_bitfield_name_search(port_speed, fc_port_speed_names)


static int
show_fc_fc4s (char *buf, u8 *fc4_list)
{
 int i, len=0;

 for (i = 0; i < FC_FC4_LIST_SIZE; i++, fc4_list++)
  len += sprintf(buf + len , "0x%02x ", *fc4_list);
 len += sprintf(buf + len, "\n");
 return len;
}


/* Convert FC_PORT_ROLE bit values to ascii string name */
static const struct {
 u32    value;
 char   *name;
} fc_port_role_names[] = {
 { FC_PORT_ROLE_FCP_TARGET,  "FCP Target" },
 { FC_PORT_ROLE_FCP_INITIATOR,  "FCP Initiator" },
 { FC_PORT_ROLE_IP_PORT,   "IP Port" },
 { FC_PORT_ROLE_FCP_DUMMY_INITIATOR, "FCP Dummy Initiator" },
 { FC_PORT_ROLE_NVME_INITIATOR,  "NVMe Initiator" },
 { FC_PORT_ROLE_NVME_TARGET,  "NVMe Target" },
 { FC_PORT_ROLE_NVME_DISCOVERY,  "NVMe Discovery" },
};
fc_bitfield_name_search(port_roles, fc_port_role_names)

/*
 * Define roles that are specific to port_id. Values are relative to ROLE_MASK.
 */

#define FC_WELLKNOWN_PORTID_MASK 0xfffff0
#define FC_WELLKNOWN_ROLE_MASK   0x00000f
#define FC_FPORT_PORTID   0x00000e
#define FC_FABCTLR_PORTID  0x00000d
#define FC_DIRSRVR_PORTID  0x00000c
#define FC_TIMESRVR_PORTID  0x00000b
#define FC_MGMTSRVR_PORTID  0x00000a


static void fc_timeout_deleted_rport(struct work_struct *work);
static void fc_timeout_fail_rport_io(struct work_struct *work);
static void fc_scsi_scan_rport(struct work_struct *work);

/*
 * Attribute counts pre object type...
 * Increase these values if you add attributes
 */

#define FC_STARGET_NUM_ATTRS  3
#define FC_RPORT_NUM_ATTRS 10
#define FC_VPORT_NUM_ATTRS 9
#define FC_HOST_NUM_ATTRS 29

struct fc_internal {
 struct scsi_transport_template t;
 struct fc_function_template *f;

 /*
 * For attributes : each object has :
 *   An array of the actual attributes structures
 *   An array of null-terminated pointers to the attribute
 *     structures - used for mid-layer interaction.
 *
 * The attribute containers for the starget and host are are
 * part of the midlayer. As the remote port is specific to the
 * fc transport, we must provide the attribute container.
 */

 struct device_attribute private_starget_attrs[
       FC_STARGET_NUM_ATTRS];
 struct device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1];

 struct device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];
 struct device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];

 struct transport_container rport_attr_cont;
 struct device_attribute private_rport_attrs[FC_RPORT_NUM_ATTRS];
 struct device_attribute *rport_attrs[FC_RPORT_NUM_ATTRS + 1];

 struct transport_container vport_attr_cont;
 struct device_attribute private_vport_attrs[FC_VPORT_NUM_ATTRS];
 struct device_attribute *vport_attrs[FC_VPORT_NUM_ATTRS + 1];
};

#define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t)

static int fc_target_setup(struct transport_container *tc, struct device *dev,
      struct device *cdev)
{
 struct scsi_target *starget = to_scsi_target(dev);
 struct fc_rport *rport = starget_to_rport(starget);

 /*
 * if parent is remote port, use values from remote port.
 * Otherwise, this host uses the fc_transport, but not the
 * remote port interface. As such, initialize to known non-values.
 */

 if (rport) {
  fc_starget_node_name(starget) = rport->node_name;
  fc_starget_port_name(starget) = rport->port_name;
  fc_starget_port_id(starget) = rport->port_id;
 } else {
  fc_starget_node_name(starget) = -1;
  fc_starget_port_name(starget) = -1;
  fc_starget_port_id(starget) = -1;
 }

 return 0;
}

static DECLARE_TRANSPORT_CLASS(fc_transport_class,
          "fc_transport",
          fc_target_setup,
          NULL,
          NULL);

static int fc_host_setup(struct transport_container *tc, struct device *dev,
    struct device *cdev)
{
 struct Scsi_Host *shost = dev_to_shost(dev);
 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);

 /*
 * Set default values easily detected by the midlayer as
 * failure cases.  The scsi lldd is responsible for initializing
 * all transport attributes to valid values per host.
 */

 fc_host->node_name = -1;
 fc_host->port_name = -1;
 fc_host->permanent_port_name = -1;
 fc_host->supported_classes = FC_COS_UNSPECIFIED;
 memset(fc_host->supported_fc4s, 0,
  sizeof(fc_host->supported_fc4s));
 fc_host->supported_speeds = FC_PORTSPEED_UNKNOWN;
 fc_host->maxframe_size = -1;
 fc_host->max_npiv_vports = 0;
 memset(fc_host->serial_number, 0,
  sizeof(fc_host->serial_number));
 memset(fc_host->manufacturer, 0,
  sizeof(fc_host->manufacturer));
 memset(fc_host->model, 0,
  sizeof(fc_host->model));
 memset(fc_host->model_description, 0,
  sizeof(fc_host->model_description));
 memset(fc_host->hardware_version, 0,
  sizeof(fc_host->hardware_version));
 memset(fc_host->driver_version, 0,
  sizeof(fc_host->driver_version));
 memset(fc_host->firmware_version, 0,
  sizeof(fc_host->firmware_version));
 memset(fc_host->optionrom_version, 0,
  sizeof(fc_host->optionrom_version));

 fc_host->port_id = -1;
 fc_host->port_type = FC_PORTTYPE_UNKNOWN;
 fc_host->port_state = FC_PORTSTATE_UNKNOWN;
 memset(fc_host->active_fc4s, 0,
  sizeof(fc_host->active_fc4s));
 fc_host->speed = FC_PORTSPEED_UNKNOWN;
 fc_host->fabric_name = -1;
 memset(fc_host->symbolic_name, 0, sizeof(fc_host->symbolic_name));
 memset(fc_host->system_hostname, 0, sizeof(fc_host->system_hostname));
 memset(&fc_host->fpin_stats, 0, sizeof(fc_host->fpin_stats));

 fc_host->tgtid_bind_type = FC_TGTID_BIND_BY_WWPN;

 INIT_LIST_HEAD(&fc_host->rports);
 INIT_LIST_HEAD(&fc_host->rport_bindings);
 INIT_LIST_HEAD(&fc_host->vports);
 fc_host->next_rport_number = 0;
 fc_host->next_target_id = 0;
 fc_host->next_vport_number = 0;
 fc_host->npiv_vports_inuse = 0;

 fc_host->work_q = alloc_workqueue("fc_wq_%d", 0, 0, shost->host_no);
 if (!fc_host->work_q)
  return -ENOMEM;

 fc_host->dev_loss_tmo = fc_dev_loss_tmo;

 fc_bsg_hostadd(shost, fc_host);
 /* ignore any bsg add error - we just can't do sgio */

 return 0;
}

static int fc_host_remove(struct transport_container *tc, struct device *dev,
    struct device *cdev)
{
 struct Scsi_Host *shost = dev_to_shost(dev);
 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);

 fc_bsg_remove(fc_host->rqst_q);
 return 0;
}

static DECLARE_TRANSPORT_CLASS(fc_host_class,
          "fc_host",
          fc_host_setup,
          fc_host_remove,
          NULL);

/*
 * Setup and Remove actions for remote ports are handled
 * in the service functions below.
 */

static DECLARE_TRANSPORT_CLASS(fc_rport_class,
          "fc_remote_ports",
          NULL,
          NULL,
          NULL);

/*
 * Setup and Remove actions for virtual ports are handled
 * in the service functions below.
 */

static DECLARE_TRANSPORT_CLASS(fc_vport_class,
          "fc_vports",
          NULL,
          NULL,
          NULL);

/*
 * Netlink Infrastructure
 */


static atomic_t fc_event_seq;

/**
 * fc_get_event_number - Obtain the next sequential FC event number
 *
 * Notes:
 *   We could have inlined this, but it would have required fc_event_seq to
 *   be exposed. For now, live with the subroutine call.
 *   Atomic used to avoid lock/unlock...
 */

u32
fc_get_event_number(void)
{
 return atomic_add_return(1, &fc_event_seq);
}
EXPORT_SYMBOL(fc_get_event_number);

/**
 * fc_host_post_fc_event - routine to do the work of posting an event
 *                      on an fc_host.
 * @shost: host the event occurred on
 * @event_number: fc event number obtained from get_fc_event_number()
 * @event_code: fc_host event being posted
 * @data_len: amount, in bytes, of event data
 * @data_buf: pointer to event data
 * @vendor_id:          value for Vendor id
 *
 * Notes:
 * This routine assumes no locks are held on entry.
 */

void
fc_host_post_fc_event(struct Scsi_Host *shost, u32 event_number,
  enum fc_host_event_code event_code,
  u32 data_len, char *data_buf, u64 vendor_id)
{
 struct sk_buff *skb;
 struct nlmsghdr *nlh;
 struct fc_nl_event *event;
 const char *name;
 size_t len, padding;
 int err;

 if (!data_buf || data_len < 4)
  data_len = 0;

 if (!scsi_nl_sock) {
  err = -ENOENT;
  goto send_fail;
 }

 len = FC_NL_MSGALIGN(sizeof(*event) - sizeof(event->event_data) + data_len);

 skb = nlmsg_new(len, GFP_KERNEL);
 if (!skb) {
  err = -ENOBUFS;
  goto send_fail;
 }

 nlh = nlmsg_put(skb, 0, 0, SCSI_TRANSPORT_MSG, len, 0);
 if (!nlh) {
  err = -ENOBUFS;
  goto send_fail_skb;
 }
 event = nlmsg_data(nlh);

 INIT_SCSI_NL_HDR(&event->snlh, SCSI_NL_TRANSPORT_FC,
    FC_NL_ASYNC_EVENT, len);
 event->seconds = ktime_get_real_seconds();
 event->vendor_id = vendor_id;
 event->host_no = shost->host_no;
 event->event_datalen = data_len; /* bytes */
 event->event_num = event_number;
 event->event_code = event_code;
 if (data_len)
  memcpy(event->event_data_flex, data_buf, data_len);
 padding = len - offsetof(typeof(*event), event_data_flex) - data_len;
 memset(event->event_data_flex + data_len, 0, padding);

 nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
   GFP_KERNEL);
 return;

send_fail_skb:
 kfree_skb(skb);
send_fail:
 name = get_fc_host_event_code_name(event_code);
 printk(KERN_WARNING
  "%s: Dropped Event : host %d %s data 0x%08x - err %d\n",
  __func__, shost->host_no,
  (name) ? name : "",
  (data_len) ? *((u32 *)data_buf) : 0xFFFFFFFF, err);
 return;
}
EXPORT_SYMBOL(fc_host_post_fc_event);

/**
 * fc_host_post_event - called to post an even on an fc_host.
 * @shost: host the event occurred on
 * @event_number: fc event number obtained from get_fc_event_number()
 * @event_code: fc_host event being posted
 * @event_data: 32bits of data for the event being posted
 *
 * Notes:
 * This routine assumes no locks are held on entry.
 */

void
fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
  enum fc_host_event_code event_code, u32 event_data)
{
 fc_host_post_fc_event(shost, event_number, event_code,
  (u32)sizeof(u32), (char *)&event_data, 0);
}
EXPORT_SYMBOL(fc_host_post_event);


/**
 * fc_host_post_vendor_event - called to post a vendor unique event
 *                      on an fc_host
 * @shost: host the event occurred on
 * @event_number: fc event number obtained from get_fc_event_number()
 * @data_len: amount, in bytes, of vendor unique data
 * @data_buf: pointer to vendor unique data
 * @vendor_id:          Vendor id
 *
 * Notes:
 * This routine assumes no locks are held on entry.
 */

void
fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
  u32 data_len, char * data_buf, u64 vendor_id)
{
 fc_host_post_fc_event(shost, event_number, FCH_EVT_VENDOR_UNIQUE,
  data_len, data_buf, vendor_id);
}
EXPORT_SYMBOL(fc_host_post_vendor_event);

/**
 * fc_find_rport_by_wwpn - find the fc_rport pointer for a given wwpn
 * @shost: host the fc_rport is associated with
 * @wwpn: wwpn of the fc_rport device
 *
 * Notes:
 * This routine assumes no locks are held on entry.
 */

struct fc_rport *
fc_find_rport_by_wwpn(struct Scsi_Host *shost, u64 wwpn)
{
 struct fc_rport *rport;
 unsigned long flags;

 spin_lock_irqsave(shost->host_lock, flags);

 list_for_each_entry(rport, &fc_host_rports(shost), peers) {
  if (rport->port_state != FC_PORTSTATE_ONLINE)
   continue;

  if (rport->port_name == wwpn) {
   spin_unlock_irqrestore(shost->host_lock, flags);
   return rport;
  }
 }

 spin_unlock_irqrestore(shost->host_lock, flags);
 return NULL;
}
EXPORT_SYMBOL(fc_find_rport_by_wwpn);

static void
fc_li_stats_update(u16 event_type,
     struct fc_fpin_stats *stats)
{
 stats->li++;
 switch (event_type) {
 case FPIN_LI_UNKNOWN:
  stats->li_failure_unknown++;
  break;
 case FPIN_LI_LINK_FAILURE:
  stats->li_link_failure_count++;
  break;
 case FPIN_LI_LOSS_OF_SYNC:
  stats->li_loss_of_sync_count++;
  break;
 case FPIN_LI_LOSS_OF_SIG:
  stats->li_loss_of_signals_count++;
  break;
 case FPIN_LI_PRIM_SEQ_ERR:
  stats->li_prim_seq_err_count++;
  break;
 case FPIN_LI_INVALID_TX_WD:
  stats->li_invalid_tx_word_count++;
  break;
 case FPIN_LI_INVALID_CRC:
  stats->li_invalid_crc_count++;
  break;
 case FPIN_LI_DEVICE_SPEC:
  stats->li_device_specific++;
  break;
 }
}

static void
fc_delivery_stats_update(u32 reason_code, struct fc_fpin_stats *stats)
{
 stats->dn++;
 switch (reason_code) {
 case FPIN_DELI_UNKNOWN:
  stats->dn_unknown++;
  break;
 case FPIN_DELI_TIMEOUT:
  stats->dn_timeout++;
  break;
 case FPIN_DELI_UNABLE_TO_ROUTE:
  stats->dn_unable_to_route++;
  break;
 case FPIN_DELI_DEVICE_SPEC:
  stats->dn_device_specific++;
  break;
 }
}

static void
fc_cn_stats_update(u16 event_type, struct fc_fpin_stats *stats)
{
 stats->cn++;
 switch (event_type) {
 case FPIN_CONGN_CLEAR:
  stats->cn_clear++;
  break;
 case FPIN_CONGN_LOST_CREDIT:
  stats->cn_lost_credit++;
  break;
 case FPIN_CONGN_CREDIT_STALL:
  stats->cn_credit_stall++;
  break;
 case FPIN_CONGN_OVERSUBSCRIPTION:
  stats->cn_oversubscription++;
  break;
 case FPIN_CONGN_DEVICE_SPEC:
  stats->cn_device_specific++;
 }
}

/*
 * fc_fpin_li_stats_update - routine to update Link Integrity
 * event statistics.
 * @shost: host the FPIN was received on
 * @tlv: pointer to link integrity descriptor
 *
 */

static void
fc_fpin_li_stats_update(struct Scsi_Host *shost, struct fc_tlv_desc *tlv)
{
 u8 i;
 struct fc_rport *rport = NULL;
 struct fc_rport *attach_rport = NULL;
 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
 struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
 u16 event_type = be16_to_cpu(li_desc->event_type);
 u64 wwpn;

 rport = fc_find_rport_by_wwpn(shost,
          be64_to_cpu(li_desc->attached_wwpn));
 if (rport &&
     (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
      rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
  attach_rport = rport;
  fc_li_stats_update(event_type, &attach_rport->fpin_stats);
 }

 if (be32_to_cpu(li_desc->pname_count) > 0) {
  for (i = 0;
      i < be32_to_cpu(li_desc->pname_count);
      i++) {
   wwpn = be64_to_cpu(li_desc->pname_list[i]);
   rport = fc_find_rport_by_wwpn(shost, wwpn);
   if (rport &&
       (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
       rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
    if (rport == attach_rport)
     continue;
    fc_li_stats_update(event_type,
         &rport->fpin_stats);
   }
  }
 }

 if (fc_host->port_name == be64_to_cpu(li_desc->attached_wwpn))
  fc_li_stats_update(event_type, &fc_host->fpin_stats);
}

/*
 * fc_fpin_delivery_stats_update - routine to update Delivery Notification
 * event statistics.
 * @shost: host the FPIN was received on
 * @tlv: pointer to delivery descriptor
 *
 */

static void
fc_fpin_delivery_stats_update(struct Scsi_Host *shost,
         struct fc_tlv_desc *tlv)
{
 struct fc_rport *rport = NULL;
 struct fc_rport *attach_rport = NULL;
 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
 struct fc_fn_deli_desc *dn_desc = (struct fc_fn_deli_desc *)tlv;
 u32 reason_code = be32_to_cpu(dn_desc->deli_reason_code);

 rport = fc_find_rport_by_wwpn(shost,
          be64_to_cpu(dn_desc->attached_wwpn));
 if (rport &&
     (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
      rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
  attach_rport = rport;
  fc_delivery_stats_update(reason_code,
      &attach_rport->fpin_stats);
 }

 if (fc_host->port_name == be64_to_cpu(dn_desc->attached_wwpn))
  fc_delivery_stats_update(reason_code, &fc_host->fpin_stats);
}

/*
 * fc_fpin_peer_congn_stats_update - routine to update Peer Congestion
 * event statistics.
 * @shost: host the FPIN was received on
 * @tlv: pointer to peer congestion descriptor
 *
 */

static void
fc_fpin_peer_congn_stats_update(struct Scsi_Host *shost,
    struct fc_tlv_desc *tlv)
{
 u8 i;
 struct fc_rport *rport = NULL;
 struct fc_rport *attach_rport = NULL;
 struct fc_fn_peer_congn_desc *pc_desc =
     (struct fc_fn_peer_congn_desc *)tlv;
 u16 event_type = be16_to_cpu(pc_desc->event_type);
 u64 wwpn;

 rport = fc_find_rport_by_wwpn(shost,
          be64_to_cpu(pc_desc->attached_wwpn));
 if (rport &&
     (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
      rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
  attach_rport = rport;
  fc_cn_stats_update(event_type, &attach_rport->fpin_stats);
 }

 if (be32_to_cpu(pc_desc->pname_count) > 0) {
  for (i = 0;
      i < be32_to_cpu(pc_desc->pname_count);
      i++) {
   wwpn = be64_to_cpu(pc_desc->pname_list[i]);
   rport = fc_find_rport_by_wwpn(shost, wwpn);
   if (rport &&
       (rport->roles & FC_PORT_ROLE_FCP_TARGET ||
        rport->roles & FC_PORT_ROLE_NVME_TARGET)) {
    if (rport == attach_rport)
     continue;
    fc_cn_stats_update(event_type,
         &rport->fpin_stats);
   }
  }
 }
}

/*
 * fc_fpin_congn_stats_update - routine to update Congestion
 * event statistics.
 * @shost: host the FPIN was received on
 * @tlv: pointer to congestion descriptor
 *
 */

static void
fc_fpin_congn_stats_update(struct Scsi_Host *shost,
      struct fc_tlv_desc *tlv)
{
 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
 struct fc_fn_congn_desc *congn = (struct fc_fn_congn_desc *)tlv;

 fc_cn_stats_update(be16_to_cpu(congn->event_type),
      &fc_host->fpin_stats);
}

/**
 * fc_host_fpin_rcv - routine to process a received FPIN.
 * @shost: host the FPIN was received on
 * @fpin_len: length of FPIN payload, in bytes
 * @fpin_buf: pointer to FPIN payload
 * @event_acknowledge: 1, if LLDD handles this event.
 * Notes:
 * This routine assumes no locks are held on entry.
 */

void
fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf,
  u8 event_acknowledge)
{
 struct fc_els_fpin *fpin = (struct fc_els_fpin *)fpin_buf;
 struct fc_tlv_desc *tlv;
 u32 bytes_remain;
 u32 dtag;
 enum fc_host_event_code event_code =
  event_acknowledge ? FCH_EVT_LINK_FPIN_ACK : FCH_EVT_LINK_FPIN;

 /* Update Statistics */
 tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
 bytes_remain = fpin_len - offsetof(struct fc_els_fpin, fpin_desc);
 bytes_remain = min_t(u32, bytes_remain, be32_to_cpu(fpin->desc_len));

 while (bytes_remain >= FC_TLV_DESC_HDR_SZ &&
        bytes_remain >= FC_TLV_DESC_SZ_FROM_LENGTH(tlv)) {
  dtag = be32_to_cpu(tlv->desc_tag);
  switch (dtag) {
  case ELS_DTAG_LNK_INTEGRITY:
   fc_fpin_li_stats_update(shost, tlv);
   break;
  case ELS_DTAG_DELIVERY:
   fc_fpin_delivery_stats_update(shost, tlv);
   break;
  case ELS_DTAG_PEER_CONGEST:
   fc_fpin_peer_congn_stats_update(shost, tlv);
   break;
  case ELS_DTAG_CONGESTION:
   fc_fpin_congn_stats_update(shost, tlv);
  }

  bytes_remain -= FC_TLV_DESC_SZ_FROM_LENGTH(tlv);
  tlv = fc_tlv_next_desc(tlv);
 }

 fc_host_post_fc_event(shost, fc_get_event_number(),
    event_code, fpin_len, fpin_buf, 0);
}
EXPORT_SYMBOL(fc_host_fpin_rcv);


static __init int fc_transport_init(void)
{
 int error;

 atomic_set(&fc_event_seq, 0);

 error = transport_class_register(&fc_host_class);
 if (error)
  return error;
 error = transport_class_register(&fc_vport_class);
 if (error)
  goto unreg_host_class;
 error = transport_class_register(&fc_rport_class);
 if (error)
  goto unreg_vport_class;
 error = transport_class_register(&fc_transport_class);
 if (error)
  goto unreg_rport_class;
 return 0;

unreg_rport_class:
 transport_class_unregister(&fc_rport_class);
unreg_vport_class:
 transport_class_unregister(&fc_vport_class);
unreg_host_class:
 transport_class_unregister(&fc_host_class);
 return error;
}

static void __exit fc_transport_exit(void)
{
 transport_class_unregister(&fc_transport_class);
 transport_class_unregister(&fc_rport_class);
 transport_class_unregister(&fc_host_class);
 transport_class_unregister(&fc_vport_class);
}

/*
 * FC Remote Port Attribute Management
 */


#define fc_rport_show_function(field, format_string, sz, cast)  \
static ssize_t        \
show_fc_rport_##field (struct device *dev,     \
         struct device_attribute *attr, char *buf) \
{         \
 struct fc_rport *rport = transport_class_to_rport(dev);  \
 struct Scsi_Host *shost = rport_to_shost(rport);  \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 if ((i->f->get_rport_##field) &&    \
     !((rport->port_state == FC_PORTSTATE_BLOCKED) ||  \
       (rport->port_state == FC_PORTSTATE_DELETED) ||  \
       (rport->port_state == FC_PORTSTATE_NOTPRESENT)))  \
  i->f->get_rport_##field(rport);    \
 return snprintf(buf, sz, format_string, cast rport->field);  \
}

#define fc_rport_store_function(field)     \
static ssize_t        \
store_fc_rport_##field(struct device *dev,    \
         struct device_attribute *attr,   \
         const char *buf, size_t count)   \
{         \
 int val;       \
 struct fc_rport *rport = transport_class_to_rport(dev);  \
 struct Scsi_Host *shost = rport_to_shost(rport);  \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 char *cp;       \
 if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||  \
     (rport->port_state == FC_PORTSTATE_DELETED) ||  \
     (rport->port_state == FC_PORTSTATE_NOTPRESENT))  \
  return -EBUSY;      \
 val = simple_strtoul(buf, &cp, 0);    \
 if (*cp && (*cp != '\n'))     \
  return -EINVAL;      \
 i->f->set_rport_##field(rport, val);    \
 return count;       \
}

#define fc_rport_rd_attr(field, format_string, sz)   \
 fc_rport_show_function(field, format_string, sz, )  \
static FC_DEVICE_ATTR(rport, field, S_IRUGO,   \
    show_fc_rport_##field, NULL)

#define fc_rport_rd_attr_cast(field, format_string, sz, cast)  \
 fc_rport_show_function(field, format_string, sz, (cast)) \
static FC_DEVICE_ATTR(rport, field, S_IRUGO,   \
     show_fc_rport_##field, NULL)

#define fc_rport_rw_attr(field, format_string, sz)   \
 fc_rport_show_function(field, format_string, sz, )  \
 fc_rport_store_function(field)     \
static FC_DEVICE_ATTR(rport, field, S_IRUGO | S_IWUSR,  \
   show_fc_rport_##field,    \
   store_fc_rport_##field)


#define fc_private_rport_show_function(field, format_string, sz, cast) \
static ssize_t        \
show_fc_rport_##field (struct device *dev,     \
         struct device_attribute *attr, char *buf) \
{         \
 struct fc_rport *rport = transport_class_to_rport(dev);  \
 return snprintf(buf, sz, format_string, cast rport->field);  \
}

#define fc_private_rport_rd_attr(field, format_string, sz)  \
 fc_private_rport_show_function(field, format_string, sz, ) \
static FC_DEVICE_ATTR(rport, field, S_IRUGO,   \
    show_fc_rport_##field, NULL)

#define fc_private_rport_rd_attr_cast(field, format_string, sz, cast) \
 fc_private_rport_show_function(field, format_string, sz, (cast)) \
static FC_DEVICE_ATTR(rport, field, S_IRUGO,   \
     show_fc_rport_##field, NULL)


#define fc_private_rport_rd_enum_attr(title, maxlen)   \
static ssize_t        \
show_fc_rport_##title (struct device *dev,    \
         struct device_attribute *attr, char *buf) \
{         \
 struct fc_rport *rport = transport_class_to_rport(dev);  \
 const char *name;      \
 name = get_fc_##title##_name(rport->title);   \
 if (!name)       \
  return -EINVAL;      \
 return snprintf(buf, maxlen, "%s\n", name);   \
}         \
static FC_DEVICE_ATTR(rport, title, S_IRUGO,   \
   show_fc_rport_##title, NULL)


#define SETUP_RPORT_ATTRIBUTE_RD(field)     \
 i->private_rport_attrs[count] = device_attr_rport_##field; \
 i->private_rport_attrs[count].attr.mode = S_IRUGO;  \
 i->private_rport_attrs[count].store = NULL;   \
 i->rport_attrs[count] = &i->private_rport_attrs[count];  \
 if (i->f->show_rport_##field)     \
  count++

#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(field)    \
 i->private_rport_attrs[count] = device_attr_rport_##field; \
 i->private_rport_attrs[count].attr.mode = S_IRUGO;  \
 i->private_rport_attrs[count].store = NULL;   \
 i->rport_attrs[count] = &i->private_rport_attrs[count];  \
 count++

#define SETUP_RPORT_ATTRIBUTE_RW(field)     \
 i->private_rport_attrs[count] = device_attr_rport_##field; \
 if (!i->f->set_rport_##field) {     \
  i->private_rport_attrs[count].attr.mode = S_IRUGO; \
  i->private_rport_attrs[count].store = NULL;  \
 }        \
 i->rport_attrs[count] = &i->private_rport_attrs[count];  \
 if (i->f->show_rport_##field)     \
  count++

#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(field)    \
{         \
 i->private_rport_attrs[count] = device_attr_rport_##field; \
 i->rport_attrs[count] = &i->private_rport_attrs[count];  \
 count++;       \
}


/* The FC Transport Remote Port Attributes: */

/* Fixed Remote Port Attributes */

fc_private_rport_rd_attr(maxframe_size, "%u bytes\n", 20);

static ssize_t
show_fc_rport_supported_classes (struct device *dev,
     struct device_attribute *attr, char *buf)
{
 struct fc_rport *rport = transport_class_to_rport(dev);
 if (rport->supported_classes == FC_COS_UNSPECIFIED)
  return snprintf(buf, 20, "unspecified\n");
 return get_fc_cos_names(rport->supported_classes, buf);
}
static FC_DEVICE_ATTR(rport, supported_classes, S_IRUGO,
  show_fc_rport_supported_classes, NULL);

/* Dynamic Remote Port Attributes */

/*
 * dev_loss_tmo attribute
 */

static int fc_str_to_dev_loss(const char *buf, unsigned long *val)
{
 char *cp;

 *val = simple_strtoul(buf, &cp, 0);
 if (*cp && (*cp != '\n'))
  return -EINVAL;
 /*
 * Check for overflow; dev_loss_tmo is u32
 */

 if (*val > UINT_MAX)
  return -EINVAL;

 return 0;
}

static int fc_rport_set_dev_loss_tmo(struct fc_rport *rport,
         unsigned long val)
{
 struct Scsi_Host *shost = rport_to_shost(rport);
 struct fc_internal *i = to_fc_internal(shost->transportt);

 if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
     (rport->port_state == FC_PORTSTATE_DELETED) ||
     (rport->port_state == FC_PORTSTATE_NOTPRESENT))
  return -EBUSY;
 /*
 * Check for overflow; dev_loss_tmo is u32
 */

 if (val > UINT_MAX)
  return -EINVAL;

 /*
 * If fast_io_fail is off we have to cap
 * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT
 */

 if (rport->fast_io_fail_tmo == -1 &&
     val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
  return -EINVAL;

 i->f->set_rport_dev_loss_tmo(rport, val);
 return 0;
}

fc_rport_show_function(dev_loss_tmo, "%u\n", 20, )
static ssize_t
store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
       const char *buf, size_t count)
{
 struct fc_rport *rport = transport_class_to_rport(dev);
 unsigned long val;
 int rc;

 rc = fc_str_to_dev_loss(buf, &val);
 if (rc)
  return rc;

 rc = fc_rport_set_dev_loss_tmo(rport, val);
 if (rc)
  return rc;
 return count;
}
static FC_DEVICE_ATTR(rport, dev_loss_tmo, S_IRUGO | S_IWUSR,
  show_fc_rport_dev_loss_tmo, store_fc_rport_dev_loss_tmo);


/* Private Remote Port Attributes */

fc_private_rport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
fc_private_rport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
fc_private_rport_rd_attr(port_id, "0x%06x\n", 20);

static ssize_t
show_fc_rport_roles (struct device *dev, struct device_attribute *attr,
       char *buf)
{
 struct fc_rport *rport = transport_class_to_rport(dev);

 /* identify any roles that are port_id specific */
 if ((rport->port_id != -1) &&
     (rport->port_id & FC_WELLKNOWN_PORTID_MASK) ==
     FC_WELLKNOWN_PORTID_MASK) {
  switch (rport->port_id & FC_WELLKNOWN_ROLE_MASK) {
  case FC_FPORT_PORTID:
   return snprintf(buf, 30, "Fabric Port\n");
  case FC_FABCTLR_PORTID:
   return snprintf(buf, 30, "Fabric Controller\n");
  case FC_DIRSRVR_PORTID:
   return snprintf(buf, 30, "Directory Server\n");
  case FC_TIMESRVR_PORTID:
   return snprintf(buf, 30, "Time Server\n");
  case FC_MGMTSRVR_PORTID:
   return snprintf(buf, 30, "Management Server\n");
  default:
   return snprintf(buf, 30, "Unknown Fabric Entity\n");
  }
 } else {
  if (rport->roles == FC_PORT_ROLE_UNKNOWN)
   return snprintf(buf, 20, "unknown\n");
  return get_fc_port_roles_names(rport->roles, buf);
 }
}
static FC_DEVICE_ATTR(rport, roles, S_IRUGO,
  show_fc_rport_roles, NULL);

static ssize_t fc_rport_set_marginal_state(struct device *dev,
      struct device_attribute *attr,
      const char *buf, size_t count)
{
 struct fc_rport *rport = transport_class_to_rport(dev);
 enum fc_port_state port_state;
 int ret = 0;

 ret = get_fc_port_state_match(buf, &port_state);
 if (ret)
  return -EINVAL;
 if (port_state == FC_PORTSTATE_MARGINAL) {
  /*
 * Change the state to Marginal only if the
 * current rport state is Online
 * Allow only Online->Marginal
 */

  if (rport->port_state == FC_PORTSTATE_ONLINE)
   rport->port_state = port_state;
  else if (port_state != rport->port_state)
   return -EINVAL;
 } else if (port_state == FC_PORTSTATE_ONLINE) {
  /*
 * Change the state to Online only if the
 * current rport state is Marginal
 * Allow only Marginal->Online
 */

  if (rport->port_state == FC_PORTSTATE_MARGINAL)
   rport->port_state = port_state;
  else if (port_state != rport->port_state)
   return -EINVAL;
 } else
  return -EINVAL;
 return count;
}

static ssize_t
show_fc_rport_port_state(struct device *dev,
    struct device_attribute *attr, char *buf)
{
 const char *name;
 struct fc_rport *rport = transport_class_to_rport(dev);

 name = get_fc_port_state_name(rport->port_state);
 if (!name)
  return -EINVAL;

 return snprintf(buf, 20, "%s\n", name);
}

static FC_DEVICE_ATTR(rport, port_state, 0444 | 0200,
   show_fc_rport_port_state, fc_rport_set_marginal_state);

fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20);

/*
 * fast_io_fail_tmo attribute
 */

static ssize_t
show_fc_rport_fast_io_fail_tmo (struct device *dev,
    struct device_attribute *attr, char *buf)
{
 struct fc_rport *rport = transport_class_to_rport(dev);

 if (rport->fast_io_fail_tmo == -1)
  return snprintf(buf, 5, "off\n");
 return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo);
}

static ssize_t
store_fc_rport_fast_io_fail_tmo(struct device *dev,
    struct device_attribute *attr, const char *buf,
    size_t count)
{
 int val;
 char *cp;
 struct fc_rport *rport = transport_class_to_rport(dev);

 if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
     (rport->port_state == FC_PORTSTATE_DELETED) ||
     (rport->port_state == FC_PORTSTATE_NOTPRESENT))
  return -EBUSY;
 if (strncmp(buf, "off", 3) == 0)
  rport->fast_io_fail_tmo = -1;
 else {
  val = simple_strtoul(buf, &cp, 0);
  if ((*cp && (*cp != '\n')) || (val < 0))
   return -EINVAL;
  /*
 * Cap fast_io_fail by dev_loss_tmo or
 * SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
 */

  if ((val >= rport->dev_loss_tmo) ||
      (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
   return -EINVAL;

  rport->fast_io_fail_tmo = val;
 }
 return count;
}
static FC_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR,
 show_fc_rport_fast_io_fail_tmo, store_fc_rport_fast_io_fail_tmo);

#define fc_rport_fpin_statistic(name)     \
static ssize_t fc_rport_fpinstat_##name(struct device *cd,  \
      struct device_attribute *attr, \
      char *buf)    \
{         \
 struct fc_rport *rport = transport_class_to_rport(cd);  \
         \
 return snprintf(buf, 20, "0x%llx\n", rport->fpin_stats.name); \
}         \
static FC_DEVICE_ATTR(rport, fpin_##name, 0444, fc_rport_fpinstat_##name, NULL)

fc_rport_fpin_statistic(dn);
fc_rport_fpin_statistic(dn_unknown);
fc_rport_fpin_statistic(dn_timeout);
fc_rport_fpin_statistic(dn_unable_to_route);
fc_rport_fpin_statistic(dn_device_specific);
fc_rport_fpin_statistic(cn);
fc_rport_fpin_statistic(cn_clear);
fc_rport_fpin_statistic(cn_lost_credit);
fc_rport_fpin_statistic(cn_credit_stall);
fc_rport_fpin_statistic(cn_oversubscription);
fc_rport_fpin_statistic(cn_device_specific);
fc_rport_fpin_statistic(li);
fc_rport_fpin_statistic(li_failure_unknown);
fc_rport_fpin_statistic(li_link_failure_count);
fc_rport_fpin_statistic(li_loss_of_sync_count);
fc_rport_fpin_statistic(li_loss_of_signals_count);
fc_rport_fpin_statistic(li_prim_seq_err_count);
fc_rport_fpin_statistic(li_invalid_tx_word_count);
fc_rport_fpin_statistic(li_invalid_crc_count);
fc_rport_fpin_statistic(li_device_specific);

static struct attribute *fc_rport_statistics_attrs[] = {
 &device_attr_rport_fpin_dn.attr,
 &device_attr_rport_fpin_dn_unknown.attr,
 &device_attr_rport_fpin_dn_timeout.attr,
 &device_attr_rport_fpin_dn_unable_to_route.attr,
 &device_attr_rport_fpin_dn_device_specific.attr,
 &device_attr_rport_fpin_li.attr,
 &device_attr_rport_fpin_li_failure_unknown.attr,
 &device_attr_rport_fpin_li_link_failure_count.attr,
 &device_attr_rport_fpin_li_loss_of_sync_count.attr,
 &device_attr_rport_fpin_li_loss_of_signals_count.attr,
 &device_attr_rport_fpin_li_prim_seq_err_count.attr,
 &device_attr_rport_fpin_li_invalid_tx_word_count.attr,
 &device_attr_rport_fpin_li_invalid_crc_count.attr,
 &device_attr_rport_fpin_li_device_specific.attr,
 &device_attr_rport_fpin_cn.attr,
 &device_attr_rport_fpin_cn_clear.attr,
 &device_attr_rport_fpin_cn_lost_credit.attr,
 &device_attr_rport_fpin_cn_credit_stall.attr,
 &device_attr_rport_fpin_cn_oversubscription.attr,
 &device_attr_rport_fpin_cn_device_specific.attr,
 NULL
};

static struct attribute_group fc_rport_statistics_group = {
 .name = "statistics",
 .attrs = fc_rport_statistics_attrs,
};


/*
 * FC SCSI Target Attribute Management
 */


/*
 * Note: in the target show function we recognize when the remote
 *  port is in the hierarchy and do not allow the driver to get
 *  involved in sysfs functions. The driver only gets involved if
 *  it's the "old" style that doesn't use rports.
 */

#define fc_starget_show_function(field, format_string, sz, cast) \
static ssize_t        \
show_fc_starget_##field (struct device *dev,     \
    struct device_attribute *attr, char *buf) \
{         \
 struct scsi_target *starget = transport_class_to_starget(dev); \
 struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 struct fc_rport *rport = starget_to_rport(starget);  \
 if (rport)       \
  fc_starget_##field(starget) = rport->field;  \
 else if (i->f->get_starget_##field)    \
  i->f->get_starget_##field(starget);   \
 return snprintf(buf, sz, format_string,    \
  cast fc_starget_##field(starget));    \
}

#define fc_starget_rd_attr(field, format_string, sz)   \
 fc_starget_show_function(field, format_string, sz, )  \
static FC_DEVICE_ATTR(starget, field, S_IRUGO,   \
    show_fc_starget_##field, NULL)

#define fc_starget_rd_attr_cast(field, format_string, sz, cast)  \
 fc_starget_show_function(field, format_string, sz, (cast)) \
static FC_DEVICE_ATTR(starget, field, S_IRUGO,   \
     show_fc_starget_##field, NULL)

#define SETUP_STARGET_ATTRIBUTE_RD(field)    \
 i->private_starget_attrs[count] = device_attr_starget_##field; \
 i->private_starget_attrs[count].attr.mode = S_IRUGO;  \
 i->private_starget_attrs[count].store = NULL;   \
 i->starget_attrs[count] = &i->private_starget_attrs[count]; \
 if (i->f->show_starget_##field)     \
  count++

#define SETUP_STARGET_ATTRIBUTE_RW(field)    \
 i->private_starget_attrs[count] = device_attr_starget_##field; \
 if (!i->f->set_starget_##field) {    \
  i->private_starget_attrs[count].attr.mode = S_IRUGO; \
  i->private_starget_attrs[count].store = NULL;  \
 }        \
 i->starget_attrs[count] = &i->private_starget_attrs[count]; \
 if (i->f->show_starget_##field)     \
  count++

/* The FC Transport SCSI Target Attributes: */
fc_starget_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
fc_starget_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
fc_starget_rd_attr(port_id, "0x%06x\n", 20);


/*
 * FC Virtual Port Attribute Management
 */


#define fc_vport_show_function(field, format_string, sz, cast)  \
static ssize_t        \
show_fc_vport_##field (struct device *dev,     \
         struct device_attribute *attr, char *buf) \
{         \
 struct fc_vport *vport = transport_class_to_vport(dev);  \
 struct Scsi_Host *shost = vport_to_shost(vport);  \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 if ((i->f->get_vport_##field) &&    \
     !(vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))) \
  i->f->get_vport_##field(vport);    \
 return snprintf(buf, sz, format_string, cast vport->field);  \
}

#define fc_vport_store_function(field)     \
static ssize_t        \
store_fc_vport_##field(struct device *dev,    \
         struct device_attribute *attr,   \
         const char *buf, size_t count)   \
{         \
 int val;       \
 struct fc_vport *vport = transport_class_to_vport(dev);  \
 struct Scsi_Host *shost = vport_to_shost(vport);  \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 char *cp;       \
 if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) \
  return -EBUSY;      \
 val = simple_strtoul(buf, &cp, 0);    \
 if (*cp && (*cp != '\n'))     \
  return -EINVAL;      \
 i->f->set_vport_##field(vport, val);    \
 return count;       \
}

#define fc_vport_store_str_function(field, slen)   \
static ssize_t        \
store_fc_vport_##field(struct device *dev,    \
         struct device_attribute *attr,    \
         const char *buf, size_t count)   \
{         \
 struct fc_vport *vport = transport_class_to_vport(dev);  \
 struct Scsi_Host *shost = vport_to_shost(vport);  \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 unsigned int cnt=count;      \
         \
 /* count may include a LF at end of string */ \
 if (buf[cnt-1] == '\n')      \
  cnt--;       \
 if (cnt > ((slen) - 1))      \
  return -EINVAL;      \
 memcpy(vport->field, buf, cnt);     \
 i->f->set_vport_##field(vport);     \
 return count;       \
}

#define fc_vport_rd_attr(field, format_string, sz)   \
 fc_vport_show_function(field, format_string, sz, )  \
static FC_DEVICE_ATTR(vport, field, S_IRUGO,   \
    show_fc_vport_##field, NULL)

#define fc_vport_rd_attr_cast(field, format_string, sz, cast)  \
 fc_vport_show_function(field, format_string, sz, (cast)) \
static FC_DEVICE_ATTR(vport, field, S_IRUGO,   \
     show_fc_vport_##field, NULL)

#define fc_vport_rw_attr(field, format_string, sz)   \
 fc_vport_show_function(field, format_string, sz, )  \
 fc_vport_store_function(field)     \
static FC_DEVICE_ATTR(vport, field, S_IRUGO | S_IWUSR,  \
   show_fc_vport_##field,    \
   store_fc_vport_##field)

#define fc_private_vport_show_function(field, format_string, sz, cast) \
static ssize_t        \
show_fc_vport_##field (struct device *dev,    \
         struct device_attribute *attr, char *buf) \
{         \
 struct fc_vport *vport = transport_class_to_vport(dev);  \
 return snprintf(buf, sz, format_string, cast vport->field);  \
}

#define fc_private_vport_store_u32_function(field)   \
static ssize_t        \
store_fc_vport_##field(struct device *dev,    \
         struct device_attribute *attr,   \
         const char *buf, size_t count)   \
{         \
 u32 val;       \
 struct fc_vport *vport = transport_class_to_vport(dev);  \
 char *cp;       \
 if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))  \
  return -EBUSY;      \
 val = simple_strtoul(buf, &cp, 0);    \
 if (*cp && (*cp != '\n'))     \
  return -EINVAL;      \
 vport->field = val;      \
 return count;       \
}


#define fc_private_vport_rd_attr(field, format_string, sz)  \
 fc_private_vport_show_function(field, format_string, sz, ) \
static FC_DEVICE_ATTR(vport, field, S_IRUGO,   \
    show_fc_vport_##field, NULL)

#define fc_private_vport_rd_attr_cast(field, format_string, sz, cast) \
 fc_private_vport_show_function(field, format_string, sz, (cast)) \
static FC_DEVICE_ATTR(vport, field, S_IRUGO,   \
     show_fc_vport_##field, NULL)

#define fc_private_vport_rw_u32_attr(field, format_string, sz)  \
 fc_private_vport_show_function(field, format_string, sz, ) \
 fc_private_vport_store_u32_function(field)   \
static FC_DEVICE_ATTR(vport, field, S_IRUGO | S_IWUSR,  \
   show_fc_vport_##field,    \
   store_fc_vport_##field)


#define fc_private_vport_rd_enum_attr(title, maxlen)   \
static ssize_t        \
show_fc_vport_##title (struct device *dev,    \
         struct device_attribute *attr,   \
         char *buf)     \
{         \
 struct fc_vport *vport = transport_class_to_vport(dev);  \
 const char *name;      \
 name = get_fc_##title##_name(vport->title);   \
 if (!name)       \
  return -EINVAL;      \
 return snprintf(buf, maxlen, "%s\n", name);   \
}         \
static FC_DEVICE_ATTR(vport, title, S_IRUGO,   \
   show_fc_vport_##title, NULL)


#define SETUP_VPORT_ATTRIBUTE_RD(field)     \
 i->private_vport_attrs[count] = device_attr_vport_##field; \
 i->private_vport_attrs[count].attr.mode = S_IRUGO;  \
 i->private_vport_attrs[count].store = NULL;   \
 i->vport_attrs[count] = &i->private_vport_attrs[count];  \
 if (i->f->get_##field)      \
  count++
 /* NOTE: Above MACRO differs: checks function not show bit */

#define SETUP_PRIVATE_VPORT_ATTRIBUTE_RD(field)    \
 i->private_vport_attrs[count] = device_attr_vport_##field; \
 i->private_vport_attrs[count].attr.mode = S_IRUGO;  \
 i->private_vport_attrs[count].store = NULL;   \
 i->vport_attrs[count] = &i->private_vport_attrs[count];  \
 count++

#define SETUP_VPORT_ATTRIBUTE_WR(field)     \
 i->private_vport_attrs[count] = device_attr_vport_##field; \
 i->vport_attrs[count] = &i->private_vport_attrs[count];  \
 if (i->f->field)      \
  count++
 /* NOTE: Above MACRO differs: checks function */

#define SETUP_VPORT_ATTRIBUTE_RW(field)     \
 i->private_vport_attrs[count] = device_attr_vport_##field; \
 if (!i->f->set_vport_##field) {     \
  i->private_vport_attrs[count].attr.mode = S_IRUGO; \
  i->private_vport_attrs[count].store = NULL;  \
 }        \
 i->vport_attrs[count] = &i->private_vport_attrs[count];  \
 count++
 /* NOTE: Above MACRO differs: does not check show bit */

#define SETUP_PRIVATE_VPORT_ATTRIBUTE_RW(field)    \
{         \
 i->private_vport_attrs[count] = device_attr_vport_##field; \
 i->vport_attrs[count] = &i->private_vport_attrs[count];  \
 count++;       \
}


/* The FC Transport Virtual Port Attributes: */

/* Fixed Virtual Port Attributes */

/* Dynamic Virtual Port Attributes */

/* Private Virtual Port Attributes */

fc_private_vport_rd_enum_attr(vport_state, FC_VPORTSTATE_MAX_NAMELEN);
fc_private_vport_rd_enum_attr(vport_last_state, FC_VPORTSTATE_MAX_NAMELEN);
fc_private_vport_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
fc_private_vport_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);

static ssize_t
show_fc_vport_roles (struct device *dev, struct device_attribute *attr,
       char *buf)
{
 struct fc_vport *vport = transport_class_to_vport(dev);

 if (vport->roles == FC_PORT_ROLE_UNKNOWN)
  return snprintf(buf, 20, "unknown\n");
 return get_fc_port_roles_names(vport->roles, buf);
}
static FC_DEVICE_ATTR(vport, roles, S_IRUGO, show_fc_vport_roles, NULL);

fc_private_vport_rd_enum_attr(vport_type, FC_PORTTYPE_MAX_NAMELEN);

fc_private_vport_show_function(symbolic_name, "%s\n",
  FC_VPORT_SYMBOLIC_NAMELEN + 1, )
fc_vport_store_str_function(symbolic_name, FC_VPORT_SYMBOLIC_NAMELEN)
static FC_DEVICE_ATTR(vport, symbolic_name, S_IRUGO | S_IWUSR,
  show_fc_vport_symbolic_name, store_fc_vport_symbolic_name);

static ssize_t
store_fc_vport_delete(struct device *dev, struct device_attribute *attr,
        const char *buf, size_t count)
{
 struct fc_vport *vport = transport_class_to_vport(dev);
 struct Scsi_Host *shost = vport_to_shost(vport);
 unsigned long flags;

 spin_lock_irqsave(shost->host_lock, flags);
 if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) {
  spin_unlock_irqrestore(shost->host_lock, flags);
  return -EBUSY;
 }
 vport->flags |= FC_VPORT_DELETING;
 spin_unlock_irqrestore(shost->host_lock, flags);

 fc_queue_work(shost, &vport->vport_delete_work);
 return count;
}
static FC_DEVICE_ATTR(vport, vport_delete, S_IWUSR,
   NULL, store_fc_vport_delete);


/*
 * Enable/Disable vport
 *  Write "1" to disable, write "0" to enable
 */

static ssize_t
store_fc_vport_disable(struct device *dev, struct device_attribute *attr,
         const char *buf,
      size_t count)
{
 struct fc_vport *vport = transport_class_to_vport(dev);
 struct Scsi_Host *shost = vport_to_shost(vport);
 struct fc_internal *i = to_fc_internal(shost->transportt);
 int stat;

 if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
  return -EBUSY;

 if (*buf == '0') {
  if (vport->vport_state != FC_VPORT_DISABLED)
   return -EALREADY;
 } else if (*buf == '1') {
  if (vport->vport_state == FC_VPORT_DISABLED)
   return -EALREADY;
 } else
  return -EINVAL;

 stat = i->f->vport_disable(vport, ((*buf == '0') ? false : true));
 return stat ? stat : count;
}
static FC_DEVICE_ATTR(vport, vport_disable, S_IWUSR,
   NULL, store_fc_vport_disable);


/*
 * Host Attribute Management
 */


#define fc_host_show_function(field, format_string, sz, cast)  \
static ssize_t        \
show_fc_host_##field (struct device *dev,    \
        struct device_attribute *attr, char *buf)  \
{         \
 struct Scsi_Host *shost = transport_class_to_shost(dev); \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 if (i->f->get_host_##field)     \
  i->f->get_host_##field(shost);    \
 return snprintf(buf, sz, format_string, cast fc_host_##field(shost)); \
}

#define fc_host_store_function(field)     \
static ssize_t        \
store_fc_host_##field(struct device *dev,     \
        struct device_attribute *attr,   \
        const char *buf, size_t count)   \
{         \
 int val;       \
 struct Scsi_Host *shost = transport_class_to_shost(dev); \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 char *cp;       \
         \
 val = simple_strtoul(buf, &cp, 0);    \
 if (*cp && (*cp != '\n'))     \
  return -EINVAL;      \
 i->f->set_host_##field(shost, val);    \
 return count;       \
}

#define fc_host_store_str_function(field, slen)    \
static ssize_t        \
store_fc_host_##field(struct device *dev,    \
        struct device_attribute *attr,   \
        const char *buf, size_t count)   \
{         \
 struct Scsi_Host *shost = transport_class_to_shost(dev); \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 unsigned int cnt=count;      \
         \
 /* count may include a LF at end of string */ \
 if (buf[cnt-1] == '\n')      \
  cnt--;       \
 if (cnt > ((slen) - 1))      \
  return -EINVAL;      \
 memcpy(fc_host_##field(shost), buf, cnt);   \
 i->f->set_host_##field(shost);     \
 return count;       \
}

#define fc_host_rd_attr(field, format_string, sz)   \
 fc_host_show_function(field, format_string, sz, )  \
static FC_DEVICE_ATTR(host, field, S_IRUGO,   \
    show_fc_host_##field, NULL)

#define fc_host_rd_attr_cast(field, format_string, sz, cast)  \
 fc_host_show_function(field, format_string, sz, (cast))  \
static FC_DEVICE_ATTR(host, field, S_IRUGO,   \
     show_fc_host_##field, NULL)

#define fc_host_rw_attr(field, format_string, sz)   \
 fc_host_show_function(field, format_string, sz, )  \
 fc_host_store_function(field)     \
static FC_DEVICE_ATTR(host, field, S_IRUGO | S_IWUSR,  \
   show_fc_host_##field,    \
   store_fc_host_##field)

#define fc_host_rd_enum_attr(title, maxlen)    \
static ssize_t        \
show_fc_host_##title (struct device *dev,    \
        struct device_attribute *attr, char *buf)  \
{         \
 struct Scsi_Host *shost = transport_class_to_shost(dev); \
 struct fc_internal *i = to_fc_internal(shost->transportt); \
 const char *name;      \
 if (i->f->get_host_##title)     \
  i->f->get_host_##title(shost);    \
 name = get_fc_##title##_name(fc_host_##title(shost));  \
 if (!name)       \
  return -EINVAL;      \
 return snprintf(buf, maxlen, "%s\n", name);   \
}         \
static FC_DEVICE_ATTR(host, title, S_IRUGO, show_fc_host_##title, NULL)

#define SETUP_HOST_ATTRIBUTE_RD(field)     \
 i->private_host_attrs[count] = device_attr_host_##field; \
 i->private_host_attrs[count].attr.mode = S_IRUGO;  \
 i->private_host_attrs[count].store = NULL;   \
 i->host_attrs[count] = &i->private_host_attrs[count];  \
 if (i->f->show_host_##field)     \
  count++

#define SETUP_HOST_ATTRIBUTE_RD_NS(field)    \
 i->private_host_attrs[count] = device_attr_host_##field; \
 i->private_host_attrs[count].attr.mode = S_IRUGO;  \
 i->private_host_attrs[count].store = NULL;   \
 i->host_attrs[count] = &i->private_host_attrs[count];  \
 count++

#define SETUP_HOST_ATTRIBUTE_RW(field)     \
 i->private_host_attrs[count] = device_attr_host_##field; \
 if (!i->f->set_host_##field) {     \
  i->private_host_attrs[count].attr.mode = S_IRUGO; \
  i->private_host_attrs[count].store = NULL;  \
 }        \
 i->host_attrs[count] = &i->private_host_attrs[count];  \
 if (i->f->show_host_##field)     \
  count++


#define fc_private_host_show_function(field, format_string, sz, cast) \
static ssize_t        \
show_fc_host_##field (struct device *dev,    \
        struct device_attribute *attr, char *buf)  \
{         \
 struct Scsi_Host *shost = transport_class_to_shost(dev); \
 return snprintf(buf, sz, format_string, cast fc_host_##field(shost)); \
}

#define fc_private_host_rd_attr(field, format_string, sz)  \
 fc_private_host_show_function(field, format_string, sz, ) \
static FC_DEVICE_ATTR(host, field, S_IRUGO,   \
    show_fc_host_##field, NULL)

#define fc_private_host_rd_attr_cast(field, format_string, sz, cast) \
 fc_private_host_show_function(field, format_string, sz, (cast)) \
static FC_DEVICE_ATTR(host, field, S_IRUGO,   \
     show_fc_host_##field, NULL)

#define SETUP_PRIVATE_HOST_ATTRIBUTE_RD(field)   \
 i->private_host_attrs[count] = device_attr_host_##field; \
 i->private_host_attrs[count].attr.mode = S_IRUGO;  \
 i->private_host_attrs[count].store = NULL;   \
 i->host_attrs[count] = &i->private_host_attrs[count];  \
 count++

#define SETUP_PRIVATE_HOST_ATTRIBUTE_RW(field)   \
{         \
 i->private_host_attrs[count] = device_attr_host_##field; \
 i->host_attrs[count] = &i->private_host_attrs[count];  \
 count++;       \
}


/* Fixed Host Attributes */

static ssize_t
show_fc_host_supported_classes (struct device *dev,
           struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);

 if (fc_host_supported_classes(shost) == FC_COS_UNSPECIFIED)
  return snprintf(buf, 20, "unspecified\n");

 return get_fc_cos_names(fc_host_supported_classes(shost), buf);
}
static FC_DEVICE_ATTR(host, supported_classes, S_IRUGO,
  show_fc_host_supported_classes, NULL);

static ssize_t
show_fc_host_supported_fc4s (struct device *dev,
        struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 return (ssize_t)show_fc_fc4s(buf, fc_host_supported_fc4s(shost));
}
static FC_DEVICE_ATTR(host, supported_fc4s, S_IRUGO,
  show_fc_host_supported_fc4s, NULL);

static ssize_t
show_fc_host_supported_speeds (struct device *dev,
          struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);

 if (fc_host_supported_speeds(shost) == FC_PORTSPEED_UNKNOWN)
  return snprintf(buf, 20, "unknown\n");

 return get_fc_port_speed_names(fc_host_supported_speeds(shost), buf);
}
static FC_DEVICE_ATTR(host, supported_speeds, S_IRUGO,
  show_fc_host_supported_speeds, NULL);


fc_private_host_rd_attr_cast(node_name, "0x%llx\n", 20, unsigned long long);
fc_private_host_rd_attr_cast(port_name, "0x%llx\n", 20, unsigned long long);
fc_private_host_rd_attr_cast(permanent_port_name, "0x%llx\n", 20,
        unsigned long long);
fc_private_host_rd_attr(maxframe_size, "%u bytes\n", 20);
fc_private_host_rd_attr(max_npiv_vports, "%u\n", 20);
fc_private_host_rd_attr(serial_number, "%s\n", (FC_SERIAL_NUMBER_SIZE +1));
fc_private_host_rd_attr(manufacturer, "%s\n", FC_SERIAL_NUMBER_SIZE + 1);
fc_private_host_rd_attr(model, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
fc_private_host_rd_attr(model_description, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);
fc_private_host_rd_attr(hardware_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
fc_private_host_rd_attr(driver_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
fc_private_host_rd_attr(firmware_version, "%s\n", FC_VERSION_STRING_SIZE + 1);
fc_private_host_rd_attr(optionrom_version, "%s\n", FC_VERSION_STRING_SIZE + 1);


/* Dynamic Host Attributes */

static ssize_t
show_fc_host_active_fc4s (struct device *dev,
     struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 struct fc_internal *i = to_fc_internal(shost->transportt);

 if (i->f->get_host_active_fc4s)
  i->f->get_host_active_fc4s(shost);

 return (ssize_t)show_fc_fc4s(buf, fc_host_active_fc4s(shost));
}
static FC_DEVICE_ATTR(host, active_fc4s, S_IRUGO,
  show_fc_host_active_fc4s, NULL);

static ssize_t
show_fc_host_speed (struct device *dev,
      struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 struct fc_internal *i = to_fc_internal(shost->transportt);

 if (i->f->get_host_speed)
  i->f->get_host_speed(shost);

 if (fc_host_speed(shost) == FC_PORTSPEED_UNKNOWN)
  return snprintf(buf, 20, "unknown\n");

 return get_fc_port_speed_names(fc_host_speed(shost), buf);
}
static FC_DEVICE_ATTR(host, speed, S_IRUGO,
  show_fc_host_speed, NULL);


fc_host_rd_attr(port_id, "0x%06x\n", 20);
fc_host_rd_enum_attr(port_type, FC_PORTTYPE_MAX_NAMELEN);
fc_host_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
fc_host_rd_attr_cast(fabric_name, "0x%llx\n", 20, unsigned long long);
fc_host_rd_attr(symbolic_name, "%s\n", FC_SYMBOLIC_NAME_SIZE + 1);

fc_private_host_show_function(system_hostname, "%s\n",
  FC_SYMBOLIC_NAME_SIZE + 1, )
fc_host_store_str_function(system_hostname, FC_SYMBOLIC_NAME_SIZE)
static FC_DEVICE_ATTR(host, system_hostname, S_IRUGO | S_IWUSR,
  show_fc_host_system_hostname, store_fc_host_system_hostname);


/* Private Host Attributes */

static ssize_t
show_fc_private_host_tgtid_bind_type(struct device *dev,
         struct device_attribute *attr, char *buf)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 const char *name;

 name = get_fc_tgtid_bind_type_name(fc_host_tgtid_bind_type(shost));
 if (!name)
  return -EINVAL;
 return snprintf(buf, FC_BINDTYPE_MAX_NAMELEN, "%s\n", name);
}

#define get_list_head_entry(pos, head, member)   \
 pos = list_entry((head)->next, typeof(*pos), member)

static ssize_t
store_fc_private_host_tgtid_bind_type(struct device *dev,
 struct device_attribute *attr, const char *buf, size_t count)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 struct fc_rport *rport;
  enum fc_tgtid_binding_type val;
 unsigned long flags;

 if (get_fc_tgtid_bind_type_match(buf, &val))
  return -EINVAL;

 /* if changing bind type, purge all unused consistent bindings */
 if (val != fc_host_tgtid_bind_type(shost)) {
  spin_lock_irqsave(shost->host_lock, flags);
  while (!list_empty(&fc_host_rport_bindings(shost))) {
   get_list_head_entry(rport,
    &fc_host_rport_bindings(shost), peers);
   list_del(&rport->peers);
   rport->port_state = FC_PORTSTATE_DELETED;
   fc_queue_work(shost, &rport->rport_delete_work);
  }
  spin_unlock_irqrestore(shost->host_lock, flags);
 }

 fc_host_tgtid_bind_type(shost) = val;
 return count;
}

static FC_DEVICE_ATTR(host, tgtid_bind_type, S_IRUGO | S_IWUSR,
   show_fc_private_host_tgtid_bind_type,
   store_fc_private_host_tgtid_bind_type);

static ssize_t
store_fc_private_host_issue_lip(struct device *dev,
 struct device_attribute *attr, const char *buf, size_t count)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 struct fc_internal *i = to_fc_internal(shost->transportt);
 int ret;

 /* ignore any data value written to the attribute */
 if (i->f->issue_fc_host_lip) {
  ret = i->f->issue_fc_host_lip(shost);
  return ret ? ret: count;
 }

 return -ENOENT;
}

static FC_DEVICE_ATTR(host, issue_lip, S_IWUSR, NULL,
   store_fc_private_host_issue_lip);

static ssize_t
store_fc_private_host_dev_loss_tmo(struct device *dev,
       struct device_attribute *attr,
       const char *buf, size_t count)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
 struct fc_rport *rport;
 unsigned long val, flags;
 int rc;

 rc = fc_str_to_dev_loss(buf, &val);
 if (rc)
  return rc;

 fc_host_dev_loss_tmo(shost) = val;
 spin_lock_irqsave(shost->host_lock, flags);
 list_for_each_entry(rport, &fc_host->rports, peers)
  fc_rport_set_dev_loss_tmo(rport, val);
 spin_unlock_irqrestore(shost->host_lock, flags);
 return count;
}

fc_private_host_show_function(dev_loss_tmo, "%d\n", 20, );
static FC_DEVICE_ATTR(host, dev_loss_tmo, S_IRUGO | S_IWUSR,
        show_fc_host_dev_loss_tmo,
        store_fc_private_host_dev_loss_tmo);

fc_private_host_rd_attr(npiv_vports_inuse, "%u\n", 20);

/*
 * Host Statistics Management
 */


/* Show a given attribute in the statistics group */
static ssize_t
fc_stat_show(const struct device *dev, char *buf, unsigned long offset)
{
 struct Scsi_Host *shost = transport_class_to_shost(dev);
 struct fc_internal *i = to_fc_internal(shost->transportt);
 struct fc_host_statistics *stats;
 ssize_t ret = -ENOENT;

 if (offset > sizeof(struct fc_host_statistics) ||
     offset % sizeof(u64) != 0)
  WARN_ON(1);

 if (i->f->get_fc_host_stats) {
  stats = (i->f->get_fc_host_stats)(shost);
  if (stats)
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=94 G=93

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Normalansicht

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge