From: Mike Christie Subject: Open-iSCSI updates References: FATE#304283 This patch updates the open-iscsi stack to include the required support for bnx2i driver. Signed-off-by: Hannes Reinecke --- drivers/infiniband/ulp/iser/iscsi_iser.c | 1 drivers/scsi/iscsi_tcp.c | 1 drivers/scsi/libiscsi.c | 72 +++++++++++++++++++++++++++---- include/scsi/iscsi_if.h | 1 include/scsi/libiscsi.h | 7 +++ 5 files changed, 75 insertions(+), 7 deletions(-) --- 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(s { struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); + iscsi_session_teardown(cls_session); iscsi_host_remove(shost); iscsi_host_free(shost); } --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -1904,6 +1904,7 @@ static void iscsi_tcp_session_destroy(st struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); iscsi_r2tpool_free(cls_session->dd_data); + iscsi_session_teardown(cls_session); iscsi_host_remove(shost); iscsi_host_free(shost); --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -368,11 +368,12 @@ void __iscsi_get_task(struct iscsi_task } EXPORT_SYMBOL_GPL(__iscsi_get_task); -static void __iscsi_put_task(struct iscsi_task *task) +void __iscsi_put_task(struct iscsi_task *task) { if (atomic_dec_and_test(&task->refcount)) iscsi_complete_command(task); } +EXPORT_SYMBOL_GPL(__iscsi_put_task); void iscsi_put_task(struct iscsi_task *task) { @@ -711,7 +712,7 @@ static int iscsi_handle_reject(struct is * * The session lock must be held. */ -static struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) +struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) { struct iscsi_session *session = conn->session; uint32_t i; @@ -725,6 +726,7 @@ static struct iscsi_task *iscsi_itt_to_t return session->cmds[i]; } +EXPORT_SYMBOL_GPL(iscsi_itt_to_task); /** * __iscsi_complete_pdu - complete pdu @@ -954,6 +956,38 @@ struct iscsi_task *iscsi_itt_to_ctask(st } EXPORT_SYMBOL_GPL(iscsi_itt_to_ctask); +void iscsi_session_failure(struct iscsi_cls_session *cls_session, + enum iscsi_err err) +{ + struct iscsi_session *session = cls_session->dd_data; + struct iscsi_conn *conn; + struct device *dev; + unsigned long flags; + + spin_lock_irqsave(&session->lock, flags); + conn = session->leadconn; + if (session->state == ISCSI_STATE_TERMINATE || !conn) { + spin_unlock_irqrestore(&session->lock, flags); + return; + } + + dev = get_device(&conn->cls_conn->dev); + spin_unlock_irqrestore(&session->lock, flags); + if (!dev) + return; + /* + * if the host is being removed bypass the connection + * recovery initialization because we are going to kill + * the session. + */ + if (err == ISCSI_ERR_INVALID_HOST) + iscsi_conn_error(conn->cls_conn, err); + else + iscsi_conn_failure(conn, err); + put_device(dev); +} +EXPORT_SYMBOL_GPL(iscsi_session_failure); + void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) { struct iscsi_session *session = conn->session; @@ -968,6 +1002,7 @@ void iscsi_conn_failure(struct iscsi_con if (conn->stop_stage == 0) session->state = ISCSI_STATE_FAILED; spin_unlock_irqrestore(&session->lock, flags); + set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); iscsi_conn_error(conn->cls_conn, err); @@ -1878,6 +1913,7 @@ struct Scsi_Host *iscsi_host_alloc(struc int dd_data_size, uint16_t qdepth) { struct Scsi_Host *shost; + struct iscsi_host *ihost; shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); if (!shost) @@ -1892,22 +1928,36 @@ struct Scsi_Host *iscsi_host_alloc(struc qdepth = ISCSI_DEF_CMD_PER_LUN; } shost->cmd_per_lun = qdepth; + + ihost = shost_priv(shost); + atomic_set(&ihost->num_sessions, 0); + init_waitqueue_head(&ihost->session_removal_wq); return shost; } EXPORT_SYMBOL_GPL(iscsi_host_alloc); +static void iscsi_kill_session(struct iscsi_cls_session *cls_session) +{ + iscsi_session_failure(cls_session, ISCSI_ERR_INVALID_HOST); +} + /** * iscsi_host_remove - remove host and sessions * @shost: scsi host * - * This will also remove any sessions attached to the host, but if userspace - * is managing the session at the same time this will break. TODO: add - * refcounting to the netlink iscsi interface so a rmmod or host hot unplug - * does not remove the memory from under us. + * If there are any sessions left, this will initiate the removal and wait + * for the completion. */ void iscsi_host_remove(struct Scsi_Host *shost) { - iscsi_host_for_each_session(shost, iscsi_session_teardown); + struct iscsi_host *ihost = shost_priv(shost); + + iscsi_host_for_each_session(shost, iscsi_kill_session); + wait_event_interruptible(ihost->session_removal_wq, + atomic_read(&ihost->num_sessions) == 0); + if (signal_pending(current)) + flush_signals(current); + scsi_remove_host(shost); } EXPORT_SYMBOL_GPL(iscsi_host_remove); @@ -1943,6 +1993,7 @@ iscsi_session_setup(struct iscsi_transpo uint16_t cmds_max, int cmd_task_size, uint32_t initial_cmdsn, unsigned int id) { + struct iscsi_host *ihost = shost_priv(shost); struct iscsi_session *session; struct iscsi_cls_session *cls_session; int cmd_i, scsi_cmds, total_cmds = cmds_max; @@ -2021,6 +2072,8 @@ iscsi_session_setup(struct iscsi_transpo if (iscsi_add_session(cls_session, id)) goto cls_session_fail; + + atomic_inc(&ihost->num_sessions); return cls_session; cls_session_fail: @@ -2043,6 +2096,7 @@ EXPORT_SYMBOL_GPL(iscsi_session_setup); void iscsi_session_teardown(struct iscsi_cls_session *cls_session) { struct iscsi_session *session = cls_session->dd_data; + struct iscsi_host *ihost = shost_priv(session->host); struct module *owner = cls_session->transport->owner; iscsi_pool_free(&session->cmdpool); @@ -2056,6 +2110,10 @@ void iscsi_session_teardown(struct iscsi kfree(session->ifacename); iscsi_destroy_session(cls_session); + + atomic_dec(&ihost->num_sessions); + wake_up(&ihost->session_removal_wq); + module_put(owner); } EXPORT_SYMBOL_GPL(iscsi_session_teardown); --- a/include/scsi/iscsi_if.h +++ b/include/scsi/iscsi_if.h @@ -213,6 +213,7 @@ enum iscsi_err { ISCSI_ERR_DATA_DGST = ISCSI_ERR_BASE + 15, ISCSI_ERR_PARAM_NOT_FOUND = ISCSI_ERR_BASE + 16, ISCSI_ERR_NO_SCSI_CMD = ISCSI_ERR_BASE + 17, + ISCSI_ERR_INVALID_HOST = ISCSI_ERR_BASE + 18, }; /* --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -295,6 +295,9 @@ struct iscsi_host { /* local address */ int local_port; char local_address[ISCSI_ADDRESS_BUF_LEN]; + + wait_queue_head_t session_removal_wq; + atomic_t num_sessions; }; /* @@ -351,6 +354,8 @@ extern void iscsi_conn_stop(struct iscsi extern int iscsi_conn_bind(struct iscsi_cls_session *, struct iscsi_cls_conn *, int); extern void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err); +extern void iscsi_session_failure(struct iscsi_cls_session *cls_session, + enum iscsi_err err); extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf); extern void iscsi_suspend_tx(struct iscsi_conn *conn); @@ -373,8 +378,10 @@ extern int __iscsi_complete_pdu(struct i char *, int); extern int iscsi_verify_itt(struct iscsi_conn *, itt_t); extern struct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *, itt_t); +extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t); extern void iscsi_requeue_task(struct iscsi_task *task); extern void iscsi_put_task(struct iscsi_task *task); +extern void __iscsi_put_task(struct iscsi_task *task); extern void __iscsi_get_task(struct iscsi_task *task); /*