xref: /titanic_41/usr/src/uts/common/io/dld/dld_drv.c (revision d4660949aa62dd6a963f4913b7120b383cf473c4)
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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 /*
29  * Data-Link Driver
30  */
31 
32 #include	<sys/conf.h>
33 #include	<sys/mkdev.h>
34 #include	<sys/modctl.h>
35 #include	<sys/stat.h>
36 #include	<sys/strsun.h>
37 #include	<sys/vlan.h>
38 #include	<sys/dld.h>
39 #include	<sys/dld_impl.h>
40 #include	<sys/dls_impl.h>
41 #include	<sys/softmac.h>
42 #include 	<sys/vlan.h>
43 #include	<inet/common.h>
44 
45 /*
46  * dld control node state, one per open control node session.
47  */
48 typedef struct dld_ctl_str_s {
49 	minor_t cs_minor;
50 	queue_t *cs_wq;
51 } dld_ctl_str_t;
52 
53 static void	drv_init(void);
54 static int	drv_fini(void);
55 
56 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
57 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
58 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
59 
60 /*
61  * Secure objects declarations
62  */
63 #define	SECOBJ_WEP_HASHSZ	67
64 static krwlock_t	drv_secobj_lock;
65 static kmem_cache_t	*drv_secobj_cachep;
66 static mod_hash_t	*drv_secobj_hash;
67 static void		drv_secobj_init(void);
68 static void		drv_secobj_fini(void);
69 static void		drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *);
70 static void		drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *);
71 static void		drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *);
72 
73 /*
74  * The following entry points are private to dld and are used for control
75  * operations only. The entry points exported to mac drivers are defined
76  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
77  */
78 static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
79 static int	drv_close(queue_t *);
80 
81 static void	drv_uw_put(queue_t *, mblk_t *);
82 static void	drv_uw_srv(queue_t *);
83 
84 dev_info_t	*dld_dip;		/* dev_info_t for the driver */
85 uint32_t	dld_opt = 0;		/* Global options */
86 static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
87 
88 #define	NAUTOPUSH 32
89 static mod_hash_t *dld_ap_hashp;
90 static krwlock_t dld_ap_hash_lock;
91 
92 static	struct	module_info	drv_info = {
93 	0,			/* mi_idnum */
94 	DLD_DRIVER_NAME,	/* mi_idname */
95 	0,			/* mi_minpsz */
96 	(64 * 1024),		/* mi_maxpsz */
97 	1,			/* mi_hiwat */
98 	0			/* mi_lowat */
99 };
100 
101 static	struct qinit		drv_ur_init = {
102 	NULL,			/* qi_putp */
103 	NULL,			/* qi_srvp */
104 	drv_open,		/* qi_qopen */
105 	drv_close,		/* qi_qclose */
106 	NULL,			/* qi_qadmin */
107 	&drv_info,		/* qi_minfo */
108 	NULL			/* qi_mstat */
109 };
110 
111 static	struct qinit		drv_uw_init = {
112 	(pfi_t)drv_uw_put,	/* qi_putp */
113 	(pfi_t)drv_uw_srv,	/* qi_srvp */
114 	NULL,			/* qi_qopen */
115 	NULL,			/* qi_qclose */
116 	NULL,			/* qi_qadmin */
117 	&drv_info,		/* qi_minfo */
118 	NULL			/* qi_mstat */
119 };
120 
121 static	struct streamtab	drv_stream = {
122 	&drv_ur_init,		/* st_rdinit */
123 	&drv_uw_init,		/* st_wrinit */
124 	NULL,			/* st_muxrinit */
125 	NULL			/* st_muxwinit */
126 };
127 
128 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
129     nodev, drv_getinfo, D_MP, &drv_stream);
130 
131 /*
132  * Module linkage information for the kernel.
133  */
134 
135 extern	struct mod_ops		mod_driverops;
136 
137 static	struct modldrv		drv_modldrv = {
138 	&mod_driverops,
139 	DLD_INFO,
140 	&drv_ops
141 };
142 
143 static	struct modlinkage	drv_modlinkage = {
144 	MODREV_1,
145 	&drv_modldrv,
146 	NULL
147 };
148 
149 int
150 _init(void)
151 {
152 	int	err;
153 
154 	drv_init();
155 
156 	if ((err = mod_install(&drv_modlinkage)) != 0)
157 		return (err);
158 
159 	return (0);
160 }
161 
162 int
163 _fini(void)
164 {
165 	int	err;
166 
167 	if ((err = mod_remove(&drv_modlinkage)) != 0)
168 		return (err);
169 
170 	if (drv_fini() != 0) {
171 		(void) mod_install(&drv_modlinkage);
172 		return (DDI_FAILURE);
173 	}
174 
175 	return (err);
176 }
177 
178 int
179 _info(struct modinfo *modinfop)
180 {
181 	return (mod_info(&drv_modlinkage, modinfop));
182 }
183 
184 /*
185  * Initialize component modules.
186  */
187 static void
188 drv_init(void)
189 {
190 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
191 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
192 	drv_secobj_init();
193 	dld_str_init();
194 	/*
195 	 * Create a hash table for autopush configuration.
196 	 */
197 	dld_ap_hashp = mod_hash_create_idhash("dld_autopush_hash",
198 	    NAUTOPUSH, mod_hash_null_valdtor);
199 
200 	ASSERT(dld_ap_hashp != NULL);
201 	rw_init(&dld_ap_hash_lock, NULL, RW_DRIVER, NULL);
202 }
203 
204 /* ARGSUSED */
205 static uint_t
206 drv_ap_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
207 {
208 	boolean_t *pexist = arg;
209 
210 	*pexist = B_TRUE;
211 	return (MH_WALK_TERMINATE);
212 }
213 
214 static int
215 drv_fini(void)
216 {
217 	int		err;
218 	boolean_t	exist = B_FALSE;
219 
220 	rw_enter(&dld_ap_hash_lock, RW_READER);
221 	mod_hash_walk(dld_ap_hashp, drv_ap_exist, &exist);
222 	rw_exit(&dld_ap_hash_lock);
223 
224 	if (exist)
225 		return (EBUSY);
226 
227 	if ((err = dld_str_fini()) != 0)
228 		return (err);
229 
230 	drv_secobj_fini();
231 	vmem_destroy(dld_ctl_vmem);
232 	mod_hash_destroy_idhash(dld_ap_hashp);
233 	rw_destroy(&dld_ap_hash_lock);
234 	return (0);
235 }
236 
237 /*
238  * devo_getinfo: getinfo(9e)
239  */
240 /*ARGSUSED*/
241 static int
242 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
243 {
244 	if (dld_dip == NULL)
245 		return (DDI_FAILURE);
246 
247 	switch (cmd) {
248 	case DDI_INFO_DEVT2INSTANCE:
249 		*resp = (void *)0;
250 		break;
251 	case DDI_INFO_DEVT2DEVINFO:
252 		*resp = (void *)dld_dip;
253 		break;
254 	default:
255 		return (DDI_FAILURE);
256 	}
257 
258 	return (DDI_SUCCESS);
259 }
260 
261 /*
262  * Check properties to set options. (See dld.h for property definitions).
263  */
264 static void
265 drv_set_opt(dev_info_t *dip)
266 {
267 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
268 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
269 		dld_opt |= DLD_OPT_NO_FASTPATH;
270 	}
271 
272 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
273 	    DLD_PROP_NO_POLL, 0) != 0) {
274 		dld_opt |= DLD_OPT_NO_POLL;
275 	}
276 
277 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
278 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
279 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
280 	}
281 
282 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
283 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
284 		dld_opt |= DLD_OPT_NO_SOFTRING;
285 	}
286 }
287 
288 /*
289  * devo_attach: attach(9e)
290  */
291 static int
292 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
293 {
294 	if (cmd != DDI_ATTACH)
295 		return (DDI_FAILURE);
296 
297 	ASSERT(ddi_get_instance(dip) == 0);
298 
299 	drv_set_opt(dip);
300 
301 	/*
302 	 * Create control node. DLPI provider nodes will be created on demand.
303 	 */
304 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
305 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
306 		return (DDI_FAILURE);
307 
308 	dld_dip = dip;
309 
310 	/*
311 	 * Log the fact that the driver is now attached.
312 	 */
313 	ddi_report_dev(dip);
314 	return (DDI_SUCCESS);
315 }
316 
317 /*
318  * devo_detach: detach(9e)
319  */
320 static int
321 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
322 {
323 	if (cmd != DDI_DETACH)
324 		return (DDI_FAILURE);
325 
326 	ASSERT(dld_dip == dip);
327 
328 	/*
329 	 * Remove the control node.
330 	 */
331 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
332 	dld_dip = NULL;
333 
334 	return (DDI_SUCCESS);
335 }
336 
337 /*
338  * dld control node open procedure.
339  */
340 /*ARGSUSED*/
341 static int
342 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
343 {
344 	dld_ctl_str_t	*ctls;
345 	minor_t		minor;
346 	queue_t *oq =	OTHERQ(rq);
347 
348 	if (sflag == MODOPEN)
349 		return (ENOTSUP);
350 
351 	/*
352 	 * This is a cloning driver and therefore each queue should only
353 	 * ever get opened once.
354 	 */
355 	if (rq->q_ptr != NULL)
356 		return (EBUSY);
357 
358 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
359 	if (minor == 0)
360 		return (ENOMEM);
361 
362 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
363 	if (ctls == NULL) {
364 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
365 		return (ENOMEM);
366 	}
367 
368 	ctls->cs_minor = minor;
369 	ctls->cs_wq = WR(rq);
370 
371 	rq->q_ptr = ctls;
372 	oq->q_ptr = ctls;
373 
374 	/*
375 	 * Enable the queue srv(9e) routine.
376 	 */
377 	qprocson(rq);
378 
379 	/*
380 	 * Construct a cloned dev_t to hand back.
381 	 */
382 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
383 	return (0);
384 }
385 
386 /*
387  * dld control node close procedure.
388  */
389 static int
390 drv_close(queue_t *rq)
391 {
392 	dld_ctl_str_t	*ctls;
393 
394 	ctls = rq->q_ptr;
395 	ASSERT(ctls != NULL);
396 
397 	/*
398 	 * Disable the queue srv(9e) routine.
399 	 */
400 	qprocsoff(rq);
401 
402 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
403 
404 	kmem_free(ctls, sizeof (dld_ctl_str_t));
405 
406 	return (0);
407 }
408 
409 /*
410  * DLDIOC_ATTR
411  */
412 static void
413 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
414 {
415 	dld_ioc_attr_t		*diap;
416 	dls_dl_handle_t		dlh;
417 	dls_vlan_t		*dvp;
418 	int			err;
419 	queue_t			*q = ctls->cs_wq;
420 
421 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
422 		goto failed;
423 
424 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
425 
426 	if ((err = dls_devnet_hold_tmp(diap->dia_linkid, &dlh)) != 0)
427 		goto failed;
428 
429 	if ((err = dls_vlan_hold(dls_devnet_mac(dlh),
430 	    dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) {
431 		dls_devnet_rele_tmp(dlh);
432 		goto failed;
433 	}
434 	mac_sdu_get(dvp->dv_dlp->dl_mh, NULL, &diap->dia_max_sdu);
435 
436 	dls_vlan_rele(dvp);
437 	dls_devnet_rele_tmp(dlh);
438 
439 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
440 	return;
441 
442 failed:
443 	ASSERT(err != 0);
444 	miocnak(q, mp, 0, err);
445 }
446 
447 /*
448  * DLDIOC_PHYS_ATTR
449  */
450 static void
451 drv_ioc_phys_attr(dld_ctl_str_t *ctls, mblk_t *mp)
452 {
453 	dld_ioc_phys_attr_t	*dipp;
454 	int			err;
455 	dls_dl_handle_t		dlh;
456 	dls_dev_handle_t	ddh;
457 	dev_t			phydev;
458 	queue_t			*q = ctls->cs_wq;
459 
460 	if ((err = miocpullup(mp, sizeof (dld_ioc_phys_attr_t))) != 0)
461 		goto failed;
462 
463 	dipp = (dld_ioc_phys_attr_t *)mp->b_cont->b_rptr;
464 
465 	/*
466 	 * Every physical link should have its physical dev_t kept in the
467 	 * daemon. If not, it is not a valid physical link.
468 	 */
469 	if (dls_mgmt_get_phydev(dipp->dip_linkid, &phydev) != 0) {
470 		err = EINVAL;
471 		goto failed;
472 	}
473 
474 	/*
475 	 * Although this is a valid physical link, it might already be removed
476 	 * by DR or during system shutdown. softmac_hold_device() would return
477 	 * ENOENT in this case.
478 	 */
479 	if ((err = softmac_hold_device(phydev, &ddh)) != 0)
480 		goto failed;
481 
482 	if (dls_devnet_hold_tmp(dipp->dip_linkid, &dlh) != 0) {
483 		/*
484 		 * Although this is an active physical link, its link type is
485 		 * not supported by GLDv3, and therefore it does not have
486 		 * vanity naming support.
487 		 */
488 		dipp->dip_novanity = B_TRUE;
489 	} else {
490 		dipp->dip_novanity = B_FALSE;
491 		dls_devnet_rele_tmp(dlh);
492 	}
493 	/*
494 	 * Get the physical device name from the major number and the instance
495 	 * number derived from phydev.
496 	 */
497 	(void) snprintf(dipp->dip_dev, MAXLINKNAMELEN, "%s%d",
498 	    ddi_major_to_name(getmajor(phydev)), getminor(phydev) - 1);
499 
500 	softmac_rele_device(ddh);
501 
502 	miocack(q, mp, sizeof (dld_ioc_phys_attr_t), 0);
503 	return;
504 
505 failed:
506 	miocnak(q, mp, 0, err);
507 }
508 
509 /*
510  * DLDIOCSETPROP
511  */
512 static void
513 drv_ioc_prop_common(dld_ctl_str_t *ctls, mblk_t *mp, boolean_t set)
514 {
515 	int		err = EINVAL, dsize;
516 	queue_t		*q = ctls->cs_wq;
517 	dld_ioc_prop_t	*dipp;
518 	dls_dl_handle_t 	dlh;
519 	dls_vlan_t		*dvp;
520 	datalink_id_t 		linkid;
521 	mac_prop_t		macprop;
522 
523 	if ((err = miocpullup(mp, sizeof (dld_ioc_prop_t))) != 0)
524 		goto done;
525 	dipp = (dld_ioc_prop_t *)mp->b_cont->b_rptr;
526 
527 	dsize = sizeof (dld_ioc_prop_t) + dipp->pr_valsize - 1;
528 	if ((err = miocpullup(mp, dsize)) != 0)
529 		goto done;
530 	dipp = (dld_ioc_prop_t *)mp->b_cont->b_rptr;
531 
532 	linkid = dipp->pr_linkid;
533 
534 	if ((err = dls_devnet_hold_tmp(linkid, &dlh)) != 0)
535 		goto done;
536 
537 	if ((err = dls_vlan_hold(dls_devnet_mac(dlh),
538 	    dls_devnet_vid(dlh), &dvp, B_FALSE, B_FALSE)) != 0) {
539 		dls_devnet_rele_tmp(dlh);
540 		goto done;
541 	}
542 
543 	macprop.mp_name = dipp->pr_name;
544 	macprop.mp_id = dipp->pr_num;
545 	macprop.mp_flags = dipp->pr_flags;
546 
547 	if (set)
548 		err = mac_set_prop(dvp->dv_dlp->dl_mh, &macprop,
549 		    dipp->pr_val, dipp->pr_valsize);
550 	else
551 		err = mac_get_prop(dvp->dv_dlp->dl_mh, &macprop,
552 		    dipp->pr_val, dipp->pr_valsize);
553 
554 	dls_vlan_rele(dvp);
555 	dls_devnet_rele_tmp(dlh);
556 done:
557 	if (err == 0)
558 		miocack(q, mp, dsize, 0);
559 	else
560 		miocnak(q, mp, 0, err);
561 }
562 
563 static void
564 drv_ioc_setprop(dld_ctl_str_t *ctls, mblk_t *mp)
565 {
566 	drv_ioc_prop_common(ctls, mp, B_TRUE);
567 }
568 
569 static void
570 drv_ioc_getprop(dld_ctl_str_t *ctls, mblk_t *mp)
571 {
572 	drv_ioc_prop_common(ctls, mp, B_FALSE);
573 }
574 
575 /*
576  * DLDIOC_CREATE_VLAN
577  */
578 static void
579 drv_ioc_create_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
580 {
581 	dld_ioc_create_vlan_t	*dicp;
582 	int			err;
583 	queue_t			*q = ctls->cs_wq;
584 
585 	if ((err = miocpullup(mp, sizeof (dld_ioc_create_vlan_t))) != 0)
586 		goto failed;
587 
588 	dicp = (dld_ioc_create_vlan_t *)mp->b_cont->b_rptr;
589 
590 	if ((err = dls_devnet_create_vlan(dicp->dic_vlanid,
591 	    dicp->dic_linkid, dicp->dic_vid, dicp->dic_force)) != 0) {
592 		goto failed;
593 	}
594 
595 	miocack(q, mp, 0, 0);
596 	return;
597 
598 failed:
599 	miocnak(q, mp, 0, err);
600 }
601 
602 /*
603  * DLDIOC_DELETE_VLAN
604  */
605 static void
606 drv_ioc_delete_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
607 {
608 	dld_ioc_delete_vlan_t	*didp;
609 	int			err;
610 	queue_t			*q = ctls->cs_wq;
611 
612 	if ((err = miocpullup(mp, sizeof (dld_ioc_delete_vlan_t))) != 0)
613 		goto done;
614 
615 	didp = (dld_ioc_delete_vlan_t *)mp->b_cont->b_rptr;
616 	err = dls_devnet_destroy_vlan(didp->did_linkid);
617 
618 done:
619 	if (err == 0)
620 		miocack(q, mp, 0, 0);
621 	else
622 		miocnak(q, mp, 0, err);
623 }
624 
625 /*
626  * DLDIOC_VLAN_ATTR
627  */
628 static void
629 drv_ioc_vlan_attr(dld_ctl_str_t *ctls, mblk_t *mp)
630 {
631 	dld_ioc_vlan_attr_t	*divp;
632 	dls_dl_handle_t		dlh;
633 	uint16_t		vid;
634 	dls_vlan_t		*dvp;
635 	int			err;
636 	queue_t			*q = ctls->cs_wq;
637 
638 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_attr_t))) != 0)
639 		goto failed;
640 
641 	divp = (dld_ioc_vlan_attr_t *)mp->b_cont->b_rptr;
642 
643 	/*
644 	 * Hold this link to prevent it from being deleted.
645 	 */
646 	err = dls_devnet_hold_tmp(divp->div_vlanid, &dlh);
647 	if (err != 0)
648 		goto failed;
649 
650 	if ((vid = dls_devnet_vid(dlh)) == VLAN_ID_NONE) {
651 		dls_devnet_rele_tmp(dlh);
652 		err = EINVAL;
653 		goto failed;
654 	}
655 
656 	err = dls_vlan_hold(dls_devnet_mac(dlh), vid, &dvp, B_FALSE, B_FALSE);
657 	if (err != 0) {
658 		dls_devnet_rele_tmp(dlh);
659 		err = EINVAL;
660 		goto failed;
661 	}
662 
663 	divp->div_linkid = dls_devnet_linkid(dlh);
664 	divp->div_implicit = !dls_devnet_is_explicit(dlh);
665 	divp->div_vid = vid;
666 	divp->div_force = dvp->dv_force;
667 
668 	dls_vlan_rele(dvp);
669 	dls_devnet_rele_tmp(dlh);
670 	miocack(q, mp, sizeof (dld_ioc_vlan_attr_t), 0);
671 	return;
672 
673 failed:
674 	miocnak(q, mp, 0, err);
675 }
676 
677 /*
678  * DLDIOC_RENAME.
679  *
680  * This function handles two cases of link renaming. See more in comments above
681  * dls_datalink_rename().
682  */
683 static void
684 drv_ioc_rename(dld_ctl_str_t *ctls, mblk_t *mp)
685 {
686 	dld_ioc_rename_t	*dir;
687 	mod_hash_key_t		key;
688 	mod_hash_val_t		val;
689 	int			err;
690 	queue_t			*q = ctls->cs_wq;
691 
692 	if ((err = miocpullup(mp, sizeof (dld_ioc_rename_t))) != 0)
693 		goto done;
694 
695 	dir = (dld_ioc_rename_t *)mp->b_cont->b_rptr;
696 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
697 	    dir->dir_link)) != 0) {
698 		goto done;
699 	}
700 
701 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
702 		goto done;
703 
704 	/*
705 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
706 	 * renaming request is to rename a valid physical link (dir_linkid1)
707 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
708 	 * or during system shutdown). In this case, the link (specified by
709 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
710 	 * and dir_linkid1 and its configuration would be lost.
711 	 *
712 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
713 	 */
714 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
715 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
716 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
717 		rw_exit(&dld_ap_hash_lock);
718 		goto done;
719 	}
720 
721 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
722 	kmem_free(val, sizeof (dld_ap_t));
723 	rw_exit(&dld_ap_hash_lock);
724 
725 done:
726 	if (err == 0)
727 		miocack(q, mp, 0, 0);
728 	else
729 		miocnak(q, mp, 0, err);
730 }
731 
732 /*
733  * DLDIOC_SETAUTOPUSH
734  */
735 static void
736 drv_ioc_setap(dld_ctl_str_t *ctls, mblk_t *mp)
737 {
738 	dld_ioc_ap_t	*diap;
739 	dld_ap_t	*dap;
740 	int		i, err;
741 	queue_t		*q = ctls->cs_wq;
742 	mod_hash_key_t	key;
743 
744 	if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0)
745 		goto failed;
746 
747 	diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr;
748 	if (diap->dia_npush == 0 || diap->dia_npush > MAXAPUSH) {
749 		err = EINVAL;
750 		goto failed;
751 	}
752 
753 	/*
754 	 * Validate that the specified list of modules exist.
755 	 */
756 	for (i = 0; i < diap->dia_npush; i++) {
757 		if (fmodsw_find(diap->dia_aplist[i], FMODSW_LOAD) == NULL) {
758 			err = EINVAL;
759 			goto failed;
760 		}
761 	}
762 
763 	key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid;
764 
765 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
766 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
767 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
768 		if (dap == NULL) {
769 			rw_exit(&dld_ap_hash_lock);
770 			err = ENOMEM;
771 			goto failed;
772 		}
773 
774 		dap->da_linkid = diap->dia_linkid;
775 		err = mod_hash_insert(dld_ap_hashp, key, (mod_hash_val_t)dap);
776 		ASSERT(err == 0);
777 	}
778 
779 	/*
780 	 * Update the configuration.
781 	 */
782 	dap->da_anchor = diap->dia_anchor;
783 	dap->da_npush = diap->dia_npush;
784 	for (i = 0; i < diap->dia_npush; i++) {
785 		(void) strlcpy(dap->da_aplist[i], diap->dia_aplist[i],
786 		    FMNAMESZ + 1);
787 	}
788 	rw_exit(&dld_ap_hash_lock);
789 
790 	miocack(q, mp, 0, 0);
791 	return;
792 
793 failed:
794 	miocnak(q, mp, 0, err);
795 }
796 
797 /*
798  * DLDIOC_GETAUTOPUSH
799  */
800 static void
801 drv_ioc_getap(dld_ctl_str_t *ctls, mblk_t *mp)
802 {
803 	dld_ioc_ap_t	*diap;
804 	dld_ap_t	*dap;
805 	int		i, err;
806 	queue_t		*q = ctls->cs_wq;
807 
808 	if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0)
809 		goto failed;
810 
811 	diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr;
812 
813 	rw_enter(&dld_ap_hash_lock, RW_READER);
814 	if (mod_hash_find(dld_ap_hashp,
815 	    (mod_hash_key_t)(uintptr_t)diap->dia_linkid,
816 	    (mod_hash_val_t *)&dap) != 0) {
817 		err = ENOENT;
818 		rw_exit(&dld_ap_hash_lock);
819 		goto failed;
820 	}
821 
822 	/*
823 	 * Retrieve the configuration.
824 	 */
825 	diap->dia_anchor = dap->da_anchor;
826 	diap->dia_npush = dap->da_npush;
827 	for (i = 0; i < dap->da_npush; i++) {
828 		(void) strlcpy(diap->dia_aplist[i], dap->da_aplist[i],
829 		    FMNAMESZ + 1);
830 	}
831 	rw_exit(&dld_ap_hash_lock);
832 
833 	miocack(q, mp, sizeof (dld_ioc_ap_t), 0);
834 	return;
835 
836 failed:
837 	miocnak(q, mp, 0, err);
838 }
839 
840 /*
841  * DLDIOC_CLRAUTOPUSH
842  */
843 static void
844 drv_ioc_clrap(dld_ctl_str_t *ctls, mblk_t *mp)
845 {
846 	dld_ioc_ap_t	*diap;
847 	mod_hash_val_t	val;
848 	mod_hash_key_t	key;
849 	int		err;
850 	queue_t		*q = ctls->cs_wq;
851 
852 	if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0)
853 		goto done;
854 
855 	diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr;
856 	key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid;
857 
858 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
859 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
860 		rw_exit(&dld_ap_hash_lock);
861 		goto done;
862 	}
863 
864 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
865 	kmem_free(val, sizeof (dld_ap_t));
866 	rw_exit(&dld_ap_hash_lock);
867 
868 done:
869 	if (err == 0)
870 		miocack(q, mp, 0, 0);
871 	else
872 		miocnak(q, mp, 0, err);
873 }
874 
875 /*
876  * DLDIOC_DOORSERVER
877  */
878 static void
879 drv_ioc_doorserver(dld_ctl_str_t *ctls, mblk_t *mp)
880 {
881 	queue_t		*q = ctls->cs_wq;
882 	dld_ioc_door_t	*did;
883 	int		err;
884 
885 	if ((err = miocpullup(mp, sizeof (dld_ioc_door_t))) != 0)
886 		goto done;
887 
888 	did = (dld_ioc_door_t *)mp->b_cont->b_rptr;
889 	err = dls_mgmt_door_set(did->did_start_door);
890 
891 done:
892 	if (err == 0)
893 		miocack(q, mp, 0, 0);
894 	else
895 		miocnak(q, mp, 0, err);
896 }
897 
898 /*
899  * DLDIOC_SETZID
900  */
901 static void
902 drv_ioc_setzid(dld_ctl_str_t *ctls, mblk_t *mp)
903 {
904 	queue_t			*q = ctls->cs_wq;
905 	dld_ioc_setzid_t	*dis;
906 	int			err;
907 
908 	if ((err = miocpullup(mp, sizeof (dld_ioc_setzid_t))) != 0)
909 		goto done;
910 
911 	dis = (dld_ioc_setzid_t *)mp->b_cont->b_rptr;
912 	err = dls_devnet_setzid(dis->dis_link, dis->dis_zid);
913 
914 done:
915 	if (err == 0)
916 		miocack(q, mp, 0, 0);
917 	else
918 		miocnak(q, mp, 0, err);
919 }
920 
921 /*
922  * DLDIOC_GETZID
923  */
924 static void
925 drv_ioc_getzid(dld_ctl_str_t *ctls, mblk_t *mp)
926 {
927 	queue_t			*q = ctls->cs_wq;
928 	dld_ioc_getzid_t	*dig;
929 	int			err;
930 
931 	if ((err = miocpullup(mp, sizeof (dld_ioc_getzid_t))) != 0)
932 		goto done;
933 
934 	dig = (dld_ioc_getzid_t *)mp->b_cont->b_rptr;
935 	err = dls_devnet_getzid(dig->dig_linkid, &dig->dig_zid);
936 
937 done:
938 	if (err == 0)
939 		miocack(q, mp, sizeof (dld_ioc_getzid_t), 0);
940 	else
941 		miocnak(q, mp, 0, err);
942 }
943 
944 /*
945  * Process an IOCTL message received by the control node.
946  */
947 static void
948 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
949 {
950 	uint_t	cmd;
951 
952 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
953 	switch (cmd) {
954 	case DLDIOC_ATTR:
955 		drv_ioc_attr(ctls, mp);
956 		return;
957 	case DLDIOC_PHYS_ATTR:
958 		drv_ioc_phys_attr(ctls, mp);
959 		return;
960 	case DLDIOC_SECOBJ_SET:
961 		drv_ioc_secobj_set(ctls, mp);
962 		return;
963 	case DLDIOC_SECOBJ_GET:
964 		drv_ioc_secobj_get(ctls, mp);
965 		return;
966 	case DLDIOC_SECOBJ_UNSET:
967 		drv_ioc_secobj_unset(ctls, mp);
968 		return;
969 	case DLDIOCSETPROP:
970 		drv_ioc_setprop(ctls, mp);
971 		return;
972 	case DLDIOCGETPROP:
973 		drv_ioc_getprop(ctls, mp);
974 		return;
975 	case DLDIOC_CREATE_VLAN:
976 		drv_ioc_create_vlan(ctls, mp);
977 		return;
978 	case DLDIOC_DELETE_VLAN:
979 		drv_ioc_delete_vlan(ctls, mp);
980 		return;
981 	case DLDIOC_VLAN_ATTR:
982 		drv_ioc_vlan_attr(ctls, mp);
983 		return;
984 	case DLDIOC_SETAUTOPUSH:
985 		drv_ioc_setap(ctls, mp);
986 		return;
987 	case DLDIOC_GETAUTOPUSH:
988 		drv_ioc_getap(ctls, mp);
989 		return;
990 	case DLDIOC_CLRAUTOPUSH:
991 		drv_ioc_clrap(ctls, mp);
992 		return;
993 	case DLDIOC_DOORSERVER:
994 		drv_ioc_doorserver(ctls, mp);
995 		return;
996 	case DLDIOC_SETZID:
997 		drv_ioc_setzid(ctls, mp);
998 		return;
999 	case DLDIOC_GETZID:
1000 		drv_ioc_getzid(ctls, mp);
1001 		return;
1002 	case DLDIOC_RENAME:
1003 		drv_ioc_rename(ctls, mp);
1004 		return;
1005 	default:
1006 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
1007 		return;
1008 	}
1009 }
1010 
1011 /*
1012  * Write side put routine of the dld control node.
1013  */
1014 static void
1015 drv_uw_put(queue_t *q, mblk_t *mp)
1016 {
1017 	dld_ctl_str_t *ctls = q->q_ptr;
1018 
1019 	switch (mp->b_datap->db_type) {
1020 	case M_IOCTL:
1021 		drv_ioc(ctls, mp);
1022 		break;
1023 	default:
1024 		freemsg(mp);
1025 		break;
1026 	}
1027 }
1028 
1029 /*
1030  * Write-side service procedure.
1031  */
1032 void
1033 drv_uw_srv(queue_t *q)
1034 {
1035 	mblk_t *mp;
1036 
1037 	while (mp = getq(q))
1038 		drv_uw_put(q, mp);
1039 }
1040 
1041 /*
1042  * Check for GLDv3 autopush information.  There are three cases:
1043  *
1044  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
1045  *	fill dlap in with that information and return 0.
1046  *
1047  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
1048  *	configuration, then replace devp with the physical device (if one
1049  *	exists) and return 1.  This allows stropen() to find the old-school
1050  *	per-driver autopush configuration.  (For softmac, the result is that
1051  *	the softmac dev_t is replaced with the legacy device's dev_t).
1052  *
1053  *   3. If neither of the above apply, don't touch the args and return -1.
1054  */
1055 int
1056 dld_autopush(dev_t *devp, struct dlautopush *dlap)
1057 {
1058 	dld_ap_t	*dap;
1059 	datalink_id_t	linkid;
1060 	dev_t		phydev;
1061 
1062 	if (!GLDV3_DRV(getmajor(*devp)))
1063 		return (-1);
1064 
1065 	/*
1066 	 * Find the linkid by the link's dev_t.
1067 	 */
1068 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1069 		return (-1);
1070 
1071 	/*
1072 	 * Find the autopush configuration associated with the linkid.
1073 	 */
1074 	rw_enter(&dld_ap_hash_lock, RW_READER);
1075 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1076 	    (mod_hash_val_t *)&dap) == 0) {
1077 		*dlap = dap->da_ap;
1078 		rw_exit(&dld_ap_hash_lock);
1079 		return (0);
1080 	}
1081 	rw_exit(&dld_ap_hash_lock);
1082 
1083 	if (dls_devnet_phydev(linkid, &phydev) != 0)
1084 		return (-1);
1085 
1086 	*devp = phydev;
1087 	return (1);
1088 }
1089 
1090 /*
1091  * Secure objects implementation
1092  */
1093 
1094 /* ARGSUSED */
1095 static int
1096 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1097 {
1098 	bzero(buf, sizeof (dld_secobj_t));
1099 	return (0);
1100 }
1101 
1102 static void
1103 drv_secobj_init(void)
1104 {
1105 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1106 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1107 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1108 	    NULL, NULL, NULL, 0);
1109 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1110 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1111 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1112 }
1113 
1114 static void
1115 drv_secobj_fini(void)
1116 {
1117 	mod_hash_destroy_hash(drv_secobj_hash);
1118 	kmem_cache_destroy(drv_secobj_cachep);
1119 	rw_destroy(&drv_secobj_lock);
1120 }
1121 
1122 static void
1123 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
1124 {
1125 	dld_ioc_secobj_set_t	*ssp;
1126 	dld_secobj_t		*sobjp, *objp;
1127 	int			err = EINVAL;
1128 	queue_t			*q = ctls->cs_wq;
1129 
1130 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
1131 		goto failed;
1132 
1133 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
1134 	sobjp = &ssp->ss_obj;
1135 
1136 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1137 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1138 		goto failed;
1139 
1140 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1141 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1142 		goto failed;
1143 
1144 	rw_enter(&drv_secobj_lock, RW_WRITER);
1145 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1146 	    (mod_hash_val_t *)&objp);
1147 	if (err == 0) {
1148 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1149 			err = EEXIST;
1150 			rw_exit(&drv_secobj_lock);
1151 			goto failed;
1152 		}
1153 	} else {
1154 		ASSERT(err == MH_ERR_NOTFOUND);
1155 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1156 			err = ENOENT;
1157 			rw_exit(&drv_secobj_lock);
1158 			goto failed;
1159 		}
1160 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1161 		(void) strlcpy(objp->so_name, sobjp->so_name,
1162 		    DLD_SECOBJ_NAME_MAX);
1163 
1164 		err = mod_hash_insert(drv_secobj_hash,
1165 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
1166 		ASSERT(err == 0);
1167 	}
1168 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1169 	objp->so_len = sobjp->so_len;
1170 	objp->so_class = sobjp->so_class;
1171 	rw_exit(&drv_secobj_lock);
1172 	miocack(q, mp, 0, 0);
1173 	return;
1174 
1175 failed:
1176 	ASSERT(err != 0);
1177 	miocnak(q, mp, 0, err);
1178 }
1179 
1180 typedef struct dld_secobj_state {
1181 	uint_t		ss_free;
1182 	uint_t		ss_count;
1183 	int		ss_rc;
1184 	dld_secobj_t	*ss_objp;
1185 } dld_secobj_state_t;
1186 
1187 /* ARGSUSED */
1188 static uint_t
1189 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1190 {
1191 	dld_secobj_state_t	*statep = arg;
1192 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1193 
1194 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1195 		statep->ss_rc = ENOSPC;
1196 		return (MH_WALK_TERMINATE);
1197 	}
1198 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
1199 	statep->ss_objp++;
1200 	statep->ss_free -= sizeof (dld_secobj_t);
1201 	statep->ss_count++;
1202 	return (MH_WALK_CONTINUE);
1203 }
1204 
1205 static void
1206 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
1207 {
1208 	dld_ioc_secobj_get_t	*sgp;
1209 	dld_secobj_t		*sobjp, *objp;
1210 	int			err = EINVAL;
1211 	uint_t			extra = 0;
1212 	queue_t			*q = ctls->cs_wq;
1213 	mblk_t			*bp;
1214 
1215 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
1216 		goto failed;
1217 
1218 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
1219 		goto failed;
1220 
1221 	freemsg(mp->b_cont);
1222 	mp->b_cont = bp;
1223 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
1224 	sobjp = &sgp->sg_obj;
1225 
1226 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1227 		goto failed;
1228 
1229 	rw_enter(&drv_secobj_lock, RW_READER);
1230 	if (sobjp->so_name[0] != '\0') {
1231 		err = mod_hash_find(drv_secobj_hash,
1232 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1233 		if (err != 0) {
1234 			ASSERT(err == MH_ERR_NOTFOUND);
1235 			err = ENOENT;
1236 			rw_exit(&drv_secobj_lock);
1237 			goto failed;
1238 		}
1239 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1240 		sobjp->so_len = objp->so_len;
1241 		sobjp->so_class = objp->so_class;
1242 		sgp->sg_count = 1;
1243 	} else {
1244 		dld_secobj_state_t	state;
1245 
1246 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
1247 		state.ss_count = 0;
1248 		state.ss_rc = 0;
1249 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
1250 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1251 		if (state.ss_rc != 0) {
1252 			err = state.ss_rc;
1253 			rw_exit(&drv_secobj_lock);
1254 			goto failed;
1255 		}
1256 		sgp->sg_count = state.ss_count;
1257 		extra = state.ss_count * sizeof (dld_secobj_t);
1258 	}
1259 	rw_exit(&drv_secobj_lock);
1260 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
1261 	return;
1262 
1263 failed:
1264 	ASSERT(err != 0);
1265 	miocnak(q, mp, 0, err);
1266 
1267 }
1268 
1269 static void
1270 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
1271 {
1272 	dld_ioc_secobj_unset_t	*sup;
1273 	dld_secobj_t		*objp;
1274 	mod_hash_val_t		val;
1275 	int			err = EINVAL;
1276 	queue_t			*q = ctls->cs_wq;
1277 
1278 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
1279 		goto failed;
1280 
1281 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
1282 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1283 		goto failed;
1284 
1285 	rw_enter(&drv_secobj_lock, RW_WRITER);
1286 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1287 	    (mod_hash_val_t *)&objp);
1288 	if (err != 0) {
1289 		ASSERT(err == MH_ERR_NOTFOUND);
1290 		err = ENOENT;
1291 		rw_exit(&drv_secobj_lock);
1292 		goto failed;
1293 	}
1294 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1295 	    (mod_hash_val_t *)&val);
1296 	ASSERT(err == 0);
1297 	ASSERT(objp == (dld_secobj_t *)val);
1298 
1299 	kmem_cache_free(drv_secobj_cachep, objp);
1300 	rw_exit(&drv_secobj_lock);
1301 	miocack(q, mp, 0, 0);
1302 	return;
1303 
1304 failed:
1305 	ASSERT(err != 0);
1306 	miocnak(q, mp, 0, err);
1307 }
1308