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
77 extern boolean_t vsw_hio_enabled; /* HybridIO enabled? */
78 extern int vsw_hio_max_cleanup_retries;
79 extern int vsw_hio_cleanup_delay;
80
81 /* Functions imported from other files */
82 extern int vsw_send_msg(vsw_ldc_t *, void *, int, boolean_t);
83 extern void vsw_hio_port_reset(vsw_port_t *portp, boolean_t immediate);
84 extern 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 */
88 void vsw_hio_init(vsw_t *vswp);
89 void vsw_hio_cleanup(vsw_t *vswp);
90 void vsw_hio_start(vsw_t *vswp, vsw_ldc_t *ldcp);
91 void vsw_hio_stop(vsw_t *vswp, vsw_ldc_t *ldcp);
92 void vsw_process_dds_msg(vsw_t *vswp, vsw_ldc_t *ldcp, void *msg);
93 void vsw_hio_start_ports(vsw_t *vswp);
94 void vsw_hio_stop_port(vsw_port_t *portp);
95
96 /* Support functions */
97 static void vsw_hio_free_all_shares(vsw_t *vswp, boolean_t reboot);
98 static vsw_share_t *vsw_hio_alloc_share(vsw_t *vswp, vsw_ldc_t *ldcp);
99 static void vsw_hio_free_share(vsw_share_t *vsharep);
100 static vsw_share_t *vsw_hio_find_free_share(vsw_t *vswp);
101 static vsw_share_t *vsw_hio_find_vshare_ldcid(vsw_t *vswp, uint64_t ldc_id);
102 static vsw_share_t *vsw_hio_find_vshare_port(vsw_t *vswp, vsw_port_t *portp);
103 static int vsw_send_dds_msg(vsw_ldc_t *ldcp, uint8_t dds_subclass,
104 uint64_t cookie, uint64_t macaddr, uint32_t req_id);
105 static int vsw_send_dds_resp_msg(vsw_ldc_t *ldcp, vio_dds_msg_t *dmsg, int ack);
106 static int vsw_hio_send_delshare_msg(vsw_share_t *vsharep);
107 static boolean_t vsw_hio_reboot_callb(void *arg, int code);
108 static 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
125 static kstat_t *vsw_hio_setup_kstats(char *ks_mod, char *ks_name, vsw_t *vswp);
126 static void vsw_hio_destroy_kstats(vsw_t *vswp);
127 static 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 */
134 void
vsw_hio_init(vsw_t * vswp)135 vsw_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 */
190 static vsw_share_t *
vsw_hio_alloc_share(vsw_t * vswp,vsw_ldc_t * ldcp)191 vsw_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 */
226 static vsw_share_t *
vsw_hio_find_free_share(vsw_t * vswp)227 vsw_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 */
250 static vsw_share_t *
vsw_hio_find_vshare_ldcid(vsw_t * vswp,uint64_t ldc_id)251 vsw_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 */
277 static vsw_share_t *
vsw_hio_find_vshare_port(vsw_t * vswp,vsw_port_t * portp)278 vsw_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 */
303 static void
vsw_hio_free_share(vsw_share_t * vsharep)304 vsw_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 */
329 void
vsw_hio_cleanup(vsw_t * vswp)330 vsw_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 */
362 static void
vsw_hio_free_all_shares(vsw_t * vswp,boolean_t reboot)363 vsw_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 */
466 void
vsw_hio_start_ports(vsw_t * vswp)467 vsw_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 */
503 void
vsw_hio_start(vsw_t * vswp,vsw_ldc_t * ldcp)504 vsw_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 */
558 void
vsw_hio_stop(vsw_t * vswp,vsw_ldc_t * ldcp)559 vsw_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 */
582 static int
vsw_hio_send_delshare_msg(vsw_share_t * vsharep)583 vsw_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 */
622 static int
vsw_send_dds_msg(vsw_ldc_t * ldcp,uint8_t dds_subclass,uint64_t cookie,uint64_t macaddr,uint32_t req_id)623 vsw_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 */
649 void
vsw_process_dds_msg(vsw_t * vswp,vsw_ldc_t * ldcp,void * msg)650 vsw_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 */
743 static int
vsw_send_dds_resp_msg(vsw_ldc_t * ldcp,vio_dds_msg_t * dmsg,int ack)744 vsw_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 */
765 void
vsw_hio_port_update(vsw_port_t * portp,boolean_t hio_enabled)766 vsw_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 */
793 void
vsw_hio_stop_port(vsw_port_t * portp)794 vsw_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 */
870 static void
vsw_hio_reset_all(vsw_t * vswp)871 vsw_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 */
901 static boolean_t
vsw_hio_reboot_callb(void * arg,int code)902 vsw_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 */
918 static boolean_t
vsw_hio_panic_callb(void * arg,int code)919 vsw_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 */
932 static kstat_t *
vsw_hio_setup_kstats(char * ks_mod,char * ks_name,vsw_t * vswp)933 vsw_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 */
990 static void
vsw_hio_destroy_kstats(vsw_t * vswp)991 vsw_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 */
1012 static int
vsw_hio_kstats_update(kstat_t * ksp,int rw)1013 vsw_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