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