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