/*
* 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.
*/
/*
* SunOS 5.x Multithreaded STREAMS DLPI FCIP Module
* This is a pseudo driver module to handle encapsulation of IP and ARP
* datagrams over FibreChannel interfaces. FCIP is a cloneable STREAMS
* is a Style-2 DLS provider.
*
* The implementation of this module is based on RFC 2625 which gives
* The fcip module needs to resolve an IP address to a port address before
* sending data to a destination port. A FC device port has 2 addresses
* associated with it: A 8 byte World Wide unique Port Name and a 3 byte
* volatile Port number or Port_ID.
*
* The mapping between a IP address and the World Wide Port Name is handled
* by the ARP layer since the IP over FC draft requires the MAC address to
* be the least significant six bytes of the WorldWide Port Names. The
* fcip module however needs to identify the destination port uniquely when
* the destination FC device has multiple FC ports.
*
* The FC layer mapping between the World Wide Port Name and the Port_ID
* will be handled through the use of a fabric name server or through the
* use of the FARP ELS command as described in the draft. Since the Port_IDs
* are volatile, the mapping between the World Wide Port Name and Port_IDs
* must be maintained and validated before use each time a datagram
* needs to be sent to the destination ports. The FC transport module
* informs the fcip module of all changes to states of ports on the
* fabric through registered callbacks. This enables the fcip module
* to maintain the WW_PN to Port_ID mappings current.
*
* For details on how this module interfaces with the FibreChannel Transport
* modules, refer to PSARC/1997/385. Chapter 3 of the FibreChannel Transport
* Programming guide details the APIs between ULPs and the Transport.
*
* Now for some Caveats:
*
* RFC 2625 requires that a FibreChannel Port name (the Port WWN) have
* the NAA bits set to '0001' indicating a IEEE 48bit address which
* corresponds to a ULA (Universal LAN MAC address). But with FibreChannel
* adapters containing 2 or more ports, IEEE naming cannot identify the
* ports on an adapter uniquely so we will in the first implementation
* be operating only on Port 0 of each adapter.
*/
#include <sys/sysmacros.h>
#include <sys/ethernet.h>
/*
* Leadville includes
*/
/*
*/
#if defined(lint) || defined(FCIP_TNF_ENABLED)
#include <sys/tnf_probe.h>
#endif
#define FCIP_ESBALLOC
/*
* Function prototypes
*/
/* standard loadable modules entry points */
/* streams specific */
struct fcipstr *(*acceptfunc)());
static int fcip_stat_update(kstat_t *, int);
/* dlpi specific */
/*
* ulp - transport interface function prototypes
*/
/* Routing table specific */
static void fcip_rte_remove_deferred(void *arg);
/* dest table specific */
struct fcip_routing_table *frp);
/* helper functions */
static void fcip_check_remove_minor_node(void);
static int fcip_is_supported_fc_topology(int fc_topology);
/* pkt specific */
static void fcip_pkt_timeout(void *arg);
static void fcip_timeout(void *arg);
static void fcip_sendup_thr(void *arg);
struct fcipstr *(*f)());
/*
* zero copy inbound data handling
*/
#ifdef FCIP_ESBALLOC
static void fcip_ubfree(char *arg);
#endif /* FCIP_ESBALLOC */
#if !defined(FCIP_ESBALLOC)
#endif
/* FCIP FARP support functions */
int is_els);
static void fcip_port_ns(void *arg);
#ifdef DEBUG
#else /* DEBUG */
#endif /* DEBUG */
/*
* Endian independent ethernet to WWN copy
*/
#define ether_to_wwn(E, W) \
(W)->raw_wwn[0] |= 0x10
/*
* wwn_to_ether : Endian independent, copies a WWN to struct ether_addr.
* The args to the macro are pointers to WWN and ether_addr structures
*/
#define wwn_to_ether(W, E) \
/*
* The module_info structure contains identification and limit values.
* All queues associated with a certain driver share the same module_info
* structures. This structure defines the characteristics of that driver/
* module's queues. The module name must be unique. The max and min packet
* sizes limit the no. of characters in M_DATA messages. The Hi and Lo
* water marks are for flow control when a module has a service procedure.
*/
FCIPIDNUM, /* mi_idnum : Module ID num */
FCIPNAME, /* mi_idname: Module Name */
FCIPMINPSZ, /* mi_minpsz: Min packet size */
FCIPMAXPSZ, /* mi_maxpsz: Max packet size */
FCIPHIWAT, /* mi_hiwat : High water mark */
FCIPLOWAT /* mi_lowat : Low water mark */
};
/*
* The qinit structres contain the module put, service. open and close
* procedure pointers. All modules and drivers with the same streamtab
* file (i.e same fmodsw or cdevsw entry points) point to the same
* upstream (read) and downstream (write) qinit structs.
*/
NULL, /* qi_putp */
NULL, /* qi_srvp */
fcip_open, /* qi_qopen */
fcip_close, /* qi_qclose */
NULL, /* qi_qadmin */
&fcipminfo, /* qi_minfo */
NULL /* qi_mstat */
};
fcip_wput, /* qi_putp */
fcip_wsrv, /* qi_srvp */
NULL, /* qi_qopen */
NULL, /* qi_qclose */
NULL, /* qi_qadmin */
&fcipminfo, /* qi_minfo */
NULL /* qi_mstat */
};
/*
* streamtab contains pointers to the read and write qinit structures
*/
&fcip_rinit, /* st_rdinit */
&fcip_winit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL, /* st_muxwrinit */
};
nodev, /* open */
nodev, /* close */
nodev, /* strategy */
nodev, /* print */
nodev, /* dump */
nodev, /* read */
nodev, /* write */
nodev, /* ioctl */
nodev, /* devmap */
nodev, /* mmap */
nodev, /* segmap */
nochpoll, /* poll */
ddi_prop_op, /* cb_prop_op */
&fcip_info, /* streamtab */
CB_REV, /* rev */
nodev, /* int (*cb_aread)() */
nodev /* int (*cb_awrite)() */
};
/*
* autoconfiguration routines.
*/
DEVO_REV, /* devo_rev, */
0, /* refcnt */
fcip_getinfo, /* info */
nulldev, /* identify */
nulldev, /* probe */
fcip_attach, /* attach */
fcip_detach, /* detach */
nodev, /* RESET */
&fcip_cb_ops, /* driver operations */
NULL, /* bus operations */
ddi_power /* power management */
};
KM_SLEEP))
&mod_driverops, /* Type of module - driver */
FCIP_NAME, /* Name of module */
&fcip_ops, /* driver ops */
};
};
/*
* Now for some global statics
*/
static int fcip_num_attaching = 0;
static int fcip_port_attach_pending = 0;
static int fcip_cache_on_arp_broadcast = 0;
static int fcip_farp_supported = 0;
static int fcip_minor_node_created = 0;
/*
* Supported FCAs
*/
QLC_MODULE_NAME) == 0)
/*
* fcip softstate structures head.
*/
/*
* linked list of active (inuse) driver streams
*/
static int fcip_num_instances = 0;
/*
* Ethernet broadcast address: Broadcast addressing in IP over fibre
* channel should be the IEEE ULA (also the low 6 bytes of the Port WWN).
*
* The broadcast addressing varies for differing topologies a node may be in:
* - On a private loop the ARP broadcast is a class 3 sequence sent
* using OPNfr (Open Broadcast Replicate primitive) followed by
* the ARP frame to D_ID 0xFFFFFF
*
* - On a public Loop the broadcast sequence is sent to AL_PA 0x00
* (no OPNfr primitive).
*
* - For direct attach and point to point topologies we just send
* the frame to D_ID 0xFFFFFF
*
* For public loop the handling would probably be different - for now
* I'll just declare this struct - It can be deleted if not necessary.
*
*/
/*
* DL_INFO_ACK template for the fcip module. The dl_info_ack_t structure is
* returned as a part of an DL_INFO_ACK message which is a M_PCPROTO message
* returned in response to a DL_INFO_REQ message sent to us from a DLS user
* Let us fake an ether header as much as possible.
*
* dl_addr_length is the Provider's DLSAP addr which is SAP addr +
* Physical addr of the provider. We set this to
* ushort_t + sizeof (la_wwn_t) for Fibre Channel ports.
* dl_mac_type Lets just use DL_ETHER - we can try using DL_IPFC, a new
* dlpi.h define later.
* dl_sap_length -2 indicating the SAP address follows the Physical addr
* component in the DLSAP addr.
* dl_service_mode: DLCLDS - connectionless data link service.
*
*/
DL_INFO_ACK, /* dl_primitive */
FCIPMTU, /* dl_max_sdu */
0, /* dl_min_sdu */
FCIPADDRL, /* dl_addr_length */
DL_ETHER, /* dl_mac_type */
0, /* dl_reserved */
0, /* dl_current_state */
-2, /* dl_sap_length */
DL_CLDLS, /* dl_service_mode */
0, /* dl_qos_length */
0, /* dl_qos_offset */
0, /* dl_range_length */
0, /* dl_range_offset */
DL_STYLE2, /* dl_provider_style */
sizeof (dl_info_ack_t), /* dl_addr_offset */
DL_VERSION_2, /* dl_version */
ETHERADDRL, /* dl_brdcst_addr_length */
0 /* dl_growth */
};
/*
* FCIP broadcast address definition.
*/
0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
/*
* RFC2625 requires the broadcast ARP address in the ARP data payload to
* be set to 0x00 00 00 00 00 00 for ARP broadcast packets
*/
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/*
* global kernel locks
*/
/*
* fctl external defines
*/
extern int fc_ulp_add(fc_ulp_modinfo_t *);
/*
* fctl data structures
*/
/* linked list of port info structures */
/* linked list of fcip structures */
/*
* Module information structure. This structure gives the FC Transport modules
* information about an ULP that registers with it.
*/
0, /* for xref checks? */
FCTL_ULP_MODREV_4, /* FCIP revision */
FC_TYPE_IS8802_SNAP, /* type 5 for SNAP encapsulated datagrams */
FCIP_NAME, /* module name as in the modldrv struct */
0x0, /* get all statec callbacks for now */
fcip_port_attach, /* port attach callback */
fcip_port_detach, /* port detach callback */
fcip_port_ioctl, /* port ioctl callback */
fcip_els_cb, /* els callback */
fcip_data_cb, /* data callback */
fcip_statec_cb /* state change callback */
};
/*
*
* ddi-forceattach=1;
*
* This will ensure that fp is loaded at bootup. No additional checks are needed
*/
int
_init(void)
{
int rval;
/*
* Initialize the mutexs used by port attach and other callbacks.
* The transport can call back into our port_attach_callback
* routine even before _init() completes and bad things can happen.
*/
/*
* Now attempt to register fcip with the transport.
* If fc_ulp_add fails, fcip module will not be loaded.
*/
if (rval != FC_SUCCESS) {
switch (rval) {
case FC_ULP_SAMEMODULE:
"!fcip: module is already registered with"
" transport"));
break;
case FC_ULP_SAMETYPE:
"!fcip: Another module of the same ULP type 0x%x"
" is already registered with the transport",
break;
case FC_BADULP:
"!fcip: Current fcip version 0x%x does not match"
" fctl version",
break;
default:
"!fcip: fc_ulp_add failed with status 0x%x", rval));
break;
}
return (rval);
}
FCIP_NUM_INSTANCES)) != 0) {
(void) fc_ulp_remove(&fcip_modinfo);
return (rval);
}
(void) fc_ulp_remove(&fcip_modinfo);
}
return (rval);
}
/*
* Unload the port driver if this was the only ULP loaded and then
* deregister with the transport.
*/
int
_fini(void)
{
int rval;
int rval1;
/*
* Do not permit the module to be unloaded before a port
* attach callback has happened.
*/
if (fcip_num_attaching || fcip_port_attach_pending) {
return (EBUSY);
}
return (rval);
}
/*
* unregister with the transport layer
*/
/*
* If the ULP was not registered with the transport, init should
* have failed. If transport has no knowledge of our existence
* we should simply bail out and succeed
*/
#ifdef DEBUG
"fcip: ULP was never registered with the transport"));
} else if (rval1 == FC_BADTYPE) {
"fcip: No ULP of this type 0x%x was registered with "
}
#endif /* DEBUG */
return (rval);
}
/*
* Info about this loadable module
*/
int
{
}
/*
* The port attach callback is invoked by the port driver when a FCA
* port comes online and binds with the transport layer. The transport
* then callsback into all ULP modules registered with it. The Port attach
* call back will also provide the ULP module with the Port's WWN and S_ID
*/
/* ARGSUSED */
static int
{
int instance;
switch (cmd) {
case FC_CMD_ATTACH: {
/*
* It was determined that, as per spec, the lower 48 bits of
* the port-WWN will always be unique. This will make the MAC
* depend on, unique too. Hence we should be able to remove the
* restriction of attaching to only one of the ports of
* multi port FCAs.
*
* Earlier, fcip used to attach only to qlc module and fail
* silently for attach failures resulting from unknown FCAs or
* unsupported FCA ports. Now, we'll do no such checks.
*/
/*
* A port has come online
*/
if (fcip_port_head == NULL) {
/* OK to sleep here ? */
ASSERT(fcip_num_attaching >= 0);
rval = FC_FAILURE;
"failed: alloc failed",
goto done;
}
} else {
/*
* traverse the port list and also check for
* duplicate port attaches - Nothing wrong in being
* paranoid Heh Heh.
*/
if (cur_fport->fcipp_handle ==
port_info->port_handle) {
ASSERT(fcip_num_attaching >= 0);
"!fcip(%d): port already "
"attached!!", ddi_get_instance(
rval = FC_FAILURE;
goto done;
}
}
rval = FC_FAILURE;
ASSERT(fcip_num_attaching >= 0);
"failed: alloc failed",
goto done;
}
}
/*
* now fill in the details about the port itself
*/
sizeof (la_wwn_t));
sizeof (la_wwn_t));
src_id.priv_lilp_posit = 0;
/*
* allocate soft state for this instance
*/
instance) != DDI_SUCCESS) {
rval = FC_FAILURE;
"soft state alloc failed", instance);
goto failure;
}
rval = FC_FAILURE;
"failure to get soft state", instance);
goto failure;
}
/*
* initialize all mutexes and locks required for this module
*/
fptr->fcip_ub_upstream = 0;
}
} else {
}
/*
* Extract our MAC addr from our port's WWN. The lower 48
* bits will be our MAC address
*/
(CE_NOTE, "fcipdest : 0x%lx, rtable : 0x%lx",
(long)(sizeof (fptr->fcip_rtable))));
/*
* create a taskq to handle sundry jobs for the driver
* This way we can have jobs run in parallel
*/
/*
* create a separate thread to handle all unsolicited
* callback handling. This is because unsolicited_callback
* can happen from an interrupt context and the upstream
* modules can put new messages right back in the same
* thread context. This usually works fine, but sometimes
* we may have to block to obtain the dest struct entries
* for some remote ports.
*/
"!unable to create fcip sendup thread for "
" instance: 0x%x", instance);
rval = FC_FAILURE;
goto done;
}
/* Let the attach handler do the rest */
/*
* We have already cleaned up so return
*/
rval = FC_FAILURE;
instance);
goto done;
}
"!fcip attach for port instance (0x%x) successful",
instance));
rval = FC_SUCCESS;
goto done;
}
case FC_CMD_POWER_UP:
/* FALLTHROUGH */
case FC_CMD_RESUME:
break;
}
}
rval = FC_SUCCESS;
goto done;
}
goto done;
default:
"unknown cmd type 0x%x in port_attach", cmd));
rval = FC_FAILURE;
goto done;
}
if (fport) {
ASSERT(fcip_num_attaching >= 0);
(void) fcip_softstate_free(fport);
}
return (rval);
done:
return (rval);
}
/*
* fcip_port_attach_handler : Completes the port attach operation after
* the ulp_port_attach routine has completed its ground work. The job
* of this function among other things is to obtain and handle topology
* specifics, initialize a port, setup broadcast address entries in
* the fcip tables etc. This routine cleans up behind itself on failures.
* Returns FC_SUCCESS or FC_FAILURE.
*/
static int
{
"fcip module dip: %p instance: %d",
if (fcip_module_dip == NULL) {
fcip_lbolt = ddi_get_lbolt();
/*
* we need to use the fcip devinfo for creating
* the clone device node, but the fcip attach
* (from its conf file entry claiming to be a
* child of pseudo) may not have happened yet.
* wait here for 10 seconds and fail port attach
* if the fcip devinfo is not attached yet
*/
if (fcip_module_dip == NULL) {
"fcip attach did not happen"));
goto port_attach_cleanup;
}
}
if ((!fcip_minor_node_created) &&
/*
* Checking for same topologies which are considered valid
* by fcip_handle_topology(). Dont create a minor node if
* nothing is hanging off the FC port.
*/
CLONE_DEV) == DDI_FAILURE) {
"failed to create minor node for fcip(%d)",
goto port_attach_cleanup;
}
}
/*
* initialize port for traffic
*/
/* fcip_init_port has already cleaned up its stuff */
if ((fcip_num_instances == 1) &&
(fcip_minor_node_created == 1)) {
/* Remove minor node iff this is the last instance */
}
goto port_attach_cleanup;
}
fptr->fcip_timeout_ticks = 0;
/*
* start the timeout threads
*/
drv_usectohz(1000000));
ASSERT(fcip_num_attaching >= 0);
rval = FC_SUCCESS;
return (rval);
(void) fcip_softstate_free(fport);
ASSERT(fcip_num_attaching >= 0);
rval = FC_FAILURE;
return (rval);
}
/*
* Handler for DDI_RESUME operations. Port must be ready to restart IP
* traffic on resume
*/
static int
{
int index;
if (cmd == FC_CMD_POWER_UP) {
return (FC_SUCCESS);
}
} else if (cmd == FC_CMD_RESUME) {
} else {
return (FC_FAILURE);
}
/*
* set the current port state and topology
*/
break;
}
}
/*
* No active streams on this port
*/
rval = FC_SUCCESS;
goto done;
}
while (frp) {
/*
* Mark the broadcast RTE available again. It
* was marked SUSPENDED during SUSPEND.
*/
frp->fcipr_state = 0;
break;
}
}
}
/*
* fcip_handle_topology will update the port entries in the
* routing table.
* fcip_handle_topology also takes care of resetting the
* fcipr_state field in the routing table structure. The entries
* were set to RT_INVALID during suspend.
*/
done:
/*
* Restart the timeout thread
*/
drv_usectohz(1000000));
return (rval);
}
/*
* Insert a destination port entry into the routing table for
* this port
*/
static void
{
int hash_bucket, i;
for (i = 0; i < listlen; i++) {
/*
* If an entry for a port in the devlist exists in the
* in the per port routing table, make sure the data
* is current. We need to do this irrespective of the
* underlying port topology.
*/
/* FALLTHROUGH */
case PORT_DEVICE_NOCHANGE:
/* FALLTHROUGH */
case PORT_DEVICE_USER_LOGIN:
/* FALLTHROUGH */
case PORT_DEVICE_CHANGED:
/* FALLTHROUGH */
case PORT_DEVICE_NEW:
goto add_new_entry;
} else if (frp) {
goto update_entry;
} else {
continue;
}
case PORT_DEVICE_OLD:
/* FALLTHROUGH */
case PORT_DEVICE_USER_LOGOUT:
/*
* Mark entry for removal from Routing Table if
* one exists. Let the timeout thread actually
* remove the entry after we've given up hopes
* of the port ever showing up.
*/
if (frp) {
/*
* Mark the routing table as invalid to bail
* the packets early that are in transit
*/
}
}
continue;
default:
"unknown map flags in rt_update"));
continue;
}
"add new entry",
frp = (struct fcip_routing_table *)
/* insert at beginning of hash bucket */
"added entry for pwwn %s and d_id 0x%x",
sizeof (la_wwn_t));
/*
* If there is no pd for a destination port that is not
* a broadcast entry, the port is pretty much unusable - so
* mark the port for removal so we can try adding back the
* entry again.
*/
(FCIP_RTE_TIMEOUT / 2);
}
frp->fcipr_fca_dev =
/*
* login to the remote port. Don't worry about
* plogi failures for now
*/
"logging into pwwn %s, d_id 0x%x",
}
"new wwn in rt", pwwn,
}
}
/*
* return a matching routing table entry for a given fcip instance
*/
struct fcip_routing_table *
{
int hash_bucket;
"rtable lookup for", wwn,
"rtable entry", nwwn,
break;
}
}
return (frp);
}
/*
* Attach of fcip under pseudo. The actual setup of the interface
* actually happens in fcip_port_attach on a callback from the
* transport. The port_attach callback however can proceed only
* after the devinfo for fcip has been created under pseudo
*/
static int
{
switch ((int)cmd) {
case DDI_ATTACH: {
/*
* this call originates as a result of fcip's conf
* file entry and will result in a fcip instance being
* a child of pseudo. We should ensure here that the port
* driver (fp) has been loaded and initted since we would
* never get a port attach callback without fp being loaded.
* If we are unable to succesfully load and initalize fp -
* just fail this attach.
*/
(CE_WARN, "global cv - signaling"));
(CE_WARN, "global cv - signaled"));
return (DDI_SUCCESS);
}
case DDI_RESUME:
/*
* Resume appears trickier
*/
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*
* The detach entry point to permit unloading fcip. We make sure
* there are no active streams before we proceed with the detach
*/
/* ARGSUSED */
static int
{
int detached;
switch (cmd) {
case DDI_DETACH: {
/*
* If we got here, any active streams should have been
* unplumbed but check anyway
*/
return (DDI_FAILURE);
}
if (fcip_port_head != NULL) {
/*
* Check to see if we have unattached/unbound
* ports. If all the ports are unattached/unbound go
* ahead and unregister with the transport
*/
continue;
}
"fcip io", /* CSTYLED */,
"fcip instance busy"));
"fcip instance busy"));
break;
}
/*
* Check for any outstanding pkts. If yes
* fail the detach
*/
if (fcip_port_get_num_pkts(fptr) > 0) {
"fcip instance busy - pkts "
"pending"));
break;
}
if (fcip_plogi_in_progress(fptr)) {
"fcip instance busy - plogi in "
"progress"));
break;
}
}
/*
* if fport is non NULL - we have active ports
*/
/*
* Remove the DETACHING flags on the ports
*/
}
return (DDI_FAILURE);
}
}
/*
* free up all softstate structures
*/
detached = 1;
if (fptr) {
/*
* Check to see if somebody beat us to the
* punch
*/
}
if (!detached) {
} else {
/*
* If the port was marked as detached
* but it was still in the list, that
* means another thread has marked it
* but we got in while it released the
* fcip_global_mutex in softstate_free.
* Given that, we're still safe to use
* fport->fcipp_next to find out what
* the next port on the list is.
*/
}
(CE_NOTE, "detaching port"));
msg, "detaching port"));
}
/*
* If we haven't removed all the port structures, we
* aren't yet ready to be detached.
*/
if (fcip_port_head != NULL) {
return (DDI_FAILURE);
}
fcip_num_instances = 0;
return (DDI_SUCCESS);
}
case DDI_SUSPEND:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*
* The port_detach callback is called from the transport when a
* FC port is being removed from the transport's control. This routine
* provides fcip with an opportunity to cleanup all activities and
* structures on the port marked for removal.
*/
/* ARGSUSED */
static int
{
switch (cmd) {
case FC_CMD_DETACH: {
if (fcip_port_head == NULL) {
/*
* we are all done but our fini has not been
* called yet!! Let's hope we have no active
* fcip instances here. - strange secnario but
* no harm in having this return a success.
*/
return (FC_SUCCESS);
} else {
/*
* traverse the port list
*/
if (fport->fcipp_handle ==
port_info->port_handle) {
/*
* Fail the port detach if there is
* still an attached, bound stream on
* this interface.
*/
return (FC_FAILURE);
}
}
/*
* fail port detach if we are in
* the middle of a deferred port attach
* or if the port has outstanding pkts
*/
(fptr) ||
(fptr->fcip_flags &
FCIP_DETACHED)) {
&fptr->fcip_mutex);
return (FC_FAILURE);
}
fptr->fcip_flags |=
}
(void) fcip_softstate_free(fport);
return (FC_SUCCESS);
}
}
}
break;
}
case FC_CMD_POWER_DOWN:
/* FALLTHROUGH */
case FC_CMD_SUSPEND:
break;
}
}
break;
}
break;
default:
(CE_WARN, "unknown port detach command!!"));
break;
}
return (rval);
}
/*
* Returns 0 if the port is not busy, else returns non zero.
*/
static int
{
rval = 1;
(CE_NOTE, "!fcip_check_port_busy: port is busy "
"fcip_flags: 0x%x, num_pkts: 0x%x, ipkts_pending: 0x%lx!",
}
return (rval);
}
/*
* Helper routine to remove fcip's minor node
* There is one minor node per system and it should be removed if there are no
* other fcip instances (which has a 1:1 mapping for fp instances) present
*/
static void
{
/*
* If there are no more fcip (fp) instances, remove the
* minor node for fcip.
* Reset fcip_minor_node_created to invalidate it.
*/
}
}
/*
* on all active interfaces
*/
static int
{
int index;
int tryagain = 0;
int count;
/*
* Fail if we are in the middle of a callback. Don't use delay during
* suspend since clock intrs are not available so busy wait
*/
count = 0;
while (count++ < 15 &&
drv_usecwait(1000000);
}
return (FC_FAILURE);
}
if (cmd == FC_CMD_POWER_DOWN) {
goto success;
} else {
}
} else if (cmd == FC_CMD_SUSPEND) {
} else {
return (FC_FAILURE);
}
/*
* If no streams are plumbed - its the easiest case - Just
* bail out without having to do much
*/
break;
}
}
/*
* No active streams on this port
*/
goto success;
}
/*
* Walk through each Routing table structure and check if
* the destination table has any outstanding commands. If yes
* wait for the commands to drain. Since we go through each
* routing table entry in succession, it may be wise to wait
* only a few seconds for each entry.
*/
while (!tryagain) {
tryagain = 0;
int hash_bucket;
while (frp) {
/*
* Mark the routing table as SUSPENDED. Even
* mark the broadcast entry SUSPENDED to
* prevent any ARP or other broadcasts. We
* can reset the state of the broadcast
* RTE when we resume.
*/
/*
* Get hold of destination pointer
*/
if (fdestp->fcipd_rtable) {
if (fcip_wwn_compare(pwwn,
&fdestp->fcipd_pwwn,
FCIP_COMPARE_PWWN) == 0) {
&fdestp->fcipd_mutex);
break;
}
}
}
continue;
}
/*
* Wait for fcip_wait_cmds seconds for
* the commands to drain.
*/
count = 0;
while (fdestp->fcipd_ncmds &&
count < fcip_wait_cmds) {
drv_usecwait(1000000);
count++;
}
/*
* Check if we were able to drain all cmds
* successfully. Else continue with other
* ports and try during the second pass
*/
if (fdestp->fcipd_ncmds) {
tryagain++;
}
}
}
if (tryagain == 0) {
break;
}
}
if (tryagain) {
return (FC_FAILURE);
}
return (FC_SUCCESS);
}
/*
* the getinfo(9E) entry point
*/
/* ARGSUSED */
static int
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if (*result)
rval = DDI_SUCCESS;
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
rval = DDI_SUCCESS;
break;
default:
break;
}
return (rval);
}
/*
* called from fcip_attach to initialize kstats for the link
*/
/* ARGSUSED */
static void
{
int instance;
#ifdef kstat
(sizeof (struct fcipstat)/ sizeof (kstat_named_t)),
#else
(sizeof (struct fcipstat)/ sizeof (kstat_named_t)), 0);
#endif
return;
}
/*
* required by kstat for MIB II objects(RFC 1213)
*/
KSTAT_DATA_ULONG); /* # octets received */
/* MIB - ifInOctets */
KSTAT_DATA_ULONG); /* # octets xmitted */
/* MIB - ifOutOctets */
KSTAT_DATA_ULONG); /* # multicast packets */
/* delivered to upper layer */
/* MIB - ifInNUcastPkts */
KSTAT_DATA_ULONG); /* # multicast packets */
/* requested to be sent */
/* MIB - ifOutNUcastPkts */
KSTAT_DATA_ULONG); /* # broadcast packets */
/* delivered to upper layer */
/* MIB - ifInNUcastPkts */
KSTAT_DATA_ULONG); /* # broadcast packets */
/* requested to be sent */
/* MIB - ifOutNUcastPkts */
KSTAT_DATA_ULONG); /* # rcv packets discarded */
/* MIB - ifInDiscards */
KSTAT_DATA_ULONG); /* # xmt packets discarded */
}
/*
* Update the defined kstats for netstat et al to use
*/
/* ARGSUSED */
static int
{
if (val == KSTAT_WRITE) {
} else {
}
return (0);
}
/*
* fcip_statec_cb: handles all required state change callback notifications
* it receives from the transport
*/
/* ARGSUSED */
static void
{
int instance;
int index;
return;
}
return;
}
(CE_NOTE, "fcip%d, state change callback: state:0x%x, "
return;
}
/*
* set fcip flags to indicate we are in the middle of a
* state change callback so we can wait till the statechange
* is handled before succeeding/failing the SUSPEND/POWER DOWN.
*/
/*
* Check if topology changed. If Yes - Modify the broadcast
* RTE entries to understand the new broadcast D_IDs
*/
(port_top != FC_TOP_UNKNOWN)) {
/* REMOVE later */
"topology changed: Old topology: 0x%x New topology 0x%x",
/*
* If topology changed - attempt a rediscovery of
* and if on_demand_node_creation is disabled
*/
}
switch (FC_PORT_STATE_MASK(port_state)) {
case FC_STATE_ONLINE:
/* FALLTHROUGH */
case FC_STATE_LIP:
/* FALLTHROUGH */
case FC_STATE_LIP_LBIT_SET:
/*
* nothing to do here actually other than if we
* were actually logged onto a port in the devlist
* (which indicates active communication between
* the host port and the port in the changelist).
* If however we are in a private loop or point to
* point mode, we need to check for any IP capable
* ports and update our routing table.
*/
switch (port_top) {
case FC_TOP_FABRIC:
/*
* This indicates a fabric port with a NameServer.
* Check the devlist to see if we are in active
* communication with a port on the devlist.
*/
"Statec_cb: fabric topology"));
break;
case FC_TOP_NO_NS:
/*
* No nameserver - so treat it like a Private loop
* or point to point topology and get a map of
* devices on the link and get IP capable ports to
* to update the routing table.
*/
(CE_NOTE, "Statec_cb: NO_NS topology"));
/* FALLTHROUGH */
case FC_TOP_PRIVATE_LOOP:
"Statec_cb: Pvt_Loop topology"));
/* FALLTHROUGH */
case FC_TOP_PT_PT:
/*
* call get_port_map() and update routing table
*/
break;
default:
(CE_NOTE, "Statec_cb: Unknown topology"));
}
/*
* to flow through downstream. The update of routing
* table should have flushed out any port entries that
* don't exist or are not available after the state change
*/
}
/*
* Enable write queues
*/
}
}
}
break;
case FC_STATE_OFFLINE:
/*
* mark the port_state OFFLINE and wait for it to
* become online. Any new messages in this state will
* simply be queued back up. If the port does not
* come online in a short while, we can begin failing
* messages and flush the routing table
*/
/*
* Mark all Routing table entries as invalid to prevent
* any commands from trickling through to ports that
* have disappeared from under us
*/
while (frtp) {
}
}
break;
case FC_STATE_RESET_REQUESTED:
/*
* This also means the port state is marked offline - so
* we may have to do what OFFLINE state requires us to do.
* Care must be taken to wait for any active unsolicited
* buffer with the other Streams modules - so wait for
* a freeb if the unsolicited buffer is passed back all
* the way upstream.
*/
#ifdef FCIP_ESBALLOC
while (fptr->fcip_ub_upstream) {
}
#endif /* FCIP_ESBALLOC */
break;
case FC_STATE_DEVICE_CHANGE:
if (listlen) {
}
break;
case FC_STATE_RESET:
/*
* Not much to do I guess - wait for port to become
* ONLINE. If the port doesn't become online in a short
* while, the upper layers abort any request themselves.
* We can just putback the messages in the streams queues
* if the link is offline
*/
break;
}
}
/*
* Given a port handle, return the fcip_port_info structure corresponding
* to that port handle. The transport allocates and communicates with
* ULPs using port handles
*/
static fcip_port_info_t *
{
/* found */
break;
}
}
return (fport);
}
/*
* Handle inbound ELS requests received by the transport. We are only
*/
/* ARGSUSED */
static int
{
int instance;
return (FC_UNCLAIMED);
}
return (FC_UNCLAIMED);
}
return (FC_UNCLAIMED);
}
/*
* set fcip flags to indicate we are in the middle of a
* ELS callback so we can wait till the statechange
* is handled before succeeding/failing the SUSPEND/POWER DOWN.
*/
switch (r_ctl & R_CTL_ROUTING) {
case R_CTL_EXTENDED_SVC:
if (r_ctl == R_CTL_ELS_REQ) {
if (ls_code == LA_ELS_FARP_REQ) {
/*
* Inbound FARP broadcast request
*/
"Invalid FARP req buffer size "
"expected 0x%lx, got 0x%x",
(long)(sizeof (la_els_farp_t)),
buf->ub_bufsize));
rval = FC_UNCLAIMED;
goto els_cb_done;
}
&fport->fcipp_nwwn,
FCIP_COMPARE_NWWN) != 0) {
rval = FC_UNCLAIMED;
goto els_cb_done;
}
/*
* copy the FARP request and release the
* unsolicited buffer
*/
sizeof (la_els_farp_t));
if (fcip_farp_supported &&
FC_SUCCESS) {
/*
* We successfully sent out a FARP
* reply to the requesting port
*/
rval = FC_SUCCESS;
goto els_cb_done;
} else {
rval = FC_UNCLAIMED;
goto els_cb_done;
}
}
} else if (r_ctl == R_CTL_ELS_RSP) {
if (ls_code == LA_ELS_FARP_REPLY) {
/*
* We received a REPLY to our FARP request
*/
"Invalid FARP req buffer size "
"expected 0x%lx, got 0x%x",
(long)(sizeof (la_els_farp_t)),
buf->ub_bufsize));
rval = FC_UNCLAIMED;
goto els_cb_done;
}
sizeof (la_els_farp_t));
if (fcip_farp_supported &&
FC_SUCCESS) {
"Successfully recevied a FARP "
"response"));
rval = FC_SUCCESS;
goto els_cb_done;
} else {
"Unable to handle a FARP response "
"receive"));
rval = FC_UNCLAIMED;
goto els_cb_done;
}
}
}
break;
default:
break;
}
return (rval);
}
/*
* Handle inbound FARP requests
*/
static int
{
/*
* Add an entry for the remote port into our routing and destination
* tables.
*/
sizeof (la_wwn_t));
sizeof (la_wwn_t));
rval = FC_FAILURE;
goto farp_done;
}
/*
* Fill in our port's PWWN and NWWN
*/
fca_dev =
/*
* Attempt a PLOGI again
*/
/*
* Login to the remote port failed. There is no
* point continuing with the FARP request further
* so bail out here.
*/
rval = FC_FAILURE;
goto farp_done;
}
}
sizeof (la_els_farp_t));
if (rval != FC_SUCCESS) {
"fcip_transport of farp reply failed",
"fcip_transport of farp reply failed 0x%x", rval));
}
return (rval);
}
/*
* Handle FARP responses to our FARP requests. When we receive a FARP
* reply, we need to add the entry for the Port that replied into our
* routing and destination hash tables. It is possible that the remote
* port did not login into us (FARP responses can be received without
* a PLOGI)
*/
static int
{
/*
* Add an entry for the remote port into our routing and destination
* tables.
*/
sizeof (la_wwn_t));
sizeof (la_wwn_t));
rval = FC_SUCCESS;
}
return (rval);
}
#define FCIP_HDRS_LENGTH \
/*
* fcip_data_cb is the heart of most IP operations. This routine is called
* by the transport when any unsolicited IP data arrives at a port (which
* is almost all IP data). This routine then strips off the Network header
* from the payload (after authenticating the received payload ofcourse),
* creates a message blk and sends the data upstream. You will see ugly
* #defines because of problems with using esballoc() as opposed to
* allocb to prevent an extra copy of data. We should probably move to
* esballoc entirely when the MTU eventually will be larger than 1500 bytes
* since copies will get more expensive then. At 1500 byte MTUs, there is
* no noticable difference between using allocb and esballoc. The other
* caveat is that the qlc firmware still cannot tell us accurately the
* no. of valid bytes in the unsol buffer it DMA'ed so we have to resort
* to looking into the IP header and hoping that the no. of bytes speficified
* in the header was actually received.
*/
/* ARGSUSED */
static int
{
int rval;
#ifdef FCIP_ESBALLOC
#endif /* FCIP_ESBALLOC */
return (FC_UNCLAIMED);
}
return (FC_UNCLAIMED);
}
rval = FC_UNCLAIMED;
goto data_cb_done;
}
/*
* set fcip flags to indicate we are in the middle of a
* data callback so we can wait till the statechange
* is handled before succeeding/failing the SUSPEND/POWER DOWN.
*/
(CE_NOTE, "fcip%d, data callback",
/*
* get to the network and snap headers in the payload
*/
sizeof (fcph_network_hdr_t));
/*
* get the IP header to obtain the no. of bytes we need to read
* off from the unsol buffer. This obviously is because not all
* data fills up the unsol buffer completely and the firmware
* doesn't tell us how many valid bytes are in there as well
*/
(CE_CONT, "SNAPHDR: dsap %x, ssap %x, ctrl %x\n",
(CE_CONT, "oui[0] 0x%x oui[1] 0x%x oui[2] 0x%x pid 0x%x\n",
/* Authneticate, Authenticate */
if (type == ETHERTYPE_IP) {
} else if (type == ETHERTYPE_ARP) {
} else {
}
rval = FC_UNCLAIMED;
goto data_cb_done;
}
rval = FC_UNCLAIMED;
goto data_cb_done;
}
FCIP_COMPARE_NWWN) != 0) &&
rval = FC_UNCLAIMED;
goto data_cb_done;
} else if (fcip_cache_on_arp_broadcast &&
}
/*
* Using esballoc instead of allocb should be faster, atleast at
* larger MTUs than 1500 bytes. Someday we'll get there :)
*/
#if defined(FCIP_ESBALLOC)
/*
* allocate memory for the frtn function arg. The Function
* (fcip_ubfree) arg is a struct fcip_esballoc_arg type
* which contains pointers to the unsol buffer and the
* opaque port handle for releasing the unsol buffer back to
* the FCA for reuse
*/
fesb_argp = (struct fcip_esballoc_arg *)
(CE_WARN, "esballoc of mblk failed in data_cb"));
rval = FC_UNCLAIMED;
goto data_cb_done;
}
/*
* Check with KM_NOSLEEP
*/
(CE_WARN, "esballoc of mblk failed in data_cb"));
rval = FC_UNCLAIMED;
goto data_cb_done;
}
(CE_WARN, "esballoc of mblk failed in data_cb"));
rval = FC_UNCLAIMED;
goto data_cb_done;
}
#elif !defined(FCIP_ESBALLOC)
/*
* allocate streams mblk and copy the contents of the
* unsolicited buffer into this newly alloc'ed mblk
*/
(CE_WARN, "alloc of mblk failed in data_cb"));
rval = FC_UNCLAIMED;
goto data_cb_done;
}
/*
* Unsolicited buffers handed up to us from the FCA must be
* endian clean so just bcopy the data into our mblk. Else
* we may have to either copy the data byte by byte or
* use the ddi_rep_get* routines to do the copy for us.
*/
/*
* for esballoc'ed mblks - free the UB in the frtn function
* along with the memory allocated for the function arg.
* for allocb'ed mblk - release the unsolicited buffer here
*/
#endif /* FCIP_ESBALLOC */
fptr->fcip_ipackets++;
if (type == ETHERTYPE_IP) {
fptr->fcip_ub_upstream++;
/*
* Check if ipq is valid in the sendup thread
*/
}
} else {
/*
* We won't get ethernet 802.3 packets in FCIP but we may get
* types other than ETHERTYPE_IP, such as ETHERTYPE_ARP. Let
* fcip_sendup() do the matching.
*/
fptr->fcip_ub_upstream++;
fcip_accept) != FC_SUCCESS) {
}
}
rval = FC_SUCCESS;
/*
* Unset fcip_flags to indicate we are out of callback and return
*/
return (rval);
}
#if !defined(FCIP_ESBALLOC)
/*
* Allocate a message block for the inbound data to be sent upstream.
*/
static void *
{
return (NULL);
}
return (mp);
}
#endif
/*
* This helper routine kmem cache alloc's a sendup element for enquing
* into the sendup list for callbacks upstream from the dedicated sendup
* thread. We enque the msg buf into the sendup list and cv_signal the
* sendup thread to finish the callback for us.
*/
static int
{
/* drop pkt to floor - update stats */
rval = FC_FAILURE;
goto sendup_alloc_done;
}
msg_elem->fcipsu_func = f;
} else {
}
fptr->fcip_sendup_cnt++;
rval = FC_SUCCESS;
return (rval);
}
/*
* One of the ways of performing the WWN to D_ID mapping required for
* IPFC data is to cache the unsolicited ARP broadcast messages received
* and update the routing table to add entry for the destination port
* if we are the intended recipient of the ARP broadcast message. This is
* one of the methods recommended in the rfc to obtain the WWN to D_ID mapping
* but is not typically used unless enabled. The driver prefers to use the
* nameserver/lilp map to obtain this mapping.
*/
static void
{
return;
}
sizeof (la_wwn_t));
sizeof (la_wwn_t));
}
}
/*
* This is a dedicated thread to do callbacks from fcip's data callback
* routines into the modules upstream. The reason for this thread is
* the data callback function can be called from an interrupt context and
* the upstream modules *can* make calls downstream in the same thread
* context. If the call is to a fabric port which is not yet in our
* routing tables, we may have to query the nameserver/fabric for the
* MAC addr to Port_ID mapping which may be blocking calls.
*/
static void
{
callb_generic_cpr, "fcip_sendup_thr");
for (;;) {
while (fptr->fcip_sendup_thr_initted &&
}
if (fptr->fcip_sendup_thr_initted == 0) {
break;
}
/*
* Message for ipq. Check to see if the ipq is
* is still valid. Since the thread is asynchronous,
* there could have been a close on the stream
*/
} else {
}
} else {
}
#if !defined(FCIP_ESBALLOC)
/*
* for allocb'ed mblk - decrement upstream count here
*/
fptr->fcip_ub_upstream--;
#endif /* FCIP_ESBALLOC */
fptr->fcip_sendup_cnt--;
}
#ifndef __lock_lint
#else
#endif /* __lock_lint */
/* Wake up fcip detach thread by the end */
thread_exit();
}
#ifdef FCIP_ESBALLOC
/*
* called from the stream head when it is done using an unsolicited buffer.
* We release this buffer then to the FCA for reuse.
*/
static void
{
(CE_WARN, "freeing ubuf after esballoc in fcip_ubfree"));
fptr->fcip_ub_upstream--;
}
#endif /* FCIP_ESBALLOC */
/*
* handle data other than that of type ETHERTYPE_IP and send it on its
* way upstream to the right streams module to handle
*/
static void
{
int type;
snaphdr =
/* No group address with fibre channel */
isgroupaddr = 0;
/*
* While holding a reader lock on the linked list of streams structures,
* attempt to match the address criteria for each stream
* and pass up the raw M_DATA ("fastpath") or a DL_UNITDATA_IND.
*/
return;
}
/*
* Loop on matching open streams until (*acceptfunc)() returns NULL.
*/
!isgroupaddr) {
/* No headers when FCIP_SLRAW is set */
}
}
}
}
/*
* Do the last one.
*/
}
} else {
}
}
/*
* Match the stream based on type and wwn if necessary.
* Destination wwn dhostp is passed to this routine is reserved
* for future usage. We don't need to use it right now since port
* to fcip instance mapping is unique and wwn is already validated when
* packet comes to fcip.
*/
/* ARGSUSED */
static struct fcipstr *
{
"fcip_accept: checking next sap = %x, type = %x",
return (slp);
}
}
return (NULL);
}
/*
* Handle DL_UNITDATA_IND messages
*/
static mblk_t *
int type)
{
int size;
/*
* Allocate an M_PROTO mblk for the DL_UNITDATA_IND.
*/
fptr->fcip_allocbfail++;
return (NULL);
}
/*
* Construct a DL_UNITDATA_IND primitive.
*/
+ FCIPADDRL);
/*
* Link the M_PROTO and M_DATA together.
*/
return (nmp);
}
/*
* The open routine. For clone opens, we return the next available minor
* no. for the stream to use
*/
/* ARGSUSED */
static int
{
/*
* We need to ensure that the port driver is loaded before
* we proceed
*/
/* no port driver instances found */
"!ddi_hold_installed_driver of fp failed\n"));
return (ENXIO);
}
/* serialize opens */
minor = 0;
break;
}
minor ++;
}
} else {
}
/*
* check if our qp's private area is already initialized. If yes
* the stream is already open - just return
*/
goto done;
}
/*
* link this new stream entry into list of active streams
*/
/*
* Disable automatic enabling of our write service procedures
* we need to control this explicitly. This will prevent
* anyone scheduling of our write service procedures.
*/
done:
/*
* enable our put and service routines on the read side
*/
/*
* There is only one instance of fcip (instance = 0)
* for multiple instances of hardware
*/
return (0);
}
/*
* close an opened stream. The minor no. will then be available for
* future opens.
*/
/* ARGSUSED */
static int
{
/* we should also have the active stream pointer in q_ptr */
/*
* disable our put and service procedures. We had enabled them
* on open
*/
/*
* Implicitly detach stream a stream from an interface.
*/
}
/*
* unlink this stream from the active stream list and free it
*/
break;
}
}
/* we should have found slp */
return (0);
}
/*
* This is not an extension of the DDI_DETACH request. This routine
* only detaches a stream from an interface
*/
static void
{
/*
* we don't support promiscuous mode currently but check
* for and disable any promiscuous mode operation
*/
}
/*
* disable ALLMULTI mode if all mulitcast addr are ON
*/
}
/*
* we are most likely going to perform multicast by
* broadcasting to the well known addr (D_ID) 0xFFFFFF or
* ALPA 0x00 in case of public loops
*/
/*
* detach unit from device structure.
*/
break;
}
}
"fcip_dodeatch - active stream struct not found"));
/* unregister with Fabric nameserver?? */
}
}
/*
* Set or clear device ipq pointer.
* Walk thru all the streams on this device, if a ETHERTYPE_IP
* stream is found, assign device ipq to its sl_rq.
*/
static void
{
ok = 0;
}
} else {
ok = 0;
}
}
}
}
if (fcip_check_port_exists(fptr)) {
/* fptr passed to us is stale */
return;
}
if (ok) {
} else {
}
}
/* ARGSUSED */
static void
{
case DLIOCRAW:
break;
case DL_IOC_HDR_INFO:
break;
default:
break;
}
}
/*
* The streams 'Put' routine.
*/
/* ARGSUSED */
static int
{
case M_DATA: {
/*
* set error in the message block and send a reply
* back upstream. Sun's merror routine does this
* for us more cleanly.
*/
break;
}
/*
* if any messages are already enqueued or if the interface
* is in promiscuous mode, causing the packets to loop back
* up, then enqueue the message. Otherwise just transmit
* the message. putq() puts the message on fcip's
* write queue and qenable() puts the queue (wq) on
* the list of queues to be called by the streams scheduler.
*/
/*
* Promiscous mode not supported but add this code in
* case it will be supported in future.
*/
} else {
break;
}
}
break;
}
case M_PROTO:
case M_PCPROTO:
/*
* to prevent recursive calls into fcip_proto
* (PROTO and PCPROTO messages are handled by fcip_proto)
* let the service procedure handle these messages by
* calling putq here.
*/
break;
case M_IOCTL:
break;
case M_FLUSH:
}
/*
* we have both FLUSHW and FLUSHR set with FLUSHRW
*/
/*
* send msg back upstream. qreply() takes care
* of using the RD(wq) queue on its reply
*/
} else {
}
break;
default:
break;
}
return (0);
}
/*
* Handle M_PROTO and M_PCPROTO messages
*/
/* ARGSUSED */
static void
{
switch (prim) {
case DL_UNITDATA_REQ:
break;
case DL_ATTACH_REQ:
break;
case DL_DETACH_REQ:
break;
case DL_BIND_REQ:
break;
case DL_UNBIND_REQ:
break;
case DL_INFO_REQ:
break;
case DL_SET_PHYS_ADDR_REQ:
(CE_NOTE, "set phy addr request"));
break;
case DL_PHYS_ADDR_REQ:
break;
case DL_ENABMULTI_REQ:
(CE_NOTE, "Enable Multicast request"));
break;
case DL_DISABMULTI_REQ:
(CE_NOTE, "Disable Multicast request"));
break;
case DL_PROMISCON_REQ:
(CE_NOTE, "Promiscuous mode ON request"));
break;
case DL_PROMISCOFF_REQ:
(CE_NOTE, "Promiscuous mode OFF request"));
break;
default:
break;
}
}
/*
* Always enqueue M_PROTO and M_PCPROTO messages pn the wq and M_DATA
* messages sometimes. Processing of M_PROTO and M_PCPROTO messages
* require us to hold fcip's internal locks across (upstream) putnext
* calls. Specifically fcip_intr could hold fcip_intrlock and fcipstruplock
* when it calls putnext(). That thread could loop back around to call
* fcip_wput and eventually fcip_init() to cause a recursive mutex panic
*
* M_DATA messages are enqueued only if we are out of xmit resources. Once
* the transmit resources are available the service procedure is enabled
* and an attempt is made to xmit all messages on the wq.
*/
/* ARGSUSED */
static int
{
case M_DATA:
&headerp->net_dest_addr);
goto done;
}
KM_SLEEP)) {
goto done;
}
} else {
}
break;
case M_PROTO:
case M_PCPROTO:
(CE_NOTE, "PROT msg in wsrv"));
break;
default:
break;
}
}
done:
return (0);
}
/*
* This routine is called from fcip_wsrv to send a message downstream
* on the fibre towards its destination. This routine performs the
* actual WWN to D_ID mapping by looking up the routing and destination
* tables.
*/
/* ARGSUSED */
static int
{
int rval;
int free;
/*
* Only return if port has gone offline and not come back online
* in a while
*/
return (0);
}
/*
* The message block coming in here already has the network and
* llc_snap hdr stuffed in
*/
/*
* Traditionally ethernet drivers at sun handle 3 cases here -
* 1. messages with one mblk
* 2. messages with 2 mblks
* 3. messages with >2 mblks
* For now lets handle all the 3 cases in a single case where we
* put them together in one mblk that has all the data
*/
(CE_WARN, "failed to concat message"));
return (1);
}
}
"msgsize with nhdr & llcsnap hdr in fcip_pkt_alloc 0x%lx",
datalen));
/*
* We cannot have requests larger than FCIPMTU+Headers
*/
sizeof (fcph_network_hdr_t))) {
"fcip_pkt_alloc: datalen is larger than "
"max possible size."));
return (1);
}
return (1);
}
/*
* If the device dynamically disappeared, just fail the request.
*/
return (1);
}
/*
* Now that we've assigned pkt_pd, we can call fc_ulp_init_packet
*/
return (1);
}
"setting cmdlen to 0x%x: rsp 0x%x : data 0x%x",
fdestp->fcipd_ncmds++;
fptr->fcip_opackets++;
return (0);
}
if (!rval) {
} else {
fdestp->fcipd_ncmds--;
}
}
if (!free) {
}
return (1);
}
/*
* This routine enqueus a packet marked to be issued to the
* transport in the dest structure. This enables us to timeout any
* without a response. fcip_pkt_timeout will attempt to clean up
* any packets hung in this state of limbo.
*/
static void
{
/*
* Just hang it off the head of packet list
*/
}
}
/*
* been successfully sent on its way. Ofcourse it doesn't mean that
* the packet will actually reach its destination but its atleast
* a step closer in that direction
*/
static int
{
while (fcipd_pkt) {
NULL);
fdestp->fcipd_head =
} else {
}
if (fcipd_pkt->fcip_pkt_next) {
}
break;
}
}
} else {
} else {
}
if (fcip_pkt->fcip_pkt_next) {
}
}
}
/*
* The transport routine - this is the routine that actually calls
* into the FCA driver (through the transport ofcourse) to transmit a
* datagram on the fibre. The dest struct assoicated with the port to
* which the data is intended is already bound to the packet, this routine
* only takes care of marking the packet a broadcast packet if it is
* intended to be a broadcast request. This permits the transport to send
* the packet down on the wire even if it doesn't have an entry for the
* D_ID in its d_id hash tables.
*/
static int
{
(CE_NOTE, "trantype set to BROADCAST"));
}
return (rval);
return (FC_TRAN_BUSY);
}
if (fdestp) {
if (frp &&
"fcip io", /* CSTYLED */,
"fcip transport - TRANBUSY"));
return (FC_TRAN_BUSY);
} else {
"fcip io", /* CSTYLED */,
"fcip transport: frp unavailable"));
return (rval);
}
}
}
}
/* Explicitly invalidate this field till fcip decides to use it */
/*
* Need to queue up the command for retry
*/
/*
* There is a distinct possiblity in our scheme of things
* that we have a routing table entry with a NULL pd struct.
* Mark the routing table entry for removal if it is not a
* broadcast entry
*/
}
}
return (rval);
}
/*
* has been put onto the wire towards its intended destination. We can
* now free the fc_packet associated with the message
*/
static void
{
int rval;
/*
* take the lock early so that we don't have a race condition
* with fcip_timeout
*
* fdestp->fcipd_mutex isn't really intended to lock per
* packet struct - see bug 5105592 for permanent solution
*/
return;
}
fdestp->fcipd_ncmds--;
if (rval) {
}
}
/*
* Return 1 if the topology is supported, else return 0.
* Topology support is consistent with what the whole
* stack supports together.
*/
static int
{
switch (fc_topology) {
case FC_TOP_PRIVATE_LOOP :
case FC_TOP_PUBLIC_LOOP :
case FC_TOP_FABRIC :
case FC_TOP_NO_NS :
return (1);
default :
return (0);
}
}
/*
* handle any topology specific initializations here
* this routine must be called while holding fcip_mutex
*/
/* ARGSUSED */
static void
{
/*
* Since we know the port's topology - handle topology
* specific details here. In Point to Point and Private Loop
* topologies - we would probably not have a name server
*/
(void) fcip_dest_add_broadcast_entry(fptr, 0);
(CE_WARN, "fcip(0x%x): Unsupported port topology (0x%x)",
return;
}
switch (fport->fcipp_topology) {
case FC_TOP_PRIVATE_LOOP: {
/*
* we may have to maintain routing. Get a list of
* all devices on this port that the transport layer is
* aware of. Check if any of them is a IS8802 type port,
* if yes get its WWN and DID mapping and cache it in
* the purport routing table. Since there is no
* State Change notification for private loop/point_point
* topologies - this table may not be accurate. The static
* routing table is updated on a state change callback.
*/
port_map = (fc_portmap_t *)
KM_SLEEP);
}
}
/*
* Now fall through and register with the transport
* that this port is IP capable
*/
}
/* FALLTHROUGH */
case FC_TOP_NO_NS:
/*
* If we don't have a nameserver, lets wait until we
* have to send out a packet to a remote port and then
*/
/* FALLTHROUGH */
case FC_TOP_PUBLIC_LOOP:
case FC_TOP_FABRIC: {
/* FC_TYPE of 0x05 goes to word 0, LSB */
fptr, KM_NOSLEEP) == 0) {
}
}
/*
* If fcip_create_nodes_on_demand is overridden to force
* we need to query for and obtain all nodes and log into
* them as with private loop devices
*/
if (!fcip_create_nodes_on_demand) {
port_map = (fc_portmap_t *)
sizeof (fc_portmap_t)), KM_SLEEP);
}
}
(alloclen * sizeof (fc_portmap_t)));
}
break;
}
default:
break;
}
}
static void
{
return;
}
/*
* Prepare the Name server structure to
* register with the transport in case of
* Fabric configuration.
*/
ns_cmd.ns_resp_len = 0;
/*
* Perform the Name Server Registration for FC IS8802_SNAP Type.
* We don't expect a reply for registering port type
*/
}
/*
* setup this instance of fcip. This routine inits kstats, allocates
* unsolicited buffers, determines' this port's siblings and handles
* topology specific details which includes registering with the name
* server and also setting up the routing table for this port for
* private loops and point to point topologies
*/
static int
{
/*
* setup mac address for this port. Don't be too worried if
* the WWN is zero, there is probably nothing attached to
* to the port. There is no point allocating unsolicited buffers
* for an unused port so return success if we don't have a MAC
* address. Do the port init on a state change notification.
*/
rval = FC_SUCCESS;
goto done;
}
/*
* clear routing table hash list for this port
*/
/*
* init kstats for this instance
*/
/*
* Allocate unsolicited buffers
*/
rval = FC_FAILURE;
(CE_WARN, "fcip(%d): failed to allocate unsol buf",
fptr->fcip_instance));
goto done;
}
if (rval != FC_SUCCESS) {
(CE_WARN, "fcip(%d): fc_ulp_uballoc failed with 0x%x!!",
}
switch (rval) {
case FC_SUCCESS:
break;
case FC_OFFLINE:
rval = FC_FAILURE;
goto done;
case FC_UB_ERROR:
(CE_WARN, "invalid ub alloc request !!"));
rval = FC_FAILURE;
goto done;
case FC_FAILURE:
/*
* requested bytes could not be alloced
*/
"!fcip(0x%x): Failed to alloc unsolicited bufs",
rval = FC_FAILURE;
goto done;
}
break;
default:
rval = FC_FAILURE;
break;
}
/*
* Preallocate a Cache of fcip packets for transmit and receive
* We don't want to be holding on to unsolicited buffers while
* we transmit the message upstream
*/
sizeof (struct fcip_sendup_elem),
(CE_WARN, "fcip%d unable to allocate xmit cache",
fptr->fcip_instance));
rval = FC_FAILURE;
goto done;
}
/*
* We may need to handle routing tables for point to point and
* fcal topologies and register with NameServer for Fabric
* topologies.
*/
(CE_WARN, "fcip(0x%x):add broadcast entry failed!!",
fptr->fcip_instance));
rval = FC_FAILURE;
goto done;
}
rval = FC_SUCCESS;
return (rval);
done:
/*
* we don't always come here from port_attach - so cleanup
* anything done in the init_port routine
*/
if (fptr->fcip_kstatp) {
}
if (fptr->fcip_xmit_cache) {
}
if (fptr->fcip_sendup_cache) {
}
/* release unsolicited buffers */
if (fptr->fcip_ub_tokens) {
tokens);
} else {
}
return (rval);
}
/*
* Sets up a port's MAC address from its WWN
*/
static int
{
fptr->fcip_addrflags = 0;
/*
* we cannot choose a MAC address for our interface - we have
* to live with whatever node WWN we get (minus the top two
* MSbytes for the MAC address) from the transport layer. We will
* treat the WWN as our factory MAC address.
*/
} else {
/*
* No WWN - just return failure - there's not much
* we can do since we cannot set the WWN.
*/
(CE_WARN, "Port does not have a valid WWN"));
return (FCIP_INVALID_WWN);
}
return (FC_SUCCESS);
}
/*
* flush routing table entries
*/
static void
{
int index;
while (frtp) {
}
}
}
/*
* Free up the fcip softstate and all allocated resources for the
* fcip instance assoicated with a given port driver instance
*
* Given that the list of structures pointed to by fcip_port_head,
* this function is called from multiple sources, and the
* fcip_global_mutex that protects fcip_port_head must be dropped,
* our best solution is to return a value that indicates the next
* port in the list. This way the caller doesn't need to worry
* about the race condition where he saves off a pointer to the
* next structure in the list and by the time this routine returns,
* that next structure has already been freed.
*/
static fcip_port_info_t *
{
int instance;
if (fport) {
} else {
return (next_fport);
}
if (fptr) {
/*
* dismantle timeout thread for this instance of fcip
*/
ASSERT(fcip_num_instances >= 0);
/*
* stop sendup thread
*/
if (fptr->fcip_sendup_thr_initted) {
fptr->fcip_sendup_thr_initted = 0;
}
/*
* dismantle taskq
*/
}
if (fptr->fcip_kstatp) {
}
/* flush the routing table entries */
if (fptr->fcip_xmit_cache) {
}
if (fptr->fcip_sendup_cache) {
}
/* release unsolicited buffers */
if (fptr->fcip_ub_tokens) {
if (phandle) {
/*
* release the global mutex here to
* permit any data pending callbacks to
* complete. Else we will deadlock in the
* FCA waiting for all unsol buffers to be
* returned.
*/
(void) fc_ulp_ubfree(phandle,
}
} else {
}
}
/*
* Now dequeue the fcip_port_info from the port list
*/
prev_fport = NULL;
break;
}
}
/*
* Assert that we found a port in our port list
*/
if (prev_fport) {
/*
* Not the first port in the port list
*/
} else {
/*
* first port
*/
}
return (next_fport);
}
/*
* This is called by transport for any ioctl operations performed
* on the devctl or other transport minor nodes. It is currently
* unused for fcip
*/
/* ARGSUSED */
static int
{
return (FC_UNCLAIMED);
}
/*
* DL_INFO_REQ - returns information about the DLPI stream to the DLS user
* requesting information about this interface
*/
static void
{
int size;
(CE_NOTE, "fcip_ireq: info request req rcvd"));
return;
}
/*
* Exchange current message for a DL_INFO_ACK
*/
return;
}
/*
* FILL in the DL_INFO_ACK fields and reply
*/
*dlip = fcip_infoack;
if (fptr) {
} else {
}
}
/*
* To handle DL_UNITDATA_REQ requests.
*/
static void
{
int hdr_size;
return;
}
return;
}
/*
* Validate destination address format
*/
return;
}
/*
* Error if no M_DATA follows
*/
return;
}
/*
* Now get the destination structure for the remote NPORT
*/
"udreq - couldn't find dest struct for remote port");
return;
}
/*
* Network header + SAP
*/
/* DB_REF gives the no. of msgs pointing to this block */
/* first put the network header */
} else {
}
/* Now the snap header */
sizeof (fcph_network_hdr_t));
} else {
/*
* Only fill in the low 48bits of WWN for now - we can
* fill in the NAA_ID after we find the port in the
* routing tables
*/
} else {
}
/* need to send our PWWN */
sizeof (la_wwn_t));
sizeof (fcph_network_hdr_t));
}
/*
* Ethernet drivers have a lot of gunk here to put the Type
* information (for Ethernet encapsulation (RFC 894) or the
* Length (for 802.2/802.3) - I guess we'll just ignore that
* here.
*/
/*
* Start the I/O on this port. If fcip_start failed for some reason
* we call putbq in fcip_start so we don't need to check the
* return value from fcip_start
*/
}
/*
* DL_ATTACH_REQ: attaches a PPA with a stream. ATTACH requets are needed
* for style 2 DLS providers to identify the physical medium through which
* the streams communication will happen
*/
static void
{
int ppa;
return;
}
return;
}
/*
* check if the PPA is valid
*/
continue;
}
break;
}
}
(CE_NOTE, "dlerrorack coz fport==NULL"));
return;
}
/*
* Wait for Port attach callback to trigger. If port_detach
* got in while we were waiting, then ddi_get_soft_state
* will return NULL, and we'll return error.
*/
return;
}
}
/*
* set link to device and update our state
*/
#ifdef DEBUG
}
#endif
}
/*
* DL_DETACH request - detaches a PPA from a stream
*/
static void
{
return;
}
return;
}
}
/*
* DL_BIND request: requests a DLS provider to bind a DLSAP to the stream.
* DLS users communicate with a physical interface through DLSAPs. Multiple
* DLSAPs can be bound to the same stream (PPA)
*/
static void
{
int xidtest;
return;
}
return;
}
return;
}
if (xidtest) {
return;
}
if (sap > ETHERTYPE_MAX) {
return;
}
/*
* save SAP for this stream and change the link state
*/
}
/*
* DL_UNBIND request to unbind a previously bound DLSAP, from this stream
*/
static void
{
return;
}
return;
}
}
/*
* Return our physical address
*/
static void
{
int type;
return;
}
return;
}
switch (type) {
case DL_FACT_PHYS_ADDR:
(CE_NOTE, "returning factory phys addr"));
break;
case DL_CURR_PHYS_ADDR:
(CE_NOTE, "returning current phys addr"));
break;
default:
(CE_NOTE, "Not known cmd type in phys addr"));
return;
}
}
/*
* Set physical address DLPI request
*/
static void
{
return;
}
return;
}
/*
* If the length of physical address is not correct or address
* specified is a broadcast address or multicast addr -
* return an error.
*/
if ((len != ETHERADDRL) ||
return;
}
/*
* check if a stream is attached to this device. Else return an error
*/
return;
}
/*
* set the new interface local address. We request the transport
* layer to change the Port WWN for this device - return an error
* if we don't succeed.
*/
(CE_WARN, "WWN changed in spareq"));
} else {
}
/*
* register The new Port WWN and Node WWN with the transport
* and Nameserver. Hope the transport ensures all current I/O
* has stopped before actually attempting to register a new
* port and Node WWN else we are hosed. Maybe a Link reset
* will get everyone's attention.
*/
fcip_ns_cmd.ns_flags = 0;
fcip_ns_cmd.ns_resp_len = 0;
(CE_WARN, "setting Port WWN failed"));
return;
}
}
/*
* change our port's WWN if permitted by hardware
*/
/* ARGSUSED */
static int
{
/*
* We're usually not allowed to change the WWN of adapters
* but some adapters do permit us to change the WWN - don't
* permit setting of WWNs (yet?) - This behavior could be
* modified if needed
*/
return (FC_FAILURE);
}
/*
* This routine fills in the header for fastpath data requests. What this
* does in simple terms is, instead of sending all data through the Unitdata
* request dlpi code paths (which will then append the protocol specific
* header - network and snap headers in our case), the upper layers issue
* a M_IOCTL with a DL_IOC_HDR_INFO request and ask the streams endpoint
* driver to give the header it needs appended and the upper layer
* allocates and fills in the header and calls our put routine
*/
static void
{
int error;
(CE_NOTE, "dliochdr : returns EINVAL1"));
return;
}
if (error != 0) {
return;
}
/*
* check if the DL_UNITDATA_REQ destination addr has valid offset
* and length values
*/
(CE_NOTE, "dliochdr : returns EINVAL2"));
return;
}
/*
* Allocate a new mblk to hold the ether header
*/
/*
* setup space for network header
*/
(CE_NOTE, "dliochdr : returns ENOMEM"));
return;
}
/*
* Fill in the Network Hdr and LLC SNAP header;
*/
/*
* just fill in the Node WWN here - we can fill in the NAA_ID when
* we search the routing table
*/
} else {
}
/*
* Link new mblk in after the "request" mblks.
*/
(CE_NOTE, "dliochdr : returns success "));
}
/*
* Establish a kmem cache for fcip packets
*/
static int
{
/*
* we allocated space for our private area at the end of the
* fc packet. Make sure we point to it correctly. Ideally we
* should just push fc_packet_private to the beginning or end
* of the fc_packet structure
*/
fcip_pkt->fcip_pkt_state = 0;
fcip_pkt->fcip_pkt_reason = 0;
fcip_pkt->fcip_pkt_flags = 0;
fcip_pkt->fcip_pkt_dma_flags = 0;
/*
* We use pkt_cmd_dma for OUTBOUND requests. We don't expect
* any responses for outbound IP data so no need to setup
* response or data dma handles.
*/
return (FCIP_FAILURE);
}
sizeof (fcip_pkt_t));
fc_pkt->pkt_data_cookie_cnt = 0;
return (FCIP_SUCCESS);
}
/*
* destroy the fcip kmem cache
*/
static void
{
if (fc_pkt->pkt_cmd_dma) {
}
}
/*
* the fcip destination structure is hashed on Node WWN assuming
* a NAA_ID of 0x1 (IEEE)
*/
static struct fcip_dest *
{
int hash_bucket;
int rval;
(CE_NOTE, "0x%x 0x%x 0x%x 0x%x 0x%x 0x%x",
if (fcip_check_port_exists(fptr)) {
/* fptr is stale, return fdestp */
return (fdestp);
}
/*
* destination port (an entry would exist in fcip_dest hash table)
*/
if (fdestp->fcipd_rtable) {
FCIP_COMPARE_NWWN) == 0) {
(CE_NOTE, "found fdestp"));
return (fdestp);
}
}
}
/*
* We did not find the destination port information in our
* active port list so search for an entry in our routing
* table.
*/
/*
* No entry for the destination port in our routing
* table too. First query the transport to see if it
* already has structures for the destination port in
* its hash tables. This must be done for all topologies
* since we could have retired entries in the hash tables
* which may have to be re-added without a statechange
* callback happening. Its better to try and get an entry
* for the destination port rather than simply failing a
* request though it may be an overkill in private loop
* topologies.
* If a entry for the remote port exists in the transport's
* hash tables, we are fine and can add the entry to our
* routing and dest hash lists, Else for fabric configs we
* query the nameserver if one exists or issue FARP ELS.
*/
/*
* We need to do a PortName based Nameserver
* query operation. So get the right PortWWN
* for the adapter.
*/
/*
* Try IEEE Name (Format 1) first, this is the default and
* Emulex uses this format.
*/
if (rval != FC_SUCCESS) {
/*
* If IEEE Name (Format 1) query failed, try IEEE
* Extended Name (Format 2) which Qlogic uses.
* And try port 1 on Qlogic FC-HBA first.
* Note: On x86, we need to byte swap the 32-bit
* word first, after the modification, swap it back.
*/
}
if (rval != FC_SUCCESS) {
/* If still failed, try port 2 on Qlogic FC-HBA. */
}
if (rval == FC_SUCCESS) {
/*
* Add the newly found destination structure
* to our routing table. Create a map with
* the device we found. We could ask the
* transport to give us the list of all
* devices connected to our port but we
* probably don't need to know all the devices
* so let us just constuct a list with only
* one device instead.
*/
} else if (fcip_farp_supported &&
/*
* The Name server request failed so
* issue an FARP
*/
0, 0);
} else {
}
/*
* Prepare a dest structure to return to caller
*/
(CE_NOTE, "in fcip get dest non fabric"));
}
return (fdestp);
}
/*
* Endian clean WWN compare.
* Returns 0 if they compare OK, else return non zero value.
* flag can be bitwise OR of FCIP_COMPARE_NWWN, FCIP_COMPARE_PWWN,
* FCIP_COMPARE_BROADCAST.
*/
static int
{
int rval = 0;
rval = 1;
} else if ((flag == FCIP_COMPARE_PWWN) &&
rval = 1;
}
return (rval);
}
/*
* Add an entry for a remote port in the dest hash table. Dest hash table
* has entries for ports in the routing hash table with which we decide
* to establish IP communication with. The no. of entries in the dest hash
* table must always be less than or equal to the entries in the routing
* hash table. Every entry in the dest hash table ofcourse must have a
* corresponding entry in the routing hash table
*/
static struct fcip_dest *
{
int hash_bucket;
return (fdestp);
}
if (fdestp->fcipd_rtable) {
FCIP_COMPARE_PWWN) == 0) {
return (fdestp);
}
}
}
fdest_new->fcipd_refcnt = 0;
fdest_new->fcipd_ncmds = 0;
return (fdest_new);
}
/*
* Cleanup the dest hash table and remove all entries
*/
static void
{
int i;
for (i = 0; i < FCIP_DEST_HASH_ELEMS; i++) {
fdest_delp = fdestp;
}
}
}
/*
* Send FARP requests for Fabric ports when we don't have the port
* we wish to talk to in our routing hash table. FARP is specially required
* to talk to FC switches for inband switch management. Most FC switches
* today have a switch FC IP address for IP over FC inband switch management
* but the WWN and Port_ID for this traffic is not available through the
* Nameservers since the switch themeselves are transparent.
*/
/* ARGSUSED */
static struct fcip_dest *
{
int rval;
return (fdestp);
}
return (fdestp);
}
return (fdestp);
}
bdestp->fcipd_ncmds++;
/*
* Now initialize the FARP payload itself
*/
/*
* for now just match the Port WWN since the other match addr
* code points are optional. We can explore matching the IP address
* if needed
*/
if (ip_addr) {
} else {
}
/*
* Request the responder port to log into us - that way
* the Transport is aware of the remote port when we create
* an entry for it in our tables
*/
/*
* copy in source IP address if we get to know it
*/
if (ip_addr) {
}
/*
* Endian safe copy
*/
sizeof (la_els_farp_t));
/*
* send the packet in polled mode.
*/
if (rval != FC_SUCCESS) {
"fcip_transport of farp pkt failed 0x%x", rval));
bdestp->fcipd_ncmds--;
return (fdestp);
}
farp_lbolt = ddi_get_lbolt();
fptr->fcip_farp_rsp_flag = 0;
while (!fptr->fcip_farp_rsp_flag) {
farp_lbolt) == -1) {
/*
* No FARP response from any destination port
* so bail out.
*/
} else {
/*
* We received a FARP response - check to see if the
* response was in reply to our FARP request.
*/
} else {
/*
* Not our FARP response so go back and wait
* again till FARP_TIMEOUT expires
*/
fptr->fcip_farp_rsp_flag = 0;
}
}
}
bdestp->fcipd_ncmds--;
return (fdestp);
}
/*
* Helper routine to PLOGI to a remote port we wish to talk to.
* This may not be required since the port driver does logins anyway,
* don't require you to be logged in?
*/
/* ARGSUSED */
static int
{
int rval;
/*
* Don't bother to login for broadcast RTE entries
*/
return (FC_FAILURE);
}
/*
* We shouldn't pound in too many logins here
*
*/
return (FC_SUCCESS);
}
return (FC_FAILURE);
}
/*
* Update back pointer for login state update
*/
/*
* Initialize frame header for ELS
*/
/*
* Everybody does class 3, so let's just set it. If the transport
* knows better, it will deal with the class appropriately.
*/
/*
* we need only fill in the ls_code and the cmd frame header
*/
sizeof (la_els_logi_t));
if (rval != FC_SUCCESS) {
"!fc_ulp_login failed for d_id: 0x%x, rval: 0x%x",
}
return (rval);
}
/*
* that the alloc'ed packet can be freed
*/
static void
{
sizeof (logi_req));
/* EMPTY */
"opcode : 0x%x to d_id: 0x%x failed",
(FCIP_RTE_TIMEOUT / 2);
} else {
d_id.priv_lilp_posit = 0;
/*
* Update PLOGI results; FCA Handle, and Port device handles
*/
frp->fcipr_fca_dev =
}
}
/*
* pkt_alloc routine for outbound IP datagrams. The cache constructor
* Only initializes the pkt_cmd_dma (which is where the outbound datagram
* is stuffed) since we don't expect response
*/
static fcip_pkt_t *
{
"fcip_pkt_alloc: kmem_cache_alloc failed"));
return (NULL);
}
fc_pkt->pkt_tran_flags = 0;
fcip_pkt->fcip_pkt_dma_flags = 0;
/*
* the cache constructor has allocated the dma handle
*/
goto fail;
}
if (fc_pkt->pkt_cmd_cookie_cnt >
goto fail;
}
goto fail;
}
*cp = pkt_cookie;
cp++;
*cp = pkt_cookie;
}
fcip_pkt->fcip_pkt_state = 0;
fcip_pkt->fcip_pkt_reason = 0;
fcip_pkt->fcip_pkt_flags = 0;
return (fcip_pkt);
fail:
if (fcip_pkt) {
fcip_pkt_free(fcip_pkt, 0);
}
return ((fcip_pkt_t *)0);
}
/*
* Free a packet and all its associated resources
*/
static void
{
sizeof (ddi_dma_cookie_t));
}
}
}
/*
* Allocate a Packet for internal driver use. This is for requests
* that originate from within the driver
*/
static fcip_pkt_t *
{
(CE_WARN, "pkt alloc of ineternal pkt failed"));
goto fail;
}
fc_pkt->pkt_tran_flags = 0;
fc_pkt->pkt_cmdlen = 0;
fc_pkt->pkt_rsplen = 0;
fc_pkt->pkt_datalen = 0;
sizeof (fcip_pkt_t));
if (cmdlen) {
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
}
if (fc_pkt->pkt_cmd_cookie_cnt >
goto fail;
}
goto fail;
}
*cp = pkt_cookie;
cp++;
*cp = pkt_cookie;
}
}
if (resplen) {
goto fail;
}
goto fail;
}
goto fail;
}
goto fail;
}
if (fc_pkt->pkt_resp_cookie_cnt >
goto fail;
}
goto fail;
}
*cp = pkt_cookie;
cp++;
*cp = pkt_cookie;
}
}
/*
* Initialize pkt_pd prior to calling fc_ulp_init_packet
*/
/*
* Ask the FCA to bless the internal packet
*/
goto fail;
}
/*
* Keep track of # of ipkts alloc-ed
* This function can get called with mutex either held or not. So, we'll
* grab mutex if it is not already held by this thread.
* This has to be cleaned up someday.
*/
held_here = 1;
}
if (held_here)
return (fcip_pkt);
fail:
if (fcip_pkt) {
}
return (NULL);
}
/*
* free up an internal IP packet (like a FARP pkt etc)
*/
static void
{
/* One less ipkt to wait for */
sizeof (ddi_dma_cookie_t));
}
sizeof (ddi_dma_cookie_t));
}
"fc_ulp_uninit_pkt failed for internal fc pkt 0x%p",
(void *)fc_pkt));
}
}
/*
* initialize a unicast request. This is a misnomer because even the
* broadcast requests are initialized with this routine
*/
static void
void (*comp) ())
{
/*
* reset all the length fields
*/
fc_pkt->pkt_rsplen = 0;
fc_pkt->pkt_datalen = 0;
if (comp) {
} else {
}
}
/*
* Initialize a fcip_packet for broadcast data transfers
*/
static void
{
if (is_els) {
} else {
}
/*
* The destination broadcast address depends on the topology
* of the underlying port
*/
/*
* mark pkt a broadcast pkt
*/
if (comp) {
} else {
}
}
/*
* Free up all DMA resources associated with an allocated packet
*/
static void
{
(CE_NOTE, "in freepktdma : flags 0x%x",
}
}
}
}
/*
* for internal commands, we need to free up the dma handles too.
* This is done in the cache destructor for non internal cmds
*/
if (fc_pkt->pkt_cmd_dma) {
}
if (fc_pkt->pkt_resp_dma) {
}
}
}
/*
* helper routine to generate a string, given an ether addr
*/
static void
{
int i;
for (i = 0; i < sizeof (struct ether_addr); i++, s += 2) {
}
*s = '\0';
}
/*
* When a broadcast request comes from the upper streams modules, it
* is ugly to look into every datagram to figure out if it is a broadcast
* datagram or a unicast packet. Instead just add the broadcast entries
* into our routing and dest tables and the standard hash table look ups
* will find the entries. It is a lot cleaner this way. Also Solaris ifconfig
* seems to be very ethernet specific and it requires broadcasts to the
* ether broadcast addr of 0xffffffffff to succeed even though we specified
* in the dl_info request that our broadcast MAC addr is 0x0000000000
* (can't figure out why RFC2625 did this though). So add broadcast entries
* for both MAC address
*/
static int
{
/*
* get port_id of destination for broadcast - this is topology
* dependent
*/
if (new_flag) {
} else {
}
return (FC_FAILURE);
}
/*
* The Upper IP layers expect the traditional broadcast MAC addr
* of 0xff ff ff ff ff ff to work too if we want to plumb the fcip
* stream through the /etc/hostname.fcipXX file. Instead of checking
* each phys addr for a match with fcip's ARP header broadcast
* addr (0x00 00 00 00 00 00), its simply easier to add another
* broadcast entry for 0xff ff ff ff ff ff.
*/
return (FC_FAILURE);
}
return (FC_SUCCESS);
}
/*
* We need to obtain the D_ID of the broadcast port for transmitting all
* our broadcast (and multicast) requests. The broadcast D_ID as we know
* is dependent on the link topology
*/
static uint32_t
{
switch (fport->fcipp_topology) {
case FC_TOP_PT_PT: {
"fcip_gpmap: listlen : 0x%x", listlen));
if (listlen == 1) {
}
}
if (port_map) {
}
if (listlen != 1) {
/* Dummy return value */
return (0x00FFFFFF);
}
break;
}
case FC_TOP_NO_NS:
/* FALLTHROUGH */
case FC_TOP_FABRIC:
/*
* The broadcast address is the same whether or not
*/
did = 0x00FFFFFF;
break;
case FC_TOP_PUBLIC_LOOP:
/*
* The open replicate primitive must not be used. The
* broadcast sequence is simply sent to ALPA 0x00. The
* fabric controller then propagates the broadcast to all
* other ports. The fabric propagates the broadcast by
* using the OPNfr primitive.
*/
did = 0x00;
break;
case FC_TOP_PRIVATE_LOOP:
/*
* The source port for broadcast in private loop mode
* must send an OPN(fr) signal forcing all ports in the
* loop to replicate the frames that they receive.
*/
did = 0x00FFFFFF;
break;
case FC_TOP_UNKNOWN:
/* FALLTHROUGH */
default:
"fcip(0x%x):unknown topology in init_broadcast_pkt",
fptr->fcip_instance));
break;
}
return (did);
}
/*
* fcip timeout performs 2 operations:
* 1. timeout any packets sent to the FCA for which a callback hasn't
* happened. If you are wondering why we need a callback since all
* traffic in FCIP is unidirectional, hence all exchanges are unidirectional
* but wait, we can only free up the resources after we know the FCA has
* DMA'ed out the data. pretty obvious eh :)
*
* 2. Retire and routing table entries we marked up for retiring. This is
* to give the link a chance to recover instead of marking a port down
* when we have lost all communication with it after a link transition
*/
static void
{
int i;
int index;
int dispatch_rte_removal = 0;
return;
}
}
}
dispatch_rte_removal = 1;
}
/*
* Check if we have any Invalid routing table entries in our
* hashtable we have marked off for deferred removal. If any,
* we can spawn a taskq thread to do the cleanup for us. We
* need to avoid cleanup in the timeout thread since we may
* have to wait for outstanding commands to complete before
* we retire a routing table entry. Also dispatch the taskq
* thread only if we are already do not have a taskq thread
* dispatched.
*/
if (dispatch_rte_removal) {
while (frtp) {
/*
* If we cannot schedule a task thread
* let us attempt again on the next
* tick rather than call
* fcip_rte_remove_deferred() from here
* directly since the routine can sleep.
*/
KM_NOSLEEP) == 0) {
/*
* failed - so mark the entry
* as invalid again.
*/
frtp->fcipr_state =
fptr->fcip_flags &=
}
}
}
}
}
/*
*/
for (i = 0; i < FCIP_DEST_HASH_ELEMS; i++) {
if (fcip_pkt->fcip_pkt_flags &
continue;
}
if (fptr->fcip_timeout_ticks >
fcip_pkt->fcip_pkt_ttl) {
KM_NOSLEEP) == 0) {
/*
* timeout immediately
*/
}
/*
* The linked list is altered because
* of one of the following reasons:
* a. Timeout code dequeued a pkt
* b. Pkt completion happened
*
* So restart the spin starting at
* the head again; This is a bit
* excessive, but okay since
* fcip_timeout_ticks isn't incremented
* for this spin, we will skip the
* not-to-be-timedout packets quickly
*/
break;
}
}
}
}
}
/*
* reschedule the timeout thread
*/
drv_usectohz(1000000));
}
/*
* This routine is either called from taskq or directly from fcip_timeout
* does the actual job of aborting the packet
*/
static void
{
int rval;
/*
* try to abort the pkt
*/
if (rval == FC_SUCCESS) {
/*
* dequeue the pkt from the dest structure pkt list
*/
/*
* Now cleanup the pkt and free the mblk
*/
} else {
/*
* abort failed - just mark the pkt as done and
* wait for it to complete in fcip_pkt_callback since
* the pkt has already been xmitted by the FCA
*/
}
return;
}
}
/*
* Remove a routing table entry marked for deferred removal. This routine
* unlike fcip_pkt_timeout, is always called from a taskq context
*/
static void
{
int hash_bucket;
int index;
while (frtp) {
/*
* Get hold of destination pointer
*/
if (fdestp->fcipd_rtable) {
if (fcip_wwn_compare(pwwn,
&fdestp->fcipd_pwwn,
FCIP_COMPARE_PWWN) == 0) {
&fdestp->fcipd_mutex);
break;
}
}
}
continue;
}
if (fdestp->fcipd_ncmds) {
/*
* Instead of waiting to drain commands
* let us revisit this RT entry in
* the next pass.
*/
continue;
}
/*
* We are clean, so remove the RTE
*/
"fcip io", /* CSTYLED */,
"remove retired routing entry",
/* first element */
} else {
}
sizeof (struct fcip_routing_table));
} else {
}
}
}
/*
* Clear the RTE_REMOVING flag
*/
}
/*
* Walk through all the dest hash table entries and count up the total
* no. of packets outstanding against a given port
*/
static int
{
int num_cmds = 0;
int i;
for (i = 0; i < FCIP_DEST_HASH_ELEMS; i++) {
if (fdestp->fcipd_ncmds > 0) {
}
}
}
return (num_cmds);
}
/*
* Walk through the routing table for this state instance and see if there is a
* PLOGI in progress for any of the entries. Return success even if we find one.
*/
static int
{
int i;
for (i = 0; i < FCIP_RT_HASH_ELEMS; i++) {
while (frp) {
/* Found an entry where PLOGI is in progress */
return (1);
}
}
}
return (0);
}
/*
* Walk through the fcip port global list and check if the given port exists in
* the list. Returns "0" if port exists and "1" if otherwise.
*/
static int
{
/* Found */
return (0);
} else {
}
}
return (1);
}
/*
* Constructor to initialize the sendup elements for callback into
* modules upstream
*/
/* ARGSUSED */
static int
{
return (FCIP_SUCCESS);
}