#ifndef _ISCSI_GLOBALS_H
#define _ISCSI_GLOBALS_H
#define DDB 1
#include <sys/param.h>
#include <sys/mutex.h>
#include <sys/rwlock.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/device.h>
#include <dev/scsipi/scsi_all.h>
#include <dev/scsipi/scsi_message.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"
#define ISCSI_DEBUG 0
#define INTERFACE_VERSION 2
#define VERSION_MAJOR 3
#define VERSION_MINOR 1
#define VERSION_STRING "NetBSD iSCSI Software Initiator 20110407"
#define CCBS_PER_SESSION 32
#define CCBS_FOR_SCSIPI 16
#define PDUS_PER_CONNECTION 64
#define SERNUM_BUFFER_LENGTH (CCBS_PER_SESSION / 2)
#define DEFAULT_MaxRecvDataSegmentLength (64*1024)
#define COMMAND_TIMEOUT (60 * hz)
#define MAX_CCB_TIMEOUTS 3
#define MAX_CCB_TRIES 9
#define CONNECTION_TIMEOUT (2 * hz)
#define CONNECTION_IDLE_TIMEOUT (30 * hz)
#define MAX_CONN_TIMEOUTS 4
#define MAX_RECOVERY_ATTEMPTS 2
#define PDUF_BUSY 0x01
#define PDUF_INQUEUE 0x02
#define PDUF_PRIORITY 0x04
#define PDUF_NOUPDATE 0x10
#define CCBF_COMPLETE 0x0001
#define CCBF_RESENT 0x0002
#define CCBF_SENDTARGET 0x0004
#define CCBF_GOT_RSP 0x0010
#define CCBF_REASSIGN 0x0020
#define CCBF_OTHERCONN 0x0040
#define CCBF_WAITQUEUE 0x0080
typedef enum {
ST_SEC_NEG = 0,
ST_SEC_FIN = 1,
ST_OP_NEG = 2,
ST_FULL_FEATURE = 3,
ST_WINDING_DOWN = 4,
ST_LOGOUT_SENT = 5,
ST_SETTLING = 6,
ST_IDLE = 7
} conn_state_t;
typedef enum {
NOT_LOGGED_OUT,
LOGOUT_SENT,
LOGOUT_SUCCESS,
LOGOUT_FAILED
} logout_state_t;
typedef enum {
CCBDISP_UNUSED,
CCBDISP_BUSY,
CCBDISP_NOWAIT,
CCBDISP_FREE,
CCBDISP_WAIT,
CCBDISP_SCSIPI,
CCBDISP_DEFER
} ccb_disp_t;
typedef enum {
PDUDISP_UNUSED,
PDUDISP_FREE,
PDUDISP_WAIT
} pdu_disp_t;
typedef enum {
TOUT_NONE,
TOUT_ARMED,
TOUT_QUEUED,
TOUT_BUSY
} tout_state_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;
typedef struct {
uint32_t ExpSN;
uint32_t next_sn;
int top;
int bottom;
uint32_t sernum[SERNUM_BUFFER_LENGTH];
bool ack[SERNUM_BUFFER_LENGTH];
} sernum_buffer_t;
struct pdu_s {
TAILQ_ENTRY(pdu_s) pdu_chain;
TAILQ_ENTRY(pdu_s) pdu_send_chain;
pdu_disp_t pdu_disp;
uint32_t pdu_flags;
pdu_header_t pdu_hdr;
void *pdu_temp_data;
uint32_t pdu_temp_data_len;
struct uio pdu_uio;
struct iovec pdu_io_vec[4];
struct uio pdu_save_uio;
struct iovec pdu_save_iovec[4];
uint32_t pdu_data_digest;
ccb_t *pdu_owner;
connection_t *pdu_connection;
};
TAILQ_HEAD(pdu_list_s, pdu_s);
typedef struct pdu_list_s pdu_list_t;
struct ccb_s {
TAILQ_ENTRY(ccb_s) ccb_chain;
uint32_t ccb_status;
ccb_disp_t ccb_disp;
struct callout ccb_timeout;
TAILQ_ENTRY(ccb_s) ccb_tchain;
tout_state_t ccb_timedout;
int ccb_num_timeouts;
int ccb_total_tries;
uint32_t ccb_ITT;
sernum_buffer_t ccb_DataSN_buf;
void *ccb_par;
struct scsipi_xfer *ccb_xs;
void *ccb_temp_data;
void *ccb_text_data;
uint32_t ccb_text_len;
uint64_t ccb_lun;
uint32_t ccb_tag;
uint8_t *ccb_cmd;
uint16_t ccb_cmdlen;
bool ccb_data_in;
uint8_t *ccb_data_ptr;
uint32_t ccb_data_len;
uint32_t ccb_xfer_len;
uint32_t ccb_residual;
void *ccb_sense_ptr;
int ccb_sense_len_req;
int ccb_sense_len_got;
pdu_t *ccb_pdu_waiting;
volatile uint32_t ccb_CmdSN;
int ccb_flags;
connection_t *ccb_connection;
session_t *ccb_session;
};
TAILQ_HEAD(ccb_list_s, ccb_s);
typedef struct ccb_list_s ccb_list_t;
struct connection_s {
TAILQ_ENTRY(connection_s) c_connections;
kmutex_t c_lock;
kcondvar_t c_conn_cv;
kcondvar_t c_pdu_cv;
kcondvar_t c_ccb_cv;
kcondvar_t c_idle_cv;
pdu_list_t c_pdu_pool;
ccb_list_t c_ccbs_waiting;
pdu_list_t c_pdus_to_send;
sernum_buffer_t c_StatSN_buf;
uint32_t c_max_transfer;
uint32_t c_max_firstimmed;
uint32_t c_max_firstdata;
uint32_t c_MaxRecvDataSegmentLength;
uint32_t c_Our_MaxRecvDataSegmentLength;
bool c_HeaderDigest;
bool c_DataDigest;
uint32_t c_Time2Wait;
uint32_t c_Time2Retain;
uint16_t c_id;
conn_state_t c_state;
struct lwp *c_threadobj;
krwlock_t c_sock_rw;
struct file *c_sock;
session_t *c_session;
struct lwp *c_rcvproc;
struct lwp *c_sendproc;
uint32_t c_terminating;
int c_recover;
volatile unsigned c_usecount;
unsigned c_pducount;
bool c_destroy;
bool c_in_session;
logout_state_t c_loggedout;
struct callout c_timeout;
TAILQ_ENTRY(connection_s) c_tchain;
tout_state_t c_timedout;
int c_num_timeouts;
uint32_t c_idle_timeout_val;
iscsi_login_parameters_t *c_login_par;
pdu_t c_pdu[PDUS_PER_CONNECTION];
};
TAILQ_HEAD(connection_list_s, connection_s);
typedef struct connection_list_s connection_list_t;
struct session_s {
struct scsipi_adapter s_sc_adapter;
struct scsipi_channel s_sc_channel;
device_t s_child_dev;
int s_refcount;
TAILQ_ENTRY(session_s) s_sessions;
kmutex_t s_lock;
kcondvar_t s_sess_cv;
kcondvar_t s_ccb_cv;
ccb_list_t s_ccb_pool;
int s_send_window;
uint16_t s_id;
uint16_t s_TSIH;
uint32_t s_CmdSN;
uint32_t s_ExpCmdSN;
uint32_t s_MaxCmdSN;
uint32_t s_ErrorRecoveryLevel;
uint32_t s_FirstBurstLength;
uint32_t s_MaxBurstLength;
bool s_ImmediateData;
bool s_InitialR2T;
uint32_t s_MaxOutstandingR2T;
uint32_t s_MaxConnections;
uint32_t s_DefaultTime2Wait;
uint32_t s_DefaultTime2Retain;
iscsi_login_session_type_t s_login_type;
uint8_t *s_target_list;
uint32_t s_target_list_len;
uint32_t s_conn_id;
uint32_t s_terminating;
uint32_t s_active_connections;
uint32_t s_total_connections;
connection_list_t s_conn_list;
connection_t *s_mru_connection;
ccb_t s_ccb[CCBS_PER_SESSION];
char s_tgtname[ISCSI_STRING_LENGTH + 1];
};
TAILQ_HEAD(session_list_s, session_s);
typedef struct session_list_s session_list_t;
typedef struct event_s {
TAILQ_ENTRY(event_s) ev_link;
iscsi_event_t ev_event_kind;
uint32_t ev_session_id;
uint32_t ev_connection_id;
uint32_t ev_reason;
} event_t;
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) evh_link;
uint32_t evh_id;
event_list_t evh_events;
iscsi_wait_event_parameters_t *evh_waiter;
event_t *evh_first_in_list;
} event_handler_t;
TAILQ_HEAD(event_handler_list_s, event_handler_s);
typedef struct event_handler_list_s event_handler_list_t;
struct iscsifd {
TAILQ_ENTRY(iscsifd) fd_link;
device_t fd_dev;
int fd_unit;
};
extern struct cfattach iscsi_ca;
extern session_list_t iscsi_sessions;
extern bool iscsi_detaching;
extern uint32_t iscsi_num_send_threads;
extern uint8_t iscsi_InitiatorName[ISCSI_STRING_LENGTH];
extern uint8_t iscsi_InitiatorAlias[ISCSI_STRING_LENGTH];
extern login_isid_t iscsi_InitiatorISID;
#ifndef DDB
#define Debugger() panic("should call debugger here (iscsi.c)")
#endif
#ifdef ISCSI_DEBUG
extern int iscsi_debug_level;
extern bool iscsi_hex_bignums;
#define DEBOUT(x) printf x
#define DEB(lev,x) { if (iscsi_debug_level >= lev) printf x ;}
#define DEBC(conn,lev,x) { if (iscsi_debug_level >= lev) { printf("S%dC%d: ", \
conn && conn->c_session ? conn->c_session->s_id : -1, \
conn ? conn->c_id : -1); printf x ;}}
#define STATIC static
#else
#define DEBOUT(x)
#define DEB(lev,x)
#define DEBC(conn,lev,x)
#define STATIC static
#endif
#define min(a, b) ((a) < (b)) ? (a) : (b)
#define max(a, b) ((a) < (b)) ? (b) : (a)
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;
}
static __inline uint32_t
ntoh3(uint8_t *bytes)
{
return (bytes[0] << 16) | (bytes[1] << 8) | bytes[2];
}
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)
static __inline bool
sn_empty(sernum_buffer_t *buf)
{
return buf->top == buf->bottom;
}
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));
}
void iscsi_init_cleanup(void);
int iscsi_destroy_cleanup(void);
void iscsi_notify_cleanup(void);
#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(uint32_t, uint32_t, int, bool);
int kill_all_sessions(void);
void handle_connection_error(connection_t *, uint32_t, int);
void add_connection_cleanup(connection_t *);
int iscsiioctl(struct file *, u_long, void *);
session_t *find_session(uint32_t);
connection_t *find_connection(session_t *, uint32_t);
int ref_session(session_t *);
void unref_session(session_t *);
int iscsidetach(device_t, int);
void iscsi_done(ccb_t *);
int map_session(session_t *, device_t);
int unmap_session(session_t *);
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_co(void *);
void ccb_timeout_co(void *);
void connection_timeout(connection_t *);
void connection_timeout_start(connection_t *, int);
void connection_timeout_stop(connection_t *);
void ccb_timeout(ccb_t *);
void ccb_timeout_start(ccb_t *, int);
void ccb_timeout_stop(ccb_t *);
void iscsi_rcv_thread(void *);
uint32_t gen_digest(const void *, size_t);
uint32_t gen_digest_2(const void *, size_t, const void *, size_t);
void create_ccbs(session_t *);
void destroy_ccbs(session_t *);
ccb_t *get_ccb(connection_t *, bool);
void free_ccb(ccb_t *);
void suspend_ccb(ccb_t *, bool);
void wake_ccb(ccb_t *, uint32_t);
void create_pdus(connection_t *);
pdu_t *get_pdu(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);
uint32_t get_sernum(session_t *, pdu_t *);
int sernum_in_window(session_t *);
int window_size(session_t *, int);
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