blob: 6fcfa8e48f97b704d260bfd058ade56276d9e951 [file] [log] [blame]
Mitchel Humpherys42296fb2015-06-23 16:29:16 -07001/*
2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 and
6 * only version 2 as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 */
14
15#define pr_fmt(fmt) "iommu-debug: %s: " fmt, __func__
16
17#include <linux/debugfs.h>
18#include <linux/device.h>
19#include <linux/iommu.h>
20#include <linux/of.h>
21#include <linux/platform_device.h>
22#include <linux/slab.h>
23#include <linux/module.h>
24
25static struct dentry *debugfs_top_dir;
26
27#ifdef CONFIG_IOMMU_DEBUG_TRACKING
28
29static DEFINE_MUTEX(iommu_debug_attachments_lock);
30static LIST_HEAD(iommu_debug_attachments);
31static struct dentry *debugfs_attachments_dir;
32
33struct iommu_debug_attachment {
34 struct iommu_domain *domain;
35 struct device *dev;
36 struct dentry *dentry;
37 struct list_head list;
38};
39
40static int iommu_debug_attachment_show(struct seq_file *s, void *ignored)
41{
42 struct iommu_debug_attachment *attach = s->private;
43
44 seq_printf(s, "Domain: 0x%p\n", attach->domain);
45 return 0;
46}
47
48static int iommu_debug_attachment_open(struct inode *inode, struct file *file)
49{
50 return single_open(file, iommu_debug_attachment_show, inode->i_private);
51}
52
53static const struct file_operations iommu_debug_attachment_fops = {
54 .open = iommu_debug_attachment_open,
55 .read = seq_read,
56 .llseek = seq_lseek,
57 .release = single_release,
58};
59
60void iommu_debug_attach_device(struct iommu_domain *domain,
61 struct device *dev)
62{
63 struct iommu_debug_attachment *attach;
64
65 mutex_lock(&iommu_debug_attachments_lock);
66
67 attach = kmalloc(sizeof(*attach), GFP_KERNEL);
68 if (!attach)
69 goto unlock;
70
71 attach->domain = domain;
72 attach->dev = dev;
73
74 attach->dentry = debugfs_create_file(
75 dev_name(dev), S_IRUSR, debugfs_attachments_dir, attach,
76 &iommu_debug_attachment_fops);
77 if (!attach->dentry) {
78 pr_err("Couldn't create iommu/attachments/%s debugfs file for domain 0x%p\n",
79 dev_name(dev), domain);
80 kfree(attach);
81 goto unlock;
82 }
83
84 list_add(&attach->list, &iommu_debug_attachments);
85unlock:
86 mutex_unlock(&iommu_debug_attachments_lock);
87}
88
89void iommu_debug_detach_device(struct iommu_domain *domain,
90 struct device *dev)
91{
92 struct iommu_debug_attachment *it;
93
94 mutex_lock(&iommu_debug_attachments_lock);
95 list_for_each_entry(it, &iommu_debug_attachments, list)
96 if (it->domain == domain && it->dev == dev)
97 break;
98
99 if (&it->list == &iommu_debug_attachments) {
100 WARN(1, "Couldn't find debug attachment for domain=0x%p dev=%s",
101 domain, dev_name(dev));
102 } else {
103 list_del(&it->list);
104 debugfs_remove_recursive(it->dentry);
105 kfree(it);
106 }
107 mutex_unlock(&iommu_debug_attachments_lock);
108}
109
110static int iommu_debug_init_tracking(void)
111{
112 debugfs_attachments_dir = debugfs_create_dir("attachments",
113 debugfs_top_dir);
114 if (!debugfs_attachments_dir) {
115 pr_err("Couldn't create iommu/attachments debugfs directory\n");
116 return -ENODEV;
117 }
118
119 return 0;
120}
121#else
122static inline int iommu_debug_init_tracking(void) { return 0; }
123#endif
124
125#ifdef CONFIG_IOMMU_TESTS
126
127static LIST_HEAD(iommu_debug_devices);
128static struct dentry *debugfs_tests_dir;
129
130struct iommu_debug_device {
131 struct device *dev;
132 struct list_head list;
133};
134
135static int iommu_debug_build_phoney_sg_table(struct device *dev,
136 struct sg_table *table,
137 unsigned long total_size,
138 unsigned long chunk_size)
139{
140 unsigned long nents = total_size / chunk_size;
141 struct scatterlist *sg;
142 int i;
143 struct page *page;
144
145 if (!IS_ALIGNED(total_size, PAGE_SIZE))
146 return -EINVAL;
147 if (!IS_ALIGNED(total_size, chunk_size))
148 return -EINVAL;
149 if (sg_alloc_table(table, nents, GFP_KERNEL))
150 return -EINVAL;
151 page = alloc_pages(GFP_KERNEL, get_order(chunk_size));
152 if (!page)
153 goto free_table;
154
155 /* all the same page... why not. */
156 for_each_sg(table->sgl, sg, table->nents, i)
157 sg_set_page(sg, page, chunk_size, 0);
158
159 return 0;
160
161free_table:
162 sg_free_table(table);
163 return -ENOMEM;
164}
165
166static void iommu_debug_destroy_phoney_sg_table(struct device *dev,
167 struct sg_table *table,
168 unsigned long chunk_size)
169{
170 __free_pages(sg_page(table->sgl), get_order(chunk_size));
171 sg_free_table(table);
172}
173
174static const char * const _size_to_string(unsigned long size)
175{
176 switch (size) {
177 case SZ_4K:
178 return "4K";
179 case SZ_64K:
180 return "64K";
181 case SZ_2M:
182 return "2M";
183 case SZ_1M * 12:
184 return "12M";
185 case SZ_1M * 20:
186 return "20M";
187 }
188 return "unknown size, please add to _size_to_string";
189}
190
191static void iommu_debug_device_profiling(struct seq_file *s, struct device *dev)
192{
193 unsigned long sizes[] = { SZ_4K, SZ_64K, SZ_2M, SZ_1M * 12,
194 SZ_1M * 20, 0 };
195 unsigned long *sz;
196 struct iommu_domain *domain;
197 unsigned long iova = 0x10000;
198 phys_addr_t paddr = 0xa000;
199
200 domain = iommu_domain_alloc(&platform_bus_type);
201 if (!domain) {
202 seq_puts(s, "Couldn't allocate domain\n");
203 return;
204 }
205
206 if (iommu_attach_device(domain, dev)) {
207 seq_puts(s,
208 "Couldn't attach new domain to device. Is it already attached?\n");
209 goto out_domain_free;
210 }
211
212 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map", "iommu_unmap");
213 for (sz = sizes; *sz; ++sz) {
214 unsigned long size = *sz;
215 size_t unmapped;
216 s64 map_elapsed_us, unmap_elapsed_us;
217 struct timespec tbefore, tafter, diff;
218
219 getnstimeofday(&tbefore);
220 if (iommu_map(domain, iova, paddr, size,
221 IOMMU_READ | IOMMU_WRITE)) {
222 seq_puts(s, "Failed to map\n");
223 continue;
224 }
225 getnstimeofday(&tafter);
226 diff = timespec_sub(tafter, tbefore);
227 map_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
228
229 getnstimeofday(&tbefore);
230 unmapped = iommu_unmap(domain, iova, size);
231 if (unmapped != size) {
232 seq_printf(s, "Only unmapped %zx instead of %zx\n",
233 unmapped, size);
234 continue;
235 }
236 getnstimeofday(&tafter);
237 diff = timespec_sub(tafter, tbefore);
238 unmap_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
239
240 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
241 map_elapsed_us, unmap_elapsed_us);
242 }
243
244 seq_putc(s, '\n');
245 seq_printf(s, "%8s %15s %12s\n", "size", "iommu_map_sg", "iommu_unmap");
246 for (sz = sizes; *sz; ++sz) {
247 unsigned long size = *sz;
248 size_t unmapped;
249 s64 map_elapsed_us, unmap_elapsed_us;
250 struct timespec tbefore, tafter, diff;
251 struct sg_table table;
252 unsigned long chunk_size = SZ_4K;
253
254 if (iommu_debug_build_phoney_sg_table(dev, &table, size,
255 chunk_size)) {
256 seq_puts(s,
257 "couldn't build phoney sg table! bailing...\n");
258 goto out_detach;
259 }
260
261 getnstimeofday(&tbefore);
262 if (iommu_map_sg(domain, iova, table.sgl, table.nents,
263 IOMMU_READ | IOMMU_WRITE) != size) {
264 seq_puts(s, "Failed to map_sg\n");
265 goto next;
266 }
267 getnstimeofday(&tafter);
268 diff = timespec_sub(tafter, tbefore);
269 map_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
270
271 getnstimeofday(&tbefore);
272 unmapped = iommu_unmap(domain, iova, size);
273 if (unmapped != size) {
274 seq_printf(s, "Only unmapped %zx instead of %zx\n",
275 unmapped, size);
276 goto next;
277 }
278 getnstimeofday(&tafter);
279 diff = timespec_sub(tafter, tbefore);
280 unmap_elapsed_us = div_s64(timespec_to_ns(&diff), 1000);
281
282 seq_printf(s, "%8s %12lld us %9lld us\n", _size_to_string(size),
283 map_elapsed_us, unmap_elapsed_us);
284
285next:
286 iommu_debug_destroy_phoney_sg_table(dev, &table, chunk_size);
287 }
288
289out_detach:
290 iommu_detach_device(domain, dev);
291out_domain_free:
292 iommu_domain_free(domain);
293}
294
295static int iommu_debug_device_show(struct seq_file *s, void *ignored)
296{
297 struct iommu_debug_device *ddev = s->private;
298
299 iommu_debug_device_profiling(s, ddev->dev);
300
301 return 0;
302}
303
304static int iommu_debug_device_open(struct inode *inode, struct file *file)
305{
306 return single_open(file, iommu_debug_device_show, inode->i_private);
307}
308
309static const struct file_operations iommu_debug_device_fops = {
310 .open = iommu_debug_device_open,
311 .read = seq_read,
312 .llseek = seq_lseek,
313 .release = single_release,
314};
315
316/*
317 * The following will only work for drivers that implement the generic
318 * device tree bindings described in
319 * Documentation/devicetree/bindings/iommu/iommu.txt
320 */
321static int snarf_iommu_devices(struct device *dev, void *ignored)
322{
323 struct iommu_debug_device *ddev;
324 struct dentry *dir, *profiling_dentry;
325
326 if (!of_find_property(dev->of_node, "iommus", NULL))
327 return 0;
328
329 ddev = kmalloc(sizeof(*ddev), GFP_KERNEL);
330 if (!ddev)
331 return -ENODEV;
332 ddev->dev = dev;
333 dir = debugfs_create_dir(dev_name(dev), debugfs_tests_dir);
334 if (!dir) {
335 pr_err("Couldn't create iommu/devices/%s debugfs dir\n",
336 dev_name(dev));
337 goto err;
338 }
339 profiling_dentry = debugfs_create_file("profiling", S_IRUSR, dir, ddev,
340 &iommu_debug_device_fops);
341 if (!profiling_dentry) {
342 pr_err("Couldn't create iommu/devices/%s/profiling debugfs file\n",
343 dev_name(dev));
344 goto err_rmdir;
345 }
346
347 list_add(&ddev->list, &iommu_debug_devices);
348 return 0;
349
350err_rmdir:
351 debugfs_remove_recursive(dir);
352err:
353 kfree(ddev);
354 return 0;
355}
356
357static int iommu_debug_init_tests(void)
358{
359 debugfs_tests_dir = debugfs_create_dir("tests",
360 debugfs_top_dir);
361 if (!debugfs_tests_dir) {
362 pr_err("Couldn't create iommu/tests debugfs directory\n");
363 return -ENODEV;
364 }
365
366 return bus_for_each_dev(&platform_bus_type, NULL, NULL,
367 snarf_iommu_devices);
368}
369#else
370static inline int iommu_debug_init_tests(void) { return 0; }
371#endif
372
373static int iommu_debug_init(void)
374{
375 debugfs_top_dir = debugfs_create_dir("iommu", NULL);
376 if (!debugfs_top_dir) {
377 pr_err("Couldn't create iommu debugfs directory\n");
378 return -ENODEV;
379 }
380
381 if (iommu_debug_init_tracking())
382 goto err;
383
384 if (iommu_debug_init_tests())
385 goto err;
386
387 return 0;
388err:
389 debugfs_remove_recursive(debugfs_top_dir);
390 return -ENODEV;
391}
392
393static void iommu_debug_exit(void)
394{
395 debugfs_remove_recursive(debugfs_top_dir);
396}
397
398module_init(iommu_debug_init);
399module_exit(iommu_debug_exit);