xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision 9c1751294cd1ff9c27007f40ee6aacc943076cbf)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ba2e4443Sseb  * Common Development and Distribution License (the "License").
6ba2e4443Sseb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22f4b3ec61Sdh  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * Data-Link Driver
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include	<sys/conf.h>
33210db224Sericheng #include	<sys/mkdev.h>
347c478bd9Sstevel@tonic-gate #include	<sys/modctl.h>
35210db224Sericheng #include	<sys/stat.h>
36210db224Sericheng #include	<sys/strsun.h>
377c478bd9Sstevel@tonic-gate #include	<sys/dld.h>
387c478bd9Sstevel@tonic-gate #include	<sys/dld_impl.h>
39210db224Sericheng #include	<sys/dls_impl.h>
40f4b3ec61Sdh #include 	<sys/vlan.h>
41210db224Sericheng #include	<inet/common.h>
42210db224Sericheng 
43210db224Sericheng /*
44210db224Sericheng  * dld control node state, one per open control node session.
45210db224Sericheng  */
46210db224Sericheng typedef struct dld_ctl_str_s {
47210db224Sericheng 	minor_t cs_minor;
48210db224Sericheng 	queue_t *cs_wq;
49210db224Sericheng } dld_ctl_str_t;
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate static void	drv_init(void);
527c478bd9Sstevel@tonic-gate static int	drv_fini(void);
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
557c478bd9Sstevel@tonic-gate static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
567c478bd9Sstevel@tonic-gate static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
577c478bd9Sstevel@tonic-gate 
580ba2cbe9Sxc /*
590ba2cbe9Sxc  * Secure objects declarations
600ba2cbe9Sxc  */
610ba2cbe9Sxc #define	SECOBJ_WEP_HASHSZ	67
620ba2cbe9Sxc static krwlock_t	drv_secobj_lock;
630ba2cbe9Sxc static kmem_cache_t	*drv_secobj_cachep;
640ba2cbe9Sxc static mod_hash_t	*drv_secobj_hash;
650ba2cbe9Sxc static void		drv_secobj_init(void);
660ba2cbe9Sxc static void		drv_secobj_fini(void);
670ba2cbe9Sxc static void		drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *);
680ba2cbe9Sxc static void		drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *);
690ba2cbe9Sxc static void		drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *);
700ba2cbe9Sxc 
71210db224Sericheng /*
72210db224Sericheng  * The following entry points are private to dld and are used for control
73210db224Sericheng  * operations only. The entry points exported to mac drivers are defined
74210db224Sericheng  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
75210db224Sericheng  */
767c478bd9Sstevel@tonic-gate static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
777c478bd9Sstevel@tonic-gate static int	drv_close(queue_t *);
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate static void	drv_uw_put(queue_t *, mblk_t *);
807c478bd9Sstevel@tonic-gate static void	drv_uw_srv(queue_t *);
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate dev_info_t	*dld_dip;		/* dev_info_t for the driver */
83210db224Sericheng uint32_t	dld_opt = 0;		/* Global options */
84210db224Sericheng static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
857c478bd9Sstevel@tonic-gate 
867c478bd9Sstevel@tonic-gate static	struct	module_info	drv_info = {
877c478bd9Sstevel@tonic-gate 	0,			/* mi_idnum */
887c478bd9Sstevel@tonic-gate 	DLD_DRIVER_NAME,	/* mi_idname */
897c478bd9Sstevel@tonic-gate 	0,			/* mi_minpsz */
907c478bd9Sstevel@tonic-gate 	(64 * 1024),		/* mi_maxpsz */
917c478bd9Sstevel@tonic-gate 	1,			/* mi_hiwat */
927c478bd9Sstevel@tonic-gate 	0			/* mi_lowat */
937c478bd9Sstevel@tonic-gate };
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate static	struct qinit		drv_ur_init = {
967c478bd9Sstevel@tonic-gate 	NULL,			/* qi_putp */
977c478bd9Sstevel@tonic-gate 	NULL,			/* qi_srvp */
987c478bd9Sstevel@tonic-gate 	drv_open,		/* qi_qopen */
997c478bd9Sstevel@tonic-gate 	drv_close,		/* qi_qclose */
1007c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qadmin */
1017c478bd9Sstevel@tonic-gate 	&drv_info,		/* qi_minfo */
1027c478bd9Sstevel@tonic-gate 	NULL			/* qi_mstat */
1037c478bd9Sstevel@tonic-gate };
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate static	struct qinit		drv_uw_init = {
1067c478bd9Sstevel@tonic-gate 	(pfi_t)drv_uw_put,	/* qi_putp */
1077c478bd9Sstevel@tonic-gate 	(pfi_t)drv_uw_srv,	/* qi_srvp */
1087c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qopen */
1097c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qclose */
1107c478bd9Sstevel@tonic-gate 	NULL,			/* qi_qadmin */
1117c478bd9Sstevel@tonic-gate 	&drv_info,		/* qi_minfo */
1127c478bd9Sstevel@tonic-gate 	NULL			/* qi_mstat */
1137c478bd9Sstevel@tonic-gate };
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate static	struct streamtab	drv_stream = {
1167c478bd9Sstevel@tonic-gate 	&drv_ur_init,		/* st_rdinit */
1177c478bd9Sstevel@tonic-gate 	&drv_uw_init,		/* st_wrinit */
1187c478bd9Sstevel@tonic-gate 	NULL,			/* st_muxrinit */
1197c478bd9Sstevel@tonic-gate 	NULL			/* st_muxwinit */
1207c478bd9Sstevel@tonic-gate };
1217c478bd9Sstevel@tonic-gate 
1227c478bd9Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
123210db224Sericheng     nodev, drv_getinfo, D_MP, &drv_stream);
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate /*
1267c478bd9Sstevel@tonic-gate  * Module linkage information for the kernel.
1277c478bd9Sstevel@tonic-gate  */
1287c478bd9Sstevel@tonic-gate 
1297c478bd9Sstevel@tonic-gate extern	struct mod_ops		mod_driverops;
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate static	struct modldrv		drv_modldrv = {
1327c478bd9Sstevel@tonic-gate 	&mod_driverops,
1337c478bd9Sstevel@tonic-gate 	DLD_INFO,
1347c478bd9Sstevel@tonic-gate 	&drv_ops
1357c478bd9Sstevel@tonic-gate };
1367c478bd9Sstevel@tonic-gate 
1377c478bd9Sstevel@tonic-gate static	struct modlinkage	drv_modlinkage = {
1387c478bd9Sstevel@tonic-gate 	MODREV_1,
1397c478bd9Sstevel@tonic-gate 	&drv_modldrv,
1407c478bd9Sstevel@tonic-gate 	NULL
1417c478bd9Sstevel@tonic-gate };
1427c478bd9Sstevel@tonic-gate 
1437c478bd9Sstevel@tonic-gate int
1447c478bd9Sstevel@tonic-gate _init(void)
1457c478bd9Sstevel@tonic-gate {
1467c478bd9Sstevel@tonic-gate 	int	err;
1477c478bd9Sstevel@tonic-gate 
148210db224Sericheng 	drv_init();
149210db224Sericheng 
1507c478bd9Sstevel@tonic-gate 	if ((err = mod_install(&drv_modlinkage)) != 0)
1517c478bd9Sstevel@tonic-gate 		return (err);
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate 	return (0);
1547c478bd9Sstevel@tonic-gate }
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate int
1577c478bd9Sstevel@tonic-gate _fini(void)
1587c478bd9Sstevel@tonic-gate {
1597c478bd9Sstevel@tonic-gate 	int	err;
1607c478bd9Sstevel@tonic-gate 
1617c478bd9Sstevel@tonic-gate 	if ((err = mod_remove(&drv_modlinkage)) != 0)
1627c478bd9Sstevel@tonic-gate 		return (err);
1637c478bd9Sstevel@tonic-gate 
164210db224Sericheng 	if (drv_fini() != 0) {
165210db224Sericheng 		(void) mod_install(&drv_modlinkage);
166210db224Sericheng 		return (DDI_FAILURE);
167210db224Sericheng 	}
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	return (err);
1707c478bd9Sstevel@tonic-gate }
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate int
1737c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
1747c478bd9Sstevel@tonic-gate {
1757c478bd9Sstevel@tonic-gate 	return (mod_info(&drv_modlinkage, modinfop));
1767c478bd9Sstevel@tonic-gate }
1777c478bd9Sstevel@tonic-gate 
1787c478bd9Sstevel@tonic-gate /*
179210db224Sericheng  * Initialize component modules.
1807c478bd9Sstevel@tonic-gate  */
1817c478bd9Sstevel@tonic-gate static void
1827c478bd9Sstevel@tonic-gate drv_init(void)
1837c478bd9Sstevel@tonic-gate {
184210db224Sericheng 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
185210db224Sericheng 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
1860ba2cbe9Sxc 	drv_secobj_init();
1877c478bd9Sstevel@tonic-gate 	dld_str_init();
1887c478bd9Sstevel@tonic-gate }
1897c478bd9Sstevel@tonic-gate 
1907c478bd9Sstevel@tonic-gate static int
1917c478bd9Sstevel@tonic-gate drv_fini(void)
1927c478bd9Sstevel@tonic-gate {
1937c478bd9Sstevel@tonic-gate 	int	err;
1947c478bd9Sstevel@tonic-gate 
195210db224Sericheng 	if ((err = dld_str_fini()) != 0)
1967c478bd9Sstevel@tonic-gate 		return (err);
1977c478bd9Sstevel@tonic-gate 
1980ba2cbe9Sxc 	drv_secobj_fini();
199210db224Sericheng 	vmem_destroy(dld_ctl_vmem);
2007c478bd9Sstevel@tonic-gate 	return (0);
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate /*
2047c478bd9Sstevel@tonic-gate  * devo_getinfo: getinfo(9e)
2057c478bd9Sstevel@tonic-gate  */
2067c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2077c478bd9Sstevel@tonic-gate static int
2087c478bd9Sstevel@tonic-gate drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
2097c478bd9Sstevel@tonic-gate {
2107c478bd9Sstevel@tonic-gate 	if (dld_dip == NULL)
2117c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	switch (cmd) {
2147c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2157c478bd9Sstevel@tonic-gate 		*resp = (void *)0;
2167c478bd9Sstevel@tonic-gate 		break;
2177c478bd9Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2187c478bd9Sstevel@tonic-gate 		*resp = (void *)dld_dip;
2197c478bd9Sstevel@tonic-gate 		break;
2207c478bd9Sstevel@tonic-gate 	default:
2217c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2227c478bd9Sstevel@tonic-gate 	}
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2257c478bd9Sstevel@tonic-gate }
2267c478bd9Sstevel@tonic-gate 
2277c478bd9Sstevel@tonic-gate /*
2287c478bd9Sstevel@tonic-gate  * Check properties to set options. (See dld.h for property definitions).
2297c478bd9Sstevel@tonic-gate  */
2307c478bd9Sstevel@tonic-gate static void
2317c478bd9Sstevel@tonic-gate drv_set_opt(dev_info_t *dip)
2327c478bd9Sstevel@tonic-gate {
2337c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2347c478bd9Sstevel@tonic-gate 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
2357c478bd9Sstevel@tonic-gate 		dld_opt |= DLD_OPT_NO_FASTPATH;
2367c478bd9Sstevel@tonic-gate 	}
2377c478bd9Sstevel@tonic-gate 
2387c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2397c478bd9Sstevel@tonic-gate 	    DLD_PROP_NO_POLL, 0) != 0) {
2407c478bd9Sstevel@tonic-gate 		dld_opt |= DLD_OPT_NO_POLL;
2417c478bd9Sstevel@tonic-gate 	}
2427c478bd9Sstevel@tonic-gate 
2437c478bd9Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2447c478bd9Sstevel@tonic-gate 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
2457c478bd9Sstevel@tonic-gate 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
2467c478bd9Sstevel@tonic-gate 	}
247*9c175129Sja 
248*9c175129Sja 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
249*9c175129Sja 	    DLD_PROP_NO_SOFTRING, 0) != 0) {
250*9c175129Sja 		dld_opt |= DLD_OPT_NO_SOFTRING;
251*9c175129Sja 	}
2527c478bd9Sstevel@tonic-gate }
2537c478bd9Sstevel@tonic-gate 
2547c478bd9Sstevel@tonic-gate /*
2557c478bd9Sstevel@tonic-gate  * devo_attach: attach(9e)
2567c478bd9Sstevel@tonic-gate  */
2577c478bd9Sstevel@tonic-gate static int
2587c478bd9Sstevel@tonic-gate drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2597c478bd9Sstevel@tonic-gate {
2607c478bd9Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
2617c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2627c478bd9Sstevel@tonic-gate 
2637c478bd9Sstevel@tonic-gate 	ASSERT(ddi_get_instance(dip) == 0);
2647c478bd9Sstevel@tonic-gate 
2657c478bd9Sstevel@tonic-gate 	drv_set_opt(dip);
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate 	/*
2687c478bd9Sstevel@tonic-gate 	 * Create control node. DLPI provider nodes will be created on demand.
2697c478bd9Sstevel@tonic-gate 	 */
2707c478bd9Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
2717c478bd9Sstevel@tonic-gate 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
2727c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2737c478bd9Sstevel@tonic-gate 
2747c478bd9Sstevel@tonic-gate 	dld_dip = dip;
2757c478bd9Sstevel@tonic-gate 
2767c478bd9Sstevel@tonic-gate 	/*
2777c478bd9Sstevel@tonic-gate 	 * Log the fact that the driver is now attached.
2787c478bd9Sstevel@tonic-gate 	 */
2797c478bd9Sstevel@tonic-gate 	ddi_report_dev(dip);
2807c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
2817c478bd9Sstevel@tonic-gate }
2827c478bd9Sstevel@tonic-gate 
2837c478bd9Sstevel@tonic-gate /*
2847c478bd9Sstevel@tonic-gate  * devo_detach: detach(9e)
2857c478bd9Sstevel@tonic-gate  */
2867c478bd9Sstevel@tonic-gate static int
2877c478bd9Sstevel@tonic-gate drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
2907c478bd9Sstevel@tonic-gate 		return (DDI_FAILURE);
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate 	ASSERT(dld_dip == dip);
2937c478bd9Sstevel@tonic-gate 
2947c478bd9Sstevel@tonic-gate 	/*
2957c478bd9Sstevel@tonic-gate 	 * Remove the control node.
2967c478bd9Sstevel@tonic-gate 	 */
2977c478bd9Sstevel@tonic-gate 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
2987c478bd9Sstevel@tonic-gate 	dld_dip = NULL;
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	return (DDI_SUCCESS);
3017c478bd9Sstevel@tonic-gate }
3027c478bd9Sstevel@tonic-gate 
3037c478bd9Sstevel@tonic-gate /*
304210db224Sericheng  * dld control node open procedure.
3057c478bd9Sstevel@tonic-gate  */
3067c478bd9Sstevel@tonic-gate /*ARGSUSED*/
3077c478bd9Sstevel@tonic-gate static int
3087c478bd9Sstevel@tonic-gate drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
3097c478bd9Sstevel@tonic-gate {
310210db224Sericheng 	dld_ctl_str_t	*ctls;
3117c478bd9Sstevel@tonic-gate 	minor_t		minor;
312210db224Sericheng 	queue_t *oq =	OTHERQ(rq);
3137c478bd9Sstevel@tonic-gate 
314210db224Sericheng 	if (sflag == MODOPEN)
315210db224Sericheng 		return (ENOTSUP);
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate 	/*
3187c478bd9Sstevel@tonic-gate 	 * This is a cloning driver and therefore each queue should only
3197c478bd9Sstevel@tonic-gate 	 * ever get opened once.
3207c478bd9Sstevel@tonic-gate 	 */
3217c478bd9Sstevel@tonic-gate 	if (rq->q_ptr != NULL)
3227c478bd9Sstevel@tonic-gate 		return (EBUSY);
3237c478bd9Sstevel@tonic-gate 
324210db224Sericheng 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
325210db224Sericheng 	if (minor == 0)
326210db224Sericheng 		return (ENOMEM);
3277c478bd9Sstevel@tonic-gate 
328210db224Sericheng 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
329210db224Sericheng 	if (ctls == NULL) {
330210db224Sericheng 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
331210db224Sericheng 		return (ENOMEM);
332210db224Sericheng 	}
3337c478bd9Sstevel@tonic-gate 
334210db224Sericheng 	ctls->cs_minor = minor;
335210db224Sericheng 	ctls->cs_wq = WR(rq);
3367c478bd9Sstevel@tonic-gate 
337210db224Sericheng 	rq->q_ptr = ctls;
338210db224Sericheng 	oq->q_ptr = ctls;
3397c478bd9Sstevel@tonic-gate 
3407c478bd9Sstevel@tonic-gate 	/*
3417c478bd9Sstevel@tonic-gate 	 * Enable the queue srv(9e) routine.
3427c478bd9Sstevel@tonic-gate 	 */
3437c478bd9Sstevel@tonic-gate 	qprocson(rq);
3447c478bd9Sstevel@tonic-gate 
3457c478bd9Sstevel@tonic-gate 	/*
3467c478bd9Sstevel@tonic-gate 	 * Construct a cloned dev_t to hand back.
3477c478bd9Sstevel@tonic-gate 	 */
348210db224Sericheng 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
3497c478bd9Sstevel@tonic-gate 	return (0);
3507c478bd9Sstevel@tonic-gate }
3517c478bd9Sstevel@tonic-gate 
3527c478bd9Sstevel@tonic-gate /*
353210db224Sericheng  * dld control node close procedure.
3547c478bd9Sstevel@tonic-gate  */
3557c478bd9Sstevel@tonic-gate static int
3567c478bd9Sstevel@tonic-gate drv_close(queue_t *rq)
3577c478bd9Sstevel@tonic-gate {
358210db224Sericheng 	dld_ctl_str_t	*ctls;
3597c478bd9Sstevel@tonic-gate 
360210db224Sericheng 	ctls = rq->q_ptr;
361210db224Sericheng 	ASSERT(ctls != NULL);
3627c478bd9Sstevel@tonic-gate 
3637c478bd9Sstevel@tonic-gate 	/*
3647c478bd9Sstevel@tonic-gate 	 * Disable the queue srv(9e) routine.
3657c478bd9Sstevel@tonic-gate 	 */
3667c478bd9Sstevel@tonic-gate 	qprocsoff(rq);
3677c478bd9Sstevel@tonic-gate 
368210db224Sericheng 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
369210db224Sericheng 
370210db224Sericheng 	kmem_free(ctls, sizeof (dld_ctl_str_t));
3717c478bd9Sstevel@tonic-gate 
3727c478bd9Sstevel@tonic-gate 	return (0);
3737c478bd9Sstevel@tonic-gate }
3747c478bd9Sstevel@tonic-gate 
3757c478bd9Sstevel@tonic-gate /*
376210db224Sericheng  * DLDIOCATTR
3777c478bd9Sstevel@tonic-gate  */
3787c478bd9Sstevel@tonic-gate static void
379210db224Sericheng drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
3807c478bd9Sstevel@tonic-gate {
381ba2e4443Sseb 	dld_ioc_attr_t	*diap;
382210db224Sericheng 	dls_vlan_t	*dvp = NULL;
383210db224Sericheng 	dls_link_t	*dlp = NULL;
384210db224Sericheng 	int		err;
385210db224Sericheng 	queue_t		*q = ctls->cs_wq;
3867c478bd9Sstevel@tonic-gate 
387210db224Sericheng 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
388210db224Sericheng 		goto failed;
3897c478bd9Sstevel@tonic-gate 
390210db224Sericheng 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
391210db224Sericheng 	diap->dia_name[IFNAMSIZ - 1] = '\0';
392210db224Sericheng 
393210db224Sericheng 	if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) {
394210db224Sericheng 		err = ENOENT;
395210db224Sericheng 		goto failed;
396210db224Sericheng 	}
397210db224Sericheng 
398210db224Sericheng 	dlp = dvp->dv_dlp;
399ba2e4443Sseb 	(void) strlcpy(diap->dia_dev, dlp->dl_name, sizeof (diap->dia_dev));
400210db224Sericheng 	diap->dia_vid = dvp->dv_id;
401210db224Sericheng 	diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max;
402210db224Sericheng 
403210db224Sericheng 	dls_vlan_rele(dvp);
404210db224Sericheng 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
405210db224Sericheng 	return;
406210db224Sericheng 
407210db224Sericheng failed:
408210db224Sericheng 	ASSERT(err != 0);
409210db224Sericheng 	if (err == ENOENT) {
410210db224Sericheng 		char	devname[MAXNAMELEN];
411210db224Sericheng 		uint_t	instance;
412210db224Sericheng 		major_t	major;
413210db224Sericheng 
414210db224Sericheng 		/*
415210db224Sericheng 		 * Try to detect if the specified device is gldv3
416210db224Sericheng 		 * and return ENODEV if it is not.
417210db224Sericheng 		 */
418210db224Sericheng 		if (ddi_parse(diap->dia_name, devname, &instance) == 0 &&
419210db224Sericheng 		    (major = ddi_name_to_major(devname)) != (major_t)-1 &&
420210db224Sericheng 		    !GLDV3_DRV(major))
421210db224Sericheng 			err = ENODEV;
422210db224Sericheng 	}
423210db224Sericheng 	miocnak(q, mp, 0, err);
4247c478bd9Sstevel@tonic-gate }
4257c478bd9Sstevel@tonic-gate 
426210db224Sericheng 
4277c478bd9Sstevel@tonic-gate /*
428210db224Sericheng  * DLDIOCVLAN
4297c478bd9Sstevel@tonic-gate  */
430210db224Sericheng typedef struct dld_ioc_vlan_state {
431210db224Sericheng 	uint_t		bytes_left;
432aae21359Skrgopi 	dld_ioc_vlan_t	*divp;
433210db224Sericheng 	dld_vlan_info_t	*vlanp;
434210db224Sericheng } dld_ioc_vlan_state_t;
435210db224Sericheng 
436210db224Sericheng static int
437210db224Sericheng drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg)
438210db224Sericheng {
439210db224Sericheng 	dld_ioc_vlan_state_t	*statep = arg;
440210db224Sericheng 
441aae21359Skrgopi 	/*
442aae21359Skrgopi 	 * passed buffer space is limited to 65536 bytes. So
443aae21359Skrgopi 	 * copy only the vlans associated with the passed link.
444aae21359Skrgopi 	 */
445ba2e4443Sseb 	if (strcmp(dvp->dv_dlp->dl_name, statep->divp->div_name) == 0 &&
446aae21359Skrgopi 	    dvp->dv_id != 0) {
447aae21359Skrgopi 		if (statep->bytes_left < sizeof (dld_vlan_info_t))
448aae21359Skrgopi 			return (ENOSPC);
449aae21359Skrgopi 
450aae21359Skrgopi 		(void) strlcpy(statep->vlanp->dvi_name,
451aae21359Skrgopi 		    dvp->dv_name, IFNAMSIZ);
452aae21359Skrgopi 		statep->divp->div_count++;
453aae21359Skrgopi 		statep->bytes_left -= sizeof (dld_vlan_info_t);
454aae21359Skrgopi 		statep->vlanp += 1;
455aae21359Skrgopi 	}
456210db224Sericheng 	return (0);
457210db224Sericheng }
458210db224Sericheng 
4597c478bd9Sstevel@tonic-gate static void
460210db224Sericheng drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
4617c478bd9Sstevel@tonic-gate {
462210db224Sericheng 	dld_ioc_vlan_t		*divp;
463210db224Sericheng 	dld_ioc_vlan_state_t	state;
464210db224Sericheng 	int			err = EINVAL;
465210db224Sericheng 	queue_t			*q = ctls->cs_wq;
466aae21359Skrgopi 	mblk_t			*bp;
4677c478bd9Sstevel@tonic-gate 
468210db224Sericheng 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0)
469210db224Sericheng 		goto failed;
4707c478bd9Sstevel@tonic-gate 
471aae21359Skrgopi 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
472aae21359Skrgopi 		goto failed;
473aae21359Skrgopi 
474aae21359Skrgopi 	freemsg(mp->b_cont);
475aae21359Skrgopi 	mp->b_cont = bp;
476aae21359Skrgopi 	divp = (dld_ioc_vlan_t *)bp->b_rptr;
477aae21359Skrgopi 	divp->div_count = 0;
478aae21359Skrgopi 	state.bytes_left = MBLKL(bp) - sizeof (dld_ioc_vlan_t);
479aae21359Skrgopi 	state.divp = divp;
480210db224Sericheng 	state.vlanp = (dld_vlan_info_t *)(divp + 1);
4817c478bd9Sstevel@tonic-gate 
482210db224Sericheng 	err = dls_vlan_walk(drv_ioc_vlan_info, &state);
483210db224Sericheng 	if (err != 0)
484210db224Sericheng 		goto failed;
485210db224Sericheng 
486210db224Sericheng 	miocack(q, mp, sizeof (dld_ioc_vlan_t) +
487aae21359Skrgopi 	    state.divp->div_count * sizeof (dld_vlan_info_t), 0);
488210db224Sericheng 	return;
489210db224Sericheng 
490210db224Sericheng failed:
491210db224Sericheng 	ASSERT(err != 0);
492210db224Sericheng 	miocnak(q, mp, 0, err);
493210db224Sericheng }
494210db224Sericheng 
495f4b3ec61Sdh /*
496f4b3ec61Sdh  * DLDIOCHOLDVLAN
497f4b3ec61Sdh  */
498f4b3ec61Sdh static void
499f4b3ec61Sdh drv_hold_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
500f4b3ec61Sdh {
501f4b3ec61Sdh 	queue_t		*q = ctls->cs_wq;
502f4b3ec61Sdh 	dld_hold_vlan_t	*dhv;
503f4b3ec61Sdh 	mblk_t		*nmp;
504789e0dbbSdh 	int		err = EINVAL;
505f4b3ec61Sdh 	dls_vlan_t	*dvp;
506789e0dbbSdh 	char		mac[MAXNAMELEN];
507789e0dbbSdh 	dev_info_t	*dip = NULL;
508789e0dbbSdh 	major_t		major;
509789e0dbbSdh 	uint_t		index;
510f4b3ec61Sdh 
511f4b3ec61Sdh 	nmp = mp->b_cont;
512789e0dbbSdh 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t))
513789e0dbbSdh 		goto failed;
514789e0dbbSdh 
515f4b3ec61Sdh 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
516f4b3ec61Sdh 
517789e0dbbSdh 	/*
518789e0dbbSdh 	 * When a device instance without opens is detached, its
519789e0dbbSdh 	 * dls_vlan_t will be destroyed. A subsequent DLDIOCHOLDVLAN
520789e0dbbSdh 	 * invoked on this device instance will fail because
521789e0dbbSdh 	 * dls_vlan_hold() does not create non-tagged vlans on demand.
522789e0dbbSdh 	 * To handle this problem, we must force the creation of the
523789e0dbbSdh 	 * dls_vlan_t (if it doesn't already exist) by calling
524789e0dbbSdh 	 * ddi_hold_devi_by_instance() before calling dls_vlan_hold().
525789e0dbbSdh 	 */
526789e0dbbSdh 	if (ddi_parse(dhv->dhv_name, mac, &index) != DDI_SUCCESS)
527789e0dbbSdh 		goto failed;
528789e0dbbSdh 
529789e0dbbSdh 	if (DLS_PPA2VID(index) == VLAN_ID_NONE && strcmp(mac, "aggr") != 0) {
530789e0dbbSdh 		if ((major = ddi_name_to_major(mac)) == (major_t)-1 ||
531789e0dbbSdh 		    (dip = ddi_hold_devi_by_instance(major,
532789e0dbbSdh 		    DLS_PPA2INST(index), 0)) == NULL)
533789e0dbbSdh 			goto failed;
534f4b3ec61Sdh 	}
535f4b3ec61Sdh 
536789e0dbbSdh 	err = dls_vlan_hold(dhv->dhv_name, &dvp, B_TRUE);
537789e0dbbSdh 	if (dip != NULL)
538789e0dbbSdh 		ddi_release_devi(dip);
539789e0dbbSdh 
540789e0dbbSdh 	if (err != 0)
541789e0dbbSdh 		goto failed;
542789e0dbbSdh 
543f4b3ec61Sdh 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
544f595a68aSyz 	    dhv->dhv_docheck)) != 0) {
545f595a68aSyz 		dls_vlan_rele(dvp);
546789e0dbbSdh 		goto failed;
547f595a68aSyz 	} else {
548f4b3ec61Sdh 		miocack(q, mp, 0, 0);
549789e0dbbSdh 		return;
550f595a68aSyz 	}
551789e0dbbSdh failed:
552789e0dbbSdh 	miocnak(q, mp, 0, err);
553f4b3ec61Sdh }
554f4b3ec61Sdh 
555f4b3ec61Sdh /*
556f4b3ec61Sdh  * DLDIOCRELEVLAN
557f4b3ec61Sdh  */
558f4b3ec61Sdh static void
559f4b3ec61Sdh drv_rele_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
560f4b3ec61Sdh {
561f4b3ec61Sdh 	queue_t		*q = ctls->cs_wq;
562f4b3ec61Sdh 	dld_hold_vlan_t	*dhv;
563f4b3ec61Sdh 	mblk_t		*nmp;
564f4b3ec61Sdh 	int		err;
565f4b3ec61Sdh 
566f4b3ec61Sdh 	nmp = mp->b_cont;
567f4b3ec61Sdh 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
568f4b3ec61Sdh 		err = EINVAL;
569f4b3ec61Sdh 		miocnak(q, mp, 0, err);
570f4b3ec61Sdh 		return;
571f4b3ec61Sdh 	}
572f4b3ec61Sdh 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
573f4b3ec61Sdh 
574f4b3ec61Sdh 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
575f4b3ec61Sdh 	    dhv->dhv_docheck)) != 0) {
576f4b3ec61Sdh 		miocnak(q, mp, 0, err);
577f4b3ec61Sdh 		return;
578f4b3ec61Sdh 	}
579f4b3ec61Sdh 
580f4b3ec61Sdh 	if ((err = dls_vlan_rele_by_name(dhv->dhv_name)) != 0) {
581f4b3ec61Sdh 		miocnak(q, mp, 0, err);
582f4b3ec61Sdh 		return;
583f4b3ec61Sdh 	}
584f4b3ec61Sdh 
585f4b3ec61Sdh 	miocack(q, mp, 0, 0);
586f4b3ec61Sdh }
587f4b3ec61Sdh 
588f4b3ec61Sdh /*
589f4b3ec61Sdh  * DLDIOCZIDGET
590f4b3ec61Sdh  */
591f4b3ec61Sdh static void
592f4b3ec61Sdh drv_ioc_zid_get(dld_ctl_str_t *ctls, mblk_t *mp)
593f4b3ec61Sdh {
594f4b3ec61Sdh 	queue_t		*q = ctls->cs_wq;
595f4b3ec61Sdh 	dld_hold_vlan_t	*dhv;
596f4b3ec61Sdh 	mblk_t		*nmp;
597f4b3ec61Sdh 	int		err;
598f4b3ec61Sdh 
599f4b3ec61Sdh 	nmp = mp->b_cont;
600f4b3ec61Sdh 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
601f4b3ec61Sdh 		err = EINVAL;
602f4b3ec61Sdh 		miocnak(q, mp, 0, err);
603f4b3ec61Sdh 		return;
604f4b3ec61Sdh 	}
605f4b3ec61Sdh 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
606f4b3ec61Sdh 
607f4b3ec61Sdh 	if ((err = dls_vlan_getzoneid(dhv->dhv_name, &dhv->dhv_zid)) != 0)
608f4b3ec61Sdh 		miocnak(q, mp, 0, err);
609f4b3ec61Sdh 	else
610f4b3ec61Sdh 		miocack(q, mp, sizeof (dld_hold_vlan_t), 0);
611f4b3ec61Sdh }
612210db224Sericheng 
613210db224Sericheng /*
614210db224Sericheng  * Process an IOCTL message received by the control node.
615210db224Sericheng  */
616210db224Sericheng static void
617210db224Sericheng drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
618210db224Sericheng {
619210db224Sericheng 	uint_t	cmd;
620210db224Sericheng 
621210db224Sericheng 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
622210db224Sericheng 	switch (cmd) {
623210db224Sericheng 	case DLDIOCATTR:
624210db224Sericheng 		drv_ioc_attr(ctls, mp);
625210db224Sericheng 		return;
626210db224Sericheng 	case DLDIOCVLAN:
627210db224Sericheng 		drv_ioc_vlan(ctls, mp);
6287c478bd9Sstevel@tonic-gate 		return;
6290ba2cbe9Sxc 	case DLDIOCSECOBJSET:
6300ba2cbe9Sxc 		drv_ioc_secobj_set(ctls, mp);
6310ba2cbe9Sxc 		return;
6320ba2cbe9Sxc 	case DLDIOCSECOBJGET:
6330ba2cbe9Sxc 		drv_ioc_secobj_get(ctls, mp);
6340ba2cbe9Sxc 		return;
6350ba2cbe9Sxc 	case DLDIOCSECOBJUNSET:
6360ba2cbe9Sxc 		drv_ioc_secobj_unset(ctls, mp);
6370ba2cbe9Sxc 		return;
638f4b3ec61Sdh 	case DLDIOCHOLDVLAN:
639f4b3ec61Sdh 		drv_hold_vlan(ctls, mp);
640f4b3ec61Sdh 		return;
641f4b3ec61Sdh 	case DLDIOCRELEVLAN:
642f4b3ec61Sdh 		drv_rele_vlan(ctls, mp);
643f4b3ec61Sdh 		return;
644f4b3ec61Sdh 	case DLDIOCZIDGET:
645f4b3ec61Sdh 		drv_ioc_zid_get(ctls, mp);
646f4b3ec61Sdh 		return;
647210db224Sericheng 	default:
648210db224Sericheng 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
649210db224Sericheng 		return;
650210db224Sericheng 	}
651210db224Sericheng }
6527c478bd9Sstevel@tonic-gate 
653210db224Sericheng /*
654210db224Sericheng  * Write side put routine of the dld control node.
655210db224Sericheng  */
656210db224Sericheng static void
657210db224Sericheng drv_uw_put(queue_t *q, mblk_t *mp)
658210db224Sericheng {
659210db224Sericheng 	dld_ctl_str_t *ctls = q->q_ptr;
660210db224Sericheng 
661210db224Sericheng 	switch (mp->b_datap->db_type) {
662210db224Sericheng 	case M_IOCTL:
663210db224Sericheng 		drv_ioc(ctls, mp);
664210db224Sericheng 		break;
665210db224Sericheng 	default:
666210db224Sericheng 		freemsg(mp);
667210db224Sericheng 		break;
668210db224Sericheng 	}
669210db224Sericheng }
670210db224Sericheng 
671210db224Sericheng /*
672210db224Sericheng  * Write-side service procedure.
673210db224Sericheng  */
674210db224Sericheng void
675210db224Sericheng drv_uw_srv(queue_t *q)
676210db224Sericheng {
677210db224Sericheng 	mblk_t *mp;
678210db224Sericheng 
679210db224Sericheng 	while (mp = getq(q))
680210db224Sericheng 		drv_uw_put(q, mp);
6817c478bd9Sstevel@tonic-gate }
6820ba2cbe9Sxc 
6830ba2cbe9Sxc /*
6840ba2cbe9Sxc  * Secure objects implementation
6850ba2cbe9Sxc  */
6860ba2cbe9Sxc 
6870ba2cbe9Sxc /* ARGSUSED */
6880ba2cbe9Sxc static int
6890ba2cbe9Sxc drv_secobj_ctor(void *buf, void *arg, int kmflag)
6900ba2cbe9Sxc {
6910ba2cbe9Sxc 	bzero(buf, sizeof (dld_secobj_t));
6920ba2cbe9Sxc 	return (0);
6930ba2cbe9Sxc }
6940ba2cbe9Sxc 
6950ba2cbe9Sxc static void
6960ba2cbe9Sxc drv_secobj_init(void)
6970ba2cbe9Sxc {
6980ba2cbe9Sxc 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
6990ba2cbe9Sxc 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
7000ba2cbe9Sxc 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
7010ba2cbe9Sxc 	    NULL, NULL, NULL, 0);
7020ba2cbe9Sxc 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
7030ba2cbe9Sxc 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
7040ba2cbe9Sxc 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
7050ba2cbe9Sxc }
7060ba2cbe9Sxc 
7070ba2cbe9Sxc static void
7080ba2cbe9Sxc drv_secobj_fini(void)
7090ba2cbe9Sxc {
7100ba2cbe9Sxc 	mod_hash_destroy_hash(drv_secobj_hash);
7110ba2cbe9Sxc 	kmem_cache_destroy(drv_secobj_cachep);
7120ba2cbe9Sxc 	rw_destroy(&drv_secobj_lock);
7130ba2cbe9Sxc }
7140ba2cbe9Sxc 
7150ba2cbe9Sxc static void
7160ba2cbe9Sxc drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
7170ba2cbe9Sxc {
7180ba2cbe9Sxc 	dld_ioc_secobj_set_t	*ssp;
7190ba2cbe9Sxc 	dld_secobj_t		*sobjp, *objp;
7200ba2cbe9Sxc 	int			err = EINVAL;
7210ba2cbe9Sxc 	queue_t			*q = ctls->cs_wq;
7220ba2cbe9Sxc 
7230ba2cbe9Sxc 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
7240ba2cbe9Sxc 		goto failed;
7250ba2cbe9Sxc 
7260ba2cbe9Sxc 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
7270ba2cbe9Sxc 	sobjp = &ssp->ss_obj;
7280ba2cbe9Sxc 
7290ba2cbe9Sxc 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP)
7300ba2cbe9Sxc 		goto failed;
7310ba2cbe9Sxc 
7320ba2cbe9Sxc 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
7330ba2cbe9Sxc 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
7340ba2cbe9Sxc 		goto failed;
7350ba2cbe9Sxc 
7360ba2cbe9Sxc 	rw_enter(&drv_secobj_lock, RW_WRITER);
7370ba2cbe9Sxc 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
7380ba2cbe9Sxc 	    (mod_hash_val_t *)&objp);
7390ba2cbe9Sxc 	if (err == 0) {
7400ba2cbe9Sxc 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
7410ba2cbe9Sxc 			err = EEXIST;
7420ba2cbe9Sxc 			rw_exit(&drv_secobj_lock);
7430ba2cbe9Sxc 			goto failed;
7440ba2cbe9Sxc 		}
7450ba2cbe9Sxc 	} else {
7460ba2cbe9Sxc 		ASSERT(err == MH_ERR_NOTFOUND);
7470ba2cbe9Sxc 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
7480ba2cbe9Sxc 			err = ENOENT;
7490ba2cbe9Sxc 			rw_exit(&drv_secobj_lock);
7500ba2cbe9Sxc 			goto failed;
7510ba2cbe9Sxc 		}
7520ba2cbe9Sxc 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
7530ba2cbe9Sxc 		(void) strlcpy(objp->so_name, sobjp->so_name,
7540ba2cbe9Sxc 		    DLD_SECOBJ_NAME_MAX);
7550ba2cbe9Sxc 
7560ba2cbe9Sxc 		err = mod_hash_insert(drv_secobj_hash,
7570ba2cbe9Sxc 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
7580ba2cbe9Sxc 		ASSERT(err == 0);
7590ba2cbe9Sxc 	}
7600ba2cbe9Sxc 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
7610ba2cbe9Sxc 	objp->so_len = sobjp->so_len;
7620ba2cbe9Sxc 	objp->so_class = sobjp->so_class;
7630ba2cbe9Sxc 	rw_exit(&drv_secobj_lock);
7640ba2cbe9Sxc 	miocack(q, mp, 0, 0);
7650ba2cbe9Sxc 	return;
7660ba2cbe9Sxc 
7670ba2cbe9Sxc failed:
7680ba2cbe9Sxc 	ASSERT(err != 0);
7690ba2cbe9Sxc 	miocnak(q, mp, 0, err);
7700ba2cbe9Sxc 
7710ba2cbe9Sxc }
7720ba2cbe9Sxc 
7730ba2cbe9Sxc typedef struct dld_secobj_state {
7740ba2cbe9Sxc 	uint_t		ss_free;
7750ba2cbe9Sxc 	uint_t		ss_count;
7760ba2cbe9Sxc 	int		ss_rc;
7770ba2cbe9Sxc 	dld_secobj_t	*ss_objp;
7780ba2cbe9Sxc } dld_secobj_state_t;
7790ba2cbe9Sxc 
7800ba2cbe9Sxc /* ARGSUSED */
7810ba2cbe9Sxc static uint_t
7820ba2cbe9Sxc drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
7830ba2cbe9Sxc {
7840ba2cbe9Sxc 	dld_secobj_state_t	*statep = arg;
7850ba2cbe9Sxc 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
7860ba2cbe9Sxc 
7870ba2cbe9Sxc 	if (statep->ss_free < sizeof (dld_secobj_t)) {
7880ba2cbe9Sxc 		statep->ss_rc = ENOSPC;
7890ba2cbe9Sxc 		return (MH_WALK_TERMINATE);
7900ba2cbe9Sxc 	}
7910ba2cbe9Sxc 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
7920ba2cbe9Sxc 	statep->ss_objp++;
7930ba2cbe9Sxc 	statep->ss_free -= sizeof (dld_secobj_t);
7940ba2cbe9Sxc 	statep->ss_count++;
7950ba2cbe9Sxc 	return (MH_WALK_CONTINUE);
7960ba2cbe9Sxc }
7970ba2cbe9Sxc 
7980ba2cbe9Sxc static void
7990ba2cbe9Sxc drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
8000ba2cbe9Sxc {
8010ba2cbe9Sxc 	dld_ioc_secobj_get_t	*sgp;
8020ba2cbe9Sxc 	dld_secobj_t		*sobjp, *objp;
8030ba2cbe9Sxc 	int			err = EINVAL;
8040ba2cbe9Sxc 	uint_t			extra = 0;
8050ba2cbe9Sxc 	queue_t			*q = ctls->cs_wq;
8060ba2cbe9Sxc 	mblk_t			*bp;
8070ba2cbe9Sxc 
8080ba2cbe9Sxc 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
8090ba2cbe9Sxc 		goto failed;
8100ba2cbe9Sxc 
8110ba2cbe9Sxc 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
8120ba2cbe9Sxc 		goto failed;
8130ba2cbe9Sxc 
8140ba2cbe9Sxc 	freemsg(mp->b_cont);
8150ba2cbe9Sxc 	mp->b_cont = bp;
8160ba2cbe9Sxc 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
8170ba2cbe9Sxc 	sobjp = &sgp->sg_obj;
8180ba2cbe9Sxc 
8190ba2cbe9Sxc 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
8200ba2cbe9Sxc 		goto failed;
8210ba2cbe9Sxc 
8220ba2cbe9Sxc 	rw_enter(&drv_secobj_lock, RW_READER);
8230ba2cbe9Sxc 	if (sobjp->so_name[0] != '\0') {
8240ba2cbe9Sxc 		err = mod_hash_find(drv_secobj_hash,
8250ba2cbe9Sxc 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
8260ba2cbe9Sxc 		if (err != 0) {
8270ba2cbe9Sxc 			ASSERT(err == MH_ERR_NOTFOUND);
8280ba2cbe9Sxc 			err = ENOENT;
8290ba2cbe9Sxc 			rw_exit(&drv_secobj_lock);
8300ba2cbe9Sxc 			goto failed;
8310ba2cbe9Sxc 		}
8320ba2cbe9Sxc 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
8330ba2cbe9Sxc 		sobjp->so_len = objp->so_len;
8340ba2cbe9Sxc 		sobjp->so_class = objp->so_class;
8350ba2cbe9Sxc 		sgp->sg_count = 1;
8360ba2cbe9Sxc 	} else {
8370ba2cbe9Sxc 		dld_secobj_state_t	state;
8380ba2cbe9Sxc 
8390ba2cbe9Sxc 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
8400ba2cbe9Sxc 		state.ss_count = 0;
8410ba2cbe9Sxc 		state.ss_rc = 0;
8420ba2cbe9Sxc 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
8430ba2cbe9Sxc 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
8440ba2cbe9Sxc 		if (state.ss_rc != 0) {
8450ba2cbe9Sxc 			err = state.ss_rc;
8460ba2cbe9Sxc 			rw_exit(&drv_secobj_lock);
8470ba2cbe9Sxc 			goto failed;
8480ba2cbe9Sxc 		}
8490ba2cbe9Sxc 		sgp->sg_count = state.ss_count;
8500ba2cbe9Sxc 		extra = state.ss_count * sizeof (dld_secobj_t);
8510ba2cbe9Sxc 	}
8520ba2cbe9Sxc 	rw_exit(&drv_secobj_lock);
8530ba2cbe9Sxc 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
8540ba2cbe9Sxc 	return;
8550ba2cbe9Sxc 
8560ba2cbe9Sxc failed:
8570ba2cbe9Sxc 	ASSERT(err != 0);
8580ba2cbe9Sxc 	miocnak(q, mp, 0, err);
8590ba2cbe9Sxc 
8600ba2cbe9Sxc }
8610ba2cbe9Sxc 
8620ba2cbe9Sxc static void
8630ba2cbe9Sxc drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
8640ba2cbe9Sxc {
8650ba2cbe9Sxc 	dld_ioc_secobj_unset_t	*sup;
8660ba2cbe9Sxc 	dld_secobj_t		*objp;
8670ba2cbe9Sxc 	mod_hash_val_t		val;
8680ba2cbe9Sxc 	int			err = EINVAL;
8690ba2cbe9Sxc 	queue_t			*q = ctls->cs_wq;
8700ba2cbe9Sxc 
8710ba2cbe9Sxc 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
8720ba2cbe9Sxc 		goto failed;
8730ba2cbe9Sxc 
8740ba2cbe9Sxc 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
8750ba2cbe9Sxc 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
8760ba2cbe9Sxc 		goto failed;
8770ba2cbe9Sxc 
8780ba2cbe9Sxc 	rw_enter(&drv_secobj_lock, RW_WRITER);
8790ba2cbe9Sxc 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
8800ba2cbe9Sxc 	    (mod_hash_val_t *)&objp);
8810ba2cbe9Sxc 	if (err != 0) {
8820ba2cbe9Sxc 		ASSERT(err == MH_ERR_NOTFOUND);
8830ba2cbe9Sxc 		err = ENOENT;
8840ba2cbe9Sxc 		rw_exit(&drv_secobj_lock);
8850ba2cbe9Sxc 		goto failed;
8860ba2cbe9Sxc 	}
8870ba2cbe9Sxc 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
8880ba2cbe9Sxc 	    (mod_hash_val_t *)&val);
8890ba2cbe9Sxc 	ASSERT(err == 0);
8900ba2cbe9Sxc 	ASSERT(objp == (dld_secobj_t *)val);
8910ba2cbe9Sxc 
8920ba2cbe9Sxc 	kmem_cache_free(drv_secobj_cachep, objp);
8930ba2cbe9Sxc 	rw_exit(&drv_secobj_lock);
8940ba2cbe9Sxc 	miocack(q, mp, 0, 0);
8950ba2cbe9Sxc 	return;
8960ba2cbe9Sxc 
8970ba2cbe9Sxc failed:
8980ba2cbe9Sxc 	ASSERT(err != 0);
8990ba2cbe9Sxc 	miocnak(q, mp, 0, err);
9000ba2cbe9Sxc }
901