diff options
author | Joerg Roedel <joerg.roedel@amd.com> | 2009-06-05 06:01:35 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2009-06-07 04:04:53 -0400 |
commit | 7caf6a49bb17d0377210693af5737563b31aa5ee (patch) | |
tree | 1a61bc90002bc2e3d1fc5d028e408b35fb765cbd /lib | |
parent | fe2245c905631a3a353504fc04388ce3dfaf9d9e (diff) |
dma-debug: change hash_bucket_find from first-fit to best-fit
Some device drivers map the same physical address multiple times to a
dma address. Without an IOMMU this results in the same dma address being
put into the dma-debug hash multiple times. With a first-fit match in
hash_bucket_find() this function may return the wrong dma_debug_entry.
This can result in false positive warnings. This patch fixes it by
changing the first-fit behavior of hash_bucket_find() into a best-fit
algorithm.
Reported-by: Torsten Kaiser <just.for.lkml@googlemail.com>
Reported-by: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Cc: lethal@linux-sh.org
Cc: just.for.lkml@googlemail.com
Cc: hancockrwd@gmail.com
Cc: jens.axboe@oracle.com
Cc: bharrosh@panasas.com
Cc: FUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: <stable@kernel.org>
LKML-Reference: <20090605104132.GE24836@amd.com>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/dma-debug.c | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index cdd205d6bf7c..8fcc09c91e1b 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -186,15 +186,50 @@ static void put_hash_bucket(struct hash_bucket *bucket, | |||
186 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, | 186 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, |
187 | struct dma_debug_entry *ref) | 187 | struct dma_debug_entry *ref) |
188 | { | 188 | { |
189 | struct dma_debug_entry *entry; | 189 | struct dma_debug_entry *entry, *ret = NULL; |
190 | int matches = 0, match_lvl, last_lvl = 0; | ||
190 | 191 | ||
191 | list_for_each_entry(entry, &bucket->list, list) { | 192 | list_for_each_entry(entry, &bucket->list, list) { |
192 | if ((entry->dev_addr == ref->dev_addr) && | 193 | if ((entry->dev_addr != ref->dev_addr) || |
193 | (entry->dev == ref->dev)) | 194 | (entry->dev != ref->dev)) |
195 | continue; | ||
196 | |||
197 | /* | ||
198 | * Some drivers map the same physical address multiple | ||
199 | * times. Without a hardware IOMMU this results in the | ||
200 | * same device addresses being put into the dma-debug | ||
201 | * hash multiple times too. This can result in false | ||
202 | * positives being reported. Therfore we implement a | ||
203 | * best-fit algorithm here which returns the entry from | ||
204 | * the hash which fits best to the reference value | ||
205 | * instead of the first-fit. | ||
206 | */ | ||
207 | matches += 1; | ||
208 | match_lvl = 0; | ||
209 | entry->size == ref->size ? ++match_lvl : match_lvl; | ||
210 | entry->type == ref->type ? ++match_lvl : match_lvl; | ||
211 | entry->direction == ref->direction ? ++match_lvl : match_lvl; | ||
212 | |||
213 | if (match_lvl == 3) { | ||
214 | /* perfect-fit - return the result */ | ||
194 | return entry; | 215 | return entry; |
216 | } else if (match_lvl > last_lvl) { | ||
217 | /* | ||
218 | * We found an entry that fits better then the | ||
219 | * previous one | ||
220 | */ | ||
221 | last_lvl = match_lvl; | ||
222 | ret = entry; | ||
223 | } | ||
195 | } | 224 | } |
196 | 225 | ||
197 | return NULL; | 226 | /* |
227 | * If we have multiple matches but no perfect-fit, just return | ||
228 | * NULL. | ||
229 | */ | ||
230 | ret = (matches == 1) ? ret : NULL; | ||
231 | |||
232 | return ret; | ||
198 | } | 233 | } |
199 | 234 | ||
200 | /* | 235 | /* |