Commit c6a21d0b8d667f403bc6186ef215a504a26ab682

Authored by Neil Horman
Committed by Joerg Roedel
1 parent fcb8ce5cfe

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>

Showing 1 changed file with 61 additions and 7 deletions Side-by-side Diff

... ... @@ -62,6 +62,8 @@
62 62 #endif
63 63 };
64 64  
  65 +typedef bool (*match_fn)(struct dma_debug_entry *, struct dma_debug_entry *);
  66 +
65 67 struct hash_bucket {
66 68 struct list_head list;
67 69 spinlock_t lock;
68 70  
69 71  
... ... @@ -240,18 +242,37 @@
240 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 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,
247   - struct dma_debug_entry *ref)
  267 +static struct dma_debug_entry *__hash_bucket_find(struct hash_bucket *bucket,
  268 + struct dma_debug_entry *ref,
  269 + match_fn match)
248 270 {
249 271 struct dma_debug_entry *entry, *ret = NULL;
250 272 int matches = 0, match_lvl, last_lvl = 0;
251 273  
252 274 list_for_each_entry(entry, &bucket->list, list) {
253   - if ((entry->dev_addr != ref->dev_addr) ||
254   - (entry->dev != ref->dev))
  275 + if (!match(ref, entry))
255 276 continue;
256 277  
257 278 /*
... ... @@ -293,6 +314,39 @@
293 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 351 * Add an entry to a hash bucket
298 352 */
... ... @@ -802,7 +856,7 @@
802 856 }
803 857  
804 858 bucket = get_hash_bucket(ref, &flags);
805   - entry = hash_bucket_find(bucket, ref);
  859 + entry = bucket_find_exact(bucket, ref);
806 860  
807 861 if (!entry) {
808 862 err_printk(ref->dev, NULL, "DMA-API: device driver tries "
... ... @@ -902,7 +956,7 @@
902 956  
903 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 961 if (!entry) {
908 962 err_printk(dev, NULL, "DMA-API: device driver tries "
... ... @@ -1060,7 +1114,7 @@
1060 1114 int mapped_ents;
1061 1115  
1062 1116 bucket = get_hash_bucket(ref, &flags);
1063   - entry = hash_bucket_find(bucket, ref);
  1117 + entry = bucket_find_exact(bucket, ref);
1064 1118 mapped_ents = 0;
1065 1119  
1066 1120 if (entry)