xref: /illumos-gate/usr/src/uts/common/fs/sockfs/sockparams.c (revision 5002558f6bfef3915c7f3b4ecb7c19c7f044bf5b)
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 /*
23  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 #include <sys/types.h>
27 #include <sys/t_lock.h>
28 #include <sys/param.h>
29 #include <sys/systm.h>
30 #include <sys/sysmacros.h>
31 #include <sys/cmn_err.h>
32 #include <sys/list.h>
33 
34 #include <sys/stropts.h>
35 #include <sys/socket.h>
36 #include <sys/socketvar.h>
37 
38 #include <fs/sockfs/sockcommon.h>
39 #include <fs/sockfs/sockfilter_impl.h>
40 #include <fs/sockfs/socktpi.h>
41 
42 /*
43  * Socket Parameters
44  *
45  * Socket parameter (struct sockparams) entries represent the socket types
46  * available on the system.
47  *
48  * Flags (sp_flags):
49  *
50  * SOCKPARAMS_EPHEMERAL: A temporary sockparams entry that will be deleted
51  * as soon as its' ref count drops to zero. In addition, ephemeral entries will
52  * never be hooked onto the global sockparams list. Ephemeral entries are
53  * created when application requests to create a socket using an application
54  * supplied device path, or when a socket is falling back to TPI.
55  *
56  * Lock order:
57  *   The lock order is sockconf_lock -> sp_lock.
58  */
59 extern int 	kobj_path_exists(char *, int);
60 
61 static int 	sockparams_sdev_init(struct sockparams *, char *, int);
62 static void 	sockparams_sdev_fini(struct sockparams *);
63 
64 /*
65  * Global sockparams list (populated via soconfig(1M)).
66  */
67 static list_t sphead;
68 
69 /*
70  * List of ephemeral sockparams.
71  */
72 static list_t sp_ephem_list;
73 
74 /* Global kstats for sockparams */
75 typedef struct sockparams_g_stats {
76 	kstat_named_t spgs_ephem_nalloc;
77 	kstat_named_t spgs_ephem_nreuse;
78 } sockparams_g_stats_t;
79 
80 static sockparams_g_stats_t sp_g_stats;
81 static kstat_t *sp_g_kstat;
82 
83 
84 void
85 sockparams_init(void)
86 {
87 	list_create(&sphead, sizeof (struct sockparams),
88 	    offsetof(struct sockparams, sp_node));
89 	list_create(&sp_ephem_list, sizeof (struct sockparams),
90 	    offsetof(struct sockparams, sp_node));
91 
92 	kstat_named_init(&sp_g_stats.spgs_ephem_nalloc, "ephemeral_nalloc",
93 	    KSTAT_DATA_UINT64);
94 	kstat_named_init(&sp_g_stats.spgs_ephem_nreuse, "ephemeral_nreuse",
95 	    KSTAT_DATA_UINT64);
96 
97 	sp_g_kstat = kstat_create("sockfs", 0, "sockparams", "misc",
98 	    KSTAT_TYPE_NAMED, sizeof (sp_g_stats) / sizeof (kstat_named_t),
99 	    KSTAT_FLAG_VIRTUAL);
100 	if (sp_g_kstat == NULL)
101 		return;
102 
103 	sp_g_kstat->ks_data = &sp_g_stats;
104 
105 	kstat_install(sp_g_kstat);
106 }
107 
108 static int
109 sockparams_kstat_update(kstat_t *ksp, int rw)
110 {
111 	struct sockparams *sp = ksp->ks_private;
112 	sockparams_stats_t *sps = ksp->ks_data;
113 
114 	if (rw == KSTAT_WRITE)
115 		return (EACCES);
116 
117 	sps->sps_nactive.value.ui64 = sp->sp_refcnt;
118 
119 	return (0);
120 }
121 
122 /*
123  * Setup kstats for the given sockparams entry.
124  */
125 static void
126 sockparams_kstat_init(struct sockparams *sp)
127 {
128 	char name[KSTAT_STRLEN];
129 
130 	(void) snprintf(name, KSTAT_STRLEN, "socket_%d_%d_%d", sp->sp_family,
131 	    sp->sp_type, sp->sp_protocol);
132 
133 	sp->sp_kstat = kstat_create("sockfs", 0, name, "misc", KSTAT_TYPE_NAMED,
134 	    sizeof (sockparams_stats_t) / sizeof (kstat_named_t),
135 	    KSTAT_FLAG_VIRTUAL);
136 
137 	if (sp->sp_kstat == NULL)
138 		return;
139 
140 	sp->sp_kstat->ks_data = &sp->sp_stats;
141 	sp->sp_kstat->ks_update = sockparams_kstat_update;
142 	sp->sp_kstat->ks_private = sp;
143 	kstat_install(sp->sp_kstat);
144 }
145 
146 static void
147 sockparams_kstat_fini(struct sockparams *sp)
148 {
149 	if (sp->sp_kstat != NULL) {
150 		kstat_delete(sp->sp_kstat);
151 		sp->sp_kstat = NULL;
152 	}
153 }
154 
155 /*
156  * sockparams_create(int family, int type, int protocol, char *modname,
157  *     char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
158  *
159  * Create a new sockparams entry.
160  *
161  * Arguments:
162  *   family, type, protocol: specifies the socket type
163  *   modname: Name of the module associated with the socket type. The
164  *            module can be NULL if a device path is given, in which
165  *            case the TPI module is used.
166  *   devpath: Path to the STREAMS device. Must be NULL for non-STREAMS
167  *            based transports.
168  *   devpathlen: Length of the devpath string. The argument can be 0,
169  *            indicating that devpath was allocated statically, and should
170  *            not be freed when the sockparams entry is destroyed.
171  *
172  *   flags  : SOCKPARAMS_EPHEMERAL is the only flag that is allowed.
173  *   kmflags: KM_{NO,}SLEEP
174  *   errorp : Value-return argument, set when an error occurs.
175  *
176  * Returns:
177  *   On success a new sockparams entry is returned, and *errorp is set
178  *   to 0. On failure NULL is returned and *errorp is set to indicate the
179  *   type of error that occured.
180  *
181  * Notes:
182  *   devpath and modname are freed upon failure.
183  */
184 struct sockparams *
185 sockparams_create(int family, int type, int protocol, char *modname,
186     char *devpath, int devpathlen, int flags, int kmflags, int *errorp)
187 {
188 	struct sockparams *sp = NULL;
189 	size_t size;
190 
191 	ASSERT((flags & ~SOCKPARAMS_EPHEMERAL) == 0);
192 	if (flags & ~SOCKPARAMS_EPHEMERAL) {
193 		*errorp = EINVAL;
194 		goto error;
195 	}
196 
197 	/* either a module or device must be given, but not both */
198 	if (modname == NULL && devpath == NULL) {
199 		*errorp = EINVAL;
200 		goto error;
201 	}
202 
203 	sp = kmem_zalloc(sizeof (*sp), kmflags);
204 	if (sp == NULL) {
205 		*errorp = ENOMEM;
206 		goto error;
207 	}
208 	sp->sp_family = family;
209 	sp->sp_type = type;
210 	sp->sp_protocol = protocol;
211 	sp->sp_refcnt = 0;
212 	sp->sp_flags = flags;
213 
214 	list_create(&sp->sp_auto_filters, sizeof (sp_filter_t),
215 	    offsetof(sp_filter_t, spf_node));
216 	list_create(&sp->sp_prog_filters, sizeof (sp_filter_t),
217 	    offsetof(sp_filter_t, spf_node));
218 
219 	kstat_named_init(&sp->sp_stats.sps_nfallback, "nfallback",
220 	    KSTAT_DATA_UINT64);
221 	kstat_named_init(&sp->sp_stats.sps_nactive, "nactive",
222 	    KSTAT_DATA_UINT64);
223 	kstat_named_init(&sp->sp_stats.sps_ncreate, "ncreate",
224 	    KSTAT_DATA_UINT64);
225 
226 	/*
227 	 * Track how many ephemeral entries we have created.
228 	 */
229 	if (sp->sp_flags & SOCKPARAMS_EPHEMERAL)
230 		sp_g_stats.spgs_ephem_nalloc.value.ui64++;
231 
232 	if (modname != NULL) {
233 		sp->sp_smod_name = modname;
234 	} else {
235 		size = strlen(SOTPI_SMOD_NAME) + 1;
236 		modname = kmem_zalloc(size, kmflags);
237 		if (modname == NULL) {
238 			*errorp = ENOMEM;
239 			goto error;
240 		}
241 		sp->sp_smod_name = modname;
242 		(void) sprintf(sp->sp_smod_name, "%s", SOTPI_SMOD_NAME);
243 	}
244 
245 	if (devpath != NULL) {
246 		/* Set up the device entry. */
247 		*errorp = sockparams_sdev_init(sp, devpath, devpathlen);
248 		if (*errorp != 0)
249 			goto error;
250 	}
251 
252 	mutex_init(&sp->sp_lock, NULL, MUTEX_DEFAULT, NULL);
253 	*errorp = 0;
254 	return (sp);
255 error:
256 	ASSERT(*errorp != 0);
257 	if (modname != NULL)
258 		kmem_free(modname, strlen(modname) + 1);
259 	if (devpathlen != 0)
260 		kmem_free(devpath, devpathlen);
261 	if (sp != NULL)
262 		kmem_free(sp, sizeof (*sp));
263 	return (NULL);
264 }
265 
266 /*
267  * Initialize the STREAMS device aspect of the sockparams entry.
268  */
269 static int
270 sockparams_sdev_init(struct sockparams *sp, char *devpath, int devpathlen)
271 {
272 	vnode_t *vp = NULL;
273 	int error;
274 
275 	ASSERT(devpath != NULL);
276 
277 	if ((error = sogetvp(devpath, &vp, UIO_SYSSPACE)) != 0) {
278 		dprint(0, ("sockparams_sdev_init: vp %s failed with %d\n",
279 		    devpath, error));
280 		return (error);
281 	}
282 
283 	ASSERT(vp != NULL);
284 	sp->sp_sdev_info.sd_vnode = vp;
285 	sp->sp_sdev_info.sd_devpath = devpath;
286 	sp->sp_sdev_info.sd_devpathlen = devpathlen;
287 
288 	return (0);
289 }
290 
291 /*
292  * sockparams_destroy(struct sockparams *sp)
293  *
294  * Releases all the resources associated with the sockparams entry,
295  * and frees the sockparams entry.
296  *
297  * Arguments:
298  *   sp: the sockparams entry to destroy.
299  *
300  * Returns:
301  *   Nothing.
302  *
303  * Locking:
304  *   The sp_lock of the entry can not be held.
305  */
306 void
307 sockparams_destroy(struct sockparams *sp)
308 {
309 	ASSERT(sp->sp_refcnt == 0);
310 	ASSERT(!list_link_active(&sp->sp_node));
311 
312 	sockparams_sdev_fini(sp);
313 
314 	if (sp->sp_smod_info != NULL)
315 		SMOD_DEC_REF(sp->sp_smod_info, sp->sp_smod_name);
316 	kmem_free(sp->sp_smod_name, strlen(sp->sp_smod_name) + 1);
317 	sp->sp_smod_name = NULL;
318 	sp->sp_smod_info = NULL;
319 	mutex_destroy(&sp->sp_lock);
320 	sockparams_kstat_fini(sp);
321 
322 	sof_sockparams_fini(sp);
323 	list_destroy(&sp->sp_auto_filters);
324 	list_destroy(&sp->sp_prog_filters);
325 
326 	kmem_free(sp, sizeof (*sp));
327 }
328 
329 /*
330  * Clean up the STREAMS device part of the sockparams entry.
331  */
332 static void
333 sockparams_sdev_fini(struct sockparams *sp)
334 {
335 	sdev_info_t sd;
336 
337 	/*
338 	 * if the entry does not have a STREAMS device, then there
339 	 * is nothing to do.
340 	 */
341 	if (!SOCKPARAMS_HAS_DEVICE(sp))
342 		return;
343 
344 	sd = sp->sp_sdev_info;
345 	if (sd.sd_vnode != NULL)
346 		VN_RELE(sd.sd_vnode);
347 	if (sd.sd_devpathlen != 0)
348 		kmem_free(sd.sd_devpath, sd.sd_devpathlen);
349 
350 	sp->sp_sdev_info.sd_vnode = NULL;
351 	sp->sp_sdev_info.sd_devpath = NULL;
352 }
353 
354 /*
355  * Look for a matching sockparams entry on the given list.
356  * The caller must hold the associated list lock.
357  */
358 static struct sockparams *
359 sockparams_find(list_t *list, int family, int type, int protocol,
360     boolean_t by_devpath, const char *name)
361 {
362 	struct sockparams *sp;
363 
364 	for (sp = list_head(list); sp != NULL; sp = list_next(list, sp)) {
365 		if (sp->sp_family == family && sp->sp_type == type) {
366 			if (sp->sp_protocol == protocol) {
367 				if (name == NULL)
368 					break;
369 				else if (by_devpath &&
370 				    sp->sp_sdev_info.sd_devpath != NULL &&
371 				    strcmp(sp->sp_sdev_info.sd_devpath,
372 				    name) == 0)
373 					break;
374 				else if (strcmp(sp->sp_smod_name, name) == 0)
375 					break;
376 			}
377 		}
378 	}
379 	return (sp);
380 }
381 
382 /*
383  * sockparams_hold_ephemeral()
384  *
385  * Returns an ephemeral sockparams entry of the requested family, type and
386  * protocol. The entry is returned held, and the caller is responsible for
387  * dropping the reference using SOCKPARAMS_DEC_REF() once done.
388  *
389  * All ephemeral entries are on list (sp_ephem_list). If there is an
390  * entry on the list that match the search criteria, then a reference is
391  * placed on that entry. Otherwise, a new entry is created and inserted
392  * in the list. The entry is removed from the list when the last reference
393  * is dropped.
394  *
395  * The tpi flag is used to determine whether name refers to a device or
396  * module name.
397  */
398 static struct sockparams *
399 sockparams_hold_ephemeral(int family, int type, int protocol,
400     const char *name, boolean_t by_devpath, int kmflag, int *errorp)
401 {
402 	struct sockparams *sp = NULL;
403 	*errorp = 0;
404 
405 	/*
406 	 * First look for an existing entry
407 	 */
408 	rw_enter(&sockconf_lock, RW_READER);
409 	sp = sockparams_find(&sp_ephem_list, family, type, protocol,
410 	    by_devpath, name);
411 	if (sp != NULL) {
412 		SOCKPARAMS_INC_REF(sp);
413 		rw_exit(&sockconf_lock);
414 		sp_g_stats.spgs_ephem_nreuse.value.ui64++;
415 
416 		return (sp);
417 	} else {
418 		struct sockparams *newsp = NULL;
419 		char *namebuf = NULL;
420 		int namelen = 0;
421 
422 		rw_exit(&sockconf_lock);
423 
424 		namelen = strlen(name) + 1;
425 		namebuf = kmem_alloc(namelen, kmflag);
426 		if (namebuf == NULL) {
427 			*errorp = ENOMEM;
428 			return (NULL);
429 		}
430 
431 		(void *)strncpy(namebuf, name, namelen);
432 		if (by_devpath) {
433 			newsp = sockparams_create(family, type,
434 			    protocol, NULL, namebuf, namelen,
435 			    SOCKPARAMS_EPHEMERAL, kmflag, errorp);
436 		} else {
437 			newsp = sockparams_create(family, type,
438 			    protocol, namebuf, NULL, 0,
439 			    SOCKPARAMS_EPHEMERAL, kmflag, errorp);
440 		}
441 
442 		if (newsp == NULL) {
443 			ASSERT(*errorp != 0);
444 			return (NULL);
445 		}
446 
447 		/*
448 		 * Time to load the socket module.
449 		 */
450 		ASSERT(newsp->sp_smod_info == NULL);
451 		newsp->sp_smod_info =
452 		    smod_lookup_byname(newsp->sp_smod_name);
453 		if (newsp->sp_smod_info == NULL) {
454 			/* Failed to load */
455 			sockparams_destroy(newsp);
456 			*errorp = ENXIO;
457 			return (NULL);
458 		}
459 
460 		/*
461 		 * The sockparams entry was created, now try to add it
462 		 * to the list. We need to hold the lock as a WRITER.
463 		 */
464 		rw_enter(&sockconf_lock, RW_WRITER);
465 		sp = sockparams_find(&sp_ephem_list, family, type, protocol,
466 		    by_devpath, name);
467 		if (sp != NULL) {
468 			/*
469 			 * Someone has requested a matching entry, so just
470 			 * place a hold on it and release the entry we alloc'ed.
471 			 */
472 			SOCKPARAMS_INC_REF(sp);
473 			rw_exit(&sockconf_lock);
474 
475 			sockparams_destroy(newsp);
476 		} else {
477 			*errorp = sof_sockparams_init(newsp);
478 			if (*errorp != 0) {
479 				rw_exit(&sockconf_lock);
480 				sockparams_destroy(newsp);
481 				return (NULL);
482 			}
483 			SOCKPARAMS_INC_REF(newsp);
484 			list_insert_tail(&sp_ephem_list, newsp);
485 			rw_exit(&sockconf_lock);
486 
487 			sp = newsp;
488 		}
489 		ASSERT(*errorp == 0);
490 
491 		return (sp);
492 	}
493 }
494 
495 struct sockparams *
496 sockparams_hold_ephemeral_bydev(int family, int type, int protocol,
497     const char *dev, int kmflag, int *errorp)
498 {
499 	return (sockparams_hold_ephemeral(family, type, protocol, dev, B_TRUE,
500 	    kmflag, errorp));
501 }
502 
503 struct sockparams *
504 sockparams_hold_ephemeral_bymod(int family, int type, int protocol,
505     const char *mod, int kmflag, int *errorp)
506 {
507 	return (sockparams_hold_ephemeral(family, type, protocol, mod, B_FALSE,
508 	    kmflag, errorp));
509 }
510 
511 /*
512  * Called when the last socket using the ephemeral entry is dropping
513  * its' reference. To maintain lock order we must drop the sockparams
514  * lock before calling this function. As a result, a new reference
515  * might be placed on the entry, in which case there is nothing to
516  * do. However, if ref count goes to zero, we delete the entry.
517  */
518 void
519 sockparams_ephemeral_drop_last_ref(struct sockparams *sp)
520 {
521 	ASSERT(sp->sp_flags & SOCKPARAMS_EPHEMERAL);
522 	ASSERT(MUTEX_NOT_HELD(&sp->sp_lock));
523 
524 	rw_enter(&sockconf_lock, RW_WRITER);
525 	mutex_enter(&sp->sp_lock);
526 
527 	if (--sp->sp_refcnt == 0) {
528 		list_remove(&sp_ephem_list, sp);
529 		mutex_exit(&sp->sp_lock);
530 		rw_exit(&sockconf_lock);
531 
532 		sockparams_destroy(sp);
533 	} else {
534 		mutex_exit(&sp->sp_lock);
535 		rw_exit(&sockconf_lock);
536 	}
537 }
538 
539 /*
540  * sockparams_add(struct sockparams *sp)
541  *
542  * Tries to add the given sockparams entry to the global list.
543  *
544  * Arguments:
545  *   sp: the sockparms entry to add
546  *
547  * Returns:
548  *   On success 0, but if an entry already exists, then EEXIST
549  *   is returned.
550  *
551  * Locking:
552  *   The caller can not be holding sockconf_lock.
553  */
554 int
555 sockparams_add(struct sockparams *sp)
556 {
557 	int error;
558 
559 	ASSERT(!(sp->sp_flags & SOCKPARAMS_EPHEMERAL));
560 
561 	rw_enter(&sockconf_lock, RW_WRITER);
562 	if (sockparams_find(&sphead, sp->sp_family, sp->sp_type,
563 	    sp->sp_protocol, B_TRUE, NULL) != 0) {
564 		rw_exit(&sockconf_lock);
565 		return (EEXIST);
566 	} else {
567 		/*
568 		 * Unique sockparams entry, so init the kstats.
569 		 */
570 		sockparams_kstat_init(sp);
571 
572 		/*
573 		 * Before making the socket type available we must make
574 		 * sure that interested socket filters are aware of it.
575 		 */
576 		error = sof_sockparams_init(sp);
577 		if (error != 0) {
578 			rw_exit(&sockconf_lock);
579 			return (error);
580 		}
581 		list_insert_tail(&sphead, sp);
582 		rw_exit(&sockconf_lock);
583 		return (0);
584 	}
585 }
586 
587 /*
588  * sockparams_delete(int family, int type, int protocol)
589  *
590  * Marks the sockparams entry for a specific family, type and protocol
591  * for deletion. The entry is removed from the list and destroyed
592  * if no one is holding a reference to it.
593  *
594  * Arguments:
595  *   family, type, protocol: the socket type that should be removed.
596  *
597  * Returns:
598  *   On success 0, otherwise ENXIO.
599  *
600  * Locking:
601  *   Caller can not be holding sockconf_lock or the sp_lock of
602  *   any sockparams entry.
603  */
604 int
605 sockparams_delete(int family, int type, int protocol)
606 {
607 	struct sockparams *sp;
608 
609 	rw_enter(&sockconf_lock, RW_WRITER);
610 	sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
611 
612 	if (sp != NULL) {
613 		/*
614 		 * If no one is holding a reference to the entry, then
615 		 * we go ahead and remove it from the list and then
616 		 * destroy it.
617 		 */
618 		mutex_enter(&sp->sp_lock);
619 		if (sp->sp_refcnt != 0) {
620 			mutex_exit(&sp->sp_lock);
621 			rw_exit(&sockconf_lock);
622 			return (EBUSY);
623 		}
624 		mutex_exit(&sp->sp_lock);
625 		/* Delete the sockparams entry. */
626 		list_remove(&sphead, sp);
627 		rw_exit(&sockconf_lock);
628 
629 		sockparams_destroy(sp);
630 		return (0);
631 	} else {
632 		rw_exit(&sockconf_lock);
633 		return (ENXIO);
634 	}
635 }
636 
637 
638 /*
639  * solookup(int family, int type, int protocol, struct sockparams **spp)
640  *
641  * Lookup an entry in the sockparams list based on the triple. The returned
642  * entry either exactly match the given tuple, or it is the 'default' entry
643  * for the given <family, type>. A default entry is on with a protocol
644  * value of zero.
645  *
646  * Arguments:
647  *   family, type, protocol: tuple to search for
648  *   spp: Value-return argument
649  *
650  * Returns:
651  *   If an entry is found, 0 is returned and *spp is set to point to the
652  *   entry. In case an entry is not found, *spp is set to NULL, and an
653  *   error code is returned. The errors are (in decreasing precedence):
654  *	EAFNOSUPPORT - address family not in list
655  *	EPROTONOSUPPORT - address family supported but not protocol.
656  *	EPROTOTYPE - address family and protocol supported but not socket type.
657  *
658  * TODO: should use ddi_modopen()/ddi_modclose()
659  */
660 int
661 solookup(int family, int type, int protocol, struct sockparams **spp)
662 {
663 	struct sockparams *sp = NULL;
664 	int error = 0;
665 
666 	*spp = NULL;
667 	rw_enter(&sockconf_lock, RW_READER);
668 
669 	/*
670 	 * Search the sockparams list for an appropiate entry.
671 	 * Hopefully we find an entry that match the exact family,
672 	 * type and protocol specified by the user, in which case
673 	 * we return that entry. However, we also keep track of
674 	 * the default entry for a specific family and type, the
675 	 * entry of which would have a protocol value of 0.
676 	 */
677 	sp = sockparams_find(&sphead, family, type, protocol, B_TRUE, NULL);
678 
679 	if (sp == NULL) {
680 		int found = 0;
681 
682 		/* Determine correct error code */
683 		for (sp = list_head(&sphead); sp != NULL;
684 		    sp = list_next(&sphead, sp)) {
685 			if (sp->sp_family == family && found < 1)
686 				found = 1;
687 			if (sp->sp_family == family &&
688 			    sp->sp_protocol == protocol && found < 2)
689 				found = 2;
690 		}
691 		rw_exit(&sockconf_lock);
692 		switch (found) {
693 		case 0:
694 			error = EAFNOSUPPORT;
695 			break;
696 		case 1:
697 			error = EPROTONOSUPPORT;
698 			break;
699 		case 2:
700 			error = EPROTOTYPE;
701 			break;
702 		}
703 		return (error);
704 	}
705 
706 	/*
707 	 * An entry was found.
708 	 *
709 	 * We put a hold on the entry early on, so if the
710 	 * sockmod is not loaded, and we have to exit
711 	 * sockconf_lock to call modload(), we know that the
712 	 * sockparams entry wont go away. That way we don't
713 	 * have to look up the entry once we come back from
714 	 * modload().
715 	 */
716 	SOCKPARAMS_INC_REF(sp);
717 	rw_exit(&sockconf_lock);
718 
719 	if (sp->sp_smod_info == NULL) {
720 		smod_info_t *smod = smod_lookup_byname(sp->sp_smod_name);
721 
722 		if (smod == NULL) {
723 			/*
724 			 * We put a hold on the sockparams entry
725 			 * earlier, hoping everything would work out.
726 			 * That obviously did not happen, so release
727 			 * the hold here.
728 			 */
729 			SOCKPARAMS_DEC_REF(sp);
730 			/*
731 			 * We should probably mark the sockparams as
732 			 * "bad", and redo the lookup skipping the
733 			 * "bad" entries. I.e., sp->sp_mod_state |= BAD,
734 			 * return (solookup(...))
735 			 */
736 			return (ENXIO);
737 		}
738 		/*
739 		 * Another thread might have already looked up the socket
740 		 * module for this entry. In that case we need to drop our
741 		 * reference to `smod' to ensure that the sockparams entry
742 		 * only holds one reference.
743 		 */
744 		mutex_enter(&sp->sp_lock);
745 		if (sp->sp_smod_info == NULL)
746 			sp->sp_smod_info = smod;
747 		else
748 			SMOD_DEC_REF(smod, sp->sp_smod_name);
749 		mutex_exit(&sp->sp_lock);
750 	}
751 
752 	/*
753 	 * Alright, we have a valid sockparams entry.
754 	 */
755 	*spp = sp;
756 	return (0);
757 }
758 
759 /*
760  * Called when filter entry `ent' is going away. All sockparams remove
761  * their references to `ent'.
762  */
763 static void
764 sockparams_filter_cleanup_impl(sof_entry_t *ent, list_t *list)
765 {
766 	struct sockparams *sp;
767 	sp_filter_t *fil;
768 	list_t *flist;
769 
770 	ASSERT(RW_WRITE_HELD(&sockconf_lock));
771 
772 	for (sp = list_head(list); sp != NULL;
773 	    sp = list_next(list, sp)) {
774 		flist = (ent->sofe_flags & SOFEF_AUTO) ?
775 		    &sp->sp_auto_filters : &sp->sp_prog_filters;
776 		fil = list_head(flist);
777 		for (fil = list_head(flist); fil != NULL;
778 		    fil = list_next(flist, fil)) {
779 			if (fil->spf_filter == ent) {
780 				list_remove(flist, fil);
781 				kmem_free(fil, sizeof (sp_filter_t));
782 				break;
783 			}
784 		}
785 	}
786 }
787 void
788 sockparams_filter_cleanup(sof_entry_t *ent)
789 {
790 	sockparams_filter_cleanup_impl(ent, &sphead);
791 	sockparams_filter_cleanup_impl(ent, &sp_ephem_list);
792 }
793 
794 /*
795  * New filter is being added; walk the list of sockparams to see if
796  * the filter is interested in any of the sockparams.
797  */
798 static int
799 sockparams_new_filter_impl(sof_entry_t *ent, list_t *list)
800 {
801 	struct sockparams *sp;
802 	int err;
803 
804 	ASSERT(RW_WRITE_HELD(&sockconf_lock));
805 
806 	for (sp = list_head(list); sp != NULL;
807 	    sp = list_next(list, sp)) {
808 		if ((err = sof_entry_proc_sockparams(ent, sp)) != 0) {
809 			sockparams_filter_cleanup(ent);
810 			return (err);
811 		}
812 	}
813 	return (0);
814 }
815 
816 int
817 sockparams_new_filter(sof_entry_t *ent)
818 {
819 	int error;
820 
821 	if ((error = sockparams_new_filter_impl(ent, &sphead)) != 0)
822 		return (error);
823 
824 	if ((error = sockparams_new_filter_impl(ent, &sp_ephem_list)) != 0)
825 		sockparams_filter_cleanup_impl(ent, &sphead);
826 	return (error);
827 }
828