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

📄 libiscsi.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * iSCSI lib functions * * Copyright (C) 2006 Red Hat, Inc.  All rights reserved. * Copyright (C) 2004 - 2006 Mike Christie * Copyright (C) 2004 - 2005 Dmitry Yusupov * Copyright (C) 2004 - 2005 Alex Aizman * maintained by open-iscsi@googlegroups.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */#include <linux/types.h>#include <linux/kfifo.h>#include <linux/delay.h>#include <asm/unaligned.h>#include <net/tcp.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_host.h>#include <scsi/scsi.h>#include <scsi/iscsi_proto.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_iscsi.h>#include <scsi/libiscsi.h>struct iscsi_session *class_to_transport_session(struct iscsi_cls_session *cls_session){	struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);	return iscsi_hostdata(shost->hostdata);}EXPORT_SYMBOL_GPL(class_to_transport_session);/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */#define SNA32_CHECK 2147483648ULstatic int iscsi_sna_lt(u32 n1, u32 n2){	return n1 != n2 && ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||			    (n1 > n2 && (n2 - n1 < SNA32_CHECK)));}/* Serial Number Arithmetic, 32 bits, less than, RFC1982 */static int iscsi_sna_lte(u32 n1, u32 n2){	return n1 == n2 || ((n1 < n2 && (n2 - n1 < SNA32_CHECK)) ||			    (n1 > n2 && (n2 - n1 < SNA32_CHECK)));}voidiscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr){	uint32_t max_cmdsn = be32_to_cpu(hdr->max_cmdsn);	uint32_t exp_cmdsn = be32_to_cpu(hdr->exp_cmdsn);	/*	 * standard specifies this check for when to update expected and	 * max sequence numbers	 */	if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1))		return;	if (exp_cmdsn != session->exp_cmdsn &&	    !iscsi_sna_lt(exp_cmdsn, session->exp_cmdsn))		session->exp_cmdsn = exp_cmdsn;	if (max_cmdsn != session->max_cmdsn &&	    !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) {		session->max_cmdsn = max_cmdsn;		/*		 * if the window closed with IO queued, then kick the		 * xmit thread		 */		if (!list_empty(&session->leadconn->xmitqueue) ||		    __kfifo_len(session->leadconn->mgmtqueue))			scsi_queue_work(session->host,					&session->leadconn->xmitwork);	}}EXPORT_SYMBOL_GPL(iscsi_update_cmdsn);void iscsi_prep_unsolicit_data_pdu(struct iscsi_cmd_task *ctask,				   struct iscsi_data *hdr){	struct iscsi_conn *conn = ctask->conn;	memset(hdr, 0, sizeof(struct iscsi_data));	hdr->ttt = cpu_to_be32(ISCSI_RESERVED_TAG);	hdr->datasn = cpu_to_be32(ctask->unsol_datasn);	ctask->unsol_datasn++;	hdr->opcode = ISCSI_OP_SCSI_DATA_OUT;	memcpy(hdr->lun, ctask->hdr->lun, sizeof(hdr->lun));	hdr->itt = ctask->hdr->itt;	hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);	hdr->offset = cpu_to_be32(ctask->unsol_offset);	if (ctask->unsol_count > conn->max_xmit_dlength) {		hton24(hdr->dlength, conn->max_xmit_dlength);		ctask->data_count = conn->max_xmit_dlength;		ctask->unsol_offset += ctask->data_count;		hdr->flags = 0;	} else {		hton24(hdr->dlength, ctask->unsol_count);		ctask->data_count = ctask->unsol_count;		hdr->flags = ISCSI_FLAG_CMD_FINAL;	}}EXPORT_SYMBOL_GPL(iscsi_prep_unsolicit_data_pdu);/** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @ctask: iscsi cmd task * * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set * fields like dlength or final based on how much data it sends */static void iscsi_prep_scsi_cmd_pdu(struct iscsi_cmd_task *ctask){	struct iscsi_conn *conn = ctask->conn;	struct iscsi_session *session = conn->session;	struct iscsi_cmd *hdr = ctask->hdr;	struct scsi_cmnd *sc = ctask->sc;        hdr->opcode = ISCSI_OP_SCSI_CMD;        hdr->flags = ISCSI_ATTR_SIMPLE;        int_to_scsilun(sc->device->lun, (struct scsi_lun *)hdr->lun);        hdr->itt = build_itt(ctask->itt, conn->id, session->age);        hdr->data_length = cpu_to_be32(scsi_bufflen(sc));        hdr->cmdsn = cpu_to_be32(session->cmdsn);        session->cmdsn++;        hdr->exp_statsn = cpu_to_be32(conn->exp_statsn);        memcpy(hdr->cdb, sc->cmnd, sc->cmd_len);	if (sc->cmd_len < MAX_COMMAND_SIZE)		memset(&hdr->cdb[sc->cmd_len], 0,			MAX_COMMAND_SIZE - sc->cmd_len);	ctask->data_count = 0;	ctask->imm_count = 0;	if (sc->sc_data_direction == DMA_TO_DEVICE) {		hdr->flags |= ISCSI_FLAG_CMD_WRITE;		/*		 * Write counters:		 *		 *	imm_count	bytes to be sent right after		 *			SCSI PDU Header		 *		 *	unsol_count	bytes(as Data-Out) to be sent		 *			without	R2T ack right after		 *			immediate data		 *		 *	r2t_data_count	bytes to be sent via R2T ack's		 *		 *      pad_count       bytes to be sent as zero-padding		 */		ctask->unsol_count = 0;		ctask->unsol_offset = 0;		ctask->unsol_datasn = 0;		if (session->imm_data_en) {			if (scsi_bufflen(sc) >= session->first_burst)				ctask->imm_count = min(session->first_burst,							conn->max_xmit_dlength);			else				ctask->imm_count = min(scsi_bufflen(sc),							conn->max_xmit_dlength);			hton24(ctask->hdr->dlength, ctask->imm_count);		} else			zero_data(ctask->hdr->dlength);		if (!session->initial_r2t_en) {			ctask->unsol_count = min((session->first_burst),				(scsi_bufflen(sc))) - ctask->imm_count;			ctask->unsol_offset = ctask->imm_count;		}		if (!ctask->unsol_count)			/* No unsolicit Data-Out's */			ctask->hdr->flags |= ISCSI_FLAG_CMD_FINAL;	} else {		hdr->flags |= ISCSI_FLAG_CMD_FINAL;		zero_data(hdr->dlength);		if (sc->sc_data_direction == DMA_FROM_DEVICE)			hdr->flags |= ISCSI_FLAG_CMD_READ;	}	conn->scsicmd_pdus_cnt++;        debug_scsi("iscsi prep [%s cid %d sc %p cdb 0x%x itt 0x%x len %d "		"cmdsn %d win %d]\n",                sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read",		conn->id, sc, sc->cmnd[0], ctask->itt, scsi_bufflen(sc),                session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1);}/** * iscsi_complete_command - return command back to scsi-ml * @ctask: iscsi cmd task * * Must be called with session lock. * This function returns the scsi command to scsi-ml and returns * the cmd task to the pool of available cmd tasks. */static void iscsi_complete_command(struct iscsi_cmd_task *ctask){	struct iscsi_session *session = ctask->conn->session;	struct scsi_cmnd *sc = ctask->sc;	ctask->state = ISCSI_TASK_COMPLETED;	ctask->sc = NULL;	/* SCSI eh reuses commands to verify us */	sc->SCp.ptr = NULL;	list_del_init(&ctask->running);	__kfifo_put(session->cmdpool.queue, (void*)&ctask, sizeof(void*));	sc->scsi_done(sc);}static void __iscsi_get_ctask(struct iscsi_cmd_task *ctask){	atomic_inc(&ctask->refcount);}static void __iscsi_put_ctask(struct iscsi_cmd_task *ctask){	if (atomic_dec_and_test(&ctask->refcount))		iscsi_complete_command(ctask);}/** * iscsi_cmd_rsp - SCSI Command Response processing * @conn: iscsi connection * @hdr: iscsi header * @ctask: scsi command task * @data: cmd data buffer * @datalen: len of buffer * * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and * then completes the command and task. **/static void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr,			       struct iscsi_cmd_task *ctask, char *data,			       int datalen){	struct iscsi_cmd_rsp *rhdr = (struct iscsi_cmd_rsp *)hdr;	struct iscsi_session *session = conn->session;	struct scsi_cmnd *sc = ctask->sc;	iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr);	conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1;	sc->result = (DID_OK << 16) | rhdr->cmd_status;	if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) {		sc->result = DID_ERROR << 16;		goto out;	}	if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) {		uint16_t senselen;		if (datalen < 2) {invalid_datalen:			printk(KERN_ERR "iscsi: Got CHECK_CONDITION but "			       "invalid data buffer size of %d\n", datalen);			sc->result = DID_BAD_TARGET << 16;			goto out;		}		senselen = be16_to_cpu(get_unaligned((__be16 *) data));		if (datalen < senselen)			goto invalid_datalen;		memcpy(sc->sense_buffer, data + 2,		       min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));		debug_scsi("copied %d bytes of sense\n",			   min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE));	}	if (rhdr->flags & ISCSI_FLAG_CMD_UNDERFLOW) {		int res_count = be32_to_cpu(rhdr->residual_count);		if (res_count > 0 && res_count <= scsi_bufflen(sc))			scsi_set_resid(sc, res_count);		else			sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;	} else if (rhdr->flags & ISCSI_FLAG_CMD_BIDI_UNDERFLOW)		sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status;	else if (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW)		scsi_set_resid(sc, be32_to_cpu(rhdr->residual_count));out:	debug_scsi("done [sc %lx res %d itt 0x%x]\n",		   (long)sc, sc->result, ctask->itt);	conn->scsirsp_pdus_cnt++;	__iscsi_put_ctask(ctask);}static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr){	struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr;	conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;	conn->tmfrsp_pdus_cnt++;	if (conn->tmabort_state != TMABORT_INITIAL)		return;	if (tmf->response == ISCSI_TMF_RSP_COMPLETE)		conn->tmabort_state = TMABORT_SUCCESS;	else if (tmf->response == ISCSI_TMF_RSP_NO_TASK)		conn->tmabort_state = TMABORT_NOT_FOUND;	else		conn->tmabort_state = TMABORT_FAILED;	wake_up(&conn->ehwait);}static int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr,			       char *data, int datalen){	struct iscsi_reject *reject = (struct iscsi_reject *)hdr;	struct iscsi_hdr rejected_pdu;	uint32_t itt;	conn->exp_statsn = be32_to_cpu(reject->statsn) + 1;	if (reject->reason == ISCSI_REASON_DATA_DIGEST_ERROR) {		if (ntoh24(reject->dlength) > datalen)			return ISCSI_ERR_PROTO;		if (ntoh24(reject->dlength) >= sizeof(struct iscsi_hdr)) {			memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr));			itt = get_itt(rejected_pdu.itt);			printk(KERN_ERR "itt 0x%x had pdu (op 0x%x) rejected "				"due to DataDigest error.\n", itt,				rejected_pdu.opcode);		}	}	return 0;}/** * __iscsi_complete_pdu - complete pdu * @conn: iscsi conn * @hdr: iscsi header * @data: data buffer * @datalen: len of data buffer * * Completes pdu processing by freeing any resources allocated at * queuecommand or send generic. session lock must be held and verify * itt must have been called. */int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,			 char *data, int datalen){	struct iscsi_session *session = conn->session;	int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0;	struct iscsi_cmd_task *ctask;	struct iscsi_mgmt_task *mtask;	uint32_t itt;	if (hdr->itt != RESERVED_ITT)		itt = get_itt(hdr->itt);	else		itt = ~0U;	if (itt < session->cmds_max) {		ctask = session->cmds[itt];		debug_scsi("cmdrsp [op 0x%x cid %d itt 0x%x len %d]\n",			   opcode, conn->id, ctask->itt, datalen);		switch(opcode) {		case ISCSI_OP_SCSI_CMD_RSP:			BUG_ON((void*)ctask != ctask->sc->SCp.ptr);			iscsi_scsi_cmd_rsp(conn, hdr, ctask, data,					   datalen);			break;		case ISCSI_OP_SCSI_DATA_IN:			BUG_ON((void*)ctask != ctask->sc->SCp.ptr);			if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {				conn->scsirsp_pdus_cnt++;				__iscsi_put_ctask(ctask);			}			break;		case ISCSI_OP_R2T:			/* LLD handles this for now */			break;		default:			rc = ISCSI_ERR_BAD_OPCODE;			break;		}	} else if (itt >= ISCSI_MGMT_ITT_OFFSET &&		   itt < ISCSI_MGMT_ITT_OFFSET + session->mgmtpool_max) {		mtask = session->mgmt_cmds[itt - ISCSI_MGMT_ITT_OFFSET];		debug_scsi("immrsp [op 0x%x cid %d itt 0x%x len %d]\n",			   opcode, conn->id, mtask->itt, datalen);		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);		switch(opcode) {		case ISCSI_OP_LOGOUT_RSP:			if (datalen) {				rc = ISCSI_ERR_PROTO;				break;			}			conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;			/* fall through */		case ISCSI_OP_LOGIN_RSP:		case ISCSI_OP_TEXT_RSP:			/*			 * login related PDU's exp_statsn is handled in			 * userspace			 */			if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))				rc = ISCSI_ERR_CONN_FAILED;			list_del(&mtask->running);			if (conn->login_mtask != mtask)				__kfifo_put(session->mgmtpool.queue,					    (void*)&mtask, sizeof(void*));			break;		case ISCSI_OP_SCSI_TMFUNC_RSP:			if (datalen) {				rc = ISCSI_ERR_PROTO;				break;			}			iscsi_tmf_rsp(conn, hdr);			break;		case ISCSI_OP_NOOP_IN:			if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) {				rc = ISCSI_ERR_PROTO;				break;			}			conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;			if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))				rc = ISCSI_ERR_CONN_FAILED;			list_del(&mtask->running);			if (conn->login_mtask != mtask)				__kfifo_put(session->mgmtpool.queue,					    (void*)&mtask, sizeof(void*));			break;		default:			rc = ISCSI_ERR_BAD_OPCODE;			break;		}	} else if (itt == ~0U) {		iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr);		switch(opcode) {		case ISCSI_OP_NOOP_IN:			if (datalen) {				rc = ISCSI_ERR_PROTO;				break;			}			if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG))				break;			if (iscsi_recv_pdu(conn->cls_conn, hdr, NULL, 0))				rc = ISCSI_ERR_CONN_FAILED;			break;		case ISCSI_OP_REJECT:			rc = iscsi_handle_reject(conn, hdr, data, datalen);			break;		case ISCSI_OP_ASYNC_EVENT:			conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;			if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen))				rc = ISCSI_ERR_CONN_FAILED;			break;		default:			rc = ISCSI_ERR_BAD_OPCODE;			break;		}	} else		rc = ISCSI_ERR_BAD_ITT;	return rc;}EXPORT_SYMBOL_GPL(__iscsi_complete_pdu);int iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,		       char *data, int datalen){	int rc;	spin_lock(&conn->session->lock);	rc = __iscsi_complete_pdu(conn, hdr, data, datalen);	spin_unlock(&conn->session->lock);	return rc;}EXPORT_SYMBOL_GPL(iscsi_complete_pdu);/* verify itt (itt encoding: age+cid+itt) */int iscsi_verify_itt(struct iscsi_conn *conn, struct iscsi_hdr *hdr,		     uint32_t *ret_itt){	struct iscsi_session *session = conn->session;	struct iscsi_cmd_task *ctask;	uint32_t itt;	if (hdr->itt != RESERVED_ITT) {		if (((__force u32)hdr->itt & ISCSI_AGE_MASK) !=		    (session->age << ISCSI_AGE_SHIFT)) {			printk(KERN_ERR "iscsi: received itt %x expected "				"session age (%x)\n", (__force u32)hdr->itt,				session->age & ISCSI_AGE_MASK);			return ISCSI_ERR_BAD_ITT;		}		if (((__force u32)hdr->itt & ISCSI_CID_MASK) !=		    (conn->id << ISCSI_CID_SHIFT)) {			printk(KERN_ERR "iscsi: received itt %x, expected "				"CID (%x)\n", (__force u32)hdr->itt, conn->id);			return ISCSI_ERR_BAD_ITT;		}		itt = get_itt(hdr->itt);	} else		itt = ~0U;

⌨️ 快捷键说明

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