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