diff options
-rw-r--r-- | lib/dma-debug.c | 101 |
1 files changed, 101 insertions, 0 deletions
diff --git a/lib/dma-debug.c b/lib/dma-debug.c index 31099712328c..5ff7d2e2b60e 100644 --- a/lib/dma-debug.c +++ b/lib/dma-debug.c | |||
@@ -18,9 +18,14 @@ | |||
18 | */ | 18 | */ |
19 | 19 | ||
20 | #include <linux/dma-debug.h> | 20 | #include <linux/dma-debug.h> |
21 | #include <linux/spinlock.h> | ||
21 | #include <linux/types.h> | 22 | #include <linux/types.h> |
22 | #include <linux/list.h> | 23 | #include <linux/list.h> |
23 | 24 | ||
25 | #define HASH_SIZE 1024ULL | ||
26 | #define HASH_FN_SHIFT 13 | ||
27 | #define HASH_FN_MASK (HASH_SIZE - 1) | ||
28 | |||
24 | enum { | 29 | enum { |
25 | dma_debug_single, | 30 | dma_debug_single, |
26 | dma_debug_page, | 31 | dma_debug_page, |
@@ -40,3 +45,99 @@ struct dma_debug_entry { | |||
40 | int sg_mapped_ents; | 45 | int sg_mapped_ents; |
41 | }; | 46 | }; |
42 | 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 | |||