📄 iscsi_tcp.c
字号:
(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 + -