|
@@ -62,6 +62,7 @@ struct dma_page { /* cacheable header for 'allocation' bytes */
|
|
};
|
|
};
|
|
|
|
|
|
static DEFINE_MUTEX(pools_lock);
|
|
static DEFINE_MUTEX(pools_lock);
|
|
|
|
+static DEFINE_MUTEX(pools_reg_lock);
|
|
|
|
|
|
static ssize_t
|
|
static ssize_t
|
|
show_pools(struct device *dev, struct device_attribute *attr, char *buf)
|
|
show_pools(struct device *dev, struct device_attribute *attr, char *buf)
|
|
@@ -132,6 +133,7 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
|
{
|
|
{
|
|
struct dma_pool *retval;
|
|
struct dma_pool *retval;
|
|
size_t allocation;
|
|
size_t allocation;
|
|
|
|
+ bool empty = false;
|
|
|
|
|
|
if (align == 0) {
|
|
if (align == 0) {
|
|
align = 1;
|
|
align = 1;
|
|
@@ -172,15 +174,34 @@ struct dma_pool *dma_pool_create(const char *name, struct device *dev,
|
|
|
|
|
|
INIT_LIST_HEAD(&retval->pools);
|
|
INIT_LIST_HEAD(&retval->pools);
|
|
|
|
|
|
|
|
+ /*
|
|
|
|
+ * pools_lock ensures that the ->dma_pools list does not get corrupted.
|
|
|
|
+ * pools_reg_lock ensures that there is not a race between
|
|
|
|
+ * dma_pool_create() and dma_pool_destroy() or within dma_pool_create()
|
|
|
|
+ * when the first invocation of dma_pool_create() failed on
|
|
|
|
+ * device_create_file() and the second assumes that it has been done (I
|
|
|
|
+ * know it is a short window).
|
|
|
|
+ */
|
|
|
|
+ mutex_lock(&pools_reg_lock);
|
|
mutex_lock(&pools_lock);
|
|
mutex_lock(&pools_lock);
|
|
- if (list_empty(&dev->dma_pools) &&
|
|
|
|
- device_create_file(dev, &dev_attr_pools)) {
|
|
|
|
- kfree(retval);
|
|
|
|
- retval = NULL;
|
|
|
|
- } else
|
|
|
|
- list_add(&retval->pools, &dev->dma_pools);
|
|
|
|
|
|
+ if (list_empty(&dev->dma_pools))
|
|
|
|
+ empty = true;
|
|
|
|
+ list_add(&retval->pools, &dev->dma_pools);
|
|
mutex_unlock(&pools_lock);
|
|
mutex_unlock(&pools_lock);
|
|
-
|
|
|
|
|
|
+ if (empty) {
|
|
|
|
+ int err;
|
|
|
|
+
|
|
|
|
+ err = device_create_file(dev, &dev_attr_pools);
|
|
|
|
+ if (err) {
|
|
|
|
+ mutex_lock(&pools_lock);
|
|
|
|
+ list_del(&retval->pools);
|
|
|
|
+ mutex_unlock(&pools_lock);
|
|
|
|
+ mutex_unlock(&pools_reg_lock);
|
|
|
|
+ kfree(retval);
|
|
|
|
+ return NULL;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&pools_reg_lock);
|
|
return retval;
|
|
return retval;
|
|
}
|
|
}
|
|
EXPORT_SYMBOL(dma_pool_create);
|
|
EXPORT_SYMBOL(dma_pool_create);
|
|
@@ -251,11 +272,17 @@ static void pool_free_page(struct dma_pool *pool, struct dma_page *page)
|
|
*/
|
|
*/
|
|
void dma_pool_destroy(struct dma_pool *pool)
|
|
void dma_pool_destroy(struct dma_pool *pool)
|
|
{
|
|
{
|
|
|
|
+ bool empty = false;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&pools_reg_lock);
|
|
mutex_lock(&pools_lock);
|
|
mutex_lock(&pools_lock);
|
|
list_del(&pool->pools);
|
|
list_del(&pool->pools);
|
|
if (pool->dev && list_empty(&pool->dev->dma_pools))
|
|
if (pool->dev && list_empty(&pool->dev->dma_pools))
|
|
- device_remove_file(pool->dev, &dev_attr_pools);
|
|
|
|
|
|
+ empty = true;
|
|
mutex_unlock(&pools_lock);
|
|
mutex_unlock(&pools_lock);
|
|
|
|
+ if (empty)
|
|
|
|
+ device_remove_file(pool->dev, &dev_attr_pools);
|
|
|
|
+ mutex_unlock(&pools_reg_lock);
|
|
|
|
|
|
while (!list_empty(&pool->page_list)) {
|
|
while (!list_empty(&pool->page_list)) {
|
|
struct dma_page *page;
|
|
struct dma_page *page;
|