📄 libiscsi.c
字号:
if (itt < session->cmds_max) { ctask = session->cmds[itt]; if (!ctask->sc) { printk(KERN_INFO "iscsi: dropping ctask with " "itt 0x%x\n", ctask->itt); /* force drop */ return ISCSI_ERR_NO_SCSI_CMD; } if (ctask->sc->SCp.phase != session->age) { printk(KERN_ERR "iscsi: ctask's session age %d, " "expected %d\n", ctask->sc->SCp.phase, session->age); return ISCSI_ERR_SESSION_FAILED; } } *ret_itt = itt; return 0;}EXPORT_SYMBOL_GPL(iscsi_verify_itt);void iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err){ struct iscsi_session *session = conn->session; unsigned long flags; spin_lock_irqsave(&session->lock, flags); if (session->state == ISCSI_STATE_FAILED) { spin_unlock_irqrestore(&session->lock, flags); return; } 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);}EXPORT_SYMBOL_GPL(iscsi_conn_failure);static void iscsi_prep_mtask(struct iscsi_conn *conn, struct iscsi_mgmt_task *mtask){ struct iscsi_session *session = conn->session; struct iscsi_hdr *hdr = mtask->hdr; struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; if (hdr->opcode != (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) && hdr->opcode != (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) nop->exp_statsn = cpu_to_be32(conn->exp_statsn); /* * pre-format CmdSN for outgoing PDU. */ nop->cmdsn = cpu_to_be32(session->cmdsn); if (hdr->itt != RESERVED_ITT) { hdr->itt = build_itt(mtask->itt, conn->id, session->age); /* * TODO: We always use immediate, so we never hit this. * If we start to send tmfs or nops as non-immediate then * we should start checking the cmdsn numbers for mgmt tasks. */ if (conn->c_stage == ISCSI_CONN_STARTED && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { session->queued_cmdsn++; session->cmdsn++; } } if (session->tt->init_mgmt_task) session->tt->init_mgmt_task(conn, mtask); debug_scsi("mgmtpdu [op 0x%x hdr->itt 0x%x datalen %d]\n", hdr->opcode, hdr->itt, mtask->data_count);}static int iscsi_xmit_mtask(struct iscsi_conn *conn){ struct iscsi_hdr *hdr = conn->mtask->hdr; int rc, was_logout = 0; spin_unlock_bh(&conn->session->lock); if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) { conn->session->state = ISCSI_STATE_IN_RECOVERY; iscsi_block_session(session_to_cls(conn->session)); was_logout = 1; } rc = conn->session->tt->xmit_mgmt_task(conn, conn->mtask); spin_lock_bh(&conn->session->lock); if (rc) return rc; /* done with this in-progress mtask */ conn->mtask = NULL; if (was_logout) { set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); return -ENODATA; } return 0;}static int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn){ struct iscsi_session *session = conn->session; /* * Check for iSCSI window and take care of CmdSN wrap-around */ if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) { debug_scsi("iSCSI CmdSN closed. ExpCmdSn %u MaxCmdSN %u " "CmdSN %u/%u\n", session->exp_cmdsn, session->max_cmdsn, session->cmdsn, session->queued_cmdsn); return -ENOSPC; } return 0;}static int iscsi_xmit_ctask(struct iscsi_conn *conn){ struct iscsi_cmd_task *ctask = conn->ctask; int rc = 0; /* * serialize with TMF AbortTask */ if (ctask->state == ISCSI_TASK_ABORTING) goto done; __iscsi_get_ctask(ctask); spin_unlock_bh(&conn->session->lock); rc = conn->session->tt->xmit_cmd_task(conn, ctask); spin_lock_bh(&conn->session->lock); __iscsi_put_ctask(ctask);done: if (!rc) /* done with this ctask */ conn->ctask = NULL; return rc;}/** * iscsi_data_xmit - xmit any command into the scheduled connection * @conn: iscsi connection * * Notes: * The function can return -EAGAIN in which case the caller must * re-schedule it again later or recover. '0' return code means * successful xmit. **/static int iscsi_data_xmit(struct iscsi_conn *conn){ int rc = 0; spin_lock_bh(&conn->session->lock); if (unlikely(conn->suspend_tx)) { debug_scsi("conn %d Tx suspended!\n", conn->id); spin_unlock_bh(&conn->session->lock); return -ENODATA; } if (conn->ctask) { rc = iscsi_xmit_ctask(conn); if (rc) goto again; } if (conn->mtask) { rc = iscsi_xmit_mtask(conn); if (rc) goto again; } /* * process mgmt pdus like nops before commands since we should * only have one nop-out as a ping from us and targets should not * overflow us with nop-ins */check_mgmt: while (__kfifo_get(conn->mgmtqueue, (void*)&conn->mtask, sizeof(void*))) { iscsi_prep_mtask(conn, conn->mtask); list_add_tail(&conn->mtask->running, &conn->mgmt_run_list); rc = iscsi_xmit_mtask(conn); if (rc) goto again; } /* process command queue */ while (!list_empty(&conn->xmitqueue)) { /* * iscsi tcp may readd the task to the xmitqueue to send * write data */ conn->ctask = list_entry(conn->xmitqueue.next, struct iscsi_cmd_task, running); switch (conn->ctask->state) { case ISCSI_TASK_ABORTING: break; case ISCSI_TASK_PENDING: iscsi_prep_scsi_cmd_pdu(conn->ctask); conn->session->tt->init_cmd_task(conn->ctask); /* fall through */ default: conn->ctask->state = ISCSI_TASK_RUNNING; break; } list_move_tail(conn->xmitqueue.next, &conn->run_list); rc = iscsi_xmit_ctask(conn); if (rc) goto again; /* * we could continuously get new ctask requests so * we need to check the mgmt queue for nops that need to * be sent to aviod starvation */ if (__kfifo_len(conn->mgmtqueue)) goto check_mgmt; } spin_unlock_bh(&conn->session->lock); return -ENODATA;again: if (unlikely(conn->suspend_tx)) rc = -ENODATA; spin_unlock_bh(&conn->session->lock); return rc;}static void iscsi_xmitworker(struct work_struct *work){ struct iscsi_conn *conn = container_of(work, struct iscsi_conn, xmitwork); int rc; /* * serialize Xmit worker on a per-connection basis. */ do { rc = iscsi_data_xmit(conn); } while (rc >= 0 || rc == -EAGAIN);}enum { FAILURE_BAD_HOST = 1, FAILURE_SESSION_FAILED, FAILURE_SESSION_FREED, FAILURE_WINDOW_CLOSED, FAILURE_OOM, FAILURE_SESSION_TERMINATE, FAILURE_SESSION_IN_RECOVERY, FAILURE_SESSION_RECOVERY_TIMEOUT,};int iscsi_queuecommand(struct scsi_cmnd *sc, void (*done)(struct scsi_cmnd *)){ struct Scsi_Host *host; int reason = 0; struct iscsi_session *session; struct iscsi_conn *conn; struct iscsi_cmd_task *ctask = NULL; sc->scsi_done = done; sc->result = 0; sc->SCp.ptr = NULL; host = sc->device->host; session = iscsi_hostdata(host->hostdata); spin_lock(&session->lock); /* * ISCSI_STATE_FAILED is a temp. state. The recovery * code will decide what is best to do with command queued * during this time */ if (session->state != ISCSI_STATE_LOGGED_IN && session->state != ISCSI_STATE_FAILED) { /* * to handle the race between when we set the recovery state * and block the session we requeue here (commands could * be entering our queuecommand while a block is starting * up because the block code is not locked) */ if (session->state == ISCSI_STATE_IN_RECOVERY) { reason = FAILURE_SESSION_IN_RECOVERY; goto reject; } if (session->state == ISCSI_STATE_RECOVERY_FAILED) reason = FAILURE_SESSION_RECOVERY_TIMEOUT; else if (session->state == ISCSI_STATE_TERMINATE) reason = FAILURE_SESSION_TERMINATE; else reason = FAILURE_SESSION_FREED; goto fault; } conn = session->leadconn; if (!conn) { reason = FAILURE_SESSION_FREED; goto fault; } if (iscsi_check_cmdsn_window_closed(conn)) { reason = FAILURE_WINDOW_CLOSED; goto reject; } if (!__kfifo_get(session->cmdpool.queue, (void*)&ctask, sizeof(void*))) { reason = FAILURE_OOM; goto reject; } session->queued_cmdsn++; sc->SCp.phase = session->age; sc->SCp.ptr = (char *)ctask; atomic_set(&ctask->refcount, 1); ctask->state = ISCSI_TASK_PENDING; ctask->mtask = NULL; ctask->conn = conn; ctask->sc = sc; INIT_LIST_HEAD(&ctask->running); list_add_tail(&ctask->running, &conn->xmitqueue); spin_unlock(&session->lock); scsi_queue_work(host, &conn->xmitwork); return 0;reject: spin_unlock(&session->lock); debug_scsi("cmd 0x%x rejected (%d)\n", sc->cmnd[0], reason); return SCSI_MLQUEUE_HOST_BUSY;fault: spin_unlock(&session->lock); printk(KERN_ERR "iscsi: cmd 0x%x is not queued (%d)\n", sc->cmnd[0], reason); sc->result = (DID_NO_CONNECT << 16); scsi_set_resid(sc, scsi_bufflen(sc)); sc->scsi_done(sc); return 0;}EXPORT_SYMBOL_GPL(iscsi_queuecommand);int iscsi_change_queue_depth(struct scsi_device *sdev, int depth){ if (depth > ISCSI_MAX_CMD_PER_LUN) depth = ISCSI_MAX_CMD_PER_LUN; scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), depth); return sdev->queue_depth;}EXPORT_SYMBOL_GPL(iscsi_change_queue_depth);static struct iscsi_mgmt_task *__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size){ struct iscsi_session *session = conn->session; struct iscsi_mgmt_task *mtask; if (session->state == ISCSI_STATE_TERMINATE) return NULL; if (hdr->opcode == (ISCSI_OP_LOGIN | ISCSI_OP_IMMEDIATE) || hdr->opcode == (ISCSI_OP_TEXT | ISCSI_OP_IMMEDIATE)) /* * Login and Text are sent serially, in * request-followed-by-response sequence. * Same mtask can be used. Same ITT must be used. * Note that login_mtask is preallocated at conn_create(). */ mtask = conn->login_mtask; else { BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); if (!__kfifo_get(session->mgmtpool.queue, (void*)&mtask, sizeof(void*))) return NULL; } if (data_size) { memcpy(mtask->data, data, data_size); mtask->data_count = data_size; } else mtask->data_count = 0; INIT_LIST_HEAD(&mtask->running); memcpy(mtask->hdr, hdr, sizeof(struct iscsi_hdr)); __kfifo_put(conn->mgmtqueue, (void*)&mtask, sizeof(void*)); return mtask;}int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size){ struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; int err = 0; spin_lock_bh(&session->lock); if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) err = -EPERM; spin_unlock_bh(&session->lock); scsi_queue_work(session->host, &conn->xmitwork); return err;}EXPORT_SYMBOL_GPL(iscsi_conn_send_pdu);void iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session){ struct iscsi_session *session = class_to_transport_session(cls_session); struct iscsi_conn *conn = session->leadconn; spin_lock_bh(&session->lock); if (session->state != ISCSI_STATE_LOGGED_IN) { session->state = ISCSI_STATE_RECOVERY_FAILED; if (conn) wake_up(&conn->ehwait); } spin_unlock_bh(&session->lock);}EXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout);int iscsi_eh_host_reset(struct scsi_cmnd *sc){ struct Scsi_Host *host = sc->device->host; struct iscsi_session *session = iscsi_hostdata(host->hostdata); struct iscsi_conn *conn = session->leadconn; int fail_session = 0; spin_lock_bh(&session->lock); if (session->state == ISCSI_STATE_TERMINATE) {failed: debug_scsi("failing host reset: session terminated " "[CID %d age %d]\n", conn->id, session->age); spin_unlock_bh(&session->lock); return FAILED; } if (sc->SCp.phase == session->age) { debug_scsi("failing connection CID %d due to SCSI host reset\n", conn->id); fail_session = 1; } spin_unlock_bh(&session->lock); /* * we drop the lock here but the leadconn cannot be destoyed while * we are in the scsi eh */ if (fail_session) iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); debug_scsi("iscsi_eh_host_reset wait for relogin\n"); wait_event_interruptible(conn->ehwait, session->state == ISCSI_STATE_TERMINATE || session->state == ISCSI_STATE_LOGGED_IN || session->state == ISCSI_STATE_RECOVERY_FAILED); if (signal_pending(current)) flush_signals(current); spin_lock_bh(&session->lock); if (session->state == ISCSI_STATE_LOGGED_IN) printk(KERN_INFO "iscsi: host reset succeeded\n"); else goto failed; spin_unlock_bh(&session->lock); return SUCCESS;}EXPORT_SYMBOL_GPL(iscsi_eh_host_reset);static void iscsi_tmabort_timedout(unsigned long data){ struct iscsi_cmd_task *ctask = (struct iscsi_cmd_task *)data; struct iscsi_conn *conn = ctask->conn; struct iscsi_session *session = conn->session; spin_lock(&session->lock); if (conn->tmabort_state == TMABORT_INITIAL) { conn->tmabort_state = TMABORT_TIMEDOUT; debug_scsi("tmabort timedout [sc %p itt 0x%x]\n", ctask->sc, ctask->itt); /* unblock eh_abort() */ wake_up(&conn->ehwait); } spin_unlock(&session->lock);}static int iscsi_exec_abort_task(struct scsi_cmnd *sc, struct iscsi_cmd_task *ctask){ struct iscsi_conn *conn = ctask->conn; struct iscsi_session *session = conn->session; struct iscsi_tm *hdr = &conn->tmhdr; /* * ctask timed out but session is OK requests must be serialized. */ memset(hdr, 0, sizeof(struct iscsi_tm)); hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; hdr->flags = ISCSI_TM_FUNC_ABORT_TASK; hdr->flags |= ISCSI_FLAG_CMD_FINAL; memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun)); hdr->rtt = ctask->hdr->itt; hdr->refcmdsn = ctask->hdr->cmdsn; ctask->mtask = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0); if (!ctask->mtask) { spin_unlock_bh(&session->lock); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); spin_lock_bh(&session->lock) debug_scsi("abort sent failure [itt 0x%x]\n", ctask->itt); return -EPERM; } ctask->state = ISCSI_TASK_ABORTING; debug_scsi("abort sent [itt 0x%x]\n", ctask->itt); if (conn->tmabort_state == TMABORT_INITIAL) { conn->tmfcmd_pdus_cnt++; conn->tmabort_timer.expires = 20*HZ + jiffies; conn->tmabort_timer.function = iscsi_tmabort_timedout; conn->tmabort_timer.data = (unsigned long)ctask; add_timer(&conn->tmabort_timer); debug_scsi("abort set timeout [itt 0x%x]\n", ctask->itt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -