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