162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * iSCSI transport class definitions
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) IBM Corporation, 2004
662306a36Sopenharmony_ci * Copyright (C) Mike Christie, 2004 - 2005
762306a36Sopenharmony_ci * Copyright (C) Dmitry Yusupov, 2004 - 2005
862306a36Sopenharmony_ci * Copyright (C) Alex Aizman, 2004 - 2005
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/mutex.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/bsg-lib.h>
1462306a36Sopenharmony_ci#include <linux/idr.h>
1562306a36Sopenharmony_ci#include <net/tcp.h>
1662306a36Sopenharmony_ci#include <scsi/scsi.h>
1762306a36Sopenharmony_ci#include <scsi/scsi_host.h>
1862306a36Sopenharmony_ci#include <scsi/scsi_device.h>
1962306a36Sopenharmony_ci#include <scsi/scsi_transport.h>
2062306a36Sopenharmony_ci#include <scsi/scsi_transport_iscsi.h>
2162306a36Sopenharmony_ci#include <scsi/iscsi_if.h>
2262306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h>
2362306a36Sopenharmony_ci#include <scsi/scsi_bsg_iscsi.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define ISCSI_TRANSPORT_VERSION "2.0-870"
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci#define ISCSI_SEND_MAX_ALLOWED  10
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#define CREATE_TRACE_POINTS
3062306a36Sopenharmony_ci#include <trace/events/iscsi.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci/*
3362306a36Sopenharmony_ci * Export tracepoint symbols to be used by other modules.
3462306a36Sopenharmony_ci */
3562306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_conn);
3662306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_eh);
3762306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_session);
3862306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_tcp);
3962306a36Sopenharmony_ciEXPORT_TRACEPOINT_SYMBOL_GPL(iscsi_dbg_sw_tcp);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int dbg_session;
4262306a36Sopenharmony_cimodule_param_named(debug_session, dbg_session, int,
4362306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR);
4462306a36Sopenharmony_ciMODULE_PARM_DESC(debug_session,
4562306a36Sopenharmony_ci		 "Turn on debugging for sessions in scsi_transport_iscsi "
4662306a36Sopenharmony_ci		 "module. Set to 1 to turn on, and zero to turn off. Default "
4762306a36Sopenharmony_ci		 "is off.");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic int dbg_conn;
5062306a36Sopenharmony_cimodule_param_named(debug_conn, dbg_conn, int,
5162306a36Sopenharmony_ci		   S_IRUGO | S_IWUSR);
5262306a36Sopenharmony_ciMODULE_PARM_DESC(debug_conn,
5362306a36Sopenharmony_ci		 "Turn on debugging for connections in scsi_transport_iscsi "
5462306a36Sopenharmony_ci		 "module. Set to 1 to turn on, and zero to turn off. Default "
5562306a36Sopenharmony_ci		 "is off.");
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci#define ISCSI_DBG_TRANS_SESSION(_session, dbg_fmt, arg...)		\
5862306a36Sopenharmony_ci	do {								\
5962306a36Sopenharmony_ci		if (dbg_session)					\
6062306a36Sopenharmony_ci			iscsi_cls_session_printk(KERN_INFO, _session,	\
6162306a36Sopenharmony_ci						 "%s: " dbg_fmt,	\
6262306a36Sopenharmony_ci						 __func__, ##arg);	\
6362306a36Sopenharmony_ci		iscsi_dbg_trace(trace_iscsi_dbg_trans_session,		\
6462306a36Sopenharmony_ci				&(_session)->dev,			\
6562306a36Sopenharmony_ci				"%s " dbg_fmt, __func__, ##arg);	\
6662306a36Sopenharmony_ci	} while (0);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci#define ISCSI_DBG_TRANS_CONN(_conn, dbg_fmt, arg...)			\
6962306a36Sopenharmony_ci	do {								\
7062306a36Sopenharmony_ci		if (dbg_conn)						\
7162306a36Sopenharmony_ci			iscsi_cls_conn_printk(KERN_INFO, _conn,		\
7262306a36Sopenharmony_ci					      "%s: " dbg_fmt,		\
7362306a36Sopenharmony_ci					      __func__, ##arg);		\
7462306a36Sopenharmony_ci		iscsi_dbg_trace(trace_iscsi_dbg_trans_conn,		\
7562306a36Sopenharmony_ci				&(_conn)->dev,				\
7662306a36Sopenharmony_ci				"%s " dbg_fmt, __func__, ##arg);	\
7762306a36Sopenharmony_ci	} while (0);
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistruct iscsi_internal {
8062306a36Sopenharmony_ci	struct scsi_transport_template t;
8162306a36Sopenharmony_ci	struct iscsi_transport *iscsi_transport;
8262306a36Sopenharmony_ci	struct list_head list;
8362306a36Sopenharmony_ci	struct device dev;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	struct transport_container conn_cont;
8662306a36Sopenharmony_ci	struct transport_container session_cont;
8762306a36Sopenharmony_ci};
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic DEFINE_IDR(iscsi_ep_idr);
9062306a36Sopenharmony_cistatic DEFINE_MUTEX(iscsi_ep_idr_mutex);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic atomic_t iscsi_session_nr; /* sysfs session id for next new session */
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic struct workqueue_struct *iscsi_conn_cleanup_workq;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic DEFINE_IDA(iscsi_sess_ida);
9762306a36Sopenharmony_ci/*
9862306a36Sopenharmony_ci * list of registered transports and lock that must
9962306a36Sopenharmony_ci * be held while accessing list. The iscsi_transport_lock must
10062306a36Sopenharmony_ci * be acquired after the rx_queue_mutex.
10162306a36Sopenharmony_ci */
10262306a36Sopenharmony_cistatic LIST_HEAD(iscsi_transports);
10362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(iscsi_transport_lock);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci#define to_iscsi_internal(tmpl) \
10662306a36Sopenharmony_ci	container_of(tmpl, struct iscsi_internal, t)
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci#define dev_to_iscsi_internal(_dev) \
10962306a36Sopenharmony_ci	container_of(_dev, struct iscsi_internal, dev)
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void iscsi_transport_release(struct device *dev)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
11462306a36Sopenharmony_ci	kfree(priv);
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci/*
11862306a36Sopenharmony_ci * iscsi_transport_class represents the iscsi_transports that are
11962306a36Sopenharmony_ci * registered.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_cistatic struct class iscsi_transport_class = {
12262306a36Sopenharmony_ci	.name = "iscsi_transport",
12362306a36Sopenharmony_ci	.dev_release = iscsi_transport_release,
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic ssize_t
12762306a36Sopenharmony_cishow_transport_handle(struct device *dev, struct device_attribute *attr,
12862306a36Sopenharmony_ci		      char *buf)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (!capable(CAP_SYS_ADMIN))
13362306a36Sopenharmony_ci		return -EACCES;
13462306a36Sopenharmony_ci	return sysfs_emit(buf, "%llu\n",
13562306a36Sopenharmony_ci		  (unsigned long long)iscsi_handle(priv->iscsi_transport));
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_cistatic DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci#define show_transport_attr(name, format)				\
14062306a36Sopenharmony_cistatic ssize_t								\
14162306a36Sopenharmony_cishow_transport_##name(struct device *dev, 				\
14262306a36Sopenharmony_ci		      struct device_attribute *attr,char *buf)		\
14362306a36Sopenharmony_ci{									\
14462306a36Sopenharmony_ci	struct iscsi_internal *priv = dev_to_iscsi_internal(dev);	\
14562306a36Sopenharmony_ci	return sysfs_emit(buf, format"\n", priv->iscsi_transport->name);\
14662306a36Sopenharmony_ci}									\
14762306a36Sopenharmony_cistatic DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cishow_transport_attr(caps, "0x%x");
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic struct attribute *iscsi_transport_attrs[] = {
15262306a36Sopenharmony_ci	&dev_attr_handle.attr,
15362306a36Sopenharmony_ci	&dev_attr_caps.attr,
15462306a36Sopenharmony_ci	NULL,
15562306a36Sopenharmony_ci};
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic struct attribute_group iscsi_transport_group = {
15862306a36Sopenharmony_ci	.attrs = iscsi_transport_attrs,
15962306a36Sopenharmony_ci};
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci/*
16262306a36Sopenharmony_ci * iSCSI endpoint attrs
16362306a36Sopenharmony_ci */
16462306a36Sopenharmony_ci#define iscsi_dev_to_endpoint(_dev) \
16562306a36Sopenharmony_ci	container_of(_dev, struct iscsi_endpoint, dev)
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci#define ISCSI_ATTR(_prefix,_name,_mode,_show,_store)	\
16862306a36Sopenharmony_cistruct device_attribute dev_attr_##_prefix##_##_name =	\
16962306a36Sopenharmony_ci        __ATTR(_name,_mode,_show,_store)
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_cistatic void iscsi_endpoint_release(struct device *dev)
17262306a36Sopenharmony_ci{
17362306a36Sopenharmony_ci	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	mutex_lock(&iscsi_ep_idr_mutex);
17662306a36Sopenharmony_ci	idr_remove(&iscsi_ep_idr, ep->id);
17762306a36Sopenharmony_ci	mutex_unlock(&iscsi_ep_idr_mutex);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	kfree(ep);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic struct class iscsi_endpoint_class = {
18362306a36Sopenharmony_ci	.name = "iscsi_endpoint",
18462306a36Sopenharmony_ci	.dev_release = iscsi_endpoint_release,
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistatic ssize_t
18862306a36Sopenharmony_cishow_ep_handle(struct device *dev, struct device_attribute *attr, char *buf)
18962306a36Sopenharmony_ci{
19062306a36Sopenharmony_ci	struct iscsi_endpoint *ep = iscsi_dev_to_endpoint(dev);
19162306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", ep->id);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_cistatic ISCSI_ATTR(ep, handle, S_IRUGO, show_ep_handle, NULL);
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_cistatic struct attribute *iscsi_endpoint_attrs[] = {
19662306a36Sopenharmony_ci	&dev_attr_ep_handle.attr,
19762306a36Sopenharmony_ci	NULL,
19862306a36Sopenharmony_ci};
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_cistatic struct attribute_group iscsi_endpoint_group = {
20162306a36Sopenharmony_ci	.attrs = iscsi_endpoint_attrs,
20262306a36Sopenharmony_ci};
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_cistruct iscsi_endpoint *
20562306a36Sopenharmony_ciiscsi_create_endpoint(int dd_size)
20662306a36Sopenharmony_ci{
20762306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
20862306a36Sopenharmony_ci	int err, id;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	ep = kzalloc(sizeof(*ep) + dd_size, GFP_KERNEL);
21162306a36Sopenharmony_ci	if (!ep)
21262306a36Sopenharmony_ci		return NULL;
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	mutex_lock(&iscsi_ep_idr_mutex);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * First endpoint id should be 1 to comply with user space
21862306a36Sopenharmony_ci	 * applications (iscsid).
21962306a36Sopenharmony_ci	 */
22062306a36Sopenharmony_ci	id = idr_alloc(&iscsi_ep_idr, ep, 1, -1, GFP_NOIO);
22162306a36Sopenharmony_ci	if (id < 0) {
22262306a36Sopenharmony_ci		mutex_unlock(&iscsi_ep_idr_mutex);
22362306a36Sopenharmony_ci		printk(KERN_ERR "Could not allocate endpoint ID. Error %d.\n",
22462306a36Sopenharmony_ci		       id);
22562306a36Sopenharmony_ci		goto free_ep;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci	mutex_unlock(&iscsi_ep_idr_mutex);
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	ep->id = id;
23062306a36Sopenharmony_ci	ep->dev.class = &iscsi_endpoint_class;
23162306a36Sopenharmony_ci	dev_set_name(&ep->dev, "ep-%d", id);
23262306a36Sopenharmony_ci	err = device_register(&ep->dev);
23362306a36Sopenharmony_ci        if (err)
23462306a36Sopenharmony_ci		goto put_dev;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	err = sysfs_create_group(&ep->dev.kobj, &iscsi_endpoint_group);
23762306a36Sopenharmony_ci	if (err)
23862306a36Sopenharmony_ci		goto unregister_dev;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (dd_size)
24162306a36Sopenharmony_ci		ep->dd_data = &ep[1];
24262306a36Sopenharmony_ci	return ep;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ciunregister_dev:
24562306a36Sopenharmony_ci	device_unregister(&ep->dev);
24662306a36Sopenharmony_ci	return NULL;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ciput_dev:
24962306a36Sopenharmony_ci	mutex_lock(&iscsi_ep_idr_mutex);
25062306a36Sopenharmony_ci	idr_remove(&iscsi_ep_idr, id);
25162306a36Sopenharmony_ci	mutex_unlock(&iscsi_ep_idr_mutex);
25262306a36Sopenharmony_ci	put_device(&ep->dev);
25362306a36Sopenharmony_ci	return NULL;
25462306a36Sopenharmony_cifree_ep:
25562306a36Sopenharmony_ci	kfree(ep);
25662306a36Sopenharmony_ci	return NULL;
25762306a36Sopenharmony_ci}
25862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_create_endpoint);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_civoid iscsi_destroy_endpoint(struct iscsi_endpoint *ep)
26162306a36Sopenharmony_ci{
26262306a36Sopenharmony_ci	sysfs_remove_group(&ep->dev.kobj, &iscsi_endpoint_group);
26362306a36Sopenharmony_ci	device_unregister(&ep->dev);
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_destroy_endpoint);
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_civoid iscsi_put_endpoint(struct iscsi_endpoint *ep)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	put_device(&ep->dev);
27062306a36Sopenharmony_ci}
27162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_put_endpoint);
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci/**
27462306a36Sopenharmony_ci * iscsi_lookup_endpoint - get ep from handle
27562306a36Sopenharmony_ci * @handle: endpoint handle
27662306a36Sopenharmony_ci *
27762306a36Sopenharmony_ci * Caller must do a iscsi_put_endpoint.
27862306a36Sopenharmony_ci */
27962306a36Sopenharmony_cistruct iscsi_endpoint *iscsi_lookup_endpoint(u64 handle)
28062306a36Sopenharmony_ci{
28162306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	mutex_lock(&iscsi_ep_idr_mutex);
28462306a36Sopenharmony_ci	ep = idr_find(&iscsi_ep_idr, handle);
28562306a36Sopenharmony_ci	if (!ep)
28662306a36Sopenharmony_ci		goto unlock;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	get_device(&ep->dev);
28962306a36Sopenharmony_ciunlock:
29062306a36Sopenharmony_ci	mutex_unlock(&iscsi_ep_idr_mutex);
29162306a36Sopenharmony_ci	return ep;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_lookup_endpoint);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci/*
29662306a36Sopenharmony_ci * Interface to display network param to sysfs
29762306a36Sopenharmony_ci */
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void iscsi_iface_release(struct device *dev)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
30262306a36Sopenharmony_ci	struct device *parent = iface->dev.parent;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	kfree(iface);
30562306a36Sopenharmony_ci	put_device(parent);
30662306a36Sopenharmony_ci}
30762306a36Sopenharmony_ci
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_cistatic struct class iscsi_iface_class = {
31062306a36Sopenharmony_ci	.name = "iscsi_iface",
31162306a36Sopenharmony_ci	.dev_release = iscsi_iface_release,
31262306a36Sopenharmony_ci};
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci#define ISCSI_IFACE_ATTR(_prefix, _name, _mode, _show, _store)	\
31562306a36Sopenharmony_cistruct device_attribute dev_attr_##_prefix##_##_name =		\
31662306a36Sopenharmony_ci	__ATTR(_name, _mode, _show, _store)
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/* iface attrs show */
31962306a36Sopenharmony_ci#define iscsi_iface_attr_show(type, name, param_type, param)		\
32062306a36Sopenharmony_cistatic ssize_t								\
32162306a36Sopenharmony_cishow_##type##_##name(struct device *dev, struct device_attribute *attr,	\
32262306a36Sopenharmony_ci		     char *buf)						\
32362306a36Sopenharmony_ci{									\
32462306a36Sopenharmony_ci	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);		\
32562306a36Sopenharmony_ci	struct iscsi_transport *t = iface->transport;			\
32662306a36Sopenharmony_ci	return t->get_iface_param(iface, param_type, param, buf);	\
32762306a36Sopenharmony_ci}									\
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci#define iscsi_iface_net_attr(type, name, param)				\
33062306a36Sopenharmony_ci	iscsi_iface_attr_show(type, name, ISCSI_NET_PARAM, param)	\
33162306a36Sopenharmony_cistatic ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci#define iscsi_iface_attr(type, name, param)				\
33462306a36Sopenharmony_ci	iscsi_iface_attr_show(type, name, ISCSI_IFACE_PARAM, param)	\
33562306a36Sopenharmony_cistatic ISCSI_IFACE_ATTR(type, name, S_IRUGO, show_##type##_##name, NULL);
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci/* generic read only ipv4 attribute */
33862306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, ipaddress, ISCSI_NET_PARAM_IPV4_ADDR);
33962306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, gateway, ISCSI_NET_PARAM_IPV4_GW);
34062306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, subnet, ISCSI_NET_PARAM_IPV4_SUBNET);
34162306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, bootproto, ISCSI_NET_PARAM_IPV4_BOOTPROTO);
34262306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_dns_address_en,
34362306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN);
34462306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_slp_da_info_en,
34562306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN);
34662306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, tos_en, ISCSI_NET_PARAM_IPV4_TOS_EN);
34762306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, tos, ISCSI_NET_PARAM_IPV4_TOS);
34862306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, grat_arp_en,
34962306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN);
35062306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_alt_client_id_en,
35162306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN);
35262306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_alt_client_id,
35362306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID);
35462306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_req_vendor_id_en,
35562306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN);
35662306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_use_vendor_id_en,
35762306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN);
35862306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_vendor_id,
35962306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID);
36062306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, dhcp_learn_iqn_en,
36162306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN);
36262306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, fragment_disable,
36362306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE);
36462306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, incoming_forwarding_en,
36562306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN);
36662306a36Sopenharmony_ciiscsi_iface_net_attr(ipv4_iface, ttl, ISCSI_NET_PARAM_IPV4_TTL);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci/* generic read only ipv6 attribute */
36962306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, ipaddress, ISCSI_NET_PARAM_IPV6_ADDR);
37062306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, link_local_addr,
37162306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_LINKLOCAL);
37262306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, router_addr, ISCSI_NET_PARAM_IPV6_ROUTER);
37362306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, ipaddr_autocfg,
37462306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG);
37562306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, link_local_autocfg,
37662306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG);
37762306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, link_local_state,
37862306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE);
37962306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, router_state,
38062306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_ROUTER_STATE);
38162306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, grat_neighbor_adv_en,
38262306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN);
38362306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, mld_en, ISCSI_NET_PARAM_IPV6_MLD_EN);
38462306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, flow_label, ISCSI_NET_PARAM_IPV6_FLOW_LABEL);
38562306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, traffic_class,
38662306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS);
38762306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, hop_limit, ISCSI_NET_PARAM_IPV6_HOP_LIMIT);
38862306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, nd_reachable_tmo,
38962306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO);
39062306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, nd_rexmit_time,
39162306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME);
39262306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, nd_stale_tmo,
39362306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_ND_STALE_TMO);
39462306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, dup_addr_detect_cnt,
39562306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT);
39662306a36Sopenharmony_ciiscsi_iface_net_attr(ipv6_iface, router_adv_link_mtu,
39762306a36Sopenharmony_ci		     ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU);
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_ci/* common read only iface attribute */
40062306a36Sopenharmony_ciiscsi_iface_net_attr(iface, enabled, ISCSI_NET_PARAM_IFACE_ENABLE);
40162306a36Sopenharmony_ciiscsi_iface_net_attr(iface, vlan_id, ISCSI_NET_PARAM_VLAN_ID);
40262306a36Sopenharmony_ciiscsi_iface_net_attr(iface, vlan_priority, ISCSI_NET_PARAM_VLAN_PRIORITY);
40362306a36Sopenharmony_ciiscsi_iface_net_attr(iface, vlan_enabled, ISCSI_NET_PARAM_VLAN_ENABLED);
40462306a36Sopenharmony_ciiscsi_iface_net_attr(iface, mtu, ISCSI_NET_PARAM_MTU);
40562306a36Sopenharmony_ciiscsi_iface_net_attr(iface, port, ISCSI_NET_PARAM_PORT);
40662306a36Sopenharmony_ciiscsi_iface_net_attr(iface, ipaddress_state, ISCSI_NET_PARAM_IPADDR_STATE);
40762306a36Sopenharmony_ciiscsi_iface_net_attr(iface, delayed_ack_en, ISCSI_NET_PARAM_DELAYED_ACK_EN);
40862306a36Sopenharmony_ciiscsi_iface_net_attr(iface, tcp_nagle_disable,
40962306a36Sopenharmony_ci		     ISCSI_NET_PARAM_TCP_NAGLE_DISABLE);
41062306a36Sopenharmony_ciiscsi_iface_net_attr(iface, tcp_wsf_disable, ISCSI_NET_PARAM_TCP_WSF_DISABLE);
41162306a36Sopenharmony_ciiscsi_iface_net_attr(iface, tcp_wsf, ISCSI_NET_PARAM_TCP_WSF);
41262306a36Sopenharmony_ciiscsi_iface_net_attr(iface, tcp_timer_scale, ISCSI_NET_PARAM_TCP_TIMER_SCALE);
41362306a36Sopenharmony_ciiscsi_iface_net_attr(iface, tcp_timestamp_en, ISCSI_NET_PARAM_TCP_TIMESTAMP_EN);
41462306a36Sopenharmony_ciiscsi_iface_net_attr(iface, cache_id, ISCSI_NET_PARAM_CACHE_ID);
41562306a36Sopenharmony_ciiscsi_iface_net_attr(iface, redirect_en, ISCSI_NET_PARAM_REDIRECT_EN);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci/* common iscsi specific settings attributes */
41862306a36Sopenharmony_ciiscsi_iface_attr(iface, def_taskmgmt_tmo, ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO);
41962306a36Sopenharmony_ciiscsi_iface_attr(iface, header_digest, ISCSI_IFACE_PARAM_HDRDGST_EN);
42062306a36Sopenharmony_ciiscsi_iface_attr(iface, data_digest, ISCSI_IFACE_PARAM_DATADGST_EN);
42162306a36Sopenharmony_ciiscsi_iface_attr(iface, immediate_data, ISCSI_IFACE_PARAM_IMM_DATA_EN);
42262306a36Sopenharmony_ciiscsi_iface_attr(iface, initial_r2t, ISCSI_IFACE_PARAM_INITIAL_R2T_EN);
42362306a36Sopenharmony_ciiscsi_iface_attr(iface, data_seq_in_order,
42462306a36Sopenharmony_ci		 ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN);
42562306a36Sopenharmony_ciiscsi_iface_attr(iface, data_pdu_in_order, ISCSI_IFACE_PARAM_PDU_INORDER_EN);
42662306a36Sopenharmony_ciiscsi_iface_attr(iface, erl, ISCSI_IFACE_PARAM_ERL);
42762306a36Sopenharmony_ciiscsi_iface_attr(iface, max_recv_dlength, ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH);
42862306a36Sopenharmony_ciiscsi_iface_attr(iface, first_burst_len, ISCSI_IFACE_PARAM_FIRST_BURST);
42962306a36Sopenharmony_ciiscsi_iface_attr(iface, max_outstanding_r2t, ISCSI_IFACE_PARAM_MAX_R2T);
43062306a36Sopenharmony_ciiscsi_iface_attr(iface, max_burst_len, ISCSI_IFACE_PARAM_MAX_BURST);
43162306a36Sopenharmony_ciiscsi_iface_attr(iface, chap_auth, ISCSI_IFACE_PARAM_CHAP_AUTH_EN);
43262306a36Sopenharmony_ciiscsi_iface_attr(iface, bidi_chap, ISCSI_IFACE_PARAM_BIDI_CHAP_EN);
43362306a36Sopenharmony_ciiscsi_iface_attr(iface, discovery_auth_optional,
43462306a36Sopenharmony_ci		 ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL);
43562306a36Sopenharmony_ciiscsi_iface_attr(iface, discovery_logout,
43662306a36Sopenharmony_ci		 ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN);
43762306a36Sopenharmony_ciiscsi_iface_attr(iface, strict_login_comp_en,
43862306a36Sopenharmony_ci		 ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN);
43962306a36Sopenharmony_ciiscsi_iface_attr(iface, initiator_name, ISCSI_IFACE_PARAM_INITIATOR_NAME);
44062306a36Sopenharmony_ci
44162306a36Sopenharmony_cistatic umode_t iscsi_iface_attr_is_visible(struct kobject *kobj,
44262306a36Sopenharmony_ci					  struct attribute *attr, int i)
44362306a36Sopenharmony_ci{
44462306a36Sopenharmony_ci	struct device *dev = container_of(kobj, struct device, kobj);
44562306a36Sopenharmony_ci	struct iscsi_iface *iface = iscsi_dev_to_iface(dev);
44662306a36Sopenharmony_ci	struct iscsi_transport *t = iface->transport;
44762306a36Sopenharmony_ci	int param = -1;
44862306a36Sopenharmony_ci
44962306a36Sopenharmony_ci	if (attr == &dev_attr_iface_def_taskmgmt_tmo.attr)
45062306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_DEF_TASKMGMT_TMO;
45162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_header_digest.attr)
45262306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_HDRDGST_EN;
45362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_data_digest.attr)
45462306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_DATADGST_EN;
45562306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_immediate_data.attr)
45662306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_IMM_DATA_EN;
45762306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_initial_r2t.attr)
45862306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_INITIAL_R2T_EN;
45962306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_data_seq_in_order.attr)
46062306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_DATASEQ_INORDER_EN;
46162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_data_pdu_in_order.attr)
46262306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_PDU_INORDER_EN;
46362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_erl.attr)
46462306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_ERL;
46562306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_max_recv_dlength.attr)
46662306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_MAX_RECV_DLENGTH;
46762306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_first_burst_len.attr)
46862306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_FIRST_BURST;
46962306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_max_outstanding_r2t.attr)
47062306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_MAX_R2T;
47162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_max_burst_len.attr)
47262306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_MAX_BURST;
47362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_chap_auth.attr)
47462306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_CHAP_AUTH_EN;
47562306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_bidi_chap.attr)
47662306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_BIDI_CHAP_EN;
47762306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_discovery_auth_optional.attr)
47862306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_DISCOVERY_AUTH_OPTIONAL;
47962306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_discovery_logout.attr)
48062306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_DISCOVERY_LOGOUT_EN;
48162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_strict_login_comp_en.attr)
48262306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_STRICT_LOGIN_COMP_EN;
48362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_initiator_name.attr)
48462306a36Sopenharmony_ci		param = ISCSI_IFACE_PARAM_INITIATOR_NAME;
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci	if (param != -1)
48762306a36Sopenharmony_ci		return t->attr_is_visible(ISCSI_IFACE_PARAM, param);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (attr == &dev_attr_iface_enabled.attr)
49062306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_IFACE_ENABLE;
49162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_vlan_id.attr)
49262306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_VLAN_ID;
49362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_vlan_priority.attr)
49462306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_VLAN_PRIORITY;
49562306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_vlan_enabled.attr)
49662306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_VLAN_ENABLED;
49762306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_mtu.attr)
49862306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_MTU;
49962306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_port.attr)
50062306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_PORT;
50162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_ipaddress_state.attr)
50262306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_IPADDR_STATE;
50362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_delayed_ack_en.attr)
50462306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_DELAYED_ACK_EN;
50562306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_tcp_nagle_disable.attr)
50662306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_TCP_NAGLE_DISABLE;
50762306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_tcp_wsf_disable.attr)
50862306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_TCP_WSF_DISABLE;
50962306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_tcp_wsf.attr)
51062306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_TCP_WSF;
51162306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_tcp_timer_scale.attr)
51262306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_TCP_TIMER_SCALE;
51362306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_tcp_timestamp_en.attr)
51462306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_TCP_TIMESTAMP_EN;
51562306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_cache_id.attr)
51662306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_CACHE_ID;
51762306a36Sopenharmony_ci	else if (attr == &dev_attr_iface_redirect_en.attr)
51862306a36Sopenharmony_ci		param = ISCSI_NET_PARAM_REDIRECT_EN;
51962306a36Sopenharmony_ci	else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV4) {
52062306a36Sopenharmony_ci		if (attr == &dev_attr_ipv4_iface_ipaddress.attr)
52162306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_ADDR;
52262306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_gateway.attr)
52362306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_GW;
52462306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_subnet.attr)
52562306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_SUBNET;
52662306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_bootproto.attr)
52762306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_BOOTPROTO;
52862306a36Sopenharmony_ci		else if (attr ==
52962306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_dhcp_dns_address_en.attr)
53062306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_DNS_ADDR_EN;
53162306a36Sopenharmony_ci		else if (attr ==
53262306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_dhcp_slp_da_info_en.attr)
53362306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_SLP_DA_EN;
53462306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_tos_en.attr)
53562306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_TOS_EN;
53662306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_tos.attr)
53762306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_TOS;
53862306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_grat_arp_en.attr)
53962306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_GRAT_ARP_EN;
54062306a36Sopenharmony_ci		else if (attr ==
54162306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_dhcp_alt_client_id_en.attr)
54262306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID_EN;
54362306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_dhcp_alt_client_id.attr)
54462306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_ALT_CLIENT_ID;
54562306a36Sopenharmony_ci		else if (attr ==
54662306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_dhcp_req_vendor_id_en.attr)
54762306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_REQ_VENDOR_ID_EN;
54862306a36Sopenharmony_ci		else if (attr ==
54962306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_dhcp_use_vendor_id_en.attr)
55062306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_USE_VENDOR_ID_EN;
55162306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_dhcp_vendor_id.attr)
55262306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_VENDOR_ID;
55362306a36Sopenharmony_ci		else if (attr ==
55462306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_dhcp_learn_iqn_en.attr)
55562306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_DHCP_LEARN_IQN_EN;
55662306a36Sopenharmony_ci		else if (attr ==
55762306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_fragment_disable.attr)
55862306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_FRAGMENT_DISABLE;
55962306a36Sopenharmony_ci		else if (attr ==
56062306a36Sopenharmony_ci			 &dev_attr_ipv4_iface_incoming_forwarding_en.attr)
56162306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_IN_FORWARD_EN;
56262306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv4_iface_ttl.attr)
56362306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV4_TTL;
56462306a36Sopenharmony_ci		else
56562306a36Sopenharmony_ci			return 0;
56662306a36Sopenharmony_ci	} else if (iface->iface_type == ISCSI_IFACE_TYPE_IPV6) {
56762306a36Sopenharmony_ci		if (attr == &dev_attr_ipv6_iface_ipaddress.attr)
56862306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ADDR;
56962306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_link_local_addr.attr)
57062306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_LINKLOCAL;
57162306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_router_addr.attr)
57262306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ROUTER;
57362306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_ipaddr_autocfg.attr)
57462306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ADDR_AUTOCFG;
57562306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_link_local_autocfg.attr)
57662306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_AUTOCFG;
57762306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_link_local_state.attr)
57862306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_LINKLOCAL_STATE;
57962306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_router_state.attr)
58062306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ROUTER_STATE;
58162306a36Sopenharmony_ci		else if (attr ==
58262306a36Sopenharmony_ci			 &dev_attr_ipv6_iface_grat_neighbor_adv_en.attr)
58362306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_GRAT_NEIGHBOR_ADV_EN;
58462306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_mld_en.attr)
58562306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_MLD_EN;
58662306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_flow_label.attr)
58762306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_FLOW_LABEL;
58862306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_traffic_class.attr)
58962306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_TRAFFIC_CLASS;
59062306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_hop_limit.attr)
59162306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_HOP_LIMIT;
59262306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_nd_reachable_tmo.attr)
59362306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ND_REACHABLE_TMO;
59462306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_nd_rexmit_time.attr)
59562306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ND_REXMIT_TIME;
59662306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_nd_stale_tmo.attr)
59762306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_ND_STALE_TMO;
59862306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_dup_addr_detect_cnt.attr)
59962306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_DUP_ADDR_DETECT_CNT;
60062306a36Sopenharmony_ci		else if (attr == &dev_attr_ipv6_iface_router_adv_link_mtu.attr)
60162306a36Sopenharmony_ci			param = ISCSI_NET_PARAM_IPV6_RTR_ADV_LINK_MTU;
60262306a36Sopenharmony_ci		else
60362306a36Sopenharmony_ci			return 0;
60462306a36Sopenharmony_ci	} else {
60562306a36Sopenharmony_ci		WARN_ONCE(1, "Invalid iface attr");
60662306a36Sopenharmony_ci		return 0;
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	return t->attr_is_visible(ISCSI_NET_PARAM, param);
61062306a36Sopenharmony_ci}
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_cistatic struct attribute *iscsi_iface_attrs[] = {
61362306a36Sopenharmony_ci	&dev_attr_iface_enabled.attr,
61462306a36Sopenharmony_ci	&dev_attr_iface_vlan_id.attr,
61562306a36Sopenharmony_ci	&dev_attr_iface_vlan_priority.attr,
61662306a36Sopenharmony_ci	&dev_attr_iface_vlan_enabled.attr,
61762306a36Sopenharmony_ci	&dev_attr_ipv4_iface_ipaddress.attr,
61862306a36Sopenharmony_ci	&dev_attr_ipv4_iface_gateway.attr,
61962306a36Sopenharmony_ci	&dev_attr_ipv4_iface_subnet.attr,
62062306a36Sopenharmony_ci	&dev_attr_ipv4_iface_bootproto.attr,
62162306a36Sopenharmony_ci	&dev_attr_ipv6_iface_ipaddress.attr,
62262306a36Sopenharmony_ci	&dev_attr_ipv6_iface_link_local_addr.attr,
62362306a36Sopenharmony_ci	&dev_attr_ipv6_iface_router_addr.attr,
62462306a36Sopenharmony_ci	&dev_attr_ipv6_iface_ipaddr_autocfg.attr,
62562306a36Sopenharmony_ci	&dev_attr_ipv6_iface_link_local_autocfg.attr,
62662306a36Sopenharmony_ci	&dev_attr_iface_mtu.attr,
62762306a36Sopenharmony_ci	&dev_attr_iface_port.attr,
62862306a36Sopenharmony_ci	&dev_attr_iface_ipaddress_state.attr,
62962306a36Sopenharmony_ci	&dev_attr_iface_delayed_ack_en.attr,
63062306a36Sopenharmony_ci	&dev_attr_iface_tcp_nagle_disable.attr,
63162306a36Sopenharmony_ci	&dev_attr_iface_tcp_wsf_disable.attr,
63262306a36Sopenharmony_ci	&dev_attr_iface_tcp_wsf.attr,
63362306a36Sopenharmony_ci	&dev_attr_iface_tcp_timer_scale.attr,
63462306a36Sopenharmony_ci	&dev_attr_iface_tcp_timestamp_en.attr,
63562306a36Sopenharmony_ci	&dev_attr_iface_cache_id.attr,
63662306a36Sopenharmony_ci	&dev_attr_iface_redirect_en.attr,
63762306a36Sopenharmony_ci	&dev_attr_iface_def_taskmgmt_tmo.attr,
63862306a36Sopenharmony_ci	&dev_attr_iface_header_digest.attr,
63962306a36Sopenharmony_ci	&dev_attr_iface_data_digest.attr,
64062306a36Sopenharmony_ci	&dev_attr_iface_immediate_data.attr,
64162306a36Sopenharmony_ci	&dev_attr_iface_initial_r2t.attr,
64262306a36Sopenharmony_ci	&dev_attr_iface_data_seq_in_order.attr,
64362306a36Sopenharmony_ci	&dev_attr_iface_data_pdu_in_order.attr,
64462306a36Sopenharmony_ci	&dev_attr_iface_erl.attr,
64562306a36Sopenharmony_ci	&dev_attr_iface_max_recv_dlength.attr,
64662306a36Sopenharmony_ci	&dev_attr_iface_first_burst_len.attr,
64762306a36Sopenharmony_ci	&dev_attr_iface_max_outstanding_r2t.attr,
64862306a36Sopenharmony_ci	&dev_attr_iface_max_burst_len.attr,
64962306a36Sopenharmony_ci	&dev_attr_iface_chap_auth.attr,
65062306a36Sopenharmony_ci	&dev_attr_iface_bidi_chap.attr,
65162306a36Sopenharmony_ci	&dev_attr_iface_discovery_auth_optional.attr,
65262306a36Sopenharmony_ci	&dev_attr_iface_discovery_logout.attr,
65362306a36Sopenharmony_ci	&dev_attr_iface_strict_login_comp_en.attr,
65462306a36Sopenharmony_ci	&dev_attr_iface_initiator_name.attr,
65562306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_dns_address_en.attr,
65662306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_slp_da_info_en.attr,
65762306a36Sopenharmony_ci	&dev_attr_ipv4_iface_tos_en.attr,
65862306a36Sopenharmony_ci	&dev_attr_ipv4_iface_tos.attr,
65962306a36Sopenharmony_ci	&dev_attr_ipv4_iface_grat_arp_en.attr,
66062306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_alt_client_id_en.attr,
66162306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_alt_client_id.attr,
66262306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_req_vendor_id_en.attr,
66362306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_use_vendor_id_en.attr,
66462306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_vendor_id.attr,
66562306a36Sopenharmony_ci	&dev_attr_ipv4_iface_dhcp_learn_iqn_en.attr,
66662306a36Sopenharmony_ci	&dev_attr_ipv4_iface_fragment_disable.attr,
66762306a36Sopenharmony_ci	&dev_attr_ipv4_iface_incoming_forwarding_en.attr,
66862306a36Sopenharmony_ci	&dev_attr_ipv4_iface_ttl.attr,
66962306a36Sopenharmony_ci	&dev_attr_ipv6_iface_link_local_state.attr,
67062306a36Sopenharmony_ci	&dev_attr_ipv6_iface_router_state.attr,
67162306a36Sopenharmony_ci	&dev_attr_ipv6_iface_grat_neighbor_adv_en.attr,
67262306a36Sopenharmony_ci	&dev_attr_ipv6_iface_mld_en.attr,
67362306a36Sopenharmony_ci	&dev_attr_ipv6_iface_flow_label.attr,
67462306a36Sopenharmony_ci	&dev_attr_ipv6_iface_traffic_class.attr,
67562306a36Sopenharmony_ci	&dev_attr_ipv6_iface_hop_limit.attr,
67662306a36Sopenharmony_ci	&dev_attr_ipv6_iface_nd_reachable_tmo.attr,
67762306a36Sopenharmony_ci	&dev_attr_ipv6_iface_nd_rexmit_time.attr,
67862306a36Sopenharmony_ci	&dev_attr_ipv6_iface_nd_stale_tmo.attr,
67962306a36Sopenharmony_ci	&dev_attr_ipv6_iface_dup_addr_detect_cnt.attr,
68062306a36Sopenharmony_ci	&dev_attr_ipv6_iface_router_adv_link_mtu.attr,
68162306a36Sopenharmony_ci	NULL,
68262306a36Sopenharmony_ci};
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_cistatic struct attribute_group iscsi_iface_group = {
68562306a36Sopenharmony_ci	.attrs = iscsi_iface_attrs,
68662306a36Sopenharmony_ci	.is_visible = iscsi_iface_attr_is_visible,
68762306a36Sopenharmony_ci};
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci/* convert iscsi_ipaddress_state values to ascii string name */
69062306a36Sopenharmony_cistatic const struct {
69162306a36Sopenharmony_ci	enum iscsi_ipaddress_state	value;
69262306a36Sopenharmony_ci	char				*name;
69362306a36Sopenharmony_ci} iscsi_ipaddress_state_names[] = {
69462306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_UNCONFIGURED,	"Unconfigured" },
69562306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_ACQUIRING,	"Acquiring" },
69662306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_TENTATIVE,	"Tentative" },
69762306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_VALID,		"Valid" },
69862306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_DISABLING,	"Disabling" },
69962306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_INVALID,		"Invalid" },
70062306a36Sopenharmony_ci	{ISCSI_IPDDRESS_STATE_DEPRECATED,	"Deprecated" },
70162306a36Sopenharmony_ci};
70262306a36Sopenharmony_ci
70362306a36Sopenharmony_cichar *iscsi_get_ipaddress_state_name(enum iscsi_ipaddress_state port_state)
70462306a36Sopenharmony_ci{
70562306a36Sopenharmony_ci	int i;
70662306a36Sopenharmony_ci	char *state = NULL;
70762306a36Sopenharmony_ci
70862306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iscsi_ipaddress_state_names); i++) {
70962306a36Sopenharmony_ci		if (iscsi_ipaddress_state_names[i].value == port_state) {
71062306a36Sopenharmony_ci			state = iscsi_ipaddress_state_names[i].name;
71162306a36Sopenharmony_ci			break;
71262306a36Sopenharmony_ci		}
71362306a36Sopenharmony_ci	}
71462306a36Sopenharmony_ci	return state;
71562306a36Sopenharmony_ci}
71662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_ipaddress_state_name);
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci/* convert iscsi_router_state values to ascii string name */
71962306a36Sopenharmony_cistatic const struct {
72062306a36Sopenharmony_ci	enum iscsi_router_state	value;
72162306a36Sopenharmony_ci	char			*name;
72262306a36Sopenharmony_ci} iscsi_router_state_names[] = {
72362306a36Sopenharmony_ci	{ISCSI_ROUTER_STATE_UNKNOWN,		"Unknown" },
72462306a36Sopenharmony_ci	{ISCSI_ROUTER_STATE_ADVERTISED,		"Advertised" },
72562306a36Sopenharmony_ci	{ISCSI_ROUTER_STATE_MANUAL,		"Manual" },
72662306a36Sopenharmony_ci	{ISCSI_ROUTER_STATE_STALE,		"Stale" },
72762306a36Sopenharmony_ci};
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_cichar *iscsi_get_router_state_name(enum iscsi_router_state router_state)
73062306a36Sopenharmony_ci{
73162306a36Sopenharmony_ci	int i;
73262306a36Sopenharmony_ci	char *state = NULL;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iscsi_router_state_names); i++) {
73562306a36Sopenharmony_ci		if (iscsi_router_state_names[i].value == router_state) {
73662306a36Sopenharmony_ci			state = iscsi_router_state_names[i].name;
73762306a36Sopenharmony_ci			break;
73862306a36Sopenharmony_ci		}
73962306a36Sopenharmony_ci	}
74062306a36Sopenharmony_ci	return state;
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_router_state_name);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_cistruct iscsi_iface *
74562306a36Sopenharmony_ciiscsi_create_iface(struct Scsi_Host *shost, struct iscsi_transport *transport,
74662306a36Sopenharmony_ci		   uint32_t iface_type, uint32_t iface_num, int dd_size)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct iscsi_iface *iface;
74962306a36Sopenharmony_ci	int err;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	iface = kzalloc(sizeof(*iface) + dd_size, GFP_KERNEL);
75262306a36Sopenharmony_ci	if (!iface)
75362306a36Sopenharmony_ci		return NULL;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	iface->transport = transport;
75662306a36Sopenharmony_ci	iface->iface_type = iface_type;
75762306a36Sopenharmony_ci	iface->iface_num = iface_num;
75862306a36Sopenharmony_ci	iface->dev.release = iscsi_iface_release;
75962306a36Sopenharmony_ci	iface->dev.class = &iscsi_iface_class;
76062306a36Sopenharmony_ci	/* parent reference released in iscsi_iface_release */
76162306a36Sopenharmony_ci	iface->dev.parent = get_device(&shost->shost_gendev);
76262306a36Sopenharmony_ci	if (iface_type == ISCSI_IFACE_TYPE_IPV4)
76362306a36Sopenharmony_ci		dev_set_name(&iface->dev, "ipv4-iface-%u-%u", shost->host_no,
76462306a36Sopenharmony_ci			     iface_num);
76562306a36Sopenharmony_ci	else
76662306a36Sopenharmony_ci		dev_set_name(&iface->dev, "ipv6-iface-%u-%u", shost->host_no,
76762306a36Sopenharmony_ci			     iface_num);
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_ci	err = device_register(&iface->dev);
77062306a36Sopenharmony_ci	if (err)
77162306a36Sopenharmony_ci		goto put_dev;
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_ci	err = sysfs_create_group(&iface->dev.kobj, &iscsi_iface_group);
77462306a36Sopenharmony_ci	if (err)
77562306a36Sopenharmony_ci		goto unreg_iface;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	if (dd_size)
77862306a36Sopenharmony_ci		iface->dd_data = &iface[1];
77962306a36Sopenharmony_ci	return iface;
78062306a36Sopenharmony_ci
78162306a36Sopenharmony_ciunreg_iface:
78262306a36Sopenharmony_ci	device_unregister(&iface->dev);
78362306a36Sopenharmony_ci	return NULL;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ciput_dev:
78662306a36Sopenharmony_ci	put_device(&iface->dev);
78762306a36Sopenharmony_ci	return NULL;
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_create_iface);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_civoid iscsi_destroy_iface(struct iscsi_iface *iface)
79262306a36Sopenharmony_ci{
79362306a36Sopenharmony_ci	sysfs_remove_group(&iface->dev.kobj, &iscsi_iface_group);
79462306a36Sopenharmony_ci	device_unregister(&iface->dev);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_destroy_iface);
79762306a36Sopenharmony_ci
79862306a36Sopenharmony_ci/*
79962306a36Sopenharmony_ci * Interface to display flash node params to sysfs
80062306a36Sopenharmony_ci */
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci#define ISCSI_FLASHNODE_ATTR(_prefix, _name, _mode, _show, _store)	\
80362306a36Sopenharmony_cistruct device_attribute dev_attr_##_prefix##_##_name =			\
80462306a36Sopenharmony_ci	__ATTR(_name, _mode, _show, _store)
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci/* flash node session attrs show */
80762306a36Sopenharmony_ci#define iscsi_flashnode_sess_attr_show(type, name, param)		\
80862306a36Sopenharmony_cistatic ssize_t								\
80962306a36Sopenharmony_cishow_##type##_##name(struct device *dev, struct device_attribute *attr,	\
81062306a36Sopenharmony_ci		     char *buf)						\
81162306a36Sopenharmony_ci{									\
81262306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess =			\
81362306a36Sopenharmony_ci					iscsi_dev_to_flash_session(dev);\
81462306a36Sopenharmony_ci	struct iscsi_transport *t = fnode_sess->transport;		\
81562306a36Sopenharmony_ci	return t->get_flashnode_param(fnode_sess, param, buf);		\
81662306a36Sopenharmony_ci}									\
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci#define iscsi_flashnode_sess_attr(type, name, param)			\
82062306a36Sopenharmony_ci	iscsi_flashnode_sess_attr_show(type, name, param)		\
82162306a36Sopenharmony_cistatic ISCSI_FLASHNODE_ATTR(type, name, S_IRUGO,			\
82262306a36Sopenharmony_ci			    show_##type##_##name, NULL);
82362306a36Sopenharmony_ci
82462306a36Sopenharmony_ci/* Flash node session attributes */
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, auto_snd_tgt_disable,
82762306a36Sopenharmony_ci			  ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE);
82862306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, discovery_session,
82962306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DISCOVERY_SESS);
83062306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, portal_type, ISCSI_FLASHNODE_PORTAL_TYPE);
83162306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, entry_enable, ISCSI_FLASHNODE_ENTRY_EN);
83262306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, immediate_data, ISCSI_FLASHNODE_IMM_DATA_EN);
83362306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, initial_r2t, ISCSI_FLASHNODE_INITIAL_R2T_EN);
83462306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, data_seq_in_order,
83562306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DATASEQ_INORDER);
83662306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, data_pdu_in_order,
83762306a36Sopenharmony_ci			  ISCSI_FLASHNODE_PDU_INORDER);
83862306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, chap_auth, ISCSI_FLASHNODE_CHAP_AUTH_EN);
83962306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, discovery_logout,
84062306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN);
84162306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, bidi_chap, ISCSI_FLASHNODE_BIDI_CHAP_EN);
84262306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, discovery_auth_optional,
84362306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL);
84462306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, erl, ISCSI_FLASHNODE_ERL);
84562306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, first_burst_len, ISCSI_FLASHNODE_FIRST_BURST);
84662306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, def_time2wait, ISCSI_FLASHNODE_DEF_TIME2WAIT);
84762306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, def_time2retain,
84862306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DEF_TIME2RETAIN);
84962306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, max_outstanding_r2t, ISCSI_FLASHNODE_MAX_R2T);
85062306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, isid, ISCSI_FLASHNODE_ISID);
85162306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, tsid, ISCSI_FLASHNODE_TSID);
85262306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, max_burst_len, ISCSI_FLASHNODE_MAX_BURST);
85362306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, def_taskmgmt_tmo,
85462306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DEF_TASKMGMT_TMO);
85562306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, targetalias, ISCSI_FLASHNODE_ALIAS);
85662306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, targetname, ISCSI_FLASHNODE_NAME);
85762306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, tpgt, ISCSI_FLASHNODE_TPGT);
85862306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, discovery_parent_idx,
85962306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX);
86062306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, discovery_parent_type,
86162306a36Sopenharmony_ci			  ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE);
86262306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, chap_in_idx, ISCSI_FLASHNODE_CHAP_IN_IDX);
86362306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, chap_out_idx, ISCSI_FLASHNODE_CHAP_OUT_IDX);
86462306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, username, ISCSI_FLASHNODE_USERNAME);
86562306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, username_in, ISCSI_FLASHNODE_USERNAME_IN);
86662306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, password, ISCSI_FLASHNODE_PASSWORD);
86762306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, password_in, ISCSI_FLASHNODE_PASSWORD_IN);
86862306a36Sopenharmony_ciiscsi_flashnode_sess_attr(fnode, is_boot_target, ISCSI_FLASHNODE_IS_BOOT_TGT);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_cistatic struct attribute *iscsi_flashnode_sess_attrs[] = {
87162306a36Sopenharmony_ci	&dev_attr_fnode_auto_snd_tgt_disable.attr,
87262306a36Sopenharmony_ci	&dev_attr_fnode_discovery_session.attr,
87362306a36Sopenharmony_ci	&dev_attr_fnode_portal_type.attr,
87462306a36Sopenharmony_ci	&dev_attr_fnode_entry_enable.attr,
87562306a36Sopenharmony_ci	&dev_attr_fnode_immediate_data.attr,
87662306a36Sopenharmony_ci	&dev_attr_fnode_initial_r2t.attr,
87762306a36Sopenharmony_ci	&dev_attr_fnode_data_seq_in_order.attr,
87862306a36Sopenharmony_ci	&dev_attr_fnode_data_pdu_in_order.attr,
87962306a36Sopenharmony_ci	&dev_attr_fnode_chap_auth.attr,
88062306a36Sopenharmony_ci	&dev_attr_fnode_discovery_logout.attr,
88162306a36Sopenharmony_ci	&dev_attr_fnode_bidi_chap.attr,
88262306a36Sopenharmony_ci	&dev_attr_fnode_discovery_auth_optional.attr,
88362306a36Sopenharmony_ci	&dev_attr_fnode_erl.attr,
88462306a36Sopenharmony_ci	&dev_attr_fnode_first_burst_len.attr,
88562306a36Sopenharmony_ci	&dev_attr_fnode_def_time2wait.attr,
88662306a36Sopenharmony_ci	&dev_attr_fnode_def_time2retain.attr,
88762306a36Sopenharmony_ci	&dev_attr_fnode_max_outstanding_r2t.attr,
88862306a36Sopenharmony_ci	&dev_attr_fnode_isid.attr,
88962306a36Sopenharmony_ci	&dev_attr_fnode_tsid.attr,
89062306a36Sopenharmony_ci	&dev_attr_fnode_max_burst_len.attr,
89162306a36Sopenharmony_ci	&dev_attr_fnode_def_taskmgmt_tmo.attr,
89262306a36Sopenharmony_ci	&dev_attr_fnode_targetalias.attr,
89362306a36Sopenharmony_ci	&dev_attr_fnode_targetname.attr,
89462306a36Sopenharmony_ci	&dev_attr_fnode_tpgt.attr,
89562306a36Sopenharmony_ci	&dev_attr_fnode_discovery_parent_idx.attr,
89662306a36Sopenharmony_ci	&dev_attr_fnode_discovery_parent_type.attr,
89762306a36Sopenharmony_ci	&dev_attr_fnode_chap_in_idx.attr,
89862306a36Sopenharmony_ci	&dev_attr_fnode_chap_out_idx.attr,
89962306a36Sopenharmony_ci	&dev_attr_fnode_username.attr,
90062306a36Sopenharmony_ci	&dev_attr_fnode_username_in.attr,
90162306a36Sopenharmony_ci	&dev_attr_fnode_password.attr,
90262306a36Sopenharmony_ci	&dev_attr_fnode_password_in.attr,
90362306a36Sopenharmony_ci	&dev_attr_fnode_is_boot_target.attr,
90462306a36Sopenharmony_ci	NULL,
90562306a36Sopenharmony_ci};
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_cistatic umode_t iscsi_flashnode_sess_attr_is_visible(struct kobject *kobj,
90862306a36Sopenharmony_ci						    struct attribute *attr,
90962306a36Sopenharmony_ci						    int i)
91062306a36Sopenharmony_ci{
91162306a36Sopenharmony_ci	struct device *dev = container_of(kobj, struct device, kobj);
91262306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess =
91362306a36Sopenharmony_ci						iscsi_dev_to_flash_session(dev);
91462306a36Sopenharmony_ci	struct iscsi_transport *t = fnode_sess->transport;
91562306a36Sopenharmony_ci	int param;
91662306a36Sopenharmony_ci
91762306a36Sopenharmony_ci	if (attr == &dev_attr_fnode_auto_snd_tgt_disable.attr) {
91862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_AUTO_SND_TGT_DISABLE;
91962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_discovery_session.attr) {
92062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DISCOVERY_SESS;
92162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_portal_type.attr) {
92262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_PORTAL_TYPE;
92362306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_entry_enable.attr) {
92462306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_ENTRY_EN;
92562306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_immediate_data.attr) {
92662306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IMM_DATA_EN;
92762306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_initial_r2t.attr) {
92862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_INITIAL_R2T_EN;
92962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_data_seq_in_order.attr) {
93062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DATASEQ_INORDER;
93162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_data_pdu_in_order.attr) {
93262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_PDU_INORDER;
93362306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_chap_auth.attr) {
93462306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_CHAP_AUTH_EN;
93562306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_discovery_logout.attr) {
93662306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DISCOVERY_LOGOUT_EN;
93762306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_bidi_chap.attr) {
93862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_BIDI_CHAP_EN;
93962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_discovery_auth_optional.attr) {
94062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DISCOVERY_AUTH_OPTIONAL;
94162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_erl.attr) {
94262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_ERL;
94362306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_first_burst_len.attr) {
94462306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_FIRST_BURST;
94562306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_def_time2wait.attr) {
94662306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DEF_TIME2WAIT;
94762306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_def_time2retain.attr) {
94862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DEF_TIME2RETAIN;
94962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_max_outstanding_r2t.attr) {
95062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_MAX_R2T;
95162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_isid.attr) {
95262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_ISID;
95362306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tsid.attr) {
95462306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TSID;
95562306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_max_burst_len.attr) {
95662306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_MAX_BURST;
95762306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_def_taskmgmt_tmo.attr) {
95862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DEF_TASKMGMT_TMO;
95962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_targetalias.attr) {
96062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_ALIAS;
96162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_targetname.attr) {
96262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_NAME;
96362306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tpgt.attr) {
96462306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TPGT;
96562306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_discovery_parent_idx.attr) {
96662306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DISCOVERY_PARENT_IDX;
96762306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_discovery_parent_type.attr) {
96862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DISCOVERY_PARENT_TYPE;
96962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_chap_in_idx.attr) {
97062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_CHAP_IN_IDX;
97162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_chap_out_idx.attr) {
97262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_CHAP_OUT_IDX;
97362306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_username.attr) {
97462306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_USERNAME;
97562306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_username_in.attr) {
97662306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_USERNAME_IN;
97762306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_password.attr) {
97862306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_PASSWORD;
97962306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_password_in.attr) {
98062306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_PASSWORD_IN;
98162306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_is_boot_target.attr) {
98262306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IS_BOOT_TGT;
98362306a36Sopenharmony_ci	} else {
98462306a36Sopenharmony_ci		WARN_ONCE(1, "Invalid flashnode session attr");
98562306a36Sopenharmony_ci		return 0;
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	return t->attr_is_visible(ISCSI_FLASHNODE_PARAM, param);
98962306a36Sopenharmony_ci}
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_cistatic struct attribute_group iscsi_flashnode_sess_attr_group = {
99262306a36Sopenharmony_ci	.attrs = iscsi_flashnode_sess_attrs,
99362306a36Sopenharmony_ci	.is_visible = iscsi_flashnode_sess_attr_is_visible,
99462306a36Sopenharmony_ci};
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic const struct attribute_group *iscsi_flashnode_sess_attr_groups[] = {
99762306a36Sopenharmony_ci	&iscsi_flashnode_sess_attr_group,
99862306a36Sopenharmony_ci	NULL,
99962306a36Sopenharmony_ci};
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic void iscsi_flashnode_sess_release(struct device *dev)
100262306a36Sopenharmony_ci{
100362306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess =
100462306a36Sopenharmony_ci						iscsi_dev_to_flash_session(dev);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	kfree(fnode_sess->targetname);
100762306a36Sopenharmony_ci	kfree(fnode_sess->targetalias);
100862306a36Sopenharmony_ci	kfree(fnode_sess->portal_type);
100962306a36Sopenharmony_ci	kfree(fnode_sess);
101062306a36Sopenharmony_ci}
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_cistatic const struct device_type iscsi_flashnode_sess_dev_type = {
101362306a36Sopenharmony_ci	.name = "iscsi_flashnode_sess_dev_type",
101462306a36Sopenharmony_ci	.groups = iscsi_flashnode_sess_attr_groups,
101562306a36Sopenharmony_ci	.release = iscsi_flashnode_sess_release,
101662306a36Sopenharmony_ci};
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci/* flash node connection attrs show */
101962306a36Sopenharmony_ci#define iscsi_flashnode_conn_attr_show(type, name, param)		\
102062306a36Sopenharmony_cistatic ssize_t								\
102162306a36Sopenharmony_cishow_##type##_##name(struct device *dev, struct device_attribute *attr,	\
102262306a36Sopenharmony_ci		     char *buf)						\
102362306a36Sopenharmony_ci{									\
102462306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);\
102562306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess =			\
102662306a36Sopenharmony_ci				iscsi_flash_conn_to_flash_session(fnode_conn);\
102762306a36Sopenharmony_ci	struct iscsi_transport *t = fnode_conn->transport;		\
102862306a36Sopenharmony_ci	return t->get_flashnode_param(fnode_sess, param, buf);		\
102962306a36Sopenharmony_ci}									\
103062306a36Sopenharmony_ci
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci#define iscsi_flashnode_conn_attr(type, name, param)			\
103362306a36Sopenharmony_ci	iscsi_flashnode_conn_attr_show(type, name, param)		\
103462306a36Sopenharmony_cistatic ISCSI_FLASHNODE_ATTR(type, name, S_IRUGO,			\
103562306a36Sopenharmony_ci			    show_##type##_##name, NULL);
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci/* Flash node connection attributes */
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, is_fw_assigned_ipv6,
104062306a36Sopenharmony_ci			  ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6);
104162306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, header_digest, ISCSI_FLASHNODE_HDR_DGST_EN);
104262306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, data_digest, ISCSI_FLASHNODE_DATA_DGST_EN);
104362306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, snack_req, ISCSI_FLASHNODE_SNACK_REQ_EN);
104462306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_timestamp_stat,
104562306a36Sopenharmony_ci			  ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT);
104662306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_nagle_disable,
104762306a36Sopenharmony_ci			  ISCSI_FLASHNODE_TCP_NAGLE_DISABLE);
104862306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_wsf_disable,
104962306a36Sopenharmony_ci			  ISCSI_FLASHNODE_TCP_WSF_DISABLE);
105062306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_timer_scale,
105162306a36Sopenharmony_ci			  ISCSI_FLASHNODE_TCP_TIMER_SCALE);
105262306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_timestamp_enable,
105362306a36Sopenharmony_ci			  ISCSI_FLASHNODE_TCP_TIMESTAMP_EN);
105462306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, fragment_disable,
105562306a36Sopenharmony_ci			  ISCSI_FLASHNODE_IP_FRAG_DISABLE);
105662306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, keepalive_tmo, ISCSI_FLASHNODE_KEEPALIVE_TMO);
105762306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, port, ISCSI_FLASHNODE_PORT);
105862306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, ipaddress, ISCSI_FLASHNODE_IPADDR);
105962306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, max_recv_dlength,
106062306a36Sopenharmony_ci			  ISCSI_FLASHNODE_MAX_RECV_DLENGTH);
106162306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, max_xmit_dlength,
106262306a36Sopenharmony_ci			  ISCSI_FLASHNODE_MAX_XMIT_DLENGTH);
106362306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, local_port, ISCSI_FLASHNODE_LOCAL_PORT);
106462306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, ipv4_tos, ISCSI_FLASHNODE_IPV4_TOS);
106562306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, ipv6_traffic_class, ISCSI_FLASHNODE_IPV6_TC);
106662306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, ipv6_flow_label,
106762306a36Sopenharmony_ci			  ISCSI_FLASHNODE_IPV6_FLOW_LABEL);
106862306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, redirect_ipaddr,
106962306a36Sopenharmony_ci			  ISCSI_FLASHNODE_REDIRECT_IPADDR);
107062306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, max_segment_size,
107162306a36Sopenharmony_ci			  ISCSI_FLASHNODE_MAX_SEGMENT_SIZE);
107262306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, link_local_ipv6,
107362306a36Sopenharmony_ci			  ISCSI_FLASHNODE_LINK_LOCAL_IPV6);
107462306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_xmit_wsf, ISCSI_FLASHNODE_TCP_XMIT_WSF);
107562306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, tcp_recv_wsf, ISCSI_FLASHNODE_TCP_RECV_WSF);
107662306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, statsn, ISCSI_FLASHNODE_STATSN);
107762306a36Sopenharmony_ciiscsi_flashnode_conn_attr(fnode, exp_statsn, ISCSI_FLASHNODE_EXP_STATSN);
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_cistatic struct attribute *iscsi_flashnode_conn_attrs[] = {
108062306a36Sopenharmony_ci	&dev_attr_fnode_is_fw_assigned_ipv6.attr,
108162306a36Sopenharmony_ci	&dev_attr_fnode_header_digest.attr,
108262306a36Sopenharmony_ci	&dev_attr_fnode_data_digest.attr,
108362306a36Sopenharmony_ci	&dev_attr_fnode_snack_req.attr,
108462306a36Sopenharmony_ci	&dev_attr_fnode_tcp_timestamp_stat.attr,
108562306a36Sopenharmony_ci	&dev_attr_fnode_tcp_nagle_disable.attr,
108662306a36Sopenharmony_ci	&dev_attr_fnode_tcp_wsf_disable.attr,
108762306a36Sopenharmony_ci	&dev_attr_fnode_tcp_timer_scale.attr,
108862306a36Sopenharmony_ci	&dev_attr_fnode_tcp_timestamp_enable.attr,
108962306a36Sopenharmony_ci	&dev_attr_fnode_fragment_disable.attr,
109062306a36Sopenharmony_ci	&dev_attr_fnode_max_recv_dlength.attr,
109162306a36Sopenharmony_ci	&dev_attr_fnode_max_xmit_dlength.attr,
109262306a36Sopenharmony_ci	&dev_attr_fnode_keepalive_tmo.attr,
109362306a36Sopenharmony_ci	&dev_attr_fnode_port.attr,
109462306a36Sopenharmony_ci	&dev_attr_fnode_ipaddress.attr,
109562306a36Sopenharmony_ci	&dev_attr_fnode_redirect_ipaddr.attr,
109662306a36Sopenharmony_ci	&dev_attr_fnode_max_segment_size.attr,
109762306a36Sopenharmony_ci	&dev_attr_fnode_local_port.attr,
109862306a36Sopenharmony_ci	&dev_attr_fnode_ipv4_tos.attr,
109962306a36Sopenharmony_ci	&dev_attr_fnode_ipv6_traffic_class.attr,
110062306a36Sopenharmony_ci	&dev_attr_fnode_ipv6_flow_label.attr,
110162306a36Sopenharmony_ci	&dev_attr_fnode_link_local_ipv6.attr,
110262306a36Sopenharmony_ci	&dev_attr_fnode_tcp_xmit_wsf.attr,
110362306a36Sopenharmony_ci	&dev_attr_fnode_tcp_recv_wsf.attr,
110462306a36Sopenharmony_ci	&dev_attr_fnode_statsn.attr,
110562306a36Sopenharmony_ci	&dev_attr_fnode_exp_statsn.attr,
110662306a36Sopenharmony_ci	NULL,
110762306a36Sopenharmony_ci};
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic umode_t iscsi_flashnode_conn_attr_is_visible(struct kobject *kobj,
111062306a36Sopenharmony_ci						    struct attribute *attr,
111162306a36Sopenharmony_ci						    int i)
111262306a36Sopenharmony_ci{
111362306a36Sopenharmony_ci	struct device *dev = container_of(kobj, struct device, kobj);
111462306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);
111562306a36Sopenharmony_ci	struct iscsi_transport *t = fnode_conn->transport;
111662306a36Sopenharmony_ci	int param;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (attr == &dev_attr_fnode_is_fw_assigned_ipv6.attr) {
111962306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IS_FW_ASSIGNED_IPV6;
112062306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_header_digest.attr) {
112162306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_HDR_DGST_EN;
112262306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_data_digest.attr) {
112362306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_DATA_DGST_EN;
112462306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_snack_req.attr) {
112562306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_SNACK_REQ_EN;
112662306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_timestamp_stat.attr) {
112762306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_TIMESTAMP_STAT;
112862306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_nagle_disable.attr) {
112962306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_NAGLE_DISABLE;
113062306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_wsf_disable.attr) {
113162306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_WSF_DISABLE;
113262306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_timer_scale.attr) {
113362306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_TIMER_SCALE;
113462306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_timestamp_enable.attr) {
113562306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_TIMESTAMP_EN;
113662306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_fragment_disable.attr) {
113762306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IP_FRAG_DISABLE;
113862306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_max_recv_dlength.attr) {
113962306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_MAX_RECV_DLENGTH;
114062306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_max_xmit_dlength.attr) {
114162306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_MAX_XMIT_DLENGTH;
114262306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_keepalive_tmo.attr) {
114362306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_KEEPALIVE_TMO;
114462306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_port.attr) {
114562306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_PORT;
114662306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_ipaddress.attr) {
114762306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IPADDR;
114862306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_redirect_ipaddr.attr) {
114962306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_REDIRECT_IPADDR;
115062306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_max_segment_size.attr) {
115162306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_MAX_SEGMENT_SIZE;
115262306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_local_port.attr) {
115362306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_LOCAL_PORT;
115462306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_ipv4_tos.attr) {
115562306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IPV4_TOS;
115662306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_ipv6_traffic_class.attr) {
115762306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IPV6_TC;
115862306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_ipv6_flow_label.attr) {
115962306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_IPV6_FLOW_LABEL;
116062306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_link_local_ipv6.attr) {
116162306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_LINK_LOCAL_IPV6;
116262306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_xmit_wsf.attr) {
116362306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_XMIT_WSF;
116462306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_tcp_recv_wsf.attr) {
116562306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_TCP_RECV_WSF;
116662306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_statsn.attr) {
116762306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_STATSN;
116862306a36Sopenharmony_ci	} else if (attr == &dev_attr_fnode_exp_statsn.attr) {
116962306a36Sopenharmony_ci		param = ISCSI_FLASHNODE_EXP_STATSN;
117062306a36Sopenharmony_ci	} else {
117162306a36Sopenharmony_ci		WARN_ONCE(1, "Invalid flashnode connection attr");
117262306a36Sopenharmony_ci		return 0;
117362306a36Sopenharmony_ci	}
117462306a36Sopenharmony_ci
117562306a36Sopenharmony_ci	return t->attr_is_visible(ISCSI_FLASHNODE_PARAM, param);
117662306a36Sopenharmony_ci}
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic struct attribute_group iscsi_flashnode_conn_attr_group = {
117962306a36Sopenharmony_ci	.attrs = iscsi_flashnode_conn_attrs,
118062306a36Sopenharmony_ci	.is_visible = iscsi_flashnode_conn_attr_is_visible,
118162306a36Sopenharmony_ci};
118262306a36Sopenharmony_ci
118362306a36Sopenharmony_cistatic const struct attribute_group *iscsi_flashnode_conn_attr_groups[] = {
118462306a36Sopenharmony_ci	&iscsi_flashnode_conn_attr_group,
118562306a36Sopenharmony_ci	NULL,
118662306a36Sopenharmony_ci};
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_cistatic void iscsi_flashnode_conn_release(struct device *dev)
118962306a36Sopenharmony_ci{
119062306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn = iscsi_dev_to_flash_conn(dev);
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci	kfree(fnode_conn->ipaddress);
119362306a36Sopenharmony_ci	kfree(fnode_conn->redirect_ipaddr);
119462306a36Sopenharmony_ci	kfree(fnode_conn->link_local_ipv6_addr);
119562306a36Sopenharmony_ci	kfree(fnode_conn);
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic const struct device_type iscsi_flashnode_conn_dev_type = {
119962306a36Sopenharmony_ci	.name = "iscsi_flashnode_conn_dev_type",
120062306a36Sopenharmony_ci	.groups = iscsi_flashnode_conn_attr_groups,
120162306a36Sopenharmony_ci	.release = iscsi_flashnode_conn_release,
120262306a36Sopenharmony_ci};
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_cistatic struct bus_type iscsi_flashnode_bus;
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ciint iscsi_flashnode_bus_match(struct device *dev,
120762306a36Sopenharmony_ci				     struct device_driver *drv)
120862306a36Sopenharmony_ci{
120962306a36Sopenharmony_ci	if (dev->bus == &iscsi_flashnode_bus)
121062306a36Sopenharmony_ci		return 1;
121162306a36Sopenharmony_ci	return 0;
121262306a36Sopenharmony_ci}
121362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_flashnode_bus_match);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_cistatic struct bus_type iscsi_flashnode_bus = {
121662306a36Sopenharmony_ci	.name = "iscsi_flashnode",
121762306a36Sopenharmony_ci	.match = &iscsi_flashnode_bus_match,
121862306a36Sopenharmony_ci};
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci/**
122162306a36Sopenharmony_ci * iscsi_create_flashnode_sess - Add flashnode session entry in sysfs
122262306a36Sopenharmony_ci * @shost: pointer to host data
122362306a36Sopenharmony_ci * @index: index of flashnode to add in sysfs
122462306a36Sopenharmony_ci * @transport: pointer to transport data
122562306a36Sopenharmony_ci * @dd_size: total size to allocate
122662306a36Sopenharmony_ci *
122762306a36Sopenharmony_ci * Adds a sysfs entry for the flashnode session attributes
122862306a36Sopenharmony_ci *
122962306a36Sopenharmony_ci * Returns:
123062306a36Sopenharmony_ci *  pointer to allocated flashnode sess on success
123162306a36Sopenharmony_ci *  %NULL on failure
123262306a36Sopenharmony_ci */
123362306a36Sopenharmony_cistruct iscsi_bus_flash_session *
123462306a36Sopenharmony_ciiscsi_create_flashnode_sess(struct Scsi_Host *shost, int index,
123562306a36Sopenharmony_ci			    struct iscsi_transport *transport,
123662306a36Sopenharmony_ci			    int dd_size)
123762306a36Sopenharmony_ci{
123862306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess;
123962306a36Sopenharmony_ci	int err;
124062306a36Sopenharmony_ci
124162306a36Sopenharmony_ci	fnode_sess = kzalloc(sizeof(*fnode_sess) + dd_size, GFP_KERNEL);
124262306a36Sopenharmony_ci	if (!fnode_sess)
124362306a36Sopenharmony_ci		return NULL;
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci	fnode_sess->transport = transport;
124662306a36Sopenharmony_ci	fnode_sess->target_id = index;
124762306a36Sopenharmony_ci	fnode_sess->dev.type = &iscsi_flashnode_sess_dev_type;
124862306a36Sopenharmony_ci	fnode_sess->dev.bus = &iscsi_flashnode_bus;
124962306a36Sopenharmony_ci	fnode_sess->dev.parent = &shost->shost_gendev;
125062306a36Sopenharmony_ci	dev_set_name(&fnode_sess->dev, "flashnode_sess-%u:%u",
125162306a36Sopenharmony_ci		     shost->host_no, index);
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci	err = device_register(&fnode_sess->dev);
125462306a36Sopenharmony_ci	if (err)
125562306a36Sopenharmony_ci		goto put_dev;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	if (dd_size)
125862306a36Sopenharmony_ci		fnode_sess->dd_data = &fnode_sess[1];
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci	return fnode_sess;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ciput_dev:
126362306a36Sopenharmony_ci	put_device(&fnode_sess->dev);
126462306a36Sopenharmony_ci	return NULL;
126562306a36Sopenharmony_ci}
126662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_create_flashnode_sess);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci/**
126962306a36Sopenharmony_ci * iscsi_create_flashnode_conn - Add flashnode conn entry in sysfs
127062306a36Sopenharmony_ci * @shost: pointer to host data
127162306a36Sopenharmony_ci * @fnode_sess: pointer to the parent flashnode session entry
127262306a36Sopenharmony_ci * @transport: pointer to transport data
127362306a36Sopenharmony_ci * @dd_size: total size to allocate
127462306a36Sopenharmony_ci *
127562306a36Sopenharmony_ci * Adds a sysfs entry for the flashnode connection attributes
127662306a36Sopenharmony_ci *
127762306a36Sopenharmony_ci * Returns:
127862306a36Sopenharmony_ci *  pointer to allocated flashnode conn on success
127962306a36Sopenharmony_ci *  %NULL on failure
128062306a36Sopenharmony_ci */
128162306a36Sopenharmony_cistruct iscsi_bus_flash_conn *
128262306a36Sopenharmony_ciiscsi_create_flashnode_conn(struct Scsi_Host *shost,
128362306a36Sopenharmony_ci			    struct iscsi_bus_flash_session *fnode_sess,
128462306a36Sopenharmony_ci			    struct iscsi_transport *transport,
128562306a36Sopenharmony_ci			    int dd_size)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn;
128862306a36Sopenharmony_ci	int err;
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_ci	fnode_conn = kzalloc(sizeof(*fnode_conn) + dd_size, GFP_KERNEL);
129162306a36Sopenharmony_ci	if (!fnode_conn)
129262306a36Sopenharmony_ci		return NULL;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	fnode_conn->transport = transport;
129562306a36Sopenharmony_ci	fnode_conn->dev.type = &iscsi_flashnode_conn_dev_type;
129662306a36Sopenharmony_ci	fnode_conn->dev.bus = &iscsi_flashnode_bus;
129762306a36Sopenharmony_ci	fnode_conn->dev.parent = &fnode_sess->dev;
129862306a36Sopenharmony_ci	dev_set_name(&fnode_conn->dev, "flashnode_conn-%u:%u:0",
129962306a36Sopenharmony_ci		     shost->host_no, fnode_sess->target_id);
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	err = device_register(&fnode_conn->dev);
130262306a36Sopenharmony_ci	if (err)
130362306a36Sopenharmony_ci		goto put_dev;
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	if (dd_size)
130662306a36Sopenharmony_ci		fnode_conn->dd_data = &fnode_conn[1];
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return fnode_conn;
130962306a36Sopenharmony_ci
131062306a36Sopenharmony_ciput_dev:
131162306a36Sopenharmony_ci	put_device(&fnode_conn->dev);
131262306a36Sopenharmony_ci	return NULL;
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_create_flashnode_conn);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci/**
131762306a36Sopenharmony_ci * iscsi_is_flashnode_conn_dev - verify passed device is to be flashnode conn
131862306a36Sopenharmony_ci * @dev: device to verify
131962306a36Sopenharmony_ci * @data: pointer to data containing value to use for verification
132062306a36Sopenharmony_ci *
132162306a36Sopenharmony_ci * Verifies if the passed device is flashnode conn device
132262306a36Sopenharmony_ci *
132362306a36Sopenharmony_ci * Returns:
132462306a36Sopenharmony_ci *  1 on success
132562306a36Sopenharmony_ci *  0 on failure
132662306a36Sopenharmony_ci */
132762306a36Sopenharmony_cistatic int iscsi_is_flashnode_conn_dev(struct device *dev, void *data)
132862306a36Sopenharmony_ci{
132962306a36Sopenharmony_ci	return dev->bus == &iscsi_flashnode_bus;
133062306a36Sopenharmony_ci}
133162306a36Sopenharmony_ci
133262306a36Sopenharmony_cistatic int iscsi_destroy_flashnode_conn(struct iscsi_bus_flash_conn *fnode_conn)
133362306a36Sopenharmony_ci{
133462306a36Sopenharmony_ci	device_unregister(&fnode_conn->dev);
133562306a36Sopenharmony_ci	return 0;
133662306a36Sopenharmony_ci}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_cistatic int flashnode_match_index(struct device *dev, void *data)
133962306a36Sopenharmony_ci{
134062306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess = NULL;
134162306a36Sopenharmony_ci	int ret = 0;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (!iscsi_flashnode_bus_match(dev, NULL))
134462306a36Sopenharmony_ci		goto exit_match_index;
134562306a36Sopenharmony_ci
134662306a36Sopenharmony_ci	fnode_sess = iscsi_dev_to_flash_session(dev);
134762306a36Sopenharmony_ci	ret = (fnode_sess->target_id == *((int *)data)) ? 1 : 0;
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ciexit_match_index:
135062306a36Sopenharmony_ci	return ret;
135162306a36Sopenharmony_ci}
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci/**
135462306a36Sopenharmony_ci * iscsi_get_flashnode_by_index -finds flashnode session entry by index
135562306a36Sopenharmony_ci * @shost: pointer to host data
135662306a36Sopenharmony_ci * @idx: index to match
135762306a36Sopenharmony_ci *
135862306a36Sopenharmony_ci * Finds the flashnode session object for the passed index
135962306a36Sopenharmony_ci *
136062306a36Sopenharmony_ci * Returns:
136162306a36Sopenharmony_ci *  pointer to found flashnode session object on success
136262306a36Sopenharmony_ci *  %NULL on failure
136362306a36Sopenharmony_ci */
136462306a36Sopenharmony_cistatic struct iscsi_bus_flash_session *
136562306a36Sopenharmony_ciiscsi_get_flashnode_by_index(struct Scsi_Host *shost, uint32_t idx)
136662306a36Sopenharmony_ci{
136762306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess = NULL;
136862306a36Sopenharmony_ci	struct device *dev;
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	dev = device_find_child(&shost->shost_gendev, &idx,
137162306a36Sopenharmony_ci				flashnode_match_index);
137262306a36Sopenharmony_ci	if (dev)
137362306a36Sopenharmony_ci		fnode_sess = iscsi_dev_to_flash_session(dev);
137462306a36Sopenharmony_ci
137562306a36Sopenharmony_ci	return fnode_sess;
137662306a36Sopenharmony_ci}
137762306a36Sopenharmony_ci
137862306a36Sopenharmony_ci/**
137962306a36Sopenharmony_ci * iscsi_find_flashnode_sess - finds flashnode session entry
138062306a36Sopenharmony_ci * @shost: pointer to host data
138162306a36Sopenharmony_ci * @data: pointer to data containing value to use for comparison
138262306a36Sopenharmony_ci * @fn: function pointer that does actual comparison
138362306a36Sopenharmony_ci *
138462306a36Sopenharmony_ci * Finds the flashnode session object comparing the data passed using logic
138562306a36Sopenharmony_ci * defined in passed function pointer
138662306a36Sopenharmony_ci *
138762306a36Sopenharmony_ci * Returns:
138862306a36Sopenharmony_ci *  pointer to found flashnode session device object on success
138962306a36Sopenharmony_ci *  %NULL on failure
139062306a36Sopenharmony_ci */
139162306a36Sopenharmony_cistruct device *
139262306a36Sopenharmony_ciiscsi_find_flashnode_sess(struct Scsi_Host *shost, void *data,
139362306a36Sopenharmony_ci			  int (*fn)(struct device *dev, void *data))
139462306a36Sopenharmony_ci{
139562306a36Sopenharmony_ci	return device_find_child(&shost->shost_gendev, data, fn);
139662306a36Sopenharmony_ci}
139762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_find_flashnode_sess);
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_ci/**
140062306a36Sopenharmony_ci * iscsi_find_flashnode_conn - finds flashnode connection entry
140162306a36Sopenharmony_ci * @fnode_sess: pointer to parent flashnode session entry
140262306a36Sopenharmony_ci *
140362306a36Sopenharmony_ci * Finds the flashnode connection object comparing the data passed using logic
140462306a36Sopenharmony_ci * defined in passed function pointer
140562306a36Sopenharmony_ci *
140662306a36Sopenharmony_ci * Returns:
140762306a36Sopenharmony_ci *  pointer to found flashnode connection device object on success
140862306a36Sopenharmony_ci *  %NULL on failure
140962306a36Sopenharmony_ci */
141062306a36Sopenharmony_cistruct device *
141162306a36Sopenharmony_ciiscsi_find_flashnode_conn(struct iscsi_bus_flash_session *fnode_sess)
141262306a36Sopenharmony_ci{
141362306a36Sopenharmony_ci	return device_find_child(&fnode_sess->dev, NULL,
141462306a36Sopenharmony_ci				 iscsi_is_flashnode_conn_dev);
141562306a36Sopenharmony_ci}
141662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_find_flashnode_conn);
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_cistatic int iscsi_iter_destroy_flashnode_conn_fn(struct device *dev, void *data)
141962306a36Sopenharmony_ci{
142062306a36Sopenharmony_ci	if (!iscsi_is_flashnode_conn_dev(dev, NULL))
142162306a36Sopenharmony_ci		return 0;
142262306a36Sopenharmony_ci
142362306a36Sopenharmony_ci	return iscsi_destroy_flashnode_conn(iscsi_dev_to_flash_conn(dev));
142462306a36Sopenharmony_ci}
142562306a36Sopenharmony_ci
142662306a36Sopenharmony_ci/**
142762306a36Sopenharmony_ci * iscsi_destroy_flashnode_sess - destroy flashnode session entry
142862306a36Sopenharmony_ci * @fnode_sess: pointer to flashnode session entry to be destroyed
142962306a36Sopenharmony_ci *
143062306a36Sopenharmony_ci * Deletes the flashnode session entry and all children flashnode connection
143162306a36Sopenharmony_ci * entries from sysfs
143262306a36Sopenharmony_ci */
143362306a36Sopenharmony_civoid iscsi_destroy_flashnode_sess(struct iscsi_bus_flash_session *fnode_sess)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	int err;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	err = device_for_each_child(&fnode_sess->dev, NULL,
143862306a36Sopenharmony_ci				    iscsi_iter_destroy_flashnode_conn_fn);
143962306a36Sopenharmony_ci	if (err)
144062306a36Sopenharmony_ci		pr_err("Could not delete all connections for %s. Error %d.\n",
144162306a36Sopenharmony_ci		       fnode_sess->dev.kobj.name, err);
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	device_unregister(&fnode_sess->dev);
144462306a36Sopenharmony_ci}
144562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_destroy_flashnode_sess);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_cistatic int iscsi_iter_destroy_flashnode_fn(struct device *dev, void *data)
144862306a36Sopenharmony_ci{
144962306a36Sopenharmony_ci	if (!iscsi_flashnode_bus_match(dev, NULL))
145062306a36Sopenharmony_ci		return 0;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci	iscsi_destroy_flashnode_sess(iscsi_dev_to_flash_session(dev));
145362306a36Sopenharmony_ci	return 0;
145462306a36Sopenharmony_ci}
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci/**
145762306a36Sopenharmony_ci * iscsi_destroy_all_flashnode - destroy all flashnode session entries
145862306a36Sopenharmony_ci * @shost: pointer to host data
145962306a36Sopenharmony_ci *
146062306a36Sopenharmony_ci * Destroys all the flashnode session entries and all corresponding children
146162306a36Sopenharmony_ci * flashnode connection entries from sysfs
146262306a36Sopenharmony_ci */
146362306a36Sopenharmony_civoid iscsi_destroy_all_flashnode(struct Scsi_Host *shost)
146462306a36Sopenharmony_ci{
146562306a36Sopenharmony_ci	device_for_each_child(&shost->shost_gendev, NULL,
146662306a36Sopenharmony_ci			      iscsi_iter_destroy_flashnode_fn);
146762306a36Sopenharmony_ci}
146862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_destroy_all_flashnode);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci/*
147162306a36Sopenharmony_ci * BSG support
147262306a36Sopenharmony_ci */
147362306a36Sopenharmony_ci/**
147462306a36Sopenharmony_ci * iscsi_bsg_host_dispatch - Dispatch command to LLD.
147562306a36Sopenharmony_ci * @job: bsg job to be processed
147662306a36Sopenharmony_ci */
147762306a36Sopenharmony_cistatic int iscsi_bsg_host_dispatch(struct bsg_job *job)
147862306a36Sopenharmony_ci{
147962306a36Sopenharmony_ci	struct Scsi_Host *shost = iscsi_job_to_shost(job);
148062306a36Sopenharmony_ci	struct iscsi_bsg_request *req = job->request;
148162306a36Sopenharmony_ci	struct iscsi_bsg_reply *reply = job->reply;
148262306a36Sopenharmony_ci	struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
148362306a36Sopenharmony_ci	int cmdlen = sizeof(uint32_t);	/* start with length of msgcode */
148462306a36Sopenharmony_ci	int ret;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	/* check if we have the msgcode value at least */
148762306a36Sopenharmony_ci	if (job->request_len < sizeof(uint32_t)) {
148862306a36Sopenharmony_ci		ret = -ENOMSG;
148962306a36Sopenharmony_ci		goto fail_host_msg;
149062306a36Sopenharmony_ci	}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	/* Validate the host command */
149362306a36Sopenharmony_ci	switch (req->msgcode) {
149462306a36Sopenharmony_ci	case ISCSI_BSG_HST_VENDOR:
149562306a36Sopenharmony_ci		cmdlen += sizeof(struct iscsi_bsg_host_vendor);
149662306a36Sopenharmony_ci		if ((shost->hostt->vendor_id == 0L) ||
149762306a36Sopenharmony_ci		    (req->rqst_data.h_vendor.vendor_id !=
149862306a36Sopenharmony_ci			shost->hostt->vendor_id)) {
149962306a36Sopenharmony_ci			ret = -ESRCH;
150062306a36Sopenharmony_ci			goto fail_host_msg;
150162306a36Sopenharmony_ci		}
150262306a36Sopenharmony_ci		break;
150362306a36Sopenharmony_ci	default:
150462306a36Sopenharmony_ci		ret = -EBADR;
150562306a36Sopenharmony_ci		goto fail_host_msg;
150662306a36Sopenharmony_ci	}
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	/* check if we really have all the request data needed */
150962306a36Sopenharmony_ci	if (job->request_len < cmdlen) {
151062306a36Sopenharmony_ci		ret = -ENOMSG;
151162306a36Sopenharmony_ci		goto fail_host_msg;
151262306a36Sopenharmony_ci	}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_ci	ret = i->iscsi_transport->bsg_request(job);
151562306a36Sopenharmony_ci	if (!ret)
151662306a36Sopenharmony_ci		return 0;
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_cifail_host_msg:
151962306a36Sopenharmony_ci	/* return the errno failure code as the only status */
152062306a36Sopenharmony_ci	BUG_ON(job->reply_len < sizeof(uint32_t));
152162306a36Sopenharmony_ci	reply->reply_payload_rcv_len = 0;
152262306a36Sopenharmony_ci	reply->result = ret;
152362306a36Sopenharmony_ci	job->reply_len = sizeof(uint32_t);
152462306a36Sopenharmony_ci	bsg_job_done(job, ret, 0);
152562306a36Sopenharmony_ci	return 0;
152662306a36Sopenharmony_ci}
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci/**
152962306a36Sopenharmony_ci * iscsi_bsg_host_add - Create and add the bsg hooks to receive requests
153062306a36Sopenharmony_ci * @shost: shost for iscsi_host
153162306a36Sopenharmony_ci * @ihost: iscsi_cls_host adding the structures to
153262306a36Sopenharmony_ci */
153362306a36Sopenharmony_cistatic int
153462306a36Sopenharmony_ciiscsi_bsg_host_add(struct Scsi_Host *shost, struct iscsi_cls_host *ihost)
153562306a36Sopenharmony_ci{
153662306a36Sopenharmony_ci	struct device *dev = &shost->shost_gendev;
153762306a36Sopenharmony_ci	struct iscsi_internal *i = to_iscsi_internal(shost->transportt);
153862306a36Sopenharmony_ci	struct request_queue *q;
153962306a36Sopenharmony_ci	char bsg_name[20];
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (!i->iscsi_transport->bsg_request)
154262306a36Sopenharmony_ci		return -ENOTSUPP;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	snprintf(bsg_name, sizeof(bsg_name), "iscsi_host%d", shost->host_no);
154562306a36Sopenharmony_ci	q = bsg_setup_queue(dev, bsg_name, iscsi_bsg_host_dispatch, NULL, 0);
154662306a36Sopenharmony_ci	if (IS_ERR(q)) {
154762306a36Sopenharmony_ci		shost_printk(KERN_ERR, shost, "bsg interface failed to "
154862306a36Sopenharmony_ci			     "initialize - no request queue\n");
154962306a36Sopenharmony_ci		return PTR_ERR(q);
155062306a36Sopenharmony_ci	}
155162306a36Sopenharmony_ci	__scsi_init_queue(shost, q);
155262306a36Sopenharmony_ci
155362306a36Sopenharmony_ci	ihost->bsg_q = q;
155462306a36Sopenharmony_ci	return 0;
155562306a36Sopenharmony_ci}
155662306a36Sopenharmony_ci
155762306a36Sopenharmony_cistatic int iscsi_setup_host(struct transport_container *tc, struct device *dev,
155862306a36Sopenharmony_ci			    struct device *cdev)
155962306a36Sopenharmony_ci{
156062306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(dev);
156162306a36Sopenharmony_ci	struct iscsi_cls_host *ihost = shost->shost_data;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	memset(ihost, 0, sizeof(*ihost));
156462306a36Sopenharmony_ci	mutex_init(&ihost->mutex);
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	iscsi_bsg_host_add(shost, ihost);
156762306a36Sopenharmony_ci	/* ignore any bsg add error - we just can't do sgio */
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	return 0;
157062306a36Sopenharmony_ci}
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_cistatic int iscsi_remove_host(struct transport_container *tc,
157362306a36Sopenharmony_ci			     struct device *dev, struct device *cdev)
157462306a36Sopenharmony_ci{
157562306a36Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(dev);
157662306a36Sopenharmony_ci	struct iscsi_cls_host *ihost = shost->shost_data;
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	bsg_remove_queue(ihost->bsg_q);
157962306a36Sopenharmony_ci	return 0;
158062306a36Sopenharmony_ci}
158162306a36Sopenharmony_ci
158262306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(iscsi_host_class,
158362306a36Sopenharmony_ci			       "iscsi_host",
158462306a36Sopenharmony_ci			       iscsi_setup_host,
158562306a36Sopenharmony_ci			       iscsi_remove_host,
158662306a36Sopenharmony_ci			       NULL);
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(iscsi_session_class,
158962306a36Sopenharmony_ci			       "iscsi_session",
159062306a36Sopenharmony_ci			       NULL,
159162306a36Sopenharmony_ci			       NULL,
159262306a36Sopenharmony_ci			       NULL);
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic DECLARE_TRANSPORT_CLASS(iscsi_connection_class,
159562306a36Sopenharmony_ci			       "iscsi_connection",
159662306a36Sopenharmony_ci			       NULL,
159762306a36Sopenharmony_ci			       NULL,
159862306a36Sopenharmony_ci			       NULL);
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_cistatic struct sock *nls;
160162306a36Sopenharmony_cistatic DEFINE_MUTEX(rx_queue_mutex);
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cistatic LIST_HEAD(sesslist);
160462306a36Sopenharmony_cistatic DEFINE_SPINLOCK(sesslock);
160562306a36Sopenharmony_cistatic LIST_HEAD(connlist);
160662306a36Sopenharmony_cistatic LIST_HEAD(connlist_err);
160762306a36Sopenharmony_cistatic DEFINE_SPINLOCK(connlock);
160862306a36Sopenharmony_ci
160962306a36Sopenharmony_cistatic uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn)
161062306a36Sopenharmony_ci{
161162306a36Sopenharmony_ci	struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent);
161262306a36Sopenharmony_ci	return sess->sid;
161362306a36Sopenharmony_ci}
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_ci/*
161662306a36Sopenharmony_ci * Returns the matching session to a given sid
161762306a36Sopenharmony_ci */
161862306a36Sopenharmony_cistatic struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid)
161962306a36Sopenharmony_ci{
162062306a36Sopenharmony_ci	unsigned long flags;
162162306a36Sopenharmony_ci	struct iscsi_cls_session *sess;
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	spin_lock_irqsave(&sesslock, flags);
162462306a36Sopenharmony_ci	list_for_each_entry(sess, &sesslist, sess_list) {
162562306a36Sopenharmony_ci		if (sess->sid == sid) {
162662306a36Sopenharmony_ci			spin_unlock_irqrestore(&sesslock, flags);
162762306a36Sopenharmony_ci			return sess;
162862306a36Sopenharmony_ci		}
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci	spin_unlock_irqrestore(&sesslock, flags);
163162306a36Sopenharmony_ci	return NULL;
163262306a36Sopenharmony_ci}
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci/*
163562306a36Sopenharmony_ci * Returns the matching connection to a given sid / cid tuple
163662306a36Sopenharmony_ci */
163762306a36Sopenharmony_cistatic struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	unsigned long flags;
164062306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	spin_lock_irqsave(&connlock, flags);
164362306a36Sopenharmony_ci	list_for_each_entry(conn, &connlist, conn_list) {
164462306a36Sopenharmony_ci		if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) {
164562306a36Sopenharmony_ci			spin_unlock_irqrestore(&connlock, flags);
164662306a36Sopenharmony_ci			return conn;
164762306a36Sopenharmony_ci		}
164862306a36Sopenharmony_ci	}
164962306a36Sopenharmony_ci	spin_unlock_irqrestore(&connlock, flags);
165062306a36Sopenharmony_ci	return NULL;
165162306a36Sopenharmony_ci}
165262306a36Sopenharmony_ci
165362306a36Sopenharmony_ci/*
165462306a36Sopenharmony_ci * The following functions can be used by LLDs that allocate
165562306a36Sopenharmony_ci * their own scsi_hosts or by software iscsi LLDs
165662306a36Sopenharmony_ci */
165762306a36Sopenharmony_cistatic struct {
165862306a36Sopenharmony_ci	int value;
165962306a36Sopenharmony_ci	char *name;
166062306a36Sopenharmony_ci} iscsi_session_state_names[] = {
166162306a36Sopenharmony_ci	{ ISCSI_SESSION_LOGGED_IN,	"LOGGED_IN" },
166262306a36Sopenharmony_ci	{ ISCSI_SESSION_FAILED,		"FAILED" },
166362306a36Sopenharmony_ci	{ ISCSI_SESSION_FREE,		"FREE" },
166462306a36Sopenharmony_ci};
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_cistatic const char *iscsi_session_state_name(int state)
166762306a36Sopenharmony_ci{
166862306a36Sopenharmony_ci	int i;
166962306a36Sopenharmony_ci	char *name = NULL;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iscsi_session_state_names); i++) {
167262306a36Sopenharmony_ci		if (iscsi_session_state_names[i].value == state) {
167362306a36Sopenharmony_ci			name = iscsi_session_state_names[i].name;
167462306a36Sopenharmony_ci			break;
167562306a36Sopenharmony_ci		}
167662306a36Sopenharmony_ci	}
167762306a36Sopenharmony_ci	return name;
167862306a36Sopenharmony_ci}
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic char *iscsi_session_target_state_name[] = {
168162306a36Sopenharmony_ci	[ISCSI_SESSION_TARGET_UNBOUND]   = "UNBOUND",
168262306a36Sopenharmony_ci	[ISCSI_SESSION_TARGET_ALLOCATED] = "ALLOCATED",
168362306a36Sopenharmony_ci	[ISCSI_SESSION_TARGET_SCANNED]   = "SCANNED",
168462306a36Sopenharmony_ci	[ISCSI_SESSION_TARGET_UNBINDING] = "UNBINDING",
168562306a36Sopenharmony_ci};
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ciint iscsi_session_chkready(struct iscsi_cls_session *session)
168862306a36Sopenharmony_ci{
168962306a36Sopenharmony_ci	int err;
169062306a36Sopenharmony_ci
169162306a36Sopenharmony_ci	switch (session->state) {
169262306a36Sopenharmony_ci	case ISCSI_SESSION_LOGGED_IN:
169362306a36Sopenharmony_ci		err = 0;
169462306a36Sopenharmony_ci		break;
169562306a36Sopenharmony_ci	case ISCSI_SESSION_FAILED:
169662306a36Sopenharmony_ci		err = DID_IMM_RETRY << 16;
169762306a36Sopenharmony_ci		break;
169862306a36Sopenharmony_ci	case ISCSI_SESSION_FREE:
169962306a36Sopenharmony_ci		err = DID_TRANSPORT_FAILFAST << 16;
170062306a36Sopenharmony_ci		break;
170162306a36Sopenharmony_ci	default:
170262306a36Sopenharmony_ci		err = DID_NO_CONNECT << 16;
170362306a36Sopenharmony_ci		break;
170462306a36Sopenharmony_ci	}
170562306a36Sopenharmony_ci	return err;
170662306a36Sopenharmony_ci}
170762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_chkready);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ciint iscsi_is_session_online(struct iscsi_cls_session *session)
171062306a36Sopenharmony_ci{
171162306a36Sopenharmony_ci	unsigned long flags;
171262306a36Sopenharmony_ci	int ret = 0;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
171562306a36Sopenharmony_ci	if (session->state == ISCSI_SESSION_LOGGED_IN)
171662306a36Sopenharmony_ci		ret = 1;
171762306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
171862306a36Sopenharmony_ci	return ret;
171962306a36Sopenharmony_ci}
172062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_is_session_online);
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_cistatic void iscsi_session_release(struct device *dev)
172362306a36Sopenharmony_ci{
172462306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_dev_to_session(dev);
172562306a36Sopenharmony_ci	struct Scsi_Host *shost;
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_ci	shost = iscsi_session_to_shost(session);
172862306a36Sopenharmony_ci	scsi_host_put(shost);
172962306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completing session release\n");
173062306a36Sopenharmony_ci	kfree(session);
173162306a36Sopenharmony_ci}
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ciint iscsi_is_session_dev(const struct device *dev)
173462306a36Sopenharmony_ci{
173562306a36Sopenharmony_ci	return dev->release == iscsi_session_release;
173662306a36Sopenharmony_ci}
173762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_is_session_dev);
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_cistatic int iscsi_iter_session_fn(struct device *dev, void *data)
174062306a36Sopenharmony_ci{
174162306a36Sopenharmony_ci	void (* fn) (struct iscsi_cls_session *) = data;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	if (!iscsi_is_session_dev(dev))
174462306a36Sopenharmony_ci		return 0;
174562306a36Sopenharmony_ci	fn(iscsi_dev_to_session(dev));
174662306a36Sopenharmony_ci	return 0;
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_civoid iscsi_host_for_each_session(struct Scsi_Host *shost,
175062306a36Sopenharmony_ci				 void (*fn)(struct iscsi_cls_session *))
175162306a36Sopenharmony_ci{
175262306a36Sopenharmony_ci	device_for_each_child(&shost->shost_gendev, fn,
175362306a36Sopenharmony_ci			      iscsi_iter_session_fn);
175462306a36Sopenharmony_ci}
175562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_for_each_session);
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_cistruct iscsi_scan_data {
175862306a36Sopenharmony_ci	unsigned int channel;
175962306a36Sopenharmony_ci	unsigned int id;
176062306a36Sopenharmony_ci	u64 lun;
176162306a36Sopenharmony_ci	enum scsi_scan_mode rescan;
176262306a36Sopenharmony_ci};
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic int iscsi_user_scan_session(struct device *dev, void *data)
176562306a36Sopenharmony_ci{
176662306a36Sopenharmony_ci	struct iscsi_scan_data *scan_data = data;
176762306a36Sopenharmony_ci	struct iscsi_cls_session *session;
176862306a36Sopenharmony_ci	struct Scsi_Host *shost;
176962306a36Sopenharmony_ci	struct iscsi_cls_host *ihost;
177062306a36Sopenharmony_ci	unsigned long flags;
177162306a36Sopenharmony_ci	unsigned int id;
177262306a36Sopenharmony_ci
177362306a36Sopenharmony_ci	if (!iscsi_is_session_dev(dev))
177462306a36Sopenharmony_ci		return 0;
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	session = iscsi_dev_to_session(dev);
177762306a36Sopenharmony_ci
177862306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Scanning session\n");
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	shost = iscsi_session_to_shost(session);
178162306a36Sopenharmony_ci	ihost = shost->shost_data;
178262306a36Sopenharmony_ci
178362306a36Sopenharmony_ci	mutex_lock(&ihost->mutex);
178462306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
178562306a36Sopenharmony_ci	if (session->state != ISCSI_SESSION_LOGGED_IN) {
178662306a36Sopenharmony_ci		spin_unlock_irqrestore(&session->lock, flags);
178762306a36Sopenharmony_ci		goto user_scan_exit;
178862306a36Sopenharmony_ci	}
178962306a36Sopenharmony_ci	id = session->target_id;
179062306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (id != ISCSI_MAX_TARGET) {
179362306a36Sopenharmony_ci		if ((scan_data->channel == SCAN_WILD_CARD ||
179462306a36Sopenharmony_ci		     scan_data->channel == 0) &&
179562306a36Sopenharmony_ci		    (scan_data->id == SCAN_WILD_CARD ||
179662306a36Sopenharmony_ci		     scan_data->id == id)) {
179762306a36Sopenharmony_ci			scsi_scan_target(&session->dev, 0, id,
179862306a36Sopenharmony_ci					 scan_data->lun, scan_data->rescan);
179962306a36Sopenharmony_ci			spin_lock_irqsave(&session->lock, flags);
180062306a36Sopenharmony_ci			session->target_state = ISCSI_SESSION_TARGET_SCANNED;
180162306a36Sopenharmony_ci			spin_unlock_irqrestore(&session->lock, flags);
180262306a36Sopenharmony_ci		}
180362306a36Sopenharmony_ci	}
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ciuser_scan_exit:
180662306a36Sopenharmony_ci	mutex_unlock(&ihost->mutex);
180762306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed session scan\n");
180862306a36Sopenharmony_ci	return 0;
180962306a36Sopenharmony_ci}
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_cistatic int iscsi_user_scan(struct Scsi_Host *shost, uint channel,
181262306a36Sopenharmony_ci			   uint id, u64 lun)
181362306a36Sopenharmony_ci{
181462306a36Sopenharmony_ci	struct iscsi_scan_data scan_data;
181562306a36Sopenharmony_ci
181662306a36Sopenharmony_ci	scan_data.channel = channel;
181762306a36Sopenharmony_ci	scan_data.id = id;
181862306a36Sopenharmony_ci	scan_data.lun = lun;
181962306a36Sopenharmony_ci	scan_data.rescan = SCSI_SCAN_MANUAL;
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	return device_for_each_child(&shost->shost_gendev, &scan_data,
182262306a36Sopenharmony_ci				     iscsi_user_scan_session);
182362306a36Sopenharmony_ci}
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_cistatic void iscsi_scan_session(struct work_struct *work)
182662306a36Sopenharmony_ci{
182762306a36Sopenharmony_ci	struct iscsi_cls_session *session =
182862306a36Sopenharmony_ci			container_of(work, struct iscsi_cls_session, scan_work);
182962306a36Sopenharmony_ci	struct iscsi_scan_data scan_data;
183062306a36Sopenharmony_ci
183162306a36Sopenharmony_ci	scan_data.channel = 0;
183262306a36Sopenharmony_ci	scan_data.id = SCAN_WILD_CARD;
183362306a36Sopenharmony_ci	scan_data.lun = SCAN_WILD_CARD;
183462306a36Sopenharmony_ci	scan_data.rescan = SCSI_SCAN_RESCAN;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	iscsi_user_scan_session(&session->dev, &scan_data);
183762306a36Sopenharmony_ci}
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci/**
184062306a36Sopenharmony_ci * iscsi_block_scsi_eh - block scsi eh until session state has transistioned
184162306a36Sopenharmony_ci * @cmd: scsi cmd passed to scsi eh handler
184262306a36Sopenharmony_ci *
184362306a36Sopenharmony_ci * If the session is down this function will wait for the recovery
184462306a36Sopenharmony_ci * timer to fire or for the session to be logged back in. If the
184562306a36Sopenharmony_ci * recovery timer fires then FAST_IO_FAIL is returned. The caller
184662306a36Sopenharmony_ci * should pass this error value to the scsi eh.
184762306a36Sopenharmony_ci */
184862306a36Sopenharmony_ciint iscsi_block_scsi_eh(struct scsi_cmnd *cmd)
184962306a36Sopenharmony_ci{
185062306a36Sopenharmony_ci	struct iscsi_cls_session *session =
185162306a36Sopenharmony_ci			starget_to_session(scsi_target(cmd->device));
185262306a36Sopenharmony_ci	unsigned long flags;
185362306a36Sopenharmony_ci	int ret = 0;
185462306a36Sopenharmony_ci
185562306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
185662306a36Sopenharmony_ci	while (session->state != ISCSI_SESSION_LOGGED_IN) {
185762306a36Sopenharmony_ci		if (session->state == ISCSI_SESSION_FREE) {
185862306a36Sopenharmony_ci			ret = FAST_IO_FAIL;
185962306a36Sopenharmony_ci			break;
186062306a36Sopenharmony_ci		}
186162306a36Sopenharmony_ci		spin_unlock_irqrestore(&session->lock, flags);
186262306a36Sopenharmony_ci		msleep(1000);
186362306a36Sopenharmony_ci		spin_lock_irqsave(&session->lock, flags);
186462306a36Sopenharmony_ci	}
186562306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
186662306a36Sopenharmony_ci	return ret;
186762306a36Sopenharmony_ci}
186862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_block_scsi_eh);
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_cistatic void session_recovery_timedout(struct work_struct *work)
187162306a36Sopenharmony_ci{
187262306a36Sopenharmony_ci	struct iscsi_cls_session *session =
187362306a36Sopenharmony_ci		container_of(work, struct iscsi_cls_session,
187462306a36Sopenharmony_ci			     recovery_work.work);
187562306a36Sopenharmony_ci	unsigned long flags;
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	iscsi_cls_session_printk(KERN_INFO, session,
187862306a36Sopenharmony_ci				 "session recovery timed out after %d secs\n",
187962306a36Sopenharmony_ci				 session->recovery_tmo);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
188262306a36Sopenharmony_ci	switch (session->state) {
188362306a36Sopenharmony_ci	case ISCSI_SESSION_FAILED:
188462306a36Sopenharmony_ci		session->state = ISCSI_SESSION_FREE;
188562306a36Sopenharmony_ci		break;
188662306a36Sopenharmony_ci	case ISCSI_SESSION_LOGGED_IN:
188762306a36Sopenharmony_ci	case ISCSI_SESSION_FREE:
188862306a36Sopenharmony_ci		/* we raced with the unblock's flush */
188962306a36Sopenharmony_ci		spin_unlock_irqrestore(&session->lock, flags);
189062306a36Sopenharmony_ci		return;
189162306a36Sopenharmony_ci	}
189262306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Unblocking SCSI target\n");
189562306a36Sopenharmony_ci	scsi_target_unblock(&session->dev, SDEV_TRANSPORT_OFFLINE);
189662306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking SCSI target\n");
189762306a36Sopenharmony_ci
189862306a36Sopenharmony_ci	if (session->transport->session_recovery_timedout)
189962306a36Sopenharmony_ci		session->transport->session_recovery_timedout(session);
190062306a36Sopenharmony_ci}
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_cistatic void __iscsi_unblock_session(struct work_struct *work)
190362306a36Sopenharmony_ci{
190462306a36Sopenharmony_ci	struct iscsi_cls_session *session =
190562306a36Sopenharmony_ci			container_of(work, struct iscsi_cls_session,
190662306a36Sopenharmony_ci				     unblock_work);
190762306a36Sopenharmony_ci	unsigned long flags;
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Unblocking session\n");
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	cancel_delayed_work_sync(&session->recovery_work);
191262306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
191362306a36Sopenharmony_ci	session->state = ISCSI_SESSION_LOGGED_IN;
191462306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
191562306a36Sopenharmony_ci	/* start IO */
191662306a36Sopenharmony_ci	scsi_target_unblock(&session->dev, SDEV_RUNNING);
191762306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed unblocking session\n");
191862306a36Sopenharmony_ci}
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci/**
192162306a36Sopenharmony_ci * iscsi_unblock_session - set a session as logged in and start IO.
192262306a36Sopenharmony_ci * @session: iscsi session
192362306a36Sopenharmony_ci *
192462306a36Sopenharmony_ci * Mark a session as ready to accept IO.
192562306a36Sopenharmony_ci */
192662306a36Sopenharmony_civoid iscsi_unblock_session(struct iscsi_cls_session *session)
192762306a36Sopenharmony_ci{
192862306a36Sopenharmony_ci	if (!cancel_work_sync(&session->block_work))
192962306a36Sopenharmony_ci		cancel_delayed_work_sync(&session->recovery_work);
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	queue_work(session->workq, &session->unblock_work);
193262306a36Sopenharmony_ci	/*
193362306a36Sopenharmony_ci	 * Blocking the session can be done from any context so we only
193462306a36Sopenharmony_ci	 * queue the block work. Make sure the unblock work has completed
193562306a36Sopenharmony_ci	 * because it flushes/cancels the other works and updates the state.
193662306a36Sopenharmony_ci	 */
193762306a36Sopenharmony_ci	flush_work(&session->unblock_work);
193862306a36Sopenharmony_ci}
193962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_unblock_session);
194062306a36Sopenharmony_ci
194162306a36Sopenharmony_cistatic void __iscsi_block_session(struct work_struct *work)
194262306a36Sopenharmony_ci{
194362306a36Sopenharmony_ci	struct iscsi_cls_session *session =
194462306a36Sopenharmony_ci			container_of(work, struct iscsi_cls_session,
194562306a36Sopenharmony_ci				     block_work);
194662306a36Sopenharmony_ci	struct Scsi_Host *shost = iscsi_session_to_shost(session);
194762306a36Sopenharmony_ci	unsigned long flags;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Blocking session\n");
195062306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
195162306a36Sopenharmony_ci	session->state = ISCSI_SESSION_FAILED;
195262306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
195362306a36Sopenharmony_ci	scsi_block_targets(shost, &session->dev);
195462306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed SCSI target blocking\n");
195562306a36Sopenharmony_ci	if (session->recovery_tmo >= 0)
195662306a36Sopenharmony_ci		queue_delayed_work(session->workq,
195762306a36Sopenharmony_ci				   &session->recovery_work,
195862306a36Sopenharmony_ci				   session->recovery_tmo * HZ);
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_civoid iscsi_block_session(struct iscsi_cls_session *session)
196262306a36Sopenharmony_ci{
196362306a36Sopenharmony_ci	queue_work(session->workq, &session->block_work);
196462306a36Sopenharmony_ci}
196562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_block_session);
196662306a36Sopenharmony_ci
196762306a36Sopenharmony_cistatic void __iscsi_unbind_session(struct work_struct *work)
196862306a36Sopenharmony_ci{
196962306a36Sopenharmony_ci	struct iscsi_cls_session *session =
197062306a36Sopenharmony_ci			container_of(work, struct iscsi_cls_session,
197162306a36Sopenharmony_ci				     unbind_work);
197262306a36Sopenharmony_ci	struct Scsi_Host *shost = iscsi_session_to_shost(session);
197362306a36Sopenharmony_ci	struct iscsi_cls_host *ihost = shost->shost_data;
197462306a36Sopenharmony_ci	unsigned long flags;
197562306a36Sopenharmony_ci	unsigned int target_id;
197662306a36Sopenharmony_ci	bool remove_target = true;
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_ci	/* Prevent new scans and make sure scanning is not in progress */
198162306a36Sopenharmony_ci	mutex_lock(&ihost->mutex);
198262306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
198362306a36Sopenharmony_ci	if (session->target_state == ISCSI_SESSION_TARGET_ALLOCATED) {
198462306a36Sopenharmony_ci		remove_target = false;
198562306a36Sopenharmony_ci	} else if (session->target_state != ISCSI_SESSION_TARGET_SCANNED) {
198662306a36Sopenharmony_ci		spin_unlock_irqrestore(&session->lock, flags);
198762306a36Sopenharmony_ci		mutex_unlock(&ihost->mutex);
198862306a36Sopenharmony_ci		ISCSI_DBG_TRANS_SESSION(session,
198962306a36Sopenharmony_ci			"Skipping target unbinding: Session is unbound/unbinding.\n");
199062306a36Sopenharmony_ci		return;
199162306a36Sopenharmony_ci	}
199262306a36Sopenharmony_ci
199362306a36Sopenharmony_ci	session->target_state = ISCSI_SESSION_TARGET_UNBINDING;
199462306a36Sopenharmony_ci	target_id = session->target_id;
199562306a36Sopenharmony_ci	session->target_id = ISCSI_MAX_TARGET;
199662306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
199762306a36Sopenharmony_ci	mutex_unlock(&ihost->mutex);
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci	if (remove_target)
200062306a36Sopenharmony_ci		scsi_remove_target(&session->dev);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (session->ida_used)
200362306a36Sopenharmony_ci		ida_free(&iscsi_sess_ida, target_id);
200462306a36Sopenharmony_ci
200562306a36Sopenharmony_ci	iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
200662306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
200762306a36Sopenharmony_ci
200862306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
200962306a36Sopenharmony_ci	session->target_state = ISCSI_SESSION_TARGET_UNBOUND;
201062306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
201162306a36Sopenharmony_ci}
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_cistatic void __iscsi_destroy_session(struct work_struct *work)
201462306a36Sopenharmony_ci{
201562306a36Sopenharmony_ci	struct iscsi_cls_session *session =
201662306a36Sopenharmony_ci		container_of(work, struct iscsi_cls_session, destroy_work);
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci	session->transport->destroy_session(session);
201962306a36Sopenharmony_ci}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_cistruct iscsi_cls_session *
202262306a36Sopenharmony_ciiscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
202362306a36Sopenharmony_ci		    int dd_size)
202462306a36Sopenharmony_ci{
202562306a36Sopenharmony_ci	struct iscsi_cls_session *session;
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci	session = kzalloc(sizeof(*session) + dd_size,
202862306a36Sopenharmony_ci			  GFP_KERNEL);
202962306a36Sopenharmony_ci	if (!session)
203062306a36Sopenharmony_ci		return NULL;
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	session->transport = transport;
203362306a36Sopenharmony_ci	session->creator = -1;
203462306a36Sopenharmony_ci	session->recovery_tmo = 120;
203562306a36Sopenharmony_ci	session->recovery_tmo_sysfs_override = false;
203662306a36Sopenharmony_ci	session->state = ISCSI_SESSION_FREE;
203762306a36Sopenharmony_ci	INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);
203862306a36Sopenharmony_ci	INIT_LIST_HEAD(&session->sess_list);
203962306a36Sopenharmony_ci	INIT_WORK(&session->unblock_work, __iscsi_unblock_session);
204062306a36Sopenharmony_ci	INIT_WORK(&session->block_work, __iscsi_block_session);
204162306a36Sopenharmony_ci	INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
204262306a36Sopenharmony_ci	INIT_WORK(&session->scan_work, iscsi_scan_session);
204362306a36Sopenharmony_ci	INIT_WORK(&session->destroy_work, __iscsi_destroy_session);
204462306a36Sopenharmony_ci	spin_lock_init(&session->lock);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci	/* this is released in the dev's release function */
204762306a36Sopenharmony_ci	scsi_host_get(shost);
204862306a36Sopenharmony_ci	session->dev.parent = &shost->shost_gendev;
204962306a36Sopenharmony_ci	session->dev.release = iscsi_session_release;
205062306a36Sopenharmony_ci	device_initialize(&session->dev);
205162306a36Sopenharmony_ci	if (dd_size)
205262306a36Sopenharmony_ci		session->dd_data = &session[1];
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed session allocation\n");
205562306a36Sopenharmony_ci	return session;
205662306a36Sopenharmony_ci}
205762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_alloc_session);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ciint iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
206062306a36Sopenharmony_ci{
206162306a36Sopenharmony_ci	struct Scsi_Host *shost = iscsi_session_to_shost(session);
206262306a36Sopenharmony_ci	unsigned long flags;
206362306a36Sopenharmony_ci	int id = 0;
206462306a36Sopenharmony_ci	int err;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	session->sid = atomic_add_return(1, &iscsi_session_nr);
206762306a36Sopenharmony_ci
206862306a36Sopenharmony_ci	session->workq = alloc_workqueue("iscsi_ctrl_%d:%d",
206962306a36Sopenharmony_ci			WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, 0,
207062306a36Sopenharmony_ci			shost->host_no, session->sid);
207162306a36Sopenharmony_ci	if (!session->workq)
207262306a36Sopenharmony_ci		return -ENOMEM;
207362306a36Sopenharmony_ci
207462306a36Sopenharmony_ci	if (target_id == ISCSI_MAX_TARGET) {
207562306a36Sopenharmony_ci		id = ida_alloc(&iscsi_sess_ida, GFP_KERNEL);
207662306a36Sopenharmony_ci
207762306a36Sopenharmony_ci		if (id < 0) {
207862306a36Sopenharmony_ci			iscsi_cls_session_printk(KERN_ERR, session,
207962306a36Sopenharmony_ci					"Failure in Target ID Allocation\n");
208062306a36Sopenharmony_ci			err = id;
208162306a36Sopenharmony_ci			goto destroy_wq;
208262306a36Sopenharmony_ci		}
208362306a36Sopenharmony_ci		session->target_id = (unsigned int)id;
208462306a36Sopenharmony_ci		session->ida_used = true;
208562306a36Sopenharmony_ci	} else
208662306a36Sopenharmony_ci		session->target_id = target_id;
208762306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
208862306a36Sopenharmony_ci	session->target_state = ISCSI_SESSION_TARGET_ALLOCATED;
208962306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_ci	dev_set_name(&session->dev, "session%u", session->sid);
209262306a36Sopenharmony_ci	err = device_add(&session->dev);
209362306a36Sopenharmony_ci	if (err) {
209462306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
209562306a36Sopenharmony_ci					 "could not register session's dev\n");
209662306a36Sopenharmony_ci		goto release_ida;
209762306a36Sopenharmony_ci	}
209862306a36Sopenharmony_ci	err = transport_register_device(&session->dev);
209962306a36Sopenharmony_ci	if (err) {
210062306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
210162306a36Sopenharmony_ci					 "could not register transport's dev\n");
210262306a36Sopenharmony_ci		goto release_dev;
210362306a36Sopenharmony_ci	}
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	spin_lock_irqsave(&sesslock, flags);
210662306a36Sopenharmony_ci	list_add(&session->sess_list, &sesslist);
210762306a36Sopenharmony_ci	spin_unlock_irqrestore(&sesslock, flags);
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_ci	iscsi_session_event(session, ISCSI_KEVENT_CREATE_SESSION);
211062306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed session adding\n");
211162306a36Sopenharmony_ci	return 0;
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_cirelease_dev:
211462306a36Sopenharmony_ci	device_del(&session->dev);
211562306a36Sopenharmony_cirelease_ida:
211662306a36Sopenharmony_ci	if (session->ida_used)
211762306a36Sopenharmony_ci		ida_free(&iscsi_sess_ida, session->target_id);
211862306a36Sopenharmony_cidestroy_wq:
211962306a36Sopenharmony_ci	destroy_workqueue(session->workq);
212062306a36Sopenharmony_ci	return err;
212162306a36Sopenharmony_ci}
212262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_add_session);
212362306a36Sopenharmony_ci
212462306a36Sopenharmony_ci/**
212562306a36Sopenharmony_ci * iscsi_create_session - create iscsi class session
212662306a36Sopenharmony_ci * @shost: scsi host
212762306a36Sopenharmony_ci * @transport: iscsi transport
212862306a36Sopenharmony_ci * @dd_size: private driver data size
212962306a36Sopenharmony_ci * @target_id: which target
213062306a36Sopenharmony_ci *
213162306a36Sopenharmony_ci * This can be called from a LLD or iscsi_transport.
213262306a36Sopenharmony_ci */
213362306a36Sopenharmony_cistruct iscsi_cls_session *
213462306a36Sopenharmony_ciiscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
213562306a36Sopenharmony_ci		     int dd_size, unsigned int target_id)
213662306a36Sopenharmony_ci{
213762306a36Sopenharmony_ci	struct iscsi_cls_session *session;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	session = iscsi_alloc_session(shost, transport, dd_size);
214062306a36Sopenharmony_ci	if (!session)
214162306a36Sopenharmony_ci		return NULL;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	if (iscsi_add_session(session, target_id)) {
214462306a36Sopenharmony_ci		iscsi_free_session(session);
214562306a36Sopenharmony_ci		return NULL;
214662306a36Sopenharmony_ci	}
214762306a36Sopenharmony_ci	return session;
214862306a36Sopenharmony_ci}
214962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_create_session);
215062306a36Sopenharmony_ci
215162306a36Sopenharmony_cistatic void iscsi_conn_release(struct device *dev)
215262306a36Sopenharmony_ci{
215362306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);
215462306a36Sopenharmony_ci	struct device *parent = conn->dev.parent;
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "Releasing conn\n");
215762306a36Sopenharmony_ci	kfree(conn);
215862306a36Sopenharmony_ci	put_device(parent);
215962306a36Sopenharmony_ci}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_cistatic int iscsi_is_conn_dev(const struct device *dev)
216262306a36Sopenharmony_ci{
216362306a36Sopenharmony_ci	return dev->release == iscsi_conn_release;
216462306a36Sopenharmony_ci}
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_cistatic int iscsi_iter_destroy_conn_fn(struct device *dev, void *data)
216762306a36Sopenharmony_ci{
216862306a36Sopenharmony_ci	if (!iscsi_is_conn_dev(dev))
216962306a36Sopenharmony_ci		return 0;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci	iscsi_remove_conn(iscsi_dev_to_conn(dev));
217262306a36Sopenharmony_ci	return 0;
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_civoid iscsi_remove_session(struct iscsi_cls_session *session)
217662306a36Sopenharmony_ci{
217762306a36Sopenharmony_ci	unsigned long flags;
217862306a36Sopenharmony_ci	int err;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Removing session\n");
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	spin_lock_irqsave(&sesslock, flags);
218362306a36Sopenharmony_ci	if (!list_empty(&session->sess_list))
218462306a36Sopenharmony_ci		list_del(&session->sess_list);
218562306a36Sopenharmony_ci	spin_unlock_irqrestore(&sesslock, flags);
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	if (!cancel_work_sync(&session->block_work))
218862306a36Sopenharmony_ci		cancel_delayed_work_sync(&session->recovery_work);
218962306a36Sopenharmony_ci	cancel_work_sync(&session->unblock_work);
219062306a36Sopenharmony_ci	/*
219162306a36Sopenharmony_ci	 * If we are blocked let commands flow again. The lld or iscsi
219262306a36Sopenharmony_ci	 * layer should set up the queuecommand to fail commands.
219362306a36Sopenharmony_ci	 * We assume that LLD will not be calling block/unblock while
219462306a36Sopenharmony_ci	 * removing the session.
219562306a36Sopenharmony_ci	 */
219662306a36Sopenharmony_ci	spin_lock_irqsave(&session->lock, flags);
219762306a36Sopenharmony_ci	session->state = ISCSI_SESSION_FREE;
219862306a36Sopenharmony_ci	spin_unlock_irqrestore(&session->lock, flags);
219962306a36Sopenharmony_ci
220062306a36Sopenharmony_ci	scsi_target_unblock(&session->dev, SDEV_TRANSPORT_OFFLINE);
220162306a36Sopenharmony_ci	/*
220262306a36Sopenharmony_ci	 * qla4xxx can perform it's own scans when it runs in kernel only
220362306a36Sopenharmony_ci	 * mode. Make sure to flush those scans.
220462306a36Sopenharmony_ci	 */
220562306a36Sopenharmony_ci	flush_work(&session->scan_work);
220662306a36Sopenharmony_ci	/* flush running unbind operations */
220762306a36Sopenharmony_ci	flush_work(&session->unbind_work);
220862306a36Sopenharmony_ci	__iscsi_unbind_session(&session->unbind_work);
220962306a36Sopenharmony_ci
221062306a36Sopenharmony_ci	/* hw iscsi may not have removed all connections from session */
221162306a36Sopenharmony_ci	err = device_for_each_child(&session->dev, NULL,
221262306a36Sopenharmony_ci				    iscsi_iter_destroy_conn_fn);
221362306a36Sopenharmony_ci	if (err)
221462306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
221562306a36Sopenharmony_ci					 "Could not delete all connections "
221662306a36Sopenharmony_ci					 "for session. Error %d.\n", err);
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ci	transport_unregister_device(&session->dev);
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci	destroy_workqueue(session->workq);
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completing session removal\n");
222362306a36Sopenharmony_ci	device_del(&session->dev);
222462306a36Sopenharmony_ci}
222562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_remove_session);
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_cistatic void iscsi_stop_conn(struct iscsi_cls_conn *conn, int flag)
222862306a36Sopenharmony_ci{
222962306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "Stopping conn.\n");
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	switch (flag) {
223262306a36Sopenharmony_ci	case STOP_CONN_RECOVER:
223362306a36Sopenharmony_ci		WRITE_ONCE(conn->state, ISCSI_CONN_FAILED);
223462306a36Sopenharmony_ci		break;
223562306a36Sopenharmony_ci	case STOP_CONN_TERM:
223662306a36Sopenharmony_ci		WRITE_ONCE(conn->state, ISCSI_CONN_DOWN);
223762306a36Sopenharmony_ci		break;
223862306a36Sopenharmony_ci	default:
223962306a36Sopenharmony_ci		iscsi_cls_conn_printk(KERN_ERR, conn, "invalid stop flag %d\n",
224062306a36Sopenharmony_ci				      flag);
224162306a36Sopenharmony_ci		return;
224262306a36Sopenharmony_ci	}
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	conn->transport->stop_conn(conn, flag);
224562306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "Stopping conn done.\n");
224662306a36Sopenharmony_ci}
224762306a36Sopenharmony_ci
224862306a36Sopenharmony_cistatic void iscsi_ep_disconnect(struct iscsi_cls_conn *conn, bool is_active)
224962306a36Sopenharmony_ci{
225062306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
225162306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "disconnect ep.\n");
225462306a36Sopenharmony_ci	WRITE_ONCE(conn->state, ISCSI_CONN_FAILED);
225562306a36Sopenharmony_ci
225662306a36Sopenharmony_ci	if (!conn->ep || !session->transport->ep_disconnect)
225762306a36Sopenharmony_ci		return;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	ep = conn->ep;
226062306a36Sopenharmony_ci	conn->ep = NULL;
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	session->transport->unbind_conn(conn, is_active);
226362306a36Sopenharmony_ci	session->transport->ep_disconnect(ep);
226462306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "disconnect ep done.\n");
226562306a36Sopenharmony_ci}
226662306a36Sopenharmony_ci
226762306a36Sopenharmony_cistatic void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn,
226862306a36Sopenharmony_ci					 struct iscsi_endpoint *ep,
226962306a36Sopenharmony_ci					 bool is_active)
227062306a36Sopenharmony_ci{
227162306a36Sopenharmony_ci	/* Check if this was a conn error and the kernel took ownership */
227262306a36Sopenharmony_ci	spin_lock_irq(&conn->lock);
227362306a36Sopenharmony_ci	if (!test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
227462306a36Sopenharmony_ci		spin_unlock_irq(&conn->lock);
227562306a36Sopenharmony_ci		iscsi_ep_disconnect(conn, is_active);
227662306a36Sopenharmony_ci	} else {
227762306a36Sopenharmony_ci		spin_unlock_irq(&conn->lock);
227862306a36Sopenharmony_ci		ISCSI_DBG_TRANS_CONN(conn, "flush kernel conn cleanup.\n");
227962306a36Sopenharmony_ci		mutex_unlock(&conn->ep_mutex);
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci		flush_work(&conn->cleanup_work);
228262306a36Sopenharmony_ci		/*
228362306a36Sopenharmony_ci		 * Userspace is now done with the EP so we can release the ref
228462306a36Sopenharmony_ci		 * iscsi_cleanup_conn_work_fn took.
228562306a36Sopenharmony_ci		 */
228662306a36Sopenharmony_ci		iscsi_put_endpoint(ep);
228762306a36Sopenharmony_ci		mutex_lock(&conn->ep_mutex);
228862306a36Sopenharmony_ci	}
228962306a36Sopenharmony_ci}
229062306a36Sopenharmony_ci
229162306a36Sopenharmony_cistatic int iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag)
229262306a36Sopenharmony_ci{
229362306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n");
229462306a36Sopenharmony_ci	/*
229562306a36Sopenharmony_ci	 * For offload, iscsid may not know about the ep like when iscsid is
229662306a36Sopenharmony_ci	 * restarted or for kernel based session shutdown iscsid is not even
229762306a36Sopenharmony_ci	 * up. For these cases, we do the disconnect now.
229862306a36Sopenharmony_ci	 */
229962306a36Sopenharmony_ci	mutex_lock(&conn->ep_mutex);
230062306a36Sopenharmony_ci	if (conn->ep)
230162306a36Sopenharmony_ci		iscsi_if_disconnect_bound_ep(conn, conn->ep, true);
230262306a36Sopenharmony_ci	mutex_unlock(&conn->ep_mutex);
230362306a36Sopenharmony_ci
230462306a36Sopenharmony_ci	/*
230562306a36Sopenharmony_ci	 * If this is a termination we have to call stop_conn with that flag
230662306a36Sopenharmony_ci	 * so the correct states get set. If we haven't run the work yet try to
230762306a36Sopenharmony_ci	 * avoid the extra run.
230862306a36Sopenharmony_ci	 */
230962306a36Sopenharmony_ci	if (flag == STOP_CONN_TERM) {
231062306a36Sopenharmony_ci		cancel_work_sync(&conn->cleanup_work);
231162306a36Sopenharmony_ci		iscsi_stop_conn(conn, flag);
231262306a36Sopenharmony_ci	} else {
231362306a36Sopenharmony_ci		/*
231462306a36Sopenharmony_ci		 * Figure out if it was the kernel or userspace initiating this.
231562306a36Sopenharmony_ci		 */
231662306a36Sopenharmony_ci		spin_lock_irq(&conn->lock);
231762306a36Sopenharmony_ci		if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
231862306a36Sopenharmony_ci			spin_unlock_irq(&conn->lock);
231962306a36Sopenharmony_ci			iscsi_stop_conn(conn, flag);
232062306a36Sopenharmony_ci		} else {
232162306a36Sopenharmony_ci			spin_unlock_irq(&conn->lock);
232262306a36Sopenharmony_ci			ISCSI_DBG_TRANS_CONN(conn,
232362306a36Sopenharmony_ci					     "flush kernel conn cleanup.\n");
232462306a36Sopenharmony_ci			flush_work(&conn->cleanup_work);
232562306a36Sopenharmony_ci		}
232662306a36Sopenharmony_ci		/*
232762306a36Sopenharmony_ci		 * Only clear for recovery to avoid extra cleanup runs during
232862306a36Sopenharmony_ci		 * termination.
232962306a36Sopenharmony_ci		 */
233062306a36Sopenharmony_ci		spin_lock_irq(&conn->lock);
233162306a36Sopenharmony_ci		clear_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags);
233262306a36Sopenharmony_ci		spin_unlock_irq(&conn->lock);
233362306a36Sopenharmony_ci	}
233462306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop done.\n");
233562306a36Sopenharmony_ci	return 0;
233662306a36Sopenharmony_ci}
233762306a36Sopenharmony_ci
233862306a36Sopenharmony_cistatic void iscsi_cleanup_conn_work_fn(struct work_struct *work)
233962306a36Sopenharmony_ci{
234062306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = container_of(work, struct iscsi_cls_conn,
234162306a36Sopenharmony_ci						   cleanup_work);
234262306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_conn_to_session(conn);
234362306a36Sopenharmony_ci
234462306a36Sopenharmony_ci	mutex_lock(&conn->ep_mutex);
234562306a36Sopenharmony_ci	/*
234662306a36Sopenharmony_ci	 * Get a ref to the ep, so we don't release its ID until after
234762306a36Sopenharmony_ci	 * userspace is done referencing it in iscsi_if_disconnect_bound_ep.
234862306a36Sopenharmony_ci	 */
234962306a36Sopenharmony_ci	if (conn->ep)
235062306a36Sopenharmony_ci		get_device(&conn->ep->dev);
235162306a36Sopenharmony_ci	iscsi_ep_disconnect(conn, false);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	if (system_state != SYSTEM_RUNNING) {
235462306a36Sopenharmony_ci		/*
235562306a36Sopenharmony_ci		 * If the user has set up for the session to never timeout
235662306a36Sopenharmony_ci		 * then hang like they wanted. For all other cases fail right
235762306a36Sopenharmony_ci		 * away since userspace is not going to relogin.
235862306a36Sopenharmony_ci		 */
235962306a36Sopenharmony_ci		if (session->recovery_tmo > 0)
236062306a36Sopenharmony_ci			session->recovery_tmo = 0;
236162306a36Sopenharmony_ci	}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci	iscsi_stop_conn(conn, STOP_CONN_RECOVER);
236462306a36Sopenharmony_ci	mutex_unlock(&conn->ep_mutex);
236562306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n");
236662306a36Sopenharmony_ci}
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_cistatic int iscsi_iter_force_destroy_conn_fn(struct device *dev, void *data)
236962306a36Sopenharmony_ci{
237062306a36Sopenharmony_ci	struct iscsi_transport *transport;
237162306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci	if (!iscsi_is_conn_dev(dev))
237462306a36Sopenharmony_ci		return 0;
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	conn = iscsi_dev_to_conn(dev);
237762306a36Sopenharmony_ci	transport = conn->transport;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	if (READ_ONCE(conn->state) != ISCSI_CONN_DOWN)
238062306a36Sopenharmony_ci		iscsi_if_stop_conn(conn, STOP_CONN_TERM);
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_ci	transport->destroy_conn(conn);
238362306a36Sopenharmony_ci	return 0;
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci/**
238762306a36Sopenharmony_ci * iscsi_force_destroy_session - destroy a session from the kernel
238862306a36Sopenharmony_ci * @session: session to destroy
238962306a36Sopenharmony_ci *
239062306a36Sopenharmony_ci * Force the destruction of a session from the kernel. This should only be
239162306a36Sopenharmony_ci * used when userspace is no longer running during system shutdown.
239262306a36Sopenharmony_ci */
239362306a36Sopenharmony_civoid iscsi_force_destroy_session(struct iscsi_cls_session *session)
239462306a36Sopenharmony_ci{
239562306a36Sopenharmony_ci	struct iscsi_transport *transport = session->transport;
239662306a36Sopenharmony_ci	unsigned long flags;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	WARN_ON_ONCE(system_state == SYSTEM_RUNNING);
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	spin_lock_irqsave(&sesslock, flags);
240162306a36Sopenharmony_ci	if (list_empty(&session->sess_list)) {
240262306a36Sopenharmony_ci		spin_unlock_irqrestore(&sesslock, flags);
240362306a36Sopenharmony_ci		/*
240462306a36Sopenharmony_ci		 * Conn/ep is already freed. Session is being torn down via
240562306a36Sopenharmony_ci		 * async path. For shutdown we don't care about it so return.
240662306a36Sopenharmony_ci		 */
240762306a36Sopenharmony_ci		return;
240862306a36Sopenharmony_ci	}
240962306a36Sopenharmony_ci	spin_unlock_irqrestore(&sesslock, flags);
241062306a36Sopenharmony_ci
241162306a36Sopenharmony_ci	device_for_each_child(&session->dev, NULL,
241262306a36Sopenharmony_ci			      iscsi_iter_force_destroy_conn_fn);
241362306a36Sopenharmony_ci	transport->destroy_session(session);
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_force_destroy_session);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_civoid iscsi_free_session(struct iscsi_cls_session *session)
241862306a36Sopenharmony_ci{
241962306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n");
242062306a36Sopenharmony_ci	iscsi_session_event(session, ISCSI_KEVENT_DESTROY_SESSION);
242162306a36Sopenharmony_ci	put_device(&session->dev);
242262306a36Sopenharmony_ci}
242362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_free_session);
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_ci/**
242662306a36Sopenharmony_ci * iscsi_alloc_conn - alloc iscsi class connection
242762306a36Sopenharmony_ci * @session: iscsi cls session
242862306a36Sopenharmony_ci * @dd_size: private driver data size
242962306a36Sopenharmony_ci * @cid: connection id
243062306a36Sopenharmony_ci */
243162306a36Sopenharmony_cistruct iscsi_cls_conn *
243262306a36Sopenharmony_ciiscsi_alloc_conn(struct iscsi_cls_session *session, int dd_size, uint32_t cid)
243362306a36Sopenharmony_ci{
243462306a36Sopenharmony_ci	struct iscsi_transport *transport = session->transport;
243562306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_ci	conn = kzalloc(sizeof(*conn) + dd_size, GFP_KERNEL);
243862306a36Sopenharmony_ci	if (!conn)
243962306a36Sopenharmony_ci		return NULL;
244062306a36Sopenharmony_ci	if (dd_size)
244162306a36Sopenharmony_ci		conn->dd_data = &conn[1];
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	mutex_init(&conn->ep_mutex);
244462306a36Sopenharmony_ci	spin_lock_init(&conn->lock);
244562306a36Sopenharmony_ci	INIT_LIST_HEAD(&conn->conn_list);
244662306a36Sopenharmony_ci	INIT_WORK(&conn->cleanup_work, iscsi_cleanup_conn_work_fn);
244762306a36Sopenharmony_ci	conn->transport = transport;
244862306a36Sopenharmony_ci	conn->cid = cid;
244962306a36Sopenharmony_ci	WRITE_ONCE(conn->state, ISCSI_CONN_DOWN);
245062306a36Sopenharmony_ci
245162306a36Sopenharmony_ci	/* this is released in the dev's release function */
245262306a36Sopenharmony_ci	if (!get_device(&session->dev))
245362306a36Sopenharmony_ci		goto free_conn;
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	dev_set_name(&conn->dev, "connection%d:%u", session->sid, cid);
245662306a36Sopenharmony_ci	device_initialize(&conn->dev);
245762306a36Sopenharmony_ci	conn->dev.parent = &session->dev;
245862306a36Sopenharmony_ci	conn->dev.release = iscsi_conn_release;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	return conn;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_cifree_conn:
246362306a36Sopenharmony_ci	kfree(conn);
246462306a36Sopenharmony_ci	return NULL;
246562306a36Sopenharmony_ci}
246662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_alloc_conn);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci/**
246962306a36Sopenharmony_ci * iscsi_add_conn - add iscsi class connection
247062306a36Sopenharmony_ci * @conn: iscsi cls connection
247162306a36Sopenharmony_ci *
247262306a36Sopenharmony_ci * This will expose iscsi_cls_conn to sysfs so make sure the related
247362306a36Sopenharmony_ci * resources for sysfs attributes are initialized before calling this.
247462306a36Sopenharmony_ci */
247562306a36Sopenharmony_ciint iscsi_add_conn(struct iscsi_cls_conn *conn)
247662306a36Sopenharmony_ci{
247762306a36Sopenharmony_ci	int err;
247862306a36Sopenharmony_ci	unsigned long flags;
247962306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_dev_to_session(conn->dev.parent);
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	err = device_add(&conn->dev);
248262306a36Sopenharmony_ci	if (err) {
248362306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
248462306a36Sopenharmony_ci					 "could not register connection's dev\n");
248562306a36Sopenharmony_ci		return err;
248662306a36Sopenharmony_ci	}
248762306a36Sopenharmony_ci	err = transport_register_device(&conn->dev);
248862306a36Sopenharmony_ci	if (err) {
248962306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
249062306a36Sopenharmony_ci					 "could not register transport's dev\n");
249162306a36Sopenharmony_ci		device_del(&conn->dev);
249262306a36Sopenharmony_ci		return err;
249362306a36Sopenharmony_ci	}
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	spin_lock_irqsave(&connlock, flags);
249662306a36Sopenharmony_ci	list_add(&conn->conn_list, &connlist);
249762306a36Sopenharmony_ci	spin_unlock_irqrestore(&connlock, flags);
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	return 0;
250062306a36Sopenharmony_ci}
250162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_add_conn);
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci/**
250462306a36Sopenharmony_ci * iscsi_remove_conn - remove iscsi class connection from sysfs
250562306a36Sopenharmony_ci * @conn: iscsi cls connection
250662306a36Sopenharmony_ci *
250762306a36Sopenharmony_ci * Remove iscsi_cls_conn from sysfs, and wait for previous
250862306a36Sopenharmony_ci * read/write of iscsi_cls_conn's attributes in sysfs to finish.
250962306a36Sopenharmony_ci */
251062306a36Sopenharmony_civoid iscsi_remove_conn(struct iscsi_cls_conn *conn)
251162306a36Sopenharmony_ci{
251262306a36Sopenharmony_ci	unsigned long flags;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	spin_lock_irqsave(&connlock, flags);
251562306a36Sopenharmony_ci	list_del(&conn->conn_list);
251662306a36Sopenharmony_ci	spin_unlock_irqrestore(&connlock, flags);
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	transport_unregister_device(&conn->dev);
251962306a36Sopenharmony_ci	device_del(&conn->dev);
252062306a36Sopenharmony_ci}
252162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_remove_conn);
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_civoid iscsi_put_conn(struct iscsi_cls_conn *conn)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	put_device(&conn->dev);
252662306a36Sopenharmony_ci}
252762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_put_conn);
252862306a36Sopenharmony_ci
252962306a36Sopenharmony_civoid iscsi_get_conn(struct iscsi_cls_conn *conn)
253062306a36Sopenharmony_ci{
253162306a36Sopenharmony_ci	get_device(&conn->dev);
253262306a36Sopenharmony_ci}
253362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_conn);
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci/*
253662306a36Sopenharmony_ci * iscsi interface functions
253762306a36Sopenharmony_ci */
253862306a36Sopenharmony_cistatic struct iscsi_internal *
253962306a36Sopenharmony_ciiscsi_if_transport_lookup(struct iscsi_transport *tt)
254062306a36Sopenharmony_ci{
254162306a36Sopenharmony_ci	struct iscsi_internal *priv;
254262306a36Sopenharmony_ci	unsigned long flags;
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	spin_lock_irqsave(&iscsi_transport_lock, flags);
254562306a36Sopenharmony_ci	list_for_each_entry(priv, &iscsi_transports, list) {
254662306a36Sopenharmony_ci		if (tt == priv->iscsi_transport) {
254762306a36Sopenharmony_ci			spin_unlock_irqrestore(&iscsi_transport_lock, flags);
254862306a36Sopenharmony_ci			return priv;
254962306a36Sopenharmony_ci		}
255062306a36Sopenharmony_ci	}
255162306a36Sopenharmony_ci	spin_unlock_irqrestore(&iscsi_transport_lock, flags);
255262306a36Sopenharmony_ci	return NULL;
255362306a36Sopenharmony_ci}
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_cistatic int
255662306a36Sopenharmony_ciiscsi_multicast_skb(struct sk_buff *skb, uint32_t group, gfp_t gfp)
255762306a36Sopenharmony_ci{
255862306a36Sopenharmony_ci	return nlmsg_multicast(nls, skb, 0, group, gfp);
255962306a36Sopenharmony_ci}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_cistatic int
256262306a36Sopenharmony_ciiscsi_unicast_skb(struct sk_buff *skb, u32 portid)
256362306a36Sopenharmony_ci{
256462306a36Sopenharmony_ci	return nlmsg_unicast(nls, skb, portid);
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_ciint iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr,
256862306a36Sopenharmony_ci		   char *data, uint32_t data_size)
256962306a36Sopenharmony_ci{
257062306a36Sopenharmony_ci	struct nlmsghdr	*nlh;
257162306a36Sopenharmony_ci	struct sk_buff *skb;
257262306a36Sopenharmony_ci	struct iscsi_uevent *ev;
257362306a36Sopenharmony_ci	char *pdu;
257462306a36Sopenharmony_ci	struct iscsi_internal *priv;
257562306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev) + sizeof(struct iscsi_hdr) +
257662306a36Sopenharmony_ci				   data_size);
257762306a36Sopenharmony_ci
257862306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(conn->transport);
257962306a36Sopenharmony_ci	if (!priv)
258062306a36Sopenharmony_ci		return -EINVAL;
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_ATOMIC);
258362306a36Sopenharmony_ci	if (!skb) {
258462306a36Sopenharmony_ci		iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED);
258562306a36Sopenharmony_ci		iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver "
258662306a36Sopenharmony_ci				      "control PDU: OOM\n");
258762306a36Sopenharmony_ci		return -ENOMEM;
258862306a36Sopenharmony_ci	}
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
259162306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
259262306a36Sopenharmony_ci	memset(ev, 0, sizeof(*ev));
259362306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(conn->transport);
259462306a36Sopenharmony_ci	ev->type = ISCSI_KEVENT_RECV_PDU;
259562306a36Sopenharmony_ci	ev->r.recv_req.cid = conn->cid;
259662306a36Sopenharmony_ci	ev->r.recv_req.sid = iscsi_conn_get_sid(conn);
259762306a36Sopenharmony_ci	pdu = (char*)ev + sizeof(*ev);
259862306a36Sopenharmony_ci	memcpy(pdu, hdr, sizeof(struct iscsi_hdr));
259962306a36Sopenharmony_ci	memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size);
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	return iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_recv_pdu);
260462306a36Sopenharmony_ci
260562306a36Sopenharmony_ciint iscsi_offload_mesg(struct Scsi_Host *shost,
260662306a36Sopenharmony_ci		       struct iscsi_transport *transport, uint32_t type,
260762306a36Sopenharmony_ci		       char *data, uint16_t data_size)
260862306a36Sopenharmony_ci{
260962306a36Sopenharmony_ci	struct nlmsghdr	*nlh;
261062306a36Sopenharmony_ci	struct sk_buff *skb;
261162306a36Sopenharmony_ci	struct iscsi_uevent *ev;
261262306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev) + data_size);
261362306a36Sopenharmony_ci
261462306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_ATOMIC);
261562306a36Sopenharmony_ci	if (!skb) {
261662306a36Sopenharmony_ci		printk(KERN_ERR "can not deliver iscsi offload message:OOM\n");
261762306a36Sopenharmony_ci		return -ENOMEM;
261862306a36Sopenharmony_ci	}
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
262162306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
262262306a36Sopenharmony_ci	memset(ev, 0, sizeof(*ev));
262362306a36Sopenharmony_ci	ev->type = type;
262462306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(transport);
262562306a36Sopenharmony_ci	switch (type) {
262662306a36Sopenharmony_ci	case ISCSI_KEVENT_PATH_REQ:
262762306a36Sopenharmony_ci		ev->r.req_path.host_no = shost->host_no;
262862306a36Sopenharmony_ci		break;
262962306a36Sopenharmony_ci	case ISCSI_KEVENT_IF_DOWN:
263062306a36Sopenharmony_ci		ev->r.notify_if_down.host_no = shost->host_no;
263162306a36Sopenharmony_ci		break;
263262306a36Sopenharmony_ci	}
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	memcpy((char *)ev + sizeof(*ev), data, data_size);
263562306a36Sopenharmony_ci
263662306a36Sopenharmony_ci	return iscsi_multicast_skb(skb, ISCSI_NL_GRP_UIP, GFP_ATOMIC);
263762306a36Sopenharmony_ci}
263862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_offload_mesg);
263962306a36Sopenharmony_ci
264062306a36Sopenharmony_civoid iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error)
264162306a36Sopenharmony_ci{
264262306a36Sopenharmony_ci	struct nlmsghdr	*nlh;
264362306a36Sopenharmony_ci	struct sk_buff	*skb;
264462306a36Sopenharmony_ci	struct iscsi_uevent *ev;
264562306a36Sopenharmony_ci	struct iscsi_internal *priv;
264662306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev));
264762306a36Sopenharmony_ci	unsigned long flags;
264862306a36Sopenharmony_ci	int state;
264962306a36Sopenharmony_ci
265062306a36Sopenharmony_ci	spin_lock_irqsave(&conn->lock, flags);
265162306a36Sopenharmony_ci	/*
265262306a36Sopenharmony_ci	 * Userspace will only do a stop call if we are at least bound. And, we
265362306a36Sopenharmony_ci	 * only need to do the in kernel cleanup if in the UP state so cmds can
265462306a36Sopenharmony_ci	 * be released to upper layers. If in other states just wait for
265562306a36Sopenharmony_ci	 * userspace to avoid races that can leave the cleanup_work queued.
265662306a36Sopenharmony_ci	 */
265762306a36Sopenharmony_ci	state = READ_ONCE(conn->state);
265862306a36Sopenharmony_ci	switch (state) {
265962306a36Sopenharmony_ci	case ISCSI_CONN_BOUND:
266062306a36Sopenharmony_ci	case ISCSI_CONN_UP:
266162306a36Sopenharmony_ci		if (!test_and_set_bit(ISCSI_CLS_CONN_BIT_CLEANUP,
266262306a36Sopenharmony_ci				      &conn->flags)) {
266362306a36Sopenharmony_ci			queue_work(iscsi_conn_cleanup_workq,
266462306a36Sopenharmony_ci				   &conn->cleanup_work);
266562306a36Sopenharmony_ci		}
266662306a36Sopenharmony_ci		break;
266762306a36Sopenharmony_ci	default:
266862306a36Sopenharmony_ci		ISCSI_DBG_TRANS_CONN(conn, "Got conn error in state %d\n",
266962306a36Sopenharmony_ci				     state);
267062306a36Sopenharmony_ci		break;
267162306a36Sopenharmony_ci	}
267262306a36Sopenharmony_ci	spin_unlock_irqrestore(&conn->lock, flags);
267362306a36Sopenharmony_ci
267462306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(conn->transport);
267562306a36Sopenharmony_ci	if (!priv)
267662306a36Sopenharmony_ci		return;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_ATOMIC);
267962306a36Sopenharmony_ci	if (!skb) {
268062306a36Sopenharmony_ci		iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
268162306a36Sopenharmony_ci				      "conn error (%d)\n", error);
268262306a36Sopenharmony_ci		return;
268362306a36Sopenharmony_ci	}
268462306a36Sopenharmony_ci
268562306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
268662306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
268762306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(conn->transport);
268862306a36Sopenharmony_ci	ev->type = ISCSI_KEVENT_CONN_ERROR;
268962306a36Sopenharmony_ci	ev->r.connerror.error = error;
269062306a36Sopenharmony_ci	ev->r.connerror.cid = conn->cid;
269162306a36Sopenharmony_ci	ev->r.connerror.sid = iscsi_conn_get_sid(conn);
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n",
269662306a36Sopenharmony_ci			      error);
269762306a36Sopenharmony_ci}
269862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_error_event);
269962306a36Sopenharmony_ci
270062306a36Sopenharmony_civoid iscsi_conn_login_event(struct iscsi_cls_conn *conn,
270162306a36Sopenharmony_ci			    enum iscsi_conn_state state)
270262306a36Sopenharmony_ci{
270362306a36Sopenharmony_ci	struct nlmsghdr *nlh;
270462306a36Sopenharmony_ci	struct sk_buff  *skb;
270562306a36Sopenharmony_ci	struct iscsi_uevent *ev;
270662306a36Sopenharmony_ci	struct iscsi_internal *priv;
270762306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev));
270862306a36Sopenharmony_ci
270962306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(conn->transport);
271062306a36Sopenharmony_ci	if (!priv)
271162306a36Sopenharmony_ci		return;
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_ATOMIC);
271462306a36Sopenharmony_ci	if (!skb) {
271562306a36Sopenharmony_ci		iscsi_cls_conn_printk(KERN_ERR, conn, "gracefully ignored "
271662306a36Sopenharmony_ci				      "conn login (%d)\n", state);
271762306a36Sopenharmony_ci		return;
271862306a36Sopenharmony_ci	}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
272162306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
272262306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(conn->transport);
272362306a36Sopenharmony_ci	ev->type = ISCSI_KEVENT_CONN_LOGIN_STATE;
272462306a36Sopenharmony_ci	ev->r.conn_login.state = state;
272562306a36Sopenharmony_ci	ev->r.conn_login.cid = conn->cid;
272662306a36Sopenharmony_ci	ev->r.conn_login.sid = iscsi_conn_get_sid(conn);
272762306a36Sopenharmony_ci	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_ATOMIC);
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn login (%d)\n",
273062306a36Sopenharmony_ci			      state);
273162306a36Sopenharmony_ci}
273262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_login_event);
273362306a36Sopenharmony_ci
273462306a36Sopenharmony_civoid iscsi_post_host_event(uint32_t host_no, struct iscsi_transport *transport,
273562306a36Sopenharmony_ci			   enum iscsi_host_event_code code, uint32_t data_size,
273662306a36Sopenharmony_ci			   uint8_t *data)
273762306a36Sopenharmony_ci{
273862306a36Sopenharmony_ci	struct nlmsghdr *nlh;
273962306a36Sopenharmony_ci	struct sk_buff *skb;
274062306a36Sopenharmony_ci	struct iscsi_uevent *ev;
274162306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev) + data_size);
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_NOIO);
274462306a36Sopenharmony_ci	if (!skb) {
274562306a36Sopenharmony_ci		printk(KERN_ERR "gracefully ignored host event (%d):%d OOM\n",
274662306a36Sopenharmony_ci		       host_no, code);
274762306a36Sopenharmony_ci		return;
274862306a36Sopenharmony_ci	}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
275162306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
275262306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(transport);
275362306a36Sopenharmony_ci	ev->type = ISCSI_KEVENT_HOST_EVENT;
275462306a36Sopenharmony_ci	ev->r.host_event.host_no = host_no;
275562306a36Sopenharmony_ci	ev->r.host_event.code = code;
275662306a36Sopenharmony_ci	ev->r.host_event.data_size = data_size;
275762306a36Sopenharmony_ci
275862306a36Sopenharmony_ci	if (data_size)
275962306a36Sopenharmony_ci		memcpy((char *)ev + sizeof(*ev), data, data_size);
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO);
276262306a36Sopenharmony_ci}
276362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_post_host_event);
276462306a36Sopenharmony_ci
276562306a36Sopenharmony_civoid iscsi_ping_comp_event(uint32_t host_no, struct iscsi_transport *transport,
276662306a36Sopenharmony_ci			   uint32_t status, uint32_t pid, uint32_t data_size,
276762306a36Sopenharmony_ci			   uint8_t *data)
276862306a36Sopenharmony_ci{
276962306a36Sopenharmony_ci	struct nlmsghdr *nlh;
277062306a36Sopenharmony_ci	struct sk_buff *skb;
277162306a36Sopenharmony_ci	struct iscsi_uevent *ev;
277262306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev) + data_size);
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_NOIO);
277562306a36Sopenharmony_ci	if (!skb) {
277662306a36Sopenharmony_ci		printk(KERN_ERR "gracefully ignored ping comp: OOM\n");
277762306a36Sopenharmony_ci		return;
277862306a36Sopenharmony_ci	}
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
278162306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
278262306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(transport);
278362306a36Sopenharmony_ci	ev->type = ISCSI_KEVENT_PING_COMP;
278462306a36Sopenharmony_ci	ev->r.ping_comp.host_no = host_no;
278562306a36Sopenharmony_ci	ev->r.ping_comp.status = status;
278662306a36Sopenharmony_ci	ev->r.ping_comp.pid = pid;
278762306a36Sopenharmony_ci	ev->r.ping_comp.data_size = data_size;
278862306a36Sopenharmony_ci	memcpy((char *)ev + sizeof(*ev), data, data_size);
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_NOIO);
279162306a36Sopenharmony_ci}
279262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_ping_comp_event);
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_cistatic int
279562306a36Sopenharmony_ciiscsi_if_send_reply(u32 portid, int type, void *payload, int size)
279662306a36Sopenharmony_ci{
279762306a36Sopenharmony_ci	struct sk_buff	*skb;
279862306a36Sopenharmony_ci	struct nlmsghdr	*nlh;
279962306a36Sopenharmony_ci	int len = nlmsg_total_size(size);
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_ATOMIC);
280262306a36Sopenharmony_ci	if (!skb) {
280362306a36Sopenharmony_ci		printk(KERN_ERR "Could not allocate skb to send reply.\n");
280462306a36Sopenharmony_ci		return -ENOMEM;
280562306a36Sopenharmony_ci	}
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, type, (len - sizeof(*nlh)), 0);
280862306a36Sopenharmony_ci	memcpy(nlmsg_data(nlh), payload, size);
280962306a36Sopenharmony_ci	return iscsi_unicast_skb(skb, portid);
281062306a36Sopenharmony_ci}
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_cistatic int
281362306a36Sopenharmony_ciiscsi_if_get_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
281462306a36Sopenharmony_ci{
281562306a36Sopenharmony_ci	struct iscsi_uevent *ev = nlmsg_data(nlh);
281662306a36Sopenharmony_ci	struct iscsi_stats *stats;
281762306a36Sopenharmony_ci	struct sk_buff *skbstat;
281862306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
281962306a36Sopenharmony_ci	struct nlmsghdr	*nlhstat;
282062306a36Sopenharmony_ci	struct iscsi_uevent *evstat;
282162306a36Sopenharmony_ci	struct iscsi_internal *priv;
282262306a36Sopenharmony_ci	int len = nlmsg_total_size(sizeof(*ev) +
282362306a36Sopenharmony_ci				   sizeof(struct iscsi_stats) +
282462306a36Sopenharmony_ci				   sizeof(struct iscsi_stats_custom) *
282562306a36Sopenharmony_ci				   ISCSI_STATS_CUSTOM_MAX);
282662306a36Sopenharmony_ci	int err = 0;
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(transport);
282962306a36Sopenharmony_ci	if (!priv)
283062306a36Sopenharmony_ci		return -EINVAL;
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_ci	conn = iscsi_conn_lookup(ev->u.get_stats.sid, ev->u.get_stats.cid);
283362306a36Sopenharmony_ci	if (!conn)
283462306a36Sopenharmony_ci		return -EEXIST;
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci	do {
283762306a36Sopenharmony_ci		int actual_size;
283862306a36Sopenharmony_ci
283962306a36Sopenharmony_ci		skbstat = alloc_skb(len, GFP_ATOMIC);
284062306a36Sopenharmony_ci		if (!skbstat) {
284162306a36Sopenharmony_ci			iscsi_cls_conn_printk(KERN_ERR, conn, "can not "
284262306a36Sopenharmony_ci					      "deliver stats: OOM\n");
284362306a36Sopenharmony_ci			return -ENOMEM;
284462306a36Sopenharmony_ci		}
284562306a36Sopenharmony_ci
284662306a36Sopenharmony_ci		nlhstat = __nlmsg_put(skbstat, 0, 0, 0,
284762306a36Sopenharmony_ci				      (len - sizeof(*nlhstat)), 0);
284862306a36Sopenharmony_ci		evstat = nlmsg_data(nlhstat);
284962306a36Sopenharmony_ci		memset(evstat, 0, sizeof(*evstat));
285062306a36Sopenharmony_ci		evstat->transport_handle = iscsi_handle(conn->transport);
285162306a36Sopenharmony_ci		evstat->type = nlh->nlmsg_type;
285262306a36Sopenharmony_ci		evstat->u.get_stats.cid =
285362306a36Sopenharmony_ci			ev->u.get_stats.cid;
285462306a36Sopenharmony_ci		evstat->u.get_stats.sid =
285562306a36Sopenharmony_ci			ev->u.get_stats.sid;
285662306a36Sopenharmony_ci		stats = (struct iscsi_stats *)
285762306a36Sopenharmony_ci			((char*)evstat + sizeof(*evstat));
285862306a36Sopenharmony_ci		memset(stats, 0, sizeof(*stats));
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci		transport->get_stats(conn, stats);
286162306a36Sopenharmony_ci		actual_size = nlmsg_total_size(sizeof(struct iscsi_uevent) +
286262306a36Sopenharmony_ci					       sizeof(struct iscsi_stats) +
286362306a36Sopenharmony_ci					       sizeof(struct iscsi_stats_custom) *
286462306a36Sopenharmony_ci					       stats->custom_length);
286562306a36Sopenharmony_ci		actual_size -= sizeof(*nlhstat);
286662306a36Sopenharmony_ci		actual_size = nlmsg_msg_size(actual_size);
286762306a36Sopenharmony_ci		skb_trim(skbstat, NLMSG_ALIGN(actual_size));
286862306a36Sopenharmony_ci		nlhstat->nlmsg_len = actual_size;
286962306a36Sopenharmony_ci
287062306a36Sopenharmony_ci		err = iscsi_multicast_skb(skbstat, ISCSI_NL_GRP_ISCSID,
287162306a36Sopenharmony_ci					  GFP_ATOMIC);
287262306a36Sopenharmony_ci	} while (err < 0 && err != -ECONNREFUSED);
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	return err;
287562306a36Sopenharmony_ci}
287662306a36Sopenharmony_ci
287762306a36Sopenharmony_ci/**
287862306a36Sopenharmony_ci * iscsi_session_event - send session destr. completion event
287962306a36Sopenharmony_ci * @session: iscsi class session
288062306a36Sopenharmony_ci * @event: type of event
288162306a36Sopenharmony_ci */
288262306a36Sopenharmony_ciint iscsi_session_event(struct iscsi_cls_session *session,
288362306a36Sopenharmony_ci			enum iscsi_uevent_e event)
288462306a36Sopenharmony_ci{
288562306a36Sopenharmony_ci	struct iscsi_internal *priv;
288662306a36Sopenharmony_ci	struct Scsi_Host *shost;
288762306a36Sopenharmony_ci	struct iscsi_uevent *ev;
288862306a36Sopenharmony_ci	struct sk_buff  *skb;
288962306a36Sopenharmony_ci	struct nlmsghdr *nlh;
289062306a36Sopenharmony_ci	int rc, len = nlmsg_total_size(sizeof(*ev));
289162306a36Sopenharmony_ci
289262306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(session->transport);
289362306a36Sopenharmony_ci	if (!priv)
289462306a36Sopenharmony_ci		return -EINVAL;
289562306a36Sopenharmony_ci	shost = iscsi_session_to_shost(session);
289662306a36Sopenharmony_ci
289762306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_KERNEL);
289862306a36Sopenharmony_ci	if (!skb) {
289962306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
290062306a36Sopenharmony_ci					 "Cannot notify userspace of session "
290162306a36Sopenharmony_ci					 "event %u\n", event);
290262306a36Sopenharmony_ci		return -ENOMEM;
290362306a36Sopenharmony_ci	}
290462306a36Sopenharmony_ci
290562306a36Sopenharmony_ci	nlh = __nlmsg_put(skb, 0, 0, 0, (len - sizeof(*nlh)), 0);
290662306a36Sopenharmony_ci	ev = nlmsg_data(nlh);
290762306a36Sopenharmony_ci	ev->transport_handle = iscsi_handle(session->transport);
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci	ev->type = event;
291062306a36Sopenharmony_ci	switch (event) {
291162306a36Sopenharmony_ci	case ISCSI_KEVENT_DESTROY_SESSION:
291262306a36Sopenharmony_ci		ev->r.d_session.host_no = shost->host_no;
291362306a36Sopenharmony_ci		ev->r.d_session.sid = session->sid;
291462306a36Sopenharmony_ci		break;
291562306a36Sopenharmony_ci	case ISCSI_KEVENT_CREATE_SESSION:
291662306a36Sopenharmony_ci		ev->r.c_session_ret.host_no = shost->host_no;
291762306a36Sopenharmony_ci		ev->r.c_session_ret.sid = session->sid;
291862306a36Sopenharmony_ci		break;
291962306a36Sopenharmony_ci	case ISCSI_KEVENT_UNBIND_SESSION:
292062306a36Sopenharmony_ci		ev->r.unbind_session.host_no = shost->host_no;
292162306a36Sopenharmony_ci		ev->r.unbind_session.sid = session->sid;
292262306a36Sopenharmony_ci		break;
292362306a36Sopenharmony_ci	default:
292462306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session, "Invalid event "
292562306a36Sopenharmony_ci					 "%u.\n", event);
292662306a36Sopenharmony_ci		kfree_skb(skb);
292762306a36Sopenharmony_ci		return -EINVAL;
292862306a36Sopenharmony_ci	}
292962306a36Sopenharmony_ci
293062306a36Sopenharmony_ci	/*
293162306a36Sopenharmony_ci	 * this will occur if the daemon is not up, so we just warn
293262306a36Sopenharmony_ci	 * the user and when the daemon is restarted it will handle it
293362306a36Sopenharmony_ci	 */
293462306a36Sopenharmony_ci	rc = iscsi_multicast_skb(skb, ISCSI_NL_GRP_ISCSID, GFP_KERNEL);
293562306a36Sopenharmony_ci	if (rc == -ESRCH)
293662306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
293762306a36Sopenharmony_ci					 "Cannot notify userspace of session "
293862306a36Sopenharmony_ci					 "event %u. Check iscsi daemon\n",
293962306a36Sopenharmony_ci					 event);
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session, "Completed handling event %d rc %d\n",
294262306a36Sopenharmony_ci				event, rc);
294362306a36Sopenharmony_ci	return rc;
294462306a36Sopenharmony_ci}
294562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_event);
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_cistatic int
294862306a36Sopenharmony_ciiscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_endpoint *ep,
294962306a36Sopenharmony_ci			struct iscsi_uevent *ev, pid_t pid,
295062306a36Sopenharmony_ci			uint32_t initial_cmdsn,	uint16_t cmds_max,
295162306a36Sopenharmony_ci			uint16_t queue_depth)
295262306a36Sopenharmony_ci{
295362306a36Sopenharmony_ci	struct iscsi_transport *transport = priv->iscsi_transport;
295462306a36Sopenharmony_ci	struct iscsi_cls_session *session;
295562306a36Sopenharmony_ci	struct Scsi_Host *shost;
295662306a36Sopenharmony_ci
295762306a36Sopenharmony_ci	session = transport->create_session(ep, cmds_max, queue_depth,
295862306a36Sopenharmony_ci					    initial_cmdsn);
295962306a36Sopenharmony_ci	if (!session)
296062306a36Sopenharmony_ci		return -ENOMEM;
296162306a36Sopenharmony_ci
296262306a36Sopenharmony_ci	session->creator = pid;
296362306a36Sopenharmony_ci	shost = iscsi_session_to_shost(session);
296462306a36Sopenharmony_ci	ev->r.c_session_ret.host_no = shost->host_no;
296562306a36Sopenharmony_ci	ev->r.c_session_ret.sid = session->sid;
296662306a36Sopenharmony_ci	ISCSI_DBG_TRANS_SESSION(session,
296762306a36Sopenharmony_ci				"Completed creating transport session\n");
296862306a36Sopenharmony_ci	return 0;
296962306a36Sopenharmony_ci}
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_cistatic int
297262306a36Sopenharmony_ciiscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
297362306a36Sopenharmony_ci{
297462306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
297562306a36Sopenharmony_ci	struct iscsi_cls_session *session;
297662306a36Sopenharmony_ci
297762306a36Sopenharmony_ci	session = iscsi_session_lookup(ev->u.c_conn.sid);
297862306a36Sopenharmony_ci	if (!session) {
297962306a36Sopenharmony_ci		printk(KERN_ERR "iscsi: invalid session %d.\n",
298062306a36Sopenharmony_ci		       ev->u.c_conn.sid);
298162306a36Sopenharmony_ci		return -EINVAL;
298262306a36Sopenharmony_ci	}
298362306a36Sopenharmony_ci
298462306a36Sopenharmony_ci	conn = transport->create_conn(session, ev->u.c_conn.cid);
298562306a36Sopenharmony_ci	if (!conn) {
298662306a36Sopenharmony_ci		iscsi_cls_session_printk(KERN_ERR, session,
298762306a36Sopenharmony_ci					 "couldn't create a new connection.");
298862306a36Sopenharmony_ci		return -ENOMEM;
298962306a36Sopenharmony_ci	}
299062306a36Sopenharmony_ci
299162306a36Sopenharmony_ci	ev->r.c_conn_ret.sid = session->sid;
299262306a36Sopenharmony_ci	ev->r.c_conn_ret.cid = conn->cid;
299362306a36Sopenharmony_ci
299462306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "Completed creating transport conn\n");
299562306a36Sopenharmony_ci	return 0;
299662306a36Sopenharmony_ci}
299762306a36Sopenharmony_ci
299862306a36Sopenharmony_cistatic int
299962306a36Sopenharmony_ciiscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev)
300062306a36Sopenharmony_ci{
300162306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci	conn = iscsi_conn_lookup(ev->u.d_conn.sid, ev->u.d_conn.cid);
300462306a36Sopenharmony_ci	if (!conn)
300562306a36Sopenharmony_ci		return -EINVAL;
300662306a36Sopenharmony_ci
300762306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "Flushing cleanup during destruction\n");
300862306a36Sopenharmony_ci	flush_work(&conn->cleanup_work);
300962306a36Sopenharmony_ci	ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci	if (transport->destroy_conn)
301262306a36Sopenharmony_ci		transport->destroy_conn(conn);
301362306a36Sopenharmony_ci	return 0;
301462306a36Sopenharmony_ci}
301562306a36Sopenharmony_ci
301662306a36Sopenharmony_cistatic int
301762306a36Sopenharmony_ciiscsi_if_set_param(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen)
301862306a36Sopenharmony_ci{
301962306a36Sopenharmony_ci	char *data = (char*)ev + sizeof(*ev);
302062306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
302162306a36Sopenharmony_ci	struct iscsi_cls_session *session;
302262306a36Sopenharmony_ci	int err = 0, value = 0, state;
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	if (ev->u.set_param.len > rlen ||
302562306a36Sopenharmony_ci	    ev->u.set_param.len > PAGE_SIZE)
302662306a36Sopenharmony_ci		return -EINVAL;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	session = iscsi_session_lookup(ev->u.set_param.sid);
302962306a36Sopenharmony_ci	conn = iscsi_conn_lookup(ev->u.set_param.sid, ev->u.set_param.cid);
303062306a36Sopenharmony_ci	if (!conn || !session)
303162306a36Sopenharmony_ci		return -EINVAL;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	/* data will be regarded as NULL-ended string, do length check */
303462306a36Sopenharmony_ci	if (strlen(data) > ev->u.set_param.len)
303562306a36Sopenharmony_ci		return -EINVAL;
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	switch (ev->u.set_param.param) {
303862306a36Sopenharmony_ci	case ISCSI_PARAM_SESS_RECOVERY_TMO:
303962306a36Sopenharmony_ci		sscanf(data, "%d", &value);
304062306a36Sopenharmony_ci		if (!session->recovery_tmo_sysfs_override)
304162306a36Sopenharmony_ci			session->recovery_tmo = value;
304262306a36Sopenharmony_ci		break;
304362306a36Sopenharmony_ci	default:
304462306a36Sopenharmony_ci		state = READ_ONCE(conn->state);
304562306a36Sopenharmony_ci		if (state == ISCSI_CONN_BOUND || state == ISCSI_CONN_UP) {
304662306a36Sopenharmony_ci			err = transport->set_param(conn, ev->u.set_param.param,
304762306a36Sopenharmony_ci					data, ev->u.set_param.len);
304862306a36Sopenharmony_ci		} else {
304962306a36Sopenharmony_ci			return -ENOTCONN;
305062306a36Sopenharmony_ci		}
305162306a36Sopenharmony_ci	}
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci	return err;
305462306a36Sopenharmony_ci}
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_cistatic int iscsi_if_ep_connect(struct iscsi_transport *transport,
305762306a36Sopenharmony_ci			       struct iscsi_uevent *ev, int msg_type)
305862306a36Sopenharmony_ci{
305962306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
306062306a36Sopenharmony_ci	struct sockaddr *dst_addr;
306162306a36Sopenharmony_ci	struct Scsi_Host *shost = NULL;
306262306a36Sopenharmony_ci	int non_blocking, err = 0;
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	if (!transport->ep_connect)
306562306a36Sopenharmony_ci		return -EINVAL;
306662306a36Sopenharmony_ci
306762306a36Sopenharmony_ci	if (msg_type == ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST) {
306862306a36Sopenharmony_ci		shost = scsi_host_lookup(ev->u.ep_connect_through_host.host_no);
306962306a36Sopenharmony_ci		if (!shost) {
307062306a36Sopenharmony_ci			printk(KERN_ERR "ep connect failed. Could not find "
307162306a36Sopenharmony_ci			       "host no %u\n",
307262306a36Sopenharmony_ci			       ev->u.ep_connect_through_host.host_no);
307362306a36Sopenharmony_ci			return -ENODEV;
307462306a36Sopenharmony_ci		}
307562306a36Sopenharmony_ci		non_blocking = ev->u.ep_connect_through_host.non_blocking;
307662306a36Sopenharmony_ci	} else
307762306a36Sopenharmony_ci		non_blocking = ev->u.ep_connect.non_blocking;
307862306a36Sopenharmony_ci
307962306a36Sopenharmony_ci	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
308062306a36Sopenharmony_ci	ep = transport->ep_connect(shost, dst_addr, non_blocking);
308162306a36Sopenharmony_ci	if (IS_ERR(ep)) {
308262306a36Sopenharmony_ci		err = PTR_ERR(ep);
308362306a36Sopenharmony_ci		goto release_host;
308462306a36Sopenharmony_ci	}
308562306a36Sopenharmony_ci
308662306a36Sopenharmony_ci	ev->r.ep_connect_ret.handle = ep->id;
308762306a36Sopenharmony_cirelease_host:
308862306a36Sopenharmony_ci	if (shost)
308962306a36Sopenharmony_ci		scsi_host_put(shost);
309062306a36Sopenharmony_ci	return err;
309162306a36Sopenharmony_ci}
309262306a36Sopenharmony_ci
309362306a36Sopenharmony_cistatic int iscsi_if_ep_disconnect(struct iscsi_transport *transport,
309462306a36Sopenharmony_ci				  u64 ep_handle)
309562306a36Sopenharmony_ci{
309662306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
309762306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	if (!transport->ep_disconnect)
310062306a36Sopenharmony_ci		return -EINVAL;
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	ep = iscsi_lookup_endpoint(ep_handle);
310362306a36Sopenharmony_ci	if (!ep)
310462306a36Sopenharmony_ci		return -EINVAL;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	conn = ep->conn;
310762306a36Sopenharmony_ci	if (!conn) {
310862306a36Sopenharmony_ci		/*
310962306a36Sopenharmony_ci		 * conn was not even bound yet, so we can't get iscsi conn
311062306a36Sopenharmony_ci		 * failures yet.
311162306a36Sopenharmony_ci		 */
311262306a36Sopenharmony_ci		transport->ep_disconnect(ep);
311362306a36Sopenharmony_ci		goto put_ep;
311462306a36Sopenharmony_ci	}
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_ci	mutex_lock(&conn->ep_mutex);
311762306a36Sopenharmony_ci	iscsi_if_disconnect_bound_ep(conn, ep, false);
311862306a36Sopenharmony_ci	mutex_unlock(&conn->ep_mutex);
311962306a36Sopenharmony_ciput_ep:
312062306a36Sopenharmony_ci	iscsi_put_endpoint(ep);
312162306a36Sopenharmony_ci	return 0;
312262306a36Sopenharmony_ci}
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_cistatic int
312562306a36Sopenharmony_ciiscsi_if_transport_ep(struct iscsi_transport *transport,
312662306a36Sopenharmony_ci		      struct iscsi_uevent *ev, int msg_type, u32 rlen)
312762306a36Sopenharmony_ci{
312862306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
312962306a36Sopenharmony_ci	int rc = 0;
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	switch (msg_type) {
313262306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
313362306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
313462306a36Sopenharmony_ci		if (rlen < sizeof(struct sockaddr))
313562306a36Sopenharmony_ci			rc = -EINVAL;
313662306a36Sopenharmony_ci		else
313762306a36Sopenharmony_ci			rc = iscsi_if_ep_connect(transport, ev, msg_type);
313862306a36Sopenharmony_ci		break;
313962306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
314062306a36Sopenharmony_ci		if (!transport->ep_poll)
314162306a36Sopenharmony_ci			return -EINVAL;
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci		ep = iscsi_lookup_endpoint(ev->u.ep_poll.ep_handle);
314462306a36Sopenharmony_ci		if (!ep)
314562306a36Sopenharmony_ci			return -EINVAL;
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci		ev->r.retcode = transport->ep_poll(ep,
314862306a36Sopenharmony_ci						   ev->u.ep_poll.timeout_ms);
314962306a36Sopenharmony_ci		iscsi_put_endpoint(ep);
315062306a36Sopenharmony_ci		break;
315162306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
315262306a36Sopenharmony_ci		rc = iscsi_if_ep_disconnect(transport,
315362306a36Sopenharmony_ci					    ev->u.ep_disconnect.ep_handle);
315462306a36Sopenharmony_ci		break;
315562306a36Sopenharmony_ci	}
315662306a36Sopenharmony_ci	return rc;
315762306a36Sopenharmony_ci}
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_cistatic int
316062306a36Sopenharmony_ciiscsi_tgt_dscvr(struct iscsi_transport *transport,
316162306a36Sopenharmony_ci		struct iscsi_uevent *ev, u32 rlen)
316262306a36Sopenharmony_ci{
316362306a36Sopenharmony_ci	struct Scsi_Host *shost;
316462306a36Sopenharmony_ci	struct sockaddr *dst_addr;
316562306a36Sopenharmony_ci	int err;
316662306a36Sopenharmony_ci
316762306a36Sopenharmony_ci	if (rlen < sizeof(*dst_addr))
316862306a36Sopenharmony_ci		return -EINVAL;
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	if (!transport->tgt_dscvr)
317162306a36Sopenharmony_ci		return -EINVAL;
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.tgt_dscvr.host_no);
317462306a36Sopenharmony_ci	if (!shost) {
317562306a36Sopenharmony_ci		printk(KERN_ERR "target discovery could not find host no %u\n",
317662306a36Sopenharmony_ci		       ev->u.tgt_dscvr.host_no);
317762306a36Sopenharmony_ci		return -ENODEV;
317862306a36Sopenharmony_ci	}
317962306a36Sopenharmony_ci
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci	dst_addr = (struct sockaddr *)((char*)ev + sizeof(*ev));
318262306a36Sopenharmony_ci	err = transport->tgt_dscvr(shost, ev->u.tgt_dscvr.type,
318362306a36Sopenharmony_ci				   ev->u.tgt_dscvr.enable, dst_addr);
318462306a36Sopenharmony_ci	scsi_host_put(shost);
318562306a36Sopenharmony_ci	return err;
318662306a36Sopenharmony_ci}
318762306a36Sopenharmony_ci
318862306a36Sopenharmony_cistatic int
318962306a36Sopenharmony_ciiscsi_set_host_param(struct iscsi_transport *transport,
319062306a36Sopenharmony_ci		     struct iscsi_uevent *ev, u32 rlen)
319162306a36Sopenharmony_ci{
319262306a36Sopenharmony_ci	char *data = (char*)ev + sizeof(*ev);
319362306a36Sopenharmony_ci	struct Scsi_Host *shost;
319462306a36Sopenharmony_ci	int err;
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ci	if (!transport->set_host_param)
319762306a36Sopenharmony_ci		return -ENOSYS;
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	if (ev->u.set_host_param.len > rlen ||
320062306a36Sopenharmony_ci	    ev->u.set_host_param.len > PAGE_SIZE)
320162306a36Sopenharmony_ci		return -EINVAL;
320262306a36Sopenharmony_ci
320362306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.set_host_param.host_no);
320462306a36Sopenharmony_ci	if (!shost) {
320562306a36Sopenharmony_ci		printk(KERN_ERR "set_host_param could not find host no %u\n",
320662306a36Sopenharmony_ci		       ev->u.set_host_param.host_no);
320762306a36Sopenharmony_ci		return -ENODEV;
320862306a36Sopenharmony_ci	}
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci	/* see similar check in iscsi_if_set_param() */
321162306a36Sopenharmony_ci	if (strlen(data) > ev->u.set_host_param.len)
321262306a36Sopenharmony_ci		return -EINVAL;
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	err = transport->set_host_param(shost, ev->u.set_host_param.param,
321562306a36Sopenharmony_ci					data, ev->u.set_host_param.len);
321662306a36Sopenharmony_ci	scsi_host_put(shost);
321762306a36Sopenharmony_ci	return err;
321862306a36Sopenharmony_ci}
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_cistatic int
322162306a36Sopenharmony_ciiscsi_set_path(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen)
322262306a36Sopenharmony_ci{
322362306a36Sopenharmony_ci	struct Scsi_Host *shost;
322462306a36Sopenharmony_ci	struct iscsi_path *params;
322562306a36Sopenharmony_ci	int err;
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	if (rlen < sizeof(*params))
322862306a36Sopenharmony_ci		return -EINVAL;
322962306a36Sopenharmony_ci
323062306a36Sopenharmony_ci	if (!transport->set_path)
323162306a36Sopenharmony_ci		return -ENOSYS;
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.set_path.host_no);
323462306a36Sopenharmony_ci	if (!shost) {
323562306a36Sopenharmony_ci		printk(KERN_ERR "set path could not find host no %u\n",
323662306a36Sopenharmony_ci		       ev->u.set_path.host_no);
323762306a36Sopenharmony_ci		return -ENODEV;
323862306a36Sopenharmony_ci	}
323962306a36Sopenharmony_ci
324062306a36Sopenharmony_ci	params = (struct iscsi_path *)((char *)ev + sizeof(*ev));
324162306a36Sopenharmony_ci	err = transport->set_path(shost, params);
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	scsi_host_put(shost);
324462306a36Sopenharmony_ci	return err;
324562306a36Sopenharmony_ci}
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_cistatic int iscsi_session_has_conns(int sid)
324862306a36Sopenharmony_ci{
324962306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
325062306a36Sopenharmony_ci	unsigned long flags;
325162306a36Sopenharmony_ci	int found = 0;
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	spin_lock_irqsave(&connlock, flags);
325462306a36Sopenharmony_ci	list_for_each_entry(conn, &connlist, conn_list) {
325562306a36Sopenharmony_ci		if (iscsi_conn_get_sid(conn) == sid) {
325662306a36Sopenharmony_ci			found = 1;
325762306a36Sopenharmony_ci			break;
325862306a36Sopenharmony_ci		}
325962306a36Sopenharmony_ci	}
326062306a36Sopenharmony_ci	spin_unlock_irqrestore(&connlock, flags);
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci	return found;
326362306a36Sopenharmony_ci}
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_cistatic int
326662306a36Sopenharmony_ciiscsi_set_iface_params(struct iscsi_transport *transport,
326762306a36Sopenharmony_ci		       struct iscsi_uevent *ev, uint32_t len)
326862306a36Sopenharmony_ci{
326962306a36Sopenharmony_ci	char *data = (char *)ev + sizeof(*ev);
327062306a36Sopenharmony_ci	struct Scsi_Host *shost;
327162306a36Sopenharmony_ci	int err;
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	if (!transport->set_iface_param)
327462306a36Sopenharmony_ci		return -ENOSYS;
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.set_iface_params.host_no);
327762306a36Sopenharmony_ci	if (!shost) {
327862306a36Sopenharmony_ci		printk(KERN_ERR "set_iface_params could not find host no %u\n",
327962306a36Sopenharmony_ci		       ev->u.set_iface_params.host_no);
328062306a36Sopenharmony_ci		return -ENODEV;
328162306a36Sopenharmony_ci	}
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci	err = transport->set_iface_param(shost, data, len);
328462306a36Sopenharmony_ci	scsi_host_put(shost);
328562306a36Sopenharmony_ci	return err;
328662306a36Sopenharmony_ci}
328762306a36Sopenharmony_ci
328862306a36Sopenharmony_cistatic int
328962306a36Sopenharmony_ciiscsi_send_ping(struct iscsi_transport *transport, struct iscsi_uevent *ev, u32 rlen)
329062306a36Sopenharmony_ci{
329162306a36Sopenharmony_ci	struct Scsi_Host *shost;
329262306a36Sopenharmony_ci	struct sockaddr *dst_addr;
329362306a36Sopenharmony_ci	int err;
329462306a36Sopenharmony_ci
329562306a36Sopenharmony_ci	if (rlen < sizeof(*dst_addr))
329662306a36Sopenharmony_ci		return -EINVAL;
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	if (!transport->send_ping)
329962306a36Sopenharmony_ci		return -ENOSYS;
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.iscsi_ping.host_no);
330262306a36Sopenharmony_ci	if (!shost) {
330362306a36Sopenharmony_ci		printk(KERN_ERR "iscsi_ping could not find host no %u\n",
330462306a36Sopenharmony_ci		       ev->u.iscsi_ping.host_no);
330562306a36Sopenharmony_ci		return -ENODEV;
330662306a36Sopenharmony_ci	}
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci	dst_addr = (struct sockaddr *)((char *)ev + sizeof(*ev));
330962306a36Sopenharmony_ci	err = transport->send_ping(shost, ev->u.iscsi_ping.iface_num,
331062306a36Sopenharmony_ci				   ev->u.iscsi_ping.iface_type,
331162306a36Sopenharmony_ci				   ev->u.iscsi_ping.payload_size,
331262306a36Sopenharmony_ci				   ev->u.iscsi_ping.pid,
331362306a36Sopenharmony_ci				   dst_addr);
331462306a36Sopenharmony_ci	scsi_host_put(shost);
331562306a36Sopenharmony_ci	return err;
331662306a36Sopenharmony_ci}
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_cistatic int
331962306a36Sopenharmony_ciiscsi_get_chap(struct iscsi_transport *transport, struct nlmsghdr *nlh)
332062306a36Sopenharmony_ci{
332162306a36Sopenharmony_ci	struct iscsi_uevent *ev = nlmsg_data(nlh);
332262306a36Sopenharmony_ci	struct Scsi_Host *shost = NULL;
332362306a36Sopenharmony_ci	struct iscsi_chap_rec *chap_rec;
332462306a36Sopenharmony_ci	struct iscsi_internal *priv;
332562306a36Sopenharmony_ci	struct sk_buff *skbchap;
332662306a36Sopenharmony_ci	struct nlmsghdr *nlhchap;
332762306a36Sopenharmony_ci	struct iscsi_uevent *evchap;
332862306a36Sopenharmony_ci	uint32_t chap_buf_size;
332962306a36Sopenharmony_ci	int len, err = 0;
333062306a36Sopenharmony_ci	char *buf;
333162306a36Sopenharmony_ci
333262306a36Sopenharmony_ci	if (!transport->get_chap)
333362306a36Sopenharmony_ci		return -EINVAL;
333462306a36Sopenharmony_ci
333562306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(transport);
333662306a36Sopenharmony_ci	if (!priv)
333762306a36Sopenharmony_ci		return -EINVAL;
333862306a36Sopenharmony_ci
333962306a36Sopenharmony_ci	chap_buf_size = (ev->u.get_chap.num_entries * sizeof(*chap_rec));
334062306a36Sopenharmony_ci	len = nlmsg_total_size(sizeof(*ev) + chap_buf_size);
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.get_chap.host_no);
334362306a36Sopenharmony_ci	if (!shost) {
334462306a36Sopenharmony_ci		printk(KERN_ERR "%s: failed. Could not find host no %u\n",
334562306a36Sopenharmony_ci		       __func__, ev->u.get_chap.host_no);
334662306a36Sopenharmony_ci		return -ENODEV;
334762306a36Sopenharmony_ci	}
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	do {
335062306a36Sopenharmony_ci		int actual_size;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci		skbchap = alloc_skb(len, GFP_KERNEL);
335362306a36Sopenharmony_ci		if (!skbchap) {
335462306a36Sopenharmony_ci			printk(KERN_ERR "can not deliver chap: OOM\n");
335562306a36Sopenharmony_ci			err = -ENOMEM;
335662306a36Sopenharmony_ci			goto exit_get_chap;
335762306a36Sopenharmony_ci		}
335862306a36Sopenharmony_ci
335962306a36Sopenharmony_ci		nlhchap = __nlmsg_put(skbchap, 0, 0, 0,
336062306a36Sopenharmony_ci				      (len - sizeof(*nlhchap)), 0);
336162306a36Sopenharmony_ci		evchap = nlmsg_data(nlhchap);
336262306a36Sopenharmony_ci		memset(evchap, 0, sizeof(*evchap));
336362306a36Sopenharmony_ci		evchap->transport_handle = iscsi_handle(transport);
336462306a36Sopenharmony_ci		evchap->type = nlh->nlmsg_type;
336562306a36Sopenharmony_ci		evchap->u.get_chap.host_no = ev->u.get_chap.host_no;
336662306a36Sopenharmony_ci		evchap->u.get_chap.chap_tbl_idx = ev->u.get_chap.chap_tbl_idx;
336762306a36Sopenharmony_ci		evchap->u.get_chap.num_entries = ev->u.get_chap.num_entries;
336862306a36Sopenharmony_ci		buf = (char *)evchap + sizeof(*evchap);
336962306a36Sopenharmony_ci		memset(buf, 0, chap_buf_size);
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_ci		err = transport->get_chap(shost, ev->u.get_chap.chap_tbl_idx,
337262306a36Sopenharmony_ci				    &evchap->u.get_chap.num_entries, buf);
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci		actual_size = nlmsg_total_size(sizeof(*ev) + chap_buf_size);
337562306a36Sopenharmony_ci		skb_trim(skbchap, NLMSG_ALIGN(actual_size));
337662306a36Sopenharmony_ci		nlhchap->nlmsg_len = actual_size;
337762306a36Sopenharmony_ci
337862306a36Sopenharmony_ci		err = iscsi_multicast_skb(skbchap, ISCSI_NL_GRP_ISCSID,
337962306a36Sopenharmony_ci					  GFP_KERNEL);
338062306a36Sopenharmony_ci	} while (err < 0 && err != -ECONNREFUSED);
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ciexit_get_chap:
338362306a36Sopenharmony_ci	scsi_host_put(shost);
338462306a36Sopenharmony_ci	return err;
338562306a36Sopenharmony_ci}
338662306a36Sopenharmony_ci
338762306a36Sopenharmony_cistatic int iscsi_set_chap(struct iscsi_transport *transport,
338862306a36Sopenharmony_ci			  struct iscsi_uevent *ev, uint32_t len)
338962306a36Sopenharmony_ci{
339062306a36Sopenharmony_ci	char *data = (char *)ev + sizeof(*ev);
339162306a36Sopenharmony_ci	struct Scsi_Host *shost;
339262306a36Sopenharmony_ci	int err = 0;
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci	if (!transport->set_chap)
339562306a36Sopenharmony_ci		return -ENOSYS;
339662306a36Sopenharmony_ci
339762306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.set_path.host_no);
339862306a36Sopenharmony_ci	if (!shost) {
339962306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
340062306a36Sopenharmony_ci		       __func__, ev->u.set_path.host_no);
340162306a36Sopenharmony_ci		return -ENODEV;
340262306a36Sopenharmony_ci	}
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci	err = transport->set_chap(shost, data, len);
340562306a36Sopenharmony_ci	scsi_host_put(shost);
340662306a36Sopenharmony_ci	return err;
340762306a36Sopenharmony_ci}
340862306a36Sopenharmony_ci
340962306a36Sopenharmony_cistatic int iscsi_delete_chap(struct iscsi_transport *transport,
341062306a36Sopenharmony_ci			     struct iscsi_uevent *ev)
341162306a36Sopenharmony_ci{
341262306a36Sopenharmony_ci	struct Scsi_Host *shost;
341362306a36Sopenharmony_ci	int err = 0;
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci	if (!transport->delete_chap)
341662306a36Sopenharmony_ci		return -ENOSYS;
341762306a36Sopenharmony_ci
341862306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.delete_chap.host_no);
341962306a36Sopenharmony_ci	if (!shost) {
342062306a36Sopenharmony_ci		printk(KERN_ERR "%s could not find host no %u\n",
342162306a36Sopenharmony_ci		       __func__, ev->u.delete_chap.host_no);
342262306a36Sopenharmony_ci		return -ENODEV;
342362306a36Sopenharmony_ci	}
342462306a36Sopenharmony_ci
342562306a36Sopenharmony_ci	err = transport->delete_chap(shost, ev->u.delete_chap.chap_tbl_idx);
342662306a36Sopenharmony_ci	scsi_host_put(shost);
342762306a36Sopenharmony_ci	return err;
342862306a36Sopenharmony_ci}
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_cistatic const struct {
343162306a36Sopenharmony_ci	enum iscsi_discovery_parent_type value;
343262306a36Sopenharmony_ci	char				*name;
343362306a36Sopenharmony_ci} iscsi_discovery_parent_names[] = {
343462306a36Sopenharmony_ci	{ISCSI_DISC_PARENT_UNKNOWN,	"Unknown" },
343562306a36Sopenharmony_ci	{ISCSI_DISC_PARENT_SENDTGT,	"Sendtarget" },
343662306a36Sopenharmony_ci	{ISCSI_DISC_PARENT_ISNS,	"isns" },
343762306a36Sopenharmony_ci};
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_cichar *iscsi_get_discovery_parent_name(int parent_type)
344062306a36Sopenharmony_ci{
344162306a36Sopenharmony_ci	int i;
344262306a36Sopenharmony_ci	char *state = "Unknown!";
344362306a36Sopenharmony_ci
344462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iscsi_discovery_parent_names); i++) {
344562306a36Sopenharmony_ci		if (iscsi_discovery_parent_names[i].value & parent_type) {
344662306a36Sopenharmony_ci			state = iscsi_discovery_parent_names[i].name;
344762306a36Sopenharmony_ci			break;
344862306a36Sopenharmony_ci		}
344962306a36Sopenharmony_ci	}
345062306a36Sopenharmony_ci	return state;
345162306a36Sopenharmony_ci}
345262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_discovery_parent_name);
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_cistatic int iscsi_set_flashnode_param(struct iscsi_transport *transport,
345562306a36Sopenharmony_ci				     struct iscsi_uevent *ev, uint32_t len)
345662306a36Sopenharmony_ci{
345762306a36Sopenharmony_ci	char *data = (char *)ev + sizeof(*ev);
345862306a36Sopenharmony_ci	struct Scsi_Host *shost;
345962306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess;
346062306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn;
346162306a36Sopenharmony_ci	struct device *dev;
346262306a36Sopenharmony_ci	uint32_t idx;
346362306a36Sopenharmony_ci	int err = 0;
346462306a36Sopenharmony_ci
346562306a36Sopenharmony_ci	if (!transport->set_flashnode_param) {
346662306a36Sopenharmony_ci		err = -ENOSYS;
346762306a36Sopenharmony_ci		goto exit_set_fnode;
346862306a36Sopenharmony_ci	}
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.set_flashnode.host_no);
347162306a36Sopenharmony_ci	if (!shost) {
347262306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
347362306a36Sopenharmony_ci		       __func__, ev->u.set_flashnode.host_no);
347462306a36Sopenharmony_ci		err = -ENODEV;
347562306a36Sopenharmony_ci		goto exit_set_fnode;
347662306a36Sopenharmony_ci	}
347762306a36Sopenharmony_ci
347862306a36Sopenharmony_ci	idx = ev->u.set_flashnode.flashnode_idx;
347962306a36Sopenharmony_ci	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
348062306a36Sopenharmony_ci	if (!fnode_sess) {
348162306a36Sopenharmony_ci		pr_err("%s could not find flashnode %u for host no %u\n",
348262306a36Sopenharmony_ci		       __func__, idx, ev->u.set_flashnode.host_no);
348362306a36Sopenharmony_ci		err = -ENODEV;
348462306a36Sopenharmony_ci		goto put_host;
348562306a36Sopenharmony_ci	}
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci	dev = iscsi_find_flashnode_conn(fnode_sess);
348862306a36Sopenharmony_ci	if (!dev) {
348962306a36Sopenharmony_ci		err = -ENODEV;
349062306a36Sopenharmony_ci		goto put_sess;
349162306a36Sopenharmony_ci	}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	fnode_conn = iscsi_dev_to_flash_conn(dev);
349462306a36Sopenharmony_ci	err = transport->set_flashnode_param(fnode_sess, fnode_conn, data, len);
349562306a36Sopenharmony_ci	put_device(dev);
349662306a36Sopenharmony_ci
349762306a36Sopenharmony_ciput_sess:
349862306a36Sopenharmony_ci	put_device(&fnode_sess->dev);
349962306a36Sopenharmony_ci
350062306a36Sopenharmony_ciput_host:
350162306a36Sopenharmony_ci	scsi_host_put(shost);
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ciexit_set_fnode:
350462306a36Sopenharmony_ci	return err;
350562306a36Sopenharmony_ci}
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_cistatic int iscsi_new_flashnode(struct iscsi_transport *transport,
350862306a36Sopenharmony_ci			       struct iscsi_uevent *ev, uint32_t len)
350962306a36Sopenharmony_ci{
351062306a36Sopenharmony_ci	char *data = (char *)ev + sizeof(*ev);
351162306a36Sopenharmony_ci	struct Scsi_Host *shost;
351262306a36Sopenharmony_ci	int index;
351362306a36Sopenharmony_ci	int err = 0;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	if (!transport->new_flashnode) {
351662306a36Sopenharmony_ci		err = -ENOSYS;
351762306a36Sopenharmony_ci		goto exit_new_fnode;
351862306a36Sopenharmony_ci	}
351962306a36Sopenharmony_ci
352062306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.new_flashnode.host_no);
352162306a36Sopenharmony_ci	if (!shost) {
352262306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
352362306a36Sopenharmony_ci		       __func__, ev->u.new_flashnode.host_no);
352462306a36Sopenharmony_ci		err = -ENODEV;
352562306a36Sopenharmony_ci		goto put_host;
352662306a36Sopenharmony_ci	}
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	index = transport->new_flashnode(shost, data, len);
352962306a36Sopenharmony_ci
353062306a36Sopenharmony_ci	if (index >= 0)
353162306a36Sopenharmony_ci		ev->r.new_flashnode_ret.flashnode_idx = index;
353262306a36Sopenharmony_ci	else
353362306a36Sopenharmony_ci		err = -EIO;
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ciput_host:
353662306a36Sopenharmony_ci	scsi_host_put(shost);
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_ciexit_new_fnode:
353962306a36Sopenharmony_ci	return err;
354062306a36Sopenharmony_ci}
354162306a36Sopenharmony_ci
354262306a36Sopenharmony_cistatic int iscsi_del_flashnode(struct iscsi_transport *transport,
354362306a36Sopenharmony_ci			       struct iscsi_uevent *ev)
354462306a36Sopenharmony_ci{
354562306a36Sopenharmony_ci	struct Scsi_Host *shost;
354662306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess;
354762306a36Sopenharmony_ci	uint32_t idx;
354862306a36Sopenharmony_ci	int err = 0;
354962306a36Sopenharmony_ci
355062306a36Sopenharmony_ci	if (!transport->del_flashnode) {
355162306a36Sopenharmony_ci		err = -ENOSYS;
355262306a36Sopenharmony_ci		goto exit_del_fnode;
355362306a36Sopenharmony_ci	}
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.del_flashnode.host_no);
355662306a36Sopenharmony_ci	if (!shost) {
355762306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
355862306a36Sopenharmony_ci		       __func__, ev->u.del_flashnode.host_no);
355962306a36Sopenharmony_ci		err = -ENODEV;
356062306a36Sopenharmony_ci		goto put_host;
356162306a36Sopenharmony_ci	}
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci	idx = ev->u.del_flashnode.flashnode_idx;
356462306a36Sopenharmony_ci	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
356562306a36Sopenharmony_ci	if (!fnode_sess) {
356662306a36Sopenharmony_ci		pr_err("%s could not find flashnode %u for host no %u\n",
356762306a36Sopenharmony_ci		       __func__, idx, ev->u.del_flashnode.host_no);
356862306a36Sopenharmony_ci		err = -ENODEV;
356962306a36Sopenharmony_ci		goto put_host;
357062306a36Sopenharmony_ci	}
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	err = transport->del_flashnode(fnode_sess);
357362306a36Sopenharmony_ci	put_device(&fnode_sess->dev);
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ciput_host:
357662306a36Sopenharmony_ci	scsi_host_put(shost);
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ciexit_del_fnode:
357962306a36Sopenharmony_ci	return err;
358062306a36Sopenharmony_ci}
358162306a36Sopenharmony_ci
358262306a36Sopenharmony_cistatic int iscsi_login_flashnode(struct iscsi_transport *transport,
358362306a36Sopenharmony_ci				 struct iscsi_uevent *ev)
358462306a36Sopenharmony_ci{
358562306a36Sopenharmony_ci	struct Scsi_Host *shost;
358662306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess;
358762306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn;
358862306a36Sopenharmony_ci	struct device *dev;
358962306a36Sopenharmony_ci	uint32_t idx;
359062306a36Sopenharmony_ci	int err = 0;
359162306a36Sopenharmony_ci
359262306a36Sopenharmony_ci	if (!transport->login_flashnode) {
359362306a36Sopenharmony_ci		err = -ENOSYS;
359462306a36Sopenharmony_ci		goto exit_login_fnode;
359562306a36Sopenharmony_ci	}
359662306a36Sopenharmony_ci
359762306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.login_flashnode.host_no);
359862306a36Sopenharmony_ci	if (!shost) {
359962306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
360062306a36Sopenharmony_ci		       __func__, ev->u.login_flashnode.host_no);
360162306a36Sopenharmony_ci		err = -ENODEV;
360262306a36Sopenharmony_ci		goto put_host;
360362306a36Sopenharmony_ci	}
360462306a36Sopenharmony_ci
360562306a36Sopenharmony_ci	idx = ev->u.login_flashnode.flashnode_idx;
360662306a36Sopenharmony_ci	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
360762306a36Sopenharmony_ci	if (!fnode_sess) {
360862306a36Sopenharmony_ci		pr_err("%s could not find flashnode %u for host no %u\n",
360962306a36Sopenharmony_ci		       __func__, idx, ev->u.login_flashnode.host_no);
361062306a36Sopenharmony_ci		err = -ENODEV;
361162306a36Sopenharmony_ci		goto put_host;
361262306a36Sopenharmony_ci	}
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	dev = iscsi_find_flashnode_conn(fnode_sess);
361562306a36Sopenharmony_ci	if (!dev) {
361662306a36Sopenharmony_ci		err = -ENODEV;
361762306a36Sopenharmony_ci		goto put_sess;
361862306a36Sopenharmony_ci	}
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	fnode_conn = iscsi_dev_to_flash_conn(dev);
362162306a36Sopenharmony_ci	err = transport->login_flashnode(fnode_sess, fnode_conn);
362262306a36Sopenharmony_ci	put_device(dev);
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ciput_sess:
362562306a36Sopenharmony_ci	put_device(&fnode_sess->dev);
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ciput_host:
362862306a36Sopenharmony_ci	scsi_host_put(shost);
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ciexit_login_fnode:
363162306a36Sopenharmony_ci	return err;
363262306a36Sopenharmony_ci}
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_cistatic int iscsi_logout_flashnode(struct iscsi_transport *transport,
363562306a36Sopenharmony_ci				  struct iscsi_uevent *ev)
363662306a36Sopenharmony_ci{
363762306a36Sopenharmony_ci	struct Scsi_Host *shost;
363862306a36Sopenharmony_ci	struct iscsi_bus_flash_session *fnode_sess;
363962306a36Sopenharmony_ci	struct iscsi_bus_flash_conn *fnode_conn;
364062306a36Sopenharmony_ci	struct device *dev;
364162306a36Sopenharmony_ci	uint32_t idx;
364262306a36Sopenharmony_ci	int err = 0;
364362306a36Sopenharmony_ci
364462306a36Sopenharmony_ci	if (!transport->logout_flashnode) {
364562306a36Sopenharmony_ci		err = -ENOSYS;
364662306a36Sopenharmony_ci		goto exit_logout_fnode;
364762306a36Sopenharmony_ci	}
364862306a36Sopenharmony_ci
364962306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.logout_flashnode.host_no);
365062306a36Sopenharmony_ci	if (!shost) {
365162306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
365262306a36Sopenharmony_ci		       __func__, ev->u.logout_flashnode.host_no);
365362306a36Sopenharmony_ci		err = -ENODEV;
365462306a36Sopenharmony_ci		goto put_host;
365562306a36Sopenharmony_ci	}
365662306a36Sopenharmony_ci
365762306a36Sopenharmony_ci	idx = ev->u.logout_flashnode.flashnode_idx;
365862306a36Sopenharmony_ci	fnode_sess = iscsi_get_flashnode_by_index(shost, idx);
365962306a36Sopenharmony_ci	if (!fnode_sess) {
366062306a36Sopenharmony_ci		pr_err("%s could not find flashnode %u for host no %u\n",
366162306a36Sopenharmony_ci		       __func__, idx, ev->u.logout_flashnode.host_no);
366262306a36Sopenharmony_ci		err = -ENODEV;
366362306a36Sopenharmony_ci		goto put_host;
366462306a36Sopenharmony_ci	}
366562306a36Sopenharmony_ci
366662306a36Sopenharmony_ci	dev = iscsi_find_flashnode_conn(fnode_sess);
366762306a36Sopenharmony_ci	if (!dev) {
366862306a36Sopenharmony_ci		err = -ENODEV;
366962306a36Sopenharmony_ci		goto put_sess;
367062306a36Sopenharmony_ci	}
367162306a36Sopenharmony_ci
367262306a36Sopenharmony_ci	fnode_conn = iscsi_dev_to_flash_conn(dev);
367362306a36Sopenharmony_ci
367462306a36Sopenharmony_ci	err = transport->logout_flashnode(fnode_sess, fnode_conn);
367562306a36Sopenharmony_ci	put_device(dev);
367662306a36Sopenharmony_ci
367762306a36Sopenharmony_ciput_sess:
367862306a36Sopenharmony_ci	put_device(&fnode_sess->dev);
367962306a36Sopenharmony_ci
368062306a36Sopenharmony_ciput_host:
368162306a36Sopenharmony_ci	scsi_host_put(shost);
368262306a36Sopenharmony_ci
368362306a36Sopenharmony_ciexit_logout_fnode:
368462306a36Sopenharmony_ci	return err;
368562306a36Sopenharmony_ci}
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_cistatic int iscsi_logout_flashnode_sid(struct iscsi_transport *transport,
368862306a36Sopenharmony_ci				      struct iscsi_uevent *ev)
368962306a36Sopenharmony_ci{
369062306a36Sopenharmony_ci	struct Scsi_Host *shost;
369162306a36Sopenharmony_ci	struct iscsi_cls_session *session;
369262306a36Sopenharmony_ci	int err = 0;
369362306a36Sopenharmony_ci
369462306a36Sopenharmony_ci	if (!transport->logout_flashnode_sid) {
369562306a36Sopenharmony_ci		err = -ENOSYS;
369662306a36Sopenharmony_ci		goto exit_logout_sid;
369762306a36Sopenharmony_ci	}
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.logout_flashnode_sid.host_no);
370062306a36Sopenharmony_ci	if (!shost) {
370162306a36Sopenharmony_ci		pr_err("%s could not find host no %u\n",
370262306a36Sopenharmony_ci		       __func__, ev->u.logout_flashnode.host_no);
370362306a36Sopenharmony_ci		err = -ENODEV;
370462306a36Sopenharmony_ci		goto put_host;
370562306a36Sopenharmony_ci	}
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	session = iscsi_session_lookup(ev->u.logout_flashnode_sid.sid);
370862306a36Sopenharmony_ci	if (!session) {
370962306a36Sopenharmony_ci		pr_err("%s could not find session id %u\n",
371062306a36Sopenharmony_ci		       __func__, ev->u.logout_flashnode_sid.sid);
371162306a36Sopenharmony_ci		err = -EINVAL;
371262306a36Sopenharmony_ci		goto put_host;
371362306a36Sopenharmony_ci	}
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	err = transport->logout_flashnode_sid(session);
371662306a36Sopenharmony_ci
371762306a36Sopenharmony_ciput_host:
371862306a36Sopenharmony_ci	scsi_host_put(shost);
371962306a36Sopenharmony_ci
372062306a36Sopenharmony_ciexit_logout_sid:
372162306a36Sopenharmony_ci	return err;
372262306a36Sopenharmony_ci}
372362306a36Sopenharmony_ci
372462306a36Sopenharmony_cistatic int
372562306a36Sopenharmony_ciiscsi_get_host_stats(struct iscsi_transport *transport, struct nlmsghdr *nlh)
372662306a36Sopenharmony_ci{
372762306a36Sopenharmony_ci	struct iscsi_uevent *ev = nlmsg_data(nlh);
372862306a36Sopenharmony_ci	struct Scsi_Host *shost = NULL;
372962306a36Sopenharmony_ci	struct iscsi_internal *priv;
373062306a36Sopenharmony_ci	struct sk_buff *skbhost_stats;
373162306a36Sopenharmony_ci	struct nlmsghdr *nlhhost_stats;
373262306a36Sopenharmony_ci	struct iscsi_uevent *evhost_stats;
373362306a36Sopenharmony_ci	int host_stats_size = 0;
373462306a36Sopenharmony_ci	int len, err = 0;
373562306a36Sopenharmony_ci	char *buf;
373662306a36Sopenharmony_ci
373762306a36Sopenharmony_ci	if (!transport->get_host_stats)
373862306a36Sopenharmony_ci		return -ENOSYS;
373962306a36Sopenharmony_ci
374062306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(transport);
374162306a36Sopenharmony_ci	if (!priv)
374262306a36Sopenharmony_ci		return -EINVAL;
374362306a36Sopenharmony_ci
374462306a36Sopenharmony_ci	host_stats_size = sizeof(struct iscsi_offload_host_stats);
374562306a36Sopenharmony_ci	len = nlmsg_total_size(sizeof(*ev) + host_stats_size);
374662306a36Sopenharmony_ci
374762306a36Sopenharmony_ci	shost = scsi_host_lookup(ev->u.get_host_stats.host_no);
374862306a36Sopenharmony_ci	if (!shost) {
374962306a36Sopenharmony_ci		pr_err("%s: failed. Could not find host no %u\n",
375062306a36Sopenharmony_ci		       __func__, ev->u.get_host_stats.host_no);
375162306a36Sopenharmony_ci		return -ENODEV;
375262306a36Sopenharmony_ci	}
375362306a36Sopenharmony_ci
375462306a36Sopenharmony_ci	do {
375562306a36Sopenharmony_ci		int actual_size;
375662306a36Sopenharmony_ci
375762306a36Sopenharmony_ci		skbhost_stats = alloc_skb(len, GFP_KERNEL);
375862306a36Sopenharmony_ci		if (!skbhost_stats) {
375962306a36Sopenharmony_ci			pr_err("cannot deliver host stats: OOM\n");
376062306a36Sopenharmony_ci			err = -ENOMEM;
376162306a36Sopenharmony_ci			goto exit_host_stats;
376262306a36Sopenharmony_ci		}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ci		nlhhost_stats = __nlmsg_put(skbhost_stats, 0, 0, 0,
376562306a36Sopenharmony_ci				      (len - sizeof(*nlhhost_stats)), 0);
376662306a36Sopenharmony_ci		evhost_stats = nlmsg_data(nlhhost_stats);
376762306a36Sopenharmony_ci		memset(evhost_stats, 0, sizeof(*evhost_stats));
376862306a36Sopenharmony_ci		evhost_stats->transport_handle = iscsi_handle(transport);
376962306a36Sopenharmony_ci		evhost_stats->type = nlh->nlmsg_type;
377062306a36Sopenharmony_ci		evhost_stats->u.get_host_stats.host_no =
377162306a36Sopenharmony_ci					ev->u.get_host_stats.host_no;
377262306a36Sopenharmony_ci		buf = (char *)evhost_stats + sizeof(*evhost_stats);
377362306a36Sopenharmony_ci		memset(buf, 0, host_stats_size);
377462306a36Sopenharmony_ci
377562306a36Sopenharmony_ci		err = transport->get_host_stats(shost, buf, host_stats_size);
377662306a36Sopenharmony_ci		if (err) {
377762306a36Sopenharmony_ci			kfree_skb(skbhost_stats);
377862306a36Sopenharmony_ci			goto exit_host_stats;
377962306a36Sopenharmony_ci		}
378062306a36Sopenharmony_ci
378162306a36Sopenharmony_ci		actual_size = nlmsg_total_size(sizeof(*ev) + host_stats_size);
378262306a36Sopenharmony_ci		skb_trim(skbhost_stats, NLMSG_ALIGN(actual_size));
378362306a36Sopenharmony_ci		nlhhost_stats->nlmsg_len = actual_size;
378462306a36Sopenharmony_ci
378562306a36Sopenharmony_ci		err = iscsi_multicast_skb(skbhost_stats, ISCSI_NL_GRP_ISCSID,
378662306a36Sopenharmony_ci					  GFP_KERNEL);
378762306a36Sopenharmony_ci	} while (err < 0 && err != -ECONNREFUSED);
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ciexit_host_stats:
379062306a36Sopenharmony_ci	scsi_host_put(shost);
379162306a36Sopenharmony_ci	return err;
379262306a36Sopenharmony_ci}
379362306a36Sopenharmony_ci
379462306a36Sopenharmony_cistatic int iscsi_if_transport_conn(struct iscsi_transport *transport,
379562306a36Sopenharmony_ci				   struct nlmsghdr *nlh, u32 pdu_len)
379662306a36Sopenharmony_ci{
379762306a36Sopenharmony_ci	struct iscsi_uevent *ev = nlmsg_data(nlh);
379862306a36Sopenharmony_ci	struct iscsi_cls_session *session;
379962306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = NULL;
380062306a36Sopenharmony_ci	struct iscsi_endpoint *ep;
380162306a36Sopenharmony_ci	int err = 0;
380262306a36Sopenharmony_ci
380362306a36Sopenharmony_ci	switch (nlh->nlmsg_type) {
380462306a36Sopenharmony_ci	case ISCSI_UEVENT_CREATE_CONN:
380562306a36Sopenharmony_ci		return iscsi_if_create_conn(transport, ev);
380662306a36Sopenharmony_ci	case ISCSI_UEVENT_DESTROY_CONN:
380762306a36Sopenharmony_ci		return iscsi_if_destroy_conn(transport, ev);
380862306a36Sopenharmony_ci	case ISCSI_UEVENT_STOP_CONN:
380962306a36Sopenharmony_ci		conn = iscsi_conn_lookup(ev->u.stop_conn.sid,
381062306a36Sopenharmony_ci					 ev->u.stop_conn.cid);
381162306a36Sopenharmony_ci		if (!conn)
381262306a36Sopenharmony_ci			return -EINVAL;
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci		return iscsi_if_stop_conn(conn, ev->u.stop_conn.flag);
381562306a36Sopenharmony_ci	}
381662306a36Sopenharmony_ci
381762306a36Sopenharmony_ci	/*
381862306a36Sopenharmony_ci	 * The following cmds need to be run under the ep_mutex so in kernel
381962306a36Sopenharmony_ci	 * conn cleanup (ep_disconnect + unbind and conn) is not done while
382062306a36Sopenharmony_ci	 * these are running. They also must not run if we have just run a conn
382162306a36Sopenharmony_ci	 * cleanup because they would set the state in a way that might allow
382262306a36Sopenharmony_ci	 * IO or send IO themselves.
382362306a36Sopenharmony_ci	 */
382462306a36Sopenharmony_ci	switch (nlh->nlmsg_type) {
382562306a36Sopenharmony_ci	case ISCSI_UEVENT_START_CONN:
382662306a36Sopenharmony_ci		conn = iscsi_conn_lookup(ev->u.start_conn.sid,
382762306a36Sopenharmony_ci					 ev->u.start_conn.cid);
382862306a36Sopenharmony_ci		break;
382962306a36Sopenharmony_ci	case ISCSI_UEVENT_BIND_CONN:
383062306a36Sopenharmony_ci		conn = iscsi_conn_lookup(ev->u.b_conn.sid, ev->u.b_conn.cid);
383162306a36Sopenharmony_ci		break;
383262306a36Sopenharmony_ci	case ISCSI_UEVENT_SEND_PDU:
383362306a36Sopenharmony_ci		conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
383462306a36Sopenharmony_ci		break;
383562306a36Sopenharmony_ci	}
383662306a36Sopenharmony_ci
383762306a36Sopenharmony_ci	if (!conn)
383862306a36Sopenharmony_ci		return -EINVAL;
383962306a36Sopenharmony_ci
384062306a36Sopenharmony_ci	mutex_lock(&conn->ep_mutex);
384162306a36Sopenharmony_ci	spin_lock_irq(&conn->lock);
384262306a36Sopenharmony_ci	if (test_bit(ISCSI_CLS_CONN_BIT_CLEANUP, &conn->flags)) {
384362306a36Sopenharmony_ci		spin_unlock_irq(&conn->lock);
384462306a36Sopenharmony_ci		mutex_unlock(&conn->ep_mutex);
384562306a36Sopenharmony_ci		ev->r.retcode = -ENOTCONN;
384662306a36Sopenharmony_ci		return 0;
384762306a36Sopenharmony_ci	}
384862306a36Sopenharmony_ci	spin_unlock_irq(&conn->lock);
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci	switch (nlh->nlmsg_type) {
385162306a36Sopenharmony_ci	case ISCSI_UEVENT_BIND_CONN:
385262306a36Sopenharmony_ci		session = iscsi_session_lookup(ev->u.b_conn.sid);
385362306a36Sopenharmony_ci		if (!session) {
385462306a36Sopenharmony_ci			err = -EINVAL;
385562306a36Sopenharmony_ci			break;
385662306a36Sopenharmony_ci		}
385762306a36Sopenharmony_ci
385862306a36Sopenharmony_ci		ev->r.retcode =	transport->bind_conn(session, conn,
385962306a36Sopenharmony_ci						ev->u.b_conn.transport_eph,
386062306a36Sopenharmony_ci						ev->u.b_conn.is_leading);
386162306a36Sopenharmony_ci		if (!ev->r.retcode)
386262306a36Sopenharmony_ci			WRITE_ONCE(conn->state, ISCSI_CONN_BOUND);
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci		if (ev->r.retcode || !transport->ep_connect)
386562306a36Sopenharmony_ci			break;
386662306a36Sopenharmony_ci
386762306a36Sopenharmony_ci		ep = iscsi_lookup_endpoint(ev->u.b_conn.transport_eph);
386862306a36Sopenharmony_ci		if (ep) {
386962306a36Sopenharmony_ci			ep->conn = conn;
387062306a36Sopenharmony_ci			conn->ep = ep;
387162306a36Sopenharmony_ci			iscsi_put_endpoint(ep);
387262306a36Sopenharmony_ci		} else {
387362306a36Sopenharmony_ci			err = -ENOTCONN;
387462306a36Sopenharmony_ci			iscsi_cls_conn_printk(KERN_ERR, conn,
387562306a36Sopenharmony_ci					      "Could not set ep conn binding\n");
387662306a36Sopenharmony_ci		}
387762306a36Sopenharmony_ci		break;
387862306a36Sopenharmony_ci	case ISCSI_UEVENT_START_CONN:
387962306a36Sopenharmony_ci		ev->r.retcode = transport->start_conn(conn);
388062306a36Sopenharmony_ci		if (!ev->r.retcode)
388162306a36Sopenharmony_ci			WRITE_ONCE(conn->state, ISCSI_CONN_UP);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci		break;
388462306a36Sopenharmony_ci	case ISCSI_UEVENT_SEND_PDU:
388562306a36Sopenharmony_ci		if ((ev->u.send_pdu.hdr_size > pdu_len) ||
388662306a36Sopenharmony_ci		    (ev->u.send_pdu.data_size > (pdu_len - ev->u.send_pdu.hdr_size))) {
388762306a36Sopenharmony_ci			err = -EINVAL;
388862306a36Sopenharmony_ci			break;
388962306a36Sopenharmony_ci		}
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci		ev->r.retcode =	transport->send_pdu(conn,
389262306a36Sopenharmony_ci				(struct iscsi_hdr *)((char *)ev + sizeof(*ev)),
389362306a36Sopenharmony_ci				(char *)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
389462306a36Sopenharmony_ci				ev->u.send_pdu.data_size);
389562306a36Sopenharmony_ci		break;
389662306a36Sopenharmony_ci	default:
389762306a36Sopenharmony_ci		err = -ENOSYS;
389862306a36Sopenharmony_ci	}
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	mutex_unlock(&conn->ep_mutex);
390162306a36Sopenharmony_ci	return err;
390262306a36Sopenharmony_ci}
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_cistatic int
390562306a36Sopenharmony_ciiscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
390662306a36Sopenharmony_ci{
390762306a36Sopenharmony_ci	int err = 0;
390862306a36Sopenharmony_ci	u32 portid;
390962306a36Sopenharmony_ci	struct iscsi_uevent *ev = nlmsg_data(nlh);
391062306a36Sopenharmony_ci	struct iscsi_transport *transport = NULL;
391162306a36Sopenharmony_ci	struct iscsi_internal *priv;
391262306a36Sopenharmony_ci	struct iscsi_cls_session *session;
391362306a36Sopenharmony_ci	struct iscsi_endpoint *ep = NULL;
391462306a36Sopenharmony_ci	u32 rlen;
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci	if (!netlink_capable(skb, CAP_SYS_ADMIN))
391762306a36Sopenharmony_ci		return -EPERM;
391862306a36Sopenharmony_ci
391962306a36Sopenharmony_ci	if (nlh->nlmsg_type == ISCSI_UEVENT_PATH_UPDATE)
392062306a36Sopenharmony_ci		*group = ISCSI_NL_GRP_UIP;
392162306a36Sopenharmony_ci	else
392262306a36Sopenharmony_ci		*group = ISCSI_NL_GRP_ISCSID;
392362306a36Sopenharmony_ci
392462306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(iscsi_ptr(ev->transport_handle));
392562306a36Sopenharmony_ci	if (!priv)
392662306a36Sopenharmony_ci		return -EINVAL;
392762306a36Sopenharmony_ci	transport = priv->iscsi_transport;
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci	if (!try_module_get(transport->owner))
393062306a36Sopenharmony_ci		return -EINVAL;
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci	portid = NETLINK_CB(skb).portid;
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci	/*
393562306a36Sopenharmony_ci	 * Even though the remaining payload may not be regarded as nlattr,
393662306a36Sopenharmony_ci	 * (like address or something else), calculate the remaining length
393762306a36Sopenharmony_ci	 * here to ease following length checks.
393862306a36Sopenharmony_ci	 */
393962306a36Sopenharmony_ci	rlen = nlmsg_attrlen(nlh, sizeof(*ev));
394062306a36Sopenharmony_ci
394162306a36Sopenharmony_ci	switch (nlh->nlmsg_type) {
394262306a36Sopenharmony_ci	case ISCSI_UEVENT_CREATE_SESSION:
394362306a36Sopenharmony_ci		err = iscsi_if_create_session(priv, ep, ev,
394462306a36Sopenharmony_ci					      portid,
394562306a36Sopenharmony_ci					      ev->u.c_session.initial_cmdsn,
394662306a36Sopenharmony_ci					      ev->u.c_session.cmds_max,
394762306a36Sopenharmony_ci					      ev->u.c_session.queue_depth);
394862306a36Sopenharmony_ci		break;
394962306a36Sopenharmony_ci	case ISCSI_UEVENT_CREATE_BOUND_SESSION:
395062306a36Sopenharmony_ci		ep = iscsi_lookup_endpoint(ev->u.c_bound_session.ep_handle);
395162306a36Sopenharmony_ci		if (!ep) {
395262306a36Sopenharmony_ci			err = -EINVAL;
395362306a36Sopenharmony_ci			break;
395462306a36Sopenharmony_ci		}
395562306a36Sopenharmony_ci
395662306a36Sopenharmony_ci		err = iscsi_if_create_session(priv, ep, ev,
395762306a36Sopenharmony_ci					portid,
395862306a36Sopenharmony_ci					ev->u.c_bound_session.initial_cmdsn,
395962306a36Sopenharmony_ci					ev->u.c_bound_session.cmds_max,
396062306a36Sopenharmony_ci					ev->u.c_bound_session.queue_depth);
396162306a36Sopenharmony_ci		iscsi_put_endpoint(ep);
396262306a36Sopenharmony_ci		break;
396362306a36Sopenharmony_ci	case ISCSI_UEVENT_DESTROY_SESSION:
396462306a36Sopenharmony_ci		session = iscsi_session_lookup(ev->u.d_session.sid);
396562306a36Sopenharmony_ci		if (!session)
396662306a36Sopenharmony_ci			err = -EINVAL;
396762306a36Sopenharmony_ci		else if (iscsi_session_has_conns(ev->u.d_session.sid))
396862306a36Sopenharmony_ci			err = -EBUSY;
396962306a36Sopenharmony_ci		else
397062306a36Sopenharmony_ci			transport->destroy_session(session);
397162306a36Sopenharmony_ci		break;
397262306a36Sopenharmony_ci	case ISCSI_UEVENT_DESTROY_SESSION_ASYNC:
397362306a36Sopenharmony_ci		session = iscsi_session_lookup(ev->u.d_session.sid);
397462306a36Sopenharmony_ci		if (!session)
397562306a36Sopenharmony_ci			err = -EINVAL;
397662306a36Sopenharmony_ci		else if (iscsi_session_has_conns(ev->u.d_session.sid))
397762306a36Sopenharmony_ci			err = -EBUSY;
397862306a36Sopenharmony_ci		else {
397962306a36Sopenharmony_ci			unsigned long flags;
398062306a36Sopenharmony_ci
398162306a36Sopenharmony_ci			/* Prevent this session from being found again */
398262306a36Sopenharmony_ci			spin_lock_irqsave(&sesslock, flags);
398362306a36Sopenharmony_ci			list_del_init(&session->sess_list);
398462306a36Sopenharmony_ci			spin_unlock_irqrestore(&sesslock, flags);
398562306a36Sopenharmony_ci
398662306a36Sopenharmony_ci			queue_work(system_unbound_wq, &session->destroy_work);
398762306a36Sopenharmony_ci		}
398862306a36Sopenharmony_ci		break;
398962306a36Sopenharmony_ci	case ISCSI_UEVENT_UNBIND_SESSION:
399062306a36Sopenharmony_ci		session = iscsi_session_lookup(ev->u.d_session.sid);
399162306a36Sopenharmony_ci		if (session)
399262306a36Sopenharmony_ci			queue_work(session->workq, &session->unbind_work);
399362306a36Sopenharmony_ci		else
399462306a36Sopenharmony_ci			err = -EINVAL;
399562306a36Sopenharmony_ci		break;
399662306a36Sopenharmony_ci	case ISCSI_UEVENT_SET_PARAM:
399762306a36Sopenharmony_ci		err = iscsi_if_set_param(transport, ev, rlen);
399862306a36Sopenharmony_ci		break;
399962306a36Sopenharmony_ci	case ISCSI_UEVENT_CREATE_CONN:
400062306a36Sopenharmony_ci	case ISCSI_UEVENT_DESTROY_CONN:
400162306a36Sopenharmony_ci	case ISCSI_UEVENT_STOP_CONN:
400262306a36Sopenharmony_ci	case ISCSI_UEVENT_START_CONN:
400362306a36Sopenharmony_ci	case ISCSI_UEVENT_BIND_CONN:
400462306a36Sopenharmony_ci	case ISCSI_UEVENT_SEND_PDU:
400562306a36Sopenharmony_ci		err = iscsi_if_transport_conn(transport, nlh, rlen);
400662306a36Sopenharmony_ci		break;
400762306a36Sopenharmony_ci	case ISCSI_UEVENT_GET_STATS:
400862306a36Sopenharmony_ci		err = iscsi_if_get_stats(transport, nlh);
400962306a36Sopenharmony_ci		break;
401062306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT:
401162306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_POLL:
401262306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_DISCONNECT:
401362306a36Sopenharmony_ci	case ISCSI_UEVENT_TRANSPORT_EP_CONNECT_THROUGH_HOST:
401462306a36Sopenharmony_ci		err = iscsi_if_transport_ep(transport, ev, nlh->nlmsg_type, rlen);
401562306a36Sopenharmony_ci		break;
401662306a36Sopenharmony_ci	case ISCSI_UEVENT_TGT_DSCVR:
401762306a36Sopenharmony_ci		err = iscsi_tgt_dscvr(transport, ev, rlen);
401862306a36Sopenharmony_ci		break;
401962306a36Sopenharmony_ci	case ISCSI_UEVENT_SET_HOST_PARAM:
402062306a36Sopenharmony_ci		err = iscsi_set_host_param(transport, ev, rlen);
402162306a36Sopenharmony_ci		break;
402262306a36Sopenharmony_ci	case ISCSI_UEVENT_PATH_UPDATE:
402362306a36Sopenharmony_ci		err = iscsi_set_path(transport, ev, rlen);
402462306a36Sopenharmony_ci		break;
402562306a36Sopenharmony_ci	case ISCSI_UEVENT_SET_IFACE_PARAMS:
402662306a36Sopenharmony_ci		err = iscsi_set_iface_params(transport, ev, rlen);
402762306a36Sopenharmony_ci		break;
402862306a36Sopenharmony_ci	case ISCSI_UEVENT_PING:
402962306a36Sopenharmony_ci		err = iscsi_send_ping(transport, ev, rlen);
403062306a36Sopenharmony_ci		break;
403162306a36Sopenharmony_ci	case ISCSI_UEVENT_GET_CHAP:
403262306a36Sopenharmony_ci		err = iscsi_get_chap(transport, nlh);
403362306a36Sopenharmony_ci		break;
403462306a36Sopenharmony_ci	case ISCSI_UEVENT_DELETE_CHAP:
403562306a36Sopenharmony_ci		err = iscsi_delete_chap(transport, ev);
403662306a36Sopenharmony_ci		break;
403762306a36Sopenharmony_ci	case ISCSI_UEVENT_SET_FLASHNODE_PARAMS:
403862306a36Sopenharmony_ci		err = iscsi_set_flashnode_param(transport, ev, rlen);
403962306a36Sopenharmony_ci		break;
404062306a36Sopenharmony_ci	case ISCSI_UEVENT_NEW_FLASHNODE:
404162306a36Sopenharmony_ci		err = iscsi_new_flashnode(transport, ev, rlen);
404262306a36Sopenharmony_ci		break;
404362306a36Sopenharmony_ci	case ISCSI_UEVENT_DEL_FLASHNODE:
404462306a36Sopenharmony_ci		err = iscsi_del_flashnode(transport, ev);
404562306a36Sopenharmony_ci		break;
404662306a36Sopenharmony_ci	case ISCSI_UEVENT_LOGIN_FLASHNODE:
404762306a36Sopenharmony_ci		err = iscsi_login_flashnode(transport, ev);
404862306a36Sopenharmony_ci		break;
404962306a36Sopenharmony_ci	case ISCSI_UEVENT_LOGOUT_FLASHNODE:
405062306a36Sopenharmony_ci		err = iscsi_logout_flashnode(transport, ev);
405162306a36Sopenharmony_ci		break;
405262306a36Sopenharmony_ci	case ISCSI_UEVENT_LOGOUT_FLASHNODE_SID:
405362306a36Sopenharmony_ci		err = iscsi_logout_flashnode_sid(transport, ev);
405462306a36Sopenharmony_ci		break;
405562306a36Sopenharmony_ci	case ISCSI_UEVENT_SET_CHAP:
405662306a36Sopenharmony_ci		err = iscsi_set_chap(transport, ev, rlen);
405762306a36Sopenharmony_ci		break;
405862306a36Sopenharmony_ci	case ISCSI_UEVENT_GET_HOST_STATS:
405962306a36Sopenharmony_ci		err = iscsi_get_host_stats(transport, nlh);
406062306a36Sopenharmony_ci		break;
406162306a36Sopenharmony_ci	default:
406262306a36Sopenharmony_ci		err = -ENOSYS;
406362306a36Sopenharmony_ci		break;
406462306a36Sopenharmony_ci	}
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_ci	module_put(transport->owner);
406762306a36Sopenharmony_ci	return err;
406862306a36Sopenharmony_ci}
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci/*
407162306a36Sopenharmony_ci * Get message from skb.  Each message is processed by iscsi_if_recv_msg.
407262306a36Sopenharmony_ci * Malformed skbs with wrong lengths or invalid creds are not processed.
407362306a36Sopenharmony_ci */
407462306a36Sopenharmony_cistatic void
407562306a36Sopenharmony_ciiscsi_if_rx(struct sk_buff *skb)
407662306a36Sopenharmony_ci{
407762306a36Sopenharmony_ci	u32 portid = NETLINK_CB(skb).portid;
407862306a36Sopenharmony_ci
407962306a36Sopenharmony_ci	mutex_lock(&rx_queue_mutex);
408062306a36Sopenharmony_ci	while (skb->len >= NLMSG_HDRLEN) {
408162306a36Sopenharmony_ci		int err;
408262306a36Sopenharmony_ci		uint32_t rlen;
408362306a36Sopenharmony_ci		struct nlmsghdr	*nlh;
408462306a36Sopenharmony_ci		struct iscsi_uevent *ev;
408562306a36Sopenharmony_ci		uint32_t group;
408662306a36Sopenharmony_ci		int retries = ISCSI_SEND_MAX_ALLOWED;
408762306a36Sopenharmony_ci
408862306a36Sopenharmony_ci		nlh = nlmsg_hdr(skb);
408962306a36Sopenharmony_ci		if (nlh->nlmsg_len < sizeof(*nlh) + sizeof(*ev) ||
409062306a36Sopenharmony_ci		    skb->len < nlh->nlmsg_len) {
409162306a36Sopenharmony_ci			break;
409262306a36Sopenharmony_ci		}
409362306a36Sopenharmony_ci
409462306a36Sopenharmony_ci		ev = nlmsg_data(nlh);
409562306a36Sopenharmony_ci		rlen = NLMSG_ALIGN(nlh->nlmsg_len);
409662306a36Sopenharmony_ci		if (rlen > skb->len)
409762306a36Sopenharmony_ci			rlen = skb->len;
409862306a36Sopenharmony_ci
409962306a36Sopenharmony_ci		err = iscsi_if_recv_msg(skb, nlh, &group);
410062306a36Sopenharmony_ci		if (err) {
410162306a36Sopenharmony_ci			ev->type = ISCSI_KEVENT_IF_ERROR;
410262306a36Sopenharmony_ci			ev->iferror = err;
410362306a36Sopenharmony_ci		}
410462306a36Sopenharmony_ci		do {
410562306a36Sopenharmony_ci			/*
410662306a36Sopenharmony_ci			 * special case for GET_STATS:
410762306a36Sopenharmony_ci			 * on success - sending reply and stats from
410862306a36Sopenharmony_ci			 * inside of if_recv_msg(),
410962306a36Sopenharmony_ci			 * on error - fall through.
411062306a36Sopenharmony_ci			 */
411162306a36Sopenharmony_ci			if (ev->type == ISCSI_UEVENT_GET_STATS && !err)
411262306a36Sopenharmony_ci				break;
411362306a36Sopenharmony_ci			if (ev->type == ISCSI_UEVENT_GET_CHAP && !err)
411462306a36Sopenharmony_ci				break;
411562306a36Sopenharmony_ci			err = iscsi_if_send_reply(portid, nlh->nlmsg_type,
411662306a36Sopenharmony_ci						  ev, sizeof(*ev));
411762306a36Sopenharmony_ci			if (err == -EAGAIN && --retries < 0) {
411862306a36Sopenharmony_ci				printk(KERN_WARNING "Send reply failed, error %d\n", err);
411962306a36Sopenharmony_ci				break;
412062306a36Sopenharmony_ci			}
412162306a36Sopenharmony_ci		} while (err < 0 && err != -ECONNREFUSED && err != -ESRCH);
412262306a36Sopenharmony_ci		skb_pull(skb, rlen);
412362306a36Sopenharmony_ci	}
412462306a36Sopenharmony_ci	mutex_unlock(&rx_queue_mutex);
412562306a36Sopenharmony_ci}
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci#define ISCSI_CLASS_ATTR(_prefix,_name,_mode,_show,_store)		\
412862306a36Sopenharmony_cistruct device_attribute dev_attr_##_prefix##_##_name =	\
412962306a36Sopenharmony_ci	__ATTR(_name,_mode,_show,_store)
413062306a36Sopenharmony_ci
413162306a36Sopenharmony_ci/*
413262306a36Sopenharmony_ci * iSCSI connection attrs
413362306a36Sopenharmony_ci */
413462306a36Sopenharmony_ci#define iscsi_conn_attr_show(param)					\
413562306a36Sopenharmony_cistatic ssize_t								\
413662306a36Sopenharmony_cishow_conn_param_##param(struct device *dev, 				\
413762306a36Sopenharmony_ci			struct device_attribute *attr, char *buf)	\
413862306a36Sopenharmony_ci{									\
413962306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);	\
414062306a36Sopenharmony_ci	struct iscsi_transport *t = conn->transport;			\
414162306a36Sopenharmony_ci	return t->get_conn_param(conn, param, buf);			\
414262306a36Sopenharmony_ci}
414362306a36Sopenharmony_ci
414462306a36Sopenharmony_ci#define iscsi_conn_attr(field, param)					\
414562306a36Sopenharmony_ci	iscsi_conn_attr_show(param)					\
414662306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(conn, field, S_IRUGO, show_conn_param_##param,	\
414762306a36Sopenharmony_ci			NULL);
414862306a36Sopenharmony_ci
414962306a36Sopenharmony_ciiscsi_conn_attr(max_recv_dlength, ISCSI_PARAM_MAX_RECV_DLENGTH);
415062306a36Sopenharmony_ciiscsi_conn_attr(max_xmit_dlength, ISCSI_PARAM_MAX_XMIT_DLENGTH);
415162306a36Sopenharmony_ciiscsi_conn_attr(header_digest, ISCSI_PARAM_HDRDGST_EN);
415262306a36Sopenharmony_ciiscsi_conn_attr(data_digest, ISCSI_PARAM_DATADGST_EN);
415362306a36Sopenharmony_ciiscsi_conn_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN);
415462306a36Sopenharmony_ciiscsi_conn_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN);
415562306a36Sopenharmony_ciiscsi_conn_attr(persistent_port, ISCSI_PARAM_PERSISTENT_PORT);
415662306a36Sopenharmony_ciiscsi_conn_attr(exp_statsn, ISCSI_PARAM_EXP_STATSN);
415762306a36Sopenharmony_ciiscsi_conn_attr(persistent_address, ISCSI_PARAM_PERSISTENT_ADDRESS);
415862306a36Sopenharmony_ciiscsi_conn_attr(ping_tmo, ISCSI_PARAM_PING_TMO);
415962306a36Sopenharmony_ciiscsi_conn_attr(recv_tmo, ISCSI_PARAM_RECV_TMO);
416062306a36Sopenharmony_ciiscsi_conn_attr(local_port, ISCSI_PARAM_LOCAL_PORT);
416162306a36Sopenharmony_ciiscsi_conn_attr(statsn, ISCSI_PARAM_STATSN);
416262306a36Sopenharmony_ciiscsi_conn_attr(keepalive_tmo, ISCSI_PARAM_KEEPALIVE_TMO);
416362306a36Sopenharmony_ciiscsi_conn_attr(max_segment_size, ISCSI_PARAM_MAX_SEGMENT_SIZE);
416462306a36Sopenharmony_ciiscsi_conn_attr(tcp_timestamp_stat, ISCSI_PARAM_TCP_TIMESTAMP_STAT);
416562306a36Sopenharmony_ciiscsi_conn_attr(tcp_wsf_disable, ISCSI_PARAM_TCP_WSF_DISABLE);
416662306a36Sopenharmony_ciiscsi_conn_attr(tcp_nagle_disable, ISCSI_PARAM_TCP_NAGLE_DISABLE);
416762306a36Sopenharmony_ciiscsi_conn_attr(tcp_timer_scale, ISCSI_PARAM_TCP_TIMER_SCALE);
416862306a36Sopenharmony_ciiscsi_conn_attr(tcp_timestamp_enable, ISCSI_PARAM_TCP_TIMESTAMP_EN);
416962306a36Sopenharmony_ciiscsi_conn_attr(fragment_disable, ISCSI_PARAM_IP_FRAGMENT_DISABLE);
417062306a36Sopenharmony_ciiscsi_conn_attr(ipv4_tos, ISCSI_PARAM_IPV4_TOS);
417162306a36Sopenharmony_ciiscsi_conn_attr(ipv6_traffic_class, ISCSI_PARAM_IPV6_TC);
417262306a36Sopenharmony_ciiscsi_conn_attr(ipv6_flow_label, ISCSI_PARAM_IPV6_FLOW_LABEL);
417362306a36Sopenharmony_ciiscsi_conn_attr(is_fw_assigned_ipv6, ISCSI_PARAM_IS_FW_ASSIGNED_IPV6);
417462306a36Sopenharmony_ciiscsi_conn_attr(tcp_xmit_wsf, ISCSI_PARAM_TCP_XMIT_WSF);
417562306a36Sopenharmony_ciiscsi_conn_attr(tcp_recv_wsf, ISCSI_PARAM_TCP_RECV_WSF);
417662306a36Sopenharmony_ciiscsi_conn_attr(local_ipaddr, ISCSI_PARAM_LOCAL_IPADDR);
417762306a36Sopenharmony_ci
417862306a36Sopenharmony_cistatic const char *const connection_state_names[] = {
417962306a36Sopenharmony_ci	[ISCSI_CONN_UP] = "up",
418062306a36Sopenharmony_ci	[ISCSI_CONN_DOWN] = "down",
418162306a36Sopenharmony_ci	[ISCSI_CONN_FAILED] = "failed",
418262306a36Sopenharmony_ci	[ISCSI_CONN_BOUND] = "bound"
418362306a36Sopenharmony_ci};
418462306a36Sopenharmony_ci
418562306a36Sopenharmony_cistatic ssize_t show_conn_state(struct device *dev,
418662306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
418762306a36Sopenharmony_ci{
418862306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);
418962306a36Sopenharmony_ci	const char *state = "unknown";
419062306a36Sopenharmony_ci	int conn_state = READ_ONCE(conn->state);
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	if (conn_state >= 0 &&
419362306a36Sopenharmony_ci	    conn_state < ARRAY_SIZE(connection_state_names))
419462306a36Sopenharmony_ci		state = connection_state_names[conn_state];
419562306a36Sopenharmony_ci
419662306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", state);
419762306a36Sopenharmony_ci}
419862306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(conn, state, S_IRUGO, show_conn_state,
419962306a36Sopenharmony_ci			NULL);
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci#define iscsi_conn_ep_attr_show(param)					\
420262306a36Sopenharmony_cistatic ssize_t show_conn_ep_param_##param(struct device *dev,		\
420362306a36Sopenharmony_ci					  struct device_attribute *attr,\
420462306a36Sopenharmony_ci					  char *buf)			\
420562306a36Sopenharmony_ci{									\
420662306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev->parent);	\
420762306a36Sopenharmony_ci	struct iscsi_transport *t = conn->transport;			\
420862306a36Sopenharmony_ci	struct iscsi_endpoint *ep;					\
420962306a36Sopenharmony_ci	ssize_t rc;							\
421062306a36Sopenharmony_ci									\
421162306a36Sopenharmony_ci	/*								\
421262306a36Sopenharmony_ci	 * Need to make sure ep_disconnect does not free the LLD's	\
421362306a36Sopenharmony_ci	 * interconnect resources while we are trying to read them.	\
421462306a36Sopenharmony_ci	 */								\
421562306a36Sopenharmony_ci	mutex_lock(&conn->ep_mutex);					\
421662306a36Sopenharmony_ci	ep = conn->ep;							\
421762306a36Sopenharmony_ci	if (!ep && t->ep_connect) {					\
421862306a36Sopenharmony_ci		mutex_unlock(&conn->ep_mutex);				\
421962306a36Sopenharmony_ci		return -ENOTCONN;					\
422062306a36Sopenharmony_ci	}								\
422162306a36Sopenharmony_ci									\
422262306a36Sopenharmony_ci	if (ep)								\
422362306a36Sopenharmony_ci		rc = t->get_ep_param(ep, param, buf);			\
422462306a36Sopenharmony_ci	else								\
422562306a36Sopenharmony_ci		rc = t->get_conn_param(conn, param, buf);		\
422662306a36Sopenharmony_ci	mutex_unlock(&conn->ep_mutex);					\
422762306a36Sopenharmony_ci	return rc;							\
422862306a36Sopenharmony_ci}
422962306a36Sopenharmony_ci
423062306a36Sopenharmony_ci#define iscsi_conn_ep_attr(field, param)				\
423162306a36Sopenharmony_ci	iscsi_conn_ep_attr_show(param)					\
423262306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(conn, field, S_IRUGO,				\
423362306a36Sopenharmony_ci			show_conn_ep_param_##param, NULL);
423462306a36Sopenharmony_ci
423562306a36Sopenharmony_ciiscsi_conn_ep_attr(address, ISCSI_PARAM_CONN_ADDRESS);
423662306a36Sopenharmony_ciiscsi_conn_ep_attr(port, ISCSI_PARAM_CONN_PORT);
423762306a36Sopenharmony_ci
423862306a36Sopenharmony_cistatic struct attribute *iscsi_conn_attrs[] = {
423962306a36Sopenharmony_ci	&dev_attr_conn_max_recv_dlength.attr,
424062306a36Sopenharmony_ci	&dev_attr_conn_max_xmit_dlength.attr,
424162306a36Sopenharmony_ci	&dev_attr_conn_header_digest.attr,
424262306a36Sopenharmony_ci	&dev_attr_conn_data_digest.attr,
424362306a36Sopenharmony_ci	&dev_attr_conn_ifmarker.attr,
424462306a36Sopenharmony_ci	&dev_attr_conn_ofmarker.attr,
424562306a36Sopenharmony_ci	&dev_attr_conn_address.attr,
424662306a36Sopenharmony_ci	&dev_attr_conn_port.attr,
424762306a36Sopenharmony_ci	&dev_attr_conn_exp_statsn.attr,
424862306a36Sopenharmony_ci	&dev_attr_conn_persistent_address.attr,
424962306a36Sopenharmony_ci	&dev_attr_conn_persistent_port.attr,
425062306a36Sopenharmony_ci	&dev_attr_conn_ping_tmo.attr,
425162306a36Sopenharmony_ci	&dev_attr_conn_recv_tmo.attr,
425262306a36Sopenharmony_ci	&dev_attr_conn_local_port.attr,
425362306a36Sopenharmony_ci	&dev_attr_conn_statsn.attr,
425462306a36Sopenharmony_ci	&dev_attr_conn_keepalive_tmo.attr,
425562306a36Sopenharmony_ci	&dev_attr_conn_max_segment_size.attr,
425662306a36Sopenharmony_ci	&dev_attr_conn_tcp_timestamp_stat.attr,
425762306a36Sopenharmony_ci	&dev_attr_conn_tcp_wsf_disable.attr,
425862306a36Sopenharmony_ci	&dev_attr_conn_tcp_nagle_disable.attr,
425962306a36Sopenharmony_ci	&dev_attr_conn_tcp_timer_scale.attr,
426062306a36Sopenharmony_ci	&dev_attr_conn_tcp_timestamp_enable.attr,
426162306a36Sopenharmony_ci	&dev_attr_conn_fragment_disable.attr,
426262306a36Sopenharmony_ci	&dev_attr_conn_ipv4_tos.attr,
426362306a36Sopenharmony_ci	&dev_attr_conn_ipv6_traffic_class.attr,
426462306a36Sopenharmony_ci	&dev_attr_conn_ipv6_flow_label.attr,
426562306a36Sopenharmony_ci	&dev_attr_conn_is_fw_assigned_ipv6.attr,
426662306a36Sopenharmony_ci	&dev_attr_conn_tcp_xmit_wsf.attr,
426762306a36Sopenharmony_ci	&dev_attr_conn_tcp_recv_wsf.attr,
426862306a36Sopenharmony_ci	&dev_attr_conn_local_ipaddr.attr,
426962306a36Sopenharmony_ci	&dev_attr_conn_state.attr,
427062306a36Sopenharmony_ci	NULL,
427162306a36Sopenharmony_ci};
427262306a36Sopenharmony_ci
427362306a36Sopenharmony_cistatic umode_t iscsi_conn_attr_is_visible(struct kobject *kobj,
427462306a36Sopenharmony_ci					 struct attribute *attr, int i)
427562306a36Sopenharmony_ci{
427662306a36Sopenharmony_ci	struct device *cdev = container_of(kobj, struct device, kobj);
427762306a36Sopenharmony_ci	struct iscsi_cls_conn *conn = transport_class_to_conn(cdev);
427862306a36Sopenharmony_ci	struct iscsi_transport *t = conn->transport;
427962306a36Sopenharmony_ci	int param;
428062306a36Sopenharmony_ci
428162306a36Sopenharmony_ci	if (attr == &dev_attr_conn_max_recv_dlength.attr)
428262306a36Sopenharmony_ci		param = ISCSI_PARAM_MAX_RECV_DLENGTH;
428362306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_max_xmit_dlength.attr)
428462306a36Sopenharmony_ci		param = ISCSI_PARAM_MAX_XMIT_DLENGTH;
428562306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_header_digest.attr)
428662306a36Sopenharmony_ci		param = ISCSI_PARAM_HDRDGST_EN;
428762306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_data_digest.attr)
428862306a36Sopenharmony_ci		param = ISCSI_PARAM_DATADGST_EN;
428962306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_ifmarker.attr)
429062306a36Sopenharmony_ci		param = ISCSI_PARAM_IFMARKER_EN;
429162306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_ofmarker.attr)
429262306a36Sopenharmony_ci		param = ISCSI_PARAM_OFMARKER_EN;
429362306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_address.attr)
429462306a36Sopenharmony_ci		param = ISCSI_PARAM_CONN_ADDRESS;
429562306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_port.attr)
429662306a36Sopenharmony_ci		param = ISCSI_PARAM_CONN_PORT;
429762306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_exp_statsn.attr)
429862306a36Sopenharmony_ci		param = ISCSI_PARAM_EXP_STATSN;
429962306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_persistent_address.attr)
430062306a36Sopenharmony_ci		param = ISCSI_PARAM_PERSISTENT_ADDRESS;
430162306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_persistent_port.attr)
430262306a36Sopenharmony_ci		param = ISCSI_PARAM_PERSISTENT_PORT;
430362306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_ping_tmo.attr)
430462306a36Sopenharmony_ci		param = ISCSI_PARAM_PING_TMO;
430562306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_recv_tmo.attr)
430662306a36Sopenharmony_ci		param = ISCSI_PARAM_RECV_TMO;
430762306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_local_port.attr)
430862306a36Sopenharmony_ci		param = ISCSI_PARAM_LOCAL_PORT;
430962306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_statsn.attr)
431062306a36Sopenharmony_ci		param = ISCSI_PARAM_STATSN;
431162306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_keepalive_tmo.attr)
431262306a36Sopenharmony_ci		param = ISCSI_PARAM_KEEPALIVE_TMO;
431362306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_max_segment_size.attr)
431462306a36Sopenharmony_ci		param = ISCSI_PARAM_MAX_SEGMENT_SIZE;
431562306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_timestamp_stat.attr)
431662306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_TIMESTAMP_STAT;
431762306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_wsf_disable.attr)
431862306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_WSF_DISABLE;
431962306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_nagle_disable.attr)
432062306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_NAGLE_DISABLE;
432162306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_timer_scale.attr)
432262306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_TIMER_SCALE;
432362306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_timestamp_enable.attr)
432462306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_TIMESTAMP_EN;
432562306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_fragment_disable.attr)
432662306a36Sopenharmony_ci		param = ISCSI_PARAM_IP_FRAGMENT_DISABLE;
432762306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_ipv4_tos.attr)
432862306a36Sopenharmony_ci		param = ISCSI_PARAM_IPV4_TOS;
432962306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_ipv6_traffic_class.attr)
433062306a36Sopenharmony_ci		param = ISCSI_PARAM_IPV6_TC;
433162306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_ipv6_flow_label.attr)
433262306a36Sopenharmony_ci		param = ISCSI_PARAM_IPV6_FLOW_LABEL;
433362306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_is_fw_assigned_ipv6.attr)
433462306a36Sopenharmony_ci		param = ISCSI_PARAM_IS_FW_ASSIGNED_IPV6;
433562306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_xmit_wsf.attr)
433662306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_XMIT_WSF;
433762306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_tcp_recv_wsf.attr)
433862306a36Sopenharmony_ci		param = ISCSI_PARAM_TCP_RECV_WSF;
433962306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_local_ipaddr.attr)
434062306a36Sopenharmony_ci		param = ISCSI_PARAM_LOCAL_IPADDR;
434162306a36Sopenharmony_ci	else if (attr == &dev_attr_conn_state.attr)
434262306a36Sopenharmony_ci		return S_IRUGO;
434362306a36Sopenharmony_ci	else {
434462306a36Sopenharmony_ci		WARN_ONCE(1, "Invalid conn attr");
434562306a36Sopenharmony_ci		return 0;
434662306a36Sopenharmony_ci	}
434762306a36Sopenharmony_ci
434862306a36Sopenharmony_ci	return t->attr_is_visible(ISCSI_PARAM, param);
434962306a36Sopenharmony_ci}
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_cistatic struct attribute_group iscsi_conn_group = {
435262306a36Sopenharmony_ci	.attrs = iscsi_conn_attrs,
435362306a36Sopenharmony_ci	.is_visible = iscsi_conn_attr_is_visible,
435462306a36Sopenharmony_ci};
435562306a36Sopenharmony_ci
435662306a36Sopenharmony_ci/*
435762306a36Sopenharmony_ci * iSCSI session attrs
435862306a36Sopenharmony_ci */
435962306a36Sopenharmony_ci#define iscsi_session_attr_show(param, perm)				\
436062306a36Sopenharmony_cistatic ssize_t								\
436162306a36Sopenharmony_cishow_session_param_##param(struct device *dev,				\
436262306a36Sopenharmony_ci			   struct device_attribute *attr, char *buf)	\
436362306a36Sopenharmony_ci{									\
436462306a36Sopenharmony_ci	struct iscsi_cls_session *session = 				\
436562306a36Sopenharmony_ci		iscsi_dev_to_session(dev->parent);			\
436662306a36Sopenharmony_ci	struct iscsi_transport *t = session->transport;			\
436762306a36Sopenharmony_ci									\
436862306a36Sopenharmony_ci	if (perm && !capable(CAP_SYS_ADMIN))				\
436962306a36Sopenharmony_ci		return -EACCES;						\
437062306a36Sopenharmony_ci	return t->get_session_param(session, param, buf);		\
437162306a36Sopenharmony_ci}
437262306a36Sopenharmony_ci
437362306a36Sopenharmony_ci#define iscsi_session_attr(field, param, perm)				\
437462306a36Sopenharmony_ci	iscsi_session_attr_show(param, perm)				\
437562306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(sess, field, S_IRUGO, show_session_param_##param, \
437662306a36Sopenharmony_ci			NULL);
437762306a36Sopenharmony_ciiscsi_session_attr(targetname, ISCSI_PARAM_TARGET_NAME, 0);
437862306a36Sopenharmony_ciiscsi_session_attr(initial_r2t, ISCSI_PARAM_INITIAL_R2T_EN, 0);
437962306a36Sopenharmony_ciiscsi_session_attr(max_outstanding_r2t, ISCSI_PARAM_MAX_R2T, 0);
438062306a36Sopenharmony_ciiscsi_session_attr(immediate_data, ISCSI_PARAM_IMM_DATA_EN, 0);
438162306a36Sopenharmony_ciiscsi_session_attr(first_burst_len, ISCSI_PARAM_FIRST_BURST, 0);
438262306a36Sopenharmony_ciiscsi_session_attr(max_burst_len, ISCSI_PARAM_MAX_BURST, 0);
438362306a36Sopenharmony_ciiscsi_session_attr(data_pdu_in_order, ISCSI_PARAM_PDU_INORDER_EN, 0);
438462306a36Sopenharmony_ciiscsi_session_attr(data_seq_in_order, ISCSI_PARAM_DATASEQ_INORDER_EN, 0);
438562306a36Sopenharmony_ciiscsi_session_attr(erl, ISCSI_PARAM_ERL, 0);
438662306a36Sopenharmony_ciiscsi_session_attr(tpgt, ISCSI_PARAM_TPGT, 0);
438762306a36Sopenharmony_ciiscsi_session_attr(username, ISCSI_PARAM_USERNAME, 1);
438862306a36Sopenharmony_ciiscsi_session_attr(username_in, ISCSI_PARAM_USERNAME_IN, 1);
438962306a36Sopenharmony_ciiscsi_session_attr(password, ISCSI_PARAM_PASSWORD, 1);
439062306a36Sopenharmony_ciiscsi_session_attr(password_in, ISCSI_PARAM_PASSWORD_IN, 1);
439162306a36Sopenharmony_ciiscsi_session_attr(chap_out_idx, ISCSI_PARAM_CHAP_OUT_IDX, 1);
439262306a36Sopenharmony_ciiscsi_session_attr(chap_in_idx, ISCSI_PARAM_CHAP_IN_IDX, 1);
439362306a36Sopenharmony_ciiscsi_session_attr(fast_abort, ISCSI_PARAM_FAST_ABORT, 0);
439462306a36Sopenharmony_ciiscsi_session_attr(abort_tmo, ISCSI_PARAM_ABORT_TMO, 0);
439562306a36Sopenharmony_ciiscsi_session_attr(lu_reset_tmo, ISCSI_PARAM_LU_RESET_TMO, 0);
439662306a36Sopenharmony_ciiscsi_session_attr(tgt_reset_tmo, ISCSI_PARAM_TGT_RESET_TMO, 0);
439762306a36Sopenharmony_ciiscsi_session_attr(ifacename, ISCSI_PARAM_IFACE_NAME, 0);
439862306a36Sopenharmony_ciiscsi_session_attr(initiatorname, ISCSI_PARAM_INITIATOR_NAME, 0);
439962306a36Sopenharmony_ciiscsi_session_attr(targetalias, ISCSI_PARAM_TARGET_ALIAS, 0);
440062306a36Sopenharmony_ciiscsi_session_attr(boot_root, ISCSI_PARAM_BOOT_ROOT, 0);
440162306a36Sopenharmony_ciiscsi_session_attr(boot_nic, ISCSI_PARAM_BOOT_NIC, 0);
440262306a36Sopenharmony_ciiscsi_session_attr(boot_target, ISCSI_PARAM_BOOT_TARGET, 0);
440362306a36Sopenharmony_ciiscsi_session_attr(auto_snd_tgt_disable, ISCSI_PARAM_AUTO_SND_TGT_DISABLE, 0);
440462306a36Sopenharmony_ciiscsi_session_attr(discovery_session, ISCSI_PARAM_DISCOVERY_SESS, 0);
440562306a36Sopenharmony_ciiscsi_session_attr(portal_type, ISCSI_PARAM_PORTAL_TYPE, 0);
440662306a36Sopenharmony_ciiscsi_session_attr(chap_auth, ISCSI_PARAM_CHAP_AUTH_EN, 0);
440762306a36Sopenharmony_ciiscsi_session_attr(discovery_logout, ISCSI_PARAM_DISCOVERY_LOGOUT_EN, 0);
440862306a36Sopenharmony_ciiscsi_session_attr(bidi_chap, ISCSI_PARAM_BIDI_CHAP_EN, 0);
440962306a36Sopenharmony_ciiscsi_session_attr(discovery_auth_optional,
441062306a36Sopenharmony_ci		   ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL, 0);
441162306a36Sopenharmony_ciiscsi_session_attr(def_time2wait, ISCSI_PARAM_DEF_TIME2WAIT, 0);
441262306a36Sopenharmony_ciiscsi_session_attr(def_time2retain, ISCSI_PARAM_DEF_TIME2RETAIN, 0);
441362306a36Sopenharmony_ciiscsi_session_attr(isid, ISCSI_PARAM_ISID, 0);
441462306a36Sopenharmony_ciiscsi_session_attr(tsid, ISCSI_PARAM_TSID, 0);
441562306a36Sopenharmony_ciiscsi_session_attr(def_taskmgmt_tmo, ISCSI_PARAM_DEF_TASKMGMT_TMO, 0);
441662306a36Sopenharmony_ciiscsi_session_attr(discovery_parent_idx, ISCSI_PARAM_DISCOVERY_PARENT_IDX, 0);
441762306a36Sopenharmony_ciiscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0);
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_cistatic ssize_t
442062306a36Sopenharmony_cishow_priv_session_target_state(struct device *dev, struct device_attribute *attr,
442162306a36Sopenharmony_ci			char *buf)
442262306a36Sopenharmony_ci{
442362306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
442462306a36Sopenharmony_ci
442562306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n",
442662306a36Sopenharmony_ci			iscsi_session_target_state_name[session->target_state]);
442762306a36Sopenharmony_ci}
442862306a36Sopenharmony_ci
442962306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(priv_sess, target_state, S_IRUGO,
443062306a36Sopenharmony_ci			show_priv_session_target_state, NULL);
443162306a36Sopenharmony_ci
443262306a36Sopenharmony_cistatic ssize_t
443362306a36Sopenharmony_cishow_priv_session_state(struct device *dev, struct device_attribute *attr,
443462306a36Sopenharmony_ci			char *buf)
443562306a36Sopenharmony_ci{
443662306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
443762306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", iscsi_session_state_name(session->state));
443862306a36Sopenharmony_ci}
443962306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(priv_sess, state, S_IRUGO, show_priv_session_state,
444062306a36Sopenharmony_ci			NULL);
444162306a36Sopenharmony_cistatic ssize_t
444262306a36Sopenharmony_cishow_priv_session_creator(struct device *dev, struct device_attribute *attr,
444362306a36Sopenharmony_ci			char *buf)
444462306a36Sopenharmony_ci{
444562306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
444662306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", session->creator);
444762306a36Sopenharmony_ci}
444862306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(priv_sess, creator, S_IRUGO, show_priv_session_creator,
444962306a36Sopenharmony_ci			NULL);
445062306a36Sopenharmony_cistatic ssize_t
445162306a36Sopenharmony_cishow_priv_session_target_id(struct device *dev, struct device_attribute *attr,
445262306a36Sopenharmony_ci			    char *buf)
445362306a36Sopenharmony_ci{
445462306a36Sopenharmony_ci	struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
445562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", session->target_id);
445662306a36Sopenharmony_ci}
445762306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(priv_sess, target_id, S_IRUGO,
445862306a36Sopenharmony_ci			show_priv_session_target_id, NULL);
445962306a36Sopenharmony_ci
446062306a36Sopenharmony_ci#define iscsi_priv_session_attr_show(field, format)			\
446162306a36Sopenharmony_cistatic ssize_t								\
446262306a36Sopenharmony_cishow_priv_session_##field(struct device *dev, 				\
446362306a36Sopenharmony_ci			  struct device_attribute *attr, char *buf)	\
446462306a36Sopenharmony_ci{									\
446562306a36Sopenharmony_ci	struct iscsi_cls_session *session = 				\
446662306a36Sopenharmony_ci			iscsi_dev_to_session(dev->parent);		\
446762306a36Sopenharmony_ci	if (session->field == -1)					\
446862306a36Sopenharmony_ci		return sysfs_emit(buf, "off\n");			\
446962306a36Sopenharmony_ci	return sysfs_emit(buf, format"\n", session->field);		\
447062306a36Sopenharmony_ci}
447162306a36Sopenharmony_ci
447262306a36Sopenharmony_ci#define iscsi_priv_session_attr_store(field)				\
447362306a36Sopenharmony_cistatic ssize_t								\
447462306a36Sopenharmony_cistore_priv_session_##field(struct device *dev,				\
447562306a36Sopenharmony_ci			   struct device_attribute *attr,		\
447662306a36Sopenharmony_ci			   const char *buf, size_t count)		\
447762306a36Sopenharmony_ci{									\
447862306a36Sopenharmony_ci	int val;							\
447962306a36Sopenharmony_ci	char *cp;							\
448062306a36Sopenharmony_ci	struct iscsi_cls_session *session =				\
448162306a36Sopenharmony_ci		iscsi_dev_to_session(dev->parent);			\
448262306a36Sopenharmony_ci	if ((session->state == ISCSI_SESSION_FREE) ||			\
448362306a36Sopenharmony_ci	    (session->state == ISCSI_SESSION_FAILED))			\
448462306a36Sopenharmony_ci		return -EBUSY;						\
448562306a36Sopenharmony_ci	if (strncmp(buf, "off", 3) == 0) {				\
448662306a36Sopenharmony_ci		session->field = -1;					\
448762306a36Sopenharmony_ci		session->field##_sysfs_override = true;			\
448862306a36Sopenharmony_ci	} else {							\
448962306a36Sopenharmony_ci		val = simple_strtoul(buf, &cp, 0);			\
449062306a36Sopenharmony_ci		if (*cp != '\0' && *cp != '\n')				\
449162306a36Sopenharmony_ci			return -EINVAL;					\
449262306a36Sopenharmony_ci		session->field = val;					\
449362306a36Sopenharmony_ci		session->field##_sysfs_override = true;			\
449462306a36Sopenharmony_ci	}								\
449562306a36Sopenharmony_ci	return count;							\
449662306a36Sopenharmony_ci}
449762306a36Sopenharmony_ci
449862306a36Sopenharmony_ci#define iscsi_priv_session_rw_attr(field, format)			\
449962306a36Sopenharmony_ci	iscsi_priv_session_attr_show(field, format)			\
450062306a36Sopenharmony_ci	iscsi_priv_session_attr_store(field)				\
450162306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(priv_sess, field, S_IRUGO | S_IWUSR,		\
450262306a36Sopenharmony_ci			show_priv_session_##field,			\
450362306a36Sopenharmony_ci			store_priv_session_##field)
450462306a36Sopenharmony_ci
450562306a36Sopenharmony_ciiscsi_priv_session_rw_attr(recovery_tmo, "%d");
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_cistatic struct attribute *iscsi_session_attrs[] = {
450862306a36Sopenharmony_ci	&dev_attr_sess_initial_r2t.attr,
450962306a36Sopenharmony_ci	&dev_attr_sess_max_outstanding_r2t.attr,
451062306a36Sopenharmony_ci	&dev_attr_sess_immediate_data.attr,
451162306a36Sopenharmony_ci	&dev_attr_sess_first_burst_len.attr,
451262306a36Sopenharmony_ci	&dev_attr_sess_max_burst_len.attr,
451362306a36Sopenharmony_ci	&dev_attr_sess_data_pdu_in_order.attr,
451462306a36Sopenharmony_ci	&dev_attr_sess_data_seq_in_order.attr,
451562306a36Sopenharmony_ci	&dev_attr_sess_erl.attr,
451662306a36Sopenharmony_ci	&dev_attr_sess_targetname.attr,
451762306a36Sopenharmony_ci	&dev_attr_sess_tpgt.attr,
451862306a36Sopenharmony_ci	&dev_attr_sess_password.attr,
451962306a36Sopenharmony_ci	&dev_attr_sess_password_in.attr,
452062306a36Sopenharmony_ci	&dev_attr_sess_username.attr,
452162306a36Sopenharmony_ci	&dev_attr_sess_username_in.attr,
452262306a36Sopenharmony_ci	&dev_attr_sess_fast_abort.attr,
452362306a36Sopenharmony_ci	&dev_attr_sess_abort_tmo.attr,
452462306a36Sopenharmony_ci	&dev_attr_sess_lu_reset_tmo.attr,
452562306a36Sopenharmony_ci	&dev_attr_sess_tgt_reset_tmo.attr,
452662306a36Sopenharmony_ci	&dev_attr_sess_ifacename.attr,
452762306a36Sopenharmony_ci	&dev_attr_sess_initiatorname.attr,
452862306a36Sopenharmony_ci	&dev_attr_sess_targetalias.attr,
452962306a36Sopenharmony_ci	&dev_attr_sess_boot_root.attr,
453062306a36Sopenharmony_ci	&dev_attr_sess_boot_nic.attr,
453162306a36Sopenharmony_ci	&dev_attr_sess_boot_target.attr,
453262306a36Sopenharmony_ci	&dev_attr_priv_sess_recovery_tmo.attr,
453362306a36Sopenharmony_ci	&dev_attr_priv_sess_state.attr,
453462306a36Sopenharmony_ci	&dev_attr_priv_sess_target_state.attr,
453562306a36Sopenharmony_ci	&dev_attr_priv_sess_creator.attr,
453662306a36Sopenharmony_ci	&dev_attr_sess_chap_out_idx.attr,
453762306a36Sopenharmony_ci	&dev_attr_sess_chap_in_idx.attr,
453862306a36Sopenharmony_ci	&dev_attr_priv_sess_target_id.attr,
453962306a36Sopenharmony_ci	&dev_attr_sess_auto_snd_tgt_disable.attr,
454062306a36Sopenharmony_ci	&dev_attr_sess_discovery_session.attr,
454162306a36Sopenharmony_ci	&dev_attr_sess_portal_type.attr,
454262306a36Sopenharmony_ci	&dev_attr_sess_chap_auth.attr,
454362306a36Sopenharmony_ci	&dev_attr_sess_discovery_logout.attr,
454462306a36Sopenharmony_ci	&dev_attr_sess_bidi_chap.attr,
454562306a36Sopenharmony_ci	&dev_attr_sess_discovery_auth_optional.attr,
454662306a36Sopenharmony_ci	&dev_attr_sess_def_time2wait.attr,
454762306a36Sopenharmony_ci	&dev_attr_sess_def_time2retain.attr,
454862306a36Sopenharmony_ci	&dev_attr_sess_isid.attr,
454962306a36Sopenharmony_ci	&dev_attr_sess_tsid.attr,
455062306a36Sopenharmony_ci	&dev_attr_sess_def_taskmgmt_tmo.attr,
455162306a36Sopenharmony_ci	&dev_attr_sess_discovery_parent_idx.attr,
455262306a36Sopenharmony_ci	&dev_attr_sess_discovery_parent_type.attr,
455362306a36Sopenharmony_ci	NULL,
455462306a36Sopenharmony_ci};
455562306a36Sopenharmony_ci
455662306a36Sopenharmony_cistatic umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
455762306a36Sopenharmony_ci					    struct attribute *attr, int i)
455862306a36Sopenharmony_ci{
455962306a36Sopenharmony_ci	struct device *cdev = container_of(kobj, struct device, kobj);
456062306a36Sopenharmony_ci	struct iscsi_cls_session *session = transport_class_to_session(cdev);
456162306a36Sopenharmony_ci	struct iscsi_transport *t = session->transport;
456262306a36Sopenharmony_ci	int param;
456362306a36Sopenharmony_ci
456462306a36Sopenharmony_ci	if (attr == &dev_attr_sess_initial_r2t.attr)
456562306a36Sopenharmony_ci		param = ISCSI_PARAM_INITIAL_R2T_EN;
456662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_max_outstanding_r2t.attr)
456762306a36Sopenharmony_ci		param = ISCSI_PARAM_MAX_R2T;
456862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_immediate_data.attr)
456962306a36Sopenharmony_ci		param = ISCSI_PARAM_IMM_DATA_EN;
457062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_first_burst_len.attr)
457162306a36Sopenharmony_ci		param = ISCSI_PARAM_FIRST_BURST;
457262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_max_burst_len.attr)
457362306a36Sopenharmony_ci		param = ISCSI_PARAM_MAX_BURST;
457462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_data_pdu_in_order.attr)
457562306a36Sopenharmony_ci		param = ISCSI_PARAM_PDU_INORDER_EN;
457662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_data_seq_in_order.attr)
457762306a36Sopenharmony_ci		param = ISCSI_PARAM_DATASEQ_INORDER_EN;
457862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_erl.attr)
457962306a36Sopenharmony_ci		param = ISCSI_PARAM_ERL;
458062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_targetname.attr)
458162306a36Sopenharmony_ci		param = ISCSI_PARAM_TARGET_NAME;
458262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_tpgt.attr)
458362306a36Sopenharmony_ci		param = ISCSI_PARAM_TPGT;
458462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_chap_in_idx.attr)
458562306a36Sopenharmony_ci		param = ISCSI_PARAM_CHAP_IN_IDX;
458662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_chap_out_idx.attr)
458762306a36Sopenharmony_ci		param = ISCSI_PARAM_CHAP_OUT_IDX;
458862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_password.attr)
458962306a36Sopenharmony_ci		param = ISCSI_PARAM_USERNAME;
459062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_password_in.attr)
459162306a36Sopenharmony_ci		param = ISCSI_PARAM_USERNAME_IN;
459262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_username.attr)
459362306a36Sopenharmony_ci		param = ISCSI_PARAM_PASSWORD;
459462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_username_in.attr)
459562306a36Sopenharmony_ci		param = ISCSI_PARAM_PASSWORD_IN;
459662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_fast_abort.attr)
459762306a36Sopenharmony_ci		param = ISCSI_PARAM_FAST_ABORT;
459862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_abort_tmo.attr)
459962306a36Sopenharmony_ci		param = ISCSI_PARAM_ABORT_TMO;
460062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_lu_reset_tmo.attr)
460162306a36Sopenharmony_ci		param = ISCSI_PARAM_LU_RESET_TMO;
460262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_tgt_reset_tmo.attr)
460362306a36Sopenharmony_ci		param = ISCSI_PARAM_TGT_RESET_TMO;
460462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_ifacename.attr)
460562306a36Sopenharmony_ci		param = ISCSI_PARAM_IFACE_NAME;
460662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_initiatorname.attr)
460762306a36Sopenharmony_ci		param = ISCSI_PARAM_INITIATOR_NAME;
460862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_targetalias.attr)
460962306a36Sopenharmony_ci		param = ISCSI_PARAM_TARGET_ALIAS;
461062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_boot_root.attr)
461162306a36Sopenharmony_ci		param = ISCSI_PARAM_BOOT_ROOT;
461262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_boot_nic.attr)
461362306a36Sopenharmony_ci		param = ISCSI_PARAM_BOOT_NIC;
461462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_boot_target.attr)
461562306a36Sopenharmony_ci		param = ISCSI_PARAM_BOOT_TARGET;
461662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_auto_snd_tgt_disable.attr)
461762306a36Sopenharmony_ci		param = ISCSI_PARAM_AUTO_SND_TGT_DISABLE;
461862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_discovery_session.attr)
461962306a36Sopenharmony_ci		param = ISCSI_PARAM_DISCOVERY_SESS;
462062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_portal_type.attr)
462162306a36Sopenharmony_ci		param = ISCSI_PARAM_PORTAL_TYPE;
462262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_chap_auth.attr)
462362306a36Sopenharmony_ci		param = ISCSI_PARAM_CHAP_AUTH_EN;
462462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_discovery_logout.attr)
462562306a36Sopenharmony_ci		param = ISCSI_PARAM_DISCOVERY_LOGOUT_EN;
462662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_bidi_chap.attr)
462762306a36Sopenharmony_ci		param = ISCSI_PARAM_BIDI_CHAP_EN;
462862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_discovery_auth_optional.attr)
462962306a36Sopenharmony_ci		param = ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL;
463062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_def_time2wait.attr)
463162306a36Sopenharmony_ci		param = ISCSI_PARAM_DEF_TIME2WAIT;
463262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_def_time2retain.attr)
463362306a36Sopenharmony_ci		param = ISCSI_PARAM_DEF_TIME2RETAIN;
463462306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_isid.attr)
463562306a36Sopenharmony_ci		param = ISCSI_PARAM_ISID;
463662306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_tsid.attr)
463762306a36Sopenharmony_ci		param = ISCSI_PARAM_TSID;
463862306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_def_taskmgmt_tmo.attr)
463962306a36Sopenharmony_ci		param = ISCSI_PARAM_DEF_TASKMGMT_TMO;
464062306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_discovery_parent_idx.attr)
464162306a36Sopenharmony_ci		param = ISCSI_PARAM_DISCOVERY_PARENT_IDX;
464262306a36Sopenharmony_ci	else if (attr == &dev_attr_sess_discovery_parent_type.attr)
464362306a36Sopenharmony_ci		param = ISCSI_PARAM_DISCOVERY_PARENT_TYPE;
464462306a36Sopenharmony_ci	else if (attr == &dev_attr_priv_sess_recovery_tmo.attr)
464562306a36Sopenharmony_ci		return S_IRUGO | S_IWUSR;
464662306a36Sopenharmony_ci	else if (attr == &dev_attr_priv_sess_state.attr)
464762306a36Sopenharmony_ci		return S_IRUGO;
464862306a36Sopenharmony_ci	else if (attr == &dev_attr_priv_sess_target_state.attr)
464962306a36Sopenharmony_ci		return S_IRUGO;
465062306a36Sopenharmony_ci	else if (attr == &dev_attr_priv_sess_creator.attr)
465162306a36Sopenharmony_ci		return S_IRUGO;
465262306a36Sopenharmony_ci	else if (attr == &dev_attr_priv_sess_target_id.attr)
465362306a36Sopenharmony_ci		return S_IRUGO;
465462306a36Sopenharmony_ci	else {
465562306a36Sopenharmony_ci		WARN_ONCE(1, "Invalid session attr");
465662306a36Sopenharmony_ci		return 0;
465762306a36Sopenharmony_ci	}
465862306a36Sopenharmony_ci
465962306a36Sopenharmony_ci	return t->attr_is_visible(ISCSI_PARAM, param);
466062306a36Sopenharmony_ci}
466162306a36Sopenharmony_ci
466262306a36Sopenharmony_cistatic struct attribute_group iscsi_session_group = {
466362306a36Sopenharmony_ci	.attrs = iscsi_session_attrs,
466462306a36Sopenharmony_ci	.is_visible = iscsi_session_attr_is_visible,
466562306a36Sopenharmony_ci};
466662306a36Sopenharmony_ci
466762306a36Sopenharmony_ci/*
466862306a36Sopenharmony_ci * iSCSI host attrs
466962306a36Sopenharmony_ci */
467062306a36Sopenharmony_ci#define iscsi_host_attr_show(param)					\
467162306a36Sopenharmony_cistatic ssize_t								\
467262306a36Sopenharmony_cishow_host_param_##param(struct device *dev, 				\
467362306a36Sopenharmony_ci			struct device_attribute *attr, char *buf)	\
467462306a36Sopenharmony_ci{									\
467562306a36Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(dev);	\
467662306a36Sopenharmony_ci	struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \
467762306a36Sopenharmony_ci	return priv->iscsi_transport->get_host_param(shost, param, buf); \
467862306a36Sopenharmony_ci}
467962306a36Sopenharmony_ci
468062306a36Sopenharmony_ci#define iscsi_host_attr(field, param)					\
468162306a36Sopenharmony_ci	iscsi_host_attr_show(param)					\
468262306a36Sopenharmony_cistatic ISCSI_CLASS_ATTR(host, field, S_IRUGO, show_host_param_##param,	\
468362306a36Sopenharmony_ci			NULL);
468462306a36Sopenharmony_ci
468562306a36Sopenharmony_ciiscsi_host_attr(netdev, ISCSI_HOST_PARAM_NETDEV_NAME);
468662306a36Sopenharmony_ciiscsi_host_attr(hwaddress, ISCSI_HOST_PARAM_HWADDRESS);
468762306a36Sopenharmony_ciiscsi_host_attr(ipaddress, ISCSI_HOST_PARAM_IPADDRESS);
468862306a36Sopenharmony_ciiscsi_host_attr(initiatorname, ISCSI_HOST_PARAM_INITIATOR_NAME);
468962306a36Sopenharmony_ciiscsi_host_attr(port_state, ISCSI_HOST_PARAM_PORT_STATE);
469062306a36Sopenharmony_ciiscsi_host_attr(port_speed, ISCSI_HOST_PARAM_PORT_SPEED);
469162306a36Sopenharmony_ci
469262306a36Sopenharmony_cistatic struct attribute *iscsi_host_attrs[] = {
469362306a36Sopenharmony_ci	&dev_attr_host_netdev.attr,
469462306a36Sopenharmony_ci	&dev_attr_host_hwaddress.attr,
469562306a36Sopenharmony_ci	&dev_attr_host_ipaddress.attr,
469662306a36Sopenharmony_ci	&dev_attr_host_initiatorname.attr,
469762306a36Sopenharmony_ci	&dev_attr_host_port_state.attr,
469862306a36Sopenharmony_ci	&dev_attr_host_port_speed.attr,
469962306a36Sopenharmony_ci	NULL,
470062306a36Sopenharmony_ci};
470162306a36Sopenharmony_ci
470262306a36Sopenharmony_cistatic umode_t iscsi_host_attr_is_visible(struct kobject *kobj,
470362306a36Sopenharmony_ci					 struct attribute *attr, int i)
470462306a36Sopenharmony_ci{
470562306a36Sopenharmony_ci	struct device *cdev = container_of(kobj, struct device, kobj);
470662306a36Sopenharmony_ci	struct Scsi_Host *shost = transport_class_to_shost(cdev);
470762306a36Sopenharmony_ci	struct iscsi_internal *priv = to_iscsi_internal(shost->transportt);
470862306a36Sopenharmony_ci	int param;
470962306a36Sopenharmony_ci
471062306a36Sopenharmony_ci	if (attr == &dev_attr_host_netdev.attr)
471162306a36Sopenharmony_ci		param = ISCSI_HOST_PARAM_NETDEV_NAME;
471262306a36Sopenharmony_ci	else if (attr == &dev_attr_host_hwaddress.attr)
471362306a36Sopenharmony_ci		param = ISCSI_HOST_PARAM_HWADDRESS;
471462306a36Sopenharmony_ci	else if (attr == &dev_attr_host_ipaddress.attr)
471562306a36Sopenharmony_ci		param = ISCSI_HOST_PARAM_IPADDRESS;
471662306a36Sopenharmony_ci	else if (attr == &dev_attr_host_initiatorname.attr)
471762306a36Sopenharmony_ci		param = ISCSI_HOST_PARAM_INITIATOR_NAME;
471862306a36Sopenharmony_ci	else if (attr == &dev_attr_host_port_state.attr)
471962306a36Sopenharmony_ci		param = ISCSI_HOST_PARAM_PORT_STATE;
472062306a36Sopenharmony_ci	else if (attr == &dev_attr_host_port_speed.attr)
472162306a36Sopenharmony_ci		param = ISCSI_HOST_PARAM_PORT_SPEED;
472262306a36Sopenharmony_ci	else {
472362306a36Sopenharmony_ci		WARN_ONCE(1, "Invalid host attr");
472462306a36Sopenharmony_ci		return 0;
472562306a36Sopenharmony_ci	}
472662306a36Sopenharmony_ci
472762306a36Sopenharmony_ci	return priv->iscsi_transport->attr_is_visible(ISCSI_HOST_PARAM, param);
472862306a36Sopenharmony_ci}
472962306a36Sopenharmony_ci
473062306a36Sopenharmony_cistatic struct attribute_group iscsi_host_group = {
473162306a36Sopenharmony_ci	.attrs = iscsi_host_attrs,
473262306a36Sopenharmony_ci	.is_visible = iscsi_host_attr_is_visible,
473362306a36Sopenharmony_ci};
473462306a36Sopenharmony_ci
473562306a36Sopenharmony_ci/* convert iscsi_port_speed values to ascii string name */
473662306a36Sopenharmony_cistatic const struct {
473762306a36Sopenharmony_ci	enum iscsi_port_speed	value;
473862306a36Sopenharmony_ci	char			*name;
473962306a36Sopenharmony_ci} iscsi_port_speed_names[] = {
474062306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_UNKNOWN,	"Unknown" },
474162306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_10MBPS,	"10 Mbps" },
474262306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_100MBPS,	"100 Mbps" },
474362306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_1GBPS,	"1 Gbps" },
474462306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_10GBPS,	"10 Gbps" },
474562306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_25GBPS,       "25 Gbps" },
474662306a36Sopenharmony_ci	{ISCSI_PORT_SPEED_40GBPS,       "40 Gbps" },
474762306a36Sopenharmony_ci};
474862306a36Sopenharmony_ci
474962306a36Sopenharmony_cichar *iscsi_get_port_speed_name(struct Scsi_Host *shost)
475062306a36Sopenharmony_ci{
475162306a36Sopenharmony_ci	int i;
475262306a36Sopenharmony_ci	char *speed = "Unknown!";
475362306a36Sopenharmony_ci	struct iscsi_cls_host *ihost = shost->shost_data;
475462306a36Sopenharmony_ci	uint32_t port_speed = ihost->port_speed;
475562306a36Sopenharmony_ci
475662306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iscsi_port_speed_names); i++) {
475762306a36Sopenharmony_ci		if (iscsi_port_speed_names[i].value & port_speed) {
475862306a36Sopenharmony_ci			speed = iscsi_port_speed_names[i].name;
475962306a36Sopenharmony_ci			break;
476062306a36Sopenharmony_ci		}
476162306a36Sopenharmony_ci	}
476262306a36Sopenharmony_ci	return speed;
476362306a36Sopenharmony_ci}
476462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_port_speed_name);
476562306a36Sopenharmony_ci
476662306a36Sopenharmony_ci/* convert iscsi_port_state values to ascii string name */
476762306a36Sopenharmony_cistatic const struct {
476862306a36Sopenharmony_ci	enum iscsi_port_state	value;
476962306a36Sopenharmony_ci	char			*name;
477062306a36Sopenharmony_ci} iscsi_port_state_names[] = {
477162306a36Sopenharmony_ci	{ISCSI_PORT_STATE_DOWN,		"LINK DOWN" },
477262306a36Sopenharmony_ci	{ISCSI_PORT_STATE_UP,		"LINK UP" },
477362306a36Sopenharmony_ci};
477462306a36Sopenharmony_ci
477562306a36Sopenharmony_cichar *iscsi_get_port_state_name(struct Scsi_Host *shost)
477662306a36Sopenharmony_ci{
477762306a36Sopenharmony_ci	int i;
477862306a36Sopenharmony_ci	char *state = "Unknown!";
477962306a36Sopenharmony_ci	struct iscsi_cls_host *ihost = shost->shost_data;
478062306a36Sopenharmony_ci	uint32_t port_state = ihost->port_state;
478162306a36Sopenharmony_ci
478262306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(iscsi_port_state_names); i++) {
478362306a36Sopenharmony_ci		if (iscsi_port_state_names[i].value & port_state) {
478462306a36Sopenharmony_ci			state = iscsi_port_state_names[i].name;
478562306a36Sopenharmony_ci			break;
478662306a36Sopenharmony_ci		}
478762306a36Sopenharmony_ci	}
478862306a36Sopenharmony_ci	return state;
478962306a36Sopenharmony_ci}
479062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_port_state_name);
479162306a36Sopenharmony_ci
479262306a36Sopenharmony_cistatic int iscsi_session_match(struct attribute_container *cont,
479362306a36Sopenharmony_ci			   struct device *dev)
479462306a36Sopenharmony_ci{
479562306a36Sopenharmony_ci	struct iscsi_cls_session *session;
479662306a36Sopenharmony_ci	struct Scsi_Host *shost;
479762306a36Sopenharmony_ci	struct iscsi_internal *priv;
479862306a36Sopenharmony_ci
479962306a36Sopenharmony_ci	if (!iscsi_is_session_dev(dev))
480062306a36Sopenharmony_ci		return 0;
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci	session = iscsi_dev_to_session(dev);
480362306a36Sopenharmony_ci	shost = iscsi_session_to_shost(session);
480462306a36Sopenharmony_ci	if (!shost->transportt)
480562306a36Sopenharmony_ci		return 0;
480662306a36Sopenharmony_ci
480762306a36Sopenharmony_ci	priv = to_iscsi_internal(shost->transportt);
480862306a36Sopenharmony_ci	if (priv->session_cont.ac.class != &iscsi_session_class.class)
480962306a36Sopenharmony_ci		return 0;
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	return &priv->session_cont.ac == cont;
481262306a36Sopenharmony_ci}
481362306a36Sopenharmony_ci
481462306a36Sopenharmony_cistatic int iscsi_conn_match(struct attribute_container *cont,
481562306a36Sopenharmony_ci			   struct device *dev)
481662306a36Sopenharmony_ci{
481762306a36Sopenharmony_ci	struct iscsi_cls_session *session;
481862306a36Sopenharmony_ci	struct iscsi_cls_conn *conn;
481962306a36Sopenharmony_ci	struct Scsi_Host *shost;
482062306a36Sopenharmony_ci	struct iscsi_internal *priv;
482162306a36Sopenharmony_ci
482262306a36Sopenharmony_ci	if (!iscsi_is_conn_dev(dev))
482362306a36Sopenharmony_ci		return 0;
482462306a36Sopenharmony_ci
482562306a36Sopenharmony_ci	conn = iscsi_dev_to_conn(dev);
482662306a36Sopenharmony_ci	session = iscsi_dev_to_session(conn->dev.parent);
482762306a36Sopenharmony_ci	shost = iscsi_session_to_shost(session);
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci	if (!shost->transportt)
483062306a36Sopenharmony_ci		return 0;
483162306a36Sopenharmony_ci
483262306a36Sopenharmony_ci	priv = to_iscsi_internal(shost->transportt);
483362306a36Sopenharmony_ci	if (priv->conn_cont.ac.class != &iscsi_connection_class.class)
483462306a36Sopenharmony_ci		return 0;
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci	return &priv->conn_cont.ac == cont;
483762306a36Sopenharmony_ci}
483862306a36Sopenharmony_ci
483962306a36Sopenharmony_cistatic int iscsi_host_match(struct attribute_container *cont,
484062306a36Sopenharmony_ci			    struct device *dev)
484162306a36Sopenharmony_ci{
484262306a36Sopenharmony_ci	struct Scsi_Host *shost;
484362306a36Sopenharmony_ci	struct iscsi_internal *priv;
484462306a36Sopenharmony_ci
484562306a36Sopenharmony_ci	if (!scsi_is_host_device(dev))
484662306a36Sopenharmony_ci		return 0;
484762306a36Sopenharmony_ci
484862306a36Sopenharmony_ci	shost = dev_to_shost(dev);
484962306a36Sopenharmony_ci	if (!shost->transportt  ||
485062306a36Sopenharmony_ci	    shost->transportt->host_attrs.ac.class != &iscsi_host_class.class)
485162306a36Sopenharmony_ci		return 0;
485262306a36Sopenharmony_ci
485362306a36Sopenharmony_ci        priv = to_iscsi_internal(shost->transportt);
485462306a36Sopenharmony_ci        return &priv->t.host_attrs.ac == cont;
485562306a36Sopenharmony_ci}
485662306a36Sopenharmony_ci
485762306a36Sopenharmony_cistruct scsi_transport_template *
485862306a36Sopenharmony_ciiscsi_register_transport(struct iscsi_transport *tt)
485962306a36Sopenharmony_ci{
486062306a36Sopenharmony_ci	struct iscsi_internal *priv;
486162306a36Sopenharmony_ci	unsigned long flags;
486262306a36Sopenharmony_ci	int err;
486362306a36Sopenharmony_ci
486462306a36Sopenharmony_ci	BUG_ON(!tt);
486562306a36Sopenharmony_ci	WARN_ON(tt->ep_disconnect && !tt->unbind_conn);
486662306a36Sopenharmony_ci
486762306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(tt);
486862306a36Sopenharmony_ci	if (priv)
486962306a36Sopenharmony_ci		return NULL;
487062306a36Sopenharmony_ci
487162306a36Sopenharmony_ci	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
487262306a36Sopenharmony_ci	if (!priv)
487362306a36Sopenharmony_ci		return NULL;
487462306a36Sopenharmony_ci	INIT_LIST_HEAD(&priv->list);
487562306a36Sopenharmony_ci	priv->iscsi_transport = tt;
487662306a36Sopenharmony_ci	priv->t.user_scan = iscsi_user_scan;
487762306a36Sopenharmony_ci
487862306a36Sopenharmony_ci	priv->dev.class = &iscsi_transport_class;
487962306a36Sopenharmony_ci	dev_set_name(&priv->dev, "%s", tt->name);
488062306a36Sopenharmony_ci	err = device_register(&priv->dev);
488162306a36Sopenharmony_ci	if (err)
488262306a36Sopenharmony_ci		goto put_dev;
488362306a36Sopenharmony_ci
488462306a36Sopenharmony_ci	err = sysfs_create_group(&priv->dev.kobj, &iscsi_transport_group);
488562306a36Sopenharmony_ci	if (err)
488662306a36Sopenharmony_ci		goto unregister_dev;
488762306a36Sopenharmony_ci
488862306a36Sopenharmony_ci	/* host parameters */
488962306a36Sopenharmony_ci	priv->t.host_attrs.ac.class = &iscsi_host_class.class;
489062306a36Sopenharmony_ci	priv->t.host_attrs.ac.match = iscsi_host_match;
489162306a36Sopenharmony_ci	priv->t.host_attrs.ac.grp = &iscsi_host_group;
489262306a36Sopenharmony_ci	priv->t.host_size = sizeof(struct iscsi_cls_host);
489362306a36Sopenharmony_ci	transport_container_register(&priv->t.host_attrs);
489462306a36Sopenharmony_ci
489562306a36Sopenharmony_ci	/* connection parameters */
489662306a36Sopenharmony_ci	priv->conn_cont.ac.class = &iscsi_connection_class.class;
489762306a36Sopenharmony_ci	priv->conn_cont.ac.match = iscsi_conn_match;
489862306a36Sopenharmony_ci	priv->conn_cont.ac.grp = &iscsi_conn_group;
489962306a36Sopenharmony_ci	transport_container_register(&priv->conn_cont);
490062306a36Sopenharmony_ci
490162306a36Sopenharmony_ci	/* session parameters */
490262306a36Sopenharmony_ci	priv->session_cont.ac.class = &iscsi_session_class.class;
490362306a36Sopenharmony_ci	priv->session_cont.ac.match = iscsi_session_match;
490462306a36Sopenharmony_ci	priv->session_cont.ac.grp = &iscsi_session_group;
490562306a36Sopenharmony_ci	transport_container_register(&priv->session_cont);
490662306a36Sopenharmony_ci
490762306a36Sopenharmony_ci	spin_lock_irqsave(&iscsi_transport_lock, flags);
490862306a36Sopenharmony_ci	list_add(&priv->list, &iscsi_transports);
490962306a36Sopenharmony_ci	spin_unlock_irqrestore(&iscsi_transport_lock, flags);
491062306a36Sopenharmony_ci
491162306a36Sopenharmony_ci	printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name);
491262306a36Sopenharmony_ci	return &priv->t;
491362306a36Sopenharmony_ci
491462306a36Sopenharmony_ciunregister_dev:
491562306a36Sopenharmony_ci	device_unregister(&priv->dev);
491662306a36Sopenharmony_ci	return NULL;
491762306a36Sopenharmony_ciput_dev:
491862306a36Sopenharmony_ci	put_device(&priv->dev);
491962306a36Sopenharmony_ci	return NULL;
492062306a36Sopenharmony_ci}
492162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_register_transport);
492262306a36Sopenharmony_ci
492362306a36Sopenharmony_civoid iscsi_unregister_transport(struct iscsi_transport *tt)
492462306a36Sopenharmony_ci{
492562306a36Sopenharmony_ci	struct iscsi_internal *priv;
492662306a36Sopenharmony_ci	unsigned long flags;
492762306a36Sopenharmony_ci
492862306a36Sopenharmony_ci	BUG_ON(!tt);
492962306a36Sopenharmony_ci
493062306a36Sopenharmony_ci	mutex_lock(&rx_queue_mutex);
493162306a36Sopenharmony_ci
493262306a36Sopenharmony_ci	priv = iscsi_if_transport_lookup(tt);
493362306a36Sopenharmony_ci	BUG_ON (!priv);
493462306a36Sopenharmony_ci
493562306a36Sopenharmony_ci	spin_lock_irqsave(&iscsi_transport_lock, flags);
493662306a36Sopenharmony_ci	list_del(&priv->list);
493762306a36Sopenharmony_ci	spin_unlock_irqrestore(&iscsi_transport_lock, flags);
493862306a36Sopenharmony_ci
493962306a36Sopenharmony_ci	transport_container_unregister(&priv->conn_cont);
494062306a36Sopenharmony_ci	transport_container_unregister(&priv->session_cont);
494162306a36Sopenharmony_ci	transport_container_unregister(&priv->t.host_attrs);
494262306a36Sopenharmony_ci
494362306a36Sopenharmony_ci	sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group);
494462306a36Sopenharmony_ci	device_unregister(&priv->dev);
494562306a36Sopenharmony_ci	mutex_unlock(&rx_queue_mutex);
494662306a36Sopenharmony_ci}
494762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_unregister_transport);
494862306a36Sopenharmony_ci
494962306a36Sopenharmony_civoid iscsi_dbg_trace(void (*trace)(struct device *dev, struct va_format *),
495062306a36Sopenharmony_ci		     struct device *dev, const char *fmt, ...)
495162306a36Sopenharmony_ci{
495262306a36Sopenharmony_ci	struct va_format vaf;
495362306a36Sopenharmony_ci	va_list args;
495462306a36Sopenharmony_ci
495562306a36Sopenharmony_ci	va_start(args, fmt);
495662306a36Sopenharmony_ci	vaf.fmt = fmt;
495762306a36Sopenharmony_ci	vaf.va = &args;
495862306a36Sopenharmony_ci	trace(dev, &vaf);
495962306a36Sopenharmony_ci	va_end(args);
496062306a36Sopenharmony_ci}
496162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_dbg_trace);
496262306a36Sopenharmony_ci
496362306a36Sopenharmony_cistatic __init int iscsi_transport_init(void)
496462306a36Sopenharmony_ci{
496562306a36Sopenharmony_ci	int err;
496662306a36Sopenharmony_ci	struct netlink_kernel_cfg cfg = {
496762306a36Sopenharmony_ci		.groups	= 1,
496862306a36Sopenharmony_ci		.input	= iscsi_if_rx,
496962306a36Sopenharmony_ci	};
497062306a36Sopenharmony_ci	printk(KERN_INFO "Loading iSCSI transport class v%s.\n",
497162306a36Sopenharmony_ci		ISCSI_TRANSPORT_VERSION);
497262306a36Sopenharmony_ci
497362306a36Sopenharmony_ci	atomic_set(&iscsi_session_nr, 0);
497462306a36Sopenharmony_ci
497562306a36Sopenharmony_ci	err = class_register(&iscsi_transport_class);
497662306a36Sopenharmony_ci	if (err)
497762306a36Sopenharmony_ci		return err;
497862306a36Sopenharmony_ci
497962306a36Sopenharmony_ci	err = class_register(&iscsi_endpoint_class);
498062306a36Sopenharmony_ci	if (err)
498162306a36Sopenharmony_ci		goto unregister_transport_class;
498262306a36Sopenharmony_ci
498362306a36Sopenharmony_ci	err = class_register(&iscsi_iface_class);
498462306a36Sopenharmony_ci	if (err)
498562306a36Sopenharmony_ci		goto unregister_endpoint_class;
498662306a36Sopenharmony_ci
498762306a36Sopenharmony_ci	err = transport_class_register(&iscsi_host_class);
498862306a36Sopenharmony_ci	if (err)
498962306a36Sopenharmony_ci		goto unregister_iface_class;
499062306a36Sopenharmony_ci
499162306a36Sopenharmony_ci	err = transport_class_register(&iscsi_connection_class);
499262306a36Sopenharmony_ci	if (err)
499362306a36Sopenharmony_ci		goto unregister_host_class;
499462306a36Sopenharmony_ci
499562306a36Sopenharmony_ci	err = transport_class_register(&iscsi_session_class);
499662306a36Sopenharmony_ci	if (err)
499762306a36Sopenharmony_ci		goto unregister_conn_class;
499862306a36Sopenharmony_ci
499962306a36Sopenharmony_ci	err = bus_register(&iscsi_flashnode_bus);
500062306a36Sopenharmony_ci	if (err)
500162306a36Sopenharmony_ci		goto unregister_session_class;
500262306a36Sopenharmony_ci
500362306a36Sopenharmony_ci	nls = netlink_kernel_create(&init_net, NETLINK_ISCSI, &cfg);
500462306a36Sopenharmony_ci	if (!nls) {
500562306a36Sopenharmony_ci		err = -ENOBUFS;
500662306a36Sopenharmony_ci		goto unregister_flashnode_bus;
500762306a36Sopenharmony_ci	}
500862306a36Sopenharmony_ci
500962306a36Sopenharmony_ci	iscsi_conn_cleanup_workq = alloc_workqueue("%s",
501062306a36Sopenharmony_ci			WQ_SYSFS | WQ_MEM_RECLAIM | WQ_UNBOUND, 0,
501162306a36Sopenharmony_ci			"iscsi_conn_cleanup");
501262306a36Sopenharmony_ci	if (!iscsi_conn_cleanup_workq) {
501362306a36Sopenharmony_ci		err = -ENOMEM;
501462306a36Sopenharmony_ci		goto release_nls;
501562306a36Sopenharmony_ci	}
501662306a36Sopenharmony_ci
501762306a36Sopenharmony_ci	return 0;
501862306a36Sopenharmony_ci
501962306a36Sopenharmony_cirelease_nls:
502062306a36Sopenharmony_ci	netlink_kernel_release(nls);
502162306a36Sopenharmony_ciunregister_flashnode_bus:
502262306a36Sopenharmony_ci	bus_unregister(&iscsi_flashnode_bus);
502362306a36Sopenharmony_ciunregister_session_class:
502462306a36Sopenharmony_ci	transport_class_unregister(&iscsi_session_class);
502562306a36Sopenharmony_ciunregister_conn_class:
502662306a36Sopenharmony_ci	transport_class_unregister(&iscsi_connection_class);
502762306a36Sopenharmony_ciunregister_host_class:
502862306a36Sopenharmony_ci	transport_class_unregister(&iscsi_host_class);
502962306a36Sopenharmony_ciunregister_iface_class:
503062306a36Sopenharmony_ci	class_unregister(&iscsi_iface_class);
503162306a36Sopenharmony_ciunregister_endpoint_class:
503262306a36Sopenharmony_ci	class_unregister(&iscsi_endpoint_class);
503362306a36Sopenharmony_ciunregister_transport_class:
503462306a36Sopenharmony_ci	class_unregister(&iscsi_transport_class);
503562306a36Sopenharmony_ci	return err;
503662306a36Sopenharmony_ci}
503762306a36Sopenharmony_ci
503862306a36Sopenharmony_cistatic void __exit iscsi_transport_exit(void)
503962306a36Sopenharmony_ci{
504062306a36Sopenharmony_ci	destroy_workqueue(iscsi_conn_cleanup_workq);
504162306a36Sopenharmony_ci	netlink_kernel_release(nls);
504262306a36Sopenharmony_ci	bus_unregister(&iscsi_flashnode_bus);
504362306a36Sopenharmony_ci	transport_class_unregister(&iscsi_connection_class);
504462306a36Sopenharmony_ci	transport_class_unregister(&iscsi_session_class);
504562306a36Sopenharmony_ci	transport_class_unregister(&iscsi_host_class);
504662306a36Sopenharmony_ci	class_unregister(&iscsi_endpoint_class);
504762306a36Sopenharmony_ci	class_unregister(&iscsi_iface_class);
504862306a36Sopenharmony_ci	class_unregister(&iscsi_transport_class);
504962306a36Sopenharmony_ci}
505062306a36Sopenharmony_ci
505162306a36Sopenharmony_cimodule_init(iscsi_transport_init);
505262306a36Sopenharmony_cimodule_exit(iscsi_transport_exit);
505362306a36Sopenharmony_ci
505462306a36Sopenharmony_ciMODULE_AUTHOR("Mike Christie <michaelc@cs.wisc.edu>, "
505562306a36Sopenharmony_ci	      "Dmitry Yusupov <dmitry_yus@yahoo.com>, "
505662306a36Sopenharmony_ci	      "Alex Aizman <itn780@yahoo.com>");
505762306a36Sopenharmony_ciMODULE_DESCRIPTION("iSCSI Transport Interface");
505862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
505962306a36Sopenharmony_ciMODULE_VERSION(ISCSI_TRANSPORT_VERSION);
506062306a36Sopenharmony_ciMODULE_ALIAS_NET_PF_PROTO(PF_NETLINK, NETLINK_ISCSI);
5061