iscsi-recv-pdu.c

来自「iSCSI协议在LINUX下的源码.源代码是IBM公布的.主要是结合其OSD设备」· C语言 代码 · 共 1,019 行 · 第 1/2 页

C
1,019
字号
	/* FIXME: we could discard the data or drop the session */	return rc;}/* * Only call this from recvs where the rx_buffer is not in * use. We don't bother checking the CRC, since we couldn't * retry the command anyway */static voiddrop_data(struct iscsi_session *session, struct iscsi_hdr *sth){	int pad, length, num_bytes;	struct kvec iov;	length = ntoh24(sth->dlength);	pad = length % PAD_WORD_LEN;	if (pad)		pad = PAD_WORD_LEN - pad;	length += pad;	if (session->data_digest == ISCSI_DIGEST_CRC32C) {		iscsi_host_info(session, "recv_data discarding %d data PDU "				"bytes, %d pad bytes, %Zu digest bytes\n",				ntoh24(sth->dlength), pad, sizeof(u32));		length += sizeof(u32);	} else		iscsi_host_info(session, "recv_data discarding %d data PDU "				"bytes, %d pad bytes\n", ntoh24(sth->dlength),				pad);	while (!signal_pending(current) && length > 0) {		num_bytes = min_t(int, length, sizeof(session->rx_buffer));		iov.iov_base = session->rx_buffer;		iov.iov_len = sizeof(session->rx_buffer);		/* should iov_len match num_bytes ? */		if (iscsi_recvmsg(session, &iov, 1, num_bytes) !=		    ISCSI_IO_SUCCESS) {			iscsi_drop_session(session);			break;		}		/* assume a PDU round-trip, connection is ok */		session->last_rx = jiffies;		length -= num_bytes;	}}static voidhandle_scsi_data(struct iscsi_session *session, struct iscsi_hdr *sth){	struct iscsi_data_rsp_hdr *stdrh = (struct iscsi_data_rsp_hdr *)sth;	struct iscsi_task *task;	struct scsi_cmnd *sc;	struct scatterlist sg;	int dlength, offset, rc;	u32 itt = ntohl(stdrh->itt);	if (stdrh->flags & ISCSI_FLAG_DATA_STATUS)		/* FIXME: check StatSN */		session->exp_stat_sn = ntohl(stdrh->statsn) + 1;	update_sn(session, ntohl(stdrh->expcmdsn), ntohl(stdrh->maxcmdsn));	dlength = ntoh24(stdrh->dlength);	offset = ntohl(stdrh->offset);	spin_lock_bh(&session->task_lock);	task = iscsi_find_session_task(session, itt);	if (!task) {		iscsi_host_warn(session, "recv_data, no task for itt %u next "				"itt %u, discarding received data, offset %u "				"len %u\n", ntohl(stdrh->itt),				session->next_itt, offset, dlength);		spin_unlock_bh(&session->task_lock);		drop_data(session, sth);		return;	}	sc = task->scsi_cmnd;	/* sanity check the PDU against the command */	if (!test_bit(ISCSI_TASK_READ, &task->flags)) {		iscsi_host_err(session, "lun%u: recv_data itt %u, command "			       "cdb 0x%02x, dropping session due to "			       "unexpected Data-in from\n", task->lun, itt,			       sc->cmnd[0]);		iscsi_drop_session(session);		goto done;	} else if ((offset + dlength) > sc->request_bufflen) {		/* buffer overflow, often because of a corrupt PDU header */		iscsi_host_err(session, "recv_data for itt %u, cmnd 0x%x, "			       "bufflen %u, Data PDU with offset %u len %u "			       "overflows command buffer, dropping session\n",			       itt, sc->cmnd[0], sc->request_bufflen, offset,			       dlength);		iscsi_drop_session(session);		goto done;	} else if (task->rxdata != offset) {		/*			 * if the data arrives out-of-order, it becomes much harder		 * for us to correctly calculate the residual if we don't get		 * enough data and also don't get an underflow from the		 * target.  This can happen if we discard Data PDUs due to		 * bogus offsets/lengths.  Since we always negotiate for		 * Data PDUs in-order, this should never happen, but check		 * for it anyway.		 */		iscsi_host_err(session, "recv_data for itt %u, cmnd 0x%x, "			       "bufflen %u, offset %u does not match expected "			       "offset %u, dropping session\n", itt,			       sc->cmnd[0], sc->request_bufflen, offset,			       task->rxdata);		iscsi_drop_session(session);		goto done;	}	/*	 * either we'll read it all, or we'll drop the session and requeue	 * the command, so it's safe to increment early	 */	task->rxdata += dlength;	spin_unlock_bh(&session->task_lock);	if (sc->use_sg)		rc = iscsi_recv_sg_data(session, dlength, sc->request_buffer,					sc->use_sg, offset,					session->data_digest);	else {		sg_init_one(&sg, sc->request_buffer, dlength);		rc = iscsi_recv_sg_data(session, dlength, &sg, 1, offset,					session->data_digest);	}	spin_lock_bh(&session->task_lock);	switch (rc) {	case ISCSI_IO_ERR:		iscsi_drop_session(session);		break;	case ISCSI_IO_CRC32C_ERR:		__set_bit(ISCSI_TASK_CRC_ERROR, &task->flags);		/* fall through */	case ISCSI_IO_SUCCESS:		if (stdrh->flags & ISCSI_FLAG_DATA_STATUS) {			iscsi_process_task_status(task, sth);			iscsi_complete_task(task);		}	} done:	__iscsi_put_task(task);	spin_unlock_bh(&session->task_lock);}/** * handle_task_mgmt_rsp - Process the task management response. * @session: to retrieve the task * @ststmrh: task management response header * * Description: *     Retrieve the task for which task mgmt response is received and take *     appropriate action based on the type of task management request. **/static voidhandle_task_mgmt_rsp(struct iscsi_session *session, struct iscsi_hdr *sth){	struct iscsi_scsi_task_mgmt_rsp_hdr *ststmrh;	struct iscsi_task *task;	u32 mgmt_itt;	ststmrh = (struct iscsi_scsi_task_mgmt_rsp_hdr *)sth;	mgmt_itt = ntohl(ststmrh->itt);	/* FIXME: check StatSN */	session->exp_stat_sn = ntohl(ststmrh->statsn) + 1;	update_sn(session, ntohl(ststmrh->expcmdsn), ntohl(ststmrh->maxcmdsn));	spin_lock_bh(&session->task_lock);	/*	 * This can fail if they timedout and we escalated the recovery	 * to a new function	 */	task = iscsi_find_session_task(session, mgmt_itt);	if (!task) {		iscsi_host_warn(session, "mgmt response 0x%x for unknown itt "				"%u, rtt %u\n", ststmrh->response,				ntohl(ststmrh->itt), ntohl(ststmrh->rtt));		goto done;	}	if (ststmrh->response == 0) {		iscsi_host_info(task->session, "task mgmt itt %u "				"successful\n", mgmt_itt);		iscsi_complete_tmf_task(task, ISCSI_TASK_TMF_SUCCESS);	} else {		iscsi_host_err(task->session, "task mgmt itt %u rejected"			       " (0x%x)\n", mgmt_itt, ststmrh->response);		iscsi_complete_tmf_task(task, ISCSI_TASK_TMF_FAILED);	}	__iscsi_put_task(task);	 done:	/*	 * we got the expected response, allow the eh thread to send	 * another task mgmt PDU whenever it wants to	 */	if (session->last_mgmt_itt == mgmt_itt)		session->last_mgmt_itt = ISCSI_RSVD_TASK_TAG;	spin_unlock_bh(&session->task_lock);}static voidprocess_immed_cmd_reject(struct iscsi_session *session, unsigned char *xbuf,			 int dlength){	u32 itt;	struct iscsi_task *task;	struct iscsi_hdr pdu;	if (dlength < sizeof(pdu)) {		iscsi_host_warn(session, "Immediate command rejected, dlength "				"%u\n", dlength);		return;	}	/* look at the rejected PDU */	memcpy(&pdu, xbuf, sizeof(pdu));	itt = ntohl(pdu.itt);	/*	 * try to find the task corresponding to this itt,	 * and wake up any process waiting on it	 */	spin_lock_bh(&session->task_lock);	if (session->last_mgmt_itt == itt)		session->last_mgmt_itt = ISCSI_RSVD_TASK_TAG;	task = iscsi_find_session_task(session, itt);	if (task) {		iscsi_host_notice(session, "task mgmt PDU rejected, mgmt %u, "				  "itt %u\n", itt, task->itt);		iscsi_complete_tmf_task(task, ISCSI_TASK_IMM_REJECT);		__iscsi_put_task(task);	} else if ((pdu.opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT_CMD)		/*		 * our Logout was rejected.  just let the		 * logout response timer drop the session		 */		iscsi_host_warn(session, "Logout PDU rejected, itt %u\n", itt);	else		iscsi_host_warn(session, "itt %u immediate command rejected\n",				itt);	spin_unlock_bh(&session->task_lock);}static voidhandle_reject(struct iscsi_session *session, struct iscsi_hdr *sth,	      unsigned char *xbuf){	struct iscsi_reject_hdr *reject;	struct iscsi_hdr pdu;	int dlength;	u32 itt;	reject = (struct iscsi_reject_hdr *)sth;	dlength = ntoh24(reject->dlength);	/* FIXME: check StatSN */	session->exp_stat_sn = ntohl(reject->statsn) + 1;	update_sn(session, ntohl(reject->expcmdsn), ntohl(reject->maxcmdsn));	if (reject->reason == ISCSI_REJECT_DATA_DIGEST_ERROR) {		/*		 * we don't need to do anything about these,		 * timers or other PDUs will handle the problem.		 */		if (dlength >= sizeof(pdu)) {			memcpy(&pdu, xbuf, sizeof(pdu));			itt = ntohl(pdu.itt);			iscsi_host_warn(session, "itt %u (opcode 0x%x) rejected"					" because of a DataDigest error\n", itt,					pdu.opcode);		} else			iscsi_host_warn(session, "Target rejected a PDU because"					" of a DataDigest error\n");	} else if (reject->reason == ISCSI_REJECT_IMM_CMD_REJECT)		process_immed_cmd_reject(session, xbuf, dlength);	else {		if (dlength >= sizeof(pdu)) {			/* look at the rejected PDU */			memcpy(&pdu, xbuf, sizeof(pdu));			itt = ntohl(pdu.itt);			iscsi_host_err(session, "Dropping session because "				       "target rejected a PDU, reason 0x%x, "				       "dlength %d, rejected itt %u, opcode "				       "0x%x\n", reject->reason, dlength, itt,				       pdu.opcode);		} else			iscsi_host_err(session, "Dropping session because "				       "target rejected a PDU, reason 0x%x, "				       "dlength %u\n", reject->reason, dlength);		iscsi_drop_session(session);	}}static voidhandle_async_msg(struct iscsi_session *session, struct iscsi_hdr *sth,		 unsigned char *xbuf){	struct iscsi_async_msg_hdr *staeh = (struct iscsi_async_msg_hdr *)sth;	unsigned int senselen;	/* FIXME: check StatSN */	session->exp_stat_sn = ntohl(staeh->statsn) + 1;	update_sn(session, ntohl(staeh->expcmdsn), ntohl(staeh->maxcmdsn));	switch (staeh->async_event) {	case ISCSI_ASYNC_MSG_SCSI_EVENT:		senselen = (xbuf[0] << 8) | xbuf[1];		xbuf += 2;		iscsi_host_info(session, "Received async SCSI event. Printing "				"sense\n");/*		remove for 2.6.11		__scsi_print_sense(ISCSI_PROC_NAME, xbuf, senselen);*/		break;	case ISCSI_ASYNC_MSG_REQUEST_LOGOUT:		/*		 * FIXME: this is really a request to drop a connection,		 * not the whole session, but we currently only have one		 * connection per session, so there's no difference		 * at the moment.		 */		iscsi_host_warn(session, "Target requests logout within %u "				"seconds for session\n", ntohs(staeh->param3));		/*		 * we need to get the task lock to make sure the TX thread		 * isn't in the middle of adding another task to the session.		 */		spin_lock_bh(&session->task_lock);		iscsi_request_logout(session, ntohs(staeh->param3) - (HZ / 10),				     session->active_timeout);		spin_unlock_bh(&session->task_lock);		break;	case ISCSI_ASYNC_MSG_DROPPING_CONNECTION:		iscsi_host_warn(session, "Target dropping connection %u, "				"reconnect min %u max %u\n",				ntohs(staeh->param1), ntohs(staeh->param2),				ntohs(staeh->param3));		session->time2wait = (long) ntohs(staeh->param2) & 0x0000FFFFFL;		break;	case ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS:		iscsi_host_warn(session, "Target dropping all connections, "				"reconnect min %u max %u\n",				ntohs(staeh->param2), ntohs(staeh->param3));		session->time2wait = (long) ntohs(staeh->param2) & 0x0000FFFFFL;		break;	case ISCSI_ASYNC_MSG_VENDOR_SPECIFIC:		iscsi_host_warn(session, "Ignoring vendor-specific async event,"				" vcode 0x%x\n", staeh->async_vcode);		break;	case ISCSI_ASYNC_MSG_PARAM_NEGOTIATION:		iscsi_host_warn(session, "Received async event param "				"negotiation, dropping session\n");		iscsi_drop_session(session);		break;	default:		iscsi_host_err(session, "Received unknown async event 0x%x\n",			       staeh->async_event);		break;	}	if (staeh->async_event == ISCSI_ASYNC_MSG_DROPPING_CONNECTION ||	    staeh->async_event == ISCSI_ASYNC_MSG_DROPPING_ALL_CONNECTIONS ||	    staeh->async_event == ISCSI_ASYNC_MSG_REQUEST_LOGOUT) {		spin_lock(&session->portal_lock);		memcpy(&session->addr, &session->portal.addr,		       sizeof(struct sockaddr));		spin_unlock(&session->portal_lock);	}}/** * iscsi_recv_pdu - Read in a iSCSI PDU * @session: iscsi session structure * @hdr: a iSCSI PDU header * @hdr_digest: digest type for header * @data: buffer for data * @max_data_len: buffer size * @data_digest: digest type for data * @timeout: come crap timeout used for login PDUs * * Description: *    Reads a iSCSI PDU into memory. Excpet for login PDUs, this function *    will also process the PDU. **/intiscsi_recv_pdu(struct iscsi_session *session, struct iscsi_hdr *hdr,	       int hdr_digest, char *data, int max_data_len, int data_digest,	       int timeout){	int rc;	int data_len;	struct scatterlist sg;	/*	 * TODO setting the login timer does nothing for FFP so	 * it is safe to set, however this will be fixed properly soon	 */	if (timeout)		session->login_phase_timer = jiffies + (timeout * HZ);	else		session->login_phase_timer = 0;	if (iscsi_recv_header(session, hdr, hdr_digest) != ISCSI_IO_SUCCESS)		goto fail;	data_len = ntoh24(hdr->dlength);	/*	 * scsi data is read in and processed by its handler for now	 */	if (data_len && hdr->opcode != ISCSI_OP_SCSI_DATA_RSP) {        	if (data_len > max_data_len) {                	iscsi_host_err(session, "iscsi_recv_pdu() cannot read "				       "%d bytes of PDU data, only %d bytes "				       "of buffer available\n", data_len,				       max_data_len);			goto fail;        	}		/*		 * must clear this, beucase the login api uses the same		 * buffer for recv and send		 */		memset(data, 0, max_data_len);		sg_init_one(&sg, data, data_len);		rc = iscsi_recv_sg_data(session, data_len, &sg, 1, 0,					data_digest);		if (rc == ISCSI_IO_CRC32C_ERR) {			switch (hdr->opcode) {			case ISCSI_OP_ASYNC_MSG:			case ISCSI_OP_REJECT:				/* unsolicited so ignore */				goto done;			default:				goto fail;			};		} else if (rc != ISCSI_IO_SUCCESS)			goto fail;	}	switch (hdr->opcode) {	case ISCSI_OP_NOOP_IN:		handle_nop_in(session, hdr);		break;	case ISCSI_OP_SCSI_RSP:		handle_scsi_rsp(session, hdr, data);		break;	case ISCSI_OP_SCSI_TASK_MGT_RSP:		handle_task_mgmt_rsp(session, hdr);		break;	case ISCSI_OP_R2T:		handle_r2t(session, hdr);		break;	case ISCSI_OP_SCSI_DATA_RSP:		handle_scsi_data(session, hdr);		break;	case ISCSI_OP_ASYNC_MSG:		handle_async_msg(session, hdr, data);		break;	case ISCSI_OP_REJECT:	        handle_reject(session, hdr, data);		break;	case ISCSI_OP_LOGOUT_RSP:		handle_logout(session, hdr);		break;	case ISCSI_OP_LOGIN_RSP:		/*		 * The login api needs the buffer to be cleared when no		 * data has been read		 */		if (!data_len)			memset(data, 0, max_data_len);			/*		 * login api will process further		 */		break;	default:		iscsi_host_err(session, "Dropping session after receiving "			       "unexpected opcode 0x%x\n", hdr->opcode);		session->time2wait = 2;		goto fail;	} done:	/* TODO rm phase_timer hack */	session->login_phase_timer = 0;	return 1; fail:	session->login_phase_timer = 0;	iscsi_drop_session(session);	return 0;}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?