]>
Commit | Line | Data |
---|---|---|
82094b55 AF |
1 | From: Mike Christie <michaelc@cs.wisc.edu> |
2 | Date: Wed, 24 Sep 2008 11:46:09 -0500 | |
3 | Subject: libiscsi: fix data corruption when target has to resend data-in packets | |
4 | References: bnc#472432 | |
5 | X-Git: 1d9edf0270cb5a434d32e95279ce9493581906b3 | |
6 | ||
7 | iscsi_tcp was updating the exp_statsn (exp_statsn acknowledges | |
8 | status and tells the target it is ok to let the resources for | |
9 | a iscsi pdu to be reused) before it got all the data for pdu read | |
10 | into OS buffers. Data corruption was occuring if something happens | |
11 | to a packet and the network layer requests a retransmit, and the | |
12 | initiator has told the target about the udpated exp_statsn ack, | |
13 | then the target may be sending data from a buffer it has reused | |
14 | for a new iscsi pdu. This fixes the problem by having the LLD | |
15 | (iscsi_tcp in this case) just handle the transferring of data, and | |
16 | has libiscsi handle the processing of status (libiscsi completion | |
17 | processing is done after LLD data transfers are complete). | |
18 | ||
19 | Signed-off-by: Mike Christie <michaelc@cs.wisc.edu> | |
20 | Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> | |
21 | Signed-off-by: Hannes Reinecke <hare@suse.de> | |
22 | --- | |
23 | drivers/scsi/iscsi_tcp.c | 29 +++++------------------------ | |
24 | drivers/scsi/libiscsi.c | 41 +++++++++++++++++++++++++++++++++++------ | |
25 | 2 files changed, 40 insertions(+), 30 deletions(-) | |
26 | ||
27 | diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c | |
28 | index 25094c9..babe81a 100644 | |
29 | --- a/drivers/scsi/iscsi_tcp.c | |
30 | +++ b/drivers/scsi/iscsi_tcp.c | |
31 | @@ -530,22 +530,20 @@ iscsi_tcp_cleanup_task(struct iscsi_conn *conn, struct iscsi_task *task) | |
32 | EXPORT_SYMBOL_GPL(iscsi_tcp_cleanup_task); | |
33 | ||
34 | /** | |
35 | - * iscsi_data_rsp - SCSI Data-In Response processing | |
36 | + * iscsi_data_in - SCSI Data-In Response processing | |
37 | * @conn: iscsi connection | |
38 | * @task: scsi command task | |
39 | **/ | |
40 | static int | |
41 | -iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task) | |
42 | +iscsi_data_in(struct iscsi_conn *conn, struct iscsi_task *task) | |
43 | { | |
44 | struct iscsi_tcp_conn *tcp_conn = conn->dd_data; | |
45 | struct iscsi_tcp_task *tcp_task = task->dd_data; | |
46 | struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)tcp_conn->in.hdr; | |
47 | - struct iscsi_session *session = conn->session; | |
48 | - struct scsi_cmnd *sc = task->sc; | |
49 | int datasn = be32_to_cpu(rhdr->datasn); | |
50 | - unsigned total_in_length = scsi_in(sc)->length; | |
51 | + unsigned total_in_length = scsi_in(task->sc)->length; | |
52 | ||
53 | - iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr); | |
54 | + iscsi_update_cmdsn(conn->session, (struct iscsi_nopin*)rhdr); | |
55 | if (tcp_conn->in.datalen == 0) | |
56 | return 0; | |
57 | ||
58 | @@ -565,23 +563,6 @@ iscsi_data_rsp(struct iscsi_conn *conn, struct iscsi_task *task) | |
59 | return ISCSI_ERR_DATA_OFFSET; | |
60 | } | |
61 | ||
62 | - if (rhdr->flags & ISCSI_FLAG_DATA_STATUS) { | |
63 | - sc->result = (DID_OK << 16) | rhdr->cmd_status; | |
64 | - conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; | |
65 | - if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | | |
66 | - ISCSI_FLAG_DATA_OVERFLOW)) { | |
67 | - int res_count = be32_to_cpu(rhdr->residual_count); | |
68 | - | |
69 | - if (res_count > 0 && | |
70 | - (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || | |
71 | - res_count <= total_in_length)) | |
72 | - scsi_in(sc)->resid = res_count; | |
73 | - else | |
74 | - sc->result = (DID_BAD_TARGET << 16) | | |
75 | - rhdr->cmd_status; | |
76 | - } | |
77 | - } | |
78 | - | |
79 | conn->datain_pdus_cnt++; | |
80 | return 0; | |
81 | } | |
82 | @@ -781,7 +762,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) | |
83 | if (!task) | |
84 | rc = ISCSI_ERR_BAD_ITT; | |
85 | else | |
86 | - rc = iscsi_data_rsp(conn, task); | |
87 | + rc = iscsi_data_in(conn, task); | |
88 | if (rc) { | |
89 | spin_unlock(&conn->session->lock); | |
90 | break; | |
91 | diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c | |
92 | index e7624db..8905895 100644 | |
93 | --- a/drivers/scsi/libiscsi.c | |
94 | +++ b/drivers/scsi/libiscsi.c | |
95 | @@ -645,6 +645,40 @@ out: | |
96 | __iscsi_put_task(task); | |
97 | } | |
98 | ||
99 | +/** | |
100 | + * iscsi_data_in_rsp - SCSI Data-In Response processing | |
101 | + * @conn: iscsi connection | |
102 | + * @hdr: iscsi pdu | |
103 | + * @task: scsi command task | |
104 | + **/ | |
105 | +static void | |
106 | +iscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |
107 | + struct iscsi_task *task) | |
108 | +{ | |
109 | + struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)hdr; | |
110 | + struct scsi_cmnd *sc = task->sc; | |
111 | + | |
112 | + if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS)) | |
113 | + return; | |
114 | + | |
115 | + sc->result = (DID_OK << 16) | rhdr->cmd_status; | |
116 | + conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; | |
117 | + if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | | |
118 | + ISCSI_FLAG_DATA_OVERFLOW)) { | |
119 | + int res_count = be32_to_cpu(rhdr->residual_count); | |
120 | + | |
121 | + if (res_count > 0 && | |
122 | + (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || | |
123 | + res_count <= scsi_in(sc)->length)) | |
124 | + scsi_in(sc)->resid = res_count; | |
125 | + else | |
126 | + sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; | |
127 | + } | |
128 | + | |
129 | + conn->scsirsp_pdus_cnt++; | |
130 | + __iscsi_put_task(task); | |
131 | +} | |
132 | + | |
133 | static void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) | |
134 | { | |
135 | struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; | |
136 | @@ -911,12 +945,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, | |
137 | iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); | |
138 | break; | |
139 | case ISCSI_OP_SCSI_DATA_IN: | |
140 | - if (hdr->flags & ISCSI_FLAG_DATA_STATUS) { | |
141 | - conn->scsirsp_pdus_cnt++; | |
142 | - iscsi_update_cmdsn(session, | |
143 | - (struct iscsi_nopin*) hdr); | |
144 | - __iscsi_put_task(task); | |
145 | - } | |
146 | + iscsi_data_in_rsp(conn, hdr, task); | |
147 | break; | |
148 | case ISCSI_OP_LOGOUT_RSP: | |
149 | iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); | |
150 | -- | |
151 | 1.6.0.2 | |
152 |