/*
* 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
*/
/*
*/
/*
* SCSI Target Port I/F for Solaris SCSI RDMA Protocol Target (SRP)
* port provider module for the COMSTAR framework.
*/
#include <sys/sysmacros.h>
#include <sys/stmf_ioctl.h>
#include "srp.h"
#include "srpt_impl.h"
#include "srpt_cm.h"
#include "srpt_ioc.h"
#include "srpt_ch.h"
#include "srpt_stp.h"
extern srpt_ctxt_t *srpt_ctxt;
extern uint32_t srpt_iu_size;
/*
* STMF LPort Interface Prototypes
*/
extern uint16_t srpt_send_msg_depth;
/*
* srpt_stp_start_srp() - Start SRP service
*
* Enable the SRP service for the specified SCSI Target Port.
*/
int
{
int port;
SRPT_DPRINTF_L1("stp_start_srp, NULL SCSI target port");
return (IBT_FAILURE);
}
SRPT_DPRINTF_L1("stp_start_srp, SCSI target port NULL"
" IOC pointer");
return (IBT_FAILURE);
}
SRPT_DPRINTF_L2("stp_start_srp, register SRP service for"
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("stp_start_srp, SRP service creation err (%d)",
status);
return (status);
}
/*
* Bind the service associated with the SCSI target port to
* each active port of the I/O Controller.
*/
if (status != IBT_SUCCESS &&
SRPT_DPRINTF_L1("start_srp, Unable to bind"
" service (%d)", status);
goto srp_start_err;
}
}
/* don't online if we have no active ports */
if (tgt->tp_num_active_ports == 0) {
SRPT_DPRINTF_L2("start_srp, no ports active for svc_id %016llx",
goto srp_start_err;
}
/*
* Calculate the new I/O Controller profile and either update the
* profile if previously registered or register it with the IB
* Device Management Agent.
*/
SRPT_DPRINTF_L3("start_srp, update I/O Controller profile (%016llx)",
ioc->ioc_ibdma_hdl =
SRPT_DPRINTF_L1("start_srp, Unable to register"
" I/O Profile for svc_id %016llx",
goto srp_start_err;
}
} else {
if (dma_status != IBDMA_SUCCESS) {
SRPT_DPRINTF_L1("start_srp, Unable to update I/O"
" Profile for svc_id %016llxi (%d)",
goto srp_start_err;
}
}
return (IBT_SUCCESS);
tgt->tp_srp_enabled = 0;
tgt->tp_num_active_ports = 0;
}
return (status);
}
/*
* srpt_stp_stop_srp() - Stop SRP service.
*
* Disable the SRP service on the specified SCSI Target Port.
*/
void
{
SRPT_DPRINTF_L2("stp_stop_srp, NULL SCSI Target Port"
" specified");
return;
}
SRPT_DPRINTF_L2("stp_stop_srp, bad Target, IOC NULL");
return;
}
/*
* Update the I/O Controller profile to remove the SRP service
* for this SCSI target port.
*/
tgt->tp_srp_enabled = 0;
SRPT_DPRINTF_L3("stp_stop_srp, update I/O Controller"
SRPT_DPRINTF_L3("stp_stop_srp, no services active"
" unregister IOC profile");
ioc->ioc_ibdma_hdl);
} else {
if (dma_status != IBDMA_SUCCESS) {
SRPT_DPRINTF_L1("stp_stop_srp, Unable to"
" update I/O Profile (%d)", dma_status);
return;
}
}
}
/*
* Unbind the SRP service associated with the SCSI target port
* from all of the I/O Controller physical ports.
*/
SRPT_DPRINTF_L2("stp_stop_srp, unbind and de-register service"
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("stp_stop_srp, de-register service"
" error(%d)", status);
}
}
/*
* SRP service is now off-line for this SCSI Target Port.
* We force a disconnect (i.e. SRP Target Logout) for any
* active SRP logins.
*/
SRPT_DPRINTF_L3("stp_stop_srp, disconnect ch(%p)",
(void *)ch);
}
/*
* wait for all sessions to terminate before returning
*/
}
}
/*
* srpt_stp_alloc_port() - Allocate SCSI Target Port
*/
{
SRPT_DPRINTF_L1("stp_alloc_port, NULL I/O Controller");
return (NULL);
}
SRPT_DPRINTF_L3("stp_alloc_port, allocate STMF local port");
SRPT_DPRINTF_L1("tgt_alloc_port, stmf_alloc failed");
return (NULL);
}
tgt->tp_drv_disabled = 0;
tgt->tp_srp_enabled = 0;
tgt->tp_hw_port =
tgt->tp_num_active_ports = 0;
/* set up as alua participating port */
SRPT_DPRINTF_L3("stp_alloc_port, register STMF LPORT");
if (status == STMF_SUCCESS) {
SRPT_DPRINTF_L3("stp_alloc_port, LPORT successfully"
" registered");
return (tgt);
}
/*
* This is only done on an administrative thread of
* execution so it is ok to take a while.
*/
SRPT_DPRINTF_L3("stp_alloc_port, delaying");
goto retry_registration;
}
SRPT_DPRINTF_L1("stp_alloc_port, STMF register local port err(0x%llx)",
SRPT_DPRINTF_L3("stp_alloc_port, free STMF local port");
if (tgt->tp_hw_port) {
}
if (tgt->tp_scsi_devid) {
}
return (NULL);
}
/*
* srpt_stp_free_port() - Free SCSI Target Port
*/
{
SRPT_DPRINTF_L3("stp_free_port, free STMF local port");
if (tgt->tp_hw_port) {
}
if (tgt->tp_scsi_devid) {
}
return (STMF_SUCCESS);
}
/*
* srpt_stp_destroy_port()
*/
{
SRPT_DPRINTF_L3("stp_destroy_port, de-register STMF LPORT");
if (tgt->tp_drv_disabled != 0) {
/* already being destroyed, get out now - should not happen */
return (STMF_ALREADY);
}
SRPT_DPRINTF_L2("stp_destroy_port: unbind and de-register"
/*
* Wait for asynchronous target off-line operation
* to complete and then deregister the target
* port.
*/
}
SRPT_DPRINTF_L3("stp_destroy_port: IOC (0x%016llx) Target"
/* loop waiting for all I/O to drain */
for (;;) {
} else {
break;
}
}
if (status == STMF_SUCCESS) {
SRPT_DPRINTF_L3("stp_destroy_port, LPORT de-register"
" complete");
} else {
/*
* Something other than a BUSY error, this should not happen.
*/
"stp_destroy_port, de-register STMF error(0x%llx)",
}
return (status);
}
/*
* srpt_stp_xfer_data()
*/
/* ARGSUSED */
static stmf_status_t
{
SRPT_DPRINTF_L3("stp_xfer_data, invoked task (%p), dbuf (%p)",
/*
* We should use iu->iu_ch->ch_swqe_posted to throttle
* send wqe posting. This is very unlikely because we limit
* the maximum number of initiator descriptors per IU (impact
* of fragmentation of intiator buffer space) but it could occur
* if the back-end (STMF) were to use too many small buffers. In
* that case we would want to return STMF_BUSY.
*/
SRPT_DPRINTF_L4("stp_xfer_data, dbuf->db_flags (0x%x)",
SRPT_DPRINTF_L4("stp_xfer_data, dbuf->db_data_size (%d)",
dbuf->db_data_size);
SRPT_DPRINTF_L4("stp_xfer_data, dbuf->db_relative_offset (%d)",
/*
* Check to see if request will overflow the remote buffer; if so
* return a bad status and let STMF abort the task.
*/
iu->iu_tot_xfer_len) {
SRPT_DPRINTF_L2("stp_xfer_data, overflow of remote buffer");
return (STMF_FAILURE);
}
/*
* We know that the data transfer is within the bounds described
* by our list of remote buffer descriptors. Find the starting
* point based on the offset for the transfer, then perform the
* RDMA operations required of this transfer.
*/
base_offset = 0;
desc++;
}
xferred_len = 0;
/*
* If the channel is no longer connected then return an
* error and do not initiate I/O. STMF should abort the
* task.
*/
return (STMF_FAILURE);
}
while (xfer_len > 0) {
/*
* We only generate completion entries on the last IB
* operation associated with any STMF buffer.
*/
} else {
}
SRPT_DPRINTF_L4("stp_xfer_data, post RDMA operation");
/*
* If this task is being aborted or has been aborted,
* do not post additional I/O.
*/
SRPT_IU_STMF_ABORTING | SRPT_IU_ABORTED)) != 0) {
return (STMF_SUCCESS);
}
/*
* If a non-error CQE will be requested, add a reference to
* the IU and initialize the work request appropriately.
*/
SRPT_SWQE_TYPE_DATA, (void *)dbuf);
return (STMF_BUSY);
}
} else {
}
if (status != IBT_SUCCESS) {
/*
* Could not post to IB transport, report to STMF and
* and let it initiate an abort of the task.
*/
SRPT_DPRINTF_L2("stp_xfer_data, post RDMA"
" error (%d)", status);
}
return (STMF_FAILURE);
}
xferred_len += rdma_len;
desc_offset = 0;
desc++;
}
return (STMF_SUCCESS);
}
/*
* srpt_stp_send_mgmt_response() - Return SRP task managment response IU
*/
{
/*
* Report ULP credits we have added since last response sent
* over this channel.
*/
/* srp_rsp_t is padded out, so use explicit size here */
if (srp_rsp != SRP_TM_SUCCESS) {
rsp_length += sizeof (srp_rsp_data_t);
}
SRPT_DPRINTF_L4("stp_send_mgmt_response, sending on ch(%p),"
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L2("stp_send_mgmt_response, post "
"response err(%d)", status);
}
return (status);
}
/*
* srpt_stp_send_response() - Send SRP command response IU
*/
{
/*
* Report ULP credits we have added since last response sent
* over this channel.
*/
if (resid != 0) {
if ((flags & SRP_RSP_DO_OVER) ||
(flags & SRP_RSP_DO_UNDER)) {
} else if ((flags & SRP_RSP_DI_OVER) ||
(flags & SRP_RSP_DI_UNDER)) {
}
}
if (sense_length != 0) {
if (SRP_RSP_SIZE + sense_length >
}
}
SRPT_DPRINTF_L4("stp_send_reponse, sending on ch(%p),"
(void *)iu, rsp_length);
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L2("stp_send_response, post response err(%d)",
status);
}
return (status);
}
/*
* srpt_stp_send_status()
*/
/* ARGSUSED */
{
SRPT_DPRINTF_L3("stp_send_status, invoked task (%p)"
", task_completion_status (%d)"
", task_resid (%d)"
", task_status_ctrl (%d)"
", task_scsi_status (%d)"
", task_sense_length (%d)"
", task_sense_data (%p)",
(void *)task,
(int)task->task_completion_status,
(void *)task->task_sense_data);
SRPT_IU_SRP_ABORTING | SRPT_IU_ABORTED)) != 0) {
return (STMF_FAILURE);
}
/*
* Indicate future aborts can not be initiated (although
* we will handle any that have been requested since the
* last I/O completed and before we are sending status).
*/
/*
* Send SRP command response or SRP task mgmt response.
*/
if (task->task_mgmt_function == 0) {
"stp_send_status, data out overrun");
"stp_send_status, data in overrun");
}
"stp_send_status, data out underrun");
"stp_send_status, data in underrun");
}
}
} else {
(task->task_scsi_status ?
}
/*
* If we have an error posting the response return bad status
* to STMF and let it initiate an abort for the task.
*/
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L2("stp_send_status, post response err(%d)",
status);
/* clear the response sent flag since it never went out */
return (STMF_FAILURE);
}
return (STMF_SUCCESS);
}
/*
* srpt_stp_task_free() - STMF call-back.
*/
static void
{
SRPT_DPRINTF_L3("stp_task_free, invoked task (%p)",
(void *)task);
/*
* Do not hold IU lock while task is being removed from
* the session list - possible deadlock if cleaning up
* channel when this is called.
*/
srpt_ch_release_ref(ch, 0);
}
/*
* srpt_stp_abort() - STMF call-back.
*/
/* ARGSUSED */
static stmf_status_t
{
SRPT_DPRINTF_L3("stp_abort, invoked lport (%p), arg (%p)",
/*
* If no I/O is outstanding then immediately transition to
* aborted state. If any I/O is in progress OR we've sent the
* completion response, then indicate that an STMF abort has been
* requested and ask STMF to call us back later to complete the abort.
*/
(iu->iu_sq_posted_cnt > 0)) {
SRPT_DPRINTF_L3("stp_abort, deferring abort request. "
"%d outstanding I/O for IU %p",
} else {
SRPT_DPRINTF_L3("stp_abort, no outstanding I/O for %p",
(void *)iu);
/* Synchronous abort - STMF will call task_free */
}
return (status);
}
/*
* srpt_stp_task_poll() - STMF call-back
*/
static void
{
SRPT_DPRINTF_L3("stp_task_poll, invoked, task (%p)",
(void *)task);
}
/*
* srpt_stp_ctl() - STMF call-back
*/
static void
{
char *why;
why = "<null>";
}
SRPT_DPRINTF_L2("stp_ctl, invoked for LPORT (0x%016llx), cmd (%d), "
switch (cmd) {
case STMF_CMD_LPORT_ONLINE:
SRPT_DPRINTF_L2("stp_ctl, LPORT_ONLINE command,"
/*
* If the SCSI Target Port is not enabled by the driver,
* don't start and instead return busy. This is a
* creation/destruction transitional state and the will
* either go away or become enabled.
*/
if (tgt->tp_drv_disabled != 0) {
SRPT_DPRINTF_L1("stp_ctl, set LPORT_ONLINE failed - "
"LPORT (0x%016llx) BUSY",
} else {
if (status != IBT_SUCCESS) {
if (tgt->tp_num_active_ports == 0) {
"stp_ctl, no ports active "
"for HCA 0x%016llx. Target will "
"not be placed online.",
}
}
}
SRPT_DPRINTF_L3("stp_ctl, (0x%016llx) LPORT_ONLINE command"
&cstatus);
if (status != STMF_SUCCESS) {
SRPT_DPRINTF_L1("stp_ctl, ONLINE_COMPLETE returned"
}
break;
case STMF_CMD_LPORT_OFFLINE:
SRPT_DPRINTF_L2("stp_ctl, LPORT_OFFLINE command,"
/*
* Only keep persistent state if explicitly requested by user
* action, such as stmfadm offline-target or
* svcadm disable stmf.
* If not requested by the user, this was likely triggered by
* not having any HCA ports active.
*/
}
} else {
}
SRPT_DPRINTF_L3("stp_ctl, notify STMF OFFLINE complete"
if (status != STMF_SUCCESS) {
SRPT_DPRINTF_L1("stp_ctl, OFFLINE_COMPLETE returned"
}
break;
SRPT_DPRINTF_L2("stp_ctl, LPORT_ONLINE_COMPLETE ACK from"
" STMF");
SRPT_DPRINTF_L2("stp_ctl, LPORT is ONLINE");
} else {
SRPT_DPRINTF_L2("stp_ctl, LPORT not on-lining");
}
break;
SRPT_DPRINTF_L2("stp_ctl, LPORT_OFFLINE_COMPLETE ACK from"
" STMF");
SRPT_DPRINTF_L2("stp_ctl, LPORT is OFFLINE");
} else {
SRPT_DPRINTF_L2("stp_ctl, LPORT not off-lining");
}
break;
default:
SRPT_DPRINTF_L2("stp_ctl, cmd (%d) not handled",
cmd);
break;
}
}
/*
* srpt_stp_info() - STMF call-back
*/
/* ARGSUSED */
static stmf_status_t
{
SRPT_DPRINTF_L3("stp_info, invoked");
return (STMF_SUCCESS);
}
/*
* srpt_stp_event_handler() - STMF call-back
*/
/* ARGSUSED */
static void
{
SRPT_DPRINTF_L3("stp_event_handler, invoked");
}
/*
* srpt_stp_alloc_scsi_devid_desc()
*
* Allocate and initialize a SCSI device ID descriptor for
* the SRP protocol. Names are eui.GUID format.
*
* Both extension and guid are passed in host order.
*/
static scsi_devid_desc_t *
{
return (sdd);
}
/*
* srpt_stp_free_scsi_devid_desc()
*
* Free a SRPT SCSI device ID descriptor previously allocated via
* srpt_stp_alloc_scsi_devid_desc().
*/
static void
{
}
/*
* srpt_stp_alloc_session()
*/
char *local_gid, char *remote_gid)
{
SRPT_DPRINTF_L3("stp_alloc_session, invoked");
sizeof (srpt_session_t), 0);
SRPT_DPRINTF_L2("stp_alloc_session, stmf_alloc"
" returned NULL");
return (NULL);
}
/* Setup remote port transport id */
sizeof (scsi_srp_transport_id_t));
/*
* Set the alias to include the initiator extension, this will enable
* the administrator to identify multiple unique sessions originating
* from the same initiator.
*/
if (status != STMF_SUCCESS) {
SRPT_DPRINTF_L1("stp_alloc_session, STMF register session"
return (NULL);
}
return (ss);
}
/*
* srpt_stp_free_session()
*/
void
{
SRPT_DPRINTF_L3("stp_free_session, invoked");
}
/*
* srpt_stp_login() - SRP SCSI Target port login
*/
{
/* Store the string representation of connection info */
/* for Dtrace probes */
/*
* The target lock taken here serializes logins to this target
* and prevents an STMF target port from starting a control
* operation to transition the target state while a login is
* being processed.
*/
SRPT_DPRINTF_L1("stp_login, NULL I/O Controller");
goto reject_login;
}
/*
* Validate that the SRP Target ID in the login request specifies
* this I/O Controller SCSI Target Port.
*/
SRP_PORT_ID_LEN) != 0) {
SRPT_DPRINTF_L2("stp_login, SRP CM SVC target ID mismatch."
" Incoming TgtID 0x%016llx:0x%016llx",
goto reject_login;
}
SRPT_DPRINTF_L2("stp_login, SRP Login target not on-line");
goto reject_login;
}
/*
* Initiator requested IU size must be as large as the specification
* minimum and no greater than what we chose to support.
*/
if (req_it_ui_len > srpt_iu_size) {
SRPT_DPRINTF_L2("stp_login, SRP Login IU size (%d) too large",
goto reject_login;
}
if (req_it_ui_len < SRP_MIN_IU_SIZE) {
SRPT_DPRINTF_L2("stp_login, SRP Login IU size (%d) too small",
goto reject_login;
}
SRPT_DPRINTF_L2("stp_login, login req InitID 0x%016llx:0x%016llx",
SRPT_DPRINTF_L2("stp_login, login req TgtID 0x%016llx:0x%016llx",
/*
* Processing is based on either single channel or multi-channel
* operation. In single channel, all current logins for this
* same I_T_Nexus should be logged out. In multi-channel
* mode we would add an additional channel to an existing
* I_T_Nexus if one currently exists (i.e. reference the
* same SCSI session).
*/
/*
* Only a single channel may be associated with a I_T_Nexus.
* Disconnect any channel with the same SRP Initiator and
* SRP target IDs.
*/
SRPT_DPRINTF_L3("stp_login, compare session,"
SRPT_DPRINTF_L3("stp_login, compare session,"
" channel not active");
continue;
}
SRPT_DPRINTF_L3("stp_login, compare session"
" I_ID 0x%016llx:0x%016llx",
SRPT_DPRINTF_L3("stp_login, compare session"
" T_ID 0x%016llx:0x%016llx",
SRP_PORT_ID_LEN) == 0) &&
SRP_PORT_ID_LEN) == 0)) {
/*
* if a session is in the process of connecting,
* reject subsequent equivalent requests.
*/
goto reject_login;
}
SRPT_DPRINTF_L2("stp_login, terminate"
" existing login");
}
}
/* Create the new session for this SRP login */
SRPT_DPRINTF_L2("stp_login, session allocation"
" failed");
goto reject_login;
}
break;
SRPT_DPRINTF_L2("stp_login, multichannel not supported yet");
goto reject_login;
/* break via goto */
default:
SRPT_DPRINTF_L2("stp_login, invalid multichannel field (%d)",
goto reject_login;
/* break via goto */
}
/*
* Create new RDMA channel for this SRP login request.
* The channel is returned with a single reference which
* represents the reference held by the CM.
*/
SRPT_DPRINTF_L2("stp_login, unable to alloc RDMA channel");
goto reject_login;
}
/*
* Add another reference to the channel which represents
* a reference placed by the target port and add it to
* the store of channels logged in for this target port.
*/
SRPT_DPRINTF_L2("stp_login, login successful");
return (ch);
return (NULL);
}
/*
* srpt_stp_logout() - SRP logout
*
* Logout is not normally initiated in-band, but is so, just
* initiate a disconnect.
*/
void
{
}
/*
* srpt_format_login_rej() - Format login reject IU
*/
static void
{
}
/*
* srpt_format_login_rsp() - Format login response IU
*/
static void
{
/* by def. > min T_IU_LEN */
}
/*
* srpt_stp_add_task()
*/
void
{
}
/*
* srpt_stp_remove_task()
*/
void
{
}