xref: /illumos-gate/usr/src/uts/sun4v/io/vsw_ldc.c (revision 6e472272)
106db247cSraghuram /*
206db247cSraghuram  * CDDL HEADER START
306db247cSraghuram  *
406db247cSraghuram  * The contents of this file are subject to the terms of the
506db247cSraghuram  * Common Development and Distribution License (the "License").
606db247cSraghuram  * You may not use this file except in compliance with the License.
706db247cSraghuram  *
806db247cSraghuram  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
906db247cSraghuram  * or http://www.opensolaris.org/os/licensing.
1006db247cSraghuram  * See the License for the specific language governing permissions
1106db247cSraghuram  * and limitations under the License.
1206db247cSraghuram  *
1306db247cSraghuram  * When distributing Covered Code, include this CDDL HEADER in each
1406db247cSraghuram  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1506db247cSraghuram  * If applicable, add the following below this CDDL HEADER, with the
1606db247cSraghuram  * fields enclosed by brackets "[]" replaced with your own identifying
1706db247cSraghuram  * information: Portions Copyright [yyyy] [name of copyright owner]
1806db247cSraghuram  *
1906db247cSraghuram  * CDDL HEADER END
2006db247cSraghuram  */
2106db247cSraghuram 
2206db247cSraghuram /*
23a862df29SSriharsha Basavapatna  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
2406db247cSraghuram  */
2506db247cSraghuram 
2606db247cSraghuram #include <sys/types.h>
2706db247cSraghuram #include <sys/errno.h>
2806db247cSraghuram #include <sys/debug.h>
2906db247cSraghuram #include <sys/time.h>
3006db247cSraghuram #include <sys/sysmacros.h>
3106db247cSraghuram #include <sys/systm.h>
3206db247cSraghuram #include <sys/user.h>
3306db247cSraghuram #include <sys/stropts.h>
3406db247cSraghuram #include <sys/stream.h>
3506db247cSraghuram #include <sys/strlog.h>
3606db247cSraghuram #include <sys/strsubr.h>
3706db247cSraghuram #include <sys/cmn_err.h>
3806db247cSraghuram #include <sys/cpu.h>
3906db247cSraghuram #include <sys/kmem.h>
4006db247cSraghuram #include <sys/conf.h>
4106db247cSraghuram #include <sys/ddi.h>
4206db247cSraghuram #include <sys/sunddi.h>
4306db247cSraghuram #include <sys/ksynch.h>
4406db247cSraghuram #include <sys/stat.h>
4506db247cSraghuram #include <sys/kstat.h>
4606db247cSraghuram #include <sys/vtrace.h>
4706db247cSraghuram #include <sys/strsun.h>
4806db247cSraghuram #include <sys/dlpi.h>
4906db247cSraghuram #include <sys/ethernet.h>
5006db247cSraghuram #include <net/if.h>
5106db247cSraghuram #include <sys/varargs.h>
5206db247cSraghuram #include <sys/machsystm.h>
5306db247cSraghuram #include <sys/modctl.h>
5406db247cSraghuram #include <sys/modhash.h>
5506db247cSraghuram #include <sys/mac.h>
5606db247cSraghuram #include <sys/mac_ether.h>
5706db247cSraghuram #include <sys/taskq.h>
5806db247cSraghuram #include <sys/note.h>
5906db247cSraghuram #include <sys/mach_descrip.h>
6006db247cSraghuram #include <sys/mdeg.h>
6106db247cSraghuram #include <sys/ldc.h>
6206db247cSraghuram #include <sys/vsw_fdb.h>
6306db247cSraghuram #include <sys/vsw.h>
6406db247cSraghuram #include <sys/vio_mailbox.h>
6506db247cSraghuram #include <sys/vnet_mailbox.h>
6606db247cSraghuram #include <sys/vnet_common.h>
6706db247cSraghuram #include <sys/vio_util.h>
6806db247cSraghuram #include <sys/sdt.h>
6906db247cSraghuram #include <sys/atomic.h>
7006db247cSraghuram #include <sys/callb.h>
71c1c61f44Ssb #include <sys/vlan.h>
7206db247cSraghuram 
7306db247cSraghuram /* Port add/deletion/etc routines */
746f09f0feSWENTAO YANG static	void vsw_port_delete(vsw_port_t *port);
7506db247cSraghuram static	int vsw_ldc_attach(vsw_port_t *port, uint64_t ldc_id);
767bd3a2e2SSriharsha Basavapatna static	void vsw_ldc_detach(vsw_ldc_t *ldcp);
7706db247cSraghuram static	int vsw_ldc_init(vsw_ldc_t *ldcp);
786f09f0feSWENTAO YANG static	void vsw_ldc_uninit(vsw_ldc_t *ldcp);
797bd3a2e2SSriharsha Basavapatna static	void vsw_ldc_drain(vsw_ldc_t *ldcp);
806f09f0feSWENTAO YANG static	void vsw_drain_port_taskq(vsw_port_t *port);
8106db247cSraghuram static	void vsw_marker_task(void *);
8206db247cSraghuram static	int vsw_plist_del_node(vsw_t *, vsw_port_t *port);
836f09f0feSWENTAO YANG void vsw_detach_ports(vsw_t *vswp);
8406db247cSraghuram int vsw_port_add(vsw_t *vswp, md_t *mdp, mde_cookie_t *node);
8506db247cSraghuram mcst_addr_t *vsw_del_addr(uint8_t devtype, void *arg, uint64_t addr);
8606db247cSraghuram int vsw_port_detach(vsw_t *vswp, int p_instance);
87da14cebeSEric Cheng int vsw_portsend(vsw_port_t *port, mblk_t *mp);
88c1c61f44Ssb int vsw_port_attach(vsw_port_t *portp);
8906db247cSraghuram vsw_port_t *vsw_lookup_port(vsw_t *vswp, int p_instance);
90c1c61f44Ssb void vsw_vlan_unaware_port_reset(vsw_port_t *portp);
91cdfc78adSraghuram void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
927b1f684aSSriharsha Basavapatna void vsw_reset_ports(vsw_t *vswp);
937b1f684aSSriharsha Basavapatna void vsw_port_reset(vsw_port_t *portp);
941107ea93SSriharsha Basavapatna void vsw_physlink_update_ports(vsw_t *vswp);
951107ea93SSriharsha Basavapatna static	void vsw_port_physlink_update(vsw_port_t *portp);
9606db247cSraghuram 
9706db247cSraghuram /* Interrupt routines */
9806db247cSraghuram static	uint_t vsw_ldc_cb(uint64_t cb, caddr_t arg);
9906db247cSraghuram 
10006db247cSraghuram /* Handshake routines */
10106db247cSraghuram static	void vsw_ldc_reinit(vsw_ldc_t *);
10206db247cSraghuram static	void vsw_conn_task(void *);
10306db247cSraghuram static	int vsw_check_flag(vsw_ldc_t *, int, uint64_t);
10406db247cSraghuram static	void vsw_next_milestone(vsw_ldc_t *);
10506db247cSraghuram static	int vsw_supported_version(vio_ver_msg_t *);
106f0ca1d9aSsb static	void vsw_set_vnet_proto_ops(vsw_ldc_t *ldcp);
107f0ca1d9aSsb static	void vsw_reset_vnet_proto_ops(vsw_ldc_t *ldcp);
1087bd3a2e2SSriharsha Basavapatna void vsw_process_conn_evt(vsw_ldc_t *, uint16_t);
10906db247cSraghuram 
11006db247cSraghuram /* Data processing routines */
1117bd3a2e2SSriharsha Basavapatna void vsw_process_pkt(void *);
1127bd3a2e2SSriharsha Basavapatna static void vsw_dispatch_ctrl_task(vsw_ldc_t *, void *, vio_msg_tag_t *, int);
11306db247cSraghuram static void vsw_process_ctrl_pkt(void *);
11406db247cSraghuram static void vsw_process_ctrl_ver_pkt(vsw_ldc_t *, void *);
11506db247cSraghuram static void vsw_process_ctrl_attr_pkt(vsw_ldc_t *, void *);
11606db247cSraghuram static void vsw_process_ctrl_mcst_pkt(vsw_ldc_t *, void *);
11706db247cSraghuram static void vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *, void *);
11806db247cSraghuram static void vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *, void *);
11906db247cSraghuram static void vsw_process_ctrl_rdx_pkt(vsw_ldc_t *, void *);
1201107ea93SSriharsha Basavapatna static void vsw_process_physlink_msg(vsw_ldc_t *, void *);
121f0ca1d9aSsb static void vsw_process_data_pkt(vsw_ldc_t *, void *, vio_msg_tag_t *,
122f0ca1d9aSsb 	uint32_t);
123f0ca1d9aSsb static void vsw_process_pkt_data_nop(void *, void *, uint32_t);
124f0ca1d9aSsb static void vsw_process_pkt_data(void *, void *, uint32_t);
12506db247cSraghuram static void vsw_process_data_ibnd_pkt(vsw_ldc_t *, void *);
126f0ca1d9aSsb static void vsw_process_err_pkt(vsw_ldc_t *, void *, vio_msg_tag_t *);
1277bd3a2e2SSriharsha Basavapatna static void vsw_process_evt_read(vsw_ldc_t *ldcp);
1287bd3a2e2SSriharsha Basavapatna static void vsw_ldc_rcv(vsw_ldc_t *ldcp);
12906db247cSraghuram 
13006db247cSraghuram /* Switching/data transmit routines */
13106db247cSraghuram static	int vsw_descrsend(vsw_ldc_t *, mblk_t *);
132f0ca1d9aSsb static void vsw_ldcsend_pkt(vsw_ldc_t *ldcp, mblk_t *mp);
133f0ca1d9aSsb static int vsw_ldcsend(vsw_ldc_t *ldcp, mblk_t *mp, uint32_t retries);
134f0ca1d9aSsb static int vsw_ldctx_pri(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count);
135f0ca1d9aSsb static int vsw_ldctx(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count);
13606db247cSraghuram 
13706db247cSraghuram /* Packet creation routines */
13806db247cSraghuram static void vsw_send_ver(void *);
13906db247cSraghuram static void vsw_send_attr(vsw_ldc_t *);
14006db247cSraghuram static void vsw_send_dring_info(vsw_ldc_t *);
14106db247cSraghuram static void vsw_send_rdx(vsw_ldc_t *);
1421107ea93SSriharsha Basavapatna static void vsw_send_physlink_msg(vsw_ldc_t *ldcp, link_state_t plink_state);
14306db247cSraghuram 
14406db247cSraghuram /* Dring routines */
14506db247cSraghuram static void vsw_create_privring(vsw_ldc_t *);
1467bd3a2e2SSriharsha Basavapatna static dring_info_t *vsw_map_dring(vsw_ldc_t *ldcp, void *pkt);
1477bd3a2e2SSriharsha Basavapatna static void vsw_unmap_dring(vsw_ldc_t *ldcp);
1487bd3a2e2SSriharsha Basavapatna static void vsw_destroy_dring(vsw_ldc_t *ldcp);
1497bd3a2e2SSriharsha Basavapatna static void vsw_free_lane_resources(vsw_ldc_t *, uint64_t);
1507bd3a2e2SSriharsha Basavapatna static int vsw_map_data(vsw_ldc_t *ldcp, dring_info_t *dp, void *pkt);
15106db247cSraghuram static void vsw_set_lane_attr(vsw_t *, lane_t *);
1527bd3a2e2SSriharsha Basavapatna dring_info_t *vsw_map_dring_cmn(vsw_ldc_t *ldcp,
1537bd3a2e2SSriharsha Basavapatna     vio_dring_reg_msg_t *dring_pkt);
15434f94fbcSWENTAO YANG static int vsw_mapin_avail(vsw_ldc_t *ldcp);
15506db247cSraghuram 
1567bd3a2e2SSriharsha Basavapatna /* tx/msg/rcv thread routines */
15706db247cSraghuram static void vsw_stop_tx_thread(vsw_ldc_t *ldcp);
15806db247cSraghuram static void vsw_ldc_tx_worker(void *arg);
15906db247cSraghuram 
16006db247cSraghuram /* Misc support routines */
16106db247cSraghuram static void vsw_save_lmacaddr(vsw_t *vswp, uint64_t macaddr);
16206db247cSraghuram static int vsw_get_same_dest_list(struct ether_header *ehp,
16306db247cSraghuram     mblk_t **rhead, mblk_t **rtail, mblk_t **mpp);
16406db247cSraghuram static mblk_t *vsw_dupmsgchain(mblk_t *mp);
16506db247cSraghuram 
16606db247cSraghuram /* Debugging routines */
16706db247cSraghuram static void dump_flags(uint64_t);
16806db247cSraghuram static void display_state(void);
16906db247cSraghuram static void display_lane(lane_t *);
17006db247cSraghuram static void display_ring(dring_info_t *);
17106db247cSraghuram 
17206db247cSraghuram /*
17306db247cSraghuram  * Functions imported from other files.
17406db247cSraghuram  */
17506db247cSraghuram extern int vsw_set_hw(vsw_t *, vsw_port_t *, int);
176da14cebeSEric Cheng extern void vsw_unset_hw(vsw_t *, vsw_port_t *, int);
17706db247cSraghuram extern int vsw_add_rem_mcst(vnet_mcast_msg_t *mcst_pkt, vsw_port_t *port);
17806db247cSraghuram extern void vsw_del_mcst_port(vsw_port_t *port);
17906db247cSraghuram extern int vsw_add_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg);
18006db247cSraghuram extern int vsw_del_mcst(vsw_t *vswp, uint8_t devtype, uint64_t addr, void *arg);
181c1c61f44Ssb extern void vsw_fdbe_add(vsw_t *vswp, void *port);
182c1c61f44Ssb extern void vsw_fdbe_del(vsw_t *vswp, struct ether_addr *eaddr);
183c1c61f44Ssb extern void vsw_create_vlans(void *arg, int type);
184c1c61f44Ssb extern void vsw_destroy_vlans(void *arg, int type);
185c1c61f44Ssb extern void vsw_vlan_add_ids(void *arg, int type);
186c1c61f44Ssb extern void vsw_vlan_remove_ids(void *arg, int type);
187c1c61f44Ssb extern boolean_t vsw_frame_lookup_vid(void *arg, int caller,
188c1c61f44Ssb 	struct ether_header *ehp, uint16_t *vidp);
189c1c61f44Ssb extern mblk_t *vsw_vlan_frame_pretag(void *arg, int type, mblk_t *mp);
190c1c61f44Ssb extern uint32_t vsw_vlan_frame_untag(void *arg, int type, mblk_t **np,
191c1c61f44Ssb 	mblk_t **npt);
192c1c61f44Ssb extern boolean_t vsw_vlan_lookup(mod_hash_t *vlan_hashp, uint16_t vid);
193678453a8Sspeer extern void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
194678453a8Sspeer extern void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
195678453a8Sspeer extern void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
196678453a8Sspeer extern void vsw_hio_stop_port(vsw_port_t *portp);
197da14cebeSEric Cheng extern void vsw_publish_macaddr(vsw_t *vswp, vsw_port_t *portp);
198da14cebeSEric Cheng extern int vsw_mac_client_init(vsw_t *vswp, vsw_port_t *port, int type);
199da14cebeSEric Cheng extern void vsw_mac_client_cleanup(vsw_t *vswp, vsw_port_t *port, int type);
20034b64c01SWENTAO YANG extern void vsw_destroy_rxpools(void *arg);
2017bd3a2e2SSriharsha Basavapatna extern void vsw_stop_msg_thread(vsw_ldc_t *ldcp);
2027bd3a2e2SSriharsha Basavapatna extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
2037bd3a2e2SSriharsha Basavapatna extern int vsw_dringsend(vsw_ldc_t *, mblk_t *);
2047bd3a2e2SSriharsha Basavapatna extern int vsw_reclaim_dring(dring_info_t *dp, int start);
2057bd3a2e2SSriharsha Basavapatna extern int vsw_dring_find_free_desc(dring_info_t *, vsw_private_desc_t **,
2067bd3a2e2SSriharsha Basavapatna     int *);
2077bd3a2e2SSriharsha Basavapatna extern vio_dring_reg_msg_t *vsw_create_tx_dring_info(vsw_ldc_t *);
2087bd3a2e2SSriharsha Basavapatna extern int vsw_setup_tx_dring(vsw_ldc_t *ldcp, dring_info_t *dp);
2097bd3a2e2SSriharsha Basavapatna extern void vsw_destroy_tx_dring(vsw_ldc_t *ldcp);
2107bd3a2e2SSriharsha Basavapatna extern dring_info_t *vsw_map_rx_dring(vsw_ldc_t *ldcp, void *pkt);
2117bd3a2e2SSriharsha Basavapatna extern void vsw_unmap_rx_dring(vsw_ldc_t *ldcp);
2127bd3a2e2SSriharsha Basavapatna extern void vsw_ldc_msg_worker(void *arg);
2137bd3a2e2SSriharsha Basavapatna extern void vsw_process_dringdata(void *, void *);
2147bd3a2e2SSriharsha Basavapatna extern vio_dring_reg_msg_t *vsw_create_rx_dring_info(vsw_ldc_t *);
2157bd3a2e2SSriharsha Basavapatna extern void vsw_destroy_rx_dring(vsw_ldc_t *ldcp);
2167bd3a2e2SSriharsha Basavapatna extern dring_info_t *vsw_map_tx_dring(vsw_ldc_t *ldcp, void *pkt);
2177bd3a2e2SSriharsha Basavapatna extern void vsw_unmap_tx_dring(vsw_ldc_t *ldcp);
2187bd3a2e2SSriharsha Basavapatna extern void vsw_ldc_rcv_worker(void *arg);
2197bd3a2e2SSriharsha Basavapatna extern void vsw_stop_rcv_thread(vsw_ldc_t *ldcp);
2207bd3a2e2SSriharsha Basavapatna extern int vsw_dringsend_shm(vsw_ldc_t *, mblk_t *);
2217bd3a2e2SSriharsha Basavapatna extern void vsw_process_dringdata_shm(void *, void *);
22206db247cSraghuram 
22306db247cSraghuram /*
22406db247cSraghuram  * Tunables used in this file.
22506db247cSraghuram  */
22606db247cSraghuram extern int vsw_num_handshakes;
22706db247cSraghuram extern int vsw_ldc_tx_delay;
22806db247cSraghuram extern int vsw_ldc_tx_retries;
2296f09f0feSWENTAO YANG extern int vsw_ldc_retries;
2306f09f0feSWENTAO YANG extern int vsw_ldc_delay;
23106db247cSraghuram extern boolean_t vsw_ldc_rxthr_enabled;
23206db247cSraghuram extern boolean_t vsw_ldc_txthr_enabled;
2337bd3a2e2SSriharsha Basavapatna extern uint32_t vsw_num_descriptors;
2347bd3a2e2SSriharsha Basavapatna extern uint8_t  vsw_dring_mode;
235f0ca1d9aSsb extern uint32_t vsw_max_tx_qcount;
236f0ca1d9aSsb extern boolean_t vsw_obp_ver_proto_workaround;
23751aa9d07Ssb extern uint32_t vsw_publish_macaddr_count;
23834f94fbcSWENTAO YANG extern uint32_t vsw_nrbufs_factor;
23906db247cSraghuram 
24006db247cSraghuram #define	LDC_ENTER_LOCK(ldcp)	\
24106db247cSraghuram 				mutex_enter(&((ldcp)->ldc_cblock));\
24206db247cSraghuram 				mutex_enter(&((ldcp)->ldc_rxlock));\
24306db247cSraghuram 				mutex_enter(&((ldcp)->ldc_txlock));
24406db247cSraghuram #define	LDC_EXIT_LOCK(ldcp)	\
24506db247cSraghuram 				mutex_exit(&((ldcp)->ldc_txlock));\
24606db247cSraghuram 				mutex_exit(&((ldcp)->ldc_rxlock));\
24706db247cSraghuram 				mutex_exit(&((ldcp)->ldc_cblock));
24806db247cSraghuram 
249f0ca1d9aSsb #define	VSW_VER_EQ(ldcp, major, minor)	\
250f0ca1d9aSsb 	((ldcp)->lane_out.ver_major == (major) &&	\
251f0ca1d9aSsb 	    (ldcp)->lane_out.ver_minor == (minor))
252f0ca1d9aSsb 
253f0ca1d9aSsb #define	VSW_VER_LT(ldcp, major, minor)	\
254f0ca1d9aSsb 	(((ldcp)->lane_out.ver_major < (major)) ||	\
255f0ca1d9aSsb 	    ((ldcp)->lane_out.ver_major == (major) &&	\
256f0ca1d9aSsb 	    (ldcp)->lane_out.ver_minor < (minor)))
25706db247cSraghuram 
258c1c61f44Ssb #define	VSW_VER_GTEQ(ldcp, major, minor)	\
259c1c61f44Ssb 	(((ldcp)->lane_out.ver_major > (major)) ||	\
260c1c61f44Ssb 	    ((ldcp)->lane_out.ver_major == (major) &&	\
261c1c61f44Ssb 	    (ldcp)->lane_out.ver_minor >= (minor)))
262c1c61f44Ssb 
2637bd3a2e2SSriharsha Basavapatna #define	VSW_VER_LTEQ(ldcp, major, minor)	\
2647bd3a2e2SSriharsha Basavapatna 	(((ldcp)->lane_out.ver_major < (major)) ||	\
2657bd3a2e2SSriharsha Basavapatna 	    ((ldcp)->lane_out.ver_major == (major) &&	\
2667bd3a2e2SSriharsha Basavapatna 	    (ldcp)->lane_out.ver_minor <= (minor)))
2677bd3a2e2SSriharsha Basavapatna 
2681107ea93SSriharsha Basavapatna /*
2691107ea93SSriharsha Basavapatna  * VIO Protocol Version Info:
2701107ea93SSriharsha Basavapatna  *
2711107ea93SSriharsha Basavapatna  * The version specified below represents the version of protocol currently
2721107ea93SSriharsha Basavapatna  * supported in the driver. It means the driver can negotiate with peers with
2731107ea93SSriharsha Basavapatna  * versions <= this version. Here is a summary of the feature(s) that are
2741107ea93SSriharsha Basavapatna  * supported at each version of the protocol:
2751107ea93SSriharsha Basavapatna  *
2761107ea93SSriharsha Basavapatna  * 1.0			Basic VIO protocol.
2771107ea93SSriharsha Basavapatna  * 1.1			vDisk protocol update (no virtual network update).
2781107ea93SSriharsha Basavapatna  * 1.2			Support for priority frames (priority-ether-types).
2791107ea93SSriharsha Basavapatna  * 1.3			VLAN and HybridIO support.
2801107ea93SSriharsha Basavapatna  * 1.4			Jumbo Frame support.
2811107ea93SSriharsha Basavapatna  * 1.5			Link State Notification support with optional support
282*6e472272SToomas Soome  *			for Physical Link information.
2837bd3a2e2SSriharsha Basavapatna  * 1.6			Support for RxDringData mode.
2841107ea93SSriharsha Basavapatna  */
2857bd3a2e2SSriharsha Basavapatna static	ver_sup_t	vsw_versions[] = { {1, 6} };
28606db247cSraghuram 
28706db247cSraghuram /*
28806db247cSraghuram  * For the moment the state dump routines have their own
28906db247cSraghuram  * private flag.
29006db247cSraghuram  */
29106db247cSraghuram #define	DUMP_STATE	0
29206db247cSraghuram 
29306db247cSraghuram #if DUMP_STATE
29406db247cSraghuram 
29506db247cSraghuram #define	DUMP_TAG(tag) \
29606db247cSraghuram {			\
29706db247cSraghuram 	D1(NULL, "DUMP_TAG: type 0x%llx", (tag).vio_msgtype); \
29806db247cSraghuram 	D1(NULL, "DUMP_TAG: stype 0x%llx", (tag).vio_subtype);	\
29906db247cSraghuram 	D1(NULL, "DUMP_TAG: senv 0x%llx", (tag).vio_subtype_env);	\
30006db247cSraghuram }
30106db247cSraghuram 
30206db247cSraghuram #define	DUMP_TAG_PTR(tag) \
30306db247cSraghuram {			\
30406db247cSraghuram 	D1(NULL, "DUMP_TAG: type 0x%llx", (tag)->vio_msgtype); \
30506db247cSraghuram 	D1(NULL, "DUMP_TAG: stype 0x%llx", (tag)->vio_subtype);	\
30606db247cSraghuram 	D1(NULL, "DUMP_TAG: senv 0x%llx", (tag)->vio_subtype_env);	\
30706db247cSraghuram }
30806db247cSraghuram 
30906db247cSraghuram #define	DUMP_FLAGS(flags) dump_flags(flags);
31006db247cSraghuram #define	DISPLAY_STATE()	display_state()
31106db247cSraghuram 
31206db247cSraghuram #else
31306db247cSraghuram 
31406db247cSraghuram #define	DUMP_TAG(tag)
31506db247cSraghuram #define	DUMP_TAG_PTR(tag)
31606db247cSraghuram #define	DUMP_FLAGS(state)
31706db247cSraghuram #define	DISPLAY_STATE()
31806db247cSraghuram 
31906db247cSraghuram #endif	/* DUMP_STATE */
32006db247cSraghuram 
32106db247cSraghuram /*
32206db247cSraghuram  * Attach the specified port.
32306db247cSraghuram  *
32406db247cSraghuram  * Returns 0 on success, 1 on failure.
32506db247cSraghuram  */
32606db247cSraghuram int
vsw_port_attach(vsw_port_t * port)327c1c61f44Ssb vsw_port_attach(vsw_port_t *port)
32806db247cSraghuram {
329c1c61f44Ssb 	vsw_t			*vswp = port->p_vswp;
33006db247cSraghuram 	vsw_port_list_t		*plist = &vswp->plist;
331c1c61f44Ssb 	vsw_port_t		*p, **pp;
332c1c61f44Ssb 	int			nids = port->num_ldcs;
333c1c61f44Ssb 	uint64_t		*ldcids;
334da14cebeSEric Cheng 	int			rv;
33506db247cSraghuram 
336c1c61f44Ssb 	D1(vswp, "%s: enter : port %d", __func__, port->p_instance);
33706db247cSraghuram 
33806db247cSraghuram 	/* port already exists? */
33906db247cSraghuram 	READ_ENTER(&plist->lockrw);
340c1c61f44Ssb 	for (p = plist->head; p != NULL; p = p->p_next) {
341c1c61f44Ssb 		if (p->p_instance == port->p_instance) {
34206db247cSraghuram 			DWARN(vswp, "%s: port instance %d already attached",
343c1c61f44Ssb 			    __func__, p->p_instance);
34406db247cSraghuram 			RW_EXIT(&plist->lockrw);
34506db247cSraghuram 			return (1);
34606db247cSraghuram 		}
34706db247cSraghuram 	}
34806db247cSraghuram 	RW_EXIT(&plist->lockrw);
34906db247cSraghuram 
35006db247cSraghuram 	mutex_init(&port->tx_lock, NULL, MUTEX_DRIVER, NULL);
35106db247cSraghuram 	mutex_init(&port->mca_lock, NULL, MUTEX_DRIVER, NULL);
352da14cebeSEric Cheng 	rw_init(&port->maccl_rwlock, NULL, RW_DRIVER, NULL);
35306db247cSraghuram 
35406db247cSraghuram 	mutex_init(&port->state_lock, NULL, MUTEX_DRIVER, NULL);
35506db247cSraghuram 	cv_init(&port->state_cv, NULL, CV_DRIVER, NULL);
35606db247cSraghuram 	port->state = VSW_PORT_INIT;
35706db247cSraghuram 
35806db247cSraghuram 	D2(vswp, "%s: %d nids", __func__, nids);
359c1c61f44Ssb 	ldcids = port->ldc_ids;
3607bd3a2e2SSriharsha Basavapatna 	D2(vswp, "%s: ldcid (%llx)", __func__, (uint64_t)ldcids[0]);
3617bd3a2e2SSriharsha Basavapatna 	if (vsw_ldc_attach(port, (uint64_t)ldcids[0]) != 0) {
3627bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: ldc_attach failed", __func__);
3637bd3a2e2SSriharsha Basavapatna 		goto exit_error;
36406db247cSraghuram 	}
36506db247cSraghuram 
36606db247cSraghuram 	if (vswp->switching_setup_done == B_TRUE) {
36706db247cSraghuram 		/*
368da14cebeSEric Cheng 		 * If the underlying network device has been setup,
369da14cebeSEric Cheng 		 * then open a mac client and porgram the mac address
370da14cebeSEric Cheng 		 * for this port.
37106db247cSraghuram 		 */
372da14cebeSEric Cheng 		rv = vsw_mac_client_init(vswp, port, VSW_VNETPORT);
373da14cebeSEric Cheng 		if (rv != 0) {
374da14cebeSEric Cheng 			goto exit_error;
375da14cebeSEric Cheng 		}
37606db247cSraghuram 	}
37706db247cSraghuram 
37806db247cSraghuram 	/* create the fdb entry for this port/mac address */
379c1c61f44Ssb 	vsw_fdbe_add(vswp, port);
380c1c61f44Ssb 
381c1c61f44Ssb 	vsw_create_vlans(port, VSW_VNETPORT);
382c1c61f44Ssb 
383c1c61f44Ssb 	WRITE_ENTER(&plist->lockrw);
38406db247cSraghuram 
38506db247cSraghuram 	/* link it into the list of ports for this vsw instance */
386c1c61f44Ssb 	pp = (vsw_port_t **)(&plist->head);
387c1c61f44Ssb 	port->p_next = *pp;
388c1c61f44Ssb 	*pp = port;
38906db247cSraghuram 	plist->num_ports++;
39006db247cSraghuram 
39106db247cSraghuram 	RW_EXIT(&plist->lockrw);
39206db247cSraghuram 
39306db247cSraghuram 	/*
39406db247cSraghuram 	 * Initialise the port and any ldc's under it.
39506db247cSraghuram 	 */
3967bd3a2e2SSriharsha Basavapatna 	(void) vsw_ldc_init(port->ldcp);
39706db247cSraghuram 
39851aa9d07Ssb 	/* announce macaddr of vnet to the physical switch */
39951aa9d07Ssb 	if (vsw_publish_macaddr_count != 0) {	/* enabled */
400da14cebeSEric Cheng 		vsw_publish_macaddr(vswp, port);
40151aa9d07Ssb 	}
40251aa9d07Ssb 
40306db247cSraghuram 	D1(vswp, "%s: exit", __func__);
40406db247cSraghuram 	return (0);
405da14cebeSEric Cheng 
406da14cebeSEric Cheng exit_error:
407da14cebeSEric Cheng 
408da14cebeSEric Cheng 	cv_destroy(&port->state_cv);
409da14cebeSEric Cheng 	mutex_destroy(&port->state_lock);
410da14cebeSEric Cheng 
411da14cebeSEric Cheng 	rw_destroy(&port->maccl_rwlock);
412da14cebeSEric Cheng 	mutex_destroy(&port->tx_lock);
413da14cebeSEric Cheng 	mutex_destroy(&port->mca_lock);
414da14cebeSEric Cheng 	kmem_free(port, sizeof (vsw_port_t));
415da14cebeSEric Cheng 	return (1);
41606db247cSraghuram }
41706db247cSraghuram 
41806db247cSraghuram /*
41906db247cSraghuram  * Detach the specified port.
42006db247cSraghuram  *
42106db247cSraghuram  * Returns 0 on success, 1 on failure.
42206db247cSraghuram  */
42306db247cSraghuram int
vsw_port_detach(vsw_t * vswp,int p_instance)42406db247cSraghuram vsw_port_detach(vsw_t *vswp, int p_instance)
42506db247cSraghuram {
42606db247cSraghuram 	vsw_port_t	*port = NULL;
42706db247cSraghuram 	vsw_port_list_t	*plist = &vswp->plist;
42806db247cSraghuram 
42906db247cSraghuram 	D1(vswp, "%s: enter: port id %d", __func__, p_instance);
43006db247cSraghuram 
43106db247cSraghuram 	WRITE_ENTER(&plist->lockrw);
43206db247cSraghuram 
43306db247cSraghuram 	if ((port = vsw_lookup_port(vswp, p_instance)) == NULL) {
43406db247cSraghuram 		RW_EXIT(&plist->lockrw);
43506db247cSraghuram 		return (1);
43606db247cSraghuram 	}
43706db247cSraghuram 
43806db247cSraghuram 	if (vsw_plist_del_node(vswp, port)) {
43906db247cSraghuram 		RW_EXIT(&plist->lockrw);
44006db247cSraghuram 		return (1);
44106db247cSraghuram 	}
44206db247cSraghuram 
443678453a8Sspeer 	/* cleanup any HybridIO for this port */
444678453a8Sspeer 	vsw_hio_stop_port(port);
445678453a8Sspeer 
44606db247cSraghuram 	/*
44706db247cSraghuram 	 * No longer need to hold writer lock on port list now
44806db247cSraghuram 	 * that we have unlinked the target port from the list.
44906db247cSraghuram 	 */
45006db247cSraghuram 	RW_EXIT(&plist->lockrw);
45106db247cSraghuram 
452da14cebeSEric Cheng 	/* Cleanup and close the mac client */
453da14cebeSEric Cheng 	vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
454da14cebeSEric Cheng 
455c1c61f44Ssb 	/* Remove the fdb entry for this port/mac address */
456c1c61f44Ssb 	vsw_fdbe_del(vswp, &(port->p_macaddr));
457c1c61f44Ssb 	vsw_destroy_vlans(port, VSW_VNETPORT);
458c1c61f44Ssb 
459c1c61f44Ssb 	/* Remove any multicast addresses.. */
460c1c61f44Ssb 	vsw_del_mcst_port(port);
461c1c61f44Ssb 
4626f09f0feSWENTAO YANG 	vsw_port_delete(port);
46306db247cSraghuram 
46406db247cSraghuram 	D1(vswp, "%s: exit: p_instance(%d)", __func__, p_instance);
46506db247cSraghuram 	return (0);
46606db247cSraghuram }
46706db247cSraghuram 
46806db247cSraghuram /*
46906db247cSraghuram  * Detach all active ports.
47006db247cSraghuram  */
4716f09f0feSWENTAO YANG void
vsw_detach_ports(vsw_t * vswp)47206db247cSraghuram vsw_detach_ports(vsw_t *vswp)
47306db247cSraghuram {
474*6e472272SToomas Soome 	vsw_port_list_t		*plist = &vswp->plist;
47506db247cSraghuram 	vsw_port_t		*port = NULL;
47606db247cSraghuram 
47706db247cSraghuram 	D1(vswp, "%s: enter", __func__);
47806db247cSraghuram 
47906db247cSraghuram 	WRITE_ENTER(&plist->lockrw);
48006db247cSraghuram 
48106db247cSraghuram 	while ((port = plist->head) != NULL) {
4826f09f0feSWENTAO YANG 		(void) vsw_plist_del_node(vswp, port);
4836f09f0feSWENTAO YANG 
4846f09f0feSWENTAO YANG 		/* cleanup any HybridIO for this port */
4856f09f0feSWENTAO YANG 		vsw_hio_stop_port(port);
48606db247cSraghuram 
487da14cebeSEric Cheng 		/* Cleanup and close the mac client */
488da14cebeSEric Cheng 		vsw_mac_client_cleanup(vswp, port, VSW_VNETPORT);
48906db247cSraghuram 
49006db247cSraghuram 		/* Remove the fdb entry for this port/mac address */
491c1c61f44Ssb 		vsw_fdbe_del(vswp, &(port->p_macaddr));
492c1c61f44Ssb 		vsw_destroy_vlans(port, VSW_VNETPORT);
49306db247cSraghuram 
49406db247cSraghuram 		/* Remove any multicast addresses.. */
49506db247cSraghuram 		vsw_del_mcst_port(port);
49606db247cSraghuram 
49706db247cSraghuram 		/*
49806db247cSraghuram 		 * No longer need to hold the lock on the port list
49906db247cSraghuram 		 * now that we have unlinked the target port from the
50006db247cSraghuram 		 * list.
50106db247cSraghuram 		 */
50206db247cSraghuram 		RW_EXIT(&plist->lockrw);
5036f09f0feSWENTAO YANG 		vsw_port_delete(port);
50406db247cSraghuram 		WRITE_ENTER(&plist->lockrw);
50506db247cSraghuram 	}
50606db247cSraghuram 	RW_EXIT(&plist->lockrw);
50706db247cSraghuram 
50806db247cSraghuram 	D1(vswp, "%s: exit", __func__);
50906db247cSraghuram }
51006db247cSraghuram 
51106db247cSraghuram /*
51206db247cSraghuram  * Delete the specified port.
51306db247cSraghuram  */
5146f09f0feSWENTAO YANG static void
vsw_port_delete(vsw_port_t * port)51506db247cSraghuram vsw_port_delete(vsw_port_t *port)
51606db247cSraghuram {
51706db247cSraghuram 	vsw_t			*vswp = port->p_vswp;
51806db247cSraghuram 
51906db247cSraghuram 	D1(vswp, "%s: enter : port id %d", __func__, port->p_instance);
52006db247cSraghuram 
5217bd3a2e2SSriharsha Basavapatna 	vsw_ldc_uninit(port->ldcp);
52206db247cSraghuram 
52306db247cSraghuram 	/*
52406db247cSraghuram 	 * Wait for any pending ctrl msg tasks which reference this
52506db247cSraghuram 	 * port to finish.
52606db247cSraghuram 	 */
5276f09f0feSWENTAO YANG 	vsw_drain_port_taskq(port);
52806db247cSraghuram 
52906db247cSraghuram 	/*
53006db247cSraghuram 	 * Wait for any active callbacks to finish
53106db247cSraghuram 	 */
5327bd3a2e2SSriharsha Basavapatna 	vsw_ldc_drain(port->ldcp);
53306db247cSraghuram 
5347bd3a2e2SSriharsha Basavapatna 	vsw_ldc_detach(port->ldcp);
53506db247cSraghuram 
536da14cebeSEric Cheng 	rw_destroy(&port->maccl_rwlock);
53706db247cSraghuram 	mutex_destroy(&port->mca_lock);
53806db247cSraghuram 	mutex_destroy(&port->tx_lock);
539c1c61f44Ssb 
54006db247cSraghuram 	cv_destroy(&port->state_cv);
54106db247cSraghuram 	mutex_destroy(&port->state_lock);
54206db247cSraghuram 
543c1c61f44Ssb 	if (port->num_ldcs != 0) {
544c1c61f44Ssb 		kmem_free(port->ldc_ids, port->num_ldcs * sizeof (uint64_t));
545c1c61f44Ssb 		port->num_ldcs = 0;
546c1c61f44Ssb 	}
547da14cebeSEric Cheng 
548da14cebeSEric Cheng 	if (port->nvids != 0) {
549da14cebeSEric Cheng 		kmem_free(port->vids, sizeof (vsw_vlanid_t) * port->nvids);
550da14cebeSEric Cheng 	}
551da14cebeSEric Cheng 
55206db247cSraghuram 	kmem_free(port, sizeof (vsw_port_t));
55306db247cSraghuram 
55406db247cSraghuram 	D1(vswp, "%s: exit", __func__);
55506db247cSraghuram }
55606db247cSraghuram 
55706db247cSraghuram /*
55806db247cSraghuram  * Attach a logical domain channel (ldc) under a specified port.
55906db247cSraghuram  *
56006db247cSraghuram  * Returns 0 on success, 1 on failure.
56106db247cSraghuram  */
56206db247cSraghuram static int
vsw_ldc_attach(vsw_port_t * port,uint64_t ldc_id)56306db247cSraghuram vsw_ldc_attach(vsw_port_t *port, uint64_t ldc_id)
56406db247cSraghuram {
565*6e472272SToomas Soome 	vsw_t		*vswp = port->p_vswp;
566*6e472272SToomas Soome 	vsw_ldc_t	*ldcp = NULL;
567*6e472272SToomas Soome 	ldc_attr_t	attr;
56806db247cSraghuram 	ldc_status_t	istatus;
569*6e472272SToomas Soome 	int		status = DDI_FAILURE;
57006db247cSraghuram 	char		kname[MAXNAMELEN];
5717b1f684aSSriharsha Basavapatna 	enum		{ PROG_init = 0x0,
5727bd3a2e2SSriharsha Basavapatna 			    PROG_callback = 0x1,
5737bd3a2e2SSriharsha Basavapatna 			    PROG_tx_thread = 0x2}
57406db247cSraghuram 			progress;
57506db247cSraghuram 
57606db247cSraghuram 	progress = PROG_init;
57706db247cSraghuram 
57806db247cSraghuram 	D1(vswp, "%s: enter", __func__);
57906db247cSraghuram 
58006db247cSraghuram 	ldcp = kmem_zalloc(sizeof (vsw_ldc_t), KM_NOSLEEP);
58106db247cSraghuram 	if (ldcp == NULL) {
58206db247cSraghuram 		DERR(vswp, "%s: kmem_zalloc failed", __func__);
58306db247cSraghuram 		return (1);
58406db247cSraghuram 	}
58506db247cSraghuram 	ldcp->ldc_id = ldc_id;
58606db247cSraghuram 
58706db247cSraghuram 	mutex_init(&ldcp->ldc_txlock, NULL, MUTEX_DRIVER, NULL);
58806db247cSraghuram 	mutex_init(&ldcp->ldc_rxlock, NULL, MUTEX_DRIVER, NULL);
58906db247cSraghuram 	mutex_init(&ldcp->ldc_cblock, NULL, MUTEX_DRIVER, NULL);
5907bd3a2e2SSriharsha Basavapatna 	ldcp->msg_thr_flags = 0;
5917bd3a2e2SSriharsha Basavapatna 	mutex_init(&ldcp->msg_thr_lock, NULL, MUTEX_DRIVER, NULL);
5927bd3a2e2SSriharsha Basavapatna 	cv_init(&ldcp->msg_thr_cv, NULL, CV_DRIVER, NULL);
5937bd3a2e2SSriharsha Basavapatna 	ldcp->rcv_thr_flags = 0;
5947bd3a2e2SSriharsha Basavapatna 	mutex_init(&ldcp->rcv_thr_lock, NULL, MUTEX_DRIVER, NULL);
5957bd3a2e2SSriharsha Basavapatna 	cv_init(&ldcp->rcv_thr_cv, NULL, CV_DRIVER, NULL);
59606db247cSraghuram 	mutex_init(&ldcp->drain_cv_lock, NULL, MUTEX_DRIVER, NULL);
59706db247cSraghuram 	cv_init(&ldcp->drain_cv, NULL, CV_DRIVER, NULL);
59806db247cSraghuram 
59906db247cSraghuram 	/* required for handshake with peer */
60006db247cSraghuram 	ldcp->local_session = (uint64_t)ddi_get_lbolt();
60106db247cSraghuram 	ldcp->peer_session = 0;
60206db247cSraghuram 	ldcp->session_status = 0;
60306db247cSraghuram 	ldcp->hss_id = 1;	/* Initial handshake session id */
6047bd3a2e2SSriharsha Basavapatna 	ldcp->hphase = VSW_MILESTONE0;
60506db247cSraghuram 
606678453a8Sspeer 	(void) atomic_swap_32(&port->p_hio_capable, B_FALSE);
607678453a8Sspeer 
60806db247cSraghuram 	/* only set for outbound lane, inbound set by peer */
60906db247cSraghuram 	vsw_set_lane_attr(vswp, &ldcp->lane_out);
61006db247cSraghuram 
61106db247cSraghuram 	attr.devclass = LDC_DEV_NT_SVC;
61206db247cSraghuram 	attr.instance = ddi_get_instance(vswp->dip);
61306db247cSraghuram 	attr.mode = LDC_MODE_UNRELIABLE;
61406db247cSraghuram 	attr.mtu = VSW_LDC_MTU;
61506db247cSraghuram 	status = ldc_init(ldc_id, &attr, &ldcp->ldc_handle);
61606db247cSraghuram 	if (status != 0) {
61706db247cSraghuram 		DERR(vswp, "%s(%lld): ldc_init failed, rv (%d)",
61806db247cSraghuram 		    __func__, ldc_id, status);
61906db247cSraghuram 		goto ldc_attach_fail;
62006db247cSraghuram 	}
62106db247cSraghuram 
62206db247cSraghuram 	if (vsw_ldc_txthr_enabled) {
62306db247cSraghuram 		ldcp->tx_thr_flags = 0;
62406db247cSraghuram 		ldcp->tx_mhead = ldcp->tx_mtail = NULL;
62506db247cSraghuram 
62606db247cSraghuram 		mutex_init(&ldcp->tx_thr_lock, NULL, MUTEX_DRIVER, NULL);
62706db247cSraghuram 		cv_init(&ldcp->tx_thr_cv, NULL, CV_DRIVER, NULL);
62806db247cSraghuram 		ldcp->tx_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
62906db247cSraghuram 		    vsw_ldc_tx_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
63006db247cSraghuram 
63106db247cSraghuram 		progress |= PROG_tx_thread;
63206db247cSraghuram 		if (ldcp->tx_thread == NULL) {
63306db247cSraghuram 			DWARN(vswp, "%s(%lld): Failed to create worker thread",
63406db247cSraghuram 			    __func__, ldc_id);
63506db247cSraghuram 			goto ldc_attach_fail;
63606db247cSraghuram 		}
63706db247cSraghuram 	}
63806db247cSraghuram 
63906db247cSraghuram 	status = ldc_reg_callback(ldcp->ldc_handle, vsw_ldc_cb, (caddr_t)ldcp);
64006db247cSraghuram 	if (status != 0) {
64106db247cSraghuram 		DERR(vswp, "%s(%lld): ldc_reg_callback failed, rv (%d)",
64206db247cSraghuram 		    __func__, ldc_id, status);
64306db247cSraghuram 		(void) ldc_fini(ldcp->ldc_handle);
64406db247cSraghuram 		goto ldc_attach_fail;
64506db247cSraghuram 	}
646f0ca1d9aSsb 	/*
647f0ca1d9aSsb 	 * allocate a message for ldc_read()s, big enough to hold ctrl and
648f0ca1d9aSsb 	 * data msgs, including raw data msgs used to recv priority frames.
649f0ca1d9aSsb 	 */
650c1c61f44Ssb 	ldcp->msglen = VIO_PKT_DATA_HDRSIZE + vswp->max_frame_size;
651f0ca1d9aSsb 	ldcp->ldcmsg = kmem_alloc(ldcp->msglen, KM_SLEEP);
65206db247cSraghuram 
65306db247cSraghuram 	progress |= PROG_callback;
65406db247cSraghuram 
65506db247cSraghuram 	mutex_init(&ldcp->status_lock, NULL, MUTEX_DRIVER, NULL);
65606db247cSraghuram 
65706db247cSraghuram 	if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
65806db247cSraghuram 		DERR(vswp, "%s: ldc_status failed", __func__);
65906db247cSraghuram 		mutex_destroy(&ldcp->status_lock);
66006db247cSraghuram 		goto ldc_attach_fail;
66106db247cSraghuram 	}
66206db247cSraghuram 
66306db247cSraghuram 	ldcp->ldc_status = istatus;
66406db247cSraghuram 	ldcp->ldc_port = port;
66506db247cSraghuram 	ldcp->ldc_vswp = vswp;
66606db247cSraghuram 
667f0ca1d9aSsb 	vsw_reset_vnet_proto_ops(ldcp);
668f0ca1d9aSsb 
66906db247cSraghuram 	(void) sprintf(kname, "%sldc0x%lx", DRV_NAME, ldcp->ldc_id);
67006db247cSraghuram 	ldcp->ksp = vgen_setup_kstats(DRV_NAME, vswp->instance,
67106db247cSraghuram 	    kname, &ldcp->ldc_stats);
67206db247cSraghuram 	if (ldcp->ksp == NULL) {
67306db247cSraghuram 		DERR(vswp, "%s: kstats setup failed", __func__);
67406db247cSraghuram 		goto ldc_attach_fail;
67506db247cSraghuram 	}
67606db247cSraghuram 
6777bd3a2e2SSriharsha Basavapatna 	/* link it into this port */
6787bd3a2e2SSriharsha Basavapatna 	port->ldcp = ldcp;
67906db247cSraghuram 
68006db247cSraghuram 	D1(vswp, "%s: exit", __func__);
68106db247cSraghuram 	return (0);
68206db247cSraghuram 
68306db247cSraghuram ldc_attach_fail:
68406db247cSraghuram 
68506db247cSraghuram 	if (progress & PROG_callback) {
68606db247cSraghuram 		(void) ldc_unreg_callback(ldcp->ldc_handle);
687f0ca1d9aSsb 		kmem_free(ldcp->ldcmsg, ldcp->msglen);
68806db247cSraghuram 	}
68906db247cSraghuram 
69006db247cSraghuram 	if (progress & PROG_tx_thread) {
69106db247cSraghuram 		if (ldcp->tx_thread != NULL) {
69206db247cSraghuram 			vsw_stop_tx_thread(ldcp);
69306db247cSraghuram 		}
69406db247cSraghuram 		mutex_destroy(&ldcp->tx_thr_lock);
69506db247cSraghuram 		cv_destroy(&ldcp->tx_thr_cv);
69606db247cSraghuram 	}
69706db247cSraghuram 	if (ldcp->ksp != NULL) {
69806db247cSraghuram 		vgen_destroy_kstats(ldcp->ksp);
69906db247cSraghuram 	}
7007bd3a2e2SSriharsha Basavapatna 	mutex_destroy(&ldcp->msg_thr_lock);
7017bd3a2e2SSriharsha Basavapatna 	mutex_destroy(&ldcp->rcv_thr_lock);
70206db247cSraghuram 	mutex_destroy(&ldcp->ldc_txlock);
70306db247cSraghuram 	mutex_destroy(&ldcp->ldc_rxlock);
70406db247cSraghuram 	mutex_destroy(&ldcp->ldc_cblock);
70506db247cSraghuram 	mutex_destroy(&ldcp->drain_cv_lock);
7067bd3a2e2SSriharsha Basavapatna 	cv_destroy(&ldcp->msg_thr_cv);
7077bd3a2e2SSriharsha Basavapatna 	cv_destroy(&ldcp->rcv_thr_cv);
70806db247cSraghuram 	cv_destroy(&ldcp->drain_cv);
70906db247cSraghuram 
71006db247cSraghuram 	kmem_free(ldcp, sizeof (vsw_ldc_t));
71106db247cSraghuram 
71206db247cSraghuram 	return (1);
71306db247cSraghuram }
71406db247cSraghuram 
71506db247cSraghuram /*
71606db247cSraghuram  * Detach a logical domain channel (ldc) belonging to a
71706db247cSraghuram  * particular port.
71806db247cSraghuram  */
7196f09f0feSWENTAO YANG static void
vsw_ldc_detach(vsw_ldc_t * ldcp)7207bd3a2e2SSriharsha Basavapatna vsw_ldc_detach(vsw_ldc_t *ldcp)
72106db247cSraghuram {
722*6e472272SToomas Soome 	int		rv;
723*6e472272SToomas Soome 	vsw_t		*vswp = ldcp->ldc_port->p_vswp;
7246f09f0feSWENTAO YANG 	int		retries = 0;
72506db247cSraghuram 
72606db247cSraghuram 	D2(vswp, "%s: detaching channel %lld", __func__, ldcp->ldc_id);
72706db247cSraghuram 
7287bd3a2e2SSriharsha Basavapatna 	/* Stop msg/rcv thread */
7297bd3a2e2SSriharsha Basavapatna 	if (ldcp->rcv_thread != NULL) {
7307bd3a2e2SSriharsha Basavapatna 		vsw_stop_rcv_thread(ldcp);
7317bd3a2e2SSriharsha Basavapatna 	} else if (ldcp->msg_thread != NULL) {
7327bd3a2e2SSriharsha Basavapatna 		vsw_stop_msg_thread(ldcp);
73306db247cSraghuram 	}
734f0ca1d9aSsb 	kmem_free(ldcp->ldcmsg, ldcp->msglen);
73506db247cSraghuram 
73606db247cSraghuram 	/* Stop the tx thread */
73706db247cSraghuram 	if (ldcp->tx_thread != NULL) {
73806db247cSraghuram 		vsw_stop_tx_thread(ldcp);
73906db247cSraghuram 		mutex_destroy(&ldcp->tx_thr_lock);
74006db247cSraghuram 		cv_destroy(&ldcp->tx_thr_cv);
74106db247cSraghuram 		if (ldcp->tx_mhead != NULL) {
74206db247cSraghuram 			freemsgchain(ldcp->tx_mhead);
74306db247cSraghuram 			ldcp->tx_mhead = ldcp->tx_mtail = NULL;
744f0ca1d9aSsb 			ldcp->tx_cnt = 0;
74506db247cSraghuram 		}
74606db247cSraghuram 	}
74706db247cSraghuram 
74806db247cSraghuram 	/* Destory kstats */
74906db247cSraghuram 	vgen_destroy_kstats(ldcp->ksp);
75006db247cSraghuram 
75106db247cSraghuram 	/*
75206db247cSraghuram 	 * Before we can close the channel we must release any mapped
75306db247cSraghuram 	 * resources (e.g. drings).
75406db247cSraghuram 	 */
75506db247cSraghuram 	vsw_free_lane_resources(ldcp, INBOUND);
75606db247cSraghuram 	vsw_free_lane_resources(ldcp, OUTBOUND);
75706db247cSraghuram 
75806db247cSraghuram 	/*
7596f09f0feSWENTAO YANG 	 * Close the channel, retry on EAAGIN.
76006db247cSraghuram 	 */
7616f09f0feSWENTAO YANG 	while ((rv = ldc_close(ldcp->ldc_handle)) == EAGAIN) {
7626f09f0feSWENTAO YANG 		if (++retries > vsw_ldc_retries) {
7636f09f0feSWENTAO YANG 			break;
7646f09f0feSWENTAO YANG 		}
7656f09f0feSWENTAO YANG 		drv_usecwait(vsw_ldc_delay);
7666f09f0feSWENTAO YANG 	}
7676f09f0feSWENTAO YANG 	if (rv != 0) {
7686f09f0feSWENTAO YANG 		cmn_err(CE_NOTE,
7696f09f0feSWENTAO YANG 		    "!vsw%d: Error(%d) closing the channel(0x%lx)\n",
7706f09f0feSWENTAO YANG 		    vswp->instance, rv, ldcp->ldc_id);
77106db247cSraghuram 	}
77206db247cSraghuram 
77306db247cSraghuram 	(void) ldc_fini(ldcp->ldc_handle);
77406db247cSraghuram 
77506db247cSraghuram 	ldcp->ldc_status = LDC_INIT;
776*6e472272SToomas Soome 	ldcp->ldc_handle = 0;
77706db247cSraghuram 	ldcp->ldc_vswp = NULL;
77806db247cSraghuram 
7797bd3a2e2SSriharsha Basavapatna 	mutex_destroy(&ldcp->msg_thr_lock);
7807bd3a2e2SSriharsha Basavapatna 	mutex_destroy(&ldcp->rcv_thr_lock);
78106db247cSraghuram 	mutex_destroy(&ldcp->ldc_txlock);
78206db247cSraghuram 	mutex_destroy(&ldcp->ldc_rxlock);
78306db247cSraghuram 	mutex_destroy(&ldcp->ldc_cblock);
78406db247cSraghuram 	mutex_destroy(&ldcp->drain_cv_lock);
78506db247cSraghuram 	mutex_destroy(&ldcp->status_lock);
7867bd3a2e2SSriharsha Basavapatna 	cv_destroy(&ldcp->msg_thr_cv);
7877bd3a2e2SSriharsha Basavapatna 	cv_destroy(&ldcp->rcv_thr_cv);
7887bd3a2e2SSriharsha Basavapatna 	cv_destroy(&ldcp->drain_cv);
78906db247cSraghuram 
79006db247cSraghuram 	kmem_free(ldcp, sizeof (vsw_ldc_t));
79106db247cSraghuram }
79206db247cSraghuram 
79306db247cSraghuram /*
79406db247cSraghuram  * Open and attempt to bring up the channel. Note that channel
79506db247cSraghuram  * can only be brought up if peer has also opened channel.
79606db247cSraghuram  *
79706db247cSraghuram  * Returns 0 if can open and bring up channel, otherwise
79806db247cSraghuram  * returns 1.
79906db247cSraghuram  */
80006db247cSraghuram static int
vsw_ldc_init(vsw_ldc_t * ldcp)80106db247cSraghuram vsw_ldc_init(vsw_ldc_t *ldcp)
80206db247cSraghuram {
803*6e472272SToomas Soome 	vsw_t		*vswp = ldcp->ldc_vswp;
80406db247cSraghuram 	ldc_status_t	istatus = 0;
80506db247cSraghuram 	int		rv;
80606db247cSraghuram 
80706db247cSraghuram 	D1(vswp, "%s: enter", __func__);
80806db247cSraghuram 
80906db247cSraghuram 	LDC_ENTER_LOCK(ldcp);
81006db247cSraghuram 
81106db247cSraghuram 	/* don't start at 0 in case clients don't like that */
81206db247cSraghuram 	ldcp->next_ident = 1;
81306db247cSraghuram 
81406db247cSraghuram 	rv = ldc_open(ldcp->ldc_handle);
81506db247cSraghuram 	if (rv != 0) {
81606db247cSraghuram 		DERR(vswp, "%s: ldc_open failed: id(%lld) rv(%d)",
81706db247cSraghuram 		    __func__, ldcp->ldc_id, rv);
81806db247cSraghuram 		LDC_EXIT_LOCK(ldcp);
81906db247cSraghuram 		return (1);
82006db247cSraghuram 	}
82106db247cSraghuram 
82206db247cSraghuram 	if (ldc_status(ldcp->ldc_handle, &istatus) != 0) {
82306db247cSraghuram 		DERR(vswp, "%s: unable to get status", __func__);
82406db247cSraghuram 		LDC_EXIT_LOCK(ldcp);
82506db247cSraghuram 		return (1);
82606db247cSraghuram 
82706db247cSraghuram 	} else if (istatus != LDC_OPEN && istatus != LDC_READY) {
82806db247cSraghuram 		DERR(vswp, "%s: id (%lld) status(%d) is not OPEN/READY",
82906db247cSraghuram 		    __func__, ldcp->ldc_id, istatus);
83006db247cSraghuram 		LDC_EXIT_LOCK(ldcp);
83106db247cSraghuram 		return (1);
83206db247cSraghuram 	}
83306db247cSraghuram 
83406db247cSraghuram 	mutex_enter(&ldcp->status_lock);
83506db247cSraghuram 	ldcp->ldc_status = istatus;
83606db247cSraghuram 	mutex_exit(&ldcp->status_lock);
83706db247cSraghuram 
83806db247cSraghuram 	rv = ldc_up(ldcp->ldc_handle);
83906db247cSraghuram 	if (rv != 0) {
84006db247cSraghuram 		/*
84106db247cSraghuram 		 * Not a fatal error for ldc_up() to fail, as peer
84206db247cSraghuram 		 * end point may simply not be ready yet.
84306db247cSraghuram 		 */
84406db247cSraghuram 		D2(vswp, "%s: ldc_up err id(%lld) rv(%d)", __func__,
84506db247cSraghuram 		    ldcp->ldc_id, rv);
84606db247cSraghuram 		LDC_EXIT_LOCK(ldcp);
84706db247cSraghuram 		return (1);
84806db247cSraghuram 	}
84906db247cSraghuram 
85006db247cSraghuram 	/*
85106db247cSraghuram 	 * ldc_up() call is non-blocking so need to explicitly
85206db247cSraghuram 	 * check channel status to see if in fact the channel
85306db247cSraghuram 	 * is UP.
85406db247cSraghuram 	 */
85506db247cSraghuram 	mutex_enter(&ldcp->status_lock);
85606db247cSraghuram 	if (ldc_status(ldcp->ldc_handle, &ldcp->ldc_status) != 0) {
85706db247cSraghuram 		DERR(vswp, "%s: unable to get status", __func__);
85806db247cSraghuram 		mutex_exit(&ldcp->status_lock);
85906db247cSraghuram 		LDC_EXIT_LOCK(ldcp);
86006db247cSraghuram 		return (1);
86106db247cSraghuram 
86206db247cSraghuram 	}
86306db247cSraghuram 
86406db247cSraghuram 	if (ldcp->ldc_status == LDC_UP) {
86506db247cSraghuram 		D2(vswp, "%s: channel %ld now UP (%ld)", __func__,
86606db247cSraghuram 		    ldcp->ldc_id, istatus);
86706db247cSraghuram 		mutex_exit(&ldcp->status_lock);
86806db247cSraghuram 		LDC_EXIT_LOCK(ldcp);
86906db247cSraghuram 
87006db247cSraghuram 		vsw_process_conn_evt(ldcp, VSW_CONN_UP);
87106db247cSraghuram 		return (0);
87206db247cSraghuram 	}
87306db247cSraghuram 
87406db247cSraghuram 	mutex_exit(&ldcp->status_lock);
87506db247cSraghuram 	LDC_EXIT_LOCK(ldcp);
87606db247cSraghuram 
87706db247cSraghuram 	D1(vswp, "%s: exit", __func__);
87806db247cSraghuram 	return (0);
87906db247cSraghuram }
88006db247cSraghuram 
88106db247cSraghuram /* disable callbacks on the channel */
8826f09f0feSWENTAO YANG static void
vsw_ldc_uninit(vsw_ldc_t * ldcp)88306db247cSraghuram vsw_ldc_uninit(vsw_ldc_t *ldcp)
88406db247cSraghuram {
88506db247cSraghuram 	vsw_t	*vswp = ldcp->ldc_vswp;
88606db247cSraghuram 	int	rv;
88706db247cSraghuram 
88806db247cSraghuram 	D1(vswp, "vsw_ldc_uninit: enter: id(%lx)\n", ldcp->ldc_id);
88906db247cSraghuram 
89006db247cSraghuram 	LDC_ENTER_LOCK(ldcp);
89106db247cSraghuram 
89206db247cSraghuram 	rv = ldc_set_cb_mode(ldcp->ldc_handle, LDC_CB_DISABLE);
89306db247cSraghuram 	if (rv != 0) {
8946f09f0feSWENTAO YANG 		cmn_err(CE_NOTE, "!vsw_ldc_uninit(%ld): error disabling "
89506db247cSraghuram 		    "interrupts (rv = %d)\n", ldcp->ldc_id, rv);
89606db247cSraghuram 	}
89706db247cSraghuram 
89806db247cSraghuram 	mutex_enter(&ldcp->status_lock);
89906db247cSraghuram 	ldcp->ldc_status = LDC_INIT;
90006db247cSraghuram 	mutex_exit(&ldcp->status_lock);
90106db247cSraghuram 
90206db247cSraghuram 	LDC_EXIT_LOCK(ldcp);
90306db247cSraghuram 
90406db247cSraghuram 	D1(vswp, "vsw_ldc_uninit: exit: id(%lx)", ldcp->ldc_id);
90506db247cSraghuram }
90606db247cSraghuram 
90706db247cSraghuram /*
90806db247cSraghuram  * Wait until the callback(s) associated with the ldcs under the specified
90906db247cSraghuram  * port have completed.
91006db247cSraghuram  *
91106db247cSraghuram  * Prior to this function being invoked each channel under this port
91206db247cSraghuram  * should have been quiesced via ldc_set_cb_mode(DISABLE).
91306db247cSraghuram  *
91406db247cSraghuram  * A short explaination of what we are doing below..
91506db247cSraghuram  *
91606db247cSraghuram  * The simplest approach would be to have a reference counter in
91706db247cSraghuram  * the ldc structure which is increment/decremented by the callbacks as
91806db247cSraghuram  * they use the channel. The drain function could then simply disable any
91906db247cSraghuram  * further callbacks and do a cv_wait for the ref to hit zero. Unfortunately
92006db247cSraghuram  * there is a tiny window here - before the callback is able to get the lock
92106db247cSraghuram  * on the channel it is interrupted and this function gets to execute. It
92206db247cSraghuram  * sees that the ref count is zero and believes its free to delete the
92306db247cSraghuram  * associated data structures.
92406db247cSraghuram  *
92506db247cSraghuram  * We get around this by taking advantage of the fact that before the ldc
92606db247cSraghuram  * framework invokes a callback it sets a flag to indicate that there is a
92706db247cSraghuram  * callback active (or about to become active). If when we attempt to
92806db247cSraghuram  * unregister a callback when this active flag is set then the unregister
92906db247cSraghuram  * will fail with EWOULDBLOCK.
93006db247cSraghuram  *
93106db247cSraghuram  * If the unregister fails we do a cv_timedwait. We will either be signaled
93206db247cSraghuram  * by the callback as it is exiting (note we have to wait a short period to
93306db247cSraghuram  * allow the callback to return fully to the ldc framework and it to clear
93406db247cSraghuram  * the active flag), or by the timer expiring. In either case we again attempt
93506db247cSraghuram  * the unregister. We repeat this until we can succesfully unregister the
93606db247cSraghuram  * callback.
93706db247cSraghuram  *
93806db247cSraghuram  * The reason we use a cv_timedwait rather than a simple cv_wait is to catch
93906db247cSraghuram  * the case where the callback has finished but the ldc framework has not yet
94006db247cSraghuram  * cleared the active flag. In this case we would never get a cv_signal.
94106db247cSraghuram  */
9426f09f0feSWENTAO YANG static void
vsw_ldc_drain(vsw_ldc_t * ldcp)9437bd3a2e2SSriharsha Basavapatna vsw_ldc_drain(vsw_ldc_t *ldcp)
94406db247cSraghuram {
9457bd3a2e2SSriharsha Basavapatna 	vsw_t	*vswp = ldcp->ldc_port->p_vswp;
94606db247cSraghuram 
94706db247cSraghuram 	D1(vswp, "%s: enter", __func__);
94806db247cSraghuram 
9497bd3a2e2SSriharsha Basavapatna 	/*
9507bd3a2e2SSriharsha Basavapatna 	 * If we can unregister the channel callback then we
9517bd3a2e2SSriharsha Basavapatna 	 * know that there is no callback either running or
9527bd3a2e2SSriharsha Basavapatna 	 * scheduled to run for this channel so move on to next
9537bd3a2e2SSriharsha Basavapatna 	 * channel in the list.
9547bd3a2e2SSriharsha Basavapatna 	 */
9557bd3a2e2SSriharsha Basavapatna 	mutex_enter(&ldcp->drain_cv_lock);
95606db247cSraghuram 
9577bd3a2e2SSriharsha Basavapatna 	/* prompt active callbacks to quit */
9587bd3a2e2SSriharsha Basavapatna 	ldcp->drain_state = VSW_LDC_DRAINING;
95906db247cSraghuram 
9607bd3a2e2SSriharsha Basavapatna 	if ((ldc_unreg_callback(ldcp->ldc_handle)) == 0) {
9617bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: unreg callback for chan %ld", __func__,
9627bd3a2e2SSriharsha Basavapatna 		    ldcp->ldc_id);
9637bd3a2e2SSriharsha Basavapatna 		mutex_exit(&ldcp->drain_cv_lock);
9647bd3a2e2SSriharsha Basavapatna 	} else {
96506db247cSraghuram 		/*
9667bd3a2e2SSriharsha Basavapatna 		 * If we end up here we know that either 1) a callback
9677bd3a2e2SSriharsha Basavapatna 		 * is currently executing, 2) is about to start (i.e.
9687bd3a2e2SSriharsha Basavapatna 		 * the ldc framework has set the active flag but
9697bd3a2e2SSriharsha Basavapatna 		 * has not actually invoked the callback yet, or 3)
9707bd3a2e2SSriharsha Basavapatna 		 * has finished and has returned to the ldc framework
9717bd3a2e2SSriharsha Basavapatna 		 * but the ldc framework has not yet cleared the
9727bd3a2e2SSriharsha Basavapatna 		 * active bit.
9737bd3a2e2SSriharsha Basavapatna 		 *
9747bd3a2e2SSriharsha Basavapatna 		 * Wait for it to finish.
97506db247cSraghuram 		 */
9767bd3a2e2SSriharsha Basavapatna 		while (ldc_unreg_callback(ldcp->ldc_handle) == EWOULDBLOCK) {
9777bd3a2e2SSriharsha Basavapatna 			(void) cv_timedwait(&ldcp->drain_cv,
9787bd3a2e2SSriharsha Basavapatna 			    &ldcp->drain_cv_lock, ddi_get_lbolt() + hz);
97906db247cSraghuram 		}
9807bd3a2e2SSriharsha Basavapatna 
9817bd3a2e2SSriharsha Basavapatna 		mutex_exit(&ldcp->drain_cv_lock);
9827bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: unreg callback for chan %ld after "
9837bd3a2e2SSriharsha Basavapatna 		    "timeout", __func__, ldcp->ldc_id);
98406db247cSraghuram 	}
98506db247cSraghuram 
98606db247cSraghuram 	D1(vswp, "%s: exit", __func__);
98706db247cSraghuram }
98806db247cSraghuram 
98906db247cSraghuram /*
99006db247cSraghuram  * Wait until all tasks which reference this port have completed.
99106db247cSraghuram  *
99206db247cSraghuram  * Prior to this function being invoked each channel under this port
99306db247cSraghuram  * should have been quiesced via ldc_set_cb_mode(DISABLE).
99406db247cSraghuram  */
9956f09f0feSWENTAO YANG static void
vsw_drain_port_taskq(vsw_port_t * port)99606db247cSraghuram vsw_drain_port_taskq(vsw_port_t *port)
99706db247cSraghuram {
99806db247cSraghuram 	vsw_t		*vswp = port->p_vswp;
99906db247cSraghuram 
100006db247cSraghuram 	D1(vswp, "%s: enter", __func__);
100106db247cSraghuram 
100206db247cSraghuram 	/*
100306db247cSraghuram 	 * Mark the port as in the process of being detached, and
100406db247cSraghuram 	 * dispatch a marker task to the queue so we know when all
100506db247cSraghuram 	 * relevant tasks have completed.
100606db247cSraghuram 	 */
100706db247cSraghuram 	mutex_enter(&port->state_lock);
100806db247cSraghuram 	port->state = VSW_PORT_DETACHING;
100906db247cSraghuram 
101006db247cSraghuram 	if ((vswp->taskq_p == NULL) ||
101106db247cSraghuram 	    (ddi_taskq_dispatch(vswp->taskq_p, vsw_marker_task,
101206db247cSraghuram 	    port, DDI_NOSLEEP) != DDI_SUCCESS)) {
10136f09f0feSWENTAO YANG 		cmn_err(CE_NOTE, "!vsw%d: unable to dispatch marker task",
10146f09f0feSWENTAO YANG 		    vswp->instance);
101506db247cSraghuram 		mutex_exit(&port->state_lock);
10166f09f0feSWENTAO YANG 		return;
101706db247cSraghuram 	}
101806db247cSraghuram 
101906db247cSraghuram 	/*
102006db247cSraghuram 	 * Wait for the marker task to finish.
102106db247cSraghuram 	 */
102206db247cSraghuram 	while (port->state != VSW_PORT_DETACHABLE)
102306db247cSraghuram 		cv_wait(&port->state_cv, &port->state_lock);
102406db247cSraghuram 
102506db247cSraghuram 	mutex_exit(&port->state_lock);
102606db247cSraghuram 
102706db247cSraghuram 	D1(vswp, "%s: exit", __func__);
102806db247cSraghuram }
102906db247cSraghuram 
103006db247cSraghuram static void
vsw_marker_task(void * arg)103106db247cSraghuram vsw_marker_task(void *arg)
103206db247cSraghuram {
103306db247cSraghuram 	vsw_port_t	*port = arg;
103406db247cSraghuram 	vsw_t		*vswp = port->p_vswp;
103506db247cSraghuram 
103606db247cSraghuram 	D1(vswp, "%s: enter", __func__);
103706db247cSraghuram 
103806db247cSraghuram 	mutex_enter(&port->state_lock);
103906db247cSraghuram 
104006db247cSraghuram 	/*
104106db247cSraghuram 	 * No further tasks should be dispatched which reference
104206db247cSraghuram 	 * this port so ok to mark it as safe to detach.
104306db247cSraghuram 	 */
104406db247cSraghuram 	port->state = VSW_PORT_DETACHABLE;
104506db247cSraghuram 
104606db247cSraghuram 	cv_signal(&port->state_cv);
104706db247cSraghuram 
104806db247cSraghuram 	mutex_exit(&port->state_lock);
104906db247cSraghuram 
105006db247cSraghuram 	D1(vswp, "%s: exit", __func__);
105106db247cSraghuram }
105206db247cSraghuram 
105306db247cSraghuram vsw_port_t *
vsw_lookup_port(vsw_t * vswp,int p_instance)105406db247cSraghuram vsw_lookup_port(vsw_t *vswp, int p_instance)
105506db247cSraghuram {
105606db247cSraghuram 	vsw_port_list_t *plist = &vswp->plist;
105706db247cSraghuram 	vsw_port_t	*port;
105806db247cSraghuram 
105906db247cSraghuram 	for (port = plist->head; port != NULL; port = port->p_next) {
106006db247cSraghuram 		if (port->p_instance == p_instance) {
106106db247cSraghuram 			D2(vswp, "vsw_lookup_port: found p_instance\n");
106206db247cSraghuram 			return (port);
106306db247cSraghuram 		}
106406db247cSraghuram 	}
106506db247cSraghuram 
106606db247cSraghuram 	return (NULL);
106706db247cSraghuram }
106806db247cSraghuram 
1069c1c61f44Ssb void
vsw_vlan_unaware_port_reset(vsw_port_t * portp)1070c1c61f44Ssb vsw_vlan_unaware_port_reset(vsw_port_t *portp)
1071c1c61f44Ssb {
10727bd3a2e2SSriharsha Basavapatna 	vsw_ldc_t	*ldcp = portp->ldcp;
1073c1c61f44Ssb 
1074c1c61f44Ssb 	mutex_enter(&ldcp->ldc_cblock);
1075c1c61f44Ssb 
1076c1c61f44Ssb 	/*
1077c1c61f44Ssb 	 * If the peer is vlan_unaware(ver < 1.3), reset channel and terminate
1078c1c61f44Ssb 	 * the connection. See comments in vsw_set_vnet_proto_ops().
1079c1c61f44Ssb 	 */
1080c1c61f44Ssb 	if (ldcp->hphase == VSW_MILESTONE4 && VSW_VER_LT(ldcp, 1, 3) &&
1081c1c61f44Ssb 	    portp->nvids != 0) {
1082c1c61f44Ssb 		vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
1083c1c61f44Ssb 	}
1084c1c61f44Ssb 
1085c1c61f44Ssb 	mutex_exit(&ldcp->ldc_cblock);
1086c1c61f44Ssb }
1087c1c61f44Ssb 
1088678453a8Sspeer void
vsw_hio_port_reset(vsw_port_t * portp,boolean_t immediate)1089cdfc78adSraghuram vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate)
1090678453a8Sspeer {
10917bd3a2e2SSriharsha Basavapatna 	vsw_ldc_t	*ldcp = portp->ldcp;
1092678453a8Sspeer 
1093678453a8Sspeer 	mutex_enter(&ldcp->ldc_cblock);
1094678453a8Sspeer 
1095678453a8Sspeer 	/*
1096678453a8Sspeer 	 * If the peer is HybridIO capable (ver >= 1.3), reset channel
1097678453a8Sspeer 	 * to trigger re-negotiation, which inturn trigger HybridIO
1098678453a8Sspeer 	 * setup/cleanup.
1099678453a8Sspeer 	 */
1100678453a8Sspeer 	if ((ldcp->hphase == VSW_MILESTONE4) &&
1101678453a8Sspeer 	    (portp->p_hio_capable == B_TRUE)) {
1102cdfc78adSraghuram 		if (immediate == B_TRUE) {
1103cdfc78adSraghuram 			(void) ldc_down(ldcp->ldc_handle);
1104cdfc78adSraghuram 		} else {
1105cdfc78adSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
1106cdfc78adSraghuram 		}
1107678453a8Sspeer 	}
1108678453a8Sspeer 
1109678453a8Sspeer 	mutex_exit(&ldcp->ldc_cblock);
1110678453a8Sspeer }
1111678453a8Sspeer 
11127b1f684aSSriharsha Basavapatna void
vsw_port_reset(vsw_port_t * portp)11137b1f684aSSriharsha Basavapatna vsw_port_reset(vsw_port_t *portp)
11147b1f684aSSriharsha Basavapatna {
11157bd3a2e2SSriharsha Basavapatna 	vsw_ldc_t	*ldcp = portp->ldcp;
11167b1f684aSSriharsha Basavapatna 
11177b1f684aSSriharsha Basavapatna 	mutex_enter(&ldcp->ldc_cblock);
11187b1f684aSSriharsha Basavapatna 
11197b1f684aSSriharsha Basavapatna 	/*
11207b1f684aSSriharsha Basavapatna 	 * reset channel and terminate the connection.
11217b1f684aSSriharsha Basavapatna 	 */
11227b1f684aSSriharsha Basavapatna 	vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
11237b1f684aSSriharsha Basavapatna 
11247b1f684aSSriharsha Basavapatna 	mutex_exit(&ldcp->ldc_cblock);
11257b1f684aSSriharsha Basavapatna }
11267b1f684aSSriharsha Basavapatna 
11277b1f684aSSriharsha Basavapatna void
vsw_reset_ports(vsw_t * vswp)11287b1f684aSSriharsha Basavapatna vsw_reset_ports(vsw_t *vswp)
11297b1f684aSSriharsha Basavapatna {
11307b1f684aSSriharsha Basavapatna 	vsw_port_list_t	*plist = &vswp->plist;
11317b1f684aSSriharsha Basavapatna 	vsw_port_t	*portp;
11327b1f684aSSriharsha Basavapatna 
11337b1f684aSSriharsha Basavapatna 	READ_ENTER(&plist->lockrw);
11347b1f684aSSriharsha Basavapatna 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
11357b1f684aSSriharsha Basavapatna 		if ((portp->p_hio_capable) && (portp->p_hio_enabled)) {
11367b1f684aSSriharsha Basavapatna 			vsw_hio_stop_port(portp);
11377b1f684aSSriharsha Basavapatna 		}
11387b1f684aSSriharsha Basavapatna 		vsw_port_reset(portp);
11397b1f684aSSriharsha Basavapatna 	}
11407b1f684aSSriharsha Basavapatna 	RW_EXIT(&plist->lockrw);
11417b1f684aSSriharsha Basavapatna }
11427b1f684aSSriharsha Basavapatna 
11431107ea93SSriharsha Basavapatna static void
vsw_send_physlink_msg(vsw_ldc_t * ldcp,link_state_t plink_state)11441107ea93SSriharsha Basavapatna vsw_send_physlink_msg(vsw_ldc_t *ldcp, link_state_t plink_state)
11451107ea93SSriharsha Basavapatna {
11461107ea93SSriharsha Basavapatna 	vnet_physlink_msg_t	msg;
11471107ea93SSriharsha Basavapatna 	vnet_physlink_msg_t	*msgp = &msg;
11481107ea93SSriharsha Basavapatna 	uint32_t		physlink_info = 0;
11491107ea93SSriharsha Basavapatna 
11501107ea93SSriharsha Basavapatna 	if (plink_state == LINK_STATE_UP) {
11511107ea93SSriharsha Basavapatna 		physlink_info |= VNET_PHYSLINK_STATE_UP;
11521107ea93SSriharsha Basavapatna 	} else {
11531107ea93SSriharsha Basavapatna 		physlink_info |= VNET_PHYSLINK_STATE_DOWN;
11541107ea93SSriharsha Basavapatna 	}
11551107ea93SSriharsha Basavapatna 
11561107ea93SSriharsha Basavapatna 	msgp->tag.vio_msgtype = VIO_TYPE_CTRL;
11571107ea93SSriharsha Basavapatna 	msgp->tag.vio_subtype = VIO_SUBTYPE_INFO;
11581107ea93SSriharsha Basavapatna 	msgp->tag.vio_subtype_env = VNET_PHYSLINK_INFO;
11591107ea93SSriharsha Basavapatna 	msgp->tag.vio_sid = ldcp->local_session;
11601107ea93SSriharsha Basavapatna 	msgp->physlink_info = physlink_info;
11611107ea93SSriharsha Basavapatna 
11621107ea93SSriharsha Basavapatna 	(void) vsw_send_msg(ldcp, msgp, sizeof (msg), B_TRUE);
11631107ea93SSriharsha Basavapatna }
11641107ea93SSriharsha Basavapatna 
11651107ea93SSriharsha Basavapatna static void
vsw_port_physlink_update(vsw_port_t * portp)11661107ea93SSriharsha Basavapatna vsw_port_physlink_update(vsw_port_t *portp)
11671107ea93SSriharsha Basavapatna {
11681107ea93SSriharsha Basavapatna 	vsw_ldc_t	*ldcp;
11691107ea93SSriharsha Basavapatna 	vsw_t		*vswp;
11701107ea93SSriharsha Basavapatna 
11711107ea93SSriharsha Basavapatna 	vswp = portp->p_vswp;
11727bd3a2e2SSriharsha Basavapatna 	ldcp = portp->ldcp;
11731107ea93SSriharsha Basavapatna 
11741107ea93SSriharsha Basavapatna 	mutex_enter(&ldcp->ldc_cblock);
11751107ea93SSriharsha Basavapatna 
11761107ea93SSriharsha Basavapatna 	/*
11771107ea93SSriharsha Basavapatna 	 * If handshake has completed successfully and if the vnet device
11781107ea93SSriharsha Basavapatna 	 * has negotiated to get physical link state updates, send a message
11791107ea93SSriharsha Basavapatna 	 * with the current state.
11801107ea93SSriharsha Basavapatna 	 */
11811107ea93SSriharsha Basavapatna 	if (ldcp->hphase == VSW_MILESTONE4 && ldcp->pls_negotiated == B_TRUE) {
11821107ea93SSriharsha Basavapatna 		vsw_send_physlink_msg(ldcp, vswp->phys_link_state);
11831107ea93SSriharsha Basavapatna 	}
11841107ea93SSriharsha Basavapatna 
11851107ea93SSriharsha Basavapatna 	mutex_exit(&ldcp->ldc_cblock);
11861107ea93SSriharsha Basavapatna }
11871107ea93SSriharsha Basavapatna 
11881107ea93SSriharsha Basavapatna void
vsw_physlink_update_ports(vsw_t * vswp)11891107ea93SSriharsha Basavapatna vsw_physlink_update_ports(vsw_t *vswp)
11901107ea93SSriharsha Basavapatna {
11911107ea93SSriharsha Basavapatna 	vsw_port_list_t	*plist = &vswp->plist;
11921107ea93SSriharsha Basavapatna 	vsw_port_t	*portp;
11931107ea93SSriharsha Basavapatna 
11941107ea93SSriharsha Basavapatna 	READ_ENTER(&plist->lockrw);
11951107ea93SSriharsha Basavapatna 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
11961107ea93SSriharsha Basavapatna 		vsw_port_physlink_update(portp);
11971107ea93SSriharsha Basavapatna 	}
11981107ea93SSriharsha Basavapatna 	RW_EXIT(&plist->lockrw);
11991107ea93SSriharsha Basavapatna }
12007b1f684aSSriharsha Basavapatna 
120106db247cSraghuram /*
120206db247cSraghuram  * Search for and remove the specified port from the port
120306db247cSraghuram  * list. Returns 0 if able to locate and remove port, otherwise
120406db247cSraghuram  * returns 1.
120506db247cSraghuram  */
120606db247cSraghuram static int
vsw_plist_del_node(vsw_t * vswp,vsw_port_t * port)120706db247cSraghuram vsw_plist_del_node(vsw_t *vswp, vsw_port_t *port)
120806db247cSraghuram {
120906db247cSraghuram 	vsw_port_list_t *plist = &vswp->plist;
121006db247cSraghuram 	vsw_port_t	*curr_p, *prev_p;
121106db247cSraghuram 
121206db247cSraghuram 	if (plist->head == NULL)
121306db247cSraghuram 		return (1);
121406db247cSraghuram 
121506db247cSraghuram 	curr_p = prev_p = plist->head;
121606db247cSraghuram 
121706db247cSraghuram 	while (curr_p != NULL) {
121806db247cSraghuram 		if (curr_p == port) {
121906db247cSraghuram 			if (prev_p == curr_p) {
122006db247cSraghuram 				plist->head = curr_p->p_next;
122106db247cSraghuram 			} else {
122206db247cSraghuram 				prev_p->p_next = curr_p->p_next;
122306db247cSraghuram 			}
122406db247cSraghuram 			plist->num_ports--;
122506db247cSraghuram 			break;
122606db247cSraghuram 		} else {
122706db247cSraghuram 			prev_p = curr_p;
122806db247cSraghuram 			curr_p = curr_p->p_next;
122906db247cSraghuram 		}
123006db247cSraghuram 	}
123106db247cSraghuram 	return (0);
123206db247cSraghuram }
123306db247cSraghuram 
123406db247cSraghuram /*
123506db247cSraghuram  * Interrupt handler for ldc messages.
123606db247cSraghuram  */
123706db247cSraghuram static uint_t
vsw_ldc_cb(uint64_t event,caddr_t arg)123806db247cSraghuram vsw_ldc_cb(uint64_t event, caddr_t arg)
123906db247cSraghuram {
124006db247cSraghuram 	vsw_ldc_t	*ldcp = (vsw_ldc_t  *)arg;
1241*6e472272SToomas Soome 	vsw_t		*vswp = ldcp->ldc_vswp;
124206db247cSraghuram 
124306db247cSraghuram 	D1(vswp, "%s: enter: ldcid (%lld)\n", __func__, ldcp->ldc_id);
124406db247cSraghuram 
124506db247cSraghuram 	mutex_enter(&ldcp->ldc_cblock);
124606db247cSraghuram 	ldcp->ldc_stats.callbacks++;
124706db247cSraghuram 
124806db247cSraghuram 	mutex_enter(&ldcp->status_lock);
1249*6e472272SToomas Soome 	if ((ldcp->ldc_status == LDC_INIT) || (ldcp->ldc_handle == 0)) {
125006db247cSraghuram 		mutex_exit(&ldcp->status_lock);
125106db247cSraghuram 		mutex_exit(&ldcp->ldc_cblock);
125206db247cSraghuram 		return (LDC_SUCCESS);
125306db247cSraghuram 	}
125406db247cSraghuram 	mutex_exit(&ldcp->status_lock);
125506db247cSraghuram 
125606db247cSraghuram 	if (event & LDC_EVT_UP) {
125706db247cSraghuram 		/*
125806db247cSraghuram 		 * Channel has come up.
125906db247cSraghuram 		 */
126006db247cSraghuram 		D2(vswp, "%s: id(%ld) event(%llx) UP: status(%ld)",
126106db247cSraghuram 		    __func__, ldcp->ldc_id, event, ldcp->ldc_status);
126206db247cSraghuram 
126306db247cSraghuram 		vsw_process_conn_evt(ldcp, VSW_CONN_UP);
126406db247cSraghuram 
126506db247cSraghuram 		ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
126606db247cSraghuram 	}
126706db247cSraghuram 
126806db247cSraghuram 	if (event & LDC_EVT_READ) {
126906db247cSraghuram 		/*
127006db247cSraghuram 		 * Data available for reading.
127106db247cSraghuram 		 */
127206db247cSraghuram 		D2(vswp, "%s: id(ld) event(%llx) data READ",
127306db247cSraghuram 		    __func__, ldcp->ldc_id, event);
127406db247cSraghuram 
12757bd3a2e2SSriharsha Basavapatna 		vsw_process_evt_read(ldcp);
127606db247cSraghuram 
127706db247cSraghuram 		ASSERT((event & (LDC_EVT_RESET | LDC_EVT_DOWN)) == 0);
127806db247cSraghuram 
127906db247cSraghuram 		goto vsw_cb_exit;
128006db247cSraghuram 	}
128106db247cSraghuram 
128206db247cSraghuram 	if (event & (LDC_EVT_DOWN | LDC_EVT_RESET)) {
128306db247cSraghuram 		D2(vswp, "%s: id(%ld) event (%lx) DOWN/RESET: status(%ld)",
128406db247cSraghuram 		    __func__, ldcp->ldc_id, event, ldcp->ldc_status);
128506db247cSraghuram 
128606db247cSraghuram 		vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
128706db247cSraghuram 	}
128806db247cSraghuram 
128906db247cSraghuram 	/*
129006db247cSraghuram 	 * Catch either LDC_EVT_WRITE which we don't support or any
129106db247cSraghuram 	 * unknown event.
129206db247cSraghuram 	 */
129306db247cSraghuram 	if (event &
129406db247cSraghuram 	    ~(LDC_EVT_UP | LDC_EVT_RESET | LDC_EVT_DOWN | LDC_EVT_READ)) {
129506db247cSraghuram 		DERR(vswp, "%s: id(%ld) Unexpected event=(%llx) status(%ld)",
129606db247cSraghuram 		    __func__, ldcp->ldc_id, event, ldcp->ldc_status);
129706db247cSraghuram 	}
129806db247cSraghuram 
129906db247cSraghuram vsw_cb_exit:
130006db247cSraghuram 	mutex_exit(&ldcp->ldc_cblock);
130106db247cSraghuram 
130206db247cSraghuram 	/*
130306db247cSraghuram 	 * Let the drain function know we are finishing if it
130406db247cSraghuram 	 * is waiting.
130506db247cSraghuram 	 */
130606db247cSraghuram 	mutex_enter(&ldcp->drain_cv_lock);
130706db247cSraghuram 	if (ldcp->drain_state == VSW_LDC_DRAINING)
130806db247cSraghuram 		cv_signal(&ldcp->drain_cv);
130906db247cSraghuram 	mutex_exit(&ldcp->drain_cv_lock);
131006db247cSraghuram 
131106db247cSraghuram 	return (LDC_SUCCESS);
131206db247cSraghuram }
131306db247cSraghuram 
131406db247cSraghuram /*
131506db247cSraghuram  * Reinitialise data structures associated with the channel.
131606db247cSraghuram  */
131706db247cSraghuram static void
vsw_ldc_reinit(vsw_ldc_t * ldcp)131806db247cSraghuram vsw_ldc_reinit(vsw_ldc_t *ldcp)
131906db247cSraghuram {
132006db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
132106db247cSraghuram 	vsw_port_t	*port;
132206db247cSraghuram 
132306db247cSraghuram 	D1(vswp, "%s: enter", __func__);
132406db247cSraghuram 
132506db247cSraghuram 	port = ldcp->ldc_port;
132606db247cSraghuram 
132706db247cSraghuram 	D2(vswp, "%s: in 0x%llx : out 0x%llx", __func__,
132806db247cSraghuram 	    ldcp->lane_in.lstate, ldcp->lane_out.lstate);
132906db247cSraghuram 
133006db247cSraghuram 	vsw_free_lane_resources(ldcp, INBOUND);
133106db247cSraghuram 	vsw_free_lane_resources(ldcp, OUTBOUND);
133206db247cSraghuram 
133306db247cSraghuram 	ldcp->lane_in.lstate = 0;
133406db247cSraghuram 	ldcp->lane_out.lstate = 0;
133506db247cSraghuram 
133606db247cSraghuram 	/*
133706db247cSraghuram 	 * Remove parent port from any multicast groups
133806db247cSraghuram 	 * it may have registered with. Client must resend
133906db247cSraghuram 	 * multicast add command after handshake completes.
134006db247cSraghuram 	 */
134106db247cSraghuram 	vsw_del_mcst_port(port);
134206db247cSraghuram 
134306db247cSraghuram 	ldcp->peer_session = 0;
134406db247cSraghuram 	ldcp->session_status = 0;
134506db247cSraghuram 	ldcp->hcnt = 0;
134606db247cSraghuram 	ldcp->hphase = VSW_MILESTONE0;
1347f0ca1d9aSsb 
1348f0ca1d9aSsb 	vsw_reset_vnet_proto_ops(ldcp);
134906db247cSraghuram 
135006db247cSraghuram 	D1(vswp, "%s: exit", __func__);
135106db247cSraghuram }
135206db247cSraghuram 
135306db247cSraghuram /*
135406db247cSraghuram  * Process a connection event.
135506db247cSraghuram  */
13567bd3a2e2SSriharsha Basavapatna void
vsw_process_conn_evt(vsw_ldc_t * ldcp,uint16_t evt)135706db247cSraghuram vsw_process_conn_evt(vsw_ldc_t *ldcp, uint16_t evt)
135806db247cSraghuram {
135906db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
136006db247cSraghuram 	vsw_conn_evt_t	*conn = NULL;
136106db247cSraghuram 
136206db247cSraghuram 	D1(vswp, "%s: enter", __func__);
136306db247cSraghuram 
136406db247cSraghuram 	/*
136506db247cSraghuram 	 * Check if either a reset or restart event is pending
136606db247cSraghuram 	 * or in progress. If so just return.
136706db247cSraghuram 	 *
136806db247cSraghuram 	 * A VSW_CONN_RESET event originates either with a LDC_RESET_EVT
136906db247cSraghuram 	 * being received by the callback handler, or a ECONNRESET error
137006db247cSraghuram 	 * code being returned from a ldc_read() or ldc_write() call.
137106db247cSraghuram 	 *
137206db247cSraghuram 	 * A VSW_CONN_RESTART event occurs when some error checking code
137306db247cSraghuram 	 * decides that there is a problem with data from the channel,
137406db247cSraghuram 	 * and that the handshake should be restarted.
137506db247cSraghuram 	 */
137606db247cSraghuram 	if (((evt == VSW_CONN_RESET) || (evt == VSW_CONN_RESTART)) &&
137706db247cSraghuram 	    (ldstub((uint8_t *)&ldcp->reset_active)))
137806db247cSraghuram 		return;
137906db247cSraghuram 
138006db247cSraghuram 	/*
138106db247cSraghuram 	 * If it is an LDC_UP event we first check the recorded
138206db247cSraghuram 	 * state of the channel. If this is UP then we know that
138306db247cSraghuram 	 * the channel moving to the UP state has already been dealt
138406db247cSraghuram 	 * with and don't need to dispatch a  new task.
138506db247cSraghuram 	 *
138606db247cSraghuram 	 * The reason for this check is that when we do a ldc_up(),
138706db247cSraghuram 	 * depending on the state of the peer, we may or may not get
138806db247cSraghuram 	 * a LDC_UP event. As we can't depend on getting a LDC_UP evt
138906db247cSraghuram 	 * every time we do ldc_up() we explicitly check the channel
139006db247cSraghuram 	 * status to see has it come up (ldc_up() is asynch and will
139106db247cSraghuram 	 * complete at some undefined time), and take the appropriate
139206db247cSraghuram 	 * action.
139306db247cSraghuram 	 *
139406db247cSraghuram 	 * The flip side of this is that we may get a LDC_UP event
139506db247cSraghuram 	 * when we have already seen that the channel is up and have
139606db247cSraghuram 	 * dealt with that.
139706db247cSraghuram 	 */
139806db247cSraghuram 	mutex_enter(&ldcp->status_lock);
139906db247cSraghuram 	if (evt == VSW_CONN_UP) {
140006db247cSraghuram 		if ((ldcp->ldc_status == LDC_UP) || (ldcp->reset_active != 0)) {
140106db247cSraghuram 			mutex_exit(&ldcp->status_lock);
140206db247cSraghuram 			return;
140306db247cSraghuram 		}
140406db247cSraghuram 	}
140506db247cSraghuram 	mutex_exit(&ldcp->status_lock);
140606db247cSraghuram 
140706db247cSraghuram 	/*
140806db247cSraghuram 	 * The transaction group id allows us to identify and discard
140906db247cSraghuram 	 * any tasks which are still pending on the taskq and refer
141006db247cSraghuram 	 * to the handshake session we are about to restart or reset.
141106db247cSraghuram 	 * These stale messages no longer have any real meaning.
141206db247cSraghuram 	 */
141306db247cSraghuram 	(void) atomic_inc_32(&ldcp->hss_id);
141406db247cSraghuram 
141506db247cSraghuram 	ASSERT(vswp->taskq_p != NULL);
141606db247cSraghuram 
141706db247cSraghuram 	if ((conn = kmem_zalloc(sizeof (vsw_conn_evt_t), KM_NOSLEEP)) == NULL) {
141806db247cSraghuram 		cmn_err(CE_WARN, "!vsw%d: unable to allocate memory for"
141906db247cSraghuram 		    " connection event", vswp->instance);
142006db247cSraghuram 		goto err_exit;
142106db247cSraghuram 	}
142206db247cSraghuram 
142306db247cSraghuram 	conn->evt = evt;
142406db247cSraghuram 	conn->ldcp = ldcp;
142506db247cSraghuram 
142606db247cSraghuram 	if (ddi_taskq_dispatch(vswp->taskq_p, vsw_conn_task, conn,
142706db247cSraghuram 	    DDI_NOSLEEP) != DDI_SUCCESS) {
142806db247cSraghuram 		cmn_err(CE_WARN, "!vsw%d: Can't dispatch connection task",
142906db247cSraghuram 		    vswp->instance);
143006db247cSraghuram 
143106db247cSraghuram 		kmem_free(conn, sizeof (vsw_conn_evt_t));
143206db247cSraghuram 		goto err_exit;
143306db247cSraghuram 	}
143406db247cSraghuram 
143506db247cSraghuram 	D1(vswp, "%s: exit", __func__);
143606db247cSraghuram 	return;
143706db247cSraghuram 
143806db247cSraghuram err_exit:
143906db247cSraghuram 	/*
144006db247cSraghuram 	 * Have mostly likely failed due to memory shortage. Clear the flag so
144106db247cSraghuram 	 * that future requests will at least be attempted and will hopefully
144206db247cSraghuram 	 * succeed.
144306db247cSraghuram 	 */
144406db247cSraghuram 	if ((evt == VSW_CONN_RESET) || (evt == VSW_CONN_RESTART))
144506db247cSraghuram 		ldcp->reset_active = 0;
144606db247cSraghuram }
144706db247cSraghuram 
144806db247cSraghuram /*
144906db247cSraghuram  * Deal with events relating to a connection. Invoked from a taskq.
145006db247cSraghuram  */
145106db247cSraghuram static void
vsw_conn_task(void * arg)145206db247cSraghuram vsw_conn_task(void *arg)
145306db247cSraghuram {
145406db247cSraghuram 	vsw_conn_evt_t	*conn = (vsw_conn_evt_t *)arg;
145506db247cSraghuram 	vsw_ldc_t	*ldcp = NULL;
1456678453a8Sspeer 	vsw_port_t	*portp;
145706db247cSraghuram 	vsw_t		*vswp = NULL;
145806db247cSraghuram 	uint16_t	evt;
145906db247cSraghuram 	ldc_status_t	curr_status;
146006db247cSraghuram 
146106db247cSraghuram 	ldcp = conn->ldcp;
146206db247cSraghuram 	evt = conn->evt;
146306db247cSraghuram 	vswp = ldcp->ldc_vswp;
1464678453a8Sspeer 	portp = ldcp->ldc_port;
146506db247cSraghuram 
146606db247cSraghuram 	D1(vswp, "%s: enter", __func__);
146706db247cSraghuram 
146806db247cSraghuram 	/* can safely free now have copied out data */
146906db247cSraghuram 	kmem_free(conn, sizeof (vsw_conn_evt_t));
147006db247cSraghuram 
14717bd3a2e2SSriharsha Basavapatna 	if (ldcp->rcv_thread != NULL) {
14727bd3a2e2SSriharsha Basavapatna 		vsw_stop_rcv_thread(ldcp);
14737bd3a2e2SSriharsha Basavapatna 	} else if (ldcp->msg_thread != NULL) {
14747bd3a2e2SSriharsha Basavapatna 		vsw_stop_msg_thread(ldcp);
14757bd3a2e2SSriharsha Basavapatna 	}
14767bd3a2e2SSriharsha Basavapatna 
147706db247cSraghuram 	mutex_enter(&ldcp->status_lock);
147806db247cSraghuram 	if (ldc_status(ldcp->ldc_handle, &curr_status) != 0) {
147906db247cSraghuram 		cmn_err(CE_WARN, "!vsw%d: Unable to read status of "
148006db247cSraghuram 		    "channel %ld", vswp->instance, ldcp->ldc_id);
148106db247cSraghuram 		mutex_exit(&ldcp->status_lock);
148206db247cSraghuram 		return;
148306db247cSraghuram 	}
148406db247cSraghuram 
148506db247cSraghuram 	/*
148606db247cSraghuram 	 * If we wish to restart the handshake on this channel, then if
148706db247cSraghuram 	 * the channel is UP we bring it DOWN to flush the underlying
148806db247cSraghuram 	 * ldc queue.
148906db247cSraghuram 	 */
149006db247cSraghuram 	if ((evt == VSW_CONN_RESTART) && (curr_status == LDC_UP))
149106db247cSraghuram 		(void) ldc_down(ldcp->ldc_handle);
149206db247cSraghuram 
1493cdfc78adSraghuram 	if ((portp->p_hio_capable) && (portp->p_hio_enabled)) {
1494678453a8Sspeer 		vsw_hio_stop(vswp, ldcp);
1495678453a8Sspeer 	}
1496678453a8Sspeer 
149706db247cSraghuram 	/*
149806db247cSraghuram 	 * re-init all the associated data structures.
149906db247cSraghuram 	 */
150006db247cSraghuram 	vsw_ldc_reinit(ldcp);
150106db247cSraghuram 
150206db247cSraghuram 	/*
150306db247cSraghuram 	 * Bring the channel back up (note it does no harm to
150406db247cSraghuram 	 * do this even if the channel is already UP, Just
150506db247cSraghuram 	 * becomes effectively a no-op).
150606db247cSraghuram 	 */
150706db247cSraghuram 	(void) ldc_up(ldcp->ldc_handle);
150806db247cSraghuram 
150906db247cSraghuram 	/*
151006db247cSraghuram 	 * Check if channel is now UP. This will only happen if
151106db247cSraghuram 	 * peer has also done a ldc_up().
151206db247cSraghuram 	 */
151306db247cSraghuram 	if (ldc_status(ldcp->ldc_handle, &curr_status) != 0) {
151406db247cSraghuram 		cmn_err(CE_WARN, "!vsw%d: Unable to read status of "
151506db247cSraghuram 		    "channel %ld", vswp->instance, ldcp->ldc_id);
151606db247cSraghuram 		mutex_exit(&ldcp->status_lock);
151706db247cSraghuram 		return;
151806db247cSraghuram 	}
151906db247cSraghuram 
152006db247cSraghuram 	ldcp->ldc_status = curr_status;
152106db247cSraghuram 
152206db247cSraghuram 	/* channel UP so restart handshake by sending version info */
152306db247cSraghuram 	if (curr_status == LDC_UP) {
152406db247cSraghuram 		if (ldcp->hcnt++ > vsw_num_handshakes) {
152506db247cSraghuram 			cmn_err(CE_WARN, "!vsw%d: exceeded number of permitted"
152606db247cSraghuram 			    " handshake attempts (%d) on channel %ld",
152706db247cSraghuram 			    vswp->instance, ldcp->hcnt, ldcp->ldc_id);
152806db247cSraghuram 			mutex_exit(&ldcp->status_lock);
152906db247cSraghuram 			return;
153006db247cSraghuram 		}
153106db247cSraghuram 
1532f0ca1d9aSsb 		if (vsw_obp_ver_proto_workaround == B_FALSE &&
1533f0ca1d9aSsb 		    (ddi_taskq_dispatch(vswp->taskq_p, vsw_send_ver, ldcp,
1534f0ca1d9aSsb 		    DDI_NOSLEEP) != DDI_SUCCESS)) {
153506db247cSraghuram 			cmn_err(CE_WARN, "!vsw%d: Can't dispatch version task",
153606db247cSraghuram 			    vswp->instance);
153706db247cSraghuram 
153806db247cSraghuram 			/*
153906db247cSraghuram 			 * Don't count as valid restart attempt if couldn't
154006db247cSraghuram 			 * send version msg.
154106db247cSraghuram 			 */
154206db247cSraghuram 			if (ldcp->hcnt > 0)
154306db247cSraghuram 				ldcp->hcnt--;
154406db247cSraghuram 		}
154506db247cSraghuram 	}
154606db247cSraghuram 
154706db247cSraghuram 	/*
154806db247cSraghuram 	 * Mark that the process is complete by clearing the flag.
154906db247cSraghuram 	 *
155006db247cSraghuram 	 * Note is it possible that the taskq dispatch above may have failed,
155106db247cSraghuram 	 * most likely due to memory shortage. We still clear the flag so
155206db247cSraghuram 	 * future attempts will at least be attempted and will hopefully
155306db247cSraghuram 	 * succeed.
155406db247cSraghuram 	 */
155506db247cSraghuram 	if ((evt == VSW_CONN_RESET) || (evt == VSW_CONN_RESTART))
155606db247cSraghuram 		ldcp->reset_active = 0;
155706db247cSraghuram 
155806db247cSraghuram 	mutex_exit(&ldcp->status_lock);
155906db247cSraghuram 
156006db247cSraghuram 	D1(vswp, "%s: exit", __func__);
156106db247cSraghuram }
156206db247cSraghuram 
156306db247cSraghuram /*
156406db247cSraghuram  * returns 0 if legal for event signified by flag to have
156506db247cSraghuram  * occured at the time it did. Otherwise returns 1.
156606db247cSraghuram  */
156706db247cSraghuram int
vsw_check_flag(vsw_ldc_t * ldcp,int dir,uint64_t flag)156806db247cSraghuram vsw_check_flag(vsw_ldc_t *ldcp, int dir, uint64_t flag)
156906db247cSraghuram {
157006db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
157106db247cSraghuram 	uint64_t	state;
157206db247cSraghuram 	uint64_t	phase;
157306db247cSraghuram 
157406db247cSraghuram 	if (dir == INBOUND)
157506db247cSraghuram 		state = ldcp->lane_in.lstate;
157606db247cSraghuram 	else
157706db247cSraghuram 		state = ldcp->lane_out.lstate;
157806db247cSraghuram 
157906db247cSraghuram 	phase = ldcp->hphase;
158006db247cSraghuram 
158106db247cSraghuram 	switch (flag) {
158206db247cSraghuram 	case VSW_VER_INFO_RECV:
158306db247cSraghuram 		if (phase > VSW_MILESTONE0) {
158406db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): VER_INFO_RECV"
158506db247cSraghuram 			    " when in state %d\n", ldcp->ldc_id, phase);
158606db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
158706db247cSraghuram 			return (1);
158806db247cSraghuram 		}
158906db247cSraghuram 		break;
159006db247cSraghuram 
159106db247cSraghuram 	case VSW_VER_ACK_RECV:
159206db247cSraghuram 	case VSW_VER_NACK_RECV:
159306db247cSraghuram 		if (!(state & VSW_VER_INFO_SENT)) {
159406db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): spurious VER_ACK or "
159506db247cSraghuram 			    "VER_NACK when in state %d\n", ldcp->ldc_id, phase);
159606db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
159706db247cSraghuram 			return (1);
159806db247cSraghuram 		} else
159906db247cSraghuram 			state &= ~VSW_VER_INFO_SENT;
160006db247cSraghuram 		break;
160106db247cSraghuram 
160206db247cSraghuram 	case VSW_ATTR_INFO_RECV:
160306db247cSraghuram 		if ((phase < VSW_MILESTONE1) || (phase >= VSW_MILESTONE2)) {
160406db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): ATTR_INFO_RECV"
160506db247cSraghuram 			    " when in state %d\n", ldcp->ldc_id, phase);
160606db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
160706db247cSraghuram 			return (1);
160806db247cSraghuram 		}
160906db247cSraghuram 		break;
161006db247cSraghuram 
161106db247cSraghuram 	case VSW_ATTR_ACK_RECV:
161206db247cSraghuram 	case VSW_ATTR_NACK_RECV:
161306db247cSraghuram 		if (!(state & VSW_ATTR_INFO_SENT)) {
161406db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): spurious ATTR_ACK"
161506db247cSraghuram 			    " or ATTR_NACK when in state %d\n",
161606db247cSraghuram 			    ldcp->ldc_id, phase);
161706db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
161806db247cSraghuram 			return (1);
161906db247cSraghuram 		} else
162006db247cSraghuram 			state &= ~VSW_ATTR_INFO_SENT;
162106db247cSraghuram 		break;
162206db247cSraghuram 
162306db247cSraghuram 	case VSW_DRING_INFO_RECV:
162406db247cSraghuram 		if (phase < VSW_MILESTONE1) {
162506db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): DRING_INFO_RECV"
162606db247cSraghuram 			    " when in state %d\n", ldcp->ldc_id, phase);
162706db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
162806db247cSraghuram 			return (1);
162906db247cSraghuram 		}
163006db247cSraghuram 		break;
163106db247cSraghuram 
163206db247cSraghuram 	case VSW_DRING_ACK_RECV:
163306db247cSraghuram 	case VSW_DRING_NACK_RECV:
163406db247cSraghuram 		if (!(state & VSW_DRING_INFO_SENT)) {
163506db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): spurious DRING_ACK "
163606db247cSraghuram 			    " or DRING_NACK when in state %d\n",
163706db247cSraghuram 			    ldcp->ldc_id, phase);
163806db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
163906db247cSraghuram 			return (1);
164006db247cSraghuram 		} else
164106db247cSraghuram 			state &= ~VSW_DRING_INFO_SENT;
164206db247cSraghuram 		break;
164306db247cSraghuram 
164406db247cSraghuram 	case VSW_RDX_INFO_RECV:
164506db247cSraghuram 		if (phase < VSW_MILESTONE3) {
164606db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): RDX_INFO_RECV"
164706db247cSraghuram 			    " when in state %d\n", ldcp->ldc_id, phase);
164806db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
164906db247cSraghuram 			return (1);
165006db247cSraghuram 		}
165106db247cSraghuram 		break;
165206db247cSraghuram 
165306db247cSraghuram 	case VSW_RDX_ACK_RECV:
165406db247cSraghuram 	case VSW_RDX_NACK_RECV:
165506db247cSraghuram 		if (!(state & VSW_RDX_INFO_SENT)) {
165606db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): spurious RDX_ACK or "
165706db247cSraghuram 			    "RDX_NACK when in state %d\n", ldcp->ldc_id, phase);
165806db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
165906db247cSraghuram 			return (1);
166006db247cSraghuram 		} else
166106db247cSraghuram 			state &= ~VSW_RDX_INFO_SENT;
166206db247cSraghuram 		break;
166306db247cSraghuram 
166406db247cSraghuram 	case VSW_MCST_INFO_RECV:
166506db247cSraghuram 		if (phase < VSW_MILESTONE3) {
166606db247cSraghuram 			DERR(vswp, "vsw_check_flag (%d): VSW_MCST_INFO_RECV"
166706db247cSraghuram 			    " when in state %d\n", ldcp->ldc_id, phase);
166806db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
166906db247cSraghuram 			return (1);
167006db247cSraghuram 		}
167106db247cSraghuram 		break;
167206db247cSraghuram 
167306db247cSraghuram 	default:
167406db247cSraghuram 		DERR(vswp, "vsw_check_flag (%lld): unknown flag (%llx)",
167506db247cSraghuram 		    ldcp->ldc_id, flag);
167606db247cSraghuram 		return (1);
167706db247cSraghuram 	}
167806db247cSraghuram 
167906db247cSraghuram 	if (dir == INBOUND)
168006db247cSraghuram 		ldcp->lane_in.lstate = state;
168106db247cSraghuram 	else
168206db247cSraghuram 		ldcp->lane_out.lstate = state;
168306db247cSraghuram 
168406db247cSraghuram 	D1(vswp, "vsw_check_flag (chan %lld): exit", ldcp->ldc_id);
168506db247cSraghuram 
168606db247cSraghuram 	return (0);
168706db247cSraghuram }
168806db247cSraghuram 
168906db247cSraghuram void
vsw_next_milestone(vsw_ldc_t * ldcp)169006db247cSraghuram vsw_next_milestone(vsw_ldc_t *ldcp)
169106db247cSraghuram {
169206db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
1693678453a8Sspeer 	vsw_port_t	*portp = ldcp->ldc_port;
16947bd3a2e2SSriharsha Basavapatna 	lane_t		*lane_out = &ldcp->lane_out;
16957bd3a2e2SSriharsha Basavapatna 	lane_t		*lane_in = &ldcp->lane_in;
169606db247cSraghuram 
169706db247cSraghuram 	D1(vswp, "%s (chan %lld): enter (phase %ld)", __func__,
169806db247cSraghuram 	    ldcp->ldc_id, ldcp->hphase);
169906db247cSraghuram 
17007bd3a2e2SSriharsha Basavapatna 	DUMP_FLAGS(lane_in->lstate);
17017bd3a2e2SSriharsha Basavapatna 	DUMP_FLAGS(lane_out->lstate);
170206db247cSraghuram 
170306db247cSraghuram 	switch (ldcp->hphase) {
170406db247cSraghuram 
170506db247cSraghuram 	case VSW_MILESTONE0:
170606db247cSraghuram 		/*
170706db247cSraghuram 		 * If we haven't started to handshake with our peer,
170806db247cSraghuram 		 * start to do so now.
170906db247cSraghuram 		 */
17107bd3a2e2SSriharsha Basavapatna 		if (lane_out->lstate == 0) {
171106db247cSraghuram 			D2(vswp, "%s: (chan %lld) starting handshake "
171206db247cSraghuram 			    "with peer", __func__, ldcp->ldc_id);
171306db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_UP);
171406db247cSraghuram 		}
171506db247cSraghuram 
171606db247cSraghuram 		/*
171706db247cSraghuram 		 * Only way to pass this milestone is to have successfully
171806db247cSraghuram 		 * negotiated version info.
171906db247cSraghuram 		 */
17207bd3a2e2SSriharsha Basavapatna 		if ((lane_in->lstate & VSW_VER_ACK_SENT) &&
17217bd3a2e2SSriharsha Basavapatna 		    (lane_out->lstate & VSW_VER_ACK_RECV)) {
172206db247cSraghuram 
172306db247cSraghuram 			D2(vswp, "%s: (chan %lld) leaving milestone 0",
172406db247cSraghuram 			    __func__, ldcp->ldc_id);
172506db247cSraghuram 
1726f0ca1d9aSsb 			vsw_set_vnet_proto_ops(ldcp);
1727f0ca1d9aSsb 
172806db247cSraghuram 			/*
172906db247cSraghuram 			 * Next milestone is passed when attribute
173006db247cSraghuram 			 * information has been successfully exchanged.
173106db247cSraghuram 			 */
173206db247cSraghuram 			ldcp->hphase = VSW_MILESTONE1;
173306db247cSraghuram 			vsw_send_attr(ldcp);
173406db247cSraghuram 
173506db247cSraghuram 		}
173606db247cSraghuram 		break;
173706db247cSraghuram 
173806db247cSraghuram 	case VSW_MILESTONE1:
173906db247cSraghuram 		/*
174006db247cSraghuram 		 * Only way to pass this milestone is to have successfully
17417bd3a2e2SSriharsha Basavapatna 		 * negotiated attribute information, in both directions.
174206db247cSraghuram 		 */
17437bd3a2e2SSriharsha Basavapatna 		if (!((lane_in->lstate & VSW_ATTR_ACK_SENT) &&
17447bd3a2e2SSriharsha Basavapatna 		    (lane_out->lstate & VSW_ATTR_ACK_RECV))) {
17457bd3a2e2SSriharsha Basavapatna 			break;
17467bd3a2e2SSriharsha Basavapatna 		}
174706db247cSraghuram 
17487bd3a2e2SSriharsha Basavapatna 		ldcp->hphase = VSW_MILESTONE2;
174906db247cSraghuram 
17507bd3a2e2SSriharsha Basavapatna 		/*
17517bd3a2e2SSriharsha Basavapatna 		 * If the peer device has said it wishes to
17527bd3a2e2SSriharsha Basavapatna 		 * use descriptor rings then we send it our ring
17537bd3a2e2SSriharsha Basavapatna 		 * info, otherwise we just set up a private ring
17547bd3a2e2SSriharsha Basavapatna 		 * which we use an internal buffer
17557bd3a2e2SSriharsha Basavapatna 		 */
17567bd3a2e2SSriharsha Basavapatna 		if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
17577bd3a2e2SSriharsha Basavapatna 		    (lane_in->xfer_mode & VIO_DRING_MODE_V1_2)) ||
17587bd3a2e2SSriharsha Basavapatna 		    (VSW_VER_LT(ldcp, 1, 2) &&
17597bd3a2e2SSriharsha Basavapatna 		    (lane_in->xfer_mode == VIO_DRING_MODE_V1_0))) {
17607bd3a2e2SSriharsha Basavapatna 			vsw_send_dring_info(ldcp);
17617bd3a2e2SSriharsha Basavapatna 			break;
176206db247cSraghuram 		}
17637bd3a2e2SSriharsha Basavapatna 
17647bd3a2e2SSriharsha Basavapatna 		/*
17657bd3a2e2SSriharsha Basavapatna 		 * The peer doesn't operate in dring mode; we
17667bd3a2e2SSriharsha Basavapatna 		 * can simply fallthru to the RDX phase from
17677bd3a2e2SSriharsha Basavapatna 		 * here.
17687bd3a2e2SSriharsha Basavapatna 		 */
17697bd3a2e2SSriharsha Basavapatna 		/*FALLTHRU*/
177006db247cSraghuram 
177106db247cSraghuram 	case VSW_MILESTONE2:
177206db247cSraghuram 		/*
177306db247cSraghuram 		 * If peer has indicated in its attribute message that
177406db247cSraghuram 		 * it wishes to use descriptor rings then the only way
177506db247cSraghuram 		 * to pass this milestone is for us to have received
177606db247cSraghuram 		 * valid dring info.
177706db247cSraghuram 		 *
177806db247cSraghuram 		 * If peer is not using descriptor rings then just fall
177906db247cSraghuram 		 * through.
178006db247cSraghuram 		 */
1781c1c61f44Ssb 		if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
17827bd3a2e2SSriharsha Basavapatna 		    (lane_in->xfer_mode & VIO_DRING_MODE_V1_2)) ||
1783f0ca1d9aSsb 		    (VSW_VER_LT(ldcp, 1, 2) &&
17847bd3a2e2SSriharsha Basavapatna 		    (lane_in->xfer_mode ==
1785f0ca1d9aSsb 		    VIO_DRING_MODE_V1_0))) {
17867bd3a2e2SSriharsha Basavapatna 			if (!(lane_in->lstate & VSW_DRING_ACK_SENT))
1787f0ca1d9aSsb 				break;
1788f0ca1d9aSsb 		}
178906db247cSraghuram 
179006db247cSraghuram 		D2(vswp, "%s: (chan %lld) leaving milestone 2",
179106db247cSraghuram 		    __func__, ldcp->ldc_id);
179206db247cSraghuram 
179306db247cSraghuram 		ldcp->hphase = VSW_MILESTONE3;
179406db247cSraghuram 		vsw_send_rdx(ldcp);
179506db247cSraghuram 		break;
179606db247cSraghuram 
179706db247cSraghuram 	case VSW_MILESTONE3:
179806db247cSraghuram 		/*
179906db247cSraghuram 		 * Pass this milestone when all paramaters have been
180006db247cSraghuram 		 * successfully exchanged and RDX sent in both directions.
180106db247cSraghuram 		 *
18027bd3a2e2SSriharsha Basavapatna 		 * Mark the relevant lane as available to transmit data. In
18037bd3a2e2SSriharsha Basavapatna 		 * RxDringData mode, lane_in is associated with transmit and
18047bd3a2e2SSriharsha Basavapatna 		 * lane_out is associated with receive. It is the reverse in
18057bd3a2e2SSriharsha Basavapatna 		 * TxDring mode.
180606db247cSraghuram 		 */
18077bd3a2e2SSriharsha Basavapatna 		if ((lane_out->lstate & VSW_RDX_ACK_SENT) &&
18087bd3a2e2SSriharsha Basavapatna 		    (lane_in->lstate & VSW_RDX_ACK_RECV)) {
180906db247cSraghuram 
181006db247cSraghuram 			D2(vswp, "%s: (chan %lld) leaving milestone 3",
181106db247cSraghuram 			    __func__, ldcp->ldc_id);
181206db247cSraghuram 			D2(vswp, "%s: ** handshake complete (0x%llx : "
18137bd3a2e2SSriharsha Basavapatna 			    "0x%llx) **", __func__, lane_in->lstate,
18147bd3a2e2SSriharsha Basavapatna 			    lane_out->lstate);
18157bd3a2e2SSriharsha Basavapatna 			if (lane_out->dring_mode == VIO_RX_DRING_DATA) {
18167bd3a2e2SSriharsha Basavapatna 				lane_in->lstate |= VSW_LANE_ACTIVE;
18177bd3a2e2SSriharsha Basavapatna 			} else {
18187bd3a2e2SSriharsha Basavapatna 				lane_out->lstate |= VSW_LANE_ACTIVE;
18197bd3a2e2SSriharsha Basavapatna 			}
182006db247cSraghuram 			ldcp->hphase = VSW_MILESTONE4;
182106db247cSraghuram 			ldcp->hcnt = 0;
182206db247cSraghuram 			DISPLAY_STATE();
1823678453a8Sspeer 			/* Start HIO if enabled and capable */
1824678453a8Sspeer 			if ((portp->p_hio_enabled) && (portp->p_hio_capable)) {
1825678453a8Sspeer 				D2(vswp, "%s: start HybridIO setup", __func__);
1826678453a8Sspeer 				vsw_hio_start(vswp, ldcp);
1827678453a8Sspeer 			}
18281107ea93SSriharsha Basavapatna 
18291107ea93SSriharsha Basavapatna 			if (ldcp->pls_negotiated == B_TRUE) {
18301107ea93SSriharsha Basavapatna 				/*
18311107ea93SSriharsha Basavapatna 				 * The vnet device has negotiated to get phys
18321107ea93SSriharsha Basavapatna 				 * link updates. Now that the handshake with
18331107ea93SSriharsha Basavapatna 				 * the vnet device is complete, send an initial
18341107ea93SSriharsha Basavapatna 				 * update with the current physical link state.
18351107ea93SSriharsha Basavapatna 				 */
18361107ea93SSriharsha Basavapatna 				vsw_send_physlink_msg(ldcp,
18371107ea93SSriharsha Basavapatna 				    vswp->phys_link_state);
18381107ea93SSriharsha Basavapatna 			}
18391107ea93SSriharsha Basavapatna 
184006db247cSraghuram 		} else {
184106db247cSraghuram 			D2(vswp, "%s: still in milestone 3 (0x%llx : 0x%llx)",
18427bd3a2e2SSriharsha Basavapatna 			    __func__, lane_in->lstate,
18437bd3a2e2SSriharsha Basavapatna 			    lane_out->lstate);
184406db247cSraghuram 		}
184506db247cSraghuram 		break;
184606db247cSraghuram 
184706db247cSraghuram 	case VSW_MILESTONE4:
184806db247cSraghuram 		D2(vswp, "%s: (chan %lld) in milestone 4", __func__,
184906db247cSraghuram 		    ldcp->ldc_id);
185006db247cSraghuram 		break;
185106db247cSraghuram 
185206db247cSraghuram 	default:
185306db247cSraghuram 		DERR(vswp, "%s: (chan %lld) Unknown Phase %x", __func__,
185406db247cSraghuram 		    ldcp->ldc_id, ldcp->hphase);
185506db247cSraghuram 	}
185606db247cSraghuram 
185706db247cSraghuram 	D1(vswp, "%s (chan %lld): exit (phase %ld)", __func__, ldcp->ldc_id,
185806db247cSraghuram 	    ldcp->hphase);
185906db247cSraghuram }
186006db247cSraghuram 
186106db247cSraghuram /*
186206db247cSraghuram  * Check if major version is supported.
186306db247cSraghuram  *
186406db247cSraghuram  * Returns 0 if finds supported major number, and if necessary
186506db247cSraghuram  * adjusts the minor field.
186606db247cSraghuram  *
186706db247cSraghuram  * Returns 1 if can't match major number exactly. Sets mjor/minor
186806db247cSraghuram  * to next lowest support values, or to zero if no other values possible.
186906db247cSraghuram  */
187006db247cSraghuram static int
vsw_supported_version(vio_ver_msg_t * vp)187106db247cSraghuram vsw_supported_version(vio_ver_msg_t *vp)
187206db247cSraghuram {
187306db247cSraghuram 	int	i;
187406db247cSraghuram 
187506db247cSraghuram 	D1(NULL, "vsw_supported_version: enter");
187606db247cSraghuram 
187706db247cSraghuram 	for (i = 0; i < VSW_NUM_VER; i++) {
187806db247cSraghuram 		if (vsw_versions[i].ver_major == vp->ver_major) {
187906db247cSraghuram 			/*
188006db247cSraghuram 			 * Matching or lower major version found. Update
188106db247cSraghuram 			 * minor number if necessary.
188206db247cSraghuram 			 */
188306db247cSraghuram 			if (vp->ver_minor > vsw_versions[i].ver_minor) {
188406db247cSraghuram 				D2(NULL, "%s: adjusting minor value from %d "
188506db247cSraghuram 				    "to %d", __func__, vp->ver_minor,
188606db247cSraghuram 				    vsw_versions[i].ver_minor);
188706db247cSraghuram 				vp->ver_minor = vsw_versions[i].ver_minor;
188806db247cSraghuram 			}
188906db247cSraghuram 
189006db247cSraghuram 			return (0);
189106db247cSraghuram 		}
189206db247cSraghuram 
1893f0ca1d9aSsb 		/*
1894f0ca1d9aSsb 		 * If the message contains a higher major version number, set
1895f0ca1d9aSsb 		 * the message's major/minor versions to the current values
1896f0ca1d9aSsb 		 * and return false, so this message will get resent with
1897f0ca1d9aSsb 		 * these values.
1898f0ca1d9aSsb 		 */
189906db247cSraghuram 		if (vsw_versions[i].ver_major < vp->ver_major) {
1900f0ca1d9aSsb 			D2(NULL, "%s: adjusting major and minor "
1901f0ca1d9aSsb 			    "values to %d, %d\n",
1902f0ca1d9aSsb 			    __func__, vsw_versions[i].ver_major,
1903f0ca1d9aSsb 			    vsw_versions[i].ver_minor);
1904f0ca1d9aSsb 			vp->ver_major = vsw_versions[i].ver_major;
1905f0ca1d9aSsb 			vp->ver_minor = vsw_versions[i].ver_minor;
190606db247cSraghuram 			return (1);
190706db247cSraghuram 		}
190806db247cSraghuram 	}
190906db247cSraghuram 
191006db247cSraghuram 	/* No match was possible, zero out fields */
191106db247cSraghuram 	vp->ver_major = 0;
191206db247cSraghuram 	vp->ver_minor = 0;
191306db247cSraghuram 
191406db247cSraghuram 	D1(NULL, "vsw_supported_version: exit");
191506db247cSraghuram 
191606db247cSraghuram 	return (1);
191706db247cSraghuram }
191806db247cSraghuram 
1919f0ca1d9aSsb /*
1920f0ca1d9aSsb  * Set vnet-protocol-version dependent functions based on version.
1921f0ca1d9aSsb  */
1922f0ca1d9aSsb static void
vsw_set_vnet_proto_ops(vsw_ldc_t * ldcp)1923f0ca1d9aSsb vsw_set_vnet_proto_ops(vsw_ldc_t *ldcp)
1924f0ca1d9aSsb {
1925f0ca1d9aSsb 	vsw_t	*vswp = ldcp->ldc_vswp;
1926f0ca1d9aSsb 	lane_t	*lp = &ldcp->lane_out;
1927f0ca1d9aSsb 
19287bd3a2e2SSriharsha Basavapatna 	/*
19297bd3a2e2SSriharsha Basavapatna 	 * Setup the appropriate dring data processing routine and any
19307bd3a2e2SSriharsha Basavapatna 	 * associated thread based on the version.
19317bd3a2e2SSriharsha Basavapatna 	 *
19327bd3a2e2SSriharsha Basavapatna 	 * In versions < 1.6, we support only TxDring mode. In this mode, the
19337bd3a2e2SSriharsha Basavapatna 	 * msg worker thread processes all types of VIO msgs (ctrl and data).
19347bd3a2e2SSriharsha Basavapatna 	 *
19357bd3a2e2SSriharsha Basavapatna 	 * In versions >= 1.6, we also support RxDringData mode. In this mode,
19367bd3a2e2SSriharsha Basavapatna 	 * the rcv worker thread processes dring data messages (msgtype:
19377bd3a2e2SSriharsha Basavapatna 	 * VIO_TYPE_DATA, subtype: VIO_SUBTYPE_INFO, env: VIO_DRING_DATA). The
19387bd3a2e2SSriharsha Basavapatna 	 * rest of the data messages (including acks) and ctrl messages are
19397bd3a2e2SSriharsha Basavapatna 	 * handled directly by the callback (intr) thread.
19407bd3a2e2SSriharsha Basavapatna 	 *
19417bd3a2e2SSriharsha Basavapatna 	 * However, for versions >= 1.6, we could still fallback to TxDring
19427bd3a2e2SSriharsha Basavapatna 	 * mode. This could happen if RxDringData mode has been disabled (see
194334f94fbcSWENTAO YANG 	 * below) on this guest or on the peer guest. This info is determined
194434f94fbcSWENTAO YANG 	 * as part of attr exchange phase of handshake. Hence, we setup these
194534f94fbcSWENTAO YANG 	 * pointers for v1.6 after attr msg phase completes during handshake.
19467bd3a2e2SSriharsha Basavapatna 	 */
19477bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 6)) {
19487bd3a2e2SSriharsha Basavapatna 		/*
19497bd3a2e2SSriharsha Basavapatna 		 * Set data dring mode for vsw_send_attr(). We setup msg worker
19507bd3a2e2SSriharsha Basavapatna 		 * thread in TxDring mode or rcv worker thread in RxDringData
19517bd3a2e2SSriharsha Basavapatna 		 * mode when attr phase of handshake completes.
19527bd3a2e2SSriharsha Basavapatna 		 */
195334f94fbcSWENTAO YANG 		if (vsw_mapin_avail(ldcp) == B_TRUE) {
19547bd3a2e2SSriharsha Basavapatna 			lp->dring_mode = (VIO_RX_DRING_DATA | VIO_TX_DRING);
19557bd3a2e2SSriharsha Basavapatna 		} else {
19567bd3a2e2SSriharsha Basavapatna 			lp->dring_mode = VIO_TX_DRING;
19577bd3a2e2SSriharsha Basavapatna 		}
19587bd3a2e2SSriharsha Basavapatna 	} else {
19597bd3a2e2SSriharsha Basavapatna 		lp->dring_mode = VIO_TX_DRING;
19607bd3a2e2SSriharsha Basavapatna 	}
19617bd3a2e2SSriharsha Basavapatna 
19627bd3a2e2SSriharsha Basavapatna 	/*
19637bd3a2e2SSriharsha Basavapatna 	 * Setup the MTU for attribute negotiation based on the version.
19647bd3a2e2SSriharsha Basavapatna 	 */
19657b1f684aSSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 4)) {
1966c1c61f44Ssb 		/*
19677b1f684aSSriharsha Basavapatna 		 * If the version negotiated with peer is >= 1.4(Jumbo Frame
19687b1f684aSSriharsha Basavapatna 		 * Support), set the mtu in our attributes to max_frame_size.
1969c1c61f44Ssb 		 */
1970c1c61f44Ssb 		lp->mtu = vswp->max_frame_size;
19717b1f684aSSriharsha Basavapatna 	} else if (VSW_VER_EQ(ldcp, 1, 3)) {
19727b1f684aSSriharsha Basavapatna 		/*
19737b1f684aSSriharsha Basavapatna 		 * If the version negotiated with peer is == 1.3 (Vlan Tag
19747b1f684aSSriharsha Basavapatna 		 * Support) set the attr.mtu to ETHERMAX + VLAN_TAGSZ.
19757b1f684aSSriharsha Basavapatna 		 */
19767b1f684aSSriharsha Basavapatna 		lp->mtu = ETHERMAX + VLAN_TAGSZ;
1977c1c61f44Ssb 	} else {
1978c1c61f44Ssb 		vsw_port_t	*portp = ldcp->ldc_port;
1979c1c61f44Ssb 		/*
1980c1c61f44Ssb 		 * Pre-1.3 peers expect max frame size of ETHERMAX.
19817b1f684aSSriharsha Basavapatna 		 * We can negotiate that size with those peers provided only
19827b1f684aSSriharsha Basavapatna 		 * pvid is defined for our peer and there are no vids. Then we
19837b1f684aSSriharsha Basavapatna 		 * can send/recv only untagged frames of max size ETHERMAX.
19847b1f684aSSriharsha Basavapatna 		 * Note that pvid of the peer can be different, as vsw has to
19857b1f684aSSriharsha Basavapatna 		 * serve the vnet in that vlan even if itself is not assigned
19867b1f684aSSriharsha Basavapatna 		 * to that vlan.
1987c1c61f44Ssb 		 */
19887b1f684aSSriharsha Basavapatna 		if (portp->nvids == 0) {
1989c1c61f44Ssb 			lp->mtu = ETHERMAX;
1990c1c61f44Ssb 		}
1991c1c61f44Ssb 	}
1992c1c61f44Ssb 
19937bd3a2e2SSriharsha Basavapatna 	/*
19947bd3a2e2SSriharsha Basavapatna 	 * Setup version dependent data processing functions.
19957bd3a2e2SSriharsha Basavapatna 	 */
1996c1c61f44Ssb 	if (VSW_VER_GTEQ(ldcp, 1, 2)) {
1997c1c61f44Ssb 		/* Versions >= 1.2 */
1998f0ca1d9aSsb 
1999f0ca1d9aSsb 		if (VSW_PRI_ETH_DEFINED(vswp)) {
2000f0ca1d9aSsb 			/*
2001f0ca1d9aSsb 			 * enable priority routines and pkt mode only if
2002f0ca1d9aSsb 			 * at least one pri-eth-type is specified in MD.
2003f0ca1d9aSsb 			 */
2004f0ca1d9aSsb 			ldcp->tx = vsw_ldctx_pri;
2005f0ca1d9aSsb 			ldcp->rx_pktdata = vsw_process_pkt_data;
2006f0ca1d9aSsb 
2007f0ca1d9aSsb 			/* set xfer mode for vsw_send_attr() */
2008f0ca1d9aSsb 			lp->xfer_mode = VIO_PKT_MODE | VIO_DRING_MODE_V1_2;
2009f0ca1d9aSsb 		} else {
2010f0ca1d9aSsb 			/* no priority eth types defined in MD */
2011f0ca1d9aSsb 
2012f0ca1d9aSsb 			ldcp->tx = vsw_ldctx;
2013f0ca1d9aSsb 			ldcp->rx_pktdata = vsw_process_pkt_data_nop;
2014f0ca1d9aSsb 
2015f0ca1d9aSsb 			/* set xfer mode for vsw_send_attr() */
2016f0ca1d9aSsb 			lp->xfer_mode = VIO_DRING_MODE_V1_2;
2017f0ca1d9aSsb 		}
2018c1c61f44Ssb 
2019f0ca1d9aSsb 	} else {
2020f0ca1d9aSsb 		/* Versions prior to 1.2  */
2021f0ca1d9aSsb 
2022f0ca1d9aSsb 		vsw_reset_vnet_proto_ops(ldcp);
2023f0ca1d9aSsb 	}
2024f0ca1d9aSsb }
2025f0ca1d9aSsb 
2026f0ca1d9aSsb /*
2027f0ca1d9aSsb  * Reset vnet-protocol-version dependent functions to v1.0.
2028f0ca1d9aSsb  */
2029f0ca1d9aSsb static void
vsw_reset_vnet_proto_ops(vsw_ldc_t * ldcp)2030f0ca1d9aSsb vsw_reset_vnet_proto_ops(vsw_ldc_t *ldcp)
2031f0ca1d9aSsb {
2032f0ca1d9aSsb 	lane_t	*lp = &ldcp->lane_out;
2033f0ca1d9aSsb 
2034f0ca1d9aSsb 	ldcp->tx = vsw_ldctx;
2035f0ca1d9aSsb 	ldcp->rx_pktdata = vsw_process_pkt_data_nop;
2036f0ca1d9aSsb 
2037f0ca1d9aSsb 	/* set xfer mode for vsw_send_attr() */
2038f0ca1d9aSsb 	lp->xfer_mode = VIO_DRING_MODE_V1_0;
2039f0ca1d9aSsb }
2040f0ca1d9aSsb 
20417bd3a2e2SSriharsha Basavapatna static void
vsw_process_evt_read(vsw_ldc_t * ldcp)20427bd3a2e2SSriharsha Basavapatna vsw_process_evt_read(vsw_ldc_t *ldcp)
20437bd3a2e2SSriharsha Basavapatna {
20447bd3a2e2SSriharsha Basavapatna 	if (ldcp->msg_thread != NULL) {
20457bd3a2e2SSriharsha Basavapatna 		/*
20467bd3a2e2SSriharsha Basavapatna 		 * TxDring mode; wakeup message worker
20477bd3a2e2SSriharsha Basavapatna 		 * thread to process the VIO messages.
20487bd3a2e2SSriharsha Basavapatna 		 */
20497bd3a2e2SSriharsha Basavapatna 		mutex_exit(&ldcp->ldc_cblock);
20507bd3a2e2SSriharsha Basavapatna 		mutex_enter(&ldcp->msg_thr_lock);
20517bd3a2e2SSriharsha Basavapatna 		if (!(ldcp->msg_thr_flags & VSW_WTHR_DATARCVD)) {
20527bd3a2e2SSriharsha Basavapatna 			ldcp->msg_thr_flags |= VSW_WTHR_DATARCVD;
20537bd3a2e2SSriharsha Basavapatna 			cv_signal(&ldcp->msg_thr_cv);
20547bd3a2e2SSriharsha Basavapatna 		}
20557bd3a2e2SSriharsha Basavapatna 		mutex_exit(&ldcp->msg_thr_lock);
20567bd3a2e2SSriharsha Basavapatna 		mutex_enter(&ldcp->ldc_cblock);
20577bd3a2e2SSriharsha Basavapatna 	} else {
20587bd3a2e2SSriharsha Basavapatna 		/*
20597bd3a2e2SSriharsha Basavapatna 		 * We invoke vsw_process_pkt() in the context of the LDC
20607bd3a2e2SSriharsha Basavapatna 		 * callback (vsw_ldc_cb()) during handshake, until the dring
20617bd3a2e2SSriharsha Basavapatna 		 * mode is negotiated. After the dring mode is negotiated, the
20627bd3a2e2SSriharsha Basavapatna 		 * msgs are processed by the msg worker thread (above case) if
20637bd3a2e2SSriharsha Basavapatna 		 * the dring mode is TxDring. Otherwise (in RxDringData mode)
20647bd3a2e2SSriharsha Basavapatna 		 * we continue to process the msgs directly in the callback
20657bd3a2e2SSriharsha Basavapatna 		 * context.
20667bd3a2e2SSriharsha Basavapatna 		 */
20677bd3a2e2SSriharsha Basavapatna 		vsw_process_pkt(ldcp);
20687bd3a2e2SSriharsha Basavapatna 	}
20697bd3a2e2SSriharsha Basavapatna }
20707bd3a2e2SSriharsha Basavapatna 
207106db247cSraghuram /*
207206db247cSraghuram  * Main routine for processing messages received over LDC.
207306db247cSraghuram  */
20747bd3a2e2SSriharsha Basavapatna void
vsw_process_pkt(void * arg)207506db247cSraghuram vsw_process_pkt(void *arg)
207606db247cSraghuram {
207706db247cSraghuram 	vsw_ldc_t	*ldcp = (vsw_ldc_t  *)arg;
2078*6e472272SToomas Soome 	vsw_t		*vswp = ldcp->ldc_vswp;
207906db247cSraghuram 	size_t		msglen;
2080f0ca1d9aSsb 	vio_msg_tag_t	*tagp;
2081f0ca1d9aSsb 	uint64_t	*ldcmsg;
2082*6e472272SToomas Soome 	int		rv = 0;
208306db247cSraghuram 
208406db247cSraghuram 
208506db247cSraghuram 	D1(vswp, "%s enter: ldcid (%lld)\n", __func__, ldcp->ldc_id);
208606db247cSraghuram 
208706db247cSraghuram 	ASSERT(MUTEX_HELD(&ldcp->ldc_cblock));
208806db247cSraghuram 
2089f0ca1d9aSsb 	ldcmsg = ldcp->ldcmsg;
209006db247cSraghuram 	/*
209106db247cSraghuram 	 * If channel is up read messages until channel is empty.
209206db247cSraghuram 	 */
209306db247cSraghuram 	do {
2094f0ca1d9aSsb 		msglen = ldcp->msglen;
2095f0ca1d9aSsb 		rv = ldc_read(ldcp->ldc_handle, (caddr_t)ldcmsg, &msglen);
209606db247cSraghuram 
209706db247cSraghuram 		if (rv != 0) {
209806db247cSraghuram 			DERR(vswp, "%s :ldc_read err id(%lld) rv(%d) len(%d)\n",
209906db247cSraghuram 			    __func__, ldcp->ldc_id, rv, msglen);
210006db247cSraghuram 		}
210106db247cSraghuram 
210206db247cSraghuram 		/* channel has been reset */
210306db247cSraghuram 		if (rv == ECONNRESET) {
210406db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESET);
210506db247cSraghuram 			break;
210606db247cSraghuram 		}
210706db247cSraghuram 
210806db247cSraghuram 		if (msglen == 0) {
210906db247cSraghuram 			D2(vswp, "%s: ldc_read id(%lld) NODATA", __func__,
211006db247cSraghuram 			    ldcp->ldc_id);
211106db247cSraghuram 			break;
211206db247cSraghuram 		}
211306db247cSraghuram 
211406db247cSraghuram 		D2(vswp, "%s: ldc_read id(%lld): msglen(%d)", __func__,
211506db247cSraghuram 		    ldcp->ldc_id, msglen);
211606db247cSraghuram 
211706db247cSraghuram 		/*
211806db247cSraghuram 		 * Figure out what sort of packet we have gotten by
211906db247cSraghuram 		 * examining the msg tag, and then switch it appropriately.
212006db247cSraghuram 		 */
2121f0ca1d9aSsb 		tagp = (vio_msg_tag_t *)ldcmsg;
212206db247cSraghuram 
2123f0ca1d9aSsb 		switch (tagp->vio_msgtype) {
212406db247cSraghuram 		case VIO_TYPE_CTRL:
21257bd3a2e2SSriharsha Basavapatna 			vsw_dispatch_ctrl_task(ldcp, ldcmsg, tagp, msglen);
212606db247cSraghuram 			break;
212706db247cSraghuram 		case VIO_TYPE_DATA:
2128f0ca1d9aSsb 			vsw_process_data_pkt(ldcp, ldcmsg, tagp, msglen);
212906db247cSraghuram 			break;
213006db247cSraghuram 		case VIO_TYPE_ERR:
2131f0ca1d9aSsb 			vsw_process_err_pkt(ldcp, ldcmsg, tagp);
213206db247cSraghuram 			break;
213306db247cSraghuram 		default:
213406db247cSraghuram 			DERR(vswp, "%s: Unknown tag(%lx) ", __func__,
2135f0ca1d9aSsb 			    "id(%lx)\n", tagp->vio_msgtype, ldcp->ldc_id);
213606db247cSraghuram 			break;
213706db247cSraghuram 		}
213806db247cSraghuram 	} while (msglen);
213906db247cSraghuram 
214006db247cSraghuram 	D1(vswp, "%s exit: ldcid (%lld)\n", __func__, ldcp->ldc_id);
214106db247cSraghuram }
214206db247cSraghuram 
214306db247cSraghuram /*
214406db247cSraghuram  * Dispatch a task to process a VIO control message.
214506db247cSraghuram  */
214606db247cSraghuram static void
vsw_dispatch_ctrl_task(vsw_ldc_t * ldcp,void * cpkt,vio_msg_tag_t * tagp,int msglen)21477bd3a2e2SSriharsha Basavapatna vsw_dispatch_ctrl_task(vsw_ldc_t *ldcp, void *cpkt, vio_msg_tag_t *tagp,
2148*6e472272SToomas Soome     int msglen)
214906db247cSraghuram {
215006db247cSraghuram 	vsw_ctrl_task_t		*ctaskp = NULL;
215106db247cSraghuram 	vsw_port_t		*port = ldcp->ldc_port;
215206db247cSraghuram 	vsw_t			*vswp = port->p_vswp;
215306db247cSraghuram 
215406db247cSraghuram 	D1(vswp, "%s: enter", __func__);
215506db247cSraghuram 
215606db247cSraghuram 	/*
215706db247cSraghuram 	 * We need to handle RDX ACK messages in-band as once they
215806db247cSraghuram 	 * are exchanged it is possible that we will get an
215906db247cSraghuram 	 * immediate (legitimate) data packet.
216006db247cSraghuram 	 */
2161f0ca1d9aSsb 	if ((tagp->vio_subtype_env == VIO_RDX) &&
2162f0ca1d9aSsb 	    (tagp->vio_subtype == VIO_SUBTYPE_ACK)) {
216306db247cSraghuram 
216406db247cSraghuram 		if (vsw_check_flag(ldcp, INBOUND, VSW_RDX_ACK_RECV))
216506db247cSraghuram 			return;
216606db247cSraghuram 
216706db247cSraghuram 		ldcp->lane_in.lstate |= VSW_RDX_ACK_RECV;
216806db247cSraghuram 		D2(vswp, "%s (%ld) handling RDX_ACK in place "
216906db247cSraghuram 		    "(ostate 0x%llx : hphase %d)", __func__,
217006db247cSraghuram 		    ldcp->ldc_id, ldcp->lane_in.lstate, ldcp->hphase);
217106db247cSraghuram 		vsw_next_milestone(ldcp);
217206db247cSraghuram 		return;
217306db247cSraghuram 	}
217406db247cSraghuram 
217506db247cSraghuram 	ctaskp = kmem_alloc(sizeof (vsw_ctrl_task_t), KM_NOSLEEP);
217606db247cSraghuram 
217706db247cSraghuram 	if (ctaskp == NULL) {
217806db247cSraghuram 		DERR(vswp, "%s: unable to alloc space for ctrl msg", __func__);
217906db247cSraghuram 		vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
218006db247cSraghuram 		return;
218106db247cSraghuram 	}
218206db247cSraghuram 
218306db247cSraghuram 	ctaskp->ldcp = ldcp;
21847bd3a2e2SSriharsha Basavapatna 	bcopy((def_msg_t *)cpkt, &ctaskp->pktp, msglen);
218506db247cSraghuram 	ctaskp->hss_id = ldcp->hss_id;
218606db247cSraghuram 
218706db247cSraghuram 	/*
218806db247cSraghuram 	 * Dispatch task to processing taskq if port is not in
218906db247cSraghuram 	 * the process of being detached.
219006db247cSraghuram 	 */
219106db247cSraghuram 	mutex_enter(&port->state_lock);
219206db247cSraghuram 	if (port->state == VSW_PORT_INIT) {
219306db247cSraghuram 		if ((vswp->taskq_p == NULL) ||
219406db247cSraghuram 		    (ddi_taskq_dispatch(vswp->taskq_p, vsw_process_ctrl_pkt,
219506db247cSraghuram 		    ctaskp, DDI_NOSLEEP) != DDI_SUCCESS)) {
2196a248ff60SWENTAO YANG 			mutex_exit(&port->state_lock);
219706db247cSraghuram 			DERR(vswp, "%s: unable to dispatch task to taskq",
219806db247cSraghuram 			    __func__);
219906db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
2200a248ff60SWENTAO YANG 			kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
220106db247cSraghuram 			return;
220206db247cSraghuram 		}
220306db247cSraghuram 	} else {
2204a248ff60SWENTAO YANG 		kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
220506db247cSraghuram 		DWARN(vswp, "%s: port %d detaching, not dispatching "
220606db247cSraghuram 		    "task", __func__, port->p_instance);
220706db247cSraghuram 	}
220806db247cSraghuram 
220906db247cSraghuram 	mutex_exit(&port->state_lock);
221006db247cSraghuram 
221106db247cSraghuram 	D2(vswp, "%s: dispatched task to taskq for chan %d", __func__,
221206db247cSraghuram 	    ldcp->ldc_id);
221306db247cSraghuram 	D1(vswp, "%s: exit", __func__);
221406db247cSraghuram }
221506db247cSraghuram 
221606db247cSraghuram /*
221706db247cSraghuram  * Process a VIO ctrl message. Invoked from taskq.
221806db247cSraghuram  */
221906db247cSraghuram static void
vsw_process_ctrl_pkt(void * arg)222006db247cSraghuram vsw_process_ctrl_pkt(void *arg)
222106db247cSraghuram {
222206db247cSraghuram 	vsw_ctrl_task_t	*ctaskp = (vsw_ctrl_task_t *)arg;
222306db247cSraghuram 	vsw_ldc_t	*ldcp = ctaskp->ldcp;
2224*6e472272SToomas Soome 	vsw_t		*vswp = ldcp->ldc_vswp;
222506db247cSraghuram 	vio_msg_tag_t	tag;
222606db247cSraghuram 	uint16_t	env;
222706db247cSraghuram 
222806db247cSraghuram 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
222906db247cSraghuram 
223006db247cSraghuram 	bcopy(&ctaskp->pktp, &tag, sizeof (vio_msg_tag_t));
223106db247cSraghuram 	env = tag.vio_subtype_env;
223206db247cSraghuram 
223306db247cSraghuram 	/* stale pkt check */
223406db247cSraghuram 	if (ctaskp->hss_id < ldcp->hss_id) {
223506db247cSraghuram 		DWARN(vswp, "%s: discarding stale packet belonging to earlier"
223606db247cSraghuram 		    " (%ld) handshake session", __func__, ctaskp->hss_id);
2237a248ff60SWENTAO YANG 		kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
223806db247cSraghuram 		return;
223906db247cSraghuram 	}
224006db247cSraghuram 
224106db247cSraghuram 	/* session id check */
224206db247cSraghuram 	if (ldcp->session_status & VSW_PEER_SESSION) {
224306db247cSraghuram 		if (ldcp->peer_session != tag.vio_sid) {
224406db247cSraghuram 			DERR(vswp, "%s (chan %d): invalid session id (%llx)",
224506db247cSraghuram 			    __func__, ldcp->ldc_id, tag.vio_sid);
224606db247cSraghuram 			kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
224706db247cSraghuram 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
224806db247cSraghuram 			return;
224906db247cSraghuram 		}
225006db247cSraghuram 	}
225106db247cSraghuram 
225206db247cSraghuram 	/*
225306db247cSraghuram 	 * Switch on vio_subtype envelope, then let lower routines
225406db247cSraghuram 	 * decide if its an INFO, ACK or NACK packet.
225506db247cSraghuram 	 */
225606db247cSraghuram 	switch (env) {
225706db247cSraghuram 	case VIO_VER_INFO:
225806db247cSraghuram 		vsw_process_ctrl_ver_pkt(ldcp, &ctaskp->pktp);
225906db247cSraghuram 		break;
226006db247cSraghuram 	case VIO_DRING_REG:
226106db247cSraghuram 		vsw_process_ctrl_dring_reg_pkt(ldcp, &ctaskp->pktp);
226206db247cSraghuram 		break;
226306db247cSraghuram 	case VIO_DRING_UNREG:
226406db247cSraghuram 		vsw_process_ctrl_dring_unreg_pkt(ldcp, &ctaskp->pktp);
226506db247cSraghuram 		break;
226606db247cSraghuram 	case VIO_ATTR_INFO:
226706db247cSraghuram 		vsw_process_ctrl_attr_pkt(ldcp, &ctaskp->pktp);
226806db247cSraghuram 		break;
226906db247cSraghuram 	case VNET_MCAST_INFO:
227006db247cSraghuram 		vsw_process_ctrl_mcst_pkt(ldcp, &ctaskp->pktp);
227106db247cSraghuram 		break;
227206db247cSraghuram 	case VIO_RDX:
227306db247cSraghuram 		vsw_process_ctrl_rdx_pkt(ldcp, &ctaskp->pktp);
227406db247cSraghuram 		break;
2275678453a8Sspeer 	case VIO_DDS_INFO:
2276678453a8Sspeer 		vsw_process_dds_msg(vswp, ldcp, &ctaskp->pktp);
2277678453a8Sspeer 		break;
22781107ea93SSriharsha Basavapatna 
22791107ea93SSriharsha Basavapatna 	case VNET_PHYSLINK_INFO:
22801107ea93SSriharsha Basavapatna 		vsw_process_physlink_msg(ldcp, &ctaskp->pktp);
22811107ea93SSriharsha Basavapatna 		break;
228206db247cSraghuram 	default:
228306db247cSraghuram 		DERR(vswp, "%s: unknown vio_subtype_env (%x)\n", __func__, env);
228406db247cSraghuram 	}
228506db247cSraghuram 
228606db247cSraghuram 	kmem_free(ctaskp, sizeof (vsw_ctrl_task_t));
228706db247cSraghuram 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
228806db247cSraghuram }
228906db247cSraghuram 
229006db247cSraghuram /*
229106db247cSraghuram  * Version negotiation. We can end up here either because our peer
229206db247cSraghuram  * has responded to a handshake message we have sent it, or our peer
229306db247cSraghuram  * has initiated a handshake with us. If its the former then can only
229406db247cSraghuram  * be ACK or NACK, if its the later can only be INFO.
229506db247cSraghuram  *
229606db247cSraghuram  * If its an ACK we move to the next stage of the handshake, namely
229706db247cSraghuram  * attribute exchange. If its a NACK we see if we can specify another
229806db247cSraghuram  * version, if we can't we stop.
229906db247cSraghuram  *
230006db247cSraghuram  * If it is an INFO we reset all params associated with communication
230106db247cSraghuram  * in that direction over this channel (remember connection is
230206db247cSraghuram  * essentially 2 independent simplex channels).
230306db247cSraghuram  */
230406db247cSraghuram void
vsw_process_ctrl_ver_pkt(vsw_ldc_t * ldcp,void * pkt)230506db247cSraghuram vsw_process_ctrl_ver_pkt(vsw_ldc_t *ldcp, void *pkt)
230606db247cSraghuram {
230706db247cSraghuram 	vio_ver_msg_t	*ver_pkt;
2308*6e472272SToomas Soome 	vsw_t		*vswp = ldcp->ldc_vswp;
230906db247cSraghuram 
231006db247cSraghuram 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
231106db247cSraghuram 
231206db247cSraghuram 	/*
231306db247cSraghuram 	 * We know this is a ctrl/version packet so
231406db247cSraghuram 	 * cast it into the correct structure.
231506db247cSraghuram 	 */
231606db247cSraghuram 	ver_pkt = (vio_ver_msg_t *)pkt;
231706db247cSraghuram 
231806db247cSraghuram 	switch (ver_pkt->tag.vio_subtype) {
231906db247cSraghuram 	case VIO_SUBTYPE_INFO:
232006db247cSraghuram 		D2(vswp, "vsw_process_ctrl_ver_pkt: VIO_SUBTYPE_INFO\n");
232106db247cSraghuram 
232206db247cSraghuram 		/*
232306db247cSraghuram 		 * Record the session id, which we will use from now
232406db247cSraghuram 		 * until we see another VER_INFO msg. Even then the
232506db247cSraghuram 		 * session id in most cases will be unchanged, execpt
232606db247cSraghuram 		 * if channel was reset.
232706db247cSraghuram 		 */
232806db247cSraghuram 		if ((ldcp->session_status & VSW_PEER_SESSION) &&
232906db247cSraghuram 		    (ldcp->peer_session != ver_pkt->tag.vio_sid)) {
233006db247cSraghuram 			DERR(vswp, "%s: updating session id for chan %lld "
233106db247cSraghuram 			    "from %llx to %llx", __func__, ldcp->ldc_id,
233206db247cSraghuram 			    ldcp->peer_session, ver_pkt->tag.vio_sid);
233306db247cSraghuram 		}
233406db247cSraghuram 
233506db247cSraghuram 		ldcp->peer_session = ver_pkt->tag.vio_sid;
233606db247cSraghuram 		ldcp->session_status |= VSW_PEER_SESSION;
233706db247cSraghuram 
233806db247cSraghuram 		/* Legal message at this time ? */
233906db247cSraghuram 		if (vsw_check_flag(ldcp, INBOUND, VSW_VER_INFO_RECV))
234006db247cSraghuram 			return;
234106db247cSraghuram 
234206db247cSraghuram 		/*
234306db247cSraghuram 		 * First check the device class. Currently only expect
234406db247cSraghuram 		 * to be talking to a network device. In the future may
234506db247cSraghuram 		 * also talk to another switch.
234606db247cSraghuram 		 */
234706db247cSraghuram 		if (ver_pkt->dev_class != VDEV_NETWORK) {
234806db247cSraghuram 			DERR(vswp, "%s: illegal device class %d", __func__,
234906db247cSraghuram 			    ver_pkt->dev_class);
235006db247cSraghuram 
235106db247cSraghuram 			ver_pkt->tag.vio_sid = ldcp->local_session;
235206db247cSraghuram 			ver_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK;
235306db247cSraghuram 
235406db247cSraghuram 			DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt);
235506db247cSraghuram 
235606db247cSraghuram 			(void) vsw_send_msg(ldcp, (void *)ver_pkt,
235706db247cSraghuram 			    sizeof (vio_ver_msg_t), B_TRUE);
235806db247cSraghuram 
235906db247cSraghuram 			ldcp->lane_in.lstate |= VSW_VER_NACK_SENT;
236006db247cSraghuram 			vsw_next_milestone(ldcp);
236106db247cSraghuram 			return;
236206db247cSraghuram 		} else {
236306db247cSraghuram 			ldcp->dev_class = ver_pkt->dev_class;
236406db247cSraghuram 		}
236506db247cSraghuram 
236606db247cSraghuram 		/*
236706db247cSraghuram 		 * Now check the version.
236806db247cSraghuram 		 */
236906db247cSraghuram 		if (vsw_supported_version(ver_pkt) == 0) {
237006db247cSraghuram 			/*
237106db247cSraghuram 			 * Support this major version and possibly
237206db247cSraghuram 			 * adjusted minor version.
237306db247cSraghuram 			 */
237406db247cSraghuram 
237506db247cSraghuram 			D2(vswp, "%s: accepted ver %d:%d", __func__,
237606db247cSraghuram 			    ver_pkt->ver_major, ver_pkt->ver_minor);
237706db247cSraghuram 
237806db247cSraghuram 			/* Store accepted values */
237906db247cSraghuram 			ldcp->lane_in.ver_major = ver_pkt->ver_major;
238006db247cSraghuram 			ldcp->lane_in.ver_minor = ver_pkt->ver_minor;
238106db247cSraghuram 
238206db247cSraghuram 			ver_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
238306db247cSraghuram 
238406db247cSraghuram 			ldcp->lane_in.lstate |= VSW_VER_ACK_SENT;
2385f0ca1d9aSsb 
2386f0ca1d9aSsb 			if (vsw_obp_ver_proto_workaround == B_TRUE) {
2387f0ca1d9aSsb 				/*
2388f0ca1d9aSsb 				 * Send a version info message
2389f0ca1d9aSsb 				 * using the accepted version that
2390f0ca1d9aSsb 				 * we are about to ack. Also note that
2391f0ca1d9aSsb 				 * we send our ver info before we ack.
2392f0ca1d9aSsb 				 * Otherwise, as soon as receiving the
2393f0ca1d9aSsb 				 * ack, obp sends attr info msg, which
2394f0ca1d9aSsb 				 * breaks vsw_check_flag() invoked
2395f0ca1d9aSsb 				 * from vsw_process_ctrl_attr_pkt();
2396f0ca1d9aSsb 				 * as we also need VSW_VER_ACK_RECV to
2397f0ca1d9aSsb 				 * be set in lane_out.lstate, before
2398f0ca1d9aSsb 				 * we can receive attr info.
2399f0ca1d9aSsb 				 */
2400f0ca1d9aSsb 				vsw_send_ver(ldcp);
2401f0ca1d9aSsb 			}
240206db247cSraghuram 		} else {
240306db247cSraghuram 			/*
240406db247cSraghuram 			 * NACK back with the next lower major/minor
240506db247cSraghuram 			 * pairing we support (if don't suuport any more
240606db247cSraghuram 			 * versions then they will be set to zero.
240706db247cSraghuram 			 */
240806db247cSraghuram 
240906db247cSraghuram 			D2(vswp, "%s: replying with ver %d:%d", __func__,
241006db247cSraghuram 			    ver_pkt->ver_major, ver_pkt->ver_minor);
241106db247cSraghuram 
241206db247cSraghuram 			/* Store updated values */
241306db247cSraghuram 			ldcp->lane_in.ver_major = ver_pkt->ver_major;
241406db247cSraghuram 			ldcp->lane_in.ver_minor = ver_pkt->ver_minor;
241506db247cSraghuram 
241606db247cSraghuram 			ver_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK;
241706db247cSraghuram 
241806db247cSraghuram 			ldcp->lane_in.lstate |= VSW_VER_NACK_SENT;
241906db247cSraghuram 		}
242006db247cSraghuram 
242106db247cSraghuram 		DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt);
242206db247cSraghuram 		ver_pkt->tag.vio_sid = ldcp->local_session;
242306db247cSraghuram 		(void) vsw_send_msg(ldcp, (void *)ver_pkt,
242406db247cSraghuram 		    sizeof (vio_ver_msg_t), B_TRUE);
242506db247cSraghuram 
242606db247cSraghuram 		vsw_next_milestone(ldcp);
242706db247cSraghuram 		break;
242806db247cSraghuram 
242906db247cSraghuram 	case VIO_SUBTYPE_ACK:
243006db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_ACK\n", __func__);
243106db247cSraghuram 
243206db247cSraghuram 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_VER_ACK_RECV))
243306db247cSraghuram 			return;
243406db247cSraghuram 
243506db247cSraghuram 		/* Store updated values */
2436f0ca1d9aSsb 		ldcp->lane_out.ver_major = ver_pkt->ver_major;
2437f0ca1d9aSsb 		ldcp->lane_out.ver_minor = ver_pkt->ver_minor;
243806db247cSraghuram 
243906db247cSraghuram 		ldcp->lane_out.lstate |= VSW_VER_ACK_RECV;
244006db247cSraghuram 		vsw_next_milestone(ldcp);
244106db247cSraghuram 
244206db247cSraghuram 		break;
244306db247cSraghuram 
244406db247cSraghuram 	case VIO_SUBTYPE_NACK:
244506db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_NACK\n", __func__);
244606db247cSraghuram 
244706db247cSraghuram 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_VER_NACK_RECV))
244806db247cSraghuram 			return;
244906db247cSraghuram 
245006db247cSraghuram 		/*
245106db247cSraghuram 		 * If our peer sent us a NACK with the ver fields set to
245206db247cSraghuram 		 * zero then there is nothing more we can do. Otherwise see
245306db247cSraghuram 		 * if we support either the version suggested, or a lesser
245406db247cSraghuram 		 * one.
245506db247cSraghuram 		 */
245606db247cSraghuram 		if ((ver_pkt->ver_major == 0) && (ver_pkt->ver_minor == 0)) {
245706db247cSraghuram 			DERR(vswp, "%s: peer unable to negotiate any "
245806db247cSraghuram 			    "further.", __func__);
245906db247cSraghuram 			ldcp->lane_out.lstate |= VSW_VER_NACK_RECV;
246006db247cSraghuram 			vsw_next_milestone(ldcp);
246106db247cSraghuram 			return;
246206db247cSraghuram 		}
246306db247cSraghuram 
246406db247cSraghuram 		/*
246506db247cSraghuram 		 * Check to see if we support this major version or
246606db247cSraghuram 		 * a lower one. If we don't then maj/min will be set
246706db247cSraghuram 		 * to zero.
246806db247cSraghuram 		 */
246906db247cSraghuram 		(void) vsw_supported_version(ver_pkt);
247006db247cSraghuram 		if ((ver_pkt->ver_major == 0) && (ver_pkt->ver_minor == 0)) {
247106db247cSraghuram 			/* Nothing more we can do */
247206db247cSraghuram 			DERR(vswp, "%s: version negotiation failed.\n",
247306db247cSraghuram 			    __func__);
247406db247cSraghuram 			ldcp->lane_out.lstate |= VSW_VER_NACK_RECV;
247506db247cSraghuram 			vsw_next_milestone(ldcp);
247606db247cSraghuram 		} else {
247706db247cSraghuram 			/* found a supported major version */
247806db247cSraghuram 			ldcp->lane_out.ver_major = ver_pkt->ver_major;
247906db247cSraghuram 			ldcp->lane_out.ver_minor = ver_pkt->ver_minor;
248006db247cSraghuram 
248106db247cSraghuram 			D2(vswp, "%s: resending with updated values (%x, %x)",
248206db247cSraghuram 			    __func__, ver_pkt->ver_major, ver_pkt->ver_minor);
248306db247cSraghuram 
248406db247cSraghuram 			ldcp->lane_out.lstate |= VSW_VER_INFO_SENT;
248506db247cSraghuram 			ver_pkt->tag.vio_sid = ldcp->local_session;
248606db247cSraghuram 			ver_pkt->tag.vio_subtype = VIO_SUBTYPE_INFO;
248706db247cSraghuram 
248806db247cSraghuram 			DUMP_TAG_PTR((vio_msg_tag_t *)ver_pkt);
248906db247cSraghuram 
249006db247cSraghuram 			(void) vsw_send_msg(ldcp, (void *)ver_pkt,
249106db247cSraghuram 			    sizeof (vio_ver_msg_t), B_TRUE);
249206db247cSraghuram 
249306db247cSraghuram 			vsw_next_milestone(ldcp);
249406db247cSraghuram 
249506db247cSraghuram 		}
249606db247cSraghuram 		break;
249706db247cSraghuram 
249806db247cSraghuram 	default:
249906db247cSraghuram 		DERR(vswp, "%s: unknown vio_subtype %x\n", __func__,
250006db247cSraghuram 		    ver_pkt->tag.vio_subtype);
250106db247cSraghuram 	}
250206db247cSraghuram 
250306db247cSraghuram 	D1(vswp, "%s(%lld): exit\n", __func__, ldcp->ldc_id);
250406db247cSraghuram }
250506db247cSraghuram 
25067bd3a2e2SSriharsha Basavapatna static int
vsw_process_attr_info(vsw_ldc_t * ldcp,vnet_attr_msg_t * msg)25077bd3a2e2SSriharsha Basavapatna vsw_process_attr_info(vsw_ldc_t *ldcp, vnet_attr_msg_t *msg)
250806db247cSraghuram {
250906db247cSraghuram 	vsw_t			*vswp = ldcp->ldc_vswp;
251006db247cSraghuram 	vsw_port_t		*port = ldcp->ldc_port;
25117bd3a2e2SSriharsha Basavapatna 	struct ether_addr	ea;
251206db247cSraghuram 	uint64_t		macaddr = 0;
25137b1f684aSSriharsha Basavapatna 	lane_t			*lane_out = &ldcp->lane_out;
25147b1f684aSSriharsha Basavapatna 	lane_t			*lane_in = &ldcp->lane_in;
25157b1f684aSSriharsha Basavapatna 	uint32_t		mtu;
251606db247cSraghuram 	int			i;
25177bd3a2e2SSriharsha Basavapatna 	uint8_t			dring_mode;
251806db247cSraghuram 
25197bd3a2e2SSriharsha Basavapatna 	D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
252006db247cSraghuram 
25217bd3a2e2SSriharsha Basavapatna 	if (vsw_check_flag(ldcp, INBOUND, VSW_ATTR_INFO_RECV)) {
25227bd3a2e2SSriharsha Basavapatna 		return (1);
25237bd3a2e2SSriharsha Basavapatna 	}
252406db247cSraghuram 
25257bd3a2e2SSriharsha Basavapatna 	if ((msg->xfer_mode != VIO_DESC_MODE) &&
25267bd3a2e2SSriharsha Basavapatna 	    (msg->xfer_mode != lane_out->xfer_mode)) {
25277bd3a2e2SSriharsha Basavapatna 		D2(NULL, "%s: unknown mode %x\n", __func__, msg->xfer_mode);
25287bd3a2e2SSriharsha Basavapatna 		return (1);
25297bd3a2e2SSriharsha Basavapatna 	}
253006db247cSraghuram 
25317bd3a2e2SSriharsha Basavapatna 	/* Only support MAC addresses at moment. */
25327bd3a2e2SSriharsha Basavapatna 	if ((msg->addr_type != ADDR_TYPE_MAC) || (msg->addr == 0)) {
25337bd3a2e2SSriharsha Basavapatna 		D2(NULL, "%s: invalid addr_type %x, or address 0x%llx\n",
25347bd3a2e2SSriharsha Basavapatna 		    __func__, msg->addr_type, msg->addr);
25357bd3a2e2SSriharsha Basavapatna 		return (1);
25367bd3a2e2SSriharsha Basavapatna 	}
253706db247cSraghuram 
25387bd3a2e2SSriharsha Basavapatna 	/*
25397bd3a2e2SSriharsha Basavapatna 	 * MAC address supplied by device should match that stored
25407bd3a2e2SSriharsha Basavapatna 	 * in the vsw-port OBP node. Need to decide what to do if they
25417bd3a2e2SSriharsha Basavapatna 	 * don't match, for the moment just warn but don't fail.
25427bd3a2e2SSriharsha Basavapatna 	 */
25437bd3a2e2SSriharsha Basavapatna 	vnet_macaddr_ultostr(msg->addr, ea.ether_addr_octet);
25447bd3a2e2SSriharsha Basavapatna 	if (ether_cmp(&ea, &port->p_macaddr) != 0) {
25457bd3a2e2SSriharsha Basavapatna 		DERR(NULL, "%s: device supplied address "
25467bd3a2e2SSriharsha Basavapatna 		    "0x%llx doesn't match node address 0x%llx\n",
25477bd3a2e2SSriharsha Basavapatna 		    __func__, msg->addr, port->p_macaddr);
25487bd3a2e2SSriharsha Basavapatna 	}
254906db247cSraghuram 
25507bd3a2e2SSriharsha Basavapatna 	/*
25517bd3a2e2SSriharsha Basavapatna 	 * Ack freq only makes sense in pkt mode, in shared
25527bd3a2e2SSriharsha Basavapatna 	 * mode the ring descriptors say whether or not to
25537bd3a2e2SSriharsha Basavapatna 	 * send back an ACK.
25547bd3a2e2SSriharsha Basavapatna 	 */
25557bd3a2e2SSriharsha Basavapatna 	if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
25567bd3a2e2SSriharsha Basavapatna 	    (msg->xfer_mode & VIO_DRING_MODE_V1_2)) ||
25577bd3a2e2SSriharsha Basavapatna 	    (VSW_VER_LT(ldcp, 1, 2) &&
25587bd3a2e2SSriharsha Basavapatna 	    (msg->xfer_mode == VIO_DRING_MODE_V1_0))) {
25597bd3a2e2SSriharsha Basavapatna 		if (msg->ack_freq > 0) {
25607bd3a2e2SSriharsha Basavapatna 			D2(NULL, "%s: non zero ack freq in SHM mode\n",
25617bd3a2e2SSriharsha Basavapatna 			    __func__);
25627bd3a2e2SSriharsha Basavapatna 			return (1);
25637bd3a2e2SSriharsha Basavapatna 		}
25647bd3a2e2SSriharsha Basavapatna 	}
256506db247cSraghuram 
25667bd3a2e2SSriharsha Basavapatna 	/*
25677bd3a2e2SSriharsha Basavapatna 	 * Process dring mode attribute.
25687bd3a2e2SSriharsha Basavapatna 	 */
25697bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 6)) {
25707bd3a2e2SSriharsha Basavapatna 		/*
25717bd3a2e2SSriharsha Basavapatna 		 * Versions >= 1.6:
25727bd3a2e2SSriharsha Basavapatna 		 * Though we are operating in v1.6 mode, it is possible that
25737bd3a2e2SSriharsha Basavapatna 		 * RxDringData mode has been disabled either on this guest or
25747bd3a2e2SSriharsha Basavapatna 		 * on the peer guest. If so, we revert to pre v1.6 behavior of
25757bd3a2e2SSriharsha Basavapatna 		 * TxDring mode. But this must be agreed upon in both
25767bd3a2e2SSriharsha Basavapatna 		 * directions of attr exchange. We first determine the mode
25777bd3a2e2SSriharsha Basavapatna 		 * that can be negotiated.
25787bd3a2e2SSriharsha Basavapatna 		 */
25797bd3a2e2SSriharsha Basavapatna 		if ((msg->options & VIO_RX_DRING_DATA) != 0 &&
258034f94fbcSWENTAO YANG 		    vsw_mapin_avail(ldcp) == B_TRUE) {
25817bd3a2e2SSriharsha Basavapatna 			/*
25827bd3a2e2SSriharsha Basavapatna 			 * The peer is capable of handling RxDringData AND we
25837bd3a2e2SSriharsha Basavapatna 			 * are also capable of it; we enable RxDringData mode
25847bd3a2e2SSriharsha Basavapatna 			 * on this channel.
25857bd3a2e2SSriharsha Basavapatna 			 */
25867bd3a2e2SSriharsha Basavapatna 			dring_mode = VIO_RX_DRING_DATA;
25877bd3a2e2SSriharsha Basavapatna 		} else if ((msg->options & VIO_TX_DRING) != 0) {
25887bd3a2e2SSriharsha Basavapatna 			/*
25897bd3a2e2SSriharsha Basavapatna 			 * If the peer is capable of TxDring mode, we
25907bd3a2e2SSriharsha Basavapatna 			 * negotiate TxDring mode on this channel.
25917bd3a2e2SSriharsha Basavapatna 			 */
25927bd3a2e2SSriharsha Basavapatna 			dring_mode = VIO_TX_DRING;
25937b1f684aSSriharsha Basavapatna 		} else {
25947bd3a2e2SSriharsha Basavapatna 			/*
25957bd3a2e2SSriharsha Basavapatna 			 * We support only VIO_TX_DRING and VIO_RX_DRING_DATA
25967bd3a2e2SSriharsha Basavapatna 			 * modes. We don't support VIO_RX_DRING mode.
25977bd3a2e2SSriharsha Basavapatna 			 */
25987bd3a2e2SSriharsha Basavapatna 			return (1);
25997bd3a2e2SSriharsha Basavapatna 		}
26007b1f684aSSriharsha Basavapatna 
26017bd3a2e2SSriharsha Basavapatna 		/*
26027bd3a2e2SSriharsha Basavapatna 		 * If we have received an ack for the attr info that we sent,
26037bd3a2e2SSriharsha Basavapatna 		 * then check if the dring mode matches what the peer had ack'd
26047bd3a2e2SSriharsha Basavapatna 		 * (saved in lane_out). If they don't match, we fail the
26057bd3a2e2SSriharsha Basavapatna 		 * handshake.
26067bd3a2e2SSriharsha Basavapatna 		 */
26077bd3a2e2SSriharsha Basavapatna 		if (lane_out->lstate & VSW_ATTR_ACK_RECV) {
26087bd3a2e2SSriharsha Basavapatna 			if (msg->options != lane_out->dring_mode) {
26097bd3a2e2SSriharsha Basavapatna 				/* send NACK */
26107bd3a2e2SSriharsha Basavapatna 				return (1);
26117b1f684aSSriharsha Basavapatna 			}
26127bd3a2e2SSriharsha Basavapatna 		} else {
26137bd3a2e2SSriharsha Basavapatna 			/*
26147bd3a2e2SSriharsha Basavapatna 			 * Save the negotiated dring mode in our attr
26157bd3a2e2SSriharsha Basavapatna 			 * parameters, so it gets sent in the attr info from us
26167bd3a2e2SSriharsha Basavapatna 			 * to the peer.
26177bd3a2e2SSriharsha Basavapatna 			 */
26187bd3a2e2SSriharsha Basavapatna 			lane_out->dring_mode = dring_mode;
26197b1f684aSSriharsha Basavapatna 		}
26207b1f684aSSriharsha Basavapatna 
26217bd3a2e2SSriharsha Basavapatna 		/* save the negotiated dring mode in the msg to be replied */
26227bd3a2e2SSriharsha Basavapatna 		msg->options = dring_mode;
26237bd3a2e2SSriharsha Basavapatna 	}
262406db247cSraghuram 
26257bd3a2e2SSriharsha Basavapatna 	/*
26267bd3a2e2SSriharsha Basavapatna 	 * Process MTU attribute.
26277bd3a2e2SSriharsha Basavapatna 	 */
26287bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 4)) {
262906db247cSraghuram 		/*
26307bd3a2e2SSriharsha Basavapatna 		 * Versions >= 1.4:
26317bd3a2e2SSriharsha Basavapatna 		 * Validate mtu of the peer is at least ETHERMAX. Then, the mtu
26327bd3a2e2SSriharsha Basavapatna 		 * is negotiated down to the minimum of our mtu and peer's mtu.
263306db247cSraghuram 		 */
26347bd3a2e2SSriharsha Basavapatna 		if (msg->mtu < ETHERMAX) {
26357bd3a2e2SSriharsha Basavapatna 			return (1);
26367bd3a2e2SSriharsha Basavapatna 		}
26377bd3a2e2SSriharsha Basavapatna 
26387bd3a2e2SSriharsha Basavapatna 		mtu = MIN(msg->mtu, vswp->max_frame_size);
26391107ea93SSriharsha Basavapatna 
26401107ea93SSriharsha Basavapatna 		/*
26417bd3a2e2SSriharsha Basavapatna 		 * If we have received an ack for the attr info
26427bd3a2e2SSriharsha Basavapatna 		 * that we sent, then check if the mtu computed
26437bd3a2e2SSriharsha Basavapatna 		 * above matches the mtu that the peer had ack'd
26447bd3a2e2SSriharsha Basavapatna 		 * (saved in local hparams). If they don't
26457bd3a2e2SSriharsha Basavapatna 		 * match, we fail the handshake.
26461107ea93SSriharsha Basavapatna 		 */
26477bd3a2e2SSriharsha Basavapatna 		if (lane_out->lstate & VSW_ATTR_ACK_RECV) {
26487bd3a2e2SSriharsha Basavapatna 			if (mtu != lane_out->mtu) {
26497bd3a2e2SSriharsha Basavapatna 				/* send NACK */
26507bd3a2e2SSriharsha Basavapatna 				return (1);
26511107ea93SSriharsha Basavapatna 			}
26521107ea93SSriharsha Basavapatna 		} else {
26531107ea93SSriharsha Basavapatna 			/*
26547bd3a2e2SSriharsha Basavapatna 			 * Save the mtu computed above in our
26557bd3a2e2SSriharsha Basavapatna 			 * attr parameters, so it gets sent in
26567bd3a2e2SSriharsha Basavapatna 			 * the attr info from us to the peer.
26571107ea93SSriharsha Basavapatna 			 */
26587bd3a2e2SSriharsha Basavapatna 			lane_out->mtu = mtu;
26591107ea93SSriharsha Basavapatna 		}
26607b1f684aSSriharsha Basavapatna 
26617bd3a2e2SSriharsha Basavapatna 		/* save the MIN mtu in the msg to be replied */
26627bd3a2e2SSriharsha Basavapatna 		msg->mtu = mtu;
26637bd3a2e2SSriharsha Basavapatna 	} else {
26647bd3a2e2SSriharsha Basavapatna 		/* Versions < 1.4, mtu must match */
26657bd3a2e2SSriharsha Basavapatna 		if (msg->mtu != lane_out->mtu) {
26667bd3a2e2SSriharsha Basavapatna 			D2(NULL, "%s: invalid MTU (0x%llx)\n",
26677bd3a2e2SSriharsha Basavapatna 			    __func__, msg->mtu);
26687bd3a2e2SSriharsha Basavapatna 			return (1);
266906db247cSraghuram 		}
26707bd3a2e2SSriharsha Basavapatna 	}
267106db247cSraghuram 
26727bd3a2e2SSriharsha Basavapatna 	/*
26737bd3a2e2SSriharsha Basavapatna 	 * Otherwise store attributes for this lane and update
26747bd3a2e2SSriharsha Basavapatna 	 * lane state.
26757bd3a2e2SSriharsha Basavapatna 	 */
26767bd3a2e2SSriharsha Basavapatna 	lane_in->mtu = msg->mtu;
26777bd3a2e2SSriharsha Basavapatna 	lane_in->addr = msg->addr;
26787bd3a2e2SSriharsha Basavapatna 	lane_in->addr_type = msg->addr_type;
26797bd3a2e2SSriharsha Basavapatna 	lane_in->xfer_mode = msg->xfer_mode;
26807bd3a2e2SSriharsha Basavapatna 	lane_in->ack_freq = msg->ack_freq;
26817bd3a2e2SSriharsha Basavapatna 	lane_in->physlink_update = msg->physlink_update;
26827bd3a2e2SSriharsha Basavapatna 	lane_in->dring_mode = msg->options;
2683c1c61f44Ssb 
26847bd3a2e2SSriharsha Basavapatna 	/*
26857bd3a2e2SSriharsha Basavapatna 	 * Check if the client has requested physlink state updates.
26867bd3a2e2SSriharsha Basavapatna 	 * If there is a physical device bound to this vswitch (L2
26877bd3a2e2SSriharsha Basavapatna 	 * mode), set the ack bits to indicate it is supported.
26887bd3a2e2SSriharsha Basavapatna 	 * Otherwise, set the nack bits.
26897bd3a2e2SSriharsha Basavapatna 	 */
26907bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 5)) {	/* Protocol ver >= 1.5 */
26917bd3a2e2SSriharsha Basavapatna 
26927bd3a2e2SSriharsha Basavapatna 		/* Does the vnet need phys link state updates ? */
26937bd3a2e2SSriharsha Basavapatna 		if ((lane_in->physlink_update &
26947bd3a2e2SSriharsha Basavapatna 		    PHYSLINK_UPDATE_STATE_MASK) ==
26957bd3a2e2SSriharsha Basavapatna 		    PHYSLINK_UPDATE_STATE) {
26967bd3a2e2SSriharsha Basavapatna 
26977bd3a2e2SSriharsha Basavapatna 			if (vswp->smode & VSW_LAYER2) {
26987bd3a2e2SSriharsha Basavapatna 				/* is a net-dev assigned to us ? */
26997bd3a2e2SSriharsha Basavapatna 				msg->physlink_update =
27007bd3a2e2SSriharsha Basavapatna 				    PHYSLINK_UPDATE_STATE_ACK;
27017bd3a2e2SSriharsha Basavapatna 				ldcp->pls_negotiated = B_TRUE;
27027bd3a2e2SSriharsha Basavapatna 			} else {
27037bd3a2e2SSriharsha Basavapatna 				/* not in L2 mode */
27047bd3a2e2SSriharsha Basavapatna 				msg->physlink_update =
27057bd3a2e2SSriharsha Basavapatna 				    PHYSLINK_UPDATE_STATE_NACK;
27067bd3a2e2SSriharsha Basavapatna 				ldcp->pls_negotiated = B_FALSE;
27077bd3a2e2SSriharsha Basavapatna 			}
270806db247cSraghuram 
27097bd3a2e2SSriharsha Basavapatna 		} else {
27107bd3a2e2SSriharsha Basavapatna 			msg->physlink_update =
27117bd3a2e2SSriharsha Basavapatna 			    PHYSLINK_UPDATE_NONE;
27127bd3a2e2SSriharsha Basavapatna 			ldcp->pls_negotiated = B_FALSE;
271306db247cSraghuram 		}
2714678453a8Sspeer 
27157bd3a2e2SSriharsha Basavapatna 	} else {
2716678453a8Sspeer 		/*
27177bd3a2e2SSriharsha Basavapatna 		 * physlink_update bits are ignored
27187bd3a2e2SSriharsha Basavapatna 		 * if set by clients < v1.5 protocol.
2719678453a8Sspeer 		 */
27207bd3a2e2SSriharsha Basavapatna 		msg->physlink_update = PHYSLINK_UPDATE_NONE;
27217bd3a2e2SSriharsha Basavapatna 		ldcp->pls_negotiated = B_FALSE;
27227bd3a2e2SSriharsha Basavapatna 	}
2723678453a8Sspeer 
27247bd3a2e2SSriharsha Basavapatna 	macaddr = lane_in->addr;
27257bd3a2e2SSriharsha Basavapatna 	for (i = ETHERADDRL - 1; i >= 0; i--) {
27267bd3a2e2SSriharsha Basavapatna 		port->p_macaddr.ether_addr_octet[i] = macaddr & 0xFF;
27277bd3a2e2SSriharsha Basavapatna 		macaddr >>= 8;
27287bd3a2e2SSriharsha Basavapatna 	}
272906db247cSraghuram 
27307bd3a2e2SSriharsha Basavapatna 	/*
27317bd3a2e2SSriharsha Basavapatna 	 * Setup device specific xmit routines. Note this could be changed
27327bd3a2e2SSriharsha Basavapatna 	 * further in vsw_send_dring_info() for versions >= 1.6 if operating in
27337bd3a2e2SSriharsha Basavapatna 	 * RxDringData mode.
27347bd3a2e2SSriharsha Basavapatna 	 */
27357bd3a2e2SSriharsha Basavapatna 	mutex_enter(&port->tx_lock);
273606db247cSraghuram 
27377bd3a2e2SSriharsha Basavapatna 	if ((VSW_VER_GTEQ(ldcp, 1, 2) &&
27387bd3a2e2SSriharsha Basavapatna 	    (lane_in->xfer_mode & VIO_DRING_MODE_V1_2)) ||
27397bd3a2e2SSriharsha Basavapatna 	    (VSW_VER_LT(ldcp, 1, 2) &&
27407bd3a2e2SSriharsha Basavapatna 	    (lane_in->xfer_mode == VIO_DRING_MODE_V1_0))) {
27417bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: mode = VIO_DRING_MODE", __func__);
27427bd3a2e2SSriharsha Basavapatna 		port->transmit = vsw_dringsend;
27437bd3a2e2SSriharsha Basavapatna 	} else if (lane_in->xfer_mode == VIO_DESC_MODE) {
27447bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: mode = VIO_DESC_MODE", __func__);
27457bd3a2e2SSriharsha Basavapatna 		vsw_create_privring(ldcp);
27467bd3a2e2SSriharsha Basavapatna 		port->transmit = vsw_descrsend;
27477bd3a2e2SSriharsha Basavapatna 		lane_out->xfer_mode = VIO_DESC_MODE;
27487bd3a2e2SSriharsha Basavapatna 	}
274906db247cSraghuram 
27507bd3a2e2SSriharsha Basavapatna 	/*
27517bd3a2e2SSriharsha Basavapatna 	 * HybridIO is supported only vnet, not by OBP.
27527bd3a2e2SSriharsha Basavapatna 	 * So, set hio_capable to true only when in DRING mode.
27537bd3a2e2SSriharsha Basavapatna 	 */
27547bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 3) &&
27557bd3a2e2SSriharsha Basavapatna 	    (lane_in->xfer_mode != VIO_DESC_MODE)) {
27567bd3a2e2SSriharsha Basavapatna 		(void) atomic_swap_32(&port->p_hio_capable, B_TRUE);
27577bd3a2e2SSriharsha Basavapatna 	} else {
27587bd3a2e2SSriharsha Basavapatna 		(void) atomic_swap_32(&port->p_hio_capable, B_FALSE);
27597bd3a2e2SSriharsha Basavapatna 	}
276006db247cSraghuram 
27617bd3a2e2SSriharsha Basavapatna 	mutex_exit(&port->tx_lock);
276206db247cSraghuram 
27637bd3a2e2SSriharsha Basavapatna 	return (0);
27647bd3a2e2SSriharsha Basavapatna }
276506db247cSraghuram 
27667bd3a2e2SSriharsha Basavapatna static int
vsw_process_attr_ack(vsw_ldc_t * ldcp,vnet_attr_msg_t * msg)27677bd3a2e2SSriharsha Basavapatna vsw_process_attr_ack(vsw_ldc_t *ldcp, vnet_attr_msg_t *msg)
27687bd3a2e2SSriharsha Basavapatna {
27697bd3a2e2SSriharsha Basavapatna 	vsw_t	*vswp = ldcp->ldc_vswp;
27707bd3a2e2SSriharsha Basavapatna 	lane_t	*lane_out = &ldcp->lane_out;
27717bd3a2e2SSriharsha Basavapatna 	lane_t	*lane_in = &ldcp->lane_in;
27727bd3a2e2SSriharsha Basavapatna 
27737bd3a2e2SSriharsha Basavapatna 	D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
27747bd3a2e2SSriharsha Basavapatna 
27757bd3a2e2SSriharsha Basavapatna 	if (vsw_check_flag(ldcp, OUTBOUND, VSW_ATTR_ACK_RECV)) {
27767bd3a2e2SSriharsha Basavapatna 		return (1);
27777bd3a2e2SSriharsha Basavapatna 	}
27787bd3a2e2SSriharsha Basavapatna 
27797bd3a2e2SSriharsha Basavapatna 	/*
27807bd3a2e2SSriharsha Basavapatna 	 * Process dring mode attribute.
27817bd3a2e2SSriharsha Basavapatna 	 */
27827bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 6)) {
27837bd3a2e2SSriharsha Basavapatna 		/*
27847bd3a2e2SSriharsha Basavapatna 		 * Versions >= 1.6:
27857bd3a2e2SSriharsha Basavapatna 		 * The ack msg sent by the peer contains the negotiated dring
27867bd3a2e2SSriharsha Basavapatna 		 * mode between our capability (that we had sent in our attr
27877bd3a2e2SSriharsha Basavapatna 		 * info) and the peer's capability.
27887bd3a2e2SSriharsha Basavapatna 		 */
27897bd3a2e2SSriharsha Basavapatna 		if (lane_in->lstate & VSW_ATTR_ACK_SENT) {
27907b1f684aSSriharsha Basavapatna 			/*
27917b1f684aSSriharsha Basavapatna 			 * If we have sent an ack for the attr info msg from
27927bd3a2e2SSriharsha Basavapatna 			 * the peer, check if the dring mode that was
27937bd3a2e2SSriharsha Basavapatna 			 * negotiated then (saved in lane_out) matches the
27947bd3a2e2SSriharsha Basavapatna 			 * mode that the peer has ack'd. If they don't match,
27957bd3a2e2SSriharsha Basavapatna 			 * we fail the handshake.
27967b1f684aSSriharsha Basavapatna 			 */
27977bd3a2e2SSriharsha Basavapatna 			if (lane_out->dring_mode != msg->options) {
27987bd3a2e2SSriharsha Basavapatna 				return (1);
27997bd3a2e2SSriharsha Basavapatna 			}
28007bd3a2e2SSriharsha Basavapatna 		} else {
28017bd3a2e2SSriharsha Basavapatna 			if ((msg->options & lane_out->dring_mode) == 0) {
28027b1f684aSSriharsha Basavapatna 				/*
28037bd3a2e2SSriharsha Basavapatna 				 * Peer ack'd with a mode that we don't
28047bd3a2e2SSriharsha Basavapatna 				 * support; we fail the handshake.
28057b1f684aSSriharsha Basavapatna 				 */
28067bd3a2e2SSriharsha Basavapatna 				return (1);
28077bd3a2e2SSriharsha Basavapatna 			}
28087bd3a2e2SSriharsha Basavapatna 			if ((msg->options & (VIO_TX_DRING|VIO_RX_DRING_DATA))
28097bd3a2e2SSriharsha Basavapatna 			    == (VIO_TX_DRING|VIO_RX_DRING_DATA)) {
28107bd3a2e2SSriharsha Basavapatna 				/*
28117bd3a2e2SSriharsha Basavapatna 				 * Peer must ack with only one negotiated mode.
28127bd3a2e2SSriharsha Basavapatna 				 * Otherwise fail handshake.
28137bd3a2e2SSriharsha Basavapatna 				 */
28147bd3a2e2SSriharsha Basavapatna 				return (1);
28157b1f684aSSriharsha Basavapatna 			}
281606db247cSraghuram 
28177bd3a2e2SSriharsha Basavapatna 			/*
28187bd3a2e2SSriharsha Basavapatna 			 * Save the negotiated mode, so we can validate it when
28197bd3a2e2SSriharsha Basavapatna 			 * we receive attr info from the peer.
28207bd3a2e2SSriharsha Basavapatna 			 */
28217bd3a2e2SSriharsha Basavapatna 			lane_out->dring_mode = msg->options;
28227bd3a2e2SSriharsha Basavapatna 		}
28237bd3a2e2SSriharsha Basavapatna 	}
282406db247cSraghuram 
28257bd3a2e2SSriharsha Basavapatna 	/*
28267bd3a2e2SSriharsha Basavapatna 	 * Process MTU attribute.
28277bd3a2e2SSriharsha Basavapatna 	 */
28287bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 4)) {
28297bd3a2e2SSriharsha Basavapatna 		/*
28307bd3a2e2SSriharsha Basavapatna 		 * Versions >= 1.4:
28317bd3a2e2SSriharsha Basavapatna 		 * The ack msg sent by the peer contains the minimum of
28327bd3a2e2SSriharsha Basavapatna 		 * our mtu (that we had sent in our attr info) and the
28337bd3a2e2SSriharsha Basavapatna 		 * peer's mtu.
28347bd3a2e2SSriharsha Basavapatna 		 *
28357bd3a2e2SSriharsha Basavapatna 		 * If we have sent an ack for the attr info msg from
28367bd3a2e2SSriharsha Basavapatna 		 * the peer, check if the mtu that was computed then
28377bd3a2e2SSriharsha Basavapatna 		 * (saved in lane_out params) matches the mtu that the
28387bd3a2e2SSriharsha Basavapatna 		 * peer has ack'd. If they don't match, we fail the
28397bd3a2e2SSriharsha Basavapatna 		 * handshake.
28407bd3a2e2SSriharsha Basavapatna 		 */
28417bd3a2e2SSriharsha Basavapatna 		if (lane_in->lstate & VSW_ATTR_ACK_SENT) {
28427bd3a2e2SSriharsha Basavapatna 			if (lane_out->mtu != msg->mtu) {
28437bd3a2e2SSriharsha Basavapatna 				return (1);
28447bd3a2e2SSriharsha Basavapatna 			}
28457bd3a2e2SSriharsha Basavapatna 		} else {
28467bd3a2e2SSriharsha Basavapatna 			/*
28477bd3a2e2SSriharsha Basavapatna 			 * If the mtu ack'd by the peer is > our mtu
28487bd3a2e2SSriharsha Basavapatna 			 * fail handshake. Otherwise, save the mtu, so
28497bd3a2e2SSriharsha Basavapatna 			 * we can validate it when we receive attr info
28507bd3a2e2SSriharsha Basavapatna 			 * from our peer.
28517bd3a2e2SSriharsha Basavapatna 			 */
28527bd3a2e2SSriharsha Basavapatna 			if (msg->mtu <= lane_out->mtu) {
28537bd3a2e2SSriharsha Basavapatna 				lane_out->mtu = msg->mtu;
28547bd3a2e2SSriharsha Basavapatna 			} else {
28557bd3a2e2SSriharsha Basavapatna 				return (1);
28567bd3a2e2SSriharsha Basavapatna 			}
28577bd3a2e2SSriharsha Basavapatna 		}
285806db247cSraghuram 	}
285906db247cSraghuram 
28607bd3a2e2SSriharsha Basavapatna 	return (0);
286106db247cSraghuram }
286206db247cSraghuram 
286306db247cSraghuram /*
28647bd3a2e2SSriharsha Basavapatna  * Process an attribute packet. We can end up here either because our peer
28657bd3a2e2SSriharsha Basavapatna  * has ACK/NACK'ed back to an earlier ATTR msg we had sent it, or our
28667bd3a2e2SSriharsha Basavapatna  * peer has sent us an attribute INFO message
28677bd3a2e2SSriharsha Basavapatna  *
28687bd3a2e2SSriharsha Basavapatna  * If its an ACK we then move to the next stage of the handshake which
28697bd3a2e2SSriharsha Basavapatna  * is to send our descriptor ring info to our peer. If its a NACK then
28707bd3a2e2SSriharsha Basavapatna  * there is nothing more we can (currently) do.
287106db247cSraghuram  *
287206db247cSraghuram  * If we get a valid/acceptable INFO packet (and we have already negotiated
28737bd3a2e2SSriharsha Basavapatna  * a version) we ACK back and set channel state to ATTR_RECV, otherwise we
28747bd3a2e2SSriharsha Basavapatna  * NACK back and reset channel state to INACTIV.
287506db247cSraghuram  *
28767bd3a2e2SSriharsha Basavapatna  * FUTURE: in time we will probably negotiate over attributes, but for
28777bd3a2e2SSriharsha Basavapatna  * the moment unacceptable attributes are regarded as a fatal error.
287806db247cSraghuram  *
287906db247cSraghuram  */
288006db247cSraghuram void
vsw_process_ctrl_attr_pkt(vsw_ldc_t * ldcp,void * pkt)28817bd3a2e2SSriharsha Basavapatna vsw_process_ctrl_attr_pkt(vsw_ldc_t *ldcp, void *pkt)
288206db247cSraghuram {
28837bd3a2e2SSriharsha Basavapatna 	vnet_attr_msg_t	*attr_pkt;
28847bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
28857bd3a2e2SSriharsha Basavapatna 	lane_t		*lane_out = &ldcp->lane_out;
28867bd3a2e2SSriharsha Basavapatna 	lane_t		*lane_in = &ldcp->lane_in;
28877bd3a2e2SSriharsha Basavapatna 	int		rv;
28887bd3a2e2SSriharsha Basavapatna 
28897bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
289006db247cSraghuram 
289106db247cSraghuram 	/*
28927bd3a2e2SSriharsha Basavapatna 	 * We know this is a ctrl/attr packet so
289306db247cSraghuram 	 * cast it into the correct structure.
289406db247cSraghuram 	 */
28957bd3a2e2SSriharsha Basavapatna 	attr_pkt = (vnet_attr_msg_t *)pkt;
289606db247cSraghuram 
28977bd3a2e2SSriharsha Basavapatna 	switch (attr_pkt->tag.vio_subtype) {
289806db247cSraghuram 	case VIO_SUBTYPE_INFO:
289906db247cSraghuram 
29007bd3a2e2SSriharsha Basavapatna 		rv = vsw_process_attr_info(ldcp, attr_pkt);
29017bd3a2e2SSriharsha Basavapatna 		if (rv != 0) {
290206db247cSraghuram 			vsw_free_lane_resources(ldcp, INBOUND);
29037bd3a2e2SSriharsha Basavapatna 			attr_pkt->tag.vio_subtype = VIO_SUBTYPE_NACK;
29047bd3a2e2SSriharsha Basavapatna 			ldcp->lane_in.lstate |= VSW_ATTR_NACK_SENT;
29057bd3a2e2SSriharsha Basavapatna 		} else {
29067bd3a2e2SSriharsha Basavapatna 			attr_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
29077bd3a2e2SSriharsha Basavapatna 			lane_in->lstate |= VSW_ATTR_ACK_SENT;
290806db247cSraghuram 		}
29097bd3a2e2SSriharsha Basavapatna 		attr_pkt->tag.vio_sid = ldcp->local_session;
29107bd3a2e2SSriharsha Basavapatna 		DUMP_TAG_PTR((vio_msg_tag_t *)attr_pkt);
29117bd3a2e2SSriharsha Basavapatna 		(void) vsw_send_msg(ldcp, (void *)attr_pkt,
29127bd3a2e2SSriharsha Basavapatna 		    sizeof (vnet_attr_msg_t), B_TRUE);
29137bd3a2e2SSriharsha Basavapatna 		vsw_next_milestone(ldcp);
29147bd3a2e2SSriharsha Basavapatna 		break;
291506db247cSraghuram 
29167bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_ACK:
291706db247cSraghuram 
29187bd3a2e2SSriharsha Basavapatna 		rv = vsw_process_attr_ack(ldcp, attr_pkt);
29197bd3a2e2SSriharsha Basavapatna 		if (rv != 0) {
292006db247cSraghuram 			return;
292106db247cSraghuram 		}
29227bd3a2e2SSriharsha Basavapatna 		lane_out->lstate |= VSW_ATTR_ACK_RECV;
29237bd3a2e2SSriharsha Basavapatna 		vsw_next_milestone(ldcp);
29247bd3a2e2SSriharsha Basavapatna 		break;
292506db247cSraghuram 
29267bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_NACK:
29277bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
292806db247cSraghuram 
29297bd3a2e2SSriharsha Basavapatna 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_ATTR_NACK_RECV))
29307bd3a2e2SSriharsha Basavapatna 			return;
293106db247cSraghuram 
29327bd3a2e2SSriharsha Basavapatna 		lane_out->lstate |= VSW_ATTR_NACK_RECV;
29337bd3a2e2SSriharsha Basavapatna 		vsw_next_milestone(ldcp);
29347bd3a2e2SSriharsha Basavapatna 		break;
293506db247cSraghuram 
29367bd3a2e2SSriharsha Basavapatna 	default:
29377bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: unknown vio_subtype %x\n", __func__,
29387bd3a2e2SSriharsha Basavapatna 		    attr_pkt->tag.vio_subtype);
29397bd3a2e2SSriharsha Basavapatna 	}
2940bbfa0259Sha 
29417bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id);
29427bd3a2e2SSriharsha Basavapatna }
294306db247cSraghuram 
29447bd3a2e2SSriharsha Basavapatna static int
vsw_process_dring_reg_info(vsw_ldc_t * ldcp,vio_msg_tag_t * tagp)29457bd3a2e2SSriharsha Basavapatna vsw_process_dring_reg_info(vsw_ldc_t *ldcp, vio_msg_tag_t *tagp)
29467bd3a2e2SSriharsha Basavapatna {
29477bd3a2e2SSriharsha Basavapatna 	int		rv;
29487bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
29497bd3a2e2SSriharsha Basavapatna 	lane_t		*lp = &ldcp->lane_out;
29507bd3a2e2SSriharsha Basavapatna 	dring_info_t	*dp = NULL;
295106db247cSraghuram 
29527bd3a2e2SSriharsha Basavapatna 	D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
295306db247cSraghuram 
29547bd3a2e2SSriharsha Basavapatna 	rv = vsw_check_flag(ldcp, INBOUND, VSW_DRING_INFO_RECV);
29557bd3a2e2SSriharsha Basavapatna 	if (rv != 0) {
29567bd3a2e2SSriharsha Basavapatna 		return (1);
29577bd3a2e2SSriharsha Basavapatna 	}
295806db247cSraghuram 
29597bd3a2e2SSriharsha Basavapatna 	if (VSW_VER_GTEQ(ldcp, 1, 6) &&
29607bd3a2e2SSriharsha Basavapatna 	    (lp->dring_mode != ((vio_dring_reg_msg_t *)tagp)->options)) {
296106db247cSraghuram 		/*
29627bd3a2e2SSriharsha Basavapatna 		 * The earlier version of Solaris vnet driver doesn't set the
29637bd3a2e2SSriharsha Basavapatna 		 * option (VIO_TX_DRING in its case) correctly in its dring reg
29647bd3a2e2SSriharsha Basavapatna 		 * message. We workaround that here by doing the check only
29657bd3a2e2SSriharsha Basavapatna 		 * for versions >= v1.6.
296606db247cSraghuram 		 */
29677bd3a2e2SSriharsha Basavapatna 		DWARN(vswp, "%s(%lld): Rcvd dring reg option (%d), "
29687bd3a2e2SSriharsha Basavapatna 		    "negotiated mode (%d)\n", __func__, ldcp->ldc_id,
29697bd3a2e2SSriharsha Basavapatna 		    ((vio_dring_reg_msg_t *)tagp)->options, lp->dring_mode);
29707bd3a2e2SSriharsha Basavapatna 		return (1);
29717bd3a2e2SSriharsha Basavapatna 	}
297206db247cSraghuram 
29737bd3a2e2SSriharsha Basavapatna 	/*
29747bd3a2e2SSriharsha Basavapatna 	 * Map dring exported by the peer.
29757bd3a2e2SSriharsha Basavapatna 	 */
29767bd3a2e2SSriharsha Basavapatna 	dp = vsw_map_dring(ldcp, (void *)tagp);
29777bd3a2e2SSriharsha Basavapatna 	if (dp == NULL) {
29787bd3a2e2SSriharsha Basavapatna 		return (1);
29797bd3a2e2SSriharsha Basavapatna 	}
298006db247cSraghuram 
29817bd3a2e2SSriharsha Basavapatna 	/*
29827bd3a2e2SSriharsha Basavapatna 	 * Map data buffers exported by the peer if we are in RxDringData mode.
29837bd3a2e2SSriharsha Basavapatna 	 */
29847bd3a2e2SSriharsha Basavapatna 	if (lp->dring_mode == VIO_RX_DRING_DATA) {
29857bd3a2e2SSriharsha Basavapatna 		rv = vsw_map_data(ldcp, dp, (void *)tagp);
29867bd3a2e2SSriharsha Basavapatna 		if (rv != 0) {
29877bd3a2e2SSriharsha Basavapatna 			vsw_unmap_dring(ldcp);
29887bd3a2e2SSriharsha Basavapatna 			return (1);
298906db247cSraghuram 		}
29907bd3a2e2SSriharsha Basavapatna 	}
29917bd3a2e2SSriharsha Basavapatna 
29927bd3a2e2SSriharsha Basavapatna 	return (0);
29937bd3a2e2SSriharsha Basavapatna }
299406db247cSraghuram 
29957bd3a2e2SSriharsha Basavapatna static int
vsw_process_dring_reg_ack(vsw_ldc_t * ldcp,vio_msg_tag_t * tagp)29967bd3a2e2SSriharsha Basavapatna vsw_process_dring_reg_ack(vsw_ldc_t *ldcp, vio_msg_tag_t *tagp)
29977bd3a2e2SSriharsha Basavapatna {
29987bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
29997bd3a2e2SSriharsha Basavapatna 	dring_info_t	*dp;
300006db247cSraghuram 
30017bd3a2e2SSriharsha Basavapatna 	D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
300206db247cSraghuram 
30037bd3a2e2SSriharsha Basavapatna 	if (vsw_check_flag(ldcp, OUTBOUND, VSW_DRING_ACK_RECV)) {
30047bd3a2e2SSriharsha Basavapatna 		return (1);
30057bd3a2e2SSriharsha Basavapatna 	}
300606db247cSraghuram 
30077bd3a2e2SSriharsha Basavapatna 	dp = ldcp->lane_out.dringp;
300806db247cSraghuram 
30097bd3a2e2SSriharsha Basavapatna 	/* save dring_ident acked by peer */
30107bd3a2e2SSriharsha Basavapatna 	dp->ident = ((vio_dring_reg_msg_t *)tagp)->dring_ident;
301106db247cSraghuram 
30127bd3a2e2SSriharsha Basavapatna 	return (0);
30137bd3a2e2SSriharsha Basavapatna }
301406db247cSraghuram 
30157bd3a2e2SSriharsha Basavapatna /*
30167bd3a2e2SSriharsha Basavapatna  * Process a dring info packet. We can end up here either because our peer
30177bd3a2e2SSriharsha Basavapatna  * has ACK/NACK'ed back to an earlier DRING msg we had sent it, or our
30187bd3a2e2SSriharsha Basavapatna  * peer has sent us a dring INFO message.
30197bd3a2e2SSriharsha Basavapatna  *
30207bd3a2e2SSriharsha Basavapatna  * If we get a valid/acceptable INFO packet (and we have already negotiated
30217bd3a2e2SSriharsha Basavapatna  * a version) we ACK back and update the lane state, otherwise we NACK back.
30227bd3a2e2SSriharsha Basavapatna  *
30237bd3a2e2SSriharsha Basavapatna  * FUTURE: nothing to stop client from sending us info on multiple dring's
30247bd3a2e2SSriharsha Basavapatna  * but for the moment we will just use the first one we are given.
30257bd3a2e2SSriharsha Basavapatna  *
30267bd3a2e2SSriharsha Basavapatna  */
30277bd3a2e2SSriharsha Basavapatna void
vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t * ldcp,void * pkt)30287bd3a2e2SSriharsha Basavapatna vsw_process_ctrl_dring_reg_pkt(vsw_ldc_t *ldcp, void *pkt)
30297bd3a2e2SSriharsha Basavapatna {
30307bd3a2e2SSriharsha Basavapatna 	int		rv;
30317bd3a2e2SSriharsha Basavapatna 	int		msgsize;
30327bd3a2e2SSriharsha Basavapatna 	dring_info_t	*dp;
30337bd3a2e2SSriharsha Basavapatna 	vio_msg_tag_t	*tagp = (vio_msg_tag_t *)pkt;
30347bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
30357bd3a2e2SSriharsha Basavapatna 	lane_t		*lane_out = &ldcp->lane_out;
30367bd3a2e2SSriharsha Basavapatna 	lane_t		*lane_in = &ldcp->lane_in;
303706db247cSraghuram 
30387bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
303906db247cSraghuram 
30407bd3a2e2SSriharsha Basavapatna 	switch (tagp->vio_subtype) {
30417bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_INFO:
30427bd3a2e2SSriharsha Basavapatna 		rv = vsw_process_dring_reg_info(ldcp, tagp);
30437bd3a2e2SSriharsha Basavapatna 		if (rv != 0) {
30447bd3a2e2SSriharsha Basavapatna 			vsw_free_lane_resources(ldcp, INBOUND);
30457bd3a2e2SSriharsha Basavapatna 			tagp->vio_subtype = VIO_SUBTYPE_NACK;
30467bd3a2e2SSriharsha Basavapatna 			lane_in->lstate |= VSW_DRING_NACK_SENT;
304706db247cSraghuram 		} else {
30487bd3a2e2SSriharsha Basavapatna 			tagp->vio_subtype = VIO_SUBTYPE_ACK;
30497bd3a2e2SSriharsha Basavapatna 			lane_in->lstate |= VSW_DRING_ACK_SENT;
305006db247cSraghuram 		}
30517bd3a2e2SSriharsha Basavapatna 		tagp->vio_sid = ldcp->local_session;
30527bd3a2e2SSriharsha Basavapatna 		DUMP_TAG_PTR(tagp);
30537bd3a2e2SSriharsha Basavapatna 		if (lane_out->dring_mode == VIO_RX_DRING_DATA) {
30547bd3a2e2SSriharsha Basavapatna 			dp = lane_in->dringp;
30557bd3a2e2SSriharsha Basavapatna 			msgsize =
30567bd3a2e2SSriharsha Basavapatna 			    VNET_DRING_REG_EXT_MSG_SIZE(dp->data_ncookies);
30577bd3a2e2SSriharsha Basavapatna 		} else {
30587bd3a2e2SSriharsha Basavapatna 			msgsize = sizeof (vio_dring_reg_msg_t);
30597bd3a2e2SSriharsha Basavapatna 		}
30607bd3a2e2SSriharsha Basavapatna 		(void) vsw_send_msg(ldcp, (void *)tagp, msgsize, B_TRUE);
30617bd3a2e2SSriharsha Basavapatna 		vsw_next_milestone(ldcp);
30627bd3a2e2SSriharsha Basavapatna 		break;
306306db247cSraghuram 
30647bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_ACK:
30657bd3a2e2SSriharsha Basavapatna 		rv = vsw_process_dring_reg_ack(ldcp, tagp);
30667bd3a2e2SSriharsha Basavapatna 		if (rv != 0) {
30677bd3a2e2SSriharsha Basavapatna 			return;
30687bd3a2e2SSriharsha Basavapatna 		}
30697bd3a2e2SSriharsha Basavapatna 		lane_out->lstate |= VSW_DRING_ACK_RECV;
307006db247cSraghuram 		vsw_next_milestone(ldcp);
307106db247cSraghuram 		break;
307206db247cSraghuram 
307306db247cSraghuram 	case VIO_SUBTYPE_NACK:
307406db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
307506db247cSraghuram 
307606db247cSraghuram 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_DRING_NACK_RECV))
307706db247cSraghuram 			return;
307806db247cSraghuram 
30797bd3a2e2SSriharsha Basavapatna 		lane_out->lstate |= VSW_DRING_NACK_RECV;
308006db247cSraghuram 		vsw_next_milestone(ldcp);
308106db247cSraghuram 		break;
308206db247cSraghuram 
308306db247cSraghuram 	default:
308406db247cSraghuram 		DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
30857bd3a2e2SSriharsha Basavapatna 		    tagp->vio_subtype);
308606db247cSraghuram 	}
308706db247cSraghuram 
308806db247cSraghuram 	D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id);
308906db247cSraghuram }
309006db247cSraghuram 
309106db247cSraghuram /*
309206db247cSraghuram  * Process a request from peer to unregister a dring.
309306db247cSraghuram  *
309406db247cSraghuram  * For the moment we just restart the handshake if our
309506db247cSraghuram  * peer endpoint attempts to unregister a dring.
309606db247cSraghuram  */
309706db247cSraghuram void
vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t * ldcp,void * pkt)309806db247cSraghuram vsw_process_ctrl_dring_unreg_pkt(vsw_ldc_t *ldcp, void *pkt)
309906db247cSraghuram {
310006db247cSraghuram 	vsw_t			*vswp = ldcp->ldc_vswp;
310106db247cSraghuram 	vio_dring_unreg_msg_t	*dring_pkt;
310206db247cSraghuram 
310306db247cSraghuram 	/*
310406db247cSraghuram 	 * We know this is a ctrl/dring packet so
310506db247cSraghuram 	 * cast it into the correct structure.
310606db247cSraghuram 	 */
310706db247cSraghuram 	dring_pkt = (vio_dring_unreg_msg_t *)pkt;
310806db247cSraghuram 
310906db247cSraghuram 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
311006db247cSraghuram 
311106db247cSraghuram 	switch (dring_pkt->tag.vio_subtype) {
311206db247cSraghuram 	case VIO_SUBTYPE_INFO:
311306db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
311406db247cSraghuram 
311506db247cSraghuram 		DWARN(vswp, "%s: restarting handshake..", __func__);
311606db247cSraghuram 		break;
311706db247cSraghuram 
311806db247cSraghuram 	case VIO_SUBTYPE_ACK:
311906db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
312006db247cSraghuram 
312106db247cSraghuram 		DWARN(vswp, "%s: restarting handshake..", __func__);
312206db247cSraghuram 		break;
312306db247cSraghuram 
312406db247cSraghuram 	case VIO_SUBTYPE_NACK:
312506db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
312606db247cSraghuram 
312706db247cSraghuram 		DWARN(vswp, "%s: restarting handshake..", __func__);
312806db247cSraghuram 		break;
312906db247cSraghuram 
313006db247cSraghuram 	default:
313106db247cSraghuram 		DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
313206db247cSraghuram 		    dring_pkt->tag.vio_subtype);
313306db247cSraghuram 	}
313406db247cSraghuram 
313506db247cSraghuram 	vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
313606db247cSraghuram 
313706db247cSraghuram 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
313806db247cSraghuram }
313906db247cSraghuram 
314006db247cSraghuram #define	SND_MCST_NACK(ldcp, pkt) \
314106db247cSraghuram 	pkt->tag.vio_subtype = VIO_SUBTYPE_NACK; \
314206db247cSraghuram 	pkt->tag.vio_sid = ldcp->local_session; \
314306db247cSraghuram 	(void) vsw_send_msg(ldcp, (void *)pkt, \
314406db247cSraghuram 			sizeof (vnet_mcast_msg_t), B_TRUE);
314506db247cSraghuram 
314606db247cSraghuram /*
314706db247cSraghuram  * Process a multicast request from a vnet.
314806db247cSraghuram  *
314906db247cSraghuram  * Vnet's specify a multicast address that they are interested in. This
315006db247cSraghuram  * address is used as a key into the hash table which forms the multicast
315106db247cSraghuram  * forwarding database (mFDB).
315206db247cSraghuram  *
315306db247cSraghuram  * The table keys are the multicast addresses, while the table entries
315406db247cSraghuram  * are pointers to lists of ports which wish to receive packets for the
315506db247cSraghuram  * specified multicast address.
315606db247cSraghuram  *
315706db247cSraghuram  * When a multicast packet is being switched we use the address as a key
315806db247cSraghuram  * into the hash table, and then walk the appropriate port list forwarding
315906db247cSraghuram  * the pkt to each port in turn.
316006db247cSraghuram  *
316106db247cSraghuram  * If a vnet is no longer interested in a particular multicast grouping
316206db247cSraghuram  * we simply find the correct location in the hash table and then delete
316306db247cSraghuram  * the relevant port from the port list.
316406db247cSraghuram  *
316506db247cSraghuram  * To deal with the case whereby a port is being deleted without first
316606db247cSraghuram  * removing itself from the lists in the hash table, we maintain a list
316706db247cSraghuram  * of multicast addresses the port has registered an interest in, within
316806db247cSraghuram  * the port structure itself. We then simply walk that list of addresses
316906db247cSraghuram  * using them as keys into the hash table and remove the port from the
317006db247cSraghuram  * appropriate lists.
317106db247cSraghuram  */
317206db247cSraghuram static void
vsw_process_ctrl_mcst_pkt(vsw_ldc_t * ldcp,void * pkt)317306db247cSraghuram vsw_process_ctrl_mcst_pkt(vsw_ldc_t *ldcp, void *pkt)
317406db247cSraghuram {
317506db247cSraghuram 	vnet_mcast_msg_t	*mcst_pkt;
317606db247cSraghuram 	vsw_port_t		*port = ldcp->ldc_port;
317706db247cSraghuram 	vsw_t			*vswp = ldcp->ldc_vswp;
317806db247cSraghuram 	int			i;
317906db247cSraghuram 
318006db247cSraghuram 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
318106db247cSraghuram 
318206db247cSraghuram 	/*
318306db247cSraghuram 	 * We know this is a ctrl/mcast packet so
318406db247cSraghuram 	 * cast it into the correct structure.
318506db247cSraghuram 	 */
318606db247cSraghuram 	mcst_pkt = (vnet_mcast_msg_t *)pkt;
318706db247cSraghuram 
318806db247cSraghuram 	switch (mcst_pkt->tag.vio_subtype) {
318906db247cSraghuram 	case VIO_SUBTYPE_INFO:
319006db247cSraghuram 		D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
319106db247cSraghuram 
319206db247cSraghuram 		/*
319306db247cSraghuram 		 * Check if in correct state to receive a multicast
319406db247cSraghuram 		 * message (i.e. handshake complete). If not reset
319506db247cSraghuram 		 * the handshake.
319606db247cSraghuram 		 */
319706db247cSraghuram 		if (vsw_check_flag(ldcp, INBOUND, VSW_MCST_INFO_RECV))
319806db247cSraghuram 			return;
319906db247cSraghuram 
320006db247cSraghuram 		/*
320106db247cSraghuram 		 * Before attempting to add or remove address check
320206db247cSraghuram 		 * that they are valid multicast addresses.
320306db247cSraghuram 		 * If not, then NACK back.
320406db247cSraghuram 		 */
320506db247cSraghuram 		for (i = 0; i < mcst_pkt->count; i++) {
320606db247cSraghuram 			if ((mcst_pkt->mca[i].ether_addr_octet[0] & 01) != 1) {
320706db247cSraghuram 				DERR(vswp, "%s: invalid multicast address",
320806db247cSraghuram 				    __func__);
320906db247cSraghuram 				SND_MCST_NACK(ldcp, mcst_pkt);
321006db247cSraghuram 				return;
321106db247cSraghuram 			}
321206db247cSraghuram 		}
321306db247cSraghuram 
321406db247cSraghuram 		/*
321506db247cSraghuram 		 * Now add/remove the addresses. If this fails we
321606db247cSraghuram 		 * NACK back.
321706db247cSraghuram 		 */
321806db247cSraghuram 		if (vsw_add_rem_mcst(mcst_pkt, port) != 0) {
321906db247cSraghuram 			SND_MCST_NACK(ldcp, mcst_pkt);
322006db247cSraghuram 			return;
322106db247cSraghuram 		}
322206db247cSraghuram 
322306db247cSraghuram 		mcst_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
322406db247cSraghuram 		mcst_pkt->tag.vio_sid = ldcp->local_session;
322506db247cSraghuram 
322606db247cSraghuram 		DUMP_TAG_PTR((vio_msg_tag_t *)mcst_pkt);
322706db247cSraghuram 
322806db247cSraghuram 		(void) vsw_send_msg(ldcp, (void *)mcst_pkt,
322906db247cSraghuram 		    sizeof (vnet_mcast_msg_t), B_TRUE);
323006db247cSraghuram 		break;
323106db247cSraghuram 
323206db247cSraghuram 	case VIO_SUBTYPE_ACK:
323306db247cSraghuram 		DWARN(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
323406db247cSraghuram 
323506db247cSraghuram 		/*
323606db247cSraghuram 		 * We shouldn't ever get a multicast ACK message as
323706db247cSraghuram 		 * at the moment we never request multicast addresses
323806db247cSraghuram 		 * to be set on some other device. This may change in
323906db247cSraghuram 		 * the future if we have cascading switches.
324006db247cSraghuram 		 */
324106db247cSraghuram 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_MCST_ACK_RECV))
324206db247cSraghuram 			return;
324306db247cSraghuram 
324406db247cSraghuram 				/* Do nothing */
324506db247cSraghuram 		break;
324606db247cSraghuram 
324706db247cSraghuram 	case VIO_SUBTYPE_NACK:
324806db247cSraghuram 		DWARN(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
324906db247cSraghuram 
325006db247cSraghuram 		/*
325106db247cSraghuram 		 * We shouldn't get a multicast NACK packet for the
325206db247cSraghuram 		 * same reasons as we shouldn't get a ACK packet.
325306db247cSraghuram 		 */
325406db247cSraghuram 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_MCST_NACK_RECV))
325506db247cSraghuram 			return;
325606db247cSraghuram 
325706db247cSraghuram 				/* Do nothing */
325806db247cSraghuram 		break;
325906db247cSraghuram 
326006db247cSraghuram 	default:
326106db247cSraghuram 		DERR(vswp, "%s: unknown vio_subtype %x\n", __func__,
326206db247cSraghuram 		    mcst_pkt->tag.vio_subtype);
326306db247cSraghuram 	}
326406db247cSraghuram 
326506db247cSraghuram 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
326606db247cSraghuram }
326706db247cSraghuram 
326806db247cSraghuram static void
vsw_process_ctrl_rdx_pkt(vsw_ldc_t * ldcp,void * pkt)326906db247cSraghuram vsw_process_ctrl_rdx_pkt(vsw_ldc_t *ldcp, void *pkt)
32707bd3a2e2SSriharsha Basavapatna {
32717bd3a2e2SSriharsha Basavapatna 	vio_rdx_msg_t	*rdx_pkt;
32727bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
327306db247cSraghuram 
32747bd3a2e2SSriharsha Basavapatna 	/*
32757bd3a2e2SSriharsha Basavapatna 	 * We know this is a ctrl/rdx packet so
32767bd3a2e2SSriharsha Basavapatna 	 * cast it into the correct structure.
32777bd3a2e2SSriharsha Basavapatna 	 */
32787bd3a2e2SSriharsha Basavapatna 	rdx_pkt = (vio_rdx_msg_t *)pkt;
327906db247cSraghuram 
32807bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
328106db247cSraghuram 
32827bd3a2e2SSriharsha Basavapatna 	switch (rdx_pkt->tag.vio_subtype) {
32837bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_INFO:
32847bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
3285bbfa0259Sha 
32867bd3a2e2SSriharsha Basavapatna 		if (vsw_check_flag(ldcp, OUTBOUND, VSW_RDX_INFO_RECV))
32877bd3a2e2SSriharsha Basavapatna 			return;
3288bbfa0259Sha 
32897bd3a2e2SSriharsha Basavapatna 		rdx_pkt->tag.vio_sid = ldcp->local_session;
32907bd3a2e2SSriharsha Basavapatna 		rdx_pkt->tag.vio_subtype = VIO_SUBTYPE_ACK;
329106db247cSraghuram 
32927bd3a2e2SSriharsha Basavapatna 		DUMP_TAG_PTR((vio_msg_tag_t *)rdx_pkt);
329306db247cSraghuram 
32947bd3a2e2SSriharsha Basavapatna 		ldcp->lane_out.lstate |= VSW_RDX_ACK_SENT;
329506db247cSraghuram 
32967bd3a2e2SSriharsha Basavapatna 		(void) vsw_send_msg(ldcp, (void *)rdx_pkt,
32977bd3a2e2SSriharsha Basavapatna 		    sizeof (vio_rdx_msg_t), B_TRUE);
329806db247cSraghuram 
32997bd3a2e2SSriharsha Basavapatna 		vsw_next_milestone(ldcp);
33007bd3a2e2SSriharsha Basavapatna 		break;
330106db247cSraghuram 
33027bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_ACK:
330306db247cSraghuram 		/*
33047bd3a2e2SSriharsha Basavapatna 		 * Should be handled in-band by callback handler.
330506db247cSraghuram 		 */
33067bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: Unexpected VIO_SUBTYPE_ACK", __func__);
33077bd3a2e2SSriharsha Basavapatna 		vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
33087bd3a2e2SSriharsha Basavapatna 		break;
330906db247cSraghuram 
33107bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_NACK:
33117bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
331206db247cSraghuram 
33137bd3a2e2SSriharsha Basavapatna 		if (vsw_check_flag(ldcp, INBOUND, VSW_RDX_NACK_RECV))
33147bd3a2e2SSriharsha Basavapatna 			return;
331506db247cSraghuram 
33167bd3a2e2SSriharsha Basavapatna 		ldcp->lane_in.lstate |= VSW_RDX_NACK_RECV;
33177bd3a2e2SSriharsha Basavapatna 		vsw_next_milestone(ldcp);
331806db247cSraghuram 		break;
331906db247cSraghuram 
33207bd3a2e2SSriharsha Basavapatna 	default:
33217bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
33227bd3a2e2SSriharsha Basavapatna 		    rdx_pkt->tag.vio_subtype);
33237bd3a2e2SSriharsha Basavapatna 	}
332406db247cSraghuram 
33257bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
33267bd3a2e2SSriharsha Basavapatna }
332706db247cSraghuram 
33287bd3a2e2SSriharsha Basavapatna static void
vsw_process_physlink_msg(vsw_ldc_t * ldcp,void * pkt)33297bd3a2e2SSriharsha Basavapatna vsw_process_physlink_msg(vsw_ldc_t *ldcp, void *pkt)
33307bd3a2e2SSriharsha Basavapatna {
33317bd3a2e2SSriharsha Basavapatna 	vnet_physlink_msg_t	*msgp;
33327bd3a2e2SSriharsha Basavapatna 	vsw_t			*vswp = ldcp->ldc_vswp;
333306db247cSraghuram 
33347bd3a2e2SSriharsha Basavapatna 	msgp = (vnet_physlink_msg_t *)pkt;
333506db247cSraghuram 
33367bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld) enter", __func__, ldcp->ldc_id);
333706db247cSraghuram 
33387bd3a2e2SSriharsha Basavapatna 	switch (msgp->tag.vio_subtype) {
33397bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_INFO:
334006db247cSraghuram 
33417bd3a2e2SSriharsha Basavapatna 		/* vsw shouldn't recv physlink info */
33427bd3a2e2SSriharsha Basavapatna 		DWARN(vswp, "%s: Unexpected VIO_SUBTYPE_INFO", __func__);
33437bd3a2e2SSriharsha Basavapatna 		break;
334406db247cSraghuram 
33457bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_ACK:
334606db247cSraghuram 
33477bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
33487bd3a2e2SSriharsha Basavapatna 		break;
334906db247cSraghuram 
33507bd3a2e2SSriharsha Basavapatna 	case VIO_SUBTYPE_NACK:
335106db247cSraghuram 
33527bd3a2e2SSriharsha Basavapatna 		D2(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
33537bd3a2e2SSriharsha Basavapatna 		break;
335406db247cSraghuram 
33557bd3a2e2SSriharsha Basavapatna 	default:
33567bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: Unknown vio_subtype %x\n", __func__,
33577bd3a2e2SSriharsha Basavapatna 		    msgp->tag.vio_subtype);
33587bd3a2e2SSriharsha Basavapatna 	}
335906db247cSraghuram 
33607bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
33617bd3a2e2SSriharsha Basavapatna }
336206db247cSraghuram 
33637bd3a2e2SSriharsha Basavapatna static void
vsw_process_data_pkt(vsw_ldc_t * ldcp,void * dpkt,vio_msg_tag_t * tagp,uint32_t msglen)33647bd3a2e2SSriharsha Basavapatna vsw_process_data_pkt(vsw_ldc_t *ldcp, void *dpkt, vio_msg_tag_t *tagp,
3365*6e472272SToomas Soome     uint32_t msglen)
33667bd3a2e2SSriharsha Basavapatna {
33677bd3a2e2SSriharsha Basavapatna 	uint16_t	env = tagp->vio_subtype_env;
33687bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
33697bd3a2e2SSriharsha Basavapatna 	lane_t		*lp = &ldcp->lane_out;
33707bd3a2e2SSriharsha Basavapatna 	uint8_t		dring_mode = lp->dring_mode;
337106db247cSraghuram 
33727bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
337306db247cSraghuram 
33747bd3a2e2SSriharsha Basavapatna 	/* session id check */
33757bd3a2e2SSriharsha Basavapatna 	if (ldcp->session_status & VSW_PEER_SESSION) {
33767bd3a2e2SSriharsha Basavapatna 		if (ldcp->peer_session != tagp->vio_sid) {
33777bd3a2e2SSriharsha Basavapatna 			DERR(vswp, "%s (chan %d): invalid session id (%llx)",
33787bd3a2e2SSriharsha Basavapatna 			    __func__, ldcp->ldc_id, tagp->vio_sid);
33797bd3a2e2SSriharsha Basavapatna 			vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
33807bd3a2e2SSriharsha Basavapatna 			return;
33817bd3a2e2SSriharsha Basavapatna 		}
33827bd3a2e2SSriharsha Basavapatna 	}
338306db247cSraghuram 
33847bd3a2e2SSriharsha Basavapatna 	/*
33857bd3a2e2SSriharsha Basavapatna 	 * It is an error for us to be getting data packets
33867bd3a2e2SSriharsha Basavapatna 	 * before the handshake has completed.
33877bd3a2e2SSriharsha Basavapatna 	 */
33887bd3a2e2SSriharsha Basavapatna 	if (ldcp->hphase != VSW_MILESTONE4) {
33897bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: got data packet before handshake complete "
33907bd3a2e2SSriharsha Basavapatna 		    "hphase %d (%x: %x)", __func__, ldcp->hphase,
33917bd3a2e2SSriharsha Basavapatna 		    ldcp->lane_in.lstate, ldcp->lane_out.lstate);
33927bd3a2e2SSriharsha Basavapatna 		DUMP_FLAGS(ldcp->lane_in.lstate);
33937bd3a2e2SSriharsha Basavapatna 		DUMP_FLAGS(ldcp->lane_out.lstate);
33947bd3a2e2SSriharsha Basavapatna 		vsw_process_conn_evt(ldcp, VSW_CONN_RESTART);
33957bd3a2e2SSriharsha Basavapatna 		return;
33967bd3a2e2SSriharsha Basavapatna 	}
33977bd3a2e2SSriharsha Basavapatna 	if (dring_mode == VIO_TX_DRING) {
339806db247cSraghuram 		/*
33997bd3a2e2SSriharsha Basavapatna 		 * To reduce the locking contention, release the ldc_cblock
34007bd3a2e2SSriharsha Basavapatna 		 * here and re-acquire it once we are done receiving packets.
34017bd3a2e2SSriharsha Basavapatna 		 * We do this only in TxDring mode to allow further callbaks to
34027bd3a2e2SSriharsha Basavapatna 		 * continue while the msg worker thread processes the messages.
34037bd3a2e2SSriharsha Basavapatna 		 * In RxDringData mode, we process the messages in the callback
34047bd3a2e2SSriharsha Basavapatna 		 * itself and wake up rcv worker thread to process only data
34057bd3a2e2SSriharsha Basavapatna 		 * info messages.
340606db247cSraghuram 		 */
34077bd3a2e2SSriharsha Basavapatna 		mutex_exit(&ldcp->ldc_cblock);
34087bd3a2e2SSriharsha Basavapatna 		mutex_enter(&ldcp->ldc_rxlock);
34097bd3a2e2SSriharsha Basavapatna 	}
341006db247cSraghuram 
34117bd3a2e2SSriharsha Basavapatna 	/*
34127bd3a2e2SSriharsha Basavapatna 	 * Switch on vio_subtype envelope, then let lower routines
34137bd3a2e2SSriharsha Basavapatna 	 * decide if its an INFO, ACK or NACK packet.
34147bd3a2e2SSriharsha Basavapatna 	 */
34157bd3a2e2SSriharsha Basavapatna 	if (env == VIO_DRING_DATA) {
34167bd3a2e2SSriharsha Basavapatna 		ldcp->rx_dringdata(ldcp, dpkt);
34177bd3a2e2SSriharsha Basavapatna 	} else if (env == VIO_PKT_DATA) {
34187bd3a2e2SSriharsha Basavapatna 		ldcp->rx_pktdata(ldcp, dpkt, msglen);
34197bd3a2e2SSriharsha Basavapatna 	} else if (env == VIO_DESC_DATA) {
34207bd3a2e2SSriharsha Basavapatna 		vsw_process_data_ibnd_pkt(ldcp, dpkt);
34217bd3a2e2SSriharsha Basavapatna 	} else {
34227bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: unknown vio_subtype_env (%x)\n",
34237bd3a2e2SSriharsha Basavapatna 		    __func__, env);
34247bd3a2e2SSriharsha Basavapatna 	}
342506db247cSraghuram 
34267bd3a2e2SSriharsha Basavapatna 	if (dring_mode == VIO_TX_DRING) {
34277bd3a2e2SSriharsha Basavapatna 		mutex_exit(&ldcp->ldc_rxlock);
34287bd3a2e2SSriharsha Basavapatna 		mutex_enter(&ldcp->ldc_cblock);
342906db247cSraghuram 	}
343006db247cSraghuram 
34317bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
343206db247cSraghuram }
343306db247cSraghuram 
343406db247cSraghuram /*
3435f0ca1d9aSsb  * dummy pkt data handler function for vnet protocol version 1.0
3436f0ca1d9aSsb  */
3437f0ca1d9aSsb static void
vsw_process_pkt_data_nop(void * arg1,void * arg2,uint32_t msglen)3438f0ca1d9aSsb vsw_process_pkt_data_nop(void *arg1, void *arg2, uint32_t msglen)
3439f0ca1d9aSsb {
3440f0ca1d9aSsb 	_NOTE(ARGUNUSED(arg1, arg2, msglen))
3441f0ca1d9aSsb }
3442f0ca1d9aSsb 
3443f0ca1d9aSsb /*
3444f0ca1d9aSsb  * This function handles raw pkt data messages received over the channel.
3445f0ca1d9aSsb  * Currently, only priority-eth-type frames are received through this mechanism.
3446f0ca1d9aSsb  * In this case, the frame(data) is present within the message itself which
3447f0ca1d9aSsb  * is copied into an mblk before switching it.
344806db247cSraghuram  */
344906db247cSraghuram static void
vsw_process_pkt_data(void * arg1,void * arg2,uint32_t msglen)3450f0ca1d9aSsb vsw_process_pkt_data(void *arg1, void *arg2, uint32_t msglen)
345106db247cSraghuram {
3452f0ca1d9aSsb 	vsw_ldc_t		*ldcp = (vsw_ldc_t *)arg1;
3453f0ca1d9aSsb 	vio_raw_data_msg_t	*dpkt = (vio_raw_data_msg_t *)arg2;
3454f0ca1d9aSsb 	uint32_t		size;
3455f0ca1d9aSsb 	mblk_t			*mp;
34567bd3a2e2SSriharsha Basavapatna 	vio_mblk_t		*vmp;
3457f0ca1d9aSsb 	vsw_t			*vswp = ldcp->ldc_vswp;
3458f0ca1d9aSsb 	vgen_stats_t		*statsp = &ldcp->ldc_stats;
3459c1c61f44Ssb 	lane_t			*lp = &ldcp->lane_out;
3460f0ca1d9aSsb 
3461f0ca1d9aSsb 	size = msglen - VIO_PKT_DATA_HDRSIZE;
3462c1c61f44Ssb 	if (size < ETHERMIN || size > lp->mtu) {
3463f0ca1d9aSsb 		(void) atomic_inc_32(&statsp->rx_pri_fail);
3464f0ca1d9aSsb 		DWARN(vswp, "%s(%lld) invalid size(%d)\n", __func__,
3465f0ca1d9aSsb 		    ldcp->ldc_id, size);
3466f0ca1d9aSsb 		return;
3467f0ca1d9aSsb 	}
3468f0ca1d9aSsb 
34697bd3a2e2SSriharsha Basavapatna 	vmp = vio_multipool_allocb(&ldcp->vmp, size + VLAN_TAGSZ);
34707bd3a2e2SSriharsha Basavapatna 	if (vmp == NULL) {
3471c1c61f44Ssb 		mp = allocb(size + VLAN_TAGSZ, BPRI_MED);
3472f0ca1d9aSsb 		if (mp == NULL) {
3473f0ca1d9aSsb 			(void) atomic_inc_32(&statsp->rx_pri_fail);
3474f0ca1d9aSsb 			DWARN(vswp, "%s(%lld) allocb failure, "
3475f0ca1d9aSsb 			    "unable to process priority frame\n", __func__,
3476f0ca1d9aSsb 			    ldcp->ldc_id);
3477f0ca1d9aSsb 			return;
3478f0ca1d9aSsb 		}
34797bd3a2e2SSriharsha Basavapatna 	} else {
34807bd3a2e2SSriharsha Basavapatna 		mp = vmp->mp;
3481f0ca1d9aSsb 	}
3482f0ca1d9aSsb 
3483c1c61f44Ssb 	/* skip over the extra space for vlan tag */
3484c1c61f44Ssb 	mp->b_rptr += VLAN_TAGSZ;
3485c1c61f44Ssb 
3486f0ca1d9aSsb 	/* copy the frame from the payload of raw data msg into the mblk */
3487f0ca1d9aSsb 	bcopy(dpkt->data, mp->b_rptr, size);
3488f0ca1d9aSsb 	mp->b_wptr = mp->b_rptr + size;
348906db247cSraghuram 
34907bd3a2e2SSriharsha Basavapatna 	if (vmp != NULL) {
34917bd3a2e2SSriharsha Basavapatna 		vmp->state = VIO_MBLK_HAS_DATA;
34927bd3a2e2SSriharsha Basavapatna 	}
34937bd3a2e2SSriharsha Basavapatna 
3494f0ca1d9aSsb 	/* update stats */
3495f0ca1d9aSsb 	(void) atomic_inc_64(&statsp->rx_pri_packets);
3496f0ca1d9aSsb 	(void) atomic_add_64(&statsp->rx_pri_bytes, size);
3497f0ca1d9aSsb 
3498c1c61f44Ssb 	/*
3499c1c61f44Ssb 	 * VLAN_TAGSZ of extra space has been pre-alloc'd if tag is needed.
3500c1c61f44Ssb 	 */
3501c1c61f44Ssb 	(void) vsw_vlan_frame_pretag(ldcp->ldc_port, VSW_VNETPORT, mp);
3502c1c61f44Ssb 
3503f0ca1d9aSsb 	/* switch the frame to destination */
3504f0ca1d9aSsb 	vswp->vsw_switch_frame(vswp, mp, VSW_VNETPORT, ldcp->ldc_port, NULL);
350506db247cSraghuram }
350606db247cSraghuram 
350706db247cSraghuram /*
350806db247cSraghuram  * Process an in-band descriptor message (most likely from
350906db247cSraghuram  * OBP).
351006db247cSraghuram  */
351106db247cSraghuram static void
vsw_process_data_ibnd_pkt(vsw_ldc_t * ldcp,void * pkt)351206db247cSraghuram vsw_process_data_ibnd_pkt(vsw_ldc_t *ldcp, void *pkt)
351306db247cSraghuram {
351406db247cSraghuram 	vnet_ibnd_desc_t	*ibnd_desc;
351506db247cSraghuram 	dring_info_t		*dp = NULL;
351606db247cSraghuram 	vsw_private_desc_t	*priv_addr = NULL;
351706db247cSraghuram 	vsw_t			*vswp = ldcp->ldc_vswp;
351806db247cSraghuram 	mblk_t			*mp = NULL;
351906db247cSraghuram 	size_t			nbytes = 0;
352006db247cSraghuram 	size_t			off = 0;
352106db247cSraghuram 	uint64_t		idx = 0;
352206db247cSraghuram 	uint32_t		num = 1, len, datalen = 0;
352306db247cSraghuram 	uint64_t		ncookies = 0;
352406db247cSraghuram 	int			i, rv;
352506db247cSraghuram 	int			j = 0;
352606db247cSraghuram 
352706db247cSraghuram 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
352806db247cSraghuram 
352906db247cSraghuram 	ibnd_desc = (vnet_ibnd_desc_t *)pkt;
353006db247cSraghuram 
353106db247cSraghuram 	switch (ibnd_desc->hdr.tag.vio_subtype) {
353206db247cSraghuram 	case VIO_SUBTYPE_INFO:
353306db247cSraghuram 		D1(vswp, "%s: VIO_SUBTYPE_INFO", __func__);
353406db247cSraghuram 
353506db247cSraghuram 		if (vsw_check_flag(ldcp, INBOUND, VSW_DRING_INFO_RECV))
353606db247cSraghuram 			return;
353706db247cSraghuram 
353806db247cSraghuram 		/*
353906db247cSraghuram 		 * Data is padded to align on a 8 byte boundary,
354006db247cSraghuram 		 * nbytes is actual data length, i.e. minus that
354106db247cSraghuram 		 * padding.
354206db247cSraghuram 		 */
354306db247cSraghuram 		datalen = ibnd_desc->nbytes;
354406db247cSraghuram 
354506db247cSraghuram 		D2(vswp, "%s(%lld): processing inband desc : "
354606db247cSraghuram 		    ": datalen 0x%lx", __func__, ldcp->ldc_id, datalen);
354706db247cSraghuram 
354806db247cSraghuram 		ncookies = ibnd_desc->ncookies;
354906db247cSraghuram 
355006db247cSraghuram 		/*
355106db247cSraghuram 		 * allocb(9F) returns an aligned data block. We
355206db247cSraghuram 		 * need to ensure that we ask ldc for an aligned
355306db247cSraghuram 		 * number of bytes also.
355406db247cSraghuram 		 */
355506db247cSraghuram 		nbytes = datalen;
355606db247cSraghuram 		if (nbytes & 0x7) {
355706db247cSraghuram 			off = 8 - (nbytes & 0x7);
355806db247cSraghuram 			nbytes += off;
355906db247cSraghuram 		}
356006db247cSraghuram 
3561c1c61f44Ssb 		/* alloc extra space for VLAN_TAG */
3562c1c61f44Ssb 		mp = allocb(datalen + 8, BPRI_MED);
356306db247cSraghuram 		if (mp == NULL) {
356406db247cSraghuram 			DERR(vswp, "%s(%lld): allocb failed",
356506db247cSraghuram 			    __func__, ldcp->ldc_id);
356606db247cSraghuram 			ldcp->ldc_stats.rx_allocb_fail++;
356706db247cSraghuram 			return;
356806db247cSraghuram 		}
356906db247cSraghuram 
3570c1c61f44Ssb 		/* skip over the extra space for VLAN_TAG */
3571c1c61f44Ssb 		mp->b_rptr += 8;
3572c1c61f44Ssb 
357306db247cSraghuram 		rv = ldc_mem_copy(ldcp->ldc_handle, (caddr_t)mp->b_rptr,
357406db247cSraghuram 		    0, &nbytes, ibnd_desc->memcookie, (uint64_t)ncookies,
357506db247cSraghuram 		    LDC_COPY_IN);
357606db247cSraghuram 
357706db247cSraghuram 		if (rv != 0) {
357806db247cSraghuram 			DERR(vswp, "%s(%d): unable to copy in data from "
357906db247cSraghuram 			    "%d cookie(s)", __func__, ldcp->ldc_id, ncookies);
358006db247cSraghuram 			freemsg(mp);
358106db247cSraghuram 			ldcp->ldc_stats.ierrors++;
358206db247cSraghuram 			return;
358306db247cSraghuram 		}
358406db247cSraghuram 
358506db247cSraghuram 		D2(vswp, "%s(%d): copied in %ld bytes using %d cookies",
358606db247cSraghuram 		    __func__, ldcp->ldc_id, nbytes, ncookies);
358706db247cSraghuram 
358806db247cSraghuram 		/* point to the actual end of data */
358906db247cSraghuram 		mp->b_wptr = mp->b_rptr + datalen;
359006db247cSraghuram 		ldcp->ldc_stats.ipackets++;
359106db247cSraghuram 		ldcp->ldc_stats.rbytes += datalen;
359206db247cSraghuram 
359306db247cSraghuram 		/*
359406db247cSraghuram 		 * We ACK back every in-band descriptor message we process
359506db247cSraghuram 		 */
359606db247cSraghuram 		ibnd_desc->hdr.tag.vio_subtype = VIO_SUBTYPE_ACK;
359706db247cSraghuram 		ibnd_desc->hdr.tag.vio_sid = ldcp->local_session;
359806db247cSraghuram 		(void) vsw_send_msg(ldcp, (void *)ibnd_desc,
359906db247cSraghuram 		    sizeof (vnet_ibnd_desc_t), B_TRUE);
360006db247cSraghuram 
3601c1c61f44Ssb 		/*
3602c1c61f44Ssb 		 * there is extra space alloc'd for VLAN_TAG
3603c1c61f44Ssb 		 */
3604c1c61f44Ssb 		(void) vsw_vlan_frame_pretag(ldcp->ldc_port, VSW_VNETPORT, mp);
3605c1c61f44Ssb 
360606db247cSraghuram 		/* send the packet to be switched */
360706db247cSraghuram 		vswp->vsw_switch_frame(vswp, mp, VSW_VNETPORT,
360806db247cSraghuram 		    ldcp->ldc_port, NULL);
360906db247cSraghuram 
361006db247cSraghuram 		break;
361106db247cSraghuram 
361206db247cSraghuram 	case VIO_SUBTYPE_ACK:
361306db247cSraghuram 		D1(vswp, "%s: VIO_SUBTYPE_ACK", __func__);
361406db247cSraghuram 
361506db247cSraghuram 		/* Verify the ACK is valid */
361606db247cSraghuram 		idx = ibnd_desc->hdr.desc_handle;
361706db247cSraghuram 
36187bd3a2e2SSriharsha Basavapatna 		if (idx >= vsw_num_descriptors) {
361906db247cSraghuram 			cmn_err(CE_WARN, "!vsw%d: corrupted ACK received "
362006db247cSraghuram 			    "(idx %ld)", vswp->instance, idx);
362106db247cSraghuram 			return;
362206db247cSraghuram 		}
362306db247cSraghuram 
362406db247cSraghuram 		if ((dp = ldcp->lane_out.dringp) == NULL) {
362506db247cSraghuram 			DERR(vswp, "%s: no dring found", __func__);
362606db247cSraghuram 			return;
362706db247cSraghuram 		}
362806db247cSraghuram 
362906db247cSraghuram 		len = dp->num_descriptors;
363006db247cSraghuram 		/*
363106db247cSraghuram 		 * If the descriptor we are being ACK'ed for is not the
363206db247cSraghuram 		 * one we expected, then pkts were lost somwhere, either
363306db247cSraghuram 		 * when we tried to send a msg, or a previous ACK msg from
363406db247cSraghuram 		 * our peer. In either case we now reclaim the descriptors
363506db247cSraghuram 		 * in the range from the last ACK we received up to the
363606db247cSraghuram 		 * current ACK.
363706db247cSraghuram 		 */
363806db247cSraghuram 		if (idx != dp->last_ack_recv) {
363906db247cSraghuram 			DWARN(vswp, "%s: dropped pkts detected, (%ld, %ld)",
364006db247cSraghuram 			    __func__, dp->last_ack_recv, idx);
364106db247cSraghuram 			num = idx >= dp->last_ack_recv ?
364206db247cSraghuram 			    idx - dp->last_ack_recv + 1:
364306db247cSraghuram 			    (len - dp->last_ack_recv + 1) + idx;
364406db247cSraghuram 		}
364506db247cSraghuram 
364606db247cSraghuram 		/*
364706db247cSraghuram 		 * When we sent the in-band message to our peer we
364806db247cSraghuram 		 * marked the copy in our private ring as READY. We now
364906db247cSraghuram 		 * check that the descriptor we are being ACK'ed for is in
365006db247cSraghuram 		 * fact READY, i.e. it is one we have shared with our peer.
365106db247cSraghuram 		 *
365206db247cSraghuram 		 * If its not we flag an error, but still reset the descr
365306db247cSraghuram 		 * back to FREE.
365406db247cSraghuram 		 */
365506db247cSraghuram 		for (i = dp->last_ack_recv; j < num; i = (i + 1) % len, j++) {
365606db247cSraghuram 			priv_addr = (vsw_private_desc_t *)dp->priv_addr + i;
365706db247cSraghuram 			mutex_enter(&priv_addr->dstate_lock);
365806db247cSraghuram 			if (priv_addr->dstate != VIO_DESC_READY) {
365906db247cSraghuram 				DERR(vswp, "%s: (%ld) desc at index %ld not "
366006db247cSraghuram 				    "READY (0x%lx)", __func__,
366106db247cSraghuram 				    ldcp->ldc_id, idx, priv_addr->dstate);
366206db247cSraghuram 				DERR(vswp, "%s: bound %d: ncookies %ld : "
366306db247cSraghuram 				    "datalen %ld", __func__,
366406db247cSraghuram 				    priv_addr->bound, priv_addr->ncookies,
366506db247cSraghuram 				    priv_addr->datalen);
366606db247cSraghuram 			}
366706db247cSraghuram 			D2(vswp, "%s: (%lld) freeing descp at %lld", __func__,
366806db247cSraghuram 			    ldcp->ldc_id, idx);
366906db247cSraghuram 			/* release resources associated with sent msg */
367006db247cSraghuram 			priv_addr->datalen = 0;
367106db247cSraghuram 			priv_addr->dstate = VIO_DESC_FREE;
367206db247cSraghuram 			mutex_exit(&priv_addr->dstate_lock);
367306db247cSraghuram 		}
367406db247cSraghuram 		/* update to next expected value */
367506db247cSraghuram 		dp->last_ack_recv = (idx + 1) % dp->num_descriptors;
367606db247cSraghuram 
367706db247cSraghuram 		break;
367806db247cSraghuram 
367906db247cSraghuram 	case VIO_SUBTYPE_NACK:
368006db247cSraghuram 		DERR(vswp, "%s: VIO_SUBTYPE_NACK", __func__);
368106db247cSraghuram 
368206db247cSraghuram 		/*
368306db247cSraghuram 		 * We should only get a NACK if our peer doesn't like
368406db247cSraghuram 		 * something about a message we have sent it. If this
368506db247cSraghuram 		 * happens we just release the resources associated with
368606db247cSraghuram 		 * the message. (We are relying on higher layers to decide
368706db247cSraghuram 		 * whether or not to resend.
368806db247cSraghuram 		 */
368906db247cSraghuram 
369006db247cSraghuram 		/* limit check */
369106db247cSraghuram 		idx = ibnd_desc->hdr.desc_handle;
369206db247cSraghuram 
36937bd3a2e2SSriharsha Basavapatna 		if (idx >= vsw_num_descriptors) {
369406db247cSraghuram 			DERR(vswp, "%s: corrupted NACK received (idx %lld)",
369506db247cSraghuram 			    __func__, idx);
369606db247cSraghuram 			return;
369706db247cSraghuram 		}
369806db247cSraghuram 
369906db247cSraghuram 		if ((dp = ldcp->lane_out.dringp) == NULL) {
370006db247cSraghuram 			DERR(vswp, "%s: no dring found", __func__);
370106db247cSraghuram 			return;
370206db247cSraghuram 		}
370306db247cSraghuram 
370406db247cSraghuram 		priv_addr = (vsw_private_desc_t *)dp->priv_addr;
370506db247cSraghuram 
370606db247cSraghuram 		/* move to correct location in ring */
370706db247cSraghuram 		priv_addr += idx;
370806db247cSraghuram 
370906db247cSraghuram 		/* release resources associated with sent msg */
371006db247cSraghuram 		mutex_enter(&priv_addr->dstate_lock);
371106db247cSraghuram 		priv_addr->datalen = 0;
371206db247cSraghuram 		priv_addr->dstate = VIO_DESC_FREE;
371306db247cSraghuram 		mutex_exit(&priv_addr->dstate_lock);
371406db247cSraghuram 
371506db247cSraghuram 		break;
371606db247cSraghuram 
371706db247cSraghuram 	default:
371806db247cSraghuram 		DERR(vswp, "%s(%lld): Unknown vio_subtype %x\n", __func__,
371906db247cSraghuram 		    ldcp->ldc_id, ibnd_desc->hdr.tag.vio_subtype);
372006db247cSraghuram 	}
372106db247cSraghuram 
372206db247cSraghuram 	D1(vswp, "%s(%lld) exit", __func__, ldcp->ldc_id);
372306db247cSraghuram }
372406db247cSraghuram 
372506db247cSraghuram static void
vsw_process_err_pkt(vsw_ldc_t * ldcp,void * epkt,vio_msg_tag_t * tagp)3726f0ca1d9aSsb vsw_process_err_pkt(vsw_ldc_t *ldcp, void *epkt, vio_msg_tag_t *tagp)
372706db247cSraghuram {
372806db247cSraghuram 	_NOTE(ARGUNUSED(epkt))
372906db247cSraghuram 
373006db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
3731f0ca1d9aSsb 	uint16_t	env = tagp->vio_subtype_env;
373206db247cSraghuram 
373306db247cSraghuram 	D1(vswp, "%s (%lld): enter\n", __func__, ldcp->ldc_id);
373406db247cSraghuram 
373506db247cSraghuram 	/*
373606db247cSraghuram 	 * Error vio_subtypes have yet to be defined. So for
373706db247cSraghuram 	 * the moment we can't do anything.
373806db247cSraghuram 	 */
373906db247cSraghuram 	D2(vswp, "%s: (%x) vio_subtype env", __func__, env);
374006db247cSraghuram 
374106db247cSraghuram 	D1(vswp, "%s (%lld): exit\n", __func__, ldcp->ldc_id);
374206db247cSraghuram }
374306db247cSraghuram 
374406db247cSraghuram /* transmit the packet over the given port */
374506db247cSraghuram int
vsw_portsend(vsw_port_t * port,mblk_t * mp)3746da14cebeSEric Cheng vsw_portsend(vsw_port_t *port, mblk_t *mp)
374706db247cSraghuram {
3748da14cebeSEric Cheng 	mblk_t		*mpt;
3749da14cebeSEric Cheng 	int		count;
3750*6e472272SToomas Soome 	vsw_ldc_t	*ldcp = port->ldcp;
375106db247cSraghuram 	int		status = 0;
375206db247cSraghuram 
3753da14cebeSEric Cheng 	count = vsw_vlan_frame_untag(port, VSW_VNETPORT, &mp, &mpt);
3754da14cebeSEric Cheng 	if (count != 0) {
3755da14cebeSEric Cheng 		status = ldcp->tx(ldcp, mp, mpt, count);
3756c1c61f44Ssb 	}
3757f0ca1d9aSsb 	return (status);
3758f0ca1d9aSsb }
3759f0ca1d9aSsb 
3760f0ca1d9aSsb /*
3761f0ca1d9aSsb  * Break up frames into 2 seperate chains: normal and
3762f0ca1d9aSsb  * priority, based on the frame type. The number of
3763f0ca1d9aSsb  * priority frames is also counted and returned.
3764f0ca1d9aSsb  *
3765f0ca1d9aSsb  * Params:
3766*6e472272SToomas Soome  *	vswp:	pointer to the instance of vsw
3767f0ca1d9aSsb  *	np:	head of packet chain to be broken
3768f0ca1d9aSsb  *	npt:	tail of packet chain to be broken
3769f0ca1d9aSsb  *
3770f0ca1d9aSsb  * Returns:
3771f0ca1d9aSsb  *	np:	head of normal data packets
3772f0ca1d9aSsb  *	npt:	tail of normal data packets
3773f0ca1d9aSsb  *	hp:	head of high priority packets
3774f0ca1d9aSsb  *	hpt:	tail of high priority packets
3775f0ca1d9aSsb  */
3776f0ca1d9aSsb static uint32_t
vsw_get_pri_packets(vsw_t * vswp,mblk_t ** np,mblk_t ** npt,mblk_t ** hp,mblk_t ** hpt)3777f0ca1d9aSsb vsw_get_pri_packets(vsw_t *vswp, mblk_t **np, mblk_t **npt,
3778*6e472272SToomas Soome     mblk_t **hp, mblk_t **hpt)
3779f0ca1d9aSsb {
3780f0ca1d9aSsb 	mblk_t			*tmp = NULL;
3781f0ca1d9aSsb 	mblk_t			*smp = NULL;
3782f0ca1d9aSsb 	mblk_t			*hmp = NULL;	/* high prio pkts head */
3783f0ca1d9aSsb 	mblk_t			*hmpt = NULL;	/* high prio pkts tail */
3784f0ca1d9aSsb 	mblk_t			*nmp = NULL;	/* normal pkts head */
3785f0ca1d9aSsb 	mblk_t			*nmpt = NULL;	/* normal pkts tail */
3786f0ca1d9aSsb 	uint32_t		count = 0;
3787f0ca1d9aSsb 	int			i;
3788f0ca1d9aSsb 	struct ether_header	*ehp;
3789f0ca1d9aSsb 	uint32_t		num_types;
3790f0ca1d9aSsb 	uint16_t		*types;
3791f0ca1d9aSsb 
3792f0ca1d9aSsb 	tmp = *np;
3793f0ca1d9aSsb 	while (tmp != NULL) {
3794f0ca1d9aSsb 
3795f0ca1d9aSsb 		smp = tmp;
3796f0ca1d9aSsb 		tmp = tmp->b_next;
3797f0ca1d9aSsb 		smp->b_next = NULL;
3798f0ca1d9aSsb 		smp->b_prev = NULL;
3799f0ca1d9aSsb 
3800f0ca1d9aSsb 		ehp = (struct ether_header *)smp->b_rptr;
3801f0ca1d9aSsb 		num_types = vswp->pri_num_types;
3802f0ca1d9aSsb 		types = vswp->pri_types;
3803f0ca1d9aSsb 		for (i = 0; i < num_types; i++) {
3804f0ca1d9aSsb 			if (ehp->ether_type == types[i]) {
3805f0ca1d9aSsb 				/* high priority frame */
3806f0ca1d9aSsb 
3807f0ca1d9aSsb 				if (hmp != NULL) {
3808f0ca1d9aSsb 					hmpt->b_next = smp;
3809f0ca1d9aSsb 					hmpt = smp;
3810f0ca1d9aSsb 				} else {
3811f0ca1d9aSsb 					hmp = hmpt = smp;
3812f0ca1d9aSsb 				}
3813f0ca1d9aSsb 				count++;
3814f0ca1d9aSsb 				break;
3815f0ca1d9aSsb 			}
3816f0ca1d9aSsb 		}
3817f0ca1d9aSsb 		if (i == num_types) {
3818f0ca1d9aSsb 			/* normal data frame */
3819f0ca1d9aSsb 
3820f0ca1d9aSsb 			if (nmp != NULL) {
3821f0ca1d9aSsb 				nmpt->b_next = smp;
3822f0ca1d9aSsb 				nmpt = smp;
3823f0ca1d9aSsb 			} else {
3824f0ca1d9aSsb 				nmp = nmpt = smp;
3825f0ca1d9aSsb 			}
3826f0ca1d9aSsb 		}
3827f0ca1d9aSsb 	}
3828f0ca1d9aSsb 
3829f0ca1d9aSsb 	*hp = hmp;
3830f0ca1d9aSsb 	*hpt = hmpt;
3831f0ca1d9aSsb 	*np = nmp;
3832f0ca1d9aSsb 	*npt = nmpt;
3833f0ca1d9aSsb 
3834f0ca1d9aSsb 	return (count);
3835f0ca1d9aSsb }
3836f0ca1d9aSsb 
3837f0ca1d9aSsb /*
3838f0ca1d9aSsb  * Wrapper function to transmit normal and/or priority frames over the channel.
3839f0ca1d9aSsb  */
3840f0ca1d9aSsb static int
vsw_ldctx_pri(void * arg,mblk_t * mp,mblk_t * mpt,uint32_t count)3841f0ca1d9aSsb vsw_ldctx_pri(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count)
3842f0ca1d9aSsb {
3843*6e472272SToomas Soome 	vsw_ldc_t		*ldcp = (vsw_ldc_t *)arg;
3844f0ca1d9aSsb 	mblk_t			*tmp;
3845f0ca1d9aSsb 	mblk_t			*smp;
3846f0ca1d9aSsb 	mblk_t			*hmp;	/* high prio pkts head */
3847f0ca1d9aSsb 	mblk_t			*hmpt;	/* high prio pkts tail */
3848f0ca1d9aSsb 	mblk_t			*nmp;	/* normal pkts head */
3849f0ca1d9aSsb 	mblk_t			*nmpt;	/* normal pkts tail */
3850f0ca1d9aSsb 	uint32_t		n = 0;
3851f0ca1d9aSsb 	vsw_t			*vswp = ldcp->ldc_vswp;
3852f0ca1d9aSsb 
3853f0ca1d9aSsb 	ASSERT(VSW_PRI_ETH_DEFINED(vswp));
3854f0ca1d9aSsb 	ASSERT(count != 0);
3855f0ca1d9aSsb 
3856f0ca1d9aSsb 	nmp = mp;
3857f0ca1d9aSsb 	nmpt = mpt;
3858f0ca1d9aSsb 
3859f0ca1d9aSsb 	/* gather any priority frames from the chain of packets */
3860f0ca1d9aSsb 	n = vsw_get_pri_packets(vswp, &nmp, &nmpt, &hmp, &hmpt);
3861f0ca1d9aSsb 
3862f0ca1d9aSsb 	/* transmit priority frames */
3863f0ca1d9aSsb 	tmp = hmp;
3864f0ca1d9aSsb 	while (tmp != NULL) {
3865f0ca1d9aSsb 		smp = tmp;
3866f0ca1d9aSsb 		tmp = tmp->b_next;
3867f0ca1d9aSsb 		smp->b_next = NULL;
3868f0ca1d9aSsb 		vsw_ldcsend_pkt(ldcp, smp);
3869f0ca1d9aSsb 	}
3870f0ca1d9aSsb 
3871f0ca1d9aSsb 	count -= n;
3872f0ca1d9aSsb 
3873f0ca1d9aSsb 	if (count == 0) {
3874f0ca1d9aSsb 		/* no normal data frames to process */
3875f0ca1d9aSsb 		return (0);
3876f0ca1d9aSsb 	}
3877f0ca1d9aSsb 
3878f0ca1d9aSsb 	return (vsw_ldctx(ldcp, nmp, nmpt, count));
3879f0ca1d9aSsb }
3880f0ca1d9aSsb 
3881f0ca1d9aSsb /*
3882f0ca1d9aSsb  * Wrapper function to transmit normal frames over the channel.
3883f0ca1d9aSsb  */
3884f0ca1d9aSsb static int
vsw_ldctx(void * arg,mblk_t * mp,mblk_t * mpt,uint32_t count)3885f0ca1d9aSsb vsw_ldctx(void *arg, mblk_t *mp, mblk_t *mpt, uint32_t count)
3886f0ca1d9aSsb {
3887*6e472272SToomas Soome 	vsw_ldc_t	*ldcp = (vsw_ldc_t *)arg;
3888f0ca1d9aSsb 	mblk_t		*tmp = NULL;
3889f0ca1d9aSsb 
3890f0ca1d9aSsb 	ASSERT(count != 0);
389106db247cSraghuram 	/*
3892f0ca1d9aSsb 	 * If the TX thread is enabled, then queue the
3893f0ca1d9aSsb 	 * ordinary frames and signal the tx thread.
389406db247cSraghuram 	 */
389506db247cSraghuram 	if (ldcp->tx_thread != NULL) {
3896f0ca1d9aSsb 
389706db247cSraghuram 		mutex_enter(&ldcp->tx_thr_lock);
3898f0ca1d9aSsb 
3899f0ca1d9aSsb 		if ((ldcp->tx_cnt + count) >= vsw_max_tx_qcount) {
3900f0ca1d9aSsb 			/*
3901f0ca1d9aSsb 			 * If we reached queue limit,
3902f0ca1d9aSsb 			 * do not queue new packets,
3903f0ca1d9aSsb 			 * drop them.
3904f0ca1d9aSsb 			 */
3905f0ca1d9aSsb 			ldcp->ldc_stats.tx_qfull += count;
3906f0ca1d9aSsb 			mutex_exit(&ldcp->tx_thr_lock);
3907f0ca1d9aSsb 			freemsgchain(mp);
3908f0ca1d9aSsb 			goto exit;
3909f0ca1d9aSsb 		}
391006db247cSraghuram 		if (ldcp->tx_mhead == NULL) {
391106db247cSraghuram 			ldcp->tx_mhead = mp;
391206db247cSraghuram 			ldcp->tx_mtail = mpt;
391306db247cSraghuram 			cv_signal(&ldcp->tx_thr_cv);
391406db247cSraghuram 		} else {
391506db247cSraghuram 			ldcp->tx_mtail->b_next = mp;
391606db247cSraghuram 			ldcp->tx_mtail = mpt;
391706db247cSraghuram 		}
3918f0ca1d9aSsb 		ldcp->tx_cnt += count;
391906db247cSraghuram 		mutex_exit(&ldcp->tx_thr_lock);
392006db247cSraghuram 	} else {
392106db247cSraghuram 		while (mp != NULL) {
392206db247cSraghuram 			tmp = mp->b_next;
392306db247cSraghuram 			mp->b_next = mp->b_prev = NULL;
392406db247cSraghuram 			(void) vsw_ldcsend(ldcp, mp, 1);
392506db247cSraghuram 			mp = tmp;
392606db247cSraghuram 		}
392706db247cSraghuram 	}
392806db247cSraghuram 
3929f0ca1d9aSsb exit:
3930f0ca1d9aSsb 	return (0);
3931f0ca1d9aSsb }
393206db247cSraghuram 
3933f0ca1d9aSsb /*
3934f0ca1d9aSsb  * This function transmits the frame in the payload of a raw data
3935f0ca1d9aSsb  * (VIO_PKT_DATA) message. Thus, it provides an Out-Of-Band path to
3936f0ca1d9aSsb  * send special frames with high priorities, without going through
3937f0ca1d9aSsb  * the normal data path which uses descriptor ring mechanism.
3938f0ca1d9aSsb  */
3939f0ca1d9aSsb static void
vsw_ldcsend_pkt(vsw_ldc_t * ldcp,mblk_t * mp)3940f0ca1d9aSsb vsw_ldcsend_pkt(vsw_ldc_t *ldcp, mblk_t *mp)
3941f0ca1d9aSsb {
3942f0ca1d9aSsb 	vio_raw_data_msg_t	*pkt;
3943f0ca1d9aSsb 	mblk_t			*bp;
3944f0ca1d9aSsb 	mblk_t			*nmp = NULL;
39457bd3a2e2SSriharsha Basavapatna 	vio_mblk_t		*vmp;
3946f0ca1d9aSsb 	caddr_t			dst;
3947f0ca1d9aSsb 	uint32_t		mblksz;
3948f0ca1d9aSsb 	uint32_t		size;
3949f0ca1d9aSsb 	uint32_t		nbytes;
3950f0ca1d9aSsb 	int			rv;
3951f0ca1d9aSsb 	vsw_t			*vswp = ldcp->ldc_vswp;
3952f0ca1d9aSsb 	vgen_stats_t		*statsp = &ldcp->ldc_stats;
3953f0ca1d9aSsb 
3954f0ca1d9aSsb 	if ((!(ldcp->lane_out.lstate & VSW_LANE_ACTIVE)) ||
3955*6e472272SToomas Soome 	    (ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == 0)) {
3956f0ca1d9aSsb 		(void) atomic_inc_32(&statsp->tx_pri_fail);
3957f0ca1d9aSsb 		DWARN(vswp, "%s(%lld) status(%d) lstate(0x%llx), dropping "
3958f0ca1d9aSsb 		    "packet\n", __func__, ldcp->ldc_id, ldcp->ldc_status,
3959f0ca1d9aSsb 		    ldcp->lane_out.lstate);
3960f0ca1d9aSsb 		goto send_pkt_exit;
3961f0ca1d9aSsb 	}
3962f0ca1d9aSsb 
3963f0ca1d9aSsb 	size = msgsize(mp);
3964f0ca1d9aSsb 
3965f0ca1d9aSsb 	/* frame size bigger than available payload len of raw data msg ? */
3966f0ca1d9aSsb 	if (size > (size_t)(ldcp->msglen - VIO_PKT_DATA_HDRSIZE)) {
3967f0ca1d9aSsb 		(void) atomic_inc_32(&statsp->tx_pri_fail);
3968f0ca1d9aSsb 		DWARN(vswp, "%s(%lld) invalid size(%d)\n", __func__,
3969f0ca1d9aSsb 		    ldcp->ldc_id, size);
3970f0ca1d9aSsb 		goto send_pkt_exit;
3971f0ca1d9aSsb 	}
3972f0ca1d9aSsb 
3973f0ca1d9aSsb 	if (size < ETHERMIN)
3974f0ca1d9aSsb 		size = ETHERMIN;
3975f0ca1d9aSsb 
3976f0ca1d9aSsb 	/* alloc space for a raw data message */
39777bd3a2e2SSriharsha Basavapatna 	vmp = vio_allocb(vswp->pri_tx_vmp);
39787bd3a2e2SSriharsha Basavapatna 	if (vmp == NULL) {
3979f0ca1d9aSsb 		(void) atomic_inc_32(&statsp->tx_pri_fail);
3980f0ca1d9aSsb 		DWARN(vswp, "vio_allocb failed\n");
3981f0ca1d9aSsb 		goto send_pkt_exit;
39827bd3a2e2SSriharsha Basavapatna 	} else {
39837bd3a2e2SSriharsha Basavapatna 		nmp = vmp->mp;
3984f0ca1d9aSsb 	}
3985f0ca1d9aSsb 	pkt = (vio_raw_data_msg_t *)nmp->b_rptr;
3986f0ca1d9aSsb 
3987f0ca1d9aSsb 	/* copy frame into the payload of raw data message */
3988f0ca1d9aSsb 	dst = (caddr_t)pkt->data;
3989f0ca1d9aSsb 	for (bp = mp; bp != NULL; bp = bp->b_cont) {
3990f0ca1d9aSsb 		mblksz = MBLKL(bp);
3991f0ca1d9aSsb 		bcopy(bp->b_rptr, dst, mblksz);
3992f0ca1d9aSsb 		dst += mblksz;
3993f0ca1d9aSsb 	}
3994f0ca1d9aSsb 
39957bd3a2e2SSriharsha Basavapatna 	vmp->state = VIO_MBLK_HAS_DATA;
39967bd3a2e2SSriharsha Basavapatna 
3997f0ca1d9aSsb 	/* setup the raw data msg */
3998f0ca1d9aSsb 	pkt->tag.vio_msgtype = VIO_TYPE_DATA;
3999f0ca1d9aSsb 	pkt->tag.vio_subtype = VIO_SUBTYPE_INFO;
4000f0ca1d9aSsb 	pkt->tag.vio_subtype_env = VIO_PKT_DATA;
4001f0ca1d9aSsb 	pkt->tag.vio_sid = ldcp->local_session;
4002f0ca1d9aSsb 	nbytes = VIO_PKT_DATA_HDRSIZE + size;
4003f0ca1d9aSsb 
4004f0ca1d9aSsb 	/* send the msg over ldc */
4005f0ca1d9aSsb 	rv = vsw_send_msg(ldcp, (void *)pkt, nbytes, B_TRUE);
4006f0ca1d9aSsb 	if (rv != 0) {
4007f0ca1d9aSsb 		(void) atomic_inc_32(&statsp->tx_pri_fail);
4008f0ca1d9aSsb 		DWARN(vswp, "%s(%lld) Error sending priority frame\n", __func__,
4009f0ca1d9aSsb 		    ldcp->ldc_id);
4010f0ca1d9aSsb 		goto send_pkt_exit;
4011f0ca1d9aSsb 	}
4012f0ca1d9aSsb 
4013f0ca1d9aSsb 	/* update stats */
4014f0ca1d9aSsb 	(void) atomic_inc_64(&statsp->tx_pri_packets);
4015f0ca1d9aSsb 	(void) atomic_add_64(&statsp->tx_pri_packets, size);
4016f0ca1d9aSsb 
4017f0ca1d9aSsb send_pkt_exit:
4018f0ca1d9aSsb 	if (nmp != NULL)
4019f0ca1d9aSsb 		freemsg(nmp);
4020f0ca1d9aSsb 	freemsg(mp);
402106db247cSraghuram }
402206db247cSraghuram 
402306db247cSraghuram /*
402406db247cSraghuram  * Transmit the packet over the given LDC channel.
402506db247cSraghuram  *
402606db247cSraghuram  * The 'retries' argument indicates how many times a packet
402706db247cSraghuram  * is retried before it is dropped. Note, the retry is done
402806db247cSraghuram  * only for a resource related failure, for all other failures
402906db247cSraghuram  * the packet is dropped immediately.
403006db247cSraghuram  */
403106db247cSraghuram static int
vsw_ldcsend(vsw_ldc_t * ldcp,mblk_t * mp,uint32_t retries)4032f0ca1d9aSsb vsw_ldcsend(vsw_ldc_t *ldcp, mblk_t *mp, uint32_t retries)
403306db247cSraghuram {
40347bd3a2e2SSriharsha Basavapatna 	int		i;
40357bd3a2e2SSriharsha Basavapatna 	int		rc;
40367bd3a2e2SSriharsha Basavapatna 	int		status = 0;
40377bd3a2e2SSriharsha Basavapatna 	vsw_port_t	*port = ldcp->ldc_port;
40387bd3a2e2SSriharsha Basavapatna 	dring_info_t	*dp = NULL;
40397bd3a2e2SSriharsha Basavapatna 	lane_t		*lp = &ldcp->lane_out;
404006db247cSraghuram 
404106db247cSraghuram 	for (i = 0; i < retries; ) {
404206db247cSraghuram 		/*
404306db247cSraghuram 		 * Send the message out using the appropriate
404406db247cSraghuram 		 * transmit function which will free mblock when it
404506db247cSraghuram 		 * is finished with it.
404606db247cSraghuram 		 */
404706db247cSraghuram 		mutex_enter(&port->tx_lock);
404806db247cSraghuram 		if (port->transmit != NULL) {
404906db247cSraghuram 			status = (*port->transmit)(ldcp, mp);
405006db247cSraghuram 		}
405106db247cSraghuram 		if (status == LDC_TX_SUCCESS) {
405206db247cSraghuram 			mutex_exit(&port->tx_lock);
405306db247cSraghuram 			break;
405406db247cSraghuram 		}
405506db247cSraghuram 		i++;	/* increment the counter here */
405606db247cSraghuram 
405706db247cSraghuram 		/* If its the last retry, then update the oerror */
405806db247cSraghuram 		if ((i == retries) && (status == LDC_TX_NORESOURCES)) {
405906db247cSraghuram 			ldcp->ldc_stats.oerrors++;
406006db247cSraghuram 		}
406106db247cSraghuram 		mutex_exit(&port->tx_lock);
406206db247cSraghuram 
406306db247cSraghuram 		if (status != LDC_TX_NORESOURCES) {
406406db247cSraghuram 			/*
406506db247cSraghuram 			 * No retrying required for errors un-related
406606db247cSraghuram 			 * to resources.
406706db247cSraghuram 			 */
406806db247cSraghuram 			break;
406906db247cSraghuram 		}
4070aba2c384Sraghuram 		if (((dp = ldcp->lane_out.dringp) != NULL) &&
4071c1c61f44Ssb 		    ((VSW_VER_GTEQ(ldcp, 1, 2) &&
4072f0ca1d9aSsb 		    (ldcp->lane_out.xfer_mode & VIO_DRING_MODE_V1_2)) ||
4073f0ca1d9aSsb 		    ((VSW_VER_LT(ldcp, 1, 2) &&
4074f0ca1d9aSsb 		    (ldcp->lane_out.xfer_mode == VIO_DRING_MODE_V1_0))))) {
407506db247cSraghuram 
40767bd3a2e2SSriharsha Basavapatna 			/* Need to reclaim in TxDring mode. */
40777bd3a2e2SSriharsha Basavapatna 			if (lp->dring_mode == VIO_TX_DRING) {
40787bd3a2e2SSriharsha Basavapatna 				rc = vsw_reclaim_dring(dp, dp->end_idx);
40797bd3a2e2SSriharsha Basavapatna 			}
408006db247cSraghuram 
40817bd3a2e2SSriharsha Basavapatna 		} else {
40827bd3a2e2SSriharsha Basavapatna 			/*
40837bd3a2e2SSriharsha Basavapatna 			 * If there is no dring or the xfer_mode is
40847bd3a2e2SSriharsha Basavapatna 			 * set to DESC_MODE(ie., OBP), then simply break here.
40857bd3a2e2SSriharsha Basavapatna 			 */
40867bd3a2e2SSriharsha Basavapatna 			break;
40877bd3a2e2SSriharsha Basavapatna 		}
408806db247cSraghuram 
408906db247cSraghuram 		/*
40907bd3a2e2SSriharsha Basavapatna 		 * Delay only if none were reclaimed
40917bd3a2e2SSriharsha Basavapatna 		 * and its not the last retry.
409206db247cSraghuram 		 */
40937bd3a2e2SSriharsha Basavapatna 		if ((rc == 0) && (i < retries)) {
40947bd3a2e2SSriharsha Basavapatna 			delay(drv_usectohz(vsw_ldc_tx_delay));
409506db247cSraghuram 		}
409606db247cSraghuram 	}
40977bd3a2e2SSriharsha Basavapatna 	freemsg(mp);
409806db247cSraghuram 	return (status);
409906db247cSraghuram }
410006db247cSraghuram 
410106db247cSraghuram /*
410206db247cSraghuram  * Send an in-band descriptor message over ldc.
410306db247cSraghuram  */
410406db247cSraghuram static int
vsw_descrsend(vsw_ldc_t * ldcp,mblk_t * mp)410506db247cSraghuram vsw_descrsend(vsw_ldc_t *ldcp, mblk_t *mp)
410606db247cSraghuram {
410706db247cSraghuram 	vsw_t			*vswp = ldcp->ldc_vswp;
410806db247cSraghuram 	vnet_ibnd_desc_t	ibnd_msg;
410906db247cSraghuram 	vsw_private_desc_t	*priv_desc = NULL;
411006db247cSraghuram 	dring_info_t		*dp = NULL;
411106db247cSraghuram 	size_t			n, size = 0;
411206db247cSraghuram 	caddr_t			bufp;
411306db247cSraghuram 	mblk_t			*bp;
411406db247cSraghuram 	int			idx, i;
411506db247cSraghuram 	int			status = LDC_TX_SUCCESS;
411606db247cSraghuram 	static int		warn_msg = 1;
4117c1c61f44Ssb 	lane_t			*lp = &ldcp->lane_out;
411806db247cSraghuram 
411906db247cSraghuram 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
412006db247cSraghuram 
412106db247cSraghuram 	ASSERT(mp != NULL);
412206db247cSraghuram 
412306db247cSraghuram 	if ((!(ldcp->lane_out.lstate & VSW_LANE_ACTIVE)) ||
4124*6e472272SToomas Soome 	    (ldcp->ldc_status != LDC_UP) || (ldcp->ldc_handle == 0)) {
412506db247cSraghuram 		DERR(vswp, "%s(%lld) status(%d) state (0x%llx), dropping pkt",
412606db247cSraghuram 		    __func__, ldcp->ldc_id, ldcp->ldc_status,
412706db247cSraghuram 		    ldcp->lane_out.lstate);
412806db247cSraghuram 		ldcp->ldc_stats.oerrors++;
412906db247cSraghuram 		return (LDC_TX_FAILURE);
413006db247cSraghuram 	}
413106db247cSraghuram 
413206db247cSraghuram 	/*
41337bd3a2e2SSriharsha Basavapatna 	 * The dring here is as an internal buffer,
41347bd3a2e2SSriharsha Basavapatna 	 * rather than a transfer channel.
413506db247cSraghuram 	 */
413606db247cSraghuram 	if ((dp = ldcp->lane_out.dringp) == NULL) {
413706db247cSraghuram 		DERR(vswp, "%s(%lld): no dring for outbound lane",
413806db247cSraghuram 		    __func__, ldcp->ldc_id);
413906db247cSraghuram 		DERR(vswp, "%s(%lld) status(%d) state (0x%llx)", __func__,
414006db247cSraghuram 		    ldcp->ldc_id, ldcp->ldc_status, ldcp->lane_out.lstate);
414106db247cSraghuram 		ldcp->ldc_stats.oerrors++;
414206db247cSraghuram 		return (LDC_TX_FAILURE);
414306db247cSraghuram 	}
414406db247cSraghuram 
414506db247cSraghuram 	size = msgsize(mp);
4146c1c61f44Ssb 	if (size > (size_t)lp->mtu) {
414706db247cSraghuram 		DERR(vswp, "%s(%lld) invalid size (%ld)\n", __func__,
414806db247cSraghuram 		    ldcp->ldc_id, size);
414906db247cSraghuram 		ldcp->ldc_stats.oerrors++;
415006db247cSraghuram 		return (LDC_TX_FAILURE);
415106db247cSraghuram 	}
415206db247cSraghuram 
415306db247cSraghuram 	/*
415406db247cSraghuram 	 * Find a free descriptor in our buffer ring
415506db247cSraghuram 	 */
415606db247cSraghuram 	if (vsw_dring_find_free_desc(dp, &priv_desc, &idx) != 0) {
415706db247cSraghuram 		if (warn_msg) {
415806db247cSraghuram 			DERR(vswp, "%s(%lld): no descriptor available for ring "
415906db247cSraghuram 			    "at 0x%llx", __func__, ldcp->ldc_id, dp);
416006db247cSraghuram 			warn_msg = 0;
416106db247cSraghuram 		}
416206db247cSraghuram 
416306db247cSraghuram 		/* nothing more we can do */
416406db247cSraghuram 		status = LDC_TX_NORESOURCES;
416506db247cSraghuram 		goto vsw_descrsend_free_exit;
416606db247cSraghuram 	} else {
416706db247cSraghuram 		D2(vswp, "%s(%lld): free private descriptor found at pos "
416806db247cSraghuram 		    "%ld addr 0x%x\n", __func__, ldcp->ldc_id, idx, priv_desc);
416906db247cSraghuram 		warn_msg = 1;
417006db247cSraghuram 	}
417106db247cSraghuram 
417206db247cSraghuram 	/* copy data into the descriptor */
417306db247cSraghuram 	bufp = priv_desc->datap;
417406db247cSraghuram 	for (bp = mp, n = 0; bp != NULL; bp = bp->b_cont) {
417506db247cSraghuram 		n = MBLKL(bp);
417606db247cSraghuram 		bcopy(bp->b_rptr, bufp, n);
417706db247cSraghuram 		bufp += n;
417806db247cSraghuram 	}
417906db247cSraghuram 
418006db247cSraghuram 	priv_desc->datalen = (size < (size_t)ETHERMIN) ? ETHERMIN : size;
418106db247cSraghuram 
418206db247cSraghuram 	/* create and send the in-band descp msg */
418306db247cSraghuram 	ibnd_msg.hdr.tag.vio_msgtype = VIO_TYPE_DATA;
418406db247cSraghuram 	ibnd_msg.hdr.tag.vio_subtype = VIO_SUBTYPE_INFO;
418506db247cSraghuram 	ibnd_msg.hdr.tag.vio_subtype_env = VIO_DESC_DATA;
418606db247cSraghuram 	ibnd_msg.hdr.tag.vio_sid = ldcp->local_session;
418706db247cSraghuram 
418806db247cSraghuram 	/*
418906db247cSraghuram 	 * Copy the mem cookies describing the data from the
419006db247cSraghuram 	 * private region of the descriptor ring into the inband
419106db247cSraghuram 	 * descriptor.
419206db247cSraghuram 	 */
419306db247cSraghuram 	for (i = 0; i < priv_desc->ncookies; i++) {
419406db247cSraghuram 		bcopy(&priv_desc->memcookie[i], &ibnd_msg.memcookie[i],
419506db247cSraghuram 		    sizeof (ldc_mem_cookie_t));
419606db247cSraghuram 	}
419706db247cSraghuram 
419806db247cSraghuram 	ibnd_msg.hdr.desc_handle = idx;
419906db247cSraghuram 	ibnd_msg.ncookies = priv_desc->ncookies;
420006db247cSraghuram 	ibnd_msg.nbytes = size;
420106db247cSraghuram 
420206db247cSraghuram 	ldcp->ldc_stats.opackets++;
420306db247cSraghuram 	ldcp->ldc_stats.obytes += size;
420406db247cSraghuram 
420506db247cSraghuram 	(void) vsw_send_msg(ldcp, (void *)&ibnd_msg,
420606db247cSraghuram 	    sizeof (vnet_ibnd_desc_t), B_TRUE);
420706db247cSraghuram 
420806db247cSraghuram vsw_descrsend_free_exit:
420906db247cSraghuram 
421006db247cSraghuram 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
421106db247cSraghuram 	return (status);
421206db247cSraghuram }
421306db247cSraghuram 
421406db247cSraghuram static void
vsw_send_ver(void * arg)421506db247cSraghuram vsw_send_ver(void *arg)
421606db247cSraghuram {
421706db247cSraghuram 	vsw_ldc_t	*ldcp = (vsw_ldc_t *)arg;
421806db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
421906db247cSraghuram 	lane_t		*lp = &ldcp->lane_out;
422006db247cSraghuram 	vio_ver_msg_t	ver_msg;
422106db247cSraghuram 
422206db247cSraghuram 	D1(vswp, "%s enter", __func__);
422306db247cSraghuram 
422406db247cSraghuram 	ver_msg.tag.vio_msgtype = VIO_TYPE_CTRL;
422506db247cSraghuram 	ver_msg.tag.vio_subtype = VIO_SUBTYPE_INFO;
422606db247cSraghuram 	ver_msg.tag.vio_subtype_env = VIO_VER_INFO;
422706db247cSraghuram 	ver_msg.tag.vio_sid = ldcp->local_session;
422806db247cSraghuram 
4229f0ca1d9aSsb 	if (vsw_obp_ver_proto_workaround == B_FALSE) {
4230f0ca1d9aSsb 		ver_msg.ver_major = vsw_versions[0].ver_major;
4231f0ca1d9aSsb 		ver_msg.ver_minor = vsw_versions[0].ver_minor;
4232f0ca1d9aSsb 	} else {
4233f0ca1d9aSsb 		/* use the major,minor that we've ack'd */
4234f0ca1d9aSsb 		lane_t	*lpi = &ldcp->lane_in;
4235f0ca1d9aSsb 		ver_msg.ver_major = lpi->ver_major;
4236f0ca1d9aSsb 		ver_msg.ver_minor = lpi->ver_minor;
4237f0ca1d9aSsb 	}
423806db247cSraghuram 	ver_msg.dev_class = VDEV_NETWORK_SWITCH;
423906db247cSraghuram 
424006db247cSraghuram 	lp->lstate |= VSW_VER_INFO_SENT;
424106db247cSraghuram 	lp->ver_major = ver_msg.ver_major;
424206db247cSraghuram 	lp->ver_minor = ver_msg.ver_minor;
424306db247cSraghuram 
424406db247cSraghuram 	DUMP_TAG(ver_msg.tag);
424506db247cSraghuram 
424606db247cSraghuram 	(void) vsw_send_msg(ldcp, &ver_msg, sizeof (vio_ver_msg_t), B_TRUE);
424706db247cSraghuram 
424806db247cSraghuram 	D1(vswp, "%s (%d): exit", __func__, ldcp->ldc_id);
424906db247cSraghuram }
425006db247cSraghuram 
425106db247cSraghuram static void
vsw_send_attr(vsw_ldc_t * ldcp)425206db247cSraghuram vsw_send_attr(vsw_ldc_t *ldcp)
425306db247cSraghuram {
425406db247cSraghuram 	vsw_t			*vswp = ldcp->ldc_vswp;
425506db247cSraghuram 	lane_t			*lp = &ldcp->lane_out;
425606db247cSraghuram 	vnet_attr_msg_t		attr_msg;
425706db247cSraghuram 
425806db247cSraghuram 	D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id);
425906db247cSraghuram 
426006db247cSraghuram 	/*
426106db247cSraghuram 	 * Subtype is set to INFO by default
426206db247cSraghuram 	 */
426306db247cSraghuram 	attr_msg.tag.vio_msgtype = VIO_TYPE_CTRL;
426406db247cSraghuram 	attr_msg.tag.vio_subtype = VIO_SUBTYPE_INFO;
426506db247cSraghuram 	attr_msg.tag.vio_subtype_env = VIO_ATTR_INFO;
426606db247cSraghuram 	attr_msg.tag.vio_sid = ldcp->local_session;
426706db247cSraghuram 
426806db247cSraghuram 	/* payload copied from default settings for lane */
426906db247cSraghuram 	attr_msg.mtu = lp->mtu;
427006db247cSraghuram 	attr_msg.addr_type = lp->addr_type;
427106db247cSraghuram 	attr_msg.xfer_mode = lp->xfer_mode;
427206db247cSraghuram 	attr_msg.ack_freq = lp->xfer_mode;
42737bd3a2e2SSriharsha Basavapatna 	attr_msg.options = lp->dring_mode;
427406db247cSraghuram 
427506db247cSraghuram 	READ_ENTER(&vswp->if_lockrw);
4276f2b610cfSwentaoy 	attr_msg.addr = vnet_macaddr_strtoul((vswp->if_addr).ether_addr_octet);
427706db247cSraghuram 	RW_EXIT(&vswp->if_lockrw);
427806db247cSraghuram 
427906db247cSraghuram 	ldcp->lane_out.lstate |= VSW_ATTR_INFO_SENT;
428006db247cSraghuram 
428106db247cSraghuram 	DUMP_TAG(attr_msg.tag);
428206db247cSraghuram 
428306db247cSraghuram 	(void) vsw_send_msg(ldcp, &attr_msg, sizeof (vnet_attr_msg_t), B_TRUE);
428406db247cSraghuram 
428506db247cSraghuram 	D1(vswp, "%s (%ld) exit", __func__, ldcp->ldc_id);
428606db247cSraghuram }
428706db247cSraghuram 
428806db247cSraghuram static void
vsw_send_dring_info(vsw_ldc_t * ldcp)428906db247cSraghuram vsw_send_dring_info(vsw_ldc_t *ldcp)
429006db247cSraghuram {
42917bd3a2e2SSriharsha Basavapatna 	int		msgsize;
42927bd3a2e2SSriharsha Basavapatna 	void		*msg;
42937bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
42947bd3a2e2SSriharsha Basavapatna 	vsw_port_t	*port = ldcp->ldc_port;
42957bd3a2e2SSriharsha Basavapatna 	lane_t		*lp = &ldcp->lane_out;
42967bd3a2e2SSriharsha Basavapatna 	vgen_stats_t	*statsp = &ldcp->ldc_stats;
429706db247cSraghuram 
429806db247cSraghuram 	D1(vswp, "%s: (%ld) enter", __func__, ldcp->ldc_id);
429906db247cSraghuram 
43007bd3a2e2SSriharsha Basavapatna 	/* dring mode has been negotiated in attr phase; save in stats */
43017bd3a2e2SSriharsha Basavapatna 	statsp->dring_mode = lp->dring_mode;
430206db247cSraghuram 
43037bd3a2e2SSriharsha Basavapatna 	if (lp->dring_mode == VIO_RX_DRING_DATA) {
43047bd3a2e2SSriharsha Basavapatna 		/*
43057bd3a2e2SSriharsha Basavapatna 		 * Change the transmit routine for RxDringData mode.
43067bd3a2e2SSriharsha Basavapatna 		 */
43077bd3a2e2SSriharsha Basavapatna 		port->transmit = vsw_dringsend_shm;
43087bd3a2e2SSriharsha Basavapatna 		msg = (void *) vsw_create_rx_dring_info(ldcp);
43097bd3a2e2SSriharsha Basavapatna 		if (msg == NULL) {
43107bd3a2e2SSriharsha Basavapatna 			return;
43117bd3a2e2SSriharsha Basavapatna 		}
43127bd3a2e2SSriharsha Basavapatna 		msgsize =
43137bd3a2e2SSriharsha Basavapatna 		    VNET_DRING_REG_EXT_MSG_SIZE(lp->dringp->data_ncookies);
43147bd3a2e2SSriharsha Basavapatna 		ldcp->rcv_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
43157bd3a2e2SSriharsha Basavapatna 		    vsw_ldc_rcv_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
43167bd3a2e2SSriharsha Basavapatna 		ldcp->rx_dringdata = vsw_process_dringdata_shm;
43177bd3a2e2SSriharsha Basavapatna 	} else {
43187bd3a2e2SSriharsha Basavapatna 		msg = (void *) vsw_create_tx_dring_info(ldcp);
43197bd3a2e2SSriharsha Basavapatna 		if (msg == NULL) {
43207bd3a2e2SSriharsha Basavapatna 			return;
43217bd3a2e2SSriharsha Basavapatna 		}
43227bd3a2e2SSriharsha Basavapatna 		msgsize = sizeof (vio_dring_reg_msg_t);
43237bd3a2e2SSriharsha Basavapatna 		ldcp->msg_thread = thread_create(NULL, 2 * DEFAULTSTKSZ,
43247bd3a2e2SSriharsha Basavapatna 		    vsw_ldc_msg_worker, ldcp, 0, &p0, TS_RUN, maxclsyspri);
43257bd3a2e2SSriharsha Basavapatna 		ldcp->rx_dringdata = vsw_process_dringdata;
43267bd3a2e2SSriharsha Basavapatna 	}
432706db247cSraghuram 
43287bd3a2e2SSriharsha Basavapatna 	lp->lstate |= VSW_DRING_INFO_SENT;
43297bd3a2e2SSriharsha Basavapatna 	DUMP_TAG_PTR((vio_msg_tag_t *)msg);
43307bd3a2e2SSriharsha Basavapatna 	(void) vsw_send_msg(ldcp, msg, msgsize, B_TRUE);
43317bd3a2e2SSriharsha Basavapatna 	kmem_free(msg, msgsize);
433206db247cSraghuram 
433306db247cSraghuram 	D1(vswp, "%s: (%ld) exit", __func__, ldcp->ldc_id);
433406db247cSraghuram }
433506db247cSraghuram 
433606db247cSraghuram static void
vsw_send_rdx(vsw_ldc_t * ldcp)433706db247cSraghuram vsw_send_rdx(vsw_ldc_t *ldcp)
433806db247cSraghuram {
433906db247cSraghuram 	vsw_t		*vswp = ldcp->ldc_vswp;
434006db247cSraghuram 	vio_rdx_msg_t	rdx_msg;
434106db247cSraghuram 
434206db247cSraghuram 	D1(vswp, "%s (%ld) enter", __func__, ldcp->ldc_id);
434306db247cSraghuram 
434406db247cSraghuram 	rdx_msg.tag.vio_msgtype = VIO_TYPE_CTRL;
434506db247cSraghuram 	rdx_msg.tag.vio_subtype = VIO_SUBTYPE_INFO;
434606db247cSraghuram 	rdx_msg.tag.vio_subtype_env = VIO_RDX;
434706db247cSraghuram 	rdx_msg.tag.vio_sid = ldcp->local_session;
434806db247cSraghuram 
434906db247cSraghuram 	ldcp->lane_in.lstate |= VSW_RDX_INFO_SENT;
435006db247cSraghuram 
435106db247cSraghuram 	DUMP_TAG(rdx_msg.tag);
435206db247cSraghuram 
435306db247cSraghuram 	(void) vsw_send_msg(ldcp, &rdx_msg, sizeof (vio_rdx_msg_t), B_TRUE);
435406db247cSraghuram 
435506db247cSraghuram 	D1(vswp, "%s (%ld) exit", __func__, ldcp->ldc_id);
435606db247cSraghuram }
435706db247cSraghuram 
435806db247cSraghuram /*
435906db247cSraghuram  * Remove the specified address from the list of address maintained
436006db247cSraghuram  * in this port node.
436106db247cSraghuram  */
436206db247cSraghuram mcst_addr_t *
vsw_del_addr(uint8_t devtype,void * arg,uint64_t addr)436306db247cSraghuram vsw_del_addr(uint8_t devtype, void *arg, uint64_t addr)
436406db247cSraghuram {
436506db247cSraghuram 	vsw_t		*vswp = NULL;
436606db247cSraghuram 	vsw_port_t	*port = NULL;
436706db247cSraghuram 	mcst_addr_t	*prev_p = NULL;
436806db247cSraghuram 	mcst_addr_t	*curr_p = NULL;
436906db247cSraghuram 
437006db247cSraghuram 	D1(NULL, "%s: enter : devtype %d : addr 0x%llx",
437106db247cSraghuram 	    __func__, devtype, addr);
437206db247cSraghuram 
437306db247cSraghuram 	if (devtype == VSW_VNETPORT) {
437406db247cSraghuram 		port = (vsw_port_t *)arg;
437506db247cSraghuram 		mutex_enter(&port->mca_lock);
437606db247cSraghuram 		prev_p = curr_p = port->mcap;
437706db247cSraghuram 	} else {
437806db247cSraghuram 		vswp = (vsw_t *)arg;
437906db247cSraghuram 		mutex_enter(&vswp->mca_lock);
438006db247cSraghuram 		prev_p = curr_p = vswp->mcap;
438106db247cSraghuram 	}
438206db247cSraghuram 
438306db247cSraghuram 	while (curr_p != NULL) {
438406db247cSraghuram 		if (curr_p->addr == addr) {
438506db247cSraghuram 			D2(NULL, "%s: address found", __func__);
438606db247cSraghuram 			/* match found */
438706db247cSraghuram 			if (prev_p == curr_p) {
43887bd3a2e2SSriharsha Basavapatna 				/* list head */
43897bd3a2e2SSriharsha Basavapatna 				if (devtype == VSW_VNETPORT)
43907bd3a2e2SSriharsha Basavapatna 					port->mcap = curr_p->nextp;
43917bd3a2e2SSriharsha Basavapatna 				else
43927bd3a2e2SSriharsha Basavapatna 					vswp->mcap = curr_p->nextp;
43937bd3a2e2SSriharsha Basavapatna 			} else {
43947bd3a2e2SSriharsha Basavapatna 				prev_p->nextp = curr_p->nextp;
439506db247cSraghuram 			}
43967bd3a2e2SSriharsha Basavapatna 			break;
43977bd3a2e2SSriharsha Basavapatna 		} else {
43987bd3a2e2SSriharsha Basavapatna 			prev_p = curr_p;
43997bd3a2e2SSriharsha Basavapatna 			curr_p = curr_p->nextp;
440006db247cSraghuram 		}
440106db247cSraghuram 	}
440206db247cSraghuram 
44037bd3a2e2SSriharsha Basavapatna 	if (devtype == VSW_VNETPORT)
44047bd3a2e2SSriharsha Basavapatna 		mutex_exit(&port->mca_lock);
44057bd3a2e2SSriharsha Basavapatna 	else
44067bd3a2e2SSriharsha Basavapatna 		mutex_exit(&vswp->mca_lock);
440706db247cSraghuram 
44087bd3a2e2SSriharsha Basavapatna 	D1(NULL, "%s: exit", __func__);
440906db247cSraghuram 
44107bd3a2e2SSriharsha Basavapatna 	return (curr_p);
441106db247cSraghuram }
441206db247cSraghuram 
441306db247cSraghuram /*
44147bd3a2e2SSriharsha Basavapatna  * Create a ring consisting of just a private portion and link
44157bd3a2e2SSriharsha Basavapatna  * it into the list of rings for the outbound lane.
441606db247cSraghuram  *
44177bd3a2e2SSriharsha Basavapatna  * These type of rings are used primarily for temporary data
44187bd3a2e2SSriharsha Basavapatna  * storage (i.e. as data buffers).
441906db247cSraghuram  */
44207bd3a2e2SSriharsha Basavapatna void
vsw_create_privring(vsw_ldc_t * ldcp)44217bd3a2e2SSriharsha Basavapatna vsw_create_privring(vsw_ldc_t *ldcp)
442206db247cSraghuram {
44237bd3a2e2SSriharsha Basavapatna 	dring_info_t		*dp;
44247bd3a2e2SSriharsha Basavapatna 	vsw_t			*vswp = ldcp->ldc_vswp;
442506db247cSraghuram 
44267bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld): enter", __func__, ldcp->ldc_id);
442706db247cSraghuram 
44287bd3a2e2SSriharsha Basavapatna 	dp = kmem_zalloc(sizeof (dring_info_t), KM_SLEEP);
44297bd3a2e2SSriharsha Basavapatna 	mutex_init(&dp->dlock, NULL, MUTEX_DRIVER, NULL);
44307bd3a2e2SSriharsha Basavapatna 	mutex_init(&dp->restart_lock, NULL, MUTEX_DRIVER, NULL);
44317bd3a2e2SSriharsha Basavapatna 	ldcp->lane_out.dringp = dp;
443206db247cSraghuram 
44337bd3a2e2SSriharsha Basavapatna 	/* no public section */
44347bd3a2e2SSriharsha Basavapatna 	dp->pub_addr = NULL;
44357bd3a2e2SSriharsha Basavapatna 	dp->priv_addr = kmem_zalloc(
44367bd3a2e2SSriharsha Basavapatna 	    (sizeof (vsw_private_desc_t) * vsw_num_descriptors), KM_SLEEP);
44377bd3a2e2SSriharsha Basavapatna 	dp->num_descriptors = vsw_num_descriptors;
443806db247cSraghuram 
44397bd3a2e2SSriharsha Basavapatna 	if (vsw_setup_tx_dring(ldcp, dp)) {
44407bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s: setup of ring failed", __func__);
44417bd3a2e2SSriharsha Basavapatna 		vsw_destroy_tx_dring(ldcp);
44427bd3a2e2SSriharsha Basavapatna 		return;
444306db247cSraghuram 	}
444406db247cSraghuram 
44457bd3a2e2SSriharsha Basavapatna 	/* haven't used any descriptors yet */
44467bd3a2e2SSriharsha Basavapatna 	dp->end_idx = 0;
44477bd3a2e2SSriharsha Basavapatna 	dp->restart_reqd = B_TRUE;
444806db247cSraghuram 
44497bd3a2e2SSriharsha Basavapatna 	D1(vswp, "%s(%lld): exit", __func__, ldcp->ldc_id);
445006db247cSraghuram }
445106db247cSraghuram 
445206db247cSraghuram /*
445306db247cSraghuram  * Set the default lane attributes. These are copied into
445406db247cSraghuram  * the attr msg we send to our peer. If they are not acceptable
445506db247cSraghuram  * then (currently) the handshake ends.
445606db247cSraghuram  */
445706db247cSraghuram static void
vsw_set_lane_attr(vsw_t * vswp,lane_t * lp)445806db247cSraghuram vsw_set_lane_attr(vsw_t *vswp, lane_t *lp)
445906db247cSraghuram {
446006db247cSraghuram 	bzero(lp, sizeof (lane_t));
446106db247cSraghuram 
446206db247cSraghuram 	READ_ENTER(&vswp->if_lockrw);
446306db247cSraghuram 	ether_copy(&(vswp->if_addr), &(lp->addr));
446406db247cSraghuram 	RW_EXIT(&vswp->if_lockrw);
446506db247cSraghuram 
4466c1c61f44Ssb 	lp->mtu = vswp->max_frame_size;
446706db247cSraghuram 	lp->addr_type = ADDR_TYPE_MAC;
4468f0ca1d9aSsb 	lp->xfer_mode = VIO_DRING_MODE_V1_0;
446906db247cSraghuram 	lp->ack_freq = 0;	/* for shared mode */
4470f0ca1d9aSsb 	lp->seq_num = VNET_ISS;
447106db247cSraghuram }
447206db247cSraghuram 
447306db247cSraghuram /*
44747bd3a2e2SSriharsha Basavapatna  * Map the descriptor ring exported by the peer.
447506db247cSraghuram  */
44767bd3a2e2SSriharsha Basavapatna static dring_info_t *
vsw_map_dring(vsw_ldc_t * ldcp,void * pkt)44777bd3a2e2SSriharsha Basavapatna vsw_map_dring(vsw_ldc_t *ldcp, void *pkt)
447806db247cSraghuram {
44797bd3a2e2SSriharsha Basavapatna 	dring_info_t	*dp = NULL;
44807bd3a2e2SSriharsha Basavapatna 	lane_t		*lp = &ldcp->lane_out;
448106db247cSraghuram 
44827bd3a2e2SSriharsha Basavapatna 	if (lp->dring_mode == VIO_RX_DRING_DATA) {
44837bd3a2e2SSriharsha Basavapatna 		/*
44847bd3a2e2SSriharsha Basavapatna 		 * In RxDringData mode, dring that we map in
44857bd3a2e2SSriharsha Basavapatna 		 * becomes our transmit descriptor ring.
44867bd3a2e2SSriharsha Basavapatna 		 */
44877bd3a2e2SSriharsha Basavapatna 		dp =  vsw_map_tx_dring(ldcp, pkt);
44887bd3a2e2SSriharsha Basavapatna 	} else {
44897bd3a2e2SSriharsha Basavapatna 		/*
44907bd3a2e2SSriharsha Basavapatna 		 * In TxDring mode, dring that we map in
44917bd3a2e2SSriharsha Basavapatna 		 * becomes our receive descriptor ring.
44927bd3a2e2SSriharsha Basavapatna 		 */
44937bd3a2e2SSriharsha Basavapatna 		dp =  vsw_map_rx_dring(ldcp, pkt);
449406db247cSraghuram 	}
44957bd3a2e2SSriharsha Basavapatna 	return (dp);
44967bd3a2e2SSriharsha Basavapatna }
449706db247cSraghuram 
44987bd3a2e2SSriharsha Basavapatna /*
44997bd3a2e2SSriharsha Basavapatna  * Common dring mapping function used in both TxDring and RxDringData modes.
45007bd3a2e2SSriharsha Basavapatna  */
45017bd3a2e2SSriharsha Basavapatna dring_info_t *
vsw_map_dring_cmn(vsw_ldc_t * ldcp,vio_dring_reg_msg_t * dring_pkt)45027bd3a2e2SSriharsha Basavapatna vsw_map_dring_cmn(vsw_ldc_t *ldcp, vio_dring_reg_msg_t *dring_pkt)
45037bd3a2e2SSriharsha Basavapatna {
45047bd3a2e2SSriharsha Basavapatna 	int		rv;
45057bd3a2e2SSriharsha Basavapatna 	dring_info_t	*dp;
45067bd3a2e2SSriharsha Basavapatna 	ldc_mem_info_t	minfo;
45077bd3a2e2SSriharsha Basavapatna 	vsw_t		*vswp = ldcp->ldc_vswp;
450806db247cSraghuram 
450906db247cSraghuram 	/*
45107bd3a2e2SSriharsha Basavapatna 	 * If the dring params are unacceptable then we NACK back.
451106db247cSraghuram 	 */
45127bd3a2e2SSriharsha Basavapatna 	if ((dring_pkt->num_descriptors == 0) ||
45137bd3a2e2SSriharsha Basavapatna 	    (dring_pkt->descriptor_size == 0) ||
45147bd3a2e2SSriharsha Basavapatna 	    (dring_pkt->ncookies != 1)) {
45157bd3a2e2SSriharsha Basavapatna 		DERR(vswp, "%s (%lld): invalid dring info",
45167bd3a2e2SSriharsha Basavapatna 		    __func__, ldcp->ldc_id);
45177bd3a2e2SSriharsha Basavapatna 		return (NULL);
451806db247cSraghuram 	}
451906db247cSraghuram 
45207bd3a2e2SSriharsha Basavapatna 	dp = kmem_zalloc(sizeof (dring_info_t), KM_SLEEP);
45217bd3a2e2SSriharsha Basavapatna 
45227bd3a2e2SSriharsha Basavapatna 	dp->num_descriptors = dring_pkt->num_descriptors;
45237bd3a2e2SSriharsha Basavapatna 	dp->descriptor_size = dring_pkt->descriptor_size;
45247bd3a2e2SSriharsha Basavapatna 	dp->options = dring_pkt->options;
45257bd3a2e2SSriharsha Basavapatna 	dp->dring_ncookies = dring_pkt->ncookies;
45267bd3a2e2SSriharsha Basavapatna 
452706db247cSraghuram 	/*
45287bd3a2e2SSriharsha Basavapatna 	 * Note: should only get one cookie. Enforced in
45297bd3a2e2SSriharsha Basavapatna 	 * the ldc layer.
453006db247cSraghuram 	 */
45317bd3a2e2SSriharsha Basavapatna 	bcopy(&dring_pkt->cookie[0], &dp->dring_cookie[0],
45327bd3a2e2SSriharsha Basavapatna 	    sizeof (ldc_mem_cookie_t));
453306db247cSraghuram 
45347bd3a2e2SSriharsha Basavapatna 	rv = ldc_mem_dring_map(ldcp->ldc_handle, &dp->dring_cookie[0],
45357bd3a2e2SSriharsha Basavapatna 	    dp->dring_ncookies, dp->num_descriptors, dp->descriptor_size,
45367bd3a2e2SSriharsha Basavapatna 	    LDC_DIRECT_MAP, &(dp->dring_handle));
45377bd3a2e2SSriharsha Basavapatna 	if (rv != 0) {
45387bd3a2e2SSriharsha Basavapatna 		goto fail;
453906db247cSraghuram 	}
454006db247cSraghuram 
45417bd3a2e2SSriharsha Basavapatna 	rv = ldc_mem_dring_info(dp->dring_handle, &minfo);
45427bd3a2e2SSriharsha Basavapatna 	if (rv != 0) {
45437bd3a2e2SSriharsha Basavapatna 		goto fail;
45447bd3a2e2SSriharsha Basavapatna 	}
45457bd3a2e2SSriharsha Basavapatna 	/* store the address of the ring */
45467bd3a2e2SSriharsha Basavapatna 	dp->pub_addr = minfo.vaddr;
454706db247cSraghuram 
45487bd3a2e2SSriharsha Basavapatna 	/* cache the dring mtype */
45497bd3a2e2SSriharsha Basavapatna 	dp->dring_mtype = minfo.mtype;
455006db247cSraghuram 
45517bd3a2e2SSriharsha Basavapatna 	/* no private section as we are importing */
45527bd3a2e2SSriharsha Basavapatna 	dp->priv_addr = NULL;
455306db247cSraghuram 
45547bd3a2e2SSriharsha Basavapatna 	/*
45557bd3a2e2SSriharsha Basavapatna 	 * Using simple mono increasing int for ident at the moment.
45567bd3a2e2SSriharsha Basavapatna 	 */
45577bd3a2e2SSriharsha Basavapatna 	dp->ident = ldcp->next_ident;
45587bd3a2e2SSriharsha Basavapatna 	ldcp->next_ident++;
455906db247cSraghuram 
45607bd3a2e2SSriharsha Basavapatna 	/*
45617bd3a2e2SSriharsha Basavapatna 	 * Acknowledge it; we send back a unique dring identifier that
45627bd3a2e2SSriharsha Basavapatna 	 * the sending side will use in future to refer to this
45637bd3a2e2SSriharsha Basavapatna 	 * descriptor ring.
45647bd3a2e2SSriharsha Basavapatna 	 */
45657bd3a2e2SSriharsha Basavapatna 	dring_pkt->dring_ident = dp->ident;
456606db247cSraghuram 
45677bd3a2e2SSriharsha Basavapatna 	return (dp);
45687bd3a2e2SSriharsha Basavapatna fail:
4569*6e472272SToomas Soome 	if (dp->dring_handle != 0) {
45707bd3a2e2SSriharsha Basavapatna 		(void) ldc_mem_dring_unmap(dp->dring_handle);
457106db247cSraghuram 	}
45727bd3a2e2SSriharsha Basavapatna 	kmem_free(dp, sizeof (*dp));
45737bd3a2e2SSriharsha Basavapatna 	return (NULL);
457406db247cSraghuram }
457506db247cSraghuram 
457606db247cSraghuram /*
45777bd3a2e2SSriharsha Basavapatna  * Unmap the descriptor ring exported by the peer.
457806db247cSraghuram  */
45797bd3a2e2SSriharsha Basavapatna static void
vsw_unmap_dring(vsw_ldc_t * ldcp)45807bd3a2e2SSriharsha Basavapatna vsw_unmap_dring(vsw_ldc_t *ldcp)
458106db247cSraghuram {
45827bd3a2e2SSriharsha Basavapatna 	lane_t	*lane_out = &ldcp->lane_out;
45837bd3a2e2SSriharsha Basavapatna 
45847bd3a2e2SSriharsha Basavapatna 	if (lane_out->dring_mode == VIO_RX_DRING_DATA) {
45857bd3a2e2SSriharsha Basavapatna 		vsw_unmap_tx_dring(ldcp);
458606db247cSraghuram 	} else {
45877bd3a2e2SSriharsha Basavapatna 		vsw_unmap_rx_dring(ldcp);
458806db247cSraghuram 	}
458906db247cSraghuram }
459006db247cSraghuram 
459106db247cSraghuram /*
45927bd3a2e2SSriharsha Basavapatna  * Map the shared memory data buffer area exported by the peer.
45937bd3a2e2SSriharsha Basavapatna  * Used in RxDringData mode only.
459406db247cSraghuram  */
459506db247cSraghuram static int
vsw_map_data(vsw_ldc_t * ldcp,dring_info_t * dp,void * pkt)45967bd3a2e2SSriharsha Basavapatna vsw_map_data(vsw_ldc_t *ldcp, dring_info_t *dp, void *pkt)
459706db247cSraghuram {
45987bd3a2e2SSriharsha Basavapatna 	int			rv;
45997bd3a2e2SSriharsha Basavapatna 	vio_dring_reg_ext_msg_t	*emsg;
46007bd3a2e2SSriharsha Basavapatna 	vio_dring_reg_msg_t	*msg = pkt;
46017bd3a2e2SSriharsha Basavapatna 	uint8_t			*buf = (uint8_t *)msg->cookie;
46027bd3a2e2SSriharsha Basavapatna 	vsw_t			*vswp = ldcp->ldc_vswp;
460334f94fbcSWENTAO YANG 	ldc_mem_info_t		minfo;
46047bd3a2e2SSriharsha Basavapatna 
46057bd3a2e2SSriharsha Basavapatna 	/* skip over dring cookies */
46067bd3a2e2SSriharsha Basavapatna 	ASSERT(msg->ncookies == 1);
46077bd3a2e2SSriharsha Basavapatna 	buf += (msg->ncookies * sizeof (ldc_mem_cookie_t));
46087bd3a2e2SSriharsha Basavapatna 
46097bd3a2e2SSriharsha Basavapatna 	emsg = (vio_dring_reg_ext_msg_t *)buf;
46107bd3a2e2SSriharsha Basavapatna 	if (emsg->data_ncookies > VNET_DATA_AREA_COOKIES) {
46117bd3a2e2SSriharsha Basavapatna 		return (1);
46127bd3a2e2SSriharsha Basavapatna 	}
46137bd3a2e2SSriharsha Basavapatna 
46147bd3a2e2SSriharsha Basavapatna 	/* save # of data area cookies */
46157bd3a2e2SSriharsha Basavapatna 	dp->data_ncookies = emsg->data_ncookies;
46167bd3a2e2SSriharsha Basavapatna 
46177bd3a2e2SSriharsha Basavapatna 	/* save data area size */
46187bd3a2e2SSriharsha Basavapatna 	dp->data_sz = emsg->data_area_size;
46197bd3a2e2SSriharsha Basavapatna 
46207bd3a2e2SSriharsha Basavapatna 	/* allocate ldc mem handle for data area */
46217bd3a2e2SSriharsha Basavapatna 	rv = ldc_mem_alloc_handle(ldcp->ldc_handle, &dp->data_handle);
46227bd3a2e2SSriharsha Basavapatna 	if (rv != 0) {
46237bd3a2e2SSriharsha Basavapatna 		cmn_err(CE_WARN, "ldc_mem_alloc_handle failed\n");
46247bd3a2e2SSriharsha Basavapatna 		DWARN(vswp, "%s (%lld) ldc_mem_alloc_handle() failed: %d\n",
46257bd3a2e2SSriharsha Basavapatna 		    __func__, ldcp->ldc_id, rv);
46267bd3a2e2SSriharsha Basavapatna 		return (1);
46277bd3a2e2SSriharsha Basavapatna 	}
46287bd3a2e2SSriharsha Basavapatna 
46297bd3a2e2SSriharsha Basavapatna 	/* map the data area */
46307bd3a2e2SSriharsha Basavapatna 	rv = ldc_mem_map(dp->data_handle, emsg->data_cookie,
46317bd3a2e2SSriharsha Basavapatna 	    emsg->data_ncookies, LDC_DIRECT_MAP, LDC_MEM_R,
46327bd3a2e2SSriharsha Basavapatna 	    (caddr_t *)&dp->data_addr, NULL);
46337bd3a2e2SSriharsha Basavapatna 	if (rv != 0) {
46347bd3a2e2SSriharsha Basavapatna 		cmn_err(CE_WARN, "ldc_mem_map failed\n");
46357bd3a2e2SSriharsha Basavapatna 		DWARN(vswp, "%s (%lld) ldc_mem_map() failed: %d\n",
46367bd3a2e2SSriharsha Basavapatna 		    __func__, ldcp->ldc_id, rv);
463706db247cSraghuram 		return (1);
463806db247cSraghuram 	}
463906db247cSraghuram 
464034f94fbcSWENTAO YANG 	/* get the map info */
464134f94fbcSWENTAO YANG 	rv = ldc_mem_info(dp->data_handle, &minfo);
464234f94fbcSWENTAO YANG 	if (rv != 0) {
464334f94fbcSWENTAO YANG 		cmn_err(CE_WARN, "ldc_mem_info failed\n");
464434f94fbcSWENTAO YANG 		DWARN(vswp, "%s (%lld) ldc_mem_info() failed: %d\n",
464534f94fbcSWENTAO YANG 		    __func__, ldcp->ldc_id, rv);
464634f94fbcSWENTAO YANG 		return (1);
464734f94fbcSWENTAO YANG 	}
464834f94fbcSWENTAO YANG 
464934f94fbcSWENTAO YANG 	if (minfo.mtype != LDC_DIRECT_MAP) {
465034f94fbcSWENTAO YANG 		DWARN(vswp, "%s (%lld) mtype(%d) is not direct map\n",
465134f94fbcSWENTAO YANG 		    __func__, ldcp->ldc_id, minfo.mtype);
465234f94fbcSWENTAO YANG 		return (1);
465334f94fbcSWENTAO YANG 	}
465434f94fbcSWENTAO YANG 
46557bd3a2e2SSriharsha Basavapatna 	/* allocate memory for data area cookies */
46567bd3a2e2SSriharsha Basavapatna 	dp->data_cookie = kmem_zalloc(emsg->data_ncookies *
46577bd3a2e2SSriharsha Basavapatna 	    sizeof (ldc_mem_cookie_t), KM_SLEEP);
46587bd3a2e2SSriharsha Basavapatna 
46597bd3a2e2SSriharsha Basavapatna 	/* save data area cookies */
46607bd3a2e2SSriharsha Basavapatna 	bcopy(emsg->data_cookie, dp->data_cookie,
46617bd3a2e2SSriharsha Basavapatna 	    emsg->data_ncookies * sizeof (ldc_mem_cookie_t));
46627bd3a2e2SSriharsha Basavapatna 
46637bd3a2e2SSriharsha Basavapatna 	return (0);
466406db247cSraghuram }
466506db247cSraghuram 
466606db247cSraghuram /*
46677bd3a2e2SSriharsha Basavapatna  * Reset and free all the resources associated with the channel.
466806db247cSraghuram  */
466906db247cSraghuram static void
vsw_free_lane_resources(vsw_ldc_t * ldcp,uint64_t dir)467006db247cSraghuram vsw_free_lane_resources(vsw_ldc_t *ldcp, uint64_t dir)
467106db247cSraghuram {
46727bd3a2e2SSriharsha Basavapatna 	lane_t	*lp;
467306db247cSraghuram 
467406db247cSraghuram 	D1(ldcp->ldc_vswp, "%s (%lld): enter", __func__, ldcp->ldc_id);
467506db247cSraghuram 
467606db247cSraghuram 	if (dir == INBOUND) {
467706db247cSraghuram 		D2(ldcp->ldc_vswp, "%s: freeing INBOUND lane"
467806db247cSraghuram 		    " of channel %lld", __func__, ldcp->ldc_id);
467906db247cSraghuram 		lp = &ldcp->lane_in;
468006db247cSraghuram 	} else {
468106db247cSraghuram 		D2(ldcp->ldc_vswp, "%s: freeing OUTBOUND lane"
468206db247cSraghuram 		    " of channel %lld", __func__, ldcp->ldc_id);
468306db247cSraghuram 		lp = &ldcp->lane_out;
468406db247cSraghuram 	}
468506db247cSraghuram 
468606db247cSraghuram 	lp->lstate = VSW_LANE_INACTIV;
4687f0ca1d9aSsb 	lp->seq_num = VNET_ISS;
468806db247cSraghuram 
46897bd3a2e2SSriharsha Basavapatna 	if (dir == INBOUND) {
46907bd3a2e2SSriharsha Basavapatna 		/* Unmap the remote dring which is imported from the peer */
46917bd3a2e2SSriharsha Basavapatna 		vsw_unmap_dring(ldcp);
46927bd3a2e2SSriharsha Basavapatna 	} else {
46937bd3a2e2SSriharsha Basavapatna 		/* Destroy the local dring which is exported to the peer */
46947bd3a2e2SSriharsha Basavapatna 		vsw_destroy_dring(ldcp);
469506db247cSraghuram 	}
469606db247cSraghuram 
469706db247cSraghuram 	D1(ldcp->ldc_vswp, "%s (%lld): exit", __func__, ldcp->ldc_id);
469806db247cSraghuram }
469906db247cSraghuram 
470006db247cSraghuram /*
47017bd3a2e2SSriharsha Basavapatna  * Destroy the descriptor ring.
470206db247cSraghuram  */
470306db247cSraghuram static void
vsw_destroy_dring(vsw_ldc_t * ldcp)47047bd3a2e2SSriharsha Basavapatna vsw_destroy_dring(vsw_ldc_t *ldcp)
470506db247cSraghuram {
47067bd3a2e2SSriharsha Basavapatna 	lane_t	*lp = &ldcp->lane_out;
47076f09f0feSWENTAO YANG 
47087bd3a2e2SSriharsha Basavapatna 	if (lp->dring_mode == VIO_RX_DRING_DATA) {
47097bd3a2e2SSriharsha Basavapatna 		vsw_destroy_rx_dring(ldcp);
47107bd3a2e2SSriharsha Basavapatna 	} else {
47117bd3a2e2SSriharsha Basavapatna 		vsw_destroy_tx_dring(ldcp);
47126f09f0feSWENTAO YANG 	}
471306db247cSraghuram }
471406db247cSraghuram 
471506db247cSraghuram /*
471606db247cSraghuram  * vsw_ldc_tx_worker -- A per LDC worker thread to transmit data.
471706db247cSraghuram  * This thread is woken up by the vsw_portsend to transmit
471806db247cSraghuram  * packets.
471906db247cSraghuram  */
472006db247cSraghuram static void
vsw_ldc_tx_worker(void * arg)472106db247cSraghuram vsw_ldc_tx_worker(void *arg)
472206db247cSraghuram {
472306db247cSraghuram 	callb_cpr_t	cprinfo;
472406db247cSraghuram 	vsw_ldc_t *ldcp = (vsw_ldc_t *)arg;
472506db247cSraghuram 	vsw_t *vswp = ldcp->ldc_vswp;
472606db247cSraghuram 	mblk_t *mp;
472706db247cSraghuram 	mblk_t *tmp;
472806db247cSraghuram 
472906db247cSraghuram 	D1(vswp, "%s(%lld):enter\n", __func__, ldcp->ldc_id);
473006db247cSraghuram 	CALLB_CPR_INIT(&cprinfo, &ldcp->tx_thr_lock, callb_generic_cpr,
473106db247cSraghuram 	    "vnet_tx_thread");
473206db247cSraghuram 	mutex_enter(&ldcp->tx_thr_lock);
473306db247cSraghuram 	while (!(ldcp->tx_thr_flags & VSW_WTHR_STOP)) {
473406db247cSraghuram 
473506db247cSraghuram 		CALLB_CPR_SAFE_BEGIN(&cprinfo);
473606db247cSraghuram 		/*
473706db247cSraghuram 		 * Wait until the data is received or a stop
473806db247cSraghuram 		 * request is received.
473906db247cSraghuram 		 */
474006db247cSraghuram 		while (!(ldcp->tx_thr_flags & VSW_WTHR_STOP) &&
474106db247cSraghuram 		    (ldcp->tx_mhead == NULL)) {
474206db247cSraghuram 			cv_wait(&ldcp->tx_thr_cv, &ldcp->tx_thr_lock);
474306db247cSraghuram 		}
474406db247cSraghuram 		CALLB_CPR_SAFE_END(&cprinfo, &ldcp->tx_thr_lock)
474506db247cSraghuram 
474606db247cSraghuram 		/*
474706db247cSraghuram 		 * First process the stop request.
474806db247cSraghuram 		 */
474906db247cSraghuram 		if (ldcp->tx_thr_flags & VSW_WTHR_STOP) {
475006db247cSraghuram 			D2(vswp, "%s(%lld):tx thread stopped\n",
475106db247cSraghuram 			    __func__, ldcp->ldc_id);
475206db247cSraghuram 			break;
475306db247cSraghuram 		}
475406db247cSraghuram 		mp = ldcp->tx_mhead;
475506db247cSraghuram 		ldcp->tx_mhead = ldcp->tx_mtail = NULL;
4756f0ca1d9aSsb 		ldcp->tx_cnt = 0;
475706db247cSraghuram 		mutex_exit(&ldcp->tx_thr_lock);
475806db247cSraghuram 		D2(vswp, "%s(%lld):calling vsw_ldcsend\n",
475906db247cSraghuram 		    __func__, ldcp->ldc_id);
476006db247cSraghuram 		while (mp != NULL) {
476106db247cSraghuram 			tmp = mp->b_next;
476206db247cSraghuram 			mp->b_next = mp->b_prev = NULL;
476306db247cSraghuram 			(void) vsw_ldcsend(ldcp, mp, vsw_ldc_tx_retries);
476406db247cSraghuram 			mp = tmp;
476506db247cSraghuram 		}
476606db247cSraghuram 		mutex_enter(&ldcp->tx_thr_lock);
476706db247cSraghuram 	}
476806db247cSraghuram 
476906db247cSraghuram 	/*
477006db247cSraghuram 	 * Update the run status and wakeup the thread that
477106db247cSraghuram 	 * has sent the stop request.
477206db247cSraghuram 	 */
47736f09f0feSWENTAO YANG 	ldcp->tx_thr_flags &= ~VSW_WTHR_STOP;
47746f09f0feSWENTAO YANG 	ldcp->tx_thread = NULL;
477506db247cSraghuram 	CALLB_CPR_EXIT(&cprinfo);
477606db247cSraghuram 	D1(vswp, "%s(%lld):exit\n", __func__, ldcp->ldc_id);
477706db247cSraghuram 	thread_exit();
477806db247cSraghuram }
477906db247cSraghuram 
478006db247cSraghuram /* vsw_stop_tx_thread -- Co-ordinate with receive thread to stop it */
478106db247cSraghuram static void
vsw_stop_tx_thread(vsw_ldc_t * ldcp)478206db247cSraghuram vsw_stop_tx_thread(vsw_ldc_t *ldcp)
478306db247cSraghuram {
47846f09f0feSWENTAO YANG 	kt_did_t	tid = 0;
47856f09f0feSWENTAO YANG 	vsw_t		*vswp = ldcp->ldc_vswp;
478606db247cSraghuram 
478706db247cSraghuram 	D1(vswp, "%s(%lld):enter\n", __func__, ldcp->ldc_id);
478806db247cSraghuram 	/*
478906db247cSraghuram 	 * Send a stop request by setting the stop flag and
479006db247cSraghuram 	 * wait until the receive thread stops.
479106db247cSraghuram 	 */
479206db247cSraghuram 	mutex_enter(&ldcp->tx_thr_lock);
47936f09f0feSWENTAO YANG 	if (ldcp->tx_thread != NULL) {
47946f09f0feSWENTAO YANG 		tid = ldcp->tx_thread->t_did;
479506db247cSraghuram 		ldcp->tx_thr_flags |= VSW_WTHR_STOP;
479606db247cSraghuram 		cv_signal(&ldcp->tx_thr_cv);
479706db247cSraghuram 	}
479806db247cSraghuram 	mutex_exit(&ldcp->tx_thr_lock);
47996f09f0feSWENTAO YANG 
48006f09f0feSWENTAO YANG 	if (tid != 0) {
48016f09f0feSWENTAO YANG 		thread_join(tid);
48026f09f0feSWENTAO YANG 	}
48036f09f0feSWENTAO YANG 
480406db247cSraghuram 	D1(vswp, "%s(%lld):exit\n", __func__, ldcp->ldc_id);
480506db247cSraghuram }
480606db247cSraghuram 
480734f94fbcSWENTAO YANG static int
vsw_mapin_avail(vsw_ldc_t * ldcp)480834f94fbcSWENTAO YANG vsw_mapin_avail(vsw_ldc_t *ldcp)
480934f94fbcSWENTAO YANG {
481034f94fbcSWENTAO YANG 	int		rv;
481134f94fbcSWENTAO YANG 	ldc_info_t	info;
481234f94fbcSWENTAO YANG 	uint64_t	mapin_sz_req;
481334f94fbcSWENTAO YANG 	uint64_t	dblk_sz;
481434f94fbcSWENTAO YANG 	vsw_t		*vswp = ldcp->ldc_vswp;
481534f94fbcSWENTAO YANG 
481634f94fbcSWENTAO YANG 	rv = ldc_info(ldcp->ldc_handle, &info);
481734f94fbcSWENTAO YANG 	if (rv != 0) {
481834f94fbcSWENTAO YANG 		return (B_FALSE);
481934f94fbcSWENTAO YANG 	}
482034f94fbcSWENTAO YANG 
482134f94fbcSWENTAO YANG 	dblk_sz = RXDRING_DBLK_SZ(vswp->max_frame_size);
482234f94fbcSWENTAO YANG 	mapin_sz_req = (VSW_RXDRING_NRBUFS * dblk_sz);
482334f94fbcSWENTAO YANG 
482434f94fbcSWENTAO YANG 	if (info.direct_map_size_max >= mapin_sz_req) {
482534f94fbcSWENTAO YANG 		return (B_TRUE);
482634f94fbcSWENTAO YANG 	}
482734f94fbcSWENTAO YANG 
482834f94fbcSWENTAO YANG 	return (B_FALSE);
482934f94fbcSWENTAO YANG }
483034f94fbcSWENTAO YANG 
483106db247cSraghuram /*
483206db247cSraghuram  * Debugging routines
483306db247cSraghuram  */
483406db247cSraghuram static void
display_state(void)483506db247cSraghuram display_state(void)
483606db247cSraghuram {
483706db247cSraghuram 	vsw_t		*vswp;
483806db247cSraghuram 	vsw_port_list_t	*plist;
4839*6e472272SToomas Soome 	vsw_port_t	*port;
4840*6e472272SToomas Soome 	vsw_ldc_t	*ldcp;
4841*6e472272SToomas Soome 	extern vsw_t	*vsw_head;
484206db247cSraghuram 
484306db247cSraghuram 	cmn_err(CE_NOTE, "***** system state *****");
484406db247cSraghuram 
484506db247cSraghuram 	for (vswp = vsw_head; vswp; vswp = vswp->next) {
484606db247cSraghuram 		plist = &vswp->plist;
484706db247cSraghuram 		READ_ENTER(&plist->lockrw);
484806db247cSraghuram 		cmn_err(CE_CONT, "vsw instance %d has %d ports attached\n",
484906db247cSraghuram 		    vswp->instance, plist->num_ports);
485006db247cSraghuram 
485106db247cSraghuram 		for (port = plist->head; port != NULL; port = port->p_next) {
485206db247cSraghuram 			cmn_err(CE_CONT, "port %d : %d ldcs attached\n",
4853c1c61f44Ssb 			    port->p_instance, port->num_ldcs);
48547bd3a2e2SSriharsha Basavapatna 			ldcp = port->ldcp;
48557bd3a2e2SSriharsha Basavapatna 			cmn_err(CE_CONT, "chan %lu : dev %d : "
48567bd3a2e2SSriharsha Basavapatna 			    "status %d : phase %u\n",
48577bd3a2e2SSriharsha Basavapatna 			    ldcp->ldc_id, ldcp->dev_class,
48587bd3a2e2SSriharsha Basavapatna 			    ldcp->ldc_status, ldcp->hphase);
48597bd3a2e2SSriharsha Basavapatna 			cmn_err(CE_CONT, "chan %lu : lsession %lu : "
48607bd3a2e2SSriharsha Basavapatna 			    "psession %lu\n", ldcp->ldc_id,
48617bd3a2e2SSriharsha Basavapatna 			    ldcp->local_session, ldcp->peer_session);
48627bd3a2e2SSriharsha Basavapatna 
48637bd3a2e2SSriharsha Basavapatna 			cmn_err(CE_CONT, "Inbound lane:\n");
48647bd3a2e2SSriharsha Basavapatna 			display_lane(&ldcp->lane_in);
48657bd3a2e2SSriharsha Basavapatna 			cmn_err(CE_CONT, "Outbound lane:\n");
48667bd3a2e2SSriharsha Basavapatna 			display_lane(&ldcp->lane_out);
486706db247cSraghuram 		}
486806db247cSraghuram 		RW_EXIT(&plist->lockrw);
486906db247cSraghuram 	}
487006db247cSraghuram 	cmn_err(CE_NOTE, "***** system state *****");
487106db247cSraghuram }
487206db247cSraghuram 
487306db247cSraghuram static void
display_lane(lane_t * lp)487406db247cSraghuram display_lane(lane_t *lp)
487506db247cSraghuram {
48767bd3a2e2SSriharsha Basavapatna 	dring_info_t	*drp = lp->dringp;
487706db247cSraghuram 
487806db247cSraghuram 	cmn_err(CE_CONT, "ver 0x%x:0x%x : state %lx : mtu 0x%lx\n",
487906db247cSraghuram 	    lp->ver_major, lp->ver_minor, lp->lstate, lp->mtu);
488006db247cSraghuram 	cmn_err(CE_CONT, "addr_type %d : addr 0x%lx : xmode %d\n",
488106db247cSraghuram 	    lp->addr_type, lp->addr, lp->xfer_mode);
488206db247cSraghuram 	cmn_err(CE_CONT, "dringp 0x%lx\n", (uint64_t)lp->dringp);
488306db247cSraghuram 
488406db247cSraghuram 	cmn_err(CE_CONT, "Dring info:\n");
48857bd3a2e2SSriharsha Basavapatna 	cmn_err(CE_CONT, "\tnum_desc %u : dsize %u\n",
48867bd3a2e2SSriharsha Basavapatna 	    drp->num_descriptors, drp->descriptor_size);
48877bd3a2e2SSriharsha Basavapatna 	cmn_err(CE_CONT, "\thandle 0x%lx\n", drp->dring_handle);
48887bd3a2e2SSriharsha Basavapatna 	cmn_err(CE_CONT, "\tpub_addr 0x%lx : priv_addr 0x%lx\n",
48897bd3a2e2SSriharsha Basavapatna 	    (uint64_t)drp->pub_addr, (uint64_t)drp->priv_addr);
48907bd3a2e2SSriharsha Basavapatna 	cmn_err(CE_CONT, "\tident 0x%lx : end_idx %lu\n",
48917bd3a2e2SSriharsha Basavapatna 	    drp->ident, drp->end_idx);
48927bd3a2e2SSriharsha Basavapatna 	display_ring(drp);
489306db247cSraghuram }
489406db247cSraghuram 
489506db247cSraghuram static void
display_ring(dring_info_t * dringp)489606db247cSraghuram display_ring(dring_info_t *dringp)
489706db247cSraghuram {
489806db247cSraghuram 	uint64_t		i;
489906db247cSraghuram 	uint64_t		priv_count = 0;
490006db247cSraghuram 	uint64_t		pub_count = 0;
490106db247cSraghuram 	vnet_public_desc_t	*pub_addr = NULL;
490206db247cSraghuram 	vsw_private_desc_t	*priv_addr = NULL;
490306db247cSraghuram 
49047bd3a2e2SSriharsha Basavapatna 	for (i = 0; i < vsw_num_descriptors; i++) {
490506db247cSraghuram 		if (dringp->pub_addr != NULL) {
490606db247cSraghuram 			pub_addr = (vnet_public_desc_t *)dringp->pub_addr + i;
490706db247cSraghuram 
490806db247cSraghuram 			if (pub_addr->hdr.dstate == VIO_DESC_FREE)
490906db247cSraghuram 				pub_count++;
491006db247cSraghuram 		}
491106db247cSraghuram 
491206db247cSraghuram 		if (dringp->priv_addr != NULL) {
491306db247cSraghuram 			priv_addr = (vsw_private_desc_t *)dringp->priv_addr + i;
491406db247cSraghuram 
491506db247cSraghuram 			if (priv_addr->dstate == VIO_DESC_FREE)
491606db247cSraghuram 				priv_count++;
491706db247cSraghuram 		}
491806db247cSraghuram 	}
491906db247cSraghuram 	cmn_err(CE_CONT, "\t%lu elements: %lu priv free: %lu pub free\n",
492006db247cSraghuram 	    i, priv_count, pub_count);
492106db247cSraghuram }
492206db247cSraghuram 
492306db247cSraghuram static void
dump_flags(uint64_t state)492406db247cSraghuram dump_flags(uint64_t state)
492506db247cSraghuram {
492606db247cSraghuram 	int	i;
492706db247cSraghuram 
492806db247cSraghuram 	typedef struct flag_name {
492906db247cSraghuram 		int	flag_val;
493006db247cSraghuram 		char	*flag_name;
493106db247cSraghuram 	} flag_name_t;
493206db247cSraghuram 
493306db247cSraghuram 	flag_name_t	flags[] = {
493406db247cSraghuram 		VSW_VER_INFO_SENT, "VSW_VER_INFO_SENT",
493506db247cSraghuram 		VSW_VER_INFO_RECV, "VSW_VER_INFO_RECV",
493606db247cSraghuram 		VSW_VER_ACK_RECV, "VSW_VER_ACK_RECV",
493706db247cSraghuram 		VSW_VER_ACK_SENT, "VSW_VER_ACK_SENT",
493806db247cSraghuram 		VSW_VER_NACK_RECV, "VSW_VER_NACK_RECV",
493906db247cSraghuram 		VSW_VER_NACK_SENT, "VSW_VER_NACK_SENT",
494006db247cSraghuram 		VSW_ATTR_INFO_SENT, "VSW_ATTR_INFO_SENT",
494106db247cSraghuram 		VSW_ATTR_INFO_RECV, "VSW_ATTR_INFO_RECV",
494206db247cSraghuram 		VSW_ATTR_ACK_SENT, "VSW_ATTR_ACK_SENT",
494306db247cSraghuram 		VSW_ATTR_ACK_RECV, "VSW_ATTR_ACK_RECV",
494406db247cSraghuram 		VSW_ATTR_NACK_SENT, "VSW_ATTR_NACK_SENT",
494506db247cSraghuram 		VSW_ATTR_NACK_RECV, "VSW_ATTR_NACK_RECV",
494606db247cSraghuram 		VSW_DRING_INFO_SENT, "VSW_DRING_INFO_SENT",
494706db247cSraghuram 		VSW_DRING_INFO_RECV, "VSW_DRING_INFO_RECV",
494806db247cSraghuram 		VSW_DRING_ACK_SENT, "VSW_DRING_ACK_SENT",
494906db247cSraghuram 		VSW_DRING_ACK_RECV, "VSW_DRING_ACK_RECV",
495006db247cSraghuram 		VSW_DRING_NACK_SENT, "VSW_DRING_NACK_SENT",
495106db247cSraghuram 		VSW_DRING_NACK_RECV, "VSW_DRING_NACK_RECV",
495206db247cSraghuram 		VSW_RDX_INFO_SENT, "VSW_RDX_INFO_SENT",
495306db247cSraghuram 		VSW_RDX_INFO_RECV, "VSW_RDX_INFO_RECV",
495406db247cSraghuram 		VSW_RDX_ACK_SENT, "VSW_RDX_ACK_SENT",
495506db247cSraghuram 		VSW_RDX_ACK_RECV, "VSW_RDX_ACK_RECV",
495606db247cSraghuram 		VSW_RDX_NACK_SENT, "VSW_RDX_NACK_SENT",
495706db247cSraghuram 		VSW_RDX_NACK_RECV, "VSW_RDX_NACK_RECV",
495806db247cSraghuram 		VSW_MCST_INFO_SENT, "VSW_MCST_INFO_SENT",
495906db247cSraghuram 		VSW_MCST_INFO_RECV, "VSW_MCST_INFO_RECV",
496006db247cSraghuram 		VSW_MCST_ACK_SENT, "VSW_MCST_ACK_SENT",
496106db247cSraghuram 		VSW_MCST_ACK_RECV, "VSW_MCST_ACK_RECV",
496206db247cSraghuram 		VSW_MCST_NACK_SENT, "VSW_MCST_NACK_SENT",
496306db247cSraghuram 		VSW_MCST_NACK_RECV, "VSW_MCST_NACK_RECV",
496406db247cSraghuram 		VSW_LANE_ACTIVE, "VSW_LANE_ACTIVE"};
496506db247cSraghuram 
496606db247cSraghuram 	DERR(NULL, "DUMP_FLAGS: %llx\n", state);
496706db247cSraghuram 	for (i = 0; i < sizeof (flags)/sizeof (flag_name_t); i++) {
496806db247cSraghuram 		if (state & flags[i].flag_val)
496906db247cSraghuram 			DERR(NULL, "DUMP_FLAGS %s", flags[i].flag_name);
497006db247cSraghuram 	}
497106db247cSraghuram }
4972