dma-debug: add hash functions for dma_debug_entries
[linux-3.10.git] / lib / dma-debug.c
1 /*
2  * Copyright (C) 2008 Advanced Micro Devices, Inc.
3  *
4  * Author: Joerg Roedel <joerg.roedel@amd.com>
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License version 2 as published
8  * by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
18  */
19
20 #include <linux/dma-debug.h>
21 #include <linux/spinlock.h>
22 #include <linux/types.h>
23 #include <linux/list.h>
24
25 #define HASH_SIZE       1024ULL
26 #define HASH_FN_SHIFT   13
27 #define HASH_FN_MASK    (HASH_SIZE - 1)
28
29 enum {
30         dma_debug_single,
31         dma_debug_page,
32         dma_debug_sg,
33         dma_debug_coherent,
34 };
35
36 struct dma_debug_entry {
37         struct list_head list;
38         struct device    *dev;
39         int              type;
40         phys_addr_t      paddr;
41         u64              dev_addr;
42         u64              size;
43         int              direction;
44         int              sg_call_ents;
45         int              sg_mapped_ents;
46 };
47
48 struct hash_bucket {
49         struct list_head list;
50         spinlock_t lock;
51 } __cacheline_aligned_in_smp;
52
53 /* Hash list to save the allocated dma addresses */
54 static struct hash_bucket dma_entry_hash[HASH_SIZE];
55
56 /*
57  * Hash related functions
58  *
59  * Every DMA-API request is saved into a struct dma_debug_entry. To
60  * have quick access to these structs they are stored into a hash.
61  */
62 static int hash_fn(struct dma_debug_entry *entry)
63 {
64         /*
65          * Hash function is based on the dma address.
66          * We use bits 20-27 here as the index into the hash
67          */
68         return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK;
69 }
70
71 /*
72  * Request exclusive access to a hash bucket for a given dma_debug_entry.
73  */
74 static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry,
75                                            unsigned long *flags)
76 {
77         int idx = hash_fn(entry);
78         unsigned long __flags;
79
80         spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags);
81         *flags = __flags;
82         return &dma_entry_hash[idx];
83 }
84
85 /*
86  * Give up exclusive access to the hash bucket
87  */
88 static void put_hash_bucket(struct hash_bucket *bucket,
89                             unsigned long *flags)
90 {
91         unsigned long __flags = *flags;
92
93         spin_unlock_irqrestore(&bucket->lock, __flags);
94 }
95
96 /*
97  * Search a given entry in the hash bucket list
98  */
99 static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket,
100                                                 struct dma_debug_entry *ref)
101 {
102         struct dma_debug_entry *entry;
103
104         list_for_each_entry(entry, &bucket->list, list) {
105                 if ((entry->dev_addr == ref->dev_addr) &&
106                     (entry->dev == ref->dev))
107                         return entry;
108         }
109
110         return NULL;
111 }
112
113 /*
114  * Add an entry to a hash bucket
115  */
116 static void hash_bucket_add(struct hash_bucket *bucket,
117                             struct dma_debug_entry *entry)
118 {
119         list_add_tail(&entry->list, &bucket->list);
120 }
121
122 /*
123  * Remove entry from a hash bucket list
124  */
125 static void hash_bucket_del(struct dma_debug_entry *entry)
126 {
127         list_del(&entry->list);
128 }
129
130 /*
131  * Wrapper function for adding an entry to the hash.
132  * This function takes care of locking itself.
133  */
134 static void add_dma_entry(struct dma_debug_entry *entry)
135 {
136         struct hash_bucket *bucket;
137         unsigned long flags;
138
139         bucket = get_hash_bucket(entry, &flags);
140         hash_bucket_add(bucket, entry);
141         put_hash_bucket(bucket, &flags);
142 }
143