xref: /onnv-gate/usr/src/uts/common/io/dld/dld_drv.c (revision 3147:2789cc0027be)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52311Sseb  * Common Development and Distribution License (the "License").
62311Sseb  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
222311Sseb  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Data-Link Driver
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include	<sys/conf.h>
33269Sericheng #include	<sys/mkdev.h>
34269Sericheng #include	<sys/modctl.h>
350Sstevel@tonic-gate #include	<sys/stat.h>
36269Sericheng #include	<sys/strsun.h>
37269Sericheng #include	<sys/dld.h>
38269Sericheng #include	<sys/dld_impl.h>
39269Sericheng #include	<sys/dls_impl.h>
400Sstevel@tonic-gate #include	<inet/common.h>
410Sstevel@tonic-gate 
42269Sericheng /*
43269Sericheng  * dld control node state, one per open control node session.
44269Sericheng  */
45269Sericheng typedef struct dld_ctl_str_s {
46269Sericheng 	minor_t cs_minor;
47269Sericheng 	queue_t *cs_wq;
48269Sericheng } dld_ctl_str_t;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate static void	drv_init(void);
510Sstevel@tonic-gate static int	drv_fini(void);
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
540Sstevel@tonic-gate static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
550Sstevel@tonic-gate static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
560Sstevel@tonic-gate 
57269Sericheng /*
58*3147Sxc151355  * Secure objects declarations
59*3147Sxc151355  */
60*3147Sxc151355 #define	SECOBJ_WEP_HASHSZ	67
61*3147Sxc151355 static krwlock_t	drv_secobj_lock;
62*3147Sxc151355 static kmem_cache_t	*drv_secobj_cachep;
63*3147Sxc151355 static mod_hash_t	*drv_secobj_hash;
64*3147Sxc151355 static void		drv_secobj_init(void);
65*3147Sxc151355 static void		drv_secobj_fini(void);
66*3147Sxc151355 static void		drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *);
67*3147Sxc151355 static void		drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *);
68*3147Sxc151355 static void		drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *);
69*3147Sxc151355 
70*3147Sxc151355 /*
71269Sericheng  * The following entry points are private to dld and are used for control
72269Sericheng  * operations only. The entry points exported to mac drivers are defined
73269Sericheng  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
74269Sericheng  */
750Sstevel@tonic-gate static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
760Sstevel@tonic-gate static int	drv_close(queue_t *);
770Sstevel@tonic-gate 
780Sstevel@tonic-gate static void	drv_uw_put(queue_t *, mblk_t *);
790Sstevel@tonic-gate static void	drv_uw_srv(queue_t *);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate dev_info_t	*dld_dip;		/* dev_info_t for the driver */
82269Sericheng uint32_t	dld_opt = 0;		/* Global options */
83269Sericheng static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
840Sstevel@tonic-gate 
850Sstevel@tonic-gate static	struct	module_info	drv_info = {
860Sstevel@tonic-gate 	0,			/* mi_idnum */
870Sstevel@tonic-gate 	DLD_DRIVER_NAME,	/* mi_idname */
880Sstevel@tonic-gate 	0,			/* mi_minpsz */
890Sstevel@tonic-gate 	(64 * 1024),		/* mi_maxpsz */
900Sstevel@tonic-gate 	1,			/* mi_hiwat */
910Sstevel@tonic-gate 	0			/* mi_lowat */
920Sstevel@tonic-gate };
930Sstevel@tonic-gate 
940Sstevel@tonic-gate static	struct qinit		drv_ur_init = {
950Sstevel@tonic-gate 	NULL,			/* qi_putp */
960Sstevel@tonic-gate 	NULL,			/* qi_srvp */
970Sstevel@tonic-gate 	drv_open,		/* qi_qopen */
980Sstevel@tonic-gate 	drv_close,		/* qi_qclose */
990Sstevel@tonic-gate 	NULL,			/* qi_qadmin */
1000Sstevel@tonic-gate 	&drv_info,		/* qi_minfo */
1010Sstevel@tonic-gate 	NULL			/* qi_mstat */
1020Sstevel@tonic-gate };
1030Sstevel@tonic-gate 
1040Sstevel@tonic-gate static	struct qinit		drv_uw_init = {
1050Sstevel@tonic-gate 	(pfi_t)drv_uw_put,	/* qi_putp */
1060Sstevel@tonic-gate 	(pfi_t)drv_uw_srv,	/* qi_srvp */
1070Sstevel@tonic-gate 	NULL,			/* qi_qopen */
1080Sstevel@tonic-gate 	NULL,			/* qi_qclose */
1090Sstevel@tonic-gate 	NULL,			/* qi_qadmin */
1100Sstevel@tonic-gate 	&drv_info,		/* qi_minfo */
1110Sstevel@tonic-gate 	NULL			/* qi_mstat */
1120Sstevel@tonic-gate };
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate static	struct streamtab	drv_stream = {
1150Sstevel@tonic-gate 	&drv_ur_init,		/* st_rdinit */
1160Sstevel@tonic-gate 	&drv_uw_init,		/* st_wrinit */
1170Sstevel@tonic-gate 	NULL,			/* st_muxrinit */
1180Sstevel@tonic-gate 	NULL			/* st_muxwinit */
1190Sstevel@tonic-gate };
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
122269Sericheng     nodev, drv_getinfo, D_MP, &drv_stream);
1230Sstevel@tonic-gate 
1240Sstevel@tonic-gate /*
1250Sstevel@tonic-gate  * Module linkage information for the kernel.
1260Sstevel@tonic-gate  */
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate extern	struct mod_ops		mod_driverops;
1290Sstevel@tonic-gate 
1300Sstevel@tonic-gate static	struct modldrv		drv_modldrv = {
1310Sstevel@tonic-gate 	&mod_driverops,
1320Sstevel@tonic-gate 	DLD_INFO,
1330Sstevel@tonic-gate 	&drv_ops
1340Sstevel@tonic-gate };
1350Sstevel@tonic-gate 
1360Sstevel@tonic-gate static	struct modlinkage	drv_modlinkage = {
1370Sstevel@tonic-gate 	MODREV_1,
1380Sstevel@tonic-gate 	&drv_modldrv,
1390Sstevel@tonic-gate 	NULL
1400Sstevel@tonic-gate };
1410Sstevel@tonic-gate 
1420Sstevel@tonic-gate int
1430Sstevel@tonic-gate _init(void)
1440Sstevel@tonic-gate {
1450Sstevel@tonic-gate 	int	err;
1460Sstevel@tonic-gate 
147269Sericheng 	drv_init();
148269Sericheng 
1490Sstevel@tonic-gate 	if ((err = mod_install(&drv_modlinkage)) != 0)
1500Sstevel@tonic-gate 		return (err);
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	return (0);
1530Sstevel@tonic-gate }
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate int
1560Sstevel@tonic-gate _fini(void)
1570Sstevel@tonic-gate {
1580Sstevel@tonic-gate 	int	err;
1590Sstevel@tonic-gate 
1600Sstevel@tonic-gate 	if ((err = mod_remove(&drv_modlinkage)) != 0)
1610Sstevel@tonic-gate 		return (err);
1620Sstevel@tonic-gate 
163269Sericheng 	if (drv_fini() != 0) {
164269Sericheng 		(void) mod_install(&drv_modlinkage);
165269Sericheng 		return (DDI_FAILURE);
166269Sericheng 	}
1670Sstevel@tonic-gate 
1680Sstevel@tonic-gate 	return (err);
1690Sstevel@tonic-gate }
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate int
1720Sstevel@tonic-gate _info(struct modinfo *modinfop)
1730Sstevel@tonic-gate {
1740Sstevel@tonic-gate 	return (mod_info(&drv_modlinkage, modinfop));
1750Sstevel@tonic-gate }
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate /*
178269Sericheng  * Initialize component modules.
1790Sstevel@tonic-gate  */
1800Sstevel@tonic-gate static void
1810Sstevel@tonic-gate drv_init(void)
1820Sstevel@tonic-gate {
183269Sericheng 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
184269Sericheng 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
185*3147Sxc151355 	drv_secobj_init();
1860Sstevel@tonic-gate 	dld_str_init();
1870Sstevel@tonic-gate }
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate static int
1900Sstevel@tonic-gate drv_fini(void)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate 	int	err;
1930Sstevel@tonic-gate 
194269Sericheng 	if ((err = dld_str_fini()) != 0)
1950Sstevel@tonic-gate 		return (err);
1960Sstevel@tonic-gate 
197*3147Sxc151355 	drv_secobj_fini();
198269Sericheng 	vmem_destroy(dld_ctl_vmem);
1990Sstevel@tonic-gate 	return (0);
2000Sstevel@tonic-gate }
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate /*
2030Sstevel@tonic-gate  * devo_getinfo: getinfo(9e)
2040Sstevel@tonic-gate  */
2050Sstevel@tonic-gate /*ARGSUSED*/
2060Sstevel@tonic-gate static int
2070Sstevel@tonic-gate drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
2080Sstevel@tonic-gate {
2090Sstevel@tonic-gate 	if (dld_dip == NULL)
2100Sstevel@tonic-gate 		return (DDI_FAILURE);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	switch (cmd) {
2130Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
2140Sstevel@tonic-gate 		*resp = (void *)0;
2150Sstevel@tonic-gate 		break;
2160Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
2170Sstevel@tonic-gate 		*resp = (void *)dld_dip;
2180Sstevel@tonic-gate 		break;
2190Sstevel@tonic-gate 	default:
2200Sstevel@tonic-gate 		return (DDI_FAILURE);
2210Sstevel@tonic-gate 	}
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	return (DDI_SUCCESS);
2240Sstevel@tonic-gate }
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate /*
2270Sstevel@tonic-gate  * Check properties to set options. (See dld.h for property definitions).
2280Sstevel@tonic-gate  */
2290Sstevel@tonic-gate static void
2300Sstevel@tonic-gate drv_set_opt(dev_info_t *dip)
2310Sstevel@tonic-gate {
2320Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2330Sstevel@tonic-gate 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
2340Sstevel@tonic-gate 		dld_opt |= DLD_OPT_NO_FASTPATH;
2350Sstevel@tonic-gate 	}
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2380Sstevel@tonic-gate 	    DLD_PROP_NO_POLL, 0) != 0) {
2390Sstevel@tonic-gate 		dld_opt |= DLD_OPT_NO_POLL;
2400Sstevel@tonic-gate 	}
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
2430Sstevel@tonic-gate 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
2440Sstevel@tonic-gate 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
2450Sstevel@tonic-gate 	}
2460Sstevel@tonic-gate }
2470Sstevel@tonic-gate 
2480Sstevel@tonic-gate /*
2490Sstevel@tonic-gate  * devo_attach: attach(9e)
2500Sstevel@tonic-gate  */
2510Sstevel@tonic-gate static int
2520Sstevel@tonic-gate drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2530Sstevel@tonic-gate {
2540Sstevel@tonic-gate 	if (cmd != DDI_ATTACH)
2550Sstevel@tonic-gate 		return (DDI_FAILURE);
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	ASSERT(ddi_get_instance(dip) == 0);
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate 	drv_set_opt(dip);
2600Sstevel@tonic-gate 
2610Sstevel@tonic-gate 	/*
2620Sstevel@tonic-gate 	 * Create control node. DLPI provider nodes will be created on demand.
2630Sstevel@tonic-gate 	 */
2640Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
2650Sstevel@tonic-gate 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
2660Sstevel@tonic-gate 		return (DDI_FAILURE);
2670Sstevel@tonic-gate 
2680Sstevel@tonic-gate 	dld_dip = dip;
2690Sstevel@tonic-gate 
2700Sstevel@tonic-gate 	/*
2710Sstevel@tonic-gate 	 * Log the fact that the driver is now attached.
2720Sstevel@tonic-gate 	 */
2730Sstevel@tonic-gate 	ddi_report_dev(dip);
2740Sstevel@tonic-gate 	return (DDI_SUCCESS);
2750Sstevel@tonic-gate }
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate /*
2780Sstevel@tonic-gate  * devo_detach: detach(9e)
2790Sstevel@tonic-gate  */
2800Sstevel@tonic-gate static int
2810Sstevel@tonic-gate drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
2820Sstevel@tonic-gate {
2830Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
2840Sstevel@tonic-gate 		return (DDI_FAILURE);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	ASSERT(dld_dip == dip);
2870Sstevel@tonic-gate 
2880Sstevel@tonic-gate 	/*
2890Sstevel@tonic-gate 	 * Remove the control node.
2900Sstevel@tonic-gate 	 */
2910Sstevel@tonic-gate 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
2920Sstevel@tonic-gate 	dld_dip = NULL;
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	return (DDI_SUCCESS);
2950Sstevel@tonic-gate }
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate /*
298269Sericheng  * dld control node open procedure.
2990Sstevel@tonic-gate  */
3000Sstevel@tonic-gate /*ARGSUSED*/
3010Sstevel@tonic-gate static int
3020Sstevel@tonic-gate drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
3030Sstevel@tonic-gate {
304269Sericheng 	dld_ctl_str_t	*ctls;
3050Sstevel@tonic-gate 	minor_t		minor;
306269Sericheng 	queue_t *oq =	OTHERQ(rq);
3070Sstevel@tonic-gate 
308269Sericheng 	if (sflag == MODOPEN)
309269Sericheng 		return (ENOTSUP);
3100Sstevel@tonic-gate 
3110Sstevel@tonic-gate 	/*
3120Sstevel@tonic-gate 	 * This is a cloning driver and therefore each queue should only
3130Sstevel@tonic-gate 	 * ever get opened once.
3140Sstevel@tonic-gate 	 */
3150Sstevel@tonic-gate 	if (rq->q_ptr != NULL)
3160Sstevel@tonic-gate 		return (EBUSY);
3170Sstevel@tonic-gate 
318269Sericheng 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
319269Sericheng 	if (minor == 0)
320269Sericheng 		return (ENOMEM);
3210Sstevel@tonic-gate 
322269Sericheng 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
323269Sericheng 	if (ctls == NULL) {
324269Sericheng 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
325269Sericheng 		return (ENOMEM);
326269Sericheng 	}
3270Sstevel@tonic-gate 
328269Sericheng 	ctls->cs_minor = minor;
329269Sericheng 	ctls->cs_wq = WR(rq);
3300Sstevel@tonic-gate 
331269Sericheng 	rq->q_ptr = ctls;
332269Sericheng 	oq->q_ptr = ctls;
3330Sstevel@tonic-gate 
3340Sstevel@tonic-gate 	/*
3350Sstevel@tonic-gate 	 * Enable the queue srv(9e) routine.
3360Sstevel@tonic-gate 	 */
3370Sstevel@tonic-gate 	qprocson(rq);
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	/*
3400Sstevel@tonic-gate 	 * Construct a cloned dev_t to hand back.
3410Sstevel@tonic-gate 	 */
342269Sericheng 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
3430Sstevel@tonic-gate 	return (0);
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate /*
347269Sericheng  * dld control node close procedure.
3480Sstevel@tonic-gate  */
3490Sstevel@tonic-gate static int
3500Sstevel@tonic-gate drv_close(queue_t *rq)
3510Sstevel@tonic-gate {
352269Sericheng 	dld_ctl_str_t	*ctls;
3530Sstevel@tonic-gate 
354269Sericheng 	ctls = rq->q_ptr;
355269Sericheng 	ASSERT(ctls != NULL);
3560Sstevel@tonic-gate 
3570Sstevel@tonic-gate 	/*
3580Sstevel@tonic-gate 	 * Disable the queue srv(9e) routine.
3590Sstevel@tonic-gate 	 */
3600Sstevel@tonic-gate 	qprocsoff(rq);
3610Sstevel@tonic-gate 
362269Sericheng 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
3630Sstevel@tonic-gate 
364269Sericheng 	kmem_free(ctls, sizeof (dld_ctl_str_t));
365269Sericheng 
3660Sstevel@tonic-gate 	return (0);
3670Sstevel@tonic-gate }
3680Sstevel@tonic-gate 
3690Sstevel@tonic-gate /*
370269Sericheng  * DLDIOCATTR
3710Sstevel@tonic-gate  */
3720Sstevel@tonic-gate static void
373269Sericheng drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
3740Sstevel@tonic-gate {
3752311Sseb 	dld_ioc_attr_t	*diap;
376269Sericheng 	dls_vlan_t	*dvp = NULL;
377269Sericheng 	dls_link_t	*dlp = NULL;
378269Sericheng 	int		err;
379269Sericheng 	queue_t		*q = ctls->cs_wq;
380269Sericheng 
381269Sericheng 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
382269Sericheng 		goto failed;
383269Sericheng 
384269Sericheng 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
385269Sericheng 	diap->dia_name[IFNAMSIZ - 1] = '\0';
386269Sericheng 
387269Sericheng 	if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) {
388269Sericheng 		err = ENOENT;
389269Sericheng 		goto failed;
390269Sericheng 	}
391269Sericheng 
392269Sericheng 	dlp = dvp->dv_dlp;
3932311Sseb 	(void) strlcpy(diap->dia_dev, dlp->dl_name, sizeof (diap->dia_dev));
394269Sericheng 	diap->dia_vid = dvp->dv_id;
395269Sericheng 	diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max;
396269Sericheng 
397269Sericheng 	dls_vlan_rele(dvp);
398269Sericheng 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
399269Sericheng 	return;
400269Sericheng 
401269Sericheng failed:
402269Sericheng 	ASSERT(err != 0);
403269Sericheng 	if (err == ENOENT) {
404269Sericheng 		char	devname[MAXNAMELEN];
405269Sericheng 		uint_t	instance;
406269Sericheng 		major_t	major;
407269Sericheng 
408269Sericheng 		/*
409269Sericheng 		 * Try to detect if the specified device is gldv3
410269Sericheng 		 * and return ENODEV if it is not.
411269Sericheng 		 */
412269Sericheng 		if (ddi_parse(diap->dia_name, devname, &instance) == 0 &&
413269Sericheng 		    (major = ddi_name_to_major(devname)) != (major_t)-1 &&
414269Sericheng 		    !GLDV3_DRV(major))
415269Sericheng 			err = ENODEV;
416269Sericheng 	}
417269Sericheng 	miocnak(q, mp, 0, err);
418269Sericheng }
419269Sericheng 
420269Sericheng 
421269Sericheng /*
422269Sericheng  * DLDIOCVLAN
423269Sericheng  */
424269Sericheng typedef struct dld_ioc_vlan_state {
425269Sericheng 	uint_t		bytes_left;
426733Skrgopi 	dld_ioc_vlan_t	*divp;
427269Sericheng 	dld_vlan_info_t	*vlanp;
428269Sericheng } dld_ioc_vlan_state_t;
429269Sericheng 
430269Sericheng static int
431269Sericheng drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg)
432269Sericheng {
433269Sericheng 	dld_ioc_vlan_state_t	*statep = arg;
4340Sstevel@tonic-gate 
435733Skrgopi 	/*
436733Skrgopi 	 * passed buffer space is limited to 65536 bytes. So
437733Skrgopi 	 * copy only the vlans associated with the passed link.
438733Skrgopi 	 */
4392311Sseb 	if (strcmp(dvp->dv_dlp->dl_name, statep->divp->div_name) == 0 &&
440733Skrgopi 	    dvp->dv_id != 0) {
441733Skrgopi 		if (statep->bytes_left < sizeof (dld_vlan_info_t))
442733Skrgopi 			return (ENOSPC);
443269Sericheng 
444733Skrgopi 		(void) strlcpy(statep->vlanp->dvi_name,
445733Skrgopi 		    dvp->dv_name, IFNAMSIZ);
446733Skrgopi 		statep->divp->div_count++;
447733Skrgopi 		statep->bytes_left -= sizeof (dld_vlan_info_t);
448733Skrgopi 		statep->vlanp += 1;
449733Skrgopi 	}
450269Sericheng 	return (0);
451269Sericheng }
452269Sericheng 
453269Sericheng static void
454269Sericheng drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
455269Sericheng {
456269Sericheng 	dld_ioc_vlan_t		*divp;
457269Sericheng 	dld_ioc_vlan_state_t	state;
458269Sericheng 	int			err = EINVAL;
459269Sericheng 	queue_t			*q = ctls->cs_wq;
460733Skrgopi 	mblk_t			*bp;
461269Sericheng 
462269Sericheng 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0)
463269Sericheng 		goto failed;
464269Sericheng 
465733Skrgopi 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
466733Skrgopi 		goto failed;
467733Skrgopi 
468733Skrgopi 	freemsg(mp->b_cont);
469733Skrgopi 	mp->b_cont = bp;
470733Skrgopi 	divp = (dld_ioc_vlan_t *)bp->b_rptr;
471733Skrgopi 	divp->div_count = 0;
472733Skrgopi 	state.bytes_left = MBLKL(bp) - sizeof (dld_ioc_vlan_t);
473733Skrgopi 	state.divp = divp;
474269Sericheng 	state.vlanp = (dld_vlan_info_t *)(divp + 1);
475269Sericheng 
476269Sericheng 	err = dls_vlan_walk(drv_ioc_vlan_info, &state);
477269Sericheng 	if (err != 0)
478269Sericheng 		goto failed;
4790Sstevel@tonic-gate 
480269Sericheng 	miocack(q, mp, sizeof (dld_ioc_vlan_t) +
481733Skrgopi 	    state.divp->div_count * sizeof (dld_vlan_info_t), 0);
482269Sericheng 	return;
483269Sericheng 
484269Sericheng failed:
485269Sericheng 	ASSERT(err != 0);
486269Sericheng 	miocnak(q, mp, 0, err);
487269Sericheng }
488269Sericheng 
489269Sericheng 
490269Sericheng /*
491269Sericheng  * Process an IOCTL message received by the control node.
492269Sericheng  */
493269Sericheng static void
494269Sericheng drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
495269Sericheng {
496269Sericheng 	uint_t	cmd;
497269Sericheng 
498269Sericheng 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
499269Sericheng 	switch (cmd) {
500269Sericheng 	case DLDIOCATTR:
501269Sericheng 		drv_ioc_attr(ctls, mp);
502269Sericheng 		return;
503269Sericheng 	case DLDIOCVLAN:
504269Sericheng 		drv_ioc_vlan(ctls, mp);
505269Sericheng 		return;
506*3147Sxc151355 	case DLDIOCSECOBJSET:
507*3147Sxc151355 		drv_ioc_secobj_set(ctls, mp);
508*3147Sxc151355 		return;
509*3147Sxc151355 	case DLDIOCSECOBJGET:
510*3147Sxc151355 		drv_ioc_secobj_get(ctls, mp);
511*3147Sxc151355 		return;
512*3147Sxc151355 	case DLDIOCSECOBJUNSET:
513*3147Sxc151355 		drv_ioc_secobj_unset(ctls, mp);
514*3147Sxc151355 		return;
515269Sericheng 	default:
516269Sericheng 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
517269Sericheng 		return;
518269Sericheng 	}
5190Sstevel@tonic-gate }
5200Sstevel@tonic-gate 
5210Sstevel@tonic-gate /*
522269Sericheng  * Write side put routine of the dld control node.
5230Sstevel@tonic-gate  */
5240Sstevel@tonic-gate static void
525269Sericheng drv_uw_put(queue_t *q, mblk_t *mp)
5260Sstevel@tonic-gate {
527269Sericheng 	dld_ctl_str_t *ctls = q->q_ptr;
5280Sstevel@tonic-gate 
529269Sericheng 	switch (mp->b_datap->db_type) {
530269Sericheng 	case M_IOCTL:
531269Sericheng 		drv_ioc(ctls, mp);
532269Sericheng 		break;
533269Sericheng 	default:
534269Sericheng 		freemsg(mp);
535269Sericheng 		break;
536269Sericheng 	}
537269Sericheng }
5380Sstevel@tonic-gate 
539269Sericheng /*
540269Sericheng  * Write-side service procedure.
541269Sericheng  */
542269Sericheng void
543269Sericheng drv_uw_srv(queue_t *q)
544269Sericheng {
545269Sericheng 	mblk_t *mp;
5460Sstevel@tonic-gate 
547269Sericheng 	while (mp = getq(q))
548269Sericheng 		drv_uw_put(q, mp);
5490Sstevel@tonic-gate }
550*3147Sxc151355 
551*3147Sxc151355 /*
552*3147Sxc151355  * Secure objects implementation
553*3147Sxc151355  */
554*3147Sxc151355 
555*3147Sxc151355 /* ARGSUSED */
556*3147Sxc151355 static int
557*3147Sxc151355 drv_secobj_ctor(void *buf, void *arg, int kmflag)
558*3147Sxc151355 {
559*3147Sxc151355 	bzero(buf, sizeof (dld_secobj_t));
560*3147Sxc151355 	return (0);
561*3147Sxc151355 }
562*3147Sxc151355 
563*3147Sxc151355 static void
564*3147Sxc151355 drv_secobj_init(void)
565*3147Sxc151355 {
566*3147Sxc151355 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
567*3147Sxc151355 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
568*3147Sxc151355 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
569*3147Sxc151355 	    NULL, NULL, NULL, 0);
570*3147Sxc151355 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
571*3147Sxc151355 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
572*3147Sxc151355 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
573*3147Sxc151355 }
574*3147Sxc151355 
575*3147Sxc151355 static void
576*3147Sxc151355 drv_secobj_fini(void)
577*3147Sxc151355 {
578*3147Sxc151355 	mod_hash_destroy_hash(drv_secobj_hash);
579*3147Sxc151355 	kmem_cache_destroy(drv_secobj_cachep);
580*3147Sxc151355 	rw_destroy(&drv_secobj_lock);
581*3147Sxc151355 }
582*3147Sxc151355 
583*3147Sxc151355 static void
584*3147Sxc151355 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
585*3147Sxc151355 {
586*3147Sxc151355 	dld_ioc_secobj_set_t	*ssp;
587*3147Sxc151355 	dld_secobj_t		*sobjp, *objp;
588*3147Sxc151355 	int			err = EINVAL;
589*3147Sxc151355 	queue_t			*q = ctls->cs_wq;
590*3147Sxc151355 
591*3147Sxc151355 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
592*3147Sxc151355 		goto failed;
593*3147Sxc151355 
594*3147Sxc151355 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
595*3147Sxc151355 	sobjp = &ssp->ss_obj;
596*3147Sxc151355 
597*3147Sxc151355 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP)
598*3147Sxc151355 		goto failed;
599*3147Sxc151355 
600*3147Sxc151355 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
601*3147Sxc151355 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
602*3147Sxc151355 		goto failed;
603*3147Sxc151355 
604*3147Sxc151355 	rw_enter(&drv_secobj_lock, RW_WRITER);
605*3147Sxc151355 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
606*3147Sxc151355 	    (mod_hash_val_t *)&objp);
607*3147Sxc151355 	if (err == 0) {
608*3147Sxc151355 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
609*3147Sxc151355 			err = EEXIST;
610*3147Sxc151355 			rw_exit(&drv_secobj_lock);
611*3147Sxc151355 			goto failed;
612*3147Sxc151355 		}
613*3147Sxc151355 	} else {
614*3147Sxc151355 		ASSERT(err == MH_ERR_NOTFOUND);
615*3147Sxc151355 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
616*3147Sxc151355 			err = ENOENT;
617*3147Sxc151355 			rw_exit(&drv_secobj_lock);
618*3147Sxc151355 			goto failed;
619*3147Sxc151355 		}
620*3147Sxc151355 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
621*3147Sxc151355 		(void) strlcpy(objp->so_name, sobjp->so_name,
622*3147Sxc151355 		    DLD_SECOBJ_NAME_MAX);
623*3147Sxc151355 
624*3147Sxc151355 		err = mod_hash_insert(drv_secobj_hash,
625*3147Sxc151355 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
626*3147Sxc151355 		ASSERT(err == 0);
627*3147Sxc151355 	}
628*3147Sxc151355 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
629*3147Sxc151355 	objp->so_len = sobjp->so_len;
630*3147Sxc151355 	objp->so_class = sobjp->so_class;
631*3147Sxc151355 	rw_exit(&drv_secobj_lock);
632*3147Sxc151355 	miocack(q, mp, 0, 0);
633*3147Sxc151355 	return;
634*3147Sxc151355 
635*3147Sxc151355 failed:
636*3147Sxc151355 	ASSERT(err != 0);
637*3147Sxc151355 	miocnak(q, mp, 0, err);
638*3147Sxc151355 
639*3147Sxc151355 }
640*3147Sxc151355 
641*3147Sxc151355 typedef struct dld_secobj_state {
642*3147Sxc151355 	uint_t		ss_free;
643*3147Sxc151355 	uint_t		ss_count;
644*3147Sxc151355 	int		ss_rc;
645*3147Sxc151355 	dld_secobj_t	*ss_objp;
646*3147Sxc151355 } dld_secobj_state_t;
647*3147Sxc151355 
648*3147Sxc151355 /* ARGSUSED */
649*3147Sxc151355 static uint_t
650*3147Sxc151355 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
651*3147Sxc151355 {
652*3147Sxc151355 	dld_secobj_state_t	*statep = arg;
653*3147Sxc151355 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
654*3147Sxc151355 
655*3147Sxc151355 	if (statep->ss_free < sizeof (dld_secobj_t)) {
656*3147Sxc151355 		statep->ss_rc = ENOSPC;
657*3147Sxc151355 		return (MH_WALK_TERMINATE);
658*3147Sxc151355 	}
659*3147Sxc151355 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
660*3147Sxc151355 	statep->ss_objp++;
661*3147Sxc151355 	statep->ss_free -= sizeof (dld_secobj_t);
662*3147Sxc151355 	statep->ss_count++;
663*3147Sxc151355 	return (MH_WALK_CONTINUE);
664*3147Sxc151355 }
665*3147Sxc151355 
666*3147Sxc151355 static void
667*3147Sxc151355 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
668*3147Sxc151355 {
669*3147Sxc151355 	dld_ioc_secobj_get_t	*sgp;
670*3147Sxc151355 	dld_secobj_t		*sobjp, *objp;
671*3147Sxc151355 	int			err = EINVAL;
672*3147Sxc151355 	uint_t			extra = 0;
673*3147Sxc151355 	queue_t			*q = ctls->cs_wq;
674*3147Sxc151355 	mblk_t			*bp;
675*3147Sxc151355 
676*3147Sxc151355 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
677*3147Sxc151355 		goto failed;
678*3147Sxc151355 
679*3147Sxc151355 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
680*3147Sxc151355 		goto failed;
681*3147Sxc151355 
682*3147Sxc151355 	freemsg(mp->b_cont);
683*3147Sxc151355 	mp->b_cont = bp;
684*3147Sxc151355 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
685*3147Sxc151355 	sobjp = &sgp->sg_obj;
686*3147Sxc151355 
687*3147Sxc151355 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
688*3147Sxc151355 		goto failed;
689*3147Sxc151355 
690*3147Sxc151355 	rw_enter(&drv_secobj_lock, RW_READER);
691*3147Sxc151355 	if (sobjp->so_name[0] != '\0') {
692*3147Sxc151355 		err = mod_hash_find(drv_secobj_hash,
693*3147Sxc151355 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
694*3147Sxc151355 		if (err != 0) {
695*3147Sxc151355 			ASSERT(err == MH_ERR_NOTFOUND);
696*3147Sxc151355 			err = ENOENT;
697*3147Sxc151355 			rw_exit(&drv_secobj_lock);
698*3147Sxc151355 			goto failed;
699*3147Sxc151355 		}
700*3147Sxc151355 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
701*3147Sxc151355 		sobjp->so_len = objp->so_len;
702*3147Sxc151355 		sobjp->so_class = objp->so_class;
703*3147Sxc151355 		sgp->sg_count = 1;
704*3147Sxc151355 	} else {
705*3147Sxc151355 		dld_secobj_state_t	state;
706*3147Sxc151355 
707*3147Sxc151355 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
708*3147Sxc151355 		state.ss_count = 0;
709*3147Sxc151355 		state.ss_rc = 0;
710*3147Sxc151355 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
711*3147Sxc151355 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
712*3147Sxc151355 		if (state.ss_rc != 0) {
713*3147Sxc151355 			err = state.ss_rc;
714*3147Sxc151355 			rw_exit(&drv_secobj_lock);
715*3147Sxc151355 			goto failed;
716*3147Sxc151355 		}
717*3147Sxc151355 		sgp->sg_count = state.ss_count;
718*3147Sxc151355 		extra = state.ss_count * sizeof (dld_secobj_t);
719*3147Sxc151355 	}
720*3147Sxc151355 	rw_exit(&drv_secobj_lock);
721*3147Sxc151355 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
722*3147Sxc151355 	return;
723*3147Sxc151355 
724*3147Sxc151355 failed:
725*3147Sxc151355 	ASSERT(err != 0);
726*3147Sxc151355 	miocnak(q, mp, 0, err);
727*3147Sxc151355 
728*3147Sxc151355 }
729*3147Sxc151355 
730*3147Sxc151355 static void
731*3147Sxc151355 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
732*3147Sxc151355 {
733*3147Sxc151355 	dld_ioc_secobj_unset_t	*sup;
734*3147Sxc151355 	dld_secobj_t		*objp;
735*3147Sxc151355 	mod_hash_val_t		val;
736*3147Sxc151355 	int			err = EINVAL;
737*3147Sxc151355 	queue_t			*q = ctls->cs_wq;
738*3147Sxc151355 
739*3147Sxc151355 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
740*3147Sxc151355 		goto failed;
741*3147Sxc151355 
742*3147Sxc151355 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
743*3147Sxc151355 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
744*3147Sxc151355 		goto failed;
745*3147Sxc151355 
746*3147Sxc151355 	rw_enter(&drv_secobj_lock, RW_WRITER);
747*3147Sxc151355 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
748*3147Sxc151355 	    (mod_hash_val_t *)&objp);
749*3147Sxc151355 	if (err != 0) {
750*3147Sxc151355 		ASSERT(err == MH_ERR_NOTFOUND);
751*3147Sxc151355 		err = ENOENT;
752*3147Sxc151355 		rw_exit(&drv_secobj_lock);
753*3147Sxc151355 		goto failed;
754*3147Sxc151355 	}
755*3147Sxc151355 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
756*3147Sxc151355 	    (mod_hash_val_t *)&val);
757*3147Sxc151355 	ASSERT(err == 0);
758*3147Sxc151355 	ASSERT(objp == (dld_secobj_t *)val);
759*3147Sxc151355 
760*3147Sxc151355 	kmem_cache_free(drv_secobj_cachep, objp);
761*3147Sxc151355 	rw_exit(&drv_secobj_lock);
762*3147Sxc151355 	miocack(q, mp, 0, 0);
763*3147Sxc151355 	return;
764*3147Sxc151355 
765*3147Sxc151355 failed:
766*3147Sxc151355 	ASSERT(err != 0);
767*3147Sxc151355 	miocnak(q, mp, 0, err);
768*3147Sxc151355 }
769