Start to move the in-kernel iSCSI initiator, kindly contributed to the

NetBSD Foundation by Wasabi Systems, from

        othersrc/external/bsd/iscsi/sys/dev/iscsi

to

        src/sys/dev/iscsi
This commit is contained in:
agc 2011-10-23 21:15:02 +00:00
parent 75a17f3ce7
commit e01460c073
20 changed files with 11675 additions and 0 deletions

7
sys/dev/iscsi/Makefile Normal file
View File

@ -0,0 +1,7 @@
# $wasabi: Makefile,v 1.2 2006/04/19 02:06:26 wrstuden Exp $
INCSDIR= /usr/include/dev/iscsi
INCS= iscsi.h iscsi_ioctl.h iscsi_test.h iscsi_perf.h
.include <bsd.kinc.mk>

182
sys/dev/iscsi/base64.c Normal file
View File

@ -0,0 +1,182 @@
/* $NetBSD: base64.c,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Adapted for kernel mode use with minimal changes from
* /usr/src/crypto/dist/heimdal/lib/roken/base64.c
*
* Original:
* Copyright (c) 1995-2001 Kungliga Tekniska Högskolan
* (Royal Institute of Technology, Stockholm, Sweden).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the Institute nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "iscsi_globals.h"
#include "base64.h"
static char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static int
pos(char c)
{
char *p;
for (p = base64_chars; *p; p++) {
if (*p == c) {
return (int)(p - base64_chars);
}
}
return -1;
}
int
base64_encode(const void *data, int size, uint8_t * buffer)
{
uint8_t *p;
int i;
int c;
const uint8_t *q;
p = buffer;
q = (const uint8_t *) data;
*p++ = '0';
*p++ = 'b';
i = 0;
for (i = 0; i < size;) {
c = q[i++];
c *= 256;
if (i < size) {
c += q[i];
}
i++;
c *= 256;
if (i < size) {
c += q[i];
}
i++;
p[0] = base64_chars[(c & 0x00fc0000) >> 18];
p[1] = base64_chars[(c & 0x0003f000) >> 12];
p[2] = base64_chars[(c & 0x00000fc0) >> 6];
p[3] = base64_chars[(c & 0x0000003f) >> 0];
if (i > size) {
p[3] = '=';
}
if (i > size + 1) {
p[2] = '=';
}
p += 4;
}
*p = 0;
return strlen(buffer);
}
#define DECODE_ERROR 0xffffffff
static uint32_t
token_decode(uint8_t * token)
{
int i;
uint32_t val = 0;
int marker = 0;
if (strlen(token) < 4) {
return DECODE_ERROR;
}
for (i = 0; i < 4; i++) {
val *= 64;
if (token[i] == '=') {
marker++;
} else if (marker > 0) {
return DECODE_ERROR;
} else {
val += pos(token[i]);
}
}
if (marker > 2) {
return DECODE_ERROR;
}
return (marker << 24) | val;
}
uint8_t *
base64_decode(uint8_t * str, void *data, int *datalen)
{
uint8_t *p, *q;
uint32_t marker = 0;
q = data;
for (p = str; *p; p += 4) {
uint32_t val = token_decode(p);
marker = (val >> 24) & 0xff;
if (val == DECODE_ERROR) {
return NULL;
}
*q++ = (val >> 16) & 0xff;
if (marker < 2) {
*q++ = (val >> 8) & 0xff;
}
if (marker < 1) {
*q++ = val & 0xff;
}
}
*datalen = (int)(q - (uint8_t *) data);
return p - marker + 1;
}

38
sys/dev/iscsi/base64.h Normal file
View File

@ -0,0 +1,38 @@
/* $NetBSD: base64.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/* size of result string including term. zero and "0b" prefix */
#define base64_enclen(size) ((size) * 4 / 3 + 4 + 2)
/* encode "size" bytes of "data" into "buffer" returning length */
int base64_encode(const void *, int, uint8_t *);
/* decode "str" into "data" returning end of source string */
uint8_t *base64_decode(uint8_t *, void *, int *);

19
sys/dev/iscsi/files.iscsi Normal file
View File

@ -0,0 +1,19 @@
# $NetBSD: files.iscsi,v 1.1 2011/10/23 21:15:02 agc Exp $
#
# Configuration file for iSCSI initiator
defpseudodev iscsi : scsi
defflag ISCSI_DEBUG
defflag ISCSI_PERFTEST
defflag ISCSI_TEST_MODE
file dev/iscsi/base64.c iscsi
file dev/iscsi/iscsi_ioctl.c iscsi
file dev/iscsi/iscsi_main.c iscsi
file dev/iscsi/iscsi_profile.c iscsi
file dev/iscsi/iscsi_rcv.c iscsi
file dev/iscsi/iscsi_send.c iscsi
file dev/iscsi/iscsi_test.c iscsi
file dev/iscsi/iscsi_text.c iscsi
file dev/iscsi/iscsi_utils.c

217
sys/dev/iscsi/iscsi.h Normal file
View File

@ -0,0 +1,217 @@
/* $NetBSD: iscsi.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2004,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_H
#define _ISCSI_H
#define ISCSI_DEV_MAJOR 202
#define ISCSI_STRING_LENGTH (223+1)
#define ISCSI_ADDRESS_LENGTH (255+1)
#define ISCSI_AUTH_OPTIONS 4
typedef enum {
ISCSI_AUTH_None = 0,
ISCSI_AUTH_CHAP = 1,
ISCSI_AUTH_KRB5 = 2,
ISCSI_AUTH_SRP = 3
} iscsi_auth_types_t;
/*
ISCSI_AUTH_None
Indicates that no authentication is necessary.
ISCSI_AUTH_CHAP
Indicates CHAP authentication should be used.
ISCSI_AUTH_KRB5
Indicates Kerberos 5 authentication (fur future use).
ISCSI_AUTH_SRP
Indicates SRP authentication (for future use).
*/
typedef struct {
int mutual_auth:1;
int is_secure:1;
int auth_number:4;
iscsi_auth_types_t auth_type[ISCSI_AUTH_OPTIONS];
} iscsi_auth_info_t;
/*
mutual_auth
Indicates that authentication should be mutual, i.e.
the initiator should authenticate the target, and the target
should authenticate the initiator. If not specified, the target
will authenticate the initiator only.
is_secure
Indicates that the connection is secure.
auth_number
Indicates the number of elements in auth_type.
When 0, no authentication will be used.
auth_type
Contains up to ISCSI_AUTH_OPTIONS enumerator values of type
ISCSI_AUTH_TYPES that indicates the authentication method that
should be used to establish a login connection (none, CHAP, KRB5,
etc.), in order of priority. The first element is the most
preferred method, the last element the least preferred.
*/
typedef enum {
ISCSI_DIGEST_None = 0,
ISCSI_DIGEST_CRC32C = 1
} iscsi_digest_t;
/*
ISCSI_DIGEST_None
Indicates that no CRC is to be generated.
ISCSI_DIGEST_CRC32C
Indicates the CRC32C digest should be used.
*/
typedef enum {
ISCSI_LOGINTYPE_DISCOVERY = 0,
ISCSI_LOGINTYPE_NOMAP = 1,
ISCSI_LOGINTYPE_MAP = 2
} iscsi_login_session_type_t;
/*
ISCSI_LOGINTYPE_DISCOVERY
Indicates that the login session is for discovery only.
Initiators use this type of session to send a SCSI SendTargets
command to an iSCSI target to request assistance in
discovering other targets accessible to the target that
receives the SendTargets command.
ISCSI_LOGINTYPE_NOMAP
This establishes a normal (full featured) session, but does
not report the target LUNs to the operating system as
available drives. Communication with the target is limited
to io-controls through the driver.
ISCSI_LOGINTYPE_MAP
Indicates that the login session is full featured, and reports
all target LUNs to the operating system to map as logical drives.
*/
typedef struct {
uint8_t address[ISCSI_ADDRESS_LENGTH];
uint16_t port;
uint16_t group_tag;
} iscsi_portal_address_t;
/*
address
IP address of the target (V4 dotted quad or V6 hex).
port
IP port number.
group_tag
Target portal group tag (0 if unknown).
*/
/* ------------------------- Status Values -------------------------- */
#define ISCSI_STATUS_SUCCESS 0 /* Indicates success. */
#define ISCSI_STATUS_LIST_EMPTY 1 /* The requested list is empty. */
#define ISCSI_STATUS_DUPLICATE_NAME 2 /* The specified symbolic identifier is not unique. */
#define ISCSI_STATUS_GENERAL_ERROR 3 /* A non-specific error occurred. */
#define ISCSI_STATUS_LOGIN_FAILED 4 /* The login failed. */
#define ISCSI_STATUS_CONNECTION_FAILED 5 /* The attempt to establish a connection failed. */
#define ISCSI_STATUS_AUTHENTICATION_FAILED 6 /* Authentication negotiation failed. */
#define ISCSI_STATUS_NO_RESOURCES 7 /* Could not allocate resources (e.g. memory). */
#define ISCSI_STATUS_MAXED_CONNECTIONS 8 /* Maximum number of connections exceeded. */
#define ISCSI_STATUS_INVALID_SESSION_ID 9 /* Session ID not found */
#define ISCSI_STATUS_INVALID_CONNECTION_ID 10 /* Connection ID not found */
#define ISCSI_STATUS_INVALID_SOCKET 11 /* Specified socket is invalid */
#define ISCSI_STATUS_NOTIMPL 12 /* Feature not implemented */
#define ISCSI_STATUS_CHECK_CONDITION 13 /* Target reported CHECK CONDITION */
#define ISCSI_STATUS_TARGET_BUSY 14 /* Target reported BUSY */
#define ISCSI_STATUS_TARGET_ERROR 15 /* Target reported other error */
#define ISCSI_STATUS_TARGET_FAILURE 16 /* Command Response was Target Failure */
#define ISCSI_STATUS_TARGET_DROP 17 /* Target dropped connection */
#define ISCSI_STATUS_SOCKET_ERROR 18 /* Communication failure */
#define ISCSI_STATUS_PARAMETER_MISSING 19 /* A required ioctl parameter is missing */
#define ISCSI_STATUS_PARAMETER_INVALID 20 /* A parameter is malformed (string too long etc.) */
#define ISCSI_STATUS_MAP_FAILED 21 /* Mapping the LUNs failed */
#define ISCSI_STATUS_NO_INITIATOR_NAME 22 /* Initiator name was not set */
#define ISCSI_STATUS_NEGOTIATION_ERROR 23 /* Negotiation failure (invalid key or value) */
#define ISCSI_STATUS_TIMEOUT 24 /* Command timed out (at iSCSI level) */
#define ISCSI_STATUS_PROTOCOL_ERROR 25 /* Internal Error (Protocol error reject) */
#define ISCSI_STATUS_PDU_ERROR 26 /* Internal Error (Invalid PDU field reject) */
#define ISCSI_STATUS_CMD_NOT_SUPPORTED 27 /* Target does not support iSCSI command */
#define ISCSI_STATUS_DRIVER_UNLOAD 28 /* Driver is unloading */
#define ISCSI_STATUS_LOGOUT 29 /* Session was logged out */
#define ISCSI_STATUS_PDUS_LOST 30 /* Excessive PDU loss */
#define ISCSI_STATUS_INVALID_EVENT_ID 31 /* Invalid Event ID */
#define ISCSI_STATUS_EVENT_DEREGISTERED 32 /* Wait for event cancelled by deregistration */
#define ISCSI_STATUS_EVENT_WAITING 33 /* Someone is already waiting for this event */
#define ISCSI_STATUS_TASK_NOT_FOUND 34 /* Task Management: task not found */
#define ISCSI_STATUS_LUN_NOT_FOUND 35 /* Task Management: LUN not found */
#define ISCSI_STATUS_TASK_ALLEGIANT 36 /* Task Management: Task still allegiant */
#define ISCSI_STATUS_CANT_REASSIGN 37 /* Task Management: Task reassignment not supported */
#define ISCSI_STATUS_FUNCTION_UNSUPPORTED 38 /* Task Management: Function unsupported */
#define ISCSI_STATUS_FUNCTION_NOT_AUTHORIZED 39 /* Task Management: Function not authorized */
#define ISCSI_STATUS_FUNCTION_REJECTED 40 /* Task Management: Function rejected */
#define ISCSI_STATUS_UNKNOWN_REASON 41 /* Task Management: Unknown reason code */
#define ISCSI_STATUS_DUPLICATE_ID 42 /* Given ID is a duplicate */
#define ISCSI_STATUS_INVALID_ID 43 /* Given ID was not found */
#define ISCSI_STATUS_TARGET_LOGOUT 44 /* Target requested logout */
#define ISCSI_STATUS_LOGOUT_CID_NOT_FOUND 45 /* Logout error: CID not found */
#define ISCSI_STATUS_LOGOUT_RECOVERY_NS 46 /* Logout error: Recovery not supported */
#define ISCSI_STATUS_LOGOUT_ERROR 47 /* Logout error: Unknown reason */
#define ISCSID_STATUS_SUCCESS 0 /* Indicates success. */
#define ISCSID_STATUS_LIST_EMPTY 1001 /* The requested list is empty. */
#define ISCSID_STATUS_DUPLICATE_NAME 1002 /* The specified name is not unique. */
#define ISCSID_STATUS_GENERAL_ERROR 1003 /* A non-specific error occurred. */
#define ISCSID_STATUS_CONNECT_ERROR 1005 /* Failed to connect to target */
#define ISCSID_STATUS_NO_RESOURCES 1007 /* Could not allocate resources (e.g. memory). */
#define ISCSID_STATUS_INVALID_SESSION_ID 1009 /* Session ID not found */
#define ISCSID_STATUS_INVALID_CONNECTION_ID 1010 /* Connection ID not found */
#define ISCSID_STATUS_NOTIMPL 1012 /* Feature not implemented */
#define ISCSID_STATUS_SOCKET_ERROR 1018 /* Failed to create socket */
#define ISCSID_STATUS_PARAMETER_MISSING 1019 /* A required parameter is missing */
#define ISCSID_STATUS_PARAMETER_INVALID 1020 /* A parameter is malformed (string too long etc.) */
#define ISCSID_STATUS_INVALID_PARAMETER 1020 /* Alternate spelling of above */
#define ISCSID_STATUS_NO_INITIATOR_NAME 1022 /* Initiator name was not set */
#define ISCSID_STATUS_TIMEOUT 1024 /* Request timed out */
#define ISCSID_STATUS_DRIVER_NOT_LOADED 1028 /* Driver not loaded */
#define ISCSID_STATUS_INVALID_REQUEST 1101 /* Unknown request code */
#define ISCSID_STATUS_INVALID_PORTAL_ID 1102 /* Portal ID not found */
#define ISCSID_STATUS_INVALID_TARGET_ID 1103 /* Target ID not found */
#define ISCSID_STATUS_NOT_FOUND 1104 /* Search failed */
#define ISCSID_STATUS_HOST_NOT_FOUND 1105 /* Target address not found */
#define ISCSID_STATUS_HOST_TRY_AGAIN 1106 /* Target address retreival failed, try again later */
#define ISCSID_STATUS_HOST_ERROR 1107 /* Target address invalid */
#define ISCSID_STATUS_NO_TARGETS_FOUND 1108 /* No targets found during refresh */
#define ISCSID_STATUS_INVALID_ISNS_ID 1111 /* iSNS ID not found */
#define ISCSID_STATUS_ISNS_ERROR 1112 /* Problem connecting to iSNS */
#define ISCSID_STATUS_ISNS_SERVER_ERROR 1113 /* iSNS server returned garbage */
#define ISCSID_STATUS_DUPLICATE_ENTRY 1114 /* The specified entry already exists */
#define ISCSID_STATUS_INVALID_INITIATOR_ID 1115 /* Initiator ID not found */
#define ISCSID_STATUS_INITIATOR_BIND_ERROR 1116 /* Bind to initiator portal failed */
#endif /* !_ISCSI_H */

View File

@ -0,0 +1,843 @@
/* $NetBSD: iscsi_globals.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_GLOBALS_H
#define _ISCSI_GLOBALS_H
/*#include "opt_ddb.h" */
#define DDB 1
/* Includes we need in all files */
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/conf.h>
#include <sys/errno.h>
#include <sys/malloc.h>
#include <sys/scsiio.h>
#include <sys/kernel.h>
#include <sys/kthread.h>
#include <sys/systm.h>
#include <sys/rnd.h>
#include <sys/device.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsipi_all.h>
#include <dev/scsipi/scsiconf.h>
#include <dev/scsipi/scsipiconf.h>
#include "iscsi.h"
#include "iscsi_pdu.h"
#include "iscsi_ioctl.h"
/* ------------------------ Code selection constants ------------------------ */
/* #define ISCSI_DEBUG 1 */
#include "iscsi_perf.h"
#include "iscsi_test.h"
/* ------------------------- Global Constants ----------------------------- */
/* Version information */
#define INTERFACE_VERSION 2
#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_STRING "NetBSD iSCSI Software Initiator 20110407"
/*
Various checks are made that the expected cmd Serial Number is less than
the actual command serial number. The extremely paranoid amongst us
believe that a malicious iSCSI server could set this artificially low
and effectively DoS a naive initiator. For this (possibly ludicrous)
reason, I have added the two definitions below (agc, 2011/04/09). The
throttling definition enables a check that the CmdSN is less than the
ExpCmdSN in iscsi_send.c, and is enabled by default. The second definition
effectively says "don't bother testing these values", and is used right
now only in iscsi_send.c.
*/
#define ISCSI_TROTTLING_ENABLED 1
#define ISCSI_SERVER_TRUSTED 1
/*
NOTE: CCBS_PER_SESSION must not exceed 256 due to the way the ITT
is constructed (it has the CCB index in its lower 8 bits). If it should ever
be necessary to increase the number beyond that (which isn't expected),
the corresponding ITT generation and extraction code must be rewritten.
*/
#define CCBS_PER_SESSION 64 /* ToDo: Reasonable number?? */
/*
NOTE: PDUS_PER_CONNECTION is a number that could potentially impact
performance if set too low, as a single command may use up a lot of PDUs for
high values of First/MaxBurstLength and small values of
MaxRecvDataSegmentLength of the target.
*/
#define PDUS_PER_CONNECTION 64 /* ToDo: Reasonable number?? */
/* max outstanding serial nums before we give up on the connection */
#define SERNUM_BUFFER_LENGTH (CCBS_PER_SESSION / 2) /* ToDo: Reasonable?? */
/* The RecvDataSegmentLength for Target->Initiator */
#define DEFAULT_MaxRecvDataSegmentLength (64*1024)
/* Command timeout (reset on received PDU associated with the command's CCB) */
#define COMMAND_TIMEOUT (7 * hz) /* ToDo: Reasonable? (7 seconds) */
#define MAX_CCB_TIMEOUTS 3 /* Max number of tries to resend or SNACK */
#define MAX_CCB_TRIES 9 /* Max number of total tries to recover */
/* Connectionn timeout (reset on every valid received PDU) */
#define CONNECTION_TIMEOUT (2 * hz) /* ToDo: Reasonable? (2 seconds) */
#define CONNECTION_IDLE_TIMEOUT (30 * hz) /* Adjusted to Time2Retain/2 later */
#define MAX_CONN_TIMEOUTS 4 /* Max number of tries to ping a target */
/* Maximum attempts to recover connection */
#define MAX_RECOVERY_ATTEMPTS 2 /* If two attempts don't work, something */
/* probably is seriously broken */
/* PDU flags */
#define PDUF_BUSY 0x01 /* PDU is being sent, don't re-send */
#define PDUF_INQUEUE 0x02 /* PDU is in send queue */
#define PDUF_PRIORITY 0x04 /* Insert PDU at head of queue */
#define PDUF_NOUPDATE 0x10 /* Do not update PDU header/digest (test mode) */
/* CCB Flags */
#define CCBF_COMPLETE 0x01 /* received status */
#define CCBF_RESENT 0x02 /* ccb was resent */
#define CCBF_SENDTARGET 0x04 /* SendTargets text request, not negotiation */
#define CCBF_WAITING 0x08 /* CCB is waiting for MaxCmdSN, wake it up */
#define CCBF_GOT_RSP 0x10 /* Got at least one response to this request */
#define CCBF_REASSIGN 0x20 /* Command can be reassigned */
#define CCBF_OTHERCONN 0x40 /* a logout for a different connection */
/* --------------------------- Global Types ------------------------------- */
/* Connection state */
typedef enum {
/* first three correspond to CSG/NSG coding */
ST_SEC_NEG = 0, /* security negotiation phase */
ST_OP_NEG = 1, /* operational negotiation phase */
ST_FULL_FEATURE = 3, /* full feature phase */
/* rest is internal */
ST_WINDING_DOWN = 4, /* connection termination initiated, logging out */
ST_LOGOUT_SENT = 5, /* logout has been sent */
ST_SETTLING = 6, /* waiting for things to settle down */
ST_IDLE = 7 /* connection is idle (ready to delete) */
} conn_state_t;
/* Logout state */
typedef enum {
NOT_LOGGED_OUT, /* Not logged out */
LOGOUT_SENT, /* Logout was sent */
LOGOUT_SUCCESS, /* Logout succeeded */
LOGOUT_FAILED /* Logout failed */
} logout_state_t;
/* CCB Disposition */
typedef enum {
CCBDISP_UNUSED, /* 0 = In free pool */
CCBDISP_BUSY, /* This CCB is busy, don't allow rx ops */
CCBDISP_NOWAIT, /* Not waiting for anything */
CCBDISP_FREE, /* Free this CCB when done */
CCBDISP_WAIT, /* Calling thread is waiting for completion */
CCBDISP_SCSIPI, /* Call scsipi_done when operation completes */
CCBDISP_DEFER /* Defer waiting until all PDUs have been queued */
} ccb_disp_t;
/* PDU Disposition */
typedef enum {
PDUDISP_UNUSED, /* 0 = In free pool */
PDUDISP_SIGNAL, /* Free this PDU when done and wakeup(pdu) */
PDUDISP_FREE, /* Free this PDU when done */
PDUDISP_WAIT /* Waiting for acknowledge */
} pdu_disp_t;
typedef struct connection_s connection_t;
typedef struct session_s session_t;
typedef struct ccb_s ccb_t;
typedef struct pdu_s pdu_t;
#include "iscsi_testlocal.h"
/* the serial number management structure (a circular buffer) */
typedef struct {
uint32_t ExpSN; /* ExpxxSN (Data or Stat) sent to the target */
uint32_t next_sn; /* next_sn (== ExpSN if no ack is pending) */
int top; /* top of buffer (newest element) */
int bottom; /* bottom of buffer (oldest element) */
uint32_t sernum[SERNUM_BUFFER_LENGTH]; /* the serial numbers */
int ack[SERNUM_BUFFER_LENGTH]; /* acknowledged? */
} sernum_buffer_t;
/*
The per-PDU data structure.
*/
struct pdu_s {
TAILQ_ENTRY(pdu_s) chain; /* freelist or wait list (or no list) */
TAILQ_ENTRY(pdu_s) send_chain;
/* chaining PDUs waiting to be sent */
pdu_disp_t disp; /* what to do with this pdu */
uint32_t flags; /* various processing flags */
pdu_header_t pdu; /* Buffer for PDU associated with cmd */
void *temp_data; /* (free after use) */
uint32_t temp_data_len; /* size of temp data */
struct uio uio; /* UIO structure */
struct iovec io_vec[4];
/* Header + data + data-digest + padding */
struct uio save_uio;
/* UIO structure save for retransmits */
struct iovec save_iovec[4];
/* Header + data + data-digest + padding */
uint32_t data_digest;
/* holds data digest if enabled */
ccb_t *owner;
/* the ccb this PDU belongs to (if any) */
connection_t *connection;
/* the connection this PDU belongs to */
#ifdef ISCSI_TEST_MODE
pdu_header_t mod_pdu;
/* Buffer for modified PDU header (test mode) */
#endif
#ifdef ISCSI_PERFTEST
int perf_index;
/* performance counter index */
perfpoint_t perf_which; /* performance point */
#endif
};
/* the PDU list type */
TAILQ_HEAD(pdu_list_s, pdu_s);
typedef struct pdu_list_s pdu_list_t;
/*
The per-command data structure. Calling it ccb in correspondence
to other HA drivers.
*/
struct ccb_s {
TAILQ_ENTRY(ccb_s) chain;
/* either freelist or waiting list (or no list) */
uint32_t status; /* Status gets entered here */
ccb_disp_t disp; /* what to do with this ccb */
struct callout timeout; /* To make sure it isn't lost */
int num_timeouts;
/* How often we've sent out SNACK without answer */
int total_tries;
/* How often we've tried to recover */
uint32_t ITT;
/* task tag: ITT counter + sess id + CCB index */
sernum_buffer_t DataSN_buf;
/* Received Data Seq nums (read ops only) */
void *par;
/* misc. parameter for this request */
struct scsipi_xfer *xs;
/* the scsipi_xfer for this cmd */
void *temp_data;
/* to hold state (mainly during negotiation) */
void *text_data;
/* holds accumulated text for continued PDUs */
uint32_t text_len;
/* length of text data so far */
uint64_t lun; /* LUN */
uint8_t *cmd; /* SCSI command block */
uint16_t cmdlen; /* SCSI command block length */
bool data_in; /* if this is a read request */
uint8_t *data_ptr; /* data pointer for read/write */
uint32_t data_len; /* total data length */
uint32_t xfer_len; /* data transferred on read */
uint32_t residual; /* residual data size */
void *sense_ptr; /* sense data pointer */
int sense_len_req; /* requested sense data length */
int sense_len_got; /* actual sense data length */
pdu_t *pdu_waiting; /* PDU waiting to be ack'ed */
uint32_t CmdSN; /* CmdSN associated with waiting PDU */
int flags;
connection_t *connection; /* connection for CCB */
session_t *session; /* session for CCB */
#ifdef ISCSI_PERFTEST
int perf_index; /* performance counter index */
#endif
};
/* the CCB list type */
TAILQ_HEAD(ccb_list_s, ccb_s);
typedef struct ccb_list_s ccb_list_t;
/*
Per connection data: the connection structure
*/
#if (__NetBSD_Version__ >= 399000900)
typedef struct lwp *PTHREADOBJ;
#else
typedef struct proc *PTHREADOBJ;
#endif
struct connection_s {
TAILQ_ENTRY(connection_s) connections;
pdu_list_t pdu_pool; /* the free PDU pool */
ccb_list_t ccbs_waiting;
/* CCBs waiting for completion */
pdu_list_t pdus_to_send;
/* the PDUs waiting to be sent */
sernum_buffer_t StatSN_buf;
/* to keep track of received StatSNs */
uint32_t max_transfer;
/* min(MaxRecvDataSegmentLength, MaxBurstLength) */
uint32_t max_firstimmed;
/* 0 if InitialR2T=Yes, else
min of (MaxRecvDataSegmentLength, FirstBurstLength) */
uint32_t max_firstdata;
/* 0 if ImmediateData=No, else min of */
/* (MaxRecvDataSegmentLength, FirstBurstLength) */
uint32_t MaxRecvDataSegmentLength;
/* Target's value */
uint32_t Our_MaxRecvDataSegmentLength;
/* Our own value */
bool HeaderDigest; /* TRUE if doing CRC */
bool DataDigest; /* TRUE if doing CRC */
uint32_t Time2Wait;
/* Negotiated default or logout value */
uint32_t Time2Retain;
/* Negotiated default or logout value */
uint16_t id;
/* connection ID (unique within session) */
conn_state_t state; /* State of connection */
PTHREADOBJ threadobj;
/* proc/thread pointer of socket owner */
struct file *sock; /* the connection's socket */
session_t *session;
/* back pointer to the owning session */
struct lwp *rcvproc; /* receive thread */
struct lwp *sendproc; /* send thread */
uint32_t terminating;
/* if closing down: status */
int recover; /* recovery count */
/* (reset on first successful data transfer) */
bool destroy; /* conn will be destroyed */
bool in_session;
/* if it's linked into the session list */
logout_state_t loggedout;
/* status of logout (for recovery) */
struct callout timeout;
/* Timeout for checking if connection is dead */
int num_timeouts;
/* How often we've sent out a NOP without answer */
uint32_t idle_timeout_val;
/* Connection timeout value when idle */
iscsi_login_parameters_t *login_par;
/* only valid during login */
pdu_t pdu[PDUS_PER_CONNECTION]; /* PDUs */
#ifdef ISCSI_TEST_MODE
test_pars_t *test_pars;
/* connection in test mode if non-NULL */
#endif
};
/* the connection list type */
TAILQ_HEAD(connection_list_s, connection_s);
typedef struct connection_list_s connection_list_t;
/*
Per session data: the session structure
*/
struct session_s {
/* Interface to child drivers.
NOTE: sc_adapter MUST be the first field in this structure so we can
easily get from adapter to session.
*/
struct scsipi_adapter sc_adapter;
struct scsipi_channel sc_channel;
device_t child_dev;
/* the child we're associated with - (NULL if not mapped) */
/* local stuff */
TAILQ_ENTRY(session_s) sessions; /* the list of sessions */
ccb_list_t ccb_pool; /* The free CCB pool */
ccb_list_t ccbs_throttled;
/* CCBs waiting for MaxCmdSN to increase */
uint16_t id; /* session ID (unique within driver) */
uint16_t TSIH; /* Target assigned session ID */
uint32_t CmdSN; /* Current CmdSN */
uint32_t ExpCmdSN; /* Current max ExpCmdSN received */
uint32_t MaxCmdSN; /* Current MaxCmdSN */
/* negotiated values */
uint32_t ErrorRecoveryLevel;
uint32_t FirstBurstLength;
uint32_t MaxBurstLength;
bool ImmediateData;
bool InitialR2T;
uint32_t MaxOutstandingR2T;
uint32_t MaxConnections;
uint32_t DefaultTime2Wait;
uint32_t DefaultTime2Retain;
iscsi_login_session_type_t login_type; /* session type */
/* for send_targets requests */
uint8_t *target_list;
uint32_t target_list_len;
uint32_t conn_id; /* connection ID counter */
uint32_t terminating; /* if closing down: status */
uint32_t active_connections;
/* currently active connections */
uint32_t total_connections;
/* connections associated with this session (active or winding down) */
connection_list_t conn_list; /* the list of connections */
connection_t *mru_connection;
/* the most recently used connection */
uint8_t itt_id; /* counter for use in ITT */
ccb_t ccb[CCBS_PER_SESSION]; /* CCBs */
char tgtname[ISCSI_STRING_LENGTH + 1];
/* iSCSI target name */
};
/* the session list type */
TAILQ_HEAD(session_list_s, session_s);
typedef struct session_list_s session_list_t;
/*
The softc structure. This driver doesn't really need one, because there's
always just one instance, and for the time being it's only loaded as
an LKM (which doesn't create a softc), but we need one to put into the
scsipi interface structures, so here it is.
*/
typedef struct iscsi_softc {
device_t sc_dev;
} iscsi_softc_t;
/*
Event notification structures
*/
typedef struct event_s {
TAILQ_ENTRY(event_s) link; /* next event in queue */
iscsi_event_t event_kind; /* which event */
uint32_t session_id; /* affected session ID */
uint32_t connection_id; /* affected connection ID */
uint32_t reason; /* event reason */
} event_t;
/* the event list entry type */
TAILQ_HEAD(event_list_s, event_s);
typedef struct event_list_s event_list_t;
typedef struct event_handler_s {
TAILQ_ENTRY(event_handler_s) link; /* next handler */
uint32_t id; /* unique ID */
event_list_t events; /* list of events pending */
iscsi_wait_event_parameters_t *waiter; /* waiting parameter */
/* following to detect dead handlers */
event_t *first_in_list;
} event_handler_t;
/* the event list entry type */
TAILQ_HEAD(event_handler_list_s, event_handler_s);
typedef struct event_handler_list_s event_handler_list_t;
/* ------------------------- Global Variables ----------------------------- */
/* In iscsi_main.c */
struct cfattach iscsi_ca; /* the device attach structure */
struct cdevsw iscsi_cdevsw; /* the character device descriptor */
iscsi_softc_t *sc; /* our device pointer */
session_list_t sessions; /* the list of sessions */
connection_list_t cleanup_list; /* connections to clean up */
bool detaching; /* signal to cleanup thread it should exit */
struct lwp *cleanproc; /* pointer to cleanup proc */
uint32_t num_send_threads; /* the number of active send threads */
uint8_t InitiatorName[ISCSI_STRING_LENGTH];
uint8_t InitiatorAlias[ISCSI_STRING_LENGTH];
login_isid_t InitiatorISID;
/* ------------------------- Global Functions ----------------------------- */
#ifdef __NetBSD__
#define GEN_RAND(buffer, len) rnd_extract_data (buffer, len, RND_EXTRACT_ANY)
#else
#define GEN_RAND(buffer, len) get_random_bytes (buffer, len)
#endif
static __inline uint8_t
randb(void)
{
uint8_t buf;
GEN_RAND(&buf, 1);
return buf;
}
/* Debugging and profiling stuff */
#include "iscsi_profile.h"
#ifndef DDB
#define Debugger() panic("should call debugger here (iscsi.c)")
#endif /* ! DDB */
#if defined(ISCSI_PERFTEST)
int perf_level; /* How much info to display */
#define PDEBOUT(x) printf x
#define PDEB(lev,x) { if (perf_level >= lev) printf x ;}
#define PDEBC(conn,lev,x) { { if (perf_level >= lev) printf("S%dC%d: ", \
conn->session->id, conn->id); printf x ;}}
#else
#define PDEBOUT(x)
#define PDEB(lev,x)
#define PDEBC(conn,lev,x)
#endif
#ifdef ISCSI_DEBUG
int debug_level; /* How much debug info to display */
#define DEBOUT(x) printf x
#define DEB(lev,x) { if (debug_level >= lev) printf x ;}
#define DEBC(conn,lev,x) { if (debug_level >= lev) { printf("S%dC%d: ", \
conn->session->id, conn->id); printf x ;}}
void dump(void *buf, int len);
#define STATIC static
#else
#define DEBOUT(x)
#define DEB(lev,x)
#define DEBC(conn,lev,x)
#define dump(a,b)
#define STATIC static
#endif
/* Critical section macros */
#define CS_BEGIN { int s = splbio ();
#define CS_END splx (s); }
/* misc stuff */
#define min(a, b) ((a) < (b)) ? (a) : (b)
#define max(a, b) ((a) < (b)) ? (b) : (a)
/*
Convert unsigned int to 3-byte value (for DataSegmentLength field in PDU)
*/
static __inline void
hton3(uint32_t val, uint8_t *bytes)
{
bytes[0] = (uint8_t) (val >> 16);
bytes[1] = (uint8_t) (val >> 8);
bytes[2] = (uint8_t) val;
}
/*
Convert 3-byte value to unsigned int (for DataSegmentLength field in PDU)
*/
static __inline uint32_t
ntoh3(uint8_t *bytes)
{
return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
}
/*
* Convert uint64 to network byte order (for LUN field in PDU)
*/
static __inline uint64_t
htonq(uint64_t x)
{
#if BYTE_ORDER == LITTLE_ENDIAN
uint8_t *s = (uint8_t *) & x;
return (uint64_t) ((uint64_t) s[0] << 56 | (uint64_t) s[1] << 48 |
(uint64_t) s[2] << 40 | (uint64_t) s[3] << 32 |
(uint64_t) s[4] << 24 | (uint64_t) s[5] << 16 |
(uint64_t) s[6] << 8 | (uint64_t) s[7]);
#else
return x;
#endif
}
#define ntohq(x) htonq(x)
/*
* Serial number buffer empty?
*/
static __inline bool
sn_empty(sernum_buffer_t *buf)
{
return buf->top == buf->bottom;
}
/*
* Serial number compare
*/
static __inline bool
sn_a_lt_b(uint32_t a, uint32_t b)
{
return (a < b && !((b - a) & 0x80000000)) ||
(a > b && ((a - b) & 0x80000000));
}
static __inline bool
sn_a_le_b(uint32_t a, uint32_t b)
{
return (a <= b && !((b - a) & 0x80000000)) ||
(a >= b && ((a - b) & 0x80000000));
}
/* Version dependencies */
#if (__NetBSD_Version__ >= 399000900)
#define PROCP(obj) (obj->l_proc)
#else
#define PROCP(obj) obj
#define UIO_SETUP_SYSSPACE(uio) (uio)->uio_segflg = UIO_SYSSPACE
#endif
#if (__NetBSD_Version__ >= 106000000)
# ifdef ISCSI_TEST_MODE
#define SET_CCB_TIMEOUT(conn, ccb, tout) do { \
if (test_ccb_timeout (conn)) { \
callout_schedule(&ccb->timeout, tout); \
} \
} while (/*CONSTCOND*/ 0)
# else
#define SET_CCB_TIMEOUT(conn, ccb, tout) callout_schedule(&ccb->timeout, tout)
# endif
#else
/* no test mode for 1.5 */
#define SET_CCB_TIMEOUT(conn, ccb, tout) \
callout_reset(&ccb->timeout, tout, ccb_timeout, ccb)
#endif
#if (__NetBSD_Version__ >= 106000000)
# ifdef ISCSI_TEST_MODE
#define SET_CONN_TIMEOUT(conn, tout) do { \
if (test_conn_timeout (conn)) { \
callout_schedule(&conn->timeout, tout); \
} \
} while (/*CONSTCOND*/0)
# else
#define SET_CONN_TIMEOUT(conn, tout) callout_schedule(&conn->timeout, tout)
# endif
#else
/* no test mode for 1.5 */
#define SET_CONN_TIMEOUT(conn, tout) \
callout_reset(&conn->timeout, tout, connection_timeout, conn)
#endif
/* in iscsi_ioctl.c */
/* Parameter for logout is reason code in logout PDU, -1 for don't send logout */
#define NO_LOGOUT -1
#define LOGOUT_SESSION 0
#define LOGOUT_CONNECTION 1
#define RECOVER_CONNECTION 2
void add_event(iscsi_event_t, uint32_t, uint32_t, uint32_t);
void kill_connection(connection_t *, uint32_t, int, bool);
void kill_session(session_t *, uint32_t, int, bool);
void kill_all_sessions(void);
void handle_connection_error(connection_t *, uint32_t, int);
void iscsi_cleanup_thread(void *);
#ifndef ISCSI_MINIMAL
uint32_t map_databuf(struct proc *, void **, uint32_t);
void unmap_databuf(struct proc *, void *, uint32_t);
#endif
int iscsiioctl(dev_t, u_long, void *, int, PTHREADOBJ);
session_t *find_session(uint32_t);
connection_t *find_connection(session_t *, uint32_t);
/* in iscsi_main.c */
/*void iscsiattach(void *); */
int iscsidetach(device_t, int);
void iscsi_done(ccb_t *);
int map_session(session_t *);
void unmap_session(session_t *);
/* in iscsi_send.c */
void iscsi_send_thread(void *);
connection_t *assign_connection(session_t *session, bool waitok);
void resend_pdu(ccb_t *);
int send_login(connection_t *);
int send_logout(connection_t *, connection_t *, int, bool);
int send_data_out(connection_t *, pdu_t *, ccb_t *, ccb_disp_t, bool);
void send_run_xfer(session_t *, struct scsipi_xfer *);
int send_send_targets(session_t *, uint8_t *);
int send_task_management(connection_t *, ccb_t *, struct scsipi_xfer *, int);
void negotiate_login(connection_t *, pdu_t *, ccb_t *);
void acknowledge_text(connection_t *, pdu_t *, ccb_t *);
void start_text_negotiation(connection_t *);
void negotiate_text(connection_t *, pdu_t *, ccb_t *);
int send_nop_out(connection_t *, pdu_t *);
void snack_missing(connection_t *, ccb_t *, uint8_t, uint32_t, uint32_t);
void send_snack(connection_t *, pdu_t *, ccb_t *, uint8_t);
int send_send_targets(session_t *, uint8_t *);
void send_command(ccb_t *, ccb_disp_t, bool, bool);
#ifndef ISCSI_MINIMAL
int send_io_command(session_t *, uint64_t, scsireq_t *, bool, uint32_t);
#endif
void connection_timeout(void *);
void ccb_timeout(void *);
/* in iscsi_rcv.c */
void iscsi_rcv_thread(void *);
/* in iscsi_utils.c */
uint32_t gen_digest(void *, int);
uint32_t gen_digest_2(void *, int, void *, int);
void create_ccbs(session_t *);
ccb_t *get_ccb(connection_t *, bool);
void free_ccb(ccb_t *);
void wake_ccb(ccb_t *);
void complete_ccb(ccb_t *);
void create_pdus(connection_t *);
pdu_t *get_pdu(connection_t *);
pdu_t *get_pdu_c(connection_t *, bool);
void free_pdu(pdu_t *);
void init_sernum(sernum_buffer_t *);
int add_sernum(sernum_buffer_t *, uint32_t);
uint32_t ack_sernum(sernum_buffer_t *, uint32_t);
/* in iscsi_text.c */
int assemble_login_parameters(connection_t *, ccb_t *, pdu_t *);
int assemble_security_parameters(connection_t *, ccb_t *, pdu_t *, pdu_t *);
int assemble_negotiation_parameters(connection_t *, ccb_t *, pdu_t *, pdu_t *);
int init_text_parameters(connection_t *, ccb_t *);
int assemble_send_targets(pdu_t *, uint8_t *);
void set_negotiated_parameters(ccb_t *);
#endif /* !_ISCSI_GLOBALS_H */

1647
sys/dev/iscsi/iscsi_ioctl.c Normal file

File diff suppressed because it is too large Load Diff

439
sys/dev/iscsi/iscsi_ioctl.h Normal file
View File

@ -0,0 +1,439 @@
/* $NetBSD: iscsi_ioctl.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_IOCTL_H
#define _ISCSI_IOCTL_H
#include <dev/iscsi/iscsi.h>
/* ================== Interface Structures ======================== */
/* ===== Login, add_connection, and restore_connection ===== */
typedef struct {
uint32_t status;
int socket;
struct {
int HeaderDigest:1;
int DataDigest:1;
int MaxConnections:1;
int DefaultTime2Wait:1;
int DefaultTime2Retain:1;
int MaxRecvDataSegmentLength:1;
int auth_info:1;
int user_name:1;
int password:1;
int target_password:1;
int TargetName:1;
int TargetAlias:1;
int ErrorRecoveryLevel:1;
} is_present;
iscsi_auth_info_t auth_info;
iscsi_login_session_type_t login_type;
iscsi_digest_t HeaderDigest;
iscsi_digest_t DataDigest;
uint32_t session_id;
uint32_t connection_id;
uint32_t MaxRecvDataSegmentLength;
uint16_t MaxConnections;
uint16_t DefaultTime2Wait;
uint16_t DefaultTime2Retain;
uint16_t ErrorRecoveryLevel;
void *user_name;
void *password;
void *target_password;
void *TargetName;
void *TargetAlias;
} iscsi_login_parameters_t;
/*
status
Contains, on return, the result of the command.
socket
A handle to the open TCP connection to the target portal.
is_present
Contains a bitfield that indicates which members of the
iscsi_login_parameters structure contain valid data.
auth_info
Is a bitfield of parameters for authorization.
The members are
mutual_auth Indicates that authentication should be mutual, i.e.
the initiator should authenticate the target, and the target
should authenticate the initiator. If not specified, the target
will authenticate the initiator only.
is_secure Indicates that the connection is secure.
auth_number Indicates the number of elements in auth_type.
When 0, no authentication will be used.
auth_type
Contains up to ISCSI_AUTH_OPTIONS enumerator values of type
iscsi_auth_types that indicates the authentication method that should
be used to establish a login connection (none, CHAP, KRB5, etc.), in
order of priority. The first element is the most preferred method, the
last element the least preferred.
login_type
Contains an enumerator value of type login_session_type that indicates
the type of logon session (discovery, informational, or full featured).
HeaderDigest
Indicates which digest (if any) to use for the PDU header.
DataDigest
Indicates which digest (if any) to use for the PDU data.
session_id
Login: OUT: Receives an integer that identifies the session.
Add_connection, Restore_connection: IN: Session ID.
connection_id
Login, Add_connection: OUT: Receives an integer that identifies the
connection.
Restore_connection: IN: Connection ID.
MaxRecvDataSegmentLength
Allows limiting or extending the maximum receive data segment length.
Must contain a value between 512 and 2**24-1 if specified.
MaxConnections
Contains a value between 1 and 65535 that specifies the maximum number
of connections to target devices that can be associated with a single
logon session. A value of 0 indicates that there no limit to the
number of connections.
DefaultTime2Wait
Specifies the minimum time to wait, in seconds, before attempting to
reconnect or reassign a connection that has been dropped.
The default is 2.
DefaultTime2Retain
Specifies the maximum time, in seconds, allowed to reassign a
connection after the initial wait indicated in DefaultTime2Retain has
elapsed. The default is 20.
ErrorRecoveryLevel
Specifies the desired error recovery level for the session.
The default and maximum is 2.
user_name
Sets the user (or CHAP) name to use during login authentication of the
initiator (zero terminated UTF-8 string). Default is initiator name.
password
Contains the password to use during login authentication of the
initiator (zero terminated UTF-8 string). Required if authentication
is requested.
target_password
Contains the password to use during login authentication of the target
(zero terminated UTF-8 string). Required if mutual authentication is
requested.
TargetName
Indicates the name of the target with which to establish the logon
session (zero terminated UTF-8 string).
TargetAlias
Receives the target alias as a zero terminated UTF-8 string. When
present, the buffer must be of size ISCSI_STRING_LENGTH.
*/
/* ===== Logout ===== */
typedef struct {
uint32_t status;
uint32_t session_id;
} iscsi_logout_parameters_t;
/*
status
Contains, on return, the result of the command.
session_id
Contains an integer that identifies the session.
*/
/* ===== remove_connection ===== */
typedef struct {
uint32_t status;
uint32_t session_id;
uint32_t connection_id;
} iscsi_remove_parameters_t;
/*
status
Contains, on return, the result of the command.
session_id
Contains an integer that identifies the session.
connection_id
Contains an integer that identifies the connection.
*/
/* ===== connection status ===== */
typedef struct {
uint32_t status;
uint32_t session_id;
uint32_t connection_id;
} iscsi_conn_status_parameters_t;
/*
status
Contains, on return, the result of the command.
session_id
Contains an integer that identifies the session.
connection_id
Contains an integer that identifies the connection.
*/
/* ===== io_command ===== */
typedef struct {
uint32_t status;
uint32_t session_id;
uint32_t connection_id;
struct {
int immediate:1;
} options;
uint64_t lun;
scsireq_t req;
} iscsi_iocommand_parameters_t;
/*
status
Contains, on return, the result of the command (an ISCSI_STATUS code).
lun
Indicates which of the target's logical units should provide the data.
session_id
Contains an integer that identifies the session.
connection_id
Contains an integer that identifies the connection.
This parameter is optional and should only be used for test purposes.
options
A bitfield indicating options for the command.
The members are
immediate Indicates that the command should be sent
immediately, ahead of any queued requests.
req
Contains the parameters for the request as defined in sys/scsiio.h
typedef struct scsireq {
u_long flags;
u_long timeout;
uint8_t cmd[16];
uint8_t cmdlen;
void * databuf;
u_long datalen;
u_long datalen_used;
uint8_t sense[SENSEBUFLEN];
uint8_t senselen;
uint8_t senselen_used;
uint8_t status;
uint8_t retsts;
int error;
} scsireq_t;
flags
Indicates request status and type.
timeout
Indicates a timeout value (reserved).
cmd
SCSI command buffer.
cmdlen
Length of SCSI command in cmd.
databuf
Pointer to user-space buffer that holds the data
read or written by the SCSI command.
datalen
Indicates the size in bytes of the buffer at databuf.
datalen_used
Returns the number of bytes actually read or written.
sense
Sense data buffer.
senselen
Indicates the requested size of sense data. Must not exceed
SENSEBUFLEN (48).
senselen_used
Contains, on return, the number of bytes written to sense.
status
Contains, on return, the original SCSI status (reserved).
retsts
Contains, on return, the status of the command as defined in scsiio.h.
error
Contains, on return, the original SCSI error bits (reserved).
*/
/* ===== send_targets ===== */
typedef struct {
uint32_t status;
uint32_t session_id;
void *response_buffer;
uint32_t response_size;
uint32_t response_used;
uint32_t response_total;
uint8_t key[ISCSI_STRING_LENGTH];
} iscsi_send_targets_parameters_t;
/*
status
Contains, on return, the result of the command.
session_id
Contains an integer that identifies the session.
response_buffer
User mode address of buffer to hold the response data retrieved by
the iSCSI send targets command.
response_size
Contains, on input, the size in bytes of the buffer at
response_buffer. If this is 0, the command will execute the
SendTargets request, and return (in response_total) the number of
bytes required.
response_used
Contains, on return, the number of bytes actually retrieved to
response_buffer.
response_total
Contains, on return, the total number of bytes required to hold the
complete list of targets. This may be larger than response_size.
key
Specifies the SendTargets key value ("All", <target name>, or empty).
*/
/* ===== set_node_name ===== */
typedef struct {
uint32_t status;
uint8_t InitiatorName[ISCSI_STRING_LENGTH];
uint8_t InitiatorAlias[ISCSI_STRING_LENGTH];
uint8_t ISID[6];
} iscsi_set_node_name_parameters_t;
/*
status
Contains, on return, the result of the command.
InitiatorName
Specifies the InitiatorName used during login. Required.
InitiatorAlias
Specifies the InitiatorAlias for use during login. May be empty.
ISID
Specifies the ISID (a 6 byte binary value) for use during login.
May be zero (all bytes) for the initiator to use a default value.
*/
/* ===== register_event and deregister_event ===== */
typedef struct {
uint32_t status;
uint32_t event_id;
} iscsi_register_event_parameters_t;
/*
status
Contains, on return, the result of the command.
event_id
Returns driver-assigned event ID to be used in
subsequent calls to wait_event and deregister_event.
*/
/* ===== wait_event ===== */
typedef enum {
ISCSI_SESSION_TERMINATED = 1,
ISCSI_CONNECTION_TERMINATED,
ISCSI_RECOVER_CONNECTION,
ISCSI_DRIVER_TERMINATING
} iscsi_event_t;
/*
Driver Events
ISCSI_SESSION_TERMINATED
The specified session (including all of its associated connections)
has been terminated.
ISCSI_CONNECTION_TERMINATED
The specified connection has been terminated.
ISCSI_RECOVER_CONNECTION
The application should attempt to recover the given connection.
ISCSI_DRIVER_TERMINATING
The driver is unloading.
The application MUST call ISCSI_DEREGISTER_EVENT as soon as possible
after receiving this event. After performing the deregister IOCTL,
the application must no longer attempt to access the driver.
*/
typedef struct {
uint32_t status;
uint32_t event_id;
iscsi_event_t event_kind;
uint32_t session_id;
uint32_t connection_id;
uint32_t reason;
} iscsi_wait_event_parameters_t;
/*
status
Contains, on return, the result of the command.
event_id
Driver assigned event ID.
event_kind
Identifies the event.
session_id
Identifies the affected session (0 for DRIVER_TERMINATING).
connection_id
Identifies the affected connection (0 for DRIVER_TERMINATING).
reason
Identifies the termination reason (ISCSI status code).
*/
typedef struct {
uint32_t status;
uint16_t interface_version;
uint16_t major;
uint16_t minor;
uint8_t version_string[ISCSI_STRING_LENGTH];
} iscsi_get_version_parameters_t;
/*
status
Contains, on return, the result of the command.
interface_version
Updated when interface changes. Current Version is 2.
major
Major version number.
minor
Minor version number.
version_string
Displayable version string (zero terminated).
*/
/* ========================= IOCTL Codes =========================== */
#define ISCSI_GET_VERSION _IOWR(0, 1, iscsi_get_version_parameters_t)
#define ISCSI_LOGIN _IOWR(0, 2, iscsi_login_parameters_t)
#define ISCSI_LOGOUT _IOWR(0, 3, iscsi_logout_parameters_t)
#define ISCSI_ADD_CONNECTION _IOWR(0, 4, iscsi_login_parameters_t)
#define ISCSI_RESTORE_CONNECTION _IOWR(0, 5, iscsi_login_parameters_t)
#define ISCSI_REMOVE_CONNECTION _IOWR(0, 6, iscsi_remove_parameters_t)
#define ISCSI_CONNECTION_STATUS _IOWR(0, 7, iscsi_conn_status_parameters_t)
#define ISCSI_SEND_TARGETS _IOWR(0, 8, iscsi_send_targets_parameters_t)
#define ISCSI_SET_NODE_NAME _IOWR(0, 9, iscsi_set_node_name_parameters_t)
#define ISCSI_IO_COMMAND _IOWR(0, 10, iscsi_iocommand_parameters_t)
#define ISCSI_REGISTER_EVENT _IOWR(0, 11, iscsi_register_event_parameters_t)
#define ISCSI_DEREGISTER_EVENT _IOWR(0, 12, iscsi_register_event_parameters_t)
#define ISCSI_WAIT_EVENT _IOWR(0, 13, iscsi_wait_event_parameters_t)
#define ISCSI_POLL_EVENT _IOWR(0, 14, iscsi_wait_event_parameters_t)
#endif /* !_ISCSI_IOCTL_H */

580
sys/dev/iscsi/iscsi_main.c Normal file
View File

@ -0,0 +1,580 @@
/* $netBSD: iscsi_main.c,v 1.1.1.1 2011/05/02 07:01:11 agc Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "iscsi_globals.h"
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/kmem.h>
#include <sys/socketvar.h>
/*------------------------- Global Variables ------------------------*/
extern struct cfdriver iscsi_cd;
#if defined(ISCSI_DEBUG)
int debug_level = ISCSI_DEBUG;
#endif
#if defined(ISCSI_PERFTEST)
int perf_level = 0;
#endif
/* Device Structure */
iscsi_softc_t *sc = NULL;
/* the list of sessions */
session_list_t sessions = TAILQ_HEAD_INITIALIZER(sessions);
/* connections to clean up */
connection_list_t cleanup_list = TAILQ_HEAD_INITIALIZER(cleanup_list);
bool detaching = FALSE;
struct lwp *cleanproc = NULL;
/* the number of active send threads (for cleanup thread) */
uint32_t num_send_threads = 0;
/* Our node name, alias, and ISID */
uint8_t InitiatorName[ISCSI_STRING_LENGTH] = "";
uint8_t InitiatorAlias[ISCSI_STRING_LENGTH] = "";
login_isid_t InitiatorISID;
/******************************************************************************/
/*
System interface: autoconf and device structures
*/
void iscsiattach(int);
void iscsi_attach(device_t parent, device_t self, void *aux);
int iscsi_match(device_t, cfdata_t, void *);
int iscsi_detach(device_t, int);
CFATTACH_DECL_NEW(iscsi, sizeof(struct iscsi_softc), iscsi_match, iscsi_attach,
iscsi_detach, NULL);
int iscsiopen(dev_t, int, int, PTHREADOBJ);
int iscsiclose(dev_t, int, int, PTHREADOBJ);
struct cdevsw iscsi_cdevsw = {
iscsiopen, iscsiclose,
noread, nowrite,
iscsiioctl, nostop, notty, nopoll, nommap, nokqfilter, D_OTHER
};
/******************************************************************************/
STATIC void iscsi_scsipi_request(struct scsipi_channel *,
scsipi_adapter_req_t, void *);
STATIC void iscsi_minphys(struct buf *);
/******************************************************************************/
/*******************************************************************************
* Open and Close device interfaces. We don't really need them, because we don't
* have to keep track of device opens and closes from userland. But apps can't
* call ioctl without a handle to the device, and the kernel doesn't hand out
* handles without an open routine in the driver. So here they are in all their
* glory...
*******************************************************************************/
int
iscsiopen(dev_t dev, int flag, int mode, PTHREADOBJ p)
{
DEB(99, ("ISCSI Open\n"));
return 0;
}
int
iscsiclose(dev_t dev, int flag, int mode, PTHREADOBJ p)
{
DEB(99, ("ISCSI Close\n"));
return 0;
}
/******************************************************************************/
/*
* The config Match routine.
* Not much to do here, either - this is a pseudo-device.
*/
int
iscsi_match(device_t self, cfdata_t cfdata, void *arg)
{
return 1;
}
/*
* iscsiattach:
* Only called when statically configured into a kernel
*/
void
iscsiattach(int n)
{
int err;
cfdata_t cf;
err = config_cfattach_attach(iscsi_cd.cd_name, &iscsi_ca);
if (err) {
aprint_error("%s: couldn't register cfattach: %d\n",
iscsi_cd.cd_name, err);
config_cfdriver_detach(&iscsi_cd);
return;
}
if (n > 1)
aprint_error("%s: only one device supported\n",
iscsi_cd.cd_name);
cf = kmem_alloc(sizeof(struct cfdata), KM_NOSLEEP);
if (cf == NULL) {
aprint_error("%s: couldn't allocate cfdata\n",
iscsi_cd.cd_name);
return;
}
cf->cf_name = iscsi_cd.cd_name;
cf->cf_atname = iscsi_cd.cd_name;
cf->cf_unit = 0;
cf->cf_fstate = FSTATE_NOTFOUND;
(void)config_attach_pseudo(cf);
return;
}
/*
* iscsi_attach:
* One-time inits go here. Not much for now, probably even less later.
*/
void
iscsi_attach(device_t parent, device_t self, void *aux)
{
DEBOUT(("ISCSI: iscsi_attach, parent=%p, self=%p, aux=%p\n", parent,
self, aux));
sc = (iscsi_softc_t *) device_private(self);
sc->sc_dev = self;
if (kthread_create(PRI_NONE, 0, NULL, iscsi_cleanup_thread,
NULL, &cleanproc, "Cleanup") != 0) {
panic("Can't create cleanup thread!");
}
aprint_normal("%s: attached. major = %d\n", iscsi_cd.cd_name,
cdevsw_lookup_major(&iscsi_cdevsw));
}
/*
* iscsi_detach:
* Cleanup.
*/
int
iscsi_detach(device_t self, int flags)
{
DEBOUT(("ISCSI: detach\n"));
kill_all_sessions();
detaching = TRUE;
while (cleanproc != NULL) {
wakeup(&cleanup_list);
tsleep(&cleanup_list, PWAIT, "detach_wait", 20);
}
return 0;
}
/******************************************************************************/
typedef struct quirktab_t {
const char *tgt;
size_t tgtlen;
const char *iqn;
size_t iqnlen;
uint32_t quirks;
} quirktab_t;
static const quirktab_t quirktab[] = {
{ "StarWind", 8,
"iqn.2008-08.com.starwindsoftware", 32,
PQUIRK_ONLYBIG },
{ "UNH", 3,
"iqn.2002-10.edu.unh.", 20,
PQUIRK_NOBIGMODESENSE |
PQUIRK_NOMODESENSE |
PQUIRK_NOSYNCCACHE },
{ "NetBSD", 6,
"iqn.1994-04.org.netbsd.", 23,
0 },
{ "Unknown", 7,
"unknown", 7,
0 },
{ NULL, 0, NULL, 0, 0 }
};
/* loop through the quirktab looking for a match on target name */
static const quirktab_t *
getquirks(const char *iqn)
{
const quirktab_t *qp;
if (iqn == NULL) {
iqn = "unknown";
}
for (qp = quirktab ; qp->iqn ; qp++) {
if (strncmp(qp->iqn, iqn, qp->iqnlen) == 0) {
break;
}
}
return qp;
}
/******************************************************************************/
/*
* map_session
* This (indirectly) maps the existing LUNs for a target to SCSI devices
* by going through config_found to tell any child drivers that there's
* a new adapter.
* Note that each session is equivalent to a SCSI adapter.
*
* Parameter: the session pointer
*
* Returns: 1 on success, 0 on failure
*
* ToDo: Figuring out how to handle more than one LUN. It appears that
* the NetBSD SCSI LUN discovery doesn't use "report LUNs", and instead
* goes through the LUNs sequentially, stopping somewhere on the way if it
* gets an error. We may have to do some LUN mapping in here if this is
* really how things work.
*/
int
map_session(session_t *session)
{
struct scsipi_adapter *adapt = &session->sc_adapter;
struct scsipi_channel *chan = &session->sc_channel;
const quirktab_t *tgt;
if (sc == NULL) {
/* we haven't gone through the config process */
/* (shouldn't happen) */
DEBOUT(("Map: No device pointer!\n"));
return 0;
}
/*
* Fill in the scsipi_adapter.
*/
adapt->adapt_dev = sc->sc_dev;
adapt->adapt_nchannels = 1;
adapt->adapt_request = iscsi_scsipi_request;
adapt->adapt_minphys = iscsi_minphys;
adapt->adapt_openings = CCBS_PER_SESSION;
adapt->adapt_max_periph = CCBS_PER_SESSION;
/*
* Fill in the scsipi_channel.
*/
if ((tgt = getquirks(chan->chan_name)) == NULL) {
tgt = getquirks("unknown");
}
chan->chan_name = tgt->tgt;
chan->chan_defquirks = tgt->quirks;
chan->chan_adapter = adapt;
chan->chan_bustype = &scsi_bustype;
chan->chan_channel = 0;
chan->chan_flags = SCSIPI_CHAN_NOSETTLE;
chan->chan_ntargets = 1;
chan->chan_nluns = 16; /* ToDo: ??? */
chan->chan_id = session->id;
session->child_dev = config_found(sc->sc_dev, chan, scsiprint);
return session->child_dev != NULL;
}
/*
* unmap_session
* This (indirectly) unmaps the existing all LUNs for a target by
* telling the config system that the adapter has detached.
*
* Parameter: the session pointer
*/
void
unmap_session(session_t *session)
{
device_t dev;
if ((dev = session->child_dev) != NULL) {
session->child_dev = NULL;
config_detach(dev, DETACH_FORCE);
}
}
/******************************************************************************/
/*****************************************************************************
* SCSI interface routines
*****************************************************************************/
/*
* iscsi_scsipi_request:
* Perform a request for the SCSIPI layer.
*/
void
iscsi_scsipi_request(struct scsipi_channel *chan, scsipi_adapter_req_t req,
void *arg)
{
struct scsipi_adapter *adapt = chan->chan_adapter;
struct scsipi_xfer *xs;
session_t *session;
int flags;
session = (session_t *) adapt; /* adapter is first field in session */
switch (req) {
case ADAPTER_REQ_RUN_XFER:
DEB(9, ("ISCSI: scsipi_request RUN_XFER\n"));
xs = arg;
flags = xs->xs_control;
if ((flags & XS_CTL_POLL) != 0) {
xs->error = XS_DRIVER_STUFFUP;
DEBOUT(("Run Xfer request with polling\n"));
scsipi_done(xs);
return;
}
/*
* NOTE: It appears that XS_CTL_DATA_UIO is not actually used anywhere.
* Since it really would complicate matters to handle offsets
* into scatter-gather lists, and a number of other drivers don't
* handle uio-based data as well, XS_CTL_DATA_UIO isn't
* implemented in this driver (at least for now).
*/
if (flags & XS_CTL_DATA_UIO) {
xs->error = XS_DRIVER_STUFFUP;
DEBOUT(("Run Xfer with data in UIO\n"));
scsipi_done(xs);
return;
}
send_run_xfer(session, xs);
DEB(9, ("scsipi_req returns\n"));
return;
case ADAPTER_REQ_GROW_RESOURCES:
DEBOUT(("ISCSI: scsipi_request GROW_RESOURCES\n"));
return;
case ADAPTER_REQ_SET_XFER_MODE:
DEB(5, ("ISCSI: scsipi_request SET_XFER_MODE\n"));
return;
default:
break;
}
DEBOUT(("ISCSI: scsipi_request with invalid REQ code %d\n", req));
}
/* cap the transfer at 64K */
#define ISCSI_MAX_XFER 65536
/*
* iscsi_minphys:
* Limit a transfer to our maximum transfer size.
*/
void
iscsi_minphys(struct buf *bp)
{
if (bp->b_bcount > ISCSI_MAX_XFER) {
bp->b_bcount = ISCSI_MAX_XFER;
}
}
/*****************************************************************************
* SCSI job execution helper routines
*****************************************************************************/
/*
* iscsi_done:
*
* A CCB has completed execution. Pass the status back to the
* upper layer.
*/
void
iscsi_done(ccb_t *ccb)
{
struct scsipi_xfer *xs = ccb->xs;
/*DEBOUT (("iscsi_done\n")); */
if (xs != NULL) {
xs->resid = ccb->residual;
switch (ccb->status) {
case ISCSI_STATUS_SUCCESS:
xs->error = 0;
break;
case ISCSI_STATUS_CHECK_CONDITION:
xs->error = XS_SENSE;
xs->error = XS_SENSE;
#ifdef ISCSI_DEBUG
{
uint8_t *s = (uint8_t *) (&xs->sense);
DEB(5, ("Scsipi_done, error=XS_SENSE, sense data=%02x "
"%02x %02x %02x...\n",
s[0], s[1], s[2], s[3]));
}
#endif
break;
case ISCSI_STATUS_TARGET_BUSY:
xs->error = XS_BUSY;
break;
case ISCSI_STATUS_SOCKET_ERROR:
case ISCSI_STATUS_TIMEOUT:
xs->error = XS_SELTIMEOUT;
break;
default:
xs->error = XS_DRIVER_STUFFUP;
break;
}
DEB(99, ("Calling scsipi_done (%p), err = %d\n", xs, xs->error));
scsipi_done(xs);
DEB(99, ("scsipi_done returned\n"));
}
free_ccb(ccb);
}
/* Kernel Module support */
#ifdef _MODULE
#include <sys/module.h>
MODULE(MODULE_CLASS_DRIVER, iscsi, NULL);
static const struct cfiattrdata ibescsi_info = { "scsi", 1,
{{"channel", "-1", -1},}
};
static const struct cfiattrdata *const iscsi_attrs[] = { &ibescsi_info, NULL };
CFDRIVER_DECL(iscsi, DV_DULL, iscsi_attrs);
static struct cfdata iscsi_cfdata[] = {
{
.cf_name = "iscsi",
.cf_atname = "iscsi",
.cf_unit = 0, /* Only unit 0 is ever used */
.cf_fstate = FSTATE_NOTFOUND,
.cf_loc = NULL,
.cf_flags = 0,
.cf_pspec = NULL,
},
{ NULL, NULL, 0, 0, NULL, 0, NULL }
};
static int
iscsi_modcmd(modcmd_t cmd, void *arg)
{
devmajor_t cmajor = NODEVMAJOR, bmajor = NODEVMAJOR;
int error;
switch (cmd) {
case MODULE_CMD_INIT:
error = config_cfdriver_attach(&iscsi_cd);
if (error) {
return error;
}
error = config_cfattach_attach(iscsi_cd.cd_name, &iscsi_ca);
if (error) {
config_cfdriver_detach(&iscsi_cd);
aprint_error("%s: unable to register cfattach\n",
iscsi_cd.cd_name);
return error;
}
error = config_cfdata_attach(iscsi_cfdata, 1);
if (error) {
aprint_error("%s: unable to attach cfdata\n",
iscsi_cd.cd_name);
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
config_cfdriver_detach(&iscsi_cd);
return error;
}
error = devsw_attach(iscsi_cd.cd_name, NULL, &bmajor,
&iscsi_cdevsw, &cmajor);
if (error) {
aprint_error("%s: unable to register devsw\n",
iscsi_cd.cd_name);
config_cfdata_detach(iscsi_cfdata);
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
config_cfdriver_detach(&iscsi_cd);
return error;
}
if (config_attach_pseudo(iscsi_cfdata) == NULL) {
aprint_error("%s: config_attach_pseudo failed\n",
iscsi_cd.cd_name);
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
config_cfdriver_detach(&iscsi_cd);
return ENXIO;
}
return 0;
break;
case MODULE_CMD_FINI:
error = config_cfdata_detach(iscsi_cfdata);
if (error)
return error;
config_cfattach_detach(iscsi_cd.cd_name, &iscsi_ca);
config_cfdriver_detach(&iscsi_cd);
devsw_detach(NULL, &iscsi_cdevsw);
return 0;
break;
default:
return ENOTTY;
break;
}
}
#endif /* _MODULE */

442
sys/dev/iscsi/iscsi_pdu.h Normal file
View File

@ -0,0 +1,442 @@
/* $NetBSD: iscsi_pdu.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_PDU_H
#define _ISCSI_PDU_H
#define BHS_SIZE 48 /* Basic Header Segment (without digest) */
#define PDU_HEADER_SIZE 52 /* PDU Header with Digest */
#define OP_IMMEDIATE 0x40 /* Bit 1 in Opcode field: immediate delivery */
#define OPCODE_MASK 0x3f /* Mask for opcode */
/* PDU Flags field */
#define FLAG_FINAL 0x80 /* Bit 0: Final PDU in sequence */
#define FLAG_TRANSIT 0x80 /* Bit 0: Transit to next login phase */
#define FLAG_CONTINUE 0x40 /* Bit 1: Continue PDU */
#define FLAG_ACK 0x40 /* Bit 1: Acknowledge */
#define FLAG_READ 0x40 /* Bit 1: Read Data */
#define FLAG_WRITE 0x20 /* Bit 2: Write Data */
#define FLAG_BIDI_OFLO 0x10 /* Bit 3: Bidirectional Read Residual Oflow */
#define FLAG_BIDI_UFLOW 0x08 /* Bit 4: Bidirectional Read Residual Uflow */
#define FLAG_OVERFLOW 0x04 /* Bit 5: Residual Overflow */
#define FLAG_UNDERFLOW 0x02 /* Bit 6: Residual Underflow */
#define FLAG_STATUS 0x01 /* Bit 7: Command Status is valid */
/* CSG/NSG flag field codes */
#define SG_SECURITY_NEGOTIATION 0
#define SG_LOGIN_OPERATIONAL_NEGOTIATION 1
#define SG_FULL_FEATURE_PHASE 3
#define CSG_SHIFT 2 /* shift factor for CSG field */
#define SG_MASK 3 /* mask for CSG (after shift) and NSG */
#define NEXT_PHASE(ph) ((!ph) ? 1 : 3) /* no phase 2 */
/* Task Management Function Codes (in Flags byte) */
#define ABORT_TASK 1
#define ABORT_TASK_SET 2
#define CLEAR_ACA 3
#define CLEAR_TASK_SET 4
#define LOGICAL_UNIT_RESET 5
#define TARGET_WARM_RESET 6
#define TARGET_COLD_RESET 7
#define TASK_REASSIGN 8
/* ISID T-Field (first byte) */
#define T_FORMAT_OUI 0x00
#define T_FORMAT_EN 0x40
#define T_FORMAT_RANDOM 0x80
/* Task Attributes */
#define ATTR_UNTAGGED 0
#define ATTR_SIMPLE 1
#define ATTR_ORDERED 2
#define ATTR_HEAD_OF_QUEUE 3
#define ATTR_ACA 4
/* Initiator Opcodes */
#define IOP_NOP_Out 0x00
#define IOP_SCSI_Command 0x01
#define IOP_SCSI_Task_Management 0x02
#define IOP_Login_Request 0x03
#define IOP_Text_Request 0x04
#define IOP_SCSI_Data_out 0x05
#define IOP_Logout_Request 0x06
#define IOP_SNACK_Request 0x10
/* Target Opcodes */
#define TOP_NOP_In 0x20
#define TOP_SCSI_Response 0x21
#define TOP_SCSI_Task_Management 0x22
#define TOP_Login_Response 0x23
#define TOP_Text_Response 0x24
#define TOP_SCSI_Data_in 0x25
#define TOP_Logout_Response 0x26
#define TOP_R2T 0x31
#define TOP_Asynchronous_Message 0x32
#define TOP_Reject 0x3f
/*
* The Opcode-dependent fields of the BHS, defined per PDU
*/
/* Command + Response */
struct scsi_command_pdu_s
{
uint32_t ExpectedDataTransferLength;
uint32_t CmdSN;
uint32_t ExpStatSN;
uint8_t SCSI_CDB[16];
} __attribute__ ((__packed__));
typedef struct scsi_command_pdu_s scsi_command_pdu_t;
struct scsi_response_pdu_s
{
uint32_t SNACKTag;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint32_t ExpStatSN;
uint32_t ReadResidualCount;
uint32_t ResidualCount;
} __attribute__ ((__packed__));
typedef struct scsi_response_pdu_s scsi_response_pdu_t;
/* Task Management */
struct task_management_req_pdu_s
{
uint32_t ReferencedTaskTag;
uint32_t CmdSN;
uint32_t ExpStatSN;
uint32_t RefCmdSN;
uint32_t ExpDataSN;
uint8_t reserved[8];
} __attribute__ ((__packed__));
typedef struct task_management_req_pdu_s task_management_req_pdu_t;
struct task_management_rsp_pdu_s
{
uint32_t reserved1;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint8_t reserved2[12];
} __attribute__ ((__packed__));
typedef struct task_management_rsp_pdu_s task_management_rsp_pdu_t;
/* Data Out & In, R2T */
struct data_out_pdu_s
{
uint32_t TargetTransferTag;
uint32_t reserved1;
uint32_t ExpStatSN;
uint32_t reserved2;
uint32_t DataSN;
uint32_t BufferOffset;
uint32_t reserved3;
} __attribute__ ((__packed__));
typedef struct data_out_pdu_s data_out_pdu_t;
struct data_in_pdu_s
{
uint32_t TargetTransferTag;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint32_t DataSN;
uint32_t BufferOffset;
uint32_t ResidualCount;
} __attribute__ ((__packed__));
typedef struct data_in_pdu_s data_in_pdu_t;
struct r2t_pdu_s
{
uint32_t TargetTransferTag;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint32_t R2TSN;
uint32_t BufferOffset;
uint32_t DesiredDataTransferLength;
} __attribute__ ((__packed__));
typedef struct r2t_pdu_s r2t_pdu_t;
/* Asynch message */
struct asynch_pdu_s
{
uint32_t reserved1;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint8_t AsyncEvent;
uint8_t AsyncVCode;
uint16_t Parameter1;
uint16_t Parameter2;
uint16_t Parameter3;
uint32_t reserved2;
} __attribute__ ((__packed__));
typedef struct asynch_pdu_s asynch_pdu_t;
/* Text request / response */
struct text_req_pdu_s
{
uint32_t TargetTransferTag;
uint32_t CmdSN;
uint32_t ExpStatSN;
uint8_t reserved[16];
} __attribute__ ((__packed__));
typedef struct text_req_pdu_s text_req_pdu_t;
struct text_rsp_pdu_s
{
uint32_t TargetTransferTag;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint8_t reserved[12];
} __attribute__ ((__packed__));
typedef struct text_rsp_pdu_s text_rsp_pdu_t;
/* Login request / response */
struct login_req_pdu_s
{
uint16_t CID;
uint16_t reserved1;
uint32_t CmdSN;
uint32_t ExpStatSN;
uint8_t reserved2[16];
} __attribute__ ((__packed__));
typedef struct login_req_pdu_s login_req_pdu_t;
/* Overlays LUN field in login request and response */
struct login_isid_s
{
uint8_t ISID_A;
uint16_t ISID_B;
uint8_t ISID_C;
uint16_t ISID_D;
uint16_t TSIH;
} __attribute__ ((__packed__));
typedef struct login_isid_s login_isid_t;
struct login_rsp_pdu_s
{
uint32_t reserved1;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint8_t StatusClass;
uint8_t StatusDetail;
uint8_t reserved2[10];
} __attribute__ ((__packed__));
typedef struct login_rsp_pdu_s login_rsp_pdu_t;
/* Logout request / response */
struct logout_req_pdu_s
{
uint16_t CID;
uint16_t reserved2;
uint32_t CmdSN;
uint32_t ExpStatSN;
uint8_t reserved3[16];
} __attribute__ ((__packed__));
typedef struct logout_req_pdu_s logout_req_pdu_t;
struct logout_rsp_pdu_s
{
uint32_t reserved2;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint32_t reserved3;
uint16_t Time2Wait;
uint16_t Time2Retain;
uint32_t reserved4;
} __attribute__ ((__packed__));
typedef struct logout_rsp_pdu_s logout_rsp_pdu_t;
/* SNACK request */
/* SNACK Types (in Flags field) */
#define SNACK_DATA_NAK 0
#define SNACK_STATUS_NAK 1
#define SNACK_DATA_ACK 2
#define SNACK_RDATA_NAK 3
struct snack_req_pdu_s
{
uint32_t TargetTransferTag;
uint32_t reserved1;
uint32_t ExpStatSN;
uint8_t reserved2[8];
uint32_t BegRun;
uint32_t RunLength;
} __attribute__ ((__packed__));
typedef struct snack_req_pdu_s snack_req_pdu_t;
/* Reject */
#define REJECT_DIGEST_ERROR 2
#define REJECT_SNACK 3
#define REJECT_PROTOCOL_ERROR 4
#define REJECT_CMD_NOT_SUPPORTED 5
#define REJECT_IMMED_COMMAND 6
#define REJECT_TASK_IN_PROGRESS 7
#define REJECT_INVALID_DATA_ACK 8
#define REJECT_INVALID_PDU_FIELD 9
#define REJECT_LONG_OPERATION 10
#define REJECT_NEGOTIATION_RESET 11
#define REJECT_WAITING_FOR_LOGOUT 12
struct reject_pdu_s
{
uint32_t reserved2;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint8_t DataSN;
uint8_t reserved[8];
} __attribute__ ((__packed__));
typedef struct reject_pdu_s reject_pdu_t;
/* NOP Out & In */
struct nop_out_pdu_s
{
uint32_t TargetTransferTag;
uint32_t CmdSN;
uint32_t ExpStatSN;
uint8_t reserved[16];
} __attribute__ ((__packed__));
typedef struct nop_out_pdu_s nop_out_pdu_t;
struct nop_in_pdu_s
{
uint32_t TargetTransferTag;
uint32_t StatSN;
uint32_t ExpCmdSN;
uint32_t MaxCmdSN;
uint8_t reserved3[12];
} __attribute__ ((__packed__));
typedef struct nop_in_pdu_s nop_in_pdu_t;
/*
* The complete PDU Header.
*/
struct pdu_header_s
{
uint8_t Opcode;
uint8_t Flags;
uint8_t OpcodeSpecific[2];
uint8_t TotalAHSLength;
uint8_t DataSegmentLength[3];
uint64_t LUN;
uint32_t InitiatorTaskTag;
union
{
scsi_command_pdu_t command;
scsi_response_pdu_t response;
task_management_req_pdu_t task_req;
task_management_rsp_pdu_t task_rsp;
data_out_pdu_t data_out;
data_in_pdu_t data_in;
r2t_pdu_t r2t;
asynch_pdu_t asynch;
text_req_pdu_t text_req;
text_rsp_pdu_t text_rsp;
login_req_pdu_t login_req;
login_rsp_pdu_t login_rsp;
logout_req_pdu_t logout_req;
logout_rsp_pdu_t logout_rsp;
snack_req_pdu_t snack;
reject_pdu_t reject;
nop_out_pdu_t nop_out;
nop_in_pdu_t nop_in;
} p;
uint32_t HeaderDigest;
} __attribute__ ((__packed__));
typedef struct pdu_header_s pdu_header_t;
#endif /* !_ISCSI_PDU_H */

116
sys/dev/iscsi/iscsi_perf.h Normal file
View File

@ -0,0 +1,116 @@
/* $NetBSD: iscsi_perf.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
typedef enum
{
PERF_BEGIN_COMMAND, /* start of command */
PERF_END_COMMAND, /* end of command */
PERF_BEGIN_PDUWRITECMD, /* start of write of command PDU */
PERF_END_PDUWRITECMD, /* end of write of command PDU */
PERF_BEGIN_PDUWRITEDATA, /* start of first data PDU write (write only) */
PERF_END_PDUWRITEDATA, /* end of last data PDU write */
PERF_BEGIN_PDURCVDATA, /* start of first data PDU receive (read only) */
PERF_END_PDURCVDATA, /* end of last data PDU receive */
PERF_PDURCVSTS, /* receive of status PDU */
PERF_NUM_PERFPOINTS
} perfpoint_t;
#define PERF_FLG_READ 0x10000000 /* this is a read command */
#define PERF_FLG_NOWAIT 0x20000000 /* this command was non-waiting */
#define PERF_FLG_COMPLETE 0x80000000 /* command completed */
typedef struct
{
uint64_t pctr[PERF_NUM_PERFPOINTS];
uint32_t datalen; /* data transfer size */
uint32_t status; /* Result of command (lower 16 bits) + flags */
} command_perf_t;
/*
Perfdata_Start:
Allocates data buffer in driver with given number of command_perf
elements. If a buffer of larger or equal size already exists, it is
not reallocated.
Resets indices and clears the buffer.
Perfdata_Stop:
Stops data collection, waits (up to 5 seconds) for completion.
Returns the number of data points collected.
*/
typedef struct
{
int num_elements; /* start: IN number of elements to allocate */
/* stop: OUT number of elements used */
uint32_t status; /* out: status */
} iscsi_perf_startstop_parameters_t;
#define ISCSI_PERFDATA_START _IOWR (0, 90, iscsi_perf_startstop_parameters_t)
#define ISCSI_PERFDATA_STOP _IOWR (0, 91, iscsi_perf_startstop_parameters_t)
/*
Perfdata_Get:
Retrieves the current data. Note that this may be called while data
collection is still active, it does not automatically stop collection.
It will not reset collection or release any buffers.
To determine the required buffer size, call with buffersize set to zero.
In that case, no data is transferred, the buffersize field is set to the
required size in bytes, and num_elements is set to the corresponding
number of data points.
If buffersize is nonzero, the number of data points that will fit into
the buffer, or the number of data points collected so far, will be
copied into the buffer, whichever is smallest. num_elements is set to
the number of elements copied.
ticks_per_100ms is the approximate conversion base to convert the CPU
cycle counter values collected into time units. This value is determined
by measuring the cycles for a delay(100) five times and taking the
average.
*/
typedef struct
{
void *buffer; /* in: buffer pointer */
uint32_t buffersize; /* in: size of buffer in bytes (0 to query size) */
uint32_t status; /* out: status */
int num_elements; /* out: number of elements written */
uint64_t ticks_per_100us; /* out: ticks per 100 usec */
} iscsi_perf_get_parameters_t;
#define ISCSI_PERFDATA_GET _IOWR (0, 92, iscsi_perf_get_parameters_t)

View File

@ -0,0 +1,251 @@
/* $NetBSD: iscsi_profile.c,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "iscsi_globals.h"
#ifdef ISCSI_PERFTEST
command_perf_t *perfdata = NULL;
int perf_active = 0;
static int perfcount = 0;
static int perfindex = 0;
static uint64_t ticks_per_100us = 0;
/*
* perf_begin_command:
* Start command profiling.
* Allocates space for a command entry, sets the data length and
* flags, and gets the begin cycle count.
* If no room is left in the data buffer, nothing is set.
*
* Note: The perf_index set in the CCB is the index + 1, so that 0 is
* reserved as profiling not active (this is the default state
* for CCB fields).
*
* This function must only be called if perf_active is nonzero.
* The wrapper macro checks for this condition.
*
* Parameter:
* ccb The associated CCB.
* nowait Nonzero if this is a no-wait command.
*/
void
perf_begin_command(ccb_t *ccb, int nowait)
{
int idx;
/* Only trace commands that actually transfer data so we don't skew results */
if (ccb->data_len < 512) {
return;
}
idx = perfindex++;
if (idx >= perfcount) {
--perfindex;
return;
}
perfdata[idx].datalen = ccb->data_len;
if (ccb->data_in) {
perfdata[idx].status |= PERF_FLG_READ;
}
if (nowait) {
perfdata[idx].status |= PERF_FLG_NOWAIT;
}
ccb->perf_index = ++idx;
perf_snap(idx, PERF_BEGIN_COMMAND);
}
/*
* perf_end_command:
* Ends command profiling.
* Gets the end cycle count, and sets the complete flag.
*
* Parameter:
* ccb The associated CCB.
*/
void
perf_end_command(ccb_t *ccb)
{
int idx = ccb->perf_index;
perf_snap(idx, PERF_END_COMMAND);
perfdata[idx - 1].status |=
(ccb->status & 0xffffff) | PERF_FLG_COMPLETE;
ccb->perf_index = 0;
}
/*
* perf_do_stop:
* Stops performance data collection (internal function).
* Clears perf_active, and waits until all commands have completed.
* The wait is limited to 5 seconds.
*/
STATIC void
perf_do_stop(void)
{
int i, n = 0;
perf_active = 0;
do {
for (i = 0; i < perfindex; i++) {
if (!(perfdata[i].status & PERF_FLG_COMPLETE)) {
break;
}
}
if (i < perfindex) {
tsleep(&perfindex, PWAIT, "wait_perf", 1 * hz);
}
} while (n++ < 10 && i < perfindex);
}
/*
* perf_start:
* Starts performance data collection.
* If collection is already running, it is stopped.
* Allocates a buffer if necessary, clears the buffer, resets indices.
* Measures tick timing.
*
* Parameter:
* par The ioctl parameter specifying the number of elements.
*/
void
perf_start(iscsi_perf_startstop_parameters_t *par)
{
uint64_t rv, rv1, rv2;
int i, s;
if (par->num_elements <= 0) {
par->status = ISCSI_STATUS_PARAMETER_INVALID;
return;
}
if (perf_active)
perf_do_stop();
if (perfdata == NULL || perfcount < par->num_elements) {
if (perfdata != NULL) {
free(perfdata, M_DEVBUF);
}
perfdata = malloc(par->num_elements * sizeof(*perfdata),
M_DEVBUF, M_WAITOK);
if (perfdata == NULL) {
perfcount = 0;
par->status = ISCSI_STATUS_NO_RESOURCES;
return;
}
perfcount = par->num_elements;
}
(void) memset(perfdata, 0x0, perfcount * sizeof(*perfdata));
perfindex = 0;
rv = 0;
for (i = 0; i < 5; i++) {
s = splhigh();
__asm __volatile("rdtsc":"=A"(rv1));
delay(100);
__asm __volatile("rdtsc":"=A"(rv2));
splx(s);
rv += rv2 - rv1;
}
ticks_per_100us = rv / 5;
par->status = ISCSI_STATUS_SUCCESS;
perf_active = 1;
}
/*
* perf_start:
* Stops performance data collection (external interface).
*
* Parameter:
* par The ioctl parameter receiving the number of
* elements collected.
*/
void
perf_stop(iscsi_perf_startstop_parameters_t *par)
{
if (perfdata == NULL || perfcount <= 0 || !perf_active) {
par->status = ISCSI_STATUS_GENERAL_ERROR;
return;
}
perf_do_stop();
par->num_elements = perfindex;
par->status = ISCSI_STATUS_SUCCESS;
}
/*
* perf_get:
* Retrieves performance data.
*
* Parameter:
* par The ioctl parameter.
*/
void
perf_get(iscsi_perf_get_parameters_t *par)
{
int nelem;
if (perfdata == NULL || perfcount <= 0) {
par->status = ISCSI_STATUS_GENERAL_ERROR;
return;
}
par->status = ISCSI_STATUS_SUCCESS;
if (!par->buffersize) {
par->buffersize = perfindex * sizeof(command_perf_t);
par->num_elements = perfindex;
return;
}
nelem = par->buffersize / sizeof(command_perf_t);
if (!nelem) {
par->status = ISCSI_STATUS_PARAMETER_INVALID;
return;
}
nelem = min(nelem, perfindex);
copyout(perfdata, par->buffer, nelem * sizeof(command_perf_t));
par->num_elements = nelem;
par->ticks_per_100us = ticks_per_100us;
}
#endif

View File

@ -0,0 +1,89 @@
/* $NetBSD: iscsi_profile.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2005,2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef ISCSI_PERFTEST
command_perf_t *perfdata;
int perf_active;
void perf_begin_command(ccb_t *, int);
void perf_end_command(ccb_t *);
void perf_start(iscsi_perf_startstop_parameters_t *);
void perf_stop(iscsi_perf_startstop_parameters_t *);
void perf_get(iscsi_perf_get_parameters_t *);
static __inline void
perf_snap(int index, perfpoint_t which)
{
uint64_t rv;
__asm __volatile("rdtsc":"=A"(rv));
perfdata[index - 1].pctr[which] = rv;
}
static __inline void
perf_snapcond(int index, perfpoint_t which)
{
uint64_t *pdst = &perfdata[index - 1].pctr[which];
uint64_t rv;
if (!*pdst) {
__asm __volatile("rdtsc":"=A"(rv));
*pdst = rv;
}
}
#define PERF_BEGIN(ccb, nowait) if (perf_active) perf_begin_command(ccb, nowait)
#define PERF_END(ccb) if (ccb->perf_index) perf_end_command(ccb)
#define PERF_SNAP(ccb,which) if (ccb->perf_index) \
perf_snap(ccb->perf_index,which)
#define PERF_SNAPC(ccb,which) if (ccb->perf_index) \
perf_snapcond(ccb->perf_index,which)
#define PERF_PDUSNAPB(pdu) if (pdu->perf_index) \
perf_snapcond(pdu->perf_index,pdu->perf_which)
#define PERF_PDUSNAPE(pdu) if (pdu->perf_index) \
perf_snap(pdu->perf_index,pdu->perf_which+1)
#define PERF_PDUSET(pdu,ccb,which) {pdu->perf_index = ccb->perf_index; \
pdu->perf_which = which;}
#else
#define PERF_BEGIN(ccb, nowait)
#define PERF_END(ccb)
#define PERF_SNAP(ccb,which)
#define PERF_SNAPC(ccb,which)
#define PERF_PDUSNAPB(pdu)
#define PERF_PDUSNAPE(pdu)
#define PERF_PDUSET(pdu,ccb,which)
#endif

1220
sys/dev/iscsi/iscsi_rcv.c Normal file

File diff suppressed because it is too large Load Diff

1643
sys/dev/iscsi/iscsi_send.c Normal file

File diff suppressed because it is too large Load Diff

1087
sys/dev/iscsi/iscsi_test.c Normal file

File diff suppressed because it is too large Load Diff

264
sys/dev/iscsi/iscsi_test.h Normal file
View File

@ -0,0 +1,264 @@
/* $NetBSD: iscsi_test.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_TEST_H
#define _ISCSI_TEST_H
#ifdef ISCSI_TEST_MODE
/* Status codes returned in test mode only */
#define ISCSI_STATUS_TEST_INACTIVE 5001 /* Test not assigned to connection */
#define ISCSI_STATUS_TEST_CANCELED 5002 /* Test was cancelled */
#define ISCSI_STATUS_TEST_CONNECTION_CLOSED 5003 /* Connection closed */
#define ISCSI_STATUS_TEST_MODIFICATION_SKIPPED 5004 /* Modification skipped due to bad offset */
#define ISCSI_STATUS_TEST_HEADER_CRC_ERROR 5005 /* Received PDU had bad header CRC */
#define ISCSI_STATUS_TEST_DATA_CRC_ERROR 5006 /* Received PDU had bad data CRC */
#define ISCSI_STATUS_TEST_DATA_READ_ERROR 5007 /* Error while receiving PDU */
#define ISCSI_STATUS_TEST_ALREADY_ASSIGNED 5008 /* Different test already assigned */
/*
PDU modification descriptor.
*/
#define ISCSITEST_MOD_FLAG_ADD_VAL 0x01 /* Add value to field */
/* (default is replace) */
#define ISCSITEST_MOD_FLAG_REORDER 0x02 /* Put bytes in network order */
/* Special values for offset field */
#define ISCSITEST_OFFSET_HEADERDIGEST (-1) /* offset is header digest */
#define ISCSITEST_OFFSET_DATA (-2) /* offset is start of data */
#define ISCSITEST_OFFSET_DATADIGEST (-3) /* offset is data digest */
#define ISCSITEST_OFFSET_DRV_CMDSN (-4) /* offset is driver's CmdSN */
typedef struct
{
uint16_t flags; /* flags */
uint16_t size; /* size of field in bytes (1..8) */
int32_t offset; /* offset into PDU */
uint8_t value[8]; /* value to use */
} iscsi_pdu_mod_t;
/* PDU kinds */
typedef enum
{
ANY_PDU, /* don't care */
COMMAND_PDU,
RESPONSE_PDU,
TASK_REQ_PDU,
TASK_RSP_PDU,
DATA_OUT_PDU,
DATA_IN_PDU,
R2T_PDU,
ASYNCH_PDU,
TEXT_REQ_PDU,
TEXT_RSP_PDU,
LOGIN_REQ_PDU,
LOGIN_RSP_PDU,
LOGOUT_REQ_PDU,
LOGOUT_RSP_PDU,
SNACK_PDU,
REJECT_PDU,
NOP_OUT_PDU,
NOP_IN_PDU,
INVALID_PDU
} iscsi_pdu_kind_t;
/* types of offsets supported for determining PDU offset */
typedef enum
{
ABSOLUTE_ANY, /* absolute to beginning of connection, any kind */
RELATIVE_ANY, /* relative to last modified PDU, any kind */
ABSOLUTE_PDUKIND, /* absolute to beginning of connection, same kind */
RELATIVE_PDUKIND, /* relative to last modified PDU, same kind */
ABSOLUTE_TX, /* absolute to beginning of connection, send only */
RELATIVE_TX, /* relative to last modified PDU, send only */
ABSOLUTE_RX, /* absolute to beginning of connection, receive only */
RELATIVE_RX /* relative to last modified PDU, receive only */
} iscsi_pdu_offset_t;
/*
Negotiation parameters
*/
/* Negotiation states */
typedef enum
{
NEG_SECURITY, /* First security negotiation PDU (method) */
NEG_CHAP_ALG, /* CHAP: Algorithm specification */
NEG_CHAP_NAME_RESPONSE, /* CHAP: Name and Response */
NEG_OP_NEG /* in Operational negotiation stage */
} iscsi_neg_state_t;
#define ISCSITEST_NEGOPT_REPLACE 0x01 /* Replace existing parameters */
/* (default is append) */
typedef struct
{
iscsi_neg_state_t state; /* which negotiation stage */
uint16_t flags; /* flags */
uint16_t size; /* size of value in bytes */
uint8_t value[0]; /* value to use */
} iscsi_test_negotiation_descriptor_t;
/*
Define and add test parameters
The calling application defines the test ID. Duplicate IDs are rejected.
*/
/* Options for define_parameters only */
#define ISCSITEST_NEGOTIATE_R2T 0x00000100 /* Negotiate MaxOutstandingR2T */
#define ISCSITEST_NEGOTIATE_MAXBURST 0x00000200 /* Negotiate MaxBurstLength */
#define ISCSITEST_NEGOTIATE_FIRSTBURST 0x00000400 /* Negotiate FirstBurstLength */
#define ISCSITEST_OVERRIDE_INITIALR2T 0x00001000 /* Override default InitialR2T (set to TRUE) */
#define ISCSITEST_OVERRIDE_IMMDATA 0x00002000 /* Override default ImmediateData (set to FALSE) */
/* options for both define and add_modification parameters */
#define ISCSITEST_OPT_DISABLE_CCB_TIMEOUT 0x00020000 /* Disable CCB timeout */
#define ISCSITEST_OPT_ENABLE_CCB_TIMEOUT 0x00040000 /* Enable CCB timeout */
#define ISCSITEST_OPT_DISABLE_CONN_TIMEOUT 0x00080000 /* Disable connection timeout */
#define ISCSITEST_OPT_ENABLE_CONN_TIMEOUT 0x00100000 /* Enable connection timeout */
#define ISCSITEST_OPT_USE_RANDOM_TX 0x00200000 /* Use random tx loss parameter */
#define ISCSITEST_OPT_USE_RANDOM_RX 0x00400000 /* Use random rx loss parameter */
/* options for add_modification parameters only */
#define ISCSITEST_OPT_WAIT_FOR_COMPLETION 0x00000001 /* Wait until processing done */
#define ISCSITEST_OPT_MOD_PERMANENT 0x20000000 /* PDU header mods are permanent */
#define ISCSITEST_OPT_NO_RESPONSE_PDU 0x40000000 /* Do not expect response to this PDU */
#define ISCSITEST_OPT_DISCARD_PDU 0x80000000 /* Don't send PDU/process PDU in driver */
/* options for add_modification and send_pdu */
#define ISCSITEST_KILL_CONNECTION 0x08000000 /* Kill connection when done */
#define ISCSITEST_SFLAG_NO_HEADER_DIGEST 0x00000020 /* Do not update header digest */
#define ISCSITEST_SFLAG_NO_DATA_DIGEST 0x00000040 /* Do not update data digest */
/* options for send_pdu only */
#define ISCSITEST_SFLAG_UPDATE_FIELDS 0x00000010 /* Update fields in PDU */
#define ISCSITEST_SFLAG_NO_PADDING 0x00000080 /* Do not add padding */
/* The parameter for TEST_DEFINE ioctl */
typedef struct
{
uint32_t test_id; /* Test ID */
uint32_t options; /* Test options */
uint32_t session_id; /* Session to attach to (may be 0) */
uint32_t connection_id; /* Connection to attach to (may be 0) */
uint8_t lose_random_tx; /* 0 disables, else mod value */
uint8_t lose_random_rx; /* 0 disables, else mod value */
uint32_t r2t_val; /* Used only if NEGOTIATE_R2T */
uint32_t maxburst_val; /* Used only if NEGOTIATE_MAXBURST */
uint32_t firstburst_val; /* Used only if NEGOTIATE_FIRSTBURST */
uint32_t neg_descriptor_size; /* Size of negotiation descriptor (may be 0) */
void *neg_descriptor_ptr; /* pointer to negotiation descriptor */
uint32_t status; /* Out: Status */
} iscsi_test_define_parameters_t;
/* The parameter for TEST_ADD_MODIFICATION ioctl */
typedef struct
{
uint32_t test_id; /* Test ID */
uint32_t options; /* Test options */
uint8_t lose_random_tx; /* 0 disables, else mod value */
uint8_t lose_random_rx; /* 0 disables, else mod value */
iscsi_pdu_kind_t which_pdu; /* Which kind of PDU to act on */
iscsi_pdu_offset_t which_offset; /* Which offset to use */
uint32_t pdu_offset; /* Offset of PDU to act on (0 means next) */
uint32_t num_pdu_mods; /* How many PDU modifications */
iscsi_pdu_mod_t *mod_ptr; /* pointer to modifications */
uint32_t pdu_size; /* size of PDU buffer for get */
void *pdu_ptr; /* pointer to PDU buffer */
uint32_t pdu_actual_size; /* Out: actual size of PDU */
uint32_t status; /* Out: Status */
} iscsi_test_add_modification_parameters_t;
/* The parameter for TEST_ADD_NEGOTIATION ioctl */
typedef struct
{
uint32_t test_id; /* Test ID */
uint32_t neg_descriptor_size; /* Size of negotiation descriptor (may be 0) */
void *neg_descriptor_ptr; /* pointer to negotiation descriptor */
uint32_t status; /* Out: Status */
} iscsi_test_add_negotiation_parameters_t;
/*
Cancel test parameters
*/
typedef struct
{
uint32_t test_id; /* Test ID */
uint32_t status; /* Out: Status */
} iscsi_test_cancel_parameters_t;
/*
Send PDU parameters
*/
typedef struct
{
uint32_t test_id; /* Test ID */
uint32_t options; /* Test options */
uint32_t pdu_size; /* size of PDU */
void *pdu_ptr; /* pointer to PDU */
uint32_t status; /* Out: Status */
} iscsi_test_send_pdu_parameters_t;
/* IOCTL codes */
#define ISCSI_TEST_DEFINE _IOWR(0, 100, iscsi_test_define_parameters_t)
#define ISCSI_TEST_ADD_NEGOTIATION _IOWR(0, 101, iscsi_test_add_negotiation_parameters_t)
#define ISCSI_TEST_ADD_MODIFICATION _IOWR(0, 102, iscsi_test_add_modification_parameters_t)
#define ISCSI_TEST_SEND_PDU _IOWR(0, 103, iscsi_test_send_pdu_parameters_t)
#define ISCSI_TEST_CANCEL _IOWR(0, 109, iscsi_test_cancel_parameters_t)
#endif
#endif

View File

@ -0,0 +1,105 @@
/* $wasabi: iscsi_testlocal.h,v 1.5 2006/05/30 23:22:20 twagner Exp $ */
/* $NetBSD: iscsi_testlocal.h,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2006,2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _ISCSI_TESTLOCAL_H
#define _ISCSI_TESTLOCAL_H
#ifdef ISCSI_TEST_MODE
typedef struct mod_desc_s
{
TAILQ_ENTRY(mod_desc_s) link;
void *pdu_ptr; /* get only */
iscsi_test_add_modification_parameters_t pars;
iscsi_pdu_mod_t mods[0];
} mod_desc_t;
typedef struct neg_desc_s
{
TAILQ_ENTRY(neg_desc_s) link;
iscsi_test_negotiation_descriptor_t entry;
} neg_desc_t;
TAILQ_HEAD(mod_list_s, mod_desc_s);
typedef struct mod_list_s mod_list_t;
TAILQ_HEAD(neg_list_s, neg_desc_s);
typedef struct neg_list_s neg_list_t;
#define CNT_TX 0
#define CNT_RX 1
typedef struct test_pars_s
{
TAILQ_ENTRY(test_pars_s) link; /* links tests to be assigned */
uint32_t test_id; /* Test ID */
connection_t *connection; /* Attached Connection */
uint16_t options; /* Test options */
uint8_t lose_random[2]; /* 0 disables, else mod value */
uint32_t r2t_val; /* Used only if NEGOTIATE_R2T */
uint32_t maxburst_val; /* Used only if NEGOTIATE_MAXBURST */
uint32_t firstburst_val; /* Used only if NEGOTIATE_FIRSTBURST */
neg_list_t negs; /* list of negotiation descriptors */
mod_list_t mods; /* list of modification decriptors */
uint32_t pdu_count[INVALID_PDU + 1][2]; /* PDU counter */
uint32_t pdu_last[INVALID_PDU + 1][2]; /* last PDU counter */
} test_pars_t;
TAILQ_HEAD(test_pars_list_s, test_pars_s);
typedef struct test_pars_list_s test_pars_list_t;
void test_assign_connection(connection_t *);
void test_remove_connection(connection_t *);
#define TEST_INVALID_HEADER_CRC -99
#define TEST_READ_ERROR 1
#define TEST_INVALID_DATA_CRC -1
int test_mode_rx(connection_t *, pdu_t *, int);
int test_mode_tx(connection_t *, pdu_t *);
void test_define(iscsi_test_define_parameters_t *);
void test_add_neg(iscsi_test_add_negotiation_parameters_t *);
void test_add_mod(struct proc *, iscsi_test_add_modification_parameters_t *);
void test_send_pdu(struct proc *, iscsi_test_send_pdu_parameters_t *);
void test_cancel(iscsi_test_cancel_parameters_t *);
#define test_ccb_timeout(conn) ((conn->test_pars == NULL) ? 1 \
: !(conn->test_pars->options & ISCSITEST_OPT_DISABLE_CCB_TIMEOUT))
#define test_conn_timeout(conn) ((conn->test_pars == NULL) ? 1 \
: !(conn->test_pars->options & ISCSITEST_OPT_DISABLE_CONN_TIMEOUT))
#endif /* TEST_MODE */
#endif /* TESTLOCAL_H */

1780
sys/dev/iscsi/iscsi_text.c Normal file

File diff suppressed because it is too large Load Diff

706
sys/dev/iscsi/iscsi_utils.c Normal file
View File

@ -0,0 +1,706 @@
/* $NetBSD: iscsi_utils.c,v 1.1 2011/10/23 21:15:02 agc Exp $ */
/*-
* Copyright (c) 2004,2005,2006,2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "iscsi_globals.h"
#include <sys/systm.h>
#include <sys/buf.h>
#include <sys/socketvar.h>
#ifdef ISCSI_DEBUG
/* debug helper routine */
void
dump(void *buff, int len)
{
uint8_t *bp = (uint8_t *) buff;
int i;
while (len > 0) {
for (i = min(16, len); i > 0; i--)
printf("%02x ", *bp++);
printf("\n");
len -= 16;
}
}
#endif
/*****************************************************************************
* Digest functions
*****************************************************************************/
/*****************************************************************
*
* CRC LOOKUP TABLE
* ================
* The following CRC lookup table was generated automagically
* by the Rocksoft^tm Model CRC Algorithm Table Generation
* Program V1.0 using the following model parameters:
*
* Width : 4 bytes.
* Poly : 0x1EDC6F41L
* Reverse : TRUE.
*
* For more information on the Rocksoft^tm Model CRC Algorithm,
* see the document titled "A Painless Guide to CRC Error
* Detection Algorithms" by Ross Williams
* (ross@guest.adelaide.edu.au.). This document is likely to be
* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".
*
*****************************************************************/
STATIC uint32_t crc_table[256] = {
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
};
/*
* gen_digest:
* Generate an iSCSI CRC32C digest over the given data.
*
* Parameters:
* buff The data
* len The length of the data in bytes
*
* Returns: The digest in network byte order
*/
uint32_t
gen_digest(void *buff, int len)
{
uint8_t *bp = (uint8_t *) buff;
uint32_t crc = 0xffffffff;
while (len--) {
crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
}
return crc ^ 0xffffffff;
}
/*
* gen_digest_2:
* Generate an iSCSI CRC32C digest over the given data, which is split over
* two buffers.
*
* Parameters:
* buf1, buf2 The data
* len1, len2 The length of the data in bytes
*
* Returns: The digest in network byte order
*/
uint32_t
gen_digest_2(void *buf1, int len1, void *buf2, int len2)
{
uint8_t *bp = (uint8_t *) buf1;
uint32_t crc = 0xffffffff;
while (len1--) {
crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
}
bp = (uint8_t *) buf2;
while (len2--) {
crc = ((crc >> 8) & 0x00ffffff) ^ crc_table[(crc ^ *bp++) & 0xff];
}
return crc ^ 0xffffffff;
}
/*****************************************************************************
* CCB management functions
*****************************************************************************/
/*
* get_ccb:
* Get a CCB for the SCSI operation, waiting if none is available.
*
* Parameter:
* sess The session containing this CCB
* waitok Whether waiting for a CCB is OK
*
* Returns: The CCB.
*/
ccb_t *
get_ccb(connection_t *conn, bool waitok)
{
ccb_t *ccb;
session_t *sess = conn->session;
do {
CS_BEGIN;
ccb = TAILQ_FIRST(&sess->ccb_pool);
if (ccb != NULL) {
TAILQ_REMOVE(&sess->ccb_pool, ccb, chain);
}
CS_END;
DEB(100, ("get_ccb: ccb = %p, waitok = %d\n", ccb, waitok));
if (ccb == NULL) {
if (!waitok || conn->terminating) {
return NULL;
}
PDEBOUT(("Waiting for CCB!\n"));
tsleep(&sess->ccb_pool, PWAIT, "get_ccb", 0);
}
} while (ccb == NULL);
ccb->flags = 0;
ccb->xs = NULL;
ccb->temp_data = NULL;
ccb->text_data = NULL;
ccb->status = ISCSI_STATUS_SUCCESS;
ccb->ITT = (ccb->ITT & 0xffffff) | (++sess->itt_id << 24);
ccb->disp = CCBDISP_NOWAIT;
ccb->connection = conn;
return ccb;
}
/*
* free_ccb:
* Put a CCB back onto the free list.
*
* Parameter: The CCB.
*/
void
free_ccb(ccb_t *ccb)
{
session_t *sess = ccb->session;
pdu_t *pdu;
ccb->disp = CCBDISP_UNUSED;
/* free temporary data */
if (ccb->temp_data != NULL) {
free(ccb->temp_data, M_TEMP);
}
if (ccb->text_data != NULL) {
free(ccb->text_data, M_TEMP);
}
/* free PDU waiting for ACK */
if ((pdu = ccb->pdu_waiting) != NULL) {
ccb->pdu_waiting = NULL;
free_pdu(pdu);
}
CS_BEGIN;
TAILQ_INSERT_TAIL(&sess->ccb_pool, ccb, chain);
CS_END;
wakeup(&sess->ccb_pool);
}
/*
* create_ccbs
* "Create" the pool of CCBs. This doesn't actually create the CCBs
* (they are allocated with the session structure), but it links them
* into the free-list.
*
* Parameter: The session owning the CCBs.
*/
void
create_ccbs(session_t *sess)
{
int i;
ccb_t *ccb;
int sid = sess->id << 8;
/* Note: CCBs are initialized to 0 with connection structure */
for (i = 0, ccb = sess->ccb; i < CCBS_PER_SESSION; i++, ccb++) {
ccb->ITT = i | sid;
ccb->session = sess;
callout_init(&ccb->timeout, 0);
#if (__NetBSD_Version__ >= 106000000)
callout_setfunc(&ccb->timeout, ccb_timeout, ccb);
#endif
/*DEB (9, ("Create_ccbs: ccb %x itt %x\n", ccb, ccb->ITT)); */
TAILQ_INSERT_HEAD(&sess->ccb_pool, ccb, chain);
}
}
/*
* wake_ccb:
* Wake up (or dispose of) a CCB. Depending on the CCB's disposition,
* either wake up the requesting thread, signal SCSIPI that we're done,
* or just free the CCB for CCBDISP_FREE.
*
* Parameter: The CCB to handle.
*/
void
wake_ccb(ccb_t *ccb)
{
ccb_disp_t disp;
connection_t *conn;
int s;
#ifdef ISCSI_DEBUG
static ccb_t *lastccb = NULL;
static int lastdisp = -1;
#endif
/* Just in case */
if (ccb == NULL)
return;
conn = ccb->connection;
#ifdef ISCSI_DEBUG
if (ccb != lastccb || ccb->disp != lastdisp) {
DEBC(conn, 9, ("Wake CCB, ccb = %p, disp = %d\n",
ccb, (ccb) ? ccb->disp : 0));
lastccb = ccb;
lastdisp = (ccb) ? ccb->disp : 0;
}
#endif
callout_stop(&ccb->timeout);
s = splbio();
disp = ccb->disp;
if (disp <= CCBDISP_NOWAIT ||
(disp == CCBDISP_DEFER && conn->state <= ST_WINDING_DOWN)) {
splx(s);
return;
}
TAILQ_REMOVE(&conn->ccbs_waiting, ccb, chain);
/* change the disposition so nobody tries this again */
ccb->disp = CCBDISP_BUSY;
splx(s);
PERF_END(ccb);
switch (disp) {
case CCBDISP_WAIT:
wakeup(ccb);
break;
case CCBDISP_SCSIPI:
iscsi_done(ccb);
break;
case CCBDISP_DEFER:
break;
default:
free_ccb(ccb);
break;
}
}
/*
* complete_ccb:
* Same as wake_ccb, but the CCB is not assumed to be in the waiting list.
*
* Parameter: The CCB to handle.
*/
void
complete_ccb(ccb_t *ccb)
{
ccb_disp_t disp;
int s;
/* Just in case */
if (ccb == NULL)
return;
callout_stop(&ccb->timeout);
s = splbio();
disp = ccb->disp;
if (disp <= CCBDISP_NOWAIT || disp == CCBDISP_DEFER) {
splx(s);
return;
}
/* change the disposition so nobody tries this again */
ccb->disp = CCBDISP_BUSY;
splx(s);
PERF_END(ccb);
switch (disp) {
case CCBDISP_WAIT:
wakeup(ccb);
break;
case CCBDISP_SCSIPI:
iscsi_done(ccb);
break;
default:
free_ccb(ccb);
break;
}
}
/*****************************************************************************
* PDU management functions
*****************************************************************************/
/*
* get_pdu_c:
* Get a PDU for the SCSI operation.
*
* Parameter:
* conn The connection this PDU should be associated with
* waitok OK to wait for PDU if TRUE
*
* Returns: The PDU or NULL if none is available and waitok is FALSE.
*/
pdu_t *
get_pdu_c(connection_t *conn, bool waitok)
{
pdu_t *pdu;
do {
CS_BEGIN;
pdu = TAILQ_FIRST(&conn->pdu_pool);
if (pdu != NULL) {
TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
}
CS_END;
DEB(100, ("get_pdu_c: pdu = %p, waitok = %d\n", pdu, waitok));
if (pdu == NULL) {
if (!waitok || conn->terminating)
return NULL;
PDEBOUT(("Waiting for PDU!\n"));
tsleep(&conn->pdu_pool, PWAIT, "get_pdu_c", 0);
}
} while (pdu == NULL);
memset(pdu, 0, sizeof(pdu_t));
pdu->connection = conn;
pdu->disp = PDUDISP_FREE;
return pdu;
}
/*
* get_pdu:
* Get a PDU for the SCSI operation, waits if none is available.
* Same as get_pdu_c, but with wait always OK.
* Duplicated code because this is the more common case.
*
* Parameter: The connection this PDU should be associated with.
*
* Returns: The PDU.
*/
pdu_t *
get_pdu(connection_t *conn)
{
pdu_t *pdu;
do {
CS_BEGIN;
pdu = TAILQ_FIRST(&conn->pdu_pool);
if (pdu != NULL) {
TAILQ_REMOVE(&conn->pdu_pool, pdu, chain);
}
CS_END;
DEB(100, ("get_pdu: pdu = %p\n", pdu));
if (pdu == NULL) {
if (conn->terminating)
return NULL;
PDEBOUT(("Waiting for PDU!\n"));
tsleep(&conn->pdu_pool, PWAIT, "get_pdu", 0);
}
} while (pdu == NULL);
memset(pdu, 0, sizeof(pdu_t));
pdu->connection = conn;
pdu->disp = PDUDISP_FREE;
return pdu;
}
/*
* free_pdu:
* Put a PDU back onto the free list.
*
* Parameter: The PDU.
*/
void
free_pdu(pdu_t *pdu)
{
connection_t *conn = pdu->connection;
pdu_disp_t pdisp;
if (PDUDISP_UNUSED == (pdisp = pdu->disp))
return;
pdu->disp = PDUDISP_UNUSED;
if (pdu->flags & PDUF_INQUEUE) {
TAILQ_REMOVE(&conn->pdus_to_send, pdu, send_chain);
pdu->flags &= ~PDUF_INQUEUE;
}
if (pdisp == PDUDISP_SIGNAL)
wakeup(pdu);
/* free temporary data in this PDU */
if (pdu->temp_data)
free(pdu->temp_data, M_TEMP);
CS_BEGIN;
TAILQ_INSERT_TAIL(&conn->pdu_pool, pdu, chain);
CS_END;
wakeup(&conn->pdu_pool);
}
/*
* create_pdus
* "Create" the pool of PDUs. This doesn't actually create the PDUs
* (they are allocated with the connection structure), but it links them
* into the free-list.
*
* Parameter: The connection owning the PDUs.
*/
void
create_pdus(connection_t *conn)
{
int i;
pdu_t *pdu;
/* Note: PDUs are initialized to 0 with connection structure */
for (i = 0, pdu = conn->pdu; i < PDUS_PER_CONNECTION; i++, pdu++) {
TAILQ_INSERT_HEAD(&conn->pdu_pool, pdu, chain);
}
}
/*****************************************************************************
* Serial Number management functions
*****************************************************************************/
/*
* init_sernum:
* Initialize serial number buffer variables.
*
* Parameter:
* buff The serial number buffer.
*/
void
init_sernum(sernum_buffer_t *buff)
{
buff->bottom = 0;
buff->top = 0;
buff->next_sn = 0;
buff->ExpSN = 0;
}
/*
* add_sernum:
* Add a received serial number to the buffer.
* If the serial number is smaller than the expected one, it is ignored.
* If it is larger, all missing serial numbers are added as well.
*
* Parameter:
* buff The serial number buffer.
* num The received serial number
*
* Returns:
* 0 if the received block is a duplicate
* 1 if the number is the expected one
* >1 if the numer is > the expected value, in this case the
* return value is the number of unacknowledged blocks
* <0 if the buffer is full (i.e. an excessive number of blocks
* is unacknowledged)
*/
int
add_sernum(sernum_buffer_t *buff, uint32_t num)
{
int i, t, b, n, diff;
/*
* next_sn is the next expected SN, so normally diff should be 1.
*/
n = buff->next_sn;
diff = (num - n) + 1;
if (diff <= 0) {
PDEB(1, ("Rx Duplicate Block: SN %d < Next SN %d\n", num, n));
return 0; /* ignore if SN is smaller than expected (dup or retransmit) */
}
buff->next_sn = num + 1;
t = buff->top;
b = buff->bottom;
for (i = 0; i < diff; i++) {
buff->sernum[t] = n++;
buff->ack[t] = 0;
t = (t + 1) % SERNUM_BUFFER_LENGTH;
if (t == b) {
DEB(1, ("AddSernum: Buffer Full! num %d, diff %d\n", num, diff));
return -1;
}
}
buff->top = t;
DEB(10, ("AddSernum bottom %d [%d], top %d, num %d, diff %d\n",
b, buff->sernum[b], buff->top, num, diff));
return diff;
}
/*
* ack_sernum:
* Mark a received serial number as acknowledged. This does not necessarily
* change the associated ExpSN if there are lower serial numbers in the
* buffer.
*
* Parameter:
* buff The serial number buffer.
* num The serial number to acknowledge.
*
* Returns: The value of ExpSN.
*/
uint32_t
ack_sernum(sernum_buffer_t *buff, uint32_t num)
{
int b = buff->bottom;
int t = buff->top;
/* shortcut for most likely case */
if (t == (b + 1) && num == buff->sernum[b]) {
/* buffer is now empty, reset top */
buff->top = b;
} else if (b != t) {
for (; b != t; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
if (!sn_a_lt_b(buff->sernum[b], num))
break;
}
if (num == buff->sernum[b]) {
if (b == buff->bottom)
buff->bottom = (b + 1) % SERNUM_BUFFER_LENGTH;
else
buff->ack[b] = 1;
}
for (b = buff->bottom, num = buff->sernum[b] - 1;
b != t && buff->ack[b]; b = (b + 1) % SERNUM_BUFFER_LENGTH) {
num = buff->sernum[b];
}
}
if (!sn_a_lt_b(num, buff->ExpSN))
buff->ExpSN = num + 1;
DEB(10, ("AckSernum bottom %d, top %d, num %d ExpSN %d\n",
buff->bottom, buff->top, num, buff->ExpSN));
return buff->ExpSN;
}