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