Commit 62f38300 authored by Mike Christie's avatar Mike Christie Committed by James Bottomley

[SCSI] iscsi_tcp: fix padding, data digests, and IO at weird offsets

iscsi_tcp calculates padding by using the expected transfer length. This
has the problem where if we have immediate data = no and initial R2T =
yes, and the transfer length ended up needing padding then we send:

1. header
2. padding which should have gone after data
3. data

Besides this bug, we also assume the target will always ask for nice
transfer lengths and the first burst length will always be a nice value.
As far as I can tell form the RFC this is not a requirement. It would be
silly to do this, but if someone did it we will end doing bad things.

Finally the last bug in that bit of code is in our handling of the
recalculation of data digests when we do not send a whole iscsi_buf in
one try. The bug here is that we call crypto_digest_final on a
iscsi_sendpage error, then when we send the rest of the iscsi_buf, we
doiscsi_data_digest_init and this causes the previous data digest to be
lost.

And to make matters worse, some of these bugs are replicated over and
over and over again for immediate data, solicited data and unsolicited
data. So the attached patch made over the iscsi git tree (see
kernel.org/git for details) which I updated today to include the patches
I said I merged, consolidates the sending of data, padding and digests
and calculation of data digests and fixes the above bugs.
Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 98a9416a
This diff is collapsed.
......@@ -31,23 +31,21 @@
#define IN_PROGRESS_DDIGEST_RECV 0x3
/* xmit state machine */
#define XMSTATE_IDLE 0x0
#define XMSTATE_R_HDR 0x1
#define XMSTATE_W_HDR 0x2
#define XMSTATE_IMM_HDR 0x4
#define XMSTATE_IMM_DATA 0x8
#define XMSTATE_UNS_INIT 0x10
#define XMSTATE_UNS_HDR 0x20
#define XMSTATE_UNS_DATA 0x40
#define XMSTATE_SOL_HDR 0x80
#define XMSTATE_SOL_DATA 0x100
#define XMSTATE_W_PAD 0x200
#define XMSTATE_DATA_DIGEST 0x400
#define ISCSI_CONN_RCVBUF_MIN 262144
#define ISCSI_CONN_SNDBUF_MIN 262144
#define XMSTATE_IDLE 0x0
#define XMSTATE_R_HDR 0x1
#define XMSTATE_W_HDR 0x2
#define XMSTATE_IMM_HDR 0x4
#define XMSTATE_IMM_DATA 0x8
#define XMSTATE_UNS_INIT 0x10
#define XMSTATE_UNS_HDR 0x20
#define XMSTATE_UNS_DATA 0x40
#define XMSTATE_SOL_HDR 0x80
#define XMSTATE_SOL_DATA 0x100
#define XMSTATE_W_PAD 0x200
#define XMSTATE_W_RESEND_PAD 0x400
#define XMSTATE_W_RESEND_DATA_DIGEST 0x800
#define ISCSI_PAD_LEN 4
#define ISCSI_R2T_MAX 16
#define ISCSI_SG_TABLESIZE SG_ALL
#define ISCSI_TCP_MAX_CMD_LEN 16
......@@ -162,13 +160,10 @@ struct iscsi_tcp_cmd_task {
struct iscsi_queue r2tpool;
struct kfifo *r2tqueue;
struct iscsi_r2t_info **r2ts;
uint32_t datadigest; /* for recover digest */
int digest_count;
uint32_t immdigest; /* for imm data */
struct iscsi_buf immbuf; /* for imm data digest */
struct iscsi_data_task *dtask; /* data task in progress*/
struct iscsi_data_task unsol_dtask; /* unsol data task */
int digest_offset; /* for partial buff digest */
};
#endif /* ISCSI_H */
......@@ -325,6 +325,30 @@ static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr)
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 = rejected_pdu.itt & ISCSI_ITT_MASK;
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
......@@ -436,6 +460,11 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
break;
}
} else if (itt == ISCSI_RESERVED_TAG) {
rc = iscsi_check_assign_cmdsn(session,
(struct iscsi_nopin*)hdr);
if (rc)
goto done;
switch(opcode) {
case ISCSI_OP_NOOP_IN:
if (datalen) {
......@@ -443,11 +472,6 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
break;
}
rc = iscsi_check_assign_cmdsn(session,
(struct iscsi_nopin*)hdr);
if (rc)
break;
if (hdr->ttt == ISCSI_RESERVED_TAG)
break;
......@@ -455,7 +479,8 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr,
rc = ISCSI_ERR_CONN_FAILED;
break;
case ISCSI_OP_REJECT:
/* we need sth like iscsi_reject_rsp()*/
rc = iscsi_handle_reject(conn, hdr, data, datalen);
break;
case ISCSI_OP_ASYNC_EVENT:
conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1;
/* we need sth like iscsi_async_event_rsp() */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment