📄 scsi_transport_iscsi.c
字号:
/* * 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 + -