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