summaryrefslogtreecommitdiffstats
path: root/sys/rpc/clnt_dg.c
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2009-06-04 14:49:27 +0000
committerrmacklem <rmacklem@FreeBSD.org>2009-06-04 14:49:27 +0000
commit981682577c61c5ffe81deb34e9b9b1f0658c7fb0 (patch)
tree51ecc8b9fcef7600baf6a87c2905f7de4a94f298 /sys/rpc/clnt_dg.c
parent4df6c5e1fcc8804ec08f4f04c8dd9d0ef83466e1 (diff)
downloadFreeBSD-src-981682577c61c5ffe81deb34e9b9b1f0658c7fb0.zip
FreeBSD-src-981682577c61c5ffe81deb34e9b9b1f0658c7fb0.tar.gz
Fix upcall races in the client side krpc. For the client side upcall,
holding SOCKBUF_LOCK() isn't sufficient to guarantee that there is no upcall in progress, since SOCKBUF_LOCK() is released/re-acquired in the upcall. An upcall reference counter was added to the upcall structure that is incremented at the beginning of the upcall and decremented at the end of the upcall. As such, a reference count == 0 when holding the SOCKBUF_LOCK() guarantees there is no upcall in progress. Add a function that is called just after soupcall_clear(), which waits until the reference count == 0. Also, move the mtx_destroy() down to after soupcall_clear(), so that the mutex is not destroyed before upcalls are done. Reviewed by: dfr, jhb Tested by: pho Approved by: kib (mentor)
Diffstat (limited to 'sys/rpc/clnt_dg.c')
-rw-r--r--sys/rpc/clnt_dg.c27
1 files changed, 26 insertions, 1 deletions
diff --git a/sys/rpc/clnt_dg.c b/sys/rpc/clnt_dg.c
index 880a16f..865c704 100644
--- a/sys/rpc/clnt_dg.c
+++ b/sys/rpc/clnt_dg.c
@@ -123,8 +123,11 @@ struct cu_socket {
struct mtx cs_lock;
int cs_refs; /* Count of clients */
struct cu_request_list cs_pending; /* Requests awaiting replies */
+ int cs_upcallrefs; /* Refcnt of upcalls in prog.*/
};
+static void clnt_dg_upcallsdone(struct socket *, struct cu_socket *);
+
/*
* Private data kept per client handle
*/
@@ -291,6 +294,7 @@ recheck_socket:
}
mtx_init(&cs->cs_lock, "cs->cs_lock", NULL, MTX_DEF);
cs->cs_refs = 1;
+ cs->cs_upcallrefs = 0;
TAILQ_INIT(&cs->cs_pending);
soupcall_set(so, SO_RCV, clnt_dg_soupcall, cs);
}
@@ -988,10 +992,12 @@ clnt_dg_destroy(CLIENT *cl)
cs->cs_refs--;
if (cs->cs_refs == 0) {
- mtx_destroy(&cs->cs_lock);
+ mtx_unlock(&cs->cs_lock);
SOCKBUF_LOCK(&cu->cu_socket->so_rcv);
soupcall_clear(cu->cu_socket, SO_RCV);
+ clnt_dg_upcallsdone(cu->cu_socket, cs);
SOCKBUF_UNLOCK(&cu->cu_socket->so_rcv);
+ mtx_destroy(&cs->cs_lock);
mem_free(cs, sizeof(*cs));
lastsocketref = TRUE;
} else {
@@ -1036,6 +1042,7 @@ clnt_dg_soupcall(struct socket *so, void *arg, int waitflag)
int error, rcvflag, foundreq;
uint32_t xid;
+ cs->cs_upcallrefs++;
uio.uio_resid = 1000000000;
uio.uio_td = curthread;
do {
@@ -1111,6 +1118,24 @@ clnt_dg_soupcall(struct socket *so, void *arg, int waitflag)
if (!foundreq)
m_freem(m);
} while (m);
+ cs->cs_upcallrefs--;
+ if (cs->cs_upcallrefs < 0)
+ panic("rpcdg upcall refcnt");
+ if (cs->cs_upcallrefs == 0)
+ wakeup(&cs->cs_upcallrefs);
return (SU_OK);
}
+/*
+ * Wait for all upcalls in progress to complete.
+ */
+static void
+clnt_dg_upcallsdone(struct socket *so, struct cu_socket *cs)
+{
+
+ SOCKBUF_LOCK_ASSERT(&so->so_rcv);
+
+ while (cs->cs_upcallrefs > 0)
+ (void) msleep(&cs->cs_upcallrefs, SOCKBUF_MTX(&so->so_rcv), 0,
+ "rpcdgup", 0);
+}
OpenPOWER on IntegriCloud