struct zone *unp_zone;
static unp_gen_t unp_gencnt;
static u_int unp_count;
-static lck_mtx_t *unp_mutex;
+
+static lck_attr_t *unp_mtx_attr;
+static lck_grp_t *unp_mtx_grp;
+static lck_grp_attr_t *unp_mtx_grp_attr;
+static lck_rw_t *unp_list_mtx;
extern lck_mtx_t * uipc_lock;
static struct unp_head unp_shead, unp_dhead;
goto release;
}
- if (control && (error = unp_internalize(control, p)))
- goto release;
+ if (control) {
+ socket_unlock(so, 0); /* release global lock to avoid deadlock (4436174) */
+ error = unp_internalize(control, p);
+ socket_lock(so, 0);
+ if (error)
+ goto release;
+ }
switch (so->so_type) {
case SOCK_DGRAM:
if (unp == NULL)
return (ENOBUFS);
bzero(unp, sizeof *unp);
- lck_mtx_lock(unp_mutex);
+ lck_rw_lock_exclusive(unp_list_mtx);
LIST_INIT(&unp->unp_refs);
unp->unp_socket = so;
unp->unp_gencnt = ++unp_gencnt;
LIST_INSERT_HEAD(so->so_type == SOCK_DGRAM ? &unp_dhead
: &unp_shead, unp, unp_link);
so->so_pcb = (caddr_t)unp;
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return (0);
}
static void
unp_detach(struct unpcb *unp)
{
- lck_mtx_assert(unp_mutex, LCK_MTX_ASSERT_OWNED);
+ lck_rw_lock_exclusive(unp_list_mtx);
LIST_REMOVE(unp, unp_link);
unp->unp_gencnt = ++unp_gencnt;
+ lck_rw_done(unp_list_mtx);
--unp_count;
if (unp->unp_vnode) {
struct vnode *tvp = unp->unp_vnode;
if (error)
goto bad;
so2 = vp->v_socket;
- if (so2 == 0) {
+ if (so2 == 0 || so2->so_pcb == NULL ) {
error = ECONNREFUSED;
goto bad;
}
}
error = unp_connect2(so, so2);
bad:
- if (so2 != NULL)
- so2->so_usecount--; /* release count on socket */
+
+ if (so2 != NULL)
+ so2->so_usecount--; /* release count on socket */
+
vnode_put(vp);
return (error);
}
if (unp2 == 0)
return;
- lck_mtx_assert(unp_mutex, LCK_MTX_ASSERT_OWNED);
unp->unp_conn = 0;
switch (unp->unp_socket->so_type) {
case SOCK_DGRAM:
+ lck_rw_lock_exclusive(unp_list_mtx);
LIST_REMOVE(unp, unp_reflink);
+ lck_rw_done(unp_list_mtx);
unp->unp_socket->so_state &= ~SS_ISCONNECTED;
break;
struct xunpgen xug;
struct unp_head *head;
- lck_mtx_lock(unp_mutex);
+ lck_rw_lock_shared(unp_list_mtx);
head = ((intptr_t)arg1 == SOCK_DGRAM ? &unp_dhead : &unp_shead);
/*
n = unp_count;
req->oldidx = 2 * (sizeof xug)
+ (n + n/8) * sizeof(struct xunpcb);
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return 0;
}
if (req->newptr != USER_ADDR_NULL) {
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return EPERM;
}
gencnt = unp_gencnt;
n = unp_count;
+ bzero(&xug, sizeof(xug));
xug.xug_len = sizeof xug;
xug.xug_count = n;
xug.xug_gen = gencnt;
xug.xug_sogen = so_gencnt;
error = SYSCTL_OUT(req, &xug, sizeof xug);
if (error) {
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return error;
}
* We are done if there is no pcb
*/
if (n == 0) {
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return 0;
}
MALLOC(unp_list, struct unpcb **, n * sizeof *unp_list, M_TEMP, M_WAITOK);
if (unp_list == 0) {
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return ENOMEM;
}
unp = unp_list[i];
if (unp->unp_gencnt <= gencnt) {
struct xunpcb xu;
+
+ bzero(&xu, sizeof(xu));
xu.xu_len = sizeof xu;
xu.xu_unpp = (struct unpcb_compat *)unp;
/*
* while we were processing this request, and it
* might be necessary to retry.
*/
+ bzero(&xug, sizeof(xug));
+ xug.xug_len = sizeof xug;
xug.xug_gen = unp_gencnt;
xug.xug_sogen = so_gencnt;
xug.xug_count = unp_count;
error = SYSCTL_OUT(req, &xug, sizeof xug);
}
FREE(unp_list, M_TEMP);
- lck_mtx_unlock(unp_mutex);
+ lck_rw_done(unp_list_mtx);
return error;
}
bzero(fp, sizeof(struct fileproc));
fp->f_iocount = 0;
fp->f_fglob = fg;
- p->p_fd->fd_ofiles[f] = fp;
fg_removeuipc(fg);
- *fdflags(p, f) &= ~UF_RESERVED;
+ procfdtbl_releasefd(p, f, fp);
unp_rights--;
*(int *)rp++ = f;
}
LIST_INIT(&unp_dhead);
LIST_INIT(&unp_shead);
- unp_mutex = localdomain.dom_mtx;
+ /*
+ * allocate lock group attribute and group for udp pcb mutexes
+ */
+ unp_mtx_grp_attr = lck_grp_attr_alloc_init();
+
+ unp_mtx_grp = lck_grp_alloc_init("unp_list", unp_mtx_grp_attr);
+
+ unp_mtx_attr = lck_attr_alloc_init();
+
+ if ((unp_list_mtx = lck_rw_alloc_init(unp_mtx_grp, unp_mtx_attr)) == NULL)
+ return; /* pretty much dead if this fails... */
+
}
#ifndef MIN
return (0);
}
-static int unp_defer, unp_gcing;
+static int unp_defer, unp_gcing, unp_gcwait;
+/* always called under uipc_lock */
+void
+unp_gc_wait(void)
+{
+ while (unp_gcing != 0) {
+ unp_gcwait = 1;
+ msleep(&unp_gcing, uipc_lock, 0 , "unp_gc_wait", NULL);
+ }
+}
static void
unp_gc()
register struct socket *so;
struct fileglob **extra_ref, **fpp;
int nunref, i;
-
+ int need_gcwakeup = 0;
+
lck_mtx_lock(uipc_lock);
if (unp_gcing) {
lck_mtx_unlock(uipc_lock);
}
for (i = nunref, fpp = extra_ref; --i >= 0; ++fpp)
closef_locked((struct fileproc *)0, *fpp, (struct proc *) NULL);
+ lck_mtx_lock(uipc_lock);
unp_gcing = 0;
+
+ if (unp_gcwait != 0) {
+ unp_gcwait = 0;
+ need_gcwakeup = 1;
+ }
+ lck_mtx_unlock(uipc_lock);
+
+ if (need_gcwakeup != 0)
+ wakeup(&unp_gcing);
FREE((caddr_t)extra_ref, M_FILEGLOB);
}