| @@ -1,1677 +1,1683 @@ | | | @@ -1,1677 +1,1683 @@ |
1 | /* $NetBSD: iscsi_send.c,v 1.34 2017/02/25 12:03:57 mlelstv Exp $ */ | | 1 | /* $NetBSD: iscsi_send.c,v 1.35 2017/12/03 07:23:12 mlelstv Exp $ */ |
2 | | | 2 | |
3 | /*- | | 3 | /*- |
4 | * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc. | | 4 | * Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation | | 7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Wasabi Systems, Inc. | | 8 | * by Wasabi Systems, Inc. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * | | 18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS | | 19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED | | 20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | | 21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS | | 22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | | 23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | | 24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | | 25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | | 26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | | 27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | | 28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. | | 29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ | | 30 | */ |
31 | #include "iscsi_globals.h" | | 31 | #include "iscsi_globals.h" |
32 | | | 32 | |
33 | #include <sys/file.h> | | 33 | #include <sys/file.h> |
34 | #include <sys/filedesc.h> | | 34 | #include <sys/filedesc.h> |
35 | #include <sys/socket.h> | | 35 | #include <sys/socket.h> |
36 | #include <sys/socketvar.h> | | 36 | #include <sys/socketvar.h> |
37 | #include <sys/atomic.h> | | 37 | #include <sys/atomic.h> |
38 | | | 38 | |
39 | /*#define LUN_1 1 */ | | 39 | /*#define LUN_1 1 */ |
40 | | | 40 | |
41 | /*****************************************************************************/ | | 41 | /*****************************************************************************/ |
42 | | | 42 | |
43 | /* | | 43 | /* |
44 | * my_soo_write: | | 44 | * my_soo_write: |
45 | * Replacement for soo_write with flag handling. | | 45 | * Replacement for soo_write with flag handling. |
46 | * | | 46 | * |
47 | * Parameter: | | 47 | * Parameter: |
48 | * conn The connection | | 48 | * conn The connection |
49 | * u The uio descriptor | | 49 | * u The uio descriptor |
50 | * | | 50 | * |
51 | * Returns: 0 on success, else EIO. | | 51 | * Returns: 0 on success, else EIO. |
52 | */ | | 52 | */ |
53 | | | 53 | |
54 | STATIC int | | 54 | STATIC int |
55 | my_soo_write(connection_t *conn, struct uio *u) | | 55 | my_soo_write(connection_t *conn, struct uio *u) |
56 | { | | 56 | { |
57 | struct socket *so = conn->sock->f_socket; | | 57 | struct socket *so = conn->sock->f_socket; |
58 | int ret; | | 58 | int ret; |
59 | #ifdef ISCSI_DEBUG | | 59 | #ifdef ISCSI_DEBUG |
60 | size_t resid = u->uio_resid; | | 60 | size_t resid = u->uio_resid; |
61 | #endif | | 61 | #endif |
62 | | | 62 | |
63 | KASSERT(u->uio_resid != 0); | | 63 | KASSERT(u->uio_resid != 0); |
64 | | | 64 | |
65 | ret = (*so->so_send)(so, NULL, u, NULL, NULL, 0, conn->threadobj); | | 65 | ret = (*so->so_send)(so, NULL, u, NULL, NULL, 0, conn->threadobj); |
66 | | | 66 | |
67 | DEB(99, ("soo_write done: len = %zu\n", u->uio_resid)); | | 67 | DEB(99, ("soo_write done: len = %zu\n", u->uio_resid)); |
68 | | | 68 | |
69 | if (ret != 0 || u->uio_resid) { | | 69 | if (ret != 0 || u->uio_resid) { |
70 | DEBC(conn, 0, ("Write failed sock %p (ret: %d, req: %zu, resid: %zu)\n", | | 70 | DEBC(conn, 0, ("Write failed sock %p (ret: %d, req: %zu, resid: %zu)\n", |
71 | conn->sock, ret, resid, u->uio_resid)); | | 71 | conn->sock, ret, resid, u->uio_resid)); |
72 | handle_connection_error(conn, ISCSI_STATUS_SOCKET_ERROR, NO_LOGOUT); | | 72 | handle_connection_error(conn, ISCSI_STATUS_SOCKET_ERROR, NO_LOGOUT); |
73 | return EIO; | | 73 | return EIO; |
74 | } | | 74 | } |
75 | return 0; | | 75 | return 0; |
76 | } | | 76 | } |
77 | | | 77 | |
78 | /*****************************************************************************/ | | 78 | /*****************************************************************************/ |
79 | | | 79 | |
80 | /* | | 80 | /* |
81 | * assign_connection: | | 81 | * assign_connection: |
82 | * This function returns the connection to use for the next transaction. | | 82 | * This function returns the connection to use for the next transaction. |
83 | * | | 83 | * |
84 | * Parameter: The session | | 84 | * Parameter: The session |
85 | * | | 85 | * |
86 | * Returns: The connection | | 86 | * Returns: The connection |
87 | */ | | 87 | */ |
88 | | | 88 | |
89 | connection_t * | | 89 | connection_t * |
90 | assign_connection(session_t *session, bool waitok) | | 90 | assign_connection(session_t *session, bool waitok) |
91 | { | | 91 | { |
92 | connection_t *conn, *next; | | 92 | connection_t *conn, *next; |
93 | | | 93 | |
94 | mutex_enter(&session->lock); | | 94 | mutex_enter(&session->lock); |
95 | do { | | 95 | do { |
96 | if (session->terminating || | | 96 | if (session->terminating || |
97 | (conn = session->mru_connection) == NULL) { | | 97 | (conn = session->mru_connection) == NULL) { |
98 | mutex_exit(&session->lock); | | 98 | mutex_exit(&session->lock); |
99 | return NULL; | | 99 | return NULL; |
100 | } | | 100 | } |
101 | next = conn; | | 101 | next = conn; |
102 | do { | | 102 | do { |
103 | next = TAILQ_NEXT(next, connections); | | 103 | next = TAILQ_NEXT(next, connections); |
104 | if (next == NULL) { | | 104 | if (next == NULL) { |
105 | next = TAILQ_FIRST(&session->conn_list); | | 105 | next = TAILQ_FIRST(&session->conn_list); |
106 | } | | 106 | } |
107 | } while (next != NULL && next != conn && | | 107 | } while (next != NULL && next != conn && |
108 | next->state != ST_FULL_FEATURE); | | 108 | next->state != ST_FULL_FEATURE); |
109 | | | 109 | |
110 | if (next->state != ST_FULL_FEATURE) { | | 110 | if (next->state != ST_FULL_FEATURE) { |
111 | if (waitok) { | | 111 | if (waitok) { |
112 | cv_wait(&session->sess_cv, &session->lock); | | 112 | cv_wait(&session->sess_cv, &session->lock); |
113 | next = TAILQ_FIRST(&session->conn_list); | | 113 | next = TAILQ_FIRST(&session->conn_list); |
114 | } else { | | 114 | } else { |
115 | mutex_exit(&session->lock); | | 115 | mutex_exit(&session->lock); |
116 | return NULL; | | 116 | return NULL; |
117 | } | | 117 | } |
118 | } else { | | 118 | } else { |
119 | session->mru_connection = next; | | 119 | session->mru_connection = next; |
120 | } | | 120 | } |
121 | } while (next != NULL && next->state != ST_FULL_FEATURE); | | 121 | } while (next != NULL && next->state != ST_FULL_FEATURE); |
122 | mutex_exit(&session->lock); | | 122 | mutex_exit(&session->lock); |
123 | | | 123 | |
124 | return next; | | 124 | return next; |
125 | } | | 125 | } |
126 | | | 126 | |
127 | | | 127 | |
128 | /* | | 128 | /* |
129 | * reassign_tasks: | | 129 | * reassign_tasks: |
130 | * Reassign pending commands to one of the still existing connections | | 130 | * Reassign pending commands to one of the still existing connections |
131 | * of a session. | | 131 | * of a session. |
132 | * | | 132 | * |
133 | * Parameter: | | 133 | * Parameter: |
134 | * oldconn The terminating connection | | 134 | * oldconn The terminating connection |
135 | */ | | 135 | */ |
136 | | | 136 | |
137 | STATIC void | | 137 | STATIC void |
138 | reassign_tasks(connection_t *oldconn) | | 138 | reassign_tasks(connection_t *oldconn) |
139 | { | | 139 | { |
140 | session_t *sess = oldconn->session; | | 140 | session_t *sess = oldconn->session; |
141 | connection_t *conn; | | 141 | connection_t *conn; |
142 | ccb_t *ccb; | | 142 | ccb_t *ccb; |
143 | pdu_t *pdu = NULL; | | 143 | pdu_t *pdu = NULL; |
144 | pdu_t *opdu; | | 144 | pdu_t *opdu; |
145 | int no_tm = 1; | | 145 | int no_tm = 1; |
146 | int rc = 1; | | 146 | int rc = 1; |
147 | uint32_t sn; | | 147 | uint32_t sn; |
148 | | | 148 | |
149 | if ((conn = assign_connection(sess, FALSE)) == NULL) { | | 149 | if ((conn = assign_connection(sess, FALSE)) == NULL) { |
150 | DEB(1, ("Reassign_tasks of Session %d, connection %d failed, " | | 150 | DEB(1, ("Reassign_tasks of Session %d, connection %d failed, " |
151 | "no active connection\n", | | 151 | "no active connection\n", |
152 | sess->id, oldconn->id)); | | 152 | sess->id, oldconn->id)); |
153 | /* XXX here we need to abort the waiting CCBs */ | | 153 | /* XXX here we need to abort the waiting CCBs */ |
154 | return; | | 154 | return; |
155 | } | | 155 | } |
156 | | | 156 | |
157 | if (sess->ErrorRecoveryLevel >= 2) { | | 157 | if (sess->ErrorRecoveryLevel >= 2) { |
158 | if (oldconn->loggedout == NOT_LOGGED_OUT) { | | 158 | if (oldconn->loggedout == NOT_LOGGED_OUT) { |
159 | oldconn->loggedout = LOGOUT_SENT; | | 159 | oldconn->loggedout = LOGOUT_SENT; |
160 | no_tm = send_logout(conn, oldconn, RECOVER_CONNECTION, TRUE); | | 160 | no_tm = send_logout(conn, oldconn, RECOVER_CONNECTION, TRUE); |
161 | oldconn->loggedout = (rc) ? LOGOUT_FAILED : LOGOUT_SUCCESS; | | 161 | oldconn->loggedout = (rc) ? LOGOUT_FAILED : LOGOUT_SUCCESS; |
162 | if (!oldconn->Time2Retain) { | | 162 | if (!oldconn->Time2Retain) { |
163 | DEBC(conn, 1, ("Time2Retain is zero, setting no_tm\n")); | | 163 | DEBC(conn, 1, ("Time2Retain is zero, setting no_tm\n")); |
164 | no_tm = 1; | | 164 | no_tm = 1; |
165 | } | | 165 | } |
166 | } else if (oldconn->loggedout == LOGOUT_SUCCESS) { | | 166 | } else if (oldconn->loggedout == LOGOUT_SUCCESS) { |
167 | no_tm = 0; | | 167 | no_tm = 0; |
168 | } | | 168 | } |
169 | if (!no_tm && oldconn->Time2Wait) { | | 169 | if (!no_tm && oldconn->Time2Wait) { |
170 | DEBC(conn, 1, ("Time2Wait=%d, hz=%d, waiting...\n", | | 170 | DEBC(conn, 1, ("Time2Wait=%d, hz=%d, waiting...\n", |
171 | oldconn->Time2Wait, hz)); | | 171 | oldconn->Time2Wait, hz)); |
172 | kpause("Time2Wait", false, oldconn->Time2Wait * hz, NULL); | | 172 | kpause("Time2Wait", false, oldconn->Time2Wait * hz, NULL); |
173 | } | | 173 | } |
174 | } | | 174 | } |
175 | | | 175 | |
176 | DEBC(conn, 1, ("Reassign_tasks: Session %d, conn %d -> conn %d, no_tm=%d\n", | | 176 | DEBC(conn, 1, ("Reassign_tasks: Session %d, conn %d -> conn %d, no_tm=%d\n", |
177 | sess->id, oldconn->id, conn->id, no_tm)); | | 177 | sess->id, oldconn->id, conn->id, no_tm)); |
178 | | | 178 | |
179 | | | 179 | |
180 | /* XXX reassign waiting CCBs to new connection */ | | 180 | /* XXX reassign waiting CCBs to new connection */ |
181 | | | 181 | |
182 | while ((ccb = TAILQ_FIRST(&oldconn->ccbs_waiting)) != NULL) { | | 182 | while ((ccb = TAILQ_FIRST(&oldconn->ccbs_waiting)) != NULL) { |
183 | /* Copy PDU contents (PDUs are bound to connection) */ | | 183 | /* Copy PDU contents (PDUs are bound to connection) */ |
184 | if ((pdu = get_pdu(conn, TRUE)) == NULL) { | | 184 | if ((pdu = get_pdu(conn, TRUE)) == NULL) { |
185 | break; | | 185 | break; |
186 | } | | 186 | } |
187 | | | 187 | |
188 | /* adjust CCB and clone PDU for new connection */ | | 188 | /* adjust CCB and clone PDU for new connection */ |
189 | TAILQ_REMOVE(&oldconn->ccbs_waiting, ccb, chain); | | 189 | TAILQ_REMOVE(&oldconn->ccbs_waiting, ccb, chain); |
190 | | | 190 | |
191 | opdu = ccb->pdu_waiting; | | 191 | opdu = ccb->pdu_waiting; |
192 | KASSERT((opdu->flags & PDUF_INQUEUE) == 0); | | 192 | KASSERT((opdu->flags & PDUF_INQUEUE) == 0); |
193 | | | 193 | |
194 | *pdu = *opdu; | | 194 | *pdu = *opdu; |
195 | | | 195 | |
196 | /* restore overwritten back ptr */ | | 196 | /* restore overwritten back ptr */ |
197 | pdu->connection = conn; | | 197 | pdu->connection = conn; |
198 | | | 198 | |
199 | /* fixup saved UIO and IOVEC (regular one will be overwritten anyway) */ | | 199 | /* fixup saved UIO and IOVEC (regular one will be overwritten anyway) */ |
200 | pdu->save_uio.uio_iov = pdu->io_vec; | | 200 | pdu->save_uio.uio_iov = pdu->io_vec; |
201 | pdu->save_iovec [0].iov_base = &pdu->pdu; | | 201 | pdu->save_iovec [0].iov_base = &pdu->pdu; |
202 | | | 202 | |
203 | if (conn->DataDigest && pdu->save_uio.uio_iovcnt > 1) { | | 203 | if (conn->DataDigest && pdu->save_uio.uio_iovcnt > 1) { |
204 | if (pdu->save_iovec [2].iov_base == NULL) { | | 204 | if (pdu->save_iovec [2].iov_base == NULL) { |
205 | pdu->save_iovec [2].iov_base = &pdu->data_digest; | | 205 | pdu->save_iovec [2].iov_base = &pdu->data_digest; |
206 | pdu->save_uio.uio_iovcnt = 3; | | 206 | pdu->save_uio.uio_iovcnt = 3; |
207 | } else { | | 207 | } else { |
208 | pdu->save_iovec [3].iov_base = &pdu->data_digest; | | 208 | pdu->save_iovec [3].iov_base = &pdu->data_digest; |
209 | pdu->save_uio.uio_iovcnt = 4; | | 209 | pdu->save_uio.uio_iovcnt = 4; |
210 | } | | 210 | } |
211 | } | | 211 | } |
212 | pdu->save_iovec [0].iov_len = | | 212 | pdu->save_iovec [0].iov_len = |
213 | (conn->HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE; | | 213 | (conn->HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE; |
214 | | | 214 | |
215 | /* link new PDU into old CCB */ | | 215 | /* link new PDU into old CCB */ |
216 | ccb->pdu_waiting = pdu; | | 216 | ccb->pdu_waiting = pdu; |
217 | /* link new CCB into new connection */ | | 217 | /* link new CCB into new connection */ |
218 | ccb->connection = conn; | | 218 | ccb->connection = conn; |
219 | /* reset timeouts */ | | 219 | /* reset timeouts */ |
220 | ccb->num_timeouts = 0; | | 220 | ccb->num_timeouts = 0; |
221 | | | 221 | |
222 | /* fixup reference counts */ | | 222 | /* fixup reference counts */ |
223 | oldconn->usecount--; | | 223 | oldconn->usecount--; |
224 | atomic_inc_uint(&conn->usecount); | | 224 | atomic_inc_uint(&conn->usecount); |
225 | | | 225 | |
226 | DEBC(conn, 1, ("CCB %p: Copied PDU %p to %p\n", | | 226 | DEBC(conn, 1, ("CCB %p: Copied PDU %p to %p\n", |
227 | ccb, opdu, pdu)); | | 227 | ccb, opdu, pdu)); |
228 | | | 228 | |
229 | /* kill temp pointer that is now referenced by the new PDU */ | | 229 | /* kill temp pointer that is now referenced by the new PDU */ |
230 | opdu->temp_data = NULL; | | 230 | opdu->temp_data = NULL; |
231 | | | 231 | |
232 | /* and free the old PDU */ | | 232 | /* and free the old PDU */ |
233 | free_pdu(opdu); | | 233 | free_pdu(opdu); |
234 | | | 234 | |
235 | /* put ready CCB into waiting list of new connection */ | | 235 | /* put ready CCB into waiting list of new connection */ |
236 | mutex_enter(&conn->lock); | | 236 | mutex_enter(&conn->lock); |
237 | suspend_ccb(ccb, TRUE); | | 237 | suspend_ccb(ccb, TRUE); |
238 | mutex_exit(&conn->lock); | | 238 | mutex_exit(&conn->lock); |
239 | } | | 239 | } |
240 | | | 240 | |
241 | if (pdu == NULL) { | | 241 | if (pdu == NULL) { |
242 | DEBC(conn, 1, ("Error while copying PDUs in reassign_tasks!\n")); | | 242 | DEBC(conn, 1, ("Error while copying PDUs in reassign_tasks!\n")); |
243 | /* give up recovering, the other connection is screwed up as well... */ | | 243 | /* give up recovering, the other connection is screwed up as well... */ |
244 | while ((ccb = TAILQ_FIRST(&oldconn->ccbs_waiting)) != NULL) { | | 244 | while ((ccb = TAILQ_FIRST(&oldconn->ccbs_waiting)) != NULL) { |
245 | wake_ccb(ccb, oldconn->terminating); | | 245 | wake_ccb(ccb, oldconn->terminating); |
246 | } | | 246 | } |
247 | /* XXX some CCBs might have been moved to new connection, but how is the | | 247 | /* XXX some CCBs might have been moved to new connection, but how is the |
248 | * new connection handled or killed ? */ | | 248 | * new connection handled or killed ? */ |
249 | return; | | 249 | return; |
250 | } | | 250 | } |
251 | | | 251 | |
252 | TAILQ_FOREACH(ccb, &conn->ccbs_waiting, chain) { | | 252 | TAILQ_FOREACH(ccb, &conn->ccbs_waiting, chain) { |
253 | if (!no_tm) { | | 253 | if (!no_tm) { |
254 | rc = send_task_management(conn, ccb, NULL, TASK_REASSIGN); | | 254 | rc = send_task_management(conn, ccb, NULL, TASK_REASSIGN); |
255 | } | | 255 | } |
256 | /* if we get an error on reassign, restart the original request */ | | 256 | /* if we get an error on reassign, restart the original request */ |
257 | if (no_tm || rc) { | | 257 | if (no_tm || rc) { |
258 | mutex_enter(&sess->lock); | | 258 | mutex_enter(&sess->lock); |
259 | if (ccb->CmdSN < sess->ExpCmdSN) { | | 259 | if (ccb->CmdSN < sess->ExpCmdSN) { |
260 | pdu = ccb->pdu_waiting; | | 260 | pdu = ccb->pdu_waiting; |
261 | sn = get_sernum(sess, pdu); | | 261 | sn = get_sernum(sess, pdu); |
262 | | | 262 | |
263 | /* update CmdSN */ | | 263 | /* update CmdSN */ |
264 | DEBC(conn, 1, ("Resend Updating CmdSN - old %d, new %d\n", | | 264 | DEBC(conn, 1, ("Resend Updating CmdSN - old %d, new %d\n", |
265 | ccb->CmdSN, sn)); | | 265 | ccb->CmdSN, sn)); |
266 | ccb->CmdSN = sn; | | 266 | ccb->CmdSN = sn; |
267 | pdu->pdu.p.command.CmdSN = htonl(ccb->CmdSN); | | 267 | pdu->pdu.p.command.CmdSN = htonl(ccb->CmdSN); |
268 | } | | 268 | } |
269 | mutex_exit(&sess->lock); | | 269 | mutex_exit(&sess->lock); |
270 | resend_pdu(ccb); | | 270 | resend_pdu(ccb); |
271 | } else { | | 271 | } else { |
272 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); | | 272 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); |
273 | } | | 273 | } |
274 | DEBC(conn, 1, ("Reassign ccb %p, no_tm=%d, rc=%d\n", | | 274 | DEBC(conn, 1, ("Reassign ccb %p, no_tm=%d, rc=%d\n", |
275 | ccb, no_tm, rc)); | | 275 | ccb, no_tm, rc)); |
276 | } | | 276 | } |
277 | } | | 277 | } |
278 | | | 278 | |
279 | | | 279 | |
280 | /* | | 280 | /* |
281 | * iscsi_send_thread: | | 281 | * iscsi_send_thread: |
282 | * This thread services the send queue, writing the PDUs to the socket. | | 282 | * This thread services the send queue, writing the PDUs to the socket. |
283 | * It also handles the cleanup when the connection is terminated. | | 283 | * It also handles the cleanup when the connection is terminated. |
284 | * | | 284 | * |
285 | * Parameter: | | 285 | * Parameter: |
286 | * par The connection this thread services | | 286 | * par The connection this thread services |
287 | */ | | 287 | */ |
288 | | | 288 | |
289 | void | | 289 | void |
290 | iscsi_send_thread(void *par) | | 290 | iscsi_send_thread(void *par) |
291 | { | | 291 | { |
292 | connection_t *conn = (connection_t *) par; | | 292 | connection_t *conn = (connection_t *) par; |
293 | session_t *sess; | | 293 | session_t *sess; |
294 | ccb_t *ccb, *nccb; | | 294 | ccb_t *ccb, *nccb; |
295 | pdu_t *pdu; | | 295 | pdu_t *pdu; |
296 | struct file *fp; | | 296 | struct file *fp; |
297 | pdu_disp_t pdisp; | | 297 | pdu_disp_t pdisp; |
298 | | | 298 | |
299 | sess = conn->session; | | 299 | sess = conn->session; |
300 | /* so cleanup thread knows there's someone left */ | | 300 | /* so cleanup thread knows there's someone left */ |
301 | iscsi_num_send_threads++; | | 301 | iscsi_num_send_threads++; |
302 | | | 302 | |
303 | do { | | 303 | do { |
304 | mutex_enter(&conn->lock); | | 304 | mutex_enter(&conn->lock); |
305 | while (!conn->terminating) { | | 305 | while (!conn->terminating) { |
306 | while (!conn->terminating && | | 306 | while (!conn->terminating && |
307 | (pdu = TAILQ_FIRST(&conn->pdus_to_send)) != NULL) { | | 307 | (pdu = TAILQ_FIRST(&conn->pdus_to_send)) != NULL) { |
308 | TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain); | | 308 | TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain); |
309 | pdu->flags &= ~PDUF_INQUEUE; | | 309 | pdu->flags &= ~PDUF_INQUEUE; |
310 | mutex_exit(&conn->lock); | | 310 | mutex_exit(&conn->lock); |
311 | | | 311 | |
312 | /* update ExpStatSN here to avoid discontinuities */ | | 312 | /* update ExpStatSN here to avoid discontinuities */ |
313 | /* and delays in updating target */ | | 313 | /* and delays in updating target */ |
314 | pdu->pdu.p.command.ExpStatSN = htonl(conn->StatSN_buf.ExpSN); | | 314 | pdu->pdu.p.command.ExpStatSN = htonl(conn->StatSN_buf.ExpSN); |
315 | | | 315 | |
316 | if (conn->HeaderDigest) | | 316 | if (conn->HeaderDigest) |
317 | pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE); | | 317 | pdu->pdu.HeaderDigest = gen_digest(&pdu->pdu, BHS_SIZE); |
318 | | | 318 | |
319 | DEBC(conn, 99, ("Transmitting PDU CmdSN = %u, ExpStatSN = %u\n", | | 319 | DEBC(conn, 99, ("Transmitting PDU CmdSN = %u, ExpStatSN = %u\n", |
320 | ntohl(pdu->pdu.p.command.CmdSN), | | 320 | ntohl(pdu->pdu.p.command.CmdSN), |
321 | ntohl(pdu->pdu.p.command.ExpStatSN))); | | 321 | ntohl(pdu->pdu.p.command.ExpStatSN))); |
322 | my_soo_write(conn, &pdu->uio); | | 322 | my_soo_write(conn, &pdu->uio); |
323 | | | 323 | |
324 | mutex_enter(&conn->lock); | | 324 | mutex_enter(&conn->lock); |
325 | pdisp = pdu->disp; | | 325 | pdisp = pdu->disp; |
326 | if (pdisp > PDUDISP_FREE) | | 326 | if (pdisp > PDUDISP_FREE) |
327 | pdu->flags &= ~PDUF_BUSY; | | 327 | pdu->flags &= ~PDUF_BUSY; |
328 | mutex_exit(&conn->lock); | | 328 | mutex_exit(&conn->lock); |
329 | if (pdisp <= PDUDISP_FREE) | | 329 | if (pdisp <= PDUDISP_FREE) |
330 | free_pdu(pdu); | | 330 | free_pdu(pdu); |
331 | | | 331 | |
332 | mutex_enter(&conn->lock); | | 332 | mutex_enter(&conn->lock); |
333 | } | | 333 | } |
334 | | | 334 | |
335 | if (!conn->terminating) | | 335 | if (!conn->terminating) |
336 | cv_wait(&conn->conn_cv, &conn->lock); | | 336 | cv_wait(&conn->conn_cv, &conn->lock); |
337 | } | | 337 | } |
338 | mutex_exit(&conn->lock); | | 338 | mutex_exit(&conn->lock); |
339 | | | 339 | |
340 | /* ------------------------------------------------------------------------ | | 340 | /* ------------------------------------------------------------------------ |
341 | * Here this thread takes over cleanup of the terminating connection. | | 341 | * Here this thread takes over cleanup of the terminating connection. |
342 | * ------------------------------------------------------------------------ | | 342 | * ------------------------------------------------------------------------ |
343 | */ | | 343 | */ |
344 | connection_timeout_stop(conn); | | 344 | connection_timeout_stop(conn); |
345 | conn->idle_timeout_val = CONNECTION_IDLE_TIMEOUT; | | 345 | conn->idle_timeout_val = CONNECTION_IDLE_TIMEOUT; |
346 | | | 346 | |
347 | fp = conn->sock; | | 347 | fp = conn->sock; |
348 | | | 348 | |
349 | /* | | 349 | /* |
350 | * We shutdown the socket here to force the receive | | 350 | * We shutdown the socket here to force the receive |
351 | * thread to wake up | | 351 | * thread to wake up |
352 | */ | | 352 | */ |
353 | DEBC(conn, 1, ("Closing Socket %p\n", conn->sock)); | | 353 | DEBC(conn, 1, ("Closing Socket %p\n", conn->sock)); |
354 | solock(fp->f_socket); | | 354 | solock(fp->f_socket); |
355 | soshutdown(fp->f_socket, SHUT_RDWR); | | 355 | soshutdown(fp->f_socket, SHUT_RDWR); |
356 | sounlock(fp->f_socket); | | 356 | sounlock(fp->f_socket); |
357 | | | 357 | |
358 | /* wake up any non-reassignable waiting CCBs */ | | 358 | /* wake up any non-reassignable waiting CCBs */ |
359 | TAILQ_FOREACH_SAFE(ccb, &conn->ccbs_waiting, chain, nccb) { | | 359 | TAILQ_FOREACH_SAFE(ccb, &conn->ccbs_waiting, chain, nccb) { |
360 | if (!(ccb->flags & CCBF_REASSIGN) || ccb->pdu_waiting == NULL) { | | 360 | if (!(ccb->flags & CCBF_REASSIGN) || ccb->pdu_waiting == NULL) { |
361 | DEBC(conn, 1, ("Terminating CCB %p (t=%p)\n", | | 361 | DEBC(conn, 1, ("Terminating CCB %p (t=%p)\n", |
362 | ccb,&ccb->timeout)); | | 362 | ccb,&ccb->timeout)); |
363 | wake_ccb(ccb, conn->terminating); | | 363 | wake_ccb(ccb, conn->terminating); |
364 | } else { | | 364 | } else { |
365 | ccb_timeout_stop(ccb); | | 365 | ccb_timeout_stop(ccb); |
366 | ccb->num_timeouts = 0; | | 366 | ccb->num_timeouts = 0; |
367 | } | | 367 | } |
368 | } | | 368 | } |
369 | | | 369 | |
370 | /* clean out anything left in send queue */ | | 370 | /* clean out anything left in send queue */ |
371 | mutex_enter(&conn->lock); | | 371 | mutex_enter(&conn->lock); |
372 | while ((pdu = TAILQ_FIRST(&conn->pdus_to_send)) != NULL) { | | 372 | while ((pdu = TAILQ_FIRST(&conn->pdus_to_send)) != NULL) { |
373 | TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain); | | 373 | TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain); |
374 | pdu->flags &= ~(PDUF_INQUEUE | PDUF_BUSY); | | 374 | pdu->flags &= ~(PDUF_INQUEUE | PDUF_BUSY); |
375 | mutex_exit(&conn->lock); | | 375 | mutex_exit(&conn->lock); |
376 | /* if it's not attached to a waiting CCB, free it */ | | 376 | /* if it's not attached to a waiting CCB, free it */ |
377 | if (pdu->owner == NULL || | | 377 | if (pdu->owner == NULL || |
378 | pdu->owner->pdu_waiting != pdu) { | | 378 | pdu->owner->pdu_waiting != pdu) { |
379 | free_pdu(pdu); | | 379 | free_pdu(pdu); |
380 | } | | 380 | } |
381 | mutex_enter(&conn->lock); | | 381 | mutex_enter(&conn->lock); |
382 | } | | 382 | } |
383 | mutex_exit(&conn->lock); | | 383 | mutex_exit(&conn->lock); |
384 | | | 384 | |
385 | /* If there's another connection available, transfer pending tasks */ | | 385 | /* If there's another connection available, transfer pending tasks */ |
386 | if (sess->active_connections && | | 386 | if (sess->active_connections && |
387 | TAILQ_FIRST(&conn->ccbs_waiting) != NULL) { | | 387 | TAILQ_FIRST(&conn->ccbs_waiting) != NULL) { |
388 | | | 388 | |
389 | reassign_tasks(conn); | | 389 | reassign_tasks(conn); |
390 | } else if (!conn->destroy && conn->Time2Wait) { | | 390 | } else if (!conn->destroy && conn->Time2Wait) { |
391 | DEBC(conn, 1, ("Time2Wait\n")); | | 391 | DEBC(conn, 1, ("Time2Wait\n")); |
392 | kpause("Time2Wait", false, conn->Time2Wait * hz, NULL); | | 392 | kpause("Time2Wait", false, conn->Time2Wait * hz, NULL); |
393 | DEBC(conn, 1, ("Time2Wait\n")); | | 393 | DEBC(conn, 1, ("Time2Wait\n")); |
394 | } | | 394 | } |
395 | /* notify event handlers of connection shutdown */ | | 395 | /* notify event handlers of connection shutdown */ |
396 | DEBC(conn, 1, ("%s\n", (conn->destroy) ? "TERMINATED" : "RECOVER")); | | 396 | DEBC(conn, 1, ("%s\n", (conn->destroy) ? "TERMINATED" : "RECOVER")); |
397 | add_event((conn->destroy) ? ISCSI_CONNECTION_TERMINATED | | 397 | add_event((conn->destroy) ? ISCSI_CONNECTION_TERMINATED |
398 | : ISCSI_RECOVER_CONNECTION, | | 398 | : ISCSI_RECOVER_CONNECTION, |
399 | sess->id, conn->id, conn->terminating); | | 399 | sess->id, conn->id, conn->terminating); |
400 | | | 400 | |
401 | DEBC(conn, 1, ("Waiting for conn_idle\n")); | | 401 | DEBC(conn, 1, ("Waiting for conn_idle\n")); |
402 | mutex_enter(&conn->lock); | | 402 | mutex_enter(&conn->lock); |
403 | if (!conn->destroy) | | 403 | if (!conn->destroy) |
404 | cv_timedwait(&conn->idle_cv, &conn->lock, CONNECTION_IDLE_TIMEOUT); | | 404 | cv_timedwait(&conn->idle_cv, &conn->lock, CONNECTION_IDLE_TIMEOUT); |
405 | mutex_exit(&conn->lock); | | 405 | mutex_exit(&conn->lock); |
406 | DEBC(conn, 1, ("Waited for conn_idle, destroy = %d\n", conn->destroy)); | | 406 | DEBC(conn, 1, ("Waited for conn_idle, destroy = %d\n", conn->destroy)); |
407 | | | 407 | |
408 | } while (!conn->destroy); | | 408 | } while (!conn->destroy); |
409 | | | 409 | |
410 | /* wake up anyone waiting for a PDU */ | | 410 | /* wake up anyone waiting for a PDU */ |
411 | mutex_enter(&conn->lock); | | 411 | mutex_enter(&conn->lock); |
412 | cv_broadcast(&conn->conn_cv); | | 412 | cv_broadcast(&conn->conn_cv); |
413 | mutex_exit(&conn->lock); | | 413 | mutex_exit(&conn->lock); |
414 | | | 414 | |
415 | /* wake up any waiting CCBs */ | | 415 | /* wake up any waiting CCBs */ |
416 | while ((ccb = TAILQ_FIRST(&conn->ccbs_waiting)) != NULL) { | | 416 | while ((ccb = TAILQ_FIRST(&conn->ccbs_waiting)) != NULL) { |
417 | KASSERT(ccb->disp >= CCBDISP_NOWAIT); | | 417 | KASSERT(ccb->disp >= CCBDISP_NOWAIT); |
418 | wake_ccb(ccb, conn->terminating); | | 418 | wake_ccb(ccb, conn->terminating); |
419 | /* NOTE: wake_ccb will remove the CCB from the queue */ | | 419 | /* NOTE: wake_ccb will remove the CCB from the queue */ |
420 | } | | 420 | } |
421 | | | 421 | |
422 | add_connection_cleanup(conn); | | 422 | add_connection_cleanup(conn); |
423 | | | 423 | |
424 | conn->sendproc = NULL; | | 424 | conn->sendproc = NULL; |
425 | DEBC(conn, 1, ("Send thread exits\n")); | | 425 | DEBC(conn, 1, ("Send thread exits\n")); |
426 | iscsi_num_send_threads--; | | 426 | iscsi_num_send_threads--; |
427 | kthread_exit(0); | | 427 | kthread_exit(0); |
428 | } | | 428 | } |
429 | | | 429 | |
430 | | | 430 | |
431 | /* | | 431 | /* |
432 | * send_pdu: | | 432 | * send_pdu: |
433 | * Enqueue a PDU to be sent, and handle its disposition as well as | | 433 | * Enqueue a PDU to be sent, and handle its disposition as well as |
434 | * the disposition of its associated CCB. | | 434 | * the disposition of its associated CCB. |
435 | * | | 435 | * |
436 | * Parameter: | | 436 | * Parameter: |
437 | * ccb The associated CCB. May be NULL if cdisp is CCBDISP_NOWAIT | | 437 | * ccb The associated CCB. May be NULL if cdisp is CCBDISP_NOWAIT |
438 | * and pdisp is not PDUDISP_WAIT | | 438 | * and pdisp is not PDUDISP_WAIT |
439 | * cdisp The CCB's disposition | | 439 | * cdisp The CCB's disposition |
440 | * pdu The PDU | | 440 | * pdu The PDU |
441 | * pdisp The PDU's disposition | | 441 | * pdisp The PDU's disposition |
442 | */ | | 442 | */ |
443 | | | 443 | |
444 | STATIC void | | 444 | STATIC void |
445 | send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_disp_t cdisp, pdu_disp_t pdisp) | | 445 | send_pdu(ccb_t *ccb, pdu_t *pdu, ccb_disp_t cdisp, pdu_disp_t pdisp) |
446 | { | | 446 | { |
447 | connection_t *conn = pdu->connection; | | 447 | connection_t *conn = pdu->connection; |
448 | ccb_disp_t prev_cdisp = 0; | | 448 | ccb_disp_t prev_cdisp = 0; |
449 | | | 449 | |
450 | if (ccb != NULL) { | | 450 | if (ccb != NULL) { |
451 | prev_cdisp = ccb->disp; | | 451 | prev_cdisp = ccb->disp; |
452 | pdu->pdu.InitiatorTaskTag = ccb->ITT; | | 452 | pdu->pdu.InitiatorTaskTag = ccb->ITT; |
453 | pdu->owner = ccb; | | 453 | pdu->owner = ccb; |
454 | if (cdisp != CCBDISP_NOWAIT) | | 454 | if (cdisp != CCBDISP_NOWAIT) |
455 | ccb->disp = cdisp; | | 455 | ccb->disp = cdisp; |
456 | } | | 456 | } |
457 | | | 457 | |
458 | pdu->disp = pdisp; | | 458 | pdu->disp = pdisp; |
459 | | | 459 | |
460 | DEBC(conn, 10, ("Send_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n", | | 460 | DEBC(conn, 10, ("Send_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n", |
461 | ntohl(pdu->pdu.p.command.CmdSN), | | 461 | ntohl(pdu->pdu.p.command.CmdSN), |
462 | conn->StatSN_buf.ExpSN, | | 462 | conn->StatSN_buf.ExpSN, |
463 | ccb, pdu)); | | 463 | ccb, pdu)); |
464 | | | 464 | |
465 | mutex_enter(&conn->lock); | | 465 | mutex_enter(&conn->lock); |
466 | if (pdisp == PDUDISP_WAIT) { | | 466 | if (pdisp == PDUDISP_WAIT) { |
467 | KASSERT(ccb != NULL); | | 467 | KASSERT(ccb != NULL); |
468 | | | 468 | |
469 | ccb->pdu_waiting = pdu; | | 469 | ccb->pdu_waiting = pdu; |
470 | | | 470 | |
471 | /* save UIO and IOVEC for retransmit */ | | 471 | /* save UIO and IOVEC for retransmit */ |
472 | pdu->save_uio = pdu->uio; | | 472 | pdu->save_uio = pdu->uio; |
473 | memcpy(pdu->save_iovec, pdu->io_vec, sizeof(pdu->save_iovec)); | | 473 | memcpy(pdu->save_iovec, pdu->io_vec, sizeof(pdu->save_iovec)); |
474 | | | 474 | |
475 | pdu->flags |= PDUF_BUSY; | | 475 | pdu->flags |= PDUF_BUSY; |
476 | } | | 476 | } |
477 | /* Enqueue for sending */ | | 477 | /* Enqueue for sending */ |
478 | pdu->flags |= PDUF_INQUEUE; | | 478 | pdu->flags |= PDUF_INQUEUE; |
479 | | | 479 | |
480 | if (pdu->flags & PDUF_PRIORITY) | | 480 | if (pdu->flags & PDUF_PRIORITY) |
481 | TAILQ_INSERT_HEAD(&conn->pdus_to_send, pdu, send_chain); | | 481 | TAILQ_INSERT_HEAD(&conn->pdus_to_send, pdu, send_chain); |
482 | else | | 482 | else |
483 | TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain); | | 483 | TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain); |
484 | | | 484 | |
485 | cv_broadcast(&conn->conn_cv); | | 485 | cv_broadcast(&conn->conn_cv); |
486 | | | 486 | |
487 | if (cdisp != CCBDISP_NOWAIT) { | | 487 | if (cdisp != CCBDISP_NOWAIT) { |
488 | KASSERT(ccb != NULL); | | 488 | KASSERT(ccb != NULL); |
489 | KASSERTMSG(ccb->connection == conn, "conn mismatch %p != %p\n", ccb->connection, conn); | | 489 | KASSERTMSG(ccb->connection == conn, "conn mismatch %p != %p\n", ccb->connection, conn); |
490 | | | 490 | |
491 | if (prev_cdisp <= CCBDISP_NOWAIT) | | 491 | if (prev_cdisp <= CCBDISP_NOWAIT) |
492 | suspend_ccb(ccb, TRUE); | | 492 | suspend_ccb(ccb, TRUE); |
493 | | | 493 | |
494 | mutex_exit(&conn->lock); | | 494 | mutex_exit(&conn->lock); |
495 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); | | 495 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); |
496 | mutex_enter(&conn->lock); | | 496 | mutex_enter(&conn->lock); |
497 | | | 497 | |
498 | while (ccb->disp == CCBDISP_WAIT) { | | 498 | while (ccb->disp == CCBDISP_WAIT) { |
499 | DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d waiting\n", | | 499 | DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d waiting\n", |
500 | ccb, ccb->disp)); | | 500 | ccb, ccb->disp)); |
501 | cv_wait(&conn->ccb_cv, &conn->lock); | | 501 | cv_wait(&conn->ccb_cv, &conn->lock); |
502 | DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d returned\n", | | 502 | DEBC(conn, 15, ("Send_pdu: ccb=%p cdisp=%d returned\n", |
503 | ccb, ccb->disp)); | | 503 | ccb, ccb->disp)); |
504 | } | | 504 | } |
505 | } | | 505 | } |
506 | | | 506 | |
507 | mutex_exit(&conn->lock); | | 507 | mutex_exit(&conn->lock); |
508 | } | | 508 | } |
509 | | | 509 | |
510 | | | 510 | |
511 | /* | | 511 | /* |
512 | * resend_pdu: | | 512 | * resend_pdu: |
513 | * Re-Enqueue a PDU that has apparently gotten lost. | | 513 | * Re-Enqueue a PDU that has apparently gotten lost. |
514 | * | | 514 | * |
515 | * Parameter: | | 515 | * Parameter: |
516 | * ccb The associated CCB. | | 516 | * ccb The associated CCB. |
517 | */ | | 517 | */ |
518 | | | 518 | |
519 | void | | 519 | void |
520 | resend_pdu(ccb_t *ccb) | | 520 | resend_pdu(ccb_t *ccb) |
521 | { | | 521 | { |
522 | connection_t *conn = ccb->connection; | | 522 | connection_t *conn = ccb->connection; |
523 | pdu_t *pdu = ccb->pdu_waiting; | | 523 | pdu_t *pdu = ccb->pdu_waiting; |
524 | | | 524 | |
525 | mutex_enter(&conn->lock); | | 525 | mutex_enter(&conn->lock); |
526 | if (pdu == NULL || (pdu->flags & PDUF_BUSY)) { | | 526 | if (pdu == NULL || (pdu->flags & PDUF_BUSY)) { |
527 | mutex_exit(&conn->lock); | | 527 | mutex_exit(&conn->lock); |
528 | return; | | 528 | return; |
529 | } | | 529 | } |
530 | pdu->flags |= PDUF_BUSY; | | 530 | pdu->flags |= PDUF_BUSY; |
531 | mutex_exit(&conn->lock); | | 531 | mutex_exit(&conn->lock); |
532 | | | 532 | |
533 | /* restore UIO and IOVEC */ | | 533 | /* restore UIO and IOVEC */ |
534 | pdu->uio = pdu->save_uio; | | 534 | pdu->uio = pdu->save_uio; |
535 | memcpy(pdu->io_vec, pdu->save_iovec, sizeof(pdu->io_vec)); | | 535 | memcpy(pdu->io_vec, pdu->save_iovec, sizeof(pdu->io_vec)); |
536 | | | 536 | |
537 | DEBC(conn, 8, ("ReSend_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n", | | 537 | DEBC(conn, 8, ("ReSend_pdu: CmdSN=%u ExpStatSN~%u ccb=%p, pdu=%p\n", |
538 | ntohl(pdu->pdu.p.command.CmdSN), | | 538 | ntohl(pdu->pdu.p.command.CmdSN), |
539 | conn->StatSN_buf.ExpSN, | | 539 | conn->StatSN_buf.ExpSN, |
540 | ccb, pdu)); | | 540 | ccb, pdu)); |
541 | | | 541 | |
542 | mutex_enter(&conn->lock); | | 542 | mutex_enter(&conn->lock); |
543 | /* Enqueue for sending */ | | 543 | /* Enqueue for sending */ |
544 | pdu->flags |= PDUF_INQUEUE; | | 544 | pdu->flags |= PDUF_INQUEUE; |
545 | | | 545 | |
546 | if (pdu->flags & PDUF_PRIORITY) { | | 546 | if (pdu->flags & PDUF_PRIORITY) { |
547 | TAILQ_INSERT_HEAD(&conn->pdus_to_send, pdu, send_chain); | | 547 | TAILQ_INSERT_HEAD(&conn->pdus_to_send, pdu, send_chain); |
548 | } else { | | 548 | } else { |
549 | TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain); | | 549 | TAILQ_INSERT_TAIL(&conn->pdus_to_send, pdu, send_chain); |
550 | } | | 550 | } |
551 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); | | 551 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); |
552 | cv_broadcast(&conn->conn_cv); | | 552 | cv_broadcast(&conn->conn_cv); |
553 | mutex_exit(&conn->lock); | | 553 | mutex_exit(&conn->lock); |
554 | } | | 554 | } |
555 | | | 555 | |
556 | | | 556 | |
557 | /* | | 557 | /* |
558 | * setup_tx_uio: | | 558 | * setup_tx_uio: |
559 | * Initialize the uio structure for sending, including header, | | 559 | * Initialize the uio structure for sending, including header, |
560 | * data (if present), padding, and Data Digest. | | 560 | * data (if present), padding, and Data Digest. |
561 | * Header Digest is generated in send thread. | | 561 | * Header Digest is generated in send thread. |
562 | * | | 562 | * |
563 | * Parameter: | | 563 | * Parameter: |
564 | * pdu The PDU | | 564 | * pdu The PDU |
565 | * dsl The Data Segment Length | | 565 | * dsl The Data Segment Length |
566 | * data The data pointer | | 566 | * data The data pointer |
567 | * read TRUE if this is a read operation | | 567 | * read TRUE if this is a read operation |
568 | */ | | 568 | */ |
569 | | | 569 | |
570 | STATIC void | | 570 | STATIC void |
571 | setup_tx_uio(pdu_t *pdu, uint32_t dsl, void *data, bool read) | | 571 | setup_tx_uio(pdu_t *pdu, uint32_t dsl, void *data, bool read) |
572 | { | | 572 | { |
573 | static uint8_t pad_bytes[4] = { 0 }; | | 573 | static uint8_t pad_bytes[4] = { 0 }; |
574 | struct uio *uio; | | 574 | struct uio *uio; |
575 | int i, pad, hlen; | | 575 | int i, pad, hlen; |
576 | connection_t *conn = pdu->connection; | | 576 | connection_t *conn = pdu->connection; |
577 | | | 577 | |
578 | DEB(99, ("SetupTxUio: dlen = %d, dptr: %p, read: %d\n", | | 578 | DEB(99, ("SetupTxUio: dlen = %d, dptr: %p, read: %d\n", |
579 | dsl, data, read)); | | 579 | dsl, data, read)); |
580 | | | 580 | |
581 | if (!read && dsl) { | | 581 | if (!read && dsl) { |
582 | hton3(dsl, pdu->pdu.DataSegmentLength); | | 582 | hton3(dsl, pdu->pdu.DataSegmentLength); |
583 | } | | 583 | } |
584 | hlen = (conn->HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE; | | 584 | hlen = (conn->HeaderDigest) ? BHS_SIZE + 4 : BHS_SIZE; |
585 | | | 585 | |
586 | pdu->io_vec[0].iov_base = &pdu->pdu; | | 586 | pdu->io_vec[0].iov_base = &pdu->pdu; |
587 | pdu->io_vec[0].iov_len = hlen; | | 587 | pdu->io_vec[0].iov_len = hlen; |
588 | | | 588 | |
589 | uio = &pdu->uio; | | 589 | uio = &pdu->uio; |
590 | | | 590 | |
591 | uio->uio_iov = pdu->io_vec; | | 591 | uio->uio_iov = pdu->io_vec; |
592 | uio->uio_iovcnt = 1; | | 592 | uio->uio_iovcnt = 1; |
593 | uio->uio_rw = UIO_WRITE; | | 593 | uio->uio_rw = UIO_WRITE; |
594 | uio->uio_resid = hlen; | | 594 | uio->uio_resid = hlen; |
595 | UIO_SETUP_SYSSPACE(uio); | | 595 | UIO_SETUP_SYSSPACE(uio); |
596 | | | 596 | |
597 | if (!read && dsl) { | | 597 | if (!read && dsl) { |
598 | uio->uio_iovcnt++; | | 598 | uio->uio_iovcnt++; |
599 | pdu->io_vec[1].iov_base = data; | | 599 | pdu->io_vec[1].iov_base = data; |
600 | pdu->io_vec[1].iov_len = dsl; | | 600 | pdu->io_vec[1].iov_len = dsl; |
601 | uio->uio_resid += dsl; | | 601 | uio->uio_resid += dsl; |
602 | | | 602 | |
603 | /* Pad to next multiple of 4 */ | | 603 | /* Pad to next multiple of 4 */ |
604 | pad = uio->uio_resid & 0x03; | | 604 | pad = uio->uio_resid & 0x03; |
605 | if (pad) { | | 605 | if (pad) { |
606 | i = uio->uio_iovcnt++; | | 606 | i = uio->uio_iovcnt++; |
607 | pad = 4 - pad; | | 607 | pad = 4 - pad; |
608 | pdu->io_vec[i].iov_base = pad_bytes; | | 608 | pdu->io_vec[i].iov_base = pad_bytes; |
609 | pdu->io_vec[i].iov_len = pad; | | 609 | pdu->io_vec[i].iov_len = pad; |
610 | uio->uio_resid += pad; | | 610 | uio->uio_resid += pad; |
611 | } | | 611 | } |
612 | | | 612 | |
613 | if (conn->DataDigest) { | | 613 | if (conn->DataDigest) { |
614 | pdu->data_digest = gen_digest_2(data, dsl, pad_bytes, pad); | | 614 | pdu->data_digest = gen_digest_2(data, dsl, pad_bytes, pad); |
615 | i = uio->uio_iovcnt++; | | 615 | i = uio->uio_iovcnt++; |
616 | pdu->io_vec[i].iov_base = &pdu->data_digest; | | 616 | pdu->io_vec[i].iov_base = &pdu->data_digest; |
617 | pdu->io_vec[i].iov_len = 4; | | 617 | pdu->io_vec[i].iov_len = 4; |
618 | uio->uio_resid += 4; | | 618 | uio->uio_resid += 4; |
619 | } | | 619 | } |
620 | } | | 620 | } |
621 | } | | 621 | } |
622 | | | 622 | |
623 | /* | | 623 | /* |
624 | * init_login_pdu: | | 624 | * init_login_pdu: |
625 | * Initialize the login PDU. | | 625 | * Initialize the login PDU. |
626 | * | | 626 | * |
627 | * Parameter: | | 627 | * Parameter: |
628 | * conn The connection | | 628 | * conn The connection |
629 | * ccb The CCB | | 629 | * ccb The CCB |
630 | * pdu The PDU | | 630 | * pdu The PDU |
631 | */ | | 631 | */ |
632 | | | 632 | |
633 | STATIC void | | 633 | STATIC void |
634 | init_login_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, bool next) | | 634 | init_login_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, bool next) |
635 | { | | 635 | { |
636 | pdu_header_t *pdu = &ppdu->pdu; | | 636 | pdu_header_t *pdu = &ppdu->pdu; |
637 | login_isid_t *isid = (login_isid_t *) & pdu->LUN; | | 637 | login_isid_t *isid = (login_isid_t *) & pdu->LUN; |
638 | uint8_t c_phase; | | 638 | uint8_t c_phase; |
639 | | | 639 | |
640 | pdu->Opcode = IOP_Login_Request | OP_IMMEDIATE; | | 640 | pdu->Opcode = IOP_Login_Request | OP_IMMEDIATE; |
641 | | | 641 | |
642 | mutex_enter(&conn->session->lock); | | 642 | mutex_enter(&conn->session->lock); |
643 | ccb->CmdSN = get_sernum(conn->session, ppdu); | | 643 | ccb->CmdSN = get_sernum(conn->session, ppdu); |
644 | mutex_exit(&conn->session->lock); | | 644 | mutex_exit(&conn->session->lock); |
645 | | | 645 | |
646 | if (next) { | | 646 | if (next) { |
647 | c_phase = (pdu->Flags >> CSG_SHIFT) & SG_MASK; | | 647 | c_phase = (pdu->Flags >> CSG_SHIFT) & SG_MASK; |
648 | pdu->Flags = FLAG_TRANSIT | (c_phase << CSG_SHIFT) | | | 648 | pdu->Flags = FLAG_TRANSIT | (c_phase << CSG_SHIFT) | |
649 | NEXT_PHASE(c_phase); | | 649 | NEXT_PHASE(c_phase); |
650 | } | | 650 | } |
651 | | | 651 | |
652 | memcpy(isid, &iscsi_InitiatorISID, 6); | | 652 | memcpy(isid, &iscsi_InitiatorISID, 6); |
653 | isid->TSIH = conn->session->TSIH; | | 653 | isid->TSIH = conn->session->TSIH; |
654 | | | 654 | |
655 | pdu->p.login_req.CID = htons(conn->id); | | 655 | pdu->p.login_req.CID = htons(conn->id); |
656 | pdu->p.login_req.CmdSN = htonl(ccb->CmdSN); | | 656 | pdu->p.login_req.CmdSN = htonl(ccb->CmdSN); |
657 | } | | 657 | } |
658 | | | 658 | |
659 | | | 659 | |
660 | /* | | 660 | /* |
661 | * negotiate_login: | | 661 | * negotiate_login: |
662 | * Control login negotiation. | | 662 | * Control login negotiation. |
663 | * | | 663 | * |
664 | * Parameter: | | 664 | * Parameter: |
665 | * conn The connection | | 665 | * conn The connection |
666 | * rx_pdu The received login response PDU | | 666 | * rx_pdu The received login response PDU |
667 | * tx_ccb The originally sent login CCB | | 667 | * tx_ccb The originally sent login CCB |
668 | */ | | 668 | */ |
669 | | | 669 | |
670 | void | | 670 | void |
671 | negotiate_login(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb) | | 671 | negotiate_login(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb) |
672 | { | | 672 | { |
673 | int rc; | | 673 | int rc; |
674 | bool next = TRUE; | | 674 | bool next = TRUE; |
675 | pdu_t *tx_pdu; | | 675 | pdu_t *tx_pdu; |
676 | uint8_t c_phase; | | 676 | uint8_t c_phase; |
677 | | | 677 | |
678 | if (rx_pdu->pdu.Flags & FLAG_TRANSIT) | | 678 | if (rx_pdu->pdu.Flags & FLAG_TRANSIT) |
679 | c_phase = rx_pdu->pdu.Flags & SG_MASK; | | 679 | c_phase = rx_pdu->pdu.Flags & SG_MASK; |
680 | else | | 680 | else |
681 | c_phase = (rx_pdu->pdu.Flags >> CSG_SHIFT) & SG_MASK; | | 681 | c_phase = (rx_pdu->pdu.Flags >> CSG_SHIFT) & SG_MASK; |
682 | | | 682 | |
683 | DEB(99, ("NegotiateLogin: Flags=%x Phase=%x\n", | | 683 | DEB(99, ("NegotiateLogin: Flags=%x Phase=%x\n", |
684 | rx_pdu->pdu.Flags, c_phase)); | | 684 | rx_pdu->pdu.Flags, c_phase)); |
685 | | | 685 | |
686 | if (c_phase == SG_FULL_FEATURE_PHASE) { | | 686 | if (c_phase == SG_FULL_FEATURE_PHASE) { |
687 | session_t *sess = conn->session; | | 687 | session_t *sess = conn->session; |
688 | | | 688 | |
689 | if (!sess->TSIH) | | 689 | if (!sess->TSIH) |
690 | sess->TSIH = ((login_isid_t *) &rx_pdu->pdu.LUN)->TSIH; | | 690 | sess->TSIH = ((login_isid_t *) &rx_pdu->pdu.LUN)->TSIH; |
691 | | | 691 | |
692 | if (rx_pdu->temp_data != NULL) | | 692 | if (rx_pdu->temp_data != NULL) |
693 | assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, NULL); | | 693 | assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, NULL); |
694 | | | 694 | |
695 | /* negotiated values are now valid */ | | 695 | /* negotiated values are now valid */ |
696 | set_negotiated_parameters(tx_ccb); | | 696 | set_negotiated_parameters(tx_ccb); |
697 | | | 697 | |
698 | DEBC(conn, 5, ("Login Successful!\n")); | | 698 | DEBC(conn, 5, ("Login Successful!\n")); |
699 | wake_ccb(tx_ccb, ISCSI_STATUS_SUCCESS); | | 699 | wake_ccb(tx_ccb, ISCSI_STATUS_SUCCESS); |
700 | return; | | 700 | return; |
701 | } | | 701 | } |
702 | | | 702 | |
703 | tx_pdu = get_pdu(conn, TRUE); | | 703 | tx_pdu = get_pdu(conn, TRUE); |
704 | if (tx_pdu == NULL) | | 704 | if (tx_pdu == NULL) |
705 | return; | | 705 | return; |
706 | | | 706 | |
707 | tx_pdu->pdu.Flags = c_phase << CSG_SHIFT; | | 707 | tx_pdu->pdu.Flags = c_phase << CSG_SHIFT; |
708 | | | 708 | |
709 | switch (c_phase) { | | 709 | switch (c_phase) { |
710 | case SG_SECURITY_NEGOTIATION: | | 710 | case SG_SECURITY_NEGOTIATION: |
711 | rc = assemble_security_parameters(conn, tx_ccb, rx_pdu, tx_pdu); | | 711 | rc = assemble_security_parameters(conn, tx_ccb, rx_pdu, tx_pdu); |
712 | if (rc < 0) | | 712 | if (rc < 0) |
713 | next = FALSE; | | 713 | next = FALSE; |
714 | break; | | 714 | break; |
715 | | | 715 | |
716 | case SG_LOGIN_OPERATIONAL_NEGOTIATION: | | 716 | case SG_LOGIN_OPERATIONAL_NEGOTIATION: |
717 | rc = assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, tx_pdu); | | 717 | rc = assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, tx_pdu); |
718 | break; | | 718 | break; |
719 | | | 719 | |
720 | default: | | 720 | default: |
721 | DEBOUT(("Invalid phase %x in negotiate_login\n", c_phase)); | | 721 | DEBOUT(("Invalid phase %x in negotiate_login\n", c_phase)); |
722 | rc = ISCSI_STATUS_TARGET_ERROR; | | 722 | rc = ISCSI_STATUS_TARGET_ERROR; |
723 | break; | | 723 | break; |
724 | } | | 724 | } |
725 | | | 725 | |
726 | if (rc > 0) { | | 726 | if (rc > 0) { |
727 | wake_ccb(tx_ccb, rc); | | 727 | wake_ccb(tx_ccb, rc); |
728 | free_pdu(tx_pdu); | | 728 | free_pdu(tx_pdu); |
729 | } else { | | 729 | } else { |
730 | init_login_pdu(conn, tx_ccb, tx_pdu, next); | | 730 | init_login_pdu(conn, tx_ccb, tx_pdu, next); |
731 | setup_tx_uio(tx_pdu, tx_pdu->temp_data_len, tx_pdu->temp_data, FALSE); | | 731 | setup_tx_uio(tx_pdu, tx_pdu->temp_data_len, tx_pdu->temp_data, FALSE); |
732 | send_pdu(tx_ccb, tx_pdu, CCBDISP_NOWAIT, PDUDISP_FREE); | | 732 | send_pdu(tx_ccb, tx_pdu, CCBDISP_NOWAIT, PDUDISP_FREE); |
733 | } | | 733 | } |
734 | } | | 734 | } |
735 | | | 735 | |
736 | | | 736 | |
737 | /* | | 737 | /* |
738 | * init_text_pdu: | | 738 | * init_text_pdu: |
739 | * Initialize the text PDU. | | 739 | * Initialize the text PDU. |
740 | * | | 740 | * |
741 | * Parameter: | | 741 | * Parameter: |
742 | * conn The connection | | 742 | * conn The connection |
743 | * ccb The transmit CCB | | 743 | * ccb The transmit CCB |
744 | * ppdu The transmit PDU | | 744 | * ppdu The transmit PDU |
745 | * rx_pdu The received PDU if this is an unsolicited negotiation | | 745 | * rx_pdu The received PDU if this is an unsolicited negotiation |
746 | */ | | 746 | */ |
747 | | | 747 | |
748 | STATIC void | | 748 | STATIC void |
749 | init_text_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, pdu_t *rx_pdu) | | 749 | init_text_pdu(connection_t *conn, ccb_t *ccb, pdu_t *ppdu, pdu_t *rx_pdu) |
750 | { | | 750 | { |
751 | pdu_header_t *pdu = &ppdu->pdu; | | 751 | pdu_header_t *pdu = &ppdu->pdu; |
752 | | | 752 | |
753 | pdu->Opcode = IOP_Text_Request | OP_IMMEDIATE; | | 753 | pdu->Opcode = IOP_Text_Request | OP_IMMEDIATE; |
754 | pdu->Flags = FLAG_FINAL; | | 754 | pdu->Flags = FLAG_FINAL; |
755 | | | 755 | |
756 | mutex_enter(&conn->session->lock); | | 756 | mutex_enter(&conn->session->lock); |
757 | ccb->CmdSN = get_sernum(conn->session, ppdu); | | 757 | ccb->CmdSN = get_sernum(conn->session, ppdu); |
758 | mutex_exit(&conn->session->lock); | | 758 | mutex_exit(&conn->session->lock); |
759 | | | 759 | |
760 | if (rx_pdu != NULL) { | | 760 | if (rx_pdu != NULL) { |
761 | pdu->p.text_req.TargetTransferTag = | | 761 | pdu->p.text_req.TargetTransferTag = |
762 | rx_pdu->pdu.p.text_rsp.TargetTransferTag; | | 762 | rx_pdu->pdu.p.text_rsp.TargetTransferTag; |
763 | pdu->LUN = rx_pdu->pdu.LUN; | | 763 | pdu->LUN = rx_pdu->pdu.LUN; |
764 | } else | | 764 | } else |
765 | pdu->p.text_req.TargetTransferTag = 0xffffffff; | | 765 | pdu->p.text_req.TargetTransferTag = 0xffffffff; |
766 | | | 766 | |
767 | pdu->p.text_req.CmdSN = htonl(ccb->CmdSN); | | 767 | pdu->p.text_req.CmdSN = htonl(ccb->CmdSN); |
768 | } | | 768 | } |
769 | | | 769 | |
770 | | | 770 | |
771 | /* | | 771 | /* |
772 | * acknowledge_text: | | 772 | * acknowledge_text: |
773 | * Acknowledge a continued login or text response. | | 773 | * Acknowledge a continued login or text response. |
774 | * | | 774 | * |
775 | * Parameter: | | 775 | * Parameter: |
776 | * conn The connection | | 776 | * conn The connection |
777 | * rx_pdu The received login/text response PDU | | 777 | * rx_pdu The received login/text response PDU |
778 | * tx_ccb The originally sent login/text request CCB | | 778 | * tx_ccb The originally sent login/text request CCB |
779 | */ | | 779 | */ |
780 | | | 780 | |
781 | void | | 781 | void |
782 | acknowledge_text(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb) | | 782 | acknowledge_text(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb) |
783 | { | | 783 | { |
784 | pdu_t *tx_pdu; | | 784 | pdu_t *tx_pdu; |
785 | | | 785 | |
786 | tx_pdu = get_pdu(conn, TRUE); | | 786 | tx_pdu = get_pdu(conn, TRUE); |
787 | if (tx_pdu == NULL) | | 787 | if (tx_pdu == NULL) |
788 | return; | | 788 | return; |
789 | | | 789 | |
790 | if (rx_pdu != NULL && | | 790 | if (rx_pdu != NULL && |
791 | (rx_pdu->pdu.Opcode & OPCODE_MASK) == IOP_Login_Request) | | 791 | (rx_pdu->pdu.Opcode & OPCODE_MASK) == IOP_Login_Request) |
792 | init_login_pdu(conn, tx_ccb, tx_pdu, FALSE); | | 792 | init_login_pdu(conn, tx_ccb, tx_pdu, FALSE); |
793 | else | | 793 | else |
794 | init_text_pdu(conn, tx_ccb, tx_pdu, rx_pdu); | | 794 | init_text_pdu(conn, tx_ccb, tx_pdu, rx_pdu); |
795 | | | 795 | |
796 | setup_tx_uio(tx_pdu, 0, NULL, FALSE); | | 796 | setup_tx_uio(tx_pdu, 0, NULL, FALSE); |
797 | send_pdu(tx_ccb, tx_pdu, CCBDISP_NOWAIT, PDUDISP_FREE); | | 797 | send_pdu(tx_ccb, tx_pdu, CCBDISP_NOWAIT, PDUDISP_FREE); |
798 | } | | 798 | } |
799 | | | 799 | |
800 | | | 800 | |
801 | /* | | 801 | /* |
802 | * start_text_negotiation: | | 802 | * start_text_negotiation: |
803 | * Handle target request to negotiate (via asynch event) | | 803 | * Handle target request to negotiate (via asynch event) |
804 | * | | 804 | * |
805 | * Parameter: | | 805 | * Parameter: |
806 | * conn The connection | | 806 | * conn The connection |
807 | */ | | 807 | */ |
808 | | | 808 | |
809 | void | | 809 | void |
810 | start_text_negotiation(connection_t *conn) | | 810 | start_text_negotiation(connection_t *conn) |
811 | { | | 811 | { |
812 | pdu_t *pdu; | | 812 | pdu_t *pdu; |
813 | ccb_t *ccb; | | 813 | ccb_t *ccb; |
814 | | | 814 | |
815 | ccb = get_ccb(conn, TRUE); | | 815 | ccb = get_ccb(conn, TRUE); |
816 | if (ccb == NULL) | | 816 | if (ccb == NULL) |
817 | return; | | 817 | return; |
818 | pdu = get_pdu(conn, TRUE); | | 818 | pdu = get_pdu(conn, TRUE); |
819 | if (pdu == NULL) { | | 819 | if (pdu == NULL) { |
820 | free_ccb(ccb); | | 820 | free_ccb(ccb); |
821 | return; | | 821 | return; |
822 | } | | 822 | } |
823 | | | 823 | |
824 | if (init_text_parameters(conn, ccb)) { | | 824 | if (init_text_parameters(conn, ccb)) { |
825 | free_ccb(ccb); | | 825 | free_ccb(ccb); |
826 | free_pdu(pdu); | | 826 | free_pdu(pdu); |
827 | return; | | 827 | return; |
828 | } | | 828 | } |
829 | | | 829 | |
830 | init_text_pdu(conn, ccb, pdu, NULL); | | 830 | init_text_pdu(conn, ccb, pdu, NULL); |
831 | setup_tx_uio(pdu, 0, NULL, FALSE); | | 831 | setup_tx_uio(pdu, 0, NULL, FALSE); |
832 | send_pdu(ccb, pdu, CCBDISP_FREE, PDUDISP_WAIT); | | 832 | send_pdu(ccb, pdu, CCBDISP_FREE, PDUDISP_WAIT); |
833 | } | | 833 | } |
834 | | | 834 | |
835 | | | 835 | |
836 | /* | | 836 | /* |
837 | * negotiate_text: | | 837 | * negotiate_text: |
838 | * Handle received text negotiation. | | 838 | * Handle received text negotiation. |
839 | * | | 839 | * |
840 | * Parameter: | | 840 | * Parameter: |
841 | * conn The connection | | 841 | * conn The connection |
842 | * rx_pdu The received text response PDU | | 842 | * rx_pdu The received text response PDU |
843 | * tx_ccb The original CCB | | 843 | * tx_ccb The original CCB |
844 | */ | | 844 | */ |
845 | | | 845 | |
846 | void | | 846 | void |
847 | negotiate_text(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb) | | 847 | negotiate_text(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb) |
848 | { | | 848 | { |
849 | int rc; | | 849 | int rc; |
850 | pdu_t *tx_pdu; | | 850 | pdu_t *tx_pdu; |
851 | | | 851 | |
852 | if (tx_ccb->flags & CCBF_SENDTARGET) { | | 852 | if (tx_ccb->flags & CCBF_SENDTARGET) { |
853 | if (!(rx_pdu->pdu.Flags & FLAG_FINAL)) { | | 853 | if (!(rx_pdu->pdu.Flags & FLAG_FINAL)) { |
854 | handle_connection_error(conn, ISCSI_STATUS_PROTOCOL_ERROR, | | 854 | handle_connection_error(conn, ISCSI_STATUS_PROTOCOL_ERROR, |
855 | LOGOUT_CONNECTION); | | 855 | LOGOUT_CONNECTION); |
856 | return; | | 856 | return; |
857 | } | | 857 | } |
858 | /* transfer ownership of text to CCB */ | | 858 | /* transfer ownership of text to CCB */ |
859 | tx_ccb->text_data = rx_pdu->temp_data; | | 859 | tx_ccb->text_data = rx_pdu->temp_data; |
860 | tx_ccb->text_len = rx_pdu->temp_data_len; | | 860 | tx_ccb->text_len = rx_pdu->temp_data_len; |
861 | rx_pdu->temp_data = NULL; | | 861 | rx_pdu->temp_data = NULL; |
862 | wake_ccb(tx_ccb, ISCSI_STATUS_SUCCESS); | | 862 | wake_ccb(tx_ccb, ISCSI_STATUS_SUCCESS); |
863 | } else { | | 863 | } else { |
864 | if (!(rx_pdu->pdu.Flags & FLAG_FINAL)) | | 864 | if (!(rx_pdu->pdu.Flags & FLAG_FINAL)) |
865 | tx_pdu = get_pdu(conn, TRUE); | | 865 | tx_pdu = get_pdu(conn, TRUE); |
866 | else | | 866 | else |
867 | tx_pdu = NULL; | | 867 | tx_pdu = NULL; |
868 | | | 868 | |
869 | rc = assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, tx_pdu); | | 869 | rc = assemble_negotiation_parameters(conn, tx_ccb, rx_pdu, tx_pdu); |
870 | if (rc) { | | 870 | if (rc) { |
871 | if (tx_pdu != NULL) | | 871 | if (tx_pdu != NULL) |
872 | free_pdu(tx_pdu); | | 872 | free_pdu(tx_pdu); |
873 | | | 873 | |
874 | handle_connection_error(conn, rc, LOGOUT_CONNECTION); | | 874 | handle_connection_error(conn, rc, LOGOUT_CONNECTION); |
875 | } else if (tx_pdu != NULL) { | | 875 | } else if (tx_pdu != NULL) { |
876 | init_text_pdu(conn, tx_ccb, tx_pdu, rx_pdu); | | 876 | init_text_pdu(conn, tx_ccb, tx_pdu, rx_pdu); |
877 | setup_tx_uio(tx_pdu, tx_pdu->temp_data_len, | | 877 | setup_tx_uio(tx_pdu, tx_pdu->temp_data_len, |
878 | tx_pdu->temp_data, FALSE); | | 878 | tx_pdu->temp_data, FALSE); |
879 | send_pdu(tx_ccb, tx_pdu, CCBDISP_NOWAIT, PDUDISP_FREE); | | 879 | send_pdu(tx_ccb, tx_pdu, CCBDISP_NOWAIT, PDUDISP_FREE); |
880 | } else { | | 880 | } else { |
881 | set_negotiated_parameters(tx_ccb); | | 881 | set_negotiated_parameters(tx_ccb); |
882 | wake_ccb(tx_ccb, ISCSI_STATUS_SUCCESS); | | 882 | wake_ccb(tx_ccb, ISCSI_STATUS_SUCCESS); |
883 | } | | 883 | } |
884 | } | | 884 | } |
885 | } | | 885 | } |
886 | | | 886 | |
887 | | | 887 | |
888 | /* | | 888 | /* |
889 | * send_send_targets: | | 889 | * send_send_targets: |
890 | * Send out a SendTargets text request. | | 890 | * Send out a SendTargets text request. |
891 | * The result is stored in the fields in the session structure. | | 891 | * The result is stored in the fields in the session structure. |
892 | * | | 892 | * |
893 | * Parameter: | | 893 | * Parameter: |
894 | * session The session | | 894 | * session The session |
895 | * key The text key to use | | 895 | * key The text key to use |
896 | * | | 896 | * |
897 | * Returns: 0 on success, else an error code. | | 897 | * Returns: 0 on success, else an error code. |
898 | */ | | 898 | */ |
899 | | | 899 | |
900 | int | | 900 | int |
901 | send_send_targets(session_t *session, uint8_t *key) | | 901 | send_send_targets(session_t *session, uint8_t *key) |
902 | { | | 902 | { |
903 | ccb_t *ccb; | | 903 | ccb_t *ccb; |
904 | pdu_t *pdu; | | 904 | pdu_t *pdu; |
905 | int rc = 0; | | 905 | int rc = 0; |
906 | connection_t *conn; | | 906 | connection_t *conn; |
907 | | | 907 | |
908 | DEB(9, ("Send_send_targets\n")); | | 908 | DEB(9, ("Send_send_targets\n")); |
909 | | | 909 | |
910 | conn = assign_connection(session, TRUE); | | 910 | conn = assign_connection(session, TRUE); |
911 | if (conn == NULL || conn->terminating || conn->state != ST_FULL_FEATURE) | | 911 | if (conn == NULL || conn->terminating || conn->state != ST_FULL_FEATURE) |
912 | return (conn != NULL && conn->terminating) ? conn->terminating | | 912 | return (conn != NULL && conn->terminating) ? conn->terminating |
913 | : ISCSI_STATUS_CONNECTION_FAILED; | | 913 | : ISCSI_STATUS_CONNECTION_FAILED; |
914 | | | 914 | |
915 | ccb = get_ccb(conn, TRUE); | | 915 | ccb = get_ccb(conn, TRUE); |
916 | if (ccb == NULL) | | 916 | if (ccb == NULL) |
917 | return conn->terminating; | | 917 | return conn->terminating; |
918 | pdu = get_pdu(conn, TRUE); | | 918 | pdu = get_pdu(conn, TRUE); |
919 | if (pdu == NULL) { | | 919 | if (pdu == NULL) { |
920 | free_ccb(ccb); | | 920 | free_ccb(ccb); |
921 | return conn->terminating; | | 921 | return conn->terminating; |
922 | } | | 922 | } |
923 | | | 923 | |
924 | ccb->flags |= CCBF_SENDTARGET; | | 924 | ccb->flags |= CCBF_SENDTARGET; |
925 | | | 925 | |
926 | if ((rc = assemble_send_targets(pdu, key)) != 0) { | | 926 | if ((rc = assemble_send_targets(pdu, key)) != 0) { |
927 | free_ccb(ccb); | | 927 | free_ccb(ccb); |
928 | free_pdu(pdu); | | 928 | free_pdu(pdu); |
929 | return rc; | | 929 | return rc; |
930 | } | | 930 | } |
931 | | | 931 | |
932 | init_text_pdu(conn, ccb, pdu, NULL); | | 932 | init_text_pdu(conn, ccb, pdu, NULL); |
933 | | | 933 | |
934 | setup_tx_uio(pdu, pdu->temp_data_len, pdu->temp_data, FALSE); | | 934 | setup_tx_uio(pdu, pdu->temp_data_len, pdu->temp_data, FALSE); |
935 | send_pdu(ccb, pdu, CCBDISP_WAIT, PDUDISP_WAIT); | | 935 | send_pdu(ccb, pdu, CCBDISP_WAIT, PDUDISP_WAIT); |
936 | | | 936 | |
937 | rc = ccb->status; | | 937 | rc = ccb->status; |
938 | if (!rc) { | | 938 | if (!rc) { |
939 | /* transfer ownership of data */ | | 939 | /* transfer ownership of data */ |
940 | session->target_list = ccb->text_data; | | 940 | session->target_list = ccb->text_data; |
941 | session->target_list_len = ccb->text_len; | | 941 | session->target_list_len = ccb->text_len; |
942 | ccb->text_data = NULL; | | 942 | ccb->text_data = NULL; |
943 | } | | 943 | } |
944 | free_ccb(ccb); | | 944 | free_ccb(ccb); |
945 | return rc; | | 945 | return rc; |
946 | } | | 946 | } |
947 | | | 947 | |
948 | | | 948 | |
949 | /* | | 949 | /* |
950 | * send_nop_out: | | 950 | * send_nop_out: |
951 | * Send nop out request. | | 951 | * Send nop out request. |
952 | * | | 952 | * |
953 | * Parameter: | | 953 | * Parameter: |
954 | * conn The connection | | 954 | * conn The connection |
955 | * rx_pdu The received Nop-In PDU | | 955 | * rx_pdu The received Nop-In PDU |
956 | * | | 956 | * |
957 | * Returns: 0 on success, else an error code. | | 957 | * Returns: 0 on success, else an error code. |
958 | */ | | 958 | */ |
959 | | | 959 | |
960 | int | | 960 | int |
961 | send_nop_out(connection_t *conn, pdu_t *rx_pdu) | | 961 | send_nop_out(connection_t *conn, pdu_t *rx_pdu) |
962 | { | | 962 | { |
963 | session_t *sess; | | 963 | session_t *sess; |
964 | ccb_t *ccb; | | 964 | ccb_t *ccb; |
965 | pdu_t *ppdu; | | 965 | pdu_t *ppdu; |
966 | pdu_header_t *pdu; | | 966 | pdu_header_t *pdu; |
967 | uint32_t sn; | | 967 | uint32_t sn; |
968 | | | 968 | |
969 | if (rx_pdu != NULL) { | | 969 | if (rx_pdu != NULL) { |
970 | ccb = NULL; | | 970 | ccb = NULL; |
971 | ppdu = get_pdu(conn, TRUE); | | 971 | ppdu = get_pdu(conn, TRUE); |
972 | if (ppdu == NULL) | | 972 | if (ppdu == NULL) |
973 | return 1; | | 973 | return 1; |
974 | } else { | | 974 | } else { |
975 | ccb = get_ccb(conn, FALSE); | | 975 | ccb = get_ccb(conn, FALSE); |
976 | if (ccb == NULL) { | | 976 | if (ccb == NULL) { |
977 | DEBOUT(("Can't get CCB in send_nop_out\n")); | | 977 | DEBOUT(("Can't get CCB in send_nop_out\n")); |
978 | return 1; | | 978 | return 1; |
979 | } | | 979 | } |
980 | ppdu = get_pdu(conn, FALSE); | | 980 | ppdu = get_pdu(conn, FALSE); |
981 | if (ppdu == NULL) { | | 981 | if (ppdu == NULL) { |
982 | free_ccb(ccb); | | 982 | free_ccb(ccb); |
983 | DEBOUT(("Can't get PDU in send_nop_out\n")); | | 983 | DEBOUT(("Can't get PDU in send_nop_out\n")); |
984 | return 1; | | 984 | return 1; |
985 | } | | 985 | } |
986 | } | | 986 | } |
987 | | | 987 | |
988 | pdu = &ppdu->pdu; | | 988 | pdu = &ppdu->pdu; |
989 | pdu->Flags = FLAG_FINAL; | | 989 | pdu->Flags = FLAG_FINAL; |
990 | pdu->Opcode = IOP_NOP_Out | OP_IMMEDIATE; | | 990 | pdu->Opcode = IOP_NOP_Out | OP_IMMEDIATE; |
991 | | | 991 | |
992 | sess = conn->session; | | 992 | sess = conn->session; |
993 | | | 993 | |
994 | mutex_enter(&sess->lock); | | 994 | mutex_enter(&sess->lock); |
995 | sn = get_sernum(sess, ppdu); | | 995 | sn = get_sernum(sess, ppdu); |
996 | mutex_exit(&sess->lock); | | 996 | mutex_exit(&sess->lock); |
997 | | | 997 | |
998 | if (rx_pdu != NULL) { | | 998 | if (rx_pdu != NULL) { |
999 | pdu->p.nop_out.TargetTransferTag = | | 999 | pdu->p.nop_out.TargetTransferTag = |
1000 | rx_pdu->pdu.p.nop_in.TargetTransferTag; | | 1000 | rx_pdu->pdu.p.nop_in.TargetTransferTag; |
1001 | pdu->InitiatorTaskTag = rx_pdu->pdu.InitiatorTaskTag; | | 1001 | pdu->InitiatorTaskTag = rx_pdu->pdu.InitiatorTaskTag; |
1002 | pdu->p.nop_out.CmdSN = htonl(sn); | | 1002 | pdu->p.nop_out.CmdSN = htonl(sn); |
1003 | pdu->LUN = rx_pdu->pdu.LUN; | | 1003 | pdu->LUN = rx_pdu->pdu.LUN; |
1004 | } else { | | 1004 | } else { |
1005 | pdu->p.nop_out.TargetTransferTag = 0xffffffff; | | 1005 | pdu->p.nop_out.TargetTransferTag = 0xffffffff; |
1006 | pdu->InitiatorTaskTag = 0xffffffff; | | 1006 | pdu->InitiatorTaskTag = 0xffffffff; |
1007 | ccb->CmdSN = sn; | | 1007 | ccb->CmdSN = sn; |
1008 | pdu->p.nop_out.CmdSN = htonl(sn); | | 1008 | pdu->p.nop_out.CmdSN = htonl(sn); |
1009 | } | | 1009 | } |
1010 | | | 1010 | |
1011 | DEBC(conn, 10, ("Send NOP_Out CmdSN=%d, rx_pdu=%p\n", sn, rx_pdu)); | | 1011 | DEBC(conn, 10, ("Send NOP_Out CmdSN=%d, rx_pdu=%p\n", sn, rx_pdu)); |
1012 | | | 1012 | |
1013 | setup_tx_uio(ppdu, 0, NULL, FALSE); | | 1013 | setup_tx_uio(ppdu, 0, NULL, FALSE); |
1014 | send_pdu(ccb, ppdu, (rx_pdu != NULL) ? CCBDISP_NOWAIT : CCBDISP_FREE, | | 1014 | send_pdu(ccb, ppdu, (rx_pdu != NULL) ? CCBDISP_NOWAIT : CCBDISP_FREE, |
1015 | PDUDISP_FREE); | | 1015 | PDUDISP_FREE); |
1016 | return 0; | | 1016 | return 0; |
1017 | } | | 1017 | } |
1018 | | | 1018 | |
1019 | | | 1019 | |
1020 | /* | | 1020 | /* |
1021 | * snack_missing: | | 1021 | * snack_missing: |
1022 | * Send SNACK request for missing data. | | 1022 | * Send SNACK request for missing data. |
1023 | * | | 1023 | * |
1024 | * Parameter: | | 1024 | * Parameter: |
1025 | * conn The connection | | 1025 | * conn The connection |
1026 | * ccb The task's CCB (for Data NAK only) | | 1026 | * ccb The task's CCB (for Data NAK only) |
1027 | * type The SNACK type | | 1027 | * type The SNACK type |
1028 | * BegRun The BegRun field | | 1028 | * BegRun The BegRun field |
1029 | * RunLength The RunLength field | | 1029 | * RunLength The RunLength field |
1030 | */ | | 1030 | */ |
1031 | | | 1031 | |
1032 | void | | 1032 | void |
1033 | snack_missing(connection_t *conn, ccb_t *ccb, uint8_t type, | | 1033 | snack_missing(connection_t *conn, ccb_t *ccb, uint8_t type, |
1034 | uint32_t BegRun, uint32_t RunLength) | | 1034 | uint32_t BegRun, uint32_t RunLength) |
1035 | { | | 1035 | { |
1036 | pdu_t *ppdu; | | 1036 | pdu_t *ppdu; |
1037 | pdu_header_t *pdu; | | 1037 | pdu_header_t *pdu; |
1038 | | | 1038 | |
1039 | ppdu = get_pdu(conn, TRUE); | | 1039 | ppdu = get_pdu(conn, TRUE); |
1040 | if (ppdu == NULL) | | 1040 | if (ppdu == NULL) |
1041 | return; | | 1041 | return; |
1042 | pdu = &ppdu->pdu; | | 1042 | pdu = &ppdu->pdu; |
1043 | pdu->Opcode = IOP_SNACK_Request; | | 1043 | pdu->Opcode = IOP_SNACK_Request; |
1044 | pdu->Flags = FLAG_FINAL | type; | | 1044 | pdu->Flags = FLAG_FINAL | type; |
1045 | | | 1045 | |
1046 | pdu->InitiatorTaskTag = (type == SNACK_DATA_NAK) ? ccb->ITT : 0xffffffff; | | 1046 | pdu->InitiatorTaskTag = (type == SNACK_DATA_NAK) ? ccb->ITT : 0xffffffff; |
1047 | pdu->p.snack.TargetTransferTag = 0xffffffff; | | 1047 | pdu->p.snack.TargetTransferTag = 0xffffffff; |
1048 | pdu->p.snack.BegRun = htonl(BegRun); | | 1048 | pdu->p.snack.BegRun = htonl(BegRun); |
1049 | pdu->p.snack.RunLength = htonl(RunLength); | | 1049 | pdu->p.snack.RunLength = htonl(RunLength); |
1050 | | | 1050 | |
1051 | ppdu->flags = PDUF_PRIORITY; | | 1051 | ppdu->flags = PDUF_PRIORITY; |
1052 | | | 1052 | |
1053 | setup_tx_uio(ppdu, 0, NULL, FALSE); | | 1053 | setup_tx_uio(ppdu, 0, NULL, FALSE); |
1054 | send_pdu(NULL, ppdu, CCBDISP_NOWAIT, PDUDISP_FREE); | | 1054 | send_pdu(NULL, ppdu, CCBDISP_NOWAIT, PDUDISP_FREE); |
1055 | } | | 1055 | } |
1056 | | | 1056 | |
1057 | | | 1057 | |
1058 | /* | | 1058 | /* |
1059 | * send_snack: | | 1059 | * send_snack: |
1060 | * Send SNACK request. | | 1060 | * Send SNACK request. |
1061 | * | | 1061 | * |
1062 | * Parameter: | | 1062 | * Parameter: |
1063 | * conn The connection | | 1063 | * conn The connection |
1064 | * rx_pdu The received data in PDU | | 1064 | * rx_pdu The received data in PDU |
1065 | * tx_ccb The original command CCB (required for Data ACK only) | | 1065 | * tx_ccb The original command CCB (required for Data ACK only) |
1066 | * type The SNACK type | | 1066 | * type The SNACK type |
1067 | * | | 1067 | * |
1068 | * Returns: 0 on success, else an error code. | | 1068 | * Returns: 0 on success, else an error code. |
1069 | */ | | 1069 | */ |
1070 | | | 1070 | |
1071 | void | | 1071 | void |
1072 | send_snack(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb, uint8_t type) | | 1072 | send_snack(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb, uint8_t type) |
1073 | { | | 1073 | { |
1074 | pdu_t *ppdu; | | 1074 | pdu_t *ppdu; |
1075 | pdu_header_t *pdu; | | 1075 | pdu_header_t *pdu; |
1076 | | | 1076 | |
1077 | ppdu = get_pdu(conn, TRUE); | | 1077 | ppdu = get_pdu(conn, TRUE); |
1078 | if (ppdu == NULL) | | 1078 | if (ppdu == NULL) |
1079 | return; | | 1079 | return; |
1080 | pdu = &ppdu->pdu; | | 1080 | pdu = &ppdu->pdu; |
1081 | pdu->Opcode = IOP_SNACK_Request; | | 1081 | pdu->Opcode = IOP_SNACK_Request; |
1082 | pdu->Flags = FLAG_FINAL | type; | | 1082 | pdu->Flags = FLAG_FINAL | type; |
1083 | | | 1083 | |
1084 | switch (type) { | | 1084 | switch (type) { |
1085 | case SNACK_DATA_NAK: | | 1085 | case SNACK_DATA_NAK: |
1086 | pdu->InitiatorTaskTag = rx_pdu->pdu.InitiatorTaskTag; | | 1086 | pdu->InitiatorTaskTag = rx_pdu->pdu.InitiatorTaskTag; |
1087 | pdu->p.snack.TargetTransferTag = 0xffffffff; | | 1087 | pdu->p.snack.TargetTransferTag = 0xffffffff; |
1088 | pdu->p.snack.BegRun = rx_pdu->pdu.p.data_in.DataSN; | | 1088 | pdu->p.snack.BegRun = rx_pdu->pdu.p.data_in.DataSN; |
1089 | pdu->p.snack.RunLength = htonl(1); | | 1089 | pdu->p.snack.RunLength = htonl(1); |
1090 | break; | | 1090 | break; |
1091 | | | 1091 | |
1092 | case SNACK_STATUS_NAK: | | 1092 | case SNACK_STATUS_NAK: |
1093 | pdu->InitiatorTaskTag = 0xffffffff; | | 1093 | pdu->InitiatorTaskTag = 0xffffffff; |
1094 | pdu->p.snack.TargetTransferTag = 0xffffffff; | | 1094 | pdu->p.snack.TargetTransferTag = 0xffffffff; |
1095 | pdu->p.snack.BegRun = rx_pdu->pdu.p.response.StatSN; | | 1095 | pdu->p.snack.BegRun = rx_pdu->pdu.p.response.StatSN; |
1096 | pdu->p.snack.RunLength = htonl(1); | | 1096 | pdu->p.snack.RunLength = htonl(1); |
1097 | break; | | 1097 | break; |
1098 | | | 1098 | |
1099 | case SNACK_DATA_ACK: | | 1099 | case SNACK_DATA_ACK: |
1100 | pdu->InitiatorTaskTag = 0xffffffff; | | 1100 | pdu->InitiatorTaskTag = 0xffffffff; |
1101 | pdu->p.snack.TargetTransferTag = | | 1101 | pdu->p.snack.TargetTransferTag = |
1102 | rx_pdu->pdu.p.data_in.TargetTransferTag; | | 1102 | rx_pdu->pdu.p.data_in.TargetTransferTag; |
1103 | pdu->p.snack.BegRun = tx_ccb->DataSN_buf.ExpSN; | | 1103 | pdu->p.snack.BegRun = tx_ccb->DataSN_buf.ExpSN; |
1104 | pdu->p.snack.RunLength = 0; | | 1104 | pdu->p.snack.RunLength = 0; |
1105 | break; | | 1105 | break; |
1106 | | | 1106 | |
1107 | default: | | 1107 | default: |
1108 | DEBOUT(("Invalid type %d in send_snack\n", type)); | | 1108 | DEBOUT(("Invalid type %d in send_snack\n", type)); |
1109 | return; | | 1109 | return; |
1110 | } | | 1110 | } |
1111 | | | 1111 | |
1112 | pdu->LUN = rx_pdu->pdu.LUN; | | 1112 | pdu->LUN = rx_pdu->pdu.LUN; |
1113 | | | 1113 | |
1114 | ppdu->flags = PDUF_PRIORITY; | | 1114 | ppdu->flags = PDUF_PRIORITY; |
1115 | | | 1115 | |
1116 | setup_tx_uio(ppdu, 0, NULL, FALSE); | | 1116 | setup_tx_uio(ppdu, 0, NULL, FALSE); |
1117 | send_pdu(NULL, ppdu, CCBDISP_NOWAIT, PDUDISP_FREE); | | 1117 | send_pdu(NULL, ppdu, CCBDISP_NOWAIT, PDUDISP_FREE); |
1118 | } | | 1118 | } |
1119 | | | 1119 | |
1120 | | | 1120 | |
1121 | /* | | 1121 | /* |
1122 | * send_login: | | 1122 | * send_login: |
1123 | * Send login request. | | 1123 | * Send login request. |
1124 | * | | 1124 | * |
1125 | * Parameter: | | 1125 | * Parameter: |
1126 | * conn The connection | | 1126 | * conn The connection |
1127 | * par The login parameters (for negotiation) | | 1127 | * par The login parameters (for negotiation) |
1128 | * | | 1128 | * |
1129 | * Returns: 0 on success, else an error code. | | 1129 | * Returns: 0 on success, else an error code. |
1130 | */ | | 1130 | */ |
1131 | | | 1131 | |
1132 | int | | 1132 | int |
1133 | send_login(connection_t *conn) | | 1133 | send_login(connection_t *conn) |
1134 | { | | 1134 | { |
1135 | ccb_t *ccb; | | 1135 | ccb_t *ccb; |
1136 | pdu_t *pdu; | | 1136 | pdu_t *pdu; |
1137 | int rc; | | 1137 | int rc; |
1138 | | | 1138 | |
1139 | DEBC(conn, 9, ("Send_login\n")); | | 1139 | DEBC(conn, 9, ("Send_login\n")); |
1140 | ccb = get_ccb(conn, TRUE); | | 1140 | ccb = get_ccb(conn, TRUE); |
1141 | /* only if terminating (which couldn't possibly happen here, but...) */ | | 1141 | /* only if terminating (which couldn't possibly happen here, but...) */ |
1142 | if (ccb == NULL) | | 1142 | if (ccb == NULL) |
1143 | return conn->terminating; | | 1143 | return conn->terminating; |
1144 | pdu = get_pdu(conn, TRUE); | | 1144 | pdu = get_pdu(conn, TRUE); |
1145 | if (pdu == NULL) { | | 1145 | if (pdu == NULL) { |
1146 | free_ccb(ccb); | | 1146 | free_ccb(ccb); |
1147 | return conn->terminating; | | 1147 | return conn->terminating; |
1148 | } | | 1148 | } |
1149 | | | 1149 | |
1150 | if ((rc = assemble_login_parameters(conn, ccb, pdu)) <= 0) { | | 1150 | if ((rc = assemble_login_parameters(conn, ccb, pdu)) <= 0) { |
1151 | init_login_pdu(conn, ccb, pdu, !rc); | | 1151 | init_login_pdu(conn, ccb, pdu, !rc); |
1152 | setup_tx_uio(pdu, pdu->temp_data_len, pdu->temp_data, FALSE); | | 1152 | setup_tx_uio(pdu, pdu->temp_data_len, pdu->temp_data, FALSE); |
1153 | send_pdu(ccb, pdu, CCBDISP_WAIT, PDUDISP_FREE); | | 1153 | send_pdu(ccb, pdu, CCBDISP_WAIT, PDUDISP_FREE); |
1154 | rc = ccb->status; | | 1154 | rc = ccb->status; |
1155 | } else { | | 1155 | } else { |
1156 | free_pdu(pdu); | | 1156 | free_pdu(pdu); |
1157 | } | | 1157 | } |
1158 | free_ccb(ccb); | | 1158 | free_ccb(ccb); |
1159 | return rc; | | 1159 | return rc; |
1160 | } | | 1160 | } |
1161 | | | 1161 | |
1162 | | | 1162 | |
1163 | /* | | 1163 | /* |
1164 | * send_logout: | | 1164 | * send_logout: |
1165 | * Send logout request. | | 1165 | * Send logout request. |
1166 | * NOTE: This function does not wait for the logout to complete. | | 1166 | * NOTE: This function does not wait for the logout to complete. |
1167 | * | | 1167 | * |
1168 | * Parameter: | | 1168 | * Parameter: |
1169 | * conn The connection | | 1169 | * conn The connection |
1170 | * refconn The referenced connection | | 1170 | * refconn The referenced connection |
1171 | * reason The reason code | | 1171 | * reason The reason code |
1172 | * wait Wait for completion if TRUE | | 1172 | * wait Wait for completion if TRUE |
1173 | * | | 1173 | * |
1174 | * Returns: 0 on success (logout sent), else an error code. | | 1174 | * Returns: 0 on success (logout sent), else an error code. |
1175 | */ | | 1175 | */ |
1176 | | | 1176 | |
1177 | int | | 1177 | int |
1178 | send_logout(connection_t *conn, connection_t *refconn, int reason, | | 1178 | send_logout(connection_t *conn, connection_t *refconn, int reason, |
1179 | bool wait) | | 1179 | bool wait) |
1180 | { | | 1180 | { |
1181 | ccb_t *ccb; | | 1181 | ccb_t *ccb; |
1182 | pdu_t *ppdu; | | 1182 | pdu_t *ppdu; |
1183 | pdu_header_t *pdu; | | 1183 | pdu_header_t *pdu; |
1184 | | | 1184 | |
1185 | DEBC(conn, 5, ("Send_logout\n")); | | 1185 | DEBC(conn, 5, ("Send_logout\n")); |
1186 | ccb = get_ccb(conn, TRUE); | | 1186 | ccb = get_ccb(conn, TRUE); |
1187 | /* can only happen if terminating... */ | | 1187 | /* can only happen if terminating... */ |
1188 | if (ccb == NULL) | | 1188 | if (ccb == NULL) |
1189 | return conn->terminating; | | 1189 | return conn->terminating; |
1190 | ppdu = get_pdu(conn, TRUE); | | 1190 | ppdu = get_pdu(conn, TRUE); |
1191 | if (ppdu == NULL) { | | 1191 | if (ppdu == NULL) { |
1192 | free_ccb(ccb); | | 1192 | free_ccb(ccb); |
1193 | return conn->terminating; | | 1193 | return conn->terminating; |
1194 | } | | 1194 | } |
1195 | | | 1195 | |
1196 | pdu = &ppdu->pdu; | | 1196 | pdu = &ppdu->pdu; |
1197 | pdu->Opcode = IOP_Logout_Request | OP_IMMEDIATE; | | 1197 | pdu->Opcode = IOP_Logout_Request | OP_IMMEDIATE; |
1198 | | | 1198 | |
1199 | pdu->Flags = FLAG_FINAL | reason; | | 1199 | pdu->Flags = FLAG_FINAL | reason; |
1200 | ccb->CmdSN = conn->session->CmdSN; | | 1200 | ccb->CmdSN = conn->session->CmdSN; |
1201 | pdu->p.logout_req.CmdSN = htonl(ccb->CmdSN); | | 1201 | pdu->p.logout_req.CmdSN = htonl(ccb->CmdSN); |
1202 | if (reason > 0) | | 1202 | if (reason > 0) |
1203 | pdu->p.logout_req.CID = htons(refconn->id); | | 1203 | pdu->p.logout_req.CID = htons(refconn->id); |
1204 | | | 1204 | |
1205 | ccb->par = refconn; | | 1205 | ccb->par = refconn; |
1206 | if (refconn != conn) { | | 1206 | if (refconn != conn) { |
1207 | ccb->flags |= CCBF_OTHERCONN; | | 1207 | ccb->flags |= CCBF_OTHERCONN; |
1208 | } else { | | 1208 | } else { |
1209 | conn->state = ST_LOGOUT_SENT; | | 1209 | conn->state = ST_LOGOUT_SENT; |
1210 | conn->loggedout = LOGOUT_SENT; | | 1210 | conn->loggedout = LOGOUT_SENT; |
1211 | } | | 1211 | } |
1212 | | | 1212 | |
1213 | setup_tx_uio(ppdu, 0, NULL, FALSE); | | 1213 | setup_tx_uio(ppdu, 0, NULL, FALSE); |
1214 | send_pdu(ccb, ppdu, (wait) ? CCBDISP_WAIT : CCBDISP_FREE, PDUDISP_FREE); | | 1214 | send_pdu(ccb, ppdu, (wait) ? CCBDISP_WAIT : CCBDISP_FREE, PDUDISP_FREE); |
1215 | | | 1215 | |
1216 | if (wait) { | | 1216 | if (wait) { |
1217 | int rc = ccb->status; | | 1217 | int rc = ccb->status; |
1218 | free_ccb (ccb); | | 1218 | free_ccb (ccb); |
1219 | return rc; | | 1219 | return rc; |
1220 | } | | 1220 | } |
1221 | return 0; | | 1221 | return 0; |
1222 | } | | 1222 | } |
1223 | | | 1223 | |
1224 | | | 1224 | |
1225 | /* | | 1225 | /* |
1226 | * send_task_management: | | 1226 | * send_task_management: |
1227 | * Send task management request. | | 1227 | * Send task management request. |
1228 | * | | 1228 | * |
1229 | * Parameter: | | 1229 | * Parameter: |
1230 | * conn The connection | | 1230 | * conn The connection |
1231 | * ref_ccb The referenced command (NULL if none) | | 1231 | * ref_ccb The referenced command (NULL if none) |
1232 | * xs The scsipi command structure (NULL if not a scsipi request) | | 1232 | * xs The scsipi command structure (NULL if not a scsipi request) |
1233 | * function The function code | | 1233 | * function The function code |
1234 | * | | 1234 | * |
1235 | * Returns: 0 on success, else an error code. | | 1235 | * Returns: 0 on success, else an error code. |
1236 | */ | | 1236 | */ |
1237 | | | 1237 | |
1238 | int | | 1238 | int |
1239 | send_task_management(connection_t *conn, ccb_t *ref_ccb, struct scsipi_xfer *xs, | | 1239 | send_task_management(connection_t *conn, ccb_t *ref_ccb, struct scsipi_xfer *xs, |
1240 | int function) | | 1240 | int function) |
1241 | { | | 1241 | { |
1242 | ccb_t *ccb; | | 1242 | ccb_t *ccb; |
1243 | pdu_t *ppdu; | | 1243 | pdu_t *ppdu; |
1244 | pdu_header_t *pdu; | | 1244 | pdu_header_t *pdu; |
1245 | | | 1245 | |
1246 | DEBC(conn, 5, ("Send_task_management, ref_ccb=%p, func = %d\n", | | 1246 | DEBC(conn, 5, ("Send_task_management, ref_ccb=%p, func = %d\n", |
1247 | ref_ccb, function)); | | 1247 | ref_ccb, function)); |
1248 | | | 1248 | |
1249 | if (function == TASK_REASSIGN && conn->session->ErrorRecoveryLevel < 2) | | 1249 | if (function == TASK_REASSIGN && conn->session->ErrorRecoveryLevel < 2) |
1250 | return ISCSI_STATUS_CANT_REASSIGN; | | 1250 | return ISCSI_STATUS_CANT_REASSIGN; |
1251 | | | 1251 | |
1252 | ccb = get_ccb(conn, xs == NULL); | | 1252 | ccb = get_ccb(conn, xs == NULL); |
1253 | /* can only happen if terminating... */ | | 1253 | /* can only happen if terminating... */ |
1254 | if (ccb == NULL) | | 1254 | if (ccb == NULL) { |
| | | 1255 | DEBC(conn, 0, ("send_task_management, ref_ccb=%p, xs=%p, term=%d. No CCB\n", |
| | | 1256 | ref_ccb, xs, conn->terminating)); |
1255 | return conn->terminating; | | 1257 | return conn->terminating; |
| | | 1258 | } |
1256 | ppdu = get_pdu(conn, xs == NULL); | | 1259 | ppdu = get_pdu(conn, xs == NULL); |
1257 | if (ppdu == NULL) { | | 1260 | if (ppdu == NULL) { |
| | | 1261 | DEBC(conn, 0, ("send_task_management, ref_ccb=%p, xs=%p, term=%d. No PDU\n", |
| | | 1262 | ref_ccb, xs, conn->terminating)); |
1258 | free_ccb(ccb); | | 1263 | free_ccb(ccb); |
1259 | return conn->terminating; | | 1264 | return conn->terminating; |
1260 | } | | 1265 | } |
1261 | | | 1266 | |
1262 | ccb->xs = xs; | | 1267 | ccb->xs = xs; |
1263 | | | 1268 | |
1264 | pdu = &ppdu->pdu; | | 1269 | pdu = &ppdu->pdu; |
1265 | pdu->Opcode = IOP_SCSI_Task_Management | OP_IMMEDIATE; | | 1270 | pdu->Opcode = IOP_SCSI_Task_Management | OP_IMMEDIATE; |
1266 | pdu->Flags = FLAG_FINAL | function; | | 1271 | pdu->Flags = FLAG_FINAL | function; |
1267 | | | 1272 | |
1268 | ccb->CmdSN = conn->session->CmdSN; | | 1273 | ccb->CmdSN = conn->session->CmdSN; |
1269 | pdu->p.task_req.CmdSN = htonl(ccb->CmdSN); | | 1274 | pdu->p.task_req.CmdSN = htonl(ccb->CmdSN); |
1270 | | | 1275 | |
1271 | if (ref_ccb != NULL) { | | 1276 | if (ref_ccb != NULL) { |
1272 | pdu->p.task_req.ReferencedTaskTag = ref_ccb->ITT; | | 1277 | pdu->p.task_req.ReferencedTaskTag = ref_ccb->ITT; |
1273 | pdu->p.task_req.RefCmdSN = htonl(ref_ccb->CmdSN); | | 1278 | pdu->p.task_req.RefCmdSN = htonl(ref_ccb->CmdSN); |
1274 | pdu->p.task_req.ExpDataSN = htonl(ref_ccb->DataSN_buf.ExpSN); | | 1279 | pdu->p.task_req.ExpDataSN = htonl(ref_ccb->DataSN_buf.ExpSN); |
1275 | } else | | 1280 | } else |
1276 | pdu->p.task_req.ReferencedTaskTag = 0xffffffff; | | 1281 | pdu->p.task_req.ReferencedTaskTag = 0xffffffff; |
1277 | | | 1282 | |
1278 | ppdu->flags |= PDUF_PRIORITY; | | 1283 | ppdu->flags |= PDUF_PRIORITY; |
1279 | | | 1284 | |
1280 | setup_tx_uio(ppdu, 0, NULL, FALSE); | | 1285 | setup_tx_uio(ppdu, 0, NULL, FALSE); |
1281 | send_pdu(ccb, ppdu, (xs) ? CCBDISP_SCSIPI : CCBDISP_WAIT, PDUDISP_FREE); | | 1286 | send_pdu(ccb, ppdu, (xs) ? CCBDISP_SCSIPI : CCBDISP_WAIT, PDUDISP_FREE); |
1282 | | | 1287 | |
1283 | if (xs == NULL) { | | 1288 | if (xs == NULL) { |
1284 | int rc = ccb->status; | | 1289 | int rc = ccb->status; |
1285 | free_ccb(ccb); | | 1290 | free_ccb(ccb); |
1286 | return rc; | | 1291 | return rc; |
1287 | } | | 1292 | } |
1288 | return 0; | | 1293 | return 0; |
1289 | } | | 1294 | } |
1290 | | | 1295 | |
1291 | | | 1296 | |
1292 | /* | | 1297 | /* |
1293 | * send_data_out: | | 1298 | * send_data_out: |
1294 | * Send data to target in response to an R2T or as unsolicited data. | | 1299 | * Send data to target in response to an R2T or as unsolicited data. |
1295 | * | | 1300 | * |
1296 | * Parameter: | | 1301 | * Parameter: |
1297 | * conn The connection | | 1302 | * conn The connection |
1298 | * rx_pdu The received R2T PDU (NULL if unsolicited) | | 1303 | * rx_pdu The received R2T PDU (NULL if unsolicited) |
1299 | * tx_ccb The originally sent command CCB | | 1304 | * tx_ccb The originally sent command CCB |
1300 | * waitok Whether it's OK to wait for an available PDU or not | | 1305 | * waitok Whether it's OK to wait for an available PDU or not |
1301 | */ | | 1306 | */ |
1302 | | | 1307 | |
1303 | int | | 1308 | int |
1304 | send_data_out(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb, | | 1309 | send_data_out(connection_t *conn, pdu_t *rx_pdu, ccb_t *tx_ccb, |
1305 | ccb_disp_t disp, bool waitok) | | 1310 | ccb_disp_t disp, bool waitok) |
1306 | { | | 1311 | { |
1307 | pdu_header_t *pdu; | | 1312 | pdu_header_t *pdu; |
1308 | uint32_t totlen, len, offs, sn; | | 1313 | uint32_t totlen, len, offs, sn; |
1309 | pdu_t *tx_pdu; | | 1314 | pdu_t *tx_pdu; |
1310 | | | 1315 | |
1311 | KASSERT(conn->max_transfer != 0); | | 1316 | KASSERT(conn->max_transfer != 0); |
1312 | | | 1317 | |
1313 | if (rx_pdu) { | | 1318 | if (rx_pdu) { |
1314 | offs = ntohl(rx_pdu->pdu.p.r2t.BufferOffset); | | 1319 | offs = ntohl(rx_pdu->pdu.p.r2t.BufferOffset); |
1315 | totlen = ntohl(rx_pdu->pdu.p.r2t.DesiredDataTransferLength); | | 1320 | totlen = ntohl(rx_pdu->pdu.p.r2t.DesiredDataTransferLength); |
1316 | } else { | | 1321 | } else { |
1317 | offs = conn->max_firstimmed; | | 1322 | offs = conn->max_firstimmed; |
1318 | totlen = min(conn->max_firstdata - offs, tx_ccb->data_len - offs); | | 1323 | totlen = min(conn->max_firstdata - offs, tx_ccb->data_len - offs); |
1319 | } | | 1324 | } |
1320 | sn = 0; | | 1325 | sn = 0; |
1321 | | | 1326 | |
1322 | while (totlen) { | | 1327 | while (totlen) { |
1323 | len = min(totlen, conn->max_transfer); | | 1328 | len = min(totlen, conn->max_transfer); |
1324 | | | 1329 | |
1325 | tx_pdu = get_pdu(conn, waitok); | | 1330 | tx_pdu = get_pdu(conn, waitok); |
1326 | if (tx_pdu == NULL) { | | 1331 | if (tx_pdu == NULL) { |
1327 | DEBC(conn, 5, ("No PDU in send_data_out\n")); | | 1332 | DEBC(conn, 5, ("No PDU in send_data_out\n")); |
1328 | | | 1333 | |
1329 | tx_ccb->disp = disp; | | 1334 | tx_ccb->disp = disp; |
1330 | tx_ccb->status = ISCSI_STATUS_NO_RESOURCES; | | 1335 | tx_ccb->status = ISCSI_STATUS_NO_RESOURCES; |
1331 | handle_connection_error(conn, ISCSI_STATUS_NO_RESOURCES, NO_LOGOUT); | | 1336 | handle_connection_error(conn, ISCSI_STATUS_NO_RESOURCES, NO_LOGOUT); |
1332 | | | 1337 | |
1333 | return ISCSI_STATUS_NO_RESOURCES; | | 1338 | return ISCSI_STATUS_NO_RESOURCES; |
1334 | } | | 1339 | } |
1335 | | | 1340 | |
1336 | totlen -= len; | | 1341 | totlen -= len; |
1337 | pdu = &tx_pdu->pdu; | | 1342 | pdu = &tx_pdu->pdu; |
1338 | pdu->Opcode = IOP_SCSI_Data_out; | | 1343 | pdu->Opcode = IOP_SCSI_Data_out; |
1339 | if (!totlen) | | 1344 | if (!totlen) |
1340 | pdu->Flags = FLAG_FINAL; | | 1345 | pdu->Flags = FLAG_FINAL; |
1341 | | | 1346 | |
1342 | if (rx_pdu != NULL) | | 1347 | if (rx_pdu != NULL) |
1343 | pdu->p.data_out.TargetTransferTag = | | 1348 | pdu->p.data_out.TargetTransferTag = |
1344 | rx_pdu->pdu.p.r2t.TargetTransferTag; | | 1349 | rx_pdu->pdu.p.r2t.TargetTransferTag; |
1345 | else | | 1350 | else |
1346 | pdu->p.data_out.TargetTransferTag = 0xffffffff; | | 1351 | pdu->p.data_out.TargetTransferTag = 0xffffffff; |
1347 | pdu->p.data_out.BufferOffset = htonl(offs); | | 1352 | pdu->p.data_out.BufferOffset = htonl(offs); |
1348 | pdu->p.data_out.DataSN = htonl(sn); | | 1353 | pdu->p.data_out.DataSN = htonl(sn); |
1349 | | | 1354 | |
1350 | DEBC(conn, 10, ("Send DataOut: DataSN %d, len %d offs %x totlen %d\n", | | 1355 | DEBC(conn, 10, ("Send DataOut: DataSN %d, len %d offs %x totlen %d\n", |
1351 | sn, len, offs, totlen)); | | 1356 | sn, len, offs, totlen)); |
1352 | | | 1357 | |
1353 | setup_tx_uio(tx_pdu, len, tx_ccb->data_ptr + offs, FALSE); | | 1358 | setup_tx_uio(tx_pdu, len, tx_ccb->data_ptr + offs, FALSE); |
1354 | send_pdu(tx_ccb, tx_pdu, (totlen) ? CCBDISP_NOWAIT : disp, PDUDISP_FREE); | | 1359 | send_pdu(tx_ccb, tx_pdu, (totlen) ? CCBDISP_NOWAIT : disp, PDUDISP_FREE); |
1355 | | | 1360 | |
1356 | sn++; | | 1361 | sn++; |
1357 | offs += len; | | 1362 | offs += len; |
1358 | } | | 1363 | } |
1359 | return 0; | | 1364 | return 0; |
1360 | } | | 1365 | } |
1361 | | | 1366 | |
1362 | | | 1367 | |
1363 | /* | | 1368 | /* |
1364 | * send_command: | | 1369 | * send_command: |
1365 | * Send a SCSI command request. | | 1370 | * Send a SCSI command request. |
1366 | * | | 1371 | * |
1367 | * Parameter: | | 1372 | * Parameter: |
1368 | * CCB The CCB | | 1373 | * CCB The CCB |
1369 | * disp The CCB disposition | | 1374 | * disp The CCB disposition |
1370 | */ | | 1375 | */ |
1371 | | | 1376 | |
1372 | void | | 1377 | void |
1373 | send_command(ccb_t *ccb, ccb_disp_t disp, bool waitok, bool immed) | | 1378 | send_command(ccb_t *ccb, ccb_disp_t disp, bool waitok, bool immed) |
1374 | { | | 1379 | { |
1375 | uint32_t totlen, len; | | 1380 | uint32_t totlen, len; |
1376 | connection_t *conn = ccb->connection; | | 1381 | connection_t *conn = ccb->connection; |
1377 | session_t *sess = ccb->session; | | 1382 | session_t *sess = ccb->session; |
1378 | pdu_t *ppdu; | | 1383 | pdu_t *ppdu; |
1379 | pdu_header_t *pdu; | | 1384 | pdu_header_t *pdu; |
1380 | | | 1385 | |
1381 | mutex_enter(&sess->lock); | | 1386 | mutex_enter(&sess->lock); |
1382 | while (!sernum_in_window(sess)) { | | 1387 | while (!sernum_in_window(sess)) { |
1383 | mutex_exit(&sess->lock); | | 1388 | mutex_exit(&sess->lock); |
1384 | ccb->disp = disp; | | 1389 | ccb->disp = disp; |
1385 | wake_ccb(ccb, ISCSI_STATUS_QUEUE_FULL); | | 1390 | wake_ccb(ccb, ISCSI_STATUS_QUEUE_FULL); |
1386 | return; | | 1391 | return; |
1387 | } | | 1392 | } |
1388 | mutex_exit(&sess->lock); | | 1393 | mutex_exit(&sess->lock); |
1389 | | | 1394 | |
1390 | /* Don't confuse targets during (re-)negotations */ | | 1395 | /* Don't confuse targets during (re-)negotations */ |
1391 | if (conn->state != ST_FULL_FEATURE) { | | 1396 | if (conn->state != ST_FULL_FEATURE) { |
1392 | DEBOUT(("Invalid connection for send_command, ccb = %p\n",ccb)); | | 1397 | DEBOUT(("Invalid connection for send_command, ccb = %p\n",ccb)); |
1393 | ccb->disp = disp; | | 1398 | ccb->disp = disp; |
1394 | wake_ccb(ccb, ISCSI_STATUS_TARGET_BUSY); | | 1399 | wake_ccb(ccb, ISCSI_STATUS_TARGET_BUSY); |
1395 | return; | | 1400 | return; |
1396 | } | | 1401 | } |
1397 | | | 1402 | |
1398 | ppdu = get_pdu(conn, waitok); | | 1403 | ppdu = get_pdu(conn, waitok); |
1399 | if (ppdu == NULL) { | | 1404 | if (ppdu == NULL) { |
1400 | DEBOUT(("No PDU for send_command, ccb = %p\n",ccb)); | | 1405 | DEBOUT(("No PDU for send_command, ccb = %p\n",ccb)); |
1401 | ccb->disp = disp; | | 1406 | ccb->disp = disp; |
1402 | wake_ccb(ccb, ISCSI_STATUS_NO_RESOURCES); | | 1407 | wake_ccb(ccb, ISCSI_STATUS_NO_RESOURCES); |
1403 | return; | | 1408 | return; |
1404 | } | | 1409 | } |
1405 | | | 1410 | |
1406 | totlen = len = ccb->data_len; | | 1411 | totlen = len = ccb->data_len; |
1407 | | | 1412 | |
1408 | pdu = &ppdu->pdu; | | 1413 | pdu = &ppdu->pdu; |
1409 | pdu->LUN = htonq(ccb->lun); | | 1414 | pdu->LUN = htonq(ccb->lun); |
1410 | memcpy(pdu->p.command.SCSI_CDB, ccb->cmd, ccb->cmdlen); | | 1415 | memcpy(pdu->p.command.SCSI_CDB, ccb->cmd, ccb->cmdlen); |
1411 | pdu->Opcode = IOP_SCSI_Command; | | 1416 | pdu->Opcode = IOP_SCSI_Command; |
1412 | if (immed) | | 1417 | if (immed) |
1413 | pdu->Opcode |= OP_IMMEDIATE; | | 1418 | pdu->Opcode |= OP_IMMEDIATE; |
1414 | pdu->p.command.ExpectedDataTransferLength = htonl(totlen); | | 1419 | pdu->p.command.ExpectedDataTransferLength = htonl(totlen); |
1415 | | | 1420 | |
1416 | if (totlen) { | | 1421 | if (totlen) { |
1417 | if (ccb->data_in) { | | 1422 | if (ccb->data_in) { |
1418 | pdu->Flags = FLAG_READ; | | 1423 | pdu->Flags = FLAG_READ; |
1419 | totlen = 0; | | 1424 | totlen = 0; |
1420 | } else { | | 1425 | } else { |
1421 | pdu->Flags = FLAG_WRITE; | | 1426 | pdu->Flags = FLAG_WRITE; |
1422 | /* immediate data we can send */ | | 1427 | /* immediate data we can send */ |
1423 | len = min(totlen, conn->max_firstimmed); | | 1428 | len = min(totlen, conn->max_firstimmed); |
1424 | | | 1429 | |
1425 | /* can we send more unsolicited data ? */ | | 1430 | /* can we send more unsolicited data ? */ |
1426 | totlen = conn->max_firstdata ? totlen - len : 0; | | 1431 | totlen = conn->max_firstdata ? totlen - len : 0; |
1427 | } | | 1432 | } |
1428 | } | | 1433 | } |
1429 | if (!totlen) | | 1434 | if (!totlen) |
1430 | pdu->Flags |= FLAG_FINAL; | | 1435 | pdu->Flags |= FLAG_FINAL; |
1431 | pdu->Flags |= ccb->tag; | | 1436 | pdu->Flags |= ccb->tag; |
1432 | | | 1437 | |
1433 | if (ccb->data_in) | | 1438 | if (ccb->data_in) |
1434 | init_sernum(&ccb->DataSN_buf); | | 1439 | init_sernum(&ccb->DataSN_buf); |
1435 | | | 1440 | |
1436 | ccb->sense_len_got = 0; | | 1441 | ccb->sense_len_got = 0; |
1437 | ccb->xfer_len = 0; | | 1442 | ccb->xfer_len = 0; |
1438 | ccb->residual = 0; | | 1443 | ccb->residual = 0; |
1439 | ccb->flags |= CCBF_REASSIGN; | | 1444 | ccb->flags |= CCBF_REASSIGN; |
1440 | | | 1445 | |
1441 | mutex_enter(&sess->lock); | | 1446 | mutex_enter(&sess->lock); |
1442 | ccb->CmdSN = get_sernum(sess, ppdu); | | 1447 | ccb->CmdSN = get_sernum(sess, ppdu); |
1443 | mutex_exit(&sess->lock); | | 1448 | mutex_exit(&sess->lock); |
1444 | | | 1449 | |
1445 | pdu->p.command.CmdSN = htonl(ccb->CmdSN); | | 1450 | pdu->p.command.CmdSN = htonl(ccb->CmdSN); |
1446 | | | 1451 | |
1447 | DEBC(conn, 10, ("Send Command: CmdSN %d (%d), data_in %d, len %d, totlen %d\n", | | 1452 | DEBC(conn, 10, ("Send Command: CmdSN %d (%d), data_in %d, len %d, totlen %d\n", |
1448 | ccb->CmdSN, sess->MaxCmdSN, ccb->data_in, len, totlen)); | | 1453 | ccb->CmdSN, sess->MaxCmdSN, ccb->data_in, len, totlen)); |
1449 | | | 1454 | |
1450 | setup_tx_uio(ppdu, len, ccb->data_ptr, ccb->data_in); | | 1455 | setup_tx_uio(ppdu, len, ccb->data_ptr, ccb->data_in); |
1451 | send_pdu(ccb, ppdu, (totlen) ? CCBDISP_DEFER : disp, PDUDISP_WAIT); | | 1456 | send_pdu(ccb, ppdu, (totlen) ? CCBDISP_DEFER : disp, PDUDISP_WAIT); |
1452 | | | 1457 | |
1453 | if (totlen) | | 1458 | if (totlen) |
1454 | send_data_out(conn, NULL, ccb, disp, waitok); | | 1459 | send_data_out(conn, NULL, ccb, disp, waitok); |
1455 | } | | 1460 | } |
1456 | | | 1461 | |
1457 | | | 1462 | |
1458 | /* | | 1463 | /* |
1459 | * send_run_xfer: | | 1464 | * send_run_xfer: |
1460 | * Handle a SCSI command transfer request from scsipi. | | 1465 | * Handle a SCSI command transfer request from scsipi. |
1461 | * | | 1466 | * |
1462 | * Parameter: | | 1467 | * Parameter: |
1463 | * session The session | | 1468 | * session The session |
1464 | * xs The transfer parameters | | 1469 | * xs The transfer parameters |
1465 | */ | | 1470 | */ |
1466 | | | 1471 | |
1467 | void | | 1472 | void |
1468 | send_run_xfer(session_t *session, struct scsipi_xfer *xs) | | 1473 | send_run_xfer(session_t *session, struct scsipi_xfer *xs) |
1469 | { | | 1474 | { |
1470 | ccb_t *ccb; | | 1475 | ccb_t *ccb; |
1471 | connection_t *conn; | | 1476 | connection_t *conn; |
1472 | bool waitok; | | 1477 | bool waitok; |
1473 | | | 1478 | |
1474 | waitok = !(xs->xs_control & XS_CTL_NOSLEEP); | | 1479 | waitok = !(xs->xs_control & XS_CTL_NOSLEEP); |
1475 | | | 1480 | |
1476 | DEB(10, ("RunXfer: flags=%x, data=%p, datalen=%d, resid=%d, cmdlen=%d, " | | 1481 | DEB(10, ("RunXfer: flags=%x, data=%p, datalen=%d, resid=%d, cmdlen=%d, " |
1477 | "waitok=%d\n", xs->xs_control, xs->data, xs->datalen, | | 1482 | "waitok=%d\n", xs->xs_control, xs->data, xs->datalen, |
1478 | xs->resid, xs->cmdlen, waitok)); | | 1483 | xs->resid, xs->cmdlen, waitok)); |
1479 | | | 1484 | |
1480 | conn = assign_connection(session, waitok); | | 1485 | conn = assign_connection(session, waitok); |
1481 | | | 1486 | |
1482 | if (conn == NULL || conn->terminating || conn->state != ST_FULL_FEATURE) { | | 1487 | if (conn == NULL || conn->terminating || conn->state != ST_FULL_FEATURE) { |
1483 | xs->error = XS_SELTIMEOUT; | | 1488 | xs->error = XS_SELTIMEOUT; |
1484 | DEBC(conn, 10, ("run_xfer on dead connection\n")); | | 1489 | DEBC(conn, 10, ("run_xfer on dead connection\n")); |
1485 | scsipi_done(xs); | | 1490 | scsipi_done(xs); |
1486 | unref_session(session); | | 1491 | unref_session(session); |
1487 | return; | | 1492 | return; |
1488 | } | | 1493 | } |
1489 | | | 1494 | |
1490 | if (xs->xs_control & XS_CTL_RESET) { | | 1495 | if (xs->xs_control & XS_CTL_RESET) { |
1491 | if (send_task_management(conn, NULL, xs, TARGET_WARM_RESET)) { | | 1496 | if (send_task_management(conn, NULL, xs, TARGET_WARM_RESET)) { |
| | | 1497 | DEBC(conn, 0, ("send_task_management TARGET_WARM_RESET failed\n")); |
1492 | xs->error = XS_SELTIMEOUT; | | 1498 | xs->error = XS_SELTIMEOUT; |
1493 | scsipi_done(xs); | | 1499 | scsipi_done(xs); |
1494 | unref_session(session); | | 1500 | unref_session(session); |
1495 | } | | 1501 | } |
1496 | return; | | 1502 | return; |
1497 | } | | 1503 | } |
1498 | | | 1504 | |
1499 | ccb = get_ccb(conn, waitok); | | 1505 | ccb = get_ccb(conn, waitok); |
1500 | if (ccb == NULL) { | | 1506 | if (ccb == NULL) { |
1501 | xs->error = XS_BUSY; | | 1507 | xs->error = XS_BUSY; |
1502 | DEBC(conn, 5, ("No CCB in run_xfer, %d in use.\n", conn->usecount)); | | 1508 | DEBC(conn, 5, ("No CCB in run_xfer, %d in use.\n", conn->usecount)); |
1503 | scsipi_done(xs); | | 1509 | scsipi_done(xs); |
1504 | unref_session(session); | | 1510 | unref_session(session); |
1505 | return; | | 1511 | return; |
1506 | } | | 1512 | } |
1507 | /* copy parameters into CCB for easier access */ | | 1513 | /* copy parameters into CCB for easier access */ |
1508 | ccb->xs = xs; | | 1514 | ccb->xs = xs; |
1509 | | | 1515 | |
1510 | ccb->data_in = (xs->xs_control & XS_CTL_DATA_IN) != 0; | | 1516 | ccb->data_in = (xs->xs_control & XS_CTL_DATA_IN) != 0; |
1511 | ccb->data_len = (uint32_t) xs->datalen; | | 1517 | ccb->data_len = (uint32_t) xs->datalen; |
1512 | ccb->data_ptr = xs->data; | | 1518 | ccb->data_ptr = xs->data; |
1513 | | | 1519 | |
1514 | ccb->sense_len_req = sizeof(xs->sense.scsi_sense); | | 1520 | ccb->sense_len_req = sizeof(xs->sense.scsi_sense); |
1515 | ccb->sense_ptr = &xs->sense; | | 1521 | ccb->sense_ptr = &xs->sense; |
1516 | | | 1522 | |
1517 | ccb->lun = ((uint64_t) (uint8_t) xs->xs_periph->periph_lun) << 48; | | 1523 | ccb->lun = ((uint64_t) (uint8_t) xs->xs_periph->periph_lun) << 48; |
1518 | ccb->cmd = (uint8_t *) xs->cmd; | | 1524 | ccb->cmd = (uint8_t *) xs->cmd; |
1519 | ccb->cmdlen = xs->cmdlen; | | 1525 | ccb->cmdlen = xs->cmdlen; |
1520 | DEB(10, ("RunXfer: Periph_lun = %d, cmd[1] = %x, cmdlen = %d\n", | | 1526 | DEB(10, ("RunXfer: Periph_lun = %d, cmd[1] = %x, cmdlen = %d\n", |
1521 | xs->xs_periph->periph_lun, ccb->cmd[1], xs->cmdlen)); | | 1527 | xs->xs_periph->periph_lun, ccb->cmd[1], xs->cmdlen)); |
1522 | | | 1528 | |
1523 | ccb->ITT |= xs->xs_tag_id << 24; | | 1529 | ccb->ITT |= xs->xs_tag_id << 24; |
1524 | switch (xs->xs_tag_type) { | | 1530 | switch (xs->xs_tag_type) { |
1525 | case MSG_ORDERED_Q_TAG: | | 1531 | case MSG_ORDERED_Q_TAG: |
1526 | ccb->tag = ATTR_ORDERED; | | 1532 | ccb->tag = ATTR_ORDERED; |
1527 | break; | | 1533 | break; |
1528 | case MSG_SIMPLE_Q_TAG: | | 1534 | case MSG_SIMPLE_Q_TAG: |
1529 | ccb->tag = ATTR_SIMPLE; | | 1535 | ccb->tag = ATTR_SIMPLE; |
1530 | break; | | 1536 | break; |
1531 | case MSG_HEAD_OF_Q_TAG: | | 1537 | case MSG_HEAD_OF_Q_TAG: |
1532 | ccb->tag = ATTR_HEAD_OF_QUEUE; | | 1538 | ccb->tag = ATTR_HEAD_OF_QUEUE; |
1533 | break; | | 1539 | break; |
1534 | default: | | 1540 | default: |
1535 | ccb->tag = 0; | | 1541 | ccb->tag = 0; |
1536 | break; | | 1542 | break; |
1537 | } | | 1543 | } |
1538 | | | 1544 | |
1539 | #ifdef LUN_1 | | 1545 | #ifdef LUN_1 |
1540 | ccb->lun += 0x1000000000000LL; | | 1546 | ccb->lun += 0x1000000000000LL; |
1541 | ccb->cmd[1] += 0x10; | | 1547 | ccb->cmd[1] += 0x10; |
1542 | #endif | | 1548 | #endif |
1543 | send_command(ccb, CCBDISP_SCSIPI, waitok, FALSE); | | 1549 | send_command(ccb, CCBDISP_SCSIPI, waitok, FALSE); |
1544 | } | | 1550 | } |
1545 | | | 1551 | |
1546 | | | 1552 | |
1547 | #ifndef ISCSI_MINIMAL | | 1553 | #ifndef ISCSI_MINIMAL |
1548 | /* | | 1554 | /* |
1549 | * send_io_command: | | 1555 | * send_io_command: |
1550 | * Handle a SCSI io command request from user space. | | 1556 | * Handle a SCSI io command request from user space. |
1551 | * | | 1557 | * |
1552 | * Parameter: | | 1558 | * Parameter: |
1553 | * session The session | | 1559 | * session The session |
1554 | * lun The LUN to use | | 1560 | * lun The LUN to use |
1555 | * req The SCSI request block | | 1561 | * req The SCSI request block |
1556 | * immed Immediate command if TRUE | | 1562 | * immed Immediate command if TRUE |
1557 | * conn_id Assign to this connection ID if nonzero | | 1563 | * conn_id Assign to this connection ID if nonzero |
1558 | */ | | 1564 | */ |
1559 | | | 1565 | |
1560 | int | | 1566 | int |
1561 | send_io_command(session_t *session, uint64_t lun, scsireq_t *req, | | 1567 | send_io_command(session_t *session, uint64_t lun, scsireq_t *req, |
1562 | bool immed, uint32_t conn_id) | | 1568 | bool immed, uint32_t conn_id) |
1563 | { | | 1569 | { |
1564 | ccb_t *ccb; | | 1570 | ccb_t *ccb; |
1565 | connection_t *conn; | | 1571 | connection_t *conn; |
1566 | int rc; | | 1572 | int rc; |
1567 | | | 1573 | |
1568 | DEB(9, ("IoCommand: lun=%x, datalen=%d, cmdlen=%d, immed=%d, cid=%d\n", | | 1574 | DEB(9, ("IoCommand: lun=%x, datalen=%d, cmdlen=%d, immed=%d, cid=%d\n", |
1569 | (int) lun, (int) req->datalen, (int) req->cmdlen, immed, conn_id)); | | 1575 | (int) lun, (int) req->datalen, (int) req->cmdlen, immed, conn_id)); |
1570 | | | 1576 | |
1571 | conn = (conn_id) ? find_connection(session, conn_id) | | 1577 | conn = (conn_id) ? find_connection(session, conn_id) |
1572 | : assign_connection(session, TRUE); | | 1578 | : assign_connection(session, TRUE); |
1573 | | | 1579 | |
1574 | if (conn == NULL || conn->terminating || conn->state != ST_FULL_FEATURE) { | | 1580 | if (conn == NULL || conn->terminating || conn->state != ST_FULL_FEATURE) { |
1575 | DEBOUT(("io_command on dead connection (state = %d)\n", | | 1581 | DEBOUT(("io_command on dead connection (state = %d)\n", |
1576 | (conn != NULL) ? conn->state : -1)); | | 1582 | (conn != NULL) ? conn->state : -1)); |
1577 | return ISCSI_STATUS_INVALID_CONNECTION_ID; | | 1583 | return ISCSI_STATUS_INVALID_CONNECTION_ID; |
1578 | } | | 1584 | } |
1579 | | | 1585 | |
1580 | ccb = get_ccb(conn, TRUE); | | 1586 | ccb = get_ccb(conn, TRUE); |
1581 | if (ccb == NULL) { | | 1587 | if (ccb == NULL) { |
1582 | DEBOUT(("No CCB in io_command\n")); | | 1588 | DEBOUT(("No CCB in io_command\n")); |
1583 | return ISCSI_STATUS_NO_RESOURCES; | | 1589 | return ISCSI_STATUS_NO_RESOURCES; |
1584 | } | | 1590 | } |
1585 | | | 1591 | |
1586 | ccb->data_in = (req->flags & SCCMD_READ) != 0; | | 1592 | ccb->data_in = (req->flags & SCCMD_READ) != 0; |
1587 | ccb->data_len = (uint32_t) req->datalen; | | 1593 | ccb->data_len = (uint32_t) req->datalen; |
1588 | ccb->data_ptr = req->databuf; | | 1594 | ccb->data_ptr = req->databuf; |
1589 | | | 1595 | |
1590 | ccb->sense_len_req = req->senselen; | | 1596 | ccb->sense_len_req = req->senselen; |
1591 | ccb->sense_ptr = &req->sense; | | 1597 | ccb->sense_ptr = &req->sense; |
1592 | | | 1598 | |
1593 | ccb->lun = lun; | | 1599 | ccb->lun = lun; |
1594 | ccb->cmd = (uint8_t *) req->cmd; | | 1600 | ccb->cmd = (uint8_t *) req->cmd; |
1595 | ccb->cmdlen = req->cmdlen; | | 1601 | ccb->cmdlen = req->cmdlen; |
1596 | DEBC(conn, 10, ("IoCommand: cmd[1] = %x, cmdlen = %d\n", | | 1602 | DEBC(conn, 10, ("IoCommand: cmd[1] = %x, cmdlen = %d\n", |
1597 | ccb->cmd[1], ccb->cmdlen)); | | 1603 | ccb->cmd[1], ccb->cmdlen)); |
1598 | | | 1604 | |
1599 | send_command(ccb, CCBDISP_WAIT, TRUE, immed); | | 1605 | send_command(ccb, CCBDISP_WAIT, TRUE, immed); |
1600 | | | 1606 | |
1601 | rc = ccb->status; | | 1607 | rc = ccb->status; |
1602 | | | 1608 | |
1603 | req->senselen_used = ccb->sense_len_got; | | 1609 | req->senselen_used = ccb->sense_len_got; |
1604 | req->datalen_used = req->datalen - ccb->residual; | | 1610 | req->datalen_used = req->datalen - ccb->residual; |
1605 | | | 1611 | |
1606 | free_ccb(ccb); | | 1612 | free_ccb(ccb); |
1607 | | | 1613 | |
1608 | return rc; | | 1614 | return rc; |
1609 | } | | 1615 | } |
1610 | #endif | | 1616 | #endif |
1611 | | | 1617 | |
1612 | | | 1618 | |
1613 | /***************************************************************************** | | 1619 | /***************************************************************************** |
1614 | * Timeout handlers | | 1620 | * Timeout handlers |
1615 | *****************************************************************************/ | | 1621 | *****************************************************************************/ |
1616 | /* | | 1622 | /* |
1617 | * connection_timeout: | | 1623 | * connection_timeout: |
1618 | * Handle prolonged silence on a connection by checking whether | | 1624 | * Handle prolonged silence on a connection by checking whether |
1619 | * it's still alive. | | 1625 | * it's still alive. |
1620 | * This has the side effect of discovering missing status or lost commands | | 1626 | * This has the side effect of discovering missing status or lost commands |
1621 | * before those time out. | | 1627 | * before those time out. |
1622 | * | | 1628 | * |
1623 | * Parameter: | | 1629 | * Parameter: |
1624 | * conn The connection | | 1630 | * conn The connection |
1625 | */ | | 1631 | */ |
1626 | | | 1632 | |
1627 | void | | 1633 | void |
1628 | connection_timeout(connection_t *conn) | | 1634 | connection_timeout(connection_t *conn) |
1629 | { | | 1635 | { |
1630 | | | 1636 | |
1631 | if (++conn->num_timeouts > MAX_CONN_TIMEOUTS) | | 1637 | if (++conn->num_timeouts > MAX_CONN_TIMEOUTS) |
1632 | handle_connection_error(conn, ISCSI_STATUS_TIMEOUT, NO_LOGOUT); | | 1638 | handle_connection_error(conn, ISCSI_STATUS_TIMEOUT, NO_LOGOUT); |
1633 | else { | | 1639 | else { |
1634 | if (conn->state == ST_FULL_FEATURE) | | 1640 | if (conn->state == ST_FULL_FEATURE) |
1635 | send_nop_out(conn, NULL); | | 1641 | send_nop_out(conn, NULL); |
1636 | | | 1642 | |
1637 | connection_timeout_start(conn, CONNECTION_TIMEOUT); | | 1643 | connection_timeout_start(conn, CONNECTION_TIMEOUT); |
1638 | } | | 1644 | } |
1639 | } | | 1645 | } |
1640 | | | 1646 | |
1641 | /* | | 1647 | /* |
1642 | * ccb_timeout: | | 1648 | * ccb_timeout: |
1643 | * Handle timeout of a sent command. | | 1649 | * Handle timeout of a sent command. |
1644 | * | | 1650 | * |
1645 | * Parameter: | | 1651 | * Parameter: |
1646 | * ccb The CCB | | 1652 | * ccb The CCB |
1647 | */ | | 1653 | */ |
1648 | | | 1654 | |
1649 | void | | 1655 | void |
1650 | ccb_timeout(ccb_t *ccb) | | 1656 | ccb_timeout(ccb_t *ccb) |
1651 | { | | 1657 | { |
1652 | connection_t *conn = ccb->connection; | | 1658 | connection_t *conn = ccb->connection; |
1653 | | | 1659 | |
1654 | ccb->total_tries++; | | 1660 | ccb->total_tries++; |
1655 | | | 1661 | |
1656 | DEBC(conn, 0, ("ccb_timeout: num=%d total=%d disp=%d\n", | | 1662 | DEBC(conn, 0, ("ccb_timeout: num=%d total=%d disp=%d\n", |
1657 | ccb->num_timeouts+1, ccb->total_tries, ccb->disp)); | | 1663 | ccb->num_timeouts+1, ccb->total_tries, ccb->disp)); |
1658 | | | 1664 | |
1659 | if (++ccb->num_timeouts > MAX_CCB_TIMEOUTS || | | 1665 | if (++ccb->num_timeouts > MAX_CCB_TIMEOUTS || |
1660 | ccb->total_tries > MAX_CCB_TRIES || | | 1666 | ccb->total_tries > MAX_CCB_TRIES || |
1661 | ccb->disp <= CCBDISP_FREE || | | 1667 | ccb->disp <= CCBDISP_FREE || |
1662 | !ccb->session->ErrorRecoveryLevel) { | | 1668 | !ccb->session->ErrorRecoveryLevel) { |
1663 | | | 1669 | |
1664 | wake_ccb(ccb, ISCSI_STATUS_TIMEOUT); | | 1670 | wake_ccb(ccb, ISCSI_STATUS_TIMEOUT); |
1665 | handle_connection_error(conn, ISCSI_STATUS_TIMEOUT, RECOVER_CONNECTION); | | 1671 | handle_connection_error(conn, ISCSI_STATUS_TIMEOUT, RECOVER_CONNECTION); |
1666 | } else { | | 1672 | } else { |
1667 | if (ccb->data_in && ccb->xfer_len < ccb->data_len) { | | 1673 | if (ccb->data_in && ccb->xfer_len < ccb->data_len) { |
1668 | /* request resend of all missing data */ | | 1674 | /* request resend of all missing data */ |
1669 | snack_missing(conn, ccb, SNACK_DATA_NAK, 0, 0); | | 1675 | snack_missing(conn, ccb, SNACK_DATA_NAK, 0, 0); |
1670 | } else { | | 1676 | } else { |
1671 | /* request resend of all missing status */ | | 1677 | /* request resend of all missing status */ |
1672 | snack_missing(conn, NULL, SNACK_STATUS_NAK, 0, 0); | | 1678 | snack_missing(conn, NULL, SNACK_STATUS_NAK, 0, 0); |
1673 | } | | 1679 | } |
1674 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); | | 1680 | ccb_timeout_start(ccb, COMMAND_TIMEOUT); |
1675 | } | | 1681 | } |
1676 | } | | 1682 | } |
1677 | | | 1683 | |