Commit 6bf078715c1998d4d10716251cc10ce45908594c
1 parent
3b1e79ed73
dma-debug: add initialization code
Impact: add code to initialize dma-debug core data structures Signed-off-by: Joerg Roedel <joerg.roedel@amd.com>
Showing 2 changed files with 80 additions and 0 deletions Inline Diff
include/linux/dma-debug.h
1 | /* | 1 | /* |
2 | * Copyright (C) 2008 Advanced Micro Devices, Inc. | 2 | * Copyright (C) 2008 Advanced Micro Devices, Inc. |
3 | * | 3 | * |
4 | * Author: Joerg Roedel <joerg.roedel@amd.com> | 4 | * Author: Joerg Roedel <joerg.roedel@amd.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 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 | 7 | * under the terms of the GNU General Public License version 2 as published |
8 | * by the Free Software Foundation. | 8 | * by the Free Software Foundation. |
9 | * | 9 | * |
10 | * This program is distributed in the hope that it will be useful, | 10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. | 13 | * GNU General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU General Public License | 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 | 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 | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #ifndef __DMA_DEBUG_H | 20 | #ifndef __DMA_DEBUG_H |
21 | #define __DMA_DEBUG_H | 21 | #define __DMA_DEBUG_H |
22 | 22 | ||
23 | #include <linux/types.h> | ||
24 | |||
23 | struct device; | 25 | struct device; |
26 | |||
27 | #ifdef CONFIG_DMA_API_DEBUG | ||
28 | |||
29 | extern void dma_debug_init(u32 num_entries); | ||
30 | |||
31 | #else /* CONFIG_DMA_API_DEBUG */ | ||
32 | |||
33 | static inline void dma_debug_init(u32 num_entries) | ||
34 | { | ||
35 | } | ||
36 | |||
37 | #endif /* CONFIG_DMA_API_DEBUG */ | ||
24 | 38 | ||
25 | #endif /* __DMA_DEBUG_H */ | 39 | #endif /* __DMA_DEBUG_H */ |
26 | 40 |
lib/dma-debug.c
1 | /* | 1 | /* |
2 | * Copyright (C) 2008 Advanced Micro Devices, Inc. | 2 | * Copyright (C) 2008 Advanced Micro Devices, Inc. |
3 | * | 3 | * |
4 | * Author: Joerg Roedel <joerg.roedel@amd.com> | 4 | * Author: Joerg Roedel <joerg.roedel@amd.com> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | 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 | 7 | * under the terms of the GNU General Public License version 2 as published |
8 | * by the Free Software Foundation. | 8 | * by the Free Software Foundation. |
9 | * | 9 | * |
10 | * This program is distributed in the hope that it will be useful, | 10 | * This program is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | * GNU General Public License for more details. | 13 | * GNU General Public License for more details. |
14 | * | 14 | * |
15 | * You should have received a copy of the GNU General Public License | 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 | 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 | 17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/dma-debug.h> | 20 | #include <linux/dma-debug.h> |
21 | #include <linux/spinlock.h> | 21 | #include <linux/spinlock.h> |
22 | #include <linux/types.h> | 22 | #include <linux/types.h> |
23 | #include <linux/list.h> | 23 | #include <linux/list.h> |
24 | #include <linux/slab.h> | ||
24 | 25 | ||
25 | #define HASH_SIZE 1024ULL | 26 | #define HASH_SIZE 1024ULL |
26 | #define HASH_FN_SHIFT 13 | 27 | #define HASH_FN_SHIFT 13 |
27 | #define HASH_FN_MASK (HASH_SIZE - 1) | 28 | #define HASH_FN_MASK (HASH_SIZE - 1) |
28 | 29 | ||
29 | enum { | 30 | enum { |
30 | dma_debug_single, | 31 | dma_debug_single, |
31 | dma_debug_page, | 32 | dma_debug_page, |
32 | dma_debug_sg, | 33 | dma_debug_sg, |
33 | dma_debug_coherent, | 34 | dma_debug_coherent, |
34 | }; | 35 | }; |
35 | 36 | ||
36 | struct dma_debug_entry { | 37 | struct dma_debug_entry { |
37 | struct list_head list; | 38 | struct list_head list; |
38 | struct device *dev; | 39 | struct device *dev; |
39 | int type; | 40 | int type; |
40 | phys_addr_t paddr; | 41 | phys_addr_t paddr; |
41 | u64 dev_addr; | 42 | u64 dev_addr; |
42 | u64 size; | 43 | u64 size; |
43 | int direction; | 44 | int direction; |
44 | int sg_call_ents; | 45 | int sg_call_ents; |
45 | int sg_mapped_ents; | 46 | int sg_mapped_ents; |
46 | }; | 47 | }; |
47 | 48 | ||
48 | struct hash_bucket { | 49 | struct hash_bucket { |
49 | struct list_head list; | 50 | struct list_head list; |
50 | spinlock_t lock; | 51 | spinlock_t lock; |
51 | } __cacheline_aligned_in_smp; | 52 | } __cacheline_aligned_in_smp; |
52 | 53 | ||
53 | /* Hash list to save the allocated dma addresses */ | 54 | /* Hash list to save the allocated dma addresses */ |
54 | static struct hash_bucket dma_entry_hash[HASH_SIZE]; | 55 | static struct hash_bucket dma_entry_hash[HASH_SIZE]; |
55 | /* List of pre-allocated dma_debug_entry's */ | 56 | /* List of pre-allocated dma_debug_entry's */ |
56 | static LIST_HEAD(free_entries); | 57 | static LIST_HEAD(free_entries); |
57 | /* Lock for the list above */ | 58 | /* Lock for the list above */ |
58 | static DEFINE_SPINLOCK(free_entries_lock); | 59 | static DEFINE_SPINLOCK(free_entries_lock); |
59 | 60 | ||
60 | /* Global disable flag - will be set in case of an error */ | 61 | /* Global disable flag - will be set in case of an error */ |
61 | static bool global_disable __read_mostly; | 62 | static bool global_disable __read_mostly; |
62 | 63 | ||
63 | static u32 num_free_entries; | 64 | static u32 num_free_entries; |
64 | static u32 min_free_entries; | 65 | static u32 min_free_entries; |
65 | 66 | ||
66 | /* | 67 | /* |
67 | * Hash related functions | 68 | * Hash related functions |
68 | * | 69 | * |
69 | * Every DMA-API request is saved into a struct dma_debug_entry. To | 70 | * Every DMA-API request is saved into a struct dma_debug_entry. To |
70 | * have quick access to these structs they are stored into a hash. | 71 | * have quick access to these structs they are stored into a hash. |
71 | */ | 72 | */ |
72 | static int hash_fn(struct dma_debug_entry *entry) | 73 | static int hash_fn(struct dma_debug_entry *entry) |
73 | { | 74 | { |
74 | /* | 75 | /* |
75 | * Hash function is based on the dma address. | 76 | * Hash function is based on the dma address. |
76 | * We use bits 20-27 here as the index into the hash | 77 | * We use bits 20-27 here as the index into the hash |
77 | */ | 78 | */ |
78 | return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK; | 79 | return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK; |
79 | } | 80 | } |
80 | 81 | ||
81 | /* | 82 | /* |
82 | * Request exclusive access to a hash bucket for a given dma_debug_entry. | 83 | * Request exclusive access to a hash bucket for a given dma_debug_entry. |
83 | */ | 84 | */ |
84 | static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, | 85 | static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry, |
85 | unsigned long *flags) | 86 | unsigned long *flags) |
86 | { | 87 | { |
87 | int idx = hash_fn(entry); | 88 | int idx = hash_fn(entry); |
88 | unsigned long __flags; | 89 | unsigned long __flags; |
89 | 90 | ||
90 | spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags); | 91 | spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags); |
91 | *flags = __flags; | 92 | *flags = __flags; |
92 | return &dma_entry_hash[idx]; | 93 | return &dma_entry_hash[idx]; |
93 | } | 94 | } |
94 | 95 | ||
95 | /* | 96 | /* |
96 | * Give up exclusive access to the hash bucket | 97 | * Give up exclusive access to the hash bucket |
97 | */ | 98 | */ |
98 | static void put_hash_bucket(struct hash_bucket *bucket, | 99 | static void put_hash_bucket(struct hash_bucket *bucket, |
99 | unsigned long *flags) | 100 | unsigned long *flags) |
100 | { | 101 | { |
101 | unsigned long __flags = *flags; | 102 | unsigned long __flags = *flags; |
102 | 103 | ||
103 | spin_unlock_irqrestore(&bucket->lock, __flags); | 104 | spin_unlock_irqrestore(&bucket->lock, __flags); |
104 | } | 105 | } |
105 | 106 | ||
106 | /* | 107 | /* |
107 | * Search a given entry in the hash bucket list | 108 | * Search a given entry in the hash bucket list |
108 | */ | 109 | */ |
109 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, | 110 | static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket, |
110 | struct dma_debug_entry *ref) | 111 | struct dma_debug_entry *ref) |
111 | { | 112 | { |
112 | struct dma_debug_entry *entry; | 113 | struct dma_debug_entry *entry; |
113 | 114 | ||
114 | list_for_each_entry(entry, &bucket->list, list) { | 115 | list_for_each_entry(entry, &bucket->list, list) { |
115 | if ((entry->dev_addr == ref->dev_addr) && | 116 | if ((entry->dev_addr == ref->dev_addr) && |
116 | (entry->dev == ref->dev)) | 117 | (entry->dev == ref->dev)) |
117 | return entry; | 118 | return entry; |
118 | } | 119 | } |
119 | 120 | ||
120 | return NULL; | 121 | return NULL; |
121 | } | 122 | } |
122 | 123 | ||
123 | /* | 124 | /* |
124 | * Add an entry to a hash bucket | 125 | * Add an entry to a hash bucket |
125 | */ | 126 | */ |
126 | static void hash_bucket_add(struct hash_bucket *bucket, | 127 | static void hash_bucket_add(struct hash_bucket *bucket, |
127 | struct dma_debug_entry *entry) | 128 | struct dma_debug_entry *entry) |
128 | { | 129 | { |
129 | list_add_tail(&entry->list, &bucket->list); | 130 | list_add_tail(&entry->list, &bucket->list); |
130 | } | 131 | } |
131 | 132 | ||
132 | /* | 133 | /* |
133 | * Remove entry from a hash bucket list | 134 | * Remove entry from a hash bucket list |
134 | */ | 135 | */ |
135 | static void hash_bucket_del(struct dma_debug_entry *entry) | 136 | static void hash_bucket_del(struct dma_debug_entry *entry) |
136 | { | 137 | { |
137 | list_del(&entry->list); | 138 | list_del(&entry->list); |
138 | } | 139 | } |
139 | 140 | ||
140 | /* | 141 | /* |
141 | * Wrapper function for adding an entry to the hash. | 142 | * Wrapper function for adding an entry to the hash. |
142 | * This function takes care of locking itself. | 143 | * This function takes care of locking itself. |
143 | */ | 144 | */ |
144 | static void add_dma_entry(struct dma_debug_entry *entry) | 145 | static void add_dma_entry(struct dma_debug_entry *entry) |
145 | { | 146 | { |
146 | struct hash_bucket *bucket; | 147 | struct hash_bucket *bucket; |
147 | unsigned long flags; | 148 | unsigned long flags; |
148 | 149 | ||
149 | bucket = get_hash_bucket(entry, &flags); | 150 | bucket = get_hash_bucket(entry, &flags); |
150 | hash_bucket_add(bucket, entry); | 151 | hash_bucket_add(bucket, entry); |
151 | put_hash_bucket(bucket, &flags); | 152 | put_hash_bucket(bucket, &flags); |
152 | } | 153 | } |
153 | 154 | ||
154 | /* struct dma_entry allocator | 155 | /* struct dma_entry allocator |
155 | * | 156 | * |
156 | * The next two functions implement the allocator for | 157 | * The next two functions implement the allocator for |
157 | * struct dma_debug_entries. | 158 | * struct dma_debug_entries. |
158 | */ | 159 | */ |
159 | static struct dma_debug_entry *dma_entry_alloc(void) | 160 | static struct dma_debug_entry *dma_entry_alloc(void) |
160 | { | 161 | { |
161 | struct dma_debug_entry *entry = NULL; | 162 | struct dma_debug_entry *entry = NULL; |
162 | unsigned long flags; | 163 | unsigned long flags; |
163 | 164 | ||
164 | spin_lock_irqsave(&free_entries_lock, flags); | 165 | spin_lock_irqsave(&free_entries_lock, flags); |
165 | 166 | ||
166 | if (list_empty(&free_entries)) { | 167 | if (list_empty(&free_entries)) { |
167 | printk(KERN_ERR "DMA-API: debugging out of memory " | 168 | printk(KERN_ERR "DMA-API: debugging out of memory " |
168 | "- disabling\n"); | 169 | "- disabling\n"); |
169 | global_disable = true; | 170 | global_disable = true; |
170 | goto out; | 171 | goto out; |
171 | } | 172 | } |
172 | 173 | ||
173 | entry = list_entry(free_entries.next, struct dma_debug_entry, list); | 174 | entry = list_entry(free_entries.next, struct dma_debug_entry, list); |
174 | list_del(&entry->list); | 175 | list_del(&entry->list); |
175 | memset(entry, 0, sizeof(*entry)); | 176 | memset(entry, 0, sizeof(*entry)); |
176 | 177 | ||
177 | num_free_entries -= 1; | 178 | num_free_entries -= 1; |
178 | if (num_free_entries < min_free_entries) | 179 | if (num_free_entries < min_free_entries) |
179 | min_free_entries = num_free_entries; | 180 | min_free_entries = num_free_entries; |
180 | 181 | ||
181 | out: | 182 | out: |
182 | spin_unlock_irqrestore(&free_entries_lock, flags); | 183 | spin_unlock_irqrestore(&free_entries_lock, flags); |
183 | 184 | ||
184 | return entry; | 185 | return entry; |
185 | } | 186 | } |
186 | 187 | ||
187 | static void dma_entry_free(struct dma_debug_entry *entry) | 188 | static void dma_entry_free(struct dma_debug_entry *entry) |
188 | { | 189 | { |
189 | unsigned long flags; | 190 | unsigned long flags; |
190 | 191 | ||
191 | /* | 192 | /* |
192 | * add to beginning of the list - this way the entries are | 193 | * add to beginning of the list - this way the entries are |
193 | * more likely cache hot when they are reallocated. | 194 | * more likely cache hot when they are reallocated. |
194 | */ | 195 | */ |
195 | spin_lock_irqsave(&free_entries_lock, flags); | 196 | spin_lock_irqsave(&free_entries_lock, flags); |
196 | list_add(&entry->list, &free_entries); | 197 | list_add(&entry->list, &free_entries); |
197 | num_free_entries += 1; | 198 | num_free_entries += 1; |
198 | spin_unlock_irqrestore(&free_entries_lock, flags); | 199 | spin_unlock_irqrestore(&free_entries_lock, flags); |
200 | } | ||
201 | |||
202 | /* | ||
203 | * DMA-API debugging init code | ||
204 | * | ||
205 | * The init code does two things: | ||
206 | * 1. Initialize core data structures | ||
207 | * 2. Preallocate a given number of dma_debug_entry structs | ||
208 | */ | ||
209 | |||
210 | static int prealloc_memory(u32 num_entries) | ||
211 | { | ||
212 | struct dma_debug_entry *entry, *next_entry; | ||
213 | int i; | ||
214 | |||
215 | for (i = 0; i < num_entries; ++i) { | ||
216 | entry = kzalloc(sizeof(*entry), GFP_KERNEL); | ||
217 | if (!entry) | ||
218 | goto out_err; | ||
219 | |||
220 | list_add_tail(&entry->list, &free_entries); | ||
221 | } | ||
222 | |||
223 | num_free_entries = num_entries; | ||
224 | min_free_entries = num_entries; | ||
225 | |||
226 | printk(KERN_INFO "DMA-API: preallocated %d debug entries\n", | ||
227 | num_entries); | ||
228 | |||
229 | return 0; | ||
230 | |||
231 | out_err: | ||
232 | |||
233 | list_for_each_entry_safe(entry, next_entry, &free_entries, list) { | ||
234 | list_del(&entry->list); | ||
235 | kfree(entry); | ||
236 | } | ||
237 | |||
238 | return -ENOMEM; | ||
239 | } | ||
240 | |||
241 | /* | ||
242 | * Let the architectures decide how many entries should be preallocated. | ||
243 | */ | ||
244 | void dma_debug_init(u32 num_entries) | ||
245 | { | ||
246 | int i; | ||
247 | |||
248 | if (global_disable) | ||
249 | return; | ||
250 | |||
251 | for (i = 0; i < HASH_SIZE; ++i) { | ||
252 | INIT_LIST_HEAD(&dma_entry_hash[i].list); | ||
253 | dma_entry_hash[i].lock = SPIN_LOCK_UNLOCKED; | ||
254 | } | ||
255 | |||
256 | if (prealloc_memory(num_entries) != 0) { | ||
257 | printk(KERN_ERR "DMA-API: debugging out of memory error " | ||
258 | "- disabled\n"); | ||
259 | global_disable = true; | ||
260 | |||
261 | return; | ||
262 | } | ||
263 | |||
264 | printk(KERN_INFO "DMA-API: debugging enabled by kernel config\n"); | ||
199 | } | 265 | } |
200 | 266 | ||
201 | 267 |