summaryrefslogtreecommitdiff
path: root/usr/src
diff options
context:
space:
mode:
authorcindi <none@none>2006-11-13 19:51:46 -0800
committercindi <none@none>2006-11-13 19:51:46 -0800
commitfa52d01bb7e683bd3f348e8daeacb561fc8b0a52 (patch)
tree98ac8af566752599368ec8d9adec4e482ca15189 /usr/src
parent3a9d44d99a8352803b186773302a309b65111b23 (diff)
downloadillumos-gate-fa52d01bb7e683bd3f348e8daeacb561fc8b0a52.tar.gz
6480790 deadlock, fmc_grow does a kmem_free while a high level (spin) mutex is held from ndi_fmc_insert
Diffstat (limited to 'usr/src')
-rw-r--r--usr/src/uts/common/os/ndifm.c20
-rw-r--r--usr/src/uts/common/sys/ddifm_impl.h3
2 files changed, 13 insertions, 10 deletions
diff --git a/usr/src/uts/common/os/ndifm.c b/usr/src/uts/common/os/ndifm.c
index b626dbb9c4..8c7d1c7d23 100644
--- a/usr/src/uts/common/os/ndifm.c
+++ b/usr/src/uts/common/os/ndifm.c
@@ -188,6 +188,7 @@ i_ndi_fmc_create(ndi_fmc_t **fcpp, int qlen, ddi_iblock_cookie_t ibc)
fcp = kmem_zalloc(sizeof (ndi_fmc_t), KM_SLEEP);
mutex_init(&fcp->fc_lock, NULL, MUTEX_DRIVER, ibc);
+ mutex_init(&fcp->fc_free_lock, NULL, MUTEX_DRIVER, NULL);
/* Preallocate and initialize entries for this fm cache */
fcp->fc_elems = kmem_zalloc(qlen * sizeof (ndi_fmcentry_t), KM_SLEEP);
@@ -230,7 +231,7 @@ fmc_grow(ndi_fmc_t *fcp, int flag, int grow_sz)
ndi_fmcentry_t *ncp, *oep, *nep, *nnep;
ASSERT(grow_sz);
- ASSERT(MUTEX_HELD(&fcp->fc_lock));
+ ASSERT(MUTEX_HELD(&fcp->fc_free_lock));
/* Allocate a new cache */
nlen = grow_sz + fcp->fc_len;
@@ -335,7 +336,7 @@ ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
}
ASSERT(*fpp == NULL);
- mutex_enter(&fcp->fc_lock);
+ mutex_enter(&fcp->fc_free_lock);
/* Get an entry from the free list */
fep = fcp->fc_free;
@@ -347,13 +348,14 @@ ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
/* Unable to get an entry or grow this cache */
atomic_add_64(
&fmhdl->fh_kstat.fek_fmc_full.value.ui64, 1);
- mutex_exit(&fcp->fc_lock);
+ mutex_exit(&fcp->fc_free_lock);
return;
}
atomic_add_64(&fmhdl->fh_kstat.fek_fmc_grew.value.ui64, 1);
fep = fcp->fc_free;
}
fcp->fc_free = fep->fce_prev;
+ mutex_exit(&fcp->fc_free_lock);
/*
* Set-up the handle resource and bus_specific information.
@@ -365,6 +367,7 @@ ndi_fmc_insert(dev_info_t *dip, int flag, void *resource, void *bus_specific)
*fpp = fep;
/* Add entry to the end of the active list */
+ mutex_enter(&fcp->fc_lock);
fep->fce_prev = fcp->fc_tail;
fcp->fc_tail->fce_next = fep;
fcp->fc_tail = fep;
@@ -404,7 +407,6 @@ ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
ASSERT(fcp);
- mutex_enter(&fcp->fc_lock);
fep = ((ddi_dma_impl_t *)resource)->dmai_error.err_fep;
((ddi_dma_impl_t *)resource)->dmai_error.err_fep = NULL;
} else if (flag == ACC_HANDLE) {
@@ -417,7 +419,6 @@ ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
ASSERT(fcp);
- mutex_enter(&fcp->fc_lock);
fep = ((ddi_acc_impl_t *)resource)->ahi_err->err_fep;
((ddi_acc_impl_t *)resource)->ahi_err->err_fep = NULL;
}
@@ -425,21 +426,22 @@ ndi_fmc_remove(dev_info_t *dip, int flag, const void *resource)
/*
* Resource not in cache, return
*/
- if (fep == NULL) {
- mutex_exit(&fcp->fc_lock);
+ if (fep == NULL)
return;
- }
+ mutex_enter(&fcp->fc_lock);
fep->fce_prev->fce_next = fep->fce_next;
if (fep == fcp->fc_tail)
fcp->fc_tail = fep->fce_prev;
else
fep->fce_next->fce_prev = fep->fce_prev;
+ mutex_exit(&fcp->fc_lock);
/* Add entry back to the free list */
+ mutex_enter(&fcp->fc_free_lock);
fep->fce_prev = fcp->fc_free;
fcp->fc_free = fep;
- mutex_exit(&fcp->fc_lock);
+ mutex_exit(&fcp->fc_free_lock);
}
int
diff --git a/usr/src/uts/common/sys/ddifm_impl.h b/usr/src/uts/common/sys/ddifm_impl.h
index fa5d648b77..a8dd18cce5 100644
--- a/usr/src/uts/common/sys/ddifm_impl.h
+++ b/usr/src/uts/common/sys/ddifm_impl.h
@@ -64,7 +64,8 @@ struct i_ddi_fmc_entry {
};
struct i_ddi_fmc {
- kmutex_t fc_lock; /* exclusive cache access */
+ kmutex_t fc_lock; /* cache active access */
+ kmutex_t fc_free_lock; /* cache freelist access */
int fc_len; /* length of FM cache array */
struct i_ddi_fmc_entry *fc_elems; /* FM cache array */
struct i_ddi_fmc_entry *fc_free; /* free list */