Index: sys/kern/uipc_usrreq.c =================================================================== --- sys/kern/uipc_usrreq.c +++ sys/kern/uipc_usrreq.c @@ -257,13 +257,30 @@ #define UNP_DEFERRED_LOCK() mtx_lock(&unp_defers_lock) #define UNP_DEFERRED_UNLOCK() mtx_unlock(&unp_defers_lock) +#define UNP_REF_LIST_LOCK() UNP_DEFERRED_LOCK(); +#define UNP_REF_LIST_UNLOCK() UNP_DEFERRED_UNLOCK(); + #define UNP_PCB_LOCK_INIT(unp) mtx_init(&(unp)->unp_mtx, \ "unp_mtx", "unp_mtx", \ - MTX_DUPOK|MTX_DEF|MTX_RECURSE) + MTX_DUPOK|MTX_DEF) #define UNP_PCB_LOCK_DESTROY(unp) mtx_destroy(&(unp)->unp_mtx) #define UNP_PCB_LOCK(unp) mtx_lock(&(unp)->unp_mtx) +#define UNP_PCB_TRYLOCK(unp) mtx_trylock(&(unp)->unp_mtx) #define UNP_PCB_UNLOCK(unp) mtx_unlock(&(unp)->unp_mtx) +#define UNP_PCB_OWNED(unp) mtx_owned(&(unp)->unp_mtx) #define UNP_PCB_LOCK_ASSERT(unp) mtx_assert(&(unp)->unp_mtx, MA_OWNED) +#define UNP_PCB_UNLOCK_ASSERT(unp) mtx_assert(&(unp)->unp_mtx, MA_NOTOWNED) + +#define UNP_PCB_CONN_LOCK_INIT(unp) mtx_init(&(unp)->unp_conn_mtx, \ + "unp_conn_mtx", "unp_conn_mtx", \ + MTX_DUPOK|MTX_DEF) +#define UNP_PCB_CONN_LOCK_DESTROY(unp) mtx_destroy(&(unp)->unp_conn_mtx) +#define UNP_PCB_CONN_LOCK(unp) mtx_lock(&(unp)->unp_conn_mtx) +#define UNP_PCB_CONN_TRYLOCK(unp) mtx_trylock(&(unp)->unp_conn_mtx) +#define UNP_PCB_CONN_OWNED(unp) mtx_owned(&(unp)->unp_conn_mtx) +#define UNP_PCB_CONN_UNLOCK(unp) mtx_unlock(&(unp)->unp_conn_mtx) +#define UNP_PCB_CONN_LOCK_ASSERT(unp) mtx_assert(&(unp)->unp_conn_mtx, MA_OWNED) +#define UNP_PCB_CONN_UNLOCK_ASSERT(unp) mtx_assert(&(unp)->unp_conn_mtx, MA_NOTOWNED) static int uipc_connect2(struct socket *, struct socket *); static int uipc_ctloutput(struct socket *, struct sockopt *); @@ -289,6 +306,72 @@ static struct mbuf *unp_addsockcred(struct thread *, struct mbuf *); static void unp_process_defers(void * __unused, int); + +static void +unp_pcb_hold(struct unpcb *unp) +{ + UNP_PCB_LOCK_ASSERT(unp); + MPASS(unp->unp_refcount); + unp->unp_refcount++; +} + +static int +unp_pcb_rele(struct unpcb *unp) +{ + int refcount; + + UNP_PCB_LOCK_ASSERT(unp); + MPASS(unp->unp_refcount); + unp->unp_refcount--; + refcount = unp->unp_refcount; + if (refcount == 0) { + /* we got here with having detached? */ + MPASS(unp->unp_socket == NULL); + UNP_PCB_UNLOCK(unp); + if (UNP_PCB_CONN_OWNED(unp)) { + UNP_PCB_CONN_UNLOCK(unp); + UNP_PCB_CONN_UNLOCK_ASSERT(unp); + } + UNP_PCB_LOCK_DESTROY(unp); + UNP_PCB_CONN_UNLOCK_ASSERT(unp); + UNP_PCB_CONN_LOCK_DESTROY(unp); + uma_zfree(unp_zone, unp); + } + return (refcount == 0); +} + +static void +unp_pcb_lock2(struct unpcb *unp, struct unpcb *unp2) +{ + UNP_PCB_UNLOCK_ASSERT(unp); + UNP_PCB_UNLOCK_ASSERT(unp2); + if ((uintptr_t)unp2 > (uintptr_t)unp) { + UNP_PCB_LOCK(unp); + UNP_PCB_LOCK(unp2); + } else { + UNP_PCB_LOCK(unp2); + UNP_PCB_LOCK(unp); + } +} + +static void +unp_pcb_owned_lock2(struct unpcb *unp, struct unpcb *unp2) +{ + + UNP_PCB_LOCK_ASSERT(unp); + UNP_PCB_UNLOCK_ASSERT(unp2); + if (__predict_true(UNP_PCB_TRYLOCK(unp2))) + return; + if ((uintptr_t)unp2 > (uintptr_t)unp) { + UNP_PCB_LOCK(unp2); + } else { + UNP_PCB_UNLOCK(unp); + UNP_PCB_LOCK(unp2); + UNP_PCB_LOCK(unp); + } +} + + /* * Definitions of protocols supported in the LOCAL domain. */ @@ -344,17 +427,17 @@ unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_abort: unp == NULL")); + UNP_PCB_UNLOCK_ASSERT(unp); - UNP_LINK_WLOCK(); + UNP_PCB_CONN_LOCK(unp); UNP_PCB_LOCK(unp); unp2 = unp->unp_conn; if (unp2 != NULL) { - UNP_PCB_LOCK(unp2); + UNP_PCB_UNLOCK_ASSERT(unp2); unp_drop(unp2); - UNP_PCB_UNLOCK(unp2); } UNP_PCB_UNLOCK(unp); - UNP_LINK_WUNLOCK(); + UNP_PCB_CONN_UNLOCK(unp); } static int @@ -424,6 +507,7 @@ return (ENOBUFS); LIST_INIT(&unp->unp_refs); UNP_PCB_LOCK_INIT(unp); + UNP_PCB_CONN_LOCK_INIT(unp); unp->unp_socket = so; so->so_pcb = unp; unp->unp_refcount = 1; @@ -551,14 +635,12 @@ ASSERT_VOP_ELOCKED(vp, "uipc_bind"); soun = (struct sockaddr_un *)sodupsockaddr(nam, M_WAITOK); - UNP_LINK_WLOCK(); UNP_PCB_LOCK(unp); VOP_UNP_BIND(vp, unp); unp->unp_vnode = vp; unp->unp_addr = soun; unp->unp_flags &= ~UNP_BINDING; UNP_PCB_UNLOCK(unp); - UNP_LINK_WUNLOCK(); VOP_UNLOCK(vp, 0); vn_finished_write(mp); free(buf, M_TEMP); @@ -585,9 +667,7 @@ int error; KASSERT(td == curthread, ("uipc_connect: td != curthread")); - UNP_LINK_WLOCK(); error = unp_connect(so, nam, td); - UNP_LINK_WUNLOCK(); return (error); } @@ -598,9 +678,7 @@ int error; KASSERT(td == curthread, ("uipc_connectat: td != curthread")); - UNP_LINK_WLOCK(); error = unp_connectat(fd, so, nam, td); - UNP_LINK_WUNLOCK(); return (error); } @@ -609,26 +687,40 @@ { struct unpcb *unp, *unp2; struct vnode *vp = NULL; - + struct mtx *vplock; unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_close: unp == NULL")); - UNP_LINK_WLOCK(); + + if ((vp = unp->unp_vnode) != NULL) { + vplock = mtx_pool_find(mtxpool_sleep, vp); + mtx_lock(vplock); + } else + vplock = NULL; + UNP_PCB_CONN_LOCK(unp); UNP_PCB_LOCK(unp); unp2 = unp->unp_conn; - if (unp2 != NULL) { - UNP_PCB_LOCK(unp2); - unp_disconnect(unp, 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_LINK_WUNLOCK(); + unp_pcb_hold(unp); + if (unp2 != NULL) { + unp_pcb_owned_lock2(unp, unp2); + unp_pcb_hold(unp2); + unp_disconnect(unp, unp2); + if (unp_pcb_rele(unp2) == 0) + UNP_PCB_UNLOCK(unp2); + } + if (unp_pcb_rele(unp) == 0) { + UNP_PCB_CONN_UNLOCK(unp); + UNP_PCB_UNLOCK(unp); + } + if (vplock) + mtx_unlock(vplock); if (vp) vrele(vp); + } static int @@ -637,17 +729,16 @@ struct unpcb *unp, *unp2; int error; - UNP_LINK_WLOCK(); unp = so1->so_pcb; KASSERT(unp != NULL, ("uipc_connect2: unp == NULL")); - UNP_PCB_LOCK(unp); + UNP_PCB_CONN_LOCK(unp); unp2 = so2->so_pcb; KASSERT(unp2 != NULL, ("uipc_connect2: unp2 == NULL")); - UNP_PCB_LOCK(unp2); + unp_pcb_lock2(unp, unp2); error = unp_connect2(so1, so2, PRU_CONNECT2); UNP_PCB_UNLOCK(unp2); UNP_PCB_UNLOCK(unp); - UNP_LINK_WUNLOCK(); + UNP_PCB_CONN_UNLOCK(unp); return (error); } @@ -658,6 +749,7 @@ struct sockaddr_un *saved_unp_addr; struct vnode *vp; int freeunp, local_unp_rights; + struct mtx *vplock; unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_detach: unp == NULL")); @@ -669,47 +761,76 @@ LIST_REMOVE(unp, unp_link); unp->unp_gencnt = ++unp_gencnt; --unp_count; - UNP_PCB_LOCK(unp); - if ((unp->unp_flags & UNP_NASCENT) != 0) - goto teardown; + UNP_LINK_WUNLOCK(); + UNP_PCB_UNLOCK_ASSERT(unp); + restart: + if ((vp = unp->unp_vnode) != NULL) { + vplock = mtx_pool_find(mtxpool_sleep, vp); + mtx_lock(vplock); + } + UNP_PCB_CONN_LOCK(unp); + if ((unp2 = unp->unp_conn) != NULL) + unp_pcb_lock2(unp, unp2); + else + UNP_PCB_LOCK(unp); + if (unp->unp_vnode != vp && + unp->unp_vnode != NULL) { + mtx_unlock(vplock); + UNP_PCB_CONN_UNLOCK(unp); + UNP_PCB_UNLOCK(unp); + if (unp2) + UNP_PCB_UNLOCK(unp2); + goto restart; + } + if ((unp->unp_flags & UNP_NASCENT) != 0) { + if (unp2) + UNP_PCB_UNLOCK(unp2); + UNP_PCB_CONN_UNLOCK(unp); + goto teardown; + } if ((vp = unp->unp_vnode) != NULL) { VOP_UNP_DETACH(vp); unp->unp_vnode = NULL; } - unp2 = unp->unp_conn; + unp_pcb_hold(unp); if (unp2 != NULL) { - UNP_PCB_LOCK(unp2); + unp_pcb_hold(unp2); unp_disconnect(unp, unp2); - UNP_PCB_UNLOCK(unp2); + if (unp_pcb_rele(unp2) == 0) + UNP_PCB_UNLOCK(unp2); } - - /* - * We hold the linkage lock exclusively, so it's OK to acquire - * multiple pcb locks at a time. - */ + UNP_PCB_UNLOCK(unp); + UNP_PCB_CONN_UNLOCK(unp); + UNP_REF_LIST_LOCK(); while (!LIST_EMPTY(&unp->unp_refs)) { struct unpcb *ref = LIST_FIRST(&unp->unp_refs); - UNP_PCB_LOCK(ref); + UNP_REF_LIST_UNLOCK(); + MPASS(ref != unp); + UNP_PCB_UNLOCK_ASSERT(ref); unp_drop(ref); - UNP_PCB_UNLOCK(ref); + UNP_REF_LIST_LOCK(); } + + UNP_REF_LIST_UNLOCK(); + UNP_PCB_LOCK(unp); + freeunp = unp_pcb_rele(unp); + MPASS(freeunp == 0); local_unp_rights = unp_rights; teardown: - UNP_LINK_WUNLOCK(); unp->unp_socket->so_pcb = NULL; saved_unp_addr = unp->unp_addr; unp->unp_addr = NULL; - unp->unp_refcount--; - freeunp = (unp->unp_refcount == 0); + unp->unp_socket = NULL; + UNP_PCB_CONN_UNLOCK_ASSERT(unp); + freeunp = unp_pcb_rele(unp); if (saved_unp_addr != NULL) free(saved_unp_addr, M_SONAME); - if (freeunp) { - UNP_PCB_LOCK_DESTROY(unp); - uma_zfree(unp_zone, unp); - } else + if (!freeunp) UNP_PCB_UNLOCK(unp); + if (vp) + mtx_unlock(vplock); if (vp) vrele(vp); if (local_unp_rights) @@ -720,20 +841,27 @@ uipc_disconnect(struct socket *so) { struct unpcb *unp, *unp2; + int freed; unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_disconnect: unp == NULL")); - UNP_LINK_WLOCK(); - UNP_PCB_LOCK(unp); + freed = 0; + UNP_PCB_CONN_LOCK(unp); unp2 = unp->unp_conn; if (unp2 != NULL) { - UNP_PCB_LOCK(unp2); + unp_pcb_lock2(unp, unp2); + unp_pcb_hold(unp2); + unp_pcb_hold(unp); unp_disconnect(unp, unp2); - UNP_PCB_UNLOCK(unp2); - } - UNP_PCB_UNLOCK(unp); - UNP_LINK_WUNLOCK(); + if (unp_pcb_rele(unp) == 0) { + UNP_PCB_CONN_UNLOCK(unp); + UNP_PCB_UNLOCK(unp); + } + if (unp_pcb_rele(unp2) == 0) + UNP_PCB_UNLOCK(unp2); + } else + UNP_PCB_CONN_UNLOCK(unp); return (0); } @@ -858,7 +986,7 @@ struct unpcb *unp, *unp2; struct socket *so2; u_int mbcnt, sbcc; - int error = 0; + int freed, error; unp = sotounpcb(so); KASSERT(unp != NULL, ("%s: unp == NULL", __func__)); @@ -866,6 +994,7 @@ so->so_type == SOCK_SEQPACKET, ("%s: socktype %d", __func__, so->so_type)); + freed = error = 0; if (flags & PRUS_OOB) { error = EOPNOTSUPP; goto release; @@ -873,9 +1002,8 @@ if (control != NULL && (error = unp_internalize(&control, td))) goto release; if ((nam != NULL) || (flags & PRUS_EOF)) - UNP_LINK_WLOCK(); - else - UNP_LINK_RLOCK(); + UNP_PCB_CONN_LOCK(unp); + switch (so->so_type) { case SOCK_DGRAM: { @@ -883,7 +1011,7 @@ unp2 = unp->unp_conn; if (nam != NULL) { - UNP_LINK_WLOCK_ASSERT(); + UNP_PCB_CONN_LOCK_ASSERT(unp); if (unp2 != NULL) { error = EISCONN; break; @@ -924,13 +1052,17 @@ SOCKBUF_UNLOCK(&so2->so_rcv); error = ENOBUFS; } + unp_pcb_hold(unp); if (nam != NULL) { - UNP_LINK_WLOCK_ASSERT(); - UNP_PCB_LOCK(unp2); + UNP_PCB_CONN_LOCK_ASSERT(unp); + unp_pcb_owned_lock2(unp, unp2); + unp_pcb_hold(unp2); unp_disconnect(unp, unp2); - UNP_PCB_UNLOCK(unp2); + if (unp_pcb_rele(unp2) == 0) + UNP_PCB_UNLOCK(unp2); } - UNP_PCB_UNLOCK(unp); + if (unp_pcb_rele(unp) == 0) + UNP_PCB_UNLOCK(unp); break; } @@ -938,7 +1070,6 @@ case SOCK_STREAM: if ((so->so_state & SS_ISCONNECTED) == 0) { if (nam != NULL) { - UNP_LINK_WLOCK_ASSERT(); error = unp_connect(so, nam, td); if (error) break; /* XXX */ @@ -1048,9 +1179,7 @@ } if ((nam != NULL) || (flags & PRUS_EOF)) - UNP_LINK_WUNLOCK(); - else - UNP_LINK_RUNLOCK(); + UNP_PCB_CONN_UNLOCK(unp); if (control != NULL && error != 0) unp_dispose_mbuf(control); @@ -1124,12 +1253,12 @@ unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_shutdown: unp == NULL")); - UNP_LINK_WLOCK(); + UNP_PCB_CONN_LOCK(unp); UNP_PCB_LOCK(unp); socantsendmore(so); unp_shutdown(unp); UNP_PCB_UNLOCK(unp); - UNP_LINK_WUNLOCK(); + UNP_PCB_CONN_UNLOCK(unp); return (0); } @@ -1334,15 +1463,10 @@ struct sockaddr *sa; cap_rights_t rights; int error, len; + struct mtx *vplock; if (nam->sa_family != AF_UNIX) return (EAFNOSUPPORT); - - UNP_LINK_WLOCK_ASSERT(); - - unp = sotounpcb(so); - KASSERT(unp != NULL, ("unp_connect: unp == NULL")); - if (nam->sa_len > sizeof(struct sockaddr_un)) return (EINVAL); len = nam->sa_len - offsetof(struct sockaddr_un, sun_path); @@ -1351,12 +1475,13 @@ bcopy(soun->sun_path, buf, len); buf[len] = 0; + unp = sotounpcb(so); + KASSERT(unp != NULL, ("unp_connect: unp == NULL")); UNP_PCB_LOCK(unp); if (unp->unp_flags & UNP_CONNECTING) { UNP_PCB_UNLOCK(unp); return (EALREADY); } - UNP_LINK_WUNLOCK(); unp->unp_flags |= UNP_CONNECTING; UNP_PCB_UNLOCK(unp); @@ -1393,8 +1518,10 @@ * Lock linkage lock for two reasons: make sure v_socket is stable, * and to protect simultaneous locking of multiple pcbs. */ - UNP_LINK_WLOCK(); + vplock = mtx_pool_find(mtxpool_sleep, vp); + mtx_lock(vplock); VOP_UNP_CONNECT(vp, &unp2); + UNP_PCB_CONN_LOCK(unp); if (unp2 == NULL) { error = ECONNREFUSED; goto bad2; @@ -1404,8 +1531,7 @@ error = EPROTOTYPE; goto bad2; } - UNP_PCB_LOCK(unp); - UNP_PCB_LOCK(unp2); + unp_pcb_lock2(unp, unp2); if (so->so_proto->pr_flags & PR_CONNREQUIRED) { if (so2->so_options & SO_ACCEPTCONN) { CURVNET_SET(so2->so_vnet); @@ -1418,7 +1544,8 @@ goto bad3; } unp3 = sotounpcb(so2); - UNP_PCB_LOCK(unp3); + UNP_PCB_UNLOCK(unp); + unp_pcb_owned_lock2(unp2, unp3); if (unp2->unp_addr != NULL) { bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len); unp3->unp_addr = (struct sockaddr_un *) sa; @@ -1445,6 +1572,7 @@ unp3->unp_flags |= UNP_WANTCRED; UNP_PCB_UNLOCK(unp2); unp2 = unp3; + unp_pcb_owned_lock2(unp2, unp); #ifdef MAC mac_socketpeer_set_from_socket(so, so2); mac_socketpeer_set_from_socket(so2, so); @@ -1459,12 +1587,12 @@ UNP_PCB_UNLOCK(unp2); UNP_PCB_UNLOCK(unp); bad2: - UNP_LINK_WUNLOCK(); + mtx_unlock(vplock); + UNP_PCB_CONN_UNLOCK(unp); bad: if (vp != NULL) vput(vp); free(sa, M_SONAME); - UNP_LINK_WLOCK(); UNP_PCB_LOCK(unp); unp->unp_flags &= ~UNP_CONNECTING; UNP_PCB_UNLOCK(unp); @@ -1482,7 +1610,7 @@ unp2 = sotounpcb(so2); KASSERT(unp2 != NULL, ("unp_connect2: unp2 == NULL")); - UNP_LINK_WLOCK_ASSERT(); + UNP_PCB_CONN_LOCK_ASSERT(unp); UNP_PCB_LOCK_ASSERT(unp); UNP_PCB_LOCK_ASSERT(unp2); @@ -1490,10 +1618,13 @@ return (EPROTOTYPE); unp2->unp_flags &= ~UNP_NASCENT; unp->unp_conn = unp2; - + unp_pcb_hold(unp2); + unp_pcb_hold(unp); switch (so->so_type) { case SOCK_DGRAM: + UNP_REF_LIST_LOCK(); LIST_INSERT_HEAD(&unp2->unp_refs, unp, unp_reflink); + UNP_REF_LIST_UNLOCK(); soisconnected(so); break; @@ -1517,31 +1648,67 @@ static void unp_disconnect(struct unpcb *unp, struct unpcb *unp2) { - struct socket *so; + struct socket *so, *so2; + int rele, freed; KASSERT(unp2 != NULL, ("unp_disconnect: unp2 == NULL")); - UNP_LINK_WLOCK_ASSERT(); + UNP_PCB_CONN_LOCK_ASSERT(unp); UNP_PCB_LOCK_ASSERT(unp); UNP_PCB_LOCK_ASSERT(unp2); + if (unp->unp_conn == NULL && unp2->unp_conn == NULL) + return; + + if (UNP_PCB_CONN_TRYLOCK(unp2) == 0) { + UNP_PCB_UNLOCK(unp); + UNP_PCB_UNLOCK(unp2); + if ((uintptr_t)unp2 > (uintptr_t)unp) { + UNP_PCB_CONN_LOCK(unp2); + } else { + UNP_PCB_CONN_UNLOCK(unp); + UNP_PCB_CONN_LOCK(unp2); + UNP_PCB_CONN_LOCK(unp); + } + unp_pcb_lock2(unp, unp2); + if (unp->unp_conn == NULL && unp2->unp_conn == NULL) { + UNP_PCB_CONN_UNLOCK(unp2); + return; + } + } + + MPASS(unp->unp_conn == unp2); unp->unp_conn = NULL; + rele = 0; + so = unp->unp_socket; + so2 = unp2->unp_socket; switch (unp->unp_socket->so_type) { case SOCK_DGRAM: + UNP_REF_LIST_LOCK(); LIST_REMOVE(unp, unp_reflink); - so = unp->unp_socket; - SOCK_LOCK(so); - so->so_state &= ~SS_ISCONNECTED; - SOCK_UNLOCK(so); + UNP_REF_LIST_UNLOCK(); + if (so) { + SOCK_LOCK(so); + so->so_state &= ~SS_ISCONNECTED; + SOCK_UNLOCK(so); + } break; case SOCK_STREAM: case SOCK_SEQPACKET: - soisdisconnected(unp->unp_socket); + if (so) + soisdisconnected(so); + MPASS(unp2->unp_conn == unp); unp2->unp_conn = NULL; - soisdisconnected(unp2->unp_socket); + if (so2) + soisdisconnected(so2); break; } + UNP_PCB_CONN_UNLOCK(unp2); + freed = unp_pcb_rele(unp); + MPASS(freed == 0); + freed = unp_pcb_rele(unp2); + MPASS(freed == 0); } /* @@ -1625,7 +1792,7 @@ continue; } unp_list[i++] = unp; - unp->unp_refcount++; + unp_pcb_hold(unp); } UNP_PCB_UNLOCK(unp); } @@ -1637,8 +1804,9 @@ for (i = 0; i < n; i++) { unp = unp_list[i]; UNP_PCB_LOCK(unp); - unp->unp_refcount--; - if (unp->unp_refcount != 0 && unp->unp_gencnt <= gencnt) { + freeunp = unp_pcb_rele(unp); + + if (freeunp == 0 && unp->unp_gencnt <= gencnt) { xu->xu_len = sizeof *xu; xu->xu_unpp = unp; /* @@ -1665,14 +1833,8 @@ sotoxsocket(unp->unp_socket, &xu->xu_socket); UNP_PCB_UNLOCK(unp); error = SYSCTL_OUT(req, xu, sizeof *xu); - } else { - freeunp = (unp->unp_refcount == 0); + } else if (freeunp == 0) UNP_PCB_UNLOCK(unp); - if (freeunp) { - UNP_PCB_LOCK_DESTROY(unp); - uma_zfree(unp_zone, unp); - } - } } free(xu, M_TEMP); if (!error) { @@ -1709,7 +1871,7 @@ struct unpcb *unp2; struct socket *so; - UNP_LINK_WLOCK_ASSERT(); + UNP_PCB_CONN_LOCK_ASSERT(unp); UNP_PCB_LOCK_ASSERT(unp); unp2 = unp->unp_conn; @@ -1727,21 +1889,28 @@ struct socket *so = unp->unp_socket; struct unpcb *unp2; - UNP_LINK_WLOCK_ASSERT(); - UNP_PCB_LOCK_ASSERT(unp); - /* * Regardless of whether the socket's peer dropped the connection * with this socket by aborting or disconnecting, POSIX requires * that ECONNRESET is returned. */ + /* acquire a reference so that unp isn't freed from underneath us */ + + UNP_PCB_CONN_LOCK(unp); + UNP_PCB_LOCK(unp); so->so_error = ECONNRESET; unp2 = unp->unp_conn; - if (unp2 == NULL) - return; - UNP_PCB_LOCK(unp2); - unp_disconnect(unp, unp2); - UNP_PCB_UNLOCK(unp2); + if (unp2 != NULL) { + unp_pcb_owned_lock2(unp, unp2); + unp_pcb_hold(unp2); + unp_disconnect(unp, unp2); + if (unp_pcb_rele(unp2) == 0) + UNP_PCB_UNLOCK(unp2); + } + if (unp_pcb_rele(unp) == 0) { + UNP_PCB_UNLOCK(unp); + UNP_PCB_CONN_UNLOCK(unp); + } } static void @@ -2464,16 +2633,19 @@ { struct unpcb *unp; int active; + struct mtx *vplock; ASSERT_VOP_ELOCKED(vp, "vfs_unp_reclaim"); KASSERT(vp->v_type == VSOCK, ("vfs_unp_reclaim: vp->v_type != VSOCK")); active = 0; - UNP_LINK_WLOCK(); + vplock = mtx_pool_find(mtxpool_sleep, vp); + mtx_lock(vplock); VOP_UNP_CONNECT(vp, &unp); if (unp == NULL) goto done; + UNP_PCB_CONN_LOCK(unp); UNP_PCB_LOCK(unp); if (unp->unp_vnode == vp) { VOP_UNP_DETACH(vp); @@ -2481,8 +2653,9 @@ active = 1; } UNP_PCB_UNLOCK(unp); -done: - UNP_LINK_WUNLOCK(); + UNP_PCB_CONN_UNLOCK(unp); + done: + mtx_unlock(vplock); if (active) vunref(vp); } Index: sys/sys/unpcb.h =================================================================== --- sys/sys/unpcb.h +++ sys/sys/unpcb.h @@ -85,6 +85,7 @@ u_int unp_refcount; u_int unp_msgcount; /* references from message queue */ struct mtx unp_mtx; /* mutex */ + struct mtx unp_conn_mtx; /* mutex */ }; /*