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