aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/kern/kern_descrip.c11
-rw-r--r--sys/kern/uipc_usrreq.c65
-rw-r--r--sys/sys/file.h1
-rw-r--r--sys/sys/unpcb.h1
4 files changed, 63 insertions, 15 deletions
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
index 685222c7893f..590a3875b170 100644
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -2181,6 +2181,17 @@ fdrop_locked(struct file *fp, struct thread *td)
FILE_UNLOCK(fp);
return (0);
}
+
+ /*
+ * We might have just dropped the last reference to a file
+ * object that is for a UNIX domain socket whose message
+ * buffers are being examined in unp_gc(). If that is the
+ * case, FWAIT will be set in f_gcflag and we need to wait for
+ * unp_gc() to finish its scan.
+ */
+ while (fp->f_gcflag & FWAIT)
+ msleep(&fp->f_gcflag, fp->f_mtxp, 0, "fpdrop", 0);
+
/* We have the last ref so we can proceed without the file lock. */
FILE_UNLOCK(fp);
if (fp->f_count < 0)
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 33a6ec2e589e..9645c8143060 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -285,6 +285,7 @@ uipc_attach(struct socket *so, int proto, struct thread *td)
unp->unp_socket = so;
so->so_pcb = unp;
+ unp->unp_refcount = 1;
UNP_LOCK();
unp->unp_gencnt = ++unp_gencnt;
unp_count++;
@@ -451,9 +452,10 @@ uipc_connect2(struct socket *so1, struct socket *so2)
static void
uipc_detach(struct socket *so)
{
- int local_unp_rights;
+ struct sockaddr_un *saved_unp_addr;
struct unpcb *unp;
struct vnode *vp;
+ int freeunp, local_unp_rights;
unp = sotounpcb(so);
KASSERT(unp != NULL, ("uipc_detach: unp == NULL"));
@@ -477,10 +479,15 @@ uipc_detach(struct socket *so)
}
unp->unp_socket->so_pcb = NULL;
local_unp_rights = unp_rights;
+ saved_unp_addr = unp->unp_addr;
+ unp->unp_addr = NULL;
+ unp->unp_refcount--;
+ freeunp = (unp->unp_refcount == 0);
UNP_UNLOCK();
- if (unp->unp_addr != NULL)
- FREE(unp->unp_addr, M_SONAME);
- uma_zfree(unp_zone, unp);
+ if (saved_unp_addr != NULL)
+ FREE(saved_unp_addr, M_SONAME);
+ if (freeunp)
+ uma_zfree(unp_zone, unp);
if (vp) {
int vfslocked;
@@ -1120,6 +1127,7 @@ static int
unp_pcblist(SYSCTL_HANDLER_ARGS)
{
int error, i, n;
+ int freeunp;
struct unpcb *unp, **unp_list;
unp_gen_t gencnt;
struct xunpgen *xug;
@@ -1171,6 +1179,7 @@ unp_pcblist(SYSCTL_HANDLER_ARGS)
unp->unp_socket->so_cred))
continue;
unp_list[i++] = unp;
+ unp->unp_refcount++;
}
}
UNP_UNLOCK();
@@ -1180,7 +1189,9 @@ unp_pcblist(SYSCTL_HANDLER_ARGS)
xu = malloc(sizeof(*xu), M_TEMP, M_WAITOK | M_ZERO);
for (i = 0; i < n; i++) {
unp = unp_list[i];
- if (unp->unp_gencnt <= gencnt) {
+ UNP_LOCK();
+ unp->unp_refcount--;
+ if (unp->unp_refcount != 0 && unp->unp_gencnt <= gencnt) {
xu->xu_len = sizeof *xu;
xu->xu_unpp = unp;
/*
@@ -1197,7 +1208,13 @@ unp_pcblist(SYSCTL_HANDLER_ARGS)
unp->unp_conn->unp_addr->sun_len);
bcopy(unp, &xu->xu_unp, sizeof *unp);
sotoxsocket(unp->unp_socket, &xu->xu_socket);
+ UNP_UNLOCK();
error = SYSCTL_OUT(req, xu, sizeof *xu);
+ } else {
+ freeunp = (unp->unp_refcount == 0);
+ UNP_UNLOCK();
+ if (freeunp)
+ uma_zfree(unp_zone, unp);
}
}
free(xu, M_TEMP);
@@ -1390,7 +1407,7 @@ unp_init(void)
{
unp_zone = uma_zcreate("unpcb", sizeof(struct unpcb), NULL, NULL,
- NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_NOFREE);
+ NULL, NULL, UMA_ALIGN_PTR, 0);
if (unp_zone == NULL)
panic("unp_init");
uma_zone_set_max(unp_zone, maxsockets);
@@ -1623,7 +1640,7 @@ unp_gc(__unused void *arg, int pending)
unp_taskcount++;
unp_defer = 0;
/*
- * Before going through all this, set all FDs to be NOT defered and
+ * Before going through all this, set all FDs to be NOT deferred and
* NOT externally accessible.
*/
sx_slock(&filelist_lock);
@@ -1648,16 +1665,16 @@ unp_gc(__unused void *arg, int pending)
continue;
}
/*
- * If we already marked it as 'defer' in a previous
- * pass, then try process it this time and un-mark
- * it.
+ * If we already marked it as 'defer' in a
+ * previous pass, then try to process it this
+ * time and un-mark it.
*/
if (fp->f_gcflag & FDEFER) {
fp->f_gcflag &= ~FDEFER;
unp_defer--;
} else {
/*
- * if it's not defered, then check if it's
+ * if it's not deferred, then check if it's
* already marked.. if so skip it
*/
if (fp->f_gcflag & FMARK) {
@@ -1680,7 +1697,7 @@ unp_gc(__unused void *arg, int pending)
fp->f_gcflag |= FMARK;
}
/*
- * Either it was defered, or it is externally
+ * Either it was deferred, or it is externally
* accessible and not already marked so. Now check
* if it is possibly one of OUR sockets.
*/
@@ -1689,13 +1706,23 @@ unp_gc(__unused void *arg, int pending)
FILE_UNLOCK(fp);
continue;
}
- FILE_UNLOCK(fp);
if (so->so_proto->pr_domain != &localdomain ||
- (so->so_proto->pr_flags&PR_RIGHTS) == 0)
+ (so->so_proto->pr_flags & PR_RIGHTS) == 0) {
+ FILE_UNLOCK(fp);
continue;
+ }
+
+ /*
+ * Tell any other threads that do a subsequent
+ * fdrop() that we are scanning the message
+ * buffers.
+ */
+ fp->f_gcflag |= FWAIT;
+ FILE_UNLOCK(fp);
+
/*
* So, Ok, it's one of our sockets and it IS
- * externally accessible (or was defered). Now we
+ * externally accessible (or was deferred). Now we
* look to see if we hold any file descriptors in its
* message buffers. Follow those links and mark them
* as accessible too.
@@ -1703,6 +1730,14 @@ unp_gc(__unused void *arg, int pending)
SOCKBUF_LOCK(&so->so_rcv);
unp_scan(so->so_rcv.sb_mb, unp_mark);
SOCKBUF_UNLOCK(&so->so_rcv);
+
+ /*
+ * Wake up any threads waiting in fdrop().
+ */
+ FILE_LOCK(fp);
+ fp->f_gcflag &= ~FWAIT;
+ wakeup(&fp->f_gcflag);
+ FILE_UNLOCK(fp);
}
} while (unp_defer);
sx_sunlock(&filelist_lock);
diff --git a/sys/sys/file.h b/sys/sys/file.h
index 16b0e35a09f7..58501fa83f37 100644
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -128,6 +128,7 @@ struct file {
short f_gcflag; /* used by thread doing fd garbage collection */
#define FMARK 0x1 /* mark during gc() */
#define FDEFER 0x2 /* defer for next gc pass */
+#define FWAIT 0x4 /* gc is scanning message buffers */
int f_msgcount; /* (f) references from message queue */
/* DTYPE_VNODE specific fields */
diff --git a/sys/sys/unpcb.h b/sys/sys/unpcb.h
index b910f035acd9..129583dbe804 100644
--- a/sys/sys/unpcb.h
+++ b/sys/sys/unpcb.h
@@ -78,6 +78,7 @@ struct unpcb {
unp_gen_t unp_gencnt; /* generation count of this instance */
int unp_flags; /* flags */
struct xucred unp_peercred; /* peer credentials, if applicable */
+ u_int unp_refcount;
};
/*