diff options
Diffstat (limited to 'lib/dma-debug.c')
-rw-r--r-- | lib/dma-debug.c | 69 |
1 files changed, 62 insertions, 7 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index db07bfd9298e..74c6c7fce749 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -24,6 +24,7 @@ | |||
24 | #include <linux/spinlock.h> | 24 | #include <linux/spinlock.h> |
25 | #include <linux/debugfs.h> | 25 | #include <linux/debugfs.h> |
26 | #include <linux/uaccess.h> | 26 | #include <linux/uaccess.h> |
27 | #include <linux/export.h> | ||
27 | #include <linux/device.h> | 28 | #include <linux/device.h> |
28 | #include <linux/types.h> | 29 | #include <linux/types.h> |
29 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
@@ -62,6 +63,8 @@ struct dma_debug_entry { | |||
62 | #endif | 63 | #endif |
63 | }; | 64 | }; |
64 | 65 | ||
66 | typedef bool (*match_fn)(struct dma_debug_entry *, struct dma_debug_entry *); | ||
67 | |||
65 | struct hash_bucket { | 68 | struct hash_bucket { |
66 | struct list_head list; | 69 | struct list_head list; |
67 | spinlock_t lock; | 70 | spinlock_t lock; |
@@ -240,18 +243,37 @@ static void put_hash_bucket(struct hash_bucket *bucket, | |||
240 | spin_unlock_irqrestore(&bucket->lock, __flags); | 243 | spin_unlock_irqrestore(&bucket->lock, __flags); |
241 | } | 244 | } |
242 | 245 | ||
246 | static bool exact_match(struct dma_debug_entry *a, struct dma_debug_entry *b) | ||
247 | { | ||
248 | return ((a->dev_addr == a->dev_addr) && | ||
249 | (a->dev == b->dev)) ? true : false; | ||
250 | } | ||
251 | |||
252 | static bool containing_match(struct dma_debug_entry *a, | ||
253 | struct dma_debug_entry *b) | ||
254 | { | ||
255 | if (a->dev != b->dev) | ||
256 | return false; | ||
257 | |||
258 | if ((b->dev_addr <= a->dev_addr) && | ||
259 | ((b->dev_addr + b->size) >= (a->dev_addr + a->size))) | ||
260 | return true; | ||
261 | |||
262 | return false; | ||
263 | } | ||
264 | |||
243 | /* | 265 | /* |
244 | * Search a given entry in the hash bucket list | 266 | * Search a given entry in the hash bucket list |
245 | */ | 267 | */ |
246 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, | 268 | static struct dma_debug_entry *__hash_bucket_find(struct hash_bucket *bucket, |
247 | struct dma_debug_entry *ref) | 269 | struct dma_debug_entry *ref, |
270 | match_fn match) | ||
248 | { | 271 | { |
249 | struct dma_debug_entry *entry, *ret = NULL; | 272 | struct dma_debug_entry *entry, *ret = NULL; |
250 | int matches = 0, match_lvl, last_lvl = 0; | 273 | int matches = 0, match_lvl, last_lvl = 0; |
251 | 274 | ||
252 | list_for_each_entry(entry, &bucket->list, list) { | 275 | list_for_each_entry(entry, &bucket->list, list) { |
253 | if ((entry->dev_addr != ref->dev_addr) || | 276 | if (!match(ref, entry)) |
254 | (entry->dev != ref->dev)) | ||
255 | continue; | 277 | continue; |
256 | 278 | ||
257 | /* | 279 | /* |
@@ -293,6 +315,39 @@ static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, | |||
293 | return ret; | 315 | return ret; |
294 | } | 316 | } |
295 | 317 | ||
318 | static struct dma_debug_entry *bucket_find_exact(struct hash_bucket *bucket, | ||
319 | struct dma_debug_entry *ref) | ||
320 | { | ||
321 | return __hash_bucket_find(bucket, ref, exact_match); | ||
322 | } | ||
323 | |||
324 | static struct dma_debug_entry *bucket_find_contain(struct hash_bucket **bucket, | ||
325 | struct dma_debug_entry *ref, | ||
326 | unsigned long *flags) | ||
327 | { | ||
328 | |||
329 | unsigned int max_range = dma_get_max_seg_size(ref->dev); | ||
330 | struct dma_debug_entry *entry, index = *ref; | ||
331 | unsigned int range = 0; | ||
332 | |||
333 | while (range <= max_range) { | ||
334 | entry = __hash_bucket_find(*bucket, &index, containing_match); | ||
335 | |||
336 | if (entry) | ||
337 | return entry; | ||
338 | |||
339 | /* | ||
340 | * Nothing found, go back a hash bucket | ||
341 | */ | ||
342 | put_hash_bucket(*bucket, flags); | ||
343 | range += (1 << HASH_FN_SHIFT); | ||
344 | index.dev_addr -= (1 << HASH_FN_SHIFT); | ||
345 | *bucket = get_hash_bucket(&index, flags); | ||
346 | } | ||
347 | |||
348 | return NULL; | ||
349 | } | ||
350 | |||
296 | /* | 351 | /* |
297 | * Add an entry to a hash bucket | 352 | * Add an entry to a hash bucket |
298 | */ | 353 | */ |
@@ -802,7 +857,7 @@ static void check_unmap(struct dma_debug_entry *ref) | |||
802 | } | 857 | } |
803 | 858 | ||
804 | bucket = get_hash_bucket(ref, &flags); | 859 | bucket = get_hash_bucket(ref, &flags); |
805 | entry = hash_bucket_find(bucket, ref); | 860 | entry = bucket_find_exact(bucket, ref); |
806 | 861 | ||
807 | if (!entry) { | 862 | if (!entry) { |
808 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " | 863 | err_printk(ref->dev, NULL, "DMA-API: device driver tries " |
@@ -902,7 +957,7 @@ static void check_sync(struct device *dev, | |||
902 | 957 | ||
903 | bucket = get_hash_bucket(ref, &flags); | 958 | bucket = get_hash_bucket(ref, &flags); |
904 | 959 | ||
905 | entry = hash_bucket_find(bucket, ref); | 960 | entry = bucket_find_contain(&bucket, ref, &flags); |
906 | 961 | ||
907 | if (!entry) { | 962 | if (!entry) { |
908 | err_printk(dev, NULL, "DMA-API: device driver tries " | 963 | err_printk(dev, NULL, "DMA-API: device driver tries " |
@@ -1060,7 +1115,7 @@ static int get_nr_mapped_entries(struct device *dev, | |||
1060 | int mapped_ents; | 1115 | int mapped_ents; |
1061 | 1116 | ||
1062 | bucket = get_hash_bucket(ref, &flags); | 1117 | bucket = get_hash_bucket(ref, &flags); |
1063 | entry = hash_bucket_find(bucket, ref); | 1118 | entry = bucket_find_exact(bucket, ref); |
1064 | mapped_ents = 0; | 1119 | mapped_ents = 0; |
1065 | 1120 | ||
1066 | if (entry) | 1121 | if (entry) |