xref: /onnv-gate/usr/src/uts/sun4v/io/vnet_gen.c (revision 9235:2e98898d0a13)
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 /*
238623SSriharsha.Basavapatna@Sun.COM  * Copyright 2009 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>
458275SEric Cheng #include <sys/mac_provider.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);
76*9235SWentao.Yang@Sun.COM void vgen_uninit(void *arg);
776495Sspeer int vgen_dds_tx(void *arg, void *dmsg);
789217SWentao.Yang@Sun.COM void vgen_mod_init(void);
799217SWentao.Yang@Sun.COM int vgen_mod_cleanup(void);
809217SWentao.Yang@Sun.COM void vgen_mod_fini(void);
811991Sheppo static int vgen_start(void *arg);
821991Sheppo static void vgen_stop(void *arg);
831991Sheppo static mblk_t *vgen_tx(void *arg, mblk_t *mp);
841991Sheppo static int vgen_multicst(void *arg, boolean_t add,
851991Sheppo 	const uint8_t *mca);
861991Sheppo static int vgen_promisc(void *arg, boolean_t on);
871991Sheppo static int vgen_unicst(void *arg, const uint8_t *mca);
882311Sseb static int vgen_stat(void *arg, uint_t stat, uint64_t *val);
891991Sheppo static void vgen_ioctl(void *arg, queue_t *wq, mblk_t *mp);
901991Sheppo 
911991Sheppo /* vgen internal functions */
926419Ssb155480 static int vgen_read_mdprops(vgen_t *vgenp);
936419Ssb155480 static void vgen_update_md_prop(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex);
946419Ssb155480 static void vgen_read_pri_eth_types(vgen_t *vgenp, md_t *mdp,
956419Ssb155480 	mde_cookie_t node);
967529SSriharsha.Basavapatna@Sun.COM static void vgen_mtu_read(vgen_t *vgenp, md_t *mdp, mde_cookie_t node,
977529SSriharsha.Basavapatna@Sun.COM 	uint32_t *mtu);
981991Sheppo static void vgen_detach_ports(vgen_t *vgenp);
991991Sheppo static void vgen_port_detach(vgen_port_t *portp);
1001991Sheppo static void vgen_port_list_insert(vgen_port_t *portp);
1011991Sheppo static void vgen_port_list_remove(vgen_port_t *portp);
1021991Sheppo static vgen_port_t *vgen_port_lookup(vgen_portlist_t *plistp,
1031991Sheppo 	int port_num);
1041991Sheppo static int vgen_mdeg_reg(vgen_t *vgenp);
1051991Sheppo static void vgen_mdeg_unreg(vgen_t *vgenp);
1061991Sheppo static int vgen_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
1076419Ssb155480 static int vgen_mdeg_port_cb(void *cb_argp, mdeg_result_t *resp);
1081991Sheppo static int vgen_add_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex);
1096419Ssb155480 static int vgen_port_read_props(vgen_port_t *portp, vgen_t *vgenp, md_t *mdp,
1106419Ssb155480 	mde_cookie_t mdex);
1111991Sheppo static int vgen_remove_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex);
1126419Ssb155480 static int vgen_port_attach(vgen_port_t *portp);
1136419Ssb155480 static void vgen_port_detach_mdeg(vgen_port_t *portp);
1141991Sheppo static void vgen_port_detach_mdeg(vgen_port_t *portp);
1151991Sheppo static int vgen_update_port(vgen_t *vgenp, md_t *curr_mdp,
1161991Sheppo 	mde_cookie_t curr_mdex, md_t *prev_mdp, mde_cookie_t prev_mdex);
1172311Sseb static uint64_t	vgen_port_stat(vgen_port_t *portp, uint_t stat);
1181991Sheppo 
1191991Sheppo static int vgen_ldc_attach(vgen_port_t *portp, uint64_t ldc_id);
1201991Sheppo static void vgen_ldc_detach(vgen_ldc_t *ldcp);
1211991Sheppo static int vgen_alloc_tx_ring(vgen_ldc_t *ldcp);
1221991Sheppo static void vgen_free_tx_ring(vgen_ldc_t *ldcp);
1231991Sheppo static void vgen_init_ports(vgen_t *vgenp);
1241991Sheppo static void vgen_port_init(vgen_port_t *portp);
1251991Sheppo static void vgen_uninit_ports(vgen_t *vgenp);
1261991Sheppo static void vgen_port_uninit(vgen_port_t *portp);
1271991Sheppo static void vgen_init_ldcs(vgen_port_t *portp);
1281991Sheppo static void vgen_uninit_ldcs(vgen_port_t *portp);
1291991Sheppo static int vgen_ldc_init(vgen_ldc_t *ldcp);
1301991Sheppo static void vgen_ldc_uninit(vgen_ldc_t *ldcp);
1311991Sheppo static int vgen_init_tbufs(vgen_ldc_t *ldcp);
1321991Sheppo static void vgen_uninit_tbufs(vgen_ldc_t *ldcp);
1331991Sheppo static void vgen_clobber_tbufs(vgen_ldc_t *ldcp);
1341991Sheppo static void vgen_clobber_rxds(vgen_ldc_t *ldcp);
1352311Sseb static uint64_t	vgen_ldc_stat(vgen_ldc_t *ldcp, uint_t stat);
1361991Sheppo static uint_t vgen_ldc_cb(uint64_t event, caddr_t arg);
1371991Sheppo static int vgen_portsend(vgen_port_t *portp, mblk_t *mp);
1385935Ssb155480 static int vgen_ldcsend(void *arg, mblk_t *mp);
1395935Ssb155480 static void vgen_ldcsend_pkt(void *arg, mblk_t *mp);
1405935Ssb155480 static int vgen_ldcsend_dring(void *arg, mblk_t *mp);
1411991Sheppo static void vgen_reclaim(vgen_ldc_t *ldcp);
1421991Sheppo static void vgen_reclaim_dring(vgen_ldc_t *ldcp);
1431991Sheppo static int vgen_num_txpending(vgen_ldc_t *ldcp);
1441991Sheppo static int vgen_tx_dring_full(vgen_ldc_t *ldcp);
1451991Sheppo static int vgen_ldc_txtimeout(vgen_ldc_t *ldcp);
1461991Sheppo static void vgen_ldc_watchdog(void *arg);
1471991Sheppo 
1481991Sheppo /* vgen handshake functions */
1491991Sheppo static vgen_ldc_t *vh_nextphase(vgen_ldc_t *ldcp);
1501991Sheppo static int vgen_sendmsg(vgen_ldc_t *ldcp, caddr_t msg,  size_t msglen,
1511991Sheppo 	boolean_t caller_holds_lock);
1521991Sheppo static int vgen_send_version_negotiate(vgen_ldc_t *ldcp);
1531991Sheppo static int vgen_send_attr_info(vgen_ldc_t *ldcp);
1541991Sheppo static int vgen_send_dring_reg(vgen_ldc_t *ldcp);
1551991Sheppo static int vgen_send_rdx_info(vgen_ldc_t *ldcp);
1562336Snarayan static int vgen_send_dring_data(vgen_ldc_t *ldcp, uint32_t start, int32_t end);
1571991Sheppo static int vgen_send_mcast_info(vgen_ldc_t *ldcp);
1581991Sheppo static int vgen_handshake_phase2(vgen_ldc_t *ldcp);
1591991Sheppo static void vgen_handshake_reset(vgen_ldc_t *ldcp);
1601991Sheppo static void vgen_reset_hphase(vgen_ldc_t *ldcp);
1611991Sheppo static void vgen_handshake(vgen_ldc_t *ldcp);
1621991Sheppo static int vgen_handshake_done(vgen_ldc_t *ldcp);
1631991Sheppo static void vgen_handshake_retry(vgen_ldc_t *ldcp);
1642793Slm66018 static int vgen_handle_version_negotiate(vgen_ldc_t *ldcp,
1651991Sheppo 	vio_msg_tag_t *tagp);
1662793Slm66018 static int vgen_handle_attr_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1672793Slm66018 static int vgen_handle_dring_reg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1682793Slm66018 static int vgen_handle_rdx_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1692793Slm66018 static int vgen_handle_mcast_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1702793Slm66018 static int vgen_handle_ctrlmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1715935Ssb155480 static void vgen_handle_pkt_data_nop(void *arg1, void *arg2, uint32_t msglen);
1725935Ssb155480 static void vgen_handle_pkt_data(void *arg1, void *arg2, uint32_t msglen);
1734647Sraghuram static int vgen_handle_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1744647Sraghuram static int vgen_handle_dring_data_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1754647Sraghuram static int vgen_process_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1764647Sraghuram static int vgen_handle_dring_data_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1774647Sraghuram static int vgen_handle_dring_data_nack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1782793Slm66018 static int vgen_send_dring_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp,
1792336Snarayan 	uint32_t start, int32_t end, uint8_t pstate);
1805935Ssb155480 static int vgen_handle_datamsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp,
1815935Ssb155480 	uint32_t msglen);
1821991Sheppo static void vgen_handle_errmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1836495Sspeer static void vgen_handle_evt_up(vgen_ldc_t *ldcp);
1846495Sspeer static void vgen_handle_evt_reset(vgen_ldc_t *ldcp);
1851991Sheppo static int vgen_check_sid(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1865935Ssb155480 static int vgen_check_datamsg_seq(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
1871991Sheppo static caddr_t vgen_print_ethaddr(uint8_t *a, char *ebuf);
1881991Sheppo static void vgen_hwatchdog(void *arg);
1891991Sheppo static void vgen_print_attr_info(vgen_ldc_t *ldcp, int endpoint);
1901991Sheppo static void vgen_print_hparams(vgen_hparams_t *hp);
1911991Sheppo static void vgen_print_ldcinfo(vgen_ldc_t *ldcp);
1924647Sraghuram static void vgen_stop_rcv_thread(vgen_ldc_t *ldcp);
1938623SSriharsha.Basavapatna@Sun.COM static void vgen_drain_rcv_thread(vgen_ldc_t *ldcp);
1944647Sraghuram static void vgen_ldc_rcv_worker(void *arg);
1954647Sraghuram static void vgen_handle_evt_read(vgen_ldc_t *ldcp);
1965935Ssb155480 static void vgen_rx(vgen_ldc_t *ldcp, mblk_t *bp);
1975935Ssb155480 static void vgen_set_vnet_proto_ops(vgen_ldc_t *ldcp);
1985935Ssb155480 static void vgen_reset_vnet_proto_ops(vgen_ldc_t *ldcp);
1991991Sheppo 
2006419Ssb155480 /* VLAN routines */
2016419Ssb155480 static void vgen_vlan_read_ids(void *arg, int type, md_t *mdp,
2026419Ssb155480 	mde_cookie_t node, uint16_t *pvidp, uint16_t **vidspp,
2036419Ssb155480 	uint16_t *nvidsp, uint16_t *default_idp);
2046419Ssb155480 static void vgen_vlan_create_hash(vgen_port_t *portp);
2056419Ssb155480 static void vgen_vlan_destroy_hash(vgen_port_t *portp);
2066419Ssb155480 static void vgen_vlan_add_ids(vgen_port_t *portp);
2076419Ssb155480 static void vgen_vlan_remove_ids(vgen_port_t *portp);
2086419Ssb155480 static boolean_t vgen_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid);
2096419Ssb155480 static boolean_t vgen_frame_lookup_vid(vnet_t *vnetp, struct ether_header *ehp,
2106419Ssb155480 	uint16_t *vidp);
2116419Ssb155480 static mblk_t *vgen_vlan_frame_fixtag(vgen_port_t *portp, mblk_t *mp,
2126419Ssb155480 	boolean_t is_tagged, uint16_t vid);
2136419Ssb155480 static void vgen_vlan_unaware_port_reset(vgen_port_t *portp);
2146419Ssb155480 static void vgen_reset_vlan_unaware_ports(vgen_t *vgenp);
2156495Sspeer static int vgen_dds_rx(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp);
2166495Sspeer 
2176495Sspeer /* externs */
2186495Sspeer extern void vnet_dds_rx(void *arg, void *dmsg);
2197529SSriharsha.Basavapatna@Sun.COM extern int vnet_mtu_update(vnet_t *vnetp, uint32_t mtu);
2206419Ssb155480 
2211991Sheppo /*
2221991Sheppo  * The handshake process consists of 5 phases defined below, with VH_PHASE0
2231991Sheppo  * being the pre-handshake phase and VH_DONE is the phase to indicate
2241991Sheppo  * successful completion of all phases.
2251991Sheppo  * Each phase may have one to several handshake states which are required
2261991Sheppo  * to complete successfully to move to the next phase.
2271991Sheppo  * Refer to the functions vgen_handshake() and vgen_handshake_done() for
2281991Sheppo  * more details.
2291991Sheppo  */
2301991Sheppo /* handshake phases */
2311991Sheppo enum {	VH_PHASE0, VH_PHASE1, VH_PHASE2, VH_PHASE3, VH_DONE = 0x80 };
2321991Sheppo 
2331991Sheppo /* handshake states */
2341991Sheppo enum {
2351991Sheppo 
2361991Sheppo 	VER_INFO_SENT	=	0x1,
2371991Sheppo 	VER_ACK_RCVD	=	0x2,
2381991Sheppo 	VER_INFO_RCVD	=	0x4,
2391991Sheppo 	VER_ACK_SENT	=	0x8,
2401991Sheppo 	VER_NEGOTIATED	=	(VER_ACK_RCVD | VER_ACK_SENT),
2411991Sheppo 
2421991Sheppo 	ATTR_INFO_SENT	=	0x10,
2431991Sheppo 	ATTR_ACK_RCVD	=	0x20,
2441991Sheppo 	ATTR_INFO_RCVD	=	0x40,
2451991Sheppo 	ATTR_ACK_SENT	=	0x80,
2461991Sheppo 	ATTR_INFO_EXCHANGED	=	(ATTR_ACK_RCVD | ATTR_ACK_SENT),
2471991Sheppo 
2481991Sheppo 	DRING_INFO_SENT	=	0x100,
2491991Sheppo 	DRING_ACK_RCVD	=	0x200,
2501991Sheppo 	DRING_INFO_RCVD	=	0x400,
2511991Sheppo 	DRING_ACK_SENT	=	0x800,
2521991Sheppo 	DRING_INFO_EXCHANGED	=	(DRING_ACK_RCVD | DRING_ACK_SENT),
2531991Sheppo 
2541991Sheppo 	RDX_INFO_SENT	=	0x1000,
2551991Sheppo 	RDX_ACK_RCVD	=	0x2000,
2561991Sheppo 	RDX_INFO_RCVD	=	0x4000,
2571991Sheppo 	RDX_ACK_SENT	=	0x8000,
2581991Sheppo 	RDX_EXCHANGED	=	(RDX_ACK_RCVD | RDX_ACK_SENT)
2591991Sheppo 
2601991Sheppo };
2611991Sheppo 
2625935Ssb155480 #define	VGEN_PRI_ETH_DEFINED(vgenp)	((vgenp)->pri_num_types != 0)
2635935Ssb155480 
2641991Sheppo #define	LDC_LOCK(ldcp)	\
2651991Sheppo 				mutex_enter(&((ldcp)->cblock));\
2664647Sraghuram 				mutex_enter(&((ldcp)->rxlock));\
2674647Sraghuram 				mutex_enter(&((ldcp)->wrlock));\
2681991Sheppo 				mutex_enter(&((ldcp)->txlock));\
2691991Sheppo 				mutex_enter(&((ldcp)->tclock));
2701991Sheppo #define	LDC_UNLOCK(ldcp)	\
2711991Sheppo 				mutex_exit(&((ldcp)->tclock));\
2721991Sheppo 				mutex_exit(&((ldcp)->txlock));\
2734647Sraghuram 				mutex_exit(&((ldcp)->wrlock));\
2744647Sraghuram 				mutex_exit(&((ldcp)->rxlock));\
2751991Sheppo 				mutex_exit(&((ldcp)->cblock));
2761991Sheppo 
2776419Ssb155480 #define	VGEN_VER_EQ(ldcp, major, minor)	\
2786419Ssb155480 	((ldcp)->local_hparams.ver_major == (major) &&	\
2796419Ssb155480 	    (ldcp)->local_hparams.ver_minor == (minor))
2806419Ssb155480 
2816419Ssb155480 #define	VGEN_VER_LT(ldcp, major, minor)	\
2826419Ssb155480 	(((ldcp)->local_hparams.ver_major < (major)) ||	\
2836419Ssb155480 	    ((ldcp)->local_hparams.ver_major == (major) &&	\
2846419Ssb155480 	    (ldcp)->local_hparams.ver_minor < (minor)))
2856419Ssb155480 
2866419Ssb155480 #define	VGEN_VER_GTEQ(ldcp, major, minor)	\
2876419Ssb155480 	(((ldcp)->local_hparams.ver_major > (major)) ||	\
2886419Ssb155480 	    ((ldcp)->local_hparams.ver_major == (major) &&	\
2896419Ssb155480 	    (ldcp)->local_hparams.ver_minor >= (minor)))
2906419Ssb155480 
2911991Sheppo static struct ether_addr etherbroadcastaddr = {
2921991Sheppo 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
2931991Sheppo };
2941991Sheppo /*
2951991Sheppo  * MIB II broadcast/multicast packets
2961991Sheppo  */
2971991Sheppo #define	IS_BROADCAST(ehp) \
2981991Sheppo 		(ether_cmp(&ehp->ether_dhost, &etherbroadcastaddr) == 0)
2991991Sheppo #define	IS_MULTICAST(ehp) \
3001991Sheppo 		((ehp->ether_dhost.ether_addr_octet[0] & 01) == 1)
3011991Sheppo 
3021991Sheppo /*
3031991Sheppo  * Property names
3041991Sheppo  */
3051991Sheppo static char macaddr_propname[] = "mac-address";
3061991Sheppo static char rmacaddr_propname[] = "remote-mac-address";
3071991Sheppo static char channel_propname[] = "channel-endpoint";
3081991Sheppo static char reg_propname[] = "reg";
3091991Sheppo static char port_propname[] = "port";
3101991Sheppo static char swport_propname[] = "switch-port";
3111991Sheppo static char id_propname[] = "id";
3125935Ssb155480 static char vdev_propname[] = "virtual-device";
3135935Ssb155480 static char vnet_propname[] = "network";
3145935Ssb155480 static char pri_types_propname[] = "priority-ether-types";
3156419Ssb155480 static char vgen_pvid_propname[] = "port-vlan-id";
3166419Ssb155480 static char vgen_vid_propname[] = "vlan-id";
3176419Ssb155480 static char vgen_dvid_propname[] = "default-vlan-id";
3186419Ssb155480 static char port_pvid_propname[] = "remote-port-vlan-id";
3196419Ssb155480 static char port_vid_propname[] = "remote-vlan-id";
3207529SSriharsha.Basavapatna@Sun.COM static char vgen_mtu_propname[] = "mtu";
3211991Sheppo 
3221991Sheppo /* versions supported - in decreasing order */
3237529SSriharsha.Basavapatna@Sun.COM static vgen_ver_t vgen_versions[VGEN_NUM_VER] = { {1, 4} };
3241991Sheppo 
3251991Sheppo /* Tunables */
3265171Ssb155480 uint32_t vgen_hwd_interval = 5;		/* handshake watchdog freq in sec */
3273297Ssb155480 uint32_t vgen_max_hretries = VNET_NUM_HANDSHAKES; /* # of handshake retries */
3281991Sheppo uint32_t vgen_ldcwr_retries = 10;	/* max # of ldc_write() retries */
3292109Slm66018 uint32_t vgen_ldcup_retries = 5;	/* max # of ldc_up() retries */
3308623SSriharsha.Basavapatna@Sun.COM uint32_t vgen_ldccl_retries = 5;	/* max # of ldc_close() retries */
3312336Snarayan uint32_t vgen_recv_delay = 1;		/* delay when rx descr not ready */
3322336Snarayan uint32_t vgen_recv_retries = 10;	/* retry when rx descr not ready */
3335022Sraghuram uint32_t vgen_tx_retries = 0x4;		/* retry when tx descr not available */
3345022Sraghuram uint32_t vgen_tx_delay = 0x30;		/* delay when tx descr not available */
3351991Sheppo 
3364647Sraghuram int vgen_rcv_thread_enabled = 1;	/* Enable Recieve thread */
3374647Sraghuram 
3389217SWentao.Yang@Sun.COM static vio_mblk_pool_t	*vgen_rx_poolp = NULL;
3399217SWentao.Yang@Sun.COM static krwlock_t	vgen_rw;
3409217SWentao.Yang@Sun.COM 
3414647Sraghuram /*
3424647Sraghuram  * max # of packets accumulated prior to sending them up. It is best
3434647Sraghuram  * to keep this at 60% of the number of recieve buffers.
3444647Sraghuram  */
3454647Sraghuram uint32_t vgen_chain_len = (VGEN_NRBUFS * 0.6);
3464647Sraghuram 
3474647Sraghuram /*
3487529SSriharsha.Basavapatna@Sun.COM  * Internal tunables for receive buffer pools, that is,  the size and number of
3497529SSriharsha.Basavapatna@Sun.COM  * mblks for each pool. At least 3 sizes must be specified if these are used.
3507529SSriharsha.Basavapatna@Sun.COM  * The sizes must be specified in increasing order. Non-zero value of the first
3517529SSriharsha.Basavapatna@Sun.COM  * size will be used as a hint to use these values instead of the algorithm
3527529SSriharsha.Basavapatna@Sun.COM  * that determines the sizes based on MTU.
3534647Sraghuram  */
3547529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz1 = 0;
3557529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz2 = 0;
3567529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz3 = 0;
3577529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_rbufsz4 = 0;
3584647Sraghuram 
3594647Sraghuram uint32_t vgen_nrbufs1 = VGEN_NRBUFS;
3604647Sraghuram uint32_t vgen_nrbufs2 = VGEN_NRBUFS;
3614647Sraghuram uint32_t vgen_nrbufs3 = VGEN_NRBUFS;
3627529SSriharsha.Basavapatna@Sun.COM uint32_t vgen_nrbufs4 = VGEN_NRBUFS;
3634647Sraghuram 
3645935Ssb155480 /*
3655935Ssb155480  * In the absence of "priority-ether-types" property in MD, the following
3665935Ssb155480  * internal tunable can be set to specify a single priority ethertype.
3675935Ssb155480  */
3685935Ssb155480 uint64_t vgen_pri_eth_type = 0;
3695935Ssb155480 
3705935Ssb155480 /*
3715935Ssb155480  * Number of transmit priority buffers that are preallocated per device.
3725935Ssb155480  * This number is chosen to be a small value to throttle transmission
3735935Ssb155480  * of priority packets. Note: Must be a power of 2 for vio_create_mblks().
3745935Ssb155480  */
3755935Ssb155480 uint32_t vgen_pri_tx_nmblks = 64;
3765935Ssb155480 
3776419Ssb155480 uint32_t	vgen_vlan_nchains = 4;	/* # of chains in vlan id hash table */
3786419Ssb155480 
3791991Sheppo #ifdef DEBUG
3801991Sheppo /* flags to simulate error conditions for debugging */
3811991Sheppo int vgen_trigger_txtimeout = 0;
3821991Sheppo int vgen_trigger_rxlost = 0;
3831991Sheppo #endif
3841991Sheppo 
3856419Ssb155480 /*
3866419Ssb155480  * Matching criteria passed to the MDEG to register interest
3876419Ssb155480  * in changes to 'virtual-device' nodes (i.e. vnet nodes) identified
3886419Ssb155480  * by their 'name' and 'cfg-handle' properties.
3896419Ssb155480  */
3906419Ssb155480 static md_prop_match_t vdev_prop_match[] = {
3916419Ssb155480 	{ MDET_PROP_STR,    "name"   },
3926419Ssb155480 	{ MDET_PROP_VAL,    "cfg-handle" },
3936419Ssb155480 	{ MDET_LIST_END,    NULL    }
3946419Ssb155480 };
3956419Ssb155480 
3966419Ssb155480 static mdeg_node_match_t vdev_match = { "virtual-device",
3976419Ssb155480 						vdev_prop_match };
3986419Ssb155480 
3991991Sheppo /* MD update matching structure */
4001991Sheppo static md_prop_match_t	vport_prop_match[] = {
4011991Sheppo 	{ MDET_PROP_VAL,	"id" },
4021991Sheppo 	{ MDET_LIST_END,	NULL }
4031991Sheppo };
4041991Sheppo 
4051991Sheppo static mdeg_node_match_t vport_match = { "virtual-device-port",
4061991Sheppo 					vport_prop_match };
4071991Sheppo 
4081991Sheppo /* template for matching a particular vnet instance */
4091991Sheppo static mdeg_prop_spec_t vgen_prop_template[] = {
4101991Sheppo 	{ MDET_PROP_STR,	"name",		"network" },
4111991Sheppo 	{ MDET_PROP_VAL,	"cfg-handle",	NULL },
4121991Sheppo 	{ MDET_LIST_END,	NULL,		NULL }
4131991Sheppo };
4141991Sheppo 
4151991Sheppo #define	VGEN_SET_MDEG_PROP_INST(specp, val)	(specp)[1].ps_val = (val)
4161991Sheppo 
4176419Ssb155480 static int vgen_mdeg_port_cb(void *cb_argp, mdeg_result_t *resp);
4181991Sheppo 
4192311Sseb static mac_callbacks_t vgen_m_callbacks = {
4202311Sseb 	0,
4212311Sseb 	vgen_stat,
4222311Sseb 	vgen_start,
4232311Sseb 	vgen_stop,
4242311Sseb 	vgen_promisc,
4252311Sseb 	vgen_multicst,
4262311Sseb 	vgen_unicst,
4272311Sseb 	vgen_tx,
4282311Sseb 	NULL,
4292311Sseb 	NULL,
4302311Sseb 	NULL
4312311Sseb };
4322311Sseb 
4331991Sheppo /* externs */
4344647Sraghuram extern pri_t	maxclsyspri;
4354647Sraghuram extern proc_t	p0;
4361991Sheppo extern uint32_t vnet_ntxds;
4371991Sheppo extern uint32_t vnet_ldcwd_interval;
4381991Sheppo extern uint32_t vnet_ldcwd_txtimeout;
4392410Slm66018 extern uint32_t vnet_ldc_mtu;
4402336Snarayan extern uint32_t vnet_nrbufs;
4416419Ssb155480 extern uint32_t	vnet_ethermtu;
4426419Ssb155480 extern uint16_t	vnet_default_vlan_id;
4437529SSriharsha.Basavapatna@Sun.COM extern boolean_t vnet_jumbo_rxpools;
4441991Sheppo 
4451991Sheppo #ifdef DEBUG
4461991Sheppo 
4474647Sraghuram extern int vnet_dbglevel;
4484647Sraghuram static void debug_printf(const char *fname, vgen_t *vgenp,
4494647Sraghuram 	vgen_ldc_t *ldcp, const char *fmt, ...);
4504647Sraghuram 
4514647Sraghuram /* -1 for all LDCs info, or ldc_id for a specific LDC info */
4524647Sraghuram int vgendbg_ldcid = -1;
4531991Sheppo 
4541991Sheppo /* simulate handshake error conditions for debug */
4551991Sheppo uint32_t vgen_hdbg;
4561991Sheppo #define	HDBG_VERSION	0x1
4571991Sheppo #define	HDBG_TIMEOUT	0x2
4581991Sheppo #define	HDBG_BAD_SID	0x4
4591991Sheppo #define	HDBG_OUT_STATE	0x8
4601991Sheppo 
4611991Sheppo #endif
4621991Sheppo 
4631991Sheppo /*
4641991Sheppo  * vgen_init() is called by an instance of vnet driver to initialize the
4651991Sheppo  * corresponding generic proxy transport layer. The arguments passed by vnet
4661991Sheppo  * are - an opaque pointer to the vnet instance, pointers to dev_info_t and
4676495Sspeer  * the mac address of the vnet device, and a pointer to vgen_t is passed
4686495Sspeer  * back as a handle to vnet.
4691991Sheppo  */
4701991Sheppo int
4716495Sspeer vgen_init(void *vnetp, uint64_t regprop, dev_info_t *vnetdip,
4726495Sspeer     const uint8_t *macaddr, void **vgenhdl)
4731991Sheppo {
4741991Sheppo 	vgen_t *vgenp;
4751991Sheppo 	int instance;
4765935Ssb155480 	int rv;
4771991Sheppo 
4782311Sseb 	if ((vnetp == NULL) || (vnetdip == NULL))
4791991Sheppo 		return (DDI_FAILURE);
4801991Sheppo 
4811991Sheppo 	instance = ddi_get_instance(vnetdip);
4821991Sheppo 
4835373Sraghuram 	DBG1(NULL, NULL, "vnet(%d): enter\n", instance);
4841991Sheppo 
4851991Sheppo 	vgenp = kmem_zalloc(sizeof (vgen_t), KM_SLEEP);
4861991Sheppo 
4871991Sheppo 	vgenp->vnetp = vnetp;
4886495Sspeer 	vgenp->instance = instance;
4896495Sspeer 	vgenp->regprop = regprop;
4901991Sheppo 	vgenp->vnetdip = vnetdip;
4911991Sheppo 	bcopy(macaddr, &(vgenp->macaddr), ETHERADDRL);
4921991Sheppo 
4931991Sheppo 	/* allocate multicast table */
4941991Sheppo 	vgenp->mctab = kmem_zalloc(VGEN_INIT_MCTAB_SIZE *
4951991Sheppo 	    sizeof (struct ether_addr), KM_SLEEP);
4961991Sheppo 	vgenp->mccount = 0;
4971991Sheppo 	vgenp->mcsize = VGEN_INIT_MCTAB_SIZE;
4981991Sheppo 
4991991Sheppo 	mutex_init(&vgenp->lock, NULL, MUTEX_DRIVER, NULL);
5005641Swentaoy 	rw_init(&vgenp->vgenports.rwlock, NULL, RW_DRIVER, NULL);
5011991Sheppo 
5025935Ssb155480 	rv = vgen_read_mdprops(vgenp);
5035935Ssb155480 	if (rv != 0) {
5045935Ssb155480 		goto vgen_init_fail;
5055935Ssb155480 	}
5065935Ssb155480 
5071991Sheppo 	/* register with MD event generator */
5085935Ssb155480 	rv = vgen_mdeg_reg(vgenp);
5095935Ssb155480 	if (rv != DDI_SUCCESS) {
5105935Ssb155480 		goto vgen_init_fail;
5111991Sheppo 	}
5121991Sheppo 
5136495Sspeer 	*vgenhdl = (void *)vgenp;
5141991Sheppo 
5155373Sraghuram 	DBG1(NULL, NULL, "vnet(%d): exit\n", instance);
5161991Sheppo 	return (DDI_SUCCESS);
5175935Ssb155480 
5185935Ssb155480 vgen_init_fail:
5195935Ssb155480 	rw_destroy(&vgenp->vgenports.rwlock);
5205935Ssb155480 	mutex_destroy(&vgenp->lock);
5215935Ssb155480 	kmem_free(vgenp->mctab, VGEN_INIT_MCTAB_SIZE *
5225935Ssb155480 	    sizeof (struct ether_addr));
5235935Ssb155480 	if (VGEN_PRI_ETH_DEFINED(vgenp)) {
5245935Ssb155480 		kmem_free(vgenp->pri_types,
5255935Ssb155480 		    sizeof (uint16_t) * vgenp->pri_num_types);
5265935Ssb155480 		(void) vio_destroy_mblks(vgenp->pri_tx_vmp);
5275935Ssb155480 	}
5285935Ssb155480 	KMEM_FREE(vgenp);
5295935Ssb155480 	return (DDI_FAILURE);
5301991Sheppo }
5311991Sheppo 
5321991Sheppo /*
5331991Sheppo  * Called by vnet to undo the initializations done by vgen_init().
5341991Sheppo  * The handle provided by generic transport during vgen_init() is the argument.
5351991Sheppo  */
536*9235SWentao.Yang@Sun.COM void
5371991Sheppo vgen_uninit(void *arg)
5381991Sheppo {
5395935Ssb155480 	vgen_t		*vgenp = (vgen_t *)arg;
5405935Ssb155480 	vio_mblk_pool_t	*rp;
5415935Ssb155480 	vio_mblk_pool_t	*nrp;
5422336Snarayan 
5432336Snarayan 	if (vgenp == NULL) {
544*9235SWentao.Yang@Sun.COM 		return;
5452336Snarayan 	}
5461991Sheppo 
5474647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
5481991Sheppo 
5491991Sheppo 	/* unregister with MD event generator */
5501991Sheppo 	vgen_mdeg_unreg(vgenp);
5511991Sheppo 
5521991Sheppo 	mutex_enter(&vgenp->lock);
5531991Sheppo 
5541991Sheppo 	/* detach all ports from the device */
5551991Sheppo 	vgen_detach_ports(vgenp);
5561991Sheppo 
5572336Snarayan 	/*
5582336Snarayan 	 * free any pending rx mblk pools,
5592336Snarayan 	 * that couldn't be freed previously during channel detach.
5602336Snarayan 	 */
5612336Snarayan 	rp = vgenp->rmp;
5622336Snarayan 	while (rp != NULL) {
5632336Snarayan 		nrp = vgenp->rmp = rp->nextp;
5642336Snarayan 		if (vio_destroy_mblks(rp)) {
5659217SWentao.Yang@Sun.COM 			WRITE_ENTER(&vgen_rw);
5669217SWentao.Yang@Sun.COM 			rp->nextp = vgen_rx_poolp;
5679217SWentao.Yang@Sun.COM 			vgen_rx_poolp = rp;
5689217SWentao.Yang@Sun.COM 			RW_EXIT(&vgen_rw);
5692336Snarayan 		}
5702336Snarayan 		rp = nrp;
5712336Snarayan 	}
5722336Snarayan 
5731991Sheppo 	/* free multicast table */
5741991Sheppo 	kmem_free(vgenp->mctab, vgenp->mcsize * sizeof (struct ether_addr));
5751991Sheppo 
5765935Ssb155480 	/* free pri_types table */
5775935Ssb155480 	if (VGEN_PRI_ETH_DEFINED(vgenp)) {
5785935Ssb155480 		kmem_free(vgenp->pri_types,
5795935Ssb155480 		    sizeof (uint16_t) * vgenp->pri_num_types);
5805935Ssb155480 		(void) vio_destroy_mblks(vgenp->pri_tx_vmp);
5815935Ssb155480 	}
5825935Ssb155480 
5831991Sheppo 	mutex_exit(&vgenp->lock);
5841991Sheppo 
5855641Swentaoy 	rw_destroy(&vgenp->vgenports.rwlock);
5861991Sheppo 	mutex_destroy(&vgenp->lock);
5871991Sheppo 
5887572SWentao.Yang@Sun.COM 	DBG1(vgenp, NULL, "exit\n");
5891991Sheppo 	KMEM_FREE(vgenp);
5901991Sheppo }
5911991Sheppo 
5929217SWentao.Yang@Sun.COM /*
5939217SWentao.Yang@Sun.COM  * module specific initialization common to all instances of vnet/vgen.
5949217SWentao.Yang@Sun.COM  */
5959217SWentao.Yang@Sun.COM void
5969217SWentao.Yang@Sun.COM vgen_mod_init(void)
5979217SWentao.Yang@Sun.COM {
5989217SWentao.Yang@Sun.COM 	rw_init(&vgen_rw, NULL, RW_DRIVER, NULL);
5999217SWentao.Yang@Sun.COM }
6009217SWentao.Yang@Sun.COM 
6019217SWentao.Yang@Sun.COM /*
6029217SWentao.Yang@Sun.COM  * module specific cleanup common to all instances of vnet/vgen.
6039217SWentao.Yang@Sun.COM  */
6049217SWentao.Yang@Sun.COM int
6059217SWentao.Yang@Sun.COM vgen_mod_cleanup(void)
6069217SWentao.Yang@Sun.COM {
6079217SWentao.Yang@Sun.COM 	vio_mblk_pool_t	*poolp, *npoolp;
6089217SWentao.Yang@Sun.COM 
6099217SWentao.Yang@Sun.COM 	/*
6109217SWentao.Yang@Sun.COM 	 * If any rx mblk pools are still in use, return
6119217SWentao.Yang@Sun.COM 	 * error and stop the module from unloading.
6129217SWentao.Yang@Sun.COM 	 */
6139217SWentao.Yang@Sun.COM 	WRITE_ENTER(&vgen_rw);
6149217SWentao.Yang@Sun.COM 	poolp = vgen_rx_poolp;
6159217SWentao.Yang@Sun.COM 	while (poolp != NULL) {
6169217SWentao.Yang@Sun.COM 		npoolp = vgen_rx_poolp = poolp->nextp;
6179217SWentao.Yang@Sun.COM 		if (vio_destroy_mblks(poolp) != 0) {
6189217SWentao.Yang@Sun.COM 			vgen_rx_poolp = poolp;
6199217SWentao.Yang@Sun.COM 			RW_EXIT(&vgen_rw);
6209217SWentao.Yang@Sun.COM 			return (EBUSY);
6219217SWentao.Yang@Sun.COM 		}
6229217SWentao.Yang@Sun.COM 		poolp = npoolp;
6239217SWentao.Yang@Sun.COM 	}
6249217SWentao.Yang@Sun.COM 	RW_EXIT(&vgen_rw);
6259217SWentao.Yang@Sun.COM 
6269217SWentao.Yang@Sun.COM 	return (0);
6279217SWentao.Yang@Sun.COM }
6289217SWentao.Yang@Sun.COM 
6299217SWentao.Yang@Sun.COM /*
6309217SWentao.Yang@Sun.COM  * module specific uninitialization common to all instances of vnet/vgen.
6319217SWentao.Yang@Sun.COM  */
6329217SWentao.Yang@Sun.COM void
6339217SWentao.Yang@Sun.COM vgen_mod_fini(void)
6349217SWentao.Yang@Sun.COM {
6359217SWentao.Yang@Sun.COM 	rw_destroy(&vgen_rw);
6369217SWentao.Yang@Sun.COM }
6379217SWentao.Yang@Sun.COM 
6381991Sheppo /* enable transmit/receive for the device */
6392311Sseb int
6401991Sheppo vgen_start(void *arg)
6411991Sheppo {
6426495Sspeer 	vgen_port_t	*portp = (vgen_port_t *)arg;
6436495Sspeer 	vgen_t		*vgenp = portp->vgenp;
6441991Sheppo 
6454647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
6466495Sspeer 	mutex_enter(&portp->lock);
6476495Sspeer 	vgen_port_init(portp);
6486495Sspeer 	portp->flags |= VGEN_STARTED;
6496495Sspeer 	mutex_exit(&portp->lock);
6504647Sraghuram 	DBG1(vgenp, NULL, "exit\n");
6516495Sspeer 
6521991Sheppo 	return (DDI_SUCCESS);
6531991Sheppo }
6541991Sheppo 
6551991Sheppo /* stop transmit/receive */
6562311Sseb void
6571991Sheppo vgen_stop(void *arg)
6581991Sheppo {
6596495Sspeer 	vgen_port_t	*portp = (vgen_port_t *)arg;
6606495Sspeer 	vgen_t		*vgenp = portp->vgenp;
6611991Sheppo 
6624647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
6631991Sheppo 
6646495Sspeer 	mutex_enter(&portp->lock);
6656495Sspeer 	vgen_port_uninit(portp);
6666495Sspeer 	portp->flags &= ~(VGEN_STARTED);
6676495Sspeer 	mutex_exit(&portp->lock);
6684647Sraghuram 	DBG1(vgenp, NULL, "exit\n");
6696495Sspeer 
6701991Sheppo }
6711991Sheppo 
6721991Sheppo /* vgen transmit function */
6731991Sheppo static mblk_t *
6741991Sheppo vgen_tx(void *arg, mblk_t *mp)
6751991Sheppo {
6765022Sraghuram 	int i;
6771991Sheppo 	vgen_port_t *portp;
6785022Sraghuram 	int status = VGEN_FAILURE;
6791991Sheppo 
6801991Sheppo 	portp = (vgen_port_t *)arg;
6815022Sraghuram 	/*
6825022Sraghuram 	 * Retry so that we avoid reporting a failure
6835022Sraghuram 	 * to the upper layer. Returning a failure may cause the
6845022Sraghuram 	 * upper layer to go into single threaded mode there by
6855022Sraghuram 	 * causing performance degradation, especially for a large
6865022Sraghuram 	 * number of connections.
6875022Sraghuram 	 */
6885022Sraghuram 	for (i = 0; i < vgen_tx_retries; ) {
6895022Sraghuram 		status = vgen_portsend(portp, mp);
6905022Sraghuram 		if (status == VGEN_SUCCESS) {
6915022Sraghuram 			break;
6925022Sraghuram 		}
6935022Sraghuram 		if (++i < vgen_tx_retries)
6945022Sraghuram 			delay(drv_usectohz(vgen_tx_delay));
6955022Sraghuram 	}
6961991Sheppo 	if (status != VGEN_SUCCESS) {
6971991Sheppo 		/* failure */
6981991Sheppo 		return (mp);
6991991Sheppo 	}
7001991Sheppo 	/* success */
7011991Sheppo 	return (NULL);
7021991Sheppo }
7031991Sheppo 
7046419Ssb155480 /*
7056419Ssb155480  * This function provides any necessary tagging/untagging of the frames
7066419Ssb155480  * that are being transmitted over the port. It first verifies the vlan
7076419Ssb155480  * membership of the destination(port) and drops the packet if the
7086419Ssb155480  * destination doesn't belong to the given vlan.
7096419Ssb155480  *
7106419Ssb155480  * Arguments:
7116419Ssb155480  *   portp:     port over which the frames should be transmitted
7126419Ssb155480  *   mp:        frame to be transmitted
7136419Ssb155480  *   is_tagged:
7146419Ssb155480  *              B_TRUE: indicates frame header contains the vlan tag already.
7156419Ssb155480  *              B_FALSE: indicates frame is untagged.
7166419Ssb155480  *   vid:       vlan in which the frame should be transmitted.
7176419Ssb155480  *
7186419Ssb155480  * Returns:
7196419Ssb155480  *              Sucess: frame(mblk_t *) after doing the necessary tag/untag.
7206419Ssb155480  *              Failure: NULL
7216419Ssb155480  */
7226419Ssb155480 static mblk_t *
7236419Ssb155480 vgen_vlan_frame_fixtag(vgen_port_t *portp, mblk_t *mp, boolean_t is_tagged,
7246419Ssb155480 	uint16_t vid)
7256419Ssb155480 {
7266419Ssb155480 	vgen_t				*vgenp;
7276419Ssb155480 	boolean_t			dst_tagged;
7286419Ssb155480 	int				rv;
7296419Ssb155480 
7306419Ssb155480 	vgenp = portp->vgenp;
7316419Ssb155480 
7326419Ssb155480 	/*
7336419Ssb155480 	 * If the packet is going to a vnet:
7346419Ssb155480 	 *   Check if the destination vnet is in the same vlan.
7356419Ssb155480 	 *   Check the frame header if tag or untag is needed.
7366419Ssb155480 	 *
7376419Ssb155480 	 * We do not check the above conditions if the packet is going to vsw:
7386419Ssb155480 	 *   vsw must be present implicitly in all the vlans that a vnet device
7396419Ssb155480 	 *   is configured into; even if vsw itself is not assigned to those
7406419Ssb155480 	 *   vlans as an interface. For instance, the packet might be destined
7416419Ssb155480 	 *   to another vnet(indirectly through vsw) or to an external host
7426419Ssb155480 	 *   which is in the same vlan as this vnet and vsw itself may not be
7436419Ssb155480 	 *   present in that vlan. Similarly packets going to vsw must be
7446419Ssb155480 	 *   always tagged(unless in the default-vlan) if not already tagged,
7456419Ssb155480 	 *   as we do not know the final destination. This is needed because
7466419Ssb155480 	 *   vsw must always invoke its switching function only after tagging
7476419Ssb155480 	 *   the packet; otherwise after switching function determines the
7486419Ssb155480 	 *   destination we cannot figure out if the destination belongs to the
7496419Ssb155480 	 *   the same vlan that the frame originated from and if it needs tag/
7506419Ssb155480 	 *   untag. Note that vsw will tag the packet itself when it receives
7516419Ssb155480 	 *   it over the channel from a client if needed. However, that is
7526419Ssb155480 	 *   needed only in the case of vlan unaware clients such as obp or
7536419Ssb155480 	 *   earlier versions of vnet.
7546419Ssb155480 	 *
7556419Ssb155480 	 */
7566419Ssb155480 	if (portp != vgenp->vsw_portp) {
7576419Ssb155480 		/*
7586419Ssb155480 		 * Packet going to a vnet. Check if the destination vnet is in
7596419Ssb155480 		 * the same vlan. Then check the frame header if tag/untag is
7606419Ssb155480 		 * needed.
7616419Ssb155480 		 */
7626419Ssb155480 		rv = vgen_vlan_lookup(portp->vlan_hashp, vid);
7636419Ssb155480 		if (rv == B_FALSE) {
7646419Ssb155480 			/* drop the packet */
7656419Ssb155480 			freemsg(mp);
7666419Ssb155480 			return (NULL);
7676419Ssb155480 		}
7686419Ssb155480 
7696419Ssb155480 		/* is the destination tagged or untagged in this vlan? */
7706419Ssb155480 		(vid == portp->pvid) ? (dst_tagged = B_FALSE) :
7716419Ssb155480 		    (dst_tagged = B_TRUE);
7726419Ssb155480 
7736419Ssb155480 		if (is_tagged == dst_tagged) {
7746419Ssb155480 			/* no tagging/untagging needed */
7756419Ssb155480 			return (mp);
7766419Ssb155480 		}
7776419Ssb155480 
7786419Ssb155480 		if (is_tagged == B_TRUE) {
7796419Ssb155480 			/* frame is tagged; destination needs untagged */
7806419Ssb155480 			mp = vnet_vlan_remove_tag(mp);
7816419Ssb155480 			return (mp);
7826419Ssb155480 		}
7836419Ssb155480 
7846419Ssb155480 		/* (is_tagged == B_FALSE): fallthru to tag tx packet: */
7856419Ssb155480 	}
7866419Ssb155480 
7876419Ssb155480 	/*
7886419Ssb155480 	 * Packet going to a vnet needs tagging.
7896419Ssb155480 	 * OR
7906419Ssb155480 	 * If the packet is going to vsw, then it must be tagged in all cases:
7916419Ssb155480 	 * unknown unicast, broadcast/multicast or to vsw interface.
7926419Ssb155480 	 */
7936419Ssb155480 
7946419Ssb155480 	if (is_tagged == B_FALSE) {
7956419Ssb155480 		mp = vnet_vlan_insert_tag(mp, vid);
7966419Ssb155480 	}
7976419Ssb155480 
7986419Ssb155480 	return (mp);
7996419Ssb155480 }
8006419Ssb155480 
8011991Sheppo /* transmit packets over the given port */
8021991Sheppo static int
8031991Sheppo vgen_portsend(vgen_port_t *portp, mblk_t *mp)
8041991Sheppo {
8056419Ssb155480 	vgen_ldclist_t		*ldclp;
8066419Ssb155480 	vgen_ldc_t		*ldcp;
8076419Ssb155480 	int			status;
8086419Ssb155480 	int			rv = VGEN_SUCCESS;
8096495Sspeer 	vgen_t			*vgenp = portp->vgenp;
8106495Sspeer 	vnet_t			*vnetp = vgenp->vnetp;
8116419Ssb155480 	boolean_t		is_tagged;
8126495Sspeer 	boolean_t		dec_refcnt = B_FALSE;
8136419Ssb155480 	uint16_t		vlan_id;
8146419Ssb155480 	struct ether_header	*ehp;
8156419Ssb155480 
8166495Sspeer 	if (portp->use_vsw_port) {
8176495Sspeer 		(void) atomic_inc_32(&vgenp->vsw_port_refcnt);
8186495Sspeer 		portp = portp->vgenp->vsw_portp;
8196495Sspeer 		dec_refcnt = B_TRUE;
8206495Sspeer 	}
8216495Sspeer 	if (portp == NULL) {
8226495Sspeer 		return (VGEN_FAILURE);
8236495Sspeer 	}
8246419Ssb155480 
8256419Ssb155480 	/*
8266419Ssb155480 	 * Determine the vlan id that the frame belongs to.
8276419Ssb155480 	 */
8286419Ssb155480 	ehp = (struct ether_header *)mp->b_rptr;
8296419Ssb155480 	is_tagged = vgen_frame_lookup_vid(vnetp, ehp, &vlan_id);
8306419Ssb155480 
8316419Ssb155480 	if (vlan_id == vnetp->default_vlan_id) {
8326419Ssb155480 
8336419Ssb155480 		/* Frames in default vlan must be untagged */
8346419Ssb155480 		ASSERT(is_tagged == B_FALSE);
8356419Ssb155480 
8366419Ssb155480 		/*
8376419Ssb155480 		 * If the destination is a vnet-port verify it belongs to the
8386419Ssb155480 		 * default vlan; otherwise drop the packet. We do not need
8396419Ssb155480 		 * this check for vsw-port, as it should implicitly belong to
8406419Ssb155480 		 * this vlan; see comments in vgen_vlan_frame_fixtag().
8416419Ssb155480 		 */
8426419Ssb155480 		if (portp != vgenp->vsw_portp &&
8436419Ssb155480 		    portp->pvid != vnetp->default_vlan_id) {
8446419Ssb155480 			freemsg(mp);
8456495Sspeer 			goto portsend_ret;
8466419Ssb155480 		}
8476419Ssb155480 
8486419Ssb155480 	} else {	/* frame not in default-vlan */
8496419Ssb155480 
8506419Ssb155480 		mp = vgen_vlan_frame_fixtag(portp, mp, is_tagged, vlan_id);
8516419Ssb155480 		if (mp == NULL) {
8526495Sspeer 			goto portsend_ret;
8536419Ssb155480 		}
8546419Ssb155480 
8556419Ssb155480 	}
8561991Sheppo 
8571991Sheppo 	ldclp = &portp->ldclist;
8581991Sheppo 	READ_ENTER(&ldclp->rwlock);
8591991Sheppo 	/*
8602336Snarayan 	 * NOTE: for now, we will assume we have a single channel.
8611991Sheppo 	 */
8621991Sheppo 	if (ldclp->headp == NULL) {
8631991Sheppo 		RW_EXIT(&ldclp->rwlock);
8646495Sspeer 		rv = VGEN_FAILURE;
8656495Sspeer 		goto portsend_ret;
8661991Sheppo 	}
8671991Sheppo 	ldcp = ldclp->headp;
8681991Sheppo 
8695935Ssb155480 	status = ldcp->tx(ldcp, mp);
8705022Sraghuram 
8711991Sheppo 	RW_EXIT(&ldclp->rwlock);
8721991Sheppo 
8735022Sraghuram 	if (status != VGEN_TX_SUCCESS) {
8745022Sraghuram 		rv = VGEN_FAILURE;
8755022Sraghuram 	}
8766495Sspeer 
8776495Sspeer portsend_ret:
8786495Sspeer 	if (dec_refcnt == B_TRUE) {
8796495Sspeer 		(void) atomic_dec_32(&vgenp->vsw_port_refcnt);
8806495Sspeer 	}
8815022Sraghuram 	return (rv);
8821991Sheppo }
8831991Sheppo 
8845935Ssb155480 /*
8855935Ssb155480  * Wrapper function to transmit normal and/or priority frames over the channel.
8865935Ssb155480  */
8871991Sheppo static int
8885935Ssb155480 vgen_ldcsend(void *arg, mblk_t *mp)
8895935Ssb155480 {
8905935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg;
8915935Ssb155480 	int			status;
8925935Ssb155480 	struct ether_header	*ehp;
8935935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
8945935Ssb155480 	uint32_t		num_types;
8955935Ssb155480 	uint16_t		*types;
8965935Ssb155480 	int			i;
8975935Ssb155480 
8985935Ssb155480 	ASSERT(VGEN_PRI_ETH_DEFINED(vgenp));
8995935Ssb155480 
9005935Ssb155480 	num_types = vgenp->pri_num_types;
9015935Ssb155480 	types = vgenp->pri_types;
9025935Ssb155480 	ehp = (struct ether_header *)mp->b_rptr;
9035935Ssb155480 
9045935Ssb155480 	for (i = 0; i < num_types; i++) {
9055935Ssb155480 
9065935Ssb155480 		if (ehp->ether_type == types[i]) {
9075935Ssb155480 			/* priority frame, use pri tx function */
9085935Ssb155480 			vgen_ldcsend_pkt(ldcp, mp);
9095935Ssb155480 			return (VGEN_SUCCESS);
9105935Ssb155480 		}
9115935Ssb155480 
9125935Ssb155480 	}
9135935Ssb155480 
9145935Ssb155480 	status  = vgen_ldcsend_dring(ldcp, mp);
9155935Ssb155480 
9165935Ssb155480 	return (status);
9175935Ssb155480 }
9185935Ssb155480 
9195935Ssb155480 /*
9205935Ssb155480  * This functions handles ldc channel reset while in the context
9215935Ssb155480  * of transmit routines: vgen_ldcsend_pkt() or vgen_ldcsend_dring().
9225935Ssb155480  */
9235935Ssb155480 static void
9245935Ssb155480 vgen_ldcsend_process_reset(vgen_ldc_t *ldcp)
9255935Ssb155480 {
9265935Ssb155480 	ldc_status_t	istatus;
9275935Ssb155480 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
9285935Ssb155480 
9295935Ssb155480 	if (mutex_tryenter(&ldcp->cblock)) {
9305935Ssb155480 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
9315935Ssb155480 			DWARN(vgenp, ldcp, "ldc_status() error\n");
9325935Ssb155480 		} else {
9335935Ssb155480 			ldcp->ldc_status = istatus;
9345935Ssb155480 		}
9355935Ssb155480 		if (ldcp->ldc_status != LDC_UP) {
9366495Sspeer 			vgen_handle_evt_reset(ldcp);
9375935Ssb155480 		}
9385935Ssb155480 		mutex_exit(&ldcp->cblock);
9395935Ssb155480 	}
9405935Ssb155480 }
9415935Ssb155480 
9425935Ssb155480 /*
9435935Ssb155480  * This function transmits the frame in the payload of a raw data
9445935Ssb155480  * (VIO_PKT_DATA) message. Thus, it provides an Out-Of-Band path to
9455935Ssb155480  * send special frames with high priorities, without going through
9465935Ssb155480  * the normal data path which uses descriptor ring mechanism.
9475935Ssb155480  */
9485935Ssb155480 static void
9495935Ssb155480 vgen_ldcsend_pkt(void *arg, mblk_t *mp)
9501991Sheppo {
9515935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg;
9525935Ssb155480 	vio_raw_data_msg_t	*pkt;
9535935Ssb155480 	mblk_t			*bp;
9545935Ssb155480 	mblk_t			*nmp = NULL;
9555935Ssb155480 	caddr_t			dst;
9565935Ssb155480 	uint32_t		mblksz;
9575935Ssb155480 	uint32_t		size;
9585935Ssb155480 	uint32_t		nbytes;
9595935Ssb155480 	int			rv;
9605935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
9615935Ssb155480 	vgen_stats_t		*statsp = &ldcp->stats;
9625935Ssb155480 
9635935Ssb155480 	/* drop the packet if ldc is not up or handshake is not done */
9645935Ssb155480 	if (ldcp->ldc_status != LDC_UP) {
9655935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9665935Ssb155480 		DWARN(vgenp, ldcp, "status(%d), dropping packet\n",
9675935Ssb155480 		    ldcp->ldc_status);
9685935Ssb155480 		goto send_pkt_exit;
9695935Ssb155480 	}
9705935Ssb155480 
9715935Ssb155480 	if (ldcp->hphase != VH_DONE) {
9725935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9735935Ssb155480 		DWARN(vgenp, ldcp, "hphase(%x), dropping packet\n",
9745935Ssb155480 		    ldcp->hphase);
9755935Ssb155480 		goto send_pkt_exit;
9765935Ssb155480 	}
9775935Ssb155480 
9785935Ssb155480 	size = msgsize(mp);
9795935Ssb155480 
9805935Ssb155480 	/* frame size bigger than available payload len of raw data msg ? */
9815935Ssb155480 	if (size > (size_t)(ldcp->msglen - VIO_PKT_DATA_HDRSIZE)) {
9825935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9835935Ssb155480 		DWARN(vgenp, ldcp, "invalid size(%d)\n", size);
9845935Ssb155480 		goto send_pkt_exit;
9855935Ssb155480 	}
9865935Ssb155480 
9875935Ssb155480 	if (size < ETHERMIN)
9885935Ssb155480 		size = ETHERMIN;
9895935Ssb155480 
9905935Ssb155480 	/* alloc space for a raw data message */
9915935Ssb155480 	nmp = vio_allocb(vgenp->pri_tx_vmp);
9925935Ssb155480 	if (nmp == NULL) {
9935935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
9945935Ssb155480 		DWARN(vgenp, ldcp, "vio_allocb failed\n");
9955935Ssb155480 		goto send_pkt_exit;
9965935Ssb155480 	}
9975935Ssb155480 	pkt = (vio_raw_data_msg_t *)nmp->b_rptr;
9985935Ssb155480 
9995935Ssb155480 	/* copy frame into the payload of raw data message */
10005935Ssb155480 	dst = (caddr_t)pkt->data;
10015935Ssb155480 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
10025935Ssb155480 		mblksz = MBLKL(bp);
10035935Ssb155480 		bcopy(bp->b_rptr, dst, mblksz);
10045935Ssb155480 		dst += mblksz;
10055935Ssb155480 	}
10065935Ssb155480 
10075935Ssb155480 	/* setup the raw data msg */
10085935Ssb155480 	pkt->tag.vio_msgtype = VIO_TYPE_DATA;
10095935Ssb155480 	pkt->tag.vio_subtype = VIO_SUBTYPE_INFO;
10105935Ssb155480 	pkt->tag.vio_subtype_env = VIO_PKT_DATA;
10115935Ssb155480 	pkt->tag.vio_sid = ldcp->local_sid;
10125935Ssb155480 	nbytes = VIO_PKT_DATA_HDRSIZE + size;
10135935Ssb155480 
10145935Ssb155480 	/* send the msg over ldc */
10155935Ssb155480 	rv = vgen_sendmsg(ldcp, (caddr_t)pkt, nbytes, B_FALSE);
10165935Ssb155480 	if (rv != VGEN_SUCCESS) {
10175935Ssb155480 		(void) atomic_inc_32(&statsp->tx_pri_fail);
10185935Ssb155480 		DWARN(vgenp, ldcp, "Error sending priority frame\n");
10195935Ssb155480 		if (rv == ECONNRESET) {
10205935Ssb155480 			vgen_ldcsend_process_reset(ldcp);
10215935Ssb155480 		}
10225935Ssb155480 		goto send_pkt_exit;
10235935Ssb155480 	}
10245935Ssb155480 
10255935Ssb155480 	/* update stats */
10265935Ssb155480 	(void) atomic_inc_64(&statsp->tx_pri_packets);
10275935Ssb155480 	(void) atomic_add_64(&statsp->tx_pri_bytes, size);
10285935Ssb155480 
10295935Ssb155480 send_pkt_exit:
10305935Ssb155480 	if (nmp != NULL)
10315935Ssb155480 		freemsg(nmp);
10325935Ssb155480 	freemsg(mp);
10335935Ssb155480 }
10345935Ssb155480 
10355935Ssb155480 /*
10365935Ssb155480  * This function transmits normal (non-priority) data frames over
10375935Ssb155480  * the channel. It queues the frame into the transmit descriptor ring
10385935Ssb155480  * and sends a VIO_DRING_DATA message if needed, to wake up the
10395935Ssb155480  * peer to (re)start processing.
10405935Ssb155480  */
10415935Ssb155480 static int
10425935Ssb155480 vgen_ldcsend_dring(void *arg, mblk_t *mp)
10435935Ssb155480 {
10445935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg;
10451991Sheppo 	vgen_private_desc_t	*tbufp;
10464647Sraghuram 	vgen_private_desc_t	*rtbufp;
10474647Sraghuram 	vnet_public_desc_t	*rtxdp;
10481991Sheppo 	vgen_private_desc_t	*ntbufp;
10491991Sheppo 	vnet_public_desc_t	*txdp;
10504647Sraghuram 	vio_dring_entry_hdr_t	*hdrp;
10511991Sheppo 	vgen_stats_t		*statsp;
10521991Sheppo 	struct ether_header	*ehp;
10536419Ssb155480 	boolean_t		is_bcast = B_FALSE;
10546419Ssb155480 	boolean_t		is_mcast = B_FALSE;
10556419Ssb155480 	size_t			mblksz;
10566419Ssb155480 	caddr_t			dst;
10576419Ssb155480 	mblk_t			*bp;
10586419Ssb155480 	size_t			size;
10596419Ssb155480 	int			rv = 0;
10606419Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
10616419Ssb155480 	vgen_hparams_t		*lp = &ldcp->local_hparams;
10624647Sraghuram 
10635373Sraghuram 	statsp = &ldcp->stats;
10642109Slm66018 	size = msgsize(mp);
10652109Slm66018 
10664647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
10674647Sraghuram 
10682109Slm66018 	if (ldcp->ldc_status != LDC_UP) {
10694647Sraghuram 		DWARN(vgenp, ldcp, "status(%d), dropping packet\n",
10704647Sraghuram 		    ldcp->ldc_status);
10712109Slm66018 		/* retry ldc_up() if needed */
10722109Slm66018 		if (ldcp->flags & CHANNEL_STARTED)
10732109Slm66018 			(void) ldc_up(ldcp->ldc_handle);
10745935Ssb155480 		goto send_dring_exit;
10751991Sheppo 	}
10761991Sheppo 
10774647Sraghuram 	/* drop the packet if ldc is not up or handshake is not done */
10782109Slm66018 	if (ldcp->hphase != VH_DONE) {
10794647Sraghuram 		DWARN(vgenp, ldcp, "hphase(%x), dropping packet\n",
10804647Sraghuram 		    ldcp->hphase);
10815935Ssb155480 		goto send_dring_exit;
10822109Slm66018 	}
10832109Slm66018 
10846419Ssb155480 	if (size > (size_t)lp->mtu) {
10854647Sraghuram 		DWARN(vgenp, ldcp, "invalid size(%d)\n", size);
10865935Ssb155480 		goto send_dring_exit;
10871991Sheppo 	}
10884647Sraghuram 	if (size < ETHERMIN)
10894647Sraghuram 		size = ETHERMIN;
10904647Sraghuram 
10914647Sraghuram 	ehp = (struct ether_header *)mp->b_rptr;
10924647Sraghuram 	is_bcast = IS_BROADCAST(ehp);
10934647Sraghuram 	is_mcast = IS_MULTICAST(ehp);
10944647Sraghuram 
10954647Sraghuram 	mutex_enter(&ldcp->txlock);
10961991Sheppo 	/*
10971991Sheppo 	 * allocate a descriptor
10981991Sheppo 	 */
10991991Sheppo 	tbufp = ldcp->next_tbufp;
11001991Sheppo 	ntbufp = NEXTTBUF(ldcp, tbufp);
11012336Snarayan 	if (ntbufp == ldcp->cur_tbufp) { /* out of tbufs/txds */
11021991Sheppo 
11031991Sheppo 		mutex_enter(&ldcp->tclock);
11044647Sraghuram 		/* Try reclaiming now */
11054647Sraghuram 		vgen_reclaim_dring(ldcp);
11064647Sraghuram 		ldcp->reclaim_lbolt = ddi_get_lbolt();
11074647Sraghuram 
11082336Snarayan 		if (ntbufp == ldcp->cur_tbufp) {
11094647Sraghuram 			/* Now we are really out of tbuf/txds */
11101991Sheppo 			ldcp->need_resched = B_TRUE;
11112336Snarayan 			mutex_exit(&ldcp->tclock);
11122336Snarayan 
11132336Snarayan 			statsp->tx_no_desc++;
11142336Snarayan 			mutex_exit(&ldcp->txlock);
11152336Snarayan 
11162336Snarayan 			return (VGEN_TX_NORESOURCES);
11172336Snarayan 		}
11181991Sheppo 		mutex_exit(&ldcp->tclock);
11191991Sheppo 	}
11204647Sraghuram 	/* update next available tbuf in the ring and update tx index */
11214647Sraghuram 	ldcp->next_tbufp = ntbufp;
11224647Sraghuram 	INCR_TXI(ldcp->next_txi, ldcp);
11234647Sraghuram 
11244647Sraghuram 	/* Mark the buffer busy before releasing the lock */
11254647Sraghuram 	tbufp->flags = VGEN_PRIV_DESC_BUSY;
11264647Sraghuram 	mutex_exit(&ldcp->txlock);
11272109Slm66018 
11282109Slm66018 	/* copy data into pre-allocated transmit buffer */
11292336Snarayan 	dst = tbufp->datap + VNET_IPALIGN;
11302336Snarayan 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
11312336Snarayan 		mblksz = MBLKL(bp);
11322336Snarayan 		bcopy(bp->b_rptr, dst, mblksz);
11332336Snarayan 		dst += mblksz;
11341991Sheppo 	}
11351991Sheppo 
11362109Slm66018 	tbufp->datalen = size;
11371991Sheppo 
11381991Sheppo 	/* initialize the corresponding public descriptor (txd) */
11391991Sheppo 	txdp = tbufp->descp;
11401991Sheppo 	hdrp = &txdp->hdr;
11412109Slm66018 	txdp->nbytes = size;
11422109Slm66018 	txdp->ncookies = tbufp->ncookies;
11431991Sheppo 	bcopy((tbufp->memcookie), (txdp->memcookie),
11444650Sraghuram 	    tbufp->ncookies * sizeof (ldc_mem_cookie_t));
11454647Sraghuram 
11464647Sraghuram 	mutex_enter(&ldcp->wrlock);
11474647Sraghuram 	/*
11484647Sraghuram 	 * If the flags not set to BUSY, it implies that the clobber
11494647Sraghuram 	 * was done while we were copying the data. In such case,
11504647Sraghuram 	 * discard the packet and return.
11514647Sraghuram 	 */
11524647Sraghuram 	if (tbufp->flags != VGEN_PRIV_DESC_BUSY) {
11534647Sraghuram 		statsp->oerrors++;
11544647Sraghuram 		mutex_exit(&ldcp->wrlock);
11555935Ssb155480 		goto send_dring_exit;
11564647Sraghuram 	}
11572336Snarayan 	hdrp->dstate = VIO_DESC_READY;
11581991Sheppo 
11591991Sheppo 	/* update stats */
11601991Sheppo 	statsp->opackets++;
11612109Slm66018 	statsp->obytes += size;
11621991Sheppo 	if (is_bcast)
11631991Sheppo 		statsp->brdcstxmt++;
11641991Sheppo 	else if (is_mcast)
11651991Sheppo 		statsp->multixmt++;
11661991Sheppo 
11674647Sraghuram 	/* send dring datamsg to the peer */
11684647Sraghuram 	if (ldcp->resched_peer) {
11694647Sraghuram 
11704647Sraghuram 		rtbufp = &ldcp->tbufp[ldcp->resched_peer_txi];
11714647Sraghuram 		rtxdp = rtbufp->descp;
11724647Sraghuram 
11734647Sraghuram 		if (rtxdp->hdr.dstate == VIO_DESC_READY) {
11744647Sraghuram 
11754647Sraghuram 			rv = vgen_send_dring_data(ldcp,
11764647Sraghuram 			    (uint32_t)ldcp->resched_peer_txi, -1);
11774647Sraghuram 			if (rv != 0) {
11784647Sraghuram 				/* error: drop the packet */
11794647Sraghuram 				DWARN(vgenp, ldcp, "vgen_send_dring_data "
11804650Sraghuram 				    "failed: rv(%d) len(%d)\n",
11814650Sraghuram 				    ldcp->ldc_id, rv, size);
11824647Sraghuram 				statsp->oerrors++;
11834647Sraghuram 			} else {
11844647Sraghuram 				ldcp->resched_peer = B_FALSE;
11854647Sraghuram 			}
11864647Sraghuram 
11874647Sraghuram 		}
11884647Sraghuram 
11894647Sraghuram 	}
11904647Sraghuram 
11914647Sraghuram 	mutex_exit(&ldcp->wrlock);
11924647Sraghuram 
11935935Ssb155480 send_dring_exit:
11942793Slm66018 	if (rv == ECONNRESET) {
11955935Ssb155480 		vgen_ldcsend_process_reset(ldcp);
11962793Slm66018 	}
11972109Slm66018 	freemsg(mp);
11984647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
11992109Slm66018 	return (VGEN_TX_SUCCESS);
12001991Sheppo }
12011991Sheppo 
12021991Sheppo /* enable/disable a multicast address */
12032311Sseb int
12041991Sheppo vgen_multicst(void *arg, boolean_t add, const uint8_t *mca)
12051991Sheppo {
12061991Sheppo 	vgen_t			*vgenp;
12071991Sheppo 	vnet_mcast_msg_t	mcastmsg;
12081991Sheppo 	vio_msg_tag_t		*tagp;
12091991Sheppo 	vgen_port_t		*portp;
12101991Sheppo 	vgen_portlist_t		*plistp;
12111991Sheppo 	vgen_ldc_t		*ldcp;
12121991Sheppo 	vgen_ldclist_t		*ldclp;
12131991Sheppo 	struct ether_addr	*addrp;
12143653Snarayan 	int			rv = DDI_FAILURE;
12151991Sheppo 	uint32_t		i;
12161991Sheppo 
12176495Sspeer 	portp = (vgen_port_t *)arg;
12186495Sspeer 	vgenp = portp->vgenp;
12196495Sspeer 
12206495Sspeer 	if (portp != vgenp->vsw_portp) {
12216495Sspeer 		return (DDI_SUCCESS);
12226495Sspeer 	}
12236495Sspeer 
12241991Sheppo 	addrp = (struct ether_addr *)mca;
12251991Sheppo 	tagp = &mcastmsg.tag;
12261991Sheppo 	bzero(&mcastmsg, sizeof (mcastmsg));
12271991Sheppo 
12281991Sheppo 	mutex_enter(&vgenp->lock);
12291991Sheppo 
12301991Sheppo 	plistp = &(vgenp->vgenports);
12311991Sheppo 
12321991Sheppo 	READ_ENTER(&plistp->rwlock);
12331991Sheppo 
12341991Sheppo 	portp = vgenp->vsw_portp;
12351991Sheppo 	if (portp == NULL) {
12361991Sheppo 		RW_EXIT(&plistp->rwlock);
12373653Snarayan 		mutex_exit(&vgenp->lock);
12383653Snarayan 		return (rv);
12391991Sheppo 	}
12401991Sheppo 	ldclp = &portp->ldclist;
12411991Sheppo 
12421991Sheppo 	READ_ENTER(&ldclp->rwlock);
12431991Sheppo 
12441991Sheppo 	ldcp = ldclp->headp;
12453653Snarayan 	if (ldcp == NULL)
12461991Sheppo 		goto vgen_mcast_exit;
12471991Sheppo 
12481991Sheppo 	mutex_enter(&ldcp->cblock);
12491991Sheppo 
12501991Sheppo 	if (ldcp->hphase == VH_DONE) {
12511991Sheppo 		/*
12521991Sheppo 		 * If handshake is done, send a msg to vsw to add/remove
12535171Ssb155480 		 * the multicast address. Otherwise, we just update this
12545171Ssb155480 		 * mcast address in our table and the table will be sync'd
12555171Ssb155480 		 * with vsw when handshake completes.
12561991Sheppo 		 */
12571991Sheppo 		tagp->vio_msgtype = VIO_TYPE_CTRL;
12581991Sheppo 		tagp->vio_subtype = VIO_SUBTYPE_INFO;
12591991Sheppo 		tagp->vio_subtype_env = VNET_MCAST_INFO;
12601991Sheppo 		tagp->vio_sid = ldcp->local_sid;
12611991Sheppo 		bcopy(mca, &(mcastmsg.mca), ETHERADDRL);
12621991Sheppo 		mcastmsg.set = add;
12631991Sheppo 		mcastmsg.count = 1;
12643653Snarayan 		if (vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (mcastmsg),
12653653Snarayan 		    B_FALSE) != VGEN_SUCCESS) {
12664647Sraghuram 			DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
12673653Snarayan 			mutex_exit(&ldcp->cblock);
12683653Snarayan 			goto vgen_mcast_exit;
12691991Sheppo 		}
12701991Sheppo 	}
12711991Sheppo 
12721991Sheppo 	mutex_exit(&ldcp->cblock);
12731991Sheppo 
12741991Sheppo 	if (add) {
12751991Sheppo 
12761991Sheppo 		/* expand multicast table if necessary */
12771991Sheppo 		if (vgenp->mccount >= vgenp->mcsize) {
12781991Sheppo 			struct ether_addr	*newtab;
12791991Sheppo 			uint32_t		newsize;
12801991Sheppo 
12811991Sheppo 
12821991Sheppo 			newsize = vgenp->mcsize * 2;
12831991Sheppo 
12841991Sheppo 			newtab = kmem_zalloc(newsize *
12851991Sheppo 			    sizeof (struct ether_addr), KM_NOSLEEP);
12863653Snarayan 			if (newtab == NULL)
12873653Snarayan 				goto vgen_mcast_exit;
12881991Sheppo 			bcopy(vgenp->mctab, newtab, vgenp->mcsize *
12891991Sheppo 			    sizeof (struct ether_addr));
12901991Sheppo 			kmem_free(vgenp->mctab,
12911991Sheppo 			    vgenp->mcsize * sizeof (struct ether_addr));
12921991Sheppo 
12931991Sheppo 			vgenp->mctab = newtab;
12941991Sheppo 			vgenp->mcsize = newsize;
12951991Sheppo 		}
12961991Sheppo 
12971991Sheppo 		/* add address to the table */
12981991Sheppo 		vgenp->mctab[vgenp->mccount++] = *addrp;
12991991Sheppo 
13001991Sheppo 	} else {
13011991Sheppo 
13021991Sheppo 		/* delete address from the table */
13031991Sheppo 		for (i = 0; i < vgenp->mccount; i++) {
13041991Sheppo 			if (ether_cmp(addrp, &(vgenp->mctab[i])) == 0) {
13051991Sheppo 
13061991Sheppo 				/*
13071991Sheppo 				 * If there's more than one address in this
13081991Sheppo 				 * table, delete the unwanted one by moving
13091991Sheppo 				 * the last one in the list over top of it;
13101991Sheppo 				 * otherwise, just remove it.
13111991Sheppo 				 */
13121991Sheppo 				if (vgenp->mccount > 1) {
13131991Sheppo 					vgenp->mctab[i] =
13144650Sraghuram 					    vgenp->mctab[vgenp->mccount-1];
13151991Sheppo 				}
13161991Sheppo 				vgenp->mccount--;
13171991Sheppo 				break;
13181991Sheppo 			}
13191991Sheppo 		}
13201991Sheppo 	}
13211991Sheppo 
13223653Snarayan 	rv = DDI_SUCCESS;
13233653Snarayan 
13243653Snarayan vgen_mcast_exit:
13251991Sheppo 	RW_EXIT(&ldclp->rwlock);
13261991Sheppo 	RW_EXIT(&plistp->rwlock);
13271991Sheppo 
13281991Sheppo 	mutex_exit(&vgenp->lock);
13293653Snarayan 	return (rv);
13301991Sheppo }
13311991Sheppo 
13321991Sheppo /* set or clear promiscuous mode on the device */
13331991Sheppo static int
13341991Sheppo vgen_promisc(void *arg, boolean_t on)
13351991Sheppo {
13361991Sheppo 	_NOTE(ARGUNUSED(arg, on))
13371991Sheppo 	return (DDI_SUCCESS);
13381991Sheppo }
13391991Sheppo 
13401991Sheppo /* set the unicast mac address of the device */
13411991Sheppo static int
13421991Sheppo vgen_unicst(void *arg, const uint8_t *mca)
13431991Sheppo {
13441991Sheppo 	_NOTE(ARGUNUSED(arg, mca))
13451991Sheppo 	return (DDI_SUCCESS);
13461991Sheppo }
13471991Sheppo 
13481991Sheppo /* get device statistics */
13492311Sseb int
13502311Sseb vgen_stat(void *arg, uint_t stat, uint64_t *val)
13511991Sheppo {
13526495Sspeer 	vgen_port_t	*portp = (vgen_port_t *)arg;
13536495Sspeer 
13546495Sspeer 	*val = vgen_port_stat(portp, stat);
13551991Sheppo 
13562311Sseb 	return (0);
13571991Sheppo }
13581991Sheppo 
13591991Sheppo static void
13601991Sheppo vgen_ioctl(void *arg, queue_t *wq, mblk_t *mp)
13611991Sheppo {
13621991Sheppo 	 _NOTE(ARGUNUSED(arg, wq, mp))
13631991Sheppo }
13641991Sheppo 
13651991Sheppo /* vgen internal functions */
13661991Sheppo /* detach all ports from the device */
13671991Sheppo static void
13681991Sheppo vgen_detach_ports(vgen_t *vgenp)
13691991Sheppo {
13701991Sheppo 	vgen_port_t	*portp;
13711991Sheppo 	vgen_portlist_t	*plistp;
13721991Sheppo 
13731991Sheppo 	plistp = &(vgenp->vgenports);
13741991Sheppo 	WRITE_ENTER(&plistp->rwlock);
13751991Sheppo 	while ((portp = plistp->headp) != NULL) {
13761991Sheppo 		vgen_port_detach(portp);
13771991Sheppo 	}
13781991Sheppo 	RW_EXIT(&plistp->rwlock);
13791991Sheppo }
13801991Sheppo 
13811991Sheppo /*
13821991Sheppo  * detach the given port.
13831991Sheppo  */
13841991Sheppo static void
13851991Sheppo vgen_port_detach(vgen_port_t *portp)
13861991Sheppo {
13871991Sheppo 	vgen_t		*vgenp;
13881991Sheppo 	vgen_ldclist_t	*ldclp;
13891991Sheppo 	int		port_num;
13901991Sheppo 
13911991Sheppo 	vgenp = portp->vgenp;
13921991Sheppo 	port_num = portp->port_num;
13931991Sheppo 
13944647Sraghuram 	DBG1(vgenp, NULL, "port(%d):enter\n", port_num);
13951991Sheppo 
13966495Sspeer 	/*
13976495Sspeer 	 * If this port is connected to the vswitch, then
13986495Sspeer 	 * potentially there could be ports that may be using
13996495Sspeer 	 * this port to transmit packets. To address this do
14006495Sspeer 	 * the following:
14016495Sspeer 	 *	- First set vgenp->vsw_portp to NULL, so that
14026495Sspeer 	 *	  its not used after that.
14036495Sspeer 	 *	- Then wait for the refcnt to go down to 0.
14046495Sspeer 	 *	- Now we can safely detach this port.
14056495Sspeer 	 */
14066495Sspeer 	if (vgenp->vsw_portp == portp) {
14076495Sspeer 		vgenp->vsw_portp = NULL;
14086495Sspeer 		while (vgenp->vsw_port_refcnt > 0) {
14096495Sspeer 			delay(drv_usectohz(vgen_tx_delay));
14106495Sspeer 		}
14116495Sspeer 		(void) atomic_swap_32(&vgenp->vsw_port_refcnt, 0);
14126495Sspeer 	}
14136495Sspeer 
14146495Sspeer 	if (portp->vhp != NULL) {
14156495Sspeer 		vio_net_resource_unreg(portp->vhp);
14166495Sspeer 		portp->vhp = NULL;
14176495Sspeer 	}
14186495Sspeer 
14196419Ssb155480 	vgen_vlan_destroy_hash(portp);
14206419Ssb155480 
14211991Sheppo 	/* remove it from port list */
14221991Sheppo 	vgen_port_list_remove(portp);
14231991Sheppo 
14241991Sheppo 	/* detach channels from this port */
14251991Sheppo 	ldclp = &portp->ldclist;
14261991Sheppo 	WRITE_ENTER(&ldclp->rwlock);
14271991Sheppo 	while (ldclp->headp) {
14281991Sheppo 		vgen_ldc_detach(ldclp->headp);
14291991Sheppo 	}
14301991Sheppo 	RW_EXIT(&ldclp->rwlock);
14315641Swentaoy 	rw_destroy(&ldclp->rwlock);
14321991Sheppo 
14336419Ssb155480 	if (portp->num_ldcs != 0) {
14346419Ssb155480 		kmem_free(portp->ldc_ids, portp->num_ldcs * sizeof (uint64_t));
14356419Ssb155480 		portp->num_ldcs = 0;
14366419Ssb155480 	}
14376419Ssb155480 
14386495Sspeer 	mutex_destroy(&portp->lock);
14391991Sheppo 	KMEM_FREE(portp);
14401991Sheppo 
14414647Sraghuram 	DBG1(vgenp, NULL, "port(%d):exit\n", port_num);
14421991Sheppo }
14431991Sheppo 
14441991Sheppo /* add a port to port list */
14451991Sheppo static void
14461991Sheppo vgen_port_list_insert(vgen_port_t *portp)
14471991Sheppo {
14481991Sheppo 	vgen_portlist_t *plistp;
14491991Sheppo 	vgen_t *vgenp;
14501991Sheppo 
14511991Sheppo 	vgenp = portp->vgenp;
14521991Sheppo 	plistp = &(vgenp->vgenports);
14531991Sheppo 
14541991Sheppo 	if (plistp->headp == NULL) {
14551991Sheppo 		plistp->headp = portp;
14561991Sheppo 	} else {
14571991Sheppo 		plistp->tailp->nextp = portp;
14581991Sheppo 	}
14591991Sheppo 	plistp->tailp = portp;
14601991Sheppo 	portp->nextp = NULL;
14611991Sheppo }
14621991Sheppo 
14631991Sheppo /* remove a port from port list */
14641991Sheppo static void
14651991Sheppo vgen_port_list_remove(vgen_port_t *portp)
14661991Sheppo {
14671991Sheppo 	vgen_port_t *prevp;
14681991Sheppo 	vgen_port_t *nextp;
14691991Sheppo 	vgen_portlist_t *plistp;
14701991Sheppo 	vgen_t *vgenp;
14711991Sheppo 
14721991Sheppo 	vgenp = portp->vgenp;
14731991Sheppo 
14741991Sheppo 	plistp = &(vgenp->vgenports);
14751991Sheppo 
14761991Sheppo 	if (plistp->headp == NULL)
14771991Sheppo 		return;
14781991Sheppo 
14791991Sheppo 	if (portp == plistp->headp) {
14801991Sheppo 		plistp->headp = portp->nextp;
14811991Sheppo 		if (portp == plistp->tailp)
14821991Sheppo 			plistp->tailp = plistp->headp;
14831991Sheppo 	} else {
14844650Sraghuram 		for (prevp = plistp->headp;
14854650Sraghuram 		    ((nextp = prevp->nextp) != NULL) && (nextp != portp);
14864650Sraghuram 		    prevp = nextp)
14874650Sraghuram 			;
14881991Sheppo 		if (nextp == portp) {
14891991Sheppo 			prevp->nextp = portp->nextp;
14901991Sheppo 		}
14911991Sheppo 		if (portp == plistp->tailp)
14921991Sheppo 			plistp->tailp = prevp;
14931991Sheppo 	}
14941991Sheppo }
14951991Sheppo 
14961991Sheppo /* lookup a port in the list based on port_num */
14971991Sheppo static vgen_port_t *
14981991Sheppo vgen_port_lookup(vgen_portlist_t *plistp, int port_num)
14991991Sheppo {
15001991Sheppo 	vgen_port_t *portp = NULL;
15011991Sheppo 
15021991Sheppo 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
15031991Sheppo 		if (portp->port_num == port_num) {
15041991Sheppo 			break;
15051991Sheppo 		}
15061991Sheppo 	}
15071991Sheppo 
15081991Sheppo 	return (portp);
15091991Sheppo }
15101991Sheppo 
15111991Sheppo /* enable ports for transmit/receive */
15121991Sheppo static void
15131991Sheppo vgen_init_ports(vgen_t *vgenp)
15141991Sheppo {
15151991Sheppo 	vgen_port_t	*portp;
15161991Sheppo 	vgen_portlist_t	*plistp;
15171991Sheppo 
15181991Sheppo 	plistp = &(vgenp->vgenports);
15191991Sheppo 	READ_ENTER(&plistp->rwlock);
15201991Sheppo 
15211991Sheppo 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
15221991Sheppo 		vgen_port_init(portp);
15231991Sheppo 	}
15241991Sheppo 
15251991Sheppo 	RW_EXIT(&plistp->rwlock);
15261991Sheppo }
15271991Sheppo 
15281991Sheppo static void
15291991Sheppo vgen_port_init(vgen_port_t *portp)
15301991Sheppo {
15316419Ssb155480 	/* Add the port to the specified vlans */
15326419Ssb155480 	vgen_vlan_add_ids(portp);
15331991Sheppo 
15341991Sheppo 	/* Bring up the channels of this port */
15351991Sheppo 	vgen_init_ldcs(portp);
15361991Sheppo }
15371991Sheppo 
15381991Sheppo /* disable transmit/receive on ports */
15391991Sheppo static void
15401991Sheppo vgen_uninit_ports(vgen_t *vgenp)
15411991Sheppo {
15421991Sheppo 	vgen_port_t	*portp;
15431991Sheppo 	vgen_portlist_t	*plistp;
15441991Sheppo 
15451991Sheppo 	plistp = &(vgenp->vgenports);
15461991Sheppo 	READ_ENTER(&plistp->rwlock);
15471991Sheppo 
15481991Sheppo 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
15491991Sheppo 		vgen_port_uninit(portp);
15501991Sheppo 	}
15511991Sheppo 
15521991Sheppo 	RW_EXIT(&plistp->rwlock);
15531991Sheppo }
15541991Sheppo 
15551991Sheppo static void
15561991Sheppo vgen_port_uninit(vgen_port_t *portp)
15571991Sheppo {
15581991Sheppo 	vgen_uninit_ldcs(portp);
15596419Ssb155480 
15606419Ssb155480 	/* remove the port from vlans it has been assigned to */
15616419Ssb155480 	vgen_vlan_remove_ids(portp);
15621991Sheppo }
15631991Sheppo 
15645935Ssb155480 /*
15655935Ssb155480  * Scan the machine description for this instance of vnet
15665935Ssb155480  * and read its properties. Called only from vgen_init().
15675935Ssb155480  * Returns: 0 on success, 1 on failure.
15685935Ssb155480  */
15695935Ssb155480 static int
15705935Ssb155480 vgen_read_mdprops(vgen_t *vgenp)
15715935Ssb155480 {
15726419Ssb155480 	vnet_t		*vnetp = vgenp->vnetp;
15735935Ssb155480 	md_t		*mdp = NULL;
15745935Ssb155480 	mde_cookie_t	rootnode;
15755935Ssb155480 	mde_cookie_t	*listp = NULL;
15765935Ssb155480 	uint64_t	cfgh;
15775935Ssb155480 	char		*name;
15785935Ssb155480 	int		rv = 1;
15795935Ssb155480 	int		num_nodes = 0;
15805935Ssb155480 	int		num_devs = 0;
15815935Ssb155480 	int		listsz = 0;
15825935Ssb155480 	int		i;
15835935Ssb155480 
15845935Ssb155480 	if ((mdp = md_get_handle()) == NULL) {
15855935Ssb155480 		return (rv);
15865935Ssb155480 	}
15875935Ssb155480 
15885935Ssb155480 	num_nodes = md_node_count(mdp);
15895935Ssb155480 	ASSERT(num_nodes > 0);
15905935Ssb155480 
15915935Ssb155480 	listsz = num_nodes * sizeof (mde_cookie_t);
15925935Ssb155480 	listp = (mde_cookie_t *)kmem_zalloc(listsz, KM_SLEEP);
15935935Ssb155480 
15945935Ssb155480 	rootnode = md_root_node(mdp);
15955935Ssb155480 
15965935Ssb155480 	/* search for all "virtual_device" nodes */
15975935Ssb155480 	num_devs = md_scan_dag(mdp, rootnode,
15985935Ssb155480 	    md_find_name(mdp, vdev_propname),
15995935Ssb155480 	    md_find_name(mdp, "fwd"), listp);
16005935Ssb155480 	if (num_devs <= 0) {
16015935Ssb155480 		goto vgen_readmd_exit;
16025935Ssb155480 	}
16035935Ssb155480 
16045935Ssb155480 	/*
16055935Ssb155480 	 * Now loop through the list of virtual-devices looking for
16065935Ssb155480 	 * devices with name "network" and for each such device compare
16075935Ssb155480 	 * its instance with what we have from the 'reg' property to
16085935Ssb155480 	 * find the right node in MD and then read all its properties.
16095935Ssb155480 	 */
16105935Ssb155480 	for (i = 0; i < num_devs; i++) {
16115935Ssb155480 
16125935Ssb155480 		if (md_get_prop_str(mdp, listp[i], "name", &name) != 0) {
16135935Ssb155480 			goto vgen_readmd_exit;
16145935Ssb155480 		}
16155935Ssb155480 
16165935Ssb155480 		/* is this a "network" device? */
16175935Ssb155480 		if (strcmp(name, vnet_propname) != 0)
16185935Ssb155480 			continue;
16195935Ssb155480 
16205935Ssb155480 		if (md_get_prop_val(mdp, listp[i], "cfg-handle", &cfgh) != 0) {
16215935Ssb155480 			goto vgen_readmd_exit;
16225935Ssb155480 		}
16235935Ssb155480 
16245935Ssb155480 		/* is this the required instance of vnet? */
16256495Sspeer 		if (vgenp->regprop != cfgh)
16265935Ssb155480 			continue;
16275935Ssb155480 
16287529SSriharsha.Basavapatna@Sun.COM 		/*
16297529SSriharsha.Basavapatna@Sun.COM 		 * Read the mtu. Note that we set the mtu of vnet device within
16307529SSriharsha.Basavapatna@Sun.COM 		 * this routine itself, after validating the range.
16317529SSriharsha.Basavapatna@Sun.COM 		 */
16327529SSriharsha.Basavapatna@Sun.COM 		vgen_mtu_read(vgenp, mdp, listp[i], &vnetp->mtu);
16337529SSriharsha.Basavapatna@Sun.COM 		if (vnetp->mtu < ETHERMTU || vnetp->mtu > VNET_MAX_MTU) {
16347529SSriharsha.Basavapatna@Sun.COM 			vnetp->mtu = ETHERMTU;
16357529SSriharsha.Basavapatna@Sun.COM 		}
16367529SSriharsha.Basavapatna@Sun.COM 		vgenp->max_frame_size = vnetp->mtu +
16377529SSriharsha.Basavapatna@Sun.COM 		    sizeof (struct ether_header) + VLAN_TAGSZ;
16387529SSriharsha.Basavapatna@Sun.COM 
16397529SSriharsha.Basavapatna@Sun.COM 		/* read priority ether types */
16405935Ssb155480 		vgen_read_pri_eth_types(vgenp, mdp, listp[i]);
16416419Ssb155480 
16426419Ssb155480 		/* read vlan id properties of this vnet instance */
16436419Ssb155480 		vgen_vlan_read_ids(vgenp, VGEN_LOCAL, mdp, listp[i],
16446419Ssb155480 		    &vnetp->pvid, &vnetp->vids, &vnetp->nvids,
16456419Ssb155480 		    &vnetp->default_vlan_id);
16466419Ssb155480 
16475935Ssb155480 		rv = 0;
16485935Ssb155480 		break;
16495935Ssb155480 	}
16505935Ssb155480 
16515935Ssb155480 vgen_readmd_exit:
16525935Ssb155480 
16535935Ssb155480 	kmem_free(listp, listsz);
16545935Ssb155480 	(void) md_fini_handle(mdp);
16555935Ssb155480 	return (rv);
16565935Ssb155480 }
16575935Ssb155480 
16585935Ssb155480 /*
16596419Ssb155480  * Read vlan id properties of the given MD node.
16606419Ssb155480  * Arguments:
16616419Ssb155480  *   arg:          device argument(vnet device or a port)
16626419Ssb155480  *   type:         type of arg; VGEN_LOCAL(vnet device) or VGEN_PEER(port)
16636419Ssb155480  *   mdp:          machine description
16646419Ssb155480  *   node:         md node cookie
16656419Ssb155480  *
16666419Ssb155480  * Returns:
16676419Ssb155480  *   pvidp:        port-vlan-id of the node
16686419Ssb155480  *   vidspp:       list of vlan-ids of the node
16696419Ssb155480  *   nvidsp:       # of vlan-ids in the list
16706419Ssb155480  *   default_idp:  default-vlan-id of the node(if node is vnet device)
16716419Ssb155480  */
16726419Ssb155480 static void
16736419Ssb155480 vgen_vlan_read_ids(void *arg, int type, md_t *mdp, mde_cookie_t node,
16746419Ssb155480 	uint16_t *pvidp, uint16_t **vidspp, uint16_t *nvidsp,
16756419Ssb155480 	uint16_t *default_idp)
16766419Ssb155480 {
16776419Ssb155480 	vgen_t		*vgenp;
16786419Ssb155480 	vnet_t		*vnetp;
16796419Ssb155480 	vgen_port_t	*portp;
16806419Ssb155480 	char		*pvid_propname;
16816419Ssb155480 	char		*vid_propname;
16826419Ssb155480 	uint_t		nvids;
16836419Ssb155480 	uint32_t	vids_size;
16846419Ssb155480 	int		rv;
16856419Ssb155480 	int		i;
16866419Ssb155480 	uint64_t	*data;
16876419Ssb155480 	uint64_t	val;
16886419Ssb155480 	int		size;
16896419Ssb155480 	int		inst;
16906419Ssb155480 
16916419Ssb155480 	if (type == VGEN_LOCAL) {
16926419Ssb155480 
16936419Ssb155480 		vgenp = (vgen_t *)arg;
16946419Ssb155480 		vnetp = vgenp->vnetp;
16956419Ssb155480 		pvid_propname = vgen_pvid_propname;
16966419Ssb155480 		vid_propname = vgen_vid_propname;
16976419Ssb155480 		inst = vnetp->instance;
16986419Ssb155480 
16996419Ssb155480 	} else if (type == VGEN_PEER) {
17006419Ssb155480 
17016419Ssb155480 		portp = (vgen_port_t *)arg;
17026419Ssb155480 		vgenp = portp->vgenp;
17036419Ssb155480 		vnetp = vgenp->vnetp;
17046419Ssb155480 		pvid_propname = port_pvid_propname;
17056419Ssb155480 		vid_propname = port_vid_propname;
17066419Ssb155480 		inst = portp->port_num;
17076419Ssb155480 
17086419Ssb155480 	} else {
17096419Ssb155480 		return;
17106419Ssb155480 	}
17116419Ssb155480 
17126419Ssb155480 	if (type == VGEN_LOCAL && default_idp != NULL) {
17136419Ssb155480 		rv = md_get_prop_val(mdp, node, vgen_dvid_propname, &val);
17146419Ssb155480 		if (rv != 0) {
17156419Ssb155480 			DWARN(vgenp, NULL, "prop(%s) not found",
17166419Ssb155480 			    vgen_dvid_propname);
17176419Ssb155480 
17186419Ssb155480 			*default_idp = vnet_default_vlan_id;
17196419Ssb155480 		} else {
17206419Ssb155480 			*default_idp = val & 0xFFF;
17216419Ssb155480 			DBG2(vgenp, NULL, "%s(%d): (%d)\n", vgen_dvid_propname,
17226419Ssb155480 			    inst, *default_idp);
17236419Ssb155480 		}
17246419Ssb155480 	}
17256419Ssb155480 
17266419Ssb155480 	rv = md_get_prop_val(mdp, node, pvid_propname, &val);
17276419Ssb155480 	if (rv != 0) {
17286419Ssb155480 		DWARN(vgenp, NULL, "prop(%s) not found", pvid_propname);
17296419Ssb155480 		*pvidp = vnet_default_vlan_id;
17306419Ssb155480 	} else {
17316419Ssb155480 
17326419Ssb155480 		*pvidp = val & 0xFFF;
17336419Ssb155480 		DBG2(vgenp, NULL, "%s(%d): (%d)\n",
17346419Ssb155480 		    pvid_propname, inst, *pvidp);
17356419Ssb155480 	}
17366419Ssb155480 
17376419Ssb155480 	rv = md_get_prop_data(mdp, node, vid_propname, (uint8_t **)&data,
17386419Ssb155480 	    &size);
17396419Ssb155480 	if (rv != 0) {
17406419Ssb155480 		DBG2(vgenp, NULL, "prop(%s) not found", vid_propname);
17416419Ssb155480 		size = 0;
17426419Ssb155480 	} else {
17436419Ssb155480 		size /= sizeof (uint64_t);
17446419Ssb155480 	}
17456419Ssb155480 	nvids = size;
17466419Ssb155480 
17476419Ssb155480 	if (nvids != 0) {
17486419Ssb155480 		DBG2(vgenp, NULL, "%s(%d): ", vid_propname, inst);
17496419Ssb155480 		vids_size = sizeof (uint16_t) * nvids;
17506419Ssb155480 		*vidspp = kmem_zalloc(vids_size, KM_SLEEP);
17516419Ssb155480 		for (i = 0; i < nvids; i++) {
17526419Ssb155480 			(*vidspp)[i] = data[i] & 0xFFFF;
17536419Ssb155480 			DBG2(vgenp, NULL, " %d ", (*vidspp)[i]);
17546419Ssb155480 		}
17556419Ssb155480 		DBG2(vgenp, NULL, "\n");
17566419Ssb155480 	}
17576419Ssb155480 
17586419Ssb155480 	*nvidsp = nvids;
17596419Ssb155480 }
17606419Ssb155480 
17616419Ssb155480 /*
17626419Ssb155480  * Create a vlan id hash table for the given port.
17636419Ssb155480  */
17646419Ssb155480 static void
17656419Ssb155480 vgen_vlan_create_hash(vgen_port_t *portp)
17666419Ssb155480 {
17676419Ssb155480 	char		hashname[MAXNAMELEN];
17686419Ssb155480 
17696419Ssb155480 	(void) snprintf(hashname, MAXNAMELEN, "port%d-vlan-hash",
17706419Ssb155480 	    portp->port_num);
17716419Ssb155480 
17726419Ssb155480 	portp->vlan_nchains = vgen_vlan_nchains;
17736419Ssb155480 	portp->vlan_hashp = mod_hash_create_idhash(hashname,
17746419Ssb155480 	    portp->vlan_nchains, mod_hash_null_valdtor);
17756419Ssb155480 }
17766419Ssb155480 
17776419Ssb155480 /*
17786419Ssb155480  * Destroy the vlan id hash table in the given port.
17796419Ssb155480  */
17806419Ssb155480 static void
17816419Ssb155480 vgen_vlan_destroy_hash(vgen_port_t *portp)
17826419Ssb155480 {
17836419Ssb155480 	if (portp->vlan_hashp != NULL) {
17846419Ssb155480 		mod_hash_destroy_hash(portp->vlan_hashp);
17856419Ssb155480 		portp->vlan_hashp = NULL;
17866419Ssb155480 		portp->vlan_nchains = 0;
17876419Ssb155480 	}
17886419Ssb155480 }
17896419Ssb155480 
17906419Ssb155480 /*
17916419Ssb155480  * Add a port to the vlans specified in its port properites.
17926419Ssb155480  */
17936419Ssb155480 static void
17946419Ssb155480 vgen_vlan_add_ids(vgen_port_t *portp)
17956419Ssb155480 {
17966419Ssb155480 	int		rv;
17976419Ssb155480 	int		i;
17986419Ssb155480 
17996419Ssb155480 	rv = mod_hash_insert(portp->vlan_hashp,
18006419Ssb155480 	    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
18016419Ssb155480 	    (mod_hash_val_t)B_TRUE);
18026419Ssb155480 	ASSERT(rv == 0);
18036419Ssb155480 
18046419Ssb155480 	for (i = 0; i < portp->nvids; i++) {
18056419Ssb155480 		rv = mod_hash_insert(portp->vlan_hashp,
18066419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]),
18076419Ssb155480 		    (mod_hash_val_t)B_TRUE);
18086419Ssb155480 		ASSERT(rv == 0);
18096419Ssb155480 	}
18106419Ssb155480 }
18116419Ssb155480 
18126419Ssb155480 /*
18136419Ssb155480  * Remove a port from the vlans it has been assigned to.
18146419Ssb155480  */
18156419Ssb155480 static void
18166419Ssb155480 vgen_vlan_remove_ids(vgen_port_t *portp)
18176419Ssb155480 {
18186419Ssb155480 	int		rv;
18196419Ssb155480 	int		i;
18206419Ssb155480 	mod_hash_val_t	vp;
18216419Ssb155480 
18226419Ssb155480 	rv = mod_hash_remove(portp->vlan_hashp,
18236419Ssb155480 	    (mod_hash_key_t)VLAN_ID_KEY(portp->pvid),
18246419Ssb155480 	    (mod_hash_val_t *)&vp);
18256419Ssb155480 	ASSERT(rv == 0);
18266419Ssb155480 
18276419Ssb155480 	for (i = 0; i < portp->nvids; i++) {
18286419Ssb155480 		rv = mod_hash_remove(portp->vlan_hashp,
18296419Ssb155480 		    (mod_hash_key_t)VLAN_ID_KEY(portp->vids[i]),
18306419Ssb155480 		    (mod_hash_val_t *)&vp);
18316419Ssb155480 		ASSERT(rv == 0);
18326419Ssb155480 	}
18336419Ssb155480 }
18346419Ssb155480 
18356419Ssb155480 /*
18366419Ssb155480  * Lookup the vlan id of the given tx frame. If it is a vlan-tagged frame,
18376419Ssb155480  * then the vlan-id is available in the tag; otherwise, its vlan id is
18386419Ssb155480  * implicitly obtained from the port-vlan-id of the vnet device.
18396419Ssb155480  * The vlan id determined is returned in vidp.
18406419Ssb155480  * Returns: B_TRUE if it is a tagged frame; B_FALSE if it is untagged.
18416419Ssb155480  */
18426419Ssb155480 static boolean_t
18436419Ssb155480 vgen_frame_lookup_vid(vnet_t *vnetp, struct ether_header *ehp, uint16_t *vidp)
18446419Ssb155480 {
18456419Ssb155480 	struct ether_vlan_header	*evhp;
18466419Ssb155480 
18476419Ssb155480 	/* If it's a tagged frame, get the vlan id from vlan header */
18486419Ssb155480 	if (ehp->ether_type == ETHERTYPE_VLAN) {
18496419Ssb155480 
18506419Ssb155480 		evhp = (struct ether_vlan_header *)ehp;
18516419Ssb155480 		*vidp = VLAN_ID(ntohs(evhp->ether_tci));
18526419Ssb155480 		return (B_TRUE);
18536419Ssb155480 	}
18546419Ssb155480 
18556419Ssb155480 	/* Untagged frame, vlan-id is the pvid of vnet device */
18566419Ssb155480 	*vidp = vnetp->pvid;
18576419Ssb155480 	return (B_FALSE);
18586419Ssb155480 }
18596419Ssb155480 
18606419Ssb155480 /*
18616419Ssb155480  * Find the given vlan id in the hash table.
18626419Ssb155480  * Return: B_TRUE if the id is found; B_FALSE if not found.
18636419Ssb155480  */
18646419Ssb155480 static boolean_t
18656419Ssb155480 vgen_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid)
18666419Ssb155480 {
18676419Ssb155480 	int		rv;
18686419Ssb155480 	mod_hash_val_t	vp;
18696419Ssb155480 
18706419Ssb155480 	rv = mod_hash_find(vlan_hashp, VLAN_ID_KEY(vid), (mod_hash_val_t *)&vp);
18716419Ssb155480 
18726419Ssb155480 	if (rv != 0)
18736419Ssb155480 		return (B_FALSE);
18746419Ssb155480 
18756419Ssb155480 	return (B_TRUE);
18766419Ssb155480 }
18776419Ssb155480 
18786419Ssb155480 /*
18795935Ssb155480  * This function reads "priority-ether-types" property from md. This property
18805935Ssb155480  * is used to enable support for priority frames. Applications which need
18815935Ssb155480  * guaranteed and timely delivery of certain high priority frames to/from
18825935Ssb155480  * a vnet or vsw within ldoms, should configure this property by providing
18835935Ssb155480  * the ether type(s) for which the priority facility is needed.
18845935Ssb155480  * Normal data frames are delivered over a ldc channel using the descriptor
18855935Ssb155480  * ring mechanism which is constrained by factors such as descriptor ring size,
18865935Ssb155480  * the rate at which the ring is processed at the peer ldc end point, etc.
18875935Ssb155480  * The priority mechanism provides an Out-Of-Band path to send/receive frames
18885935Ssb155480  * as raw pkt data (VIO_PKT_DATA) messages over the channel, avoiding the
18895935Ssb155480  * descriptor ring path and enables a more reliable and timely delivery of
18905935Ssb155480  * frames to the peer.
18915935Ssb155480  */
18925935Ssb155480 static void
18935935Ssb155480 vgen_read_pri_eth_types(vgen_t *vgenp, md_t *mdp, mde_cookie_t node)
18945935Ssb155480 {
18955935Ssb155480 	int		rv;
18965935Ssb155480 	uint16_t	*types;
18975935Ssb155480 	uint64_t	*data;
18985935Ssb155480 	int		size;
18995935Ssb155480 	int		i;
19005935Ssb155480 	size_t		mblk_sz;
19015935Ssb155480 
19025935Ssb155480 	rv = md_get_prop_data(mdp, node, pri_types_propname,
19035935Ssb155480 	    (uint8_t **)&data, &size);
19045935Ssb155480 	if (rv != 0) {
19055935Ssb155480 		/*
19065935Ssb155480 		 * Property may not exist if we are running pre-ldoms1.1 f/w.
19075935Ssb155480 		 * Check if 'vgen_pri_eth_type' has been set in that case.
19085935Ssb155480 		 */
19095935Ssb155480 		if (vgen_pri_eth_type != 0) {
19105935Ssb155480 			size = sizeof (vgen_pri_eth_type);
19115935Ssb155480 			data = &vgen_pri_eth_type;
19125935Ssb155480 		} else {
19136495Sspeer 			DBG2(vgenp, NULL,
19145935Ssb155480 			    "prop(%s) not found", pri_types_propname);
19155935Ssb155480 			size = 0;
19165935Ssb155480 		}
19175935Ssb155480 	}
19185935Ssb155480 
19195935Ssb155480 	if (size == 0) {
19205935Ssb155480 		vgenp->pri_num_types = 0;
19215935Ssb155480 		return;
19225935Ssb155480 	}
19235935Ssb155480 
19245935Ssb155480 	/*
19255935Ssb155480 	 * we have some priority-ether-types defined;
19265935Ssb155480 	 * allocate a table of these types and also
19275935Ssb155480 	 * allocate a pool of mblks to transmit these
19285935Ssb155480 	 * priority packets.
19295935Ssb155480 	 */
19305935Ssb155480 	size /= sizeof (uint64_t);
19315935Ssb155480 	vgenp->pri_num_types = size;
19325935Ssb155480 	vgenp->pri_types = kmem_zalloc(size * sizeof (uint16_t), KM_SLEEP);
19335935Ssb155480 	for (i = 0, types = vgenp->pri_types; i < size; i++) {
19345935Ssb155480 		types[i] = data[i] & 0xFFFF;
19355935Ssb155480 	}
19366419Ssb155480 	mblk_sz = (VIO_PKT_DATA_HDRSIZE + vgenp->max_frame_size + 7) & ~7;
19375935Ssb155480 	(void) vio_create_mblks(vgen_pri_tx_nmblks, mblk_sz,
19385935Ssb155480 	    &vgenp->pri_tx_vmp);
19395935Ssb155480 }
19405935Ssb155480 
19417529SSriharsha.Basavapatna@Sun.COM static void
19427529SSriharsha.Basavapatna@Sun.COM vgen_mtu_read(vgen_t *vgenp, md_t *mdp, mde_cookie_t node, uint32_t *mtu)
19437529SSriharsha.Basavapatna@Sun.COM {
19447529SSriharsha.Basavapatna@Sun.COM 	int		rv;
19457529SSriharsha.Basavapatna@Sun.COM 	uint64_t	val;
19467529SSriharsha.Basavapatna@Sun.COM 	char		*mtu_propname;
19477529SSriharsha.Basavapatna@Sun.COM 
19487529SSriharsha.Basavapatna@Sun.COM 	mtu_propname = vgen_mtu_propname;
19497529SSriharsha.Basavapatna@Sun.COM 
19507529SSriharsha.Basavapatna@Sun.COM 	rv = md_get_prop_val(mdp, node, mtu_propname, &val);
19517529SSriharsha.Basavapatna@Sun.COM 	if (rv != 0) {
19527529SSriharsha.Basavapatna@Sun.COM 		DWARN(vgenp, NULL, "prop(%s) not found", mtu_propname);
19537529SSriharsha.Basavapatna@Sun.COM 		*mtu = vnet_ethermtu;
19547529SSriharsha.Basavapatna@Sun.COM 	} else {
19557529SSriharsha.Basavapatna@Sun.COM 
19567529SSriharsha.Basavapatna@Sun.COM 		*mtu = val & 0xFFFF;
19577529SSriharsha.Basavapatna@Sun.COM 		DBG2(vgenp, NULL, "%s(%d): (%d)\n", mtu_propname,
19587529SSriharsha.Basavapatna@Sun.COM 		    vgenp->instance, *mtu);
19597529SSriharsha.Basavapatna@Sun.COM 	}
19607529SSriharsha.Basavapatna@Sun.COM }
19617529SSriharsha.Basavapatna@Sun.COM 
19621991Sheppo /* register with MD event generator */
19631991Sheppo static int
19641991Sheppo vgen_mdeg_reg(vgen_t *vgenp)
19651991Sheppo {
19661991Sheppo 	mdeg_prop_spec_t	*pspecp;
19671991Sheppo 	mdeg_node_spec_t	*parentp;
19681991Sheppo 	uint_t			templatesz;
19691991Sheppo 	int			rv;
19706419Ssb155480 	mdeg_handle_t		dev_hdl = NULL;
19716419Ssb155480 	mdeg_handle_t		port_hdl = NULL;
19726419Ssb155480 
19731991Sheppo 	templatesz = sizeof (vgen_prop_template);
19741991Sheppo 	pspecp = kmem_zalloc(templatesz, KM_NOSLEEP);
19751991Sheppo 	if (pspecp == NULL) {
19761991Sheppo 		return (DDI_FAILURE);
19771991Sheppo 	}
19781991Sheppo 	parentp = kmem_zalloc(sizeof (mdeg_node_spec_t), KM_NOSLEEP);
19791991Sheppo 	if (parentp == NULL) {
19801991Sheppo 		kmem_free(pspecp, templatesz);
19811991Sheppo 		return (DDI_FAILURE);
19821991Sheppo 	}
19831991Sheppo 
19841991Sheppo 	bcopy(vgen_prop_template, pspecp, templatesz);
19851991Sheppo 
19861991Sheppo 	/*
19871991Sheppo 	 * NOTE: The instance here refers to the value of "reg" property and
19881991Sheppo 	 * not the dev_info instance (ddi_get_instance()) of vnet.
19891991Sheppo 	 */
19906419Ssb155480 	VGEN_SET_MDEG_PROP_INST(pspecp, vgenp->regprop);
19911991Sheppo 
19921991Sheppo 	parentp->namep = "virtual-device";
19931991Sheppo 	parentp->specp = pspecp;
19941991Sheppo 
19951991Sheppo 	/* save parentp in vgen_t */
19961991Sheppo 	vgenp->mdeg_parentp = parentp;
19971991Sheppo 
19986419Ssb155480 	/*
19996419Ssb155480 	 * Register an interest in 'virtual-device' nodes with a
20006419Ssb155480 	 * 'name' property of 'network'
20016419Ssb155480 	 */
20026419Ssb155480 	rv = mdeg_register(parentp, &vdev_match, vgen_mdeg_cb, vgenp, &dev_hdl);
20031991Sheppo 	if (rv != MDEG_SUCCESS) {
20044647Sraghuram 		DERR(vgenp, NULL, "mdeg_register failed\n");
20056419Ssb155480 		goto mdeg_reg_fail;
20066419Ssb155480 	}
20076419Ssb155480 
20086419Ssb155480 	/* Register an interest in 'port' nodes */
20096419Ssb155480 	rv = mdeg_register(parentp, &vport_match, vgen_mdeg_port_cb, vgenp,
20106419Ssb155480 	    &port_hdl);
20116419Ssb155480 	if (rv != MDEG_SUCCESS) {
20126419Ssb155480 		DERR(vgenp, NULL, "mdeg_register failed\n");
20136419Ssb155480 		goto mdeg_reg_fail;
20141991Sheppo 	}
20151991Sheppo 
20161991Sheppo 	/* save mdeg handle in vgen_t */
20176419Ssb155480 	vgenp->mdeg_dev_hdl = dev_hdl;
20186419Ssb155480 	vgenp->mdeg_port_hdl = port_hdl;
20191991Sheppo 
20201991Sheppo 	return (DDI_SUCCESS);
20216419Ssb155480 
20226419Ssb155480 mdeg_reg_fail:
20236419Ssb155480 	if (dev_hdl != NULL) {
20246419Ssb155480 		(void) mdeg_unregister(dev_hdl);
20256419Ssb155480 	}
20266419Ssb155480 	KMEM_FREE(parentp);
20276419Ssb155480 	kmem_free(pspecp, templatesz);
20286419Ssb155480 	vgenp->mdeg_parentp = NULL;
20296419Ssb155480 	return (DDI_FAILURE);
20301991Sheppo }
20311991Sheppo 
20321991Sheppo /* unregister with MD event generator */
20331991Sheppo static void
20341991Sheppo vgen_mdeg_unreg(vgen_t *vgenp)
20351991Sheppo {
20366419Ssb155480 	(void) mdeg_unregister(vgenp->mdeg_dev_hdl);
20376419Ssb155480 	(void) mdeg_unregister(vgenp->mdeg_port_hdl);
20383297Ssb155480 	kmem_free(vgenp->mdeg_parentp->specp, sizeof (vgen_prop_template));
20391991Sheppo 	KMEM_FREE(vgenp->mdeg_parentp);
20401991Sheppo 	vgenp->mdeg_parentp = NULL;
20416419Ssb155480 	vgenp->mdeg_dev_hdl = NULL;
20426419Ssb155480 	vgenp->mdeg_port_hdl = NULL;
20431991Sheppo }
20441991Sheppo 
20456419Ssb155480 /* mdeg callback function for the port node */
20461991Sheppo static int
20476419Ssb155480 vgen_mdeg_port_cb(void *cb_argp, mdeg_result_t *resp)
20481991Sheppo {
20491991Sheppo 	int idx;
20501991Sheppo 	int vsw_idx = -1;
20511991Sheppo 	uint64_t val;
20521991Sheppo 	vgen_t *vgenp;
20531991Sheppo 
20541991Sheppo 	if ((resp == NULL) || (cb_argp == NULL)) {
20551991Sheppo 		return (MDEG_FAILURE);
20561991Sheppo 	}
20571991Sheppo 
20581991Sheppo 	vgenp = (vgen_t *)cb_argp;
20594647Sraghuram 	DBG1(vgenp, NULL, "enter\n");
20601991Sheppo 
20611991Sheppo 	mutex_enter(&vgenp->lock);
20621991Sheppo 
20634647Sraghuram 	DBG1(vgenp, NULL, "ports: removed(%x), "
20644647Sraghuram 	"added(%x), updated(%x)\n", resp->removed.nelem,
20654647Sraghuram 	    resp->added.nelem, resp->match_curr.nelem);
20661991Sheppo 
20671991Sheppo 	for (idx = 0; idx < resp->removed.nelem; idx++) {
20681991Sheppo 		(void) vgen_remove_port(vgenp, resp->removed.mdp,
20691991Sheppo 		    resp->removed.mdep[idx]);
20701991Sheppo 	}
20711991Sheppo 
20721991Sheppo 	if (vgenp->vsw_portp == NULL) {
20731991Sheppo 		/*
20741991Sheppo 		 * find vsw_port and add it first, because other ports need
20751991Sheppo 		 * this when adding fdb entry (see vgen_port_init()).
20761991Sheppo 		 */
20771991Sheppo 		for (idx = 0; idx < resp->added.nelem; idx++) {
20781991Sheppo 			if (!(md_get_prop_val(resp->added.mdp,
20791991Sheppo 			    resp->added.mdep[idx], swport_propname, &val))) {
20801991Sheppo 				if (val == 0) {
20811991Sheppo 					/*
20821991Sheppo 					 * This port is connected to the
20836419Ssb155480 					 * vsw on service domain.
20841991Sheppo 					 */
20851991Sheppo 					vsw_idx = idx;
20864663Szk194757 					if (vgen_add_port(vgenp,
20871991Sheppo 					    resp->added.mdp,
20884663Szk194757 					    resp->added.mdep[idx]) !=
20894663Szk194757 					    DDI_SUCCESS) {
20904663Szk194757 						cmn_err(CE_NOTE, "vnet%d Could "
20914663Szk194757 						    "not initialize virtual "
20924663Szk194757 						    "switch port.",
20936495Sspeer 						    vgenp->instance);
20944663Szk194757 						mutex_exit(&vgenp->lock);
20954663Szk194757 						return (MDEG_FAILURE);
20964663Szk194757 					}
20971991Sheppo 					break;
20981991Sheppo 				}
20991991Sheppo 			}
21001991Sheppo 		}
21014666Szk194757 		if (vsw_idx == -1) {
21024647Sraghuram 			DWARN(vgenp, NULL, "can't find vsw_port\n");
21034663Szk194757 			mutex_exit(&vgenp->lock);
21041991Sheppo 			return (MDEG_FAILURE);
21051991Sheppo 		}
21061991Sheppo 	}
21071991Sheppo 
21081991Sheppo 	for (idx = 0; idx < resp->added.nelem; idx++) {
21091991Sheppo 		if ((vsw_idx != -1) && (vsw_idx == idx)) /* skip vsw_port */
21101991Sheppo 			continue;
21114663Szk194757 
21124663Szk194757 		/* If this port can't be added just skip it. */
21131991Sheppo 		(void) vgen_add_port(vgenp, resp->added.mdp,
21141991Sheppo 		    resp->added.mdep[idx]);
21151991Sheppo 	}
21161991Sheppo 
21171991Sheppo 	for (idx = 0; idx < resp->match_curr.nelem; idx++) {
21181991Sheppo 		(void) vgen_update_port(vgenp, resp->match_curr.mdp,
21191991Sheppo 		    resp->match_curr.mdep[idx],
21201991Sheppo 		    resp->match_prev.mdp,
21211991Sheppo 		    resp->match_prev.mdep[idx]);
21221991Sheppo 	}
21231991Sheppo 
21241991Sheppo 	mutex_exit(&vgenp->lock);
21254647Sraghuram 	DBG1(vgenp, NULL, "exit\n");
21261991Sheppo 	return (MDEG_SUCCESS);
21271991Sheppo }
21281991Sheppo 
21296419Ssb155480 /* mdeg callback function for the vnet node */
21306419Ssb155480 static int
21316419Ssb155480 vgen_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
21326419Ssb155480 {
21336419Ssb155480 	vgen_t		*vgenp;
21346419Ssb155480 	vnet_t		*vnetp;
21356419Ssb155480 	md_t		*mdp;
21366419Ssb155480 	mde_cookie_t	node;
21376419Ssb155480 	uint64_t	inst;
21386419Ssb155480 	char		*node_name = NULL;
21396419Ssb155480 
21406419Ssb155480 	if ((resp == NULL) || (cb_argp == NULL)) {
21416419Ssb155480 		return (MDEG_FAILURE);
21426419Ssb155480 	}
21436419Ssb155480 
21446419Ssb155480 	vgenp = (vgen_t *)cb_argp;
21456419Ssb155480 	vnetp = vgenp->vnetp;
21466419Ssb155480 
21477572SWentao.Yang@Sun.COM 	DBG1(vgenp, NULL, "added %d : removed %d : curr matched %d"
21486419Ssb155480 	    " : prev matched %d", resp->added.nelem, resp->removed.nelem,
21496419Ssb155480 	    resp->match_curr.nelem, resp->match_prev.nelem);
21506419Ssb155480 
21516419Ssb155480 	mutex_enter(&vgenp->lock);
21526419Ssb155480 
21536419Ssb155480 	/*
21546419Ssb155480 	 * We get an initial callback for this node as 'added' after
21556419Ssb155480 	 * registering with mdeg. Note that we would have already gathered
21566419Ssb155480 	 * information about this vnet node by walking MD earlier during attach
21576419Ssb155480 	 * (in vgen_read_mdprops()). So, there is a window where the properties
21586419Ssb155480 	 * of this node might have changed when we get this initial 'added'
21596419Ssb155480 	 * callback. We handle this as if an update occured and invoke the same
21606419Ssb155480 	 * function which handles updates to the properties of this vnet-node
21616419Ssb155480 	 * if any. A non-zero 'match' value indicates that the MD has been
21626419Ssb155480 	 * updated and that a 'network' node is present which may or may not
21636419Ssb155480 	 * have been updated. It is up to the clients to examine their own
21646419Ssb155480 	 * nodes and determine if they have changed.
21656419Ssb155480 	 */
21666419Ssb155480 	if (resp->added.nelem != 0) {
21676419Ssb155480 
21686419Ssb155480 		if (resp->added.nelem != 1) {
21696419Ssb155480 			cmn_err(CE_NOTE, "!vnet%d: number of nodes added "
21706419Ssb155480 			    "invalid: %d\n", vnetp->instance,
21716419Ssb155480 			    resp->added.nelem);
21726419Ssb155480 			goto vgen_mdeg_cb_err;
21736419Ssb155480 		}
21746419Ssb155480 
21756419Ssb155480 		mdp = resp->added.mdp;
21766419Ssb155480 		node = resp->added.mdep[0];
21776419Ssb155480 
21786419Ssb155480 	} else if (resp->match_curr.nelem != 0) {
21796419Ssb155480 
21806419Ssb155480 		if (resp->match_curr.nelem != 1) {
21816419Ssb155480 			cmn_err(CE_NOTE, "!vnet%d: number of nodes updated "
21826419Ssb155480 			    "invalid: %d\n", vnetp->instance,
21836419Ssb155480 			    resp->match_curr.nelem);
21846419Ssb155480 			goto vgen_mdeg_cb_err;
21856419Ssb155480 		}
21866419Ssb155480 
21876419Ssb155480 		mdp = resp->match_curr.mdp;
21886419Ssb155480 		node = resp->match_curr.mdep[0];
21896419Ssb155480 
21906419Ssb155480 	} else {
21916419Ssb155480 		goto vgen_mdeg_cb_err;
21926419Ssb155480 	}
21936419Ssb155480 
21946419Ssb155480 	/* Validate name and instance */
21956419Ssb155480 	if (md_get_prop_str(mdp, node, "name", &node_name) != 0) {
21966419Ssb155480 		DERR(vgenp, NULL, "unable to get node name\n");
21976419Ssb155480 		goto vgen_mdeg_cb_err;
21986419Ssb155480 	}
21996419Ssb155480 
22006419Ssb155480 	/* is this a virtual-network device? */
22016419Ssb155480 	if (strcmp(node_name, vnet_propname) != 0) {
22026419Ssb155480 		DERR(vgenp, NULL, "%s: Invalid node name: %s\n", node_name);
22036419Ssb155480 		goto vgen_mdeg_cb_err;
22046419Ssb155480 	}
22056419Ssb155480 
22066419Ssb155480 	if (md_get_prop_val(mdp, node, "cfg-handle", &inst)) {
22076419Ssb155480 		DERR(vgenp, NULL, "prop(cfg-handle) not found\n");
22086419Ssb155480 		goto vgen_mdeg_cb_err;
22096419Ssb155480 	}
22106419Ssb155480 
22116495Sspeer 	/* is this the right instance of vnet? */
22126419Ssb155480 	if (inst != vgenp->regprop) {
22136419Ssb155480 		DERR(vgenp, NULL,  "Invalid cfg-handle: %lx\n", inst);
22146419Ssb155480 		goto vgen_mdeg_cb_err;
22156419Ssb155480 	}
22166419Ssb155480 
22176419Ssb155480 	vgen_update_md_prop(vgenp, mdp, node);
22186419Ssb155480 
22196419Ssb155480 	mutex_exit(&vgenp->lock);
22206419Ssb155480 	return (MDEG_SUCCESS);
22216419Ssb155480 
22226419Ssb155480 vgen_mdeg_cb_err:
22236419Ssb155480 	mutex_exit(&vgenp->lock);
22246419Ssb155480 	return (MDEG_FAILURE);
22256419Ssb155480 }
22266419Ssb155480 
22276419Ssb155480 /*
22286419Ssb155480  * Check to see if the relevant properties in the specified node have
22296419Ssb155480  * changed, and if so take the appropriate action.
22306419Ssb155480  */
22316419Ssb155480 static void
22326419Ssb155480 vgen_update_md_prop(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex)
22336419Ssb155480 {
22346419Ssb155480 	uint16_t	pvid;
22356419Ssb155480 	uint16_t	*vids;
22366419Ssb155480 	uint16_t	nvids;
22376419Ssb155480 	vnet_t		*vnetp = vgenp->vnetp;
22387529SSriharsha.Basavapatna@Sun.COM 	uint32_t	mtu;
22397529SSriharsha.Basavapatna@Sun.COM 	enum		{ MD_init = 0x1,
22407529SSriharsha.Basavapatna@Sun.COM 			    MD_vlans = 0x2,
22417529SSriharsha.Basavapatna@Sun.COM 			    MD_mtu = 0x4 } updated;
22427529SSriharsha.Basavapatna@Sun.COM 	int		rv;
22437529SSriharsha.Basavapatna@Sun.COM 
22447529SSriharsha.Basavapatna@Sun.COM 	updated = MD_init;
22456419Ssb155480 
22466419Ssb155480 	/* Read the vlan ids */
22476419Ssb155480 	vgen_vlan_read_ids(vgenp, VGEN_LOCAL, mdp, mdex, &pvid, &vids,
22486419Ssb155480 	    &nvids, NULL);
22496419Ssb155480 
22506419Ssb155480 	/* Determine if there are any vlan id updates */
22516419Ssb155480 	if ((pvid != vnetp->pvid) ||		/* pvid changed? */
22526419Ssb155480 	    (nvids != vnetp->nvids) ||		/* # of vids changed? */
22536419Ssb155480 	    ((nvids != 0) && (vnetp->nvids != 0) &&	/* vids changed? */
22546419Ssb155480 	    bcmp(vids, vnetp->vids, sizeof (uint16_t) * nvids))) {
22557529SSriharsha.Basavapatna@Sun.COM 		updated |= MD_vlans;
22567529SSriharsha.Basavapatna@Sun.COM 	}
22577529SSriharsha.Basavapatna@Sun.COM 
22587529SSriharsha.Basavapatna@Sun.COM 	/* Read mtu */
22597529SSriharsha.Basavapatna@Sun.COM 	vgen_mtu_read(vgenp, mdp, mdex, &mtu);
22607529SSriharsha.Basavapatna@Sun.COM 	if (mtu != vnetp->mtu) {
22617529SSriharsha.Basavapatna@Sun.COM 		if (mtu >= ETHERMTU && mtu <= VNET_MAX_MTU) {
22627529SSriharsha.Basavapatna@Sun.COM 			updated |= MD_mtu;
22637529SSriharsha.Basavapatna@Sun.COM 		} else {
22647529SSriharsha.Basavapatna@Sun.COM 			cmn_err(CE_NOTE, "!vnet%d: Unable to process mtu update"
22657529SSriharsha.Basavapatna@Sun.COM 			    " as the specified value:%d is invalid\n",
22667529SSriharsha.Basavapatna@Sun.COM 			    vnetp->instance, mtu);
22677529SSriharsha.Basavapatna@Sun.COM 		}
22687529SSriharsha.Basavapatna@Sun.COM 	}
22697529SSriharsha.Basavapatna@Sun.COM 
22707529SSriharsha.Basavapatna@Sun.COM 	/* Now process the updated props */
22717529SSriharsha.Basavapatna@Sun.COM 
22727529SSriharsha.Basavapatna@Sun.COM 	if (updated & MD_vlans) {
22737529SSriharsha.Basavapatna@Sun.COM 
22747529SSriharsha.Basavapatna@Sun.COM 		/* save the new vlan ids */
22757529SSriharsha.Basavapatna@Sun.COM 		vnetp->pvid = pvid;
22767529SSriharsha.Basavapatna@Sun.COM 		if (vnetp->nvids != 0) {
22777529SSriharsha.Basavapatna@Sun.COM 			kmem_free(vnetp->vids,
22787529SSriharsha.Basavapatna@Sun.COM 			    sizeof (uint16_t) * vnetp->nvids);
22797529SSriharsha.Basavapatna@Sun.COM 			vnetp->nvids = 0;
22807529SSriharsha.Basavapatna@Sun.COM 		}
22817529SSriharsha.Basavapatna@Sun.COM 		if (nvids != 0) {
22827529SSriharsha.Basavapatna@Sun.COM 			vnetp->nvids = nvids;
22837529SSriharsha.Basavapatna@Sun.COM 			vnetp->vids = vids;
22847529SSriharsha.Basavapatna@Sun.COM 		}
22857529SSriharsha.Basavapatna@Sun.COM 
22867529SSriharsha.Basavapatna@Sun.COM 		/* reset vlan-unaware peers (ver < 1.3) and restart handshake */
22877529SSriharsha.Basavapatna@Sun.COM 		vgen_reset_vlan_unaware_ports(vgenp);
22887529SSriharsha.Basavapatna@Sun.COM 
22897529SSriharsha.Basavapatna@Sun.COM 	} else {
22907529SSriharsha.Basavapatna@Sun.COM 
22916419Ssb155480 		if (nvids != 0) {
22926419Ssb155480 			kmem_free(vids, sizeof (uint16_t) * nvids);
22936419Ssb155480 		}
22947529SSriharsha.Basavapatna@Sun.COM 	}
22957529SSriharsha.Basavapatna@Sun.COM 
22967529SSriharsha.Basavapatna@Sun.COM 	if (updated & MD_mtu) {
22977529SSriharsha.Basavapatna@Sun.COM 
22987529SSriharsha.Basavapatna@Sun.COM 		DBG2(vgenp, NULL, "curr_mtu(%d) new_mtu(%d)\n",
22997529SSriharsha.Basavapatna@Sun.COM 		    vnetp->mtu, mtu);
23007529SSriharsha.Basavapatna@Sun.COM 
23017529SSriharsha.Basavapatna@Sun.COM 		rv = vnet_mtu_update(vnetp, mtu);
23027529SSriharsha.Basavapatna@Sun.COM 		if (rv == 0) {
23037529SSriharsha.Basavapatna@Sun.COM 			vgenp->max_frame_size = mtu +
23047529SSriharsha.Basavapatna@Sun.COM 			    sizeof (struct ether_header) + VLAN_TAGSZ;
23057529SSriharsha.Basavapatna@Sun.COM 		}
23067529SSriharsha.Basavapatna@Sun.COM 	}
23076419Ssb155480 }
23086419Ssb155480 
23091991Sheppo /* add a new port to the device */
23101991Sheppo static int
23111991Sheppo vgen_add_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex)
23121991Sheppo {
23136419Ssb155480 	vgen_port_t	*portp;
23146419Ssb155480 	int		rv;
23156419Ssb155480 
23166419Ssb155480 	portp = kmem_zalloc(sizeof (vgen_port_t), KM_SLEEP);
23176419Ssb155480 
23186419Ssb155480 	rv = vgen_port_read_props(portp, vgenp, mdp, mdex);
23196419Ssb155480 	if (rv != DDI_SUCCESS) {
23206419Ssb155480 		KMEM_FREE(portp);
23216419Ssb155480 		return (DDI_FAILURE);
23226419Ssb155480 	}
23236419Ssb155480 
23246419Ssb155480 	rv = vgen_port_attach(portp);
23256419Ssb155480 	if (rv != DDI_SUCCESS) {
23266419Ssb155480 		return (DDI_FAILURE);
23276419Ssb155480 	}
23286419Ssb155480 
23296419Ssb155480 	return (DDI_SUCCESS);
23306419Ssb155480 }
23316419Ssb155480 
23326419Ssb155480 /* read properties of the port from its md node */
23336419Ssb155480 static int
23346419Ssb155480 vgen_port_read_props(vgen_port_t *portp, vgen_t *vgenp, md_t *mdp,
23356419Ssb155480 	mde_cookie_t mdex)
23366419Ssb155480 {
23376419Ssb155480 	uint64_t		port_num;
23386419Ssb155480 	uint64_t		*ldc_ids;
23396419Ssb155480 	uint64_t		macaddr;
23406419Ssb155480 	uint64_t		val;
23416419Ssb155480 	int			num_ldcs;
23426419Ssb155480 	int			i;
23436419Ssb155480 	int			addrsz;
23446419Ssb155480 	int			num_nodes = 0;
23456419Ssb155480 	int			listsz = 0;
23466419Ssb155480 	mde_cookie_t		*listp = NULL;
23476419Ssb155480 	uint8_t			*addrp;
23481991Sheppo 	struct ether_addr	ea;
23491991Sheppo 
23501991Sheppo 	/* read "id" property to get the port number */
23511991Sheppo 	if (md_get_prop_val(mdp, mdex, id_propname, &port_num)) {
23524647Sraghuram 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
23531991Sheppo 		return (DDI_FAILURE);
23541991Sheppo 	}
23551991Sheppo 
23561991Sheppo 	/*
23571991Sheppo 	 * Find the channel endpoint node(s) under this port node.
23581991Sheppo 	 */
23591991Sheppo 	if ((num_nodes = md_node_count(mdp)) <= 0) {
23604647Sraghuram 		DWARN(vgenp, NULL, "invalid number of nodes found (%d)",
23614647Sraghuram 		    num_nodes);
23621991Sheppo 		return (DDI_FAILURE);
23631991Sheppo 	}
23641991Sheppo 
23651991Sheppo 	/* allocate space for node list */
23661991Sheppo 	listsz = num_nodes * sizeof (mde_cookie_t);
23671991Sheppo 	listp = kmem_zalloc(listsz, KM_NOSLEEP);
23681991Sheppo 	if (listp == NULL)
23691991Sheppo 		return (DDI_FAILURE);
23701991Sheppo 
23711991Sheppo 	num_ldcs = md_scan_dag(mdp, mdex,
23724650Sraghuram 	    md_find_name(mdp, channel_propname),
23734650Sraghuram 	    md_find_name(mdp, "fwd"), listp);
23741991Sheppo 
23751991Sheppo 	if (num_ldcs <= 0) {
23764647Sraghuram 		DWARN(vgenp, NULL, "can't find %s nodes", channel_propname);
23771991Sheppo 		kmem_free(listp, listsz);
23781991Sheppo 		return (DDI_FAILURE);
23791991Sheppo 	}
23801991Sheppo 
23814647Sraghuram 	DBG2(vgenp, NULL, "num_ldcs %d", num_ldcs);
23821991Sheppo 
23831991Sheppo 	ldc_ids = kmem_zalloc(num_ldcs * sizeof (uint64_t), KM_NOSLEEP);
23841991Sheppo 	if (ldc_ids == NULL) {
23851991Sheppo 		kmem_free(listp, listsz);
23861991Sheppo 		return (DDI_FAILURE);
23871991Sheppo 	}
23881991Sheppo 
23891991Sheppo 	for (i = 0; i < num_ldcs; i++) {
23901991Sheppo 		/* read channel ids */
23911991Sheppo 		if (md_get_prop_val(mdp, listp[i], id_propname, &ldc_ids[i])) {
23924647Sraghuram 			DWARN(vgenp, NULL, "prop(%s) not found\n",
23934647Sraghuram 			    id_propname);
23941991Sheppo 			kmem_free(listp, listsz);
23951991Sheppo 			kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
23961991Sheppo 			return (DDI_FAILURE);
23971991Sheppo 		}
23984647Sraghuram 		DBG2(vgenp, NULL, "ldc_id 0x%llx", ldc_ids[i]);
23991991Sheppo 	}
24001991Sheppo 
24011991Sheppo 	kmem_free(listp, listsz);
24021991Sheppo 
24031991Sheppo 	if (md_get_prop_data(mdp, mdex, rmacaddr_propname, &addrp,
24041991Sheppo 	    &addrsz)) {
24054647Sraghuram 		DWARN(vgenp, NULL, "prop(%s) not found\n", rmacaddr_propname);
24061991Sheppo 		kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
24071991Sheppo 		return (DDI_FAILURE);
24081991Sheppo 	}
24091991Sheppo 
24101991Sheppo 	if (addrsz < ETHERADDRL) {
24114647Sraghuram 		DWARN(vgenp, NULL, "invalid address size (%d)\n", addrsz);
24121991Sheppo 		kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
24131991Sheppo 		return (DDI_FAILURE);
24141991Sheppo 	}
24151991Sheppo 
24161991Sheppo 	macaddr = *((uint64_t *)addrp);
24171991Sheppo 
24184647Sraghuram 	DBG2(vgenp, NULL, "remote mac address 0x%llx\n", macaddr);
24191991Sheppo 
24201991Sheppo 	for (i = ETHERADDRL - 1; i >= 0; i--) {
24211991Sheppo 		ea.ether_addr_octet[i] = macaddr & 0xFF;
24221991Sheppo 		macaddr >>= 8;
24231991Sheppo 	}
24241991Sheppo 
24251991Sheppo 	if (vgenp->vsw_portp == NULL) {
24261991Sheppo 		if (!(md_get_prop_val(mdp, mdex, swport_propname, &val))) {
24271991Sheppo 			if (val == 0) {
24286495Sspeer 				(void) atomic_swap_32(
24296495Sspeer 				    &vgenp->vsw_port_refcnt, 0);
24306419Ssb155480 				/* This port is connected to the vsw */
24316419Ssb155480 				vgenp->vsw_portp = portp;
24321991Sheppo 			}
24331991Sheppo 		}
24341991Sheppo 	}
24356419Ssb155480 
24366419Ssb155480 	/* now update all properties into the port */
24376419Ssb155480 	portp->vgenp = vgenp;
24386419Ssb155480 	portp->port_num = port_num;
24396419Ssb155480 	ether_copy(&ea, &portp->macaddr);
24406419Ssb155480 	portp->ldc_ids = kmem_zalloc(sizeof (uint64_t) * num_ldcs, KM_SLEEP);
24416419Ssb155480 	bcopy(ldc_ids, portp->ldc_ids, sizeof (uint64_t) * num_ldcs);
24426419Ssb155480 	portp->num_ldcs = num_ldcs;
24436419Ssb155480 
24446419Ssb155480 	/* read vlan id properties of this port node */
24456419Ssb155480 	vgen_vlan_read_ids(portp, VGEN_PEER, mdp, mdex, &portp->pvid,
24466419Ssb155480 	    &portp->vids, &portp->nvids, NULL);
24471991Sheppo 
24481991Sheppo 	kmem_free(ldc_ids, num_ldcs * sizeof (uint64_t));
24491991Sheppo 
24506419Ssb155480 	return (DDI_SUCCESS);
24511991Sheppo }
24521991Sheppo 
24531991Sheppo /* remove a port from the device */
24541991Sheppo static int
24551991Sheppo vgen_remove_port(vgen_t *vgenp, md_t *mdp, mde_cookie_t mdex)
24561991Sheppo {
24571991Sheppo 	uint64_t	port_num;
24581991Sheppo 	vgen_port_t	*portp;
24591991Sheppo 	vgen_portlist_t	*plistp;
24601991Sheppo 
24611991Sheppo 	/* read "id" property to get the port number */
24621991Sheppo 	if (md_get_prop_val(mdp, mdex, id_propname, &port_num)) {
24634647Sraghuram 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
24641991Sheppo 		return (DDI_FAILURE);
24651991Sheppo 	}
24661991Sheppo 
24671991Sheppo 	plistp = &(vgenp->vgenports);
24681991Sheppo 
24691991Sheppo 	WRITE_ENTER(&plistp->rwlock);
24701991Sheppo 	portp = vgen_port_lookup(plistp, (int)port_num);
24711991Sheppo 	if (portp == NULL) {
24724647Sraghuram 		DWARN(vgenp, NULL, "can't find port(%lx)\n", port_num);
24731991Sheppo 		RW_EXIT(&plistp->rwlock);
24741991Sheppo 		return (DDI_FAILURE);
24751991Sheppo 	}
24761991Sheppo 
24771991Sheppo 	vgen_port_detach_mdeg(portp);
24781991Sheppo 	RW_EXIT(&plistp->rwlock);
24791991Sheppo 
24801991Sheppo 	return (DDI_SUCCESS);
24811991Sheppo }
24821991Sheppo 
24831991Sheppo /* attach a port to the device based on mdeg data */
24841991Sheppo static int
24856419Ssb155480 vgen_port_attach(vgen_port_t *portp)
24861991Sheppo {
24871991Sheppo 	int			i;
24886419Ssb155480 	vgen_portlist_t		*plistp;
24896419Ssb155480 	vgen_t			*vgenp;
24906419Ssb155480 	uint64_t		*ldcids;
24916419Ssb155480 	uint32_t		num_ldcs;
24926495Sspeer 	mac_register_t		*macp;
24936495Sspeer 	vio_net_res_type_t	type;
24946495Sspeer 	int			rv;
24956419Ssb155480 
24966419Ssb155480 	ASSERT(portp != NULL);
24976419Ssb155480 
24986419Ssb155480 	vgenp = portp->vgenp;
24996419Ssb155480 	ldcids = portp->ldc_ids;
25006419Ssb155480 	num_ldcs = portp->num_ldcs;
25011991Sheppo 
25024647Sraghuram 	DBG1(vgenp, NULL, "port_num(%d)\n", portp->port_num);
25031991Sheppo 
25046495Sspeer 	mutex_init(&portp->lock, NULL, MUTEX_DRIVER, NULL);
25056419Ssb155480 	rw_init(&portp->ldclist.rwlock, NULL, RW_DRIVER, NULL);
25061991Sheppo 	portp->ldclist.headp = NULL;
25076419Ssb155480 
25086419Ssb155480 	for (i = 0; i < num_ldcs; i++) {
25094647Sraghuram 		DBG2(vgenp, NULL, "ldcid (%lx)\n", ldcids[i]);
25104663Szk194757 		if (vgen_ldc_attach(portp, ldcids[i]) == DDI_FAILURE) {
25114663Szk194757 			vgen_port_detach(portp);
25124663Szk194757 			return (DDI_FAILURE);
25134663Szk194757 		}
25141991Sheppo 	}
25151991Sheppo 
25166419Ssb155480 	/* create vlan id hash table */
25176419Ssb155480 	vgen_vlan_create_hash(portp);
25186419Ssb155480 
25196495Sspeer 	if (portp == vgenp->vsw_portp) {
25206495Sspeer 		/* This port is connected to the switch port */
25216495Sspeer 		vgenp->vsw_portp = portp;
25226495Sspeer 		(void) atomic_swap_32(&portp->use_vsw_port, B_FALSE);
25236495Sspeer 		type = VIO_NET_RES_LDC_SERVICE;
25246495Sspeer 	} else {
25256495Sspeer 		(void) atomic_swap_32(&portp->use_vsw_port, B_TRUE);
25266495Sspeer 		type = VIO_NET_RES_LDC_GUEST;
25276495Sspeer 	}
25286495Sspeer 
25296495Sspeer 	if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
25306495Sspeer 		vgen_port_detach(portp);
25316495Sspeer 		return (DDI_FAILURE);
25326495Sspeer 	}
25336495Sspeer 	macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
25346495Sspeer 	macp->m_driver = portp;
25356495Sspeer 	macp->m_dip = vgenp->vnetdip;
25366495Sspeer 	macp->m_src_addr = (uint8_t *)&(vgenp->macaddr);
25376495Sspeer 	macp->m_callbacks = &vgen_m_callbacks;
25386495Sspeer 	macp->m_min_sdu = 0;
25396495Sspeer 	macp->m_max_sdu = ETHERMTU;
25406495Sspeer 
25416495Sspeer 	mutex_enter(&portp->lock);
25426495Sspeer 	rv = vio_net_resource_reg(macp, type, vgenp->macaddr,
25436495Sspeer 	    portp->macaddr, &portp->vhp, &portp->vcb);
25446495Sspeer 	mutex_exit(&portp->lock);
25456495Sspeer 	mac_free(macp);
25466495Sspeer 
25476495Sspeer 	if (rv == 0) {
25486495Sspeer 		/* link it into the list of ports */
25496495Sspeer 		plistp = &(vgenp->vgenports);
25506495Sspeer 		WRITE_ENTER(&plistp->rwlock);
25516495Sspeer 		vgen_port_list_insert(portp);
25526495Sspeer 		RW_EXIT(&plistp->rwlock);
25536495Sspeer 	} else {
25546495Sspeer 		DERR(vgenp, NULL, "vio_net_resource_reg failed for portp=0x%p",
25556495Sspeer 		    portp);
25566495Sspeer 		vgen_port_detach(portp);
25571991Sheppo 	}
25581991Sheppo 
25594647Sraghuram 	DBG1(vgenp, NULL, "exit: port_num(%d)\n", portp->port_num);
25601991Sheppo 	return (DDI_SUCCESS);
25611991Sheppo }
25621991Sheppo 
25631991Sheppo /* detach a port from the device based on mdeg data */
25641991Sheppo static void
25651991Sheppo vgen_port_detach_mdeg(vgen_port_t *portp)
25661991Sheppo {
25671991Sheppo 	vgen_t *vgenp = portp->vgenp;
25681991Sheppo 
25694647Sraghuram 	DBG1(vgenp, NULL, "enter: port_num(%d)\n", portp->port_num);
25706495Sspeer 
25716495Sspeer 	mutex_enter(&portp->lock);
25726495Sspeer 
25731991Sheppo 	/* stop the port if needed */
25746495Sspeer 	if (portp->flags & VGEN_STARTED) {
25751991Sheppo 		vgen_port_uninit(portp);
25761991Sheppo 	}
25776495Sspeer 
25786495Sspeer 	mutex_exit(&portp->lock);
25791991Sheppo 	vgen_port_detach(portp);
25801991Sheppo 
25814647Sraghuram 	DBG1(vgenp, NULL, "exit: port_num(%d)\n", portp->port_num);
25821991Sheppo }
25831991Sheppo 
25841991Sheppo static int
25851991Sheppo vgen_update_port(vgen_t *vgenp, md_t *curr_mdp, mde_cookie_t curr_mdex,
25861991Sheppo 	md_t *prev_mdp, mde_cookie_t prev_mdex)
25871991Sheppo {
25886419Ssb155480 	uint64_t	cport_num;
25896419Ssb155480 	uint64_t	pport_num;
25906419Ssb155480 	vgen_portlist_t	*plistp;
25916419Ssb155480 	vgen_port_t	*portp;
25926419Ssb155480 	boolean_t	updated_vlans = B_FALSE;
25936419Ssb155480 	uint16_t	pvid;
25946419Ssb155480 	uint16_t	*vids;
25956419Ssb155480 	uint16_t	nvids;
25966419Ssb155480 
25976419Ssb155480 	/*
25986419Ssb155480 	 * For now, we get port updates only if vlan ids changed.
25996419Ssb155480 	 * We read the port num and do some sanity check.
26006419Ssb155480 	 */
26016419Ssb155480 	if (md_get_prop_val(curr_mdp, curr_mdex, id_propname, &cport_num)) {
26026419Ssb155480 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
26036419Ssb155480 		return (DDI_FAILURE);
26046419Ssb155480 	}
26056419Ssb155480 
26066419Ssb155480 	if (md_get_prop_val(prev_mdp, prev_mdex, id_propname, &pport_num)) {
26076419Ssb155480 		DWARN(vgenp, NULL, "prop(%s) not found\n", id_propname);
26086419Ssb155480 		return (DDI_FAILURE);
26096419Ssb155480 	}
26106419Ssb155480 	if (cport_num != pport_num)
26116419Ssb155480 		return (DDI_FAILURE);
26126419Ssb155480 
26136419Ssb155480 	plistp = &(vgenp->vgenports);
26146419Ssb155480 
26156419Ssb155480 	READ_ENTER(&plistp->rwlock);
26166419Ssb155480 
26176419Ssb155480 	portp = vgen_port_lookup(plistp, (int)cport_num);
26186419Ssb155480 	if (portp == NULL) {
26196419Ssb155480 		DWARN(vgenp, NULL, "can't find port(%lx)\n", cport_num);
26206419Ssb155480 		RW_EXIT(&plistp->rwlock);
26216419Ssb155480 		return (DDI_FAILURE);
26226419Ssb155480 	}
26236419Ssb155480 
26246419Ssb155480 	/* Read the vlan ids */
26256419Ssb155480 	vgen_vlan_read_ids(portp, VGEN_PEER, curr_mdp, curr_mdex, &pvid, &vids,
26266419Ssb155480 	    &nvids, NULL);
26276419Ssb155480 
26286419Ssb155480 	/* Determine if there are any vlan id updates */
26296419Ssb155480 	if ((pvid != portp->pvid) ||		/* pvid changed? */
26306419Ssb155480 	    (nvids != portp->nvids) ||		/* # of vids changed? */
26316419Ssb155480 	    ((nvids != 0) && (portp->nvids != 0) &&	/* vids changed? */
26326419Ssb155480 	    bcmp(vids, portp->vids, sizeof (uint16_t) * nvids))) {
26336419Ssb155480 		updated_vlans = B_TRUE;
26346419Ssb155480 	}
26356419Ssb155480 
26366419Ssb155480 	if (updated_vlans == B_FALSE) {
26376419Ssb155480 		RW_EXIT(&plistp->rwlock);
26386419Ssb155480 		return (DDI_FAILURE);
26396419Ssb155480 	}
26406419Ssb155480 
26416419Ssb155480 	/* remove the port from vlans it has been assigned to */
26426419Ssb155480 	vgen_vlan_remove_ids(portp);
26436419Ssb155480 
26446419Ssb155480 	/* save the new vlan ids */
26456419Ssb155480 	portp->pvid = pvid;
26466419Ssb155480 	if (portp->nvids != 0) {
26476419Ssb155480 		kmem_free(portp->vids, sizeof (uint16_t) * portp->nvids);
26486419Ssb155480 		portp->nvids = 0;
26496419Ssb155480 	}
26506419Ssb155480 	if (nvids != 0) {
26516419Ssb155480 		portp->vids = kmem_zalloc(sizeof (uint16_t) * nvids, KM_SLEEP);
26526419Ssb155480 		bcopy(vids, portp->vids, sizeof (uint16_t) * nvids);
26536419Ssb155480 		portp->nvids = nvids;
26546419Ssb155480 		kmem_free(vids, sizeof (uint16_t) * nvids);
26556419Ssb155480 	}
26566419Ssb155480 
26576419Ssb155480 	/* add port to the new vlans */
26586419Ssb155480 	vgen_vlan_add_ids(portp);
26596419Ssb155480 
26606419Ssb155480 	/* reset the port if it is vlan unaware (ver < 1.3) */
26616419Ssb155480 	vgen_vlan_unaware_port_reset(portp);
26626419Ssb155480 
26636419Ssb155480 	RW_EXIT(&plistp->rwlock);
26646419Ssb155480 
26651991Sheppo 	return (DDI_SUCCESS);
26661991Sheppo }
26671991Sheppo 
26681991Sheppo static uint64_t
26692311Sseb vgen_port_stat(vgen_port_t *portp, uint_t stat)
26701991Sheppo {
26711991Sheppo 	vgen_ldclist_t	*ldclp;
26721991Sheppo 	vgen_ldc_t *ldcp;
26731991Sheppo 	uint64_t	val;
26741991Sheppo 
26751991Sheppo 	val = 0;
26761991Sheppo 	ldclp = &portp->ldclist;
26771991Sheppo 
26781991Sheppo 	READ_ENTER(&ldclp->rwlock);
26791991Sheppo 	for (ldcp = ldclp->headp; ldcp != NULL; ldcp = ldcp->nextp) {
26801991Sheppo 		val += vgen_ldc_stat(ldcp, stat);
26811991Sheppo 	}
26821991Sheppo 	RW_EXIT(&ldclp->rwlock);
26831991Sheppo 
26841991Sheppo 	return (val);
26851991Sheppo }
26861991Sheppo 
26877529SSriharsha.Basavapatna@Sun.COM /* allocate receive resources */
26887529SSriharsha.Basavapatna@Sun.COM static int
26897529SSriharsha.Basavapatna@Sun.COM vgen_init_multipools(vgen_ldc_t *ldcp)
26907529SSriharsha.Basavapatna@Sun.COM {
26917529SSriharsha.Basavapatna@Sun.COM 	size_t		data_sz;
26927529SSriharsha.Basavapatna@Sun.COM 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
26937529SSriharsha.Basavapatna@Sun.COM 	int		status;
26947529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz1 = 0;
26957529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz2 = 0;
26967529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz3 = 0;
26977529SSriharsha.Basavapatna@Sun.COM 	uint32_t	sz4 = 0;
26987529SSriharsha.Basavapatna@Sun.COM 
26997529SSriharsha.Basavapatna@Sun.COM 	/*
27007529SSriharsha.Basavapatna@Sun.COM 	 * We round up the mtu specified to be a multiple of 2K.
27017529SSriharsha.Basavapatna@Sun.COM 	 * We then create rx pools based on the rounded up size.
27027529SSriharsha.Basavapatna@Sun.COM 	 */
27037529SSriharsha.Basavapatna@Sun.COM 	data_sz = vgenp->max_frame_size + VNET_IPALIGN + VNET_LDCALIGN;
27047529SSriharsha.Basavapatna@Sun.COM 	data_sz = VNET_ROUNDUP_2K(data_sz);
27057529SSriharsha.Basavapatna@Sun.COM 
27067529SSriharsha.Basavapatna@Sun.COM 	/*
27077529SSriharsha.Basavapatna@Sun.COM 	 * If pool sizes are specified, use them. Note that the presence of
27087529SSriharsha.Basavapatna@Sun.COM 	 * the first tunable will be used as a hint.
27097529SSriharsha.Basavapatna@Sun.COM 	 */
27107529SSriharsha.Basavapatna@Sun.COM 	if (vgen_rbufsz1 != 0) {
27117529SSriharsha.Basavapatna@Sun.COM 
27127529SSriharsha.Basavapatna@Sun.COM 		sz1 = vgen_rbufsz1;
27137529SSriharsha.Basavapatna@Sun.COM 		sz2 = vgen_rbufsz2;
27147529SSriharsha.Basavapatna@Sun.COM 		sz3 = vgen_rbufsz3;
27157529SSriharsha.Basavapatna@Sun.COM 		sz4 = vgen_rbufsz4;
27167529SSriharsha.Basavapatna@Sun.COM 
27177529SSriharsha.Basavapatna@Sun.COM 		if (sz4 == 0) { /* need 3 pools */
27187529SSriharsha.Basavapatna@Sun.COM 
27197529SSriharsha.Basavapatna@Sun.COM 			ldcp->max_rxpool_size = sz3;
27207529SSriharsha.Basavapatna@Sun.COM 			status = vio_init_multipools(&ldcp->vmp,
27217529SSriharsha.Basavapatna@Sun.COM 			    VGEN_NUM_VMPOOLS, sz1, sz2, sz3, vgen_nrbufs1,
27227529SSriharsha.Basavapatna@Sun.COM 			    vgen_nrbufs2, vgen_nrbufs3);
27237529SSriharsha.Basavapatna@Sun.COM 
27247529SSriharsha.Basavapatna@Sun.COM 		} else {
27257529SSriharsha.Basavapatna@Sun.COM 
27267529SSriharsha.Basavapatna@Sun.COM 			ldcp->max_rxpool_size = sz4;
27277529SSriharsha.Basavapatna@Sun.COM 			status = vio_init_multipools(&ldcp->vmp,
27287529SSriharsha.Basavapatna@Sun.COM 			    VGEN_NUM_VMPOOLS + 1, sz1, sz2, sz3, sz4,
27297529SSriharsha.Basavapatna@Sun.COM 			    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3,
27307529SSriharsha.Basavapatna@Sun.COM 			    vgen_nrbufs4);
27317529SSriharsha.Basavapatna@Sun.COM 		}
27327529SSriharsha.Basavapatna@Sun.COM 		return (status);
27337529SSriharsha.Basavapatna@Sun.COM 	}
27347529SSriharsha.Basavapatna@Sun.COM 
27357529SSriharsha.Basavapatna@Sun.COM 	/*
27367529SSriharsha.Basavapatna@Sun.COM 	 * Pool sizes are not specified. We select the pool sizes based on the
27377529SSriharsha.Basavapatna@Sun.COM 	 * mtu if vnet_jumbo_rxpools is enabled.
27387529SSriharsha.Basavapatna@Sun.COM 	 */
27397529SSriharsha.Basavapatna@Sun.COM 	if (vnet_jumbo_rxpools == B_FALSE || data_sz == VNET_2K) {
27407529SSriharsha.Basavapatna@Sun.COM 		/*
27417529SSriharsha.Basavapatna@Sun.COM 		 * Receive buffer pool allocation based on mtu is disabled.
27427529SSriharsha.Basavapatna@Sun.COM 		 * Use the default mechanism of standard size pool allocation.
27437529SSriharsha.Basavapatna@Sun.COM 		 */
27447529SSriharsha.Basavapatna@Sun.COM 		sz1 = VGEN_DBLK_SZ_128;
27457529SSriharsha.Basavapatna@Sun.COM 		sz2 = VGEN_DBLK_SZ_256;
27467529SSriharsha.Basavapatna@Sun.COM 		sz3 = VGEN_DBLK_SZ_2048;
27477529SSriharsha.Basavapatna@Sun.COM 		ldcp->max_rxpool_size = sz3;
27487529SSriharsha.Basavapatna@Sun.COM 
27497529SSriharsha.Basavapatna@Sun.COM 		status = vio_init_multipools(&ldcp->vmp, VGEN_NUM_VMPOOLS,
27507529SSriharsha.Basavapatna@Sun.COM 		    sz1, sz2, sz3,
27517529SSriharsha.Basavapatna@Sun.COM 		    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3);
27527529SSriharsha.Basavapatna@Sun.COM 
27537529SSriharsha.Basavapatna@Sun.COM 		return (status);
27547529SSriharsha.Basavapatna@Sun.COM 	}
27557529SSriharsha.Basavapatna@Sun.COM 
27567529SSriharsha.Basavapatna@Sun.COM 	switch (data_sz) {
27577529SSriharsha.Basavapatna@Sun.COM 
27587529SSriharsha.Basavapatna@Sun.COM 	case VNET_4K:
27597529SSriharsha.Basavapatna@Sun.COM 
27607529SSriharsha.Basavapatna@Sun.COM 		sz1 = VGEN_DBLK_SZ_128;
27617529SSriharsha.Basavapatna@Sun.COM 		sz2 = VGEN_DBLK_SZ_256;
27627529SSriharsha.Basavapatna@Sun.COM 		sz3 = VGEN_DBLK_SZ_2048;
27637529SSriharsha.Basavapatna@Sun.COM 		sz4 = sz3 << 1;			/* 4K */
27647529SSriharsha.Basavapatna@Sun.COM 		ldcp->max_rxpool_size = sz4;
27657529SSriharsha.Basavapatna@Sun.COM 
27667529SSriharsha.Basavapatna@Sun.COM 		status = vio_init_multipools(&ldcp->vmp, VGEN_NUM_VMPOOLS + 1,
27677529SSriharsha.Basavapatna@Sun.COM 		    sz1, sz2, sz3, sz4,
27687529SSriharsha.Basavapatna@Sun.COM 		    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3, vgen_nrbufs4);
27697529SSriharsha.Basavapatna@Sun.COM 		break;
27707529SSriharsha.Basavapatna@Sun.COM 
27717529SSriharsha.Basavapatna@Sun.COM 	default:	/* data_sz:  4K+ to 16K */
27727529SSriharsha.Basavapatna@Sun.COM 
27737529SSriharsha.Basavapatna@Sun.COM 		sz1 = VGEN_DBLK_SZ_256;
27747529SSriharsha.Basavapatna@Sun.COM 		sz2 = VGEN_DBLK_SZ_2048;
27757529SSriharsha.Basavapatna@Sun.COM 		sz3 = data_sz >> 1;	/* Jumbo-size/2 */
27767529SSriharsha.Basavapatna@Sun.COM 		sz4 = data_sz;		/* Jumbo-size  */
27777529SSriharsha.Basavapatna@Sun.COM 		ldcp->max_rxpool_size = sz4;
27787529SSriharsha.Basavapatna@Sun.COM 
27797529SSriharsha.Basavapatna@Sun.COM 		status = vio_init_multipools(&ldcp->vmp, VGEN_NUM_VMPOOLS + 1,
27807529SSriharsha.Basavapatna@Sun.COM 		    sz1, sz2, sz3, sz4,
27817529SSriharsha.Basavapatna@Sun.COM 		    vgen_nrbufs1, vgen_nrbufs2, vgen_nrbufs3, vgen_nrbufs4);
27827529SSriharsha.Basavapatna@Sun.COM 		break;
27837529SSriharsha.Basavapatna@Sun.COM 
27847529SSriharsha.Basavapatna@Sun.COM 	}
27857529SSriharsha.Basavapatna@Sun.COM 
27867529SSriharsha.Basavapatna@Sun.COM 	return (status);
27877529SSriharsha.Basavapatna@Sun.COM }
27887529SSriharsha.Basavapatna@Sun.COM 
27891991Sheppo /* attach the channel corresponding to the given ldc_id to the port */
27901991Sheppo static int
27911991Sheppo vgen_ldc_attach(vgen_port_t *portp, uint64_t ldc_id)
27921991Sheppo {
27931991Sheppo 	vgen_t 		*vgenp;
27941991Sheppo 	vgen_ldclist_t	*ldclp;
27951991Sheppo 	vgen_ldc_t 	*ldcp, **prev_ldcp;
27961991Sheppo 	ldc_attr_t 	attr;
27971991Sheppo 	int 		status;
27981991Sheppo 	ldc_status_t	istatus;
27995373Sraghuram 	char		kname[MAXNAMELEN];
28005373Sraghuram 	int		instance;
28014647Sraghuram 	enum	{AST_init = 0x0, AST_ldc_alloc = 0x1,
28024647Sraghuram 		AST_mutex_init = 0x2, AST_ldc_init = 0x4,
28034647Sraghuram 		AST_ldc_reg_cb = 0x8, AST_alloc_tx_ring = 0x10,
28045935Ssb155480 		AST_create_rxmblks = 0x20,
28055935Ssb155480 		AST_create_rcv_thread = 0x40} attach_state;
28061991Sheppo 
28071991Sheppo 	attach_state = AST_init;
28081991Sheppo 	vgenp = portp->vgenp;
28091991Sheppo 	ldclp = &portp->ldclist;
28101991Sheppo 
28111991Sheppo 	ldcp = kmem_zalloc(sizeof (vgen_ldc_t), KM_NOSLEEP);
28121991Sheppo 	if (ldcp == NULL) {
28131991Sheppo 		goto ldc_attach_failed;
28141991Sheppo 	}
28151991Sheppo 	ldcp->ldc_id = ldc_id;
28161991Sheppo 	ldcp->portp = portp;
28171991Sheppo 
28181991Sheppo 	attach_state |= AST_ldc_alloc;
28191991Sheppo 
28201991Sheppo 	mutex_init(&ldcp->txlock, NULL, MUTEX_DRIVER, NULL);
28211991Sheppo 	mutex_init(&ldcp->cblock, NULL, MUTEX_DRIVER, NULL);
28221991Sheppo 	mutex_init(&ldcp->tclock, NULL, MUTEX_DRIVER, NULL);
28234647Sraghuram 	mutex_init(&ldcp->wrlock, NULL, MUTEX_DRIVER, NULL);
28244647Sraghuram 	mutex_init(&ldcp->rxlock, NULL, MUTEX_DRIVER, NULL);
28251991Sheppo 
28261991Sheppo 	attach_state |= AST_mutex_init;
28271991Sheppo 
28281991Sheppo 	attr.devclass = LDC_DEV_NT;
28296495Sspeer 	attr.instance = vgenp->instance;
28301991Sheppo 	attr.mode = LDC_MODE_UNRELIABLE;
28312410Slm66018 	attr.mtu = vnet_ldc_mtu;
28321991Sheppo 	status = ldc_init(ldc_id, &attr, &ldcp->ldc_handle);
28331991Sheppo 	if (status != 0) {
28344647Sraghuram 		DWARN(vgenp, ldcp, "ldc_init failed,rv (%d)\n", status);
28351991Sheppo 		goto ldc_attach_failed;
28361991Sheppo 	}
28371991Sheppo 	attach_state |= AST_ldc_init;
28381991Sheppo 
28394647Sraghuram 	if (vgen_rcv_thread_enabled) {
28404647Sraghuram 		ldcp->rcv_thr_flags = 0;
28414647Sraghuram 
28424647Sraghuram 		mutex_init(&ldcp->rcv_thr_lock, NULL, MUTEX_DRIVER, NULL);
28434647Sraghuram 		cv_init(&ldcp->rcv_thr_cv, NULL, CV_DRIVER, NULL);
28444647Sraghuram 		ldcp->rcv_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
28454647Sraghuram 		    vgen_ldc_rcv_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
28464647Sraghuram 
28474647Sraghuram 		attach_state |= AST_create_rcv_thread;
28484647Sraghuram 		if (ldcp->rcv_thread == NULL) {
28494647Sraghuram 			DWARN(vgenp, ldcp, "Failed to create worker thread");
28504647Sraghuram 			goto ldc_attach_failed;
28514647Sraghuram 		}
28524647Sraghuram 	}
28534647Sraghuram 
28541991Sheppo 	status = ldc_reg_callback(ldcp->ldc_handle, vgen_ldc_cb, (caddr_t)ldcp);
28551991Sheppo 	if (status != 0) {
28564647Sraghuram 		DWARN(vgenp, ldcp, "ldc_reg_callback failed, rv (%d)\n",
28574647Sraghuram 		    status);
28581991Sheppo 		goto ldc_attach_failed;
28591991Sheppo 	}
28605935Ssb155480 	/*
28615935Ssb155480 	 * allocate a message for ldc_read()s, big enough to hold ctrl and
28625935Ssb155480 	 * data msgs, including raw data msgs used to recv priority frames.
28635935Ssb155480 	 */
28646419Ssb155480 	ldcp->msglen = VIO_PKT_DATA_HDRSIZE + vgenp->max_frame_size;
28655935Ssb155480 	ldcp->ldcmsg = kmem_alloc(ldcp->msglen, KM_SLEEP);
28661991Sheppo 	attach_state |= AST_ldc_reg_cb;
28671991Sheppo 
28681991Sheppo 	(void) ldc_status(ldcp->ldc_handle, &istatus);
28691991Sheppo 	ASSERT(istatus == LDC_INIT);
28701991Sheppo 	ldcp->ldc_status = istatus;
28711991Sheppo 
28721991Sheppo 	/* allocate transmit resources */
28731991Sheppo 	status = vgen_alloc_tx_ring(ldcp);
28741991Sheppo 	if (status != 0) {
28751991Sheppo 		goto ldc_attach_failed;
28761991Sheppo 	}
28771991Sheppo 	attach_state |= AST_alloc_tx_ring;
28781991Sheppo 
28792336Snarayan 	/* allocate receive resources */
28807529SSriharsha.Basavapatna@Sun.COM 	status = vgen_init_multipools(ldcp);
28812336Snarayan 	if (status != 0) {
28829217SWentao.Yang@Sun.COM 		/*
28839217SWentao.Yang@Sun.COM 		 * We do not return failure if receive mblk pools can't be
28849217SWentao.Yang@Sun.COM 		 * allocated; instead allocb(9F) will be used to dynamically
28859217SWentao.Yang@Sun.COM 		 * allocate buffers during receive.
28869217SWentao.Yang@Sun.COM 		 */
28879217SWentao.Yang@Sun.COM 		DWARN(vgenp, ldcp,
28889217SWentao.Yang@Sun.COM 		    "vnet%d: status(%d), failed to allocate rx mblk pools for "
28899217SWentao.Yang@Sun.COM 		    "channel(0x%lx)\n",
28909217SWentao.Yang@Sun.COM 		    vgenp->instance, status, ldcp->ldc_id);
28919217SWentao.Yang@Sun.COM 	} else {
28929217SWentao.Yang@Sun.COM 		attach_state |= AST_create_rxmblks;
28939217SWentao.Yang@Sun.COM 	}
28942336Snarayan 
28951991Sheppo 	/* Setup kstats for the channel */
28966495Sspeer 	instance = vgenp->instance;
28975373Sraghuram 	(void) sprintf(kname, "vnetldc0x%lx", ldcp->ldc_id);
28985373Sraghuram 	ldcp->ksp = vgen_setup_kstats("vnet", instance, kname, &ldcp->stats);
28995373Sraghuram 	if (ldcp->ksp == NULL) {
29001991Sheppo 		goto ldc_attach_failed;
29011991Sheppo 	}
29021991Sheppo 
29031991Sheppo 	/* initialize vgen_versions supported */
29041991Sheppo 	bcopy(vgen_versions, ldcp->vgen_versions, sizeof (ldcp->vgen_versions));
29055935Ssb155480 	vgen_reset_vnet_proto_ops(ldcp);
29061991Sheppo 
29071991Sheppo 	/* link it into the list of channels for this port */
29081991Sheppo 	WRITE_ENTER(&ldclp->rwlock);
29091991Sheppo 	prev_ldcp = (vgen_ldc_t **)(&ldclp->headp);
29101991Sheppo 	ldcp->nextp = *prev_ldcp;
29111991Sheppo 	*prev_ldcp = ldcp;
29121991Sheppo 	RW_EXIT(&ldclp->rwlock);
29131991Sheppo 
29141991Sheppo 	ldcp->flags |= CHANNEL_ATTACHED;
29151991Sheppo 	return (DDI_SUCCESS);
29161991Sheppo 
29171991Sheppo ldc_attach_failed:
29184647Sraghuram 	if (attach_state & AST_ldc_reg_cb) {
29194647Sraghuram 		(void) ldc_unreg_callback(ldcp->ldc_handle);
29205935Ssb155480 		kmem_free(ldcp->ldcmsg, ldcp->msglen);
29214647Sraghuram 	}
29224647Sraghuram 	if (attach_state & AST_create_rcv_thread) {
29234647Sraghuram 		if (ldcp->rcv_thread != NULL) {
29244647Sraghuram 			vgen_stop_rcv_thread(ldcp);
29254647Sraghuram 		}
29264647Sraghuram 		mutex_destroy(&ldcp->rcv_thr_lock);
29274647Sraghuram 		cv_destroy(&ldcp->rcv_thr_cv);
29284647Sraghuram 	}
29292336Snarayan 	if (attach_state & AST_create_rxmblks) {
29304647Sraghuram 		vio_mblk_pool_t *fvmp = NULL;
29314647Sraghuram 		vio_destroy_multipools(&ldcp->vmp, &fvmp);
29324647Sraghuram 		ASSERT(fvmp == NULL);
29332336Snarayan 	}
29341991Sheppo 	if (attach_state & AST_alloc_tx_ring) {
29351991Sheppo 		vgen_free_tx_ring(ldcp);
29361991Sheppo 	}
29371991Sheppo 	if (attach_state & AST_ldc_init) {
29381991Sheppo 		(void) ldc_fini(ldcp->ldc_handle);
29391991Sheppo 	}
29401991Sheppo 	if (attach_state & AST_mutex_init) {
29411991Sheppo 		mutex_destroy(&ldcp->tclock);
29421991Sheppo 		mutex_destroy(&ldcp->txlock);
29431991Sheppo 		mutex_destroy(&ldcp->cblock);
29444647Sraghuram 		mutex_destroy(&ldcp->wrlock);
29454647Sraghuram 		mutex_destroy(&ldcp->rxlock);
29461991Sheppo 	}
29471991Sheppo 	if (attach_state & AST_ldc_alloc) {
29481991Sheppo 		KMEM_FREE(ldcp);
29491991Sheppo 	}
29501991Sheppo 	return (DDI_FAILURE);
29511991Sheppo }
29521991Sheppo 
29531991Sheppo /* detach a channel from the port */
29541991Sheppo static void
29551991Sheppo vgen_ldc_detach(vgen_ldc_t *ldcp)
29561991Sheppo {
29571991Sheppo 	vgen_port_t	*portp;
29581991Sheppo 	vgen_t 		*vgenp;
29591991Sheppo 	vgen_ldc_t 	*pldcp;
29601991Sheppo 	vgen_ldc_t	**prev_ldcp;
29611991Sheppo 	vgen_ldclist_t	*ldclp;
29621991Sheppo 
29631991Sheppo 	portp = ldcp->portp;
29641991Sheppo 	vgenp = portp->vgenp;
29651991Sheppo 	ldclp = &portp->ldclist;
29661991Sheppo 
29671991Sheppo 	prev_ldcp =  (vgen_ldc_t **)&ldclp->headp;
29681991Sheppo 	for (; (pldcp = *prev_ldcp) != NULL; prev_ldcp = &pldcp->nextp) {
29691991Sheppo 		if (pldcp == ldcp) {
29701991Sheppo 			break;
29711991Sheppo 		}
29721991Sheppo 	}
29731991Sheppo 
29741991Sheppo 	if (pldcp == NULL) {
29751991Sheppo 		/* invalid ldcp? */
29761991Sheppo 		return;
29771991Sheppo 	}
29781991Sheppo 
29791991Sheppo 	if (ldcp->ldc_status != LDC_INIT) {
29804647Sraghuram 		DWARN(vgenp, ldcp, "ldc_status is not INIT\n");
29811991Sheppo 	}
29821991Sheppo 
29831991Sheppo 	if (ldcp->flags & CHANNEL_ATTACHED) {
29841991Sheppo 		ldcp->flags &= ~(CHANNEL_ATTACHED);
29851991Sheppo 
29864647Sraghuram 		(void) ldc_unreg_callback(ldcp->ldc_handle);
29874647Sraghuram 		if (ldcp->rcv_thread != NULL) {
29884647Sraghuram 			/* First stop the receive thread */
29894647Sraghuram 			vgen_stop_rcv_thread(ldcp);
29904647Sraghuram 			mutex_destroy(&ldcp->rcv_thr_lock);
29914647Sraghuram 			cv_destroy(&ldcp->rcv_thr_cv);
29924647Sraghuram 		}
29935935Ssb155480 		kmem_free(ldcp->ldcmsg, ldcp->msglen);
29944647Sraghuram 
29955373Sraghuram 		vgen_destroy_kstats(ldcp->ksp);
29965373Sraghuram 		ldcp->ksp = NULL;
29975373Sraghuram 
29984647Sraghuram 		/*
29994647Sraghuram 		 * if we cannot reclaim all mblks, put this
30004647Sraghuram 		 * on the list of pools(vgenp->rmp) to be reclaimed when the
30014647Sraghuram 		 * device gets detached (see vgen_uninit()).
30024647Sraghuram 		 */
30034647Sraghuram 		vio_destroy_multipools(&ldcp->vmp, &vgenp->rmp);
30042336Snarayan 
30051991Sheppo 		/* free transmit resources */
30061991Sheppo 		vgen_free_tx_ring(ldcp);
30072336Snarayan 
30081991Sheppo 		(void) ldc_fini(ldcp->ldc_handle);
30091991Sheppo 		mutex_destroy(&ldcp->tclock);
30101991Sheppo 		mutex_destroy(&ldcp->txlock);
30111991Sheppo 		mutex_destroy(&ldcp->cblock);
30124647Sraghuram 		mutex_destroy(&ldcp->wrlock);
30134647Sraghuram 		mutex_destroy(&ldcp->rxlock);
30141991Sheppo 
30151991Sheppo 		/* unlink it from the list */
30161991Sheppo 		*prev_ldcp = ldcp->nextp;
30171991Sheppo 		KMEM_FREE(ldcp);
30181991Sheppo 	}
30191991Sheppo }
30201991Sheppo 
30211991Sheppo /*
30221991Sheppo  * This function allocates transmit resources for the channel.
30231991Sheppo  * The resources consist of a transmit descriptor ring and an associated
30241991Sheppo  * transmit buffer ring.
30251991Sheppo  */
30261991Sheppo static int
30271991Sheppo vgen_alloc_tx_ring(vgen_ldc_t *ldcp)
30281991Sheppo {
30291991Sheppo 	void *tbufp;
30301991Sheppo 	ldc_mem_info_t minfo;
30311991Sheppo 	uint32_t txdsize;
30321991Sheppo 	uint32_t tbufsize;
30331991Sheppo 	int status;
30344647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
30351991Sheppo 
30361991Sheppo 	ldcp->num_txds = vnet_ntxds;
30371991Sheppo 	txdsize = sizeof (vnet_public_desc_t);
30381991Sheppo 	tbufsize = sizeof (vgen_private_desc_t);
30391991Sheppo 
30401991Sheppo 	/* allocate transmit buffer ring */
30411991Sheppo 	tbufp = kmem_zalloc(ldcp->num_txds * tbufsize, KM_NOSLEEP);
30421991Sheppo 	if (tbufp == NULL) {
30431991Sheppo 		return (DDI_FAILURE);
30441991Sheppo 	}
30451991Sheppo 
30461991Sheppo 	/* create transmit descriptor ring */
30471991Sheppo 	status = ldc_mem_dring_create(ldcp->num_txds, txdsize,
30481991Sheppo 	    &ldcp->tx_dhandle);
30491991Sheppo 	if (status) {
30504647Sraghuram 		DWARN(vgenp, ldcp, "ldc_mem_dring_create() failed\n");
30511991Sheppo 		kmem_free(tbufp, ldcp->num_txds * tbufsize);
30521991Sheppo 		return (DDI_FAILURE);
30531991Sheppo 	}
30541991Sheppo 
30551991Sheppo 	/* get the addr of descripror ring */
30561991Sheppo 	status = ldc_mem_dring_info(ldcp->tx_dhandle, &minfo);
30571991Sheppo 	if (status) {
30584647Sraghuram 		DWARN(vgenp, ldcp, "ldc_mem_dring_info() failed\n");
30591991Sheppo 		kmem_free(tbufp, ldcp->num_txds * tbufsize);
30601991Sheppo 		(void) ldc_mem_dring_destroy(ldcp->tx_dhandle);
30611991Sheppo 		ldcp->tbufp = NULL;
30621991Sheppo 		return (DDI_FAILURE);
30631991Sheppo 	}
30641991Sheppo 	ldcp->txdp = (vnet_public_desc_t *)(minfo.vaddr);
30651991Sheppo 	ldcp->tbufp = tbufp;
30661991Sheppo 
30671991Sheppo 	ldcp->txdendp = &((ldcp->txdp)[ldcp->num_txds]);
30681991Sheppo 	ldcp->tbufendp = &((ldcp->tbufp)[ldcp->num_txds]);
30691991Sheppo 
30701991Sheppo 	return (DDI_SUCCESS);
30711991Sheppo }
30721991Sheppo 
30731991Sheppo /* Free transmit resources for the channel */
30741991Sheppo static void
30751991Sheppo vgen_free_tx_ring(vgen_ldc_t *ldcp)
30761991Sheppo {
30771991Sheppo 	int tbufsize = sizeof (vgen_private_desc_t);
30781991Sheppo 
30791991Sheppo 	/* free transmit descriptor ring */
30801991Sheppo 	(void) ldc_mem_dring_destroy(ldcp->tx_dhandle);
30811991Sheppo 
30821991Sheppo 	/* free transmit buffer ring */
30831991Sheppo 	kmem_free(ldcp->tbufp, ldcp->num_txds * tbufsize);
30841991Sheppo 	ldcp->txdp = ldcp->txdendp = NULL;
30851991Sheppo 	ldcp->tbufp = ldcp->tbufendp = NULL;
30861991Sheppo }
30871991Sheppo 
30881991Sheppo /* enable transmit/receive on the channels for the port */
30891991Sheppo static void
30901991Sheppo vgen_init_ldcs(vgen_port_t *portp)
30911991Sheppo {
30921991Sheppo 	vgen_ldclist_t	*ldclp = &portp->ldclist;
30931991Sheppo 	vgen_ldc_t	*ldcp;
30941991Sheppo 
30951991Sheppo 	READ_ENTER(&ldclp->rwlock);
30961991Sheppo 	ldcp =  ldclp->headp;
30971991Sheppo 	for (; ldcp  != NULL; ldcp = ldcp->nextp) {
30981991Sheppo 		(void) vgen_ldc_init(ldcp);
30991991Sheppo 	}
31001991Sheppo 	RW_EXIT(&ldclp->rwlock);
31011991Sheppo }
31021991Sheppo 
31031991Sheppo /* stop transmit/receive on the channels for the port */
31041991Sheppo static void
31051991Sheppo vgen_uninit_ldcs(vgen_port_t *portp)
31061991Sheppo {
31071991Sheppo 	vgen_ldclist_t	*ldclp = &portp->ldclist;
31081991Sheppo 	vgen_ldc_t	*ldcp;
31091991Sheppo 
31101991Sheppo 	READ_ENTER(&ldclp->rwlock);
31111991Sheppo 	ldcp =  ldclp->headp;
31121991Sheppo 	for (; ldcp  != NULL; ldcp = ldcp->nextp) {
31131991Sheppo 		vgen_ldc_uninit(ldcp);
31141991Sheppo 	}
31151991Sheppo 	RW_EXIT(&ldclp->rwlock);
31161991Sheppo }
31171991Sheppo 
31181991Sheppo /* enable transmit/receive on the channel */
31191991Sheppo static int
31201991Sheppo vgen_ldc_init(vgen_ldc_t *ldcp)
31211991Sheppo {
31224647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
31231991Sheppo 	ldc_status_t	istatus;
31241991Sheppo 	int		rv;
31252109Slm66018 	uint32_t	retries = 0;
31264647Sraghuram 	enum	{ ST_init = 0x0, ST_ldc_open = 0x1,
31274647Sraghuram 		ST_init_tbufs = 0x2, ST_cb_enable = 0x4} init_state;
31281991Sheppo 	init_state = ST_init;
31291991Sheppo 
31304647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
31311991Sheppo 	LDC_LOCK(ldcp);
31321991Sheppo 
31331991Sheppo 	rv = ldc_open(ldcp->ldc_handle);
31341991Sheppo 	if (rv != 0) {
31354647Sraghuram 		DWARN(vgenp, ldcp, "ldc_open failed: rv(%d)\n", rv);
31361991Sheppo 		goto ldcinit_failed;
31371991Sheppo 	}
31381991Sheppo 	init_state |= ST_ldc_open;
31391991Sheppo 
31401991Sheppo 	(void) ldc_status(ldcp->ldc_handle, &istatus);
31411991Sheppo 	if (istatus != LDC_OPEN && istatus != LDC_READY) {
31424647Sraghuram 		DWARN(vgenp, ldcp, "status(%d) is not OPEN/READY\n", istatus);
31431991Sheppo 		goto ldcinit_failed;
31441991Sheppo 	}
31451991Sheppo 	ldcp->ldc_status = istatus;
31461991Sheppo 
31471991Sheppo 	rv = vgen_init_tbufs(ldcp);
31481991Sheppo 	if (rv != 0) {
31494647Sraghuram 		DWARN(vgenp, ldcp, "vgen_init_tbufs() failed\n");
31501991Sheppo 		goto ldcinit_failed;
31511991Sheppo 	}
31521991Sheppo 	init_state |= ST_init_tbufs;
31531991Sheppo 
31542748Slm66018 	rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_ENABLE);
31552748Slm66018 	if (rv != 0) {
31564647Sraghuram 		DWARN(vgenp, ldcp, "ldc_set_cb_mode failed: rv(%d)\n", rv);
31572748Slm66018 		goto ldcinit_failed;
31582748Slm66018 	}
31592748Slm66018 
31602748Slm66018 	init_state |= ST_cb_enable;
31612748Slm66018 
31622109Slm66018 	do {
31632109Slm66018 		rv = ldc_up(ldcp->ldc_handle);
31642109Slm66018 		if ((rv != 0) && (rv == EWOULDBLOCK)) {
31654647Sraghuram 			DBG2(vgenp, ldcp, "ldc_up err rv(%d)\n", rv);
31662109Slm66018 			drv_usecwait(VGEN_LDC_UP_DELAY);
31672109Slm66018 		}
31682109Slm66018 		if (retries++ >= vgen_ldcup_retries)
31692109Slm66018 			break;
31702109Slm66018 	} while (rv == EWOULDBLOCK);
31711991Sheppo 
31721991Sheppo 	(void) ldc_status(ldcp->ldc_handle, &istatus);
31732793Slm66018 	if (istatus == LDC_UP) {
31744647Sraghuram 		DWARN(vgenp, ldcp, "status(%d) is UP\n", istatus);
31751991Sheppo 	}
31762793Slm66018 
31771991Sheppo 	ldcp->ldc_status = istatus;
31781991Sheppo 
31791991Sheppo 	/* initialize transmit watchdog timeout */
31801991Sheppo 	ldcp->wd_tid = timeout(vgen_ldc_watchdog, (caddr_t)ldcp,
31811991Sheppo 	    drv_usectohz(vnet_ldcwd_interval * 1000));
31821991Sheppo 
31832793Slm66018 	ldcp->hphase = -1;
31841991Sheppo 	ldcp->flags |= CHANNEL_STARTED;
31851991Sheppo 
31862793Slm66018 	/* if channel is already UP - start handshake */
31872793Slm66018 	if (istatus == LDC_UP) {
31882793Slm66018 		vgen_t *vgenp = LDC_TO_VGEN(ldcp);
31892793Slm66018 		if (ldcp->portp != vgenp->vsw_portp) {
31902793Slm66018 			/*
31916495Sspeer 			 * As the channel is up, use this port from now on.
31922793Slm66018 			 */
31936495Sspeer 			(void) atomic_swap_32(
31946495Sspeer 			    &ldcp->portp->use_vsw_port, B_FALSE);
31952793Slm66018 		}
31962793Slm66018 
31972793Slm66018 		/* Initialize local session id */
31982793Slm66018 		ldcp->local_sid = ddi_get_lbolt();
31992793Slm66018 
32002793Slm66018 		/* clear peer session id */
32012793Slm66018 		ldcp->peer_sid = 0;
32022793Slm66018 		ldcp->hretries = 0;
32032793Slm66018 
32042793Slm66018 		/* Initiate Handshake process with peer ldc endpoint */
32052793Slm66018 		vgen_reset_hphase(ldcp);
32062793Slm66018 
32072793Slm66018 		mutex_exit(&ldcp->tclock);
32082793Slm66018 		mutex_exit(&ldcp->txlock);
32094647Sraghuram 		mutex_exit(&ldcp->wrlock);
32105708Sraghuram 		mutex_exit(&ldcp->rxlock);
32112793Slm66018 		vgen_handshake(vh_nextphase(ldcp));
32122793Slm66018 		mutex_exit(&ldcp->cblock);
32132793Slm66018 	} else {
32142793Slm66018 		LDC_UNLOCK(ldcp);
32152793Slm66018 	}
32162793Slm66018 
32171991Sheppo 	return (DDI_SUCCESS);
32181991Sheppo 
32191991Sheppo ldcinit_failed:
32202748Slm66018 	if (init_state & ST_cb_enable) {
32212748Slm66018 		(void) ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE);
32222748Slm66018 	}
32231991Sheppo 	if (init_state & ST_init_tbufs) {
32241991Sheppo 		vgen_uninit_tbufs(ldcp);
32251991Sheppo 	}
32261991Sheppo 	if (init_state & ST_ldc_open) {
32271991Sheppo 		(void) ldc_close(ldcp->ldc_handle);
32281991Sheppo 	}
32291991Sheppo 	LDC_UNLOCK(ldcp);
32304647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
32311991Sheppo 	return (DDI_FAILURE);
32321991Sheppo }
32331991Sheppo 
32341991Sheppo /* stop transmit/receive on the channel */
32351991Sheppo static void
32361991Sheppo vgen_ldc_uninit(vgen_ldc_t *ldcp)
32371991Sheppo {
32384647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
32391991Sheppo 	int	rv;
32408623SSriharsha.Basavapatna@Sun.COM 	uint_t	retries = 0;
32411991Sheppo 
32424647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
32431991Sheppo 	LDC_LOCK(ldcp);
32441991Sheppo 
32451991Sheppo 	if ((ldcp->flags & CHANNEL_STARTED) == 0) {
32461991Sheppo 		LDC_UNLOCK(ldcp);
32474647Sraghuram 		DWARN(vgenp, ldcp, "CHANNEL_STARTED flag is not set\n");
32481991Sheppo 		return;
32491991Sheppo 	}
32501991Sheppo 
32511991Sheppo 	/* disable further callbacks */
32521991Sheppo 	rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE);
32531991Sheppo 	if (rv != 0) {
32544647Sraghuram 		DWARN(vgenp, ldcp, "ldc_set_cb_mode failed\n");
32551991Sheppo 	}
32561991Sheppo 
32576495Sspeer 	if (vgenp->vsw_portp == ldcp->portp) {
32586495Sspeer 		vio_net_report_err_t rep_err =
32596495Sspeer 		    ldcp->portp->vcb.vio_net_report_err;
32606495Sspeer 		rep_err(ldcp->portp->vhp, VIO_NET_RES_DOWN);
32616495Sspeer 	}
32626495Sspeer 
32633653Snarayan 	/*
32643653Snarayan 	 * clear handshake done bit and wait for pending tx and cb to finish.
32653653Snarayan 	 * release locks before untimeout(9F) is invoked to cancel timeouts.
32663653Snarayan 	 */
32671991Sheppo 	ldcp->hphase &= ~(VH_DONE);
32681991Sheppo 	LDC_UNLOCK(ldcp);
32693653Snarayan 
32703653Snarayan 	/* cancel handshake watchdog timeout */
32713653Snarayan 	if (ldcp->htid) {
32723653Snarayan 		(void) untimeout(ldcp->htid);
32733653Snarayan 		ldcp->htid = 0;
32743653Snarayan 	}
32753653Snarayan 
32767572SWentao.Yang@Sun.COM 	if (ldcp->cancel_htid) {
32777572SWentao.Yang@Sun.COM 		(void) untimeout(ldcp->cancel_htid);
32787572SWentao.Yang@Sun.COM 		ldcp->cancel_htid = 0;
32797572SWentao.Yang@Sun.COM 	}
32807572SWentao.Yang@Sun.COM 
32813653Snarayan 	/* cancel transmit watchdog timeout */
32821991Sheppo 	if (ldcp->wd_tid) {
32831991Sheppo 		(void) untimeout(ldcp->wd_tid);
32841991Sheppo 		ldcp->wd_tid = 0;
32851991Sheppo 	}
32861991Sheppo 
32873653Snarayan 	drv_usecwait(1000);
32883653Snarayan 
32898623SSriharsha.Basavapatna@Sun.COM 	if (ldcp->rcv_thread != NULL) {
32908623SSriharsha.Basavapatna@Sun.COM 		/*
32918623SSriharsha.Basavapatna@Sun.COM 		 * Note that callbacks have been disabled already(above). The
32928623SSriharsha.Basavapatna@Sun.COM 		 * drain function takes care of the condition when an already
32938623SSriharsha.Basavapatna@Sun.COM 		 * executing callback signals the worker to start processing or
32948623SSriharsha.Basavapatna@Sun.COM 		 * the worker has already been signalled and is in the middle of
32958623SSriharsha.Basavapatna@Sun.COM 		 * processing.
32968623SSriharsha.Basavapatna@Sun.COM 		 */
32978623SSriharsha.Basavapatna@Sun.COM 		vgen_drain_rcv_thread(ldcp);
32988623SSriharsha.Basavapatna@Sun.COM 	}
32998623SSriharsha.Basavapatna@Sun.COM 
33003653Snarayan 	/* acquire locks again; any pending transmits and callbacks are done */
33013653Snarayan 	LDC_LOCK(ldcp);
33023653Snarayan 
33033653Snarayan 	vgen_reset_hphase(ldcp);
33043653Snarayan 
33051991Sheppo 	vgen_uninit_tbufs(ldcp);
33061991Sheppo 
33078623SSriharsha.Basavapatna@Sun.COM 	/* close the channel - retry on EAGAIN */
33088623SSriharsha.Basavapatna@Sun.COM 	while ((rv = ldc_close(ldcp->ldc_handle)) == EAGAIN) {
33098623SSriharsha.Basavapatna@Sun.COM 		if (++retries > vgen_ldccl_retries) {
33108623SSriharsha.Basavapatna@Sun.COM 			break;
33118623SSriharsha.Basavapatna@Sun.COM 		}
33128623SSriharsha.Basavapatna@Sun.COM 		drv_usecwait(VGEN_LDC_CLOSE_DELAY);
33138623SSriharsha.Basavapatna@Sun.COM 	}
33141991Sheppo 	if (rv != 0) {
33158623SSriharsha.Basavapatna@Sun.COM 		cmn_err(CE_NOTE,
33168623SSriharsha.Basavapatna@Sun.COM 		    "!vnet%d: Error(%d) closing the channel(0x%lx)\n",
33178623SSriharsha.Basavapatna@Sun.COM 		    vgenp->instance, rv, ldcp->ldc_id);
33188623SSriharsha.Basavapatna@Sun.COM 	}
33198623SSriharsha.Basavapatna@Sun.COM 
33201991Sheppo 	ldcp->ldc_status = LDC_INIT;
33211991Sheppo 	ldcp->flags &= ~(CHANNEL_STARTED);
33221991Sheppo 
33231991Sheppo 	LDC_UNLOCK(ldcp);
33241991Sheppo 
33254647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
33261991Sheppo }
33271991Sheppo 
33281991Sheppo /* Initialize the transmit buffer ring for the channel */
33291991Sheppo static int
33301991Sheppo vgen_init_tbufs(vgen_ldc_t *ldcp)
33311991Sheppo {
33321991Sheppo 	vgen_private_desc_t	*tbufp;
33331991Sheppo 	vnet_public_desc_t	*txdp;
33341991Sheppo 	vio_dring_entry_hdr_t		*hdrp;
33351991Sheppo 	int 			i;
33361991Sheppo 	int 			rv;
33372109Slm66018 	caddr_t			datap = NULL;
33382109Slm66018 	int			ci;
33392109Slm66018 	uint32_t		ncookies;
33406419Ssb155480 	size_t			data_sz;
33416419Ssb155480 	vgen_t			*vgenp;
33426419Ssb155480 
33436419Ssb155480 	vgenp = LDC_TO_VGEN(ldcp);
33441991Sheppo 
33451991Sheppo 	bzero(ldcp->tbufp, sizeof (*tbufp) * (ldcp->num_txds));
33461991Sheppo 	bzero(ldcp->txdp, sizeof (*txdp) * (ldcp->num_txds));
33471991Sheppo 
33487529SSriharsha.Basavapatna@Sun.COM 	/*
33497529SSriharsha.Basavapatna@Sun.COM 	 * In order to ensure that the number of ldc cookies per descriptor is
33507529SSriharsha.Basavapatna@Sun.COM 	 * limited to be within the default MAX_COOKIES (2), we take the steps
33517529SSriharsha.Basavapatna@Sun.COM 	 * outlined below:
33527529SSriharsha.Basavapatna@Sun.COM 	 *
33537529SSriharsha.Basavapatna@Sun.COM 	 * Align the entire data buffer area to 8K and carve out per descriptor
33547529SSriharsha.Basavapatna@Sun.COM 	 * data buffers starting from this 8K aligned base address.
33557529SSriharsha.Basavapatna@Sun.COM 	 *
33567529SSriharsha.Basavapatna@Sun.COM 	 * We round up the mtu specified to be a multiple of 2K or 4K.
33577529SSriharsha.Basavapatna@Sun.COM 	 * For sizes up to 12K we round up the size to the next 2K.
33587529SSriharsha.Basavapatna@Sun.COM 	 * For sizes > 12K we round up to the next 4K (otherwise sizes such as
33597529SSriharsha.Basavapatna@Sun.COM 	 * 14K could end up needing 3 cookies, with the buffer spread across
33607529SSriharsha.Basavapatna@Sun.COM 	 * 3 8K pages:  8K+6K, 2K+8K+2K, 6K+8K, ...).
33617529SSriharsha.Basavapatna@Sun.COM 	 */
33626419Ssb155480 	data_sz = vgenp->max_frame_size + VNET_IPALIGN + VNET_LDCALIGN;
33637529SSriharsha.Basavapatna@Sun.COM 	if (data_sz <= VNET_12K) {
33647529SSriharsha.Basavapatna@Sun.COM 		data_sz = VNET_ROUNDUP_2K(data_sz);
33657529SSriharsha.Basavapatna@Sun.COM 	} else {
33667529SSriharsha.Basavapatna@Sun.COM 		data_sz = VNET_ROUNDUP_4K(data_sz);
33677529SSriharsha.Basavapatna@Sun.COM 	}
33687529SSriharsha.Basavapatna@Sun.COM 
33697529SSriharsha.Basavapatna@Sun.COM 	/* allocate extra 8K bytes for alignment */
33707529SSriharsha.Basavapatna@Sun.COM 	ldcp->tx_data_sz = (data_sz * ldcp->num_txds) + VNET_8K;
33716419Ssb155480 	datap = kmem_zalloc(ldcp->tx_data_sz, KM_SLEEP);
33722109Slm66018 	ldcp->tx_datap = datap;
33732109Slm66018 
33747529SSriharsha.Basavapatna@Sun.COM 
33757529SSriharsha.Basavapatna@Sun.COM 	/* align the starting address of the data area to 8K */
33767529SSriharsha.Basavapatna@Sun.COM 	datap = (caddr_t)VNET_ROUNDUP_8K((uintptr_t)datap);
33777529SSriharsha.Basavapatna@Sun.COM 
33781991Sheppo 	/*
33792109Slm66018 	 * for each private descriptor, allocate a ldc mem_handle which is
33801991Sheppo 	 * required to map the data during transmit, set the flags
33811991Sheppo 	 * to free (available for use by transmit routine).
33821991Sheppo 	 */
33831991Sheppo 
33841991Sheppo 	for (i = 0; i < ldcp->num_txds; i++) {
33852109Slm66018 
33861991Sheppo 		tbufp = &(ldcp->tbufp[i]);
33871991Sheppo 		rv = ldc_mem_alloc_handle(ldcp->ldc_handle,
33884650Sraghuram 		    &(tbufp->memhandle));
33891991Sheppo 		if (rv) {
33901991Sheppo 			tbufp->memhandle = 0;
33911991Sheppo 			goto init_tbufs_failed;
33921991Sheppo 		}
33932109Slm66018 
33942109Slm66018 		/*
33952109Slm66018 		 * bind ldc memhandle to the corresponding transmit buffer.
33962109Slm66018 		 */
33972109Slm66018 		ci = ncookies = 0;
33982109Slm66018 		rv = ldc_mem_bind_handle(tbufp->memhandle,
33996419Ssb155480 		    (caddr_t)datap, data_sz, LDC_SHADOW_MAP,
34002109Slm66018 		    LDC_MEM_R, &(tbufp->memcookie[ci]), &ncookies);
34012109Slm66018 		if (rv != 0) {
34022109Slm66018 			goto init_tbufs_failed;
34032109Slm66018 		}
34042109Slm66018 
34052109Slm66018 		/*
34062109Slm66018 		 * successful in binding the handle to tx data buffer.
34072109Slm66018 		 * set datap in the private descr to this buffer.
34082109Slm66018 		 */
34092109Slm66018 		tbufp->datap = datap;
34102109Slm66018 
34112109Slm66018 		if ((ncookies == 0) ||
34124650Sraghuram 		    (ncookies > MAX_COOKIES)) {
34132109Slm66018 			goto init_tbufs_failed;
34142109Slm66018 		}
34152109Slm66018 
34162109Slm66018 		for (ci = 1; ci < ncookies; ci++) {
34172109Slm66018 			rv = ldc_mem_nextcookie(tbufp->memhandle,
34184650Sraghuram 			    &(tbufp->memcookie[ci]));
34192109Slm66018 			if (rv != 0) {
34202109Slm66018 				goto init_tbufs_failed;
34212109Slm66018 			}
34222109Slm66018 		}
34232109Slm66018 
34242109Slm66018 		tbufp->ncookies = ncookies;
34256419Ssb155480 		datap += data_sz;
34262109Slm66018 
34271991Sheppo 		tbufp->flags = VGEN_PRIV_DESC_FREE;
34281991Sheppo 		txdp = &(ldcp->txdp[i]);
34291991Sheppo 		hdrp = &txdp->hdr;
34301991Sheppo 		hdrp->dstate = VIO_DESC_FREE;
34311991Sheppo 		hdrp->ack = B_FALSE;
34321991Sheppo 		tbufp->descp = txdp;
34332109Slm66018 
34341991Sheppo 	}
34351991Sheppo 
34361991Sheppo 	/* reset tbuf walking pointers */
34371991Sheppo 	ldcp->next_tbufp = ldcp->tbufp;
34381991Sheppo 	ldcp->cur_tbufp = ldcp->tbufp;
34391991Sheppo 
34401991Sheppo 	/* initialize tx seqnum and index */
34411991Sheppo 	ldcp->next_txseq = VNET_ISS;
34421991Sheppo 	ldcp->next_txi = 0;
34431991Sheppo 
34442336Snarayan 	ldcp->resched_peer = B_TRUE;
34454647Sraghuram 	ldcp->resched_peer_txi = 0;
34462336Snarayan 
34471991Sheppo 	return (DDI_SUCCESS);
34481991Sheppo 
34491991Sheppo init_tbufs_failed:;
34501991Sheppo 	vgen_uninit_tbufs(ldcp);
34511991Sheppo 	return (DDI_FAILURE);
34521991Sheppo }
34531991Sheppo 
34541991Sheppo /* Uninitialize transmit buffer ring for the channel */
34551991Sheppo static void
34561991Sheppo vgen_uninit_tbufs(vgen_ldc_t *ldcp)
34571991Sheppo {
34581991Sheppo 	vgen_private_desc_t	*tbufp = ldcp->tbufp;
34591991Sheppo 	int 			i;
34601991Sheppo 
34611991Sheppo 	/* for each tbuf (priv_desc), free ldc mem_handle */
34621991Sheppo 	for (i = 0; i < ldcp->num_txds; i++) {
34631991Sheppo 
34641991Sheppo 		tbufp = &(ldcp->tbufp[i]);
34651991Sheppo 
34662109Slm66018 		if (tbufp->datap) { /* if bound to a ldc memhandle */
34671991Sheppo 			(void) ldc_mem_unbind_handle(tbufp->memhandle);
34682109Slm66018 			tbufp->datap = NULL;
34691991Sheppo 		}
34701991Sheppo 		if (tbufp->memhandle) {
34711991Sheppo 			(void) ldc_mem_free_handle(tbufp->memhandle);
34721991Sheppo 			tbufp->memhandle = 0;
34731991Sheppo 		}
34741991Sheppo 	}
34751991Sheppo 
34762109Slm66018 	if (ldcp->tx_datap) {
34772109Slm66018 		/* prealloc'd tx data buffer */
34786419Ssb155480 		kmem_free(ldcp->tx_datap, ldcp->tx_data_sz);
34792109Slm66018 		ldcp->tx_datap = NULL;
34806419Ssb155480 		ldcp->tx_data_sz = 0;
34812109Slm66018 	}
34822109Slm66018 
34832748Slm66018 	bzero(ldcp->tbufp, sizeof (vgen_private_desc_t) * (ldcp->num_txds));
34842748Slm66018 	bzero(ldcp->txdp, sizeof (vnet_public_desc_t) * (ldcp->num_txds));
34851991Sheppo }
34861991Sheppo 
34871991Sheppo /* clobber tx descriptor ring */
34881991Sheppo static void
34891991Sheppo vgen_clobber_tbufs(vgen_ldc_t *ldcp)
34901991Sheppo {
34911991Sheppo 	vnet_public_desc_t	*txdp;
34921991Sheppo 	vgen_private_desc_t	*tbufp;
34934647Sraghuram 	vio_dring_entry_hdr_t	*hdrp;
34944647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
34951991Sheppo 	int i;
34961991Sheppo #ifdef DEBUG
34971991Sheppo 	int ndone = 0;
34981991Sheppo #endif
34991991Sheppo 
35001991Sheppo 	for (i = 0; i < ldcp->num_txds; i++) {
35011991Sheppo 
35021991Sheppo 		tbufp = &(ldcp->tbufp[i]);
35031991Sheppo 		txdp = tbufp->descp;
35041991Sheppo 		hdrp = &txdp->hdr;
35051991Sheppo 
35061991Sheppo 		if (tbufp->flags & VGEN_PRIV_DESC_BUSY) {
35071991Sheppo 			tbufp->flags = VGEN_PRIV_DESC_FREE;
35081991Sheppo #ifdef DEBUG
35091991Sheppo 			if (hdrp->dstate == VIO_DESC_DONE)
35101991Sheppo 				ndone++;
35111991Sheppo #endif
35121991Sheppo 			hdrp->dstate = VIO_DESC_FREE;
35131991Sheppo 			hdrp->ack = B_FALSE;
35141991Sheppo 		}
35151991Sheppo 	}
35161991Sheppo 	/* reset tbuf walking pointers */
35171991Sheppo 	ldcp->next_tbufp = ldcp->tbufp;
35181991Sheppo 	ldcp->cur_tbufp = ldcp->tbufp;
35191991Sheppo 
35201991Sheppo 	/* reset tx seqnum and index */
35211991Sheppo 	ldcp->next_txseq = VNET_ISS;
35221991Sheppo 	ldcp->next_txi = 0;
35232336Snarayan 
35242336Snarayan 	ldcp->resched_peer = B_TRUE;
35254647Sraghuram 	ldcp->resched_peer_txi = 0;
35264647Sraghuram 
35274647Sraghuram 	DBG2(vgenp, ldcp, "num descrs done (%d)\n", ndone);
35281991Sheppo }
35291991Sheppo 
35301991Sheppo /* clobber receive descriptor ring */
35311991Sheppo static void
35321991Sheppo vgen_clobber_rxds(vgen_ldc_t *ldcp)
35331991Sheppo {
35341991Sheppo 	ldcp->rx_dhandle = 0;
35351991Sheppo 	bzero(&ldcp->rx_dcookie, sizeof (ldcp->rx_dcookie));
35361991Sheppo 	ldcp->rxdp = NULL;
35371991Sheppo 	ldcp->next_rxi = 0;
35381991Sheppo 	ldcp->num_rxds = 0;
35391991Sheppo 	ldcp->next_rxseq = VNET_ISS;
35401991Sheppo }
35411991Sheppo 
35421991Sheppo /* initialize receive descriptor ring */
35431991Sheppo static int
35441991Sheppo vgen_init_rxds(vgen_ldc_t *ldcp, uint32_t num_desc, uint32_t desc_size,
35451991Sheppo 	ldc_mem_cookie_t *dcookie, uint32_t ncookies)
35461991Sheppo {
35471991Sheppo 	int rv;
35481991Sheppo 	ldc_mem_info_t minfo;
35491991Sheppo 
35501991Sheppo 	rv = ldc_mem_dring_map(ldcp->ldc_handle, dcookie, ncookies, num_desc,
35516845Sha137994 	    desc_size, LDC_DIRECT_MAP, &(ldcp->rx_dhandle));
35521991Sheppo 	if (rv != 0) {
35531991Sheppo 		return (DDI_FAILURE);
35541991Sheppo 	}
35551991Sheppo 
35561991Sheppo 	/*
35571991Sheppo 	 * sucessfully mapped, now try to
35581991Sheppo 	 * get info about the mapped dring
35591991Sheppo 	 */
35601991Sheppo 	rv = ldc_mem_dring_info(ldcp->rx_dhandle, &minfo);
35611991Sheppo 	if (rv != 0) {
35621991Sheppo 		(void) ldc_mem_dring_unmap(ldcp->rx_dhandle);
35631991Sheppo 		return (DDI_FAILURE);
35641991Sheppo 	}
35651991Sheppo 
35661991Sheppo 	/*
35671991Sheppo 	 * save ring address, number of descriptors.
35681991Sheppo 	 */
35691991Sheppo 	ldcp->rxdp = (vnet_public_desc_t *)(minfo.vaddr);
35701991Sheppo 	bcopy(dcookie, &(ldcp->rx_dcookie), sizeof (*dcookie));
35711991Sheppo 	ldcp->num_rxdcookies = ncookies;
35721991Sheppo 	ldcp->num_rxds = num_desc;
35731991Sheppo 	ldcp->next_rxi = 0;
35741991Sheppo 	ldcp->next_rxseq = VNET_ISS;
35756845Sha137994 	ldcp->dring_mtype = minfo.mtype;
35761991Sheppo 
35771991Sheppo 	return (DDI_SUCCESS);
35781991Sheppo }
35791991Sheppo 
35801991Sheppo /* get channel statistics */
35811991Sheppo static uint64_t
35822311Sseb vgen_ldc_stat(vgen_ldc_t *ldcp, uint_t stat)
35831991Sheppo {
35841991Sheppo 	vgen_stats_t *statsp;
35851991Sheppo 	uint64_t val;
35861991Sheppo 
35871991Sheppo 	val = 0;
35885373Sraghuram 	statsp = &ldcp->stats;
35891991Sheppo 	switch (stat) {
35901991Sheppo 
35911991Sheppo 	case MAC_STAT_MULTIRCV:
35921991Sheppo 		val = statsp->multircv;
35931991Sheppo 		break;
35941991Sheppo 
35951991Sheppo 	case MAC_STAT_BRDCSTRCV:
35961991Sheppo 		val = statsp->brdcstrcv;
35971991Sheppo 		break;
35981991Sheppo 
35991991Sheppo 	case MAC_STAT_MULTIXMT:
36001991Sheppo 		val = statsp->multixmt;
36011991Sheppo 		break;
36021991Sheppo 
36031991Sheppo 	case MAC_STAT_BRDCSTXMT:
36041991Sheppo 		val = statsp->brdcstxmt;
36051991Sheppo 		break;
36061991Sheppo 
36071991Sheppo 	case MAC_STAT_NORCVBUF:
36081991Sheppo 		val = statsp->norcvbuf;
36091991Sheppo 		break;
36101991Sheppo 
36111991Sheppo 	case MAC_STAT_IERRORS:
36121991Sheppo 		val = statsp->ierrors;
36131991Sheppo 		break;
36141991Sheppo 
36151991Sheppo 	case MAC_STAT_NOXMTBUF:
36161991Sheppo 		val = statsp->noxmtbuf;
36171991Sheppo 		break;
36181991Sheppo 
36191991Sheppo 	case MAC_STAT_OERRORS:
36201991Sheppo 		val = statsp->oerrors;
36211991Sheppo 		break;
36221991Sheppo 
36231991Sheppo 	case MAC_STAT_COLLISIONS:
36241991Sheppo 		break;
36251991Sheppo 
36261991Sheppo 	case MAC_STAT_RBYTES:
36271991Sheppo 		val = statsp->rbytes;
36281991Sheppo 		break;
36291991Sheppo 
36301991Sheppo 	case MAC_STAT_IPACKETS:
36311991Sheppo 		val = statsp->ipackets;
36321991Sheppo 		break;
36331991Sheppo 
36341991Sheppo 	case MAC_STAT_OBYTES:
36351991Sheppo 		val = statsp->obytes;
36361991Sheppo 		break;
36371991Sheppo 
36381991Sheppo 	case MAC_STAT_OPACKETS:
36391991Sheppo 		val = statsp->opackets;
36401991Sheppo 		break;
36411991Sheppo 
36421991Sheppo 	/* stats not relevant to ldc, return 0 */
36431991Sheppo 	case MAC_STAT_IFSPEED:
36442311Sseb 	case ETHER_STAT_ALIGN_ERRORS:
36452311Sseb 	case ETHER_STAT_FCS_ERRORS:
36462311Sseb 	case ETHER_STAT_FIRST_COLLISIONS:
36472311Sseb 	case ETHER_STAT_MULTI_COLLISIONS:
36482311Sseb 	case ETHER_STAT_DEFER_XMTS:
36492311Sseb 	case ETHER_STAT_TX_LATE_COLLISIONS:
36502311Sseb 	case ETHER_STAT_EX_COLLISIONS:
36512311Sseb 	case ETHER_STAT_MACXMT_ERRORS:
36522311Sseb 	case ETHER_STAT_CARRIER_ERRORS:
36532311Sseb 	case ETHER_STAT_TOOLONG_ERRORS:
36542311Sseb 	case ETHER_STAT_XCVR_ADDR:
36552311Sseb 	case ETHER_STAT_XCVR_ID:
36562311Sseb 	case ETHER_STAT_XCVR_INUSE:
36572311Sseb 	case ETHER_STAT_CAP_1000FDX:
36582311Sseb 	case ETHER_STAT_CAP_1000HDX:
36592311Sseb 	case ETHER_STAT_CAP_100FDX:
36602311Sseb 	case ETHER_STAT_CAP_100HDX:
36612311Sseb 	case ETHER_STAT_CAP_10FDX:
36622311Sseb 	case ETHER_STAT_CAP_10HDX:
36632311Sseb 	case ETHER_STAT_CAP_ASMPAUSE:
36642311Sseb 	case ETHER_STAT_CAP_PAUSE:
36652311Sseb 	case ETHER_STAT_CAP_AUTONEG:
36662311Sseb 	case ETHER_STAT_ADV_CAP_1000FDX:
36672311Sseb 	case ETHER_STAT_ADV_CAP_1000HDX:
36682311Sseb 	case ETHER_STAT_ADV_CAP_100FDX:
36692311Sseb 	case ETHER_STAT_ADV_CAP_100HDX:
36702311Sseb 	case ETHER_STAT_ADV_CAP_10FDX:
36712311Sseb 	case ETHER_STAT_ADV_CAP_10HDX:
36722311Sseb 	case ETHER_STAT_ADV_CAP_ASMPAUSE:
36732311Sseb 	case ETHER_STAT_ADV_CAP_PAUSE:
36742311Sseb 	case ETHER_STAT_ADV_CAP_AUTONEG:
36752311Sseb 	case ETHER_STAT_LP_CAP_1000FDX:
36762311Sseb 	case ETHER_STAT_LP_CAP_1000HDX:
36772311Sseb 	case ETHER_STAT_LP_CAP_100FDX:
36782311Sseb 	case ETHER_STAT_LP_CAP_100HDX:
36792311Sseb 	case ETHER_STAT_LP_CAP_10FDX:
36802311Sseb 	case ETHER_STAT_LP_CAP_10HDX:
36812311Sseb 	case ETHER_STAT_LP_CAP_ASMPAUSE:
36822311Sseb 	case ETHER_STAT_LP_CAP_PAUSE:
36832311Sseb 	case ETHER_STAT_LP_CAP_AUTONEG:
36842311Sseb 	case ETHER_STAT_LINK_ASMPAUSE:
36852311Sseb 	case ETHER_STAT_LINK_PAUSE:
36862311Sseb 	case ETHER_STAT_LINK_AUTONEG:
36872311Sseb 	case ETHER_STAT_LINK_DUPLEX:
36881991Sheppo 	default:
36891991Sheppo 		val = 0;
36901991Sheppo 		break;
36911991Sheppo 
36921991Sheppo 	}
36931991Sheppo 	return (val);
36941991Sheppo }
36951991Sheppo 
36962793Slm66018 /*
36976495Sspeer  * LDC channel is UP, start handshake process with peer.
36982793Slm66018  */
36992793Slm66018 static void
37006495Sspeer vgen_handle_evt_up(vgen_ldc_t *ldcp)
37012793Slm66018 {
37022793Slm66018 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
37034647Sraghuram 
37044647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
37052793Slm66018 
37062793Slm66018 	ASSERT(MUTEX_HELD(&ldcp->cblock));
37072793Slm66018 
37082793Slm66018 	if (ldcp->portp != vgenp->vsw_portp) {
37092793Slm66018 		/*
37106495Sspeer 		 * As the channel is up, use this port from now on.
37112793Slm66018 		 */
37126495Sspeer 		(void) atomic_swap_32(&ldcp->portp->use_vsw_port, B_FALSE);
37132793Slm66018 	}
37142793Slm66018 
37152793Slm66018 	/* Initialize local session id */
37162793Slm66018 	ldcp->local_sid = ddi_get_lbolt();
37172793Slm66018 
37182793Slm66018 	/* clear peer session id */
37192793Slm66018 	ldcp->peer_sid = 0;
37202793Slm66018 	ldcp->hretries = 0;
37212793Slm66018 
37222793Slm66018 	if (ldcp->hphase != VH_PHASE0) {
37232793Slm66018 		vgen_handshake_reset(ldcp);
37242793Slm66018 	}
37252793Slm66018 
37262793Slm66018 	/* Initiate Handshake process with peer ldc endpoint */
37272793Slm66018 	vgen_handshake(vh_nextphase(ldcp));
37282793Slm66018 
37294647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
37302793Slm66018 }
37312793Slm66018 
37322793Slm66018 /*
37332793Slm66018  * LDC channel is Reset, terminate connection with peer and try to
37342793Slm66018  * bring the channel up again.
37352793Slm66018  */
37362793Slm66018 static void
37376495Sspeer vgen_handle_evt_reset(vgen_ldc_t *ldcp)
37382793Slm66018 {
37392793Slm66018 	ldc_status_t istatus;
37402793Slm66018 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
37412793Slm66018 	int	rv;
37422793Slm66018 
37434647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
37442793Slm66018 
37452793Slm66018 	ASSERT(MUTEX_HELD(&ldcp->cblock));
37462793Slm66018 
37472793Slm66018 	if ((ldcp->portp != vgenp->vsw_portp) &&
37484650Sraghuram 	    (vgenp->vsw_portp != NULL)) {
37492793Slm66018 		/*
37506495Sspeer 		 * As the channel is down, use the switch port until
37516495Sspeer 		 * the channel becomes ready to be used.
37522793Slm66018 		 */
37536495Sspeer 		(void) atomic_swap_32(&ldcp->portp->use_vsw_port, B_TRUE);
37546495Sspeer 	}
37556495Sspeer 
37566495Sspeer 	if (vgenp->vsw_portp == ldcp->portp) {
37576495Sspeer 		vio_net_report_err_t rep_err =
37586495Sspeer 		    ldcp->portp->vcb.vio_net_report_err;
37596495Sspeer 
37606495Sspeer 		/* Post a reset message */
37616495Sspeer 		rep_err(ldcp->portp->vhp, VIO_NET_RES_DOWN);
37622793Slm66018 	}
37632793Slm66018 
37642793Slm66018 	if (ldcp->hphase != VH_PHASE0) {
37652793Slm66018 		vgen_handshake_reset(ldcp);
37662793Slm66018 	}
37672793Slm66018 
37682793Slm66018 	/* try to bring the channel up */
37692793Slm66018 	rv = ldc_up(ldcp->ldc_handle);
37702793Slm66018 	if (rv != 0) {
37714647Sraghuram 		DWARN(vgenp, ldcp, "ldc_up err rv(%d)\n", rv);
37722793Slm66018 	}
37732793Slm66018 
37742793Slm66018 	if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
37754647Sraghuram 		DWARN(vgenp, ldcp, "ldc_status err\n");
37762793Slm66018 	} else {
37772793Slm66018 		ldcp->ldc_status = istatus;
37782793Slm66018 	}
37792793Slm66018 
37802793Slm66018 	/* if channel is already UP - restart handshake */
37812793Slm66018 	if (ldcp->ldc_status == LDC_UP) {
37826495Sspeer 		vgen_handle_evt_up(ldcp);
37832793Slm66018 	}
37842793Slm66018 
37854647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
37862793Slm66018 }
37872793Slm66018 
37881991Sheppo /* Interrupt handler for the channel */
37891991Sheppo static uint_t
37901991Sheppo vgen_ldc_cb(uint64_t event, caddr_t arg)
37911991Sheppo {
37921991Sheppo 	_NOTE(ARGUNUSED(event))
37931991Sheppo 	vgen_ldc_t	*ldcp;
37941991Sheppo 	vgen_t		*vgenp;
37951991Sheppo 	ldc_status_t 	istatus;
37961991Sheppo 	vgen_stats_t	*statsp;
37977572SWentao.Yang@Sun.COM 	timeout_id_t	cancel_htid = 0;
37987572SWentao.Yang@Sun.COM 	uint_t		ret = LDC_SUCCESS;
37991991Sheppo 
38001991Sheppo 	ldcp = (vgen_ldc_t *)arg;
38011991Sheppo 	vgenp = LDC_TO_VGEN(ldcp);
38025373Sraghuram 	statsp = &ldcp->stats;
38031991Sheppo 
38044647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
38051991Sheppo 
38061991Sheppo 	mutex_enter(&ldcp->cblock);
38071991Sheppo 	statsp->callbacks++;
38081991Sheppo 	if ((ldcp->ldc_status == LDC_INIT) || (ldcp->ldc_handle == NULL)) {
38094647Sraghuram 		DWARN(vgenp, ldcp, "status(%d) is LDC_INIT\n",
38104647Sraghuram 		    ldcp->ldc_status);
38111991Sheppo 		mutex_exit(&ldcp->cblock);
38121991Sheppo 		return (LDC_SUCCESS);
38131991Sheppo 	}
38141991Sheppo 
38152793Slm66018 	/*
38167572SWentao.Yang@Sun.COM 	 * cache cancel_htid before the events specific
38177572SWentao.Yang@Sun.COM 	 * code may overwrite it. Do not clear ldcp->cancel_htid
38187572SWentao.Yang@Sun.COM 	 * as it is also used to indicate the timer to quit immediately.
38197572SWentao.Yang@Sun.COM 	 */
38207572SWentao.Yang@Sun.COM 	cancel_htid = ldcp->cancel_htid;
38217572SWentao.Yang@Sun.COM 
38227572SWentao.Yang@Sun.COM 	/*
38232793Slm66018 	 * NOTE: not using switch() as event could be triggered by
38242793Slm66018 	 * a state change and a read request. Also the ordering	of the
38252793Slm66018 	 * check for the event types is deliberate.
38262793Slm66018 	 */
38272793Slm66018 	if (event & LDC_EVT_UP) {
38282793Slm66018 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
38294647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
38305708Sraghuram 			/* status couldn't be determined */
38317572SWentao.Yang@Sun.COM 			ret = LDC_FAILURE;
38327572SWentao.Yang@Sun.COM 			goto ldc_cb_ret;
38332793Slm66018 		}
38345708Sraghuram 		ldcp->ldc_status = istatus;
38355708Sraghuram 		if (ldcp->ldc_status != LDC_UP) {
38365708Sraghuram 			DWARN(vgenp, ldcp, "LDC_EVT_UP received "
38375708Sraghuram 			    " but ldc status is not UP(0x%x)\n",
38385708Sraghuram 			    ldcp->ldc_status);
38395708Sraghuram 			/* spurious interrupt, return success */
38407572SWentao.Yang@Sun.COM 			goto ldc_cb_ret;
38415708Sraghuram 		}
38424647Sraghuram 		DWARN(vgenp, ldcp, "event(%lx) UP, status(%d)\n",
38434647Sraghuram 		    event, ldcp->ldc_status);
38442793Slm66018 
38456495Sspeer 		vgen_handle_evt_up(ldcp);
38462793Slm66018 
38472793Slm66018 		ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
38482793Slm66018 	}
38492793Slm66018 
38505708Sraghuram 	/* Handle RESET/DOWN before READ event */
38515708Sraghuram 	if (event & (LDC_EVT_RESET | LDC_EVT_DOWN)) {
38525708Sraghuram 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
38535708Sraghuram 			DWARN(vgenp, ldcp, "ldc_status error\n");
38545708Sraghuram 			/* status couldn't be determined */
38557572SWentao.Yang@Sun.COM 			ret = LDC_FAILURE;
38567572SWentao.Yang@Sun.COM 			goto ldc_cb_ret;
38575708Sraghuram 		}
38585708Sraghuram 		ldcp->ldc_status = istatus;
38595708Sraghuram 		DWARN(vgenp, ldcp, "event(%lx) RESET/DOWN, status(%d)\n",
38605708Sraghuram 		    event, ldcp->ldc_status);
38615708Sraghuram 
38626495Sspeer 		vgen_handle_evt_reset(ldcp);
38635708Sraghuram 
38645708Sraghuram 		/*
38655708Sraghuram 		 * As the channel is down/reset, ignore READ event
38665708Sraghuram 		 * but print a debug warning message.
38675708Sraghuram 		 */
38685708Sraghuram 		if (event & LDC_EVT_READ) {
38695708Sraghuram 			DWARN(vgenp, ldcp,
38705708Sraghuram 			    "LDC_EVT_READ set along with RESET/DOWN\n");
38715708Sraghuram 			event &= ~LDC_EVT_READ;
38725708Sraghuram 		}
38735708Sraghuram 	}
38745708Sraghuram 
38752793Slm66018 	if (event & LDC_EVT_READ) {
38764647Sraghuram 		DBG2(vgenp, ldcp, "event(%lx) READ, status(%d)\n",
38774647Sraghuram 		    event, ldcp->ldc_status);
38782793Slm66018 
38792793Slm66018 		ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
38804647Sraghuram 
38814647Sraghuram 		if (ldcp->rcv_thread != NULL) {
38824647Sraghuram 			/*
38834647Sraghuram 			 * If the receive thread is enabled, then
38844647Sraghuram 			 * wakeup the receive thread to process the
38854647Sraghuram 			 * LDC messages.
38864647Sraghuram 			 */
38874647Sraghuram 			mutex_exit(&ldcp->cblock);
38884647Sraghuram 			mutex_enter(&ldcp->rcv_thr_lock);
38894647Sraghuram 			if (!(ldcp->rcv_thr_flags & VGEN_WTHR_DATARCVD)) {
38904647Sraghuram 				ldcp->rcv_thr_flags |= VGEN_WTHR_DATARCVD;
38914647Sraghuram 				cv_signal(&ldcp->rcv_thr_cv);
38924647Sraghuram 			}
38934647Sraghuram 			mutex_exit(&ldcp->rcv_thr_lock);
38944647Sraghuram 			mutex_enter(&ldcp->cblock);
38954647Sraghuram 		} else  {
38964647Sraghuram 			vgen_handle_evt_read(ldcp);
38974647Sraghuram 		}
38982793Slm66018 	}
38997572SWentao.Yang@Sun.COM 
39007572SWentao.Yang@Sun.COM ldc_cb_ret:
39017572SWentao.Yang@Sun.COM 	/*
39027572SWentao.Yang@Sun.COM 	 * Check to see if the status of cancel_htid has
39037572SWentao.Yang@Sun.COM 	 * changed. If another timer needs to be cancelled,
39047572SWentao.Yang@Sun.COM 	 * then let the next callback to clear it.
39057572SWentao.Yang@Sun.COM 	 */
39067572SWentao.Yang@Sun.COM 	if (cancel_htid == 0) {
39077572SWentao.Yang@Sun.COM 		cancel_htid = ldcp->cancel_htid;
39087572SWentao.Yang@Sun.COM 	}
39092793Slm66018 	mutex_exit(&ldcp->cblock);
39104647Sraghuram 
39117572SWentao.Yang@Sun.COM 	if (cancel_htid) {
39124647Sraghuram 		/*
39134647Sraghuram 		 * Cancel handshake timer.
39144647Sraghuram 		 * untimeout(9F) will not return until the pending callback is
39154647Sraghuram 		 * cancelled or has run. No problems will result from calling
39164647Sraghuram 		 * untimeout if the handler has already completed.
39174647Sraghuram 		 * If the timeout handler did run, then it would just
39184647Sraghuram 		 * return as cancel_htid is set.
39194647Sraghuram 		 */
39207572SWentao.Yang@Sun.COM 		DBG2(vgenp, ldcp, "calling cance_htid =0x%X \n", cancel_htid);
39217572SWentao.Yang@Sun.COM 		(void) untimeout(cancel_htid);
39227572SWentao.Yang@Sun.COM 		mutex_enter(&ldcp->cblock);
39237572SWentao.Yang@Sun.COM 		/* clear it only if its the same as the one we cancelled */
39247572SWentao.Yang@Sun.COM 		if (ldcp->cancel_htid == cancel_htid) {
39257572SWentao.Yang@Sun.COM 			ldcp->cancel_htid = 0;
39267572SWentao.Yang@Sun.COM 		}
39277572SWentao.Yang@Sun.COM 		mutex_exit(&ldcp->cblock);
39284647Sraghuram 	}
39294647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
39307572SWentao.Yang@Sun.COM 	return (ret);
39314647Sraghuram }
39324647Sraghuram 
39334647Sraghuram static void
39344647Sraghuram vgen_handle_evt_read(vgen_ldc_t *ldcp)
39354647Sraghuram {
39364647Sraghuram 	int		rv;
39375935Ssb155480 	uint64_t	*ldcmsg;
39384647Sraghuram 	size_t		msglen;
39394647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
39404647Sraghuram 	vio_msg_tag_t	*tagp;
39414647Sraghuram 	ldc_status_t 	istatus;
39424647Sraghuram 	boolean_t 	has_data;
39434647Sraghuram 
39444647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
39454647Sraghuram 
39465935Ssb155480 	ldcmsg = ldcp->ldcmsg;
39474647Sraghuram 	/*
39484647Sraghuram 	 * If the receive thread is enabled, then the cblock
39494647Sraghuram 	 * need to be acquired here. If not, the vgen_ldc_cb()
39504647Sraghuram 	 * calls this function with cblock held already.
39514647Sraghuram 	 */
39524647Sraghuram 	if (ldcp->rcv_thread != NULL) {
39534647Sraghuram 		mutex_enter(&ldcp->cblock);
39544647Sraghuram 	} else {
39554647Sraghuram 		ASSERT(MUTEX_HELD(&ldcp->cblock));
39564647Sraghuram 	}
39574647Sraghuram 
39584647Sraghuram vgen_evt_read:
39591991Sheppo 	do {
39605935Ssb155480 		msglen = ldcp->msglen;
39615935Ssb155480 		rv = ldc_read(ldcp->ldc_handle, (caddr_t)ldcmsg, &msglen);
39621991Sheppo 
39631991Sheppo 		if (rv != 0) {
39644647Sraghuram 			DWARN(vgenp, ldcp, "err rv(%d) len(%d)\n",
39654647Sraghuram 			    rv, msglen);
39662793Slm66018 			if (rv == ECONNRESET)
39674647Sraghuram 				goto vgen_evtread_error;
39681991Sheppo 			break;
39691991Sheppo 		}
39701991Sheppo 		if (msglen == 0) {
39714647Sraghuram 			DBG2(vgenp, ldcp, "ldc_read NODATA");
39721991Sheppo 			break;
39731991Sheppo 		}
39744647Sraghuram 		DBG2(vgenp, ldcp, "ldc_read msglen(%d)", msglen);
39751991Sheppo 
39761991Sheppo 		tagp = (vio_msg_tag_t *)ldcmsg;
39771991Sheppo 
39781991Sheppo 		if (ldcp->peer_sid) {
39791991Sheppo 			/*
39801991Sheppo 			 * check sid only after we have received peer's sid
39811991Sheppo 			 * in the version negotiate msg.
39821991Sheppo 			 */
39831991Sheppo #ifdef DEBUG
39841991Sheppo 			if (vgen_hdbg & HDBG_BAD_SID) {
39851991Sheppo 				/* simulate bad sid condition */
39861991Sheppo 				tagp->vio_sid = 0;
39871991Sheppo 				vgen_hdbg &= ~(HDBG_BAD_SID);
39881991Sheppo 			}
39891991Sheppo #endif
39902793Slm66018 			rv = vgen_check_sid(ldcp, tagp);
39912793Slm66018 			if (rv != VGEN_SUCCESS) {
39921991Sheppo 				/*
39931991Sheppo 				 * If sid mismatch is detected,
39941991Sheppo 				 * reset the channel.
39951991Sheppo 				 */
39961991Sheppo 				ldcp->need_ldc_reset = B_TRUE;
39974647Sraghuram 				goto vgen_evtread_error;
39981991Sheppo 			}
39991991Sheppo 		}
40001991Sheppo 
40011991Sheppo 		switch (tagp->vio_msgtype) {
40021991Sheppo 		case VIO_TYPE_CTRL:
40032793Slm66018 			rv = vgen_handle_ctrlmsg(ldcp, tagp);
40041991Sheppo 			break;
40051991Sheppo 
40061991Sheppo 		case VIO_TYPE_DATA:
40075935Ssb155480 			rv = vgen_handle_datamsg(ldcp, tagp, msglen);
40081991Sheppo 			break;
40091991Sheppo 
40101991Sheppo 		case VIO_TYPE_ERR:
40111991Sheppo 			vgen_handle_errmsg(ldcp, tagp);
40121991Sheppo 			break;
40131991Sheppo 
40141991Sheppo 		default:
40154647Sraghuram 			DWARN(vgenp, ldcp, "Unknown VIO_TYPE(%x)\n",
40164647Sraghuram 			    tagp->vio_msgtype);
40171991Sheppo 			break;
40181991Sheppo 		}
40191991Sheppo 
40204647Sraghuram 		/*
40214647Sraghuram 		 * If an error is encountered, stop processing and
40224647Sraghuram 		 * handle the error.
40234647Sraghuram 		 */
40244647Sraghuram 		if (rv != 0) {
40254647Sraghuram 			goto vgen_evtread_error;
40262793Slm66018 		}
40272793Slm66018 
40281991Sheppo 	} while (msglen);
40291991Sheppo 
40304647Sraghuram 	/* check once more before exiting */
40314647Sraghuram 	rv = ldc_chkq(ldcp->ldc_handle, &has_data);
40324647Sraghuram 	if ((rv == 0) && (has_data == B_TRUE)) {
40334647Sraghuram 		DTRACE_PROBE(vgen_chkq);
40344647Sraghuram 		goto vgen_evt_read;
40354647Sraghuram 	}
40364647Sraghuram 
40374647Sraghuram vgen_evtread_error:
40384647Sraghuram 	if (rv == ECONNRESET) {
40394647Sraghuram 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
40404647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
40414647Sraghuram 		} else {
40424647Sraghuram 			ldcp->ldc_status = istatus;
40434647Sraghuram 		}
40446495Sspeer 		vgen_handle_evt_reset(ldcp);
40454647Sraghuram 	} else if (rv) {
40464647Sraghuram 		vgen_handshake_retry(ldcp);
40474647Sraghuram 	}
40484647Sraghuram 
40494647Sraghuram 	/*
40507572SWentao.Yang@Sun.COM 	 * If the receive thread is enabled, then cancel the
40514647Sraghuram 	 * handshake timeout here.
40524647Sraghuram 	 */
40534647Sraghuram 	if (ldcp->rcv_thread != NULL) {
40547572SWentao.Yang@Sun.COM 		timeout_id_t cancel_htid = ldcp->cancel_htid;
40557572SWentao.Yang@Sun.COM 
40564647Sraghuram 		mutex_exit(&ldcp->cblock);
40577572SWentao.Yang@Sun.COM 		if (cancel_htid) {
40584647Sraghuram 			/*
40594647Sraghuram 			 * Cancel handshake timer. untimeout(9F) will
40604647Sraghuram 			 * not return until the pending callback is cancelled
40614647Sraghuram 			 * or has run. No problems will result from calling
40624647Sraghuram 			 * untimeout if the handler has already completed.
40634647Sraghuram 			 * If the timeout handler did run, then it would just
40644647Sraghuram 			 * return as cancel_htid is set.
40654647Sraghuram 			 */
40667572SWentao.Yang@Sun.COM 			DBG2(vgenp, ldcp, "calling cance_htid =0x%X \n",
40677572SWentao.Yang@Sun.COM 			    cancel_htid);
40687572SWentao.Yang@Sun.COM 			(void) untimeout(cancel_htid);
40697572SWentao.Yang@Sun.COM 
40707572SWentao.Yang@Sun.COM 			/*
40717572SWentao.Yang@Sun.COM 			 * clear it only if its the same as the one we
40727572SWentao.Yang@Sun.COM 			 * cancelled
40737572SWentao.Yang@Sun.COM 			 */
40747572SWentao.Yang@Sun.COM 			mutex_enter(&ldcp->cblock);
40757572SWentao.Yang@Sun.COM 			if (ldcp->cancel_htid == cancel_htid) {
40767572SWentao.Yang@Sun.COM 				ldcp->cancel_htid = 0;
40777572SWentao.Yang@Sun.COM 			}
40787572SWentao.Yang@Sun.COM 			mutex_exit(&ldcp->cblock);
40794647Sraghuram 		}
40804647Sraghuram 	}
40814647Sraghuram 
40824647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
40831991Sheppo }
40841991Sheppo 
40851991Sheppo /* vgen handshake functions */
40861991Sheppo 
40871991Sheppo /* change the hphase for the channel to the next phase */
40881991Sheppo static vgen_ldc_t *
40891991Sheppo vh_nextphase(vgen_ldc_t *ldcp)
40901991Sheppo {
40911991Sheppo 	if (ldcp->hphase == VH_PHASE3) {
40921991Sheppo 		ldcp->hphase = VH_DONE;
40931991Sheppo 	} else {
40941991Sheppo 		ldcp->hphase++;
40951991Sheppo 	}
40961991Sheppo 	return (ldcp);
40971991Sheppo }
40981991Sheppo 
40991991Sheppo /*
41001991Sheppo  * wrapper routine to send the given message over ldc using ldc_write().
41011991Sheppo  */
41021991Sheppo static int
41031991Sheppo vgen_sendmsg(vgen_ldc_t *ldcp, caddr_t msg,  size_t msglen,
41041991Sheppo     boolean_t caller_holds_lock)
41051991Sheppo {
41065935Ssb155480 	int			rv;
41075935Ssb155480 	size_t			len;
41085935Ssb155480 	uint32_t		retries = 0;
41095935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
41105935Ssb155480 	vio_msg_tag_t		*tagp = (vio_msg_tag_t *)msg;
41115935Ssb155480 	vio_dring_msg_t		*dmsg;
41125935Ssb155480 	vio_raw_data_msg_t	*rmsg;
41135935Ssb155480 	boolean_t		data_msg = B_FALSE;
41141991Sheppo 
41151991Sheppo 	len = msglen;
41161991Sheppo 	if ((len == 0) || (msg == NULL))
41171991Sheppo 		return (VGEN_FAILURE);
41181991Sheppo 
41191991Sheppo 	if (!caller_holds_lock) {
41204647Sraghuram 		mutex_enter(&ldcp->wrlock);
41211991Sheppo 	}
41221991Sheppo 
41235935Ssb155480 	if (tagp->vio_subtype == VIO_SUBTYPE_INFO) {
41245935Ssb155480 		if (tagp->vio_subtype_env == VIO_DRING_DATA) {
41255935Ssb155480 			dmsg = (vio_dring_msg_t *)tagp;
41265935Ssb155480 			dmsg->seq_num = ldcp->next_txseq;
41275935Ssb155480 			data_msg = B_TRUE;
41285935Ssb155480 		} else if (tagp->vio_subtype_env == VIO_PKT_DATA) {
41295935Ssb155480 			rmsg = (vio_raw_data_msg_t *)tagp;
41305935Ssb155480 			rmsg->seq_num = ldcp->next_txseq;
41315935Ssb155480 			data_msg = B_TRUE;
41325935Ssb155480 		}
41335935Ssb155480 	}
41345935Ssb155480 
41351991Sheppo 	do {
41361991Sheppo 		len = msglen;
41371991Sheppo 		rv = ldc_write(ldcp->ldc_handle, (caddr_t)msg, &len);
41381991Sheppo 		if (retries++ >= vgen_ldcwr_retries)
41391991Sheppo 			break;
41401991Sheppo 	} while (rv == EWOULDBLOCK);
41411991Sheppo 
41425935Ssb155480 	if (rv == 0 && data_msg == B_TRUE) {
41435935Ssb155480 		ldcp->next_txseq++;
41445935Ssb155480 	}
41455935Ssb155480 
41461991Sheppo 	if (!caller_holds_lock) {
41474647Sraghuram 		mutex_exit(&ldcp->wrlock);
41481991Sheppo 	}
41491991Sheppo 
41502793Slm66018 	if (rv != 0) {
41514647Sraghuram 		DWARN(vgenp, ldcp, "ldc_write failed: rv(%d)\n",
41524647Sraghuram 		    rv, msglen);
41532793Slm66018 		return (rv);
41542793Slm66018 	}
41552793Slm66018 
41562793Slm66018 	if (len != msglen) {
41574647Sraghuram 		DWARN(vgenp, ldcp, "ldc_write failed: rv(%d) msglen (%d)\n",
41584647Sraghuram 		    rv, msglen);
41591991Sheppo 		return (VGEN_FAILURE);
41601991Sheppo 	}
41612793Slm66018 
41621991Sheppo 	return (VGEN_SUCCESS);
41631991Sheppo }
41641991Sheppo 
41651991Sheppo /* send version negotiate message to the peer over ldc */
41661991Sheppo static int
41671991Sheppo vgen_send_version_negotiate(vgen_ldc_t *ldcp)
41681991Sheppo {
41694647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
41701991Sheppo 	vio_ver_msg_t	vermsg;
41711991Sheppo 	vio_msg_tag_t	*tagp = &vermsg.tag;
41721991Sheppo 	int		rv;
41731991Sheppo 
41741991Sheppo 	bzero(&vermsg, sizeof (vermsg));
41751991Sheppo 
41761991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
41771991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
41781991Sheppo 	tagp->vio_subtype_env = VIO_VER_INFO;
41791991Sheppo 	tagp->vio_sid = ldcp->local_sid;
41801991Sheppo 
41811991Sheppo 	/* get version msg payload from ldcp->local */
41821991Sheppo 	vermsg.ver_major = ldcp->local_hparams.ver_major;
41831991Sheppo 	vermsg.ver_minor = ldcp->local_hparams.ver_minor;
41841991Sheppo 	vermsg.dev_class = ldcp->local_hparams.dev_class;
41851991Sheppo 
41861991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (vermsg), B_FALSE);
41871991Sheppo 	if (rv != VGEN_SUCCESS) {
41884647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
41892793Slm66018 		return (rv);
41901991Sheppo 	}
41911991Sheppo 
41921991Sheppo 	ldcp->hstate |= VER_INFO_SENT;
41934647Sraghuram 	DBG2(vgenp, ldcp, "VER_INFO_SENT ver(%d,%d)\n",
41944647Sraghuram 	    vermsg.ver_major, vermsg.ver_minor);
41951991Sheppo 
41961991Sheppo 	return (VGEN_SUCCESS);
41971991Sheppo }
41981991Sheppo 
41991991Sheppo /* send attr info message to the peer over ldc */
42001991Sheppo static int
42011991Sheppo vgen_send_attr_info(vgen_ldc_t *ldcp)
42021991Sheppo {
42034647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
42041991Sheppo 	vnet_attr_msg_t	attrmsg;
42051991Sheppo 	vio_msg_tag_t	*tagp = &attrmsg.tag;
42061991Sheppo 	int		rv;
42071991Sheppo 
42081991Sheppo 	bzero(&attrmsg, sizeof (attrmsg));
42091991Sheppo 
42101991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
42111991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
42121991Sheppo 	tagp->vio_subtype_env = VIO_ATTR_INFO;
42131991Sheppo 	tagp->vio_sid = ldcp->local_sid;
42141991Sheppo 
42151991Sheppo 	/* get attr msg payload from ldcp->local */
42161991Sheppo 	attrmsg.mtu = ldcp->local_hparams.mtu;
42171991Sheppo 	attrmsg.addr = ldcp->local_hparams.addr;
42181991Sheppo 	attrmsg.addr_type = ldcp->local_hparams.addr_type;
42191991Sheppo 	attrmsg.xfer_mode = ldcp->local_hparams.xfer_mode;
42201991Sheppo 	attrmsg.ack_freq = ldcp->local_hparams.ack_freq;
42211991Sheppo 
42221991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (attrmsg), B_FALSE);
42231991Sheppo 	if (rv != VGEN_SUCCESS) {
42244647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
42252793Slm66018 		return (rv);
42261991Sheppo 	}
42271991Sheppo 
42281991Sheppo 	ldcp->hstate |= ATTR_INFO_SENT;
42294647Sraghuram 	DBG2(vgenp, ldcp, "ATTR_INFO_SENT\n");
42301991Sheppo 
42311991Sheppo 	return (VGEN_SUCCESS);
42321991Sheppo }
42331991Sheppo 
42341991Sheppo /* send descriptor ring register message to the peer over ldc */
42351991Sheppo static int
42361991Sheppo vgen_send_dring_reg(vgen_ldc_t *ldcp)
42371991Sheppo {
42384647Sraghuram 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
42391991Sheppo 	vio_dring_reg_msg_t	msg;
42401991Sheppo 	vio_msg_tag_t		*tagp = &msg.tag;
42411991Sheppo 	int		rv;
42421991Sheppo 
42431991Sheppo 	bzero(&msg, sizeof (msg));
42441991Sheppo 
42451991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
42461991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
42471991Sheppo 	tagp->vio_subtype_env = VIO_DRING_REG;
42481991Sheppo 	tagp->vio_sid = ldcp->local_sid;
42491991Sheppo 
42501991Sheppo 	/* get dring info msg payload from ldcp->local */
42511991Sheppo 	bcopy(&(ldcp->local_hparams.dring_cookie), (msg.cookie),
42524650Sraghuram 	    sizeof (ldc_mem_cookie_t));
42531991Sheppo 	msg.ncookies = ldcp->local_hparams.num_dcookies;
42541991Sheppo 	msg.num_descriptors = ldcp->local_hparams.num_desc;
42551991Sheppo 	msg.descriptor_size = ldcp->local_hparams.desc_size;
42561991Sheppo 
42571991Sheppo 	/*
42581991Sheppo 	 * dring_ident is set to 0. After mapping the dring, peer sets this
42591991Sheppo 	 * value and sends it in the ack, which is saved in
42601991Sheppo 	 * vgen_handle_dring_reg().
42611991Sheppo 	 */
42621991Sheppo 	msg.dring_ident = 0;
42631991Sheppo 
42641991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (msg), B_FALSE);
42651991Sheppo 	if (rv != VGEN_SUCCESS) {
42664647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
42672793Slm66018 		return (rv);
42681991Sheppo 	}
42691991Sheppo 
42701991Sheppo 	ldcp->hstate |= DRING_INFO_SENT;
42714647Sraghuram 	DBG2(vgenp, ldcp, "DRING_INFO_SENT \n");
42721991Sheppo 
42731991Sheppo 	return (VGEN_SUCCESS);
42741991Sheppo }
42751991Sheppo 
42761991Sheppo static int
42771991Sheppo vgen_send_rdx_info(vgen_ldc_t *ldcp)
42781991Sheppo {
42794647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
42801991Sheppo 	vio_rdx_msg_t	rdxmsg;
42811991Sheppo 	vio_msg_tag_t	*tagp = &rdxmsg.tag;
42821991Sheppo 	int		rv;
42831991Sheppo 
42841991Sheppo 	bzero(&rdxmsg, sizeof (rdxmsg));
42851991Sheppo 
42861991Sheppo 	tagp->vio_msgtype = VIO_TYPE_CTRL;
42871991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
42881991Sheppo 	tagp->vio_subtype_env = VIO_RDX;
42891991Sheppo 	tagp->vio_sid = ldcp->local_sid;
42901991Sheppo 
42911991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (rdxmsg), B_FALSE);
42921991Sheppo 	if (rv != VGEN_SUCCESS) {
42934647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
42942793Slm66018 		return (rv);
42951991Sheppo 	}
42961991Sheppo 
42971991Sheppo 	ldcp->hstate |= RDX_INFO_SENT;
42984647Sraghuram 	DBG2(vgenp, ldcp, "RDX_INFO_SENT\n");
42991991Sheppo 
43001991Sheppo 	return (VGEN_SUCCESS);
43011991Sheppo }
43021991Sheppo 
43031991Sheppo /* send descriptor ring data message to the peer over ldc */
43041991Sheppo static int
43052336Snarayan vgen_send_dring_data(vgen_ldc_t *ldcp, uint32_t start, int32_t end)
43061991Sheppo {
43074647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
43081991Sheppo 	vio_dring_msg_t	dringmsg, *msgp = &dringmsg;
43091991Sheppo 	vio_msg_tag_t	*tagp = &msgp->tag;
43105373Sraghuram 	vgen_stats_t	*statsp = &ldcp->stats;
43111991Sheppo 	int		rv;
43121991Sheppo 
43131991Sheppo 	bzero(msgp, sizeof (*msgp));
43141991Sheppo 
43151991Sheppo 	tagp->vio_msgtype = VIO_TYPE_DATA;
43161991Sheppo 	tagp->vio_subtype = VIO_SUBTYPE_INFO;
43171991Sheppo 	tagp->vio_subtype_env = VIO_DRING_DATA;
43181991Sheppo 	tagp->vio_sid = ldcp->local_sid;
43191991Sheppo 
43201991Sheppo 	msgp->dring_ident = ldcp->local_hparams.dring_ident;
43211991Sheppo 	msgp->start_idx = start;
43221991Sheppo 	msgp->end_idx = end;
43231991Sheppo 
43241991Sheppo 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (dringmsg), B_TRUE);
43251991Sheppo 	if (rv != VGEN_SUCCESS) {
43264647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
43272793Slm66018 		return (rv);
43281991Sheppo 	}
43291991Sheppo 
43305373Sraghuram 	statsp->dring_data_msgs++;
43312336Snarayan 
43324647Sraghuram 	DBG2(vgenp, ldcp, "DRING_DATA_SENT \n");
43331991Sheppo 
43341991Sheppo 	return (VGEN_SUCCESS);
43351991Sheppo }
43361991Sheppo 
43371991Sheppo /* send multicast addr info message to vsw */
43381991Sheppo static int
43391991Sheppo vgen_send_mcast_info(vgen_ldc_t *ldcp)
43401991Sheppo {
43411991Sheppo 	vnet_mcast_msg_t	mcastmsg;
43421991Sheppo 	vnet_mcast_msg_t	*msgp;
43431991Sheppo 	vio_msg_tag_t		*tagp;
43441991Sheppo 	vgen_t			*vgenp;
43451991Sheppo 	struct ether_addr	*mca;
43461991Sheppo 	int			rv;
43471991Sheppo 	int			i;
43481991Sheppo 	uint32_t		size;
43491991Sheppo 	uint32_t		mccount;
43501991Sheppo 	uint32_t		n;
43511991Sheppo 
43521991Sheppo 	msgp = &mcastmsg;
43531991Sheppo 	tagp = &msgp->tag;
43541991Sheppo 	vgenp = LDC_TO_VGEN(ldcp);
43551991Sheppo 
43561991Sheppo 	mccount = vgenp->mccount;
43571991Sheppo 	i = 0;
43581991Sheppo 
43591991Sheppo 	do {
43601991Sheppo 		tagp->vio_msgtype = VIO_TYPE_CTRL;
43611991Sheppo 		tagp->vio_subtype = VIO_SUBTYPE_INFO;
43621991Sheppo 		tagp->vio_subtype_env = VNET_MCAST_INFO;
43631991Sheppo 		tagp->vio_sid = ldcp->local_sid;
43641991Sheppo 
43651991Sheppo 		n = ((mccount >= VNET_NUM_MCAST) ? VNET_NUM_MCAST : mccount);
43661991Sheppo 		size = n * sizeof (struct ether_addr);
43671991Sheppo 
43681991Sheppo 		mca = &(vgenp->mctab[i]);
43691991Sheppo 		bcopy(mca, (msgp->mca), size);
43701991Sheppo 		msgp->set = B_TRUE;
43711991Sheppo 		msgp->count = n;
43721991Sheppo 
43731991Sheppo 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msgp),
43741991Sheppo 		    B_FALSE);
43751991Sheppo 		if (rv != VGEN_SUCCESS) {
43764647Sraghuram 			DWARN(vgenp, ldcp, "vgen_sendmsg err(%d)\n", rv);
43772793Slm66018 			return (rv);
43781991Sheppo 		}
43791991Sheppo 
43801991Sheppo 		mccount -= n;
43811991Sheppo 		i += n;
43821991Sheppo 
43831991Sheppo 	} while (mccount);
43841991Sheppo 
43851991Sheppo 	return (VGEN_SUCCESS);
43861991Sheppo }
43871991Sheppo 
43881991Sheppo /* Initiate Phase 2 of handshake */
43891991Sheppo static int
43901991Sheppo vgen_handshake_phase2(vgen_ldc_t *ldcp)
43911991Sheppo {
43921991Sheppo 	int rv;
43932793Slm66018 	uint32_t ncookies = 0;
43944647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
43954647Sraghuram 
43961991Sheppo #ifdef DEBUG
43971991Sheppo 	if (vgen_hdbg & HDBG_OUT_STATE) {
43981991Sheppo 		/* simulate out of state condition */
43991991Sheppo 		vgen_hdbg &= ~(HDBG_OUT_STATE);
44001991Sheppo 		rv = vgen_send_rdx_info(ldcp);
44011991Sheppo 		return (rv);
44021991Sheppo 	}
44031991Sheppo 	if (vgen_hdbg & HDBG_TIMEOUT) {
44041991Sheppo 		/* simulate timeout condition */
44051991Sheppo 		vgen_hdbg &= ~(HDBG_TIMEOUT);
44061991Sheppo 		return (VGEN_SUCCESS);
44071991Sheppo 	}
44081991Sheppo #endif
44092793Slm66018 	rv = vgen_send_attr_info(ldcp);
44102793Slm66018 	if (rv != VGEN_SUCCESS) {
44111991Sheppo 		return (rv);
44121991Sheppo 	}
44132793Slm66018 
44142793Slm66018 	/* Bind descriptor ring to the channel */
44152793Slm66018 	if (ldcp->num_txdcookies == 0) {
44162793Slm66018 		rv = ldc_mem_dring_bind(ldcp->ldc_handle, ldcp->tx_dhandle,
44176845Sha137994 		    LDC_DIRECT_MAP | LDC_SHADOW_MAP, LDC_MEM_RW,
44186845Sha137994 		    &ldcp->tx_dcookie, &ncookies);
44192793Slm66018 		if (rv != 0) {
44204647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_dring_bind failed "
44214650Sraghuram 			    "rv(%x)\n", rv);
44222793Slm66018 			return (rv);
44232793Slm66018 		}
44242793Slm66018 		ASSERT(ncookies == 1);
44252793Slm66018 		ldcp->num_txdcookies = ncookies;
44262793Slm66018 	}
44272793Slm66018 
44282793Slm66018 	/* update local dring_info params */
44292793Slm66018 	bcopy(&(ldcp->tx_dcookie), &(ldcp->local_hparams.dring_cookie),
44304650Sraghuram 	    sizeof (ldc_mem_cookie_t));
44312793Slm66018 	ldcp->local_hparams.num_dcookies = ldcp->num_txdcookies;
44322793Slm66018 	ldcp->local_hparams.num_desc = ldcp->num_txds;
44332793Slm66018 	ldcp->local_hparams.desc_size = sizeof (vnet_public_desc_t);
44342793Slm66018 
44352793Slm66018 	rv = vgen_send_dring_reg(ldcp);
44362793Slm66018 	if (rv != VGEN_SUCCESS) {
44371991Sheppo 		return (rv);
44381991Sheppo 	}
44391991Sheppo 
44401991Sheppo 	return (VGEN_SUCCESS);
44411991Sheppo }
44421991Sheppo 
44431991Sheppo /*
44445935Ssb155480  * Set vnet-protocol-version dependent functions based on version.
44455935Ssb155480  */
44465935Ssb155480 static void
44475935Ssb155480 vgen_set_vnet_proto_ops(vgen_ldc_t *ldcp)
44485935Ssb155480 {
44495935Ssb155480 	vgen_hparams_t	*lp = &ldcp->local_hparams;
44505935Ssb155480 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
44515935Ssb155480 
44527529SSriharsha.Basavapatna@Sun.COM 	if (VGEN_VER_GTEQ(ldcp, 1, 4)) {
44536419Ssb155480 		/*
44547529SSriharsha.Basavapatna@Sun.COM 		 * If the version negotiated with peer is >= 1.4(Jumbo Frame
44557529SSriharsha.Basavapatna@Sun.COM 		 * Support), set the mtu in our attributes to max_frame_size.
44566419Ssb155480 		 */
44576419Ssb155480 		lp->mtu = vgenp->max_frame_size;
44587529SSriharsha.Basavapatna@Sun.COM 	} else  if (VGEN_VER_EQ(ldcp, 1, 3)) {
44597529SSriharsha.Basavapatna@Sun.COM 		/*
44607529SSriharsha.Basavapatna@Sun.COM 		 * If the version negotiated with peer is == 1.3 (Vlan Tag
44617529SSriharsha.Basavapatna@Sun.COM 		 * Support) set the attr.mtu to ETHERMAX + VLAN_TAGSZ.
44627529SSriharsha.Basavapatna@Sun.COM 		 */
44637529SSriharsha.Basavapatna@Sun.COM 		lp->mtu = ETHERMAX + VLAN_TAGSZ;
44646419Ssb155480 	} else {
44656419Ssb155480 		vgen_port_t	*portp = ldcp->portp;
44666419Ssb155480 		vnet_t		*vnetp = vgenp->vnetp;
44676419Ssb155480 		/*
44686419Ssb155480 		 * Pre-1.3 peers expect max frame size of ETHERMAX.
44696419Ssb155480 		 * We can negotiate that size with those peers provided the
44706419Ssb155480 		 * following conditions are true:
44716419Ssb155480 		 * - Only pvid is defined for our peer and there are no vids.
44726419Ssb155480 		 * - pvids are equal.
44736419Ssb155480 		 * If the above conditions are true, then we can send/recv only
44746419Ssb155480 		 * untagged frames of max size ETHERMAX.
44756419Ssb155480 		 */
44767529SSriharsha.Basavapatna@Sun.COM 		if (portp->nvids == 0 && portp->pvid == vnetp->pvid) {
44776419Ssb155480 			lp->mtu = ETHERMAX;
44786419Ssb155480 		}
44796419Ssb155480 	}
44806419Ssb155480 
44816419Ssb155480 	if (VGEN_VER_GTEQ(ldcp, 1, 2)) {
44826419Ssb155480 		/* Versions >= 1.2 */
44835935Ssb155480 
44845935Ssb155480 		if (VGEN_PRI_ETH_DEFINED(vgenp)) {
44855935Ssb155480 			/*
44865935Ssb155480 			 * enable priority routines and pkt mode only if
44875935Ssb155480 			 * at least one pri-eth-type is specified in MD.
44885935Ssb155480 			 */
44895935Ssb155480 
44905935Ssb155480 			ldcp->tx = vgen_ldcsend;
44915935Ssb155480 			ldcp->rx_pktdata = vgen_handle_pkt_data;
44925935Ssb155480 
44935935Ssb155480 			/* set xfer mode for vgen_send_attr_info() */
44945935Ssb155480 			lp->xfer_mode = VIO_PKT_MODE | VIO_DRING_MODE_V1_2;
44955935Ssb155480 
44965935Ssb155480 		} else {
44975935Ssb155480 			/* no priority eth types defined in MD */
44985935Ssb155480 
44995935Ssb155480 			ldcp->tx = vgen_ldcsend_dring;
45005935Ssb155480 			ldcp->rx_pktdata = vgen_handle_pkt_data_nop;
45015935Ssb155480 
45025935Ssb155480 			/* set xfer mode for vgen_send_attr_info() */
45035935Ssb155480 			lp->xfer_mode = VIO_DRING_MODE_V1_2;
45045935Ssb155480 
45055935Ssb155480 		}
45065935Ssb155480 	} else {
45075935Ssb155480 		/* Versions prior to 1.2  */
45085935Ssb155480 
45095935Ssb155480 		vgen_reset_vnet_proto_ops(ldcp);
45105935Ssb155480 	}
45115935Ssb155480 }
45125935Ssb155480 
45135935Ssb155480 /*
45145935Ssb155480  * Reset vnet-protocol-version dependent functions to pre-v1.2.
45155935Ssb155480  */
45165935Ssb155480 static void
45175935Ssb155480 vgen_reset_vnet_proto_ops(vgen_ldc_t *ldcp)
45185935Ssb155480 {
45195935Ssb155480 	vgen_hparams_t	*lp = &ldcp->local_hparams;
45205935Ssb155480 
45215935Ssb155480 	ldcp->tx = vgen_ldcsend_dring;
45225935Ssb155480 	ldcp->rx_pktdata = vgen_handle_pkt_data_nop;
45235935Ssb155480 
45245935Ssb155480 	/* set xfer mode for vgen_send_attr_info() */
45255935Ssb155480 	lp->xfer_mode = VIO_DRING_MODE_V1_0;
45265935Ssb155480 }
45275935Ssb155480 
45286419Ssb155480 static void
45296419Ssb155480 vgen_vlan_unaware_port_reset(vgen_port_t *portp)
45306419Ssb155480 {
45316419Ssb155480 	vgen_ldclist_t	*ldclp;
45326419Ssb155480 	vgen_ldc_t	*ldcp;
45336419Ssb155480 	vgen_t		*vgenp = portp->vgenp;
45346419Ssb155480 	vnet_t		*vnetp = vgenp->vnetp;
45356419Ssb155480 
45366419Ssb155480 	ldclp = &portp->ldclist;
45376419Ssb155480 
45386419Ssb155480 	READ_ENTER(&ldclp->rwlock);
45396419Ssb155480 
45406419Ssb155480 	/*
45416419Ssb155480 	 * NOTE: for now, we will assume we have a single channel.
45426419Ssb155480 	 */
45436419Ssb155480 	if (ldclp->headp == NULL) {
45446419Ssb155480 		RW_EXIT(&ldclp->rwlock);
45456419Ssb155480 		return;
45466419Ssb155480 	}
45476419Ssb155480 	ldcp = ldclp->headp;
45486419Ssb155480 
45496419Ssb155480 	mutex_enter(&ldcp->cblock);
45506419Ssb155480 
45516419Ssb155480 	/*
45526419Ssb155480 	 * If the peer is vlan_unaware(ver < 1.3), reset channel and terminate
45536419Ssb155480 	 * the connection. See comments in vgen_set_vnet_proto_ops().
45546419Ssb155480 	 */
45556419Ssb155480 	if (ldcp->hphase == VH_DONE && VGEN_VER_LT(ldcp, 1, 3) &&
45566419Ssb155480 	    (portp->nvids != 0 || portp->pvid != vnetp->pvid)) {
45576419Ssb155480 		ldcp->need_ldc_reset = B_TRUE;
45586419Ssb155480 		vgen_handshake_retry(ldcp);
45596419Ssb155480 	}
45606419Ssb155480 
45616419Ssb155480 	mutex_exit(&ldcp->cblock);
45626419Ssb155480 
45636419Ssb155480 	RW_EXIT(&ldclp->rwlock);
45646419Ssb155480 }
45656419Ssb155480 
45666419Ssb155480 static void
45676419Ssb155480 vgen_reset_vlan_unaware_ports(vgen_t *vgenp)
45686419Ssb155480 {
45696419Ssb155480 	vgen_port_t	*portp;
45706419Ssb155480 	vgen_portlist_t	*plistp;
45716419Ssb155480 
45726419Ssb155480 	plistp = &(vgenp->vgenports);
45736419Ssb155480 	READ_ENTER(&plistp->rwlock);
45746419Ssb155480 
45756419Ssb155480 	for (portp = plistp->headp; portp != NULL; portp = portp->nextp) {
45766419Ssb155480 
45776419Ssb155480 		vgen_vlan_unaware_port_reset(portp);
45786419Ssb155480 
45796419Ssb155480 	}
45806419Ssb155480 
45816419Ssb155480 	RW_EXIT(&plistp->rwlock);
45826419Ssb155480 }
45836419Ssb155480 
45845935Ssb155480 /*
45851991Sheppo  * This function resets the handshake phase to VH_PHASE0(pre-handshake phase).
45861991Sheppo  * This can happen after a channel comes up (status: LDC_UP) or
45871991Sheppo  * when handshake gets terminated due to various conditions.
45881991Sheppo  */
45891991Sheppo static void
45901991Sheppo vgen_reset_hphase(vgen_ldc_t *ldcp)
45911991Sheppo {
45924647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
45931991Sheppo 	ldc_status_t istatus;
45942793Slm66018 	int rv;
45951991Sheppo 
45964647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
45971991Sheppo 	/* reset hstate and hphase */
45981991Sheppo 	ldcp->hstate = 0;
45991991Sheppo 	ldcp->hphase = VH_PHASE0;
46001991Sheppo 
46015935Ssb155480 	vgen_reset_vnet_proto_ops(ldcp);
46025935Ssb155480 
46033653Snarayan 	/*
46043653Snarayan 	 * Save the id of pending handshake timer in cancel_htid.
46053653Snarayan 	 * This will be checked in vgen_ldc_cb() and the handshake timer will
46063653Snarayan 	 * be cancelled after releasing cblock.
46073653Snarayan 	 */
46081991Sheppo 	if (ldcp->htid) {
46093653Snarayan 		ldcp->cancel_htid = ldcp->htid;
46101991Sheppo 		ldcp->htid = 0;
46111991Sheppo 	}
46121991Sheppo 
46131991Sheppo 	if (ldcp->local_hparams.dring_ready) {
46141991Sheppo 		ldcp->local_hparams.dring_ready = B_FALSE;
46152793Slm66018 	}
46162793Slm66018 
46172793Slm66018 	/* Unbind tx descriptor ring from the channel */
46182793Slm66018 	if (ldcp->num_txdcookies) {
46192793Slm66018 		rv = ldc_mem_dring_unbind(ldcp->tx_dhandle);
46202793Slm66018 		if (rv != 0) {
46214647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_dring_unbind failed\n");
46222793Slm66018 		}
46232793Slm66018 		ldcp->num_txdcookies = 0;
46241991Sheppo 	}
46251991Sheppo 
46261991Sheppo 	if (ldcp->peer_hparams.dring_ready) {
46271991Sheppo 		ldcp->peer_hparams.dring_ready = B_FALSE;
46281991Sheppo 		/* Unmap peer's dring */
46291991Sheppo 		(void) ldc_mem_dring_unmap(ldcp->rx_dhandle);
46301991Sheppo 		vgen_clobber_rxds(ldcp);
46311991Sheppo 	}
46321991Sheppo 
46331991Sheppo 	vgen_clobber_tbufs(ldcp);
46341991Sheppo 
46351991Sheppo 	/*
46361991Sheppo 	 * clear local handshake params and initialize.
46371991Sheppo 	 */
46381991Sheppo 	bzero(&(ldcp->local_hparams), sizeof (ldcp->local_hparams));
46391991Sheppo 
46401991Sheppo 	/* set version to the highest version supported */
46411991Sheppo 	ldcp->local_hparams.ver_major =
46424650Sraghuram 	    ldcp->vgen_versions[0].ver_major;
46431991Sheppo 	ldcp->local_hparams.ver_minor =
46444650Sraghuram 	    ldcp->vgen_versions[0].ver_minor;
46451991Sheppo 	ldcp->local_hparams.dev_class = VDEV_NETWORK;
46461991Sheppo 
46471991Sheppo 	/* set attr_info params */
46486419Ssb155480 	ldcp->local_hparams.mtu = vgenp->max_frame_size;
46491991Sheppo 	ldcp->local_hparams.addr =
46505462Swentaoy 	    vnet_macaddr_strtoul(vgenp->macaddr);
46511991Sheppo 	ldcp->local_hparams.addr_type = ADDR_TYPE_MAC;
46525935Ssb155480 	ldcp->local_hparams.xfer_mode = VIO_DRING_MODE_V1_0;
46531991Sheppo 	ldcp->local_hparams.ack_freq = 0;	/* don't need acks */
46541991Sheppo 
46551991Sheppo 	/*
46562793Slm66018 	 * Note: dring is created, but not bound yet.
46572793Slm66018 	 * local dring_info params will be updated when we bind the dring in
46582793Slm66018 	 * vgen_handshake_phase2().
46591991Sheppo 	 * dring_ident is set to 0. After mapping the dring, peer sets this
46601991Sheppo 	 * value and sends it in the ack, which is saved in
46611991Sheppo 	 * vgen_handle_dring_reg().
46621991Sheppo 	 */
46631991Sheppo 	ldcp->local_hparams.dring_ident = 0;
46641991Sheppo 
46651991Sheppo 	/* clear peer_hparams */
46661991Sheppo 	bzero(&(ldcp->peer_hparams), sizeof (ldcp->peer_hparams));
46671991Sheppo 
46681991Sheppo 	/* reset the channel if required */
46691991Sheppo 	if (ldcp->need_ldc_reset) {
46704647Sraghuram 		DWARN(vgenp, ldcp, "Doing Channel Reset...\n");
46711991Sheppo 		ldcp->need_ldc_reset = B_FALSE;
46722410Slm66018 		(void) ldc_down(ldcp->ldc_handle);
46731991Sheppo 		(void) ldc_status(ldcp->ldc_handle, &istatus);
46744647Sraghuram 		DBG2(vgenp, ldcp, "Reset Done,ldc_status(%x)\n", istatus);
46751991Sheppo 		ldcp->ldc_status = istatus;
46762793Slm66018 
46771991Sheppo 		/* clear sids */
46781991Sheppo 		ldcp->local_sid = 0;
46791991Sheppo 		ldcp->peer_sid = 0;
46802793Slm66018 
46812793Slm66018 		/* try to bring the channel up */
46822793Slm66018 		rv = ldc_up(ldcp->ldc_handle);
46832793Slm66018 		if (rv != 0) {
46844647Sraghuram 			DWARN(vgenp, ldcp, "ldc_up err rv(%d)\n", rv);
46852793Slm66018 		}
46862793Slm66018 
46872793Slm66018 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
46884647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
46892793Slm66018 		} else {
46902793Slm66018 			ldcp->ldc_status = istatus;
46912793Slm66018 		}
46921991Sheppo 	}
46931991Sheppo }
46941991Sheppo 
46951991Sheppo /* wrapper function for vgen_reset_hphase */
46961991Sheppo static void
46971991Sheppo vgen_handshake_reset(vgen_ldc_t *ldcp)
46981991Sheppo {
46991991Sheppo 	ASSERT(MUTEX_HELD(&ldcp->cblock));
47004647Sraghuram 	mutex_enter(&ldcp->rxlock);
47014647Sraghuram 	mutex_enter(&ldcp->wrlock);
47021991Sheppo 	mutex_enter(&ldcp->txlock);
47031991Sheppo 	mutex_enter(&ldcp->tclock);
47041991Sheppo 
47051991Sheppo 	vgen_reset_hphase(ldcp);
47061991Sheppo 
47071991Sheppo 	mutex_exit(&ldcp->tclock);
47081991Sheppo 	mutex_exit(&ldcp->txlock);
47094647Sraghuram 	mutex_exit(&ldcp->wrlock);
47104647Sraghuram 	mutex_exit(&ldcp->rxlock);
47111991Sheppo }
47121991Sheppo 
47131991Sheppo /*
47141991Sheppo  * Initiate handshake with the peer by sending various messages
47151991Sheppo  * based on the handshake-phase that the channel is currently in.
47161991Sheppo  */
47171991Sheppo static void
47181991Sheppo vgen_handshake(vgen_ldc_t *ldcp)
47191991Sheppo {
47201991Sheppo 	uint32_t hphase = ldcp->hphase;
47211991Sheppo 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
47222793Slm66018 	ldc_status_t	istatus;
47232793Slm66018 	int	rv = 0;
47241991Sheppo 
47251991Sheppo 	switch (hphase) {
47261991Sheppo 
47271991Sheppo 	case VH_PHASE1:
47281991Sheppo 
47291991Sheppo 		/*
47301991Sheppo 		 * start timer, for entire handshake process, turn this timer
47311991Sheppo 		 * off if all phases of handshake complete successfully and
47321991Sheppo 		 * hphase goes to VH_DONE(below) or
47331991Sheppo 		 * vgen_reset_hphase() gets called or
47341991Sheppo 		 * channel is reset due to errors or
47351991Sheppo 		 * vgen_ldc_uninit() is invoked(vgen_stop).
47361991Sheppo 		 */
47377572SWentao.Yang@Sun.COM 		ASSERT(ldcp->htid == 0);
47381991Sheppo 		ldcp->htid = timeout(vgen_hwatchdog, (caddr_t)ldcp,
47395171Ssb155480 		    drv_usectohz(vgen_hwd_interval * MICROSEC));
47401991Sheppo 
47411991Sheppo 		/* Phase 1 involves negotiating the version */
47422793Slm66018 		rv = vgen_send_version_negotiate(ldcp);
47431991Sheppo 		break;
47441991Sheppo 
47451991Sheppo 	case VH_PHASE2:
47462793Slm66018 		rv = vgen_handshake_phase2(ldcp);
47471991Sheppo 		break;
47481991Sheppo 
47491991Sheppo 	case VH_PHASE3:
47502793Slm66018 		rv = vgen_send_rdx_info(ldcp);
47511991Sheppo 		break;
47521991Sheppo 
47531991Sheppo 	case VH_DONE:
47543653Snarayan 		/*
47553653Snarayan 		 * Save the id of pending handshake timer in cancel_htid.
47563653Snarayan 		 * This will be checked in vgen_ldc_cb() and the handshake
47573653Snarayan 		 * timer will be cancelled after releasing cblock.
47583653Snarayan 		 */
47591991Sheppo 		if (ldcp->htid) {
47603653Snarayan 			ldcp->cancel_htid = ldcp->htid;
47611991Sheppo 			ldcp->htid = 0;
47621991Sheppo 		}
47631991Sheppo 		ldcp->hretries = 0;
47644647Sraghuram 		DBG1(vgenp, ldcp, "Handshake Done\n");
47651991Sheppo 
47665171Ssb155480 		if (ldcp->portp == vgenp->vsw_portp) {
47675171Ssb155480 			/*
47685171Ssb155480 			 * If this channel(port) is connected to vsw,
47695171Ssb155480 			 * need to sync multicast table with vsw.
47705171Ssb155480 			 */
47711991Sheppo 			mutex_exit(&ldcp->cblock);
47721991Sheppo 
47731991Sheppo 			mutex_enter(&vgenp->lock);
47742793Slm66018 			rv = vgen_send_mcast_info(ldcp);
47751991Sheppo 			mutex_exit(&vgenp->lock);
47761991Sheppo 
47771991Sheppo 			mutex_enter(&ldcp->cblock);
47782793Slm66018 			if (rv != VGEN_SUCCESS)
47792793Slm66018 				break;
47801991Sheppo 		}
47812793Slm66018 
47822793Slm66018 		/*
47832793Slm66018 		 * Check if mac layer should be notified to restart
47842793Slm66018 		 * transmissions. This can happen if the channel got
47852793Slm66018 		 * reset and vgen_clobber_tbufs() is called, while
47862793Slm66018 		 * need_resched is set.
47872793Slm66018 		 */
47882793Slm66018 		mutex_enter(&ldcp->tclock);
47892793Slm66018 		if (ldcp->need_resched) {
47906495Sspeer 			vio_net_tx_update_t vtx_update =
47916495Sspeer 			    ldcp->portp->vcb.vio_net_tx_update;
47926495Sspeer 
47932793Slm66018 			ldcp->need_resched = B_FALSE;
47946495Sspeer 			vtx_update(ldcp->portp->vhp);
47952793Slm66018 		}
47962793Slm66018 		mutex_exit(&ldcp->tclock);
47972793Slm66018 
47981991Sheppo 		break;
47991991Sheppo 
48001991Sheppo 	default:
48011991Sheppo 		break;
48021991Sheppo 	}
48032793Slm66018 
48042793Slm66018 	if (rv == ECONNRESET) {
48052793Slm66018 		if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
48064647Sraghuram 			DWARN(vgenp, ldcp, "ldc_status err\n");
48072793Slm66018 		} else {
48082793Slm66018 			ldcp->ldc_status = istatus;
48092793Slm66018 		}
48106495Sspeer 		vgen_handle_evt_reset(ldcp);
48112793Slm66018 	} else if (rv) {
48122793Slm66018 		vgen_handshake_reset(ldcp);
48132793Slm66018 	}
48141991Sheppo }
48151991Sheppo 
48161991Sheppo /*
48171991Sheppo  * Check if the current handshake phase has completed successfully and
48181991Sheppo  * return the status.
48191991Sheppo  */
48201991Sheppo static int
48211991Sheppo vgen_handshake_done(vgen_ldc_t *ldcp)
48221991Sheppo {
48234647Sraghuram 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
48241991Sheppo 	uint32_t	hphase = ldcp->hphase;
48251991Sheppo 	int 		status = 0;
48261991Sheppo 
48271991Sheppo 	switch (hphase) {
48281991Sheppo 
48291991Sheppo 	case VH_PHASE1:
48301991Sheppo 		/*
48311991Sheppo 		 * Phase1 is done, if version negotiation
48321991Sheppo 		 * completed successfully.
48331991Sheppo 		 */
48341991Sheppo 		status = ((ldcp->hstate & VER_NEGOTIATED) ==
48354650Sraghuram 		    VER_NEGOTIATED);
48361991Sheppo 		break;
48371991Sheppo 
48381991Sheppo 	case VH_PHASE2:
48391991Sheppo 		/*
48401991Sheppo 		 * Phase 2 is done, if attr info and dring info
48411991Sheppo 		 * have been exchanged successfully.
48421991Sheppo 		 */
48431991Sheppo 		status = (((ldcp->hstate & ATTR_INFO_EXCHANGED) ==
48444650Sraghuram 		    ATTR_INFO_EXCHANGED) &&
48454650Sraghuram 		    ((ldcp->hstate & DRING_INFO_EXCHANGED) ==
48464650Sraghuram 		    DRING_INFO_EXCHANGED));
48471991Sheppo 		break;
48481991Sheppo 
48491991Sheppo 	case VH_PHASE3:
48501991Sheppo 		/* Phase 3 is done, if rdx msg has been exchanged */
48511991Sheppo 		status = ((ldcp->hstate & RDX_EXCHANGED) ==
48524650Sraghuram 		    RDX_EXCHANGED);
48531991Sheppo 		break;
48541991Sheppo 
48551991Sheppo 	default:
48561991Sheppo 		break;
48571991Sheppo 	}
48581991Sheppo 
48591991Sheppo 	if (status == 0) {
48601991Sheppo 		return (VGEN_FAILURE);
48611991Sheppo 	}
48624647Sraghuram 	DBG2(vgenp, ldcp, "PHASE(%d)\n", hphase);
48631991Sheppo 	return (VGEN_SUCCESS);
48641991Sheppo }
48651991Sheppo 
48661991Sheppo /* retry handshake on failure */
48671991Sheppo static void
48681991Sheppo vgen_handshake_retry(vgen_ldc_t *ldcp)
48691991Sheppo {
48701991Sheppo 	/* reset handshake phase */
48711991Sheppo 	vgen_handshake_reset(ldcp);
48723653Snarayan 
48733653Snarayan 	/* handshake retry is specified and the channel is UP */
48743653Snarayan 	if (vgen_max_hretries && (ldcp->ldc_status == LDC_UP)) {
48753653Snarayan 		if (ldcp->hretries++ < vgen_max_hretries) {
48763653Snarayan 			ldcp->local_sid = ddi_get_lbolt();
48771991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
48783653Snarayan 		}
48791991Sheppo 	}
48801991Sheppo }
48811991Sheppo 
48821991Sheppo /*
48831991Sheppo  * Handle a version info msg from the peer or an ACK/NACK from the peer
48841991Sheppo  * to a version info msg that we sent.
48851991Sheppo  */
48862793Slm66018 static int
48871991Sheppo vgen_handle_version_negotiate(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
48881991Sheppo {
48894647Sraghuram 	vgen_t		*vgenp;
48901991Sheppo 	vio_ver_msg_t	*vermsg = (vio_ver_msg_t *)tagp;
48911991Sheppo 	int		ack = 0;
48921991Sheppo 	int		failed = 0;
48931991Sheppo 	int		idx;
48941991Sheppo 	vgen_ver_t	*versions = ldcp->vgen_versions;
48952793Slm66018 	int		rv = 0;
48961991Sheppo 
48974647Sraghuram 	vgenp = LDC_TO_VGEN(ldcp);
48984647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
48991991Sheppo 	switch (tagp->vio_subtype) {
49001991Sheppo 	case VIO_SUBTYPE_INFO:
49011991Sheppo 
49021991Sheppo 		/*  Cache sid of peer if this is the first time */
49031991Sheppo 		if (ldcp->peer_sid == 0) {
49044647Sraghuram 			DBG2(vgenp, ldcp, "Caching peer_sid(%x)\n",
49054647Sraghuram 			    tagp->vio_sid);
49061991Sheppo 			ldcp->peer_sid = tagp->vio_sid;
49071991Sheppo 		}
49081991Sheppo 
49091991Sheppo 		if (ldcp->hphase != VH_PHASE1) {
49101991Sheppo 			/*
49111991Sheppo 			 * If we are not already in VH_PHASE1, reset to
49121991Sheppo 			 * pre-handshake state, and initiate handshake
49131991Sheppo 			 * to the peer too.
49141991Sheppo 			 */
49151991Sheppo 			vgen_handshake_reset(ldcp);
49161991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
49171991Sheppo 		}
49181991Sheppo 		ldcp->hstate |= VER_INFO_RCVD;
49191991Sheppo 
49201991Sheppo 		/* save peer's requested values */
49211991Sheppo 		ldcp->peer_hparams.ver_major = vermsg->ver_major;
49221991Sheppo 		ldcp->peer_hparams.ver_minor = vermsg->ver_minor;
49231991Sheppo 		ldcp->peer_hparams.dev_class = vermsg->dev_class;
49241991Sheppo 
49251991Sheppo 		if ((vermsg->dev_class != VDEV_NETWORK) &&
49261991Sheppo 		    (vermsg->dev_class != VDEV_NETWORK_SWITCH)) {
49271991Sheppo 			/* unsupported dev_class, send NACK */
49281991Sheppo 
49294647Sraghuram 			DWARN(vgenp, ldcp, "Version Negotiation Failed\n");
49302793Slm66018 
49311991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
49321991Sheppo 			tagp->vio_sid = ldcp->local_sid;
49331991Sheppo 			/* send reply msg back to peer */
49342793Slm66018 			rv = vgen_sendmsg(ldcp, (caddr_t)tagp,
49351991Sheppo 			    sizeof (*vermsg), B_FALSE);
49362793Slm66018 			if (rv != VGEN_SUCCESS) {
49372793Slm66018 				return (rv);
49382793Slm66018 			}
49392793Slm66018 			return (VGEN_FAILURE);
49401991Sheppo 		}
49411991Sheppo 
49424647Sraghuram 		DBG2(vgenp, ldcp, "VER_INFO_RCVD, ver(%d,%d)\n",
49434647Sraghuram 		    vermsg->ver_major,  vermsg->ver_minor);
49441991Sheppo 
49451991Sheppo 		idx = 0;
49461991Sheppo 
49471991Sheppo 		for (;;) {
49481991Sheppo 
49491991Sheppo 			if (vermsg->ver_major > versions[idx].ver_major) {
49501991Sheppo 
49511991Sheppo 				/* nack with next lower version */
49521991Sheppo 				tagp->vio_subtype = VIO_SUBTYPE_NACK;
49531991Sheppo 				vermsg->ver_major = versions[idx].ver_major;
49541991Sheppo 				vermsg->ver_minor = versions[idx].ver_minor;
49551991Sheppo 				break;
49561991Sheppo 			}
49571991Sheppo 
49581991Sheppo 			if (vermsg->ver_major == versions[idx].ver_major) {
49591991Sheppo 
49601991Sheppo 				/* major version match - ACK version */
49611991Sheppo 				tagp->vio_subtype = VIO_SUBTYPE_ACK;
49621991Sheppo 				ack = 1;
49631991Sheppo 
49641991Sheppo 				/*
49651991Sheppo 				 * lower minor version to the one this endpt
49661991Sheppo 				 * supports, if necessary
49671991Sheppo 				 */
49681991Sheppo 				if (vermsg->ver_minor >
49691991Sheppo 				    versions[idx].ver_minor) {
49701991Sheppo 					vermsg->ver_minor =
49714650Sraghuram 					    versions[idx].ver_minor;
49721991Sheppo 					ldcp->peer_hparams.ver_minor =
49734650Sraghuram 					    versions[idx].ver_minor;
49741991Sheppo 				}
49751991Sheppo 				break;
49761991Sheppo 			}
49771991Sheppo 
49781991Sheppo 			idx++;
49791991Sheppo 
49801991Sheppo 			if (idx == VGEN_NUM_VER) {
49811991Sheppo 
49821991Sheppo 				/* no version match - send NACK */
49831991Sheppo 				tagp->vio_subtype = VIO_SUBTYPE_NACK;
49841991Sheppo 				vermsg->ver_major = 0;
49851991Sheppo 				vermsg->ver_minor = 0;
49861991Sheppo 				failed = 1;
49871991Sheppo 				break;
49881991Sheppo 			}
49891991Sheppo 
49901991Sheppo 		}
49911991Sheppo 
49921991Sheppo 		tagp->vio_sid = ldcp->local_sid;
49931991Sheppo 
49941991Sheppo 		/* send reply msg back to peer */
49952793Slm66018 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*vermsg),
49962793Slm66018 		    B_FALSE);
49972793Slm66018 		if (rv != VGEN_SUCCESS) {
49982793Slm66018 			return (rv);
49991991Sheppo 		}
50001991Sheppo 
50011991Sheppo 		if (ack) {
50021991Sheppo 			ldcp->hstate |= VER_ACK_SENT;
50034647Sraghuram 			DBG2(vgenp, ldcp, "VER_ACK_SENT, ver(%d,%d) \n",
50044647Sraghuram 			    vermsg->ver_major, vermsg->ver_minor);
50051991Sheppo 		}
50061991Sheppo 		if (failed) {
50074647Sraghuram 			DWARN(vgenp, ldcp, "Negotiation Failed\n");
50082793Slm66018 			return (VGEN_FAILURE);
50091991Sheppo 		}
50101991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
50111991Sheppo 
50121991Sheppo 			/*  VER_ACK_SENT and VER_ACK_RCVD */
50131991Sheppo 
50141991Sheppo 			/* local and peer versions match? */
50151991Sheppo 			ASSERT((ldcp->local_hparams.ver_major ==
50164650Sraghuram 			    ldcp->peer_hparams.ver_major) &&
50174650Sraghuram 			    (ldcp->local_hparams.ver_minor ==
50184650Sraghuram 			    ldcp->peer_hparams.ver_minor));
50191991Sheppo 
50205935Ssb155480 			vgen_set_vnet_proto_ops(ldcp);
50215935Ssb155480 
50221991Sheppo 			/* move to the next phase */
50231991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
50241991Sheppo 		}
50251991Sheppo 
50261991Sheppo 		break;
50271991Sheppo 
50281991Sheppo 	case VIO_SUBTYPE_ACK:
50291991Sheppo 
50301991Sheppo 		if (ldcp->hphase != VH_PHASE1) {
50311991Sheppo 			/*  This should not happen. */
50324647Sraghuram 			DWARN(vgenp, ldcp, "Invalid Phase(%u)\n", ldcp->hphase);
50332793Slm66018 			return (VGEN_FAILURE);
50341991Sheppo 		}
50351991Sheppo 
50361991Sheppo 		/* SUCCESS - we have agreed on a version */
50371991Sheppo 		ldcp->local_hparams.ver_major = vermsg->ver_major;
50381991Sheppo 		ldcp->local_hparams.ver_minor = vermsg->ver_minor;
50391991Sheppo 		ldcp->hstate |= VER_ACK_RCVD;
50401991Sheppo 
50414647Sraghuram 		DBG2(vgenp, ldcp, "VER_ACK_RCVD, ver(%d,%d) \n",
50424647Sraghuram 		    vermsg->ver_major,  vermsg->ver_minor);
50431991Sheppo 
50441991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
50451991Sheppo 
50461991Sheppo 			/*  VER_ACK_SENT and VER_ACK_RCVD */
50471991Sheppo 
50481991Sheppo 			/* local and peer versions match? */
50491991Sheppo 			ASSERT((ldcp->local_hparams.ver_major ==
50504650Sraghuram 			    ldcp->peer_hparams.ver_major) &&
50514650Sraghuram 			    (ldcp->local_hparams.ver_minor ==
50524650Sraghuram 			    ldcp->peer_hparams.ver_minor));
50531991Sheppo 
50545935Ssb155480 			vgen_set_vnet_proto_ops(ldcp);
50555935Ssb155480 
50561991Sheppo 			/* move to the next phase */
50571991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
50581991Sheppo 		}
50591991Sheppo 		break;
50601991Sheppo 
50611991Sheppo 	case VIO_SUBTYPE_NACK:
50621991Sheppo 
50631991Sheppo 		if (ldcp->hphase != VH_PHASE1) {
50641991Sheppo 			/*  This should not happen.  */
50654647Sraghuram 			DWARN(vgenp, ldcp, "VER_NACK_RCVD Invalid "
50664647Sraghuram 			"Phase(%u)\n", ldcp->hphase);
50672793Slm66018 			return (VGEN_FAILURE);
50681991Sheppo 		}
50691991Sheppo 
50704647Sraghuram 		DBG2(vgenp, ldcp, "VER_NACK_RCVD next ver(%d,%d)\n",
50714647Sraghuram 		    vermsg->ver_major, vermsg->ver_minor);
50721991Sheppo 
50731991Sheppo 		/* check if version in NACK is zero */
50741991Sheppo 		if (vermsg->ver_major == 0 && vermsg->ver_minor == 0) {
50751991Sheppo 			/*
50761991Sheppo 			 * Version Negotiation has failed.
50771991Sheppo 			 */
50784647Sraghuram 			DWARN(vgenp, ldcp, "Version Negotiation Failed\n");
50792793Slm66018 			return (VGEN_FAILURE);
50801991Sheppo 		}
50811991Sheppo 
50821991Sheppo 		idx = 0;
50831991Sheppo 
50841991Sheppo 		for (;;) {
50851991Sheppo 
50861991Sheppo 			if (vermsg->ver_major > versions[idx].ver_major) {
50871991Sheppo 				/* select next lower version */
50881991Sheppo 
50891991Sheppo 				ldcp->local_hparams.ver_major =
50904650Sraghuram 				    versions[idx].ver_major;
50911991Sheppo 				ldcp->local_hparams.ver_minor =
50924650Sraghuram 				    versions[idx].ver_minor;
50931991Sheppo 				break;
50941991Sheppo 			}
50951991Sheppo 
50961991Sheppo 			if (vermsg->ver_major == versions[idx].ver_major) {
50971991Sheppo 				/* major version match */
50981991Sheppo 
50991991Sheppo 				ldcp->local_hparams.ver_major =
51004650Sraghuram 				    versions[idx].ver_major;
51011991Sheppo 
51021991Sheppo 				ldcp->local_hparams.ver_minor =
51034650Sraghuram 				    versions[idx].ver_minor;
51041991Sheppo 				break;
51051991Sheppo 			}
51061991Sheppo 
51071991Sheppo 			idx++;
51081991Sheppo 
51091991Sheppo 			if (idx == VGEN_NUM_VER) {
51101991Sheppo 				/*
51111991Sheppo 				 * no version match.
51121991Sheppo 				 * Version Negotiation has failed.
51131991Sheppo 				 */
51144647Sraghuram 				DWARN(vgenp, ldcp,
51154647Sraghuram 				    "Version Negotiation Failed\n");
51162793Slm66018 				return (VGEN_FAILURE);
51171991Sheppo 			}
51181991Sheppo 
51191991Sheppo 		}
51201991Sheppo 
51212793Slm66018 		rv = vgen_send_version_negotiate(ldcp);
51222793Slm66018 		if (rv != VGEN_SUCCESS) {
51232793Slm66018 			return (rv);
51241991Sheppo 		}
51251991Sheppo 
51261991Sheppo 		break;
51271991Sheppo 	}
51282793Slm66018 
51294647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
51302793Slm66018 	return (VGEN_SUCCESS);
51311991Sheppo }
51321991Sheppo 
51331991Sheppo /* Check if the attributes are supported */
51341991Sheppo static int
51351991Sheppo vgen_check_attr_info(vgen_ldc_t *ldcp, vnet_attr_msg_t *msg)
51361991Sheppo {
51375935Ssb155480 	vgen_hparams_t	*lp = &ldcp->local_hparams;
51385935Ssb155480 
51397529SSriharsha.Basavapatna@Sun.COM 	if ((msg->addr_type != ADDR_TYPE_MAC) ||
51405935Ssb155480 	    (msg->ack_freq > 64) ||
51415935Ssb155480 	    (msg->xfer_mode != lp->xfer_mode)) {
51421991Sheppo 		return (VGEN_FAILURE);
51431991Sheppo 	}
51441991Sheppo 
51457529SSriharsha.Basavapatna@Sun.COM 	if (VGEN_VER_LT(ldcp, 1, 4)) {
51467529SSriharsha.Basavapatna@Sun.COM 		/* versions < 1.4, mtu must match */
51477529SSriharsha.Basavapatna@Sun.COM 		if (msg->mtu != lp->mtu) {
51487529SSriharsha.Basavapatna@Sun.COM 			return (VGEN_FAILURE);
51497529SSriharsha.Basavapatna@Sun.COM 		}
51507529SSriharsha.Basavapatna@Sun.COM 	} else {
51517529SSriharsha.Basavapatna@Sun.COM 		/* Ver >= 1.4, validate mtu of the peer is at least ETHERMAX */
51527529SSriharsha.Basavapatna@Sun.COM 		if (msg->mtu < ETHERMAX) {
51537529SSriharsha.Basavapatna@Sun.COM 			return (VGEN_FAILURE);
51547529SSriharsha.Basavapatna@Sun.COM 		}
51557529SSriharsha.Basavapatna@Sun.COM 	}
51567529SSriharsha.Basavapatna@Sun.COM 
51571991Sheppo 	return (VGEN_SUCCESS);
51581991Sheppo }
51591991Sheppo 
51601991Sheppo /*
51611991Sheppo  * Handle an attribute info msg from the peer or an ACK/NACK from the peer
51621991Sheppo  * to an attr info msg that we sent.
51631991Sheppo  */
51642793Slm66018 static int
51651991Sheppo vgen_handle_attr_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
51661991Sheppo {
51677529SSriharsha.Basavapatna@Sun.COM 	vgen_t		*vgenp = LDC_TO_VGEN(ldcp);
51687529SSriharsha.Basavapatna@Sun.COM 	vnet_attr_msg_t	*msg = (vnet_attr_msg_t *)tagp;
51697529SSriharsha.Basavapatna@Sun.COM 	vgen_hparams_t	*lp = &ldcp->local_hparams;
51707529SSriharsha.Basavapatna@Sun.COM 	vgen_hparams_t	*rp = &ldcp->peer_hparams;
51717529SSriharsha.Basavapatna@Sun.COM 	int		ack = 1;
51722793Slm66018 	int		rv = 0;
51737529SSriharsha.Basavapatna@Sun.COM 	uint32_t	mtu;
51741991Sheppo 
51754647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
51761991Sheppo 	if (ldcp->hphase != VH_PHASE2) {
51774647Sraghuram 		DWARN(vgenp, ldcp, "Rcvd ATTR_INFO subtype(%d),"
51784647Sraghuram 		" Invalid Phase(%u)\n",
51794647Sraghuram 		    tagp->vio_subtype, ldcp->hphase);
51802793Slm66018 		return (VGEN_FAILURE);
51811991Sheppo 	}
51821991Sheppo 	switch (tagp->vio_subtype) {
51831991Sheppo 	case VIO_SUBTYPE_INFO:
51841991Sheppo 
51854647Sraghuram 		DBG2(vgenp, ldcp, "ATTR_INFO_RCVD \n");
51861991Sheppo 		ldcp->hstate |= ATTR_INFO_RCVD;
51871991Sheppo 
51881991Sheppo 		/* save peer's values */
51897529SSriharsha.Basavapatna@Sun.COM 		rp->mtu = msg->mtu;
51907529SSriharsha.Basavapatna@Sun.COM 		rp->addr = msg->addr;
51917529SSriharsha.Basavapatna@Sun.COM 		rp->addr_type = msg->addr_type;
51927529SSriharsha.Basavapatna@Sun.COM 		rp->xfer_mode = msg->xfer_mode;
51937529SSriharsha.Basavapatna@Sun.COM 		rp->ack_freq = msg->ack_freq;
51947529SSriharsha.Basavapatna@Sun.COM 
51957529SSriharsha.Basavapatna@Sun.COM 		rv = vgen_check_attr_info(ldcp, msg);
51967529SSriharsha.Basavapatna@Sun.COM 		if (rv == VGEN_FAILURE) {
51971991Sheppo 			/* unsupported attr, send NACK */
51987529SSriharsha.Basavapatna@Sun.COM 			ack = 0;
51991991Sheppo 		} else {
52007529SSriharsha.Basavapatna@Sun.COM 
52017529SSriharsha.Basavapatna@Sun.COM 			if (VGEN_VER_GTEQ(ldcp, 1, 4)) {
52027529SSriharsha.Basavapatna@Sun.COM 
52037529SSriharsha.Basavapatna@Sun.COM 				/*
52047529SSriharsha.Basavapatna@Sun.COM 				 * Versions >= 1.4:
52057529SSriharsha.Basavapatna@Sun.COM 				 * The mtu is negotiated down to the
52067529SSriharsha.Basavapatna@Sun.COM 				 * minimum of our mtu and peer's mtu.
52077529SSriharsha.Basavapatna@Sun.COM 				 */
52087529SSriharsha.Basavapatna@Sun.COM 				mtu = MIN(msg->mtu, vgenp->max_frame_size);
52097529SSriharsha.Basavapatna@Sun.COM 
52107529SSriharsha.Basavapatna@Sun.COM 				/*
52117529SSriharsha.Basavapatna@Sun.COM 				 * If we have received an ack for the attr info
52127529SSriharsha.Basavapatna@Sun.COM 				 * that we sent, then check if the mtu computed
52137529SSriharsha.Basavapatna@Sun.COM 				 * above matches the mtu that the peer had ack'd
52147529SSriharsha.Basavapatna@Sun.COM 				 * (saved in local hparams). If they don't
52157529SSriharsha.Basavapatna@Sun.COM 				 * match, we fail the handshake.
52167529SSriharsha.Basavapatna@Sun.COM 				 */
52177529SSriharsha.Basavapatna@Sun.COM 				if (ldcp->hstate & ATTR_ACK_RCVD) {
52187529SSriharsha.Basavapatna@Sun.COM 					if (mtu != lp->mtu) {
52197529SSriharsha.Basavapatna@Sun.COM 						/* send NACK */
52207529SSriharsha.Basavapatna@Sun.COM 						ack = 0;
52217529SSriharsha.Basavapatna@Sun.COM 					}
52227529SSriharsha.Basavapatna@Sun.COM 				} else {
52237529SSriharsha.Basavapatna@Sun.COM 					/*
52247529SSriharsha.Basavapatna@Sun.COM 					 * Save the mtu computed above in our
52257529SSriharsha.Basavapatna@Sun.COM 					 * attr parameters, so it gets sent in
52267529SSriharsha.Basavapatna@Sun.COM 					 * the attr info from us to the peer.
52277529SSriharsha.Basavapatna@Sun.COM 					 */
52287529SSriharsha.Basavapatna@Sun.COM 					lp->mtu = mtu;
52297529SSriharsha.Basavapatna@Sun.COM 				}
52307529SSriharsha.Basavapatna@Sun.COM 
52317529SSriharsha.Basavapatna@Sun.COM 				/* save the MIN mtu in the msg to be replied */
52327529SSriharsha.Basavapatna@Sun.COM 				msg->mtu = mtu;
52337529SSriharsha.Basavapatna@Sun.COM 
52347529SSriharsha.Basavapatna@Sun.COM 			}
52357529SSriharsha.Basavapatna@Sun.COM 		}
52367529SSriharsha.Basavapatna@Sun.COM 
52377529SSriharsha.Basavapatna@Sun.COM 
52387529SSriharsha.Basavapatna@Sun.COM 		if (ack) {
52391991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_ACK;
52407529SSriharsha.Basavapatna@Sun.COM 		} else {
52417529SSriharsha.Basavapatna@Sun.COM 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
52421991Sheppo 		}
52431991Sheppo 		tagp->vio_sid = ldcp->local_sid;
52441991Sheppo 
52451991Sheppo 		/* send reply msg back to peer */
52467529SSriharsha.Basavapatna@Sun.COM 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msg),
52472793Slm66018 		    B_FALSE);
52482793Slm66018 		if (rv != VGEN_SUCCESS) {
52492793Slm66018 			return (rv);
52501991Sheppo 		}
52511991Sheppo 
52521991Sheppo 		if (ack) {
52531991Sheppo 			ldcp->hstate |= ATTR_ACK_SENT;
52544647Sraghuram 			DBG2(vgenp, ldcp, "ATTR_ACK_SENT \n");
52551991Sheppo 		} else {
52561991Sheppo 			/* failed */
52574647Sraghuram 			DWARN(vgenp, ldcp, "ATTR_NACK_SENT \n");
52582793Slm66018 			return (VGEN_FAILURE);
52591991Sheppo 		}
52601991Sheppo 
52611991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
52621991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
52631991Sheppo 		}
52641991Sheppo 
52651991Sheppo 		break;
52661991Sheppo 
52671991Sheppo 	case VIO_SUBTYPE_ACK:
52681991Sheppo 
52697529SSriharsha.Basavapatna@Sun.COM 		if (VGEN_VER_GTEQ(ldcp, 1, 4)) {
52707529SSriharsha.Basavapatna@Sun.COM 			/*
52717529SSriharsha.Basavapatna@Sun.COM 			 * Versions >= 1.4:
52727529SSriharsha.Basavapatna@Sun.COM 			 * The ack msg sent by the peer contains the minimum of
52737529SSriharsha.Basavapatna@Sun.COM 			 * our mtu (that we had sent in our attr info) and the
52747529SSriharsha.Basavapatna@Sun.COM 			 * peer's mtu.
52757529SSriharsha.Basavapatna@Sun.COM 			 *
52767529SSriharsha.Basavapatna@Sun.COM 			 * If we have sent an ack for the attr info msg from
52777529SSriharsha.Basavapatna@Sun.COM 			 * the peer, check if the mtu that was computed then
52787529SSriharsha.Basavapatna@Sun.COM 			 * (saved in local hparams) matches the mtu that the
52797529SSriharsha.Basavapatna@Sun.COM 			 * peer has ack'd. If they don't match, we fail the
52807529SSriharsha.Basavapatna@Sun.COM 			 * handshake.
52817529SSriharsha.Basavapatna@Sun.COM 			 */
52827529SSriharsha.Basavapatna@Sun.COM 			if (ldcp->hstate & ATTR_ACK_SENT) {
52837529SSriharsha.Basavapatna@Sun.COM 				if (lp->mtu != msg->mtu) {
52847529SSriharsha.Basavapatna@Sun.COM 					return (VGEN_FAILURE);
52857529SSriharsha.Basavapatna@Sun.COM 				}
52867529SSriharsha.Basavapatna@Sun.COM 			} else {
52877529SSriharsha.Basavapatna@Sun.COM 				/*
52887529SSriharsha.Basavapatna@Sun.COM 				 * If the mtu ack'd by the peer is > our mtu
52897529SSriharsha.Basavapatna@Sun.COM 				 * fail handshake. Otherwise, save the mtu, so
52907529SSriharsha.Basavapatna@Sun.COM 				 * we can validate it when we receive attr info
52917529SSriharsha.Basavapatna@Sun.COM 				 * from our peer.
52927529SSriharsha.Basavapatna@Sun.COM 				 */
52937529SSriharsha.Basavapatna@Sun.COM 				if (msg->mtu > lp->mtu) {
52947529SSriharsha.Basavapatna@Sun.COM 					return (VGEN_FAILURE);
52957529SSriharsha.Basavapatna@Sun.COM 				}
52967529SSriharsha.Basavapatna@Sun.COM 				if (msg->mtu <= lp->mtu) {
52977529SSriharsha.Basavapatna@Sun.COM 					lp->mtu = msg->mtu;
52987529SSriharsha.Basavapatna@Sun.COM 				}
52997529SSriharsha.Basavapatna@Sun.COM 			}
53007529SSriharsha.Basavapatna@Sun.COM 		}
53017529SSriharsha.Basavapatna@Sun.COM 
53021991Sheppo 		ldcp->hstate |= ATTR_ACK_RCVD;
53031991Sheppo 
53044647Sraghuram 		DBG2(vgenp, ldcp, "ATTR_ACK_RCVD \n");
53051991Sheppo 
53061991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
53071991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
53081991Sheppo 		}
53091991Sheppo 		break;
53101991Sheppo 
53111991Sheppo 	case VIO_SUBTYPE_NACK:
53121991Sheppo 
53134647Sraghuram 		DBG2(vgenp, ldcp, "ATTR_NACK_RCVD \n");
53142793Slm66018 		return (VGEN_FAILURE);
53151991Sheppo 	}
53164647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
53172793Slm66018 	return (VGEN_SUCCESS);
53181991Sheppo }
53191991Sheppo 
53201991Sheppo /* Check if the dring info msg is ok */
53211991Sheppo static int
53221991Sheppo vgen_check_dring_reg(vio_dring_reg_msg_t *msg)
53231991Sheppo {
53241991Sheppo 	/* check if msg contents are ok */
53251991Sheppo 	if ((msg->num_descriptors < 128) || (msg->descriptor_size <
53261991Sheppo 	    sizeof (vnet_public_desc_t))) {
53271991Sheppo 		return (VGEN_FAILURE);
53281991Sheppo 	}
53291991Sheppo 	return (VGEN_SUCCESS);
53301991Sheppo }
53311991Sheppo 
53321991Sheppo /*
53331991Sheppo  * Handle a descriptor ring register msg from the peer or an ACK/NACK from
53341991Sheppo  * the peer to a dring register msg that we sent.
53351991Sheppo  */
53362793Slm66018 static int
53371991Sheppo vgen_handle_dring_reg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
53381991Sheppo {
53391991Sheppo 	vio_dring_reg_msg_t *msg = (vio_dring_reg_msg_t *)tagp;
53401991Sheppo 	ldc_mem_cookie_t dcookie;
53414647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
53421991Sheppo 	int ack = 0;
53431991Sheppo 	int rv = 0;
53441991Sheppo 
53454647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
53461991Sheppo 	if (ldcp->hphase < VH_PHASE2) {
53471991Sheppo 		/* dring_info can be rcvd in any of the phases after Phase1 */
53484647Sraghuram 		DWARN(vgenp, ldcp,
53494647Sraghuram 		    "Rcvd DRING_INFO Subtype (%d), Invalid Phase(%u)\n",
53504650Sraghuram 		    tagp->vio_subtype, ldcp->hphase);
53512793Slm66018 		return (VGEN_FAILURE);
53521991Sheppo 	}
53531991Sheppo 	switch (tagp->vio_subtype) {
53541991Sheppo 	case VIO_SUBTYPE_INFO:
53551991Sheppo 
53564647Sraghuram 		DBG2(vgenp, ldcp, "DRING_INFO_RCVD \n");
53571991Sheppo 		ldcp->hstate |= DRING_INFO_RCVD;
53581991Sheppo 		bcopy((msg->cookie), &dcookie, sizeof (dcookie));
53591991Sheppo 
53601991Sheppo 		ASSERT(msg->ncookies == 1);
53611991Sheppo 
53621991Sheppo 		if (vgen_check_dring_reg(msg) == VGEN_SUCCESS) {
53631991Sheppo 			/*
53641991Sheppo 			 * verified dring info msg to be ok,
53651991Sheppo 			 * now try to map the remote dring.
53661991Sheppo 			 */
53671991Sheppo 			rv = vgen_init_rxds(ldcp, msg->num_descriptors,
53681991Sheppo 			    msg->descriptor_size, &dcookie,
53691991Sheppo 			    msg->ncookies);
53701991Sheppo 			if (rv == DDI_SUCCESS) {
53711991Sheppo 				/* now we can ack the peer */
53721991Sheppo 				ack = 1;
53731991Sheppo 			}
53741991Sheppo 		}
53751991Sheppo 		if (ack == 0) {
53761991Sheppo 			/* failed, send NACK */
53771991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
53781991Sheppo 		} else {
53791991Sheppo 			if (!(ldcp->peer_hparams.dring_ready)) {
53801991Sheppo 
53811991Sheppo 				/* save peer's dring_info values */
53821991Sheppo 				bcopy(&dcookie,
53831991Sheppo 				    &(ldcp->peer_hparams.dring_cookie),
53841991Sheppo 				    sizeof (dcookie));
53851991Sheppo 				ldcp->peer_hparams.num_desc =
53864650Sraghuram 				    msg->num_descriptors;
53871991Sheppo 				ldcp->peer_hparams.desc_size =
53884650Sraghuram 				    msg->descriptor_size;
53891991Sheppo 				ldcp->peer_hparams.num_dcookies =
53904650Sraghuram 				    msg->ncookies;
53911991Sheppo 
53921991Sheppo 				/* set dring_ident for the peer */
53931991Sheppo 				ldcp->peer_hparams.dring_ident =
53944650Sraghuram 				    (uint64_t)ldcp->rxdp;
53951991Sheppo 				/* return the dring_ident in ack msg */
53961991Sheppo 				msg->dring_ident =
53974650Sraghuram 				    (uint64_t)ldcp->rxdp;
53981991Sheppo 
53991991Sheppo 				ldcp->peer_hparams.dring_ready = B_TRUE;
54001991Sheppo 			}
54011991Sheppo 			tagp->vio_subtype = VIO_SUBTYPE_ACK;
54021991Sheppo 		}
54031991Sheppo 		tagp->vio_sid = ldcp->local_sid;
54041991Sheppo 		/* send reply msg back to peer */
54052793Slm66018 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msg),
54062793Slm66018 		    B_FALSE);
54072793Slm66018 		if (rv != VGEN_SUCCESS) {
54082793Slm66018 			return (rv);
54091991Sheppo 		}
54101991Sheppo 
54111991Sheppo 		if (ack) {
54121991Sheppo 			ldcp->hstate |= DRING_ACK_SENT;
54134647Sraghuram 			DBG2(vgenp, ldcp, "DRING_ACK_SENT");
54141991Sheppo 		} else {
54154647Sraghuram 			DWARN(vgenp, ldcp, "DRING_NACK_SENT");
54162793Slm66018 			return (VGEN_FAILURE);
54171991Sheppo 		}
54181991Sheppo 
54191991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
54201991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
54211991Sheppo 		}
54221991Sheppo 
54231991Sheppo 		break;
54241991Sheppo 
54251991Sheppo 	case VIO_SUBTYPE_ACK:
54261991Sheppo 
54271991Sheppo 		ldcp->hstate |= DRING_ACK_RCVD;
54281991Sheppo 
54294647Sraghuram 		DBG2(vgenp, ldcp, "DRING_ACK_RCVD");
54301991Sheppo 
54311991Sheppo 		if (!(ldcp->local_hparams.dring_ready)) {
54321991Sheppo 			/* local dring is now ready */
54331991Sheppo 			ldcp->local_hparams.dring_ready = B_TRUE;
54341991Sheppo 
54351991Sheppo 			/* save dring_ident acked by peer */
54361991Sheppo 			ldcp->local_hparams.dring_ident =
54374650Sraghuram 			    msg->dring_ident;
54381991Sheppo 		}
54391991Sheppo 
54401991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
54411991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
54421991Sheppo 		}
54431991Sheppo 
54441991Sheppo 		break;
54451991Sheppo 
54461991Sheppo 	case VIO_SUBTYPE_NACK:
54471991Sheppo 
54484647Sraghuram 		DBG2(vgenp, ldcp, "DRING_NACK_RCVD");
54492793Slm66018 		return (VGEN_FAILURE);
54501991Sheppo 	}
54514647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
54522793Slm66018 	return (VGEN_SUCCESS);
54531991Sheppo }
54541991Sheppo 
54551991Sheppo /*
54561991Sheppo  * Handle a rdx info msg from the peer or an ACK/NACK
54571991Sheppo  * from the peer to a rdx info msg that we sent.
54581991Sheppo  */
54592793Slm66018 static int
54601991Sheppo vgen_handle_rdx_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
54611991Sheppo {
54622793Slm66018 	int rv = 0;
54634647Sraghuram 	vgen_t	*vgenp = LDC_TO_VGEN(ldcp);
54644647Sraghuram 
54654647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
54661991Sheppo 	if (ldcp->hphase != VH_PHASE3) {
54674647Sraghuram 		DWARN(vgenp, ldcp,
54684647Sraghuram 		    "Rcvd RDX_INFO Subtype (%d), Invalid Phase(%u)\n",
54694650Sraghuram 		    tagp->vio_subtype, ldcp->hphase);
54702793Slm66018 		return (VGEN_FAILURE);
54711991Sheppo 	}
54721991Sheppo 	switch (tagp->vio_subtype) {
54731991Sheppo 	case VIO_SUBTYPE_INFO:
54741991Sheppo 
54754647Sraghuram 		DBG2(vgenp, ldcp, "RDX_INFO_RCVD \n");
54761991Sheppo 		ldcp->hstate |= RDX_INFO_RCVD;
54771991Sheppo 
54781991Sheppo 		tagp->vio_subtype = VIO_SUBTYPE_ACK;
54791991Sheppo 		tagp->vio_sid = ldcp->local_sid;
54801991Sheppo 		/* send reply msg back to peer */
54812793Slm66018 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (vio_rdx_msg_t),
54822793Slm66018 		    B_FALSE);
54832793Slm66018 		if (rv != VGEN_SUCCESS) {
54842793Slm66018 			return (rv);
54851991Sheppo 		}
54861991Sheppo 
54871991Sheppo 		ldcp->hstate |= RDX_ACK_SENT;
54884647Sraghuram 		DBG2(vgenp, ldcp, "RDX_ACK_SENT \n");
54891991Sheppo 
54901991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
54911991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
54921991Sheppo 		}
54931991Sheppo 
54941991Sheppo 		break;
54951991Sheppo 
54961991Sheppo 	case VIO_SUBTYPE_ACK:
54971991Sheppo 
54981991Sheppo 		ldcp->hstate |= RDX_ACK_RCVD;
54991991Sheppo 
55004647Sraghuram 		DBG2(vgenp, ldcp, "RDX_ACK_RCVD \n");
55011991Sheppo 
55021991Sheppo 		if (vgen_handshake_done(ldcp) == VGEN_SUCCESS) {
55031991Sheppo 			vgen_handshake(vh_nextphase(ldcp));
55041991Sheppo 		}
55051991Sheppo 		break;
55061991Sheppo 
55071991Sheppo 	case VIO_SUBTYPE_NACK:
55081991Sheppo 
55094647Sraghuram 		DBG2(vgenp, ldcp, "RDX_NACK_RCVD \n");
55102793Slm66018 		return (VGEN_FAILURE);
55111991Sheppo 	}
55124647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
55132793Slm66018 	return (VGEN_SUCCESS);
55141991Sheppo }
55151991Sheppo 
55161991Sheppo /* Handle ACK/NACK from vsw to a set multicast msg that we sent */
55172793Slm66018 static int
55181991Sheppo vgen_handle_mcast_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
55191991Sheppo {
55201991Sheppo 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
55211991Sheppo 	vnet_mcast_msg_t *msgp = (vnet_mcast_msg_t *)tagp;
55221991Sheppo 	struct ether_addr *addrp;
55231991Sheppo 	int count;
55241991Sheppo 	int i;
55251991Sheppo 
55264647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
55271991Sheppo 	switch (tagp->vio_subtype) {
55281991Sheppo 
55291991Sheppo 	case VIO_SUBTYPE_INFO:
55301991Sheppo 
55311991Sheppo 		/* vnet shouldn't recv set mcast msg, only vsw handles it */
55324647Sraghuram 		DWARN(vgenp, ldcp, "rcvd SET_MCAST_INFO \n");
55331991Sheppo 		break;
55341991Sheppo 
55351991Sheppo 	case VIO_SUBTYPE_ACK:
55361991Sheppo 
55371991Sheppo 		/* success adding/removing multicast addr */
55384647Sraghuram 		DBG1(vgenp, ldcp, "rcvd SET_MCAST_ACK \n");
55391991Sheppo 		break;
55401991Sheppo 
55411991Sheppo 	case VIO_SUBTYPE_NACK:
55421991Sheppo 
55434647Sraghuram 		DWARN(vgenp, ldcp, "rcvd SET_MCAST_NACK \n");
55441991Sheppo 		if (!(msgp->set)) {
55451991Sheppo 			/* multicast remove request failed */
55461991Sheppo 			break;
55471991Sheppo 		}
55481991Sheppo 
55491991Sheppo 		/* multicast add request failed */
55501991Sheppo 		for (count = 0; count < msgp->count; count++) {
55511991Sheppo 			addrp = &(msgp->mca[count]);
55521991Sheppo 
55531991Sheppo 			/* delete address from the table */
55541991Sheppo 			for (i = 0; i < vgenp->mccount; i++) {
55551991Sheppo 				if (ether_cmp(addrp,
55561991Sheppo 				    &(vgenp->mctab[i])) == 0) {
55571991Sheppo 					if (vgenp->mccount > 1) {
55584647Sraghuram 						int t = vgenp->mccount - 1;
55591991Sheppo 						vgenp->mctab[i] =
55604650Sraghuram 						    vgenp->mctab[t];
55611991Sheppo 					}
55621991Sheppo 					vgenp->mccount--;
55631991Sheppo 					break;
55641991Sheppo 				}
55651991Sheppo 			}
55661991Sheppo 		}
55671991Sheppo 		break;
55681991Sheppo 
55691991Sheppo 	}
55704647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
55712793Slm66018 
55722793Slm66018 	return (VGEN_SUCCESS);
55731991Sheppo }
55741991Sheppo 
55751991Sheppo /* handler for control messages received from the peer ldc end-point */
55762793Slm66018 static int
55771991Sheppo vgen_handle_ctrlmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
55781991Sheppo {
55792793Slm66018 	int rv = 0;
55804647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
55814647Sraghuram 
55824647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
55831991Sheppo 	switch (tagp->vio_subtype_env) {
55841991Sheppo 
55851991Sheppo 	case VIO_VER_INFO:
55862793Slm66018 		rv = vgen_handle_version_negotiate(ldcp, tagp);
55871991Sheppo 		break;
55881991Sheppo 
55891991Sheppo 	case VIO_ATTR_INFO:
55902793Slm66018 		rv = vgen_handle_attr_info(ldcp, tagp);
55911991Sheppo 		break;
55921991Sheppo 
55931991Sheppo 	case VIO_DRING_REG:
55942793Slm66018 		rv = vgen_handle_dring_reg(ldcp, tagp);
55951991Sheppo 		break;
55961991Sheppo 
55971991Sheppo 	case VIO_RDX:
55982793Slm66018 		rv = vgen_handle_rdx_info(ldcp, tagp);
55991991Sheppo 		break;
56001991Sheppo 
56011991Sheppo 	case VNET_MCAST_INFO:
56022793Slm66018 		rv = vgen_handle_mcast_info(ldcp, tagp);
56031991Sheppo 		break;
56041991Sheppo 
56056495Sspeer 	case VIO_DDS_INFO:
56066495Sspeer 		rv = vgen_dds_rx(ldcp, tagp);
56076495Sspeer 		break;
56081991Sheppo 	}
56092793Slm66018 
56104647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
56112793Slm66018 	return (rv);
56121991Sheppo }
56131991Sheppo 
56141991Sheppo /* handler for data messages received from the peer ldc end-point */
56152793Slm66018 static int
56165935Ssb155480 vgen_handle_datamsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp, uint32_t msglen)
56171991Sheppo {
56182793Slm66018 	int rv = 0;
56194647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
56204647Sraghuram 
56214647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
56221991Sheppo 
56231991Sheppo 	if (ldcp->hphase != VH_DONE)
56242793Slm66018 		return (rv);
56255935Ssb155480 
56265935Ssb155480 	if (tagp->vio_subtype == VIO_SUBTYPE_INFO) {
56275935Ssb155480 		rv = vgen_check_datamsg_seq(ldcp, tagp);
56285935Ssb155480 		if (rv != 0) {
56295935Ssb155480 			return (rv);
56305935Ssb155480 		}
56315935Ssb155480 	}
56325935Ssb155480 
56331991Sheppo 	switch (tagp->vio_subtype_env) {
56341991Sheppo 	case VIO_DRING_DATA:
56354647Sraghuram 		rv = vgen_handle_dring_data(ldcp, tagp);
56361991Sheppo 		break;
56375935Ssb155480 
56385935Ssb155480 	case VIO_PKT_DATA:
56395935Ssb155480 		ldcp->rx_pktdata((void *)ldcp, (void *)tagp, msglen);
56405935Ssb155480 		break;
56411991Sheppo 	default:
56421991Sheppo 		break;
56431991Sheppo 	}
56441991Sheppo 
56454647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
56462793Slm66018 	return (rv);
56471991Sheppo }
56481991Sheppo 
56495935Ssb155480 /*
56505935Ssb155480  * dummy pkt data handler function for vnet protocol version 1.0
56515935Ssb155480  */
56525935Ssb155480 static void
56535935Ssb155480 vgen_handle_pkt_data_nop(void *arg1, void *arg2, uint32_t msglen)
56545935Ssb155480 {
56555935Ssb155480 	_NOTE(ARGUNUSED(arg1, arg2, msglen))
56565935Ssb155480 }
56575935Ssb155480 
56585935Ssb155480 /*
56595935Ssb155480  * This function handles raw pkt data messages received over the channel.
56605935Ssb155480  * Currently, only priority-eth-type frames are received through this mechanism.
56615935Ssb155480  * In this case, the frame(data) is present within the message itself which
56625935Ssb155480  * is copied into an mblk before sending it up the stack.
56635935Ssb155480  */
56645935Ssb155480 static void
56655935Ssb155480 vgen_handle_pkt_data(void *arg1, void *arg2, uint32_t msglen)
56665935Ssb155480 {
56675935Ssb155480 	vgen_ldc_t		*ldcp = (vgen_ldc_t *)arg1;
56685935Ssb155480 	vio_raw_data_msg_t	*pkt	= (vio_raw_data_msg_t *)arg2;
56695935Ssb155480 	uint32_t		size;
56705935Ssb155480 	mblk_t			*mp;
56715935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
56725935Ssb155480 	vgen_stats_t		*statsp = &ldcp->stats;
56736419Ssb155480 	vgen_hparams_t		*lp = &ldcp->local_hparams;
56746495Sspeer 	vio_net_rx_cb_t		vrx_cb;
56755935Ssb155480 
56765935Ssb155480 	ASSERT(MUTEX_HELD(&ldcp->cblock));
56775935Ssb155480 
56785935Ssb155480 	mutex_exit(&ldcp->cblock);
56795935Ssb155480 
56805935Ssb155480 	size = msglen - VIO_PKT_DATA_HDRSIZE;
56816419Ssb155480 	if (size < ETHERMIN || size > lp->mtu) {
56825935Ssb155480 		(void) atomic_inc_32(&statsp->rx_pri_fail);
56835935Ssb155480 		goto exit;
56845935Ssb155480 	}
56855935Ssb155480 
56865935Ssb155480 	mp = vio_multipool_allocb(&ldcp->vmp, size);
56875935Ssb155480 	if (mp == NULL) {
56885935Ssb155480 		mp = allocb(size, BPRI_MED);
56895935Ssb155480 		if (mp == NULL) {
56905935Ssb155480 			(void) atomic_inc_32(&statsp->rx_pri_fail);
56915935Ssb155480 			DWARN(vgenp, ldcp, "allocb failure, "
56925935Ssb155480 			    "unable to process priority frame\n");
56935935Ssb155480 			goto exit;
56945935Ssb155480 		}
56955935Ssb155480 	}
56965935Ssb155480 
56975935Ssb155480 	/* copy the frame from the payload of raw data msg into the mblk */
56985935Ssb155480 	bcopy(pkt->data, mp->b_rptr, size);
56995935Ssb155480 	mp->b_wptr = mp->b_rptr + size;
57005935Ssb155480 
57015935Ssb155480 	/* update stats */
57025935Ssb155480 	(void) atomic_inc_64(&statsp->rx_pri_packets);
57035935Ssb155480 	(void) atomic_add_64(&statsp->rx_pri_bytes, size);
57045935Ssb155480 
57056495Sspeer 	/* send up; call vrx_cb() as cblock is already released */
57066495Sspeer 	vrx_cb = ldcp->portp->vcb.vio_net_rx_cb;
57076495Sspeer 	vrx_cb(ldcp->portp->vhp, mp);
57085935Ssb155480 
57095935Ssb155480 exit:
57105935Ssb155480 	mutex_enter(&ldcp->cblock);
57115935Ssb155480 }
57125935Ssb155480 
57132793Slm66018 static int
57142336Snarayan vgen_send_dring_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp, uint32_t start,
57152336Snarayan     int32_t end, uint8_t pstate)
57162336Snarayan {
57174647Sraghuram 	int rv = 0;
57184647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
57192336Snarayan 	vio_dring_msg_t *msgp = (vio_dring_msg_t *)tagp;
57202336Snarayan 
57212336Snarayan 	tagp->vio_subtype = VIO_SUBTYPE_ACK;
57222336Snarayan 	tagp->vio_sid = ldcp->local_sid;
57232336Snarayan 	msgp->start_idx = start;
57242336Snarayan 	msgp->end_idx = end;
57252336Snarayan 	msgp->dring_process_state = pstate;
57262793Slm66018 	rv = vgen_sendmsg(ldcp, (caddr_t)tagp, sizeof (*msgp), B_FALSE);
57272793Slm66018 	if (rv != VGEN_SUCCESS) {
57284647Sraghuram 		DWARN(vgenp, ldcp, "vgen_sendmsg failed\n");
57292336Snarayan 	}
57302793Slm66018 	return (rv);
57312336Snarayan }
57322336Snarayan 
57332793Slm66018 static int
57344647Sraghuram vgen_handle_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
57351991Sheppo {
57362793Slm66018 	int rv = 0;
57374647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
57384647Sraghuram 
57394647Sraghuram 
57404647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
57411991Sheppo 	switch (tagp->vio_subtype) {
57421991Sheppo 
57431991Sheppo 	case VIO_SUBTYPE_INFO:
57441991Sheppo 		/*
57454647Sraghuram 		 * To reduce the locking contention, release the
57464647Sraghuram 		 * cblock here and re-acquire it once we are done
57474647Sraghuram 		 * receiving packets.
57481991Sheppo 		 */
57494647Sraghuram 		mutex_exit(&ldcp->cblock);
57504647Sraghuram 		mutex_enter(&ldcp->rxlock);
57514647Sraghuram 		rv = vgen_handle_dring_data_info(ldcp, tagp);
57524647Sraghuram 		mutex_exit(&ldcp->rxlock);
57534647Sraghuram 		mutex_enter(&ldcp->cblock);
57544647Sraghuram 		break;
57554647Sraghuram 
57564647Sraghuram 	case VIO_SUBTYPE_ACK:
57574647Sraghuram 		rv = vgen_handle_dring_data_ack(ldcp, tagp);
57584647Sraghuram 		break;
57594647Sraghuram 
57604647Sraghuram 	case VIO_SUBTYPE_NACK:
57614647Sraghuram 		rv = vgen_handle_dring_data_nack(ldcp, tagp);
57624647Sraghuram 		break;
57634647Sraghuram 	}
57644647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
57654647Sraghuram 	return (rv);
57664647Sraghuram }
57674647Sraghuram 
57684647Sraghuram static int
57694647Sraghuram vgen_handle_dring_data_info(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
57704647Sraghuram {
57714647Sraghuram 	uint32_t start;
57724647Sraghuram 	int32_t end;
57734647Sraghuram 	int rv = 0;
57744647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
57754647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
57764647Sraghuram #ifdef VGEN_HANDLE_LOST_PKTS
57775373Sraghuram 	vgen_stats_t *statsp = &ldcp->stats;
57784647Sraghuram 	uint32_t rxi;
57794647Sraghuram 	int n;
57804647Sraghuram #endif
57814647Sraghuram 
57824647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
57834647Sraghuram 
57844647Sraghuram 	start = dringmsg->start_idx;
57854647Sraghuram 	end = dringmsg->end_idx;
57864647Sraghuram 	/*
57874647Sraghuram 	 * received a data msg, which contains the start and end
57884647Sraghuram 	 * indices of the descriptors within the rx ring holding data,
57894647Sraghuram 	 * the seq_num of data packet corresponding to the start index,
57904647Sraghuram 	 * and the dring_ident.
57914647Sraghuram 	 * We can now read the contents of each of these descriptors
57924647Sraghuram 	 * and gather data from it.
57934647Sraghuram 	 */
57944647Sraghuram 	DBG1(vgenp, ldcp, "INFO: start(%d), end(%d)\n",
57954650Sraghuram 	    start, end);
57964647Sraghuram 
57974647Sraghuram 	/* validate rx start and end indeces */
57984647Sraghuram 	if (!(CHECK_RXI(start, ldcp)) || ((end != -1) &&
57994647Sraghuram 	    !(CHECK_RXI(end, ldcp)))) {
58004647Sraghuram 		DWARN(vgenp, ldcp, "Invalid Rx start(%d) or end(%d)\n",
58014647Sraghuram 		    start, end);
58024647Sraghuram 		/* drop the message if invalid index */
58034647Sraghuram 		return (rv);
58044647Sraghuram 	}
58054647Sraghuram 
58064647Sraghuram 	/* validate dring_ident */
58074647Sraghuram 	if (dringmsg->dring_ident != ldcp->peer_hparams.dring_ident) {
58084647Sraghuram 		DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
58094647Sraghuram 		    dringmsg->dring_ident);
58104647Sraghuram 		/* invalid dring_ident, drop the msg */
58114647Sraghuram 		return (rv);
58124647Sraghuram 	}
58131991Sheppo #ifdef DEBUG
58144647Sraghuram 	if (vgen_trigger_rxlost) {
58154647Sraghuram 		/* drop this msg to simulate lost pkts for debugging */
58164647Sraghuram 		vgen_trigger_rxlost = 0;
58174647Sraghuram 		return (rv);
58184647Sraghuram 	}
58191991Sheppo #endif
58201991Sheppo 
58211991Sheppo #ifdef	VGEN_HANDLE_LOST_PKTS
58221991Sheppo 
58234647Sraghuram 	/* receive start index doesn't match expected index */
58244647Sraghuram 	if (ldcp->next_rxi != start) {
58254647Sraghuram 		DWARN(vgenp, ldcp, "next_rxi(%d) != start(%d)\n",
58264647Sraghuram 		    ldcp->next_rxi, start);
58274647Sraghuram 
58284647Sraghuram 		/* calculate the number of pkts lost */
58294647Sraghuram 		if (start >= ldcp->next_rxi) {
58304647Sraghuram 			n = start - ldcp->next_rxi;
58314647Sraghuram 		} else  {
58324647Sraghuram 			n = ldcp->num_rxds - (ldcp->next_rxi - start);
58334647Sraghuram 		}
58344647Sraghuram 
58355935Ssb155480 		statsp->rx_lost_pkts += n;
58365935Ssb155480 		tagp->vio_subtype = VIO_SUBTYPE_NACK;
58375935Ssb155480 		tagp->vio_sid = ldcp->local_sid;
58385935Ssb155480 		/* indicate the range of lost descriptors */
58395935Ssb155480 		dringmsg->start_idx = ldcp->next_rxi;
58405935Ssb155480 		rxi = start;
58415935Ssb155480 		DECR_RXI(rxi, ldcp);
58425935Ssb155480 		dringmsg->end_idx = rxi;
58435935Ssb155480 		/* dring ident is left unchanged */
58445935Ssb155480 		rv = vgen_sendmsg(ldcp, (caddr_t)tagp,
58455935Ssb155480 		    sizeof (*dringmsg), B_FALSE);
58465935Ssb155480 		if (rv != VGEN_SUCCESS) {
58475935Ssb155480 			DWARN(vgenp, ldcp,
58485935Ssb155480 			    "vgen_sendmsg failed, stype:NACK\n");
58494647Sraghuram 			return (rv);
58501991Sheppo 		}
58511991Sheppo 		/*
58525935Ssb155480 		 * treat this range of descrs/pkts as dropped
58535935Ssb155480 		 * and set the new expected value of next_rxi
58545935Ssb155480 		 * and continue(below) to process from the new
58555935Ssb155480 		 * start index.
58561991Sheppo 		 */
58575935Ssb155480 		ldcp->next_rxi = start;
58584647Sraghuram 	}
58594647Sraghuram 
58604647Sraghuram #endif	/* VGEN_HANDLE_LOST_PKTS */
58614647Sraghuram 
58624647Sraghuram 	/* Now receive messages */
58634647Sraghuram 	rv = vgen_process_dring_data(ldcp, tagp);
58644647Sraghuram 
58654647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
58664647Sraghuram 	return (rv);
58674647Sraghuram }
58684647Sraghuram 
58694647Sraghuram static int
58704647Sraghuram vgen_process_dring_data(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
58714647Sraghuram {
58724647Sraghuram 	boolean_t set_ack_start = B_FALSE;
58734647Sraghuram 	uint32_t start;
58744647Sraghuram 	uint32_t ack_end;
58754647Sraghuram 	uint32_t next_rxi;
58764647Sraghuram 	uint32_t rxi;
58774647Sraghuram 	int count = 0;
58784647Sraghuram 	int rv = 0;
58794647Sraghuram 	uint32_t retries = 0;
58804647Sraghuram 	vgen_stats_t *statsp;
58816845Sha137994 	vnet_public_desc_t rxd;
58824647Sraghuram 	vio_dring_entry_hdr_t *hdrp;
58834647Sraghuram 	mblk_t *bp = NULL;
58844647Sraghuram 	mblk_t *bpt = NULL;
58854647Sraghuram 	uint32_t ack_start;
58864647Sraghuram 	boolean_t rxd_err = B_FALSE;
58874647Sraghuram 	mblk_t *mp = NULL;
58884647Sraghuram 	size_t nbytes;
58894647Sraghuram 	boolean_t ack_needed = B_FALSE;
58904647Sraghuram 	size_t nread;
58914647Sraghuram 	uint64_t off = 0;
58924647Sraghuram 	struct ether_header *ehp;
58934647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
58944647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
58957529SSriharsha.Basavapatna@Sun.COM 	vgen_hparams_t	*lp = &ldcp->local_hparams;
58964647Sraghuram 
58974647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
58984647Sraghuram 
58995373Sraghuram 	statsp = &ldcp->stats;
59004647Sraghuram 	start = dringmsg->start_idx;
59014647Sraghuram 
59024647Sraghuram 	/*
59034647Sraghuram 	 * start processing the descriptors from the specified
59044647Sraghuram 	 * start index, up to the index a descriptor is not ready
59054647Sraghuram 	 * to be processed or we process the entire descriptor ring
59064647Sraghuram 	 * and wrap around upto the start index.
59074647Sraghuram 	 */
59084647Sraghuram 
59094647Sraghuram 	/* need to set the start index of descriptors to be ack'd */
59104647Sraghuram 	set_ack_start = B_TRUE;
59114647Sraghuram 
59124647Sraghuram 	/* index upto which we have ack'd */
59134647Sraghuram 	ack_end = start;
59144647Sraghuram 	DECR_RXI(ack_end, ldcp);
59154647Sraghuram 
59164647Sraghuram 	next_rxi = rxi =  start;
59174647Sraghuram 	do {
59184647Sraghuram vgen_recv_retry:
59196845Sha137994 		rv = vnet_dring_entry_copy(&(ldcp->rxdp[rxi]), &rxd,
59206845Sha137994 		    ldcp->dring_mtype, ldcp->rx_dhandle, rxi, rxi);
59214647Sraghuram 		if (rv != 0) {
59224647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_dring_acquire() failed"
59234647Sraghuram 			    " rv(%d)\n", rv);
59244647Sraghuram 			statsp->ierrors++;
59254647Sraghuram 			return (rv);
59264647Sraghuram 		}
59274647Sraghuram 
59286845Sha137994 		hdrp = &rxd.hdr;
59294647Sraghuram 
59304647Sraghuram 		if (hdrp->dstate != VIO_DESC_READY) {
59314647Sraghuram 			/*
59325935Ssb155480 			 * Before waiting and retry here, send up
59335935Ssb155480 			 * the packets that are received already
59344647Sraghuram 			 */
59354647Sraghuram 			if (bp != NULL) {
59364647Sraghuram 				DTRACE_PROBE1(vgen_rcv_msgs, int, count);
59375935Ssb155480 				vgen_rx(ldcp, bp);
59384647Sraghuram 				count = 0;
59394647Sraghuram 				bp = bpt = NULL;
59404647Sraghuram 			}
59414647Sraghuram 			/*
59424647Sraghuram 			 * descriptor is not ready.
59434647Sraghuram 			 * retry descriptor acquire, stop processing
59444647Sraghuram 			 * after max # retries.
59454647Sraghuram 			 */
59464647Sraghuram 			if (retries == vgen_recv_retries)
59474647Sraghuram 				break;
59484647Sraghuram 			retries++;
59494647Sraghuram 			drv_usecwait(vgen_recv_delay);
59504647Sraghuram 			goto vgen_recv_retry;
59514647Sraghuram 		}
59524647Sraghuram 		retries = 0;
59534647Sraghuram 
59544647Sraghuram 		if (set_ack_start) {
59554647Sraghuram 			/*
59564647Sraghuram 			 * initialize the start index of the range
59574647Sraghuram 			 * of descriptors to be ack'd.
59584647Sraghuram 			 */
59594647Sraghuram 			ack_start = rxi;
59604647Sraghuram 			set_ack_start = B_FALSE;
59614647Sraghuram 		}
59624647Sraghuram 
59636845Sha137994 		if ((rxd.nbytes < ETHERMIN) ||
59647529SSriharsha.Basavapatna@Sun.COM 		    (rxd.nbytes > lp->mtu) ||
59656845Sha137994 		    (rxd.ncookies == 0) ||
59666845Sha137994 		    (rxd.ncookies > MAX_COOKIES)) {
59674647Sraghuram 			rxd_err = B_TRUE;
59684647Sraghuram 		} else {
59694647Sraghuram 			/*
59704647Sraghuram 			 * Try to allocate an mblk from the free pool
59714647Sraghuram 			 * of recv mblks for the channel.
59724647Sraghuram 			 * If this fails, use allocb().
59734647Sraghuram 			 */
59746845Sha137994 			nbytes = (VNET_IPALIGN + rxd.nbytes + 7) & ~7;
59757529SSriharsha.Basavapatna@Sun.COM 			if (nbytes > ldcp->max_rxpool_size) {
59766845Sha137994 				mp = allocb(VNET_IPALIGN + rxd.nbytes + 8,
59774647Sraghuram 				    BPRI_MED);
59787529SSriharsha.Basavapatna@Sun.COM 			} else {
59797529SSriharsha.Basavapatna@Sun.COM 				mp = vio_multipool_allocb(&ldcp->vmp, nbytes);
59807529SSriharsha.Basavapatna@Sun.COM 				if (mp == NULL) {
59817529SSriharsha.Basavapatna@Sun.COM 					statsp->rx_vio_allocb_fail++;
59827529SSriharsha.Basavapatna@Sun.COM 					/*
59837529SSriharsha.Basavapatna@Sun.COM 					 * Data buffer returned by allocb(9F)
59847529SSriharsha.Basavapatna@Sun.COM 					 * is 8byte aligned. We allocate extra
59857529SSriharsha.Basavapatna@Sun.COM 					 * 8 bytes to ensure size is multiple
59867529SSriharsha.Basavapatna@Sun.COM 					 * of 8 bytes for ldc_mem_copy().
59877529SSriharsha.Basavapatna@Sun.COM 					 */
59887529SSriharsha.Basavapatna@Sun.COM 					mp = allocb(VNET_IPALIGN +
59897529SSriharsha.Basavapatna@Sun.COM 					    rxd.nbytes + 8, BPRI_MED);
59907529SSriharsha.Basavapatna@Sun.COM 				}
59911991Sheppo 			}
59924647Sraghuram 		}
59934647Sraghuram 		if ((rxd_err) || (mp == NULL)) {
59944647Sraghuram 			/*
59954647Sraghuram 			 * rxd_err or allocb() failure,
59964647Sraghuram 			 * drop this packet, get next.
59974647Sraghuram 			 */
59984647Sraghuram 			if (rxd_err) {
59992793Slm66018 				statsp->ierrors++;
60004647Sraghuram 				rxd_err = B_FALSE;
60014647Sraghuram 			} else {
60024647Sraghuram 				statsp->rx_allocb_fail++;
60032793Slm66018 			}
60042793Slm66018 
60052793Slm66018 			ack_needed = hdrp->ack;
60064647Sraghuram 
60074647Sraghuram 			/* set descriptor done bit */
60086845Sha137994 			rv = vnet_dring_entry_set_dstate(&(ldcp->rxdp[rxi]),
60096845Sha137994 			    ldcp->dring_mtype, ldcp->rx_dhandle, rxi, rxi,
60106845Sha137994 			    VIO_DESC_DONE);
60112793Slm66018 			if (rv != 0) {
60124647Sraghuram 				DWARN(vgenp, ldcp,
60136845Sha137994 				    "vnet_dring_entry_set_dstate err rv(%d)\n",
60146845Sha137994 				    rv);
60154647Sraghuram 				return (rv);
60162793Slm66018 			}
60172336Snarayan 
60182793Slm66018 			if (ack_needed) {
60192793Slm66018 				ack_needed = B_FALSE;
60201991Sheppo 				/*
60212336Snarayan 				 * sender needs ack for this packet,
60222336Snarayan 				 * ack pkts upto this index.
60231991Sheppo 				 */
60242336Snarayan 				ack_end = rxi;
60252336Snarayan 
60262793Slm66018 				rv = vgen_send_dring_ack(ldcp, tagp,
60274647Sraghuram 				    ack_start, ack_end,
60284647Sraghuram 				    VIO_DP_ACTIVE);
60292793Slm66018 				if (rv != VGEN_SUCCESS) {
60302793Slm66018 					goto error_ret;
60312793Slm66018 				}
60322336Snarayan 
60332336Snarayan 				/* need to set new ack start index */
60342336Snarayan 				set_ack_start = B_TRUE;
60351991Sheppo 			}
60364647Sraghuram 			goto vgen_next_rxi;
60374647Sraghuram 		}
60384647Sraghuram 
60394647Sraghuram 		nread = nbytes;
60404647Sraghuram 		rv = ldc_mem_copy(ldcp->ldc_handle,
60414647Sraghuram 		    (caddr_t)mp->b_rptr, off, &nread,
60426845Sha137994 		    rxd.memcookie, rxd.ncookies, LDC_COPY_IN);
60434647Sraghuram 
60444647Sraghuram 		/* if ldc_mem_copy() failed */
60454647Sraghuram 		if (rv) {
60464647Sraghuram 			DWARN(vgenp, ldcp, "ldc_mem_copy err rv(%d)\n", rv);
60474647Sraghuram 			statsp->ierrors++;
60484647Sraghuram 			freemsg(mp);
60494647Sraghuram 			goto error_ret;
60504647Sraghuram 		}
60514647Sraghuram 
60524647Sraghuram 		ack_needed = hdrp->ack;
60536845Sha137994 
60546845Sha137994 		rv = vnet_dring_entry_set_dstate(&(ldcp->rxdp[rxi]),
60556845Sha137994 		    ldcp->dring_mtype, ldcp->rx_dhandle, rxi, rxi,
60566845Sha137994 		    VIO_DESC_DONE);
60574647Sraghuram 		if (rv != 0) {
60584647Sraghuram 			DWARN(vgenp, ldcp,
60596845Sha137994 			    "vnet_dring_entry_set_dstate err rv(%d)\n", rv);
60604647Sraghuram 			goto error_ret;
60614647Sraghuram 		}
60624647Sraghuram 
60634647Sraghuram 		mp->b_rptr += VNET_IPALIGN;
60644647Sraghuram 
60654647Sraghuram 		if (ack_needed) {
60664647Sraghuram 			ack_needed = B_FALSE;
60674647Sraghuram 			/*
60684647Sraghuram 			 * sender needs ack for this packet,
60694647Sraghuram 			 * ack pkts upto this index.
60704647Sraghuram 			 */
60714647Sraghuram 			ack_end = rxi;
60724647Sraghuram 
60734647Sraghuram 			rv = vgen_send_dring_ack(ldcp, tagp,
60744647Sraghuram 			    ack_start, ack_end, VIO_DP_ACTIVE);
60754647Sraghuram 			if (rv != VGEN_SUCCESS) {
60764647Sraghuram 				goto error_ret;
60771991Sheppo 			}
60781991Sheppo 
60794647Sraghuram 			/* need to set new ack start index */
60804647Sraghuram 			set_ack_start = B_TRUE;
60814647Sraghuram 		}
60824647Sraghuram 
60834647Sraghuram 		if (nread != nbytes) {
60844647Sraghuram 			DWARN(vgenp, ldcp,
60854647Sraghuram 			    "ldc_mem_copy nread(%lx), nbytes(%lx)\n",
60864647Sraghuram 			    nread, nbytes);
60874647Sraghuram 			statsp->ierrors++;
60884647Sraghuram 			freemsg(mp);
60894647Sraghuram 			goto vgen_next_rxi;
60904647Sraghuram 		}
60914647Sraghuram 
60924647Sraghuram 		/* point to the actual end of data */
60936845Sha137994 		mp->b_wptr = mp->b_rptr + rxd.nbytes;
60944647Sraghuram 
60954647Sraghuram 		/* update stats */
60964647Sraghuram 		statsp->ipackets++;
60976845Sha137994 		statsp->rbytes += rxd.nbytes;
60984647Sraghuram 		ehp = (struct ether_header *)mp->b_rptr;
60994647Sraghuram 		if (IS_BROADCAST(ehp))
61004647Sraghuram 			statsp->brdcstrcv++;
61014647Sraghuram 		else if (IS_MULTICAST(ehp))
61024647Sraghuram 			statsp->multircv++;
61034647Sraghuram 
61044647Sraghuram 		/* build a chain of received packets */
61054647Sraghuram 		if (bp == NULL) {
61064647Sraghuram 			/* first pkt */
61074647Sraghuram 			bp = mp;
61084647Sraghuram 			bpt = bp;
61094647Sraghuram 			bpt->b_next = NULL;
61104647Sraghuram 		} else {
61114647Sraghuram 			mp->b_next = NULL;
61124647Sraghuram 			bpt->b_next = mp;
61134647Sraghuram 			bpt = mp;
61144647Sraghuram 		}
61154647Sraghuram 
61164647Sraghuram 		if (count++ > vgen_chain_len) {
61174647Sraghuram 			DTRACE_PROBE1(vgen_rcv_msgs, int, count);
61185935Ssb155480 			vgen_rx(ldcp, bp);
61194647Sraghuram 			count = 0;
61204647Sraghuram 			bp = bpt = NULL;
61214647Sraghuram 		}
61222336Snarayan 
61232336Snarayan vgen_next_rxi:
61244647Sraghuram 		/* update end index of range of descrs to be ack'd */
61254647Sraghuram 		ack_end = rxi;
61264647Sraghuram 
61274647Sraghuram 		/* update the next index to be processed */
61284647Sraghuram 		INCR_RXI(next_rxi, ldcp);
61294647Sraghuram 		if (next_rxi == start) {
61302336Snarayan 			/*
61314647Sraghuram 			 * processed the entire descriptor ring upto
61324647Sraghuram 			 * the index at which we started.
61332336Snarayan 			 */
61342336Snarayan 			break;
61352336Snarayan 		}
61362336Snarayan 
61374647Sraghuram 		rxi = next_rxi;
61384647Sraghuram 
61394647Sraghuram 	_NOTE(CONSTCOND)
61404647Sraghuram 	} while (1);
61414647Sraghuram 
61424647Sraghuram 	/*
61434647Sraghuram 	 * send an ack message to peer indicating that we have stopped
61444647Sraghuram 	 * processing descriptors.
61454647Sraghuram 	 */
61464647Sraghuram 	if (set_ack_start) {
61472336Snarayan 		/*
61484647Sraghuram 		 * We have ack'd upto some index and we have not
61494647Sraghuram 		 * processed any descriptors beyond that index.
61504647Sraghuram 		 * Use the last ack'd index as both the start and
61514647Sraghuram 		 * end of range of descrs being ack'd.
61524647Sraghuram 		 * Note: This results in acking the last index twice
61534647Sraghuram 		 * and should be harmless.
61542336Snarayan 		 */
61554647Sraghuram 		ack_start = ack_end;
61564647Sraghuram 	}
61574647Sraghuram 
61584647Sraghuram 	rv = vgen_send_dring_ack(ldcp, tagp, ack_start, ack_end,
61594647Sraghuram 	    VIO_DP_STOPPED);
61604647Sraghuram 	if (rv != VGEN_SUCCESS) {
61614647Sraghuram 		goto error_ret;
61624647Sraghuram 	}
61634647Sraghuram 
61645935Ssb155480 	/* save new recv index of next dring msg */
61654647Sraghuram 	ldcp->next_rxi = next_rxi;
61664647Sraghuram 
61674647Sraghuram error_ret:
61685935Ssb155480 	/* send up packets received so far */
61694647Sraghuram 	if (bp != NULL) {
61704647Sraghuram 		DTRACE_PROBE1(vgen_rcv_msgs, int, count);
61715935Ssb155480 		vgen_rx(ldcp, bp);
61724647Sraghuram 		bp = bpt = NULL;
61734647Sraghuram 	}
61744647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
61754647Sraghuram 	return (rv);
61764647Sraghuram 
61774647Sraghuram }
61784647Sraghuram 
61794647Sraghuram static int
61804647Sraghuram vgen_handle_dring_data_ack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
61814647Sraghuram {
61824647Sraghuram 	int rv = 0;
61834647Sraghuram 	uint32_t start;
61844647Sraghuram 	int32_t end;
61854647Sraghuram 	uint32_t txi;
61864647Sraghuram 	boolean_t ready_txd = B_FALSE;
61874647Sraghuram 	vgen_stats_t *statsp;
61884647Sraghuram 	vgen_private_desc_t *tbufp;
61894647Sraghuram 	vnet_public_desc_t *txdp;
61904647Sraghuram 	vio_dring_entry_hdr_t *hdrp;
61914647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
61924647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
61934647Sraghuram 
61944647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
61954647Sraghuram 	start = dringmsg->start_idx;
61964647Sraghuram 	end = dringmsg->end_idx;
61975373Sraghuram 	statsp = &ldcp->stats;
61984647Sraghuram 
61994647Sraghuram 	/*
62004647Sraghuram 	 * received an ack corresponding to a specific descriptor for
62014647Sraghuram 	 * which we had set the ACK bit in the descriptor (during
62024647Sraghuram 	 * transmit). This enables us to reclaim descriptors.
62034647Sraghuram 	 */
62044647Sraghuram 
62054647Sraghuram 	DBG2(vgenp, ldcp, "ACK:  start(%d), end(%d)\n", start, end);
62064647Sraghuram 
62074647Sraghuram 	/* validate start and end indeces in the tx ack msg */
62084647Sraghuram 	if (!(CHECK_TXI(start, ldcp)) || !(CHECK_TXI(end, ldcp))) {
62094647Sraghuram 		/* drop the message if invalid index */
62104647Sraghuram 		DWARN(vgenp, ldcp, "Invalid Tx ack start(%d) or end(%d)\n",
62114647Sraghuram 		    start, end);
62124647Sraghuram 		return (rv);
62134647Sraghuram 	}
62144647Sraghuram 	/* validate dring_ident */
62154647Sraghuram 	if (dringmsg->dring_ident != ldcp->local_hparams.dring_ident) {
62164647Sraghuram 		/* invalid dring_ident, drop the msg */
62174647Sraghuram 		DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
62184647Sraghuram 		    dringmsg->dring_ident);
62194647Sraghuram 		return (rv);
62204647Sraghuram 	}
62214647Sraghuram 	statsp->dring_data_acks++;
62224647Sraghuram 
62234647Sraghuram 	/* reclaim descriptors that are done */
62244647Sraghuram 	vgen_reclaim(ldcp);
62254647Sraghuram 
62264647Sraghuram 	if (dringmsg->dring_process_state != VIO_DP_STOPPED) {
62272336Snarayan 		/*
62284647Sraghuram 		 * receiver continued processing descriptors after
62294647Sraghuram 		 * sending us the ack.
62302336Snarayan 		 */
62314647Sraghuram 		return (rv);
62324647Sraghuram 	}
62334647Sraghuram 
62344647Sraghuram 	statsp->dring_stopped_acks++;
62354647Sraghuram 
62364647Sraghuram 	/* receiver stopped processing descriptors */
62374647Sraghuram 	mutex_enter(&ldcp->wrlock);
62384647Sraghuram 	mutex_enter(&ldcp->tclock);
62394647Sraghuram 
62404647Sraghuram 	/*
62414647Sraghuram 	 * determine if there are any pending tx descriptors
62424647Sraghuram 	 * ready to be processed by the receiver(peer) and if so,
62434647Sraghuram 	 * send a message to the peer to restart receiving.
62444647Sraghuram 	 */
62454647Sraghuram 	ready_txd = B_FALSE;
62464647Sraghuram 
62474647Sraghuram 	/*
62484647Sraghuram 	 * using the end index of the descriptor range for which
62494647Sraghuram 	 * we received the ack, check if the next descriptor is
62504647Sraghuram 	 * ready.
62514647Sraghuram 	 */
62524647Sraghuram 	txi = end;
62534647Sraghuram 	INCR_TXI(txi, ldcp);
62544647Sraghuram 	tbufp = &ldcp->tbufp[txi];
62554647Sraghuram 	txdp = tbufp->descp;
62564647Sraghuram 	hdrp = &txdp->hdr;
62574647Sraghuram 	if (hdrp->dstate == VIO_DESC_READY) {
62584647Sraghuram 		ready_txd = B_TRUE;
62594647Sraghuram 	} else {
62604647Sraghuram 		/*
62614647Sraghuram 		 * descr next to the end of ack'd descr range is not
62624647Sraghuram 		 * ready.
62634647Sraghuram 		 * starting from the current reclaim index, check
62644647Sraghuram 		 * if any descriptor is ready.
62654647Sraghuram 		 */
62664647Sraghuram 
62674647Sraghuram 		txi = ldcp->cur_tbufp - ldcp->tbufp;
62682336Snarayan 		tbufp = &ldcp->tbufp[txi];
62694647Sraghuram 
62702336Snarayan 		txdp = tbufp->descp;
62712336Snarayan 		hdrp = &txdp->hdr;
62722336Snarayan 		if (hdrp->dstate == VIO_DESC_READY) {
62732336Snarayan 			ready_txd = B_TRUE;
62744647Sraghuram 		}
62754647Sraghuram 
62764647Sraghuram 	}
62774647Sraghuram 
62784647Sraghuram 	if (ready_txd) {
62794647Sraghuram 		/*
62804647Sraghuram 		 * we have tx descriptor(s) ready to be
62814647Sraghuram 		 * processed by the receiver.
62824647Sraghuram 		 * send a message to the peer with the start index
62834647Sraghuram 		 * of ready descriptors.
62844647Sraghuram 		 */
62854647Sraghuram 		rv = vgen_send_dring_data(ldcp, txi, -1);
62864647Sraghuram 		if (rv != VGEN_SUCCESS) {
62874647Sraghuram 			ldcp->resched_peer = B_TRUE;
62884647Sraghuram 			ldcp->resched_peer_txi = txi;
62894647Sraghuram 			mutex_exit(&ldcp->tclock);
62904647Sraghuram 			mutex_exit(&ldcp->wrlock);
62914647Sraghuram 			return (rv);
62922336Snarayan 		}
62934647Sraghuram 	} else {
62944647Sraghuram 		/*
62954647Sraghuram 		 * no ready tx descriptors. set the flag to send a
62964647Sraghuram 		 * message to peer when tx descriptors are ready in
62974647Sraghuram 		 * transmit routine.
62984647Sraghuram 		 */
62994647Sraghuram 		ldcp->resched_peer = B_TRUE;
63004647Sraghuram 		ldcp->resched_peer_txi = ldcp->cur_tbufp - ldcp->tbufp;
63014647Sraghuram 	}
63024647Sraghuram 
63034647Sraghuram 	mutex_exit(&ldcp->tclock);
63044647Sraghuram 	mutex_exit(&ldcp->wrlock);
63054647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
63064647Sraghuram 	return (rv);
63074647Sraghuram }
63084647Sraghuram 
63094647Sraghuram static int
63104647Sraghuram vgen_handle_dring_data_nack(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
63114647Sraghuram {
63124647Sraghuram 	int rv = 0;
63134647Sraghuram 	uint32_t start;
63144647Sraghuram 	int32_t end;
63154647Sraghuram 	uint32_t txi;
63164647Sraghuram 	vnet_public_desc_t *txdp;
63174647Sraghuram 	vio_dring_entry_hdr_t *hdrp;
63184647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
63194647Sraghuram 	vio_dring_msg_t *dringmsg = (vio_dring_msg_t *)tagp;
63204647Sraghuram 
63214647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
63224647Sraghuram 	start = dringmsg->start_idx;
63234647Sraghuram 	end = dringmsg->end_idx;
63244647Sraghuram 
63254647Sraghuram 	/*
63264647Sraghuram 	 * peer sent a NACK msg to indicate lost packets.
63274647Sraghuram 	 * The start and end correspond to the range of descriptors
63284647Sraghuram 	 * for which the peer didn't receive a dring data msg and so
63294647Sraghuram 	 * didn't receive the corresponding data.
63304647Sraghuram 	 */
63314647Sraghuram 	DWARN(vgenp, ldcp, "NACK: start(%d), end(%d)\n", start, end);
63324647Sraghuram 
63334647Sraghuram 	/* validate start and end indeces in the tx nack msg */
63344647Sraghuram 	if (!(CHECK_TXI(start, ldcp)) || !(CHECK_TXI(end, ldcp))) {
63354647Sraghuram 		/* drop the message if invalid index */
63364647Sraghuram 		DWARN(vgenp, ldcp, "Invalid Tx nack start(%d) or end(%d)\n",
63374647Sraghuram 		    start, end);
63384647Sraghuram 		return (rv);
63394647Sraghuram 	}
63404647Sraghuram 	/* validate dring_ident */
63414647Sraghuram 	if (dringmsg->dring_ident != ldcp->local_hparams.dring_ident) {
63424647Sraghuram 		/* invalid dring_ident, drop the msg */
63434647Sraghuram 		DWARN(vgenp, ldcp, "Invalid dring ident 0x%x\n",
63444647Sraghuram 		    dringmsg->dring_ident);
63454647Sraghuram 		return (rv);
63464647Sraghuram 	}
63474647Sraghuram 	mutex_enter(&ldcp->txlock);
63484647Sraghuram 	mutex_enter(&ldcp->tclock);
63494647Sraghuram 
63504647Sraghuram 	if (ldcp->next_tbufp == ldcp->cur_tbufp) {
63514647Sraghuram 		/* no busy descriptors, bogus nack ? */
63522336Snarayan 		mutex_exit(&ldcp->tclock);
63532336Snarayan 		mutex_exit(&ldcp->txlock);
63544647Sraghuram 		return (rv);
63554647Sraghuram 	}
63561991Sheppo 
63574647Sraghuram 	/* we just mark the descrs as done so they can be reclaimed */
63584647Sraghuram 	for (txi = start; txi <= end; ) {
63594647Sraghuram 		txdp = &(ldcp->txdp[txi]);
63604647Sraghuram 		hdrp = &txdp->hdr;
63614647Sraghuram 		if (hdrp->dstate == VIO_DESC_READY)
63624647Sraghuram 			hdrp->dstate = VIO_DESC_DONE;
63634647Sraghuram 		INCR_TXI(txi, ldcp);
63644647Sraghuram 	}
63654647Sraghuram 	mutex_exit(&ldcp->tclock);
63664647Sraghuram 	mutex_exit(&ldcp->txlock);
63674647Sraghuram 	DBG1(vgenp, ldcp, "exit rv(%d)\n", rv);
63682793Slm66018 	return (rv);
63691991Sheppo }
63701991Sheppo 
63711991Sheppo static void
63721991Sheppo vgen_reclaim(vgen_ldc_t *ldcp)
63731991Sheppo {
63742336Snarayan 	mutex_enter(&ldcp->tclock);
63752336Snarayan 
63761991Sheppo 	vgen_reclaim_dring(ldcp);
63771991Sheppo 	ldcp->reclaim_lbolt = ddi_get_lbolt();
63782336Snarayan 
63791991Sheppo 	mutex_exit(&ldcp->tclock);
63801991Sheppo }
63811991Sheppo 
63821991Sheppo /*
63831991Sheppo  * transmit reclaim function. starting from the current reclaim index
63841991Sheppo  * look for descriptors marked DONE and reclaim the descriptor and the
63851991Sheppo  * corresponding buffers (tbuf).
63861991Sheppo  */
63871991Sheppo static void
63881991Sheppo vgen_reclaim_dring(vgen_ldc_t *ldcp)
63891991Sheppo {
63905022Sraghuram 	int count = 0;
63911991Sheppo 	vnet_public_desc_t *txdp;
63921991Sheppo 	vgen_private_desc_t *tbufp;
63931991Sheppo 	vio_dring_entry_hdr_t	*hdrp;
63941991Sheppo 
63951991Sheppo #ifdef DEBUG
63961991Sheppo 	if (vgen_trigger_txtimeout)
63971991Sheppo 		return;
63981991Sheppo #endif
63991991Sheppo 
64001991Sheppo 	tbufp = ldcp->cur_tbufp;
64011991Sheppo 	txdp = tbufp->descp;
64021991Sheppo 	hdrp = &txdp->hdr;
64031991Sheppo 
64041991Sheppo 	while ((hdrp->dstate == VIO_DESC_DONE) &&
64051991Sheppo 	    (tbufp != ldcp->next_tbufp)) {
64061991Sheppo 		tbufp->flags = VGEN_PRIV_DESC_FREE;
64071991Sheppo 		hdrp->dstate = VIO_DESC_FREE;
64081991Sheppo 		hdrp->ack = B_FALSE;
64091991Sheppo 
64101991Sheppo 		tbufp = NEXTTBUF(ldcp, tbufp);
64111991Sheppo 		txdp = tbufp->descp;
64121991Sheppo 		hdrp = &txdp->hdr;
64135022Sraghuram 		count++;
64141991Sheppo 	}
64151991Sheppo 
64161991Sheppo 	ldcp->cur_tbufp = tbufp;
64171991Sheppo 
64181991Sheppo 	/*
64191991Sheppo 	 * Check if mac layer should be notified to restart transmissions
64201991Sheppo 	 */
64215022Sraghuram 	if ((ldcp->need_resched) && (count > 0)) {
64226495Sspeer 		vio_net_tx_update_t vtx_update =
64236495Sspeer 		    ldcp->portp->vcb.vio_net_tx_update;
64246495Sspeer 
64251991Sheppo 		ldcp->need_resched = B_FALSE;
64266495Sspeer 		vtx_update(ldcp->portp->vhp);
64271991Sheppo 	}
64281991Sheppo }
64291991Sheppo 
64301991Sheppo /* return the number of pending transmits for the channel */
64311991Sheppo static int
64321991Sheppo vgen_num_txpending(vgen_ldc_t *ldcp)
64331991Sheppo {
64341991Sheppo 	int n;
64351991Sheppo 
64361991Sheppo 	if (ldcp->next_tbufp >= ldcp->cur_tbufp) {
64371991Sheppo 		n = ldcp->next_tbufp - ldcp->cur_tbufp;
64381991Sheppo 	} else  {
64391991Sheppo 		/* cur_tbufp > next_tbufp */
64401991Sheppo 		n = ldcp->num_txds - (ldcp->cur_tbufp - ldcp->next_tbufp);
64411991Sheppo 	}
64421991Sheppo 
64431991Sheppo 	return (n);
64441991Sheppo }
64451991Sheppo 
64461991Sheppo /* determine if the transmit descriptor ring is full */
64471991Sheppo static int
64481991Sheppo vgen_tx_dring_full(vgen_ldc_t *ldcp)
64491991Sheppo {
64501991Sheppo 	vgen_private_desc_t	*tbufp;
64511991Sheppo 	vgen_private_desc_t	*ntbufp;
64521991Sheppo 
64531991Sheppo 	tbufp = ldcp->next_tbufp;
64541991Sheppo 	ntbufp = NEXTTBUF(ldcp, tbufp);
64551991Sheppo 	if (ntbufp == ldcp->cur_tbufp) { /* out of tbufs/txds */
64561991Sheppo 		return (VGEN_SUCCESS);
64571991Sheppo 	}
64581991Sheppo 	return (VGEN_FAILURE);
64591991Sheppo }
64601991Sheppo 
64611991Sheppo /* determine if timeout condition has occured */
64621991Sheppo static int
64631991Sheppo vgen_ldc_txtimeout(vgen_ldc_t *ldcp)
64641991Sheppo {
64651991Sheppo 	if (((ddi_get_lbolt() - ldcp->reclaim_lbolt) >
64664650Sraghuram 	    drv_usectohz(vnet_ldcwd_txtimeout * 1000)) &&
64674650Sraghuram 	    (vnet_ldcwd_txtimeout) &&
64684650Sraghuram 	    (vgen_tx_dring_full(ldcp) == VGEN_SUCCESS)) {
64691991Sheppo 		return (VGEN_SUCCESS);
64701991Sheppo 	} else {
64711991Sheppo 		return (VGEN_FAILURE);
64721991Sheppo 	}
64731991Sheppo }
64741991Sheppo 
64751991Sheppo /* transmit watchdog timeout handler */
64761991Sheppo static void
64771991Sheppo vgen_ldc_watchdog(void *arg)
64781991Sheppo {
64791991Sheppo 	vgen_ldc_t *ldcp;
64802336Snarayan 	vgen_t *vgenp;
64811991Sheppo 	int rv;
64821991Sheppo 
64831991Sheppo 	ldcp = (vgen_ldc_t *)arg;
64842336Snarayan 	vgenp = LDC_TO_VGEN(ldcp);
64851991Sheppo 
64861991Sheppo 	rv = vgen_ldc_txtimeout(ldcp);
64871991Sheppo 	if (rv == VGEN_SUCCESS) {
64884647Sraghuram 		DWARN(vgenp, ldcp, "transmit timeout\n");
64891991Sheppo #ifdef DEBUG
64901991Sheppo 		if (vgen_trigger_txtimeout) {
64911991Sheppo 			/* tx timeout triggered for debugging */
64921991Sheppo 			vgen_trigger_txtimeout = 0;
64931991Sheppo 		}
64941991Sheppo #endif
64951991Sheppo 		mutex_enter(&ldcp->cblock);
64962793Slm66018 		ldcp->need_ldc_reset = B_TRUE;
64973653Snarayan 		vgen_handshake_retry(ldcp);
64981991Sheppo 		mutex_exit(&ldcp->cblock);
64991991Sheppo 		if (ldcp->need_resched) {
65006495Sspeer 			vio_net_tx_update_t vtx_update =
65016495Sspeer 			    ldcp->portp->vcb.vio_net_tx_update;
65026495Sspeer 
65031991Sheppo 			ldcp->need_resched = B_FALSE;
65046495Sspeer 			vtx_update(ldcp->portp->vhp);
65051991Sheppo 		}
65061991Sheppo 	}
65071991Sheppo 
65081991Sheppo 	ldcp->wd_tid = timeout(vgen_ldc_watchdog, (caddr_t)ldcp,
65091991Sheppo 	    drv_usectohz(vnet_ldcwd_interval * 1000));
65101991Sheppo }
65111991Sheppo 
65121991Sheppo /* handler for error messages received from the peer ldc end-point */
65131991Sheppo static void
65141991Sheppo vgen_handle_errmsg(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
65151991Sheppo {
65161991Sheppo 	_NOTE(ARGUNUSED(ldcp, tagp))
65171991Sheppo }
65181991Sheppo 
65195935Ssb155480 static int
65205935Ssb155480 vgen_check_datamsg_seq(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
65215935Ssb155480 {
65225935Ssb155480 	vio_raw_data_msg_t	*rmsg;
65235935Ssb155480 	vio_dring_msg_t		*dmsg;
65245935Ssb155480 	uint64_t		seq_num;
65255935Ssb155480 	vgen_t			*vgenp = LDC_TO_VGEN(ldcp);
65265935Ssb155480 
65275935Ssb155480 	if (tagp->vio_subtype_env == VIO_DRING_DATA) {
65285935Ssb155480 		dmsg = (vio_dring_msg_t *)tagp;
65295935Ssb155480 		seq_num = dmsg->seq_num;
65305935Ssb155480 	} else if (tagp->vio_subtype_env == VIO_PKT_DATA) {
65315935Ssb155480 		rmsg = (vio_raw_data_msg_t *)tagp;
65325935Ssb155480 		seq_num = rmsg->seq_num;
65335935Ssb155480 	} else {
65345935Ssb155480 		return (EINVAL);
65355935Ssb155480 	}
65365935Ssb155480 
65375935Ssb155480 	if (seq_num != ldcp->next_rxseq) {
65385935Ssb155480 
65395935Ssb155480 		/* seqnums don't match */
65405935Ssb155480 		DWARN(vgenp, ldcp,
65415935Ssb155480 		    "next_rxseq(0x%lx) != seq_num(0x%lx)\n",
65425935Ssb155480 		    ldcp->next_rxseq, seq_num);
65435935Ssb155480 
65445935Ssb155480 		ldcp->need_ldc_reset = B_TRUE;
65455935Ssb155480 		return (EINVAL);
65465935Ssb155480 
65475935Ssb155480 	}
65485935Ssb155480 
65495935Ssb155480 	ldcp->next_rxseq++;
65505935Ssb155480 
65515935Ssb155480 	return (0);
65525935Ssb155480 }
65535935Ssb155480 
65541991Sheppo /* Check if the session id in the received message is valid */
65551991Sheppo static int
65561991Sheppo vgen_check_sid(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
65571991Sheppo {
65584647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
65594647Sraghuram 
65601991Sheppo 	if (tagp->vio_sid != ldcp->peer_sid) {
65614647Sraghuram 		DWARN(vgenp, ldcp, "sid mismatch: expected(%x), rcvd(%x)\n",
65624647Sraghuram 		    ldcp->peer_sid, tagp->vio_sid);
65631991Sheppo 		return (VGEN_FAILURE);
65641991Sheppo 	}
65651991Sheppo 	else
65661991Sheppo 		return (VGEN_SUCCESS);
65671991Sheppo }
65681991Sheppo 
65691991Sheppo static caddr_t
65701991Sheppo vgen_print_ethaddr(uint8_t *a, char *ebuf)
65711991Sheppo {
65721991Sheppo 	(void) sprintf(ebuf,
65734650Sraghuram 	    "%x:%x:%x:%x:%x:%x", a[0], a[1], a[2], a[3], a[4], a[5]);
65741991Sheppo 	return (ebuf);
65751991Sheppo }
65761991Sheppo 
65771991Sheppo /* Handshake watchdog timeout handler */
65781991Sheppo static void
65791991Sheppo vgen_hwatchdog(void *arg)
65801991Sheppo {
65811991Sheppo 	vgen_ldc_t *ldcp = (vgen_ldc_t *)arg;
65824647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
65834647Sraghuram 
65844647Sraghuram 	DWARN(vgenp, ldcp,
65854647Sraghuram 	    "handshake timeout ldc(%lx) phase(%x) state(%x)\n",
65864647Sraghuram 	    ldcp->hphase, ldcp->hstate);
65871991Sheppo 
65881991Sheppo 	mutex_enter(&ldcp->cblock);
65893653Snarayan 	if (ldcp->cancel_htid) {
65903653Snarayan 		ldcp->cancel_htid = 0;
65913653Snarayan 		mutex_exit(&ldcp->cblock);
65923653Snarayan 		return;
65933653Snarayan 	}
65941991Sheppo 	ldcp->htid = 0;
65952841Snarayan 	ldcp->need_ldc_reset = B_TRUE;
65961991Sheppo 	vgen_handshake_retry(ldcp);
65971991Sheppo 	mutex_exit(&ldcp->cblock);
65981991Sheppo }
65991991Sheppo 
66001991Sheppo static void
66011991Sheppo vgen_print_hparams(vgen_hparams_t *hp)
66021991Sheppo {
66031991Sheppo 	uint8_t	addr[6];
66041991Sheppo 	char	ea[6];
66051991Sheppo 	ldc_mem_cookie_t *dc;
66061991Sheppo 
66071991Sheppo 	cmn_err(CE_CONT, "version_info:\n");
66081991Sheppo 	cmn_err(CE_CONT,
66091991Sheppo 	    "\tver_major: %d, ver_minor: %d, dev_class: %d\n",
66101991Sheppo 	    hp->ver_major, hp->ver_minor, hp->dev_class);
66111991Sheppo 
66125462Swentaoy 	vnet_macaddr_ultostr(hp->addr, addr);
66131991Sheppo 	cmn_err(CE_CONT, "attr_info:\n");
66141991Sheppo 	cmn_err(CE_CONT, "\tMTU: %lx, addr: %s\n", hp->mtu,
66151991Sheppo 	    vgen_print_ethaddr(addr, ea));
66161991Sheppo 	cmn_err(CE_CONT,
66171991Sheppo 	    "\taddr_type: %x, xfer_mode: %x, ack_freq: %x\n",
66181991Sheppo 	    hp->addr_type, hp->xfer_mode, hp->ack_freq);
66191991Sheppo 
66201991Sheppo 	dc = &hp->dring_cookie;
66211991Sheppo 	cmn_err(CE_CONT, "dring_info:\n");
66221991Sheppo 	cmn_err(CE_CONT,
66231991Sheppo 	    "\tlength: %d, dsize: %d\n", hp->num_desc, hp->desc_size);
66241991Sheppo 	cmn_err(CE_CONT,
66251991Sheppo 	    "\tldc_addr: 0x%lx, ldc_size: %ld\n",
66261991Sheppo 	    dc->addr, dc->size);
66271991Sheppo 	cmn_err(CE_CONT, "\tdring_ident: 0x%lx\n", hp->dring_ident);
66281991Sheppo }
66291991Sheppo 
66301991Sheppo static void
66311991Sheppo vgen_print_ldcinfo(vgen_ldc_t *ldcp)
66321991Sheppo {
66331991Sheppo 	vgen_hparams_t *hp;
66341991Sheppo 
66351991Sheppo 	cmn_err(CE_CONT, "Channel Information:\n");
66361991Sheppo 	cmn_err(CE_CONT,
66371991Sheppo 	    "\tldc_id: 0x%lx, ldc_status: 0x%x\n",
66381991Sheppo 	    ldcp->ldc_id, ldcp->ldc_status);
66391991Sheppo 	cmn_err(CE_CONT,
66401991Sheppo 	    "\tlocal_sid: 0x%x, peer_sid: 0x%x\n",
66411991Sheppo 	    ldcp->local_sid, ldcp->peer_sid);
66421991Sheppo 	cmn_err(CE_CONT,
66431991Sheppo 	    "\thphase: 0x%x, hstate: 0x%x\n",
66441991Sheppo 	    ldcp->hphase, ldcp->hstate);
66451991Sheppo 
66461991Sheppo 	cmn_err(CE_CONT, "Local handshake params:\n");
66471991Sheppo 	hp = &ldcp->local_hparams;
66481991Sheppo 	vgen_print_hparams(hp);
66491991Sheppo 
66501991Sheppo 	cmn_err(CE_CONT, "Peer handshake params:\n");
66511991Sheppo 	hp = &ldcp->peer_hparams;
66521991Sheppo 	vgen_print_hparams(hp);
66531991Sheppo }
66544647Sraghuram 
66554647Sraghuram /*
66565935Ssb155480  * Send received packets up the stack.
66574647Sraghuram  */
66584647Sraghuram static void
66595935Ssb155480 vgen_rx(vgen_ldc_t *ldcp, mblk_t *bp)
66604647Sraghuram {
66616495Sspeer 	vio_net_rx_cb_t vrx_cb = ldcp->portp->vcb.vio_net_rx_cb;
66624647Sraghuram 
66634647Sraghuram 	if (ldcp->rcv_thread != NULL) {
66645935Ssb155480 		ASSERT(MUTEX_HELD(&ldcp->rxlock));
66655935Ssb155480 		mutex_exit(&ldcp->rxlock);
66664647Sraghuram 	} else {
66675935Ssb155480 		ASSERT(MUTEX_HELD(&ldcp->cblock));
66685935Ssb155480 		mutex_exit(&ldcp->cblock);
66695935Ssb155480 	}
66705935Ssb155480 
66716495Sspeer 	vrx_cb(ldcp->portp->vhp, bp);
66725935Ssb155480 
66734647Sraghuram 	if (ldcp->rcv_thread != NULL) {
66745935Ssb155480 		mutex_enter(&ldcp->rxlock);
66755935Ssb155480 	} else {
66765935Ssb155480 		mutex_enter(&ldcp->cblock);
66775935Ssb155480 	}
66784647Sraghuram }
66794647Sraghuram 
66804647Sraghuram /*
66814647Sraghuram  * vgen_ldc_rcv_worker -- A per LDC worker thread to receive data.
66824647Sraghuram  * This thread is woken up by the LDC interrupt handler to process
66834647Sraghuram  * LDC packets and receive data.
66844647Sraghuram  */
66854647Sraghuram static void
66864647Sraghuram vgen_ldc_rcv_worker(void *arg)
66874647Sraghuram {
66884647Sraghuram 	callb_cpr_t	cprinfo;
66894647Sraghuram 	vgen_ldc_t *ldcp = (vgen_ldc_t *)arg;
66904647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
66914647Sraghuram 
66924647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
66934647Sraghuram 	CALLB_CPR_INIT(&cprinfo, &ldcp->rcv_thr_lock, callb_generic_cpr,
66944650Sraghuram 	    "vnet_rcv_thread");
66954647Sraghuram 	mutex_enter(&ldcp->rcv_thr_lock);
66964647Sraghuram 	while (!(ldcp->rcv_thr_flags & VGEN_WTHR_STOP)) {
66974647Sraghuram 
66984647Sraghuram 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
66994647Sraghuram 		/*
67004647Sraghuram 		 * Wait until the data is received or a stop
67014647Sraghuram 		 * request is received.
67024647Sraghuram 		 */
67034647Sraghuram 		while (!(ldcp->rcv_thr_flags &
67044647Sraghuram 		    (VGEN_WTHR_DATARCVD | VGEN_WTHR_STOP))) {
67054647Sraghuram 			cv_wait(&ldcp->rcv_thr_cv, &ldcp->rcv_thr_lock);
67064647Sraghuram 		}
67074647Sraghuram 		CALLB_CPR_SAFE_END(&cprinfo, &ldcp->rcv_thr_lock)
67084647Sraghuram 
67094647Sraghuram 		/*
67104647Sraghuram 		 * First process the stop request.
67114647Sraghuram 		 */
67124647Sraghuram 		if (ldcp->rcv_thr_flags & VGEN_WTHR_STOP) {
67134647Sraghuram 			DBG2(vgenp, ldcp, "stopped\n");
67144647Sraghuram 			break;
67154647Sraghuram 		}
67164647Sraghuram 		ldcp->rcv_thr_flags &= ~VGEN_WTHR_DATARCVD;
67178623SSriharsha.Basavapatna@Sun.COM 		ldcp->rcv_thr_flags |= VGEN_WTHR_PROCESSING;
67184647Sraghuram 		mutex_exit(&ldcp->rcv_thr_lock);
67194647Sraghuram 		DBG2(vgenp, ldcp, "calling vgen_handle_evt_read\n");
67204647Sraghuram 		vgen_handle_evt_read(ldcp);
67214647Sraghuram 		mutex_enter(&ldcp->rcv_thr_lock);
67228623SSriharsha.Basavapatna@Sun.COM 		ldcp->rcv_thr_flags &= ~VGEN_WTHR_PROCESSING;
67234647Sraghuram 	}
67244647Sraghuram 
67254647Sraghuram 	/*
67264647Sraghuram 	 * Update the run status and wakeup the thread that
67274647Sraghuram 	 * has sent the stop request.
67284647Sraghuram 	 */
67299217SWentao.Yang@Sun.COM 	ldcp->rcv_thr_flags &= ~VGEN_WTHR_STOP;
67309217SWentao.Yang@Sun.COM 	ldcp->rcv_thread = NULL;
67314647Sraghuram 	CALLB_CPR_EXIT(&cprinfo);
67329217SWentao.Yang@Sun.COM 
67334647Sraghuram 	thread_exit();
67344647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
67354647Sraghuram }
67364647Sraghuram 
67374647Sraghuram /* vgen_stop_rcv_thread -- Co-ordinate with receive thread to stop it */
67384647Sraghuram static void
67394647Sraghuram vgen_stop_rcv_thread(vgen_ldc_t *ldcp)
67404647Sraghuram {
67419217SWentao.Yang@Sun.COM 	kt_did_t	tid = 0;
67424647Sraghuram 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
67434647Sraghuram 
67444647Sraghuram 	DBG1(vgenp, ldcp, "enter\n");
67454647Sraghuram 	/*
67464647Sraghuram 	 * Send a stop request by setting the stop flag and
67474647Sraghuram 	 * wait until the receive thread stops.
67484647Sraghuram 	 */
67494647Sraghuram 	mutex_enter(&ldcp->rcv_thr_lock);
67509217SWentao.Yang@Sun.COM 	if (ldcp->rcv_thread != NULL) {
67519217SWentao.Yang@Sun.COM 		tid = ldcp->rcv_thread->t_did;
67524647Sraghuram 		ldcp->rcv_thr_flags |= VGEN_WTHR_STOP;
67534647Sraghuram 		cv_signal(&ldcp->rcv_thr_cv);
67544647Sraghuram 	}
67554647Sraghuram 	mutex_exit(&ldcp->rcv_thr_lock);
67569217SWentao.Yang@Sun.COM 
67579217SWentao.Yang@Sun.COM 	if (tid != 0) {
67589217SWentao.Yang@Sun.COM 		thread_join(tid);
67599217SWentao.Yang@Sun.COM 	}
67604647Sraghuram 	DBG1(vgenp, ldcp, "exit\n");
67614647Sraghuram }
67624647Sraghuram 
67636495Sspeer /*
67648623SSriharsha.Basavapatna@Sun.COM  * Wait for the channel rx-queue to be drained by allowing the receive
67658623SSriharsha.Basavapatna@Sun.COM  * worker thread to read all messages from the rx-queue of the channel.
67668623SSriharsha.Basavapatna@Sun.COM  * Assumption: further callbacks are disabled at this time.
67678623SSriharsha.Basavapatna@Sun.COM  */
67688623SSriharsha.Basavapatna@Sun.COM static void
67698623SSriharsha.Basavapatna@Sun.COM vgen_drain_rcv_thread(vgen_ldc_t *ldcp)
67708623SSriharsha.Basavapatna@Sun.COM {
67718623SSriharsha.Basavapatna@Sun.COM 	clock_t	tm;
67728623SSriharsha.Basavapatna@Sun.COM 	clock_t	wt;
67738623SSriharsha.Basavapatna@Sun.COM 	clock_t	rv;
67748623SSriharsha.Basavapatna@Sun.COM 
67758623SSriharsha.Basavapatna@Sun.COM 	/*
67768623SSriharsha.Basavapatna@Sun.COM 	 * If there is data in ldc rx queue, wait until the rx
67778623SSriharsha.Basavapatna@Sun.COM 	 * worker thread runs and drains all msgs in the queue.
67788623SSriharsha.Basavapatna@Sun.COM 	 */
67798623SSriharsha.Basavapatna@Sun.COM 	wt = drv_usectohz(MILLISEC);
67808623SSriharsha.Basavapatna@Sun.COM 
67818623SSriharsha.Basavapatna@Sun.COM 	mutex_enter(&ldcp->rcv_thr_lock);
67828623SSriharsha.Basavapatna@Sun.COM 
67838623SSriharsha.Basavapatna@Sun.COM 	tm = ddi_get_lbolt() + wt;
67848623SSriharsha.Basavapatna@Sun.COM 
67858623SSriharsha.Basavapatna@Sun.COM 	/*
67868623SSriharsha.Basavapatna@Sun.COM 	 * We need to check both bits - DATARCVD and PROCESSING, to be cleared.
67878623SSriharsha.Basavapatna@Sun.COM 	 * If DATARCVD is set, that means the callback has signalled the worker
67888623SSriharsha.Basavapatna@Sun.COM 	 * thread, but the worker hasn't started processing yet. If PROCESSING
67898623SSriharsha.Basavapatna@Sun.COM 	 * is set, that means the thread is awake and processing. Note that the
67908623SSriharsha.Basavapatna@Sun.COM 	 * DATARCVD state can only be seen once, as the assumption is that
67918623SSriharsha.Basavapatna@Sun.COM 	 * further callbacks have been disabled at this point.
67928623SSriharsha.Basavapatna@Sun.COM 	 */
67938623SSriharsha.Basavapatna@Sun.COM 	while (ldcp->rcv_thr_flags &
67948623SSriharsha.Basavapatna@Sun.COM 	    (VGEN_WTHR_DATARCVD | VGEN_WTHR_PROCESSING)) {
67958623SSriharsha.Basavapatna@Sun.COM 		rv = cv_timedwait(&ldcp->rcv_thr_cv, &ldcp->rcv_thr_lock, tm);
67968623SSriharsha.Basavapatna@Sun.COM 		if (rv == -1) {	/* timeout */
67978623SSriharsha.Basavapatna@Sun.COM 			/*
67988623SSriharsha.Basavapatna@Sun.COM 			 * Note that the only way we return is due to a timeout;
67998623SSriharsha.Basavapatna@Sun.COM 			 * we set the new time to wait, before we go back and
68008623SSriharsha.Basavapatna@Sun.COM 			 * check the condition. The other(unlikely) possibility
68018623SSriharsha.Basavapatna@Sun.COM 			 * is a premature wakeup(see cv_timedwait(9F)) in which
68028623SSriharsha.Basavapatna@Sun.COM 			 * case we just continue to use the same time to wait.
68038623SSriharsha.Basavapatna@Sun.COM 			 */
68048623SSriharsha.Basavapatna@Sun.COM 			tm = ddi_get_lbolt() + wt;
68058623SSriharsha.Basavapatna@Sun.COM 		}
68068623SSriharsha.Basavapatna@Sun.COM 	}
68078623SSriharsha.Basavapatna@Sun.COM 
68088623SSriharsha.Basavapatna@Sun.COM 	mutex_exit(&ldcp->rcv_thr_lock);
68098623SSriharsha.Basavapatna@Sun.COM }
68108623SSriharsha.Basavapatna@Sun.COM 
68118623SSriharsha.Basavapatna@Sun.COM /*
68126495Sspeer  * vgen_dds_rx -- post DDS messages to vnet.
68136495Sspeer  */
68146495Sspeer static int
68156495Sspeer vgen_dds_rx(vgen_ldc_t *ldcp, vio_msg_tag_t *tagp)
68166495Sspeer {
68176495Sspeer 	vio_dds_msg_t *dmsg = (vio_dds_msg_t *)tagp;
68186495Sspeer 	vgen_t *vgenp = LDC_TO_VGEN(ldcp);
68196495Sspeer 
68206495Sspeer 	if (dmsg->dds_class != DDS_VNET_NIU) {
68216495Sspeer 		DWARN(vgenp, ldcp, "Unknown DDS class, dropping");
68226495Sspeer 		return (EBADMSG);
68236495Sspeer 	}
68246495Sspeer 	vnet_dds_rx(vgenp->vnetp, dmsg);
68256495Sspeer 	return (0);
68266495Sspeer }
68276495Sspeer 
68286495Sspeer /*
68296495Sspeer  * vgen_dds_tx -- an interface called by vnet to send DDS messages.
68306495Sspeer  */
68316495Sspeer int
68326495Sspeer vgen_dds_tx(void *arg, void *msg)
68336495Sspeer {
68346495Sspeer 	vgen_t *vgenp = arg;
68356495Sspeer 	vio_dds_msg_t *dmsg = msg;
68366495Sspeer 	vgen_portlist_t *plistp = &vgenp->vgenports;
68376495Sspeer 	vgen_ldc_t *ldcp;
68386495Sspeer 	vgen_ldclist_t *ldclp;
68396495Sspeer 	int rv = EIO;
68406495Sspeer 
68416495Sspeer 
68426495Sspeer 	READ_ENTER(&plistp->rwlock);
68436495Sspeer 	ldclp = &(vgenp->vsw_portp->ldclist);
68446495Sspeer 	READ_ENTER(&ldclp->rwlock);
68456495Sspeer 	ldcp = ldclp->headp;
68466495Sspeer 	if ((ldcp == NULL) || (ldcp->hphase != VH_DONE)) {
68476495Sspeer 		goto vgen_dsend_exit;
68486495Sspeer 	}
68496495Sspeer 
68506495Sspeer 	dmsg->tag.vio_sid = ldcp->local_sid;
68516495Sspeer 	rv = vgen_sendmsg(ldcp, (caddr_t)dmsg, sizeof (vio_dds_msg_t), B_FALSE);
68526495Sspeer 	if (rv != VGEN_SUCCESS) {
68536495Sspeer 		rv = EIO;
68546495Sspeer 	} else {
68556495Sspeer 		rv = 0;
68566495Sspeer 	}
68576495Sspeer 
68586495Sspeer vgen_dsend_exit:
68596495Sspeer 	RW_EXIT(&ldclp->rwlock);
68606495Sspeer 	RW_EXIT(&plistp->rwlock);
68616495Sspeer 	return (rv);
68626495Sspeer 
68636495Sspeer }
68646495Sspeer 
68654647Sraghuram #if DEBUG
68664647Sraghuram 
68674647Sraghuram /*
68684647Sraghuram  * Print debug messages - set to 0xf to enable all msgs
68694647Sraghuram  */
68704647Sraghuram static void
68714647Sraghuram debug_printf(const char *fname, vgen_t *vgenp,
68724647Sraghuram     vgen_ldc_t *ldcp, const char *fmt, ...)
68734647Sraghuram {
68744647Sraghuram 	char    buf[256];
68754647Sraghuram 	char    *bufp = buf;
68764647Sraghuram 	va_list ap;
68774647Sraghuram 
68784647Sraghuram 	if ((vgenp != NULL) && (vgenp->vnetp != NULL)) {
68794647Sraghuram 		(void) sprintf(bufp, "vnet%d:",
68804650Sraghuram 		    ((vnet_t *)(vgenp->vnetp))->instance);
68814647Sraghuram 		bufp += strlen(bufp);
68824647Sraghuram 	}
68834647Sraghuram 	if (ldcp != NULL) {
68844647Sraghuram 		(void) sprintf(bufp, "ldc(%ld):", ldcp->ldc_id);
68854647Sraghuram 		bufp += strlen(bufp);
68864647Sraghuram 	}
68874647Sraghuram 	(void) sprintf(bufp, "%s: ", fname);
68884647Sraghuram 	bufp += strlen(bufp);
68894647Sraghuram 
68904647Sraghuram 	va_start(ap, fmt);
68914647Sraghuram 	(void) vsprintf(bufp, fmt, ap);
68924647Sraghuram 	va_end(ap);
68934647Sraghuram 
68944647Sraghuram 	if ((ldcp == NULL) ||(vgendbg_ldcid == -1) ||
68954647Sraghuram 	    (vgendbg_ldcid == ldcp->ldc_id)) {
68964647Sraghuram 		cmn_err(CE_CONT, "%s\n", buf);
68974647Sraghuram 	}
68984647Sraghuram }
68994647Sraghuram #endif
6900