Home | History | Annotate | Line # | Download | only in iscsi
iscsi_utils.c revision 1.7
      1 /*	$NetBSD: iscsi_utils.c,v 1.7 2015/05/30 18:00:09 joerg 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 (at) 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 	int s;
    223 
    224 	do {
    225 		s = splbio();
    226 		ccb = TAILQ_FIRST(&sess->ccb_pool);
    227 		if (ccb != NULL)
    228 			TAILQ_REMOVE(&sess->ccb_pool, ccb, chain);
    229 		splx(s);
    230 
    231 		DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
    232 		if (ccb == NULL) {
    233 			if (!waitok || conn->terminating) {
    234 				return NULL;
    235 			}
    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 	int s;
    266 
    267 	KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
    268 	KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
    269 
    270 	ccb->connection->usecount--;
    271 	ccb->connection = NULL;
    272 
    273 	ccb->disp = CCBDISP_UNUSED;
    274 
    275 	/* free temporary data */
    276 	if (ccb->temp_data != NULL) {
    277 		free(ccb->temp_data, M_TEMP);
    278 	}
    279 	if (ccb->text_data != NULL) {
    280 		free(ccb->text_data, M_TEMP);
    281 	}
    282 	/* free PDU waiting for ACK */
    283 	if ((pdu = ccb->pdu_waiting) != NULL) {
    284 		ccb->pdu_waiting = NULL;
    285 		free_pdu(pdu);
    286 	}
    287 
    288 	s = splbio();
    289 	TAILQ_INSERT_TAIL(&sess->ccb_pool, ccb, chain);
    290 	splx(s);
    291 
    292 	wakeup(&sess->ccb_pool);
    293 }
    294 
    295 /*
    296  *    create_ccbs
    297  *       "Create" the pool of CCBs. This doesn't actually create the CCBs
    298  *       (they are allocated with the session structure), but it links them
    299  *       into the free-list.
    300  *
    301  *    Parameter:  The session owning the CCBs.
    302  */
    303 
    304 void
    305 create_ccbs(session_t *sess)
    306 {
    307 	int i;
    308 	ccb_t *ccb;
    309 	int sid = sess->id << 8;
    310 
    311 	/* Note: CCBs are initialized to 0 with connection structure */
    312 
    313 	for (i = 0, ccb = sess->ccb; i < CCBS_PER_SESSION; i++, ccb++) {
    314 		ccb->ITT = i | sid;
    315 		ccb->session = sess;
    316 
    317 		callout_init(&ccb->timeout, 0);
    318 		callout_setfunc(&ccb->timeout, ccb_timeout, ccb);
    319 
    320 		/*DEB (9, ("Create_ccbs: ccb %x itt %x\n", ccb, ccb->ITT)); */
    321 		TAILQ_INSERT_HEAD(&sess->ccb_pool, ccb, chain);
    322 	}
    323 }
    324 
    325 /*
    326  * suspend_ccb:
    327  *    Put CCB on wait queue
    328  */
    329 void
    330 suspend_ccb(ccb_t *ccb, bool yes)
    331 {
    332 	connection_t *conn;
    333 
    334 	conn = ccb->connection;
    335 	if (yes) {
    336 		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
    337 		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
    338 		TAILQ_INSERT_TAIL(&conn->ccbs_waiting, ccb, chain);
    339 		ccb->flags |= CCBF_WAITQUEUE;
    340 	} else if (ccb->flags & CCBF_WAITQUEUE) {
    341 		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
    342 		TAILQ_REMOVE(&conn->ccbs_waiting, ccb, chain);
    343 		ccb->flags &= ~CCBF_WAITQUEUE;
    344 	}
    345 }
    346 
    347 /*
    348  * throttle_ccb:
    349  *    Put CCB on throttling queue
    350  */
    351 void
    352 throttle_ccb(ccb_t *ccb, bool yes)
    353 {
    354 	session_t *sess;
    355 
    356 	sess = ccb->session;
    357 	if (yes) {
    358 		KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
    359 		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
    360 		TAILQ_INSERT_TAIL(&sess->ccbs_throttled, ccb, chain);
    361 		ccb->flags |= CCBF_THROTTLING;
    362 	} else if (ccb->flags & CCBF_THROTTLING) {
    363 		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
    364 		TAILQ_REMOVE(&sess->ccbs_throttled, ccb, chain);
    365 		ccb->flags &= ~CCBF_THROTTLING;
    366 	}
    367 }
    368 
    369 
    370 /*
    371  * wake_ccb:
    372  *    Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
    373  *    either wake up the requesting thread, signal SCSIPI that we're done,
    374  *    or just free the CCB for CCBDISP_FREE.
    375  *
    376  *    Parameter:  The CCB to handle and the new status of the CCB
    377  */
    378 
    379 void
    380 wake_ccb(ccb_t *ccb, uint32_t status)
    381 {
    382 	ccb_disp_t disp;
    383 	connection_t *conn;
    384 	int s;
    385 
    386 	conn = ccb->connection;
    387 
    388 #ifdef ISCSI_DEBUG
    389 	DEBC(conn, 9, ("CCB done, ccb = %p, disp = %d\n",
    390 		ccb, ccb->disp));
    391 #endif
    392 
    393 	callout_stop(&ccb->timeout);
    394 
    395 	s = splbio();
    396 	disp = ccb->disp;
    397 	if (disp <= CCBDISP_NOWAIT ||
    398 		(disp == CCBDISP_DEFER && conn->state <= ST_WINDING_DOWN)) {
    399 		splx(s);
    400 		return;
    401 	}
    402 
    403 	suspend_ccb(ccb, FALSE);
    404 	throttle_ccb(ccb, FALSE);
    405 
    406 	/* change the disposition so nobody tries this again */
    407 	ccb->disp = CCBDISP_BUSY;
    408 	ccb->status = status;
    409 	splx(s);
    410 
    411 	switch (disp) {
    412 	case CCBDISP_FREE:
    413 		free_ccb(ccb);
    414 		break;
    415 
    416 	case CCBDISP_WAIT:
    417 		wakeup(ccb);
    418 		break;
    419 
    420 	case CCBDISP_SCSIPI:
    421 		iscsi_done(ccb);
    422 		free_ccb(ccb);
    423 		break;
    424 
    425 	case CCBDISP_DEFER:
    426 		break;
    427 
    428 	default:
    429 		DEBC(conn, 1, ("CCB done, ccb = %p, invalid disposition %d", ccb, disp));
    430 		free_ccb(ccb);
    431 		break;
    432 	}
    433 }
    434 
    435 /*****************************************************************************
    436  * PDU management functions
    437  *****************************************************************************/
    438 
    439 /*
    440  * get_pdu:
    441  *    Get a PDU for the SCSI operation.
    442  *
    443  *    Parameter:
    444  *          conn     The connection this PDU should be associated with
    445  *          waitok   OK to wait for PDU if TRUE
    446  *
    447  *    Returns:    The PDU or NULL if none is available and waitok is FALSE.
    448  */
    449 
    450 pdu_t *
    451 get_pdu(connection_t *conn, bool waitok)
    452 {
    453 	pdu_t *pdu;
    454 	int s;
    455 
    456 	do {
    457 		s = splbio();
    458 		pdu = TAILQ_FIRST(&conn->pdu_pool);
    459 		if (pdu != NULL)
    460 			TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
    461 		splx(s);
    462 
    463 		DEB(100, ("get_pdu_c: pdu = %p, waitok = %d\n", pdu, waitok));
    464 		if (pdu == NULL) {
    465 			if (!waitok || conn->terminating)
    466 				return NULL;
    467 			tsleep(&conn->pdu_pool, PWAIT, "get_pdu_c", 0);
    468 		}
    469 	} while (pdu == NULL);
    470 
    471 	memset(pdu, 0, sizeof(pdu_t));
    472 	pdu->connection = conn;
    473 	pdu->disp = PDUDISP_FREE;
    474 
    475 	return pdu;
    476 }
    477 
    478 /*
    479  * free_pdu:
    480  *    Put a PDU back onto the free list.
    481  *
    482  *    Parameter:  The PDU.
    483  */
    484 
    485 void
    486 free_pdu(pdu_t *pdu)
    487 {
    488 	connection_t *conn = pdu->connection;
    489 	pdu_disp_t pdisp;
    490 	int s;
    491 
    492 	if (PDUDISP_UNUSED == (pdisp = pdu->disp))
    493 		return;
    494 	pdu->disp = PDUDISP_UNUSED;
    495 
    496 	if (pdu->flags & PDUF_INQUEUE) {
    497 		TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
    498 		pdu->flags &= ~PDUF_INQUEUE;
    499 	}
    500 
    501 	if (pdisp == PDUDISP_SIGNAL)
    502 		wakeup(pdu);
    503 
    504 	/* free temporary data in this PDU */
    505 	if (pdu->temp_data)
    506 		free(pdu->temp_data, M_TEMP);
    507 
    508 	s = splbio();
    509 	TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
    510 	splx(s);
    511 
    512 	wakeup(&conn->pdu_pool);
    513 }
    514 
    515 /*
    516  *    create_pdus
    517  *       "Create" the pool of PDUs. This doesn't actually create the PDUs
    518  *       (they are allocated with the connection structure), but it links them
    519  *       into the free-list.
    520  *
    521  *    Parameter:  The connection owning the PDUs.
    522  */
    523 
    524 void
    525 create_pdus(connection_t *conn)
    526 {
    527 	int i;
    528 	pdu_t *pdu;
    529 
    530 	/* Note: PDUs are initialized to 0 with connection structure */
    531 
    532 	for (i = 0, pdu = conn->pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
    533 		TAILQ_INSERT_HEAD(&conn->pdu_pool, pdu, chain);
    534 	}
    535 }
    536 
    537 
    538 /*****************************************************************************
    539  * Serial Number management functions
    540  *****************************************************************************/
    541 
    542 /*
    543  * init_sernum:
    544  *    Initialize serial number buffer variables.
    545  *
    546  *    Parameter:
    547  *          buff   The serial number buffer.
    548  */
    549 
    550 void
    551 init_sernum(sernum_buffer_t *buff)
    552 {
    553 
    554 	buff->bottom = 0;
    555 	buff->top = 0;
    556 	buff->next_sn = 0;
    557 	buff->ExpSN = 0;
    558 }
    559 
    560 
    561 /*
    562  * add_sernum:
    563  *    Add a received serial number to the buffer.
    564  *    If the serial number is smaller than the expected one, it is ignored.
    565  *    If it is larger, all missing serial numbers are added as well.
    566  *
    567  *    Parameter:
    568  *          buff   The serial number buffer.
    569  *          num   The received serial number
    570  *
    571  *    Returns:
    572  *          0     if the received block is a duplicate
    573  *          1     if the number is the expected one
    574  *          >1    if the numer is > the expected value, in this case the
    575  *                return value is the number of unacknowledged blocks
    576  *          <0    if the buffer is full (i.e. an excessive number of blocks
    577  *                is unacknowledged)
    578  */
    579 
    580 int
    581 add_sernum(sernum_buffer_t *buff, uint32_t num)
    582 {
    583 	int i, t, b;
    584 	uint32_t n;
    585 	int32_t diff;
    586 
    587 	/*
    588 	 * next_sn is the next expected SN, so normally diff should be 1.
    589 	 */
    590 	n = buff->next_sn;
    591 	diff = (num - n) + 1;
    592 
    593 	if (diff <= 0) {
    594 		return 0;				/* ignore if SN is smaller than expected (dup or retransmit) */
    595 	}
    596 
    597 	buff->next_sn = num + 1;
    598 	t = buff->top;
    599 	b = buff->bottom;
    600 
    601 	for (i = 0; i < diff; i++) {
    602 		buff->sernum[t] = n++;
    603 		buff->ack[t] = 0;
    604 		t = (t + 1) % SERNUM_BUFFER_LENGTH;
    605 		if (t == b) {
    606 			DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff));
    607 			return -1;
    608 		}
    609 	}
    610 
    611 	buff->top = t;
    612 	DEB(10, ("AddSernum bottom %d [%d], top %d, num %u, diff %d\n",
    613 			 b, buff->sernum[b], buff->top, num, diff));
    614 
    615 	return diff;
    616 }
    617 
    618 
    619 /*
    620  * ack_sernum:
    621  *    Mark a received serial number as acknowledged. This does not necessarily
    622  *    change the associated ExpSN if there are lower serial numbers in the
    623  *    buffer.
    624  *
    625  *    Parameter:
    626  *          buff   The serial number buffer.
    627  *          num   The serial number to acknowledge.
    628  *
    629  *    Returns:    The value of ExpSN.
    630  */
    631 
    632 uint32_t
    633 ack_sernum(sernum_buffer_t *buff, uint32_t num)
    634 {
    635 	int b = buff->bottom;
    636 	int t = buff->top;
    637 
    638 	/* shortcut for most likely case */
    639 	if (t == (b + 1) && num == buff->sernum[b]) {
    640 		/* buffer is now empty, reset top */
    641 		buff->top = b;
    642 	} else if (b != t) {
    643 		for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
    644 			if (!sn_a_lt_b(buff->sernum[b], num))
    645 				break;
    646 		}
    647 		if (num == buff->sernum[b]) {
    648 			if (b == buff->bottom)
    649 				buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH;
    650 			else
    651 				buff->ack[b] = 1;
    652 		}
    653 
    654 		for (b = buff->bottom, num = buff->sernum[b] - 1;
    655 			 b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
    656 			num = buff->sernum[b];
    657 		}
    658 	}
    659 
    660 	if (!sn_a_lt_b(num, buff->ExpSN))
    661 		buff->ExpSN = num + 1;
    662 
    663 	DEB(10, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n",
    664 			 buff->bottom, buff->top, num, buff->ExpSN));
    665 
    666 	return buff->ExpSN;
    667 }
    668