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#include <sys/bswap.h>
37
38
39#ifdef ISCSI_DEBUG
40
41/* debug helper routine */
42void
43dump(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
82STATIC 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
161uint32_t
162gen_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
186uint32_t
187gen_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
217ccb_t *
218get_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
260void
261free_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
299void
300create_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
332void
333wake_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
402void
403complete_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
457pdu_t *
458get_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
496pdu_t *
497get_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
532void
533free_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
569void
570create_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
595void
596init_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
625int
626add_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
678uint32_t
679ack_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