#include <sys/note.h>
#include <sys/types.h>
#include <sys/varargs.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/uio.h>
#include <sys/buf.h>
#include <sys/modctl.h>
#include <sys/open.h>
#include <sys/kmem.h>
#include <sys/poll.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/promif.h>
#include <sys/byteorder.h>
#include <sys/fibre-channel/fc.h>
#include <sys/fibre-channel/impl/fc_ulpif.h>
#include <sys/fibre-channel/impl/fc_fcaif.h>
#include <sys/fibre-channel/impl/fctl_private.h>
#include <sys/fibre-channel/impl/fc_portif.h>
int did_table_size = D_ID_HASH_TABLE_SIZE;
int pwwn_table_size = PWWN_HASH_TABLE_SIZE;
static fc_ulp_module_t *fctl_ulp_modules;
static fc_fca_port_t *fctl_fca_portlist;
static fc_ulp_list_t *fctl_ulp_list;
static char fctl_greeting[] =
"fctl: %s ULP same type (0x%x) as existing module.\n";
static char *fctl_undefined = "Undefined";
static krwlock_t fctl_ulp_lock;
static krwlock_t fctl_mod_ports_lock;
static kmutex_t fctl_port_lock;
static kmutex_t fctl_ulp_list_mutex;
static fctl_nwwn_list_t *fctl_nwwn_hash_table;
static kmutex_t fctl_nwwn_hash_mutex;
int fctl_nwwn_table_size = NWWN_HASH_TABLE_SIZE;
#if !defined(lint)
_NOTE(MUTEX_PROTECTS_DATA(fctl_nwwn_hash_mutex, fctl_nwwn_hash_table))
_NOTE(MUTEX_PROTECTS_DATA(fctl_ulp_list_mutex, fctl_ulp_list))
_NOTE(RWLOCK_PROTECTS_DATA(fctl_ulp_lock, ulp_module::mod_next))
_NOTE(RWLOCK_PROTECTS_DATA(fctl_mod_ports_lock, ulp_module::mod_ports
ulp_ports::port_handle))
_NOTE(DATA_READABLE_WITHOUT_LOCK(ulp_module::mod_info))
_NOTE(MUTEX_PROTECTS_DATA(ulp_ports::port_mutex, ulp_ports::port_statec
ulp_ports::port_dstate))
#endif
#define FCTL_VERSION "20090729-1.70"
#define FCTL_NAME_VERSION "SunFC Transport v" FCTL_VERSION
char *fctl_version = FCTL_NAME_VERSION;
extern struct mod_ops mod_miscops;
static struct modlmisc modlmisc = {
&mod_miscops,
FCTL_NAME_VERSION
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modlmisc, NULL
};
static struct bus_ops fctl_fca_busops = {
BUSO_REV,
nullbusmap,
NULL,
NULL,
NULL,
i_ddi_map_fault,
NULL,
ddi_dma_allochdl,
ddi_dma_freehdl,
ddi_dma_bindhdl,
ddi_dma_unbindhdl,
ddi_dma_flush,
ddi_dma_win,
ddi_dma_mctl,
fctl_fca_bus_ctl,
ddi_bus_prop_op,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
struct kmem_cache *fctl_job_cache;
static fc_errmap_t fc_errlist [] = {
{ FC_FAILURE, "Operation failed" },
{ FC_SUCCESS, "Operation success" },
{ FC_CAP_ERROR, "Capability error" },
{ FC_CAP_FOUND, "Capability found" },
{ FC_CAP_SETTABLE, "Capability settable" },
{ FC_UNBOUND, "Port not bound" },
{ FC_NOMEM, "No memory" },
{ FC_BADPACKET, "Bad packet" },
{ FC_OFFLINE, "Port offline" },
{ FC_OLDPORT, "Old Port" },
{ FC_NO_MAP, "No map available" },
{ FC_TRANSPORT_ERROR, "Transport error" },
{ FC_ELS_FREJECT, "ELS Frejected" },
{ FC_ELS_PREJECT, "ELS PRejected" },
{ FC_ELS_BAD, "Bad ELS request" },
{ FC_ELS_MALFORMED, "Malformed ELS request" },
{ FC_TOOMANY, "Too many commands" },
{ FC_UB_BADTOKEN, "Bad Unsolicited buffer token" },
{ FC_UB_ERROR, "Unsolicited buffer error" },
{ FC_UB_BUSY, "Unsolicited buffer busy" },
{ FC_BADULP, "Bad ULP" },
{ FC_BADTYPE, "Bad Type" },
{ FC_UNCLAIMED, "Not Claimed" },
{ FC_ULP_SAMEMODULE, "Same ULP Module" },
{ FC_ULP_SAMETYPE, "Same ULP Type" },
{ FC_ABORTED, "Command Aborted" },
{ FC_ABORT_FAILED, "Abort Failed" },
{ FC_BADEXCHANGE, "Bad Exchange" },
{ FC_BADWWN, "Bad World Wide Name" },
{ FC_BADDEV, "Bad Device" },
{ FC_BADCMD, "Bad Command" },
{ FC_BADOBJECT, "Bad Object" },
{ FC_BADPORT, "Bad Port" },
{ FC_NOTTHISPORT, "Not on this Port" },
{ FC_PREJECT, "Operation Prejected" },
{ FC_FREJECT, "Operation Frejected" },
{ FC_PBUSY, "Operation Pbusyed" },
{ FC_FBUSY, "Operation Fbusyed" },
{ FC_ALREADY, "Already done" },
{ FC_LOGINREQ, "PLOGI Required" },
{ FC_RESETFAIL, "Reset operation failed" },
{ FC_INVALID_REQUEST, "Invalid Request" },
{ FC_OUTOFBOUNDS, "Out of Bounds" },
{ FC_TRAN_BUSY, "Command transport Busy" },
{ FC_STATEC_BUSY, "State change Busy" },
{ FC_DEVICE_BUSY, "Port driver is working on this device" }
};
fc_pkt_reason_t remote_stop_reasons [] = {
{ FC_REASON_ABTS, "Abort Sequence" },
{ FC_REASON_ABTX, "Abort Exchange" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_reason_t general_reasons [] = {
{ FC_REASON_HW_ERROR, "Hardware Error" },
{ FC_REASON_SEQ_TIMEOUT, "Sequence Timeout" },
{ FC_REASON_ABORTED, "Aborted" },
{ FC_REASON_ABORT_FAILED, "Abort Failed" },
{ FC_REASON_NO_CONNECTION, "No Connection" },
{ FC_REASON_XCHG_DROPPED, "Exchange Dropped" },
{ FC_REASON_ILLEGAL_FRAME, "Illegal Frame" },
{ FC_REASON_ILLEGAL_LENGTH, "Illegal Length" },
{ FC_REASON_UNSUPPORTED, "Unsuported" },
{ FC_REASON_RX_BUF_TIMEOUT, "Receive Buffer Timeout" },
{ FC_REASON_FCAL_OPN_FAIL, "FC AL Open Failed" },
{ FC_REASON_OVERRUN, "Over run" },
{ FC_REASON_QFULL, "Queue Full" },
{ FC_REASON_ILLEGAL_REQ, "Illegal Request", },
{ FC_REASON_PKT_BUSY, "Busy" },
{ FC_REASON_OFFLINE, "Offline" },
{ FC_REASON_BAD_XID, "Bad Exchange Id" },
{ FC_REASON_XCHG_BSY, "Exchange Busy" },
{ FC_REASON_NOMEM, "No Memory" },
{ FC_REASON_BAD_SID, "Bad S_ID" },
{ FC_REASON_NO_SEQ_INIT, "No Sequence Initiative" },
{ FC_REASON_DIAG_BUSY, "Diagnostic Busy" },
{ FC_REASON_DMA_ERROR, "DMA Error" },
{ FC_REASON_CRC_ERROR, "CRC Error" },
{ FC_REASON_ABORT_TIMEOUT, "Abort Timeout" },
{ FC_REASON_FCA_UNIQUE, "FCA Unique" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_reason_t rjt_reasons [] = {
{ FC_REASON_INVALID_D_ID, "Invalid D_ID" },
{ FC_REASON_INVALID_S_ID, "Invalid S_ID" },
{ FC_REASON_TEMP_UNAVAILABLE, "Temporarily Unavailable" },
{ FC_REASON_PERM_UNAVAILABLE, "Permamnently Unavailable" },
{ FC_REASON_CLASS_NOT_SUPP, "Class Not Supported", },
{ FC_REASON_DELIMTER_USAGE_ERROR,
"Delimeter Usage Error" },
{ FC_REASON_TYPE_NOT_SUPP, "Type Not Supported" },
{ FC_REASON_INVALID_LINK_CTRL, "Invalid Link Control" },
{ FC_REASON_INVALID_R_CTL, "Invalid R_CTL" },
{ FC_REASON_INVALID_F_CTL, "Invalid F_CTL" },
{ FC_REASON_INVALID_OX_ID, "Invalid OX_ID" },
{ FC_REASON_INVALID_RX_ID, "Invalid RX_ID" },
{ FC_REASON_INVALID_SEQ_ID, "Invalid Sequence ID" },
{ FC_REASON_INVALID_DF_CTL, "Invalid DF_CTL" },
{ FC_REASON_INVALID_SEQ_CNT, "Invalid Sequence count" },
{ FC_REASON_INVALID_PARAM, "Invalid Parameter" },
{ FC_REASON_EXCH_ERROR, "Exchange Error" },
{ FC_REASON_PROTOCOL_ERROR, "Protocol Error" },
{ FC_REASON_INCORRECT_LENGTH, "Incorrect Length" },
{ FC_REASON_UNEXPECTED_ACK, "Unexpected Ack" },
{ FC_REASON_UNEXPECTED_LR, "Unexpected Link reset" },
{ FC_REASON_LOGIN_REQUIRED, "Login Required" },
{ FC_REASON_EXCESSIVE_SEQS, "Excessive Sequences"
" Attempted" },
{ FC_REASON_EXCH_UNABLE, "Exchange incapable" },
{ FC_REASON_ESH_NOT_SUPP, "Expiration Security Header "
"Not Supported" },
{ FC_REASON_NO_FABRIC_PATH, "No Fabric Path" },
{ FC_REASON_VENDOR_UNIQUE, "Vendor Unique" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_reason_t n_port_busy_reasons [] = {
{ FC_REASON_PHYSICAL_BUSY, "Physical Busy" },
{ FC_REASON_N_PORT_RESOURCE_BSY, "Resource Busy" },
{ FC_REASON_N_PORT_VENDOR_UNIQUE, "Vendor Unique" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_reason_t f_busy_reasons [] = {
{ FC_REASON_FABRIC_BSY, "Fabric Busy" },
{ FC_REASON_N_PORT_BSY, "N_Port Busy" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_reason_t ls_ba_rjt_reasons [] = {
{ FC_REASON_INVALID_LA_CODE, "Invalid Link Application Code" },
{ FC_REASON_LOGICAL_ERROR, "Logical Error" },
{ FC_REASON_LOGICAL_BSY, "Logical Busy" },
{ FC_REASON_PROTOCOL_ERROR_RJT, "Protocol Error Reject" },
{ FC_REASON_CMD_UNABLE, "Unable to Perform Command" },
{ FC_REASON_CMD_UNSUPPORTED, "Unsupported Command" },
{ FC_REASON_VU_RJT, "Vendor Unique" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_reason_t fs_rjt_reasons [] = {
{ FC_REASON_FS_INVALID_CMD, "Invalid Command" },
{ FC_REASON_FS_INVALID_VER, "Invalid Version" },
{ FC_REASON_FS_LOGICAL_ERR, "Logical Error" },
{ FC_REASON_FS_INVALID_IUSIZE, "Invalid IU Size" },
{ FC_REASON_FS_LOGICAL_BUSY, "Logical Busy" },
{ FC_REASON_FS_PROTOCOL_ERR, "Protocol Error" },
{ FC_REASON_FS_CMD_UNABLE, "Unable to Perform Command" },
{ FC_REASON_FS_CMD_UNSUPPORTED, "Unsupported Command" },
{ FC_REASON_FS_VENDOR_UNIQUE, "Vendor Unique" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_action_t n_port_busy_actions [] = {
{ FC_ACTION_SEQ_TERM_RETRY, "Retry terminated Sequence" },
{ FC_ACTION_SEQ_ACTIVE_RETRY, "Retry Active Sequence" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_action_t rjt_timeout_actions [] = {
{ FC_ACTION_RETRYABLE, "Retryable" },
{ FC_ACTION_NON_RETRYABLE, "Non Retryable" },
{ FC_REASON_INVALID, NULL }
};
fc_pkt_expln_t ba_rjt_explns [] = {
{ FC_EXPLN_NONE, "No Explanation" },
{ FC_EXPLN_INVALID_OX_RX_ID, "Invalid X_ID" },
{ FC_EXPLN_SEQ_ABORTED, "Sequence Aborted" },
{ FC_EXPLN_INVALID, NULL }
};
fc_pkt_error_t fc_pkt_errlist[] = {
{
FC_PKT_SUCCESS,
"Operation Success",
NULL,
NULL,
NULL
},
{ FC_PKT_REMOTE_STOP,
"Remote Stop",
remote_stop_reasons,
NULL,
NULL
},
{
FC_PKT_LOCAL_RJT,
"Local Reject",
general_reasons,
rjt_timeout_actions,
NULL
},
{
FC_PKT_NPORT_RJT,
"N_Port Reject",
rjt_reasons,
rjt_timeout_actions,
NULL
},
{
FC_PKT_FABRIC_RJT,
"Fabric Reject",
rjt_reasons,
rjt_timeout_actions,
NULL
},
{
FC_PKT_LOCAL_BSY,
"Local Busy",
general_reasons,
NULL,
NULL,
},
{
FC_PKT_TRAN_BSY,
"Transport Busy",
general_reasons,
NULL,
NULL,
},
{
FC_PKT_NPORT_BSY,
"N_Port Busy",
n_port_busy_reasons,
n_port_busy_actions,
NULL
},
{
FC_PKT_FABRIC_BSY,
"Fabric Busy",
f_busy_reasons,
NULL,
NULL,
},
{
FC_PKT_LS_RJT,
"Link Service Reject",
ls_ba_rjt_reasons,
NULL,
NULL,
},
{
FC_PKT_BA_RJT,
"Basic Reject",
ls_ba_rjt_reasons,
NULL,
ba_rjt_explns,
},
{
FC_PKT_TIMEOUT,
"Timeout",
general_reasons,
rjt_timeout_actions,
NULL
},
{
FC_PKT_FS_RJT,
"Fabric Switch Reject",
fs_rjt_reasons,
NULL,
NULL
},
{
FC_PKT_TRAN_ERROR,
"Packet Transport error",
general_reasons,
NULL,
NULL
},
{
FC_PKT_FAILURE,
"Packet Failure",
general_reasons,
NULL,
NULL
},
{
FC_PKT_PORT_OFFLINE,
"Port Offline",
NULL,
NULL,
NULL
},
{
FC_PKT_ELS_IN_PROGRESS,
"ELS is in Progress",
NULL,
NULL,
NULL
}
};
int
_init()
{
int rval;
rw_init(&fctl_ulp_lock, NULL, RW_DRIVER, NULL);
rw_init(&fctl_mod_ports_lock, NULL, RW_DRIVER, NULL);
mutex_init(&fctl_port_lock, NULL, MUTEX_DRIVER, NULL);
mutex_init(&fctl_nwwn_hash_mutex, NULL, MUTEX_DRIVER, NULL);
fctl_nwwn_hash_table = kmem_zalloc(sizeof (*fctl_nwwn_hash_table) *
fctl_nwwn_table_size, KM_SLEEP);
fctl_ulp_modules = NULL;
fctl_fca_portlist = NULL;
fctl_job_cache = kmem_cache_create("fctl_cache",
sizeof (job_request_t), 8, fctl_cache_constructor,
fctl_cache_destructor, NULL, NULL, NULL, 0);
if (fctl_job_cache == NULL) {
kmem_free(fctl_nwwn_hash_table,
sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
mutex_destroy(&fctl_nwwn_hash_mutex);
mutex_destroy(&fctl_port_lock);
rw_destroy(&fctl_ulp_lock);
rw_destroy(&fctl_mod_ports_lock);
return (ENOMEM);
}
if ((rval = mod_install(&modlinkage)) != 0) {
kmem_cache_destroy(fctl_job_cache);
kmem_free(fctl_nwwn_hash_table,
sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
mutex_destroy(&fctl_nwwn_hash_mutex);
mutex_destroy(&fctl_port_lock);
rw_destroy(&fctl_ulp_lock);
rw_destroy(&fctl_mod_ports_lock);
}
return (rval);
}
int
_fini()
{
int rval;
if ((rval = mod_remove(&modlinkage)) != 0) {
return (rval);
}
kmem_cache_destroy(fctl_job_cache);
kmem_free(fctl_nwwn_hash_table,
sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
mutex_destroy(&fctl_nwwn_hash_mutex);
mutex_destroy(&fctl_port_lock);
rw_destroy(&fctl_ulp_lock);
rw_destroy(&fctl_mod_ports_lock);
return (rval);
}
int
_info(struct modinfo *modinfo_p)
{
return (mod_info(&modlinkage, modinfo_p));
}
static int
fctl_cache_constructor(void *buf, void *cdarg, int kmflag)
{
job_request_t *job = (job_request_t *)buf;
mutex_init(&job->job_mutex, NULL, MUTEX_DRIVER, NULL);
sema_init(&job->job_fctl_sema, 0, NULL, SEMA_DEFAULT, NULL);
sema_init(&job->job_port_sema, 0, NULL, SEMA_DEFAULT, NULL);
return (0);
}
static void
fctl_cache_destructor(void *buf, void *cdarg)
{
job_request_t *job = (job_request_t *)buf;
sema_destroy(&job->job_fctl_sema);
sema_destroy(&job->job_port_sema);
mutex_destroy(&job->job_mutex);
}
int
fc_ulp_add(fc_ulp_modinfo_t *ulp_info)
{
fc_ulp_module_t *mod;
fc_ulp_module_t *prev;
job_request_t *job;
fc_ulp_list_t *new;
fc_fca_port_t *fca_port;
int ntry = 0;
ASSERT(ulp_info != NULL);
if (ulp_info->ulp_rev != FCTL_ULP_MODREV_4) {
cmn_err(CE_WARN, "fctl: ULP %s version mismatch;"
" ULP %s would not be loaded", ulp_info->ulp_name,
ulp_info->ulp_name);
return (FC_BADULP);
}
new = kmem_zalloc(sizeof (*new), KM_SLEEP);
ASSERT(new != NULL);
mutex_enter(&fctl_ulp_list_mutex);
new->ulp_info = ulp_info;
if (fctl_ulp_list != NULL) {
new->ulp_next = fctl_ulp_list;
}
fctl_ulp_list = new;
mutex_exit(&fctl_ulp_list_mutex);
while (rw_tryenter(&fctl_ulp_lock, RW_WRITER) == 0) {
delay(drv_usectohz(1000000));
if (ntry++ > FC_ULP_ADD_RETRY_COUNT) {
fc_ulp_list_t *list;
fc_ulp_list_t *last;
mutex_enter(&fctl_ulp_list_mutex);
for (last = NULL, list = fctl_ulp_list; list != NULL;
list = list->ulp_next) {
if (list->ulp_info == ulp_info) {
break;
}
last = list;
}
if (list) {
if (last) {
last->ulp_next = list->ulp_next;
} else {
fctl_ulp_list = list->ulp_next;
}
kmem_free(list, sizeof (*list));
}
mutex_exit(&fctl_ulp_list_mutex);
cmn_err(CE_WARN, "fctl: ULP %s unable to load",
ulp_info->ulp_name);
return (FC_FAILURE);
}
}
for (mod = fctl_ulp_modules, prev = NULL; mod; mod = mod->mod_next) {
ASSERT(mod->mod_info != NULL);
if (ulp_info == mod->mod_info &&
ulp_info->ulp_type == mod->mod_info->ulp_type) {
rw_exit(&fctl_ulp_lock);
return (FC_ULP_SAMEMODULE);
}
if (ulp_info->ulp_type == mod->mod_info->ulp_type) {
cmn_err(CE_NOTE, fctl_greeting, ulp_info->ulp_name,
ulp_info->ulp_type);
}
prev = mod;
}
mod = kmem_zalloc(sizeof (*mod), KM_SLEEP);
mod->mod_info = ulp_info;
mod->mod_next = NULL;
if (prev) {
prev->mod_next = mod;
} else {
fctl_ulp_modules = mod;
}
mutex_enter(&fctl_port_lock);
for (fca_port = fctl_fca_portlist; fca_port != NULL;
fca_port = fca_port->port_next) {
job = fctl_alloc_job(JOB_ATTACH_ULP, JOB_TYPE_FCTL_ASYNC,
NULL, NULL, KM_SLEEP);
fctl_enque_job(fca_port->port_handle, job);
}
mutex_exit(&fctl_port_lock);
rw_exit(&fctl_ulp_lock);
return (FC_SUCCESS);
}
int
fc_ulp_remove(fc_ulp_modinfo_t *ulp_info)
{
fc_ulp_module_t *mod;
fc_ulp_list_t *list;
fc_ulp_list_t *last;
fc_ulp_module_t *prev;
mutex_enter(&fctl_ulp_list_mutex);
for (last = NULL, list = fctl_ulp_list; list != NULL;
list = list->ulp_next) {
if (list->ulp_info == ulp_info) {
break;
}
last = list;
}
if (list) {
if (last) {
last->ulp_next = list->ulp_next;
} else {
fctl_ulp_list = list->ulp_next;
}
kmem_free(list, sizeof (*list));
}
mutex_exit(&fctl_ulp_list_mutex);
rw_enter(&fctl_ulp_lock, RW_WRITER);
for (mod = fctl_ulp_modules, prev = NULL; mod != NULL;
mod = mod->mod_next) {
if (mod->mod_info == ulp_info) {
break;
}
prev = mod;
}
if (mod) {
fc_ulp_ports_t *next;
if (prev) {
prev->mod_next = mod->mod_next;
} else {
fctl_ulp_modules = mod->mod_next;
}
rw_enter(&fctl_mod_ports_lock, RW_WRITER);
while ((next = mod->mod_ports) != NULL) {
mod->mod_ports = next->port_next;
fctl_dealloc_ulp_port(next);
}
rw_exit(&fctl_mod_ports_lock);
rw_exit(&fctl_ulp_lock);
kmem_free(mod, sizeof (*mod));
return (FC_SUCCESS);
}
rw_exit(&fctl_ulp_lock);
return (FC_FAILURE);
}
int
fc_ulp_init_packet(opaque_t port_handle, fc_packet_t *pkt, int sleep)
{
int rval;
fc_local_port_t *port = port_handle;
fc_remote_port_t *pd;
ASSERT(pkt != NULL);
pd = pkt->pkt_pd;
rval = port->fp_fca_tran->fca_init_pkt(port->fp_fca_handle, pkt, sleep);
if ((rval == FC_SUCCESS) && (pd != NULL)) {
mutex_enter(&pd->pd_mutex);
ASSERT(pd->pd_ref_count >= 0);
pd->pd_ref_count++;
mutex_exit(&pd->pd_mutex);
}
return (rval);
}
int
fc_ulp_uninit_packet(opaque_t port_handle, fc_packet_t *pkt)
{
int rval;
fc_local_port_t *port = port_handle;
fc_remote_port_t *pd;
ASSERT(pkt != NULL);
pd = pkt->pkt_pd;
rval = port->fp_fca_tran->fca_un_init_pkt(port->fp_fca_handle, pkt);
if ((rval == FC_SUCCESS) && (pd != NULL)) {
mutex_enter(&pd->pd_mutex);
ASSERT(pd->pd_ref_count > 0);
pd->pd_ref_count--;
if (pd->pd_state == PORT_DEVICE_INVALID &&
pd->pd_ref_count == 0) {
fc_remote_node_t *node = pd->pd_remote_nodep;
mutex_exit(&pd->pd_mutex);
if ((fctl_destroy_remote_port(port, pd) == 0) &&
(node != NULL)) {
fctl_destroy_remote_node(node);
}
return (rval);
}
mutex_exit(&pd->pd_mutex);
}
return (rval);
}
int
fc_ulp_getportmap(opaque_t port_handle, fc_portmap_t **map, uint32_t *len,
int flag)
{
int job_code;
fc_local_port_t *port;
job_request_t *job;
fc_portmap_t *tmp_map;
uint32_t tmp_len;
fc_portmap_t *change_list = NULL;
uint32_t listlen = 0;
port = port_handle;
mutex_enter(&port->fp_mutex);
if (port->fp_statec_busy) {
mutex_exit(&port->fp_mutex);
return (FC_STATEC_BUSY);
}
if (FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) {
mutex_exit(&port->fp_mutex);
return (FC_OFFLINE);
}
if (port->fp_dev_count && (port->fp_dev_count ==
port->fp_total_devices)) {
mutex_exit(&port->fp_mutex);
fctl_fillout_map(port, &change_list, &listlen, 1, 1, 0);
if (listlen > *len) {
tmp_map = (fc_portmap_t *)kmem_zalloc(
listlen * sizeof (fc_portmap_t), KM_NOSLEEP);
if (tmp_map == NULL) {
return (FC_NOMEM);
}
if (*map) {
kmem_free(*map, (*len) * sizeof (fc_portmap_t));
}
*map = tmp_map;
}
if (change_list) {
bcopy(change_list, *map,
listlen * sizeof (fc_portmap_t));
kmem_free(change_list, listlen * sizeof (fc_portmap_t));
}
*len = listlen;
} else {
mutex_exit(&port->fp_mutex);
switch (flag) {
case FC_ULP_PLOGI_DONTCARE:
job_code = JOB_PORT_GETMAP;
break;
case FC_ULP_PLOGI_PRESERVE:
job_code = JOB_PORT_GETMAP_PLOGI_ALL;
break;
default:
return (FC_INVALID_REQUEST);
}
job = fctl_alloc_job(job_code, 0, NULL, NULL, KM_SLEEP);
job->job_private = (opaque_t)map;
job->job_arg = (opaque_t)len;
fctl_enque_job(port, job);
fctl_jobwait(job);
fctl_dealloc_job(job);
}
tmp_len = *len;
tmp_map = *map;
while (tmp_len-- != 0) {
if (tmp_map->map_state != PORT_DEVICE_INVALID) {
fc_remote_port_t *pd =
(fc_remote_port_t *)tmp_map->map_pd;
mutex_enter(&pd->pd_mutex);
pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
mutex_exit(&pd->pd_mutex);
}
tmp_map++;
}
return (FC_SUCCESS);
}
int
fc_ulp_login(opaque_t port_handle, fc_packet_t **ulp_pkt, uint32_t listlen)
{
int rval = FC_SUCCESS;
int job_flags;
uint32_t count;
fc_packet_t **tmp_array;
job_request_t *job;
fc_local_port_t *port = port_handle;
fc_ulp_rscn_info_t *rscnp =
(fc_ulp_rscn_info_t *)(ulp_pkt[0])->pkt_ulp_rscn_infop;
mutex_enter(&port->fp_mutex);
if (port->fp_statec_busy) {
mutex_exit(&port->fp_mutex);
return (FC_STATEC_BUSY);
}
if ((FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ||
(port->fp_soft_state &
(FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
mutex_exit(&port->fp_mutex);
return (FC_OFFLINE);
}
if ((rscnp != NULL) &&
(rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
(rscnp->ulp_rscn_count != port->fp_rscn_count)) {
mutex_exit(&port->fp_mutex);
return (FC_DEVICE_BUSY_NEW_RSCN);
}
mutex_exit(&port->fp_mutex);
tmp_array = kmem_zalloc(sizeof (*tmp_array) * listlen, KM_SLEEP);
for (count = 0; count < listlen; count++) {
tmp_array[count] = ulp_pkt[count];
}
job_flags = ((ulp_pkt[0]->pkt_tran_flags) & FC_TRAN_NO_INTR)
? 0 : JOB_TYPE_FCTL_ASYNC;
#ifdef DEBUG
{
int next;
int count;
int polled;
polled = ((ulp_pkt[0]->pkt_tran_flags) &
FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
for (count = 0; count < listlen; count++) {
next = ((ulp_pkt[count]->pkt_tran_flags)
& FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
ASSERT(next == polled);
}
}
#endif
job = fctl_alloc_job(JOB_PLOGI_GROUP, job_flags, NULL, NULL, KM_SLEEP);
job->job_ulp_pkts = tmp_array;
job->job_ulp_listlen = listlen;
while (listlen--) {
fc_packet_t *pkt;
pkt = tmp_array[listlen];
if (pkt->pkt_pd == NULL) {
pkt->pkt_state = FC_PKT_SUCCESS;
continue;
}
mutex_enter(&pkt->pkt_pd->pd_mutex);
if (pkt->pkt_pd->pd_flags == PD_ELS_IN_PROGRESS ||
pkt->pkt_pd->pd_flags == PD_ELS_MARK) {
mutex_exit(&pkt->pkt_pd->pd_mutex);
pkt->pkt_state = FC_PKT_ELS_IN_PROGRESS;
continue;
}
if (pkt->pkt_pd->pd_state == PORT_DEVICE_INVALID ||
pkt->pkt_pd->pd_type == PORT_DEVICE_OLD) {
mutex_exit(&pkt->pkt_pd->pd_mutex);
pkt->pkt_state = FC_PKT_LOCAL_RJT;
continue;
}
mutex_exit(&pkt->pkt_pd->pd_mutex);
pkt->pkt_state = FC_PKT_SUCCESS;
}
fctl_enque_job(port, job);
if (!(job_flags & JOB_TYPE_FCTL_ASYNC)) {
fctl_jobwait(job);
rval = job->job_result;
fctl_dealloc_job(job);
}
return (rval);
}
opaque_t
fc_ulp_get_remote_port(opaque_t port_handle, la_wwn_t *pwwn, int *error,
int create)
{
fc_local_port_t *port;
job_request_t *job;
fc_remote_port_t *pd;
port = port_handle;
pd = fctl_get_remote_port_by_pwwn(port, pwwn);
if (pd != NULL) {
*error = FC_SUCCESS;
mutex_enter(&pd->pd_mutex);
pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
mutex_exit(&pd->pd_mutex);
return (pd);
}
mutex_enter(&port->fp_mutex);
if (FC_IS_TOP_SWITCH(port->fp_topology) && create) {
uint32_t d_id;
fctl_ns_req_t *ns_cmd;
mutex_exit(&port->fp_mutex);
job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
if (job == NULL) {
*error = FC_NOMEM;
return (pd);
}
ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gid_pn_t),
sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
0, KM_SLEEP);
if (ns_cmd == NULL) {
fctl_dealloc_job(job);
*error = FC_NOMEM;
return (pd);
}
ns_cmd->ns_cmd_code = NS_GID_PN;
((ns_req_gid_pn_t *)(ns_cmd->ns_cmd_buf))->pwwn = *pwwn;
job->job_result = FC_SUCCESS;
job->job_private = (void *)ns_cmd;
job->job_counter = 1;
fctl_enque_job(port, job);
fctl_jobwait(job);
if (job->job_result != FC_SUCCESS) {
*error = job->job_result;
fctl_free_ns_cmd(ns_cmd);
fctl_dealloc_job(job);
return (pd);
}
d_id = ((ns_resp_gid_pn_t *)ns_cmd->ns_data_buf)->pid.port_id;
fctl_free_ns_cmd(ns_cmd);
ns_cmd = fctl_alloc_ns_cmd(sizeof (ns_req_gan_t),
sizeof (ns_resp_gan_t), 0, FCTL_NS_CREATE_DEVICE,
KM_SLEEP);
ASSERT(ns_cmd != NULL);
ns_cmd->ns_gan_max = 1;
ns_cmd->ns_cmd_code = NS_GA_NXT;
ns_cmd->ns_gan_sid = FCTL_GAN_START_ID;
((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.port_id = d_id - 1;
((ns_req_gan_t *)(ns_cmd->ns_cmd_buf))->pid.priv_lilp_posit = 0;
job->job_result = FC_SUCCESS;
job->job_private = (void *)ns_cmd;
job->job_counter = 1;
fctl_enque_job(port, job);
fctl_jobwait(job);
fctl_free_ns_cmd(ns_cmd);
if (job->job_result != FC_SUCCESS) {
*error = job->job_result;
fctl_dealloc_job(job);
return (pd);
}
fctl_dealloc_job(job);
pd = fctl_get_remote_port_by_pwwn(port, pwwn);
if (pd == NULL) {
*error = FC_FAILURE;
} else {
*error = FC_SUCCESS;
mutex_enter(&pd->pd_mutex);
pd->pd_aux_flags |= PD_GIVEN_TO_ULPS;
mutex_exit(&pd->pd_mutex);
}
} else {
mutex_exit(&port->fp_mutex);
*error = FC_FAILURE;
}
return (pd);
}
int
fc_ulp_port_ns(opaque_t port_handle, opaque_t pd, fc_ns_cmd_t *ns_req)
{
int rval;
int fabric;
job_request_t *job;
fctl_ns_req_t *ns_cmd;
fc_local_port_t *port = port_handle;
mutex_enter(&port->fp_mutex);
fabric = FC_IS_TOP_SWITCH(port->fp_topology) ? 1 : 0;
mutex_exit(&port->fp_mutex);
if (!fabric && pd) {
return (FC_BADOBJECT);
}
if (FC_IS_CMD_A_REG(ns_req->ns_cmd)) {
if (pd == NULL) {
rval = fctl_update_host_ns_values(port, ns_req);
if (rval != FC_SUCCESS) {
return (rval);
}
} else {
return (FC_BADOBJECT);
}
if (!fabric) {
return (FC_SUCCESS);
}
} else if (!fabric) {
return (fctl_retrieve_host_ns_values(port, ns_req));
}
job = fctl_alloc_job(JOB_NS_CMD, 0, NULL, NULL, KM_SLEEP);
ASSERT(job != NULL);
ns_cmd = fctl_alloc_ns_cmd(ns_req->ns_req_len,
ns_req->ns_resp_len, ns_req->ns_resp_len, 0, KM_SLEEP);
ASSERT(ns_cmd != NULL);
ns_cmd->ns_cmd_code = ns_req->ns_cmd;
bcopy(ns_req->ns_req_payload, ns_cmd->ns_cmd_buf,
ns_req->ns_req_len);
job->job_private = (void *)ns_cmd;
fctl_enque_job(port, job);
fctl_jobwait(job);
rval = job->job_result;
if (ns_req->ns_resp_len >= ns_cmd->ns_data_len) {
bcopy(ns_cmd->ns_data_buf, ns_req->ns_resp_payload,
ns_cmd->ns_data_len);
}
bcopy(&ns_cmd->ns_resp_hdr, &ns_req->ns_resp_hdr,
sizeof (fc_ct_header_t));
fctl_free_ns_cmd(ns_cmd);
fctl_dealloc_job(job);
return (rval);
}
int
fc_ulp_transport(opaque_t port_handle, fc_packet_t *pkt)
{
int rval;
fc_local_port_t *port;
fc_remote_port_t *pd, *newpd;
fc_ulp_rscn_info_t *rscnp =
(fc_ulp_rscn_info_t *)pkt->pkt_ulp_rscn_infop;
port = port_handle;
if (pkt->pkt_tran_flags & FC_TRAN_DUMPING) {
return (port->fp_fca_tran->fca_transport(
port->fp_fca_handle, pkt));
}
mutex_enter(&port->fp_mutex);
if (port->fp_statec_busy) {
mutex_exit(&port->fp_mutex);
return (FC_STATEC_BUSY);
}
if (((FC_PORT_STATE_MASK(port->fp_state)) == FC_STATE_OFFLINE) ||
(port->fp_soft_state &
(FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
mutex_exit(&port->fp_mutex);
return (FC_OFFLINE);
}
if ((rscnp != NULL) &&
(rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
(rscnp->ulp_rscn_count != port->fp_rscn_count)) {
mutex_exit(&port->fp_mutex);
return (FC_DEVICE_BUSY_NEW_RSCN);
}
pd = pkt->pkt_pd;
if (pd) {
if (pd->pd_type == PORT_DEVICE_OLD ||
pd->pd_state == PORT_DEVICE_INVALID) {
newpd = fctl_get_remote_port_by_pwwn_mutex_held(port,
&pd->pd_port_name);
if ((newpd) && (newpd != pd)) {
mutex_enter(&newpd->pd_mutex);
newpd->pd_ref_count++;
pkt->pkt_pd = newpd;
mutex_exit(&newpd->pd_mutex);
mutex_enter(&pd->pd_mutex);
pd->pd_ref_count--;
if ((pd->pd_state == PORT_DEVICE_INVALID) &&
(pd->pd_ref_count == 0)) {
fc_remote_node_t *node =
pd->pd_remote_nodep;
mutex_exit(&pd->pd_mutex);
mutex_exit(&port->fp_mutex);
if ((fctl_destroy_remote_port(port, pd)
== 0) && (node != NULL)) {
fctl_destroy_remote_node(node);
}
mutex_enter(&port->fp_mutex);
} else {
mutex_exit(&pd->pd_mutex);
}
pd = newpd;
}
}
if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
rval = (pd->pd_state == PORT_DEVICE_VALID) ?
FC_LOGINREQ : FC_BADDEV;
mutex_exit(&port->fp_mutex);
return (rval);
}
if (pd->pd_flags != PD_IDLE) {
mutex_exit(&port->fp_mutex);
return (FC_DEVICE_BUSY);
}
if (pd->pd_type == PORT_DEVICE_OLD ||
pd->pd_state == PORT_DEVICE_INVALID) {
mutex_exit(&port->fp_mutex);
return (FC_BADDEV);
}
} else if (FC_IS_REAL_DEVICE(pkt->pkt_cmd_fhdr.d_id)) {
mutex_exit(&port->fp_mutex);
return (FC_BADPACKET);
}
mutex_exit(&port->fp_mutex);
return (port->fp_fca_tran->fca_transport(port->fp_fca_handle, pkt));
}
int
fc_ulp_issue_els(opaque_t port_handle, fc_packet_t *pkt)
{
int rval;
fc_local_port_t *port = port_handle;
fc_remote_port_t *pd;
fc_ulp_rscn_info_t *rscnp =
(fc_ulp_rscn_info_t *)pkt->pkt_ulp_rscn_infop;
mutex_enter(&port->fp_mutex);
if ((FC_PORT_STATE_MASK(port->fp_state) == FC_STATE_OFFLINE) ||
(port->fp_soft_state &
(FP_SOFT_IN_DETACH | FP_SOFT_SUSPEND | FP_SOFT_POWER_DOWN))) {
mutex_exit(&port->fp_mutex);
return (FC_OFFLINE);
}
if (port->fp_statec_busy) {
mutex_exit(&port->fp_mutex);
return (FC_STATEC_BUSY);
}
if ((rscnp != NULL) &&
(rscnp->ulp_rscn_count != FC_INVALID_RSCN_COUNT) &&
(rscnp->ulp_rscn_count != port->fp_rscn_count)) {
mutex_exit(&port->fp_mutex);
return (FC_DEVICE_BUSY_NEW_RSCN);
}
mutex_exit(&port->fp_mutex);
if ((pd = pkt->pkt_pd) != NULL) {
mutex_enter(&pd->pd_mutex);
if (pd->pd_state != PORT_DEVICE_LOGGED_IN) {
rval = (pd->pd_state == PORT_DEVICE_VALID) ?
FC_LOGINREQ : FC_BADDEV;
mutex_exit(&pd->pd_mutex);
return (rval);
}
if (pd->pd_flags != PD_IDLE) {
mutex_exit(&pd->pd_mutex);
return (FC_DEVICE_BUSY);
}
if (pd->pd_type == PORT_DEVICE_OLD ||
pd->pd_state == PORT_DEVICE_INVALID) {
mutex_exit(&pd->pd_mutex);
return (FC_BADDEV);
}
mutex_exit(&pd->pd_mutex);
}
return (port->fp_fca_tran->fca_els_send(port->fp_fca_handle, pkt));
}
int
fc_ulp_uballoc(opaque_t port_handle, uint32_t *count, uint32_t size,
uint32_t type, uint64_t *tokens)
{
fc_local_port_t *port = port_handle;
return (port->fp_fca_tran->fca_ub_alloc(port->fp_fca_handle,
tokens, size, count, type));
}
int
fc_ulp_ubfree(opaque_t port_handle, uint32_t count, uint64_t *tokens)
{
fc_local_port_t *port = port_handle;
return (port->fp_fca_tran->fca_ub_free(port->fp_fca_handle,
count, tokens));
}
int
fc_ulp_ubrelease(opaque_t port_handle, uint32_t count, uint64_t *tokens)
{
fc_local_port_t *port = port_handle;
return (port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
count, tokens));
}
int
fc_ulp_abort(opaque_t port_handle, fc_packet_t *pkt, int flags)
{
fc_local_port_t *port = port_handle;
return (port->fp_fca_tran->fca_abort(port->fp_fca_handle, pkt, flags));
}
int
fc_ulp_linkreset(opaque_t port_handle, la_wwn_t *pwwn, int sleep)
{
int rval;
fc_local_port_t *port;
job_request_t *job;
port = port_handle;
mutex_enter(&port->fp_mutex);
if (port->fp_soft_state & FP_SOFT_IN_LINK_RESET) {
mutex_exit(&port->fp_mutex);
return (FC_SUCCESS);
}
if (port->fp_statec_busy) {
mutex_exit(&port->fp_mutex);
return (FC_STATEC_BUSY);
}
port->fp_soft_state |= FP_SOFT_IN_LINK_RESET;
mutex_exit(&port->fp_mutex);
if (fctl_busy_port(port) != 0) {
mutex_enter(&port->fp_mutex);
port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
mutex_exit(&port->fp_mutex);
return (FC_FAILURE);
}
if (sleep == KM_SLEEP) {
job = fctl_alloc_job(JOB_LINK_RESET, 0, NULL, NULL, sleep);
ASSERT(job != NULL);
job->job_private = (void *)pwwn;
job->job_counter = 1;
fctl_enque_job(port, job);
fctl_jobwait(job);
mutex_enter(&port->fp_mutex);
port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
mutex_exit(&port->fp_mutex);
fctl_idle_port(port);
rval = job->job_result;
fctl_dealloc_job(job);
} else {
job = fctl_alloc_job(JOB_LINK_RESET, JOB_TYPE_FCTL_ASYNC,
fctl_link_reset_done, port, sleep);
if (job == NULL) {
mutex_enter(&port->fp_mutex);
port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
mutex_exit(&port->fp_mutex);
fctl_idle_port(port);
return (FC_NOMEM);
}
job->job_private = (void *)pwwn;
job->job_counter = 1;
fctl_priority_enque_job(port, job);
rval = FC_SUCCESS;
}
return (rval);
}
int
fc_ulp_port_reset(opaque_t port_handle, uint32_t cmd)
{
int rval = FC_SUCCESS;
fc_local_port_t *port = port_handle;
switch (cmd) {
case FC_RESET_PORT:
rval = port->fp_fca_tran->fca_reset(
port->fp_fca_handle, FC_FCA_LINK_RESET);
break;
case FC_RESET_ADAPTER:
rval = port->fp_fca_tran->fca_reset(
port->fp_fca_handle, FC_FCA_RESET);
break;
case FC_RESET_DUMP:
rval = port->fp_fca_tran->fca_reset(
port->fp_fca_handle, FC_FCA_CORE);
break;
case FC_RESET_CRASH:
rval = port->fp_fca_tran->fca_reset(
port->fp_fca_handle, FC_FCA_RESET_CORE);
break;
default:
rval = FC_FAILURE;
}
return (rval);
}
int
fc_ulp_get_port_login_params(opaque_t port_handle, la_els_logi_t *login_params)
{
fc_local_port_t *port = port_handle;
*login_params = port->fp_service_params;
return (FC_SUCCESS);
}
int
fc_ulp_get_port_instance(opaque_t port_handle)
{
fc_local_port_t *port = port_handle;
return (port->fp_instance);
}
opaque_t
fc_ulp_get_port_handle(int port_instance)
{
opaque_t port_handle = NULL;
fc_fca_port_t *cur;
mutex_enter(&fctl_port_lock);
for (cur = fctl_fca_portlist; cur; cur = cur->port_next) {
if (cur->port_handle->fp_instance == port_instance) {
port_handle = (opaque_t)cur->port_handle;
break;
}
}
mutex_exit(&fctl_port_lock);
return (port_handle);
}
int
fc_ulp_error(int fc_errno, char **errmsg)
{
return (fctl_error(fc_errno, errmsg));
}
int
fc_ulp_pkt_error(fc_packet_t *pkt, char **state, char **reason,
char **action, char **expln)
{
return (fctl_pkt_error(pkt, state, reason, action, expln));
}
int
fc_ulp_is_name_present(caddr_t ulp_name)
{
int rval = FC_FAILURE;
fc_ulp_list_t *list;
mutex_enter(&fctl_ulp_list_mutex);
for (list = fctl_ulp_list; list != NULL; list = list->ulp_next) {
if (strcmp(list->ulp_info->ulp_name, ulp_name) == 0) {
rval = FC_SUCCESS;
break;
}
}
mutex_exit(&fctl_ulp_list_mutex);
return (rval);
}
int
fc_ulp_get_pwwn_by_did(opaque_t port_handle, fc_portid_t d_id, la_wwn_t *pwwn)
{
int rval = FC_FAILURE;
fc_remote_port_t *pd;
fc_local_port_t *port = port_handle;
pd = fctl_get_remote_port_by_did(port, d_id.port_id);
if (pd != NULL) {
mutex_enter(&pd->pd_mutex);
*pwwn = pd->pd_port_name;
mutex_exit(&pd->pd_mutex);
rval = FC_SUCCESS;
}
return (rval);
}
int
fc_ulp_pwwn_to_portmap(opaque_t port_handle, la_wwn_t *bytes, fc_portmap_t *map)
{
fc_local_port_t *port = port_handle;
fc_remote_node_t *node;
fc_remote_port_t *pd;
pd = fctl_get_remote_port_by_pwwn(port, bytes);
if (pd == NULL) {
return (FC_FAILURE);
}
mutex_enter(&pd->pd_mutex);
map->map_pwwn = pd->pd_port_name;
map->map_did = pd->pd_port_id;
map->map_hard_addr = pd->pd_hard_addr;
map->map_state = pd->pd_state;
map->map_type = pd->pd_type;
map->map_flags = 0;
ASSERT(map->map_type <= PORT_DEVICE_DELETE);
bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
node = pd->pd_remote_nodep;
mutex_exit(&pd->pd_mutex);
if (node) {
mutex_enter(&node->fd_mutex);
map->map_nwwn = node->fd_node_name;
mutex_exit(&node->fd_mutex);
}
map->map_pd = pd;
return (FC_SUCCESS);
}
opaque_t
fc_ulp_get_fca_device(opaque_t port_handle, fc_portid_t d_id)
{
fc_local_port_t *port = port_handle;
if (port->fp_fca_tran->fca_get_device == NULL) {
return (NULL);
}
return (port->fp_fca_tran->fca_get_device(port->fp_fca_handle, d_id));
}
int
fc_ulp_port_notify(opaque_t port_handle, uint32_t cmd)
{
int rval = FC_SUCCESS;
fc_local_port_t *port = port_handle;
if (port->fp_fca_tran->fca_notify) {
mutex_enter(&port->fp_mutex);
switch (cmd) {
case FC_NOTIFY_TARGET_MODE:
port->fp_options |= FP_TARGET_MODE;
break;
case FC_NOTIFY_NO_TARGET_MODE:
port->fp_options &= ~FP_TARGET_MODE;
break;
}
mutex_exit(&port->fp_mutex);
rval = port->fp_fca_tran->fca_notify(port->fp_fca_handle, cmd);
}
return (rval);
}
void
fc_ulp_disable_relogin(opaque_t *fc_port, la_wwn_t *pwwn)
{
fc_remote_port_t *pd =
fctl_get_remote_port_by_pwwn((fc_local_port_t *)fc_port, pwwn);
if (pd) {
mutex_enter(&pd->pd_mutex);
pd->pd_aux_flags |= PD_DISABLE_RELOGIN;
mutex_exit(&pd->pd_mutex);
}
}
void
fc_ulp_enable_relogin(opaque_t *fc_port, la_wwn_t *pwwn)
{
fc_remote_port_t *pd =
fctl_get_remote_port_by_pwwn((fc_local_port_t *)fc_port, pwwn);
if (pd) {
mutex_enter(&pd->pd_mutex);
pd->pd_aux_flags &= ~PD_DISABLE_RELOGIN;
mutex_exit(&pd->pd_mutex);
}
}
void
fc_fca_init(struct dev_ops *fca_devops_p)
{
#ifndef __lock_lint
fca_devops_p->devo_bus_ops = &fctl_fca_busops;
#endif
}
int
fc_fca_attach(dev_info_t *fca_dip, fc_fca_tran_t *tran)
{
if (tran->fca_version != FCTL_FCA_MODREV_5) {
const char *name = ddi_driver_name(fca_dip);
ASSERT(name != NULL);
cmn_err(CE_WARN, "fctl: FCA %s version mismatch"
" please upgrade %s", name, name);
return (DDI_FAILURE);
}
ddi_set_driver_private(fca_dip, (caddr_t)tran);
return (DDI_SUCCESS);
}
int
fc_fca_detach(dev_info_t *fca_dip)
{
ddi_set_driver_private(fca_dip, NULL);
return (DDI_SUCCESS);
}
int
fc_fca_update_errors(fc_packet_t *pkt)
{
int ret = FC_SUCCESS;
switch (pkt->pkt_resp_fhdr.r_ctl) {
case R_CTL_P_RJT: {
uint32_t prjt;
prjt = pkt->pkt_resp_fhdr.ro;
pkt->pkt_state = FC_PKT_NPORT_RJT;
pkt->pkt_action = (prjt & 0xFF000000) >> 24;
pkt->pkt_reason = (prjt & 0xFF0000) >> 16;
break;
}
case R_CTL_F_RJT: {
uint32_t frjt;
frjt = pkt->pkt_resp_fhdr.ro;
pkt->pkt_state = FC_PKT_FABRIC_RJT;
pkt->pkt_action = (frjt & 0xFF000000) >> 24;
pkt->pkt_reason = (frjt & 0xFF0000) >> 16;
break;
}
case R_CTL_P_BSY: {
uint32_t pbsy;
pbsy = pkt->pkt_resp_fhdr.ro;
pkt->pkt_state = FC_PKT_NPORT_BSY;
pkt->pkt_action = (pbsy & 0xFF000000) >> 24;
pkt->pkt_reason = (pbsy & 0xFF0000) >> 16;
break;
}
case R_CTL_F_BSY_LC:
case R_CTL_F_BSY_DF: {
uchar_t fbsy;
fbsy = pkt->pkt_resp_fhdr.type;
pkt->pkt_state = FC_PKT_FABRIC_BSY;
pkt->pkt_reason = (fbsy & 0xF0) >> 4;
break;
}
case R_CTL_LS_BA_RJT: {
uint32_t brjt;
brjt = *(uint32_t *)pkt->pkt_resp;
pkt->pkt_state = FC_PKT_BA_RJT;
pkt->pkt_reason = (brjt & 0xFF0000) >> 16;
pkt->pkt_expln = (brjt & 0xFF00) >> 8;
break;
}
case R_CTL_ELS_RSP: {
la_els_rjt_t *lsrjt;
lsrjt = (la_els_rjt_t *)pkt->pkt_resp;
if (lsrjt->ls_code.ls_code == LA_ELS_RJT) {
pkt->pkt_state = FC_PKT_LS_RJT;
pkt->pkt_reason = lsrjt->reason;
pkt->pkt_action = lsrjt->action;
break;
}
}
default:
ret = FC_FAILURE;
break;
}
return (ret);
}
int
fc_fca_error(int fc_errno, char **errmsg)
{
return (fctl_error(fc_errno, errmsg));
}
int
fc_fca_pkt_error(fc_packet_t *pkt, char **state, char **reason,
char **action, char **expln)
{
return (fctl_pkt_error(pkt, state, reason, action, expln));
}
void
fc_wwn_to_str(la_wwn_t *wwn, caddr_t str)
{
int count;
for (count = 0; count < FCTL_WWN_SIZE(wwn); count++, str += 2) {
(void) sprintf(str, "%02x", wwn->raw_wwn[count]);
}
*str = '\0';
}
#define FC_ATOB(x) (((x) >= '0' && (x) <= '9') ? ((x) - '0') : \
((x) >= 'a' && (x) <= 'f') ? \
((x) - 'a' + 10) : ((x) - 'A' + 10))
void
fc_str_to_wwn(caddr_t str, la_wwn_t *wwn)
{
int count = 0;
uchar_t byte;
while (*str) {
byte = FC_ATOB(*str);
str++;
byte = byte << 4 | FC_ATOB(*str);
str++;
wwn->raw_wwn[count++] = byte;
}
}
static int
fctl_fca_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip,
ddi_ctl_enum_t op, void *arg, void *result)
{
switch (op) {
case DDI_CTLOPS_REPORTDEV:
break;
case DDI_CTLOPS_IOMIN:
break;
case DDI_CTLOPS_INITCHILD:
return (fctl_initchild(fca_dip, (dev_info_t *)arg));
case DDI_CTLOPS_UNINITCHILD:
return (fctl_uninitchild(fca_dip, (dev_info_t *)arg));
default:
return (ddi_ctlops(fca_dip, rip, op, arg, result));
}
return (DDI_SUCCESS);
}
static int
fctl_initchild(dev_info_t *fca_dip, dev_info_t *port_dip)
{
int rval;
int port_no;
int port_len;
char name[20];
fc_fca_tran_t *tran;
dev_info_t *dip;
int portprop;
port_len = sizeof (port_no);
portprop = ddi_prop_get_int(DDI_DEV_T_ANY, port_dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
"phyport-instance", -1);
if ((portprop == -1) && ndi_dev_is_persistent_node(port_dip)) {
ddi_set_name_addr(port_dip, NULL);
return (DDI_FAILURE);
}
rval = ddi_prop_op(DDI_DEV_T_ANY, port_dip, PROP_LEN_AND_VAL_BUF,
DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "port",
(caddr_t)&port_no, &port_len);
if (rval != DDI_SUCCESS) {
return (DDI_FAILURE);
}
tran = (fc_fca_tran_t *)ddi_get_driver_private(fca_dip);
ASSERT(tran != NULL);
(void) sprintf((char *)name, "%x,0", port_no);
ddi_set_name_addr(port_dip, name);
dip = ndi_devi_find(fca_dip, ddi_binding_name(port_dip), name);
if ((portprop == -1) && dip && ndi_dev_is_persistent_node(dip)) {
ddi_set_name_addr(dip, NULL);
dip = ndi_devi_find(fca_dip, ddi_binding_name(port_dip), name);
}
if ((portprop == -1) && dip && (dip != port_dip)) {
ddi_set_name_addr(port_dip, NULL);
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
static int
fctl_uninitchild(dev_info_t *fca_dip, dev_info_t *port_dip)
{
ddi_set_name_addr(port_dip, NULL);
return (DDI_SUCCESS);
}
static dev_info_t *
fctl_findchild(dev_info_t *pdip, char *cname, char *caddr)
{
dev_info_t *dip;
char *addr;
ASSERT(cname != NULL && caddr != NULL);
for (dip = ddi_get_child(pdip); dip != NULL;
dip = ddi_get_next_sibling(dip)) {
if (strcmp(cname, ddi_node_name(dip)) != 0) {
continue;
}
if ((addr = ddi_get_name_addr(dip)) == NULL) {
if (ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
"bus-addr", &addr) == DDI_PROP_SUCCESS) {
if (strcmp(caddr, addr) == 0) {
ddi_prop_free(addr);
return (dip);
}
ddi_prop_free(addr);
}
} else {
if (strcmp(caddr, addr) == 0) {
return (dip);
}
}
}
return (NULL);
}
int
fctl_check_npiv_portindex(dev_info_t *dip, int vindex)
{
int i, instance;
fc_local_port_t *port;
instance = ddi_get_instance(dip);
port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
if ((!port) || (vindex <= 0) || (vindex >= FC_NPIV_MAX_PORT)) {
return (0);
}
i = vindex-1;
mutex_enter(&port->fp_mutex);
if (port->fp_npiv_portindex[i] == 0) {
mutex_exit(&port->fp_mutex);
return (vindex);
}
mutex_exit(&port->fp_mutex);
return (0);
}
int
fctl_get_npiv_portindex(dev_info_t *dip)
{
int i, instance;
fc_local_port_t *port;
instance = ddi_get_instance(dip);
port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
if (!port) {
return (0);
}
mutex_enter(&port->fp_mutex);
for (i = 0; i < FC_NPIV_MAX_PORT; i++) {
if (port->fp_npiv_portindex[i] == 0) {
mutex_exit(&port->fp_mutex);
return (i+1);
}
}
mutex_exit(&port->fp_mutex);
return (0);
}
void
fctl_set_npiv_portindex(dev_info_t *dip, int index)
{
int instance;
fc_local_port_t *port;
instance = ddi_get_instance(dip);
port = (fc_local_port_t *)fc_ulp_get_port_handle(instance);
if (!port) {
return;
}
mutex_enter(&port->fp_mutex);
port->fp_npiv_portindex[index - 1] = 1;
mutex_exit(&port->fp_mutex);
}
int
fctl_fca_create_npivport(dev_info_t *parent,
dev_info_t *phydip, char *nname, char *pname, uint32_t *vindex)
{
int rval = 0, devstrlen;
char *devname, *cname, *caddr, *devstr;
dev_info_t *child = NULL;
int portnum;
if (*vindex == 0) {
portnum = fctl_get_npiv_portindex(phydip);
*vindex = portnum;
} else {
portnum = fctl_check_npiv_portindex(phydip, *vindex);
}
if (portnum == 0) {
cmn_err(CE_WARN,
"Cann't find valid port index, fail to create devnode");
return (NDI_FAILURE);
}
devname = kmem_zalloc(MAXNAMELEN, KM_SLEEP);
(void) sprintf(devname, "fp@%x,0", portnum);
devstrlen = strlen(devname) + 1;
devstr = i_ddi_strdup(devname, KM_SLEEP);
i_ddi_parse_name(devstr, &cname, &caddr, NULL);
if (fctl_findchild(parent, cname, caddr) != NULL) {
rval = NDI_FAILURE;
goto freememory;
}
ndi_devi_alloc_sleep(parent, cname, DEVI_PSEUDO_NODEID, &child);
if (child == NULL) {
cmn_err(CE_WARN,
"fctl_create_npiv_port fail to create new devinfo");
rval = NDI_FAILURE;
goto freememory;
}
if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
"bus-addr", caddr) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "fctl%d: prop update bus-addr %s@%s failed",
ddi_get_instance(parent), cname, caddr);
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
if (strlen(nname) != 0) {
if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
"node-name", nname) != DDI_PROP_SUCCESS) {
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
}
if (strlen(pname) != 0) {
if (ndi_prop_update_string(DDI_DEV_T_NONE, child,
"port-name", pname) != DDI_PROP_SUCCESS) {
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
}
if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
"port", portnum) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN, "fp%d: prop_update port %s@%s failed",
ddi_get_instance(parent), cname, caddr);
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
if (ddi_prop_update_int(DDI_DEV_T_NONE, child,
"phyport-instance", ddi_get_instance(phydip)) != DDI_PROP_SUCCESS) {
cmn_err(CE_WARN,
"fp%d: prop_update phyport-instance %s@%s failed",
ddi_get_instance(parent), cname, caddr);
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
rval = ndi_devi_online(child, NDI_ONLINE_ATTACH);
if (rval != NDI_SUCCESS) {
cmn_err(CE_WARN, "fp%d: online_driver %s failed",
ddi_get_instance(parent), cname);
rval = NDI_FAILURE;
goto freememory;
}
fctl_set_npiv_portindex(phydip, portnum);
freememory:
kmem_free(devstr, devstrlen);
kmem_free(devname, MAXNAMELEN);
return (rval);
}
void
fctl_add_port(fc_local_port_t *port)
{
fc_fca_port_t *new;
new = kmem_zalloc(sizeof (*new), KM_SLEEP);
mutex_enter(&fctl_port_lock);
new->port_handle = port;
new->port_next = fctl_fca_portlist;
fctl_fca_portlist = new;
mutex_exit(&fctl_port_lock);
}
void
fctl_remove_port(fc_local_port_t *port)
{
fc_ulp_module_t *mod;
fc_fca_port_t *prev;
fc_fca_port_t *list;
fc_ulp_ports_t *ulp_port;
rw_enter(&fctl_ulp_lock, RW_WRITER);
rw_enter(&fctl_mod_ports_lock, RW_WRITER);
for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
ulp_port = fctl_get_ulp_port(mod, port);
if (ulp_port == NULL) {
continue;
}
#ifndef __lock_lint
ASSERT((ulp_port->port_dstate & ULP_PORT_ATTACH) == 0);
#endif
(void) fctl_remove_ulp_port(mod, port);
}
rw_exit(&fctl_mod_ports_lock);
rw_exit(&fctl_ulp_lock);
mutex_enter(&fctl_port_lock);
list = fctl_fca_portlist;
prev = NULL;
while (list != NULL) {
if (list->port_handle == port) {
if (prev == NULL) {
fctl_fca_portlist = list->port_next;
} else {
prev->port_next = list->port_next;
}
kmem_free(list, sizeof (*list));
break;
}
prev = list;
list = list->port_next;
}
mutex_exit(&fctl_port_lock);
}
void
fctl_attach_ulps(fc_local_port_t *port, fc_attach_cmd_t cmd,
struct modlinkage *linkage)
{
int rval;
uint32_t s_id;
uint32_t state;
fc_ulp_module_t *mod;
fc_ulp_port_info_t info;
fc_ulp_ports_t *ulp_port;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
info.port_linkage = linkage;
info.port_dip = port->fp_port_dip;
info.port_handle = (opaque_t)port;
info.port_dma_behavior = port->fp_dma_behavior;
info.port_fcp_dma = port->fp_fcp_dma;
info.port_acc_attr = port->fp_fca_tran->fca_acc_attr;
info.port_fca_pkt_size = port->fp_fca_tran->fca_pkt_size;
info.port_reset_action = port->fp_reset_action;
mutex_enter(&port->fp_mutex);
if (port->fp_soft_state & FP_SOFT_IN_DETACH) {
mutex_exit(&port->fp_mutex);
return;
}
s_id = port->fp_port_id.port_id;
if (port->fp_statec_busy) {
info.port_state = port->fp_bind_state;
} else {
info.port_state = port->fp_state;
}
switch (state = FC_PORT_STATE_MASK(info.port_state)) {
case FC_STATE_LOOP:
case FC_STATE_NAMESERVICE:
info.port_state &= ~state;
info.port_state |= FC_STATE_ONLINE;
break;
default:
break;
}
ASSERT((info.port_state & FC_STATE_LOOP) == 0);
info.port_flags = port->fp_topology;
info.port_pwwn = port->fp_service_params.nport_ww_name;
info.port_nwwn = port->fp_service_params.node_ww_name;
mutex_exit(&port->fp_mutex);
rw_enter(&fctl_ulp_lock, RW_READER);
rw_enter(&fctl_mod_ports_lock, RW_WRITER);
for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
if ((port->fp_soft_state & FP_SOFT_FCA_IS_NODMA) &&
(mod->mod_info->ulp_type == FC_TYPE_IS8802_SNAP)) {
continue;
}
if ((ulp_port = fctl_get_ulp_port(mod, port)) == NULL) {
ulp_port = fctl_add_ulp_port(mod, port, KM_SLEEP);
ASSERT(ulp_port != NULL);
mutex_enter(&ulp_port->port_mutex);
ulp_port->port_statec = ((info.port_state &
FC_STATE_ONLINE) ? FC_ULP_STATEC_ONLINE :
FC_ULP_STATEC_OFFLINE);
mutex_exit(&ulp_port->port_mutex);
}
}
rw_downgrade(&fctl_mod_ports_lock);
for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
if ((port->fp_soft_state & FP_SOFT_FCA_IS_NODMA) &&
(mod->mod_info->ulp_type == FC_TYPE_IS8802_SNAP)) {
continue;
}
ulp_port = fctl_get_ulp_port(mod, port);
ASSERT(ulp_port != NULL);
if (fctl_pre_attach(ulp_port, cmd) == FC_FAILURE) {
continue;
}
fctl_init_dma_attr(port, mod, &info);
rval = mod->mod_info->ulp_port_attach(
mod->mod_info->ulp_handle, &info, cmd, s_id);
fctl_post_attach(mod, ulp_port, cmd, rval);
if (rval == FC_SUCCESS && cmd == FC_CMD_ATTACH &&
strcmp(mod->mod_info->ulp_name, "fcp") == 0) {
ASSERT(ddi_get_driver_private(info.port_dip) != NULL);
}
}
rw_exit(&fctl_mod_ports_lock);
rw_exit(&fctl_ulp_lock);
}
static int
fctl_pre_attach(fc_ulp_ports_t *ulp_port, fc_attach_cmd_t cmd)
{
int rval = FC_SUCCESS;
mutex_enter(&ulp_port->port_mutex);
switch (cmd) {
case FC_CMD_ATTACH:
if (ulp_port->port_dstate & ULP_PORT_ATTACH) {
rval = FC_FAILURE;
}
break;
case FC_CMD_RESUME:
ASSERT((ulp_port->port_dstate & ULP_PORT_POWER_DOWN) == 0);
if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
!(ulp_port->port_dstate & ULP_PORT_SUSPEND)) {
rval = FC_FAILURE;
}
break;
case FC_CMD_POWER_UP:
if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
!(ulp_port->port_dstate & ULP_PORT_POWER_DOWN)) {
rval = FC_FAILURE;
}
break;
}
if (rval == FC_SUCCESS) {
ulp_port->port_dstate |= ULP_PORT_BUSY;
}
mutex_exit(&ulp_port->port_mutex);
return (rval);
}
static void
fctl_post_attach(fc_ulp_module_t *mod, fc_ulp_ports_t *ulp_port,
fc_attach_cmd_t cmd, int rval)
{
int be_chatty;
ASSERT(cmd == FC_CMD_ATTACH || cmd == FC_CMD_RESUME ||
cmd == FC_CMD_POWER_UP);
mutex_enter(&ulp_port->port_mutex);
ulp_port->port_dstate &= ~ULP_PORT_BUSY;
be_chatty = (rval == FC_FAILURE_SILENT) ? 0 : 1;
if (rval != FC_SUCCESS) {
caddr_t op;
fc_local_port_t *port = ulp_port->port_handle;
mutex_exit(&ulp_port->port_mutex);
switch (cmd) {
case FC_CMD_ATTACH:
op = "attach";
break;
case FC_CMD_RESUME:
op = "resume";
break;
case FC_CMD_POWER_UP:
op = "power up";
break;
}
if (be_chatty) {
cmn_err(CE_WARN, "!fctl(%d): %s failed for %s",
port->fp_instance, op, mod->mod_info->ulp_name);
}
return;
}
switch (cmd) {
case FC_CMD_ATTACH:
ulp_port->port_dstate |= ULP_PORT_ATTACH;
break;
case FC_CMD_RESUME:
ulp_port->port_dstate &= ~ULP_PORT_SUSPEND;
break;
case FC_CMD_POWER_UP:
ulp_port->port_dstate &= ~ULP_PORT_POWER_DOWN;
break;
}
mutex_exit(&ulp_port->port_mutex);
}
int
fctl_detach_ulps(fc_local_port_t *port, fc_detach_cmd_t cmd,
struct modlinkage *linkage)
{
int rval = FC_SUCCESS;
fc_ulp_module_t *mod;
fc_ulp_port_info_t info;
fc_ulp_ports_t *ulp_port;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
info.port_linkage = linkage;
info.port_dip = port->fp_port_dip;
info.port_handle = (opaque_t)port;
info.port_acc_attr = port->fp_fca_tran->fca_acc_attr;
info.port_fca_pkt_size = port->fp_fca_tran->fca_pkt_size;
rw_enter(&fctl_ulp_lock, RW_READER);
rw_enter(&fctl_mod_ports_lock, RW_READER);
for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
if ((ulp_port = fctl_get_ulp_port(mod, port)) == NULL) {
continue;
}
if (fctl_pre_detach(ulp_port, cmd) != FC_SUCCESS) {
continue;
}
fctl_init_dma_attr(port, mod, &info);
rval = mod->mod_info->ulp_port_detach(
mod->mod_info->ulp_handle, &info, cmd);
fctl_post_detach(mod, ulp_port, cmd, rval);
if (rval != FC_SUCCESS) {
break;
}
if (cmd == FC_CMD_DETACH && strcmp(mod->mod_info->ulp_name,
"fcp") == 0) {
ASSERT(ddi_get_driver_private(info.port_dip) == NULL);
}
mutex_enter(&ulp_port->port_mutex);
ulp_port->port_statec = FC_ULP_STATEC_DONT_CARE;
mutex_exit(&ulp_port->port_mutex);
}
rw_exit(&fctl_mod_ports_lock);
rw_exit(&fctl_ulp_lock);
return (rval);
}
static void
fctl_init_dma_attr(fc_local_port_t *port, fc_ulp_module_t *mod,
fc_ulp_port_info_t *info)
{
if ((strcmp(mod->mod_info->ulp_name, "fcp") == 0) ||
(strcmp(mod->mod_info->ulp_name, "ltct") == 0)) {
info->port_cmd_dma_attr =
port->fp_fca_tran->fca_dma_fcp_cmd_attr;
info->port_data_dma_attr =
port->fp_fca_tran->fca_dma_fcp_data_attr;
info->port_resp_dma_attr =
port->fp_fca_tran->fca_dma_fcp_rsp_attr;
} else if (strcmp(mod->mod_info->ulp_name, "fcsm") == 0) {
info->port_cmd_dma_attr =
port->fp_fca_tran->fca_dma_fcsm_cmd_attr;
info->port_data_dma_attr =
port->fp_fca_tran->fca_dma_attr;
info->port_resp_dma_attr =
port->fp_fca_tran->fca_dma_fcsm_rsp_attr;
} else if (strcmp(mod->mod_info->ulp_name, "fcip") == 0) {
info->port_cmd_dma_attr =
port->fp_fca_tran->fca_dma_fcip_cmd_attr;
info->port_data_dma_attr =
port->fp_fca_tran->fca_dma_attr;
info->port_resp_dma_attr =
port->fp_fca_tran->fca_dma_fcip_rsp_attr;
} else {
info->port_cmd_dma_attr = info->port_data_dma_attr =
info->port_resp_dma_attr =
port->fp_fca_tran->fca_dma_attr;
}
}
static int
fctl_pre_detach(fc_ulp_ports_t *ulp_port, fc_detach_cmd_t cmd)
{
int rval = FC_SUCCESS;
mutex_enter(&ulp_port->port_mutex);
switch (cmd) {
case FC_CMD_DETACH:
if ((ulp_port->port_dstate & ULP_PORT_ATTACH) == 0) {
rval = FC_FAILURE;
}
break;
case FC_CMD_SUSPEND:
if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
ulp_port->port_dstate & ULP_PORT_SUSPEND) {
rval = FC_FAILURE;
}
break;
case FC_CMD_POWER_DOWN:
if (!(ulp_port->port_dstate & ULP_PORT_ATTACH) ||
ulp_port->port_dstate & ULP_PORT_POWER_DOWN) {
rval = FC_FAILURE;
}
break;
}
if (rval == FC_SUCCESS) {
ulp_port->port_dstate |= ULP_PORT_BUSY;
}
mutex_exit(&ulp_port->port_mutex);
return (rval);
}
static void
fctl_post_detach(fc_ulp_module_t *mod, fc_ulp_ports_t *ulp_port,
fc_detach_cmd_t cmd, int rval)
{
ASSERT(cmd == FC_CMD_DETACH || cmd == FC_CMD_SUSPEND ||
cmd == FC_CMD_POWER_DOWN);
mutex_enter(&ulp_port->port_mutex);
ulp_port->port_dstate &= ~ULP_PORT_BUSY;
if (rval != FC_SUCCESS) {
caddr_t op;
fc_local_port_t *port = ulp_port->port_handle;
mutex_exit(&ulp_port->port_mutex);
switch (cmd) {
case FC_CMD_DETACH:
op = "detach";
break;
case FC_CMD_SUSPEND:
op = "suspend";
break;
case FC_CMD_POWER_DOWN:
op = "power down";
break;
}
cmn_err(CE_WARN, "!fctl(%d): %s failed for %s",
port->fp_instance, op, mod->mod_info->ulp_name);
return;
}
switch (cmd) {
case FC_CMD_DETACH:
ulp_port->port_dstate &= ~ULP_PORT_ATTACH;
break;
case FC_CMD_SUSPEND:
ulp_port->port_dstate |= ULP_PORT_SUSPEND;
break;
case FC_CMD_POWER_DOWN:
ulp_port->port_dstate |= ULP_PORT_POWER_DOWN;
break;
}
mutex_exit(&ulp_port->port_mutex);
}
static fc_ulp_ports_t *
fctl_add_ulp_port(fc_ulp_module_t *ulp_module, fc_local_port_t *port_handle,
int sleep)
{
fc_ulp_ports_t *last;
fc_ulp_ports_t *next;
fc_ulp_ports_t *new;
ASSERT(RW_READ_HELD(&fctl_ulp_lock));
ASSERT(RW_WRITE_HELD(&fctl_mod_ports_lock));
last = NULL;
next = ulp_module->mod_ports;
while (next != NULL) {
last = next;
next = next->port_next;
}
new = fctl_alloc_ulp_port(sleep);
if (new == NULL) {
return (new);
}
new->port_handle = port_handle;
if (last == NULL) {
ulp_module->mod_ports = new;
} else {
last->port_next = new;
}
return (new);
}
static fc_ulp_ports_t *
fctl_alloc_ulp_port(int sleep)
{
fc_ulp_ports_t *new;
new = kmem_zalloc(sizeof (*new), sleep);
if (new == NULL) {
return (new);
}
mutex_init(&new->port_mutex, NULL, MUTEX_DRIVER, NULL);
return (new);
}
static int
fctl_remove_ulp_port(struct ulp_module *ulp_module,
fc_local_port_t *port_handle)
{
fc_ulp_ports_t *last;
fc_ulp_ports_t *next;
ASSERT(RW_WRITE_HELD(&fctl_ulp_lock));
ASSERT(RW_WRITE_HELD(&fctl_mod_ports_lock));
last = NULL;
next = ulp_module->mod_ports;
while (next != NULL) {
if (next->port_handle == port_handle) {
if (next->port_dstate & ULP_PORT_ATTACH) {
return (FC_FAILURE);
}
break;
}
last = next;
next = next->port_next;
}
if (next != NULL) {
ASSERT((next->port_dstate & ULP_PORT_ATTACH) == 0);
if (last == NULL) {
ulp_module->mod_ports = next->port_next;
} else {
last->port_next = next->port_next;
}
fctl_dealloc_ulp_port(next);
return (FC_SUCCESS);
} else {
return (FC_FAILURE);
}
}
static void
fctl_dealloc_ulp_port(fc_ulp_ports_t *next)
{
mutex_destroy(&next->port_mutex);
kmem_free(next, sizeof (*next));
}
static fc_ulp_ports_t *
fctl_get_ulp_port(struct ulp_module *ulp_module, fc_local_port_t *port_handle)
{
fc_ulp_ports_t *next;
ASSERT(RW_LOCK_HELD(&fctl_ulp_lock));
ASSERT(RW_LOCK_HELD(&fctl_mod_ports_lock));
for (next = ulp_module->mod_ports; next != NULL;
next = next->port_next) {
if (next->port_handle == port_handle) {
return (next);
}
}
return (NULL);
}
void
fctl_ulp_statec_cb(void *arg)
{
uint32_t s_id;
uint32_t new_state;
fc_local_port_t *port;
fc_ulp_ports_t *ulp_port;
fc_ulp_module_t *mod;
fc_port_clist_t *clist = (fc_port_clist_t *)arg;
ASSERT(clist != NULL);
port = clist->clist_port;
mutex_enter(&port->fp_mutex);
s_id = port->fp_port_id.port_id;
mutex_exit(&port->fp_mutex);
switch (clist->clist_state) {
case FC_STATE_ONLINE:
new_state = FC_ULP_STATEC_ONLINE;
break;
case FC_STATE_OFFLINE:
if (clist->clist_len) {
new_state = FC_ULP_STATEC_OFFLINE_TIMEOUT;
} else {
new_state = FC_ULP_STATEC_OFFLINE;
}
break;
default:
new_state = FC_ULP_STATEC_DONT_CARE;
break;
}
#ifdef DEBUG
if (clist->clist_size) {
int count;
fc_remote_port_t *pd;
ASSERT(clist->clist_map != NULL);
for (count = 0; count < clist->clist_len; count++) {
if (clist->clist_map[count].map_state ==
PORT_DEVICE_INVALID) {
la_wwn_t pwwn;
fc_portid_t d_id;
pd = clist->clist_map[count].map_pd;
if (pd != NULL) {
mutex_enter(&pd->pd_mutex);
pwwn = pd->pd_port_name;
d_id = pd->pd_port_id;
mutex_exit(&pd->pd_mutex);
pd = fctl_get_remote_port_by_pwwn(port,
&pwwn);
ASSERT(pd != clist->clist_map[count].
map_pd);
pd = fctl_get_remote_port_by_did(port,
d_id.port_id);
ASSERT(pd != clist->clist_map[count].
map_pd);
}
}
}
}
#endif
if (clist->clist_size) {
int count;
fc_remote_port_t *pd1, *pd2;
ASSERT(clist->clist_map != NULL);
for (count = 0; count < clist->clist_len-1; count++) {
int count2;
pd1 = clist->clist_map[count].map_pd;
if (pd1 == NULL) {
continue;
}
for (count2 = count+1;
count2 < clist->clist_len;
count2++) {
pd2 = clist->clist_map[count2].map_pd;
if (pd2 == NULL) {
continue;
}
if (pd1 == pd2) {
clist->clist_map[count].map_flags |=
PORT_DEVICE_DUPLICATE_MAP_ENTRY;
break;
}
}
}
}
rw_enter(&fctl_ulp_lock, RW_READER);
for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
rw_enter(&fctl_mod_ports_lock, RW_READER);
ulp_port = fctl_get_ulp_port(mod, port);
rw_exit(&fctl_mod_ports_lock);
if (ulp_port == NULL) {
continue;
}
mutex_enter(&ulp_port->port_mutex);
if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate)) {
mutex_exit(&ulp_port->port_mutex);
continue;
}
switch (ulp_port->port_statec) {
case FC_ULP_STATEC_DONT_CARE:
if (ulp_port->port_statec != new_state) {
ulp_port->port_statec = new_state;
}
break;
case FC_ULP_STATEC_ONLINE:
case FC_ULP_STATEC_OFFLINE:
if (ulp_port->port_statec == new_state) {
mutex_exit(&ulp_port->port_mutex);
continue;
}
ulp_port->port_statec = new_state;
break;
case FC_ULP_STATEC_OFFLINE_TIMEOUT:
if (ulp_port->port_statec == new_state ||
new_state == FC_ULP_STATEC_OFFLINE) {
mutex_exit(&ulp_port->port_mutex);
continue;
}
ulp_port->port_statec = new_state;
break;
default:
ASSERT(0);
break;
}
mod->mod_info->ulp_statec_callback(
mod->mod_info->ulp_handle, (opaque_t)port,
clist->clist_state, clist->clist_flags,
clist->clist_map, clist->clist_len, s_id);
mutex_exit(&ulp_port->port_mutex);
}
rw_exit(&fctl_ulp_lock);
if (clist->clist_size) {
int count;
fc_remote_node_t *node;
fc_remote_port_t *pd;
ASSERT(clist->clist_map != NULL);
for (count = 0; count < clist->clist_len; count++) {
if ((pd = clist->clist_map[count].map_pd) == NULL) {
continue;
}
mutex_enter(&pd->pd_mutex);
pd->pd_ref_count--;
ASSERT(pd->pd_ref_count >= 0);
if (clist->clist_map[count].map_state !=
PORT_DEVICE_INVALID) {
mutex_exit(&pd->pd_mutex);
continue;
}
node = pd->pd_remote_nodep;
pd->pd_aux_flags &= ~PD_GIVEN_TO_ULPS;
mutex_exit(&pd->pd_mutex);
if ((fctl_destroy_remote_port(port, pd) == 0) &&
(node != NULL)) {
fctl_destroy_remote_node(node);
}
}
kmem_free(clist->clist_map,
sizeof (*(clist->clist_map)) * clist->clist_size);
}
if (clist->clist_wait) {
mutex_enter(&clist->clist_mutex);
clist->clist_wait = 0;
cv_signal(&clist->clist_cv);
mutex_exit(&clist->clist_mutex);
} else {
kmem_free(clist, sizeof (*clist));
}
}
fc_remote_node_t *
fctl_create_remote_node(la_wwn_t *nwwn, int sleep)
{
fc_remote_node_t *rnodep;
if ((rnodep = kmem_zalloc(sizeof (*rnodep), sleep)) == NULL) {
return (NULL);
}
mutex_init(&rnodep->fd_mutex, NULL, MUTEX_DRIVER, NULL);
rnodep->fd_node_name = *nwwn;
rnodep->fd_flags = FC_REMOTE_NODE_VALID;
rnodep->fd_numports = 1;
if (fctl_enlist_nwwn_table(rnodep, sleep) != FC_SUCCESS) {
mutex_destroy(&rnodep->fd_mutex);
kmem_free(rnodep, sizeof (*rnodep));
return (NULL);
}
return (rnodep);
}
void
fctl_destroy_remote_node(fc_remote_node_t *rnodep)
{
mutex_enter(&rnodep->fd_mutex);
if (rnodep->fd_numports != 0 || rnodep->fd_portlistp) {
mutex_exit(&rnodep->fd_mutex);
return;
}
mutex_exit(&rnodep->fd_mutex);
mutex_destroy(&rnodep->fd_mutex);
kmem_free(rnodep, sizeof (*rnodep));
}
int
fctl_enlist_nwwn_table(fc_remote_node_t *rnodep, int sleep)
{
int index;
fctl_nwwn_elem_t *new;
fctl_nwwn_list_t *head;
ASSERT(!MUTEX_HELD(&rnodep->fd_mutex));
if ((new = kmem_zalloc(sizeof (*new), sleep)) == NULL) {
return (FC_FAILURE);
}
mutex_enter(&fctl_nwwn_hash_mutex);
new->fne_nodep = rnodep;
mutex_enter(&rnodep->fd_mutex);
ASSERT(fctl_is_wwn_zero(&rnodep->fd_node_name) == FC_FAILURE);
index = HASH_FUNC(WWN_HASH_KEY(rnodep->fd_node_name.raw_wwn),
fctl_nwwn_table_size);
mutex_exit(&rnodep->fd_mutex);
head = &fctl_nwwn_hash_table[index];
new->fne_nextp = head->fnl_headp;
head->fnl_headp = new;
mutex_exit(&fctl_nwwn_hash_mutex);
return (FC_SUCCESS);
}
void
fctl_delist_nwwn_table(fc_remote_node_t *rnodep)
{
int index;
fctl_nwwn_list_t *head;
fctl_nwwn_elem_t *elem;
fctl_nwwn_elem_t *prev;
ASSERT(MUTEX_HELD(&fctl_nwwn_hash_mutex));
ASSERT(MUTEX_HELD(&rnodep->fd_mutex));
index = HASH_FUNC(WWN_HASH_KEY(rnodep->fd_node_name.raw_wwn),
fctl_nwwn_table_size);
head = &fctl_nwwn_hash_table[index];
elem = head->fnl_headp;
prev = NULL;
while (elem != NULL) {
if (elem->fne_nodep == rnodep) {
if (prev == NULL) {
head->fnl_headp = elem->fne_nextp;
} else {
prev->fne_nextp = elem->fne_nextp;
}
break;
}
prev = elem;
elem = elem->fne_nextp;
}
if (elem != NULL) {
kmem_free(elem, sizeof (*elem));
}
}
fc_remote_node_t *
fctl_get_remote_node_by_nwwn(la_wwn_t *node_wwn)
{
int index;
fctl_nwwn_elem_t *elem;
fc_remote_node_t *next;
fc_remote_node_t *rnodep = NULL;
index = HASH_FUNC(WWN_HASH_KEY(node_wwn->raw_wwn),
fctl_nwwn_table_size);
ASSERT(index >= 0 && index < fctl_nwwn_table_size);
mutex_enter(&fctl_nwwn_hash_mutex);
elem = fctl_nwwn_hash_table[index].fnl_headp;
while (elem != NULL) {
next = elem->fne_nodep;
if (next != NULL) {
mutex_enter(&next->fd_mutex);
if (fctl_wwn_cmp(node_wwn, &next->fd_node_name) == 0) {
rnodep = next;
mutex_exit(&next->fd_mutex);
break;
}
mutex_exit(&next->fd_mutex);
}
elem = elem->fne_nextp;
}
mutex_exit(&fctl_nwwn_hash_mutex);
return (rnodep);
}
fc_remote_node_t *
fctl_lock_remote_node_by_nwwn(la_wwn_t *node_wwn)
{
int index;
fctl_nwwn_elem_t *elem;
fc_remote_node_t *next;
fc_remote_node_t *rnodep = NULL;
index = HASH_FUNC(WWN_HASH_KEY(node_wwn->raw_wwn),
fctl_nwwn_table_size);
ASSERT(index >= 0 && index < fctl_nwwn_table_size);
mutex_enter(&fctl_nwwn_hash_mutex);
elem = fctl_nwwn_hash_table[index].fnl_headp;
while (elem != NULL) {
next = elem->fne_nodep;
if (next != NULL) {
mutex_enter(&next->fd_mutex);
if (fctl_wwn_cmp(node_wwn, &next->fd_node_name) == 0) {
rnodep = next;
rnodep->fd_numports++;
mutex_exit(&next->fd_mutex);
break;
}
mutex_exit(&next->fd_mutex);
}
elem = elem->fne_nextp;
}
mutex_exit(&fctl_nwwn_hash_mutex);
return (rnodep);
}
fc_remote_port_t *
fctl_alloc_remote_port(fc_local_port_t *port, la_wwn_t *port_wwn,
uint32_t d_id, uchar_t recepient, int sleep)
{
fc_remote_port_t *pd;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(FC_IS_REAL_DEVICE(d_id));
if ((pd = kmem_zalloc(sizeof (*pd), sleep)) == NULL) {
return (NULL);
}
fctl_tc_constructor(&pd->pd_logo_tc, FC_LOGO_TOLERANCE_LIMIT,
FC_LOGO_TOLERANCE_TIME_LIMIT);
mutex_init(&pd->pd_mutex, NULL, MUTEX_DRIVER, NULL);
pd->pd_port_id.port_id = d_id;
pd->pd_port_name = *port_wwn;
pd->pd_port = port;
pd->pd_state = PORT_DEVICE_VALID;
pd->pd_type = PORT_DEVICE_NEW;
pd->pd_recepient = recepient;
return (pd);
}
void
fctl_dealloc_remote_port(fc_remote_port_t *pd)
{
ASSERT(!MUTEX_HELD(&pd->pd_mutex));
fctl_tc_destructor(&pd->pd_logo_tc);
mutex_destroy(&pd->pd_mutex);
kmem_free(pd, sizeof (*pd));
}
void
fctl_link_remote_port_to_remote_node(fc_remote_node_t *rnodep,
fc_remote_port_t *pd)
{
fc_remote_port_t *last;
fc_remote_port_t *ports;
mutex_enter(&rnodep->fd_mutex);
last = NULL;
for (ports = rnodep->fd_portlistp; ports != NULL;
ports = ports->pd_port_next) {
if (ports == pd) {
mutex_exit(&rnodep->fd_mutex);
return;
}
last = ports;
}
if (last != NULL) {
last->pd_port_next = pd;
} else {
rnodep->fd_portlistp = pd;
}
pd->pd_port_next = NULL;
mutex_enter(&pd->pd_mutex);
pd->pd_remote_nodep = rnodep;
mutex_exit(&pd->pd_mutex);
mutex_exit(&rnodep->fd_mutex);
}
int
fctl_unlink_remote_port_from_remote_node(fc_remote_node_t *rnodep,
fc_remote_port_t *pd)
{
int rcount = 0;
fc_remote_port_t *last;
fc_remote_port_t *ports;
ASSERT(!MUTEX_HELD(&rnodep->fd_mutex));
ASSERT(!MUTEX_HELD(&pd->pd_mutex));
last = NULL;
mutex_enter(&fctl_nwwn_hash_mutex);
mutex_enter(&rnodep->fd_mutex);
ports = rnodep->fd_portlistp;
while (ports != NULL) {
if (ports == pd) {
break;
}
last = ports;
ports = ports->pd_port_next;
}
if (ports) {
rcount = --rnodep->fd_numports;
if (rcount == 0) {
fctl_delist_nwwn_table(rnodep);
}
if (last) {
last->pd_port_next = pd->pd_port_next;
} else {
rnodep->fd_portlistp = pd->pd_port_next;
}
mutex_enter(&pd->pd_mutex);
pd->pd_remote_nodep = NULL;
mutex_exit(&pd->pd_mutex);
}
pd->pd_port_next = NULL;
mutex_exit(&rnodep->fd_mutex);
mutex_exit(&fctl_nwwn_hash_mutex);
return (rcount);
}
void
fctl_enlist_did_table(fc_local_port_t *port, fc_remote_port_t *pd)
{
struct d_id_hash *head;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(MUTEX_HELD(&pd->pd_mutex));
if (pd->pd_aux_flags & PD_IN_DID_QUEUE) {
return;
}
head = &port->fp_did_table[D_ID_HASH_FUNC(pd->pd_port_id.port_id,
did_table_size)];
#ifdef DEBUG
{
int index;
fc_remote_port_t *tmp_pd;
struct d_id_hash *tmp_head;
for (index = 0; index < did_table_size; index++) {
tmp_head = &port->fp_did_table[index];
tmp_pd = tmp_head->d_id_head;
while (tmp_pd != NULL) {
ASSERT(tmp_pd != pd);
if (tmp_pd->pd_state != PORT_DEVICE_INVALID &&
tmp_pd->pd_type != PORT_DEVICE_OLD) {
ASSERT(tmp_pd->pd_port_id.port_id !=
pd->pd_port_id.port_id);
}
tmp_pd = tmp_pd->pd_did_hnext;
}
}
}
bzero(pd->pd_d_stack, sizeof (pd->pd_d_stack));
pd->pd_d_depth = getpcstack(pd->pd_d_stack, FC_STACK_DEPTH);
#endif
pd->pd_did_hnext = head->d_id_head;
head->d_id_head = pd;
pd->pd_aux_flags |= PD_IN_DID_QUEUE;
head->d_id_count++;
}
void
fctl_delist_did_table(fc_local_port_t *port, fc_remote_port_t *pd)
{
uint32_t d_id;
struct d_id_hash *head;
fc_remote_port_t *pd_next;
fc_remote_port_t *last;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(MUTEX_HELD(&pd->pd_mutex));
d_id = pd->pd_port_id.port_id;
head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
pd_next = head->d_id_head;
last = NULL;
while (pd_next != NULL) {
if (pd == pd_next) {
break;
}
last = pd_next;
pd_next = pd_next->pd_did_hnext;
}
if (pd_next) {
head->d_id_count--;
if (last == NULL) {
head->d_id_head = pd->pd_did_hnext;
} else {
last->pd_did_hnext = pd->pd_did_hnext;
}
pd->pd_aux_flags &= ~PD_IN_DID_QUEUE;
pd->pd_did_hnext = NULL;
}
}
void
fctl_enlist_pwwn_table(fc_local_port_t *port, fc_remote_port_t *pd)
{
int index;
struct pwwn_hash *head;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(MUTEX_HELD(&pd->pd_mutex));
ASSERT(fctl_is_wwn_zero(&pd->pd_port_name) == FC_FAILURE);
index = HASH_FUNC(WWN_HASH_KEY(pd->pd_port_name.raw_wwn),
pwwn_table_size);
head = &port->fp_pwwn_table[index];
#ifdef DEBUG
{
int index;
fc_remote_port_t *tmp_pd;
struct pwwn_hash *tmp_head;
for (index = 0; index < pwwn_table_size; index++) {
tmp_head = &port->fp_pwwn_table[index];
tmp_pd = tmp_head->pwwn_head;
while (tmp_pd != NULL) {
ASSERT(tmp_pd != pd);
if (tmp_pd->pd_state != PORT_DEVICE_INVALID &&
tmp_pd->pd_type != PORT_DEVICE_OLD) {
ASSERT(fctl_wwn_cmp(
&tmp_pd->pd_port_name,
&pd->pd_port_name) != 0);
}
tmp_pd = tmp_pd->pd_wwn_hnext;
}
}
}
bzero(pd->pd_w_stack, sizeof (pd->pd_w_stack));
pd->pd_w_depth = getpcstack(pd->pd_w_stack, FC_STACK_DEPTH);
#endif
pd->pd_wwn_hnext = head->pwwn_head;
head->pwwn_head = pd;
head->pwwn_count++;
port->fp_dev_count++;
}
void
fctl_delist_pwwn_table(fc_local_port_t *port, fc_remote_port_t *pd)
{
int index;
la_wwn_t pwwn;
struct pwwn_hash *head;
fc_remote_port_t *pd_next;
fc_remote_port_t *last;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(MUTEX_HELD(&pd->pd_mutex));
pwwn = pd->pd_port_name;
index = HASH_FUNC(WWN_HASH_KEY(pwwn.raw_wwn), pwwn_table_size);
head = &port->fp_pwwn_table[index];
last = NULL;
pd_next = head->pwwn_head;
while (pd_next != NULL) {
if (pd_next == pd) {
break;
}
last = pd_next;
pd_next = pd_next->pd_wwn_hnext;
}
if (pd_next) {
head->pwwn_count--;
port->fp_dev_count--;
if (last == NULL) {
head->pwwn_head = pd->pd_wwn_hnext;
} else {
last->pd_wwn_hnext = pd->pd_wwn_hnext;
}
pd->pd_wwn_hnext = NULL;
}
}
fc_remote_port_t *
fctl_get_remote_port_by_did(fc_local_port_t *port, uint32_t d_id)
{
struct d_id_hash *head;
fc_remote_port_t *pd;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
pd = head->d_id_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (pd->pd_port_id.port_id == d_id) {
mutex_exit(&pd->pd_mutex);
break;
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_did_hnext;
}
mutex_exit(&port->fp_mutex);
return (pd);
}
#ifndef __lock_lint
void
fc_ulp_hold_remote_port(opaque_t port_handle)
{
fc_remote_port_t *pd = port_handle;
mutex_enter(&pd->pd_mutex);
pd->pd_ref_count++;
mutex_exit(&pd->pd_mutex);
}
fc_remote_port_t *
fctl_hold_remote_port_by_did(fc_local_port_t *port, uint32_t d_id)
{
struct d_id_hash *head;
fc_remote_port_t *pd;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
head = &port->fp_did_table[D_ID_HASH_FUNC(d_id, did_table_size)];
pd = head->d_id_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (pd->pd_port_id.port_id == d_id && pd->pd_state !=
PORT_DEVICE_INVALID && pd->pd_type != PORT_DEVICE_OLD) {
ASSERT(pd->pd_ref_count >= 0);
pd->pd_ref_count++;
mutex_exit(&pd->pd_mutex);
break;
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_did_hnext;
}
mutex_exit(&port->fp_mutex);
return (pd);
}
#endif
fc_remote_port_t *
fctl_get_remote_port_by_pwwn(fc_local_port_t *port, la_wwn_t *pwwn)
{
int index;
struct pwwn_hash *head;
fc_remote_port_t *pd;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
head = &port->fp_pwwn_table[index];
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0) {
mutex_exit(&pd->pd_mutex);
break;
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_wwn_hnext;
}
mutex_exit(&port->fp_mutex);
return (pd);
}
fc_remote_port_t *
fctl_get_remote_port_by_pwwn_mutex_held(fc_local_port_t *port, la_wwn_t *pwwn)
{
int index;
struct pwwn_hash *head;
fc_remote_port_t *pd;
ASSERT(MUTEX_HELD(&port->fp_mutex));
index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
head = &port->fp_pwwn_table[index];
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0) {
mutex_exit(&pd->pd_mutex);
break;
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_wwn_hnext;
}
return (pd);
}
fc_remote_port_t *
fctl_hold_remote_port_by_pwwn(fc_local_port_t *port, la_wwn_t *pwwn)
{
int index;
struct pwwn_hash *head;
fc_remote_port_t *pd;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
index = HASH_FUNC(WWN_HASH_KEY(pwwn->raw_wwn), pwwn_table_size);
head = &port->fp_pwwn_table[index];
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (fctl_wwn_cmp(&pd->pd_port_name, pwwn) == 0 &&
pd->pd_state != PORT_DEVICE_INVALID &&
pd->pd_type != PORT_DEVICE_OLD) {
ASSERT(pd->pd_ref_count >= 0);
pd->pd_ref_count++;
mutex_exit(&pd->pd_mutex);
break;
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_wwn_hnext;
}
mutex_exit(&port->fp_mutex);
return (pd);
}
void
fctl_release_remote_port(fc_remote_port_t *pd)
{
int remove = 0;
fc_remote_node_t *node;
fc_local_port_t *port;
mutex_enter(&pd->pd_mutex);
port = pd->pd_port;
ASSERT(pd->pd_ref_count > 0);
pd->pd_ref_count--;
if (pd->pd_ref_count == 0 &&
(pd->pd_aux_flags & PD_NEEDS_REMOVAL) &&
(pd->pd_flags != PD_ELS_IN_PROGRESS) &&
(pd->pd_flags != PD_ELS_MARK)) {
remove = 1;
pd->pd_aux_flags &= ~PD_NEEDS_REMOVAL;
}
node = pd->pd_remote_nodep;
ASSERT(node != NULL);
mutex_exit(&pd->pd_mutex);
if (remove) {
if (fctl_destroy_remote_port(port, pd) == 0) {
if (node) {
fctl_destroy_remote_node(node);
}
}
}
}
void
fctl_fillout_map(fc_local_port_t *port, fc_portmap_t **map, uint32_t *len,
int whole_map, int justcopy, int orphan)
{
int index;
int listlen;
int full_list;
int initiator;
uint32_t topology;
struct pwwn_hash *head;
fc_remote_port_t *pd;
fc_remote_port_t *old_pd;
fc_remote_port_t *last_pd;
fc_portmap_t *listptr;
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
topology = port->fp_topology;
if (orphan) {
ASSERT(!FC_IS_TOP_SWITCH(topology));
}
for (full_list = listlen = index = 0;
index < pwwn_table_size; index++) {
head = &port->fp_pwwn_table[index];
pd = head->pwwn_head;
while (pd != NULL) {
full_list++;
mutex_enter(&pd->pd_mutex);
if (pd->pd_type != PORT_DEVICE_NOCHANGE) {
listlen++;
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_wwn_hnext;
}
}
if (whole_map == 0) {
if (listlen == 0 && *len == 0) {
*map = NULL;
*len = listlen;
mutex_exit(&port->fp_mutex);
return;
}
} else {
if (full_list == 0 && *len == 0) {
*map = NULL;
*len = full_list;
mutex_exit(&port->fp_mutex);
return;
}
}
if (*len == 0) {
ASSERT(*map == NULL);
if (whole_map == 0) {
listptr = *map = kmem_zalloc(
sizeof (*listptr) * listlen, KM_SLEEP);
*len = listlen;
} else {
listptr = *map = kmem_zalloc(
sizeof (*listptr) * full_list, KM_SLEEP);
*len = full_list;
}
} else {
ASSERT(whole_map == 1);
if (*len < full_list) {
*len = full_list;
mutex_exit(&port->fp_mutex);
return;
}
listptr = *map;
*len = full_list;
}
for (index = 0; index < pwwn_table_size; index++) {
head = &port->fp_pwwn_table[index];
last_pd = NULL;
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if ((whole_map == 0 &&
pd->pd_type == PORT_DEVICE_NOCHANGE) ||
pd->pd_state == PORT_DEVICE_INVALID) {
mutex_exit(&pd->pd_mutex);
last_pd = pd;
pd = pd->pd_wwn_hnext;
continue;
}
mutex_exit(&pd->pd_mutex);
fctl_copy_portmap(listptr, pd);
if (justcopy) {
last_pd = pd;
pd = pd->pd_wwn_hnext;
listptr++;
continue;
}
mutex_enter(&pd->pd_mutex);
ASSERT(pd->pd_state != PORT_DEVICE_INVALID);
if (pd->pd_type == PORT_DEVICE_OLD) {
listptr->map_pd = pd;
listptr->map_state = pd->pd_state =
PORT_DEVICE_INVALID;
old_pd = pd;
pd = old_pd->pd_wwn_hnext;
if (last_pd == NULL) {
ASSERT(old_pd == head->pwwn_head);
head->pwwn_head = pd;
} else {
last_pd->pd_wwn_hnext = pd;
}
head->pwwn_count--;
port->fp_dev_count--;
old_pd->pd_wwn_hnext = NULL;
if (port->fp_topology == FC_TOP_PRIVATE_LOOP &&
port->fp_statec_busy && !orphan) {
fctl_check_alpa_list(port, old_pd);
}
fctl_delist_did_table(port, old_pd);
ASSERT(old_pd->pd_remote_nodep != NULL);
initiator = (old_pd->pd_recepient ==
PD_PLOGI_INITIATOR) ? 1 : 0;
mutex_exit(&old_pd->pd_mutex);
mutex_exit(&port->fp_mutex);
if (orphan) {
fctl_print_if_not_orphan(port, old_pd);
(void) fctl_add_orphan(port, old_pd,
KM_NOSLEEP);
}
if (FC_IS_TOP_SWITCH(topology) && initiator) {
(void) fctl_add_orphan(port, old_pd,
KM_NOSLEEP);
}
mutex_enter(&port->fp_mutex);
} else {
listptr->map_pd = pd;
pd->pd_type = PORT_DEVICE_NOCHANGE;
mutex_exit(&pd->pd_mutex);
last_pd = pd;
pd = pd->pd_wwn_hnext;
}
listptr++;
}
}
mutex_exit(&port->fp_mutex);
}
job_request_t *
fctl_alloc_job(int job_code, int job_flags, void (*comp) (opaque_t, uchar_t),
opaque_t arg, int sleep)
{
job_request_t *job;
job = (job_request_t *)kmem_cache_alloc(fctl_job_cache, sleep);
if (job != NULL) {
job->job_result = FC_SUCCESS;
job->job_code = job_code;
job->job_flags = job_flags;
job->job_cb_arg = arg;
job->job_comp = comp;
job->job_private = NULL;
job->job_ulp_pkts = NULL;
job->job_ulp_listlen = 0;
#ifndef __lock_lint
job->job_counter = 0;
job->job_next = NULL;
#endif
}
return (job);
}
void
fctl_dealloc_job(job_request_t *job)
{
kmem_cache_free(fctl_job_cache, (void *)job);
}
void
fctl_enque_job(fc_local_port_t *port, job_request_t *job)
{
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
if (port->fp_job_tail == NULL) {
ASSERT(port->fp_job_head == NULL);
port->fp_job_head = port->fp_job_tail = job;
} else {
port->fp_job_tail->job_next = job;
port->fp_job_tail = job;
}
job->job_next = NULL;
cv_signal(&port->fp_cv);
mutex_exit(&port->fp_mutex);
}
job_request_t *
fctl_deque_job(fc_local_port_t *port)
{
job_request_t *job;
ASSERT(MUTEX_HELD(&port->fp_mutex));
if (port->fp_job_head == NULL) {
ASSERT(port->fp_job_tail == NULL);
job = NULL;
} else {
job = port->fp_job_head;
if (job->job_next == NULL) {
ASSERT(job == port->fp_job_tail);
port->fp_job_tail = NULL;
}
port->fp_job_head = job->job_next;
}
return (job);
}
void
fctl_priority_enque_job(fc_local_port_t *port, job_request_t *job)
{
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
if (port->fp_job_tail == NULL) {
ASSERT(port->fp_job_head == NULL);
port->fp_job_head = port->fp_job_tail = job;
job->job_next = NULL;
} else {
job->job_next = port->fp_job_head;
port->fp_job_head = job;
}
cv_signal(&port->fp_cv);
mutex_exit(&port->fp_mutex);
}
void
fctl_jobwait(job_request_t *job)
{
ASSERT(!(job->job_flags & JOB_TYPE_FCTL_ASYNC));
sema_p(&job->job_fctl_sema);
ASSERT(!MUTEX_HELD(&job->job_mutex));
}
void
fctl_jobdone(job_request_t *job)
{
if (job->job_flags & JOB_TYPE_FCTL_ASYNC) {
if (job->job_comp) {
job->job_comp(job->job_cb_arg, job->job_result);
}
fctl_dealloc_job(job);
} else {
sema_v(&job->job_fctl_sema);
}
}
int
fctl_wwn_cmp(la_wwn_t *src, la_wwn_t *dst)
{
uint8_t *l, *r;
int i;
uint64_t wl, wr;
l = (uint8_t *)src;
r = (uint8_t *)dst;
for (i = 0, wl = 0; i < 8; i++) {
wl <<= 8;
wl |= l[i];
}
for (i = 0, wr = 0; i < 8; i++) {
wr <<= 8;
wr |= r[i];
}
if (wl > wr) {
return (1);
} else if (wl == wr) {
return (0);
} else {
return (-1);
}
}
int
fctl_atoi(char *s, int base)
{
int val;
int ch;
for (val = 0; *s != '\0'; s++) {
switch (base) {
case 16:
if (*s >= '0' && *s <= '9') {
ch = *s - '0';
} else if (*s >= 'a' && *s <= 'f') {
ch = *s - 'a' + 10;
} else if (*s >= 'A' && *s <= 'F') {
ch = *s - 'A' + 10;
} else {
return (-1);
}
break;
case 10:
if (*s < '0' || *s > '9') {
return (-1);
}
ch = *s - '0';
break;
case 2:
if (*s < '0' || *s > '1') {
return (-1);
}
ch = *s - '0';
break;
case 8:
if (*s < '0' || *s > '7') {
return (-1);
}
ch = *s - '0';
break;
default:
return (-1);
}
val = (val * base) + ch;
}
return (val);
}
fc_remote_port_t *
fctl_create_remote_port(fc_local_port_t *port, la_wwn_t *node_wwn,
la_wwn_t *port_wwn, uint32_t d_id, uchar_t recepient, int sleep)
{
int invalid = 0;
fc_remote_node_t *rnodep;
fc_remote_port_t *pd;
rnodep = fctl_get_remote_node_by_nwwn(node_wwn);
if (rnodep) {
mutex_enter(&rnodep->fd_mutex);
invalid = (rnodep->fd_flags == FC_REMOTE_NODE_INVALID) ? 1 : 0;
mutex_exit(&rnodep->fd_mutex);
}
if (rnodep == NULL || invalid) {
rnodep = fctl_create_remote_node(node_wwn, sleep);
if (rnodep == NULL) {
return (NULL);
}
}
mutex_enter(&port->fp_mutex);
pd = fctl_get_remote_port_by_pwwn_mutex_held(port, port_wwn);
if (pd) {
mutex_enter(&pd->pd_mutex);
invalid = (pd->pd_state == PORT_DEVICE_INVALID) ? 1 : 0;
mutex_exit(&pd->pd_mutex);
}
if (pd == NULL || invalid) {
if ((pd = fctl_alloc_remote_port(port, port_wwn, d_id,
recepient, sleep)) == NULL) {
mutex_exit(&port->fp_mutex);
fctl_destroy_remote_node(rnodep);
return (pd);
}
mutex_enter(&pd->pd_mutex);
pd->pd_remote_nodep = rnodep;
fctl_enlist_did_table(port, pd);
fctl_enlist_pwwn_table(port, pd);
mutex_exit(&pd->pd_mutex);
mutex_exit(&port->fp_mutex);
rnodep = fctl_lock_remote_node_by_nwwn(node_wwn);
} else {
mutex_enter(&pd->pd_mutex);
ASSERT(pd->pd_remote_nodep != NULL);
if (pd->pd_port_id.port_id != d_id) {
char string[(FCTL_WWN_SIZE(port_wwn) << 1) + 1];
uint32_t old_id;
fc_wwn_to_str(port_wwn, string);
old_id = pd->pd_port_id.port_id;
fctl_delist_did_table(port, pd);
cmn_err(CE_NOTE, "!fctl(%d): D_ID of a device"
" with PWWN %s changed. New D_ID = %x,"
" OLD D_ID = %x", port->fp_instance, string,
d_id, old_id);
pd->pd_port_id.port_id = d_id;
pd->pd_type = PORT_DEVICE_CHANGED;
fctl_enlist_did_table(port, pd);
} else if (pd->pd_type == PORT_DEVICE_OLD) {
pd->pd_type = PORT_DEVICE_NOCHANGE;
pd->pd_state = PORT_DEVICE_VALID;
fctl_enlist_did_table(port, pd);
} else {
pd->pd_type = PORT_DEVICE_NOCHANGE;
pd->pd_state = PORT_DEVICE_VALID;
}
mutex_exit(&pd->pd_mutex);
mutex_exit(&port->fp_mutex);
if (rnodep != pd->pd_remote_nodep) {
if ((rnodep != NULL) &&
(fctl_wwn_cmp(&pd->pd_remote_nodep->fd_node_name,
node_wwn) != 0)) {
char ww1_name[17];
char ww2_name[17];
fc_wwn_to_str(
&pd->pd_remote_nodep->fd_node_name,
ww1_name);
fc_wwn_to_str(node_wwn, ww2_name);
cmn_err(CE_WARN, "fctl(%d) NWWN Mismatch: "
"Expected %s Got %s", port->fp_instance,
ww1_name, ww2_name);
}
return (NULL);
}
}
fctl_link_remote_port_to_remote_node(rnodep, pd);
return (pd);
}
int
fctl_destroy_remote_port(fc_local_port_t *port, fc_remote_port_t *pd)
{
fc_remote_node_t *rnodep;
int rcount = 0;
mutex_enter(&pd->pd_mutex);
if ((pd->pd_ref_count > 0) ||
(pd->pd_aux_flags & PD_GIVEN_TO_ULPS)) {
pd->pd_aux_flags |= PD_NEEDS_REMOVAL;
pd->pd_type = PORT_DEVICE_OLD;
mutex_exit(&pd->pd_mutex);
return (1);
}
pd->pd_type = PORT_DEVICE_OLD;
rnodep = pd->pd_remote_nodep;
mutex_exit(&pd->pd_mutex);
if (rnodep != NULL) {
rcount = fctl_unlink_remote_port_from_remote_node(rnodep, pd);
}
mutex_enter(&port->fp_mutex);
mutex_enter(&pd->pd_mutex);
fctl_delist_did_table(port, pd);
fctl_delist_pwwn_table(port, pd);
mutex_exit(&pd->pd_mutex);
fctl_dealloc_remote_port(pd);
mutex_exit(&port->fp_mutex);
return (rcount);
}
void
fctl_destroy_all_remote_ports(fc_local_port_t *port)
{
int index;
fc_remote_port_t *pd;
fc_remote_node_t *rnodep;
struct d_id_hash *head;
mutex_enter(&port->fp_mutex);
for (index = 0; index < did_table_size; index++) {
head = &port->fp_did_table[index];
while (head->d_id_head != NULL) {
pd = head->d_id_head;
mutex_enter(&pd->pd_mutex);
rnodep = pd->pd_remote_nodep;
mutex_exit(&pd->pd_mutex);
if (rnodep != NULL) {
if (fctl_unlink_remote_port_from_remote_node(
rnodep, pd) == 0) {
fctl_destroy_remote_node(rnodep);
}
}
mutex_enter(&pd->pd_mutex);
fctl_delist_pwwn_table(port, pd);
pd->pd_aux_flags &= ~PD_IN_DID_QUEUE;
mutex_exit(&pd->pd_mutex);
head->d_id_head = pd->pd_did_hnext;
fctl_dealloc_remote_port(pd);
}
}
mutex_exit(&port->fp_mutex);
}
int
fctl_is_wwn_zero(la_wwn_t *wwn)
{
int count;
for (count = 0; count < sizeof (la_wwn_t); count++) {
if (wwn->raw_wwn[count] != 0) {
return (FC_FAILURE);
}
}
return (FC_SUCCESS);
}
void
fctl_ulp_unsol_cb(fc_local_port_t *port, fc_unsol_buf_t *buf, uchar_t type)
{
int data_cb;
int check_type;
int rval;
uint32_t claimed;
fc_ulp_module_t *mod;
fc_ulp_ports_t *ulp_port;
claimed = 0;
check_type = 1;
switch ((buf->ub_frame.r_ctl) & R_CTL_ROUTING) {
case R_CTL_DEVICE_DATA:
data_cb = 1;
break;
case R_CTL_EXTENDED_SVC:
check_type = 0;
case R_CTL_FC4_SVC:
data_cb = 0;
break;
default:
mutex_enter(&port->fp_mutex);
ASSERT(port->fp_active_ubs > 0);
if (--(port->fp_active_ubs) == 0) {
port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
}
mutex_exit(&port->fp_mutex);
port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
1, &buf->ub_token);
return;
}
rw_enter(&fctl_ulp_lock, RW_READER);
for (mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
if (check_type && mod->mod_info->ulp_type != type) {
continue;
}
rw_enter(&fctl_mod_ports_lock, RW_READER);
ulp_port = fctl_get_ulp_port(mod, port);
rw_exit(&fctl_mod_ports_lock);
if (ulp_port == NULL) {
continue;
}
mutex_enter(&ulp_port->port_mutex);
if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate)) {
mutex_exit(&ulp_port->port_mutex);
continue;
}
mutex_exit(&ulp_port->port_mutex);
if (data_cb == 1) {
rval = mod->mod_info->ulp_data_callback(
mod->mod_info->ulp_handle,
(opaque_t)port, buf, claimed);
} else {
rval = mod->mod_info->ulp_els_callback(
mod->mod_info->ulp_handle,
(opaque_t)port, buf, claimed);
}
if (rval == FC_SUCCESS && claimed == 0) {
claimed = 1;
}
}
rw_exit(&fctl_ulp_lock);
if (claimed == 0) {
mutex_enter(&port->fp_mutex);
ASSERT(port->fp_active_ubs > 0);
if (--(port->fp_active_ubs) == 0) {
port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
}
mutex_exit(&port->fp_mutex);
port->fp_fca_tran->fca_ub_release(port->fp_fca_handle,
1, &buf->ub_token);
} else {
mutex_enter(&port->fp_mutex);
if (--port->fp_active_ubs == 0) {
port->fp_soft_state &= ~FP_SOFT_IN_UNSOL_CB;
}
mutex_exit(&port->fp_mutex);
}
}
void
fctl_copy_portmap_held(fc_portmap_t *map, fc_remote_port_t *pd)
{
fc_remote_node_t *node;
ASSERT(MUTEX_HELD(&pd->pd_mutex));
map->map_pwwn = pd->pd_port_name;
map->map_did = pd->pd_port_id;
map->map_hard_addr = pd->pd_hard_addr;
map->map_state = pd->pd_state;
map->map_type = pd->pd_type;
map->map_flags = 0;
ASSERT(map->map_type <= PORT_DEVICE_DELETE);
bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
node = pd->pd_remote_nodep;
ASSERT(MUTEX_HELD(&node->fd_mutex));
if (node) {
map->map_nwwn = node->fd_node_name;
}
map->map_pd = pd;
}
void
fctl_copy_portmap(fc_portmap_t *map, fc_remote_port_t *pd)
{
fc_remote_node_t *node;
ASSERT(!MUTEX_HELD(&pd->pd_mutex));
mutex_enter(&pd->pd_mutex);
map->map_pwwn = pd->pd_port_name;
map->map_did = pd->pd_port_id;
map->map_hard_addr = pd->pd_hard_addr;
map->map_state = pd->pd_state;
map->map_type = pd->pd_type;
map->map_flags = 0;
ASSERT(map->map_type <= PORT_DEVICE_DELETE);
bcopy(pd->pd_fc4types, map->map_fc4_types, sizeof (pd->pd_fc4types));
node = pd->pd_remote_nodep;
mutex_exit(&pd->pd_mutex);
if (node) {
mutex_enter(&node->fd_mutex);
map->map_nwwn = node->fd_node_name;
mutex_exit(&node->fd_mutex);
}
map->map_pd = pd;
}
static int
fctl_update_host_ns_values(fc_local_port_t *port, fc_ns_cmd_t *ns_req)
{
int rval = FC_SUCCESS;
switch (ns_req->ns_cmd) {
case NS_RFT_ID: {
int count;
uint32_t *src;
uint32_t *dst;
ns_rfc_type_t *rfc;
rfc = (ns_rfc_type_t *)ns_req->ns_req_payload;
mutex_enter(&port->fp_mutex);
src = (uint32_t *)port->fp_fc4_types;
dst = (uint32_t *)rfc->rfc_types;
for (count = 0; count < 8; count++) {
*src++ |= *dst++;
}
mutex_exit(&port->fp_mutex);
break;
}
case NS_RSPN_ID: {
ns_spn_t *spn;
spn = (ns_spn_t *)ns_req->ns_req_payload;
mutex_enter(&port->fp_mutex);
port->fp_sym_port_namelen = spn->spn_len;
if (spn->spn_len) {
bcopy((caddr_t)spn + sizeof (ns_spn_t),
port->fp_sym_port_name, spn->spn_len);
}
mutex_exit(&port->fp_mutex);
break;
}
case NS_RSNN_NN: {
ns_snn_t *snn;
snn = (ns_snn_t *)ns_req->ns_req_payload;
mutex_enter(&port->fp_mutex);
port->fp_sym_node_namelen = snn->snn_len;
if (snn->snn_len) {
bcopy((caddr_t)snn + sizeof (ns_snn_t),
port->fp_sym_node_name, snn->snn_len);
}
mutex_exit(&port->fp_mutex);
break;
}
case NS_RIP_NN: {
ns_rip_t *rip;
rip = (ns_rip_t *)ns_req->ns_req_payload;
mutex_enter(&port->fp_mutex);
bcopy(rip->rip_ip_addr, port->fp_ip_addr,
sizeof (rip->rip_ip_addr));
mutex_exit(&port->fp_mutex);
break;
}
case NS_RIPA_NN: {
ns_ipa_t *ipa;
ipa = (ns_ipa_t *)ns_req->ns_req_payload;
mutex_enter(&port->fp_mutex);
bcopy(ipa->ipa_value, port->fp_ipa, sizeof (ipa->ipa_value));
mutex_exit(&port->fp_mutex);
break;
}
default:
rval = FC_BADOBJECT;
break;
}
return (rval);
}
static int
fctl_retrieve_host_ns_values(fc_local_port_t *port, fc_ns_cmd_t *ns_req)
{
int rval = FC_SUCCESS;
switch (ns_req->ns_cmd) {
case NS_GFT_ID: {
ns_rfc_type_t *rfc;
rfc = (ns_rfc_type_t *)ns_req->ns_resp_payload;
mutex_enter(&port->fp_mutex);
bcopy(port->fp_fc4_types, rfc->rfc_types,
sizeof (rfc->rfc_types));
mutex_exit(&port->fp_mutex);
break;
}
case NS_GSPN_ID: {
ns_spn_t *spn;
spn = (ns_spn_t *)ns_req->ns_resp_payload;
mutex_enter(&port->fp_mutex);
spn->spn_len = port->fp_sym_port_namelen;
if (spn->spn_len) {
bcopy(port->fp_sym_port_name, (caddr_t)spn +
sizeof (ns_spn_t), spn->spn_len);
}
mutex_exit(&port->fp_mutex);
break;
}
case NS_GSNN_NN: {
ns_snn_t *snn;
snn = (ns_snn_t *)ns_req->ns_resp_payload;
mutex_enter(&port->fp_mutex);
snn->snn_len = port->fp_sym_node_namelen;
if (snn->snn_len) {
bcopy(port->fp_sym_node_name, (caddr_t)snn +
sizeof (ns_snn_t), snn->snn_len);
}
mutex_exit(&port->fp_mutex);
break;
}
case NS_GIP_NN: {
ns_rip_t *rip;
rip = (ns_rip_t *)ns_req->ns_resp_payload;
mutex_enter(&port->fp_mutex);
bcopy(port->fp_ip_addr, rip->rip_ip_addr,
sizeof (rip->rip_ip_addr));
mutex_exit(&port->fp_mutex);
break;
}
case NS_GIPA_NN: {
ns_ipa_t *ipa;
ipa = (ns_ipa_t *)ns_req->ns_resp_payload;
mutex_enter(&port->fp_mutex);
bcopy(port->fp_ipa, ipa->ipa_value, sizeof (ipa->ipa_value));
mutex_exit(&port->fp_mutex);
break;
}
default:
rval = FC_BADOBJECT;
break;
}
return (rval);
}
fctl_ns_req_t *
fctl_alloc_ns_cmd(uint32_t cmd_len, uint32_t resp_len, uint32_t data_len,
uint32_t ns_flags, int sleep)
{
fctl_ns_req_t *ns_cmd;
ns_cmd = kmem_zalloc(sizeof (*ns_cmd), sleep);
if (ns_cmd == NULL) {
return (NULL);
}
if (cmd_len) {
ns_cmd->ns_cmd_buf = kmem_zalloc(cmd_len, sleep);
if (ns_cmd->ns_cmd_buf == NULL) {
kmem_free(ns_cmd, sizeof (*ns_cmd));
return (NULL);
}
ns_cmd->ns_cmd_size = cmd_len;
}
ns_cmd->ns_resp_size = resp_len;
if (data_len) {
ns_cmd->ns_data_buf = kmem_zalloc(data_len, sleep);
if (ns_cmd->ns_data_buf == NULL) {
if (ns_cmd->ns_cmd_buf && cmd_len) {
kmem_free(ns_cmd->ns_cmd_buf, cmd_len);
}
kmem_free(ns_cmd, sizeof (*ns_cmd));
return (NULL);
}
ns_cmd->ns_data_len = data_len;
}
ns_cmd->ns_flags = ns_flags;
return (ns_cmd);
}
void
fctl_free_ns_cmd(fctl_ns_req_t *ns_cmd)
{
if (ns_cmd->ns_cmd_size && ns_cmd->ns_cmd_buf) {
kmem_free(ns_cmd->ns_cmd_buf, ns_cmd->ns_cmd_size);
}
if (ns_cmd->ns_data_len && ns_cmd->ns_data_buf) {
kmem_free(ns_cmd->ns_data_buf, ns_cmd->ns_data_len);
}
kmem_free(ns_cmd, sizeof (*ns_cmd));
}
int
fctl_ulp_port_ioctl(fc_local_port_t *port, dev_t dev, int cmd,
intptr_t data, int mode, cred_t *credp, int *rval)
{
int ret;
int save;
uint32_t claimed;
fc_ulp_module_t *mod;
fc_ulp_ports_t *ulp_port;
save = *rval;
*rval = ENOTTY;
rw_enter(&fctl_ulp_lock, RW_READER);
for (claimed = 0, mod = fctl_ulp_modules; mod; mod = mod->mod_next) {
rw_enter(&fctl_mod_ports_lock, RW_READER);
ulp_port = fctl_get_ulp_port(mod, port);
rw_exit(&fctl_mod_ports_lock);
if (ulp_port == NULL) {
continue;
}
mutex_enter(&ulp_port->port_mutex);
if (FCTL_DISALLOW_CALLBACKS(ulp_port->port_dstate) ||
mod->mod_info->ulp_port_ioctl == NULL) {
mutex_exit(&ulp_port->port_mutex);
continue;
}
mutex_exit(&ulp_port->port_mutex);
ret = mod->mod_info->ulp_port_ioctl(
mod->mod_info->ulp_handle, (opaque_t)port,
dev, cmd, data, mode, credp, rval, claimed);
if (ret == FC_SUCCESS && claimed == 0) {
claimed = 1;
}
}
rw_exit(&fctl_ulp_lock);
ret = *rval;
*rval = save;
return (ret);
}
int
fc_ulp_busy_port(opaque_t port_handle)
{
fc_local_port_t *port = port_handle;
return (fctl_busy_port(port));
}
void
fc_ulp_idle_port(opaque_t port_handle)
{
fc_local_port_t *port = port_handle;
fctl_idle_port(port);
}
void
fc_ulp_copy_portmap(fc_portmap_t *map, opaque_t pd)
{
fctl_copy_portmap(map, (fc_remote_port_t *)pd);
}
int
fc_ulp_get_npiv_port_num(opaque_t port_handle)
{
int portsnum = 0;
fc_local_port_t *port = port_handle;
fc_local_port_t *tmpport;
mutex_enter(&port->fp_mutex);
tmpport = port->fp_port_next;
if (!tmpport) {
mutex_exit(&port->fp_mutex);
return (portsnum);
}
while (tmpport != port) {
portsnum ++;
tmpport = tmpport->fp_port_next;
}
mutex_exit(&port->fp_mutex);
return (portsnum);
}
fc_local_port_t *
fc_get_npiv_port(fc_local_port_t *phyport, la_wwn_t *pwwn)
{
fc_fca_port_t *fca_port;
fc_local_port_t *tmpPort = phyport;
mutex_enter(&fctl_port_lock);
for (fca_port = fctl_fca_portlist; fca_port != NULL;
fca_port = fca_port->port_next) {
tmpPort = fca_port->port_handle;
if (tmpPort == NULL) {
continue;
}
mutex_enter(&tmpPort->fp_mutex);
if (bcmp(tmpPort->fp_service_params.nport_ww_name.raw_wwn,
pwwn->raw_wwn, sizeof (la_wwn_t)) == 0) {
mutex_exit(&tmpPort->fp_mutex);
mutex_exit(&fctl_port_lock);
return (tmpPort);
}
mutex_exit(&tmpPort->fp_mutex);
}
mutex_exit(&fctl_port_lock);
return (NULL);
}
int
fc_ulp_get_npiv_port_list(opaque_t port_handle, char *pathList)
{
int portsnum = 0;
fc_local_port_t *port = port_handle;
fc_local_port_t *tmpport;
mutex_enter(&port->fp_mutex);
tmpport = port->fp_port_next;
if (!tmpport || (port->fp_npiv_type == FC_NPIV_PORT)) {
mutex_exit(&port->fp_mutex);
return (portsnum);
}
while (tmpport != port) {
(void) ddi_pathname(tmpport->fp_port_dip,
&pathList[MAXPATHLEN * portsnum]);
portsnum ++;
tmpport = tmpport->fp_port_next;
}
mutex_exit(&port->fp_mutex);
return (portsnum);
}
fc_local_port_t *
fc_delete_npiv_port(fc_local_port_t *port, la_wwn_t *pwwn)
{
fc_local_port_t *tmpport;
mutex_enter(&port->fp_mutex);
tmpport = port->fp_port_next;
if (!tmpport || (port->fp_npiv_type == FC_NPIV_PORT)) {
mutex_exit(&port->fp_mutex);
return (NULL);
}
while (tmpport != port) {
if ((bcmp(tmpport->fp_service_params.nport_ww_name.raw_wwn,
pwwn->raw_wwn, sizeof (la_wwn_t)) == 0) &&
(tmpport->fp_npiv_state == 0)) {
tmpport->fp_npiv_state = FC_NPIV_DELETING;
mutex_exit(&port->fp_mutex);
return (tmpport);
}
tmpport = tmpport->fp_port_next;
}
mutex_exit(&port->fp_mutex);
return (NULL);
}
int
fc_ulp_get_adapter_paths(char *pathList, int count)
{
fc_fca_port_t *fca_port;
int in = 0, out = 0, check, skip, maxPorts = 0;
fc_local_port_t **portList;
fc_local_port_t *new_port, *stored_port;
fca_hba_fru_details_t *new_fru, *stored_fru;
ASSERT(pathList != NULL);
mutex_enter(&fctl_port_lock);
for (fca_port = fctl_fca_portlist; fca_port != NULL;
fca_port = fca_port->port_next) {
maxPorts ++;
}
if (maxPorts == 0) {
mutex_exit(&fctl_port_lock);
return (0);
}
portList = kmem_zalloc(sizeof (fc_local_port_t *) * maxPorts, KM_SLEEP);
for (fca_port = fctl_fca_portlist; fca_port != NULL;
fca_port = fca_port->port_next) {
skip = 0;
new_port = fca_port->port_handle;
mutex_enter(&new_port->fp_mutex);
new_fru = &new_port->fp_hba_port_attrs.hba_fru_details;
for (check = 0; check < out; check++) {
if (portList[check] == NULL) {
continue;
}
if (portList[check] == fca_port->port_handle) {
skip = 1;
break;
}
stored_port = portList[check];
mutex_enter(&stored_port->fp_mutex);
stored_fru =
&stored_port->fp_hba_port_attrs.hba_fru_details;
if (new_fru->high == stored_fru->high &&
new_fru->low == stored_fru->low) {
if (strncmp(
new_port->fp_hba_port_attrs.driver_name,
stored_port->fp_hba_port_attrs.driver_name,
FCHBA_DRIVER_NAME_LEN) == 0) {
skip = 1;
if (new_fru->port_index <
stored_fru->port_index) {
mutex_exit(
&stored_port->fp_mutex);
if (new_port->fp_npiv_type ==
FC_NPIV_PORT) {
break;
}
portList[check] = new_port;
break;
}
}
}
mutex_exit(&stored_port->fp_mutex);
}
mutex_exit(&new_port->fp_mutex);
if (!skip) {
if (fca_port->port_handle->fp_npiv_type ==
FC_NPIV_PORT) {
continue;
} else {
portList[out++] = fca_port->port_handle;
}
}
}
if (out <= count) {
for (in = 0; in < out; in++) {
(void) ddi_pathname(portList[in]->fp_port_dip,
&pathList[MAXPATHLEN * in]);
}
}
mutex_exit(&fctl_port_lock);
kmem_free(portList, sizeof (*portList) * maxPorts);
return (out);
}
uint32_t
fc_ulp_get_rscn_count(opaque_t port_handle)
{
uint32_t count;
fc_local_port_t *port;
port = (fc_local_port_t *)port_handle;
mutex_enter(&port->fp_mutex);
count = port->fp_rscn_count;
mutex_exit(&port->fp_mutex);
return (count);
}
int
fctl_add_orphan_held(fc_local_port_t *port, fc_remote_port_t *pd)
{
int rval = FC_FAILURE;
la_wwn_t pwwn;
fc_orphan_t *orp;
fc_orphan_t *orphan;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(MUTEX_HELD(&pd->pd_mutex));
pwwn = pd->pd_port_name;
for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
return (FC_SUCCESS);
}
}
orphan = kmem_zalloc(sizeof (*orphan), KM_NOSLEEP);
if (orphan) {
orphan->orp_pwwn = pwwn;
orphan->orp_tstamp = ddi_get_lbolt();
if (port->fp_orphan_list) {
ASSERT(port->fp_orphan_count > 0);
orphan->orp_next = port->fp_orphan_list;
}
port->fp_orphan_list = orphan;
port->fp_orphan_count++;
rval = FC_SUCCESS;
}
return (rval);
}
int
fctl_add_orphan(fc_local_port_t *port, fc_remote_port_t *pd, int sleep)
{
int rval = FC_FAILURE;
la_wwn_t pwwn;
fc_orphan_t *orp;
fc_orphan_t *orphan;
mutex_enter(&port->fp_mutex);
mutex_enter(&pd->pd_mutex);
pwwn = pd->pd_port_name;
mutex_exit(&pd->pd_mutex);
for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
mutex_exit(&port->fp_mutex);
return (FC_SUCCESS);
}
}
mutex_exit(&port->fp_mutex);
orphan = kmem_zalloc(sizeof (*orphan), sleep);
if (orphan != NULL) {
mutex_enter(&port->fp_mutex);
orphan->orp_pwwn = pwwn;
orphan->orp_tstamp = ddi_get_lbolt();
if (port->fp_orphan_list) {
ASSERT(port->fp_orphan_count > 0);
orphan->orp_next = port->fp_orphan_list;
}
port->fp_orphan_list = orphan;
port->fp_orphan_count++;
mutex_exit(&port->fp_mutex);
rval = FC_SUCCESS;
}
return (rval);
}
int
fctl_remove_if_orphan(fc_local_port_t *port, la_wwn_t *pwwn)
{
int rval = FC_FAILURE;
fc_orphan_t *prev = NULL;
fc_orphan_t *orp;
mutex_enter(&port->fp_mutex);
for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
if (fctl_wwn_cmp(&orp->orp_pwwn, pwwn) == 0) {
if (prev) {
prev->orp_next = orp->orp_next;
} else {
ASSERT(port->fp_orphan_list == orp);
port->fp_orphan_list = orp->orp_next;
}
port->fp_orphan_count--;
rval = FC_SUCCESS;
break;
}
prev = orp;
}
mutex_exit(&port->fp_mutex);
if (rval == FC_SUCCESS) {
kmem_free(orp, sizeof (*orp));
}
return (rval);
}
static void
fctl_print_if_not_orphan(fc_local_port_t *port, fc_remote_port_t *pd)
{
char ww_name[17];
la_wwn_t pwwn;
fc_orphan_t *orp;
mutex_enter(&port->fp_mutex);
mutex_enter(&pd->pd_mutex);
pwwn = pd->pd_port_name;
mutex_exit(&pd->pd_mutex);
for (orp = port->fp_orphan_list; orp != NULL; orp = orp->orp_next) {
if (fctl_wwn_cmp(&orp->orp_pwwn, &pwwn) == 0) {
mutex_exit(&port->fp_mutex);
return;
}
}
mutex_exit(&port->fp_mutex);
fc_wwn_to_str(&pwwn, ww_name);
cmn_err(CE_WARN, "!fctl(%d): N_x Port with D_ID=%x, PWWN=%s"
" disappeared from fabric", port->fp_instance,
pd->pd_port_id.port_id, ww_name);
}
static void
fctl_link_reset_done(opaque_t port_handle, uchar_t result)
{
fc_local_port_t *port = port_handle;
mutex_enter(&port->fp_mutex);
port->fp_soft_state &= ~FP_SOFT_IN_LINK_RESET;
mutex_exit(&port->fp_mutex);
fctl_idle_port(port);
}
static int
fctl_error(int fc_errno, char **errmsg)
{
int count;
for (count = 0; count < sizeof (fc_errlist) /
sizeof (fc_errlist[0]); count++) {
if (fc_errlist[count].fc_errno == fc_errno) {
*errmsg = fc_errlist[count].fc_errname;
return (FC_SUCCESS);
}
}
*errmsg = fctl_undefined;
return (FC_FAILURE);
}
static int
fctl_pkt_error(fc_packet_t *pkt, char **state, char **reason,
char **action, char **expln)
{
int ret;
int len;
int index;
fc_pkt_error_t *error;
fc_pkt_reason_t *reason_b;
fc_pkt_action_t *action_b;
fc_pkt_expln_t *expln_b;
ret = 0;
*state = *reason = *action = *expln = fctl_undefined;
len = sizeof (fc_pkt_errlist) / sizeof fc_pkt_errlist[0];
for (index = 0; index < len; index++) {
error = fc_pkt_errlist + index;
if (pkt->pkt_state == error->pkt_state) {
*state = error->pkt_msg;
ret++;
reason_b = error->pkt_reason;
action_b = error->pkt_action;
expln_b = error->pkt_expln;
while (reason_b != NULL &&
reason_b->reason_val != FC_REASON_INVALID) {
if (reason_b->reason_val == pkt->pkt_reason) {
*reason = reason_b->reason_msg;
ret++;
break;
}
reason_b++;
}
while (action_b != NULL &&
action_b->action_val != FC_ACTION_INVALID) {
if (action_b->action_val == pkt->pkt_action) {
*action = action_b->action_msg;
ret++;
break;
}
action_b++;
}
while (expln_b != NULL &&
expln_b->expln_val != FC_EXPLN_INVALID) {
if (expln_b->expln_val == pkt->pkt_expln) {
*expln = expln_b->expln_msg;
ret++;
break;
}
expln_b++;
}
break;
}
}
return (ret);
}
void
fctl_remove_oldies(fc_local_port_t *port)
{
int index;
int initiator;
fc_remote_node_t *node;
struct pwwn_hash *head;
fc_remote_port_t *pd;
fc_remote_port_t *old_pd;
fc_remote_port_t *last_pd;
mutex_enter(&port->fp_mutex);
for (index = 0; index < pwwn_table_size; index++) {
head = &port->fp_pwwn_table[index];
last_pd = NULL;
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (pd->pd_type != PORT_DEVICE_OLD) {
mutex_exit(&pd->pd_mutex);
last_pd = pd;
pd = pd->pd_wwn_hnext;
continue;
}
old_pd = pd;
pd = old_pd->pd_wwn_hnext;
if (last_pd == NULL) {
ASSERT(old_pd == head->pwwn_head);
head->pwwn_head = pd;
} else {
last_pd->pd_wwn_hnext = pd;
}
head->pwwn_count--;
port->fp_dev_count--;
old_pd->pd_wwn_hnext = NULL;
fctl_delist_did_table(port, old_pd);
node = old_pd->pd_remote_nodep;
ASSERT(node != NULL);
initiator = (old_pd->pd_recepient ==
PD_PLOGI_INITIATOR) ? 1 : 0;
mutex_exit(&old_pd->pd_mutex);
if (FC_IS_TOP_SWITCH(port->fp_topology) && initiator) {
mutex_exit(&port->fp_mutex);
(void) fctl_add_orphan(port, old_pd,
KM_NOSLEEP);
} else {
mutex_exit(&port->fp_mutex);
}
if (fctl_destroy_remote_port(port, old_pd) == 0) {
if (node) {
fctl_destroy_remote_node(node);
}
}
mutex_enter(&port->fp_mutex);
}
}
mutex_exit(&port->fp_mutex);
}
static void
fctl_check_alpa_list(fc_local_port_t *port, fc_remote_port_t *pd)
{
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(port->fp_topology == FC_TOP_PRIVATE_LOOP);
if (fctl_is_alpa_present(port, pd->pd_port_id.port_id) == FC_SUCCESS) {
return;
}
cmn_err(CE_WARN, "!fctl(%d): AL_PA=0x%x doesn't exist in LILP map",
port->fp_instance, pd->pd_port_id.port_id);
}
static int
fctl_is_alpa_present(fc_local_port_t *port, uchar_t alpa)
{
int index;
ASSERT(MUTEX_HELD(&port->fp_mutex));
ASSERT(port->fp_topology == FC_TOP_PRIVATE_LOOP);
for (index = 0; index < port->fp_lilp_map.lilp_length; index++) {
if (port->fp_lilp_map.lilp_alpalist[index] == alpa) {
return (FC_SUCCESS);
}
}
return (FC_FAILURE);
}
fc_remote_port_t *
fctl_lookup_pd_by_did(fc_local_port_t *port, uint32_t d_id)
{
int index;
struct pwwn_hash *head;
fc_remote_port_t *pd;
ASSERT(MUTEX_HELD(&port->fp_mutex));
for (index = 0; index < pwwn_table_size; index++) {
head = &port->fp_pwwn_table[index];
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (pd->pd_port_id.port_id == d_id) {
mutex_exit(&pd->pd_mutex);
return (pd);
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_wwn_hnext;
}
}
return (pd);
}
void
fc_trace_debug(fc_trace_logq_t *logq, caddr_t name, int dflag, int dlevel,
int errno, const char *fmt, ...)
{
char buf[FC_MAX_TRACE_BUF_LEN + 3];
char *bufptr = buf;
va_list ap;
int cnt = 0;
if ((dlevel & dflag) == 0) {
return;
}
if (name) {
cnt = snprintf(buf, FC_MAX_TRACE_BUF_LEN + 1, "%d=>%s::",
logq->il_id++, name);
} else {
cnt = snprintf(buf, FC_MAX_TRACE_BUF_LEN + 1, "%d=>trace::",
logq->il_id++);
}
if (cnt < FC_MAX_TRACE_BUF_LEN) {
va_start(ap, fmt);
cnt += vsnprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 1 - cnt,
fmt, ap);
va_end(ap);
}
if (cnt > FC_MAX_TRACE_BUF_LEN) {
cnt = FC_MAX_TRACE_BUF_LEN;
}
if (errno && (cnt < FC_MAX_TRACE_BUF_LEN)) {
cnt += snprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 1 - cnt,
"error=0x%x\n", errno);
}
(void) snprintf(buf + cnt, FC_MAX_TRACE_BUF_LEN + 3 - cnt, "\n");
if (logq && (dlevel & FC_TRACE_LOG_BUF) != 0) {
fc_trace_logmsg(logq, buf, dlevel);
}
if ((bufptr = strchr(buf, '>')) == NULL) {
bufptr = buf;
} else {
bufptr++;
}
switch (dlevel & FC_TRACE_LOG_MASK) {
case FC_TRACE_LOG_CONSOLE:
cmn_err(CE_WARN, "%s", bufptr);
break;
case FC_TRACE_LOG_CONSOLE_MSG:
cmn_err(CE_WARN, "%s", bufptr);
break;
case FC_TRACE_LOG_MSG:
cmn_err(CE_WARN, "!%s", bufptr);
break;
default:
break;
}
}
fc_trace_logq_t *
fc_trace_alloc_logq(int maxsize)
{
fc_trace_logq_t *logq;
logq = kmem_zalloc(sizeof (*logq), KM_SLEEP);
mutex_init(&logq->il_lock, NULL, MUTEX_DRIVER, NULL);
logq->il_hiwat = maxsize;
logq->il_flags |= FC_TRACE_LOGQ_V2;
return (logq);
}
void
fc_trace_free_logq(fc_trace_logq_t *logq)
{
mutex_enter(&logq->il_lock);
while (logq->il_msgh) {
fc_trace_freemsg(logq);
}
mutex_exit(&logq->il_lock);
mutex_destroy(&logq->il_lock);
kmem_free(logq, sizeof (*logq));
}
void
fc_trace_logmsg(fc_trace_logq_t *logq, caddr_t buf, int level)
{
int qfull = 0;
fc_trace_dmsg_t *dmsg;
dmsg = kmem_alloc(sizeof (*dmsg), KM_NOSLEEP);
if (dmsg == NULL) {
mutex_enter(&logq->il_lock);
logq->il_afail++;
mutex_exit(&logq->il_lock);
return;
}
gethrestime(&dmsg->id_time);
dmsg->id_size = strlen(buf) + 1;
dmsg->id_buf = kmem_alloc(dmsg->id_size, KM_NOSLEEP);
if (dmsg->id_buf == NULL) {
kmem_free(dmsg, sizeof (*dmsg));
mutex_enter(&logq->il_lock);
logq->il_afail++;
mutex_exit(&logq->il_lock);
return;
}
bcopy(buf, dmsg->id_buf, strlen(buf));
dmsg->id_buf[strlen(buf)] = '\0';
mutex_enter(&logq->il_lock);
logq->il_size += dmsg->id_size;
if (logq->il_size >= logq->il_hiwat) {
qfull = 1;
}
if (qfull) {
fc_trace_freemsg(logq);
}
dmsg->id_next = NULL;
if (logq->il_msgt) {
logq->il_msgt->id_next = dmsg;
} else {
ASSERT(logq->il_msgh == NULL);
logq->il_msgh = dmsg;
}
logq->il_msgt = dmsg;
mutex_exit(&logq->il_lock);
}
static void
fc_trace_freemsg(fc_trace_logq_t *logq)
{
fc_trace_dmsg_t *dmsg;
ASSERT(MUTEX_HELD(&logq->il_lock));
if ((dmsg = logq->il_msgh) != NULL) {
logq->il_msgh = dmsg->id_next;
if (logq->il_msgh == NULL) {
logq->il_msgt = NULL;
}
logq->il_size -= dmsg->id_size;
kmem_free(dmsg->id_buf, dmsg->id_size);
kmem_free(dmsg, sizeof (*dmsg));
} else {
ASSERT(logq->il_msgt == NULL);
}
}
fc_remote_port_t *
fctl_lookup_pd_by_index(fc_local_port_t *port, uint32_t index)
{
int outer;
int match = 0;
struct pwwn_hash *head;
fc_remote_port_t *pd;
ASSERT(MUTEX_HELD(&port->fp_mutex));
for (outer = 0;
outer < pwwn_table_size && match <= index;
outer++) {
head = &port->fp_pwwn_table[outer];
pd = head->pwwn_head;
if (pd != NULL) match ++;
while (pd != NULL && match <= index) {
pd = pd->pd_wwn_hnext;
if (pd != NULL) match ++;
}
}
return (pd);
}
fc_remote_port_t *
fctl_lookup_pd_by_wwn(fc_local_port_t *port, la_wwn_t wwn)
{
int index;
struct pwwn_hash *head;
fc_remote_port_t *pd;
ASSERT(MUTEX_HELD(&port->fp_mutex));
for (index = 0; index < pwwn_table_size; index++) {
head = &port->fp_pwwn_table[index];
pd = head->pwwn_head;
while (pd != NULL) {
mutex_enter(&pd->pd_mutex);
if (bcmp(pd->pd_port_name.raw_wwn, wwn.raw_wwn,
sizeof (la_wwn_t)) == 0) {
mutex_exit(&pd->pd_mutex);
return (pd);
}
if (bcmp(pd->pd_remote_nodep->fd_node_name.raw_wwn,
wwn.raw_wwn, sizeof (la_wwn_t)) == 0) {
mutex_exit(&pd->pd_mutex);
return (pd);
}
mutex_exit(&pd->pd_mutex);
pd = pd->pd_wwn_hnext;
}
}
return (NULL);
}
int
fctl_count_fru_ports(fc_local_port_t *port, int npivflag)
{
fca_hba_fru_details_t *fru;
fc_fca_port_t *fca_port;
fc_local_port_t *tmpPort = NULL;
uint32_t count = 1;
mutex_enter(&fctl_port_lock);
mutex_enter(&port->fp_mutex);
fru = &port->fp_hba_port_attrs.hba_fru_details;
if (fru->high == 0 && fru->low == 0 && fru->port_index == 0) {
mutex_exit(&port->fp_mutex);
mutex_exit(&fctl_port_lock);
return (1);
}
for (fca_port = fctl_fca_portlist; fca_port != NULL;
fca_port = fca_port->port_next) {
tmpPort = fca_port->port_handle;
if (tmpPort == port) {
continue;
}
mutex_enter(&tmpPort->fp_mutex);
if (tmpPort->fp_hba_port_attrs.hba_fru_details.high ==
fru->high &&
tmpPort->fp_hba_port_attrs.hba_fru_details.low ==
fru->low) {
if (strncmp(port->fp_hba_port_attrs.driver_name,
tmpPort->fp_hba_port_attrs.driver_name,
FCHBA_DRIVER_NAME_LEN) == 0) {
if (!npivflag ||
(tmpPort->fp_npiv_type != FC_NPIV_PORT)) {
count++;
}
}
}
mutex_exit(&tmpPort->fp_mutex);
}
mutex_exit(&port->fp_mutex);
mutex_exit(&fctl_port_lock);
return (count);
}
fc_fca_port_t *
fctl_local_port_list_add(fc_fca_port_t *list, fc_local_port_t *port)
{
fc_fca_port_t *tmp = list, *newentry = NULL;
newentry = kmem_zalloc(sizeof (fc_fca_port_t), KM_NOSLEEP);
if (newentry == NULL) {
return (list);
}
newentry->port_handle = port;
if (tmp == NULL) {
return (newentry);
}
while (tmp->port_next != NULL) tmp = tmp->port_next;
tmp->port_next = newentry;
return (list);
}
void
fctl_local_port_list_free(fc_fca_port_t *list)
{
fc_fca_port_t *tmp = list, *nextentry;
if (tmp == NULL) {
return;
}
while (tmp != NULL) {
nextentry = tmp->port_next;
kmem_free(tmp, sizeof (*tmp));
tmp = nextentry;
}
}
fc_local_port_t *
fctl_get_adapter_port_by_index(fc_local_port_t *port, uint32_t port_index)
{
fca_hba_fru_details_t *fru;
fc_fca_port_t *fca_port;
fc_local_port_t *tmpPort = NULL;
fc_fca_port_t *list = NULL, *tmpEntry;
fc_local_port_t *phyPort, *virPort = NULL;
int index, phyPortNum = 0;
mutex_enter(&fctl_port_lock);
mutex_enter(&port->fp_mutex);
fru = &port->fp_hba_port_attrs.hba_fru_details;
if (fru->port_index == port_index) {
mutex_exit(&port->fp_mutex);
mutex_exit(&fctl_port_lock);
return (port);
}
if (fru->high == 0 && fru->low == 0 && fru->port_index == 0) {
mutex_exit(&port->fp_mutex);
mutex_exit(&fctl_port_lock);
return (NULL);
}
list = fctl_local_port_list_add(list, port);
phyPortNum++;
for (fca_port = fctl_fca_portlist; fca_port != NULL;
fca_port = fca_port->port_next) {
tmpPort = fca_port->port_handle;
if (tmpPort == port) {
continue;
}
mutex_enter(&tmpPort->fp_mutex);
if (tmpPort->fp_hba_port_attrs.hba_fru_details.high ==
fru->high &&
tmpPort->fp_hba_port_attrs.hba_fru_details.low ==
fru->low) {
if (strncmp(port->fp_hba_port_attrs.driver_name,
tmpPort->fp_hba_port_attrs.driver_name,
FCHBA_DRIVER_NAME_LEN) == 0) {
fru =
&tmpPort->fp_hba_port_attrs.hba_fru_details;
if ((tmpPort->fp_npiv_type != FC_NPIV_PORT) &&
(fru->port_index == port_index)) {
mutex_exit(&tmpPort->fp_mutex);
mutex_exit(&port->fp_mutex);
mutex_exit(&fctl_port_lock);
fctl_local_port_list_free(list);
return (tmpPort);
}
if (tmpPort->fp_npiv_type != FC_NPIV_PORT) {
(void) fctl_local_port_list_add(list,
tmpPort);
phyPortNum++;
}
}
}
mutex_exit(&tmpPort->fp_mutex);
}
tmpEntry = list;
index = phyPortNum - 1;
virPort = NULL;
while (index < port_index) {
if (tmpEntry == NULL) {
break;
}
if (virPort == NULL) {
phyPort = tmpEntry->port_handle;
virPort = phyPort->fp_port_next;
if (virPort == NULL) {
tmpEntry = tmpEntry->port_next;
continue;
}
} else {
virPort = virPort->fp_port_next;
}
if (virPort == phyPort) {
tmpEntry = tmpEntry->port_next;
virPort = NULL;
} else {
index++;
}
}
mutex_exit(&port->fp_mutex);
mutex_exit(&fctl_port_lock);
fctl_local_port_list_free(list);
if (virPort) {
return (virPort);
}
return (NULL);
}
int
fctl_busy_port(fc_local_port_t *port)
{
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
if (port->fp_soft_state & FP_SOFT_NO_PMCOMP) {
port->fp_pm_busy_nocomp++;
mutex_exit(&port->fp_mutex);
return (0);
}
port->fp_pm_busy++;
mutex_exit(&port->fp_mutex);
if (pm_busy_component(port->fp_port_dip,
FP_PM_COMPONENT) != DDI_SUCCESS) {
mutex_enter(&port->fp_mutex);
port->fp_pm_busy--;
mutex_exit(&port->fp_mutex);
return (ENXIO);
}
mutex_enter(&port->fp_mutex);
if (port->fp_pm_level == FP_PM_PORT_DOWN) {
mutex_exit(&port->fp_mutex);
if (pm_raise_power(port->fp_port_dip, FP_PM_COMPONENT,
FP_PM_PORT_UP) != DDI_SUCCESS) {
mutex_enter(&port->fp_mutex);
port->fp_pm_busy--;
mutex_exit(&port->fp_mutex);
(void) pm_idle_component(port->fp_port_dip,
FP_PM_COMPONENT);
return (EIO);
}
return (0);
}
mutex_exit(&port->fp_mutex);
return (0);
}
void
fctl_idle_port(fc_local_port_t *port)
{
ASSERT(!MUTEX_HELD(&port->fp_mutex));
mutex_enter(&port->fp_mutex);
if (port->fp_pm_busy_nocomp > 0) {
port->fp_pm_busy_nocomp--;
mutex_exit(&port->fp_mutex);
return;
}
port->fp_pm_busy--;
mutex_exit(&port->fp_mutex);
(void) pm_idle_component(port->fp_port_dip, FP_PM_COMPONENT);
}
static void
fctl_tc_timer(void *arg)
{
timed_counter_t *tc = (timed_counter_t *)arg;
ASSERT(tc != NULL);
ASSERT(tc->sig == tc);
mutex_enter(&tc->mutex);
if (tc->active) {
tc->active = B_FALSE;
tc->counter = 0;
}
mutex_exit(&tc->mutex);
}
void
fctl_tc_constructor(timed_counter_t *tc, uint32_t max_value, clock_t timer)
{
ASSERT(tc != NULL);
ASSERT(tc->sig != tc);
bzero(tc, sizeof (*tc));
mutex_init(&tc->mutex, NULL, MUTEX_DRIVER, NULL);
tc->timer = drv_usectohz(timer);
tc->active = B_FALSE;
tc->maxed_out = B_FALSE;
tc->max_value = max_value;
tc->sig = tc;
}
void
fctl_tc_destructor(timed_counter_t *tc)
{
ASSERT(tc != NULL);
ASSERT(tc->sig == tc);
ASSERT(!mutex_owned(&tc->mutex));
mutex_enter(&tc->mutex);
if (tc->active) {
tc->active = B_FALSE;
mutex_exit(&tc->mutex);
(void) untimeout(tc->tid);
mutex_enter(&tc->mutex);
tc->sig = NULL;
}
mutex_exit(&tc->mutex);
mutex_destroy(&tc->mutex);
}
boolean_t
fctl_tc_increment(timed_counter_t *tc)
{
ASSERT(tc != NULL);
ASSERT(tc->sig == tc);
mutex_enter(&tc->mutex);
if (!tc->maxed_out) {
++tc->counter;
if (tc->counter >= tc->max_value) {
tc->maxed_out = B_TRUE;
}
if (!tc->active) {
tc->tid = timeout(fctl_tc_timer, tc, tc->timer);
tc->active = B_TRUE;
}
}
mutex_exit(&tc->mutex);
return (tc->maxed_out);
}
void
fctl_tc_reset(timed_counter_t *tc)
{
ASSERT(tc != NULL);
ASSERT(tc->sig == tc);
mutex_enter(&tc->mutex);
tc->counter = 0;
tc->maxed_out = B_FALSE;
if (tc->active) {
tc->active = B_FALSE;
(void) untimeout(tc->tid);
}
mutex_exit(&tc->mutex);
}
void
fc_ulp_log_device_event(opaque_t port_handle, int type)
{
fc_local_port_t *port = port_handle;
nvlist_t *attr_list;
if (nvlist_alloc(&attr_list, NV_UNIQUE_NAME_TYPE,
KM_SLEEP) != DDI_SUCCESS) {
return;
}
if (nvlist_add_uint32(attr_list, "instance",
port->fp_instance) != DDI_SUCCESS) {
goto error;
}
if (nvlist_add_byte_array(attr_list, "port-wwn",
port->fp_service_params.nport_ww_name.raw_wwn,
sizeof (la_wwn_t)) != DDI_SUCCESS) {
goto error;
}
(void) ddi_log_sysevent(port->fp_port_dip, DDI_VENDOR_SUNW, EC_SUNFC,
(type == FC_ULP_DEVICE_ONLINE) ?
ESC_SUNFC_DEVICE_ONLINE : ESC_SUNFC_DEVICE_OFFLINE,
attr_list, NULL, DDI_SLEEP);
nvlist_free(attr_list);
return;
error:
nvlist_free(attr_list);
}