- * 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 int
-iscsi_tcp_copy(struct iscsi_conn *conn)
-{
- struct iscsi_tcp_conn *tcp_conn = conn->dd_data;
- int buf_size = tcp_conn->in.datalen;
- 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 void
-partial_sg_digest_update(struct iscsi_tcp_conn *tcp_conn,
- struct scatterlist *sg, int offset, int length)
-{
- struct scatterlist temp;
-
- memcpy(&temp, sg, sizeof(struct scatterlist));
- temp.offset = offset;
- temp.length = length;
- crypto_digest_update(tcp_conn->data_rx_tfm, &temp, 1);
-}
-
-static void
-iscsi_recv_digest_update(struct iscsi_tcp_conn *tcp_conn, char* buf, int len)
-{
- struct scatterlist tmp;
-
- sg_init_one(&tmp, buf, len);
- crypto_digest_update(tcp_conn->data_rx_tfm, &tmp, 1);
-}
-
-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);
-
- /*
- * copying Data-In into the Scsi_Cmnd
- */
- if (!sc->use_sg) {
- i = ctask->data_count;
- rc = iscsi_ctask_copy(tcp_conn, ctask, sc->request_buffer,
- sc->request_bufflen,
- tcp_ctask->data_offset);
- if (rc == -EAGAIN)
- return rc;
- if (conn->datadgst_en)
- iscsi_recv_digest_update(tcp_conn, sc->request_buffer,
- i);
- rc = 0;
- goto done;
- }
-
- offset = tcp_ctask->data_offset;
- sg = sc->request_buffer;
-
- 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 < sc->use_sg; i++) {
- char *dest;
-
- dest = kmap_atomic(sg[i].page, 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_digest_update(
- tcp_conn->data_rx_tfm,
- &sg[i], 1);
- else
- partial_sg_digest_update(tcp_conn,
- &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, &sg[i],
- sg[i].offset, sg[i].length-rc);
- rc = 0;
- break;
- }
-
- if (!tcp_conn->in.copy)
- return -EAGAIN;
- }
- BUG_ON(ctask->data_count);
-
-done:
- /* 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_tcp_cleanup_ctask(conn, ctask);
- __iscsi_complete_pdu(conn, tcp_conn->in.hdr, NULL, 0);
- spin_unlock(&conn->session->lock);
- }
-
- return rc;
-}
-
-static int
-iscsi_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:
- spin_lock(&conn->session->lock);
- iscsi_tcp_cleanup_ctask(conn, tcp_conn->in.ctask);
- spin_unlock(&conn->session->lock);
- 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)) {
- 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