xref: /netbsd-src/sys/dev/iscsi/iscsi_utils.c (revision 4ccc61761f963414f959565e9aa29a7e438bd848)
1*4ccc6176Smlelstv /*	$NetBSD: iscsi_utils.c,v 1.29 2023/11/25 10:08:27 mlelstv Exp $	*/
2e01460c0Sagc 
3e01460c0Sagc /*-
4e01460c0Sagc  * Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
5e01460c0Sagc  * All rights reserved.
6e01460c0Sagc  *
7e01460c0Sagc  * This code is derived from software contributed to The NetBSD Foundation
8e01460c0Sagc  * by Wasabi Systems, Inc.
9e01460c0Sagc  *
10e01460c0Sagc  * Redistribution and use in source and binary forms, with or without
11e01460c0Sagc  * modification, are permitted provided that the following conditions
12e01460c0Sagc  * are met:
13e01460c0Sagc  * 1. Redistributions of source code must retain the above copyright
14e01460c0Sagc  *    notice, this list of conditions and the following disclaimer.
15e01460c0Sagc  * 2. Redistributions in binary form must reproduce the above copyright
16e01460c0Sagc  *    notice, this list of conditions and the following disclaimer in the
17e01460c0Sagc  *    documentation and/or other materials provided with the distribution.
18e01460c0Sagc  *
19e01460c0Sagc  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20e01460c0Sagc  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21e01460c0Sagc  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22e01460c0Sagc  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23e01460c0Sagc  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24e01460c0Sagc  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e01460c0Sagc  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e01460c0Sagc  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27e01460c0Sagc  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e01460c0Sagc  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29e01460c0Sagc  * POSSIBILITY OF SUCH DAMAGE.
30e01460c0Sagc  */
31e01460c0Sagc #include "iscsi_globals.h"
32e01460c0Sagc 
33e01460c0Sagc #include <sys/systm.h>
34e01460c0Sagc #include <sys/buf.h>
35e01460c0Sagc #include <sys/socketvar.h>
36001d5cd7Smlelstv #include <sys/bswap.h>
37996d5208Smlelstv #include <sys/atomic.h>
38e01460c0Sagc 
39e01460c0Sagc 
40e01460c0Sagc /*****************************************************************************
41e01460c0Sagc  * Digest functions
42e01460c0Sagc  *****************************************************************************/
43e01460c0Sagc 
44e01460c0Sagc /*****************************************************************
45e01460c0Sagc  *
46e01460c0Sagc  * CRC LOOKUP TABLE
47e01460c0Sagc  * ================
48e01460c0Sagc  * The following CRC lookup table was generated automagically
49e01460c0Sagc  * by the Rocksoft^tm Model CRC Algorithm Table Generation
50e01460c0Sagc  * Program V1.0 using the following model parameters:
51e01460c0Sagc  *
52e01460c0Sagc  *    Width   : 4 bytes.
53e01460c0Sagc  *    Poly    : 0x1EDC6F41L
54e01460c0Sagc  *    Reverse : TRUE.
55e01460c0Sagc  *
56e01460c0Sagc  * For more information on the Rocksoft^tm Model CRC Algorithm,
57e01460c0Sagc  * see the document titled "A Painless Guide to CRC Error
58e01460c0Sagc  * Detection Algorithms" by Ross Williams
59e01460c0Sagc  * (ross@guest.adelaide.edu.au.). This document is likely to be
60e01460c0Sagc  * in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".
61e01460c0Sagc  *
62e01460c0Sagc  *****************************************************************/
63e01460c0Sagc 
64e01460c0Sagc STATIC uint32_t crc_table[256] = {
65e01460c0Sagc 	0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
66e01460c0Sagc 	0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
67e01460c0Sagc 	0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
68e01460c0Sagc 	0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
69e01460c0Sagc 	0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
70e01460c0Sagc 	0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
71e01460c0Sagc 	0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
72e01460c0Sagc 	0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
73e01460c0Sagc 	0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
74e01460c0Sagc 	0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
75e01460c0Sagc 	0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
76e01460c0Sagc 	0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
77e01460c0Sagc 	0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
78e01460c0Sagc 	0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
79e01460c0Sagc 	0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
80e01460c0Sagc 	0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
81e01460c0Sagc 	0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
82e01460c0Sagc 	0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
83e01460c0Sagc 	0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
84e01460c0Sagc 	0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
85e01460c0Sagc 	0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
86e01460c0Sagc 	0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
87e01460c0Sagc 	0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
88e01460c0Sagc 	0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
89e01460c0Sagc 	0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
90e01460c0Sagc 	0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
91e01460c0Sagc 	0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
92e01460c0Sagc 	0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
93e01460c0Sagc 	0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
94e01460c0Sagc 	0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
95e01460c0Sagc 	0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
96e01460c0Sagc 	0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
97e01460c0Sagc 	0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
98e01460c0Sagc 	0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
99e01460c0Sagc 	0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
100e01460c0Sagc 	0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
101e01460c0Sagc 	0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
102e01460c0Sagc 	0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
103e01460c0Sagc 	0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
104e01460c0Sagc 	0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
105e01460c0Sagc 	0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
106e01460c0Sagc 	0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
107e01460c0Sagc 	0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
108e01460c0Sagc 	0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
109e01460c0Sagc 	0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
110e01460c0Sagc 	0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
111e01460c0Sagc 	0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
112e01460c0Sagc 	0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
113e01460c0Sagc 	0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
114e01460c0Sagc 	0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
115e01460c0Sagc 	0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
116e01460c0Sagc 	0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
117e01460c0Sagc 	0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
118e01460c0Sagc 	0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
119e01460c0Sagc 	0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
120e01460c0Sagc 	0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
121e01460c0Sagc 	0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
122e01460c0Sagc 	0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
123e01460c0Sagc 	0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
124e01460c0Sagc 	0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
125e01460c0Sagc 	0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
126e01460c0Sagc 	0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
127e01460c0Sagc 	0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
128e01460c0Sagc 	0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
129e01460c0Sagc };
130e01460c0Sagc 
131e01460c0Sagc 
132e01460c0Sagc /*
133e01460c0Sagc  * gen_digest:
134e01460c0Sagc  *    Generate an iSCSI CRC32C digest over the given data.
135e01460c0Sagc  *
136e01460c0Sagc  *    Parameters:
137e01460c0Sagc  *          buff   The data
138e01460c0Sagc  *          len   The length of the data in bytes
139e01460c0Sagc  *
140e01460c0Sagc  *    Returns:    The digest in network byte order
141e01460c0Sagc  */
142e01460c0Sagc 
143e01460c0Sagc uint32_t
gen_digest(const void * buff,size_t len)1448ab41cb9Schristos gen_digest(const void *buff, size_t len)
145e01460c0Sagc {
1468ab41cb9Schristos 	const uint8_t *bp = (const uint8_t *) buff;
147e01460c0Sagc 	uint32_t crc = 0xffffffff;
148e01460c0Sagc 
149e01460c0Sagc 	while (len--) {
150e01460c0Sagc 		crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
151e01460c0Sagc 	}
152001d5cd7Smlelstv 	return htonl(bswap32(crc ^ 0xffffffff));
153e01460c0Sagc }
154e01460c0Sagc 
155e01460c0Sagc 
156e01460c0Sagc /*
157e01460c0Sagc  * gen_digest_2:
158e01460c0Sagc  *    Generate an iSCSI CRC32C digest over the given data, which is split over
159e01460c0Sagc  *    two buffers.
160e01460c0Sagc  *
161e01460c0Sagc  *    Parameters:
162e01460c0Sagc  *          buf1, buf2  The data
163e01460c0Sagc  *          len1, len2  The length of the data in bytes
164e01460c0Sagc  *
165e01460c0Sagc  *    Returns:    The digest in network byte order
166e01460c0Sagc  */
167e01460c0Sagc 
168e01460c0Sagc uint32_t
gen_digest_2(const void * buf1,size_t len1,const void * buf2,size_t len2)1698ab41cb9Schristos gen_digest_2(const void *buf1, size_t len1, const void *buf2, size_t len2)
170e01460c0Sagc {
1718ab41cb9Schristos 	const uint8_t *bp = (const uint8_t *) buf1;
172e01460c0Sagc 	uint32_t crc = 0xffffffff;
173e01460c0Sagc 
174e01460c0Sagc 	while (len1--) {
175e01460c0Sagc 		crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
176e01460c0Sagc 	}
1778ab41cb9Schristos 	bp = (const uint8_t *) buf2;
178e01460c0Sagc 	while (len2--) {
179e01460c0Sagc 		crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
180e01460c0Sagc 	}
181001d5cd7Smlelstv 	return htonl(bswap32(crc ^ 0xffffffff));
182e01460c0Sagc }
183e01460c0Sagc 
184e01460c0Sagc /*****************************************************************************
185e01460c0Sagc  * CCB management functions
186e01460c0Sagc  *****************************************************************************/
187e01460c0Sagc 
188e01460c0Sagc /*
189e01460c0Sagc  * get_ccb:
190e01460c0Sagc  *    Get a CCB for the SCSI operation, waiting if none is available.
191e01460c0Sagc  *
192e01460c0Sagc  *    Parameter:
193e01460c0Sagc  *       sess     The session containing this CCB
194e01460c0Sagc  *       waitok   Whether waiting for a CCB is OK
195e01460c0Sagc  *
196e01460c0Sagc  *    Returns:    The CCB.
197e01460c0Sagc  */
198e01460c0Sagc 
199e01460c0Sagc ccb_t *
get_ccb(connection_t * conn,bool waitok)200e01460c0Sagc get_ccb(connection_t *conn, bool waitok)
201e01460c0Sagc {
202e01460c0Sagc 	ccb_t *ccb;
2038ab41cb9Schristos 	session_t *sess = conn->c_session;
204e01460c0Sagc 
2058ab41cb9Schristos 	mutex_enter(&sess->s_lock);
2061ec2b09aSmlelstv 	for (;;) {
2078ab41cb9Schristos 		ccb = TAILQ_FIRST(&sess->s_ccb_pool);
2081ec2b09aSmlelstv 
209e01460c0Sagc 		DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
210996d5208Smlelstv 
211996d5208Smlelstv 		if (ccb != NULL) {
2128ab41cb9Schristos 			TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain);
2131ec2b09aSmlelstv 			break;
214e01460c0Sagc 		}
2151ec2b09aSmlelstv 
2161ec2b09aSmlelstv 		if (!waitok)
2171ec2b09aSmlelstv 			break;
2181ec2b09aSmlelstv 
2198ab41cb9Schristos 		cv_wait(&sess->s_ccb_cv, &sess->s_lock);
220e01460c0Sagc 	}
2218ab41cb9Schristos 	mutex_exit(&sess->s_lock);
222e01460c0Sagc 
2231ec2b09aSmlelstv 	if (ccb == NULL) {
2241ec2b09aSmlelstv 		DEB(15, ("get_ccb: failed"));
2251ec2b09aSmlelstv 		return NULL;
2261ec2b09aSmlelstv 	}
2271ec2b09aSmlelstv 
2288ab41cb9Schristos 	ccb->ccb_flags = 0;
2298ab41cb9Schristos 	ccb->ccb_timedout = TOUT_NONE;
2308ab41cb9Schristos 	ccb->ccb_xs = NULL;
2318ab41cb9Schristos 	ccb->ccb_temp_data = NULL;
2328ab41cb9Schristos 	ccb->ccb_text_data = NULL;
2338ab41cb9Schristos 	ccb->ccb_status = ISCSI_STATUS_SUCCESS;
2348ab41cb9Schristos 	ccb->ccb_ITT = (ccb->ccb_ITT & 0xffffff);
2358ab41cb9Schristos 	ccb->ccb_disp = CCBDISP_NOWAIT;
2368ab41cb9Schristos 	ccb->ccb_connection = conn;
2378ab41cb9Schristos 	ccb->ccb_num_timeouts = 0;
238*4ccc6176Smlelstv 	mutex_enter(&conn->c_lock);
239*4ccc6176Smlelstv 	conn->c_usecount++;
240*4ccc6176Smlelstv 	mutex_exit(&conn->c_lock);
241996d5208Smlelstv 
242fb85db77Smlelstv 	DEBC(conn, 15, (
243996d5208Smlelstv 		"get_ccb: ccb = %p, usecount = %d\n",
2448ab41cb9Schristos 		ccb, conn->c_usecount));
245e01460c0Sagc 
246e01460c0Sagc 	return ccb;
247e01460c0Sagc }
248e01460c0Sagc 
249e01460c0Sagc /*
250e01460c0Sagc  * free_ccb:
251e01460c0Sagc  *    Put a CCB back onto the free list.
252e01460c0Sagc  *
253e01460c0Sagc  *    Parameter:  The CCB.
254e01460c0Sagc  */
255e01460c0Sagc 
256e01460c0Sagc void
free_ccb(ccb_t * ccb)257e01460c0Sagc free_ccb(ccb_t *ccb)
258e01460c0Sagc {
2598ab41cb9Schristos 	session_t *sess = ccb->ccb_session;
2608ab41cb9Schristos 	connection_t *conn = ccb->ccb_connection;
261e01460c0Sagc 	pdu_t *pdu;
262996d5208Smlelstv 
2633e271162Smlelstv 	DEBC(conn, 15, (
264996d5208Smlelstv 		"free_ccb: ccb = %p, usecount = %d\n",
2658ab41cb9Schristos 		ccb, conn->c_usecount-1));
26621dddfceSmlelstv 
2678ab41cb9Schristos 	KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
268e01460c0Sagc 
2698ab41cb9Schristos 	ccb->ccb_connection = NULL;
270*4ccc6176Smlelstv 	mutex_enter(&conn->c_lock);
271*4ccc6176Smlelstv 	conn->c_usecount--;
272*4ccc6176Smlelstv 	mutex_exit(&conn->c_lock);
273d1c48dffSmlelstv 
2748ab41cb9Schristos 	if (ccb->ccb_disp > CCBDISP_NOWAIT) {
2758ab41cb9Schristos 		DEBOUT(("Freeing CCB with disp %d\n",ccb->ccb_disp));
276996d5208Smlelstv 	}
277996d5208Smlelstv 
2788ab41cb9Schristos 	ccb->ccb_disp = CCBDISP_UNUSED;
279e01460c0Sagc 
280e01460c0Sagc 	/* free temporary data */
2818ab41cb9Schristos 	if (ccb->ccb_temp_data != NULL) {
2828ab41cb9Schristos 		free(ccb->ccb_temp_data, M_TEMP);
283e01460c0Sagc 	}
2848ab41cb9Schristos 	if (ccb->ccb_text_data != NULL) {
2858ab41cb9Schristos 		free(ccb->ccb_text_data, M_TEMP);
286e01460c0Sagc 	}
287e01460c0Sagc 	/* free PDU waiting for ACK */
2888ab41cb9Schristos 	if ((pdu = ccb->ccb_pdu_waiting) != NULL) {
2898ab41cb9Schristos 		ccb->ccb_pdu_waiting = NULL;
2908ab41cb9Schristos 		mutex_enter(&conn->c_lock);
2918ab41cb9Schristos 		if ((pdu->pdu_flags & PDUF_INQUEUE) != 0) {
2928ab41cb9Schristos 			TAILQ_REMOVE(&conn->c_pdus_to_send, pdu, pdu_send_chain);
2938ab41cb9Schristos 			pdu->pdu_flags &= ~PDUF_INQUEUE;
2943e271162Smlelstv 		}
2958ab41cb9Schristos 		mutex_exit(&conn->c_lock);
296e01460c0Sagc 		free_pdu(pdu);
297e01460c0Sagc 	}
298e01460c0Sagc 
2998ab41cb9Schristos 	mutex_enter(&sess->s_lock);
3008ab41cb9Schristos 	TAILQ_INSERT_TAIL(&sess->s_ccb_pool, ccb, ccb_chain);
3018ab41cb9Schristos 	cv_broadcast(&sess->s_ccb_cv);
3028ab41cb9Schristos 	mutex_exit(&sess->s_lock);
303e01460c0Sagc }
304e01460c0Sagc 
305e01460c0Sagc /*
306e01460c0Sagc  *    create_ccbs
307e01460c0Sagc  *       "Create" the pool of CCBs. This doesn't actually create the CCBs
308e01460c0Sagc  *       (they are allocated with the session structure), but it links them
309e01460c0Sagc  *       into the free-list.
310e01460c0Sagc  *
311e01460c0Sagc  *    Parameter:  The session owning the CCBs.
312e01460c0Sagc  */
313e01460c0Sagc 
314e01460c0Sagc void
create_ccbs(session_t * sess)315e01460c0Sagc create_ccbs(session_t *sess)
316e01460c0Sagc {
317e01460c0Sagc 	int i;
318e01460c0Sagc 	ccb_t *ccb;
3198ab41cb9Schristos 	int sid = sess->s_id << 8;
320e01460c0Sagc 
321e01460c0Sagc 	/* Note: CCBs are initialized to 0 with connection structure */
322e01460c0Sagc 
3238ab41cb9Schristos 	for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) {
3248ab41cb9Schristos 		ccb->ccb_ITT = i | sid;
3258ab41cb9Schristos 		ccb->ccb_session = sess;
326e01460c0Sagc 
3278ab41cb9Schristos 		callout_init(&ccb->ccb_timeout, CALLOUT_MPSAFE);
3288ab41cb9Schristos 		callout_setfunc(&ccb->ccb_timeout, ccb_timeout_co, ccb);
329e01460c0Sagc 
3308ab41cb9Schristos 		DEB(9, ("Create_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT));
3318ab41cb9Schristos 		TAILQ_INSERT_HEAD(&sess->s_ccb_pool, ccb, ccb_chain);
332e01460c0Sagc 	}
333e01460c0Sagc }
334e01460c0Sagc 
33521dddfceSmlelstv /*
33661e95c24Smlelstv  *    destroy_ccbs
33761e95c24Smlelstv  *       Kill the callouts
33861e95c24Smlelstv  *
33961e95c24Smlelstv  *    Parameter:  The session owning the CCBs.
34061e95c24Smlelstv  */
34161e95c24Smlelstv 
34261e95c24Smlelstv void
destroy_ccbs(session_t * sess)34361e95c24Smlelstv destroy_ccbs(session_t *sess)
34461e95c24Smlelstv {
34561e95c24Smlelstv 	int i;
34661e95c24Smlelstv 	ccb_t *ccb;
34761e95c24Smlelstv 
34861e95c24Smlelstv 	/* Note: CCBs are initialized to 0 with connection structure */
34961e95c24Smlelstv 
3508ab41cb9Schristos 	for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) {
35161e95c24Smlelstv 
3528ab41cb9Schristos 		callout_halt(&ccb->ccb_timeout, NULL);
3538ab41cb9Schristos 		callout_destroy(&ccb->ccb_timeout);
35461e95c24Smlelstv 
3558ab41cb9Schristos 		DEB(9, ("destroy_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT));
3568ab41cb9Schristos 		KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
3578ab41cb9Schristos 		KASSERT(ccb->ccb_disp == CCBDISP_UNUSED);
3588ab41cb9Schristos 		KASSERT(ccb->ccb_connection == NULL);
3598ab41cb9Schristos 		TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain);
36061e95c24Smlelstv 	}
36161e95c24Smlelstv }
36261e95c24Smlelstv 
36361e95c24Smlelstv /*
36421dddfceSmlelstv  * suspend_ccb:
36521dddfceSmlelstv  *    Put CCB on wait queue
36621dddfceSmlelstv  */
36721dddfceSmlelstv void
suspend_ccb(ccb_t * ccb,bool yes)36821dddfceSmlelstv suspend_ccb(ccb_t *ccb, bool yes)
36921dddfceSmlelstv {
37021dddfceSmlelstv 	connection_t *conn;
37121dddfceSmlelstv 
3728ab41cb9Schristos 	conn = ccb->ccb_connection;
373015f574cSmlelstv 	KASSERT(conn != NULL);
374bbe94f43Smlelstv 
3758ab41cb9Schristos 	KASSERT(mutex_owned(&conn->c_lock));
376bbe94f43Smlelstv 
37721dddfceSmlelstv 	if (yes) {
3788ab41cb9Schristos 		KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
3798ab41cb9Schristos 		TAILQ_INSERT_TAIL(&conn->c_ccbs_waiting, ccb, ccb_chain);
3808ab41cb9Schristos 		ccb->ccb_flags |= CCBF_WAITQUEUE;
3818ab41cb9Schristos 	} else if (ccb->ccb_flags & CCBF_WAITQUEUE) {
3828ab41cb9Schristos 		TAILQ_REMOVE(&conn->c_ccbs_waiting, ccb, ccb_chain);
3838ab41cb9Schristos 		ccb->ccb_flags &= ~CCBF_WAITQUEUE;
38421dddfceSmlelstv 	}
38521dddfceSmlelstv }
38621dddfceSmlelstv 
38721dddfceSmlelstv /*
388e01460c0Sagc  * wake_ccb:
389e01460c0Sagc  *    Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
390e01460c0Sagc  *    either wake up the requesting thread, signal SCSIPI that we're done,
391e01460c0Sagc  *    or just free the CCB for CCBDISP_FREE.
392e01460c0Sagc  *
39321dddfceSmlelstv  *    Parameter:  The CCB to handle and the new status of the CCB
394e01460c0Sagc  */
395e01460c0Sagc 
396e01460c0Sagc void
wake_ccb(ccb_t * ccb,uint32_t status)39721dddfceSmlelstv wake_ccb(ccb_t *ccb, uint32_t status)
398e01460c0Sagc {
399e01460c0Sagc 	ccb_disp_t disp;
400e01460c0Sagc 	connection_t *conn;
401e01460c0Sagc 
4028ab41cb9Schristos 	conn = ccb->ccb_connection;
403015f574cSmlelstv 	KASSERT(conn != NULL);
404e01460c0Sagc 
405bbe94f43Smlelstv 	DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n",
4068ab41cb9Schristos 		ccb->ccb_CmdSN, ccb, ccb->ccb_disp));
407e01460c0Sagc 
4085762a611Smlelstv 	ccb_timeout_stop(ccb);
409e01460c0Sagc 
4108ab41cb9Schristos 	mutex_enter(&conn->c_lock);
4118ab41cb9Schristos 	disp = ccb->ccb_disp;
412e01460c0Sagc 	if (disp <= CCBDISP_NOWAIT ||
4138ab41cb9Schristos 		(disp == CCBDISP_DEFER && conn->c_state <= ST_WINDING_DOWN)) {
4148ab41cb9Schristos 		mutex_exit(&conn->c_lock);
415e01460c0Sagc 		return;
416e01460c0Sagc 	}
417e01460c0Sagc 
41821dddfceSmlelstv 	suspend_ccb(ccb, FALSE);
419e01460c0Sagc 
420e01460c0Sagc 	/* change the disposition so nobody tries this again */
4218ab41cb9Schristos 	ccb->ccb_disp = CCBDISP_BUSY;
4228ab41cb9Schristos 	ccb->ccb_status = status;
423015f574cSmlelstv 
424015f574cSmlelstv 	if (disp == CCBDISP_WAIT)
4258ab41cb9Schristos 		cv_broadcast(&conn->c_ccb_cv);
4268ab41cb9Schristos 	mutex_exit(&conn->c_lock);
427e01460c0Sagc 
428e01460c0Sagc 	switch(disp) {
429e01460c0Sagc 	case CCBDISP_WAIT:
430015f574cSmlelstv 	case CCBDISP_DEFER:
431e01460c0Sagc 		break;
432e01460c0Sagc 
433e01460c0Sagc 	case CCBDISP_SCSIPI:
434e01460c0Sagc 		iscsi_done(ccb);
435cdf28a30Smrg 		/* FALLTHROUGH */
436015f574cSmlelstv 	case CCBDISP_FREE:
43721dddfceSmlelstv 		free_ccb(ccb);
438e01460c0Sagc 		break;
439e01460c0Sagc 	default:
44021dddfceSmlelstv 		DEBC(conn, 1, ("CCB done, ccb = %p, invalid disposition %d", ccb, disp));
441e01460c0Sagc 		free_ccb(ccb);
442e01460c0Sagc 		break;
443e01460c0Sagc 	}
444e01460c0Sagc }
445e01460c0Sagc 
446e01460c0Sagc /*****************************************************************************
447e01460c0Sagc  * PDU management functions
448e01460c0Sagc  *****************************************************************************/
449e01460c0Sagc 
450e01460c0Sagc /*
45121dddfceSmlelstv  * get_pdu:
452e01460c0Sagc  *    Get a PDU for the SCSI operation.
453e01460c0Sagc  *
454e01460c0Sagc  *    Parameter:
455e01460c0Sagc  *          conn     The connection this PDU should be associated with
456e01460c0Sagc  *          waitok   OK to wait for PDU if TRUE
457e01460c0Sagc  *
458e01460c0Sagc  *    Returns:    The PDU or NULL if none is available and waitok is FALSE.
459e01460c0Sagc  */
460e01460c0Sagc 
461e01460c0Sagc pdu_t *
get_pdu(connection_t * conn,bool waitok)46221dddfceSmlelstv get_pdu(connection_t *conn, bool waitok)
463e01460c0Sagc {
464e01460c0Sagc 	pdu_t *pdu;
465e01460c0Sagc 
4668ab41cb9Schristos 	mutex_enter(&conn->c_lock);
4671ec2b09aSmlelstv 	for (;;) {
4688ab41cb9Schristos 		pdu = TAILQ_FIRST(&conn->c_pdu_pool);
4691ec2b09aSmlelstv 
4701ec2b09aSmlelstv 		if (pdu != NULL) {
4718ab41cb9Schristos 			TAILQ_REMOVE(&conn->c_pdu_pool, pdu, pdu_chain);
4721ec2b09aSmlelstv 			conn->c_pducount++;
4731ec2b09aSmlelstv 			break;
4741ec2b09aSmlelstv 		}
4751ec2b09aSmlelstv 
4761ec2b09aSmlelstv 		if (!waitok)
4771ec2b09aSmlelstv 			break;
4781ec2b09aSmlelstv 
4791ec2b09aSmlelstv 		cv_wait(&conn->c_pdu_cv, &conn->c_lock);
4801ec2b09aSmlelstv 	}
4811ec2b09aSmlelstv 	mutex_exit(&conn->c_lock);
48221dddfceSmlelstv 
483e01460c0Sagc 	if (pdu == NULL) {
484bbe94f43Smlelstv 		DEB(15, ("get_pdu: failed"));
485e01460c0Sagc 		return NULL;
486996d5208Smlelstv 	}
487e01460c0Sagc 
488e01460c0Sagc 	memset(pdu, 0, sizeof(pdu_t));
4898ab41cb9Schristos 	pdu->pdu_connection = conn;
4908ab41cb9Schristos 	pdu->pdu_disp = PDUDISP_FREE;
491e01460c0Sagc 
4928ab41cb9Schristos 	DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount));
493bbe94f43Smlelstv 
494e01460c0Sagc 	return pdu;
495e01460c0Sagc }
496e01460c0Sagc 
497e01460c0Sagc /*
498e01460c0Sagc  * free_pdu:
499e01460c0Sagc  *    Put a PDU back onto the free list.
500e01460c0Sagc  *
501e01460c0Sagc  *    Parameter:  The PDU.
502e01460c0Sagc  */
503e01460c0Sagc 
504e01460c0Sagc void
free_pdu(pdu_t * pdu)505e01460c0Sagc free_pdu(pdu_t *pdu)
506e01460c0Sagc {
5078ab41cb9Schristos 	connection_t *conn = pdu->pdu_connection;
508e01460c0Sagc 	pdu_disp_t pdisp;
509e01460c0Sagc 
5108ab41cb9Schristos 	DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount-1));
511bbe94f43Smlelstv 
5128ab41cb9Schristos 	KASSERT((pdu->pdu_flags & PDUF_INQUEUE) == 0);
513aac72e4bSmlelstv 
5141ec2b09aSmlelstv 	if (PDUDISP_UNUSED == (pdisp = pdu->pdu_disp)) {
5151ec2b09aSmlelstv 		DEBC(conn, 0, ("freeing UNUSED pdu\n"));
516e01460c0Sagc 		return;
5171ec2b09aSmlelstv 	}
5181ec2b09aSmlelstv 
5198ab41cb9Schristos 	pdu->pdu_disp = PDUDISP_UNUSED;
520e01460c0Sagc 
521e01460c0Sagc 	/* free temporary data in this PDU */
5228ab41cb9Schristos 	if (pdu->pdu_temp_data)
5238ab41cb9Schristos 		free(pdu->pdu_temp_data, M_TEMP);
524e01460c0Sagc 
5258ab41cb9Schristos 	mutex_enter(&conn->c_lock);
5261ec2b09aSmlelstv 	conn->c_pducount--;
5278ab41cb9Schristos 	TAILQ_INSERT_TAIL(&conn->c_pdu_pool, pdu, pdu_chain);
5288ab41cb9Schristos 	cv_broadcast(&conn->c_pdu_cv);
5298ab41cb9Schristos 	mutex_exit(&conn->c_lock);
530e01460c0Sagc }
531e01460c0Sagc 
532e01460c0Sagc /*
533e01460c0Sagc  *    create_pdus
534e01460c0Sagc  *       "Create" the pool of PDUs. This doesn't actually create the PDUs
535e01460c0Sagc  *       (they are allocated with the connection structure), but it links them
536e01460c0Sagc  *       into the free-list.
537e01460c0Sagc  *
538e01460c0Sagc  *    Parameter:  The connection owning the PDUs.
539e01460c0Sagc  */
540e01460c0Sagc 
541e01460c0Sagc void
create_pdus(connection_t * conn)542e01460c0Sagc create_pdus(connection_t *conn)
543e01460c0Sagc {
544e01460c0Sagc 	int i;
545e01460c0Sagc 	pdu_t *pdu;
546e01460c0Sagc 
547e01460c0Sagc 	/* Note: PDUs are initialized to 0 with connection structure */
548e01460c0Sagc 
5498ab41cb9Schristos 	for (i = 0, pdu = conn->c_pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
5508ab41cb9Schristos 		TAILQ_INSERT_HEAD(&conn->c_pdu_pool, pdu, pdu_chain);
551e01460c0Sagc 	}
552e01460c0Sagc }
553e01460c0Sagc 
554e01460c0Sagc 
555e01460c0Sagc /*****************************************************************************
556e01460c0Sagc  * Serial Number management functions
557e01460c0Sagc  *****************************************************************************/
558e01460c0Sagc 
559e01460c0Sagc /*
560e01460c0Sagc  * init_sernum:
561e01460c0Sagc  *    Initialize serial number buffer variables.
562e01460c0Sagc  *
563e01460c0Sagc  *    Parameter:
564e01460c0Sagc  *          buff   The serial number buffer.
565e01460c0Sagc  */
566e01460c0Sagc 
567e01460c0Sagc void
init_sernum(sernum_buffer_t * buff)568e01460c0Sagc init_sernum(sernum_buffer_t *buff)
569e01460c0Sagc {
570e01460c0Sagc 
571e01460c0Sagc 	buff->bottom = 0;
572e01460c0Sagc 	buff->top = 0;
573e01460c0Sagc 	buff->next_sn = 0;
574e01460c0Sagc 	buff->ExpSN = 0;
575e01460c0Sagc }
576e01460c0Sagc 
577e01460c0Sagc 
578e01460c0Sagc /*
579e01460c0Sagc  * add_sernum:
580e01460c0Sagc  *    Add a received serial number to the buffer.
581e01460c0Sagc  *    If the serial number is smaller than the expected one, it is ignored.
582e01460c0Sagc  *    If it is larger, all missing serial numbers are added as well.
583e01460c0Sagc  *
584e01460c0Sagc  *    Parameter:
585e01460c0Sagc  *          buff   The serial number buffer.
586e01460c0Sagc  *          num   The received serial number
587e01460c0Sagc  *
588e01460c0Sagc  *    Returns:
589e01460c0Sagc  *          0     if the received block is a duplicate
590e01460c0Sagc  *          1     if the number is the expected one
59125629ef4Smsaitoh  *          >1    if the number is > the expected value, in this case the
592e01460c0Sagc  *                return value is the number of unacknowledged blocks
593e01460c0Sagc  *          <0    if the buffer is full (i.e. an excessive number of blocks
594e01460c0Sagc  *                is unacknowledged)
595e01460c0Sagc  */
596e01460c0Sagc 
597e01460c0Sagc int
add_sernum(sernum_buffer_t * buff,uint32_t num)598e01460c0Sagc add_sernum(sernum_buffer_t *buff, uint32_t num)
599e01460c0Sagc {
600da501d22Smlelstv 	int i, t, b;
601da501d22Smlelstv 	uint32_t n;
602da501d22Smlelstv 	int32_t diff;
603e01460c0Sagc 
604e01460c0Sagc 	/*
605e01460c0Sagc 	 * next_sn is the next expected SN, so normally diff should be 1.
606e01460c0Sagc 	 */
607e01460c0Sagc 	n = buff->next_sn;
608e01460c0Sagc 	diff = (num - n) + 1;
609e01460c0Sagc 
610e01460c0Sagc 	if (diff <= 0) {
611e01460c0Sagc 		return 0;				/* ignore if SN is smaller than expected (dup or retransmit) */
612e01460c0Sagc 	}
613e01460c0Sagc 
614e01460c0Sagc 	buff->next_sn = num + 1;
615e01460c0Sagc 	t = buff->top;
616e01460c0Sagc 	b = buff->bottom;
617e01460c0Sagc 
618e01460c0Sagc 	for (i = 0; i < diff; i++) {
619e01460c0Sagc 		buff->sernum[t] = n++;
62071697517Sjoerg 		buff->ack[t] = false;
621e01460c0Sagc 		t = (t + 1) % SERNUM_BUFFER_LENGTH;
622e01460c0Sagc 		if (t == b) {
623e01460c0Sagc 			DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff));
624e01460c0Sagc 			return -1;
625e01460c0Sagc 		}
626e01460c0Sagc 	}
627e01460c0Sagc 
628e01460c0Sagc 	buff->top = t;
629fb85db77Smlelstv 	DEB(11, ("AddSernum bottom %d [%d], top %d, num %u, diff %d\n",
630e01460c0Sagc 			 b, buff->sernum[b], buff->top, num, diff));
631e01460c0Sagc 
632e01460c0Sagc 	return diff;
633e01460c0Sagc }
634e01460c0Sagc 
635e01460c0Sagc 
636e01460c0Sagc /*
637e01460c0Sagc  * ack_sernum:
638e01460c0Sagc  *    Mark a received serial number as acknowledged. This does not necessarily
639e01460c0Sagc  *    change the associated ExpSN if there are lower serial numbers in the
640e01460c0Sagc  *    buffer.
641e01460c0Sagc  *
642e01460c0Sagc  *    Parameter:
643e01460c0Sagc  *          buff   The serial number buffer.
644e01460c0Sagc  *          num   The serial number to acknowledge.
645e01460c0Sagc  *
646e01460c0Sagc  *    Returns:    The value of ExpSN.
647e01460c0Sagc  */
648e01460c0Sagc 
649e01460c0Sagc uint32_t
ack_sernum(sernum_buffer_t * buff,uint32_t num)650e01460c0Sagc ack_sernum(sernum_buffer_t *buff, uint32_t num)
651e01460c0Sagc {
652e01460c0Sagc 	int b = buff->bottom;
653e01460c0Sagc 	int t = buff->top;
654e01460c0Sagc 
655e01460c0Sagc 	/* shortcut for most likely case */
656e01460c0Sagc 	if (t == (b + 1) && num == buff->sernum[b]) {
657e01460c0Sagc 		/* buffer is now empty, reset top */
658e01460c0Sagc 		buff->top = b;
659e01460c0Sagc 	} else if (b != t) {
660e01460c0Sagc 		for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
661e01460c0Sagc 			if (!sn_a_lt_b(buff->sernum[b], num))
662e01460c0Sagc 				break;
663e01460c0Sagc 		}
664e01460c0Sagc 		if (num == buff->sernum[b]) {
665e01460c0Sagc 			if (b == buff->bottom)
666e01460c0Sagc 				buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH;
667e01460c0Sagc 			else
66871697517Sjoerg 				buff->ack[b] = true;
669e01460c0Sagc 		}
670e01460c0Sagc 
671e01460c0Sagc 		for (b = buff->bottom, num = buff->sernum[b] - 1;
672e01460c0Sagc 			 b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
673e01460c0Sagc 			num = buff->sernum[b];
674e01460c0Sagc 		}
675e01460c0Sagc 	}
676e01460c0Sagc 
677e01460c0Sagc 	if (!sn_a_lt_b(num, buff->ExpSN))
678e01460c0Sagc 		buff->ExpSN = num + 1;
679e01460c0Sagc 
680fb85db77Smlelstv 	DEB(11, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n",
681e01460c0Sagc 			 buff->bottom, buff->top, num, buff->ExpSN));
682e01460c0Sagc 
683e01460c0Sagc 	return buff->ExpSN;
684e01460c0Sagc }
68562dba08eSmlelstv 
68662dba08eSmlelstv /*
68762dba08eSmlelstv  * next_sernum:
68862dba08eSmlelstv  *   Return the current command serial number of the session
68962dba08eSmlelstv  *   and optionally increment it for the next query
69062dba08eSmlelstv  */
69162dba08eSmlelstv uint32_t
get_sernum(session_t * sess,pdu_t * pdu)692bbe94f43Smlelstv get_sernum(session_t *sess, pdu_t *pdu)
69362dba08eSmlelstv {
69462dba08eSmlelstv 	uint32_t sn;
69562dba08eSmlelstv 
6968ab41cb9Schristos 	KASSERT(mutex_owned(&sess->s_lock));
69762dba08eSmlelstv 
6988ab41cb9Schristos 	sn = sess->s_CmdSN;
6998ab41cb9Schristos 	if ((pdu->pdu_hdr.pduh_Opcode & OP_IMMEDIATE) == 0)
7008ab41cb9Schristos 		atomic_inc_32(&sess->s_CmdSN);
70162dba08eSmlelstv 	return sn;
70262dba08eSmlelstv }
70362dba08eSmlelstv 
70462dba08eSmlelstv /*
70562dba08eSmlelstv  * sernum_in_window:
706f93d6526Smaya  *   Check whether serial number is in send window
70762dba08eSmlelstv  *
70862dba08eSmlelstv  */
70962dba08eSmlelstv int
sernum_in_window(session_t * sess)71062dba08eSmlelstv sernum_in_window(session_t *sess)
71162dba08eSmlelstv {
71262dba08eSmlelstv 
7138ab41cb9Schristos 	KASSERT(mutex_owned(&sess->s_lock));
7148ab41cb9Schristos 	return sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN);
71562dba08eSmlelstv }
71662dba08eSmlelstv 
717bbe94f43Smlelstv /*
718bbe94f43Smlelstv  * window_size:
719bbe94f43Smlelstv  *    Compute send window size
720bbe94f43Smlelstv  */
721bbe94f43Smlelstv int
window_size(session_t * sess,int limit)722bbe94f43Smlelstv window_size(session_t *sess, int limit)
723bbe94f43Smlelstv {
724bbe94f43Smlelstv 	uint32_t win;
725bbe94f43Smlelstv 
7268ab41cb9Schristos 	KASSERT(mutex_owned(&sess->s_lock));
727bbe94f43Smlelstv 
728bbe94f43Smlelstv 	win = 0;
7298ab41cb9Schristos 	if (sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN))
7308ab41cb9Schristos 		win = sess->s_MaxCmdSN - sess->s_CmdSN + 1;
731bbe94f43Smlelstv 	if (win > INT_MAX || win > limit)
732bbe94f43Smlelstv 		win = limit;
733bbe94f43Smlelstv 
734bbe94f43Smlelstv 	return win;
735bbe94f43Smlelstv }
736