vsw_hio.c revision 12011:2377022c7a2d
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27#include <sys/types.h>
28#include <sys/errno.h>
29#include <sys/debug.h>
30#include <sys/time.h>
31#include <sys/sysmacros.h>
32#include <sys/systm.h>
33#include <sys/user.h>
34#include <sys/stropts.h>
35#include <sys/stream.h>
36#include <sys/strlog.h>
37#include <sys/strsubr.h>
38#include <sys/cmn_err.h>
39#include <sys/cpu.h>
40#include <sys/kmem.h>
41#include <sys/conf.h>
42#include <sys/ddi.h>
43#include <sys/sunddi.h>
44#include <sys/ksynch.h>
45#include <sys/stat.h>
46#include <sys/kstat.h>
47#include <sys/vtrace.h>
48#include <sys/strsun.h>
49#include <sys/dlpi.h>
50#include <sys/ethernet.h>
51#include <net/if.h>
52#include <sys/varargs.h>
53#include <sys/machsystm.h>
54#include <sys/modctl.h>
55#include <sys/modhash.h>
56#include <sys/mac_provider.h>
57#include <sys/mac_ether.h>
58#include <sys/taskq.h>
59#include <sys/note.h>
60#include <sys/mach_descrip.h>
61#include <sys/mac.h>
62#include <sys/mdeg.h>
63#include <sys/ldc.h>
64#include <sys/vsw_fdb.h>
65#include <sys/vsw.h>
66#include <sys/vio_mailbox.h>
67#include <sys/vnet_mailbox.h>
68#include <sys/vnet_common.h>
69#include <sys/vio_util.h>
70#include <sys/sdt.h>
71#include <sys/atomic.h>
72#include <sys/callb.h>
73
74
75#define	VSW_DDS_NEXT_REQID(vsharep)	(++vsharep->vs_req_id)
76
77extern boolean_t vsw_hio_enabled;		/* HybridIO enabled? */
78extern int vsw_hio_max_cleanup_retries;
79extern int vsw_hio_cleanup_delay;
80
81/* Functions imported from other files */
82extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
83extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
84extern void vsw_port_mac_reconfig(vsw_port_t *portp, boolean_t update_vlans,
85    uint16_t new_pvid, vsw_vlanid_t *new_vids, int new_nvids);
86
87/* Functions exported to other files */
88void vsw_hio_init(vsw_t *vswp);
89void vsw_hio_cleanup(vsw_t *vswp);
90void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
91void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
92void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
93void vsw_hio_start_ports(vsw_t *vswp);
94void vsw_hio_stop_port(vsw_port_t *portp);
95
96/* Support functions */
97static void vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot);
98static vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp);
99static void vsw_hio_free_share(vsw_share_t *vsharep);
100static vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp);
101static vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id);
102static vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp);
103static int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass,
104    uint64_t cookie, uint64_t macaddr, uint32_t req_id);
105static int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack);
106static int vsw_hio_send_delshare_msg(vsw_share_t *vsharep);
107static boolean_t vsw_hio_reboot_callb(void *arg, int code);
108static boolean_t vsw_hio_panic_callb(void *arg, int code);
109
110/*
111 * Locking strategy for HybridIO is followed as below:
112 *
113 *	- As the Shares are associated with a network device, the
114 *	  the global lock('vswp>mac_lock') is used for all Shares
115 *	  related operations.
116 *	- The 'port->maccl_rwlock' is used to synchronize only the
117 *	  the operations that operate on that port's mac client. That
118 *	  is, the share_bind and unbind operations only.
119 *
120 *	- The locking hierarchy follows that the global mac_lock is
121 *	  acquired first and then the ports mac client lock(maccl_rwlock)
122 */
123
124
125static kstat_t *vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp);
126static void vsw_hio_destroy_kstats(vsw_t *vswp);
127static int vsw_hio_kstats_update(kstat_t *ksp, int rw);
128
129/*
130 * vsw_hio_init -- Initialize the HybridIO related info.
131 *	- Query SHARES and RINGS capability. Both capabilities
132 *	  need to be supported by the physical-device.
133 */
134void
135vsw_hio_init(vsw_t *vswp)
136{
137	vsw_hio_t	*hiop = &vswp->vhio;
138	int		num_shares;
139	int		i;
140
141	ASSERT(MUTEX_HELD(&vswp->mac_lock));
142	D1(vswp, "%s:enter\n", __func__);
143	if (vsw_hio_enabled == B_FALSE) {
144		return;
145	}
146
147	vswp->hio_capable = B_FALSE;
148	num_shares = mac_share_capable(vswp->mh);
149	if (num_shares == 0) {
150		D2(vswp, "%s: %s is not HybridIO capable\n", __func__,
151		    vswp->physname);
152		return;
153	}
154	hiop->vh_num_shares = num_shares;
155	hiop->vh_shares = kmem_zalloc((sizeof (vsw_share_t) *
156	    hiop->vh_num_shares), KM_SLEEP);
157	for (i = 0; i < hiop->vh_num_shares; i++) {
158		hiop->vh_shares[i].vs_state = VSW_SHARE_FREE;
159		hiop->vh_shares[i].vs_index = i;
160		hiop->vh_shares[i].vs_vswp = vswp;
161	}
162	vswp->hio_capable = B_TRUE;
163
164	/*
165	 * Register to get reboot and panic events so that
166	 * we can cleanup HybridIO resources gracefully.
167	 */
168	vswp->hio_reboot_cb_id = callb_add(vsw_hio_reboot_callb,
169	    (void *)vswp, CB_CL_MDBOOT, "vsw_hio");
170
171	vswp->hio_panic_cb_id = callb_add(vsw_hio_panic_callb,
172	    (void *)vswp, CB_CL_PANIC, "vsw_hio");
173
174	/* setup kstats for hybrid resources */
175	hiop->vh_ksp = vsw_hio_setup_kstats(DRV_NAME, "hio", vswp);
176	if (hiop->vh_ksp == NULL) {
177		DERR(vswp, "%s: kstats setup failed", __func__);
178	}
179
180	D2(vswp, "%s: %s is HybridIO capable num_shares=%d\n", __func__,
181	    vswp->physname, hiop->vh_num_shares);
182	D1(vswp, "%s:exit\n", __func__);
183}
184
185/*
186 * vsw_hio_alloc_share -- Allocate and setup the share for a guest domain.
187 *	- Allocate a free share.
188 *	- Bind the Guest's MAC address.
189 */
190static vsw_share_t *
191vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp)
192{
193	vsw_share_t	*vsharep;
194	vsw_port_t	*portp = ldcp->ldc_port;
195	uint64_t	ldc_id = ldcp->ldc_id;
196	int		rv;
197
198	D1(vswp, "%s:enter\n", __func__);
199	vsharep = vsw_hio_find_free_share(vswp);
200	if (vsharep == NULL) {
201		/* No free shares available */
202		return (NULL);
203	}
204
205	WRITE_ENTER(&portp->maccl_rwlock);
206	rv = mac_share_bind(portp->p_mch, ldc_id, &vsharep->vs_cookie);
207	RW_EXIT(&portp->maccl_rwlock);
208	if (rv != 0) {
209		return (NULL);
210	}
211
212	/* Cache some useful info */
213	vsharep->vs_ldcid = ldcp->ldc_id;
214	vsharep->vs_macaddr = vnet_macaddr_strtoul(
215	    portp->p_macaddr.ether_addr_octet);
216	vsharep->vs_portp = ldcp->ldc_port;
217	vsharep->vs_state |= VSW_SHARE_ASSIGNED;
218
219	D1(vswp, "%s:exit\n", __func__);
220	return (vsharep);
221}
222
223/*
224 * vsw_hio_find_free_share -- Find a free Share.
225 */
226static vsw_share_t *
227vsw_hio_find_free_share(vsw_t *vswp)
228{
229	vsw_hio_t *hiop = &vswp->vhio;
230	vsw_share_t *vsharep;
231	int i;
232
233	D1(vswp, "%s:enter\n", __func__);
234	for (i = 0; i < hiop->vh_num_shares; i++) {
235		vsharep = &hiop->vh_shares[i];
236		if (vsharep->vs_state == VSW_SHARE_FREE) {
237			D1(vswp, "%s:Returning free share(%d)\n",
238			    __func__, vsharep->vs_index);
239			return (vsharep);
240		}
241	}
242	D1(vswp, "%s:no free share\n", __func__);
243	return (NULL);
244}
245
246/*
247 * vsw_hio_find_vshare_ldcid -- Given ldc_id, find the corresponding
248 *	share structure.
249 */
250static vsw_share_t *
251vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id)
252{
253	vsw_hio_t *hiop = &vswp->vhio;
254	vsw_share_t *vsharep;
255	int i;
256
257	D1(vswp, "%s:enter, ldc=0x%lx", __func__, ldc_id);
258	for (i = 0; i < hiop->vh_num_shares; i++) {
259		vsharep = &hiop->vh_shares[i];
260		if (vsharep->vs_state == VSW_SHARE_FREE) {
261			continue;
262		}
263		if (vsharep->vs_ldcid == ldc_id) {
264			D1(vswp, "%s:returning share(%d)",
265			    __func__, vsharep->vs_index);
266			return (vsharep);
267		}
268	}
269	D1(vswp, "%s:returning NULL", __func__);
270	return (NULL);
271}
272
273/*
274 * vsw_hio_find_vshare_port -- Given portp, find the corresponding
275 *	share structure.
276 */
277static vsw_share_t *
278vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp)
279{
280	vsw_hio_t *hiop = &vswp->vhio;
281	vsw_share_t *vsharep;
282	int i;
283
284	D1(vswp, "%s:enter, portp=0x%p", __func__, portp);
285	for (i = 0; i < hiop->vh_num_shares; i++) {
286		vsharep = &hiop->vh_shares[i];
287		if (vsharep->vs_state == VSW_SHARE_FREE) {
288			continue;
289		}
290		if (vsharep->vs_portp == portp) {
291			D1(vswp, "%s:returning share(%d)",
292			    __func__, vsharep->vs_index);
293			return (vsharep);
294		}
295	}
296	D1(vswp, "%s:returning NULL", __func__);
297	return (NULL);
298}
299
300/*
301 * vsw_hio_free_share -- Unbind the MAC address and free share.
302 */
303static void
304vsw_hio_free_share(vsw_share_t *vsharep)
305{
306	vsw_t		*vswp = vsharep->vs_vswp;
307	vsw_port_t	*portp = vsharep->vs_portp;
308
309	D1(vswp, "%s:enter\n", __func__);
310
311	WRITE_ENTER(&portp->maccl_rwlock);
312	mac_share_unbind(portp->p_mch);
313	RW_EXIT(&portp->maccl_rwlock);
314	vsharep->vs_state = VSW_SHARE_FREE;
315	vsharep->vs_macaddr = 0;
316	vsharep->vs_portp = NULL;
317
318	/* DERR only for printing by default */
319	DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX",
320	    vsharep->vs_ldcid, vsharep->vs_cookie);
321	D1(vswp, "%s:exit\n", __func__);
322}
323
324
325/*
326 * vsw_hio_cleanup -- Cleanup the HybridIO. It unregisters the callbs
327 *	and frees all shares.
328 */
329void
330vsw_hio_cleanup(vsw_t *vswp)
331{
332	D1(vswp, "%s:enter\n", __func__);
333
334	/* Unregister reboot and panic callbs. */
335	if (vswp->hio_reboot_cb_id) {
336		(void) callb_delete(vswp->hio_reboot_cb_id);
337		vswp->hio_reboot_cb_id = 0;
338	}
339	if (vswp->hio_panic_cb_id) {
340		(void) callb_delete(vswp->hio_panic_cb_id);
341		vswp->hio_panic_cb_id = 0;
342	}
343	vsw_hio_free_all_shares(vswp, B_FALSE);
344	vsw_hio_destroy_kstats(vswp);
345	D1(vswp, "%s:exit\n", __func__);
346}
347
348/*
349 * vsw_hio_free_all_shares -- A routine to free all shares gracefully.
350 *	The following are the steps followed to accomplish this:
351 *
352 *	- First clear 'hio_capable' to avoid further share allocations.
353 *	- If a share is in accepted(ACKD) state, that means the guest
354 *	  has HybridIO setup etc. If so, send a DEL_SHARE message and
355 *	  give some time(delay) for the guest to ACK.
356 *	- If the Share is another state, give some time to transition to
357 *	  ACKD state, then try the above.
358 *	- After max retries, reset the ports to brute force the shares
359 *	  to be freed. Give a little delay for the LDC reset code to
360 *	  free the Share.
361 */
362static void
363vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot)
364{
365	vsw_hio_t	*hiop = &vswp->vhio;
366	vsw_port_list_t	*plist = &vswp->plist;
367	vsw_share_t	*vsharep;
368	int		free_shares = 0;
369	int		max_retries = vsw_hio_max_cleanup_retries;
370	int		i;
371
372	D1(vswp, "%s:enter\n", __func__);
373
374	/*
375	 * Acquire plist->lockrw to make the locking a bit easier
376	 * and keep the ports in a stable state while we are cleaningup
377	 * HybridIO.
378	 */
379	READ_ENTER(&plist->lockrw);
380	mutex_enter(&vswp->mac_lock);
381	/*
382	 * first clear the hio_capable flag so that no more
383	 * HybridIO operations are initiated.
384	 */
385	vswp->hio_capable = B_FALSE;
386
387	do {
388		free_shares = 0;
389		for (i = 0; i < hiop->vh_num_shares; i++) {
390			vsharep = &hiop->vh_shares[i];
391			if (vsharep->vs_state == VSW_SHARE_FREE) {
392				free_shares++;
393				continue;
394			}
395			/*
396			 * If the share is in DDS_ACKD state, then
397			 * send DEL_SHARE message so that guest can
398			 * release its Hybrid resource.
399			 */
400			if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
401				int rv;
402
403				/* send DDS_DEL_SHARE */
404				D1(vswp, "%s:sending DEL_SHARE msg for "
405				    "share(%d)", __func__, vsharep->vs_index);
406				rv = vsw_hio_send_delshare_msg(vsharep);
407				if (rv != 0) {
408					/*
409					 * No alternative, reset the port
410					 * to force the release of Hybrid
411					 * resources.
412					 */
413					vsw_hio_port_reset(vsharep->vs_portp,
414					    B_FALSE);
415				}
416			}
417			if (max_retries == 1) {
418				/*
419				 * Last retry,  reset the port.
420				 * If it is reboot case, issue an immediate
421				 * reset.
422				 */
423				DWARN(vswp, "%s:All retries failed, "
424				    " cause a reset to trigger cleanup for "
425				    "share(%d)", __func__, vsharep->vs_index);
426				vsw_hio_port_reset(vsharep->vs_portp, reboot);
427			}
428		}
429		if (free_shares == hiop->vh_num_shares) {
430			/* Clean up is done */
431			break;
432		}
433		/*
434		 * Release the lock so that reply for DEL_SHARE
435		 * messages come and get processed, that is, shares
436		 * get freed.
437		 * This delay is also needed for the port reset to
438		 * release the Hybrid resource.
439		 */
440		mutex_exit(&vswp->mac_lock);
441		drv_usecwait(vsw_hio_cleanup_delay);
442		mutex_enter(&vswp->mac_lock);
443		max_retries--;
444	} while ((free_shares < hiop->vh_num_shares) && (max_retries > 0));
445
446	/* By now, all shares should be freed */
447	if (free_shares != hiop->vh_num_shares) {
448		if (reboot == B_FALSE) {
449			cmn_err(CE_NOTE, "vsw%d: All physical resources "
450			    "could not be freed", vswp->instance);
451		}
452	}
453
454	kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares);
455	hiop->vh_shares = NULL;
456	hiop->vh_num_shares = 0;
457	mutex_exit(&vswp->mac_lock);
458	RW_EXIT(&plist->lockrw);
459	D1(vswp, "%s:exit\n", __func__);
460}
461
462/*
463 * vsw_hio_start_ports -- Start HybridIO for ports that have
464 *	already established connection before HybridIO is intialized.
465 */
466void
467vsw_hio_start_ports(vsw_t *vswp)
468{
469	vsw_port_list_t	*plist = &vswp->plist;
470	vsw_port_t	*portp;
471	vsw_share_t	*vsharep;
472	boolean_t	reset;
473
474	if (vswp->hio_capable == B_FALSE) {
475		return;
476	}
477	READ_ENTER(&plist->lockrw);
478	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
479		if ((portp->p_hio_enabled == B_FALSE) ||
480		    (portp->p_hio_capable == B_FALSE)) {
481			continue;
482		}
483
484		reset = B_FALSE;
485		mutex_enter(&vswp->mac_lock);
486		vsharep = vsw_hio_find_vshare_port(vswp, portp);
487		if (vsharep == NULL) {
488			reset = B_TRUE;
489		}
490		mutex_exit(&vswp->mac_lock);
491
492		if (reset == B_TRUE) {
493			/* Cause a rest to trigger HybridIO setup */
494			vsw_hio_port_reset(portp, B_FALSE);
495		}
496	}
497	RW_EXIT(&plist->lockrw);
498}
499
500/*
501 * vsw_hio_start -- Start HybridIO for a guest(given LDC)
502 */
503void
504vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp)
505{
506	vsw_share_t	*vsharep;
507	uint32_t	req_id;
508	int		rv;
509
510	D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
511	mutex_enter(&vswp->mac_lock);
512	if (vswp->hio_capable == B_FALSE) {
513		mutex_exit(&vswp->mac_lock);
514		D2(vswp, "%s:not HIO capable", __func__);
515		return;
516	}
517
518	/* Verify if a share was already allocated */
519	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
520	if (vsharep != NULL) {
521		mutex_exit(&vswp->mac_lock);
522		D2(vswp, "%s:Share already allocated to ldc=0x%lx",
523		    __func__, ldcp->ldc_id);
524		return;
525	}
526	vsharep = vsw_hio_alloc_share(vswp, ldcp);
527	if (vsharep == NULL) {
528		mutex_exit(&vswp->mac_lock);
529		D2(vswp, "%s: no Share available for ldc=0x%lx",
530		    __func__, ldcp->ldc_id);
531		return;
532	}
533	req_id = VSW_DDS_NEXT_REQID(vsharep);
534	rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie,
535	    vsharep->vs_macaddr, req_id);
536	if (rv != 0) {
537		/*
538		 * Failed to send a DDS message, so cleanup now.
539		 */
540		vsw_hio_free_share(vsharep);
541		mutex_exit(&vswp->mac_lock);
542		return;
543	}
544	vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD;
545	vsharep->vs_state |= VSW_SHARE_DDS_SENT;
546	mutex_exit(&vswp->mac_lock);
547
548	/* DERR only to print by default */
549	DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX",
550	    ldcp->ldc_id, vsharep->vs_cookie);
551
552	D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
553}
554
555/*
556 * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc).
557 */
558void
559vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp)
560{
561	vsw_share_t *vsharep;
562
563	D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
564
565	mutex_enter(&vswp->mac_lock);
566	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
567	if (vsharep == NULL) {
568		D1(vswp, "%s:no share found for ldc=0x%lx",
569		    __func__, ldcp->ldc_id);
570		mutex_exit(&vswp->mac_lock);
571		return;
572	}
573	vsw_hio_free_share(vsharep);
574	mutex_exit(&vswp->mac_lock);
575
576	D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
577}
578
579/*
580 * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the	guest.
581 */
582static int
583vsw_hio_send_delshare_msg(vsw_share_t *vsharep)
584{
585	vsw_t *vswp = vsharep->vs_vswp;
586	vsw_port_t *portp;
587	vsw_ldc_t	*ldcp;
588	uint32_t	req_id;
589	uint64_t	cookie = vsharep->vs_cookie;
590	uint64_t	macaddr = vsharep->vs_macaddr;
591	int		rv;
592
593	ASSERT(MUTEX_HELD(&vswp->mac_lock));
594	mutex_exit(&vswp->mac_lock);
595
596	portp = vsharep->vs_portp;
597	if (portp == NULL) {
598		mutex_enter(&vswp->mac_lock);
599		return (0);
600	}
601
602	ldcp = portp->ldcp;
603	if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) {
604		mutex_enter(&vswp->mac_lock);
605		return (0);
606	}
607	req_id = VSW_DDS_NEXT_REQID(vsharep);
608	rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE,
609	    cookie, macaddr, req_id);
610
611	mutex_enter(&vswp->mac_lock);
612	if (rv == 0) {
613		vsharep->vs_state &= ~VSW_SHARE_DDS_ACKD;
614		vsharep->vs_state |= VSW_SHARE_DDS_SENT;
615	}
616	return (rv);
617}
618
619/*
620 * vsw_send_dds_msg -- Send a DDS message.
621 */
622static int
623vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t
624    cookie, uint64_t macaddr, uint32_t req_id)
625{
626	vsw_t *vswp = ldcp->ldc_port->p_vswp;
627	vio_dds_msg_t	vmsg;
628	dds_share_msg_t	*smsg = &vmsg.msg.share_msg;
629	int rv;
630
631	D1(vswp, "%s:enter\n", __func__);
632	vmsg.tag.vio_msgtype = VIO_TYPE_CTRL;
633	vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO;
634	vmsg.tag.vio_subtype_env = VIO_DDS_INFO;
635	vmsg.tag.vio_sid = ldcp->local_session;
636	vmsg.dds_class = DDS_VNET_NIU;
637	vmsg.dds_subclass = dds_subclass;
638	vmsg.dds_req_id = req_id;
639	smsg->macaddr = macaddr;
640	smsg->cookie = cookie;
641	rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE);
642	D1(vswp, "%s:exit rv=%d\n", __func__, rv);
643	return (rv);
644}
645
646/*
647 * vsw_process_dds_msg -- Process a DDS message received from a guest.
648 */
649void
650vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg)
651{
652	vsw_share_t	*vsharep;
653	vio_dds_msg_t	*dmsg = msg;
654
655	D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id);
656	if (dmsg->dds_class != DDS_VNET_NIU) {
657		/* discard */
658		return;
659	}
660	mutex_enter(&vswp->mac_lock);
661	/*
662	 * We expect to receive DDS messages only from guests that
663	 * have HybridIO started.
664	 */
665	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
666	if (vsharep == NULL) {
667		mutex_exit(&vswp->mac_lock);
668		return;
669	}
670
671	switch (dmsg->dds_subclass) {
672	case DDS_VNET_ADD_SHARE:
673		/* A response for ADD_SHARE message. */
674		D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__);
675		if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
676			DWARN(vswp, "%s: invalid ADD_SHARE response  message "
677			    " share state=0x%X", __func__, vsharep->vs_state);
678			break;
679		}
680
681		if (dmsg->dds_req_id != vsharep->vs_req_id) {
682			DWARN(vswp, "%s: invalid req_id in ADD_SHARE response"
683			    " message req_id=0x%X share's req_id=0x%X",
684			    __func__, dmsg->dds_req_id, vsharep->vs_req_id);
685			break;
686		}
687
688		if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
689			DWARN(vswp, "%s: NACK received for ADD_SHARE"
690			    " message ldcid=0x%lx", __func__, ldcp->ldc_id);
691			/* cleanup for NACK */
692			vsw_hio_free_share(vsharep);
693		} else {
694			D2(vswp, "%s: ACK received for ADD_SHARE", __func__);
695			vsharep->vs_state &= ~VSW_SHARE_DDS_SENT;
696			vsharep->vs_state |= VSW_SHARE_DDS_ACKD;
697		}
698		break;
699
700	case DDS_VNET_DEL_SHARE:
701		/* A response for DEL_SHARE message */
702		D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__);
703		if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
704			DWARN(vswp, "%s: invalid DEL_SHARE response message "
705			    " share state=0x%X", __func__, vsharep->vs_state);
706			break;
707		}
708
709		if (dmsg->dds_req_id != vsharep->vs_req_id) {
710			DWARN(vswp, "%s: invalid req_id in DEL_SHARE response"
711			    " message share req_id=0x%X share's req_id=0x%X",
712			    __func__, dmsg->dds_req_id, vsharep->vs_req_id);
713			break;
714		}
715		if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
716			DWARN(vswp, "%s: NACK received for DEL_SHARE",
717			    __func__);
718		}
719
720		/* There is nothing we can do, free share now */
721		vsw_hio_free_share(vsharep);
722		break;
723
724	case DDS_VNET_REL_SHARE:
725		/* Guest has released Share voluntarily, so free it now */
726		D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__);
727		/* send ACK */
728		(void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE);
729		vsw_hio_free_share(vsharep);
730		break;
731	default:
732		DERR(vswp, "%s: Invalid DDS message type=0x%X",
733		    __func__, dmsg->dds_subclass);
734		break;
735	}
736	mutex_exit(&vswp->mac_lock);
737	D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id);
738}
739
740/*
741 * vsw_send_dds_resp_msg -- Send a DDS response message.
742 */
743static int
744vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack)
745{
746	vsw_t	*vswp = ldcp->ldc_port->p_vswp;
747	int	rv;
748
749	D1(vswp, "%s:enter\n", __func__);
750	if (ack == B_TRUE) {
751		dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK;
752		dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS;
753	} else {
754		dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK;
755		dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL;
756	}
757	rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE);
758	D1(vswp, "%s:exit rv=%d\n", __func__, rv);
759	return (rv);
760}
761
762/*
763 * vsw_hio_port_update -- update Hybrid mode change for a port.
764 */
765void
766vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled)
767{
768	/* Verify if the mode really changed */
769	if (portp->p_hio_enabled == hio_enabled) {
770		return;
771	}
772
773	if (hio_enabled == B_FALSE) {
774		/* Hybrid Mode is disabled, so stop HybridIO */
775		vsw_hio_stop_port(portp);
776		portp->p_hio_enabled = B_FALSE;
777
778		vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0);
779	} else {
780		portp->p_hio_enabled =  B_TRUE;
781		vsw_port_mac_reconfig(portp, B_FALSE, 0, NULL, 0);
782
783		/* reset the port to initiate HybridIO setup */
784		vsw_hio_port_reset(portp, B_FALSE);
785	}
786}
787
788/*
789 * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence
790 *	followed is similar to vsw_hio_free_all_shares().
791 *
792 */
793void
794vsw_hio_stop_port(vsw_port_t *portp)
795{
796	vsw_t *vswp = portp->p_vswp;
797	vsw_share_t *vsharep;
798	int max_retries = vsw_hio_max_cleanup_retries;
799
800	D1(vswp, "%s:enter\n", __func__);
801	mutex_enter(&vswp->mac_lock);
802
803	if (vswp->hio_capable == B_FALSE) {
804		mutex_exit(&vswp->mac_lock);
805		return;
806	}
807
808	vsharep = vsw_hio_find_vshare_port(vswp, portp);
809	if (vsharep == NULL) {
810		mutex_exit(&vswp->mac_lock);
811		return;
812	}
813
814	do {
815		if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
816			int rv;
817
818			/* send DDS_DEL_SHARE */
819			D1(vswp, "%s:sending DEL_SHARE msg for "
820			    "share(%d)", __func__, vsharep->vs_index);
821			rv = vsw_hio_send_delshare_msg(vsharep);
822			if (rv != 0) {
823				/*
824				 * Cause a port reset to trigger
825				 * cleanup.
826				 */
827				vsw_hio_port_reset(vsharep->vs_portp, B_FALSE);
828			}
829		}
830		if (max_retries == 1) {
831			/* last retry */
832			DWARN(vswp, "%s:All retries failed, "
833			    " cause a reset to trigger cleanup for "
834			    "share(%d)", __func__, vsharep->vs_index);
835			vsw_hio_port_reset(vsharep->vs_portp, B_FALSE);
836		}
837
838		/* Check if the share still assigned to this port */
839		if ((vsharep->vs_portp != portp) ||
840		    (vsharep->vs_state == VSW_SHARE_FREE)) {
841			break;
842		}
843
844		/*
845		 * Release the lock so that reply for DEL_SHARE
846		 * messages come and get processed, that is, shares
847		 * get freed.
848		 */
849		mutex_exit(&vswp->mac_lock);
850		drv_usecwait(vsw_hio_cleanup_delay);
851		mutex_enter(&vswp->mac_lock);
852
853		/* Check if the share still assigned to this port */
854		if ((vsharep->vs_portp != portp) ||
855		    (vsharep->vs_state == VSW_SHARE_FREE)) {
856			break;
857		}
858		max_retries--;
859	} while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0));
860
861	mutex_exit(&vswp->mac_lock);
862	D1(vswp, "%s:exit\n", __func__);
863}
864
865/*
866 * vsw_hio_rest_all -- Resets all ports that have shares allocated.
867 *	It is called only in the panic code path, so the LDC channels
868 *	are reset immediately.
869 */
870static void
871vsw_hio_reset_all(vsw_t *vswp)
872{
873	vsw_hio_t	*hiop = &vswp->vhio;
874	vsw_share_t	*vsharep;
875	int		i;
876
877	D1(vswp, "%s:enter\n", __func__);
878
879	if (vswp->hio_capable != B_TRUE)
880		return;
881
882	for (i = 0; i < hiop->vh_num_shares; i++) {
883		vsharep = &hiop->vh_shares[i];
884		if (vsharep->vs_state == VSW_SHARE_FREE) {
885			continue;
886		}
887		/*
888		 * Reset the port with immediate flag enabled,
889		 * to cause LDC reset immediately.
890		 */
891		vsw_hio_port_reset(vsharep->vs_portp, B_TRUE);
892	}
893	D1(vswp, "%s:exit\n", __func__);
894}
895
896/*
897 * vsw_hio_reboot_callb -- Called for reboot event. It tries to
898 *	free all currently allocated shares.
899 */
900/* ARGSUSED */
901static boolean_t
902vsw_hio_reboot_callb(void *arg, int code)
903{
904	vsw_t *vswp = arg;
905
906	D1(vswp, "%s:enter\n", __func__);
907	vsw_hio_free_all_shares(vswp, B_TRUE);
908	D1(vswp, "%s:exit\n", __func__);
909	return (B_TRUE);
910}
911
912/*
913 * vsw_hio_panic_callb -- Called from panic event. It resets all
914 *	the ports that have shares allocated. This is done to
915 *	trigger the cleanup in the guest ahead of HV reset.
916 */
917/* ARGSUSED */
918static boolean_t
919vsw_hio_panic_callb(void *arg, int code)
920{
921	vsw_t *vswp = arg;
922
923	D1(vswp, "%s:enter\n", __func__);
924	vsw_hio_reset_all(vswp);
925	D1(vswp, "%s:exit\n", __func__);
926	return (B_TRUE);
927}
928
929/*
930 * Setup kstats for hio statistics.
931 */
932static kstat_t *
933vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp)
934{
935	kstat_t			*ksp;
936	vsw_hio_kstats_t	*hiokp;
937	vsw_hio_t		*hiop;
938	char			share_assigned_info[MAXNAMELEN];
939	size_t			size;
940	int			i;
941
942	hiop = &vswp->vhio;
943	/*
944	 * vsw_hio_stats_t structure is variable size structure
945	 * having fields defined only for one share. So, we need
946	 * allocate additional space for the rest of the shares.
947	 */
948	size = sizeof (vsw_hio_kstats_t) / sizeof (kstat_named_t);
949	ASSERT(hiop->vh_num_shares >= 1);
950	size += ((hiop->vh_num_shares - 1) * 2);
951
952	ksp = kstat_create(ks_mod, vswp->instance, ks_name, "misc",
953	    KSTAT_TYPE_NAMED, size, KSTAT_FLAG_VIRTUAL);
954
955	if (ksp == NULL) {
956		return (NULL);
957	}
958	hiokp = (vsw_hio_kstats_t *)kmem_zalloc(sizeof (kstat_named_t) *
959	    size, KM_SLEEP);
960	ksp->ks_data = hiokp;
961
962	hiop->vh_ksp = ksp;
963	hiop->vh_kstatsp = hiokp;
964	hiop->vh_kstat_size =  size;
965
966	kstat_named_init(&hiokp->hio_capable, "hio_capable", KSTAT_DATA_CHAR);
967	kstat_named_init(&hiokp->hio_num_shares, "hio_num_shares",
968	    KSTAT_DATA_ULONG);
969
970	for (i = 0; i < hiop->vh_num_shares; i++) {
971		(void) sprintf(share_assigned_info, "%s%d", "hio_share_", i);
972		kstat_named_init(&(hiokp->share[i].assigned),
973		    share_assigned_info, KSTAT_DATA_ULONG);
974
975		(void) sprintf(share_assigned_info, "%s%d%s",
976		    "hio_share_", i, "_state");
977		kstat_named_init(&(hiokp->share[i].state),
978		    share_assigned_info, KSTAT_DATA_ULONG);
979	}
980
981	ksp->ks_update = vsw_hio_kstats_update;
982	ksp->ks_private = (void *)vswp;
983	kstat_install(ksp);
984	return (ksp);
985}
986
987/*
988 * Destroy hio kstats.
989 */
990static void
991vsw_hio_destroy_kstats(vsw_t *vswp)
992{
993	kstat_t			*ksp;
994	vsw_hio_t		*hiop;
995
996	ASSERT(vswp != NULL);
997
998	ksp = vswp->vhio.vh_ksp;
999	hiop = &vswp->vhio;
1000	if (ksp != NULL) {
1001		kmem_free(hiop->vh_kstatsp, sizeof (kstat_named_t) *
1002		    hiop->vh_kstat_size);
1003		kstat_delete(ksp);
1004		hiop->vh_kstatsp = NULL;
1005		hiop->vh_ksp = NULL;
1006	}
1007}
1008
1009/*
1010 * Update hio kstats.
1011 */
1012static int
1013vsw_hio_kstats_update(kstat_t *ksp, int rw)
1014{
1015	vsw_t			*vswp;
1016	vsw_hio_t		*hiop;
1017	vsw_hio_kstats_t	*hiokp;
1018	int			i;
1019
1020	vswp = (vsw_t *)ksp->ks_private;
1021	ASSERT(vswp != NULL);
1022
1023	hiop = &vswp->vhio;
1024	hiokp = hiop->vh_kstatsp;
1025
1026	if (rw == KSTAT_READ) {
1027		if (vswp->hio_capable) {
1028			(void) strcpy(hiokp->hio_capable.value.c, "Yes");
1029		} else {
1030			/* not hio capable, just return */
1031			(void) strcpy(hiokp->hio_capable.value.c, "No");
1032			return (0);
1033		}
1034
1035		mutex_enter(&vswp->mac_lock);
1036		hiokp->hio_num_shares.value.ul = (uint32_t)hiop->vh_num_shares;
1037		for (i = 0; i < hiop->vh_num_shares; i++) {
1038			hiokp->share[i].assigned.value.ul =
1039			    hiop->vh_shares[i].vs_macaddr;
1040			hiokp->share[i].state.value.ul =
1041			    hiop->vh_shares[i].vs_state;
1042		}
1043		mutex_exit(&vswp->mac_lock);
1044	} else {
1045		return (EACCES);
1046	}
1047
1048	return (0);
1049}
1050