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