Changeset View
Changeset View
Standalone View
Standalone View
head/sys/kern/uipc_usrreq.c
Show First 20 Lines • Show All 183 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: | ||||
* | * | ||||
* Three types of locks exit in the local domain socket implementation: a | * Two types of locks exist in the local domain socket implementation: a | ||||
* global list mutex, a global linkage rwlock, and per-unpcb mutexes. Of the | * a global linkage rwlock and per-unpcb mutexes. The linkage lock protects | ||||
* global locks, the list lock protects the socket count, global generation | * the socket count, global generation number, stream/datagram global lists and | ||||
* number, and stream/datagram global lists. The linkage lock protects the | |||||
* interconnection of unpcbs, the v_socket and unp_vnode pointers, and can be | * interconnection of unpcbs, the v_socket and unp_vnode pointers, and can be | ||||
* held exclusively over the acquisition of multiple unpcb locks to prevent | * held exclusively over the acquisition of multiple unpcb locks to prevent | ||||
* deadlock. | * deadlock. | ||||
* | * | ||||
* 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, | ||||
Show All 24 Lines | |||||
* | * | ||||
* Another tricky issue is simultaneous multi-threaded or multi-process | * Another tricky issue is simultaneous multi-threaded or multi-process | ||||
* access to a single UNIX domain socket. These are handled by the flags | * access to a single UNIX domain socket. These are handled by the flags | ||||
* UNP_CONNECTING and UNP_BINDING, which prevent concurrent connecting or | * UNP_CONNECTING and UNP_BINDING, which prevent concurrent connecting or | ||||
* binding, both of which involve dropping UNIX domain socket locks in order | * binding, both of which involve dropping UNIX domain socket locks in order | ||||
* to perform namei() and other file system operations. | * to perform namei() and other file system operations. | ||||
*/ | */ | ||||
static struct rwlock unp_link_rwlock; | static struct rwlock unp_link_rwlock; | ||||
static struct mtx unp_list_lock; | |||||
static struct mtx unp_defers_lock; | static struct mtx unp_defers_lock; | ||||
#define UNP_LINK_LOCK_INIT() rw_init(&unp_link_rwlock, \ | #define UNP_LINK_LOCK_INIT() rw_init(&unp_link_rwlock, \ | ||||
"unp_link_rwlock") | "unp_link_rwlock") | ||||
#define UNP_LINK_LOCK_ASSERT() rw_assert(&unp_link_rwlock, \ | #define UNP_LINK_LOCK_ASSERT() rw_assert(&unp_link_rwlock, \ | ||||
RA_LOCKED) | RA_LOCKED) | ||||
#define UNP_LINK_UNLOCK_ASSERT() rw_assert(&unp_link_rwlock, \ | #define UNP_LINK_UNLOCK_ASSERT() rw_assert(&unp_link_rwlock, \ | ||||
RA_UNLOCKED) | RA_UNLOCKED) | ||||
#define UNP_LINK_RLOCK() rw_rlock(&unp_link_rwlock) | #define UNP_LINK_RLOCK() rw_rlock(&unp_link_rwlock) | ||||
#define UNP_LINK_RUNLOCK() rw_runlock(&unp_link_rwlock) | #define UNP_LINK_RUNLOCK() rw_runlock(&unp_link_rwlock) | ||||
#define UNP_LINK_WLOCK() rw_wlock(&unp_link_rwlock) | #define UNP_LINK_WLOCK() rw_wlock(&unp_link_rwlock) | ||||
#define UNP_LINK_WUNLOCK() rw_wunlock(&unp_link_rwlock) | #define UNP_LINK_WUNLOCK() rw_wunlock(&unp_link_rwlock) | ||||
#define UNP_LINK_WLOCK_ASSERT() rw_assert(&unp_link_rwlock, \ | #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_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) | |||||
#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_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|MTX_RECURSE) | ||||
▲ Show 20 Lines • Show All 124 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
uipc_attach(struct socket *so, int proto, struct thread *td) | uipc_attach(struct socket *so, int proto, struct thread *td) | ||||
{ | { | ||||
u_long sendspace, recvspace; | u_long sendspace, recvspace; | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
int error; | int error; | ||||
bool locked; | |||||
KASSERT(so->so_pcb == NULL, ("uipc_attach: so_pcb != NULL")); | KASSERT(so->so_pcb == NULL, ("uipc_attach: so_pcb != NULL")); | ||||
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { | if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) { | ||||
switch (so->so_type) { | switch (so->so_type) { | ||||
case SOCK_STREAM: | case SOCK_STREAM: | ||||
sendspace = unpst_sendspace; | sendspace = unpst_sendspace; | ||||
recvspace = unpst_recvspace; | recvspace = unpst_recvspace; | ||||
break; | break; | ||||
Show All 18 Lines | uipc_attach(struct socket *so, int proto, struct thread *td) | ||||
unp = uma_zalloc(unp_zone, M_NOWAIT | M_ZERO); | unp = uma_zalloc(unp_zone, M_NOWAIT | M_ZERO); | ||||
if (unp == NULL) | if (unp == NULL) | ||||
return (ENOBUFS); | return (ENOBUFS); | ||||
LIST_INIT(&unp->unp_refs); | LIST_INIT(&unp->unp_refs); | ||||
UNP_PCB_LOCK_INIT(unp); | UNP_PCB_LOCK_INIT(unp); | ||||
unp->unp_socket = so; | unp->unp_socket = so; | ||||
so->so_pcb = unp; | so->so_pcb = unp; | ||||
unp->unp_refcount = 1; | unp->unp_refcount = 1; | ||||
if (so->so_head != NULL) | if (so->so_listen != NULL) | ||||
unp->unp_flags |= UNP_NASCENT; | unp->unp_flags |= UNP_NASCENT; | ||||
UNP_LIST_LOCK(); | if ((locked = UNP_LINK_WOWNED()) == false) | ||||
UNP_LINK_WLOCK(); | |||||
unp->unp_gencnt = ++unp_gencnt; | unp->unp_gencnt = ++unp_gencnt; | ||||
unp_count++; | unp_count++; | ||||
switch (so->so_type) { | switch (so->so_type) { | ||||
case SOCK_STREAM: | case SOCK_STREAM: | ||||
LIST_INSERT_HEAD(&unp_shead, unp, unp_link); | LIST_INSERT_HEAD(&unp_shead, unp, unp_link); | ||||
break; | break; | ||||
case SOCK_DGRAM: | case SOCK_DGRAM: | ||||
LIST_INSERT_HEAD(&unp_dhead, unp, unp_link); | LIST_INSERT_HEAD(&unp_dhead, unp, unp_link); | ||||
break; | break; | ||||
case SOCK_SEQPACKET: | case SOCK_SEQPACKET: | ||||
LIST_INSERT_HEAD(&unp_sphead, unp, unp_link); | LIST_INSERT_HEAD(&unp_sphead, unp, unp_link); | ||||
break; | break; | ||||
default: | default: | ||||
panic("uipc_attach"); | panic("uipc_attach"); | ||||
} | } | ||||
UNP_LIST_UNLOCK(); | |||||
if (locked == false) | |||||
UNP_LINK_WUNLOCK(); | |||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
uipc_bindat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td) | uipc_bindat(int fd, struct socket *so, struct sockaddr *nam, struct thread *td) | ||||
{ | { | ||||
struct sockaddr_un *soun = (struct sockaddr_un *)nam; | struct sockaddr_un *soun = (struct sockaddr_un *)nam; | ||||
struct vattr vattr; | struct vattr vattr; | ||||
▲ Show 20 Lines • Show All 137 Lines • ▼ Show 20 Lines | uipc_connectat(int fd, struct socket *so, struct sockaddr *nam, | ||||
UNP_LINK_WUNLOCK(); | 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; | |||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
KASSERT(unp != NULL, ("uipc_close: unp == NULL")); | KASSERT(unp != NULL, ("uipc_close: unp == NULL")); | ||||
UNP_LINK_WLOCK(); | 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_LOCK(unp2); | ||||
unp_disconnect(unp, unp2); | unp_disconnect(unp, unp2); | ||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
} | } | ||||
if (SOLISTENING(so) && ((vp = unp->unp_vnode) != NULL)) { | |||||
VOP_UNP_DETACH(vp); | |||||
unp->unp_vnode = NULL; | |||||
} | |||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
UNP_LINK_WUNLOCK(); | UNP_LINK_WUNLOCK(); | ||||
if (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; | ||||
Show All 20 Lines | uipc_detach(struct socket *so) | ||||
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_LIST_LOCK(); | 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_LIST_UNLOCK(); | |||||
if ((unp->unp_flags & UNP_NASCENT) != 0) { | |||||
UNP_PCB_LOCK(unp); | UNP_PCB_LOCK(unp); | ||||
if ((unp->unp_flags & UNP_NASCENT) != 0) | |||||
goto teardown; | goto teardown; | ||||
} | |||||
UNP_LINK_WLOCK(); | |||||
UNP_PCB_LOCK(unp); | |||||
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; | unp2 = unp->unp_conn; | ||||
if (unp2 != NULL) { | if (unp2 != NULL) { | ||||
UNP_PCB_LOCK(unp2); | UNP_PCB_LOCK(unp2); | ||||
unp_disconnect(unp, unp2); | unp_disconnect(unp, unp2); | ||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
} | } | ||||
/* | /* | ||||
* We hold the linkage lock exclusively, so it's OK to acquire | * We hold the linkage lock exclusively, so it's OK to acquire | ||||
* multiple pcb locks at a time. | * 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_LOCK(ref); | ||||
unp_drop(ref); | unp_drop(ref); | ||||
UNP_PCB_UNLOCK(ref); | UNP_PCB_UNLOCK(ref); | ||||
} | } | ||||
local_unp_rights = unp_rights; | local_unp_rights = unp_rights; | ||||
UNP_LINK_WUNLOCK(); | |||||
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_refcount--; | ||||
freeunp = (unp->unp_refcount == 0); | freeunp = (unp->unp_refcount == 0); | ||||
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) { | ||||
▲ Show 20 Lines • Show All 604 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
static int | static int | ||||
unp_connectat(int fd, struct socket *so, struct sockaddr *nam, | unp_connectat(int fd, struct socket *so, struct sockaddr *nam, | ||||
struct thread *td) | struct thread *td) | ||||
{ | { | ||||
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, *so3; | 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; | ||||
if (nam->sa_family != AF_UNIX) | if (nam->sa_family != AF_UNIX) | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | 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_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); | ||||
so3 = sonewconn(so2, 0); | so2 = sonewconn(so2, 0); | ||||
CURVNET_RESTORE(); | CURVNET_RESTORE(); | ||||
} else | } else | ||||
so3 = NULL; | so2 = NULL; | ||||
if (so3 == NULL) { | if (so2 == NULL) { | ||||
error = ECONNREFUSED; | error = ECONNREFUSED; | ||||
goto bad2; | goto bad3; | ||||
} | } | ||||
unp = sotounpcb(so); | unp3 = sotounpcb(so2); | ||||
unp2 = sotounpcb(so2); | |||||
unp3 = sotounpcb(so3); | |||||
UNP_PCB_LOCK(unp); | |||||
UNP_PCB_LOCK(unp2); | |||||
UNP_PCB_LOCK(unp3); | UNP_PCB_LOCK(unp3); | ||||
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; | ||||
} | } | ||||
/* | /* | ||||
Show All 9 Lines | if (so->so_proto->pr_flags & PR_CONNREQUIRED) { | ||||
* listen(); uipc_listen() cached that process's credentials | * listen(); uipc_listen() cached that process's credentials | ||||
* at that time so we can use them now. | * at that time so we can use them now. | ||||
*/ | */ | ||||
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(unp3); | |||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
UNP_PCB_UNLOCK(unp); | unp2 = unp3; | ||||
#ifdef MAC | #ifdef MAC | ||||
mac_socketpeer_set_from_socket(so, so3); | mac_socketpeer_set_from_socket(so, so2); | ||||
mac_socketpeer_set_from_socket(so3, so); | mac_socketpeer_set_from_socket(so2, so); | ||||
#endif | #endif | ||||
so2 = so3; | |||||
} | } | ||||
unp = sotounpcb(so); | |||||
KASSERT(unp != NULL, ("unp_connect: unp == NULL")); | KASSERT(unp2 != NULL && so2 != NULL && unp2->unp_socket == so2 && | ||||
unp2 = sotounpcb(so2); | sotounpcb(so2) == unp2, | ||||
KASSERT(unp2 != NULL, ("unp_connect: unp2 == NULL")); | ("%s: unp2 %p so2 %p", __func__, unp2, so2)); | ||||
UNP_PCB_LOCK(unp); | |||||
UNP_PCB_LOCK(unp2); | |||||
error = unp_connect2(so, so2, PRU_CONNECT); | error = unp_connect2(so, so2, PRU_CONNECT); | ||||
bad3: | |||||
UNP_PCB_UNLOCK(unp2); | UNP_PCB_UNLOCK(unp2); | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
bad2: | bad2: | ||||
UNP_LINK_WUNLOCK(); | UNP_LINK_WUNLOCK(); | ||||
bad: | bad: | ||||
if (vp != NULL) | if (vp != NULL) | ||||
vput(vp); | vput(vp); | ||||
free(sa, M_SONAME); | free(sa, M_SONAME); | ||||
▲ Show 20 Lines • Show All 125 Lines • ▼ Show 20 Lines | unp_pcblist(SYSCTL_HANDLER_ARGS) | ||||
if (req->newptr != NULL) | if (req->newptr != NULL) | ||||
return (EPERM); | return (EPERM); | ||||
/* | /* | ||||
* OK, now we're committed to doing something. | * OK, now we're committed to doing something. | ||||
*/ | */ | ||||
xug = malloc(sizeof(*xug), M_TEMP, M_WAITOK); | xug = malloc(sizeof(*xug), M_TEMP, M_WAITOK); | ||||
UNP_LIST_LOCK(); | UNP_LINK_RLOCK(); | ||||
gencnt = unp_gencnt; | gencnt = unp_gencnt; | ||||
n = unp_count; | n = unp_count; | ||||
UNP_LIST_UNLOCK(); | UNP_LINK_RUNLOCK(); | ||||
xug->xug_len = sizeof *xug; | xug->xug_len = sizeof *xug; | ||||
xug->xug_count = n; | xug->xug_count = n; | ||||
xug->xug_gen = gencnt; | xug->xug_gen = gencnt; | ||||
xug->xug_sogen = so_gencnt; | xug->xug_sogen = so_gencnt; | ||||
error = SYSCTL_OUT(req, xug, sizeof *xug); | error = SYSCTL_OUT(req, xug, sizeof *xug); | ||||
if (error) { | if (error) { | ||||
free(xug, M_TEMP); | free(xug, M_TEMP); | ||||
return (error); | return (error); | ||||
} | } | ||||
unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK); | unp_list = malloc(n * sizeof *unp_list, M_TEMP, M_WAITOK); | ||||
UNP_LIST_LOCK(); | UNP_LINK_RLOCK(); | ||||
for (unp = LIST_FIRST(head), i = 0; unp && i < n; | for (unp = LIST_FIRST(head), i = 0; unp && i < n; | ||||
unp = LIST_NEXT(unp, unp_link)) { | unp = LIST_NEXT(unp, unp_link)) { | ||||
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->unp_refcount++; | ||||
} | } | ||||
UNP_PCB_UNLOCK(unp); | UNP_PCB_UNLOCK(unp); | ||||
} | } | ||||
UNP_LIST_UNLOCK(); | 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--; | unp->unp_refcount--; | ||||
▲ Show 20 Lines • Show All 241 Lines • ▼ Show 20 Lines | 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); | ||||
LIST_INIT(&unp_sphead); | LIST_INIT(&unp_sphead); | ||||
SLIST_INIT(&unp_defers); | SLIST_INIT(&unp_defers); | ||||
TIMEOUT_TASK_INIT(taskqueue_thread, &unp_gc_task, 0, unp_gc, NULL); | TIMEOUT_TASK_INIT(taskqueue_thread, &unp_gc_task, 0, unp_gc, NULL); | ||||
TASK_INIT(&unp_defer_task, 0, unp_process_defers, NULL); | TASK_INIT(&unp_defer_task, 0, unp_process_defers, NULL); | ||||
UNP_LINK_LOCK_INIT(); | UNP_LINK_LOCK_INIT(); | ||||
UNP_LIST_LOCK_INIT(); | |||||
UNP_DEFERRED_LOCK_INIT(); | UNP_DEFERRED_LOCK_INIT(); | ||||
} | } | ||||
static int | static int | ||||
unp_internalize(struct mbuf **controlp, struct thread *td) | unp_internalize(struct mbuf **controlp, struct thread *td) | ||||
{ | { | ||||
struct mbuf *control = *controlp; | struct mbuf *control = *controlp; | ||||
struct proc *p = td->td_proc; | struct proc *p = td->td_proc; | ||||
▲ Show 20 Lines • Show All 334 Lines • ▼ Show 20 Lines | for (i = 0; i < fdcount; i++) { | ||||
unp->unp_gcflag |= UNPGC_REF; | unp->unp_gcflag |= UNPGC_REF; | ||||
unp_marked++; | unp_marked++; | ||||
} | } | ||||
} | } | ||||
static void | static void | ||||
unp_gc_process(struct unpcb *unp) | unp_gc_process(struct unpcb *unp) | ||||
{ | { | ||||
struct socket *soa; | struct socket *so, *soa; | ||||
struct socket *so; | |||||
struct file *fp; | struct file *fp; | ||||
/* Already processed. */ | /* Already processed. */ | ||||
if (unp->unp_gcflag & UNPGC_SCANNED) | if (unp->unp_gcflag & UNPGC_SCANNED) | ||||
return; | return; | ||||
fp = unp->unp_file; | fp = unp->unp_file; | ||||
/* | /* | ||||
* Check for a socket potentially in a cycle. It must be in a | * Check for a socket potentially in a cycle. It must be in a | ||||
* queue as indicated by msgcount, and this must equal the file | * queue as indicated by msgcount, and this must equal the file | ||||
* reference count. Note that when msgcount is 0 the file is NULL. | * reference count. Note that when msgcount is 0 the file is NULL. | ||||
*/ | */ | ||||
if ((unp->unp_gcflag & UNPGC_REF) == 0 && fp && | if ((unp->unp_gcflag & UNPGC_REF) == 0 && fp && | ||||
unp->unp_msgcount != 0 && fp->f_count == unp->unp_msgcount) { | unp->unp_msgcount != 0 && fp->f_count == unp->unp_msgcount) { | ||||
unp->unp_gcflag |= UNPGC_DEAD; | unp->unp_gcflag |= UNPGC_DEAD; | ||||
unp_unreachable++; | unp_unreachable++; | ||||
return; | return; | ||||
} | } | ||||
/* | |||||
* Mark all sockets we reference with RIGHTS. | |||||
*/ | |||||
so = unp->unp_socket; | so = unp->unp_socket; | ||||
if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) { | SOCK_LOCK(so); | ||||
SOCKBUF_LOCK(&so->so_rcv); | if (SOLISTENING(so)) { | ||||
unp_scan(so->so_rcv.sb_mb, unp_accessable); | |||||
SOCKBUF_UNLOCK(&so->so_rcv); | |||||
} | |||||
/* | /* | ||||
* Mark all sockets in our accept queue. | * Mark all sockets in our accept queue. | ||||
*/ | */ | ||||
ACCEPT_LOCK(); | TAILQ_FOREACH(soa, &so->sol_comp, so_list) { | ||||
TAILQ_FOREACH(soa, &so->so_comp, so_list) { | if (sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) | ||||
if ((sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) != 0) | |||||
continue; | continue; | ||||
SOCKBUF_LOCK(&soa->so_rcv); | SOCKBUF_LOCK(&soa->so_rcv); | ||||
unp_scan(soa->so_rcv.sb_mb, unp_accessable); | unp_scan(soa->so_rcv.sb_mb, unp_accessable); | ||||
SOCKBUF_UNLOCK(&soa->so_rcv); | SOCKBUF_UNLOCK(&soa->so_rcv); | ||||
} | } | ||||
ACCEPT_UNLOCK(); | } else { | ||||
/* | |||||
* Mark all sockets we reference with RIGHTS. | |||||
*/ | |||||
if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) { | |||||
SOCKBUF_LOCK(&so->so_rcv); | |||||
unp_scan(so->so_rcv.sb_mb, unp_accessable); | |||||
SOCKBUF_UNLOCK(&so->so_rcv); | |||||
} | |||||
} | |||||
SOCK_UNLOCK(so); | |||||
unp->unp_gcflag |= UNPGC_SCANNED; | unp->unp_gcflag |= UNPGC_SCANNED; | ||||
} | } | ||||
static int unp_recycled; | static int unp_recycled; | ||||
SYSCTL_INT(_net_local, OID_AUTO, recycled, CTLFLAG_RD, &unp_recycled, 0, | SYSCTL_INT(_net_local, OID_AUTO, recycled, CTLFLAG_RD, &unp_recycled, 0, | ||||
"Number of unreachable sockets claimed by the garbage collector."); | "Number of unreachable sockets claimed by the garbage collector."); | ||||
static int unp_taskcount; | static int unp_taskcount; | ||||
SYSCTL_INT(_net_local, OID_AUTO, taskcount, CTLFLAG_RD, &unp_taskcount, 0, | SYSCTL_INT(_net_local, OID_AUTO, taskcount, CTLFLAG_RD, &unp_taskcount, 0, | ||||
"Number of times the garbage collector has run."); | "Number of times the garbage collector has run."); | ||||
static void | static void | ||||
unp_gc(__unused void *arg, int pending) | unp_gc(__unused void *arg, int pending) | ||||
{ | { | ||||
struct unp_head *heads[] = { &unp_dhead, &unp_shead, &unp_sphead, | struct unp_head *heads[] = { &unp_dhead, &unp_shead, &unp_sphead, | ||||
NULL }; | NULL }; | ||||
struct unp_head **head; | struct unp_head **head; | ||||
struct file *f, **unref; | struct file *f, **unref; | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
int i, total; | int i, total; | ||||
unp_taskcount++; | unp_taskcount++; | ||||
UNP_LIST_LOCK(); | UNP_LINK_RLOCK(); | ||||
/* | /* | ||||
* First clear all gc flags from previous runs, apart from | * First clear all gc flags from previous runs, apart from | ||||
* UNPGC_IGNORE_RIGHTS. | * UNPGC_IGNORE_RIGHTS. | ||||
*/ | */ | ||||
for (head = heads; *head != NULL; head++) | for (head = heads; *head != NULL; head++) | ||||
LIST_FOREACH(unp, *head, unp_link) | LIST_FOREACH(unp, *head, unp_link) | ||||
unp->unp_gcflag = | unp->unp_gcflag = | ||||
(unp->unp_gcflag & UNPGC_IGNORE_RIGHTS); | (unp->unp_gcflag & UNPGC_IGNORE_RIGHTS); | ||||
/* | /* | ||||
* Scan marking all reachable sockets with UNPGC_REF. Once a socket | * Scan marking all reachable sockets with UNPGC_REF. Once a socket | ||||
* is reachable all of the sockets it references are reachable. | * is reachable all of the sockets it references are reachable. | ||||
* Stop the scan once we do a complete loop without discovering | * Stop the scan once we do a complete loop without discovering | ||||
* a new reachable socket. | * a new reachable socket. | ||||
*/ | */ | ||||
do { | do { | ||||
unp_unreachable = 0; | unp_unreachable = 0; | ||||
unp_marked = 0; | unp_marked = 0; | ||||
for (head = heads; *head != NULL; head++) | for (head = heads; *head != NULL; head++) | ||||
LIST_FOREACH(unp, *head, unp_link) | LIST_FOREACH(unp, *head, unp_link) | ||||
unp_gc_process(unp); | unp_gc_process(unp); | ||||
} while (unp_marked); | } while (unp_marked); | ||||
UNP_LIST_UNLOCK(); | UNP_LINK_RUNLOCK(); | ||||
if (unp_unreachable == 0) | if (unp_unreachable == 0) | ||||
return; | return; | ||||
/* | /* | ||||
* Allocate space for a local list of dead unpcbs. | * Allocate space for a local list of dead unpcbs. | ||||
*/ | */ | ||||
unref = malloc(unp_unreachable * sizeof(struct file *), | unref = malloc(unp_unreachable * sizeof(struct file *), | ||||
M_TEMP, M_WAITOK); | M_TEMP, M_WAITOK); | ||||
/* | /* | ||||
* Iterate looking for sockets which have been specifically marked | * Iterate looking for sockets which have been specifically marked | ||||
* as as unreachable and store them locally. | * as as unreachable and store them locally. | ||||
*/ | */ | ||||
UNP_LINK_RLOCK(); | UNP_LINK_RLOCK(); | ||||
UNP_LIST_LOCK(); | |||||
for (total = 0, head = heads; *head != NULL; head++) | for (total = 0, head = heads; *head != NULL; head++) | ||||
LIST_FOREACH(unp, *head, unp_link) | LIST_FOREACH(unp, *head, unp_link) | ||||
if ((unp->unp_gcflag & UNPGC_DEAD) != 0) { | if ((unp->unp_gcflag & UNPGC_DEAD) != 0) { | ||||
f = unp->unp_file; | f = unp->unp_file; | ||||
if (unp->unp_msgcount == 0 || f == NULL || | if (unp->unp_msgcount == 0 || f == NULL || | ||||
f->f_count != unp->unp_msgcount) | f->f_count != unp->unp_msgcount) | ||||
continue; | continue; | ||||
unref[total++] = f; | unref[total++] = f; | ||||
fhold(f); | fhold(f); | ||||
KASSERT(total <= unp_unreachable, | KASSERT(total <= unp_unreachable, | ||||
("unp_gc: incorrect unreachable count.")); | ("unp_gc: incorrect unreachable count.")); | ||||
} | } | ||||
UNP_LIST_UNLOCK(); | |||||
UNP_LINK_RUNLOCK(); | UNP_LINK_RUNLOCK(); | ||||
/* | /* | ||||
* Now flush all sockets, free'ing rights. This will free the | * Now flush all sockets, free'ing rights. This will free the | ||||
* struct files associated with these sockets but leave each socket | * struct files associated with these sockets but leave each socket | ||||
* with one remaining ref. | * with one remaining ref. | ||||
*/ | */ | ||||
for (i = 0; i < total; i++) { | for (i = 0; i < total; i++) { | ||||
Show All 26 Lines | |||||
* Synchronize against unp_gc, which can trip over data as we are freeing it. | * Synchronize against unp_gc, which can trip over data as we are freeing it. | ||||
*/ | */ | ||||
static void | static void | ||||
unp_dispose(struct socket *so) | unp_dispose(struct socket *so) | ||||
{ | { | ||||
struct unpcb *unp; | struct unpcb *unp; | ||||
unp = sotounpcb(so); | unp = sotounpcb(so); | ||||
UNP_LIST_LOCK(); | UNP_LINK_WLOCK(); | ||||
unp->unp_gcflag |= UNPGC_IGNORE_RIGHTS; | unp->unp_gcflag |= UNPGC_IGNORE_RIGHTS; | ||||
UNP_LIST_UNLOCK(); | UNP_LINK_WUNLOCK(); | ||||
if (!SOLISTENING(so)) | |||||
unp_dispose_mbuf(so->so_rcv.sb_mb); | unp_dispose_mbuf(so->so_rcv.sb_mb); | ||||
} | } | ||||
static void | static void | ||||
unp_scan(struct mbuf *m0, void (*op)(struct filedescent **, int)) | unp_scan(struct mbuf *m0, void (*op)(struct filedescent **, int)) | ||||
{ | { | ||||
struct mbuf *m; | struct mbuf *m; | ||||
struct cmsghdr *cm; | struct cmsghdr *cm; | ||||
void *data; | void *data; | ||||
▲ Show 20 Lines • Show All 181 Lines • Show Last 20 Lines |