alua_ic_if.c revision aab83bb83be7342f6cfccaed8d5fe0b2f404855d
/*
* 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
*/
/*
*/
/*
* XXX TODO
* #includes cribbed from stmf.c -- undoubtedly only a small subset of these
* are actually needed.
*/
#include <sys/byteorder.h>
#include <sys/stmf_ioctl.h>
#include <sys/pppt_ic_if.h>
#include "pppt.h"
/*
* Macros
*/
/* Free a struct if it was allocated */
#define FREE_IF_ALLOC(m) \
do { \
if ((m)) kmem_free((m), sizeof (*(m))); \
} while (0)
/*
* Macros to simplify the addition of struct fields to an nvlist.
* The name of the fields in the nvlist is the same as the name
* of the struct field.
*
* These macros require an int rc and a "done:" return retval label;
* they assume that the nvlist is named "nvl".
*/
do { \
} while (0)
/* use this macro when the array is defined as part of the struct */
do { \
} while (0)
/*
* use this macro when the array field is a ptr or you need to explictly
* call out the size.
*/
do { \
} while (0)
do { \
} while (0)
do { \
} while (0)
/*
* Macros to simplify the extraction of struct fields from an nvlist.
* The name of the fields in the nvlist is the same as the name
* of the struct field.
*
* Requires an int rc and a "done:" return retval label.
* Assumes that the nvlist is named "nvl".
*
* Sample usage: NVLIST_LOOKUP_FIELD(uint8, structname, fieldname);
*/
do { \
if (rc) { \
goto done; \
} \
} while (0)
/*
* Look up a field which gets stored into a structure bit field.
* The type passed is a uint type which can hold the largest value
* in the bit field.
*
* Requires an int rc and a "done:" return retval label.
* Assumes that the nvlist is named "nvl".
*
* Sample usage: NVLIST_LOOKUP_BIT_FIELD(uint8, structname, fieldname);
*/
do { \
if (rc) { \
goto done; \
} \
} while (0)
/*
* Look up a boolean field which gets stored into a structure bit field.
*
* Requires an int rc and a "done:" return retval label.
* Assumes that the nvlist is named "nvl".
*/
do { \
if (rc) { \
goto done; \
} \
} while (0)
/* shorthand for nvlist_lookup_pairs() args */
/* number of times to retry the upcall to transmit */
#define STMF_MSG_TRANSMIT_RETRY 3
/*
* How was the message constructed?
*
* We need to know this when we free the message in order to
* determine what to do with pointers in the message:
*
* - messages which were unmarshaled from an nvlist may point to
* memory within that nvlist; this memory should not be freed since
* it will be deallocated when we free the nvlist.
*
* - messages which built using a constructor (alloc) function may
* point to memory which was explicitly allocated by the constructor;
* it should be freed when the message is freed.
*
*/
typedef enum {
STMF_CONSTRUCTOR = 0,
/*
* Function prototypes.
*/
/*
* Helpers for msg_alloc routines, used when the msg payload is
* the same for multiple types of messages.
*/
/*
* Msg free routines.
*/
static void stmf_ic_reg_port_msg_free(stmf_ic_reg_port_msg_t *m,
static void stmf_ic_dereg_port_msg_free(stmf_ic_dereg_port_msg_t *m,
static void stmf_ic_reg_dereg_lun_msg_free(stmf_ic_reg_dereg_lun_msg_t *m,
static void stmf_ic_scsi_cmd_msg_free(stmf_ic_scsi_cmd_msg_t *m,
static void stmf_ic_scsi_data_msg_free(stmf_ic_scsi_data_msg_t *m,
static void stmf_ic_scsi_data_xfer_done_msg_free(
static void stmf_ic_scsi_status_msg_free(stmf_ic_scsi_status_msg_t *m,
static void stmf_ic_r2t_msg_free(stmf_ic_r2t_msg_t *m,
static void stmf_ic_status_msg_free(stmf_ic_status_msg_t *m,
static void stmf_ic_session_create_destroy_msg_free(
static void stmf_ic_echo_request_reply_msg_free(
/*
* Marshaling routines.
*/
/*
* Unmarshaling routines.
*/
/*
* Transmit and recieve routines.
*/
/*
* Utilities.
*/
static char *stmf_ic_strdup(char *str);
/*
* Send a message out over the interconnect, in the process marshalling
* the arguments.
*
* After being sent, the message is freed.
*/
{
int err = 0;
if (!nvl) {
goto done;
}
if (err) {
goto done;
}
if (err) {
goto done;
}
/* push the bits out on the wire */
done:
if (buf)
return (status);
}
/*
* Pass the command to the daemon for transmission to the other node.
*/
{
int i;
int rc;
/* daemon not listening */
return (STMF_IC_MSG_INTERNAL_ERROR);
}
/*
* Retry a few times if there is a shortage of threads to
* service the upcall. This shouldn't happen unless a large
* number of initiators issue commands at once.
*/
for (i = 0; i < STMF_MSG_TRANSMIT_RETRY; i++) {
break;
}
if (rc != 0) {
"stmf_ic_transmit door_ki_upcall failed %d", rc);
return (STMF_IC_MSG_INTERNAL_ERROR);
}
if (result != 0) {
/* XXX Just warn for now */
"stmf_ic_transmit bad result from daemon %d", result);
}
return (STMF_IC_MSG_SUCCESS);
}
/*
* This is a low-level upcall which is called when a message has
* been received on the interconnect.
*
* The caller is responsible for freeing the buffer which is passed in.
*/
/*ARGSUSED*/
void
{
stmf_ic_msg_t *m = NULL;
int rc = 0;
if (rc) {
return;
}
m = stmf_ic_msg_unmarshal(nvl);
if (m == NULL) {
return;
}
switch (m->icm_msg_type) {
case STMF_ICM_SCSI_CMD:
case STMF_ICM_SESSION_CREATE:
case STMF_ICM_SESSION_DESTROY:
/*
* These messages are all received by pppt.
* Currently, pppt will parse the message for type
*/
(void) pppt_msg_rx(m);
break;
case STMF_ICM_LUN_ACTIVE:
case STMF_ICM_REGISTER_LUN:
case STMF_ICM_DEREGISTER_LUN:
case STMF_ICM_SCSI_DATA:
case STMF_ICM_SCSI_STATUS:
/*
* These messages are all received by stmf.
* Currently, stmf will parse the message for type
*/
(void) stmf_msg_rx(m);
break;
case STMF_ICM_ECHO_REQUEST:
icerr->icerr_data, 0);
(void) stmf_ic_tx_msg(echo_msg);
}
stmf_ic_msg_free(m);
break;
case STMF_ICM_ECHO_REPLY:
stmf_ic_msg_free(m);
break;
case STMF_ICM_R2T:
/*
* XXX currently not supported
*/
stmf_ic_msg_free(m);
break;
case STMF_ICM_STATUS:
(void) stmf_msg_rx(m);
break;
default:
ASSERT(0);
}
}
/*
* IC message allocation routines.
*/
{
if (cb_arg_len) {
}
return (icm);
}
{
KM_SLEEP);
if (cb_arg_len) {
}
return (icm);
}
char *lu_provider_name,
{
}
char *lu_provider_name,
{
}
char *lu_provider_name,
{
}
/*
* Guts of lun register/deregister/active alloc routines.
*/
static stmf_ic_msg_t *
char *lu_provider_name,
{
if (cb_arg_len) {
}
return (icm);
}
{
}
sizeof (icsc->icsc_task_lun_no));
if (task->task_cdb_length) {
}
return (icm);
}
{
return (icm);
}
{
return (icm);
}
{
KM_SLEEP);
return (icm);
}
{
return (icm);
}
{
return (icm);
}
{
}
{
}
/*
*/
static stmf_ic_msg_t *
{
return (icm);
}
{
return (stmf_ic_echo_request_reply_msg_alloc(
}
{
return (stmf_ic_echo_request_reply_msg_alloc(
}
static stmf_ic_msg_t *
{
return (icm);
}
/*
* msg free routines.
*/
void
{
switch (msg->icm_msg_type) {
break;
break;
case STMF_ICM_LUN_ACTIVE:
case STMF_ICM_REGISTER_LUN:
case STMF_ICM_DEREGISTER_LUN:
break;
case STMF_ICM_SCSI_CMD:
break;
case STMF_ICM_SCSI_DATA:
break;
break;
case STMF_ICM_SCSI_STATUS:
break;
case STMF_ICM_R2T:
break;
case STMF_ICM_STATUS:
break;
case STMF_ICM_SESSION_CREATE:
case STMF_ICM_SESSION_DESTROY:
cmethod);
break;
case STMF_ICM_ECHO_REQUEST:
case STMF_ICM_ECHO_REPLY:
break;
case STMF_ICM_MAX_MSG_TYPE:
ASSERT(0);
break;
default:
ASSERT(0);
}
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*
* Works for both reg_lun_msg and dereg_lun_msg, since the message
* payload is the same.
*/
static void
{
if (cmethod == STMF_CONSTRUCTOR) {
}
kmem_free(m, sizeof (*m));
}
static void
{
}
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*
* Works for both session_create and session_destroy msgs, since the message
* payload is the same.
*/
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*ARGSUSED*/
static void
{
kmem_free(m, sizeof (*m));
}
/*
* Marshaling routines.
*/
static nvlist_t *
{
int rc = 0;
if (rc)
goto done;
switch (msg->icm_msg_type) {
break;
break;
case STMF_ICM_LUN_ACTIVE:
case STMF_ICM_REGISTER_LUN:
case STMF_ICM_DEREGISTER_LUN:
break;
case STMF_ICM_SCSI_CMD:
break;
case STMF_ICM_SCSI_DATA:
break;
break;
case STMF_ICM_SCSI_STATUS:
break;
case STMF_ICM_R2T:
break;
case STMF_ICM_STATUS:
break;
case STMF_ICM_SESSION_CREATE:
case STMF_ICM_SESSION_DESTROY:
break;
case STMF_ICM_ECHO_REQUEST:
case STMF_ICM_ECHO_REPLY:
break;
case STMF_ICM_MAX_MSG_TYPE:
ASSERT(0);
break;
default:
ASSERT(0);
}
done:
if (!rc)
return (nvl);
return (NULL);
}
static int
{
int rc = 0;
/* only add the callback arg if necessary */
if (m->icrp_cb_arg_len) {
}
done:
return (rc);
}
static int
{
int rc = 0;
/* only add the callback arg if necessary */
if (m->icdp_cb_arg_len) {
}
done:
return (rc);
}
/*
* Handles STMF_ICM_LUN_ACTIVE, STMF_ICM_REGISTER_LUN and
* STMF_ICM_DEREGISTER_LUN;
* msg payload is the same for all.
*/
static int
{
int rc = 0;
/* only add the callback arg if necessary */
if (m->icrl_cb_arg_len) {
}
done:
return (rc);
}
static int
{
int rc = 0;
/*
* icsc_task_cdb_length may be zero in the case of a task
* management function.
*/
/* only add immediate data if necessary */
if (m->icsc_immed_data_len) {
m->icsc_immed_data_len);
}
done:
return (rc);
}
static int
{
int rc = 0;
done:
return (rc);
}
static int
{
int rc = 0;
done:
return (rc);
}
static int
{
int rc = 0;
if (m->icss_sense_len)
done:
return (rc);
}
static int
{
int rc = 0;
done:
return (rc);
}
static int
{
int rc = 0;
done:
return (rc);
}
static int
{
int rc = 0;
done:
return (rc);
}
static int
{
int rc = 0;
if (m->icerr_datalen)
done:
return (rc);
}
/*
* Allocate a new nvlist representing the scsi_devid_desc and add it
* to the nvlist.
*/
static int
char *sdid_name,
{
int rc = 0;
if (rc)
goto done;
sdid->ident_length);
if (rc)
goto done;
done:
if (nvl) {
}
return (rc);
}
/*
* Allocate a new nvlist representing the stmf_remote_port and add it
* to the nvlist.
*/
static int
int rc = 0;
if (rc)
goto done;
if (rc)
goto done;
done:
if (nvl) {
}
return (rc);
}
/*
* Unmarshaling routines.
*/
static stmf_ic_msg_t *
{
int rc = 0;
/*
* We'd like to do this:
*
* NVLIST_LOOKUP_FIELD(uint8, m, icm_msg_type);
*
* but the fact that msg type is an enum causes type problems.
*/
if (rc) {
goto done;
}
m->icm_msg_type = msg_type;
m->icm_nvlist = nvl;
switch (m->icm_msg_type) {
break;
break;
case STMF_ICM_LUN_ACTIVE:
case STMF_ICM_REGISTER_LUN:
case STMF_ICM_DEREGISTER_LUN:
break;
case STMF_ICM_SCSI_CMD:
break;
case STMF_ICM_SCSI_DATA:
break;
break;
case STMF_ICM_SCSI_STATUS:
break;
case STMF_ICM_R2T:
break;
case STMF_ICM_STATUS:
break;
case STMF_ICM_SESSION_CREATE:
case STMF_ICM_SESSION_DESTROY:
break;
case STMF_ICM_ECHO_REQUEST:
case STMF_ICM_ECHO_REPLY:
break;
case STMF_ICM_MAX_MSG_TYPE:
ASSERT(0);
break;
default:
ASSERT(0);
}
done:
if (!m->icm_msg) {
kmem_free(m, sizeof (*m));
return (NULL);
}
return (m);
}
static void *
{
int rc = 0;
if (rc) {
goto done;
}
if (m->icrp_port_id == NULL) {
goto done;
}
if (m->icrp_cb_arg_len) {
if (m->icrp_cb_arg == NULL) {
goto done;
}
}
done:
if (!rc)
return (m);
return (NULL);
}
/*
* XXX largely the same as stmf_ic_reg_port_msg_unmarshal()
* Common stuff should be factored out. Type issues may make this
* painful.
*/
static void *
{
int rc = 0;
if (rc) {
goto done;
}
if (m->icdp_port_id == NULL) {
goto done;
}
if (m->icdp_cb_arg_len) {
if (m->icdp_cb_arg == NULL) {
goto done;
}
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
sizeof (m->icrl_lun_id), m->icrl_lun_id)) {
goto done;
}
"icrl_lu_provider_name");
if (!m->icrl_lu_provider_name) {
goto done;
}
if (m->icrl_cb_arg_len) {
if (m->icrl_cb_arg == NULL) {
goto done;
}
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
nvl, "icsc_ini_devid");
if (m->icsc_ini_devid == NULL) {
goto done;
}
nvl, "icsc_tgt_devid");
if (m->icsc_tgt_devid == NULL) {
goto done;
}
nvl, "icsc_rport");
if (m->icsc_rport == NULL) {
goto done;
}
/* icsc_lun_id */
sizeof (m->icsc_lun_id), m->icsc_lun_id)) {
goto done;
}
/* icsc_task_lun_no */
sizeof (m->icsc_task_lun_no), m->icsc_task_lun_no)) {
goto done;
}
/* icsc_task_cdb */
if (m->icsc_task_cdb_length) {
if (!m->icsc_task_cdb) {
goto done;
}
}
/* immediate data, if there is any */
if (m->icsc_immed_data_len) {
if (!m->icsc_immed_data) {
goto done;
}
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
sizeof (m->icsd_lun_id), m->icsd_lun_id)) {
goto done;
}
m->icsd_data_len, NULL);
if (!m->icsd_data) {
goto done;
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
kmem_zalloc(sizeof (*m), KM_SLEEP);
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
sizeof (m->icss_lun_id), m->icss_lun_id)) {
goto done;
}
if (m->icss_sense_len) {
m->icss_sense_len, NULL);
if (!m->icss_sense) {
goto done;
}
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
stmf_ic_session_create_destroy_msg_t *m = kmem_zalloc(sizeof (*m),
KM_SLEEP);
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
nvl, "icscd_ini_devid");
if (m->icscd_ini_devid == NULL) {
goto done;
}
nvl, "icscd_tgt_devid");
if (m->icscd_tgt_devid == NULL) {
goto done;
}
nvl, "icscd_rport");
if (m->icscd_rport == NULL) {
goto done;
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
stmf_ic_echo_request_reply_msg_t *m = kmem_zalloc(sizeof (*m),
KM_SLEEP);
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
/* immediate data, if there is any */
if (m->icerr_datalen) {
if (!m->icerr_data) {
goto done;
}
}
done:
if (!rc)
return (m);
return (NULL);
}
static void *
{
int rc = 0;
if (nvlist_lookup_pairs(nvl, 0,
NULL) != 0) {
goto done;
}
done:
if (!rc)
return (m);
kmem_free(m, sizeof (*m));
return (NULL);
}
static scsi_devid_desc_t *
{
int rc;
if (rc) {
goto done;
}
done:
return (did);
}
static scsi_devid_desc_t *
{
uint8_t ident_length = 0;
int rc = 0;
/*
* we get the ident_length first, since that's the only
* variable-sized field in the struct.
*/
if (rc)
goto done;
goto done;
}
done:
if (!rc)
return (sdid);
return (NULL);
}
static stmf_remote_port_t *
{
return (NULL);
return (stmf_ic_remote_port_unmarshal(nvl_rport));
}
static stmf_remote_port_t *
{
uint16_t rport_tptid_sz = 0;
int rc = 0;
return (NULL);
}
return (rport);
}
/*
* Unmarshal a uint8_t array.
*
* Takes a buf argument:
*
* - if non-null, the array contents are copied into the buf,
* and we return a pointer to the buffer.
*
* - if null, we return a pointer to the unmarshaled data, which
* resides in the nvlist.
*
* Returns NULL on failure.
*/
static uint8_t *
char *field_name,
{
int rc = 0;
if (rc) {
return (NULL);
}
if (len != actual_len) {
"stmf_ic_uint8_array_unmarshal: wrong len (%d != %d)",
(int)len, actual_len);
return (NULL);
}
if (buf) {
/* preallocated buf, copy in */
} else {
/* return a pointer to the underlying array in the nvlist */
}
return (buf);
}
/*
* Unmarshal a string.
*
* Returns NULL on failure.
*/
static char *
char *field_name)
{
char *s = NULL;
int rc = 0;
if (rc) {
return (NULL);
}
return (s);
}
/*
* Utility routines.
*/
static stmf_ic_msg_t *
{
return (icm);
}
static size_t
{
int num_ident_elems;
ASSERT(ident_length > 0);
/*
* Need to account for the fact that there's
* already a single element in scsi_devid_desc_t.
*
* XXX would really like to have a way to determine the
* sizeof (struct scsi_devid_desc.ident[0]), but
* it's not clear that can be done.
* Thus, this code relies on the knowledge of the type of
* that field.
*/
size = sizeof (scsi_devid_desc_t) +
(num_ident_elems * sizeof (uint8_t));
return (size);
}
/*
* Duplicate the scsi_devid_desc_t.
*/
static scsi_devid_desc_t *
{
return (dup);
}
/*
* May be called with a null pointer.
*/
static void
{
if (!did)
return;
}
/*
* Duplicate the stmf_remote_port_t.
*/
static stmf_remote_port_t *
{
if (rport) {
}
return (dup);
}
/*
* Helper functions, returns NULL if no memory.
*/
static char *
stmf_ic_strdup(char *str)
{
char *copy;
return (copy);
}
static inline void
{
}