aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeil Horman <nhorman@tuxdriver.com>2011-08-08 15:13:54 -0400
committerJoerg Roedel <joerg.roedel@amd.com>2011-08-23 09:36:00 -0400
commitc6a21d0b8d667f403bc6186ef215a504a26ab682 (patch)
treed9626c31a7a3a91b9f2c92fc56f210673660dab1
parentfcb8ce5cfe30ca9ca5c9a79cdfe26d1993e65e0c (diff)
dma-debug: hash_bucket_find needs to allow for offsets within an entry
Summary: Users of the pci_dma_sync_single_* api allow users to sync address ranges within the range of a mapped entry (i.e. you can dma map address X to dma_addr_t A and then pci_dma_sync_single on dma_addr_t A+1. The dma-debug library however assume dma syncs will always occur using the base address of a mapped region, and uses that assumption to find entries in its hash table. Since thats often (but not always the case), the dma debug library can give us false errors about missing entries, which are reported as syncing of memory not allocated by the driver. This was noted in the cxgb3 driver as this error: WARNING: at lib/dma-debug.c:902 check_sync+0xdd/0x48c() Hardware name: To be filled by O.E.M. cxgb3 0000:01:00.0: DMA-API: device driver tries to sync DMA memory it has not allocated [device address=0x00000000fff97800] [size=1984 bytes] Modules linked in: autofs4 sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf ip6t_REJECT nf_conntrack_ipv6 ip6table_filter ip6_tables ipv6 uinput snd_hda_codec_intelhdmi snd_hda_codec_realtek snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer e1000e snd soundcore r8169 cxgb3 iTCO_wdt snd_page_alloc mii shpchp i2c_i801 iTCO_vendor_support mdio microcode firewire_ohci firewire_core crc_itu_t ata_generic pata_acpi i915 drm_kms_helper drm i2c_algo_bit i2c_core video output [last unloaded: scsi_wait_scan] Pid: 1818, comm: ifconfig Not tainted 2.6.35-0.23.rc3.git6.fc14.x86_64 #1 Call Trace: [<ffffffff81050f71>] warn_slowpath_common+0x85/0x9d [<ffffffff8105102c>] warn_slowpath_fmt+0x46/0x48 [<ffffffff8124658e>] ? check_sync+0x39/0x48c [<ffffffff8107c470>] ? trace_hardirqs_on+0xd/0xf [<ffffffff81246632>] check_sync+0xdd/0x48c [<ffffffff81246ca6>] debug_dma_sync_single_for_device+0x3f/0x41 [<ffffffffa011615c>] ? pci_map_page+0x84/0x97 [cxgb3] [<ffffffffa0117bc3>] pci_dma_sync_single_for_device.clone.0+0x65/0x6e [cxgb3] [<ffffffffa0117ed1>] refill_fl+0x305/0x30a [cxgb3] [<ffffffffa011857d>] t3_sge_alloc_qset+0x6a7/0x821 [cxgb3] [<ffffffffa010a07b>] cxgb_up+0x4d0/0xe62 [cxgb3] [<ffffffff81086037>] ? __module_text_address+0x12/0x58 [<ffffffffa010aa4c>] cxgb_open+0x3f/0x309 [cxgb3] [<ffffffff813e9f6c>] __dev_open+0x8e/0xbc [<ffffffff813e7ca5>] __dev_change_flags+0xbe/0x142 [<ffffffff813e9ea8>] dev_change_flags+0x21/0x57 [<ffffffff81445937>] devinet_ioctl+0x29a/0x54b [<ffffffff811f9a87>] ? inode_has_perm+0xaa/0xce [<ffffffff81446ed2>] inet_ioctl+0x8f/0xa7 [<ffffffff813d683a>] sock_do_ioctl+0x29/0x48 [<ffffffff813d6c83>] sock_ioctl+0x213/0x222 [<ffffffff81137f78>] vfs_ioctl+0x32/0xa6 [<ffffffff811384e2>] do_vfs_ioctl+0x47a/0x4b3 [<ffffffff81138571>] sys_ioctl+0x56/0x79 [<ffffffff81009c32>] system_call_fastpath+0x16/0x1b ---[ end trace 69a4d4cc77b58004 ]--- (some edits by Joerg Roedel) Signed-off-by: Neil Horman <nhorman@tuxdriver.com> Reported-by: Jay Fenalson <fenlason@redhat.com> CC: Divy LeRay <divy@chelsio.com> CC: Stanislaw Gruszka <sgruszka@redhat.com> CC: Joerg Roedel <joerg.roedel@amd.com> CC: Arnd Bergmann <arnd@arndb.de> Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
-rw-r--r--lib/dma-debug.c68
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
65typedef bool (*match_fn)(struct dma_debug_entry *, struct dma_debug_entry *);
66
65struct hash_bucket { 67struct 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
245static 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
251static 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 */
246static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, 267static 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
317static 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
323static 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)