xref: /illumos-gate/usr/src/uts/common/io/dld/dld_drv.c (revision f595a68a3b8953a12aa778c2abd7642df8da8c3a)
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;
500 	dls_vlan_t	*dvp;
501 
502 	nmp = mp->b_cont;
503 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
504 		err = EINVAL;
505 		miocnak(q, mp, 0, err);
506 		return;
507 	}
508 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
509 
510 	if ((err = dls_vlan_hold(dhv->dhv_name, &dvp, B_TRUE)) != 0) {
511 		miocnak(q, mp, 0, err);
512 		return;
513 	}
514 
515 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
516 	    dhv->dhv_docheck)) != 0) {
517 		dls_vlan_rele(dvp);
518 		miocnak(q, mp, 0, err);
519 	} else {
520 		miocack(q, mp, 0, 0);
521 	}
522 }
523 
524 /*
525  * DLDIOCRELEVLAN
526  */
527 static void
528 drv_rele_vlan(dld_ctl_str_t *ctls, mblk_t *mp)
529 {
530 	queue_t		*q = ctls->cs_wq;
531 	dld_hold_vlan_t	*dhv;
532 	mblk_t		*nmp;
533 	int		err;
534 
535 	nmp = mp->b_cont;
536 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
537 		err = EINVAL;
538 		miocnak(q, mp, 0, err);
539 		return;
540 	}
541 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
542 
543 	if ((err = dls_vlan_setzoneid(dhv->dhv_name, dhv->dhv_zid,
544 	    dhv->dhv_docheck)) != 0) {
545 		miocnak(q, mp, 0, err);
546 		return;
547 	}
548 
549 	if ((err = dls_vlan_rele_by_name(dhv->dhv_name)) != 0) {
550 		miocnak(q, mp, 0, err);
551 		return;
552 	}
553 
554 	miocack(q, mp, 0, 0);
555 }
556 
557 /*
558  * DLDIOCZIDGET
559  */
560 static void
561 drv_ioc_zid_get(dld_ctl_str_t *ctls, mblk_t *mp)
562 {
563 	queue_t		*q = ctls->cs_wq;
564 	dld_hold_vlan_t	*dhv;
565 	mblk_t		*nmp;
566 	int		err;
567 
568 	nmp = mp->b_cont;
569 	if (nmp == NULL || MBLKL(nmp) < sizeof (dld_hold_vlan_t)) {
570 		err = EINVAL;
571 		miocnak(q, mp, 0, err);
572 		return;
573 	}
574 	dhv = (dld_hold_vlan_t *)nmp->b_rptr;
575 
576 	if ((err = dls_vlan_getzoneid(dhv->dhv_name, &dhv->dhv_zid)) != 0)
577 		miocnak(q, mp, 0, err);
578 	else
579 		miocack(q, mp, sizeof (dld_hold_vlan_t), 0);
580 }
581 
582 /*
583  * Process an IOCTL message received by the control node.
584  */
585 static void
586 drv_ioc(dld_ctl_str_t *ctls, mblk_t *mp)
587 {
588 	uint_t	cmd;
589 
590 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
591 	switch (cmd) {
592 	case DLDIOCATTR:
593 		drv_ioc_attr(ctls, mp);
594 		return;
595 	case DLDIOCVLAN:
596 		drv_ioc_vlan(ctls, mp);
597 		return;
598 	case DLDIOCSECOBJSET:
599 		drv_ioc_secobj_set(ctls, mp);
600 		return;
601 	case DLDIOCSECOBJGET:
602 		drv_ioc_secobj_get(ctls, mp);
603 		return;
604 	case DLDIOCSECOBJUNSET:
605 		drv_ioc_secobj_unset(ctls, mp);
606 		return;
607 	case DLDIOCHOLDVLAN:
608 		drv_hold_vlan(ctls, mp);
609 		return;
610 	case DLDIOCRELEVLAN:
611 		drv_rele_vlan(ctls, mp);
612 		return;
613 	case DLDIOCZIDGET:
614 		drv_ioc_zid_get(ctls, mp);
615 		return;
616 	default:
617 		miocnak(ctls->cs_wq, mp, 0, ENOTSUP);
618 		return;
619 	}
620 }
621 
622 /*
623  * Write side put routine of the dld control node.
624  */
625 static void
626 drv_uw_put(queue_t *q, mblk_t *mp)
627 {
628 	dld_ctl_str_t *ctls = q->q_ptr;
629 
630 	switch (mp->b_datap->db_type) {
631 	case M_IOCTL:
632 		drv_ioc(ctls, mp);
633 		break;
634 	default:
635 		freemsg(mp);
636 		break;
637 	}
638 }
639 
640 /*
641  * Write-side service procedure.
642  */
643 void
644 drv_uw_srv(queue_t *q)
645 {
646 	mblk_t *mp;
647 
648 	while (mp = getq(q))
649 		drv_uw_put(q, mp);
650 }
651 
652 /*
653  * Secure objects implementation
654  */
655 
656 /* ARGSUSED */
657 static int
658 drv_secobj_ctor(void *buf, void *arg, int kmflag)
659 {
660 	bzero(buf, sizeof (dld_secobj_t));
661 	return (0);
662 }
663 
664 static void
665 drv_secobj_init(void)
666 {
667 	rw_init(&drv_secobj_lock, NULL, RW_DEFAULT, NULL);
668 	drv_secobj_cachep = kmem_cache_create("drv_secobj_cache",
669 	    sizeof (dld_secobj_t), 0, drv_secobj_ctor, NULL,
670 	    NULL, NULL, NULL, 0);
671 	drv_secobj_hash = mod_hash_create_extended("drv_secobj_hash",
672 	    SECOBJ_WEP_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
673 	    mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
674 }
675 
676 static void
677 drv_secobj_fini(void)
678 {
679 	mod_hash_destroy_hash(drv_secobj_hash);
680 	kmem_cache_destroy(drv_secobj_cachep);
681 	rw_destroy(&drv_secobj_lock);
682 }
683 
684 static void
685 drv_ioc_secobj_set(dld_ctl_str_t *ctls, mblk_t *mp)
686 {
687 	dld_ioc_secobj_set_t	*ssp;
688 	dld_secobj_t		*sobjp, *objp;
689 	int			err = EINVAL;
690 	queue_t			*q = ctls->cs_wq;
691 
692 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_set_t))) != 0)
693 		goto failed;
694 
695 	ssp = (dld_ioc_secobj_set_t *)mp->b_cont->b_rptr;
696 	sobjp = &ssp->ss_obj;
697 
698 	if (sobjp->so_class != DLD_SECOBJ_CLASS_WEP)
699 		goto failed;
700 
701 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0' ||
702 	    sobjp->so_len > DLD_SECOBJ_VAL_MAX)
703 		goto failed;
704 
705 	rw_enter(&drv_secobj_lock, RW_WRITER);
706 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sobjp->so_name,
707 	    (mod_hash_val_t *)&objp);
708 	if (err == 0) {
709 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) != 0) {
710 			err = EEXIST;
711 			rw_exit(&drv_secobj_lock);
712 			goto failed;
713 		}
714 	} else {
715 		ASSERT(err == MH_ERR_NOTFOUND);
716 		if ((ssp->ss_flags & DLD_SECOBJ_OPT_CREATE) == 0) {
717 			err = ENOENT;
718 			rw_exit(&drv_secobj_lock);
719 			goto failed;
720 		}
721 		objp = kmem_cache_alloc(drv_secobj_cachep, KM_SLEEP);
722 		(void) strlcpy(objp->so_name, sobjp->so_name,
723 		    DLD_SECOBJ_NAME_MAX);
724 
725 		err = mod_hash_insert(drv_secobj_hash,
726 		    (mod_hash_key_t)objp->so_name, (mod_hash_val_t)objp);
727 		ASSERT(err == 0);
728 	}
729 	bcopy(sobjp->so_val, objp->so_val, sobjp->so_len);
730 	objp->so_len = sobjp->so_len;
731 	objp->so_class = sobjp->so_class;
732 	rw_exit(&drv_secobj_lock);
733 	miocack(q, mp, 0, 0);
734 	return;
735 
736 failed:
737 	ASSERT(err != 0);
738 	miocnak(q, mp, 0, err);
739 
740 }
741 
742 typedef struct dld_secobj_state {
743 	uint_t		ss_free;
744 	uint_t		ss_count;
745 	int		ss_rc;
746 	dld_secobj_t	*ss_objp;
747 } dld_secobj_state_t;
748 
749 /* ARGSUSED */
750 static uint_t
751 drv_secobj_walker(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
752 {
753 	dld_secobj_state_t	*statep = arg;
754 	dld_secobj_t		*sobjp = (dld_secobj_t *)val;
755 
756 	if (statep->ss_free < sizeof (dld_secobj_t)) {
757 		statep->ss_rc = ENOSPC;
758 		return (MH_WALK_TERMINATE);
759 	}
760 	bcopy(sobjp, statep->ss_objp, sizeof (dld_secobj_t));
761 	statep->ss_objp++;
762 	statep->ss_free -= sizeof (dld_secobj_t);
763 	statep->ss_count++;
764 	return (MH_WALK_CONTINUE);
765 }
766 
767 static void
768 drv_ioc_secobj_get(dld_ctl_str_t *ctls, mblk_t *mp)
769 {
770 	dld_ioc_secobj_get_t	*sgp;
771 	dld_secobj_t		*sobjp, *objp;
772 	int			err = EINVAL;
773 	uint_t			extra = 0;
774 	queue_t			*q = ctls->cs_wq;
775 	mblk_t			*bp;
776 
777 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_get_t))) != 0)
778 		goto failed;
779 
780 	if ((bp = msgpullup(mp->b_cont, -1)) == NULL)
781 		goto failed;
782 
783 	freemsg(mp->b_cont);
784 	mp->b_cont = bp;
785 	sgp = (dld_ioc_secobj_get_t *)bp->b_rptr;
786 	sobjp = &sgp->sg_obj;
787 
788 	if (sobjp->so_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
789 		goto failed;
790 
791 	rw_enter(&drv_secobj_lock, RW_READER);
792 	if (sobjp->so_name[0] != '\0') {
793 		err = mod_hash_find(drv_secobj_hash,
794 		    (mod_hash_key_t)sobjp->so_name, (mod_hash_val_t *)&objp);
795 		if (err != 0) {
796 			ASSERT(err == MH_ERR_NOTFOUND);
797 			err = ENOENT;
798 			rw_exit(&drv_secobj_lock);
799 			goto failed;
800 		}
801 		bcopy(objp->so_val, sobjp->so_val, objp->so_len);
802 		sobjp->so_len = objp->so_len;
803 		sobjp->so_class = objp->so_class;
804 		sgp->sg_count = 1;
805 	} else {
806 		dld_secobj_state_t	state;
807 
808 		state.ss_free = MBLKL(bp) - sizeof (dld_ioc_secobj_get_t);
809 		state.ss_count = 0;
810 		state.ss_rc = 0;
811 		state.ss_objp = (dld_secobj_t *)(sgp + 1);
812 		mod_hash_walk(drv_secobj_hash, drv_secobj_walker, &state);
813 		if (state.ss_rc != 0) {
814 			err = state.ss_rc;
815 			rw_exit(&drv_secobj_lock);
816 			goto failed;
817 		}
818 		sgp->sg_count = state.ss_count;
819 		extra = state.ss_count * sizeof (dld_secobj_t);
820 	}
821 	rw_exit(&drv_secobj_lock);
822 	miocack(q, mp, sizeof (dld_ioc_secobj_get_t) + extra, 0);
823 	return;
824 
825 failed:
826 	ASSERT(err != 0);
827 	miocnak(q, mp, 0, err);
828 
829 }
830 
831 static void
832 drv_ioc_secobj_unset(dld_ctl_str_t *ctls, mblk_t *mp)
833 {
834 	dld_ioc_secobj_unset_t	*sup;
835 	dld_secobj_t		*objp;
836 	mod_hash_val_t		val;
837 	int			err = EINVAL;
838 	queue_t			*q = ctls->cs_wq;
839 
840 	if ((err = miocpullup(mp, sizeof (dld_ioc_secobj_unset_t))) != 0)
841 		goto failed;
842 
843 	sup = (dld_ioc_secobj_unset_t *)mp->b_cont->b_rptr;
844 	if (sup->su_name[DLD_SECOBJ_NAME_MAX - 1] != '\0')
845 		goto failed;
846 
847 	rw_enter(&drv_secobj_lock, RW_WRITER);
848 	err = mod_hash_find(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
849 	    (mod_hash_val_t *)&objp);
850 	if (err != 0) {
851 		ASSERT(err == MH_ERR_NOTFOUND);
852 		err = ENOENT;
853 		rw_exit(&drv_secobj_lock);
854 		goto failed;
855 	}
856 	err = mod_hash_remove(drv_secobj_hash, (mod_hash_key_t)sup->su_name,
857 	    (mod_hash_val_t *)&val);
858 	ASSERT(err == 0);
859 	ASSERT(objp == (dld_secobj_t *)val);
860 
861 	kmem_cache_free(drv_secobj_cachep, objp);
862 	rw_exit(&drv_secobj_lock);
863 	miocack(q, mp, 0, 0);
864 	return;
865 
866 failed:
867 	ASSERT(err != 0);
868 	miocnak(q, mp, 0, err);
869 }
870