aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorJoerg Roedel <joerg.roedel@amd.com>2009-06-05 06:01:35 -0400
committerIngo Molnar <mingo@elte.hu>2009-06-07 04:04:53 -0400
commit7caf6a49bb17d0377210693af5737563b31aa5ee (patch)
tree1a61bc90002bc2e3d1fc5d028e408b35fb765cbd /lib
parentfe2245c905631a3a353504fc04388ce3dfaf9d9e (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.c43
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,
186static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, 186static 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/*