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

📄 iscsi_tcp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
				   (u8 *)&cdgst);		rdgst = *(uint32_t*)((char*)hdr + sizeof(struct iscsi_hdr) +				     ahslen);		if (cdgst != rdgst) {			printk(KERN_ERR "iscsi_tcp: hdrdgst error "			       "recv 0x%x calc 0x%x\n", rdgst, cdgst);			return ISCSI_ERR_HDR_DGST;		}	}	opcode = hdr->opcode & ISCSI_OPCODE_MASK;	/* verify itt (itt encoding: age+cid+itt) */	rc = iscsi_verify_itt(conn, hdr, &itt);	if (rc == ISCSI_ERR_NO_SCSI_CMD) {		tcp_conn->in.datalen = 0; /* force drop */		return 0;	} else if (rc)		return rc;	debug_tcp("opcode 0x%x offset %d copy %d ahslen %d datalen %d\n",		  opcode, tcp_conn->in.offset, tcp_conn->in.copy,		  ahslen, tcp_conn->in.datalen);	switch(opcode) {	case ISCSI_OP_SCSI_DATA_IN:		tcp_conn->in.ctask = session->cmds[itt];		rc = iscsi_data_rsp(conn, tcp_conn->in.ctask);		if (rc)			return rc;		/* fall through */	case ISCSI_OP_SCSI_CMD_RSP:		tcp_conn->in.ctask = session->cmds[itt];		if (tcp_conn->in.datalen)			goto copy_hdr;		spin_lock(&session->lock);		rc = __iscsi_complete_pdu(conn, hdr, NULL, 0);		spin_unlock(&session->lock);		break;	case ISCSI_OP_R2T:		tcp_conn->in.ctask = session->cmds[itt];		if (ahslen)			rc = ISCSI_ERR_AHSLEN;		else if (tcp_conn->in.ctask->sc->sc_data_direction ==								DMA_TO_DEVICE)			rc = iscsi_r2t_rsp(conn, tcp_conn->in.ctask);		else			rc = ISCSI_ERR_PROTO;		break;	case ISCSI_OP_LOGIN_RSP:	case ISCSI_OP_TEXT_RSP:	case ISCSI_OP_REJECT:	case ISCSI_OP_ASYNC_EVENT:		/*		 * It is possible that we could get a PDU with a buffer larger		 * than 8K, but there are no targets that currently do this.		 * For now we fail until we find a vendor that needs it		 */		if (ISCSI_DEF_MAX_RECV_SEG_LEN <		    tcp_conn->in.datalen) {			printk(KERN_ERR "iscsi_tcp: received buffer of len %u "			      "but conn buffer is only %u (opcode %0x)\n",			      tcp_conn->in.datalen,			      ISCSI_DEF_MAX_RECV_SEG_LEN, opcode);			rc = ISCSI_ERR_PROTO;			break;		}		if (tcp_conn->in.datalen)			goto copy_hdr;	/* fall through */	case ISCSI_OP_LOGOUT_RSP:	case ISCSI_OP_NOOP_IN:	case ISCSI_OP_SCSI_TMFUNC_RSP:		rc = iscsi_complete_pdu(conn, hdr, NULL, 0);		break;	default:		rc = ISCSI_ERR_BAD_OPCODE;		break;	}	return rc;copy_hdr:	/*	 * if we did zero copy for the header but we will need multiple	 * skbs to complete the command then we have to copy the header	 * for later use	 */	if (tcp_conn->in.zero_copy_hdr && tcp_conn->in.copy <=	   (tcp_conn->in.datalen + tcp_conn->in.padding +	    (conn->datadgst_en ? 4 : 0))) {		debug_tcp("Copying header for later use. in.copy %d in.datalen"			  " %d\n", tcp_conn->in.copy, tcp_conn->in.datalen);		memcpy(&tcp_conn->hdr, tcp_conn->in.hdr,		       sizeof(struct iscsi_hdr));		tcp_conn->in.hdr = &tcp_conn->hdr;		tcp_conn->in.zero_copy_hdr = 0;	}	return 0;}/** * iscsi_ctask_copy - copy skb bits to the destanation cmd task * @conn: iscsi tcp connection * @ctask: scsi command task * @buf: buffer to copy to * @buf_size: size of buffer * @offset: offset within the buffer * * Notes: *	The function calls skb_copy_bits() and updates per-connection and *	per-cmd byte counters. * *	Read counters (in bytes): * *	conn->in.offset		offset within in progress SKB *	conn->in.copy		left to copy from in progress SKB *				including padding *	conn->in.copied		copied already from in progress SKB *	conn->data_copied	copied already from in progress buffer *	ctask->sent		total bytes sent up to the MidLayer *	ctask->data_count	left to copy from in progress Data-In *	buf_left		left to copy from in progress buffer **/static inline intiscsi_ctask_copy(struct iscsi_tcp_conn *tcp_conn, struct iscsi_cmd_task *ctask,		void *buf, int buf_size, int offset){	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;	int buf_left = buf_size - (tcp_conn->data_copied + offset);	unsigned size = min(tcp_conn->in.copy, buf_left);	int rc;	size = min(size, ctask->data_count);	debug_tcp("ctask_copy %d bytes at offset %d copied %d\n",	       size, tcp_conn->in.offset, tcp_conn->in.copied);	BUG_ON(size <= 0);	BUG_ON(tcp_ctask->sent + size > scsi_bufflen(ctask->sc));	rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,			   (char*)buf + (offset + tcp_conn->data_copied), size);	/* must fit into skb->len */	BUG_ON(rc);	tcp_conn->in.offset += size;	tcp_conn->in.copy -= size;	tcp_conn->in.copied += size;	tcp_conn->data_copied += size;	tcp_ctask->sent += size;	ctask->data_count -= size;	BUG_ON(tcp_conn->in.copy < 0);	BUG_ON(ctask->data_count < 0);	if (buf_size != (tcp_conn->data_copied + offset)) {		if (!ctask->data_count) {			BUG_ON(buf_size - tcp_conn->data_copied < 0);			/* done with this PDU */			return buf_size - tcp_conn->data_copied;		}		return -EAGAIN;	}	/* done with this buffer or with both - PDU and buffer */	tcp_conn->data_copied = 0;	return 0;}/** * iscsi_tcp_copy - copy skb bits to the destanation buffer * @conn: iscsi tcp connection * * Notes: *	The function calls skb_copy_bits() and updates per-connection *	byte counters. **/static inline intiscsi_tcp_copy(struct iscsi_conn *conn, int buf_size){	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;	int buf_left = buf_size - tcp_conn->data_copied;	int size = min(tcp_conn->in.copy, buf_left);	int rc;	debug_tcp("tcp_copy %d bytes at offset %d copied %d\n",	       size, tcp_conn->in.offset, tcp_conn->data_copied);	BUG_ON(size <= 0);	rc = skb_copy_bits(tcp_conn->in.skb, tcp_conn->in.offset,			   (char*)conn->data + tcp_conn->data_copied, size);	BUG_ON(rc);	tcp_conn->in.offset += size;	tcp_conn->in.copy -= size;	tcp_conn->in.copied += size;	tcp_conn->data_copied += size;	if (buf_size != tcp_conn->data_copied)		return -EAGAIN;	return 0;}static inline voidpartial_sg_digest_update(struct hash_desc *desc, struct scatterlist *sg,			 int offset, int length){	struct scatterlist temp;	sg_init_table(&temp, 1);	sg_set_page(&temp, sg_page(sg), length, offset);	crypto_hash_update(desc, &temp, length);}static voidiscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len){	struct scatterlist tmp;	sg_init_one(&tmp, buf, len);	crypto_hash_update(&tcp_conn->rx_hash, &tmp, len);}static int iscsi_scsi_data_in(struct iscsi_conn *conn){	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;	struct iscsi_cmd_task *ctask = tcp_conn->in.ctask;	struct iscsi_tcp_cmd_task *tcp_ctask = ctask->dd_data;	struct scsi_cmnd *sc = ctask->sc;	struct scatterlist *sg;	int i, offset, rc = 0;	BUG_ON((void*)ctask != sc->SCp.ptr);	offset = tcp_ctask->data_offset;	sg = scsi_sglist(sc);	if (tcp_ctask->data_offset)		for (i = 0; i < tcp_ctask->sg_count; i++)			offset -= sg[i].length;	/* we've passed through partial sg*/	if (offset < 0)		offset = 0;	for (i = tcp_ctask->sg_count; i < scsi_sg_count(sc); i++) {		char *dest;		dest = kmap_atomic(sg_page(&sg[i]), KM_SOFTIRQ0);		rc = iscsi_ctask_copy(tcp_conn, ctask, dest + sg[i].offset,				      sg[i].length, offset);		kunmap_atomic(dest, KM_SOFTIRQ0);		if (rc == -EAGAIN)			/* continue with the next SKB/PDU */			return rc;		if (!rc) {			if (conn->datadgst_en) {				if (!offset)					crypto_hash_update(							&tcp_conn->rx_hash,							&sg[i], sg[i].length);				else					partial_sg_digest_update(							&tcp_conn->rx_hash,							&sg[i],							sg[i].offset + offset,							sg[i].length - offset);			}			offset = 0;			tcp_ctask->sg_count++;		}		if (!ctask->data_count) {			if (rc && conn->datadgst_en)				/*				 * data-in is complete, but buffer not...				 */				partial_sg_digest_update(&tcp_conn->rx_hash,							 &sg[i],							 sg[i].offset,							 sg[i].length-rc);			rc = 0;			break;		}		if (!tcp_conn->in.copy)			return -EAGAIN;	}	BUG_ON(ctask->data_count);	/* check for non-exceptional status */	if (tcp_conn->in.hdr->flags & ISCSI_FLAG_DATA_STATUS) {		debug_scsi("done [sc %lx res %d itt 0x%x flags 0x%x]\n",			   (long)sc, sc->result, ctask->itt,			   tcp_conn->in.hdr->flags);		spin_lock(&conn->session->lock);		__iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);		spin_unlock(&conn->session->lock);	}	return rc;}static intiscsi_data_recv(struct iscsi_conn *conn){	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;	int rc = 0, opcode;	opcode = tcp_conn->in.hdr->opcode & ISCSI_OPCODE_MASK;	switch (opcode) {	case ISCSI_OP_SCSI_DATA_IN:		rc = iscsi_scsi_data_in(conn);		break;	case ISCSI_OP_SCSI_CMD_RSP:	case ISCSI_OP_TEXT_RSP:	case ISCSI_OP_LOGIN_RSP:	case ISCSI_OP_ASYNC_EVENT:	case ISCSI_OP_REJECT:		/*		 * Collect data segment to the connection's data		 * placeholder		 */		if (iscsi_tcp_copy(conn, tcp_conn->in.datalen)) {			rc = -EAGAIN;			goto exit;		}		rc = iscsi_complete_pdu(conn, tcp_conn->in.hdr, conn->data,					tcp_conn->in.datalen);		if (!rc && conn->datadgst_en && opcode != ISCSI_OP_LOGIN_RSP)			iscsi_recv_digest_update(tcp_conn, conn->data,			  			tcp_conn->in.datalen);		break;	default:		BUG_ON(1);	}exit:	return rc;}/** * iscsi_tcp_data_recv - TCP receive in sendfile fashion * @rd_desc: read descriptor * @skb: socket buffer * @offset: offset in skb * @len: skb->len - offset **/static intiscsi_tcp_data_recv(read_descriptor_t *rd_desc, struct sk_buff *skb,		unsigned int offset, size_t len){	int rc;	struct iscsi_conn *conn = rd_desc->arg.data;	struct iscsi_tcp_conn *tcp_conn = conn->dd_data;	int processed;	char pad[ISCSI_PAD_LEN];	struct scatterlist sg;	/*	 * Save current SKB and its offset in the corresponding	 * connection context.	 */	tcp_conn->in.copy = skb->len - offset;	tcp_conn->in.offset = offset;	tcp_conn->in.skb = skb;	tcp_conn->in.len = tcp_conn->in.copy;	BUG_ON(tcp_conn->in.copy <= 0);	debug_tcp("in %d bytes\n", tcp_conn->in.copy);more:	tcp_conn->in.copied = 0;	rc = 0;	if (unlikely(conn->suspend_rx)) {		debug_tcp("conn %d Rx suspended!\n", conn->id);		return 0;	}	if (tcp_conn->in_progress == IN_PROGRESS_WAIT_HEADER ||	    tcp_conn->in_progress == IN_PROGRESS_HEADER_GATHER) {		rc = iscsi_hdr_extract(tcp_conn);		if (rc) {		       if (rc == -EAGAIN)				goto nomore;		       else {				iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);				return 0;		       }		}		/*		 * Verify and process incoming PDU header.		 */		rc = iscsi_tcp_hdr_recv(conn);		if (!rc && tcp_conn->in.datalen) {			if (conn->datadgst_en)				crypto_hash_init(&tcp_conn->rx_hash);			tcp_conn->in_progress = IN_PROGRESS_DATA_RECV;		} else if (rc) {			iscsi_conn_failure(conn, rc);			return 0;		}	}	if (tcp_conn->in_progress == IN_PROGRESS_DDIGEST_RECV &&	    tcp_conn->in.copy) {		uint32_t recv_digest;		debug_tcp("extra data_recv offset %d copy %d\n",			  tcp_conn->in.offset, tcp_conn->in.copy);		if (!tcp_conn->data_copied) {			if (tcp_conn->in.padding) {				debug_tcp("padding -> %d\n",					  tcp_conn->in.padding);				memset(pad, 0, tcp_conn->in.padding);				sg_init_one(&sg, pad, tcp_conn->in.padding);				crypto_hash_update(&tcp_conn->rx_hash,						   &sg, sg.length);			}			crypto_hash_final(&tcp_conn->rx_hash,					  (u8 *) &tcp_conn->in.datadgst);			debug_tcp("rx digest 0x%x\n", tcp_conn->in.datadgst);		}		rc = iscsi_tcp_copy(conn, sizeof(uint32_t));		if (rc) {			if (rc == -EAGAIN)				goto again;			iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);			return 0;		}		memcpy(&recv_digest, conn->data, sizeof(uint32_t));		if (recv_digest != tcp_conn->in.datadgst) {			debug_tcp("iscsi_tcp: data digest error!"				  "0x%x != 0x%x\n", recv_digest,				  tcp_conn->in.datadgst);			iscsi_conn_failure(conn, ISCSI_ERR_DATA_DGST);			return 0;		} else {			debug_tcp("iscsi_tcp: data digest match!"				  "0x%x == 0x%x\n", recv_digest,				  tcp_conn->in.datadgst);			tcp_conn->in_progress = IN_PROGRESS_WAIT_HEADER;		}	}	if (tcp_conn->in_progress == IN_PROGRESS_DATA_RECV &&	    tcp_conn->in.copy) {		debug_tcp("data_recv offset %d copy %d\n",		       tcp_conn->in.offset, tcp_conn->in.copy);		rc = iscsi_data_recv(conn);		if (rc) {			if (rc == -EAGAIN)				goto again;			iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);			return 0;		}

⌨️ 快捷键说明

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