From a81df0a5d715363cc1841810a87818dfa95675c0 Mon Sep 17 00:00:00 2001 From: Jerry Jelinek Date: Tue, 29 Mar 2011 15:47:25 -0700 Subject: [PATCH] 3933 contract adoption can race Reviewed by: Bryan Cantrill Reviewed by: Richard Lowe Reviewed by: Garrett D'Amore Approved by: Dan McDonald --- usr/src/uts/common/os/contract.c | 50 +++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 11 deletions(-) diff --git a/usr/src/uts/common/os/contract.c b/usr/src/uts/common/os/contract.c index a292f4e14f..ebaa6bfe41 100644 --- a/usr/src/uts/common/os/contract.c +++ b/usr/src/uts/common/os/contract.c @@ -497,7 +497,7 @@ contract_abandon(contract_t *ct, proc_t *p, int explicit) contract_t *parent = &p->p_ct_process->conp_contract; int inherit = 0; - ASSERT(p == curproc); + VERIFY(p == curproc); mutex_enter(&ct->ct_lock); @@ -547,7 +547,7 @@ contract_abandon(contract_t *ct, proc_t *p, int explicit) if (inherit) { ct->ct_state = CTS_INHERITED; - ASSERT(ct->ct_regent == parent); + VERIFY(ct->ct_regent == parent); contract_process_take(parent, ct); /* @@ -2063,8 +2063,8 @@ cte_copy(ct_equeue_t *q, ct_equeue_t *newq) { ct_kevent_t *e, *first = NULL; - ASSERT(q->ctq_listno == CTEL_CONTRACT); - ASSERT(newq->ctq_listno == CTEL_PBUNDLE); + VERIFY(q->ctq_listno == CTEL_CONTRACT); + VERIFY(newq->ctq_listno == CTEL_PBUNDLE); mutex_enter(&q->ctq_lock); mutex_enter(&newq->ctq_lock); @@ -2077,8 +2077,16 @@ cte_copy(ct_equeue_t *q, ct_equeue_t *newq) if ((e->cte_flags & (CTE_INFO | CTE_ACK)) == 0) { if (first == NULL) first = e; - list_insert_tail(&newq->ctq_events, e); - cte_hold(e); + /* + * It is possible for adoption to race with an owner's + * cte_publish_all(); we must only enqueue events that + * have not already been enqueued. + */ + if (!list_link_active((list_node_t *) + ((uintptr_t)e + newq->ctq_events.list_offset))) { + list_insert_tail(&newq->ctq_events, e); + cte_hold(e); + } } } @@ -2117,7 +2125,7 @@ cte_trim(ct_equeue_t *q, contract_t *ct) int flags, stopper; int start = 1; - ASSERT(MUTEX_HELD(&q->ctq_lock)); + VERIFY(MUTEX_HELD(&q->ctq_lock)); for (e = list_head(&q->ctq_events); e != NULL; e = next) { next = list_next(&q->ctq_events, e); @@ -2227,13 +2235,24 @@ cte_queue_drain(ct_equeue_t *q, int ack) * cte_publish_all. */ static void -cte_publish(ct_equeue_t *q, ct_kevent_t *e, timespec_t *tsp) +cte_publish(ct_equeue_t *q, ct_kevent_t *e, timespec_t *tsp, boolean_t mayexist) { ASSERT(MUTEX_HELD(&q->ctq_lock)); q->ctq_atime = *tsp; /* + * If this event may already exist on this queue, check to see if it + * is already there and return if so. + */ + if (mayexist && list_link_active((list_node_t *)((uintptr_t)e + + q->ctq_events.list_offset))) { + mutex_exit(&q->ctq_lock); + cte_rele(e); + return; + } + + /* * Don't publish if the event is informative and there aren't * any listeners, or if the queue has been shut down. */ @@ -2247,6 +2266,8 @@ cte_publish(ct_equeue_t *q, ct_kevent_t *e, timespec_t *tsp) /* * Enqueue event */ + VERIFY(!list_link_active((list_node_t *) + ((uintptr_t)e + q->ctq_events.list_offset))); list_insert_tail(&q->ctq_events, e); /* @@ -2318,14 +2339,14 @@ cte_publish_all(contract_t *ct, ct_kevent_t *e, nvlist_t *data, nvlist_t *gdata) ct->ct_evcnt++; } mutex_exit(&ct->ct_lock); - cte_publish(&ct->ct_events, e, &ts); + cte_publish(&ct->ct_events, e, &ts, B_FALSE); /* * CTEL_BUNDLE - Next deliver to the contract type's bundle * queue. */ mutex_enter(&ct->ct_type->ct_type_events.ctq_lock); - cte_publish(&ct->ct_type->ct_type_events, e, &ts); + cte_publish(&ct->ct_type->ct_type_events, e, &ts, B_FALSE); /* * CTEL_PBUNDLE - Finally, if the contract has an owner, @@ -2342,7 +2363,14 @@ cte_publish_all(contract_t *ct, ct_kevent_t *e, nvlist_t *data, nvlist_t *gdata) q = ct->ct_owner->p_ct_equeue[ct->ct_type->ct_type_index]; mutex_enter(&q->ctq_lock); mutex_exit(&ct->ct_lock); - cte_publish(q, e, &ts); + + /* + * It is possible for this code to race with adoption; we + * publish the event indicating that the event may already + * be enqueued because adoption beat us to it (in which case + * cte_pubish() does nothing). + */ + cte_publish(q, e, &ts, B_TRUE); } else { mutex_exit(&ct->ct_lock); cte_rele(e); -- 2.11.4.GIT