⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 libiscsi.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	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 + -