xref: /onnv-gate/usr/src/uts/sun4v/io/vnet_gen.c (revision 7572:f70548c0c5aa)
11991Sheppo /*
21991Sheppo  * CDDL HEADER START
31991Sheppo  *
41991Sheppo  * The contents of this file are subject to the terms of the
51991Sheppo  * Common Development and Distribution License (the "License").
61991Sheppo  * You may not use this file except in compliance with the License.
71991Sheppo  *
81991Sheppo  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91991Sheppo  * or http://www.opensolaris.org/os/licensing.
101991Sheppo  * See the License for the specific language governing permissions
111991Sheppo  * and limitations under the License.
121991Sheppo  *
131991Sheppo  * When distributing Covered Code, include this CDDL HEADER in each
141991Sheppo  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151991Sheppo  * If applicable, add the following below this CDDL HEADER, with the
161991Sheppo  * fields enclosed by brackets "[]" replaced with your own identifying
171991Sheppo  * information: Portions Copyright [yyyy] [name of copyright owner]
181991Sheppo  *
191991Sheppo  * CDDL HEADER END
201991Sheppo  */
211991Sheppo 
221991Sheppo /*
235935Ssb155480  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
241991Sheppo  * Use is subject to license terms.
251991Sheppo  */
261991Sheppo 
271991Sheppo #include <sys/types.h>
281991Sheppo #include <sys/errno.h>
297529SSriharsha.Basavapatna@Sun.COM #include <sys/sysmacros.h>
301991Sheppo #include <sys/param.h>
311991Sheppo #include <sys/stream.h>
324647Sraghuram #include <sys/strsubr.h>
331991Sheppo #include <sys/kmem.h>
341991Sheppo #include <sys/conf.h>
351991Sheppo #include <sys/devops.h>
361991Sheppo #include <sys/ksynch.h>
371991Sheppo #include <sys/stat.h>
381991Sheppo #include <sys/modctl.h>
391991Sheppo #include <sys/debug.h>
401991Sheppo #include <sys/ethernet.h>
411991Sheppo #include <sys/ddi.h>
421991Sheppo #include <sys/sunddi.h>
431991Sheppo #include <sys/strsun.h>
441991Sheppo #include <sys/note.h>
451991Sheppo #include <sys/mac.h>
462311Sseb #include <sys/mac_ether.h>
471991Sheppo #include <sys/ldc.h>
481991Sheppo #include <sys/mach_descrip.h>
491991Sheppo #include <sys/mdeg.h>
504647Sraghuram #include <net/if.h>
514647Sraghuram #include <sys/vnet.h>
521991Sheppo #include <sys/vio_mailbox.h>
531991Sheppo #include <sys/vio_common.h>
541991Sheppo #include <sys/vnet_common.h>
552336Snarayan #include <sys/vnet_mailbox.h>
562336Snarayan #include <sys/vio_util.h>
571991Sheppo #include <sys/vnet_gen.h>
585935Ssb155480 #include <sys/atomic.h>
594647Sraghuram #include <sys/callb.h>
604647Sraghuram #include <sys/sdt.h>
614647Sraghuram #include <sys/intr.h>
624647Sraghuram #include <sys/pattr.h>
636419Ssb155480 #include <sys/vlan.h>
641991Sheppo 
651991Sheppo /*
661991Sheppo  * Implementation of the mac functionality for vnet using the
671991Sheppo  * generic(default) transport layer of sun4v Logical Domain Channels(LDC).
681991Sheppo  */
691991Sheppo 
701991Sheppo /*
711991Sheppo  * Function prototypes.
721991Sheppo  */
731991Sheppo /* vgen proxy entry points */
746495Sspeer int vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip,
756495Sspeer     const uint8_t *macaddr, void **vgenhdl);
762336Snarayan int vgen_uninit(void *arg);
776495Sspeer int vgen_dds_tx(void *arg, void *dmsg);
781991Sheppo static int vgen_start(void *arg);
791991Sheppo static void vgen_stop(void *arg);
801991Sheppo static mblk_t *vgen_tx(void *arg, mblk_t *mp);
811991Sheppo static int vgen_multicst(void *arg, boolean_t add,
821991Sheppo 	const uint8_t *mca);
831991Sheppo static int vgen_promisc(void *arg, boolean_t on);
841991Sheppo static int vgen_unicst(void *arg, const uint8_t *mca);
852311Sseb static int vgen_stat(void *arg, uint_t stat, uint64_t *val);
861991Sheppo static void vgen_ioctl(void *arg, queue_t *wq, mblk_t *mp);
871991Sheppo 
881991Sheppo /* vgen internal functions */
896419Ssb155480 static int vgen_read_mdprops(vgen_t *vgenp);
906419Ssb155480 static void vgen_update_md_prop(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex);
916419Ssb155480 static void vgen_read_pri_eth_types(vgen_t *vgenp, md_t *mdp,
926419Ssb155480 	mde_cookie_t node);
937529SSriharsha.Basavapatna@Sun.COM static void vgen_mtu_read(vgen_t *vgenp, md_t *mdp, mde_cookie_t node,
947529SSriharsha.Basavapatna@Sun.COM 	uint32_t *mtu);
951991Sheppo static void vgen_detach_ports(vgen_t *vgenp);
961991Sheppo static void vgen_port_detach(vgen_port_t *portp);
971991Sheppo static void vgen_port_list_insert(vgen_port_t *portp);
981991Sheppo static void vgen_port_list_remove(vgen_port_t *portp);
991991Sheppo static vgen_port_t *vgen_port_lookup(vgen_portlist_t *plistp,
1001991Sheppo 	int port_num);
1011991Sheppo static int vgen_mdeg_reg(vgen_t *vgenp);
1021991Sheppo static void vgen_mdeg_unreg(vgen_t *vgenp);
1031991Sheppo static int vgen_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
1046419Ssb155480 static int vgen_mdeg_port_cb(void *cb_argp, mdeg_result_t *resp);
1051991Sheppo static int vgen_add_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex);
1066419Ssb155480 static int vgen_port_read_props(vgen_port_t *portp, vgen_t *vgenp, md_t *mdp,
1076419Ssb155480 	mde_cookie_t mdex);
1081991Sheppo static int vgen_remove_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex);
1096419Ssb155480 static int vgen_port_attach(vgen_port_t *portp);
1106419Ssb155480 static void vgen_port_detach_mdeg(vgen_port_t *portp);
1111991Sheppo static void vgen_port_detach_mdeg(vgen_port_t *portp);
1121991Sheppo static int vgen_update_port(vgen_t *vgenp, md_t *curr_mdp,
1131991Sheppo 	mde_cookie_t curr_mdex, md_t *prev_mdp, mde_cookie_t prev_mdex);
1142311Sseb static uint64_t	vgen_port_stat(vgen_port_t *portp, uint_t stat);
1151991Sheppo 
1161991Sheppo static int vgen_ldc_attach(vgen_port_t *portp, uint64_t ldc_id);
1171991Sheppo static void vgen_ldc_detach(vgen_ldc_t *ldcp);
1181991Sheppo static int vgen_alloc_tx_ring(vgen_ldc_t *ldcp);
1191991Sheppo static void vgen_free_tx_ring(vgen_ldc_t *ldcp);
1201991Sheppo static void vgen_init_ports(vgen_t *vgenp);
1211991Sheppo static void vgen_port_init(vgen_port_t *portp);
1221991Sheppo static void vgen_uninit_ports(vgen_t *vgenp);
1231991Sheppo static void vgen_port_uninit(vgen_port_t *portp);
1241991Sheppo static void vgen_init_ldcs(vgen_port_t *portp);
1251991Sheppo static void vgen_uninit_ldcs(vgen_port_t *portp);
1261991Sheppo static int vgen_ldc_init(vgen_ldc_t *ldcp);
1271991Sheppo static void vgen_ldc_uninit(vgen_ldc_t *ldcp);
1281991Sheppo static int vgen_init_tbufs(vgen_ldc_t *ldcp);
1291991Sheppo static void vgen_uninit_tbufs(vgen_ldc_t *ldcp);
1301991Sheppo static void vgen_clobber_tbufs(vgen_ldc_t *ldcp);
1311991Sheppo static void vgen_clobber_rxds(vgen_ldc_t *ldcp);
1322311Sseb static uint64_t	vgen_ldc_stat(vgen_ldc_t *ldcp, uint_t stat);
1331991Sheppo static uint_t vgen_ldc_cb(uint64_t event, caddr_t arg);
1341991Sheppo static int vgen_portsend(vgen_port_t *portp, mblk_t *mp);
1355935Ssb155480 static int vgen_ldcsend(void *arg, mblk_t *mp);
1365935Ssb155480 static void vgen_ldcsend_pkt(void *arg, mblk_t *mp);
1375935Ssb155480 static int vgen_ldcsend_dring(void *arg, mblk_t *mp);
1381991Sheppo static void vgen_reclaim(vgen_ldc_t *ldcp);
1391991Sheppo static void vgen_reclaim_dring(vgen_ldc_t *ldcp);
1401991Sheppo static int vgen_num_txpending(vgen_ldc_t *ldcp);
1411991Sheppo static int vgen_tx_dring_full(vgen_ldc_t *ldcp);
1421991Sheppo static int vgen_ldc_txtimeout(vgen_ldc_t *ldcp);
1431991Sheppo static void vgen_ldc_watchdog(void *arg);
1441991Sheppo 
1451991Sheppo /* vgen handshake functions */
1461991Sheppo static vgen_ldc_t *vh_nextphase(vgen_ldc_t *ldcp);
1471991Sheppo static int vgen_sendmsg(vgen_ldc_t *ldcp, caddr_t msg,  size_t msglen,
1481991Sheppo 	boolean_t caller_holds_lock);
1491991Sheppo static int vgen_send_version_negotiate(vgen_ldc_t *ldcp);
1501991Sheppo static int vgen_send_attr_info(vgen_ldc_t *ldcp);
1511991Sheppo static int vgen_send_dring_reg(vgen_ldc_t *ldcp);
1521991Sheppo static int vgen_send_rdx_info(vgen_ldc_t *ldcp);
1532336Snarayan static int vgen_send_dring_data(vgen_ldc_t *ldcp, uint32_t start, int32_t end);
1541991Sheppo static int vgen_send_mcast_info(vgen_ldc_t *ldcp);
1551991Sheppo static int vgen_handshake_phase2(vgen_ldc_t *ldcp);
1561991Sheppo static void vgen_handshake_reset(vgen_ldc_t *ldcp);
1571991Sheppo static void vgen_reset_hphase(vgen_ldc_t *ldcp);
1581991Sheppo static void vgen_handshake(vgen_ldc_t *ldcp);
1591991Sheppo static int vgen_handshake_done(vgen_ldc_t *ldcp);
1601991Sheppo static void vgen_handshake_retry(vgen_ldc_t *ldcp);
1612793Slm66018 static int vgen_handle_version_negotiate(vgen_ldc_t *ldcp,
1621991Sheppo 	vio_msg_tag_t *tagp);
1632793Slm66018 static int vgen_handle_attr_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1642793Slm66018 static int vgen_handle_dring_reg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1652793Slm66018 static int vgen_handle_rdx_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1662793Slm66018 static int vgen_handle_mcast_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1672793Slm66018 static int vgen_handle_ctrlmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1685935Ssb155480 static void vgen_handle_pkt_data_nop(void *arg1, void *arg2, uint32_t msglen);
1695935Ssb155480 static void vgen_handle_pkt_data(void *arg1, void *arg2, uint32_t msglen);
1704647Sraghuram static int vgen_handle_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1714647Sraghuram static int vgen_handle_dring_data_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1724647Sraghuram static int vgen_process_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1734647Sraghuram static int vgen_handle_dring_data_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1744647Sraghuram static int vgen_handle_dring_data_nack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1752793Slm66018 static int vgen_send_dring_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp,
1762336Snarayan 	uint32_t start, int32_t end, uint8_t pstate);
1775935Ssb155480 static int vgen_handle_datamsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp,
1785935Ssb155480 	uint32_t msglen);
1791991Sheppo static void vgen_handle_errmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1806495Sspeer static void vgen_handle_evt_up(vgen_ldc_t *ldcp);
1816495Sspeer static void vgen_handle_evt_reset(vgen_ldc_t *ldcp);
1821991Sheppo static int vgen_check_sid(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1835935Ssb155480 static int vgen_check_datamsg_seq(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1841991Sheppo static caddr_t vgen_print_ethaddr(uint8_t *a, char *ebuf);
1851991Sheppo static void vgen_hwatchdog(void *arg);
1861991Sheppo static void vgen_print_attr_info(vgen_ldc_t *ldcp, int endpoint);
1871991Sheppo static void vgen_print_hparams(vgen_hparams_t *hp);
1881991Sheppo static void vgen_print_ldcinfo(vgen_ldc_t *ldcp);
1894647Sraghuram static void vgen_stop_rcv_thread(vgen_ldc_t *ldcp);
1904647Sraghuram static void vgen_ldc_rcv_worker(void *arg);
1914647Sraghuram static void vgen_handle_evt_read(vgen_ldc_t *ldcp);
1925935Ssb155480 static void vgen_rx(vgen_ldc_t *ldcp, mblk_t *bp);
1935935Ssb155480 static void vgen_set_vnet_proto_ops(vgen_ldc_t *ldcp);
1945935Ssb155480 static void vgen_reset_vnet_proto_ops(vgen_ldc_t *ldcp);
1951991Sheppo 
1966419Ssb155480 /* VLAN routines */
1976419Ssb155480 static void vgen_vlan_read_ids(void *arg, int type, md_t *mdp,
1986419Ssb155480 	mde_cookie_t node, uint16_t *pvidp, uint16_t **vidspp,
1996419Ssb155480 	uint16_t *nvidsp, uint16_t *default_idp);
2006419Ssb155480 static void vgen_vlan_create_hash(vgen_port_t *portp);
2016419Ssb155480 static void vgen_vlan_destroy_hash(vgen_port_t *portp);
2026419Ssb155480 static void vgen_vlan_add_ids(vgen_port_t *portp);
2036419Ssb155480 static void vgen_vlan_remove_ids(vgen_port_t *portp);
2046419Ssb155480 static boolean_t vgen_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid);
2056419Ssb155480 static boolean_t vgen_frame_lookup_vid(vnet_t *vnetp, struct ether_header *ehp,
2066419Ssb155480 	uint16_t *vidp);
2076419Ssb155480 static mblk_t *vgen_vlan_frame_fixtag(vgen_port_t *portp, mblk_t *mp,
2086419Ssb155480 	boolean_t is_tagged, uint16_t vid);
2096419Ssb155480 static void vgen_vlan_unaware_port_reset(vgen_port_t *portp);
2106419Ssb155480 static void vgen_reset_vlan_unaware_ports(vgen_t *vgenp);
2116495Sspeer static int vgen_dds_rx(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
2126495Sspeer 
2136495Sspeer /* externs */
2146495Sspeer extern void vnet_dds_rx(void *arg, void *dmsg);
2157529SSriharsha.Basavapatna@Sun.COM extern int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu);
2166419Ssb155480 
2171991Sheppo /*
2181991Sheppo  * The handshake process consists of 5 phases defined below, with VH_PHASE0
2191991Sheppo  * being the pre-handshake phase and VH_DONE is the phase to indicate
2201991Sheppo  * successful completion of all phases.
2211991Sheppo  * Each phase may have one to several handshake states which are required
2221991Sheppo  * to complete successfully to move to the next phase.
2231991Sheppo  * Refer to the functions vgen_handshake() and vgen_handshake_done() for
2241991Sheppo  * more details.
2251991Sheppo  */
2261991Sheppo /* handshake phases */
2271991Sheppo enum {	VH_PHASE0, VH_PHASE1, VH_PHASE2, VH_PHASE3, VH_DONE = 0x80 };
2281991Sheppo 
2291991Sheppo /* handshake states */
2301991Sheppo enum {
2311991Sheppo 
2321991Sheppo 	VER_INFO_SENT	=	0x1,
2331991Sheppo 	VER_ACK_RCVD	=	0x2,
2341991Sheppo 	VER_INFO_RCVD	=	0x4,
2351991Sheppo 	VER_ACK_SENT	=	0x8,
2361991Sheppo 	VER_NEGOTIATED	=	(VER_ACK_RCVD | VER_ACK_SENT),
2371991Sheppo 
2381991Sheppo 	ATTR_INFO_SENT	=	0x10,
2391991Sheppo 	ATTR_ACK_RCVD	=	0x20,
2401991Sheppo 	ATTR_INFO_RCVD	=	0x40,
2411991Sheppo 	ATTR_ACK_SENT	=	0x80,
2421991Sheppo 	ATTR_INFO_EXCHANGED	=	(ATTR_ACK_RCVD | ATTR_ACK_SENT),
2431991Sheppo 
2441991Sheppo 	DRING_INFO_SENT	=	0x100,
2451991Sheppo 	DRING_ACK_RCVD	=	0x200,
2461991Sheppo 	DRING_INFO_RCVD	=	0x400,
2471991Sheppo 	DRING_ACK_SENT	=	0x800,
2481991Sheppo 	DRING_INFO_EXCHANGED	=	(DRING_ACK_RCVD | DRING_ACK_SENT),
2491991Sheppo 
2501991Sheppo 	RDX_INFO_SENT	=	0x1000,
2511991Sheppo 	RDX_ACK_RCVD	=	0x2000,
2521991Sheppo 	RDX_INFO_RCVD	=	0x4000,
2531991Sheppo 	RDX_ACK_SENT	=	0x8000,
2541991Sheppo 	RDX_EXCHANGED	=	(RDX_ACK_RCVD | RDX_ACK_SENT)
2551991Sheppo 
2561991Sheppo };
2571991Sheppo 
2585935Ssb155480 #define	VGEN_PRI_ETH_DEFINED(vgenp)	((vgenp)->pri_num_types != 0)
2595935Ssb155480 
2601991Sheppo #define	LDC_LOCK(ldcp)	\
2611991Sheppo 				mutex_enter(&((ldcp)->cblock));\
2624647Sraghuram 				mutex_enter(&((ldcp)->rxlock));\
2634647Sraghuram 				mutex_enter(&((ldcp)->wrlock));\
2641991Sheppo 				mutex_enter(&((ldcp)->txlock));\
2651991Sheppo 				mutex_enter(&((ldcp)->tclock));
2661991Sheppo #define	LDC_UNLOCK(ldcp)	\
2671991Sheppo 				mutex_exit(&((ldcp)->tclock));\
2681991Sheppo 				mutex_exit(&((ldcp)->txlock));\
2694647Sraghuram 				mutex_exit(&((ldcp)->wrlock));\
2704647Sraghuram 				mutex_exit(&((ldcp)->rxlock));\
2711991Sheppo 				mutex_exit(&((ldcp)->cblock));
2721991Sheppo 
2736419Ssb155480 #define	VGEN_VER_EQ(ldcp, major, minor)	\
2746419Ssb155480 	((ldcp)->local_hparams.ver_major == (major) &&	\
2756419Ssb155480 	    (ldcp)->local_hparams.ver_minor == (minor))
2766419Ssb155480 
2776419Ssb155480 #define	VGEN_VER_LT(ldcp, major, minor)	\
2786419Ssb155480 	(((ldcp)->local_hparams.ver_major < (major)) ||	\
2796419Ssb155480 	    ((ldcp)->local_hparams.ver_major == (major) &&	\
2806419Ssb155480 	    (ldcp)->local_hparams.ver_minor < (minor)))
2816419Ssb155480 
2826419Ssb155480 #define	VGEN_VER_GTEQ(ldcp, major, minor)	\
2836419Ssb155480 	(((ldcp)->local_hparams.ver_major > (major)) ||	\
2846419Ssb155480 	    ((ldcp)->local_hparams.ver_major == (major) &&	\
2856419Ssb155480 	    (ldcp)->local_hparams.ver_minor >= (minor)))
2866419Ssb155480 
2871991Sheppo static struct ether_addr etherbroadcastaddr = {
2881991Sheppo 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2891991Sheppo };
2901991Sheppo /*
2911991Sheppo  * MIB II broadcast/multicast packets
2921991Sheppo  */
2931991Sheppo #define	IS_BROADCAST(ehp) \
2941991Sheppo 		(ether_cmp(&ehp->ether_dhost, &etherbroadcastaddr) == 0)
2951991Sheppo #define	IS_MULTICAST(ehp) \
2961991Sheppo 		((ehp->ether_dhost.ether_addr_octet[0] & 01) == 1)
2971991Sheppo 
2981991Sheppo /*
2991991Sheppo  * Property names
3001991Sheppo  */
3011991Sheppo static char macaddr_propname[] = "mac-address";
3021991Sheppo static char rmacaddr_propname[] = "remote-mac-address";
3031991Sheppo static char channel_propname[] = "channel-endpoint";
3041991Sheppo static char reg_propname[] = "reg";
3051991Sheppo static char port_propname[] = "port";
3061991Sheppo static char swport_propname[] = "switch-port";
3071991Sheppo static char id_propname[] = "id";
3085935Ssb155480 static char vdev_propname[] = "virtual-device";
3095935Ssb155480 static char vnet_propname[] = "network";
3105935Ssb155480 static char pri_types_propname[] = "priority-ether-types";
3116419Ssb155480 static char vgen_pvid_propname[] = "port-vlan-id";
3126419Ssb155480 static char vgen_vid_propname[] = "vlan-id";
3136419Ssb155480 static char vgen_dvid_propname[] = "default-vlan-id";
3146419Ssb155480 static char port_pvid_propname[] = "remote-port-vlan-id";
3156419Ssb155480 static char port_vid_propname[] = "remote-vlan-id";
3167529SSriharsha.Basavapatna@Sun.COM static char vgen_mtu_propname[] = "mtu";
3171991Sheppo 
3181991Sheppo /* versions supported - in decreasing order */
3197529SSriharsha.Basavapatna@Sun.COM static vgen_ver_t vgen_versions[VGEN_NUM_VER] = { {1, 4} };
3201991Sheppo 
3211991Sheppo /* Tunables */
3225171Ssb155480 uint32_t vgen_hwd_interval = 5;		/* handshake watchdog freq in sec */
3233297Ssb155480 uint32_t vgen_max_hretries = VNET_NUM_HANDSHAKES; /* # of handshake retries */
3241991Sheppo uint32_t vgen_ldcwr_retries = 10;	/* max # of ldc_write() retries */
3252109Slm66018 uint32_t vgen_ldcup_retries = 5;	/* max # of ldc_up() retries */
3262336Snarayan uint32_t vgen_recv_delay = 1;		/* delay when rx descr not ready */
3272336Snarayan uint32_t vgen_recv_retries = 10;	/* retry when rx descr not ready */
3285022Sraghuram uint32_t vgen_tx_retries = 0x4;		/* retry when tx descr not available */
3295022Sraghuram uint32_t vgen_tx_delay = 0x30;		/* delay when tx descr not available */
3301991Sheppo 
3314647Sraghuram int vgen_rcv_thread_enabled = 1;	/* Enable Recieve thread */
3324647Sraghuram 
3334647Sraghuram /*
3344647Sraghuram  * max # of packets accumulated prior to sending them up. It is best
3354647Sraghuram  * to keep this at 60% of the number of recieve buffers.
3364647Sraghuram  */
3374647Sraghuram uint32_t vgen_chain_len = (VGEN_NRBUFS * 0.6);
3384647Sraghuram 
3394647Sraghuram /*
3407529SSriharsha.Basavapatna@Sun.COM  * Internal tunables for receive buffer pools, that is,  the size and number of
3417529SSriharsha.Basavapatna@Sun.COM  * mblks for each pool. At least 3 sizes must be specified if these are used.
3427529SSriharsha.Basavapatna@Sun.COM  * The sizes must be specified in increasing order. Non-zero value of the first
3437529SSriharsha.Basavapatna@Sun.COM  * size will be used as a hint to use these values instead of the algorithm
3447529SSriharsha.Basavapatna@Sun.COM  * that determines the sizes based on MTU.
3454647Sraghuram  */
3467529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz1 = 0;
3477529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz2 = 0;
3487529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz3 = 0;
3497529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz4 = 0;
3504647Sraghuram 
3514647Sraghuram uint32_t vgen_nrbufs1 = VGEN_NRBUFS;
3524647Sraghuram uint32_t vgen_nrbufs2 = VGEN_NRBUFS;
3534647Sraghuram uint32_t vgen_nrbufs3 = VGEN_NRBUFS;
3547529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_nrbufs4 = VGEN_NRBUFS;
3554647Sraghuram 
3565935Ssb155480 /*
3575935Ssb155480  * In the absence of "priority-ether-types" property in MD, the following
3585935Ssb155480  * internal tunable can be set to specify a single priority ethertype.
3595935Ssb155480  */
3605935Ssb155480 uint64_t vgen_pri_eth_type = 0;
3615935Ssb155480 
3625935Ssb155480 /*
3635935Ssb155480  * Number of transmit priority buffers that are preallocated per device.
3645935Ssb155480  * This number is chosen to be a small value to throttle transmission
3655935Ssb155480  * of priority packets. Note: Must be a power of 2 for vio_create_mblks().
3665935Ssb155480  */
3675935Ssb155480 uint32_t vgen_pri_tx_nmblks = 64;
3685935Ssb155480 
3696419Ssb155480 uint32_t	vgen_vlan_nchains = 4;	/* # of chains in vlan id hash table */
3706419Ssb155480 
3711991Sheppo #ifdef DEBUG
3721991Sheppo /* flags to simulate error conditions for debugging */
3731991Sheppo int vgen_trigger_txtimeout = 0;
3741991Sheppo int vgen_trigger_rxlost = 0;
3751991Sheppo #endif
3761991Sheppo 
3776419Ssb155480 /*
3786419Ssb155480  * Matching criteria passed to the MDEG to register interest
3796419Ssb155480  * in changes to 'virtual-device' nodes (i.e. vnet nodes) identified
3806419Ssb155480  * by their 'name' and 'cfg-handle' properties.
3816419Ssb155480  */
3826419Ssb155480 static md_prop_match_t vdev_prop_match[] = {
3836419Ssb155480 	{ MDET_PROP_STR,    "name"   },
3846419Ssb155480 	{ MDET_PROP_VAL,    "cfg-handle" },
3856419Ssb155480 	{ MDET_LIST_END,    NULL    }
3866419Ssb155480 };
3876419Ssb155480 
3886419Ssb155480 static mdeg_node_match_t vdev_match = { "virtual-device",
3896419Ssb155480 						vdev_prop_match };
3906419Ssb155480 
3911991Sheppo /* MD update matching structure */
3921991Sheppo static md_prop_match_t	vport_prop_match[] = {
3931991Sheppo 	{ MDET_PROP_VAL,	"id" },
3941991Sheppo 	{ MDET_LIST_END,	NULL }
3951991Sheppo };
3961991Sheppo 
3971991Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port",
3981991Sheppo 					vport_prop_match };
3991991Sheppo 
4001991Sheppo /* template for matching a particular vnet instance */
4011991Sheppo static mdeg_prop_spec_t vgen_prop_template[] = {
4021991Sheppo 	{ MDET_PROP_STR,	"name",		"network" },
4031991Sheppo 	{ MDET_PROP_VAL,	"cfg-handle",	NULL },
4041991Sheppo 	{ MDET_LIST_END,	NULL,		NULL }
4051991Sheppo };
4061991Sheppo 
4071991Sheppo #define	VGEN_SET_MDEG_PROP_INST(specp, val)	(specp)[1].ps_val = (val)
4081991Sheppo 
4096419Ssb155480 static int vgen_mdeg_port_cb(void *cb_argp, mdeg_result_t *resp);
4101991Sheppo 
4112311Sseb static mac_callbacks_t vgen_m_callbacks = {
4122311Sseb 	0,
4132311Sseb 	vgen_stat,
4142311Sseb 	vgen_start,
4152311Sseb 	vgen_stop,
4162311Sseb 	vgen_promisc,
4172311Sseb 	vgen_multicst,
4182311Sseb 	vgen_unicst,
4192311Sseb 	vgen_tx,
4202311Sseb 	NULL,
4212311Sseb 	NULL,
4222311Sseb 	NULL
4232311Sseb };
4242311Sseb 
4251991Sheppo /* externs */
4264647Sraghuram extern pri_t	maxclsyspri;
4274647Sraghuram extern proc_t	p0;
4281991Sheppo extern uint32_t vnet_ntxds;
4291991Sheppo extern uint32_t vnet_ldcwd_interval;
4301991Sheppo extern uint32_t vnet_ldcwd_txtimeout;
4312410Slm66018 extern uint32_t vnet_ldc_mtu;
4322336Snarayan extern uint32_t vnet_nrbufs;
4336419Ssb155480 extern uint32_t	vnet_ethermtu;
4346419Ssb155480 extern uint16_t	vnet_default_vlan_id;
4357529SSriharsha.Basavapatna@Sun.COM extern boolean_t vnet_jumbo_rxpools;
4361991Sheppo 
4371991Sheppo #ifdef DEBUG
4381991Sheppo 
4394647Sraghuram extern int vnet_dbglevel;
4404647Sraghuram static void debug_printf(const char *fname, vgen_t *vgenp,
4414647Sraghuram 	vgen_ldc_t *ldcp, const char *fmt, ...);
4424647Sraghuram 
4434647Sraghuram /* -1 for all LDCs info, or ldc_id for a specific LDC info */
4444647Sraghuram int vgendbg_ldcid = -1;
4451991Sheppo 
4461991Sheppo /* simulate handshake error conditions for debug */
4471991Sheppo uint32_t vgen_hdbg;
4481991Sheppo #define	HDBG_VERSION	0x1
4491991Sheppo #define	HDBG_TIMEOUT	0x2
4501991Sheppo #define	HDBG_BAD_SID	0x4
4511991Sheppo #define	HDBG_OUT_STATE	0x8
4521991Sheppo 
4531991Sheppo #endif
4541991Sheppo 
4551991Sheppo /*
4561991Sheppo  * vgen_init() is called by an instance of vnet driver to initialize the
4571991Sheppo  * corresponding generic proxy transport layer. The arguments passed by vnet
4581991Sheppo  * are - an opaque pointer to the vnet instance, pointers to dev_info_t and
4596495Sspeer  * the mac address of the vnet device, and a pointer to vgen_t is passed
4606495Sspeer  * back as a handle to vnet.
4611991Sheppo  */
4621991Sheppo int
4636495Sspeer vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip,
4646495Sspeer     const uint8_t *macaddr, void **vgenhdl)
4651991Sheppo {
4661991Sheppo 	vgen_t *vgenp;
4671991Sheppo 	int instance;
4685935Ssb155480 	int rv;
4691991Sheppo 
4702311Sseb 	if ((vnetp == NULL) || (vnetdip == NULL))
4711991Sheppo 		return (DDI_FAILURE);
4721991Sheppo 
4731991Sheppo 	instance = ddi_get_instance(vnetdip);
4741991Sheppo 
4755373Sraghuram 	DBG1(NULL, NULL, "vnet(%d): enter\n", instance);
4761991Sheppo 
4771991Sheppo 	vgenp = kmem_zalloc(sizeof (vgen_t), KM_SLEEP);
4781991Sheppo 
4791991Sheppo 	vgenp->vnetp = vnetp;
4806495Sspeer 	vgenp->instance = instance;
4816495Sspeer 	vgenp->regprop = regprop;
4821991Sheppo 	vgenp->vnetdip = vnetdip;
4831991Sheppo 	bcopy(macaddr, &(vgenp->macaddr), ETHERADDRL);
4841991Sheppo 
4851991Sheppo 	/* allocate multicast table */
4861991Sheppo 	vgenp->mctab = kmem_zalloc(VGEN_INIT_MCTAB_SIZE *
4871991Sheppo 	    sizeof (struct ether_addr), KM_SLEEP);
4881991Sheppo 	vgenp->mccount = 0;
4891991Sheppo 	vgenp->mcsize = VGEN_INIT_MCTAB_SIZE;
4901991Sheppo 
4911991Sheppo 	mutex_init(&vgenp->lock, NULL, MUTEX_DRIVER, NULL);
4925641Swentaoy 	rw_init(&vgenp->vgenports.rwlock, NULL, RW_DRIVER, NULL);
4931991Sheppo 
4945935Ssb155480 	rv = vgen_read_mdprops(vgenp);
4955935Ssb155480 	if (rv != 0) {
4965935Ssb155480 		goto vgen_init_fail;
4975935Ssb155480 	}
4985935Ssb155480 
4991991Sheppo 	/* register with MD event generator */
5005935Ssb155480 	rv = vgen_mdeg_reg(vgenp);
5015935Ssb155480 	if (rv != DDI_SUCCESS) {
5025935Ssb155480 		goto vgen_init_fail;
5031991Sheppo 	}
5041991Sheppo 
5056495Sspeer 	*vgenhdl = (void *)vgenp;
5061991Sheppo 
5075373Sraghuram 	DBG1(NULL, NULL, "vnet(%d): exit\n", instance);
5081991Sheppo 	return (DDI_SUCCESS);
5095935Ssb155480 
5105935Ssb155480 vgen_init_fail:
5115935Ssb155480 	rw_destroy(&vgenp->vgenports.rwlock);
5125935Ssb155480 	mutex_destroy(&vgenp->lock);
5135935Ssb155480 	kmem_free(vgenp->mctab, VGEN_INIT_MCTAB_SIZE *
5145935Ssb155480 	    sizeof (struct ether_addr));
5155935Ssb155480 	if (VGEN_PRI_ETH_DEFINED(vgenp)) {
5165935Ssb155480 		kmem_free(vgenp->pri_types,
5175935Ssb155480 		    sizeof (uint16_t) * vgenp->pri_num_types);
5185935Ssb155480 		(void) vio_destroy_mblks(vgenp->pri_tx_vmp);
5195935Ssb155480 	}
5205935Ssb155480 	KMEM_FREE(vgenp);
5215935Ssb155480 	return (DDI_FAILURE);
5221991Sheppo }
5231991Sheppo 
5241991Sheppo /*
5251991Sheppo  * Called by vnet to undo the initializations done by vgen_init().
5261991Sheppo  * The handle provided by generic transport during vgen_init() is the argument.
5271991Sheppo  */
5282336Snarayan int
5291991Sheppo vgen_uninit(void *arg)
5301991Sheppo {
5315935Ssb155480 	vgen_t		*vgenp = (vgen_t *)arg;
5325935Ssb155480 	vio_mblk_pool_t	*rp;
5335935Ssb155480 	vio_mblk_pool_t	*nrp;
5342336Snarayan 
5352336Snarayan 	if (vgenp == NULL) {
5362336Snarayan 		return (DDI_FAILURE);
5372336Snarayan 	}
5381991Sheppo 
5394647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
5401991Sheppo 
5411991Sheppo 	/* unregister with MD event generator */
5421991Sheppo 	vgen_mdeg_unreg(vgenp);
5431991Sheppo 
5441991Sheppo 	mutex_enter(&vgenp->lock);
5451991Sheppo 
5461991Sheppo 	/* detach all ports from the device */
5471991Sheppo 	vgen_detach_ports(vgenp);
5481991Sheppo 
5492336Snarayan 	/*
5502336Snarayan 	 * free any pending rx mblk pools,
5512336Snarayan 	 * that couldn't be freed previously during channel detach.
5522336Snarayan 	 */
5532336Snarayan 	rp = vgenp->rmp;
5542336Snarayan 	while (rp != NULL) {
5552336Snarayan 		nrp = vgenp->rmp = rp->nextp;
5562336Snarayan 		if (vio_destroy_mblks(rp)) {
5572336Snarayan 			vgenp->rmp = rp;
5582336Snarayan 			mutex_exit(&vgenp->lock);
5592336Snarayan 			return (DDI_FAILURE);
5602336Snarayan 		}
5612336Snarayan 		rp = nrp;
5622336Snarayan 	}
5632336Snarayan 
5641991Sheppo 	/* free multicast table */
5651991Sheppo 	kmem_free(vgenp->mctab, vgenp->mcsize * sizeof (struct ether_addr));
5661991Sheppo 
5675935Ssb155480 	/* free pri_types table */
5685935Ssb155480 	if (VGEN_PRI_ETH_DEFINED(vgenp)) {
5695935Ssb155480 		kmem_free(vgenp->pri_types,
5705935Ssb155480 		    sizeof (uint16_t) * vgenp->pri_num_types);
5715935Ssb155480 		(void) vio_destroy_mblks(vgenp->pri_tx_vmp);
5725935Ssb155480 	}
5735935Ssb155480 
5741991Sheppo 	mutex_exit(&vgenp->lock);
5751991Sheppo 
5765641Swentaoy 	rw_destroy(&vgenp->vgenports.rwlock);
5771991Sheppo 	mutex_destroy(&vgenp->lock);
5781991Sheppo 
579*7572SWentao.Yang@Sun.COM 	DBG1(vgenp, NULL, "exit\n");
5801991Sheppo 	KMEM_FREE(vgenp);
5811991Sheppo 
5822336Snarayan 	return (DDI_SUCCESS);
5831991Sheppo }
5841991Sheppo 
5851991Sheppo /* enable transmit/receive for the device */
5862311Sseb int
5871991Sheppo vgen_start(void *arg)
5881991Sheppo {
5896495Sspeer 	vgen_port_t	*portp = (vgen_port_t *)arg;
5906495Sspeer 	vgen_t		*vgenp = portp->vgenp;
5911991Sheppo 
5924647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
5936495Sspeer 	mutex_enter(&portp->lock);
5946495Sspeer 	vgen_port_init(portp);
5956495Sspeer 	portp->flags |= VGEN_STARTED;
5966495Sspeer 	mutex_exit(&portp->lock);
5974647Sraghuram 	DBG1(vgenp, NULL, "exit\n");
5986495Sspeer 
5991991Sheppo 	return (DDI_SUCCESS);
6001991Sheppo }
6011991Sheppo 
6021991Sheppo /* stop transmit/receive */
6032311Sseb void
6041991Sheppo vgen_stop(void *arg)
6051991Sheppo {
6066495Sspeer 	vgen_port_t	*portp = (vgen_port_t *)arg;
6076495Sspeer 	vgen_t		*vgenp = portp->vgenp;
6081991Sheppo 
6094647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
6101991Sheppo 
6116495Sspeer 	mutex_enter(&portp->lock);
6126495Sspeer 	vgen_port_uninit(portp);
6136495Sspeer 	portp->flags &= ~(VGEN_STARTED);
6146495Sspeer 	mutex_exit(&portp->lock);
6154647Sraghuram 	DBG1(vgenp, NULL, "exit\n");
6166495Sspeer 
6171991Sheppo }
6181991Sheppo 
6191991Sheppo /* vgen transmit function */
6201991Sheppo static mblk_t *
6211991Sheppo vgen_tx(void *arg, mblk_t *mp)
6221991Sheppo {
6235022Sraghuram 	int i;
6241991Sheppo 	vgen_port_t *portp;
6255022Sraghuram 	int status = VGEN_FAILURE;
6261991Sheppo 
6271991Sheppo 	portp = (vgen_port_t *)arg;
6285022Sraghuram 	/*
6295022Sraghuram 	 * Retry so that we avoid reporting a failure
6305022Sraghuram 	 * to the upper layer. Returning a failure may cause the
6315022Sraghuram 	 * upper layer to go into single threaded mode there by
6325022Sraghuram 	 * causing performance degradation, especially for a large
6335022Sraghuram 	 * number of connections.
6345022Sraghuram 	 */
6355022Sraghuram 	for (i = 0; i < vgen_tx_retries; ) {
6365022Sraghuram 		status = vgen_portsend(portp, mp);
6375022Sraghuram 		if (status == VGEN_SUCCESS) {
6385022Sraghuram 			break;
6395022Sraghuram 		}
6405022Sraghuram 		if (++i < vgen_tx_retries)
6415022Sraghuram 			delay(drv_usectohz(vgen_tx_delay));
6425022Sraghuram 	}
6431991Sheppo 	if (status != VGEN_SUCCESS) {
6441991Sheppo 		/* failure */
6451991Sheppo 		return (mp);
6461991Sheppo 	}
6471991Sheppo 	/* success */
6481991Sheppo 	return (NULL);
6491991Sheppo }
6501991Sheppo 
6516419Ssb155480 /*
6526419Ssb155480  * This function provides any necessary tagging/untagging of the frames
6536419Ssb155480  * that are being transmitted over the port. It first verifies the vlan
6546419Ssb155480  * membership of the destination(port) and drops the packet if the
6556419Ssb155480  * destination doesn't belong to the given vlan.
6566419Ssb155480  *
6576419Ssb155480  * Arguments:
6586419Ssb155480  *   portp:     port over which the frames should be transmitted
6596419Ssb155480  *   mp:        frame to be transmitted
6606419Ssb155480  *   is_tagged:
6616419Ssb155480  *              B_TRUE: indicates frame header contains the vlan tag already.
6626419Ssb155480  *              B_FALSE: indicates frame is untagged.
6636419Ssb155480  *   vid:       vlan in which the frame should be transmitted.
6646419Ssb155480  *
6656419Ssb155480  * Returns:
6666419Ssb155480  *              Sucess: frame(mblk_t *) after doing the necessary tag/untag.
6676419Ssb155480  *              Failure: NULL
6686419Ssb155480  */
6696419Ssb155480 static mblk_t *
6706419Ssb155480 vgen_vlan_frame_fixtag(vgen_port_t *portp, mblk_t *mp, boolean_t is_tagged,
6716419Ssb155480 	uint16_t vid)
6726419Ssb155480 {
6736419Ssb155480 	vgen_t				*vgenp;
6746419Ssb155480 	boolean_t			dst_tagged;
6756419Ssb155480 	int				rv;
6766419Ssb155480 
6776419Ssb155480 	vgenp = portp->vgenp;
6786419Ssb155480 
6796419Ssb155480 	/*
6806419Ssb155480 	 * If the packet is going to a vnet:
6816419Ssb155480 	 *   Check if the destination vnet is in the same vlan.
6826419Ssb155480 	 *   Check the frame header if tag or untag is needed.
6836419Ssb155480 	 *
6846419Ssb155480 	 * We do not check the above conditions if the packet is going to vsw:
6856419Ssb155480 	 *   vsw must be present implicitly in all the vlans that a vnet device
6866419Ssb155480 	 *   is configured into; even if vsw itself is not assigned to those
6876419Ssb155480 	 *   vlans as an interface. For instance, the packet might be destined
6886419Ssb155480 	 *   to another vnet(indirectly through vsw) or to an external host
6896419Ssb155480 	 *   which is in the same vlan as this vnet and vsw itself may not be
6906419Ssb155480 	 *   present in that vlan. Similarly packets going to vsw must be
6916419Ssb155480 	 *   always tagged(unless in the default-vlan) if not already tagged,
6926419Ssb155480 	 *   as we do not know the final destination. This is needed because
6936419Ssb155480 	 *   vsw must always invoke its switching function only after tagging
6946419Ssb155480 	 *   the packet; otherwise after switching function determines the
6956419Ssb155480 	 *   destination we cannot figure out if the destination belongs to the
6966419Ssb155480 	 *   the same vlan that the frame originated from and if it needs tag/
6976419Ssb155480 	 *   untag. Note that vsw will tag the packet itself when it receives
6986419Ssb155480 	 *   it over the channel from a client if needed. However, that is
6996419Ssb155480 	 *   needed only in the case of vlan unaware clients such as obp or
7006419Ssb155480 	 *   earlier versions of vnet.
7016419Ssb155480 	 *
7026419Ssb155480 	 */
7036419Ssb155480 	if (portp != vgenp->vsw_portp) {
7046419Ssb155480 		/*
7056419Ssb155480 		 * Packet going to a vnet. Check if the destination vnet is in
7066419Ssb155480 		 * the same vlan. Then check the frame header if tag/untag is
7076419Ssb155480 		 * needed.
7086419Ssb155480 		 */
7096419Ssb155480 		rv = vgen_vlan_lookup(portp->vlan_hashp, vid);
7106419Ssb155480 		if (rv == B_FALSE) {
7116419Ssb155480 			/* drop the packet */
7126419Ssb155480 			freemsg(mp);
7136419Ssb155480 			return (NULL);
7146419Ssb155480 		}
7156419Ssb155480 
7166419Ssb155480 		/* is the destination tagged or untagged in this vlan? */
7176419Ssb155480 		(vid == portp->pvid) ? (dst_tagged = B_FALSE) :
7186419Ssb155480 		    (dst_tagged = B_TRUE);
7196419Ssb155480 
7206419Ssb155480 		if (is_tagged == dst_tagged) {
7216419Ssb155480 			/* no tagging/untagging needed */
7226419Ssb155480 			return (mp);
7236419Ssb155480 		}
7246419Ssb155480 
7256419Ssb155480 		if (is_tagged == B_TRUE) {
7266419Ssb155480 			/* frame is tagged; destination needs untagged */
7276419Ssb155480 			mp = vnet_vlan_remove_tag(mp);
7286419Ssb155480 			return (mp);
7296419Ssb155480 		}
7306419Ssb155480 
7316419Ssb155480 		/* (is_tagged == B_FALSE): fallthru to tag tx packet: */
7326419Ssb155480 	}
7336419Ssb155480 
7346419Ssb155480 	/*
7356419Ssb155480 	 * Packet going to a vnet needs tagging.
7366419Ssb155480 	 * OR
7376419Ssb155480 	 * If the packet is going to vsw, then it must be tagged in all cases:
7386419Ssb155480 	 * unknown unicast, broadcast/multicast or to vsw interface.
7396419Ssb155480 	 */
7406419Ssb155480 
7416419Ssb155480 	if (is_tagged == B_FALSE) {
7426419Ssb155480 		mp = vnet_vlan_insert_tag(mp, vid);
7436419Ssb155480 	}
7446419Ssb155480 
7456419Ssb155480 	return (mp);
7466419Ssb155480 }
7476419Ssb155480 
7481991Sheppo /* transmit packets over the given port */
7491991Sheppo static int
7501991Sheppo vgen_portsend(vgen_port_t *portp, mblk_t *mp)
7511991Sheppo {
7526419Ssb155480 	vgen_ldclist_t		*ldclp;
7536419Ssb155480 	vgen_ldc_t		*ldcp;
7546419Ssb155480 	int			status;
7556419Ssb155480 	int			rv = VGEN_SUCCESS;
7566495Sspeer 	vgen_t			*vgenp = portp->vgenp;
7576495Sspeer 	vnet_t			*vnetp = vgenp->vnetp;
7586419Ssb155480 	boolean_t		is_tagged;
7596495Sspeer 	boolean_t		dec_refcnt = B_FALSE;
7606419Ssb155480 	uint16_t		vlan_id;
7616419Ssb155480 	struct ether_header	*ehp;
7626419Ssb155480 
7636495Sspeer 	if (portp->use_vsw_port) {
7646495Sspeer 		(void) atomic_inc_32(&vgenp->vsw_port_refcnt);
7656495Sspeer 		portp = portp->vgenp->vsw_portp;
7666495Sspeer 		dec_refcnt = B_TRUE;
7676495Sspeer 	}
7686495Sspeer 	if (portp == NULL) {
7696495Sspeer 		return (VGEN_FAILURE);
7706495Sspeer 	}
7716419Ssb155480 
7726419Ssb155480 	/*
7736419Ssb155480 	 * Determine the vlan id that the frame belongs to.
7746419Ssb155480 	 */
7756419Ssb155480 	ehp = (struct ether_header *)mp->b_rptr;
7766419Ssb155480 	is_tagged = vgen_frame_lookup_vid(vnetp, ehp, &vlan_id);
7776419Ssb155480 
7786419Ssb155480 	if (vlan_id == vnetp->default_vlan_id) {
7796419Ssb155480 
7806419Ssb155480 		/* Frames in default vlan must be untagged */
7816419Ssb155480 		ASSERT(is_tagged == B_FALSE);
7826419Ssb155480 
7836419Ssb155480 		/*
7846419Ssb155480 		 * If the destination is a vnet-port verify it belongs to the
7856419Ssb155480 		 * default vlan; otherwise drop the packet. We do not need
7866419Ssb155480 		 * this check for vsw-port, as it should implicitly belong to
7876419Ssb155480 		 * this vlan; see comments in vgen_vlan_frame_fixtag().
7886419Ssb155480 		 */
7896419Ssb155480 		if (portp != vgenp->vsw_portp &&
7906419Ssb155480 		    portp->pvid != vnetp->default_vlan_id) {
7916419Ssb155480 			freemsg(mp);
7926495Sspeer 			goto portsend_ret;
7936419Ssb155480 		}
7946419Ssb155480 
7956419Ssb155480 	} else {	/* frame not in default-vlan */
7966419Ssb155480 
7976419Ssb155480 		mp = vgen_vlan_frame_fixtag(portp, mp, is_tagged, vlan_id);
7986419Ssb155480 		if (mp == NULL) {
7996495Sspeer 			goto portsend_ret;
8006419Ssb155480 		}
8016419Ssb155480 
8026419Ssb155480 	}
8031991Sheppo 
8041991Sheppo 	ldclp = &portp->ldclist;
8051991Sheppo 	READ_ENTER(&ldclp->rwlock);
8061991Sheppo 	/*
8072336Snarayan 	 * NOTE: for now, we will assume we have a single channel.
8081991Sheppo 	 */
8091991Sheppo 	if (ldclp->headp == NULL) {
8101991Sheppo 		RW_EXIT(&ldclp->rwlock);
8116495Sspeer 		rv = VGEN_FAILURE;
8126495Sspeer 		goto portsend_ret;
8131991Sheppo 	}
8141991Sheppo 	ldcp = ldclp->headp;
8151991Sheppo 
8165935Ssb155480 	status = ldcp->tx(ldcp, mp);
8175022Sraghuram 
8181991Sheppo 	RW_EXIT(&ldclp->rwlock);
8191991Sheppo 
8205022Sraghuram 	if (status != VGEN_TX_SUCCESS) {
8215022Sraghuram 		rv = VGEN_FAILURE;
8225022Sraghuram 	}
8236495Sspeer 
8246495Sspeer portsend_ret:
8256495Sspeer 	if (dec_refcnt == B_TRUE) {
8266495Sspeer 		(void) atomic_dec_32(&vgenp->vsw_port_refcnt);
8276495Sspeer 	}
8285022Sraghuram 	return (rv);
8291991Sheppo }
8301991Sheppo 
8315935Ssb155480 /*
8325935Ssb155480  * Wrapper function to transmit normal and/or priority frames over the channel.
8335935Ssb155480  */
8341991Sheppo static int
8355935Ssb155480 vgen_ldcsend(void *arg, mblk_t *mp)
8365935Ssb155480 {
8375935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg;
8385935Ssb155480 	int			status;
8395935Ssb155480 	struct ether_header	*ehp;
8405935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
8415935Ssb155480 	uint32_t		num_types;
8425935Ssb155480 	uint16_t		*types;
8435935Ssb155480 	int			i;
8445935Ssb155480 
8455935Ssb155480 	ASSERT(VGEN_PRI_ETH_DEFINED(vgenp));
8465935Ssb155480 
8475935Ssb155480 	num_types = vgenp->pri_num_types;
8485935Ssb155480 	types = vgenp->pri_types;
8495935Ssb155480 	ehp = (struct ether_header *)mp->b_rptr;
8505935Ssb155480 
8515935Ssb155480 	for (i = 0; i < num_types; i++) {
8525935Ssb155480 
8535935Ssb155480 		if (ehp->ether_type == types[i]) {
8545935Ssb155480 			/* priority frame, use pri tx function */
8555935Ssb155480 			vgen_ldcsend_pkt(ldcp, mp);
8565935Ssb155480 			return (VGEN_SUCCESS);
8575935Ssb155480 		}
8585935Ssb155480 
8595935Ssb155480 	}
8605935Ssb155480 
8615935Ssb155480 	status  = vgen_ldcsend_dring(ldcp, mp);
8625935Ssb155480 
8635935Ssb155480 	return (status);
8645935Ssb155480 }
8655935Ssb155480 
8665935Ssb155480 /*
8675935Ssb155480  * This functions handles ldc channel reset while in the context
8685935Ssb155480  * of transmit routines: vgen_ldcsend_pkt() or vgen_ldcsend_dring().
8695935Ssb155480  */
8705935Ssb155480 static void
8715935Ssb155480 vgen_ldcsend_process_reset(vgen_ldc_t *ldcp)
8725935Ssb155480 {
8735935Ssb155480 	ldc_status_t	istatus;
8745935Ssb155480 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
8755935Ssb155480 
8765935Ssb155480 	if (mutex_tryenter(&ldcp->cblock)) {
8775935Ssb155480 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
8785935Ssb155480 			DWARN(vgenp, ldcp, "ldc_status() error\n");
8795935Ssb155480 		} else {
8805935Ssb155480 			ldcp->ldc_status = istatus;
8815935Ssb155480 		}
8825935Ssb155480 		if (ldcp->ldc_status != LDC_UP) {
8836495Sspeer 			vgen_handle_evt_reset(ldcp);
8845935Ssb155480 		}
8855935Ssb155480 		mutex_exit(&ldcp->cblock);
8865935Ssb155480 	}
8875935Ssb155480 }
8885935Ssb155480 
8895935Ssb155480 /*
8905935Ssb155480  * This function transmits the frame in the payload of a raw data
8915935Ssb155480  * (VIO_PKT_DATA) message. Thus, it provides an Out-Of-Band path to
8925935Ssb155480  * send special frames with high priorities, without going through
8935935Ssb155480  * the normal data path which uses descriptor ring mechanism.
8945935Ssb155480  */
8955935Ssb155480 static void
8965935Ssb155480 vgen_ldcsend_pkt(void *arg, mblk_t *mp)
8971991Sheppo {
8985935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg;
8995935Ssb155480 	vio_raw_data_msg_t	*pkt;
9005935Ssb155480 	mblk_t			*bp;
9015935Ssb155480 	mblk_t			*nmp = NULL;
9025935Ssb155480 	caddr_t			dst;
9035935Ssb155480 	uint32_t		mblksz;
9045935Ssb155480 	uint32_t		size;
9055935Ssb155480 	uint32_t		nbytes;
9065935Ssb155480 	int			rv;
9075935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
9085935Ssb155480 	vgen_stats_t		*statsp = &ldcp->stats;
9095935Ssb155480 
9105935Ssb155480 	/* drop the packet if ldc is not up or handshake is not done */
9115935Ssb155480 	if (ldcp->ldc_status != LDC_UP) {
9125935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9135935Ssb155480 		DWARN(vgenp, ldcp, "status(%d), dropping packet\n",
9145935Ssb155480 		    ldcp->ldc_status);
9155935Ssb155480 		goto send_pkt_exit;
9165935Ssb155480 	}
9175935Ssb155480 
9185935Ssb155480 	if (ldcp->hphase != VH_DONE) {
9195935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9205935Ssb155480 		DWARN(vgenp, ldcp, "hphase(%x), dropping packet\n",
9215935Ssb155480 		    ldcp->hphase);
9225935Ssb155480 		goto send_pkt_exit;
9235935Ssb155480 	}
9245935Ssb155480 
9255935Ssb155480 	size = msgsize(mp);
9265935Ssb155480 
9275935Ssb155480 	/* frame size bigger than available payload len of raw data msg ? */
9285935Ssb155480 	if (size > (size_t)(ldcp->msglen - VIO_PKT_DATA_HDRSIZE)) {
9295935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9305935Ssb155480 		DWARN(vgenp, ldcp, "invalid size(%d)\n", size);
9315935Ssb155480 		goto send_pkt_exit;
9325935Ssb155480 	}
9335935Ssb155480 
9345935Ssb155480 	if (size < ETHERMIN)
9355935Ssb155480 		size = ETHERMIN;
9365935Ssb155480 
9375935Ssb155480 	/* alloc space for a raw data message */
9385935Ssb155480 	nmp = vio_allocb(vgenp->pri_tx_vmp);
9395935Ssb155480 	if (nmp == NULL) {
9405935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9415935Ssb155480 		DWARN(vgenp, ldcp, "vio_allocb failed\n");
9425935Ssb155480 		goto send_pkt_exit;
9435935Ssb155480 	}
9445935Ssb155480 	pkt = (vio_raw_data_msg_t *)nmp->b_rptr;
9455935Ssb155480 
9465935Ssb155480 	/* copy frame into the payload of raw data message */
9475935Ssb155480 	dst = (caddr_t)pkt->data;
9485935Ssb155480 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
9495935Ssb155480 		mblksz = MBLKL(bp);
9505935Ssb155480 		bcopy(bp->b_rptr, dst, mblksz);
9515935Ssb155480 		dst += mblksz;
9525935Ssb155480 	}
9535935Ssb155480 
9545935Ssb155480 	/* setup the raw data msg */
9555935Ssb155480 	pkt->tag.vio_msgtype = VIO_TYPE_DATA;
9565935Ssb155480 	pkt->tag.vio_subtype = VIO_SUBTYPE_INFO;
9575935Ssb155480 	pkt->tag.vio_subtype_env = VIO_PKT_DATA;
9585935Ssb155480 	pkt->tag.vio_sid = ldcp->local_sid;
9595935Ssb155480 	nbytes = VIO_PKT_DATA_HDRSIZE + size;
9605935Ssb155480 
9615935Ssb155480 	/* send the msg over ldc */
9625935Ssb155480 	rv = vgen_sendmsg(ldcp, (caddr_t)pkt, nbytes, B_FALSE);
9635935Ssb155480 	if (rv != VGEN_SUCCESS) {
9645935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9655935Ssb155480 		DWARN(vgenp, ldcp, "Error sending priority frame\n");
9665935Ssb155480 		if (rv == ECONNRESET) {
9675935Ssb155480 			vgen_ldcsend_process_reset(ldcp);
9685935Ssb155480 		}
9695935Ssb155480 		goto send_pkt_exit;
9705935Ssb155480 	}
9715935Ssb155480 
9725935Ssb155480 	/* update stats */
9735935Ssb155480 	(void) atomic_inc_64(&statsp->tx_pri_packets);
9745935Ssb155480 	(void) atomic_add_64(&statsp->tx_pri_bytes, size);
9755935Ssb155480 
9765935Ssb155480 send_pkt_exit:
9775935Ssb155480 	if (nmp != NULL)
9785935Ssb155480 		freemsg(nmp);
9795935Ssb155480 	freemsg(mp);
9805935Ssb155480 }
9815935Ssb155480 
9825935Ssb155480 /*
9835935Ssb155480  * This function transmits normal (non-priority) data frames over
9845935Ssb155480  * the channel. It queues the frame into the transmit descriptor ring
9855935Ssb155480  * and sends a VIO_DRING_DATA message if needed, to wake up the
9865935Ssb155480  * peer to (re)start processing.
9875935Ssb155480  */
9885935Ssb155480 static int
9895935Ssb155480 vgen_ldcsend_dring(void *arg, mblk_t *mp)
9905935Ssb155480 {
9915935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg;
9921991Sheppo 	vgen_private_desc_t	*tbufp;
9934647Sraghuram 	vgen_private_desc_t	*rtbufp;
9944647Sraghuram 	vnet_public_desc_t	*rtxdp;
9951991Sheppo 	vgen_private_desc_t	*ntbufp;
9961991Sheppo 	vnet_public_desc_t	*txdp;
9974647Sraghuram 	vio_dring_entry_hdr_t	*hdrp;
9981991Sheppo 	vgen_stats_t		*statsp;
9991991Sheppo 	struct ether_header	*ehp;
10006419Ssb155480 	boolean_t		is_bcast = B_FALSE;
10016419Ssb155480 	boolean_t		is_mcast = B_FALSE;
10026419Ssb155480 	size_t			mblksz;
10036419Ssb155480 	caddr_t			dst;
10046419Ssb155480 	mblk_t			*bp;
10056419Ssb155480 	size_t			size;
10066419Ssb155480 	int			rv = 0;
10076419Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
10086419Ssb155480 	vgen_hparams_t		*lp = &ldcp->local_hparams;
10094647Sraghuram 
10105373Sraghuram 	statsp = &ldcp->stats;
10112109Slm66018 	size = msgsize(mp);
10122109Slm66018 
10134647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
10144647Sraghuram 
10152109Slm66018 	if (ldcp->ldc_status != LDC_UP) {
10164647Sraghuram 		DWARN(vgenp, ldcp, "status(%d), dropping packet\n",
10174647Sraghuram 		    ldcp->ldc_status);
10182109Slm66018 		/* retry ldc_up() if needed */
10192109Slm66018 		if (ldcp->flags & CHANNEL_STARTED)
10202109Slm66018 			(void) ldc_up(ldcp->ldc_handle);
10215935Ssb155480 		goto send_dring_exit;
10221991Sheppo 	}
10231991Sheppo 
10244647Sraghuram 	/* drop the packet if ldc is not up or handshake is not done */
10252109Slm66018 	if (ldcp->hphase != VH_DONE) {
10264647Sraghuram 		DWARN(vgenp, ldcp, "hphase(%x), dropping packet\n",
10274647Sraghuram 		    ldcp->hphase);
10285935Ssb155480 		goto send_dring_exit;
10292109Slm66018 	}
10302109Slm66018 
10316419Ssb155480 	if (size > (size_t)lp->mtu) {
10324647Sraghuram 		DWARN(vgenp, ldcp, "invalid size(%d)\n", size);
10335935Ssb155480 		goto send_dring_exit;
10341991Sheppo 	}
10354647Sraghuram 	if (size < ETHERMIN)
10364647Sraghuram 		size = ETHERMIN;
10374647Sraghuram 
10384647Sraghuram 	ehp = (struct ether_header *)mp->b_rptr;
10394647Sraghuram 	is_bcast = IS_BROADCAST(ehp);
10404647Sraghuram 	is_mcast = IS_MULTICAST(ehp);
10414647Sraghuram 
10424647Sraghuram 	mutex_enter(&ldcp->txlock);
10431991Sheppo 	/*
10441991Sheppo 	 * allocate a descriptor
10451991Sheppo 	 */
10461991Sheppo 	tbufp = ldcp->next_tbufp;
10471991Sheppo 	ntbufp = NEXTTBUF(ldcp, tbufp);
10482336Snarayan 	if (ntbufp == ldcp->cur_tbufp) { /* out of tbufs/txds */
10491991Sheppo 
10501991Sheppo 		mutex_enter(&ldcp->tclock);
10514647Sraghuram 		/* Try reclaiming now */
10524647Sraghuram 		vgen_reclaim_dring(ldcp);
10534647Sraghuram 		ldcp->reclaim_lbolt = ddi_get_lbolt();
10544647Sraghuram 
10552336Snarayan 		if (ntbufp == ldcp->cur_tbufp) {
10564647Sraghuram 			/* Now we are really out of tbuf/txds */
10571991Sheppo 			ldcp->need_resched = B_TRUE;
10582336Snarayan 			mutex_exit(&ldcp->tclock);
10592336Snarayan 
10602336Snarayan 			statsp->tx_no_desc++;
10612336Snarayan 			mutex_exit(&ldcp->txlock);
10622336Snarayan 
10632336Snarayan 			return (VGEN_TX_NORESOURCES);
10642336Snarayan 		}
10651991Sheppo 		mutex_exit(&ldcp->tclock);
10661991Sheppo 	}
10674647Sraghuram 	/* update next available tbuf in the ring and update tx index */
10684647Sraghuram 	ldcp->next_tbufp = ntbufp;
10694647Sraghuram 	INCR_TXI(ldcp->next_txi, ldcp);
10704647Sraghuram 
10714647Sraghuram 	/* Mark the buffer busy before releasing the lock */
10724647Sraghuram 	tbufp->flags = VGEN_PRIV_DESC_BUSY;
10734647Sraghuram 	mutex_exit(&ldcp->txlock);
10742109Slm66018 
10752109Slm66018 	/* copy data into pre-allocated transmit buffer */
10762336Snarayan 	dst = tbufp->datap + VNET_IPALIGN;
10772336Snarayan 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
10782336Snarayan 		mblksz = MBLKL(bp);
10792336Snarayan 		bcopy(bp->b_rptr, dst, mblksz);
10802336Snarayan 		dst += mblksz;
10811991Sheppo 	}
10821991Sheppo 
10832109Slm66018 	tbufp->datalen = size;
10841991Sheppo 
10851991Sheppo 	/* initialize the corresponding public descriptor (txd) */
10861991Sheppo 	txdp = tbufp->descp;
10871991Sheppo 	hdrp = &txdp->hdr;
10882109Slm66018 	txdp->nbytes = size;
10892109Slm66018 	txdp->ncookies = tbufp->ncookies;
10901991Sheppo 	bcopy((tbufp->memcookie), (txdp->memcookie),
10914650Sraghuram 	    tbufp->ncookies * sizeof (ldc_mem_cookie_t));
10924647Sraghuram 
10934647Sraghuram 	mutex_enter(&ldcp->wrlock);
10944647Sraghuram 	/*
10954647Sraghuram 	 * If the flags not set to BUSY, it implies that the clobber
10964647Sraghuram 	 * was done while we were copying the data. In such case,
10974647Sraghuram 	 * discard the packet and return.
10984647Sraghuram 	 */
10994647Sraghuram 	if (tbufp->flags != VGEN_PRIV_DESC_BUSY) {
11004647Sraghuram 		statsp->oerrors++;
11014647Sraghuram 		mutex_exit(&ldcp->wrlock);
11025935Ssb155480 		goto send_dring_exit;
11034647Sraghuram 	}
11042336Snarayan 	hdrp->dstate = VIO_DESC_READY;
11051991Sheppo 
11061991Sheppo 	/* update stats */
11071991Sheppo 	statsp->opackets++;
11082109Slm66018 	statsp->obytes += size;
11091991Sheppo 	if (is_bcast)
11101991Sheppo 		statsp->brdcstxmt++;
11111991Sheppo 	else if (is_mcast)
11121991Sheppo 		statsp->multixmt++;
11131991Sheppo 
11144647Sraghuram 	/* send dring datamsg to the peer */
11154647Sraghuram 	if (ldcp->resched_peer) {
11164647Sraghuram 
11174647Sraghuram 		rtbufp = &ldcp->tbufp[ldcp->resched_peer_txi];
11184647Sraghuram 		rtxdp = rtbufp->descp;
11194647Sraghuram 
11204647Sraghuram 		if (rtxdp->hdr.dstate == VIO_DESC_READY) {
11214647Sraghuram 
11224647Sraghuram 			rv = vgen_send_dring_data(ldcp,
11234647Sraghuram 			    (uint32_t)ldcp->resched_peer_txi, -1);
11244647Sraghuram 			if (rv != 0) {
11254647Sraghuram 				/* error: drop the packet */
11264647Sraghuram 				DWARN(vgenp, ldcp, "vgen_send_dring_data "
11274650Sraghuram 				    "failed: rv(%d) len(%d)\n",
11284650Sraghuram 				    ldcp->ldc_id, rv, size);
11294647Sraghuram 				statsp->oerrors++;
11304647Sraghuram 			} else {
11314647Sraghuram 				ldcp->resched_peer = B_FALSE;
11324647Sraghuram 			}
11334647Sraghuram 
11344647Sraghuram 		}
11354647Sraghuram 
11364647Sraghuram 	}
11374647Sraghuram 
11384647Sraghuram 	mutex_exit(&ldcp->wrlock);
11394647Sraghuram 
11405935Ssb155480 send_dring_exit:
11412793Slm66018 	if (rv == ECONNRESET) {
11425935Ssb155480 		vgen_ldcsend_process_reset(ldcp);
11432793Slm66018 	}
11442109Slm66018 	freemsg(mp);
11454647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
11462109Slm66018 	return (VGEN_TX_SUCCESS);
11471991Sheppo }
11481991Sheppo 
11491991Sheppo /* enable/disable a multicast address */
11502311Sseb int
11511991Sheppo vgen_multicst(void *arg, boolean_t add, const uint8_t *mca)
11521991Sheppo {
11531991Sheppo 	vgen_t			*vgenp;
11541991Sheppo 	vnet_mcast_msg_t	mcastmsg;
11551991Sheppo 	vio_msg_tag_t		*tagp;
11561991Sheppo 	vgen_port_t		*portp;
11571991Sheppo 	vgen_portlist_t		*plistp;
11581991Sheppo 	vgen_ldc_t		*ldcp;
11591991Sheppo 	vgen_ldclist_t		*ldclp;
11601991Sheppo 	struct ether_addr	*addrp;
11613653Snarayan 	int			rv = DDI_FAILURE;
11621991Sheppo 	uint32_t		i;
11631991Sheppo 
11646495Sspeer 	portp = (vgen_port_t *)arg;
11656495Sspeer 	vgenp = portp->vgenp;
11666495Sspeer 
11676495Sspeer 	if (portp != vgenp->vsw_portp) {
11686495Sspeer 		return (DDI_SUCCESS);
11696495Sspeer 	}
11706495Sspeer 
11711991Sheppo 	addrp = (struct ether_addr *)mca;
11721991Sheppo 	tagp = &mcastmsg.tag;
11731991Sheppo 	bzero(&mcastmsg, sizeof (mcastmsg));
11741991Sheppo 
11751991Sheppo 	mutex_enter(&vgenp->lock);
11761991Sheppo 
11771991Sheppo 	plistp = &(vgenp->vgenports);
11781991Sheppo 
11791991Sheppo 	READ_ENTER(&plistp->rwlock);
11801991Sheppo 
11811991Sheppo 	portp = vgenp->vsw_portp;
11821991Sheppo 	if (portp == NULL) {
11831991Sheppo 		RW_EXIT(&plistp->rwlock);
11843653Snarayan 		mutex_exit(&vgenp->lock);
11853653Snarayan 		return (rv);
11861991Sheppo 	}
11871991Sheppo 	ldclp = &portp->ldclist;
11881991Sheppo 
11891991Sheppo 	READ_ENTER(&ldclp->rwlock);
11901991Sheppo 
11911991Sheppo 	ldcp = ldclp->headp;
11923653Snarayan 	if (ldcp == NULL)
11931991Sheppo 		goto vgen_mcast_exit;
11941991Sheppo 
11951991Sheppo 	mutex_enter(&ldcp->cblock);
11961991Sheppo 
11971991Sheppo 	if (ldcp->hphase == VH_DONE) {
11981991Sheppo 		/*
11991991Sheppo 		 * If handshake is done, send a msg to vsw to add/remove
12005171Ssb155480 		 * the multicast address. Otherwise, we just update this
12015171Ssb155480 		 * mcast address in our table and the table will be sync'd
12025171Ssb155480 		 * with vsw when handshake completes.
12031991Sheppo 		 */
12041991Sheppo 		tagp->vio_msgtype = VIO_TYPE_CTRL;
12051991Sheppo 		tagp->vio_subtype = VIO_SUBTYPE_INFO;
12061991Sheppo 		tagp->vio_subtype_env = VNET_MCAST_INFO;
12071991Sheppo 		tagp->vio_sid = ldcp->local_sid;
12081991Sheppo 		bcopy(mca, &(mcastmsg.mca), ETHERADDRL);
12091991Sheppo 		mcastmsg.set = add;
12101991Sheppo 		mcastmsg.count = 1;
12113653Snarayan 		if (vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (mcastmsg),
12123653Snarayan 		    B_FALSE) != VGEN_SUCCESS) {
12134647Sraghuram 			DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
12143653Snarayan 			mutex_exit(&ldcp->cblock);
12153653Snarayan 			goto vgen_mcast_exit;
12161991Sheppo 		}
12171991Sheppo 	}
12181991Sheppo 
12191991Sheppo 	mutex_exit(&ldcp->cblock);
12201991Sheppo 
12211991Sheppo 	if (add) {
12221991Sheppo 
12231991Sheppo 		/* expand multicast table if necessary */
12241991Sheppo 		if (vgenp->mccount >= vgenp->mcsize) {
12251991Sheppo 			struct ether_addr	*newtab;
12261991Sheppo 			uint32_t		newsize;
12271991Sheppo 
12281991Sheppo 
12291991Sheppo 			newsize = vgenp->mcsize * 2;
12301991Sheppo 
12311991Sheppo 			newtab = kmem_zalloc(newsize *
12321991Sheppo 			    sizeof (struct ether_addr), KM_NOSLEEP);
12333653Snarayan 			if (newtab == NULL)
12343653Snarayan 				goto vgen_mcast_exit;
12351991Sheppo 			bcopy(vgenp->mctab, newtab, vgenp->mcsize *
12361991Sheppo 			    sizeof (struct ether_addr));
12371991Sheppo 			kmem_free(vgenp->mctab,
12381991Sheppo 			    vgenp->mcsize * sizeof (struct ether_addr));
12391991Sheppo 
12401991Sheppo 			vgenp->mctab = newtab;
12411991Sheppo 			vgenp->mcsize = newsize;
12421991Sheppo 		}
12431991Sheppo 
12441991Sheppo 		/* add address to the table */
12451991Sheppo 		vgenp->mctab[vgenp->mccount++] = *addrp;
12461991Sheppo 
12471991Sheppo 	} else {
12481991Sheppo 
12491991Sheppo 		/* delete address from the table */
12501991Sheppo 		for (i = 0; i < vgenp->mccount; i++) {
12511991Sheppo 			if (ether_cmp(addrp, &(vgenp->mctab[i])) == 0) {
12521991Sheppo 
12531991Sheppo 				/*
12541991Sheppo 				 * If there's more than one address in this
12551991Sheppo 				 * table, delete the unwanted one by moving
12561991Sheppo 				 * the last one in the list over top of it;
12571991Sheppo 				 * otherwise, just remove it.
12581991Sheppo 				 */
12591991Sheppo 				if (vgenp->mccount > 1) {
12601991Sheppo 					vgenp->mctab[i] =
12614650Sraghuram 					    vgenp->mctab[vgenp->mccount-1];
12621991Sheppo 				}
12631991Sheppo 				vgenp->mccount--;
12641991Sheppo 				break;
12651991Sheppo 			}
12661991Sheppo 		}
12671991Sheppo 	}
12681991Sheppo 
12693653Snarayan 	rv = DDI_SUCCESS;
12703653Snarayan 
12713653Snarayan vgen_mcast_exit:
12721991Sheppo 	RW_EXIT(&ldclp->rwlock);
12731991Sheppo 	RW_EXIT(&plistp->rwlock);
12741991Sheppo 
12751991Sheppo 	mutex_exit(&vgenp->lock);
12763653Snarayan 	return (rv);
12771991Sheppo }
12781991Sheppo 
12791991Sheppo /* set or clear promiscuous mode on the device */
12801991Sheppo static int
12811991Sheppo vgen_promisc(void *arg, boolean_t on)
12821991Sheppo {
12831991Sheppo 	_NOTE(ARGUNUSED(arg, on))
12841991Sheppo 	return (DDI_SUCCESS);
12851991Sheppo }
12861991Sheppo 
12871991Sheppo /* set the unicast mac address of the device */
12881991Sheppo static int
12891991Sheppo vgen_unicst(void *arg, const uint8_t *mca)
12901991Sheppo {
12911991Sheppo 	_NOTE(ARGUNUSED(arg, mca))
12921991Sheppo 	return (DDI_SUCCESS);
12931991Sheppo }
12941991Sheppo 
12951991Sheppo /* get device statistics */
12962311Sseb int
12972311Sseb vgen_stat(void *arg, uint_t stat, uint64_t *val)
12981991Sheppo {
12996495Sspeer 	vgen_port_t	*portp = (vgen_port_t *)arg;
13006495Sspeer 
13016495Sspeer 	*val = vgen_port_stat(portp, stat);
13021991Sheppo 
13032311Sseb 	return (0);
13041991Sheppo }
13051991Sheppo 
13061991Sheppo static void
13071991Sheppo vgen_ioctl(void *arg, queue_t *wq, mblk_t *mp)
13081991Sheppo {
13091991Sheppo 	 _NOTE(ARGUNUSED(arg, wq, mp))
13101991Sheppo }
13111991Sheppo 
13121991Sheppo /* vgen internal functions */
13131991Sheppo /* detach all ports from the device */
13141991Sheppo static void
13151991Sheppo vgen_detach_ports(vgen_t *vgenp)
13161991Sheppo {
13171991Sheppo 	vgen_port_t	*portp;
13181991Sheppo 	vgen_portlist_t	*plistp;
13191991Sheppo 
13201991Sheppo 	plistp = &(vgenp->vgenports);
13211991Sheppo 	WRITE_ENTER(&plistp->rwlock);
13221991Sheppo 
13231991Sheppo 	while ((portp = plistp->headp) != NULL) {
13241991Sheppo 		vgen_port_detach(portp);
13251991Sheppo 	}
13261991Sheppo 
13271991Sheppo 	RW_EXIT(&plistp->rwlock);
13281991Sheppo }
13291991Sheppo 
13301991Sheppo /*
13311991Sheppo  * detach the given port.
13321991Sheppo  */
13331991Sheppo static void
13341991Sheppo vgen_port_detach(vgen_port_t *portp)
13351991Sheppo {
13361991Sheppo 	vgen_t		*vgenp;
13371991Sheppo 	vgen_ldclist_t	*ldclp;
13381991Sheppo 	int		port_num;
13391991Sheppo 
13401991Sheppo 	vgenp = portp->vgenp;
13411991Sheppo 	port_num = portp->port_num;
13421991Sheppo 
13434647Sraghuram 	DBG1(vgenp, NULL, "port(%d):enter\n", port_num);
13441991Sheppo 
13456495Sspeer 	/*
13466495Sspeer 	 * If this port is connected to the vswitch, then
13476495Sspeer 	 * potentially there could be ports that may be using
13486495Sspeer 	 * this port to transmit packets. To address this do
13496495Sspeer 	 * the following:
13506495Sspeer 	 *	- First set vgenp->vsw_portp to NULL, so that
13516495Sspeer 	 *	  its not used after that.
13526495Sspeer 	 *	- Then wait for the refcnt to go down to 0.
13536495Sspeer 	 *	- Now we can safely detach this port.
13546495Sspeer 	 */
13556495Sspeer 	if (vgenp->vsw_portp == portp) {
13566495Sspeer 		vgenp->vsw_portp = NULL;
13576495Sspeer 		while (vgenp->vsw_port_refcnt > 0) {
13586495Sspeer 			delay(drv_usectohz(vgen_tx_delay));
13596495Sspeer 		}
13606495Sspeer 		(void) atomic_swap_32(&vgenp->vsw_port_refcnt, 0);
13616495Sspeer 	}
13626495Sspeer 
13636495Sspeer 	if (portp->vhp != NULL) {
13646495Sspeer 		vio_net_resource_unreg(portp->vhp);
13656495Sspeer 		portp->vhp = NULL;
13666495Sspeer 	}
13676495Sspeer 
13686419Ssb155480 	vgen_vlan_destroy_hash(portp);
13696419Ssb155480 
13701991Sheppo 	/* remove it from port list */
13711991Sheppo 	vgen_port_list_remove(portp);
13721991Sheppo 
13731991Sheppo 	/* detach channels from this port */
13741991Sheppo 	ldclp = &portp->ldclist;
13751991Sheppo 	WRITE_ENTER(&ldclp->rwlock);
13761991Sheppo 	while (ldclp->headp) {
13771991Sheppo 		vgen_ldc_detach(ldclp->headp);
13781991Sheppo 	}
13791991Sheppo 	RW_EXIT(&ldclp->rwlock);
13805641Swentaoy 	rw_destroy(&ldclp->rwlock);
13811991Sheppo 
13826419Ssb155480 	if (portp->num_ldcs != 0) {
13836419Ssb155480 		kmem_free(portp->ldc_ids, portp->num_ldcs * sizeof (uint64_t));
13846419Ssb155480 		portp->num_ldcs = 0;
13856419Ssb155480 	}
13866419Ssb155480 
13876495Sspeer 	mutex_destroy(&portp->lock);
13881991Sheppo 	KMEM_FREE(portp);
13891991Sheppo 
13904647Sraghuram 	DBG1(vgenp, NULL, "port(%d):exit\n", port_num);
13911991Sheppo }
13921991Sheppo 
13931991Sheppo /* add a port to port list */
13941991Sheppo static void
13951991Sheppo vgen_port_list_insert(vgen_port_t *portp)
13961991Sheppo {
13971991Sheppo 	vgen_portlist_t *plistp;
13981991Sheppo 	vgen_t *vgenp;
13991991Sheppo 
14001991Sheppo 	vgenp = portp->vgenp;
14011991Sheppo 	plistp = &(vgenp->vgenports);
14021991Sheppo 
14031991Sheppo 	if (plistp->headp == NULL) {
14041991Sheppo 		plistp->headp = portp;
14051991Sheppo 	} else {
14061991Sheppo 		plistp->tailp->nextp = portp;
14071991Sheppo 	}
14081991Sheppo 	plistp->tailp = portp;
14091991Sheppo 	portp->nextp = NULL;
14101991Sheppo }
14111991Sheppo 
14121991Sheppo /* remove a port from port list */
14131991Sheppo static void
14141991Sheppo vgen_port_list_remove(vgen_port_t *portp)
14151991Sheppo {
14161991Sheppo 	vgen_port_t *prevp;
14171991Sheppo 	vgen_port_t *nextp;
14181991Sheppo 	vgen_portlist_t *plistp;
14191991Sheppo 	vgen_t *vgenp;
14201991Sheppo 
14211991Sheppo 	vgenp = portp->vgenp;
14221991Sheppo 
14231991Sheppo 	plistp = &(vgenp->vgenports);
14241991Sheppo 
14251991Sheppo 	if (plistp->headp == NULL)
14261991Sheppo 		return;
14271991Sheppo 
14281991Sheppo 	if (portp == plistp->headp) {
14291991Sheppo 		plistp->headp = portp->nextp;
14301991Sheppo 		if (portp == plistp->tailp)
14311991Sheppo 			plistp->tailp = plistp->headp;
14321991Sheppo 	} else {
14334650Sraghuram 		for (prevp = plistp->headp;
14344650Sraghuram 		    ((nextp = prevp->nextp) != NULL) && (nextp != portp);
14354650Sraghuram 		    prevp = nextp)
14364650Sraghuram 			;
14371991Sheppo 		if (nextp == portp) {
14381991Sheppo 			prevp->nextp = portp->nextp;
14391991Sheppo 		}
14401991Sheppo 		if (portp == plistp->tailp)
14411991Sheppo 			plistp->tailp = prevp;
14421991Sheppo 	}
14431991Sheppo }
14441991Sheppo 
14451991Sheppo /* lookup a port in the list based on port_num */
14461991Sheppo static vgen_port_t *
14471991Sheppo vgen_port_lookup(vgen_portlist_t *plistp, int port_num)
14481991Sheppo {
14491991Sheppo 	vgen_port_t *portp = NULL;
14501991Sheppo 
14511991Sheppo 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
14521991Sheppo 		if (portp->port_num == port_num) {
14531991Sheppo 			break;
14541991Sheppo 		}
14551991Sheppo 	}
14561991Sheppo 
14571991Sheppo 	return (portp);
14581991Sheppo }
14591991Sheppo 
14601991Sheppo /* enable ports for transmit/receive */
14611991Sheppo static void
14621991Sheppo vgen_init_ports(vgen_t *vgenp)
14631991Sheppo {
14641991Sheppo 	vgen_port_t	*portp;
14651991Sheppo 	vgen_portlist_t	*plistp;
14661991Sheppo 
14671991Sheppo 	plistp = &(vgenp->vgenports);
14681991Sheppo 	READ_ENTER(&plistp->rwlock);
14691991Sheppo 
14701991Sheppo 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
14711991Sheppo 		vgen_port_init(portp);
14721991Sheppo 	}
14731991Sheppo 
14741991Sheppo 	RW_EXIT(&plistp->rwlock);
14751991Sheppo }
14761991Sheppo 
14771991Sheppo static void
14781991Sheppo vgen_port_init(vgen_port_t *portp)
14791991Sheppo {
14806419Ssb155480 	/* Add the port to the specified vlans */
14816419Ssb155480 	vgen_vlan_add_ids(portp);
14821991Sheppo 
14831991Sheppo 	/* Bring up the channels of this port */
14841991Sheppo 	vgen_init_ldcs(portp);
14851991Sheppo }
14861991Sheppo 
14871991Sheppo /* disable transmit/receive on ports */
14881991Sheppo static void
14891991Sheppo vgen_uninit_ports(vgen_t *vgenp)
14901991Sheppo {
14911991Sheppo 	vgen_port_t	*portp;
14921991Sheppo 	vgen_portlist_t	*plistp;
14931991Sheppo 
14941991Sheppo 	plistp = &(vgenp->vgenports);
14951991Sheppo 	READ_ENTER(&plistp->rwlock);
14961991Sheppo 
14971991Sheppo 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
14981991Sheppo 		vgen_port_uninit(portp);
14991991Sheppo 	}
15001991Sheppo 
15011991Sheppo 	RW_EXIT(&plistp->rwlock);
15021991Sheppo }
15031991Sheppo 
15041991Sheppo static void
15051991Sheppo vgen_port_uninit(vgen_port_t *portp)
15061991Sheppo {
15071991Sheppo 	vgen_uninit_ldcs(portp);
15086419Ssb155480 
15096419Ssb155480 	/* remove the port from vlans it has been assigned to */
15106419Ssb155480 	vgen_vlan_remove_ids(portp);
15111991Sheppo }
15121991Sheppo 
15135935Ssb155480 /*
15145935Ssb155480  * Scan the machine description for this instance of vnet
15155935Ssb155480  * and read its properties. Called only from vgen_init().
15165935Ssb155480  * Returns: 0 on success, 1 on failure.
15175935Ssb155480  */
15185935Ssb155480 static int
15195935Ssb155480 vgen_read_mdprops(vgen_t *vgenp)
15205935Ssb155480 {
15216419Ssb155480 	vnet_t		*vnetp = vgenp->vnetp;
15225935Ssb155480 	md_t		*mdp = NULL;
15235935Ssb155480 	mde_cookie_t	rootnode;
15245935Ssb155480 	mde_cookie_t	*listp = NULL;
15255935Ssb155480 	uint64_t	cfgh;
15265935Ssb155480 	char		*name;
15275935Ssb155480 	int		rv = 1;
15285935Ssb155480 	int		num_nodes = 0;
15295935Ssb155480 	int		num_devs = 0;
15305935Ssb155480 	int		listsz = 0;
15315935Ssb155480 	int		i;
15325935Ssb155480 
15335935Ssb155480 	if ((mdp = md_get_handle()) == NULL) {
15345935Ssb155480 		return (rv);
15355935Ssb155480 	}
15365935Ssb155480 
15375935Ssb155480 	num_nodes = md_node_count(mdp);
15385935Ssb155480 	ASSERT(num_nodes > 0);
15395935Ssb155480 
15405935Ssb155480 	listsz = num_nodes * sizeof (mde_cookie_t);
15415935Ssb155480 	listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP);
15425935Ssb155480 
15435935Ssb155480 	rootnode = md_root_node(mdp);
15445935Ssb155480 
15455935Ssb155480 	/* search for all "virtual_device" nodes */
15465935Ssb155480 	num_devs = md_scan_dag(mdp, rootnode,
15475935Ssb155480 	    md_find_name(mdp, vdev_propname),
15485935Ssb155480 	    md_find_name(mdp, "fwd"), listp);
15495935Ssb155480 	if (num_devs <= 0) {
15505935Ssb155480 		goto vgen_readmd_exit;
15515935Ssb155480 	}
15525935Ssb155480 
15535935Ssb155480 	/*
15545935Ssb155480 	 * Now loop through the list of virtual-devices looking for
15555935Ssb155480 	 * devices with name "network" and for each such device compare
15565935Ssb155480 	 * its instance with what we have from the 'reg' property to
15575935Ssb155480 	 * find the right node in MD and then read all its properties.
15585935Ssb155480 	 */
15595935Ssb155480 	for (i = 0; i < num_devs; i++) {
15605935Ssb155480 
15615935Ssb155480 		if (md_get_prop_str(mdp, listp[i], "name", &name) != 0) {
15625935Ssb155480 			goto vgen_readmd_exit;
15635935Ssb155480 		}
15645935Ssb155480 
15655935Ssb155480 		/* is this a "network" device? */
15665935Ssb155480 		if (strcmp(name, vnet_propname) != 0)
15675935Ssb155480 			continue;
15685935Ssb155480 
15695935Ssb155480 		if (md_get_prop_val(mdp, listp[i], "cfg-handle", &cfgh) != 0) {
15705935Ssb155480 			goto vgen_readmd_exit;
15715935Ssb155480 		}
15725935Ssb155480 
15735935Ssb155480 		/* is this the required instance of vnet? */
15746495Sspeer 		if (vgenp->regprop != cfgh)
15755935Ssb155480 			continue;
15765935Ssb155480 
15777529SSriharsha.Basavapatna@Sun.COM 		/*
15787529SSriharsha.Basavapatna@Sun.COM 		 * Read the mtu. Note that we set the mtu of vnet device within
15797529SSriharsha.Basavapatna@Sun.COM 		 * this routine itself, after validating the range.
15807529SSriharsha.Basavapatna@Sun.COM 		 */
15817529SSriharsha.Basavapatna@Sun.COM 		vgen_mtu_read(vgenp, mdp, listp[i], &vnetp->mtu);
15827529SSriharsha.Basavapatna@Sun.COM 		if (vnetp->mtu < ETHERMTU || vnetp->mtu > VNET_MAX_MTU) {
15837529SSriharsha.Basavapatna@Sun.COM 			vnetp->mtu = ETHERMTU;
15847529SSriharsha.Basavapatna@Sun.COM 		}
15857529SSriharsha.Basavapatna@Sun.COM 		vgenp->max_frame_size = vnetp->mtu +
15867529SSriharsha.Basavapatna@Sun.COM 		    sizeof (struct ether_header) + VLAN_TAGSZ;
15877529SSriharsha.Basavapatna@Sun.COM 
15887529SSriharsha.Basavapatna@Sun.COM 		/* read priority ether types */
15895935Ssb155480 		vgen_read_pri_eth_types(vgenp, mdp, listp[i]);
15906419Ssb155480 
15916419Ssb155480 		/* read vlan id properties of this vnet instance */
15926419Ssb155480 		vgen_vlan_read_ids(vgenp, VGEN_LOCAL, mdp, listp[i],
15936419Ssb155480 		    &vnetp->pvid, &vnetp->vids, &vnetp->nvids,
15946419Ssb155480 		    &vnetp->default_vlan_id);
15956419Ssb155480 
15965935Ssb155480 		rv = 0;
15975935Ssb155480 		break;
15985935Ssb155480 	}
15995935Ssb155480 
16005935Ssb155480 vgen_readmd_exit:
16015935Ssb155480 
16025935Ssb155480 	kmem_free(listp, listsz);
16035935Ssb155480 	(void) md_fini_handle(mdp);
16045935Ssb155480 	return (rv);
16055935Ssb155480 }
16065935Ssb155480 
16075935Ssb155480 /*
16086419Ssb155480  * Read vlan id properties of the given MD node.
16096419Ssb155480  * Arguments:
16106419Ssb155480  *   arg:          device argument(vnet device or a port)
16116419Ssb155480  *   type:         type of arg; VGEN_LOCAL(vnet device) or VGEN_PEER(port)
16126419Ssb155480  *   mdp:          machine description
16136419Ssb155480  *   node:         md node cookie
16146419Ssb155480  *
16156419Ssb155480  * Returns:
16166419Ssb155480  *   pvidp:        port-vlan-id of the node
16176419Ssb155480  *   vidspp:       list of vlan-ids of the node
16186419Ssb155480  *   nvidsp:       # of vlan-ids in the list
16196419Ssb155480  *   default_idp:  default-vlan-id of the node(if node is vnet device)
16206419Ssb155480  */
16216419Ssb155480 static void
16226419Ssb155480 vgen_vlan_read_ids(void *arg, int type, md_t *mdp, mde_cookie_t node,
16236419Ssb155480 	uint16_t *pvidp, uint16_t **vidspp, uint16_t *nvidsp,
16246419Ssb155480 	uint16_t *default_idp)
16256419Ssb155480 {
16266419Ssb155480 	vgen_t		*vgenp;
16276419Ssb155480 	vnet_t		*vnetp;
16286419Ssb155480 	vgen_port_t	*portp;
16296419Ssb155480 	char		*pvid_propname;
16306419Ssb155480 	char		*vid_propname;
16316419Ssb155480 	uint_t		nvids;
16326419Ssb155480 	uint32_t	vids_size;
16336419Ssb155480 	int		rv;
16346419Ssb155480 	int		i;
16356419Ssb155480 	uint64_t	*data;
16366419Ssb155480 	uint64_t	val;
16376419Ssb155480 	int		size;
16386419Ssb155480 	int		inst;
16396419Ssb155480 
16406419Ssb155480 	if (type == VGEN_LOCAL) {
16416419Ssb155480 
16426419Ssb155480 		vgenp = (vgen_t *)arg;
16436419Ssb155480 		vnetp = vgenp->vnetp;
16446419Ssb155480 		pvid_propname = vgen_pvid_propname;
16456419Ssb155480 		vid_propname = vgen_vid_propname;
16466419Ssb155480 		inst = vnetp->instance;
16476419Ssb155480 
16486419Ssb155480 	} else if (type == VGEN_PEER) {
16496419Ssb155480 
16506419Ssb155480 		portp = (vgen_port_t *)arg;
16516419Ssb155480 		vgenp = portp->vgenp;
16526419Ssb155480 		vnetp = vgenp->vnetp;
16536419Ssb155480 		pvid_propname = port_pvid_propname;
16546419Ssb155480 		vid_propname = port_vid_propname;
16556419Ssb155480 		inst = portp->port_num;
16566419Ssb155480 
16576419Ssb155480 	} else {
16586419Ssb155480 		return;
16596419Ssb155480 	}
16606419Ssb155480 
16616419Ssb155480 	if (type == VGEN_LOCAL && default_idp != NULL) {
16626419Ssb155480 		rv = md_get_prop_val(mdp, node, vgen_dvid_propname, &val);
16636419Ssb155480 		if (rv != 0) {
16646419Ssb155480 			DWARN(vgenp, NULL, "prop(%s) not found",
16656419Ssb155480 			    vgen_dvid_propname);
16666419Ssb155480 
16676419Ssb155480 			*default_idp = vnet_default_vlan_id;
16686419Ssb155480 		} else {
16696419Ssb155480 			*default_idp = val & 0xFFF;
16706419Ssb155480 			DBG2(vgenp, NULL, "%s(%d): (%d)\n", vgen_dvid_propname,
16716419Ssb155480 			    inst, *default_idp);
16726419Ssb155480 		}
16736419Ssb155480 	}
16746419Ssb155480 
16756419Ssb155480 	rv = md_get_prop_val(mdp, node, pvid_propname, &val);
16766419Ssb155480 	if (rv != 0) {
16776419Ssb155480 		DWARN(vgenp, NULL, "prop(%s) not found", pvid_propname);
16786419Ssb155480 		*pvidp = vnet_default_vlan_id;
16796419Ssb155480 	} else {
16806419Ssb155480 
16816419Ssb155480 		*pvidp = val & 0xFFF;
16826419Ssb155480 		DBG2(vgenp, NULL, "%s(%d): (%d)\n",
16836419Ssb155480 		    pvid_propname, inst, *pvidp);
16846419Ssb155480 	}
16856419Ssb155480 
16866419Ssb155480 	rv = md_get_prop_data(mdp, node, vid_propname, (uint8_t **)&data,
16876419Ssb155480 	    &size);
16886419Ssb155480 	if (rv != 0) {
16896419Ssb155480 		DBG2(vgenp, NULL, "prop(%s) not found", vid_propname);
16906419Ssb155480 		size = 0;
16916419Ssb155480 	} else {
16926419Ssb155480 		size /= sizeof (uint64_t);
16936419Ssb155480 	}
16946419Ssb155480 	nvids = size;
16956419Ssb155480 
16966419Ssb155480 	if (nvids != 0) {
16976419Ssb155480 		DBG2(vgenp, NULL, "%s(%d): ", vid_propname, inst);
16986419Ssb155480 		vids_size = sizeof (uint16_t) * nvids;
16996419Ssb155480 		*vidspp = kmem_zalloc(vids_size, KM_SLEEP);
17006419Ssb155480 		for (i = 0; i < nvids; i++) {
17016419Ssb155480 			(*vidspp)[i] = data[i] & 0xFFFF;
17026419Ssb155480 			DBG2(vgenp, NULL, " %d ", (*vidspp)[i]);
17036419Ssb155480 		}
17046419Ssb155480 		DBG2(vgenp, NULL, "\n");
17056419Ssb155480 	}
17066419Ssb155480 
17076419Ssb155480 	*nvidsp = nvids;
17086419Ssb155480 }
17096419Ssb155480 
17106419Ssb155480 /*
17116419Ssb155480  * Create a vlan id hash table for the given port.
17126419Ssb155480  */
17136419Ssb155480 static void
17146419Ssb155480 vgen_vlan_create_hash(vgen_port_t *portp)
17156419Ssb155480 {
17166419Ssb155480 	char		hashname[MAXNAMELEN];
17176419Ssb155480 
17186419Ssb155480 	(void) snprintf(hashname, MAXNAMELEN, "port%d-vlan-hash",
17196419Ssb155480 	    portp->port_num);
17206419Ssb155480 
17216419Ssb155480 	portp->vlan_nchains = vgen_vlan_nchains;
17226419Ssb155480 	portp->vlan_hashp = mod_hash_create_idhash(hashname,
17236419Ssb155480 	    portp->vlan_nchains, mod_hash_null_valdtor);
17246419Ssb155480 }
17256419Ssb155480 
17266419Ssb155480 /*
17276419Ssb155480  * Destroy the vlan id hash table in the given port.
17286419Ssb155480  */
17296419Ssb155480 static void
17306419Ssb155480 vgen_vlan_destroy_hash(vgen_port_t *portp)
17316419Ssb155480 {
17326419Ssb155480 	if (portp->vlan_hashp != NULL) {
17336419Ssb155480 		mod_hash_destroy_hash(portp->vlan_hashp);
17346419Ssb155480 		portp->vlan_hashp = NULL;
17356419Ssb155480 		portp->vlan_nchains = 0;
17366419Ssb155480 	}
17376419Ssb155480 }
17386419Ssb155480 
17396419Ssb155480 /*
17406419Ssb155480  * Add a port to the vlans specified in its port properites.
17416419Ssb155480  */
17426419Ssb155480 static void
17436419Ssb155480 vgen_vlan_add_ids(vgen_port_t *portp)
17446419Ssb155480 {
17456419Ssb155480 	int		rv;
17466419Ssb155480 	int		i;
17476419Ssb155480 
17486419Ssb155480 	rv = mod_hash_insert(portp->vlan_hashp,
17496419Ssb155480 	    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
17506419Ssb155480 	    (mod_hash_val_t)B_TRUE);
17516419Ssb155480 	ASSERT(rv == 0);
17526419Ssb155480 
17536419Ssb155480 	for (i = 0; i < portp->nvids; i++) {
17546419Ssb155480 		rv = mod_hash_insert(portp->vlan_hashp,
17556419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]),
17566419Ssb155480 		    (mod_hash_val_t)B_TRUE);
17576419Ssb155480 		ASSERT(rv == 0);
17586419Ssb155480 	}
17596419Ssb155480 }
17606419Ssb155480 
17616419Ssb155480 /*
17626419Ssb155480  * Remove a port from the vlans it has been assigned to.
17636419Ssb155480  */
17646419Ssb155480 static void
17656419Ssb155480 vgen_vlan_remove_ids(vgen_port_t *portp)
17666419Ssb155480 {
17676419Ssb155480 	int		rv;
17686419Ssb155480 	int		i;
17696419Ssb155480 	mod_hash_val_t	vp;
17706419Ssb155480 
17716419Ssb155480 	rv = mod_hash_remove(portp->vlan_hashp,
17726419Ssb155480 	    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
17736419Ssb155480 	    (mod_hash_val_t *)&vp);
17746419Ssb155480 	ASSERT(rv == 0);
17756419Ssb155480 
17766419Ssb155480 	for (i = 0; i < portp->nvids; i++) {
17776419Ssb155480 		rv = mod_hash_remove(portp->vlan_hashp,
17786419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]),
17796419Ssb155480 		    (mod_hash_val_t *)&vp);
17806419Ssb155480 		ASSERT(rv == 0);
17816419Ssb155480 	}
17826419Ssb155480 }
17836419Ssb155480 
17846419Ssb155480 /*
17856419Ssb155480  * Lookup the vlan id of the given tx frame. If it is a vlan-tagged frame,
17866419Ssb155480  * then the vlan-id is available in the tag; otherwise, its vlan id is
17876419Ssb155480  * implicitly obtained from the port-vlan-id of the vnet device.
17886419Ssb155480  * The vlan id determined is returned in vidp.
17896419Ssb155480  * Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
17906419Ssb155480  */
17916419Ssb155480 static boolean_t
17926419Ssb155480 vgen_frame_lookup_vid(vnet_t *vnetp, struct ether_header *ehp, uint16_t *vidp)
17936419Ssb155480 {
17946419Ssb155480 	struct ether_vlan_header	*evhp;
17956419Ssb155480 
17966419Ssb155480 	/* If it's a tagged frame, get the vlan id from vlan header */
17976419Ssb155480 	if (ehp->ether_type == ETHERTYPE_VLAN) {
17986419Ssb155480 
17996419Ssb155480 		evhp = (struct ether_vlan_header *)ehp;
18006419Ssb155480 		*vidp = VLAN_ID(ntohs(evhp->ether_tci));
18016419Ssb155480 		return (B_TRUE);
18026419Ssb155480 	}
18036419Ssb155480 
18046419Ssb155480 	/* Untagged frame, vlan-id is the pvid of vnet device */
18056419Ssb155480 	*vidp = vnetp->pvid;
18066419Ssb155480 	return (B_FALSE);
18076419Ssb155480 }
18086419Ssb155480 
18096419Ssb155480 /*
18106419Ssb155480  * Find the given vlan id in the hash table.
18116419Ssb155480  * Return: B_TRUE if the id is found; B_FALSE if not found.
18126419Ssb155480  */
18136419Ssb155480 static boolean_t
18146419Ssb155480 vgen_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid)
18156419Ssb155480 {
18166419Ssb155480 	int		rv;
18176419Ssb155480 	mod_hash_val_t	vp;
18186419Ssb155480 
18196419Ssb155480 	rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp);
18206419Ssb155480 
18216419Ssb155480 	if (rv != 0)
18226419Ssb155480 		return (B_FALSE);
18236419Ssb155480 
18246419Ssb155480 	return (B_TRUE);
18256419Ssb155480 }
18266419Ssb155480 
18276419Ssb155480 /*
18285935Ssb155480  * This function reads "priority-ether-types" property from md. This property
18295935Ssb155480  * is used to enable support for priority frames. Applications which need
18305935Ssb155480  * guaranteed and timely delivery of certain high priority frames to/from
18315935Ssb155480  * a vnet or vsw within ldoms, should configure this property by providing
18325935Ssb155480  * the ether type(s) for which the priority facility is needed.
18335935Ssb155480  * Normal data frames are delivered over a ldc channel using the descriptor
18345935Ssb155480  * ring mechanism which is constrained by factors such as descriptor ring size,
18355935Ssb155480  * the rate at which the ring is processed at the peer ldc end point, etc.
18365935Ssb155480  * The priority mechanism provides an Out-Of-Band path to send/receive frames
18375935Ssb155480  * as raw pkt data (VIO_PKT_DATA) messages over the channel, avoiding the
18385935Ssb155480  * descriptor ring path and enables a more reliable and timely delivery of
18395935Ssb155480  * frames to the peer.
18405935Ssb155480  */
18415935Ssb155480 static void
18425935Ssb155480 vgen_read_pri_eth_types(vgen_t *vgenp, md_t *mdp, mde_cookie_t node)
18435935Ssb155480 {
18445935Ssb155480 	int		rv;
18455935Ssb155480 	uint16_t	*types;
18465935Ssb155480 	uint64_t	*data;
18475935Ssb155480 	int		size;
18485935Ssb155480 	int		i;
18495935Ssb155480 	size_t		mblk_sz;
18505935Ssb155480 
18515935Ssb155480 	rv = md_get_prop_data(mdp, node, pri_types_propname,
18525935Ssb155480 	    (uint8_t **)&data, &size);
18535935Ssb155480 	if (rv != 0) {
18545935Ssb155480 		/*
18555935Ssb155480 		 * Property may not exist if we are running pre-ldoms1.1 f/w.
18565935Ssb155480 		 * Check if 'vgen_pri_eth_type' has been set in that case.
18575935Ssb155480 		 */
18585935Ssb155480 		if (vgen_pri_eth_type != 0) {
18595935Ssb155480 			size = sizeof (vgen_pri_eth_type);
18605935Ssb155480 			data = &vgen_pri_eth_type;
18615935Ssb155480 		} else {
18626495Sspeer 			DBG2(vgenp, NULL,
18635935Ssb155480 			    "prop(%s) not found", pri_types_propname);
18645935Ssb155480 			size = 0;
18655935Ssb155480 		}
18665935Ssb155480 	}
18675935Ssb155480 
18685935Ssb155480 	if (size == 0) {
18695935Ssb155480 		vgenp->pri_num_types = 0;
18705935Ssb155480 		return;
18715935Ssb155480 	}
18725935Ssb155480 
18735935Ssb155480 	/*
18745935Ssb155480 	 * we have some priority-ether-types defined;
18755935Ssb155480 	 * allocate a table of these types and also
18765935Ssb155480 	 * allocate a pool of mblks to transmit these
18775935Ssb155480 	 * priority packets.
18785935Ssb155480 	 */
18795935Ssb155480 	size /= sizeof (uint64_t);
18805935Ssb155480 	vgenp->pri_num_types = size;
18815935Ssb155480 	vgenp->pri_types = kmem_zalloc(size * sizeof (uint16_t), KM_SLEEP);
18825935Ssb155480 	for (i = 0, types = vgenp->pri_types; i < size; i++) {
18835935Ssb155480 		types[i] = data[i] & 0xFFFF;
18845935Ssb155480 	}
18856419Ssb155480 	mblk_sz = (VIO_PKT_DATA_HDRSIZE + vgenp->max_frame_size + 7) & ~7;
18865935Ssb155480 	(void) vio_create_mblks(vgen_pri_tx_nmblks, mblk_sz,
18875935Ssb155480 	    &vgenp->pri_tx_vmp);
18885935Ssb155480 }
18895935Ssb155480 
18907529SSriharsha.Basavapatna@Sun.COM static void
18917529SSriharsha.Basavapatna@Sun.COM vgen_mtu_read(vgen_t *vgenp, md_t *mdp, mde_cookie_t node, uint32_t *mtu)
18927529SSriharsha.Basavapatna@Sun.COM {
18937529SSriharsha.Basavapatna@Sun.COM 	int		rv;
18947529SSriharsha.Basavapatna@Sun.COM 	uint64_t	val;
18957529SSriharsha.Basavapatna@Sun.COM 	char		*mtu_propname;
18967529SSriharsha.Basavapatna@Sun.COM 
18977529SSriharsha.Basavapatna@Sun.COM 	mtu_propname = vgen_mtu_propname;
18987529SSriharsha.Basavapatna@Sun.COM 
18997529SSriharsha.Basavapatna@Sun.COM 	rv = md_get_prop_val(mdp, node, mtu_propname, &val);
19007529SSriharsha.Basavapatna@Sun.COM 	if (rv != 0) {
19017529SSriharsha.Basavapatna@Sun.COM 		DWARN(vgenp, NULL, "prop(%s) not found", mtu_propname);
19027529SSriharsha.Basavapatna@Sun.COM 		*mtu = vnet_ethermtu;
19037529SSriharsha.Basavapatna@Sun.COM 	} else {
19047529SSriharsha.Basavapatna@Sun.COM 
19057529SSriharsha.Basavapatna@Sun.COM 		*mtu = val & 0xFFFF;
19067529SSriharsha.Basavapatna@Sun.COM 		DBG2(vgenp, NULL, "%s(%d): (%d)\n", mtu_propname,
19077529SSriharsha.Basavapatna@Sun.COM 		    vgenp->instance, *mtu);
19087529SSriharsha.Basavapatna@Sun.COM 	}
19097529SSriharsha.Basavapatna@Sun.COM }
19107529SSriharsha.Basavapatna@Sun.COM 
19111991Sheppo /* register with MD event generator */
19121991Sheppo static int
19131991Sheppo vgen_mdeg_reg(vgen_t *vgenp)
19141991Sheppo {
19151991Sheppo 	mdeg_prop_spec_t	*pspecp;
19161991Sheppo 	mdeg_node_spec_t	*parentp;
19171991Sheppo 	uint_t			templatesz;
19181991Sheppo 	int			rv;
19196419Ssb155480 	mdeg_handle_t		dev_hdl = NULL;
19206419Ssb155480 	mdeg_handle_t		port_hdl = NULL;
19216419Ssb155480 
19221991Sheppo 	templatesz = sizeof (vgen_prop_template);
19231991Sheppo 	pspecp = kmem_zalloc(templatesz, KM_NOSLEEP);
19241991Sheppo 	if (pspecp == NULL) {
19251991Sheppo 		return (DDI_FAILURE);
19261991Sheppo 	}
19271991Sheppo 	parentp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_NOSLEEP);
19281991Sheppo 	if (parentp == NULL) {
19291991Sheppo 		kmem_free(pspecp, templatesz);
19301991Sheppo 		return (DDI_FAILURE);
19311991Sheppo 	}
19321991Sheppo 
19331991Sheppo 	bcopy(vgen_prop_template, pspecp, templatesz);
19341991Sheppo 
19351991Sheppo 	/*
19361991Sheppo 	 * NOTE: The instance here refers to the value of "reg" property and
19371991Sheppo 	 * not the dev_info instance (ddi_get_instance()) of vnet.
19381991Sheppo 	 */
19396419Ssb155480 	VGEN_SET_MDEG_PROP_INST(pspecp, vgenp->regprop);
19401991Sheppo 
19411991Sheppo 	parentp->namep = "virtual-device";
19421991Sheppo 	parentp->specp = pspecp;
19431991Sheppo 
19441991Sheppo 	/* save parentp in vgen_t */
19451991Sheppo 	vgenp->mdeg_parentp = parentp;
19461991Sheppo 
19476419Ssb155480 	/*
19486419Ssb155480 	 * Register an interest in 'virtual-device' nodes with a
19496419Ssb155480 	 * 'name' property of 'network'
19506419Ssb155480 	 */
19516419Ssb155480 	rv = mdeg_register(parentp, &vdev_match, vgen_mdeg_cb, vgenp, &dev_hdl);
19521991Sheppo 	if (rv != MDEG_SUCCESS) {
19534647Sraghuram 		DERR(vgenp, NULL, "mdeg_register failed\n");
19546419Ssb155480 		goto mdeg_reg_fail;
19556419Ssb155480 	}
19566419Ssb155480 
19576419Ssb155480 	/* Register an interest in 'port' nodes */
19586419Ssb155480 	rv = mdeg_register(parentp, &vport_match, vgen_mdeg_port_cb, vgenp,
19596419Ssb155480 	    &port_hdl);
19606419Ssb155480 	if (rv != MDEG_SUCCESS) {
19616419Ssb155480 		DERR(vgenp, NULL, "mdeg_register failed\n");
19626419Ssb155480 		goto mdeg_reg_fail;
19631991Sheppo 	}
19641991Sheppo 
19651991Sheppo 	/* save mdeg handle in vgen_t */
19666419Ssb155480 	vgenp->mdeg_dev_hdl = dev_hdl;
19676419Ssb155480 	vgenp->mdeg_port_hdl = port_hdl;
19681991Sheppo 
19691991Sheppo 	return (DDI_SUCCESS);
19706419Ssb155480 
19716419Ssb155480 mdeg_reg_fail:
19726419Ssb155480 	if (dev_hdl != NULL) {
19736419Ssb155480 		(void) mdeg_unregister(dev_hdl);
19746419Ssb155480 	}
19756419Ssb155480 	KMEM_FREE(parentp);
19766419Ssb155480 	kmem_free(pspecp, templatesz);
19776419Ssb155480 	vgenp->mdeg_parentp = NULL;
19786419Ssb155480 	return (DDI_FAILURE);
19791991Sheppo }
19801991Sheppo 
19811991Sheppo /* unregister with MD event generator */
19821991Sheppo static void
19831991Sheppo vgen_mdeg_unreg(vgen_t *vgenp)
19841991Sheppo {
19856419Ssb155480 	(void) mdeg_unregister(vgenp->mdeg_dev_hdl);
19866419Ssb155480 	(void) mdeg_unregister(vgenp->mdeg_port_hdl);
19873297Ssb155480 	kmem_free(vgenp->mdeg_parentp->specp, sizeof (vgen_prop_template));
19881991Sheppo 	KMEM_FREE(vgenp->mdeg_parentp);
19891991Sheppo 	vgenp->mdeg_parentp = NULL;
19906419Ssb155480 	vgenp->mdeg_dev_hdl = NULL;
19916419Ssb155480 	vgenp->mdeg_port_hdl = NULL;
19921991Sheppo }
19931991Sheppo 
19946419Ssb155480 /* mdeg callback function for the port node */
19951991Sheppo static int
19966419Ssb155480 vgen_mdeg_port_cb(void *cb_argp, mdeg_result_t *resp)
19971991Sheppo {
19981991Sheppo 	int idx;
19991991Sheppo 	int vsw_idx = -1;
20001991Sheppo 	uint64_t val;
20011991Sheppo 	vgen_t *vgenp;
20021991Sheppo 
20031991Sheppo 	if ((resp == NULL) || (cb_argp == NULL)) {
20041991Sheppo 		return (MDEG_FAILURE);
20051991Sheppo 	}
20061991Sheppo 
20071991Sheppo 	vgenp = (vgen_t *)cb_argp;
20084647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
20091991Sheppo 
20101991Sheppo 	mutex_enter(&vgenp->lock);
20111991Sheppo 
20124647Sraghuram 	DBG1(vgenp, NULL, "ports: removed(%x), "
20134647Sraghuram 	"added(%x), updated(%x)\n", resp->removed.nelem,
20144647Sraghuram 	    resp->added.nelem, resp->match_curr.nelem);
20151991Sheppo 
20161991Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
20171991Sheppo 		(void) vgen_remove_port(vgenp, resp->removed.mdp,
20181991Sheppo 		    resp->removed.mdep[idx]);
20191991Sheppo 	}
20201991Sheppo 
20211991Sheppo 	if (vgenp->vsw_portp == NULL) {
20221991Sheppo 		/*
20231991Sheppo 		 * find vsw_port and add it first, because other ports need
20241991Sheppo 		 * this when adding fdb entry (see vgen_port_init()).
20251991Sheppo 		 */
20261991Sheppo 		for (idx = 0; idx < resp->added.nelem; idx++) {
20271991Sheppo 			if (!(md_get_prop_val(resp->added.mdp,
20281991Sheppo 			    resp->added.mdep[idx], swport_propname, &val))) {
20291991Sheppo 				if (val == 0) {
20301991Sheppo 					/*
20311991Sheppo 					 * This port is connected to the
20326419Ssb155480 					 * vsw on service domain.
20331991Sheppo 					 */
20341991Sheppo 					vsw_idx = idx;
20354663Szk194757 					if (vgen_add_port(vgenp,
20361991Sheppo 					    resp->added.mdp,
20374663Szk194757 					    resp->added.mdep[idx]) !=
20384663Szk194757 					    DDI_SUCCESS) {
20394663Szk194757 						cmn_err(CE_NOTE, "vnet%d Could "
20404663Szk194757 						    "not initialize virtual "
20414663Szk194757 						    "switch port.",
20426495Sspeer 						    vgenp->instance);
20434663Szk194757 						mutex_exit(&vgenp->lock);
20444663Szk194757 						return (MDEG_FAILURE);
20454663Szk194757 					}
20461991Sheppo 					break;
20471991Sheppo 				}
20481991Sheppo 			}
20491991Sheppo 		}
20504666Szk194757 		if (vsw_idx == -1) {
20514647Sraghuram 			DWARN(vgenp, NULL, "can't find vsw_port\n");
20524663Szk194757 			mutex_exit(&vgenp->lock);
20531991Sheppo 			return (MDEG_FAILURE);
20541991Sheppo 		}
20551991Sheppo 	}
20561991Sheppo 
20571991Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
20581991Sheppo 		if ((vsw_idx != -1) && (vsw_idx == idx)) /* skip vsw_port */
20591991Sheppo 			continue;
20604663Szk194757 
20614663Szk194757 		/* If this port can't be added just skip it. */
20621991Sheppo 		(void) vgen_add_port(vgenp, resp->added.mdp,
20631991Sheppo 		    resp->added.mdep[idx]);
20641991Sheppo 	}
20651991Sheppo 
20661991Sheppo 	for (idx = 0; idx < resp->match_curr.nelem; idx++) {
20671991Sheppo 		(void) vgen_update_port(vgenp, resp->match_curr.mdp,
20681991Sheppo 		    resp->match_curr.mdep[idx],
20691991Sheppo 		    resp->match_prev.mdp,
20701991Sheppo 		    resp->match_prev.mdep[idx]);
20711991Sheppo 	}
20721991Sheppo 
20731991Sheppo 	mutex_exit(&vgenp->lock);
20744647Sraghuram 	DBG1(vgenp, NULL, "exit\n");
20751991Sheppo 	return (MDEG_SUCCESS);
20761991Sheppo }
20771991Sheppo 
20786419Ssb155480 /* mdeg callback function for the vnet node */
20796419Ssb155480 static int
20806419Ssb155480 vgen_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
20816419Ssb155480 {
20826419Ssb155480 	vgen_t		*vgenp;
20836419Ssb155480 	vnet_t		*vnetp;
20846419Ssb155480 	md_t		*mdp;
20856419Ssb155480 	mde_cookie_t	node;
20866419Ssb155480 	uint64_t	inst;
20876419Ssb155480 	char		*node_name = NULL;
20886419Ssb155480 
20896419Ssb155480 	if ((resp == NULL) || (cb_argp == NULL)) {
20906419Ssb155480 		return (MDEG_FAILURE);
20916419Ssb155480 	}
20926419Ssb155480 
20936419Ssb155480 	vgenp = (vgen_t *)cb_argp;
20946419Ssb155480 	vnetp = vgenp->vnetp;
20956419Ssb155480 
2096*7572SWentao.Yang@Sun.COM 	DBG1(vgenp, NULL, "added %d : removed %d : curr matched %d"
20976419Ssb155480 	    " : prev matched %d", resp->added.nelem, resp->removed.nelem,
20986419Ssb155480 	    resp->match_curr.nelem, resp->match_prev.nelem);
20996419Ssb155480 
21006419Ssb155480 	mutex_enter(&vgenp->lock);
21016419Ssb155480 
21026419Ssb155480 	/*
21036419Ssb155480 	 * We get an initial callback for this node as 'added' after
21046419Ssb155480 	 * registering with mdeg. Note that we would have already gathered
21056419Ssb155480 	 * information about this vnet node by walking MD earlier during attach
21066419Ssb155480 	 * (in vgen_read_mdprops()). So, there is a window where the properties
21076419Ssb155480 	 * of this node might have changed when we get this initial 'added'
21086419Ssb155480 	 * callback. We handle this as if an update occured and invoke the same
21096419Ssb155480 	 * function which handles updates to the properties of this vnet-node
21106419Ssb155480 	 * if any. A non-zero 'match' value indicates that the MD has been
21116419Ssb155480 	 * updated and that a 'network' node is present which may or may not
21126419Ssb155480 	 * have been updated. It is up to the clients to examine their own
21136419Ssb155480 	 * nodes and determine if they have changed.
21146419Ssb155480 	 */
21156419Ssb155480 	if (resp->added.nelem != 0) {
21166419Ssb155480 
21176419Ssb155480 		if (resp->added.nelem != 1) {
21186419Ssb155480 			cmn_err(CE_NOTE, "!vnet%d: number of nodes added "
21196419Ssb155480 			    "invalid: %d\n", vnetp->instance,
21206419Ssb155480 			    resp->added.nelem);
21216419Ssb155480 			goto vgen_mdeg_cb_err;
21226419Ssb155480 		}
21236419Ssb155480 
21246419Ssb155480 		mdp = resp->added.mdp;
21256419Ssb155480 		node = resp->added.mdep[0];
21266419Ssb155480 
21276419Ssb155480 	} else if (resp->match_curr.nelem != 0) {
21286419Ssb155480 
21296419Ssb155480 		if (resp->match_curr.nelem != 1) {
21306419Ssb155480 			cmn_err(CE_NOTE, "!vnet%d: number of nodes updated "
21316419Ssb155480 			    "invalid: %d\n", vnetp->instance,
21326419Ssb155480 			    resp->match_curr.nelem);
21336419Ssb155480 			goto vgen_mdeg_cb_err;
21346419Ssb155480 		}
21356419Ssb155480 
21366419Ssb155480 		mdp = resp->match_curr.mdp;
21376419Ssb155480 		node = resp->match_curr.mdep[0];
21386419Ssb155480 
21396419Ssb155480 	} else {
21406419Ssb155480 		goto vgen_mdeg_cb_err;
21416419Ssb155480 	}
21426419Ssb155480 
21436419Ssb155480 	/* Validate name and instance */
21446419Ssb155480 	if (md_get_prop_str(mdp, node, "name", &node_name) != 0) {
21456419Ssb155480 		DERR(vgenp, NULL, "unable to get node name\n");
21466419Ssb155480 		goto vgen_mdeg_cb_err;
21476419Ssb155480 	}
21486419Ssb155480 
21496419Ssb155480 	/* is this a virtual-network device? */
21506419Ssb155480 	if (strcmp(node_name, vnet_propname) != 0) {
21516419Ssb155480 		DERR(vgenp, NULL, "%s: Invalid node name: %s\n", node_name);
21526419Ssb155480 		goto vgen_mdeg_cb_err;
21536419Ssb155480 	}
21546419Ssb155480 
21556419Ssb155480 	if (md_get_prop_val(mdp, node, "cfg-handle", &inst)) {
21566419Ssb155480 		DERR(vgenp, NULL, "prop(cfg-handle) not found\n");
21576419Ssb155480 		goto vgen_mdeg_cb_err;
21586419Ssb155480 	}
21596419Ssb155480 
21606495Sspeer 	/* is this the right instance of vnet? */
21616419Ssb155480 	if (inst != vgenp->regprop) {
21626419Ssb155480 		DERR(vgenp, NULL,  "Invalid cfg-handle: %lx\n", inst);
21636419Ssb155480 		goto vgen_mdeg_cb_err;
21646419Ssb155480 	}
21656419Ssb155480 
21666419Ssb155480 	vgen_update_md_prop(vgenp, mdp, node);
21676419Ssb155480 
21686419Ssb155480 	mutex_exit(&vgenp->lock);
21696419Ssb155480 	return (MDEG_SUCCESS);
21706419Ssb155480 
21716419Ssb155480 vgen_mdeg_cb_err:
21726419Ssb155480 	mutex_exit(&vgenp->lock);
21736419Ssb155480 	return (MDEG_FAILURE);
21746419Ssb155480 }
21756419Ssb155480 
21766419Ssb155480 /*
21776419Ssb155480  * Check to see if the relevant properties in the specified node have
21786419Ssb155480  * changed, and if so take the appropriate action.
21796419Ssb155480  */
21806419Ssb155480 static void
21816419Ssb155480 vgen_update_md_prop(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex)
21826419Ssb155480 {
21836419Ssb155480 	uint16_t	pvid;
21846419Ssb155480 	uint16_t	*vids;
21856419Ssb155480 	uint16_t	nvids;
21866419Ssb155480 	vnet_t		*vnetp = vgenp->vnetp;
21877529SSriharsha.Basavapatna@Sun.COM 	uint32_t	mtu;
21887529SSriharsha.Basavapatna@Sun.COM 	enum		{ MD_init = 0x1,
21897529SSriharsha.Basavapatna@Sun.COM 			    MD_vlans = 0x2,
21907529SSriharsha.Basavapatna@Sun.COM 			    MD_mtu = 0x4 } updated;
21917529SSriharsha.Basavapatna@Sun.COM 	int		rv;
21927529SSriharsha.Basavapatna@Sun.COM 
21937529SSriharsha.Basavapatna@Sun.COM 	updated = MD_init;
21946419Ssb155480 
21956419Ssb155480 	/* Read the vlan ids */
21966419Ssb155480 	vgen_vlan_read_ids(vgenp, VGEN_LOCAL, mdp, mdex, &pvid, &vids,
21976419Ssb155480 	    &nvids, NULL);
21986419Ssb155480 
21996419Ssb155480 	/* Determine if there are any vlan id updates */
22006419Ssb155480 	if ((pvid != vnetp->pvid) ||		/* pvid changed? */
22016419Ssb155480 	    (nvids != vnetp->nvids) ||		/* # of vids changed? */
22026419Ssb155480 	    ((nvids != 0) && (vnetp->nvids != 0) &&	/* vids changed? */
22036419Ssb155480 	    bcmp(vids, vnetp->vids, sizeof (uint16_t) * nvids))) {
22047529SSriharsha.Basavapatna@Sun.COM 		updated |= MD_vlans;
22057529SSriharsha.Basavapatna@Sun.COM 	}
22067529SSriharsha.Basavapatna@Sun.COM 
22077529SSriharsha.Basavapatna@Sun.COM 	/* Read mtu */
22087529SSriharsha.Basavapatna@Sun.COM 	vgen_mtu_read(vgenp, mdp, mdex, &mtu);
22097529SSriharsha.Basavapatna@Sun.COM 	if (mtu != vnetp->mtu) {
22107529SSriharsha.Basavapatna@Sun.COM 		if (mtu >= ETHERMTU && mtu <= VNET_MAX_MTU) {
22117529SSriharsha.Basavapatna@Sun.COM 			updated |= MD_mtu;
22127529SSriharsha.Basavapatna@Sun.COM 		} else {
22137529SSriharsha.Basavapatna@Sun.COM 			cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu update"
22147529SSriharsha.Basavapatna@Sun.COM 			    " as the specified value:%d is invalid\n",
22157529SSriharsha.Basavapatna@Sun.COM 			    vnetp->instance, mtu);
22167529SSriharsha.Basavapatna@Sun.COM 		}
22177529SSriharsha.Basavapatna@Sun.COM 	}
22187529SSriharsha.Basavapatna@Sun.COM 
22197529SSriharsha.Basavapatna@Sun.COM 	/* Now process the updated props */
22207529SSriharsha.Basavapatna@Sun.COM 
22217529SSriharsha.Basavapatna@Sun.COM 	if (updated & MD_vlans) {
22227529SSriharsha.Basavapatna@Sun.COM 
22237529SSriharsha.Basavapatna@Sun.COM 		/* save the new vlan ids */
22247529SSriharsha.Basavapatna@Sun.COM 		vnetp->pvid = pvid;
22257529SSriharsha.Basavapatna@Sun.COM 		if (vnetp->nvids != 0) {
22267529SSriharsha.Basavapatna@Sun.COM 			kmem_free(vnetp->vids,
22277529SSriharsha.Basavapatna@Sun.COM 			    sizeof (uint16_t) * vnetp->nvids);
22287529SSriharsha.Basavapatna@Sun.COM 			vnetp->nvids = 0;
22297529SSriharsha.Basavapatna@Sun.COM 		}
22307529SSriharsha.Basavapatna@Sun.COM 		if (nvids != 0) {
22317529SSriharsha.Basavapatna@Sun.COM 			vnetp->nvids = nvids;
22327529SSriharsha.Basavapatna@Sun.COM 			vnetp->vids = vids;
22337529SSriharsha.Basavapatna@Sun.COM 		}
22347529SSriharsha.Basavapatna@Sun.COM 
22357529SSriharsha.Basavapatna@Sun.COM 		/* reset vlan-unaware peers (ver < 1.3) and restart handshake */
22367529SSriharsha.Basavapatna@Sun.COM 		vgen_reset_vlan_unaware_ports(vgenp);
22377529SSriharsha.Basavapatna@Sun.COM 
22387529SSriharsha.Basavapatna@Sun.COM 	} else {
22397529SSriharsha.Basavapatna@Sun.COM 
22406419Ssb155480 		if (nvids != 0) {
22416419Ssb155480 			kmem_free(vids, sizeof (uint16_t) * nvids);
22426419Ssb155480 		}
22437529SSriharsha.Basavapatna@Sun.COM 	}
22447529SSriharsha.Basavapatna@Sun.COM 
22457529SSriharsha.Basavapatna@Sun.COM 	if (updated & MD_mtu) {
22467529SSriharsha.Basavapatna@Sun.COM 
22477529SSriharsha.Basavapatna@Sun.COM 		DBG2(vgenp, NULL, "curr_mtu(%d) new_mtu(%d)\n",
22487529SSriharsha.Basavapatna@Sun.COM 		    vnetp->mtu, mtu);
22497529SSriharsha.Basavapatna@Sun.COM 
22507529SSriharsha.Basavapatna@Sun.COM 		rv = vnet_mtu_update(vnetp, mtu);
22517529SSriharsha.Basavapatna@Sun.COM 		if (rv == 0) {
22527529SSriharsha.Basavapatna@Sun.COM 			vgenp->max_frame_size = mtu +
22537529SSriharsha.Basavapatna@Sun.COM 			    sizeof (struct ether_header) + VLAN_TAGSZ;
22547529SSriharsha.Basavapatna@Sun.COM 		}
22557529SSriharsha.Basavapatna@Sun.COM 	}
22566419Ssb155480 }
22576419Ssb155480 
22581991Sheppo /* add a new port to the device */
22591991Sheppo static int
22601991Sheppo vgen_add_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex)
22611991Sheppo {
22626419Ssb155480 	vgen_port_t	*portp;
22636419Ssb155480 	int		rv;
22646419Ssb155480 
22656419Ssb155480 	portp = kmem_zalloc(sizeof (vgen_port_t), KM_SLEEP);
22666419Ssb155480 
22676419Ssb155480 	rv = vgen_port_read_props(portp, vgenp, mdp, mdex);
22686419Ssb155480 	if (rv != DDI_SUCCESS) {
22696419Ssb155480 		KMEM_FREE(portp);
22706419Ssb155480 		return (DDI_FAILURE);
22716419Ssb155480 	}
22726419Ssb155480 
22736419Ssb155480 	rv = vgen_port_attach(portp);
22746419Ssb155480 	if (rv != DDI_SUCCESS) {
22756419Ssb155480 		return (DDI_FAILURE);
22766419Ssb155480 	}
22776419Ssb155480 
22786419Ssb155480 	return (DDI_SUCCESS);
22796419Ssb155480 }
22806419Ssb155480 
22816419Ssb155480 /* read properties of the port from its md node */
22826419Ssb155480 static int
22836419Ssb155480 vgen_port_read_props(vgen_port_t *portp, vgen_t *vgenp, md_t *mdp,
22846419Ssb155480 	mde_cookie_t mdex)
22856419Ssb155480 {
22866419Ssb155480 	uint64_t		port_num;
22876419Ssb155480 	uint64_t		*ldc_ids;
22886419Ssb155480 	uint64_t		macaddr;
22896419Ssb155480 	uint64_t		val;
22906419Ssb155480 	int			num_ldcs;
22916419Ssb155480 	int			i;
22926419Ssb155480 	int			addrsz;
22936419Ssb155480 	int			num_nodes = 0;
22946419Ssb155480 	int			listsz = 0;
22956419Ssb155480 	mde_cookie_t		*listp = NULL;
22966419Ssb155480 	uint8_t			*addrp;
22971991Sheppo 	struct ether_addr	ea;
22981991Sheppo 
22991991Sheppo 	/* read "id" property to get the port number */
23001991Sheppo 	if (md_get_prop_val(mdp, mdex, id_propname, &port_num)) {
23014647Sraghuram 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
23021991Sheppo 		return (DDI_FAILURE);
23031991Sheppo 	}
23041991Sheppo 
23051991Sheppo 	/*
23061991Sheppo 	 * Find the channel endpoint node(s) under this port node.
23071991Sheppo 	 */
23081991Sheppo 	if ((num_nodes = md_node_count(mdp)) <= 0) {
23094647Sraghuram 		DWARN(vgenp, NULL, "invalid number of nodes found (%d)",
23104647Sraghuram 		    num_nodes);
23111991Sheppo 		return (DDI_FAILURE);
23121991Sheppo 	}
23131991Sheppo 
23141991Sheppo 	/* allocate space for node list */
23151991Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
23161991Sheppo 	listp = kmem_zalloc(listsz, KM_NOSLEEP);
23171991Sheppo 	if (listp == NULL)
23181991Sheppo 		return (DDI_FAILURE);
23191991Sheppo 
23201991Sheppo 	num_ldcs = md_scan_dag(mdp, mdex,
23214650Sraghuram 	    md_find_name(mdp, channel_propname),
23224650Sraghuram 	    md_find_name(mdp, "fwd"), listp);
23231991Sheppo 
23241991Sheppo 	if (num_ldcs <= 0) {
23254647Sraghuram 		DWARN(vgenp, NULL, "can't find %s nodes", channel_propname);
23261991Sheppo 		kmem_free(listp, listsz);
23271991Sheppo 		return (DDI_FAILURE);
23281991Sheppo 	}
23291991Sheppo 
23304647Sraghuram 	DBG2(vgenp, NULL, "num_ldcs %d", num_ldcs);
23311991Sheppo 
23321991Sheppo 	ldc_ids = kmem_zalloc(num_ldcs * sizeof (uint64_t), KM_NOSLEEP);
23331991Sheppo 	if (ldc_ids == NULL) {
23341991Sheppo 		kmem_free(listp, listsz);
23351991Sheppo 		return (DDI_FAILURE);
23361991Sheppo 	}
23371991Sheppo 
23381991Sheppo 	for (i = 0; i < num_ldcs; i++) {
23391991Sheppo 		/* read channel ids */
23401991Sheppo 		if (md_get_prop_val(mdp, listp[i], id_propname, &ldc_ids[i])) {
23414647Sraghuram 			DWARN(vgenp, NULL, "prop(%s) not found\n",
23424647Sraghuram 			    id_propname);
23431991Sheppo 			kmem_free(listp, listsz);
23441991Sheppo 			kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
23451991Sheppo 			return (DDI_FAILURE);
23461991Sheppo 		}
23474647Sraghuram 		DBG2(vgenp, NULL, "ldc_id 0x%llx", ldc_ids[i]);
23481991Sheppo 	}
23491991Sheppo 
23501991Sheppo 	kmem_free(listp, listsz);
23511991Sheppo 
23521991Sheppo 	if (md_get_prop_data(mdp, mdex, rmacaddr_propname, &addrp,
23531991Sheppo 	    &addrsz)) {
23544647Sraghuram 		DWARN(vgenp, NULL, "prop(%s) not found\n", rmacaddr_propname);
23551991Sheppo 		kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
23561991Sheppo 		return (DDI_FAILURE);
23571991Sheppo 	}
23581991Sheppo 
23591991Sheppo 	if (addrsz < ETHERADDRL) {
23604647Sraghuram 		DWARN(vgenp, NULL, "invalid address size (%d)\n", addrsz);
23611991Sheppo 		kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
23621991Sheppo 		return (DDI_FAILURE);
23631991Sheppo 	}
23641991Sheppo 
23651991Sheppo 	macaddr = *((uint64_t *)addrp);
23661991Sheppo 
23674647Sraghuram 	DBG2(vgenp, NULL, "remote mac address 0x%llx\n", macaddr);
23681991Sheppo 
23691991Sheppo 	for (i = ETHERADDRL - 1; i >= 0; i--) {
23701991Sheppo 		ea.ether_addr_octet[i] = macaddr & 0xFF;
23711991Sheppo 		macaddr >>= 8;
23721991Sheppo 	}
23731991Sheppo 
23741991Sheppo 	if (vgenp->vsw_portp == NULL) {
23751991Sheppo 		if (!(md_get_prop_val(mdp, mdex, swport_propname, &val))) {
23761991Sheppo 			if (val == 0) {
23776495Sspeer 				(void) atomic_swap_32(
23786495Sspeer 				    &vgenp->vsw_port_refcnt, 0);
23796419Ssb155480 				/* This port is connected to the vsw */
23806419Ssb155480 				vgenp->vsw_portp = portp;
23811991Sheppo 			}
23821991Sheppo 		}
23831991Sheppo 	}
23846419Ssb155480 
23856419Ssb155480 	/* now update all properties into the port */
23866419Ssb155480 	portp->vgenp = vgenp;
23876419Ssb155480 	portp->port_num = port_num;
23886419Ssb155480 	ether_copy(&ea, &portp->macaddr);
23896419Ssb155480 	portp->ldc_ids = kmem_zalloc(sizeof (uint64_t) * num_ldcs, KM_SLEEP);
23906419Ssb155480 	bcopy(ldc_ids, portp->ldc_ids, sizeof (uint64_t) * num_ldcs);
23916419Ssb155480 	portp->num_ldcs = num_ldcs;
23926419Ssb155480 
23936419Ssb155480 	/* read vlan id properties of this port node */
23946419Ssb155480 	vgen_vlan_read_ids(portp, VGEN_PEER, mdp, mdex, &portp->pvid,
23956419Ssb155480 	    &portp->vids, &portp->nvids, NULL);
23961991Sheppo 
23971991Sheppo 	kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
23981991Sheppo 
23996419Ssb155480 	return (DDI_SUCCESS);
24001991Sheppo }
24011991Sheppo 
24021991Sheppo /* remove a port from the device */
24031991Sheppo static int
24041991Sheppo vgen_remove_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex)
24051991Sheppo {
24061991Sheppo 	uint64_t	port_num;
24071991Sheppo 	vgen_port_t	*portp;
24081991Sheppo 	vgen_portlist_t	*plistp;
24091991Sheppo 
24101991Sheppo 	/* read "id" property to get the port number */
24111991Sheppo 	if (md_get_prop_val(mdp, mdex, id_propname, &port_num)) {
24124647Sraghuram 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
24131991Sheppo 		return (DDI_FAILURE);
24141991Sheppo 	}
24151991Sheppo 
24161991Sheppo 	plistp = &(vgenp->vgenports);
24171991Sheppo 
24181991Sheppo 	WRITE_ENTER(&plistp->rwlock);
24191991Sheppo 	portp = vgen_port_lookup(plistp, (int)port_num);
24201991Sheppo 	if (portp == NULL) {
24214647Sraghuram 		DWARN(vgenp, NULL, "can't find port(%lx)\n", port_num);
24221991Sheppo 		RW_EXIT(&plistp->rwlock);
24231991Sheppo 		return (DDI_FAILURE);
24241991Sheppo 	}
24251991Sheppo 
24261991Sheppo 	vgen_port_detach_mdeg(portp);
24271991Sheppo 	RW_EXIT(&plistp->rwlock);
24281991Sheppo 
24291991Sheppo 	return (DDI_SUCCESS);
24301991Sheppo }
24311991Sheppo 
24321991Sheppo /* attach a port to the device based on mdeg data */
24331991Sheppo static int
24346419Ssb155480 vgen_port_attach(vgen_port_t *portp)
24351991Sheppo {
24361991Sheppo 	int			i;
24376419Ssb155480 	vgen_portlist_t		*plistp;
24386419Ssb155480 	vgen_t			*vgenp;
24396419Ssb155480 	uint64_t		*ldcids;
24406419Ssb155480 	uint32_t		num_ldcs;
24416495Sspeer 	mac_register_t		*macp;
24426495Sspeer 	vio_net_res_type_t	type;
24436495Sspeer 	int			rv;
24446419Ssb155480 
24456419Ssb155480 	ASSERT(portp != NULL);
24466419Ssb155480 
24476419Ssb155480 	vgenp = portp->vgenp;
24486419Ssb155480 	ldcids = portp->ldc_ids;
24496419Ssb155480 	num_ldcs = portp->num_ldcs;
24501991Sheppo 
24514647Sraghuram 	DBG1(vgenp, NULL, "port_num(%d)\n", portp->port_num);
24521991Sheppo 
24536495Sspeer 	mutex_init(&portp->lock, NULL, MUTEX_DRIVER, NULL);
24546419Ssb155480 	rw_init(&portp->ldclist.rwlock, NULL, RW_DRIVER, NULL);
24551991Sheppo 	portp->ldclist.headp = NULL;
24566419Ssb155480 
24576419Ssb155480 	for (i = 0; i < num_ldcs; i++) {
24584647Sraghuram 		DBG2(vgenp, NULL, "ldcid (%lx)\n", ldcids[i]);
24594663Szk194757 		if (vgen_ldc_attach(portp, ldcids[i]) == DDI_FAILURE) {
24604663Szk194757 			vgen_port_detach(portp);
24614663Szk194757 			return (DDI_FAILURE);
24624663Szk194757 		}
24631991Sheppo 	}
24641991Sheppo 
24656419Ssb155480 	/* create vlan id hash table */
24666419Ssb155480 	vgen_vlan_create_hash(portp);
24676419Ssb155480 
24686495Sspeer 	if (portp == vgenp->vsw_portp) {
24696495Sspeer 		/* This port is connected to the switch port */
24706495Sspeer 		vgenp->vsw_portp = portp;
24716495Sspeer 		(void) atomic_swap_32(&portp->use_vsw_port, B_FALSE);
24726495Sspeer 		type = VIO_NET_RES_LDC_SERVICE;
24736495Sspeer 	} else {
24746495Sspeer 		(void) atomic_swap_32(&portp->use_vsw_port, B_TRUE);
24756495Sspeer 		type = VIO_NET_RES_LDC_GUEST;
24766495Sspeer 	}
24776495Sspeer 
24786495Sspeer 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
24796495Sspeer 		vgen_port_detach(portp);
24806495Sspeer 		return (DDI_FAILURE);
24816495Sspeer 	}
24826495Sspeer 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
24836495Sspeer 	macp->m_driver = portp;
24846495Sspeer 	macp->m_dip = vgenp->vnetdip;
24856495Sspeer 	macp->m_src_addr = (uint8_t *)&(vgenp->macaddr);
24866495Sspeer 	macp->m_callbacks = &vgen_m_callbacks;
24876495Sspeer 	macp->m_min_sdu = 0;
24886495Sspeer 	macp->m_max_sdu = ETHERMTU;
24896495Sspeer 
24906495Sspeer 	mutex_enter(&portp->lock);
24916495Sspeer 	rv = vio_net_resource_reg(macp, type, vgenp->macaddr,
24926495Sspeer 	    portp->macaddr, &portp->vhp, &portp->vcb);
24936495Sspeer 	mutex_exit(&portp->lock);
24946495Sspeer 	mac_free(macp);
24956495Sspeer 
24966495Sspeer 	if (rv == 0) {
24976495Sspeer 		/* link it into the list of ports */
24986495Sspeer 		plistp = &(vgenp->vgenports);
24996495Sspeer 		WRITE_ENTER(&plistp->rwlock);
25006495Sspeer 		vgen_port_list_insert(portp);
25016495Sspeer 		RW_EXIT(&plistp->rwlock);
25026495Sspeer 	} else {
25036495Sspeer 		DERR(vgenp, NULL, "vio_net_resource_reg failed for portp=0x%p",
25046495Sspeer 		    portp);
25056495Sspeer 		vgen_port_detach(portp);
25061991Sheppo 	}
25071991Sheppo 
25084647Sraghuram 	DBG1(vgenp, NULL, "exit: port_num(%d)\n", portp->port_num);
25091991Sheppo 	return (DDI_SUCCESS);
25101991Sheppo }
25111991Sheppo 
25121991Sheppo /* detach a port from the device based on mdeg data */
25131991Sheppo static void
25141991Sheppo vgen_port_detach_mdeg(vgen_port_t *portp)
25151991Sheppo {
25161991Sheppo 	vgen_t *vgenp = portp->vgenp;
25171991Sheppo 
25184647Sraghuram 	DBG1(vgenp, NULL, "enter: port_num(%d)\n", portp->port_num);
25196495Sspeer 
25206495Sspeer 	mutex_enter(&portp->lock);
25216495Sspeer 
25221991Sheppo 	/* stop the port if needed */
25236495Sspeer 	if (portp->flags & VGEN_STARTED) {
25241991Sheppo 		vgen_port_uninit(portp);
25251991Sheppo 	}
25266495Sspeer 
25276495Sspeer 	mutex_exit(&portp->lock);
25281991Sheppo 	vgen_port_detach(portp);
25291991Sheppo 
25304647Sraghuram 	DBG1(vgenp, NULL, "exit: port_num(%d)\n", portp->port_num);
25311991Sheppo }
25321991Sheppo 
25331991Sheppo static int
25341991Sheppo vgen_update_port(vgen_t *vgenp, md_t *curr_mdp, mde_cookie_t curr_mdex,
25351991Sheppo 	md_t *prev_mdp, mde_cookie_t prev_mdex)
25361991Sheppo {
25376419Ssb155480 	uint64_t	cport_num;
25386419Ssb155480 	uint64_t	pport_num;
25396419Ssb155480 	vgen_portlist_t	*plistp;
25406419Ssb155480 	vgen_port_t	*portp;
25416419Ssb155480 	boolean_t	updated_vlans = B_FALSE;
25426419Ssb155480 	uint16_t	pvid;
25436419Ssb155480 	uint16_t	*vids;
25446419Ssb155480 	uint16_t	nvids;
25456419Ssb155480 
25466419Ssb155480 	/*
25476419Ssb155480 	 * For now, we get port updates only if vlan ids changed.
25486419Ssb155480 	 * We read the port num and do some sanity check.
25496419Ssb155480 	 */
25506419Ssb155480 	if (md_get_prop_val(curr_mdp, curr_mdex, id_propname, &cport_num)) {
25516419Ssb155480 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
25526419Ssb155480 		return (DDI_FAILURE);
25536419Ssb155480 	}
25546419Ssb155480 
25556419Ssb155480 	if (md_get_prop_val(prev_mdp, prev_mdex, id_propname, &pport_num)) {
25566419Ssb155480 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
25576419Ssb155480 		return (DDI_FAILURE);
25586419Ssb155480 	}
25596419Ssb155480 	if (cport_num != pport_num)
25606419Ssb155480 		return (DDI_FAILURE);
25616419Ssb155480 
25626419Ssb155480 	plistp = &(vgenp->vgenports);
25636419Ssb155480 
25646419Ssb155480 	READ_ENTER(&plistp->rwlock);
25656419Ssb155480 
25666419Ssb155480 	portp = vgen_port_lookup(plistp, (int)cport_num);
25676419Ssb155480 	if (portp == NULL) {
25686419Ssb155480 		DWARN(vgenp, NULL, "can't find port(%lx)\n", cport_num);
25696419Ssb155480 		RW_EXIT(&plistp->rwlock);
25706419Ssb155480 		return (DDI_FAILURE);
25716419Ssb155480 	}
25726419Ssb155480 
25736419Ssb155480 	/* Read the vlan ids */
25746419Ssb155480 	vgen_vlan_read_ids(portp, VGEN_PEER, curr_mdp, curr_mdex, &pvid, &vids,
25756419Ssb155480 	    &nvids, NULL);
25766419Ssb155480 
25776419Ssb155480 	/* Determine if there are any vlan id updates */
25786419Ssb155480 	if ((pvid != portp->pvid) ||		/* pvid changed? */
25796419Ssb155480 	    (nvids != portp->nvids) ||		/* # of vids changed? */
25806419Ssb155480 	    ((nvids != 0) && (portp->nvids != 0) &&	/* vids changed? */
25816419Ssb155480 	    bcmp(vids, portp->vids, sizeof (uint16_t) * nvids))) {
25826419Ssb155480 		updated_vlans = B_TRUE;
25836419Ssb155480 	}
25846419Ssb155480 
25856419Ssb155480 	if (updated_vlans == B_FALSE) {
25866419Ssb155480 		RW_EXIT(&plistp->rwlock);
25876419Ssb155480 		return (DDI_FAILURE);
25886419Ssb155480 	}
25896419Ssb155480 
25906419Ssb155480 	/* remove the port from vlans it has been assigned to */
25916419Ssb155480 	vgen_vlan_remove_ids(portp);
25926419Ssb155480 
25936419Ssb155480 	/* save the new vlan ids */
25946419Ssb155480 	portp->pvid = pvid;
25956419Ssb155480 	if (portp->nvids != 0) {
25966419Ssb155480 		kmem_free(portp->vids, sizeof (uint16_t) * portp->nvids);
25976419Ssb155480 		portp->nvids = 0;
25986419Ssb155480 	}
25996419Ssb155480 	if (nvids != 0) {
26006419Ssb155480 		portp->vids = kmem_zalloc(sizeof (uint16_t) * nvids, KM_SLEEP);
26016419Ssb155480 		bcopy(vids, portp->vids, sizeof (uint16_t) * nvids);
26026419Ssb155480 		portp->nvids = nvids;
26036419Ssb155480 		kmem_free(vids, sizeof (uint16_t) * nvids);
26046419Ssb155480 	}
26056419Ssb155480 
26066419Ssb155480 	/* add port to the new vlans */
26076419Ssb155480 	vgen_vlan_add_ids(portp);
26086419Ssb155480 
26096419Ssb155480 	/* reset the port if it is vlan unaware (ver < 1.3) */
26106419Ssb155480 	vgen_vlan_unaware_port_reset(portp);
26116419Ssb155480 
26126419Ssb155480 	RW_EXIT(&plistp->rwlock);
26136419Ssb155480 
26141991Sheppo 	return (DDI_SUCCESS);
26151991Sheppo }
26161991Sheppo 
26171991Sheppo static uint64_t
26182311Sseb vgen_port_stat(vgen_port_t *portp, uint_t stat)
26191991Sheppo {
26201991Sheppo 	vgen_ldclist_t	*ldclp;
26211991Sheppo 	vgen_ldc_t *ldcp;
26221991Sheppo 	uint64_t	val;
26231991Sheppo 
26241991Sheppo 	val = 0;
26251991Sheppo 	ldclp = &portp->ldclist;
26261991Sheppo 
26271991Sheppo 	READ_ENTER(&ldclp->rwlock);
26281991Sheppo 	for (ldcp = ldclp->headp; ldcp != NULL; ldcp = ldcp->nextp) {
26291991Sheppo 		val += vgen_ldc_stat(ldcp, stat);
26301991Sheppo 	}
26311991Sheppo 	RW_EXIT(&ldclp->rwlock);
26321991Sheppo 
26331991Sheppo 	return (val);
26341991Sheppo }
26351991Sheppo 
26367529SSriharsha.Basavapatna@Sun.COM /* allocate receive resources */
26377529SSriharsha.Basavapatna@Sun.COM static int
26387529SSriharsha.Basavapatna@Sun.COM vgen_init_multipools(vgen_ldc_t *ldcp)
26397529SSriharsha.Basavapatna@Sun.COM {
26407529SSriharsha.Basavapatna@Sun.COM 	size_t		data_sz;
26417529SSriharsha.Basavapatna@Sun.COM 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
26427529SSriharsha.Basavapatna@Sun.COM 	int		status;
26437529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz1 = 0;
26447529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz2 = 0;
26457529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz3 = 0;
26467529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz4 = 0;
26477529SSriharsha.Basavapatna@Sun.COM 
26487529SSriharsha.Basavapatna@Sun.COM 	/*
26497529SSriharsha.Basavapatna@Sun.COM 	 * We round up the mtu specified to be a multiple of 2K.
26507529SSriharsha.Basavapatna@Sun.COM 	 * We then create rx pools based on the rounded up size.
26517529SSriharsha.Basavapatna@Sun.COM 	 */
26527529SSriharsha.Basavapatna@Sun.COM 	data_sz = vgenp->max_frame_size + VNET_IPALIGN + VNET_LDCALIGN;
26537529SSriharsha.Basavapatna@Sun.COM 	data_sz = VNET_ROUNDUP_2K(data_sz);
26547529SSriharsha.Basavapatna@Sun.COM 
26557529SSriharsha.Basavapatna@Sun.COM 	/*
26567529SSriharsha.Basavapatna@Sun.COM 	 * If pool sizes are specified, use them. Note that the presence of
26577529SSriharsha.Basavapatna@Sun.COM 	 * the first tunable will be used as a hint.
26587529SSriharsha.Basavapatna@Sun.COM 	 */
26597529SSriharsha.Basavapatna@Sun.COM 	if (vgen_rbufsz1 != 0) {
26607529SSriharsha.Basavapatna@Sun.COM 
26617529SSriharsha.Basavapatna@Sun.COM 		sz1 = vgen_rbufsz1;
26627529SSriharsha.Basavapatna@Sun.COM 		sz2 = vgen_rbufsz2;
26637529SSriharsha.Basavapatna@Sun.COM 		sz3 = vgen_rbufsz3;
26647529SSriharsha.Basavapatna@Sun.COM 		sz4 = vgen_rbufsz4;
26657529SSriharsha.Basavapatna@Sun.COM 
26667529SSriharsha.Basavapatna@Sun.COM 		if (sz4 == 0) { /* need 3 pools */
26677529SSriharsha.Basavapatna@Sun.COM 
26687529SSriharsha.Basavapatna@Sun.COM 			ldcp->max_rxpool_size = sz3;
26697529SSriharsha.Basavapatna@Sun.COM 			status = vio_init_multipools(&ldcp->vmp,
26707529SSriharsha.Basavapatna@Sun.COM 			    VGEN_NUM_VMPOOLS, sz1, sz2, sz3, vgen_nrbufs1,
26717529SSriharsha.Basavapatna@Sun.COM 			    vgen_nrbufs2, vgen_nrbufs3);
26727529SSriharsha.Basavapatna@Sun.COM 
26737529SSriharsha.Basavapatna@Sun.COM 		} else {
26747529SSriharsha.Basavapatna@Sun.COM 
26757529SSriharsha.Basavapatna@Sun.COM 			ldcp->max_rxpool_size = sz4;
26767529SSriharsha.Basavapatna@Sun.COM 			status = vio_init_multipools(&ldcp->vmp,
26777529SSriharsha.Basavapatna@Sun.COM 			    VGEN_NUM_VMPOOLS + 1, sz1, sz2, sz3, sz4,
26787529SSriharsha.Basavapatna@Sun.COM 			    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3,
26797529SSriharsha.Basavapatna@Sun.COM 			    vgen_nrbufs4);
26807529SSriharsha.Basavapatna@Sun.COM 		}
26817529SSriharsha.Basavapatna@Sun.COM 		return (status);
26827529SSriharsha.Basavapatna@Sun.COM 	}
26837529SSriharsha.Basavapatna@Sun.COM 
26847529SSriharsha.Basavapatna@Sun.COM 	/*
26857529SSriharsha.Basavapatna@Sun.COM 	 * Pool sizes are not specified. We select the pool sizes based on the
26867529SSriharsha.Basavapatna@Sun.COM 	 * mtu if vnet_jumbo_rxpools is enabled.
26877529SSriharsha.Basavapatna@Sun.COM 	 */
26887529SSriharsha.Basavapatna@Sun.COM 	if (vnet_jumbo_rxpools == B_FALSE || data_sz == VNET_2K) {
26897529SSriharsha.Basavapatna@Sun.COM 		/*
26907529SSriharsha.Basavapatna@Sun.COM 		 * Receive buffer pool allocation based on mtu is disabled.
26917529SSriharsha.Basavapatna@Sun.COM 		 * Use the default mechanism of standard size pool allocation.
26927529SSriharsha.Basavapatna@Sun.COM 		 */
26937529SSriharsha.Basavapatna@Sun.COM 		sz1 = VGEN_DBLK_SZ_128;
26947529SSriharsha.Basavapatna@Sun.COM 		sz2 = VGEN_DBLK_SZ_256;
26957529SSriharsha.Basavapatna@Sun.COM 		sz3 = VGEN_DBLK_SZ_2048;
26967529SSriharsha.Basavapatna@Sun.COM 		ldcp->max_rxpool_size = sz3;
26977529SSriharsha.Basavapatna@Sun.COM 
26987529SSriharsha.Basavapatna@Sun.COM 		status = vio_init_multipools(&ldcp->vmp, VGEN_NUM_VMPOOLS,
26997529SSriharsha.Basavapatna@Sun.COM 		    sz1, sz2, sz3,
27007529SSriharsha.Basavapatna@Sun.COM 		    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3);
27017529SSriharsha.Basavapatna@Sun.COM 
27027529SSriharsha.Basavapatna@Sun.COM 		return (status);
27037529SSriharsha.Basavapatna@Sun.COM 	}
27047529SSriharsha.Basavapatna@Sun.COM 
27057529SSriharsha.Basavapatna@Sun.COM 	switch (data_sz) {
27067529SSriharsha.Basavapatna@Sun.COM 
27077529SSriharsha.Basavapatna@Sun.COM 	case VNET_4K:
27087529SSriharsha.Basavapatna@Sun.COM 
27097529SSriharsha.Basavapatna@Sun.COM 		sz1 = VGEN_DBLK_SZ_128;
27107529SSriharsha.Basavapatna@Sun.COM 		sz2 = VGEN_DBLK_SZ_256;
27117529SSriharsha.Basavapatna@Sun.COM 		sz3 = VGEN_DBLK_SZ_2048;
27127529SSriharsha.Basavapatna@Sun.COM 		sz4 = sz3 << 1;			/* 4K */
27137529SSriharsha.Basavapatna@Sun.COM 		ldcp->max_rxpool_size = sz4;
27147529SSriharsha.Basavapatna@Sun.COM 
27157529SSriharsha.Basavapatna@Sun.COM 		status = vio_init_multipools(&ldcp->vmp, VGEN_NUM_VMPOOLS + 1,
27167529SSriharsha.Basavapatna@Sun.COM 		    sz1, sz2, sz3, sz4,
27177529SSriharsha.Basavapatna@Sun.COM 		    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3, vgen_nrbufs4);
27187529SSriharsha.Basavapatna@Sun.COM 		break;
27197529SSriharsha.Basavapatna@Sun.COM 
27207529SSriharsha.Basavapatna@Sun.COM 	default:	/* data_sz:  4K+ to 16K */
27217529SSriharsha.Basavapatna@Sun.COM 
27227529SSriharsha.Basavapatna@Sun.COM 		sz1 = VGEN_DBLK_SZ_256;
27237529SSriharsha.Basavapatna@Sun.COM 		sz2 = VGEN_DBLK_SZ_2048;
27247529SSriharsha.Basavapatna@Sun.COM 		sz3 = data_sz >> 1;	/* Jumbo-size/2 */
27257529SSriharsha.Basavapatna@Sun.COM 		sz4 = data_sz;		/* Jumbo-size  */
27267529SSriharsha.Basavapatna@Sun.COM 		ldcp->max_rxpool_size = sz4;
27277529SSriharsha.Basavapatna@Sun.COM 
27287529SSriharsha.Basavapatna@Sun.COM 		status = vio_init_multipools(&ldcp->vmp, VGEN_NUM_VMPOOLS + 1,
27297529SSriharsha.Basavapatna@Sun.COM 		    sz1, sz2, sz3, sz4,
27307529SSriharsha.Basavapatna@Sun.COM 		    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3, vgen_nrbufs4);
27317529SSriharsha.Basavapatna@Sun.COM 		break;
27327529SSriharsha.Basavapatna@Sun.COM 
27337529SSriharsha.Basavapatna@Sun.COM 	}
27347529SSriharsha.Basavapatna@Sun.COM 
27357529SSriharsha.Basavapatna@Sun.COM 	return (status);
27367529SSriharsha.Basavapatna@Sun.COM }
27377529SSriharsha.Basavapatna@Sun.COM 
27381991Sheppo /* attach the channel corresponding to the given ldc_id to the port */
27391991Sheppo static int
27401991Sheppo vgen_ldc_attach(vgen_port_t *portp, uint64_t ldc_id)
27411991Sheppo {
27421991Sheppo 	vgen_t 		*vgenp;
27431991Sheppo 	vgen_ldclist_t	*ldclp;
27441991Sheppo 	vgen_ldc_t 	*ldcp, **prev_ldcp;
27451991Sheppo 	ldc_attr_t 	attr;
27461991Sheppo 	int 		status;
27471991Sheppo 	ldc_status_t	istatus;
27485373Sraghuram 	char		kname[MAXNAMELEN];
27495373Sraghuram 	int		instance;
27504647Sraghuram 	enum	{AST_init = 0x0, AST_ldc_alloc = 0x1,
27514647Sraghuram 		AST_mutex_init = 0x2, AST_ldc_init = 0x4,
27524647Sraghuram 		AST_ldc_reg_cb = 0x8, AST_alloc_tx_ring = 0x10,
27535935Ssb155480 		AST_create_rxmblks = 0x20,
27545935Ssb155480 		AST_create_rcv_thread = 0x40} attach_state;
27551991Sheppo 
27561991Sheppo 	attach_state = AST_init;
27571991Sheppo 	vgenp = portp->vgenp;
27581991Sheppo 	ldclp = &portp->ldclist;
27591991Sheppo 
27601991Sheppo 	ldcp = kmem_zalloc(sizeof (vgen_ldc_t), KM_NOSLEEP);
27611991Sheppo 	if (ldcp == NULL) {
27621991Sheppo 		goto ldc_attach_failed;
27631991Sheppo 	}
27641991Sheppo 	ldcp->ldc_id = ldc_id;
27651991Sheppo 	ldcp->portp = portp;
27661991Sheppo 
27671991Sheppo 	attach_state |= AST_ldc_alloc;
27681991Sheppo 
27691991Sheppo 	mutex_init(&ldcp->txlock, NULL, MUTEX_DRIVER, NULL);
27701991Sheppo 	mutex_init(&ldcp->cblock, NULL, MUTEX_DRIVER, NULL);
27711991Sheppo 	mutex_init(&ldcp->tclock, NULL, MUTEX_DRIVER, NULL);
27724647Sraghuram 	mutex_init(&ldcp->wrlock, NULL, MUTEX_DRIVER, NULL);
27734647Sraghuram 	mutex_init(&ldcp->rxlock, NULL, MUTEX_DRIVER, NULL);
27741991Sheppo 
27751991Sheppo 	attach_state |= AST_mutex_init;
27761991Sheppo 
27771991Sheppo 	attr.devclass = LDC_DEV_NT;
27786495Sspeer 	attr.instance = vgenp->instance;
27791991Sheppo 	attr.mode = LDC_MODE_UNRELIABLE;
27802410Slm66018 	attr.mtu = vnet_ldc_mtu;
27811991Sheppo 	status = ldc_init(ldc_id, &attr, &ldcp->ldc_handle);
27821991Sheppo 	if (status != 0) {
27834647Sraghuram 		DWARN(vgenp, ldcp, "ldc_init failed,rv (%d)\n", status);
27841991Sheppo 		goto ldc_attach_failed;
27851991Sheppo 	}
27861991Sheppo 	attach_state |= AST_ldc_init;
27871991Sheppo 
27884647Sraghuram 	if (vgen_rcv_thread_enabled) {
27894647Sraghuram 		ldcp->rcv_thr_flags = 0;
27904647Sraghuram 
27914647Sraghuram 		mutex_init(&ldcp->rcv_thr_lock, NULL, MUTEX_DRIVER, NULL);
27924647Sraghuram 		cv_init(&ldcp->rcv_thr_cv, NULL, CV_DRIVER, NULL);
27934647Sraghuram 		ldcp->rcv_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
27944647Sraghuram 		    vgen_ldc_rcv_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
27954647Sraghuram 
27964647Sraghuram 		attach_state |= AST_create_rcv_thread;
27974647Sraghuram 		if (ldcp->rcv_thread == NULL) {
27984647Sraghuram 			DWARN(vgenp, ldcp, "Failed to create worker thread");
27994647Sraghuram 			goto ldc_attach_failed;
28004647Sraghuram 		}
28014647Sraghuram 	}
28024647Sraghuram 
28031991Sheppo 	status = ldc_reg_callback(ldcp->ldc_handle, vgen_ldc_cb, (caddr_t)ldcp);
28041991Sheppo 	if (status != 0) {
28054647Sraghuram 		DWARN(vgenp, ldcp, "ldc_reg_callback failed, rv (%d)\n",
28064647Sraghuram 		    status);
28071991Sheppo 		goto ldc_attach_failed;
28081991Sheppo 	}
28095935Ssb155480 	/*
28105935Ssb155480 	 * allocate a message for ldc_read()s, big enough to hold ctrl and
28115935Ssb155480 	 * data msgs, including raw data msgs used to recv priority frames.
28125935Ssb155480 	 */
28136419Ssb155480 	ldcp->msglen = VIO_PKT_DATA_HDRSIZE + vgenp->max_frame_size;
28145935Ssb155480 	ldcp->ldcmsg = kmem_alloc(ldcp->msglen, KM_SLEEP);
28151991Sheppo 	attach_state |= AST_ldc_reg_cb;
28161991Sheppo 
28171991Sheppo 	(void) ldc_status(ldcp->ldc_handle, &istatus);
28181991Sheppo 	ASSERT(istatus == LDC_INIT);
28191991Sheppo 	ldcp->ldc_status = istatus;
28201991Sheppo 
28211991Sheppo 	/* allocate transmit resources */
28221991Sheppo 	status = vgen_alloc_tx_ring(ldcp);
28231991Sheppo 	if (status != 0) {
28241991Sheppo 		goto ldc_attach_failed;
28251991Sheppo 	}
28261991Sheppo 	attach_state |= AST_alloc_tx_ring;
28271991Sheppo 
28282336Snarayan 	/* allocate receive resources */
28297529SSriharsha.Basavapatna@Sun.COM 	status = vgen_init_multipools(ldcp);
28302336Snarayan 	if (status != 0) {
28312336Snarayan 		goto ldc_attach_failed;
28322336Snarayan 	}
28332336Snarayan 	attach_state |= AST_create_rxmblks;
28342336Snarayan 
28351991Sheppo 	/* Setup kstats for the channel */
28366495Sspeer 	instance = vgenp->instance;
28375373Sraghuram 	(void) sprintf(kname, "vnetldc0x%lx", ldcp->ldc_id);
28385373Sraghuram 	ldcp->ksp = vgen_setup_kstats("vnet", instance, kname, &ldcp->stats);
28395373Sraghuram 	if (ldcp->ksp == NULL) {
28401991Sheppo 		goto ldc_attach_failed;
28411991Sheppo 	}
28421991Sheppo 
28431991Sheppo 	/* initialize vgen_versions supported */
28441991Sheppo 	bcopy(vgen_versions, ldcp->vgen_versions, sizeof (ldcp->vgen_versions));
28455935Ssb155480 	vgen_reset_vnet_proto_ops(ldcp);
28461991Sheppo 
28471991Sheppo 	/* link it into the list of channels for this port */
28481991Sheppo 	WRITE_ENTER(&ldclp->rwlock);
28491991Sheppo 	prev_ldcp = (vgen_ldc_t **)(&ldclp->headp);
28501991Sheppo 	ldcp->nextp = *prev_ldcp;
28511991Sheppo 	*prev_ldcp = ldcp;
28521991Sheppo 	RW_EXIT(&ldclp->rwlock);
28531991Sheppo 
28541991Sheppo 	ldcp->flags |= CHANNEL_ATTACHED;
28551991Sheppo 	return (DDI_SUCCESS);
28561991Sheppo 
28571991Sheppo ldc_attach_failed:
28584647Sraghuram 	if (attach_state & AST_ldc_reg_cb) {
28594647Sraghuram 		(void) ldc_unreg_callback(ldcp->ldc_handle);
28605935Ssb155480 		kmem_free(ldcp->ldcmsg, ldcp->msglen);
28614647Sraghuram 	}
28624647Sraghuram 	if (attach_state & AST_create_rcv_thread) {
28634647Sraghuram 		if (ldcp->rcv_thread != NULL) {
28644647Sraghuram 			vgen_stop_rcv_thread(ldcp);
28654647Sraghuram 		}
28664647Sraghuram 		mutex_destroy(&ldcp->rcv_thr_lock);
28674647Sraghuram 		cv_destroy(&ldcp->rcv_thr_cv);
28684647Sraghuram 	}
28692336Snarayan 	if (attach_state & AST_create_rxmblks) {
28704647Sraghuram 		vio_mblk_pool_t *fvmp = NULL;
28714647Sraghuram 
28724647Sraghuram 		vio_destroy_multipools(&ldcp->vmp, &fvmp);
28734647Sraghuram 		ASSERT(fvmp == NULL);
28742336Snarayan 	}
28751991Sheppo 	if (attach_state & AST_alloc_tx_ring) {
28761991Sheppo 		vgen_free_tx_ring(ldcp);
28771991Sheppo 	}
28781991Sheppo 	if (attach_state & AST_ldc_init) {
28791991Sheppo 		(void) ldc_fini(ldcp->ldc_handle);
28801991Sheppo 	}
28811991Sheppo 	if (attach_state & AST_mutex_init) {
28821991Sheppo 		mutex_destroy(&ldcp->tclock);
28831991Sheppo 		mutex_destroy(&ldcp->txlock);
28841991Sheppo 		mutex_destroy(&ldcp->cblock);
28854647Sraghuram 		mutex_destroy(&ldcp->wrlock);
28864647Sraghuram 		mutex_destroy(&ldcp->rxlock);
28871991Sheppo 	}
28881991Sheppo 	if (attach_state & AST_ldc_alloc) {
28891991Sheppo 		KMEM_FREE(ldcp);
28901991Sheppo 	}
28911991Sheppo 	return (DDI_FAILURE);
28921991Sheppo }
28931991Sheppo 
28941991Sheppo /* detach a channel from the port */
28951991Sheppo static void
28961991Sheppo vgen_ldc_detach(vgen_ldc_t *ldcp)
28971991Sheppo {
28981991Sheppo 	vgen_port_t	*portp;
28991991Sheppo 	vgen_t 		*vgenp;
29001991Sheppo 	vgen_ldc_t 	*pldcp;
29011991Sheppo 	vgen_ldc_t	**prev_ldcp;
29021991Sheppo 	vgen_ldclist_t	*ldclp;
29031991Sheppo 
29041991Sheppo 	portp = ldcp->portp;
29051991Sheppo 	vgenp = portp->vgenp;
29061991Sheppo 	ldclp = &portp->ldclist;
29071991Sheppo 
29081991Sheppo 	prev_ldcp =  (vgen_ldc_t **)&ldclp->headp;
29091991Sheppo 	for (; (pldcp = *prev_ldcp) != NULL; prev_ldcp = &pldcp->nextp) {
29101991Sheppo 		if (pldcp == ldcp) {
29111991Sheppo 			break;
29121991Sheppo 		}
29131991Sheppo 	}
29141991Sheppo 
29151991Sheppo 	if (pldcp == NULL) {
29161991Sheppo 		/* invalid ldcp? */
29171991Sheppo 		return;
29181991Sheppo 	}
29191991Sheppo 
29201991Sheppo 	if (ldcp->ldc_status != LDC_INIT) {
29214647Sraghuram 		DWARN(vgenp, ldcp, "ldc_status is not INIT\n");
29221991Sheppo 	}
29231991Sheppo 
29241991Sheppo 	if (ldcp->flags & CHANNEL_ATTACHED) {
29251991Sheppo 		ldcp->flags &= ~(CHANNEL_ATTACHED);
29261991Sheppo 
29274647Sraghuram 		(void) ldc_unreg_callback(ldcp->ldc_handle);
29284647Sraghuram 		if (ldcp->rcv_thread != NULL) {
29294647Sraghuram 			/* First stop the receive thread */
29304647Sraghuram 			vgen_stop_rcv_thread(ldcp);
29314647Sraghuram 			mutex_destroy(&ldcp->rcv_thr_lock);
29324647Sraghuram 			cv_destroy(&ldcp->rcv_thr_cv);
29334647Sraghuram 		}
29345935Ssb155480 		kmem_free(ldcp->ldcmsg, ldcp->msglen);
29354647Sraghuram 
29365373Sraghuram 		vgen_destroy_kstats(ldcp->ksp);
29375373Sraghuram 		ldcp->ksp = NULL;
29385373Sraghuram 
29394647Sraghuram 		/*
29404647Sraghuram 		 * if we cannot reclaim all mblks, put this
29414647Sraghuram 		 * on the list of pools(vgenp->rmp) to be reclaimed when the
29424647Sraghuram 		 * device gets detached (see vgen_uninit()).
29434647Sraghuram 		 */
29444647Sraghuram 		vio_destroy_multipools(&ldcp->vmp, &vgenp->rmp);
29452336Snarayan 
29461991Sheppo 		/* free transmit resources */
29471991Sheppo 		vgen_free_tx_ring(ldcp);
29482336Snarayan 
29491991Sheppo 		(void) ldc_fini(ldcp->ldc_handle);
29501991Sheppo 		mutex_destroy(&ldcp->tclock);
29511991Sheppo 		mutex_destroy(&ldcp->txlock);
29521991Sheppo 		mutex_destroy(&ldcp->cblock);
29534647Sraghuram 		mutex_destroy(&ldcp->wrlock);
29544647Sraghuram 		mutex_destroy(&ldcp->rxlock);
29551991Sheppo 
29561991Sheppo 		/* unlink it from the list */
29571991Sheppo 		*prev_ldcp = ldcp->nextp;
29581991Sheppo 		KMEM_FREE(ldcp);
29591991Sheppo 	}
29601991Sheppo }
29611991Sheppo 
29621991Sheppo /*
29631991Sheppo  * This function allocates transmit resources for the channel.
29641991Sheppo  * The resources consist of a transmit descriptor ring and an associated
29651991Sheppo  * transmit buffer ring.
29661991Sheppo  */
29671991Sheppo static int
29681991Sheppo vgen_alloc_tx_ring(vgen_ldc_t *ldcp)
29691991Sheppo {
29701991Sheppo 	void *tbufp;
29711991Sheppo 	ldc_mem_info_t minfo;
29721991Sheppo 	uint32_t txdsize;
29731991Sheppo 	uint32_t tbufsize;
29741991Sheppo 	int status;
29754647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
29761991Sheppo 
29771991Sheppo 	ldcp->num_txds = vnet_ntxds;
29781991Sheppo 	txdsize = sizeof (vnet_public_desc_t);
29791991Sheppo 	tbufsize = sizeof (vgen_private_desc_t);
29801991Sheppo 
29811991Sheppo 	/* allocate transmit buffer ring */
29821991Sheppo 	tbufp = kmem_zalloc(ldcp->num_txds * tbufsize, KM_NOSLEEP);
29831991Sheppo 	if (tbufp == NULL) {
29841991Sheppo 		return (DDI_FAILURE);
29851991Sheppo 	}
29861991Sheppo 
29871991Sheppo 	/* create transmit descriptor ring */
29881991Sheppo 	status = ldc_mem_dring_create(ldcp->num_txds, txdsize,
29891991Sheppo 	    &ldcp->tx_dhandle);
29901991Sheppo 	if (status) {
29914647Sraghuram 		DWARN(vgenp, ldcp, "ldc_mem_dring_create() failed\n");
29921991Sheppo 		kmem_free(tbufp, ldcp->num_txds * tbufsize);
29931991Sheppo 		return (DDI_FAILURE);
29941991Sheppo 	}
29951991Sheppo 
29961991Sheppo 	/* get the addr of descripror ring */
29971991Sheppo 	status = ldc_mem_dring_info(ldcp->tx_dhandle, &minfo);
29981991Sheppo 	if (status) {
29994647Sraghuram 		DWARN(vgenp, ldcp, "ldc_mem_dring_info() failed\n");
30001991Sheppo 		kmem_free(tbufp, ldcp->num_txds * tbufsize);
30011991Sheppo 		(void) ldc_mem_dring_destroy(ldcp->tx_dhandle);
30021991Sheppo 		ldcp->tbufp = NULL;
30031991Sheppo 		return (DDI_FAILURE);
30041991Sheppo 	}
30051991Sheppo 	ldcp->txdp = (vnet_public_desc_t *)(minfo.vaddr);
30061991Sheppo 	ldcp->tbufp = tbufp;
30071991Sheppo 
30081991Sheppo 	ldcp->txdendp = &((ldcp->txdp)[ldcp->num_txds]);
30091991Sheppo 	ldcp->tbufendp = &((ldcp->tbufp)[ldcp->num_txds]);
30101991Sheppo 
30111991Sheppo 	return (DDI_SUCCESS);
30121991Sheppo }
30131991Sheppo 
30141991Sheppo /* Free transmit resources for the channel */
30151991Sheppo static void
30161991Sheppo vgen_free_tx_ring(vgen_ldc_t *ldcp)
30171991Sheppo {
30181991Sheppo 	int tbufsize = sizeof (vgen_private_desc_t);
30191991Sheppo 
30201991Sheppo 	/* free transmit descriptor ring */
30211991Sheppo 	(void) ldc_mem_dring_destroy(ldcp->tx_dhandle);
30221991Sheppo 
30231991Sheppo 	/* free transmit buffer ring */
30241991Sheppo 	kmem_free(ldcp->tbufp, ldcp->num_txds * tbufsize);
30251991Sheppo 	ldcp->txdp = ldcp->txdendp = NULL;
30261991Sheppo 	ldcp->tbufp = ldcp->tbufendp = NULL;
30271991Sheppo }
30281991Sheppo 
30291991Sheppo /* enable transmit/receive on the channels for the port */
30301991Sheppo static void
30311991Sheppo vgen_init_ldcs(vgen_port_t *portp)
30321991Sheppo {
30331991Sheppo 	vgen_ldclist_t	*ldclp = &portp->ldclist;
30341991Sheppo 	vgen_ldc_t	*ldcp;
30351991Sheppo 
30361991Sheppo 	READ_ENTER(&ldclp->rwlock);
30371991Sheppo 	ldcp =  ldclp->headp;
30381991Sheppo 	for (; ldcp  != NULL; ldcp = ldcp->nextp) {
30391991Sheppo 		(void) vgen_ldc_init(ldcp);
30401991Sheppo 	}
30411991Sheppo 	RW_EXIT(&ldclp->rwlock);
30421991Sheppo }
30431991Sheppo 
30441991Sheppo /* stop transmit/receive on the channels for the port */
30451991Sheppo static void
30461991Sheppo vgen_uninit_ldcs(vgen_port_t *portp)
30471991Sheppo {
30481991Sheppo 	vgen_ldclist_t	*ldclp = &portp->ldclist;
30491991Sheppo 	vgen_ldc_t	*ldcp;
30501991Sheppo 
30511991Sheppo 	READ_ENTER(&ldclp->rwlock);
30521991Sheppo 	ldcp =  ldclp->headp;
30531991Sheppo 	for (; ldcp  != NULL; ldcp = ldcp->nextp) {
30541991Sheppo 		vgen_ldc_uninit(ldcp);
30551991Sheppo 	}
30561991Sheppo 	RW_EXIT(&ldclp->rwlock);
30571991Sheppo }
30581991Sheppo 
30591991Sheppo /* enable transmit/receive on the channel */
30601991Sheppo static int
30611991Sheppo vgen_ldc_init(vgen_ldc_t *ldcp)
30621991Sheppo {
30634647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
30641991Sheppo 	ldc_status_t	istatus;
30651991Sheppo 	int		rv;
30662109Slm66018 	uint32_t	retries = 0;
30674647Sraghuram 	enum	{ ST_init = 0x0, ST_ldc_open = 0x1,
30684647Sraghuram 		ST_init_tbufs = 0x2, ST_cb_enable = 0x4} init_state;
30691991Sheppo 	init_state = ST_init;
30701991Sheppo 
30714647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
30721991Sheppo 	LDC_LOCK(ldcp);
30731991Sheppo 
30741991Sheppo 	rv = ldc_open(ldcp->ldc_handle);
30751991Sheppo 	if (rv != 0) {
30764647Sraghuram 		DWARN(vgenp, ldcp, "ldc_open failed: rv(%d)\n", rv);
30771991Sheppo 		goto ldcinit_failed;
30781991Sheppo 	}
30791991Sheppo 	init_state |= ST_ldc_open;
30801991Sheppo 
30811991Sheppo 	(void) ldc_status(ldcp->ldc_handle, &istatus);
30821991Sheppo 	if (istatus != LDC_OPEN && istatus != LDC_READY) {
30834647Sraghuram 		DWARN(vgenp, ldcp, "status(%d) is not OPEN/READY\n", istatus);
30841991Sheppo 		goto ldcinit_failed;
30851991Sheppo 	}
30861991Sheppo 	ldcp->ldc_status = istatus;
30871991Sheppo 
30881991Sheppo 	rv = vgen_init_tbufs(ldcp);
30891991Sheppo 	if (rv != 0) {
30904647Sraghuram 		DWARN(vgenp, ldcp, "vgen_init_tbufs() failed\n");
30911991Sheppo 		goto ldcinit_failed;
30921991Sheppo 	}
30931991Sheppo 	init_state |= ST_init_tbufs;
30941991Sheppo 
30952748Slm66018 	rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_ENABLE);
30962748Slm66018 	if (rv != 0) {
30974647Sraghuram 		DWARN(vgenp, ldcp, "ldc_set_cb_mode failed: rv(%d)\n", rv);
30982748Slm66018 		goto ldcinit_failed;
30992748Slm66018 	}
31002748Slm66018 
31012748Slm66018 	init_state |= ST_cb_enable;
31022748Slm66018 
31032109Slm66018 	do {
31042109Slm66018 		rv = ldc_up(ldcp->ldc_handle);
31052109Slm66018 		if ((rv != 0) && (rv == EWOULDBLOCK)) {
31064647Sraghuram 			DBG2(vgenp, ldcp, "ldc_up err rv(%d)\n", rv);
31072109Slm66018 			drv_usecwait(VGEN_LDC_UP_DELAY);
31082109Slm66018 		}
31092109Slm66018 		if (retries++ >= vgen_ldcup_retries)
31102109Slm66018 			break;
31112109Slm66018 	} while (rv == EWOULDBLOCK);
31121991Sheppo 
31131991Sheppo 	(void) ldc_status(ldcp->ldc_handle, &istatus);
31142793Slm66018 	if (istatus == LDC_UP) {
31154647Sraghuram 		DWARN(vgenp, ldcp, "status(%d) is UP\n", istatus);
31161991Sheppo 	}
31172793Slm66018 
31181991Sheppo 	ldcp->ldc_status = istatus;
31191991Sheppo 
31201991Sheppo 	/* initialize transmit watchdog timeout */
31211991Sheppo 	ldcp->wd_tid = timeout(vgen_ldc_watchdog, (caddr_t)ldcp,
31221991Sheppo 	    drv_usectohz(vnet_ldcwd_interval * 1000));
31231991Sheppo 
31242793Slm66018 	ldcp->hphase = -1;
31251991Sheppo 	ldcp->flags |= CHANNEL_STARTED;
31261991Sheppo 
31272793Slm66018 	/* if channel is already UP - start handshake */
31282793Slm66018 	if (istatus == LDC_UP) {
31292793Slm66018 		vgen_t *vgenp = LDC_TO_VGEN(ldcp);
31302793Slm66018 		if (ldcp->portp != vgenp->vsw_portp) {
31312793Slm66018 			/*
31326495Sspeer 			 * As the channel is up, use this port from now on.
31332793Slm66018 			 */
31346495Sspeer 			(void) atomic_swap_32(
31356495Sspeer 			    &ldcp->portp->use_vsw_port, B_FALSE);
31362793Slm66018 		}
31372793Slm66018 
31382793Slm66018 		/* Initialize local session id */
31392793Slm66018 		ldcp->local_sid = ddi_get_lbolt();
31402793Slm66018 
31412793Slm66018 		/* clear peer session id */
31422793Slm66018 		ldcp->peer_sid = 0;
31432793Slm66018 		ldcp->hretries = 0;
31442793Slm66018 
31452793Slm66018 		/* Initiate Handshake process with peer ldc endpoint */
31462793Slm66018 		vgen_reset_hphase(ldcp);
31472793Slm66018 
31482793Slm66018 		mutex_exit(&ldcp->tclock);
31492793Slm66018 		mutex_exit(&ldcp->txlock);
31504647Sraghuram 		mutex_exit(&ldcp->wrlock);
31515708Sraghuram 		mutex_exit(&ldcp->rxlock);
31522793Slm66018 		vgen_handshake(vh_nextphase(ldcp));
31532793Slm66018 		mutex_exit(&ldcp->cblock);
31542793Slm66018 	} else {
31552793Slm66018 		LDC_UNLOCK(ldcp);
31562793Slm66018 	}
31572793Slm66018 
31581991Sheppo 	return (DDI_SUCCESS);
31591991Sheppo 
31601991Sheppo ldcinit_failed:
31612748Slm66018 	if (init_state & ST_cb_enable) {
31622748Slm66018 		(void) ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE);
31632748Slm66018 	}
31641991Sheppo 	if (init_state & ST_init_tbufs) {
31651991Sheppo 		vgen_uninit_tbufs(ldcp);
31661991Sheppo 	}
31671991Sheppo 	if (init_state & ST_ldc_open) {
31681991Sheppo 		(void) ldc_close(ldcp->ldc_handle);
31691991Sheppo 	}
31701991Sheppo 	LDC_UNLOCK(ldcp);
31714647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
31721991Sheppo 	return (DDI_FAILURE);
31731991Sheppo }
31741991Sheppo 
31751991Sheppo /* stop transmit/receive on the channel */
31761991Sheppo static void
31771991Sheppo vgen_ldc_uninit(vgen_ldc_t *ldcp)
31781991Sheppo {
31794647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
31801991Sheppo 	int	rv;
31811991Sheppo 
31824647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
31831991Sheppo 	LDC_LOCK(ldcp);
31841991Sheppo 
31851991Sheppo 	if ((ldcp->flags & CHANNEL_STARTED) == 0) {
31861991Sheppo 		LDC_UNLOCK(ldcp);
31874647Sraghuram 		DWARN(vgenp, ldcp, "CHANNEL_STARTED flag is not set\n");
31881991Sheppo 		return;
31891991Sheppo 	}
31901991Sheppo 
31911991Sheppo 	/* disable further callbacks */
31921991Sheppo 	rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE);
31931991Sheppo 	if (rv != 0) {
31944647Sraghuram 		DWARN(vgenp, ldcp, "ldc_set_cb_mode failed\n");
31951991Sheppo 	}
31961991Sheppo 
31976495Sspeer 	if (vgenp->vsw_portp == ldcp->portp) {
31986495Sspeer 		vio_net_report_err_t rep_err =
31996495Sspeer 		    ldcp->portp->vcb.vio_net_report_err;
32006495Sspeer 		rep_err(ldcp->portp->vhp, VIO_NET_RES_DOWN);
32016495Sspeer 	}
32026495Sspeer 
32033653Snarayan 	/*
32043653Snarayan 	 * clear handshake done bit and wait for pending tx and cb to finish.
32053653Snarayan 	 * release locks before untimeout(9F) is invoked to cancel timeouts.
32063653Snarayan 	 */
32071991Sheppo 	ldcp->hphase &= ~(VH_DONE);
32081991Sheppo 	LDC_UNLOCK(ldcp);
32093653Snarayan 
32103653Snarayan 	/* cancel handshake watchdog timeout */
32113653Snarayan 	if (ldcp->htid) {
32123653Snarayan 		(void) untimeout(ldcp->htid);
32133653Snarayan 		ldcp->htid = 0;
32143653Snarayan 	}
32153653Snarayan 
3216*7572SWentao.Yang@Sun.COM 	if (ldcp->cancel_htid) {
3217*7572SWentao.Yang@Sun.COM 		(void) untimeout(ldcp->cancel_htid);
3218*7572SWentao.Yang@Sun.COM 		ldcp->cancel_htid = 0;
3219*7572SWentao.Yang@Sun.COM 	}
3220*7572SWentao.Yang@Sun.COM 
32213653Snarayan 	/* cancel transmit watchdog timeout */
32221991Sheppo 	if (ldcp->wd_tid) {
32231991Sheppo 		(void) untimeout(ldcp->wd_tid);
32241991Sheppo 		ldcp->wd_tid = 0;
32251991Sheppo 	}
32261991Sheppo 
32273653Snarayan 	drv_usecwait(1000);
32283653Snarayan 
32293653Snarayan 	/* acquire locks again; any pending transmits and callbacks are done */
32303653Snarayan 	LDC_LOCK(ldcp);
32313653Snarayan 
32323653Snarayan 	vgen_reset_hphase(ldcp);
32333653Snarayan 
32341991Sheppo 	vgen_uninit_tbufs(ldcp);
32351991Sheppo 
32361991Sheppo 	rv = ldc_close(ldcp->ldc_handle);
32371991Sheppo 	if (rv != 0) {
32384647Sraghuram 		DWARN(vgenp, ldcp, "ldc_close err\n");
32391991Sheppo 	}
32401991Sheppo 	ldcp->ldc_status = LDC_INIT;
32411991Sheppo 	ldcp->flags &= ~(CHANNEL_STARTED);
32421991Sheppo 
32431991Sheppo 	LDC_UNLOCK(ldcp);
32441991Sheppo 
32454647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
32461991Sheppo }
32471991Sheppo 
32481991Sheppo /* Initialize the transmit buffer ring for the channel */
32491991Sheppo static int
32501991Sheppo vgen_init_tbufs(vgen_ldc_t *ldcp)
32511991Sheppo {
32521991Sheppo 	vgen_private_desc_t	*tbufp;
32531991Sheppo 	vnet_public_desc_t	*txdp;
32541991Sheppo 	vio_dring_entry_hdr_t		*hdrp;
32551991Sheppo 	int 			i;
32561991Sheppo 	int 			rv;
32572109Slm66018 	caddr_t			datap = NULL;
32582109Slm66018 	int			ci;
32592109Slm66018 	uint32_t		ncookies;
32606419Ssb155480 	size_t			data_sz;
32616419Ssb155480 	vgen_t			*vgenp;
32626419Ssb155480 
32636419Ssb155480 	vgenp = LDC_TO_VGEN(ldcp);
32641991Sheppo 
32651991Sheppo 	bzero(ldcp->tbufp, sizeof (*tbufp) * (ldcp->num_txds));
32661991Sheppo 	bzero(ldcp->txdp, sizeof (*txdp) * (ldcp->num_txds));
32671991Sheppo 
32687529SSriharsha.Basavapatna@Sun.COM 	/*
32697529SSriharsha.Basavapatna@Sun.COM 	 * In order to ensure that the number of ldc cookies per descriptor is
32707529SSriharsha.Basavapatna@Sun.COM 	 * limited to be within the default MAX_COOKIES (2), we take the steps
32717529SSriharsha.Basavapatna@Sun.COM 	 * outlined below:
32727529SSriharsha.Basavapatna@Sun.COM 	 *
32737529SSriharsha.Basavapatna@Sun.COM 	 * Align the entire data buffer area to 8K and carve out per descriptor
32747529SSriharsha.Basavapatna@Sun.COM 	 * data buffers starting from this 8K aligned base address.
32757529SSriharsha.Basavapatna@Sun.COM 	 *
32767529SSriharsha.Basavapatna@Sun.COM 	 * We round up the mtu specified to be a multiple of 2K or 4K.
32777529SSriharsha.Basavapatna@Sun.COM 	 * For sizes up to 12K we round up the size to the next 2K.
32787529SSriharsha.Basavapatna@Sun.COM 	 * For sizes > 12K we round up to the next 4K (otherwise sizes such as
32797529SSriharsha.Basavapatna@Sun.COM 	 * 14K could end up needing 3 cookies, with the buffer spread across
32807529SSriharsha.Basavapatna@Sun.COM 	 * 3 8K pages:  8K+6K, 2K+8K+2K, 6K+8K, ...).
32817529SSriharsha.Basavapatna@Sun.COM 	 */
32826419Ssb155480 	data_sz = vgenp->max_frame_size + VNET_IPALIGN + VNET_LDCALIGN;
32837529SSriharsha.Basavapatna@Sun.COM 	if (data_sz <= VNET_12K) {
32847529SSriharsha.Basavapatna@Sun.COM 		data_sz = VNET_ROUNDUP_2K(data_sz);
32857529SSriharsha.Basavapatna@Sun.COM 	} else {
32867529SSriharsha.Basavapatna@Sun.COM 		data_sz = VNET_ROUNDUP_4K(data_sz);
32877529SSriharsha.Basavapatna@Sun.COM 	}
32887529SSriharsha.Basavapatna@Sun.COM 
32897529SSriharsha.Basavapatna@Sun.COM 	/* allocate extra 8K bytes for alignment */
32907529SSriharsha.Basavapatna@Sun.COM 	ldcp->tx_data_sz = (data_sz * ldcp->num_txds) + VNET_8K;
32916419Ssb155480 	datap = kmem_zalloc(ldcp->tx_data_sz, KM_SLEEP);
32922109Slm66018 	ldcp->tx_datap = datap;
32932109Slm66018 
32947529SSriharsha.Basavapatna@Sun.COM 
32957529SSriharsha.Basavapatna@Sun.COM 	/* align the starting address of the data area to 8K */
32967529SSriharsha.Basavapatna@Sun.COM 	datap = (caddr_t)VNET_ROUNDUP_8K((uintptr_t)datap);
32977529SSriharsha.Basavapatna@Sun.COM 
32981991Sheppo 	/*
32992109Slm66018 	 * for each private descriptor, allocate a ldc mem_handle which is
33001991Sheppo 	 * required to map the data during transmit, set the flags
33011991Sheppo 	 * to free (available for use by transmit routine).
33021991Sheppo 	 */
33031991Sheppo 
33041991Sheppo 	for (i = 0; i < ldcp->num_txds; i++) {
33052109Slm66018 
33061991Sheppo 		tbufp = &(ldcp->tbufp[i]);
33071991Sheppo 		rv = ldc_mem_alloc_handle(ldcp->ldc_handle,
33084650Sraghuram 		    &(tbufp->memhandle));
33091991Sheppo 		if (rv) {
33101991Sheppo 			tbufp->memhandle = 0;
33111991Sheppo 			goto init_tbufs_failed;
33121991Sheppo 		}
33132109Slm66018 
33142109Slm66018 		/*
33152109Slm66018 		 * bind ldc memhandle to the corresponding transmit buffer.
33162109Slm66018 		 */
33172109Slm66018 		ci = ncookies = 0;
33182109Slm66018 		rv = ldc_mem_bind_handle(tbufp->memhandle,
33196419Ssb155480 		    (caddr_t)datap, data_sz, LDC_SHADOW_MAP,
33202109Slm66018 		    LDC_MEM_R, &(tbufp->memcookie[ci]), &ncookies);
33212109Slm66018 		if (rv != 0) {
33222109Slm66018 			goto init_tbufs_failed;
33232109Slm66018 		}
33242109Slm66018 
33252109Slm66018 		/*
33262109Slm66018 		 * successful in binding the handle to tx data buffer.
33272109Slm66018 		 * set datap in the private descr to this buffer.
33282109Slm66018 		 */
33292109Slm66018 		tbufp->datap = datap;
33302109Slm66018 
33312109Slm66018 		if ((ncookies == 0) ||
33324650Sraghuram 		    (ncookies > MAX_COOKIES)) {
33332109Slm66018 			goto init_tbufs_failed;
33342109Slm66018 		}
33352109Slm66018 
33362109Slm66018 		for (ci = 1; ci < ncookies; ci++) {
33372109Slm66018 			rv = ldc_mem_nextcookie(tbufp->memhandle,
33384650Sraghuram 			    &(tbufp->memcookie[ci]));
33392109Slm66018 			if (rv != 0) {
33402109Slm66018 				goto init_tbufs_failed;
33412109Slm66018 			}
33422109Slm66018 		}
33432109Slm66018 
33442109Slm66018 		tbufp->ncookies = ncookies;
33456419Ssb155480 		datap += data_sz;
33462109Slm66018 
33471991Sheppo 		tbufp->flags = VGEN_PRIV_DESC_FREE;
33481991Sheppo 		txdp = &(ldcp->txdp[i]);
33491991Sheppo 		hdrp = &txdp->hdr;
33501991Sheppo 		hdrp->dstate = VIO_DESC_FREE;
33511991Sheppo 		hdrp->ack = B_FALSE;
33521991Sheppo 		tbufp->descp = txdp;
33532109Slm66018 
33541991Sheppo 	}
33551991Sheppo 
33561991Sheppo 	/* reset tbuf walking pointers */
33571991Sheppo 	ldcp->next_tbufp = ldcp->tbufp;
33581991Sheppo 	ldcp->cur_tbufp = ldcp->tbufp;
33591991Sheppo 
33601991Sheppo 	/* initialize tx seqnum and index */
33611991Sheppo 	ldcp->next_txseq = VNET_ISS;
33621991Sheppo 	ldcp->next_txi = 0;
33631991Sheppo 
33642336Snarayan 	ldcp->resched_peer = B_TRUE;
33654647Sraghuram 	ldcp->resched_peer_txi = 0;
33662336Snarayan 
33671991Sheppo 	return (DDI_SUCCESS);
33681991Sheppo 
33691991Sheppo init_tbufs_failed:;
33701991Sheppo 	vgen_uninit_tbufs(ldcp);
33711991Sheppo 	return (DDI_FAILURE);
33721991Sheppo }
33731991Sheppo 
33741991Sheppo /* Uninitialize transmit buffer ring for the channel */
33751991Sheppo static void
33761991Sheppo vgen_uninit_tbufs(vgen_ldc_t *ldcp)
33771991Sheppo {
33781991Sheppo 	vgen_private_desc_t	*tbufp = ldcp->tbufp;
33791991Sheppo 	int 			i;
33801991Sheppo 
33811991Sheppo 	/* for each tbuf (priv_desc), free ldc mem_handle */
33821991Sheppo 	for (i = 0; i < ldcp->num_txds; i++) {
33831991Sheppo 
33841991Sheppo 		tbufp = &(ldcp->tbufp[i]);
33851991Sheppo 
33862109Slm66018 		if (tbufp->datap) { /* if bound to a ldc memhandle */
33871991Sheppo 			(void) ldc_mem_unbind_handle(tbufp->memhandle);
33882109Slm66018 			tbufp->datap = NULL;
33891991Sheppo 		}
33901991Sheppo 		if (tbufp->memhandle) {
33911991Sheppo 			(void) ldc_mem_free_handle(tbufp->memhandle);
33921991Sheppo 			tbufp->memhandle = 0;
33931991Sheppo 		}
33941991Sheppo 	}
33951991Sheppo 
33962109Slm66018 	if (ldcp->tx_datap) {
33972109Slm66018 		/* prealloc'd tx data buffer */
33986419Ssb155480 		kmem_free(ldcp->tx_datap, ldcp->tx_data_sz);
33992109Slm66018 		ldcp->tx_datap = NULL;
34006419Ssb155480 		ldcp->tx_data_sz = 0;
34012109Slm66018 	}
34022109Slm66018 
34032748Slm66018 	bzero(ldcp->tbufp, sizeof (vgen_private_desc_t) * (ldcp->num_txds));
34042748Slm66018 	bzero(ldcp->txdp, sizeof (vnet_public_desc_t) * (ldcp->num_txds));
34051991Sheppo }
34061991Sheppo 
34071991Sheppo /* clobber tx descriptor ring */
34081991Sheppo static void
34091991Sheppo vgen_clobber_tbufs(vgen_ldc_t *ldcp)
34101991Sheppo {
34111991Sheppo 	vnet_public_desc_t	*txdp;
34121991Sheppo 	vgen_private_desc_t	*tbufp;
34134647Sraghuram 	vio_dring_entry_hdr_t	*hdrp;
34144647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
34151991Sheppo 	int i;
34161991Sheppo #ifdef DEBUG
34171991Sheppo 	int ndone = 0;
34181991Sheppo #endif
34191991Sheppo 
34201991Sheppo 	for (i = 0; i < ldcp->num_txds; i++) {
34211991Sheppo 
34221991Sheppo 		tbufp = &(ldcp->tbufp[i]);
34231991Sheppo 		txdp = tbufp->descp;
34241991Sheppo 		hdrp = &txdp->hdr;
34251991Sheppo 
34261991Sheppo 		if (tbufp->flags & VGEN_PRIV_DESC_BUSY) {
34271991Sheppo 			tbufp->flags = VGEN_PRIV_DESC_FREE;
34281991Sheppo #ifdef DEBUG
34291991Sheppo 			if (hdrp->dstate == VIO_DESC_DONE)
34301991Sheppo 				ndone++;
34311991Sheppo #endif
34321991Sheppo 			hdrp->dstate = VIO_DESC_FREE;
34331991Sheppo 			hdrp->ack = B_FALSE;
34341991Sheppo 		}
34351991Sheppo 	}
34361991Sheppo 	/* reset tbuf walking pointers */
34371991Sheppo 	ldcp->next_tbufp = ldcp->tbufp;
34381991Sheppo 	ldcp->cur_tbufp = ldcp->tbufp;
34391991Sheppo 
34401991Sheppo 	/* reset tx seqnum and index */
34411991Sheppo 	ldcp->next_txseq = VNET_ISS;
34421991Sheppo 	ldcp->next_txi = 0;
34432336Snarayan 
34442336Snarayan 	ldcp->resched_peer = B_TRUE;
34454647Sraghuram 	ldcp->resched_peer_txi = 0;
34464647Sraghuram 
34474647Sraghuram 	DBG2(vgenp, ldcp, "num descrs done (%d)\n", ndone);
34481991Sheppo }
34491991Sheppo 
34501991Sheppo /* clobber receive descriptor ring */
34511991Sheppo static void
34521991Sheppo vgen_clobber_rxds(vgen_ldc_t *ldcp)
34531991Sheppo {
34541991Sheppo 	ldcp->rx_dhandle = 0;
34551991Sheppo 	bzero(&ldcp->rx_dcookie, sizeof (ldcp->rx_dcookie));
34561991Sheppo 	ldcp->rxdp = NULL;
34571991Sheppo 	ldcp->next_rxi = 0;
34581991Sheppo 	ldcp->num_rxds = 0;
34591991Sheppo 	ldcp->next_rxseq = VNET_ISS;
34601991Sheppo }
34611991Sheppo 
34621991Sheppo /* initialize receive descriptor ring */
34631991Sheppo static int
34641991Sheppo vgen_init_rxds(vgen_ldc_t *ldcp, uint32_t num_desc, uint32_t desc_size,
34651991Sheppo 	ldc_mem_cookie_t *dcookie, uint32_t ncookies)
34661991Sheppo {
34671991Sheppo 	int rv;
34681991Sheppo 	ldc_mem_info_t minfo;
34691991Sheppo 
34701991Sheppo 	rv = ldc_mem_dring_map(ldcp->ldc_handle, dcookie, ncookies, num_desc,
34716845Sha137994 	    desc_size, LDC_DIRECT_MAP, &(ldcp->rx_dhandle));
34721991Sheppo 	if (rv != 0) {
34731991Sheppo 		return (DDI_FAILURE);
34741991Sheppo 	}
34751991Sheppo 
34761991Sheppo 	/*
34771991Sheppo 	 * sucessfully mapped, now try to
34781991Sheppo 	 * get info about the mapped dring
34791991Sheppo 	 */
34801991Sheppo 	rv = ldc_mem_dring_info(ldcp->rx_dhandle, &minfo);
34811991Sheppo 	if (rv != 0) {
34821991Sheppo 		(void) ldc_mem_dring_unmap(ldcp->rx_dhandle);
34831991Sheppo 		return (DDI_FAILURE);
34841991Sheppo 	}
34851991Sheppo 
34861991Sheppo 	/*
34871991Sheppo 	 * save ring address, number of descriptors.
34881991Sheppo 	 */
34891991Sheppo 	ldcp->rxdp = (vnet_public_desc_t *)(minfo.vaddr);
34901991Sheppo 	bcopy(dcookie, &(ldcp->rx_dcookie), sizeof (*dcookie));
34911991Sheppo 	ldcp->num_rxdcookies = ncookies;
34921991Sheppo 	ldcp->num_rxds = num_desc;
34931991Sheppo 	ldcp->next_rxi = 0;
34941991Sheppo 	ldcp->next_rxseq = VNET_ISS;
34956845Sha137994 	ldcp->dring_mtype = minfo.mtype;
34961991Sheppo 
34971991Sheppo 	return (DDI_SUCCESS);
34981991Sheppo }
34991991Sheppo 
35001991Sheppo /* get channel statistics */
35011991Sheppo static uint64_t
35022311Sseb vgen_ldc_stat(vgen_ldc_t *ldcp, uint_t stat)
35031991Sheppo {
35041991Sheppo 	vgen_stats_t *statsp;
35051991Sheppo 	uint64_t val;
35061991Sheppo 
35071991Sheppo 	val = 0;
35085373Sraghuram 	statsp = &ldcp->stats;
35091991Sheppo 	switch (stat) {
35101991Sheppo 
35111991Sheppo 	case MAC_STAT_MULTIRCV:
35121991Sheppo 		val = statsp->multircv;
35131991Sheppo 		break;
35141991Sheppo 
35151991Sheppo 	case MAC_STAT_BRDCSTRCV:
35161991Sheppo 		val = statsp->brdcstrcv;
35171991Sheppo 		break;
35181991Sheppo 
35191991Sheppo 	case MAC_STAT_MULTIXMT:
35201991Sheppo 		val = statsp->multixmt;
35211991Sheppo 		break;
35221991Sheppo 
35231991Sheppo 	case MAC_STAT_BRDCSTXMT:
35241991Sheppo 		val = statsp->brdcstxmt;
35251991Sheppo 		break;
35261991Sheppo 
35271991Sheppo 	case MAC_STAT_NORCVBUF:
35281991Sheppo 		val = statsp->norcvbuf;
35291991Sheppo 		break;
35301991Sheppo 
35311991Sheppo 	case MAC_STAT_IERRORS:
35321991Sheppo 		val = statsp->ierrors;
35331991Sheppo 		break;
35341991Sheppo 
35351991Sheppo 	case MAC_STAT_NOXMTBUF:
35361991Sheppo 		val = statsp->noxmtbuf;
35371991Sheppo 		break;
35381991Sheppo 
35391991Sheppo 	case MAC_STAT_OERRORS:
35401991Sheppo 		val = statsp->oerrors;
35411991Sheppo 		break;
35421991Sheppo 
35431991Sheppo 	case MAC_STAT_COLLISIONS:
35441991Sheppo 		break;
35451991Sheppo 
35461991Sheppo 	case MAC_STAT_RBYTES:
35471991Sheppo 		val = statsp->rbytes;
35481991Sheppo 		break;
35491991Sheppo 
35501991Sheppo 	case MAC_STAT_IPACKETS:
35511991Sheppo 		val = statsp->ipackets;
35521991Sheppo 		break;
35531991Sheppo 
35541991Sheppo 	case MAC_STAT_OBYTES:
35551991Sheppo 		val = statsp->obytes;
35561991Sheppo 		break;
35571991Sheppo 
35581991Sheppo 	case MAC_STAT_OPACKETS:
35591991Sheppo 		val = statsp->opackets;
35601991Sheppo 		break;
35611991Sheppo 
35621991Sheppo 	/* stats not relevant to ldc, return 0 */
35631991Sheppo 	case MAC_STAT_IFSPEED:
35642311Sseb 	case ETHER_STAT_ALIGN_ERRORS:
35652311Sseb 	case ETHER_STAT_FCS_ERRORS:
35662311Sseb 	case ETHER_STAT_FIRST_COLLISIONS:
35672311Sseb 	case ETHER_STAT_MULTI_COLLISIONS:
35682311Sseb 	case ETHER_STAT_DEFER_XMTS:
35692311Sseb 	case ETHER_STAT_TX_LATE_COLLISIONS:
35702311Sseb 	case ETHER_STAT_EX_COLLISIONS:
35712311Sseb 	case ETHER_STAT_MACXMT_ERRORS:
35722311Sseb 	case ETHER_STAT_CARRIER_ERRORS:
35732311Sseb 	case ETHER_STAT_TOOLONG_ERRORS:
35742311Sseb 	case ETHER_STAT_XCVR_ADDR:
35752311Sseb 	case ETHER_STAT_XCVR_ID:
35762311Sseb 	case ETHER_STAT_XCVR_INUSE:
35772311Sseb 	case ETHER_STAT_CAP_1000FDX:
35782311Sseb 	case ETHER_STAT_CAP_1000HDX:
35792311Sseb 	case ETHER_STAT_CAP_100FDX:
35802311Sseb 	case ETHER_STAT_CAP_100HDX:
35812311Sseb 	case ETHER_STAT_CAP_10FDX:
35822311Sseb 	case ETHER_STAT_CAP_10HDX:
35832311Sseb 	case ETHER_STAT_CAP_ASMPAUSE:
35842311Sseb 	case ETHER_STAT_CAP_PAUSE:
35852311Sseb 	case ETHER_STAT_CAP_AUTONEG:
35862311Sseb 	case ETHER_STAT_ADV_CAP_1000FDX:
35872311Sseb 	case ETHER_STAT_ADV_CAP_1000HDX:
35882311Sseb 	case ETHER_STAT_ADV_CAP_100FDX:
35892311Sseb 	case ETHER_STAT_ADV_CAP_100HDX:
35902311Sseb 	case ETHER_STAT_ADV_CAP_10FDX:
35912311Sseb 	case ETHER_STAT_ADV_CAP_10HDX:
35922311Sseb 	case ETHER_STAT_ADV_CAP_ASMPAUSE:
35932311Sseb 	case ETHER_STAT_ADV_CAP_PAUSE:
35942311Sseb 	case ETHER_STAT_ADV_CAP_AUTONEG:
35952311Sseb 	case ETHER_STAT_LP_CAP_1000FDX:
35962311Sseb 	case ETHER_STAT_LP_CAP_1000HDX:
35972311Sseb 	case ETHER_STAT_LP_CAP_100FDX:
35982311Sseb 	case ETHER_STAT_LP_CAP_100HDX:
35992311Sseb 	case ETHER_STAT_LP_CAP_10FDX:
36002311Sseb 	case ETHER_STAT_LP_CAP_10HDX:
36012311Sseb 	case ETHER_STAT_LP_CAP_ASMPAUSE:
36022311Sseb 	case ETHER_STAT_LP_CAP_PAUSE:
36032311Sseb 	case ETHER_STAT_LP_CAP_AUTONEG:
36042311Sseb 	case ETHER_STAT_LINK_ASMPAUSE:
36052311Sseb 	case ETHER_STAT_LINK_PAUSE:
36062311Sseb 	case ETHER_STAT_LINK_AUTONEG:
36072311Sseb 	case ETHER_STAT_LINK_DUPLEX:
36081991Sheppo 	default:
36091991Sheppo 		val = 0;
36101991Sheppo 		break;
36111991Sheppo 
36121991Sheppo 	}
36131991Sheppo 	return (val);
36141991Sheppo }
36151991Sheppo 
36162793Slm66018 /*
36176495Sspeer  * LDC channel is UP, start handshake process with peer.
36182793Slm66018  */
36192793Slm66018 static void
36206495Sspeer vgen_handle_evt_up(vgen_ldc_t *ldcp)
36212793Slm66018 {
36222793Slm66018 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
36234647Sraghuram 
36244647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
36252793Slm66018 
36262793Slm66018 	ASSERT(MUTEX_HELD(&ldcp->cblock));
36272793Slm66018 
36282793Slm66018 	if (ldcp->portp != vgenp->vsw_portp) {
36292793Slm66018 		/*
36306495Sspeer 		 * As the channel is up, use this port from now on.
36312793Slm66018 		 */
36326495Sspeer 		(void) atomic_swap_32(&ldcp->portp->use_vsw_port, B_FALSE);
36332793Slm66018 	}
36342793Slm66018 
36352793Slm66018 	/* Initialize local session id */
36362793Slm66018 	ldcp->local_sid = ddi_get_lbolt();
36372793Slm66018 
36382793Slm66018 	/* clear peer session id */
36392793Slm66018 	ldcp->peer_sid = 0;
36402793Slm66018 	ldcp->hretries = 0;
36412793Slm66018 
36422793Slm66018 	if (ldcp->hphase != VH_PHASE0) {
36432793Slm66018 		vgen_handshake_reset(ldcp);
36442793Slm66018 	}
36452793Slm66018 
36462793Slm66018 	/* Initiate Handshake process with peer ldc endpoint */
36472793Slm66018 	vgen_handshake(vh_nextphase(ldcp));
36482793Slm66018 
36494647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
36502793Slm66018 }
36512793Slm66018 
36522793Slm66018 /*
36532793Slm66018  * LDC channel is Reset, terminate connection with peer and try to
36542793Slm66018  * bring the channel up again.
36552793Slm66018  */
36562793Slm66018 static void
36576495Sspeer vgen_handle_evt_reset(vgen_ldc_t *ldcp)
36582793Slm66018 {
36592793Slm66018 	ldc_status_t istatus;
36602793Slm66018 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
36612793Slm66018 	int	rv;
36622793Slm66018 
36634647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
36642793Slm66018 
36652793Slm66018 	ASSERT(MUTEX_HELD(&ldcp->cblock));
36662793Slm66018 
36672793Slm66018 	if ((ldcp->portp != vgenp->vsw_portp) &&
36684650Sraghuram 	    (vgenp->vsw_portp != NULL)) {
36692793Slm66018 		/*
36706495Sspeer 		 * As the channel is down, use the switch port until
36716495Sspeer 		 * the channel becomes ready to be used.
36722793Slm66018 		 */
36736495Sspeer 		(void) atomic_swap_32(&ldcp->portp->use_vsw_port, B_TRUE);
36746495Sspeer 	}
36756495Sspeer 
36766495Sspeer 	if (vgenp->vsw_portp == ldcp->portp) {
36776495Sspeer 		vio_net_report_err_t rep_err =
36786495Sspeer 		    ldcp->portp->vcb.vio_net_report_err;
36796495Sspeer 
36806495Sspeer 		/* Post a reset message */
36816495Sspeer 		rep_err(ldcp->portp->vhp, VIO_NET_RES_DOWN);
36822793Slm66018 	}
36832793Slm66018 
36842793Slm66018 	if (ldcp->hphase != VH_PHASE0) {
36852793Slm66018 		vgen_handshake_reset(ldcp);
36862793Slm66018 	}
36872793Slm66018 
36882793Slm66018 	/* try to bring the channel up */
36892793Slm66018 	rv = ldc_up(ldcp->ldc_handle);
36902793Slm66018 	if (rv != 0) {
36914647Sraghuram 		DWARN(vgenp, ldcp, "ldc_up err rv(%d)\n", rv);
36922793Slm66018 	}
36932793Slm66018 
36942793Slm66018 	if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
36954647Sraghuram 		DWARN(vgenp, ldcp, "ldc_status err\n");
36962793Slm66018 	} else {
36972793Slm66018 		ldcp->ldc_status = istatus;
36982793Slm66018 	}
36992793Slm66018 
37002793Slm66018 	/* if channel is already UP - restart handshake */
37012793Slm66018 	if (ldcp->ldc_status == LDC_UP) {
37026495Sspeer 		vgen_handle_evt_up(ldcp);
37032793Slm66018 	}
37042793Slm66018 
37054647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
37062793Slm66018 }
37072793Slm66018 
37081991Sheppo /* Interrupt handler for the channel */
37091991Sheppo static uint_t
37101991Sheppo vgen_ldc_cb(uint64_t event, caddr_t arg)
37111991Sheppo {
37121991Sheppo 	_NOTE(ARGUNUSED(event))
37131991Sheppo 	vgen_ldc_t	*ldcp;
37141991Sheppo 	vgen_t		*vgenp;
37151991Sheppo 	ldc_status_t 	istatus;
37161991Sheppo 	vgen_stats_t	*statsp;
3717*7572SWentao.Yang@Sun.COM 	timeout_id_t	cancel_htid = 0;
3718*7572SWentao.Yang@Sun.COM 	uint_t		ret = LDC_SUCCESS;
37191991Sheppo 
37201991Sheppo 	ldcp = (vgen_ldc_t *)arg;
37211991Sheppo 	vgenp = LDC_TO_VGEN(ldcp);
37225373Sraghuram 	statsp = &ldcp->stats;
37231991Sheppo 
37244647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
37251991Sheppo 
37261991Sheppo 	mutex_enter(&ldcp->cblock);
37271991Sheppo 	statsp->callbacks++;
37281991Sheppo 	if ((ldcp->ldc_status == LDC_INIT) || (ldcp->ldc_handle == NULL)) {
37294647Sraghuram 		DWARN(vgenp, ldcp, "status(%d) is LDC_INIT\n",
37304647Sraghuram 		    ldcp->ldc_status);
37311991Sheppo 		mutex_exit(&ldcp->cblock);
37321991Sheppo 		return (LDC_SUCCESS);
37331991Sheppo 	}
37341991Sheppo 
37352793Slm66018 	/*
3736*7572SWentao.Yang@Sun.COM 	 * cache cancel_htid before the events specific
3737*7572SWentao.Yang@Sun.COM 	 * code may overwrite it. Do not clear ldcp->cancel_htid
3738*7572SWentao.Yang@Sun.COM 	 * as it is also used to indicate the timer to quit immediately.
3739*7572SWentao.Yang@Sun.COM 	 */
3740*7572SWentao.Yang@Sun.COM 	cancel_htid = ldcp->cancel_htid;
3741*7572SWentao.Yang@Sun.COM 
3742*7572SWentao.Yang@Sun.COM 	/*
37432793Slm66018 	 * NOTE: not using switch() as event could be triggered by
37442793Slm66018 	 * a state change and a read request. Also the ordering	of the
37452793Slm66018 	 * check for the event types is deliberate.
37462793Slm66018 	 */
37472793Slm66018 	if (event & LDC_EVT_UP) {
37482793Slm66018 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
37494647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
37505708Sraghuram 			/* status couldn't be determined */
3751*7572SWentao.Yang@Sun.COM 			ret = LDC_FAILURE;
3752*7572SWentao.Yang@Sun.COM 			goto ldc_cb_ret;
37532793Slm66018 		}
37545708Sraghuram 		ldcp->ldc_status = istatus;
37555708Sraghuram 		if (ldcp->ldc_status != LDC_UP) {
37565708Sraghuram 			DWARN(vgenp, ldcp, "LDC_EVT_UP received "
37575708Sraghuram 			    " but ldc status is not UP(0x%x)\n",
37585708Sraghuram 			    ldcp->ldc_status);
37595708Sraghuram 			/* spurious interrupt, return success */
3760*7572SWentao.Yang@Sun.COM 			goto ldc_cb_ret;
37615708Sraghuram 		}
37624647Sraghuram 		DWARN(vgenp, ldcp, "event(%lx) UP, status(%d)\n",
37634647Sraghuram 		    event, ldcp->ldc_status);
37642793Slm66018 
37656495Sspeer 		vgen_handle_evt_up(ldcp);
37662793Slm66018 
37672793Slm66018 		ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
37682793Slm66018 	}
37692793Slm66018 
37705708Sraghuram 	/* Handle RESET/DOWN before READ event */
37715708Sraghuram 	if (event & (LDC_EVT_RESET | LDC_EVT_DOWN)) {
37725708Sraghuram 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
37735708Sraghuram 			DWARN(vgenp, ldcp, "ldc_status error\n");
37745708Sraghuram 			/* status couldn't be determined */
3775*7572SWentao.Yang@Sun.COM 			ret = LDC_FAILURE;
3776*7572SWentao.Yang@Sun.COM 			goto ldc_cb_ret;
37775708Sraghuram 		}
37785708Sraghuram 		ldcp->ldc_status = istatus;
37795708Sraghuram 		DWARN(vgenp, ldcp, "event(%lx) RESET/DOWN, status(%d)\n",
37805708Sraghuram 		    event, ldcp->ldc_status);
37815708Sraghuram 
37826495Sspeer 		vgen_handle_evt_reset(ldcp);
37835708Sraghuram 
37845708Sraghuram 		/*
37855708Sraghuram 		 * As the channel is down/reset, ignore READ event
37865708Sraghuram 		 * but print a debug warning message.
37875708Sraghuram 		 */
37885708Sraghuram 		if (event & LDC_EVT_READ) {
37895708Sraghuram 			DWARN(vgenp, ldcp,
37905708Sraghuram 			    "LDC_EVT_READ set along with RESET/DOWN\n");
37915708Sraghuram 			event &= ~LDC_EVT_READ;
37925708Sraghuram 		}
37935708Sraghuram 	}
37945708Sraghuram 
37952793Slm66018 	if (event & LDC_EVT_READ) {
37964647Sraghuram 		DBG2(vgenp, ldcp, "event(%lx) READ, status(%d)\n",
37974647Sraghuram 		    event, ldcp->ldc_status);
37982793Slm66018 
37992793Slm66018 		ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
38004647Sraghuram 
38014647Sraghuram 		if (ldcp->rcv_thread != NULL) {
38024647Sraghuram 			/*
38034647Sraghuram 			 * If the receive thread is enabled, then
38044647Sraghuram 			 * wakeup the receive thread to process the
38054647Sraghuram 			 * LDC messages.
38064647Sraghuram 			 */
38074647Sraghuram 			mutex_exit(&ldcp->cblock);
38084647Sraghuram 			mutex_enter(&ldcp->rcv_thr_lock);
38094647Sraghuram 			if (!(ldcp->rcv_thr_flags & VGEN_WTHR_DATARCVD)) {
38104647Sraghuram 				ldcp->rcv_thr_flags |= VGEN_WTHR_DATARCVD;
38114647Sraghuram 				cv_signal(&ldcp->rcv_thr_cv);
38124647Sraghuram 			}
38134647Sraghuram 			mutex_exit(&ldcp->rcv_thr_lock);
38144647Sraghuram 			mutex_enter(&ldcp->cblock);
38154647Sraghuram 		} else  {
38164647Sraghuram 			vgen_handle_evt_read(ldcp);
38174647Sraghuram 		}
38182793Slm66018 	}
3819*7572SWentao.Yang@Sun.COM 
3820*7572SWentao.Yang@Sun.COM ldc_cb_ret:
3821*7572SWentao.Yang@Sun.COM 	/*
3822*7572SWentao.Yang@Sun.COM 	 * Check to see if the status of cancel_htid has
3823*7572SWentao.Yang@Sun.COM 	 * changed. If another timer needs to be cancelled,
3824*7572SWentao.Yang@Sun.COM 	 * then let the next callback to clear it.
3825*7572SWentao.Yang@Sun.COM 	 */
3826*7572SWentao.Yang@Sun.COM 	if (cancel_htid == 0) {
3827*7572SWentao.Yang@Sun.COM 		cancel_htid = ldcp->cancel_htid;
3828*7572SWentao.Yang@Sun.COM 	}
38292793Slm66018 	mutex_exit(&ldcp->cblock);
38304647Sraghuram 
3831*7572SWentao.Yang@Sun.COM 	if (cancel_htid) {
38324647Sraghuram 		/*
38334647Sraghuram 		 * Cancel handshake timer.
38344647Sraghuram 		 * untimeout(9F) will not return until the pending callback is
38354647Sraghuram 		 * cancelled or has run. No problems will result from calling
38364647Sraghuram 		 * untimeout if the handler has already completed.
38374647Sraghuram 		 * If the timeout handler did run, then it would just
38384647Sraghuram 		 * return as cancel_htid is set.
38394647Sraghuram 		 */
3840*7572SWentao.Yang@Sun.COM 		DBG2(vgenp, ldcp, "calling cance_htid =0x%X \n", cancel_htid);
3841*7572SWentao.Yang@Sun.COM 		(void) untimeout(cancel_htid);
3842*7572SWentao.Yang@Sun.COM 		mutex_enter(&ldcp->cblock);
3843*7572SWentao.Yang@Sun.COM 		/* clear it only if its the same as the one we cancelled */
3844*7572SWentao.Yang@Sun.COM 		if (ldcp->cancel_htid == cancel_htid) {
3845*7572SWentao.Yang@Sun.COM 			ldcp->cancel_htid = 0;
3846*7572SWentao.Yang@Sun.COM 		}
3847*7572SWentao.Yang@Sun.COM 		mutex_exit(&ldcp->cblock);
38484647Sraghuram 	}
38494647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
3850*7572SWentao.Yang@Sun.COM 	return (ret);
38514647Sraghuram }
38524647Sraghuram 
38534647Sraghuram static void
38544647Sraghuram vgen_handle_evt_read(vgen_ldc_t *ldcp)
38554647Sraghuram {
38564647Sraghuram 	int		rv;
38575935Ssb155480 	uint64_t	*ldcmsg;
38584647Sraghuram 	size_t		msglen;
38594647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
38604647Sraghuram 	vio_msg_tag_t	*tagp;
38614647Sraghuram 	ldc_status_t 	istatus;
38624647Sraghuram 	boolean_t 	has_data;
38634647Sraghuram 
38644647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
38654647Sraghuram 
38665935Ssb155480 	ldcmsg = ldcp->ldcmsg;
38674647Sraghuram 	/*
38684647Sraghuram 	 * If the receive thread is enabled, then the cblock
38694647Sraghuram 	 * need to be acquired here. If not, the vgen_ldc_cb()
38704647Sraghuram 	 * calls this function with cblock held already.
38714647Sraghuram 	 */
38724647Sraghuram 	if (ldcp->rcv_thread != NULL) {
38734647Sraghuram 		mutex_enter(&ldcp->cblock);
38744647Sraghuram 	} else {
38754647Sraghuram 		ASSERT(MUTEX_HELD(&ldcp->cblock));
38764647Sraghuram 	}
38774647Sraghuram 
38784647Sraghuram vgen_evt_read:
38791991Sheppo 	do {
38805935Ssb155480 		msglen = ldcp->msglen;
38815935Ssb155480 		rv = ldc_read(ldcp->ldc_handle, (caddr_t)ldcmsg, &msglen);
38821991Sheppo 
38831991Sheppo 		if (rv != 0) {
38844647Sraghuram 			DWARN(vgenp, ldcp, "err rv(%d) len(%d)\n",
38854647Sraghuram 			    rv, msglen);
38862793Slm66018 			if (rv == ECONNRESET)
38874647Sraghuram 				goto vgen_evtread_error;
38881991Sheppo 			break;
38891991Sheppo 		}
38901991Sheppo 		if (msglen == 0) {
38914647Sraghuram 			DBG2(vgenp, ldcp, "ldc_read NODATA");
38921991Sheppo 			break;
38931991Sheppo 		}
38944647Sraghuram 		DBG2(vgenp, ldcp, "ldc_read msglen(%d)", msglen);
38951991Sheppo 
38961991Sheppo 		tagp = (vio_msg_tag_t *)ldcmsg;
38971991Sheppo 
38981991Sheppo 		if (ldcp->peer_sid) {
38991991Sheppo 			/*
39001991Sheppo 			 * check sid only after we have received peer's sid
39011991Sheppo 			 * in the version negotiate msg.
39021991Sheppo 			 */
39031991Sheppo #ifdef DEBUG
39041991Sheppo 			if (vgen_hdbg & HDBG_BAD_SID) {
39051991Sheppo 				/* simulate bad sid condition */
39061991Sheppo 				tagp->vio_sid = 0;
39071991Sheppo 				vgen_hdbg &= ~(HDBG_BAD_SID);
39081991Sheppo 			}
39091991Sheppo #endif
39102793Slm66018 			rv = vgen_check_sid(ldcp, tagp);
39112793Slm66018 			if (rv != VGEN_SUCCESS) {
39121991Sheppo 				/*
39131991Sheppo 				 * If sid mismatch is detected,
39141991Sheppo 				 * reset the channel.
39151991Sheppo 				 */
39161991Sheppo 				ldcp->need_ldc_reset = B_TRUE;
39174647Sraghuram 				goto vgen_evtread_error;
39181991Sheppo 			}
39191991Sheppo 		}
39201991Sheppo 
39211991Sheppo 		switch (tagp->vio_msgtype) {
39221991Sheppo 		case VIO_TYPE_CTRL:
39232793Slm66018 			rv = vgen_handle_ctrlmsg(ldcp, tagp);
39241991Sheppo 			break;
39251991Sheppo 
39261991Sheppo 		case VIO_TYPE_DATA:
39275935Ssb155480 			rv = vgen_handle_datamsg(ldcp, tagp, msglen);
39281991Sheppo 			break;
39291991Sheppo 
39301991Sheppo 		case VIO_TYPE_ERR:
39311991Sheppo 			vgen_handle_errmsg(ldcp, tagp);
39321991Sheppo 			break;
39331991Sheppo 
39341991Sheppo 		default:
39354647Sraghuram 			DWARN(vgenp, ldcp, "Unknown VIO_TYPE(%x)\n",
39364647Sraghuram 			    tagp->vio_msgtype);
39371991Sheppo 			break;
39381991Sheppo 		}
39391991Sheppo 
39404647Sraghuram 		/*
39414647Sraghuram 		 * If an error is encountered, stop processing and
39424647Sraghuram 		 * handle the error.
39434647Sraghuram 		 */
39444647Sraghuram 		if (rv != 0) {
39454647Sraghuram 			goto vgen_evtread_error;
39462793Slm66018 		}
39472793Slm66018 
39481991Sheppo 	} while (msglen);
39491991Sheppo 
39504647Sraghuram 	/* check once more before exiting */
39514647Sraghuram 	rv = ldc_chkq(ldcp->ldc_handle, &has_data);
39524647Sraghuram 	if ((rv == 0) && (has_data == B_TRUE)) {
39534647Sraghuram 		DTRACE_PROBE(vgen_chkq);
39544647Sraghuram 		goto vgen_evt_read;
39554647Sraghuram 	}
39564647Sraghuram 
39574647Sraghuram vgen_evtread_error:
39584647Sraghuram 	if (rv == ECONNRESET) {
39594647Sraghuram 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
39604647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
39614647Sraghuram 		} else {
39624647Sraghuram 			ldcp->ldc_status = istatus;
39634647Sraghuram 		}
39646495Sspeer 		vgen_handle_evt_reset(ldcp);
39654647Sraghuram 	} else if (rv) {
39664647Sraghuram 		vgen_handshake_retry(ldcp);
39674647Sraghuram 	}
39684647Sraghuram 
39694647Sraghuram 	/*
3970*7572SWentao.Yang@Sun.COM 	 * If the receive thread is enabled, then cancel the
39714647Sraghuram 	 * handshake timeout here.
39724647Sraghuram 	 */
39734647Sraghuram 	if (ldcp->rcv_thread != NULL) {
3974*7572SWentao.Yang@Sun.COM 		timeout_id_t cancel_htid = ldcp->cancel_htid;
3975*7572SWentao.Yang@Sun.COM 
39764647Sraghuram 		mutex_exit(&ldcp->cblock);
3977*7572SWentao.Yang@Sun.COM 		if (cancel_htid) {
39784647Sraghuram 			/*
39794647Sraghuram 			 * Cancel handshake timer. untimeout(9F) will
39804647Sraghuram 			 * not return until the pending callback is cancelled
39814647Sraghuram 			 * or has run. No problems will result from calling
39824647Sraghuram 			 * untimeout if the handler has already completed.
39834647Sraghuram 			 * If the timeout handler did run, then it would just
39844647Sraghuram 			 * return as cancel_htid is set.
39854647Sraghuram 			 */
3986*7572SWentao.Yang@Sun.COM 			DBG2(vgenp, ldcp, "calling cance_htid =0x%X \n",
3987*7572SWentao.Yang@Sun.COM 			    cancel_htid);
3988*7572SWentao.Yang@Sun.COM 			(void) untimeout(cancel_htid);
3989*7572SWentao.Yang@Sun.COM 
3990*7572SWentao.Yang@Sun.COM 			/*
3991*7572SWentao.Yang@Sun.COM 			 * clear it only if its the same as the one we
3992*7572SWentao.Yang@Sun.COM 			 * cancelled
3993*7572SWentao.Yang@Sun.COM 			 */
3994*7572SWentao.Yang@Sun.COM 			mutex_enter(&ldcp->cblock);
3995*7572SWentao.Yang@Sun.COM 			if (ldcp->cancel_htid == cancel_htid) {
3996*7572SWentao.Yang@Sun.COM 				ldcp->cancel_htid = 0;
3997*7572SWentao.Yang@Sun.COM 			}
3998*7572SWentao.Yang@Sun.COM 			mutex_exit(&ldcp->cblock);
39994647Sraghuram 		}
40004647Sraghuram 	}
40014647Sraghuram 
40024647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
40031991Sheppo }
40041991Sheppo 
40051991Sheppo /* vgen handshake functions */
40061991Sheppo 
40071991Sheppo /* change the hphase for the channel to the next phase */
40081991Sheppo static vgen_ldc_t *
40091991Sheppo vh_nextphase(vgen_ldc_t *ldcp)
40101991Sheppo {
40111991Sheppo 	if (ldcp->hphase == VH_PHASE3) {
40121991Sheppo 		ldcp->hphase = VH_DONE;
40131991Sheppo 	} else {
40141991Sheppo 		ldcp->hphase++;
40151991Sheppo 	}
40161991Sheppo 	return (ldcp);
40171991Sheppo }
40181991Sheppo 
40191991Sheppo /*
40201991Sheppo  * wrapper routine to send the given message over ldc using ldc_write().
40211991Sheppo  */
40221991Sheppo static int
40231991Sheppo vgen_sendmsg(vgen_ldc_t *ldcp, caddr_t msg,  size_t msglen,
40241991Sheppo     boolean_t caller_holds_lock)
40251991Sheppo {
40265935Ssb155480 	int			rv;
40275935Ssb155480 	size_t			len;
40285935Ssb155480 	uint32_t		retries = 0;
40295935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
40305935Ssb155480 	vio_msg_tag_t		*tagp = (vio_msg_tag_t *)msg;
40315935Ssb155480 	vio_dring_msg_t		*dmsg;
40325935Ssb155480 	vio_raw_data_msg_t	*rmsg;
40335935Ssb155480 	boolean_t		data_msg = B_FALSE;
40341991Sheppo 
40351991Sheppo 	len = msglen;
40361991Sheppo 	if ((len == 0) || (msg == NULL))
40371991Sheppo 		return (VGEN_FAILURE);
40381991Sheppo 
40391991Sheppo 	if (!caller_holds_lock) {
40404647Sraghuram 		mutex_enter(&ldcp->wrlock);
40411991Sheppo 	}
40421991Sheppo 
40435935Ssb155480 	if (tagp->vio_subtype == VIO_SUBTYPE_INFO) {
40445935Ssb155480 		if (tagp->vio_subtype_env == VIO_DRING_DATA) {
40455935Ssb155480 			dmsg = (vio_dring_msg_t *)tagp;
40465935Ssb155480 			dmsg->seq_num = ldcp->next_txseq;
40475935Ssb155480 			data_msg = B_TRUE;
40485935Ssb155480 		} else if (tagp->vio_subtype_env == VIO_PKT_DATA) {
40495935Ssb155480 			rmsg = (vio_raw_data_msg_t *)tagp;
40505935Ssb155480 			rmsg->seq_num = ldcp->next_txseq;
40515935Ssb155480 			data_msg = B_TRUE;
40525935Ssb155480 		}
40535935Ssb155480 	}
40545935Ssb155480 
40551991Sheppo 	do {
40561991Sheppo 		len = msglen;
40571991Sheppo 		rv = ldc_write(ldcp->ldc_handle, (caddr_t)msg, &len);
40581991Sheppo 		if (retries++ >= vgen_ldcwr_retries)
40591991Sheppo 			break;
40601991Sheppo 	} while (rv == EWOULDBLOCK);
40611991Sheppo 
40625935Ssb155480 	if (rv == 0 && data_msg == B_TRUE) {
40635935Ssb155480 		ldcp->next_txseq++;
40645935Ssb155480 	}
40655935Ssb155480 
40661991Sheppo 	if (!caller_holds_lock) {
40674647Sraghuram 		mutex_exit(&ldcp->wrlock);
40681991Sheppo 	}
40691991Sheppo 
40702793Slm66018 	if (rv != 0) {
40714647Sraghuram 		DWARN(vgenp, ldcp, "ldc_write failed: rv(%d)\n",
40724647Sraghuram 		    rv, msglen);
40732793Slm66018 		return (rv);
40742793Slm66018 	}
40752793Slm66018 
40762793Slm66018 	if (len != msglen) {
40774647Sraghuram 		DWARN(vgenp, ldcp, "ldc_write failed: rv(%d) msglen (%d)\n",
40784647Sraghuram 		    rv, msglen);
40791991Sheppo 		return (VGEN_FAILURE);
40801991Sheppo 	}
40812793Slm66018 
40821991Sheppo 	return (VGEN_SUCCESS);
40831991Sheppo }
40841991Sheppo 
40851991Sheppo /* send version negotiate message to the peer over ldc */
40861991Sheppo static int
40871991Sheppo vgen_send_version_negotiate(vgen_ldc_t *ldcp)
40881991Sheppo {
40894647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
40901991Sheppo 	vio_ver_msg_t	vermsg;
40911991Sheppo 	vio_msg_tag_t	*tagp = &vermsg.tag;
40921991Sheppo 	int		rv;
40931991Sheppo 
40941991Sheppo 	bzero(&vermsg, sizeof (vermsg));
40951991Sheppo 
40961991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
40971991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
40981991Sheppo 	tagp->vio_subtype_env = VIO_VER_INFO;
40991991Sheppo 	tagp->vio_sid = ldcp->local_sid;
41001991Sheppo 
41011991Sheppo 	/* get version msg payload from ldcp->local */
41021991Sheppo 	vermsg.ver_major = ldcp->local_hparams.ver_major;
41031991Sheppo 	vermsg.ver_minor = ldcp->local_hparams.ver_minor;
41041991Sheppo 	vermsg.dev_class = ldcp->local_hparams.dev_class;
41051991Sheppo 
41061991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (vermsg), B_FALSE);
41071991Sheppo 	if (rv != VGEN_SUCCESS) {
41084647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
41092793Slm66018 		return (rv);
41101991Sheppo 	}
41111991Sheppo 
41121991Sheppo 	ldcp->hstate |= VER_INFO_SENT;
41134647Sraghuram 	DBG2(vgenp, ldcp, "VER_INFO_SENT ver(%d,%d)\n",
41144647Sraghuram 	    vermsg.ver_major, vermsg.ver_minor);
41151991Sheppo 
41161991Sheppo 	return (VGEN_SUCCESS);
41171991Sheppo }
41181991Sheppo 
41191991Sheppo /* send attr info message to the peer over ldc */
41201991Sheppo static int
41211991Sheppo vgen_send_attr_info(vgen_ldc_t *ldcp)
41221991Sheppo {
41234647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
41241991Sheppo 	vnet_attr_msg_t	attrmsg;
41251991Sheppo 	vio_msg_tag_t	*tagp = &attrmsg.tag;
41261991Sheppo 	int		rv;
41271991Sheppo 
41281991Sheppo 	bzero(&attrmsg, sizeof (attrmsg));
41291991Sheppo 
41301991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
41311991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
41321991Sheppo 	tagp->vio_subtype_env = VIO_ATTR_INFO;
41331991Sheppo 	tagp->vio_sid = ldcp->local_sid;
41341991Sheppo 
41351991Sheppo 	/* get attr msg payload from ldcp->local */
41361991Sheppo 	attrmsg.mtu = ldcp->local_hparams.mtu;
41371991Sheppo 	attrmsg.addr = ldcp->local_hparams.addr;
41381991Sheppo 	attrmsg.addr_type = ldcp->local_hparams.addr_type;
41391991Sheppo 	attrmsg.xfer_mode = ldcp->local_hparams.xfer_mode;
41401991Sheppo 	attrmsg.ack_freq = ldcp->local_hparams.ack_freq;
41411991Sheppo 
41421991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (attrmsg), B_FALSE);
41431991Sheppo 	if (rv != VGEN_SUCCESS) {
41444647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
41452793Slm66018 		return (rv);
41461991Sheppo 	}
41471991Sheppo 
41481991Sheppo 	ldcp->hstate |= ATTR_INFO_SENT;
41494647Sraghuram 	DBG2(vgenp, ldcp, "ATTR_INFO_SENT\n");
41501991Sheppo 
41511991Sheppo 	return (VGEN_SUCCESS);
41521991Sheppo }
41531991Sheppo 
41541991Sheppo /* send descriptor ring register message to the peer over ldc */
41551991Sheppo static int
41561991Sheppo vgen_send_dring_reg(vgen_ldc_t *ldcp)
41571991Sheppo {
41584647Sraghuram 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
41591991Sheppo 	vio_dring_reg_msg_t	msg;
41601991Sheppo 	vio_msg_tag_t		*tagp = &msg.tag;
41611991Sheppo 	int		rv;
41621991Sheppo 
41631991Sheppo 	bzero(&msg, sizeof (msg));
41641991Sheppo 
41651991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
41661991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
41671991Sheppo 	tagp->vio_subtype_env = VIO_DRING_REG;
41681991Sheppo 	tagp->vio_sid = ldcp->local_sid;
41691991Sheppo 
41701991Sheppo 	/* get dring info msg payload from ldcp->local */
41711991Sheppo 	bcopy(&(ldcp->local_hparams.dring_cookie), (msg.cookie),
41724650Sraghuram 	    sizeof (ldc_mem_cookie_t));
41731991Sheppo 	msg.ncookies = ldcp->local_hparams.num_dcookies;
41741991Sheppo 	msg.num_descriptors = ldcp->local_hparams.num_desc;
41751991Sheppo 	msg.descriptor_size = ldcp->local_hparams.desc_size;
41761991Sheppo 
41771991Sheppo 	/*
41781991Sheppo 	 * dring_ident is set to 0. After mapping the dring, peer sets this
41791991Sheppo 	 * value and sends it in the ack, which is saved in
41801991Sheppo 	 * vgen_handle_dring_reg().
41811991Sheppo 	 */
41821991Sheppo 	msg.dring_ident = 0;
41831991Sheppo 
41841991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (msg), B_FALSE);
41851991Sheppo 	if (rv != VGEN_SUCCESS) {
41864647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
41872793Slm66018 		return (rv);
41881991Sheppo 	}
41891991Sheppo 
41901991Sheppo 	ldcp->hstate |= DRING_INFO_SENT;
41914647Sraghuram 	DBG2(vgenp, ldcp, "DRING_INFO_SENT \n");
41921991Sheppo 
41931991Sheppo 	return (VGEN_SUCCESS);
41941991Sheppo }
41951991Sheppo 
41961991Sheppo static int
41971991Sheppo vgen_send_rdx_info(vgen_ldc_t *ldcp)
41981991Sheppo {
41994647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
42001991Sheppo 	vio_rdx_msg_t	rdxmsg;
42011991Sheppo 	vio_msg_tag_t	*tagp = &rdxmsg.tag;
42021991Sheppo 	int		rv;
42031991Sheppo 
42041991Sheppo 	bzero(&rdxmsg, sizeof (rdxmsg));
42051991Sheppo 
42061991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
42071991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
42081991Sheppo 	tagp->vio_subtype_env = VIO_RDX;
42091991Sheppo 	tagp->vio_sid = ldcp->local_sid;
42101991Sheppo 
42111991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (rdxmsg), B_FALSE);
42121991Sheppo 	if (rv != VGEN_SUCCESS) {
42134647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
42142793Slm66018 		return (rv);
42151991Sheppo 	}
42161991Sheppo 
42171991Sheppo 	ldcp->hstate |= RDX_INFO_SENT;
42184647Sraghuram 	DBG2(vgenp, ldcp, "RDX_INFO_SENT\n");
42191991Sheppo 
42201991Sheppo 	return (VGEN_SUCCESS);
42211991Sheppo }
42221991Sheppo 
42231991Sheppo /* send descriptor ring data message to the peer over ldc */
42241991Sheppo static int
42252336Snarayan vgen_send_dring_data(vgen_ldc_t *ldcp, uint32_t start, int32_t end)
42261991Sheppo {
42274647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
42281991Sheppo 	vio_dring_msg_t	dringmsg, *msgp = &dringmsg;
42291991Sheppo 	vio_msg_tag_t	*tagp = &msgp->tag;
42305373Sraghuram 	vgen_stats_t	*statsp = &ldcp->stats;
42311991Sheppo 	int		rv;
42321991Sheppo 
42331991Sheppo 	bzero(msgp, sizeof (*msgp));
42341991Sheppo 
42351991Sheppo 	tagp->vio_msgtype = VIO_TYPE_DATA;
42361991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
42371991Sheppo 	tagp->vio_subtype_env = VIO_DRING_DATA;
42381991Sheppo 	tagp->vio_sid = ldcp->local_sid;
42391991Sheppo 
42401991Sheppo 	msgp->dring_ident = ldcp->local_hparams.dring_ident;
42411991Sheppo 	msgp->start_idx = start;
42421991Sheppo 	msgp->end_idx = end;
42431991Sheppo 
42441991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (dringmsg), B_TRUE);
42451991Sheppo 	if (rv != VGEN_SUCCESS) {
42464647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
42472793Slm66018 		return (rv);
42481991Sheppo 	}
42491991Sheppo 
42505373Sraghuram 	statsp->dring_data_msgs++;
42512336Snarayan 
42524647Sraghuram 	DBG2(vgenp, ldcp, "DRING_DATA_SENT \n");
42531991Sheppo 
42541991Sheppo 	return (VGEN_SUCCESS);
42551991Sheppo }
42561991Sheppo 
42571991Sheppo /* send multicast addr info message to vsw */
42581991Sheppo static int
42591991Sheppo vgen_send_mcast_info(vgen_ldc_t *ldcp)
42601991Sheppo {
42611991Sheppo 	vnet_mcast_msg_t	mcastmsg;
42621991Sheppo 	vnet_mcast_msg_t	*msgp;
42631991Sheppo 	vio_msg_tag_t		*tagp;
42641991Sheppo 	vgen_t			*vgenp;
42651991Sheppo 	struct ether_addr	*mca;
42661991Sheppo 	int			rv;
42671991Sheppo 	int			i;
42681991Sheppo 	uint32_t		size;
42691991Sheppo 	uint32_t		mccount;
42701991Sheppo 	uint32_t		n;
42711991Sheppo 
42721991Sheppo 	msgp = &mcastmsg;
42731991Sheppo 	tagp = &msgp->tag;
42741991Sheppo 	vgenp = LDC_TO_VGEN(ldcp);
42751991Sheppo 
42761991Sheppo 	mccount = vgenp->mccount;
42771991Sheppo 	i = 0;
42781991Sheppo 
42791991Sheppo 	do {
42801991Sheppo 		tagp->vio_msgtype = VIO_TYPE_CTRL;
42811991Sheppo 		tagp->vio_subtype = VIO_SUBTYPE_INFO;
42821991Sheppo 		tagp->vio_subtype_env = VNET_MCAST_INFO;
42831991Sheppo 		tagp->vio_sid = ldcp->local_sid;
42841991Sheppo 
42851991Sheppo 		n = ((mccount >= VNET_NUM_MCAST) ? VNET_NUM_MCAST : mccount);
42861991Sheppo 		size = n * sizeof (struct ether_addr);
42871991Sheppo 
42881991Sheppo 		mca = &(vgenp->mctab[i]);
42891991Sheppo 		bcopy(mca, (msgp->mca), size);
42901991Sheppo 		msgp->set = B_TRUE;
42911991Sheppo 		msgp->count = n;
42921991Sheppo 
42931991Sheppo 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msgp),
42941991Sheppo 		    B_FALSE);
42951991Sheppo 		if (rv != VGEN_SUCCESS) {
42964647Sraghuram 			DWARN(vgenp, ldcp, "vgen_sendmsg err(%d)\n", rv);
42972793Slm66018 			return (rv);
42981991Sheppo 		}
42991991Sheppo 
43001991Sheppo 		mccount -= n;
43011991Sheppo 		i += n;
43021991Sheppo 
43031991Sheppo 	} while (mccount);
43041991Sheppo 
43051991Sheppo 	return (VGEN_SUCCESS);
43061991Sheppo }
43071991Sheppo 
43081991Sheppo /* Initiate Phase 2 of handshake */
43091991Sheppo static int
43101991Sheppo vgen_handshake_phase2(vgen_ldc_t *ldcp)
43111991Sheppo {
43121991Sheppo 	int rv;
43132793Slm66018 	uint32_t ncookies = 0;
43144647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
43154647Sraghuram 
43161991Sheppo #ifdef DEBUG
43171991Sheppo 	if (vgen_hdbg & HDBG_OUT_STATE) {
43181991Sheppo 		/* simulate out of state condition */
43191991Sheppo 		vgen_hdbg &= ~(HDBG_OUT_STATE);
43201991Sheppo 		rv = vgen_send_rdx_info(ldcp);
43211991Sheppo 		return (rv);
43221991Sheppo 	}
43231991Sheppo 	if (vgen_hdbg & HDBG_TIMEOUT) {
43241991Sheppo 		/* simulate timeout condition */
43251991Sheppo 		vgen_hdbg &= ~(HDBG_TIMEOUT);
43261991Sheppo 		return (VGEN_SUCCESS);
43271991Sheppo 	}
43281991Sheppo #endif
43292793Slm66018 	rv = vgen_send_attr_info(ldcp);
43302793Slm66018 	if (rv != VGEN_SUCCESS) {
43311991Sheppo 		return (rv);
43321991Sheppo 	}
43332793Slm66018 
43342793Slm66018 	/* Bind descriptor ring to the channel */
43352793Slm66018 	if (ldcp->num_txdcookies == 0) {
43362793Slm66018 		rv = ldc_mem_dring_bind(ldcp->ldc_handle, ldcp->tx_dhandle,
43376845Sha137994 		    LDC_DIRECT_MAP | LDC_SHADOW_MAP, LDC_MEM_RW,
43386845Sha137994 		    &ldcp->tx_dcookie, &ncookies);
43392793Slm66018 		if (rv != 0) {
43404647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_dring_bind failed "
43414650Sraghuram 			    "rv(%x)\n", rv);
43422793Slm66018 			return (rv);
43432793Slm66018 		}
43442793Slm66018 		ASSERT(ncookies == 1);
43452793Slm66018 		ldcp->num_txdcookies = ncookies;
43462793Slm66018 	}
43472793Slm66018 
43482793Slm66018 	/* update local dring_info params */
43492793Slm66018 	bcopy(&(ldcp->tx_dcookie), &(ldcp->local_hparams.dring_cookie),
43504650Sraghuram 	    sizeof (ldc_mem_cookie_t));
43512793Slm66018 	ldcp->local_hparams.num_dcookies = ldcp->num_txdcookies;
43522793Slm66018 	ldcp->local_hparams.num_desc = ldcp->num_txds;
43532793Slm66018 	ldcp->local_hparams.desc_size = sizeof (vnet_public_desc_t);
43542793Slm66018 
43552793Slm66018 	rv = vgen_send_dring_reg(ldcp);
43562793Slm66018 	if (rv != VGEN_SUCCESS) {
43571991Sheppo 		return (rv);
43581991Sheppo 	}
43591991Sheppo 
43601991Sheppo 	return (VGEN_SUCCESS);
43611991Sheppo }
43621991Sheppo 
43631991Sheppo /*
43645935Ssb155480  * Set vnet-protocol-version dependent functions based on version.
43655935Ssb155480  */
43665935Ssb155480 static void
43675935Ssb155480 vgen_set_vnet_proto_ops(vgen_ldc_t *ldcp)
43685935Ssb155480 {
43695935Ssb155480 	vgen_hparams_t	*lp = &ldcp->local_hparams;
43705935Ssb155480 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
43715935Ssb155480 
43727529SSriharsha.Basavapatna@Sun.COM 	if (VGEN_VER_GTEQ(ldcp, 1, 4)) {
43736419Ssb155480 		/*
43747529SSriharsha.Basavapatna@Sun.COM 		 * If the version negotiated with peer is >= 1.4(Jumbo Frame
43757529SSriharsha.Basavapatna@Sun.COM 		 * Support), set the mtu in our attributes to max_frame_size.
43766419Ssb155480 		 */
43776419Ssb155480 		lp->mtu = vgenp->max_frame_size;
43787529SSriharsha.Basavapatna@Sun.COM 	} else  if (VGEN_VER_EQ(ldcp, 1, 3)) {
43797529SSriharsha.Basavapatna@Sun.COM 		/*
43807529SSriharsha.Basavapatna@Sun.COM 		 * If the version negotiated with peer is == 1.3 (Vlan Tag
43817529SSriharsha.Basavapatna@Sun.COM 		 * Support) set the attr.mtu to ETHERMAX + VLAN_TAGSZ.
43827529SSriharsha.Basavapatna@Sun.COM 		 */
43837529SSriharsha.Basavapatna@Sun.COM 		lp->mtu = ETHERMAX + VLAN_TAGSZ;
43846419Ssb155480 	} else {
43856419Ssb155480 		vgen_port_t	*portp = ldcp->portp;
43866419Ssb155480 		vnet_t		*vnetp = vgenp->vnetp;
43876419Ssb155480 		/*
43886419Ssb155480 		 * Pre-1.3 peers expect max frame size of ETHERMAX.
43896419Ssb155480 		 * We can negotiate that size with those peers provided the
43906419Ssb155480 		 * following conditions are true:
43916419Ssb155480 		 * - Only pvid is defined for our peer and there are no vids.
43926419Ssb155480 		 * - pvids are equal.
43936419Ssb155480 		 * If the above conditions are true, then we can send/recv only
43946419Ssb155480 		 * untagged frames of max size ETHERMAX.
43956419Ssb155480 		 */
43967529SSriharsha.Basavapatna@Sun.COM 		if (portp->nvids == 0 && portp->pvid == vnetp->pvid) {
43976419Ssb155480 			lp->mtu = ETHERMAX;
43986419Ssb155480 		}
43996419Ssb155480 	}
44006419Ssb155480 
44016419Ssb155480 	if (VGEN_VER_GTEQ(ldcp, 1, 2)) {
44026419Ssb155480 		/* Versions >= 1.2 */
44035935Ssb155480 
44045935Ssb155480 		if (VGEN_PRI_ETH_DEFINED(vgenp)) {
44055935Ssb155480 			/*
44065935Ssb155480 			 * enable priority routines and pkt mode only if
44075935Ssb155480 			 * at least one pri-eth-type is specified in MD.
44085935Ssb155480 			 */
44095935Ssb155480 
44105935Ssb155480 			ldcp->tx = vgen_ldcsend;
44115935Ssb155480 			ldcp->rx_pktdata = vgen_handle_pkt_data;
44125935Ssb155480 
44135935Ssb155480 			/* set xfer mode for vgen_send_attr_info() */
44145935Ssb155480 			lp->xfer_mode = VIO_PKT_MODE | VIO_DRING_MODE_V1_2;
44155935Ssb155480 
44165935Ssb155480 		} else {
44175935Ssb155480 			/* no priority eth types defined in MD */
44185935Ssb155480 
44195935Ssb155480 			ldcp->tx = vgen_ldcsend_dring;
44205935Ssb155480 			ldcp->rx_pktdata = vgen_handle_pkt_data_nop;
44215935Ssb155480 
44225935Ssb155480 			/* set xfer mode for vgen_send_attr_info() */
44235935Ssb155480 			lp->xfer_mode = VIO_DRING_MODE_V1_2;
44245935Ssb155480 
44255935Ssb155480 		}
44265935Ssb155480 	} else {
44275935Ssb155480 		/* Versions prior to 1.2  */
44285935Ssb155480 
44295935Ssb155480 		vgen_reset_vnet_proto_ops(ldcp);
44305935Ssb155480 	}
44315935Ssb155480 }
44325935Ssb155480 
44335935Ssb155480 /*
44345935Ssb155480  * Reset vnet-protocol-version dependent functions to pre-v1.2.
44355935Ssb155480  */
44365935Ssb155480 static void
44375935Ssb155480 vgen_reset_vnet_proto_ops(vgen_ldc_t *ldcp)
44385935Ssb155480 {
44395935Ssb155480 	vgen_hparams_t	*lp = &ldcp->local_hparams;
44405935Ssb155480 
44415935Ssb155480 	ldcp->tx = vgen_ldcsend_dring;
44425935Ssb155480 	ldcp->rx_pktdata = vgen_handle_pkt_data_nop;
44435935Ssb155480 
44445935Ssb155480 	/* set xfer mode for vgen_send_attr_info() */
44455935Ssb155480 	lp->xfer_mode = VIO_DRING_MODE_V1_0;
44465935Ssb155480 }
44475935Ssb155480 
44486419Ssb155480 static void
44496419Ssb155480 vgen_vlan_unaware_port_reset(vgen_port_t *portp)
44506419Ssb155480 {
44516419Ssb155480 	vgen_ldclist_t	*ldclp;
44526419Ssb155480 	vgen_ldc_t	*ldcp;
44536419Ssb155480 	vgen_t		*vgenp = portp->vgenp;
44546419Ssb155480 	vnet_t		*vnetp = vgenp->vnetp;
44556419Ssb155480 
44566419Ssb155480 	ldclp = &portp->ldclist;
44576419Ssb155480 
44586419Ssb155480 	READ_ENTER(&ldclp->rwlock);
44596419Ssb155480 
44606419Ssb155480 	/*
44616419Ssb155480 	 * NOTE: for now, we will assume we have a single channel.
44626419Ssb155480 	 */
44636419Ssb155480 	if (ldclp->headp == NULL) {
44646419Ssb155480 		RW_EXIT(&ldclp->rwlock);
44656419Ssb155480 		return;
44666419Ssb155480 	}
44676419Ssb155480 	ldcp = ldclp->headp;
44686419Ssb155480 
44696419Ssb155480 	mutex_enter(&ldcp->cblock);
44706419Ssb155480 
44716419Ssb155480 	/*
44726419Ssb155480 	 * If the peer is vlan_unaware(ver < 1.3), reset channel and terminate
44736419Ssb155480 	 * the connection. See comments in vgen_set_vnet_proto_ops().
44746419Ssb155480 	 */
44756419Ssb155480 	if (ldcp->hphase == VH_DONE && VGEN_VER_LT(ldcp, 1, 3) &&
44766419Ssb155480 	    (portp->nvids != 0 || portp->pvid != vnetp->pvid)) {
44776419Ssb155480 		ldcp->need_ldc_reset = B_TRUE;
44786419Ssb155480 		vgen_handshake_retry(ldcp);
44796419Ssb155480 	}
44806419Ssb155480 
44816419Ssb155480 	mutex_exit(&ldcp->cblock);
44826419Ssb155480 
44836419Ssb155480 	RW_EXIT(&ldclp->rwlock);
44846419Ssb155480 }
44856419Ssb155480 
44866419Ssb155480 static void
44876419Ssb155480 vgen_reset_vlan_unaware_ports(vgen_t *vgenp)
44886419Ssb155480 {
44896419Ssb155480 	vgen_port_t	*portp;
44906419Ssb155480 	vgen_portlist_t	*plistp;
44916419Ssb155480 
44926419Ssb155480 	plistp = &(vgenp->vgenports);
44936419Ssb155480 	READ_ENTER(&plistp->rwlock);
44946419Ssb155480 
44956419Ssb155480 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
44966419Ssb155480 
44976419Ssb155480 		vgen_vlan_unaware_port_reset(portp);
44986419Ssb155480 
44996419Ssb155480 	}
45006419Ssb155480 
45016419Ssb155480 	RW_EXIT(&plistp->rwlock);
45026419Ssb155480 }
45036419Ssb155480 
45045935Ssb155480 /*
45051991Sheppo  * This function resets the handshake phase to VH_PHASE0(pre-handshake phase).
45061991Sheppo  * This can happen after a channel comes up (status: LDC_UP) or
45071991Sheppo  * when handshake gets terminated due to various conditions.
45081991Sheppo  */
45091991Sheppo static void
45101991Sheppo vgen_reset_hphase(vgen_ldc_t *ldcp)
45111991Sheppo {
45124647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
45131991Sheppo 	ldc_status_t istatus;
45142793Slm66018 	int rv;
45151991Sheppo 
45164647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
45171991Sheppo 	/* reset hstate and hphase */
45181991Sheppo 	ldcp->hstate = 0;
45191991Sheppo 	ldcp->hphase = VH_PHASE0;
45201991Sheppo 
45215935Ssb155480 	vgen_reset_vnet_proto_ops(ldcp);
45225935Ssb155480 
45233653Snarayan 	/*
45243653Snarayan 	 * Save the id of pending handshake timer in cancel_htid.
45253653Snarayan 	 * This will be checked in vgen_ldc_cb() and the handshake timer will
45263653Snarayan 	 * be cancelled after releasing cblock.
45273653Snarayan 	 */
45281991Sheppo 	if (ldcp->htid) {
45293653Snarayan 		ldcp->cancel_htid = ldcp->htid;
45301991Sheppo 		ldcp->htid = 0;
45311991Sheppo 	}
45321991Sheppo 
45331991Sheppo 	if (ldcp->local_hparams.dring_ready) {
45341991Sheppo 		ldcp->local_hparams.dring_ready = B_FALSE;
45352793Slm66018 	}
45362793Slm66018 
45372793Slm66018 	/* Unbind tx descriptor ring from the channel */
45382793Slm66018 	if (ldcp->num_txdcookies) {
45392793Slm66018 		rv = ldc_mem_dring_unbind(ldcp->tx_dhandle);
45402793Slm66018 		if (rv != 0) {
45414647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_dring_unbind failed\n");
45422793Slm66018 		}
45432793Slm66018 		ldcp->num_txdcookies = 0;
45441991Sheppo 	}
45451991Sheppo 
45461991Sheppo 	if (ldcp->peer_hparams.dring_ready) {
45471991Sheppo 		ldcp->peer_hparams.dring_ready = B_FALSE;
45481991Sheppo 		/* Unmap peer's dring */
45491991Sheppo 		(void) ldc_mem_dring_unmap(ldcp->rx_dhandle);
45501991Sheppo 		vgen_clobber_rxds(ldcp);
45511991Sheppo 	}
45521991Sheppo 
45531991Sheppo 	vgen_clobber_tbufs(ldcp);
45541991Sheppo 
45551991Sheppo 	/*
45561991Sheppo 	 * clear local handshake params and initialize.
45571991Sheppo 	 */
45581991Sheppo 	bzero(&(ldcp->local_hparams), sizeof (ldcp->local_hparams));
45591991Sheppo 
45601991Sheppo 	/* set version to the highest version supported */
45611991Sheppo 	ldcp->local_hparams.ver_major =
45624650Sraghuram 	    ldcp->vgen_versions[0].ver_major;
45631991Sheppo 	ldcp->local_hparams.ver_minor =
45644650Sraghuram 	    ldcp->vgen_versions[0].ver_minor;
45651991Sheppo 	ldcp->local_hparams.dev_class = VDEV_NETWORK;
45661991Sheppo 
45671991Sheppo 	/* set attr_info params */
45686419Ssb155480 	ldcp->local_hparams.mtu = vgenp->max_frame_size;
45691991Sheppo 	ldcp->local_hparams.addr =
45705462Swentaoy 	    vnet_macaddr_strtoul(vgenp->macaddr);
45711991Sheppo 	ldcp->local_hparams.addr_type = ADDR_TYPE_MAC;
45725935Ssb155480 	ldcp->local_hparams.xfer_mode = VIO_DRING_MODE_V1_0;
45731991Sheppo 	ldcp->local_hparams.ack_freq = 0;	/* don't need acks */
45741991Sheppo 
45751991Sheppo 	/*
45762793Slm66018 	 * Note: dring is created, but not bound yet.
45772793Slm66018 	 * local dring_info params will be updated when we bind the dring in
45782793Slm66018 	 * vgen_handshake_phase2().
45791991Sheppo 	 * dring_ident is set to 0. After mapping the dring, peer sets this
45801991Sheppo 	 * value and sends it in the ack, which is saved in
45811991Sheppo 	 * vgen_handle_dring_reg().
45821991Sheppo 	 */
45831991Sheppo 	ldcp->local_hparams.dring_ident = 0;
45841991Sheppo 
45851991Sheppo 	/* clear peer_hparams */
45861991Sheppo 	bzero(&(ldcp->peer_hparams), sizeof (ldcp->peer_hparams));
45871991Sheppo 
45881991Sheppo 	/* reset the channel if required */
45891991Sheppo 	if (ldcp->need_ldc_reset) {
45904647Sraghuram 		DWARN(vgenp, ldcp, "Doing Channel Reset...\n");
45911991Sheppo 		ldcp->need_ldc_reset = B_FALSE;
45922410Slm66018 		(void) ldc_down(ldcp->ldc_handle);
45931991Sheppo 		(void) ldc_status(ldcp->ldc_handle, &istatus);
45944647Sraghuram 		DBG2(vgenp, ldcp, "Reset Done,ldc_status(%x)\n", istatus);
45951991Sheppo 		ldcp->ldc_status = istatus;
45962793Slm66018 
45971991Sheppo 		/* clear sids */
45981991Sheppo 		ldcp->local_sid = 0;
45991991Sheppo 		ldcp->peer_sid = 0;
46002793Slm66018 
46012793Slm66018 		/* try to bring the channel up */
46022793Slm66018 		rv = ldc_up(ldcp->ldc_handle);
46032793Slm66018 		if (rv != 0) {
46044647Sraghuram 			DWARN(vgenp, ldcp, "ldc_up err rv(%d)\n", rv);
46052793Slm66018 		}
46062793Slm66018 
46072793Slm66018 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
46084647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
46092793Slm66018 		} else {
46102793Slm66018 			ldcp->ldc_status = istatus;
46112793Slm66018 		}
46121991Sheppo 	}
46131991Sheppo }
46141991Sheppo 
46151991Sheppo /* wrapper function for vgen_reset_hphase */
46161991Sheppo static void
46171991Sheppo vgen_handshake_reset(vgen_ldc_t *ldcp)
46181991Sheppo {
46191991Sheppo 	ASSERT(MUTEX_HELD(&ldcp->cblock));
46204647Sraghuram 	mutex_enter(&ldcp->rxlock);
46214647Sraghuram 	mutex_enter(&ldcp->wrlock);
46221991Sheppo 	mutex_enter(&ldcp->txlock);
46231991Sheppo 	mutex_enter(&ldcp->tclock);
46241991Sheppo 
46251991Sheppo 	vgen_reset_hphase(ldcp);
46261991Sheppo 
46271991Sheppo 	mutex_exit(&ldcp->tclock);
46281991Sheppo 	mutex_exit(&ldcp->txlock);
46294647Sraghuram 	mutex_exit(&ldcp->wrlock);
46304647Sraghuram 	mutex_exit(&ldcp->rxlock);
46311991Sheppo }
46321991Sheppo 
46331991Sheppo /*
46341991Sheppo  * Initiate handshake with the peer by sending various messages
46351991Sheppo  * based on the handshake-phase that the channel is currently in.
46361991Sheppo  */
46371991Sheppo static void
46381991Sheppo vgen_handshake(vgen_ldc_t *ldcp)
46391991Sheppo {
46401991Sheppo 	uint32_t hphase = ldcp->hphase;
46411991Sheppo 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
46422793Slm66018 	ldc_status_t	istatus;
46432793Slm66018 	int	rv = 0;
46441991Sheppo 
46451991Sheppo 	switch (hphase) {
46461991Sheppo 
46471991Sheppo 	case VH_PHASE1:
46481991Sheppo 
46491991Sheppo 		/*
46501991Sheppo 		 * start timer, for entire handshake process, turn this timer
46511991Sheppo 		 * off if all phases of handshake complete successfully and
46521991Sheppo 		 * hphase goes to VH_DONE(below) or
46531991Sheppo 		 * vgen_reset_hphase() gets called or
46541991Sheppo 		 * channel is reset due to errors or
46551991Sheppo 		 * vgen_ldc_uninit() is invoked(vgen_stop).
46561991Sheppo 		 */
4657*7572SWentao.Yang@Sun.COM 		ASSERT(ldcp->htid == 0);
46581991Sheppo 		ldcp->htid = timeout(vgen_hwatchdog, (caddr_t)ldcp,
46595171Ssb155480 		    drv_usectohz(vgen_hwd_interval * MICROSEC));
46601991Sheppo 
46611991Sheppo 		/* Phase 1 involves negotiating the version */
46622793Slm66018 		rv = vgen_send_version_negotiate(ldcp);
46631991Sheppo 		break;
46641991Sheppo 
46651991Sheppo 	case VH_PHASE2:
46662793Slm66018 		rv = vgen_handshake_phase2(ldcp);
46671991Sheppo 		break;
46681991Sheppo 
46691991Sheppo 	case VH_PHASE3:
46702793Slm66018 		rv = vgen_send_rdx_info(ldcp);
46711991Sheppo 		break;
46721991Sheppo 
46731991Sheppo 	case VH_DONE:
46743653Snarayan 		/*
46753653Snarayan 		 * Save the id of pending handshake timer in cancel_htid.
46763653Snarayan 		 * This will be checked in vgen_ldc_cb() and the handshake
46773653Snarayan 		 * timer will be cancelled after releasing cblock.
46783653Snarayan 		 */
46791991Sheppo 		if (ldcp->htid) {
46803653Snarayan 			ldcp->cancel_htid = ldcp->htid;
46811991Sheppo 			ldcp->htid = 0;
46821991Sheppo 		}
46831991Sheppo 		ldcp->hretries = 0;
46844647Sraghuram 		DBG1(vgenp, ldcp, "Handshake Done\n");
46851991Sheppo 
46865171Ssb155480 		if (ldcp->portp == vgenp->vsw_portp) {
46875171Ssb155480 			/*
46885171Ssb155480 			 * If this channel(port) is connected to vsw,
46895171Ssb155480 			 * need to sync multicast table with vsw.
46905171Ssb155480 			 */
46911991Sheppo 			mutex_exit(&ldcp->cblock);
46921991Sheppo 
46931991Sheppo 			mutex_enter(&vgenp->lock);
46942793Slm66018 			rv = vgen_send_mcast_info(ldcp);
46951991Sheppo 			mutex_exit(&vgenp->lock);
46961991Sheppo 
46971991Sheppo 			mutex_enter(&ldcp->cblock);
46982793Slm66018 			if (rv != VGEN_SUCCESS)
46992793Slm66018 				break;
47001991Sheppo 		}
47012793Slm66018 
47022793Slm66018 		/*
47032793Slm66018 		 * Check if mac layer should be notified to restart
47042793Slm66018 		 * transmissions. This can happen if the channel got
47052793Slm66018 		 * reset and vgen_clobber_tbufs() is called, while
47062793Slm66018 		 * need_resched is set.
47072793Slm66018 		 */
47082793Slm66018 		mutex_enter(&ldcp->tclock);
47092793Slm66018 		if (ldcp->need_resched) {
47106495Sspeer 			vio_net_tx_update_t vtx_update =
47116495Sspeer 			    ldcp->portp->vcb.vio_net_tx_update;
47126495Sspeer 
47132793Slm66018 			ldcp->need_resched = B_FALSE;
47146495Sspeer 			vtx_update(ldcp->portp->vhp);
47152793Slm66018 		}
47162793Slm66018 		mutex_exit(&ldcp->tclock);
47172793Slm66018 
47181991Sheppo 		break;
47191991Sheppo 
47201991Sheppo 	default:
47211991Sheppo 		break;
47221991Sheppo 	}
47232793Slm66018 
47242793Slm66018 	if (rv == ECONNRESET) {
47252793Slm66018 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
47264647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
47272793Slm66018 		} else {
47282793Slm66018 			ldcp->ldc_status = istatus;
47292793Slm66018 		}
47306495Sspeer 		vgen_handle_evt_reset(ldcp);
47312793Slm66018 	} else if (rv) {
47322793Slm66018 		vgen_handshake_reset(ldcp);
47332793Slm66018 	}
47341991Sheppo }
47351991Sheppo 
47361991Sheppo /*
47371991Sheppo  * Check if the current handshake phase has completed successfully and
47381991Sheppo  * return the status.
47391991Sheppo  */
47401991Sheppo static int
47411991Sheppo vgen_handshake_done(vgen_ldc_t *ldcp)
47421991Sheppo {
47434647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
47441991Sheppo 	uint32_t	hphase = ldcp->hphase;
47451991Sheppo 	int 		status = 0;
47461991Sheppo 
47471991Sheppo 	switch (hphase) {
47481991Sheppo 
47491991Sheppo 	case VH_PHASE1:
47501991Sheppo 		/*
47511991Sheppo 		 * Phase1 is done, if version negotiation
47521991Sheppo 		 * completed successfully.
47531991Sheppo 		 */
47541991Sheppo 		status = ((ldcp->hstate & VER_NEGOTIATED) ==
47554650Sraghuram 		    VER_NEGOTIATED);
47561991Sheppo 		break;
47571991Sheppo 
47581991Sheppo 	case VH_PHASE2:
47591991Sheppo 		/*
47601991Sheppo 		 * Phase 2 is done, if attr info and dring info
47611991Sheppo 		 * have been exchanged successfully.
47621991Sheppo 		 */
47631991Sheppo 		status = (((ldcp->hstate & ATTR_INFO_EXCHANGED) ==
47644650Sraghuram 		    ATTR_INFO_EXCHANGED) &&
47654650Sraghuram 		    ((ldcp->hstate & DRING_INFO_EXCHANGED) ==
47664650Sraghuram 		    DRING_INFO_EXCHANGED));
47671991Sheppo 		break;
47681991Sheppo 
47691991Sheppo 	case VH_PHASE3:
47701991Sheppo 		/* Phase 3 is done, if rdx msg has been exchanged */
47711991Sheppo 		status = ((ldcp->hstate & RDX_EXCHANGED) ==
47724650Sraghuram 		    RDX_EXCHANGED);
47731991Sheppo 		break;
47741991Sheppo 
47751991Sheppo 	default:
47761991Sheppo 		break;
47771991Sheppo 	}
47781991Sheppo 
47791991Sheppo 	if (status == 0) {
47801991Sheppo 		return (VGEN_FAILURE);
47811991Sheppo 	}
47824647Sraghuram 	DBG2(vgenp, ldcp, "PHASE(%d)\n", hphase);
47831991Sheppo 	return (VGEN_SUCCESS);
47841991Sheppo }
47851991Sheppo 
47861991Sheppo /* retry handshake on failure */
47871991Sheppo static void
47881991Sheppo vgen_handshake_retry(vgen_ldc_t *ldcp)
47891991Sheppo {
47901991Sheppo 	/* reset handshake phase */
47911991Sheppo 	vgen_handshake_reset(ldcp);
47923653Snarayan 
47933653Snarayan 	/* handshake retry is specified and the channel is UP */
47943653Snarayan 	if (vgen_max_hretries && (ldcp->ldc_status == LDC_UP)) {
47953653Snarayan 		if (ldcp->hretries++ < vgen_max_hretries) {
47963653Snarayan 			ldcp->local_sid = ddi_get_lbolt();
47971991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
47983653Snarayan 		}
47991991Sheppo 	}
48001991Sheppo }
48011991Sheppo 
48021991Sheppo /*
48031991Sheppo  * Handle a version info msg from the peer or an ACK/NACK from the peer
48041991Sheppo  * to a version info msg that we sent.
48051991Sheppo  */
48062793Slm66018 static int
48071991Sheppo vgen_handle_version_negotiate(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
48081991Sheppo {
48094647Sraghuram 	vgen_t		*vgenp;
48101991Sheppo 	vio_ver_msg_t	*vermsg = (vio_ver_msg_t *)tagp;
48111991Sheppo 	int		ack = 0;
48121991Sheppo 	int		failed = 0;
48131991Sheppo 	int		idx;
48141991Sheppo 	vgen_ver_t	*versions = ldcp->vgen_versions;
48152793Slm66018 	int		rv = 0;
48161991Sheppo 
48174647Sraghuram 	vgenp = LDC_TO_VGEN(ldcp);
48184647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
48191991Sheppo 	switch (tagp->vio_subtype) {
48201991Sheppo 	case VIO_SUBTYPE_INFO:
48211991Sheppo 
48221991Sheppo 		/*  Cache sid of peer if this is the first time */
48231991Sheppo 		if (ldcp->peer_sid == 0) {
48244647Sraghuram 			DBG2(vgenp, ldcp, "Caching peer_sid(%x)\n",
48254647Sraghuram 			    tagp->vio_sid);
48261991Sheppo 			ldcp->peer_sid = tagp->vio_sid;
48271991Sheppo 		}
48281991Sheppo 
48291991Sheppo 		if (ldcp->hphase != VH_PHASE1) {
48301991Sheppo 			/*
48311991Sheppo 			 * If we are not already in VH_PHASE1, reset to
48321991Sheppo 			 * pre-handshake state, and initiate handshake
48331991Sheppo 			 * to the peer too.
48341991Sheppo 			 */
48351991Sheppo 			vgen_handshake_reset(ldcp);
48361991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
48371991Sheppo 		}
48381991Sheppo 		ldcp->hstate |= VER_INFO_RCVD;
48391991Sheppo 
48401991Sheppo 		/* save peer's requested values */
48411991Sheppo 		ldcp->peer_hparams.ver_major = vermsg->ver_major;
48421991Sheppo 		ldcp->peer_hparams.ver_minor = vermsg->ver_minor;
48431991Sheppo 		ldcp->peer_hparams.dev_class = vermsg->dev_class;
48441991Sheppo 
48451991Sheppo 		if ((vermsg->dev_class != VDEV_NETWORK) &&
48461991Sheppo 		    (vermsg->dev_class != VDEV_NETWORK_SWITCH)) {
48471991Sheppo 			/* unsupported dev_class, send NACK */
48481991Sheppo 
48494647Sraghuram 			DWARN(vgenp, ldcp, "Version Negotiation Failed\n");
48502793Slm66018 
48511991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
48521991Sheppo 			tagp->vio_sid = ldcp->local_sid;
48531991Sheppo 			/* send reply msg back to peer */
48542793Slm66018 			rv = vgen_sendmsg(ldcp, (caddr_t)tagp,
48551991Sheppo 			    sizeof (*vermsg), B_FALSE);
48562793Slm66018 			if (rv != VGEN_SUCCESS) {
48572793Slm66018 				return (rv);
48582793Slm66018 			}
48592793Slm66018 			return (VGEN_FAILURE);
48601991Sheppo 		}
48611991Sheppo 
48624647Sraghuram 		DBG2(vgenp, ldcp, "VER_INFO_RCVD, ver(%d,%d)\n",
48634647Sraghuram 		    vermsg->ver_major,  vermsg->ver_minor);
48641991Sheppo 
48651991Sheppo 		idx = 0;
48661991Sheppo 
48671991Sheppo 		for (;;) {
48681991Sheppo 
48691991Sheppo 			if (vermsg->ver_major > versions[idx].ver_major) {
48701991Sheppo 
48711991Sheppo 				/* nack with next lower version */
48721991Sheppo 				tagp->vio_subtype = VIO_SUBTYPE_NACK;
48731991Sheppo 				vermsg->ver_major = versions[idx].ver_major;
48741991Sheppo 				vermsg->ver_minor = versions[idx].ver_minor;
48751991Sheppo 				break;
48761991Sheppo 			}
48771991Sheppo 
48781991Sheppo 			if (vermsg->ver_major == versions[idx].ver_major) {
48791991Sheppo 
48801991Sheppo 				/* major version match - ACK version */
48811991Sheppo 				tagp->vio_subtype = VIO_SUBTYPE_ACK;
48821991Sheppo 				ack = 1;
48831991Sheppo 
48841991Sheppo 				/*
48851991Sheppo 				 * lower minor version to the one this endpt
48861991Sheppo 				 * supports, if necessary
48871991Sheppo 				 */
48881991Sheppo 				if (vermsg->ver_minor >
48891991Sheppo 				    versions[idx].ver_minor) {
48901991Sheppo 					vermsg->ver_minor =
48914650Sraghuram 					    versions[idx].ver_minor;
48921991Sheppo 					ldcp->peer_hparams.ver_minor =
48934650Sraghuram 					    versions[idx].ver_minor;
48941991Sheppo 				}
48951991Sheppo 				break;
48961991Sheppo 			}
48971991Sheppo 
48981991Sheppo 			idx++;
48991991Sheppo 
49001991Sheppo 			if (idx == VGEN_NUM_VER) {
49011991Sheppo 
49021991Sheppo 				/* no version match - send NACK */
49031991Sheppo 				tagp->vio_subtype = VIO_SUBTYPE_NACK;
49041991Sheppo 				vermsg->ver_major = 0;
49051991Sheppo 				vermsg->ver_minor = 0;
49061991Sheppo 				failed = 1;
49071991Sheppo 				break;
49081991Sheppo 			}
49091991Sheppo 
49101991Sheppo 		}
49111991Sheppo 
49121991Sheppo 		tagp->vio_sid = ldcp->local_sid;
49131991Sheppo 
49141991Sheppo 		/* send reply msg back to peer */
49152793Slm66018 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*vermsg),
49162793Slm66018 		    B_FALSE);
49172793Slm66018 		if (rv != VGEN_SUCCESS) {
49182793Slm66018 			return (rv);
49191991Sheppo 		}
49201991Sheppo 
49211991Sheppo 		if (ack) {
49221991Sheppo 			ldcp->hstate |= VER_ACK_SENT;
49234647Sraghuram 			DBG2(vgenp, ldcp, "VER_ACK_SENT, ver(%d,%d) \n",
49244647Sraghuram 			    vermsg->ver_major, vermsg->ver_minor);
49251991Sheppo 		}
49261991Sheppo 		if (failed) {
49274647Sraghuram 			DWARN(vgenp, ldcp, "Negotiation Failed\n");
49282793Slm66018 			return (VGEN_FAILURE);
49291991Sheppo 		}
49301991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
49311991Sheppo 
49321991Sheppo 			/*  VER_ACK_SENT and VER_ACK_RCVD */
49331991Sheppo 
49341991Sheppo 			/* local and peer versions match? */
49351991Sheppo 			ASSERT((ldcp->local_hparams.ver_major ==
49364650Sraghuram 			    ldcp->peer_hparams.ver_major) &&
49374650Sraghuram 			    (ldcp->local_hparams.ver_minor ==
49384650Sraghuram 			    ldcp->peer_hparams.ver_minor));
49391991Sheppo 
49405935Ssb155480 			vgen_set_vnet_proto_ops(ldcp);
49415935Ssb155480 
49421991Sheppo 			/* move to the next phase */
49431991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
49441991Sheppo 		}
49451991Sheppo 
49461991Sheppo 		break;
49471991Sheppo 
49481991Sheppo 	case VIO_SUBTYPE_ACK:
49491991Sheppo 
49501991Sheppo 		if (ldcp->hphase != VH_PHASE1) {
49511991Sheppo 			/*  This should not happen. */
49524647Sraghuram 			DWARN(vgenp, ldcp, "Invalid Phase(%u)\n", ldcp->hphase);
49532793Slm66018 			return (VGEN_FAILURE);
49541991Sheppo 		}
49551991Sheppo 
49561991Sheppo 		/* SUCCESS - we have agreed on a version */
49571991Sheppo 		ldcp->local_hparams.ver_major = vermsg->ver_major;
49581991Sheppo 		ldcp->local_hparams.ver_minor = vermsg->ver_minor;
49591991Sheppo 		ldcp->hstate |= VER_ACK_RCVD;
49601991Sheppo 
49614647Sraghuram 		DBG2(vgenp, ldcp, "VER_ACK_RCVD, ver(%d,%d) \n",
49624647Sraghuram 		    vermsg->ver_major,  vermsg->ver_minor);
49631991Sheppo 
49641991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
49651991Sheppo 
49661991Sheppo 			/*  VER_ACK_SENT and VER_ACK_RCVD */
49671991Sheppo 
49681991Sheppo 			/* local and peer versions match? */
49691991Sheppo 			ASSERT((ldcp->local_hparams.ver_major ==
49704650Sraghuram 			    ldcp->peer_hparams.ver_major) &&
49714650Sraghuram 			    (ldcp->local_hparams.ver_minor ==
49724650Sraghuram 			    ldcp->peer_hparams.ver_minor));
49731991Sheppo 
49745935Ssb155480 			vgen_set_vnet_proto_ops(ldcp);
49755935Ssb155480 
49761991Sheppo 			/* move to the next phase */
49771991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
49781991Sheppo 		}
49791991Sheppo 		break;
49801991Sheppo 
49811991Sheppo 	case VIO_SUBTYPE_NACK:
49821991Sheppo 
49831991Sheppo 		if (ldcp->hphase != VH_PHASE1) {
49841991Sheppo 			/*  This should not happen.  */
49854647Sraghuram 			DWARN(vgenp, ldcp, "VER_NACK_RCVD Invalid "
49864647Sraghuram 			"Phase(%u)\n", ldcp->hphase);
49872793Slm66018 			return (VGEN_FAILURE);
49881991Sheppo 		}
49891991Sheppo 
49904647Sraghuram 		DBG2(vgenp, ldcp, "VER_NACK_RCVD next ver(%d,%d)\n",
49914647Sraghuram 		    vermsg->ver_major, vermsg->ver_minor);
49921991Sheppo 
49931991Sheppo 		/* check if version in NACK is zero */
49941991Sheppo 		if (vermsg->ver_major == 0 && vermsg->ver_minor == 0) {
49951991Sheppo 			/*
49961991Sheppo 			 * Version Negotiation has failed.
49971991Sheppo 			 */
49984647Sraghuram 			DWARN(vgenp, ldcp, "Version Negotiation Failed\n");
49992793Slm66018 			return (VGEN_FAILURE);
50001991Sheppo 		}
50011991Sheppo 
50021991Sheppo 		idx = 0;
50031991Sheppo 
50041991Sheppo 		for (;;) {
50051991Sheppo 
50061991Sheppo 			if (vermsg->ver_major > versions[idx].ver_major) {
50071991Sheppo 				/* select next lower version */
50081991Sheppo 
50091991Sheppo 				ldcp->local_hparams.ver_major =
50104650Sraghuram 				    versions[idx].ver_major;
50111991Sheppo 				ldcp->local_hparams.ver_minor =
50124650Sraghuram 				    versions[idx].ver_minor;
50131991Sheppo 				break;
50141991Sheppo 			}
50151991Sheppo 
50161991Sheppo 			if (vermsg->ver_major == versions[idx].ver_major) {
50171991Sheppo 				/* major version match */
50181991Sheppo 
50191991Sheppo 				ldcp->local_hparams.ver_major =
50204650Sraghuram 				    versions[idx].ver_major;
50211991Sheppo 
50221991Sheppo 				ldcp->local_hparams.ver_minor =
50234650Sraghuram 				    versions[idx].ver_minor;
50241991Sheppo 				break;
50251991Sheppo 			}
50261991Sheppo 
50271991Sheppo 			idx++;
50281991Sheppo 
50291991Sheppo 			if (idx == VGEN_NUM_VER) {
50301991Sheppo 				/*
50311991Sheppo 				 * no version match.
50321991Sheppo 				 * Version Negotiation has failed.
50331991Sheppo 				 */
50344647Sraghuram 				DWARN(vgenp, ldcp,
50354647Sraghuram 				    "Version Negotiation Failed\n");
50362793Slm66018 				return (VGEN_FAILURE);
50371991Sheppo 			}
50381991Sheppo 
50391991Sheppo 		}
50401991Sheppo 
50412793Slm66018 		rv = vgen_send_version_negotiate(ldcp);
50422793Slm66018 		if (rv != VGEN_SUCCESS) {
50432793Slm66018 			return (rv);
50441991Sheppo 		}
50451991Sheppo 
50461991Sheppo 		break;
50471991Sheppo 	}
50482793Slm66018 
50494647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
50502793Slm66018 	return (VGEN_SUCCESS);
50511991Sheppo }
50521991Sheppo 
50531991Sheppo /* Check if the attributes are supported */
50541991Sheppo static int
50551991Sheppo vgen_check_attr_info(vgen_ldc_t *ldcp, vnet_attr_msg_t *msg)
50561991Sheppo {
50575935Ssb155480 	vgen_hparams_t	*lp = &ldcp->local_hparams;
50585935Ssb155480 
50597529SSriharsha.Basavapatna@Sun.COM 	if ((msg->addr_type != ADDR_TYPE_MAC) ||
50605935Ssb155480 	    (msg->ack_freq > 64) ||
50615935Ssb155480 	    (msg->xfer_mode != lp->xfer_mode)) {
50621991Sheppo 		return (VGEN_FAILURE);
50631991Sheppo 	}
50641991Sheppo 
50657529SSriharsha.Basavapatna@Sun.COM 	if (VGEN_VER_LT(ldcp, 1, 4)) {
50667529SSriharsha.Basavapatna@Sun.COM 		/* versions < 1.4, mtu must match */
50677529SSriharsha.Basavapatna@Sun.COM 		if (msg->mtu != lp->mtu) {
50687529SSriharsha.Basavapatna@Sun.COM 			return (VGEN_FAILURE);
50697529SSriharsha.Basavapatna@Sun.COM 		}
50707529SSriharsha.Basavapatna@Sun.COM 	} else {
50717529SSriharsha.Basavapatna@Sun.COM 		/* Ver >= 1.4, validate mtu of the peer is at least ETHERMAX */
50727529SSriharsha.Basavapatna@Sun.COM 		if (msg->mtu < ETHERMAX) {
50737529SSriharsha.Basavapatna@Sun.COM 			return (VGEN_FAILURE);
50747529SSriharsha.Basavapatna@Sun.COM 		}
50757529SSriharsha.Basavapatna@Sun.COM 	}
50767529SSriharsha.Basavapatna@Sun.COM 
50771991Sheppo 	return (VGEN_SUCCESS);
50781991Sheppo }
50791991Sheppo 
50801991Sheppo /*
50811991Sheppo  * Handle an attribute info msg from the peer or an ACK/NACK from the peer
50821991Sheppo  * to an attr info msg that we sent.
50831991Sheppo  */
50842793Slm66018 static int
50851991Sheppo vgen_handle_attr_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
50861991Sheppo {
50877529SSriharsha.Basavapatna@Sun.COM 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
50887529SSriharsha.Basavapatna@Sun.COM 	vnet_attr_msg_t	*msg = (vnet_attr_msg_t *)tagp;
50897529SSriharsha.Basavapatna@Sun.COM 	vgen_hparams_t	*lp = &ldcp->local_hparams;
50907529SSriharsha.Basavapatna@Sun.COM 	vgen_hparams_t	*rp = &ldcp->peer_hparams;
50917529SSriharsha.Basavapatna@Sun.COM 	int		ack = 1;
50922793Slm66018 	int		rv = 0;
50937529SSriharsha.Basavapatna@Sun.COM 	uint32_t	mtu;
50941991Sheppo 
50954647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
50961991Sheppo 	if (ldcp->hphase != VH_PHASE2) {
50974647Sraghuram 		DWARN(vgenp, ldcp, "Rcvd ATTR_INFO subtype(%d),"
50984647Sraghuram 		" Invalid Phase(%u)\n",
50994647Sraghuram 		    tagp->vio_subtype, ldcp->hphase);
51002793Slm66018 		return (VGEN_FAILURE);
51011991Sheppo 	}
51021991Sheppo 	switch (tagp->vio_subtype) {
51031991Sheppo 	case VIO_SUBTYPE_INFO:
51041991Sheppo 
51054647Sraghuram 		DBG2(vgenp, ldcp, "ATTR_INFO_RCVD \n");
51061991Sheppo 		ldcp->hstate |= ATTR_INFO_RCVD;
51071991Sheppo 
51081991Sheppo 		/* save peer's values */
51097529SSriharsha.Basavapatna@Sun.COM 		rp->mtu = msg->mtu;
51107529SSriharsha.Basavapatna@Sun.COM 		rp->addr = msg->addr;
51117529SSriharsha.Basavapatna@Sun.COM 		rp->addr_type = msg->addr_type;
51127529SSriharsha.Basavapatna@Sun.COM 		rp->xfer_mode = msg->xfer_mode;
51137529SSriharsha.Basavapatna@Sun.COM 		rp->ack_freq = msg->ack_freq;
51147529SSriharsha.Basavapatna@Sun.COM 
51157529SSriharsha.Basavapatna@Sun.COM 		rv = vgen_check_attr_info(ldcp, msg);
51167529SSriharsha.Basavapatna@Sun.COM 		if (rv == VGEN_FAILURE) {
51171991Sheppo 			/* unsupported attr, send NACK */
51187529SSriharsha.Basavapatna@Sun.COM 			ack = 0;
51191991Sheppo 		} else {
51207529SSriharsha.Basavapatna@Sun.COM 
51217529SSriharsha.Basavapatna@Sun.COM 			if (VGEN_VER_GTEQ(ldcp, 1, 4)) {
51227529SSriharsha.Basavapatna@Sun.COM 
51237529SSriharsha.Basavapatna@Sun.COM 				/*
51247529SSriharsha.Basavapatna@Sun.COM 				 * Versions >= 1.4:
51257529SSriharsha.Basavapatna@Sun.COM 				 * The mtu is negotiated down to the
51267529SSriharsha.Basavapatna@Sun.COM 				 * minimum of our mtu and peer's mtu.
51277529SSriharsha.Basavapatna@Sun.COM 				 */
51287529SSriharsha.Basavapatna@Sun.COM 				mtu = MIN(msg->mtu, vgenp->max_frame_size);
51297529SSriharsha.Basavapatna@Sun.COM 
51307529SSriharsha.Basavapatna@Sun.COM 				/*
51317529SSriharsha.Basavapatna@Sun.COM 				 * If we have received an ack for the attr info
51327529SSriharsha.Basavapatna@Sun.COM 				 * that we sent, then check if the mtu computed
51337529SSriharsha.Basavapatna@Sun.COM 				 * above matches the mtu that the peer had ack'd
51347529SSriharsha.Basavapatna@Sun.COM 				 * (saved in local hparams). If they don't
51357529SSriharsha.Basavapatna@Sun.COM 				 * match, we fail the handshake.
51367529SSriharsha.Basavapatna@Sun.COM 				 */
51377529SSriharsha.Basavapatna@Sun.COM 				if (ldcp->hstate & ATTR_ACK_RCVD) {
51387529SSriharsha.Basavapatna@Sun.COM 					if (mtu != lp->mtu) {
51397529SSriharsha.Basavapatna@Sun.COM 						/* send NACK */
51407529SSriharsha.Basavapatna@Sun.COM 						ack = 0;
51417529SSriharsha.Basavapatna@Sun.COM 					}
51427529SSriharsha.Basavapatna@Sun.COM 				} else {
51437529SSriharsha.Basavapatna@Sun.COM 					/*
51447529SSriharsha.Basavapatna@Sun.COM 					 * Save the mtu computed above in our
51457529SSriharsha.Basavapatna@Sun.COM 					 * attr parameters, so it gets sent in
51467529SSriharsha.Basavapatna@Sun.COM 					 * the attr info from us to the peer.
51477529SSriharsha.Basavapatna@Sun.COM 					 */
51487529SSriharsha.Basavapatna@Sun.COM 					lp->mtu = mtu;
51497529SSriharsha.Basavapatna@Sun.COM 				}
51507529SSriharsha.Basavapatna@Sun.COM 
51517529SSriharsha.Basavapatna@Sun.COM 				/* save the MIN mtu in the msg to be replied */
51527529SSriharsha.Basavapatna@Sun.COM 				msg->mtu = mtu;
51537529SSriharsha.Basavapatna@Sun.COM 
51547529SSriharsha.Basavapatna@Sun.COM 			}
51557529SSriharsha.Basavapatna@Sun.COM 		}
51567529SSriharsha.Basavapatna@Sun.COM 
51577529SSriharsha.Basavapatna@Sun.COM 
51587529SSriharsha.Basavapatna@Sun.COM 		if (ack) {
51591991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_ACK;
51607529SSriharsha.Basavapatna@Sun.COM 		} else {
51617529SSriharsha.Basavapatna@Sun.COM 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
51621991Sheppo 		}
51631991Sheppo 		tagp->vio_sid = ldcp->local_sid;
51641991Sheppo 
51651991Sheppo 		/* send reply msg back to peer */
51667529SSriharsha.Basavapatna@Sun.COM 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msg),
51672793Slm66018 		    B_FALSE);
51682793Slm66018 		if (rv != VGEN_SUCCESS) {
51692793Slm66018 			return (rv);
51701991Sheppo 		}
51711991Sheppo 
51721991Sheppo 		if (ack) {
51731991Sheppo 			ldcp->hstate |= ATTR_ACK_SENT;
51744647Sraghuram 			DBG2(vgenp, ldcp, "ATTR_ACK_SENT \n");
51751991Sheppo 		} else {
51761991Sheppo 			/* failed */
51774647Sraghuram 			DWARN(vgenp, ldcp, "ATTR_NACK_SENT \n");
51782793Slm66018 			return (VGEN_FAILURE);
51791991Sheppo 		}
51801991Sheppo 
51811991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
51821991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
51831991Sheppo 		}
51841991Sheppo 
51851991Sheppo 		break;
51861991Sheppo 
51871991Sheppo 	case VIO_SUBTYPE_ACK:
51881991Sheppo 
51897529SSriharsha.Basavapatna@Sun.COM 		if (VGEN_VER_GTEQ(ldcp, 1, 4)) {
51907529SSriharsha.Basavapatna@Sun.COM 			/*
51917529SSriharsha.Basavapatna@Sun.COM 			 * Versions >= 1.4:
51927529SSriharsha.Basavapatna@Sun.COM 			 * The ack msg sent by the peer contains the minimum of
51937529SSriharsha.Basavapatna@Sun.COM 			 * our mtu (that we had sent in our attr info) and the
51947529SSriharsha.Basavapatna@Sun.COM 			 * peer's mtu.
51957529SSriharsha.Basavapatna@Sun.COM 			 *
51967529SSriharsha.Basavapatna@Sun.COM 			 * If we have sent an ack for the attr info msg from
51977529SSriharsha.Basavapatna@Sun.COM 			 * the peer, check if the mtu that was computed then
51987529SSriharsha.Basavapatna@Sun.COM 			 * (saved in local hparams) matches the mtu that the
51997529SSriharsha.Basavapatna@Sun.COM 			 * peer has ack'd. If they don't match, we fail the
52007529SSriharsha.Basavapatna@Sun.COM 			 * handshake.
52017529SSriharsha.Basavapatna@Sun.COM 			 */
52027529SSriharsha.Basavapatna@Sun.COM 			if (ldcp->hstate & ATTR_ACK_SENT) {
52037529SSriharsha.Basavapatna@Sun.COM 				if (lp->mtu != msg->mtu) {
52047529SSriharsha.Basavapatna@Sun.COM 					return (VGEN_FAILURE);
52057529SSriharsha.Basavapatna@Sun.COM 				}
52067529SSriharsha.Basavapatna@Sun.COM 			} else {
52077529SSriharsha.Basavapatna@Sun.COM 				/*
52087529SSriharsha.Basavapatna@Sun.COM 				 * If the mtu ack'd by the peer is > our mtu
52097529SSriharsha.Basavapatna@Sun.COM 				 * fail handshake. Otherwise, save the mtu, so
52107529SSriharsha.Basavapatna@Sun.COM 				 * we can validate it when we receive attr info
52117529SSriharsha.Basavapatna@Sun.COM 				 * from our peer.
52127529SSriharsha.Basavapatna@Sun.COM 				 */
52137529SSriharsha.Basavapatna@Sun.COM 				if (msg->mtu > lp->mtu) {
52147529SSriharsha.Basavapatna@Sun.COM 					return (VGEN_FAILURE);
52157529SSriharsha.Basavapatna@Sun.COM 				}
52167529SSriharsha.Basavapatna@Sun.COM 				if (msg->mtu <= lp->mtu) {
52177529SSriharsha.Basavapatna@Sun.COM 					lp->mtu = msg->mtu;
52187529SSriharsha.Basavapatna@Sun.COM 				}
52197529SSriharsha.Basavapatna@Sun.COM 			}
52207529SSriharsha.Basavapatna@Sun.COM 		}
52217529SSriharsha.Basavapatna@Sun.COM 
52221991Sheppo 		ldcp->hstate |= ATTR_ACK_RCVD;
52231991Sheppo 
52244647Sraghuram 		DBG2(vgenp, ldcp, "ATTR_ACK_RCVD \n");
52251991Sheppo 
52261991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
52271991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
52281991Sheppo 		}
52291991Sheppo 		break;
52301991Sheppo 
52311991Sheppo 	case VIO_SUBTYPE_NACK:
52321991Sheppo 
52334647Sraghuram 		DBG2(vgenp, ldcp, "ATTR_NACK_RCVD \n");
52342793Slm66018 		return (VGEN_FAILURE);
52351991Sheppo 	}
52364647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
52372793Slm66018 	return (VGEN_SUCCESS);
52381991Sheppo }
52391991Sheppo 
52401991Sheppo /* Check if the dring info msg is ok */
52411991Sheppo static int
52421991Sheppo vgen_check_dring_reg(vio_dring_reg_msg_t *msg)
52431991Sheppo {
52441991Sheppo 	/* check if msg contents are ok */
52451991Sheppo 	if ((msg->num_descriptors < 128) || (msg->descriptor_size <
52461991Sheppo 	    sizeof (vnet_public_desc_t))) {
52471991Sheppo 		return (VGEN_FAILURE);
52481991Sheppo 	}
52491991Sheppo 	return (VGEN_SUCCESS);
52501991Sheppo }
52511991Sheppo 
52521991Sheppo /*
52531991Sheppo  * Handle a descriptor ring register msg from the peer or an ACK/NACK from
52541991Sheppo  * the peer to a dring register msg that we sent.
52551991Sheppo  */
52562793Slm66018 static int
52571991Sheppo vgen_handle_dring_reg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
52581991Sheppo {
52591991Sheppo 	vio_dring_reg_msg_t *msg = (vio_dring_reg_msg_t *)tagp;
52601991Sheppo 	ldc_mem_cookie_t dcookie;
52614647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
52621991Sheppo 	int ack = 0;
52631991Sheppo 	int rv = 0;
52641991Sheppo 
52654647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
52661991Sheppo 	if (ldcp->hphase < VH_PHASE2) {
52671991Sheppo 		/* dring_info can be rcvd in any of the phases after Phase1 */
52684647Sraghuram 		DWARN(vgenp, ldcp,
52694647Sraghuram 		    "Rcvd DRING_INFO Subtype (%d), Invalid Phase(%u)\n",
52704650Sraghuram 		    tagp->vio_subtype, ldcp->hphase);
52712793Slm66018 		return (VGEN_FAILURE);
52721991Sheppo 	}
52731991Sheppo 	switch (tagp->vio_subtype) {
52741991Sheppo 	case VIO_SUBTYPE_INFO:
52751991Sheppo 
52764647Sraghuram 		DBG2(vgenp, ldcp, "DRING_INFO_RCVD \n");
52771991Sheppo 		ldcp->hstate |= DRING_INFO_RCVD;
52781991Sheppo 		bcopy((msg->cookie), &dcookie, sizeof (dcookie));
52791991Sheppo 
52801991Sheppo 		ASSERT(msg->ncookies == 1);
52811991Sheppo 
52821991Sheppo 		if (vgen_check_dring_reg(msg) == VGEN_SUCCESS) {
52831991Sheppo 			/*
52841991Sheppo 			 * verified dring info msg to be ok,
52851991Sheppo 			 * now try to map the remote dring.
52861991Sheppo 			 */
52871991Sheppo 			rv = vgen_init_rxds(ldcp, msg->num_descriptors,
52881991Sheppo 			    msg->descriptor_size, &dcookie,
52891991Sheppo 			    msg->ncookies);
52901991Sheppo 			if (rv == DDI_SUCCESS) {
52911991Sheppo 				/* now we can ack the peer */
52921991Sheppo 				ack = 1;
52931991Sheppo 			}
52941991Sheppo 		}
52951991Sheppo 		if (ack == 0) {
52961991Sheppo 			/* failed, send NACK */
52971991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
52981991Sheppo 		} else {
52991991Sheppo 			if (!(ldcp->peer_hparams.dring_ready)) {
53001991Sheppo 
53011991Sheppo 				/* save peer's dring_info values */
53021991Sheppo 				bcopy(&dcookie,
53031991Sheppo 				    &(ldcp->peer_hparams.dring_cookie),
53041991Sheppo 				    sizeof (dcookie));
53051991Sheppo 				ldcp->peer_hparams.num_desc =
53064650Sraghuram 				    msg->num_descriptors;
53071991Sheppo 				ldcp->peer_hparams.desc_size =
53084650Sraghuram 				    msg->descriptor_size;
53091991Sheppo 				ldcp->peer_hparams.num_dcookies =
53104650Sraghuram 				    msg->ncookies;
53111991Sheppo 
53121991Sheppo 				/* set dring_ident for the peer */
53131991Sheppo 				ldcp->peer_hparams.dring_ident =
53144650Sraghuram 				    (uint64_t)ldcp->rxdp;
53151991Sheppo 				/* return the dring_ident in ack msg */
53161991Sheppo 				msg->dring_ident =
53174650Sraghuram 				    (uint64_t)ldcp->rxdp;
53181991Sheppo 
53191991Sheppo 				ldcp->peer_hparams.dring_ready = B_TRUE;
53201991Sheppo 			}
53211991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_ACK;
53221991Sheppo 		}
53231991Sheppo 		tagp->vio_sid = ldcp->local_sid;
53241991Sheppo 		/* send reply msg back to peer */
53252793Slm66018 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msg),
53262793Slm66018 		    B_FALSE);
53272793Slm66018 		if (rv != VGEN_SUCCESS) {
53282793Slm66018 			return (rv);
53291991Sheppo 		}
53301991Sheppo 
53311991Sheppo 		if (ack) {
53321991Sheppo 			ldcp->hstate |= DRING_ACK_SENT;
53334647Sraghuram 			DBG2(vgenp, ldcp, "DRING_ACK_SENT");
53341991Sheppo 		} else {
53354647Sraghuram 			DWARN(vgenp, ldcp, "DRING_NACK_SENT");
53362793Slm66018 			return (VGEN_FAILURE);
53371991Sheppo 		}
53381991Sheppo 
53391991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
53401991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
53411991Sheppo 		}
53421991Sheppo 
53431991Sheppo 		break;
53441991Sheppo 
53451991Sheppo 	case VIO_SUBTYPE_ACK:
53461991Sheppo 
53471991Sheppo 		ldcp->hstate |= DRING_ACK_RCVD;
53481991Sheppo 
53494647Sraghuram 		DBG2(vgenp, ldcp, "DRING_ACK_RCVD");
53501991Sheppo 
53511991Sheppo 		if (!(ldcp->local_hparams.dring_ready)) {
53521991Sheppo 			/* local dring is now ready */
53531991Sheppo 			ldcp->local_hparams.dring_ready = B_TRUE;
53541991Sheppo 
53551991Sheppo 			/* save dring_ident acked by peer */
53561991Sheppo 			ldcp->local_hparams.dring_ident =
53574650Sraghuram 			    msg->dring_ident;
53581991Sheppo 		}
53591991Sheppo 
53601991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
53611991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
53621991Sheppo 		}
53631991Sheppo 
53641991Sheppo 		break;
53651991Sheppo 
53661991Sheppo 	case VIO_SUBTYPE_NACK:
53671991Sheppo 
53684647Sraghuram 		DBG2(vgenp, ldcp, "DRING_NACK_RCVD");
53692793Slm66018 		return (VGEN_FAILURE);
53701991Sheppo 	}
53714647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
53722793Slm66018 	return (VGEN_SUCCESS);
53731991Sheppo }
53741991Sheppo 
53751991Sheppo /*
53761991Sheppo  * Handle a rdx info msg from the peer or an ACK/NACK
53771991Sheppo  * from the peer to a rdx info msg that we sent.
53781991Sheppo  */
53792793Slm66018 static int
53801991Sheppo vgen_handle_rdx_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
53811991Sheppo {
53822793Slm66018 	int rv = 0;
53834647Sraghuram 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
53844647Sraghuram 
53854647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
53861991Sheppo 	if (ldcp->hphase != VH_PHASE3) {
53874647Sraghuram 		DWARN(vgenp, ldcp,
53884647Sraghuram 		    "Rcvd RDX_INFO Subtype (%d), Invalid Phase(%u)\n",
53894650Sraghuram 		    tagp->vio_subtype, ldcp->hphase);
53902793Slm66018 		return (VGEN_FAILURE);
53911991Sheppo 	}
53921991Sheppo 	switch (tagp->vio_subtype) {
53931991Sheppo 	case VIO_SUBTYPE_INFO:
53941991Sheppo 
53954647Sraghuram 		DBG2(vgenp, ldcp, "RDX_INFO_RCVD \n");
53961991Sheppo 		ldcp->hstate |= RDX_INFO_RCVD;
53971991Sheppo 
53981991Sheppo 		tagp->vio_subtype = VIO_SUBTYPE_ACK;
53991991Sheppo 		tagp->vio_sid = ldcp->local_sid;
54001991Sheppo 		/* send reply msg back to peer */
54012793Slm66018 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (vio_rdx_msg_t),
54022793Slm66018 		    B_FALSE);
54032793Slm66018 		if (rv != VGEN_SUCCESS) {
54042793Slm66018 			return (rv);
54051991Sheppo 		}
54061991Sheppo 
54071991Sheppo 		ldcp->hstate |= RDX_ACK_SENT;
54084647Sraghuram 		DBG2(vgenp, ldcp, "RDX_ACK_SENT \n");
54091991Sheppo 
54101991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
54111991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
54121991Sheppo 		}
54131991Sheppo 
54141991Sheppo 		break;
54151991Sheppo 
54161991Sheppo 	case VIO_SUBTYPE_ACK:
54171991Sheppo 
54181991Sheppo 		ldcp->hstate |= RDX_ACK_RCVD;
54191991Sheppo 
54204647Sraghuram 		DBG2(vgenp, ldcp, "RDX_ACK_RCVD \n");
54211991Sheppo 
54221991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
54231991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
54241991Sheppo 		}
54251991Sheppo 		break;
54261991Sheppo 
54271991Sheppo 	case VIO_SUBTYPE_NACK:
54281991Sheppo 
54294647Sraghuram 		DBG2(vgenp, ldcp, "RDX_NACK_RCVD \n");
54302793Slm66018 		return (VGEN_FAILURE);
54311991Sheppo 	}
54324647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
54332793Slm66018 	return (VGEN_SUCCESS);
54341991Sheppo }
54351991Sheppo 
54361991Sheppo /* Handle ACK/NACK from vsw to a set multicast msg that we sent */
54372793Slm66018 static int
54381991Sheppo vgen_handle_mcast_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
54391991Sheppo {
54401991Sheppo 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
54411991Sheppo 	vnet_mcast_msg_t *msgp = (vnet_mcast_msg_t *)tagp;
54421991Sheppo 	struct ether_addr *addrp;
54431991Sheppo 	int count;
54441991Sheppo 	int i;
54451991Sheppo 
54464647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
54471991Sheppo 	switch (tagp->vio_subtype) {
54481991Sheppo 
54491991Sheppo 	case VIO_SUBTYPE_INFO:
54501991Sheppo 
54511991Sheppo 		/* vnet shouldn't recv set mcast msg, only vsw handles it */
54524647Sraghuram 		DWARN(vgenp, ldcp, "rcvd SET_MCAST_INFO \n");
54531991Sheppo 		break;
54541991Sheppo 
54551991Sheppo 	case VIO_SUBTYPE_ACK:
54561991Sheppo 
54571991Sheppo 		/* success adding/removing multicast addr */
54584647Sraghuram 		DBG1(vgenp, ldcp, "rcvd SET_MCAST_ACK \n");
54591991Sheppo 		break;
54601991Sheppo 
54611991Sheppo 	case VIO_SUBTYPE_NACK:
54621991Sheppo 
54634647Sraghuram 		DWARN(vgenp, ldcp, "rcvd SET_MCAST_NACK \n");
54641991Sheppo 		if (!(msgp->set)) {
54651991Sheppo 			/* multicast remove request failed */
54661991Sheppo 			break;
54671991Sheppo 		}
54681991Sheppo 
54691991Sheppo 		/* multicast add request failed */
54701991Sheppo 		for (count = 0; count < msgp->count; count++) {
54711991Sheppo 			addrp = &(msgp->mca[count]);
54721991Sheppo 
54731991Sheppo 			/* delete address from the table */
54741991Sheppo 			for (i = 0; i < vgenp->mccount; i++) {
54751991Sheppo 				if (ether_cmp(addrp,
54761991Sheppo 				    &(vgenp->mctab[i])) == 0) {
54771991Sheppo 					if (vgenp->mccount > 1) {
54784647Sraghuram 						int t = vgenp->mccount - 1;
54791991Sheppo 						vgenp->mctab[i] =
54804650Sraghuram 						    vgenp->mctab[t];
54811991Sheppo 					}
54821991Sheppo 					vgenp->mccount--;
54831991Sheppo 					break;
54841991Sheppo 				}
54851991Sheppo 			}
54861991Sheppo 		}
54871991Sheppo 		break;
54881991Sheppo 
54891991Sheppo 	}
54904647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
54912793Slm66018 
54922793Slm66018 	return (VGEN_SUCCESS);
54931991Sheppo }
54941991Sheppo 
54951991Sheppo /* handler for control messages received from the peer ldc end-point */
54962793Slm66018 static int
54971991Sheppo vgen_handle_ctrlmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
54981991Sheppo {
54992793Slm66018 	int rv = 0;
55004647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
55014647Sraghuram 
55024647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
55031991Sheppo 	switch (tagp->vio_subtype_env) {
55041991Sheppo 
55051991Sheppo 	case VIO_VER_INFO:
55062793Slm66018 		rv = vgen_handle_version_negotiate(ldcp, tagp);
55071991Sheppo 		break;
55081991Sheppo 
55091991Sheppo 	case VIO_ATTR_INFO:
55102793Slm66018 		rv = vgen_handle_attr_info(ldcp, tagp);
55111991Sheppo 		break;
55121991Sheppo 
55131991Sheppo 	case VIO_DRING_REG:
55142793Slm66018 		rv = vgen_handle_dring_reg(ldcp, tagp);
55151991Sheppo 		break;
55161991Sheppo 
55171991Sheppo 	case VIO_RDX:
55182793Slm66018 		rv = vgen_handle_rdx_info(ldcp, tagp);
55191991Sheppo 		break;
55201991Sheppo 
55211991Sheppo 	case VNET_MCAST_INFO:
55222793Slm66018 		rv = vgen_handle_mcast_info(ldcp, tagp);
55231991Sheppo 		break;
55241991Sheppo 
55256495Sspeer 	case VIO_DDS_INFO:
55266495Sspeer 		rv = vgen_dds_rx(ldcp, tagp);
55276495Sspeer 		break;
55281991Sheppo 	}
55292793Slm66018 
55304647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
55312793Slm66018 	return (rv);
55321991Sheppo }
55331991Sheppo 
55341991Sheppo /* handler for data messages received from the peer ldc end-point */
55352793Slm66018 static int
55365935Ssb155480 vgen_handle_datamsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp, uint32_t msglen)
55371991Sheppo {
55382793Slm66018 	int rv = 0;
55394647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
55404647Sraghuram 
55414647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
55421991Sheppo 
55431991Sheppo 	if (ldcp->hphase != VH_DONE)
55442793Slm66018 		return (rv);
55455935Ssb155480 
55465935Ssb155480 	if (tagp->vio_subtype == VIO_SUBTYPE_INFO) {
55475935Ssb155480 		rv = vgen_check_datamsg_seq(ldcp, tagp);
55485935Ssb155480 		if (rv != 0) {
55495935Ssb155480 			return (rv);
55505935Ssb155480 		}
55515935Ssb155480 	}
55525935Ssb155480 
55531991Sheppo 	switch (tagp->vio_subtype_env) {
55541991Sheppo 	case VIO_DRING_DATA:
55554647Sraghuram 		rv = vgen_handle_dring_data(ldcp, tagp);
55561991Sheppo 		break;
55575935Ssb155480 
55585935Ssb155480 	case VIO_PKT_DATA:
55595935Ssb155480 		ldcp->rx_pktdata((void *)ldcp, (void *)tagp, msglen);
55605935Ssb155480 		break;
55611991Sheppo 	default:
55621991Sheppo 		break;
55631991Sheppo 	}
55641991Sheppo 
55654647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
55662793Slm66018 	return (rv);
55671991Sheppo }
55681991Sheppo 
55695935Ssb155480 /*
55705935Ssb155480  * dummy pkt data handler function for vnet protocol version 1.0
55715935Ssb155480  */
55725935Ssb155480 static void
55735935Ssb155480 vgen_handle_pkt_data_nop(void *arg1, void *arg2, uint32_t msglen)
55745935Ssb155480 {
55755935Ssb155480 	_NOTE(ARGUNUSED(arg1, arg2, msglen))
55765935Ssb155480 }
55775935Ssb155480 
55785935Ssb155480 /*
55795935Ssb155480  * This function handles raw pkt data messages received over the channel.
55805935Ssb155480  * Currently, only priority-eth-type frames are received through this mechanism.
55815935Ssb155480  * In this case, the frame(data) is present within the message itself which
55825935Ssb155480  * is copied into an mblk before sending it up the stack.
55835935Ssb155480  */
55845935Ssb155480 static void
55855935Ssb155480 vgen_handle_pkt_data(void *arg1, void *arg2, uint32_t msglen)
55865935Ssb155480 {
55875935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg1;
55885935Ssb155480 	vio_raw_data_msg_t	*pkt	= (vio_raw_data_msg_t *)arg2;
55895935Ssb155480 	uint32_t		size;
55905935Ssb155480 	mblk_t			*mp;
55915935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
55925935Ssb155480 	vgen_stats_t		*statsp = &ldcp->stats;
55936419Ssb155480 	vgen_hparams_t		*lp = &ldcp->local_hparams;
55946495Sspeer 	vio_net_rx_cb_t		vrx_cb;
55955935Ssb155480 
55965935Ssb155480 	ASSERT(MUTEX_HELD(&ldcp->cblock));
55975935Ssb155480 
55985935Ssb155480 	mutex_exit(&ldcp->cblock);
55995935Ssb155480 
56005935Ssb155480 	size = msglen - VIO_PKT_DATA_HDRSIZE;
56016419Ssb155480 	if (size < ETHERMIN || size > lp->mtu) {
56025935Ssb155480 		(void) atomic_inc_32(&statsp->rx_pri_fail);
56035935Ssb155480 		goto exit;
56045935Ssb155480 	}
56055935Ssb155480 
56065935Ssb155480 	mp = vio_multipool_allocb(&ldcp->vmp, size);
56075935Ssb155480 	if (mp == NULL) {
56085935Ssb155480 		mp = allocb(size, BPRI_MED);
56095935Ssb155480 		if (mp == NULL) {
56105935Ssb155480 			(void) atomic_inc_32(&statsp->rx_pri_fail);
56115935Ssb155480 			DWARN(vgenp, ldcp, "allocb failure, "
56125935Ssb155480 			    "unable to process priority frame\n");
56135935Ssb155480 			goto exit;
56145935Ssb155480 		}
56155935Ssb155480 	}
56165935Ssb155480 
56175935Ssb155480 	/* copy the frame from the payload of raw data msg into the mblk */
56185935Ssb155480 	bcopy(pkt->data, mp->b_rptr, size);
56195935Ssb155480 	mp->b_wptr = mp->b_rptr + size;
56205935Ssb155480 
56215935Ssb155480 	/* update stats */
56225935Ssb155480 	(void) atomic_inc_64(&statsp->rx_pri_packets);
56235935Ssb155480 	(void) atomic_add_64(&statsp->rx_pri_bytes, size);
56245935Ssb155480 
56256495Sspeer 	/* send up; call vrx_cb() as cblock is already released */
56266495Sspeer 	vrx_cb = ldcp->portp->vcb.vio_net_rx_cb;
56276495Sspeer 	vrx_cb(ldcp->portp->vhp, mp);
56285935Ssb155480 
56295935Ssb155480 exit:
56305935Ssb155480 	mutex_enter(&ldcp->cblock);
56315935Ssb155480 }
56325935Ssb155480 
56332793Slm66018 static int
56342336Snarayan vgen_send_dring_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp, uint32_t start,
56352336Snarayan     int32_t end, uint8_t pstate)
56362336Snarayan {
56374647Sraghuram 	int rv = 0;
56384647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
56392336Snarayan 	vio_dring_msg_t *msgp = (vio_dring_msg_t *)tagp;
56402336Snarayan 
56412336Snarayan 	tagp->vio_subtype = VIO_SUBTYPE_ACK;
56422336Snarayan 	tagp->vio_sid = ldcp->local_sid;
56432336Snarayan 	msgp->start_idx = start;
56442336Snarayan 	msgp->end_idx = end;
56452336Snarayan 	msgp->dring_process_state = pstate;
56462793Slm66018 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msgp), B_FALSE);
56472793Slm66018 	if (rv != VGEN_SUCCESS) {
56484647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
56492336Snarayan 	}
56502793Slm66018 	return (rv);
56512336Snarayan }
56522336Snarayan 
56532793Slm66018 static int
56544647Sraghuram vgen_handle_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
56551991Sheppo {
56562793Slm66018 	int rv = 0;
56574647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
56584647Sraghuram 
56594647Sraghuram 
56604647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
56611991Sheppo 	switch (tagp->vio_subtype) {
56621991Sheppo 
56631991Sheppo 	case VIO_SUBTYPE_INFO:
56641991Sheppo 		/*
56654647Sraghuram 		 * To reduce the locking contention, release the
56664647Sraghuram 		 * cblock here and re-acquire it once we are done
56674647Sraghuram 		 * receiving packets.
56681991Sheppo 		 */
56694647Sraghuram 		mutex_exit(&ldcp->cblock);
56704647Sraghuram 		mutex_enter(&ldcp->rxlock);
56714647Sraghuram 		rv = vgen_handle_dring_data_info(ldcp, tagp);
56724647Sraghuram 		mutex_exit(&ldcp->rxlock);
56734647Sraghuram 		mutex_enter(&ldcp->cblock);
56744647Sraghuram 		break;
56754647Sraghuram 
56764647Sraghuram 	case VIO_SUBTYPE_ACK:
56774647Sraghuram 		rv = vgen_handle_dring_data_ack(ldcp, tagp);
56784647Sraghuram 		break;
56794647Sraghuram 
56804647Sraghuram 	case VIO_SUBTYPE_NACK:
56814647Sraghuram 		rv = vgen_handle_dring_data_nack(ldcp, tagp);
56824647Sraghuram 		break;
56834647Sraghuram 	}
56844647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
56854647Sraghuram 	return (rv);
56864647Sraghuram }
56874647Sraghuram 
56884647Sraghuram static int
56894647Sraghuram vgen_handle_dring_data_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
56904647Sraghuram {
56914647Sraghuram 	uint32_t start;
56924647Sraghuram 	int32_t end;
56934647Sraghuram 	int rv = 0;
56944647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
56954647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
56964647Sraghuram #ifdef VGEN_HANDLE_LOST_PKTS
56975373Sraghuram 	vgen_stats_t *statsp = &ldcp->stats;
56984647Sraghuram 	uint32_t rxi;
56994647Sraghuram 	int n;
57004647Sraghuram #endif
57014647Sraghuram 
57024647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
57034647Sraghuram 
57044647Sraghuram 	start = dringmsg->start_idx;
57054647Sraghuram 	end = dringmsg->end_idx;
57064647Sraghuram 	/*
57074647Sraghuram 	 * received a data msg, which contains the start and end
57084647Sraghuram 	 * indices of the descriptors within the rx ring holding data,
57094647Sraghuram 	 * the seq_num of data packet corresponding to the start index,
57104647Sraghuram 	 * and the dring_ident.
57114647Sraghuram 	 * We can now read the contents of each of these descriptors
57124647Sraghuram 	 * and gather data from it.
57134647Sraghuram 	 */
57144647Sraghuram 	DBG1(vgenp, ldcp, "INFO: start(%d), end(%d)\n",
57154650Sraghuram 	    start, end);
57164647Sraghuram 
57174647Sraghuram 	/* validate rx start and end indeces */
57184647Sraghuram 	if (!(CHECK_RXI(start, ldcp)) || ((end != -1) &&
57194647Sraghuram 	    !(CHECK_RXI(end, ldcp)))) {
57204647Sraghuram 		DWARN(vgenp, ldcp, "Invalid Rx start(%d) or end(%d)\n",
57214647Sraghuram 		    start, end);
57224647Sraghuram 		/* drop the message if invalid index */
57234647Sraghuram 		return (rv);
57244647Sraghuram 	}
57254647Sraghuram 
57264647Sraghuram 	/* validate dring_ident */
57274647Sraghuram 	if (dringmsg->dring_ident != ldcp->peer_hparams.dring_ident) {
57284647Sraghuram 		DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
57294647Sraghuram 		    dringmsg->dring_ident);
57304647Sraghuram 		/* invalid dring_ident, drop the msg */
57314647Sraghuram 		return (rv);
57324647Sraghuram 	}
57331991Sheppo #ifdef DEBUG
57344647Sraghuram 	if (vgen_trigger_rxlost) {
57354647Sraghuram 		/* drop this msg to simulate lost pkts for debugging */
57364647Sraghuram 		vgen_trigger_rxlost = 0;
57374647Sraghuram 		return (rv);
57384647Sraghuram 	}
57391991Sheppo #endif
57401991Sheppo 
57411991Sheppo #ifdef	VGEN_HANDLE_LOST_PKTS
57421991Sheppo 
57434647Sraghuram 	/* receive start index doesn't match expected index */
57444647Sraghuram 	if (ldcp->next_rxi != start) {
57454647Sraghuram 		DWARN(vgenp, ldcp, "next_rxi(%d) != start(%d)\n",
57464647Sraghuram 		    ldcp->next_rxi, start);
57474647Sraghuram 
57484647Sraghuram 		/* calculate the number of pkts lost */
57494647Sraghuram 		if (start >= ldcp->next_rxi) {
57504647Sraghuram 			n = start - ldcp->next_rxi;
57514647Sraghuram 		} else  {
57524647Sraghuram 			n = ldcp->num_rxds - (ldcp->next_rxi - start);
57534647Sraghuram 		}
57544647Sraghuram 
57555935Ssb155480 		statsp->rx_lost_pkts += n;
57565935Ssb155480 		tagp->vio_subtype = VIO_SUBTYPE_NACK;
57575935Ssb155480 		tagp->vio_sid = ldcp->local_sid;
57585935Ssb155480 		/* indicate the range of lost descriptors */
57595935Ssb155480 		dringmsg->start_idx = ldcp->next_rxi;
57605935Ssb155480 		rxi = start;
57615935Ssb155480 		DECR_RXI(rxi, ldcp);
57625935Ssb155480 		dringmsg->end_idx = rxi;
57635935Ssb155480 		/* dring ident is left unchanged */
57645935Ssb155480 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp,
57655935Ssb155480 		    sizeof (*dringmsg), B_FALSE);
57665935Ssb155480 		if (rv != VGEN_SUCCESS) {
57675935Ssb155480 			DWARN(vgenp, ldcp,
57685935Ssb155480 			    "vgen_sendmsg failed, stype:NACK\n");
57694647Sraghuram 			return (rv);
57701991Sheppo 		}
57711991Sheppo 		/*
57725935Ssb155480 		 * treat this range of descrs/pkts as dropped
57735935Ssb155480 		 * and set the new expected value of next_rxi
57745935Ssb155480 		 * and continue(below) to process from the new
57755935Ssb155480 		 * start index.
57761991Sheppo 		 */
57775935Ssb155480 		ldcp->next_rxi = start;
57784647Sraghuram 	}
57794647Sraghuram 
57804647Sraghuram #endif	/* VGEN_HANDLE_LOST_PKTS */
57814647Sraghuram 
57824647Sraghuram 	/* Now receive messages */
57834647Sraghuram 	rv = vgen_process_dring_data(ldcp, tagp);
57844647Sraghuram 
57854647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
57864647Sraghuram 	return (rv);
57874647Sraghuram }
57884647Sraghuram 
57894647Sraghuram static int
57904647Sraghuram vgen_process_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
57914647Sraghuram {
57924647Sraghuram 	boolean_t set_ack_start = B_FALSE;
57934647Sraghuram 	uint32_t start;
57944647Sraghuram 	uint32_t ack_end;
57954647Sraghuram 	uint32_t next_rxi;
57964647Sraghuram 	uint32_t rxi;
57974647Sraghuram 	int count = 0;
57984647Sraghuram 	int rv = 0;
57994647Sraghuram 	uint32_t retries = 0;
58004647Sraghuram 	vgen_stats_t *statsp;
58016845Sha137994 	vnet_public_desc_t rxd;
58024647Sraghuram 	vio_dring_entry_hdr_t *hdrp;
58034647Sraghuram 	mblk_t *bp = NULL;
58044647Sraghuram 	mblk_t *bpt = NULL;
58054647Sraghuram 	uint32_t ack_start;
58064647Sraghuram 	boolean_t rxd_err = B_FALSE;
58074647Sraghuram 	mblk_t *mp = NULL;
58084647Sraghuram 	size_t nbytes;
58094647Sraghuram 	boolean_t ack_needed = B_FALSE;
58104647Sraghuram 	size_t nread;
58114647Sraghuram 	uint64_t off = 0;
58124647Sraghuram 	struct ether_header *ehp;
58134647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
58144647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
58157529SSriharsha.Basavapatna@Sun.COM 	vgen_hparams_t	*lp = &ldcp->local_hparams;
58164647Sraghuram 
58174647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
58184647Sraghuram 
58195373Sraghuram 	statsp = &ldcp->stats;
58204647Sraghuram 	start = dringmsg->start_idx;
58214647Sraghuram 
58224647Sraghuram 	/*
58234647Sraghuram 	 * start processing the descriptors from the specified
58244647Sraghuram 	 * start index, up to the index a descriptor is not ready
58254647Sraghuram 	 * to be processed or we process the entire descriptor ring
58264647Sraghuram 	 * and wrap around upto the start index.
58274647Sraghuram 	 */
58284647Sraghuram 
58294647Sraghuram 	/* need to set the start index of descriptors to be ack'd */
58304647Sraghuram 	set_ack_start = B_TRUE;
58314647Sraghuram 
58324647Sraghuram 	/* index upto which we have ack'd */
58334647Sraghuram 	ack_end = start;
58344647Sraghuram 	DECR_RXI(ack_end, ldcp);
58354647Sraghuram 
58364647Sraghuram 	next_rxi = rxi =  start;
58374647Sraghuram 	do {
58384647Sraghuram vgen_recv_retry:
58396845Sha137994 		rv = vnet_dring_entry_copy(&(ldcp->rxdp[rxi]), &rxd,
58406845Sha137994 		    ldcp->dring_mtype, ldcp->rx_dhandle, rxi, rxi);
58414647Sraghuram 		if (rv != 0) {
58424647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_dring_acquire() failed"
58434647Sraghuram 			    " rv(%d)\n", rv);
58444647Sraghuram 			statsp->ierrors++;
58454647Sraghuram 			return (rv);
58464647Sraghuram 		}
58474647Sraghuram 
58486845Sha137994 		hdrp = &rxd.hdr;
58494647Sraghuram 
58504647Sraghuram 		if (hdrp->dstate != VIO_DESC_READY) {
58514647Sraghuram 			/*
58525935Ssb155480 			 * Before waiting and retry here, send up
58535935Ssb155480 			 * the packets that are received already
58544647Sraghuram 			 */
58554647Sraghuram 			if (bp != NULL) {
58564647Sraghuram 				DTRACE_PROBE1(vgen_rcv_msgs, int, count);
58575935Ssb155480 				vgen_rx(ldcp, bp);
58584647Sraghuram 				count = 0;
58594647Sraghuram 				bp = bpt = NULL;
58604647Sraghuram 			}
58614647Sraghuram 			/*
58624647Sraghuram 			 * descriptor is not ready.
58634647Sraghuram 			 * retry descriptor acquire, stop processing
58644647Sraghuram 			 * after max # retries.
58654647Sraghuram 			 */
58664647Sraghuram 			if (retries == vgen_recv_retries)
58674647Sraghuram 				break;
58684647Sraghuram 			retries++;
58694647Sraghuram 			drv_usecwait(vgen_recv_delay);
58704647Sraghuram 			goto vgen_recv_retry;
58714647Sraghuram 		}
58724647Sraghuram 		retries = 0;
58734647Sraghuram 
58744647Sraghuram 		if (set_ack_start) {
58754647Sraghuram 			/*
58764647Sraghuram 			 * initialize the start index of the range
58774647Sraghuram 			 * of descriptors to be ack'd.
58784647Sraghuram 			 */
58794647Sraghuram 			ack_start = rxi;
58804647Sraghuram 			set_ack_start = B_FALSE;
58814647Sraghuram 		}
58824647Sraghuram 
58836845Sha137994 		if ((rxd.nbytes < ETHERMIN) ||
58847529SSriharsha.Basavapatna@Sun.COM 		    (rxd.nbytes > lp->mtu) ||
58856845Sha137994 		    (rxd.ncookies == 0) ||
58866845Sha137994 		    (rxd.ncookies > MAX_COOKIES)) {
58874647Sraghuram 			rxd_err = B_TRUE;
58884647Sraghuram 		} else {
58894647Sraghuram 			/*
58904647Sraghuram 			 * Try to allocate an mblk from the free pool
58914647Sraghuram 			 * of recv mblks for the channel.
58924647Sraghuram 			 * If this fails, use allocb().
58934647Sraghuram 			 */
58946845Sha137994 			nbytes = (VNET_IPALIGN + rxd.nbytes + 7) & ~7;
58957529SSriharsha.Basavapatna@Sun.COM 			if (nbytes > ldcp->max_rxpool_size) {
58966845Sha137994 				mp = allocb(VNET_IPALIGN + rxd.nbytes + 8,
58974647Sraghuram 				    BPRI_MED);
58987529SSriharsha.Basavapatna@Sun.COM 			} else {
58997529SSriharsha.Basavapatna@Sun.COM 				mp = vio_multipool_allocb(&ldcp->vmp, nbytes);
59007529SSriharsha.Basavapatna@Sun.COM 				if (mp == NULL) {
59017529SSriharsha.Basavapatna@Sun.COM 					statsp->rx_vio_allocb_fail++;
59027529SSriharsha.Basavapatna@Sun.COM 					/*
59037529SSriharsha.Basavapatna@Sun.COM 					 * Data buffer returned by allocb(9F)
59047529SSriharsha.Basavapatna@Sun.COM 					 * is 8byte aligned. We allocate extra
59057529SSriharsha.Basavapatna@Sun.COM 					 * 8 bytes to ensure size is multiple
59067529SSriharsha.Basavapatna@Sun.COM 					 * of 8 bytes for ldc_mem_copy().
59077529SSriharsha.Basavapatna@Sun.COM 					 */
59087529SSriharsha.Basavapatna@Sun.COM 					mp = allocb(VNET_IPALIGN +
59097529SSriharsha.Basavapatna@Sun.COM 					    rxd.nbytes + 8, BPRI_MED);
59107529SSriharsha.Basavapatna@Sun.COM 				}
59111991Sheppo 			}
59124647Sraghuram 		}
59134647Sraghuram 		if ((rxd_err) || (mp == NULL)) {
59144647Sraghuram 			/*
59154647Sraghuram 			 * rxd_err or allocb() failure,
59164647Sraghuram 			 * drop this packet, get next.
59174647Sraghuram 			 */
59184647Sraghuram 			if (rxd_err) {
59192793Slm66018 				statsp->ierrors++;
59204647Sraghuram 				rxd_err = B_FALSE;
59214647Sraghuram 			} else {
59224647Sraghuram 				statsp->rx_allocb_fail++;
59232793Slm66018 			}
59242793Slm66018 
59252793Slm66018 			ack_needed = hdrp->ack;
59264647Sraghuram 
59274647Sraghuram 			/* set descriptor done bit */
59286845Sha137994 			rv = vnet_dring_entry_set_dstate(&(ldcp->rxdp[rxi]),
59296845Sha137994 			    ldcp->dring_mtype, ldcp->rx_dhandle, rxi, rxi,
59306845Sha137994 			    VIO_DESC_DONE);
59312793Slm66018 			if (rv != 0) {
59324647Sraghuram 				DWARN(vgenp, ldcp,
59336845Sha137994 				    "vnet_dring_entry_set_dstate err rv(%d)\n",
59346845Sha137994 				    rv);
59354647Sraghuram 				return (rv);
59362793Slm66018 			}
59372336Snarayan 
59382793Slm66018 			if (ack_needed) {
59392793Slm66018 				ack_needed = B_FALSE;
59401991Sheppo 				/*
59412336Snarayan 				 * sender needs ack for this packet,
59422336Snarayan 				 * ack pkts upto this index.
59431991Sheppo 				 */
59442336Snarayan 				ack_end = rxi;
59452336Snarayan 
59462793Slm66018 				rv = vgen_send_dring_ack(ldcp, tagp,
59474647Sraghuram 				    ack_start, ack_end,
59484647Sraghuram 				    VIO_DP_ACTIVE);
59492793Slm66018 				if (rv != VGEN_SUCCESS) {
59502793Slm66018 					goto error_ret;
59512793Slm66018 				}
59522336Snarayan 
59532336Snarayan 				/* need to set new ack start index */
59542336Snarayan 				set_ack_start = B_TRUE;
59551991Sheppo 			}
59564647Sraghuram 			goto vgen_next_rxi;
59574647Sraghuram 		}
59584647Sraghuram 
59594647Sraghuram 		nread = nbytes;
59604647Sraghuram 		rv = ldc_mem_copy(ldcp->ldc_handle,
59614647Sraghuram 		    (caddr_t)mp->b_rptr, off, &nread,
59626845Sha137994 		    rxd.memcookie, rxd.ncookies, LDC_COPY_IN);
59634647Sraghuram 
59644647Sraghuram 		/* if ldc_mem_copy() failed */
59654647Sraghuram 		if (rv) {
59664647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_copy err rv(%d)\n", rv);
59674647Sraghuram 			statsp->ierrors++;
59684647Sraghuram 			freemsg(mp);
59694647Sraghuram 			goto error_ret;
59704647Sraghuram 		}
59714647Sraghuram 
59724647Sraghuram 		ack_needed = hdrp->ack;
59736845Sha137994 
59746845Sha137994 		rv = vnet_dring_entry_set_dstate(&(ldcp->rxdp[rxi]),
59756845Sha137994 		    ldcp->dring_mtype, ldcp->rx_dhandle, rxi, rxi,
59766845Sha137994 		    VIO_DESC_DONE);
59774647Sraghuram 		if (rv != 0) {
59784647Sraghuram 			DWARN(vgenp, ldcp,
59796845Sha137994 			    "vnet_dring_entry_set_dstate err rv(%d)\n", rv);
59804647Sraghuram 			goto error_ret;
59814647Sraghuram 		}
59824647Sraghuram 
59834647Sraghuram 		mp->b_rptr += VNET_IPALIGN;
59844647Sraghuram 
59854647Sraghuram 		if (ack_needed) {
59864647Sraghuram 			ack_needed = B_FALSE;
59874647Sraghuram 			/*
59884647Sraghuram 			 * sender needs ack for this packet,
59894647Sraghuram 			 * ack pkts upto this index.
59904647Sraghuram 			 */
59914647Sraghuram 			ack_end = rxi;
59924647Sraghuram 
59934647Sraghuram 			rv = vgen_send_dring_ack(ldcp, tagp,
59944647Sraghuram 			    ack_start, ack_end, VIO_DP_ACTIVE);
59954647Sraghuram 			if (rv != VGEN_SUCCESS) {
59964647Sraghuram 				goto error_ret;
59971991Sheppo 			}
59981991Sheppo 
59994647Sraghuram 			/* need to set new ack start index */
60004647Sraghuram 			set_ack_start = B_TRUE;
60014647Sraghuram 		}
60024647Sraghuram 
60034647Sraghuram 		if (nread != nbytes) {
60044647Sraghuram 			DWARN(vgenp, ldcp,
60054647Sraghuram 			    "ldc_mem_copy nread(%lx), nbytes(%lx)\n",
60064647Sraghuram 			    nread, nbytes);
60074647Sraghuram 			statsp->ierrors++;
60084647Sraghuram 			freemsg(mp);
60094647Sraghuram 			goto vgen_next_rxi;
60104647Sraghuram 		}
60114647Sraghuram 
60124647Sraghuram 		/* point to the actual end of data */
60136845Sha137994 		mp->b_wptr = mp->b_rptr + rxd.nbytes;
60144647Sraghuram 
60154647Sraghuram 		/* update stats */
60164647Sraghuram 		statsp->ipackets++;
60176845Sha137994 		statsp->rbytes += rxd.nbytes;
60184647Sraghuram 		ehp = (struct ether_header *)mp->b_rptr;
60194647Sraghuram 		if (IS_BROADCAST(ehp))
60204647Sraghuram 			statsp->brdcstrcv++;
60214647Sraghuram 		else if (IS_MULTICAST(ehp))
60224647Sraghuram 			statsp->multircv++;
60234647Sraghuram 
60244647Sraghuram 		/* build a chain of received packets */
60254647Sraghuram 		if (bp == NULL) {
60264647Sraghuram 			/* first pkt */
60274647Sraghuram 			bp = mp;
60284647Sraghuram 			bpt = bp;
60294647Sraghuram 			bpt->b_next = NULL;
60304647Sraghuram 		} else {
60314647Sraghuram 			mp->b_next = NULL;
60324647Sraghuram 			bpt->b_next = mp;
60334647Sraghuram 			bpt = mp;
60344647Sraghuram 		}
60354647Sraghuram 
60364647Sraghuram 		if (count++ > vgen_chain_len) {
60374647Sraghuram 			DTRACE_PROBE1(vgen_rcv_msgs, int, count);
60385935Ssb155480 			vgen_rx(ldcp, bp);
60394647Sraghuram 			count = 0;
60404647Sraghuram 			bp = bpt = NULL;
60414647Sraghuram 		}
60422336Snarayan 
60432336Snarayan vgen_next_rxi:
60444647Sraghuram 		/* update end index of range of descrs to be ack'd */
60454647Sraghuram 		ack_end = rxi;
60464647Sraghuram 
60474647Sraghuram 		/* update the next index to be processed */
60484647Sraghuram 		INCR_RXI(next_rxi, ldcp);
60494647Sraghuram 		if (next_rxi == start) {
60502336Snarayan 			/*
60514647Sraghuram 			 * processed the entire descriptor ring upto
60524647Sraghuram 			 * the index at which we started.
60532336Snarayan 			 */
60542336Snarayan 			break;
60552336Snarayan 		}
60562336Snarayan 
60574647Sraghuram 		rxi = next_rxi;
60584647Sraghuram 
60594647Sraghuram 	_NOTE(CONSTCOND)
60604647Sraghuram 	} while (1);
60614647Sraghuram 
60624647Sraghuram 	/*
60634647Sraghuram 	 * send an ack message to peer indicating that we have stopped
60644647Sraghuram 	 * processing descriptors.
60654647Sraghuram 	 */
60664647Sraghuram 	if (set_ack_start) {
60672336Snarayan 		/*
60684647Sraghuram 		 * We have ack'd upto some index and we have not
60694647Sraghuram 		 * processed any descriptors beyond that index.
60704647Sraghuram 		 * Use the last ack'd index as both the start and
60714647Sraghuram 		 * end of range of descrs being ack'd.
60724647Sraghuram 		 * Note: This results in acking the last index twice
60734647Sraghuram 		 * and should be harmless.
60742336Snarayan 		 */
60754647Sraghuram 		ack_start = ack_end;
60764647Sraghuram 	}
60774647Sraghuram 
60784647Sraghuram 	rv = vgen_send_dring_ack(ldcp, tagp, ack_start, ack_end,
60794647Sraghuram 	    VIO_DP_STOPPED);
60804647Sraghuram 	if (rv != VGEN_SUCCESS) {
60814647Sraghuram 		goto error_ret;
60824647Sraghuram 	}
60834647Sraghuram 
60845935Ssb155480 	/* save new recv index of next dring msg */
60854647Sraghuram 	ldcp->next_rxi = next_rxi;
60864647Sraghuram 
60874647Sraghuram error_ret:
60885935Ssb155480 	/* send up packets received so far */
60894647Sraghuram 	if (bp != NULL) {
60904647Sraghuram 		DTRACE_PROBE1(vgen_rcv_msgs, int, count);
60915935Ssb155480 		vgen_rx(ldcp, bp);
60924647Sraghuram 		bp = bpt = NULL;
60934647Sraghuram 	}
60944647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
60954647Sraghuram 	return (rv);
60964647Sraghuram 
60974647Sraghuram }
60984647Sraghuram 
60994647Sraghuram static int
61004647Sraghuram vgen_handle_dring_data_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
61014647Sraghuram {
61024647Sraghuram 	int rv = 0;
61034647Sraghuram 	uint32_t start;
61044647Sraghuram 	int32_t end;
61054647Sraghuram 	uint32_t txi;
61064647Sraghuram 	boolean_t ready_txd = B_FALSE;
61074647Sraghuram 	vgen_stats_t *statsp;
61084647Sraghuram 	vgen_private_desc_t *tbufp;
61094647Sraghuram 	vnet_public_desc_t *txdp;
61104647Sraghuram 	vio_dring_entry_hdr_t *hdrp;
61114647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
61124647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
61134647Sraghuram 
61144647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
61154647Sraghuram 	start = dringmsg->start_idx;
61164647Sraghuram 	end = dringmsg->end_idx;
61175373Sraghuram 	statsp = &ldcp->stats;
61184647Sraghuram 
61194647Sraghuram 	/*
61204647Sraghuram 	 * received an ack corresponding to a specific descriptor for
61214647Sraghuram 	 * which we had set the ACK bit in the descriptor (during
61224647Sraghuram 	 * transmit). This enables us to reclaim descriptors.
61234647Sraghuram 	 */
61244647Sraghuram 
61254647Sraghuram 	DBG2(vgenp, ldcp, "ACK:  start(%d), end(%d)\n", start, end);
61264647Sraghuram 
61274647Sraghuram 	/* validate start and end indeces in the tx ack msg */
61284647Sraghuram 	if (!(CHECK_TXI(start, ldcp)) || !(CHECK_TXI(end, ldcp))) {
61294647Sraghuram 		/* drop the message if invalid index */
61304647Sraghuram 		DWARN(vgenp, ldcp, "Invalid Tx ack start(%d) or end(%d)\n",
61314647Sraghuram 		    start, end);
61324647Sraghuram 		return (rv);
61334647Sraghuram 	}
61344647Sraghuram 	/* validate dring_ident */
61354647Sraghuram 	if (dringmsg->dring_ident != ldcp->local_hparams.dring_ident) {
61364647Sraghuram 		/* invalid dring_ident, drop the msg */
61374647Sraghuram 		DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
61384647Sraghuram 		    dringmsg->dring_ident);
61394647Sraghuram 		return (rv);
61404647Sraghuram 	}
61414647Sraghuram 	statsp->dring_data_acks++;
61424647Sraghuram 
61434647Sraghuram 	/* reclaim descriptors that are done */
61444647Sraghuram 	vgen_reclaim(ldcp);
61454647Sraghuram 
61464647Sraghuram 	if (dringmsg->dring_process_state != VIO_DP_STOPPED) {
61472336Snarayan 		/*
61484647Sraghuram 		 * receiver continued processing descriptors after
61494647Sraghuram 		 * sending us the ack.
61502336Snarayan 		 */
61514647Sraghuram 		return (rv);
61524647Sraghuram 	}
61534647Sraghuram 
61544647Sraghuram 	statsp->dring_stopped_acks++;
61554647Sraghuram 
61564647Sraghuram 	/* receiver stopped processing descriptors */
61574647Sraghuram 	mutex_enter(&ldcp->wrlock);
61584647Sraghuram 	mutex_enter(&ldcp->tclock);
61594647Sraghuram 
61604647Sraghuram 	/*
61614647Sraghuram 	 * determine if there are any pending tx descriptors
61624647Sraghuram 	 * ready to be processed by the receiver(peer) and if so,
61634647Sraghuram 	 * send a message to the peer to restart receiving.
61644647Sraghuram 	 */
61654647Sraghuram 	ready_txd = B_FALSE;
61664647Sraghuram 
61674647Sraghuram 	/*
61684647Sraghuram 	 * using the end index of the descriptor range for which
61694647Sraghuram 	 * we received the ack, check if the next descriptor is
61704647Sraghuram 	 * ready.
61714647Sraghuram 	 */
61724647Sraghuram 	txi = end;
61734647Sraghuram 	INCR_TXI(txi, ldcp);
61744647Sraghuram 	tbufp = &ldcp->tbufp[txi];
61754647Sraghuram 	txdp = tbufp->descp;
61764647Sraghuram 	hdrp = &txdp->hdr;
61774647Sraghuram 	if (hdrp->dstate == VIO_DESC_READY) {
61784647Sraghuram 		ready_txd = B_TRUE;
61794647Sraghuram 	} else {
61804647Sraghuram 		/*
61814647Sraghuram 		 * descr next to the end of ack'd descr range is not
61824647Sraghuram 		 * ready.
61834647Sraghuram 		 * starting from the current reclaim index, check
61844647Sraghuram 		 * if any descriptor is ready.
61854647Sraghuram 		 */
61864647Sraghuram 
61874647Sraghuram 		txi = ldcp->cur_tbufp - ldcp->tbufp;
61882336Snarayan 		tbufp = &ldcp->tbufp[txi];
61894647Sraghuram 
61902336Snarayan 		txdp = tbufp->descp;
61912336Snarayan 		hdrp = &txdp->hdr;
61922336Snarayan 		if (hdrp->dstate == VIO_DESC_READY) {
61932336Snarayan 			ready_txd = B_TRUE;
61944647Sraghuram 		}
61954647Sraghuram 
61964647Sraghuram 	}
61974647Sraghuram 
61984647Sraghuram 	if (ready_txd) {
61994647Sraghuram 		/*
62004647Sraghuram 		 * we have tx descriptor(s) ready to be
62014647Sraghuram 		 * processed by the receiver.
62024647Sraghuram 		 * send a message to the peer with the start index
62034647Sraghuram 		 * of ready descriptors.
62044647Sraghuram 		 */
62054647Sraghuram 		rv = vgen_send_dring_data(ldcp, txi, -1);
62064647Sraghuram 		if (rv != VGEN_SUCCESS) {
62074647Sraghuram 			ldcp->resched_peer = B_TRUE;
62084647Sraghuram 			ldcp->resched_peer_txi = txi;
62094647Sraghuram 			mutex_exit(&ldcp->tclock);
62104647Sraghuram 			mutex_exit(&ldcp->wrlock);
62114647Sraghuram 			return (rv);
62122336Snarayan 		}
62134647Sraghuram 	} else {
62144647Sraghuram 		/*
62154647Sraghuram 		 * no ready tx descriptors. set the flag to send a
62164647Sraghuram 		 * message to peer when tx descriptors are ready in
62174647Sraghuram 		 * transmit routine.
62184647Sraghuram 		 */
62194647Sraghuram 		ldcp->resched_peer = B_TRUE;
62204647Sraghuram 		ldcp->resched_peer_txi = ldcp->cur_tbufp - ldcp->tbufp;
62214647Sraghuram 	}
62224647Sraghuram 
62234647Sraghuram 	mutex_exit(&ldcp->tclock);
62244647Sraghuram 	mutex_exit(&ldcp->wrlock);
62254647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
62264647Sraghuram 	return (rv);
62274647Sraghuram }
62284647Sraghuram 
62294647Sraghuram static int
62304647Sraghuram vgen_handle_dring_data_nack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
62314647Sraghuram {
62324647Sraghuram 	int rv = 0;
62334647Sraghuram 	uint32_t start;
62344647Sraghuram 	int32_t end;
62354647Sraghuram 	uint32_t txi;
62364647Sraghuram 	vnet_public_desc_t *txdp;
62374647Sraghuram 	vio_dring_entry_hdr_t *hdrp;
62384647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
62394647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
62404647Sraghuram 
62414647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
62424647Sraghuram 	start = dringmsg->start_idx;
62434647Sraghuram 	end = dringmsg->end_idx;
62444647Sraghuram 
62454647Sraghuram 	/*
62464647Sraghuram 	 * peer sent a NACK msg to indicate lost packets.
62474647Sraghuram 	 * The start and end correspond to the range of descriptors
62484647Sraghuram 	 * for which the peer didn't receive a dring data msg and so
62494647Sraghuram 	 * didn't receive the corresponding data.
62504647Sraghuram 	 */
62514647Sraghuram 	DWARN(vgenp, ldcp, "NACK: start(%d), end(%d)\n", start, end);
62524647Sraghuram 
62534647Sraghuram 	/* validate start and end indeces in the tx nack msg */
62544647Sraghuram 	if (!(CHECK_TXI(start, ldcp)) || !(CHECK_TXI(end, ldcp))) {
62554647Sraghuram 		/* drop the message if invalid index */
62564647Sraghuram 		DWARN(vgenp, ldcp, "Invalid Tx nack start(%d) or end(%d)\n",
62574647Sraghuram 		    start, end);
62584647Sraghuram 		return (rv);
62594647Sraghuram 	}
62604647Sraghuram 	/* validate dring_ident */
62614647Sraghuram 	if (dringmsg->dring_ident != ldcp->local_hparams.dring_ident) {
62624647Sraghuram 		/* invalid dring_ident, drop the msg */
62634647Sraghuram 		DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
62644647Sraghuram 		    dringmsg->dring_ident);
62654647Sraghuram 		return (rv);
62664647Sraghuram 	}
62674647Sraghuram 	mutex_enter(&ldcp->txlock);
62684647Sraghuram 	mutex_enter(&ldcp->tclock);
62694647Sraghuram 
62704647Sraghuram 	if (ldcp->next_tbufp == ldcp->cur_tbufp) {
62714647Sraghuram 		/* no busy descriptors, bogus nack ? */
62722336Snarayan 		mutex_exit(&ldcp->tclock);
62732336Snarayan 		mutex_exit(&ldcp->txlock);
62744647Sraghuram 		return (rv);
62754647Sraghuram 	}
62761991Sheppo 
62774647Sraghuram 	/* we just mark the descrs as done so they can be reclaimed */
62784647Sraghuram 	for (txi = start; txi <= end; ) {
62794647Sraghuram 		txdp = &(ldcp->txdp[txi]);
62804647Sraghuram 		hdrp = &txdp->hdr;
62814647Sraghuram 		if (hdrp->dstate == VIO_DESC_READY)
62824647Sraghuram 			hdrp->dstate = VIO_DESC_DONE;
62834647Sraghuram 		INCR_TXI(txi, ldcp);
62844647Sraghuram 	}
62854647Sraghuram 	mutex_exit(&ldcp->tclock);
62864647Sraghuram 	mutex_exit(&ldcp->txlock);
62874647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
62882793Slm66018 	return (rv);
62891991Sheppo }
62901991Sheppo 
62911991Sheppo static void
62921991Sheppo vgen_reclaim(vgen_ldc_t *ldcp)
62931991Sheppo {
62942336Snarayan 	mutex_enter(&ldcp->tclock);
62952336Snarayan 
62961991Sheppo 	vgen_reclaim_dring(ldcp);
62971991Sheppo 	ldcp->reclaim_lbolt = ddi_get_lbolt();
62982336Snarayan 
62991991Sheppo 	mutex_exit(&ldcp->tclock);
63001991Sheppo }
63011991Sheppo 
63021991Sheppo /*
63031991Sheppo  * transmit reclaim function. starting from the current reclaim index
63041991Sheppo  * look for descriptors marked DONE and reclaim the descriptor and the
63051991Sheppo  * corresponding buffers (tbuf).
63061991Sheppo  */
63071991Sheppo static void
63081991Sheppo vgen_reclaim_dring(vgen_ldc_t *ldcp)
63091991Sheppo {
63105022Sraghuram 	int count = 0;
63111991Sheppo 	vnet_public_desc_t *txdp;
63121991Sheppo 	vgen_private_desc_t *tbufp;
63131991Sheppo 	vio_dring_entry_hdr_t	*hdrp;
63141991Sheppo 
63151991Sheppo #ifdef DEBUG
63161991Sheppo 	if (vgen_trigger_txtimeout)
63171991Sheppo 		return;
63181991Sheppo #endif
63191991Sheppo 
63201991Sheppo 	tbufp = ldcp->cur_tbufp;
63211991Sheppo 	txdp = tbufp->descp;
63221991Sheppo 	hdrp = &txdp->hdr;
63231991Sheppo 
63241991Sheppo 	while ((hdrp->dstate == VIO_DESC_DONE) &&
63251991Sheppo 	    (tbufp != ldcp->next_tbufp)) {
63261991Sheppo 		tbufp->flags = VGEN_PRIV_DESC_FREE;
63271991Sheppo 		hdrp->dstate = VIO_DESC_FREE;
63281991Sheppo 		hdrp->ack = B_FALSE;
63291991Sheppo 
63301991Sheppo 		tbufp = NEXTTBUF(ldcp, tbufp);
63311991Sheppo 		txdp = tbufp->descp;
63321991Sheppo 		hdrp = &txdp->hdr;
63335022Sraghuram 		count++;
63341991Sheppo 	}
63351991Sheppo 
63361991Sheppo 	ldcp->cur_tbufp = tbufp;
63371991Sheppo 
63381991Sheppo 	/*
63391991Sheppo 	 * Check if mac layer should be notified to restart transmissions
63401991Sheppo 	 */
63415022Sraghuram 	if ((ldcp->need_resched) && (count > 0)) {
63426495Sspeer 		vio_net_tx_update_t vtx_update =
63436495Sspeer 		    ldcp->portp->vcb.vio_net_tx_update;
63446495Sspeer 
63451991Sheppo 		ldcp->need_resched = B_FALSE;
63466495Sspeer 		vtx_update(ldcp->portp->vhp);
63471991Sheppo 	}
63481991Sheppo }
63491991Sheppo 
63501991Sheppo /* return the number of pending transmits for the channel */
63511991Sheppo static int
63521991Sheppo vgen_num_txpending(vgen_ldc_t *ldcp)
63531991Sheppo {
63541991Sheppo 	int n;
63551991Sheppo 
63561991Sheppo 	if (ldcp->next_tbufp >= ldcp->cur_tbufp) {
63571991Sheppo 		n = ldcp->next_tbufp - ldcp->cur_tbufp;
63581991Sheppo 	} else  {
63591991Sheppo 		/* cur_tbufp > next_tbufp */
63601991Sheppo 		n = ldcp->num_txds - (ldcp->cur_tbufp - ldcp->next_tbufp);
63611991Sheppo 	}
63621991Sheppo 
63631991Sheppo 	return (n);
63641991Sheppo }
63651991Sheppo 
63661991Sheppo /* determine if the transmit descriptor ring is full */
63671991Sheppo static int
63681991Sheppo vgen_tx_dring_full(vgen_ldc_t *ldcp)
63691991Sheppo {
63701991Sheppo 	vgen_private_desc_t	*tbufp;
63711991Sheppo 	vgen_private_desc_t	*ntbufp;
63721991Sheppo 
63731991Sheppo 	tbufp = ldcp->next_tbufp;
63741991Sheppo 	ntbufp = NEXTTBUF(ldcp, tbufp);
63751991Sheppo 	if (ntbufp == ldcp->cur_tbufp) { /* out of tbufs/txds */
63761991Sheppo 		return (VGEN_SUCCESS);
63771991Sheppo 	}
63781991Sheppo 	return (VGEN_FAILURE);
63791991Sheppo }
63801991Sheppo 
63811991Sheppo /* determine if timeout condition has occured */
63821991Sheppo static int
63831991Sheppo vgen_ldc_txtimeout(vgen_ldc_t *ldcp)
63841991Sheppo {
63851991Sheppo 	if (((ddi_get_lbolt() - ldcp->reclaim_lbolt) >
63864650Sraghuram 	    drv_usectohz(vnet_ldcwd_txtimeout * 1000)) &&
63874650Sraghuram 	    (vnet_ldcwd_txtimeout) &&
63884650Sraghuram 	    (vgen_tx_dring_full(ldcp) == VGEN_SUCCESS)) {
63891991Sheppo 		return (VGEN_SUCCESS);
63901991Sheppo 	} else {
63911991Sheppo 		return (VGEN_FAILURE);
63921991Sheppo 	}
63931991Sheppo }
63941991Sheppo 
63951991Sheppo /* transmit watchdog timeout handler */
63961991Sheppo static void
63971991Sheppo vgen_ldc_watchdog(void *arg)
63981991Sheppo {
63991991Sheppo 	vgen_ldc_t *ldcp;
64002336Snarayan 	vgen_t *vgenp;
64011991Sheppo 	int rv;
64021991Sheppo 
64031991Sheppo 	ldcp = (vgen_ldc_t *)arg;
64042336Snarayan 	vgenp = LDC_TO_VGEN(ldcp);
64051991Sheppo 
64061991Sheppo 	rv = vgen_ldc_txtimeout(ldcp);
64071991Sheppo 	if (rv == VGEN_SUCCESS) {
64084647Sraghuram 		DWARN(vgenp, ldcp, "transmit timeout\n");
64091991Sheppo #ifdef DEBUG
64101991Sheppo 		if (vgen_trigger_txtimeout) {
64111991Sheppo 			/* tx timeout triggered for debugging */
64121991Sheppo 			vgen_trigger_txtimeout = 0;
64131991Sheppo 		}
64141991Sheppo #endif
64151991Sheppo 		mutex_enter(&ldcp->cblock);
64162793Slm66018 		ldcp->need_ldc_reset = B_TRUE;
64173653Snarayan 		vgen_handshake_retry(ldcp);
64181991Sheppo 		mutex_exit(&ldcp->cblock);
64191991Sheppo 		if (ldcp->need_resched) {
64206495Sspeer 			vio_net_tx_update_t vtx_update =
64216495Sspeer 			    ldcp->portp->vcb.vio_net_tx_update;
64226495Sspeer 
64231991Sheppo 			ldcp->need_resched = B_FALSE;
64246495Sspeer 			vtx_update(ldcp->portp->vhp);
64251991Sheppo 		}
64261991Sheppo 	}
64271991Sheppo 
64281991Sheppo 	ldcp->wd_tid = timeout(vgen_ldc_watchdog, (caddr_t)ldcp,
64291991Sheppo 	    drv_usectohz(vnet_ldcwd_interval * 1000));
64301991Sheppo }
64311991Sheppo 
64321991Sheppo /* handler for error messages received from the peer ldc end-point */
64331991Sheppo static void
64341991Sheppo vgen_handle_errmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
64351991Sheppo {
64361991Sheppo 	_NOTE(ARGUNUSED(ldcp, tagp))
64371991Sheppo }
64381991Sheppo 
64395935Ssb155480 static int
64405935Ssb155480 vgen_check_datamsg_seq(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
64415935Ssb155480 {
64425935Ssb155480 	vio_raw_data_msg_t	*rmsg;
64435935Ssb155480 	vio_dring_msg_t		*dmsg;
64445935Ssb155480 	uint64_t		seq_num;
64455935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
64465935Ssb155480 
64475935Ssb155480 	if (tagp->vio_subtype_env == VIO_DRING_DATA) {
64485935Ssb155480 		dmsg = (vio_dring_msg_t *)tagp;
64495935Ssb155480 		seq_num = dmsg->seq_num;
64505935Ssb155480 	} else if (tagp->vio_subtype_env == VIO_PKT_DATA) {
64515935Ssb155480 		rmsg = (vio_raw_data_msg_t *)tagp;
64525935Ssb155480 		seq_num = rmsg->seq_num;
64535935Ssb155480 	} else {
64545935Ssb155480 		return (EINVAL);
64555935Ssb155480 	}
64565935Ssb155480 
64575935Ssb155480 	if (seq_num != ldcp->next_rxseq) {
64585935Ssb155480 
64595935Ssb155480 		/* seqnums don't match */
64605935Ssb155480 		DWARN(vgenp, ldcp,
64615935Ssb155480 		    "next_rxseq(0x%lx) != seq_num(0x%lx)\n",
64625935Ssb155480 		    ldcp->next_rxseq, seq_num);
64635935Ssb155480 
64645935Ssb155480 		ldcp->need_ldc_reset = B_TRUE;
64655935Ssb155480 		return (EINVAL);
64665935Ssb155480 
64675935Ssb155480 	}
64685935Ssb155480 
64695935Ssb155480 	ldcp->next_rxseq++;
64705935Ssb155480 
64715935Ssb155480 	return (0);
64725935Ssb155480 }
64735935Ssb155480 
64741991Sheppo /* Check if the session id in the received message is valid */
64751991Sheppo static int
64761991Sheppo vgen_check_sid(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
64771991Sheppo {
64784647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
64794647Sraghuram 
64801991Sheppo 	if (tagp->vio_sid != ldcp->peer_sid) {
64814647Sraghuram 		DWARN(vgenp, ldcp, "sid mismatch: expected(%x), rcvd(%x)\n",
64824647Sraghuram 		    ldcp->peer_sid, tagp->vio_sid);
64831991Sheppo 		return (VGEN_FAILURE);
64841991Sheppo 	}
64851991Sheppo 	else
64861991Sheppo 		return (VGEN_SUCCESS);
64871991Sheppo }
64881991Sheppo 
64891991Sheppo static caddr_t
64901991Sheppo vgen_print_ethaddr(uint8_t *a, char *ebuf)
64911991Sheppo {
64921991Sheppo 	(void) sprintf(ebuf,
64934650Sraghuram 	    "%x:%x:%x:%x:%x:%x", a[0], a[1], a[2], a[3], a[4], a[5]);
64941991Sheppo 	return (ebuf);
64951991Sheppo }
64961991Sheppo 
64971991Sheppo /* Handshake watchdog timeout handler */
64981991Sheppo static void
64991991Sheppo vgen_hwatchdog(void *arg)
65001991Sheppo {
65011991Sheppo 	vgen_ldc_t *ldcp = (vgen_ldc_t *)arg;
65024647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
65034647Sraghuram 
65044647Sraghuram 	DWARN(vgenp, ldcp,
65054647Sraghuram 	    "handshake timeout ldc(%lx) phase(%x) state(%x)\n",
65064647Sraghuram 	    ldcp->hphase, ldcp->hstate);
65071991Sheppo 
65081991Sheppo 	mutex_enter(&ldcp->cblock);
65093653Snarayan 	if (ldcp->cancel_htid) {
65103653Snarayan 		ldcp->cancel_htid = 0;
65113653Snarayan 		mutex_exit(&ldcp->cblock);
65123653Snarayan 		return;
65133653Snarayan 	}
65141991Sheppo 	ldcp->htid = 0;
65152841Snarayan 	ldcp->need_ldc_reset = B_TRUE;
65161991Sheppo 	vgen_handshake_retry(ldcp);
65171991Sheppo 	mutex_exit(&ldcp->cblock);
65181991Sheppo }
65191991Sheppo 
65201991Sheppo static void
65211991Sheppo vgen_print_hparams(vgen_hparams_t *hp)
65221991Sheppo {
65231991Sheppo 	uint8_t	addr[6];
65241991Sheppo 	char	ea[6];
65251991Sheppo 	ldc_mem_cookie_t *dc;
65261991Sheppo 
65271991Sheppo 	cmn_err(CE_CONT, "version_info:\n");
65281991Sheppo 	cmn_err(CE_CONT,
65291991Sheppo 	    "\tver_major: %d, ver_minor: %d, dev_class: %d\n",
65301991Sheppo 	    hp->ver_major, hp->ver_minor, hp->dev_class);
65311991Sheppo 
65325462Swentaoy 	vnet_macaddr_ultostr(hp->addr, addr);
65331991Sheppo 	cmn_err(CE_CONT, "attr_info:\n");
65341991Sheppo 	cmn_err(CE_CONT, "\tMTU: %lx, addr: %s\n", hp->mtu,
65351991Sheppo 	    vgen_print_ethaddr(addr, ea));
65361991Sheppo 	cmn_err(CE_CONT,
65371991Sheppo 	    "\taddr_type: %x, xfer_mode: %x, ack_freq: %x\n",
65381991Sheppo 	    hp->addr_type, hp->xfer_mode, hp->ack_freq);
65391991Sheppo 
65401991Sheppo 	dc = &hp->dring_cookie;
65411991Sheppo 	cmn_err(CE_CONT, "dring_info:\n");
65421991Sheppo 	cmn_err(CE_CONT,
65431991Sheppo 	    "\tlength: %d, dsize: %d\n", hp->num_desc, hp->desc_size);
65441991Sheppo 	cmn_err(CE_CONT,
65451991Sheppo 	    "\tldc_addr: 0x%lx, ldc_size: %ld\n",
65461991Sheppo 	    dc->addr, dc->size);
65471991Sheppo 	cmn_err(CE_CONT, "\tdring_ident: 0x%lx\n", hp->dring_ident);
65481991Sheppo }
65491991Sheppo 
65501991Sheppo static void
65511991Sheppo vgen_print_ldcinfo(vgen_ldc_t *ldcp)
65521991Sheppo {
65531991Sheppo 	vgen_hparams_t *hp;
65541991Sheppo 
65551991Sheppo 	cmn_err(CE_CONT, "Channel Information:\n");
65561991Sheppo 	cmn_err(CE_CONT,
65571991Sheppo 	    "\tldc_id: 0x%lx, ldc_status: 0x%x\n",
65581991Sheppo 	    ldcp->ldc_id, ldcp->ldc_status);
65591991Sheppo 	cmn_err(CE_CONT,
65601991Sheppo 	    "\tlocal_sid: 0x%x, peer_sid: 0x%x\n",
65611991Sheppo 	    ldcp->local_sid, ldcp->peer_sid);
65621991Sheppo 	cmn_err(CE_CONT,
65631991Sheppo 	    "\thphase: 0x%x, hstate: 0x%x\n",
65641991Sheppo 	    ldcp->hphase, ldcp->hstate);
65651991Sheppo 
65661991Sheppo 	cmn_err(CE_CONT, "Local handshake params:\n");
65671991Sheppo 	hp = &ldcp->local_hparams;
65681991Sheppo 	vgen_print_hparams(hp);
65691991Sheppo 
65701991Sheppo 	cmn_err(CE_CONT, "Peer handshake params:\n");
65711991Sheppo 	hp = &ldcp->peer_hparams;
65721991Sheppo 	vgen_print_hparams(hp);
65731991Sheppo }
65744647Sraghuram 
65754647Sraghuram /*
65765935Ssb155480  * Send received packets up the stack.
65774647Sraghuram  */
65784647Sraghuram static void
65795935Ssb155480 vgen_rx(vgen_ldc_t *ldcp, mblk_t *bp)
65804647Sraghuram {
65816495Sspeer 	vio_net_rx_cb_t vrx_cb = ldcp->portp->vcb.vio_net_rx_cb;
65824647Sraghuram 
65834647Sraghuram 	if (ldcp->rcv_thread != NULL) {
65845935Ssb155480 		ASSERT(MUTEX_HELD(&ldcp->rxlock));
65855935Ssb155480 		mutex_exit(&ldcp->rxlock);
65864647Sraghuram 	} else {
65875935Ssb155480 		ASSERT(MUTEX_HELD(&ldcp->cblock));
65885935Ssb155480 		mutex_exit(&ldcp->cblock);
65895935Ssb155480 	}
65905935Ssb155480 
65916495Sspeer 	vrx_cb(ldcp->portp->vhp, bp);
65925935Ssb155480 
65934647Sraghuram 	if (ldcp->rcv_thread != NULL) {
65945935Ssb155480 		mutex_enter(&ldcp->rxlock);
65955935Ssb155480 	} else {
65965935Ssb155480 		mutex_enter(&ldcp->cblock);
65975935Ssb155480 	}
65984647Sraghuram }
65994647Sraghuram 
66004647Sraghuram /*
66014647Sraghuram  * vgen_ldc_rcv_worker -- A per LDC worker thread to receive data.
66024647Sraghuram  * This thread is woken up by the LDC interrupt handler to process
66034647Sraghuram  * LDC packets and receive data.
66044647Sraghuram  */
66054647Sraghuram static void
66064647Sraghuram vgen_ldc_rcv_worker(void *arg)
66074647Sraghuram {
66084647Sraghuram 	callb_cpr_t	cprinfo;
66094647Sraghuram 	vgen_ldc_t *ldcp = (vgen_ldc_t *)arg;
66104647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
66114647Sraghuram 
66124647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
66134647Sraghuram 	CALLB_CPR_INIT(&cprinfo, &ldcp->rcv_thr_lock, callb_generic_cpr,
66144650Sraghuram 	    "vnet_rcv_thread");
66154647Sraghuram 	mutex_enter(&ldcp->rcv_thr_lock);
66164647Sraghuram 	ldcp->rcv_thr_flags |= VGEN_WTHR_RUNNING;
66174647Sraghuram 	while (!(ldcp->rcv_thr_flags & VGEN_WTHR_STOP)) {
66184647Sraghuram 
66194647Sraghuram 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
66204647Sraghuram 		/*
66214647Sraghuram 		 * Wait until the data is received or a stop
66224647Sraghuram 		 * request is received.
66234647Sraghuram 		 */
66244647Sraghuram 		while (!(ldcp->rcv_thr_flags &
66254647Sraghuram 		    (VGEN_WTHR_DATARCVD | VGEN_WTHR_STOP))) {
66264647Sraghuram 			cv_wait(&ldcp->rcv_thr_cv, &ldcp->rcv_thr_lock);
66274647Sraghuram 		}
66284647Sraghuram 		CALLB_CPR_SAFE_END(&cprinfo, &ldcp->rcv_thr_lock)
66294647Sraghuram 
66304647Sraghuram 		/*
66314647Sraghuram 		 * First process the stop request.
66324647Sraghuram 		 */
66334647Sraghuram 		if (ldcp->rcv_thr_flags & VGEN_WTHR_STOP) {
66344647Sraghuram 			DBG2(vgenp, ldcp, "stopped\n");
66354647Sraghuram 			break;
66364647Sraghuram 		}
66374647Sraghuram 		ldcp->rcv_thr_flags &= ~VGEN_WTHR_DATARCVD;
66384647Sraghuram 		mutex_exit(&ldcp->rcv_thr_lock);
66394647Sraghuram 		DBG2(vgenp, ldcp, "calling vgen_handle_evt_read\n");
66404647Sraghuram 		vgen_handle_evt_read(ldcp);
66414647Sraghuram 		mutex_enter(&ldcp->rcv_thr_lock);
66424647Sraghuram 	}
66434647Sraghuram 
66444647Sraghuram 	/*
66454647Sraghuram 	 * Update the run status and wakeup the thread that
66464647Sraghuram 	 * has sent the stop request.
66474647Sraghuram 	 */
66484647Sraghuram 	ldcp->rcv_thr_flags &= ~VGEN_WTHR_RUNNING;
66494647Sraghuram 	cv_signal(&ldcp->rcv_thr_cv);
66504647Sraghuram 	CALLB_CPR_EXIT(&cprinfo);
66514647Sraghuram 	thread_exit();
66524647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
66534647Sraghuram }
66544647Sraghuram 
66554647Sraghuram /* vgen_stop_rcv_thread -- Co-ordinate with receive thread to stop it */
66564647Sraghuram static void
66574647Sraghuram vgen_stop_rcv_thread(vgen_ldc_t *ldcp)
66584647Sraghuram {
66594647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
66604647Sraghuram 
66614647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
66624647Sraghuram 	/*
66634647Sraghuram 	 * Send a stop request by setting the stop flag and
66644647Sraghuram 	 * wait until the receive thread stops.
66654647Sraghuram 	 */
66664647Sraghuram 	mutex_enter(&ldcp->rcv_thr_lock);
66674647Sraghuram 	if (ldcp->rcv_thr_flags & VGEN_WTHR_RUNNING) {
66684647Sraghuram 		ldcp->rcv_thr_flags |= VGEN_WTHR_STOP;
66694647Sraghuram 		cv_signal(&ldcp->rcv_thr_cv);
66704647Sraghuram 		DBG2(vgenp, ldcp, "waiting...");
66714647Sraghuram 		while (ldcp->rcv_thr_flags & VGEN_WTHR_RUNNING) {
66724647Sraghuram 			cv_wait(&ldcp->rcv_thr_cv, &ldcp->rcv_thr_lock);
66734647Sraghuram 		}
66744647Sraghuram 	}
66754647Sraghuram 	mutex_exit(&ldcp->rcv_thr_lock);
66764647Sraghuram 	ldcp->rcv_thread = NULL;
66774647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
66784647Sraghuram }
66794647Sraghuram 
66806495Sspeer /*
66816495Sspeer  * vgen_dds_rx -- post DDS messages to vnet.
66826495Sspeer  */
66836495Sspeer static int
66846495Sspeer vgen_dds_rx(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
66856495Sspeer {
66866495Sspeer 	vio_dds_msg_t *dmsg = (vio_dds_msg_t *)tagp;
66876495Sspeer 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
66886495Sspeer 
66896495Sspeer 	if (dmsg->dds_class != DDS_VNET_NIU) {
66906495Sspeer 		DWARN(vgenp, ldcp, "Unknown DDS class, dropping");
66916495Sspeer 		return (EBADMSG);
66926495Sspeer 	}
66936495Sspeer 	vnet_dds_rx(vgenp->vnetp, dmsg);
66946495Sspeer 	return (0);
66956495Sspeer }
66966495Sspeer 
66976495Sspeer /*
66986495Sspeer  * vgen_dds_tx -- an interface called by vnet to send DDS messages.
66996495Sspeer  */
67006495Sspeer int
67016495Sspeer vgen_dds_tx(void *arg, void *msg)
67026495Sspeer {
67036495Sspeer 	vgen_t *vgenp = arg;
67046495Sspeer 	vio_dds_msg_t *dmsg = msg;
67056495Sspeer 	vgen_portlist_t *plistp = &vgenp->vgenports;
67066495Sspeer 	vgen_ldc_t *ldcp;
67076495Sspeer 	vgen_ldclist_t *ldclp;
67086495Sspeer 	int rv = EIO;
67096495Sspeer 
67106495Sspeer 
67116495Sspeer 	READ_ENTER(&plistp->rwlock);
67126495Sspeer 	ldclp = &(vgenp->vsw_portp->ldclist);
67136495Sspeer 	READ_ENTER(&ldclp->rwlock);
67146495Sspeer 	ldcp = ldclp->headp;
67156495Sspeer 	if ((ldcp == NULL) || (ldcp->hphase != VH_DONE)) {
67166495Sspeer 		goto vgen_dsend_exit;
67176495Sspeer 	}
67186495Sspeer 
67196495Sspeer 	dmsg->tag.vio_sid = ldcp->local_sid;
67206495Sspeer 	rv = vgen_sendmsg(ldcp, (caddr_t)dmsg, sizeof (vio_dds_msg_t), B_FALSE);
67216495Sspeer 	if (rv != VGEN_SUCCESS) {
67226495Sspeer 		rv = EIO;
67236495Sspeer 	} else {
67246495Sspeer 		rv = 0;
67256495Sspeer 	}
67266495Sspeer 
67276495Sspeer vgen_dsend_exit:
67286495Sspeer 	RW_EXIT(&ldclp->rwlock);
67296495Sspeer 	RW_EXIT(&plistp->rwlock);
67306495Sspeer 	return (rv);
67316495Sspeer 
67326495Sspeer }
67336495Sspeer 
67344647Sraghuram #if DEBUG
67354647Sraghuram 
67364647Sraghuram /*
67374647Sraghuram  * Print debug messages - set to 0xf to enable all msgs
67384647Sraghuram  */
67394647Sraghuram static void
67404647Sraghuram debug_printf(const char *fname, vgen_t *vgenp,
67414647Sraghuram     vgen_ldc_t *ldcp, const char *fmt, ...)
67424647Sraghuram {
67434647Sraghuram 	char    buf[256];
67444647Sraghuram 	char    *bufp = buf;
67454647Sraghuram 	va_list ap;
67464647Sraghuram 
67474647Sraghuram 	if ((vgenp != NULL) && (vgenp->vnetp != NULL)) {
67484647Sraghuram 		(void) sprintf(bufp, "vnet%d:",
67494650Sraghuram 		    ((vnet_t *)(vgenp->vnetp))->instance);
67504647Sraghuram 		bufp += strlen(bufp);
67514647Sraghuram 	}
67524647Sraghuram 	if (ldcp != NULL) {
67534647Sraghuram 		(void) sprintf(bufp, "ldc(%ld):", ldcp->ldc_id);
67544647Sraghuram 		bufp += strlen(bufp);
67554647Sraghuram 	}
67564647Sraghuram 	(void) sprintf(bufp, "%s: ", fname);
67574647Sraghuram 	bufp += strlen(bufp);
67584647Sraghuram 
67594647Sraghuram 	va_start(ap, fmt);
67604647Sraghuram 	(void) vsprintf(bufp, fmt, ap);
67614647Sraghuram 	va_end(ap);
67624647Sraghuram 
67634647Sraghuram 	if ((ldcp == NULL) ||(vgendbg_ldcid == -1) ||
67644647Sraghuram 	    (vgendbg_ldcid == ldcp->ldc_id)) {
67654647Sraghuram 		cmn_err(CE_CONT, "%s\n", buf);
67664647Sraghuram 	}
67674647Sraghuram }
67684647Sraghuram #endif
6769