1*4ccc6176Smlelstv /* $NetBSD: iscsi_utils.c,v 1.29 2023/11/25 10:08:27 mlelstv Exp $ */
2e01460c0Sagc
3e01460c0Sagc /*-
4e01460c0Sagc * Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
5e01460c0Sagc * All rights reserved.
6e01460c0Sagc *
7e01460c0Sagc * This code is derived from software contributed to The NetBSD Foundation
8e01460c0Sagc * by Wasabi Systems, Inc.
9e01460c0Sagc *
10e01460c0Sagc * Redistribution and use in source and binary forms, with or without
11e01460c0Sagc * modification, are permitted provided that the following conditions
12e01460c0Sagc * are met:
13e01460c0Sagc * 1. Redistributions of source code must retain the above copyright
14e01460c0Sagc * notice, this list of conditions and the following disclaimer.
15e01460c0Sagc * 2. Redistributions in binary form must reproduce the above copyright
16e01460c0Sagc * notice, this list of conditions and the following disclaimer in the
17e01460c0Sagc * documentation and/or other materials provided with the distribution.
18e01460c0Sagc *
19e01460c0Sagc * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20e01460c0Sagc * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21e01460c0Sagc * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22e01460c0Sagc * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23e01460c0Sagc * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24e01460c0Sagc * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25e01460c0Sagc * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26e01460c0Sagc * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27e01460c0Sagc * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28e01460c0Sagc * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29e01460c0Sagc * POSSIBILITY OF SUCH DAMAGE.
30e01460c0Sagc */
31e01460c0Sagc #include "iscsi_globals.h"
32e01460c0Sagc
33e01460c0Sagc #include <sys/systm.h>
34e01460c0Sagc #include <sys/buf.h>
35e01460c0Sagc #include <sys/socketvar.h>
36001d5cd7Smlelstv #include <sys/bswap.h>
37996d5208Smlelstv #include <sys/atomic.h>
38e01460c0Sagc
39e01460c0Sagc
40e01460c0Sagc /*****************************************************************************
41e01460c0Sagc * Digest functions
42e01460c0Sagc *****************************************************************************/
43e01460c0Sagc
44e01460c0Sagc /*****************************************************************
45e01460c0Sagc *
46e01460c0Sagc * CRC LOOKUP TABLE
47e01460c0Sagc * ================
48e01460c0Sagc * The following CRC lookup table was generated automagically
49e01460c0Sagc * by the Rocksoft^tm Model CRC Algorithm Table Generation
50e01460c0Sagc * Program V1.0 using the following model parameters:
51e01460c0Sagc *
52e01460c0Sagc * Width : 4 bytes.
53e01460c0Sagc * Poly : 0x1EDC6F41L
54e01460c0Sagc * Reverse : TRUE.
55e01460c0Sagc *
56e01460c0Sagc * For more information on the Rocksoft^tm Model CRC Algorithm,
57e01460c0Sagc * see the document titled "A Painless Guide to CRC Error
58e01460c0Sagc * Detection Algorithms" by Ross Williams
59e01460c0Sagc * (ross@guest.adelaide.edu.au.). This document is likely to be
60e01460c0Sagc * in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".
61e01460c0Sagc *
62e01460c0Sagc *****************************************************************/
63e01460c0Sagc
64e01460c0Sagc STATIC uint32_t crc_table[256] = {
65e01460c0Sagc 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
66e01460c0Sagc 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
67e01460c0Sagc 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
68e01460c0Sagc 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
69e01460c0Sagc 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
70e01460c0Sagc 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
71e01460c0Sagc 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
72e01460c0Sagc 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
73e01460c0Sagc 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
74e01460c0Sagc 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
75e01460c0Sagc 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
76e01460c0Sagc 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
77e01460c0Sagc 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
78e01460c0Sagc 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
79e01460c0Sagc 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
80e01460c0Sagc 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
81e01460c0Sagc 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
82e01460c0Sagc 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
83e01460c0Sagc 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
84e01460c0Sagc 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
85e01460c0Sagc 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
86e01460c0Sagc 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
87e01460c0Sagc 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
88e01460c0Sagc 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
89e01460c0Sagc 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
90e01460c0Sagc 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
91e01460c0Sagc 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
92e01460c0Sagc 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
93e01460c0Sagc 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
94e01460c0Sagc 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
95e01460c0Sagc 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
96e01460c0Sagc 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
97e01460c0Sagc 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
98e01460c0Sagc 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
99e01460c0Sagc 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
100e01460c0Sagc 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
101e01460c0Sagc 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
102e01460c0Sagc 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
103e01460c0Sagc 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
104e01460c0Sagc 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
105e01460c0Sagc 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
106e01460c0Sagc 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
107e01460c0Sagc 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
108e01460c0Sagc 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
109e01460c0Sagc 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
110e01460c0Sagc 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
111e01460c0Sagc 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
112e01460c0Sagc 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
113e01460c0Sagc 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
114e01460c0Sagc 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
115e01460c0Sagc 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
116e01460c0Sagc 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
117e01460c0Sagc 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
118e01460c0Sagc 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
119e01460c0Sagc 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
120e01460c0Sagc 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
121e01460c0Sagc 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
122e01460c0Sagc 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
123e01460c0Sagc 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
124e01460c0Sagc 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
125e01460c0Sagc 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
126e01460c0Sagc 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
127e01460c0Sagc 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
128e01460c0Sagc 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
129e01460c0Sagc };
130e01460c0Sagc
131e01460c0Sagc
132e01460c0Sagc /*
133e01460c0Sagc * gen_digest:
134e01460c0Sagc * Generate an iSCSI CRC32C digest over the given data.
135e01460c0Sagc *
136e01460c0Sagc * Parameters:
137e01460c0Sagc * buff The data
138e01460c0Sagc * len The length of the data in bytes
139e01460c0Sagc *
140e01460c0Sagc * Returns: The digest in network byte order
141e01460c0Sagc */
142e01460c0Sagc
143e01460c0Sagc uint32_t
gen_digest(const void * buff,size_t len)1448ab41cb9Schristos gen_digest(const void *buff, size_t len)
145e01460c0Sagc {
1468ab41cb9Schristos const uint8_t *bp = (const uint8_t *) buff;
147e01460c0Sagc uint32_t crc = 0xffffffff;
148e01460c0Sagc
149e01460c0Sagc while (len--) {
150e01460c0Sagc crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
151e01460c0Sagc }
152001d5cd7Smlelstv return htonl(bswap32(crc ^ 0xffffffff));
153e01460c0Sagc }
154e01460c0Sagc
155e01460c0Sagc
156e01460c0Sagc /*
157e01460c0Sagc * gen_digest_2:
158e01460c0Sagc * Generate an iSCSI CRC32C digest over the given data, which is split over
159e01460c0Sagc * two buffers.
160e01460c0Sagc *
161e01460c0Sagc * Parameters:
162e01460c0Sagc * buf1, buf2 The data
163e01460c0Sagc * len1, len2 The length of the data in bytes
164e01460c0Sagc *
165e01460c0Sagc * Returns: The digest in network byte order
166e01460c0Sagc */
167e01460c0Sagc
168e01460c0Sagc uint32_t
gen_digest_2(const void * buf1,size_t len1,const void * buf2,size_t len2)1698ab41cb9Schristos gen_digest_2(const void *buf1, size_t len1, const void *buf2, size_t len2)
170e01460c0Sagc {
1718ab41cb9Schristos const uint8_t *bp = (const uint8_t *) buf1;
172e01460c0Sagc uint32_t crc = 0xffffffff;
173e01460c0Sagc
174e01460c0Sagc while (len1--) {
175e01460c0Sagc crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
176e01460c0Sagc }
1778ab41cb9Schristos bp = (const uint8_t *) buf2;
178e01460c0Sagc while (len2--) {
179e01460c0Sagc crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
180e01460c0Sagc }
181001d5cd7Smlelstv return htonl(bswap32(crc ^ 0xffffffff));
182e01460c0Sagc }
183e01460c0Sagc
184e01460c0Sagc /*****************************************************************************
185e01460c0Sagc * CCB management functions
186e01460c0Sagc *****************************************************************************/
187e01460c0Sagc
188e01460c0Sagc /*
189e01460c0Sagc * get_ccb:
190e01460c0Sagc * Get a CCB for the SCSI operation, waiting if none is available.
191e01460c0Sagc *
192e01460c0Sagc * Parameter:
193e01460c0Sagc * sess The session containing this CCB
194e01460c0Sagc * waitok Whether waiting for a CCB is OK
195e01460c0Sagc *
196e01460c0Sagc * Returns: The CCB.
197e01460c0Sagc */
198e01460c0Sagc
199e01460c0Sagc ccb_t *
get_ccb(connection_t * conn,bool waitok)200e01460c0Sagc get_ccb(connection_t *conn, bool waitok)
201e01460c0Sagc {
202e01460c0Sagc ccb_t *ccb;
2038ab41cb9Schristos session_t *sess = conn->c_session;
204e01460c0Sagc
2058ab41cb9Schristos mutex_enter(&sess->s_lock);
2061ec2b09aSmlelstv for (;;) {
2078ab41cb9Schristos ccb = TAILQ_FIRST(&sess->s_ccb_pool);
2081ec2b09aSmlelstv
209e01460c0Sagc DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
210996d5208Smlelstv
211996d5208Smlelstv if (ccb != NULL) {
2128ab41cb9Schristos TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain);
2131ec2b09aSmlelstv break;
214e01460c0Sagc }
2151ec2b09aSmlelstv
2161ec2b09aSmlelstv if (!waitok)
2171ec2b09aSmlelstv break;
2181ec2b09aSmlelstv
2198ab41cb9Schristos cv_wait(&sess->s_ccb_cv, &sess->s_lock);
220e01460c0Sagc }
2218ab41cb9Schristos mutex_exit(&sess->s_lock);
222e01460c0Sagc
2231ec2b09aSmlelstv if (ccb == NULL) {
2241ec2b09aSmlelstv DEB(15, ("get_ccb: failed"));
2251ec2b09aSmlelstv return NULL;
2261ec2b09aSmlelstv }
2271ec2b09aSmlelstv
2288ab41cb9Schristos ccb->ccb_flags = 0;
2298ab41cb9Schristos ccb->ccb_timedout = TOUT_NONE;
2308ab41cb9Schristos ccb->ccb_xs = NULL;
2318ab41cb9Schristos ccb->ccb_temp_data = NULL;
2328ab41cb9Schristos ccb->ccb_text_data = NULL;
2338ab41cb9Schristos ccb->ccb_status = ISCSI_STATUS_SUCCESS;
2348ab41cb9Schristos ccb->ccb_ITT = (ccb->ccb_ITT & 0xffffff);
2358ab41cb9Schristos ccb->ccb_disp = CCBDISP_NOWAIT;
2368ab41cb9Schristos ccb->ccb_connection = conn;
2378ab41cb9Schristos ccb->ccb_num_timeouts = 0;
238*4ccc6176Smlelstv mutex_enter(&conn->c_lock);
239*4ccc6176Smlelstv conn->c_usecount++;
240*4ccc6176Smlelstv mutex_exit(&conn->c_lock);
241996d5208Smlelstv
242fb85db77Smlelstv DEBC(conn, 15, (
243996d5208Smlelstv "get_ccb: ccb = %p, usecount = %d\n",
2448ab41cb9Schristos ccb, conn->c_usecount));
245e01460c0Sagc
246e01460c0Sagc return ccb;
247e01460c0Sagc }
248e01460c0Sagc
249e01460c0Sagc /*
250e01460c0Sagc * free_ccb:
251e01460c0Sagc * Put a CCB back onto the free list.
252e01460c0Sagc *
253e01460c0Sagc * Parameter: The CCB.
254e01460c0Sagc */
255e01460c0Sagc
256e01460c0Sagc void
free_ccb(ccb_t * ccb)257e01460c0Sagc free_ccb(ccb_t *ccb)
258e01460c0Sagc {
2598ab41cb9Schristos session_t *sess = ccb->ccb_session;
2608ab41cb9Schristos connection_t *conn = ccb->ccb_connection;
261e01460c0Sagc pdu_t *pdu;
262996d5208Smlelstv
2633e271162Smlelstv DEBC(conn, 15, (
264996d5208Smlelstv "free_ccb: ccb = %p, usecount = %d\n",
2658ab41cb9Schristos ccb, conn->c_usecount-1));
26621dddfceSmlelstv
2678ab41cb9Schristos KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
268e01460c0Sagc
2698ab41cb9Schristos ccb->ccb_connection = NULL;
270*4ccc6176Smlelstv mutex_enter(&conn->c_lock);
271*4ccc6176Smlelstv conn->c_usecount--;
272*4ccc6176Smlelstv mutex_exit(&conn->c_lock);
273d1c48dffSmlelstv
2748ab41cb9Schristos if (ccb->ccb_disp > CCBDISP_NOWAIT) {
2758ab41cb9Schristos DEBOUT(("Freeing CCB with disp %d\n",ccb->ccb_disp));
276996d5208Smlelstv }
277996d5208Smlelstv
2788ab41cb9Schristos ccb->ccb_disp = CCBDISP_UNUSED;
279e01460c0Sagc
280e01460c0Sagc /* free temporary data */
2818ab41cb9Schristos if (ccb->ccb_temp_data != NULL) {
2828ab41cb9Schristos free(ccb->ccb_temp_data, M_TEMP);
283e01460c0Sagc }
2848ab41cb9Schristos if (ccb->ccb_text_data != NULL) {
2858ab41cb9Schristos free(ccb->ccb_text_data, M_TEMP);
286e01460c0Sagc }
287e01460c0Sagc /* free PDU waiting for ACK */
2888ab41cb9Schristos if ((pdu = ccb->ccb_pdu_waiting) != NULL) {
2898ab41cb9Schristos ccb->ccb_pdu_waiting = NULL;
2908ab41cb9Schristos mutex_enter(&conn->c_lock);
2918ab41cb9Schristos if ((pdu->pdu_flags & PDUF_INQUEUE) != 0) {
2928ab41cb9Schristos TAILQ_REMOVE(&conn->c_pdus_to_send, pdu, pdu_send_chain);
2938ab41cb9Schristos pdu->pdu_flags &= ~PDUF_INQUEUE;
2943e271162Smlelstv }
2958ab41cb9Schristos mutex_exit(&conn->c_lock);
296e01460c0Sagc free_pdu(pdu);
297e01460c0Sagc }
298e01460c0Sagc
2998ab41cb9Schristos mutex_enter(&sess->s_lock);
3008ab41cb9Schristos TAILQ_INSERT_TAIL(&sess->s_ccb_pool, ccb, ccb_chain);
3018ab41cb9Schristos cv_broadcast(&sess->s_ccb_cv);
3028ab41cb9Schristos mutex_exit(&sess->s_lock);
303e01460c0Sagc }
304e01460c0Sagc
305e01460c0Sagc /*
306e01460c0Sagc * create_ccbs
307e01460c0Sagc * "Create" the pool of CCBs. This doesn't actually create the CCBs
308e01460c0Sagc * (they are allocated with the session structure), but it links them
309e01460c0Sagc * into the free-list.
310e01460c0Sagc *
311e01460c0Sagc * Parameter: The session owning the CCBs.
312e01460c0Sagc */
313e01460c0Sagc
314e01460c0Sagc void
create_ccbs(session_t * sess)315e01460c0Sagc create_ccbs(session_t *sess)
316e01460c0Sagc {
317e01460c0Sagc int i;
318e01460c0Sagc ccb_t *ccb;
3198ab41cb9Schristos int sid = sess->s_id << 8;
320e01460c0Sagc
321e01460c0Sagc /* Note: CCBs are initialized to 0 with connection structure */
322e01460c0Sagc
3238ab41cb9Schristos for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) {
3248ab41cb9Schristos ccb->ccb_ITT = i | sid;
3258ab41cb9Schristos ccb->ccb_session = sess;
326e01460c0Sagc
3278ab41cb9Schristos callout_init(&ccb->ccb_timeout, CALLOUT_MPSAFE);
3288ab41cb9Schristos callout_setfunc(&ccb->ccb_timeout, ccb_timeout_co, ccb);
329e01460c0Sagc
3308ab41cb9Schristos DEB(9, ("Create_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT));
3318ab41cb9Schristos TAILQ_INSERT_HEAD(&sess->s_ccb_pool, ccb, ccb_chain);
332e01460c0Sagc }
333e01460c0Sagc }
334e01460c0Sagc
33521dddfceSmlelstv /*
33661e95c24Smlelstv * destroy_ccbs
33761e95c24Smlelstv * Kill the callouts
33861e95c24Smlelstv *
33961e95c24Smlelstv * Parameter: The session owning the CCBs.
34061e95c24Smlelstv */
34161e95c24Smlelstv
34261e95c24Smlelstv void
destroy_ccbs(session_t * sess)34361e95c24Smlelstv destroy_ccbs(session_t *sess)
34461e95c24Smlelstv {
34561e95c24Smlelstv int i;
34661e95c24Smlelstv ccb_t *ccb;
34761e95c24Smlelstv
34861e95c24Smlelstv /* Note: CCBs are initialized to 0 with connection structure */
34961e95c24Smlelstv
3508ab41cb9Schristos for (i = 0, ccb = sess->s_ccb; i < CCBS_PER_SESSION; i++, ccb++) {
35161e95c24Smlelstv
3528ab41cb9Schristos callout_halt(&ccb->ccb_timeout, NULL);
3538ab41cb9Schristos callout_destroy(&ccb->ccb_timeout);
35461e95c24Smlelstv
3558ab41cb9Schristos DEB(9, ("destroy_ccbs: ccb %p itt %x\n", ccb, ccb->ccb_ITT));
3568ab41cb9Schristos KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
3578ab41cb9Schristos KASSERT(ccb->ccb_disp == CCBDISP_UNUSED);
3588ab41cb9Schristos KASSERT(ccb->ccb_connection == NULL);
3598ab41cb9Schristos TAILQ_REMOVE(&sess->s_ccb_pool, ccb, ccb_chain);
36061e95c24Smlelstv }
36161e95c24Smlelstv }
36261e95c24Smlelstv
36361e95c24Smlelstv /*
36421dddfceSmlelstv * suspend_ccb:
36521dddfceSmlelstv * Put CCB on wait queue
36621dddfceSmlelstv */
36721dddfceSmlelstv void
suspend_ccb(ccb_t * ccb,bool yes)36821dddfceSmlelstv suspend_ccb(ccb_t *ccb, bool yes)
36921dddfceSmlelstv {
37021dddfceSmlelstv connection_t *conn;
37121dddfceSmlelstv
3728ab41cb9Schristos conn = ccb->ccb_connection;
373015f574cSmlelstv KASSERT(conn != NULL);
374bbe94f43Smlelstv
3758ab41cb9Schristos KASSERT(mutex_owned(&conn->c_lock));
376bbe94f43Smlelstv
37721dddfceSmlelstv if (yes) {
3788ab41cb9Schristos KASSERT((ccb->ccb_flags & CCBF_WAITQUEUE) == 0);
3798ab41cb9Schristos TAILQ_INSERT_TAIL(&conn->c_ccbs_waiting, ccb, ccb_chain);
3808ab41cb9Schristos ccb->ccb_flags |= CCBF_WAITQUEUE;
3818ab41cb9Schristos } else if (ccb->ccb_flags & CCBF_WAITQUEUE) {
3828ab41cb9Schristos TAILQ_REMOVE(&conn->c_ccbs_waiting, ccb, ccb_chain);
3838ab41cb9Schristos ccb->ccb_flags &= ~CCBF_WAITQUEUE;
38421dddfceSmlelstv }
38521dddfceSmlelstv }
38621dddfceSmlelstv
38721dddfceSmlelstv /*
388e01460c0Sagc * wake_ccb:
389e01460c0Sagc * Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
390e01460c0Sagc * either wake up the requesting thread, signal SCSIPI that we're done,
391e01460c0Sagc * or just free the CCB for CCBDISP_FREE.
392e01460c0Sagc *
39321dddfceSmlelstv * Parameter: The CCB to handle and the new status of the CCB
394e01460c0Sagc */
395e01460c0Sagc
396e01460c0Sagc void
wake_ccb(ccb_t * ccb,uint32_t status)39721dddfceSmlelstv wake_ccb(ccb_t *ccb, uint32_t status)
398e01460c0Sagc {
399e01460c0Sagc ccb_disp_t disp;
400e01460c0Sagc connection_t *conn;
401e01460c0Sagc
4028ab41cb9Schristos conn = ccb->ccb_connection;
403015f574cSmlelstv KASSERT(conn != NULL);
404e01460c0Sagc
405bbe94f43Smlelstv DEBC(conn, 9, ("CCB %d done, ccb = %p, disp = %d\n",
4068ab41cb9Schristos ccb->ccb_CmdSN, ccb, ccb->ccb_disp));
407e01460c0Sagc
4085762a611Smlelstv ccb_timeout_stop(ccb);
409e01460c0Sagc
4108ab41cb9Schristos mutex_enter(&conn->c_lock);
4118ab41cb9Schristos disp = ccb->ccb_disp;
412e01460c0Sagc if (disp <= CCBDISP_NOWAIT ||
4138ab41cb9Schristos (disp == CCBDISP_DEFER && conn->c_state <= ST_WINDING_DOWN)) {
4148ab41cb9Schristos mutex_exit(&conn->c_lock);
415e01460c0Sagc return;
416e01460c0Sagc }
417e01460c0Sagc
41821dddfceSmlelstv suspend_ccb(ccb, FALSE);
419e01460c0Sagc
420e01460c0Sagc /* change the disposition so nobody tries this again */
4218ab41cb9Schristos ccb->ccb_disp = CCBDISP_BUSY;
4228ab41cb9Schristos ccb->ccb_status = status;
423015f574cSmlelstv
424015f574cSmlelstv if (disp == CCBDISP_WAIT)
4258ab41cb9Schristos cv_broadcast(&conn->c_ccb_cv);
4268ab41cb9Schristos mutex_exit(&conn->c_lock);
427e01460c0Sagc
428e01460c0Sagc switch(disp) {
429e01460c0Sagc case CCBDISP_WAIT:
430015f574cSmlelstv case CCBDISP_DEFER:
431e01460c0Sagc break;
432e01460c0Sagc
433e01460c0Sagc case CCBDISP_SCSIPI:
434e01460c0Sagc iscsi_done(ccb);
435cdf28a30Smrg /* FALLTHROUGH */
436015f574cSmlelstv case CCBDISP_FREE:
43721dddfceSmlelstv free_ccb(ccb);
438e01460c0Sagc break;
439e01460c0Sagc default:
44021dddfceSmlelstv DEBC(conn, 1, ("CCB done, ccb = %p, invalid disposition %d", ccb, disp));
441e01460c0Sagc free_ccb(ccb);
442e01460c0Sagc break;
443e01460c0Sagc }
444e01460c0Sagc }
445e01460c0Sagc
446e01460c0Sagc /*****************************************************************************
447e01460c0Sagc * PDU management functions
448e01460c0Sagc *****************************************************************************/
449e01460c0Sagc
450e01460c0Sagc /*
45121dddfceSmlelstv * get_pdu:
452e01460c0Sagc * Get a PDU for the SCSI operation.
453e01460c0Sagc *
454e01460c0Sagc * Parameter:
455e01460c0Sagc * conn The connection this PDU should be associated with
456e01460c0Sagc * waitok OK to wait for PDU if TRUE
457e01460c0Sagc *
458e01460c0Sagc * Returns: The PDU or NULL if none is available and waitok is FALSE.
459e01460c0Sagc */
460e01460c0Sagc
461e01460c0Sagc pdu_t *
get_pdu(connection_t * conn,bool waitok)46221dddfceSmlelstv get_pdu(connection_t *conn, bool waitok)
463e01460c0Sagc {
464e01460c0Sagc pdu_t *pdu;
465e01460c0Sagc
4668ab41cb9Schristos mutex_enter(&conn->c_lock);
4671ec2b09aSmlelstv for (;;) {
4688ab41cb9Schristos pdu = TAILQ_FIRST(&conn->c_pdu_pool);
4691ec2b09aSmlelstv
4701ec2b09aSmlelstv if (pdu != NULL) {
4718ab41cb9Schristos TAILQ_REMOVE(&conn->c_pdu_pool, pdu, pdu_chain);
4721ec2b09aSmlelstv conn->c_pducount++;
4731ec2b09aSmlelstv break;
4741ec2b09aSmlelstv }
4751ec2b09aSmlelstv
4761ec2b09aSmlelstv if (!waitok)
4771ec2b09aSmlelstv break;
4781ec2b09aSmlelstv
4791ec2b09aSmlelstv cv_wait(&conn->c_pdu_cv, &conn->c_lock);
4801ec2b09aSmlelstv }
4811ec2b09aSmlelstv mutex_exit(&conn->c_lock);
48221dddfceSmlelstv
483e01460c0Sagc if (pdu == NULL) {
484bbe94f43Smlelstv DEB(15, ("get_pdu: failed"));
485e01460c0Sagc return NULL;
486996d5208Smlelstv }
487e01460c0Sagc
488e01460c0Sagc memset(pdu, 0, sizeof(pdu_t));
4898ab41cb9Schristos pdu->pdu_connection = conn;
4908ab41cb9Schristos pdu->pdu_disp = PDUDISP_FREE;
491e01460c0Sagc
4928ab41cb9Schristos DEBC(conn, 15, ("get_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount));
493bbe94f43Smlelstv
494e01460c0Sagc return pdu;
495e01460c0Sagc }
496e01460c0Sagc
497e01460c0Sagc /*
498e01460c0Sagc * free_pdu:
499e01460c0Sagc * Put a PDU back onto the free list.
500e01460c0Sagc *
501e01460c0Sagc * Parameter: The PDU.
502e01460c0Sagc */
503e01460c0Sagc
504e01460c0Sagc void
free_pdu(pdu_t * pdu)505e01460c0Sagc free_pdu(pdu_t *pdu)
506e01460c0Sagc {
5078ab41cb9Schristos connection_t *conn = pdu->pdu_connection;
508e01460c0Sagc pdu_disp_t pdisp;
509e01460c0Sagc
5108ab41cb9Schristos DEBC(conn, 15, ("free_pdu: pdu = %p, usecount = %d\n", pdu, conn->c_pducount-1));
511bbe94f43Smlelstv
5128ab41cb9Schristos KASSERT((pdu->pdu_flags & PDUF_INQUEUE) == 0);
513aac72e4bSmlelstv
5141ec2b09aSmlelstv if (PDUDISP_UNUSED == (pdisp = pdu->pdu_disp)) {
5151ec2b09aSmlelstv DEBC(conn, 0, ("freeing UNUSED pdu\n"));
516e01460c0Sagc return;
5171ec2b09aSmlelstv }
5181ec2b09aSmlelstv
5198ab41cb9Schristos pdu->pdu_disp = PDUDISP_UNUSED;
520e01460c0Sagc
521e01460c0Sagc /* free temporary data in this PDU */
5228ab41cb9Schristos if (pdu->pdu_temp_data)
5238ab41cb9Schristos free(pdu->pdu_temp_data, M_TEMP);
524e01460c0Sagc
5258ab41cb9Schristos mutex_enter(&conn->c_lock);
5261ec2b09aSmlelstv conn->c_pducount--;
5278ab41cb9Schristos TAILQ_INSERT_TAIL(&conn->c_pdu_pool, pdu, pdu_chain);
5288ab41cb9Schristos cv_broadcast(&conn->c_pdu_cv);
5298ab41cb9Schristos mutex_exit(&conn->c_lock);
530e01460c0Sagc }
531e01460c0Sagc
532e01460c0Sagc /*
533e01460c0Sagc * create_pdus
534e01460c0Sagc * "Create" the pool of PDUs. This doesn't actually create the PDUs
535e01460c0Sagc * (they are allocated with the connection structure), but it links them
536e01460c0Sagc * into the free-list.
537e01460c0Sagc *
538e01460c0Sagc * Parameter: The connection owning the PDUs.
539e01460c0Sagc */
540e01460c0Sagc
541e01460c0Sagc void
create_pdus(connection_t * conn)542e01460c0Sagc create_pdus(connection_t *conn)
543e01460c0Sagc {
544e01460c0Sagc int i;
545e01460c0Sagc pdu_t *pdu;
546e01460c0Sagc
547e01460c0Sagc /* Note: PDUs are initialized to 0 with connection structure */
548e01460c0Sagc
5498ab41cb9Schristos for (i = 0, pdu = conn->c_pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
5508ab41cb9Schristos TAILQ_INSERT_HEAD(&conn->c_pdu_pool, pdu, pdu_chain);
551e01460c0Sagc }
552e01460c0Sagc }
553e01460c0Sagc
554e01460c0Sagc
555e01460c0Sagc /*****************************************************************************
556e01460c0Sagc * Serial Number management functions
557e01460c0Sagc *****************************************************************************/
558e01460c0Sagc
559e01460c0Sagc /*
560e01460c0Sagc * init_sernum:
561e01460c0Sagc * Initialize serial number buffer variables.
562e01460c0Sagc *
563e01460c0Sagc * Parameter:
564e01460c0Sagc * buff The serial number buffer.
565e01460c0Sagc */
566e01460c0Sagc
567e01460c0Sagc void
init_sernum(sernum_buffer_t * buff)568e01460c0Sagc init_sernum(sernum_buffer_t *buff)
569e01460c0Sagc {
570e01460c0Sagc
571e01460c0Sagc buff->bottom = 0;
572e01460c0Sagc buff->top = 0;
573e01460c0Sagc buff->next_sn = 0;
574e01460c0Sagc buff->ExpSN = 0;
575e01460c0Sagc }
576e01460c0Sagc
577e01460c0Sagc
578e01460c0Sagc /*
579e01460c0Sagc * add_sernum:
580e01460c0Sagc * Add a received serial number to the buffer.
581e01460c0Sagc * If the serial number is smaller than the expected one, it is ignored.
582e01460c0Sagc * If it is larger, all missing serial numbers are added as well.
583e01460c0Sagc *
584e01460c0Sagc * Parameter:
585e01460c0Sagc * buff The serial number buffer.
586e01460c0Sagc * num The received serial number
587e01460c0Sagc *
588e01460c0Sagc * Returns:
589e01460c0Sagc * 0 if the received block is a duplicate
590e01460c0Sagc * 1 if the number is the expected one
59125629ef4Smsaitoh * >1 if the number is > the expected value, in this case the
592e01460c0Sagc * return value is the number of unacknowledged blocks
593e01460c0Sagc * <0 if the buffer is full (i.e. an excessive number of blocks
594e01460c0Sagc * is unacknowledged)
595e01460c0Sagc */
596e01460c0Sagc
597e01460c0Sagc int
add_sernum(sernum_buffer_t * buff,uint32_t num)598e01460c0Sagc add_sernum(sernum_buffer_t *buff, uint32_t num)
599e01460c0Sagc {
600da501d22Smlelstv int i, t, b;
601da501d22Smlelstv uint32_t n;
602da501d22Smlelstv int32_t diff;
603e01460c0Sagc
604e01460c0Sagc /*
605e01460c0Sagc * next_sn is the next expected SN, so normally diff should be 1.
606e01460c0Sagc */
607e01460c0Sagc n = buff->next_sn;
608e01460c0Sagc diff = (num - n) + 1;
609e01460c0Sagc
610e01460c0Sagc if (diff <= 0) {
611e01460c0Sagc return 0; /* ignore if SN is smaller than expected (dup or retransmit) */
612e01460c0Sagc }
613e01460c0Sagc
614e01460c0Sagc buff->next_sn = num + 1;
615e01460c0Sagc t = buff->top;
616e01460c0Sagc b = buff->bottom;
617e01460c0Sagc
618e01460c0Sagc for (i = 0; i < diff; i++) {
619e01460c0Sagc buff->sernum[t] = n++;
62071697517Sjoerg buff->ack[t] = false;
621e01460c0Sagc t = (t + 1) % SERNUM_BUFFER_LENGTH;
622e01460c0Sagc if (t == b) {
623e01460c0Sagc DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff));
624e01460c0Sagc return -1;
625e01460c0Sagc }
626e01460c0Sagc }
627e01460c0Sagc
628e01460c0Sagc buff->top = t;
629fb85db77Smlelstv DEB(11, ("AddSernum bottom %d [%d], top %d, num %u, diff %d\n",
630e01460c0Sagc b, buff->sernum[b], buff->top, num, diff));
631e01460c0Sagc
632e01460c0Sagc return diff;
633e01460c0Sagc }
634e01460c0Sagc
635e01460c0Sagc
636e01460c0Sagc /*
637e01460c0Sagc * ack_sernum:
638e01460c0Sagc * Mark a received serial number as acknowledged. This does not necessarily
639e01460c0Sagc * change the associated ExpSN if there are lower serial numbers in the
640e01460c0Sagc * buffer.
641e01460c0Sagc *
642e01460c0Sagc * Parameter:
643e01460c0Sagc * buff The serial number buffer.
644e01460c0Sagc * num The serial number to acknowledge.
645e01460c0Sagc *
646e01460c0Sagc * Returns: The value of ExpSN.
647e01460c0Sagc */
648e01460c0Sagc
649e01460c0Sagc uint32_t
ack_sernum(sernum_buffer_t * buff,uint32_t num)650e01460c0Sagc ack_sernum(sernum_buffer_t *buff, uint32_t num)
651e01460c0Sagc {
652e01460c0Sagc int b = buff->bottom;
653e01460c0Sagc int t = buff->top;
654e01460c0Sagc
655e01460c0Sagc /* shortcut for most likely case */
656e01460c0Sagc if (t == (b + 1) && num == buff->sernum[b]) {
657e01460c0Sagc /* buffer is now empty, reset top */
658e01460c0Sagc buff->top = b;
659e01460c0Sagc } else if (b != t) {
660e01460c0Sagc for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
661e01460c0Sagc if (!sn_a_lt_b(buff->sernum[b], num))
662e01460c0Sagc break;
663e01460c0Sagc }
664e01460c0Sagc if (num == buff->sernum[b]) {
665e01460c0Sagc if (b == buff->bottom)
666e01460c0Sagc buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH;
667e01460c0Sagc else
66871697517Sjoerg buff->ack[b] = true;
669e01460c0Sagc }
670e01460c0Sagc
671e01460c0Sagc for (b = buff->bottom, num = buff->sernum[b] - 1;
672e01460c0Sagc b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
673e01460c0Sagc num = buff->sernum[b];
674e01460c0Sagc }
675e01460c0Sagc }
676e01460c0Sagc
677e01460c0Sagc if (!sn_a_lt_b(num, buff->ExpSN))
678e01460c0Sagc buff->ExpSN = num + 1;
679e01460c0Sagc
680fb85db77Smlelstv DEB(11, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n",
681e01460c0Sagc buff->bottom, buff->top, num, buff->ExpSN));
682e01460c0Sagc
683e01460c0Sagc return buff->ExpSN;
684e01460c0Sagc }
68562dba08eSmlelstv
68662dba08eSmlelstv /*
68762dba08eSmlelstv * next_sernum:
68862dba08eSmlelstv * Return the current command serial number of the session
68962dba08eSmlelstv * and optionally increment it for the next query
69062dba08eSmlelstv */
69162dba08eSmlelstv uint32_t
get_sernum(session_t * sess,pdu_t * pdu)692bbe94f43Smlelstv get_sernum(session_t *sess, pdu_t *pdu)
69362dba08eSmlelstv {
69462dba08eSmlelstv uint32_t sn;
69562dba08eSmlelstv
6968ab41cb9Schristos KASSERT(mutex_owned(&sess->s_lock));
69762dba08eSmlelstv
6988ab41cb9Schristos sn = sess->s_CmdSN;
6998ab41cb9Schristos if ((pdu->pdu_hdr.pduh_Opcode & OP_IMMEDIATE) == 0)
7008ab41cb9Schristos atomic_inc_32(&sess->s_CmdSN);
70162dba08eSmlelstv return sn;
70262dba08eSmlelstv }
70362dba08eSmlelstv
70462dba08eSmlelstv /*
70562dba08eSmlelstv * sernum_in_window:
706f93d6526Smaya * Check whether serial number is in send window
70762dba08eSmlelstv *
70862dba08eSmlelstv */
70962dba08eSmlelstv int
sernum_in_window(session_t * sess)71062dba08eSmlelstv sernum_in_window(session_t *sess)
71162dba08eSmlelstv {
71262dba08eSmlelstv
7138ab41cb9Schristos KASSERT(mutex_owned(&sess->s_lock));
7148ab41cb9Schristos return sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN);
71562dba08eSmlelstv }
71662dba08eSmlelstv
717bbe94f43Smlelstv /*
718bbe94f43Smlelstv * window_size:
719bbe94f43Smlelstv * Compute send window size
720bbe94f43Smlelstv */
721bbe94f43Smlelstv int
window_size(session_t * sess,int limit)722bbe94f43Smlelstv window_size(session_t *sess, int limit)
723bbe94f43Smlelstv {
724bbe94f43Smlelstv uint32_t win;
725bbe94f43Smlelstv
7268ab41cb9Schristos KASSERT(mutex_owned(&sess->s_lock));
727bbe94f43Smlelstv
728bbe94f43Smlelstv win = 0;
7298ab41cb9Schristos if (sn_a_le_b(sess->s_CmdSN, sess->s_MaxCmdSN))
7308ab41cb9Schristos win = sess->s_MaxCmdSN - sess->s_CmdSN + 1;
731bbe94f43Smlelstv if (win > INT_MAX || win > limit)
732bbe94f43Smlelstv win = limit;
733bbe94f43Smlelstv
734bbe94f43Smlelstv return win;
735bbe94f43Smlelstv }
736