diff options
author | Mike Christie <michaelc@cs.wisc.edu> | 2008-09-24 12:46:10 -0400 |
---|---|---|
committer | James Bottomley <James.Bottomley@HansenPartnership.com> | 2008-10-13 09:28:59 -0400 |
commit | e5bd7b54e93ef7151469a12b8c28d863b9f8a088 (patch) | |
tree | b1e63758dc0272346b7d5e9af6435a87fd94a7ad /drivers | |
parent | 1d9edf0270cb5a434d32e95279ce9493581906b3 (diff) |
[SCSI] libiscsi: Support drivers initiating session removal
If the driver knows when hardware is removed like with cxgb3i,
bnx2i, qla4xxx and iser then we will want to remove the sessions/devices
that are bound to that device before removing the host.
cxgb3i and in the future bnx2i will remove the host and that will
remove all the sessions on the hba. iser can call iscsi_kill_session
when it gets an event that indicates that a hca is removed.
And when qla4xxx is hooked in to the lib (it is only hooked into
the class right now) it can call iscsi remove host like the
partial offload card drivers.
Signed-off-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/infiniband/ulp/iser/iscsi_iser.c | 1 | ||||
-rw-r--r-- | drivers/scsi/iscsi_tcp.c | 1 | ||||
-rw-r--r-- | drivers/scsi/libiscsi.c | 107 | ||||
-rw-r--r-- | drivers/scsi/qla4xxx/ql4_os.c | 2 | ||||
-rw-r--r-- | drivers/scsi/scsi_transport_iscsi.c | 6 |
5 files changed, 105 insertions, 12 deletions
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 5a1cf2580e16..0474da173eb1 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c | |||
@@ -378,6 +378,7 @@ static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session) | |||
378 | { | 378 | { |
379 | struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); | 379 | struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); |
380 | 380 | ||
381 | iscsi_session_teardown(cls_session); | ||
381 | iscsi_host_remove(shost); | 382 | iscsi_host_remove(shost); |
382 | iscsi_host_free(shost); | 383 | iscsi_host_free(shost); |
383 | } | 384 | } |
diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index e960f00da93a..752f42884cc1 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c | |||
@@ -1885,6 +1885,7 @@ static void iscsi_tcp_session_destroy(struct iscsi_cls_session *cls_session) | |||
1885 | struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); | 1885 | struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); |
1886 | 1886 | ||
1887 | iscsi_r2tpool_free(cls_session->dd_data); | 1887 | iscsi_r2tpool_free(cls_session->dd_data); |
1888 | iscsi_session_teardown(cls_session); | ||
1888 | 1889 | ||
1889 | iscsi_host_remove(shost); | 1890 | iscsi_host_remove(shost); |
1890 | iscsi_host_free(shost); | 1891 | iscsi_host_free(shost); |
diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index f9539af28f02..390781894be9 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c | |||
@@ -983,6 +983,38 @@ struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt) | |||
983 | } | 983 | } |
984 | EXPORT_SYMBOL_GPL(iscsi_itt_to_ctask); | 984 | EXPORT_SYMBOL_GPL(iscsi_itt_to_ctask); |
985 | 985 | ||
986 | void iscsi_session_failure(struct iscsi_cls_session *cls_session, | ||
987 | enum iscsi_err err) | ||
988 | { | ||
989 | struct iscsi_session *session = cls_session->dd_data; | ||
990 | struct iscsi_conn *conn; | ||
991 | struct device *dev; | ||
992 | unsigned long flags; | ||
993 | |||
994 | spin_lock_irqsave(&session->lock, flags); | ||
995 | conn = session->leadconn; | ||
996 | if (session->state == ISCSI_STATE_TERMINATE || !conn) { | ||
997 | spin_unlock_irqrestore(&session->lock, flags); | ||
998 | return; | ||
999 | } | ||
1000 | |||
1001 | dev = get_device(&conn->cls_conn->dev); | ||
1002 | spin_unlock_irqrestore(&session->lock, flags); | ||
1003 | if (!dev) | ||
1004 | return; | ||
1005 | /* | ||
1006 | * if the host is being removed bypass the connection | ||
1007 | * recovery initialization because we are going to kill | ||
1008 | * the session. | ||
1009 | */ | ||
1010 | if (err == ISCSI_ERR_INVALID_HOST) | ||
1011 | iscsi_conn_error_event(conn->cls_conn, err); | ||
1012 | else | ||
1013 | iscsi_conn_failure(conn, err); | ||
1014 | put_device(dev); | ||
1015 | } | ||
1016 | EXPORT_SYMBOL_GPL(iscsi_session_failure); | ||
1017 | |||
986 | void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) | 1018 | void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) |
987 | { | 1019 | { |
988 | struct iscsi_session *session = conn->session; | 1020 | struct iscsi_session *session = conn->session; |
@@ -997,9 +1029,10 @@ void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) | |||
997 | if (conn->stop_stage == 0) | 1029 | if (conn->stop_stage == 0) |
998 | session->state = ISCSI_STATE_FAILED; | 1030 | session->state = ISCSI_STATE_FAILED; |
999 | spin_unlock_irqrestore(&session->lock, flags); | 1031 | spin_unlock_irqrestore(&session->lock, flags); |
1032 | |||
1000 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); | 1033 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); |
1001 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); | 1034 | set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); |
1002 | iscsi_conn_error(conn->cls_conn, err); | 1035 | iscsi_conn_error_event(conn->cls_conn, err); |
1003 | } | 1036 | } |
1004 | EXPORT_SYMBOL_GPL(iscsi_conn_failure); | 1037 | EXPORT_SYMBOL_GPL(iscsi_conn_failure); |
1005 | 1038 | ||
@@ -1905,6 +1938,7 @@ struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, | |||
1905 | int dd_data_size, uint16_t qdepth) | 1938 | int dd_data_size, uint16_t qdepth) |
1906 | { | 1939 | { |
1907 | struct Scsi_Host *shost; | 1940 | struct Scsi_Host *shost; |
1941 | struct iscsi_host *ihost; | ||
1908 | 1942 | ||
1909 | shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); | 1943 | shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); |
1910 | if (!shost) | 1944 | if (!shost) |
@@ -1919,22 +1953,43 @@ struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, | |||
1919 | qdepth = ISCSI_DEF_CMD_PER_LUN; | 1953 | qdepth = ISCSI_DEF_CMD_PER_LUN; |
1920 | } | 1954 | } |
1921 | shost->cmd_per_lun = qdepth; | 1955 | shost->cmd_per_lun = qdepth; |
1956 | |||
1957 | ihost = shost_priv(shost); | ||
1958 | spin_lock_init(&ihost->lock); | ||
1959 | ihost->state = ISCSI_HOST_SETUP; | ||
1960 | ihost->num_sessions = 0; | ||
1961 | init_waitqueue_head(&ihost->session_removal_wq); | ||
1922 | return shost; | 1962 | return shost; |
1923 | } | 1963 | } |
1924 | EXPORT_SYMBOL_GPL(iscsi_host_alloc); | 1964 | EXPORT_SYMBOL_GPL(iscsi_host_alloc); |
1925 | 1965 | ||
1966 | static void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session) | ||
1967 | { | ||
1968 | iscsi_session_failure(cls_session, ISCSI_ERR_INVALID_HOST); | ||
1969 | } | ||
1970 | |||
1926 | /** | 1971 | /** |
1927 | * iscsi_host_remove - remove host and sessions | 1972 | * iscsi_host_remove - remove host and sessions |
1928 | * @shost: scsi host | 1973 | * @shost: scsi host |
1929 | * | 1974 | * |
1930 | * This will also remove any sessions attached to the host, but if userspace | 1975 | * If there are any sessions left, this will initiate the removal and wait |
1931 | * is managing the session at the same time this will break. TODO: add | 1976 | * for the completion. |
1932 | * refcounting to the netlink iscsi interface so a rmmod or host hot unplug | ||
1933 | * does not remove the memory from under us. | ||
1934 | */ | 1977 | */ |
1935 | void iscsi_host_remove(struct Scsi_Host *shost) | 1978 | void iscsi_host_remove(struct Scsi_Host *shost) |
1936 | { | 1979 | { |
1937 | iscsi_host_for_each_session(shost, iscsi_session_teardown); | 1980 | struct iscsi_host *ihost = shost_priv(shost); |
1981 | unsigned long flags; | ||
1982 | |||
1983 | spin_lock_irqsave(&ihost->lock, flags); | ||
1984 | ihost->state = ISCSI_HOST_REMOVED; | ||
1985 | spin_unlock_irqrestore(&ihost->lock, flags); | ||
1986 | |||
1987 | iscsi_host_for_each_session(shost, iscsi_notify_host_removed); | ||
1988 | wait_event_interruptible(ihost->session_removal_wq, | ||
1989 | ihost->num_sessions == 0); | ||
1990 | if (signal_pending(current)) | ||
1991 | flush_signals(current); | ||
1992 | |||
1938 | scsi_remove_host(shost); | 1993 | scsi_remove_host(shost); |
1939 | } | 1994 | } |
1940 | EXPORT_SYMBOL_GPL(iscsi_host_remove); | 1995 | EXPORT_SYMBOL_GPL(iscsi_host_remove); |
@@ -1950,6 +2005,27 @@ void iscsi_host_free(struct Scsi_Host *shost) | |||
1950 | } | 2005 | } |
1951 | EXPORT_SYMBOL_GPL(iscsi_host_free); | 2006 | EXPORT_SYMBOL_GPL(iscsi_host_free); |
1952 | 2007 | ||
2008 | static void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) | ||
2009 | { | ||
2010 | struct iscsi_host *ihost = shost_priv(shost); | ||
2011 | unsigned long flags; | ||
2012 | |||
2013 | shost = scsi_host_get(shost); | ||
2014 | if (!shost) { | ||
2015 | printk(KERN_ERR "Invalid state. Cannot notify host removal " | ||
2016 | "of session teardown event because host already " | ||
2017 | "removed.\n"); | ||
2018 | return; | ||
2019 | } | ||
2020 | |||
2021 | spin_lock_irqsave(&ihost->lock, flags); | ||
2022 | ihost->num_sessions--; | ||
2023 | if (ihost->num_sessions == 0) | ||
2024 | wake_up(&ihost->session_removal_wq); | ||
2025 | spin_unlock_irqrestore(&ihost->lock, flags); | ||
2026 | scsi_host_put(shost); | ||
2027 | } | ||
2028 | |||
1953 | /** | 2029 | /** |
1954 | * iscsi_session_setup - create iscsi cls session and host and session | 2030 | * iscsi_session_setup - create iscsi cls session and host and session |
1955 | * @iscsit: iscsi transport template | 2031 | * @iscsit: iscsi transport template |
@@ -1970,9 +2046,19 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, | |||
1970 | uint16_t cmds_max, int cmd_task_size, | 2046 | uint16_t cmds_max, int cmd_task_size, |
1971 | uint32_t initial_cmdsn, unsigned int id) | 2047 | uint32_t initial_cmdsn, unsigned int id) |
1972 | { | 2048 | { |
2049 | struct iscsi_host *ihost = shost_priv(shost); | ||
1973 | struct iscsi_session *session; | 2050 | struct iscsi_session *session; |
1974 | struct iscsi_cls_session *cls_session; | 2051 | struct iscsi_cls_session *cls_session; |
1975 | int cmd_i, scsi_cmds, total_cmds = cmds_max; | 2052 | int cmd_i, scsi_cmds, total_cmds = cmds_max; |
2053 | unsigned long flags; | ||
2054 | |||
2055 | spin_lock_irqsave(&ihost->lock, flags); | ||
2056 | if (ihost->state == ISCSI_HOST_REMOVED) { | ||
2057 | spin_unlock_irqrestore(&ihost->lock, flags); | ||
2058 | return NULL; | ||
2059 | } | ||
2060 | ihost->num_sessions++; | ||
2061 | spin_unlock_irqrestore(&ihost->lock, flags); | ||
1976 | 2062 | ||
1977 | if (!total_cmds) | 2063 | if (!total_cmds) |
1978 | total_cmds = ISCSI_DEF_XMIT_CMDS_MAX; | 2064 | total_cmds = ISCSI_DEF_XMIT_CMDS_MAX; |
@@ -1985,7 +2071,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, | |||
1985 | printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue " | 2071 | printk(KERN_ERR "iscsi: invalid can_queue of %d. can_queue " |
1986 | "must be a power of two that is at least %d.\n", | 2072 | "must be a power of two that is at least %d.\n", |
1987 | total_cmds, ISCSI_TOTAL_CMDS_MIN); | 2073 | total_cmds, ISCSI_TOTAL_CMDS_MIN); |
1988 | return NULL; | 2074 | goto dec_session_count; |
1989 | } | 2075 | } |
1990 | 2076 | ||
1991 | if (total_cmds > ISCSI_TOTAL_CMDS_MAX) { | 2077 | if (total_cmds > ISCSI_TOTAL_CMDS_MAX) { |
@@ -2009,7 +2095,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, | |||
2009 | cls_session = iscsi_alloc_session(shost, iscsit, | 2095 | cls_session = iscsi_alloc_session(shost, iscsit, |
2010 | sizeof(struct iscsi_session)); | 2096 | sizeof(struct iscsi_session)); |
2011 | if (!cls_session) | 2097 | if (!cls_session) |
2012 | return NULL; | 2098 | goto dec_session_count; |
2013 | session = cls_session->dd_data; | 2099 | session = cls_session->dd_data; |
2014 | session->cls_session = cls_session; | 2100 | session->cls_session = cls_session; |
2015 | session->host = shost; | 2101 | session->host = shost; |
@@ -2048,6 +2134,7 @@ iscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, | |||
2048 | 2134 | ||
2049 | if (iscsi_add_session(cls_session, id)) | 2135 | if (iscsi_add_session(cls_session, id)) |
2050 | goto cls_session_fail; | 2136 | goto cls_session_fail; |
2137 | |||
2051 | return cls_session; | 2138 | return cls_session; |
2052 | 2139 | ||
2053 | cls_session_fail: | 2140 | cls_session_fail: |
@@ -2056,6 +2143,8 @@ module_get_fail: | |||
2056 | iscsi_pool_free(&session->cmdpool); | 2143 | iscsi_pool_free(&session->cmdpool); |
2057 | cmdpool_alloc_fail: | 2144 | cmdpool_alloc_fail: |
2058 | iscsi_free_session(cls_session); | 2145 | iscsi_free_session(cls_session); |
2146 | dec_session_count: | ||
2147 | iscsi_host_dec_session_cnt(shost); | ||
2059 | return NULL; | 2148 | return NULL; |
2060 | } | 2149 | } |
2061 | EXPORT_SYMBOL_GPL(iscsi_session_setup); | 2150 | EXPORT_SYMBOL_GPL(iscsi_session_setup); |
@@ -2071,6 +2160,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) | |||
2071 | { | 2160 | { |
2072 | struct iscsi_session *session = cls_session->dd_data; | 2161 | struct iscsi_session *session = cls_session->dd_data; |
2073 | struct module *owner = cls_session->transport->owner; | 2162 | struct module *owner = cls_session->transport->owner; |
2163 | struct Scsi_Host *shost = session->host; | ||
2074 | 2164 | ||
2075 | iscsi_pool_free(&session->cmdpool); | 2165 | iscsi_pool_free(&session->cmdpool); |
2076 | 2166 | ||
@@ -2083,6 +2173,7 @@ void iscsi_session_teardown(struct iscsi_cls_session *cls_session) | |||
2083 | kfree(session->ifacename); | 2173 | kfree(session->ifacename); |
2084 | 2174 | ||
2085 | iscsi_destroy_session(cls_session); | 2175 | iscsi_destroy_session(cls_session); |
2176 | iscsi_host_dec_session_cnt(shost); | ||
2086 | module_put(owner); | 2177 | module_put(owner); |
2087 | } | 2178 | } |
2088 | EXPORT_SYMBOL_GPL(iscsi_session_teardown); | 2179 | EXPORT_SYMBOL_GPL(iscsi_session_teardown); |
diff --git a/drivers/scsi/qla4xxx/ql4_os.c b/drivers/scsi/qla4xxx/ql4_os.c index 4255b36ff968..db7ea3bb4e83 100644 --- a/drivers/scsi/qla4xxx/ql4_os.c +++ b/drivers/scsi/qla4xxx/ql4_os.c | |||
@@ -353,7 +353,7 @@ void qla4xxx_mark_device_missing(struct scsi_qla_host *ha, | |||
353 | ha->host_no, ddb_entry->bus, ddb_entry->target, | 353 | ha->host_no, ddb_entry->bus, ddb_entry->target, |
354 | ddb_entry->fw_ddb_index)); | 354 | ddb_entry->fw_ddb_index)); |
355 | iscsi_block_session(ddb_entry->sess); | 355 | iscsi_block_session(ddb_entry->sess); |
356 | iscsi_conn_error(ddb_entry->conn, ISCSI_ERR_CONN_FAILED); | 356 | iscsi_conn_error_event(ddb_entry->conn, ISCSI_ERR_CONN_FAILED); |
357 | } | 357 | } |
358 | 358 | ||
359 | static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, | 359 | static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha, |
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index cbaae48f47ed..f9e45f83e467 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c | |||
@@ -1010,7 +1010,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, | |||
1010 | 1010 | ||
1011 | skb = alloc_skb(len, GFP_ATOMIC); | 1011 | skb = alloc_skb(len, GFP_ATOMIC); |
1012 | if (!skb) { | 1012 | if (!skb) { |
1013 | iscsi_conn_error(conn, ISCSI_ERR_CONN_FAILED); | 1013 | iscsi_conn_error_event(conn, ISCSI_ERR_CONN_FAILED); |
1014 | iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver " | 1014 | iscsi_cls_conn_printk(KERN_ERR, conn, "can not deliver " |
1015 | "control PDU: OOM\n"); | 1015 | "control PDU: OOM\n"); |
1016 | return -ENOMEM; | 1016 | return -ENOMEM; |
@@ -1031,7 +1031,7 @@ int iscsi_recv_pdu(struct iscsi_cls_conn *conn, struct iscsi_hdr *hdr, | |||
1031 | } | 1031 | } |
1032 | EXPORT_SYMBOL_GPL(iscsi_recv_pdu); | 1032 | EXPORT_SYMBOL_GPL(iscsi_recv_pdu); |
1033 | 1033 | ||
1034 | void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) | 1034 | void iscsi_conn_error_event(struct iscsi_cls_conn *conn, enum iscsi_err error) |
1035 | { | 1035 | { |
1036 | struct nlmsghdr *nlh; | 1036 | struct nlmsghdr *nlh; |
1037 | struct sk_buff *skb; | 1037 | struct sk_buff *skb; |
@@ -1063,7 +1063,7 @@ void iscsi_conn_error(struct iscsi_cls_conn *conn, enum iscsi_err error) | |||
1063 | iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n", | 1063 | iscsi_cls_conn_printk(KERN_INFO, conn, "detected conn error (%d)\n", |
1064 | error); | 1064 | error); |
1065 | } | 1065 | } |
1066 | EXPORT_SYMBOL_GPL(iscsi_conn_error); | 1066 | EXPORT_SYMBOL_GPL(iscsi_conn_error_event); |
1067 | 1067 | ||
1068 | static int | 1068 | static int |
1069 | iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, | 1069 | iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, |