diff options
author | kib <kib@FreeBSD.org> | 2010-12-03 16:15:44 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2010-12-03 16:15:44 +0000 |
commit | 44fb3ef253ef07efd282244795add8ecf7d10674 (patch) | |
tree | 6fefdf407d86d6f3d5e293364ea6ae0f552ff094 /sys/kern/uipc_usrreq.c | |
parent | ff0f2f76b263109af717dd850f6f5d51c4ad5e49 (diff) | |
download | FreeBSD-src-44fb3ef253ef07efd282244795add8ecf7d10674.zip FreeBSD-src-44fb3ef253ef07efd282244795add8ecf7d10674.tar.gz |
Reviewed by: jeff, rwatson
MFC after: 1 week
Diffstat (limited to 'sys/kern/uipc_usrreq.c')
-rw-r--r-- | sys/kern/uipc_usrreq.c | 79 |
1 files changed, 74 insertions, 5 deletions
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 63437bc..1c0903a 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -76,6 +76,7 @@ __FBSDID("$FreeBSD$"); #include <sys/namei.h> #include <sys/proc.h> #include <sys/protosw.h> +#include <sys/queue.h> #include <sys/resourcevar.h> #include <sys/rwlock.h> #include <sys/socket.h> @@ -115,6 +116,13 @@ static struct unp_head unp_shead; /* (l) List of stream sockets. */ static struct unp_head unp_dhead; /* (l) List of datagram sockets. */ static struct unp_head unp_sphead; /* (l) List of seqpacket sockets. */ +struct unp_defer { + SLIST_ENTRY(unp_defer) ud_link; + struct file *ud_fp; +}; +static SLIST_HEAD(, unp_defer) unp_defers; +static int unp_defers_count; + static const struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL }; /* @@ -126,6 +134,13 @@ static const struct sockaddr sun_noname = { sizeof(sun_noname), AF_LOCAL }; static struct task unp_gc_task; /* + * The close of unix domain sockets attached as SCM_RIGHTS is + * postponed to the taskqueue, to avoid arbitrary recursion depth. + * The attached sockets might have another sockets attached. + */ +static struct task unp_defer_task; + +/* * 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. @@ -164,6 +179,9 @@ SYSCTL_ULONG(_net_local_seqpacket, OID_AUTO, recvspace, CTLFLAG_RW, &unpsp_recvspace, 0, "Default seqpacket receive space."); SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, "File descriptors in flight."); +SYSCTL_INT(_net_local, OID_AUTO, deferred, CTLFLAG_RD, + &unp_defers_count, 0, + "File descriptors deferred to taskqueue for close."); /* * Locking and synchronization: @@ -213,6 +231,7 @@ SYSCTL_INT(_net_local, OID_AUTO, inflight, CTLFLAG_RD, &unp_rights, 0, */ static struct rwlock unp_link_rwlock; static struct mtx unp_list_lock; +static struct mtx unp_defers_lock; #define UNP_LINK_LOCK_INIT() rw_init(&unp_link_rwlock, \ "unp_link_rwlock") @@ -234,6 +253,11 @@ static struct mtx unp_list_lock; #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, \ + "unp_defer", NULL, MTX_DEF) +#define UNP_DEFERRED_LOCK() mtx_lock(&unp_defers_lock) +#define UNP_DEFERRED_UNLOCK() mtx_unlock(&unp_defers_lock) + #define UNP_PCB_LOCK_INIT(unp) mtx_init(&(unp)->unp_mtx, \ "unp_mtx", "unp_mtx", \ MTX_DUPOK|MTX_DEF|MTX_RECURSE) @@ -259,8 +283,9 @@ static void unp_init(void); static int unp_internalize(struct mbuf **, struct thread *); static void unp_internalize_fp(struct file *); static int unp_externalize(struct mbuf *, struct mbuf **); -static void unp_externalize_fp(struct file *); +static int unp_externalize_fp(struct file *); static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *); +static void unp_process_defers(void * __unused, int); /* * Definitions of protocols supported in the LOCAL domain. @@ -1764,9 +1789,12 @@ unp_init(void) LIST_INIT(&unp_dhead); LIST_INIT(&unp_shead); LIST_INIT(&unp_sphead); + SLIST_INIT(&unp_defers); TASK_INIT(&unp_gc_task, 0, unp_gc, NULL); + TASK_INIT(&unp_defer_task, 0, unp_process_defers, NULL); UNP_LINK_LOCK_INIT(); UNP_LIST_LOCK_INIT(); + UNP_DEFERRED_LOCK_INIT(); } static int @@ -1970,9 +1998,45 @@ fptounp(struct file *fp) static void unp_discard(struct file *fp) { + struct unp_defer *dr; + + if (unp_externalize_fp(fp)) { + dr = malloc(sizeof(*dr), M_TEMP, M_WAITOK); + dr->ud_fp = fp; + UNP_DEFERRED_LOCK(); + SLIST_INSERT_HEAD(&unp_defers, dr, ud_link); + UNP_DEFERRED_UNLOCK(); + atomic_add_int(&unp_defers_count, 1); + taskqueue_enqueue(taskqueue_thread, &unp_defer_task); + } else + (void) closef(fp, (struct thread *)NULL); +} - unp_externalize_fp(fp); - (void) closef(fp, (struct thread *)NULL); +static void +unp_process_defers(void *arg __unused, int pending) +{ + struct unp_defer *dr; + SLIST_HEAD(, unp_defer) drl; + int count; + + SLIST_INIT(&drl); + for (;;) { + UNP_DEFERRED_LOCK(); + if (SLIST_FIRST(&unp_defers) == NULL) { + UNP_DEFERRED_UNLOCK(); + break; + } + SLIST_SWAP(&unp_defers, &drl, unp_defer); + UNP_DEFERRED_UNLOCK(); + count = 0; + while ((dr = SLIST_FIRST(&drl)) != NULL) { + SLIST_REMOVE_HEAD(&drl, ud_link); + closef(dr->ud_fp, NULL); + free(dr, M_TEMP); + count++; + } + atomic_add_int(&unp_defers_count, -count); + } } static void @@ -1990,16 +2054,21 @@ unp_internalize_fp(struct file *fp) UNP_LINK_WUNLOCK(); } -static void +static int unp_externalize_fp(struct file *fp) { struct unpcb *unp; + int ret; UNP_LINK_WLOCK(); - if ((unp = fptounp(fp)) != NULL) + if ((unp = fptounp(fp)) != NULL) { unp->unp_msgcount--; + ret = 1; + } else + ret = 0; unp_rights--; UNP_LINK_WUNLOCK(); + return (ret); } /* |