/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*
* iSCSI connection interfaces
*/
#define ISCSI_ICS_NAMES
#include "iscsi.h"
#include "persistent.h"
#include <sys/bootprops.h>
extern ib_boot_prop_t *iscsiboot_prop;
static void iscsi_client_notify_task(void *cn_task_void);
extern int modrootloaded;
/*
* +--------------------------------------------------------------------+
* | External Connection Interfaces |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_conn_create - This creates an iscsi connection structure and
* associates it with a session structure. The session's sess_conn_list_rwlock
* should be held as a writer before calling this function.
*/
{
/* See if this connection already exists */
/*
* Compare the ioctl information to see if
* its a match for this connection. (This
* is done by making sure the IPs are of
* the same size and then they are the
* same value.
*/
SIZEOF_SOCKADDR(addr)) == 0) {
/* It's a match, record this connection */
break;
}
}
/* If icp is found return it */
return (ISCSI_STATUS_SUCCESS);
}
/* We are creating the connection, allocate, and setup */
/*
* Setup connection
*/
/*
* IDM CN taskq
*/
return (ISCSI_STATUS_INTERNAL_ERROR);
}
icp->conn_cn_taskq =
TASKQ_DEFAULTPRI, 0);
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/* Creation of the transfer thread */
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/* setup connection queues */
/* Add new connection to the session connection list */
} else {
}
(void) iscsi_conn_kstat_init(icp);
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_conn_online - This attempts to take a connection from
* ISCSI_CONN_STATE_FREE to ISCSI_CONN_STATE_LOGGED_IN.
*/
{
/*
* If we are attempting to connect then for the purposes of the
* other initiator code we are effectively in ISCSI_CONN_STATE_IN_LOGIN.
*/
/*
* Sync base connection information before login
* A login redirection might have shifted the
* current information from the base.
*/
sizeof (icp->conn_curr_addr));
return (rval);
}
/*
* iscsi_conn_offline - This attempts to take a connection from
* any state to ISCSI_CONN_STATE_FREE.
*/
{
/*
* We can only destroy a connection if its either in
* a state of FREE or LOGGED. The other states are
* transitionary and its unsafe to perform actions
* on the connection in those states. Set a flag
* on the connection to influence the transitions
* to quickly complete. Then wait for a state
* transition.
*
* ISCSI_CONN_STATE_LOGGED_IN is set immediately at the
* start of CN_NOTIFY_FFP processing. icp->conn_state_ffp
* is set to true at the end of ffp processing, at which
* point any session updates are complete. We don't
* want to start offlining the connection before we're
* done completing the FFP processing since this might
* interrupt the discovery process.
*/
!icp->conn_state_ffp)) &&
(ddi_get_lbolt() < delay)) {
/* wait for transition */
}
switch (icp->conn_state) {
case ISCSI_CONN_STATE_FREE:
break;
if (icp->conn_state_ffp) {
/* Hold is released in iscsi_handle_logout */
(void) iscsi_handle_logout(icp);
} else {
return (ISCSI_STATUS_INTERNAL_ERROR);
}
break;
case ISCSI_CONN_STATE_FAILED:
case ISCSI_CONN_STATE_POLLING:
default:
return (ISCSI_STATUS_INTERNAL_ERROR);
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* iscsi_conn_destroy - This destroys an iscsi connection structure
* and de-associates it with the session. The connection should
* already been in the ISCSI_CONN_STATE_FREE when attempting this
* operation.
*/
{
return (ISCSI_STATUS_INTERNAL_ERROR);
}
/* Destroy transfer thread */
/* Terminate connection queues */
/*
* Remove connection from sessions linked list.
*/
/* connection first item in list */
/*
* check if this is also the last item in the list
*/
}
} else {
/*
* search session list for icp pointing
* to connection being removed. Then
* update that connections next pointer.
*/
break;
}
}
/*
* if this is the last connection in the list
* update the last_ptr to point to t_icp
*/
}
} else {
/* couldn't find session */
}
}
/* Free this Connections Data */
return (ISCSI_STATUS_SUCCESS);
}
/*
*
* Used to set the min and max login window. Input values
* are in seconds.
*/
void
{
}
/*
* Process the idm notifications
*/
{
/*
* Don't access icp if the notification is CN_CONNECT_DESTROY
* since icp may have already been freed.
*
* In particular, we cannot audit the CN_CONNECT_DESTROY event.
*
* Handle a few cases immediately, the rest in a task queue.
*/
switch (icn) {
case CN_CONNECT_FAIL:
case CN_LOGIN_FAIL:
/*
* Wakeup any thread waiting for login stuff to happen.
*/
return (IDM_STATUS_SUCCESS);
case CN_READY_FOR_LOGIN:
return (IDM_STATUS_SUCCESS);
case CN_CONNECT_DESTROY:
/*
* We released any dependecies we had on this object in
* either CN_LOGIN_FAIL or CN_CONNECT_LOST so we just need
* to destroy the IDM connection now.
*/
return (IDM_STATUS_SUCCESS);
}
/*
* Dispatch notifications to the taskq since they often require
* long blocking operations. In the case of CN_CONNECT_DESTROY
* we actually just want to destroy the connection which we
* can't do in the IDM taskq context.
*/
}
return (IDM_STATUS_SUCCESS);
}
static void
{
switch (icn) {
case CN_FFP_ENABLED:
/*
* This logic assumes that the IDM login-snooping code
* and the initiator login code will agree to go when
* the connection is in FFP or final error received.
* The reason we do this is that we don't want to process
* CN_FFP_DISABLED until CN_FFP_ENABLED has been full handled.
*/
}
break;
case CN_FFP_DISABLED:
switch (disable_type) {
case FD_SESS_LOGOUT:
case FD_CONN_LOGOUT:
if (icp->conn_async_logout) {
/*
* Our logout was in response to an
* async logout request so treat this
* like a connection failure (we will
* try to re-establish the connection)
*/
} else {
/*
* Logout due to to user config change,
* we will not try to re-establish
* the connection.
*/
/*
* Hold off generating the ISCSI_SESS_EVENT_N3
* event until we get the CN_CONNECT_LOST
* notification. This matches the pre-IDM
* implementation better.
*/
}
break;
case FD_CONN_FAIL:
default:
} else {
}
break;
}
break;
case CN_CONNECT_LOST:
/*
* We only care about CN_CONNECT_LOST if we've logged in. IDM
* sends a flag as the data payload to indicate whether we
* were trying to login. The CN_LOGIN_FAIL notification
* gives us what we need to know for login failures and
* otherwise we will need to keep a bunch of state to know
* what CN_CONNECT_LOST means to us.
*/
if (in_login ||
/* Release connect hold from CN_READY_FOR_LOGIN */
break;
}
/* Any remaining commands are never going to finish */
/*
* The connection is no longer active so cleanup any
* references to the connection and release any holds so
* that IDM can finish cleanup.
*/
} else {
/*
* If session type is NORMAL, try to reestablish the
* connection.
*/
!(ISCSI_LOGIN_TPGT_NEGO_ERROR(icp))) {
} else {
}
}
/* Release connect hold from CN_READY_FOR_LOGIN */
break;
default:
"iscsi_client_notify: unknown notification: "
"%x: NOT IMPLEMENTED YET: icp: %p ic: %p ",
break;
}
/* free the task notify structure we allocated in iscsi_client_notify */
/* Release the hold we acquired in iscsi_client_notify */
}
/*
* iscsi_conn_sync_params - used to update connection parameters
*
* Used to update connection parameters with current configured
* parameters in the persistent store. This should be called
* before starting to make a new iscsi connection in iscsi_login.
*/
{
int param_id;
char *name;
/*
* Check if someone is trying to destroy this
* connection. If so fail the sync request,
* as a method of fast fail.
*/
return (ISCSI_STATUS_SHUTDOWN);
}
/* First get a copy of the HBA params */
sizeof (iscsi_login_params_t));
sizeof (iscsi_tunable_params_t));
/*
* Now we need to get the session configured
* values from the persistent store and apply
* them to our connection.
*/
param_id++) {
if (iscsiboot_prop && modrootloaded &&
/*
* iscsi boot with mpxio disabled
* while iscsi booting target's parameter overriden
* do no update target's parameters.
*/
" default login parameters in"
" boot session as MPxIO is disabled");
}
break;
}
switch (param_id) {
/*
* Boolean parameters
*/
break;
break;
break;
break;
/*
* Integer parameters
*/
break;
break;
break;
break;
break;
break;
break;
/*
* Integer parameters which currently are unsettable
*/
/* FALLTHRU */
/* FALLTHRU */
/* FALLTHRU */
default:
break;
}
}
}
B_TRUE) {
}
}
}
}
/* Skip binding checks on discovery sessions */
return (ISCSI_STATUS_SUCCESS);
}
/*
* Now we need to get the current optional connection
* binding information.
*/
/* setup initial buffer for configured session information */
/* get configured sessions information */
/*
* If we were unable to get target level information
* then check the initiator level information.
*/
/*
* No hba information is found. So assume default
* one session unbound behavior.
*/
}
}
/*
* iscsi booting session with mpxio disabled,
* no need set multiple sessions for booting session
*/
" no need to configure multiple boot sessions");
}
/*
* Check to make sure this session is still a configured
* session. The user might have decreased the session
* count. (NOTE: byte 5 of the sess_isid is the session
* count (via MS/T). This counter starts at 0.)
*/
/*
* This is temporary session for boot session propose
* no need to bound IP for this session
*/
return (ISCSI_STATUS_SUCCESS);
}
/*
* No longer a configured session. Return a
* failure so we don't attempt to relogin.
*/
return (ISCSI_STATUS_SHUTDOWN);
}
/*
* If sessions are unbound set this information on
* the connection and return success.
*/
return (ISCSI_STATUS_SUCCESS);
}
/*
* Since the sessions are bound we need to find the matching
* binding information for the session's isid. If this
* session's isid is > 0 then we need to get more configured
* session information to find the binding info.
*/
if (idx > 0) {
int ics_out;
/* record new size and free last buffer */
/* allocate new buffer */
/* get configured sessions information */
"unable to get configured session information\n",
return (ISCSI_STATUS_SHUTDOWN);
}
}
/* Copy correct binding information to the connection */
sizeof (struct in_addr));
} else {
sizeof (struct in6_addr));
}
return (ISCSI_STATUS_SUCCESS);
}
/*
* +--------------------------------------------------------------------+
* | Internal Connection Interfaces |
* +--------------------------------------------------------------------+
*/
/*
* iscsi_conn_flush_active_cmds - flush all active icmdps
* for a connection.
*/
static void
{
} else {
}
/* Flush active queue */
}
}
/* Wait for active queue to drain */
}
}
/* Wait for IDM abort queue to drain (if necessary) */
}
}
/*
*/
void
{
(void *)icp,
/*
* Sync base connection information before login.
* A login redirection might have shifted the
* current information from the base.
*/
sizeof (icp->conn_curr_addr));
/* schedule login task */
DDI_SUCCESS) {
}
}
void
{
}
void
{
switch (next_state) {
case ISCSI_CONN_STATE_FREE:
case ISCSI_CONN_STATE_FAILED:
case ISCSI_CONN_STATE_POLLING:
"iscsi_conn_update_state conn %p %s(%d) -> %s(%d)",
(void *)icp,
break;
default:
ASSERT(0);
}
}