Author: kib
Date: Fri Dec  3 16:15:44 2010
New Revision: 216150
URL: http://svn.freebsd.org/changeset/base/216150

Log:
  Reviewed by:  jeff, rwatson
  MFC after:    1 week

Modified:
  head/sys/kern/uipc_usrreq.c

Modified: head/sys/kern/uipc_usrreq.c
==============================================================================
--- head/sys/kern/uipc_usrreq.c Fri Dec  3 16:07:50 2010        (r216149)
+++ head/sys/kern/uipc_usrreq.c Fri Dec  3 16:15:44 2010        (r216150)
@@ -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)
 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 
 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_A
           &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, infligh
  */
 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);
+}
+
+static void
+unp_process_defers(void *arg __unused, int pending)
+{
+       struct unp_defer *dr;
+       SLIST_HEAD(, unp_defer) drl;
+       int count;
 
-       unp_externalize_fp(fp);
-       (void) closef(fp, (struct thread *)NULL);
+       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);
 }
 
 /*
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to