/*
* 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
*/
/*
*/
/*
* iSER transport routines
*
* All transport functions except iser_tgt_svc_create() are called through
* the ops vector, iser_tgt_svc_create() is called from the async handler
* inaddition to being called by the ULP
*/
struct idm_conn_s *ic);
/*
* iSER IDM transport operations
*/
&iser_pdu_tx, /* it_tx_pdu */
&iser_buf_tx_to_ini, /* it_buf_tx_to_ini */
&iser_buf_rx_from_ini, /* it_buf_rx_from_ini */
NULL, /* it_rx_datain */
NULL, /* it_rx_rtt */
NULL, /* it_rx_dataout */
NULL, /* it_alloc_conn_rsrc */
NULL, /* it_free_conn_rsrc */
&iser_tgt_enable_datamover, /* it_tgt_enable_datamover */
&iser_ini_enable_datamover, /* it_ini_enable_datamover */
NULL, /* it_conn_terminate */
&iser_free_task_rsrcs, /* it_free_task_rsrc */
&iser_negotiate_key_values, /* it_negotiate_key_values */
&iser_notice_key_values, /* it_notice_key_values */
&iser_conn_is_capable, /* it_conn_is_capable */
&iser_buf_alloc, /* it_buf_alloc */
&iser_buf_free, /* it_buf_free */
&iser_buf_setup, /* it_buf_setup */
&iser_buf_teardown, /* it_buf_teardown */
&iser_tgt_svc_create, /* it_tgt_svc_create */
&iser_tgt_svc_destroy, /* it_tgt_svc_destroy */
&iser_tgt_svc_online, /* it_tgt_svc_online */
&iser_tgt_svc_offline, /* it_tgt_svc_offline */
&iser_conn_destroy, /* it_tgt_conn_destroy */
&iser_tgt_conn_connect, /* it_tgt_conn_connect */
&iser_conn_disconnect, /* it_tgt_conn_disconnect */
&iser_ini_conn_create, /* it_ini_conn_create */
&iser_conn_destroy, /* it_ini_conn_destroy */
&iser_ini_conn_connect, /* it_ini_conn_connect */
&iser_conn_disconnect, /* it_ini_conn_disconnect */
&iser_declare_key_values /* it_declare_key_values */
};
/*
* iSER IDM transport capabilities
*/
0 /* flags */
};
int
{
if (status != IDM_STATUS_SUCCESS) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* iser_ini_conn_create()
* Allocate an iSER initiator connection context
*/
static idm_status_t
{
/* Allocate and set up a connection handle */
/* Allocate and open a channel to the target node */
return (IDM_STATUS_FAIL);
}
/*
* The local IP and remote IP are filled in iser_channel_alloc. The
* remote port needs to be filled in from idm_conn_req_t. The local
* port is irrelevant. Internal representation of the port in the
* IDM sockaddr structure is in network byte order. IBT expects the
* port in host byte order.
*/
case AF_INET:
break;
case AF_INET6:
break;
default:
}
/*
* Set a pointer to the iser_conn in the iser_chan for easy
* access during CM event handling
*/
/* Set the iSER conn handle in the IDM conn private handle */
/* Set the transport header length */
return (IDM_STATUS_SUCCESS);
}
/*
* iser_internal_conn_destroy()
* Tear down iSER-specific connection resources. This is used below
* in iser_conn_destroy(), but also from the CM code when we may have
* some of the connection established, but not fully connected.
*/
void
{
/*
* This is a target connection that has yet to be
* established. Free our reference on the target
* service handle.
*/
}
}
/*
* iser_conn_destroy()
* Tear down an initiator or target connection.
*/
static void
{
}
/*
* iser_ini_conn_connect()
* Establish the connection referred to by the handle previously allocated via
* iser_ini_conn_create().
*/
static idm_status_t
{
if (status != ISER_STATUS_SUCCESS) {
return (IDM_STATUS_FAIL);
}
/*
* Set the local and remote addresses in the idm conn handle.
*/
/* Hold a reference on the IDM connection handle */
return (IDM_STATUS_SUCCESS);
}
/*
* iser_conn_disconnect()
* Shutdown this iSER connection
*/
static void
{
/* Close the channel */
/* Free our reference held on the IDM conn handle, and set CLOSED */
}
/*
* iser_tgt_svc_create()
* Establish the CM service for inbound iSER service requests on the port
* indicated by sr->sr_port.
* idm_svc_req_t contains the service parameters.
*/
{
int rc;
/*
* Register an iSER target service for the requested port
* and set the iser_svc structure in the idm_svc handle.
*/
if (rc != DDI_SUCCESS) {
return (IDM_STATUS_FAIL);
}
return (IDM_STATUS_SUCCESS);
}
/* IDM refcnt utilities for the iSER service handle */
void
{
}
void
{
}
/*
* iser_tgt_svc_destroy()
* Teardown resources allocated in iser_tgt_svc_create()
*/
static void
{
/*
* Deregister the iSER target service on this port and free
* the iser_svc structure from the idm_svc handle.
*/
/* Wait for the iSER service handle's refcnt to zero */
}
/*
* iser_tgt_svc_online()
* Bind the CM service allocated via iser_tgt_svc_create().
*/
static idm_status_t
{
/*
* Pass the IDM service handle as the client private data for
* later use.
*/
if (status != ISER_STATUS_SUCCESS) {
return (IDM_STATUS_FAIL);
}
return (IDM_STATUS_SUCCESS);
}
/*
* iser_tgt_svc_offline
* Unbind the service on all available HCA ports.
*/
static void
{
}
/*
* iser_tgt_conn_connect()
* Establish the connection in ic, passed from idm_tgt_conn_finish(), which
* is invoked from the SM as a result of an inbound connection request.
*/
/* ARGSUSED */
static idm_status_t
{
/* No action required */
return (IDM_STATUS_SUCCESS);
}
/*
* iser_tgt_enable_datamover() sets the transport private data on the
* idm_conn_t and move the conn stage to indicate logged in.
*/
static idm_status_t
{
return (IDM_STATUS_SUCCESS);
}
/*
* iser_ini_enable_datamover() is used by the iSCSI initator to request that a
* specified iSCSI connection be transitioned to iSER-assisted mode.
* In the case of iSER, the RDMA resources for a reliable connection have
* already been allocated at this time, and the 'RDMAExtensions' is set to 'Yes'
* so no further negotiations are required at this time.
* The initiator now sends the first iSER Message - 'Hello' to the target
* and waits for the 'HelloReply' Message from the target before directing
* the initiator to go into the Full Feature Phase.
*
* No transport op is required on the target side.
*/
static idm_status_t
{
int status;
/* Send the iSER Hello Message to the target */
if (status != ISER_STATUS_SUCCESS) {
return (IDM_STATUS_FAIL);
}
/*
* Acquire the iser_conn->ic_lock and wait for the iSER HelloReply
* Message from the target, i.e. iser_conn_stage_t to be set to
* ISER_CONN_STAGE_HELLOREPLY_RCV. If the handshake does not
* complete within a specified time period (.5s), then return failure.
*
*/
(ddi_get_lbolt() < delay)) {
}
/*
* Return suceess to indicate that the initiator connection can
* go to the next phase - FFP
*/
return (IDM_STATUS_SUCCESS);
default:
return (IDM_STATUS_FAIL);
}
/* STATEMENT_NEVER_REACHED */
}
/*
* iser_free_task_rsrcs()
* This routine does not currently need to do anything. It is used in
* the sockets transport to explicitly complete any buffers on the task,
* but we can rely on our RCaP layer to finish up it's work without any
* intervention.
*/
/* ARGSUSED */
{
return (IDM_STATUS_SUCCESS);
}
/*
* iser_negotiate_key_values() validates the key values for this connection
*/
/* ARGSUSED */
static kv_status_t
{
/* Process the request nvlist */
/* We must be using RDMA, so set the flag on the ic handle */
return (kvrc);
}
/* Process a list of key=value pairs from a login request */
static kv_status_t
{
char *nvp_name;
/* Process the list */
if (kvrc != KV_HANDLED) {
if (kvrc == KV_HANDLED_NO_TRANSIT) {
/* we countered, clear the transit flag */
} else {
/* error, bail out */
break;
}
}
}
/*
* If the current kv_status_t indicates success, we've handled
* the entire list. Explicitly set kvrc to NO_TRANSIT if we've
* cleared the transit flag along the way.
*/
}
return (kvrc);
}
/* Handle a given list, boolean or numerical key=value pair */
static kv_status_t
{
int nvrc;
/* Retrieve values for booleans and numericals */
/* Booleans */
case KI_RDMA_EXTENSIONS:
case KI_IMMEDIATE_DATA:
break;
/* Numericals */
break;
default:
break;
}
/*
* Now handle the values according to the key name. Keys not
* specifically handled here will be negotiated by the iscsi
* target. Negotiated values take effect when
* iser_notice_key_values gets called.
*/
case KI_RDMA_EXTENSIONS:
/* Ensure "Yes" */
break;
/* Validate the proposed value */
break;
/* Validate the proposed value */
break;
case KI_IMMEDIATE_DATA:
/* Ensure "No" */
break;
/* Validate the proposed value */
break;
default:
/*
* All other keys, including invalid keys, will be
* handled at the client layer.
*/
kvrc = KV_HANDLED;
break;
}
return (kvrc);
}
/* Validate a proposed boolean value, and set the alternate if necessary */
static kv_status_t
{
int nvrc;
if (value != iser_value) {
/*
* Respond back to initiator with our value, and
* set the return value to unset the transit bit.
*/
value = iser_value;
if (nvrc == 0) {
}
} else {
/* Add this to our negotiated values */
/* Respond if this is not a declarative */
}
/* Response of Simple-value Negotiation */
/* Remove from the request (we've handled it) */
}
if (kvrc == KV_HANDLED_NO_TRANSIT) {
return (kvrc);
}
return (idm_nvstat_to_kvstat(nvrc));
}
/*
* maximum values, and set an alternate, if necessary. Note that the value
* 'iser_max_value" represents our implementation maximum (typically the max).
*/
static kv_status_t
{
int nvrc;
/* Validate against standard */
} else {
if (value > iser_max_value) {
/*
* Respond back to initiator with our value, and
* set the return value to unset the transit bit.
*/
if (nvrc == 0) {
}
} else {
/* Add this to our negotiated values */
/* Respond if this is not a declarative */
}
/* Response of Simple-value Negotiation */
/* Remove from the request (we've handled it) */
(void) nvlist_remove_all(request_nvl,
ikvx->ik_key_name);
}
}
if (kvrc == KV_HANDLED_NO_TRANSIT) {
return (kvrc);
}
return (idm_nvstat_to_kvstat(nvrc));
}
/*
* iser_declare_key_values() declares the declarative key values for
* this connection.
*/
/* ARGSUSED */
static kv_status_t
{
int nvrc = 0;
int rc;
if (outgoing_nvl) {
}
}
return (kvrc);
}
/*
* iser_notice_key_values() activates the negotiated key values for
* this connection.
*/
static void
{
int nvrc;
char *digest_choice_string;
/*
* Validate the final negotiated operational parameters,
* and save a copy.
*/
/*
* Per the iSER RFC, override the negotiated value with "None"
*/
}
/*
* Per the iSER RFC, override the negotiated value with "None"
*/
}
}
/*
* Per the iSER RFC, override the negotiated value with "No"
*/
}
/*
* Per the iSER RFC, override the negotiated value with "No"
*/
}
}
}
}
/* Test boolean values which are required by RFC 5046 */
#ifdef ISER_DEBUG
#endif
}
/*
* iser_conn_is_capable() verifies that the passed connection is provided
* for by an iSER-capable link.
* NOTE: When utilizing InfiniBand RC as an RCaP, this routine will check
* if the link is on IPoIB. This only indicates a chance that the link is
* on an RCaP, and thus iSER-capable, since we may be running on an IB-Eth
* gateway, or other IB but non-RCaP link. Rather than fully establishing the
* link to verify RCaP here, we instead will return B_TRUE
* indicating the link is iSER-capable, if the link is IPoIB. If then in
* iser_ini_conn_create() the link proves not be RCaP, IDM will fall back
* to using the IDM Sockets transport.
*/
/* ARGSUSED */
static boolean_t
{
/* A NULL value for laddr indicates implicit source */
}
/*
* iser_pdu_tx() transmits a Control PDU via the iSER channel. We pull the
* channel out of the idm_conn_t passed in, and pass it and the pdu to the
* iser_xfer routine.
*/
static void
{
if (iser_status != ISER_STATUS_SUCCESS) {
/* Fail this PDU transmission */
}
/*
* We successfully posted this PDU for transmission.
* The completion handler will invoke idm_pdu_complete()
* with the completion status. See iser_cq.c for more
* information.
*/
}
/*
* iser_buf_tx_to_ini() transmits the data buffer encoded in idb to the
* initiator to fulfill SCSI Read commands. An iser_xfer routine is invoked
* to implement the RDMA operations.
*
* Caller holds idt->idt_mutex.
*/
static idm_status_t
{
if (iser_status != ISER_STATUS_SUCCESS) {
"iser_xfer_buf_to_ini: idt (0x%p) idb (0x%p)",
return (IDM_STATUS_FAIL);
}
/*
* iSCSIt's Data Completion Notify callback is invoked from
* the Work Request Send completion Handler
*/
return (idm_status);
}
/*
* iser_buf_tx_from_ini() transmits data from the initiator into the buffer
* in idb to fulfill SCSI Write commands. An iser_xfer routine is invoked
* to implement the RDMA operations.
*
* Caller holds idt->idt_mutex.
*/
static idm_status_t
{
if (iser_status != ISER_STATUS_SUCCESS) {
"iser_xfer_buf_from_ini: idt (0x%p) idb (0x%p)",
return (IDM_STATUS_FAIL);
}
/*
* iSCSIt's Data Completion Notify callback is invoked from
* the Work Request Send completion Handler
*/
return (idm_status);
}
/*
* iser_buf_alloc() allocates a buffer and registers it with the IBTF for
* use with iSER. Each HCA has it's own kmem cache for establishing a pool
* of registered buffers, when once initially allocated, will remain
* registered with the HCA. This routine is invoked only on the target,
* where we have the requirement to pre-allocate buffers for the upper layers.
* Note: buflen is compared to ISER_DEFAULT_BUFLEN, and allocation is failed
* if the requested buflen is larger than our default.
*/
/* ARGSUSED */
static idm_status_t
{
if (buflen > ISER_DEFAULT_BUFLEN) {
return (IDM_STATUS_FAIL);
}
/*
* Allocate a buffer from this HCA's cache. Once initialized, these
* will remain allocated and registered (see above).
*/
return (IDM_STATUS_FAIL);
}
/* Set the allocated data buffer pointer in the IDM buf handle */
/* Set the private buf and reg handles in the IDM buf handle */
return (IDM_STATUS_SUCCESS);
}
/*
* iser_buf_free() frees the buffer handle passed in. Note that the cached
* kmem object has an HCA-registered buffer in it which will not be freed.
* This allows us to build up a cache of pre-allocated and registered
* buffers for use on the target.
*/
static void
{
}
/*
* iser_buf_setup() is invoked on the initiator in order to register memory
* on demand for use with the iSER layer.
*/
static idm_status_t
{
int status;
/*
* Memory registration is known to be slow, so for small
* transfers, use pre-registered memory buffers and just
*/
iser_buf =
/* Fail over to dynamic registration */
return (status);
}
/*
* Set the allocated data buffer pointer in the IDM buf handle
*/
/* Set the private buf and reg handles in the IDM buf handle */
/* Ensure bufalloc'd flag is set */
return (IDM_STATUS_SUCCESS);
} else {
/* Dynamically register the memory passed in on the idb */
/* Ensure bufalloc'd flag is unset */
return (status);
}
}
/*
* iser_buf_teardown() is invoked on the initiator in order to register memory
* on demand for use with the iSER layer.
*/
static void
{
/* Deregister the memory passed in on the idb */
}