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