⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi_transport_iscsi.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * iSCSI transport class definitions * * Copyright (C) IBM Corporation, 2004 * Copyright (C) Mike Christie, 2004 - 2005 * Copyright (C) Dmitry Yusupov, 2004 - 2005 * Copyright (C) Alex Aizman, 2004 - 2005 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/module.h>#include <linux/mutex.h>#include <net/tcp.h>#include <scsi/scsi.h>#include <scsi/scsi_host.h>#include <scsi/scsi_device.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_iscsi.h>#include <scsi/iscsi_if.h>#define ISCSI_SESSION_ATTRS 15#define ISCSI_CONN_ATTRS 11#define ISCSI_HOST_ATTRS 4#define ISCSI_TRANSPORT_VERSION "2.0-724"struct iscsi_internal {	int daemon_pid;	struct scsi_transport_template t;	struct iscsi_transport *iscsi_transport;	struct list_head list;	struct class_device cdev;	struct class_device_attribute *host_attrs[ISCSI_HOST_ATTRS + 1];	struct transport_container conn_cont;	struct class_device_attribute *conn_attrs[ISCSI_CONN_ATTRS + 1];	struct transport_container session_cont;	struct class_device_attribute *session_attrs[ISCSI_SESSION_ATTRS + 1];};static atomic_t iscsi_session_nr; /* sysfs session id for next new session *//* * list of registered transports and lock that must * be held while accessing list. The iscsi_transport_lock must * be acquired after the rx_queue_mutex. */static LIST_HEAD(iscsi_transports);static DEFINE_SPINLOCK(iscsi_transport_lock);#define to_iscsi_internal(tmpl) \	container_of(tmpl, struct iscsi_internal, t)#define cdev_to_iscsi_internal(_cdev) \	container_of(_cdev, struct iscsi_internal, cdev)static void iscsi_transport_release(struct class_device *cdev){	struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);	kfree(priv);}/* * iscsi_transport_class represents the iscsi_transports that are * registered. */static struct class iscsi_transport_class = {	.name = "iscsi_transport",	.release = iscsi_transport_release,};static ssize_tshow_transport_handle(struct class_device *cdev, char *buf){	struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);	return sprintf(buf, "%llu\n", (unsigned long long)iscsi_handle(priv->iscsi_transport));}static CLASS_DEVICE_ATTR(handle, S_IRUGO, show_transport_handle, NULL);#define show_transport_attr(name, format)				\static ssize_t								\show_transport_##name(struct class_device *cdev, char *buf)		\{									\	struct iscsi_internal *priv = cdev_to_iscsi_internal(cdev);	\	return sprintf(buf, format"\n", priv->iscsi_transport->name);	\}									\static CLASS_DEVICE_ATTR(name, S_IRUGO, show_transport_##name, NULL);show_transport_attr(caps, "0x%x");show_transport_attr(max_lun, "%d");show_transport_attr(max_conn, "%d");show_transport_attr(max_cmd_len, "%d");static struct attribute *iscsi_transport_attrs[] = {	&class_device_attr_handle.attr,	&class_device_attr_caps.attr,	&class_device_attr_max_lun.attr,	&class_device_attr_max_conn.attr,	&class_device_attr_max_cmd_len.attr,	NULL,};static struct attribute_group iscsi_transport_group = {	.attrs = iscsi_transport_attrs,};static int iscsi_setup_host(struct transport_container *tc, struct device *dev,			    struct class_device *cdev){	struct Scsi_Host *shost = dev_to_shost(dev);	struct iscsi_host *ihost = shost->shost_data;	memset(ihost, 0, sizeof(*ihost));	INIT_LIST_HEAD(&ihost->sessions);	mutex_init(&ihost->mutex);	return 0;}static DECLARE_TRANSPORT_CLASS(iscsi_host_class,			       "iscsi_host",			       iscsi_setup_host,			       NULL,			       NULL);static DECLARE_TRANSPORT_CLASS(iscsi_session_class,			       "iscsi_session",			       NULL,			       NULL,			       NULL);static DECLARE_TRANSPORT_CLASS(iscsi_connection_class,			       "iscsi_connection",			       NULL,			       NULL,			       NULL);static struct sock *nls;static DEFINE_MUTEX(rx_queue_mutex);static LIST_HEAD(sesslist);static DEFINE_SPINLOCK(sesslock);static LIST_HEAD(connlist);static DEFINE_SPINLOCK(connlock);static uint32_t iscsi_conn_get_sid(struct iscsi_cls_conn *conn){	struct iscsi_cls_session *sess = iscsi_dev_to_session(conn->dev.parent);	return sess->sid;}/* * Returns the matching session to a given sid */static struct iscsi_cls_session *iscsi_session_lookup(uint32_t sid){	unsigned long flags;	struct iscsi_cls_session *sess;	spin_lock_irqsave(&sesslock, flags);	list_for_each_entry(sess, &sesslist, sess_list) {		if (sess->sid == sid) {			spin_unlock_irqrestore(&sesslock, flags);			return sess;		}	}	spin_unlock_irqrestore(&sesslock, flags);	return NULL;}/* * Returns the matching connection to a given sid / cid tuple */static struct iscsi_cls_conn *iscsi_conn_lookup(uint32_t sid, uint32_t cid){	unsigned long flags;	struct iscsi_cls_conn *conn;	spin_lock_irqsave(&connlock, flags);	list_for_each_entry(conn, &connlist, conn_list) {		if ((conn->cid == cid) && (iscsi_conn_get_sid(conn) == sid)) {			spin_unlock_irqrestore(&connlock, flags);			return conn;		}	}	spin_unlock_irqrestore(&connlock, flags);	return NULL;}/* * The following functions can be used by LLDs that allocate * their own scsi_hosts or by software iscsi LLDs */static void iscsi_session_release(struct device *dev){	struct iscsi_cls_session *session = iscsi_dev_to_session(dev);	struct Scsi_Host *shost;	shost = iscsi_session_to_shost(session);	scsi_host_put(shost);	kfree(session);}static int iscsi_is_session_dev(const struct device *dev){	return dev->release == iscsi_session_release;}static int iscsi_user_scan(struct Scsi_Host *shost, uint channel,			   uint id, uint lun){	struct iscsi_host *ihost = shost->shost_data;	struct iscsi_cls_session *session;	mutex_lock(&ihost->mutex);	list_for_each_entry(session, &ihost->sessions, host_list) {		if ((channel == SCAN_WILD_CARD || channel == 0) &&		    (id == SCAN_WILD_CARD || id == session->target_id))			scsi_scan_target(&session->dev, 0,					 session->target_id, lun, 1);	}	mutex_unlock(&ihost->mutex);	return 0;}static void session_recovery_timedout(struct work_struct *work){	struct iscsi_cls_session *session =		container_of(work, struct iscsi_cls_session,			     recovery_work.work);	dev_printk(KERN_INFO, &session->dev, "iscsi: session recovery timed "		  "out after %d secs\n", session->recovery_tmo);	if (session->transport->session_recovery_timedout)		session->transport->session_recovery_timedout(session);	scsi_target_unblock(&session->dev);}void iscsi_unblock_session(struct iscsi_cls_session *session){	if (!cancel_delayed_work(&session->recovery_work))		flush_scheduled_work();	scsi_target_unblock(&session->dev);}EXPORT_SYMBOL_GPL(iscsi_unblock_session);void iscsi_block_session(struct iscsi_cls_session *session){	scsi_target_block(&session->dev);	schedule_delayed_work(&session->recovery_work,			     session->recovery_tmo * HZ);}EXPORT_SYMBOL_GPL(iscsi_block_session);struct iscsi_cls_session *iscsi_alloc_session(struct Scsi_Host *shost,		    struct iscsi_transport *transport){	struct iscsi_cls_session *session;	session = kzalloc(sizeof(*session) + transport->sessiondata_size,			  GFP_KERNEL);	if (!session)		return NULL;	session->transport = transport;	session->recovery_tmo = 120;	INIT_DELAYED_WORK(&session->recovery_work, session_recovery_timedout);	INIT_LIST_HEAD(&session->host_list);	INIT_LIST_HEAD(&session->sess_list);	/* this is released in the dev's release function */	scsi_host_get(shost);	session->dev.parent = &shost->shost_gendev;	session->dev.release = iscsi_session_release;	device_initialize(&session->dev);	if (transport->sessiondata_size)		session->dd_data = &session[1];	return session;}EXPORT_SYMBOL_GPL(iscsi_alloc_session);int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id){	struct Scsi_Host *shost = iscsi_session_to_shost(session);	struct iscsi_host *ihost;	int err;	ihost = shost->shost_data;	session->sid = atomic_add_return(1, &iscsi_session_nr);	session->target_id = target_id;	snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u",		 session->sid);	err = device_add(&session->dev);	if (err) {		dev_printk(KERN_ERR, &session->dev, "iscsi: could not "			   "register session's dev\n");		goto release_host;	}	transport_register_device(&session->dev);	mutex_lock(&ihost->mutex);	list_add(&session->host_list, &ihost->sessions);	mutex_unlock(&ihost->mutex);	return 0;release_host:	scsi_host_put(shost);	return err;}EXPORT_SYMBOL_GPL(iscsi_add_session);/** * iscsi_create_session - create iscsi class session * @shost: scsi host * @transport: iscsi transport * * This can be called from a LLD or iscsi_transport. **/struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost,		     struct iscsi_transport *transport,		     unsigned int target_id){	struct iscsi_cls_session *session;	session = iscsi_alloc_session(shost, transport);	if (!session)		return NULL;	if (iscsi_add_session(session, target_id)) {		iscsi_free_session(session);		return NULL;	}	return session;}EXPORT_SYMBOL_GPL(iscsi_create_session);void iscsi_remove_session(struct iscsi_cls_session *session){	struct Scsi_Host *shost = iscsi_session_to_shost(session);	struct iscsi_host *ihost = shost->shost_data;	if (!cancel_delayed_work(&session->recovery_work))		flush_scheduled_work();	mutex_lock(&ihost->mutex);	list_del(&session->host_list);	mutex_unlock(&ihost->mutex);	scsi_remove_target(&session->dev);	transport_unregister_device(&session->dev);	device_del(&session->dev);}EXPORT_SYMBOL_GPL(iscsi_remove_session);void iscsi_free_session(struct iscsi_cls_session *session){	put_device(&session->dev);}EXPORT_SYMBOL_GPL(iscsi_free_session);/** * iscsi_destroy_session - destroy iscsi session * @session: iscsi_session * * Can be called by a LLD or iscsi_transport. There must not be * any running connections. **/int iscsi_destroy_session(struct iscsi_cls_session *session){	iscsi_remove_session(session);	iscsi_free_session(session);	return 0;}EXPORT_SYMBOL_GPL(iscsi_destroy_session);static void iscsi_conn_release(struct device *dev){	struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev);	struct device *parent = conn->dev.parent;	kfree(conn);	put_device(parent);}static int iscsi_is_conn_dev(const struct device *dev){	return dev->release == iscsi_conn_release;}/** * iscsi_create_conn - create iscsi class connection * @session: iscsi cls session * @cid: connection id * * This can be called from a LLD or iscsi_transport. The connection * is child of the session so cid must be unique for all connections * on the session. * * Since we do not support MCS, cid will normally be zero. In some cases * for software iscsi we could be trying to preallocate a connection struct * in which case there could be two connection structs and cid would be * non-zero. **/struct iscsi_cls_conn *iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid){	struct iscsi_transport *transport = session->transport;	struct iscsi_cls_conn *conn;	int err;	conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL);	if (!conn)		return NULL;	if (transport->conndata_size)		conn->dd_data = &conn[1];	INIT_LIST_HEAD(&conn->conn_list);	conn->transport = transport;	conn->cid = cid;	/* this is released in the dev's release function */	if (!get_device(&session->dev))		goto free_conn;	snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u",		 session->sid, cid);	conn->dev.parent = &session->dev;	conn->dev.release = iscsi_conn_release;	err = device_register(&conn->dev);	if (err) {		dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register "			   "connection's dev\n");		goto release_parent_ref;	}	transport_register_device(&conn->dev);	return conn;release_parent_ref:	put_device(&session->dev);free_conn:	kfree(conn);	return NULL;}EXPORT_SYMBOL_GPL(iscsi_create_conn);/** * iscsi_destroy_conn - destroy iscsi class connection * @session: iscsi cls session * * This can be called from a LLD or iscsi_transport. **/int iscsi_destroy_conn(struct iscsi_cls_conn *conn){	transport_unregister_device(&conn->dev);	device_unregister(&conn->dev);	return 0;}EXPORT_SYMBOL_GPL(iscsi_destroy_conn);/* * iscsi interface functions */static struct iscsi_internal *iscsi_if_transport_lookup(struct iscsi_transport *tt){	struct iscsi_internal *priv;	unsigned long flags;	spin_lock_irqsave(&iscsi_transport_lock, flags);	list_for_each_entry(priv, &iscsi_transports, list) {		if (tt == priv->iscsi_transport) {			spin_unlock_irqrestore(&iscsi_transport_lock, flags);			return priv;		}	}	spin_unlock_irqrestore(&iscsi_transport_lock, flags);	return NULL;}static intiscsi_broadcast_skb(struct sk_buff *skb, gfp_t gfp){	int rc;	rc = netlink_broadcast(nls, skb, 0, 1, gfp);	if (rc < 0) {		printk(KERN_ERR "iscsi: can not broadcast skb (%d)\n", rc);		return rc;	}	return 0;}static intiscsi_unicast_skb(struct sk_buff *skb, int pid){	int rc;

⌨️ 快捷键说明

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