iscsi_utils.c revision 1.10 1 /* $NetBSD: iscsi_utils.c,v 1.10 2016/06/01 04:19: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 #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 (at) 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->xs = NULL;
243 ccb->temp_data = NULL;
244 ccb->text_data = NULL;
245 ccb->status = ISCSI_STATUS_SUCCESS;
246 ccb->ITT = (ccb->ITT & 0xffffff) | (++sess->itt_id << 24);
247 ccb->disp = CCBDISP_NOWAIT;
248 ccb->connection = conn;
249 atomic_inc_uint(&conn->usecount);
250
251 DEBC(conn, 5, (
252 "get_ccb: ccb = %p, usecount = %d\n",
253 ccb, conn->usecount));
254
255 return ccb;
256 }
257
258 /*
259 * free_ccb:
260 * Put a CCB back onto the free list.
261 *
262 * Parameter: The CCB.
263 */
264
265 void
266 free_ccb(ccb_t *ccb)
267 {
268 session_t *sess = ccb->session;
269 pdu_t *pdu;
270
271 DEBC(ccb->connection, 5, (
272 "free_ccb: ccb = %p, usecount = %d\n",
273 ccb, ccb->connection->usecount-1));
274
275 KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
276 KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
277
278 atomic_dec_uint(&ccb->connection->usecount);
279 ccb->connection = NULL;
280
281 if (ccb->disp > CCBDISP_NOWAIT) {
282 DEBOUT(("Freeing CCB with disp %d\n",ccb->disp));
283 }
284
285 ccb->disp = CCBDISP_UNUSED;
286
287 /* free temporary data */
288 if (ccb->temp_data != NULL) {
289 free(ccb->temp_data, M_TEMP);
290 }
291 if (ccb->text_data != NULL) {
292 free(ccb->text_data, M_TEMP);
293 }
294 /* free PDU waiting for ACK */
295 if ((pdu = ccb->pdu_waiting) != NULL) {
296 ccb->pdu_waiting = NULL;
297 free_pdu(pdu);
298 }
299
300 mutex_enter(&sess->lock);
301 TAILQ_INSERT_TAIL(&sess->ccb_pool, ccb, chain);
302 mutex_exit(&sess->lock);
303
304 cv_broadcast(&sess->ccb_cv);
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->id << 8;
322
323 /* Note: CCBs are initialized to 0 with connection structure */
324
325 for (i = 0, ccb = sess->ccb; i < CCBS_PER_SESSION; i++, ccb++) {
326 ccb->ITT = i | sid;
327 ccb->session = sess;
328
329 callout_init(&ccb->timeout, CALLOUT_MPSAFE);
330 callout_setfunc(&ccb->timeout, ccb_timeout_co, ccb);
331
332 DEB(9, ("Create_ccbs: ccb %p itt %x\n", ccb, ccb->ITT));
333 TAILQ_INSERT_HEAD(&sess->ccb_pool, ccb, chain);
334 }
335 }
336
337 /*
338 * suspend_ccb:
339 * Put CCB on wait queue
340 */
341 void
342 suspend_ccb(ccb_t *ccb, bool yes)
343 {
344 connection_t *conn;
345
346 conn = ccb->connection;
347 if (yes) {
348 KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
349 KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
350 TAILQ_INSERT_TAIL(&conn->ccbs_waiting, ccb, chain);
351 ccb->flags |= CCBF_WAITQUEUE;
352 } else if (ccb->flags & CCBF_WAITQUEUE) {
353 KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
354 TAILQ_REMOVE(&conn->ccbs_waiting, ccb, chain);
355 ccb->flags &= ~CCBF_WAITQUEUE;
356 }
357 }
358
359 /*
360 * throttle_ccb:
361 * Put CCB on throttling queue
362 */
363 void
364 throttle_ccb(ccb_t *ccb, bool yes)
365 {
366 session_t *sess;
367
368 KASSERT(mutex_owned(&sess->lock));
369
370 sess = ccb->session;
371 if (yes) {
372 KASSERT((ccb->flags & CCBF_THROTTLING) == 0);
373 KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
374 TAILQ_INSERT_TAIL(&sess->ccbs_throttled, ccb, chain);
375 ccb->flags |= CCBF_THROTTLING;
376 } else if (ccb->flags & CCBF_THROTTLING) {
377 KASSERT((ccb->flags & CCBF_WAITQUEUE) == 0);
378 TAILQ_REMOVE(&sess->ccbs_throttled, ccb, chain);
379 ccb->flags &= ~CCBF_THROTTLING;
380 }
381 }
382
383
384 /*
385 * wake_ccb:
386 * Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
387 * either wake up the requesting thread, signal SCSIPI that we're done,
388 * or just free the CCB for CCBDISP_FREE.
389 *
390 * Parameter: The CCB to handle and the new status of the CCB
391 */
392
393 void
394 wake_ccb(ccb_t *ccb, uint32_t status)
395 {
396 ccb_disp_t disp;
397 connection_t *conn;
398
399 conn = ccb->connection;
400
401 #ifdef ISCSI_DEBUG
402 DEBC(conn, 9, ("CCB done, ccb = %p, disp = %d\n",
403 ccb, ccb->disp));
404 #endif
405
406 callout_stop(&ccb->timeout);
407
408 mutex_enter(&conn->lock);
409 disp = ccb->disp;
410 if (disp <= CCBDISP_NOWAIT ||
411 (disp == CCBDISP_DEFER && conn->state <= ST_WINDING_DOWN)) {
412 mutex_exit(&conn->lock);
413 return;
414 }
415
416 suspend_ccb(ccb, FALSE);
417 throttle_ccb(ccb, FALSE);
418
419 /* change the disposition so nobody tries this again */
420 ccb->disp = CCBDISP_BUSY;
421 ccb->status = status;
422 mutex_exit(&conn->lock);
423
424 switch (disp) {
425 case CCBDISP_FREE:
426 free_ccb(ccb);
427 break;
428
429 case CCBDISP_WAIT:
430 cv_broadcast(&conn->ccb_cv);
431 break;
432
433 case CCBDISP_SCSIPI:
434 iscsi_done(ccb);
435 free_ccb(ccb);
436 break;
437
438 case CCBDISP_DEFER:
439 break;
440
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->lock);
469 do {
470 pdu = TAILQ_FIRST(&conn->pdu_pool);
471 if (pdu != NULL)
472 TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
473
474 DEB(100, ("get_pdu_c: pdu = %p, waitok = %d\n", pdu, waitok));
475
476 if (pdu == NULL) {
477 if (!waitok || conn->terminating) {
478 mutex_exit(&conn->lock);
479 return NULL;
480 }
481 cv_wait(&conn->conn_cv, &conn->lock);
482 }
483 } while (pdu == NULL);
484 mutex_exit(&conn->lock);
485
486 memset(pdu, 0, sizeof(pdu_t));
487 pdu->connection = conn;
488 pdu->disp = PDUDISP_FREE;
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->connection;
504 pdu_disp_t pdisp;
505
506 if (PDUDISP_UNUSED == (pdisp = pdu->disp))
507 return;
508 pdu->disp = PDUDISP_UNUSED;
509
510 mutex_enter(&conn->lock);
511 if (pdu->flags & PDUF_INQUEUE) {
512 TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
513 pdu->flags &= ~PDUF_INQUEUE;
514 }
515 mutex_exit(&conn->lock);
516
517 /* free temporary data in this PDU */
518 if (pdu->temp_data)
519 free(pdu->temp_data, M_TEMP);
520
521 mutex_enter(&conn->lock);
522 TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
523 mutex_exit(&conn->lock);
524
525 cv_broadcast(&conn->conn_cv);
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->pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
546 TAILQ_INSERT_HEAD(&conn->pdu_pool, 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 numer 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(10, ("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(10, ("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, bool bump)
689 {
690 uint32_t sn;
691
692 KASSERT(mutex_owned(&sess->lock));
693
694 sn = sess->CmdSN;
695 if (bump)
696 atomic_inc_32(&sess->CmdSN);
697 return sn;
698 }
699
700 /*
701 * sernum_in_window:
702 * Check wether serial number is in send window
703 *
704 */
705 int
706 sernum_in_window(session_t *sess)
707 {
708
709 KASSERT(mutex_owned(&sess->lock));
710 return sn_a_le_b(sess->CmdSN, sess->MaxCmdSN);
711 }
712
713