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