Home | History | Annotate | Line # | Download | only in iscsi
iscsi_utils.c revision 1.22
      1 /*	$NetBSD: iscsi_utils.c,v 1.22 2016/12/25 06:55:28 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 (at) guest.adelaide.edu.au.). This document is likely to be
     60  * in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".
     61  *
     62  *****************************************************************/
     63 
     64 STATIC 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 
    143 uint32_t
    144 gen_digest(void *buff, int len)
    145 {
    146 	uint8_t *bp = (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 
    168 uint32_t
    169 gen_digest_2(void *buf1, int len1, void *buf2, int len2)
    170 {
    171 	uint8_t *bp = (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 = (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 
    199 ccb_t *
    200 get_ccb(connection_t *conn, bool waitok)
    201 {
    202 	ccb_t *ccb;
    203 	session_t *sess = conn->session;
    204 
    205 	mutex_enter(&sess->lock);
    206 	do {
    207 		ccb = TAILQ_FIRST(&sess->ccb_pool);
    208 		DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
    209 
    210 		if (ccb != NULL) {
    211 			TAILQ_REMOVE(&sess->ccb_pool, ccb, chain);
    212 		} else {
    213 			if (!waitok || conn->terminating) {
    214 				mutex_exit(&sess->lock);
    215 				return NULL;
    216 			}
    217 			cv_wait(&sess->ccb_cv, &sess->lock);
    218 		}
    219 	} while (ccb == NULL);
    220 	mutex_exit(&sess->lock);
    221 
    222 	ccb->flags = 0;
    223 	ccb->timedout = TOUT_NONE;
    224 	ccb->xs = NULL;
    225 	ccb->temp_data = NULL;
    226 	ccb->text_data = NULL;
    227 	ccb->status = ISCSI_STATUS_SUCCESS;
    228 	ccb->ITT = (ccb->ITT & 0xffffff);
    229 	ccb->disp = CCBDISP_NOWAIT;
    230 	ccb->connection = conn;
    231 	ccb->num_timeouts = 0;
    232 	atomic_inc_uint(&conn->usecount);
    233 
    234 	DEBC(conn, 15, (
    235 		"get_ccb: ccb = %p, usecount = %d\n",
    236 		ccb, conn->usecount));
    237 
    238 	return ccb;
    239 }
    240 
    241 /*
    242  * free_ccb:
    243  *    Put a CCB back onto the free list.
    244  *
    245  *    Parameter:  The CCB.
    246  */
    247 
    248 void
    249 free_ccb(ccb_t *ccb)
    250 {
    251 	session_t *sess = ccb->session;
    252 	connection_t *conn = ccb->connection;
    253 	pdu_t *pdu;
    254 
    255 	DEBC(conn, 15, (
    256 		"free_ccb: ccb = %p, usecount = %d\n",
    257 		ccb, conn->usecount-1));
    258 
    259 	KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
    260 
    261 	atomic_dec_uint(&conn->usecount);
    262 	ccb->connection = NULL;
    263 
    264 	if (ccb->disp > CCBDISP_NOWAIT) {
    265 		DEBOUT(("Freeing CCB with disp %d\n",ccb->disp));
    266 	}
    267 
    268 	ccb->disp = CCBDISP_UNUSED;
    269 
    270 	/* free temporary data */
    271 	if (ccb->temp_data != NULL) {
    272 		free(ccb->temp_data, M_TEMP);
    273 	}
    274 	if (ccb->text_data != NULL) {
    275 		free(ccb->text_data, M_TEMP);
    276 	}
    277 	/* free PDU waiting for ACK */
    278 	if ((pdu = ccb->pdu_waiting) != NULL) {
    279 		ccb->pdu_waiting = NULL;
    280 		mutex_enter(&conn->lock);
    281 		if ((pdu->flags & PDUF_INQUEUE) != 0) {
    282 			TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
    283 			pdu->flags &= ~PDUF_INQUEUE;
    284 		}
    285 		mutex_exit(&conn->lock);
    286 		free_pdu(pdu);
    287 	}
    288 
    289 	mutex_enter(&sess->lock);
    290 	TAILQ_INSERT_TAIL(&sess->ccb_pool, ccb, chain);
    291 	cv_broadcast(&sess->ccb_cv);
    292 	mutex_exit(&sess->lock);
    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, CALLOUT_MPSAFE);
    318 		callout_setfunc(&ccb->timeout, ccb_timeout_co, ccb);
    319 
    320 		DEB(9, ("Create_ccbs: ccb %p 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 	KASSERT(conn != NULL);
    336 
    337 	KASSERT(mutex_owned(&conn->lock));
    338 
    339 	if (yes) {
    340 		KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
    341 		TAILQ_INSERT_TAIL(&conn->ccbs_waiting, ccb, chain);
    342 		ccb->flags |= CCBF_WAITQUEUE;
    343 	} else if (ccb->flags & CCBF_WAITQUEUE) {
    344 		TAILQ_REMOVE(&conn->ccbs_waiting, ccb, chain);
    345 		ccb->flags &= ~CCBF_WAITQUEUE;
    346 	}
    347 }
    348 
    349 /*
    350  * wake_ccb:
    351  *    Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
    352  *    either wake up the requesting thread, signal SCSIPI that we're done,
    353  *    or just free the CCB for CCBDISP_FREE.
    354  *
    355  *    Parameter:  The CCB to handle and the new status of the CCB
    356  */
    357 
    358 void
    359 wake_ccb(ccb_t *ccb, uint32_t status)
    360 {
    361 	ccb_disp_t disp;
    362 	connection_t *conn;
    363 
    364 	conn = ccb->connection;
    365 	KASSERT(conn != NULL);
    366 
    367 	DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n",
    368 		ccb->CmdSN, ccb, ccb->disp));
    369 
    370 	ccb_timeout_stop(ccb);
    371 
    372 	mutex_enter(&conn->lock);
    373 	disp = ccb->disp;
    374 	if (disp <= CCBDISP_NOWAIT ||
    375 		(disp == CCBDISP_DEFER && conn->state <= ST_WINDING_DOWN)) {
    376 		mutex_exit(&conn->lock);
    377 		return;
    378 	}
    379 
    380 	suspend_ccb(ccb, FALSE);
    381 
    382 	/* change the disposition so nobody tries this again */
    383 	ccb->disp = CCBDISP_BUSY;
    384 	ccb->status = status;
    385 
    386 	if (disp == CCBDISP_WAIT)
    387 		cv_broadcast(&conn->ccb_cv);
    388 	mutex_exit(&conn->lock);
    389 
    390 	switch(disp) {
    391 	case CCBDISP_WAIT:
    392 	case CCBDISP_DEFER:
    393 		break;
    394 
    395 	case CCBDISP_SCSIPI:
    396 		iscsi_done(ccb);
    397 		/* FALLTRHOUGH */
    398 	case CCBDISP_FREE:
    399 		free_ccb(ccb);
    400 		break;
    401 	default:
    402 		DEBC(conn, 1, ("CCB done, ccb = %p, invalid disposition %d", ccb, disp));
    403 		free_ccb(ccb);
    404 		break;
    405 	}
    406 }
    407 
    408 /*****************************************************************************
    409  * PDU management functions
    410  *****************************************************************************/
    411 
    412 /*
    413  * get_pdu:
    414  *    Get a PDU for the SCSI operation.
    415  *
    416  *    Parameter:
    417  *          conn     The connection this PDU should be associated with
    418  *          waitok   OK to wait for PDU if TRUE
    419  *
    420  *    Returns:    The PDU or NULL if none is available and waitok is FALSE.
    421  */
    422 
    423 pdu_t *
    424 get_pdu(connection_t *conn, bool waitok)
    425 {
    426 	pdu_t *pdu;
    427 
    428 	mutex_enter(&conn->lock);
    429 	do {
    430 		pdu = TAILQ_FIRST(&conn->pdu_pool);
    431 		if (pdu != NULL)
    432 			TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
    433 
    434 		if (pdu == NULL) {
    435 			if (!waitok || conn->terminating) {
    436 				mutex_exit(&conn->lock);
    437 				DEB(15, ("get_pdu: failed"));
    438 				return NULL;
    439 			}
    440 			cv_wait(&conn->pdu_cv, &conn->lock);
    441 		}
    442 	} while (pdu == NULL);
    443 	atomic_inc_uint(&conn->pducount);
    444 	mutex_exit(&conn->lock);
    445 
    446 	memset(pdu, 0, sizeof(pdu_t));
    447 	pdu->connection = conn;
    448 	pdu->disp = PDUDISP_FREE;
    449 
    450 	DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->pducount));
    451 
    452 	return pdu;
    453 }
    454 
    455 /*
    456  * free_pdu:
    457  *    Put a PDU back onto the free list.
    458  *
    459  *    Parameter:  The PDU.
    460  */
    461 
    462 void
    463 free_pdu(pdu_t *pdu)
    464 {
    465 	connection_t *conn = pdu->connection;
    466 	pdu_disp_t pdisp;
    467 
    468 	DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->pducount-1));
    469 
    470 	KASSERT((pdu->flags & PDUF_INQUEUE) == 0);
    471 
    472 	if (PDUDISP_UNUSED == (pdisp = pdu->disp))
    473 		return;
    474 	pdu->disp = PDUDISP_UNUSED;
    475 
    476 	/* free temporary data in this PDU */
    477 	if (pdu->temp_data)
    478 		free(pdu->temp_data, M_TEMP);
    479 
    480 	mutex_enter(&conn->lock);
    481 	atomic_dec_uint(&conn->pducount);
    482 	TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
    483 	cv_broadcast(&conn->pdu_cv);
    484 	mutex_exit(&conn->lock);
    485 }
    486 
    487 /*
    488  *    create_pdus
    489  *       "Create" the pool of PDUs. This doesn't actually create the PDUs
    490  *       (they are allocated with the connection structure), but it links them
    491  *       into the free-list.
    492  *
    493  *    Parameter:  The connection owning the PDUs.
    494  */
    495 
    496 void
    497 create_pdus(connection_t *conn)
    498 {
    499 	int i;
    500 	pdu_t *pdu;
    501 
    502 	/* Note: PDUs are initialized to 0 with connection structure */
    503 
    504 	for (i = 0, pdu = conn->pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
    505 		TAILQ_INSERT_HEAD(&conn->pdu_pool, pdu, chain);
    506 	}
    507 }
    508 
    509 
    510 /*****************************************************************************
    511  * Serial Number management functions
    512  *****************************************************************************/
    513 
    514 /*
    515  * init_sernum:
    516  *    Initialize serial number buffer variables.
    517  *
    518  *    Parameter:
    519  *          buff   The serial number buffer.
    520  */
    521 
    522 void
    523 init_sernum(sernum_buffer_t *buff)
    524 {
    525 
    526 	buff->bottom = 0;
    527 	buff->top = 0;
    528 	buff->next_sn = 0;
    529 	buff->ExpSN = 0;
    530 }
    531 
    532 
    533 /*
    534  * add_sernum:
    535  *    Add a received serial number to the buffer.
    536  *    If the serial number is smaller than the expected one, it is ignored.
    537  *    If it is larger, all missing serial numbers are added as well.
    538  *
    539  *    Parameter:
    540  *          buff   The serial number buffer.
    541  *          num   The received serial number
    542  *
    543  *    Returns:
    544  *          0     if the received block is a duplicate
    545  *          1     if the number is the expected one
    546  *          >1    if the numer is > the expected value, in this case the
    547  *                return value is the number of unacknowledged blocks
    548  *          <0    if the buffer is full (i.e. an excessive number of blocks
    549  *                is unacknowledged)
    550  */
    551 
    552 int
    553 add_sernum(sernum_buffer_t *buff, uint32_t num)
    554 {
    555 	int i, t, b;
    556 	uint32_t n;
    557 	int32_t diff;
    558 
    559 	/*
    560 	 * next_sn is the next expected SN, so normally diff should be 1.
    561 	 */
    562 	n = buff->next_sn;
    563 	diff = (num - n) + 1;
    564 
    565 	if (diff <= 0) {
    566 		return 0;				/* ignore if SN is smaller than expected (dup or retransmit) */
    567 	}
    568 
    569 	buff->next_sn = num + 1;
    570 	t = buff->top;
    571 	b = buff->bottom;
    572 
    573 	for (i = 0; i < diff; i++) {
    574 		buff->sernum[t] = n++;
    575 		buff->ack[t] = false;
    576 		t = (t + 1) % SERNUM_BUFFER_LENGTH;
    577 		if (t == b) {
    578 			DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff));
    579 			return -1;
    580 		}
    581 	}
    582 
    583 	buff->top = t;
    584 	DEB(11, ("AddSernum bottom %d [%d], top %d, num %u, diff %d\n",
    585 			 b, buff->sernum[b], buff->top, num, diff));
    586 
    587 	return diff;
    588 }
    589 
    590 
    591 /*
    592  * ack_sernum:
    593  *    Mark a received serial number as acknowledged. This does not necessarily
    594  *    change the associated ExpSN if there are lower serial numbers in the
    595  *    buffer.
    596  *
    597  *    Parameter:
    598  *          buff   The serial number buffer.
    599  *          num   The serial number to acknowledge.
    600  *
    601  *    Returns:    The value of ExpSN.
    602  */
    603 
    604 uint32_t
    605 ack_sernum(sernum_buffer_t *buff, uint32_t num)
    606 {
    607 	int b = buff->bottom;
    608 	int t = buff->top;
    609 
    610 	/* shortcut for most likely case */
    611 	if (t == (b + 1) && num == buff->sernum[b]) {
    612 		/* buffer is now empty, reset top */
    613 		buff->top = b;
    614 	} else if (b != t) {
    615 		for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
    616 			if (!sn_a_lt_b(buff->sernum[b], num))
    617 				break;
    618 		}
    619 		if (num == buff->sernum[b]) {
    620 			if (b == buff->bottom)
    621 				buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH;
    622 			else
    623 				buff->ack[b] = true;
    624 		}
    625 
    626 		for (b = buff->bottom, num = buff->sernum[b] - 1;
    627 			 b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
    628 			num = buff->sernum[b];
    629 		}
    630 	}
    631 
    632 	if (!sn_a_lt_b(num, buff->ExpSN))
    633 		buff->ExpSN = num + 1;
    634 
    635 	DEB(11, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n",
    636 			 buff->bottom, buff->top, num, buff->ExpSN));
    637 
    638 	return buff->ExpSN;
    639 }
    640 
    641 /*
    642  * next_sernum:
    643  *   Return the current command serial number of the session
    644  *   and optionally increment it for the next query
    645  */
    646 uint32_t
    647 get_sernum(session_t *sess, pdu_t *pdu)
    648 {
    649 	uint32_t sn;
    650 
    651 	KASSERT(mutex_owned(&sess->lock));
    652 
    653 	sn = sess->CmdSN;
    654 	if ((pdu->pdu.Opcode & OP_IMMEDIATE) == 0)
    655 		atomic_inc_32(&sess->CmdSN);
    656 	return sn;
    657 }
    658 
    659 /*
    660  * sernum_in_window:
    661  *   Check wether serial number is in send window
    662  *
    663  */
    664 int
    665 sernum_in_window(session_t *sess)
    666 {
    667 
    668 	KASSERT(mutex_owned(&sess->lock));
    669 	return sn_a_le_b(sess->CmdSN, sess->MaxCmdSN);
    670 }
    671 
    672 /*
    673  * window_size:
    674  *    Compute send window size
    675  */
    676 int
    677 window_size(session_t *sess, int limit)
    678 {
    679 	uint32_t win;
    680 
    681 	KASSERT(mutex_owned(&sess->lock));
    682 
    683 	win = 0;
    684 	if (sn_a_le_b(sess->CmdSN, sess->MaxCmdSN))
    685 		win = sess->MaxCmdSN - sess->CmdSN + 1;
    686 	if (win > INT_MAX || win > limit)
    687 		win = limit;
    688 
    689 	return win;
    690 }
    691