dma-debug: fix locking bug in check_unmap()

In check_unmap() it is possible to get into a dead-locked state if
dma_mapping_error is called.  The problem is that the bucket is locked in
check_unmap, and locked again by debug_dma_mapping_error which is called
by dma_mapping_error.  To resolve that we must release the lock on the
bucket before making the call to dma_mapping_error.

[akpm@linux-foundation.org: restore 80-col trickery to be consistent with the rest of the file]
Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Reviewed-by: Shuah Khan <shuah.khan@hp.com>
Tested-by: Shuah Khan <shuah.khan@hp.com>
Cc: Jakub Kicinski <kubakici@wp.pl>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Alexander Duyck 2013-03-22 15:04:48 -07:00 коммит произвёл Linus Torvalds
Родитель 0ef1594c01
Коммит 8d640a51ec
1 изменённых файлов: 12 добавлений и 9 удалений

Просмотреть файл

@ -862,17 +862,21 @@ static void check_unmap(struct dma_debug_entry *ref)
entry = bucket_find_exact(bucket, ref); entry = bucket_find_exact(bucket, ref);
if (!entry) { if (!entry) {
/* must drop lock before calling dma_mapping_error */
put_hash_bucket(bucket, &flags);
if (dma_mapping_error(ref->dev, ref->dev_addr)) { if (dma_mapping_error(ref->dev, ref->dev_addr)) {
err_printk(ref->dev, NULL, err_printk(ref->dev, NULL,
"DMA-API: device driver tries " "DMA-API: device driver tries to free an "
"to free an invalid DMA memory address\n"); "invalid DMA memory address\n");
return; } else {
err_printk(ref->dev, NULL,
"DMA-API: device driver tries to free DMA "
"memory it has not allocated [device "
"address=0x%016llx] [size=%llu bytes]\n",
ref->dev_addr, ref->size);
} }
err_printk(ref->dev, NULL, "DMA-API: device driver tries " return;
"to free DMA memory it has not allocated "
"[device address=0x%016llx] [size=%llu bytes]\n",
ref->dev_addr, ref->size);
goto out;
} }
if (ref->size != entry->size) { if (ref->size != entry->size) {
@ -936,7 +940,6 @@ static void check_unmap(struct dma_debug_entry *ref)
hash_bucket_del(entry); hash_bucket_del(entry);
dma_entry_free(entry); dma_entry_free(entry);
out:
put_hash_bucket(bucket, &flags); put_hash_bucket(bucket, &flags);
} }