blob: b4aaef3437a74a16a97dbf69d85ae6b4786733f8 [file] [log] [blame]
/*
* 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
* or http://www.opensolaris.org/os/licensing.
* 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.
* Copyright (c) 2016 by Delphix. All rights reserved.
*/
/*
* 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
* driver module which interfaces with IP/ARP using DLPI. This module
* is a Style-2 DLS provider.
*
* The implementation of this module is based on RFC 2625 which gives
* details on the encapsulation of IP/ARP data over FibreChannel.
* 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/types.h>
#include <sys/errno.h>
#include <sys/debug.h>
#include <sys/time.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/strlog.h>
#include <sys/strsubr.h>
#include <sys/cmn_err.h>
#include <sys/cpu.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>
#include <sys/stat.h>
#include <sys/kstat.h>
#include <sys/vtrace.h>
#include <sys/strsun.h>
#include <sys/varargs.h>
#include <sys/modctl.h>
#include <sys/thread.h>
#include <sys/var.h>
#include <sys/proc.h>
#include <inet/common.h>
#include <netinet/ip6.h>
#include <inet/ip.h>
#include <inet/arp.h>
#include <inet/mi.h>
#include <inet/nd.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/file.h>
#include <sys/syslog.h>
#include <sys/disp.h>
#include <sys/taskq.h>
/*
* Leadville includes
*/
#include <sys/fibre-channel/fc.h>
#include <sys/fibre-channel/impl/fc_ulpif.h>
#include <sys/fibre-channel/ulp/fcip.h>
/*
* TNF Probe/trace facility include
*/
#if defined(lint) || defined(FCIP_TNF_ENABLED)
#include <sys/tnf_probe.h>
#endif
#define FCIP_ESBALLOC
/*
* Function prototypes
*/
/* standard loadable modules entry points */
static int fcip_attach(dev_info_t *, ddi_attach_cmd_t);
static int fcip_detach(dev_info_t *, ddi_detach_cmd_t);
static void fcip_dodetach(struct fcipstr *slp);
static int fcip_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd,
void *arg, void **result);
/* streams specific */
static void fcip_setipq(struct fcip *fptr);
static int fcip_wput(queue_t *, mblk_t *);
static int fcip_wsrv(queue_t *);
static void fcip_proto(queue_t *, mblk_t *);
static void fcip_ioctl(queue_t *, mblk_t *);
static int fcip_open(queue_t *wq, dev_t *devp, int flag,
int sflag, cred_t *credp);
static int fcip_close(queue_t *rq, int flag, int otyp, cred_t *credp);
static int fcip_start(queue_t *wq, mblk_t *mp, struct fcip *fptr,
struct fcip_dest *fdestp, int flags);
static void fcip_sendup(struct fcip *fptr, mblk_t *mp,
struct fcipstr *(*acceptfunc)());
static struct fcipstr *fcip_accept(struct fcipstr *slp, struct fcip *fptr,
int type, la_wwn_t *dhostp);
static mblk_t *fcip_addudind(struct fcip *fptr, mblk_t *mp,
fcph_network_hdr_t *nhdr, int type);
static int fcip_setup_mac_addr(struct fcip *fptr);
static void fcip_kstat_init(struct fcip *fptr);
static int fcip_stat_update(kstat_t *, int);
/* dlpi specific */
static void fcip_spareq(queue_t *wq, mblk_t *mp);
static void fcip_pareq(queue_t *wq, mblk_t *mp);
static void fcip_ubreq(queue_t *wq, mblk_t *mp);
static void fcip_breq(queue_t *wq, mblk_t *mp);
static void fcip_dreq(queue_t *wq, mblk_t *mp);
static void fcip_areq(queue_t *wq, mblk_t *mp);
static void fcip_udreq(queue_t *wq, mblk_t *mp);
static void fcip_ireq(queue_t *wq, mblk_t *mp);
static void fcip_dl_ioc_hdr_info(queue_t *wq, mblk_t *mp);
/* solaris sundry, DR/CPR etc */
static int fcip_cache_constructor(void *buf, void *arg, int size);
static void fcip_cache_destructor(void *buf, void *size);
static int fcip_handle_suspend(fcip_port_info_t *fport, fc_detach_cmd_t cmd);
static int fcip_handle_resume(fcip_port_info_t *fport,
fc_ulp_port_info_t *port_info, fc_attach_cmd_t cmd);
static fcip_port_info_t *fcip_softstate_free(fcip_port_info_t *fport);
static int fcip_port_attach_handler(struct fcip *fptr);
/*
* ulp - transport interface function prototypes
*/
static int fcip_port_attach(opaque_t ulp_handle, fc_ulp_port_info_t *,
fc_attach_cmd_t cmd, uint32_t sid);
static int fcip_port_detach(opaque_t ulp_handle, fc_ulp_port_info_t *,
fc_detach_cmd_t cmd);
static int fcip_port_ioctl(opaque_t ulp_handle, opaque_t port_handle,
dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval,
uint32_t claimed);
static void fcip_statec_cb(opaque_t ulp_handle, opaque_t phandle,
uint32_t port_state, uint32_t port_top, fc_portmap_t changelist[],
uint32_t listlen, uint32_t sid);
static int fcip_els_cb(opaque_t ulp_handle, opaque_t phandle,
fc_unsol_buf_t *buf, uint32_t claimed);
static int fcip_data_cb(opaque_t ulp_handle, opaque_t phandle,
fc_unsol_buf_t *payload, uint32_t claimed);
/* Routing table specific */
static void fcip_handle_topology(struct fcip *fptr);
static int fcip_init_port(struct fcip *fptr);
struct fcip_routing_table *fcip_lookup_rtable(struct fcip *fptr,
la_wwn_t *pwwn, int matchflag);
static void fcip_rt_update(struct fcip *fptr, fc_portmap_t *devlist,
uint32_t listlen);
static void fcip_rt_flush(struct fcip *fptr);
static void fcip_rte_remove_deferred(void *arg);
static int fcip_do_plogi(struct fcip *fptr, struct fcip_routing_table *frp);
/* dest table specific */
static struct fcip_dest *fcip_get_dest(struct fcip *fptr,
la_wwn_t *dlphys);
static struct fcip_dest *fcip_add_dest(struct fcip *fptr,
struct fcip_routing_table *frp);
static int fcip_dest_add_broadcast_entry(struct fcip *fptr, int new_flag);
static uint32_t fcip_get_broadcast_did(struct fcip *fptr);
static void fcip_cleanup_dest(struct fcip *fptr);
/* helper functions */
static fcip_port_info_t *fcip_get_port(opaque_t phandle);
static int fcip_wwn_compare(la_wwn_t *wwn1, la_wwn_t *wwn2, int flag);
static void fcip_ether_to_str(struct ether_addr *e, caddr_t s);
static int fcip_port_get_num_pkts(struct fcip *fptr);
static int fcip_check_port_busy(struct fcip *fptr);
static void fcip_check_remove_minor_node(void);
static int fcip_set_wwn(la_wwn_t *pwwn);
static int fcip_plogi_in_progress(struct fcip *fptr);
static int fcip_check_port_exists(struct fcip *fptr);
static int fcip_is_supported_fc_topology(int fc_topology);
/* pkt specific */
static fcip_pkt_t *fcip_pkt_alloc(struct fcip *fptr, mblk_t *bp,
int flags, int datalen);
static void fcip_pkt_free(struct fcip_pkt *fcip_pkt, int flags);
static fcip_pkt_t *fcip_ipkt_alloc(struct fcip *fptr, int cmdlen,
int resplen, opaque_t pd, int flags);
static void fcip_ipkt_free(fcip_pkt_t *fcip_pkt);
static void fcip_ipkt_callback(fc_packet_t *fc_pkt);
static void fcip_free_pkt_dma(fcip_pkt_t *fcip_pkt);
static void fcip_pkt_callback(fc_packet_t *fc_pkt);
static void fcip_init_unicast_pkt(fcip_pkt_t *fcip_pkt, fc_portid_t sid,
fc_portid_t did, void (*comp) ());
static int fcip_transport(fcip_pkt_t *fcip_pkt);
static void fcip_pkt_timeout(void *arg);
static void fcip_timeout(void *arg);
static void fcip_fdestp_enqueue_pkt(struct fcip_dest *fdestp,
fcip_pkt_t *fcip_pkt);
static int fcip_fdestp_dequeue_pkt(struct fcip_dest *fdestp,
fcip_pkt_t *fcip_pkt);
static int fcip_sendup_constructor(void *buf, void *arg, int flags);
static void fcip_sendup_thr(void *arg);
static int fcip_sendup_alloc_enque(struct fcip *ftpr, mblk_t *mp,
struct fcipstr *(*f)());
/*
* zero copy inbound data handling
*/
#ifdef FCIP_ESBALLOC
static void fcip_ubfree(char *arg);
#endif /* FCIP_ESBALLOC */
#if !defined(FCIP_ESBALLOC)
static void *fcip_allocb(size_t size, uint_t pri);
#endif
/* FCIP FARP support functions */
static struct fcip_dest *fcip_do_farp(struct fcip *fptr, la_wwn_t *pwwn,
char *ip_addr, size_t ip_addr_len, int flags);
static void fcip_init_broadcast_pkt(fcip_pkt_t *fcip_pkt, void (*comp) (),
int is_els);
static int fcip_handle_farp_request(struct fcip *fptr, la_els_farp_t *fcmd);
static int fcip_handle_farp_response(struct fcip *fptr, la_els_farp_t *fcmd);
static void fcip_cache_arp_broadcast(struct fcip *ftpr, fc_unsol_buf_t *buf);
static void fcip_port_ns(void *arg);
#ifdef DEBUG
#include <sys/debug.h>
#define FCIP_DEBUG_DEFAULT 0x1
#define FCIP_DEBUG_ATTACH 0x2
#define FCIP_DEBUG_INIT 0x4
#define FCIP_DEBUG_DETACH 0x8
#define FCIP_DEBUG_DLPI 0x10
#define FCIP_DEBUG_ELS 0x20
#define FCIP_DEBUG_DOWNSTREAM 0x40
#define FCIP_DEBUG_UPSTREAM 0x80
#define FCIP_DEBUG_MISC 0x100
#define FCIP_DEBUG_STARTUP (FCIP_DEBUG_ATTACH|FCIP_DEBUG_INIT)
#define FCIP_DEBUG_DATAOUT (FCIP_DEBUG_DLPI|FCIP_DEBUG_DOWNSTREAM)
#define FCIP_DEBUG_DATAIN (FCIP_DEBUG_ELS|FCIP_DEBUG_UPSTREAM)
static int fcip_debug = FCIP_DEBUG_DEFAULT;
#define FCIP_DEBUG(level, args) \
if (fcip_debug & (level)) cmn_err args;
#else /* DEBUG */
#define FCIP_DEBUG(level, args) /* do nothing */
#endif /* DEBUG */
#define KIOIP KSTAT_INTR_PTR(fcip->fcip_intrstats)
/*
* Endian independent ethernet to WWN copy
*/
#define ether_to_wwn(E, W) \
bzero((void *)(W), sizeof (la_wwn_t)); \
bcopy((void *)(E), (void *)&((W)->raw_wwn[2]), ETHERADDRL); \
(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) \
bcopy((void *)&((W)->raw_wwn[2]), (void *)E, ETHERADDRL)
/*
* 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.
*/
static struct module_info fcipminfo = {
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.
*/
static struct qinit fcip_rinit = {
NULL, /* qi_putp */
NULL, /* qi_srvp */
fcip_open, /* qi_qopen */
fcip_close, /* qi_qclose */
NULL, /* qi_qadmin */
&fcipminfo, /* qi_minfo */
NULL /* qi_mstat */
};
static struct qinit fcip_winit = {
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
*/
static struct streamtab fcip_info = {
&fcip_rinit, /* st_rdinit */
&fcip_winit, /* st_wrinit */
NULL, /* st_muxrinit */
NULL, /* st_muxwrinit */
};
static struct cb_ops fcip_cb_ops = {
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 */
D_MP | D_HOTPLUG, /* Driver compatibility flag */
CB_REV, /* rev */
nodev, /* int (*cb_aread)() */
nodev /* int (*cb_awrite)() */
};
/*
* autoconfiguration routines.
*/
static struct dev_ops fcip_ops = {
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 */
};
#define FCIP_VERSION "1.61"
#define FCIP_NAME "SunFC FCIP v" FCIP_VERSION
#define PORT_DRIVER "fp"
#define GETSTRUCT(struct, number) \
((struct *)kmem_zalloc((size_t)(sizeof (struct) * (number)), \
KM_SLEEP))
static struct modldrv modldrv = {
&mod_driverops, /* Type of module - driver */
FCIP_NAME, /* Name of module */
&fcip_ops, /* driver ops */
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
/*
* Now for some global statics
*/
static uint32_t fcip_ub_nbufs = FCIP_UB_NBUFS;
static uint32_t fcip_ub_size = FCIP_UB_SIZE;
static int fcip_pkt_ttl_ticks = FCIP_PKT_TTL;
static int fcip_tick_incr = 1;
static int fcip_wait_cmds = FCIP_WAIT_CMDS;
static int fcip_num_attaching = 0;
static int fcip_port_attach_pending = 0;
static int fcip_create_nodes_on_demand = 1; /* keep it similar to fcp */
static int fcip_cache_on_arp_broadcast = 0;
static int fcip_farp_supported = 0;
static int fcip_minor_node_created = 0;
/*
* Supported FCAs
*/
#define QLC_PORT_1_ID_BITS 0x100
#define QLC_PORT_2_ID_BITS 0x101
#define QLC_PORT_NAA 0x2
#define QLC_MODULE_NAME "qlc"
#define IS_QLC_PORT(port_dip) \
(strcmp(ddi_driver_name(ddi_get_parent((port_dip))),\
QLC_MODULE_NAME) == 0)
/*
* fcip softstate structures head.
*/
static void *fcip_softp = NULL;
/*
* linked list of active (inuse) driver streams
*/
static int fcip_num_instances = 0;
static dev_info_t *fcip_module_dip = (dev_info_t *)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.
*
*/
static dl_info_ack_t fcip_infoack = {
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 */
sizeof (dl_info_ack_t) + FCIPADDRL, /* dl_brdcst_addr_offset */
0 /* dl_growth */
};
/*
* FCIP broadcast address definition.
*/
static struct ether_addr fcipnhbroadcastaddr = {
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
*/
static struct ether_addr fcip_arpbroadcast_addr = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
#define ether_bcopy(src, dest) bcopy((src), (dest), ETHERADDRL);
/*
* global kernel locks
*/
static kcondvar_t fcip_global_cv;
static kmutex_t fcip_global_mutex;
/*
* fctl external defines
*/
extern int fc_ulp_add(fc_ulp_modinfo_t *);
/*
* fctl data structures
*/
#define FCIP_REV 0x07
/* linked list of port info structures */
static fcip_port_info_t *fcip_port_head = NULL;
/* linked list of fcip structures */
static struct fcipstr *fcipstrup = NULL;
static krwlock_t fcipstruplock;
/*
* Module information structure. This structure gives the FC Transport modules
* information about an ULP that registers with it.
*/
static fc_ulp_modinfo_t fcip_modinfo = {
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 */
};
/*
* Solaris 9 and up, the /kernel/drv/fp.conf file will have the following entry
*
* ddi-forceattach=1;
*
* This will ensure that fp is loaded at bootup. No additional checks are needed
*/
int
_init(void)
{
int rval;
FCIP_TNF_LOAD();
/*
* 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.
*/
mutex_init(&fcip_global_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&fcip_global_cv, NULL, CV_DRIVER, NULL);
rw_init(&fcipstruplock, NULL, RW_DRIVER, NULL);
mutex_enter(&fcip_global_mutex);
fcip_port_attach_pending = 1;
mutex_exit(&fcip_global_mutex);
/*
* Now attempt to register fcip with the transport.
* If fc_ulp_add fails, fcip module will not be loaded.
*/
rval = fc_ulp_add(&fcip_modinfo);
if (rval != FC_SUCCESS) {
mutex_destroy(&fcip_global_mutex);
cv_destroy(&fcip_global_cv);
rw_destroy(&fcipstruplock);
switch (rval) {
case FC_ULP_SAMEMODULE:
FCIP_DEBUG(FCIP_DEBUG_DEFAULT, (CE_WARN,
"!fcip: module is already registered with"
" transport"));
rval = EEXIST;
break;
case FC_ULP_SAMETYPE:
FCIP_DEBUG(FCIP_DEBUG_DEFAULT, (CE_WARN,
"!fcip: Another module of the same ULP type 0x%x"
" is already registered with the transport",
fcip_modinfo.ulp_type));
rval = EEXIST;
break;
case FC_BADULP:
FCIP_DEBUG(FCIP_DEBUG_DEFAULT, (CE_WARN,
"!fcip: Current fcip version 0x%x does not match"
" fctl version",
fcip_modinfo.ulp_rev));
rval = ENODEV;
break;
default:
FCIP_DEBUG(FCIP_DEBUG_DEFAULT, (CE_WARN,
"!fcip: fc_ulp_add failed with status 0x%x", rval));
rval = ENODEV;
break;
}
FCIP_TNF_UNLOAD(&modlinkage);
return (rval);
}
if ((rval = ddi_soft_state_init(&fcip_softp, sizeof (struct fcip),
FCIP_NUM_INSTANCES)) != 0) {
mutex_destroy(&fcip_global_mutex);
cv_destroy(&fcip_global_cv);
rw_destroy(&fcipstruplock);
(void) fc_ulp_remove(&fcip_modinfo);
FCIP_TNF_UNLOAD(&modlinkage);
return (rval);
}
if ((rval = mod_install(&modlinkage)) != 0) {
FCIP_TNF_UNLOAD(&modlinkage);
(void) fc_ulp_remove(&fcip_modinfo);
mutex_destroy(&fcip_global_mutex);
cv_destroy(&fcip_global_cv);
rw_destroy(&fcipstruplock);
ddi_soft_state_fini(&fcip_softp);
}
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.
*/
mutex_enter(&fcip_global_mutex);
if (fcip_num_attaching || fcip_port_attach_pending) {
mutex_exit(&fcip_global_mutex);
return (EBUSY);
}
mutex_exit(&fcip_global_mutex);
if ((rval = mod_remove(&modlinkage)) != 0) {
return (rval);
}
/*
* unregister with the transport layer
*/
rval1 = fc_ulp_remove(&fcip_modinfo);
/*
* 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
if (rval1 == FC_BADULP) {
FCIP_DEBUG(FCIP_DEBUG_DEFAULT, (CE_WARN,
"fcip: ULP was never registered with the transport"));
rval = ENODEV;
} else if (rval1 == FC_BADTYPE) {
FCIP_DEBUG(FCIP_DEBUG_DEFAULT, (CE_WARN,
"fcip: No ULP of this type 0x%x was registered with "
"transport", fcip_modinfo.ulp_type));
rval = ENODEV;
}
#endif /* DEBUG */
mutex_destroy(&fcip_global_mutex);
rw_destroy(&fcipstruplock);
cv_destroy(&fcip_global_cv);
ddi_soft_state_fini(&fcip_softp);
FCIP_TNF_UNLOAD(&modlinkage);
return (rval);
}
/*
* Info about this loadable module
*/
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
/*
* 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
fcip_port_attach(opaque_t ulp_handle, fc_ulp_port_info_t *port_info,
fc_attach_cmd_t cmd, uint32_t sid)
{
int rval = FC_FAILURE;
int instance;
struct fcip *fptr;
fcip_port_info_t *fport = NULL;
fcip_port_info_t *cur_fport;
fc_portid_t src_id;
switch (cmd) {
case FC_CMD_ATTACH: {
la_wwn_t *ww_pn = NULL;
/*
* It was determined that, as per spec, the lower 48 bits of
* the port-WWN will always be unique. This will make the MAC
* address (i.e the lower 48 bits of the WWN), that IP/ARP
* 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.
*/
ww_pn = &port_info->port_pwwn;
FCIP_TNF_PROBE_2((fcip_port_attach, "fcip io", /* CSTYLED */,
tnf_string, msg, "port id bits",
tnf_opaque, nport_id, ww_pn->w.nport_id));
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_NOTE,
"port id bits: 0x%x", ww_pn->w.nport_id));
/*
* A port has come online
*/
mutex_enter(&fcip_global_mutex);
fcip_num_instances++;
fcip_num_attaching++;
if (fcip_port_head == NULL) {
/* OK to sleep here ? */
fport = kmem_zalloc(sizeof (fcip_port_info_t),
KM_NOSLEEP);
if (fport == NULL) {
fcip_num_instances--;
fcip_num_attaching--;
ASSERT(fcip_num_attaching >= 0);
mutex_exit(&fcip_global_mutex);
rval = FC_FAILURE;
cmn_err(CE_WARN, "!fcip(%d): port attach "
"failed: alloc failed",
ddi_get_instance(port_info->port_dip));
goto done;
}
fcip_port_head = fport;
} else {
/*
* traverse the port list and also check for
* duplicate port attaches - Nothing wrong in being
* paranoid Heh Heh.
*/
cur_fport = fcip_port_head;
while (cur_fport != NULL) {
if (cur_fport->fcipp_handle ==
port_info->port_handle) {
fcip_num_instances--;
fcip_num_attaching--;
ASSERT(fcip_num_attaching >= 0);
mutex_exit(&fcip_global_mutex);
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_WARN,
"!fcip(%d): port already "
"attached!!", ddi_get_instance(
port_info->port_dip)));
rval = FC_FAILURE;
goto done;
}
cur_fport = cur_fport->fcipp_next;
}
fport = kmem_zalloc(sizeof (fcip_port_info_t),
KM_NOSLEEP);
if (fport == NULL) {
rval = FC_FAILURE;
fcip_num_instances--;
fcip_num_attaching--;
ASSERT(fcip_num_attaching >= 0);
mutex_exit(&fcip_global_mutex);
cmn_err(CE_WARN, "!fcip(%d): port attach "
"failed: alloc failed",
ddi_get_instance(port_info->port_dip));
goto done;
}
fport->fcipp_next = fcip_port_head;
fcip_port_head = fport;
}
mutex_exit(&fcip_global_mutex);
/*
* now fill in the details about the port itself
*/
fport->fcipp_linkage = *port_info->port_linkage;
fport->fcipp_handle = port_info->port_handle;
fport->fcipp_dip = port_info->port_dip;
fport->fcipp_topology = port_info->port_flags;
fport->fcipp_pstate = port_info->port_state;
fport->fcipp_naa = port_info->port_pwwn.w.naa_id;
bcopy(&port_info->port_pwwn, &fport->fcipp_pwwn,
sizeof (la_wwn_t));
bcopy(&port_info->port_nwwn, &fport->fcipp_nwwn,
sizeof (la_wwn_t));
fport->fcipp_fca_pkt_size = port_info->port_fca_pkt_size;
fport->fcipp_cmd_dma_attr = *port_info->port_cmd_dma_attr;
fport->fcipp_resp_dma_attr = *port_info->port_resp_dma_attr;
fport->fcipp_fca_acc_attr = *port_info->port_acc_attr;
src_id.port_id = sid;
src_id.priv_lilp_posit = 0;
fport->fcipp_sid = src_id;
/*
* allocate soft state for this instance
*/
instance = ddi_get_instance(fport->fcipp_dip);
if (ddi_soft_state_zalloc(fcip_softp,
instance) != DDI_SUCCESS) {
rval = FC_FAILURE;
cmn_err(CE_WARN, "!fcip(%d): port attach failed: "
"soft state alloc failed", instance);
goto failure;
}
fptr = ddi_get_soft_state(fcip_softp, instance);
if (fptr == NULL) {
rval = FC_FAILURE;
cmn_err(CE_WARN, "!fcip(%d): port attach failed: "
"failure to get soft state", instance);
goto failure;
}
/*
* initialize all mutexes and locks required for this module
*/
mutex_init(&fptr->fcip_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&fptr->fcip_ub_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&fptr->fcip_rt_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&fptr->fcip_dest_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&fptr->fcip_sendup_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(&fptr->fcip_farp_cv, NULL, CV_DRIVER, NULL);
cv_init(&fptr->fcip_sendup_cv, NULL, CV_DRIVER, NULL);
cv_init(&fptr->fcip_ub_cv, NULL, CV_DRIVER, NULL);
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_dip = fport->fcipp_dip; /* parent's dip */
fptr->fcip_instance = instance;
fptr->fcip_ub_upstream = 0;
if (FC_PORT_STATE_MASK(port_info->port_state) ==
FC_STATE_ONLINE) {
fptr->fcip_port_state = FCIP_PORT_ONLINE;
if (fptr->fcip_flags & FCIP_LINK_DOWN) {
fptr->fcip_flags &= ~FCIP_LINK_DOWN;
}
} else {
fptr->fcip_port_state = FCIP_PORT_OFFLINE;
}
fptr->fcip_flags |= FCIP_ATTACHING;
fptr->fcip_port_info = fport;
/*
* Extract our MAC addr from our port's WWN. The lower 48
* bits will be our MAC address
*/
wwn_to_ether(&fport->fcipp_nwwn, &fptr->fcip_macaddr);
fport->fcipp_fcip = fptr;
FCIP_DEBUG(FCIP_DEBUG_ATTACH,
(CE_NOTE, "fcipdest : 0x%lx, rtable : 0x%lx",
(long)(sizeof (fptr->fcip_dest)),
(long)(sizeof (fptr->fcip_rtable))));
bzero(fptr->fcip_dest, sizeof (fptr->fcip_dest));
bzero(fptr->fcip_rtable, sizeof (fptr->fcip_rtable));
/*
* create a taskq to handle sundry jobs for the driver
* This way we can have jobs run in parallel
*/
fptr->fcip_tq = taskq_create("fcip_tasks",
FCIP_NUM_THREADS, MINCLSYSPRI, FCIP_MIN_TASKS,
FCIP_MAX_TASKS, TASKQ_PREPOPULATE);
mutex_exit(&fptr->fcip_mutex);
/*
* 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.
*/
mutex_enter(&fptr->fcip_sendup_mutex);
if (thread_create(NULL, DEFAULTSTKSZ,
(void (*)())fcip_sendup_thr, (caddr_t)fptr, 0, &p0,
TS_RUN, minclsyspri) == NULL) {
mutex_exit(&fptr->fcip_sendup_mutex);
cmn_err(CE_WARN,
"!unable to create fcip sendup thread for "
" instance: 0x%x", instance);
rval = FC_FAILURE;
goto done;
}
fptr->fcip_sendup_thr_initted = 1;
fptr->fcip_sendup_head = fptr->fcip_sendup_tail = NULL;
mutex_exit(&fptr->fcip_sendup_mutex);
/* Let the attach handler do the rest */
if (fcip_port_attach_handler(fptr) != FC_SUCCESS) {
/*
* We have already cleaned up so return
*/
rval = FC_FAILURE;
cmn_err(CE_WARN, "!fcip(%d): port attach failed",
instance);
goto done;
}
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_CONT,
"!fcip attach for port instance (0x%x) successful",
instance));
rval = FC_SUCCESS;
goto done;
}
case FC_CMD_POWER_UP:
/* FALLTHROUGH */
case FC_CMD_RESUME:
mutex_enter(&fcip_global_mutex);
fport = fcip_port_head;
while (fport != NULL) {
if (fport->fcipp_handle == port_info->port_handle) {
break;
}
fport = fport->fcipp_next;
}
if (fport == NULL) {
rval = FC_SUCCESS;
mutex_exit(&fcip_global_mutex);
goto done;
}
rval = fcip_handle_resume(fport, port_info, cmd);
mutex_exit(&fcip_global_mutex);
goto done;
default:
FCIP_TNF_PROBE_2((fcip_port_attach, "fcip io", /* CSTYLED */,
tnf_string, msg, "unknown command type",
tnf_uint, cmd, cmd));
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_WARN,
"unknown cmd type 0x%x in port_attach", cmd));
rval = FC_FAILURE;
goto done;
}
failure:
if (fport) {
mutex_enter(&fcip_global_mutex);
fcip_num_attaching--;
ASSERT(fcip_num_attaching >= 0);
(void) fcip_softstate_free(fport);
fcip_port_attach_pending = 0;
mutex_exit(&fcip_global_mutex);
}
return (rval);
done:
mutex_enter(&fcip_global_mutex);
fcip_port_attach_pending = 0;
mutex_exit(&fcip_global_mutex);
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_port_attach_handler(struct fcip *fptr)
{
fcip_port_info_t *fport = fptr->fcip_port_info;
int rval = FC_FAILURE;
ASSERT(fport != NULL);
mutex_enter(&fcip_global_mutex);
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_NOTE,
"fcip module dip: %p instance: %d",
(void *)fcip_module_dip, ddi_get_instance(fptr->fcip_dip)));
if (fcip_module_dip == NULL) {
clock_t fcip_lbolt;
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
*/
fcip_lbolt += drv_usectohz(FCIP_INIT_DELAY);
FCIP_DEBUG(FCIP_DEBUG_ATTACH,
(CE_WARN, "cv_timedwait lbolt %lx", fcip_lbolt));
(void) cv_timedwait(&fcip_global_cv, &fcip_global_mutex,
fcip_lbolt);
if (fcip_module_dip == NULL) {
mutex_exit(&fcip_global_mutex);
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_WARN,
"fcip attach did not happen"));
goto port_attach_cleanup;
}
}
if ((!fcip_minor_node_created) &&
fcip_is_supported_fc_topology(fport->fcipp_topology)) {
/*
* 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.
*/
if (ddi_create_minor_node(fcip_module_dip, "fcip", S_IFCHR,
ddi_get_instance(fptr->fcip_dip), DDI_PSEUDO,
CLONE_DEV) == DDI_FAILURE) {
mutex_exit(&fcip_global_mutex);
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_WARN,
"failed to create minor node for fcip(%d)",
ddi_get_instance(fptr->fcip_dip)));
goto port_attach_cleanup;
}
fcip_minor_node_created++;
}
mutex_exit(&fcip_global_mutex);
/*
* initialize port for traffic
*/
if (fcip_init_port(fptr) != FC_SUCCESS) {
/* fcip_init_port has already cleaned up its stuff */
mutex_enter(&fcip_global_mutex);
if ((fcip_num_instances == 1) &&
(fcip_minor_node_created == 1)) {
/* Remove minor node iff this is the last instance */
ddi_remove_minor_node(fcip_module_dip, NULL);
}
mutex_exit(&fcip_global_mutex);
goto port_attach_cleanup;
}
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_flags &= ~FCIP_ATTACHING;
fptr->fcip_flags |= FCIP_INITED;
fptr->fcip_timeout_ticks = 0;
/*
* start the timeout threads
*/
fptr->fcip_timeout_id = timeout(fcip_timeout, fptr,
drv_usectohz(1000000));
mutex_exit(&fptr->fcip_mutex);
mutex_enter(&fcip_global_mutex);
fcip_num_attaching--;
ASSERT(fcip_num_attaching >= 0);
mutex_exit(&fcip_global_mutex);
rval = FC_SUCCESS;
return (rval);
port_attach_cleanup:
mutex_enter(&fcip_global_mutex);
(void) fcip_softstate_free(fport);
fcip_num_attaching--;
ASSERT(fcip_num_attaching >= 0);
mutex_exit(&fcip_global_mutex);
rval = FC_FAILURE;
return (rval);
}
/*
* Handler for DDI_RESUME operations. Port must be ready to restart IP
* traffic on resume
*/
static int
fcip_handle_resume(fcip_port_info_t *fport, fc_ulp_port_info_t *port_info,
fc_attach_cmd_t cmd)
{
int rval = FC_SUCCESS;
struct fcip *fptr = fport->fcipp_fcip;
struct fcipstr *tslp;
int index;
ASSERT(fptr != NULL);
mutex_enter(&fptr->fcip_mutex);
if (cmd == FC_CMD_POWER_UP) {
fptr->fcip_flags &= ~(FCIP_POWER_DOWN);
if (fptr->fcip_flags & FCIP_SUSPENDED) {
mutex_exit(&fptr->fcip_mutex);
return (FC_SUCCESS);
}
} else if (cmd == FC_CMD_RESUME) {
fptr->fcip_flags &= ~(FCIP_SUSPENDED);
} else {
mutex_exit(&fptr->fcip_mutex);
return (FC_FAILURE);
}
/*
* set the current port state and topology
*/
fport->fcipp_topology = port_info->port_flags;
fport->fcipp_pstate = port_info->port_state;
rw_enter(&fcipstruplock, RW_READER);
for (tslp = fcipstrup; tslp; tslp = tslp->sl_nextp) {
if (tslp->sl_fcip == fptr) {
break;
}
}
rw_exit(&fcipstruplock);
/*
* No active streams on this port
*/
if (tslp == NULL) {
rval = FC_SUCCESS;
goto done;
}
mutex_enter(&fptr->fcip_rt_mutex);
for (index = 0; index < FCIP_RT_HASH_ELEMS; index++) {
struct fcip_routing_table *frp;
frp = fptr->fcip_rtable[index];
while (frp) {
uint32_t did;
/*
* Mark the broadcast RTE available again. It
* was marked SUSPENDED during SUSPEND.
*/
did = fcip_get_broadcast_did(fptr);
if (frp->fcipr_d_id.port_id == did) {
frp->fcipr_state = 0;
index = FCIP_RT_HASH_ELEMS;
break;
}
frp = frp->fcipr_next;
}
}
mutex_exit(&fptr->fcip_rt_mutex);
/*
* 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.
*/
fcip_handle_topology(fptr);
done:
/*
* Restart the timeout thread
*/
fptr->fcip_timeout_id = timeout(fcip_timeout, fptr,
drv_usectohz(1000000));
mutex_exit(&fptr->fcip_mutex);
return (rval);
}
/*
* Insert a destination port entry into the routing table for
* this port
*/
static void
fcip_rt_update(struct fcip *fptr, fc_portmap_t *devlist, uint32_t listlen)
{
struct fcip_routing_table *frp;
fcip_port_info_t *fport = fptr->fcip_port_info;
int hash_bucket, i;
fc_portmap_t *pmap;
char wwn_buf[20];
FCIP_TNF_PROBE_2((fcip_rt_update, "fcip io", /* CSTYLED */,
tnf_string, msg, "enter",
tnf_int, listlen, listlen));
ASSERT(!mutex_owned(&fptr->fcip_mutex));
mutex_enter(&fptr->fcip_rt_mutex);
for (i = 0; i < listlen; i++) {
pmap = &(devlist[i]);
frp = fcip_lookup_rtable(fptr, &(pmap->map_pwwn),
FCIP_COMPARE_PWWN);
/*
* 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.
*/
switch (pmap->map_type) {
/* FALLTHROUGH */
case PORT_DEVICE_NOCHANGE:
/* FALLTHROUGH */
case PORT_DEVICE_USER_LOGIN:
/* FALLTHROUGH */
case PORT_DEVICE_CHANGED:
/* FALLTHROUGH */
case PORT_DEVICE_NEW:
if (frp == NULL) {
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) {
uint32_t did;
/*
* Mark the routing table as invalid to bail
* the packets early that are in transit
*/
did = fptr->fcip_broadcast_did;
if (frp->fcipr_d_id.port_id != did) {
frp->fcipr_pd = NULL;
frp->fcipr_state = FCIP_RT_INVALID;
frp->fcipr_invalid_timeout =
fptr->fcip_timeout_ticks +
FCIP_RTE_TIMEOUT;
}
}
continue;
default:
FCIP_DEBUG(FCIP_DEBUG_INIT, (CE_WARN,
"unknown map flags in rt_update"));
continue;
}
add_new_entry:
ASSERT(frp == NULL);
hash_bucket = FCIP_RT_HASH(pmap->map_pwwn.raw_wwn);
ASSERT(hash_bucket < FCIP_RT_HASH_ELEMS);
FCIP_TNF_PROBE_2((fcip_rt_update, "cfip io", /* CSTYLED */,
tnf_string, msg,
"add new entry",
tnf_int, hashbucket, hash_bucket));
frp = (struct fcip_routing_table *)
kmem_zalloc(sizeof (struct fcip_routing_table), KM_SLEEP);
/* insert at beginning of hash bucket */
frp->fcipr_next = fptr->fcip_rtable[hash_bucket];
fptr->fcip_rtable[hash_bucket] = frp;
fc_wwn_to_str(&pmap->map_pwwn, wwn_buf);
FCIP_DEBUG(FCIP_DEBUG_ATTACH, (CE_NOTE,
"added entry for pwwn %s and d_id 0x%x",
wwn_buf, pmap->map_did.port_id));
update_entry:
bcopy((void *)&pmap->map_pwwn,
(void *)&frp->fcipr_pwwn, sizeof (la_wwn_t));
bcopy((void *)&pmap->map_nwwn, (void *)&frp->fcipr_nwwn,
sizeof (la_wwn_t));
frp->fcipr_d_id = pmap->map_did;
frp->fcipr_state = pmap->map_state;
frp->fcipr_pd = pmap->map_pd;
/*
* 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.
*/
if ((frp->fcipr_pd == NULL) &&
(frp->fcipr_d_id.port_id != fptr->fcip_broadcast_did)) {
frp->fcipr_state = PORT_DEVICE_INVALID;
frp->fcipr_invalid_timeout = fptr->fcip_timeout_ticks +
(FCIP_RTE_TIMEOUT / 2);
}
frp->fcipr_fca_dev =
fc_ulp_get_fca_device(fport->fcipp_handle, pmap->map_did);
/*
* login to the remote port. Don't worry about
* plogi failures for now
*/
if (pmap->map_pd != NULL) {
(void) fcip_do_plogi(fptr, frp);
} else if (FC_TOP_EXTERNAL(fport->fcipp_topology)) {
fc_wwn_to_str(&frp->fcipr_pwwn, wwn_buf);
FCIP_DEBUG(FCIP_DEBUG_MISC, (CE_NOTE,
"logging into pwwn %s, d_id 0x%x",
wwn_buf, frp->fcipr_d_id.port_id));
(void) fcip_do_plogi(fptr, frp);
}
FCIP_TNF_BYTE_ARRAY(fcip_rt_update, "fcip io", "detail",
"new wwn in rt", pwwn,
&frp->fcipr_pwwn, sizeof (la_wwn_t));
}
mutex_exit(&fptr->fcip_rt_mutex);
}
/*
* return a matching routing table entry for a given fcip instance
*/
struct fcip_routing_table *
fcip_lookup_rtable(struct fcip *fptr, la_wwn_t *wwn, int matchflag)
{
struct fcip_routing_table *frp = NULL;
int hash_bucket;
FCIP_TNF_PROBE_1((fcip_lookup_rtable, "fcip io", /* CSTYLED */,
tnf_string, msg, "enter"));
FCIP_TNF_BYTE_ARRAY(fcip_lookup_rtable, "fcip io", "detail",
"rtable lookup for", wwn,
&wwn->raw_wwn, sizeof (la_wwn_t));
FCIP_TNF_PROBE_2((fcip_lookup_rtable, "fcip io", /* CSTYLED */,
tnf_string, msg, "match by",
tnf_int, matchflag, matchflag));
ASSERT(mutex_owned(&fptr->fcip_rt_mutex));
hash_bucket = FCIP_RT_HASH(wwn->raw_wwn);
frp = fptr->fcip_rtable[hash_bucket];
while (frp != NULL) {
FCIP_TNF_BYTE_ARRAY(fcip_lookup_rtable, "fcip io", "detail",
"rtable entry", nwwn,
&(frp->fcipr_nwwn.raw_wwn), sizeof (la_wwn_t));
if (fcip_wwn_compare(&frp->fcipr_pwwn, wwn, matchflag) == 0) {
break;
}
frp = frp->fcipr_next;
}
FCIP_TNF_PROBE_2((fcip_lookup_rtable, "fcip io", /* CSTYLED */,
tnf_string, msg, "lookup result",
tnf_opaque, frp, frp));
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
fcip_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
switch ((int)cmd) {
case DDI_ATTACH: {
ASSERT(fcip_module_dip == NULL);
fcip_module_dip = dip;
/*
* 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.
*/
mutex_enter(&fcip_global_mutex);
FCIP_DEBUG(FCIP_DEBUG_ATTACH,
(CE_WARN, "global cv - signaling"));
cv_signal(&fcip_global_cv);
FCIP_DEBUG(FCIP_DEBUG_ATTACH,
(CE_WARN, "global cv - signaled"));
mutex_exit(&fcip_global_mutex);
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
fcip_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
struct fcip *fptr;
fcip_port_info_t *fport;
int detached;
switch (cmd) {
case DDI_DETACH: {
/*
* If we got here, any active streams should have been
* unplumbed but check anyway
*/
mutex_enter(&fcip_global_mutex);
if (fcipstrup != NULL) {
mutex_exit(&fcip_global_mutex);
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
*/
fport = fcip_port_head;
while (fport != NULL) {
fptr = fport->fcipp_fcip;
if (fptr == NULL) {
continue;
}
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_flags |= FCIP_DETACHING;
if (fptr->fcip_ipq ||
fptr->fcip_flags & (FCIP_IN_TIMEOUT |
FCIP_IN_CALLBACK | FCIP_ATTACHING |
FCIP_SUSPENDED | FCIP_POWER_DOWN |
FCIP_REG_INPROGRESS)) {
FCIP_TNF_PROBE_1((fcip_detach,
"fcip io", /* CSTYLED */,
tnf_string, msg,
"fcip instance busy"));
mutex_exit(&fptr->fcip_mutex);
FCIP_DEBUG(FCIP_DEBUG_DETACH, (CE_WARN,
"fcip instance busy"));
break;
}
/*
* Check for any outstanding pkts. If yes
* fail the detach
*/
mutex_enter(&fptr->fcip_dest_mutex);
if (fcip_port_get_num_pkts(fptr) > 0) {
mutex_exit(&fptr->fcip_dest_mutex);
mutex_exit(&fptr->fcip_mutex);
FCIP_DEBUG(FCIP_DEBUG_DETACH, (CE_WARN,
"fcip instance busy - pkts "
"pending"));
break;
}
mutex_exit(&fptr->fcip_dest_mutex);
mutex_enter(&fptr->fcip_rt_mutex);
if (fcip_plogi_in_progress(fptr)) {
mutex_exit(&fptr->fcip_rt_mutex);
mutex_exit(&fptr->fcip_mutex);
FCIP_DEBUG(FCIP_DEBUG_DETACH, (CE_WARN,
"fcip instance busy - plogi in "
"progress"));
break;
}
mutex_exit(&fptr->fcip_rt_mutex);
mutex_exit(&fptr->fcip_mutex);
fport = fport->fcipp_next;
}
/*
* if fport is non NULL - we have active ports
*/
if (fport != NULL) {
/*
* Remove the DETACHING flags on the ports
*/
fport = fcip_port_head;
while (fport != NULL) {
fptr = fport->fcipp_fcip;
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_flags &= ~(FCIP_DETACHING);
mutex_exit(&fptr->fcip_mutex);
fport = fport->fcipp_next;
}
mutex_exit(&fcip_global_mutex);
return (DDI_FAILURE);
}
}
/*
* free up all softstate structures
*/
fport = fcip_port_head;
while (fport != NULL) {
detached = 1;
fptr = fport->fcipp_fcip;
if (fptr) {
mutex_enter(&fptr->fcip_mutex);
/*
* Check to see if somebody beat us to the
* punch
*/
detached = fptr->fcip_flags & FCIP_DETACHED;
fptr->fcip_flags &= ~(FCIP_DETACHING);
fptr->fcip_flags |= FCIP_DETACHED;
mutex_exit(&fptr->fcip_mutex);
}
if (!detached) {
fport = fcip_softstate_free(fport);
} 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.
*/
fport = fport->fcipp_next;
}
FCIP_DEBUG(FCIP_DEBUG_DETACH,
(CE_NOTE, "detaching port"));
FCIP_TNF_PROBE_1((fcip_detach,
"fcip io", /* CSTYLED */, tnf_string,
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) {
mutex_exit(&fcip_global_mutex);
return (DDI_FAILURE);
}
fcip_num_instances = 0;
mutex_exit(&fcip_global_mutex);
fcip_module_dip = NULL;
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
fcip_port_detach(opaque_t ulp_handle, fc_ulp_port_info_t *port_info,
fc_detach_cmd_t cmd)
{
int rval = FC_FAILURE;
fcip_port_info_t *fport;
struct fcip *fptr;
struct fcipstr *strp;
switch (cmd) {
case FC_CMD_DETACH: {
mutex_enter(&fcip_global_mutex);
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.
*/
fcip_check_remove_minor_node();
mutex_exit(&fcip_global_mutex);
return (FC_SUCCESS);
} else {
/*
* traverse the port list
*/
fport = fcip_port_head;
while (fport != NULL) {
if (fport->fcipp_handle ==
port_info->port_handle) {
fptr = fport->fcipp_fcip;
/*
* Fail the port detach if there is
* still an attached, bound stream on
* this interface.
*/
rw_enter(&fcipstruplock, RW_READER);
for (strp = fcipstrup; strp != NULL;
strp = strp->sl_nextp) {
if (strp->sl_fcip == fptr) {
rw_exit(&fcipstruplock);
mutex_exit(
&fcip_global_mutex);
return (FC_FAILURE);
}
}
rw_exit(&fcipstruplock);
/*
* fail port detach if we are in
* the middle of a deferred port attach
* or if the port has outstanding pkts
*/
if (fptr != NULL) {
mutex_enter(&fptr->fcip_mutex);
if (fcip_check_port_busy
(fptr) ||
(fptr->fcip_flags &
FCIP_DETACHED)) {
mutex_exit(
&fptr->fcip_mutex);
mutex_exit(
&fcip_global_mutex);
return (FC_FAILURE);
}
fptr->fcip_flags |=
FCIP_DETACHED;
mutex_exit(&fptr->fcip_mutex);
}
(void) fcip_softstate_free(fport);
fcip_check_remove_minor_node();
mutex_exit(&fcip_global_mutex);
return (FC_SUCCESS);
}
fport = fport->fcipp_next;
}
ASSERT(fport == NULL);
}
mutex_exit(&fcip_global_mutex);
break;
}
case FC_CMD_POWER_DOWN:
/* FALLTHROUGH */
case FC_CMD_SUSPEND:
mutex_enter(&fcip_global_mutex);
fport = fcip_port_head;
while (fport != NULL) {
if (fport->fcipp_handle == port_info->port_handle) {
break;
}
fport = fport->fcipp_next;
}
if (fport == NULL) {
mutex_exit(&fcip_global_mutex);
break;
}
rval = fcip_handle_suspend(fport, cmd);
mutex_exit(&fcip_global_mutex);
break;
default:
FCIP_DEBUG(FCIP_DEBUG_DETACH,
(CE_WARN, "unknown port detach command!!"));
break;
}
return (rval);
}
/*
* Returns 0 if the port is not busy, else returns non zero.
*/
static int
fcip_check_port_busy(struct fcip *fptr)
{
int rval = 0, num_pkts = 0;
ASSERT(fptr != NULL);
ASSERT(MUTEX_HELD(&fptr->fcip_mutex));
mutex_enter(&fptr->fcip_dest_mutex);
if (fptr->fcip_flags & FCIP_PORT_BUSY ||
((num_pkts = fcip_port_get_num_pkts(fptr)) > 0) ||
fptr->fcip_num_ipkts_pending) {
rval = 1;
FCIP_DEBUG(FCIP_DEBUG_DETACH,
(CE_NOTE, "!fcip_check_port_busy: port is busy "
"fcip_flags: 0x%x, num_pkts: 0x%x, ipkts_pending: 0x%lx!",
fptr->fcip_flags, num_pkts, fptr->fcip_num_ipkts_pending));
}
mutex_exit(&fptr->fcip_dest_mutex);
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
fcip_check_remove_minor_node(void)
{
ASSERT(MUTEX_HELD(&fcip_global_mutex));
/*
* If there are no more fcip (fp) instances, remove the
* minor node for fcip.
* Reset fcip_minor_node_created to invalidate it.
*/
if (fcip_num_instances == 0 && (fcip_module_dip != NULL)) {
ddi_remove_minor_node(fcip_module_dip, NULL);
fcip_minor_node_created = 0;
}
}
/*
* This routine permits the suspend operation during a CPR/System
* power management operation. The routine basically quiesces I/Os
* on all active interfaces
*/
static int
fcip_handle_suspend(fcip_port_info_t *fport, fc_detach_cmd_t cmd)
{
struct fcip *fptr = fport->fcipp_fcip;
timeout_id_t tid;
int index;
int tryagain = 0;
int count;
struct fcipstr *tslp;
ASSERT(fptr != NULL);
mutex_enter(&fptr->fcip_mutex);
/*
* 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 &&
((fptr->fcip_flags & FCIP_IN_CALLBACK) ||
(fptr->fcip_flags & FCIP_IN_TIMEOUT))) {
mutex_exit(&fptr->fcip_mutex);
drv_usecwait(1000000);
mutex_enter(&fptr->fcip_mutex);
}
if (fptr->fcip_flags & FCIP_IN_CALLBACK ||
fptr->fcip_flags & FCIP_IN_TIMEOUT) {
mutex_exit(&fptr->fcip_mutex);
return (FC_FAILURE);
}
if (cmd == FC_CMD_POWER_DOWN) {
if (fptr->fcip_flags & FCIP_SUSPENDED) {
fptr->fcip_flags |= FCIP_POWER_DOWN;
mutex_exit(&fptr->fcip_mutex);
goto success;
} else {
fptr->fcip_flags |= FCIP_POWER_DOWN;
}
} else if (cmd == FC_CMD_SUSPEND) {
fptr->fcip_flags |= FCIP_SUSPENDED;
} else {
mutex_exit(&fptr->fcip_mutex);
return (FC_FAILURE);
}
mutex_exit(&fptr->fcip_mutex);
/*
* If no streams are plumbed - its the easiest case - Just
* bail out without having to do much
*/
rw_enter(&fcipstruplock, RW_READER);
for (tslp = fcipstrup; tslp; tslp = tslp->sl_nextp) {
if (tslp->sl_fcip == fptr) {
break;
}
}
rw_exit(&fcipstruplock);
/*
* No active streams on this port
*/
if (tslp == NULL) {
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.
*/
mutex_enter(&fptr->fcip_rt_mutex);
while (!tryagain) {
tryagain = 0;
for (index = 0; index < FCIP_RT_HASH_ELEMS; index++) {
struct fcip_routing_table *frp;
struct fcip_dest *fdestp;
la_wwn_t *pwwn;
int hash_bucket;
frp = fptr->fcip_rtable[index];
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.
*/
frp->fcipr_state = FCIP_RT_SUSPENDED;
pwwn = &frp->fcipr_pwwn;
/*
* Get hold of destination pointer
*/
mutex_enter(&fptr->fcip_dest_mutex);
hash_bucket = FCIP_DEST_HASH(pwwn->raw_wwn);
ASSERT(hash_bucket < FCIP_DEST_HASH_ELEMS);
fdestp = fptr->fcip_dest[hash_bucket];
while (fdestp != NULL) {
mutex_enter(&fdestp->fcipd_mutex);
if (fdestp->fcipd_rtable) {
if (fcip_wwn_compare(pwwn,
&fdestp->fcipd_pwwn,
FCIP_COMPARE_PWWN) == 0) {
mutex_exit(
&fdestp->fcipd_mutex);
break;
}
}
mutex_exit(&fdestp->fcipd_mutex);
fdestp = fdestp->fcipd_next;
}
mutex_exit(&fptr->fcip_dest_mutex);
if (fdestp == NULL) {
frp = frp->fcipr_next;
continue;
}
/*
* Wait for fcip_wait_cmds seconds for
* the commands to drain.
*/
count = 0;
mutex_enter(&fdestp->fcipd_mutex);
while (fdestp->fcipd_ncmds &&
count < fcip_wait_cmds) {
mutex_exit(&fdestp->fcipd_mutex);
mutex_exit(&fptr->fcip_rt_mutex);
drv_usecwait(1000000);
mutex_enter(&fptr->fcip_rt_mutex);
mutex_enter(&fdestp->fcipd_mutex);
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++;
}
mutex_exit(&fdestp->fcipd_mutex);
frp = frp->fcipr_next;
}
}
if (tryagain == 0) {
break;
}
}
mutex_exit(&fptr->fcip_rt_mutex);
if (tryagain) {
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_flags &= ~(FCIP_SUSPENDED | FCIP_POWER_DOWN);
mutex_exit(&fptr->fcip_mutex);
return (FC_FAILURE);
}
success:
mutex_enter(&fptr->fcip_mutex);
tid = fptr->fcip_timeout_id;
fptr->fcip_timeout_id = NULL;
mutex_exit(&fptr->fcip_mutex);
(void) untimeout(tid);
return (FC_SUCCESS);
}
/*
* the getinfo(9E) entry point
*/
/* ARGSUSED */
static int
fcip_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result)
{
int rval = DDI_FAILURE;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
*result = fcip_module_dip;
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
fcip_kstat_init(struct fcip *fptr)
{
int instance;
char buf[16];
struct fcipstat *fcipstatp;
ASSERT(mutex_owned(&fptr->fcip_mutex));
instance = ddi_get_instance(fptr->fcip_dip);
(void) sprintf(buf, "fcip%d", instance);
#ifdef kstat
fptr->fcip_kstatp = kstat_create("fcip", instance, buf, "net",
KSTAT_TYPE_NAMED,
(sizeof (struct fcipstat)/ sizeof (kstat_named_t)),
KSTAT_FLAG_PERSISTENT);
#else
fptr->fcip_kstatp = kstat_create("fcip", instance, buf, "net",
KSTAT_TYPE_NAMED,
(sizeof (struct fcipstat)/ sizeof (kstat_named_t)), 0);
#endif
if (fptr->fcip_kstatp == NULL) {
FCIP_DEBUG(FCIP_DEBUG_INIT, (CE_WARN, "kstat created failed"));
return;
}
fcipstatp = (struct fcipstat *)fptr->fcip_kstatp->ks_data;
kstat_named_init(&fcipstatp->fcips_ipackets, "ipackets",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_ierrors, "ierrors",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_opackets, "opackets",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_oerrors, "oerrors",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_collisions, "collisions",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_nocanput, "nocanput",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_allocbfail, "allocbfail",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_defer, "defer",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_fram, "fram",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_crc, "crc",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_oflo, "oflo",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_uflo, "uflo",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_missed, "missed",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_tlcol, "tlcol",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_trtry, "trtry",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_tnocar, "tnocar",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_inits, "inits",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_notbufs, "notbufs",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_norbufs, "norbufs",
KSTAT_DATA_ULONG);
kstat_named_init(&fcipstatp->fcips_allocbfail, "allocbfail",
KSTAT_DATA_ULONG);
/*
* required by kstat for MIB II objects(RFC 1213)
*/
kstat_named_init(&fcipstatp->fcips_rcvbytes, "fcips_rcvbytes",
KSTAT_DATA_ULONG); /* # octets received */
/* MIB - ifInOctets */
kstat_named_init(&fcipstatp->fcips_xmtbytes, "fcips_xmtbytes",
KSTAT_DATA_ULONG); /* # octets xmitted */
/* MIB - ifOutOctets */
kstat_named_init(&fcipstatp->fcips_multircv, "fcips_multircv",
KSTAT_DATA_ULONG); /* # multicast packets */
/* delivered to upper layer */
/* MIB - ifInNUcastPkts */
kstat_named_init(&fcipstatp->fcips_multixmt, "fcips_multixmt",
KSTAT_DATA_ULONG); /* # multicast packets */
/* requested to be sent */
/* MIB - ifOutNUcastPkts */
kstat_named_init(&fcipstatp->fcips_brdcstrcv, "fcips_brdcstrcv",
KSTAT_DATA_ULONG); /* # broadcast packets */
/* delivered to upper layer */
/* MIB - ifInNUcastPkts */
kstat_named_init(&fcipstatp->fcips_brdcstxmt, "fcips_brdcstxmt",
KSTAT_DATA_ULONG); /* # broadcast packets */
/* requested to be sent */
/* MIB - ifOutNUcastPkts */
kstat_named_init(&fcipstatp->fcips_norcvbuf, "fcips_norcvbuf",
KSTAT_DATA_ULONG); /* # rcv packets discarded */
/* MIB - ifInDiscards */
kstat_named_init(&fcipstatp->fcips_noxmtbuf, "fcips_noxmtbuf",
KSTAT_DATA_ULONG); /* # xmt packets discarded */
fptr->fcip_kstatp->ks_update = fcip_stat_update;
fptr->fcip_kstatp->ks_private = (void *) fptr;
kstat_install(fptr->fcip_kstatp);
}
/*
* Update the defined kstats for netstat et al to use
*/
/* ARGSUSED */
static int
fcip_stat_update(kstat_t *fcip_statp, int val)
{
struct fcipstat *fcipstatp;
struct fcip *fptr;
fptr = (struct fcip *)fcip_statp->ks_private;
fcipstatp = (struct fcipstat *)fcip_statp->ks_data;
if (val == KSTAT_WRITE) {
fptr->fcip_ipackets = fcipstatp->fcips_ipackets.value.ul;
fptr->fcip_ierrors = fcipstatp->fcips_ierrors.value.ul;
fptr->fcip_opackets = fcipstatp->fcips_opackets.value.ul;
fptr->fcip_oerrors = fcipstatp->fcips_oerrors.value.ul;
fptr->fcip_collisions = fcipstatp->fcips_collisions.value.ul;
fptr->fcip_defer = fcipstatp->fcips_defer.value.ul;
fptr->fcip_fram = fcipstatp->fcips_fram.value.ul;
fptr->fcip_crc = fcipstatp->fcips_crc.value.ul;
fptr->fcip_oflo = fcipstatp->fcips_oflo.value.ul;
fptr->fcip_uflo = fcipstatp->fcips_uflo.value.ul;
fptr->fcip_missed = fcipstatp->fcips_missed.value.ul;
fptr->fcip_tlcol = fcipstatp->fcips_tlcol.value.ul;
fptr->fcip_trtry = fcipstatp->fcips_trtry.value.ul;
fptr->fcip_tnocar = fcipstatp->fcips_tnocar.value.ul;
fptr->fcip_inits = fcipstatp->fcips_inits.value.ul;
fptr->fcip_notbufs = fcipstatp->fcips_notbufs.value.ul;
fptr->fcip_norbufs = fcipstatp->fcips_norbufs.value.ul;
fptr->fcip_nocanput = fcipstatp->fcips_nocanput.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_rcvbytes = fcipstatp->fcips_rcvbytes.value.ul;
fptr->fcip_xmtbytes = fcipstatp->fcips_xmtbytes.value.ul;
fptr->fcip_multircv = fcipstatp->fcips_multircv.value.ul;
fptr->fcip_multixmt = fcipstatp->fcips_multixmt.value.ul;
fptr->fcip_brdcstrcv = fcipstatp->fcips_brdcstrcv.value.ul;
fptr->fcip_norcvbuf = fcipstatp->fcips_norcvbuf.value.ul;
fptr->fcip_noxmtbuf = fcipstatp->fcips_noxmtbuf.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
fptr->fcip_allocbfail = fcipstatp->fcips_allocbfail.value.ul;
} else {
fcipstatp->fcips_ipackets.value.ul = fptr->fcip_ipackets;
fcipstatp->fcips_ierrors.value.ul = fptr->fcip_ierrors;
fcipstatp->fcips_opackets.value.ul = fptr->fcip_opackets;
fcipstatp->fcips_oerrors.value.ul = fptr->fcip_oerrors;
fcipstatp->fcips_collisions.value.ul = fptr->fcip_collisions;
fcipstatp->fcips_nocanput.value.ul = fptr->fcip_nocanput;
fcipstatp->fcips_allocbfail.value.ul = fptr->fcip_allocbfail;
fcipstatp->fcips_defer.value.ul = fptr->fcip_defer;
fcipstatp->fcips_fram.value.ul = fptr->fcip_fram;
fcipstatp->fcips_crc.value.ul = fptr->fcip_crc;
fcipstatp->fcips_oflo.value.ul = fptr->fcip_oflo;
fcipstatp->fcips_uflo.value.ul = fptr->fcip_uflo;
fcipstatp->fcips_missed.value.ul = fptr->fcip_missed;
fcipstatp->fcips_tlcol.value.ul = fptr->fcip_tlcol;
fcipstatp->fcips_trtry.value.ul = fptr->fcip_trtry;
fcipstatp->fcips_tnocar.value.ul = fptr->fcip_tnocar;
fcipstatp->fcips_inits.value.ul = fptr->fcip_inits;
fcipstatp->fcips_norbufs.value.ul = fptr->fcip_norbufs;
fcipstatp->fcips_notbufs.value.ul = fptr->fcip_notbufs;
fcipstatp->fcips_rcvbytes.value.ul = fptr->fcip_rcvbytes;
fcipstatp->fcips_xmtbytes.value.ul = fptr->fcip_xmtbytes;
fcipstatp->fcips_multircv.value.ul = fptr->fcip_multircv;
fcipstatp->fcips_multixmt.value.ul = fptr->fcip_multixmt;
fcipstatp->fcips_brdcstrcv.value.ul = fptr->fcip_brdcstrcv;
fcipstatp->fcips_brdcstxmt.value.ul = fptr->fcip_brdcstxmt;
fcipstatp->fcips_norcvbuf.value.ul = fptr->fcip_norcvbuf;
fcipstatp->fcips_noxmtbuf.value.ul = fptr->fcip_noxmtbuf;
}
return (0);
}
/*
* fcip_statec_cb: handles all required state change callback notifications
* it receives from the transport
*/
/* ARGSUSED */
static void
fcip_statec_cb(opaque_t ulp_handle, opaque_t phandle,
uint32_t port_state, uint32_t port_top, fc_portmap_t changelist[],
uint32_t listlen, uint32_t sid)
{
fcip_port_info_t *fport;
struct fcip *fptr;
struct fcipstr *slp;
queue_t *wrq;
int instance;
int index;
struct fcip_routing_table *frtp;
fport = fcip_get_port(phandle);
if (fport == NULL) {
return;
}
fptr = fport->fcipp_fcip;
ASSERT(fptr != NULL);
if (fptr == NULL) {
return;
}
instance = ddi_get_instance(fport->fcipp_dip);
FCIP_TNF_PROBE_4((fcip_statec_cb, "fcip io", /* CSTYLED */,
tnf_string, msg, "state change callback",
tnf_uint, instance, instance,
tnf_uint, S_ID, sid,
tnf_int, count, listlen));
FCIP_DEBUG(FCIP_DEBUG_ELS,
(CE_NOTE, "fcip%d, state change callback: state:0x%x, "
"S_ID:0x%x, count:0x%x", instance, port_state, sid, listlen));
mutex_enter(&fptr->fcip_mutex);
if ((fptr->fcip_flags & (FCIP_DETACHING | FCIP_DETACHED)) ||
(fptr->fcip_flags & (FCIP_SUSPENDED | FCIP_POWER_DOWN))) {
mutex_exit(&fptr->fcip_mutex);
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.
*/
fptr->fcip_flags |= FCIP_IN_SC_CB;
fport->fcipp_pstate = port_state;
/*
* Check if topology changed. If Yes - Modify the broadcast
* RTE entries to understand the new broadcast D_IDs
*/
if (fport->fcipp_topology != port_top &&
(port_top != FC_TOP_UNKNOWN)) {
/* REMOVE later */
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_NOTE,
"topology changed: Old topology: 0x%x New topology 0x%x",
fport->fcipp_topology, port_top));
/*
* If topology changed - attempt a rediscovery of
* devices. Helps specially in Fabric/Public loops
* and if on_demand_node_creation is disabled
*/
fport->fcipp_topology = port_top;
fcip_handle_topology(fptr);
}
mutex_exit(&fptr->fcip_mutex);
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.
*/
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_NOTE,
"Statec_cb: fabric topology"));
fcip_rt_update(fptr, changelist, listlen);
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.
*/
FCIP_DEBUG(FCIP_DEBUG_ELS,
(CE_NOTE, "Statec_cb: NO_NS topology"));
/* FALLTHROUGH */
case FC_TOP_PRIVATE_LOOP:
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_NOTE,
"Statec_cb: Pvt_Loop topology"));
/* FALLTHROUGH */
case FC_TOP_PT_PT:
/*
* call get_port_map() and update routing table
*/
fcip_rt_update(fptr, changelist, listlen);
break;
default:
FCIP_DEBUG(FCIP_DEBUG_ELS,
(CE_NOTE, "Statec_cb: Unknown topology"));
}
/*
* We should now enable the Queues and permit I/Os
* 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
*/
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_port_state = FCIP_PORT_ONLINE;
if (fptr->fcip_flags & FCIP_LINK_DOWN) {
fptr->fcip_flags &= ~FCIP_LINK_DOWN;
}
mutex_exit(&fptr->fcip_mutex);
/*
* Enable write queues
*/
rw_enter(&fcipstruplock, RW_READER);
for (slp = fcipstrup; slp != NULL; slp = slp->sl_nextp) {
if (slp && slp->sl_fcip == fptr) {
wrq = WR(slp->sl_rq);
if (wrq->q_flag & QFULL) {
qenable(wrq);
}
}
}
rw_exit(&fcipstruplock);
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
*/
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_mark_offline = fptr->fcip_timeout_ticks +
FCIP_OFFLINE_TIMEOUT;
fptr->fcip_port_state = FCIP_PORT_OFFLINE;
mutex_exit(&fptr->fcip_mutex);
/*
* Mark all Routing table entries as invalid to prevent
* any commands from trickling through to ports that
* have disappeared from under us
*/
mutex_enter(&fptr->fcip_rt_mutex);
for (index = 0; index < FCIP_RT_HASH_ELEMS; index++) {
frtp = fptr->fcip_rtable[index];
while (frtp) {
frtp->fcipr_state = PORT_DEVICE_INVALID;
frtp = frtp->fcipr_next;
}
}
mutex_exit(&fptr->fcip_rt_mutex);
break;
case FC_STATE_RESET_REQUESTED:
/*
* Release all Unsolicited buffers back to transport/FCA.
* 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.
*/
mutex_enter(&fptr->fcip_mutex);
#ifdef FCIP_ESBALLOC
while (fptr->fcip_ub_upstream) {
cv_wait(&fptr->fcip_ub_cv, &fptr->fcip_mutex);
}
#endif /* FCIP_ESBALLOC */
fptr->fcip_mark_offline = fptr->fcip_timeout_ticks +
FCIP_OFFLINE_TIMEOUT;
fptr->fcip_port_state = FCIP_PORT_OFFLINE;
mutex_exit(&fptr->fcip_mutex);
break;
case FC_STATE_DEVICE_CHANGE:
if (listlen) {
fcip_rt_update(fptr, changelist, 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;
}
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_flags &= ~(FCIP_IN_SC_CB);
mutex_exit(&fptr->fcip_mutex);
}
/*
* 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 *
fcip_get_port(opaque_t phandle)
{
fcip_port_info_t *fport;
ASSERT(phandle != NULL);
mutex_enter(&fcip_global_mutex);
fport = fcip_port_head;
while (fport != NULL) {
if (fport->fcipp_handle == phandle) {
/* found */
break;
}
fport = fport->fcipp_next;
}
mutex_exit(&fcip_global_mutex);
return (fport);
}
/*
* Handle inbound ELS requests received by the transport. We are only
* intereseted in FARP/InARP mostly.
*/
/* ARGSUSED */
static int
fcip_els_cb(opaque_t ulp_handle, opaque_t phandle,
fc_unsol_buf_t *buf, uint32_t claimed)
{
fcip_port_info_t *fport;
struct fcip *fptr;
int instance;
uchar_t r_ctl;
uchar_t ls_code;
la_els_farp_t farp_cmd;
la_els_farp_t *fcmd;
int rval = FC_UNCLAIMED;
fport = fcip_get_port(phandle);
if (fport == NULL) {
return (FC_UNCLAIMED);
}
fptr = fport->fcipp_fcip;
ASSERT(fptr != NULL);
if (fptr == NULL) {
return (FC_UNCLAIMED);
}
instance = ddi_get_instance(fport->fcipp_dip);
mutex_enter(&fptr->fcip_mutex);
if ((fptr->fcip_flags & (FCIP_DETACHING | FCIP_DETACHED)) ||
(fptr->fcip_flags & (FCIP_SUSPENDED | FCIP_POWER_DOWN))) {
mutex_exit(&fptr->fcip_mutex);
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.
*/
fptr->fcip_flags |= FCIP_IN_ELS_CB;
mutex_exit(&fptr->fcip_mutex);
FCIP_TNF_PROBE_2((fcip_els_cb, "fcip io", /* CSTYLED */,
tnf_string, msg, "ELS callback",
tnf_uint, instance, instance));
FCIP_DEBUG(FCIP_DEBUG_ELS,
(CE_NOTE, "fcip%d, ELS callback , ", instance));
r_ctl = buf->ub_frame.r_ctl;
switch (r_ctl & R_CTL_ROUTING) {
case R_CTL_EXTENDED_SVC:
if (r_ctl == R_CTL_ELS_REQ) {
ls_code = buf->ub_buffer[0];
if (ls_code == LA_ELS_FARP_REQ) {
/*
* Inbound FARP broadcast request
*/
if (buf->ub_bufsize != sizeof (la_els_farp_t)) {
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_WARN,
"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;
}
fcmd = (la_els_farp_t *)buf;
if (fcip_wwn_compare(&fcmd->resp_nwwn,
&fport->fcipp_nwwn,
FCIP_COMPARE_NWWN) != 0) {
rval = FC_UNCLAIMED;
goto els_cb_done;
}
/*
* copy the FARP request and release the
* unsolicited buffer
*/
fcmd = &farp_cmd;
bcopy((void *)buf, (void *)fcmd,
sizeof (la_els_farp_t));
(void) fc_ulp_ubrelease(fport->fcipp_handle, 1,
&buf->ub_token);
if (fcip_farp_supported &&
fcip_handle_farp_request(fptr, fcmd) ==
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) {
ls_code = buf->ub_buffer[0];
if (ls_code == LA_ELS_FARP_REPLY) {
/*
* We received a REPLY to our FARP request
*/
if (buf->ub_bufsize != sizeof (la_els_farp_t)) {
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_WARN,
"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;
}
fcmd = &farp_cmd;
bcopy((void *)buf, (void *)fcmd,
sizeof (la_els_farp_t));
(void) fc_ulp_ubrelease(fport->fcipp_handle, 1,
&buf->ub_token);
if (fcip_farp_supported &&
fcip_handle_farp_response(fptr, fcmd) ==
FC_SUCCESS) {
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_NOTE,
"Successfully recevied a FARP "
"response"));
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_farp_rsp_flag = 1;
cv_signal(&fptr->fcip_farp_cv);
mutex_exit(&fptr->fcip_mutex);
rval = FC_SUCCESS;
goto els_cb_done;
} else {
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_WARN,
"Unable to handle a FARP response "
"receive"));
rval = FC_UNCLAIMED;
goto els_cb_done;
}
}
}
break;
default:
break;
}
els_cb_done:
mutex_enter(&fptr->fcip_mutex);
fptr->fcip_flags &= ~(FCIP_IN_ELS_CB);
mutex_exit(&fptr->fcip_mutex);
return (rval);
}
/*
* Handle inbound FARP requests
*/
static int
fcip_handle_farp_request(struct fcip *fptr, la_els_farp_t *fcmd)
{
fcip_pkt_t *fcip_pkt;
fc_packet_t *fc_pkt;
fcip_port_info_t *fport = fptr->fcip_port_info;
int rval = FC_FAILURE;
opaque_t fca_dev;
fc_portmap_t map;
struct fcip_routing_table *frp;
struct fcip_dest *fdestp;
/*
* Add an entry for the remote port into our routing and destination
* tables.
*/
map.map_did = fcmd->req_id;
map.map_hard_addr.hard_addr = fcmd->req_id.port_id;
map.map_state = PORT_DEVICE_VALID;
map.map_type = PORT_DEVICE_NEW;
map.map_flags = 0;
map.map_pd = NULL;
bcopy((void *)&fcmd->req_pwwn, (void *)&map.map_pwwn,
sizeof (la_wwn_t));
bcopy((void *)&fcmd->req_nwwn, (void *)&map.map_nwwn,
sizeof (la_wwn_t));
fcip_rt_update(fptr, &map, 1);
mutex_enter(&fptr->fcip_rt_mutex);
frp = fcip_lookup_rtable(fptr, &fcmd->req_pwwn, FCIP_COMPARE_NWWN);
mutex_exit(&fptr->fcip_rt_mutex);
fdestp = fcip_add_dest(fptr, frp);
fcip_pkt = fcip_ipkt_alloc(fptr, sizeof (la_els_farp_t),
sizeof (la_els_farp_t), NULL, KM_SLEEP);
if (fcip_pkt == NULL) {
rval = FC_FAILURE;
goto farp_done;
}
/*
* Fill in our port's PWWN and NWWN
*/
fcmd->resp_pwwn = fport->fcipp_pwwn;
fcmd->resp_nwwn = fport->fcipp_nwwn;
fcip_init_unicast_pkt(fcip_pkt, fport->fcipp_sid,
fcmd->req_id, NULL);
fca_dev =
fc_ulp_get_fca_device(fport->fcipp_handle, fcmd->req_id);
fc_pkt = FCIP_PKT_TO_FC_PKT(fcip_pkt);
fc_pkt->pkt_cmd_fhdr.r_ctl = R_CTL_ELS_RSP;
fc_pkt->pkt_fca_device = fca_dev;
fcip_pkt->fcip_pkt_dest = fdestp;
/*
* Attempt a PLOGI again
*/
if (fcmd->resp_flags & FARP_INIT_P_LOGI) {
if (fcip_do_plogi(fptr, frp) != FC_SUCCESS) {
/*
* Login to the remote port failed. There is no
* point continuing with the FARP request further
* so bail out here.
*/
frp->fcipr_state = PORT_DEVICE_INVALID;
rval = FC_FAILURE;
goto farp_done;
}
}
FCIP_CP_OUT(fcmd, fc_pkt->pkt_cmd, fc_pkt->pkt_cmd_acc,
sizeof (la_els_farp_t));
rval = fc_ulp_issue_els(fport->fcipp_handle, fc_pkt);
if (rval != FC_SUCCESS) {
FCIP_TNF_PROBE_2((fcip_handle_farp_request, "fcip io",
/* CSTYLED */, tnf_string, msg,
"fcip_transport of farp reply failed",
tnf_uint, rval, rval));
FCIP_DEBUG(FCIP_DEBUG_ELS, (CE_WARN,
"fcip_transport of farp reply failed 0x%x", rval));
}
farp_done:
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
fcip_handle_farp_response(struct fcip *fptr, la_els_farp_t *fcmd)
{
int rval = FC_FAILURE;
fc_portmap_t map;
struct fcip_routing_table *frp;
struct fcip_dest *fdestp;
/*
* Add an entry for the remote port into our routing and destination
* tables.
*/
map.map_did = fcmd->dest_id;
map.map_hard_addr.hard_addr = fcmd->dest_id.port_id;
map.map_state = PORT_DEVICE_VALID;
map.map_type = PORT_DEVICE_NEW;
map.map_flags = 0;
map.map_pd = NULL;
bcopy((void *)&fcmd->resp_pwwn, (void *)&map.map_pwwn,
sizeof (la_wwn_t));
bcopy((void *)&fcmd->resp_nwwn, (void *)&map.map_nwwn,
sizeof (la_wwn_t));
fcip_rt_update(fptr, &map, 1);
mutex_enter(&fptr->fcip_rt_mutex);
frp = fcip_lookup_rtable(fptr, &fcmd->resp_pwwn, FCIP_COMPARE_NWWN);
mutex_exit(&fptr->fcip_rt_mutex);
fdestp = fcip_add_dest(fptr, frp);
if (fdestp != NULL) {
rval = FC_SUCCESS;
}
return (rval);
}
#define FCIP_HDRS_LENGTH \
sizeof (fcph_network_hdr_t)+sizeof (llc_snap_hdr_t)+sizeof (ipha_t)
/*
* 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
fcip_data_cb(opaque_t ulp_handle, opaque_t phandle,
fc_unsol_buf_t *buf, uint32_t claimed)
{
fcip_port_info_t *fport;
struct fcip *fptr;
fcph_network_hdr_t *nhdr;
llc_snap_hdr_t *snaphdr;
mblk_t *bp;
uint32_t len;
uint32_t hdrlen;
ushort_t type;
ipha_t *iphdr;
int rval;
#ifdef FCIP_ESBALLOC
frtn_t *free_ubuf;
struct fcip_esballoc_arg *fesb_argp;
#endif /* FCIP_ESBALLOC */
fport = fcip_get_port(phandle);
if (fport == NULL) {
return (FC_UNCLAIMED);
}
fptr = fport->fcipp_fcip;
ASSERT(fptr != NULL);
if (fptr == NULL) {
return (FC_UNCLAIMED);
}
mutex_enter(&fptr->fcip_mutex);
if ((fptr->fcip_flags & (FCIP_DETACHING | FCIP_DETACHED)) ||
(fptr->fcip_flags & (FCIP_SUSPENDED | FCIP_POWER_DOWN))) {
mutex_exit(&fptr->fcip_mutex);
rval = FC_UNCLAIMED;
goto data_cb_done;
}
/*
* set fcip flags to indicate we are in the middle of a