Mercurial > illumos > illumos-gate
changeset 5160:6a35c54999f3
6554813 Possible deadlock when enabling/disabling soft rings
author | udpa |
---|---|
date | Mon, 01 Oct 2007 16:07:13 -0700 |
parents | 6cdd421a2458 |
children | 6118969b7712 |
files | usr/src/uts/common/io/dls/dls.c usr/src/uts/common/io/dls/dls_soft_ring.c usr/src/uts/common/sys/dls_soft_ring.h |
diffstat | 3 files changed, 81 insertions(+), 44 deletions(-) [+] |
line wrap: on
line diff
--- a/usr/src/uts/common/io/dls/dls.c Mon Oct 01 15:49:09 2007 -0700 +++ b/usr/src/uts/common/io/dls/dls.c Mon Oct 01 16:07:13 2007 -0700 @@ -308,12 +308,7 @@ dip->di_dvp = NULL; dip->di_txinfo = NULL; - if (dip->di_soft_ring_list != NULL) { - soft_ring_set_destroy(dip->di_soft_ring_list, - dip->di_soft_ring_size); - dip->di_soft_ring_list = NULL; - } - dip->di_soft_ring_size = 0; + ASSERT(dip->di_soft_ring_list == NULL); kmem_cache_free(i_dls_impl_cachep, dip);
--- a/usr/src/uts/common/io/dls/dls_soft_ring.c Mon Oct 01 15:49:09 2007 -0700 +++ b/usr/src/uts/common/io/dls/dls_soft_ring.c Mon Oct 01 16:07:13 2007 -0700 @@ -69,7 +69,13 @@ static void soft_ring_fire(void *); static void soft_ring_drain(soft_ring_t *, clock_t); static void soft_ring_worker(soft_ring_t *); -static void soft_ring_stop_workers(soft_ring_t **, int); +static void soft_ring_set_destroy(soft_ring_t **, int); +static void dls_taskq_stop_soft_ring(void *); + +typedef struct soft_ring_taskq { + soft_ring_t **ringp_list; + uint_t ring_size; +} soft_ring_taskq_t; kmem_cache_t *soft_ring_cache; @@ -177,38 +183,38 @@ soft_ring_t ** soft_ring_set_create(char *name, processorid_t bind, clock_t wait, - uint_t type, pri_t pri, int cnt) + uint_t type, pri_t pri, int ring_size) { int i; soft_ring_t **ringp_list; if ((ringp_list = - (soft_ring_t **) kmem_zalloc(sizeof (soft_ring_t *) * cnt, + (soft_ring_t **) kmem_zalloc(sizeof (soft_ring_t *) * ring_size, KM_NOSLEEP)) != NULL) { - for (i = 0; i < cnt; i++) { + for (i = 0; i < ring_size; i++) { ringp_list[i] = soft_ring_create(name, bind, wait, type, pri); if (ringp_list[i] == NULL) break; } - if (i != cnt) { - soft_ring_stop_workers(ringp_list, i); - soft_ring_set_destroy(ringp_list, i); + if (i != ring_size) { + soft_ring_set_destroy(ringp_list, ring_size); ringp_list = NULL; } } return (ringp_list); } -static void -soft_ring_stop_workers(soft_ring_t **ringp_set, int cnt) +void +soft_ring_set_destroy(soft_ring_t **ringp_set, int ring_size) { int i; + mblk_t *mp; soft_ring_t *ringp; timeout_id_t tid; kt_did_t t_did = 0; - for (i = 0; i < cnt; i++) { + for (i = 0; (i < ring_size) && (ringp_set[i] != NULL); i++) { ringp = ringp_set[i]; soft_ring_unbind((void *)ringp); @@ -239,18 +245,6 @@ */ if (t_did) thread_join(t_did); - } -} - -void -soft_ring_set_destroy(soft_ring_t **ringp_set, int cnt) -{ - int i; - mblk_t *mp; - soft_ring_t *ringp; - - for (i = 0; i < cnt; i++) { - ringp = ringp_set[i]; mutex_enter(&ringp->s_ring_lock); @@ -272,7 +266,7 @@ kmem_cache_free(soft_ring_cache, ringp); ringp_set[i] = NULL; } - kmem_free(ringp_set, sizeof (soft_ring_t *) * cnt); + kmem_free(ringp_set, sizeof (soft_ring_t *) * ring_size); } /* ARGSUSED */ @@ -542,7 +536,7 @@ dls_impl_t *dip = (dls_impl_t *)dc; boolean_t ret = B_FALSE; - rw_enter(&(dip->di_lock), RW_WRITER); + rw_enter(&(dip->di_lock), RW_READER); if (dip->di_soft_ring_list != NULL) ret = B_TRUE; rw_exit(&(dip->di_lock)); @@ -553,33 +547,82 @@ dls_soft_ring_disable(dls_channel_t dc) { dls_impl_t *dip = (dls_impl_t *)dc; + soft_ring_t **ringp_list = NULL; + int ring_size; rw_enter(&(dip->di_lock), RW_WRITER); - if (dip->di_soft_ring_list != NULL) - soft_ring_stop_workers(dip->di_soft_ring_list, - dip->di_soft_ring_size); + if (dip->di_soft_ring_list != NULL) { + ringp_list = dip->di_soft_ring_list; + ring_size = dip->di_soft_ring_size; + dip->di_soft_ring_list = NULL; + } + dip->di_soft_ring_size = 0; rw_exit(&(dip->di_lock)); + + if (ringp_list != NULL) + soft_ring_set_destroy(ringp_list, ring_size); +} + +static void +dls_taskq_stop_soft_ring(void *arg) +{ + soft_ring_taskq_t *ring_taskq; + soft_ring_t **ringp_list; + int ring_size; + + ring_taskq = (soft_ring_taskq_t *)arg; + ringp_list = ring_taskq->ringp_list; + ring_size = ring_taskq->ring_size; + kmem_free(ring_taskq, sizeof (soft_ring_taskq_t)); + + soft_ring_set_destroy(ringp_list, ring_size); } boolean_t dls_soft_ring_enable(dls_channel_t dc, dl_capab_dls_t *soft_ringp) { - dls_impl_t *dip; - int i; - soft_ring_t **softring_set; - soft_ring_t *softring; - mac_rx_fifo_t mrf; - char name[64]; + dls_impl_t *dip; + int i; + soft_ring_t **softring_set; + soft_ring_t *softring; + mac_rx_fifo_t mrf; + soft_ring_taskq_t *ring_taskq; + char name[64]; dip = (dls_impl_t *)dc; rw_enter(&(dip->di_lock), RW_WRITER); if (dip->di_soft_ring_list != NULL) { - soft_ring_stop_workers(dip->di_soft_ring_list, - dip->di_soft_ring_size); - soft_ring_set_destroy(dip->di_soft_ring_list, - dip->di_soft_ring_size); + /* + * Both ds_lock and di_lock are held as writer. + * As soft_ring_set_destroy() blocks for the + * worker thread(s) to complete, there is a possibility + * that the worker thread(s) could be in the process + * of draining the queue and is blocked waiting for + * either ds_lock or di_lock. Moreover the NIC interrupt + * thread could be blocked in dls_accept(). + * To avoid deadlock condition, taskq thread would be + * created to handle soft_ring_set_destroy() and + * blocking if required which would avoid holding + * both ds_lock and di_lock. + * NOTE: we cannot drop either locks here, due to + * weird race conditions seen. + */ + ring_taskq = (soft_ring_taskq_t *) + kmem_zalloc(sizeof (soft_ring_taskq_t), KM_NOSLEEP); + if (ring_taskq == NULL) { + rw_exit(&(dip->di_lock)); + return (B_FALSE); + } + ring_taskq->ringp_list = dip->di_soft_ring_list; + ring_taskq->ring_size = dip->di_soft_ring_size; + if (taskq_dispatch(system_taskq, dls_taskq_stop_soft_ring, + ring_taskq, TQ_NOSLEEP) == NULL) { + rw_exit(&(dip->di_lock)); + kmem_free(ring_taskq, sizeof (soft_ring_taskq_t)); + return (B_FALSE); + } dip->di_soft_ring_list = NULL; } dip->di_soft_ring_size = 0;
--- a/usr/src/uts/common/sys/dls_soft_ring.h Mon Oct 01 15:49:09 2007 -0700 +++ b/usr/src/uts/common/sys/dls_soft_ring.h Mon Oct 01 16:07:13 2007 -0700 @@ -82,7 +82,6 @@ uint_t, pri_t); extern soft_ring_t **soft_ring_set_create(char *, processorid_t, clock_t, uint_t, pri_t, int); -extern void soft_ring_set_destroy(soft_ring_t **, int); extern void soft_ring_bind(void *, processorid_t); extern void soft_ring_unbind(void *); extern void dls_soft_ring_fanout(void *, void *, mblk_t *, mac_header_info_t *);