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