xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision 789e0dbbcdddab55f064dbca13950cb068a30efe)
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 2007 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/dld.h>
38 #include	<sys/dld_impl.h>
39 #include	<sys/dls_impl.h>
40 #include 	<sys/vlan.h>
41 #include	<inet/common.h>
42 
43 /*
44  * dld control node state, one per open control node session.
45  */
46 typedef struct dld_ctl_str_s {
47 	minor_t cs_minor;
48 	queue_t *cs_wq;
49 } dld_ctl_str_t;
50 
51 static void	drv_init(void);
52 static int	drv_fini(void);
53 
54 static int	drv_getinfo(dev_info_t	*, ddi_info_cmd_t, void *, void **);
55 static int	drv_attach(dev_info_t *, ddi_attach_cmd_t);
56 static int	drv_detach(dev_info_t *, ddi_detach_cmd_t);
57 
58 /*
59  * Secure objects declarations
60  */
61 #define	SECOBJ_WEP_HASHSZ	67
62 static krwlock_t	drv_secobj_lock;
63 static kmem_cache_t	*drv_secobj_cachep;
64 static mod_hash_t	*drv_secobj_hash;
65 static void		drv_secobj_init(void);
66 static void		drv_secobj_fini(void);
67 static void		drv_ioc_secobj_set(dld_ctl_str_t *, mblk_t *);
68 static void		drv_ioc_secobj_get(dld_ctl_str_t *, mblk_t *);
69 static void		drv_ioc_secobj_unset(dld_ctl_str_t *, mblk_t *);
70 
71 /*
72  * The following entry points are private to dld and are used for control
73  * operations only. The entry points exported to mac drivers are defined
74  * in dld_str.c. Refer to the comment on top of dld_str.c for details.
75  */
76 static int	drv_open(queue_t *, dev_t *, int, int, cred_t *);
77 static int	drv_close(queue_t *);
78 
79 static void	drv_uw_put(queue_t *, mblk_t *);
80 static void	drv_uw_srv(queue_t *);
81 
82 dev_info_t	*dld_dip;		/* dev_info_t for the driver */
83 uint32_t	dld_opt = 0;		/* Global options */
84 static vmem_t	*dld_ctl_vmem;		/* for control minor numbers */
85 
86 static	struct	module_info	drv_info = {
87 	0,			/* mi_idnum */
88 	DLD_DRIVER_NAME,	/* mi_idname */
89 	0,			/* mi_minpsz */
90 	(64 * 1024),		/* mi_maxpsz */
91 	1,			/* mi_hiwat */
92 	0			/* mi_lowat */
93 };
94 
95 static	struct qinit		drv_ur_init = {
96 	NULL,			/* qi_putp */
97 	NULL,			/* qi_srvp */
98 	drv_open,		/* qi_qopen */
99 	drv_close,		/* qi_qclose */
100 	NULL,			/* qi_qadmin */
101 	&drv_info,		/* qi_minfo */
102 	NULL			/* qi_mstat */
103 };
104 
105 static	struct qinit		drv_uw_init = {
106 	(pfi_t)drv_uw_put,	/* qi_putp */
107 	(pfi_t)drv_uw_srv,	/* qi_srvp */
108 	NULL,			/* qi_qopen */
109 	NULL,			/* qi_qclose */
110 	NULL,			/* qi_qadmin */
111 	&drv_info,		/* qi_minfo */
112 	NULL			/* qi_mstat */
113 };
114 
115 static	struct streamtab	drv_stream = {
116 	&drv_ur_init,		/* st_rdinit */
117 	&drv_uw_init,		/* st_wrinit */
118 	NULL,			/* st_muxrinit */
119 	NULL			/* st_muxwinit */
120 };
121 
122 DDI_DEFINE_STREAM_OPS(drv_ops, nulldev, nulldev, drv_attach, drv_detach,
123     nodev, drv_getinfo, D_MP, &drv_stream);
124 
125 /*
126  * Module linkage information for the kernel.
127  */
128 
129 extern	struct mod_ops		mod_driverops;
130 
131 static	struct modldrv		drv_modldrv = {
132 	&mod_driverops,
133 	DLD_INFO,
134 	&drv_ops
135 };
136 
137 static	struct modlinkage	drv_modlinkage = {
138 	MODREV_1,
139 	&drv_modldrv,
140 	NULL
141 };
142 
143 int
144 _init(void)
145 {
146 	int	err;
147 
148 	drv_init();
149 
150 	if ((err = mod_install(&drv_modlinkage)) != 0)
151 		return (err);
152 
153 	return (0);
154 }
155 
156 int
157 _fini(void)
158 {
159 	int	err;
160 
161 	if ((err = mod_remove(&drv_modlinkage)) != 0)
162 		return (err);
163 
164 	if (drv_fini() != 0) {
165 		(void) mod_install(&drv_modlinkage);
166 		return (DDI_FAILURE);
167 	}
168 
169 	return (err);
170 }
171 
172 int
173 _info(struct modinfo *modinfop)
174 {
175 	return (mod_info(&drv_modlinkage, modinfop));
176 }
177 
178 /*
179  * Initialize component modules.
180  */
181 static void
182 drv_init(void)
183 {
184 	dld_ctl_vmem = vmem_create("dld_ctl", (void *)1, MAXMIN, 1,
185 	    NULL, NULL, NULL, 1, VM_SLEEP | VMC_IDENTIFIER);
186 	drv_secobj_init();
187 	dld_str_init();
188 }
189 
190 static int
191 drv_fini(void)
192 {
193 	int	err;
194 
195 	if ((err = dld_str_fini()) != 0)
196 		return (err);
197 
198 	drv_secobj_fini();
199 	vmem_destroy(dld_ctl_vmem);
200 	return (0);
201 }
202 
203 /*
204  * devo_getinfo: getinfo(9e)
205  */
206 /*ARGSUSED*/
207 static int
208 drv_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resp)
209 {
210 	if (dld_dip == NULL)
211 		return (DDI_FAILURE);
212 
213 	switch (cmd) {
214 	case DDI_INFO_DEVT2INSTANCE:
215 		*resp = (void *)0;
216 		break;
217 	case DDI_INFO_DEVT2DEVINFO:
218 		*resp = (void *)dld_dip;
219 		break;
220 	default:
221 		return (DDI_FAILURE);
222 	}
223 
224 	return (DDI_SUCCESS);
225 }
226 
227 /*
228  * Check properties to set options. (See dld.h for property definitions).
229  */
230 static void
231 drv_set_opt(dev_info_t *dip)
232 {
233 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
234 	    DLD_PROP_NO_FASTPATH, 0) != 0) {
235 		dld_opt |= DLD_OPT_NO_FASTPATH;
236 	}
237 
238 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
239 	    DLD_PROP_NO_POLL, 0) != 0) {
240 		dld_opt |= DLD_OPT_NO_POLL;
241 	}
242 
243 	if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
244 	    DLD_PROP_NO_ZEROCOPY, 0) != 0) {
245 		dld_opt |= DLD_OPT_NO_ZEROCOPY;
246 	}
247 }
248 
249 /*
250  * devo_attach: attach(9e)
251  */
252 static int
253 drv_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
254 {
255 	if (cmd != DDI_ATTACH)
256 		return (DDI_FAILURE);
257 
258 	ASSERT(ddi_get_instance(dip) == 0);
259 
260 	drv_set_opt(dip);
261 
262 	/*
263 	 * Create control node. DLPI provider nodes will be created on demand.
264 	 */
265 	if (ddi_create_minor_node(dip, DLD_CONTROL_MINOR_NAME, S_IFCHR,
266 	    DLD_CONTROL_MINOR, DDI_PSEUDO, 0) != DDI_SUCCESS)
267 		return (DDI_FAILURE);
268 
269 	dld_dip = dip;
270 
271 	/*
272 	 * Log the fact that the driver is now attached.
273 	 */
274 	ddi_report_dev(dip);
275 	return (DDI_SUCCESS);
276 }
277 
278 /*
279  * devo_detach: detach(9e)
280  */
281 static int
282 drv_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
283 {
284 	if (cmd != DDI_DETACH)
285 		return (DDI_FAILURE);
286 
287 	ASSERT(dld_dip == dip);
288 
289 	/*
290 	 * Remove the control node.
291 	 */
292 	ddi_remove_minor_node(dip, DLD_CONTROL_MINOR_NAME);
293 	dld_dip = NULL;
294 
295 	return (DDI_SUCCESS);
296 }
297 
298 /*
299  * dld control node open procedure.
300  */
301 /*ARGSUSED*/
302 static int
303 drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
304 {
305 	dld_ctl_str_t	*ctls;
306 	minor_t		minor;
307 	queue_t *oq =	OTHERQ(rq);
308 
309 	if (sflag == MODOPEN)
310 		return (ENOTSUP);
311 
312 	/*
313 	 * This is a cloning driver and therefore each queue should only
314 	 * ever get opened once.
315 	 */
316 	if (rq->q_ptr != NULL)
317 		return (EBUSY);
318 
319 	minor = (minor_t)(uintptr_t)vmem_alloc(dld_ctl_vmem, 1, VM_NOSLEEP);
320 	if (minor == 0)
321 		return (ENOMEM);
322 
323 	ctls = kmem_zalloc(sizeof (dld_ctl_str_t), KM_NOSLEEP);
324 	if (ctls == NULL) {
325 		vmem_free(dld_ctl_vmem, (void *)(uintptr_t)minor, 1);
326 		return (ENOMEM);
327 	}
328 
329 	ctls->cs_minor = minor;
330 	ctls->cs_wq = WR(rq);
331 
332 	rq->q_ptr = ctls;
333 	oq->q_ptr = ctls;
334 
335 	/*
336 	 * Enable the queue srv(9e) routine.
337 	 */
338 	qprocson(rq);
339 
340 	/*
341 	 * Construct a cloned dev_t to hand back.
342 	 */
343 	*devp = makedevice(getmajor(*devp), ctls->cs_minor);
344 	return (0);
345 }
346 
347 /*
348  * dld control node close procedure.
349  */
350 static int
351 drv_close(queue_t *rq)
352 {
353 	dld_ctl_str_t	*ctls;
354 
355 	ctls = rq->q_ptr;
356 	ASSERT(ctls != NULL);
357 
358 	/*
359 	 * Disable the queue srv(9e) routine.
360 	 */
361 	qprocsoff(rq);
362 
363 	vmem_free(dld_ctl_vmem, (void *)(uintptr_t)ctls->cs_minor, 1);
364 
365 	kmem_free(ctls, sizeof (dld_ctl_str_t));
366 
367 	return (0);
368 }
369 
370 /*
371  * DLDIOCATTR
372  */
373 static void
374 drv_ioc_attr(dld_ctl_str_t *ctls, mblk_t *mp)
375 {
376 	dld_ioc_attr_t	*diap;
377 	dls_vlan_t	*dvp = NULL;
378 	dls_link_t	*dlp = NULL;
379 	int		err;
380 	queue_t		*q = ctls->cs_wq;
381 
382 	if ((err = miocpullup(mp, sizeof (dld_ioc_attr_t))) != 0)
383 		goto failed;
384 
385 	diap = (dld_ioc_attr_t *)mp->b_cont->b_rptr;
386 	diap->dia_name[IFNAMSIZ - 1] = '\0';
387 
388 	if (dls_vlan_hold(diap->dia_name, &dvp, B_FALSE) != 0) {
389 		err = ENOENT;
390 		goto failed;
391 	}
392 
393 	dlp = dvp->dv_dlp;
394 	(void) strlcpy(diap->dia_dev, dlp->dl_name, sizeof (diap->dia_dev));
395 	diap->dia_vid = dvp->dv_id;
396 	diap->dia_max_sdu = dlp->dl_mip->mi_sdu_max;
397 
398 	dls_vlan_rele(dvp);
399 	miocack(q, mp, sizeof (dld_ioc_attr_t), 0);
400 	return;
401 
402 failed:
403 	ASSERT(err != 0);
404 	if (err == ENOENT) {
405 		char	devname[MAXNAMELEN];
406 		uint_t	instance;
407 		major_t	major;
408 
409 		/*
410 		 * Try to detect if the specified device is gldv3
411 		 * and return ENODEV if it is not.
412 		 */
413 		if (ddi_parse(diap->dia_name, devname, &instance) == 0 &&
414 		    (major = ddi_name_to_major(devname)) != (major_t)-1 &&
415 		    !GLDV3_DRV(major))
416 			err = ENODEV;
417 	}
418 	miocnak(q, mp, 0, err);
419 }
420 
421 
422 /*
423  * DLDIOCVLAN
424  */
425 typedef struct dld_ioc_vlan_state {
426 	uint_t		bytes_left;
427 	dld_ioc_vlan_t	*divp;
428 	dld_vlan_info_t	*vlanp;
429 } dld_ioc_vlan_state_t;
430 
431 static int
432 drv_ioc_vlan_info(dls_vlan_t *dvp, void *arg)
433 {
434 	dld_ioc_vlan_state_t	*statep = arg;
435 
436 	/*
437 	 * passed buffer space is limited to 65536 bytes. So
438 	 * copy only the vlans associated with the passed link.
439 	 */
440 	if (strcmp(dvp->dv_dlp->dl_name, statep->divp->div_name) == 0 &&
441 	    dvp->dv_id != 0) {
442 		if (statep->bytes_left < sizeof (dld_vlan_info_t))
443 			return (ENOSPC);
444 
445 		(void) strlcpy(statep->vlanp->dvi_name,
446 		    dvp->dv_name, IFNAMSIZ);
447 		statep->divp->div_count++;
448 		statep->bytes_left -= sizeof (dld_vlan_info_t);
449 		statep->vlanp += 1;
450 	}
451 	return (0);
452 }
453 
454 static void
455 drv_ioc_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
456 {
457 	dld_ioc_vlan_t		*divp;
458 	dld_ioc_vlan_state_t	state;
459 	int			err = EINVAL;
460 	queue_t			*q = ctls->cs_wq;
461 	mblk_t			*bp;
462 
463 	if ((err = miocpullup(mp, sizeof (dld_ioc_vlan_t))) != 0)
464 		goto failed;
465 
466 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
467 		goto failed;
468 
469 	freemsg(mp->b_cont);
470 	mp->b_cont = bp;
471 	divp = (dld_ioc_vlan_t *)bp->b_rptr;
472 	divp->div_count = 0;
473 	state.bytes_left = MBLKL(bp) - sizeof (dld_ioc_vlan_t);
474 	state.divp = divp;
475 	state.vlanp = (dld_vlan_info_t *)(divp + 1);
476 
477 	err = dls_vlan_walk(drv_ioc_vlan_info, &state);
478 	if (err != 0)
479 		goto failed;
480 
481 	miocack(q, mp, sizeof (dld_ioc_vlan_t) +
482 	    state.divp->div_count * sizeof (dld_vlan_info_t), 0);
483 	return;
484 
485 failed:
486 	ASSERT(err != 0);
487 	miocnak(q, mp, 0, err);
488 }
489 
490 /*
491  * DLDIOCHOLDVLAN
492  */
493 static void
494 drv_hold_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
495 {
496 	queue_t		*q = ctls->cs_wq;
497 	dld_hold_vlan_t	*dhv;
498 	mblk_t		*nmp;
499 	int		err = EINVAL;
500 	dls_vlan_t	*dvp;
501 	char		mac[MAXNAMELEN];
502 	dev_info_t	*dip = NULL;
503 	major_t		major;
504 	uint_t		index;
505 
506 	nmp = mp->b_cont;
507 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t))
508 		goto failed;
509 
510 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
511 
512 	/*
513 	 * When a device instance without opens is detached, its
514 	 * dls_vlan_t will be destroyed. A subsequent DLDIOCHOLDVLAN
515 	 * invoked on this device instance will fail because
516 	 * dls_vlan_hold() does not create non-tagged vlans on demand.
517 	 * To handle this problem, we must force the creation of the
518 	 * dls_vlan_t (if it doesn't already exist) by calling
519 	 * ddi_hold_devi_by_instance() before calling dls_vlan_hold().
520 	 */
521 	if (ddi_parse(dhv->dhv_name, mac, &index) != DDI_SUCCESS)
522 		goto failed;
523 
524 	if (DLS_PPA2VID(index) == VLAN_ID_NONE && strcmp(mac, "aggr") != 0) {
525 		if ((major = ddi_name_to_major(mac)) == (major_t)-1 ||
526 		    (dip = ddi_hold_devi_by_instance(major,
527 		    DLS_PPA2INST(index), 0)) == NULL)
528 			goto failed;
529 	}
530 
531 	err = dls_vlan_hold(dhv->dhv_name, &dvp, B_TRUE);
532 	if (dip != NULL)
533 		ddi_release_devi(dip);
534 
535 	if (err != 0)
536 		goto failed;
537 
538 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
539 	    dhv->dhv_docheck)) != 0) {
540 		dls_vlan_rele(dvp);
541 		goto failed;
542 	} else {
543 		miocack(q, mp, 0, 0);
544 		return;
545 	}
546 failed:
547 	miocnak(q, mp, 0, err);
548 }
549 
550 /*
551  * DLDIOCRELEVLAN
552  */
553 static void
554 drv_rele_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
555 {
556 	queue_t		*q = ctls->cs_wq;
557 	dld_hold_vlan_t	*dhv;
558 	mblk_t		*nmp;
559 	int		err;
560 
561 	nmp = mp->b_cont;
562 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
563 		err = EINVAL;
564 		miocnak(q, mp, 0, err);
565 		return;
566 	}
567 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
568 
569 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
570 	    dhv->dhv_docheck)) != 0) {
571 		miocnak(q, mp, 0, err);
572 		return;
573 	}
574 
575 	if ((err = dls_vlan_rele_by_name(dhv->dhv_name)) != 0) {
576 		miocnak(q, mp, 0, err);
577 		return;
578 	}
579 
580 	miocack(q, mp, 0, 0);
581 }
582 
583 /*
584  * DLDIOCZIDGET
585  */
586 static void
587 drv_ioc_zid_get(dld_ctl_str_t *ctls, mblk_t *mp)
588 {
589 	queue_t		*q = ctls->cs_wq;
590 	dld_hold_vlan_t	*dhv;
591 	mblk_t		*nmp;
592 	int		err;
593 
594 	nmp = mp->b_cont;
595 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
596 		err = EINVAL;
597 		miocnak(q, mp, 0, err);
598 		return;
599 	}
600 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
601 
602 	if ((err = dls_vlan_getzoneid(dhv->dhv_name, &dhv->dhv_zid)) != 0)
603 		miocnak(q, mp, 0, err);
604 	else
605 		miocack(q, mp, sizeof (dld_hold_vlan_t), 0);
606 }
607 
608 /*
609  * Process an IOCTL message received by the control node.
610  */
611 static void
612 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
613 {
614 	uint_t	cmd;
615 
616 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
617 	switch (cmd) {
618 	case DLDIOCATTR:
619 		drv_ioc_attr(ctls, mp);
620 		return;
621 	case DLDIOCVLAN:
622 		drv_ioc_vlan(ctls, mp);
623 		return;
624 	case DLDIOCSECOBJSET:
625 		drv_ioc_secobj_set(ctls, mp);
626 		return;
627 	case DLDIOCSECOBJGET:
628 		drv_ioc_secobj_get(ctls, mp);
629 		return;
630 	case DLDIOCSECOBJUNSET:
631 		drv_ioc_secobj_unset(ctls, mp);
632 		return;
633 	case DLDIOCHOLDVLAN:
634 		drv_hold_vlan(ctls, mp);
635 		return;
636 	case DLDIOCRELEVLAN:
637 		drv_rele_vlan(ctls, mp);
638 		return;
639 	case DLDIOCZIDGET:
640 		drv_ioc_zid_get(ctls, mp);
641 		return;
642 	default:
643 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
644 		return;
645 	}
646 }
647 
648 /*
649  * Write side put routine of the dld control node.
650  */
651 static void
652 drv_uw_put(queue_t *q, mblk_t *mp)
653 {
654 	dld_ctl_str_t *ctls = q->q_ptr;
655 
656 	switch (mp->b_datap->db_type) {
657 	case M_IOCTL:
658 		drv_ioc(ctls, mp);
659 		break;
660 	default:
661 		freemsg(mp);
662 		break;
663 	}
664 }
665 
666 /*
667  * Write-side service procedure.
668  */
669 void
670 drv_uw_srv(queue_t *q)
671 {
672 	mblk_t *mp;
673 
674 	while (mp = getq(q))
675 		drv_uw_put(q, mp);
676 }
677 
678 /*
679  * Secure objects implementation
680  */
681 
682 /* ARGSUSED */
683 static int
684 drv_secobj_ctor(void *buf, void *arg, int kmflag)
685 {
686 	bzero(buf, sizeof (dld_secobj_t));
687 	return (0);
688 }
689 
690 static void
691 drv_secobj_init(void)
692 {
693 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
694 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
695 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
696 	    NULL, NULL, NULL, 0);
697 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
698 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
699 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
700 }
701 
702 static void
703 drv_secobj_fini(void)
704 {
705 	mod_hash_destroy_hash(drv_secobj_hash);
706 	kmem_cache_destroy(drv_secobj_cachep);
707 	rw_destroy(&drv_secobj_lock);
708 }
709 
710 static void
711 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
712 {
713 	dld_ioc_secobj_set_t	*ssp;
714 	dld_secobj_t		*sobjp, *objp;
715 	int			err = EINVAL;
716 	queue_t			*q = ctls->cs_wq;
717 
718 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
719 		goto failed;
720 
721 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
722 	sobjp = &ssp->ss_obj;
723 
724 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP)
725 		goto failed;
726 
727 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
728 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
729 		goto failed;
730 
731 	rw_enter(&drv_secobj_lock, RW_WRITER);
732 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
733 	    (mod_hash_val_t *)&objp);
734 	if (err == 0) {
735 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
736 			err = EEXIST;
737 			rw_exit(&drv_secobj_lock);
738 			goto failed;
739 		}
740 	} else {
741 		ASSERT(err == MH_ERR_NOTFOUND);
742 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
743 			err = ENOENT;
744 			rw_exit(&drv_secobj_lock);
745 			goto failed;
746 		}
747 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
748 		(void) strlcpy(objp->so_name, sobjp->so_name,
749 		    DLD_SECOBJ_NAME_MAX);
750 
751 		err = mod_hash_insert(drv_secobj_hash,
752 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
753 		ASSERT(err == 0);
754 	}
755 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
756 	objp->so_len = sobjp->so_len;
757 	objp->so_class = sobjp->so_class;
758 	rw_exit(&drv_secobj_lock);
759 	miocack(q, mp, 0, 0);
760 	return;
761 
762 failed:
763 	ASSERT(err != 0);
764 	miocnak(q, mp, 0, err);
765 
766 }
767 
768 typedef struct dld_secobj_state {
769 	uint_t		ss_free;
770 	uint_t		ss_count;
771 	int		ss_rc;
772 	dld_secobj_t	*ss_objp;
773 } dld_secobj_state_t;
774 
775 /* ARGSUSED */
776 static uint_t
777 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
778 {
779 	dld_secobj_state_t	*statep = arg;
780 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
781 
782 	if (statep->ss_free < sizeof (dld_secobj_t)) {
783 		statep->ss_rc = ENOSPC;
784 		return (MH_WALK_TERMINATE);
785 	}
786 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
787 	statep->ss_objp++;
788 	statep->ss_free -= sizeof (dld_secobj_t);
789 	statep->ss_count++;
790 	return (MH_WALK_CONTINUE);
791 }
792 
793 static void
794 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
795 {
796 	dld_ioc_secobj_get_t	*sgp;
797 	dld_secobj_t		*sobjp, *objp;
798 	int			err = EINVAL;
799 	uint_t			extra = 0;
800 	queue_t			*q = ctls->cs_wq;
801 	mblk_t			*bp;
802 
803 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
804 		goto failed;
805 
806 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
807 		goto failed;
808 
809 	freemsg(mp->b_cont);
810 	mp->b_cont = bp;
811 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
812 	sobjp = &sgp->sg_obj;
813 
814 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
815 		goto failed;
816 
817 	rw_enter(&drv_secobj_lock, RW_READER);
818 	if (sobjp->so_name[0] != '\0') {
819 		err = mod_hash_find(drv_secobj_hash,
820 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
821 		if (err != 0) {
822 			ASSERT(err == MH_ERR_NOTFOUND);
823 			err = ENOENT;
824 			rw_exit(&drv_secobj_lock);
825 			goto failed;
826 		}
827 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
828 		sobjp->so_len = objp->so_len;
829 		sobjp->so_class = objp->so_class;
830 		sgp->sg_count = 1;
831 	} else {
832 		dld_secobj_state_t	state;
833 
834 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
835 		state.ss_count = 0;
836 		state.ss_rc = 0;
837 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
838 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
839 		if (state.ss_rc != 0) {
840 			err = state.ss_rc;
841 			rw_exit(&drv_secobj_lock);
842 			goto failed;
843 		}
844 		sgp->sg_count = state.ss_count;
845 		extra = state.ss_count * sizeof (dld_secobj_t);
846 	}
847 	rw_exit(&drv_secobj_lock);
848 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
849 	return;
850 
851 failed:
852 	ASSERT(err != 0);
853 	miocnak(q, mp, 0, err);
854 
855 }
856 
857 static void
858 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
859 {
860 	dld_ioc_secobj_unset_t	*sup;
861 	dld_secobj_t		*objp;
862 	mod_hash_val_t		val;
863 	int			err = EINVAL;
864 	queue_t			*q = ctls->cs_wq;
865 
866 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
867 		goto failed;
868 
869 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
870 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
871 		goto failed;
872 
873 	rw_enter(&drv_secobj_lock, RW_WRITER);
874 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
875 	    (mod_hash_val_t *)&objp);
876 	if (err != 0) {
877 		ASSERT(err == MH_ERR_NOTFOUND);
878 		err = ENOENT;
879 		rw_exit(&drv_secobj_lock);
880 		goto failed;
881 	}
882 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
883 	    (mod_hash_val_t *)&val);
884 	ASSERT(err == 0);
885 	ASSERT(objp == (dld_secobj_t *)val);
886 
887 	kmem_cache_free(drv_secobj_cachep, objp);
888 	rw_exit(&drv_secobj_lock);
889 	miocack(q, mp, 0, 0);
890 	return;
891 
892 failed:
893 	ASSERT(err != 0);
894 	miocnak(q, mp, 0, err);
895 }
896