Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/uipc_usrreq.c
Show First 20 Lines • Show All 185 Lines • ▼ Show 20 Lines | SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, | ||||
"File descriptors in flight."); | "File descriptors in flight."); | ||||
SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD, | SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD, | ||||
&unp_defers_count, 0, | &unp_defers_count, 0, | ||||
"File descriptors deferred to taskqueue for close."); | "File descriptors deferred to taskqueue for close."); | ||||
/* | /* | ||||
* Locking and synchronization: | * Locking and synchronization: | ||||
* | * | ||||
* Two types of locks exist in the local domain socket implementation: a | * Three types of locks exist in the local domain socket implementation: a | ||||
* a global linkage rwlock and per-unpcb mutexes. The linkage lock protects | * a global linkage rwlock, the mtxpool lock, and per-unpcb mutexes. | ||||
* the socket count, global generation number, stream/datagram global lists and | * The linkage lock protects the socket count, global generation number, | ||||
* interconnection of unpcbs, the v_socket and unp_vnode pointers, and can be | * and stream/datagram global lists. | ||||
* held exclusively over the acquisition of multiple unpcb locks to prevent | |||||
* deadlock. | |||||
* | * | ||||
* The mtxpool lock protects the vnode from being modified while referenced. | |||||
* Lock ordering requires that it be acquired before any unpcb locks. | |||||
* | |||||
* The unpcb lock (unp_mtx) protects all fields in the unpcb. Of particular | |||||
* note is that this includes the unp_conn field. So long as the unpcb lock | |||||
* is held the reference to the unpcb pointed to by unp_conn is valid. If we | |||||
* require that the unpcb pointed to by unp_conn remain live in cases where | |||||
* we need to drop the unp_mtx as when we need to acquire the lock for a | |||||
* second unpcb the caller must first acquire an additional reference on the | |||||
* second unpcb and then revalidate any state (typically check that unp_conn | |||||
* is non-NULL) upon requiring the initial unpcb lock. The lock ordering | |||||
* between unpcbs is the conventional ascending address order. Two helper | |||||
* routines exist for this: | |||||
* | |||||
* - unp_pcb_lock2(unp, unp2) - which just acquires the two locks in the | |||||
* safe ordering. | |||||
* | |||||
* - unp_pcb_owned_lock2(unp, unp2, freed) - the lock for unp is held | |||||
* when called. If unp is unlocked and unp2 is subsequently freed | |||||
* freed will be set to 1. | |||||
* | |||||
* The helper routines for references are: | |||||
* | |||||
* - unp_pcb_hold(unp): Can be called any time we currently hold a valid | |||||
* reference to unp. | |||||
* | |||||
* - unp_pcb_rele(unp): The caller must hold the unp lock. If we are | |||||
* releasing the last reference, detach must have been called thus | |||||
* unp->unp_socket be NULL. | |||||
* | |||||
* UNIX domain sockets each have an unpcb hung off of their so_pcb pointer, | * 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 | * 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 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, | * 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 | * 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 | * has a back-pointer to its socket, unp_socket, which will be stable under | ||||
* the same circumstances. | * the same circumstances. | ||||
* | * | ||||
* This pointer may only be safely dereferenced as long as a valid reference | * 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, | * 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 | * 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 | * 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, | * unp->unp_conn->unp_socket, you need to hold a lock on unp_conn to guarantee | ||||
* as unp_socket remains valid as long as the reference to unp_conn is valid. | * that detach is not run clearing unp_socket. | ||||
* | * | ||||
* 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 | |||||
* acquired at the same time only when holding the linkage rwlock | |||||
* exclusively, which prevents deadlocks. | |||||
* | |||||
* Blocking with UNIX domain sockets is a tricky issue: unlike most network | * Blocking with UNIX domain sockets is a tricky issue: unlike most network | ||||
* protocols, bind() is a non-atomic operation, and connect() requires | * protocols, bind() is a non-atomic operation, and connect() requires | ||||
* potential sleeping in the protocol, due to potentially waiting on local or | * potential sleeping in the protocol, due to potentially waiting on local or | ||||
* distributed file systems. We try to separate "lookup" operations, which | * distributed file systems. We try to separate "lookup" operations, which | ||||
* may sleep, and the IPC operations themselves, which typically can occur | * may sleep, and the IPC operations themselves, which typically can occur | ||||
* with relative atomicity as locks can be held over the entire operation. | * with relative atomicity as locks can be held over the entire operation. | ||||
* | * | ||||
* Another tricky issue is simultaneous multi-threaded or multi-process | * Another tricky issue is simultaneous multi-threaded or multi-process | ||||
Show All 21 Lines | #define UNP_LINK_WLOCK_ASSERT() rw_assert(&unp_link_rwlock, \ | ||||
RA_WLOCKED) | RA_WLOCKED) | ||||
#define UNP_LINK_WOWNED() rw_wowned(&unp_link_rwlock) | #define UNP_LINK_WOWNED() rw_wowned(&unp_link_rwlock) | ||||
#define UNP_DEFERRED_LOCK_INIT() mtx_init(&unp_defers_lock, \ | #define UNP_DEFERRED_LOCK_INIT() mtx_init(&unp_defers_lock, \ | ||||
"unp_defer", NULL, MTX_DEF) | "unp_defer", NULL, MTX_DEF) | ||||
#define UNP_DEFERRED_LOCK() mtx_lock(&unp_defers_lock) | #define UNP_DEFERRED_LOCK() mtx_lock(&unp_defers_lock) | ||||
#define UNP_DEFERRED_UNLOCK() mtx_unlock(&unp_defers_lock) | #define UNP_DEFERRED_UNLOCK() mtx_unlock(&unp_defers_lock) | ||||
#define UNP_REF_LIST_LOCK() UNP_DEFERRED_LOCK(); | |||||
#define UNP_REF_LIST_UNLOCK() UNP_DEFERRED_UNLOCK(); | |||||
#define UNP_PCB_LOCK_INIT(unp) mtx_init(&(unp)->unp_mtx, \ | #define UNP_PCB_LOCK_INIT(unp) mtx_init(&(unp)->unp_mtx, \ | ||||
"unp_mtx", "unp_mtx", \ | "unp_mtx", "unp_mtx", \ | ||||
MTX_DUPOK|MTX_DEF|MTX_RECURSE) | MTX_DUPOK|MTX_DEF) | ||||
#define UNP_PCB_LOCK_DESTROY(unp) mtx_destroy(&(unp)->unp_mtx) | #define UNP_PCB_LOCK_DESTROY(unp) mtx_destroy(&(unp)->unp_mtx) | ||||
#define UNP_PCB_LOCK(unp) mtx_lock(&(unp)->unp_mtx) | #define UNP_PCB_LOCK(unp) mtx_lock(&(unp)->unp_mtx) | ||||
#define UNP_PCB_TRYLOCK(unp) mtx_trylock(&(unp)->unp_mtx) | |||||
#define UNP_PCB_UNLOCK(unp) mtx_unlock(&(unp)->unp_mtx) | #define UNP_PCB_UNLOCK(unp) mtx_unlock(&(unp)->unp_mtx) | ||||
#define UNP_PCB_OWNED(unp) mtx_owned(&(unp)->unp_mtx) | |||||
#define UNP_PCB_LOCK_ASSERT(unp) mtx_assert(&(unp)->unp_mtx, MA_OWNED) | #define UNP_PCB_LOCK_ASSERT(unp) mtx_assert(&(unp)->unp_mtx, MA_OWNED) | ||||
#define UNP_PCB_UNLOCK_ASSERT(unp) mtx_assert(&(unp)->unp_mtx, MA_NOTOWNED) | |||||
static int uipc_connect2(struct socket *, struct socket *); | static int uipc_connect2(struct socket *, struct socket *); | ||||
static int uipc_ctloutput(struct socket *, struct sockopt *); | static int uipc_ctloutput(struct socket *, struct sockopt *); | ||||
static int unp_connect(struct socket *, struct sockaddr *, | static int unp_connect(struct socket *, struct sockaddr *, | ||||
struct thread *); | struct thread *); | ||||
static int unp_connectat(int, struct socket *, struct sockaddr *, | static int unp_connectat(int, struct socket *, struct sockaddr *, | ||||
struct thread *); | struct thread *); | ||||
static int unp_connect2(struct socket *so, struct socket *so2, int); | static int unp_connect2(struct socket *so, struct socket *so2, int); | ||||
Show All 9 Lines | |||||
static void unp_init(void); | static void unp_init(void); | ||||
static int unp_internalize(struct mbuf **, struct thread *); | static int unp_internalize(struct mbuf **, struct thread *); | ||||
static void unp_internalize_fp(struct file *); | static void unp_internalize_fp(struct file *); | ||||
static int unp_externalize(struct mbuf *, struct mbuf **, int); | static int unp_externalize(struct mbuf *, struct mbuf **, int); | ||||
static int unp_externalize_fp(struct file *); | static int unp_externalize_fp(struct file *); | ||||
static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *); | static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *); | ||||
static void unp_process_defers(void * __unused, int); | static void unp_process_defers(void * __unused, int); | ||||
static void | |||||
unp_pcb_hold(struct unpcb *unp) | |||||
{ | |||||
MPASS(unp->unp_refcount); | |||||
refcount_acquire(&unp->unp_refcount); | |||||
} | |||||
static int | |||||
unp_pcb_rele(struct unpcb *unp) | |||||
{ | |||||
int freed; | |||||
UNP_PCB_LOCK_ASSERT(unp); | |||||
MPASS(unp->unp_refcount); | |||||
if ((freed = refcount_release(&unp->unp_refcount))) { | |||||
/* we got here with having detached? */ | |||||
MPASS(unp->unp_socket == NULL); | |||||
UNP_PCB_UNLOCK(unp); | |||||
UNP_PCB_LOCK_DESTROY(unp); | |||||
uma_zfree(unp_zone, unp); | |||||
} | |||||
return (freed); | |||||
} | |||||
static void | |||||
unp_pcb_lock2(struct unpcb *unp, struct unpcb *unp2) | |||||
{ | |||||
UNP_PCB_UNLOCK_ASSERT(unp); | |||||
UNP_PCB_UNLOCK_ASSERT(unp2); | |||||
if ((uintptr_t)unp2 > (uintptr_t)unp) { | |||||
UNP_PCB_LOCK(unp); | |||||
UNP_PCB_LOCK(unp2); | |||||
} else { | |||||
UNP_PCB_LOCK(unp2); | |||||
UNP_PCB_LOCK(unp); | |||||
} | |||||
} | |||||
static __noinline void | |||||
unp_pcb_owned_lock2_slowpath(struct unpcb *unp, struct unpcb **unp2p, int *freed) | |||||
{ | |||||
struct unpcb *unp2; | |||||
unp2 = *unp2p; | |||||
unp_pcb_hold((unp2)); | |||||
UNP_PCB_UNLOCK((unp)); | |||||
UNP_PCB_LOCK((unp2)); | |||||
UNP_PCB_LOCK((unp)); | |||||
*freed = unp_pcb_rele((unp2)); | |||||
if (*freed) | |||||
*unp2p = NULL; | |||||
} | |||||
#define unp_pcb_owned_lock2(unp, unp2, freed) do { \ | |||||
freed = 0; \ | |||||
UNP_PCB_LOCK_ASSERT((unp)); \ | |||||
UNP_PCB_UNLOCK_ASSERT((unp2)); \ | |||||
if (__predict_true(UNP_PCB_TRYLOCK((unp2)))) \ | |||||
break; \ | |||||
else if ((uintptr_t)(unp2) > (uintptr_t)(unp)) \ | |||||
UNP_PCB_LOCK((unp2)); \ | |||||
else { \ | |||||
unp_pcb_owned_lock2_slowpath((unp), &(unp2), &freed); \ | |||||
} \ | |||||
} while (0) | |||||
/* | /* | ||||
* Definitions of protocols supported in the LOCAL domain. | * Definitions of protocols supported in the LOCAL domain. | ||||
*/ | */ | ||||
static struct domain localdomain; | static struct domain localdomain; | ||||
static struct pr_usrreqs uipc_usrreqs_dgram, uipc_usrreqs_stream; | static struct pr_usrreqs uipc_usrreqs_dgram, uipc_usrreqs_stream; | ||||
static struct pr_usrreqs uipc_usrreqs_seqpacket; | static struct pr_usrreqs uipc_usrreqs_seqpacket; | ||||
static struct protosw localsw[] = { | static struct protosw localsw[] = { | ||||
{ | { | ||||
Show All 39 Lines | |||||
static void | static void | ||||
uipc_abort(struct socket *so) | uipc_abort(struct socket *so) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("uipc_abort: unp == NULL")); | KASSERT(unp != NULL, ("uipc_abort: unp == NULL")); | ||||
UNP_PCB_UNLOCK_ASSERT(unp); | |||||
UNP_LINK_WLOCK(); | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
unp2 = unp->unp_conn; | unp2 = unp->unp_conn; | ||||
if (unp2 != NULL) { | if (unp2 != NULL) { | ||||
UNP_PCB_LOCK(unp2); | unp_pcb_hold(unp2); | ||||
UNP_PCB_UNLOCK(unp); | |||||
unp_drop(unp2); | unp_drop(unp2); | ||||
UNP_PCB_UNLOCK(unp2); | } else | ||||
} | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | |||||
} | } | ||||
static int | static int | ||||
uipc_accept(struct socket *so, struct sockaddr **nam) | uipc_accept(struct socket *so, struct sockaddr **nam) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
const struct sockaddr *sa; | const struct sockaddr *sa; | ||||
▲ Show 20 Lines • Show All 180 Lines • ▼ Show 20 Lines | #endif | ||||
if (error) { | if (error) { | ||||
vn_finished_write(mp); | vn_finished_write(mp); | ||||
goto error; | goto error; | ||||
} | } | ||||
vp = nd.ni_vp; | vp = nd.ni_vp; | ||||
ASSERT_VOP_ELOCKED(vp, "uipc_bind"); | ASSERT_VOP_ELOCKED(vp, "uipc_bind"); | ||||
soun = (struct sockaddr_un *)sodupsockaddr(nam, M_WAITOK); | soun = (struct sockaddr_un *)sodupsockaddr(nam, M_WAITOK); | ||||
UNP_LINK_WLOCK(); | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
VOP_UNP_BIND(vp, unp); | VOP_UNP_BIND(vp, unp); | ||||
unp->unp_vnode = vp; | unp->unp_vnode = vp; | ||||
unp->unp_addr = soun; | unp->unp_addr = soun; | ||||
unp->unp_flags &= ~UNP_BINDING; | unp->unp_flags &= ~UNP_BINDING; | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | |||||
VOP_UNLOCK(vp, 0); | VOP_UNLOCK(vp, 0); | ||||
vn_finished_write(mp); | vn_finished_write(mp); | ||||
free(buf, M_TEMP); | free(buf, M_TEMP); | ||||
return (0); | return (0); | ||||
error: | error: | ||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
unp->unp_flags &= ~UNP_BINDING; | unp->unp_flags &= ~UNP_BINDING; | ||||
Show All 10 Lines | |||||
} | } | ||||
static int | static int | ||||
uipc_connect(struct socket *so, struct sockaddr *nam, struct thread *td) | uipc_connect(struct socket *so, struct sockaddr *nam, struct thread *td) | ||||
{ | { | ||||
int error; | int error; | ||||
KASSERT(td == curthread, ("uipc_connect: td != curthread")); | KASSERT(td == curthread, ("uipc_connect: td != curthread")); | ||||
UNP_LINK_WLOCK(); | |||||
error = unp_connect(so, nam, td); | error = unp_connect(so, nam, td); | ||||
UNP_LINK_WUNLOCK(); | |||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
uipc_connectat(int fd, struct socket *so, struct sockaddr *nam, | uipc_connectat(int fd, struct socket *so, struct sockaddr *nam, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
int error; | int error; | ||||
KASSERT(td == curthread, ("uipc_connectat: td != curthread")); | KASSERT(td == curthread, ("uipc_connectat: td != curthread")); | ||||
UNP_LINK_WLOCK(); | |||||
error = unp_connectat(fd, so, nam, td); | error = unp_connectat(fd, so, nam, td); | ||||
UNP_LINK_WUNLOCK(); | |||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
uipc_close(struct socket *so) | uipc_close(struct socket *so) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
struct vnode *vp = NULL; | struct vnode *vp = NULL; | ||||
struct mtx *vplock; | |||||
int freed; | |||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("uipc_close: unp == NULL")); | KASSERT(unp != NULL, ("uipc_close: unp == NULL")); | ||||
UNP_LINK_WLOCK(); | |||||
vplock = NULL; | |||||
if ((vp = unp->unp_vnode) != NULL) { | |||||
vplock = mtx_pool_find(mtxpool_sleep, vp); | |||||
mtx_lock(vplock); | |||||
} | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
if (vp && unp->unp_vnode == NULL) { | |||||
mtx_unlock(vplock); | |||||
vp = NULL; | |||||
} | |||||
if (vp != NULL) { | |||||
VOP_UNP_DETACH(vp); | |||||
unp->unp_vnode = NULL; | |||||
} | |||||
unp2 = unp->unp_conn; | unp2 = unp->unp_conn; | ||||
unp_pcb_hold(unp); | |||||
if (unp2 != NULL) { | if (unp2 != NULL) { | ||||
UNP_PCB_LOCK(unp2); | unp_pcb_hold(unp2); | ||||
unp_pcb_owned_lock2(unp, unp2, freed); | |||||
unp_disconnect(unp, unp2); | unp_disconnect(unp, unp2); | ||||
if (unp_pcb_rele(unp2) == 0) | |||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
} | } | ||||
if (SOLISTENING(so) && ((vp = unp->unp_vnode) != NULL)) { | if (unp_pcb_rele(unp) == 0) | ||||
VOP_UNP_DETACH(vp); | |||||
unp->unp_vnode = NULL; | |||||
} | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | if (vp) { | ||||
if (vp) | mtx_unlock(vplock); | ||||
vrele(vp); | vrele(vp); | ||||
} | } | ||||
} | |||||
static int | static int | ||||
uipc_connect2(struct socket *so1, struct socket *so2) | uipc_connect2(struct socket *so1, struct socket *so2) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
int error; | int error; | ||||
UNP_LINK_WLOCK(); | |||||
unp = so1->so_pcb; | unp = so1->so_pcb; | ||||
KASSERT(unp != NULL, ("uipc_connect2: unp == NULL")); | KASSERT(unp != NULL, ("uipc_connect2: unp == NULL")); | ||||
UNP_PCB_LOCK(unp); | |||||
unp2 = so2->so_pcb; | unp2 = so2->so_pcb; | ||||
KASSERT(unp2 != NULL, ("uipc_connect2: unp2 == NULL")); | KASSERT(unp2 != NULL, ("uipc_connect2: unp2 == NULL")); | ||||
UNP_PCB_LOCK(unp2); | unp_pcb_lock2(unp, unp2); | ||||
error = unp_connect2(so1, so2, PRU_CONNECT2); | error = unp_connect2(so1, so2, PRU_CONNECT2); | ||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | |||||
return (error); | return (error); | ||||
} | } | ||||
static void | static void | ||||
uipc_detach(struct socket *so) | uipc_detach(struct socket *so) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
struct mtx *vplock; | |||||
struct sockaddr_un *saved_unp_addr; | struct sockaddr_un *saved_unp_addr; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
int freeunp, local_unp_rights; | int freeunp, local_unp_rights; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("uipc_detach: unp == NULL")); | KASSERT(unp != NULL, ("uipc_detach: unp == NULL")); | ||||
vp = NULL; | vp = NULL; | ||||
local_unp_rights = 0; | local_unp_rights = 0; | ||||
UNP_LINK_WLOCK(); | UNP_LINK_WLOCK(); | ||||
LIST_REMOVE(unp, unp_link); | LIST_REMOVE(unp, unp_link); | ||||
unp->unp_gencnt = ++unp_gencnt; | unp->unp_gencnt = ++unp_gencnt; | ||||
--unp_count; | --unp_count; | ||||
UNP_LINK_WUNLOCK(); | |||||
UNP_PCB_UNLOCK_ASSERT(unp); | |||||
restart: | |||||
if ((vp = unp->unp_vnode) != NULL) { | |||||
vplock = mtx_pool_find(mtxpool_sleep, vp); | |||||
mtx_lock(vplock); | |||||
} | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
if ((unp->unp_flags & UNP_NASCENT) != 0) | if ((unp2 = unp->unp_conn) != NULL) { | ||||
unp_pcb_owned_lock2(unp, unp2, freeunp); | |||||
if (freeunp) | |||||
unp2 = NULL; | |||||
} | |||||
if (unp->unp_vnode != vp && | |||||
unp->unp_vnode != NULL) { | |||||
mtx_unlock(vplock); | |||||
UNP_PCB_UNLOCK(unp); | |||||
if (unp2) | |||||
UNP_PCB_UNLOCK(unp2); | |||||
goto restart; | |||||
} | |||||
if ((unp->unp_flags & UNP_NASCENT) != 0) { | |||||
if (unp2) | |||||
UNP_PCB_UNLOCK(unp2); | |||||
goto teardown; | goto teardown; | ||||
} | |||||
if ((vp = unp->unp_vnode) != NULL) { | if ((vp = unp->unp_vnode) != NULL) { | ||||
VOP_UNP_DETACH(vp); | VOP_UNP_DETACH(vp); | ||||
unp->unp_vnode = NULL; | unp->unp_vnode = NULL; | ||||
} | } | ||||
unp2 = unp->unp_conn; | unp_pcb_hold(unp); | ||||
if (unp2 != NULL) { | if (unp2 != NULL) { | ||||
UNP_PCB_LOCK(unp2); | unp_pcb_hold(unp2); | ||||
unp_disconnect(unp, unp2); | unp_disconnect(unp, unp2); | ||||
if (unp_pcb_rele(unp2) == 0) | |||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
} | } | ||||
UNP_PCB_UNLOCK(unp); | |||||
/* | UNP_REF_LIST_LOCK(); | ||||
* We hold the linkage lock exclusively, so it's OK to acquire | |||||
* multiple pcb locks at a time. | |||||
*/ | |||||
while (!LIST_EMPTY(&unp->unp_refs)) { | while (!LIST_EMPTY(&unp->unp_refs)) { | ||||
struct unpcb *ref = LIST_FIRST(&unp->unp_refs); | struct unpcb *ref = LIST_FIRST(&unp->unp_refs); | ||||
UNP_PCB_LOCK(ref); | unp_pcb_hold(ref); | ||||
UNP_REF_LIST_UNLOCK(); | |||||
MPASS(ref != unp); | |||||
UNP_PCB_UNLOCK_ASSERT(ref); | |||||
unp_drop(ref); | unp_drop(ref); | ||||
UNP_PCB_UNLOCK(ref); | UNP_REF_LIST_LOCK(); | ||||
} | } | ||||
UNP_REF_LIST_UNLOCK(); | |||||
UNP_PCB_LOCK(unp); | |||||
freeunp = unp_pcb_rele(unp); | |||||
MPASS(freeunp == 0); | |||||
local_unp_rights = unp_rights; | local_unp_rights = unp_rights; | ||||
teardown: | teardown: | ||||
UNP_LINK_WUNLOCK(); | |||||
unp->unp_socket->so_pcb = NULL; | unp->unp_socket->so_pcb = NULL; | ||||
saved_unp_addr = unp->unp_addr; | saved_unp_addr = unp->unp_addr; | ||||
unp->unp_addr = NULL; | unp->unp_addr = NULL; | ||||
unp->unp_refcount--; | unp->unp_socket = NULL; | ||||
freeunp = (unp->unp_refcount == 0); | freeunp = unp_pcb_rele(unp); | ||||
if (saved_unp_addr != NULL) | if (saved_unp_addr != NULL) | ||||
free(saved_unp_addr, M_SONAME); | free(saved_unp_addr, M_SONAME); | ||||
if (freeunp) { | if (!freeunp) | ||||
UNP_PCB_LOCK_DESTROY(unp); | |||||
uma_zfree(unp_zone, unp); | |||||
} else | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
if (vp) | if (vp) { | ||||
mtx_unlock(vplock); | |||||
vrele(vp); | vrele(vp); | ||||
} | |||||
if (local_unp_rights) | if (local_unp_rights) | ||||
taskqueue_enqueue_timeout(taskqueue_thread, &unp_gc_task, -1); | taskqueue_enqueue_timeout(taskqueue_thread, &unp_gc_task, -1); | ||||
} | } | ||||
static int | static int | ||||
uipc_disconnect(struct socket *so) | uipc_disconnect(struct socket *so) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
int freed; | |||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("uipc_disconnect: unp == NULL")); | KASSERT(unp != NULL, ("uipc_disconnect: unp == NULL")); | ||||
UNP_LINK_WLOCK(); | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
unp2 = unp->unp_conn; | if ((unp2 = unp->unp_conn) == NULL) { | ||||
if (unp2 != NULL) { | UNP_PCB_UNLOCK(unp); | ||||
UNP_PCB_LOCK(unp2); | return (0); | ||||
unp_disconnect(unp, unp2); | |||||
UNP_PCB_UNLOCK(unp2); | |||||
} | } | ||||
unp_pcb_owned_lock2(unp, unp2, freed); | |||||
if (__predict_false(freed)) { | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | |||||
return (0); | return (0); | ||||
} | } | ||||
unp_pcb_hold(unp2); | |||||
unp_pcb_hold(unp); | |||||
unp_disconnect(unp, unp2); | |||||
if (unp_pcb_rele(unp) == 0) | |||||
UNP_PCB_UNLOCK(unp); | |||||
if (unp_pcb_rele(unp2) == 0) | |||||
UNP_PCB_UNLOCK(unp2); | |||||
return (0); | |||||
} | |||||
static int | static int | ||||
uipc_listen(struct socket *so, int backlog, struct thread *td) | uipc_listen(struct socket *so, int backlog, struct thread *td) | ||||
{ | { | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
int error; | int error; | ||||
if (so->so_type != SOCK_STREAM && so->so_type != SOCK_SEQPACKET) | if (so->so_type != SOCK_STREAM && so->so_type != SOCK_SEQPACKET) | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | uipc_rcvd(struct socket *so, int flags) | ||||
if (sbcc < so2->so_snd.sb_hiwat && mbcnt < so2->so_snd.sb_mbmax) | if (sbcc < so2->so_snd.sb_hiwat && mbcnt < so2->so_snd.sb_mbmax) | ||||
so2->so_snd.sb_flags &= ~SB_STOP; | so2->so_snd.sb_flags &= ~SB_STOP; | ||||
sowwakeup_locked(so2); | sowwakeup_locked(so2); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
connect_internal(struct socket *so, struct sockaddr *nam, struct thread *td) | |||||
{ | |||||
int error; | |||||
struct unpcb *unp; | |||||
unp = so->so_pcb; | |||||
if (unp->unp_conn != NULL) | |||||
return (EISCONN); | |||||
error = unp_connect(so, nam, td); | |||||
if (error) | |||||
return (error); | |||||
UNP_PCB_LOCK(unp); | |||||
if (unp->unp_conn == NULL) { | |||||
UNP_PCB_UNLOCK(unp); | |||||
if (error == 0) | |||||
error = ENOTCONN; | |||||
} | |||||
return (error); | |||||
} | |||||
static int | |||||
uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, | uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, | ||||
struct mbuf *control, struct thread *td) | struct mbuf *control, struct thread *td) | ||||
{ | { | ||||
struct unpcb *unp, *unp2; | struct unpcb *unp, *unp2; | ||||
struct socket *so2; | struct socket *so2; | ||||
u_int mbcnt, sbcc; | u_int mbcnt, sbcc; | ||||
int error = 0; | int freed, error; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("%s: unp == NULL", __func__)); | KASSERT(unp != NULL, ("%s: unp == NULL", __func__)); | ||||
KASSERT(so->so_type == SOCK_STREAM || so->so_type == SOCK_DGRAM || | KASSERT(so->so_type == SOCK_STREAM || so->so_type == SOCK_DGRAM || | ||||
so->so_type == SOCK_SEQPACKET, | so->so_type == SOCK_SEQPACKET, | ||||
("%s: socktype %d", __func__, so->so_type)); | ("%s: socktype %d", __func__, so->so_type)); | ||||
freed = error = 0; | |||||
if (flags & PRUS_OOB) { | if (flags & PRUS_OOB) { | ||||
error = EOPNOTSUPP; | error = EOPNOTSUPP; | ||||
goto release; | goto release; | ||||
} | } | ||||
if (control != NULL && (error = unp_internalize(&control, td))) | if (control != NULL && (error = unp_internalize(&control, td))) | ||||
goto release; | goto release; | ||||
if ((nam != NULL) || (flags & PRUS_EOF)) | |||||
UNP_LINK_WLOCK(); | unp2 = NULL; | ||||
else | |||||
UNP_LINK_RLOCK(); | |||||
switch (so->so_type) { | switch (so->so_type) { | ||||
case SOCK_DGRAM: | case SOCK_DGRAM: | ||||
{ | { | ||||
const struct sockaddr *from; | const struct sockaddr *from; | ||||
unp2 = unp->unp_conn; | |||||
if (nam != NULL) { | if (nam != NULL) { | ||||
UNP_LINK_WLOCK_ASSERT(); | /* | ||||
if (unp2 != NULL) { | * We return with UNP_PCB_LOCK_HELD so we know that | ||||
error = EISCONN; | * the reference is live if the pointer is valid. | ||||
*/ | |||||
if ((error = connect_internal(so, nam, td))) | |||||
break; | break; | ||||
} | MPASS(unp->unp_conn != NULL); | ||||
error = unp_connect(so, nam, td); | |||||
if (error) | |||||
break; | |||||
unp2 = unp->unp_conn; | unp2 = unp->unp_conn; | ||||
} | } else { | ||||
UNP_PCB_LOCK(unp); | |||||
/* | /* | ||||
* Because connect() and send() are non-atomic in a sendto() | * Because connect() and send() are non-atomic in a sendto() | ||||
* with a target address, it's possible that the socket will | * with a target address, it's possible that the socket will | ||||
* have disconnected before the send() can run. In that case | * have disconnected before the send() can run. In that case | ||||
* return the slightly counter-intuitive but otherwise | * return the slightly counter-intuitive but otherwise | ||||
* correct error that the socket is not connected. | * correct error that the socket is not connected. | ||||
*/ | */ | ||||
if (unp2 == NULL) { | if ((unp2 = unp->unp_conn) == NULL) { | ||||
UNP_PCB_UNLOCK(unp); | |||||
error = ENOTCONN; | error = ENOTCONN; | ||||
break; | break; | ||||
} | } | ||||
/* Lockless read. */ | } | ||||
unp_pcb_owned_lock2(unp, unp2, freed); | |||||
if (__predict_false(freed)) { | |||||
UNP_PCB_UNLOCK(unp); | |||||
error = ENOTCONN; | |||||
break; | |||||
} | |||||
/* | |||||
* The socket referencing unp2 may have been closed | |||||
* or unp may have been disconnected if the unp lock | |||||
* was dropped to acquire unp2. | |||||
*/ | |||||
if (__predict_false(unp->unp_conn == NULL) || | |||||
unp2->unp_socket == NULL) { | |||||
UNP_PCB_UNLOCK(unp); | |||||
if (unp_pcb_rele(unp2) == 0) | |||||
UNP_PCB_UNLOCK(unp2); | |||||
error = ENOTCONN; | |||||
break; | |||||
} | |||||
if (unp2->unp_flags & UNP_WANTCRED) | if (unp2->unp_flags & UNP_WANTCRED) | ||||
control = unp_addsockcred(td, control); | control = unp_addsockcred(td, control); | ||||
UNP_PCB_LOCK(unp); | |||||
if (unp->unp_addr != NULL) | if (unp->unp_addr != NULL) | ||||
from = (struct sockaddr *)unp->unp_addr; | from = (struct sockaddr *)unp->unp_addr; | ||||
else | else | ||||
from = &sun_noname; | from = &sun_noname; | ||||
so2 = unp2->unp_socket; | so2 = unp2->unp_socket; | ||||
SOCKBUF_LOCK(&so2->so_rcv); | SOCKBUF_LOCK(&so2->so_rcv); | ||||
if (sbappendaddr_locked(&so2->so_rcv, from, m, | if (sbappendaddr_locked(&so2->so_rcv, from, m, | ||||
control)) { | control)) { | ||||
sorwakeup_locked(so2); | sorwakeup_locked(so2); | ||||
m = NULL; | m = NULL; | ||||
control = NULL; | control = NULL; | ||||
} else { | } else { | ||||
SOCKBUF_UNLOCK(&so2->so_rcv); | SOCKBUF_UNLOCK(&so2->so_rcv); | ||||
error = ENOBUFS; | error = ENOBUFS; | ||||
} | } | ||||
if (nam != NULL) { | if (nam != NULL) | ||||
UNP_LINK_WLOCK_ASSERT(); | |||||
UNP_PCB_LOCK(unp2); | |||||
unp_disconnect(unp, unp2); | unp_disconnect(unp, unp2); | ||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
} | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
break; | break; | ||||
} | } | ||||
case SOCK_SEQPACKET: | case SOCK_SEQPACKET: | ||||
case SOCK_STREAM: | case SOCK_STREAM: | ||||
if ((so->so_state & SS_ISCONNECTED) == 0) { | if ((so->so_state & SS_ISCONNECTED) == 0) { | ||||
if (nam != NULL) { | if (nam != NULL) { | ||||
UNP_LINK_WLOCK_ASSERT(); | if ((error = connect_internal(so, nam, td))) | ||||
error = unp_connect(so, nam, td); | break; | ||||
if (error) | |||||
break; /* XXX */ | |||||
} else { | } else { | ||||
error = ENOTCONN; | error = ENOTCONN; | ||||
break; | break; | ||||
} | } | ||||
} | } else if ((unp2 = unp->unp_conn) == NULL) { | ||||
error = ENOTCONN; | |||||
/* Lockless read. */ | break; | ||||
if (so->so_snd.sb_state & SBS_CANTSENDMORE) { | } else if (so->so_snd.sb_state & SBS_CANTSENDMORE) { | ||||
error = EPIPE; | error = EPIPE; | ||||
break; | break; | ||||
} else { | |||||
UNP_PCB_LOCK(unp); | |||||
if ((unp2 = unp->unp_conn) == NULL) { | |||||
UNP_PCB_UNLOCK(unp); | |||||
error = ENOTCONN; | |||||
break; | |||||
} | } | ||||
} | |||||
/* | unp_pcb_owned_lock2(unp, unp2, freed); | ||||
* Because connect() and send() are non-atomic in a sendto() | UNP_PCB_UNLOCK(unp); | ||||
* with a target address, it's possible that the socket will | if (__predict_false(freed)) { | ||||
* 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. | |||||
* | |||||
* Locking here must be done carefully: the linkage lock | |||||
* 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. | |||||
*/ | |||||
unp2 = unp->unp_conn; | |||||
if (unp2 == NULL) { | |||||
error = ENOTCONN; | error = ENOTCONN; | ||||
break; | break; | ||||
} | } | ||||
so2 = unp2->unp_socket; | if ((so2 = unp2->unp_socket) == NULL) { | ||||
UNP_PCB_LOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
error = ENOTCONN; | |||||
break; | |||||
} | |||||
SOCKBUF_LOCK(&so2->so_rcv); | SOCKBUF_LOCK(&so2->so_rcv); | ||||
if (unp2->unp_flags & UNP_WANTCRED) { | if (unp2->unp_flags & UNP_WANTCRED) { | ||||
/* | /* | ||||
* Credentials are passed only once on SOCK_STREAM | * Credentials are passed only once on SOCK_STREAM | ||||
* and SOCK_SEQPACKET. | * and SOCK_SEQPACKET. | ||||
*/ | */ | ||||
unp2->unp_flags &= ~UNP_WANTCRED; | unp2->unp_flags &= ~UNP_WANTCRED; | ||||
control = unp_addsockcred(td, control); | control = unp_addsockcred(td, control); | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | uipc_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *nam, | ||||
* PRUS_EOF is equivalent to pru_send followed by pru_shutdown. | * PRUS_EOF is equivalent to pru_send followed by pru_shutdown. | ||||
*/ | */ | ||||
if (flags & PRUS_EOF) { | if (flags & PRUS_EOF) { | ||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
socantsendmore(so); | socantsendmore(so); | ||||
unp_shutdown(unp); | unp_shutdown(unp); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
} | } | ||||
if ((nam != NULL) || (flags & PRUS_EOF)) | |||||
UNP_LINK_WUNLOCK(); | |||||
else | |||||
UNP_LINK_RUNLOCK(); | |||||
if (control != NULL && error != 0) | if (control != NULL && error != 0) | ||||
unp_dispose_mbuf(control); | unp_dispose_mbuf(control); | ||||
release: | release: | ||||
if (control != NULL) | if (control != NULL) | ||||
m_freem(control); | m_freem(control); | ||||
/* | /* | ||||
* In case of PRUS_NOTREADY, uipc_ready() is responsible | * In case of PRUS_NOTREADY, uipc_ready() is responsible | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | |||||
static int | static int | ||||
uipc_shutdown(struct socket *so) | uipc_shutdown(struct socket *so) | ||||
{ | { | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("uipc_shutdown: unp == NULL")); | KASSERT(unp != NULL, ("uipc_shutdown: unp == NULL")); | ||||
UNP_LINK_WLOCK(); | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
socantsendmore(so); | socantsendmore(so); | ||||
unp_shutdown(unp); | unp_shutdown(unp); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
uipc_sockaddr(struct socket *so, struct sockaddr **nam) | uipc_sockaddr(struct socket *so, struct sockaddr **nam) | ||||
{ | { | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
const struct sockaddr *sa; | const struct sockaddr *sa; | ||||
▲ Show 20 Lines • Show All 187 Lines • ▼ Show 20 Lines | unp_connectat(int fd, struct socket *so, struct sockaddr *nam, | ||||
struct sockaddr_un *soun = (struct sockaddr_un *)nam; | struct sockaddr_un *soun = (struct sockaddr_un *)nam; | ||||
struct vnode *vp; | struct vnode *vp; | ||||
struct socket *so2; | struct socket *so2; | ||||
struct unpcb *unp, *unp2, *unp3; | struct unpcb *unp, *unp2, *unp3; | ||||
struct nameidata nd; | struct nameidata nd; | ||||
char buf[SOCK_MAXADDRLEN]; | char buf[SOCK_MAXADDRLEN]; | ||||
struct sockaddr *sa; | struct sockaddr *sa; | ||||
cap_rights_t rights; | cap_rights_t rights; | ||||
int error, len; | int error, len, freed; | ||||
struct mtx *vplock; | |||||
if (nam->sa_family != AF_UNIX) | if (nam->sa_family != AF_UNIX) | ||||
return (EAFNOSUPPORT); | return (EAFNOSUPPORT); | ||||
UNP_LINK_WLOCK_ASSERT(); | |||||
unp = sotounpcb(so); | |||||
KASSERT(unp != NULL, ("unp_connect: unp == NULL")); | |||||
if (nam->sa_len > sizeof(struct sockaddr_un)) | if (nam->sa_len > sizeof(struct sockaddr_un)) | ||||
return (EINVAL); | return (EINVAL); | ||||
len = nam->sa_len - offsetof(struct sockaddr_un, sun_path); | len = nam->sa_len - offsetof(struct sockaddr_un, sun_path); | ||||
if (len <= 0) | if (len <= 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
bcopy(soun->sun_path, buf, len); | bcopy(soun->sun_path, buf, len); | ||||
buf[len] = 0; | buf[len] = 0; | ||||
unp = sotounpcb(so); | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
if (unp->unp_flags & UNP_CONNECTING) { | if (unp->unp_flags & UNP_CONNECTING) { | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
return (EALREADY); | return (EALREADY); | ||||
} | } | ||||
UNP_LINK_WUNLOCK(); | |||||
unp->unp_flags |= UNP_CONNECTING; | unp->unp_flags |= UNP_CONNECTING; | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
sa = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK); | sa = malloc(sizeof(struct sockaddr_un), M_SONAME, M_WAITOK); | ||||
NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF, | NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF, | ||||
UIO_SYSSPACE, buf, fd, cap_rights_init(&rights, CAP_CONNECTAT), td); | UIO_SYSSPACE, buf, fd, cap_rights_init(&rights, CAP_CONNECTAT), td); | ||||
error = namei(&nd); | error = namei(&nd); | ||||
if (error) | if (error) | ||||
Show All 16 Lines | |||||
#endif | #endif | ||||
error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td); | error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td); | ||||
if (error) | if (error) | ||||
goto bad; | goto bad; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("unp_connect: unp == NULL")); | KASSERT(unp != NULL, ("unp_connect: unp == NULL")); | ||||
/* | vplock = mtx_pool_find(mtxpool_sleep, vp); | ||||
* Lock linkage lock for two reasons: make sure v_socket is stable, | mtx_lock(vplock); | ||||
* and to protect simultaneous locking of multiple pcbs. | |||||
*/ | |||||
UNP_LINK_WLOCK(); | |||||
VOP_UNP_CONNECT(vp, &unp2); | VOP_UNP_CONNECT(vp, &unp2); | ||||
if (unp2 == NULL) { | if (unp2 == NULL) { | ||||
error = ECONNREFUSED; | error = ECONNREFUSED; | ||||
goto bad2; | goto bad2; | ||||
} | } | ||||
so2 = unp2->unp_socket; | so2 = unp2->unp_socket; | ||||
if (so->so_type != so2->so_type) { | if (so->so_type != so2->so_type) { | ||||
error = EPROTOTYPE; | error = EPROTOTYPE; | ||||
goto bad2; | goto bad2; | ||||
} | } | ||||
UNP_PCB_LOCK(unp); | unp_pcb_lock2(unp, unp2); | ||||
UNP_PCB_LOCK(unp2); | |||||
if (so->so_proto->pr_flags & PR_CONNREQUIRED) { | if (so->so_proto->pr_flags & PR_CONNREQUIRED) { | ||||
if (so2->so_options & SO_ACCEPTCONN) { | if (so2->so_options & SO_ACCEPTCONN) { | ||||
CURVNET_SET(so2->so_vnet); | CURVNET_SET(so2->so_vnet); | ||||
so2 = sonewconn(so2, 0); | so2 = sonewconn(so2, 0); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} else | } else | ||||
so2 = NULL; | so2 = NULL; | ||||
if (so2 == NULL) { | if (so2 == NULL) { | ||||
error = ECONNREFUSED; | error = ECONNREFUSED; | ||||
goto bad3; | goto bad3; | ||||
} | } | ||||
unp3 = sotounpcb(so2); | unp3 = sotounpcb(so2); | ||||
UNP_PCB_LOCK(unp3); | UNP_PCB_UNLOCK(unp); | ||||
unp_pcb_owned_lock2(unp2, unp3, freed); | |||||
MPASS(!freed); | |||||
if (unp2->unp_addr != NULL) { | if (unp2->unp_addr != NULL) { | ||||
bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len); | bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len); | ||||
unp3->unp_addr = (struct sockaddr_un *) sa; | unp3->unp_addr = (struct sockaddr_un *) sa; | ||||
sa = NULL; | sa = NULL; | ||||
} | } | ||||
/* | /* | ||||
* The connector's (client's) credentials are copied from its | * The connector's (client's) credentials are copied from its | ||||
Show All 10 Lines | if (so->so_proto->pr_flags & PR_CONNREQUIRED) { | ||||
*/ | */ | ||||
memcpy(&unp->unp_peercred, &unp2->unp_peercred, | memcpy(&unp->unp_peercred, &unp2->unp_peercred, | ||||
sizeof(unp->unp_peercred)); | sizeof(unp->unp_peercred)); | ||||
unp->unp_flags |= UNP_HAVEPC; | unp->unp_flags |= UNP_HAVEPC; | ||||
if (unp2->unp_flags & UNP_WANTCRED) | if (unp2->unp_flags & UNP_WANTCRED) | ||||
unp3->unp_flags |= UNP_WANTCRED; | unp3->unp_flags |= UNP_WANTCRED; | ||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
unp2 = unp3; | unp2 = unp3; | ||||
unp_pcb_owned_lock2(unp2, unp, freed); | |||||
MPASS(!freed); | |||||
#ifdef MAC | #ifdef MAC | ||||
mac_socketpeer_set_from_socket(so, so2); | mac_socketpeer_set_from_socket(so, so2); | ||||
mac_socketpeer_set_from_socket(so2, so); | mac_socketpeer_set_from_socket(so2, so); | ||||
#endif | #endif | ||||
} | } | ||||
KASSERT(unp2 != NULL && so2 != NULL && unp2->unp_socket == so2 && | KASSERT(unp2 != NULL && so2 != NULL && unp2->unp_socket == so2 && | ||||
sotounpcb(so2) == unp2, | sotounpcb(so2) == unp2, | ||||
("%s: unp2 %p so2 %p", __func__, unp2, so2)); | ("%s: unp2 %p so2 %p", __func__, unp2, so2)); | ||||
error = unp_connect2(so, so2, PRU_CONNECT); | error = unp_connect2(so, so2, PRU_CONNECT); | ||||
bad3: | bad3: | ||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
bad2: | bad2: | ||||
UNP_LINK_WUNLOCK(); | mtx_unlock(vplock); | ||||
bad: | bad: | ||||
if (vp != NULL) | if (vp != NULL) { | ||||
vput(vp); | vput(vp); | ||||
} | |||||
free(sa, M_SONAME); | free(sa, M_SONAME); | ||||
UNP_LINK_WLOCK(); | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
unp->unp_flags &= ~UNP_CONNECTING; | unp->unp_flags &= ~UNP_CONNECTING; | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
unp_connect2(struct socket *so, struct socket *so2, int req) | unp_connect2(struct socket *so, struct socket *so2, int req) | ||||
{ | { | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
struct unpcb *unp2; | struct unpcb *unp2; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("unp_connect2: unp == NULL")); | KASSERT(unp != NULL, ("unp_connect2: unp == NULL")); | ||||
unp2 = sotounpcb(so2); | unp2 = sotounpcb(so2); | ||||
KASSERT(unp2 != NULL, ("unp_connect2: unp2 == NULL")); | KASSERT(unp2 != NULL, ("unp_connect2: unp2 == NULL")); | ||||
UNP_LINK_WLOCK_ASSERT(); | |||||
UNP_PCB_LOCK_ASSERT(unp); | UNP_PCB_LOCK_ASSERT(unp); | ||||
UNP_PCB_LOCK_ASSERT(unp2); | UNP_PCB_LOCK_ASSERT(unp2); | ||||
if (so2->so_type != so->so_type) | if (so2->so_type != so->so_type) | ||||
return (EPROTOTYPE); | return (EPROTOTYPE); | ||||
unp2->unp_flags &= ~UNP_NASCENT; | unp2->unp_flags &= ~UNP_NASCENT; | ||||
unp->unp_conn = unp2; | unp->unp_conn = unp2; | ||||
unp_pcb_hold(unp2); | |||||
unp_pcb_hold(unp); | |||||
switch (so->so_type) { | switch (so->so_type) { | ||||
case SOCK_DGRAM: | case SOCK_DGRAM: | ||||
UNP_REF_LIST_LOCK(); | |||||
LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink); | LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink); | ||||
UNP_REF_LIST_UNLOCK(); | |||||
soisconnected(so); | soisconnected(so); | ||||
break; | break; | ||||
case SOCK_STREAM: | case SOCK_STREAM: | ||||
case SOCK_SEQPACKET: | case SOCK_SEQPACKET: | ||||
unp2->unp_conn = unp; | unp2->unp_conn = unp; | ||||
if (req == PRU_CONNECT && | if (req == PRU_CONNECT && | ||||
((unp->unp_flags | unp2->unp_flags) & UNP_CONNWAIT)) | ((unp->unp_flags | unp2->unp_flags) & UNP_CONNWAIT)) | ||||
soisconnecting(so); | soisconnecting(so); | ||||
else | else | ||||
soisconnected(so); | soisconnected(so); | ||||
soisconnected(so2); | soisconnected(so2); | ||||
break; | break; | ||||
default: | default: | ||||
panic("unp_connect2"); | panic("unp_connect2"); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
unp_disconnect(struct unpcb *unp, struct unpcb *unp2) | unp_disconnect(struct unpcb *unp, struct unpcb *unp2) | ||||
{ | { | ||||
struct socket *so; | struct socket *so, *so2; | ||||
int rele, freed; | |||||
KASSERT(unp2 != NULL, ("unp_disconnect: unp2 == NULL")); | KASSERT(unp2 != NULL, ("unp_disconnect: unp2 == NULL")); | ||||
UNP_LINK_WLOCK_ASSERT(); | |||||
UNP_PCB_LOCK_ASSERT(unp); | UNP_PCB_LOCK_ASSERT(unp); | ||||
UNP_PCB_LOCK_ASSERT(unp2); | UNP_PCB_LOCK_ASSERT(unp2); | ||||
if (unp->unp_conn == NULL && unp2->unp_conn == NULL) | |||||
return; | |||||
MPASS(unp->unp_conn == unp2); | |||||
unp->unp_conn = NULL; | unp->unp_conn = NULL; | ||||
rele = 0; | |||||
so = unp->unp_socket; | |||||
so2 = unp2->unp_socket; | |||||
switch (unp->unp_socket->so_type) { | switch (unp->unp_socket->so_type) { | ||||
case SOCK_DGRAM: | case SOCK_DGRAM: | ||||
UNP_REF_LIST_LOCK(); | |||||
LIST_REMOVE(unp, unp_reflink); | LIST_REMOVE(unp, unp_reflink); | ||||
so = unp->unp_socket; | UNP_REF_LIST_UNLOCK(); | ||||
if (so) { | |||||
SOCK_LOCK(so); | SOCK_LOCK(so); | ||||
so->so_state &= ~SS_ISCONNECTED; | so->so_state &= ~SS_ISCONNECTED; | ||||
SOCK_UNLOCK(so); | SOCK_UNLOCK(so); | ||||
} | |||||
break; | break; | ||||
case SOCK_STREAM: | case SOCK_STREAM: | ||||
case SOCK_SEQPACKET: | case SOCK_SEQPACKET: | ||||
soisdisconnected(unp->unp_socket); | if (so) | ||||
soisdisconnected(so); | |||||
MPASS(unp2->unp_conn == unp); | |||||
unp2->unp_conn = NULL; | unp2->unp_conn = NULL; | ||||
soisdisconnected(unp2->unp_socket); | if (so2) | ||||
soisdisconnected(so2); | |||||
break; | break; | ||||
} | } | ||||
freed = unp_pcb_rele(unp); | |||||
MPASS(freed == 0); | |||||
freed = unp_pcb_rele(unp2); | |||||
MPASS(freed == 0); | |||||
} | } | ||||
/* | /* | ||||
* unp_pcblist() walks the global list of struct unpcb's to generate a | * 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 | * 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 | * sequentially, validating the generation number on each to see if it has | ||||
* been detached. All of this is necessary because copyout() may sleep on | * been detached. All of this is necessary because copyout() may sleep on | ||||
* disk I/O. | * disk I/O. | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | for (unp = LIST_FIRST(head), i = 0; unp && i < n; | ||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
if (unp->unp_gencnt <= gencnt) { | if (unp->unp_gencnt <= gencnt) { | ||||
if (cr_cansee(req->td->td_ucred, | if (cr_cansee(req->td->td_ucred, | ||||
unp->unp_socket->so_cred)) { | unp->unp_socket->so_cred)) { | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
continue; | continue; | ||||
} | } | ||||
unp_list[i++] = unp; | unp_list[i++] = unp; | ||||
unp->unp_refcount++; | unp_pcb_hold(unp); | ||||
} | } | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
} | } | ||||
UNP_LINK_RUNLOCK(); | UNP_LINK_RUNLOCK(); | ||||
n = i; /* In case we lost some during malloc. */ | n = i; /* In case we lost some during malloc. */ | ||||
error = 0; | error = 0; | ||||
xu = malloc(sizeof(*xu), M_TEMP, M_WAITOK | M_ZERO); | xu = malloc(sizeof(*xu), M_TEMP, M_WAITOK | M_ZERO); | ||||
for (i = 0; i < n; i++) { | for (i = 0; i < n; i++) { | ||||
unp = unp_list[i]; | unp = unp_list[i]; | ||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
unp->unp_refcount--; | freeunp = unp_pcb_rele(unp); | ||||
if (unp->unp_refcount != 0 && unp->unp_gencnt <= gencnt) { | |||||
if (freeunp == 0 && unp->unp_gencnt <= gencnt) { | |||||
xu->xu_len = sizeof *xu; | xu->xu_len = sizeof *xu; | ||||
xu->xu_unpp = unp; | xu->xu_unpp = unp; | ||||
/* | /* | ||||
* XXX - need more locking here to protect against | * XXX - need more locking here to protect against | ||||
* connect/disconnect races for SMP. | * connect/disconnect races for SMP. | ||||
*/ | */ | ||||
if (unp->unp_addr != NULL) | if (unp->unp_addr != NULL) | ||||
bcopy(unp->unp_addr, &xu->xu_addr, | bcopy(unp->unp_addr, &xu->xu_addr, | ||||
Show All 10 Lines | if (freeunp == 0 && unp->unp_gencnt <= gencnt) { | ||||
xu->unp_vnode = unp->unp_vnode; | xu->unp_vnode = unp->unp_vnode; | ||||
xu->unp_conn = unp->unp_conn; | xu->unp_conn = unp->unp_conn; | ||||
xu->xu_firstref = LIST_FIRST(&unp->unp_refs); | xu->xu_firstref = LIST_FIRST(&unp->unp_refs); | ||||
xu->xu_nextref = LIST_NEXT(unp, unp_reflink); | xu->xu_nextref = LIST_NEXT(unp, unp_reflink); | ||||
xu->unp_gencnt = unp->unp_gencnt; | xu->unp_gencnt = unp->unp_gencnt; | ||||
sotoxsocket(unp->unp_socket, &xu->xu_socket); | sotoxsocket(unp->unp_socket, &xu->xu_socket); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
error = SYSCTL_OUT(req, xu, sizeof *xu); | error = SYSCTL_OUT(req, xu, sizeof *xu); | ||||
} else { | } else if (freeunp == 0) | ||||
freeunp = (unp->unp_refcount == 0); | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
if (freeunp) { | |||||
UNP_PCB_LOCK_DESTROY(unp); | |||||
uma_zfree(unp_zone, unp); | |||||
} | } | ||||
} | |||||
} | |||||
free(xu, M_TEMP); | free(xu, M_TEMP); | ||||
if (!error) { | if (!error) { | ||||
/* | /* | ||||
* Give the user an updated idea of our state. If the | * Give the user an updated idea of our state. If the | ||||
* generation differs from what we told her before, she knows | * generation differs from what we told her before, she knows | ||||
* that something happened while we were processing this | * that something happened while we were processing this | ||||
* request, and it might be necessary to retry. | * request, and it might be necessary to retry. | ||||
*/ | */ | ||||
Show All 19 Lines | SYSCTL_PROC(_net_local_seqpacket, OID_AUTO, pcblist, | ||||
"List of active local seqpacket sockets"); | "List of active local seqpacket sockets"); | ||||
static void | static void | ||||
unp_shutdown(struct unpcb *unp) | unp_shutdown(struct unpcb *unp) | ||||
{ | { | ||||
struct unpcb *unp2; | struct unpcb *unp2; | ||||
struct socket *so; | struct socket *so; | ||||
UNP_LINK_WLOCK_ASSERT(); | |||||
UNP_PCB_LOCK_ASSERT(unp); | UNP_PCB_LOCK_ASSERT(unp); | ||||
unp2 = unp->unp_conn; | unp2 = unp->unp_conn; | ||||
if ((unp->unp_socket->so_type == SOCK_STREAM || | if ((unp->unp_socket->so_type == SOCK_STREAM || | ||||
(unp->unp_socket->so_type == SOCK_SEQPACKET)) && unp2 != NULL) { | (unp->unp_socket->so_type == SOCK_SEQPACKET)) && unp2 != NULL) { | ||||
so = unp2->unp_socket; | so = unp2->unp_socket; | ||||
if (so != NULL) | if (so != NULL) | ||||
socantrcvmore(so); | socantrcvmore(so); | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
unp_drop(struct unpcb *unp) | unp_drop(struct unpcb *unp) | ||||
{ | { | ||||
struct socket *so = unp->unp_socket; | struct socket *so = unp->unp_socket; | ||||
struct unpcb *unp2; | struct unpcb *unp2; | ||||
int freed; | |||||
UNP_LINK_WLOCK_ASSERT(); | |||||
UNP_PCB_LOCK_ASSERT(unp); | |||||
/* | /* | ||||
* Regardless of whether the socket's peer dropped the connection | * Regardless of whether the socket's peer dropped the connection | ||||
* with this socket by aborting or disconnecting, POSIX requires | * with this socket by aborting or disconnecting, POSIX requires | ||||
* that ECONNRESET is returned. | * that ECONNRESET is returned. | ||||
*/ | */ | ||||
/* acquire a reference so that unp isn't freed from underneath us */ | |||||
UNP_PCB_LOCK(unp); | |||||
if (so) | |||||
so->so_error = ECONNRESET; | so->so_error = ECONNRESET; | ||||
unp2 = unp->unp_conn; | unp2 = unp->unp_conn; | ||||
if (unp2 == NULL) | if (unp2 != NULL) { | ||||
return; | unp_pcb_hold(unp2); | ||||
UNP_PCB_LOCK(unp2); | unp_pcb_owned_lock2(unp, unp2, freed); | ||||
unp_disconnect(unp, unp2); | unp_disconnect(unp, unp2); | ||||
if (unp_pcb_rele(unp2) == 0) | |||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
} | } | ||||
if (unp_pcb_rele(unp) == 0) | |||||
UNP_PCB_UNLOCK(unp); | |||||
} | |||||
static void | static void | ||||
unp_freerights(struct filedescent **fdep, int fdcount) | unp_freerights(struct filedescent **fdep, int fdcount) | ||||
{ | { | ||||
struct file *fp; | struct file *fp; | ||||
int i; | int i; | ||||
KASSERT(fdcount > 0, ("%s: fdcount %d", __func__, fdcount)); | KASSERT(fdcount > 0, ("%s: fdcount %d", __func__, fdcount)); | ||||
▲ Show 20 Lines • Show All 122 Lines • ▼ Show 20 Lines | |||||
unp_init(void) | unp_init(void) | ||||
{ | { | ||||
#ifdef VIMAGE | #ifdef VIMAGE | ||||
if (!IS_DEFAULT_VNET(curvnet)) | if (!IS_DEFAULT_VNET(curvnet)) | ||||
return; | return; | ||||
#endif | #endif | ||||
unp_zone = uma_zcreate("unpcb", sizeof(struct unpcb), NULL, NULL, | unp_zone = uma_zcreate("unpcb", sizeof(struct unpcb), NULL, NULL, | ||||
NULL, NULL, UMA_ALIGN_PTR, 0); | NULL, NULL, UMA_ALIGN_CACHE, 0); | ||||
if (unp_zone == NULL) | if (unp_zone == NULL) | ||||
panic("unp_init"); | panic("unp_init"); | ||||
uma_zone_set_max(unp_zone, maxsockets); | uma_zone_set_max(unp_zone, maxsockets); | ||||
uma_zone_set_warning(unp_zone, "kern.ipc.maxsockets limit reached"); | uma_zone_set_warning(unp_zone, "kern.ipc.maxsockets limit reached"); | ||||
EVENTHANDLER_REGISTER(maxsockets_change, unp_zone_change, | EVENTHANDLER_REGISTER(maxsockets_change, unp_zone_change, | ||||
NULL, EVENTHANDLER_PRI_ANY); | NULL, EVENTHANDLER_PRI_ANY); | ||||
LIST_INIT(&unp_dhead); | LIST_INIT(&unp_dhead); | ||||
LIST_INIT(&unp_shead); | LIST_INIT(&unp_shead); | ||||
▲ Show 20 Lines • Show All 566 Lines • ▼ Show 20 Lines | |||||
* For an active vnode it clears unp_vnode pointer and decrements unp_vnode | * For an active vnode it clears unp_vnode pointer and decrements unp_vnode | ||||
* use count. | * use count. | ||||
*/ | */ | ||||
void | void | ||||
vfs_unp_reclaim(struct vnode *vp) | vfs_unp_reclaim(struct vnode *vp) | ||||
{ | { | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
int active; | int active; | ||||
struct mtx *vplock; | |||||
ASSERT_VOP_ELOCKED(vp, "vfs_unp_reclaim"); | ASSERT_VOP_ELOCKED(vp, "vfs_unp_reclaim"); | ||||
KASSERT(vp->v_type == VSOCK, | KASSERT(vp->v_type == VSOCK, | ||||
("vfs_unp_reclaim: vp->v_type != VSOCK")); | ("vfs_unp_reclaim: vp->v_type != VSOCK")); | ||||
active = 0; | active = 0; | ||||
UNP_LINK_WLOCK(); | vplock = mtx_pool_find(mtxpool_sleep, vp); | ||||
mtx_lock(vplock); | |||||
VOP_UNP_CONNECT(vp, &unp); | VOP_UNP_CONNECT(vp, &unp); | ||||
if (unp == NULL) | if (unp == NULL) | ||||
goto done; | goto done; | ||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
if (unp->unp_vnode == vp) { | if (unp->unp_vnode == vp) { | ||||
VOP_UNP_DETACH(vp); | VOP_UNP_DETACH(vp); | ||||
unp->unp_vnode = NULL; | unp->unp_vnode = NULL; | ||||
active = 1; | active = 1; | ||||
} | } | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
done: | done: | ||||
UNP_LINK_WUNLOCK(); | mtx_unlock(vplock); | ||||
if (active) | if (active) | ||||
vunref(vp); | vunref(vp); | ||||
} | } | ||||
#ifdef DDB | #ifdef DDB | ||||
static void | static void | ||||
db_print_indent(int indent) | db_print_indent(int indent) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 106 Lines • Show Last 20 Lines |