xref: /titanic_52/usr/src/uts/common/io/dld/dld_drv.c (revision b6c3f7863936abeae522e48a13887dddeb691a45)
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 
546 	if (set)
547 		err = mac_set_prop(dvp->dv_dlp->dl_mh, &macprop,
548 		    dipp->pr_val, dipp->pr_valsize);
549 	else
550 		err = mac_get_prop(dvp->dv_dlp->dl_mh, &macprop,
551 		    dipp->pr_val, dipp->pr_valsize);
552 
553 	dls_vlan_rele(dvp);
554 	dls_devnet_rele_tmp(dlh);
555 done:
556 	if (err == 0)
557 		miocack(q, mp, dsize, 0);
558 	else
559 		miocnak(q, mp, 0, err);
560 }
561 
562 static void
563 drv_ioc_setprop(dld_ctl_str_t *ctls, mblk_t *mp)
564 {
565 	drv_ioc_prop_common(ctls, mp, B_TRUE);
566 }
567 
568 static void
569 drv_ioc_getprop(dld_ctl_str_t *ctls, mblk_t *mp)
570 {
571 	drv_ioc_prop_common(ctls, mp, B_FALSE);
572 }
573 
574 /*
575  * DLDIOC_CREATE_VLAN
576  */
577 static void
578 drv_ioc_create_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
579 {
580 	dld_ioc_create_vlan_t	*dicp;
581 	int			err;
582 	queue_t			*q = ctls->cs_wq;
583 
584 	if ((err = miocpullup(mp, sizeof (dld_ioc_create_vlan_t))) != 0)
585 		goto failed;
586 
587 	dicp = (dld_ioc_create_vlan_t *)mp->b_cont->b_rptr;
588 
589 	if ((err = dls_devnet_create_vlan(dicp->dic_vlanid,
590 	    dicp->dic_linkid, dicp->dic_vid, dicp->dic_force)) != 0) {
591 		goto failed;
592 	}
593 
594 	miocack(q, mp, 0, 0);
595 	return;
596 
597 failed:
598 	miocnak(q, mp, 0, err);
599 }
600 
601 /*
602  * DLDIOC_DELETE_VLAN
603  */
604 static void
605 drv_ioc_delete_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
606 {
607 	dld_ioc_delete_vlan_t	*didp;
608 	int			err;
609 	queue_t			*q = ctls->cs_wq;
610 
611 	if ((err = miocpullup(mp, sizeof (dld_ioc_delete_vlan_t))) != 0)
612 		goto done;
613 
614 	didp = (dld_ioc_delete_vlan_t *)mp->b_cont->b_rptr;
615 	err = dls_devnet_destroy_vlan(didp->did_linkid);
616 
617 done:
618 	if (err == 0)
619 		miocack(q, mp, 0, 0);
620 	else
621 		miocnak(q, mp, 0, err);
622 }
623 
624 /*
625  * DLDIOC_VLAN_ATTR
626  */
627 static void
628 drv_ioc_vlan_attr(dld_ctl_str_t *ctls, mblk_t *mp)
629 {
630 	dld_ioc_vlan_attr_t	*divp;
631 	dls_dl_handle_t		dlh;
632 	uint16_t		vid;
633 	dls_vlan_t		*dvp;
634 	int			err;
635 	queue_t			*q = ctls->cs_wq;
636 
637 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_attr_t))) != 0)
638 		goto failed;
639 
640 	divp = (dld_ioc_vlan_attr_t *)mp->b_cont->b_rptr;
641 
642 	/*
643 	 * Hold this link to prevent it from being deleted.
644 	 */
645 	err = dls_devnet_hold_tmp(divp->div_vlanid, &dlh);
646 	if (err != 0)
647 		goto failed;
648 
649 	if ((vid = dls_devnet_vid(dlh)) == VLAN_ID_NONE) {
650 		dls_devnet_rele_tmp(dlh);
651 		err = EINVAL;
652 		goto failed;
653 	}
654 
655 	err = dls_vlan_hold(dls_devnet_mac(dlh), vid, &dvp, B_FALSE, B_FALSE);
656 	if (err != 0) {
657 		dls_devnet_rele_tmp(dlh);
658 		err = EINVAL;
659 		goto failed;
660 	}
661 
662 	divp->div_linkid = dls_devnet_linkid(dlh);
663 	divp->div_implicit = !dls_devnet_is_explicit(dlh);
664 	divp->div_vid = vid;
665 	divp->div_force = dvp->dv_force;
666 
667 	dls_vlan_rele(dvp);
668 	dls_devnet_rele_tmp(dlh);
669 	miocack(q, mp, sizeof (dld_ioc_vlan_attr_t), 0);
670 	return;
671 
672 failed:
673 	miocnak(q, mp, 0, err);
674 }
675 
676 /*
677  * DLDIOC_RENAME.
678  *
679  * This function handles two cases of link renaming. See more in comments above
680  * dls_datalink_rename().
681  */
682 static void
683 drv_ioc_rename(dld_ctl_str_t *ctls, mblk_t *mp)
684 {
685 	dld_ioc_rename_t	*dir;
686 	mod_hash_key_t		key;
687 	mod_hash_val_t		val;
688 	int			err;
689 	queue_t			*q = ctls->cs_wq;
690 
691 	if ((err = miocpullup(mp, sizeof (dld_ioc_rename_t))) != 0)
692 		goto done;
693 
694 	dir = (dld_ioc_rename_t *)mp->b_cont->b_rptr;
695 	if ((err = dls_devnet_rename(dir->dir_linkid1, dir->dir_linkid2,
696 	    dir->dir_link)) != 0) {
697 		goto done;
698 	}
699 
700 	if (dir->dir_linkid2 == DATALINK_INVALID_LINKID)
701 		goto done;
702 
703 	/*
704 	 * if dir_linkid2 is not DATALINK_INVALID_LINKID, it means this
705 	 * renaming request is to rename a valid physical link (dir_linkid1)
706 	 * to a "removed" physical link (dir_linkid2, which is removed by DR
707 	 * or during system shutdown). In this case, the link (specified by
708 	 * dir_linkid1) would inherit all the configuration of dir_linkid2,
709 	 * and dir_linkid1 and its configuration would be lost.
710 	 *
711 	 * Remove per-link autopush configuration of dir_linkid1 in this case.
712 	 */
713 	key = (mod_hash_key_t)(uintptr_t)dir->dir_linkid1;
714 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
715 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
716 		rw_exit(&dld_ap_hash_lock);
717 		goto done;
718 	}
719 
720 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
721 	kmem_free(val, sizeof (dld_ap_t));
722 	rw_exit(&dld_ap_hash_lock);
723 
724 done:
725 	if (err == 0)
726 		miocack(q, mp, 0, 0);
727 	else
728 		miocnak(q, mp, 0, err);
729 }
730 
731 /*
732  * DLDIOC_SETAUTOPUSH
733  */
734 static void
735 drv_ioc_setap(dld_ctl_str_t *ctls, mblk_t *mp)
736 {
737 	dld_ioc_ap_t	*diap;
738 	dld_ap_t	*dap;
739 	int		i, err;
740 	queue_t		*q = ctls->cs_wq;
741 	mod_hash_key_t	key;
742 
743 	if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0)
744 		goto failed;
745 
746 	diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr;
747 	if (diap->dia_npush == 0 || diap->dia_npush > MAXAPUSH) {
748 		err = EINVAL;
749 		goto failed;
750 	}
751 
752 	/*
753 	 * Validate that the specified list of modules exist.
754 	 */
755 	for (i = 0; i < diap->dia_npush; i++) {
756 		if (fmodsw_find(diap->dia_aplist[i], FMODSW_LOAD) == NULL) {
757 			err = EINVAL;
758 			goto failed;
759 		}
760 	}
761 
762 	key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid;
763 
764 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
765 	if (mod_hash_find(dld_ap_hashp, key, (mod_hash_val_t *)&dap) != 0) {
766 		dap = kmem_zalloc(sizeof (dld_ap_t), KM_NOSLEEP);
767 		if (dap == NULL) {
768 			rw_exit(&dld_ap_hash_lock);
769 			err = ENOMEM;
770 			goto failed;
771 		}
772 
773 		dap->da_linkid = diap->dia_linkid;
774 		err = mod_hash_insert(dld_ap_hashp, key, (mod_hash_val_t)dap);
775 		ASSERT(err == 0);
776 	}
777 
778 	/*
779 	 * Update the configuration.
780 	 */
781 	dap->da_anchor = diap->dia_anchor;
782 	dap->da_npush = diap->dia_npush;
783 	for (i = 0; i < diap->dia_npush; i++) {
784 		(void) strlcpy(dap->da_aplist[i], diap->dia_aplist[i],
785 		    FMNAMESZ + 1);
786 	}
787 	rw_exit(&dld_ap_hash_lock);
788 
789 	miocack(q, mp, 0, 0);
790 	return;
791 
792 failed:
793 	miocnak(q, mp, 0, err);
794 }
795 
796 /*
797  * DLDIOC_GETAUTOPUSH
798  */
799 static void
800 drv_ioc_getap(dld_ctl_str_t *ctls, mblk_t *mp)
801 {
802 	dld_ioc_ap_t	*diap;
803 	dld_ap_t	*dap;
804 	int		i, err;
805 	queue_t		*q = ctls->cs_wq;
806 
807 	if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0)
808 		goto failed;
809 
810 	diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr;
811 
812 	rw_enter(&dld_ap_hash_lock, RW_READER);
813 	if (mod_hash_find(dld_ap_hashp,
814 	    (mod_hash_key_t)(uintptr_t)diap->dia_linkid,
815 	    (mod_hash_val_t *)&dap) != 0) {
816 		err = ENOENT;
817 		rw_exit(&dld_ap_hash_lock);
818 		goto failed;
819 	}
820 
821 	/*
822 	 * Retrieve the configuration.
823 	 */
824 	diap->dia_anchor = dap->da_anchor;
825 	diap->dia_npush = dap->da_npush;
826 	for (i = 0; i < dap->da_npush; i++) {
827 		(void) strlcpy(diap->dia_aplist[i], dap->da_aplist[i],
828 		    FMNAMESZ + 1);
829 	}
830 	rw_exit(&dld_ap_hash_lock);
831 
832 	miocack(q, mp, sizeof (dld_ioc_ap_t), 0);
833 	return;
834 
835 failed:
836 	miocnak(q, mp, 0, err);
837 }
838 
839 /*
840  * DLDIOC_CLRAUTOPUSH
841  */
842 static void
843 drv_ioc_clrap(dld_ctl_str_t *ctls, mblk_t *mp)
844 {
845 	dld_ioc_ap_t	*diap;
846 	mod_hash_val_t	val;
847 	mod_hash_key_t	key;
848 	int		err;
849 	queue_t		*q = ctls->cs_wq;
850 
851 	if ((err = miocpullup(mp, sizeof (dld_ioc_ap_t))) != 0)
852 		goto done;
853 
854 	diap = (dld_ioc_ap_t *)mp->b_cont->b_rptr;
855 	key = (mod_hash_key_t)(uintptr_t)diap->dia_linkid;
856 
857 	rw_enter(&dld_ap_hash_lock, RW_WRITER);
858 	if (mod_hash_find(dld_ap_hashp, key, &val) != 0) {
859 		rw_exit(&dld_ap_hash_lock);
860 		goto done;
861 	}
862 
863 	VERIFY(mod_hash_remove(dld_ap_hashp, key, &val) == 0);
864 	kmem_free(val, sizeof (dld_ap_t));
865 	rw_exit(&dld_ap_hash_lock);
866 
867 done:
868 	if (err == 0)
869 		miocack(q, mp, 0, 0);
870 	else
871 		miocnak(q, mp, 0, err);
872 }
873 
874 /*
875  * DLDIOC_DOORSERVER
876  */
877 static void
878 drv_ioc_doorserver(dld_ctl_str_t *ctls, mblk_t *mp)
879 {
880 	queue_t		*q = ctls->cs_wq;
881 	dld_ioc_door_t	*did;
882 	int		err;
883 
884 	if ((err = miocpullup(mp, sizeof (dld_ioc_door_t))) != 0)
885 		goto done;
886 
887 	did = (dld_ioc_door_t *)mp->b_cont->b_rptr;
888 	err = dls_mgmt_door_set(did->did_start_door);
889 
890 done:
891 	if (err == 0)
892 		miocack(q, mp, 0, 0);
893 	else
894 		miocnak(q, mp, 0, err);
895 }
896 
897 /*
898  * DLDIOC_SETZID
899  */
900 static void
901 drv_ioc_setzid(dld_ctl_str_t *ctls, mblk_t *mp)
902 {
903 	queue_t			*q = ctls->cs_wq;
904 	dld_ioc_setzid_t	*dis;
905 	int			err;
906 
907 	if ((err = miocpullup(mp, sizeof (dld_ioc_setzid_t))) != 0)
908 		goto done;
909 
910 	dis = (dld_ioc_setzid_t *)mp->b_cont->b_rptr;
911 	err = dls_devnet_setzid(dis->dis_link, dis->dis_zid);
912 
913 done:
914 	if (err == 0)
915 		miocack(q, mp, 0, 0);
916 	else
917 		miocnak(q, mp, 0, err);
918 }
919 
920 /*
921  * DLDIOC_GETZID
922  */
923 static void
924 drv_ioc_getzid(dld_ctl_str_t *ctls, mblk_t *mp)
925 {
926 	queue_t			*q = ctls->cs_wq;
927 	dld_ioc_getzid_t	*dig;
928 	int			err;
929 
930 	if ((err = miocpullup(mp, sizeof (dld_ioc_getzid_t))) != 0)
931 		goto done;
932 
933 	dig = (dld_ioc_getzid_t *)mp->b_cont->b_rptr;
934 	err = dls_devnet_getzid(dig->dig_linkid, &dig->dig_zid);
935 
936 done:
937 	if (err == 0)
938 		miocack(q, mp, sizeof (dld_ioc_getzid_t), 0);
939 	else
940 		miocnak(q, mp, 0, err);
941 }
942 
943 /*
944  * Process an IOCTL message received by the control node.
945  */
946 static void
947 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
948 {
949 	uint_t	cmd;
950 
951 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
952 	switch (cmd) {
953 	case DLDIOC_ATTR:
954 		drv_ioc_attr(ctls, mp);
955 		return;
956 	case DLDIOC_PHYS_ATTR:
957 		drv_ioc_phys_attr(ctls, mp);
958 		return;
959 	case DLDIOC_SECOBJ_SET:
960 		drv_ioc_secobj_set(ctls, mp);
961 		return;
962 	case DLDIOC_SECOBJ_GET:
963 		drv_ioc_secobj_get(ctls, mp);
964 		return;
965 	case DLDIOC_SECOBJ_UNSET:
966 		drv_ioc_secobj_unset(ctls, mp);
967 		return;
968 	case DLDIOCSETPROP:
969 		drv_ioc_setprop(ctls, mp);
970 		return;
971 	case DLDIOCGETPROP:
972 		drv_ioc_getprop(ctls, mp);
973 		return;
974 	case DLDIOC_CREATE_VLAN:
975 		drv_ioc_create_vlan(ctls, mp);
976 		return;
977 	case DLDIOC_DELETE_VLAN:
978 		drv_ioc_delete_vlan(ctls, mp);
979 		return;
980 	case DLDIOC_VLAN_ATTR:
981 		drv_ioc_vlan_attr(ctls, mp);
982 		return;
983 	case DLDIOC_SETAUTOPUSH:
984 		drv_ioc_setap(ctls, mp);
985 		return;
986 	case DLDIOC_GETAUTOPUSH:
987 		drv_ioc_getap(ctls, mp);
988 		return;
989 	case DLDIOC_CLRAUTOPUSH:
990 		drv_ioc_clrap(ctls, mp);
991 		return;
992 	case DLDIOC_DOORSERVER:
993 		drv_ioc_doorserver(ctls, mp);
994 		return;
995 	case DLDIOC_SETZID:
996 		drv_ioc_setzid(ctls, mp);
997 		return;
998 	case DLDIOC_GETZID:
999 		drv_ioc_getzid(ctls, mp);
1000 		return;
1001 	case DLDIOC_RENAME:
1002 		drv_ioc_rename(ctls, mp);
1003 		return;
1004 	default:
1005 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
1006 		return;
1007 	}
1008 }
1009 
1010 /*
1011  * Write side put routine of the dld control node.
1012  */
1013 static void
1014 drv_uw_put(queue_t *q, mblk_t *mp)
1015 {
1016 	dld_ctl_str_t *ctls = q->q_ptr;
1017 
1018 	switch (mp->b_datap->db_type) {
1019 	case M_IOCTL:
1020 		drv_ioc(ctls, mp);
1021 		break;
1022 	default:
1023 		freemsg(mp);
1024 		break;
1025 	}
1026 }
1027 
1028 /*
1029  * Write-side service procedure.
1030  */
1031 void
1032 drv_uw_srv(queue_t *q)
1033 {
1034 	mblk_t *mp;
1035 
1036 	while (mp = getq(q))
1037 		drv_uw_put(q, mp);
1038 }
1039 
1040 /*
1041  * Check for GLDv3 autopush information.  There are three cases:
1042  *
1043  *   1. If devp points to a GLDv3 datalink and it has autopush configuration,
1044  *	fill dlap in with that information and return 0.
1045  *
1046  *   2. If devp points to a GLDv3 datalink but it doesn't have autopush
1047  *	configuration, then replace devp with the physical device (if one
1048  *	exists) and return 1.  This allows stropen() to find the old-school
1049  *	per-driver autopush configuration.  (For softmac, the result is that
1050  *	the softmac dev_t is replaced with the legacy device's dev_t).
1051  *
1052  *   3. If neither of the above apply, don't touch the args and return -1.
1053  */
1054 int
1055 dld_autopush(dev_t *devp, struct dlautopush *dlap)
1056 {
1057 	dld_ap_t	*dap;
1058 	datalink_id_t	linkid;
1059 	dev_t		phydev;
1060 
1061 	if (!GLDV3_DRV(getmajor(*devp)))
1062 		return (-1);
1063 
1064 	/*
1065 	 * Find the linkid by the link's dev_t.
1066 	 */
1067 	if (dls_devnet_dev2linkid(*devp, &linkid) != 0)
1068 		return (-1);
1069 
1070 	/*
1071 	 * Find the autopush configuration associated with the linkid.
1072 	 */
1073 	rw_enter(&dld_ap_hash_lock, RW_READER);
1074 	if (mod_hash_find(dld_ap_hashp, (mod_hash_key_t)(uintptr_t)linkid,
1075 	    (mod_hash_val_t *)&dap) == 0) {
1076 		*dlap = dap->da_ap;
1077 		rw_exit(&dld_ap_hash_lock);
1078 		return (0);
1079 	}
1080 	rw_exit(&dld_ap_hash_lock);
1081 
1082 	if (dls_devnet_phydev(linkid, &phydev) != 0)
1083 		return (-1);
1084 
1085 	*devp = phydev;
1086 	return (1);
1087 }
1088 
1089 /*
1090  * Secure objects implementation
1091  */
1092 
1093 /* ARGSUSED */
1094 static int
1095 drv_secobj_ctor(void *buf, void *arg, int kmflag)
1096 {
1097 	bzero(buf, sizeof (dld_secobj_t));
1098 	return (0);
1099 }
1100 
1101 static void
1102 drv_secobj_init(void)
1103 {
1104 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
1105 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
1106 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
1107 	    NULL, NULL, NULL, 0);
1108 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
1109 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
1110 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
1111 }
1112 
1113 static void
1114 drv_secobj_fini(void)
1115 {
1116 	mod_hash_destroy_hash(drv_secobj_hash);
1117 	kmem_cache_destroy(drv_secobj_cachep);
1118 	rw_destroy(&drv_secobj_lock);
1119 }
1120 
1121 static void
1122 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
1123 {
1124 	dld_ioc_secobj_set_t	*ssp;
1125 	dld_secobj_t		*sobjp, *objp;
1126 	int			err = EINVAL;
1127 	queue_t			*q = ctls->cs_wq;
1128 
1129 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
1130 		goto failed;
1131 
1132 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
1133 	sobjp = &ssp->ss_obj;
1134 
1135 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP &&
1136 	    sobjp->so_class != DLD_SECOBJ_CLASS_WPA)
1137 		goto failed;
1138 
1139 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
1140 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
1141 		goto failed;
1142 
1143 	rw_enter(&drv_secobj_lock, RW_WRITER);
1144 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
1145 	    (mod_hash_val_t *)&objp);
1146 	if (err == 0) {
1147 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
1148 			err = EEXIST;
1149 			rw_exit(&drv_secobj_lock);
1150 			goto failed;
1151 		}
1152 	} else {
1153 		ASSERT(err == MH_ERR_NOTFOUND);
1154 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
1155 			err = ENOENT;
1156 			rw_exit(&drv_secobj_lock);
1157 			goto failed;
1158 		}
1159 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
1160 		(void) strlcpy(objp->so_name, sobjp->so_name,
1161 		    DLD_SECOBJ_NAME_MAX);
1162 
1163 		err = mod_hash_insert(drv_secobj_hash,
1164 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
1165 		ASSERT(err == 0);
1166 	}
1167 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
1168 	objp->so_len = sobjp->so_len;
1169 	objp->so_class = sobjp->so_class;
1170 	rw_exit(&drv_secobj_lock);
1171 	miocack(q, mp, 0, 0);
1172 	return;
1173 
1174 failed:
1175 	ASSERT(err != 0);
1176 	miocnak(q, mp, 0, err);
1177 }
1178 
1179 typedef struct dld_secobj_state {
1180 	uint_t		ss_free;
1181 	uint_t		ss_count;
1182 	int		ss_rc;
1183 	dld_secobj_t	*ss_objp;
1184 } dld_secobj_state_t;
1185 
1186 /* ARGSUSED */
1187 static uint_t
1188 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
1189 {
1190 	dld_secobj_state_t	*statep = arg;
1191 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
1192 
1193 	if (statep->ss_free < sizeof (dld_secobj_t)) {
1194 		statep->ss_rc = ENOSPC;
1195 		return (MH_WALK_TERMINATE);
1196 	}
1197 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
1198 	statep->ss_objp++;
1199 	statep->ss_free -= sizeof (dld_secobj_t);
1200 	statep->ss_count++;
1201 	return (MH_WALK_CONTINUE);
1202 }
1203 
1204 static void
1205 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
1206 {
1207 	dld_ioc_secobj_get_t	*sgp;
1208 	dld_secobj_t		*sobjp, *objp;
1209 	int			err = EINVAL;
1210 	uint_t			extra = 0;
1211 	queue_t			*q = ctls->cs_wq;
1212 	mblk_t			*bp;
1213 
1214 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
1215 		goto failed;
1216 
1217 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
1218 		goto failed;
1219 
1220 	freemsg(mp->b_cont);
1221 	mp->b_cont = bp;
1222 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
1223 	sobjp = &sgp->sg_obj;
1224 
1225 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1226 		goto failed;
1227 
1228 	rw_enter(&drv_secobj_lock, RW_READER);
1229 	if (sobjp->so_name[0] != '\0') {
1230 		err = mod_hash_find(drv_secobj_hash,
1231 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
1232 		if (err != 0) {
1233 			ASSERT(err == MH_ERR_NOTFOUND);
1234 			err = ENOENT;
1235 			rw_exit(&drv_secobj_lock);
1236 			goto failed;
1237 		}
1238 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
1239 		sobjp->so_len = objp->so_len;
1240 		sobjp->so_class = objp->so_class;
1241 		sgp->sg_count = 1;
1242 	} else {
1243 		dld_secobj_state_t	state;
1244 
1245 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
1246 		state.ss_count = 0;
1247 		state.ss_rc = 0;
1248 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
1249 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
1250 		if (state.ss_rc != 0) {
1251 			err = state.ss_rc;
1252 			rw_exit(&drv_secobj_lock);
1253 			goto failed;
1254 		}
1255 		sgp->sg_count = state.ss_count;
1256 		extra = state.ss_count * sizeof (dld_secobj_t);
1257 	}
1258 	rw_exit(&drv_secobj_lock);
1259 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
1260 	return;
1261 
1262 failed:
1263 	ASSERT(err != 0);
1264 	miocnak(q, mp, 0, err);
1265 
1266 }
1267 
1268 static void
1269 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
1270 {
1271 	dld_ioc_secobj_unset_t	*sup;
1272 	dld_secobj_t		*objp;
1273 	mod_hash_val_t		val;
1274 	int			err = EINVAL;
1275 	queue_t			*q = ctls->cs_wq;
1276 
1277 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
1278 		goto failed;
1279 
1280 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
1281 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
1282 		goto failed;
1283 
1284 	rw_enter(&drv_secobj_lock, RW_WRITER);
1285 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1286 	    (mod_hash_val_t *)&objp);
1287 	if (err != 0) {
1288 		ASSERT(err == MH_ERR_NOTFOUND);
1289 		err = ENOENT;
1290 		rw_exit(&drv_secobj_lock);
1291 		goto failed;
1292 	}
1293 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
1294 	    (mod_hash_val_t *)&val);
1295 	ASSERT(err == 0);
1296 	ASSERT(objp == (dld_secobj_t *)val);
1297 
1298 	kmem_cache_free(drv_secobj_cachep, objp);
1299 	rw_exit(&drv_secobj_lock);
1300 	miocack(q, mp, 0, 0);
1301 	return;
1302 
1303 failed:
1304 	ASSERT(err != 0);
1305 	miocnak(q, mp, 0, err);
1306 }
1307