1/*	$NetBSD: iscsi_utils.c,v 1.29 2023/11/25 10:08:27 mlelstv Exp $	*/
2
3/*-
4 * Copyright (c) 2004,2005,2006,2008 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/systm.h>
34#include <sys/buf.h>
35#include <sys/socketvar.h>
36#include <sys/bswap.h>
37#include <sys/atomic.h>
38
39
40/*****************************************************************************
41 * Digest functions
42 *****************************************************************************/
43
44/*****************************************************************
45 *
46 * CRC LOOKUP TABLE
47 * ================
48 * The following CRC lookup table was generated automagically
49 * by the Rocksoft^tm Model CRC Algorithm Table Generation
50 * Program V1.0 using the following model parameters:
51 *
52 *    Width   : 4 bytes.
53 *    Poly    : 0x1EDC6F41L
54 *    Reverse : TRUE.
55 *
56 * For more information on the Rocksoft^tm Model CRC Algorithm,
57 * see the document titled "A Painless Guide to CRC Error
58 * Detection Algorithms" by Ross Williams
59 * (ross@guest.adelaide.edu.au.). This document is likely to be
60 * in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".
61 *
62 *****************************************************************/
63
64STATIC uint32_t crc_table[256] = {
65	0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
66	0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
67	0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
68	0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
69	0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
70	0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
71	0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
72	0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
73	0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
74	0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
75	0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
76	0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
77	0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
78	0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
79	0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
80	0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
81	0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
82	0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
83	0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
84	0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
85	0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
86	0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
87	0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
88	0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
89	0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
90	0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
91	0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
92	0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
93	0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
94	0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
95	0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
96	0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
97	0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
98	0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
99	0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
100	0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
101	0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
102	0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
103	0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
104	0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
105	0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
106	0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
107	0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
108	0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
109	0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
110	0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
111	0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
112	0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
113	0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
114	0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
115	0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
116	0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
117	0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
118	0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
119	0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
120	0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
121	0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
122	0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
123	0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
124	0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
125	0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
126	0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
127	0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
128	0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
129};
130
131
132/*
133 * gen_digest:
134 *    Generate an iSCSI CRC32C digest over the given data.
135 *
136 *    Parameters:
137 *          buff   The data
138 *          len   The length of the data in bytes
139 *
140 *    Returns:    The digest in network byte order
141 */
142
143uint32_t
144gen_digest(const void *buff, size_t len)
145{
146	const uint8_t *bp = (const uint8_t *) buff;
147	uint32_t crc = 0xffffffff;
148
149	while (len--) {
150		crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
151	}
152	return htonl(bswap32(crc ^ 0xffffffff));
153}
154
155
156/*
157 * gen_digest_2:
158 *    Generate an iSCSI CRC32C digest over the given data, which is split over
159 *    two buffers.
160 *
161 *    Parameters:
162 *          buf1, buf2  The data
163 *          len1, len2  The length of the data in bytes
164 *
165 *    Returns:    The digest in network byte order
166 */
167
168uint32_t
169gen_digest_2(const void *buf1, size_t len1, const void *buf2, size_t len2)
170{
171	const uint8_t *bp = (const uint8_t *) buf1;
172	uint32_t crc = 0xffffffff;
173
174	while (len1--) {
175		crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
176	}
177	bp = (const uint8_t *) buf2;
178	while (len2--) {
179		crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
180	}
181	return htonl(bswap32(crc ^ 0xffffffff));
182}
183
184/*****************************************************************************
185 * CCB management functions
186 *****************************************************************************/
187
188/*
189 * get_ccb:
190 *    Get a CCB for the SCSI operation, waiting if none is available.
191 *
192 *    Parameter:
193 *       sess     The session containing this CCB
194 *       waitok   Whether waiting for a CCB is OK
195 *
196 *    Returns:    The CCB.
197 */
198
199ccb_t *
200get_ccb(connection_t *conn, bool waitok)
201{
202	ccb_t *ccb;
203	session_t *sess = conn->c_session;
204
205	mutex_enter(&sess->s_lock);
206	for (;;) {
207		ccb = TAILQ_FIRST(&sess->s_ccb_pool);
208
209		DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
210
211		if (ccb != NULL) {
212			TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain);
213			break;
214		}
215
216		if (!waitok)
217			break;
218
219		cv_wait(&sess->s_ccb_cv, &sess->s_lock);
220	}
221	mutex_exit(&sess->s_lock);
222
223	if (ccb == NULL) {
224		DEB(15, ("get_ccb: failed"));
225		return NULL;
226	}
227
228	ccb->ccb_flags = 0;
229	ccb->ccb_timedout = TOUT_NONE;
230	ccb->ccb_xs = NULL;
231	ccb->ccb_temp_data = NULL;
232	ccb->ccb_text_data = NULL;
233	ccb->ccb_status = ISCSI_STATUS_SUCCESS;
234	ccb->ccb_ITT = (ccb->ccb_ITT & 0xffffff);
235	ccb->ccb_disp = CCBDISP_NOWAIT;
236	ccb->ccb_connection = conn;
237	ccb->ccb_num_timeouts = 0;
238	mutex_enter(&conn->c_lock);
239	conn->c_usecount++;
240	mutex_exit(&conn->c_lock);
241
242	DEBC(conn, 15, (
243		"get_ccb: ccb = %p, usecount = %d\n",
244		ccb, conn->c_usecount));
245
246	return ccb;
247}
248
249/*
250 * free_ccb:
251 *    Put a CCB back onto the free list.
252 *
253 *    Parameter:  The CCB.
254 */
255
256void
257free_ccb(ccb_t *ccb)
258{
259	session_t *sess = ccb->ccb_session;
260	connection_t *conn = ccb->ccb_connection;
261	pdu_t *pdu;
262
263	DEBC(conn, 15, (
264		"free_ccb: ccb = %p, usecount = %d\n",
265		ccb, conn->c_usecount-1));
266
267	KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
268
269	ccb->ccb_connection = NULL;
270	mutex_enter(&conn->c_lock);
271	conn->c_usecount--;
272	mutex_exit(&conn->c_lock);
273
274	if (ccb->ccb_disp > CCBDISP_NOWAIT) {
275		DEBOUT(("Freeing CCB with disp %d\n",ccb->ccb_disp));
276	}
277
278	ccb->ccb_disp = CCBDISP_UNUSED;
279
280	/* free temporary data */
281	if (ccb->ccb_temp_data != NULL) {
282		free(ccb->ccb_temp_data, M_TEMP);
283	}
284	if (ccb->ccb_text_data != NULL) {
285		free(ccb->ccb_text_data, M_TEMP);
286	}
287	/* free PDU waiting for ACK */
288	if ((pdu = ccb->ccb_pdu_waiting) != NULL) {
289		ccb->ccb_pdu_waiting = NULL;
290		mutex_enter(&conn->c_lock);
291		if ((pdu->pdu_flags & PDUF_INQUEUE) != 0) {
292			TAILQ_REMOVE(&conn->c_pdus_to_send, pdu, pdu_send_chain);
293			pdu->pdu_flags &= ~PDUF_INQUEUE;
294		}
295		mutex_exit(&conn->c_lock);
296		free_pdu(pdu);
297	}
298
299	mutex_enter(&sess->s_lock);
300	TAILQ_INSERT_TAIL(&sess->s_ccb_pool, ccb, ccb_chain);
301	cv_broadcast(&sess->s_ccb_cv);
302	mutex_exit(&sess->s_lock);
303}
304
305/*
306 *    create_ccbs
307 *       "Create" the pool of CCBs. This doesn't actually create the CCBs
308 *       (they are allocated with the session structure), but it links them
309 *       into the free-list.
310 *
311 *    Parameter:  The session owning the CCBs.
312 */
313
314void
315create_ccbs(session_t *sess)
316{
317	int i;
318	ccb_t *ccb;
319	int sid = sess->s_id << 8;
320
321	/* Note: CCBs are initialized to 0 with connection structure */
322
323	for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) {
324		ccb->ccb_ITT = i | sid;
325		ccb->ccb_session = sess;
326
327		callout_init(&ccb->ccb_timeout, CALLOUT_MPSAFE);
328		callout_setfunc(&ccb->ccb_timeout, ccb_timeout_co, ccb);
329
330		DEB(9, ("Create_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT));
331		TAILQ_INSERT_HEAD(&sess->s_ccb_pool, ccb, ccb_chain);
332	}
333}
334
335/*
336 *    destroy_ccbs
337 *       Kill the callouts
338 *
339 *    Parameter:  The session owning the CCBs.
340 */
341
342void
343destroy_ccbs(session_t *sess)
344{
345	int i;
346	ccb_t *ccb;
347
348	/* Note: CCBs are initialized to 0 with connection structure */
349
350	for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) {
351
352		callout_halt(&ccb->ccb_timeout, NULL);
353		callout_destroy(&ccb->ccb_timeout);
354
355		DEB(9, ("destroy_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT));
356		KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
357		KASSERT(ccb->ccb_disp == CCBDISP_UNUSED);
358		KASSERT(ccb->ccb_connection == NULL);
359		TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain);
360	}
361}
362
363/*
364 * suspend_ccb:
365 *    Put CCB on wait queue
366 */
367void
368suspend_ccb(ccb_t *ccb, bool yes)
369{
370	connection_t *conn;
371
372	conn = ccb->ccb_connection;
373	KASSERT(conn != NULL);
374
375	KASSERT(mutex_owned(&conn->c_lock));
376
377	if (yes) {
378		KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
379		TAILQ_INSERT_TAIL(&conn->c_ccbs_waiting, ccb, ccb_chain);
380		ccb->ccb_flags |= CCBF_WAITQUEUE;
381	} else if (ccb->ccb_flags & CCBF_WAITQUEUE) {
382		TAILQ_REMOVE(&conn->c_ccbs_waiting, ccb, ccb_chain);
383		ccb->ccb_flags &= ~CCBF_WAITQUEUE;
384	}
385}
386
387/*
388 * wake_ccb:
389 *    Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
390 *    either wake up the requesting thread, signal SCSIPI that we're done,
391 *    or just free the CCB for CCBDISP_FREE.
392 *
393 *    Parameter:  The CCB to handle and the new status of the CCB
394 */
395
396void
397wake_ccb(ccb_t *ccb, uint32_t status)
398{
399	ccb_disp_t disp;
400	connection_t *conn;
401
402	conn = ccb->ccb_connection;
403	KASSERT(conn != NULL);
404
405	DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n",
406		ccb->ccb_CmdSN, ccb, ccb->ccb_disp));
407
408	ccb_timeout_stop(ccb);
409
410	mutex_enter(&conn->c_lock);
411	disp = ccb->ccb_disp;
412	if (disp <= CCBDISP_NOWAIT ||
413		(disp == CCBDISP_DEFER && conn->c_state <= ST_WINDING_DOWN)) {
414		mutex_exit(&conn->c_lock);
415		return;
416	}
417
418	suspend_ccb(ccb, FALSE);
419
420	/* change the disposition so nobody tries this again */
421	ccb->ccb_disp = CCBDISP_BUSY;
422	ccb->ccb_status = status;
423
424	if (disp == CCBDISP_WAIT)
425		cv_broadcast(&conn->c_ccb_cv);
426	mutex_exit(&conn->c_lock);
427
428	switch(disp) {
429	case CCBDISP_WAIT:
430	case CCBDISP_DEFER:
431		break;
432
433	case CCBDISP_SCSIPI:
434		iscsi_done(ccb);
435		/* FALLTHROUGH */
436	case CCBDISP_FREE:
437		free_ccb(ccb);
438		break;
439	default:
440		DEBC(conn, 1, ("CCB done, ccb = %p, invalid disposition %d", ccb, disp));
441		free_ccb(ccb);
442		break;
443	}
444}
445
446/*****************************************************************************
447 * PDU management functions
448 *****************************************************************************/
449
450/*
451 * get_pdu:
452 *    Get a PDU for the SCSI operation.
453 *
454 *    Parameter:
455 *          conn     The connection this PDU should be associated with
456 *          waitok   OK to wait for PDU if TRUE
457 *
458 *    Returns:    The PDU or NULL if none is available and waitok is FALSE.
459 */
460
461pdu_t *
462get_pdu(connection_t *conn, bool waitok)
463{
464	pdu_t *pdu;
465
466	mutex_enter(&conn->c_lock);
467	for (;;) {
468		pdu = TAILQ_FIRST(&conn->c_pdu_pool);
469
470		if (pdu != NULL) {
471			TAILQ_REMOVE(&conn->c_pdu_pool, pdu, pdu_chain);
472			conn->c_pducount++;
473			break;
474		}
475
476		if (!waitok)
477			break;
478
479		cv_wait(&conn->c_pdu_cv, &conn->c_lock);
480	}
481	mutex_exit(&conn->c_lock);
482
483	if (pdu == NULL) {
484		DEB(15, ("get_pdu: failed"));
485		return NULL;
486	}
487
488	memset(pdu, 0, sizeof(pdu_t));
489	pdu->pdu_connection = conn;
490	pdu->pdu_disp = PDUDISP_FREE;
491
492	DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount));
493
494	return pdu;
495}
496
497/*
498 * free_pdu:
499 *    Put a PDU back onto the free list.
500 *
501 *    Parameter:  The PDU.
502 */
503
504void
505free_pdu(pdu_t *pdu)
506{
507	connection_t *conn = pdu->pdu_connection;
508	pdu_disp_t pdisp;
509
510	DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount-1));
511
512	KASSERT((pdu->pdu_flags & PDUF_INQUEUE) == 0);
513
514	if (PDUDISP_UNUSED == (pdisp = pdu->pdu_disp)) {
515		DEBC(conn, 0, ("freeing UNUSED pdu\n"));
516		return;
517	}
518
519	pdu->pdu_disp = PDUDISP_UNUSED;
520
521	/* free temporary data in this PDU */
522	if (pdu->pdu_temp_data)
523		free(pdu->pdu_temp_data, M_TEMP);
524
525	mutex_enter(&conn->c_lock);
526	conn->c_pducount--;
527	TAILQ_INSERT_TAIL(&conn->c_pdu_pool, pdu, pdu_chain);
528	cv_broadcast(&conn->c_pdu_cv);
529	mutex_exit(&conn->c_lock);
530}
531
532/*
533 *    create_pdus
534 *       "Create" the pool of PDUs. This doesn't actually create the PDUs
535 *       (they are allocated with the connection structure), but it links them
536 *       into the free-list.
537 *
538 *    Parameter:  The connection owning the PDUs.
539 */
540
541void
542create_pdus(connection_t *conn)
543{
544	int i;
545	pdu_t *pdu;
546
547	/* Note: PDUs are initialized to 0 with connection structure */
548
549	for (i = 0, pdu = conn->c_pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
550		TAILQ_INSERT_HEAD(&conn->c_pdu_pool, pdu, pdu_chain);
551	}
552}
553
554
555/*****************************************************************************
556 * Serial Number management functions
557 *****************************************************************************/
558
559/*
560 * init_sernum:
561 *    Initialize serial number buffer variables.
562 *
563 *    Parameter:
564 *          buff   The serial number buffer.
565 */
566
567void
568init_sernum(sernum_buffer_t *buff)
569{
570
571	buff->bottom = 0;
572	buff->top = 0;
573	buff->next_sn = 0;
574	buff->ExpSN = 0;
575}
576
577
578/*
579 * add_sernum:
580 *    Add a received serial number to the buffer.
581 *    If the serial number is smaller than the expected one, it is ignored.
582 *    If it is larger, all missing serial numbers are added as well.
583 *
584 *    Parameter:
585 *          buff   The serial number buffer.
586 *          num   The received serial number
587 *
588 *    Returns:
589 *          0     if the received block is a duplicate
590 *          1     if the number is the expected one
591 *          >1    if the number is > the expected value, in this case the
592 *                return value is the number of unacknowledged blocks
593 *          <0    if the buffer is full (i.e. an excessive number of blocks
594 *                is unacknowledged)
595 */
596
597int
598add_sernum(sernum_buffer_t *buff, uint32_t num)
599{
600	int i, t, b;
601	uint32_t n;
602	int32_t diff;
603
604	/*
605	 * next_sn is the next expected SN, so normally diff should be 1.
606	 */
607	n = buff->next_sn;
608	diff = (num - n) + 1;
609
610	if (diff <= 0) {
611		return 0;				/* ignore if SN is smaller than expected (dup or retransmit) */
612	}
613
614	buff->next_sn = num + 1;
615	t = buff->top;
616	b = buff->bottom;
617
618	for (i = 0; i < diff; i++) {
619		buff->sernum[t] = n++;
620		buff->ack[t] = false;
621		t = (t + 1) % SERNUM_BUFFER_LENGTH;
622		if (t == b) {
623			DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff));
624			return -1;
625		}
626	}
627
628	buff->top = t;
629	DEB(11, ("AddSernum bottom %d [%d], top %d, num %u, diff %d\n",
630			 b, buff->sernum[b], buff->top, num, diff));
631
632	return diff;
633}
634
635
636/*
637 * ack_sernum:
638 *    Mark a received serial number as acknowledged. This does not necessarily
639 *    change the associated ExpSN if there are lower serial numbers in the
640 *    buffer.
641 *
642 *    Parameter:
643 *          buff   The serial number buffer.
644 *          num   The serial number to acknowledge.
645 *
646 *    Returns:    The value of ExpSN.
647 */
648
649uint32_t
650ack_sernum(sernum_buffer_t *buff, uint32_t num)
651{
652	int b = buff->bottom;
653	int t = buff->top;
654
655	/* shortcut for most likely case */
656	if (t == (b + 1) && num == buff->sernum[b]) {
657		/* buffer is now empty, reset top */
658		buff->top = b;
659	} else if (b != t) {
660		for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
661			if (!sn_a_lt_b(buff->sernum[b], num))
662				break;
663		}
664		if (num == buff->sernum[b]) {
665			if (b == buff->bottom)
666				buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH;
667			else
668				buff->ack[b] = true;
669		}
670
671		for (b = buff->bottom, num = buff->sernum[b] - 1;
672			 b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
673			num = buff->sernum[b];
674		}
675	}
676
677	if (!sn_a_lt_b(num, buff->ExpSN))
678		buff->ExpSN = num + 1;
679
680	DEB(11, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n",
681			 buff->bottom, buff->top, num, buff->ExpSN));
682
683	return buff->ExpSN;
684}
685
686/*
687 * next_sernum:
688 *   Return the current command serial number of the session
689 *   and optionally increment it for the next query
690 */
691uint32_t
692get_sernum(session_t *sess, pdu_t *pdu)
693{
694	uint32_t sn;
695
696	KASSERT(mutex_owned(&sess->s_lock));
697
698	sn = sess->s_CmdSN;
699	if ((pdu->pdu_hdr.pduh_Opcode & OP_IMMEDIATE) == 0)
700		atomic_inc_32(&sess->s_CmdSN);
701	return sn;
702}
703
704/*
705 * sernum_in_window:
706 *   Check whether serial number is in send window
707 *
708 */
709int
710sernum_in_window(session_t *sess)
711{
712
713	KASSERT(mutex_owned(&sess->s_lock));
714	return sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN);
715}
716
717/*
718 * window_size:
719 *    Compute send window size
720 */
721int
722window_size(session_t *sess, int limit)
723{
724	uint32_t win;
725
726	KASSERT(mutex_owned(&sess->s_lock));
727
728	win = 0;
729	if (sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN))
730		win = sess->s_MaxCmdSN - sess->s_CmdSN + 1;
731	if (win > INT_MAX || win > limit)
732		win = limit;
733
734	return win;
735}
736