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