Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/scsi/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 149 kB image not shown  

Quelle  scsi_transport_iscsi.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * iSCSI transport class definitions
 *
 * Copyright (C) IBM Corporation, 2004
 * Copyright (C) Mike Christie, 2004 - 2005
 * Copyright (C) Dmitry Yusupov, 2004 - 2005
 * Copyright (C) Alex Aizman, 2004 - 2005
 */

#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/bsg-lib.h>
#include <linux/idr.h>
#include <net/tcp.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/iscsi_if.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_bsg_iscsi.h>

#define ISCSI_TRANSPORT_VERSION "2.0-870"

#define ISCSI_SEND_MAX_ALLOWED  10

#define CREATE_TRACE_POINTS
#include <trace/events/iscsi.h>

/*
 * Export tracepoint symbols to be used by other modules.
 */

EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_conn);
EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_eh);
EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_session);
EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_tcp);
EXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_sw_tcp);

static int dbg_session;
module_param_named(debug_session, dbg_session, int,
     S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug_session,
   "Turn on debugging for sessions in scsi_transport_iscsi "
   "module. Set to 1 to turn on, and zero to turn off. Default "
   "is off.");

static int dbg_conn;
module_param_named(debug_conn, dbg_conn, int,
     S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug_conn,
   "Turn on debugging for connections in scsi_transport_iscsi "
   "module. Set to 1 to turn on, and zero to turn off. Default "
   "is off.");

#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)  \
 do {        \
  if (dbg_session)     \
   iscsi_cls_session_printk(KERN_INFO, _session, \
       "%s: " dbg_fmt, \
       __func__, ##arg); \
  iscsi_dbg_trace(trace_iscsi_dbg_trans_session,  \
    &(_session)->dev,   \
    "%s " dbg_fmt, __func__, ##arg); \
 } while (0);

#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)   \
 do {        \
  if (dbg_conn)      \
   iscsi_cls_conn_printk(KERN_INFO, _conn,  \
           "%s: " dbg_fmt,  \
           __func__, ##arg);  \
  iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,  \
    &(_conn)->dev,    \
    "%s " dbg_fmt, __func__, ##arg); \
 } while (0);

struct iscsi_internal {
 struct scsi_transport_template t;
 struct iscsi_transport *iscsi_transport;
 struct list_head list;
 struct device dev;

 struct transport_container conn_cont;
 struct transport_container session_cont;
};

static DEFINE_IDR(iscsi_ep_idr);
static DEFINE_MUTEX(iscsi_ep_idr_mutex);

static atomic_t iscsi_session_nr; /* sysfs session id for next new session */

static struct workqueue_struct *iscsi_conn_cleanup_workq;

static DEFINE_IDA(iscsi_sess_ida);
/*
 * list of registered transports and lock that must
 * be held while accessing list. The iscsi_transport_lock must
 * be acquired after the rx_queue_mutex.
 */

static LIST_HEAD(iscsi_transports);
static DEFINE_SPINLOCK(iscsi_transport_lock);

#define to_iscsi_internal(tmpl) \
 container_of(tmpl, struct iscsi_internal, t)

#define dev_to_iscsi_internal(_dev) \
 container_of(_dev, struct iscsi_internal, dev)

static void iscsi_transport_release(struct device *dev)
{
 struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
 kfree(priv);
}

/*
 * iscsi_transport_class represents the iscsi_transports that are
 * registered.
 */

static struct class iscsi_transport_class = {
 .name = "iscsi_transport",
 .dev_release = iscsi_transport_release,
};

static ssize_t
show_transport_handle(struct device *dev, struct device_attribute *attr,
        char *buf)
{
 struct iscsi_internal *priv = dev_to_iscsi_internal(dev);

 if (!capable(CAP_SYS_ADMIN))
  return -EACCES;
 return sysfs_emit(buf, "%llu\n",
    (unsigned long long)iscsi_handle(priv->iscsi_transport));
}
static DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);

#define show_transport_attr(name, format)    \
static ssize_t        \
show_transport_##name(struct device *dev,     \
        struct device_attribute *attr,char *buf)  \
{         \
 struct iscsi_internal *priv = dev_to_iscsi_internal(dev); \
 return sysfs_emit(buf, format"\n", priv->iscsi_transport->name);\
}         \
static DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);

show_transport_attr(caps, "0x%x");

static struct attribute *iscsi_transport_attrs[] = {
 &dev_attr_handle.attr,
 &dev_attr_caps.attr,
 NULL,
};

static struct attribute_group iscsi_transport_group = {
 .attrs = iscsi_transport_attrs,
};

/*
 * iSCSI endpoint attrs
 */

#define iscsi_dev_to_endpoint(_dev) \
 container_of(_dev, struct iscsi_endpoint, dev)

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

static void iscsi_endpoint_release(struct device *dev)
{
 struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);

 mutex_lock(&iscsi_ep_idr_mutex);
 idr_remove(&iscsi_ep_idr, ep->id);
 mutex_unlock(&iscsi_ep_idr_mutex);

 kfree(ep);
}

static struct class iscsi_endpoint_class = {
 .name = "iscsi_endpoint",
 .dev_release = iscsi_endpoint_release,
};

static ssize_t
show_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
{
 struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
 return sysfs_emit(buf, "%d\n", ep->id);
}
static ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);

static struct attribute *iscsi_endpoint_attrs[] = {
 &dev_attr_ep_handle.attr,
 NULL,
};

static struct attribute_group iscsi_endpoint_group = {
 .attrs = iscsi_endpoint_attrs,
};

struct iscsi_endpoint *
iscsi_create_endpoint(int dd_size)
{
 struct iscsi_endpoint *ep;
 int err, id;

 ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
 if (!ep)
  return NULL;

 mutex_lock(&iscsi_ep_idr_mutex);

 /*
 * First endpoint id should be 1 to comply with user space
 * applications (iscsid).
 */

 id = idr_alloc(&iscsi_ep_idr, ep, 1, -1, GFP_NOIO);
 if (id < 0) {
  mutex_unlock(&iscsi_ep_idr_mutex);
  printk(KERN_ERR "Could not allocate endpoint ID. Error %d.\n",
         id);
  goto free_ep;
 }
 mutex_unlock(&iscsi_ep_idr_mutex);

 ep->id = id;
 ep->dev.class = &iscsi_endpoint_class;
 dev_set_name(&ep->dev, "ep-%d", id);
 err = device_register(&ep->dev);
        if (err)
  goto put_dev;

 err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
 if (err)
  goto unregister_dev;

 if (dd_size)
  ep->dd_data = &ep[1];
 return ep;

unregister_dev:
 device_unregister(&ep->dev);
 return NULL;

put_dev:
 mutex_lock(&iscsi_ep_idr_mutex);
 idr_remove(&iscsi_ep_idr, id);
 mutex_unlock(&iscsi_ep_idr_mutex);
 put_device(&ep->dev);
 return NULL;
free_ep:
 kfree(ep);
 return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_create_endpoint);

void iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
{
 sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
 device_unregister(&ep->dev);
}
EXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);

void iscsi_put_endpoint(struct iscsi_endpoint *ep)
{
 put_device(&ep->dev);
}
EXPORT_SYMBOL_GPL(iscsi_put_endpoint);

/**
 * iscsi_lookup_endpoint - get ep from handle
 * @handle: endpoint handle
 *
 * Caller must do a iscsi_put_endpoint.
 */

struct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
{
 struct iscsi_endpoint *ep;

 mutex_lock(&iscsi_ep_idr_mutex);
 ep = idr_find(&iscsi_ep_idr, handle);
 if (!ep)
  goto unlock;

 get_device(&ep->dev);
unlock:
 mutex_unlock(&iscsi_ep_idr_mutex);
 return ep;
}
EXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);

/*
 * Interface to display network param to sysfs
 */


static void iscsi_iface_release(struct device *dev)
{
 struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
 struct device *parent = iface->dev.parent;

 kfree(iface);
 put_device(parent);
}


static struct class iscsi_iface_class = {
 .name = "iscsi_iface",
 .dev_release = iscsi_iface_release,
};

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

/* iface attrs show */
#define iscsi_iface_attr_show(type, name, param_type, param)  \
static ssize_t        \
show_##type##_##name(struct device *dev, struct device_attribute *attr, \
       char *buf)      \
{         \
 struct iscsi_iface *iface = iscsi_dev_to_iface(dev);  \
 struct iscsi_transport *t = iface->transport;   \
 return t->get_iface_param(iface, param_type, param, buf); \
}         \

#define iscsi_iface_net_attr(type, name, param)    \
 iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param) \
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);

#define iscsi_iface_attr(type, name, param)    \
 iscsi_iface_attr_show(type, name, ISCSI_IFACE_PARAM, param) \
static ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);

/* generic read only ipv4 attribute */
iscsi_iface_net_attr(ipv4_iface, ipaddress, ISCSI_NET_PARAM_IPV4_ADDR);
iscsi_iface_net_attr(ipv4_iface, gateway, ISCSI_NET_PARAM_IPV4_GW);
iscsi_iface_net_attr(ipv4_iface, subnet, ISCSI_NET_PARAM_IPV4_SUBNET);
iscsi_iface_net_attr(ipv4_iface, bootproto, ISCSI_NET_PARAM_IPV4_BOOTPROTO);
iscsi_iface_net_attr(ipv4_iface, dhcp_dns_address_en,
       ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN);
iscsi_iface_net_attr(ipv4_iface, dhcp_slp_da_info_en,
       ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN);
iscsi_iface_net_attr(ipv4_iface, tos_en, ISCSI_NET_PARAM_IPV4_TOS_EN);
iscsi_iface_net_attr(ipv4_iface, tos, ISCSI_NET_PARAM_IPV4_TOS);
iscsi_iface_net_attr(ipv4_iface, grat_arp_en,
       ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN);
iscsi_iface_net_attr(ipv4_iface, dhcp_alt_client_id_en,
       ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN);
iscsi_iface_net_attr(ipv4_iface, dhcp_alt_client_id,
       ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID);
iscsi_iface_net_attr(ipv4_iface, dhcp_req_vendor_id_en,
       ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN);
iscsi_iface_net_attr(ipv4_iface, dhcp_use_vendor_id_en,
       ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN);
iscsi_iface_net_attr(ipv4_iface, dhcp_vendor_id,
       ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID);
iscsi_iface_net_attr(ipv4_iface, dhcp_learn_iqn_en,
       ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN);
iscsi_iface_net_attr(ipv4_iface, fragment_disable,
       ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE);
iscsi_iface_net_attr(ipv4_iface, incoming_forwarding_en,
       ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN);
iscsi_iface_net_attr(ipv4_iface, ttl, ISCSI_NET_PARAM_IPV4_TTL);

/* generic read only ipv6 attribute */
iscsi_iface_net_attr(ipv6_iface, ipaddress, ISCSI_NET_PARAM_IPV6_ADDR);
iscsi_iface_net_attr(ipv6_iface, link_local_addr,
       ISCSI_NET_PARAM_IPV6_LINKLOCAL);
iscsi_iface_net_attr(ipv6_iface, router_addr, ISCSI_NET_PARAM_IPV6_ROUTER);
iscsi_iface_net_attr(ipv6_iface, ipaddr_autocfg,
       ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG);
iscsi_iface_net_attr(ipv6_iface, link_local_autocfg,
       ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG);
iscsi_iface_net_attr(ipv6_iface, link_local_state,
       ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE);
iscsi_iface_net_attr(ipv6_iface, router_state,
       ISCSI_NET_PARAM_IPV6_ROUTER_STATE);
iscsi_iface_net_attr(ipv6_iface, grat_neighbor_adv_en,
       ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN);
iscsi_iface_net_attr(ipv6_iface, mld_en, ISCSI_NET_PARAM_IPV6_MLD_EN);
iscsi_iface_net_attr(ipv6_iface, flow_label, ISCSI_NET_PARAM_IPV6_FLOW_LABEL);
iscsi_iface_net_attr(ipv6_iface, traffic_class,
       ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS);
iscsi_iface_net_attr(ipv6_iface, hop_limit, ISCSI_NET_PARAM_IPV6_HOP_LIMIT);
iscsi_iface_net_attr(ipv6_iface, nd_reachable_tmo,
       ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO);
iscsi_iface_net_attr(ipv6_iface, nd_rexmit_time,
       ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME);
iscsi_iface_net_attr(ipv6_iface, nd_stale_tmo,
       ISCSI_NET_PARAM_IPV6_ND_STALE_TMO);
iscsi_iface_net_attr(ipv6_iface, dup_addr_detect_cnt,
       ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT);
iscsi_iface_net_attr(ipv6_iface, router_adv_link_mtu,
       ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU);

/* common read only iface attribute */
iscsi_iface_net_attr(iface, enabled, ISCSI_NET_PARAM_IFACE_ENABLE);
iscsi_iface_net_attr(iface, vlan_id, ISCSI_NET_PARAM_VLAN_ID);
iscsi_iface_net_attr(iface, vlan_priority, ISCSI_NET_PARAM_VLAN_PRIORITY);
iscsi_iface_net_attr(iface, vlan_enabled, ISCSI_NET_PARAM_VLAN_ENABLED);
iscsi_iface_net_attr(iface, mtu, ISCSI_NET_PARAM_MTU);
iscsi_iface_net_attr(iface, port, ISCSI_NET_PARAM_PORT);
iscsi_iface_net_attr(iface, ipaddress_state, ISCSI_NET_PARAM_IPADDR_STATE);
iscsi_iface_net_attr(iface, delayed_ack_en, ISCSI_NET_PARAM_DELAYED_ACK_EN);
iscsi_iface_net_attr(iface, tcp_nagle_disable,
       ISCSI_NET_PARAM_TCP_NAGLE_DISABLE);
iscsi_iface_net_attr(iface, tcp_wsf_disable, ISCSI_NET_PARAM_TCP_WSF_DISABLE);
iscsi_iface_net_attr(iface, tcp_wsf, ISCSI_NET_PARAM_TCP_WSF);
iscsi_iface_net_attr(iface, tcp_timer_scale, ISCSI_NET_PARAM_TCP_TIMER_SCALE);
iscsi_iface_net_attr(iface, tcp_timestamp_en, ISCSI_NET_PARAM_TCP_TIMESTAMP_EN);
iscsi_iface_net_attr(iface, cache_id, ISCSI_NET_PARAM_CACHE_ID);
iscsi_iface_net_attr(iface, redirect_en, ISCSI_NET_PARAM_REDIRECT_EN);

/* common iscsi specific settings attributes */
iscsi_iface_attr(iface, def_taskmgmt_tmo, ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO);
iscsi_iface_attr(iface, header_digest, ISCSI_IFACE_PARAM_HDRDGST_EN);
iscsi_iface_attr(iface, data_digest, ISCSI_IFACE_PARAM_DATADGST_EN);
iscsi_iface_attr(iface, immediate_data, ISCSI_IFACE_PARAM_IMM_DATA_EN);
iscsi_iface_attr(iface, initial_r2t, ISCSI_IFACE_PARAM_INITIAL_R2T_EN);
iscsi_iface_attr(iface, data_seq_in_order,
   ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN);
iscsi_iface_attr(iface, data_pdu_in_order, ISCSI_IFACE_PARAM_PDU_INORDER_EN);
iscsi_iface_attr(iface, erl, ISCSI_IFACE_PARAM_ERL);
iscsi_iface_attr(iface, max_recv_dlength, ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH);
iscsi_iface_attr(iface, first_burst_len, ISCSI_IFACE_PARAM_FIRST_BURST);
iscsi_iface_attr(iface, max_outstanding_r2t, ISCSI_IFACE_PARAM_MAX_R2T);
iscsi_iface_attr(iface, max_burst_len, ISCSI_IFACE_PARAM_MAX_BURST);
iscsi_iface_attr(iface, chap_auth, ISCSI_IFACE_PARAM_CHAP_AUTH_EN);
iscsi_iface_attr(iface, bidi_chap, ISCSI_IFACE_PARAM_BIDI_CHAP_EN);
iscsi_iface_attr(iface, discovery_auth_optional,
   ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL);
iscsi_iface_attr(iface, discovery_logout,
   ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN);
iscsi_iface_attr(iface, strict_login_comp_en,
   ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN);
iscsi_iface_attr(iface, initiator_name, ISCSI_IFACE_PARAM_INITIATOR_NAME);

static umode_t iscsi_iface_attr_is_visible(struct kobject *kobj,
       struct attribute *attr, int i)
{
 struct device *dev = container_of(kobj, struct device, kobj);
 struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
 struct iscsi_transport *t = iface->transport;
 int param = -1;

 if (attr == &dev_attr_iface_def_taskmgmt_tmo.attr)
  param = ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO;
 else if (attr == &dev_attr_iface_header_digest.attr)
  param = ISCSI_IFACE_PARAM_HDRDGST_EN;
 else if (attr == &dev_attr_iface_data_digest.attr)
  param = ISCSI_IFACE_PARAM_DATADGST_EN;
 else if (attr == &dev_attr_iface_immediate_data.attr)
  param = ISCSI_IFACE_PARAM_IMM_DATA_EN;
 else if (attr == &dev_attr_iface_initial_r2t.attr)
  param = ISCSI_IFACE_PARAM_INITIAL_R2T_EN;
 else if (attr == &dev_attr_iface_data_seq_in_order.attr)
  param = ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN;
 else if (attr == &dev_attr_iface_data_pdu_in_order.attr)
  param = ISCSI_IFACE_PARAM_PDU_INORDER_EN;
 else if (attr == &dev_attr_iface_erl.attr)
  param = ISCSI_IFACE_PARAM_ERL;
 else if (attr == &dev_attr_iface_max_recv_dlength.attr)
  param = ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH;
 else if (attr == &dev_attr_iface_first_burst_len.attr)
  param = ISCSI_IFACE_PARAM_FIRST_BURST;
 else if (attr == &dev_attr_iface_max_outstanding_r2t.attr)
  param = ISCSI_IFACE_PARAM_MAX_R2T;
 else if (attr == &dev_attr_iface_max_burst_len.attr)
  param = ISCSI_IFACE_PARAM_MAX_BURST;
 else if (attr == &dev_attr_iface_chap_auth.attr)
  param = ISCSI_IFACE_PARAM_CHAP_AUTH_EN;
 else if (attr == &dev_attr_iface_bidi_chap.attr)
  param = ISCSI_IFACE_PARAM_BIDI_CHAP_EN;
 else if (attr == &dev_attr_iface_discovery_auth_optional.attr)
  param = ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL;
 else if (attr == &dev_attr_iface_discovery_logout.attr)
  param = ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN;
 else if (attr == &dev_attr_iface_strict_login_comp_en.attr)
  param = ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN;
 else if (attr == &dev_attr_iface_initiator_name.attr)
  param = ISCSI_IFACE_PARAM_INITIATOR_NAME;

 if (param != -1)
  return t->attr_is_visible(ISCSI_IFACE_PARAM, param);

 if (attr == &dev_attr_iface_enabled.attr)
  param = ISCSI_NET_PARAM_IFACE_ENABLE;
 else if (attr == &dev_attr_iface_vlan_id.attr)
  param = ISCSI_NET_PARAM_VLAN_ID;
 else if (attr == &dev_attr_iface_vlan_priority.attr)
  param = ISCSI_NET_PARAM_VLAN_PRIORITY;
 else if (attr == &dev_attr_iface_vlan_enabled.attr)
  param = ISCSI_NET_PARAM_VLAN_ENABLED;
 else if (attr == &dev_attr_iface_mtu.attr)
  param = ISCSI_NET_PARAM_MTU;
 else if (attr == &dev_attr_iface_port.attr)
  param = ISCSI_NET_PARAM_PORT;
 else if (attr == &dev_attr_iface_ipaddress_state.attr)
  param = ISCSI_NET_PARAM_IPADDR_STATE;
 else if (attr == &dev_attr_iface_delayed_ack_en.attr)
  param = ISCSI_NET_PARAM_DELAYED_ACK_EN;
 else if (attr == &dev_attr_iface_tcp_nagle_disable.attr)
  param = ISCSI_NET_PARAM_TCP_NAGLE_DISABLE;
 else if (attr == &dev_attr_iface_tcp_wsf_disable.attr)
  param = ISCSI_NET_PARAM_TCP_WSF_DISABLE;
 else if (attr == &dev_attr_iface_tcp_wsf.attr)
  param = ISCSI_NET_PARAM_TCP_WSF;
 else if (attr == &dev_attr_iface_tcp_timer_scale.attr)
  param = ISCSI_NET_PARAM_TCP_TIMER_SCALE;
 else if (attr == &dev_attr_iface_tcp_timestamp_en.attr)
  param = ISCSI_NET_PARAM_TCP_TIMESTAMP_EN;
 else if (attr == &dev_attr_iface_cache_id.attr)
  param = ISCSI_NET_PARAM_CACHE_ID;
 else if (attr == &dev_attr_iface_redirect_en.attr)
  param = ISCSI_NET_PARAM_REDIRECT_EN;
 else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) {
  if (attr == &dev_attr_ipv4_iface_ipaddress.attr)
   param = ISCSI_NET_PARAM_IPV4_ADDR;
  else if (attr == &dev_attr_ipv4_iface_gateway.attr)
   param = ISCSI_NET_PARAM_IPV4_GW;
  else if (attr == &dev_attr_ipv4_iface_subnet.attr)
   param = ISCSI_NET_PARAM_IPV4_SUBNET;
  else if (attr == &dev_attr_ipv4_iface_bootproto.attr)
   param = ISCSI_NET_PARAM_IPV4_BOOTPROTO;
  else if (attr ==
    &dev_attr_ipv4_iface_dhcp_dns_address_en.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN;
  else if (attr ==
    &dev_attr_ipv4_iface_dhcp_slp_da_info_en.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN;
  else if (attr == &dev_attr_ipv4_iface_tos_en.attr)
   param = ISCSI_NET_PARAM_IPV4_TOS_EN;
  else if (attr == &dev_attr_ipv4_iface_tos.attr)
   param = ISCSI_NET_PARAM_IPV4_TOS;
  else if (attr == &dev_attr_ipv4_iface_grat_arp_en.attr)
   param = ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN;
  else if (attr ==
    &dev_attr_ipv4_iface_dhcp_alt_client_id_en.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN;
  else if (attr == &dev_attr_ipv4_iface_dhcp_alt_client_id.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID;
  else if (attr ==
    &dev_attr_ipv4_iface_dhcp_req_vendor_id_en.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN;
  else if (attr ==
    &dev_attr_ipv4_iface_dhcp_use_vendor_id_en.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN;
  else if (attr == &dev_attr_ipv4_iface_dhcp_vendor_id.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID;
  else if (attr ==
    &dev_attr_ipv4_iface_dhcp_learn_iqn_en.attr)
   param = ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN;
  else if (attr ==
    &dev_attr_ipv4_iface_fragment_disable.attr)
   param = ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE;
  else if (attr ==
    &dev_attr_ipv4_iface_incoming_forwarding_en.attr)
   param = ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN;
  else if (attr == &dev_attr_ipv4_iface_ttl.attr)
   param = ISCSI_NET_PARAM_IPV4_TTL;
  else
   return 0;
 } else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) {
  if (attr == &dev_attr_ipv6_iface_ipaddress.attr)
   param = ISCSI_NET_PARAM_IPV6_ADDR;
  else if (attr == &dev_attr_ipv6_iface_link_local_addr.attr)
   param = ISCSI_NET_PARAM_IPV6_LINKLOCAL;
  else if (attr == &dev_attr_ipv6_iface_router_addr.attr)
   param = ISCSI_NET_PARAM_IPV6_ROUTER;
  else if (attr == &dev_attr_ipv6_iface_ipaddr_autocfg.attr)
   param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG;
  else if (attr == &dev_attr_ipv6_iface_link_local_autocfg.attr)
   param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG;
  else if (attr == &dev_attr_ipv6_iface_link_local_state.attr)
   param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE;
  else if (attr == &dev_attr_ipv6_iface_router_state.attr)
   param = ISCSI_NET_PARAM_IPV6_ROUTER_STATE;
  else if (attr ==
    &dev_attr_ipv6_iface_grat_neighbor_adv_en.attr)
   param = ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN;
  else if (attr == &dev_attr_ipv6_iface_mld_en.attr)
   param = ISCSI_NET_PARAM_IPV6_MLD_EN;
  else if (attr == &dev_attr_ipv6_iface_flow_label.attr)
   param = ISCSI_NET_PARAM_IPV6_FLOW_LABEL;
  else if (attr == &dev_attr_ipv6_iface_traffic_class.attr)
   param = ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS;
  else if (attr == &dev_attr_ipv6_iface_hop_limit.attr)
   param = ISCSI_NET_PARAM_IPV6_HOP_LIMIT;
  else if (attr == &dev_attr_ipv6_iface_nd_reachable_tmo.attr)
   param = ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO;
  else if (attr == &dev_attr_ipv6_iface_nd_rexmit_time.attr)
   param = ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME;
  else if (attr == &dev_attr_ipv6_iface_nd_stale_tmo.attr)
   param = ISCSI_NET_PARAM_IPV6_ND_STALE_TMO;
  else if (attr == &dev_attr_ipv6_iface_dup_addr_detect_cnt.attr)
   param = ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT;
  else if (attr == &dev_attr_ipv6_iface_router_adv_link_mtu.attr)
   param = ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU;
  else
   return 0;
 } else {
  WARN_ONCE(1, "Invalid iface attr");
  return 0;
 }

 return t->attr_is_visible(ISCSI_NET_PARAM, param);
}

static struct attribute *iscsi_iface_attrs[] = {
 &dev_attr_iface_enabled.attr,
 &dev_attr_iface_vlan_id.attr,
 &dev_attr_iface_vlan_priority.attr,
 &dev_attr_iface_vlan_enabled.attr,
 &dev_attr_ipv4_iface_ipaddress.attr,
 &dev_attr_ipv4_iface_gateway.attr,
 &dev_attr_ipv4_iface_subnet.attr,
 &dev_attr_ipv4_iface_bootproto.attr,
 &dev_attr_ipv6_iface_ipaddress.attr,
 &dev_attr_ipv6_iface_link_local_addr.attr,
 &dev_attr_ipv6_iface_router_addr.attr,
 &dev_attr_ipv6_iface_ipaddr_autocfg.attr,
 &dev_attr_ipv6_iface_link_local_autocfg.attr,
 &dev_attr_iface_mtu.attr,
 &dev_attr_iface_port.attr,
 &dev_attr_iface_ipaddress_state.attr,
 &dev_attr_iface_delayed_ack_en.attr,
 &dev_attr_iface_tcp_nagle_disable.attr,
 &dev_attr_iface_tcp_wsf_disable.attr,
 &dev_attr_iface_tcp_wsf.attr,
 &dev_attr_iface_tcp_timer_scale.attr,
 &dev_attr_iface_tcp_timestamp_en.attr,
 &dev_attr_iface_cache_id.attr,
 &dev_attr_iface_redirect_en.attr,
 &dev_attr_iface_def_taskmgmt_tmo.attr,
 &dev_attr_iface_header_digest.attr,
 &dev_attr_iface_data_digest.attr,
 &dev_attr_iface_immediate_data.attr,
 &dev_attr_iface_initial_r2t.attr,
 &dev_attr_iface_data_seq_in_order.attr,
 &dev_attr_iface_data_pdu_in_order.attr,
 &dev_attr_iface_erl.attr,
 &dev_attr_iface_max_recv_dlength.attr,
 &dev_attr_iface_first_burst_len.attr,
 &dev_attr_iface_max_outstanding_r2t.attr,
 &dev_attr_iface_max_burst_len.attr,
 &dev_attr_iface_chap_auth.attr,
 &dev_attr_iface_bidi_chap.attr,
 &dev_attr_iface_discovery_auth_optional.attr,
 &dev_attr_iface_discovery_logout.attr,
 &dev_attr_iface_strict_login_comp_en.attr,
 &dev_attr_iface_initiator_name.attr,
 &dev_attr_ipv4_iface_dhcp_dns_address_en.attr,
 &dev_attr_ipv4_iface_dhcp_slp_da_info_en.attr,
 &dev_attr_ipv4_iface_tos_en.attr,
 &dev_attr_ipv4_iface_tos.attr,
 &dev_attr_ipv4_iface_grat_arp_en.attr,
 &dev_attr_ipv4_iface_dhcp_alt_client_id_en.attr,
 &dev_attr_ipv4_iface_dhcp_alt_client_id.attr,
 &dev_attr_ipv4_iface_dhcp_req_vendor_id_en.attr,
 &dev_attr_ipv4_iface_dhcp_use_vendor_id_en.attr,
 &dev_attr_ipv4_iface_dhcp_vendor_id.attr,
 &dev_attr_ipv4_iface_dhcp_learn_iqn_en.attr,
 &dev_attr_ipv4_iface_fragment_disable.attr,
 &dev_attr_ipv4_iface_incoming_forwarding_en.attr,
 &dev_attr_ipv4_iface_ttl.attr,
 &dev_attr_ipv6_iface_link_local_state.attr,
 &dev_attr_ipv6_iface_router_state.attr,
 &dev_attr_ipv6_iface_grat_neighbor_adv_en.attr,
 &dev_attr_ipv6_iface_mld_en.attr,
 &dev_attr_ipv6_iface_flow_label.attr,
 &dev_attr_ipv6_iface_traffic_class.attr,
 &dev_attr_ipv6_iface_hop_limit.attr,
 &dev_attr_ipv6_iface_nd_reachable_tmo.attr,
 &dev_attr_ipv6_iface_nd_rexmit_time.attr,
 &dev_attr_ipv6_iface_nd_stale_tmo.attr,
 &dev_attr_ipv6_iface_dup_addr_detect_cnt.attr,
 &dev_attr_ipv6_iface_router_adv_link_mtu.attr,
 NULL,
};

static struct attribute_group iscsi_iface_group = {
 .attrs = iscsi_iface_attrs,
 .is_visible = iscsi_iface_attr_is_visible,
};

/* convert iscsi_ipaddress_state values to ascii string name */
static const struct {
 enum iscsi_ipaddress_state value;
 char    *name;
} iscsi_ipaddress_state_names[] = {
 {ISCSI_IPDDRESS_STATE_UNCONFIGURED, "Unconfigured" },
 {ISCSI_IPDDRESS_STATE_ACQUIRING, "Acquiring" },
 {ISCSI_IPDDRESS_STATE_TENTATIVE, "Tentative" },
 {ISCSI_IPDDRESS_STATE_VALID,  "Valid" },
 {ISCSI_IPDDRESS_STATE_DISABLING, "Disabling" },
 {ISCSI_IPDDRESS_STATE_INVALID,  "Invalid" },
 {ISCSI_IPDDRESS_STATE_DEPRECATED, "Deprecated" },
};

char *iscsi_get_ipaddress_state_name(enum iscsi_ipaddress_state port_state)
{
 int i;
 char *state = NULL;

 for (i = 0; i < ARRAY_SIZE(iscsi_ipaddress_state_names); i++) {
  if (iscsi_ipaddress_state_names[i].value == port_state) {
   state = iscsi_ipaddress_state_names[i].name;
   break;
  }
 }
 return state;
}
EXPORT_SYMBOL_GPL(iscsi_get_ipaddress_state_name);

/* convert iscsi_router_state values to ascii string name */
static const struct {
 enum iscsi_router_state value;
 char   *name;
} iscsi_router_state_names[] = {
 {ISCSI_ROUTER_STATE_UNKNOWN,  "Unknown" },
 {ISCSI_ROUTER_STATE_ADVERTISED,  "Advertised" },
 {ISCSI_ROUTER_STATE_MANUAL,  "Manual" },
 {ISCSI_ROUTER_STATE_STALE,  "Stale" },
};

char *iscsi_get_router_state_name(enum iscsi_router_state router_state)
{
 int i;
 char *state = NULL;

 for (i = 0; i < ARRAY_SIZE(iscsi_router_state_names); i++) {
  if (iscsi_router_state_names[i].value == router_state) {
   state = iscsi_router_state_names[i].name;
   break;
  }
 }
 return state;
}
EXPORT_SYMBOL_GPL(iscsi_get_router_state_name);

struct iscsi_iface *
iscsi_create_iface(struct Scsi_Host *shost, struct iscsi_transport *transport,
     uint32_t iface_type, uint32_t iface_num, int dd_size)
{
 struct iscsi_iface *iface;
 int err;

 iface = kzalloc(sizeof(*iface) + dd_size, GFP_KERNEL);
 if (!iface)
  return NULL;

 iface->transport = transport;
 iface->iface_type = iface_type;
 iface->iface_num = iface_num;
 iface->dev.release = iscsi_iface_release;
 iface->dev.class = &iscsi_iface_class;
 /* parent reference released in iscsi_iface_release */
 iface->dev.parent = get_device(&shost->shost_gendev);
 if (iface_type == ISCSI_IFACE_TYPE_IPV4)
  dev_set_name(&iface->dev, "ipv4-iface-%u-%u", shost->host_no,
        iface_num);
 else
  dev_set_name(&iface->dev, "ipv6-iface-%u-%u", shost->host_no,
        iface_num);

 err = device_register(&iface->dev);
 if (err)
  goto put_dev;

 err = sysfs_create_group(&iface->dev.kobj, &iscsi_iface_group);
 if (err)
  goto unreg_iface;

 if (dd_size)
  iface->dd_data = &iface[1];
 return iface;

unreg_iface:
 device_unregister(&iface->dev);
 return NULL;

put_dev:
 put_device(&iface->dev);
 return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_create_iface);

void iscsi_destroy_iface(struct iscsi_iface *iface)
{
 sysfs_remove_group(&iface->dev.kobj, &iscsi_iface_group);
 device_unregister(&iface->dev);
}
EXPORT_SYMBOL_GPL(iscsi_destroy_iface);

/*
 * Interface to display flash node params to sysfs
 */


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

/* flash node session attrs show */
#define iscsi_flashnode_sess_attr_show(type, name, param)  \
static ssize_t        \
show_##type##_##name(struct device *dev, struct device_attribute *attr, \
       char *buf)      \
{         \
 struct iscsi_bus_flash_session *fnode_sess =   \
     iscsi_dev_to_flash_session(dev);\
 struct iscsi_transport *t = fnode_sess->transport;  \
 return t->get_flashnode_param(fnode_sess, param, buf);  \
}         \


#define iscsi_flashnode_sess_attr(type, name, param)   \
 iscsi_flashnode_sess_attr_show(type, name, param)  \
static ISCSI_FLASHNODE_ATTR(type, name, S_IRUGO,   \
       show_##type##_##name, NULL);

/* Flash node session attributes */

iscsi_flashnode_sess_attr(fnode, auto_snd_tgt_disable,
     ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE);
iscsi_flashnode_sess_attr(fnode, discovery_session,
     ISCSI_FLASHNODE_DISCOVERY_SESS);
iscsi_flashnode_sess_attr(fnode, portal_type, ISCSI_FLASHNODE_PORTAL_TYPE);
iscsi_flashnode_sess_attr(fnode, entry_enable, ISCSI_FLASHNODE_ENTRY_EN);
iscsi_flashnode_sess_attr(fnode, immediate_data, ISCSI_FLASHNODE_IMM_DATA_EN);
iscsi_flashnode_sess_attr(fnode, initial_r2t, ISCSI_FLASHNODE_INITIAL_R2T_EN);
iscsi_flashnode_sess_attr(fnode, data_seq_in_order,
     ISCSI_FLASHNODE_DATASEQ_INORDER);
iscsi_flashnode_sess_attr(fnode, data_pdu_in_order,
     ISCSI_FLASHNODE_PDU_INORDER);
iscsi_flashnode_sess_attr(fnode, chap_auth, ISCSI_FLASHNODE_CHAP_AUTH_EN);
iscsi_flashnode_sess_attr(fnode, discovery_logout,
     ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN);
iscsi_flashnode_sess_attr(fnode, bidi_chap, ISCSI_FLASHNODE_BIDI_CHAP_EN);
iscsi_flashnode_sess_attr(fnode, discovery_auth_optional,
     ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL);
iscsi_flashnode_sess_attr(fnode, erl, ISCSI_FLASHNODE_ERL);
iscsi_flashnode_sess_attr(fnode, first_burst_len, ISCSI_FLASHNODE_FIRST_BURST);
iscsi_flashnode_sess_attr(fnode, def_time2wait, ISCSI_FLASHNODE_DEF_TIME2WAIT);
iscsi_flashnode_sess_attr(fnode, def_time2retain,
     ISCSI_FLASHNODE_DEF_TIME2RETAIN);
iscsi_flashnode_sess_attr(fnode, max_outstanding_r2t, ISCSI_FLASHNODE_MAX_R2T);
iscsi_flashnode_sess_attr(fnode, isid, ISCSI_FLASHNODE_ISID);
iscsi_flashnode_sess_attr(fnode, tsid, ISCSI_FLASHNODE_TSID);
iscsi_flashnode_sess_attr(fnode, max_burst_len, ISCSI_FLASHNODE_MAX_BURST);
iscsi_flashnode_sess_attr(fnode, def_taskmgmt_tmo,
     ISCSI_FLASHNODE_DEF_TASKMGMT_TMO);
iscsi_flashnode_sess_attr(fnode, targetalias, ISCSI_FLASHNODE_ALIAS);
iscsi_flashnode_sess_attr(fnode, targetname, ISCSI_FLASHNODE_NAME);
iscsi_flashnode_sess_attr(fnode, tpgt, ISCSI_FLASHNODE_TPGT);
iscsi_flashnode_sess_attr(fnode, discovery_parent_idx,
     ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX);
iscsi_flashnode_sess_attr(fnode, discovery_parent_type,
     ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE);
iscsi_flashnode_sess_attr(fnode, chap_in_idx, ISCSI_FLASHNODE_CHAP_IN_IDX);
iscsi_flashnode_sess_attr(fnode, chap_out_idx, ISCSI_FLASHNODE_CHAP_OUT_IDX);
iscsi_flashnode_sess_attr(fnode, username, ISCSI_FLASHNODE_USERNAME);
iscsi_flashnode_sess_attr(fnode, username_in, ISCSI_FLASHNODE_USERNAME_IN);
iscsi_flashnode_sess_attr(fnode, password, ISCSI_FLASHNODE_PASSWORD);
iscsi_flashnode_sess_attr(fnode, password_in, ISCSI_FLASHNODE_PASSWORD_IN);
iscsi_flashnode_sess_attr(fnode, is_boot_target, ISCSI_FLASHNODE_IS_BOOT_TGT);

static struct attribute *iscsi_flashnode_sess_attrs[] = {
 &dev_attr_fnode_auto_snd_tgt_disable.attr,
 &dev_attr_fnode_discovery_session.attr,
 &dev_attr_fnode_portal_type.attr,
 &dev_attr_fnode_entry_enable.attr,
 &dev_attr_fnode_immediate_data.attr,
 &dev_attr_fnode_initial_r2t.attr,
 &dev_attr_fnode_data_seq_in_order.attr,
 &dev_attr_fnode_data_pdu_in_order.attr,
 &dev_attr_fnode_chap_auth.attr,
 &dev_attr_fnode_discovery_logout.attr,
 &dev_attr_fnode_bidi_chap.attr,
 &dev_attr_fnode_discovery_auth_optional.attr,
 &dev_attr_fnode_erl.attr,
 &dev_attr_fnode_first_burst_len.attr,
 &dev_attr_fnode_def_time2wait.attr,
 &dev_attr_fnode_def_time2retain.attr,
 &dev_attr_fnode_max_outstanding_r2t.attr,
 &dev_attr_fnode_isid.attr,
 &dev_attr_fnode_tsid.attr,
 &dev_attr_fnode_max_burst_len.attr,
 &dev_attr_fnode_def_taskmgmt_tmo.attr,
 &dev_attr_fnode_targetalias.attr,
 &dev_attr_fnode_targetname.attr,
 &dev_attr_fnode_tpgt.attr,
 &dev_attr_fnode_discovery_parent_idx.attr,
 &dev_attr_fnode_discovery_parent_type.attr,
 &dev_attr_fnode_chap_in_idx.attr,
 &dev_attr_fnode_chap_out_idx.attr,
 &dev_attr_fnode_username.attr,
 &dev_attr_fnode_username_in.attr,
 &dev_attr_fnode_password.attr,
 &dev_attr_fnode_password_in.attr,
 &dev_attr_fnode_is_boot_target.attr,
 NULL,
};

static umode_t iscsi_flashnode_sess_attr_is_visible(struct kobject *kobj,
          struct attribute *attr,
          int i)
{
 struct device *dev = container_of(kobj, struct device, kobj);
 struct iscsi_bus_flash_session *fnode_sess =
      iscsi_dev_to_flash_session(dev);
 struct iscsi_transport *t = fnode_sess->transport;
 int param;

 if (attr == &dev_attr_fnode_auto_snd_tgt_disable.attr) {
  param = ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE;
 } else if (attr == &dev_attr_fnode_discovery_session.attr) {
  param = ISCSI_FLASHNODE_DISCOVERY_SESS;
 } else if (attr == &dev_attr_fnode_portal_type.attr) {
  param = ISCSI_FLASHNODE_PORTAL_TYPE;
 } else if (attr == &dev_attr_fnode_entry_enable.attr) {
  param = ISCSI_FLASHNODE_ENTRY_EN;
 } else if (attr == &dev_attr_fnode_immediate_data.attr) {
  param = ISCSI_FLASHNODE_IMM_DATA_EN;
 } else if (attr == &dev_attr_fnode_initial_r2t.attr) {
  param = ISCSI_FLASHNODE_INITIAL_R2T_EN;
 } else if (attr == &dev_attr_fnode_data_seq_in_order.attr) {
  param = ISCSI_FLASHNODE_DATASEQ_INORDER;
 } else if (attr == &dev_attr_fnode_data_pdu_in_order.attr) {
  param = ISCSI_FLASHNODE_PDU_INORDER;
 } else if (attr == &dev_attr_fnode_chap_auth.attr) {
  param = ISCSI_FLASHNODE_CHAP_AUTH_EN;
 } else if (attr == &dev_attr_fnode_discovery_logout.attr) {
  param = ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN;
 } else if (attr == &dev_attr_fnode_bidi_chap.attr) {
  param = ISCSI_FLASHNODE_BIDI_CHAP_EN;
 } else if (attr == &dev_attr_fnode_discovery_auth_optional.attr) {
  param = ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL;
 } else if (attr == &dev_attr_fnode_erl.attr) {
  param = ISCSI_FLASHNODE_ERL;
 } else if (attr == &dev_attr_fnode_first_burst_len.attr) {
  param = ISCSI_FLASHNODE_FIRST_BURST;
 } else if (attr == &dev_attr_fnode_def_time2wait.attr) {
  param = ISCSI_FLASHNODE_DEF_TIME2WAIT;
 } else if (attr == &dev_attr_fnode_def_time2retain.attr) {
  param = ISCSI_FLASHNODE_DEF_TIME2RETAIN;
 } else if (attr == &dev_attr_fnode_max_outstanding_r2t.attr) {
  param = ISCSI_FLASHNODE_MAX_R2T;
 } else if (attr == &dev_attr_fnode_isid.attr) {
  param = ISCSI_FLASHNODE_ISID;
 } else if (attr == &dev_attr_fnode_tsid.attr) {
  param = ISCSI_FLASHNODE_TSID;
 } else if (attr == &dev_attr_fnode_max_burst_len.attr) {
  param = ISCSI_FLASHNODE_MAX_BURST;
 } else if (attr == &dev_attr_fnode_def_taskmgmt_tmo.attr) {
  param = ISCSI_FLASHNODE_DEF_TASKMGMT_TMO;
 } else if (attr == &dev_attr_fnode_targetalias.attr) {
  param = ISCSI_FLASHNODE_ALIAS;
 } else if (attr == &dev_attr_fnode_targetname.attr) {
  param = ISCSI_FLASHNODE_NAME;
 } else if (attr == &dev_attr_fnode_tpgt.attr) {
  param = ISCSI_FLASHNODE_TPGT;
 } else if (attr == &dev_attr_fnode_discovery_parent_idx.attr) {
  param = ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX;
 } else if (attr == &dev_attr_fnode_discovery_parent_type.attr) {
  param = ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE;
 } else if (attr == &dev_attr_fnode_chap_in_idx.attr) {
  param = ISCSI_FLASHNODE_CHAP_IN_IDX;
 } else if (attr == &dev_attr_fnode_chap_out_idx.attr) {
  param = ISCSI_FLASHNODE_CHAP_OUT_IDX;
 } else if (attr == &dev_attr_fnode_username.attr) {
  param = ISCSI_FLASHNODE_USERNAME;
 } else if (attr == &dev_attr_fnode_username_in.attr) {
  param = ISCSI_FLASHNODE_USERNAME_IN;
 } else if (attr == &dev_attr_fnode_password.attr) {
  param = ISCSI_FLASHNODE_PASSWORD;
 } else if (attr == &dev_attr_fnode_password_in.attr) {
  param = ISCSI_FLASHNODE_PASSWORD_IN;
 } else if (attr == &dev_attr_fnode_is_boot_target.attr) {
  param = ISCSI_FLASHNODE_IS_BOOT_TGT;
 } else {
  WARN_ONCE(1, "Invalid flashnode session attr");
  return 0;
 }

 return t->attr_is_visible(ISCSI_FLASHNODE_PARAM, param);
}

static struct attribute_group iscsi_flashnode_sess_attr_group = {
 .attrs = iscsi_flashnode_sess_attrs,
 .is_visible = iscsi_flashnode_sess_attr_is_visible,
};

static const struct attribute_group *iscsi_flashnode_sess_attr_groups[] = {
 &iscsi_flashnode_sess_attr_group,
 NULL,
};

static void iscsi_flashnode_sess_release(struct device *dev)
{
 struct iscsi_bus_flash_session *fnode_sess =
      iscsi_dev_to_flash_session(dev);

 kfree(fnode_sess->targetname);
 kfree(fnode_sess->targetalias);
 kfree(fnode_sess->portal_type);
 kfree(fnode_sess);
}

static const struct device_type iscsi_flashnode_sess_dev_type = {
 .name = "iscsi_flashnode_sess_dev_type",
 .groups = iscsi_flashnode_sess_attr_groups,
 .release = iscsi_flashnode_sess_release,
};

/* flash node connection attrs show */
#define iscsi_flashnode_conn_attr_show(type, name, param)  \
static ssize_t        \
show_##type##_##name(struct device *dev, struct device_attribute *attr, \
       char *buf)      \
{         \
 struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);\
 struct iscsi_bus_flash_session *fnode_sess =   \
    iscsi_flash_conn_to_flash_session(fnode_conn);\
 struct iscsi_transport *t = fnode_conn->transport;  \
 return t->get_flashnode_param(fnode_sess, param, buf);  \
}         \


#define iscsi_flashnode_conn_attr(type, name, param)   \
 iscsi_flashnode_conn_attr_show(type, name, param)  \
static ISCSI_FLASHNODE_ATTR(type, name, S_IRUGO,   \
       show_##type##_##name, NULL);

/* Flash node connection attributes */

iscsi_flashnode_conn_attr(fnode, is_fw_assigned_ipv6,
     ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6);
iscsi_flashnode_conn_attr(fnode, header_digest, ISCSI_FLASHNODE_HDR_DGST_EN);
iscsi_flashnode_conn_attr(fnode, data_digest, ISCSI_FLASHNODE_DATA_DGST_EN);
iscsi_flashnode_conn_attr(fnode, snack_req, ISCSI_FLASHNODE_SNACK_REQ_EN);
iscsi_flashnode_conn_attr(fnode, tcp_timestamp_stat,
     ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT);
iscsi_flashnode_conn_attr(fnode, tcp_nagle_disable,
     ISCSI_FLASHNODE_TCP_NAGLE_DISABLE);
iscsi_flashnode_conn_attr(fnode, tcp_wsf_disable,
     ISCSI_FLASHNODE_TCP_WSF_DISABLE);
iscsi_flashnode_conn_attr(fnode, tcp_timer_scale,
     ISCSI_FLASHNODE_TCP_TIMER_SCALE);
iscsi_flashnode_conn_attr(fnode, tcp_timestamp_enable,
     ISCSI_FLASHNODE_TCP_TIMESTAMP_EN);
iscsi_flashnode_conn_attr(fnode, fragment_disable,
     ISCSI_FLASHNODE_IP_FRAG_DISABLE);
iscsi_flashnode_conn_attr(fnode, keepalive_tmo, ISCSI_FLASHNODE_KEEPALIVE_TMO);
iscsi_flashnode_conn_attr(fnode, port, ISCSI_FLASHNODE_PORT);
iscsi_flashnode_conn_attr(fnode, ipaddress, ISCSI_FLASHNODE_IPADDR);
iscsi_flashnode_conn_attr(fnode, max_recv_dlength,
     ISCSI_FLASHNODE_MAX_RECV_DLENGTH);
iscsi_flashnode_conn_attr(fnode, max_xmit_dlength,
     ISCSI_FLASHNODE_MAX_XMIT_DLENGTH);
iscsi_flashnode_conn_attr(fnode, local_port, ISCSI_FLASHNODE_LOCAL_PORT);
iscsi_flashnode_conn_attr(fnode, ipv4_tos, ISCSI_FLASHNODE_IPV4_TOS);
iscsi_flashnode_conn_attr(fnode, ipv6_traffic_class, ISCSI_FLASHNODE_IPV6_TC);
iscsi_flashnode_conn_attr(fnode, ipv6_flow_label,
     ISCSI_FLASHNODE_IPV6_FLOW_LABEL);
iscsi_flashnode_conn_attr(fnode, redirect_ipaddr,
     ISCSI_FLASHNODE_REDIRECT_IPADDR);
iscsi_flashnode_conn_attr(fnode, max_segment_size,
     ISCSI_FLASHNODE_MAX_SEGMENT_SIZE);
iscsi_flashnode_conn_attr(fnode, link_local_ipv6,
     ISCSI_FLASHNODE_LINK_LOCAL_IPV6);
iscsi_flashnode_conn_attr(fnode, tcp_xmit_wsf, ISCSI_FLASHNODE_TCP_XMIT_WSF);
iscsi_flashnode_conn_attr(fnode, tcp_recv_wsf, ISCSI_FLASHNODE_TCP_RECV_WSF);
iscsi_flashnode_conn_attr(fnode, statsn, ISCSI_FLASHNODE_STATSN);
iscsi_flashnode_conn_attr(fnode, exp_statsn, ISCSI_FLASHNODE_EXP_STATSN);

static struct attribute *iscsi_flashnode_conn_attrs[] = {
 &dev_attr_fnode_is_fw_assigned_ipv6.attr,
 &dev_attr_fnode_header_digest.attr,
 &dev_attr_fnode_data_digest.attr,
 &dev_attr_fnode_snack_req.attr,
 &dev_attr_fnode_tcp_timestamp_stat.attr,
 &dev_attr_fnode_tcp_nagle_disable.attr,
 &dev_attr_fnode_tcp_wsf_disable.attr,
 &dev_attr_fnode_tcp_timer_scale.attr,
 &dev_attr_fnode_tcp_timestamp_enable.attr,
 &dev_attr_fnode_fragment_disable.attr,
 &dev_attr_fnode_max_recv_dlength.attr,
 &dev_attr_fnode_max_xmit_dlength.attr,
 &dev_attr_fnode_keepalive_tmo.attr,
 &dev_attr_fnode_port.attr,
 &dev_attr_fnode_ipaddress.attr,
 &dev_attr_fnode_redirect_ipaddr.attr,
 &dev_attr_fnode_max_segment_size.attr,
 &dev_attr_fnode_local_port.attr,
 &dev_attr_fnode_ipv4_tos.attr,
 &dev_attr_fnode_ipv6_traffic_class.attr,
 &dev_attr_fnode_ipv6_flow_label.attr,
 &dev_attr_fnode_link_local_ipv6.attr,
 &dev_attr_fnode_tcp_xmit_wsf.attr,
 &dev_attr_fnode_tcp_recv_wsf.attr,
 &dev_attr_fnode_statsn.attr,
 &dev_attr_fnode_exp_statsn.attr,
 NULL,
};

static umode_t iscsi_flashnode_conn_attr_is_visible(struct kobject *kobj,
          struct attribute *attr,
          int i)
{
 struct device *dev = container_of(kobj, struct device, kobj);
 struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);
 struct iscsi_transport *t = fnode_conn->transport;
 int param;

 if (attr == &dev_attr_fnode_is_fw_assigned_ipv6.attr) {
  param = ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6;
 } else if (attr == &dev_attr_fnode_header_digest.attr) {
  param = ISCSI_FLASHNODE_HDR_DGST_EN;
 } else if (attr == &dev_attr_fnode_data_digest.attr) {
  param = ISCSI_FLASHNODE_DATA_DGST_EN;
 } else if (attr == &dev_attr_fnode_snack_req.attr) {
  param = ISCSI_FLASHNODE_SNACK_REQ_EN;
 } else if (attr == &dev_attr_fnode_tcp_timestamp_stat.attr) {
  param = ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT;
 } else if (attr == &dev_attr_fnode_tcp_nagle_disable.attr) {
  param = ISCSI_FLASHNODE_TCP_NAGLE_DISABLE;
 } else if (attr == &dev_attr_fnode_tcp_wsf_disable.attr) {
  param = ISCSI_FLASHNODE_TCP_WSF_DISABLE;
 } else if (attr == &dev_attr_fnode_tcp_timer_scale.attr) {
  param = ISCSI_FLASHNODE_TCP_TIMER_SCALE;
 } else if (attr == &dev_attr_fnode_tcp_timestamp_enable.attr) {
  param = ISCSI_FLASHNODE_TCP_TIMESTAMP_EN;
 } else if (attr == &dev_attr_fnode_fragment_disable.attr) {
  param = ISCSI_FLASHNODE_IP_FRAG_DISABLE;
 } else if (attr == &dev_attr_fnode_max_recv_dlength.attr) {
  param = ISCSI_FLASHNODE_MAX_RECV_DLENGTH;
 } else if (attr == &dev_attr_fnode_max_xmit_dlength.attr) {
  param = ISCSI_FLASHNODE_MAX_XMIT_DLENGTH;
 } else if (attr == &dev_attr_fnode_keepalive_tmo.attr) {
  param = ISCSI_FLASHNODE_KEEPALIVE_TMO;
 } else if (attr == &dev_attr_fnode_port.attr) {
  param = ISCSI_FLASHNODE_PORT;
 } else if (attr == &dev_attr_fnode_ipaddress.attr) {
  param = ISCSI_FLASHNODE_IPADDR;
 } else if (attr == &dev_attr_fnode_redirect_ipaddr.attr) {
  param = ISCSI_FLASHNODE_REDIRECT_IPADDR;
 } else if (attr == &dev_attr_fnode_max_segment_size.attr) {
  param = ISCSI_FLASHNODE_MAX_SEGMENT_SIZE;
 } else if (attr == &dev_attr_fnode_local_port.attr) {
  param = ISCSI_FLASHNODE_LOCAL_PORT;
 } else if (attr == &dev_attr_fnode_ipv4_tos.attr) {
  param = ISCSI_FLASHNODE_IPV4_TOS;
 } else if (attr == &dev_attr_fnode_ipv6_traffic_class.attr) {
  param = ISCSI_FLASHNODE_IPV6_TC;
 } else if (attr == &dev_attr_fnode_ipv6_flow_label.attr) {
  param = ISCSI_FLASHNODE_IPV6_FLOW_LABEL;
 } else if (attr == &dev_attr_fnode_link_local_ipv6.attr) {
  param = ISCSI_FLASHNODE_LINK_LOCAL_IPV6;
 } else if (attr == &dev_attr_fnode_tcp_xmit_wsf.attr) {
  param = ISCSI_FLASHNODE_TCP_XMIT_WSF;
 } else if (attr == &dev_attr_fnode_tcp_recv_wsf.attr) {
  param = ISCSI_FLASHNODE_TCP_RECV_WSF;
 } else if (attr == &dev_attr_fnode_statsn.attr) {
  param = ISCSI_FLASHNODE_STATSN;
 } else if (attr == &dev_attr_fnode_exp_statsn.attr) {
  param = ISCSI_FLASHNODE_EXP_STATSN;
 } else {
  WARN_ONCE(1, "Invalid flashnode connection attr");
  return 0;
 }

 return t->attr_is_visible(ISCSI_FLASHNODE_PARAM, param);
}

static struct attribute_group iscsi_flashnode_conn_attr_group = {
 .attrs = iscsi_flashnode_conn_attrs,
 .is_visible = iscsi_flashnode_conn_attr_is_visible,
};

static const struct attribute_group *iscsi_flashnode_conn_attr_groups[] = {
 &iscsi_flashnode_conn_attr_group,
 NULL,
};

static void iscsi_flashnode_conn_release(struct device *dev)
{
 struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);

 kfree(fnode_conn->ipaddress);
 kfree(fnode_conn->redirect_ipaddr);
 kfree(fnode_conn->link_local_ipv6_addr);
 kfree(fnode_conn);
}

static const struct device_type iscsi_flashnode_conn_dev_type = {
 .name = "iscsi_flashnode_conn_dev_type",
 .groups = iscsi_flashnode_conn_attr_groups,
 .release = iscsi_flashnode_conn_release,
};

static const struct bus_type iscsi_flashnode_bus;

int iscsi_flashnode_bus_match(struct device *dev,
         const struct device_driver *drv)
{
 if (dev->bus == &iscsi_flashnode_bus)
  return 1;
 return 0;
}
EXPORT_SYMBOL_GPL(iscsi_flashnode_bus_match);

static const struct bus_type iscsi_flashnode_bus = {
 .name = "iscsi_flashnode",
 .match = &iscsi_flashnode_bus_match,
};

/**
 * iscsi_create_flashnode_sess - Add flashnode session entry in sysfs
 * @shost: pointer to host data
 * @index: index of flashnode to add in sysfs
 * @transport: pointer to transport data
 * @dd_size: total size to allocate
 *
 * Adds a sysfs entry for the flashnode session attributes
 *
 * Returns:
 *  pointer to allocated flashnode sess on success
 *  %NULL on failure
 */

struct iscsi_bus_flash_session *
iscsi_create_flashnode_sess(struct Scsi_Host *shost, int index,
       struct iscsi_transport *transport,
       int dd_size)
{
 struct iscsi_bus_flash_session *fnode_sess;
 int err;

 fnode_sess = kzalloc(sizeof(*fnode_sess) + dd_size, GFP_KERNEL);
 if (!fnode_sess)
  return NULL;

 fnode_sess->transport = transport;
 fnode_sess->target_id = index;
 fnode_sess->dev.type = &iscsi_flashnode_sess_dev_type;
 fnode_sess->dev.bus = &iscsi_flashnode_bus;
 fnode_sess->dev.parent = &shost->shost_gendev;
 dev_set_name(&fnode_sess->dev, "flashnode_sess-%u:%u",
       shost->host_no, index);

 err = device_register(&fnode_sess->dev);
 if (err)
  goto put_dev;

 if (dd_size)
  fnode_sess->dd_data = &fnode_sess[1];

 return fnode_sess;

put_dev:
 put_device(&fnode_sess->dev);
 return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_create_flashnode_sess);

/**
 * iscsi_create_flashnode_conn - Add flashnode conn entry in sysfs
 * @shost: pointer to host data
 * @fnode_sess: pointer to the parent flashnode session entry
 * @transport: pointer to transport data
 * @dd_size: total size to allocate
 *
 * Adds a sysfs entry for the flashnode connection attributes
 *
 * Returns:
 *  pointer to allocated flashnode conn on success
 *  %NULL on failure
 */

struct iscsi_bus_flash_conn *
iscsi_create_flashnode_conn(struct Scsi_Host *shost,
       struct iscsi_bus_flash_session *fnode_sess,
       struct iscsi_transport *transport,
       int dd_size)
{
 struct iscsi_bus_flash_conn *fnode_conn;
 int err;

 fnode_conn = kzalloc(sizeof(*fnode_conn) + dd_size, GFP_KERNEL);
 if (!fnode_conn)
  return NULL;

 fnode_conn->transport = transport;
 fnode_conn->dev.type = &iscsi_flashnode_conn_dev_type;
 fnode_conn->dev.bus = &iscsi_flashnode_bus;
 fnode_conn->dev.parent = &fnode_sess->dev;
 dev_set_name(&fnode_conn->dev, "flashnode_conn-%u:%u:0",
       shost->host_no, fnode_sess->target_id);

 err = device_register(&fnode_conn->dev);
 if (err)
  goto put_dev;

 if (dd_size)
  fnode_conn->dd_data = &fnode_conn[1];

 return fnode_conn;

put_dev:
 put_device(&fnode_conn->dev);
 return NULL;
}
EXPORT_SYMBOL_GPL(iscsi_create_flashnode_conn);

/**
 * iscsi_is_flashnode_conn_dev - verify passed device is to be flashnode conn
 * @dev: device to verify
 * @data: pointer to data containing value to use for verification
 *
 * Verifies if the passed device is flashnode conn device
 *
 * Returns:
 *  1 on success
 *  0 on failure
 */

static int iscsi_is_flashnode_conn_dev(struct device *dev, const void *data)
{
 return dev->bus == &iscsi_flashnode_bus;
}

static int iscsi_destroy_flashnode_conn(struct iscsi_bus_flash_conn *fnode_conn)
{
 device_unregister(&fnode_conn->dev);
 return 0;
}

static int flashnode_match_index(struct device *dev, const void *data)
{
 struct iscsi_bus_flash_session *fnode_sess = NULL;
 int ret = 0;

 if (!iscsi_flashnode_bus_match(dev, NULL))
  goto exit_match_index;

 fnode_sess = iscsi_dev_to_flash_session(dev);
 ret = (fnode_sess->target_id == *((const int *)data)) ? 1 : 0;

exit_match_index:
 return ret;
}

/**
 * iscsi_get_flashnode_by_index -finds flashnode session entry by index
 * @shost: pointer to host data
 * @idx: index to match
 *
 * Finds the flashnode session object for the passed index
 *
 * Returns:
 *  pointer to found flashnode session object on success
 *  %NULL on failure
 */

static struct iscsi_bus_flash_session *
iscsi_get_flashnode_by_index(struct Scsi_Host *shost, uint32_t idx)
{
 struct iscsi_bus_flash_session *fnode_sess = NULL;
 struct device *dev;

 dev = device_find_child(&shost->shost_gendev, &idx,
    flashnode_match_index);
 if (dev)
  fnode_sess = iscsi_dev_to_flash_session(dev);

 return fnode_sess;
}

/**
 * iscsi_find_flashnode_sess - finds flashnode session entry
 * @shost: pointer to host data
 * @data: pointer to data containing value to use for comparison
 * @fn: function pointer that does actual comparison
 *
 * Finds the flashnode session object comparing the data passed using logic
 * defined in passed function pointer
 *
 * Returns:
 *  pointer to found flashnode session device object on success
 *  %NULL on failure
 */

struct device *
iscsi_find_flashnode_sess(struct Scsi_Host *shost, const void *data,
     device_match_t fn)
{
 return device_find_child(&shost->shost_gendev, data, fn);
}
EXPORT_SYMBOL_GPL(iscsi_find_flashnode_sess);

/**
 * iscsi_find_flashnode_conn - finds flashnode connection entry
 * @fnode_sess: pointer to parent flashnode session entry
 *
 * Finds the flashnode connection object comparing the data passed using logic
 * defined in passed function pointer
 *
 * Returns:
 *  pointer to found flashnode connection device object on success
 *  %NULL on failure
 */

struct device *
iscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess)
{
 return device_find_child(&fnode_sess->dev, NULL,
     iscsi_is_flashnode_conn_dev);
}
EXPORT_SYMBOL_GPL(iscsi_find_flashnode_conn);

static int iscsi_iter_destroy_flashnode_conn_fn(struct device *dev, void *data)
{
 if (!iscsi_is_flashnode_conn_dev(dev, NULL))
  return 0;

 return iscsi_destroy_flashnode_conn(iscsi_dev_to_flash_conn(dev));
}

/**
 * iscsi_destroy_flashnode_sess - destroy flashnode session entry
 * @fnode_sess: pointer to flashnode session entry to be destroyed
 *
 * Deletes the flashnode session entry and all children flashnode connection
 * entries from sysfs
 */

void iscsi_destroy_flashnode_sess(struct iscsi_bus_flash_session *fnode_sess)
{
 int err;

 err = device_for_each_child(&fnode_sess->dev, NULL,
        iscsi_iter_destroy_flashnode_conn_fn);
 if (err)
  pr_err("Could not delete all connections for %s. Error %d.\n",
         fnode_sess->dev.kobj.name, err);

 device_unregister(&fnode_sess->dev);
}
EXPORT_SYMBOL_GPL(iscsi_destroy_flashnode_sess);

static int iscsi_iter_destroy_flashnode_fn(struct device *dev, void *data)
{
 if (!iscsi_flashnode_bus_match(dev, NULL))
  return 0;

 iscsi_destroy_flashnode_sess(iscsi_dev_to_flash_session(dev));
 return 0;
}

/**
 * iscsi_destroy_all_flashnode - destroy all flashnode session entries
 * @shost: pointer to host data
 *
 * Destroys all the flashnode session entries and all corresponding children
 * flashnode connection entries from sysfs
 */

void iscsi_destroy_all_flashnode(struct Scsi_Host *shost)
{
 device_for_each_child(&shost->shost_gendev, NULL,
         iscsi_iter_destroy_flashnode_fn);
}
EXPORT_SYMBOL_GPL(iscsi_destroy_all_flashnode);

/*
 * BSG support
 */

/**
 * iscsi_bsg_host_dispatch - Dispatch command to LLD.
 * @job: bsg job to be processed
 */

static int iscsi_bsg_host_dispatch(struct bsg_job *job)
{
 struct Scsi_Host *shost = iscsi_job_to_shost(job);
 struct iscsi_bsg_request *req = job->request;
 struct iscsi_bsg_reply *reply = job->reply;
 struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
 int cmdlen = sizeof(uint32_t); /* start with length of msgcode */
 int ret;

 /* check if we have the msgcode value at least */
 if (job->request_len < sizeof(uint32_t)) {
  ret = -ENOMSG;
  goto fail_host_msg;
 }

 /* Validate the host command */
 switch (req->msgcode) {
 case ISCSI_BSG_HST_VENDOR:
  cmdlen += sizeof(struct iscsi_bsg_host_vendor);
  if ((shost->hostt->vendor_id == 0L) ||
      (req->rqst_data.h_vendor.vendor_id !=
   shost->hostt->vendor_id)) {
   ret = -ESRCH;
   goto fail_host_msg;
  }
  break;
 default:
  ret = -EBADR;
  goto fail_host_msg;
 }

 /* check if we really have all the request data needed */
 if (job->request_len < cmdlen) {
  ret = -ENOMSG;
  goto fail_host_msg;
 }

 ret = i->iscsi_transport->bsg_request(job);
 if (!ret)
  return 0;

fail_host_msg:
 /* return the errno failure code as the only status */
 BUG_ON(job->reply_len < sizeof(uint32_t));
 reply->reply_payload_rcv_len = 0;
 reply->result = ret;
 job->reply_len = sizeof(uint32_t);
 bsg_job_done(job, ret, 0);
 return 0;
}

/**
 * iscsi_bsg_host_add - Create and add the bsg hooks to receive requests
 * @shost: shost for iscsi_host
 * @ihost: iscsi_cls_host adding the structures to
 */

static int
iscsi_bsg_host_add(struct Scsi_Host *shost, struct iscsi_cls_host *ihost)
{
 struct device *dev = &shost->shost_gendev;
 struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
 struct queue_limits lim;
 struct request_queue *q;
 char bsg_name[20];

 if (!i->iscsi_transport->bsg_request)
  return -ENOTSUPP;

 snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no);
 scsi_init_limits(shost, &lim);
 q = bsg_setup_queue(dev, bsg_name, &lim, iscsi_bsg_host_dispatch, NULL,
   0);
 if (IS_ERR(q)) {
  shost_printk(KERN_ERR, shost, "bsg interface failed to "
        "initialize - no request queue\n");
  return PTR_ERR(q);
 }

 ihost->bsg_q = q;
 return 0;
}

static int iscsi_setup_host(struct transport_container *tc, struct device *dev,
       struct device *cdev)
{
 struct Scsi_Host *shost = dev_to_shost(dev);
 struct iscsi_cls_host *ihost = shost->shost_data;

 memset(ihost, 0, sizeof(*ihost));
 mutex_init(&ihost->mutex);

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

 return 0;
}

static int iscsi_remove_host(struct transport_container *tc,
        struct device *dev, struct device *cdev)
{
 struct Scsi_Host *shost = dev_to_shost(dev);
 struct iscsi_cls_host *ihost = shost->shost_data;

 bsg_remove_queue(ihost->bsg_q);
 return 0;
}

static DECLARE_TRANSPORT_CLASS(iscsi_host_class,
          "iscsi_host",
          iscsi_setup_host,
          iscsi_remove_host,
          NULL);

static DECLARE_TRANSPORT_CLASS(iscsi_session_class,
          "iscsi_session",
          NULL,
          NULL,
          NULL);

static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
          "iscsi_connection",
          NULL,
          NULL,
          NULL);

static struct sock *nls;
static DEFINE_MUTEX(rx_queue_mutex);

static LIST_HEAD(sesslist);
static DEFINE_SPINLOCK(sesslock);
static LIST_HEAD(connlist);
static DEFINE_SPINLOCK(connlock);

static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn)
{
 struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent);
 return sess->sid;
}

/*
 * Returns the matching session to a given sid
 */

static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid)
{
 unsigned long flags;
 struct iscsi_cls_session *sess;

 spin_lock_irqsave(&sesslock, flags);
 list_for_each_entry(sess, &sesslist, sess_list) {
  if (sess->sid == sid) {
   spin_unlock_irqrestore(&sesslock, flags);
   return sess;
  }
 }
 spin_unlock_irqrestore(&sesslock, flags);
 return NULL;
}

/*
 * Returns the matching connection to a given sid / cid tuple
 */

static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
{
 unsigned long flags;
 struct iscsi_cls_conn *conn;

 spin_lock_irqsave(&connlock, flags);
 list_for_each_entry(conn, &connlist, conn_list) {
  if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) {
   spin_unlock_irqrestore(&connlock, flags);
   return conn;
  }
 }
 spin_unlock_irqrestore(&connlock, flags);
 return NULL;
}

/*
 * The following functions can be used by LLDs that allocate
 * their own scsi_hosts or by software iscsi LLDs
 */

static struct {
 int value;
 char *name;
} iscsi_session_state_names[] = {
 { ISCSI_SESSION_LOGGED_IN, "LOGGED_IN" },
 { ISCSI_SESSION_FAILED,  "FAILED" },
 { ISCSI_SESSION_FREE,  "FREE" },
};

static const char *iscsi_session_state_name(int state)
{
 int i;
 char *name = NULL;

 for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
  if (iscsi_session_state_names[i].value == state) {
   name = iscsi_session_state_names[i].name;
   break;
  }
 }
 return name;
}

static char *iscsi_session_target_state_name[] = {
 [ISCSI_SESSION_TARGET_UNBOUND]   = "UNBOUND",
 [ISCSI_SESSION_TARGET_ALLOCATED] = "ALLOCATED",
 [ISCSI_SESSION_TARGET_SCANNED]   = "SCANNED",
 [ISCSI_SESSION_TARGET_UNBINDING] = "UNBINDING",
};

int iscsi_session_chkready(struct iscsi_cls_session *session)
{
 int err;

 switch (session->state) {
 case ISCSI_SESSION_LOGGED_IN:
  err = 0;
  break;
 case ISCSI_SESSION_FAILED:
  err = DID_IMM_RETRY << 16;
  break;
 case ISCSI_SESSION_FREE:
  err = DID_TRANSPORT_FAILFAST << 16;
  break;
 default:
  err = DID_NO_CONNECT << 16;
  break;
 }
 return err;
}
EXPORT_SYMBOL_GPL(iscsi_session_chkready);

int iscsi_is_session_online(struct iscsi_cls_session *session)
{
 unsigned long flags;
 int ret = 0;

 spin_lock_irqsave(&session->lock, flags);
 if (session->state == ISCSI_SESSION_LOGGED_IN)
  ret = 1;
 spin_unlock_irqrestore(&session->lock, flags);
 return ret;
}
EXPORT_SYMBOL_GPL(iscsi_is_session_online);

static void iscsi_session_release(struct device *dev)
{
 struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
 struct Scsi_Host *shost;

 shost = iscsi_session_to_shost(session);
 scsi_host_put(shost);
 ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
 kfree(session);
}

int iscsi_is_session_dev(const struct device *dev)
{
 return dev->release == iscsi_session_release;
}
EXPORT_SYMBOL_GPL(iscsi_is_session_dev);

static int iscsi_iter_session_fn(struct device *dev, void *data)
{
 void (* fn) (struct iscsi_cls_session *) = data;

 if (!iscsi_is_session_dev(dev))
  return 0;
 fn(iscsi_dev_to_session(dev));
 return 0;
}

void iscsi_host_for_each_session(struct Scsi_Host *shost,
     void (*fn)(struct iscsi_cls_session *))
{
 device_for_each_child(&shost->shost_gendev, fn,
         iscsi_iter_session_fn);
}
EXPORT_SYMBOL_GPL(iscsi_host_for_each_session);

struct iscsi_scan_data {
 unsigned int channel;
 unsigned int id;
 u64 lun;
 enum scsi_scan_mode rescan;
};

static int iscsi_user_scan_session(struct device *dev, void *data)
{
 struct iscsi_scan_data *scan_data = data;
 struct iscsi_cls_session *session;
 struct Scsi_Host *shost;
 struct iscsi_cls_host *ihost;
 unsigned long flags;
 unsigned int id;

 if (!iscsi_is_session_dev(dev))
  return 0;

 session = iscsi_dev_to_session(dev);

 ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");

 shost = iscsi_session_to_shost(session);
 ihost = shost->shost_data;

 mutex_lock(&ihost->mutex);
 spin_lock_irqsave(&session->lock, flags);
 if (session->state != ISCSI_SESSION_LOGGED_IN) {
  spin_unlock_irqrestore(&session->lock, flags);
  goto user_scan_exit;
 }
 id = session->target_id;
 spin_unlock_irqrestore(&session->lock, flags);

 if (id != ISCSI_MAX_TARGET) {
  if ((scan_data->channel == SCAN_WILD_CARD ||
       scan_data->channel == 0) &&
      (scan_data->id == SCAN_WILD_CARD ||
       scan_data->id == id)) {
   scsi_scan_target(&session->dev, 0, id,
      scan_data->lun, scan_data->rescan);
   spin_lock_irqsave(&session->lock, flags);
   session->target_state = ISCSI_SESSION_TARGET_SCANNED;
   spin_unlock_irqrestore(&session->lock, flags);
  }
 }

user_scan_exit:
 mutex_unlock(&ihost->mutex);
 ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
 return 0;
}

static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
      uint id, u64 lun)
{
 struct iscsi_scan_data scan_data;

 scan_data.channel = channel;
 scan_data.id = id;
 scan_data.lun = lun;
 scan_data.rescan = SCSI_SCAN_MANUAL;

 return device_for_each_child(&shost->shost_gendev, &scan_data,
         iscsi_user_scan_session);
}

static void iscsi_scan_session(struct work_struct *work)
{
 struct iscsi_cls_session *session =
   container_of(work, struct iscsi_cls_session, scan_work);
 struct iscsi_scan_data scan_data;

 scan_data.channel = 0;
 scan_data.id = SCAN_WILD_CARD;
 scan_data.lun = SCAN_WILD_CARD;
 scan_data.rescan = SCSI_SCAN_RESCAN;

 iscsi_user_scan_session(&session->dev, &scan_data);
}

/**
 * iscsi_block_scsi_eh - block scsi eh until session state has transistioned
 * @cmd: scsi cmd passed to scsi eh handler
 *
 * If the session is down this function will wait for the recovery
 * timer to fire or for the session to be logged back in. If the
 * recovery timer fires then FAST_IO_FAIL is returned. The caller
 * should pass this error value to the scsi eh.
 */

int iscsi_block_scsi_eh(struct scsi_cmnd *cmd)
{
 struct iscsi_cls_session *session =
   starget_to_session(scsi_target(cmd->device));
 unsigned long flags;
 int ret = 0;

 spin_lock_irqsave(&session->lock, flags);
 while (session->state != ISCSI_SESSION_LOGGED_IN) {
  if (session->state == ISCSI_SESSION_FREE) {
   ret = FAST_IO_FAIL;
   break;
  }
  spin_unlock_irqrestore(&session->lock, flags);
  msleep(1000);
  spin_lock_irqsave(&session->lock, flags);
 }
 spin_unlock_irqrestore(&session->lock, flags);
 return ret;
}
EXPORT_SYMBOL_GPL(iscsi_block_scsi_eh);

static void session_recovery_timedout(struct work_struct *work)
{
 struct iscsi_cls_session *session =
  container_of(work, struct iscsi_cls_session,
        recovery_work.work);
 unsigned long flags;

 iscsi_cls_session_printk(KERN_INFO, session,
     "session recovery timed out after %d secs\n",
     session->recovery_tmo);

 spin_lock_irqsave(&session->lock, flags);
 switch (session->state) {
 case ISCSI_SESSION_FAILED:
  session->state = ISCSI_SESSION_FREE;
  break;
 case ISCSI_SESSION_LOGGED_IN:
 case ISCSI_SESSION_FREE:
  /* we raced with the unblock's flush */
  spin_unlock_irqrestore(&session->lock, flags);
  return;
 }
 spin_unlock_irqrestore(&session->lock, flags);

 ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
 scsi_target_unblock(&session->dev, SDEV_TRANSPORT_OFFLINE);
 ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");

 if (session->transport->session_recovery_timedout)
  session->transport->session_recovery_timedout(session);
}

static void __iscsi_unblock_session(struct work_struct *work)
{
 struct iscsi_cls_session *session =
   container_of(work, struct iscsi_cls_session,
         unblock_work);
 unsigned long flags;

 ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");

 cancel_delayed_work_sync(&session->recovery_work);
 spin_lock_irqsave(&session->lock, flags);
 session->state = ISCSI_SESSION_LOGGED_IN;
 spin_unlock_irqrestore(&session->lock, flags);
 /* start IO */
 scsi_target_unblock(&session->dev, SDEV_RUNNING);
 ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
}

/**
 * iscsi_unblock_session - set a session as logged in and start IO.
 * @session: iscsi session
 *
 * Mark a session as ready to accept IO.
 */

void iscsi_unblock_session(struct iscsi_cls_session *session)
{
 if (!cancel_work_sync(&session->block_work))
  cancel_delayed_work_sync(&session->recovery_work);

 queue_work(session->workq, &session->unblock_work);
 /*
 * Blocking the session can be done from any context so we only
 * queue the block work. Make sure the unblock work has completed
 * because it flushes/cancels the other works and updates the state.
 */

 flush_work(&session->unblock_work);
}
EXPORT_SYMBOL_GPL(iscsi_unblock_session);

static void __iscsi_block_session(struct work_struct *work)
{
 struct iscsi_cls_session *session =
   container_of(work, struct iscsi_cls_session,
         block_work);
 struct Scsi_Host *shost = iscsi_session_to_shost(session);
 unsigned long flags;

 ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
 spin_lock_irqsave(&session->lock, flags);
 session->state = ISCSI_SESSION_FAILED;
 spin_unlock_irqrestore(&session->lock, flags);
 scsi_block_targets(shost, &session->dev);
 ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
 if (session->recovery_tmo >= 0)
  queue_delayed_work(session->workq,
       &session->recovery_work,
       session->recovery_tmo * HZ);
}

void iscsi_block_session(struct iscsi_cls_session *session)
{
 queue_work(session->workq, &session->block_work);
}
EXPORT_SYMBOL_GPL(iscsi_block_session);

static void __iscsi_unbind_session(struct work_struct *work)
{
 struct iscsi_cls_session *session =
   container_of(work, struct iscsi_cls_session,
         unbind_work);
 struct Scsi_Host *shost = iscsi_session_to_shost(session);
 struct iscsi_cls_host *ihost = shost->shost_data;
 unsigned long flags;
--> --------------------

--> maximum size reached

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

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

¤ 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.0.30Bemerkung:  (vorverarbeitet)  ¤

*Bot Zugriff






Wurzel

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.