2005-01-06 23:35:40 +00:00
|
|
|
/*-
|
1994-05-24 10:09:53 +00:00
|
|
|
* Copyright (c) 1982, 1986, 1989, 1991, 1993
|
2005-09-23 12:41:06 +00:00
|
|
|
* The Regents of the University of California.
|
2009-03-08 21:48:29 +00:00
|
|
|
* Copyright (c) 2004-2009 Robert N. M. Watson
|
2005-09-23 12:41:06 +00:00
|
|
|
* All rights reserved.
|
1994-05-24 10:09:53 +00:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
1995-05-11 00:13:26 +00:00
|
|
|
* From: @(#)uipc_usrreq.c 8.3 (Berkeley) 1/4/94
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
|
|
|
|
2006-07-23 20:06:45 +00:00
|
|
|
/*
|
|
|
|
* UNIX Domain (Local) Sockets
|
|
|
|
*
|
|
|
|
* This is an implementation of UNIX (local) domain sockets. Each socket has
|
|
|
|
* an associated struct unpcb (UNIX protocol control block). Stream sockets
|
|
|
|
* may be connected to 0 or 1 other socket. Datagram sockets may be
|
|
|
|
* connected to 0, 1, or many other sockets. Sockets may be created and
|
|
|
|
* connected in pairs (socketpair(2)), or bound/connected to using the file
|
|
|
|
* system name space. For most purposes, only the receive socket buffer is
|
|
|
|
* used, as sending on one socket delivers directly to the receive socket
|
2007-02-20 10:50:02 +00:00
|
|
|
* buffer of a second socket.
|
|
|
|
*
|
|
|
|
* The implementation is substantially complicated by the fact that
|
|
|
|
* "ancillary data", such as file descriptors or credentials, may be passed
|
|
|
|
* across UNIX domain sockets. The potential for passing UNIX domain sockets
|
|
|
|
* over other UNIX domain sockets requires the implementation of a simple
|
|
|
|
* garbage collector to find and tear down cycles of disconnected sockets.
|
2007-02-14 15:05:40 +00:00
|
|
|
*
|
|
|
|
* TODO:
|
2009-10-05 14:49:16 +00:00
|
|
|
* RDM
|
|
|
|
* distinguish datagram size limits from flow control limits in SEQPACKET
|
2007-02-14 15:05:40 +00:00
|
|
|
* rethink name space problems
|
|
|
|
* need a proper out-of-band
|
2006-07-23 20:06:45 +00:00
|
|
|
*/
|
|
|
|
|
2003-06-11 00:56:59 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
2007-05-29 12:36:00 +00:00
|
|
|
#include "opt_ddb.h"
|
2002-07-31 03:03:22 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/param.h>
|
2001-05-01 08:13:21 +00:00
|
|
|
#include <sys/domain.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/fcntl.h>
|
1997-11-23 10:43:49 +00:00
|
|
|
#include <sys/malloc.h> /* XXX must be before <sys/file.h> */
|
2006-04-21 09:25:40 +00:00
|
|
|
#include <sys/eventhandler.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/file.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/filedesc.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/lock.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/mbuf.h>
|
2006-01-30 08:19:01 +00:00
|
|
|
#include <sys/mount.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/mutex.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/namei.h>
|
|
|
|
#include <sys/proc.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/protosw.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/resourcevar.h>
|
2007-02-26 20:47:52 +00:00
|
|
|
#include <sys/rwlock.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/socketvar.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/signalvar.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/stat.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/sx.h>
|
1997-02-24 20:30:58 +00:00
|
|
|
#include <sys/sysctl.h>
|
2002-04-30 01:54:54 +00:00
|
|
|
#include <sys/systm.h>
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
#include <sys/taskqueue.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/un.h>
|
1998-05-15 20:11:40 +00:00
|
|
|
#include <sys/unpcb.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
#include <sys/vnode.h>
|
2009-08-01 19:26:27 +00:00
|
|
|
|
|
|
|
#include <net/vnet.h>
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2007-05-29 12:36:00 +00:00
|
|
|
#ifdef DDB
|
|
|
|
#include <ddb/ddb.h>
|
|
|
|
#endif
|
|
|
|
|
2006-10-22 11:52:19 +00:00
|
|
|
#include <security/mac/mac_framework.h>
|
|
|
|
|
2002-03-20 04:11:52 +00:00
|
|
|
#include <vm/uma.h>
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
/*
|
|
|
|
* Locking key:
|
|
|
|
* (l) Locked using list lock
|
|
|
|
* (g) Locked using linkage lock
|
|
|
|
*/
|
|
|
|
|
2007-02-14 15:05:40 +00:00
|
|
|
static uma_zone_t unp_zone;
|
2009-03-08 21:48:29 +00:00
|
|
|
static unp_gen_t unp_gencnt; /* (l) */
|
|
|
|
static u_int unp_count; /* (l) Count of local sockets. */
|
2007-02-14 15:05:40 +00:00
|
|
|
static ino_t unp_ino; /* Prototype for fake inode numbers. */
|
2009-03-08 21:48:29 +00:00
|
|
|
static int unp_rights; /* (g) File descriptors in flight. */
|
|
|
|
static struct unp_head unp_shead; /* (l) List of stream sockets. */
|
|
|
|
static struct unp_head unp_dhead; /* (l) List of datagram sockets. */
|
2009-10-05 14:49:16 +00:00
|
|
|
static struct unp_head unp_sphead; /* (l) List of seqpacket sockets. */
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2007-02-14 15:05:40 +00:00
|
|
|
static const struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL };
|
1998-05-15 20:11:40 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
/*
|
2007-02-14 15:05:40 +00:00
|
|
|
* Garbage collection of cyclic file descriptor/socket references occurs
|
|
|
|
* asynchronously in a taskqueue context in order to avoid recursion and
|
|
|
|
* reentrance in the UNIX domain socket, file descriptor, and socket layer
|
|
|
|
* code. See unp_gc() for a full description.
|
1994-05-24 10:09:53 +00:00
|
|
|
*/
|
2007-02-14 15:05:40 +00:00
|
|
|
static struct task unp_gc_task;
|
1995-12-14 09:55:16 +00:00
|
|
|
|
2006-07-23 10:19:04 +00:00
|
|
|
/*
|
|
|
|
* Both send and receive buffers are allocated PIPSIZ bytes of buffering for
|
|
|
|
* stream sockets, although the total for sender and receiver is actually
|
|
|
|
* only PIPSIZ.
|
|
|
|
*
|
|
|
|
* Datagram sockets really use the sendspace as the maximum datagram size,
|
|
|
|
* and don't really want to reserve the sendspace. Their recvspace should be
|
|
|
|
* large enough for at least one max-size datagram plus address.
|
|
|
|
*/
|
|
|
|
#ifndef PIPSIZ
|
|
|
|
#define PIPSIZ 8192
|
|
|
|
#endif
|
|
|
|
static u_long unpst_sendspace = PIPSIZ;
|
|
|
|
static u_long unpst_recvspace = PIPSIZ;
|
|
|
|
static u_long unpdg_sendspace = 2*1024; /* really max datagram size */
|
|
|
|
static u_long unpdg_recvspace = 4*1024;
|
2009-10-05 14:49:16 +00:00
|
|
|
static u_long unpsp_sendspace = PIPSIZ; /* really max datagram size */
|
|
|
|
static u_long unpsp_recvspace = PIPSIZ;
|
2006-07-23 10:19:04 +00:00
|
|
|
|
2006-08-07 12:02:43 +00:00
|
|
|
SYSCTL_NODE(_net, PF_LOCAL, local, CTLFLAG_RW, 0, "Local domain");
|
|
|
|
SYSCTL_NODE(_net_local, SOCK_STREAM, stream, CTLFLAG_RW, 0, "SOCK_STREAM");
|
|
|
|
SYSCTL_NODE(_net_local, SOCK_DGRAM, dgram, CTLFLAG_RW, 0, "SOCK_DGRAM");
|
2009-10-05 14:49:16 +00:00
|
|
|
SYSCTL_NODE(_net_local, SOCK_SEQPACKET, seqpacket, CTLFLAG_RW, 0,
|
|
|
|
"SOCK_SEQPACKET");
|
2006-08-07 12:02:43 +00:00
|
|
|
|
2006-07-23 10:19:04 +00:00
|
|
|
SYSCTL_ULONG(_net_local_stream, OID_AUTO, sendspace, CTLFLAG_RW,
|
2008-07-26 00:55:35 +00:00
|
|
|
&unpst_sendspace, 0, "Default stream send space.");
|
2006-07-23 10:19:04 +00:00
|
|
|
SYSCTL_ULONG(_net_local_stream, OID_AUTO, recvspace, CTLFLAG_RW,
|
2008-07-26 00:55:35 +00:00
|
|
|
&unpst_recvspace, 0, "Default stream receive space.");
|
2006-07-23 10:19:04 +00:00
|
|
|
SYSCTL_ULONG(_net_local_dgram, OID_AUTO, maxdgram, CTLFLAG_RW,
|
2008-07-26 00:55:35 +00:00
|
|
|
&unpdg_sendspace, 0, "Default datagram send space.");
|
2006-07-23 10:19:04 +00:00
|
|
|
SYSCTL_ULONG(_net_local_dgram, OID_AUTO, recvspace, CTLFLAG_RW,
|
2008-07-26 00:55:35 +00:00
|
|
|
&unpdg_recvspace, 0, "Default datagram receive space.");
|
2009-10-05 14:49:16 +00:00
|
|
|
SYSCTL_ULONG(_net_local_seqpacket, OID_AUTO, maxseqpacket, CTLFLAG_RW,
|
|
|
|
&unpsp_sendspace, 0, "Default seqpacket send space.");
|
|
|
|
SYSCTL_ULONG(_net_local_seqpacket, OID_AUTO, recvspace, CTLFLAG_RW,
|
|
|
|
&unpsp_recvspace, 0, "Default seqpacket receive space.");
|
2008-07-26 00:55:35 +00:00
|
|
|
SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0,
|
|
|
|
"File descriptors in flight.");
|
2006-07-23 10:19:04 +00:00
|
|
|
|
2007-02-26 20:47:52 +00:00
|
|
|
/*-
|
|
|
|
* Locking and synchronization:
|
|
|
|
*
|
2009-03-08 21:48:29 +00:00
|
|
|
* Three types of locks exit in the local domain socket implementation: a
|
|
|
|
* global list mutex, a global linkage rwlock, and per-unpcb mutexes. Of the
|
|
|
|
* global locks, the list lock protects the socket count, global generation
|
|
|
|
* number, and stream/datagram global lists. The linkage lock protects the
|
|
|
|
* interconnection of unpcbs, the v_socket and unp_vnode pointers, and can be
|
|
|
|
* held exclusively over the acquisition of multiple unpcb locks to prevent
|
|
|
|
* deadlock.
|
2007-02-26 20:47:52 +00:00
|
|
|
*
|
|
|
|
* UNIX domain sockets each have an unpcb hung off of their so_pcb pointer,
|
|
|
|
* allocated in pru_attach() and freed in pru_detach(). The validity of that
|
|
|
|
* pointer is an invariant, so no lock is required to dereference the so_pcb
|
|
|
|
* pointer if a valid socket reference is held by the caller. In practice,
|
|
|
|
* this is always true during operations performed on a socket. Each unpcb
|
|
|
|
* has a back-pointer to its socket, unp_socket, which will be stable under
|
|
|
|
* the same circumstances.
|
|
|
|
*
|
|
|
|
* This pointer may only be safely dereferenced as long as a valid reference
|
|
|
|
* to the unpcb is held. Typically, this reference will be from the socket,
|
|
|
|
* or from another unpcb when the referring unpcb's lock is held (in order
|
|
|
|
* that the reference not be invalidated during use). For example, to follow
|
|
|
|
* unp->unp_conn->unp_socket, you need unlock the lock on unp, not unp_conn,
|
|
|
|
* as unp_socket remains valid as long as the reference to unp_conn is valid.
|
|
|
|
*
|
|
|
|
* Fields of unpcbss are locked using a per-unpcb lock, unp_mtx. Individual
|
|
|
|
* atomic reads without the lock may be performed "lockless", but more
|
|
|
|
* complex reads and read-modify-writes require the mutex to be held. No
|
|
|
|
* lock order is defined between unpcb locks -- multiple unpcb locks may be
|
2009-03-08 21:48:29 +00:00
|
|
|
* acquired at the same time only when holding the linkage rwlock
|
|
|
|
* exclusively, which prevents deadlocks.
|
2004-08-16 01:52:04 +00:00
|
|
|
*
|
2007-02-26 20:47:52 +00:00
|
|
|
* Blocking with UNIX domain sockets is a tricky issue: unlike most network
|
|
|
|
* protocols, bind() is a non-atomic operation, and connect() requires
|
|
|
|
* potential sleeping in the protocol, due to potentially waiting on local or
|
|
|
|
* distributed file systems. We try to separate "lookup" operations, which
|
|
|
|
* may sleep, and the IPC operations themselves, which typically can occur
|
|
|
|
* with relative atomicity as locks can be held over the entire operation.
|
2004-08-16 01:52:04 +00:00
|
|
|
*
|
2007-02-26 20:47:52 +00:00
|
|
|
* Another tricky issue is simultaneous multi-threaded or multi-process
|
|
|
|
* access to a single UNIX domain socket. These are handled by the flags
|
|
|
|
* UNP_CONNECTING and UNP_BINDING, which prevent concurrent connecting or
|
|
|
|
* binding, both of which involve dropping UNIX domain socket locks in order
|
|
|
|
* to perform namei() and other file system operations.
|
2004-08-16 01:52:04 +00:00
|
|
|
*/
|
2009-03-08 21:48:29 +00:00
|
|
|
static struct rwlock unp_link_rwlock;
|
|
|
|
static struct mtx unp_list_lock;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
#define UNP_LINK_LOCK_INIT() rw_init(&unp_link_rwlock, \
|
|
|
|
"unp_link_rwlock")
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
#define UNP_LINK_LOCK_ASSERT() rw_assert(&unp_link_rwlock, \
|
2007-02-26 20:47:52 +00:00
|
|
|
RA_LOCKED)
|
2009-03-08 21:48:29 +00:00
|
|
|
#define UNP_LINK_UNLOCK_ASSERT() rw_assert(&unp_link_rwlock, \
|
2007-02-26 20:47:52 +00:00
|
|
|
RA_UNLOCKED)
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
#define UNP_LINK_RLOCK() rw_rlock(&unp_link_rwlock)
|
|
|
|
#define UNP_LINK_RUNLOCK() rw_runlock(&unp_link_rwlock)
|
|
|
|
#define UNP_LINK_WLOCK() rw_wlock(&unp_link_rwlock)
|
|
|
|
#define UNP_LINK_WUNLOCK() rw_wunlock(&unp_link_rwlock)
|
|
|
|
#define UNP_LINK_WLOCK_ASSERT() rw_assert(&unp_link_rwlock, \
|
2007-02-26 20:47:52 +00:00
|
|
|
RA_WLOCKED)
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
#define UNP_LIST_LOCK_INIT() mtx_init(&unp_list_lock, \
|
|
|
|
"unp_list_lock", NULL, MTX_DEF)
|
|
|
|
#define UNP_LIST_LOCK() mtx_lock(&unp_list_lock)
|
|
|
|
#define UNP_LIST_UNLOCK() mtx_unlock(&unp_list_lock)
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
#define UNP_PCB_LOCK_INIT(unp) mtx_init(&(unp)->unp_mtx, \
|
|
|
|
"unp_mtx", "unp_mtx", \
|
|
|
|
MTX_DUPOK|MTX_DEF|MTX_RECURSE)
|
|
|
|
#define UNP_PCB_LOCK_DESTROY(unp) mtx_destroy(&(unp)->unp_mtx)
|
|
|
|
#define UNP_PCB_LOCK(unp) mtx_lock(&(unp)->unp_mtx)
|
|
|
|
#define UNP_PCB_UNLOCK(unp) mtx_unlock(&(unp)->unp_mtx)
|
|
|
|
#define UNP_PCB_LOCK_ASSERT(unp) mtx_assert(&(unp)->unp_mtx, MA_OWNED)
|
|
|
|
|
2008-10-06 18:43:11 +00:00
|
|
|
static int uipc_connect2(struct socket *, struct socket *);
|
2008-10-03 13:01:56 +00:00
|
|
|
static int uipc_ctloutput(struct socket *, struct sockopt *);
|
2007-02-26 20:47:52 +00:00
|
|
|
static int unp_connect(struct socket *, struct sockaddr *,
|
2007-02-14 15:05:40 +00:00
|
|
|
struct thread *);
|
2007-02-26 20:47:52 +00:00
|
|
|
static int unp_connect2(struct socket *so, struct socket *so2, int);
|
|
|
|
static void unp_disconnect(struct unpcb *unp, struct unpcb *unp2);
|
2008-10-03 13:01:56 +00:00
|
|
|
static void unp_dispose(struct mbuf *);
|
2007-02-26 20:47:52 +00:00
|
|
|
static void unp_shutdown(struct unpcb *);
|
|
|
|
static void unp_drop(struct unpcb *, int);
|
|
|
|
static void unp_gc(__unused void *, int);
|
|
|
|
static void unp_scan(struct mbuf *, void (*)(struct file *));
|
|
|
|
static void unp_discard(struct file *);
|
|
|
|
static void unp_freerights(struct file **, int);
|
2008-10-03 13:01:56 +00:00
|
|
|
static void unp_init(void);
|
2007-02-26 20:47:52 +00:00
|
|
|
static int unp_internalize(struct mbuf **, struct thread *);
|
2007-12-30 01:42:15 +00:00
|
|
|
static void unp_internalize_fp(struct file *);
|
2008-10-03 13:01:56 +00:00
|
|
|
static int unp_externalize(struct mbuf *, struct mbuf **);
|
2007-12-30 01:42:15 +00:00
|
|
|
static void unp_externalize_fp(struct file *);
|
2007-02-20 10:50:02 +00:00
|
|
|
static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *);
|
1995-12-14 09:55:16 +00:00
|
|
|
|
2006-08-07 12:02:43 +00:00
|
|
|
/*
|
|
|
|
* Definitions of protocols supported in the LOCAL domain.
|
|
|
|
*/
|
|
|
|
static struct domain localdomain;
|
2008-10-08 06:19:49 +00:00
|
|
|
static struct pr_usrreqs uipc_usrreqs_dgram, uipc_usrreqs_stream;
|
2009-10-05 14:49:16 +00:00
|
|
|
static struct pr_usrreqs uipc_usrreqs_seqpacket;
|
2006-08-07 12:02:43 +00:00
|
|
|
static struct protosw localsw[] = {
|
|
|
|
{
|
|
|
|
.pr_type = SOCK_STREAM,
|
|
|
|
.pr_domain = &localdomain,
|
|
|
|
.pr_flags = PR_CONNREQUIRED|PR_WANTRCVD|PR_RIGHTS,
|
|
|
|
.pr_ctloutput = &uipc_ctloutput,
|
2008-10-08 06:19:49 +00:00
|
|
|
.pr_usrreqs = &uipc_usrreqs_stream
|
2006-08-07 12:02:43 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.pr_type = SOCK_DGRAM,
|
|
|
|
.pr_domain = &localdomain,
|
|
|
|
.pr_flags = PR_ATOMIC|PR_ADDR|PR_RIGHTS,
|
2008-10-08 06:19:49 +00:00
|
|
|
.pr_usrreqs = &uipc_usrreqs_dgram
|
2006-08-07 12:02:43 +00:00
|
|
|
},
|
2009-10-05 14:49:16 +00:00
|
|
|
{
|
|
|
|
.pr_type = SOCK_SEQPACKET,
|
|
|
|
.pr_domain = &localdomain,
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXXRW: For now, PR_ADDR because soreceive will bump into them
|
|
|
|
* due to our use of sbappendaddr. A new sbappend variants is needed
|
|
|
|
* that supports both atomic record writes and control data.
|
|
|
|
*/
|
|
|
|
.pr_flags = PR_ADDR|PR_ATOMIC|PR_CONNREQUIRED|PR_WANTRCVD|
|
|
|
|
PR_RIGHTS,
|
|
|
|
.pr_usrreqs = &uipc_usrreqs_seqpacket,
|
|
|
|
},
|
2006-08-07 12:02:43 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static struct domain localdomain = {
|
|
|
|
.dom_family = AF_LOCAL,
|
|
|
|
.dom_name = "local",
|
|
|
|
.dom_init = unp_init,
|
|
|
|
.dom_externalize = unp_externalize,
|
|
|
|
.dom_dispose = unp_dispose,
|
|
|
|
.dom_protosw = localsw,
|
|
|
|
.dom_protoswNPROTOSW = &localsw[sizeof(localsw)/sizeof(localsw[0])]
|
|
|
|
};
|
|
|
|
DOMAIN_SET(local);
|
|
|
|
|
2006-04-01 15:15:05 +00:00
|
|
|
static void
|
1997-04-27 20:01:29 +00:00
|
|
|
uipc_abort(struct socket *so)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_abort: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 != NULL) {
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
unp_drop(unp2, ECONNABORTED);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
}
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_accept(struct socket *so, struct sockaddr **nam)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
const struct sockaddr *sa;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* Pass back name of connected socket, if it was bound and we are
|
|
|
|
* still connected (our peer may have closed already!).
|
1997-04-27 20:01:29 +00:00
|
|
|
*/
|
2006-03-17 13:52:57 +00:00
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("uipc_accept: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
*nam = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_RLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 != NULL && unp2->unp_addr != NULL) {
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
sa = (struct sockaddr *) unp2->unp_addr;
|
|
|
|
bcopy(sa, *nam, sa->sa_len);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
} else {
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
sa = &sun_noname;
|
2007-02-26 20:47:52 +00:00
|
|
|
bcopy(sa, *nam, sa->sa_len);
|
|
|
|
}
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_RUNLOCK();
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
2001-09-12 08:38:13 +00:00
|
|
|
uipc_attach(struct socket *so, int proto, struct thread *td)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
u_long sendspace, recvspace;
|
2006-07-23 10:25:28 +00:00
|
|
|
struct unpcb *unp;
|
2009-03-08 21:48:29 +00:00
|
|
|
int error;
|
2006-07-23 10:25:28 +00:00
|
|
|
|
|
|
|
KASSERT(so->so_pcb == NULL, ("uipc_attach: so_pcb != NULL"));
|
|
|
|
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
|
|
|
|
switch (so->so_type) {
|
|
|
|
case SOCK_STREAM:
|
2007-02-26 20:47:52 +00:00
|
|
|
sendspace = unpst_sendspace;
|
|
|
|
recvspace = unpst_recvspace;
|
2006-07-23 10:25:28 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_DGRAM:
|
2007-02-26 20:47:52 +00:00
|
|
|
sendspace = unpdg_sendspace;
|
|
|
|
recvspace = unpdg_recvspace;
|
2006-07-23 10:25:28 +00:00
|
|
|
break;
|
|
|
|
|
2009-10-05 14:49:16 +00:00
|
|
|
case SOCK_SEQPACKET:
|
|
|
|
sendspace = unpsp_sendspace;
|
|
|
|
recvspace = unpsp_recvspace;
|
|
|
|
break;
|
|
|
|
|
2006-07-23 10:25:28 +00:00
|
|
|
default:
|
2007-02-26 20:47:52 +00:00
|
|
|
panic("uipc_attach");
|
2006-07-23 10:25:28 +00:00
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
error = soreserve(so, sendspace, recvspace);
|
2006-07-23 10:25:28 +00:00
|
|
|
if (error)
|
|
|
|
return (error);
|
|
|
|
}
|
2007-02-14 12:22:11 +00:00
|
|
|
unp = uma_zalloc(unp_zone, M_NOWAIT | M_ZERO);
|
2006-07-23 10:25:28 +00:00
|
|
|
if (unp == NULL)
|
|
|
|
return (ENOBUFS);
|
|
|
|
LIST_INIT(&unp->unp_refs);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK_INIT(unp);
|
2006-07-23 10:25:28 +00:00
|
|
|
unp->unp_socket = so;
|
|
|
|
so->so_pcb = unp;
|
2007-01-05 19:59:46 +00:00
|
|
|
unp->unp_refcount = 1;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_LOCK();
|
2006-07-23 10:25:28 +00:00
|
|
|
unp->unp_gencnt = ++unp_gencnt;
|
|
|
|
unp_count++;
|
2009-10-05 14:49:16 +00:00
|
|
|
switch (so->so_type) {
|
|
|
|
case SOCK_STREAM:
|
|
|
|
LIST_INSERT_HEAD(&unp_shead, unp, unp_link);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_DGRAM:
|
|
|
|
LIST_INSERT_HEAD(&unp_dhead, unp, unp_link);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_SEQPACKET:
|
|
|
|
LIST_INSERT_HEAD(&unp_sphead, unp, unp_link);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("uipc_attach");
|
|
|
|
}
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_UNLOCK();
|
2006-07-23 10:25:28 +00:00
|
|
|
|
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
2001-09-12 08:38:13 +00:00
|
|
|
uipc_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2006-07-23 11:02:12 +00:00
|
|
|
struct sockaddr_un *soun = (struct sockaddr_un *)nam;
|
|
|
|
struct vattr vattr;
|
2007-05-06 12:00:38 +00:00
|
|
|
int error, namelen, vfslocked;
|
2006-07-23 11:02:12 +00:00
|
|
|
struct nameidata nd;
|
2004-08-16 04:41:03 +00:00
|
|
|
struct unpcb *unp;
|
2006-07-23 11:02:12 +00:00
|
|
|
struct vnode *vp;
|
|
|
|
struct mount *mp;
|
|
|
|
char *buf;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_bind: unp == NULL"));
|
2006-07-23 12:01:14 +00:00
|
|
|
|
|
|
|
namelen = soun->sun_len - offsetof(struct sockaddr_un, sun_path);
|
|
|
|
if (namelen <= 0)
|
|
|
|
return (EINVAL);
|
2006-07-23 11:02:12 +00:00
|
|
|
|
|
|
|
/*
|
2006-07-23 12:01:14 +00:00
|
|
|
* We don't allow simultaneous bind() calls on a single UNIX domain
|
|
|
|
* socket, so flag in-progress operations, and return an error if an
|
|
|
|
* operation is already in progress.
|
|
|
|
*
|
|
|
|
* Historically, we have not allowed a socket to be rebound, so this
|
2007-05-11 12:10:45 +00:00
|
|
|
* also returns an error. Not allowing re-binding simplifies the
|
|
|
|
* implementation and avoids a great many possible failure modes.
|
2006-07-23 11:02:12 +00:00
|
|
|
*/
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2006-07-23 11:02:12 +00:00
|
|
|
if (unp->unp_vnode != NULL) {
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2006-07-23 11:02:12 +00:00
|
|
|
return (EINVAL);
|
|
|
|
}
|
2006-07-23 12:01:14 +00:00
|
|
|
if (unp->unp_flags & UNP_BINDING) {
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2006-07-23 12:01:14 +00:00
|
|
|
return (EALREADY);
|
2006-07-23 11:02:12 +00:00
|
|
|
}
|
2006-07-23 12:01:14 +00:00
|
|
|
unp->unp_flags |= UNP_BINDING;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2006-07-23 11:02:12 +00:00
|
|
|
|
|
|
|
buf = malloc(namelen + 1, M_TEMP, M_WAITOK);
|
2008-07-03 23:26:10 +00:00
|
|
|
bcopy(soun->sun_path, buf, namelen);
|
|
|
|
buf[namelen] = 0;
|
2006-07-23 11:02:12 +00:00
|
|
|
|
|
|
|
restart:
|
2007-05-06 12:00:38 +00:00
|
|
|
vfslocked = 0;
|
|
|
|
NDINIT(&nd, CREATE, MPSAFE | NOFOLLOW | LOCKPARENT | SAVENAME,
|
|
|
|
UIO_SYSSPACE, buf, td);
|
2006-07-23 11:02:12 +00:00
|
|
|
/* SHOULD BE ABLE TO ADOPT EXISTING AND wakeup() ALA FIFO's */
|
|
|
|
error = namei(&nd);
|
|
|
|
if (error)
|
2006-07-23 12:01:14 +00:00
|
|
|
goto error;
|
2006-07-23 11:02:12 +00:00
|
|
|
vp = nd.ni_vp;
|
2007-05-06 12:00:38 +00:00
|
|
|
vfslocked = NDHASGIANT(&nd);
|
2006-07-23 11:02:12 +00:00
|
|
|
if (vp != NULL || vn_start_write(nd.ni_dvp, &mp, V_NOWAIT) != 0) {
|
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
|
|
|
if (nd.ni_dvp == vp)
|
|
|
|
vrele(nd.ni_dvp);
|
|
|
|
else
|
|
|
|
vput(nd.ni_dvp);
|
|
|
|
if (vp != NULL) {
|
|
|
|
vrele(vp);
|
|
|
|
error = EADDRINUSE;
|
2006-07-23 12:01:14 +00:00
|
|
|
goto error;
|
2006-07-23 11:02:12 +00:00
|
|
|
}
|
|
|
|
error = vn_start_write(NULL, &mp, V_XSLEEP | PCATCH);
|
|
|
|
if (error)
|
2006-07-23 12:01:14 +00:00
|
|
|
goto error;
|
2007-05-06 12:00:38 +00:00
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
2006-07-23 11:02:12 +00:00
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
VATTR_NULL(&vattr);
|
|
|
|
vattr.va_type = VSOCK;
|
|
|
|
vattr.va_mode = (ACCESSPERMS & ~td->td_proc->p_fd->fd_cmask);
|
|
|
|
#ifdef MAC
|
2007-10-24 19:04:04 +00:00
|
|
|
error = mac_vnode_check_create(td->td_ucred, nd.ni_dvp, &nd.ni_cnd,
|
2006-07-23 11:02:12 +00:00
|
|
|
&vattr);
|
|
|
|
#endif
|
2009-04-10 10:52:19 +00:00
|
|
|
if (error == 0)
|
2006-07-23 11:02:12 +00:00
|
|
|
error = VOP_CREATE(nd.ni_dvp, &nd.ni_vp, &nd.ni_cnd, &vattr);
|
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
|
|
|
vput(nd.ni_dvp);
|
|
|
|
if (error) {
|
|
|
|
vn_finished_write(mp);
|
2006-07-23 12:01:14 +00:00
|
|
|
goto error;
|
2006-07-23 11:02:12 +00:00
|
|
|
}
|
|
|
|
vp = nd.ni_vp;
|
2007-07-26 16:58:09 +00:00
|
|
|
ASSERT_VOP_ELOCKED(vp, "uipc_bind");
|
2006-07-23 11:02:12 +00:00
|
|
|
soun = (struct sockaddr_un *)sodupsockaddr(nam, M_WAITOK);
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2006-07-23 11:02:12 +00:00
|
|
|
vp->v_socket = unp->unp_socket;
|
|
|
|
unp->unp_vnode = vp;
|
|
|
|
unp->unp_addr = soun;
|
2006-07-23 12:01:14 +00:00
|
|
|
unp->unp_flags &= ~UNP_BINDING;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2008-01-13 14:44:15 +00:00
|
|
|
VOP_UNLOCK(vp, 0);
|
2006-07-23 11:02:12 +00:00
|
|
|
vn_finished_write(mp);
|
2007-05-06 12:00:38 +00:00
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
2006-07-23 12:01:14 +00:00
|
|
|
free(buf, M_TEMP);
|
|
|
|
return (0);
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2006-07-23 12:01:14 +00:00
|
|
|
error:
|
2007-05-06 12:00:38 +00:00
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2006-07-23 12:01:14 +00:00
|
|
|
unp->unp_flags &= ~UNP_BINDING;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2006-07-23 11:02:12 +00:00
|
|
|
free(buf, M_TEMP);
|
2004-08-16 04:41:03 +00:00
|
|
|
return (error);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
2001-09-12 08:38:13 +00:00
|
|
|
uipc_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
int error;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-07-25 23:30:43 +00:00
|
|
|
KASSERT(td == curthread, ("uipc_connect: td != curthread"));
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2004-07-25 23:30:43 +00:00
|
|
|
error = unp_connect(so, nam, td);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
return (error);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2006-07-21 17:11:15 +00:00
|
|
|
static void
|
|
|
|
uipc_close(struct socket *so)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
2006-07-21 17:11:15 +00:00
|
|
|
|
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("uipc_close: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 != NULL) {
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
unp_disconnect(unp, unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
}
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2006-07-21 17:11:15 +00:00
|
|
|
}
|
|
|
|
|
2008-10-06 18:43:11 +00:00
|
|
|
static int
|
1997-04-27 20:01:29 +00:00
|
|
|
uipc_connect2(struct socket *so1, struct socket *so2)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
int error;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
unp = so1->so_pcb;
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_connect2: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
unp2 = so2->so_pcb;
|
|
|
|
KASSERT(unp2 != NULL, ("uipc_connect2: unp2 == NULL"));
|
|
|
|
UNP_PCB_LOCK(unp2);
|
2005-04-13 00:01:46 +00:00
|
|
|
error = unp_connect2(so1, so2, PRU_CONNECT2);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
return (error);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
Chance protocol switch method pru_detach() so that it returns void
rather than an error. Detaches do not "fail", they other occur or
the protocol flags SS_PROTOREF to take ownership of the socket.
soclose() no longer looks at so_pcb to see if it's NULL, relying
entirely on the protocol to decide whether it's time to free the
socket or not using SS_PROTOREF. so_pcb is now entirely owned and
managed by the protocol code. Likewise, no longer test so_pcb in
other socket functions, such as soreceive(), which have no business
digging into protocol internals.
Protocol detach routines no longer try to free the socket on detach,
this is performed in the socket code if the protocol permits it.
In rts_detach(), no longer test for rp != NULL in detach, and
likewise in other protocols that don't permit a NULL so_pcb, reduce
the incidence of testing for it during detach.
netinet and netinet6 are not fully updated to this change, which
will be in an upcoming commit. In their current state they may leak
memory or panic.
MFC after: 3 months
2006-04-01 15:42:02 +00:00
|
|
|
static void
|
1997-04-27 20:01:29 +00:00
|
|
|
uipc_detach(struct socket *so)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
2007-01-05 19:59:46 +00:00
|
|
|
struct sockaddr_un *saved_unp_addr;
|
2006-07-23 10:25:28 +00:00
|
|
|
struct vnode *vp;
|
2007-01-05 19:59:46 +00:00
|
|
|
int freeunp, local_unp_rights;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_detach: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
|
|
|
UNP_LIST_LOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2006-07-23 10:25:28 +00:00
|
|
|
LIST_REMOVE(unp, unp_link);
|
|
|
|
unp->unp_gencnt = ++unp_gencnt;
|
|
|
|
--unp_count;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_UNLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XXXRW: Should assert vp->v_socket == so.
|
|
|
|
*/
|
2006-07-23 10:25:28 +00:00
|
|
|
if ((vp = unp->unp_vnode) != NULL) {
|
|
|
|
unp->unp_vnode->v_socket = NULL;
|
|
|
|
unp->unp_vnode = NULL;
|
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 != NULL) {
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
unp_disconnect(unp, unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2009-03-08 21:48:29 +00:00
|
|
|
* We hold the linkage lock exclusively, so it's OK to acquire
|
2008-10-03 09:01:55 +00:00
|
|
|
* multiple pcb locks at a time.
|
2007-02-26 20:47:52 +00:00
|
|
|
*/
|
2006-07-23 10:25:28 +00:00
|
|
|
while (!LIST_EMPTY(&unp->unp_refs)) {
|
|
|
|
struct unpcb *ref = LIST_FIRST(&unp->unp_refs);
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
UNP_PCB_LOCK(ref);
|
2006-07-23 10:25:28 +00:00
|
|
|
unp_drop(ref, ECONNRESET);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(ref);
|
2006-07-23 10:25:28 +00:00
|
|
|
}
|
2007-12-30 01:42:15 +00:00
|
|
|
local_unp_rights = unp_rights;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2006-07-23 10:25:28 +00:00
|
|
|
unp->unp_socket->so_pcb = NULL;
|
2007-01-05 19:59:46 +00:00
|
|
|
saved_unp_addr = unp->unp_addr;
|
|
|
|
unp->unp_addr = NULL;
|
|
|
|
unp->unp_refcount--;
|
|
|
|
freeunp = (unp->unp_refcount == 0);
|
|
|
|
if (saved_unp_addr != NULL)
|
2008-10-23 15:53:51 +00:00
|
|
|
free(saved_unp_addr, M_SONAME);
|
2007-02-26 20:47:52 +00:00
|
|
|
if (freeunp) {
|
|
|
|
UNP_PCB_LOCK_DESTROY(unp);
|
2007-01-05 19:59:46 +00:00
|
|
|
uma_zfree(unp_zone, unp);
|
2007-03-12 14:52:00 +00:00
|
|
|
} else
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2006-07-23 10:25:28 +00:00
|
|
|
if (vp) {
|
|
|
|
int vfslocked;
|
|
|
|
|
|
|
|
vfslocked = VFS_LOCK_GIANT(vp->v_mount);
|
|
|
|
vrele(vp);
|
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
|
|
|
}
|
|
|
|
if (local_unp_rights)
|
|
|
|
taskqueue_enqueue(taskqueue_thread, &unp_gc_task);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_disconnect(struct socket *so)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_disconnect: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 != NULL) {
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
unp_disconnect(unp, unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
}
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2005-10-30 19:44:40 +00:00
|
|
|
uipc_listen(struct socket *so, int backlog, struct thread *td)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2004-08-16 04:41:03 +00:00
|
|
|
struct unpcb *unp;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
int error;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_listen: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
UNP_PCB_LOCK(unp);
|
2006-03-17 13:52:57 +00:00
|
|
|
if (unp->unp_vnode == NULL) {
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2004-08-16 04:41:03 +00:00
|
|
|
return (EINVAL);
|
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
SOCK_LOCK(so);
|
|
|
|
error = solisten_proto_check(so);
|
|
|
|
if (error == 0) {
|
|
|
|
cru2x(td->td_ucred, &unp->unp_peercred);
|
|
|
|
unp->unp_flags |= UNP_HAVEPCCACHED;
|
|
|
|
solisten_proto(so, backlog);
|
|
|
|
}
|
|
|
|
SOCK_UNLOCK(so);
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
return (error);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_peeraddr(struct socket *so, struct sockaddr **nam)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
const struct sockaddr *sa;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2006-03-17 13:52:57 +00:00
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("uipc_peeraddr: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
*nam = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
|
2009-06-18 20:56:22 +00:00
|
|
|
UNP_LINK_RLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
/*
|
|
|
|
* XXX: It seems that this test always fails even when connection is
|
|
|
|
* established. So, this else clause is added as workaround to
|
|
|
|
* return PF_LOCAL sockaddr.
|
|
|
|
*/
|
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 != NULL) {
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
if (unp2->unp_addr != NULL)
|
2009-06-18 20:56:22 +00:00
|
|
|
sa = (struct sockaddr *) unp2->unp_addr;
|
2007-02-26 20:47:52 +00:00
|
|
|
else
|
|
|
|
sa = &sun_noname;
|
|
|
|
bcopy(sa, *nam, sa->sa_len);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
} else {
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
sa = &sun_noname;
|
2007-02-26 20:47:52 +00:00
|
|
|
bcopy(sa, *nam, sa->sa_len);
|
2003-01-22 18:03:06 +00:00
|
|
|
}
|
2009-06-18 20:56:22 +00:00
|
|
|
UNP_LINK_RUNLOCK();
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_rcvd(struct socket *so, int flags)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
1997-04-27 20:01:29 +00:00
|
|
|
struct socket *so2;
|
2006-07-11 21:49:54 +00:00
|
|
|
u_int mbcnt, sbcc;
|
2000-08-29 11:28:06 +00:00
|
|
|
u_long newhiwat;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_rcvd: unp == NULL"));
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2009-10-05 14:49:16 +00:00
|
|
|
if (so->so_type != SOCK_STREAM && so->so_type != SOCK_SEQPACKET)
|
|
|
|
panic("uipc_rcvd socktype %d", so->so_type);
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Adjust backpressure on sender and wakeup any waiting to write.
|
|
|
|
*
|
2007-05-11 12:10:45 +00:00
|
|
|
* The unp lock is acquired to maintain the validity of the unp_conn
|
|
|
|
* pointer; no lock on unp2 is required as unp2->unp_socket will be
|
|
|
|
* static as long as we don't permit unp2 to disconnect from unp,
|
|
|
|
* which is prevented by the lock on unp. We cache values from
|
|
|
|
* so_rcv to avoid holding the so_rcv lock over the entire
|
|
|
|
* transaction on the remote so_snd.
|
2007-02-26 20:47:52 +00:00
|
|
|
*/
|
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
|
|
|
mbcnt = so->so_rcv.sb_mbcnt;
|
|
|
|
sbcc = so->so_rcv.sb_cc;
|
|
|
|
SOCKBUF_UNLOCK(&so->so_rcv);
|
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 == NULL) {
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
so2 = unp2->unp_socket;
|
|
|
|
SOCKBUF_LOCK(&so2->so_snd);
|
|
|
|
so2->so_snd.sb_mbmax += unp->unp_mbcnt - mbcnt;
|
|
|
|
newhiwat = so2->so_snd.sb_hiwat + unp->unp_cc - sbcc;
|
|
|
|
(void)chgsbsize(so2->so_cred->cr_uidinfo, &so2->so_snd.sb_hiwat,
|
|
|
|
newhiwat, RLIM_INFINITY);
|
|
|
|
sowwakeup_locked(so2);
|
|
|
|
unp->unp_mbcnt = mbcnt;
|
|
|
|
unp->unp_cc = sbcc;
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam,
|
2005-02-20 23:22:13 +00:00
|
|
|
struct mbuf *control, struct thread *td)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2006-07-22 18:41:42 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
1997-04-27 20:01:29 +00:00
|
|
|
struct socket *so2;
|
2008-12-30 16:09:57 +00:00
|
|
|
u_int mbcnt_delta, sbcc;
|
2000-08-29 11:28:06 +00:00
|
|
|
u_long newhiwat;
|
2006-07-22 18:41:42 +00:00
|
|
|
int error = 0;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_send: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
if (flags & PRUS_OOB) {
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
goto release;
|
|
|
|
}
|
2004-03-30 02:16:25 +00:00
|
|
|
if (control != NULL && (error = unp_internalize(&control, td)))
|
1997-04-27 20:01:29 +00:00
|
|
|
goto release;
|
2007-02-26 20:47:52 +00:00
|
|
|
if ((nam != NULL) || (flags & PRUS_EOF))
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
else
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_RLOCK();
|
1997-04-27 20:01:29 +00:00
|
|
|
switch (so->so_type) {
|
2004-01-11 19:48:19 +00:00
|
|
|
case SOCK_DGRAM:
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2004-06-04 04:07:08 +00:00
|
|
|
const struct sockaddr *from;
|
1995-05-30 08:16:23 +00:00
|
|
|
|
2007-02-26 20:47:52 +00:00
|
|
|
unp2 = unp->unp_conn;
|
2004-03-30 02:16:25 +00:00
|
|
|
if (nam != NULL) {
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2007-02-26 20:47:52 +00:00
|
|
|
if (unp2 != NULL) {
|
1997-04-27 20:01:29 +00:00
|
|
|
error = EISCONN;
|
|
|
|
break;
|
|
|
|
}
|
2001-09-12 08:38:13 +00:00
|
|
|
error = unp_connect(so, nam, td);
|
1997-04-27 20:01:29 +00:00
|
|
|
if (error)
|
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
unp2 = unp->unp_conn;
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2006-07-31 23:00:05 +00:00
|
|
|
/*
|
|
|
|
* Because connect() and send() are non-atomic in a sendto()
|
|
|
|
* with a target address, it's possible that the socket will
|
|
|
|
* have disconnected before the send() can run. In that case
|
|
|
|
* return the slightly counter-intuitive but otherwise
|
|
|
|
* correct error that the socket is not connected.
|
|
|
|
*/
|
|
|
|
if (unp2 == NULL) {
|
|
|
|
error = ENOTCONN;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-01 09:00:42 +00:00
|
|
|
/* Lockless read. */
|
|
|
|
if (unp2->unp_flags & UNP_WANTCRED)
|
|
|
|
control = unp_addsockcred(td, control);
|
|
|
|
UNP_PCB_LOCK(unp);
|
2004-03-30 02:16:25 +00:00
|
|
|
if (unp->unp_addr != NULL)
|
1997-08-16 19:16:27 +00:00
|
|
|
from = (struct sockaddr *)unp->unp_addr;
|
1997-04-27 20:01:29 +00:00
|
|
|
else
|
|
|
|
from = &sun_noname;
|
2007-03-01 09:00:42 +00:00
|
|
|
so2 = unp2->unp_socket;
|
Merge next step in socket buffer locking:
- sowakeup() now asserts the socket buffer lock on entry. Move
the call to KNOTE higher in sowakeup() so that it is made with
the socket buffer lock held for consistency with other calls.
Release the socket buffer lock prior to calling into pgsigio(),
so_upcall(), or aio_swake(). Locking for this event management
will need revisiting in the future, but this model avoids lock
order reversals when upcalls into other subsystems result in
socket/socket buffer operations. Assert that the socket buffer
lock is not held at the end of the function.
- Wrapper macros for sowakeup(), sorwakeup() and sowwakeup(), now
have _locked versions which assert the socket buffer lock on
entry. If a wakeup is required by sb_notify(), invoke
sowakeup(); otherwise, unconditionally release the socket buffer
lock. This results in the socket buffer lock being released
whether a wakeup is required or not.
- Break out socantsendmore() into socantsendmore_locked() that
asserts the socket buffer lock. socantsendmore()
unconditionally locks the socket buffer before calling
socantsendmore_locked(). Note that both functions return with
the socket buffer unlocked as socantsendmore_locked() calls
sowwakeup_locked() which has the same properties. Assert that
the socket buffer is unlocked on return.
- Break out socantrcvmore() into socantrcvmore_locked() that
asserts the socket buffer lock. socantrcvmore() unconditionally
locks the socket buffer before calling socantrcvmore_locked().
Note that both functions return with the socket buffer unlocked
as socantrcvmore_locked() calls sorwakeup_locked() which has
similar properties. Assert that the socket buffer is unlocked
on return.
- Break out sbrelease() into a sbrelease_locked() that asserts the
socket buffer lock. sbrelease() unconditionally locks the
socket buffer before calling sbrelease_locked().
sbrelease_locked() now invokes sbflush_locked() instead of
sbflush().
- Assert the socket buffer lock in socket buffer sanity check
functions sblastrecordchk(), sblastmbufchk().
- Assert the socket buffer lock in SBLINKRECORD().
- Break out various sbappend() functions into sbappend_locked()
(and variations on that name) that assert the socket buffer
lock. The !_locked() variations unconditionally lock the socket
buffer before calling their _locked counterparts. Internally,
make sure to call _locked() support routines, etc, if already
holding the socket buffer lock.
- Break out sbinsertoob() into sbinsertoob_locked() that asserts
the socket buffer lock. sbinsertoob() unconditionally locks the
socket buffer before calling sbinsertoob_locked().
- Break out sbflush() into sbflush_locked() that asserts the
socket buffer lock. sbflush() unconditionally locks the socket
buffer before calling sbflush_locked(). Update panic strings
for new function names.
- Break out sbdrop() into sbdrop_locked() that asserts the socket
buffer lock. sbdrop() unconditionally locks the socket buffer
before calling sbdrop_locked().
- Break out sbdroprecord() into sbdroprecord_locked() that asserts
the socket buffer lock. sbdroprecord() unconditionally locks
the socket buffer before calling sbdroprecord_locked().
- sofree() now calls socantsendmore_locked() and re-acquires the
socket buffer lock on return. It also now calls
sbrelease_locked().
- sorflush() now calls socantrcvmore_locked() and re-acquires the
socket buffer lock on return. Clean up/mess up other behavior
in sorflush() relating to the temporary stack copy of the socket
buffer used with dom_dispose by more properly initializing the
temporary copy, and selectively bzeroing/copying more carefully
to prevent WITNESS from getting confused by improperly
initialized mutexes. Annotate why that's necessary, or at
least, needed.
- soisconnected() now calls sbdrop_locked() before unlocking the
socket buffer to avoid locking overhead.
Some parts of this change were:
Submitted by: sam
Sponsored by: FreeBSD Foundation
Obtained from: BSD/OS
2004-06-21 00:20:43 +00:00
|
|
|
SOCKBUF_LOCK(&so2->so_rcv);
|
|
|
|
if (sbappendaddr_locked(&so2->so_rcv, from, m, control)) {
|
Reduce the number of unnecessary unlock-relocks on socket buffer mutexes
associated with performing a wakeup on the socket buffer:
- When performing an sbappend*() followed by a so[rw]wakeup(), explicitly
acquire the socket buffer lock and use the _locked() variants of both
calls. Note that the _locked() sowakeup() versions unlock the mutex on
return. This is done in uipc_send(), divert_packet(), mroute
socket_send(), raw_append(), tcp_reass(), tcp_input(), and udp_append().
- When the socket buffer lock is dropped before a sowakeup(), remove the
explicit unlock and use the _locked() sowakeup() variant. This is done
in soisdisconnecting(), soisdisconnected() when setting the can't send/
receive flags and dropping data, and in uipc_rcvd() which adjusting
back-pressure on the sockets.
For UNIX domain sockets running mpsafe with a contention-intensive SMP
mysql benchmark, this results in a 1.6% query rate improvement due to
reduce mutex costs.
2004-06-26 19:10:39 +00:00
|
|
|
sorwakeup_locked(so2);
|
2004-03-30 02:16:25 +00:00
|
|
|
m = NULL;
|
|
|
|
control = NULL;
|
2004-01-11 19:48:19 +00:00
|
|
|
} else {
|
Merge next step in socket buffer locking:
- sowakeup() now asserts the socket buffer lock on entry. Move
the call to KNOTE higher in sowakeup() so that it is made with
the socket buffer lock held for consistency with other calls.
Release the socket buffer lock prior to calling into pgsigio(),
so_upcall(), or aio_swake(). Locking for this event management
will need revisiting in the future, but this model avoids lock
order reversals when upcalls into other subsystems result in
socket/socket buffer operations. Assert that the socket buffer
lock is not held at the end of the function.
- Wrapper macros for sowakeup(), sorwakeup() and sowwakeup(), now
have _locked versions which assert the socket buffer lock on
entry. If a wakeup is required by sb_notify(), invoke
sowakeup(); otherwise, unconditionally release the socket buffer
lock. This results in the socket buffer lock being released
whether a wakeup is required or not.
- Break out socantsendmore() into socantsendmore_locked() that
asserts the socket buffer lock. socantsendmore()
unconditionally locks the socket buffer before calling
socantsendmore_locked(). Note that both functions return with
the socket buffer unlocked as socantsendmore_locked() calls
sowwakeup_locked() which has the same properties. Assert that
the socket buffer is unlocked on return.
- Break out socantrcvmore() into socantrcvmore_locked() that
asserts the socket buffer lock. socantrcvmore() unconditionally
locks the socket buffer before calling socantrcvmore_locked().
Note that both functions return with the socket buffer unlocked
as socantrcvmore_locked() calls sorwakeup_locked() which has
similar properties. Assert that the socket buffer is unlocked
on return.
- Break out sbrelease() into a sbrelease_locked() that asserts the
socket buffer lock. sbrelease() unconditionally locks the
socket buffer before calling sbrelease_locked().
sbrelease_locked() now invokes sbflush_locked() instead of
sbflush().
- Assert the socket buffer lock in socket buffer sanity check
functions sblastrecordchk(), sblastmbufchk().
- Assert the socket buffer lock in SBLINKRECORD().
- Break out various sbappend() functions into sbappend_locked()
(and variations on that name) that assert the socket buffer
lock. The !_locked() variations unconditionally lock the socket
buffer before calling their _locked counterparts. Internally,
make sure to call _locked() support routines, etc, if already
holding the socket buffer lock.
- Break out sbinsertoob() into sbinsertoob_locked() that asserts
the socket buffer lock. sbinsertoob() unconditionally locks the
socket buffer before calling sbinsertoob_locked().
- Break out sbflush() into sbflush_locked() that asserts the
socket buffer lock. sbflush() unconditionally locks the socket
buffer before calling sbflush_locked(). Update panic strings
for new function names.
- Break out sbdrop() into sbdrop_locked() that asserts the socket
buffer lock. sbdrop() unconditionally locks the socket buffer
before calling sbdrop_locked().
- Break out sbdroprecord() into sbdroprecord_locked() that asserts
the socket buffer lock. sbdroprecord() unconditionally locks
the socket buffer before calling sbdroprecord_locked().
- sofree() now calls socantsendmore_locked() and re-acquires the
socket buffer lock on return. It also now calls
sbrelease_locked().
- sorflush() now calls socantrcvmore_locked() and re-acquires the
socket buffer lock on return. Clean up/mess up other behavior
in sorflush() relating to the temporary stack copy of the socket
buffer used with dom_dispose by more properly initializing the
temporary copy, and selectively bzeroing/copying more carefully
to prevent WITNESS from getting confused by improperly
initialized mutexes. Annotate why that's necessary, or at
least, needed.
- soisconnected() now calls sbdrop_locked() before unlocking the
socket buffer to avoid locking overhead.
Some parts of this change were:
Submitted by: sam
Sponsored by: FreeBSD Foundation
Obtained from: BSD/OS
2004-06-21 00:20:43 +00:00
|
|
|
SOCKBUF_UNLOCK(&so2->so_rcv);
|
1997-04-27 20:01:29 +00:00
|
|
|
error = ENOBUFS;
|
2004-01-11 19:48:19 +00:00
|
|
|
}
|
2007-03-01 09:00:42 +00:00
|
|
|
if (nam != NULL) {
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2007-03-01 09:00:42 +00:00
|
|
|
UNP_PCB_LOCK(unp2);
|
2007-02-26 20:47:52 +00:00
|
|
|
unp_disconnect(unp, unp2);
|
2007-03-01 09:00:42 +00:00
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
}
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
1997-04-27 20:01:29 +00:00
|
|
|
break;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2009-10-05 14:49:16 +00:00
|
|
|
case SOCK_SEQPACKET:
|
1997-04-27 20:01:29 +00:00
|
|
|
case SOCK_STREAM:
|
|
|
|
if ((so->so_state & SS_ISCONNECTED) == 0) {
|
2004-03-30 02:16:25 +00:00
|
|
|
if (nam != NULL) {
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2001-09-12 08:38:13 +00:00
|
|
|
error = unp_connect(so, nam, td);
|
1997-04-27 20:01:29 +00:00
|
|
|
if (error)
|
|
|
|
break; /* XXX */
|
|
|
|
} else {
|
|
|
|
error = ENOTCONN;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-01 09:00:42 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2006-07-11 21:49:54 +00:00
|
|
|
/* Lockless read. */
|
2004-06-14 18:16:22 +00:00
|
|
|
if (so->so_snd.sb_state & SBS_CANTSENDMORE) {
|
1997-04-27 20:01:29 +00:00
|
|
|
error = EPIPE;
|
|
|
|
break;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2006-07-31 23:00:05 +00:00
|
|
|
/*
|
|
|
|
* Because connect() and send() are non-atomic in a sendto()
|
|
|
|
* with a target address, it's possible that the socket will
|
|
|
|
* have disconnected before the send() can run. In that case
|
|
|
|
* return the slightly counter-intuitive but otherwise
|
|
|
|
* correct error that the socket is not connected.
|
2007-02-26 20:47:52 +00:00
|
|
|
*
|
2009-06-18 20:56:22 +00:00
|
|
|
* Locking here must be done carefully: the linkage lock
|
2007-05-11 12:10:45 +00:00
|
|
|
* prevents interconnections between unpcbs from changing, so
|
|
|
|
* we can traverse from unp to unp2 without acquiring unp's
|
|
|
|
* lock. Socket buffer locks follow unpcb locks, so we can
|
|
|
|
* acquire both remote and lock socket buffer locks.
|
2006-07-31 23:00:05 +00:00
|
|
|
*/
|
2006-07-22 18:41:42 +00:00
|
|
|
unp2 = unp->unp_conn;
|
2006-07-31 23:00:05 +00:00
|
|
|
if (unp2 == NULL) {
|
|
|
|
error = ENOTCONN;
|
|
|
|
break;
|
|
|
|
}
|
2006-07-22 18:41:42 +00:00
|
|
|
so2 = unp2->unp_socket;
|
2007-03-01 09:00:42 +00:00
|
|
|
UNP_PCB_LOCK(unp2);
|
Merge next step in socket buffer locking:
- sowakeup() now asserts the socket buffer lock on entry. Move
the call to KNOTE higher in sowakeup() so that it is made with
the socket buffer lock held for consistency with other calls.
Release the socket buffer lock prior to calling into pgsigio(),
so_upcall(), or aio_swake(). Locking for this event management
will need revisiting in the future, but this model avoids lock
order reversals when upcalls into other subsystems result in
socket/socket buffer operations. Assert that the socket buffer
lock is not held at the end of the function.
- Wrapper macros for sowakeup(), sorwakeup() and sowwakeup(), now
have _locked versions which assert the socket buffer lock on
entry. If a wakeup is required by sb_notify(), invoke
sowakeup(); otherwise, unconditionally release the socket buffer
lock. This results in the socket buffer lock being released
whether a wakeup is required or not.
- Break out socantsendmore() into socantsendmore_locked() that
asserts the socket buffer lock. socantsendmore()
unconditionally locks the socket buffer before calling
socantsendmore_locked(). Note that both functions return with
the socket buffer unlocked as socantsendmore_locked() calls
sowwakeup_locked() which has the same properties. Assert that
the socket buffer is unlocked on return.
- Break out socantrcvmore() into socantrcvmore_locked() that
asserts the socket buffer lock. socantrcvmore() unconditionally
locks the socket buffer before calling socantrcvmore_locked().
Note that both functions return with the socket buffer unlocked
as socantrcvmore_locked() calls sorwakeup_locked() which has
similar properties. Assert that the socket buffer is unlocked
on return.
- Break out sbrelease() into a sbrelease_locked() that asserts the
socket buffer lock. sbrelease() unconditionally locks the
socket buffer before calling sbrelease_locked().
sbrelease_locked() now invokes sbflush_locked() instead of
sbflush().
- Assert the socket buffer lock in socket buffer sanity check
functions sblastrecordchk(), sblastmbufchk().
- Assert the socket buffer lock in SBLINKRECORD().
- Break out various sbappend() functions into sbappend_locked()
(and variations on that name) that assert the socket buffer
lock. The !_locked() variations unconditionally lock the socket
buffer before calling their _locked counterparts. Internally,
make sure to call _locked() support routines, etc, if already
holding the socket buffer lock.
- Break out sbinsertoob() into sbinsertoob_locked() that asserts
the socket buffer lock. sbinsertoob() unconditionally locks the
socket buffer before calling sbinsertoob_locked().
- Break out sbflush() into sbflush_locked() that asserts the
socket buffer lock. sbflush() unconditionally locks the socket
buffer before calling sbflush_locked(). Update panic strings
for new function names.
- Break out sbdrop() into sbdrop_locked() that asserts the socket
buffer lock. sbdrop() unconditionally locks the socket buffer
before calling sbdrop_locked().
- Break out sbdroprecord() into sbdroprecord_locked() that asserts
the socket buffer lock. sbdroprecord() unconditionally locks
the socket buffer before calling sbdroprecord_locked().
- sofree() now calls socantsendmore_locked() and re-acquires the
socket buffer lock on return. It also now calls
sbrelease_locked().
- sorflush() now calls socantrcvmore_locked() and re-acquires the
socket buffer lock on return. Clean up/mess up other behavior
in sorflush() relating to the temporary stack copy of the socket
buffer used with dom_dispose by more properly initializing the
temporary copy, and selectively bzeroing/copying more carefully
to prevent WITNESS from getting confused by improperly
initialized mutexes. Annotate why that's necessary, or at
least, needed.
- soisconnected() now calls sbdrop_locked() before unlocking the
socket buffer to avoid locking overhead.
Some parts of this change were:
Submitted by: sam
Sponsored by: FreeBSD Foundation
Obtained from: BSD/OS
2004-06-21 00:20:43 +00:00
|
|
|
SOCKBUF_LOCK(&so2->so_rcv);
|
2006-07-22 18:41:42 +00:00
|
|
|
if (unp2->unp_flags & UNP_WANTCRED) {
|
2005-04-13 00:01:46 +00:00
|
|
|
/*
|
2007-03-01 09:00:42 +00:00
|
|
|
* Credentials are passed only once on SOCK_STREAM.
|
2005-04-13 00:01:46 +00:00
|
|
|
*/
|
2006-07-22 18:41:42 +00:00
|
|
|
unp2->unp_flags &= ~UNP_WANTCRED;
|
2005-04-13 00:01:46 +00:00
|
|
|
control = unp_addsockcred(td, control);
|
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* Send to paired receive port, and then reduce send buffer
|
|
|
|
* hiwater marks to maintain backpressure. Wake up readers.
|
1997-04-27 20:01:29 +00:00
|
|
|
*/
|
2009-10-05 14:49:16 +00:00
|
|
|
switch (so->so_type) {
|
|
|
|
case SOCK_STREAM:
|
|
|
|
if (control != NULL) {
|
|
|
|
if (sbappendcontrol_locked(&so2->so_rcv, m,
|
|
|
|
control))
|
|
|
|
control = NULL;
|
|
|
|
} else
|
|
|
|
sbappend_locked(&so2->so_rcv, m);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_SEQPACKET: {
|
|
|
|
const struct sockaddr *from;
|
|
|
|
|
|
|
|
from = &sun_noname;
|
|
|
|
if (sbappendaddr_locked(&so2->so_rcv, from, m,
|
|
|
|
control))
|
2004-03-30 02:16:25 +00:00
|
|
|
control = NULL;
|
2009-10-05 14:49:16 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XXXRW: While fine for SOCK_STREAM, this conflates maximum
|
|
|
|
* datagram size and back-pressure for SOCK_SEQPACKET, which
|
|
|
|
* can lead to undesired return of EMSGSIZE on send instead
|
|
|
|
* of more desirable blocking.
|
|
|
|
*/
|
2008-12-30 16:09:57 +00:00
|
|
|
mbcnt_delta = so2->so_rcv.sb_mbcnt - unp2->unp_mbcnt;
|
2006-07-22 18:41:42 +00:00
|
|
|
unp2->unp_mbcnt = so2->so_rcv.sb_mbcnt;
|
2006-07-11 21:49:54 +00:00
|
|
|
sbcc = so2->so_rcv.sb_cc;
|
|
|
|
sorwakeup_locked(so2);
|
|
|
|
|
|
|
|
SOCKBUF_LOCK(&so->so_snd);
|
2006-07-22 18:41:42 +00:00
|
|
|
newhiwat = so->so_snd.sb_hiwat - (sbcc - unp2->unp_cc);
|
2000-09-05 22:11:13 +00:00
|
|
|
(void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat,
|
2000-08-29 11:28:06 +00:00
|
|
|
newhiwat, RLIM_INFINITY);
|
2008-12-30 16:09:57 +00:00
|
|
|
so->so_snd.sb_mbmax -= mbcnt_delta;
|
2004-12-22 20:28:46 +00:00
|
|
|
SOCKBUF_UNLOCK(&so->so_snd);
|
2006-07-22 18:41:42 +00:00
|
|
|
unp2->unp_cc = sbcc;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp2);
|
2004-03-30 02:16:25 +00:00
|
|
|
m = NULL;
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
1997-04-27 20:01:29 +00:00
|
|
|
panic("uipc_send unknown socktype");
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
1997-04-27 20:01:29 +00:00
|
|
|
|
|
|
|
/*
|
2008-10-03 09:01:55 +00:00
|
|
|
* PRUS_EOF is equivalent to pru_send followed by pru_shutdown.
|
1997-04-27 20:01:29 +00:00
|
|
|
*/
|
|
|
|
if (flags & PRUS_EOF) {
|
2007-03-01 09:00:42 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
1997-04-27 20:01:29 +00:00
|
|
|
socantsendmore(so);
|
|
|
|
unp_shutdown(unp);
|
2007-03-01 09:00:42 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
if ((nam != NULL) || (flags & PRUS_EOF))
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
else
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_RUNLOCK();
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2004-03-30 02:16:25 +00:00
|
|
|
if (control != NULL && error != 0)
|
1999-05-10 18:09:39 +00:00
|
|
|
unp_dispose(control);
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
release:
|
2004-03-30 02:16:25 +00:00
|
|
|
if (control != NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
m_freem(control);
|
2004-03-30 02:16:25 +00:00
|
|
|
if (m != NULL)
|
1994-05-24 10:09:53 +00:00
|
|
|
m_freem(m);
|
2004-01-11 19:48:19 +00:00
|
|
|
return (error);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_sense(struct socket *so, struct stat *sb)
|
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp, *unp2;
|
1997-04-27 20:01:29 +00:00
|
|
|
struct socket *so2;
|
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_sense: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
1997-04-27 20:01:29 +00:00
|
|
|
sb->st_blksize = so->so_snd.sb_hiwat;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_RLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
unp2 = unp->unp_conn;
|
2009-10-05 14:49:16 +00:00
|
|
|
if ((so->so_type == SOCK_STREAM || so->so_type == SOCK_SEQPACKET) &&
|
|
|
|
unp2 != NULL) {
|
2007-02-26 20:47:52 +00:00
|
|
|
so2 = unp2->unp_socket;
|
1997-04-27 20:01:29 +00:00
|
|
|
sb->st_blksize += so2->so_rcv.sb_cc;
|
|
|
|
}
|
2004-06-17 17:16:53 +00:00
|
|
|
sb->st_dev = NODEV;
|
1997-04-27 20:01:29 +00:00
|
|
|
if (unp->unp_ino == 0)
|
2002-12-25 07:59:39 +00:00
|
|
|
unp->unp_ino = (++unp_ino == 0) ? ++unp_ino : unp_ino;
|
1997-04-27 20:01:29 +00:00
|
|
|
sb->st_ino = unp->unp_ino;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_RUNLOCK();
|
1997-04-27 20:01:29 +00:00
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
uipc_shutdown(struct socket *so)
|
|
|
|
{
|
2004-08-16 04:41:03 +00:00
|
|
|
struct unpcb *unp;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2004-08-16 04:41:03 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_shutdown: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
1997-04-27 20:01:29 +00:00
|
|
|
socantsendmore(so);
|
|
|
|
unp_shutdown(unp);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1997-04-27 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
1997-08-16 19:16:27 +00:00
|
|
|
uipc_sockaddr(struct socket *so, struct sockaddr **nam)
|
1997-04-27 20:01:29 +00:00
|
|
|
{
|
2004-08-16 04:41:03 +00:00
|
|
|
struct unpcb *unp;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
const struct sockaddr *sa;
|
1997-04-27 20:01:29 +00:00
|
|
|
|
2006-03-17 13:52:57 +00:00
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("uipc_sockaddr: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
*nam = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2004-03-30 02:16:25 +00:00
|
|
|
if (unp->unp_addr != NULL)
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
sa = (struct sockaddr *) unp->unp_addr;
|
2001-04-24 19:09:23 +00:00
|
|
|
else
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
sa = &sun_noname;
|
|
|
|
bcopy(sa, *nam, sa->sa_len);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2008-10-08 06:19:49 +00:00
|
|
|
static struct pr_usrreqs uipc_usrreqs_dgram = {
|
2004-11-08 14:44:54 +00:00
|
|
|
.pru_abort = uipc_abort,
|
|
|
|
.pru_accept = uipc_accept,
|
|
|
|
.pru_attach = uipc_attach,
|
|
|
|
.pru_bind = uipc_bind,
|
|
|
|
.pru_connect = uipc_connect,
|
|
|
|
.pru_connect2 = uipc_connect2,
|
|
|
|
.pru_detach = uipc_detach,
|
|
|
|
.pru_disconnect = uipc_disconnect,
|
|
|
|
.pru_listen = uipc_listen,
|
|
|
|
.pru_peeraddr = uipc_peeraddr,
|
|
|
|
.pru_rcvd = uipc_rcvd,
|
|
|
|
.pru_send = uipc_send,
|
|
|
|
.pru_sense = uipc_sense,
|
|
|
|
.pru_shutdown = uipc_shutdown,
|
|
|
|
.pru_sockaddr = uipc_sockaddr,
|
2008-10-08 06:19:49 +00:00
|
|
|
.pru_soreceive = soreceive_dgram,
|
|
|
|
.pru_close = uipc_close,
|
|
|
|
};
|
|
|
|
|
2009-10-05 14:49:16 +00:00
|
|
|
static struct pr_usrreqs uipc_usrreqs_seqpacket = {
|
|
|
|
.pru_abort = uipc_abort,
|
|
|
|
.pru_accept = uipc_accept,
|
|
|
|
.pru_attach = uipc_attach,
|
|
|
|
.pru_bind = uipc_bind,
|
|
|
|
.pru_connect = uipc_connect,
|
|
|
|
.pru_connect2 = uipc_connect2,
|
|
|
|
.pru_detach = uipc_detach,
|
|
|
|
.pru_disconnect = uipc_disconnect,
|
|
|
|
.pru_listen = uipc_listen,
|
|
|
|
.pru_peeraddr = uipc_peeraddr,
|
|
|
|
.pru_rcvd = uipc_rcvd,
|
|
|
|
.pru_send = uipc_send,
|
|
|
|
.pru_sense = uipc_sense,
|
|
|
|
.pru_shutdown = uipc_shutdown,
|
|
|
|
.pru_sockaddr = uipc_sockaddr,
|
|
|
|
.pru_soreceive = soreceive_generic, /* XXX: or...? */
|
|
|
|
.pru_close = uipc_close,
|
|
|
|
};
|
|
|
|
|
2008-10-08 06:19:49 +00:00
|
|
|
static struct pr_usrreqs uipc_usrreqs_stream = {
|
|
|
|
.pru_abort = uipc_abort,
|
|
|
|
.pru_accept = uipc_accept,
|
|
|
|
.pru_attach = uipc_attach,
|
|
|
|
.pru_bind = uipc_bind,
|
|
|
|
.pru_connect = uipc_connect,
|
|
|
|
.pru_connect2 = uipc_connect2,
|
|
|
|
.pru_detach = uipc_detach,
|
|
|
|
.pru_disconnect = uipc_disconnect,
|
|
|
|
.pru_listen = uipc_listen,
|
|
|
|
.pru_peeraddr = uipc_peeraddr,
|
|
|
|
.pru_rcvd = uipc_rcvd,
|
|
|
|
.pru_send = uipc_send,
|
|
|
|
.pru_sense = uipc_sense,
|
|
|
|
.pru_shutdown = uipc_shutdown,
|
|
|
|
.pru_sockaddr = uipc_sockaddr,
|
|
|
|
.pru_soreceive = soreceive_generic,
|
2006-07-21 17:11:15 +00:00
|
|
|
.pru_close = uipc_close,
|
1997-04-27 20:01:29 +00:00
|
|
|
};
|
2001-08-17 22:01:18 +00:00
|
|
|
|
2008-10-03 13:01:56 +00:00
|
|
|
static int
|
2005-02-20 23:22:13 +00:00
|
|
|
uipc_ctloutput(struct socket *so, struct sockopt *sopt)
|
2001-08-17 22:01:18 +00:00
|
|
|
{
|
2004-08-16 04:41:03 +00:00
|
|
|
struct unpcb *unp;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
struct xucred xu;
|
2005-04-13 00:01:46 +00:00
|
|
|
int error, optval;
|
|
|
|
|
2005-04-20 02:57:56 +00:00
|
|
|
if (sopt->sopt_level != 0)
|
|
|
|
return (EINVAL);
|
|
|
|
|
2005-04-13 00:01:46 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("uipc_ctloutput: unp == NULL"));
|
2005-04-13 00:01:46 +00:00
|
|
|
error = 0;
|
2001-08-17 22:01:18 +00:00
|
|
|
switch (sopt->sopt_dir) {
|
|
|
|
case SOPT_GET:
|
|
|
|
switch (sopt->sopt_name) {
|
|
|
|
case LOCAL_PEERCRED:
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2001-08-17 22:01:18 +00:00
|
|
|
if (unp->unp_flags & UNP_HAVEPC)
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
xu = unp->unp_peercred;
|
2001-08-17 22:01:18 +00:00
|
|
|
else {
|
|
|
|
if (so->so_type == SOCK_STREAM)
|
|
|
|
error = ENOTCONN;
|
|
|
|
else
|
|
|
|
error = EINVAL;
|
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
if (error == 0)
|
|
|
|
error = sooptcopyout(sopt, &xu, sizeof(xu));
|
2001-08-17 22:01:18 +00:00
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2005-04-13 00:01:46 +00:00
|
|
|
case LOCAL_CREDS:
|
2008-01-10 12:29:12 +00:00
|
|
|
/* Unlocked read. */
|
2005-04-13 00:01:46 +00:00
|
|
|
optval = unp->unp_flags & UNP_WANTCRED ? 1 : 0;
|
|
|
|
error = sooptcopyout(sopt, &optval, sizeof(optval));
|
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2005-04-13 00:01:46 +00:00
|
|
|
case LOCAL_CONNWAIT:
|
2008-01-10 12:29:12 +00:00
|
|
|
/* Unlocked read. */
|
2005-04-13 00:01:46 +00:00
|
|
|
optval = unp->unp_flags & UNP_CONNWAIT ? 1 : 0;
|
|
|
|
error = sooptcopyout(sopt, &optval, sizeof(optval));
|
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2001-08-17 22:01:18 +00:00
|
|
|
default:
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2001-08-17 22:01:18 +00:00
|
|
|
case SOPT_SET:
|
2005-04-13 00:01:46 +00:00
|
|
|
switch (sopt->sopt_name) {
|
|
|
|
case LOCAL_CREDS:
|
|
|
|
case LOCAL_CONNWAIT:
|
|
|
|
error = sooptcopyin(sopt, &optval, sizeof(optval),
|
|
|
|
sizeof(optval));
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
|
2007-02-26 20:47:52 +00:00
|
|
|
#define OPTSET(bit) do { \
|
|
|
|
UNP_PCB_LOCK(unp); \
|
|
|
|
if (optval) \
|
|
|
|
unp->unp_flags |= bit; \
|
|
|
|
else \
|
|
|
|
unp->unp_flags &= ~bit; \
|
|
|
|
UNP_PCB_UNLOCK(unp); \
|
|
|
|
} while (0)
|
2005-04-13 00:01:46 +00:00
|
|
|
|
|
|
|
switch (sopt->sopt_name) {
|
|
|
|
case LOCAL_CREDS:
|
|
|
|
OPTSET(UNP_WANTCRED);
|
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2005-04-13 00:01:46 +00:00
|
|
|
case LOCAL_CONNWAIT:
|
|
|
|
OPTSET(UNP_CONNWAIT);
|
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2005-04-13 00:01:46 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#undef OPTSET
|
|
|
|
default:
|
|
|
|
error = ENOPROTOOPT;
|
|
|
|
break;
|
|
|
|
}
|
2005-04-25 00:48:04 +00:00
|
|
|
break;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2001-08-17 22:01:18 +00:00
|
|
|
default:
|
|
|
|
error = EOPNOTSUPP;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return (error);
|
|
|
|
}
|
2004-01-11 19:48:19 +00:00
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2005-02-20 23:22:13 +00:00
|
|
|
struct sockaddr_un *soun = (struct sockaddr_un *)nam;
|
|
|
|
struct vnode *vp;
|
|
|
|
struct socket *so2, *so3;
|
2004-08-14 03:43:49 +00:00
|
|
|
struct unpcb *unp, *unp2, *unp3;
|
2007-05-06 12:00:38 +00:00
|
|
|
int error, len, vfslocked;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct nameidata nd;
|
1997-08-16 19:16:27 +00:00
|
|
|
char buf[SOCK_MAXADDRLEN];
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
struct sockaddr *sa;
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2006-03-17 13:52:57 +00:00
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("unp_connect: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
1997-08-16 19:16:27 +00:00
|
|
|
len = nam->sa_len - offsetof(struct sockaddr_un, sun_path);
|
|
|
|
if (len <= 0)
|
2004-01-11 19:48:19 +00:00
|
|
|
return (EINVAL);
|
2008-07-03 23:26:10 +00:00
|
|
|
bcopy(soun->sun_path, buf, len);
|
|
|
|
buf[len] = 0;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
UNP_PCB_LOCK(unp);
|
2006-07-23 12:01:14 +00:00
|
|
|
if (unp->unp_flags & UNP_CONNECTING) {
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2006-07-23 12:01:14 +00:00
|
|
|
return (EALREADY);
|
|
|
|
}
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2007-02-13 21:00:57 +00:00
|
|
|
unp->unp_flags |= UNP_CONNECTING;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
|
|
|
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
sa = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK);
|
2007-05-06 12:00:38 +00:00
|
|
|
NDINIT(&nd, LOOKUP, MPSAFE | FOLLOW | LOCKLEAF, UIO_SYSSPACE, buf,
|
|
|
|
td);
|
1994-10-02 17:35:40 +00:00
|
|
|
error = namei(&nd);
|
|
|
|
if (error)
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
vp = NULL;
|
|
|
|
else
|
|
|
|
vp = nd.ni_vp;
|
|
|
|
ASSERT_VOP_LOCKED(vp, "unp_connect");
|
2007-05-06 12:00:38 +00:00
|
|
|
vfslocked = NDHASGIANT(&nd);
|
1999-12-15 23:02:35 +00:00
|
|
|
NDFREE(&nd, NDF_ONLY_PNBUF);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
if (error)
|
|
|
|
goto bad;
|
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (vp->v_type != VSOCK) {
|
|
|
|
error = ENOTSOCK;
|
|
|
|
goto bad;
|
|
|
|
}
|
2007-02-22 09:37:44 +00:00
|
|
|
#ifdef MAC
|
2007-10-24 19:04:04 +00:00
|
|
|
error = mac_vnode_check_open(td->td_ucred, vp, VWRITE | VREAD);
|
2007-02-22 09:37:44 +00:00
|
|
|
if (error)
|
|
|
|
goto bad;
|
|
|
|
#endif
|
2002-02-27 18:32:23 +00:00
|
|
|
error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td);
|
1994-10-02 17:35:40 +00:00
|
|
|
if (error)
|
1994-05-24 10:09:53 +00:00
|
|
|
goto bad;
|
2007-05-06 12:00:38 +00:00
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
2007-02-26 20:47:52 +00:00
|
|
|
|
2004-08-14 03:43:49 +00:00
|
|
|
unp = sotounpcb(so);
|
2006-03-17 13:52:57 +00:00
|
|
|
KASSERT(unp != NULL, ("unp_connect: unp == NULL"));
|
2007-02-26 20:47:52 +00:00
|
|
|
|
|
|
|
/*
|
2009-03-08 21:48:29 +00:00
|
|
|
* Lock linkage lock for two reasons: make sure v_socket is stable,
|
2007-02-26 20:47:52 +00:00
|
|
|
* and to protect simultaneous locking of multiple pcbs.
|
|
|
|
*/
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
1994-05-24 10:09:53 +00:00
|
|
|
so2 = vp->v_socket;
|
2004-03-30 02:16:25 +00:00
|
|
|
if (so2 == NULL) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ECONNREFUSED;
|
2004-07-18 01:29:43 +00:00
|
|
|
goto bad2;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
if (so->so_type != so2->so_type) {
|
|
|
|
error = EPROTOTYPE;
|
2004-07-18 01:29:43 +00:00
|
|
|
goto bad2;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
|
2007-02-26 20:47:52 +00:00
|
|
|
if (so2->so_options & SO_ACCEPTCONN) {
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
so3 = sonewconn(so2, 0);
|
2007-02-26 20:47:52 +00:00
|
|
|
} else
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
so3 = NULL;
|
|
|
|
if (so3 == NULL) {
|
1994-05-24 10:09:53 +00:00
|
|
|
error = ECONNREFUSED;
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
goto bad2;
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2001-08-17 22:01:18 +00:00
|
|
|
unp = sotounpcb(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
unp2 = sotounpcb(so2);
|
|
|
|
unp3 = sotounpcb(so3);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
UNP_PCB_LOCK(unp3);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
if (unp2->unp_addr != NULL) {
|
|
|
|
bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len);
|
|
|
|
unp3->unp_addr = (struct sockaddr_un *) sa;
|
|
|
|
sa = NULL;
|
|
|
|
}
|
2009-01-01 20:03:22 +00:00
|
|
|
|
2001-08-17 22:01:18 +00:00
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* The connecter's (client's) credentials are copied from its
|
|
|
|
* process structure at the time of connect() (which is now).
|
2001-08-17 22:01:18 +00:00
|
|
|
*/
|
2002-02-27 18:32:23 +00:00
|
|
|
cru2x(td->td_ucred, &unp3->unp_peercred);
|
2001-08-17 22:01:18 +00:00
|
|
|
unp3->unp_flags |= UNP_HAVEPC;
|
2009-01-01 20:03:22 +00:00
|
|
|
|
2001-08-17 22:01:18 +00:00
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* The receiver's (server's) credentials are copied from the
|
|
|
|
* unp_peercred member of socket on which the former called
|
2007-02-26 20:47:52 +00:00
|
|
|
* listen(); uipc_listen() cached that process's credentials
|
2006-07-22 17:24:55 +00:00
|
|
|
* at that time so we can use them now.
|
2001-08-17 22:01:18 +00:00
|
|
|
*/
|
|
|
|
KASSERT(unp2->unp_flags & UNP_HAVEPCCACHED,
|
|
|
|
("unp_connect: listener without cached peercred"));
|
|
|
|
memcpy(&unp->unp_peercred, &unp2->unp_peercred,
|
|
|
|
sizeof(unp->unp_peercred));
|
|
|
|
unp->unp_flags |= UNP_HAVEPC;
|
2006-04-24 19:09:33 +00:00
|
|
|
if (unp2->unp_flags & UNP_WANTCRED)
|
|
|
|
unp3->unp_flags |= UNP_WANTCRED;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp3);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2002-07-31 03:03:22 +00:00
|
|
|
#ifdef MAC
|
2007-10-24 19:04:04 +00:00
|
|
|
mac_socketpeer_set_from_socket(so, so3);
|
|
|
|
mac_socketpeer_set_from_socket(so3, so);
|
2002-07-31 03:03:22 +00:00
|
|
|
#endif
|
2001-08-17 22:01:18 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
so2 = so3;
|
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("unp_connect: unp == NULL"));
|
|
|
|
unp2 = sotounpcb(so2);
|
|
|
|
KASSERT(unp2 != NULL, ("unp_connect: unp2 == NULL"));
|
|
|
|
UNP_PCB_LOCK(unp);
|
|
|
|
UNP_PCB_LOCK(unp2);
|
2005-04-13 00:01:46 +00:00
|
|
|
error = unp_connect2(so, so2, PRU_CONNECT);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
bad2:
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2007-05-06 12:00:38 +00:00
|
|
|
if (vfslocked)
|
|
|
|
/*
|
|
|
|
* Giant has been previously acquired. This means filesystem
|
2008-10-03 09:01:55 +00:00
|
|
|
* isn't MPSAFE. Do it once again.
|
2007-05-06 12:00:38 +00:00
|
|
|
*/
|
|
|
|
mtx_lock(&Giant);
|
1994-05-24 10:09:53 +00:00
|
|
|
bad:
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
if (vp != NULL)
|
|
|
|
vput(vp);
|
2007-05-06 12:00:38 +00:00
|
|
|
VFS_UNLOCK_GIANT(vfslocked);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
free(sa, M_SONAME);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2006-07-23 12:01:14 +00:00
|
|
|
unp->unp_flags &= ~UNP_CONNECTING;
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
1994-05-24 10:09:53 +00:00
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2004-03-31 01:41:30 +00:00
|
|
|
static int
|
2005-04-13 00:01:46 +00:00
|
|
|
unp_connect2(struct socket *so, struct socket *so2, int req)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp;
|
2005-02-20 23:22:13 +00:00
|
|
|
struct unpcb *unp2;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2007-02-26 20:47:52 +00:00
|
|
|
unp = sotounpcb(so);
|
|
|
|
KASSERT(unp != NULL, ("unp_connect2: unp == NULL"));
|
|
|
|
unp2 = sotounpcb(so2);
|
|
|
|
KASSERT(unp2 != NULL, ("unp_connect2: unp2 == NULL"));
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK_ASSERT(unp);
|
|
|
|
UNP_PCB_LOCK_ASSERT(unp2);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (so2->so_type != so->so_type)
|
|
|
|
return (EPROTOTYPE);
|
|
|
|
unp->unp_conn = unp2;
|
2007-02-26 20:47:52 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
switch (so->so_type) {
|
|
|
|
case SOCK_DGRAM:
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink);
|
1994-05-24 10:09:53 +00:00
|
|
|
soisconnected(so);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_STREAM:
|
2009-10-05 14:49:16 +00:00
|
|
|
case SOCK_SEQPACKET:
|
1994-05-24 10:09:53 +00:00
|
|
|
unp2->unp_conn = unp;
|
2005-04-13 00:01:46 +00:00
|
|
|
if (req == PRU_CONNECT &&
|
|
|
|
((unp->unp_flags | unp2->unp_flags) & UNP_CONNWAIT))
|
|
|
|
soisconnecting(so);
|
|
|
|
else
|
|
|
|
soisconnected(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
soisconnected(so2);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
panic("unp_connect2");
|
|
|
|
}
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
2007-02-26 20:47:52 +00:00
|
|
|
unp_disconnect(struct unpcb *unp, struct unpcb *unp2)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2004-06-20 21:29:56 +00:00
|
|
|
struct socket *so;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2007-02-26 20:47:52 +00:00
|
|
|
KASSERT(unp2 != NULL, ("unp_disconnect: unp2 == NULL"));
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK_ASSERT(unp);
|
|
|
|
UNP_PCB_LOCK_ASSERT(unp2);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
|
2004-03-30 02:16:25 +00:00
|
|
|
unp->unp_conn = NULL;
|
1994-05-24 10:09:53 +00:00
|
|
|
switch (unp->unp_socket->so_type) {
|
|
|
|
case SOCK_DGRAM:
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_REMOVE(unp, unp_reflink);
|
2004-06-20 21:29:56 +00:00
|
|
|
so = unp->unp_socket;
|
|
|
|
SOCK_LOCK(so);
|
|
|
|
so->so_state &= ~SS_ISCONNECTED;
|
|
|
|
SOCK_UNLOCK(so);
|
1994-05-24 10:09:53 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_STREAM:
|
2009-10-05 14:49:16 +00:00
|
|
|
case SOCK_SEQPACKET:
|
1994-05-24 10:09:53 +00:00
|
|
|
soisdisconnected(unp->unp_socket);
|
2004-03-30 02:16:25 +00:00
|
|
|
unp2->unp_conn = NULL;
|
1994-05-24 10:09:53 +00:00
|
|
|
soisdisconnected(unp2->unp_socket);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
/*
|
2007-05-11 12:10:45 +00:00
|
|
|
* unp_pcblist() walks the global list of struct unpcb's to generate a
|
|
|
|
* pointer list, bumping the refcount on each unpcb. It then copies them out
|
|
|
|
* sequentially, validating the generation number on each to see if it has
|
|
|
|
* been detached. All of this is necessary because copyout() may sleep on
|
|
|
|
* disk I/O.
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
*/
|
1998-05-15 20:11:40 +00:00
|
|
|
static int
|
2000-07-04 11:25:35 +00:00
|
|
|
unp_pcblist(SYSCTL_HANDLER_ARGS)
|
1998-05-15 20:11:40 +00:00
|
|
|
{
|
1998-10-25 17:44:59 +00:00
|
|
|
int error, i, n;
|
2007-01-05 19:59:46 +00:00
|
|
|
int freeunp;
|
1998-05-15 20:11:40 +00:00
|
|
|
struct unpcb *unp, **unp_list;
|
|
|
|
unp_gen_t gencnt;
|
2001-08-18 02:53:50 +00:00
|
|
|
struct xunpgen *xug;
|
1998-05-15 20:11:40 +00:00
|
|
|
struct unp_head *head;
|
2001-08-18 02:53:50 +00:00
|
|
|
struct xunpcb *xu;
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2009-10-05 14:49:16 +00:00
|
|
|
switch ((intptr_t)arg1) {
|
|
|
|
case SOCK_STREAM:
|
|
|
|
head = &unp_shead;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_DGRAM:
|
|
|
|
head = &unp_dhead;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SOCK_SEQPACKET:
|
|
|
|
head = &unp_sphead;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2009-10-05 22:23:12 +00:00
|
|
|
panic("unp_pcblist: arg1 %d", (int)(intptr_t)arg1);
|
2009-10-05 14:49:16 +00:00
|
|
|
}
|
1998-05-15 20:11:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The process of preparing the PCB list is too time-consuming and
|
|
|
|
* resource-intensive to repeat twice on every request.
|
|
|
|
*/
|
2004-03-30 02:16:25 +00:00
|
|
|
if (req->oldptr == NULL) {
|
1998-05-15 20:11:40 +00:00
|
|
|
n = unp_count;
|
2001-08-18 02:53:50 +00:00
|
|
|
req->oldidx = 2 * (sizeof *xug)
|
1998-05-15 20:11:40 +00:00
|
|
|
+ (n + n/8) * sizeof(struct xunpcb);
|
2004-01-11 19:48:19 +00:00
|
|
|
return (0);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
|
2004-03-30 02:16:25 +00:00
|
|
|
if (req->newptr != NULL)
|
2004-01-11 19:48:19 +00:00
|
|
|
return (EPERM);
|
1998-05-15 20:11:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* OK, now we're committed to doing something.
|
|
|
|
*/
|
2003-02-19 05:47:46 +00:00
|
|
|
xug = malloc(sizeof(*xug), M_TEMP, M_WAITOK);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_LOCK();
|
1998-05-15 20:11:40 +00:00
|
|
|
gencnt = unp_gencnt;
|
|
|
|
n = unp_count;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_UNLOCK();
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2001-08-18 02:53:50 +00:00
|
|
|
xug->xug_len = sizeof *xug;
|
|
|
|
xug->xug_count = n;
|
|
|
|
xug->xug_gen = gencnt;
|
|
|
|
xug->xug_sogen = so_gencnt;
|
|
|
|
error = SYSCTL_OUT(req, xug, sizeof *xug);
|
|
|
|
if (error) {
|
|
|
|
free(xug, M_TEMP);
|
2004-01-11 19:48:19 +00:00
|
|
|
return (error);
|
2001-08-18 02:53:50 +00:00
|
|
|
}
|
1998-05-15 20:11:40 +00:00
|
|
|
|
2003-02-19 05:47:46 +00:00
|
|
|
unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK);
|
2004-01-11 19:48:19 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_LOCK();
|
1999-11-16 10:56:05 +00:00
|
|
|
for (unp = LIST_FIRST(head), i = 0; unp && i < n;
|
|
|
|
unp = LIST_NEXT(unp, unp_link)) {
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2001-10-09 21:40:30 +00:00
|
|
|
if (unp->unp_gencnt <= gencnt) {
|
2002-02-27 18:32:23 +00:00
|
|
|
if (cr_cansee(req->td->td_ucred,
|
2007-02-26 20:47:52 +00:00
|
|
|
unp->unp_socket->so_cred)) {
|
|
|
|
UNP_PCB_UNLOCK(unp);
|
2001-10-05 07:06:32 +00:00
|
|
|
continue;
|
2007-02-26 20:47:52 +00:00
|
|
|
}
|
1998-05-15 20:11:40 +00:00
|
|
|
unp_list[i++] = unp;
|
2007-01-05 19:59:46 +00:00
|
|
|
unp->unp_refcount++;
|
2001-10-05 07:06:32 +00:00
|
|
|
}
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_UNLOCK();
|
2006-07-22 17:24:55 +00:00
|
|
|
n = i; /* In case we lost some during malloc. */
|
1998-05-15 20:11:40 +00:00
|
|
|
|
|
|
|
error = 0;
|
2005-05-07 00:41:36 +00:00
|
|
|
xu = malloc(sizeof(*xu), M_TEMP, M_WAITOK | M_ZERO);
|
1998-05-15 20:11:40 +00:00
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
unp = unp_list[i];
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK(unp);
|
2007-01-05 19:59:46 +00:00
|
|
|
unp->unp_refcount--;
|
|
|
|
if (unp->unp_refcount != 0 && unp->unp_gencnt <= gencnt) {
|
2001-08-18 02:53:50 +00:00
|
|
|
xu->xu_len = sizeof *xu;
|
|
|
|
xu->xu_unpp = unp;
|
1998-05-15 20:11:40 +00:00
|
|
|
/*
|
|
|
|
* XXX - need more locking here to protect against
|
|
|
|
* connect/disconnect races for SMP.
|
|
|
|
*/
|
2004-03-30 02:16:25 +00:00
|
|
|
if (unp->unp_addr != NULL)
|
2004-01-11 19:48:19 +00:00
|
|
|
bcopy(unp->unp_addr, &xu->xu_addr,
|
1998-05-15 20:11:40 +00:00
|
|
|
unp->unp_addr->sun_len);
|
2004-03-30 02:16:25 +00:00
|
|
|
if (unp->unp_conn != NULL &&
|
|
|
|
unp->unp_conn->unp_addr != NULL)
|
1998-05-15 20:11:40 +00:00
|
|
|
bcopy(unp->unp_conn->unp_addr,
|
2001-08-18 02:53:50 +00:00
|
|
|
&xu->xu_caddr,
|
1998-05-15 20:11:40 +00:00
|
|
|
unp->unp_conn->unp_addr->sun_len);
|
2001-08-18 02:53:50 +00:00
|
|
|
bcopy(unp, &xu->xu_unp, sizeof *unp);
|
|
|
|
sotoxsocket(unp->unp_socket, &xu->xu_socket);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
2001-08-18 02:53:50 +00:00
|
|
|
error = SYSCTL_OUT(req, xu, sizeof *xu);
|
2007-01-05 19:59:46 +00:00
|
|
|
} else {
|
|
|
|
freeunp = (unp->unp_refcount == 0);
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_UNLOCK(unp);
|
|
|
|
if (freeunp) {
|
|
|
|
UNP_PCB_LOCK_DESTROY(unp);
|
2007-01-05 19:59:46 +00:00
|
|
|
uma_zfree(unp_zone, unp);
|
2007-02-26 20:47:52 +00:00
|
|
|
}
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
}
|
2001-08-18 02:53:50 +00:00
|
|
|
free(xu, M_TEMP);
|
1998-05-15 20:11:40 +00:00
|
|
|
if (!error) {
|
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* Give the user an updated idea of our state. If the
|
|
|
|
* generation differs from what we told her before, she knows
|
|
|
|
* that something happened while we were processing this
|
|
|
|
* request, and it might be necessary to retry.
|
1998-05-15 20:11:40 +00:00
|
|
|
*/
|
2001-08-18 02:53:50 +00:00
|
|
|
xug->xug_gen = unp_gencnt;
|
|
|
|
xug->xug_sogen = so_gencnt;
|
|
|
|
xug->xug_count = unp_count;
|
|
|
|
error = SYSCTL_OUT(req, xug, sizeof *xug);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
free(unp_list, M_TEMP);
|
2001-08-18 02:53:50 +00:00
|
|
|
free(xug, M_TEMP);
|
2004-01-11 19:48:19 +00:00
|
|
|
return (error);
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
|
2004-01-11 19:48:19 +00:00
|
|
|
SYSCTL_PROC(_net_local_dgram, OID_AUTO, pcblist, CTLFLAG_RD,
|
1998-05-15 20:11:40 +00:00
|
|
|
(caddr_t)(long)SOCK_DGRAM, 0, unp_pcblist, "S,xunpcb",
|
|
|
|
"List of active local datagram sockets");
|
2004-01-11 19:48:19 +00:00
|
|
|
SYSCTL_PROC(_net_local_stream, OID_AUTO, pcblist, CTLFLAG_RD,
|
1998-05-15 20:11:40 +00:00
|
|
|
(caddr_t)(long)SOCK_STREAM, 0, unp_pcblist, "S,xunpcb",
|
|
|
|
"List of active local stream sockets");
|
2009-10-05 14:49:16 +00:00
|
|
|
SYSCTL_PROC(_net_local_seqpacket, OID_AUTO, pcblist, CTLFLAG_RD,
|
|
|
|
(caddr_t)(long)SOCK_SEQPACKET, 0, unp_pcblist, "S,xunpcb",
|
|
|
|
"List of active local seqpacket sockets");
|
1998-05-15 20:11:40 +00:00
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_shutdown(struct unpcb *unp)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp2;
|
1994-05-24 10:09:53 +00:00
|
|
|
struct socket *so;
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK_ASSERT(unp);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
|
2007-02-26 20:47:52 +00:00
|
|
|
unp2 = unp->unp_conn;
|
2009-10-05 14:49:16 +00:00
|
|
|
if ((unp->unp_socket->so_type == SOCK_STREAM ||
|
|
|
|
(unp->unp_socket->so_type == SOCK_SEQPACKET)) && unp2 != NULL) {
|
2007-02-26 20:47:52 +00:00
|
|
|
so = unp2->unp_socket;
|
|
|
|
if (so != NULL)
|
|
|
|
socantrcvmore(so);
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_drop(struct unpcb *unp, int errno)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
|
|
|
struct socket *so = unp->unp_socket;
|
2007-02-26 20:47:52 +00:00
|
|
|
struct unpcb *unp2;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK_ASSERT();
|
2007-02-26 20:47:52 +00:00
|
|
|
UNP_PCB_LOCK_ASSERT(unp);
|
Introduce a subsystem lock around UNIX domain sockets in order to protect
global and allocated variables. This strategy is derived from work
originally developed by BSDi for BSD/OS, and applied to FreeBSD by Sam
Leffler:
- Add unp_mtx, a global mutex which will protect all UNIX domain socket
related variables, structures, etc.
- Add UNP_LOCK(), UNP_UNLOCK(), UNP_LOCK_ASSERT() macros.
- Acquire unp_mtx on entering most UNIX domain socket code,
drop/re-acquire around calls into VFS, and release it on return.
- Avoid performing sodupsockaddr() while holding the mutex, so in general
move to allocating storage before acquiring the mutex to copy the data.
- Make a stack copy of the xucred rather than copying out while holding
unp_mtx. Copy the peer credential out after releasing the mutex.
- Add additional assertions of vnode locks following VOP_CREATE().
A few notes:
- Use of an sx lock for the file list mutex may cause problems with regard
to unp_mtx when garbage collection passed file descriptors.
- The locking in unp_pcblist() for sysctl monitoring is correct subject to
the unpcb zone not returning memory for reuse by other subsystems
(consistent with similar existing concerns).
- Sam's version of this change, as with the BSD/OS version, made use of
both a global lock and per-unpcb locks. However, in practice, the
global lock covered all accesses, so I have simplified out the unpcb
locks in the interest of getting this merged faster (reducing the
overhead but not sacrificing granularity in most cases). We will want
to explore possibilities for improving lock granularity in this code in
the future.
Submitted by: sam
Sponsored by: FreeBSD Foundatiuon
Obtained from: BSD/OS 5 snapshot provided by BSDi
2004-06-10 21:34:38 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
so->so_error = errno;
|
2007-02-26 20:47:52 +00:00
|
|
|
unp2 = unp->unp_conn;
|
|
|
|
if (unp2 == NULL)
|
|
|
|
return;
|
|
|
|
UNP_PCB_LOCK(unp2);
|
|
|
|
unp_disconnect(unp, unp2);
|
|
|
|
UNP_PCB_UNLOCK(unp2);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
static void
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_freerights(struct file **rp, int fdcount)
|
2001-10-04 13:11:48 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
struct file *fp;
|
|
|
|
|
|
|
|
for (i = 0; i < fdcount; i++) {
|
2007-02-26 20:47:52 +00:00
|
|
|
fp = *rp;
|
|
|
|
*rp++ = NULL;
|
2001-10-04 13:11:48 +00:00
|
|
|
unp_discard(fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-03 13:01:56 +00:00
|
|
|
static int
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_externalize(struct mbuf *control, struct mbuf **controlp)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2001-09-12 08:38:13 +00:00
|
|
|
struct thread *td = curthread; /* XXX */
|
2001-10-04 13:11:48 +00:00
|
|
|
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
|
|
|
|
int i;
|
|
|
|
int *fdp;
|
|
|
|
struct file **rp;
|
|
|
|
struct file *fp;
|
|
|
|
void *data;
|
|
|
|
socklen_t clen = control->m_len, datalen;
|
|
|
|
int error, newfds;
|
1994-05-24 10:09:53 +00:00
|
|
|
int f;
|
2001-10-04 13:11:48 +00:00
|
|
|
u_int newlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_UNLOCK_ASSERT();
|
2004-08-19 01:45:16 +00:00
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
error = 0;
|
|
|
|
if (controlp != NULL) /* controlp == NULL => free control messages */
|
|
|
|
*controlp = NULL;
|
|
|
|
while (cm != NULL) {
|
|
|
|
if (sizeof(*cm) > clen || cm->cmsg_len > clen) {
|
|
|
|
error = EINVAL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
data = CMSG_DATA(cm);
|
|
|
|
datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
|
|
|
|
if (cm->cmsg_level == SOL_SOCKET
|
|
|
|
&& cm->cmsg_type == SCM_RIGHTS) {
|
|
|
|
newfds = datalen / sizeof(struct file *);
|
|
|
|
rp = data;
|
|
|
|
|
2003-03-23 19:41:34 +00:00
|
|
|
/* If we're not outputting the descriptors free them. */
|
2001-10-04 13:11:48 +00:00
|
|
|
if (error || controlp == NULL) {
|
|
|
|
unp_freerights(rp, newfds);
|
|
|
|
goto next;
|
|
|
|
}
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_XLOCK(td->td_proc->p_fd);
|
2001-10-04 13:11:48 +00:00
|
|
|
/* if the new FD's will not fit free them. */
|
|
|
|
if (!fdavail(td, newfds)) {
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_XUNLOCK(td->td_proc->p_fd);
|
2001-10-04 13:11:48 +00:00
|
|
|
error = EMSGSIZE;
|
|
|
|
unp_freerights(rp, newfds);
|
|
|
|
goto next;
|
|
|
|
}
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2000-03-09 15:15:27 +00:00
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* Now change each pointer to an fd in the global
|
|
|
|
* table to an integer that is the index to the local
|
|
|
|
* fd table entry that we set up to point to the
|
|
|
|
* global one we are transferring.
|
2000-03-09 15:15:27 +00:00
|
|
|
*/
|
2001-10-04 13:11:48 +00:00
|
|
|
newlen = newfds * sizeof(int);
|
|
|
|
*controlp = sbcreatecontrol(NULL, newlen,
|
|
|
|
SCM_RIGHTS, SOL_SOCKET);
|
|
|
|
if (*controlp == NULL) {
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_XUNLOCK(td->td_proc->p_fd);
|
2001-10-04 13:11:48 +00:00
|
|
|
error = E2BIG;
|
|
|
|
unp_freerights(rp, newfds);
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
|
|
|
|
fdp = (int *)
|
|
|
|
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
|
|
|
|
for (i = 0; i < newfds; i++) {
|
2004-01-17 00:59:04 +00:00
|
|
|
if (fdalloc(td, 0, &f))
|
2001-10-04 13:11:48 +00:00
|
|
|
panic("unp_externalize fdalloc failed");
|
|
|
|
fp = *rp++;
|
|
|
|
td->td_proc->p_fd->fd_ofiles[f] = fp;
|
2007-12-30 01:42:15 +00:00
|
|
|
unp_externalize_fp(fp);
|
2001-10-04 13:11:48 +00:00
|
|
|
*fdp++ = f;
|
|
|
|
}
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_XUNLOCK(td->td_proc->p_fd);
|
2006-07-22 17:24:55 +00:00
|
|
|
} else {
|
|
|
|
/* We can just copy anything else across. */
|
2001-10-04 13:11:48 +00:00
|
|
|
if (error || controlp == NULL)
|
|
|
|
goto next;
|
|
|
|
*controlp = sbcreatecontrol(NULL, datalen,
|
|
|
|
cm->cmsg_type, cm->cmsg_level);
|
|
|
|
if (*controlp == NULL) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto next;
|
|
|
|
}
|
|
|
|
bcopy(data,
|
|
|
|
CMSG_DATA(mtod(*controlp, struct cmsghdr *)),
|
|
|
|
datalen);
|
2000-03-09 15:15:27 +00:00
|
|
|
}
|
2001-10-04 13:11:48 +00:00
|
|
|
controlp = &(*controlp)->m_next;
|
|
|
|
|
|
|
|
next:
|
|
|
|
if (CMSG_SPACE(datalen) < clen) {
|
|
|
|
clen -= CMSG_SPACE(datalen);
|
|
|
|
cm = (struct cmsghdr *)
|
|
|
|
((caddr_t)cm + CMSG_SPACE(datalen));
|
|
|
|
} else {
|
|
|
|
clen = 0;
|
|
|
|
cm = NULL;
|
2000-03-09 15:15:27 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2000-03-09 15:15:27 +00:00
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
m_freem(control);
|
|
|
|
return (error);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2006-04-21 09:25:40 +00:00
|
|
|
static void
|
|
|
|
unp_zone_change(void *tag)
|
|
|
|
{
|
|
|
|
|
|
|
|
uma_zone_set_max(unp_zone, maxsockets);
|
|
|
|
}
|
|
|
|
|
2008-10-03 13:01:56 +00:00
|
|
|
static void
|
1998-05-15 20:11:40 +00:00
|
|
|
unp_init(void)
|
|
|
|
{
|
2006-07-22 17:24:55 +00:00
|
|
|
|
Change the curvnet variable from a global const struct vnet *,
previously always pointing to the default vnet context, to a
dynamically changing thread-local one. The currvnet context
should be set on entry to networking code via CURVNET_SET() macros,
and reverted to previous state via CURVNET_RESTORE(). Recursions
on curvnet are permitted, though strongly discuouraged.
This change should have no functional impact on nooptions VIMAGE
kernel builds, where CURVNET_* macros expand to whitespace.
The curthread->td_vnet (aka curvnet) variable's purpose is to be an
indicator of the vnet context in which the current network-related
operation takes place, in case we cannot deduce the current vnet
context from any other source, such as by looking at mbuf's
m->m_pkthdr.rcvif->if_vnet, sockets's so->so_vnet etc. Moreover, so
far curvnet has turned out to be an invaluable consistency checking
aid: it helps to catch cases when sockets, ifnets or any other
vnet-aware structures may have leaked from one vnet to another.
The exact placement of the CURVNET_SET() / CURVNET_RESTORE() macros
was a result of an empirical iterative process, whith an aim to
reduce recursions on CURVNET_SET() to a minimum, while still reducing
the scope of CURVNET_SET() to networking only operations - the
alternative would be calling CURVNET_SET() on each system call entry.
In general, curvnet has to be set in three typicall cases: when
processing socket-related requests from userspace or from within the
kernel; when processing inbound traffic flowing from device drivers
to upper layers of the networking stack, and when executing
timer-driven networking functions.
This change also introduces a DDB subcommand to show the list of all
vnet instances.
Approved by: julian (mentor)
2009-05-05 10:56:12 +00:00
|
|
|
#ifdef VIMAGE
|
|
|
|
if (!IS_DEFAULT_VNET(curvnet))
|
|
|
|
return;
|
|
|
|
#endif
|
2002-03-20 04:11:52 +00:00
|
|
|
unp_zone = uma_zcreate("unpcb", sizeof(struct unpcb), NULL, NULL,
|
2007-01-05 19:59:46 +00:00
|
|
|
NULL, NULL, UMA_ALIGN_PTR, 0);
|
2004-03-30 02:16:25 +00:00
|
|
|
if (unp_zone == NULL)
|
1998-05-15 20:11:40 +00:00
|
|
|
panic("unp_init");
|
2006-04-21 09:25:40 +00:00
|
|
|
uma_zone_set_max(unp_zone, maxsockets);
|
|
|
|
EVENTHANDLER_REGISTER(maxsockets_change, unp_zone_change,
|
|
|
|
NULL, EVENTHANDLER_PRI_ANY);
|
1998-05-15 20:11:40 +00:00
|
|
|
LIST_INIT(&unp_dhead);
|
|
|
|
LIST_INIT(&unp_shead);
|
2009-10-05 14:49:16 +00:00
|
|
|
LIST_INIT(&unp_sphead);
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
TASK_INIT(&unp_gc_task, 0, unp_gc, NULL);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_LOCK_INIT();
|
|
|
|
UNP_LIST_LOCK_INIT();
|
1998-05-15 20:11:40 +00:00
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static int
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_internalize(struct mbuf **controlp, struct thread *td)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2001-10-04 13:11:48 +00:00
|
|
|
struct mbuf *control = *controlp;
|
2001-09-12 08:38:13 +00:00
|
|
|
struct proc *p = td->td_proc;
|
2000-03-09 15:15:27 +00:00
|
|
|
struct filedesc *fdescp = p->p_fd;
|
2001-10-04 13:11:48 +00:00
|
|
|
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
|
|
|
|
struct cmsgcred *cmcred;
|
|
|
|
struct file **rp;
|
|
|
|
struct file *fp;
|
|
|
|
struct timeval *tv;
|
|
|
|
int i, fd, *fdp;
|
|
|
|
void *data;
|
|
|
|
socklen_t clen = control->m_len, datalen;
|
|
|
|
int error, oldfds;
|
2000-03-09 15:15:27 +00:00
|
|
|
u_int newlen;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_UNLOCK_ASSERT();
|
2004-08-19 01:45:16 +00:00
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
error = 0;
|
|
|
|
*controlp = NULL;
|
|
|
|
while (cm != NULL) {
|
|
|
|
if (sizeof(*cm) > clen || cm->cmsg_level != SOL_SOCKET
|
|
|
|
|| cm->cmsg_len > clen) {
|
|
|
|
error = EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
data = CMSG_DATA(cm);
|
|
|
|
datalen = (caddr_t)cm + cm->cmsg_len - (caddr_t)data;
|
|
|
|
|
|
|
|
switch (cm->cmsg_type) {
|
|
|
|
/*
|
|
|
|
* Fill in credential information.
|
|
|
|
*/
|
|
|
|
case SCM_CREDS:
|
|
|
|
*controlp = sbcreatecontrol(NULL, sizeof(*cmcred),
|
|
|
|
SCM_CREDS, SOL_SOCKET);
|
|
|
|
if (*controlp == NULL) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
cmcred = (struct cmsgcred *)
|
|
|
|
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
|
|
|
|
cmcred->cmcred_pid = p->p_pid;
|
2002-02-27 18:32:23 +00:00
|
|
|
cmcred->cmcred_uid = td->td_ucred->cr_ruid;
|
|
|
|
cmcred->cmcred_gid = td->td_ucred->cr_rgid;
|
|
|
|
cmcred->cmcred_euid = td->td_ucred->cr_uid;
|
|
|
|
cmcred->cmcred_ngroups = MIN(td->td_ucred->cr_ngroups,
|
2008-10-03 13:01:56 +00:00
|
|
|
CMGROUP_MAX);
|
2001-10-04 13:11:48 +00:00
|
|
|
for (i = 0; i < cmcred->cmcred_ngroups; i++)
|
|
|
|
cmcred->cmcred_groups[i] =
|
2002-02-27 18:32:23 +00:00
|
|
|
td->td_ucred->cr_groups[i];
|
2001-10-04 13:11:48 +00:00
|
|
|
break;
|
Add support to sendmsg()/recvmsg() for passing credentials between
processes using AF_LOCAL sockets. This hack is going to be used with
Secure RPC to duplicate a feature of STREAMS which has no real counterpart
in sockets (with STREAMS/TLI, you can apparently use t_getinfo() to learn
UID of a local process on the other side of a transport endpoint).
What happens is this: the client sets up a sendmsg() call with ancillary
data using the SCM_CREDS socket-level control message type. It does not
need to fill in the structure. When the kernel notices the data,
unp_internalize() fills in the cmesgcred structure with the sending
process' credentials (UID, EUID, GID, and ancillary groups). This data
is later delivered to the receiving process. The receiver can then
perform the follwing tests:
- Did the client send ancillary data?
o Yes, proceed.
o No, refuse to authenticate the client.
- The the client send data of type SCM_CREDS?
o Yes, proceed.
o No, refuse to authenticate the client.
- Is the cmsgcred structure the right size?
o Yes, proceed.
o No, signal a possible error.
The receiver can now inspect the credential information and use it to
authenticate the client.
1997-03-21 16:12:32 +00:00
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
case SCM_RIGHTS:
|
|
|
|
oldfds = datalen / sizeof (int);
|
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* Check that all the FDs passed in refer to legal
|
|
|
|
* files. If not, reject the entire operation.
|
2001-10-04 13:11:48 +00:00
|
|
|
*/
|
|
|
|
fdp = data;
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_SLOCK(fdescp);
|
2001-10-04 13:11:48 +00:00
|
|
|
for (i = 0; i < oldfds; i++) {
|
|
|
|
fd = *fdp++;
|
|
|
|
if ((unsigned)fd >= fdescp->fd_nfiles ||
|
|
|
|
fdescp->fd_ofiles[fd] == NULL) {
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_SUNLOCK(fdescp);
|
2001-10-04 13:11:48 +00:00
|
|
|
error = EBADF;
|
|
|
|
goto out;
|
|
|
|
}
|
2003-02-15 06:04:55 +00:00
|
|
|
fp = fdescp->fd_ofiles[fd];
|
|
|
|
if (!(fp->f_ops->fo_flags & DFLAG_PASSABLE)) {
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_SUNLOCK(fdescp);
|
2003-02-15 06:04:55 +00:00
|
|
|
error = EOPNOTSUPP;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
}
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
/*
|
2008-10-03 13:01:56 +00:00
|
|
|
* Now replace the integer FDs with pointers to the
|
|
|
|
* associated global file table entry..
|
2001-10-04 13:11:48 +00:00
|
|
|
*/
|
|
|
|
newlen = oldfds * sizeof(struct file *);
|
|
|
|
*controlp = sbcreatecontrol(NULL, newlen,
|
|
|
|
SCM_RIGHTS, SOL_SOCKET);
|
|
|
|
if (*controlp == NULL) {
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_SUNLOCK(fdescp);
|
2001-10-04 13:11:48 +00:00
|
|
|
error = E2BIG;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
fdp = data;
|
|
|
|
rp = (struct file **)
|
|
|
|
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
|
|
|
|
for (i = 0; i < oldfds; i++) {
|
|
|
|
fp = fdescp->fd_ofiles[*fdp++];
|
|
|
|
*rp++ = fp;
|
2007-12-30 01:42:15 +00:00
|
|
|
unp_internalize_fp(fp);
|
2001-10-04 13:11:48 +00:00
|
|
|
}
|
Replace custom file descriptor array sleep lock constructed using a mutex
and flags with an sxlock. This leads to a significant and measurable
performance improvement as a result of access to shared locking for
frequent lookup operations, reduced general overhead, and reduced overhead
in the event of contention. All of these are imported for threaded
applications where simultaneous access to a shared file descriptor array
occurs frequently. Kris has reported 2x-4x transaction rate improvements
on 8-core MySQL benchmarks; smaller improvements can be expected for many
workloads as a result of reduced overhead.
- Generally eliminate the distinction between "fast" and regular
acquisisition of the filedesc lock; the plan is that they will now all
be fast. Change all locking instances to either shared or exclusive
locks.
- Correct a bug (pointed out by kib) in fdfree() where previously msleep()
was called without the mutex held; sx_sleep() is now always called with
the sxlock held exclusively.
- Universally hold the struct file lock over changes to struct file,
rather than the filedesc lock or no lock. Always update the f_ops
field last. A further memory barrier is required here in the future
(discussed with jhb).
- Improve locking and reference management in linux_at(), which fails to
properly acquire vnode references before using vnode pointers. Annotate
improper use of vn_fullpath(), which will be replaced at a future date.
In fcntl(), we conservatively acquire an exclusive lock, even though in
some cases a shared lock may be sufficient, which should be revisited.
The dropping of the filedesc lock in fdgrowtable() is no longer required
as the sxlock can be held over the sleep operation; we should consider
removing that (pointed out by attilio).
Tested by: kris
Discussed with: jhb, kris, attilio, jeff
2007-04-04 09:11:34 +00:00
|
|
|
FILEDESC_SUNLOCK(fdescp);
|
2001-10-04 13:11:48 +00:00
|
|
|
break;
|
2000-03-09 15:15:27 +00:00
|
|
|
|
2001-10-04 13:11:48 +00:00
|
|
|
case SCM_TIMESTAMP:
|
|
|
|
*controlp = sbcreatecontrol(NULL, sizeof(*tv),
|
|
|
|
SCM_TIMESTAMP, SOL_SOCKET);
|
|
|
|
if (*controlp == NULL) {
|
|
|
|
error = ENOBUFS;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
tv = (struct timeval *)
|
|
|
|
CMSG_DATA(mtod(*controlp, struct cmsghdr *));
|
|
|
|
microtime(tv);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
error = EINVAL;
|
|
|
|
goto out;
|
2000-03-09 15:15:27 +00:00
|
|
|
}
|
2001-10-04 13:11:48 +00:00
|
|
|
|
|
|
|
controlp = &(*controlp)->m_next;
|
|
|
|
if (CMSG_SPACE(datalen) < clen) {
|
|
|
|
clen -= CMSG_SPACE(datalen);
|
|
|
|
cm = (struct cmsghdr *)
|
|
|
|
((caddr_t)cm + CMSG_SPACE(datalen));
|
|
|
|
} else {
|
|
|
|
clen = 0;
|
|
|
|
cm = NULL;
|
2000-03-09 15:15:27 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2001-10-04 13:11:48 +00:00
|
|
|
|
|
|
|
out:
|
|
|
|
m_freem(control);
|
|
|
|
return (error);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2007-02-20 10:50:02 +00:00
|
|
|
static struct mbuf *
|
2005-04-13 00:01:46 +00:00
|
|
|
unp_addsockcred(struct thread *td, struct mbuf *control)
|
|
|
|
{
|
2006-06-13 14:33:35 +00:00
|
|
|
struct mbuf *m, *n, *n_prev;
|
2005-04-13 00:01:46 +00:00
|
|
|
struct sockcred *sc;
|
2006-06-13 14:33:35 +00:00
|
|
|
const struct cmsghdr *cm;
|
2005-04-13 00:01:46 +00:00
|
|
|
int ngroups;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
ngroups = MIN(td->td_ucred->cr_ngroups, CMGROUP_MAX);
|
|
|
|
m = sbcreatecontrol(NULL, SOCKCREDSIZE(ngroups), SCM_CREDS, SOL_SOCKET);
|
|
|
|
if (m == NULL)
|
|
|
|
return (control);
|
|
|
|
|
|
|
|
sc = (struct sockcred *) CMSG_DATA(mtod(m, struct cmsghdr *));
|
|
|
|
sc->sc_uid = td->td_ucred->cr_ruid;
|
|
|
|
sc->sc_euid = td->td_ucred->cr_uid;
|
|
|
|
sc->sc_gid = td->td_ucred->cr_rgid;
|
|
|
|
sc->sc_egid = td->td_ucred->cr_gid;
|
|
|
|
sc->sc_ngroups = ngroups;
|
|
|
|
for (i = 0; i < sc->sc_ngroups; i++)
|
|
|
|
sc->sc_groups[i] = td->td_ucred->cr_groups[i];
|
|
|
|
|
|
|
|
/*
|
2006-07-22 17:24:55 +00:00
|
|
|
* Unlink SCM_CREDS control messages (struct cmsgcred), since just
|
|
|
|
* created SCM_CREDS control message (struct sockcred) has another
|
|
|
|
* format.
|
2005-04-13 00:01:46 +00:00
|
|
|
*/
|
2006-06-13 14:33:35 +00:00
|
|
|
if (control != NULL)
|
|
|
|
for (n = control, n_prev = NULL; n != NULL;) {
|
|
|
|
cm = mtod(n, struct cmsghdr *);
|
|
|
|
if (cm->cmsg_level == SOL_SOCKET &&
|
|
|
|
cm->cmsg_type == SCM_CREDS) {
|
|
|
|
if (n_prev == NULL)
|
|
|
|
control = n->m_next;
|
|
|
|
else
|
|
|
|
n_prev->m_next = n->m_next;
|
|
|
|
n = m_free(n);
|
|
|
|
} else {
|
|
|
|
n_prev = n;
|
|
|
|
n = n->m_next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Prepend it to the head. */
|
|
|
|
m->m_next = control;
|
|
|
|
return (m);
|
2005-04-13 00:01:46 +00:00
|
|
|
}
|
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
static struct unpcb *
|
|
|
|
fptounp(struct file *fp)
|
|
|
|
{
|
|
|
|
struct socket *so;
|
|
|
|
|
|
|
|
if (fp->f_type != DTYPE_SOCKET)
|
|
|
|
return (NULL);
|
|
|
|
if ((so = fp->f_data) == NULL)
|
|
|
|
return (NULL);
|
|
|
|
if (so->so_proto->pr_domain != &localdomain)
|
|
|
|
return (NULL);
|
|
|
|
return sotounpcb(so);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
unp_discard(struct file *fp)
|
|
|
|
{
|
|
|
|
|
|
|
|
unp_externalize_fp(fp);
|
|
|
|
(void) closef(fp, (struct thread *)NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
unp_internalize_fp(struct file *fp)
|
|
|
|
{
|
|
|
|
struct unpcb *unp;
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
if ((unp = fptounp(fp)) != NULL) {
|
|
|
|
unp->unp_file = fp;
|
|
|
|
unp->unp_msgcount++;
|
|
|
|
}
|
2008-01-01 01:46:42 +00:00
|
|
|
fhold(fp);
|
2007-12-30 01:42:15 +00:00
|
|
|
unp_rights++;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
unp_externalize_fp(struct file *fp)
|
|
|
|
{
|
|
|
|
struct unpcb *unp;
|
|
|
|
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WLOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
if ((unp = fptounp(fp)) != NULL)
|
|
|
|
unp->unp_msgcount--;
|
|
|
|
unp_rights--;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LINK_WUNLOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
}
|
|
|
|
|
2004-08-25 21:24:36 +00:00
|
|
|
/*
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
* unp_defer indicates whether additional work has been defered for a future
|
|
|
|
* pass through unp_gc(). It is thread local and does not require explicit
|
|
|
|
* synchronization.
|
2004-08-25 21:24:36 +00:00
|
|
|
*/
|
2007-12-30 01:42:15 +00:00
|
|
|
static int unp_marked;
|
|
|
|
static int unp_unreachable;
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
static void
|
|
|
|
unp_accessable(struct file *fp)
|
|
|
|
{
|
|
|
|
struct unpcb *unp;
|
|
|
|
|
2007-12-31 03:44:54 +00:00
|
|
|
if ((unp = fptounp(fp)) == NULL)
|
2007-12-30 01:42:15 +00:00
|
|
|
return;
|
|
|
|
if (unp->unp_gcflag & UNPGC_REF)
|
|
|
|
return;
|
|
|
|
unp->unp_gcflag &= ~UNPGC_DEAD;
|
|
|
|
unp->unp_gcflag |= UNPGC_REF;
|
|
|
|
unp_marked++;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
unp_gc_process(struct unpcb *unp)
|
|
|
|
{
|
|
|
|
struct socket *soa;
|
|
|
|
struct socket *so;
|
|
|
|
struct file *fp;
|
|
|
|
|
|
|
|
/* Already processed. */
|
|
|
|
if (unp->unp_gcflag & UNPGC_SCANNED)
|
|
|
|
return;
|
|
|
|
fp = unp->unp_file;
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* Check for a socket potentially in a cycle. It must be in a
|
|
|
|
* queue as indicated by msgcount, and this must equal the file
|
|
|
|
* reference count. Note that when msgcount is 0 the file is NULL.
|
|
|
|
*/
|
2008-01-01 01:46:42 +00:00
|
|
|
if ((unp->unp_gcflag & UNPGC_REF) == 0 && fp &&
|
|
|
|
unp->unp_msgcount != 0 && fp->f_count == unp->unp_msgcount) {
|
2007-12-30 01:42:15 +00:00
|
|
|
unp->unp_gcflag |= UNPGC_DEAD;
|
|
|
|
unp_unreachable++;
|
|
|
|
return;
|
|
|
|
}
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* Mark all sockets we reference with RIGHTS.
|
|
|
|
*/
|
|
|
|
so = unp->unp_socket;
|
|
|
|
SOCKBUF_LOCK(&so->so_rcv);
|
|
|
|
unp_scan(so->so_rcv.sb_mb, unp_accessable);
|
|
|
|
SOCKBUF_UNLOCK(&so->so_rcv);
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* Mark all sockets in our accept queue.
|
|
|
|
*/
|
|
|
|
ACCEPT_LOCK();
|
|
|
|
TAILQ_FOREACH(soa, &so->so_comp, so_list) {
|
|
|
|
SOCKBUF_LOCK(&soa->so_rcv);
|
|
|
|
unp_scan(soa->so_rcv.sb_mb, unp_accessable);
|
|
|
|
SOCKBUF_UNLOCK(&soa->so_rcv);
|
|
|
|
}
|
|
|
|
ACCEPT_UNLOCK();
|
|
|
|
unp->unp_gcflag |= UNPGC_SCANNED;
|
|
|
|
}
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
|
|
|
|
static int unp_recycled;
|
2008-07-26 00:55:35 +00:00
|
|
|
SYSCTL_INT(_net_local, OID_AUTO, recycled, CTLFLAG_RD, &unp_recycled, 0,
|
|
|
|
"Number of unreachable sockets claimed by the garbage collector.");
|
1994-05-24 10:09:53 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
static int unp_taskcount;
|
2008-07-26 00:55:35 +00:00
|
|
|
SYSCTL_INT(_net_local, OID_AUTO, taskcount, CTLFLAG_RD, &unp_taskcount, 0,
|
|
|
|
"Number of times the garbage collector has run.");
|
2007-12-30 01:42:15 +00:00
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
unp_gc(__unused void *arg, int pending)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2009-10-05 14:49:16 +00:00
|
|
|
struct unp_head *heads[] = { &unp_dhead, &unp_shead, &unp_sphead,
|
|
|
|
NULL };
|
2007-12-30 01:42:15 +00:00
|
|
|
struct unp_head **head;
|
|
|
|
struct file **unref;
|
|
|
|
struct unpcb *unp;
|
|
|
|
int i;
|
1994-05-24 10:09:53 +00:00
|
|
|
|
Correct a number of serious and closely related bugs in the UNIX domain
socket file descriptor garbage collection code, which is intended to
detect and clear cycles of orphaned file descriptors that are "in-flight"
in a socket when that socket is closed before they are received. The
algorithm present was both run at poor times (resulting in recursion and
reentrance), and also buggy in the presence of parallelism. In order to
fix these problems, make the following changes:
- When there are in-flight sockets and a UNIX domain socket is destroyed,
asynchronously schedule the garbage collector, rather than running it
synchronously in the current context. This avoids lock order issues
when the garbage collection code reenters the UNIX domain socket code,
avoiding lock order reversals, deadlocks, etc. Run the code
asynchronously in a task queue.
- In the garbage collector, when skipping file descriptors that have
entered a closing state (i.e., have f_count == 0), re-test the FDEFER
flag, and decrement unp_defer. As file descriptors can now transition
to a closed state, while the garbage collector is running, it is no
longer the case that unp_defer will remain an accurate count of
deferred sockets in the mark portion of the GC algorithm. Otherwise,
the garbage collector will loop waiting waiting for unp_defer to reach
zero, which it will never do as it is skipping file descriptors that
were marked in an earlier pass, but now closed.
- Acquire the UNIX domain socket subsystem lock in unp_discard() when
modifying the unp_rights counter, or a read/write race is risked with
other threads also manipulating the counter.
While here:
- Remove #if 0'd code regarding acquiring the socket buffer sleep lock in
the garbage collector, this is not required as we are able to use the
socket buffer receive lock to protect scanning the receive buffer for
in-flight file descriptors on the socket buffer.
- Annotate that the description of the garbage collector implementation
is increasingly inaccurate and needs to be updated.
- Add counters of the number of deferred garbage collections and recycled
file descriptors. This will be removed and is here temporarily for
debugging purposes.
With these changes in place, the unp_passfd regression test now appears
to be passed consistently on UP and SMP systems for extended runs,
whereas before it hung quickly or panicked, depending on which bug was
triggered.
Reported by: Philip Kizer <pckizer at nostrum dot com>
MFC after: 2 weeks
2005-11-10 16:06:04 +00:00
|
|
|
unp_taskcount++;
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_LOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* First clear all gc flags from previous runs.
|
|
|
|
*/
|
|
|
|
for (head = heads; *head != NULL; head++)
|
|
|
|
LIST_FOREACH(unp, *head, unp_link)
|
2008-01-01 01:46:42 +00:00
|
|
|
unp->unp_gcflag = 0;
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2004-01-11 19:48:19 +00:00
|
|
|
/*
|
2007-12-30 01:42:15 +00:00
|
|
|
* Scan marking all reachable sockets with UNPGC_REF. Once a socket
|
|
|
|
* is reachable all of the sockets it references are reachable.
|
|
|
|
* Stop the scan once we do a complete loop without discovering
|
|
|
|
* a new reachable socket.
|
1996-12-05 22:41:13 +00:00
|
|
|
*/
|
1994-05-24 10:09:53 +00:00
|
|
|
do {
|
2007-12-30 01:42:15 +00:00
|
|
|
unp_unreachable = 0;
|
|
|
|
unp_marked = 0;
|
|
|
|
for (head = heads; *head != NULL; head++)
|
|
|
|
LIST_FOREACH(unp, *head, unp_link)
|
|
|
|
unp_gc_process(unp);
|
|
|
|
} while (unp_marked);
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_UNLOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
if (unp_unreachable == 0)
|
|
|
|
return;
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* Allocate space for a local list of dead unpcbs.
|
|
|
|
*/
|
|
|
|
unref = malloc(unp_unreachable * sizeof(struct file *),
|
|
|
|
M_TEMP, M_WAITOK);
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* Iterate looking for sockets which have been specifically marked
|
|
|
|
* as as unreachable and store them locally.
|
|
|
|
*/
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_LOCK();
|
2007-12-30 01:42:15 +00:00
|
|
|
for (i = 0, head = heads; *head != NULL; head++)
|
|
|
|
LIST_FOREACH(unp, *head, unp_link)
|
|
|
|
if (unp->unp_gcflag & UNPGC_DEAD) {
|
|
|
|
unref[i++] = unp->unp_file;
|
2008-01-01 01:46:42 +00:00
|
|
|
fhold(unp->unp_file);
|
2007-12-30 01:42:15 +00:00
|
|
|
KASSERT(unp->unp_file != NULL,
|
|
|
|
("unp_gc: Invalid unpcb."));
|
|
|
|
KASSERT(i <= unp_unreachable,
|
|
|
|
("unp_gc: incorrect unreachable count."));
|
2007-01-05 19:59:46 +00:00
|
|
|
}
|
2009-03-08 21:48:29 +00:00
|
|
|
UNP_LIST_UNLOCK();
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2004-01-11 19:48:19 +00:00
|
|
|
/*
|
2007-12-30 01:42:15 +00:00
|
|
|
* Now flush all sockets, free'ing rights. This will free the
|
|
|
|
* struct files associated with these sockets but leave each socket
|
|
|
|
* with one remaining ref.
|
1996-12-05 22:41:13 +00:00
|
|
|
*/
|
2007-12-30 01:42:15 +00:00
|
|
|
for (i = 0; i < unp_unreachable; i++)
|
|
|
|
sorflush(unref[i]->f_data);
|
2008-10-03 09:01:55 +00:00
|
|
|
|
2007-12-30 01:42:15 +00:00
|
|
|
/*
|
|
|
|
* And finally release the sockets so they can be reclaimed.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < unp_unreachable; i++)
|
|
|
|
fdrop(unref[i], NULL);
|
|
|
|
unp_recycled += unp_unreachable;
|
|
|
|
free(unref, M_TEMP);
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
|
|
|
|
2008-10-03 13:01:56 +00:00
|
|
|
static void
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_dispose(struct mbuf *m)
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
1997-02-10 02:22:35 +00:00
|
|
|
|
1994-05-24 10:09:53 +00:00
|
|
|
if (m)
|
|
|
|
unp_scan(m, unp_discard);
|
|
|
|
}
|
|
|
|
|
1995-12-14 09:55:16 +00:00
|
|
|
static void
|
2005-02-20 23:22:13 +00:00
|
|
|
unp_scan(struct mbuf *m0, void (*op)(struct file *))
|
1994-05-24 10:09:53 +00:00
|
|
|
{
|
2001-10-04 13:11:48 +00:00
|
|
|
struct mbuf *m;
|
|
|
|
struct file **rp;
|
|
|
|
struct cmsghdr *cm;
|
|
|
|
void *data;
|
|
|
|
int i;
|
|
|
|
socklen_t clen, datalen;
|
1994-05-24 10:09:53 +00:00
|
|
|
int qfds;
|
|
|
|
|
2004-03-30 02:16:25 +00:00
|
|
|
while (m0 != NULL) {
|
2001-10-04 13:11:48 +00:00
|
|
|
for (m = m0; m; m = m->m_next) {
|
2001-10-29 20:04:03 +00:00
|
|
|
if (m->m_type != MT_CONTROL)
|
2001-10-04 13:11:48 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
cm = mtod(m, struct cmsghdr *);
|
|
|
|
clen = m->m_len;
|
|
|
|
|
|
|
|
while (cm != NULL) {
|
|
|
|
if (sizeof(*cm) > clen || cm->cmsg_len > clen)
|
|
|
|
break;
|
|
|
|
|
|
|
|
data = CMSG_DATA(cm);
|
|
|
|
datalen = (caddr_t)cm + cm->cmsg_len
|
|
|
|
- (caddr_t)data;
|
|
|
|
|
|
|
|
if (cm->cmsg_level == SOL_SOCKET &&
|
|
|
|
cm->cmsg_type == SCM_RIGHTS) {
|
|
|
|
qfds = datalen / sizeof (struct file *);
|
|
|
|
rp = data;
|
|
|
|
for (i = 0; i < qfds; i++)
|
|
|
|
(*op)(*rp++);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CMSG_SPACE(datalen) < clen) {
|
|
|
|
clen -= CMSG_SPACE(datalen);
|
|
|
|
cm = (struct cmsghdr *)
|
|
|
|
((caddr_t)cm + CMSG_SPACE(datalen));
|
|
|
|
} else {
|
|
|
|
clen = 0;
|
|
|
|
cm = NULL;
|
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
}
|
2001-10-04 13:11:48 +00:00
|
|
|
}
|
1994-05-24 10:09:53 +00:00
|
|
|
m0 = m0->m_act;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-29 12:36:00 +00:00
|
|
|
#ifdef DDB
|
|
|
|
static void
|
|
|
|
db_print_indent(int indent)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < indent; i++)
|
|
|
|
db_printf(" ");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
db_print_unpflags(int unp_flags)
|
|
|
|
{
|
|
|
|
int comma;
|
|
|
|
|
|
|
|
comma = 0;
|
|
|
|
if (unp_flags & UNP_HAVEPC) {
|
|
|
|
db_printf("%sUNP_HAVEPC", comma ? ", " : "");
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
if (unp_flags & UNP_HAVEPCCACHED) {
|
|
|
|
db_printf("%sUNP_HAVEPCCACHED", comma ? ", " : "");
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
if (unp_flags & UNP_WANTCRED) {
|
|
|
|
db_printf("%sUNP_WANTCRED", comma ? ", " : "");
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
if (unp_flags & UNP_CONNWAIT) {
|
|
|
|
db_printf("%sUNP_CONNWAIT", comma ? ", " : "");
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
if (unp_flags & UNP_CONNECTING) {
|
|
|
|
db_printf("%sUNP_CONNECTING", comma ? ", " : "");
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
if (unp_flags & UNP_BINDING) {
|
|
|
|
db_printf("%sUNP_BINDING", comma ? ", " : "");
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
db_print_xucred(int indent, struct xucred *xu)
|
|
|
|
{
|
|
|
|
int comma, i;
|
|
|
|
|
|
|
|
db_print_indent(indent);
|
|
|
|
db_printf("cr_version: %u cr_uid: %u cr_ngroups: %d\n",
|
|
|
|
xu->cr_version, xu->cr_uid, xu->cr_ngroups);
|
|
|
|
db_print_indent(indent);
|
|
|
|
db_printf("cr_groups: ");
|
|
|
|
comma = 0;
|
|
|
|
for (i = 0; i < xu->cr_ngroups; i++) {
|
|
|
|
db_printf("%s%u", comma ? ", " : "", xu->cr_groups[i]);
|
|
|
|
comma = 1;
|
|
|
|
}
|
|
|
|
db_printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
db_print_unprefs(int indent, struct unp_head *uh)
|
|
|
|
{
|
|
|
|
struct unpcb *unp;
|
|
|
|
int counter;
|
|
|
|
|
|
|
|
counter = 0;
|
|
|
|
LIST_FOREACH(unp, uh, unp_reflink) {
|
|
|
|
if (counter % 4 == 0)
|
|
|
|
db_print_indent(indent);
|
|
|
|
db_printf("%p ", unp);
|
|
|
|
if (counter % 4 == 3)
|
|
|
|
db_printf("\n");
|
|
|
|
counter++;
|
|
|
|
}
|
|
|
|
if (counter != 0 && counter % 4 != 0)
|
|
|
|
db_printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
DB_SHOW_COMMAND(unpcb, db_show_unpcb)
|
|
|
|
{
|
|
|
|
struct unpcb *unp;
|
|
|
|
|
|
|
|
if (!have_addr) {
|
|
|
|
db_printf("usage: show unpcb <addr>\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
unp = (struct unpcb *)addr;
|
|
|
|
|
|
|
|
db_printf("unp_socket: %p unp_vnode: %p\n", unp->unp_socket,
|
|
|
|
unp->unp_vnode);
|
|
|
|
|
|
|
|
db_printf("unp_ino: %d unp_conn: %p\n", unp->unp_ino,
|
|
|
|
unp->unp_conn);
|
|
|
|
|
|
|
|
db_printf("unp_refs:\n");
|
|
|
|
db_print_unprefs(2, &unp->unp_refs);
|
|
|
|
|
|
|
|
/* XXXRW: Would be nice to print the full address, if any. */
|
|
|
|
db_printf("unp_addr: %p\n", unp->unp_addr);
|
|
|
|
|
|
|
|
db_printf("unp_cc: %d unp_mbcnt: %d unp_gencnt: %llu\n",
|
|
|
|
unp->unp_cc, unp->unp_mbcnt,
|
|
|
|
(unsigned long long)unp->unp_gencnt);
|
|
|
|
|
|
|
|
db_printf("unp_flags: %x (", unp->unp_flags);
|
|
|
|
db_print_unpflags(unp->unp_flags);
|
|
|
|
db_printf(")\n");
|
|
|
|
|
|
|
|
db_printf("unp_peercred:\n");
|
|
|
|
db_print_xucred(2, &unp->unp_peercred);
|
|
|
|
|
|
|
|
db_printf("unp_refcount: %u\n", unp->unp_refcount);
|
|
|
|
}
|
|
|
|
#endif
|