dma-debug: add debugfs interface
[safe/jmp/linux-2.6] / 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/debugfs.h>
23 #include <linux/types.h>
24 #include <linux/list.h>
25 #include <linux/slab.h>
26
27 #define HASH_SIZE       1024ULL
28 #define HASH_FN_SHIFT   13
29 #define HASH_FN_MASK    (HASH_SIZE - 1)
30
31 enum {
32         dma_debug_single,
33         dma_debug_page,
34         dma_debug_sg,
35         dma_debug_coherent,
36 };
37
38 struct dma_debug_entry {
39         struct list_head list;
40         struct device    *dev;
41         int              type;
42         phys_addr_t      paddr;
43         u64              dev_addr;
44         u64              size;
45         int              direction;
46         int              sg_call_ents;
47         int              sg_mapped_ents;
48 };
49
50 struct hash_bucket {
51         struct list_head list;
52         spinlock_t lock;
53 } __cacheline_aligned_in_smp;
54
55 /* Hash list to save the allocated dma addresses */
56 static struct hash_bucket dma_entry_hash[HASH_SIZE];
57 /* List of pre-allocated dma_debug_entry's */
58 static LIST_HEAD(free_entries);
59 /* Lock for the list above */
60 static DEFINE_SPINLOCK(free_entries_lock);
61
62 /* Global disable flag - will be set in case of an error */
63 static bool global_disable __read_mostly;
64
65 /* Global error count */
66 static u32 error_count;
67
68 /* Global error show enable*/
69 static u32 show_all_errors __read_mostly;
70 /* Number of errors to show */
71 static u32 show_num_errors = 1;
72
73 static u32 num_free_entries;
74 static u32 min_free_entries;
75
76 /* number of preallocated entries requested by kernel cmdline */
77 static u32 req_entries;
78
79 /* debugfs dentry's for the stuff above */
80 static struct dentry *dma_debug_dent        __read_mostly;
81 static struct dentry *global_disable_dent   __read_mostly;
82 static struct dentry *error_count_dent      __read_mostly;
83 static struct dentry *show_all_errors_dent  __read_mostly;
84 static struct dentry *show_num_errors_dent  __read_mostly;
85 static struct dentry *num_free_entries_dent __read_mostly;
86 static struct dentry *min_free_entries_dent __read_mostly;
87
88 /*
89  * Hash related functions
90  *
91  * Every DMA-API request is saved into a struct dma_debug_entry. To
92  * have quick access to these structs they are stored into a hash.
93  */
94 static int hash_fn(struct dma_debug_entry *entry)
95 {
96         /*
97          * Hash function is based on the dma address.
98          * We use bits 20-27 here as the index into the hash
99          */
100         return (entry->dev_addr >> HASH_FN_SHIFT) & HASH_FN_MASK;
101 }
102
103 /*
104  * Request exclusive access to a hash bucket for a given dma_debug_entry.
105  */
106 static struct hash_bucket *get_hash_bucket(struct dma_debug_entry *entry,
107                                            unsigned long *flags)
108 {
109         int idx = hash_fn(entry);
110         unsigned long __flags;
111
112         spin_lock_irqsave(&dma_entry_hash[idx].lock, __flags);
113         *flags = __flags;
114         return &dma_entry_hash[idx];
115 }
116
117 /*
118  * Give up exclusive access to the hash bucket
119  */
120 static void put_hash_bucket(struct hash_bucket *bucket,
121                             unsigned long *flags)
122 {
123         unsigned long __flags = *flags;
124
125         spin_unlock_irqrestore(&bucket->lock, __flags);
126 }
127
128 /*
129  * Search a given entry in the hash bucket list
130  */
131 static struct dma_debug_entry *hash_bucket_find(struct hash_bucket *bucket,
132                                                 struct dma_debug_entry *ref)
133 {
134         struct dma_debug_entry *entry;
135
136         list_for_each_entry(entry, &bucket->list, list) {
137                 if ((entry->dev_addr == ref->dev_addr) &&
138                     (entry->dev == ref->dev))
139                         return entry;
140         }
141
142         return NULL;
143 }
144
145 /*
146  * Add an entry to a hash bucket
147  */
148 static void hash_bucket_add(struct hash_bucket *bucket,
149                             struct dma_debug_entry *entry)
150 {
151         list_add_tail(&entry->list, &bucket->list);
152 }
153
154 /*
155  * Remove entry from a hash bucket list
156  */
157 static void hash_bucket_del(struct dma_debug_entry *entry)
158 {
159         list_del(&entry->list);
160 }
161
162 /*
163  * Wrapper function for adding an entry to the hash.
164  * This function takes care of locking itself.
165  */
166 static void add_dma_entry(struct dma_debug_entry *entry)
167 {
168         struct hash_bucket *bucket;
169         unsigned long flags;
170
171         bucket = get_hash_bucket(entry, &flags);
172         hash_bucket_add(bucket, entry);
173         put_hash_bucket(bucket, &flags);
174 }
175
176 /* struct dma_entry allocator
177  *
178  * The next two functions implement the allocator for
179  * struct dma_debug_entries.
180  */
181 static struct dma_debug_entry *dma_entry_alloc(void)
182 {
183         struct dma_debug_entry *entry = NULL;
184         unsigned long flags;
185
186         spin_lock_irqsave(&free_entries_lock, flags);
187
188         if (list_empty(&free_entries)) {
189                 printk(KERN_ERR "DMA-API: debugging out of memory "
190                                 "- disabling\n");
191                 global_disable = true;
192                 goto out;
193         }
194
195         entry = list_entry(free_entries.next, struct dma_debug_entry, list);
196         list_del(&entry->list);
197         memset(entry, 0, sizeof(*entry));
198
199         num_free_entries -= 1;
200         if (num_free_entries < min_free_entries)
201                 min_free_entries = num_free_entries;
202
203 out:
204         spin_unlock_irqrestore(&free_entries_lock, flags);
205
206         return entry;
207 }
208
209 static void dma_entry_free(struct dma_debug_entry *entry)
210 {
211         unsigned long flags;
212
213         /*
214          * add to beginning of the list - this way the entries are
215          * more likely cache hot when they are reallocated.
216          */
217         spin_lock_irqsave(&free_entries_lock, flags);
218         list_add(&entry->list, &free_entries);
219         num_free_entries += 1;
220         spin_unlock_irqrestore(&free_entries_lock, flags);
221 }
222
223 /*
224  * DMA-API debugging init code
225  *
226  * The init code does two things:
227  *   1. Initialize core data structures
228  *   2. Preallocate a given number of dma_debug_entry structs
229  */
230
231 static int prealloc_memory(u32 num_entries)
232 {
233         struct dma_debug_entry *entry, *next_entry;
234         int i;
235
236         for (i = 0; i < num_entries; ++i) {
237                 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
238                 if (!entry)
239                         goto out_err;
240
241                 list_add_tail(&entry->list, &free_entries);
242         }
243
244         num_free_entries = num_entries;
245         min_free_entries = num_entries;
246
247         printk(KERN_INFO "DMA-API: preallocated %d debug entries\n",
248                         num_entries);
249
250         return 0;
251
252 out_err:
253
254         list_for_each_entry_safe(entry, next_entry, &free_entries, list) {
255                 list_del(&entry->list);
256                 kfree(entry);
257         }
258
259         return -ENOMEM;
260 }
261
262 static int dma_debug_fs_init(void)
263 {
264         dma_debug_dent = debugfs_create_dir("dma-api", NULL);
265         if (!dma_debug_dent) {
266                 printk(KERN_ERR "DMA-API: can not create debugfs directory\n");
267                 return -ENOMEM;
268         }
269
270         global_disable_dent = debugfs_create_bool("disabled", 0444,
271                         dma_debug_dent,
272                         (u32 *)&global_disable);
273         if (!global_disable_dent)
274                 goto out_err;
275
276         error_count_dent = debugfs_create_u32("error_count", 0444,
277                         dma_debug_dent, &error_count);
278         if (!error_count_dent)
279                 goto out_err;
280
281         show_all_errors_dent = debugfs_create_u32("all_errors", 0644,
282                         dma_debug_dent,
283                         &show_all_errors);
284         if (!show_all_errors_dent)
285                 goto out_err;
286
287         show_num_errors_dent = debugfs_create_u32("num_errors", 0644,
288                         dma_debug_dent,
289                         &show_num_errors);
290         if (!show_num_errors_dent)
291                 goto out_err;
292
293         num_free_entries_dent = debugfs_create_u32("num_free_entries", 0444,
294                         dma_debug_dent,
295                         &num_free_entries);
296         if (!num_free_entries_dent)
297                 goto out_err;
298
299         min_free_entries_dent = debugfs_create_u32("min_free_entries", 0444,
300                         dma_debug_dent,
301                         &min_free_entries);
302         if (!min_free_entries_dent)
303                 goto out_err;
304
305         return 0;
306
307 out_err:
308         debugfs_remove_recursive(dma_debug_dent);
309
310         return -ENOMEM;
311 }
312
313
314 /*
315  * Let the architectures decide how many entries should be preallocated.
316  */
317 void dma_debug_init(u32 num_entries)
318 {
319         int i;
320
321         if (global_disable)
322                 return;
323
324         for (i = 0; i < HASH_SIZE; ++i) {
325                 INIT_LIST_HEAD(&dma_entry_hash[i].list);
326                 dma_entry_hash[i].lock = SPIN_LOCK_UNLOCKED;
327         }
328
329         if (dma_debug_fs_init() != 0) {
330                 printk(KERN_ERR "DMA-API: error creating debugfs entries "
331                                 "- disabling\n");
332                 global_disable = true;
333
334                 return;
335         }
336
337         if (req_entries)
338                 num_entries = req_entries;
339
340         if (prealloc_memory(num_entries) != 0) {
341                 printk(KERN_ERR "DMA-API: debugging out of memory error "
342                                 "- disabled\n");
343                 global_disable = true;
344
345                 return;
346         }
347
348         printk(KERN_INFO "DMA-API: debugging enabled by kernel config\n");
349 }
350
351 static __init int dma_debug_cmdline(char *str)
352 {
353         if (!str)
354                 return -EINVAL;
355
356         if (strncmp(str, "off", 3) == 0) {
357                 printk(KERN_INFO "DMA-API: debugging disabled on kernel "
358                                  "command line\n");
359                 global_disable = true;
360         }
361
362         return 0;
363 }
364
365 static __init int dma_debug_entries_cmdline(char *str)
366 {
367         int res;
368
369         if (!str)
370                 return -EINVAL;
371
372         res = get_option(&str, &req_entries);
373
374         if (!res)
375                 req_entries = 0;
376
377         return 0;
378 }
379
380 __setup("dma_debug=", dma_debug_cmdline);
381 __setup("dma_debug_entries=", dma_debug_entries_cmdline);
382