/* $NetBSD: iscsi_globals.h,v 1.25 2019/04/21 11:45:08 maya 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "iscsi.h" #include "iscsi_pdu.h" #include "iscsi_ioctl.h" /* ------------------------ Code selection constants ------------------------ */ #define ISCSI_DEBUG 0 /* ------------------------- Global Constants ----------------------------- */ /* Version information */ #define INTERFACE_VERSION 2 #define VERSION_MAJOR 3 #define VERSION_MINOR 1 #define VERSION_STRING "NetBSD iSCSI Software Initiator 20110407" /* 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 32 /* ToDo: Reasonable number?? */ /* NOTE: CCBS_FOR_SCSPI limits the number of outstanding commands for SCSI commands, leaving some CCBs for keepalive and logout attempts, which are needed for each connection. */ #define CCBS_FOR_SCSIPI 16 /* 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 (60 * hz) /* ToDo: Reasonable? (60 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 0x0001 /* received status */ #define CCBF_RESENT 0x0002 /* ccb was resent */ #define CCBF_SENDTARGET 0x0004 /* SendTargets text request, not negotiation */ #define CCBF_GOT_RSP 0x0010 /* Got at least one response to this request */ #define CCBF_REASSIGN 0x0020 /* Command can be reassigned */ #define CCBF_OTHERCONN 0x0040 /* a logout for a different connection */ #define CCBF_WAITQUEUE 0x0080 /* CCB is on waiting queue */ /* --------------------------- 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_FREE, /* Free this PDU when done */ PDUDISP_WAIT /* Waiting for acknowledge */ } pdu_disp_t; /* Timeout state */ typedef enum { TOUT_NONE, /* Initial */ TOUT_ARMED, /* callout is scheduled */ TOUT_QUEUED, /* put into timeout queue */ TOUT_BUSY /* cleanup thread working */ } 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; /* 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 */ bool ack[SERNUM_BUFFER_LENGTH]; /* acknowledged? */ } sernum_buffer_t; /* The per-PDU data structure. */ struct pdu_s { TAILQ_ENTRY(pdu_s) pdu_chain; /* freelist or wait list (or no list) */ TAILQ_ENTRY(pdu_s) pdu_send_chain; /* chaining PDUs waiting to be sent */ pdu_disp_t pdu_disp; /* what to do with this pdu */ uint32_t pdu_flags; /* various processing flags */ pdu_header_t pdu_hdr; /* Buffer for PDU associated with cmd */ void *pdu_temp_data; /* (free after use) */ uint32_t pdu_temp_data_len; /* size of temp data */ struct uio pdu_uio; /* UIO structure */ struct iovec pdu_io_vec[4]; /* Header + data + data-digest + padding */ struct uio pdu_save_uio; /* UIO structure save for retransmits */ struct iovec pdu_save_iovec[4]; /* Header + data + data-digest + padding */ uint32_t pdu_data_digest; /* holds data digest if enabled */ ccb_t *pdu_owner; /* the ccb this PDU belongs to (if any) */ connection_t *pdu_connection; /* the connection this PDU belongs to */ }; /* 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) ccb_chain; /* either freelist or waiting list (or no list) */ uint32_t ccb_status; /* Status gets entered here */ ccb_disp_t ccb_disp; /* what to do with this ccb */ struct callout ccb_timeout; /* To make sure it isn't lost */ TAILQ_ENTRY(ccb_s) ccb_tchain; tout_state_t ccb_timedout; int ccb_num_timeouts; /* How often we've sent out SNACK without answer */ int ccb_total_tries; /* How often we've tried to recover */ uint32_t ccb_ITT; /* task tag: ITT counter + sess id + CCB index */ sernum_buffer_t ccb_DataSN_buf; /* Received Data Seq nums (read ops only) */ void *ccb_par; /* misc. parameter for this request */ struct scsipi_xfer *ccb_xs; /* the scsipi_xfer for this cmd */ void *ccb_temp_data; /* to hold state (mainly during negotiation) */ void *ccb_text_data; /* holds accumulated text for continued PDUs */ uint32_t ccb_text_len; /* length of text data so far */ uint64_t ccb_lun; /* LUN */ uint32_t ccb_tag; /* Command tag */ uint8_t *ccb_cmd; /* SCSI command block */ uint16_t ccb_cmdlen; /* SCSI command block length */ bool ccb_data_in; /* if this is a read request */ uint8_t *ccb_data_ptr; /* data pointer for read/write */ uint32_t ccb_data_len; /* total data length */ uint32_t ccb_xfer_len; /* data transferred on read */ uint32_t ccb_residual; /* residual data size */ void *ccb_sense_ptr; /* sense data pointer */ int ccb_sense_len_req; /* requested sense data length */ int ccb_sense_len_got; /* actual sense data length */ pdu_t *ccb_pdu_waiting; /* PDU waiting to be ack'ed */ volatile uint32_t ccb_CmdSN; /* CmdSN associated with waiting PDU */ int ccb_flags; connection_t *ccb_connection; /* connection for CCB */ session_t *ccb_session; /* session for CCB */ }; /* 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 */ 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; /* the free PDU pool */ ccb_list_t c_ccbs_waiting; /* CCBs waiting for completion */ pdu_list_t c_pdus_to_send; /* the PDUs waiting to be sent */ sernum_buffer_t c_StatSN_buf; /* to keep track of received StatSNs */ uint32_t c_max_transfer; /* min(MaxRecvDataSegmentLength, MaxBurstLength) */ uint32_t c_max_firstimmed; /* 0 if InitialR2T=Yes, else min of (MaxRecvDataSegmentLength, FirstBurstLength) */ uint32_t c_max_firstdata; /* 0 if ImmediateData=No, else min of */ /* (MaxRecvDataSegmentLength, FirstBurstLength) */ uint32_t c_MaxRecvDataSegmentLength; /* Target's value */ uint32_t c_Our_MaxRecvDataSegmentLength; /* Our own value */ bool c_HeaderDigest; /* TRUE if doing CRC */ bool c_DataDigest; /* TRUE if doing CRC */ uint32_t c_Time2Wait; /* Negotiated default or logout value */ uint32_t c_Time2Retain; /* Negotiated default or logout value */ uint16_t c_id; /* connection ID (unique within session) */ conn_state_t c_state; /* State of connection */ struct lwp *c_threadobj; /* proc/thread pointer of socket owner */ struct file *c_sock; /* the connection's socket */ session_t *c_session; /* back pointer to the owning session */ struct lwp *c_rcvproc; /* receive thread */ struct lwp *c_sendproc; /* send thread */ uint32_t c_terminating; /* if closing down: status */ int c_recover; /* recovery count */ /* (reset on first successful data transfer) */ volatile unsigned c_usecount; /* number of active CCBs */ volatile unsigned c_pducount; /* number of active PDUs */ bool c_destroy; /* conn will be destroyed */ bool c_in_session; /* if it's linked into the session list */ logout_state_t c_loggedout; /* status of logout (for recovery) */ struct callout c_timeout; /* Timeout for checking if connection is dead */ TAILQ_ENTRY(connection_s) c_tchain; tout_state_t c_timedout; int c_num_timeouts; /* How often we've sent out a NOP without answer */ uint32_t c_idle_timeout_val; /* Connection timeout value when idle */ iscsi_login_parameters_t *c_login_par; /* only valid during login */ pdu_t c_pdu[PDUS_PER_CONNECTION]; /* PDUs */ }; /* 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 s_sc_adapter; struct scsipi_channel s_sc_channel; device_t s_child_dev; /* the child we're associated with - (NULL if not mapped) */ int s_refcount; /* session in use by scsipi */ /* local stuff */ TAILQ_ENTRY(session_s) s_sessions; /* the list of sessions */ kmutex_t s_lock; kcondvar_t s_sess_cv; kcondvar_t s_ccb_cv; ccb_list_t s_ccb_pool; /* The free CCB pool */ int s_send_window; uint16_t s_id; /* session ID (unique within driver) */ uint16_t s_TSIH; /* Target assigned session ID */ uint32_t s_CmdSN; /* Current CmdSN */ uint32_t s_ExpCmdSN; /* Current max ExpCmdSN received */ uint32_t s_MaxCmdSN; /* Current MaxCmdSN */ /* negotiated values */ 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; /* session type */ /* for send_targets requests */ uint8_t *s_target_list; uint32_t s_target_list_len; uint32_t s_conn_id; /* connection ID counter */ uint32_t s_terminating; /* if closing down: status */ uint32_t s_active_connections; /* currently active connections */ uint32_t s_total_connections; /* connections associated with this session (active or winding down) */ connection_list_t s_conn_list; /* the list of connections */ connection_t *s_mru_connection; /* the most recently used connection */ ccb_t s_ccb[CCBS_PER_SESSION]; /* CCBs */ char s_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; /* Event notification structures */ typedef struct event_s { TAILQ_ENTRY(event_s) ev_link; /* next event in queue */ iscsi_event_t ev_event_kind; /* which event */ uint32_t ev_session_id; /* affected session ID */ uint32_t ev_connection_id;/* affected connection ID */ uint32_t ev_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) evh_link; /* next handler */ uint32_t evh_id; /* unique ID */ event_list_t evh_events; /* list of events pending */ iscsi_wait_event_parameters_t *evh_waiter; /* waiting parameter */ /* following to detect dead handlers */ event_t *evh_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; /* /dev/iscsi0 state */ struct iscsifd { TAILQ_ENTRY(iscsifd) fd_link; device_t fd_dev; int fd_unit; }; /* ------------------------- Global Variables ----------------------------- */ /* In iscsi_main.c */ extern struct cfattach iscsi_ca; /* the device attach structure */ extern session_list_t iscsi_sessions; /* the list of sessions */ extern bool iscsi_detaching; /* signal to cleanup thread it should exit */ extern uint32_t iscsi_num_send_threads; /* the number of active send threads */ extern uint8_t iscsi_InitiatorName[ISCSI_STRING_LENGTH]; extern uint8_t iscsi_InitiatorAlias[ISCSI_STRING_LENGTH]; extern login_isid_t iscsi_InitiatorISID; /* Debugging stuff */ #ifndef DDB #define Debugger() panic("should call debugger here (iscsi.c)") #endif /* ! DDB */ #ifdef ISCSI_DEBUG extern int iscsi_debug_level; /* How much debug info to display */ extern bool iscsi_hex_bignums; /* Whether to encode parameters in hex or base64 */ #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->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 /* Critical section macros */ /* 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)); } /* in iscsi_ioctl.c */ void iscsi_init_cleanup(void); int iscsi_destroy_cleanup(void); void iscsi_notify_cleanup(void); /* 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); int kill_all_sessions(void); void handle_connection_error(connection_t *, uint32_t, int); void add_connection_cleanup(connection_t *); #ifndef ISCSI_MINIMAL uint32_t map_databuf(struct proc *, void **, uint32_t); void unmap_databuf(struct proc *, void *, uint32_t); #endif 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 *); /* in iscsi_main.c */ /*void iscsiattach(void *); */ int iscsidetach(device_t, int); void iscsi_done(ccb_t *); int map_session(session_t *, device_t); int 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_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 *); /* in iscsi_rcv.c */ void iscsi_rcv_thread(void *); /* in iscsi_utils.c */ 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); /* 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 */