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