📄 libiscsi.c
字号:
if (session->leadconn == conn) { /* * leading connection? then give up on recovery. */ session->state = ISCSI_STATE_TERMINATE; wake_up(&conn->ehwait); } spin_unlock_bh(&session->lock); /* * Block until all in-progress commands for this connection * time out or fail. */ for (;;) { spin_lock_irqsave(session->host->host_lock, flags); if (!session->host->host_busy) { /* OK for ERL == 0 */ spin_unlock_irqrestore(session->host->host_lock, flags); break; } spin_unlock_irqrestore(session->host->host_lock, flags); msleep_interruptible(500); printk(KERN_INFO "iscsi: scsi conn_destroy(): host_busy %d " "host_failed %d\n", session->host->host_busy, session->host->host_failed); /* * force eh_abort() to unblock */ wake_up(&conn->ehwait); } /* flush queued up work because we free the connection below */ scsi_flush_work(session->host); spin_lock_bh(&session->lock); kfree(conn->data); kfree(conn->persistent_address); __kfifo_put(session->mgmtpool.queue, (void*)&conn->login_mtask, sizeof(void*)); if (session->leadconn == conn) session->leadconn = NULL; spin_unlock_bh(&session->lock); kfifo_free(conn->mgmtqueue); iscsi_destroy_conn(cls_conn);}EXPORT_SYMBOL_GPL(iscsi_conn_teardown);int iscsi_conn_start(struct iscsi_cls_conn *cls_conn){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; if (!session) { printk(KERN_ERR "iscsi: can't start unbound connection\n"); return -EPERM; } if ((session->imm_data_en || !session->initial_r2t_en) && session->first_burst > session->max_burst) { printk("iscsi: invalid burst lengths: " "first_burst %d max_burst %d\n", session->first_burst, session->max_burst); return -EINVAL; } spin_lock_bh(&session->lock); conn->c_stage = ISCSI_CONN_STARTED; session->state = ISCSI_STATE_LOGGED_IN; session->queued_cmdsn = session->cmdsn; switch(conn->stop_stage) { case STOP_CONN_RECOVER: /* * unblock eh_abort() if it is blocked. re-try all * commands after successful recovery */ conn->stop_stage = 0; conn->tmabort_state = TMABORT_INITIAL; session->age++; spin_unlock_bh(&session->lock); iscsi_unblock_session(session_to_cls(session)); wake_up(&conn->ehwait); return 0; case STOP_CONN_TERM: conn->stop_stage = 0; break; default: break; } spin_unlock_bh(&session->lock); return 0;}EXPORT_SYMBOL_GPL(iscsi_conn_start);static voidflush_control_queues(struct iscsi_session *session, struct iscsi_conn *conn){ struct iscsi_mgmt_task *mtask, *tmp; /* handle pending */ while (__kfifo_get(conn->mgmtqueue, (void*)&mtask, sizeof(void*))) { if (mtask == conn->login_mtask) continue; debug_scsi("flushing pending mgmt task itt 0x%x\n", mtask->itt); __kfifo_put(session->mgmtpool.queue, (void*)&mtask, sizeof(void*)); } /* handle running */ list_for_each_entry_safe(mtask, tmp, &conn->mgmt_run_list, running) { debug_scsi("flushing running mgmt task itt 0x%x\n", mtask->itt); list_del(&mtask->running); if (mtask == conn->login_mtask) continue; __kfifo_put(session->mgmtpool.queue, (void*)&mtask, sizeof(void*)); } conn->mtask = NULL;}/* Fail commands. Mutex and session lock held and recv side suspended */static void fail_all_commands(struct iscsi_conn *conn){ struct iscsi_cmd_task *ctask, *tmp; /* flush pending */ list_for_each_entry_safe(ctask, tmp, &conn->xmitqueue, running) { debug_scsi("failing pending sc %p itt 0x%x\n", ctask->sc, ctask->itt); fail_command(conn, ctask, DID_BUS_BUSY << 16); } /* fail all other running */ list_for_each_entry_safe(ctask, tmp, &conn->run_list, running) { debug_scsi("failing in progress sc %p itt 0x%x\n", ctask->sc, ctask->itt); fail_command(conn, ctask, DID_BUS_BUSY << 16); } conn->ctask = NULL;}static void iscsi_start_session_recovery(struct iscsi_session *session, struct iscsi_conn *conn, int flag){ int old_stop_stage; mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); if (conn->stop_stage == STOP_CONN_TERM) { spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); return; } /* * The LLD either freed/unset the lock on us, or userspace called * stop but did not create a proper connection (connection was never * bound or it was unbound then stop was called). */ if (!conn->recv_lock) { spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); return; } /* * When this is called for the in_login state, we only want to clean * up the login task and connection. We do not need to block and set * the recovery state again */ if (flag == STOP_CONN_TERM) session->state = ISCSI_STATE_TERMINATE; else if (conn->stop_stage != STOP_CONN_RECOVER) session->state = ISCSI_STATE_IN_RECOVERY; old_stop_stage = conn->stop_stage; conn->stop_stage = flag; conn->c_stage = ISCSI_CONN_STOPPED; spin_unlock_bh(&session->lock); iscsi_suspend_tx(conn); write_lock_bh(conn->recv_lock); set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); write_unlock_bh(conn->recv_lock); /* * for connection level recovery we should not calculate * header digest. conn->hdr_size used for optimization * in hdr_extract() and will be re-negotiated at * set_param() time. */ if (flag == STOP_CONN_RECOVER) { conn->hdrdgst_en = 0; conn->datadgst_en = 0; if (session->state == ISCSI_STATE_IN_RECOVERY && old_stop_stage != STOP_CONN_RECOVER) { debug_scsi("blocking session\n"); iscsi_block_session(session_to_cls(session)); } } /* * flush queues. */ spin_lock_bh(&session->lock); fail_all_commands(conn); flush_control_queues(session, conn); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex);}void iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; switch (flag) { case STOP_CONN_RECOVER: case STOP_CONN_TERM: iscsi_start_session_recovery(session, conn, flag); break; default: printk(KERN_ERR "iscsi: invalid stop flag %d\n", flag); }}EXPORT_SYMBOL_GPL(iscsi_conn_stop);int iscsi_conn_bind(struct iscsi_cls_session *cls_session, struct iscsi_cls_conn *cls_conn, int is_leading){ struct iscsi_session *session = class_to_transport_session(cls_session); struct iscsi_conn *conn = cls_conn->dd_data; spin_lock_bh(&session->lock); if (is_leading) session->leadconn = conn; spin_unlock_bh(&session->lock); /* * Unblock xmitworker(), Login Phase will pass through. */ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); return 0;}EXPORT_SYMBOL_GPL(iscsi_conn_bind);int iscsi_set_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf, int buflen){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; uint32_t value; switch(param) { case ISCSI_PARAM_MAX_RECV_DLENGTH: sscanf(buf, "%d", &conn->max_recv_dlength); break; case ISCSI_PARAM_MAX_XMIT_DLENGTH: sscanf(buf, "%d", &conn->max_xmit_dlength); break; case ISCSI_PARAM_HDRDGST_EN: sscanf(buf, "%d", &conn->hdrdgst_en); break; case ISCSI_PARAM_DATADGST_EN: sscanf(buf, "%d", &conn->datadgst_en); break; case ISCSI_PARAM_INITIAL_R2T_EN: sscanf(buf, "%d", &session->initial_r2t_en); break; case ISCSI_PARAM_MAX_R2T: sscanf(buf, "%d", &session->max_r2t); break; case ISCSI_PARAM_IMM_DATA_EN: sscanf(buf, "%d", &session->imm_data_en); break; case ISCSI_PARAM_FIRST_BURST: sscanf(buf, "%d", &session->first_burst); break; case ISCSI_PARAM_MAX_BURST: sscanf(buf, "%d", &session->max_burst); break; case ISCSI_PARAM_PDU_INORDER_EN: sscanf(buf, "%d", &session->pdu_inorder_en); break; case ISCSI_PARAM_DATASEQ_INORDER_EN: sscanf(buf, "%d", &session->dataseq_inorder_en); break; case ISCSI_PARAM_ERL: sscanf(buf, "%d", &session->erl); break; case ISCSI_PARAM_IFMARKER_EN: sscanf(buf, "%d", &value); BUG_ON(value); break; case ISCSI_PARAM_OFMARKER_EN: sscanf(buf, "%d", &value); BUG_ON(value); break; case ISCSI_PARAM_EXP_STATSN: sscanf(buf, "%u", &conn->exp_statsn); break; case ISCSI_PARAM_USERNAME: kfree(session->username); session->username = kstrdup(buf, GFP_KERNEL); if (!session->username) return -ENOMEM; break; case ISCSI_PARAM_USERNAME_IN: kfree(session->username_in); session->username_in = kstrdup(buf, GFP_KERNEL); if (!session->username_in) return -ENOMEM; break; case ISCSI_PARAM_PASSWORD: kfree(session->password); session->password = kstrdup(buf, GFP_KERNEL); if (!session->password) return -ENOMEM; break; case ISCSI_PARAM_PASSWORD_IN: kfree(session->password_in); session->password_in = kstrdup(buf, GFP_KERNEL); if (!session->password_in) return -ENOMEM; break; case ISCSI_PARAM_TARGET_NAME: /* this should not change between logins */ if (session->targetname) break; session->targetname = kstrdup(buf, GFP_KERNEL); if (!session->targetname) return -ENOMEM; break; case ISCSI_PARAM_TPGT: sscanf(buf, "%d", &session->tpgt); break; case ISCSI_PARAM_PERSISTENT_PORT: sscanf(buf, "%d", &conn->persistent_port); break; case ISCSI_PARAM_PERSISTENT_ADDRESS: /* * this is the address returned in discovery so it should * not change between logins. */ if (conn->persistent_address) break; conn->persistent_address = kstrdup(buf, GFP_KERNEL); if (!conn->persistent_address) return -ENOMEM; break; default: return -ENOSYS; } return 0;}EXPORT_SYMBOL_GPL(iscsi_set_param);int iscsi_session_get_param(struct iscsi_cls_session *cls_session, enum iscsi_param param, char *buf){ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); struct iscsi_session *session = iscsi_hostdata(shost->hostdata); int len; switch(param) { case ISCSI_PARAM_INITIAL_R2T_EN: len = sprintf(buf, "%d\n", session->initial_r2t_en); break; case ISCSI_PARAM_MAX_R2T: len = sprintf(buf, "%hu\n", session->max_r2t); break; case ISCSI_PARAM_IMM_DATA_EN: len = sprintf(buf, "%d\n", session->imm_data_en); break; case ISCSI_PARAM_FIRST_BURST: len = sprintf(buf, "%u\n", session->first_burst); break; case ISCSI_PARAM_MAX_BURST: len = sprintf(buf, "%u\n", session->max_burst); break; case ISCSI_PARAM_PDU_INORDER_EN: len = sprintf(buf, "%d\n", session->pdu_inorder_en); break; case ISCSI_PARAM_DATASEQ_INORDER_EN: len = sprintf(buf, "%d\n", session->dataseq_inorder_en); break; case ISCSI_PARAM_ERL: len = sprintf(buf, "%d\n", session->erl); break; case ISCSI_PARAM_TARGET_NAME: len = sprintf(buf, "%s\n", session->targetname); break; case ISCSI_PARAM_TPGT: len = sprintf(buf, "%d\n", session->tpgt); break; case ISCSI_PARAM_USERNAME: len = sprintf(buf, "%s\n", session->username); break; case ISCSI_PARAM_USERNAME_IN: len = sprintf(buf, "%s\n", session->username_in); break; case ISCSI_PARAM_PASSWORD: len = sprintf(buf, "%s\n", session->password); break; case ISCSI_PARAM_PASSWORD_IN: len = sprintf(buf, "%s\n", session->password_in); break; default: return -ENOSYS; } return len;}EXPORT_SYMBOL_GPL(iscsi_session_get_param);int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, enum iscsi_param param, char *buf){ struct iscsi_conn *conn = cls_conn->dd_data; int len; switch(param) { case ISCSI_PARAM_MAX_RECV_DLENGTH: len = sprintf(buf, "%u\n", conn->max_recv_dlength); break; case ISCSI_PARAM_MAX_XMIT_DLENGTH: len = sprintf(buf, "%u\n", conn->max_xmit_dlength); break; case ISCSI_PARAM_HDRDGST_EN: len = sprintf(buf, "%d\n", conn->hdrdgst_en); break; case ISCSI_PARAM_DATADGST_EN: len = sprintf(buf, "%d\n", conn->datadgst_en); break; case ISCSI_PARAM_IFMARKER_EN: len = sprintf(buf, "%d\n", conn->ifmarker_en); break; case ISCSI_PARAM_OFMARKER_EN: len = sprintf(buf, "%d\n", conn->ofmarker_en); break; case ISCSI_PARAM_EXP_STATSN: len = sprintf(buf, "%u\n", conn->exp_statsn); break; case ISCSI_PARAM_PERSISTENT_PORT: len = sprintf(buf, "%d\n", conn->persistent_port); break; case ISCSI_PARAM_PERSISTENT_ADDRESS: len = sprintf(buf, "%s\n", conn->persistent_address); break; default: return -ENOSYS; } return len;}EXPORT_SYMBOL_GPL(iscsi_conn_get_param);int iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, char *buf){ struct iscsi_session *session = iscsi_hostdata(shost->hostdata); int len; switch (param) { case ISCSI_HOST_PARAM_NETDEV_NAME: if (!session->netdev) len = sprintf(buf, "%s\n", "default"); else len = sprintf(buf, "%s\n", session->netdev); break; case ISCSI_HOST_PARAM_HWADDRESS: if (!session->hwaddress) len = sprintf(buf, "%s\n", "default"); else len = sprintf(buf, "%s\n", session->hwaddress); break; case ISCSI_HOST_PARAM_INITIATOR_NAME: if (!session->initiatorname) len = sprintf(buf, "%s\n", "unknown"); else len = sprintf(buf, "%s\n", session->initiatorname); break; default: return -ENOSYS; } return len;}EXPORT_SYMBOL_GPL(iscsi_host_get_param);int iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param, char *buf, int buflen){ struct iscsi_session *session = iscsi_hostdata(shost->hostdata); switch (param) { case ISCSI_HOST_PARAM_NETDEV_NAME: if (!session->netdev) session->netdev = kstrdup(buf, GFP_KERNEL); break; case ISCSI_HOST_PARAM_HWADDRESS: if (!session->hwaddress) session->hwaddress = kstrdup(buf, GFP_KERNEL); break; case ISCSI_HOST_PARAM_INITIATOR_NAME: if (!session->initiatorname) session->initiatorname = kstrdup(buf, GFP_KERNEL); break; default: return -ENOSYS; } return 0;}EXPORT_SYMBOL_GPL(iscsi_host_set_param);MODULE_AUTHOR("Mike Christie");MODULE_DESCRIPTION("iSCSI library functions");MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -