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