1 /* $NetBSD: iscsi_utils.c,v 1.28 2022/09/13 13:09:16 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@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(const void *buff, size_t len) 145 { 146 const uint8_t *bp = (const 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(const void *buf1, size_t len1, const void *buf2, size_t len2) 170 { 171 const uint8_t *bp = (const 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 = (const 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->c_session; 204 205 mutex_enter(&sess->s_lock); 206 for (;;) { 207 ccb = TAILQ_FIRST(&sess->s_ccb_pool); 208 209 DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok)); 210 211 if (ccb != NULL) { 212 TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain); 213 break; 214 } 215 216 if (!waitok) 217 break; 218 219 cv_wait(&sess->s_ccb_cv, &sess->s_lock); 220 } 221 mutex_exit(&sess->s_lock); 222 223 if (ccb == NULL) { 224 DEB(15, ("get_ccb: failed")); 225 return NULL; 226 } 227 228 ccb->ccb_flags = 0; 229 ccb->ccb_timedout = TOUT_NONE; 230 ccb->ccb_xs = NULL; 231 ccb->ccb_temp_data = NULL; 232 ccb->ccb_text_data = NULL; 233 ccb->ccb_status = ISCSI_STATUS_SUCCESS; 234 ccb->ccb_ITT = (ccb->ccb_ITT & 0xffffff); 235 ccb->ccb_disp = CCBDISP_NOWAIT; 236 ccb->ccb_connection = conn; 237 ccb->ccb_num_timeouts = 0; 238 atomic_inc_uint(&conn->c_usecount); 239 240 DEBC(conn, 15, ( 241 "get_ccb: ccb = %p, usecount = %d\n", 242 ccb, conn->c_usecount)); 243 244 return ccb; 245 } 246 247 /* 248 * free_ccb: 249 * Put a CCB back onto the free list. 250 * 251 * Parameter: The CCB. 252 */ 253 254 void 255 free_ccb(ccb_t *ccb) 256 { 257 session_t *sess = ccb->ccb_session; 258 connection_t *conn = ccb->ccb_connection; 259 pdu_t *pdu; 260 261 DEBC(conn, 15, ( 262 "free_ccb: ccb = %p, usecount = %d\n", 263 ccb, conn->c_usecount-1)); 264 265 KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0); 266 267 atomic_dec_uint(&conn->c_usecount); 268 ccb->ccb_connection = NULL; 269 270 if (ccb->ccb_disp > CCBDISP_NOWAIT) { 271 DEBOUT(("Freeing CCB with disp %d\n",ccb->ccb_disp)); 272 } 273 274 ccb->ccb_disp = CCBDISP_UNUSED; 275 276 /* free temporary data */ 277 if (ccb->ccb_temp_data != NULL) { 278 free(ccb->ccb_temp_data, M_TEMP); 279 } 280 if (ccb->ccb_text_data != NULL) { 281 free(ccb->ccb_text_data, M_TEMP); 282 } 283 /* free PDU waiting for ACK */ 284 if ((pdu = ccb->ccb_pdu_waiting) != NULL) { 285 ccb->ccb_pdu_waiting = NULL; 286 mutex_enter(&conn->c_lock); 287 if ((pdu->pdu_flags & PDUF_INQUEUE) != 0) { 288 TAILQ_REMOVE(&conn->c_pdus_to_send, pdu, pdu_send_chain); 289 pdu->pdu_flags &= ~PDUF_INQUEUE; 290 } 291 mutex_exit(&conn->c_lock); 292 free_pdu(pdu); 293 } 294 295 mutex_enter(&sess->s_lock); 296 TAILQ_INSERT_TAIL(&sess->s_ccb_pool, ccb, ccb_chain); 297 cv_broadcast(&sess->s_ccb_cv); 298 mutex_exit(&sess->s_lock); 299 } 300 301 /* 302 * create_ccbs 303 * "Create" the pool of CCBs. This doesn't actually create the CCBs 304 * (they are allocated with the session structure), but it links them 305 * into the free-list. 306 * 307 * Parameter: The session owning the CCBs. 308 */ 309 310 void 311 create_ccbs(session_t *sess) 312 { 313 int i; 314 ccb_t *ccb; 315 int sid = sess->s_id << 8; 316 317 /* Note: CCBs are initialized to 0 with connection structure */ 318 319 for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) { 320 ccb->ccb_ITT = i | sid; 321 ccb->ccb_session = sess; 322 323 callout_init(&ccb->ccb_timeout, CALLOUT_MPSAFE); 324 callout_setfunc(&ccb->ccb_timeout, ccb_timeout_co, ccb); 325 326 DEB(9, ("Create_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT)); 327 TAILQ_INSERT_HEAD(&sess->s_ccb_pool, ccb, ccb_chain); 328 } 329 } 330 331 /* 332 * destroy_ccbs 333 * Kill the callouts 334 * 335 * Parameter: The session owning the CCBs. 336 */ 337 338 void 339 destroy_ccbs(session_t *sess) 340 { 341 int i; 342 ccb_t *ccb; 343 344 /* Note: CCBs are initialized to 0 with connection structure */ 345 346 for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) { 347 348 callout_halt(&ccb->ccb_timeout, NULL); 349 callout_destroy(&ccb->ccb_timeout); 350 351 DEB(9, ("destroy_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT)); 352 KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0); 353 KASSERT(ccb->ccb_disp == CCBDISP_UNUSED); 354 KASSERT(ccb->ccb_connection == NULL); 355 TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain); 356 } 357 } 358 359 /* 360 * suspend_ccb: 361 * Put CCB on wait queue 362 */ 363 void 364 suspend_ccb(ccb_t *ccb, bool yes) 365 { 366 connection_t *conn; 367 368 conn = ccb->ccb_connection; 369 KASSERT(conn != NULL); 370 371 KASSERT(mutex_owned(&conn->c_lock)); 372 373 if (yes) { 374 KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0); 375 TAILQ_INSERT_TAIL(&conn->c_ccbs_waiting, ccb, ccb_chain); 376 ccb->ccb_flags |= CCBF_WAITQUEUE; 377 } else if (ccb->ccb_flags & CCBF_WAITQUEUE) { 378 TAILQ_REMOVE(&conn->c_ccbs_waiting, ccb, ccb_chain); 379 ccb->ccb_flags &= ~CCBF_WAITQUEUE; 380 } 381 } 382 383 /* 384 * wake_ccb: 385 * Wake up (or dispose of) a CCB. Depending on the CCB's disposition, 386 * either wake up the requesting thread, signal SCSIPI that we're done, 387 * or just free the CCB for CCBDISP_FREE. 388 * 389 * Parameter: The CCB to handle and the new status of the CCB 390 */ 391 392 void 393 wake_ccb(ccb_t *ccb, uint32_t status) 394 { 395 ccb_disp_t disp; 396 connection_t *conn; 397 398 conn = ccb->ccb_connection; 399 KASSERT(conn != NULL); 400 401 DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n", 402 ccb->ccb_CmdSN, ccb, ccb->ccb_disp)); 403 404 ccb_timeout_stop(ccb); 405 406 mutex_enter(&conn->c_lock); 407 disp = ccb->ccb_disp; 408 if (disp <= CCBDISP_NOWAIT || 409 (disp == CCBDISP_DEFER && conn->c_state <= ST_WINDING_DOWN)) { 410 mutex_exit(&conn->c_lock); 411 return; 412 } 413 414 suspend_ccb(ccb, FALSE); 415 416 /* change the disposition so nobody tries this again */ 417 ccb->ccb_disp = CCBDISP_BUSY; 418 ccb->ccb_status = status; 419 420 if (disp == CCBDISP_WAIT) 421 cv_broadcast(&conn->c_ccb_cv); 422 mutex_exit(&conn->c_lock); 423 424 switch(disp) { 425 case CCBDISP_WAIT: 426 case CCBDISP_DEFER: 427 break; 428 429 case CCBDISP_SCSIPI: 430 iscsi_done(ccb); 431 /* FALLTHROUGH */ 432 case CCBDISP_FREE: 433 free_ccb(ccb); 434 break; 435 default: 436 DEBC(conn, 1, ("CCB done, ccb = %p, invalid disposition %d", ccb, disp)); 437 free_ccb(ccb); 438 break; 439 } 440 } 441 442 /***************************************************************************** 443 * PDU management functions 444 *****************************************************************************/ 445 446 /* 447 * get_pdu: 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 457 pdu_t * 458 get_pdu(connection_t *conn, bool waitok) 459 { 460 pdu_t *pdu; 461 462 mutex_enter(&conn->c_lock); 463 for (;;) { 464 pdu = TAILQ_FIRST(&conn->c_pdu_pool); 465 466 if (pdu != NULL) { 467 TAILQ_REMOVE(&conn->c_pdu_pool, pdu, pdu_chain); 468 conn->c_pducount++; 469 break; 470 } 471 472 if (!waitok) 473 break; 474 475 cv_wait(&conn->c_pdu_cv, &conn->c_lock); 476 } 477 mutex_exit(&conn->c_lock); 478 479 if (pdu == NULL) { 480 DEB(15, ("get_pdu: failed")); 481 return NULL; 482 } 483 484 memset(pdu, 0, sizeof(pdu_t)); 485 pdu->pdu_connection = conn; 486 pdu->pdu_disp = PDUDISP_FREE; 487 488 DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount)); 489 490 return pdu; 491 } 492 493 /* 494 * free_pdu: 495 * Put a PDU back onto the free list. 496 * 497 * Parameter: The PDU. 498 */ 499 500 void 501 free_pdu(pdu_t *pdu) 502 { 503 connection_t *conn = pdu->pdu_connection; 504 pdu_disp_t pdisp; 505 506 DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount-1)); 507 508 KASSERT((pdu->pdu_flags & PDUF_INQUEUE) == 0); 509 510 if (PDUDISP_UNUSED == (pdisp = pdu->pdu_disp)) { 511 DEBC(conn, 0, ("freeing UNUSED pdu\n")); 512 return; 513 } 514 515 pdu->pdu_disp = PDUDISP_UNUSED; 516 517 /* free temporary data in this PDU */ 518 if (pdu->pdu_temp_data) 519 free(pdu->pdu_temp_data, M_TEMP); 520 521 mutex_enter(&conn->c_lock); 522 conn->c_pducount--; 523 TAILQ_INSERT_TAIL(&conn->c_pdu_pool, pdu, pdu_chain); 524 cv_broadcast(&conn->c_pdu_cv); 525 mutex_exit(&conn->c_lock); 526 } 527 528 /* 529 * create_pdus 530 * "Create" the pool of PDUs. This doesn't actually create the PDUs 531 * (they are allocated with the connection structure), but it links them 532 * into the free-list. 533 * 534 * Parameter: The connection owning the PDUs. 535 */ 536 537 void 538 create_pdus(connection_t *conn) 539 { 540 int i; 541 pdu_t *pdu; 542 543 /* Note: PDUs are initialized to 0 with connection structure */ 544 545 for (i = 0, pdu = conn->c_pdu; i < PDUS_PER_CONNECTION; i++, pdu++) { 546 TAILQ_INSERT_HEAD(&conn->c_pdu_pool, pdu, pdu_chain); 547 } 548 } 549 550 551 /***************************************************************************** 552 * Serial Number management functions 553 *****************************************************************************/ 554 555 /* 556 * init_sernum: 557 * Initialize serial number buffer variables. 558 * 559 * Parameter: 560 * buff The serial number buffer. 561 */ 562 563 void 564 init_sernum(sernum_buffer_t *buff) 565 { 566 567 buff->bottom = 0; 568 buff->top = 0; 569 buff->next_sn = 0; 570 buff->ExpSN = 0; 571 } 572 573 574 /* 575 * add_sernum: 576 * Add a received serial number to the buffer. 577 * If the serial number is smaller than the expected one, it is ignored. 578 * If it is larger, all missing serial numbers are added as well. 579 * 580 * Parameter: 581 * buff The serial number buffer. 582 * num The received serial number 583 * 584 * Returns: 585 * 0 if the received block is a duplicate 586 * 1 if the number is the expected one 587 * >1 if the number is > the expected value, in this case the 588 * return value is the number of unacknowledged blocks 589 * <0 if the buffer is full (i.e. an excessive number of blocks 590 * is unacknowledged) 591 */ 592 593 int 594 add_sernum(sernum_buffer_t *buff, uint32_t num) 595 { 596 int i, t, b; 597 uint32_t n; 598 int32_t diff; 599 600 /* 601 * next_sn is the next expected SN, so normally diff should be 1. 602 */ 603 n = buff->next_sn; 604 diff = (num - n) + 1; 605 606 if (diff <= 0) { 607 return 0; /* ignore if SN is smaller than expected (dup or retransmit) */ 608 } 609 610 buff->next_sn = num + 1; 611 t = buff->top; 612 b = buff->bottom; 613 614 for (i = 0; i < diff; i++) { 615 buff->sernum[t] = n++; 616 buff->ack[t] = false; 617 t = (t + 1) % SERNUM_BUFFER_LENGTH; 618 if (t == b) { 619 DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff)); 620 return -1; 621 } 622 } 623 624 buff->top = t; 625 DEB(11, ("AddSernum bottom %d [%d], top %d, num %u, diff %d\n", 626 b, buff->sernum[b], buff->top, num, diff)); 627 628 return diff; 629 } 630 631 632 /* 633 * ack_sernum: 634 * Mark a received serial number as acknowledged. This does not necessarily 635 * change the associated ExpSN if there are lower serial numbers in the 636 * buffer. 637 * 638 * Parameter: 639 * buff The serial number buffer. 640 * num The serial number to acknowledge. 641 * 642 * Returns: The value of ExpSN. 643 */ 644 645 uint32_t 646 ack_sernum(sernum_buffer_t *buff, uint32_t num) 647 { 648 int b = buff->bottom; 649 int t = buff->top; 650 651 /* shortcut for most likely case */ 652 if (t == (b + 1) && num == buff->sernum[b]) { 653 /* buffer is now empty, reset top */ 654 buff->top = b; 655 } else if (b != t) { 656 for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) { 657 if (!sn_a_lt_b(buff->sernum[b], num)) 658 break; 659 } 660 if (num == buff->sernum[b]) { 661 if (b == buff->bottom) 662 buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH; 663 else 664 buff->ack[b] = true; 665 } 666 667 for (b = buff->bottom, num = buff->sernum[b] - 1; 668 b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) { 669 num = buff->sernum[b]; 670 } 671 } 672 673 if (!sn_a_lt_b(num, buff->ExpSN)) 674 buff->ExpSN = num + 1; 675 676 DEB(11, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n", 677 buff->bottom, buff->top, num, buff->ExpSN)); 678 679 return buff->ExpSN; 680 } 681 682 /* 683 * next_sernum: 684 * Return the current command serial number of the session 685 * and optionally increment it for the next query 686 */ 687 uint32_t 688 get_sernum(session_t *sess, pdu_t *pdu) 689 { 690 uint32_t sn; 691 692 KASSERT(mutex_owned(&sess->s_lock)); 693 694 sn = sess->s_CmdSN; 695 if ((pdu->pdu_hdr.pduh_Opcode & OP_IMMEDIATE) == 0) 696 atomic_inc_32(&sess->s_CmdSN); 697 return sn; 698 } 699 700 /* 701 * sernum_in_window: 702 * Check whether serial number is in send window 703 * 704 */ 705 int 706 sernum_in_window(session_t *sess) 707 { 708 709 KASSERT(mutex_owned(&sess->s_lock)); 710 return sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN); 711 } 712 713 /* 714 * window_size: 715 * Compute send window size 716 */ 717 int 718 window_size(session_t *sess, int limit) 719 { 720 uint32_t win; 721 722 KASSERT(mutex_owned(&sess->s_lock)); 723 724 win = 0; 725 if (sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN)) 726 win = sess->s_MaxCmdSN - sess->s_CmdSN + 1; 727 if (win > INT_MAX || win > limit) 728 win = limit; 729 730 return win; 731 } 732