diff options
Diffstat (limited to 'lib/dma-debug.c')
| -rw-r--r-- | lib/dma-debug.c | 45 | 
1 files changed, 31 insertions, 14 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 5e396accd3d..d87a17a819d 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c @@ -862,17 +862,21 @@ static void check_unmap(struct dma_debug_entry *ref)  	entry = bucket_find_exact(bucket, ref);  	if (!entry) { +		/* must drop lock before calling dma_mapping_error */ +		put_hash_bucket(bucket, &flags); +  		if (dma_mapping_error(ref->dev, ref->dev_addr)) {  			err_printk(ref->dev, NULL, -				   "DMA-API: device driver tries " -				   "to free an invalid DMA memory address\n"); -			return; +				   "DMA-API: device driver tries to free an " +				   "invalid DMA memory address\n"); +		} 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 " -			   "to free DMA memory it has not allocated " -			   "[device address=0x%016llx] [size=%llu bytes]\n", -			   ref->dev_addr, ref->size); -		goto out; +		return;  	}  	if (ref->size != entry->size) { @@ -936,7 +940,6 @@ static void check_unmap(struct dma_debug_entry *ref)  	hash_bucket_del(entry);  	dma_entry_free(entry); -out:  	put_hash_bucket(bucket, &flags);  } @@ -1082,13 +1085,27 @@ void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr)  	ref.dev = dev;  	ref.dev_addr = dma_addr;  	bucket = get_hash_bucket(&ref, &flags); -	entry = bucket_find_exact(bucket, &ref); -	if (!entry) -		goto out; +	list_for_each_entry(entry, &bucket->list, list) { +		if (!exact_match(&ref, entry)) +			continue; + +		/* +		 * The same physical address can be mapped multiple +		 * times. Without a hardware IOMMU this results in the +		 * same device addresses being put into the dma-debug +		 * hash multiple times too. This can result in false +		 * positives being reported. Therefore we implement a +		 * best-fit algorithm here which updates the first entry +		 * from the hash which fits the reference value and is +		 * not currently listed as being checked. +		 */ +		if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { +			entry->map_err_type = MAP_ERR_CHECKED; +			break; +		} +	} -	entry->map_err_type = MAP_ERR_CHECKED; -out:  	put_hash_bucket(bucket, &flags);  }  EXPORT_SYMBOL(debug_dma_mapping_error);  |