srpt_ioc.c revision 285c05bcaff47f0043483452c70351ad34aeb880
/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* I/O Controller functions for the Solaris COMSTAR SCSI RDMA Protocol
* Target (SRPT) port provider.
*/
#include <sys/sysmacros.h>
#include "srp.h"
#include "srpt_impl.h"
#include "srpt_ioc.h"
#include "srpt_stp.h"
#include "srpt_ch.h"
/*
* srpt_ioc_srq_size - Tunable parameter that specifies the number
* of receive WQ entries that can be posted to the IOC shared
* receive queue.
*/
extern uint16_t srpt_send_msg_depth;
/* IOC profile capabilities mask must be big-endian */
typedef struct srpt_ioc_opcap_bits_s {
#if defined(_BIT_FIELDS_LTOH)
at:1,
wf:1,
wt:1,
rf:1,
rt:1,
sf:1,
st:1;
#elif defined(_BIT_FIELDS_HTOL)
sf:1,
rt:1,
rf:1,
wt:1,
wf:1,
at:1,
af:1;
#else
#endif
typedef union {
/*
* vmem arena variables - values derived from iSER
*/
/* use less memory on 32-bit kernels as it's much more constrained */
#ifdef _LP64
#else
#endif
static ibt_mr_flags_t srpt_dbuf_mr_flags =
static struct ibt_clnt_modinfo_s srpt_ibt_modinfo = {
NULL,
"srpt"
};
static int srpt_vmem_mr_compare(const void *a, const void *b);
/*
* srpt_ioc_attach() - I/O Controller attach
*
* Attach to IBTF and initialize I/O controllers. The srpt_ctxt->sc_rwlock
* should be held outside of this call.
*/
int
{
int status;
int hca_cnt;
int hca_ndx;
/*
* Attach to IBTF and initialize a list of IB devices. Each
* HCA will be represented by an I/O Controller.
*/
if (status != DDI_SUCCESS) {
SRPT_DPRINTF_L1("ioc_attach, ibt_attach failed (0x%x)",
status);
return (DDI_FAILURE);
}
if (hca_cnt < 1) {
/*
* not a fatal error. Service will be up and
* waiting for ATTACH events.
*/
SRPT_DPRINTF_L2("ioc_attach, no HCA found");
return (DDI_SUCCESS);
}
SRPT_DPRINTF_L2("ioc_attach, adding I/O"
SRPT_DPRINTF_L1("ioc_attach, ioc_init GUID(%016llx)"
continue;
}
SRPT_DPRINTF_L2("ioc_attach, I/O Controller ibt HCA hdl (%p)",
(void *)ioc->ioc_ibt_hdl);
srpt_ctxt->sc_num_iocs++;
}
SRPT_DPRINTF_L3("ioc_attach, added %d I/O Controller(s)",
return (DDI_SUCCESS);
}
/*
* srpt_ioc_detach() - I/O Controller detach
*
* srpt_ctxt->sc_rwlock should be held outside of this call.
*/
void
{
SRPT_DPRINTF_L2("ioc_detach, removing I/O Controller(%p)"
" (%016llx), ibt_hdl(%p)",
(void *)ioc,
(void *)ioc->ioc_ibt_hdl);
}
}
/*
* srpt_ioc_init() - I/O Controller initialization
*
* Requires srpt_ctxt->rw_lock be held outside of call.
*/
static srpt_ioc_t *
{
char namebuf[32];
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_init, HCA query error (%d)",
status);
return (NULL);
}
SRPT_DPRINTF_L1("ioc_init, HCA already exists");
return (NULL);
}
SRPT_DPRINTF_L2("ioc_init, HCA max mr=%d, mrlen=%lld",
if (status != IBT_SUCCESS) {
goto hca_open_err;
}
&ioc->ioc_pd_hdl);
if (status != IBT_SUCCESS) {
goto pd_alloc_err;
}
/*
* We require hardware support for SRQs. We use a common SRQ to
* reduce channel memory consumption.
*/
SRPT_DPRINTF_L0("ioc_init, no SRQ capability, not supported");
goto srq_alloc_err;
}
SRPT_DPRINTF_L3("ioc_init, Using shared receive queues, max srq work"
&ioc->ioc_srq_attr);
if (status != IBT_SUCCESS) {
goto srq_alloc_err;
}
SRPT_DPRINTF_L2("ioc_init, SRQ WR size(%d), SG size(%d)",
/*
* Allocate a pool of SRP IU message buffers and post them to
* the I/O Controller SRQ. We let the SRQ manage the free IU
* messages.
*/
SRPT_DPRINTF_L1("ioc_init, failed to allocate SRQ IUs");
goto srq_iu_alloc_err;
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_init, IU buffer pool MR err(%d)",
status);
goto srq_iu_alloc_err;
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_init, SRQ IU post err(%d)",
status);
goto srq_iu_post_err;
}
}
/*
* Initialize the dbuf vmem arena
*/
goto stmf_db_alloc_err;
}
/*
* Allocate the I/O Controller STMF data buffer allocator. The
* data store will span all targets associated with this IOC.
*/
SRPT_DPRINTF_L1("ioc_attach, STMF DBUF alloc failure for IOC");
goto stmf_db_alloc_err;
}
return (ioc);
}
ioc->ioc_iu_mr_hdl);
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_init, error deregistering"
" memory region (%d)", status);
}
}
}
}
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_init, error freeing SRQ (%d)",
status);
}
}
if (status != IBT_SUCCESS) {
}
if (status != IBT_SUCCESS) {
}
return (NULL);
}
/*
* srpt_ioc_fini() - I/O Controller Cleanup
*
* Requires srpt_ctxt->sc_rwlock be held outside of call.
*/
static void
{
int status;
int ndx;
/*
* Note driver flows will have already taken all SRP
* services running on the I/O Controller off-line.
*/
}
SRPT_DPRINTF_L4("ioc_fini, freeing SRQ");
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_fini, free SRQ"
" error (%d)", status);
}
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_fini, error deregistering"
" memory region (%d)", status);
}
}
}
SRPT_DPRINTF_L4("ioc_fini, freeing IU entries");
}
SRPT_DPRINTF_L4("ioc_fini, free IU pool struct");
ioc->ioc_num_iu_entries = 0;
}
}
ioc->ioc_pd_hdl);
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_fini, free PD"
" error (%d)", status);
}
}
if (status != IBT_SUCCESS) {
"ioc_fini, close ioc error (%d)", status);
}
}
}
/*
* srpt_ioc_port_active() - I/O Controller port active
*/
static void
{
SRPT_DPRINTF_L3("ioc_port_active event handler, invoked");
/*
* Find the HCA in question and if the HCA has completed
* initialization, and the SRP Target service for the
* the I/O Controller exists, then bind this port.
*/
SRPT_DPRINTF_L2("ioc_port_active, I/O Controller not"
" active");
return;
}
SRPT_DPRINTF_L2("ioc_port_active, no I/O Controller target"
" undefined");
return;
}
/*
* We take the target lock here to serialize this operation
* with any STMF initiated target state transitions. If
* SRP is off-line then the service handle is NULL.
*/
if ((status != IBT_SUCCESS) &&
(status != IBT_HCA_PORT_NOT_ACTIVE)) {
SRPT_DPRINTF_L1("ioc_port_active, bind failed (%d)",
status);
}
} else {
/* if we were offline because of no ports, try onlining now */
if ((tgt->tp_num_active_ports == 0) &&
}
}
if (online_target) {
if (ret == STMF_SUCCESS) {
SRPT_DPRINTF_L1("ioc_port_active, port %d active, "
} else if (ret != STMF_ALREADY) {
SRPT_DPRINTF_L1("ioc_port_active, port %d active, "
"target %016llx failed online request: %d",
(int)ret);
}
}
}
/*
* srpt_ioc_port_down()
*/
static void
{
SRPT_DPRINTF_L3("ioc_port_down event handler, invoked");
/*
* Find the HCA in question and if the HCA has completed
* initialization, and the SRP Target service for the
* the I/O Controller exists, then logout initiators
* through this port.
*/
SRPT_DPRINTF_L2("ioc_port_down, I/O Controller not"
" active");
return;
}
/*
* We only have one target now, but we could go through all
* SCSI target ports if more are added.
*/
SRPT_DPRINTF_L2("ioc_port_down, no I/O Controller target"
" undefined");
return;
}
/*
* For all channel's logged in through this port, initiate a
* disconnect.
*/
}
}
/* if we have no active ports, take the target offline */
if ((tgt->tp_num_active_ports == 0) &&
}
if (offline_target) {
if (ret == STMF_SUCCESS) {
SRPT_DPRINTF_L1("ioc_port_down, port %d down, target "
} else if (ret != STMF_ALREADY) {
SRPT_DPRINTF_L1("ioc_port_down, port %d down, target "
"%016llx failed offline request: %d",
}
}
}
/*
* srpt_ioc_ib_async_hdlr - I/O Controller IB asynchronous events
*/
/* ARGSUSED */
void
{
switch (code) {
case IBT_EVENT_PORT_UP:
break;
case IBT_ERROR_PORT_DOWN:
break;
case IBT_HCA_ATTACH_EVENT:
SRPT_DPRINTF_L1("ib_async_hdlr, HCA_ATTACH"
" event failed to initialize HCA (0x%016llx)",
return;
}
SRPT_DPRINTF_L2("HCA_ATTACH_EVENT: I/O Controller"
" ibt hdl (%p)",
(void *)ioc->ioc_ibt_hdl);
SRPT_DPRINTF_L1("ioc_ib_async_hdlr, alloc SCSI "
"target port error for HCA (0x%016llx)",
return;
}
/*
* New HCA added with default SCSI Target Port, SRP service
* will be started when SCSI Target Port is brought
* on-line by STMF.
*/
srpt_ctxt->sc_num_iocs++;
break;
case IBT_HCA_DETACH_EVENT:
"ioc_iob_async_hdlr, HCA_DETACH_EVENT received.");
break;
case IBT_EVENT_EMPTY_CHAN:
/* Channel in ERROR state is now empty */
"ioc_iob_async_hdlr, received empty channel error on %p",
(void *)ch);
break;
default:
SRPT_DPRINTF_L2("ioc_ib_async_hdlr, event not "
"handled (%d)", code);
break;
}
}
/*
* srpt_ioc_svc_bind()
*/
{
SRPT_DPRINTF_L2("ioc_svc_bind, NULL SCSI target port"
" service");
return (IBT_INVALID_PARAM);
}
return (IBT_INVALID_PARAM);
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_svc_bind, query port error (%d)",
portnum);
return (IBT_INVALID_PARAM);
}
/*
* If port is not active do nothing, caller should attempt to bind
* after the port goes active.
*/
SRPT_DPRINTF_L2("ioc_svc_bind, port %d not in active state",
portnum);
return (IBT_HCA_PORT_NOT_ACTIVE);
}
/*
* If previously bound and the port GID has changed,
* unbind the old GID.
*/
SRPT_DPRINTF_L2("ioc_svc_bind, unregister current"
" bind");
port->hwp_bind_hdl);
} else {
SRPT_DPRINTF_L2("ioc_svc_bind, port %d already bound",
portnum);
}
}
/* bind the new port GID */
SRPT_DPRINTF_L2("ioc_svc_bind, bind service, %016llx:%016llx",
/*
* Pass SCSI Target Port as CM private data, the target will
* always exist while this service is bound.
*/
SRPT_DPRINTF_L1("ioc_svc_bind, bind error (%d)",
status);
return (status);
}
}
/* port is now active */
/* setting up a transient structure for the dtrace probe. */
return (IBT_SUCCESS);
}
/*
* srpt_ioc_svc_unbind()
*/
void
{
SRPT_DPRINTF_L2("ioc_svc_unbind, SCSI target does not exist");
return;
}
return;
}
/* setting up a transient structure for the dtrace probe. */
SRPT_DPRINTF_L2("ioc_svc_unbind, unregister current bind");
port->hwp_bind_hdl);
if (ret != IBT_SUCCESS) {
"ioc_svc_unbind, unregister port %d failed: %d",
} else {
}
}
}
/*
* srpt_ioc_svc_unbind_all()
*/
void
{
SRPT_DPRINTF_L2("ioc_svc_unbind_all, NULL SCSI target port"
" specified");
return;
}
}
}
/*
* srpt_ioc_get_locked()
*
* Requires srpt_ctxt->rw_lock be held outside of call.
*/
{
break;
}
}
return (ioc);
}
/*
* srpt_ioc_get()
*/
{
return (ioc);
}
/*
* srpt_ioc_post_recv_iu()
*/
{
posted = 0;
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L2("ioc_post_recv_iu, post error (%d)",
status);
}
return (status);
}
/*
* srpt_ioc_repost_recv_iu()
*/
void
{
/*
* Some additional sanity checks while in debug state, all STMF
* related task activities should be complete prior to returning
* this IU to the available pool.
*/
iu->iu_num_rdescs = 0;
iu->iu_tot_xfer_len = 0;
iu->iu_sq_posted_cnt = 0;
if (status != IBT_SUCCESS) {
/*
* Very bad, we should initiate a shutdown of the I/O
* Controller here, off-lining any targets associated
* with this I/O Controller (and therefore disconnecting
* any logins that remain).
*
* In practice this should never happen so we put
* the code near the bottom of the implementation list.
*/
SRPT_DPRINTF_L0("ioc_repost_recv_iu, error RX IU (%d)",
status);
ASSERT(0);
}
}
/*
* srpt_ioc_init_profile()
*
* SRP I/O Controller serialization lock must be held when this
* routine is invoked.
*/
void
{
srpt_ioc_opcap_mask_t capmask = {0};
"Solaris SRP Target 0.9a", 23);
/*
* Note vendor ID and subsystem ID are 24 bit values. Low order
* 8 bits in vendor ID field is slot and is initialized to zero.
* Low order 8 bits of subsystem ID is a reserved field and
* initialized to zero.
*/
/*
* We currently only have one target, but if we had a list we would
* go through that list and only count those that are ONLINE when
* setting the services count and entries.
*/
IB_DM_MAX_SVC_NAME_LEN, "SRP.T10:%016llx",
} else {
}
}
/*
* srpt_ioc_ds_alloc_dbuf()
*/
/* ARGSUSED */
{
void *buf;
SRPT_DPRINTF_L4("ioc_ds_alloc_dbuf, invoked ioc(%p)"
" size(%d), flags(%x)",
return (NULL);
}
goto stmf_alloc_err;
}
0);
SRPT_DPRINTF_L2("ioc_ds_alloc_dbuf, stmf_alloc failed");
goto stmf_alloc_err;
}
stmf_dbuf->db_relative_offset = 0;
stmf_dbuf->db_xfer_status = 0;
return (stmf_dbuf);
return (NULL);
}
void
{
SRPT_DPRINTF_L4("ioc_ds_free_dbuf, invoked buf (%p)",
(void *)dbuf);
dbuf->db_buf_size);
}
/* Memory arena routines */
static srpt_vmem_pool_t *
{
return (result);
}
static void
{
}
}
static void *
{
void *result;
/* memory successfully allocated */
return (result);
}
/* need more vmem */
/* no more room to alloc */
return (NULL);
}
}
/*
* Note that the size of the chunk we got
* may not be the size we requested. Use the
* length returned in the chunk itself.
*/
SRPT_DPRINTF_L2("vmem_add failed");
} else {
}
}
return (result);
}
static void
{
}
static int
{
int status = DDI_FAILURE;
}
/* Verify this chunk contains the specified address range */
}
}
return (status);
}
static srpt_mr_t *
{
SRPT_DPRINTF_L2("srpt_vmem_chunk_alloc: "
"failed to alloc chunk of %d, trying %d",
chunksize /= 2;
}
}
SRPT_DPRINTF_L2("srpt_vmem_chunk_alloc: "
"chunk registration failed");
}
}
return (result);
}
static void
{
}
static srpt_mr_t *
{
SRPT_DPRINTF_L2("srpt_reg_mem: failed to allocate");
return (NULL);
}
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L2("srpt_reg_mem: ibt_register_mr "
"failed %d", status);
return (NULL);
}
return (result);
}
static void
{
if (status != IBT_SUCCESS) {
SRPT_DPRINTF_L1("ioc_fini, error deregistering MR (%d)",
status);
}
}
static int
srpt_vmem_mr_compare(const void *a, const void *b)
{
/* sort and match by virtual address */
return (-1);
return (1);
}
return (0);
}