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