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