xref: /illumos-gate/usr/src/uts/sun4v/io/vsw_hio.c (revision ca9327a6de44d69ddab3668cc1e143ce781387a3)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/errno.h>
31 #include <sys/debug.h>
32 #include <sys/time.h>
33 #include <sys/sysmacros.h>
34 #include <sys/systm.h>
35 #include <sys/user.h>
36 #include <sys/stropts.h>
37 #include <sys/stream.h>
38 #include <sys/strlog.h>
39 #include <sys/strsubr.h>
40 #include <sys/cmn_err.h>
41 #include <sys/cpu.h>
42 #include <sys/kmem.h>
43 #include <sys/conf.h>
44 #include <sys/ddi.h>
45 #include <sys/sunddi.h>
46 #include <sys/ksynch.h>
47 #include <sys/stat.h>
48 #include <sys/kstat.h>
49 #include <sys/vtrace.h>
50 #include <sys/strsun.h>
51 #include <sys/dlpi.h>
52 #include <sys/ethernet.h>
53 #include <net/if.h>
54 #include <sys/varargs.h>
55 #include <sys/machsystm.h>
56 #include <sys/modctl.h>
57 #include <sys/modhash.h>
58 #include <sys/mac.h>
59 #include <sys/mac_ether.h>
60 #include <sys/taskq.h>
61 #include <sys/note.h>
62 #include <sys/mach_descrip.h>
63 #include <sys/mac.h>
64 #include <sys/mdeg.h>
65 #include <sys/ldc.h>
66 #include <sys/vsw_fdb.h>
67 #include <sys/vsw.h>
68 #include <sys/vio_mailbox.h>
69 #include <sys/vnet_mailbox.h>
70 #include <sys/vnet_common.h>
71 #include <sys/vio_util.h>
72 #include <sys/sdt.h>
73 #include <sys/atomic.h>
74 #include <sys/callb.h>
75 
76 
77 #define	VSW_DDS_NEXT_REQID(vsharep)	(++vsharep->vs_req_id)
78 
79 extern boolean_t vsw_hio_enabled;		/* HybridIO enabled? */
80 extern int vsw_hio_max_cleanup_retries;
81 extern int vsw_hio_cleanup_delay;
82 
83 /* Functions imported from other files */
84 extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
85 extern int vsw_set_hw(vsw_t *, vsw_port_t *, int);
86 extern int vsw_unset_hw(vsw_t *, vsw_port_t *, int);
87 extern void vsw_hio_port_reset(vsw_port_t *portp);
88 
89 /* Functions exported to other files */
90 void vsw_hio_init(vsw_t *vswp);
91 void vsw_hio_cleanup(vsw_t *vswp);
92 void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
93 void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
94 void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
95 void vsw_hio_start_ports(vsw_t *vswp);
96 void vsw_hio_stop_port(vsw_port_t *portp);
97 
98 /* Support functions */
99 static vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp);
100 static void vsw_hio_free_share(vsw_share_t *vsharep);
101 static vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp);
102 static vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id);
103 static vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp);
104 static int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass,
105     uint64_t cookie, uint64_t macaddr, uint32_t req_id);
106 static int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack);
107 static int vsw_hio_send_delshare_msg(vsw_share_t *vsharep);
108 static int vsw_hio_bind_macaddr(vsw_share_t *vsharep);
109 static void vsw_hio_unbind_macaddr(vsw_share_t *vsharep);
110 
111 
112 /*
113  * vsw_hio_init -- Initialize the HybridIO related info.
114  *	- Query SHARES and RINGS capability. Both capabilities
115  *	  need to be supported by the physical-device.
116  */
117 void
118 vsw_hio_init(vsw_t *vswp)
119 {
120 	vsw_hio_t	*hiop = &vswp->vhio;
121 	int		i;
122 	int		rv;
123 
124 	D1(vswp, "%s:enter\n", __func__);
125 	mutex_enter(&vswp->hw_lock);
126 	if (vsw_hio_enabled == B_FALSE) {
127 		mutex_exit(&vswp->hw_lock);
128 		return;
129 	}
130 
131 	vswp->hio_capable = B_FALSE;
132 	rv = mac_capab_get(vswp->mh, MAC_CAPAB_SHARES, &hiop->vh_scapab);
133 	if (rv == B_FALSE) {
134 		D2(vswp, "%s: %s is not HybridIO capable\n", __func__,
135 		    vswp->physname);
136 		mutex_exit(&vswp->hw_lock);
137 		return;
138 	}
139 	rv = mac_capab_get(vswp->mh, MAC_CAPAB_RINGS, &hiop->vh_rcapab);
140 	if (rv == B_FALSE) {
141 		DWARN(vswp, "%s: %s has no RINGS capability\n", __func__,
142 		    vswp->physname);
143 		mutex_exit(&vswp->hw_lock);
144 		return;
145 	}
146 	hiop->vh_num_shares = hiop->vh_scapab.ms_snum;
147 	hiop->vh_shares = kmem_zalloc((sizeof (vsw_share_t) *
148 	    hiop->vh_num_shares), KM_SLEEP);
149 	for (i = 0; i < hiop->vh_num_shares; i++) {
150 		hiop->vh_shares[i].vs_state = VSW_SHARE_FREE;
151 		hiop->vh_shares[i].vs_index = i;
152 		hiop->vh_shares[i].vs_vswp = vswp;
153 	}
154 	vswp->hio_capable = B_TRUE;
155 	D2(vswp, "%s: %s is HybridIO capable num_shares=%d\n", __func__,
156 	    vswp->physname, hiop->vh_num_shares);
157 	D1(vswp, "%s:exit\n", __func__);
158 	mutex_exit(&vswp->hw_lock);
159 }
160 
161 /*
162  * vsw_hio_alloc_share -- Allocate and setup the share for a guest domain.
163  *	- Allocate a free share.
164  *	- Bind the Guest's MAC address.
165  */
166 static vsw_share_t *
167 vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp)
168 {
169 	vsw_hio_t	*hiop = &vswp->vhio;
170 	mac_capab_share_t *hcapab = &hiop->vh_scapab;
171 	vsw_share_t	*vsharep;
172 	vsw_port_t	*portp = ldcp->ldc_port;
173 	uint64_t	ldc_id = ldcp->ldc_id;
174 	uint32_t	rmin, rmax;
175 	uint64_t	rmap;
176 	int		rv;
177 
178 	D1(vswp, "%s:enter\n", __func__);
179 	vsharep = vsw_hio_find_free_share(vswp);
180 	if (vsharep == NULL) {
181 		/* No free shares available */
182 		return (NULL);
183 	}
184 	/*
185 	 * Allocate a Share - it will come with rings/groups
186 	 * already assigned to it.
187 	 */
188 	rv = hcapab->ms_salloc(hcapab->ms_handle, ldc_id,
189 	    &vsharep->vs_cookie, &vsharep->vs_shdl);
190 	if (rv != 0) {
191 		D2(vswp, "Alloc a share failed for ldc=0x%lx rv=%d",
192 		    ldc_id, rv);
193 		return (NULL);
194 	}
195 
196 	/*
197 	 * Query the RX group number to bind the port's
198 	 * MAC address to it.
199 	 */
200 	hcapab->ms_squery(vsharep->vs_shdl, MAC_RING_TYPE_RX,
201 	    &rmin, &rmax, &rmap, &vsharep->vs_gnum);
202 
203 	/* Cache some useful info */
204 	vsharep->vs_ldcid = ldcp->ldc_id;
205 	vsharep->vs_macaddr = vnet_macaddr_strtoul(
206 	    portp->p_macaddr.ether_addr_octet);
207 	vsharep->vs_portp = ldcp->ldc_port;
208 
209 	/* Bind the Guest's MAC address */
210 	rv = vsw_hio_bind_macaddr(vsharep);
211 	if (rv != 0) {
212 		/* something went wrong, cleanup */
213 		hcapab->ms_sfree(vsharep->vs_shdl);
214 		return (NULL);
215 	}
216 
217 	vsharep->vs_state |= VSW_SHARE_ASSIGNED;
218 
219 	D1(vswp, "%s:exit\n", __func__);
220 	return (vsharep);
221 }
222 
223 /*
224  * vsw_hio_bind_macaddr -- Remove the port's MAC address from the
225  *	physdev and bind it to the Share's RX group.
226  */
227 static int
228 vsw_hio_bind_macaddr(vsw_share_t *vsharep)
229 {
230 	vsw_t		*vswp = vsharep->vs_vswp;
231 	vsw_port_t	*portp = vsharep->vs_portp;
232 	mac_capab_rings_t *rcapab = &vswp->vhio.vh_rcapab;
233 	mac_group_info_t *ginfop = &vsharep->vs_rxginfo;
234 	int		rv;
235 
236 	/* Get the RX groupinfo */
237 	rcapab->mr_gget(rcapab->mr_handle, MAC_RING_TYPE_RX,
238 	    vsharep->vs_gnum, &vsharep->vs_rxginfo, NULL);
239 
240 	/* Unset the MAC address first */
241 	if (portp->addr_set != VSW_ADDR_UNSET) {
242 		(void) vsw_unset_hw(vswp, portp, VSW_VNETPORT);
243 	}
244 
245 	/* Bind the MAC address to the RX group */
246 	rv = ginfop->mrg_addmac(ginfop->mrg_driver,
247 	    (uint8_t *)&portp->p_macaddr.ether_addr_octet);
248 	if (rv != 0) {
249 		/* Restore the address back as it was */
250 		(void) vsw_set_hw(vswp, portp, VSW_VNETPORT);
251 		return (rv);
252 	}
253 	return (0);
254 }
255 
256 /*
257  * vsw_hio_unbind_macaddr -- Unbind the port's MAC address and restore
258  *	it back as it was before.
259  */
260 static void
261 vsw_hio_unbind_macaddr(vsw_share_t *vsharep)
262 {
263 	vsw_t		*vswp = vsharep->vs_vswp;
264 	vsw_port_t	*portp = vsharep->vs_portp;
265 	mac_group_info_t *ginfop = &vsharep->vs_rxginfo;
266 
267 	if (portp == NULL) {
268 		return;
269 	}
270 	/* Unbind the MAC address from the RX group */
271 	(void) ginfop->mrg_remmac(ginfop->mrg_driver,
272 	    (uint8_t *)&portp->p_macaddr.ether_addr_octet);
273 
274 	/* Program the MAC address back */
275 	(void) vsw_set_hw(vswp, portp, VSW_VNETPORT);
276 }
277 
278 /*
279  * vsw_hio_find_free_share -- Find a free Share.
280  */
281 static vsw_share_t *
282 vsw_hio_find_free_share(vsw_t *vswp)
283 {
284 	vsw_hio_t *hiop = &vswp->vhio;
285 	vsw_share_t *vsharep;
286 	int i;
287 
288 	D1(vswp, "%s:enter\n", __func__);
289 	for (i = 0; i < hiop->vh_num_shares; i++) {
290 		vsharep = &hiop->vh_shares[i];
291 		if (vsharep->vs_state == VSW_SHARE_FREE) {
292 			D1(vswp, "%s:Returning free share(%d)\n",
293 			    __func__, vsharep->vs_index);
294 			return (vsharep);
295 		}
296 	}
297 	D1(vswp, "%s:no free share\n", __func__);
298 	return (NULL);
299 }
300 
301 /*
302  * vsw_hio_find_vshare_ldcid -- Given ldc_id, find the corresponding
303  *	share structure.
304  */
305 static vsw_share_t *
306 vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id)
307 {
308 	vsw_hio_t *hiop = &vswp->vhio;
309 	vsw_share_t *vsharep;
310 	int i;
311 
312 	D1(vswp, "%s:enter, ldc=0x%lx", __func__, ldc_id);
313 	for (i = 0; i < hiop->vh_num_shares; i++) {
314 		vsharep = &hiop->vh_shares[i];
315 		if (vsharep->vs_state == VSW_SHARE_FREE) {
316 			continue;
317 		}
318 		if (vsharep->vs_ldcid == ldc_id) {
319 			D1(vswp, "%s:returning share(%d)",
320 			    __func__, vsharep->vs_index);
321 			return (vsharep);
322 		}
323 	}
324 	D1(vswp, "%s:returning NULL", __func__);
325 	return (NULL);
326 }
327 
328 /*
329  * vsw_hio_find_vshare_port -- Given portp, find the corresponding
330  *	share structure.
331  */
332 static vsw_share_t *
333 vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp)
334 {
335 	vsw_hio_t *hiop = &vswp->vhio;
336 	vsw_share_t *vsharep;
337 	int i;
338 
339 	D1(vswp, "%s:enter, portp=0x%p", __func__, portp);
340 	for (i = 0; i < hiop->vh_num_shares; i++) {
341 		vsharep = &hiop->vh_shares[i];
342 		if (vsharep->vs_state == VSW_SHARE_FREE) {
343 			continue;
344 		}
345 		if (vsharep->vs_portp == portp) {
346 			D1(vswp, "%s:returning share(%d)",
347 			    __func__, vsharep->vs_index);
348 			return (vsharep);
349 		}
350 	}
351 	D1(vswp, "%s:returning NULL", __func__);
352 	return (NULL);
353 }
354 
355 /*
356  * vsw_hio_free_share -- Unbind the MAC address and free share.
357  */
358 static void
359 vsw_hio_free_share(vsw_share_t *vsharep)
360 {
361 	vsw_t		*vswp = vsharep->vs_vswp;
362 	vsw_hio_t	*hiop = &vswp->vhio;
363 	mac_capab_share_t *hcapab = &hiop->vh_scapab;
364 
365 	D1(vswp, "%s:enter\n", __func__);
366 
367 	/* First unbind the MAC address and restore it back */
368 	vsw_hio_unbind_macaddr(vsharep);
369 
370 	/* free share */
371 	hcapab->ms_sfree(vsharep->vs_shdl);
372 	vsharep->vs_state = VSW_SHARE_FREE;
373 
374 	/* DERR only for printing by default */
375 	DERR(vswp, "Share freed for ldc_id=0x%lx Cookie=0x%lX",
376 	    vsharep->vs_ldcid, vsharep->vs_cookie);
377 	D1(vswp, "%s:exit\n", __func__);
378 }
379 
380 
381 /*
382  * vsw_hio_cleanup -- A rountine to free all shares gracefully.
383  *	The following are the steps followed to accomplish this:
384  *
385  *	- First clear 'hio_capable' to avoid further share allocations.
386  *	- If a share is in accepted(ACKD) state, that means the guest
387  *	  has HybridIO setup etc. If so, send a DEL_SHARE message and
388  *	  give some time(delay) for the guest to ACK.
389  *	- If the Share is another state, give some time to transition to
390  *	  ACKD state, then try the above.
391  *	- After max retries, reset the ports to brute force the shares
392  *	  to be freed. Give a little delay for the LDC reset code to
393  *	  free the Share.
394  */
395 void
396 vsw_hio_cleanup(vsw_t *vswp)
397 {
398 	vsw_hio_t	*hiop = &vswp->vhio;
399 	vsw_port_list_t	*plist = &vswp->plist;
400 	vsw_share_t	*vsharep;
401 	int		free_shares = 0;
402 	int		max_retries = vsw_hio_max_cleanup_retries;
403 	int		i;
404 
405 	D1(vswp, "%s:enter\n", __func__);
406 
407 	/*
408 	 * Acquire plist->lockrw to make the locking a bit easier
409 	 * and keep the ports in a stable state while we are cleaningup
410 	 * HybridIO.
411 	 */
412 	READ_ENTER(&plist->lockrw);
413 	mutex_enter(&vswp->hw_lock);
414 	/*
415 	 * first clear the hio_capable flag so that no more
416 	 * HybridIO operations are initiated.
417 	 */
418 	vswp->hio_capable = B_FALSE;
419 
420 	do {
421 		free_shares = 0;
422 		for (i = 0; i < hiop->vh_num_shares; i++) {
423 			vsharep = &hiop->vh_shares[i];
424 			if (vsharep->vs_state == VSW_SHARE_FREE) {
425 				free_shares++;
426 				continue;
427 			}
428 			/*
429 			 * If the share is in DDS_ACKD state, then
430 			 * send DEL_SHARE message so that guest can
431 			 * release its Hybrid resource.
432 			 */
433 			if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
434 				int rv;
435 
436 				/* send DDS_DEL_SHARE */
437 				D1(vswp, "%s:sending DEL_SHARE msg for "
438 				    "share(%d)", __func__, vsharep->vs_index);
439 				rv = vsw_hio_send_delshare_msg(vsharep);
440 				if (rv != 0) {
441 					/*
442 					 * No alternative, reset the port
443 					 * to force the release of Hybrid
444 					 * resources.
445 					 */
446 					vsw_hio_port_reset(vsharep->vs_portp);
447 				}
448 			}
449 			if (max_retries == 1) {
450 				/* Last retry,  reset the port */
451 				DWARN(vswp, "%s:All retries failed, "
452 				    " cause a reset to trigger cleanup for "
453 				    "share(%d)", __func__, vsharep->vs_index);
454 				vsw_hio_port_reset(vsharep->vs_portp);
455 			}
456 		}
457 		if (free_shares == hiop->vh_num_shares) {
458 			/* Clean up is done */
459 			break;
460 		}
461 		/*
462 		 * Release the lock so that reply for DEL_SHARE
463 		 * messages come and get processed, that is, shares
464 		 * get freed.
465 		 * This delay is also needed for the port reset to
466 		 * release the Hybrid resource.
467 		 */
468 		mutex_exit(&vswp->hw_lock);
469 		delay(drv_usectohz(vsw_hio_cleanup_delay));
470 		mutex_enter(&vswp->hw_lock);
471 		max_retries--;
472 	} while ((free_shares < hiop->vh_num_shares) && (max_retries > 0));
473 
474 	/* By now, all shares should be freed */
475 	ASSERT(free_shares == hiop->vh_num_shares);
476 	if (free_shares != hiop->vh_num_shares) {
477 		cmn_err(CE_NOTE, "vsw%d: All physical resources "
478 		    "could not be freed", vswp->instance);
479 	}
480 
481 	kmem_free(hiop->vh_shares, sizeof (vsw_share_t) * hiop->vh_num_shares);
482 	hiop->vh_shares = NULL;
483 	hiop->vh_num_shares = 0;
484 	mutex_exit(&vswp->hw_lock);
485 	RW_EXIT(&plist->lockrw);
486 	D1(vswp, "%s:exit\n", __func__);
487 }
488 
489 /*
490  * vsw_hio_start_ports -- Start HybridIO for ports that have
491  *	already established connection before HybridIO is intialized.
492  */
493 void
494 vsw_hio_start_ports(vsw_t *vswp)
495 {
496 	vsw_port_list_t	*plist = &vswp->plist;
497 	vsw_port_t	*portp;
498 	vsw_share_t	*vsharep;
499 	boolean_t	reset;
500 
501 	if (vswp->hio_capable == B_FALSE) {
502 		return;
503 	}
504 	READ_ENTER(&plist->lockrw);
505 	for (portp = plist->head; portp != NULL; portp = portp->p_next) {
506 		if ((portp->p_hio_enabled == B_FALSE) ||
507 		    (portp->p_hio_capable == B_FALSE)) {
508 			continue;
509 		}
510 
511 		reset = B_FALSE;
512 		mutex_enter(&vswp->hw_lock);
513 		vsharep = vsw_hio_find_vshare_port(vswp, portp);
514 		if (vsharep == NULL) {
515 			reset = B_TRUE;
516 		}
517 		mutex_exit(&vswp->hw_lock);
518 
519 		if (reset == B_TRUE) {
520 			/* Cause a rest to trigger HybridIO setup */
521 			vsw_hio_port_reset(portp);
522 		}
523 	}
524 	RW_EXIT(&plist->lockrw);
525 }
526 
527 /*
528  * vsw_hio_start -- Start HybridIO for a guest(given LDC)
529  */
530 void
531 vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp)
532 {
533 	vsw_share_t	*vsharep;
534 	uint32_t	req_id;
535 	int		rv;
536 
537 	D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
538 	mutex_enter(&vswp->hw_lock);
539 	if (vswp->hio_capable == B_FALSE) {
540 		mutex_exit(&vswp->hw_lock);
541 		D2(vswp, "%s:not HIO capable", __func__);
542 		return;
543 	}
544 
545 	/* Verify if a share was already allocated */
546 	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
547 	if (vsharep != NULL) {
548 		mutex_exit(&vswp->hw_lock);
549 		D2(vswp, "%s:Share already allocated to ldc=0x%lx",
550 		    __func__, ldcp->ldc_id);
551 		return;
552 	}
553 	vsharep = vsw_hio_alloc_share(vswp, ldcp);
554 	if (vsharep == NULL) {
555 		mutex_exit(&vswp->hw_lock);
556 		D2(vswp, "%s: no Share available for ldc=0x%lx",
557 		    __func__, ldcp->ldc_id);
558 		return;
559 	}
560 	req_id = VSW_DDS_NEXT_REQID(vsharep);
561 	rv = vsw_send_dds_msg(ldcp, DDS_VNET_ADD_SHARE, vsharep->vs_cookie,
562 	    vsharep->vs_macaddr, req_id);
563 	if (rv != 0) {
564 		/*
565 		 * Failed to send a DDS message, so cleanup now.
566 		 */
567 		vsw_hio_free_share(vsharep);
568 		mutex_exit(&vswp->hw_lock);
569 		return;
570 	}
571 	vsharep->vs_state |= VSW_SHARE_DDS_SENT;
572 	mutex_exit(&vswp->hw_lock);
573 
574 	/* DERR only to print by default */
575 	DERR(vswp, "Share allocated for ldc_id=0x%lx Cookie=0x%lX",
576 	    ldcp->ldc_id, vsharep->vs_cookie);
577 
578 	D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
579 }
580 
581 /*
582  * vsw_hio_stop -- Stop/clean the HybridIO config for a guest(given ldc).
583  */
584 void
585 vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp)
586 {
587 	vsw_share_t *vsharep;
588 
589 	D1(vswp, "%s:enter ldc=0x%lx", __func__, ldcp->ldc_id);
590 
591 	mutex_enter(&vswp->hw_lock);
592 	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
593 	if (vsharep == NULL) {
594 		D1(vswp, "%s:no share found for ldc=0x%lx",
595 		    __func__, ldcp->ldc_id);
596 		mutex_exit(&vswp->hw_lock);
597 		return;
598 	}
599 	vsw_hio_free_share(vsharep);
600 	mutex_exit(&vswp->hw_lock);
601 
602 	D1(vswp, "%s:exit ldc=0x%lx", __func__, ldcp->ldc_id);
603 }
604 
605 /*
606  * vsw_hio_send_delshare_msg -- Send a DEL_SHARE message to the	guest.
607  */
608 static int
609 vsw_hio_send_delshare_msg(vsw_share_t *vsharep)
610 {
611 	vsw_t *vswp = vsharep->vs_vswp;
612 	vsw_port_t *portp;
613 	vsw_ldc_list_t	*ldcl;
614 	vsw_ldc_t	*ldcp;
615 	uint32_t	req_id;
616 	uint64_t	cookie = vsharep->vs_cookie;
617 	uint64_t	macaddr = vsharep->vs_macaddr;
618 	int		rv;
619 
620 	ASSERT(MUTEX_HELD(&vswp->hw_lock));
621 	mutex_exit(&vswp->hw_lock);
622 
623 	portp = vsharep->vs_portp;
624 	if (portp == NULL) {
625 		mutex_enter(&vswp->hw_lock);
626 		return (0);
627 	}
628 
629 	ldcl = &portp->p_ldclist;
630 	READ_ENTER(&ldcl->lockrw);
631 	ldcp = ldcl->head;
632 	if ((ldcp == NULL) || (ldcp->ldc_id != vsharep->vs_ldcid)) {
633 		RW_EXIT(&ldcl->lockrw);
634 		mutex_enter(&vswp->hw_lock);
635 		return (0);
636 	}
637 	req_id = VSW_DDS_NEXT_REQID(vsharep);
638 	rv = vsw_send_dds_msg(ldcp, DDS_VNET_DEL_SHARE,
639 	    cookie, macaddr, req_id);
640 	RW_EXIT(&ldcl->lockrw);
641 	mutex_enter(&vswp->hw_lock);
642 	return (rv);
643 }
644 
645 /*
646  * vsw_send_dds_msg -- Send a DDS message.
647  */
648 static int
649 vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass, uint64_t
650     cookie, uint64_t macaddr, uint32_t req_id)
651 {
652 	vsw_t *vswp = ldcp->ldc_port->p_vswp;
653 	vio_dds_msg_t	vmsg;
654 	dds_share_msg_t	*smsg = &vmsg.msg.share_msg;
655 	int rv;
656 
657 	D1(vswp, "%s:enter\n", __func__);
658 	vmsg.tag.vio_msgtype = VIO_TYPE_CTRL;
659 	vmsg.tag.vio_subtype = VIO_SUBTYPE_INFO;
660 	vmsg.tag.vio_subtype_env = VIO_DDS_INFO;
661 	vmsg.tag.vio_sid = ldcp->local_session;
662 	vmsg.dds_class = DDS_VNET_NIU;
663 	vmsg.dds_subclass = dds_subclass;
664 	vmsg.dds_req_id = req_id;
665 	smsg->macaddr = macaddr;
666 	smsg->cookie = cookie;
667 	rv = vsw_send_msg(ldcp, &vmsg, sizeof (vmsg), B_FALSE);
668 	D1(vswp, "%s:exit rv=%d\n", __func__, rv);
669 	return (rv);
670 }
671 
672 /*
673  * vsw_process_dds_msg -- Process a DDS message received from a guest.
674  */
675 void
676 vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg)
677 {
678 	vsw_share_t	*vsharep;
679 	vio_dds_msg_t	*dmsg = msg;
680 
681 	D1(vswp, "%s:enter ldc=0x%lx\n", __func__, ldcp->ldc_id);
682 	if (dmsg->dds_class != DDS_VNET_NIU) {
683 		/* discard */
684 		return;
685 	}
686 	mutex_enter(&vswp->hw_lock);
687 	/*
688 	 * We expect to receive DDS messages only from guests that
689 	 * have HybridIO started.
690 	 */
691 	vsharep = vsw_hio_find_vshare_ldcid(vswp, ldcp->ldc_id);
692 	if (vsharep == NULL) {
693 		mutex_exit(&vswp->hw_lock);
694 		return;
695 	}
696 
697 	switch (dmsg->dds_subclass) {
698 	case DDS_VNET_ADD_SHARE:
699 		/* A response for ADD_SHARE message. */
700 		D1(vswp, "%s:DDS_VNET_ADD_SHARE\n", __func__);
701 		if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
702 			DWARN(vswp, "%s: invalid ADD_SHARE response  message "
703 			    " share state=0x%X", __func__, vsharep->vs_state);
704 			break;
705 		}
706 
707 		if (dmsg->dds_req_id != vsharep->vs_req_id) {
708 			DWARN(vswp, "%s: invalid req_id in ADD_SHARE response"
709 			    " message req_id=0x%X share's req_id=0x%X",
710 			    __func__, dmsg->dds_req_id, vsharep->vs_req_id);
711 			break;
712 		}
713 
714 		if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
715 			DWARN(vswp, "%s: NACK received for ADD_SHARE"
716 			    " message ldcid=0x%lx", __func__, ldcp->ldc_id);
717 			/* cleanup for NACK */
718 			vsw_hio_free_share(vsharep);
719 		} else {
720 			D2(vswp, "%s: ACK received for ADD_SHARE", __func__);
721 			vsharep->vs_state &= ~VSW_SHARE_DDS_SENT;
722 			vsharep->vs_state |= VSW_SHARE_DDS_ACKD;
723 		}
724 		break;
725 
726 	case DDS_VNET_DEL_SHARE:
727 		/* A response for DEL_SHARE message */
728 		D1(vswp, "%s:DDS_VNET_DEL_SHARE\n", __func__);
729 		if (!(vsharep->vs_state & VSW_SHARE_DDS_SENT)) {
730 			DWARN(vswp, "%s: invalid ADD_SHARE response message "
731 			    " share state=0x%X", __func__, vsharep->vs_state);
732 			break;
733 		}
734 
735 		if (dmsg->dds_req_id != vsharep->vs_req_id) {
736 			DWARN(vswp, "%s: invalid req_id in ADD_SHARE response"
737 			    " message share req_id=0x%X share's req_id=0x%X",
738 			    __func__, dmsg->dds_req_id, vsharep->vs_req_id);
739 			break;
740 		}
741 		if (dmsg->tag.vio_subtype == VIO_SUBTYPE_NACK) {
742 			DWARN(vswp, "%s: NACK received for ADD_SHARE",
743 			    __func__);
744 		}
745 		/* There is nothing we can do, free share now */
746 		vsw_hio_free_share(vsharep);
747 		break;
748 
749 	case DDS_VNET_REL_SHARE:
750 		/* Guest has released Share voluntarily, so free it now */
751 		D1(vswp, "%s:DDS_VNET_REL_SHARE\n", __func__);
752 		/* send ACK */
753 		(void) vsw_send_dds_resp_msg(ldcp, dmsg, B_FALSE);
754 		vsw_hio_free_share(vsharep);
755 		break;
756 	default:
757 		DERR(vswp, "%s: Invalid DDS message type=0x%X",
758 		    __func__, dmsg->dds_subclass);
759 		break;
760 	}
761 	mutex_exit(&vswp->hw_lock);
762 	D1(vswp, "%s:exit ldc=0x%lx\n", __func__, ldcp->ldc_id);
763 }
764 
765 /*
766  * vsw_send_dds_resp_msg -- Send a DDS response message.
767  */
768 static int
769 vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack)
770 {
771 	vsw_t	*vswp = ldcp->ldc_port->p_vswp;
772 	int	rv;
773 
774 	D1(vswp, "%s:enter\n", __func__);
775 	if (ack == B_TRUE) {
776 		dmsg->tag.vio_subtype = VIO_SUBTYPE_ACK;
777 		dmsg->msg.share_resp_msg.status = DDS_VNET_SUCCESS;
778 	} else {
779 		dmsg->tag.vio_subtype = VIO_SUBTYPE_NACK;
780 		dmsg->msg.share_resp_msg.status = DDS_VNET_FAIL;
781 	}
782 	rv = vsw_send_msg(ldcp, dmsg, sizeof (vio_dds_msg_t), B_FALSE);
783 	D1(vswp, "%s:exit rv=%d\n", __func__, rv);
784 	return (rv);
785 }
786 
787 /*
788  * vsw_hio_port_update -- update Hybrid mode change for a port.
789  */
790 void
791 vsw_hio_port_update(vsw_port_t *portp, boolean_t hio_enabled)
792 {
793 	/* Verify if the mode really changed */
794 	if (portp->p_hio_enabled == hio_enabled) {
795 		return;
796 	}
797 
798 	if (hio_enabled == B_FALSE) {
799 		/* Hybrid Mode is disabled, so stop HybridIO */
800 		vsw_hio_stop_port(portp);
801 		portp->p_hio_enabled = B_FALSE;
802 	} else {
803 		portp->p_hio_enabled =  B_TRUE;
804 		/* reset the port to initiate HybridIO setup */
805 		vsw_hio_port_reset(portp);
806 	}
807 }
808 
809 /*
810  * vsw_hio_stop_port -- Stop HybridIO for a given port. Sequence
811  *	followed is similar to vsw_hio_cleanup().
812  *
813  */
814 void
815 vsw_hio_stop_port(vsw_port_t *portp)
816 {
817 	vsw_t *vswp = portp->p_vswp;
818 	vsw_share_t *vsharep;
819 	int max_retries = vsw_hio_max_cleanup_retries;
820 
821 	D1(vswp, "%s:enter\n", __func__);
822 	mutex_enter(&vswp->hw_lock);
823 
824 	if (vswp->hio_capable == B_FALSE) {
825 		mutex_exit(&vswp->hw_lock);
826 		return;
827 	}
828 
829 	vsharep = vsw_hio_find_vshare_port(vswp, portp);
830 	if (vsharep == NULL) {
831 		mutex_exit(&vswp->hw_lock);
832 		return;
833 	}
834 
835 	do {
836 		if (vsharep->vs_state & VSW_SHARE_DDS_ACKD) {
837 			int rv;
838 
839 			/* send DDS_DEL_SHARE */
840 			D1(vswp, "%s:sending DEL_SHARE msg for "
841 			    "share(%d)", __func__, vsharep->vs_index);
842 			rv = vsw_hio_send_delshare_msg(vsharep);
843 			if (rv != 0) {
844 				/*
845 				 * Cause a port reset to trigger
846 				 * cleanup.
847 				 */
848 				vsw_hio_port_reset(vsharep->vs_portp);
849 			}
850 		}
851 		if (max_retries == 1) {
852 			/* last retry */
853 			DWARN(vswp, "%s:All retries failed, "
854 			    " cause a reset to trigger cleanup for "
855 			    "share(%d)", __func__, vsharep->vs_index);
856 			vsw_hio_port_reset(vsharep->vs_portp);
857 		}
858 
859 		/* Check if the share still assigned to this port */
860 		if ((vsharep->vs_portp != portp) ||
861 		    (vsharep->vs_state == VSW_SHARE_FREE)) {
862 			break;
863 		}
864 
865 		/*
866 		 * Release the lock so that reply for DEL_SHARE
867 		 * messages come and get processed, that is, shares
868 		 * get freed.
869 		 */
870 		mutex_exit(&vswp->hw_lock);
871 		delay(drv_usectohz(vsw_hio_cleanup_delay));
872 		mutex_enter(&vswp->hw_lock);
873 
874 		/* Check if the share still assigned to this port */
875 		if ((vsharep->vs_portp != portp) ||
876 		    (vsharep->vs_state == VSW_SHARE_FREE)) {
877 			break;
878 		}
879 		max_retries--;
880 	} while ((vsharep->vs_state != VSW_SHARE_FREE) && (max_retries > 0));
881 
882 	mutex_exit(&vswp->hw_lock);
883 	D1(vswp, "%s:exit\n", __func__);
884 }
885