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