fctl.c revision fcf3ce441efd61da9bb2884968af01cb7c1452cc
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Fibre channel Transport Library (fctl)
*
* Function naming conventions:
* Functions called from ULPs begin with fc_ulp_
* Functions called from FCAs begin with fc_fca_
* Internal functions begin with fctl_
*
* Fibre channel packet layout:
* +---------------------+<--------+
* | | |
* | ULP Packet private | |
* | | |
* +---------------------+ |
* | |---------+
* | struct fc_packet |---------+
* | | |
* +---------------------+<--------+
* | |
* | FCA Packet private |
* | |
* +---------------------+
*
* So you loved the ascii art ? It's strongly desirable to cache
* allocate the entire packet in one common place. So we define a set a
* of rules. In a contiguous block of memory, the top portion of the
* block points to ulp packet private area, next follows the fc_packet
* structure used extensively by all the consumers and what follows this
* is the FCA packet private. Note that given a packet structure, it is
* possible to get to the ULP and FCA Packet private fields using
* ulp_private and fca_private fields (which hold pointers) respectively.
*
* It should be noted with a grain of salt that ULP Packet private size
* varies between two different ULP types, So this poses a challenge to
* compute the correct size of the whole block on a per port basis. The
* transport layer doesn't have a problem in dealing with FCA packet
* private sizes as it is the sole manager of ports underneath. Since
* it's not a good idea to cache allocate different sizes of memory for
* different ULPs and have the ability to choose from one of these caches
* based on ULP type during every packet allocation, the transport some
* what wisely (?) hands off this job of cache allocation to the ULPs
* themselves.
*
* That means FCAs need to make their packet private size known to the
* transport to pass it up to the ULPs. This is done during
* fc_fca_attach(). And the transport passes this size up to ULPs during
* fc_ulp_port_attach() of each ULP.
*
* This leaves us with another possible question; How are packets
* allocated for ELS's started by the transport itself ? Well, the port
* driver during attach time, cache allocates on a per port basis to
* handle ELSs too.
*/
#include <sys/byteorder.h>
/* These are referenced by fp.c! */
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";
/*
* This lock protects the fc_ulp_module_t linked list (i.e. mod_next field)
*/
static krwlock_t fctl_ulp_lock;
/*
* The fctl_mod_ports_lock protects the mod_ports element in the
* fc_ulp_ports_t structure
*/
static krwlock_t fctl_mod_ports_lock;
/*
* fctl_port_lock protects the linked list of local port structures
* (fctl_fca_portlist). When walking the list, this lock must be obtained
* prior to any local port locks.
*/
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;
#if !defined(lint)
#endif /* lint */
#define FCTL_VERSION "1.69"
char *fctl_version = FCTL_NAME_VERSION;
extern struct mod_ops mod_miscops;
&mod_miscops, /* type of module */
FCTL_NAME_VERSION /* Module name */
};
static struct modlinkage modlinkage = {
};
static struct bus_ops fctl_fca_busops = {
nullbusmap, /* bus_map */
NULL, /* bus_get_intrspec */
NULL, /* bus_add_intrspec */
NULL, /* bus_remove_intrspec */
i_ddi_map_fault, /* bus_map_fault */
ddi_dma_map, /* bus_dma_map */
ddi_dma_allochdl, /* bus_dma_allochdl */
ddi_dma_freehdl, /* bus_dma_freehdl */
ddi_dma_bindhdl, /* bus_dma_bindhdl */
ddi_dma_unbindhdl, /* bus_unbindhdl */
ddi_dma_flush, /* bus_dma_flush */
ddi_dma_win, /* bus_dma_win */
ddi_dma_mctl, /* bus_dma_ctl */
fctl_fca_bus_ctl, /* bus_ctl */
ddi_bus_prop_op, /* bus_prop_op */
NULL, /* bus_get_eventcookie */
NULL, /* bus_add_eventcall */
NULL, /* bus_remove_event */
NULL, /* bus_post_event */
NULL, /* bus_intr_ctl */
NULL, /* bus_config */
NULL, /* bus_unconfig */
NULL, /* bus_fm_init */
NULL, /* bus_fm_fini */
NULL, /* bus_fm_access_enter */
NULL, /* bus_fm_access_exit */
NULL, /* bus_power */
};
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_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", },
"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_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_ACTION_SEQ_TERM_RETRY, "Retry terminated Sequence" },
{ FC_ACTION_SEQ_ACTIVE_RETRY, "Retry Active Sequence" },
{ FC_REASON_INVALID, NULL }
};
{ 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[] = {
{
"Operation Success",
NULL,
NULL,
},
"Remote Stop",
NULL,
},
{
"Local Reject",
},
{
"N_Port Reject",
},
{
"Fabric Reject",
},
{
"Local Busy",
NULL,
NULL,
},
{
"Transport Busy",
NULL,
NULL,
},
{
"N_Port Busy",
},
{
"Fabric Busy",
NULL,
NULL,
},
{
"Link Service Reject",
NULL,
NULL,
},
{
"Basic Reject",
NULL,
},
{
"Timeout",
},
{
"Fabric Switch Reject",
NULL,
},
{
"Packet Transport error",
NULL,
},
{
"Packet Failure",
NULL,
},
{
"Port Offline",
NULL,
NULL,
},
{
"ELS is in Progress",
NULL,
NULL,
}
};
int
_init()
{
int rval;
if (fctl_job_cache == NULL) {
sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
return (ENOMEM);
}
sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
}
return (rval);
}
/*
* The mod_uninstall code doesn't call _fini when
* there is living dependent module on fctl. So
* there is no need to be extra careful here ?
*/
int
_fini()
{
int rval;
return (rval);
}
sizeof (*fctl_nwwn_hash_table) * fctl_nwwn_table_size);
return (rval);
}
int
{
}
/* ARGSUSED */
static int
{
return (0);
}
/* ARGSUSED */
static void
{
}
/*
* fc_ulp_add:
* Add a ULP module
*
* Return Codes:
* FC_ULP_SAMEMODULE
* FC_SUCCESS
* FC_FAILURE
*
* fc_ulp_add prints a warning message if there is already a
* similar ULP type attached and this is unlikely to change as
* we trudge along. Further, this function returns a failure
* code if the same module attempts to add more than once for
* the same FC-4 type.
*/
int
{
int ntry = 0;
/*
* Make sure ulp_rev matches fctl version.
* Whenever non-private data structure or non-static interface changes,
* we should use an increased FCTL_ULP_MODREV_# number here and in all
* ulps to prevent version mismatch.
*/
return (FC_BADULP);
}
if (fctl_ulp_list != NULL) {
}
fctl_ulp_list = new;
if (ntry++ > FC_ULP_ADD_RETRY_COUNT) {
break;
}
}
if (list) {
if (last) {
} else {
}
}
return (FC_FAILURE);
}
}
return (FC_ULP_SAMEMODULE);
}
}
}
if (prev) {
} else {
}
/*
* Schedule a job to each port's job_handler
* thread to attach their ports with this ULP.
*/
}
return (FC_SUCCESS);
}
/*
* fc_ulp_remove
* Remove a ULP module
*
* Currently there is no mechanism to detect it to fail such a request.
*
* Return Codes:
* FC_SUCCESS
* FC_FAILURE
*/
int
{
break;
}
}
if (list) {
if (last) {
} else {
}
}
break;
}
}
if (mod) {
if (prev) {
} else {
}
}
return (FC_SUCCESS);
}
return (FC_FAILURE);
}
/*
* The callers typically cache allocate the packet, complete the
* DMA setup for pkt_cmd and pkt_resp fields of the packet and
* call this function to see if the FCA is interested in doing
* its own intialization. For example, socal may like to initialize
* the soc_hdr which is pointed to by the pkt_fca_private field
* and sitting right below fc_packet_t in memory.
*
* The caller is required to ensure that pkt_pd is populated with the
* handle that it was given when the transport notified it about the
* device this packet is associated with. If there is no associated
* device, pkt_pd must be set to NULL. A non-NULL pkt_pd will cause an
* increment of the reference count for said pd. When the packet is freed,
* the reference count will be decremented. This reference count, in
* combination with the PD_GIVEN_TO_ULPS flag guarantees that the pd
* will not wink out of existence while there is a packet outstanding.
*
* This function and fca_init_pkt must not perform any operations that
* would result in a call back to the ULP, as the ULP may be required
* to hold a mutex across this call to ensure that the pd in question
* won't go away prior the call to fc_ulp_transport.
*
* ULPs are responsible for using the handles they are given during state
* change callback processing in a manner that ensures consistency. That
* is, they must be aware that they could be processing a state change
* notification that tells them the device associated with a particular
* handle has gone away at the same time they are being asked to
* initialize a packet using that handle. ULPs must therefore ensure
* that their state change processing and packet initialization code
* paths are sufficiently synchronized to avoid the use of an
* invalidated handle in any fc_packet_t struct that is passed to the
* fc_ulp_init_packet() function.
*/
int
{
int rval;
/* Call the FCA driver's fca_init_pkt entry point function. */
/*
* A !NULL pd here must still be a valid
* reference to the fc_remote_port_t.
*/
pd->pd_ref_count++;
}
return (rval);
}
/*
* This function is called before destroying the cache allocated
* fc_packet to free up (and uninitialize) any resource specially
* allocated by the FCA driver during tran_init_pkt().
*
* If the pkt_pd field in the given fc_packet_t struct is not NULL, then
* the pd_ref_count reference count is decremented for the indicated
* fc_remote_port_t struct.
*/
int
{
int rval;
/* Call the FCA driver's fca_un_init_pkt entry point function */
pd->pd_ref_count--;
/*
* If at this point the state of this fc_remote_port_t
* struct is PORT_DEVICE_INVALID, it probably means somebody
* is cleaning up old (e.g. retried) packets. If the
* pd_ref_count has also dropped to zero, it's time to
* deallocate this fc_remote_port_t struct.
*/
pd->pd_ref_count == 0) {
/*
* Also deallocate the associated fc_remote_node_t
* struct if it has no other associated
* fc_remote_port_t structs.
*/
}
return (rval);
}
}
return (rval);
}
int
int flag)
{
int job_code;
port = port_handle;
if (port->fp_statec_busy) {
return (FC_STATEC_BUSY);
}
return (FC_OFFLINE);
}
port->fp_total_devices)) {
return (FC_NOMEM);
}
if (*map) {
}
}
if (change_list) {
listlen * sizeof (fc_portmap_t));
}
} else {
switch (flag) {
case FC_ULP_PLOGI_DONTCARE:
break;
case FC_ULP_PLOGI_PRESERVE:
break;
default:
return (FC_INVALID_REQUEST);
}
/*
* Submit a job request to the job handler
* thread to get the map and wait
*/
/*
* The result of the last I/O operation is
* in job_code. We don't care to look at it
* Rather we look at the number of devices
* that are found to fill out the map for
* ULPs.
*/
}
/*
* If we're here, we're returning a map to the caller, which means
* we'd better make sure every pd in that map has the
* PD_GIVEN_TO_ULPS flag set.
*/
while (tmp_len-- != 0) {
}
tmp_map++;
}
return (FC_SUCCESS);
}
int
{
int rval = FC_SUCCESS;
int job_flags;
/*
* If the port is OFFLINE, or if the port driver is
* being SUSPENDED/PM_SUSPENDED/DETACHED, block all
* PLOGI operations
*/
if (port->fp_statec_busy) {
return (FC_STATEC_BUSY);
}
(port->fp_soft_state &
return (FC_OFFLINE);
}
/*
* If the rscn count in the packet is not the same as the rscn count
* in the fc_local_port_t, then one or more new RSCNs has occurred.
*/
return (FC_DEVICE_BUSY_NEW_RSCN);
}
}
? 0 : JOB_TYPE_FCTL_ASYNC;
#ifdef DEBUG
{
int next;
int count;
int polled;
FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
& FC_TRAN_NO_INTR) ? 0 : JOB_TYPE_FCTL_ASYNC;
}
}
#endif
while (listlen--) {
continue;
}
/*
* Set the packet state and let the port
* driver call the completion routine
* from its thread
*/
continue;
}
continue;
}
}
if (!(job_flags & JOB_TYPE_FCTL_ASYNC)) {
}
return (rval);
}
int create)
{
port = port_handle;
*error = FC_SUCCESS;
/*
* A ULP now knows about this pd, so mark it
*/
return (pd);
}
return (pd);
}
sizeof (ns_resp_gid_pn_t), sizeof (ns_resp_gid_pn_t),
0, KM_SLEEP);
return (pd);
}
return (pd);
}
sizeof (ns_resp_gan_t), 0, FCTL_NS_CREATE_DEVICE,
KM_SLEEP);
return (pd);
}
/*
* Check if the port device is created now.
*/
*error = FC_FAILURE;
} else {
*error = FC_SUCCESS;
/*
* A ULP now knows about this pd, so mark it
*/
}
} else {
*error = FC_FAILURE;
}
return (pd);
}
/*
* If a NS object exists in the host and query is performed
* on that object, we should retrieve it from our basket
* and return it right here, there by saving a request going
* all the up to the Name Server.
*/
int
{
int rval;
int fabric;
/*
* Name server query can't be performed for devices not in Fabric
*/
return (FC_BADOBJECT);
}
if (rval != FC_SUCCESS) {
return (rval);
}
} else {
/*
* Guess what, FC-GS-2 currently prohibits (not
* in the strongest language though) setting of
* NS object values by other ports. But we might
* get that changed to at least accommodate setting
* were going to provide a method to set these
* values directly (which in turn might register
* with the NS when they come up; yep, for that
* to happen the disks will have to be very well
* behaved Fabric citizen) we won't need to
* other ports too (rather send down SCSI commands
* to the devices to set the names)
*
* Be that as it may, let's continue to fail
* registration requests for other ports. period.
*/
return (FC_BADOBJECT);
}
if (!fabric) {
return (FC_SUCCESS);
}
} else if (!fabric) {
}
ns_req->ns_req_len);
}
sizeof (fc_ct_header_t));
return (rval);
}
int
{
int rval;
port = port_handle;
}
if (port->fp_statec_busy) {
return (FC_STATEC_BUSY);
}
/* A locus of race conditions */
(port->fp_soft_state &
return (FC_OFFLINE);
}
/*
* If the rscn count in the packet is not the same as the rscn count
* in the fc_local_port_t, then one or more new RSCNs has occurred.
*/
return (FC_DEVICE_BUSY_NEW_RSCN);
}
if (pd) {
&pd->pd_port_name);
/*
* The remote port (pd) in the packet is no longer
* usable, as the old pd still exists we can use the
* WWN to check if we have a current pd for the device
* we want. Either way we continue with the old logic
* whether we have a new pd or not, as the new pd
* could be bad, or have become unusable.
*/
/*
* There is a better remote port (pd) to try,
* so we need to fix the reference counts, etc.
*/
newpd->pd_ref_count++;
pd->pd_ref_count--;
(pd->pd_ref_count == 0)) {
/*
* This will create another PD hole
* where we have a reference to a pd,
* but someone else could remove it.
*/
}
} else {
}
}
}
return (rval);
}
return (FC_DEVICE_BUSY);
}
return (FC_BADDEV);
}
return (FC_BADPACKET);
}
}
int
{
int rval;
/*
* If the port is OFFLINE, or if the port driver is
* being SUSPENDED/PM_SUSPENDED/DETACHED, block all
* ELS operations
*/
(port->fp_soft_state &
return (FC_OFFLINE);
}
if (port->fp_statec_busy) {
return (FC_STATEC_BUSY);
}
/*
* If the rscn count in the packet is not the same as the rscn count
* in the fc_local_port_t, then one or more new RSCNs has occurred.
*/
return (FC_DEVICE_BUSY_NEW_RSCN);
}
return (rval);
}
return (FC_DEVICE_BUSY);
}
return (FC_BADDEV);
}
}
}
int
{
}
int
{
}
int
{
}
int
{
}
/*
* Submit an asynchronous request to the job handler if the sleep
* flag is set to KM_NOSLEEP, as such calls could have been made
* in interrupt contexts, and the goal is to avoid busy waiting,
* blocking on a conditional variable, a semaphore or any of the
* synchronization primitives. A noticeable draw back with this
* asynchronous request is that an FC_SUCCESS is returned long
* before the reset is complete (successful or not).
*/
int
{
int rval;
port = port_handle;
/*
* Many a times, this function is called from interrupt
* contexts and there have been several dead locks and
* hangs - One of the simplest work arounds is to fib
* if a RESET is in progress.
*/
return (FC_SUCCESS);
}
/*
* Ward off this reset if a state change is in progress.
*/
if (port->fp_statec_busy) {
return (FC_STATEC_BUSY);
}
if (fctl_busy_port(port) != 0) {
return (FC_FAILURE);
}
} else {
return (FC_NOMEM);
}
rval = FC_SUCCESS;
}
return (rval);
}
int
{
int rval = FC_SUCCESS;
switch (cmd) {
case FC_RESET_PORT:
break;
case FC_RESET_ADAPTER:
break;
case FC_RESET_DUMP:
break;
case FC_RESET_CRASH:
break;
default:
rval = FC_FAILURE;
}
return (rval);
}
int
{
/* Copy the login parameters */
return (FC_SUCCESS);
}
int
{
return (port->fp_instance);
}
{
break;
}
}
return (port_handle);
}
int
{
}
int
{
}
/*
* If an ULP by the specified name exists, return FC_SUCCESS, else FC_FAILURE
*/
int
{
int rval = FC_FAILURE;
rval = FC_SUCCESS;
break;
}
}
return (rval);
}
/*
* Return port WWN for a port Identifier
*/
int
{
int rval = FC_FAILURE;
rval = FC_SUCCESS;
}
return (rval);
}
/*
* Return a port map for a port WWN
*/
int
{
return (FC_FAILURE);
}
if (node) {
}
return (FC_SUCCESS);
}
{
return (NULL);
}
}
int
{
int rval = FC_SUCCESS;
switch (cmd) {
case FC_NOTIFY_TARGET_MODE:
break;
case FC_NOTIFY_NO_TARGET_MODE:
break;
}
}
return (rval);
}
void
{
if (pd) {
}
}
void
{
if (pd) {
}
}
/*
* fc_fca_init
* Overload the FCA bus_ops vector in its dev_ops with
* fctl_fca_busops to handle all the INITchilds for "sf"
* in one common place.
*
* Should be called from FCA _init routine.
*/
void
{
#ifndef __lock_lint
#endif /* __lock_lint */
}
/*
* fc_fca_attach
*/
int
{
/*
* When we are in a position to offer downward compatibility
* we should change the following check to allow lower revision
* of FCAs; But we aren't there right now.
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* fc_fca_detach
*/
int
{
return (DDI_SUCCESS);
}
/*
* Check if the frame is a Link response Frame; Handle all cases (P_RJT,
* F_RJT, P_BSY, F_BSY fall into this category). Check also for some Basic
* Link Service responses such as BA_RJT and Extended Link Service response
* such as LS_RJT. If the response is a Link_Data Frame or something that
* this function doesn't understand return FC_FAILURE; Otherwise, fill out
* various fields (state, action, reason, expln) from the response gotten
* in the packet and return FC_SUCCESS.
*/
int
{
int ret = FC_SUCCESS;
case R_CTL_P_RJT: {
break;
}
case R_CTL_F_RJT: {
break;
}
case R_CTL_P_BSY: {
break;
}
case R_CTL_F_BSY_LC:
case R_CTL_F_BSY_DF: {
break;
}
case R_CTL_LS_BA_RJT: {
break;
}
case R_CTL_ELS_RSP: {
break;
}
/* FALLTHROUGH */
}
default:
ret = FC_FAILURE;
break;
}
return (ret);
}
int
{
}
int
{
}
/*
* WWN to string goodie. Unpredictable results will happen
* if enough memory isn't supplied in str argument. If you
* are wondering how much does this routine need, it is just
* (2 * WWN size + 1). So for a WWN size of 8 bytes the str
* argument should have atleast 17 bytes allocated.
*/
void
{
int count;
}
*str = '\0';
}
((x) >= 'a' && (x) <= 'f') ?\
((x) - 'a' + 10) : ((x) - 'A' + 10))
void
{
int count = 0;
while (*str) {
str++;
str++;
}
}
/*
* FCA driver's intercepted bus control operations.
*/
static int
{
switch (op) {
case DDI_CTLOPS_REPORTDEV:
break;
case DDI_CTLOPS_IOMIN:
break;
case DDI_CTLOPS_INITCHILD:
case DDI_CTLOPS_UNINITCHILD:
default:
}
return (DDI_SUCCESS);
}
/*
* FCAs indicate the maximum number of ports supported in their
* tran structure. Fail the INITCHILD if the child port number
* is any greater than the maximum number of ports supported
* by the FCA.
*/
static int
{
int rval;
int port_no;
int port_len;
char name[20];
int portprop;
/* physical port do not has this property */
"phyport-instance", -1);
/*
* Clear any addr bindings created by fcode interpreter
* in devi_last_addr so that a ndi_devi_find should never
* return this fcode node.
*/
return (DDI_FAILURE);
}
if (rval != DDI_SUCCESS) {
return (DDI_FAILURE);
}
/*
* Even though we never initialize FCode nodes of fp, such a node
* could still be there after a DR operation. There will only be
* one FCode node, so if this is the one, clear it and issue a
* ndi_devi_find again.
*/
}
/*
* Here we have a duplicate .conf entry. Clear the addr
* set previously and return failure.
*/
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/* ARGSUSED */
static int
{
return (DDI_SUCCESS);
}
static dev_info_t *
{
char *addr;
/* ASSERT(DEVI_BUSY_OWNED(pdip)); */
continue;
return (dip);
}
}
} else {
return (dip);
}
}
return (NULL);
}
int
{
int i, instance;
return (0);
}
i = vindex-1;
if (port->fp_npiv_portindex[i] == 0) {
return (vindex);
}
return (0);
}
int
{
int i, instance;
if (!port) {
return (0);
}
for (i = 0; i < FC_NPIV_MAX_PORT; i++) {
if (port->fp_npiv_portindex[i] == 0) {
return (i+1);
}
}
return (0);
}
void
{
int instance;
if (!port) {
return;
}
}
int
{
int portnum;
if (*vindex == 0) {
} else {
}
if (portnum == 0) {
"Cann't find valid port index, fail to create devnode");
return (NDI_FAILURE);
}
rval = NDI_FAILURE;
goto freememory;
}
"fctl_create_npiv_port fail to create new devinfo");
rval = NDI_FAILURE;
goto freememory;
}
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
}
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
}
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
"fp%d: prop_update phyport-instance %s@%s failed",
(void) ndi_devi_free(child);
rval = NDI_FAILURE;
goto freememory;
}
if (rval != NDI_SUCCESS) {
rval = NDI_FAILURE;
goto freememory;
}
return (rval);
}
void
{
}
void
{
continue;
}
#ifndef __lock_lint
#endif /* __lock_lint */
}
} else {
}
break;
}
}
}
void
struct modlinkage *linkage)
{
int rval;
/*
* It is still possible that another thread could have gotten
* into the detach process before we got here.
*/
return;
}
if (port->fp_statec_busy) {
} else {
}
case FC_STATE_LOOP:
case FC_STATE_NAMESERVICE:
break;
default:
break;
}
}
}
continue;
}
}
}
}
static int
{
int rval = FC_SUCCESS;
switch (cmd) {
case FC_CMD_ATTACH:
rval = FC_FAILURE;
}
break;
case FC_CMD_RESUME:
rval = FC_FAILURE;
}
break;
case FC_CMD_POWER_UP:
rval = FC_FAILURE;
}
break;
}
if (rval == FC_SUCCESS) {
}
return (rval);
}
static void
{
int be_chatty;
cmd == FC_CMD_POWER_UP);
if (rval != FC_SUCCESS) {
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) {
}
return;
}
switch (cmd) {
case FC_CMD_ATTACH:
break;
case FC_CMD_RESUME:
break;
case FC_CMD_POWER_UP:
break;
}
}
int
struct modlinkage *linkage)
{
int rval = FC_SUCCESS;
continue;
}
continue;
}
if (rval != FC_SUCCESS) {
break;
}
"fcp") == 0) {
}
}
return (rval);
}
static void
{
} else {
}
}
static int
{
int rval = FC_SUCCESS;
switch (cmd) {
case FC_CMD_DETACH:
rval = FC_FAILURE;
}
break;
case FC_CMD_SUSPEND:
rval = FC_FAILURE;
}
break;
case FC_CMD_POWER_DOWN:
rval = FC_FAILURE;
}
break;
}
if (rval == FC_SUCCESS) {
}
return (rval);
}
static void
{
cmd == FC_CMD_POWER_DOWN);
if (rval != FC_SUCCESS) {
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;
}
return;
}
switch (cmd) {
case FC_CMD_DETACH:
break;
case FC_CMD_SUSPEND:
break;
case FC_CMD_POWER_DOWN:
break;
}
}
static fc_ulp_ports_t *
int sleep)
{
}
return (new);
}
} else {
}
return (new);
}
static fc_ulp_ports_t *
fctl_alloc_ulp_port(int sleep)
{
return (new);
}
return (new);
}
static int
{
return (FC_FAILURE);
}
break;
}
}
} else {
}
return (FC_SUCCESS);
} else {
return (FC_FAILURE);
}
}
static void
{
}
static fc_ulp_ports_t *
{
return (next);
}
}
return (NULL);
}
/*
* Pass state change notfications on to registered ULPs.
*
* Can issue wakeups to client callers who might be waiting for completions
* on other threads.
*
* fc_remote_node_t structs it finds that are not in use.
*/
void
fctl_ulp_statec_cb(void *arg)
{
switch (clist->clist_state) {
case FC_STATE_ONLINE:
break;
case FC_STATE_OFFLINE:
} else {
}
break;
default:
break;
}
#ifdef DEBUG
/*
* sanity check for presence of OLD devices in the hash lists
*/
if (clist->clist_size) {
int count;
&pwwn);
map_pd);
map_pd);
}
}
}
}
#endif
/*
* Check for duplicate map entries
*/
if (clist->clist_size) {
int count;
int count2;
continue;
}
count2++) {
continue;
}
break;
}
}
}
}
continue;
}
continue;
}
switch (ulp_port->port_statec) {
case FC_ULP_STATEC_DONT_CARE:
}
break;
case FC_ULP_STATEC_ONLINE:
case FC_ULP_STATEC_OFFLINE:
continue;
}
break;
continue;
}
break;
default:
ASSERT(0);
break;
}
}
if (clist->clist_size) {
int count;
continue;
}
pd->pd_ref_count--;
continue;
}
/*
* This fc_remote_port_t is no longer referenced
* by any ULPs. Deallocate it if its pd_ref_count
* has reached zero.
*/
}
}
}
if (clist->clist_wait) {
clist->clist_wait = 0;
} else {
}
}
/*
* Allocate an fc_remote_node_t struct to represent a remote node for the
* given nwwn. This will also add the nwwn to the global nwwn table.
*
* Returns a pointer to the newly-allocated struct. Returns NULL if
* the kmem_zalloc fails or if the enlist_wwn attempt fails.
*/
{
return (NULL);
}
return (NULL);
}
return (rnodep);
}
/*
* Deconstruct and free the given fc_remote_node_t struct (remote node struct).
* Silently skips the deconstruct/free if there are any fc_remote_port_t
* (remote port device) structs still referenced by the given
* fc_remote_node_t struct.
*/
void
{
/*
* Look at the count and linked list of of remote ports
* (fc_remote_port_t structs); bail if these indicate that
* given fc_remote_node_t may be in use.
*/
return;
}
}
/*
* Add the given fc_remote_node_t to the global fctl_nwwn_hash_table[]. This
* uses the nwwn in the fd_node_name.raw_wwn of the given struct.
* This only fails if the kmem_zalloc fails. This does not check for a
* unique or pre-existing nwwn in the fctl_nwwn_hash_table[].
* This is only called from fctl_create_remote_node().
*/
int
{
int index;
return (FC_FAILURE);
}
/* Link it in at the head of the hash list */
return (FC_SUCCESS);
}
/*
* Remove the given fc_remote_node_t from the global fctl_nwwn_hash_table[].
* This uses the nwwn in the fd_node_name.raw_wwn of the given struct.
*/
void
{
int index;
/*
* Found it -- unlink it from the list & decrement
* the count for the hash chain.
*/
} else {
}
break;
}
}
}
}
/*
* Returns a reference to an fc_remote_node_t struct for the given node_wwn.
* Looks in the global fctl_nwwn_hash_table[]. Identical to the
* fctl_lock_remote_node_by_nwwn() function, except that this does NOT increment
* the fc_count reference count in the f_device_t before returning.
*
* This function is called by: fctl_create_remote_port_t().
*
* OLD COMMENT:
* Note: The calling thread needs to make sure it isn't holding any device
* mutex (more so the fc_remote_node_t that could potentially have this wwn).
*/
{
int index;
break;
}
}
}
return (rnodep);
}
/*
* Returns a reference to an fc_remote_node_t struct for the given node_wwn.
* Looks in the global fctl_nwwn_hash_table[]. Increments the fd_numports
* reference count in the f_device_t before returning.
*
* This function is only called by fctl_create_remote_port_t().
*/
{
int index;
rnodep->fd_numports++;
break;
}
}
}
return (rnodep);
}
/*
* Allocate and initialize an fc_remote_port_t struct & returns a pointer to
* the newly allocated struct. Only fails if the kmem_zalloc() fails.
*/
{
return (NULL);
}
return (pd);
}
/*
* Deconstruct and free the given fc_remote_port_t struct (unconditionally).
*/
void
{
}
/*
* Add the given fc_remote_port_t onto the linked list of remote port
* devices associated with the given fc_remote_node_t. Does NOT add the
* fc_remote_port_t to the list if already exists on the list.
*/
void
{
/*
* The given fc_remote_port_t is already on the linked
* list chain for the given remote node, so bail now.
*/
return;
}
}
/* Add the fc_remote_port_t to the tail of the linked list */
} else {
}
/*
* Link the fc_remote_port_t back to the associated fc_remote_node_t.
*/
}
/*
* Remove the specified fc_remote_port_t from the linked list of remote ports
* for the given fc_remote_node_t.
*
* Returns a count of the _remaining_ fc_remote_port_t structs on the linked
* list of the fc_remote_node_t.
*
* The fd_numports on the given fc_remote_node_t is decremented, and if
* it hits zero then this function also removes the fc_remote_node_t from the
* global fctl_nwwn_hash_table[]. This appears to be the ONLY WAY that entries
* are removed from the fctl_nwwn_hash_table[].
*/
int
{
int rcount = 0;
/*
* Go thru the linked list of fc_remote_port_t structs for the given
* fc_remote_node_t; try to find the specified fc_remote_port_t (pd).
*/
break; /* Found the requested fc_remote_port_t */
}
}
if (ports) {
if (rcount == 0) {
/* Note: this is only ever called from here */
}
if (last) {
} else {
}
}
return (rcount);
}
/*
* Add the given fc_remote_port_t struct to the d_id table in the given
* fc_local_port_t struct. Hashes based upon the pd->pd_port_id.port_id in the
* fc_remote_port_t.
*
* No memory allocs are required, so this never fails, but it does use the
* (pd->pd_aux_flags & PD_IN_DID_QUEUE) to keep duplicates off the list.
* (There does not seem to be a way to tell the caller that a duplicate
* exists.)
*/
void
{
return;
}
#ifdef DEBUG
{
int index;
/*
* Search down in each bucket for a duplicate pd
* Also search for duplicate D_IDs
* This DEBUG code will force an ASSERT if a duplicate
* is ever found.
*/
}
}
}
}
#endif
head->d_id_count++;
}
/*
* Remove the given fc_remote_port_t struct from the d_id table in the given
* fc_local_port_t struct. Hashes based upon the pd->pd_port_id.port_id in the
* fc_remote_port_t.
*
* Does nothing if the requested fc_remote_port_t was not found.
*/
void
{
break; /* Found the given fc_remote_port_t */
}
}
if (pd_next) {
/*
* Found the given fc_remote_port_t; now remove it from the
* d_id list.
*/
head->d_id_count--;
} else {
}
}
}
/*
* Add the given fc_remote_port_t struct to the pwwn table in the given
* fc_local_port_t struct. Hashes based upon the pd->pd_port_name.raw_wwn
* in the fc_remote_port_t.
*
* No memory allocs are required, so this never fails.
*/
void
{
int index;
#ifdef DEBUG
{
int index;
/*
* Search down in each bucket for a duplicate pd
* Search also for a duplicate WWN
* Throw an ASSERT if any duplicate is found.
*/
&pd->pd_port_name) != 0);
}
}
}
}
#endif /* DEBUG */
head->pwwn_count++;
/*
* Make sure we tie fp_dev_count to the size of the
* pwwn_table
*/
port->fp_dev_count++;
}
/*
* Remove the given fc_remote_port_t struct from the pwwn table in the given
* fc_local_port_t struct. Hashes based upon the pd->pd_port_name.raw_wwn
* in the fc_remote_port_t.
*
* Does nothing if the requested fc_remote_port_t was not found.
*/
void
{
int index;
break; /* Found the given fc_remote_port_t */
}
}
if (pd_next) {
/*
* Found the given fc_remote_port_t; now remove it from the
* pwwn list.
*/
head->pwwn_count--;
/*
* Make sure we tie fp_dev_count to the size of the
* pwwn_table
*/
port->fp_dev_count--;
} else {
}
}
}
/*
* Looks in the d_id table of the specified fc_local_port_t for the
* fc_remote_port_t that matches the given d_id. Hashes based upon
* the given d_id.
* Returns a pointer to the fc_remote_port_t struct, but does not update any
* reference counts or otherwise indicate that the fc_remote_port_t is in
* use.
*/
{
/* Match found -- break out of the loop */
break;
}
}
return (pd);
}
#ifndef __lock_lint /* uncomment when there is a consumer */
void
{
pd->pd_ref_count++;
}
/*
* Looks in the d_id table of the specified fc_local_port_t for the
* fc_remote_port_t that matches the given d_id. Hashes based upon
* the given d_id. Returns a pointer to the fc_remote_port_t struct.
*
* Increments pd_ref_count in the fc_remote_port_t if the
* fc_remote_port_t is found at the given d_id.
*
* The fc_remote_port_t is ignored (treated as non-existent) if either
* its pd_state == PORT_DEVICE_INVALID _OR_ its pd_type == PORT_DEVICE_OLD.
*/
{
pd->pd_ref_count++;
break;
}
}
return (pd);
}
#endif /* __lock_lint */
/*
* Looks in the pwwn table of the specified fc_local_port_t for the
* fc_remote_port_t that matches the given pwwn. Hashes based upon the
* given pwwn->raw_wwn. Returns a pointer to the fc_remote_port_t struct,
* but does not update any reference counts or otherwise indicate that
* the fc_remote_port_t is in use.
*/
{
int index;
break;
}
}
return (pd);
}
/*
* Basically the same as fctl_get_remote_port_by_pwwn(), but requires that
* the caller already hold the fp_mutex in the fc_local_port_t struct.
*/
{
int index;
break;
}
}
return (pd);
}
/*
* Looks in the pwwn table of the specified fc_local_port_t for the
* fc_remote_port_t that matches the given d_id. Hashes based upon the
* given pwwn->raw_wwn. Returns a pointer to the fc_remote_port_t struct.
*
* Increments pd_ref_count in the fc_remote_port_t if the
* fc_remote_port_t is found at the given pwwn.
*
* The fc_remote_port_t is ignored (treated as non-existent) if either
* its pd_state == PORT_DEVICE_INVALID _OR_ its pd_type == PORT_DEVICE_OLD.
*/
{
int index;
pd->pd_ref_count++;
break;
}
}
return (pd);
}
/*
* Unconditionally decrement pd_ref_count in the given fc_remote_port_t
* struct.
*
* If pd_ref_count reaches zero, then this function will see if the
* fc_remote_port_t has been marked for deallocation. If so (and also if there
* are no other potential operations in progress, as indicated by the
* PD_ELS_IN_PROGRESS & PD_ELS_MARK settings in the pd_flags), then
* fctl_destroy_remote_port_t() is called to deconstruct/free the given
* fc_remote_port_t (which will also remove it from the d_id and pwwn tables
* on the associated fc_local_port_t). If the associated fc_remote_node_t is no
* longer in use, then it too is deconstructed/freed.
*/
void
{
int remove = 0;
pd->pd_ref_count--;
if (pd->pd_ref_count == 0 &&
remove = 1;
}
if (remove) {
/*
* The fc_remote_port_t struct has to go away now, so call the
* cleanup function to get it off the various lists and remove
* references to it in any other associated structs.
*/
/*
* No more fc_remote_port_t references found in the
* associated fc_remote_node_t, so deallocate the
* fc_remote_node_t (if it even exists).
*/
if (node) {
}
}
}
}
void
{
int index;
int listlen;
int full_list;
int initiator;
if (orphan) {
}
full_list++;
listlen++;
}
}
}
if (whole_map == 0) {
return;
}
} else {
return;
}
}
if (*len == 0) {
if (whole_map == 0) {
} else {
}
} else {
/*
* By design this routine mandates the callers to
* ask for a whole map when they specify the length
* and the listptr.
*/
return;
}
}
if ((whole_map == 0 &&
continue;
}
if (justcopy) {
listptr++;
continue;
}
/*
* Remove this from the PWWN hash table.
*/
} else {
}
head->pwwn_count--;
/*
* Make sure we tie fp_dev_count to the size
* of the pwwn_table
*/
port->fp_dev_count--;
}
/*
* Remove if the port device has stealthily
* present in the D_ID hash table
*/
PD_PLOGI_INITIATOR) ? 1 : 0;
if (orphan) {
}
}
} else {
}
listptr++;
}
}
}
{
job->job_ulp_listlen = 0;
#ifndef __lock_lint
job->job_counter = 0;
#endif /* __lock_lint */
}
return (job);
}
void
{
}
void
{
} else {
}
}
{
} else {
}
}
return (job);
}
void
{
} else {
}
}
void
{
}
void
{
}
} else {
}
}
/*
* Compare two WWNs. The NAA is omitted for comparison.
*
* Note particularly that the indentation used in this
* function isn't according to Sun recommendations. It
* is indented to make reading a bit easy.
*
* Return Values:
* if src == dst return 0
* if src > dst return 1
* if src < dst return -1
*/
int
{
/*
* Fibre Channel protocol is big endian, so compare
* as big endian values
*/
return (
}
/*
* ASCII to Integer goodie with support for base 16, 10, 2 and 8
*/
int
{
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') {
} else if (*s >= 'A' && *s <= 'F') {
} 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);
}
}
return (val);
}
/*
* Create the fc_remote_port_t struct for the given port_wwn and d_id.
*
* If the struct already exists (and is "valid"), then use it. Before using
* it, the code below also checks: (a) if the d_id has changed, and (b) if
* the device is maked as PORT_DEVICE_OLD.
*
* If no fc_remote_node_t struct exists for the given node_wwn, then that
* struct is also created (and linked with the fc_remote_port_t).
*
* The given fc_local_port_t struct is updated with the info on the new
* struct(s). The d_id and pwwn hash tables in the port_wwn are updated.
* The global node_hash_table[] is updated (if necessary).
*/
{
int invalid = 0;
if (rnodep) {
/*
* We found an fc_remote_node_t for the remote node -- see if
* anyone has marked it as going away or gone.
*/
}
/*
* No valid remote node struct found -- create it.
* Note: this is the only place that this func is called.
*/
return (NULL);
}
}
/*
* See if there already is an fc_remote_port_t struct in existence
* on the specified fc_local_port_t for the given pwwn. If so, then
* grab a reference to it. The 'held' here just means that fp_mutex
* is held by the caller -- no reference counts are updated.
*/
if (pd) {
/*
* An fc_remote_port_t struct was found -- see if anyone has
* marked it as "invalid", which means that it is in the
* process of going away & we don't want to use it.
*/
}
/*
* No fc_remote_port_t was found (or the existing one is
* marked as "invalid".) Allocate a new one and use that.
* This call will also update the d_id and pwwn hash tables
* in the given fc_local_port_t struct with the newly allocated
* fc_remote_port_t.
*/
/* Just give up if the allocation fails. */
return (pd);
}
/*
* Add the new fc_remote_port_t struct to the d_id and pwwn
* hash tables on the associated fc_local_port_t struct.
*/
/*
* Retrieve a pointer to the fc_remote_node_t (i.e., remote
* node) specified by the given node_wwn. This looks in the
* global fctl_nwwn_hash_table[]. The fd_numports reference
* count in the fc_remote_node_t struct is incremented.
*/
} else {
/*
* An existing and valid fc_remote_port_t struct already
* exists on the fc_local_port_t for the given pwwn.
*/
/*
* A very unlikely occurance in a well
* behaved environment.
*/
/*
* The existing fc_remote_port_t has a different
* d_id than what we were given. This code will
* update the existing one with the one that was
* just given.
*/
" with PWWN %s changed. New D_ID = %x,"
/*
* Looks like we have to presume here that the
* remote port could be something entirely different
* from what was previously existing & valid at this
* pwwn.
*/
/* Record (update) the new d_id for the remote port */
/*
* OK at least the old & new d_id's match. So for
* PORT_DEVICE_OLD, this assumes that the remote
* port had disappeared but now has come back.
* Update the pd_type and pd_state to put the
* remote port back into service.
*/
} else {
/*
* OK the old & new d_id's match, and the remote
* port struct is not marked as PORT_DEVICE_OLD, so
* presume that it's still the same device and is
* still in good shape. Also this presumes that we
* do not need to update d_id or pwwn hash tables.
*/
/* sanitize device values */
}
node_wwn) != 0)) {
/*
* Rut-roh, there is an fc_remote_node_t remote
* node struct for the given node_wwn, but the
* fc_remote_port_t remote port struct doesn't
* know about it. This just prints a warning
* message & fails the fc_remote_port_t
* allocation (possible leak here?).
*/
char ww1_name[17];
char ww2_name[17];
ww1_name);
}
return (NULL);
}
}
/*
* Add the fc_remote_port_t onto the linked list of remote port
* devices associated with the given fc_remote_node_t (remote node).
*/
return (pd);
}
/*
* Disassociate the given fc_local_port_t and fc_remote_port_t structs. Removes
* the fc_remote_port_t from the associated fc_remote_node_t. Also removes any
* references to the fc_remote_port_t from the d_id and pwwn tables in the
* given fc_local_port_t. Deallocates the given fc_remote_port_t.
*
* Returns a count of the number of remaining fc_remote_port_t structs
* associated with the fc_remote_node_t struct.
*
* If pd_ref_count in the given fc_remote_port_t is nonzero, then this
* function just sets the pd->pd_aux_flags |= PD_NEEDS_REMOVAL and the
* pd->pd_type = PORT_DEVICE_OLD and lets some other function(s) worry about
* the cleanup. The function then also returns '1'
* instead of the actual number of remaining fc_remote_port_t structs
*
* If there are no more remote ports on the remote node, return 0.
* Otherwise, return non-zero.
*/
int
{
int rcount = 0;
/*
* If pd_ref_count > 0, we can't pull the rug out from any
* current users of this fc_remote_port_t. We'll mark it as old
* and in need of removal. The same goes for any fc_remote_port_t
* that has a reference handle(s) in a ULP(s) but for which the ULP(s)
* have not yet been notified that the handle is no longer valid
* (i.e., PD_GIVEN_TO_ULPS is set).
*/
if ((pd->pd_ref_count > 0) ||
return (1);
}
/*
* Remove the fc_remote_port_t from the linked list of remote
* ports for the given fc_remote_node_t. This is only called
* here and in fctl_destroy_all_remote_ports().
*/
}
/*
* Deconstruct & free the fc_remote_port_t. This is only called
* here and in fctl_destroy_all_remote_ports().
*/
return (rcount);
}
/*
* This goes thru the d_id table on the given fc_local_port_t.
* For each fc_remote_port_t found, this will:
*
* - Remove the fc_remote_port_t from the linked list of remote ports for
* the associated fc_remote_node_t. If the linked list goes empty, then this
* tries to deconstruct & free the fc_remote_node_t (that also removes the
* fc_remote_node_t from the global fctl_nwwn_hash_table[]).
*
* - Remove the fc_remote_port_t from the pwwn list on the given
* fc_local_port_t.
*
* - Deconstruct and free the fc_remote_port_t.
*
* - Removes the link to the fc_remote_port_t in the d_id table. Note, this
* does not appear to correctle decrement the d_id_count tho.
*/
void
{
int index;
/*
* See if this remote port (fc_remote_port_t) has a
* reference to a remote node (fc_remote_node_t) in its
* pd->pd_remote_nodep pointer.
*/
/*
* An fc_remote_node_t reference exists. Remove
* the fc_remote_port_t from the linked list of
* remote ports for fc_remote_node_t.
*/
/*
* The fd_numports reference count
* in the fc_remote_node_t has come
* back as zero, so we can free the
* fc_remote_node_t. This also means
* that the fc_remote_node_t was
* removed from the
* fctl_nwwn_hash_table[].
*
* This will silently skip the
* kmem_free() if either the
* fd_numports is nonzero or
* the fd_port is not NULL in
* the fc_remote_node_t.
*/
}
}
/*
* Clean up the entry in the fc_local_port_t's pwwn
* table for the given fc_remote_port_t (i.e., the pd).
*/
/*
* Remove the current entry from the d_id list.
*/
/*
* Deconstruct & free the fc_remote_port_t (pd)
* Note: this is only called here and in
* fctl_destroy_remote_port_t().
*/
}
}
}
int
{
int count;
return (FC_FAILURE);
}
}
return (FC_SUCCESS);
}
void
{
int data_cb;
int check_type;
int rval;
claimed = 0;
check_type = 1;
case R_CTL_DEVICE_DATA:
data_cb = 1;
break;
case R_CTL_EXTENDED_SVC:
check_type = 0;
/* FALLTHROUGH */
case R_CTL_FC4_SVC:
data_cb = 0;
break;
default:
if (--(port->fp_active_ubs) == 0) {
}
return;
}
continue;
}
continue;
}
continue;
}
if (data_cb == 1) {
} else {
}
claimed = 1;
}
}
if (claimed == 0) {
/*
* We should actually RJT since nobody claimed it.
*/
if (--(port->fp_active_ubs) == 0) {
}
} else {
if (--port->fp_active_ubs == 0) {
}
}
}
/*
* Both fd_mutex and pd_mutex are held (in that order) coming in to this func
*
* With all these mutexes held, we should make sure this function does not eat
* up much time.
*/
void
{
if (node) {
}
}
void
{
if (node) {
}
}
static int
{
int rval = FC_SUCCESS;
case NS_RFT_ID: {
int count;
}
break;
}
case NS_RSPN_ID: {
}
break;
}
case NS_RSNN_NN: {
}
break;
}
case NS_RIP_NN: {
sizeof (rip->rip_ip_addr));
break;
}
case NS_RIPA_NN: {
break;
}
default:
rval = FC_BADOBJECT;
break;
}
return (rval);
}
static int
{
int rval = FC_SUCCESS;
case NS_GFT_ID: {
break;
}
case NS_GSPN_ID: {
}
break;
}
case NS_GSNN_NN: {
}
break;
}
case NS_GIP_NN: {
sizeof (rip->rip_ip_addr));
break;
}
case NS_GIPA_NN: {
break;
}
default:
rval = FC_BADOBJECT;
break;
}
return (rval);
}
{
return (NULL);
}
if (cmd_len) {
return (NULL);
}
}
if (data_len) {
}
return (NULL);
}
}
return (ns_cmd);
}
void
{
}
}
}
int
{
int ret;
int save;
continue;
}
continue;
}
claimed = 1;
}
}
return (ret);
}
/*
* raise power if necessary, and set the port busy
*
* this may cause power to be raised, so no power related locks should
* be held
*/
int
{
return (fctl_busy_port(port));
}
void
{
}
void
{
}
int
{
int portsnum = 0;
if (!tmpport) {
return (portsnum);
}
portsnum ++;
}
return (portsnum);
}
{
continue;
}
return (tmpPort);
}
}
return (NULL);
}
int
{
int portsnum = 0;
return (portsnum);
}
portsnum ++;
}
return (portsnum);
}
{
return (NULL);
}
(tmpport->fp_npiv_state == 0)) {
return (tmpport);
}
}
return (NULL);
}
/*
* Get the list of Adapters. On multi-ported adapters,
* only ONE port on the adapter will be returned.
* pathList should be (count * MAXPATHLEN) long.
* The return value will be set to the number of
* HBAs that were found on the system. If the value
* is greater than count, the routine should be retried
* with a larger buffer.
*/
int
{
/* First figure out how many ports we have */
maxPorts ++;
}
/* Now allocate a buffer to store all the pointers for comparisons */
skip = 0;
/* Lock the new port for subsequent comparisons */
/* Filter out secondary ports from the list */
continue;
}
/* Guard against duplicates (should never happen) */
/* Same port */
skip = 1;
break;
}
/* Lock the already stored port for comparison */
/* Are these ports on the same HBA? */
/* Now double check driver */
FCHBA_DRIVER_NAME_LEN) == 0) {
/* we no we don't need to grow the list */
skip = 1;
/* Are we looking at a lower port index? */
/* Replace the port in the list */
break;
}
break;
} /* Else, just skip this port */
}
}
}
if (!skip) {
/*
* Either this is the first port for this HBA, or
* it's a secondary port and we haven't stored the
* will just filter it out as we proceed to loop.
*/
continue;
} else {
}
}
}
}
}
return (out);
}
{
return (count);
}
/*
* This function is a very similar to fctl_add_orphan except that it expects
* that the fp_mutex and pd_mutex of the pd passed in are held coming in.
*
* Note that there is a lock hierarchy here (fp_mutex should be held first) but
* since this function could be called with a different pd's pd_mutex held, we
* should take care not to release fp_mutex in this function.
*/
int
{
int rval = FC_FAILURE;
return (FC_SUCCESS);
}
}
if (orphan) {
if (port->fp_orphan_list) {
}
port->fp_orphan_count++;
rval = FC_SUCCESS;
}
return (rval);
}
int
{
int rval = FC_FAILURE;
return (FC_SUCCESS);
}
}
if (port->fp_orphan_list) {
}
port->fp_orphan_count++;
rval = FC_SUCCESS;
}
return (rval);
}
int
{
int rval = FC_FAILURE;
if (prev) {
} else {
}
port->fp_orphan_count--;
rval = FC_SUCCESS;
break;
}
}
if (rval == FC_SUCCESS) {
}
return (rval);
}
static void
{
char ww_name[17];
return;
}
}
}
/* ARGSUSED */
static void
{
}
static int
{
int count;
sizeof (fc_errlist[0]); count++) {
return (FC_SUCCESS);
}
}
*errmsg = fctl_undefined;
return (FC_FAILURE);
}
/*
* Return number of successful translations.
* Anybody with some userland programming experience would have
* figured it by now that the return value exactly resembles that
* of scanf(3c). This function returns a count of successful
* translations. It could range from 0 (no match for state, reason,
* action, expln) to 4 (successful matches for all state, reason,
* action, expln) and where translation isn't successful into a
* friendlier message the relevent field is set to "Undefined"
*/
static int
{
int ret;
int len;
int index;
ret = 0;
ret++;
ret++;
break;
}
reason_b++;
}
ret++;
break;
}
action_b++;
}
ret++;
break;
}
expln_b++;
}
break;
}
}
return (ret);
}
/*
* Remove all port devices that are marked OLD, remove
* corresponding node devices (fc_remote_node_t)
*/
void
{
int index;
int initiator;
/*
* Nuke all OLD devices
*/
continue;
}
/*
* Remove this from the PWWN hash table
*/
} else {
}
head->pwwn_count--;
/*
* Make sure we tie fp_dev_count to the size of the
* pwwn_table
*/
port->fp_dev_count--;
PD_PLOGI_INITIATOR) ? 1 : 0;
} else {
}
if (node) {
}
}
}
}
}
static void
{
return;
}
}
static int
{
int index;
return (FC_SUCCESS);
}
}
return (FC_FAILURE);
}
{
int index;
return (pd);
}
}
}
return (pd);
}
/*
* trace debugging
*/
void
{
int cnt = 0;
return;
}
if (name) {
} else {
}
if (cnt < FC_MAX_TRACE_BUF_LEN) {
}
if (cnt > FC_MAX_TRACE_BUF_LEN) {
}
"error=0x%x\n", errno);
}
}
/*
* We do not want to print the log numbers that appear as
* random numbers at the console and messages files, to
* the user.
*/
/*
* We would have added the a string with "=>" above and so,
* ideally, we should not get here at all. But, if we do,
* we'll just use the full buf.
*/
} else {
bufptr++;
}
switch (dlevel & FC_TRACE_LOG_MASK) {
case FC_TRACE_LOG_CONSOLE:
break;
case FC_TRACE_LOG_CONSOLE_MSG:
break;
case FC_TRACE_LOG_MSG:
break;
default:
break;
}
}
/*
* This function can block
*/
{
return (logq);
}
void
{
}
}
/* ARGSUSED */
void
{
int qfull = 0;
return;
}
return;
}
qfull = 1;
}
if (qfull) {
}
} else {
}
}
static void
{
}
} else {
}
}
/*
* Used by T11 FC-HBA to fetch discovered ports by index.
* Returns NULL if the index isn't valid.
*/
{
int outer;
int match = 0;
for (outer = 0;
outer++) {
}
}
return (pd);
}
/*
* Search for a matching Node or Port WWN in the discovered port list
*/
{
int index;
sizeof (la_wwn_t)) == 0) {
return (pd);
}
sizeof (la_wwn_t)) == 0) {
return (pd);
}
}
}
/* No match */
return (NULL);
}
/*
* Count the number of ports on this adapter.
* This routine will walk the port list and count up the number of adapters
* with matching fp_hba_port_attrs.hba_fru_details.high and
* fp_hba_port_attrs.hba_fru_details.low.
*
* port->fp_mutex must not be held.
*/
int
{
/* Detect FCA drivers that don't support linking HBA ports */
return (1);
}
continue;
}
/*
* If an FCA driver returns unique fru->high and fru->low for
* ports on the same card, there is no way for the transport
* layer to determine that the two ports on the same FRU. So,
* the discovery of the ports on a same FRU is limited to what
* the FCA driver can report back.
*/
/* Now double check driver */
FCHBA_DRIVER_NAME_LEN) == 0) {
if (!npivflag ||
count++;
}
} /* Else, different FCA driver */
} /* Else not the same HBA FRU */
}
return (count);
}
{
return (list);
}
return (newentry);
}
return (list);
}
void
{
return;
}
}
}
/*
* Fetch another port on the HBA FRU based on index.
* Returns NULL if index not found.
*
* port->fp_mutex must not be held.
*/
{
int index, phyPortNum = 0;
/* Are we looking for this port? */
return (port);
}
/* Detect FCA drivers that don't support linking HBA ports */
return (NULL);
}
phyPortNum++;
/* Loop through all known ports */
/* Skip over the port that was passed in as the argument */
continue;
}
/* See if this port is on the same HBA FRU (fast check) */
/* Now double check driver (slower check) */
FCHBA_DRIVER_NAME_LEN) == 0) {
/* Check for the matching port_index */
/* Found it! */
return (tmpPort);
}
phyPortNum++;
}
} /* Else, different FCA driver */
} /* Else not the same HBA FRU */
}
/* scan all physical port on same chip to find virtual port */
while (index < port_index) {
break;
}
continue;
}
} else {
}
} else {
index++;
}
}
if (virPort) {
return (virPort);
}
return (NULL);
}
int
{
/*
* If fctl_busy_port() is called before we've registered our
* PM components, we return success. We need to be aware of
* this because the caller will eventually call fctl_idle_port.
* This wouldn't be a problem except that if we have
* registered our PM components in the meantime, we will
* then be idling a component that was never busied. PM
* will be very unhappy if we do this. Thus, we keep
* track of this with port->fp_pm_busy_nocomp.
*/
return (0);
}
port->fp_pm_busy++;
FP_PM_COMPONENT) != DDI_SUCCESS) {
port->fp_pm_busy--;
return (ENXIO);
}
FP_PM_PORT_UP) != DDI_SUCCESS) {
port->fp_pm_busy--;
return (EIO);
}
return (0);
}
return (0);
}
void
{
/*
* If port->fp_pm_busy_nocomp is > 0, that means somebody had
* called fctl_busy_port prior to us registering our PM components.
* In that case, we just decrement fp_pm_busy_nocomp and return.
*/
if (port->fp_pm_busy_nocomp > 0) {
return;
}
port->fp_pm_busy--;
}
/*
* Function: fctl_tc_timer
*
* Description: Resets the value of the timed counter.
*
* Arguments: *tc Timed counter
*
* Return Value: Nothing
*
* Context: Kernel context.
*/
static void
void *arg
)
{
}
}
/*
* Function: fctl_tc_constructor
*
* Description: Constructs a timed counter.
*
* Arguments: *tc Address where the timed counter will reside.
* max_value Maximum value the counter is allowed to take.
* timer Number of microseconds after which the counter
* will be reset. The timer is started when the
* value of the counter goes from 0 to 1.
*
* Return Value: Nothing
*
* Context: Kernel context.
*/
void
)
{
}
/*
* Function: fctl_tc_destructor
*
* Description: Destroyes a timed counter.
*
* Arguments: *tc Timed counter to destroy.
*
* Return Value: Nothing
*
* Context: Kernel context.
*/
void
)
{
}
}
/*
* Function: fctl_tc_increment
*
* Description: Increments a timed counter
*
* Arguments: *tc Timed counter to increment.
*
* Return Value: B_TRUE Counter reached the max value.
* B_FALSE Counter hasn't reached the max value.
*
* Context: Kernel or interrupt context.
*/
)
{
/* Hasn't maxed out yet. */
/* Just maxed out. */
}
}
}
}
/*
* Function: fctl_tc_reset
*
* Description: Resets a timed counter. The caller of this function has to
* to make sure that while in fctl_tc_reset() fctl_tc_increment()
* is not called.
*
* Arguments: *tc Timed counter to reset.
*
* Return Value: 0 Counter reached the max value.
* Not 0 Counter hasn't reached the max value.
*
* Context: Kernel or interrupt context.
*/
void
)
{
}
}
void
{
KM_SLEEP) != DDI_SUCCESS) {
return;
}
goto error;
}
sizeof (la_wwn_t)) != DDI_SUCCESS) {
goto error;
}
(type == FC_ULP_DEVICE_ONLINE) ?
return;
}